Category : Assembly Language Source Code
Archive   : A86CNVRT.ZIP
Filename : S4.8

 
Output of file : S4.8 contained in archive : A86CNVRT.ZIP

; EPSONSET is a memory-resident program that sends control codes to an
; Epson-compatible printer. After EPSONSET is installed, it may be invoked
; at any time by pressing the CTRL and the right-SHIFT keys simultaneuosly.
; This causes a window to pop up, inviting the user to press a choice
; of function keys to send codes to the printer. The window is deactivated
; when the ESC key is pressed.

EPSONSET:
JMP >L1 ; skip over the copyright message

DB '(C) Copyright 1986, Ziff-Davis Publishing Company ',01A


; The first part of our program exists only at installation time. The buffer
; OLD_IMAGE is declared at location 0 in our code segment; so our PSP and
; all of our installation code will be destroyed the first time our window
; is popped up.

OLD_IMAGE EQU 0

W_LINES EQU 19 ; height of the pop-up window
W_COLS EQU 28 ; width of the pop-up window
W_SIZE EQU W_LINES * W_COLS ; number of chars in the window
IMAGE_SIZE EQU W_SIZE * 2 ; number of bytes in a video window image

W_ROW EQU 2 ; row number (0-24) of the top of the window
W_COLUMN EQU 26 ; column number (0-79) of start of the window
VIDEO_OFFSET EQU W_ROW*160 + W_COLUMN*2 ; offset of the pop-up window
VIDEO_GAP EQU 160-W_COLS*2 ; gap between successive window lines

L1:
CALL MAKE_WINDOW_IMAGE ; construct the video image of our pop-up window
CALL ADJUST_PCJR ; if we are on a PCJr then adjust the cursor
CALL NEW_KB_VECTOR ; replace the keyboard-interrupt handler
MOV DX,END_OF_PROGRAM ; point DX to the end of our resident section
INT 027 ; terminate but stay resident


; MAKE_WINDOW_IMAGE determines the appropriate attribute bytes for our pop-up
; window, combines those bytes with the text from WINDOW_SOURCE, and
; places the conglomeration into WINDOW_IMAGE.

KEYBOARD_INT DD 0:9*4

MAKE_WINDOW_IMAGE:
CLD ; string scanning is forward
MOV AH,15 ; BIOS function number for GET_VIDEO_MODE
INT 010 ; set AL to the video mode
CMP AL,7 ; is it a monochrome mode?
MOV AX,0704F ; load color attributes in case it isn't
IF E MOV AX,0770 ; if monochrome then load the monochrome attributes
MOV SI,WINDOW_SOURCE ; point SI to table of text
MOV DI,WINDOW_IMAGE ; point DI to the storage area
CALL COPY_4_LINES ; create the first four lines
MOV BX,11 ; load the count of the second-attribute lines
L1: ; loop here for each of the middle 11 lines of window
MOVSB ; copy the left-border byte to the buffer
STOSB ; output the first attribute byte, to complete the word
MOV CX,W_COLS-2 ; load the number of bytes in the middle of the line
XCHG AL,AH ; swap the second attribute byte into AL
CALL COPY_ATTRIBUTE ; output the middle of the line
XCHG AL,AH ; swap the first attribute byte back to AL
MOVSB ; copy the right-border byte to the buffer
STOSB ; output the first attribute byte, to complete the word
DEC BX ; count down the middle lines
JNZ L1 ; loop if more; else drop to make the last 4 lines
COPY_4_LINES: ; copy 4 lines, attribute AL, to video image
MOV CX,4 * W_COLS ; load the number of characters in 4 lines
COPY_ATTRIBUTE: ; copy CX bytes from SI to words at DI with attr. AL
MOVSB ; copy the text byte
STOSB ; output the attribute byte to complete the video word
LOOP COPY_ATTRIBUTE ; loop for the next text byte
RET


; ADJUST_PCJR checks to see if we are a PCJr, and if we are, resets the
; cursor and corrects the CURSOR_MODE word at 0040:0060.

MACHINE_ID DD 0F000:0FFFE ; pointer to ROM identification byte
CURSOR_MODE DD 040:060 ; pointer to cursor mode word

ADJUST_PCJR:

LES DI,MACHINE_ID ; point to the identification byte for this machine
MOV AL,0FD ; load the ID to the PCJr
SCASB ; is this a PCJr?
JNE RET ; no, then skip this routine
LES DI,CURSOR_MODE ; point to the BIOS's cursor mode indicator
MOV AX,0607 ; load our new value for the indicator
STOSW ; store the new value in the BIOS area
XCHG CX,AX ; swap the value into CX, for a BIOS call
MOV AH,1 ; BIOS function number for SET_CURSOR_SIZE
INT 010 ; set cursor to scan-lines 6 and 7 in character block
RET


; NEW_KB_VECTOR saves the old keyboard interrupt vector, and replaces it with
; the new one.

NEW_KB_VECTOR:
LDS SI,KEYBOARD_INT ; point DS:SI to INT 9 in the vector table
MOV DI,OFFSET OLD_KB_INT ; point ES:DI to our storage for old INT 9 vector
MOV ES,CS
CLI ; disable all interrupts but NMI
MOV AX,OUR_HANDLER ; point to our new handler
XCHG AX,[SI] ; store new handler offset and fetch the old one
STOSW ; store the old handler offset
MOV AX,CS ; point to this code segment
XCHG AX,[SI+2] ; store new segment and fetch the old one
STOSW ; store the old handler segment
STI ; re-enable interrupts
RET


; WINDOW_SOURCE contains the text to be used to construct the pop-up window.
; Our program will combine this text with attribute bytes, to make a video
; image.

WINDOW_SOURCE:
DB 201, W_COLS-2 DUP 205 ,187
DB 186,' PRINTER SETUP MENU ',186
DB 186,' EPSON RX/FX PRINTERS ',186
DB 199, W_COLS-2 DUP 196 ,182
DB 186,' F1 Compressed Mode ',186
DB 186,' F2 Expanded Mode ',186
DB 186,' F3 Emphasized Mode ',186
DB 186,' F4 Double-Strike Mode ',186
DB 186,' F5 Elite Mode ',186
DB 186,' F6 Miniature Mode ',186
DB 186,' F7 Skip Perforation ',186
DB 186,' F8 Indent Left Margin ',186
DB 186,' F9 Reset Top-of-Form ',186
DB 186,' F10 Reset Print Modes ',186
DB 186,' ESC Exit ',186
DB 199, W_COLS-2 DUP 196 ,182
DB 186,' Unshifted: Toggle ON ',186
DB 186,' Shifted: Toggle OFF ',186
DB 200, W_COLS-2 DUP 205 ,188
W_SIZE EQU ($-WINDOW_SOURCE)



;-----------------------------------------------------------------------------
; Above this point is the part of the program that exists only when we
; being installed. Everything above this point is wiped out by
; the filling of OLD_IMAGE when our window is popped up.



TRUE EQU (0 LT 1) ; define TRUE so following line is an assertion
TRUE EQU ($ LT IMAGE_SIZE) ; insure we haven't used up too much room
ORG IMAGE_SIZE ; now there's enough room for OLD_IMAGE


OLD_KB_INT DD ? ; storage for old keyboard interrupt vector
CURSOR_TYPE DW ? ; cursor scan line definition saved from user screen
WINDOW_ACTIVE? DB 0 ; NZ if our pop-up window is already active
ENABLE_VALUE DB ? ; video-chip programming value for the user's screen
VIDEO_SEG DW ? ; segment address of the current memory-mapped video


; CODE_TABLE gives the codes to be output to the printer for each successive
; function key that can be pressed when the window is popped up. The
; 255 terminator bytes are also used to locate the code for a given key.

CODE_TABLE:
DB 15,255 ; F1: compressed mode on
DB 27,87,1,255 ; F2: expanded mode on
DB 27,69,255 ; F3: emphasized mode on
DB 27,71,255 ; F4: double-strike mode on
DB 27,77,255 ; F5: elite mode on
DB 15,27,83,0,27,65,6,255 ; F6: miniature mode on
DB 27,78,12,255 ; F7: perfskip on
DB 27,108,10,255 ; F8: indent left margin
DB 27,67,66,255 ; F9: reset top-of-form
DB 18,27,87,0,27,70,27,72 ; F10: reset print modes
DB 27,80,27,84,27,50,255 ; (continued)
DB 18,255 ; Shift-F1: compress off
DB 27,87,0,255 ; Shift-F2: expand off
DB 27,70,255 ; Shift-F3: emphasize off
DB 27,72,255 ; Shift-F4: double-strike off
DB 27,80,255 ; Shift-F5: elite off
DB 18,27,84,27,50,255 ; Shift-F6: miniature off
DB 27,79,255 ; Shift-F7: perfskip off
DB 27,108,0,255 ; Shift-F8: indent off



; OUR_HANDLER is our replacement for the INT 9 keyboard handler. We save
; registers, then call the old keyboard handler. If CTRL+RIGHT_SHIFT is
; pressed, we activate our code. Otherwise, we exit as if we were the
; old handler.

OUR_HANDLER:
STI ; enable software interrupts
PUSH AX ; hot-key check clobbers AX only so save it
IN AL,0A0 ; re-enable NMI on PCjr
PUSHF ; simulate interrupt call to old keyboard routine
CS CALL OLD_KB_INT ; call old routine
MOV AH,2 ; BIOS function number for GET_SHIFT_STATUS
INT 016 ; set AL to the shift-status bitmap
AND AL,5 ; mask the map down to the CTRL and RIGHT_SHIFT keys
CMP AL,5 ; are CTRL and RIGHT_SHIFT depressed?
JNE AX_EXIT ; exit if they are-- we're already activated
PUSH BX,CX,DX,SI,DI ; save the other general registers
PUSH DS,ES ; also save segment registers
MOV DS,ES,CS ; set ES and DS to the code segment
TEST WINDOW_ACTIVE? ; is the window already open?
JNZ EXIT ; if yes then ignore request
MOV AH,15 ; BIOS function number for GET_VIDEO_STATUS
INT 010 ; set AL = video mode, BH = video page number
MOV AH,0 ; ENABLE_BYTE is zero for video mode 7
MOV CX,0B000 ; VIDEO_SEG is 0B000 for video mode 7
CMP AL,7 ; is the video mode 7 (monochrome)?
JE >L0 ; jump if yes, that's an acceptible mode
MOV CH,0B8 ; VIDEO_SEG is 0B800 for page 0, color modes
ADD CH,BH ; advance VIDEO_SEG to the proper value for this page
MOV AH,02D ; ENABLE_BYTE is 02D for video mode 2
CMP AL,2 ; is the video mode 2?
JE >L0 ; jump if yes, that's also an acceptible mode
MOV AH,029 ; ENABLE_BYTE is 029 for video mode 3
CMP AL,3 ; not 7 or 2; it had better be 3
JNE EXIT ; error if not
L0:
CALL POPUP_WINDOW ; everything's OK so pop up our window
JMP SHORT NEXT_KEY ; jump into the interactive loop


; ESC_SEEN is jumped to when the ESC key is pressed. The window is refilled
; with its original contents, the cursor is restored, and control is handed
; back to the application program.

ESC_SEEN:
CALL VIDEO_DISABLE ; if not then turn off the display
MOV SI,OLD_IMAGE ; point SI to the buffer area
CALL MEM_TO_VIDEO ; write the buffer contents to the display
CALL VIDEO_ENABLE ; if not then turn display back on
MOV CX,CURSOR_TYPE ; fetch the user's cursor type
MOV AH,1 ; BIOS function number for SET_CURSOR_SIZE
INT 010 ; restore the user's cursor
MOV WINDOW_ACTIVE?,0 ; the window is no longer active
EXIT: ; common exit point
POP ES,DS ; restore interrupted segment registers
POP DI,SI,DX,CX,BX ; restore interrupted general registers
AX_EXIT: ; exit here if we saved AX only
POP AX ; restore interrupted AX
IRET ; return from interrupt


; NEXT_KEY is the main loop for the interactive portion of our handler. We
; intercept all keystrokes while our window is open, and act on them.
; This interactive mode is terminated when the ESC key is seen.

FUNC EQU 58 ; defining code for non-shifted function keys
SHIFT_F EQU 83 ; defining code for shifted function keys

BEEP_KEY: ; jump here if an illegal key is seen
CALL BEEP ; beep and wait for another keypress
NEXT_KEY:
MOV AH,0 ; BIOS function code for GET_KEY
INT 016 ; fetch a keystroke from the BIOS
CMP AL,27 ; is it the ESC key?
JE ESC_SEEN ; jump if it is, to exit our interactive mode
CMP AL,0 ; is it an extended code?
JNE BEEP_KEY ; error if not
MOV AL,AH ; move the extended code into AL, for examination
CMP AL,FUNC 1 ; less than F1?
JB BEEP_KEY ; yes, then don't accept it
CMP AL,SHIFT_F 8 ; greater than Shift-F8?
JA BEEP_KEY ; yes, then don't accept it
CMP AL,FUNC 10 ; between F1 and F10?
JBE >L1 ; jump if yes -- unshifted function key
CMP AL,SHIFT_F 1 ; between Shift-F1 and Shift-F9?
JB BEEP_KEY ; error if not
SUB AL,15 ; adjust so Shift-F1 has an index of 10
L1:
SUB AL,FUNC 1 ; adjust so F1 has an index of 0
CBW ; extend the index AL to AX
XCHG CX,AX ; swap the index into CX
MOV SI,CODE_TABLE ; point to the first of our code sequences
JCXZ >L3 ; skip if our index is zero
L2: ; loop here for each code byte to be skipped
LODSB ; fetch a code byte
CMP AL,255 ; is it a terminator?
JNE L2 ; if yes then loop without counting down the index
LOOP L2 ; terminator: count down index and loop for next line
L3:
CALL LPT1_ERROR? ; check for printer ready
JNZ BEEP_KEY ; beep if printer not ready
MOV BL,255 ; specify delimiter for call to LPRINTZ
CALL LPRINTZ ; send control code string to printer
JMP NEXT_KEY ; return for another keypress


; POPUP_WINDOW saves the current video state, then overlays our window
; on the screen.

POPUP_WINDOW:
MOV VIDEO_SEG,CX ; store our video segment
MOV ENABLE_VALUE,AH ; store hardware value for re-enabling the screen
CLD ; all string scanning is forward
INC WINDOW_ACTIVE? ; store the fact that the window is now active
MOV AH,3 ; BIOS function number for READ_CURSOR
INT 010 ; set CX to the cursor type (scan-line boundaries)
MOV CURSOR_TYPE,CX ; save the cursor type
MOV CH,32 ; load impossible scan-line number
MOV AH,1 ; BIOS function number for SET_CURSOR_SIZE
INT 010 ; the cursor is now invisible
CALL VIDEO_DISABLE ; turn display off for snow-free writing
MOV DS,VIDEO_SEG ; fetch the video segment in effect
MOV DI,OLD_IMAGE ; set DI to buffer area to save screen contents
CALL VIDEO_TO_MEM ; then transfer screen contents to buffer
MOV ES,DS ; set ES to video memory
MOV DS,CS ; reset DS to code segment
LEA SI,WINDOW_IMAGE ; point SI to window image
CALL MEM_TO_VIDEO ; and write the window to the display
VIDEO_ENABLE:
MOV AL,ENABLE_VALUE ; fetch the re-enable hardware value
TEST AL ; are we in monochrome mode?
JZ RET ; return if yes, we never disabled the screen
MOV DX,03D8 ; address the CGA Mode Control Register
OUT DX,AL ; output the ENABLE_VALUE to the control register
RET



; VIDEO_ENABLE and VIDEO_DISABLE manipulate bit 3 of port 3D8H, the CGA Mode
; Control Register, to temporarily turn the display on or off. Since these
; routines write directly to hardware, they have no effect on other video
; adapters.

VIDEO_DISABLE:
TEST ENABLE_VALUE ; are we in monochrome mode?
JZ RET ; return if yes-- snow is not a problem
MOV DX,03DA ; address CGA status port
L1: ; loop here until we see vertical retrace
IN AL,DX ; fetch the status directly from hardware
TEST AL,8 ; mask the vertical-retrace bit
JE L1 ; loop if we do not yet see vertical retrace
MOV DL,0D8 ; retrace is seen: address the Control Register
MOV AL,025 ; this value will disable the display
OUT DX,AL ; the display is disabled
RET



; VIDEO_TO_MEM copies a window from the video segment DS, to a memory buffer at
; ES:DI.

VIDEO_TO_MEM:
MOV BL,W_LINES ; load the number of lines in a window
MOV SI,VIDEO_OFFSET ; point to our window within the video segment
L1: ; loop here to copy each line
MOV CX,W_COLS ; load the number of columns in the line
REP MOVSW ; transfer one line
ADD SI,VIDEO_GAP ; advance SI to next line address
DEC BL ; count down lines
JNZ L1 ; loop until all lines are done
RET



; MEM_TO_VIDEO writes a window from memory at DS:SI to the video segment ES.

MEM_TO_VIDEO:
MOV BL,W_LINES ; load the number of lines in a window
MOV DI,VIDEO_OFFSET ; point to our window within the video segment
L1: ; loop here to copy each line
MOV CX,W_COLS ; load the number of columns in the line
REP MOVSW ; transfer one line
ADD DI,VIDEO_GAP ; advance DI to next line address
DEC BL ; count down lines
JNZ L1 ; loop until all lines are done
RET



; LPRINTZ routine sends a string of bytes, delimited by BL, from DS:SI to
; LPT1: thru INT 17h.

L1: ; loop here to send AL to the printer
MOV DX,0 ; printer no. 0 (LPT1:)
MOV AH,0 ; BIOS function number for SEND_BYTE_TO_PRINTER
INT 017 ; send the byte
LPRINTZ:
LODSB ; get one byte
CMP AL,BL ; is it the delimiter?
JNE L1 ; loop if not, to output the byte
RET


; LPT1_ERROR? checks the current status of printer LPT1:. If it's either
; powered off or off-line, then we return NZ to signal an error condition.
; If LPT1 is ready, we return Z.

LPT1_ERROR?:
SUB DX,DX ; printer number 0
MOV AH,2 ; use ROM BIOS 'get status' function
INT 017 ; status is returned in AH
TEST AH,8 ; test bit 3, I/O error indicator
RET ; Z means ready, NZ means error


; BEEP uses the 8253 timer chip to emit a short beep thru the PC's speaker.

BEEP:
MOV AL,182 ; notify 8253 that frequency data is coming
OUT 67,AL ; 8253 now programmed to take a frequency
MOV AL,0 ; load the low byte of the frequency (776.8 Hz)
OUT 66,AL ; output the low byte
MOV AL,6 ; load the high byte of the frequency
OUT 66,AL ; output the high byte
IN AL,97 ; fetch program value from hardware
OR AL,3 ; turn on the bottom two bits, to activate the speaker
OUT 97,AL ; speaker is activated
MOV CX,06000 ; time delay for sound duration
L1: ; loop here while speaker sounds
LOOP L1
IN AL,97 ; fetch program value from hardware
AND AL,NOT 3 ; turn off the bottom two bits, to deactivate the speaker
OUT 97,AL ; speaker is shut off
RET


; WINDOW_IMAGE is the place where the video image of our pop-up window is
; constructed when we install ourselves.

WINDOW_IMAGE:

END_OF_PROGRAM EQU $ + IMAGE_SIZE