📄 rtc.8
字号:
;
; Timer / RTC functions
;
; (C)1997-2001 Pascal Dornier / PC Engines; All rights reserved.
; This file is licensed pursuant to the COMMON PUBLIC LICENSE 0.5.
;
; Limitations:
;
; - Wait function not supported.
; - Reinitialization doesn't set SET bit.
; - We don't use CMOS RAM for configuration data
; (exception: extended memory size).
; - There is no CMOS checksum.
;
; Year 2000 issue:
;
; - Years below 1980 are considered century roll-over, replaced
; by year 2000.
; - Please note that DOS will force default date for anything
; before 1980.
; pd 000817 add TICK_RATE option to support AMD Elan SC520
; (needs modified tick rate)
; pd 990211 move tests from cm_shut to cm_test
; pd 980416 fix alarm function (interrupt mask, CX saved)
;
; INT1A timer BIOS
;
int1a:
#if def PCI ;transfer to PCI BIOS if necessary
cmp ah,0b1h ;PCI
jnz int1a1
jmp pci_i1a ;go to PCI BIOS
int1a1:
#endif
sti ;enable interrupts
push ds
push bx
xor bx,bx ;BIOS segment
mov ds,bx
cmp ah,7
ja int1a_err ;:bad command code
mov bl,ah
shl bx,1
cli ;disable interrupts
jmp [cs:bx+int1atab] ;dispatch function
;
; AH=2: get RTC time
;
rtc_get: call rtc_uip ;check for RTC update
jb int1a_err ;:error
mov ah,cm_ss ;read seconds -> DH
call rtc_read
mov dh,al
mov ah,cm_mm ;read minutes -> CL
call rtc_read
mov cl,al
mov ah,cm_hh ;read hours -> CH
call rtc_read
mov ch,al
mov ah,cm_b ;read daylight savings bit -> DL
call rtc_read
and al,1
mov dl,al
jmp short int1a_ok
;
; AH=3: set RTC time
;
rtc_set: push cx
call rtc_uip ;check for RTC update
pop cx
mov ah,cm_ss ;DH -> seconds
mov al,dh
call rtc_write
mov ah,cm_mm ;CL -> minutes
mov al,cl
call rtc_write
mov ah,cm_hh ;CH -> hours
mov al,ch
call rtc_write
mov ah,cm_b ;read status register B
call rtc_read
and al,01100010xb ;mask off set, update interrupt,
;square wave, daylight savings
or al,2 ;set 24 hour mode
or al,dl ;add daylight savings bit from DL
call rtc_write ;update status register B
int1a_ok: clc
int1a_ret: sti
pop bx
pop ds
retf 2
int1a_err: stc
jmp short int1a_ret
;
; AH=4: get RTC date
;
rtc_date: call rtc_uip ;check for RTC update
jb int1a_err
mov ah,cm_dd ;day -> DL
call rtc_read
mov dl,al
mov ah,cm_mo ;month -> DH
call rtc_read
mov dh,al
mov ah,cm_yy ;year -> CL
call rtc_read
mov cl,al
mov ah,cm_cent ;century -> CH
call rtc_read
mov ch,al
cmp cx,1980h ;century roll-over ?
jae rtc_date9
mov ax,cm_cent*256+20h ;update century register
call rtc_write
mov ch,20h ;force 2000
rtc_date9: jmp int1a_ok
;
; AH=5: set RTC date
;
rtc_sdat: push cx
call rtc_uip ;check for RTC update
pop cx
mov ax,cm_day*256 ;0 -> day of week (not used)
call rtc_write
mov ah,cm_dd ;DL -> day
mov al,dl
call rtc_write
mov ah,cm_mo ;DH -> month
mov al,dh
call rtc_write
mov ah,cm_yy ;CL -> year
mov al,cl
call rtc_write
mov ah,cm_cent ;CH -> century
mov al,ch
call rtc_write
jmp int1a_ok
;
; AH=6: set RTC alarm
;
rtc_alrm: mov ah,cm_b ;read status B
call rtc_read
and al,20h ;alarm enabled ?
jnz int1a_err ;:error
push cx ;save CX !
call rtc_uip ;check for RTC update
pop cx ;restore...
mov ah,cm_ssa ;DH -> alarm second
mov al,dh
call rtc_write
mov ah,cm_mma ;CL -> alarm minute
mov al,cl
call rtc_write
mov ah,cm_hha ;CH -> alarm hour
mov al,ch
call rtc_write
in al,pic1+1 ;read mask register
and al,0feh ;enable RTC interrupt (8)
out pic1+1,al
mov ah,cm_b ;read status B
call rtc_read
or al,20h ;enable alarm
call rtc_write
jmp int1a_ok
;
; AH=7: clear RTC alarm
;
rtc_snz: mov ah,cm_b ;read status B
call rtc_read
and al,5fh ;disable alarm interrupt
call rtc_write
jmp int1a_ok
;
; AH=0: get time
;
tm_get: mov dx,[m_timer] ;DX = timer low
mov cx,[m_timer+2] ;CX = timer high
mov al,0
xchg al,[m_timofl] ;AL = timer overflow, reset flag
jmp int1a_ok
;
; AH=1: set time
;
tm_set: mov byte [m_timofl],0 ;clear timer overflow flag
mov [m_timer],dx ;DX = timer low
mov [m_timer+2],cx ;CX = timer high
jmp int1a_ok
;
; INT1A dispatch table
;
even
int1atab: dw tm_get ;AH=0: get time
dw tm_set ;AH=1: set time
dw rtc_get ;AH=2: get RTC time
dw rtc_set ;AH=3: set RTC time
dw rtc_date ;AH=4: get RTC date
dw rtc_sdat ;AH=5: set RTC date
dw rtc_alrm ;AH=6: set RTC alarm
dw rtc_snz ;AH=7: clear RTC alarm
;
; Clear RTC interrupt, test shutdown byte
;
; Be sure to leave cm_test non-zero, as this is used by the
; master reset code in cs_clr to determine whether to reset
; the bus.
;
rtc_test: mov al,cm_nmi+cm_c ;clear pending interrupt
out cm_idx,al
out iowait,al
in al,cm_dat
mov bx,8000h+cm_nmi+cm_test ;bit to test, test register
rtc_test1: mov al,bl ;write pattern
out cm_idx,al
out iowait,al
mov al,bh
out cm_dat,al
out iowait,al
mov al,bl ;read back
out cm_idx,al
out iowait,al
in al,cm_dat
out iowait,al
cmp al,bh
jnz rtc_test8 ;:error - clc -> inverted -> error
shr bh,1 ;shift pattern right
jnb rtc_test1 ;:another bit
rtc_test8: cmc ;last bit inverted -> no carry if ok
ret
;
; Wait for RTC UIP bit cleared
;
; This bit is set about 250鎠 before the next update. If clear,
; we have at least 250鎠 to read or write the RTC without
; updates coming in between.
;
rtc_uip: mov cx,1000
mov ah,cm_a
rtc_uip1: cli
call rtc_read
and al,80h ;UIP ?
jz rtc_uip9 ;:ok, carry clear
sti ;give interrupts a chance
loop rtc_uip1 ;:try again
mov ax,cm_a*256+26h ;set 32768 Hz oscillator, 1 ms int
call rtc_write ;to restart...
stc
rtc_uip9: ret
;
; read RTC register [AH] -> AL
;
rtc_read: mov al,ah
out cm_idx,al
out iowait,al
in al,cm_dat
out iowait,al
ret
;
; write RTC register AL -> [AH]
;
rtc_write: xchg al,ah
out cm_idx,al
out iowait,al
xchg al,ah
out cm_dat,al
ret
;
; clock tick (IRQ0)
;
irq0: sti ;enable interrupts
push ax
push dx
push ds
#if def debug
call diag_csip ;debug: display CS:IP on MDA
#endif
xor ax,ax ;access BIOS segment
mov ds,ax
mov ax,[m_timer] ;update timer
mov dx,[m_timer+2]
add ax,1
adc dx,0
cmp ax,00b2h ;24 hours ?
jnz irq0_1
cmp dx,0018h
jnz irq0_1
xor ax,ax ;timer overflow - back to 0
xor dx,dx
mov byte [m_timofl],1
irq0_1: mov [m_timer],ax
mov [m_timer+2],dx
dec byte [m_fdcnt] ;floppy motor timer
jnz irq0_2 ;:not yet
mov al,0ch
mov dx,fdc_ctrl
out dx,al ;turn off motor
and byte [m_fdmot],0f0h ;turn off motor bits
irq0_2: int 1ch ;call user hook
pop ds
pop dx
mov al,eoi ;signal end of interrupt
cli
out pic0,al
pop ax
iret
;
; RTC interrupt (IRQ8)
;
irq8: push ax
mov al,cm_c ;check alarm interrupt bit
out cm_idx,al
out iowait,al
in al,cm_dat
test al,20h
jz irq8_1
push ax
int 4ah ;call user hook
pop ax
irq8_1: mov al,eoi ;signal end of interrupt
out pic1,al
out pic0,al
pop ax
iret
;
; Timer initialization -> 18.2 Hz tick
; Unmask timer and keyboard interrupts
;
tim_init: mov al,36h
out timer+3,al
#if def TICK_RATE
mov al,low(TICK_RATE) ;LSB
out timer,al
mov al,high(TICK_RATE) ;MSB
out timer,al
#else
mov al,0
out timer,al
out timer,al
#endif
in al,pic0+1 ;enable timer, keyboard interrupt
and al,11111100xb
out iowait,al
out pic0+1,al
out iowait,al
mov al,eoi
out pic0,al
ret
;
; RTC init
;
rtc_ini: mov ah,cm_d ;read status register D
call rtc_read
and al,80h ;battery low ?
jz rtc_ini0 ;:yes
; battery ok - validate the time / date
mov ah,2 ;get RTC time
int 1ah
jb rtc_ini0 ;:error
mov al,dh ;validate seconds
mov ah,60h
call rtc_val
jb rtc_ini0 ;:bad
mov al,cl ;validate minutes
mov ah,60h
call rtc_val
jb rtc_ini0 ;:bad
mov al,ch ;validate hours
mov ah,24h
call rtc_val
jb rtc_ini0 ;:bad
mov ah,4 ;get RTC date
int 1ah
mov al,dl ;day
mov ah,31h
call rtc_val
jb rtc_ini0 ;:bad
mov al,dh ;month
mov ah,12h
call rtc_val
jb rtc_ini0 ;:bad
mov ax,cx
cmp ax,1980h ;minimum 1980
jb rtc_ini0
cmp ax,2099h ;maximum 2099
ja rtc_ini0
mov ah,99h ;maximum year
call rtc_val
jb rtc_ini0 ;:bad
mov ax,cm_dia*256 ;clear diag register
call rtc_write
jmp short rtc_ini2
; battery was low or invalid time - initialize the RTC
rtc_ini0: inc byte [tmp_rtc] ;set RTC failure flag
mov si,offset rtc_tab
rtc_ini1: cs: lodsw ;get value from table
call rtc_write ;write to RTC
cmp si,offset rtc_tab9 ;end of table ?
jb rtc_ini1 ;:no
; Set timer tick value from RTC time
;
; Please note that there are different algorithms with varying
; accuracy for doing this, there can be slight time discrepancies
; depending on what algorithm is used by the OS.
rtc_ini2: mov ah,2 ;get RTC time
int 1ah
jb rtc_ini9 ;:error
mov [tmp_ss],dh ;save second for run check
mov [tmp_mm],cl ;save minute for run check
push dx
xor ebx,ebx
mov al,ch ;hour
mov edx,01000755h ;18.206 * 3600 * 256
call rtc_mul
mov al,cl ;minute
mov edx,00044464h ;18.206 * 60 * 256
call rtc_mul
pop ax ;second
mov al,ah
mov dx,4661 ;18.206 * 256
call rtc_mul
shr ebx,8 ;timer / 256
mov dword [m_timer],ebx ;set timer
mov [tmp_timer],bx ;backup for run check
rtc_ini9: ret
;
; convert number in AL from BCD -> binary, * EDX -> add to EBX
;
rtc_mul: mov ah,al ;high digit
and al,15 ;mask low digit
shr ah,4 ;high -> low nibble
aad ;convert to binary
db 066h
cbw ;clear top of eax
mul edx
add ebx,eax ;add ebx,eax
ret
;
; validate a BCD number in AL, AH = limit
;
rtc_val: cmp al,ah ;exceed limit ?
ja rtc_val9 ;(no carry -> cmc -> error)
and al,15 ;high digit is ok, now check low digit
cmp al,10 ;(less than 10 -> carry -> cmc -> ok)
rtc_val9: cmc ;return error status
ret
;
; Timer & RTC test
;
tim_test: mov ax,[m_timer] ;did we get at least one timer tick ?
cmp ax,[tmp_timer]
jnz tim_test0 ;:ok
; Could fail if floppy and IDE init were super fast. Give it
; another chance.
mov bx,60 ;wait 60 ms
call cs_waitbx
mov ax,[m_timer] ;did we get at least one timer tick ?
cmp ax,[tmp_timer]
jz tim_test8 ;no: error
tim_test0: add ax,20 ;wait max. of one second
xchg ax,bx
tim_test1: mov ah,2 ;read RTC
int 1ah
jb tim_test8 ;:error
cmp dh,[tmp_ss] ;compare second
jnz tim_test9 ;:ok
cmp cl,[tmp_mm] ;compare minute
jnz tim_test9 ;:ok
cmp bx,[m_timer] ;time-out ?
js tim_test8 ;:yes
hlt ;wait for next interrupt
jmp tim_test1 ;look again
tim_test8: inc byte [tmp_tim] ;set error flag
tim_test9: ret
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -