mirror of
https://github.com/SEPPDROID/DR-DOS-OpenDOS.git
synced 2025-10-22 16:04:20 +00:00
468 lines
13 KiB
NASM
468 lines
13 KiB
NASM
; File : $CLOCK.ASM$
|
|
;
|
|
; Description :
|
|
;
|
|
; Original Author : DIGITAL RESEARCH
|
|
;
|
|
; Last Edited By : $CALDERA$
|
|
;
|
|
;-----------------------------------------------------------------------;
|
|
; Copyright Work of Caldera, Inc. All Rights Reserved.
|
|
;
|
|
; THIS WORK IS A COPYRIGHT WORK AND CONTAINS CONFIDENTIAL,
|
|
; PROPRIETARY AND TRADE SECRET INFORMATION OF CALDERA, INC.
|
|
; ACCESS TO THIS WORK IS RESTRICTED TO (I) CALDERA, INC. EMPLOYEES
|
|
; WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF
|
|
; THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN CALDERA, INC. WHO
|
|
; HAVE ACCEPTED THE CALDERA OPENDOS SOURCE LICENSE OR OTHER CALDERA LICENSE
|
|
; AGREEMENTS. EXCEPT UNDER THE EXPRESS TERMS OF THE CALDERA LICENSE
|
|
; AGREEMENT NO PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED,
|
|
; COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
|
|
; CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST,
|
|
; TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF
|
|
; CALDERA, INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT
|
|
; AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND
|
|
; CIVIL LIABILITY.
|
|
;-----------------------------------------------------------------------;
|
|
;
|
|
; *** Current Edit History ***
|
|
; *** End of Current Edit History ***
|
|
;
|
|
; $Log$
|
|
; CLOCK.ASM 1.12 93/07/22 19:43:10
|
|
; switch over to REQUEST.EQU
|
|
; ENDLOG
|
|
|
|
include BIOSGRPS.EQU
|
|
include DRMACROS.EQU ; standard DR macros
|
|
include IBMROS.EQU ; ROM BIOS equates
|
|
include REQUEST.EQU ; request header equates
|
|
include DRIVER.EQU ; device driver equates
|
|
|
|
page
|
|
CGROUP group CODE, RCODE, RESUMECODE, ICODE
|
|
|
|
CG equ offset CGROUP
|
|
|
|
TIME struc
|
|
DAYS dw ?
|
|
MINUTES db ?
|
|
HOURS db ?
|
|
HUNDREDTHS db ?
|
|
SECONDS db ?
|
|
TIME ends
|
|
|
|
Assume CS:CGROUP, DS:CGROUP, ES:Nothing, SS:Nothing
|
|
|
|
CODE segment 'CODE'
|
|
|
|
extrn endbios:word ; for device driver INIT function
|
|
extrn daycount:word
|
|
|
|
; There are 1193180/65536 ticks per second, or 0E90Bh/10000h ticks per 5/100th.
|
|
|
|
CONVERSION_FACTOR equ 0E90Bh
|
|
|
|
CODE ends
|
|
|
|
RCODE segment 'RCODE'
|
|
|
|
extrn DataSegment:word
|
|
|
|
monlen db 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
|
|
|
|
|
Public ClockTable
|
|
|
|
ClockTable:
|
|
db 9 ; Last supported function
|
|
dw CG:dd_init ; 0-initialize driver
|
|
dw CG:dd_error ; 1-media change check (disks only)
|
|
dw CG:dd_error ; 2-build BPB (disks only)
|
|
dw CG:dd_error ; 3-IOCTL string input
|
|
dw CG:dd_input ; 4-input
|
|
dw CG:dd_error ; 5-nondestructive input (char only)
|
|
dw CG:dd_error ; 6-input status (char only)
|
|
dw CG:dd_error ; 7-input flush
|
|
dw CG:dd_output ; 8-output
|
|
dw CG:dd_output ; 9-output with verify
|
|
|
|
page
|
|
driver proc near
|
|
|
|
dd_error: ; used for all unsupported driver functions
|
|
;--------
|
|
mov ax,RHS_ERROR+3 ; "invalid command" error
|
|
ret
|
|
|
|
dd_input: ; 4-input
|
|
;--------
|
|
les di,es:RH4_BUFFER[bx] ; ES:DI -> requested date/time buffer
|
|
call read_system_ticks ; read system tick counter
|
|
mov ax,daycount ; AX = date
|
|
stosw ; return date
|
|
push dx ; save low word of ticks
|
|
mov ax,5
|
|
mul cx ; multiply high word of ticks by 5
|
|
xchg ax,cx ; save result in CX
|
|
pop dx
|
|
mov ax,5
|
|
mul dx ; multiply low word of tick by 5
|
|
add dx,cx ; and add in high word
|
|
mov bx,CONVERSION_FACTOR
|
|
div bx ; convert to centi-secs
|
|
push ax ; save high word of result
|
|
xor ax,ax
|
|
div bx ; divide remainder
|
|
pop dx ; recover high word of result
|
|
; giving us time in centi-secs
|
|
mov bx,60*100 ; BX = centi-secs/minute
|
|
div bx ; AX = # minutes
|
|
push dx ; save centi-secs remainder
|
|
cwd ; DX = 0
|
|
mov bx,60+(256*100) ; BL = minutes/hour,BH = centi-secs/sec
|
|
div bl ; AL = hours, AH = minutes remainder
|
|
xchg al,ah
|
|
stosw ; return minutes then hours
|
|
pop ax ; recover centi-secs remainder
|
|
div bh ; AL = secs, AH = centi-secs remainder
|
|
xchg al,ah
|
|
stosw ; return centi-secs then secs
|
|
sub ax,ax
|
|
ret
|
|
|
|
page
|
|
|
|
dd_output: ; 8-output
|
|
;---------
|
|
les si,es:RH4_BUFFER[bx]
|
|
|
|
; First we'll convert the date & set the RTC if present:
|
|
|
|
mov ax,es:DAYS[si] ; # of days since 1/1/1980
|
|
mov daycount,ax
|
|
|
|
mov dx,1980 ; get initial year
|
|
output1:
|
|
mov cx,365 ; assumed year size
|
|
test dl,3 ; test for leap years
|
|
jnz output2 ; skip if not a leap year
|
|
inc cx ; leap years have 366 days
|
|
output2:
|
|
cmp ax,cx ; more days than this year?
|
|
jb output3 ; skip if less - same year
|
|
sub ax,cx ; else date in future year
|
|
inc dx ; subtract from total, next year
|
|
jmps output1 ; check again
|
|
|
|
output3: ; DX = binary year, AX = day in year
|
|
sub bx,bx ; start with January
|
|
sub cx,cx ; CH = 0
|
|
output4:
|
|
mov cl,cs:monlen[bx] ; CX = # of days in next month
|
|
cmp cl,28 ; is it february ?
|
|
jne output5
|
|
test dl,3 ; is it a leap year ?
|
|
jnz output5
|
|
inc cx ; leap years have 29 days in february
|
|
output5:
|
|
cmp ax,cx ; remaining day count >= month length?
|
|
jb output6 ; skip if right month found
|
|
sub ax,cx ; else subtract days in that month
|
|
inc bx ; move on to next month
|
|
jmps output4 ; repeat until month found
|
|
|
|
output6: ; DX = binary year
|
|
inc ax ; AX = day-1 => convert to day
|
|
inc bx ; BX = month-1 => convert to month
|
|
mov ah,bl ; high byte is month
|
|
call bin2bcd ; convert to month
|
|
|
|
xchg ax,dx ; DL, DH = day, month of date
|
|
; AX = binary year
|
|
|
|
mov bl,100
|
|
div bl ; AL = century, AH = year
|
|
xchg al,ah ; AH = century, AL = year
|
|
call bin2bcd ; convert AL, AH from binary to BCD
|
|
xchg ax,cx ; CL, CH = year, century for date
|
|
|
|
mov ah,5 ; set real time clock date
|
|
int RTC_INT ; on AT, XT-286, PS/2, etc.
|
|
|
|
; Now we'll convert the time & set the RTC if present
|
|
|
|
; mov ah,es:HOURS[si]
|
|
; mov al,es:MINUTES[si] ; get binary hours & minutes
|
|
mov ax,es:word ptr MINUTES[si]
|
|
call bin2bcd ; convert to BCD values
|
|
xchg ax,cx ; CH, CL = hh:mm in BCD
|
|
mov ah,es:SECONDS[si]
|
|
mov al,0 ; get binary seconds & no daylight saving
|
|
call bin2bcd ; convert to BCD values
|
|
xchg ax,dx ; DH, DL = ss.000 in BCD
|
|
|
|
mov ah,3 ; set real time clock time
|
|
int RTC_INT ; on AT, XT-286, PS/2, etc.
|
|
|
|
mov al,100
|
|
mul es:SECONDS[si] ; AX = seconds in hundredths
|
|
xchg ax,dx ; save in DX
|
|
mov al,es:HUNDREDTHS[si]
|
|
cbw ; AX = hundredths
|
|
add ax,dx ; AX = secs and hundredths in 1/100ths
|
|
cwd ; make the a dword
|
|
mov bx,5
|
|
div bx ; AX = secs and hundredths in 5/100ths
|
|
xchg ax,bx ; save in BX
|
|
mov al,60 ; convert hours into minutes
|
|
mul es:HOURS[si] ; AX = hours in mins
|
|
xchg ax,dx
|
|
mov al,es:MINUTES[si]
|
|
cbw ; AX = minutes value
|
|
add ax,dx ; AX = hours and mins in mins
|
|
mov dx,60*20
|
|
mul dx ; DX:AX = hours and mins in 5/100ths
|
|
add ax,bx
|
|
adc dx,0 ; DX:AX = total in 5/100ths
|
|
mov bx,CONVERSION_FACTOR ; load up our magic value
|
|
push dx ; save high word
|
|
mul bx ; DX = low word result
|
|
mov cx,dx ; save for later
|
|
pop ax ; recover high word
|
|
mul bx ; DX:AX = result from high word
|
|
add ax,cx ; add low and high word results
|
|
adc dx,0 ; together in DX:AX
|
|
xchg ax,dx ; DX = low word of result
|
|
xchg ax,cx ; CX = high word of result
|
|
|
|
mov ah,1 ; set system timer
|
|
int RTC_INT ; CX = high word, DX = low word
|
|
|
|
sub ax,ax ; return successfully when done
|
|
ret
|
|
|
|
|
|
Public read_system_ticks
|
|
|
|
read_system_ticks:
|
|
;-----------------
|
|
mov ah,0 ; read system tick counter
|
|
int RTC_INT
|
|
test al,al ; have we passed midnight ?
|
|
jz read_st10 ; if so a new day has dawned
|
|
inc daycount
|
|
read_st10:
|
|
ret
|
|
|
|
bin2bcd: ; convert AL and AH to BCD values
|
|
;-------
|
|
call bin2bcd1 ; swap AL, AH, convert to BCD
|
|
; call bin2bcd1
|
|
; ret
|
|
bin2bcd1:
|
|
push cx
|
|
mov ch,ah ; save AH in scratch register
|
|
aam ; AL = AL % 10; AH = AL/10;
|
|
mov cl,4
|
|
shl ah,cl ; shift tens into high nibble
|
|
or ah,al ; combine the nibbles
|
|
mov al,ch ; restore the high byte into low byte
|
|
pop cx
|
|
ret
|
|
|
|
driver endp
|
|
|
|
RCODE ends ; end of device driver code
|
|
|
|
RESUMECODE segment 'RESUMECODE'
|
|
|
|
; If the system ROM BIOS supports RESUME mode then it will call Int 6C
|
|
; when returning from sleep mode. We take this over and reset the clock
|
|
; based upon the RTC value. To save space we only relocate the code if
|
|
; required.
|
|
;
|
|
|
|
Public ResumeHandler
|
|
ResumeHandler proc far
|
|
sti
|
|
mov ax,cs:DataSegment ; we have been asleep and are being
|
|
mov ds,ax ; woken by the BIOS
|
|
mov es,ax ; lets re-read the RTC before
|
|
call set_clock ; we return to them
|
|
clc
|
|
ret 2
|
|
ResumeHandler endp
|
|
|
|
set_monlen db 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
|
|
|
|
|
set_clock:
|
|
;---------
|
|
; We may also be called after a RESUME when we have to reset the time.
|
|
|
|
mov ah,2 ; read real time clock
|
|
xor cx,cx
|
|
xor dx,dx ; assume it won't work
|
|
stc
|
|
int RTC_INT ; CH = hours, CL = mins, DH = secs
|
|
jc set_clock40 ; (all in BCD remember)
|
|
|
|
xchg al,dh ; AL = secs
|
|
call bcd2bin ; AL = secs in binary
|
|
cmp al,59
|
|
ja set_clock40 ; reject invalid seconds
|
|
mov ah,100
|
|
mul ah ; AX = seconds in hundredths
|
|
cwd ; make it a dword
|
|
mov bx,5
|
|
div bx ; AX = secs and hundredths in 5/100ths
|
|
xchg ax,bx ; save in BX
|
|
|
|
mov al,ch ; AL = hours in BCD
|
|
call bcd2bin ; AL = hours in binary
|
|
cmp al,23
|
|
ja set_clock40 ; reject invalid hours
|
|
mov ah,60 ; convert hours into minutes
|
|
mul ah ; AX = hours in mins
|
|
xchg ax,dx ; save in DX
|
|
|
|
mov al,cl ; AL = mins in BCD
|
|
call bcd2bin ; AL = mins in binary
|
|
cmp al,59
|
|
je set_clock40 ; reject invalid mins
|
|
cbw ; AX = minutes value
|
|
add ax,dx ; AX = hours and mins in mins
|
|
mov dx,60*20
|
|
mul dx ; DX:AX = hours and mins in 5/100ths
|
|
add ax,bx
|
|
adc dx,0 ; DX:AX = total in 5/100ths
|
|
mov bx,CONVERSION_FACTOR ; load up our magic value
|
|
push dx ; save high word
|
|
mul bx ; DX = low word result
|
|
mov cx,dx ; save for later
|
|
pop ax ; recover high word
|
|
mul bx ; DX:AX = result from high word
|
|
add ax,cx ; add low and high word results
|
|
adc dx,0 ; together in DX:AX
|
|
xchg ax,dx ; DX = low word of result
|
|
xchg ax,cx ; CX = high word of result
|
|
|
|
mov ah,1 ; set system timer
|
|
int RTC_INT ; CX = high word, DX = low word
|
|
|
|
set_clock40:
|
|
mov ah,4 ; read RTC (if present)
|
|
int RTC_INT ; validate date - CMOS may be corrupt
|
|
cmp cx,1980h ; Too low?
|
|
jb set_clock45 ; Yes so skip
|
|
cmp cx,2099h ; Too high ?
|
|
ja set_clock45 ; Yes so skip
|
|
cmp dx,0101h ; Too low?
|
|
jb set_clock45 ; Yes so skip
|
|
cmp dx,3112h ; Too high ?
|
|
jbe set_clock50 ; No its okay so scram
|
|
set_clock45:
|
|
mov cx,1980h ; assume the year 1980
|
|
mov dx,0101h ; assume 1st of January of that year
|
|
set_clock50:
|
|
xchg ax,cx ; AL, AH = year, century in BCD
|
|
call bcd2bin ; convert values to binary
|
|
xchg ax,cx
|
|
xchg ax,dx ; AL, AH = day, month in BCD
|
|
call bcd2bin ; convert values to binary
|
|
xchg ax,dx
|
|
|
|
mov daycount,0 ; zero the daycount in case of RESUME
|
|
mov ax,19*256 + 80 ; assume 1980
|
|
set_clock55:
|
|
cmp ax,cx ; same year?
|
|
je set_clock65
|
|
mov bx,365 ; assume 365 days in that year
|
|
test al,3 ; test for leap year
|
|
jnz set_clock60 ; (this works til 2400 A.D.)
|
|
inc bx ; add FEB 29 if divisible by four
|
|
set_clock60:
|
|
add daycount,bx ; add days in previous year to total
|
|
inc al ; next year
|
|
cmp al,100 ; end of century?
|
|
jb set_clock55 ; skip if same century
|
|
mov al,0 ; continue with XX00
|
|
inc ah ; ...next century
|
|
jmps set_clock55 ; check year again
|
|
|
|
|
|
set_clock65: ; same year by now
|
|
xchg ax,cx ; CX = year
|
|
sub dx,0101h ; make month, day 0 relative
|
|
sub bx,bx ; assume January
|
|
sub ax,ax ; AH = 0
|
|
set_clock70:
|
|
cmp dh,bl ; does current month match?
|
|
je set_clock80 ; skip if it does
|
|
mov al,cs:set_monlen[bx] ; get length of that month
|
|
cmp al,28 ; is it february ?
|
|
jne set_clock75
|
|
test cl,3 ; is it a leap year ?
|
|
jnz set_clock75
|
|
inc ax ; leap year, 29 days in february
|
|
set_clock75:
|
|
add daycount,ax ; add it to total day count
|
|
inc bx ; move on to next month
|
|
jmps set_clock70
|
|
|
|
set_clock80:
|
|
mov al,dl ; get days in that month
|
|
add daycount,ax ; add them to day count
|
|
ret
|
|
|
|
bcd2bin:
|
|
;-------
|
|
; entry: AL, AH = BCD values
|
|
; AL, AH = binary equivalents
|
|
|
|
call bcd2bin1 ; swap AL, AH, convert AL to binary
|
|
; call bcd2bin1 ; swap AL, AH, convert AL to binary
|
|
; ret
|
|
|
|
bcd2bin1: ; convert BCD to binary
|
|
xchg al,ah ; swap the two values
|
|
push bx
|
|
mov bl,0 ; start off without tens
|
|
bcd2bin2:
|
|
cmp al,10h ; check if more tens
|
|
jb bcd2bin3 ; all tens done
|
|
sub al,10h ; else subtract 10 in BCD
|
|
add bl,10 ; ...and add it in binary
|
|
jmps bcd2bin2 ; repeat for all tens
|
|
bcd2bin3: ; AL = ones, BL = tens
|
|
add al,bl ; AL = binary value
|
|
pop bx ; restore BX
|
|
ret
|
|
|
|
|
|
RESUMECODE ends
|
|
|
|
|
|
ICODE segment 'ICODE' ; initialization code
|
|
|
|
Assume CS:CGROUP, DS:CGROUP
|
|
|
|
dd_init: ; 0-initialize driver
|
|
;-------
|
|
call set_clock ; set elapsed ticks
|
|
|
|
les bx,REQUEST[bp] ; ES:BX -> request header
|
|
|
|
mov ax,endbios
|
|
mov es:RH0_RESIDENT[bx],ax ; set end of device driver
|
|
mov es:RH0_RESIDENT+2[bx],ds
|
|
|
|
sub ax,ax ; initialization succeeded
|
|
ret ; (BIOS init always does...)
|
|
|
|
|
|
ICODE ends
|
|
|
|
end
|