2020-09-30 17:17:25 +02:00

565 lines
14 KiB
NASM

title "Cmos Access Routines"
;++
;
; Module Name:
;
; ixcmos.asm
;
; Abstract:
;
; Procedures necessary to access CMOS/ECMOS information.
;
; Author:
;
; David Risner (o-ncrdr) 20 Apr 1992
;
; Revision History:
;
; Landy Wang (corollary!landy) 04 Dec 1992
; - Move much code from ixclock.asm to here so different HALs
; can reuse the common functionality.
;
;--
.386p
.xlist
include hal386.inc
include callconv.inc ; calling convention macros
include i386\ix8259.inc
include i386\ixcmos.inc
.list
EXTRNP _DbgBreakPoint,0
_DATA SEGMENT DWORD PUBLIC 'DATA'
;
; Holds the value of the eflags register before a cmos spinlock is
; acquired (used in HalpAcquire/ReleaseCmosSpinLock().
;
_HalpHardwareLockFlags dd 0
_DATA ends
_TEXT SEGMENT DWORD PUBLIC 'CODE'
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
page ,132
subttl "Read System Time"
;++
;
; BOOLEAN
; HalQueryRealTimeClock (
; PTIME_FIELDS TimeFields
; )
;
; Routine Description:
;
; This routine reads current time from CMOS memory and stores it
; in the TIME_FIELDS structure passed in by caller.
;
; Arguments:
;
; TimeFields - A pointer to the TIME_FIELDS structure.
;
; Return Value:
;
; If the power to the realtime clock has not failed, then the time
; values are read from the realtime clock and a value of TRUE is
; returned. Otherwise, a value of FALSE is returned.
;
;--
;
; Parameters:
;
KrctPTimeFields equ [esp+4]
cPublicProc _HalQueryRealTimeClock ,1
if DBG
krctwait0:
mov ecx, 100
krctwait:
push ecx
else
krctwait:
endif
stdCall _HalpAcquireCmosSpinLock
mov ecx, 100
align 4
krct00: mov al, 0Ah ; Specify register A
CMOS_READ ; (al) = CMOS register A
test al, CMOS_STATUS_BUSY ; Is time update in progress?
jz short krct10 ; if z, no, go read CMOS time
loop short krct00 ; otherwise, try again.
;
; CMOS is still busy. Try again ...
;
stdCall _HalpReleaseCmosSpinLock
if DBG
pop ecx
loop short krctwait
stdCall _DbgBreakPoint
jmp short krctwait0
else
jmp short krctwait
endif
align 4
if DBG
krct10:
pop ecx
else
krct10:
endif
mov edx, KrctPTimeFields ; (edx)-> TIME_FIELDS structure
xor eax, eax ; (eax) = 0
;
; The RTC is only accurate within one second. So
; add a half a second so that we are closer, on average,
; to the right answer.
;
mov word ptr [edx].TfMilliseconds, 500 ; add a half a second
mov al, RTC_OFFSET_SECOND
CMOS_READ ; (al) = second in BCD form
BCD_TO_BIN ; (ax) = second
mov [edx].TfSecond, ax ; set second in TIME_FIELDS
mov al, RTC_OFFSET_MINUTE
CMOS_READ ; (al) = minute in BCD form
BCD_TO_BIN ; (ax) = Minute
mov [edx].TfMinute, ax ; set minute in TIME_FIELDS
mov al, RTC_OFFSET_HOUR
CMOS_READ ; (al) = hour in BCD form
BCD_TO_BIN ; (ax) = Hour
mov [edx].TfHour, ax ; set hour in TIME_FIELDS
mov al, RTC_OFFSET_DAY_OF_WEEK
CMOS_READ ; (al) = day-of-week in BCD form
BCD_TO_BIN ; (ax) = day-of-week
mov [edx].TfWeekday, ax ; set Weekday in TIME_FIELDS
mov al, RTC_OFFSET_DATE_OF_MONTH
CMOS_READ ; (al) = date-of-month in BCD form
BCD_TO_BIN ; (ax) = date_of_month
mov [edx].TfDay, ax ; set day in TIME_FIELDS
mov al, RTC_OFFSET_MONTH
CMOS_READ ; (al) = month in BCD form
BCD_TO_BIN ; (ax) = month
mov [edx].TfMonth, ax ; set month in TIME_FIELDS
mov al, RTC_OFFSET_YEAR
CMOS_READ ; (al) = year in BCD form
BCD_TO_BIN ; (ax) = year
push eax ; save year in stack
mov al, RTC_OFFSET_CENTURY
CMOS_READ ; (al) = century byte in BCD form
BCD_TO_BIN ; (ax) = century
mov ah, 100
mul ah ; (ax) = century * 100
pop ecx ; (cx) = year
add ax, cx ; (ax)= year
cmp ax, 1900 ; Is year > 1900
jb short krct40
cmp ax, 1920 ; and < 1920
jae short krct40
add ax, 100 ; Compensate for century field
krct40:
mov [edx].TfYear, ax ; set year in TIME_FIELDS
stdCall _HalpReleaseCmosSpinLock
mov al, 1 ; return TRUE
stdRET _HalQueryRealTimeClock
stdENDP _HalQueryRealTimeClock
page ,132
subttl "Write System Time"
;++
;
; BOOLEAN
; HalSetRealTimeClock (
; PTIME_FIELDS TimeFields
; )
;
; Routine Description:
;
; This routine writes current time from TIME_FILEDS structure
; to CMOS memory.
;
; Arguments:
;
; TimeFields - A pointer to the TIME_FIELDS structure.
;
; Return Value:
;
; If the power to the realtime clock has not failed, then the time
; values are written to the realtime clock and a value of TRUE is
; returned. Otherwise, a value of FALSE is returned.
;
;--
;
; Parameters:
;
KrctPTimeFields equ [esp+4]
cPublicProc _HalSetRealTimeClock ,1
if DBG
kwctwait0:
mov ecx, 100
kwctwait:
push ecx
else
kwctwait:
endif
stdCall _HalpAcquireCmosSpinLock
mov ecx, 100
align 4
kwct00: mov al, 0Ah ; Specify register A
CMOS_READ ; (al) = CMOS register A
test al, CMOS_STATUS_BUSY ; Is time update in progress?
jz short kwct10 ; if z, no, go write CMOS time
loop short kwct00 ; otherwise, try again.
;
; CMOS is still busy. Try again ...
;
stdCall _HalpReleaseCmosSpinLock
if DBG
pop ecx
loop short kwctwait
stdCall _DbgBreakPoint
jmp short kwctwait0
else
jmp short kwctwait
endif
align 4
if DBG
kwct10:
pop ecx
else
kwct10:
endif
mov edx, KrctPTimeFields ; (edx)-> TIME_FIELDS structure
mov al, [edx].TfSecond ; Read second in TIME_FIELDS
BIN_TO_BCD
mov ah, al
mov al, RTC_OFFSET_SECOND
CMOS_WRITE
mov al, [edx].TfMinute ; Read minute in TIME_FIELDS
BIN_TO_BCD
mov ah, al
mov al, RTC_OFFSET_MINUTE
CMOS_WRITE
mov al, [edx].TfHour ; Read Hour in TIME_FIELDS
BIN_TO_BCD
mov ah, al
mov al, RTC_OFFSET_HOUR
CMOS_WRITE
mov al, [edx].TfWeekDay ; Read WeekDay in TIME_FIELDS
BIN_TO_BCD
mov ah, al
mov al, RTC_OFFSET_DAY_OF_WEEK
CMOS_WRITE
mov al, [edx].TfDay ; Read day in TIME_FIELDS
BIN_TO_BCD
mov ah, al
mov al, RTC_OFFSET_DATE_OF_MONTH
CMOS_WRITE
mov al, [edx].TfMonth ; Read month in TIME_FIELDS
BIN_TO_BCD
mov ah, al
mov al, RTC_OFFSET_MONTH
CMOS_WRITE
mov ax, [edx].TfYear ; Read Year in TIME_FIELDS
cmp ax, 9999
jbe short kwct15
mov ax, 9999
align 4
kwct15:
mov cl, 100
div cl ; [ax]/[cl]->al=quo, ah=rem
push eax
BIN_TO_BCD
mov ah, al ; [ah] = Century in BCD form
mov al, RTC_OFFSET_CENTURY
CMOS_WRITE
pop eax
mov al, ah ; [al] = Year
BIN_TO_BCD
mov ah, al ; [ah] = year in BCD
mov al, RTC_OFFSET_YEAR
CMOS_WRITE
stdCall _HalpReleaseCmosSpinLock
mov al, 1 ; return TRUE
stdRET _HalSetRealTimeClock
stdENDP _HalSetRealTimeClock
page ,132
subttl "CMOS Subrange Validity Check"
;++
;
; BOOLEAN
; HalpIsCmosSubrangeValid(
; IN ULONG NumberOfBytes,
; IN ULONG RegisterNumber
; )
;
; Routine Description:
;
; This routine checks if the CMOS subrange data is valid.
;
; Arguments:
;
; NumberOfBytes - Number of bytes in the subrange.
;
; RegisterNumber - First register of the subrange to check.
;
; Return Value:
;
; Returns ZF=1 if data stored in the CMOS subrange is valid, else ZF=0.
;
;--
cPublicFastCall HalpIsCmosSubrangeValid, 2
mov ah, 0AAh ; opposite of first expected bit pattern
icsv10: mov al, dl ; CMOS register to index
ECMOS_READ
not ah ; flip bit pattern
inc edx
cmp al, ah ; check if the byte matches
loope icsv10 ; loop while bytes remaining and equal
fstRET HalpIsCmosSubrangeValid
fstENDP HalpIsCmosSubrangeValid
page ,132
subttl "CMOS Validity Check"
;++
;
; BOOLEAN
; HalIsCmosValid(
; VOID
; )
;
; Routine Description:
;
; This routine checks if the data stored in the CMOS is valid.
;
; Arguments:
;
; None.
;
; Return Value:
;
; Returns TRUE if the data stored in the CMOS is valid, else FALSE.
;
;--
cPublicProc _HalIsCmosValid, 0
stdCall _HalpAcquireCmosSpinLock
;
; Break the check of the CMOS data into two subranges, because some of the
; registers are used for the real time clock or control registers.
;
mov ecx, 60h
mov edx, 10h
fstCall HalpIsCmosSubrangeValid
jne icv10
mov ecx, 80h
mov edx, ecx
fstCall HalpIsCmosSubrangeValid
icv10: sete al
stdCall _HalpReleaseCmosSpinLock
stdRET _HalIsCmosValid
stdENDP _HalIsCmosValid
page ,132
subttl "CMOS Subrange Validity Check"
;++
;
; VOID
; HalpMarkCmosSubrangeValid(
; IN ULONG NumberOfBytes,
; IN ULONG RegisterNumber
; )
;
; Routine Description:
;
; This routine checks if the CMOS subrange data is valid.
;
; Arguments:
;
; NumberOfBytes - Number of bytes in the subrange.
;
; RegisterNumber - First register of the subrange to check.
;
; Return Value:
;
; Returns ZF=1 if data stored in the CMOS subrange is valid, else ZF=0.
;
;--
cPublicFastCall HalpMarkCmosSubrangeValid, 0
mov ah, 055h ; first expected bit pattern
mcrv10: mov al, dl ; CMOS register to index
ECMOS_WRITE
not ah ; flip bit pattern
inc edx
loop mcrv10
fstRET HalpMarkCmosSubrangeValid
fstENDP HalpMarkCmosSubrangeValid
page ,132
subttl "Marks CMOS data as valid"
;++
;
; VOID
; HalMarkCmosValid(
; VOID
; )
;
; Routine Description:
;
; This routine sets the real time clock data to the valid state.
;
; Arguments:
;
; None.
;
; Return Value:
;
; None.
;
;--
cPublicProc _HalMarkCmosValid, 0
stdCall _HalpAcquireCmosSpinLock
;
; Break the marking of the CMOS data into two subranges, because some of the
; registers are used for the real time clock or control registers.
;
mov ecx, 60h
mov edx, 10h
fstCall HalpMarkCmosSubrangeValid
mov ecx, 80h
mov edx, ecx
fstCall HalpMarkCmosSubrangeValid
stdCall _HalpReleaseCmosSpinLock
stdRET _HalMarkCmosValid
stdENDP _HalMarkCmosValid
;++
;
; Routine Description:
;
; Acquires a spinlock to access the cmos chip. The cmos chip is
; accessed at different irql levels, so to be safe, we 'cli'.
; We could replace that to raise irql to PROFILE_LEVEL, but that's
; a lot of code.
;
; Arguments:
;
; None
;
; Return Value:
;
; Interrupt is disabled.
; Irql level not affected.
; Flags saved in _HalpHardwareLockFlags.
;--
cPublicProc _HalpAcquireCmosSpinLock ,0
pushfd
cli
pop _HalpHardwareLockFlags ; save flags for release S.L.
stdRET _HalpAcquireCmosSpinLock
stdENDP _HalpAcquireCmosSpinLock
;++
;
; Routine Description:
;
; Release spinlock, and restore flags to the state it was before
; acquiring the spinlock.
;
; Arguments:
;
; None
;
; Return Value:
;
; Interrupts restored to their state before acquiring spinlock.
; Irql level not affected.
;
;--
cPublicProc _HalpReleaseCmosSpinLock ,0
;
; restore eflags as it was before acquiring spinlock. Put it on
; stack before releasing spinlock (so other cpus cannot overwrite
; it with their own eflags).
;
push _HalpHardwareLockFlags ; old eflags on stack.
popfd ; restore eflags.
stdRET _HalpReleaseCmosSpinLock
stdENDP _HalpReleaseCmosSpinLock
_TEXT ends
end