TSR source code - The whole shimolia 
Author Message
 TSR source code - The whole shimolia

With so many request for TSRs, here is the source code for most everything
I have with some pretty good comments and documentation.

;  clock.asm  Works thru Win 98
;
        .MODEL  SMALL
        .CODE
        ORG     100H

FIRST:  jmp     Install
        old_int_label       label   word
        old_int             dd      ?
        H                   db      ?               ; hours
        M                   db      ?               ; minutes
        S                   db      ?               ; seconds
        LD                  db      ?               ; lower digit
        HD                  db      ?               ; higher digit
new_int PROC
        push    ax      ;save registers and flags
        push    bx
        push    cx
        push    dx
        pushf

        mov     ah,03           ; save the original x,y coord.
        mov     bh,0
        int     10h             ; dh:row,dl:col
        push    cx
        push    dx
        mov     ah,1            ; and hide cursor for a while
        mov     cx,2000h
        int     10h

;Begin WRITE TIME
        mov    ah,2             ; Get time from CMOS directly, (not from
DOS)
        int    1Ah              ; Use the interrrupt 1Ah to get real time
        cmp    ch,25            ; from RTC (CMOS)
        jle     h1
        sub    ch,12
        jmp    h2
h1:     cmp    ch,9             ; Adjust the hour's value from RTC to resolve
        jle     h2              ; the actual time
        sub    ch,6
h2:     mov    H,ch

        cmp    cl,73
        jle     m1
        sub    cl,30         ; Adjust the minutes' value from RTC to resolve
        jmp    m5            ; the correct minutes' value
m1:     cmp    cl,57
        jle     m2
        sub    cl,24
        jmp    m5
m2:     cmp    cl,41
        jle     m3
        sub    cl,18
        jmp    m5
m3:     cmp    cl,25
        jle     m4
        sub    cl,12
        jmp    m5
m4:     cmp    cl,9
        jle     m5
        sub    cl,6
m5:     mov    M,cl

        cmp    dh,73
        jle     s1          ;Adjust the seconds' value from RTC to resolve
        sub    dh,30        ;the correct seconds' value
        jmp    s5
s1:     cmp    dh,57
        jle     s2
        sub    dh,24
        jmp    s5
s2:     cmp    dh,41
        jle     s3
        sub    dh,18
        jmp    s5
s3:     cmp    dh,25
        jle     s4
        sub    dh,12
        jmp    s5
s4:     cmp    dh,9
        jle     s5
        sub    dh,6
s5:     mov    S,dh

        mov     ah,0           ;Since we can only type one digit at a time,
        mov     al,H           ;with the int 10H, We should partition the
        mov     bl,10          ;BCD value of H, M, and S to 2 digits(LD,HD)
        div     bl
        mov     HD,al
        mov     LD,ah          ;get HD & LD of hours (low and high digits)

        mov     ah,2           ;go to position (2,70)
        mov     dh,0
        mov     dl,70
        int     10h
        mov     ah,9
        mov     al,'o'        ;and write a 'o'
        mov     bl,27
        mov     cx,1
        int     10h

        mov     ah,2           ;go to hour position (2,71)
        mov     dl,71
        int     10h
        mov     ah,9
        add     HD,48
        mov     al,HD        ;and First write HD of the Hours
        mov     bl,27
        int     10h

        mov     ah,2           ;go to hour position (2,72)
        mov     dl,72
        int     10h
        mov     ah,9
        add     LD,48
        mov     al,LD        ;and write the LD of Hours
        mov     bl,27
        int     10h

        mov     ah,2           ;go to hour's : position (2,73)
        mov     dl,73
        int     10h
        mov     ah,9
        mov     al,':'        ;and write a ':'
        mov     bl,27
        int     10h

        mov     ah,0
        mov     al,M
        mov     bl,10
        div     bl              ;get HD & B of Minutes
        mov     HD,al
        mov     LD,ah

        mov     ah,2           ;go to hour position (2,74)
        mov     dl,74
        int     10h
        mov     ah,9
        add     HD,48
        mov     al,HD        ;and First write HD of the Minutes
        mov     bl,27
        int     10h

        mov     ah,2           ;go to Minute position (2,75)
        mov     dl,75
        int     10h
        mov     ah,9
        add     LD,48
        mov     al,LD        ;and write the LD of Minutes
        mov     bl,27
        int     10h

        mov     ah,2           ;go to  position (2,76)
        mov     dl,76
        int     10h
        mov     ah,9
        mov     al,58        ;and write a blinking ':'
        mov     bl,155      
        int     10h

        mov     ah,0
        mov     al,S
        mov     bl,10
        div     bl              ;get HD & LD of Seconds
        mov     HD,al
        mov     LD,ah

        mov     ah,2           ;go to second's position (2,77)
        mov     dl,77
        int     10h
        mov     ah,9
        add     HD,48
        mov     al,HD        ;and First write HD of the Seconds
        mov     bl,27
        mov     cx,1
        int     10h

        mov     ah,2           ;go to hour position (2,78)
        mov     dl,78
        int     10h
        mov     ah,9
        add     LD,48
        mov     al,LD        ;and write the LD of Seconds
        mov     bl,27
        int     10h

        mov     ah,2           ;go to position (2,79)
        mov     dl,79
        int     10h
        mov     ah,9
        mov     al,'o'        ;and write a 'o'
        mov     bl,27
        int     10h
;End write time

exit:   mov     ah,1            ;enable cursor
        mov     ch,6
        mov     cl,7
        int     10h

        pop     dx
        mov     ah,02           ;go back to where you were
        int     10h
        pop     cx
        mov     ah,1            ;with the same cursor type
        int     10h

        popf
        pop     dx      ;restore registers
        pop     cx
        pop     bx
        pop     ax
        pushf
        call    old_int      ;and call old_int (original int 8)
        iret
new_int    ENDP

Install       PROC
        mov     ax,3508h
        int     21h
        mov     old_int_label, bx
        mov     old_int_label[2], es    ;save the old int 8
        mov     ah, 25h
        lea     dx, new_int
        int     21h                     ;associate new_int with int 8
        mov     dx, offset Install
        int     27h
Install       ENDP
END     FIRST

; saver.asm  Com program   Modified 6/13/2002   Andrew Kennedy
;  Works up to Win 98
; Screen saver (blanks screen)   COM file..
; Only works in 80x25 screen modes (2,3,7) [could easily be adapted/others]
;        Uses F-8 key to activate, uninstall by typing name of program again
;
;   **** ADJUSTABLE time interval to pop up
;
;        There is a conflict when this is run when chime.com is also loaded.
;                                                                      

;
SEQ               ; store segment sequentially as they appear
286               ; use 80286+ code
MODEL tiny        ; TINY model to make it a .COM program

;* Macros *

StAlloc MACRO sizew    ; macro to allocate stack, size "sizew" words
 MOV BX,((sizew*2)+15) SHR 4    ; BX = size in paragraphs
 MOV AH,48h        ; allocate memory function
 INT 21h           ; call DOS
 CLI               ; freeze interrupts
 MOV SS,AX         ; set stack segment to allocated segment returned in AX
 MOV SP,sizew*2-2  ; set stack pointer to end of segment (goes top down)
 STI               ; restore interrupts
ENDM

StDeAlloc MACRO    ; macro to deallocate stack
 MOV AX,SS
 MOV ES,AX
 MOV AH,49h        ; call DOS deallocate block function
 INT 21h
ENDM

;* Code Segment *

code SEGMENT PARA PUBLIC 'CODE'    ; code segment
ASSUME CS:code, DS:code, ES:code, SS:code  ; assume CS->"code" etc.
ORG 100h                           ; start assembling at offset 0h (default)

;* Program Start *

start:               ; label for start of program
 JMP init

;* Procedures *

; Save screen and display (init) screen saver logo

ScreenSaver PROC NEAR
 MOV AX,CS
 MOV ES,AX
 MOV DI,OFFSET ScrData
 MOV DS,CS:ScrAddress
 XOR SI,SI
 CLD
 MOV CX,2000
 REP MOVSW           ; save screen
 MOV CS:SSOn,1

 MOV AH,0Fh
 INT 10h             ; get current page in BH
 MOV AH,03h
 INT 10h             ; get old cursor size
 MOV CS:OldCursor,CX
 MOV AH,01h
 MOV CX,0100h
 INT 10h             ; hide cursor
 CALL RunSaver       ; clear screen
 CALL DrawSaver      ; draw logo
 RET
ENDP

; Restore screen and deinitialize screen saver

RemoveSaver PROC NEAR
 MOV ES,CS:ScrAddress
 XOR DI,DI
 MOV AX,CS
 MOV DS,AX
 MOV SI,OFFSET ScrData
 CLD
 MOV CX,2000
 REP MOVSW
 MOV CS:SSOn,0
 MOV AH,01h
 MOV CX,CS:OldCursor
 INT 10h           ; restore cursor
 RET
ENDP

; Re-hide screen to cover any writing

RunSaver PROC NEAR
 MOV ES,CS:ScrAddress
 XOR DI,DI
 MOV CX,2000
 XOR AX,AX
 REP STOSW
 RET
ENDP

; Find parts of screen that were changed and save to virtual screen

DeltaSaver PROC NEAR
 CLD
 MOV DS,CS:ScrAddress
 XOR SI,SI            ; DS:SI -> physical screen
 MOV AX,CS
 MOV ES,AX            
 MOV DI,OFFSET ScrData  ; ES:DI -> virtual screen
 XOR BH,BH            ; row 0
ds_nextrow:
 MOV CX,80
 CMP BH,CS:Row        
 JZ ds_check          ; if row of copyright string handle separately
ds_loop:
 LODSW
 CMP AX,0             ; check if still zero (as set by screen saver)
 JZ ds_again

 MOV ES:[DI],AX       ; set to new value
ds_again:
 INC DI
 INC DI
 LOOP ds_loop
ds_new:  
 INC BH
 CMP BH,25
 JNZ ds_nextrow
 RET
ds_check:
 MOV BP,OFFSET Copyright
 MOV BL,CS:Color
ds_loop2:
 LODSW
 CMP AL,ES:[BP]
 JNZ ds_delta
 CMP AH,BL
 JZ ds_ok
ds_delta:
 MOV ES:[DI],AX
ds_ok:
 INC DI
 INC DI
 INC BP
 LOOP ds_loop2
 JMP SHORT ds_new
ENDP

; Draw copyright string

DrawSaver PROC NEAR
 MOV AX,WORD PTR CS:Row2
 MOV WORD PTR CS:Row,AX  ; now safe to copy incremented data
 MOV AH,CS:Row
 MOV AL,160
 MUL AH
 MOV DI,AX
 MOV AX,CS
 MOV DS,AX
 MOV SI,OFFSET CS:Copyright
 MOV AH,CS:Color
ds_write:
 LODSB
 OR AL,AL
 JZ ds_donemsg
 STOSW
 JMP SHORT ds_write
ds_donemsg:
 RET
ENDP

; move message and change color

IncSaver PROC NEAR
 MOV AX,WORD PTR CS:Row2  ; AL = row, AH = attribute
 ADD AX,101h              ; increment both
 CMP AL,25
 JNZ is_rowset

 XOR AL,AL
is_rowset:
 CMP AH,16
 JNZ is_colset
 MOV AH,1
is_colset:
 MOV WORD PTR CS:Row2,AX
 RET
ENDP

; INT 09h interrupt handler
; checks for hotkey combinations and deinstalls or activates program if found

Int09Handler PROC FAR
 PUSHF
 CALL CS:OldInt09      ; call old handler
 PUSHA
 PUSH DS
 PUSH ES
 MOV AX,40h
 MOV ES,AX
 MOV DI,ES:[1Ah]
 CMP DI,ES:[1Ch]       ; quit if no keys in keyboard buffer
 JZ i9_exit
 MOV CS:SSTimer,0      ; zero timer for screen saver
 CMP CS:SSOn,0
 JZ i9_skip
 CALL RemoveSaver      ; if screen saver active, restore screen
i9_skip:
 MOV DI,ES:[DI]        ; get key in keyboard buffer
 CMP DI,4200h          ; check for F8 , zeroes needed after scan code
 JNZ i9_exit
 PUSH DI
 PUSH ES
 CALL ScreenSaver      ; startup screen saver
 POP ES
 POP DI
i9_remove:
 INC WORD PTR ES:[1Ah]
 INC WORD PTR ES:[1Ah] ; remove key from buffer
 CMP WORD PTR ES:[1Ah],3Eh
 JNZ i9_exit
 MOV WORD PTR ES:[1Ah],1Eh      ; wrap around if required
i9_exit:
 POP ES
 POP DS
 POPA
 IRET
ENDP

; INT 1Ch interrupt handler
; activates screen saver when timeout occurs

Int1CHandler PROC FAR
 PUSHF
 CALL CS:OldInt1C
 PUSHA
 PUSH DS

 PUSH ES
 CMP CS:SSOn,0
 JZ i1c_off
 CALL DeltaSaver      ; store changes to virtual screen
 CALL RunSaver        ; blank screen
 CALL DrawSaver       ; draw message or graphic
i1c_off:
 MOV AL,CS:Timer1
 DEC AL
 MOV CS:Timer1,AL
 JNZ i1c_exit
 MOV AH,18
 MOV AL,CS:Timer2
 DEC AL
 JNZ i1c_not19
 INC AH
 MOV AL,5
i1c_not19:
 MOV CS:Timer2,AL
 MOV CS:Timer1,AH
 CMP CS:SSOn,0
 JZ i1c_count
 CALL IncSaver
 JMP SHORT i1c_exit
i1c_count:
 MOV AX,CS:SSTimer
 INC AX
 MOV CS:SSTimer,AX
 CMP AX,180           ; 3 minutes (min x 60 secs)
 JNZ i1c_exit
 XOR AX,AX
 MOV CS:SSTimer,AX    ; set to zero
 CALL ScreenSaver     ; startup screen saver
i1c_exit:
 POP ES
 POP DS
 POPA
 IRET
ENDP

; TSR data

ScrAddress DW 0
OldCursor  DW 0
OldInt09   DD 0
OldInt1C   DD 0
SSTimer    DW 0
SSOn       DB 0
Timer1     DB 18
Timer2     DB 5
ScrData    DW 2000 DUP (?)
Row        DB 0
Color      DB 1
Row2       DB 0
Color2     DB 1
Copyright  DB '? ? Oil I Maintenance Services A. Kennedy May 2002(c)',0
Sig        DB 'VXSS'

TSR_end LABEL BYTE

; * Main Program *

init:
 MOV AX,CS
 MOV DS,AX
 MOV ES,AX
 MOV BX,OFFSET code_end
 ADD BX,15
 SHR BX,4
 MOV AH,4AH
 INT 21H                         ; free unneeded memory given to program
 StAlloc 50h                     ; allocate stack (saves memory on disk)
 XOR AX,AX
 MOV ES,AX
 PUSH DS
 MOV DS,ES:[26h]
 CMP WORD PTR DS:Sig,'XV'
 JNZ i_skip
 CMP WORD PTR DS:Sig+2,'SS'
 JNZ i_skip
 MOV AX,WORD PTR DS:OldInt09
 MOV BX,WORD PTR DS:OldInt09+2
 CLI
 MOV ES:[24h],AX
 MOV ES:[26h],BX
 MOV AX,WORD PTR DS:OldInt1C
 MOV BX,WORD PTR DS:OldInt1C+2
 MOV ES:[70h],AX
 MOV ES:[72h],BX
 STI
 MOV AX,DS
 MOV ES,AX
 MOV AH,49h
 INT 21h
 POP DS
 MOV AH,09h
 MOV DX,OFFSET Deinstall
 INT 21h
 StDeAlloc
 MOV AX,4C00h
 INT 21h
i_skip:
 POP DS
 MOV AX,0B800h

 JNZ i_gotmode
 MOV AX,0B000h
i_gotmode:
 MOV ScrAddress,AX               ; store screen address
 MOV AX,ES:[24h]
 MOV BX,ES:[26h]
 MOV WORD PTR OldInt09,AX
 MOV WORD PTR OldInt09+2,BX      ; save old interrupt 09h
 MOV AX,OFFSET Int09Handler
 MOV BX,CS
 CLI
 MOV ES:[24h],AX
 MOV ES:[26h],BX
 STI
 MOV AX,ES:[70h]
 MOV BX,ES:[72h]
 MOV WORD PTR OldInt1C,AX

 MOV WORD PTR OldInt1C+2,BX      ; save old interrupt 1Ch
 MOV AX,OFFSET Int1CHandler
 MOV BX,CS
 CLI
 MOV ES:[70h],AX
 MOV ES:[72h],BX
 STI
 MOV AH,49h
 MOV ES,CS:[2Ch]
 INT 21h                         ; deallocate environment block
 MOV AH,09h
 MOV DX,OFFSET Install
 INT 21h                         ; display message
 StDeAlloc                       ; deallocate stack
 MOV DX,OFFSET TSR_end
 ADD DX,15
 SHR DX,4
 MOV AX,3100h
 INT 21h                         ; go TSR, exit with errorlevel 0

;* Data Area *

; main program data

Install     DB 13,10,'Screen Saver now installed - press F8 to '
            DB 'activate.',13,10,10,'$'
Deinstall   DB 13,10,'Screen Saver deinstalled Ok.',13,10,10,'$'

;* End Program *

code_end LABEL BYTE                ; mark end of code segment (end of program)
code ENDS                          ; end code segment "code"
END start                          ; end program, start execution at "start:"

;  chime.asm
;              Chimes every 15 minutes
;
; RESIDENT TIME CLOCK DISPLAY with optional chime
;
;       Original author unidentified
;
;       Revised by Thomas A. Lundin
;               Graphics Unlimited Inc.
;               3000 Second St. No.
;               Minneapolis, MN 55411
;               (612) 588-7571
;
;       Later revisions -- 1/22/91
;
; usage: chime [/c]
;               /c to toggle chiming. No chimes by default.
;
; Clock display can be toggled on and off by repeating 'chime'.
;
;       Chimes should be toggled off when using heavy interrupt-driven
;       software such as communications to avoid losing characters or
;      {*filter*} the system.
;
; 9/4/87 note:
;       This version uses the clock tick to regulate the duration of the
;       chimes, meaning that the chimes should be the same length from
;       one system to another, no matter what the CPU speed.
;       Also modified start-up routines to automatically set the time
;       display background to color or monochrome.
;
; 9/21/87 note:
;       This version alterates a date display with the time display, every
;       1.5 seconds.  The date display is static, i.e., it is not updated
;       at midnight (it would be inefficient to check every hour for an
;       event which occurs once every 24 hours).
;
;     Turning the chime off and on (typing chime, twice) will reset the date.
;
; 1/05/88 note:
;       This revision sets the clock display in the lower right corner
;       instead of the upper right.
;
; 1/22/91 note:
;     This revision causes the clock to automatically position itself
;     according to the number of columns in the display.  The
;     program did this before, but only when it was initially loaded.
;     Thus if you switched between an 80 and 132 column mode, the clock would
;     no longer be displayed in the upper right hand corner or wherever.
;     The program now reads the number of columns every time the clock is
;     displayed.
;
; 2/1/91  The chime has now been replaced by a ?cuckoo.  The clock will
now
;         cuckoo for the appropriate hourly figure and cuckoo once on the
;         quarter hour.
;
;v1.1   Toad Hall Tweak, 17 Apr 93
; - Retabified, reformatted for consistent upper, lower case.
; - Minor tweaks, tightening.
; David Kirschbaum, Toad Hall

TICKS   EQU     27              ; number of ticks between updates (1.5 sec)
                                ;old value -- 27
BEEPS1  EQU     1               ; duration of first beep in clock ticks
BEEPS2  EQU     3               ; duration of second beep in clock ticks
BEEPS3  EQU     1               ; pause between cuckoos
TONE1   EQU     5e7h            ; first chime tone 5f7h
TONE2   EQU     74ch            ; second chime tone

CR      EQU     0DH             ;v1.1
LF      EQU     0AH             ;v1.1

INTERRUPTS segment at 0h
        org     1ch*4           ; This is to use INT 1Ch
timer_int label dword           ; which is the timer interupt
INTERRUPTS ends

SCREEN  SEGMENT AT 0B000H       ; A dummy segment to use
SCREEN  ENDS                    ; as the Extra Segment

CSEG    SEGMENT
        ASSUME  CS:CSEG
        ORG     100h            ; org = 100 to make this into
                                ; a .COM file
First:  jmp     Load_Clock

old_time_int    dd      ?               ; The address INT 1Ch normally uses
count500        dw      TICKS           ; Used to update clock every nnn
counts
beepticks       dw      0               ; number of ticks for a BEEP
beepsleft       db      0               ; number of beeps to output
cuckoo          dw      0               ; number of cuckoos to output
cursor          dw      0               ; Location of the cursor on the screen
beept           db      0               ; have-we-beeped flag
inbeep          db      0               ; beep-in-progress flag
flash           db      1               ; flashing colon flag
spkrstat        db      0               ; old speaker status
video_port      dw      ?               ; Video status port - check for scanning
numcol          db      ?               ; number of columns

hh              dw      0               ; hours
mm              dw      0               ; minutes
sc              dw      0               ; seconds
hh1             dw      0               ; 12-hour value for cuckoo
hhdiv           dw      32771           ; hours divisor (65543/2)
mmdiv           dw      546             ; minutes divisor (1092/2)
ssdiv           dw      9               ; second divisor (18/2)

display         dw      (7020h)         ; leading space
                dw      5 dup(703ah)    ; Initial value for the clock
                dw      2 dup(7020h)    ; Add 2 ' 's for am/pm.

day_of_wk       dw      0               ; day of the week
month           dw      0               ; month
day             dw      0               ; day
chimon          dw      1               ; flag for chime in use or not
clokon          dw      1               ; flag for chime in use or not

Clock   PROC    NEAR                    ; The timer INT will now come here

        cmp     CS:clokon,1             ; is this interrupt silent ?
        jz      NewInt                  ; no, go execute it
        jmp    dword ptr old_time_int   ; silent, just execute old int
NewInt:
        pushf                   ; First call old time interrupt to update
count
        call    dword ptr old_time_int

        call    NeedBeep                ; need to continue beep ?
        dec     CS:count500             ; should recalculate the time ?
        jnz     Dont_Recalculate

        push    ax                      ; Save the registers - good form
        push    cx
        push    di
        push    si
        push    ES

        xor     CS:flash,1              ; toggle the flashing colon
        call    Calc                    ; Recalculate the time
        mov     CS:count500,TICKS       ; Reset Count500
;
;Dont_Recalculate:
;This is the routine for recalculating clock position when you switch in
and
;out of different column modes

        mov     ax,0            ;move memory location for column numbers
        mov     ES,ax           ;into ES:di
        mov     ax,044ah        ;
        mov     di,ax
        mov     ah,ES:[di]      ;move number of columns into ah
        sub     ah,8            ; Move to eight places before edge
        shl     ah,1            ; Mult by two (char and attribute bytes)

;end of clock position routine
        assume  ES:SCREEN       ; Set up screen as the Extra Segment
        mov     cx,SCREEN
        mov     ES,cx
        mov     dx,video_port           ; This is the screen status port
        mov     byte ptr cursor,ah      ; Move cursor to it's memory location

        mov     di,cursor       ; Set up cursor on screen as destination
        mov     si,offset display       ;v1.1
        mov     cx,16           ; To move char and attributes
Scan_Low:
        mov     ah,CS:[si]      ; Move byte to be written into AH
        mov     ES:[di],ah      ; Move to screen one byte at a time.
        inc     di              ; Position to attribute byte
        inc     si              ; on screen.
        loop    Scan_Low        ; Go back foe next byte

        pop     ES              ; Here are required pops to exit
        pop     si
        pop     di
        pop     cx
        pop     ax

Dont_Recalculate:
        iret                    ; An interrupt needs an IRET

Clock   ENDP

Calc    PROC    NEAR            ; Here we recalculate the time and store
it
        push    ax              ; Pushes to save everything that
        push    bx              ; gets destroyed
        push    cx
        push    dx

        cmp     CS:flash,1              ; do date or time?
        jz      Dtime                   ; TIME
        mov     bx,offset display      
        mov     ax,CS:month
        mov     CS:[bx+0],ah            ; Move first month digit into display
        mov     CS:[bx+2],al            ; Then the second digit
        mov     byte ptr CS:[bx+4],'-'  ; a hyphen
        mov     ax,CS:day               ; get day
        mov     CS:[bx+6],ah            ; and move them into the display
in memory
        mov     CS:[bx+8],al
        mov     byte ptr CS:[bx+10],' ' ; move space into display
        mov     ax,CS:day_of_wk
        mov     CS:[bx+12],ah           ; move day of the week into display
        mov     CS:[bx+14],al
        jmp     Restore

Dtime:
; note: Peter Norton p.223 explains that the time formula is more precisely
; shown as:
;       hh = clkcount / 65543
;       mm = hh.remainder / 1092
;       ss = mm.remainder / 18
;
; trouble is, the 65543 value won't work as a single-word divisor,
; so our trick is to divide the clock count and divisor values in half,
; which should have no appreciable affect on the accuracy of the time.

        xor     ax,ax           ; Set up for clock read.
        INT     1Ah             ; Read the clock.
        mov     bx,dx           ; Save low(minutes) portion.
        mov     dx,cx           ; Move high(hours) portion to AX.
        mov     ax,bx           ; dx:ax = clock count

        clc
        rcr     dx,1            ; div count by 2 so we can use a
        rcr     ax,1            ; single precision dividend

        div     CS:hhdiv        ; compute hours
        mov     CS:hh,ax        ; save it
        mov     ax,dx           ; prepare remainder for minutes
        xor     dx,dx
        div     CS:mmdiv        ; compute minutes

        cmp     ax,60           ; 60 minutes shows up sometimes
        jl      Mm_Ok           ; mostly it doesn't
        xor    ax,ax            ; but if it does, zero out the minutes
        inc    CS:hh            ; and bump up the hour

Mm_Ok:  mov     CS:mm,ax        ; save it
        mov     bx,offset display       ;v1.1
        mov     byte ptr CS:[bx],' '    ; blank out first and last positions
        mov     byte ptr CS:[bx+14],' '
        mov     ax,CS:hh
        cmp     ax,12                   ; is it am or pm?
        jl      Am                      ; am
;Pm:
        mov     byte ptr CS:[bx+12],'p' ; Otherwise move 'P' into the display.
        sub     ax,12                   ; pm, subtract 12
        jmp     short Chek12            ; Continue.

Am:     mov     byte ptr CS:[bx+12],'a' ; Move an 'A' into the display.

Chek12: or      ax,ax                   ; Make zero hour...
        jnz     Am_Pm_Done
         mov    ax,12                   ; ...a twelve
Am_Pm_Done:
        mov     CS:hh1,ax               ; hour value for cuckoo
        aam                             ; Convert AX to BCD - a nice command
        add     ax,3030h                ; Add '0' to both bytes in AX to
make ascii
        cmp     ah,'0'                  ; Is the 1st digit '0'?
        jne     Dont_Edit               ; Then don't blank the character.
         mov    ah,' '                  ; Otherwise, put a space in AH.
Dont_Edit:
        mov     CS:[bx+2],ah            ; Move first hours digit into display
        mov     CS:[bx+4],al            ; Then the second digit
;----------------------------------
        mov     byte ptr CS:[bx+6],':'  ; in which case use a colon
        mov     ax,CS:mm                ; get minutes
        aam                             ; Again convert AX to Binary Coded
Decimal
        add     ax,3030h                ; Add to make two ASCII characters
        mov     CS:[bx+8],ah            ; and move them into the display
in memory
        mov     CS:[bx+10],al

;---------routine for alarm chime goes here------------------------------------
        cmp     CS:chimon,0             ; chimes off?
        jz      Restore                 ; yes, don't beep
        cmp     CS:inbeep,1             ; already in a beep loop?
        jz      Restore                 ; yes, don't be redundant

        cmp     ax,3030h                ; on the hour (full cuckoo routine)
        jz      Alarm3
        cmp     ax,3135h                ; on the 1/4 hour (single cuckoo)
        jz      Alarm2
        cmp     ax,3330h                ; on the 1/2 hour (single cuckoo)
        jz      Alarm2
        cmp     ax,3435h                ; on the 3/4 hour (single cuckoo)
        jz      Alarm2
        mov     CS:beept,0              ; we have not beeped
;------------------------------------------------------------------------------
Restore:                                ; Restore registers
        pop     dx
        pop     cx
ImRet2: pop     bx
ImRet1: pop     ax

ImRet:  ret
;-----------------------------------------------------------------------------
NeedBeep:
        cmp     CS:inbeep,1             ; are we beeping right now ?
        jnz     ImRet                   ; no, immediate return
        dec     CS:beepticks            ; yes, done beeping?
        jnz     ImRet                   ; no, immediate return
        push    ax
        mov     al,CS:spkrstat          ; yes, shut off speaker
        out     61h,al
        dec     CS:beepsleft            ; any more beeps waiting?
        jz      NoBeeps                 ; no, go home
        cmp     CS:beepsleft,1          ; how many await?
        jnz     Onward
         call   Pause
         jmp    ImRet1

Onward:
        push    bx
        mov     bx,TONE2                ; second tone in cuckoo
        mov     CS:beepticks,BEEPS2     ; second tone is longer than first
tone
        call    Tone_a                  ; start it beeping
        jmp     ImRet2                  ; go home
NoBeeps:
        dec     CS:cuckoo               ; are there any more cuckoos?
        jz      No_Cuckoo               ; no more cuckoos
        push    bx
        mov     bx,TONE1                ; beginning of next cuckoo
        mov     CS:beepsleft,3
        call    Tone                    ;start it beeping
        jmp     ImRet2
;------------------------------------------------------------------------------
Alarm3: push    ax                      ;save the register
        mov     ax,hh1                  ;move hour value into ax
        mov     CS:cuckoo,ax            ; hourly cuckoo - repeat for number
of hours
        pop     ax                                                      

        mov     bx,TONE1                ; send tone 1
        mov     CS:beepsleft,3
        call    Tone
        jmp     Restore

Alarm2: mov     CS:cuckoo,1             ; only a single cuckoo for quarter
hour
                                        ; intervals
        mov     bx,TONE1                ; send tone 2
        mov     CS:beepsleft,2
        call    Tone
        jmp     Restore
;---------------------------------------
No_Cuckoo:
        mov     CS:beept,1              ; we have beeped
        mov     CS:inbeep,0             ; and we're not in one any more
        jmp     ImRet1
;---------------------------------------
Tone:
        cmp     CS:beept,1              ; do nothing if chime has been beeped
        jz      NoTone                  ; earlier in this clock update
        mov     CS:beepticks,BEEPS1     ; this long on beeps
Tone_a:
        MOV     AL,0B6H                 ; else condition the timer
        OUT     43H,AL
        MOV     AX,BX                   ; this is the freq
        OUT     42H,AL
        MOV     AL,AH
        OUT     42H,AL                  ; out you all go
        IN      AL,61H                  ; read spkr port
        MOV     CS:spkrstat,AL
        OR      AL,3
        OUT     61H,AL                  ; send a beep
        mov     CS:inbeep,1
NoTone: ret
;--------------------------------------------------------
Pause:
        mov     CS:beepticks,BEEPS3
        ret
;------------------------------------------------------------------------------
Calc    ENDP

Load_Clock PROC NEAR            ; This procedure initializes everything
        assume  DS:INTERRUPTS   ; The Data Segment will be the interrupt
area
;       mov     ax,INTERRUPTS
        xor     ax,ax           ;0                              v1.1
        mov     DS,ax

        MOV     SI,0081H        ; addr of command line arguments
Next:   MOV     AL,CS:[SI]      ; get command line char
        CMP     AL,0DH          ; Return ends it.
        JZ      Again
        CMP     AL,'/'          ; switch char
        JZ      GetSwitch       ; see what it is
Next1:  INC     SI              ; else point to next char
        JMP     Next            ; and loop

GetSwitch:
        inc     si
        mov     al,CS:[si]
        cmp     al,'c'             ; chime toggle switch
        jnz     Next1              ; wrong switch                      

        mov     CS:togchim,1       ; toggle them chimes
        jmp     Next1              ; get next switch if there is one

Again:  mov     ax,CS:sig_vector   ; get signature vector
        cmp     ax,5fh             ; if less than 0x60
        jg      Vok
        jmp     NoLoad             ; forget it

Vok:    add     ax,3500h
        int     21h
        mov     ax,ES
        cmp     ax,434ch                ; are we already loaded ?
        jnz     NoSig                   ; no
        cmp     bx,4f4bh
        jnz     NoSig                   ; and no
        mov     bx,word ptr timer_int   ; yes
        mov     ES,word ptr timer_int+2
        call    Gdate                   ; get the system date
        cmp     CS:togchim,1
        jnz     No1                     ; no toggle chimes
        call   ToglChim                 ; go toggle chimes
        jmp    short Exit

No1:
        mov     ax,ES:[bx-2]
        xor     ax,1                    ; toggle activation mode
        mov     word ptr ES:[bx-2],ax
Exit:   mov     ax,4c00h                ; return to DOS
        int     21h

;---------------------------------------
ToglChim:
        mov     ax,ES:[bx-4]            ; get the chime flag
        xor     ax,1                    ; toggle it
        mov     word ptr ES:[bx-4],ax
        push    DS
        mov     di,CS
        mov     DS,di
        mov     dx,offset chimonmsg     ; chimes on
        cmp     ax,1                    ; beep if it's been turned on
        jz      BeepMsg
        mov     dx,offset chimoffmsg    ; chimes off
BeepMsg:
        mov     ah,9
        int     21h
        pop     DS
        ret
;---------------------------------------
NoSig:  mov     ax,ES                   ; no current signature...
        or      ax,bx                   ; ...but is it safe to load one?
        jz      SetSig                  ; yes
        mov     ax,CS:sig_vector        ; no, try another slot
        dec     ax
        mov     CS:sig_vector,ax
        jmp     Again
;---------------------------------------
Gdate:
        push    bx
        mov     ah,2ah          ; call DOS GetDate command
        int     21h
        xor     ah,ah           ; make an offset out of the day of the week
        clc
        rcl     ax,1
        mov     si,ax
        lea     bp,days
        mov     ax,[bp+si]      ; get the ASCII day of the week
        pop     bx
        mov     word ptr ES:[bx-10],ax  ; save it
        mov     al,dh                   ; month
        xor     ah,ah
        aam                     ; Convert AX to BCD - a nice command
        add     ax,3030h        ; Add '0' to both bytes in AX to make ASCII
        cmp     ah,'0'          ; Is the 1st digit '0'?
        jne     Gd1             ; Then don't blank the character.
        mov     ah,' '          ; Otherwise, put a space in AH.
Gd1:    mov     word ptr ES:[bx-8],ax
        mov     al,dl           ; day
        xor     ah,ah
        aam                     ; Convert AX to BCD - a nice command
        add     ax,3030h        ; Add '0' to both bytes in AX to make ASCII
        mov     word ptr ES:[bx-6],ax
        ret                     ; go home
;---------------------------------------
NoLoad  label near
        mov     ax,4c01h        ; abort with error
        int     21h

SetSig:
        cli
        push    DS
        mov     ax,CS:sig_vector
        add     ax,2500h        ; signature reads:
        mov     dx,434ch        ; 'CL'
        mov     DS,dx
        mov     dx,4f4bh        ; 'OK'
        int     21h
        pop     DS

        mov     ax,word ptr timer_int   ; Get the old interrupt service routine
        mov     word ptr old_time_int,ax        ; address and put it into
our location
        mov     ax,word ptr timer_int+2 ; OLD_TIME_INT so we can still call
it
        mov     word ptr old_time_int+2,ax

        mov     word ptr timer_int,offset Clock ; Now load the address of
our clock
        mov     word ptr timer_int+2,CS ; routine into TIMER_INT so the timer
                                        ; interrupt will call CLOCK
        sti

        mov     ah,15           ; Ask for service 15 of INT 10H
        int     10h             ; This tells us how the display is set up
        sub     ah,8            ; Move to eight places before edge
        shl     ah,1            ; Mult by two (char and attribute bytes)
        mov     byte ptr cursor,ah ; Move cursor to it's memory location
        mov     video_port,03bah        ; Assume this is a monochrome display
        test    al,4                    ; Is it?
        jnz     Get_Time                ; Yes - jump out
        add     cursor,8000h            ; No - set up for graphics display
        mov     video_port,03dah
        mov     cx,8
        mov     ax,4f20h
        mov     di,offset display       ;v1.1
Color:  mov     CS:[di],ax
        inc     di
        inc     di
        loop    Color

Get_Time:
        mov     bx,offset Clock ; yes   ; get addr of chime
        mov     ax,CS
        mov     ES,ax
        cmp     CS:togchim,1    ; need to toggle ?
        jnz     Noct            ; no
        call   ToglChim
Noct:   call    Gdate

        mov     di,CS
        mov     DS,di
        mov     dx,offset hello         ; chimes on
        mov     ah,9
        int     21h
        call    Calc                    ; This is to avoid showing
                                        ; 00:00 for first 500 counts
        mov     dx,offset Load_Clock    ; Set up for everything but Load_Clock
        int     27h                     ; to stay attached to DOS

sig_vector      dw      67h             ; signature vector
togchim         db      0               ; to toggle chimes
days            db      'uSoMuTeWhTrFaS'
chimonmsg       db      'Chime ON',CR,LF,'$'
chimoffmsg      db      'Chime OFF',CR,LF,'$'
hello           db       CR,LF,'USAGE: ',CR,LF,CR,LF
                db      '       chime [/c]  (/c: toggle chimes)',CR,LF
                db      '       chime by itself toggles display',CR,LF
                db      'CHIME INSTALLED',CR,LF,'$'

Load_Clock      ENDP

        CSEG    ENDS

        END     First

;
; breaks.asm                     Andy Kennedy        Compiled with TASM 3.1
;            TSR                                     Tested with DOS 6.2
;
;  Capture CTRL C and prevent DOS from displaying a ^C on the screen
;
; ^C can be caused by Ctrl C, Ctrl Break, Ctrl 2, and Alt 3(on numeric keypad)

model       small
stack       1024

cr           equ          13     ; carriage return
lf           equ          10     ; line feed

data

;current video page
vpage        db           ?

;storage for old 1bh vector

old1b0       dw           ?
old1bs       dw           ?

bstring      db           cr,lf,'BREAKS',cr,lf
             db           'Prevent Ctrl C (^C) from appearing on screen.',cr,lf
             db           'Press ESCAPE to end program.',cr,lf

code

; storage for old int 9 vector
; we want to jump to this when ds != the data segment
; so it is in the code segment

oldkey       dw           ?
oldkeys      dw           ?

;actual break handler

brk          proc         far
             push         ds
             push         ax
             push         bx

;get data addressability            


             mov          ds,ax
             mov          bx,offset bstring
             mov          ah,0eh
             mov          bh,vpage
bloop:
             mov          al,[bx]
             inc          bx
             or           al,al
             jz           short outbrk
             push         bx
             int          10h              ; print break message
             pop          bx
             jmp          bloop
outbrk:
             pop          bx
             pop          ax
             pop          ds
             iret
brk          endp

; new keyboard (int 16h) handler
; here we catch functions 0,1 (10h, 11h), which are the same
; functions except that they are aware of the extended keyboard
; keys (like F11 and F12).
; we never allow a program (inc DOS) to see a ^C, ^2, or Alt-numeric pad
3

newkey       proc         far

             cmp          ah,10h
             jz           pkey
             cmp          ah,11h
             jz           pkey2
             cmp          ah,1
             jz           pkey2
             or           ah,ah

; only worry about functions 0 and 1 10h and 11h

             jz           pkey

; continue with old handler(its IRET will return to caller)

             jmp          dword ptr cs:[oldkey]

; handle functions 0, 10h

pkey:
             push         ax
             jmp          short pk1

; call break routine when ^C detected

pkey1:
             int          1bh
pk1:            
             pop          ax
             push         ax
             pushf                  ; simulate old int16
             call         dword ptr cs:[oldkey]
             cmp          al,3
             jz           pkey1     ; do not return ^C
             cmp          ax,0300h  ; or ^2
             jz           pkey1
             add          sp,2      ;remove ax off stack
             iret

; handle functions 1, 11h

pkey2:
             push         ax
             jmp          short pk3

; call break when ^C detected            

pkey3:
             int          1bh

pk3:         pop          ax
             push         ax
             pushf                  ; simulate old int16
             call         dword ptr cs:[oldkey]

; no key waiting-report to caller            

             jz           pkey6
             cmp          al,3
             jz           pkey4     ;do not return ^C
             cmp          ax,0300h
             jnz          pkey5
pkey4:
; "eat" waiting key(do not care about fn 10h return)

             xor          ah,ah
             pushf
             call         dword ptr cs:[oldkey]
             jmp          pkey3
pkey5:      
             add          sp,2       ;remove ax off stack
             cmp          ax,0300h   ;clear zero flag
             ret          2          ;return with flags
pkey6:
             add          sp,2       ;remove ax off stack
             cmp          ax,ax      ;set zero flag
             ret          2          ;return with flags
newkey       endp

; set up(or remove) interrupt handlers
; if ax==0 install, else remove

break_setup  proc

             push         ds
             or           ax,ax
             jnz          cleanup
             mov          ax,351bh      ;get int 1bh vector
             int          21h
             mov          old1b0,bx
             mov          bx,es
             mov          old1bs,bx
             mov          ax,3516h       ;and int 16 too
             int          21h
             mov          cs:oldkey,bx
             mov          bx,es
             mov          cs:oldkeys,bx

             mov          ds,ax
             mov          dx,offset newkey
             mov          ax,2516h
             int          21h             ;set int 16
             mov          dx,offset brk
             jmp          short endsetup
cleanup:
             push         ds
             mov          dx,cs:oldkey
             mov          ds,cs:oldkeys
             mov          ax,2516h        
             int          21h
             pop          ds
;reset 1bh
             mov          dx,old1b0
             mov          ds,old1bs
endsetup:
             mov          ax,251bh       ;set or reset 1bh
             int          21h
             pop          ds
             ret
break_setup  endp

; main routine-echo characters until escape

main         proc        


             mov          ds,ax

; call break-setup to set break vectors            

             mov          ax,0
             call         break_setup
             mov          ah,0fh          ;get video page
             int          10h
             mov          vpage,bh
kloop:
             mov          ah,8
             int          21h
             cmp          al,27      ; check for esc key
             jz           short xit
             mov          dl,al
             mov          ah,2
             int          21h        ; print character
             cmp          dl,0dh     ; carriage return
             jnz          kloop
             mov          dl,0ah     ; line feed
             mov          ah,2
             int          21h
             jmp          short kloop

; leave program

xit:
             mov          ax,1         ;call break_setup to reset
             call         break_setup
             mov          ah,4ch
             int          21h

main         endp
             end          main

COMMENT ~
 PROGRAM: DRVLIGHT.ASM
  AUTHOR: Denis Boyles
 RELEASE: Public Domain (Nov 30, 1996 third draft)

 COMPILE: Arrowsoft Assembler (MASM v3.0)
      OS: MS-DOS

 PURPOSE: creates a drive activity light out of your scroll-lock LED

 CHANGES: 1) removed INT 16 call and programmed LEDS directly (faster?)
          2) changes the LED on read/write calls only
          3) added self check/removal code
~

;--------------------------------------
;define some constants for us
;--------------------------------------

ID      EQU 0DBD0h                     ;program ID

CR      EQU 00Dh                       ;carriage return
LF      EQU 00Ah                       ;line feed

DSKWR   EQU 002h                       ;BIOS disk read? function
DSKRD   EQU 003h                       ;BIOS disk write? function

KBDAT   EQU 060h                       ;keyboard data port
KBSTAT  EQU 064h                       ;keyboard status port

;--------------------------------------
;define our default segments
;--------------------------------------

CODE SEGMENT WORD
    ASSUME CS:CODE,DS:CODE,ES:CODE,SS:CODE
    ORG 0100h

;--------------------------------------
;skip TSR code to install code
;--------------------------------------

MAIN:
    jmp     short START

;--------------------------------------
;TSR resident data
;--------------------------------------

OldDsk      dw ?                       ;and OldDsk-2 for old disk vector
OldPlx      dd ?                       ;for old INT 2Fh vector

;--------------------------------------
;NewDsk - our new BIOS INT 13h handler
;--------------------------------------

NewDsk proc far
    cmp     AH,DSKRD                   ;is a disk read being requested?
    jz      DoIt                       ;YES - then toggle the SL-LED

    cmp     AH,DSKWR                   ;NO - is a disk write requested?
    jnz     ExDsk                      ;NO - then never mind the change

DoIt:
    call    SetLED                     ;toggle the SL-LED on/off
    pushf                              ;call the original BIOS handler
    call    dword ptr CS:[OldDsk-2]
    call    SetLED                     ;toggle the SL-LED back on/off
    ret     2                          ;exit INT and save the disk flags?!

ExDsk:
    jmp     dword ptr CS:[OldDsk-2]    ;straight to BIOS on other functions
NewDsk endp

;--------------------------------------
;NewPlx - new multiplex TSR handler
;         for TSR detection
;--------------------------------------

NewPlx proc far
    cmp     AX,ID                      ;did we call ourselves?
    jnz     ExPlx                      ;NO exit INT to continue

    push    CS                         ;YES we did, save so our CS on the
    pop     ES                         ;stack to bring back as ES
    stc                                ;set carry flag to confirm ourselves
    ret     2                          ;return back and keep out flag state

ExPlx:
    jmp     dword ptr CS:[OldPlx]      ;not ourselves, jump to previous INT
NewPlx endp

;--------------------------------------
;SetLED - toggles the Scroll-Lock LED
;--------------------------------------

SetLED proc
    push    AX                         ;save used registers on the stack
    push    DS

    mov     AX,040h                    ;setup data segment to BIOS data area
    mov     DS,AX

    xor     AX,AX                      ;zero out AX, AH=0,AL=0

    xor     byte ptr DS:[017h],010h    ;toggle SL bit within key status byte
    mov     AL,DS:[017h]               ;AL = changed keyboard status byte

    test    AL,020h                    ;was Num-Lock on before?
    jz      M0                         ;NO then check next lock key

    or      AH,002h                    ;YES set bit for keyboard port

M0:
    test    AL,040h                    ;was Caps-Lock on before?
    jz      M1                         ;NO then check next lock key

    or      AH,004h                    ;YES add it's bit for keyboard port

M1:
    test    AL,010h                    ;did we turn on Scroll-Lock?
    jz      M2                         ;NO then send bits to keyboard port

    or      AH,001h                    ;YES add it's bit for keyboard port

M2:
    mov     AL,0EDh                    ;tell keyboard we want to set LEDs
    out     KBDAT,AL                   ;send function to keyboard

M3:
    in      AL,KBSTAT                  ;read keyboard status
    test    AL,002h                    ;is the keyboard ready?
    jnz     M3                         ;NO so loop until it is
    mov     AL,AH                      ;transfer combined LED bits to AL
    out     KBDAT,AL                   ;send LED bits to keyboard to set

M4:
    in      AL,KBSTAT                  ;read keyboard status
    test    AL,002h                    ;is the keyboard ready?
    jnz     M4                         ;NO so loop until it is

    pop     DS                         ;restored saved registers from stack
    pop     AX
    ret                                ;return to caller
SetLED endp

;--------------------------------------
;program starts here for setup
;--------------------------------------

START:
    mov     AX,ID                      ;check if our program is already
    int     02Fh                       ;resident by calling INT 2Fh with
ID

    jc      RemoveTSR                  ;carry flag set? YES remove ourselves

    mov     AX,DS:[02Ch]               ;get environment segment of program
    mov     ES,AX                      ;from PSP and store in ES
    mov     AH,049h                    ;DOS free memory block
    int     021h                       ;call DOS to free the environment

    mov     AX,03513h                  ;DOS get vector for INT 13h
    int     021h                       ;call DOS to get the vector

    mov     [OldDsk-2],BX              ;store offset of vector in memory
    mov     [OldDsk],ES                ;store segment of vector in memory

    mov     AX,0352Fh                  ;DOS get vector for INT 2Fh
    int     021h                       ;call DOS to get vector

    mov     word ptr [OldPlx],BX       ;store offset of vector in memory
    mov     word ptr [OldPlx+2],ES     ;store segment of vector in memory

    mov     AX,02513h                  ;DOS set vector for new INT 13h
    mov     DX,offset NewDsk           ;DS:DX -> new interrupt handler
    int     021h                       ;call DOS to set new handler vector

    mov     AX,0252Fh                  ;DOS set vector for new INT 2fh
    mov     DX,offset NewPlx           ;DS:DX -> new interrupt handler
    int     021h                       ;call DOS to set new handler vector

    mov     AH,009h                    ;DOS print an ASCIID string
    mov     DX,offset msg0             ;DS:DX -> string to print
    int     021h                       ;call DOS to print title message

    mov     DX,offset START            ;everything upto START is kept
    mov     CL,004h                    ;in memory, so convert bytes to
    shr     DX,CL                      ;paragraphs. 1p = 16 bytes
    inc     DX                         ;plus one more to even it out

    mov     AX,03100h                  ;DOS TSR program with code (0)
    int     021h                       ;call DOS to leave resident

RemoveTSR:                             ;On ID check ES = program's CS
    push    DS                         ;save DS register to stack

    lds     DX,dword ptr ES:[OldDsk-2] ;DS:DX -> old vector for INT 1Ch
    mov     AX,02513h                  ;DOS set vector for old INT 1Ch
    int     021h                       ;call DOS to restore INT 1Ch vector

    lds     DX,ES:[OldPlx]             ;DS:DX -> old vector for INT 2Fh
    mov     AX,0252Fh                  ;DOS set vector for old INT 2Fh
    int     021h                       ;call DOS to restore INT 2Fh vector

    pop     DS                         ;restore our saved DS register


    int     021h                       ;call DOS to free our TSR memory

    mov     AH,009h                    ;DOS print an ASCIID string
    mov     DX,offset msg1             ;DS:DX -> string to print
    int     021h                       ;call DOS to print our removed message

    mov     AX,04C00h                  ;DOS terminate program with code (0)
    int     021h                       ;call DOS to end our program

msg0        db 'DRVLIGHT - The Scroll-Lock disk drive activity LED'
            db CR,LF,'installed!',CR,LF,'$'
msg1        db 'DRVLIGHT removed!',CR,LF,'$'

CODE ENDS
END MAIN

; drlite.asm - Puts a drive activity light in the upper right  TSR
; corner of your screen. Source listing by James Vahn.  Tasm code
; Disassembled from a program written by Tim Zerabaldi.
; Doesn't work from Win 95+

cseg segment
assume cs:cseg,ds:cseg
org 100h

Begin:
        JMP     Init

 sto1   db 0
 sto2   db 0                ; Color byte. Red for HD, green for FD
 screen dw 0
 tmp    dw 0                      ; Video controller Port.
 tmp2   dw 0                      ; Old vector.
 tmp3   dw 0

New_ISR:
        CMP     DL,80h            ;Is it the hard drive?
        JNB     Skip1
        MOV     cs:sto2,20h       ;Nope, set color to green.
        JMP     Skip2
 Skip1:
        MOV     cs:sto2,40h       ;Yup, set color to red.
 Skip2:
        PUSH    AX
        push    bx
        PUSH    DX
        PUSH    DS
        MOV     DS,cs:screen
        MOV     DX,cs:tmp
        MOV     AH,cs:sto2
 lo1:
        IN      AL,DX
        TEST    AL,01h            ;Wait for video enable.
        JNZ     lo1
        CLI
 lo2:
        IN      AL,DX
        TEST    AL,01h            ;Wait for disable.
        JZ      lo2
        CMP     cs:sto2,40h
        JNZ     Skip3
        mov     bx,09Fh           ;Set color for hard drive.
        XCHG    AH,byte ptr[bx]
        JMP     Skip4
 Skip3:
        mov     bx,09Dh           ;Set color for floppy disk.
        XCHG    AH,byte ptr[bx]
 Skip4:
        STI
        MOV     cs:sto1,AH
        POP     DS
        POP     DX
        pop     bx
        POP     AX
        PUSHF
        CALL    dword ptr cs:tmp2       ;Call old INT 13
        PUSHF
        PUSH    AX
        push    bx
        PUSH    DX
        PUSH    DS
        MOV     DS,cs:screen
        MOV     DX,cs:tmp
        MOV     AH,cs:sto1
 lo3:
        IN      AL,DX             ;Test for enable.
        TEST    AL,01h
        JNZ     lo3
        CLI
 lo4:
        IN      AL,DX             ;Test for disable.
        TEST    AL,01h
        JZ      lo4
        CMP     cs:sto2,40h
        JNZ     Skip5

        mov     bx,09Fh           ;Turn HD color off.
        MOV     [bx],AH
        JMP     Skip6
 Skip5:
        mov     bx,09Dh           ;Turn FD color off.
        MOV     [bx],AH
 Skip6:
        STI
        POP     DS
        POP     DX
        pop     bx
        POP     AX
        POPF
        RETF    0002

Init:

        mov     dx,offset msg     ;Hello!
        mov     ah,09h
        int     21h

        mov     bx,02Ch           ;Free environment space.
        mov     es,[bx]
        mov     ah,49h
        int     21h

        INT     11h               ;Get equipment list
        AND     AL,30h
        MOV     screen,0B000h     ;Our screen addresses
        CMP     AL,30h
        JZ      Skip7
        MOV     screen,0B800h
 Skip7:
        PUSH    ES
        SUB     AX,AX
        MOV     ES,AX
        MOV     AX,es:[0463h]     ;Get port address for Video
        ADD     AX,0006h
        MOV     tmp,AX
        MOV     AX,3513h          ;Save old INT 13 vector
        INT     21h
        MOV     tmp2,BX
        MOV     tmp3,ES
        LEA     DX,New_ISR        ;Install new INT 13 vector
        MOV     AX,2513h
        INT     21h
        POP     ES
        LEA     DX,Init
        INT     27h               ;Go TSR

msg db 'On-screen drive activity light installed',13,10,36

cseg ends
end Begin



Mon, 27 Jun 2005 04:29:56 GMT  
 
 [ 1 post ] 

 Relevant Pages 

1. some source code/tsr/io - code2.zip (1/1)

2. Source Code- TSR to write keystrokes to a file

3. Data Recovery SOURCE CODE ( SOURCE CODES of Professional Data Recovery Software )

4. Converting FORTRAN source code to C source code

5. HELP, tsr, source

6. TSR example posted to alt.sources ...

7. The source and the sink different type with its source code

8. Find Forth source (was: 8031 Forth Source Code Needed)

9. Source Code Formatter - with source

10. ASM code public/private key cryptography source code

11. Source code for a Morse Code simulator

12. Open Source Code or Free code

 

 
Powered by phpBB® Forum Software