PAGE ,132 TITLE ADI INT DRIVER FOR EPSON LQ SERIES PRINTERS ; THIS PROGRAM IS A MEMORY RESIDENT UTILITY THAT ; PROVIDES AN ADI DRIVER INTERRUPT PROCESSOR FOR ; AUTOCAD VERSION 2.5 OR HIGHER TO ALLOW PLOTTING ; ON AN EPSON LQ SERIES PRINTER OR COMPATIBLE PRINTER. ; (360 X 360 VERSION) ; ; BY PATRICK SWAYNE, HUG SOFTWARE ENGINEER 23-FEB-90 TRUE EQU 0FFFFH ;DEFINE TRUE FALSE EQU NOT TRUE ;AND FALSE TEST EQU FALSE ;ASSEMBLE TEST VERSION IF TRUE ADIINT EQU 7BH ;ADI INTERRUPT USED PORT EQU 0 ;PRINTER PORT TO USE (LPT1) TIMEOUT EQU 78H ;ADDRESS OF LPT TIMEOUT JMPF MACRO DB 0EAH ;DEFINE FAR JUMP ENDM CODE SEGMENT ASSUME CS:CODE,DS:CODE,ES:CODE,SS:CODE ; DEFINE SOME MEMORY LOCATIONS ORG 1 PSPSEG LABEL WORD ;PROGRAM SEGMENT PREFIX SEGMENT ADDR. ORG 3 MCBSIZE LABEL WORD ;MEMORY CONTROL BLOCK SIZE ORG 2CH ENVSEG LABEL WORD ;ENVIRONMENT SEGMENT ORG 5DH FCBARG LABEL BYTE ;FCB ARGUMENT ORG 100H START: JMP SETUP ;SET UP PROGRAM EVEN SIG DB 'ADIEPLQ 1.0' ; DATA AREA ADIVEC DW 0,0 ;OLD ADI INT VECTOR UNFLG DB 0 ;UNLOAD FLAG OTIMOUT DB 0 ;OLD TIMEOUT STLINE DW OFFSET LINES ;STARTING LINE LINPTR DW 0 ;LINE POINTER FEED DW 0 ;LINE FEED AMOUNT ; OUTPUT LINE BUFFER. THE LINE IS 48 TIMES THE LENGTH ; OF ONE INPUT LINE, BECAUSE OF 8 DOTS PER BYTE,3 BYTES ; PER COLUMN, AND DOUBLE PASS COLUMNS. ; (THE ACTUAL BUFFER IS NOT HERE. A COPY OF THIS HEADER ; IS MOVED TO THE ACTUAL BUFFER.) ;GRCODE DB 1BH,'<',1BH,'*',39 ;PRINT GRAPHICS COMMAND GRCODE DB 1BH,'<',1BH,'*',40 ;PRINT GRAPHICS COMMAND N1 DB 0 ;LENGTH CODES N2 DB 0 OUTLINE: IF TEST DB 8646 DUP (0) ;BUFFER IS HERE IF TEST ENDIF ENDFLG DB 0 ;END OF PLOT FLAG ENDMSG DB 1BH,'2',0CH ;RESET PRINTER MSG ENDMSZ EQU $-ENDMSG ; ADI INTERRUPT PROCESSOR ADIPROC:PUSH DX ;WE'LL USE THIS STI CMP AX,8001H ;START PLOT? JZ STPLOT ;YES CMP CS:ENDFLG,1 ;THIS PLOT ENDED? JNZ DOPLOT ;NO, CAN DO PLOT POP DX XOR AX,AX IRET DOPLOT: CMP AX,8002H ;END PLOT? JB DATALIN ;NO, MUST BE DATA LINE PUSH BX PUSH CX PUSH SI PUSH DI PUSH DS PUSH ES PUSH CS POP ES ;PUT ES HERE PUSH CS POP DS ;PUT DS HERE MOV ENDFLG,1 ;MARK END OF PLOT JMP PRTLIN ;PRINT THE LAST LINE STPLOT: CLD MOV CS:ENDFLG,0 ;CLEAR END FLAG PUSH DI PUSH CX PUSH ES MOV AX,CS MOV ES,AX ;PUT ES HERE MOV DI,OFFSET LINES ;POINT TO DATA MOV CX,(362*48)/2 MOV AX,0 REP STOSW ;CLEAR INPUT BUFFER IF TEST MOV DI,OFFSET OUTLINE ELSE MOV DI,OFFSET BA.OUTLIN ENDIF MOV CX,8646/2 REP STOSW ;CLEAR OUTPUT BUFFER MOV AX,40H MOV ES,AX ;POINT TO BIOS RAM MOV DI,OFFSET TIMEOUT+PORT ;POINT TO TIMEOUT FOR CURRENT PORT MOV AL,ES:[DI] ;GET OLD TIMEOUT MOV CS:OTIMOUT,AL ;SAVE IT MOV BYTE PTR ES:[DI],0FFH ;SET TIMEOUT TO MAX POP ES POP CX POP DI MOV AH,2 MOV DX,PORT ;GET PRINTER PORT INT 17H ;TEST PRINTER PORT AND AH,90H CMP AH,90H ;IS PRINTER READY? JNZ PRNBAD ;NO PRCH1: MOV AX,1BH ;AH = 0, AL = ESC INT 17H ;SEND ESCAPE TO INIT PRINTER TEST AH,1 JNZ PRNBAD ;PRINTER CONKED OUT PRCH2: MOV AX,'@' INT 17H ;RESET PRINTER TEST AH,1 JNZ PRNBAD MOV BX,0 ;INDICATE MONOCHROME PRNOK: MOV AX,1 ;RETURN INIT OK POP DX IRET PRNBAD: XOR AX,AX ;RETURN INIT NO GOOD POP DX IRET ; A DATA LINE HAS BEEN SENT. STORE IT, AND IF 48 LINES ; HAVE BEEN SENT, PRINT THEM. DATALIN:PUSH BX PUSH CX PUSH SI ;SAVE SOME REGISTERS PUSH DI PUSH DS PUSH ES PUSH CS POP ES ;PUT ES HERE OR AX,AX ;EMPTY LINE? JZ EMLIN ;IF SO, SKIP IT CMP AX,360 ;TEST LINE SIZE JBE DATAL1 ;OK MOV AX,360 ;ELSE, TRUNCATE DATAL1: MOV DS,CX ;DATA SEGMENT TO DS MOV SI,BX ;DATA OFFSET TO SI MOV CX,AX ;COUNT TO CX MOV AX,ES:LINPTR ;GET LINE POINTER PUSH BX MOV BX,362 ;AND SIZE OF LINE MUL BX ;GET LINE OFFSET POP BX ADD AX,OFFSET LINES ;AX = DESTINATION ADDRESS MOV DI,AX ;RESULT TO DI CLD MOV AX,CX STOSW ;STORE COUNT REP MOVSB ;MOVE THE DATA EMLIN: PUSH CS POP DS ;PUT DS HERE MOV AX,LINPTR ;GET LINE POINTER INC AL ;ADD 1 MOV LINPTR,AX ;UPDATE POINTER CMP AL,48 ;48 LINES DONE? JZ PRTLIN ;TIME TO PRINT LINE LINDN: POP ES POP DS POP DI POP SI POP CX POP BX JMP PRNOK ;EXIT, INDICATING OK PRTLIN: MOV LINPTR,0 ;RESET LINE POINTER ; FIND OUT WHICH LINE OF THE 24 WE'RE WORKING WITH IS LONGEST, ; AND USE LENGTH FOR CONVERSION COUNT BIGLP: MOV SI,STLINE ;POINT TO STARTING LINE MOV AX,[SI] ;GET COUNT THERE MOV CX,23 ;SET COUNTER FNDSIZ: ADD SI,362*2 ;MOVE TO NEXT LINE CMP AX,[SI] ;COMPARE SIZES JAE SIZOK ;CURRENT SIZE IS OK MOV AX,[SI] ;ELSE, GET BIGGER SIZE SIZOK: LOOP FNDSIZ MOV DX,AX ;SIZE TO DH PUSH DX ;SAVE COUNT ; DATA CONVERSION SECTION. WHAT WE HAVE TO DO IS CONVERT ; INFORMATION IN SCREEN (SCAN LINE) FORMAT (HORIZONTAL BYTES ; OF PIXELS) INTO PRINT HEAD FORMAT (VERTICAL BYTES OF DOTS). IF TEST MOV DI,OFFSET OUTLINE ;POINT TO OUTPUT LINE ELSE MOV DI,OFFSET BA.OUTLIN ;POINT TO OUTPUT LINE ENDIF MOV SI,STLINE ;POINT TO STARTING INPUT LINE INC SI ;GO PAST COUNT INC SI OR DX,DX ;TEST COUNT JZ DOPRT ;DON'T DO LOOP PUSH BX MLOOP: MOV BL,8 ;8 DOTS PER BYTE BLOOP: MOV BH,3 LOOP24: MOV AL,0 ;CLEAR OUTPUT BYTE MOV CX,8 ;8 LINES TO PROCESS SLOOP: SHL BYTE PTR [SI],1 ;SHIFT BYTE IN A LINE RCL AL,1 ;PUT RESULT IN OUTPUT BYTE ADD SI,362*2 ;MOVE TO NEXT LINE LOOP SLOOP ;UNTIL 8 LINES DONE STOSB ;STORE IT DEC BH ;COUNT THIS BLOCK OF 8 LINES JNZ LOOP24 ;UNTIL 24 LINES DONE SUB SI,362*48 ;MOVE BACK TO LINE 1 DEC BL ;COUNT THIS ROW OF DOTS JNZ BLOOP ;LOOP UNTIL INPUT BYTE PROCESSED INC SI ;MOVE TO NEXT BYTE DEC DX ;COUNT THIS BYTE JNZ MLOOP ;UNTIL ALL BYTES PROCESSED POP BX ; NOW, PRINT THE CONVERTED LINE, WHICH REPRESENTS 8 LINES OF ; DATA FROM AUTOCAD. DOPRT: MOV AX,STLINE ;GET STARTING LINE ADD AX,362 ;MOVE TO NEXT LINE MOV CX,1 ;ASSUME NOT 3RD LINE CMP AX,362*2+OFFSET LINES ;ON SECOND LINE? JNZ NOT2ND ;NO MOV CX,47 ;ELSE, PREPARE TO MOVE 47/360" MOV AX,OFFSET LINES ;AND RESET POINTER NOT2ND: MOV STLINE,AX ;FIX STARTING LINE MOV FEED,CX ;SAVE FEED AMOUNT POP DX PUSH DX OR DX,DX ;GOT A COUNT? JZ NOCNT ;NO XOR CX,CX ;ELSE, ZERO FEED NOCNT: MOV DX,PORT ;GET PRINTER PORT MOV AX,1BH INT 17H ;SEND ESC MOV AX,'+' ;MOVE IN 360TH INCH INCREMENTS INT 17H ;ESC-3 MOV AX,CX ;DO NOT FEED PAPER ON FIRST PASS INT 17H ;SET LINE SPACING POP DX ;GET CONVERSION COUNT MOV AX,8 ;GET MPYR (8 3-BYTE PAIRS/ACAD BYTE) MUL DX ;MULTIPLY BY COUNT IF TEST MOV N1,AL ;SET LINE COUNT MOV N2,AH ELSE MOV BA.L1,AL ;SET LINE COUNT MOV BA.L2,AH ENDIF MOV CX,AX ;COUNT TO CX PUSH CX MOV CX,7 ;GET LENGTH OF SETUP CODE IF TEST MOV SI,OFFSET GRCODE ;POINT OT OUTPUT LINE + GRAPHIC CODE ELSE MOV SI,OFFSET BA.GRCODE1 ;POINT TO OUTPUT LINE + GRAPHIC CODE ENDIF MOV DX,PORT ;SET PRINTER PORT PRLOOP: LODSB ;GET A BYTE MOV AH,0 INT 17H ;PRINT IT TEST AH,1 ;TEST PRINTER JNZ PRTBADY ;IT'S BAD LOOP PRLOOP ;LOOP UNTIL DONE POP CX ;GET COUNT OR CX,CX ;NULL COUNT? JZ NULCNT ;YES PUSH CX ;SAVE AGAIN PUSH SI ;SAVE POINTER PRLOOP1:CALL PRT3 ;PRINT 3 BYTES JNZ PRTBADX DEC CX ;LOOP DONE? JZ PRL1D CALL SKIP3 ;SKIP 3 BYTES JZ PRTOK PRTBADX:POP SI PRTBADY:POP CX PRTBADJ:JMP PRTBAD PRTOK: LOOP PRLOOP1 ;LOOP UNTIL DONE PRL1D: CALL PCRLF ;PRINT CR,LF JNZ PRTBADJ MOV AX,1BH INT 17H ;SEND ESC MOV AX,'+' ;MOVE IN 360TH INCH INCREMENTS INT 17H ;ESC-3 MOV AX,FEED ;GET AMOUNT TO FEED PAPER INT 17H ;SET LINE SPACING MOV CX,7 ;GET LENGTH OF SETUP CODE IF TEST MOV SI,OFFSET GRCODE ;POINT OT OUTPUT LINE + GRAPHIC CODE ELSE MOV SI,OFFSET BA.GRCODE1 ;POINT TO OUTPUT LINE + GRAPHIC CODE ENDIF PRLOOPA:LODSB ;GET A BYTE MOV AH,0 INT 17H ;PRINT IT TEST AH,1 ;TEST PRINTER JNZ PRTBADX ;IT'S BAD LOOP PRLOOPA ;LOOP UNTIL DONE POP SI ;RESTORE POINTER POP CX ;RESTORE COUNT PRLOOP2:CALL SKIP3 ;SKIP 3 BYTES JNZ PRTBAD DEC CX ;LOOP DONE? JZ NULCNT CALL PRT3 ;PRINT 3 BYTES JNZ PRTBAD LOOP PRLOOP2 NULCNT: CALL PCRLF ;PRINT CR,LF JNZ PRTBAD IF TEST MOV DI,OFFSET OUTLINE ELSE MOV DI,OFFSET BA.OUTLIN ENDIF MOV CX,8646/2 MOV AX,0 REP STOSW ;CLEAR OUTPUT BUFFER CMP STLINE,OFFSET LINES ;3RD LINE DONE? JZ PRTDN ;IF SO, DONE JMP BIGLP ;ELSE, PRINT ANOTHER LINE PRTDN: MOV DI,OFFSET LINES ;POINT TO DATA MOV CX,(362*48)/2 MOV AX,0 REP STOSW ;CLEAR INPUT BUFFER CMP ENDFLG,1 ;END OF PLOT? JNZ LINDNJ ;NO MOV SI,OFFSET ENDMSG ;POINT TO PRINTER RESET MSG MOV DX,PORT ;SET PRINTER PORT MOV CX,ENDMSZ ;NO. OF CHARACTERS TO PRINT PELOOP: LODSB ;GET A BYTE MOV AH,0 INT 17H ;PRINT IT TEST AH,1 ;TEST PRINTER JNZ PRTBAD ;IT'S BAD LOOP PELOOP ;LOOP UNTIL DONE MOV CL,OTIMOUT ;GET OLD TIMEOUT MOV AX,40H MOV DS,AX ;POINT TO BIOS RAM MOV SI,OFFSET TIMEOUT+PORT ;POINT TO CURRENT TIMEOUT MOV [SI],CL ;RESTORE OLD TIMEOUT LINDNJ: JMP LINDN ;LINE DONE, EXIT PRTBAD: POP ES POP DS POP DI POP SI POP CX POP BX JMP PRNBAD ;EXIT, INDICATING BAD ; PRINT 3 BYTES PRT3: PUSH CX MOV CX,3 PRT3LP: LODSB ;GET A BYTE MOV AH,0 INT 17H ;PRINT IT TEST AH,1 JNZ PRT3B LOOP PRT3LP XOR AH,AH PRT3B: POP CX RET ; SKIP 3 BYTES SKIP3: PUSH CX MOV CX,3 ADD SI,CX ;SKIP NEXT 3 BYTES SKIP3LP:XOR AX,AX INT 17H ;PRINT BLANK COLUMN TEST AH,1 JNZ PRT3B LOOP SKIP3LP XOR AH,AH POP CX RET PCRLF: MOV AX,0DH INT 17H ;PRINT CR TEST AH,1 JNZ PRTCRB MOV AX,0AH INT 17H ;PRINT LF TEST AH,1 PRTCRB: RET IF NOT TEST ; INT 28H PROCESSOR. USED FOR UN-INSTALLING ADIEPLQ. INT28: CMP CS:UNFLG,1 ;UN-INSTALL ADIEPLQ? JZ UNINS ;YES JMPF INT28V DW 0,0 ;ELSE, EXIT UNINS: PUSH CS POP DS ;POINT TO THIS SEGMENT XOR AX,AX MOV ES,AX ;AND TO INT. SEGMENT MOV SI,OFFSET ADIVEC ;POINT TO ADI INT VECTOR MOV DI,ADIINT*4 ;WHERE IT GOES CLD CLI MOVSW ;RESTORE VECTOR MOVSW MOV SI,OFFSET INT28V ;POINT TO INT 28 VECTOR MOV DI,28H*4 ;WHERE IT GOES MOVSW ;RESTORE VECTOR MOVSW PUSH CS POP ES ;PUT ES HERE MOV AH,49H INT 21H ;RELEASE THIS PROGRAM'S MEMORY MOV ES,ENVSEG ;POINT TO ENVIRONMENT SEGMENT MOV AH,49H INT 21H ;RELEASE ENVIRONMENT SEGMENT STI ;ENABLE INTERRUPTS INT 20H ;EXIT ENDIF BA: ;BUFFER AREA ; INPUT LINE BUFFERS. THERE IS ROOM FOR 48 LINES AT 360 ; BYTES PER LINE HERE, BECAUSE EVERY SECOND LINE IS USED ; TO MAKE UP 24 DOTS FOR THE PRINT HEAD. THE PRINT HEAD ; IS MOVED DOWN IN 1/2 LINE INCREMENTS. THE COUNT FOR ; EACH LINE IS STORED AT THE HEAD OF THAT LINE. LINES LABEL BYTE ;STORE LINES HERE IF TEST DB 362*48 DUP (0) ;STORE LINES HERE IF TEST VERSION ENDIF BUFF STRUC IF TEST DB ? ELSE DB 362*48 DUP (?) ;STORE LINES HERE IF NORMAL VERSION GRCODE1 DB 5 DUP (?) ;PRINT GRAPHICS COMMAND L1 DB ? ;LENGTH CODES L2 DB ? OUTLIN DB 8646 DUP (?) ENDIF ENDRES DB ? ;END OF RESIDENT CODE BUFF ENDS ; INSTALL THIS DRIVER IN MEMORY SETUP: MOV DX,OFFSET SIGNON MOV AH,9 INT 21H ;PRINT SIGN-ON CLD IF NOT TEST MOV SI,OFFSET GRCODE MOV DI,OFFSET BA.GRCODE1 MOV CX,OUTLINE-GRCODE REP MOVSB ;FIX OUTPUT LINE ENDIF MOV AH,52H INT 21H ;GET MEMORY CONTROL BLOCK ADDR. MOV AX,ES:[BX-2] ;GET FIRST MCB SEGMENT MOV DS,AX ;POINT TO IT FNDLP: MOV AX,PSPSEG ;GET PSP SEGMENT MOV DX,CS CMP AX,DX ;IN THIS SEGMENT? JAE CHKFND ;IF SO, CHECK IF ADIEPLQ FOUND MOV ES,AX ;ELSE, PSP SEGMENT TO ES MOV SI,OFFSET SIG MOV DI,SI PUSH DS ;SAVE MCB SEGMENT PUSH CS POP DS ;PUT DS HERE MOV CX,6 ;6 WORDS IN SIGNATURE REPZ CMPSW ;IS ADIEPLQ HERE? JZ GOTCC ;WE FOUND ADIEPLQ MOV LSTSEG,ES ;SAVE PSP SEGMENT FNDNXT: POP DS ;RESTORE MCB SEGMENT MOV AX,MCBSIZE ;GET MCB SIZE INC AX ;CORRECT IT MOV BX,DS ADD AX,BX ;CALCULATE NEXT MCB SEGMENT MOV DS,AX ;POINT TO IT JMP FNDLP ;TRY AGAIN GOTCC: MOV FNDSEG,ES ;SAVE SEGMENT WHERE FOUND MOV BYTE PTR FNDFLG,1 ;MARK PROGRAM FOUND JMP FNDNXT CHKFND: PUSH CS POP DS ;FIX DS CMP BYTE PTR FNDFLG,1 ;WAS ADIEPLQ FOUND? JNZ NOTFND ;NO MOV ES,FNDSEG ;GET SEGMENT WHERE FOUND CMP FCBARG,'U' ;UNINSTALL? JZ DOUNIN MOV DX,OFFSET ALMSG MOV AH,9 INT 21H ;SAY "ADIEPLQ ALREADY IN" INT 20H DOUNIN: MOV BX,FNDSEG ;GET SEGMENT WHERE FOUND CMP BX,LSTSEG ;COMPARE WITH LAST PSP FOUND JNC UNLOK ;OK TO UNLOAD MOV DX,OFFSET NOUNMSG MOV AH,9 INT 21H ;ELSE, SAY "CAN'T UNLOAD" INT 20H UNLOK: MOV DX,OFFSET UNMSG MOV AH,9 INT 21H ;PRINT UNLOAD MESSAGE MOV ES:UNFLG,1 ;FLAG UNLOAD DESIRED INT 20H ;AND EXIT NOTFND: CMP FCBARG,'U' ;UNINSTALL? JNZ DOINS ;NO MOV DX,OFFSET NINMSG MOV AH,9 INT 21H ;ELSE, SAY "ALREADY UNLOADED" INT 20H ;AND EXIT DOINS: PUSH CS POP ES ;FIX ES PUSH DS ;SAVE DS XOR AX,AX MOV DS,AX ;DS AT 0 MOV SI,OFFSET ADIINT*4 ;POINT TO ADI INTERRUPT MOV DI,OFFSET ADIVEC ;PUT OLD VECTOR HERE PUSH SI CLI ;TURN OFF INTERRUPTS CLD MOVSW ;MOVE VECTOR MOVSW POP SI MOV WORD PTR [SI],OFFSET ADIPROC ;PUT IN MY VECTOR MOV 2[SI],CS ;AND THIS SEGMENT IF NOT TEST MOV SI,OFFSET 28H*4 ;POINT TO INT 28 VECTOR MOV DI,OFFSET INT28V PUSH SI MOVSW ;COPY INT 28 VECTOR MOVSW POP SI MOV WORD PTR [SI],OFFSET INT28 ;SET NEW ONE MOV 2[SI],CS ENDIF STI POP DS IF NOT TEST MOV DX,OFFSET INMSG MOV AH,9 INT 21H ;SAY "ADIEPLQ INSTALLED" MOV DX,OFFSET BA.ENDRES ;POINT TO END OF RES. CODE INT 27H ;EXIT, LEAVE PROGRAM HERE ELSE MOV AX,8001H INT 7BH ;INITIALIZE PRINTER OR AL,AL ;TEST PRINTER JNZ GOON ;GO ON WITH TEST MOV DX,OFFSET SAYBAD MOV AH,9 INT 21H INT 20H GOON: MOV AX,48 ;GET A BYTE COUNT MOV BX,OFFSET GDATA ;POINT TO GRAPHIC DATA TSTLOOP:MOV CX,DS ;USE THIS DS PUSH AX ;SAVE COUNT PUSH BX ;AND POINTER INT 7BH ;CALL DRIVER POP BX POP AX DEC AX ;MAKE NEXT LINE SHORTER JNZ TSTLOOP MOV AX,8002H INT 7BH ;SIGNAL END XOR AX,AX MOV ES,AX ;AND TO INT. SEGMENT MOV SI,OFFSET ADIVEC ;POINT TO ADI INT VECTOR MOV DI,ADIINT*4 ;WHERE IT GOES CLD MOVSW ;RESTORE VECTOR MOVSW INT 20H SAYBAD DB 13,10,'Printer reported bad.$' GDATA DB 48 DUP (0FFH) ENDIF LSTSEG DW 0 ;LAST PSP SEGMENT (SHOULD BE < PSPSEG) FNDSEG DW 0 ;SEGMENT WHERE FOUND FNDFLG DB 0 ;PROGRAM FOUND FLAG SIGNON DB 13,10,'ADI Driver for Epson LQ series printers, v 1.0',13,10 DB 'By Patrick Swayne, HUG Software Engineer',13,10 DB "Copyright (C) 1990 by Heath/Zenith Users' Group",13,10,10,'$' INMSG DB 'ADIEPLQ is now installed.',13,10 DB 'To uninstall, type ADIEPLQ U.',13,10,'$' ALMSG DB 'ADIEPLQ is already installed.',13,10,'$' UNMSG DB 'ADIEPLQ is now uninstalled.',13,10,'$' NOUNMSG DB "ADIEPLQ is not the last TSR. Can't uninstall.",13,10,'$' NINMSG DB "ADIEPLQ is already not installed.",13,10,'$' CODE ENDS END START