; 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