                title           -  Animated "o" system program

vectors         segment at 0
                ORG     8h*4
timer   label   dword
                ORG     16h*4
kbio    label   dword
vectors         ends

cseg            segment para
                assume  cs:cseg
                org     100h

begin:          jmp     init_vectors            ; go initialize vectors


;-------------------------------;
;                               ;
;  data area for this program   ;
;                               ;
;-------------------------------;

old_timer       dd      ?       ; Old timer interrupt location
old_kbio        dd      ?       ; Old keyboard I/O interrupt location
timer_tick      db      0       ; set if timer ticked
in_kbio         db      0
which           db      0       ; selector in "o_table"
csize           db      10h     ; size of character in bytes in "o_table"
enabled         db      1       ; spinning "o"s enabled flag
shift_ticks     db      0
o_table:                        ; font table of spinning "o"s
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00110000b
        db      01100000b
        db      11100000b
        db      11010110b
        db      11001110b
        db      01101100b
        db      00111000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b

        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00111000b
        db      01101100b
        db      11010000b
        db      11010000b
        db      11010000b
        db      01101100b
        db      00111000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b

        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00111000b
        db      01101100b
        db      11001110b
        db      11010110b
        db      11100000b
        db      01100000b
        db      00110000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b

        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00111000b
        db      01101100b
        db      11000110b
        db      11111110b
        db      11000110b
        db      01000100b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b

        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00111000b
        db      01101100b
        db      11100110b
        db      11010110b
        db      00001110b
        db      00001100b
        db      00011000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b

        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00111000b
        db      01101100b
        db      00010110b
        db      00010110b
        db      00010110b
        db      01101100b
        db      00111000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b

        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00011000b
        db      00001100b
        db      00001110b
        db      11010110b
        db      11100110b
        db      01101100b
        db      00111000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b

        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      01000100b
        db      11000110b
        db      11111110b
        db      11000110b
        db      01101100b
        db      00111000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b

        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00111000b
        db      01101100b
        db      11000110b
        db      11000110b
        db      11000110b
        db      01101100b
        db      00111000b
        db      00000000b
        db      00000000b
        db      00000000b
        db      00000000b

; The keyboard I/O interrupt intercept routine follows.
intercept       proc    far
                cli
                nop
                pushf
                cmp     timer_tick,0    ; wait 1/18 of a second before font update
                je      dont_do_this
                cmp     in_kbio,1       ; make sure we don't overflow any stacks
                ja      dont_do_this
                cmp     ah,1            ; only update font on keyboard poll
                je      do_this
                cmp     ah,11h          ; extended keyboard poll
                je      do_this
dont_do_this:
                jmp     go_kbio1
do_this:
                mov     timer_tick,0
                inc     in_kbio
                push    es
                push    ds
                push    si
                push    di
                push    bp
                push    ax
                push    bx
                push    cx
                push    dx

                mov     ax,040h
                mov     es,ax           ; load BIOS data segment

                mov     bh,es:[85h]     ; Character height
                cmp     bh,14
                jb      close_jmp
                cmp     bh,16
                ja      close_jmp
                mov     al,es:[49h]     ; Video mode
                cmp     al,3
                je      mode_ok
close_jmp:
                jmp     go_kbio
mode_ok:
                cmp     in_kbio,1
                jne     still_counting

                mov     al,es:[17h]     ; Get keyboard flags
                and     al,3
                cmp     al,3
                jne     shifts_not_down
                inc     shift_ticks     ; both shift keys are down
                cmp     shift_ticks,18
                jne     still_counting
                xor     enabled,1       ; toggle enabled status
                jmp     enabled_status_changed
shifts_not_down:
                mov     shift_ticks,0
still_counting:
                cmp     enabled,0
                je      go_kbio
enabled_status_changed:
                mov     dx,es:[63h]     ; video controller base address
                add     dl,6            ; address of status register
updating:
                nop
                nop
                cli
                nop
                in      al,dx           ; get vertical retrace status
                test    al,8
                jnz     now             ; jump if in vertical retrace
                sti                     ; temporarily enable interrupts
                jmp     updating
now:
                cmp     enabled,0
                je      load_orig       ; jump to load a plain, unbroken "o"
                mov     al,which        ; get which "o" to load
                inc     al
                and     al,7            ; 8 possible "o"s to load
                mov     which,al
                jmp     load_next_o
load_orig:
                mov     al,8
load_next_o:
                mul     csize           ; make index into "o_table"
                mov     bp,offset o_table
                add     bp,ax
                mov     ax,cs
                mov     es,ax
                mov     ax,01100h       ; do interrupt to load character font
                mov     bl,0            ; table number
                                        ; bh = number of bytes per character
                mov     cx,1            ; number of characters to load
                mov     dx,06fh         ; ASCII value of "o"
                int     10h
go_kbio:
                pop     dx
                pop     cx
                pop     bx
                pop     ax
                pop     bp
                pop     di
                pop     si
                pop     ds
                pop     es
                dec     in_kbio
go_kbio1:
                popf
                jmp     old_kbio
intercept       endp

; The new timer interrupt routine follows.
new_timer       proc    far
                mov     timer_tick,1
                jmp     old_timer
new_timer       endp

; Do TSR initialization.
init_vectors    proc    near
                assume  ds:vectors
                mov     ax,vectors
                mov     ds,ax

                cli
; Load timer interrupt vector.
                mov     ax,word ptr timer
                mov     word ptr old_timer,ax
                mov     ax,word ptr timer[2]
                mov     word ptr old_timer[2],ax
                mov     ax, offset new_timer
                mov     word ptr timer,ax
                mov     word ptr timer[2],cs

; Load keyboard I/O interrupt vector.
                mov     ax,word ptr kbio
                mov     word ptr old_kbio,ax
                mov     ax,word ptr kbio[2]
                mov     word ptr old_kbio[2],ax
                mov     ax, offset intercept
                mov     word ptr kbio,ax
                mov     word ptr kbio[2],cs
                sti

                mov     es,cs:[02ch]            ; release environment
                mov     ah,49h
                int     21h

                mov     dx,offset init_vectors  ; end of resident portion
                int     27h                     ; terminate but stay resident
init_vectors    endp

cseg           ends

                END     begin
