; ============================================================================
;
; MicroDOS - DOS clock
;
; ============================================================================
; ----------------------------------------------------------------------------
; Pack 4 bytes to BCD
; ----------------------------------------------------------------------------
; INPUT: DH,DL,AH,AL = binary numbers to pack
; OUTPUT: DH,DL,AH,AL = BCD numbers
; ----------------------------------------------------------------------------
; ------------- Pack DX
BinBCDD: xchg ax,dx ; prepare words
call BinBCDW ; pack word in AX to BCD
xchg ax,dx ; return words
; ------------- Pack AH
BinBCDW: xchg al,ah ; prepare bytes
call BinBCDB ; pak byte in AL to BCD
xchg al,ah ; return bytes
; ------------- Push registers
BinBCDB: push cx ; push CX
mov cx,ax ; CH <- push registers AH
; ------------- Pack number in AL
aam ; unpack digits
mov cl,4 ; CL <- number of rotations
shl ah,cl ; shift AH 4 bits left
or al,ah ; AL <- BCD number
; ------------- Pop registers
mov ah,ch ; pop register AH
pop cx ; pop CX
ret
; ----------------------------------------------------------------------------
; Unpack 4 BCD bytes
; ----------------------------------------------------------------------------
; INPUT: DH,DL,AH,AL = BCD numbers to unpack
; OUTPUT: DH,DL,AH,AL = binary numbers
; ----------------------------------------------------------------------------
; ------------- Unpack DX
BCDBinD: xchg ax,dx ; prepare words
call BCDBinW ; unpack BCD word in AX
xchg ax,dx ; return words
; ------------- Unpack AH
BCDBinW: xchg al,ah ; prepare bytes
call BCDBinB ; unpack BCD byte in AL
xchg al,ah ; return bytes
; ------------- Push registers
BCDBinB: push cx ; push CX
xchg ax,cx ; CH <- push register AH
; ------------- Unpack number in AL
mov cl,4 ; CL <- number of rotations
mov ah,al ; AH <- number to unpack
shr ah,cl ; AH = BCD digit HIGH
and al,0fh ; AL = BCD digit LOW
aad ; pack digits to binary
; ------------- Pop registers
mov ah,ch ; pop register AH
pop cx ; pop CX
ret
; ----------------------------------------------------------------------------
; Prepare date in DOS file format
; ----------------------------------------------------------------------------
; INPUT: DS = data segment
; ----------------------------------------------------------------------------
; ------------- Push registers
CalcFileDate: push ax ; push AX
push bx ; push BX
push cx ; push CX
; ------------- Prepare date
mov ax,[Year] ; AX <- year
sub ax,1980 ; offset from 1980
mov cl,9 ; CL <- 9 number of rotations
shl ax,cl ; shift year to bit 9
xchg ax,bx ; BX <- year
mov al,[Month] ; AL <- month
cbw ; AX <- month
mov cl,5 ; CL <- 5 number of rotations
shl ax,cl ; shift month to bit 5
or ax,bx ; add year
or al,[Day] ; add day
mov [FileDate],ax ; store date in DOS file format
; ------------- Pop registers
pop cx ; pop CX
pop bx ; pop BX
pop ax ; pop AX
ret
; ----------------------------------------------------------------------------
; Read system timer
; ----------------------------------------------------------------------------
; OUTPUT: CX:DX = system timer (number of 1/18 sec ticks)
; NOTES: It shifts "current date" when timer overflows 24 hours.
; ----------------------------------------------------------------------------
; ------------- Push registers
GetTimer: push ax ; push AX
; ------------- Read system timer
mov ah,0 ; AH <- function code
int 1ah ; read system timer
or al,al ; shift day on midnight?
jz GetTimer8 ; no
; ------------- Push registers 2
push bx ; push BX
push cx ; push CX
push ds ; push DS
; ------------- Prepare data segment
push cs ; push CS
pop ds ; DS <- CS
; ------------- Prepare days in February
mov cx,[Year] ; CX <- year
call PrepareFeb ; prepare days in February
; ------------- Increase day
inc byte [Day] ; increase day
mov al,[Month] ; AL <- month
cbw ; AX <- month
xchg ax,bx ; BX <- month
mov al,[Day] ; AL <- day
cmp al,[bx+DayMonthTab-1] ; check day
jbe GetTimer6 ; day is OK
; ------------- Increase month
mov byte [Day],1 ; correct day in month
inc byte [Month] ; increase month
cmp byte [Month],12 ; maximal month
jbe GetTimer6 ; month is OK
; ------------- Increase year
mov byte [Month],1 ; correct month
inc word [Year] ; increase year
; ------------- Prepare date in DOS file format
GetTimer6: call CalcFileDate ; prepare date in DOS file format
; ------------- Pop registers 2
pop ds ; pop DS
pop cx ; pop CX
pop bx ; pop BX
; ------------- Pop registers
GetTimer8: pop ax ; pop AX
ret
; ----------------------------------------------------------------------------
; Recalculate system timer to time
; ----------------------------------------------------------------------------
; INPUT: CX:DX = timer
; OUTPUT: CL = hundredth
; CH = second
; AL = minute
; AH = hour
; DESTROYS: BX, DX
; ----------------------------------------------------------------------------
; ------------- Calculate CX:DX * 5 (-> DX:AX, max. 7865195)
Clock2Time: mov bx,5 ; BX <- 5
xchg ax,dx ; AX <- timer LOW
xchg ax,cx ; AX <- timer HIGH, CX <- timer LOW
mul bx ; AX <- timer HIGH*5
xchg ax,cx ; AX <- timer LOW, CX <- timer HIGH*5
mul bx ; DX:AX <- timer LOW*5
add dx,cx ; add timer HIGH
; ------------- Calculate DX:AX / 59659 * 65536 (-> CX:AX, max. 8639994)
mov bx,59659 ; BX <- 59659
div bx ; AX <- quotient, DX <- remainder
xchg cx,ax ; CX <- hundredths HIGH
xor ax,ax ; AX <- 0, DX:AX = remainder * 65536
div bx ; AX <- hundredths LOW
; ------------- Calculate seconds (->DX:AX, max.86399)+hundredths (->CL, 0-99)
xchg ax,cx ; AX <- hundredths HIGH, CX <- LOW
xor dx,dx ; DX <- 0
mov bx,100 ; BX <- divisor
div bx ; AX <- seconds HIGH, DX <- reaminder
xchg ax,cx ; AX <- hundredths LOW, CX <- sec. HIGH
div bx ; AX <- seconds LOW, DX <- hundredths
xchg dx,cx ; DX <- seconds HIGH, CL <- hundredths
; ------------- Calculate second (-> CH, 0 to 59)+minutes (-> AX, max. 1439)
mov bl,60 ; BX <- divisor
div bx ; calculate minute and second
mov ch,dl ; CH <- second
; ------------- Calculate hour (0 to 23) and minute (0 to 59)
div bl ; AL <- hour, AH <- minute
xchg ah,al ; AL <- minute, AH <- hour
ret
; ----------------------------------------------------------------------------
; Recalculate time to system timer
; ----------------------------------------------------------------------------
; INPUT: BL = minute
; BH = hour
; CL = hundredth
; CH = second
; OUTPUT: CX:DX = timer
; DESTROYS: AX, BX
; ----------------------------------------------------------------------------
; ------------- Merge second with hundredth (-> CX, max. 5999 hundredths)
Time2Clock: mov al,100 ; AL <- 100 hundredths in second
mul ch ; AX <- second in hundredths
mov ch,0 ; CX = hundredth
add cx,ax ; CX <- second*100 + hundredth
; ------------- Merge minute with hour (-> AX, max. 1439 minutes)
mov al,60 ; AL <- 60 minutes in hour
mul bh ; AX <- recalc hour to minutes
mov bh,0 ; BX = minute
add ax,bx ; AX = hour*60 + minute
; ------------- Merge all items (-> DX:CX, max. 8639999 hundredths)
mov dx,6000 ; DX <- 6000 hundredths in minute
mul dx ; DX:AX <- hour & minute in hundredths
add cx,ax ; CX <- hundredths LOW
adc dx,byte 0 ; DX <- hundredths HIGH
; ------------- Calculate DX:CX * 59659 / 65536 (-> DX:AX, max. 7865199)
xchg ax,dx ; AX <- hundredths HIGH
xchg ax,cx ; AX <- hundredths LOW, CX <- HIGH
mov bx,59659 ; BX <- 59659
mul bx ; DX <- result LOW (e.g. / 65536)
xchg ax,dx ; AX <- result LOW
xchg ax,cx ; CX <- result LOW, AX <- hundr. HIGH
mul bx ; DX:AX <- result
add ax,cx ; AX <- result LOW
adc dx,byte 0 ; DX <- result HIGH
; ------------- Calculate DX:AX / 5 (-> CX:DX, max. 1573039)
mov bx,5 ; BX <- divisor
xchg ax,dx ; AX <- HIGH (= max. 24)
div bl ; AL <- result HIGH
xchg ax,cx ; CL <- result HIGH
mov al,ch ; AL <- remainder
mov ch,0 ; CX = result HIGH
cbw ; AX = remainder
xchg ax,dx ; AX <- LOW, DX <- HIGH
div bx ; calculate result LOW
xchg ax,dx ; DX <- result LOW
ret
; ----------------------------------------------------------------------------
; Prepare days in February
; ----------------------------------------------------------------------------
; INPUT: CL = year (may be absolute or relative from 1980 or 1900)
; ----------------------------------------------------------------------------
PrepareFeb: push ax ; push AX
mov al,28 ; AL <- 28 days in non-leap year
test cl,3 ; is it leap year?
jnz PrepareFeb2 ; it is not leap year
inc ax ; AL <- 29 days in leap year
PrepareFeb2: mov [cs:DayMonthFeb],al; store days in February
pop ax ; pop AX
ret
; ----------------------------------------------------------------------------
; Int 21h, function 2Ah - Get system date
; ----------------------------------------------------------------------------
; INT21 INPUT: AH = 2Ah (function code)
; INT21 OUTPUT: CX = year (1980-2099)
; DH = month (1-12)
; DL = day (1-31)
; ----------------------------------------------------------------------------
; ------------- Read system timer (to shift current day)
Int212A: call GetTimer ; read system timer
; ------------- Return current date
mov cx,[Year] ; CX <- year
call SetRegCX ; set year
mov dx,[DayMonth] ; DL <- day, DH <- month
call SetRegDX ; set day and month
mov al,0 ; AL <- 0 day of week is invalid
ret
; ----------------------------------------------------------------------------
; Int 21h, function 2Bh - Set system date
; ----------------------------------------------------------------------------
; INT21 INPUT: AH = 2Bh (function code)
; CX = year (1980-2099)
; DH = month (1-12)
; DL = day (1-31)
; INT21 OUTPUT: AL = result
; 0: successful
; 0ffh: invalid date, system date unchanged
; ----------------------------------------------------------------------------
; ------------- Prepare data segment
Int212B: push cs ; push CS
pop ds ; DS <- CS
; ------------- Check year
mov al,0ffh ; AL <- 0ffh, invalid date flag
cmp cx,1980 ; minimal year
jb Int212B8 ; invalid year
cmp cx,2099 ; maximal year
ja Int212B8 ; invalid year
; ------------- Check month
or dh,dh ; minimal month
jz Int212B8 ; invalid month
cmp dh,12 ; maximal month
ja Int212B8 ; invalid month
; ------------- Prepare days in February
push cx ; push CX
sub cx,1980 ; CX <- offset from 1980
call PrepareFeb ; prepare February
pop cx ; pop CX
; ------------- Check day
or dl,dl ; minimal day
jz Int212B8 ; invalid day
push bx ; push BX
xor bx,bx ; BX <- 0
mov bl,dh ; BL <- month
cmp dl,[cs:bx+DayMonthTab-1] ; check days in month
pop bx ; pop BX
ja Int212B8 ; invalid day
; ------------- Read system timer (to flush midnight flag)
push cx ; push CX
push dx ; push DX
call GetTimer ; read system timer
pop dx ; pop DX
pop cx ; pop CX
; ------------- Store system date
mov [Year],cx ; store year
mov [DayMonth],dx ; store day and month
; ------------- Split year into century and year
mov al,100 ; AL <- 100 years in century
xchg ax,cx ; AX <- year, CL <- 100
div cl ; split into year and century
xchg al,ah ; AL <- year, AH <- century
; ------------- Set new date to RTC clock
call BinBCDD ; pack to BCD
xchg ax,cx ; CX <- year
mov ah,5 ; AH <- function code
int 1ah ; set new date
; ------------- Prepare date in DOS file format
call CalcFileDate ; recalc to DOS file format
mov al,0 ; status OK
Int212B8: ret
; ----------------------------------------------------------------------------
; Int 21h, function 2Ch - Get system time
; ----------------------------------------------------------------------------
; INT21 INPUT: AH = 2Ch (function code)
; INT21 OUTPUT: CH = hour (0 to 23)
; CL = minute (0 to 59)
; DH = second (0 to 59)
; DL = 1/100 seconds (0 to 99)
; NOTES: Time resolution is 55 ms. On some systems DL may return 0.
; ----------------------------------------------------------------------------
; ------------- Read system timer
Int212C: call GetTimer ; read system timer
; ------------- Recalc to time
call Clock2Time ; recalc timer to time
xchg ax,cx ; CH <- hour, CL <- minute
xchg ax,dx ; DH <- second, DL <- hundredth
; ------------- Return current time
call SetRegCX ; set minute and hour
call SetRegDX ; set hundredth and second
mov al,0 ; return code OK
ret
; ----------------------------------------------------------------------------
; Int 21h, function 2Dh - Set system time
; ----------------------------------------------------------------------------
; INT21 INPUT: AH = 2Dh (function code)
; CH = hour (0 to 23)
; CL = minute (0 to 59)
; DH = second (0 to 59)
; DL = 1/100 seconds (0 to 99)
; INT21 OUTPUT: AL = result
; 0: successful
; 0ffh: invalid time, system time unchanged
; ----------------------------------------------------------------------------
; ------------- Check time
Int212D: mov al,0ffh ; AL <- 0ffh, invalid time flag
cmp ch,23 ; maximal hour
ja Int212D8 ; invalid hour
cmp cl,59 ; maximal minute
ja Int212D8 ; invalid minute
cmp dh,59 ; maximal second
ja Int212D8 ; invalid second
cmp dl,99 ; maximal hundredth
ja Int212D8 ; invalid hundredth
; ------------- Set new time to RTC clock
push cx ; push CX (CL=minute, CH=hour)
push dx ; push DX (DL=hundredth, DH=second)
xchg ax,cx ; AX <- AL=minute, AH=hour
call BinBCDD ; pack to BCD
xchg ax,cx ; CL <- minute, CH <- hour
mov dl,0 ; no daylight savings
mov ah,3 ; AH <- function code
int 1ah ; set current time
; ------------- Read system timer (to flush midnight flag)
call GetTimer ; read system timer
pop cx ; pop CX (CL=hundredth, CH=second)
pop bx ; pop BX (BL=minute, BH=hour)
; ------------- Recalculate time to system timer (-> CX:DX)
call Time2Clock ; recalc time to system timer
; ------------- Set new time to system timer
mov ah,1 ; AH <- function code
int 1ah ; set new time
mov al,0 ; return code OK
Int212D8: ret
; ----------------------------------------------------------------------------
; Data
; ----------------------------------------------------------------------------
; ------------- Current date
DayMonth:
Day: db 1 ; current day (1 to 31)
Month: db 1 ; current month
Year: dw 1980 ; current year (1980 to 2099)
FileDate: dw 0 ; current date in DOS file format
; ------------- Days in months
DayMonthTab: db 31 ; January
DayMonthFeb: db 28 ; February
db 31 ; March
db 30 ; April
db 31 ; May
db 30 ; June
db 31 ; July
db 31 ; August
db 30 ; September
db 31 ; October
db 30 ; November
db 31 ; December
|