Files
DR-DOS-OpenDOS/IBMBIO/CLOCK.ASM
2020-11-04 23:59:28 +01:00

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