Metropoli BBS
VIEWER: pcmag.asm MODE: TEXT (CP437)
;╒══════════════════════════════════════════════════════════════════════════╕
;│                         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


[ RETURN TO DIRECTORY ]