;╒══════════════════════════════════════════════════════════════════════════╕ ;│ CMOS Handling Functions │ ;╘══════════════════════════════════════════════════════════════════════════╛ ;╒══════════════════════════════════════════════════════════════════════════╕ ;│ CONSTANTS │ ;╘══════════════════════════════════════════════════════════════════════════╛ PERIODIC_INT_ACTIVE equ 40h PERIODIC_INT_OFF equ 0BFh UPDATE_INT_ACTIVE equ 10h ALARM_INT_ACTIVE equ 20h CMOS_ADDRESS equ 70h CMOS_DATA equ 71h DEFAULT_CMOS_ADDRESS equ 0Dh DISABLE_NMI equ 80h BIOS_WAIT_FLAG equ 0A0h WAIT_TIME_LOW equ 9Ch WAIT_TIME_HIGH equ 9Eh ON equ 01h OFF equ 00h RTC_SECONDS equ 00h ALARM_SECONDS equ 01h RTC_MINUTES equ 02h ALARM_MINUTES equ 03h RTC_HOURS equ 04h ALARM_HOURS equ 05h DAY_OF_WEEK equ 06h DAY_OF_MONTH equ 07h RTC_MONTH equ 08h RTC_YEAR equ 09h RTC_STATUS_A equ 0Ah UPDATING_RTC equ 80h RTC_STATUS_B equ 0Bh ENABLE_PERIODIC_INTS equ 40h ; 01000000b ENABLE_UPDATE_INTS equ 10h DISABLE_UPDATE_INTS equ 0EFh RTC_STATUS_C equ 0Ch RTC_STATUS_D equ 0Dh CMOS_DIAG_RESULTS equ 0Eh RTC_LOST_POWER equ 80h BAD_CHKSUM equ 40h BAD_CONFIG equ 20h MEM_SIZE_BAD equ 10h DISK_FAILURE equ 08h TIME_INVALID equ 04h CMOS_FLOPPY_TYPE equ 10h ALARM_FUNCTION equ 4Ah ;╒══════════════════════════════════════════════════════════════════════════╕ ;│ VARIABLES │ ;╘══════════════════════════════════════════════════════════════════════════╛ OriginalRTCVector dd 0000 ; Vector to the ; INT 70h handler TimeOutCounter dw 0000 ; Time out variable CallingFlags dw 0000 ; Holding location ; for current flags CallingStatusB db 00 ; Original RTC Status ; B INT70hFlags db 00 ; Flags used during ; interrupt tests ; 1 - ON = Use our INT 70h handler ;╒══════════════════════════════════════════════════════════════════════════╕ ;│ ROUTINES │ ;╘══════════════════════════════════════════════════════════════════════════╛ ;┌──────────────────────────────────────────────────────────────────────────┐ ;│ ReadCMOSByte │ ;│ This routine will read a byte from the CMOS and return it to the │ ;│ caller. │ ;│ Call With : AL = CMOS Byte Address │ ;│ Alters : AL │ ;│ Returns : AL = Data byte │ ;│ Calls : Nothing │ ;└──────────────────────────────────────────────────────────────────────────┘ ReadCMOSByte: pushf ; Save current flags or al, DISABLE_NMI ; Make sure to turn off NMI cli ; Disable system interrupts out CMOS_ADDRESS, al ; Send address and disable ; NMI jmp $+2 ; I/O delay jmp $+2 in al, CMOS_DATA ; Get the byte of data jmp $+2 ; I/O delay jmp $+2 push ax ; Save byte of data mov al, 0Dh ; Get a default address out CMOS_ADDRESS, al ; Select that address jmp $+2 ; I/O delay jmp $+2 pop ax ; Restore saved byte ;-------------------------------------------------------------------------- push cs ; Flags are already on stack Call Handle286Bug ; push CS to imitate INT ret ; Return with flags restored ;────────────────────────────────────────────────────────────────────────── Handle286Bug: iret ; Pop flags from stack ; without 286 bug ;┌──────────────────────────────────────────────────────────────────────────┐ ;│ WriteCMOSByte │ ;│ This routine will write a byte to the CMOS and return to the │ ;│ caller. │ ;│ Call With : AH = Value to write │ ;│ AL = CMOS Byte to write to │ ;│ Alters : AX │ ;│ Returns : AL = Data byte │ ;│ Calls : Nothing │ ;└──────────────────────────────────────────────────────────────────────────┘ WriteCMOSByte: pushf ; Save current flags or al, DISABLE_NMI ; Make sure to disable NMI ;-------------------------------------------------------------------------- cli ; Disable system interrupts out CMOS_ADDRESS, al ; Send address and disable ; NMI jmp $+2 ; I/O delay jmp $+2 ;-------------------------------------------------------------------------- xchg al, ah ; Move byte to write into ; al out CMOS_DATA, al ; Write the byte of data jmp $+2 ; I/O delay jmp $+2 ;-------------------------------------------------------------------------- mov al, RTC_STATUS_D ; Get a default address out CMOS_ADDRESS, al ; Select that address jmp $+2 ; I/O delay jmp $+2 ;-------------------------------------------------------------------------- push cs ; Restore machine state and Call Handle286Bug ; leave ret ;┌──────────────────────────────────────────────────────────────────────────┐ ;│ Install RTC Interrupt Handler │ ;│ This routine will install the routines to intercept INT 70h (used by │ ;│ the Real Time Clock Interrupts) and replace it with our code │ ;│ Call With : Nothing │ ;│ Alters : Nothing │ ;│ Returns : Nothing │ ;│ Calls : Nothing │ ;└──────────────────────────────────────────────────────────────────────────┘ InstallRTCInterruptHandler: push ax ; Save calling registers push bx push dx push es ;────────────────────────────────────────────────────────────────────────── mov ax, (DOS_GET_VECTOR * 256) + REAL_TIME_CLOCK_INT int DOS_FUNCTION ; Get and save the INT 70h mov WORD PTR OriginalRTCVector, bx ; vector for later mov WORD PTR OriginalRTCVector + 2, es ; use mov dx, OFFSET RTCInterruptHandler ; Replace the RTC ; int with our counter mov ax, (DOS_SET_VECTOR * 256) + REAL_TIME_CLOCK_INT int DOS_FUNCTION ;────────────────────────────────────────────────────────────────────────── in al, PIC_TWO_MASK ; Get the current enabled jmp $+2 ; system interrupt from jmp $+2 ; the second programmable ; interrupt controller and al, TURN_ON_RTC ; Turn on IRQ 8, the RTC ; system interrupt out PIC_TWO_MASK, al ; Send the value back to jmp $+2 ; the programmable interrupt jmp $+2 ; controller (PIC) ;────────────────────────────────────────────────────────────────────────── pop es ; Restore calling registers pop dx pop bx pop ax ret ;┌──────────────────────────────────────────────────────────────────────────┐ ;│ Remove RTC Interrupt Handler │ ;│ This routine will replace the original INT 70h handler. │ ;│ Call With : Nothing │ ;│ Alters : Nothing │ ;│ Returns : Nothing │ ;│ Calls : Nothing │ ;└──────────────────────────────────────────────────────────────────────────┘ RemoveRTCInterruptHandler: push ds ; Save calling registers push dx push ax ;────────────────────────────────────────────────────────────────────────── mov dx, WORD PTR OriginalRTCVector ; Get original RTC mov ds, WORD PTR OriginalRTCVector + 2 ; vector and mov ax, (DOS_SET_VECTOR * 256) + REAL_TIME_CLOCK_INT int DOS_FUNCTION ; replace it ;────────────────────────────────────────────────────────────────────────── pop ax ; Restore calling registers pop dx pop ds ret ;┌──────────────────────────────────────────────────────────────────────────┐ ;│ Real Time Clock INT Handler │ ;│ This routine handles the IRQ generated by the RTC 1024 times per │ ;│ second. It will simply add one to our counters and pass control │ ;│ onto the original vector. │ ;│ Call With : THIS ROUTINE IS NOT CALLED │ ;│ Alters : Nothing │ ;│ Returns : Nothing │ ;│ Calls : Nothing │ ;└──────────────────────────────────────────────────────────────────────────┘ RTCInterruptHandler: push ds ; Save the calling registers push ax ;────────────────────────────────────────────────────────────────────────── GetActiveINTs: mov ax, RTC_STATUS_B * 256 + RTC_STATUS_C ; Get status Call ReadCMOSByte ; registers B and C. AND xchg al, ah ; them together to get ints Call ReadCMOSByte ; that are both active and and al, ah ; enabled. Also and off any and al, 01110000b ; unused bits. or al, al jz AllINTsHandled ;────────────────────────────────────────────────────────────────────────── test al, PERIODIC_INT_ACTIVE jz NoPeriodicInt ;---------------------- Handle The Periodic Interrupt ----------------------- Call HandlePeriodicInt test BYTE PTR DS:[BIOS_WAIT_FLAG], ON jz NoPeriodicInt ;----------------------------- BIOS Wait Active ----------------------------- push ax mov ax, BIOS_RAM_SEG mov ds, ax sub WORD PTR DS:[WAIT_TIME_LOW], 0976 sbb WORD PTR DS:[WAIT_TIME_HIGH], 0 jnc FinishedBIOSWait ;------------- BIOS Wait Finished, Maybe Turn Off Periodic Int -------------- mov BYTE PTR DS:[BIOS_WAIT_FLAG], OFF test Int70hFlags, PERIODIC_INT_ACTIVE jnz FinishedBIOSWait ;----------------------- Disable Periodic Interrupts ------------------------ mov al, RTC_STATUS_B Call ReadCMOSByte and al, PERIODIC_INT_OFF xchg ah, al mov al, RTC_STATUS_B Call WriteCMOSByte ;-------------------------------------------------------------------------- FinishedBIOSWait: pop ax ;────────────────────────────────────────────────────────────────────────── NoPeriodicInt: test al, UPDATE_INT_ACTIVE jz NoUpdateInt ;-------------------------------------------------------------------------- Call HandleUpdateInt ;────────────────────────────────────────────────────────────────────────── NoUpdateInt: test al, ALARM_INT_ACTIVE jz NoAlarmInt ;-------------------------------------------------------------------------- sti int ALARM_FUNCTION cli ;────────────────────────────────────────────────────────────────────────── NoAlarmInt: jmp GetActiveINTs ;────────────────────────────────────────────────────────────────────────── AllINTsHandled: mov al, END_OF_INT ; Send the end of interrupt out PIC_TWO, al ; signal to the two PICs out PIC_ONE, al pop ax ; Restore our saved values pop ds iret HandlePeriodicInt: ret HandleUpdateInt: ret