Mail Archives: djgpp/1995/04/01/13:02:50
>I recall several discussions regarding code for speeding up the PC timer
>so that interrupts could occur at a much faster rate but the time-of-day
>ISR would still be serviced properly. However, this code did not seem to
>be on SimTel and was not mentioned in the new expanded FAQ. Does anyone
>know where this code can be found?
[I'm not on the list, your msg was forwarded to me by Paul Koning]
I've written code to do this for a simulator I'm working on, which simulates
a machine that has 50/60 Hz clock interrupts. What you do is go ahead and
set timer 0 for your own rate, but then maintain a counter of the number
of clocks since the last BIOS clock tick. On each interrupt, you add the
number of clocks since the last interrupt (i.e. the divisor that you set to
get your rate) to this counter, and every time the counter wraps past 65536
you call the BIOS ISR. This way there's a little jitter in the BIOS ticks,
but no cumulative error so the DOS clock is still OK when you exit.
I hacked my code out of the program and included it below. It's for real
mode but should be easy to convert.
John Wilson
--------------------------------
From Ersatz-11 V1.1 BETA:
; Sample divisors for 50 and 60 Hz
hz50= 23864d ;50 Hz timer divisor =14318180./(12.*50.)
hz60= 19886d ;60 Hz timer divisor =14318180./(12.*60.)
timer_low=word ptr 046Ch ;low word of system BIOS time
; sample call to set up faster ints:
mov cx,hz60 ;divisor for 60 Hz
mov ax,cs ;point at our ISR
mov dx,offset clkisr
mov di,offset dat:clkold ;dword buf for current vector
call clkset ;go set up the clock
[...]
; sample call to restore things before exiting:
xor cx,cx ;divisor=0 for usual 18.2 Hz rate
mov dx,ds:clkold ;get old ISR address
mov ax,ds:clkold+2
mov di,offset dat:clkold ;go ahead and overwrite it, we're exiting
call clkset ;restore clock
[...]
;+
;
; Set/restore clock ISR.
;
; cx divisor
; 0 for regular PC rate (1/18.2 sec)
; HZ60 for 60 Hz rate
; HZ50 for 50 Hz rate
; ax:dx ptr to timer ISR
; ds:di ptr to dword buf for old vector
;
;-
clkset: push ds ;save
push ax ;save seg of ISR
mov ax,3508h ;func=get INT 08h vector
int 21h
mov ds:[di],bx ;save
mov ds:[di+2],es
; wait for leading edge of a BIOS clock tick
; (so we don't screw up the PC's time of day too much)
xor bx,bx ;load 0
mov es,bx ;into es
mov bx,timer_low ;low word of BIOS clock
mov ax,es:[bx] ;get it
@@1: cmp ax,es:[bx] ;has it changed?
je @@1 ;spin until it does (up to 1/18.2 sec)
cli ;ints off
mov al,36h ;;timer 0, lsb/msb
out 43h,al
mov al,cl ;;LSB
out 40h,al
mov al,ch ;;MSB
out 40h,al
mov ds:clkcnt,0 ;;init simulated timer #0 counter
sti ;;(ints back on after next inst)
mov ds:clkinc,cx ;;set # clock counts per tick
pop ds ;catch seg addr of ISR
mov ax,2508h ;func=set INT 08h vector
int 21h ;to ds:dx
pop ds ;catch original ds
ret
;+
;
; 60 Hz clock ISR.
;
; This routine keeps track of number of clocks the timer chip has counted, and
; calls the BIOS timer during the 60 Hz tick at or after each time the BIOS
; timer would have overflowed. So BIOS doesn't get its interrupts at exactly
; even intervals but there's no cumulative error, so the DOS/BIOS time-of-day
; clock will be correct when we exit.
;
;-
public clkisr
clkisr: push ax ;;save
push ds
mov ax,seg dat ;;establish addressability
mov ds,ax
;;; do whatever processing we need at our higher tick rate
; see if it's time for a BIOS clock tick
mov ax,ds:clkinc ;;get # cycles to increment by
add ds:clkcnt,ax ;;bump clock, see if 1/18.2 sec has passed
jc @@4 ;;yes (happens every 65536 counts)
mov al,20h ;;generic EOI cmd
out 20h,al ;;send to 8259A
pop ds ;;restore
pop ax
iret
@@4: ; BIOS 54ms tick
pushf ;;simulate INT instruction
call dword ptr ds:clkold ;;chain old handler, send EOI cmd to 8259A
pop ds ;;restore
pop ax
iret ;;(restore int enable flag)
; in data segment:
clkcnt dw 1 dup(?) ;simulated timer #0 counter (BIOS int on carry)
clkinc dw 1 dup(?) ;# of counts to add to above for each tick
clkold dw 2 dup(?) ;FAR ptr to old clock ISR
- Raw text -