home *** CD-ROM | disk | FTP | other *** search
- ; INT70
- ;
- ; Demonstrates the use of the Real Time Clock interrupt. This program doesn't
- ; use BIOS services or the BIOS data area, but goes to the hardware directly.
- ; The IRQ masks of both PICS are also reprogrammed, since some BIOS
- ; implementations leave the Real Time Clock IRQ off by default.
- ;
- ; Also demonstrates that the interrupt doesn't have to tick at 1024 Hz per se.
- ; The base frequency is 32768 Hz (and the IBM technical reference says it
- ; should remain that value). The lowest 4 bits of status register A divide the
- ; square-wave output frequency (and the interrupt rate). The interrupt rate
- ; is: [32768 SHR (rate-1)] where "rate" is the value of the lowest 4 bits.
- ; This value must be between 3 (8,192 Hz) and 0fh (2 Hz). The default value
- ; of "rate" is 6, giving the default interrupt rate of 1024 Hz.
- ; Adjusting the interrupt frequency does not offset the time of the Real Time
- ; clock. You can set any supported interrupt rate, and the time is maintained
- ; correctly.
- ;
- ; The normal way to use the Real Time Clock is by calling the "Wait Event"
- ; function of interrupt 15h. This works, but has some drawbacks:
- ; - It fails if an int 70h hook is already installed (i.e. there is a nested
- ; call to the "Wait Event" function).
- ; - It requires a memory address where it can toggle a bit.
- ; - It turns the timer IRQ off after the time-out specified with the "Wait
- ; Event" function.
- ; - It modifies the BIOS data area, and may therefore give problems in
- ; protected mode. (Note: this program also modifies the BIOS data area, but
- ; only to enhance compatibility with the BIOS. It is not required for the
- ; operation of the program.)
- ;
- ; This program should be accompagnied by a text file that explains everything
- ; in more detail.
- ;
- ; Assembled with MASM 6.0
- ;
- ; August 25, 1993
- ; Thiadmer Riemersma (ITB CompuPhase, The Netherlands)
- ; CompuServe: 100115,2074
- ; -----
-
- .MODEL SMALL
-
- .STACK 400h ; 1K bytes is definitly enough
-
- .CODE
-
- old70int dd 0
-
- main PROC
- mov ax, @data ; set ds=@data
- mov ds, ax
-
- call installisr ; set up ISR and RTC
-
- mov ax, 0 ; wait until a key is pressed
- int 16h
-
- call uninstallisr
-
- mov ax, 4c00h ; terminate program
- int 21h
- main endp
-
-
- PAUSE equ <jmp short $+2> ; short pauses
-
-
- ; These macros were constructed by analyzing the system BIOS. It is important
- ; to:
- ; - disable interrupts while reading/writing CMOS values (even the NMI)
- ; - leave the index register (70h) point at status register D (index 0dh)
- ; - insert a pause between reading and writing to the ports
- ; - always access port 71h (by reading or writing) after setting the index of
- ; the index register (70h)
- ; -----
- ReadRTC macro index
- pushf ;; save flags
- cli ;; no interrupts while changing CMOS values
- mov ax, index
- or al, 80h ;; set NMI bit, disable even NMI
- out 70h, al
- PAUSE ;; pause between accessing RTC ports
- in al, 71h
- push ax ;; save value read at indicated index
- PAUSE
- mov al, 0dh ;; leave index at status register D, and...
- out 70h, al ;; ...enable NMI again
- PAUSE ;; *always* read/write port 71h after...
- in al, 71h ;; ...writing to port 70h
- pop ax
- popf ;; restore flags (includes the interrupt flag)
- endm
-
- SetRTC macro index, value
- pushf ;; save flags
- push ax
- cli ;; No interrupts while changing CMOS values
- mov ax, index
- or al, 80h ;; set NMI bit, disable even NMI
- out 70h, al ;; write index
- PAUSE
- ifdifi <value>, <al>
- mov al, value ;; value is not "al", move it into al
- else
- pop ax ;; value is "al", restore "al" from the stack
- push ax ;; save ax again
- endif
- out 71h, al ;; write value at indicated index
- PAUSE
- mov al, 0dh ;; leave index at status register D, and...
- out 70h, al ;; ...enable NMI again
- PAUSE ;; *always* read/write port 71h after...
- in al, 71h ;; ...writing to port 70h
- pop ax ;; restore ax
- popf ;; restore flags (including interrupt flag)
- endm
-
-
- installisr proc
- ; Install ISR for Real Time Clock (RTC)
- mov ax, 3570h ; get interrupt vector 70h (RTC)
- int 21h
- mov word ptr cs:[old70int], bx ; store the curent vector
- mov word ptr cs:[old70int+2], es
- mov dx, offset cs:int70proc ; new ISR for int 70h
- push ds ; save ds
- mov ax, @code
- mov ds, ax ; copy CS to DS
- mov ax, 2570h ; set interrupt vector nr. 70h
- int 21h
- pop ds ; restore ds
-
- ; Now initialize the RTC
- ReadRTC 0bh ; read status register B
- or al, 40h ; set periodic interrupt bit
- SetRTC 0bh, al
- ReadRTC 0ch ; clear pending interrupt with a read
-
- ; Alter the interrupt rate (to the slowest rate: 2 ticks/second)
- ReadRTC 0ah ; alter the interrupt rate
- and al, 0f0h ; clear "rate selection bits"
- or al, 0fh ; set rate selection to 2 ticks/second
- SetRTC 0ah, al
-
- ; Finally, initialize the secondary PIC
- cli ; no interrupts while programmning PIC
- in al, 0a1h ; read mask of secondary PIC
- and al, 0feh ; clear bit 0 (IRQ8=RTC)
- PAUSE ; pause between reads and writes
- out 0a1h, al ; store mask
- sti ; re-enable interrupts
- ret
- installisr endp
-
-
- uninstallisr proc
- ; Clean up the RTC
- ReadRTC 0bh ; read status register B
- and al, 0bfh ; clear periodic interrupt bit
- SetRTC 0bh, al
- ReadRTC 0ah ; reset the interrupt rate
- and al, 0f0h ; clear "rate selection bits"
- or al, 06h ; set rate selection to 1024 Hz
- SetRTC 0ah, al
-
- ; Reset interrupt vectors
- push ds ; save ds
- mov ax, 2570h ; reset int 70h
- lds dx, cs:[old70int] ; ds:dx -> old interrupt vector
- int 21h
- pop ds ; reset ds
-
- ; Most BIOSes never reset the secondary PIC, so we don't either. IRQ 8
- ; is no longer generated anyway.
- ret
- uninstallisr endp
-
-
- ; The new int 70h ISR. Toggles the "speaker enable" bit to get an audible
- ; indication that the ISR works.
- ;
- ; Note:
- ; We don't call the previous interrupt 70h handler, because the default
- ; handler in the BIOS adjusts fields in the BIOS data area and may switch
- ; itself off after a specified time-out. The problems for dealing with the
- ; default interrupt handler is probably the main reason that the RTC is not
- ; more widely used.
- ; -----
- int70proc proc FAR
- push ax
- push es
- in al, 61h ; read current value of register 61h
- xor al, 2 ; toggle 2nd bit
- out 61h,al ; enable/disable speaker
-
- ReadRTC 0ch ; read status register C to clear...
- ; ...pending interrupt
- cli ; avoid interrupts between both EOIs
- mov al, 20h ; send non-specific EOI...
- out 0a0h, al ; ...to secondary PIC (slave) and...
- out 20h, al ; ...to primary PIC (master)
- pop es
- pop ax
- iret
- int70proc endp
-
-
- end main
-