home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 December
/
simtel1292_SIMTEL_1292_Walnut_Creek.iso
/
msdos
/
pcmag
/
vol6n22.arc
/
PRN2FILE.ASM
next >
Wrap
Assembly Source File
|
1987-09-24
|
25KB
|
719 lines
;----------------------------------------------------------------------
; PRN2FILE.ASM - A resident program which redirects printer output.
; SYNTAX: PRN2FILE d:path:filename.ext [/Pn] [/Bn] [/U]
; 1) Run PRN2FILE with the desired filename to activate it.
; 2) Run it again with no filename to turn off redirection.
; 3) Run it with a differant filename to change destination file.
; 4) Use /P to designate the printer number (defaults to 1)
; 5) Use /B to enter buffer size in K bytes (defaults to 4)
; 6) Use /U to uninstall the program
;----------------------------------------------------------------------
CSEG SEGMENT
ASSUME CS:CSEG,DS:NOTHING
ORG 100H ;Beginning for .COM programs
START: JMP INITIALIZE ;Initialization code is at end
;----------------------------------------------------------------------
; Data area used by this program
;----------------------------------------------------------------------
COPYRIGHT DB "PRN2FILE 1.0 (c) 1987 Ziff Communications Co.$",1AH
PROGRAMMER DB "Tom Kihlken"
REDIRECT_MESS DB "LPT"
PRN_NUM DB "1 Redirected to: $"
BAD_FILENAME DB "Invalid filename.$"
BAD_PARAM DB "Usage: PRN2FILE [path][filename][/Pn][/Bnn][/U]$"
BAD_ALLOC DB "Memory Allocation Error.$"
BAD_UNINSTALL DB "Cannot Uninstall.$"
PRN_TXT DB "PRN$"
CRLF DB 13,10,"$"
ERR_MESSAGE DB 13,10,"*Buffer Overflow*",13,10
MESS_LENGTH EQU $ - OFFSET ERR_MESSAGE
OLDINT08 DD ? ;Old timer tick interrupt vector
OLDINT17 DD ? ;Old printer output vector
OLDINT21 DD ? ;Old dos function interrupt vector
OLDINT28 DD ? ;Old dos waiting interrupt vector
DOS_FLAG DD ? ;Dos busy flag
SWITCH DB 0 ;On/off switch for redirecting printer
TIMEOUT DW 0 ;Holds timeout counter to flush buffer
INSTALLED_SEG DW 0 ;Segment location of installed copy
WRITE_FLAG DB 0 ;Indicates buffer should be written
PRINTER_NUM DW 0 ;Default to first parallel printer
BUFF_POINTER DW 0 ;Pointer to next space in buffer
BUFF_SIZE DW 4 ;Size of buffer
BUFF_SEGMENT DW 0 ;Segment address of buffer
TIME_TO_WRITE EQU 400H ;Flush buffer when this full
;-----------------------------------------------------------------------
; Interrupt 17 routine. (BIOS printer output)
; If output is to the selected printer and switch is on then redirect
; the character into a file.
;-----------------------------------------------------------------------
NEWINT17 PROC FAR
ASSUME DS:NOTHING, ES:NOTHING
CMP DX,CS:PRINTER_NUM ;Is this the selected printer?
JNE IGNORE ;If not, let bios handle it
CMP CS:SWITCH,1 ;Is redirection turned on?
JE REDIRECT_IT ;If on, take jump
IGNORE:
JMP CS:OLDINT17 ;Jump to the bios routine
REDIRECT_IT:
STI ;Get interrupts back on
MOV CS:TIMEOUT,91 ;Reset timeout counter
PUSH SI ;Si will be used for a pointer
CMP AH,1 ;Initializing the printer?
JE WRITE_BUFF ;If yes, then flush the buffer
OR AH,AH ;Printing a character?
JNZ PRINT_RET ;If not, take jump to return
MOV SI,CS:BUFF_POINTER ;Get pointer to the buffer
CMP SI,CS:BUFF_SIZE ;Is buffer filled up yet?
JE PRINT_RET ;If full just return.
PUSH DS ;Save the data segment
MOV DS,CS:BUFF_SEGMENT ;Load DS with the buffer seg
MOV DS:[SI],AL ;Store the character in buffer
POP DS ;Restore data segment
INC SI ;And point to next position
MOV CS:BUFF_POINTER,SI ;Save the new pointer
CMP SI,TIME_TO_WRITE ;Is buffer filling up yet?
JL PRINT_RET ;If not, just return
WRITE_BUFF:
MOV CS:WRITE_FLAG,1 ;Signal buffer needs emptying
PUSH DS
PUSH BX
LDS BX,CS:DOS_FLAG ;Get location of dos flag
CMP BYTE PTR [BX],0 ;Is dos busy flag set?
POP BX
POP DS
JNE PRINT_RET ;If busy, do nothing
CALL WRITE_TO_FILE ;This empties the buffer
PRINT_RET:
POP SI
MOV AH,10010000B ;Return printer status good
IRET ;Return from interrupt
NEWINT17 ENDP
;----------------------------------------------------------------------
; New interrupt 08h (timer tick) decrement the timeout counter. Set
; the flush flag when counter reaches zero.
;----------------------------------------------------------------------
NEWINT08 PROC FAR
ASSUME DS:NOTHING, ES:NOTHING
PUSHF ;Simulate an interrupt
CALL CS:OLDINT08 ;Do normal timer routine
DEC CS:TIMEOUT ;Count down the flush time count
JNZ STILL_TIME ;Count until it gets to zero
CMP CS:BUFF_POINTER,0 ;Anything in buffer?
JE STILL_TIME ;If not, just continue
MOV CS:WRITE_FLAG,1 ;Set flush trigger
STILL_TIME:
IRET ;Return from timer interrupt
NEWINT08 ENDP
;----------------------------------------------------------------------
; Interrupt 21 routine. (DOS function calls) intercept function 40h
; when it writes to the printer. Also check to see if WRITE_FLAG is
; set to one. If it is then flush the buffer.
;----------------------------------------------------------------------
NEWINT21 PROC FAR
ASSUME DS:NOTHING, ES:NOTHING
PUSHF ;Save the callers flags
CMP CS:WRITE_FLAG,1 ;Buffer need to be written?
JNE DONT_WRITE ;If not, then just return
PUSH DS
PUSH BX
LDS BX,CS:DOS_FLAG ;Get location of DOS flag
CMP BYTE PTR [BX],0 ;Is DOS busy flag set?
POP BX
POP DS
JNE DONT_WRITE ;If busy, do nothing
CALL WRITE_TO_FILE ;Empty the buffer now
DONT_WRITE:
OR AH,AH ;Doing function zero?
JNE NOT_ZERO
MOV AX,4C00H ;If yes, change it to 4Ch
NOT_ZERO:
CMP AH,40H ;Writing to a device?
JNE NOT_PRINTER ;If not, just continue
CMP BX,4 ;Writing to the printer handle?
JNE NOT_PRINTER ;If not, just continue
CMP CS:SWITCH,1 ;Is redirection on?
JE PRINT_IT ;If yes, then redirect it
NOT_PRINTER:
POPF ;Recover flags from stack
CLI
JMP CS:OLDINT21 ;Do the DOS function
; Emulate print string function by involking INT 17h
PRINT_IT:
STI ;Reenable interrupts
CLD ;String moves forward
PUSH CX ;Save these registers
PUSH DX
PUSH SI
MOV SI,DX ;Get pointer to string
MOV DX,PRINTER_NUM ;Selected printer ID in DX
JCXZ END_LOOP ;Skip loop if count is zero
PRINT_LOOP:
LODSB ;Load next character from string
MOV AH,00 ;Print character function
INT 17H ;BIOS print
LOOP PRINT_LOOP ;Loop through whole string
END_LOOP:
POP SI
POP DX
POP CX
MOV AX,CX ;All bytes were output
POPF ;Restore the callers flags
CLC ;Return success status
STI ;Reenable interrupts
RET 2 ;Return with current flags
NEWINT21 ENDP
;----------------------------------------------------------------------
; This copies the buffer contents to a file. It should only be called
; when dos is in a reentrant condition. All registers are preserved
;----------------------------------------------------------------------
WRITE_TO_FILE PROC NEAR
ASSUME DS:NOTHING, ES:NOTHING
PUSH AX ;Save registers we need to use
PUSH BX
PUSH CX
PUSH DX
PUSH DS
PUSH ES
PUSH CS
POP DS ;Set DS to code segment
ASSUME DS:CSEG ;Tell assembler DS is CSEG
MOV WRITE_FLAG,0 ;Clear write request flag
MOV AX,3524H ;Get dos critical error vector
CALL DOS_FUNCTION ;Do the dos function
PUSH BX ;Save old vector on stack
PUSH ES
; Replace the dos severe error interrupt with our own routine.
MOV DX,OFFSET NEWINT24
MOV AX,2524H ;Setup to change int 24h vector
CALL DOS_FUNCTION ;Do the dos function
; First try to open the file. If dos returns with the carry flag set,
; the file didn't exist and we must create it. Once the file is opened,
; advance the file pointer to the end of file to append.
CMP BUFF_POINTER,0 ;Anything in the buffer?
JE REP_VECTOR ;If not, no nothing
MOV DX,OFFSET FILENAME ;Point to filename
MOV AX,3D02H ;Dos function to open file
CALL DOS_FUNCTION ;Do the dos function
JC FILE_NOT_FOUND ;Set if file doesn't exist.
MOV BX,AX ;Keep handle in BX also
XOR CX,CX ;Move dos file pointer to the
XOR DX,DX ;End of the file. this lets us
MOV AX,4202H ;Append this to an existing file
CALL DOS_FUNCTION ;Do the dos function
JC CLOSE_FILE ;On any error, take jump
JMP SHORT WRITE_FILE
FILE_NOT_FOUND:
CMP AX,2 ;Was it file not found error?
JNE REP_VECTOR ;If not, just quit
MOV CX,0020H ;Attribute for new file
MOV AH,3CH ;Create file for writing
CALL DOS_FUNCTION ;Do the dos function
JC CLOSE_FILE ;On any error, take jump
MOV BX,AX ;Save handle in BX also
WRITE_FILE: MOV DX,0 ;Point to buffer
MOV CX,BUFF_POINTER ;Number of chars in buffer
MOV AH,40H ;Dos write to a device function
PUSH DS
MOV DS,BUFF_SEGMENT ;Point to buffer segment
CALL DOS_FUNCTION ;Do the dos function
POP DS
JC CLOSE_FILE ;On any error, take jump
CMP CX,AX ;Was everything written
JNE CLOSE_FILE ;If not, it was an error
CMP CX,BUFF_SIZE ;Was buffer full?
JNE CLOSE_FILE ;If not everything is OK
MOV DX,OFFSET ERR_MESSAGE ;Insert the error message
MOV CX,MESS_LENGTH
MOV AH,40H ;Dos write to file function
CALL DOS_FUNCTION ;Do the dos function
CLOSE_FILE:
MOV AH,3EH ;Dos function to close the file
CALL DOS_FUNCTION ;Do the dos function
REP_VECTOR:
MOV BUFF_POINTER,0 ;Indicate buffer is empty
POP DS ;Recover int 24h vector from stack
POP DX
MOV AX,2524H ;Restore critical error vector
CALL DOS_FUNCTION ;Do the dos function
ASSUME DS:NOTHING
POP ES ;Restore all registers
POP DS
POP DX
POP CX
POP BX
POP AX
RET ;Finished with writing to disk
WRITE_TO_FILE ENDP
;----------------------------------------------------------------------
; This routine emulates an INT 21 by calling the dos interrupt address
;----------------------------------------------------------------------
DOS_FUNCTION PROC NEAR
ASSUME DS:NOTHING, ES:NOTHING
PUSHF ;Save the processor flags
CLI ;Clear interrupt enable bit
CALL CS:OLDINT21 ;Execute the interupt procedure
STI ;Enable further interrupts
RET ;And return to calling routine
DOS_FUNCTION ENDP
;----------------------------------------------------------------------
; New interrupt 24h (critical dos error). This interrupt is only in
; effect when writing to the disk. It is required to suppress the
; 'Abort, Retry, Ignore' message. All fatal disk errors are ignored.
;----------------------------------------------------------------------
NEWINT24 PROC FAR
ASSUME DS:NOTHING, ES:NOTHING
STI ;Turn interrupts back on
XOR AL,AL ;Tells dos to ignore the error
MOV CS:SWITCH,AL ;Turn off logging of output
IRET ;And return to dos
NEWINT24 ENDP
;----------------------------------------------------------------------
; New interrupt 28h (DOS idle). Check to see if write_flag is set to
; one. If it is, then flush the buffer
;----------------------------------------------------------------------
NEWINT28 PROC FAR
ASSUME DS:NOTHING, ES:NOTHING
STI
CMP CS:WRITE_FLAG,0 ;Buffer need to be written?
JE DO_NOTHING ;If not, just continue
CALL WRITE_TO_FILE ;Empty the buffer
DO_NOTHING:
JMP CS:OLDINT28 ;Continue with old interrupt
NEWINT28 ENDP
;----------------------------------------------------------------------
; Here is the code used to initialize prn2file.com. First determine
; if prn2file is already installed. If it is, just copy new parameters
; into the resident programs data area, otherwise save old vectors
; and replace with new ones. The output buffer will later overlay
; this code to conserve memory.
;----------------------------------------------------------------------
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING
INITIALIZE:
MOV DX,OFFSET COPYRIGHT
CALL STRING_CRLF ;Display the string
; Search for a previously installed copy of prn2file
NOT WORD PTR START ;Modify to avoid false match
XOR BX,BX ;Start search at segment zero
MOV AX,CS ;Compare to this code segment
NEXT_SEGMENT:
INC BX ;Look at next segment
CMP AX,BX ;Until reaching this code seg
MOV ES,BX
JE NOT_INSTALLED
MOV SI,OFFSET START ;Setup to compare strings
MOV DI,SI
MOV CX,16 ;16 bytes must match
REP CMPSB ;Compare DS:SI to ES:DI
OR CX,CX
JNZ NEXT_SEGMENT ;If no match, try next segment
MOV ES:SWITCH,1 ;Turn redirection on
MOV DX,ES:PRINTER_NUM ;Retrieve old printer number
MOV DS:PRINTER_NUM,DX ;Save it here
MOV AH,1 ;Initialize the resident copy
INT 17H ;To flush it's buffer
ADD DL,31H ;Convert printer num to ascii
MOV PRN_NUM,DL ;Put it into the message area
NOT_INSTALLED:
MOV INSTALLED_SEG,ES
PUSH CS
POP ES ;Set ES to this segment
ASSUME ES:CSEG
CMP BYTE PTR DS:[0080],0 ;Anything entered?
JE NO_PARAMS ;If not, take jump
PARSE:
MOV AL,"/" ;Look for a slash
CALL LOAD_PARAMS
REPNE SCASB ;Scan for slashes
JCXZ PARSE_DONE ;Quit when no more slashes
MOV AL,[DI] ;Get the parameter
MOV WORD PTR [DI-1],2020H;Erase the slash and letter
OR AL,32 ;Convert to lower case
CMP AL,"p" ;Is it the "p" parameter
JE SLASH_P
CMP AL,"b" ;Is it the "b" parameter
JE SLASH_B
CMP AL,"u" ;Is it the "u" parameter
JE SLASH_U
INVALID_PARAM:
MOV DX,OFFSET BAD_PARAM ;Point to error message
JMP ERR_EXIT
SLASH_U:
JMP UNINSTALL ;Slash "u" means uninstall it
SLASH_B:
MOV BUFF_SIZE,0 ;Zero buff size for accumulator
NEXT_DIGIT:
MOV AX,BUFF_SIZE ;Get current buff size
MOV BL,10
MUL BL ;Times 10 for next digit
INC DI ;Point to next digit
MOV BL,[DI] ;And get the next one
SUB BL,30H ;Convert it to binary
JC PARSE ;If not a digit, keep parsing
CMP BL,9
JA PARSE ;If not a digit, keep parsing
MOV BYTE PTR [DI]," ";Erase character from command
XOR BH,BH
ADD AX,BX ;Add in this digit
MOV BUFF_SIZE,AX ;And save the new total
JMP NEXT_DIGIT
SLASH_P:
INC DI ;Point to the printer number
MOV AL,[DI]
MOV BYTE PTR [DI]," ";Erase this char from command
MOV PRN_NUM,AL ;Put it in the message area
SUB AL,31H ;Convert it to printer number
XOR AH,AH ;Make it a word
CMP AL,3 ;Printer id must be less than 3
JAE INVALID_PARAM ;If it isn't, take jump
MOV PRINTER_NUM,AX ;Store the parameter
JMP PARSE ;Look for more parameters
NO_PARAMS:
MOV DX,OFFSET REDIRECT_MESS ;Point to message
MOV AH,9 ;Display the string of text
INT 21H ;Using DOS display function
MOV DX,OFFSET PRN_TXT ;Point to "PRN"
CALL STRING_CRLF ;Display the string
MOV AL,0 ;Turn off redirection switch
JMP CHECK_FOR_INSTALL
PARSE_DONE:
CMP BUFF_SIZE,1 ;Buff must be at least 1K
JB INVALID_PARAM ;If not, exit with error
CMP BUFF_SIZE,64 ;Check for maximum buff size
JA INVALID_PARAM ;If above, exit with error
MOV AL," " ;Look for spaces
CALL LOAD_PARAMS
REPE SCASB ;Scan for non-space character
JCXZ NO_PARAMS ;Any letters found?
CMP BYTE PTR [DI],":" ;Was a drive specified?
JNE GET_DEF_DRIVE ;If not, get the default drive
DEC DI ;Now DI points to first letter
MOV AL,[DI] ;Get drive letter in AL
MOV WORD PTR [DI],2020H;Erase the drive and colon
JMP STORE_DRIVE
GET_DEF_DRIVE:
MOV AH,19H ;Get default drive
INT 21H
ADD AL,65 ;Convert integer drive to ascii
STORE_DRIVE:
MOV AH,":" ;AL has drive, AH has colon
MOV WORD PTR FILENAME,AX ;Store drive and colon
MOV AL,"\" ;Look for a backslash
MOV FILENAME+2,AL ;Add a backslash to filename
CALL LOAD_PARAMS
REPNE SCASB ;Scan for a backslash
JCXZ GET_DEF_PATH ;If no path, use current path
MOV DI,OFFSET FILENAME+2 ;Location to store path
JMP STORE_PATH
GET_DEF_PATH:
MOV DL,FILENAME ;Selected drive letter
AND DL,11011111B ;Convert it to upper case
SUB DL,64 ;Convert it to integer
MOV SI,OFFSET FILENAME + 3 ;Put current path at SI
MOV DI,SI ;Save this for search later
MOV AH,47H ;DOS get current directory
INT 21H
JC BAD_NAME_EXIT ;Exit if invalid drive
MOV AL,0 ;Look for end of path
CMP [DI],AL ;Was there any path?
JE STORE_PATH ;If not, don't scan it
MOV CX,64 ;Maximum number of bytes in path
REPNE SCASB ;Scan for end of path string
MOV BYTE PTR [DI-1],"\" ;Add the trailing backslash
STORE_PATH:
PUSH DI ;Save location to append path
MOV AL," " ;Look for blank spaces
CALL LOAD_PARAMS
REPE SCASB ;Scan for non-blank character
MOV SI,DI
DEC SI ;This is first letter of path
POP DI ;Get back location to append
COPY_PATH:
LODSB ;Get next char of path
CMP AL," " ;Is it a blank?
JE VERIFY_NAME ;If yes, its the last char
CMP AL,13 ;Is it a carriage return?
JE VERIFY_NAME ;If yes, its the last char
STOSB ;Store this letter
JMP COPY_PATH ;Copy until end of path found
VERIFY_NAME:
PUSH DI ;Save end of string location
MOV BYTE PTR [DI],"$" ;Mark eos for dos display
MOV DX,OFFSET REDIRECT_MESS ;Point to message
MOV AH,9 ;Display the string of text
INT 21H ;Using dos display function
MOV DX,OFFSET FILENAME ;Point to filename for display
CALL STRING_CRLF ;Display the string
POP DI
MOV BYTE PTR [DI],0 ;Now make it an ascii string
MOV DX,OFFSET FILENAME ;Dx points to the filename
MOV AX,3D00H ;Open this file for reading
INT 21H
JC OPEN_ERR ;Error may indicate not found
CLOSE_IT:
MOV BX,AX ;Get the handle into BX
MOV AH,3EH ;Close the file
INT 21H
JMP FILENAME_OK
OPEN_ERR:
MOV CX,0020H ;Attribute for new file
MOV AH,3CH ;Create file for writing
INT 21H ;Dos function to create file
JNC CLOSE_IT ;If no error, just close it
BAD_NAME_EXIT:
MOV DX,OFFSET BAD_FILENAME
ERR_EXIT:
CALL STRING_CRLF ;Display the string
INT 20H ;Just exit to dos
FILENAME_OK:
MOV ES,INSTALLED_SEG;Point to installed program
PUSH DS:PRINTER_NUM ;This moves the new printer
POP ES:PRINTER_NUM ;number to the resident copy
MOV DI,OFFSET FILENAME ;Setup to copy the filename
MOV SI,DI
MOV CX,128 ;Copy entire file specification
REP MOVSB ;String move instruction
MOV AL,1 ;Turn redirection on
CHECK_FOR_INSTALL:
MOV CX,CS
CMP CX,INSTALLED_SEG
MOV ES,INSTALLED_SEG
MOV ES:SWITCH,AL ;Store the new on/off switch
JE INSTALL ;If not installed yet, do it now
INT 20H ;Otherwise terminate
;----------------------------------------------------------------------
; This subroutine displays a string followed by a CR and LF
;----------------------------------------------------------------------
STRING_CRLF PROC NEAR
MOV AH,9 ;Display the string of text
INT 21H ;Using dos display function
MOV DX,OFFSET CRLF ;Now point to CR/LF characters
MOV AH,9 ;Send the CR and LF
INT 21H
RET
STRING_CRLF ENDP
;----------------------------------------------------------------------
; This subroutine sets DI to the command line and CX to the byte count
;----------------------------------------------------------------------
LOAD_PARAMS PROC NEAR
MOV DI,80H ;Point to parameter area
MOV CL,CS:[DI] ;Get number of chars into CL
XOR CH,CH ;Make it a word
INC DI ;Point to first character
CLD ;String search forward
RET
LOAD_PARAMS ENDP
;----------------------------------------------------------------------
; This code does the actual installation by storing the existing
; interrupt vectors and replacing them with the new ones.
; Then allocate memory for the buffer. Exit and remain resident.
;----------------------------------------------------------------------
ASSUME DS:CSEG, ES:CSEG
INSTALL:
MOV BX,OFFSET END_OF_CODE ;Get end of resident code
ADD BX,15
MOV CL,4 ;Shift by 4 to divide by 16
SHR BX,CL ;This converts to paragraphs
MOV AH,4AH ;Modify memory block
INT 21H ;Dos setblock function call
JNC ALLOCATE_BUFFER ;If it worked ok, then continue
ALLOC_ERROR:
MOV DX,OFFSET BAD_ALLOC ;Err message for bad allocation
JMP ERR_EXIT ;Display message and exit
ALLOCATE_BUFFER:
MOV BX,BUFF_SIZE ;Buffer size in K bytes
MOV CL,6 ;Shift by 6 to get paragraphs
SHL BX,CL ;Buffersize is in paragraphs
MOV AH,48H
INT 21H ;Dos allocate memory
JC ALLOC_ERROR ;If allocation error, take jump
MOV BUFF_SEGMENT,AX ;Save the segment for the buffer
MOV AX,BUFF_SIZE ;Buffer size in K bytes
MOV CL,10 ;Shift by 10 to get bytes
SHL AX,CL
OR AX,AX ;Is buff_size=0 (64K)?
JNZ SIZE_OK
DEC AX ;If yes, make it FFFFh
SIZE_OK:
MOV BUFF_SIZE,AX ;Now buff_size is in bytes
ASSUME ES:NOTHING
MOV AH,34H ;Get dos busy flag location
INT 21H
MOV WORD PTR [DOS_FLAG] ,BX ;Store flag address
MOV WORD PTR [DOS_FLAG+2],ES
MOV AX,3508H ;Get timer interrupt vector
INT 21H
MOV WORD PTR [OLDINT08] ,BX
MOV WORD PTR [OLDINT08+2],ES
MOV DX, OFFSET NEWINT08
MOV AX, 2508H
INT 21H ;Dos function to change vector
MOV AX,3517H ;Get printer interrupt vector
INT 21H
MOV WORD PTR [OLDINT17] ,BX
MOV WORD PTR [OLDINT17+2],ES
MOV DX, OFFSET NEWINT17
MOV AX, 2517H
INT 21H ;Dos function to change vector
MOV AX,3521H ;Get dos function vector
INT 21H
MOV WORD PTR [OLDINT21] ,BX
MOV WORD PTR [OLDINT21+2],ES
MOV DX, OFFSET NEWINT21
MOV AX, 2521H
INT 21H ;Dos function to change vector
MOV AX,3528H ;Get dos waiting vector
INT 21H
MOV WORD PTR [OLDINT28] ,BX
MOV WORD PTR [OLDINT28+2],ES
MOV DX, OFFSET NEWINT28
MOV AX, 2528H
INT 21H ;Dos function to change vector
;----------------------------------------------------------------------
; Deallocate our copy of the enviornment. Exit using interrupt 27h
; (TSR). This leaves code and space for buffer resident.
;----------------------------------------------------------------------
MOV AX,DS:[002CH] ;Get segment of enviornment
MOV ES,AX ;Put it into ES
MOV AH,49H ;Release allocated memory
INT 21H
MOV DX,(OFFSET END_OF_CODE - OFFSET CSEG + 15)SHR 4
MOV AX,3100H
INT 21H ;Terminate and stay resident
;----------------------------------------------------------------------
; This procedure removes PRN2FILE from memory by replacing the vectors
; and releasing the memory used for the code and buffer.
;----------------------------------------------------------------------
ASSUME DS:CSEG, ES:NOTHING
UNINSTALL:
MOV AL,08H ;Check the timer interrupt
CALL CHECK_SEG ;If changed, can't uninstall
JNE CANT_UNINSTALL
MOV AL,17H ;Check the printer interrupt
CALL CHECK_SEG ;If changed, can't uninstall
JNE CANT_UNINSTALL
MOV AL,21H ;Check dos interrupt
CALL CHECK_SEG ;If changed, can't uninstall
JNE CANT_UNINSTALL
MOV AL,28H ;Check dos idle interrupt
CALL CHECK_SEG ;If changed, can't uninstall
JNE CANT_UNINSTALL
MOV ES,INSTALLED_SEG
ASSUME DS:NOTHING, ES:NOTHING
LDS DX,ES:OLDINT08 ;Get original vector
MOV AX,2508H
INT 21H ;Dos function to change vector
LDS DX,ES:OLDINT17 ;Get original vector
MOV AX,2517H
INT 21H ;Dos function to change vector
LDS DX,ES:OLDINT21 ;Get original vector
MOV AX,2521H
INT 21H ;Dos function to change vector
LDS DX,ES:OLDINT28 ;Get original vector
MOV AX,2528H
INT 21H ;Dos function to change vector
MOV ES,ES:BUFF_SEGMENT;Get segment of buffer
MOV AH,49H ;Free its allocated memory
INT 21H
JC RELEASE_ERR ;If error, take jump
MOV ES,INSTALLED_SEG;The resident program segment
NOT WORD PTR ES:START
MOV AH,49H ;Free its allocated memory
INT 21H
JC RELEASE_ERR ;If error, take jump
MOV AX,4C00H
INT 21H ;Exit to dos
RELEASE_ERR:
MOV DX,OFFSET BAD_ALLOC ;Memory allocation error
JMP ERR_EXIT ;Exit with error message
CANT_UNINSTALL:
MOV DX,OFFSET BAD_UNINSTALL ;Point to error message
JMP ERR_EXIT ;Exit with error message
;----------------------------------------------------------------------
; This subroutine checks to see if an interrupt vector points to the
; installed program segment. Returns with ZF=1 if it does.
;----------------------------------------------------------------------
CHECK_SEG PROC NEAR
MOV AH,35H ;Get the vector
INT 21H ;Dos function to get the vector
MOV AX,ES
CMP AX,INSTALLED_SEG;Is it the installed segment?
RET
CHECK_SEG ENDP
;----------------------------------------------------------------------
FILENAME LABEL BYTE ;File name will go here
END_OF_CODE = $ + 128 ;Allow 128 bytes for it
CSEG ENDS
END START