home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programming
/
powerprogramming1994.iso
/
progtool
/
pcmag
/
vol7n21.arc
/
LOG.ASM
< prev
next >
Wrap
Assembly Source File
|
1988-10-07
|
27KB
|
859 lines
;=============================================================================
; LOG maintains a log of system usage in an ASCII file. Syntax is:
; LOG [filespec] [/U]
; where filespec = Name of and/or path to log file, /U = Uninstall
;=============================================================================
CODE SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:CODE
ORG 100H
BEGIN: JMP INITIALIZE
PROGRAM DB "LOG 1.0 "
COPYRIGHT DB "(c) 1988 Ziff Communications Co.",13,10
AUTHOR DB "PC Magazine ",254," Jeff Prosise",13,10,"$",26
HANDLE DW ? ;file handle
DOSVERSION DW ? ;DOS version number
INT21H DD ? ;interrupt 21h vector
LEVEL DW 0 ;EXEC reentrancy level
EXECFLAG DW 0 ;EXEC flag
XERROR_AX DW ? ;extended error information
XERROR_BX DW ?
XERROR_CX DW ?
XERROR_DX DW ?
XERROR_SI DW ?
XERROR_DI DW ?
XERROR_DS DW ?
XERROR_ES DW ?
DW 3 DUP (0)
;=============================================================================
; DOSINT intercepts calls to interrupt 21h.
;=============================================================================
DOSINT PROC FAR
CMP AX,4B00H ;exit immediately if this
JNE EXEC0 ; isn't a call to EXEC
CMP CS:[LEVEL],9 ;exit if we've exceeded
JB EXEC1 ; reentrancy limits
EXEC0: JMP INT21H
;--Save registers passed to EXEC by the parent program.
EXEC1: STI ;interrupts on
PUSH AX ;save registers
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH BP
PUSH DS
PUSH ES
;--Record the program start/end time and write an entry to the log.
PUSH BX ;save registers set for
PUSH DX ; EXEC call
PUSH DS
PUSH ES
PUSH CS ;point DS to code segment
POP DS
ASSUME DS:CODE
INC LEVEL ;increment reentrancy count
CALL RECORD_TIME ;record the current time
MOV DX,OFFSET FILESPEC
CALL OPENFILE ;open the log file
MOV SI,LEVEL ;point SI to start time and
DEC SI ; DI to end time
SHL SI,1
SHL SI,1
ADD SI,OFFSET TIMES
MOV DI,SI
ADD DI,4
MOV AL,BYTE PTR LEVEL ;put previous level number
DEC AL ; in AL
CALL WRITE_ENTRY ;write log entry
CALL CLOSEFILE ;close the log file
MOV EXECFLAG,1 ;set EXEC flag
ASSUME DS:NOTHING
POP ES ;restore EXEC register
POP DS ; parameters
POP DX
POP BX
;--Record the name of the program just EXECed.
PUSH ES ;save ES and BX for later
PUSH BX
PUSH DS ;set ES equal to DS
POP ES
MOV DI,DX ;scan for terminating zero
XOR AL,AL ; in ASCIIZ filename
MOV CX,128
CLD
REPNE SCASB
MOV BX,127 ;transfer string length
SUB BX,CX ; to CX
MOV CX,BX
MOV SI,DI ;get ending address in SI
SUB SI,2 ;set SI to last character
STD ;set DF for reverse string ops
EXEC2: LODSB ;scan backwards for first
CMP AL,"\" ; character in filename
JE EXEC3
CMP AL,":"
JE EXEC3
LOOP EXEC2
DEC SI
EXEC3: ADD SI,2 ;point SI to first character
SUB BX,CX ;calculate length of filename
MOV CX,BX ;transfer length to CX
CLD ;clear DF again
PUSH CS ;set ES to code segment
POP ES
MOV AL,13 ;calculate offset into table
MOV DL,BYTE PTR CS:[LEVEL] ; of program names
MUL DL
MOV DI,AX
ADD DI,OFFSET NAMES
MOV AL,CL ;write filename character
STOSB ; count into table
EXEC5: LODSB ;convert lowercase characters
CMP AL,"a" ; to upper and copy filename
JB EXEC6 ; into table
CMP AL,"z"
JA EXEC6
AND AL,0DFH
EXEC6: STOSB
LOOP EXEC5
;--Record the command line passed to the program just EXECed.
POP BX ;restore ES and BX
POP ES
LDS SI,ES:[BX+2] ;load parm string address
MOV CL,[SI] ;get length
XOR CH,CH
INC CX
PUSH CS ;point ES:DI to command line
POP ES ; buffer in code segment
MOV DI,OFFSET COMLINE
REP MOVSB ;copy command line
;--Restore register values originally passed by the parent program.
POP ES
POP DS
POP BP
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
;--Save registers again since the DOS 2.X EXEC function destroys them.
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH BP
PUSH DS
PUSH ES
;--Save the values of SS and SP, call EXEC, and restore the stack.
MOV CS:[HANDLE],BX ;calculate an index into
MOV BX,CS:[LEVEL] ; the STACKSEG and
DEC BX ; STACKPTR tables
SHL BX,1
CLI ;disable interrupts and
MOV WORD PTR CS:[STACKSEG][BX],SS ; save the SS and SP
MOV WORD PTR CS:[STACKPTR][BX],SP ; registers
MOV BX,CS:[HANDLE]
PUSHF ;PUSH the flags register and
CALL INT21H ; call DOS EXEC
LAHF ;store flags temporarily in AH
MOV BX,CS:[LEVEL] ;recalculate table index
DEC BX
SHL BX,1
CLI ;disable interrupts and
MOV SS,WORD PTR CS:[STACKSEG][BX] ; restore SS and SP
MOV SP,WORD PTR CS:[STACKPTR][BX]
STI
SAHF ;restore flags
;--Restore the registers to their conditions before EXEC was called.
POP ES
POP DS
POP BP
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
;--Save registers again while post-processing is performed.
PUSHF ;then save it for exit
PUSH AX ;save general registers
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH BP
PUSH DS
PUSH ES
;--Save extended error information if DOS version is 3.10 or later.
PUSH CS ;set DS to code segment
POP DS
ASSUME DS:CODE
CMP DOSVERSION,030AH ;skip if not 3.10 or later
JB EXEC7
PUSH DS ;save DS
MOV AH,59H ;get extended error
XOR BX,BX ; information
INT 21H
MOV CS:[XERROR_DS],DS ;save return value of DS
POP DS ;set DS to code segment again
MOV XERROR_AX,AX ;save remaining register
MOV XERROR_BX,BX ; values in XERROR array
MOV XERROR_CX,CX
MOV XERROR_DX,DX
MOV XERROR_SI,SI
MOV XERROR_DI,DI
MOV XERROR_ES,ES
;--Record the program start/end time and write an entry to the log.
EXEC7: DEC LEVEL ;decrement reentrancy count
CALL RECORD_TIME ;record the current time
MOV DX,OFFSET FILESPEC
CALL OPENFILE ;open the log file
MOV SI,LEVEL ;point SI to start time and
INC SI ; DI to end time
SHL SI,1
SHL SI,1
ADD SI,OFFSET TIMES
MOV DI,SI
SUB DI,4
MOV AL,BYTE PTR LEVEL ;put previous level number
INC AL ; in AL
CALL WRITE_ENTRY ;write log entry
CALL CLOSEFILE ;close the log file
MOV EXECFLAG,0 ;clear EXEC flag
;--Restore extended error information if DOS version is 3.10 or later.
CMP DOSVERSION,030AH ;skip if not 3.10 or later
JB EXEC8
MOV AX,5D0AH ;restore information with
MOV DX,OFFSET XERROR_AX ; undocumented function 5Dh
INT 21H
;--Restore registers a final time and exit to the parent program.
ASSUME DS:NOTHING
EXEC8: POP ES ;restore registers
POP DS
POP BP
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
POPF
DOSEXIT: IRET
DOSINT ENDP
;-----------------------------------------------------------------------------
; OPENFILE opens an existing log file or creates a new one.
; Entry: DS - code segment
; Exit: CF: clear-opened/created, set-file could not be opened/created
;-----------------------------------------------------------------------------
ADDRSPEC DW ?
CALLFLAG DB 0
OPENFILE PROC NEAR
ASSUME DS:CODE
MOV ADDRSPEC,DX ;save filespec address
MOV AX,3D02H ;attempt to open an existing
INT 21H ; log file
JC OPEN3 ;branch if call failed
MOV HANDLE,AX ;store file handle
MOV BX,AX ;transfer it to BX
MOV AX,4202H ;position file pointer at
XOR CX,CX ; the end of the file
XOR DX,DX
INT 21H
CMP CALLFLAG,0 ;write date to log file
JNE OPEN1 ; if this is the first
INC CALLFLAG ; call to OPENFILE
OPEN0: CALL WRITE_DATE
CALL WRITE_HEADER ;then write header line
OPEN1: CLC ;clear CF for exit
OPEN2: RET ;exit
;--Call to open an existing file failed. Create a new one and initialize it.
OPEN3: MOV AH,3CH ;attempt to create a new
XOR CX,CX ; file of the same name
MOV DX,ADDRSPEC
INT 21H
JC OPEN2 ;exit on error
MOV HANDLE,AX ;store file handle
MOV BX,AX ;transfer it to BX
MOV CALLFLAG,1 ;set entry flag
MOV AH,40H ;write copyright text
MOV CX,70
MOV DX,OFFSET PROGRAM
INT 21H
JMP OPEN0 ;exit
OPENFILE ENDP
;-----------------------------------------------------------------------------
; CLOSEFILE closes the log file.
;-----------------------------------------------------------------------------
CLOSEFILE PROC NEAR
ASSUME DS:CODE
MOV AH,3EH
MOV BX,HANDLE
INT 21H
RET
CLOSEFILE ENDP
;-----------------------------------------------------------------------------
; WRITE_ENTRY writes a one-line entry to the log file.
; Entry: DS:SI - start time
; DS:DI - end time
; AL - last reentrancy level
;-----------------------------------------------------------------------------
LASTLEVEL DB ?
WRITE_ENTRY PROC NEAR
ASSUME DS:CODE
MOV LASTLEVEL,AL ;store last level number
CALL WRITE_TIME ;write start time
MOV CX,4
CALL WRITE_SPACES
PUSH SI ;save start time
MOV SI,DI ;write end time
CALL WRITE_TIME
MOV CX,3
CALL WRITE_SPACES
POP SI ;retrieve start time
CALL WRITE_DIFF ;write elapsed time
MOV CX,5
CALL WRITE_SPACES
MOV AL,LASTLEVEL ;write reentrancy level
MOV BL,1
CALL WRITE_NUM
MOV CX,6
CALL WRITE_SPACES
MOV AL,13 ;calculate offset into
MUL LASTLEVEL ; program name table
MOV DX,AX
ADD DX,OFFSET NAMES
MOV AH,40H ;write program name
MOV BX,DX
MOV CL,[BX]
XOR CH,CH
PUSH CX
MOV BX,HANDLE
INC DX
INT 21H
POP BX
CMP EXECFLAG,0 ;exit now if this entry is
JE NOPARMS ; a return from EXEC
MOV CX,12 ;calculate number of spaces
SUB CX,BX ; to skip
ADD CX,5
CALL WRITE_SPACES
MOV BX,OFFSET COMLINE ;write command line passed
MOV CL,[BX] ; to the last program
XOR CH,CH ; EXECed to the log file
JCXZ NOPARMS
MOV AH,40H
MOV BX,HANDLE
MOV DX,OFFSET COMLINE+1
INT 21H
NOPARMS: CALL WRITE_CRLF ;end line with CR/LF
RET
WRITE_ENTRY ENDP
;-----------------------------------------------------------------------------
; WRITE_DATE writes the current date to the log file.
;-----------------------------------------------------------------------------
MONTHS DB "JanFebMarAprMayJunJulAugSepOctNovDec"
CENTURY DB "19"
WRITE_DATE PROC NEAR
ASSUME DS:CODE
CALL WRITE_CRLF ;skip one line
MOV AH,2AH ;get date from DOS
INT 21H
PUSH CX ;save it
PUSH DX
MOV AL,DL ;write day of the month to
XOR BL,BL ; the log file
CALL WRITE_NUM
MOV CX,1 ;skip a space
CALL WRITE_SPACES
POP DX ;retrieve month from stack
DEC DH ;calculate offset into MONTH
MOV CL,DH ; table of text for the
XOR CH,CH ; current month
MOV DX,OFFSET MONTHS
JCXZ WRDATE2
WRDATE1: ADD DX,3
LOOP WRDATE1
WRDATE2: MOV AH,40H ;write month to log file
MOV BX,HANDLE
MOV CX,3
INT 21H
MOV CX,1 ;skip a space
CALL WRITE_SPACES
MOV AH,40H ;write "19" portion of year
MOV CX,2
MOV DX,OFFSET CENTURY
INT 21H
POP CX ;retrieve year from stack
SUB CX,1900 ;subtract century portion
MOV AL,CL ;write year to the log file
XOR BL,BL
CALL WRITE_NUM
CALL WRITE_CRLF ;finish with CRLF pairs
CALL WRITE_CRLF
RET
WRITE_DATE ENDP
;-----------------------------------------------------------------------------
; WRITE_TIME writes the time to the log file. Entry: DS:SI - time
;-----------------------------------------------------------------------------
COLON DB ":"
WRITE_TIME PROC NEAR
ASSUME DS:CODE
MOV AL,[SI+1] ;write hours to log file
MOV BL,1
CALL WRITE_NUM
MOV AH,40H ;write ":" to separate
MOV BX,HANDLE ; hours and minutes
MOV CX,1
MOV DX,OFFSET COLON
INT 21H
MOV AL,[SI] ;write minutes to log file
XOR BL,BL
CALL WRITE_NUM
RET
WRITE_TIME ENDP
;-----------------------------------------------------------------------------
; WRITE_DIFF writes the elapsed time to the log file.
; Entry: DS:SI - start time, DS:DI - end time
;-----------------------------------------------------------------------------
DELTA DB 3 DUP (?)
WRITE_DIFF PROC NEAR
ASSUME DS:CODE
MOV AL,[DI] ;retrieve starting and ending
MOV BL,[SI] ; seconds, minutes, and
MOV CL,[DI+1] ; hours
MOV DL,[SI+1]
MOV AH,[DI+2]
MOV BH,[SI+2]
CMP AH,BH ;if ending seconds is less
JAE WRDIFF1 ; than starting, add 60 to
ADD AH,60 ; ending seconds and
DEC AL ; decrement ending minutes
WRDIFF1: CMP AL,BL ;if ending minutes is less
JGE WRDIFF2 ; than starting, add 60 to
ADD AL,60 ; ending minutes and
DEC CL ; decrement ending hours
WRDIFF2: CMP CL,DL ;if ending hours is less
JGE WRDIFF3 ; than starting, add 24
ADD CL,24 ; to ending hours
WRDIFF3: SUB AH,BH ;calculate seconds difference
MOV DELTA+2,AH ;store it
SUB AL,BL ;calculate minutes difference
MOV DELTA,AL ;store it
SUB CL,DL ;calculate hours difference
MOV DELTA+1,CL ;store it
MOV SI,OFFSET DELTA ;write hours and minutes to
CALL WRITE_TIME ; the log file
MOV AH,40H ;write ":" to log file
MOV BX,HANDLE
MOV CX,1
MOV DX,OFFSET COLON
INT 21H
MOV AL,[SI+2] ;write seconds to log file
XOR BL,BL
CALL WRITE_NUM
RET
WRITE_DIFF ENDP
;-----------------------------------------------------------------------------
; WRITE_NUM converts a binary byte value to ASCII and writes it to the log
; file. Value must be between 0 and 99, inclusive.
; Entry: AL - byte value, BL - 0 = include leading zeroes, 1 = don't include
;-----------------------------------------------------------------------------
FILEWORD DW ?
WRITE_NUM PROC NEAR
ASSUME DS:CODE
AAM ;convert to BCD in AX
ADD AX,3030H ;convert to ASCII
OR BL,BL ;convert leading zero to
JZ WRNUM1 ; space if BL = 1 on
CMP AH,30H ; entry
JNE WRNUM1
MOV AH,20H
WRNUM1: XCHG AH,AL ;swap bytes
MOV FILEWORD,AX ;store them
MOV AH,40H ;then write them to the
MOV BX,HANDLE ; log file
MOV CX,2
MOV DX,OFFSET FILEWORD
INT 21H
RET
WRITE_NUM ENDP
;-----------------------------------------------------------------------------
; WRITE_HEADER writes the column titles to the log file.
;-----------------------------------------------------------------------------
TITLES DB "START",5 DUP (32),"END",5 DUP (32)
DB "ELAPSED",4 DUP (32),"LEVEL",4 DUP (32)
DB "PROGRAM",10 DUP (32),"PARAMETERS",13,10
EQUALSIGN DB "="
WRITE_HEADER PROC NEAR
ASSUME DS:CODE
MOV AH,40H ;write column titles
MOV BX,HANDLE
MOV CX,67
MOV DX,OFFSET TITLES
INT 21H
MOV CX,79 ;write row of "=" symbols
WRHEAD1: PUSH CX
MOV AH,40H
MOV CX,1
MOV DX,OFFSET EQUALSIGN
INT 21H
POP CX
LOOP WRHEAD1
CALL WRITE_CRLF ;end it with a CR/LF pair
RET
WRITE_HEADER ENDP
;-----------------------------------------------------------------------------
; WRITE_CRLF writes a carriage return / line feed to the log file.
;-----------------------------------------------------------------------------
CRLF DB 13,10
WRITE_CRLF PROC NEAR
ASSUME DS:CODE
MOV AH,40H
MOV BX,HANDLE
MOV CX,2
MOV DX,OFFSET CRLF
INT 21H
RET
WRITE_CRLF ENDP
;-----------------------------------------------------------------------------
; WRITE_SPACES writes CX spaces to the log file.
;-----------------------------------------------------------------------------
SPACE DB 32
WRITE_SPACES PROC NEAR
ASSUME DS:CODE
PUSH CX ;save counter
MOV AH,40H ;write one space
MOV BX,HANDLE
MOV CX,1
MOV DX,OFFSET SPACE
INT 21H
POP CX ;retrieve count
LOOP WRITE_SPACES ;loop until done
RET
WRITE_SPACES ENDP
;-----------------------------------------------------------------------------
; RECORD_TIME records the current time in the slot designated by LEVEL.
;-----------------------------------------------------------------------------
RECORD_TIME PROC NEAR
ASSUME DS:CODE
MOV AH,2CH ;get current time
INT 21H
MOV BX,LEVEL ;calculate offset into table
SHL BX,1 ; of start/end times
SHL BX,1
MOV WORD PTR TIMES[BX],CX ;store current time
MOV BYTE PTR TIMES[BX+2],DH
RET
RECORD_TIME ENDP
;=============================================================================
; 418-byte buffer area used after LOG becomes resident.
;=============================================================================
PC = $
FILESPEC = PC ;filespec buffer
PC = PC + 80
NAMES = PC ;storage array for program
PC = PC + 130 ; names
TIMES = PC ;storage array for program
PC = PC + 40 ; start/end times
STACKSEG = PC ;storage array for SS
PC = PC + 20 ; register values
STACKPTR = PC ;storage array for SP
PC = PC + 20 ; register values
COMLINE = PC ;command line parameter buffer
PC = PC + 128
LASTBYTE = PC
;=============================================================================
; INITIALIZE installs or uninstalls the program.
;=============================================================================
ERRMSG1 DB "Usage: LOG [filespec] [/U]$"
ERRMSG2 DB "Not Installed$"
ERRMSG3 DB "Cannot Uninstall$"
ERRMSG4 DB "Already Installed$"
ERRMSG5 DB "Invalid Filespec$"
OUTTEXT DB "Uninstalled$"
DEFNAME DB "\USAGE.LOG",13
IDLETEXT DB 6,"<idle>"
INSTALLED DB 0
INITIALIZE PROC NEAR
ASSUME CS:CODE, DS:CODE
;--See if a copy of LOG is already resident in memory.
CLD ;clear DF for string ops
MOV WORD PTR [BEGIN],0 ;initialize fingerprint
XOR BX,BX ;zero BX for start
MOV AX,CS ;keep CS value in AX
INIT1: INC BX ;increment search segment value
MOV ES,BX
CMP AX,BX ;not installed if current
JE PARSE1 ; segment is reached
MOV SI,OFFSET BEGIN ;search this segment for ASCII
MOV DI,SI ; fingerprint
MOV CX,16
REPE CMPSB
JNE INIT1 ;loop back if not found
MOV INSTALLED,1 ;set installed flag
;--Parse the command line for entries.
PARSE1: MOV SI,81H ;point SI to command line
PARSE2: LODSB ;get a character
CMP AL,20H ;skip it if it's a space
JE PARSE2
CMP AL,0DH ;exit loop when a carriage
JE NOSPEC ; return is encountered
CMP AL,"/" ;check for forward slash
JNE QUALIFY ;anything else is a filespec
LODSB ;get the next character
AND AL,0DFH ;capitalize it
CMP AL,"U" ;branch to uninstall code if
JE UNINSTALL ; character is a "U"
;--An error was encountered in parsing. Display error message and exit.
MOV DX,OFFSET ERRMSG1 ;load message address
ERROR_EXIT: MOV AH,9 ;display error message
INT 21H
MOV AX,4C01H ;exit with ERRORLEVEL = 1
INT 21H
;--Uninstall the program.
UNINSTALL: MOV DX,OFFSET ERRMSG2 ;error if program isn't
CMP INSTALLED,0 ; installed
JE ERROR_EXIT
CALL REMOVE ;call uninstall routine
MOV DX,OFFSET ERRMSG3 ;error if uninstall failed
JC ERROR_EXIT
MOV DX,OFFSET OUTTEXT ;display "Uninstalled"
MOV AH,9 ; message
INT 21H
MOV AX,4C00H ;exit with ERRORLEVEL = 0
INT 21H
;--Convert the filespec into a fully qualified filename.
NOSPEC: MOV SI,OFFSET DEFNAME+1 ;Use default filespec
QUALIFY: MOV DX,OFFSET ERRMSG4 ;error if program is already
CMP INSTALLED,0 ; installed
JNE ERROR_EXIT
DEC SI ;point DS:SI to filespec
PUSH CS ;point ES:DI to temporary
POP ES ; filespec buffer
MOV DI,OFFSET CWDIR+80
CALL GENSPEC ;generate complete filespec
MOV DX,OFFSET ERRMSG5 ;exit if error flag is set
JC ERROR_EXIT ; on return
MOV DI,OFFSET CWDIR+80 ;find the terminating zero in
XOR AL,AL ; the ASCIIZ string
MOV CX,80
REPNE SCASB
DEC DI
CMP BYTE PTR [DI-1],"\" ;append default filename if
JNE TESTSPEC ; last character is a
MOV SI,OFFSET DEFNAME+1 ; backslash
MOV CX,9
REP MOVSB
XOR AL,AL ;write ASCIIZ terminator
STOSB
;--Test the filespec and create the log file if it doesn't already exist.
TESTSPEC: MOV DX,OFFSET CWDIR+80
CALL OPENFILE ;open/create log file
MOV DX,OFFSET ERRMSG5 ;error if file could not
JC ERROR_EXIT ; be opened
CALL CLOSEFILE ;close log file
CALL RECORD_TIME ;record starting time
MOV AX,3000H ;record version of DOS
INT 21H ; LOG is running under
XCHG AH,AL
MOV DOSVERSION,AX
;--Hook into interrupt 21h and deallocate the program's environment block.
MOV AX,3521H ;save old interrupt vector
INT 21H
MOV WORD PTR INT21H,BX
MOV WORD PTR INT21H[2],ES
MOV AX,2521H ;then set the new vector
MOV DX,OFFSET DOSINT
INT 21H
MOV AX,DS:[2CH] ;deallocate the program's
MOV ES,AX ; environment block
MOV AH,49H
INT 21H
;--Initialize buffer areas.
PUSH CS ;point ES to code segment
POP ES
MOV SI,OFFSET CWDIR+80 ;copy filepsec string to
MOV DI,OFFSET FILESPEC ; filespec buffer
COPYLOOP: LODSB
STOSB
OR AL,AL
JNZ COPYLOOP
MOV SI,OFFSET IDLETEXT ;copy "<idle>" string to
MOV DI,OFFSET NAMES ; program names buffer
MOV CX,7
REP MOVSB
;--Display copyright notice, then terminate and remain resident in memory.
MOV AH,9 ;display copyright
MOV DX,OFFSET PROGRAM
INT 21H
MOV AX,3100H
MOV DX,(OFFSET LASTBYTE - OFFSET CODE + 15) SHR 4
INT 21H
INITIALIZE ENDP
;-----------------------------------------------------------------------------
; REMOVE deallocates the memory block addressed by ES and restores the
; interrupt vector displaced on installation.
; Entry: ES - segment to release
; Exit: CF clear - program uninstalled, CF set - can't uninstall
;-----------------------------------------------------------------------------
REMOVE PROC NEAR
MOV CX,ES ;abort if the interrupt 21h
MOV AX,3521H ; vector has been altered
INT 21H ; since installation
MOV AX,ES
CMP AX,CX
JNE REMOVE_ERROR
MOV ES,CX
MOV AH,49H ;free memory given to
INT 21h ; original program block
JC REMOVE_ERROR ;branch on error
PUSH DS ;restore interrupt vector
ASSUME DS:NOTHING
MOV AX,2521H
LDS DX,ES:[INT21H]
INT 21H
POP DS
ASSUME DS:CODE
NOT WORD PTR ES:[BEGIN] ;destroy ASCII fingerprint
CLC ;clear CF for exit
RET
REMOVE_ERROR: STC ;set CF to indicate program
RET ; couldn't be uninstalled
REMOVE ENDP
;-----------------------------------------------------------------------------
; GENSPEC generates a fully qualified filename.
; Entry: DS:SI - filename, ES:DI - filespec buffer
; Exit: CF clear - drive/directory valid, CF set - drive/directory invalid
;-----------------------------------------------------------------------------
ADDRIN DW ? ;input string address
ADDROUT DW ? ;output string address
DEFDRIVE DB ? ;default drive
ADDRSLASH DW ? ;address of last backslash
GENSPEC PROC NEAR
MOV ADDROUT,DI ;save output buffer address
;--Save the current drive and directory.
PUSH SI ;save SI
MOV AH,19H ;get current drive and
INT 21H ; save it
MOV DEFDRIVE,AL
MOV AH,47H ;get current directory and
XOR DL,DL ; save it
MOV SI,OFFSET CWDIR+1
INT 21H
POP SI ;restore SI
;--Check for a drive designator and supply default drive code if needed.
CMP BYTE PTR [SI+1],":" ;see if drive designator
JNE GEN1 ; appears in filespec
LODSW ; buffer and capitalize
AND AL,0DFH ; drive letter if it does
JMP SHORT GEN2
GEN1: MOV AL,DEFDRIVE ;get default drive letter
ADD AL,"A" ;convert to ASCII
MOV AH,":" ;append colon
GEN2: STOSW ;and output drive spec
;--Scan the input filespec for the rightmost backslash character.
MOV ADDRIN,SI ;save filespec address
XOR CX,CX ;initialize counter
GEN3: LODSB ;get next byte
CMP AL,0DH ;scan finished if character
JE GEN4 ; is a carriage return or
CMP AL,20H ; space
JE GEN4
CMP AL,"\" ;loop back if it's anything
JNE GEN3 ; but a backslash
MOV CX,SI ;save address of backslash
DEC CX ; character and return to
JMP GEN3 ; scan loop
;--Copy everything up to the last backslash to the output buffer.
GEN4: MOV ADDRSLASH,CX ;save backslash address
MOV SI,ADDRIN ;point SI back to filespec
OR CX,CX ;terminate string if no
JZ GEN6 ; backslashes found
CMP SI,CX ;copy leading backslash if
JE GEN5 ; if it was the only one
SUB CX,SI ;otherwise copy everything
DEC CX ; up to the last backslash
JCXZ GEN5
REP MOVSB
GEN5: MOVSB
GEN6: XOR AL,AL ;append binary zero to form
STOSB ; ASCIIZ string
;--Change to the drive and directory specified and get a full pathname.
MOV AH,0EH ;switch current drive
MOV DI,ADDROUT
MOV DL,[DI]
SUB DL,"A"
INT 21H
CMP BYTE PTR [DI+2],0
JE GEN7
MOV AH,3BH ;set current directory to
MOV DX,ADDROUT ; the one just formulated
ADD DX,2 ; and exit if the call fails
INT 21H
JC GEN_EXIT
GEN7: MOV AH,47H ;now request a complete
MOV SI,ADDROUT ; directory string from
MOV DL,[SI] ; DOS
SUB DL,40H
ADD SI,3
MOV BYTE PTR [SI-1],"\"
INT 21H
JC GEN_EXIT ;exit on error
;--Reset the default drive and directory.
MOV AH,0EH ;reset default drive
MOV DL,DEFDRIVE
INT 21H
MOV AH,3BH ;finish up by setting the
MOV DX,OFFSET CWDIR ; current directory to what
INT 21H ; it was when we started
JC GEN_EXIT
;--Append the filename to the directory specification if one was entered.
MOV DI,ADDROUT ;find ASCIIZ terminator byte
XOR AL,AL ; in output buffer
MOV CX,80
REPNE SCASB
DEC DI
CMP BYTE PTR [DI-1],"\" ;insert a backslash if
JE GEN8 ; there's not one at the
MOV AL,"\" ; end of the string
STOSB
GEN8: MOV SI,ADDRSLASH ;point SI to the first
OR SI,SI ; character in the
JNZ GEN9 ; input filename
MOV SI,ADDRIN
JMP SHORT GEN10
GEN9: INC SI
GEN10: LODSB ;copy characters until a
CMP AL,0DH ; carriage return or space
JE GEN11 ; delimiter is reached
CMP AL,20H
JE GEN11
STOSB
JMP GEN10
GEN11: XOR AL,AL ;append terminating zero byte
STOSB ; to string
CLC ;clear CF and exit
GEN_EXIT: RET
GENSPEC ENDP
CWDIR DB "\" ;directory string buffer
CODE ENDS
END BEGIN