	PAGE  60,132
	TITLE	EPSON MX-GRAFTRAX PrtSc, V 3.3
; GRAF.COM  2/10/85
; Interrupt replacement for print screen function on ibmpc(tm)
;	Please send problem reports and suggestions to:
;		Marty Smith
;		310 Cinnamon Oak Lane
;		Houston, Texas	77079
;		Compuserve 72155,1214
;		(713) 464-6737
;
;    Create GRAF.COM with MASM*, LINK* and EXE2BIN* as follows:
;
;	masm graf,graf,graf;
;
;	link graf;    (ignor the error message about no stack segment,
;			  that's taken care of in the next step.)
;	exe2bin graf.exe graf.com
;
;  *  MASM is the MicroSoft(tm) Macro Assembler v. 3.0
;	or IBM(tm) Macro Assembler v. 1.0
;     LINK and EXE2BIN are PC-DOS(tm) utilities.
;
;  This program originally designed for Epson-MX(tm) series printers
;  with Graftrax80(tm) and Graftrax+(tm) bit-plot graphic capabilites.
;  ======> Now modified with conditional compilation and macros for
;  ======> c.itoh(tm) model 8510a and other printers.
;
;	CHANGE HISTORY:
;	9/18/82 - Buffer in routine for a line of bit-plot bytes to allow for
;  checking for blank lines replaced by pre-scan routine, saving space.
;	1/24/83 - Modifications for conditional assembly with other printers
;  and C.ITOH 8510a.
;	1/24/83 - Improved error checking for out-of-paper and I/O errors
;  involving printer.
;	1/24/83 - Bug in error check corrected, occuring when routine does
;  error exit and is then called again, resulting in bit-plot data sent in
;  regular mode.
;	3/12/83 - Allow calling as a subroutine. i.e. no shift key depressed.
;  Defaults to small print mode. Can be set to LARGE.
;	2/4/84	- Allow correct printing of 640x200 mode.
;	3/18/84 - Use int 31h for dos 2.+ terminate process.
;	3/24/84 - Use BIOS for keyboard scan, in case screen is printed
;		from DOS.
;	4/21/84 - -OTHER- section complete for changes from 640 mode.
;	9/22/84 - Add code to set lines back in 6/inch order, so CR's
;		can advance to TOF.
;	9/22/84 - Jump to other print screen routine instead of reassigning
;		it to int f1h. Only luck has kept this vector from being used
;		by someone else.
;	10/20/84- Add check for already installed, don't reinstall.
;	10/20/84- Compatibility with MASM 1.0 reestablished, FAR call to old
;		routine caused 'fixup error' from EXE2BIN. 
;	10/20/84- CALLGRAF now points to common address variable for default
;		mode, is now the same for all versions of program.  Demo now
;		works!
;
;	Features:
;  Accepts ESC key exit, prescans to test for blank line
;  left shift prtsc = small graphics, right shift prtsc = big
;  Runs as a .COM type program under dos
;     resident until power down or reset.
;   1 = screen sent horiz. 320 bits in 480 mode
;   2 = screen sent vert. 400 bits double printed in 480 mode
; **************  1 mode **********************
;	DL = masking character
;	DH = count of 25 (physical lines)
;	CX = counter for each line (80)
;	DS = used to index screen at 'b8000'
;   These regs must be preserved during routine
;	  (increment each line by adding '14' hex to ds: for paragraph
;		boundary of 320 bytes 0x'140')
; **************  2 mode *********************
;	DH = count of 40 (physical lines)
;	CX = counter for each line (100)
;	SI = index to screen via ds:
;   These regs must be preserved during routine
;	all output to printer is done from routine -send2-, which uses
;	bios routine int 17h, and provides safe error exit.
;
;  GRAF.COM is designed with the idea that the user's main program is the
;  primary function and GRAF.COM should not cause problems of its own.
;
; =======================================================
; =		USER MODIFICATION SECTION.		=
; =	If your printer can treat a byte of data to	=
; =	control the wires on the dot-matrix head you	=
; =	can probably get this working with your printer =
; =	unless it is an IDS type, which treats bit plot =
; =	a lot differently than the EPSON.		=
; =	If you have Epson Graftrax or a C.Itoh 8510a,	=
; =	just set the two equates indicated to -true-	=
; =	and compile. Otherwise get out your manual	=
; =	and put the code indicated in one of the printer=
; =	sections.					=
; =	**>	The title message starts at label	=
; =	**>	-buffer-. If you set for another	=
; =	**>	printer you should change the		=
; =	**>	greeting to indicate which printer	=
; =	===> ONLY SET *ONE* CONDITION TO -TRUE- or you	=
; =	will have a real mess!		marty smith	=
; =======================================================

TRUE	equ	1	; DON'T CHANGE THESE!
FALSE	equ	0

escape	equ	27	; for printer
CR	equ	13
LF	equ	10

; ===============>  A L L  U S E R S  <===================
; ====> SET ONE AND ONLY ONE OF THE FOLLOWING THREE <=====

EPSON	EQU	TRUE
CITOH	EQU	FALSE	; citoh and nec 8023 use similar codes.
NEC	EQU	FALSE
OTHER	EQU	FALSE

; Each bit of a byte is mapped to the wire head of the printer.
; If the Epson MX is sent 80h (bit 7), the TOP wire makes a dot.
; If the C.ITOH is sent   01h (bit 0), the TOP wire makes a dot.
; ===============>  A L L  U S E R S  <===================
; =====> SET ONE AND ONLY ONE OF THE FOLLOWING TWO <======

BIT7	EQU	TRUE
BIT0	EQU	FALSE

; BIT7 is TRUE for EPSON
; BIT0 is TRUE for CITOH,NEC8023

print	macro	char
	mov	al,char
	call	SEND2
	endm

; *****************************
; *  START of -OTHER- SECTION *
; *****************************
;
; ALL routines must set either BIT7 or BIT0.
;   above. If your printer can't see bit 7 or 0 as the top wire, you
;   will probably have quite a time getting this routine to work.
;
; OLINE Resets line spacing so that the print head
; will make a continuous line DOWN the page.
; This is the sequence to set the EPSON for this. (ESC A 8)
; SEND2 sends the byte in AL to the printer for ALL routines.
; It uses the INT 17h routine in order to avoid DOS's extra line
; feeds and CR's. Set for LPT1: Change SEND2 to redirect. DX=0=LPT1,1=LPT2
OLINE	MACRO
	print	escape
	print	'A'
	print	 8
	ENDM

; ORLINE restores the printer to normal line spacing
; Example is for EPSON (ESC 2)
ORLINE	MACRO
	print	escape
	print	'2'
	ENDM

; ORESET reinitializes the printer to default settings, spacings,
; current line becomes Top of Form.
; Is used by LARGE print to allow a series to be printed
; on separate pages. It can be modified by getting rid of label
; TOF: up to but not including	 JMP DONE, which is the exit from
; the whole routine. Example is for EPSON. (ESC @)
ORESET	MACRO
	print	escape
	print	'@'
	ENDM

; BP1 initiates bit-plot graphics. It tells the printer
; that the next xxx bytes are to be considered bit-plots and not
; regular characters. The small print routine sends 320 bit plot bytes
; to the printer. On the EPSON this is:
;	  ESC K 64 1   hex 1b 4b 40 1  > 1*256+64=320
; --> the first part indents the page with ordinary spaces
; --> to find spaces take TOTAL_DOTS_PER_LINE - 320. Then / BITS_PER_CHARACTER
; --> Divide this by two and you have the spaces to indent
; BP1 also needs to be able to handle the case of 640 mode, which will
; double the output of dots.
;    The example below is the code to do this on the EPSON/IBM.
BP1	MACRO
	mov	cx,13	; PICTURE ( we've got 320 dots and 480 to work with
inlop:	print	20H	; 480-320=160 / 6 dots per char. = 26.67 extra
	loop	inlop

; ESC K 64 1  = 256+64=320 bit plot type bytes on the way

	cmp	cs:mode640,TRUE
	jz	ind10
	print	escape 	; SEQUENCE TO SET UP 320
		   	; BIT PLOTS IN 480 MODE
	print	'K' 	; OF MX-80
		   	; This is the set-up for the small print
	print	64
	print	 1
	jmp short indend

ind10:
	print	escape
	print	'L'  	; 640 dots in 960 mode
	print	128
	print	2

indend:
	ENDM

; BP2.
; The LARGE print sends 400 bit plot bytes to the printer. On the EPSON:
; ESC K 144 1  hex 1b 4b 90 1  > 1*256+144=400
; FIND YOUR INDENT FOR 400 BITS
BP2	MACRO
	mov	cx,6	; EPSON ( we've got 400 dots and 480 to work with
inlop2: print	20H	; 480-400=80 / 6 dots per char. = 13.33 extra
			; so indent the picture 6 spaces to center
	loop	inlop2
	print	escape
	print	'K'
	print	144
	print	1
	ENDM

; ****************************
; *   END OF -OTHER- SECTION *
; ****************************

;  ***************> START OF ACTUAL CODE <*****************

cseg	segment 'code'
	assume cs:cseg
	org	100h		; set up for .com conversion
init	proc
	jmp	initial 	; so we have to set up first
init	endp
gowait	dw	0
wheresi dw	0
ptflag	db	0
oneor2	db	0
dstor	dw	0
mode640 dw	0

; **** the 1 below is the POKE to use in CALLing from another program. ****
;
default_routine dw	1	;  1 for small, 2 for LARGE.		  *
;
; *************************************************************************

old_print_routine  dd	0	; address of former print screen routine.

do_old	proc	near
; jump to old routine
	pop	bp
	pop	si
	pop	di
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	pop	ds
	jmp	cs:old_print_routine

do_old	endp

; --->	ACTUAL INTERRUPT ROUTINE STARTS HERE  <---
start	proc	far	; Start of main routine--Shift Prt.Sc hit.
	assume cs:cseg
	sti		; This follows ROM routine real close
	push	ds
	push	ax	;SAVE REGS
	push	bx
	push	cx
	push	dx
	push	di
	push	si
	push	bp
	mov	bp,sp		; Save in case of error for reset
	mov	ax,50h		; Check here first to see if
	mov	ds,ax		; routine is already in progress
	mov	si,0		; otherwise it will be a mess.
	mov	al,01h		; if [si]=1 then there is a 
	cmp	[si],al		; print screen already in progress.
	jnz	nxts		; if not we're go for routine
	jmp	exit		; otherwise go back home.
nxts:	mov	[si],al 	; 
	mov	ah,15		; Get the current video state.
	int	10h		; from the ROM routine,
	mov	cs:mode640,FALSE
	cmp	al,4		; AL=4-6 are all graphics so we're OK
	jz	graphic
	cmp	al,5
	jz	graphic
	cmp	al,6
	jnz	nxts10
	mov	cs:mode640,TRUE ; special case
	jmp	short graphic
nxts10:
	mov	al,0	; else reset and go to ROM routine.
	mov	[si],al
	jmp	do_old	; this is where we stored the ROM routine entry.
;	jmp	exit	; Do the ROM routine but come back here to leave.
graphic:
	mov	ax,40h	; Get the keyboard shift flag
	mov	ds,ax	; segment
	mov	si,17h	; and address
	mov	ax,[si] ; pick it up
	and	ax,3	; get rid of other stuff
	or	ax,ax	; Mod. to create default small
	jnz	gr1	; for case where routine is called as a subroutine.
	mov	ax,cs:default_routine
gr1:	mov	cs:oneor2,al	; store for later
	push	ax	; also here
	xor	al,al	; make sure this starts out as NO print.
	mov	cs:ptflag,al
	xor	dl,dl	; These bits indicate whether R or L Shift is down
	mov	dh,19h	; 25 lines of graphic dots at 8 dots per line
	mov	ax,0b800h ; stored in DX
	mov	ds,ax	;SET UP FOR SCREEN PEEK
; Printer setup section to change line spacing to 8/72" for continuous dots

; line spacing routine - All Epson Graftrax and IBM Graphics should
; accept esc 'A' 8  or  esc '3' 24 for line spacing, but IBM Graphics only
; recognizes esc '3' 24
	IF	EPSON
	print	escape
	print	'3'     ; A
	print	24	; 8
	ENDIF

	IF	CITOH	; ESC T 16
	print	escape
	print	'T'     ; T
	print	'1'     ; 1
	print	'6'     ; 6
	print	escape

	IF	NEC
	print	'['     ; Set printer to unidirectional for dot alignment
	ELSE
	print	'>'
	ENDIF

	ENDIF

	IF	OTHER
	OLINE
	ENDIF
	pop	ax	; get back which routine
	cmp	al,2	; Left Shift Prt Sc means LARGE graphic print
	jnz	gr2
	jmp	main2	; so hop over there if so.
gr2:
	cmp	cs:mode640,TRUE
	jnz	MAIN
	jmp	m640

; START OF small GRAPHICS PRINT ROUTINE.
; This routine scans across the screen from left to right,
; building an EPSON bit plot byte out of IBM screen dots.
; EPSON wire head		IBM screen color dots
; TOP	 o  128  80h bit 7	| 00 | 01 | 10 | 11 | = 4 dots, one byte
;	 o   64  40h  "  6
; one	 o   32  20h  "  5      ibm dots go one raster line then the next
; bit	 o   16  10h  "  4      EVEN line, ie 0, 2, 4 etc.
; plot	 o    8  08h  "  3
; byte	 o    4  04h  "  2      then you go back and do 1, 3, 5 etc.
;	 o    2  02h  "  1
; BOTTOM o    1  01h  "  0      At loc. 0000h are 4 dots, 0,0|0,1|0,2|0,3
;				At loc. 2000h are 4 dots, 1,0|1,1|1,2|1,3
;
main:	mov	cx,80	; 80 x 4 = 320 dots.
mloop:	mov	dl,0c0h ; 11000000b
	call	tst4	; see if this comes back <> 0
	mov	al,ah	; we are testing bit patterns for one screen byte
	call	send	; don't send to printer unless something to send
	mov	dl,30h	; 00110000b
	call	tst4	; each byte is 4 dots
	mov	al,ah	; so we test for each dot in a byte
	call	send	; send sets PTFLAG if there is a dot on the line
	mov	dl,0ch	; 00001100b
	call	tst4	; then resets to start of line and starts printing
	mov	al,ah	; AL is the bit plot byte being built
	call	send	; This keeps us from printing a line of '0's.
	mov	dl,03h	; 00000011b
	call	tst4	; TST4 scans down 8 screen dot lines each time called
	mov	al,ah
	call	send
	loop	mloop	; 80 bytes make 320 dots
	call	lfcr	; this is a good old regular line feed/carriage return
	call	break?	; someone hit ESC key? so take early exit
	or	al,al
	dec	dh	; DL is line counter
	cmp	dh,0	; when it goes 0 we're through
	jz	done	; reset everything and do an IRET
	mov	ax,ds	; otherwise bump the SEGMENT reg so that location
	add	ax,14h	; 0 is the start of the next line
	mov	ds,ax	; X'140' = 320
	jmp	main	; and do this 80 times (80x4=320)
done:	mov	ax,0
; This is the common exit for both routines, Printer is restored.
done1:	push	ax	; save AX cause it has error exit flag
; EPSON command to reset printer to 6 lines/in. = ESC 2 (1b 32)
	IF	EPSON
	print	escape
	print	'3'
	print	12	; add 12/216 to reset line spacing
	print	CR
	print	LF
	print	escape ; RESET PRINTER, RESTORE REGS
	print	'2'
	ENDIF
; FOR CITOH MAKE SURE BIDIRECTIONAL PRINTING IS RESTORED
	IF	CITOH
	print	escape
	print	'A'
	print	escape

	IF	NEC
	print	']'
	ELSE
	print	'<'
	ENDIF

	ENDIF

	IF	OTHER
	ORLINE
	ENDIF
edone:	mov	ax,50h	; Set end of PrtSc indication
	mov	ds,ax	; OK to come back and do again
	mov	si,0
	pop	ax
	mov	[si],al
exit:	pop	bp
	pop	si	; restore regs and return to caller
	pop	di
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	pop	ds
	iret		; were an interrupt routine so we IRET

; START OF LARGE PRINT ROUTINE
; +-------------+
; | ^ ^ 	|  This time we scan from 199,0 to 0,0
; | ^ ^ 	|  and go across
; | ^ ^ 	|  o  o    x  x    o  x    x  x   These are representations
; | | | 	|  o  o    o  o    x  o    x  x   of one color dot.
; +-------------+    0	     1	     2	     3	   Palettes

main2:	mov	dh,80	; we have 80 colunms x 25 lines here
	mov	cs:wheresi,3ef0h ; si is our index
	mov	si,cs:wheresi
main2a: mov	cx,64h	; 100
mloop2: mov	al,[si] ; idea is to get a byte starting at screen BOTTOM
	IF	BIT0
	call	reverse ; Bits have to be reversed on wire 0 type
	ENDIF
	call	send	; send it since these resemble bit plot bytes
	cmp	cs:mode640,TRUE
	jz	ml10
	call	flipflop ; then reverse(sort of) this byte and send it
ml10:
	call	send	; again.
	mov	cs:gowait,si ; store SI for next EVEN raster line
	sub	si,2000h ; subtract 2000h for the next ODD raster line
	mov	al,[si] ; and do the same here
	IF	BIT0
	call	reverse
	ENDIF
	call	send
	cmp	cs:mode640,TRUE
	jz	ml20
	call	flipflop
ml20:
	call	send
	mov	si,cs:gowait ; get back the EVEN line
	sub	si,80	; advance UP the screen one line (say 199,0 to 197,0)
	loop	mloop2	; and do this 100 times
	call	lfcr	; finished with one line we send normal line-end
	call	break?	; check for an ESC if we want to abort
	or	al,al	; clear flags
	dec	dh	; DH is our line counter,
	cmp	dh,0	; when it goes 0 we're done.
	jz	tof	; so we'll try to reset Top of Form and exit
	inc	cs:wheresi	; else go to the next byte location
	mov	si,cs:wheresi	; store
	jmp	main2a		; and do again
tof:	mov	cx,19	; tof restores page to 11 inches from where it started
tofl:	print	13	; send a bunch of cr's and lf's
	print	10
	call	break?	; check for early exit
	loop	tofl	; on and on.
; This restores the EPSON to 6 lines per inch
; ESC @ = Restore all settings to default
	IF	EPSON
	print	escape
	print	'@'
	ENDIF
;	IF	CITOH	; No equivalent to Epson ESC @
;	MOV	AL,escape ; for CITOH
;	CALL	SEND2	; so just reset line feed pitch
;	MOV	AL,'A'  ; this is done by DONE anyway
;	CALL	SEND2	; so leave open if someone wants to patch
;	ENDIF
	IF	OTHER
	ORESET
	ENDIF
	jmp	done	; clean up and back to caller.
start	endp
send2	proc	near	; BIOS routine to print the character in AL
	push	ax
	mov	ah,00h	; 0=print, 1=initialize port, 2=read status to AH
	push	dx
	mov	dx,0	; DX specifies printer 0 (LPT1:)
	int	17h	; BIOS used instead of DOS because DOS sends
	pop	dx	; CR/LF's in the middle of the bit-plots
	test	ah,29h	; check for timeout or errors or out-of-paper
	pop	ax
	jnz	erret
	ret
erret:	mov	ax,00ffh ; Flag for printer foulup
	mov	sp,bp
	push	ax
	jmp	edone	; special abort
send2	endp

; EPSON bit plots operate at 480 or 960 dots across the page
; called by ESC K 'low byte';'high byte'
;  i.e.  300 dots would be 256+44 or 012CH
;  This is sent to the EPSON as --> 1b 4b 2c 01
;		or in decimal	--> 27 75 44 1

indent	proc	near
	push	cx	; 13 spaces in to center
	IF	EPSON
	mov	cx,13	; PICTURE ( we've got 320 dots and 480 to work with
inlop:	print	20h	; 480-320=160 / 6 dots per char. = 26.67 extra
			; so indent the picture 13 spaces to center
	loop	inlop
; ESC K 64 1  = 256+64=320 bit plot type bytes on the way
	cmp	cs:mode640,TRUE
	jz	ind10
	print	escape	; SEQUENCE TO SET UP 320
			; BIT PLOTS IN 480 MODE
	print	'K'	; OF MX-80
			; This is the set-up for the small print
	print	64
	print	1
	jmp	short indend

ind10:
	print	escape
	print	'L'     ; 640 dots in 960 mode
	print	128
	print	2

indend:
	ENDIF
	IF	CITOH
	print	escape	; ESC N = Pica pitch
	print	'N'
	cmp	cs:mode640,TRUE
	jz	ind10
	mov	cx,20	; PICTURE ( we've got 320 dots and 640 to work with )
inlop:	print	20h	; 640-320=320 / 8 dots per char. = 40 extra
			; so indent the picture 13 spaces to center
	loop	inlop
	jmp	ind20

ind10:
	print	escape
	print	'S'
	print	'0'
	print	'6'
	print	'4'
	print	'0'
	jmp	ind30
ind20:
; ESC S 0320 = 320 bit plot type bytes on the way
	print	escape	; SEQUENCE TO SET UP 320 BIT PLOTS IN 640 MODE
	print	'S'     ; OF CITOH  This is the set-up for the small print
	print	'0'     ; Would love to try to use all 640 bits here
	print	'3'
	print	'2'
	print	'0'

ind30:
	ENDIF
	IF	OTHER
	BP1
	ENDIF
	pop	cx
	ret
indent	endp
; This is indent for LARGE print
; This time we have 400 bit plots to send (200 lines x 2)
; 480-400=80 / 6 = 13.3 extra
indent2 proc	near
	push	cx
	IF	EPSON
	mov	cx,6	; so indent 6 character type spaces
inlop2: print	20h
	loop	inlop2
; ESC 27 K 144 1 = 256+144=400 bit-plots
	print	escape
	print	'K'
	print	144
	print	1
	ENDIF
; 640-400=240 / 8 = 30 EXTRA characters
	IF	CITOH
	print	escape ; ESC N = Pica pitch
	print	'N'
	mov	cx,15	; PICTURE ( we've got 400 dots and 640 to work with
inlop2: print	20h	; 640-400 / 8 dots per char. = 30 extra so indent
			; the picture 15 spaces to center
	loop	inlop2
; ESC S 0400 = 400 bit plot type bytes on the way
	print	escape	; SEQUENCE TO SET UP 400 BIT PLOTS IN 640 MODE
	print	'S'     ; OF CITOH
	print	'0'
	print	'4'
	print	'0'
	print	'0'
	ENDIF
	IF	OTHER
	BP2
	ENDIF
	pop	cx
	ret
indent2 endp

m640	proc	near
m6ain:	mov	cx,80	; 80 x 4 = 320 dots.
m6loop: mov	dl,80h ; 10000000b
	call	tst4	; see if this comes back <> 0
	mov	al,ah	; we are testing bit patterns for one screen byte
	call	send	; don't send to printer unless something to send
	mov	dl,40h	; 01000000b
	call	tst4	; each byte is 4 dots
	mov	al,ah	; so we test for each dot in a byte
	call	send	; send sets PTFLAG if there is a dot on the line
	mov	dl,20h	; 00100000b
	call	tst4	; then resets to start of line and starts printing
	mov	al,ah	; AL is the bit plot byte being built
	call	send
	mov	dl,10h	; 00010000b
	call	tst4	; TST4 scans down 8 screen dot lines each time called
	mov	al,ah
	call	send
	mov	dl,08h ; 00001000b
	call	tst4	; see if this comes back <> 0
	mov	al,ah	; we are testing bit patterns for one screen byte
	call	send	; don't send to printer unless something to send
	mov	dl,04h	; 00000100b
	call	tst4	; each byte is 4 dots
	mov	al,ah	; so we test for each dot in a byte
	call	send	; send sets PTFLAG if there is a dot on the line
	mov	dl,02h	; 00000010b
	call	tst4	; then resets to start of line and starts printing
	mov	al,ah	; AL is the bit plot byte being built
	call	send
	mov	dl,01h	; 00000001b
	call	tst4	; TST4 scans down 8 screen dot lines each time called
	mov	al,ah
	call	send
	loop	m6loop	; 80 bytes make 320 dots
	call	lfcr	; this is a good old regular line feed/carriage return
	call	break?	; see if someone hit ESC key. If so take early exit
	or	al,al
	dec	dh	; DL is line counter
	cmp	dh,0	; when it goes 0 we're through
	jz	d6one	; reset everything and do an IRET
	mov	ax,ds	; otherwise bump the SEGMENT reg so that location
	add	ax,14h	; 0 is the start of the next line
	mov	ds,ax	; X'140' = 320
	jmp	m6ain	; and do this 80 times (80x4=320)
d6one:	mov	ax,0
	jmp	done1

m640	endp

tst4	proc	near	;  This routine builds ONE bit plot byte
	mov	ax,80	;  by testing a dot with the mask sent
	sub	ax,cx	;  from MLOOP.
	mov	si,ax	;  First it does the ODD row then the EVEN
	mov	ah,0	;  since alternate lines are offset 2000h
	mov	al,[si] ;  from each other in memory.
	and	al,dl	;  DL has the mask
	cmp	al,0	;  SI the location
	jz	no7	;  AL the memory byte
	call	set7	;  AH is the byte being built
no7:	add	si,80	; +80 gets us from say 0,0 to 2,0
	mov	al,[si] ; get the memory byte ( 4 dots )
	and	al,dl	; get rid of dots we aren't testing now
	cmp	al,0	; see if its COLOR 0
	jz	no5	; if yes, go on
	call	set5	; otherwise set that bit
no5:	add	si,80	; continue 7 5 3 1
	mov	al,[si]
	and	al,dl
	cmp	al,0
	jz	no3
	call	set3
no3:	add	si,80
	mov	al,[si]
	and	al,dl
	cmp	al,0
	jz	no1
	call	set1
no1:	push	ax
	mov	ax,80
	sub	ax,cx	; CX counts our screen position
	add	ax,2000h ; add 2000h for the EVEN rows
	mov	si,ax	; with seg set to B800h we can use SI like an
	pop	ax	; array pointer ( AH has our byte so don't lose )
	mov	al,[si] ; and continue with the same idea for 6 4 2 0
	and	al,dl
	cmp	al,0
	jz	no6
	call	set6
no6:	add	si,80
	mov	al,[si]
	and	al,dl
	cmp	al,0
	jz	no4
	call	set4
no4:	add	si,80
	mov	al,[si]
	and	al,dl
	cmp	al,0
	jz	no2
	call	set2
no2:	add	si,80
	mov	al,[si]
	and	al,dl
	cmp	al,0
	jz	no0
	call	set0
no0:	ret
; where's my Z80 now
; reverse this table if your bit plots use bit 0 for the top wire
	IF	BIT7
set7:	or	ah,80h	; top wire - bit 7
	ret
set6:	or	ah,40h	; bit 6
	ret
set5:	or	ah,20h	; bit 5
	ret
set4:	or	ah,10h	; bit 4
	ret
set3:	or	ah,08h	; bit 3
	ret
set2:	or	ah,04h	; bit 2
	ret
set1:	or	ah,02h	; bit 1
	ret
set0:	or	ah,01h	; bit 0
	ret
	ENDIF
	IF	BIT0
set7:	or	ah,01h	; top wire - bit 0
	ret
set6:	or	ah,02h	; bit 1
	ret
set5:	or	ah,04h	; bit 2
	ret
set4:	or	ah,08h	; bit 3
	ret
set3:	or	ah,10h	; bit 4
	ret
set2:	or	ah,20h	; bit 5
	ret
set1:	or	ah,40h	; bit 6
	ret
set0:	or	ah,80h	; bit 7
	ret
	ENDIF
tst4	endp
; This routine pre-scans a line to see if in fact there are any bit
; plots to send. The main routine will keep sending bytes here
; If a whole line of 0's are sent we avoid going through the
; set-up for bit-plot (i.e. slower movement) graphics when a CR/LF
; would take care of everything.
; If there IS something to send, PTFLAG is set, the current line
; position is set to 0, bit-plot is init., and bits are really sent to printer.
send	proc	near
	push	ax	; save the character
	push	ds	; DS saved cause it points to lines
	mov	ax,cs	; set seg for here
	mov	ds,ax	; This was some of my first stuff with the 8088
	pop	ax	; and I see some needless complexity here now
	mov	cs:dstor,ax	; but it works and if I mess with it
	cmp	cs:ptflag,0ffh	; its back to DEBUG.
	jnz	nosend	; if PTFLAG isn't FFh we are still scanning
	pop	ax	; else get the char. in AL and print it
	call	send2	; this is the real out to printer routine
	jmp	short noset ; restore DS and return
nosend: pop	ax	; This is the SCAN routine
	cmp	al,0	; get the char. > test for 0 > if so reset and go back
	jz	noset
	mov	al,0ffh ; if <> 0
	mov	cs:ptflag,al ; set PTFLAG to go
	pop	ax	;DISCARD RETURN
	cmp	cs:oneor2,1 ; check which (small or LARGE)
	jnz	two	; indent 6 or 13 depending on which routine
	cmp	cs:mode640,TRUE
	jz	nos10
	call	indent	; indent also sets up bit-plot mode
	call	noset	; NOSET will restore DS to right pos.
	jmp	main	; and do the line for real.
nos10:
	call	indent
	call	noset
	jmp	m6ain
two:	call	indent2 ; init. for LARGE
	mov	si,cs:wheresi	; SI set back to start of line
	call	noset	; get right DS
	jmp	main2a	; back to beginning
noset:	push	ax	; routine to restore DS
	mov	ax,cs:dstor
	mov	ds,ax
	pop	ax
	ret
send	endp
lfcr	proc	near	; send a regular CR/LF combo
	print	13
	print	10
	mov	ax,0
	mov	cs:ptflag,al ; reset PTFLAG for next line
	mov	ax,cs:dstor  ; restore DS
	mov	ds,ax
	ret		; onward
lfcr	endp
break?	proc	near	; Test for early exit
	push	ax	; don't lose any regs. here
	push	dx
	mov	ah,01h	; call direct keyboard io (constat) by BIOS
	int	16h
	jnz	goback? ; if zero flag clear we have a character
bcont:	pop	dx	; no char. return
	pop	ax
	ret
goback?:
	mov	ah,0
	int	16h
	cmp	al,1bh	; ESC
	jz	back	; so go back, else return
	jmp	short bcont	; no ESC exit
back:	pop	dx	; ESC exit This doesn't check for Ctrl-Break
	pop	ax	; so if it is hit we save it for the caller to handle
	pop	ax	;DISCARD RETURN
	jmp	done	; and go back to orig. caller
break?	endp
flipflop	proc near	; This creates different combinations
	push	cx	; of a box of four bit-plot dots for one color dot.
	push	bx	; Don't lose any variables or loop counters
	push	ax
	mov	cl,2	; AL has present bit-plot finished byte
	mov	bx,0
	and	al,3	; 00000011b
	call	flip
	ror	bl,cl	; 11000000b
	pop	ax	; basically rotate bits around for
	push	ax	; o x
	call	r2	; x o	color 1
	and	al,3	; and
	call	flip	; o x
	ror	bl,cl	; o x	color 2
	pop	ax	; instead of
	push	ax	; x o
	call	r4	; x o	color 1
	and	al,3	; and
	call	flip	; o x
	ror	bl,cl	; o x	color 2
	pop	ax	; which aren't to convincing as
	call	r6	; two different patterns
	and	al,3
	call	flip
	ror	bl,cl
	mov	al,bl
	pop	bx
	pop	cx
	ret
r6:	ror	al,cl
r4:	ror	al,cl
r2:	ror	al,cl
	ret
flip:	cmp	al,3	; make sure there are two dots for color 1 and 2
	jnz	flip2
	or	bl,3
flip2:	cmp	al,2
	jnz	flip3
	or	bl,1
flip3:	cmp	al,1
	jnz	flip4
	or	bl,1
flip4:	ret
flipflop	endp
reverse proc	near	; take AL and make bit 0 bit 7 , 1 - 6, etc
	push	dx	; Save our counters and masks
	push	cx
	mov	dl,01h	; 00000001B
	mov	dh,80h	; 10000000B
	mov	ah,00h	; start out blank
	mov	cx,8	; set counter for 8 times through
rev1:	test	al,dl	; see if bit is set
	jz	rev2	; if not skip next step
	or	ah,dh	; else set bit in AH
rev2:	shl	dl,1	; shift left test bit
	shr	dh,1	; shift right mask bit (pad other bits with 0)
	loop	rev1	; do this 8 times
	mov	al,ah	; and we have a reversed character.
	pop	cx	; get back these
	pop	dx
	ret		; and back to caller
reverse endp
last	dw	0
buffer	db	'          GRAFTRAX.COM v3.4',10,13
	if	EPSON
	db	'EPSON/IBM GRAFTRAX(tm) Screen Printer.',13,10
	else
	db	'       Graphics Screen Printer',13,10
	endif
	db	'   Left Shift PrtSc = LARGE GRAPHICS',13,10
	db	'  Right Shift PrtSc = small graphics',13,10
	db	' Text mode uses regular print routine.',13,10
	db	'   ESCape will exit GRAPHICS print.',13,10,'$'
werehere db	' ** GRAF.COM is already resident **',13,10,10
	db	' There is no need to reinstall.',13,10,'$'
initr	proc	far
initial:
	mov	ax,0	; get addr of
	mov	ds,ax	; print screen routine
	mov	si,14h	; in rom
	mov	ax,[si] ; from interrupt table in ram
	inc	si
	inc	si
	mov	dx,[si]

	mov	word ptr cs:old_print_routine,ax
	mov	word ptr cs:old_print_routine+2,dx
	mov	cx,offset last-offset start
	mov	di,ax
	mov	si,offset start
	cmp	dx,0efffh	; if routine points to ROM, ours is not it.
	ja	initok
	cmp	si,di
	jne	initok		; if start location not same, it can't be ours.

	; otherwise check to see if this routine is

	mov	es,dx		; already in memory, and don't reinstall if so.
	mov	ax,cs
	mov	ds,ax
	repe	cmpsb


	or	cx,cx		; cx=0 means there is a copy of this at the
	jnz	initok		; other address.
	mov	dx,offset werehere
	mov	ah,9		; so we print a message and
	int	21h		; abort.
	int	20h
initok:

;	 mov	 ds,ax
;	 mov	 al,0f1h ; move it to
;	 mov	 ah,25h
;	 int	 21h	 ; int f1h described in tech. manual as unused vector
	mov	ax,cs	; reset int 5
	mov	ds,ax	; to point to
	mov	ax,offset start ; this routine
	mov	dx,ax
	mov	al,5
	mov	ah,25h	; dos routine to reset int. vector
	int	21h
	mov	ax,offset buffer
	mov	dx,ax
	mov	ah,9
	int	21h	; print greeting
	mov	ax,3000h  ; get dos version
	int	21h
	or	al,al
	jz	dos1
	mov	ax,offset last
	mov	cx,16
	xor	dx,dx
	div	cx
	inc	ax	; make number of paragraphs
	mov	dx,ax
	mov	al,0	; exit code
	mov	ah,31h	; terminate process, keep resident
	int	21h

dos1:
	mov	dx,offset last	; last address here
	inc	dx
	int	27h	; terminate but stay resident
initr	endp
cseg	ends
	end	init

