	PAGE  60,132
	TITLE	GRAFTRAX SCREEN PRINTER, V 3.4
; OKIGRAF.COM  10/29/84
; ****!****!****!****!****!****!****!****!****!****!
; Special version for Okidata ML93,92 printers,    !
; and others with 7-bit graphics.		   !
; ****!****!****!****!****!****!****!****!****!****!
; 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) 661-1241 office, (713) 464-6737, home.
;
;    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 IBM(tm) MacroAssembler(tm),
;     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/29/84 - Program goes to, god forgive me, two separate versions
;		to cope with printers like the Oki and IDS with their 7-bit
;		graphics and alternate calling conventions.
;      11/07/84 - Routine added to prevent reinstalling routine if already
;		resident.
;
;	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 para
;		boundary of 320 bytes '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	FALSE
OKI	EQU	TRUE

; 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	FALSE
BIT0	EQU	TRUE

	; BIT7 is TRUE for EPSON
	; BIT0 is TRUE for OKI

print	macro	char
	mov	al,char
	call	SEND2
	endm

; **NOTE** This section applies to the 8bit versions, NOT implemented for
; 7bit (Oki).  Use this as a reference to modify for other 7bit, like IDS
; *****************************
; *  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	65
	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	75 ; 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	75
	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		; variables during routine
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	; runs with interrupts enabled.
	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
	cmp	[si],al
	jnz	nxts	; if not we're go for routine
	jmp	exit	; otherwise go back home.
nxts:	mov	[si],al ; if we're go don't let us start again until done.
	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.
			; used to use  int f1h.
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,29	; 29 lines of graphic dots at 7 dots per line
	mov	ax,0b800h ; stored in DX, last line has 4 extra dots
	mov	ds,ax	;SET UP FOR SCREEN PEEK
	xor	bx,bx	; segment indexing dropped in 7bit version, bx instead
; Printer setup section to change line spacing to 7/72" for continuous dots
; ** 7 bit **

	IF	EPSON
	print	escape
	print	'3'     ; A
	print	21	; 7
	ENDIF

	IF	OKI
	print	30	; set to 10 cpi
	print	3	; set to graphics mode
	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

	IF	OKI
	print	3	; for 640x200, get out of graphics,
	print	2
	print	28	; set up for 12 cpi
	print	3	; back to graphics
	ENDIF

	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
	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
	IF	EPSON
	call	lfcr	; this is a good old regular line feed/carriage return
	ENDIF
	IF	OKI
	call	lfcroki
	ENDIF
	call	break?	; someone hit ESC key? so take early exit
	dec	dh	; DL is line counter
	or	dh,dh	; when it goes 0 we're through
	jz	done	; reset everything and do an IRET
	test	dh,1	; otherwise check counter for odd/even
	jz	mloop10 ; and adjust index (bx) accordingly
	sub	bx,7872
	jmp	short mloop20
mloop10:
	add	bx,8432
mloop20:
	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

	IF	OKI
	print	3	; graphics escape code
	print	2	; exit graphics mode
	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,92	; we have 80 colunms x 25 lines here
	mov	dl,0
	mov	cs:wheresi,3ef0h ; si is our index
	mov	si,cs:wheresi
main2a: mov	cx,64h	; 100
mloop2:
	call	m2_calc_send

	mov	cs:gowait,si ; store SI for next EVEN raster line
	sub	si,2000h ; subtract 2000h for the next ODD raster line

	call	m2_calc_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

	IF	EPSON
	call	lfcr
	ENDIF
	IF	OKI
	call	lfcroki ; finished with one line we send normal line-end
	ENDIF
	call	break?	; check for an ESC if we want to abort
	dec	dh	; DH is our line counter,
	or	dh,dh	; when it goes 0 we're done.
	jz	tof	; so we'll try to reset Top of Form and exit
	inc	dl
	cmp	dl,7
	je	ml_no_inc

	inc	cs:wheresi	; else go to the next byte location
				; except if at last circuit
ml_no_inc:
	and	dl,7		; 0-7 only, 8 byte circuit
	mov	si,cs:wheresi	; store
	jmp	main2a		; and do again
tof:
	mov	cx,19	; tof restores page to 11 inches from where it started
	IF	OKI
	print	3	; exit graphics mode
	print	2
	call	break?	; check for no do.
	print	12	; form feed if ok.
	jmp	short tofl_end
	ENDIF
tofl:
	print	CR	; send a bunch of cr's and lf's
	print	LF
	call	break?	; check for early exit
	loop	tofl	; and so on.
; This restores the EPSON to 6 lines per inch
; ESC @ = Restore all settings to default
	IF	EPSON
	print	escape
	print	64   ; '@'
	ENDIF
tofl_end:
	jmp	done	; clean up and back to caller.
start	endp

m2_calc_send	proc	near
	mov	al,[si] ; idea is to get a byte starting at screen BOTTOM
	mov	ah,[si-1] ; can't use a mov word instruction, since
	or	dl,dl	; bits are read by least,most significant
	jz	m2on	; which would be backwards in this case.
	cmp	dl,7	; This down to m2on basically aligns the screen
	je	m2on	; dots in a register depending on where we are in
	cmp	dl,4	; an 8 step cycle.  With 7 bit graphics and an 8 bit
	ja	m2o50	; byte width, it takes 8 steps for things to repeat.
	shr	ax,1	; This involves lots of shifts and conditionals
	shr	ax,1	; and this routine attemps to do this as efficiently
	cmp	dl,3	; as possible.
	jb	m2on	; ** one screen byte looks like this:
	shr	ax,1	; | 7 6 | 5 4 | 3 2 | 1 0 |  #'s are bits, two make up
	shr	ax,1	;			     a color pixel 00 01 10 11
	jmp	short m2on ; this is sent twice for 620x200, or twice with some
			; twidling to simulate color, and reversing of the
m2o50:			; bits for wire 0 type printers, (Oki)
	shl	ax,1	; This results in an image formed sideways with the
	shl	ax,1	; bottom left corner of the screen in the top left
	mov	al,ah	; corner on a page of paper.

m2on:
	mov	bh,al	; copy pattern for second
	test	dl,1	; check for odd
	jz	m2on10
	shl	al,1	; if so, bump bit7

m2on10:
	call	reverse ; Bits have to be reversed on wire 0 type
	and	al,7fh	; turn off bit7
	cmp	dh,1	; last byte only 2 bits
	jnz	m2on15
	and	al,3	; so erase any extras.

m2on15:
	call	send	; send it since these resemble bit plot bytes
	cmp	cs:mode640,TRUE
	jz	ml10
	mov	al,bh
	call	flipflop ; then reverse(sort of) this byte and send it
	test	dl,1	; check odd
	jz	m2on20
	shl	al,1	;  align if so

m2on20:
	call	reverse ; bit 7 becomes bit 0, etc.
	and	al,7fh
	cmp	dh,1
	jnz	ml10
	and	al,3
ml10:
	call	send	; again.

	ret

m2_calc_send	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	75	; 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	OKI
	cmp	cs:mode640,TRUE
	jz	ind10
	mov	cx,80	; PICTURE ( we've got 320 dots and 480 to work with
inlop:	print	0	; 480-320=160
			; 80 graphics spaces to center
	loop	inlop

ind10:			; oki doesn't have the resolution for 640 dots on 8x11
	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	75
	print	144
	print	1
	ENDIF


	IF	OKI
	mov	cx,40	; PICTURE ( we've got 400 dots and 480 to work with
inlop2: print	0	; 480-400 = 80 so print 40 graphics spaces to center
	loop	inlop2

; ESC S 0400 = 400 bit plot type bytes on the way
	ENDIF

	pop	cx
	ret

indent2 endp

m640	proc	near
m6ain:	mov	cx,72	; 72 x 8 = 576 dots. OKI will lose last 10%
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
	IF	EPSON
	call	lfcr	; this is a good old regular line feed/carriage return
	ENDIF
	IF	OKI
	call	lfcroki
	ENDIF
	call	break?	; see if someone hit ESC key. If so take early exit
	dec	dh	; DL is line counter
	or	dh,dh	; when it goes 0 we're through
	jz	d6one	; reset everything and do an IRET
	test	dh,1	; otherwise check counter for odd/even
	jz	m6oop10 ; and adjust index (bx) accordingly
	sub	bx,7872
	jmp	short m6oop20
m6oop10:
	add	bx,8432
m6oop20:
	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
	cmp	cs:mode640,TRUE
	jne	tst10
	mov	ax,72
tst10:
	sub	ax,cx	;  from MLOOP.
	mov	si,ax	;  si is added to an index value stored in
	add	si,bx	;  bx, and then moved down the screen line by line.
	xor	ah,ah	;  Alternate lines are offset 2000h in segment b800h
	cmp	si,8192
	jb	test_even ; if the current address is over 8192, the line
	jmp	test_odd  ; start location is odd
test_even:
	mov	al,[si] ;  from each other in memory. |-----------------|screen
	and	al,dl	;  DL has the mask	      | 0,0 0,1 0,2 etc |addr.
	or	al,al	;  SI the location	      | 2,0 2,1 2,2	|0
	jz	no7	;  AL the memory byte	      |-----------------|
	IF	BIT7	;			      |-----------------|
	or	ah,80h	;  AH is the byte being built | 1,0 1,1 1,2 etc |addr.
	ELSE		;			      | 3,0 3,1 3,2	|8192
	or	ah,01h	;  AH is the byte being built |-----------------|2000h
	ENDIF
no7:	add	si,8192 ;  get to next line down (odd lines are offset 2000h)
	mov	al,[si] ; get the memory byte ( 4 dots )
	and	al,dl	; get rid of dots we aren't testing now
	or	al,al	; see if its COLOR 0
	jz	no6	; if yes, go on
	IF	BIT7
	or	ah,40h
	ELSE
	or	ah,02h
	ENDIF
no6:	sub	si,8112
	mov	al,[si]
	and	al,dl
	or	al,al
	jz	no5
	IF	BIT7
	or	ah,20h
	ELSE
	or	ah,04h
	ENDIF
no5:
	cmp	dh,1	; if dh=1 we are on the last line and don't need
	jne	no5a	; the bottom four bits, so take early exit
	ret		;
no5a:
	add	si,8192
	mov	al,[si]
	and	al,dl
	or	al,al
	jz	no4
	IF	BIT7
	or	ah,10h
	ELSE
	or	ah,08h
	ENDIF
no4:
	sub	si,8112
	mov	al,[si]
	and	al,dl
	or	al,al
	jz	no3
	IF	BIT7
	or	ah,08h
	ELSE
	or	ah,10h
	ENDIF
no3:	add	si,8192
	mov	al,[si]
	and	al,dl
	or	al,al
	jz	no2
	IF	BIT7
	or	ah,04h
	ELSE
	or	ah,20h
	ENDIF
no2:	sub	si,8112
	mov	al,[si]
	and	al,dl
	or	al,al
	jz	no1
	IF	BIT7
	or	ah,02h
	ELSE
	or	ah,40h
	ENDIF
no1:

no0:	ret

test_odd:
	mov	al,[si] ;  from each other in memory. |-----------------|screen
	and	al,dl	;  DL has the mask	      | 0,0 0,1 0,2 etc |addr.
	or	al,al	;  SI the location	      | 2,0 2,1 2,2	|0
	jz	od7	;  AL the memory byte	      |-----------------|
	IF	BIT7	;			      |-----------------|
	or	ah,80h	;  AH is the byte being built | 1,0 1,1 1,2 etc |addr.
	ELSE		;			      | 3,0 3,1 3,2	|8192
	or	ah,01h	;  AH is the byte being built |-----------------|2000h
	ENDIF
od7:	sub	si,8112 ;  get to next line down (odd lines are offset 2000h)
	mov	al,[si] ; get the memory byte ( 4 dots )
	and	al,dl	; get rid of dots we aren't testing now
	or	al,al	; see if its COLOR 0
	jz	od6	; if yes, go on
	IF	BIT7
	or	ah,40h
	ELSE
	or	ah,02h
	ENDIF
od6:	add	si,8192
	mov	al,[si]
	and	al,dl
	or	al,al
	jz	od5
	IF	BIT7
	or	ah,20h
	ELSE
	or	ah,04h
	ENDIF
od5:
	cmp	dh,1	; if dh=1 we are on the last line and don't need
	jne	od5a	; the bottom four bits, so take early exit
	ret		;
od5a:
	sub	si,8112
	mov	al,[si]
	and	al,dl
	or	al,al
	jz	od4
	IF	BIT7
	or	ah,10h
	ELSE
	or	ah,08h
	ENDIF
od4:
	add	si,8192
	mov	al,[si]
	and	al,dl
	or	al,al
	jz	od3
	IF	BIT7
	or	ah,08h
	ELSE
	or	ah,10h
	ENDIF
od3:	sub	si,8112
	mov	al,[si]
	and	al,dl
	or	al,al
	jz	od2
	IF	BIT7
	or	ah,04h
	ELSE
	or	ah,20h
	ENDIF
od2:	add	si,8192
	mov	al,[si]
	and	al,dl
	or	al,al
	jz	od1
	IF	BIT7
	or	ah,02h
	ELSE
	or	ah,40h
	ENDIF
od1:
od0:	ret

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
	IF	OKI
	cmp	al,3	; a 3 is the OKI graphics escape code, so to send
	jnz	not_esc ; a graphics 3 you send it twice.
	push	ax
	call	send2
	pop	ax
not_esc:
	ENDIF
	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
	pop	ax	; discard return again (ml_calc_send)
	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

lfcroki proc	near
	print	3	; graphics escape code
	print	14	; advance one graphics line, return carriage
	xor	al,al
	mov	cs:ptflag,al	; restore ds no longer needed.
	ret

;	print	3
;	print	14	; oki graphics cr/lf

lfcroki 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	'         GRAF.COM v3.4',10,13
	if	OKI
	db	'OKIDATA ML92 ML93 (tm) Screen Printer.',13,10
	else
	db	'     Graphics Screen Printer v3.2',13,10
	endif
	db	'   Left Sh. PrtSc. = LARGE GRAPHICS',13,10
	db	'  Right Sh. PrtSc. = small graphics',13,10
	db	' Text screen uses regular ROM 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

