Category : Files from Magazines
Archive   : VOL7N22.ZIP
Filename : ENVELOPE.ASM

 
Output of file : ENVELOPE.ASM contained in archive : VOL7N22.ZIP
;======================================================================
; ENVELOPE: Print envelopes on LaserJet Printers * PC Magazine
;-----------------------------------------------------------------------
; EQUATES
;-----------------------------------------------------------------------
LF EQU 0Ah ; Line feed
CR EQU 0Dh ; Carriage return
ESCAPE EQU 1Bh ; Escape
SCAN_CODE EQU 18 ; scan code for E
SHIFT_MASK EQU 8 ; shift status for Alt
LJ_MODEL EQU 1B ; Printer Flag Bit in status
PR_RET_ADD EQU 10B ; Print Return Address Flag
ENV_SIZE_BIT EQU 100B ; Envelope Size (Large if set)
EGA EQU 1000000B ; EGA Flag
INSTALLED EQU 10000000B ; Installed Flag
;-----------------------------------------------------------------------
; EQUATES for Envelope Margins - Note Bytes reversed
;-----------------------------------------------------------------------
LMARG_ADD_S EQU "06" ; Left Address Margin - Small
LMARG_RET_S EQU "54" ; Left Return Margin - Small
LMARG_ADD_L EQU "05" ; Left Address Margin - Large
LMARG_RET_L EQU "71" ; Left Return Margin - Large
TMARG_RET_S EQU "03" ; Top Return Margin - Small
TMARG_RET_L EQU "92" ; Top Return Margin - Large
TMARG_RET_2 EQU "61" ; Top Return Margin - Series 2
;----------------------------------------------------------------------
CSEG SEGMENT PUBLIC 'CODE'
ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:CSEG

ORG 100H
START: JMP INSTALL ; install TSR program

COPYRIGHT$ DB "ENVELOPE 1.0 Copyright (c) 1988 Ziff Communications Co."
DB CR,LF,"PC Magazine ",254," R. M. Saidikowski",CR,LF,"$",26
;-----------------------------------------------------------------------
; Program Data Area -- Static text first for Residency Check
;-----------------------------------------------------------------------
TEXT_UPPER DB "ark Upp",0,"Left ",0
TEXT_LOWER DB "ove Low",0,"Right",0

OLD09H DD 0 ; Holds old Interrupt 09H vector
BUSY DB 0 ; busy flag for ENVELOPE
; status byte for ENVELOPE ; bit 0 = LJ Series 2 if set
; bit 1 = Print return address ; bit 2 = Large Envelope
; bit 6 = EGA video adapter ; bit 7 = ENVELOPE installed
STATUS DB 00000101B
CURLOC DW 0 ; save cursor location
VID_PAGE DB 0 ; current video page
NUM_COLS DB 80 ; number columns in display
LAST_COL DB 79 ; last column number
LAST_LINE DB 24 ; last line number
ADDR_COLS DB 0 ; columns in address
START_COL DB 0 ; starting column for swap
UPLEFT DW 0 ; upper left position of box
BOTRIGHT DW 0 ; bottom right position of box
HILITE_LOC DW 0 ; highlight location of Small
PRINT_PORT DW 0 ; 0-2=LPT1-LPT3, 3-6=COM1-COM4

MENU_BLOCK DB 0DAH,7,10 DUP(0C4H,7),0BFH,7,0B3H,7,"M",7,"a",7
DB "r",7,"k",7," ",7,"U",7,"p",7,"p",7,"e",7,"r",7
DB 2 DUP(0B3H,7),"L",7,"e",7,"f",7,"t",7," ",7
DB " ",7,"&",7,17,7,0C4H,7,0D9H,7,0B3H,7,0C0H,7
DB 0C4H,7,"E",7,"s",7,"c",7,"=",7,"E",7,"x",7
DB "i",7,"t",7,0C4H,7,0D9H,7

MENU_PRINT DB 0C9H,7,8 DUP(0CDH,7),0BBH,7,0BAH,7,"F",7,"1",7
DB "-",7,"S",7,"m",7,"a",7,"l",7,"l",7,0BAH,7
DB 0BAH,7,"F",7,"2",7,"-",7,"L",7,"a",7,"r",7,"g",7
DB "e",7,2 DUP(0BAH,7),"F",7,"3",7,"-",7,"R",7
DB "t",7,"A",7,"d",7,"d",7,0BAH,7,0C7H,7
DB 8 DUP(0C4H,7),0B6H,7,0BAH,7,"L",7,"P",7,"T",7
DB "1",7," ",7,"L",7,"J",7,"2",7,2 DUP(0BAH,7)
DB 17,7,0C4H,7,0D9H,7,"P",7,"r",7,"i",7,"n",7,"t",7
DB 0BAH,7,0C8H,7,"E",7,"s",7,"c",7,"=",7,"E",7
DB "x",7,"i",7,"t",7,0BCH,7

LINE_SIZE EQU 40
RET_ADD DB LINE_SIZE DUP(32),CR,LF
DB LINE_SIZE DUP(32),CR,LF
DB LINE_SIZE DUP(32)
SKIP_LINE DB CR,LF,0,"$"
LENGTH_RET = (LINE_SIZE + 2) * 3
;-----------------------------------------------------------------------
; Laser Jet Command Sequences
;-----------------------------------------------------------------------
RESET_LJ DB ESCAPE,"E",0 ; reset printer
FONT DB ESCAPE,"&l1O" ; landscape
DB ESCAPE,"(8U" ; Roman-8 symbol set
DB ESCAPE,"(sp10h12vsb3T" ; 10-pitch 12-point
; upright med-weight Courier
DB ESCAPE,"&l2H",0 ; manual feed
TOP_MARGIN DB ESCAPE,"&l" ; skip lines
MODELX DB "16E",0 ; 16 for LJII 30 for LJ and LJ+
LEFT_MARGIN DB ESCAPE,"&a" ; left margin
ENV_SIZE DB "50L",0 ; 50 spaces - default Large
;-----------------------------------------------------------------------
; Arrow Key Jump Offsets
;-----------------------------------------------------------------------
KEYTAB DB 72, 75 , 77 , 80 ; Up Left Right Down
JMPTAB_TOP DW OFFSET TOP_UP,OFFSET TOP_LEFT
DW OFFSET TOP_RIGHT,OFFSET TOP_DOWN
JMPTAB_BOT DW OFFSET BOT_UP,OFFSET BOT_LEFT
DW OFFSET BOT_RIGHT,OFFSET BOT_DOWN
;======================================================================
; Intercept Keyboard Interrupt
;-----------------------------------------------------------------------
INT09H PROC FAR
ASSUME CS:CSEG,DS:NOTHING,ES:NOTHING,SS:NOTHING

STI ; Turn interrupts back on
PUSH AX
IN AL,60H ; get scan code
CMP AL,SCAN_CODE ; compare scan code
JNE INT9_2 ; if not, exit to old INT09H
MOV AH,2 ; shift key status
INT 16H ; BIOS
AND AL,0FH
CMP AL,SHIFT_MASK ; compare shift status
JE INT9_3 ; if yes, check if routine busy
INT9_2: POP AX
CLI ; flags already pushed
JMP DWORD PTR CS:[OLD09H] ; look like an interrupt
;-----------------------------------------------------------------------
; More ckecks before calling main ENVELOPE routine
;-----------------------------------------------------------------------
INT9_3: CMP CS:[BUSY],0 ; ENVR already active?
JNE INT9_2 ; normal exit
IN AL,61H ; reset keyboard
MOV AH,AL
OR AL,80H ; set bit 7
OUT 6AH,AL ; send to control port
MOV AH,AL ; recover old value
JMP SHORT $+2
OUT 6AH,AL ; send to control port
CLI
MOV AL,20H ; reset the
OUT 20H,AL ; interrupt controller
STI
PUSH BX ; AX already pushed
PUSH CX
PUSH DX
PUSH SI ; Save all registers
PUSH DI
PUSH DS
PUSH ES
PUSH BP


PUSH CS ; establish addressing
POP DS
ASSUME DS:CSEG
INC [BUSY] ; set busy flag
;-----------------------------------------------------------------------
; Get screen parameters -- part of test if interrupt OK
;-----------------------------------------------------------------------
MOV AH,0FH ; get video mode
INT 10H ; BIOS video
CMP AL,7 ; MONO
JE MODEOK
CMP AL,3 ; regular 40 and 80 column modes
JA INT09_X ; graphics mode - exit
MODEOK: MOV [NUM_COLS],AH ; number of columns
DEC AH
MOV [LAST_COL],AH ; last column number
MOV [VID_PAGE],BH ; video page
TEST [STATUS],EGA ; check if EGA (or VGA)
JZ SET_LINES ; not EGA
XOR AX,AX
MOV ES,AX
MOV AL,BYTE PTR ES:[484H]
MOV [LAST_LINE],AL
SET_LINES: PUSH CS ; set segment
POP ES ; for ES
ASSUME ES:CSEG
CALL GET_CURSOR ; get current cursor attributes
MOV [CURLOC],DX ; save cursor position
CALL ENVL_MAIN ; call main envelope routine
MOV DX,[CURLOC] ; saved cursor location
CALL SET_CURSOR ; restore cursor
INT09_X: DEC [BUSY] ; Envelope finished
ASSUME DS:NOTHING, ES:NOTHING
POP BP
POP ES
POP DS
POP DI ; restore all registers
POP SI
POP DX
POP CX
POP BX
POP AX
IRET ; return to Interrupted Routine

INT09H ENDP
;======================================================================
; Main ENVELOPE Routine
;-----------------------------------------------------------------------
ENVL_MAIN PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:NOTHING
;-----------------------------------------------------------------------
; Define address on screen
; Display upper left message and cursor in upper left
;-----------------------------------------------------------------------
MOV UPLEFT,0 ; initialize
CALL BLOCK_WINDOW ; place window on screen
XOR DX,DX ; upper left location
CALL SET_CURSOR
CALL GET_CHAR ; get character
PUSH AX ; save character
CALL BLOCK_WINDOW ; remove window from screen
;-----------------------------------------------------------------------
; set text in window for lower right corner message
;-----------------------------------------------------------------------
MOV SI,OFFSET TEXT_LOWER ; text for lower message
MOV DI,OFFSET MENU_BLOCK+28 ; window location
CALL MOV_TEXT
MOV DI,OFFSET MENU_BLOCK+50 ; window location
CALL MOV_TEXT ; move 'right' message
XOR DX,DX ; cursor location
CALL SET_CURSOR
POP AX ; recover character
JMP SHORT TOP_WIN_GONE
TOP_BADKE: CALL BEEP ; illegal key
TOP_NEXT: CALL SET_CURSOR ; set cursor
TOP_NEXT_KE: CALL GET_CHAR ; get character
TOP_WIN_GONE: MOV UPLEFT,DX ; save cursor location
CMP AL,CR ; carriage return
JE MOV_BOT_RT ; finished with top-left moves
CMP AL,ESCAPE ; Quit
JNE TOP_NOT_ESC
JMP ENV_EXIT ; exit ENVELOPE
TOP_NOT_ESC: OR AL,AL ; extended key code
JNZ TOP_NEXT_KE
MOV BX,OFFSET JMPTAB_TOP+6 ; last entry in table
CALL GET_JMP_OFF ; get jump offset
JC TOP_BADKE ; no match
JMP [DI] ; offset returned by Call
TOP_UP: OR DH,DH ; row number in DH
JE TOP_BADKE ; can't move higher
DEC DH
JMP TOP_NEXT ; get next cursor movement
TOP_LEFT: OR DL,DL ; column number in DL
JE TOP_BADKE ; can't move to left
DEC DL
JMP TOP_NEXT ; get next cursor movement
TOP_RIGHT: CMP DL,LAST_COL ; column number in DL
JE TOP_BADKE ; can't move to right
INC DL
JMP TOP_NEXT ; get next cursor movement
TOP_DOWN: CMP DH,LAST_LINE ; row number in DH
JE TOP_BADKE ; can't move lower
INC DH
JMP TOP_NEXT ; get next cursor movement
;-----------------------------------------------------------------------
; now move to bottom right corner to define address
; first -- display window and reverse video upper-left
;-----------------------------------------------------------------------
MOV_BOT_RT: CALL BLOCK_WINDOW ; move window on screen
MOV DX,UPLEFT ; current location
CALL SET_CURSOR
PUSH DX ; save cursor loc
CALL REVERSE ; reverse video
CALL BURY_CURSOR
CALL GET_CHAR
PUSH AX ; save character
CALL BLOCK_WINDOW ; remove window
POP AX ; recover character
POP DX ; recover cursor location
JMP SHORT BOT_WIN_GONE
BOT_BADKE: CALL BEEP ; illegal key
BOT_NEXTKE: CALL GET_CHAR ; get character
BOT_WIN_GONE: MOV BOTRIGHT,DX ; save cursor location
CMP AL,CR ; carriage return
JE PRINT_MENU ; display print window
CMP AL,ESCAPE ; Quit
JNE BOT_NOT_ESC ; not Escape
JMP SCREEN_RST ; restore screen
BOT_NOT_ESC: OR AL,AL ; extended key code
JNZ BOT_NEXTKE
MOV BX,OFFSET JMPTAB_BOT+6 ; last entry in table
CALL GET_JMP_OFF ; get jump offset
JC BOT_BADKE ; no match
MOV BX,UPLEFT ; upper left location
JMP [DI] ; offset returned by Call
BOT_UP: CMP DH,BH ; compare to top
JE BOT_BADKE ; can't move up
DEC DH ; new bottom right location
PUSH DX ; save
INC DH ; reset
REVERSE_ROW: CALL SET_CURSOR
CALL REVERSE ; reverse video at cursor
DEC DL ; column number
CMP DL,BL ; beyond beginning?
JL BR_FINISH ; finish reversing
JMP REVERSE_ROW ; next column
BOT_LEFT: CMP DL,BL ; compare to left
JE BOT_BADKE ; can't move left
DEC DL ; new bottom right location
PUSH DX ; save
INC DL ; reset
REVERSE_COL: CALL SET_CURSOR
CALL REVERSE ; reverse video at cursor
DEC DH ; row number
CMP DH,BH ; at end?
JGE REVERSE_COL ; next row
BR_FINISH: CALL BURY_CURSOR
POP DX ; recover current location
JMP BOT_NEXTKE ; get next move
BOT_RIGHT: CMP DL,LAST_COL ; can't move beyond last column
JE BOT_BADKE
INC DL ; new bottom right location
PUSH DX ; save
JMP REVERSE_COL ; add column
BOT_DOWN: MOV AX,DX
SUB AH,BH ; number rows
CMP AH,5 ; max rows (is 6)
JE BOT_BADKE
CMP DH,LAST_LINE ; can't move beyond last row
JE BOT_BADKE
INC DH ; new bottom right location
PUSH DX ; save
JMP REVERSE_ROW ; add row
;-----------------------------------------------------------------------
; Display Print Menu
;-----------------------------------------------------------------------
PRINT_MENU: CALL PRINT_WINDOW ; display print window
SET_SIZE: CALL SET_HILITE ; set size and return hilites
PR_NEXT: CALL GET_CHAR
CMP AL,CR ; print if carriage return
JE PRINT_ENV ; print envelope
CMP AL,ESCAPE ; exit without printing
JNE PR_NOTESC
CALL PRINT_WINDOW ; remove print window
JMP SCREEN_RST ; restore screen and exit
PR_NOTESC: OR AL,AL ; test for extended code
JZ PR_CODE
PR_BADKE: CALL BEEP
JMP PR_NEXT ; get another character
PR_CODE: SUB AH,59 ; convert function keys to digit codes
JL PR_BADKE
JG PR_CODE_2
AND STATUS,NOT ENV_SIZE_BIT ; Small Envelope
JMP SET_SIZE
PR_CODE_2: SUB AH,2
JG PR_BADKE ; F4 or greater
JE PR_CODE_3 ; F3
OR STATUS,ENV_SIZE_BIT ; Large Envelope
JMP SET_SIZE
PR_CODE_3: XOR STATUS,PR_RET_ADD ; toggle return address bit
JMP SET_SIZE
;-----------------------------------------------------------------------
; print envelope here
;-----------------------------------------------------------------------
PRINT_ENV: CALL PRINT_WINDOW ; remove print display window
MOV SI,OFFSET RESET_LJ ; reset printer
CALL OUT_STRING
JNC PRINT_OK
;-----------------------------------------------------------------------
; printer error -- beep twice and try again
;-----------------------------------------------------------------------
CALL BEEP
XOR CX,CX
DUMB_LOOP: LOOP DUMB_LOOP ; slight delay
CALL BEEP
JMP PRINT_MENU ; try again
PRINT_OK: MOV SI,OFFSET FONT ; set fonts, etc.
CALL OUT_STRING
;-----------------------------------------------------------------------
; set margins for location of return address
;-----------------------------------------------------------------------
TEST STATUS,LJ_MODEL ; this affects top margin
MOV AX,TMARG_RET_2 ; LJ Series 2
JNZ SET_TOP_RET
TEST STATUS,ENV_SIZE_BIT ; affects top on LJ & LJ+
MOV AX,TMARG_RET_S ; Small on original
JZ SET_TOP_RET
MOV AX,TMARG_RET_L ; Large on original
SET_TOP_RET: MOV WORD PTR MODELX,AX ; set top margin
TEST STATUS,ENV_SIZE_BIT ; this affects left margin
MOV AX,LMARG_RET_S ; small envelope
JZ SET_LEFT_RET
MOV AX,LMARG_RET_L ; large envelope
SET_LEFT_RET: MOV WORD PTR ENV_SIZE,AX ; set left margin
CALL SET_MARGINS ; send commands to printer
;-----------------------------------------------------------------------
; print the return address if requested
;-----------------------------------------------------------------------
MOV CX,9 ; lines to skip if no return
TEST STATUS,PR_RET_ADD ; print return address?
JZ TO_ADDRESS ; no return address
MOV SI,OFFSET RET_ADD ; print return address
CALL OUT_STRING
MOV CX,6 ; lines to skip if return addr
;-----------------------------------------------------------------------
; print 'to' address -- first set margins
;-----------------------------------------------------------------------
TO_ADDRESS: CALL LINE_FEED ; skip a line
LOOP TO_ADDRESS ; count in CX
TEST STATUS,ENV_SIZE_BIT ; this affects left margin
MOV AX,LMARG_ADD_S ; small envelope
JZ SET_LEFT_ADD
MOV AX,LMARG_ADD_L ; large envelope
SET_LEFT_ADD: MOV WORD PTR ENV_SIZE,AX ; set left margin
CALL SET_LEFT_ONLY ; send commands to printer
;-----------------------------------------------------------------------
; read address from reverse-video part of screen - send to printer
;-----------------------------------------------------------------------
XOR CX,CX
MOV AX,BOTRIGHT
MOV DX,UPLEFT ; starting loc on screen
SUB AX,DX ; rows-1 and cols-1 in AX
MOV CL,AH ; rows - 1
INC AL ; columns
MOV ADDR_COLS,AL ; save columns in address
JCXZ LAST_ROW ; only one line
NEXT_ROW: PUSH CX ; save loop counter
PUSH DX ; save cursor location
CALL OUT_LINE ; print line from screen
CALL LINE_FEED ; send carriage return
POP DX ; retrieve cursor
INC DH ; next row
POP CX ; retrieve counter
LOOP NEXT_ROW
LAST_ROW: CALL OUT_LINE ; print last line
MOV SI,OFFSET RESET_LJ ; reset printer
CALL OUT_STRING ; also flushes printer buffer
;-----------------------------------------------------------------------
; restore screen to normal video
;-----------------------------------------------------------------------
SCREEN_RST: MOV DX,UPLEFT
MOV AX,BOTRIGHT
SUB AX,DX
ADD AX,0101H ; rows and columns in AX
XOR CX,CX
MOV CL,AH ; number rows
XOR AH,AH
MOV SI,AX ; save columns
ROW_RESTORE: PUSH CX
MOV CX,SI ; number columns
PUSH DX ; save cursor location
COL_RESTORE: CALL SET_CURSOR
CALL REVERSE ; reset video
INC DL ; next column
LOOP COL_RESTORE
POP DX
INC DH ; next row
POP CX
LOOP ROW_RESTORE
;-----------------------------------------------------------------------
; reset text in window for upper left corner message
;-----------------------------------------------------------------------
ENV_EXIT: MOV SI,OFFSET TEXT_UPPER ; top message
MOV DI,OFFSET MENU_BLOCK+28 ; window location
CALL MOV_TEXT
MOV DI,OFFSET MENU_BLOCK+50 ; window location
CALL MOV_TEXT ; mov 'left ' message
RET

ENVL_MAIN ENDP
;======================================================================
; output a string to the printer
; string address in SI, terminated by ASCII0
;-----------------------------------------------------------------------
OUT_STRING PROC NEAR

LODSB ; next character
OR AL,AL ; check for 0
JZ OUT_STR_X ; CY is clear
CALL OUT_CHAR ; output character
JC OUT_STR_X ; error return
JMP OUT_STRING ; get next character
OUT_STR_X: RET ; ret on ASCII0

OUT_STRING ENDP
;======================================================================
; print line of text from screen at cursor location DX
; number to print in ADDR_COLS
;-----------------------------------------------------------------------
OUT_LINE PROC NEAR

PUSH CX ; save counter
XOR CX,CX
MOV CL,ADDR_COLS ; column counter
LOOP_LINE: CALL SET_CURSOR ; cursor to location DX
MOV AH,8 ; read character
CALL VIDEO_INT ; into AL
PUSH DX ; save location
CALL OUT_CHAR ; print character
POP DX ; recover location
INC DL ; next column
LOOP LOOP_LINE
CALL BURY_CURSOR
POP CX ; recover counter
RET
OUT_LINE ENDP
;======================================================================
; skip line by sending CR LF combination alters AX DX SI
;-----------------------------------------------------------------------
LINE_FEED PROC NEAR

MOV SI,OFFSET SKIP_LINE
CALL OUT_STRING ; send characters CR LF 0
RET

LINE_FEED ENDP
;======================================================================
; output character in AL to printer alters AX DX
;-----------------------------------------------------------------------
OUT_CHAR PROC NEAR

MOV DX,PRINT_PORT ; printer port number
CMP DX,2 ; 0 to 2 are LPT1-LPT3
JG SERIAL
XOR AH,AH ; print character
INT 17H ; BIOS printer
TEST AH,101001B ; check for printer error
TEST_ERROR: JZ NO_ERROR ; normal return
RET_ERROR: STC ; error return
RET
SERIAL: SUB DX,3 ; COM port number
MOV AH,1 ; send char - DX already set
INT 14H ; BIOS
TEST AH,10000000B ; check for printer error
JMP TEST_ERROR ; test for error

OUT_CHAR ENDP
;======================================================================
; Set top and left margins for return or to address
;-----------------------------------------------------------------------
SET_MARGINS PROC NEAR

MOV SI,OFFSET TOP_MARGIN ; top margin
CALL OUT_STRING
SET_LEFT_ONLY LABEL NEAR
MOV SI,OFFSET LEFT_MARGIN ; left margin
CALL OUT_STRING
RET

SET_MARGINS ENDP
;======================================================================
; Calculate offset for NEAR JMP to LABEL
; extended code in AH (from GETCHAR) BX points to end of JumpTable
; Returns JUMP offset in DI No match if CY flag set
;-----------------------------------------------------------------------
GET_JMP_OFF PROC NEAR

MOV DI,OFFSET KEYTAB ; table of key codes
MOV CX,4 ; number of codes
XCHG AH,AL ; code into AL
REPNZ SCASB ; look for match
JNZ RET_ERROR ; no match
MOV DI,BX ; last entry in table
SHL CX,1 ; 2 bytes/word
SUB DI,CX ; offset
NO_ERROR: CLC ; normal return
RET

GET_JMP_OFF ENDP
;======================================================================
; set highlites in printer menu
;-----------------------------------------------------------------------
SET_HILITE PROC NEAR

MOV CX,3 ; 3 lines to reset
MOV DX,HILITE_LOC ; beginning location of Small
PUSH DX ; save
PUSH DX ; save again
SET_H_LOOP: PUSH DX ; save
MOV BL,07H ; normal video
CALL SET_ATTRIB ; set attribute byte
POP DX ; Small message
INC DH ; next row
LOOP SET_H_LOOP
POP DX ; retrieve HILITE_LOC
TEST STATUS,ENV_SIZE_BIT ; test for small
JZ SET_HIL_2 ; not set = small
INC DH ; Large to be hilited
SET_HIL_2: MOV BL,70H ; reverse video
CALL SET_ATTRIB
POP DX ; retrieve HILITE_LOC
ADD DH,2 ; location of 'RtAdd'
TEST STATUS,PR_RET_ADD ; test for print of rt-add
JZ SET_HIL_X ; no print if 0
CALL SET_ATTRIB ; color should still be in BL
SET_HIL_X: CALL BURY_CURSOR ; bury cursor
RET

SET_HILITE ENDP
;======================================================================
; set attributes -- start at DX (cursor)
; change 5 attributes to BL
;-----------------------------------------------------------------------
SET_ATTRIB PROC NEAR

PUSH CX
MOV CX,5 ; attributes to set
SET_ATT_LOOP: CALL SET_CURSOR
MOV AH,8 ; read character
CALL VIDEO_INT
PUSH CX
MOV AH,9 ; write character
MOV CX,1 ; one character
CALL VIDEO_INT ; attribute in BL
INC DL ; next position
POP CX
LOOP SET_ATT_LOOP ; next character
POP CX
RET

SET_ATTRIB ENDP
;======================================================================
BEEP PROC NEAR
PUSH AX
MOV AX,0E07H ; Beep (ASCII 7)
INT 10H ; BIOS
POP AX
RET
BEEP ENDP
;======================================================================
; call BIOS video INT function in AH
;-----------------------------------------------------------------------
VIDEO_INT PROC NEAR

PUSH BX
MOV BH,VID_PAGE ; page number
INT 10H ; BIOS video
POP BX
RET

VIDEO_INT ENDP
;======================================================================
; bury cursor alters DX
;-----------------------------------------------------------------------
BURY_CURSOR PROC NEAR

MOV DH,[LAST_LINE]
INC DH
XOR DL,DL
;-----------------------------------------------------------------------
; set cursor to position in DH DL
;-----------------------------------------------------------------------
SET_CURSOR LABEL NEAR

PUSH AX
MOV AH,2 ; set cursor
VIDEO_CURSOR: CALL VIDEO_INT ; BIOS video
POP AX
RET
;-----------------------------------------------------------------------
; get cursor in DX ( DH DL )
;-----------------------------------------------------------------------
GET_CURSOR LABEL NEAR

PUSH AX
MOV AH,3 ; get cursor
JMP VIDEO_CURSOR ; BIOS video

BURY_CURSOR ENDP
;======================================================================
; get character from keyboard
;-----------------------------------------------------------------------
GET_CHAR PROC NEAR

XOR AH,AH
INT 16H
RET

GET_CHAR ENDP
;======================================================================
; reverse video attribute at cursor location alters AX
;-----------------------------------------------------------------------
REVERSE PROC NEAR

PUSH BX
PUSH CX
MOV AH,8 ; read character
CALL VIDEO_INT ; attribute to AH
MOV CL,4
ROR AH,CL ; reverse video attribute
MOV BL,AH ; need attribute in BL
MOV AH,9 ; write character
MOV CX,1 ; write 1 character
CALL VIDEO_INT ; attribute in BL
POP CX
POP BX
RET
REVERSE ENDP
;======================================================================
; Display Window with Instructions for Blocking out address
; SI offset in memory DX screen location
; CX chars/row AX number rows
;-----------------------------------------------------------------------
BLOCK_WINDOW PROC NEAR

MOV DH,2 ; row start
MOV DL,[NUM_COLS] ; number columns in mode
SUB DL,12 ; number columns
CMP BYTE PTR [UPLEFT],27 ; check start column
JLE BLOCK_WIND_2
XOR DL,DL ; start in first column
BLOCK_WIND_2:
MOV SI,OFFSET MENU_BLOCK ; memory LOCATION
MOV AX,4 ; rows
MOV CX,12 ; columns
;-----------------------------------------------------------------------
; swap window with screen - use BIOS
; call with screen loc in DX memory image at SI
; rows in AX columns in CX
; alters AX BX CX DX SI
;-----------------------------------------------------------------------
WINDOW LABEL NEAR

MOV [START_COL],DL ; starting column for swap
LOOP_ROW: PUSH AX ; rows
PUSH CX ; columns
LOOP_COL: CALL SET_CURSOR
MOV AH,8 ; read character and attrib
CALL VIDEO_INT
XCHG AX,DS:[SI] ; swap with memory
INC SI
INC SI ; next image location
MOV BL,AH ; attribute
MOV AH,9 ; write character and attrib
PUSH CX
MOV CX,1 ; write only one character
CALL VIDEO_INT
POP CX
INC DL ; move cursor
LOOP LOOP_COL ; next character and attribute
POP CX ; number columns
INC DH ; next row
MOV DL,[START_COL] ; starting column
POP AX ; rows to move
DEC AX ; row moved
JNZ LOOP_ROW ; next row
CALL BURY_CURSOR
RET

BLOCK_WINDOW ENDP
;======================================================================
; Display Print Command Window
; SI offset in memory DX screen location
; CX chars/row AX number rows
;-----------------------------------------------------------------------
PRINT_WINDOW PROC NEAR

MOV AH,2 ; row start
MOV AL,[NUM_COLS] ; number columns in mode
SUB AL,10 ; start column
MOV DX,AX ; save
ADD AX,0104H ; location for highlight
MOV [HILITE_LOC],AX ; save
MOV SI,OFFSET MENU_PRINT ; memory location
MOV AX,8 ; rows
MOV CX,10 ; columns
JMP WINDOW ; swap

PRINT_WINDOW ENDP
;======================================================================
; move text in memory from DS:[SI] to ES:[DI]
; the destination is assumed to be a screen image w/attribute bytes
; ASCII 0 terminates move alters AL
; sets SI and DI to next space in sequences
;-----------------------------------------------------------------------
MOV_TEXT PROC NEAR

LODSB ; next character
OR AL,AL
JNZ MOV_TEXT_X ; ASCII 0
RET
MOV_TEXT_X: STOSB ; send to display
INC DI ; 2 bytes/video
JMP MOV_TEXT ; get next character

MOV_TEXT ENDP
;======================================================================
; this part of code will not remain resident
;-----------------------------------------------------------------------
INSTRUCT$ DB "/U - Uninstall",CR,LF
DB "/Pxx - Printer Port, xx=L1-L3,C1-C4. Default"
DB "=L1 (LPT1)",CR,LF
DB "/Ln - LaserJet Model"
DB " n=1 original & +, n=2 Series 2. Default=2",CR,LF
DB "/R - Change Return Address",LF,"$"

WHITE DB " ,;",9 ; space, comma, semicolon, tab
FILENAME DB "ENVELOPE.COM",0
RESIDENT_SEG DW 0 ; segment of installed ENVELOPE

INSTALLED$ DB CR,LF,"Port "
PORT$ DB "LPT1, Model LJ"
MODEL$ DB "2, Hotkey is Alt-E$"
ALREADY_IN$ DB "Resident$"
NOT_INST$ DB "NOT Resident$"
UNINSTALL$ DB "Uninstalled$"
NO_UN_INST$ DB "Can't Uninstall$"

SAVE_TO_FILE$ DB "Save new ENVELOPE.COM?$"
FILE_ERROR$ DB "FILE ERROR - nothing saved$"
FILE_UPDATE$ DB "ENVELOPE.COM updated$"
RET_UPDATE$ DB "Resident Return Address Updated$"

PORT_NAMES DB "LPT1",0,"LPT2",0,"LPT3",0
DB "COM1",0,"COM2",0,"COM3",0,"COM4",0
;----------------------------------------------------------------------
INSTALL PROC NEAR
ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:CSEG

CLD ; standard UP direction
MOV DX,OFFSET COPYRIGHT$
MOV AH,9 ; copyright message
INT 21H ; DOS
MOV DX,OFFSET INSTRUCT$
CALL MESSAGE

MOV NUM_COLS,80
MOV LAST_COL,79
MOV LAST_LINE,24
MOV PRINT_PORT,0
MOV STATUS,5
;-----------------------------------------------------------------------
; first check if ENVELOPE already installed and set flag in STATUS
;-----------------------------------------------------------------------
ASSUME ES:NOTHING
NOT WORD PTR [START] ; Modify
MOV BX,600H ; segment to compare
MOV AX,CS ; this segment
FIND_RES: INC BX ; next paragraph
MOV ES,BX ; ES is search segment
CMP AX,BX ; current segment?
JE CHECK_TAIL ; not in memory flag is 0
MOV SI,OFFSET START ; compare static part of data
MOV DI,SI ; same offset in both strings
MOV CX,16 ; bytes to compare
REP CMPSB ; compare DS:SI to ES:DI
OR CX,CX ; all matched
JNZ FIND_RES ; check next paragraph
OR STATUS,INSTALLED ; set bit for installed
MOV RESIDENT_SEG,BX ; save installed segment
MOV DX,OFFSET ALREADY_IN$
CALL MESSAGE
;-----------------------------------------------------------------------
; Now check command tail for switches
; DS points to CODE and to PSP
;-----------------------------------------------------------------------
CHECK_TAIL:
PUSH DS ; reset extra segment
POP ES
ASSUME ES:CSEG
MOV SI,80H ; location of command tail
LODSB ; length in AL
OR AL,AL ; Check if parameters
JNZ CHECK_0 ; commands to check
ATTEMPT_INST: TEST STATUS,INSTALLED
JNZ RLH_1
JMP OK_INSTALL ; proceed with default install
RLH_1: MOV AX,4C01H ;Terminate with return code
INT 21H
;-----------------------------------------------------------------------
; Decode command line using brute force
;-----------------------------------------------------------------------
CHECK_0: MOV BL,AL ; character count
CHECK_1: OR BL,BL ; more commands?
JZ ATTEMPT_INST ; processing complete
LODSB ; next character
DEC BL ; keep count current
MOV DI,OFFSET WHITE ; delimiters and space
MOV CX,4 ; number to check
REPNE SCASB
JE CHECK_1 ; look for next character
CHECK_2: CMP AL,"/" ; normal delimiter
JNE ATTEMPT_INST ; anything else - default
;-----------------------------------------------------------------------
; check character after slash
;-----------------------------------------------------------------------
OR BL,BL ; if / terminates
JZ ATTEMPT_INST ; then try default install
LODSB ; character after slash
AND AL,01011111B ; capitalize
CMP AL,"U" ; uninstall
JNE CHECK_3
JMP UNINSTALL ; attempt to uninstall
CHECK_3: CMP AL,"P" ; check for port input
JNE CHECK_4
CALL GET_PORT
JC ATTEMPT_INST ; if input errors
JMP CHECK_1 ; continue with input
CHECK_4: CMP AL,"L" ; check LJ Model input
JNE CHECK_5
CALL GET_MODEL
JC ATTEMPT_INST ; if input errors
JMP CHECK_1 ; continue with input
CHECK_5: CMP AL,"R" ; input new return address
JNE ATTEMPT_INST ; no match
CALL GET_RET_ADD
;-----------------------------------------------------------------------
; Prompt for saving new Return Address to ENVELOPE.COM
;-----------------------------------------------------------------------
CHECK_SAVE: MOV DX,OFFSET SAVE_TO_FILE$
MOV AH,9
INT 21H
;;; CALL MESSAGE
;;; CALL GET_CHAR ; get response

MOV AH,1 ;DOS get char fn
INT 21H ; allows for redirection

AND AL,01011111B ; capitalize
CMP AL,"Y"
JNE NO_RET_SAVE
;-----------------------------------------------------------------------
; save to file here - attempt to open file
;-----------------------------------------------------------------------
NOT WORD PTR [START]
MOV AH,3CH ;Create file
MOV DX,OFFSET FILENAME ; name of ENVELOPE.COM
XOR CX,CX ;Normal attribute
INT 21H ; DOS
JNC SAV_FILE_OK
SAV_FILE_ERR: MOV DX,OFFSET FILE_ERROR$
JMP SHORT SAV_FILE_MSG ; display string, proceed
SAV_FILE_OK: MOV BX,AX ; file handle
MOV AH,40H ; write to file
MOV CX,(OFFSET COM_SIZE - OFFSET CSEG - 100H) ;bytes
MOV DX,100H
INT 21H ; DOS
JC SAV_FILE_ERR
MOV AH,3EH ; close file handle
INT 21H ; DOS
MOV DX,OFFSET FILE_UPDATE$
SAV_FILE_MSG: CALL MESSAGE
NOT WORD PTR [START]
NO_RET_SAVE:
TEST STATUS,INSTALLED ; is ENVELOPE installed?
JZ OK_INSTALL ; install
MOV SI,OFFSET RET_ADD
MOV DI,SI ; same offset
MOV AX,RESIDENT_SEG ; segment of installed ENVELOPE
MOV ES,AX ; destination
MOV CX,LENGTH_RET ; bytes to move
REP MOVSB
MOV DX,OFFSET RET_UPDATE$
CALL MESSAGE
MOV AX,4C05H
INT 21H
;-----------------------------------------------------------------------
; final preparations for installation of ENVELOPE
; set printer model and port name in PRINT_MENU
;-----------------------------------------------------------------------
OK_INSTALL: PUSH CS
POP ES ; reset segment
ASSUME ES:CSEG
MOV DI,OFFSET MENU_PRINT+116 ; location for model
MOV AL," " ; original LJ or LJ+
TEST STATUS,LJ_MODEL ; check for LJ Series 2
JZ SET_MODEL
MOV AL,"2" ; LJ Series II
SET_MODEL: STOSB
MOV MODEL$,AL ; Model in message
MOV DI,OFFSET MENU_PRINT+102 ; location for port
MOV SI,OFFSET PORT_NAMES ; location for names
MOV AX,PRINT_PORT ; 0 through 6 LPT1 - COM4
ADD SI,AX
SHL AX,1
SHL AX,1
ADD SI,AX ; offset in memory in SI
PUSH SI ; save location
CALL MOV_TEXT ; move text to video image
POP SI ; location of port name
MOV DI,OFFSET PORT$ ; location in message
MOVSW
MOVSW ; move 4 bytes
;-----------------------------------------------------------------------
; check for EGA (or VGA) and set byte if present
;-----------------------------------------------------------------------
MOV AH,12H ; special EGA function
MOV BL,10H ; return EGA information
INT 10H ; BIOS video
CMP BL,10H ; if BL unchanged then
JE ENV_REL ; no EGA or VGA
OR STATUS,EGA ; set EGA bit
;-----------------------------------------------------------------------
; Release environment block
;-----------------------------------------------------------------------
ENV_REL: MOV BX,WORD PTR DS:[2CH] ; environment block
MOV ES,BX ; segment
ASSUME ES:NOTHING
MOV AH,49H ; free memory block
INT 21H ; DOS
;-----------------------------------------------------------------------
; go resident
;-----------------------------------------------------------------------
MOV AX,3509H ; get interrupt vector 09h
INT 21H ; DOS

MOV WORD PTR OLD09H,BX ; offset old interrupt 09h
MOV WORD PTR OLD09H[2],ES ; segment for above

MOV DX,OFFSET INT09H ; new INT handler
MOV AX,2509H ; set interrupt vector 09h
INT 21H ; DOS

MOV DX,OFFSET INSTALLED$ ; installation message
CALL MESSAGE

MOV DX,OFFSET INSTALL ; save code up to INSTALL
INT 27H ; The infamous TSR interrupt
;-----------------------------------------------------------------------
; attempt to uninstall ENVELOPE
;-----------------------------------------------------------------------
UNINSTALL: TEST STATUS,INSTALLED ; first see if it is resident
JNZ UNINST_2 ; it is in memory
MOV DX,OFFSET NOT_INST$
CALL MESSAGE
MOV AX,4C02H
INT 21H
UNINST_2: MOV AX,3509H ; get segment of keyboard INT
INT 21H ; DOS
ASSUME ES:NOTHING
MOV AX,ES ; current segment
CMP AX,RESIDENT_SEG ; segment of ENVELOPE
JE UNINST_3 ; OK to uninstall
NO_UNINST: MOV DX,OFFSET NO_UN_INST$
MOV AX,4C03H
INT 21H
;-----------------------------------------------------------------------
; OK to uninstall
;-----------------------------------------------------------------------
UNINST_3: MOV ES,RESIDENT_SEG ; segment of installed ENVELOPE
NOT WORD PTR ES:[START] ; don't confuse next install
LDS DX,ES:[OLD09H] ; old INT09H interrupt vector
ASSUME DS:NOTHING
MOV AX,2509H ; reset vector 09H
INT 21H ; DOS
PUSH CS ; reset DS to code
POP DS
ASSUME DS:CSEG
MOV AH,49H ; free memory block in ES
INT 21H ; DOS
JC NO_UNINST ; error return
MOV DX,OFFSET UNINSTALL$
CALL MESSAGE
MOV AX,4C04H
INT 21H

INSTALL ENDP
;-----------------------------------------------------------------------
; Read port number from command tail SI points to next character
; BL characters left CY set on return if error
;-----------------------------------------------------------------------
GET_PORT PROC NEAR

CALL GET_NEXT_CH ; next character from tail
JZ G_ERROR_RET ; no characters left
AND AL,01011111B ; capitalize
CMP AL,"L" ; parallel port
JNE GET_PORT_1
CALL GET_NEXT_CH ; next character from tail
SUB AL,"1" ; should be number
CMP AL,2 ; LPT3
JBE SET_PORT_NUM
G_ERROR_RET: STC ; carry set on error
RET
SET_PORT_NUM: XOR AH,AH ; port number in AX
MOV PRINT_PORT,AX ; save port number
CLC ; carry clear on normal return
RET
GET_PORT_1: CMP AL,"C" ; serial port
JNE G_ERROR_RET
CALL GET_NEXT_CH ; next character from tail
SUB AL,"1" ; should be number
CMP AL,3 ; COM4
JA G_ERROR_RET
ADD AL,3 ;Skew port number in AX
JMP SET_PORT_NUM

GET_PORT ENDP
;-----------------------------------------------------------------------
; Read LJ Model type from command tail SI points to next character
; BL characters left CY set on return if error
;-----------------------------------------------------------------------
GET_MODEL PROC NEAR

CALL GET_NEXT_CH ; next character from tail
JZ G_ERROR_RET ; no characters left
SUB AL,"1" ; should be number
JB G_ERROR_RET ; illegal input
JA GET_MODEL_2
AND STATUS,NOT LJ_MODEL ; clear model bit
CLC
RET
GET_MODEL_2: CMP AL,1
JNE G_ERROR_RET ; illegal input
OR STATUS,LJ_MODEL ; set model bit
CLC
RET

GET_MODEL ENDP
;-----------------------------------------------------------------------
; enter new return address from keyboard
; if CY set on return - then return address is not altered
; first display current return address
;-----------------------------------------------------------------------
CURRENT_RET$ DB "Current Address:$"
ACCEPT_RET$ DB "Enter new address (Enter = no change).",CR,LF
DB "3 lines, 40 characters max.$"

GET_RET_ADD PROC NEAR

MOV DX,OFFSET CURRENT_RET$
CALL MESSAGE
MOV DX,OFFSET RET_ADD
MOV AH,9 ; display string
INT 21H ; DOS
MOV DX,OFFSET ACCEPT_RET$ ; accept prompt
CALL MESSAGE
MOV DX,OFFSET ADR_BUF ;Single line buffer
MOV BYTE PTR [ADR_BUF],LINE_SIZE+1
MOV AH,0AH ; buffered input
INT 21H ; DOS
CMP BYTE PTR ADR_BUF[1],0 ; any input?
JE GET_RET_ADD_2 ; first line read
MOV DI,OFFSET RET_ADD
CALL MOVE_LINE ; blank line, move new, saves DI
CALL GET_LINE ; get next line
CALL GET_LINE ; get final line
GET_RET_ADD_2: RET

GET_RET_ADD ENDP
;======================================================================
; get line from STDIN and then move to return address
;----------------------------------------------------------------------
GET_LINE PROC NEAR


MOV DX,OFFSET ADR_BUF
MOV AH,0AH ; buffered input
INT 21H ; DOS
ADD DI,LINE_SIZE+2 ; next line
;----------------------------------------------------------------------
; blank line in return address
;----------------------------------------------------------------------
MOVE_LINE LABEL NEAR

PUSH DI ; save pointer
PUSH DI ; save pointer
MOV CX,LINE_SIZE ; # characters
MOV AL," " ; set to blanks
REP STOSB
POP DI ; recover address
MOV SI,OFFSET ADR_BUF+2 ; last input
MOV CL,BYTE PTR ADR_BUF[1] ; number characters CH=0
JCXZ MOVE_LINE_X ; no characters
REP MOVSB
MOVE_LINE_X: MOV DX,OFFSET CR_LF$ ; move to next line
MOV AH,9 ; display string
INT 21H ; DOS
POP DI
RET

GET_LINE ENDP
;======================================================================
; get next character from command tail (pointed to by SI)
; BL set to number left ZF set if not chacters left
;----------------------------------------------------------------------
GET_NEXT_CH PROC NEAR

LODSB ; next character
DEC BL ; update count
OR BL,BL ; check if last character
RET

GET_NEXT_CH ENDP
;======================================================================
; Write a line using the DOS print string function.
;----------------------------------------------------------------------
CR_LF$ DB CR,LF,"$" ; carriage return -- line feed

MESSAGE PROC NEAR

MOV AH,9
INT 21H
MOV DX,OFFSET CR_LF$
MOV AH,9
INT 21H
RET

MESSAGE ENDP
;======================================================================
COM_SIZE = $
PC = $
ADR_BUF = PC ;ADR_BUF DB 41, 0, 41 DUP(0)

CSEG ENDS
END START


  3 Responses to “Category : Files from Magazines
Archive   : VOL7N22.ZIP
Filename : ENVELOPE.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/