.data

    prevIsr8  dd 0
    tickCount dd 0
    counter   dw 0

.code

InitFastTimer proc

    ; Hook the system timer tick interrupt (interrupt 8).
    ; The Get Interrupt Vector returns the current vector
    ; in ES:BX.
    ;
    mov ax, 3508h
    int 21h
    mov WORD PTR prevIsr8, bx
    mov WORD PTR prevIsr8[2], es

    ; The Set Interrupt Vector function expects the
    ; address of the new handler in DS:DX.
    ;
    push ds
    push cs
    pop ds
    mov dx, OFFSET Isr8
    mov ax, 2508h
    int 21h
    pop ds

    ; Reprogram system timer 0 to provide a higher resolution.
    ; The system timers share a 1193182Hz clock, and system
    ; timer 0 is normally loaded with an initial count of zero.
    ; This causes it to count 65536 clock cycles between each
    ; interrupt, generating 1193182 / 65536 ~ 18.2 interrupts
    ; per second. An initial count of 1192 will produce an
    ; interrupt rate of ~1000.99 interrupts per second.
    ;
    ; For reference, running on a 112MHz Pentium-class processor,
    ; speeding up the timer tick interrupt by a factor of 100,000
    ; worked OK.
    ;
    ; Program system timer 0 for LSB then MSB, mode 3, binary.
    ; The system timer control word register is at I/O port 43h.
    ; The system timer control word is set as follows:
    ;   bit 7-6: 00     = timer 0
    ;   bit 5-4: 11     = R/W LSB then MSB
    ;   bit 3-1: 011    = mode 3, periodic square wave
    ;   bit 0:   0     	= binary
    ;
    cli
    mov al, 00110110b
    out 43h, al
    jmp $+2
    mov ax, 1192
    out 40h, al
    jmp $+2
    mov al, ah
    out 40h, al

    ; Program system timer 2 for LSB then MSB, mode 3, binary.
    ;   bit 7-6: 10     = timer 2
    ;   bit 5-4: 11     = R/W LSB then MSB
    ;   bit 3-1: 011    = mode 3, periodic square wave
    ;   bit 0:   0     	= binary
    ;
    mov al, 10110110b
    out 43h, al
    sti

    ret

InitFastTimer endp

TermFastTimer proc

    ; Restore system timer 0 to normal.
    ;
    cli
    mov al, 00110110b
    out 43h, al
    jmp $+2
    xor al, al
    out 40h, al
    jmp $+2
    out 40h, al
    sti

    ; Unhook the system timer tick interrupt.
    ;
    push ds
    mov dx, WORD PTR prevIsr8
    mov ds, WORD PTR prevIsr8[2]
    mov ax, 2508h
    int 21h
    pop ds

    ret

TermFastTimer endp

; This is the new handler for the system timer tick interrupt.
; The SS override is necessary because DS may not be set to
; our data segment when the interrupt is called. For the
; Microsoft memory models other than tiny, the startup code
; will effectively move the stack into DGROUP and set SS=DS.

Isr8 proc

    pushf

    ; Update the tick count.
    ;
    inc ss:tickCount

    ; Pass the interrupt to the previous interrupt 8
    ; handler every 55 interrupts. This will allow the
    ; system timer tick to function at close to its
    ; normal rate (actually ~.037% lower than normal).
    ;
    inc ss:counter
    cmp ss:counter, 55
    jb  @F
    mov ss:counter, 0
    popf
    jmp ss:prevIsr8

  @@:

    ; For a hardware interrupt the last handler in the
    ; handler chain must issue an EOI to the interrupt
    ; controller.
    ;
    cli
    push ax
    mov al, 20h
    out 20h, al
    pop ax
    popf

    iret

Isr8 endp

Sleep proc wMilliseconds:WORD

    movzx eax, wMilliseconds
    add eax, tickCount
  @@:
    cmp tickCount, eax
    jb @B
    ret

Sleep endp
