Date: Sat, 1 Apr 1995 11:56:18 -0500 From: John Wilson To: jwvm AT umdsun2 DOT umd DOT umich DOT edu Subject: Re: Code for controlling PC Timer Cc: djgpp AT sun DOT soe DOT clarkson DOT edu >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