page 88,132
title DST.COM - DriveST.ASM
;-----------------------------------------------------------------------|
;                PUBLIC DOMAIN MATERIAL by ScanSoft(tm)                 |
;             6402 Ingram Rd., San Antonio, TX  78238                   |
;-----------------------------------------------------------------------|
;  program:      DriveST.ASM (DST.COM)                                  |
;                                                                       |
;  version:      1                                                      |
;                                                                       |
;       by:      Cornel Huth                                            |
;                                                                       |
;     date:      Nov 05 88                                              |
;                                                                       |
; function:      display drive status on                                |
;                  line 26 of CGA, EGA/VGA and line 1 of MDA            |
;                                                                       |
;   caller:      user timer interrupt 1Ch (18.2 times/sec)              |
;                                                                       |
;      use:    C>DST    * install TSR                                   |
;                DST /- * remove from memory (if possible)              |
;                DST /d * make dormant (norm screen, unchain if poss)   |
;                DST /r * restore status line; awaken if dormant        |
;                DST /a * autopark HD C:(after 30 seconds of inactivity)|
;                                                                       |
;                                                                       |
;    notes:      CPU overhead: 4.77 MHz 8088  3.2                       |
;                              8.00 MHz 8088  1.8                       |
;                              6.00 MHz 80286 0.5                       |
;                              when DORMANT   none                      |
;                                                                       |
;                if using MDA, press <Alt> to deactivate status printing|
;                   on line 1 or use Scroll Lock (on) for continuous    |
;                   non-display (allows viewing of line 1)              |
;                                                                       |
;------------------------------------------------------------------------

subttl BIOS segment description

;DEBUGCGA        equ 1

BIOS_SEG        SEGMENT AT 0040h
                ORG 17h
KBDflags1       db ?            ;keyboard toggle flags
                ORG 41h
status          db ?            ;controller status (not all BIOS's update here)
drive           db ?            ;low nybble = 0 then A:
                                ;low nybble = 1 then B:
                                ;low nybble >=8 then HD
headHD          db ?            ;current HD head  (0 to heads on device - 1)
cylHI           label byte      ;bits 6 & 7 represent HI cylinder * 256
secHD           label byte      ;bits 0 - 5 represent sector of HD only
                db ?
cylLO           db ?            ;LO cylinder (0 - 255 + (cylHI * 256)if HD)
headFD          db ?            ;current floppy head (0 or 1)
secFD           db ?            ;sector of floppy only
                ORG 49h
modeCRT         db ?            ;current mode of video system
                ORG 62h
pageCRT         db ?            ;active page of display
                ORG 63h
addrCRTC        dw ?            ;CRTC address register I/O port
                ORG 87h
EGAinfo         db ?            ;EGA info byte (0 only if 64K EGA or no EGA)
EGAinfo2        db ?            ;checked if EGAinfo = 0 (this is 0 if no EGA)

BIOS_SEG        ENDS

subttl Resident code and data

DriveST_SEG     SEGMENT BYTE PUBLIC 'CODE'
                ASSUME cs:DriveST_SEG,ds:DriveST_SEG,es:NOTHING

                org 100h
                ;need 16 bytes of constant data after go: for DUP install check

go:             jmp start

checkdata       db 'DST v1.0 (PD)'

ERRORMSG        db 'ERROR '
OKMSG           db 'OK    '
PARKMSG         db 'PARKED'

EGA_data        dw 5    ;registers to change for EGA/VGA (reg, data)
EGA_26_data     db 06h,07Ah,12h,06Bh,15h,06Ch,10h,06Ch,16h,000h
EGA_25_data     db 06h,06Ch,12h,05Dh,15h,05Eh,10h,05eh,16h,00Ah
EGA_VGA         db 0

M6845_data      dw 1    ;registers to change
M6845_26_data   db 06h,26
M6845_25_data   db 06h,25
CGA             db 0    ;CGA uses the M6845 CRTC (MCGA should be compatible)

M6845_data7     dw 0    ;registers to change
M6845_26_data7  db 0,0
M6845_25_data7  db 0,0
MDA             db 1    ;MDA is 'worst' case

VALIDMODE       db 0    ;= 1 if operating in a compatible video mode

WasError        db 0    ;= 32 if drive error (bg from blk on grn to blk on red)

Busy1C          dw 0    ;= 1 if already in our New_1C routine
OLD_1C          dd ?    ;interrupt vector for old user timer interrupt

CRTCaddr        dw 03D4h  ;CRTC reg addr
Video_Seg       dw 0B800h ;segment of video diplay
Video_Page      db 0      ;active video page
Video_PageOff   dw 0      ;offset into video segment of page

CmdLineParm     db 0    ;command line parameter  (preceded by a /)

StatLnOff       dw 0    ;offset into video buffer of Status line (4000/26,0/1)
StatLnCol       dw 0    ;column of first characater of StatLn
StatLnAttr      db 0    ;character attribute of StatLn
StatLn          db 'drv:   cyl:      sec:    head:   stat:                                          '
StatLnend       label word

TickTrip        equ 546         ;ticks approx = 30 seconds (546/18.2=30)

ticks           dw 0    ;tick cnt since HD active (if DoAuto and not parked)
DoAuto          db 0    ;= 1 then park if ticks > TickTrip
Parked          db 0    ;= 1 then HD is already parked
JustPark        db 0    ;= 1 if we just parked, = 0 if not
LastCylSec      db 0    ;last cylinder/sector in BIOS form
LastCyl         db 0    ;last cylinder (low)
LastSec         db 0    ;last sector
LastHead        db 0    ;last head

New_1C          PROC FAR
                ASSUME  ds:NOTHING, es:NOTHING
                sti
                push    ax
                push    bx
                push    cx
                push    dx
                push    bp
                push    di
                push    si
                push    es
                push    ds
                pushf                   ;preserve callings flags in case needed
                cld                     ;a must
                mov     ax,cs
                mov     ds,ax           ;point ds to data
                ASSUME  ds:DriveST_SEG
                inc     ticks           ;let's time the need for a park
                cmp     Busy1C,0        ;are we BUSY?
                jne     New_1Cxit
                mov     Busy1C,1        ;no, make us BUSY

                mov     ax,0040h
                mov     es,ax
                ASSUME  es:NOTHING

                cmp     DoAuto,1        ;autopark on?
                je      New_1C1         ;yes
                mov     ticks,0         ;no, zero ticks for a fresh start on /a

New_1C1:        call    Xlate_BIOS      ;es->BIOS
                cmp     ticks,TickTrip  ;need to park?
                jb      New_1C2         ;no
                cmp     Parked,1        ;already parked?
                je      New_1C2         ;yes
                call    ParkC

New_1C2:        call    Get_Video_Info  ;es->BIOS (after)
                cmp     VALIDMODE,1     ;are we valid?
                jne     New_1Cxit       ;no
                lea     dx,StatLn
                mov     cx,offset StatLnend - offset StatLn
                mov     ah,StatLnAttr

                cmp     MDA,1
                jne     New_1C3
                test    es:KBDflags1,18h;is <Alt> down or <Scroll Lock> on?
                jnz     New_1Cxit       ;yes, no display of MDA status

                cmp     WasError,0
                je      New_1C4

                mov     ah,07           ;normal video on error for MDA
                jne     New_1C4

New_1C3:        add     ah,WasError     ;WasError = (2*16) if disk error

New_1C4:        mov     bx,StatLnCol    ;start column of StatLn
                call    Write_StatLn

New_1Cxit:      popf
                pop     ds
                pop     es
                ASSUME  ds:NOTHING, es:NOTHING
                pop     si
                pop     di
                pop     bp
                pop     dx
                pop     cx
                pop     bx
                pop     ax
                cli                     ;turn off (OLD_1C or IRET turns it on)
                mov     cs:Busy1C,0     ;make us READY
                jmp     cs:OLD_1C
New_1C          ENDP

;
; get video system information
;

Get_Video_Info  PROC NEAR
                ASSUME  ds:DriveST_SEG, ds:NOTHING
                mov     ax,0040h
                mov     es,ax
                ASSUME  es:NOTHING
                mov     MDA,1           ;assume 'worst case'
                mov     VALIDMODE,0     ;ditto
                mov     CGA,0           ;these may change from zero
                mov     EGA_VGA,0       ;so zero then here
                cmp     es:modeCRT,2    ;valid modes are 2, 3, 7
                jb      GVIjmp          ;below 2 invalid
                cmp     es:modeCRT,7    ;7 ok
                je      GVI0
                cmp     es:modeCRT,4
                jb      GVI0            ;2 or 3 ok

GVIjmp:         jmp     GVIxit

                ; get the CRTC port address

GVI0:           cmp     es:addrCRTC,03D4h       ;color?
                jne     GVI1
                mov     MDA,0                   ;yes, we know it's not mono
                mov     Video_Seg,0B800h        ;video base segment of color
                mov     CRTCaddr,03D4h          ;its controller
                mov     StatLnOff,4000          ;StatLn offset (line 26)
                mov     StatLnCol,0             ;StatLn column (col 1)
                mov     StatLnAttr,0+(2*16)     ;black on green
                je      GVI2

GVI1:           mov     Video_Seg,0B000h        ;mono's video base segment
                mov     CRTCaddr,03B4h          ;its controller
                mov     StatLnOff,0             ;StatLn offset (line 1)
                mov     StatLnCol,0             ;StatLn column (col 1)
                mov     StatLnAttr,70h          ;reverse video
                jmp     short GVI4

GVI2:           cmp     es:EGAinfo,0            ;64K EGA or no EGA?
                jne     GVI2a                   ;no, an EGA with >64K
                cmp     es:EGAinfo2,0           ;no EGA at all?
                je      GVI3                    ;should be a CGA
GVI2a:          mov     EGA_VGA,1
                jne     GVI4

GVI3:           mov     CGA,1           ;it's CGA (MCGA may work, not tested)
GVI4:           mov     VALIDMODE,1

                ;calculate offset into video buffer of the current page

                mov     al,es:pageCRT
                cmp     al,Video_Page   ;still using same page?
                je      GVIxit
                mov     Video_Page,al   ;no, inform DST of new page
                xor     bh,bh
                mov     bl,Video_Page
                mov     ax,1000h        ;video page size (based on 80 X 25)
                mul     bx              ;times page (0-7)
                mov     Video_PageOff,ax
GVIxit:         ret
Get_Video_Info  ENDP

;
; program CRTC to display 26 lines
;

Set_Line26      PROC NEAR
                ASSUME  ds:DriveST_SEG

                cmp     MDA,1                   ;skip if MDA
                jne     SL261
                ret

SL261:          mov     dx,CRTCaddr             ;CRTC port address
                cmp     EGA_VGA,1
                jne     SL26_M6845
                mov     cx,EGA_data             ;set up EGA (byte 1 = # data)
                mov     si,offset EGA_26_data
                je      SL26_LR
SL26_M6845:     mov     cx,M6845_data           ;set up M6845 (CGA/MCGA)
                mov     si,offset M6845_26_data
SL26_LR:        cli                             ;interrupts off
SL26_LR1:       lodsb
                out     dx,al                   ;CRTC register
                lodsb
                inc     dx
                out     dx,al                   ;data
                dec     dx
                loop    SL26_LR1
                sti                             ;interrupts on
                ret
Set_Line26      ENDP

;
; program CRTC to display 25 lines
;

Set_Line25      PROC NEAR
                ASSUME  ds:DriveST_SEG

                cmp     MDA,1                   ;skip if MDA
                jne     SL251
                ret

SL251:          mov     dx,CRTCaddr             ;as Set_Line26
                cmp     EGA_VGA,1
                jne     SL25_M6845
                mov     cx,EGA_data
                mov     si,offset EGA_25_data
                jmp     short SL25_LR
SL25_M6845:     mov     cx,M6845_data
                mov     si,offset M6845_25_data
SL25_LR:        cli
SL25_LR1:       lodsb
                out     dx,al
                lodsb
                inc     dx
                out     dx,al
                dec     dx
                loop    SL25_LR1
                sti
                ret
Set_Line25      ENDP

;
; write the status
;

Write_StatLn    PROC NEAR
                ASSUME  ds:DriveST_SEG

                ;ENTER with...
                ;ds:dx -> DST write buffer
                ;cx: length of write buffer
                ;bx: start in this column of line
                ;ah: attribute of characters in line

                mov     bp,Video_Seg
                mov     es,bp
                ASSUME  es:NOTHING
                shl     bx,1             ;multiply column by 2 (2 bytes/column)
                add     bx,Video_PageOff ;add adjustment for video page
                add     bx,StatLnOff     ;add offset into video buff of StatLn
                mov     di,bx            ;es:di -> destination
                mov     si,dx            ;ds:si -> source
WR26:           lodsb                    ;get a character from the write buffer
                stosw                    ;store it and the attribute
                loop    WR26             ;print cx char/attr pairs
                ret
Write_StatLn    ENDP

Xlate_BIOS      PROC NEAR
                ASSUME  ds:DriveST_SEG, es:NOTHING
                ;es->BIOS SEG
                mov     al,es:drive     ;controller status/drive

                mov     di,offset StatLn+38
                mov     cx,6            ;length of messages

                test    al,11000000b    ;test for normal termination
                jz      XLB1
                lea     dx,ERRORMSG     ;error of some sort
                mov     si,dx
                mov     WasError,(2*16) ;attr=red bg on error (attr+WasError)
                jmp     short XLB2

XLB1:           lea     dx,OKMSG        ;normal termination
                and     al,0Fh          ;isolate low nybble
                cmp     al,8
                jb      XLB1a           ;a floppy
                cmp     Parked,1        ;are we parked?
                jne     XLB1a
                lea     dx,PARKMSG      ;yes
XLB1a:          mov     si,dx
                mov     WasError,0      ;normal attr

XLB2:           push    es
                push    cs
                pop     es              ;es:di -> write buffer
                ASSUME  es:NOTHING

                rep     movsb           ;write the message to the write buffer
                pop     es              ;get back es->BIOS
                ASSUME  es:NOTHING

                cmp     al,8            ;again, <8 = floppy
                jnb     XL_HD
                jmp     XL_floppy

XL_HD:          mov     al,"C"          ;I am assuming one HD and that it is C:
                mov     [StatLn+4],al

                mov     al,es:headHD    ;head

                cmp     LastHead,al     ;* check last head
                je      XLB3
                mov     ticks,0         ;different so zero ticks (safe)
                mov     LastHead,al     ;update new last head
                cmp     JustPark,1      ;just did a park?
                je      XLB3            ;yes
                mov     Parked,0        ;no, then we cannot be parked

XLB3:           add     al,'0'
                mov     [StatLn+30],al

                mov     ah,es:cylHI     ;let's get the cyl hi
                mov     LastCylSec,ah   ;used by ParkErrorError only
                rol     ah,1            ;move cyl hi bits @ 6&7 to bits 0&1
                rol     ah,1
                and     ah,00000011b    ;isolate cyl hi
                mov     al,es:cylLO     ;cylinder low (ax = 10 bit cylinder)

                cmp     LastCyl,al      ;* check last cylinder low (safe enough)
                je      XLB4            ;* note we're not checking cyl hi
                mov     ticks,0         ;different so zero ticks
                mov     LastCyl,al      ;update new last cylinder
                cmp     JustPark,1      ;just did a park?
                je      XLB4            ;yes
                mov     Parked,0        ;no, then we cannot be parked

XLB4:           mov     di,offset StatLn+11
                mov     cx,4            ;number of digits
                call    AX2DEC

                mov     al,es:cylHI     ;isolate sector
                and     al,00111111b    ;bits 0 - 5

                cmp     LastSec,al      ;* check last sector
                je      XLB5
                mov     ticks,0         ;different so zero ticks
                mov     LastSec,al      ;update new last sector
                cmp     JustPark,1      ;just did a park?
                je      XLB5            ;yes
                mov     Parked,0        ;no, then we cannot be  parked

XLB5:           xor     ah,ah
                mov     di,offset StatLn+21-2   ;-2 since cx = 4-2
                mov     cx,2
                call    AX2DEC
                mov     JustPark,0      ;no longer just did a park now
                ret

XL_floppy:      and     al,00000011b    ;drive 0-3
                add     al,'A'
                mov     [StatLn+4],al

                mov     al,es:headFD
                and     al,00000001     ;head 0-1
                add     al,'0'
                mov     [StatLn+30],al

                xor     ah,ah
                mov     al,es:cylLO     ;track
                mov     di,offset StatLn+11
                mov     cx,4            ;number of digits (HD cover up)
                call    AX2DEC

                xor     ah,ah
                mov     al,es:secFD
                mov     di,offset StatLn+21-2   ;-2 since cx = 4-2
                mov     cx,2
                call    AX2DEC
                ret
Xlate_BIOS      ENDP


;  ENTER with:
;       ax = int value 0-9999
;       cx = number of digits to interpret
;       di -> buffer
;  EXIT with:
;       ASCII value of ax in di->buffer

AX2DEC          PROC NEAR
                ASSUME  ds:DriveST_SEG

                ;divide by 10 - get remainder
                mov     bx,10                   ; divisor
                xor     dx,dx                   ; using AX as dividend
                div     bx                      ; remainder in dx
                add     dl,"0"                  ; to ASCII
                mov     byte ptr [di+3],dl      ; store it
                cmp     cx,1
                je      AX2DECxit
                ;tens
                xor     dx,dx
                div     bx
                add     dl,"0"
                mov     byte ptr [di+2],dl
                cmp     cx,2
                je      AX2DECxit
                ;hundreds
                xor     dx,dx
                div     bx
                add     dl,"0"
                mov     byte ptr [di+1],dl
                cmp     cx,3
                je      AX2DECxit
                ;thousands
                xor     dx,dx
                div     bx
                add     dl,"0"
                mov     byte ptr [di],dl
AX2DECxit:      ret
AX2DEC          ENDP


ParkC           PROC NEAR
                ASSUME  ds:DriveST_SEG

                mov     dl,80h
                mov     ah,08h
                int 13h                 ;get HD info (when dl >= 80h)
                                        ;ch = last cyl(lo) position
                                        ;cl = cyl(hi)(bits 6-7) & sec/cyl (0-5)
                                        ;dh = sides (0 based)
                                        ;dl = number of HD's
                cmp     dl,0            ;any HD's?
                je      ParkCxit
                cmp     dl,80h          ;HD BIOS not there?
                je      ParkCxit

                mov     dl,80h          ;select first HD
                push    cx              ;save cyl/sec info
                rol     cl,1            ;move bits 6 & 7 to bit positions 0 & 1
                rol     cl,1            ; moving sec/cyl to bits 2-7
                xchg    cl,ch           ;put hi cyl&sec in ch and lo cyl in cl
                and     cx,03FFh        ;make 10-bit last cyl & zero sector
                inc     cx              ;let's make it last cyl+1
                xchg    cl,ch           ;put hi cyl in cl and lo cyl in ch
                ror     cl,1            ;move bits 0 & 1 back to bit pos 6 & 7
                ror     cl,1            ; moving sec/cyl back to bits 0 - 5
                or      cl,01h          ;sector to 1 (physical HD sector 0)
                mov     dh,0            ;head to 0
                mov     ah,0Ch          ;seek to cylinder (AT BIOS documented)
                int 13h                 ; should work for >10MEG HD controller
                jc      ParkError       ;an error
                mov     Parked,1        ;we're parked
                mov     JustPark,1      ;just now did it
                mov     ticks,0         ;zero (for completeness)
                clc
                pop     cx              ;remove saved cyl/sec info
ParkCxit:       ret

ParkError:      mov     dl,80h          ;select first HD
                pop     cx              ;get saved cyl/sec info
                rol     cl,1
                rol     cl,1
                xchg    cl,ch
                and     cx,03FFh        ;no inc this time
                xchg    cl,ch
                ror     cl,1
                ror     cl,1
                or      cl,01h
                mov     dh,0
                mov     ah,0Ch          ;seek to last cyl (not last cyl+1)
                int 13h
                jc      ParkErrorError  ;another error
                mov     Parked,1
                mov     JustPark,1
                mov     ticks,0
                clc
ParkErrorxit:   ret

ParkErrorError: mov     dl,80h
                mov     ch,LastCyl      ;get last cylinder low
                mov     cl,LastCylSec   ;and last cylinder hi and sector
                mov     dh,0
                mov     ah,0Ch          ;seek to previous cylinder
                int 13h
                mov     Parked,0
                mov     JustPark,0
                mov     ticks,0
                stc
                ret
ParkC           ENDP

subttl Installation code

                ;===============================================
                ;let's install it - code after start is not kept
                ;===============================================

                ASSUME  cs:DriveST_SEG,ds:DriveST_SEG

start:          cld
                lea     dx, Copyright   ;it is PD material
                mov     ah,9
                int     21h             ;DOS print string

                call    GetParm

                ;---------------------
                not     byte ptr go     ;modify to avoid self-match
                xor     bx,bx           ;start search at segment zero (really 1)
                mov     ax,cs           ;compare to current code segment
start1:         inc     bx              ;look at next segment
                cmp     ax,bx           ;until reaching this code seg
                mov     es,bx
                je      start3
                mov     si,offset go    ;set up to compare strings
                mov     di,si
                mov     cx,16           ;16 bytes must match
                rep     cmpsb           ;compare ds:si to es:di
                or      cx,cx           ;did the strings match?
                jnz     start1          ;if no match, try next segment
                                        ;else fall through (already installed)
                                        ; with es -> matching code segment
                cmp     CmdLineParm,'-' ;unchain
                jne     startR
                jmp     Unchain
startR:         and     CmdLineParm,255-32  ;make it uppercase
                cmp     CmdLineParm,'R' ;restore 26 lines; awaken if dormant
                jne     startD
                jmp     RedoStatLn
startD:         cmp     CmdLineParm,'D' ;sleep
                jne     startA
                jmp     Sleep
startA:         cmp     CmdLineParm,'A' ;autopark
                jne     start?
                jmp     AutoPark
start?:         lea     dx,Use
                cmp     CmdLineParm,0
                jne     start2          ;invalid parm
                lea     dx,DupMsg       ;no parms?,(/) already installed msg
start2:         mov     ah,9
                int 21h
                mov     al,1
                jmp     Exit4c

start3:         lea     dx,Use          ;we're not installed
                mov     ah,9
                int 21h
                jmp     notinstalled

                ;---------------------
                ;es->match segment
AutoPark:       mov     es:ticks,0      ;set TSR data
                mov     es:Parked,0
                mov     es:JustPark,0
                mov     es:DoAuto,1

                lea     dx,AutoMsg
                mov     ah,9
                int 21h
                mov     al,0
                jmp     Exit4c

                ;---------------------
Sleep:          push    es              ;save TSR's segment
                mov     ax,351Ch        ;get current timer interrupt (1Ch)
                int     21h
                pop     ax
                mov     dx,es
                cmp     ax,dx           ;compare matched seg with 1Ch seg
                jne     Sleep2          ;cannot sleep since seg has changed
                push    ax
                pop     es              ;get back es
                mov     dx,es:word ptr Old_1C
                mov     ax,es:word ptr Old_1C+2
                mov     ds,ax
                mov     ax,251Ch
                int 21h
                mov     ax,cs           ;restore ds
                mov     ds,ax
                call    Get_Video_Info
                cmp     VALIDMODE,1
                mov     ax,0
                push    ax              ;save errorlevel
                jne     Sleep1
                call    Set_Line25      ;only if in a valid mode
Sleep1:         lea     dx,SleepMsg
                jmp     short Sleep3
Sleep2:         lea     dx,NoSleepMsg
                mov     ax,1
                push    ax              ;save errorlevel
Sleep3:         mov     ah,9
                int 21h
                pop     ax
                jmp     Exit4c

                ;---------------------
RedoStatLn:     push    es              ;save es
                call    Get_Video_Info
                pop     es
                cmp     VALIDMODE,1
                je      RedoStatLn1
                lea     dx,NoRestoreMsg
                mov     ah,9
                int 21h
                mov     al,1
                jmp     Exit4c
RedoStatLn1:    call    Set_Line26
                lea     dx,Redo26Msg
                mov     ah,9
                int 21h
                push    es              ;save es
                mov     ax,351Ch        ;get current timer interrupt (1Ch)
                int     21h             ; since it may have changed
                mov     cx,es           ;save int 21h's es into cx
                pop     es              ;get back es
                mov     dx,es
                cmp     cx,dx           ;if interrupt 1Ch has changed
                jne     RedoStatLn2     ; then need to awaken
                je      RedoStatLn3
RedoStatLn2:    mov     es:word ptr [Old_1C],bx    ;save current offset in TSR
                mov     es:word ptr [Old_1C+2],cx  ;save current segment
                mov     dx,offset New_1C
                push    es
                pop     ds              ;matching code seg
                ASSUME  ds:NOTHING
                mov     ax,251Ch
                int     21h             ;set interrupt (1Ch) (time to awaken)
                push    cs
                pop     ds              ;get back ds
                ASSUME  ds:DriveST_SEG
RedoStatLn3:    mov     ah,0
                jmp     Exit4c

                ;---------------------
                ;es -> matching segment
Unchain:        push    es              ;save TSR's segment
                mov     ax,351Ch        ;get current timer interrupt (1Ch)
                int     21h
                pop     ax
                mov     dx,es
                cmp     ax,dx           ;compare matched seg with 1Ch seg
                jne     Unchain4        ;cannot unchain since seg has changed
                mov     es,ax           ;get back es
                mov     dx,es:word ptr Old_1C
                mov     ax,es:word ptr Old_1C+2
                mov     ds,ax
                mov     ax,251Ch        ;set timer interrupt to interrupt valid
                int 21h                 ; when installed
                mov     ax,cs           ;restore ds
                mov     ds,ax

                not     es:byte ptr go  ;precludes matching on next install

                mov     cx,es           ;save es -> PSP/COM BLOCK (not in EXE's)
                mov     bx,es:[002Ch]   ;segment address of environment block
                mov     es,bx
                mov     ah,49h
                int 21h                 ;release the environment block
                jc      Unchain1        ;an error

                mov     es,cx
                mov     ah,49h          ;release the TSR block
                int 21h
                jnc     Unchain2

Unchain1:       lea     dx,DeAllocMsg   ;memory dealloc error
                mov     ah,9
                int     21h
                mov     al,1
                jmp     short Exit4c

Unchain2:       call    Get_Video_Info
                cmp     VALIDMODE,1
                jne     Unchain3
                call    Set_Line25      ;only if in a valid mode

Unchain3:       lea     dx,UniMsg
                mov     ah,9
                int 21h
                mov     al,00h

                ;---------------------
Exit4c:         mov     ah,4Ch
                int 21h

                ;---------------------
Unchain4:       lea     dx,NoUnchain
                mov     ah,9
                int 21h
                mov     al,1
                jmp     short Exit4c

                ;---------------------
notinstalled:   cmp     CmdLineParm,0   ;not yet installed, should be no parms
                je      notinstalled1
                lea     dx,NotInstalledMsg
                mov     ah,9
                int 21h
                mov     al,1
                jmp     short Exit4c

                ;---------------------
notinstalled1:  call    Get_Video_Info
                cmp     VALIDMODE,1     ;are we in a valid mode?
                je      notinstalled2
                lea     dx,NoInstallMsg
                mov     ah,9
                int 21h
                mov     al,1
                jmp     short Exit4c

                ;---------------------
notinstalled2:  mov     ax,351Ch        ;get timer interrupt (1Ch)
                int     21h
                mov     word ptr [Old_1C],bx    ;save offset
                mov     word ptr [Old_1C+2],es  ;save segment
                mov     dx,offset New_1C
                mov     ax,251Ch
                int     21h             ;set new interrupt (1Ch)

                call    Set_Line26

                call    EntryAt
                lea     dx,EntryAtMsg   ;1Ch entry address
                mov     ah,9
                int 21h

                mov     dx,offset start
                mov     cl,4
                shr     dx,cl
                inc     dl
                inc     dl
                mov     ax,3100h
                int     21h     ;Terminate - stay resident (keep up to start)

subttl Installation called routines

                ;---------------------
GetParm:        mov     si,81h
                mov     CmdLineParm,0
GetParm1:       mov     bl,[si]
                cmp     bl,0dh
                je      GetParm_Exit
                inc     si
                cmp     bl,'/'
                jne     GetParm1
                mov     bl,[si]
                mov     CmdLineParm,bl
GetParm_Exit:   ret

;
; byte in al to two-byte ASCII hex in ax (A5 to 'A5')
;

ConvByte2ASCII  PROC NEAR
                ASSUME  ds:DriveST_SEG

                push    cx
                mov     cl,4            ;mult x 16
                xor     ah,ah           ;just the low-order byte
                push    ax              ;save data passed in AX
                and     al,0F0h         ;hi-order nybble
                shr     al,cl           ;div hi-nybble by 16
ConvB:          cmp     al,9            ;hex letter?
                jna     ConvBdec
                add     al,55           ;10+55=65(A)
                jmp     short ConvB1
ConvBdec:       add     al,'0'          ;adjust ASCII to decimal 9+30h(0)=39h(9)
ConvB1:         cmp     cl,0            ;done both ASCII bytes?
                je      ConvB2          ;*** exit 2x loop here
                xchg    al,ch           ;save it
                pop     ax              ;get it again
                and     al,0Fh          ;low-order nybble
                mov     cl,0            ;make it last time around
                jmp     short ConvB     ;do low-order nybble
ConvB2:         xchg    ch,ah           ;get hi-order
                xchg    al,ah           ;put in correct order
                clc                     ;and we're done
                pop     cx
                ret

ConvByte2ASCII  endp

;
; get 1Ch entry address
;

EntryAt         PROC NEAR
                ASSUME  ds:DriveST_SEG

                mov     bx,offset EntryAtMsg+7
                mov     ax,cs
                xchg    ah,al
                call    ConvByte2ASCII  ;seg high byte
                mov     word ptr [bx],ax
                mov     ax,cs
                call    ConvByte2ASCII  ;seg low byte
                mov     word ptr [bx+2],ax
                mov     ax,offset New_1C
                xchg    ah,al
                call    ConvByte2ASCII  ;off high byte
                mov     word ptr [bx+5],ax
                mov     ax,offset New_1C
                call    ConvByte2ASCII  ;off low byte
                mov     word ptr [bx+7],ax
                ret

EntryAt         ENDP

subttl Installation messages

DeAllocMsg      db '*** Error in releasing memory - suggest a REBOOT ASAP!'
                db 13,10,7,'$'
NoSleepMsg      db '*** Cannot become dormant - interrupt 1Ch has changed.'
                db 13,10,7,'$'
NoInstallMsg    db '*** Cannot install - must be in BIOS video modes 2, 3, or 7.'
                db 13,10,7,'$'
NoRestoreMsg    db '*** Cannot restore - must be in BIOS video modes 2, 3, or 7.'
                db 13,10,7,'$'
NoUnchain       db '*** Cannot remove from memory - interrupt 1Ch has changed.'
                db 13,10,7,'$'
NotInstalledMsg db '*** Parameter ignored - DST is NOT installed.'
                db 13,10,7,'$'
DupMsg          db '*** Already installed - use DST /r to restore.'
                db 13,10,7,'$'
Copyright       db 'DST 1.0 - Public Domain material brought to you by Cornel Huth. '
                db 13,10,'$'
Use             db 13,10
                db 'Use:  DST     * install TSR ',13,10
                db '      DST /-  * remove from memory ',13,10
                db '      DST /d  * make dormant ',13,10
                db '      DST /r  * restore status line; awaken if dormant ',13,10
                db '      DST /a  * activate autopark of hard disk C: ',13,10
                db 13,10,'$'
EntryAtMsg      db '[0x1C] 0000:0000 '
                db 13,10,'$'
UniMsg          db 'Released from memory.  Use DST to install again.'
                db 13,10,'$'
SleepMsg        db 'Dormant.  Use DST /r to restore.'
                db 13,10,'$'
Redo26Msg       db 'Restored.'
                db 13,10,'$'
AutoMsg         db 'Autopark of hard disk C: activated.'
                db 13,10,'$'

DriveST_SEG     ENDS
                END go

