Category : Files from Magazines
Archive   : VOL7N21.ZIP
Filename : LOG.ASM

 
Output of file : LOG.ASM contained in archive : VOL7N21.ZIP
;=============================================================================
; 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,""
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 "" 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


  3 Responses to “Category : Files from Magazines
Archive   : VOL7N21.ZIP
Filename : LOG.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/