Metropoli BBS
VIEWER: install.asm MODE: TEXT (ASCII)
;****************************************************************************
; INSTALL inserts a bookmark in memory that allows TSRs installed above it
; to be removed from memory. Its syntax is:
;
;       INSTALL [bookmark]
;
; where "bookmark" is the name of the bookmark. The bookmark name can be
; passed to REMOVE when a bookmark is removed. Only the first 32 characters
; of the name are used.
;****************************************************************************

code            segment
                assume  cs:code
                org     100h
begin:          jmp     init

nextblock       dw      0FFFFh                  ;Pointer to next block
bookmark        db      "(No Name)",24 dup (0Dh);Default bookmark name
signature       db      0,1,2,"INSTALL",2,1,0   ;Program signature
vectors         dw      512 dup (?)             ;Interrupt vectors
prog_id         db      ?                       ;Multiplex ID number
int2Fh          dd      ?                       ;Interrupt 2FH vector

;****************************************************************************
; MPLEX_INT handles interrupt 2FH. If, on entry, AH is set to INSTALL's
; multiplex ID number, MPLEX_INT uses the value in AL as a function code.
; The functions supported are:
;
;    00H    Returns FFH in AL to indicate the program is installed
;           and returns the address of its signature in ES:DI.
;****************************************************************************

mplex_int       proc    far
                pushf                           ;Save FLAGS register
                cmp     ah,cs:[prog_id]         ;Branch if AH holds the
                je      mplex2                  ;multiplex ID
                popf                            ;Restore FLAGS
                jmp     cs:[int2Fh]             ;Pass the interrupt on
;
; Function 00H verifies that the program is installed.
;           
mplex2:         popf                            ;Restore FLAGS
                or      al,al                   ;Branch if function code
                jnz     mplex3                  ;is other than 00H
                mov     al,0FFh                 ;Set AL to FFH
                push    cs                      ;Point ES:DI to the program
                pop     es                      ;signature
                mov     di,offset signature
mplex3:         iret                            ;Return from interrupt
mplex_int       endp

;****************************************************************************
; Data that will be discarded when the program becomes memory-resident.
;****************************************************************************

helpmsg         db      "Places a bookmark in memory that marks TSRs for "
                db      "unloading.",13,10,13,10
                db      "INSTALL [bookmark]",13,10,13,10
                db      "  bookmark  Name of the bookmark being installed."
                db      13,10,13,10
                db      "Bookmarks can be removed with INSTALL's companion "
                db      "program, REMOVE. When a",13,10
                db      "bookmark is removed, all the TSRs installed after "
                db      "it are removed, too.",13,10,"$"

errmsg1         db      "Requires DOS 3.0 or higher",13,10,"$"
errmsg2         db      "INSTALL cannot be loaded high",13,10,"$"
errmsg3         db      "Program could not be installed",13,10,"$"

msg1            db      "INSTALL 1.3 Copyright (c) 1993 Jeff Prosise",13,10
                db      "From: PC Magazine DOS 6 Memory Management "
                db      "with Utilities",13,10,13,10
                db      "Bookmark created. Use REMOVE to remove it."
                db      13,10,"$"

;****************************************************************************
; INIT makes the program resident in memory.
;****************************************************************************

                assume  cs:code,ds:code

init            proc    near
                cld                             ;Clear direction flag
                mov     si,81h                  ;Point SI to command line
                call    scanhelp                ;Scan for "/?" switch
                jnc     checkver                ;Branch if not found
                mov     ah,09h                  ;Display help text and exit
                mov     dx,offset helpmsg       ;with ERRORLEVEL=0
                int     21h
                mov     ax,4C00h
                int     21h
;
; Check the DOS version and make sure the program isn't loaded high.
;
checkver:       mov     dx,offset errmsg1       ;Exit if DOS version
                mov     ah,30h                  ;is less than 3.0
                int     21h
                cmp     al,3
                jae     checkaddr

error:          mov     ah,09h                  ;Display error message and
                int     21h                     ;exit with ERRORLEVEL=1
                mov     ax,4C01h
                int     21h

checkaddr:      mov     dx,offset errmsg2       ;Error if program is loaded
                mov     ax,cs                   ;at segment address A000H or
                cmp     ax,0A000h               ;higher
                jae     error
;
; Parse the command line.
;
                call    findchar                ;Advance to first character
                jc      copy                    ;Branch if no parameters
                mov     di,offset bookmark      ;Point DI to buffer
                mov     cx,32                   ;Initialize counter
parse1:         lodsb                           ;Get a character
                cmp     al,"a"                  ;Capitalize it if it's
                jb      parse2                  ;between "a" and "z"
                cmp     al,"z"
                ja      parse2
                and     al,0DFh
parse2:         stosb                           ;Store it
                cmp     al,0Dh                  ;Branch if was a carriage
                je      copy                    ;return
                loop    parse1                  ;Loop back for more
;
; Copy the interrupt vector table from low memory.
;
copy:           push    ds                      ;Save DS
                sub     si,si                   ;Point DS:SI to 0000:0000
                mov     ds,si
                assume  ds:nothing
                mov     di,offset vectors       ;Point DI to storage area
                mov     cx,512                  ;Initialize counter
                cli                             ;Disable interrupts
                rep     movsw                   ;Copy the vector table
                sti                             ;Enable interrupts
                pop     ds                      ;Restore DS
                assume  ds:code
;
; Install the program.
;
                call    check_install           ;Branch if a copy of INSTALL
                jnc     install_full            ;is not already installed

                mov     di,offset nextblock     ;Point DI to NEXTBLOCK
install1:       cmp     word ptr es:[di],0FFFFh ;Branch if this is not the
                jne     install2                ;last block in the chain
                mov     word ptr es:[di],cs     ;Record the address of this
                jmp     short install_part      ;block if it is
install2:       mov     es,es:[di]              ;Point ES to next block in
                jmp     install1                ;the chain and loop back

install_full:   call    mplex_id                ;Find a multiplex ID number
                mov     dx,offset errmsg3       ;Error if none available
                jc      error
                mov     prog_id,ah              ;Save the ID number

                mov     ax,352Fh                ;Hook interrupt 2FH
                int     21h
                mov     word ptr int2Fh,bx
                mov     word ptr int2Fh[2],es
                mov     ax,252Fh
                mov     dx,offset mplex_int
                int     21h

install_part:   mov     ah,49h                  ;Get the segment address of
                mov     es,ds:[2Ch]             ;the environment block
                int     21h                     ;and free the segment

                mov     ah,09h                  ;Display message verifying
                mov     dx,offset msg1          ;the installation
                int     21h

                mov     ax,3100h                ;Terminate with function 31H
                mov     dx,(offset helpmsg - offset code + 15) shr 4
                int     21h
init            endp

;****************************************************************************
; FINDCHAR advances SI to the next non-white-space character. On return,
; carry set indicates EOL was encountered.
;****************************************************************************

findchar        proc    near
                lodsb                           ;Get the next character
                cmp     al,09h                  ;Loop if tab
                je      findchar
                cmp     al,20h                  ;Loop if space
                je      findchar
                cmp     al,2Ch                  ;Loop if comma
                je      findchar
                dec     si                      ;Point SI to the character
                cmp     al,0Dh                  ;Exit with carry set if end
                je      eol                     ;of line is reached

                clc                             ;Clear carry and exit
                ret

eol:            stc                             ;Set carry and exit
                ret
findchar        endp

;****************************************************************************
; SCANHELP scans the command line for a /? switch. If found, carry returns
; set and SI contains its offset. If not found, carry returns clear.
;****************************************************************************

scanhelp        proc    near
                push    si                      ;Save SI
scanloop:       lodsb                           ;Get a character
                cmp     al,0Dh                  ;Exit if end of line
                je      scan_exit
                cmp     al,"?"                  ;Loop if not "?"
                jne     scanloop
                cmp     byte ptr [si-2],"/"     ;Loop if not "/"
                jne     scanloop

                add     sp,2                    ;Clear the stack
                sub     si,2                    ;Adjust SI
                stc                             ;Set carry and exit
                ret

scan_exit:      pop     si                      ;Restore SI
                clc                             ;Clear carry and exit
                ret
scanhelp        endp

;****************************************************************************
; CHECK_INSTALL returns carry set if the program is already installed,
; carry clear if it's not. If carry returns set, AH holds the program's
; multiplex ID number.
;****************************************************************************

check_install   proc    near
                mov     ax,0C000h               ;Initialize AH and AL
                mov     cx,40h                  ;Initialize count

chinst1:        push    ax                      ;Save AX and CX
                push    cx
                sub     di,di                   ;Set ES and DI to 0
                mov     es,di
                int     2Fh                     ;Interrupt 2Fh
                cmp     al,0FFh                 ;Nothing here if AL isn't
                jne     chinst2                 ;equal to FFH

                mov     si,offset signature     ;See if program signature
                mov     cx,13                   ;appears at the address
                repe    cmpsb                   ;returned in ES:DI
                jne     chinst2                 ;Branch if it does not

                pop     cx                      ;Clear the stack and exit
                pop     ax                      ;with carry set
                stc
                ret

chinst2:        pop     cx                      ;Retrieve AX and CX
                pop     ax
                inc     ah                      ;Next multiplex ID
                loop    chinst1                 ;Loop until done

                clc                             ;Exit with carry clear
                ret
check_install   endp

;****************************************************************************
; MPLEX_ID searches for an unused multiplex ID number. If one is found,
; it is returned in AH with carry clear. Carry set means no multiplex
; ID numbers are currently available.
;****************************************************************************

mplex_id        proc    near
                mov     ax,0C000h               ;Initialize AH and AL
                mov     cx,40h                  ;Initialize count

mxid1:          push    ax                      ;Save AX and CX
                push    cx
                int     2Fh                     ;Interrupt 2Fh
                or      al,al                   ;Branch if AL=0
                jz      mxid2
                pop     cx                      ;Retrieve AX and CX
                pop     ax
                inc     ah                      ;Increment ID number
                loop    mxid1                   ;Loop until done

                stc                             ;Exit with carry set
                ret

mxid2:          pop     cx                      ;Clear the stack and exit
                pop     ax                      ;with carry clear
                clc
                ret
mplex_id        endp

code            ends
                end     begin
[ RETURN TO DIRECTORY ]