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

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

; SETUP is a memory-resident program that sends control codes to an
; Epson-compatible printer. After SETUP 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.

JMP INITIALIZE ; goto initialization routine

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

KEYBOARD_INT DD 0:(09 * 4)
MACHINE_ID DD 0F000:0FFFE
CURSOR_MODE DD 040:060

COLUMN_COUNT DW ? ; width of window in columns
CURSOR_TYPE DW ? ; cursor scan line definition
WINDOW_ACTIVE? DB 0 ; indicates if printer window is already active
DISPLAY_MODE DW ? ; current crt display mode
PAGE_NUMBER DW ? ; current displayed page
ATTRIBUTE1 DB 4FH ; window attribute bytes
ATTRIBUTE2 DB 70H

DISPLAY_TABLE DB 2DH,29H ; display re-enable values for modes 2 and 3
VIDEO DW 0B800,0B900 ; starting addresses of video memory for CGA
DW 0BA00,0BB00

MONO_VIDEO DW 0B000H ; video segment address for
; monochrome adapter

OLD_KB_INT DD ? ; storage for old keyboard interrupt vector


; Text of the Printer Setup Menu window.
; After initialization, text and attribute bytes are combined and stored
; in the WINDOW_IMAGE area, and this area is used to store the contents of
; the screen that underlie the window when the window is called up.

WINDOW_SOURCE:
DB 201, 26 DUP 205 ,187
DB 186,' PRINTER SETUP MENU ',186
DB 186,' EPSON RX/FX PRINTERS ',186
DB 199, 26 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, 26 DUP 196 ,182
DB 186,' Unshifted: Toggle ON ',186
DB 186,' Shifted: Toggle OFF ',186
DB 200, 26 DUP 205 ,188
DB 532 DUP (?)

; Storage area for the combination of text and attribute bytes that
; form the window image.

WINDOW_IMAGE:
DW 532 DUP ?

; Control code strings for all of the printer setup options.

CODE_TABLE:
DB 15,255,14 DUP 0 ; compressed mode on
DB 27,87,1,255,12 DUP 0 ; expanded mode on
DB 27,69,255,13 DUP 0 ; emphasized mode on
DB 27,71,255,13 DUP 0 ; double-strike mode on
DB 27,77,255,13 DUP 0 ; elite mode on
DB 15,27,83,0,27,65,6,255,8 DUP 0 ; miniature mode on
DB 27,78,12,255,12 DUP 0 ; perfskip on
DB 27,108,10,255,12 DUP 0 ; indent left margin
DB 27,67,66,255,12 DUP 0 ; reset top-of-form
DB 18,27,87,0,27,70,27,72,27,80,27,84,27,50,255,0 ; reset print modes
DB 18,255,14 DUP 0 ; compress off
DB 27,87,0,255,12 DUP 0 ; expand off
DB 27,70,255,13 DUP 0 ; emphasize off
DB 27,72,255,13 DUP 0 ; double-strike off
DB 27,80,255,13 DUP 0 ; elite off
DB 18,27,84,27,50,255,10 DUP 0 ; miniature off
DB 27,79,255,13 DUP 0 ; perfskip off
DB 27,108,0,255,12 DUP 0 ; 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,BX,CX,DX,SI,DI ; save all registers
PUSH DS,ES
PUSH AX ; save ax for call to old routine
IN AL,0A0H ; re-enable NMI on PCjr
POP AX ; restore ax
PUSHF ; simulate interrupt call to old keyboard routine
CS CALL OLD_KB_INT ; call old routine
MOV AH,2 ; check status of the shift keys
INT 16H
AND AL,5 ; Ctrl and Rt-Shift depressed?
CMP AL,5
JE >L1 ; yes, then skip exit routine
EXIT: ; common exit point
POP ES,DS
POP DI,SI,DX,CX,BX,AX
IRET ; return from interrupt

; Execution comes here when the proper key combination, Ctrl/Rt-Shift, is
; pressed. First task is to check whether or not the window is already open.

L1:
MOV DS,ES,CS ; set es and ds to the code segment
TEST WINDOW_ACTIVE? ; is the window already open?
JNZ EXIT ; yes, then ignore request

; Check current video mode. If it's mode 2, 3, or 7, then set the window
; status flag, store the mode number and page number, save the cursor type,
; and hide the cursor. If any other display mode is active instead, ignore
; the request and exit.

MOV AH,15 ; get page and mode numbers
INT 10H ; al=mode, bh=page
CMP AL,2 ; is crt now in an acceptable mode?
JE >L0 ; yes, then continue
CMP AL,3
JE >L0
CMP AL,7
JE >L0
JMP EXIT

L0:
CLD ; all string scanning is forward
INC WINDOW_ACTIVE? ; store the fact that the window is now active
MOV AH,0 ; extend the mode number AL to AX
MOV DISPLAY_MODE,AX ; save the mode number
PUSH BX
MOV BL,BH ; save page number for color displays
MOV BH,0
MOV PAGE_NUMBER,BX
POP BX
MOV AH,3 ; get cursor type
INT 10H
MOV CURSOR_TYPE,CX ; save it
MOV AH,1 ; hide the cursor until later
MOV CH,20H
INT 10H ;

; Preparatory routines are completed. Now open the window by first saving the
; contents of video memory beneath the window and then writing the window text
; directly to memory.

MOV BX,PAGE_NUMBER ; use BX as index into video segment address table
CMP DISPLAY_MODE,7 ; manually adjust index for monochrome adapter
IF E MOV BX,4
SHL BX,1 ; multiply BX by two since table is made up of words
MOV AX,VIDEO[BX] ; read segment from table
CMP DISPLAY_MODE,7 ; skip disable if in mode 7
MOV DS,AX ; DS set to video memory
IF NE CALL VIDEO_DISABLE ; turn display off for snow-free writing
MOV DI,WINDOW_SOURCE ; set DI to buffer area to save screen contents
MOV CX,28 BY 19 ; define window dimensions and location
MOV DX,2 BY 26
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
MOV CX,28 BY 19 ; define window region
MOV DX,2 BY 26
CALL MEM_TO_VIDEO ; and write the window to the display
CMP DISPLAY_MODE,7 ; skip enable if in mode 7
IF NE CALL VIDEO_ENABLE ; re-enable the video display
JMP SHORT NEXT_KEY


; 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:
CMP DISPLAY_MODE,7 ; are we in monochrome mode?
IF NE CALL VIDEO_DISABLE ; if not then turn off the display
MOV SI,WINDOW_SOURCE ; point SI to the buffer area
MOV CX,28 BY 19 ; define the window
MOV DX,2 BY 26
CALL MEM_TO_VIDEO ; write the buffer contents to the display
CMP DISPLAY_MODE,7 ; are we in monochrome mode?
IF NE CALL VIDEO_ENABLE ; if not then turn display back on
MOV AH,1 ; restore cursor
MOV CX,CURSOR_TYPE
INT 10H
MOV WINDOW_ACTIVE?,0 ; the window is no longer active
JMP EXIT ; exit back to the interrupted user program



; 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.

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 16H ; 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,59 ; less than F1?
JB BEEP_KEY ; yes, then don't accept it
CMP AL,91 ; greater than Shft-F8?
JA BEEP_KEY ; yes, then don't accept it
CMP AL,68 ; between F1 and F10?
JBE UNSHIFTED ; yes
CMP AL,84 ; between Shft-F1 and Shft-F9?
JAE SHIFTED ; yes
JMP BEEP_KEY ; if all tests failed, then keypress was illegal

; If a legal function key was pressed, its scan code is translated here to the
; starting address of the string of bytes to be sent to the printer. The
; string is then sent to LPT1: provided it's powered on and on-line.

SHIFTED:
SUB AL,15 ; adjustment for shifted function keys
UNSHIFTED:
SUB AL,59 ; adjustment for unshifted function keys
MOV AH,0
MOV CL,4 ; multiply ax by 16
SHL AX,CL
ADD AX,CODE_TABLE ; convert ax to full offset address
MOV SI,AX ; and transfer it to si
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



; 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:
MOV DX,03DA ; read CGA status port
L1:
IN AL,DX ; wait for vertical retrace to occur
TEST AL,8 ; is bit 3 set?
JE L1 ; no, wait until it is
MOV DX,03D8 ; now disable the display
MOV AL,025 ; by clearing bit 3 of the Mode Control Register
OUT DX,AL
RET

VIDEO_ENABLE:
MOV DX,03D8 ; CGA Mode Control Register
MOV BX,DISPLAY_MODE ; get value to re-enable display
MOV AL,DISPLAY_TABLE[BX-2]
OUT DX,AL ; and send it to the port
RET



; VIDEO_TO_MEM copies a window (size CH columns by CL lines) from row DH,
; column DL of the video segment DS, to a memory buffer at ES:DI.

VIDEO_TO_MEM:
MOV AL,CH ; store number of columns
MOV AH,0
MOV COLUMN_COUNT,AX
MOV CH,0 ; CX = number of lines
PUSH DI ; save DI
CALL VIDEO_OFFSET ; get cell address of first character
MOV SI,DI ; put it in SI
POP DI ; restore DI
L1:
PUSH SI,CX ; save next line pointer and line count
MOV CX,COLUMN_COUNT ; load the number of columns in the line
REP MOVSW ; transfer one line
POP CX,SI ; restore saved registers
ADD SI,160 ; set si for next line address
LOOP L1 ; loop until all lines are done
RET



; MEM_TO_VIDEO writes a window (size CH columns by CL lines), from memory at
; DS:SI to the video segment ES, row DH, column DL.

MEM_TO_VIDEO:
MOV AL,CH ; save number of columns
MOV AH,0
MOV COLUMN_COUNT,AX
MOV CH,0 ; cx = number of lines
CALL VIDEO_OFFSET ; get offset into video memory
L1:
PUSH DI,CX ; save video starting address and line count
MOV CX,COLUMN_COUNT ; load the number of columns in the line
REP MOVSW ; transfer one line
POP CX,DI ; restore registers
ADD DI,160 ; set di for next display line
LOOP L1 ; loop until done
RET



; VIDEO_OFFSET sets DI to the offset within the video memory segment of row
; DH (0-24), column DL (0-79).

VIDEO_OFFSET:
MOV AL,160
MUL DH ; row * 160
SHL DL,1 ; column * 2
MOV DH,0 ; byte to word
ADD AX,DX ; (row *160)+(column*2)
MOV DI,AX ; set offset in di
RET


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

L1:
MOV DX,0 ; printer no. 0 (LPT1:)
MOV AH,0
INT 17H ; send byte to printer
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?:
MOV DX,0 ; printer no. 0
MOV AH,2 ; use ROM BIOS 'get status' function
INT 17H ; 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
MOV AL,0 ; send frequency (776.8 Hz)
OUT 66,AL
MOV AL,6
OUT 66,AL
IN AL,97 ; activate speaker
OR AL,3
OUT 97,AL
MOV CX,6000H ; time delay for sound duration
L1:
LOOP L1
IN AL,97 ; deactivate speaker
AND AL,NOT 3
OUT 97,AL
RET



; Initialization routine sets up the window image in the WINDOW_IMAGE area,
; resets the CURSOR_MODE word if this is a PCjr, and saves and replaces the
; old keyboard interrupt vector.

INITIALIZE:

; Initialize the window text area by combining the text data with the attribute
; bytes and placing the conglomeration in the WINDOW_IMAGE area.

CLD ; string scanning is forward
MOV AH,15 ; check the current video mode
INT 10H
CMP AL,7 ; if it's mode 7, then replace the attribute
JNE >L0 ; bytes with ones appropriate for mono adapter
MOV ATTRIBUTE1,70H
MOV ATTRIBUTE2,07H
L0: ; now combine the text and attribute bytes
LEA SI,WINDOW_SOURCE ; point SI to table of text
LEA DI,WINDOW_IMAGE ; point DI to the storage area
MOV CX,112 ; create first four lines by combining text with
MOV AL,ATTRIBUTE1 ; ATTRIBUTE1 (112 words)
L1:
MOVSB ; text byte
STOSB ; attribute byte
LOOP L1 ; loop until all 112 words are done
MOV CX,11 ; now do the next 11 lines
L2:
PUSH CX ; first attribute in each line is ATTRIBUTE1
MOVSB
STOSB
MOV CX,26 ; next 26 attributes are ATTRIBUTE2
MOV AL,ATTRIBUTE2
L3:
MOVSB
STOSB
LOOP L3
MOVSB
MOV AL,ATTRIBUTE1 ; and the last in each line is ATTRIBUTE1
STOSB
POP CX
LOOP L2 ; loop until all 11 lines are done
MOV CX,112 ; create the last four lines just like the first four
L4:
MOVSB
STOSB
LOOP L4

; Check the machine ID byte in ROM and if this is a PCjr, then reset the
; cursor and correct the CURSOR_MODE word at 0040:0060.

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 >L5 ; 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
XCHG CX,AX ; swap the value into CX, for a BIOS call
MOV AH,1 ; then physically reset the cursor
INT 10H

; Now save the old keyboard interrupt vector and replace it with the new one.

L5:
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
MOV DX,INITIALIZE ; point dx to end of resident section
INT 27H ; terminate-but-stay-resident