
********************************************************************
* Example source code for DEGAS Epson printer driver routine       *
* Written by Tom Hudson, Copyright 1985 Batteries Included         *
* Placed on Compuserve Information Service for public distribution *
* Not to be sold                                                   *
********************************************************************
* Inputs:							   *
* 	4(a7).w Driver command [0=reset, 1=print screen]	   *
*	6(a7).w Screen resolution [0=low, 1=medium, 2=high]	   *
*	8(a7).l Screen base address				   *
*	12(a7).l Color palette base address			   *
*	16(a7).l 1280-byte work area base address		   *
********************************************************************
* Outputs:							   *
*	d0.w Result code [0=OK, 1=error, 2=dump aborted]	   *
********************************************************************
* Notes:							   *
*								   *
* The code in the printer driver must be position-independent!     *
* Use BSR instead of JSR, BRA instead of JMP, etc.  The code must  *
* be written to the disk file as it will appear in memory (no      *
* load header, just raw 68000 object code).  The file should be    *
* 2000 bytes in length.  					   *
********************************************************************

.text

*******************************
* Epson MX-80 graphics dumper *
*******************************

_dumper:

move.l a0,d0		;save A0 in D0
move.l 16(a7),a0	;move work area address to A0
movem.l d0-a7,(a0)	;save all registers to work area
move.l 8(a7),a1		;get screen address in A1
move.l 12(a7),a2	;get color palette address in A2
tst.w 4(a7)		;is command byte zero?
bne process		;no, it's the real dump, go process it!

******************************
* reset & initialize printer *
******************************

init:

*********************
* BUILD PIXEL TABLE *
*********************

move.l #$ffffffff,1000(a0)	;this table is set up...
move.l #$55ff55ff,1004(a0)	;to contain the eight...
move.l #$55bb55ee,1008(a0)	;gray-scale levels for...
move.l #$aa55aa55,1012(a0)	;the printer.  They are...
move.l #$aa44aa11,1016(a0)	;simply eight different...
move.l #$aa00aa00,1020(a0)	;dot patterns.  These are...
move.l #$88002200,1024(a0)	;placed in the work area...
move.l #$00000000,1028(a0)	;for later use.

cmp.w #0,6(a7)		;check rez -- low?
beq lowinit		;yes, go init for low rez!

***********************
* reset phase counter *
***********************

move.b #128,64(a0)	;set up one-bit pixel mask for high & medium rez,
bra fininit		;and finish initialization.

lowinit:
move.b #192,64(a0)	;low rez gets two-bit pixel mask.

*******************
* set init string *
*******************

fininit:
move.l #$1b401b41,100(a0)	;tell printer to reset itself and...
move.w #$080d,104(a0)		;set linefeed for graphics.

*******************************
* print the string to printer *
*******************************

move.l #6,d2		;we want to print 6 bytes...
lea 100(a0),a3		;starting at 100(a0)...
bsr printit		;print them!

*********************************
* clear out printer data buffer *
*********************************

lea 100(a0),a3		;point to start of print buffer,
move.w #210,d1		;clear out 210 * 4 bytes...
clpln:
move.l #0,(a3)+		;clear 4 bytes of buffer, increment pointer
subq #1,d1		;1 less group of 4 bytes
bne clpln		;loop back if more
tst.w d0		;did print work OK?
bpl exit		;yes, normal exit!

******************************
* exit w/error condition (0) *
******************************

error:
movem.l (a0),d0-a7	;restore the register set,
move.l d0,a0		;replacing A0's old contents from D0
move.w #0,d0		;zero D0 (error condition)
rts			;and back to DEGAS!

*************************************
* exit the subroutine, no error (1) *
*************************************

exit:
movem.l (a0),d0-a7	;put back all the registers,
move.l d0,a0		;and restore A0 properly,
move.w #1,d0		;set D0 to 1 (no error)
rts			;and go back to DEGAS!

******************************
* exit w/abort condition (2) *
******************************

abort:
movem.l (a0),d0-a7	;restore register set,
move.l d0,a0		;restore A0,
move.w #2,d0		;set D0 to 2 (print dump aborted)
rts			;and return to DEGAS.

******************************************
* process the pixels and send to printer *
******************************************

process:
cmp.w #0,6(a7)		;are we in low resolution?
beq lowproc		;yes, go process low rez screen dump!

cmp.w #1,6(a7)		;in medium resolution?
beq medproc		;yes, go process the sucker!

********************
* HI-REZ PROCESSOR *
********************

move.w #0,66(a0)	;Start at pixel X = 0
nx2:
lea 104(a0),a4		;point A4 to 5th byte of print buffer
move.w #399,68(a0)	;set Y counter to 399 pixels (range is 0-399)

*************
* GET PIXEL *
*************

nx2b:
bsr get2		;get pixel (color returned in D0)
move.b d0,d1		;move to D1
andi.l #$000000ff,d1	;mask off excess garbage in register
lsl.l #1,d1		;Multiply color number by 2,
add.l a2,d1		;add to color palette start address,
movea.l d1,a5		;and move to address register A5.
cmpi.w #$0777,(a5)	;is color white?
beq white		;you betcha!

***************
* black pixel *
***************

move.b (a4),d1		;get printer buffer data byte
or.b 64(a0),d1		;or with pixel mask
move.b d1,(a4)+		;and move it back to the next two bytes...
move.b d1,(a4)+		;of the printer data buffer.
bra np2			;go get next pixel!

***************
* white pixel *
***************

white:
addq.l #2,a4		;don't need to change any pixels if white!

np2:
subq.w #1,68(a0)	;move up 1 (Y coordinate)
bpl nx2b		;loop back if more Y coordinates

move.b 64(a0),d1	;get pixel mask from memory
lsr.b #1,d1		;and shift it right 1 bit,
move.b d1,64(a0)	;put back in RAM,
tst.b d1		;is it zero?
bne xl2			;no, we don't print yet!

move.b #128,64(a0)	;reset pixel mask to 128
bsr doprnt		;and print the line!
tst.w d0		;print OK?
bmi error		;no, error!

xl2:
bsr pabchk		;check for a print abort.
tst.w d0		;was it aborted?
bne abort		;yes!
addq.w #1,66(a0)	;increment X coordinate
cmp.w #640,66(a0)	;end of screen?
bne nx2			;no, loop back.
bra exit		;end of screen, do a normal exit!

************************
* MEDIUM-REZ PROCESSOR *
************************

medproc:
move.w #0,66(a0)	;zero out X coordinate
nx1:
lea 104(a0),a4		;point A4 to printer buffer 5th byte
move.w #199,68(a0)	;point to Y coordinate 199 (0-199 in medium rez)

*************
* GET PIXEL *
*************

nx1b:
bsr get1		;get medium-rez pixel
bsr ppix		;convert to gray-scale

subq.w #1,68(a0)	;next Y coordinate
bpl nx1b		;branch if more in Y column

move.b 64(a0),d1	;get pixel mask
lsr.b #1,d1		;shift right 1 bit
move.b d1,64(a0)	;put back in RAM
tst.b d1		;is mask = 0?
bne xl1			;no, process next Y column.

move.b #128,64(a0)	;reset pixel mask to 128
bsr doprnt		;dump the printer buffer to the printer
tst.w d0		;print OK?
bmi error		;no!

xl1:
bsr pabchk		;check for user abort
tst.w d0		;aborted?
bne abort		;yes!
addq.w #1,66(a0)	;advance to next X coordinate
cmp.w #640,66(a0)	;end of screen?
bne nx1			;no, loop back!
bra exit		;all done, do a normal exit.

*********************
* LOW-REZ PROCESSOR *
*********************

lowproc:
move.w #0,66(a0)	;zero pixel X
nx0:
lea 104(a0),a4		;point to 5th byte of printer buffer
move.w #199,68(a0)	;Y coordinate 199 (0-199 in low rez)

*************
* GET PIXEL *
*************

nx0b:
bsr get0		;get low-rez pixel
bsr ppix		;and convert to gray-scale in print line

subq.w #1,68(a0)	;next Y coordinate
bpl nx0b		;branch if more in Y column

move.b 64(a0),d1	;get pixel mask,
lsr.b #2,d1		;shift right 2 bits,
move.b d1,64(a0)	;put back in RAM
tst.b d1		;mask = 0?
bne xl0			;no, process next column!

move.b #192,64(a0)	;reset pixel mask
bsr doprnt		;and print a line
tst.w d0		;was print successful?
bmi error		;no, return an error!

xl0:
bsr pabchk		;check for print abort
tst.w d0		;was it aborted?
bne abort		;yes!
addq.w #1,66(a0)	;next X coordinate
cmp.w #320,66(a0)	;screen dump done?
bne nx0			;no, loop for more!
bra exit		;all done, use normal exit!

*****************************
*  print string to printer  *
*---------------------------*
* input:                    *
* d2.l = # bytes to print   *
* A3.l = buffer address     *
*---------------------------*
* returns:                  *
* d0.w = -1: error!         *
* d0.w =  0: OK!            *
*****************************

printit:
move.l #20000,d1	;timeout retry count

statloop:
link a5,#-20		;reserve stack space
move.l a0,(sp)		;save used registers on stack
move.l a1,-(sp)
move.l a2,-(sp)
move.l d1,-(sp)
move.l d2,-(sp)
link a6,#-4		;reserve 4 bytes on stack
move.w #0,(sp)		;indicate printer
move.w #8,-(sp)		;output status command
trap #13		;call OS to see if printer ready
unlk a6			;unreserve stack
move.l (sp)+,d2		;restore my registers
move.l (sp)+,d1
move.l (sp)+,a2
move.l (sp)+,a1
move.l (sp)+,a0
unlk a5			;unreserve stack
tst.l d0		;printer ready for characters?
bmi prtrdy		;yes!
subq.l #1,d1		;printer not ready, try again (20000 times!)
bpl statloop		;loop for retry if more allowed!
move.w #-1,d0		;printer timed out, return error (-1 in D0)
rts			;exit back to caller!

prtrdy:
link a5,#-20		;reserve stack,
move.l a0,(sp)		;save working registers
move.l a1,-(sp)
move.l a2,-(sp)
move.l d1,-(sp)
move.l d2,-(sp)
link a6,#-6		;reserve 6 bytes on stack
move.b (a3),d0		;move byte from buffer to d0
andi.w #$00FF,d0	;be sure it's just a 0-255 byte
move.w d0,(sp)		;move byte (in word size) to stack
move.w #0,-(sp)		;indicate device 0 (printer)
move.w #3,-(sp)		;and character output function
trap #13		;call OS to print the character
unlk a6			;unreserve stack space
move.l (sp)+,d2		;restore my registers
move.l (sp)+,d1
move.l (sp)+,a2
move.l (sp)+,a1
move.l (sp)+,a0
unlk a5			;get back stack space
addq.l #1,a3		;point to next byte in printer buffer
subq.w #1,d2		;1 less byte to print
bne printit		;loop back if more to print
move.w #0,d0		;indicate successful print operation (0 in D0)
rts			;and return to caller!

****************************
* Check for UNDO key abort *
*--------------------------*
* input:                   *
* none                     *
*--------------------------*
* returns:                 *
* d0 = 1: ABORT!           *
* d0 = 0: OK!              *
****************************

pabchk:
link a5,#-20		;reserve stack space
move.l a0,(sp)		;save my working registers
move.l a1,-(sp)
move.l a2,-(sp)
move.l d1,-(sp)
move.l d2,-(sp)
link a6,#-4		;reserve stack space
move.w #2,(sp)		;indicate device #2 (keyboard)
move.w #01,-(sp)	;see if key was pressed
trap #13		;call OS
unlk a6			;free up stack space
tst.w d0		;was a key pressed?
beq nobort		;no!
link a6,#-4		;yes, reserve stack space,
move.w #2,(sp)		;indicate device #2 (keyboard)
move.w #2,-(sp)		;get character command
trap #13		;call OS
unlk a6			;free up stack space
cmp.l #$00610000,d0	;was it UNDO key?
bne nobort		;no!
move.w #1,d0		;set abort flag!
abcxit:
move.l (sp)+,d2		;restore my working registers
move.l (sp)+,d1
move.l (sp)+,a2
move.l (sp)+,a1
move.l (sp)+,a0
unlk a5			;free stack space
rts			;and return to caller

nobort:
move.w #0,d0		;clear abort flag
bra abcxit		;and go to abort check exit!

*************************************
* Get a high-resolution pixel value *
*************************************

get2:
link a5,#-20		;reserve stack space
move.l a0,(sp)		;save working registers
move.l a1,-(sp)
move.l a2,-(sp)
move.l d1,-(sp)
move.l d2,-(sp)

move.w 68(a0),d0	;get pixel Y value
and.l #$0000ffff,d0	;mask it off to eliminate high garbage
mulu #80,d0		;multiply by 80 (bytes per scan line)
adda.l d0,a1		;add to screen base address
move.w 66(a0),d1	;get pixel X value
move.w d1,d2		;duplicate in d2
lsr.w #3,d1		;divide by 8,
adda.w d1,a1		;add to pixel group address
move.b (a1),d0		;get that byte!
and.l #7,d2		;now mask to get pixel # in group
move.b #$80,d1		;set up a bit mask
lsr.b d2,d1		;shift mask right d2 times
and.b d1,d0		;and it with the pixel group - we've got pixel!
beq zero2		;it's a zero pixel!

move.w #1,d0		;it's a 1 pixel!
bra getx2		;now leave routine

zero2:
move.w #0,d0		;it's a zero pixel!

getx2:
move.l (sp)+,d2		;restore my working registers
move.l (sp)+,d1
move.l (sp)+,a2
move.l (sp)+,a1
move.l (sp)+,a0
unlk a5			;free up stack space
rts			;and return to caller

***************************************
* Get a medium-resolution pixel value *
***************************************

get1:
link a5,#-24		;set up stack frame
move.l a0,(sp)		;save working registers
move.l a1,-(sp)
move.l a2,-(sp)
move.l d1,-(sp)
move.l d2,-(sp)
move.l d3,-(sp)

move.w #0,d0		;zero out work register
move.w 68(a0),d3	;get pixel Y value
and.l #$0000ffff,d3	;strip off garbage in register
mulu #160,d3		;multiply by 160 (160 bytes per line in medium rez)
adda.l d3,a1		;add to screen base address
move.w 66(a0),d1	;get pixel X value
move.w d1,d2		;duplicate in d2
lsr.w #2,d1		;divide X by 4
and.w #$00fc,d1		;and with $fc
adda.w d1,a1		;add to address for pixel group address

move.w (a1),d3		;get the pixel group in d3
and.l #15,d2		;find pixel number within group
move.w #$8000,d1	;set up a bit mask
lsr.w d2,d1		;shift mask right d2 times
and.w d1,d3		;mask off pixel!
beq bit1b		;if equal, low bit is zero, bypass next instruction.
move.w #1,d0		;if not equal, set low bit in result

bit1b:
move.w 2(a1),d3		;get next bit plane pixel group
and.w d1,d3		;mask off high pixel
beq getx1		;branch if it's zero
or.w #2,d0		;otherwise set the high bit in result

getx1:
move.l (sp)+,d3		;restore my working registers
move.l (sp)+,d2
move.l (sp)+,d1
move.l (sp)+,a2
move.l (sp)+,a1
move.l (sp)+,a0
unlk a5			;free up stack
rts			;and return to caller!

************************************
* Get a low-resolution pixel value *
************************************

get0:
link a5,#-24		;reserve stack space
move.l a0,(sp)		;save my working regs on stack
move.l a1,-(sp)
move.l a2,-(sp)
move.l d1,-(sp)
move.l d2,-(sp)
move.l d3,-(sp)

move.w #0,d0		;zero out return work register
move.w 68(a0),d3	;get pixel Y value
and.l #$0000ffff,d3	;mask off register garbage
mulu #160,d3		;multiply by 160 (bytes per line)
adda.l d3,a1		;add to screen base address
move.w 66(a0),d1	;get pixel X value
move.w d1,d2		;duplicate in d2
lsr.w #1,d1		;divide by 2
and.w #$00f8,d1		;mask it off
adda.w d1,a1		;and add for pixel group address

move.w (a1),d3		;get the pixel group!
and.l #15,d2		;get pixel number within group
move.w #$8000,d1	;set up a pixel mask
lsr.w d2,d1		;shift mask right d2 times
and.w d1,d3		;get the pixel!
beq bit0b		;if zero, try next bit plane
move.w #1,d0		;pixel on, set low bit in color number

bit0b:
move.w 2(a1),d3		;get next pixel group (next bit plane)
and.w d1,d3		;mask off pixel
beq bit0c		;branch if it's zero
or.w #2,d0		;pixel on, set next bit in color number

bit0c:
move.w 4(a1),d3		;get next pixel group (next bit plane)
and.w d1,d3		;mask off pixel
beq bit0d		;branch if it's zero
or.w #4,d0		;pixel on, set next bit in color number

bit0d:
move.w 6(a1),d3		;get final pixel group (bit plane 4)
and.w d1,d3		;mask off pixel
beq getx0		;branch if zero
or.w #8,d0		;pixel on, set final bit in color number

getx0:
move.l (sp)+,d3		;restore my working registers
move.l (sp)+,d2
move.l (sp)+,d1
move.l (sp)+,a2
move.l (sp)+,a1
move.l (sp)+,a0
unlk a5			;free up stack space
rts			;and return to caller!

**********************************************
* Set graphics control codes in print buffer *
* and print it!				     *
**********************************************

doprnt:
move.l #$1b4c2003,100(a0)	;set graphics start in print buffer
move.w #$0d0a,904(a0)		;set up CR/LF in print buffer
move.l #806,d2			;print 806 bytes
lea 100(a0),a3			;starting at 100(a0)
bsr printit			;print!

lea 100(a0),a3			;point to print buffer start
move.w #210,d1			;clear 210 X 4 bytes
clpln2:
move.l #0,(a3)+			;clear 4 bytes
subq #1,d1			;next group
bne clpln2			;branch if more groups
rts				;no more groups, return to caller

******************************
* CHANGE COLOR TO GRAY-SCALE *
* AND PUT IN PRINT LINE      *
******************************

ppix:
move.b d0,d1		;move color # to d1
andi.l #$000000ff,d1	;mask off garbage in register
lsl.l #1,d1		;multiply by 2
add.l a2,d1		;add to color palette base address
movea.l d1,a5		;put color address in d5
move.w (a5),d0		;get RGB color in d0
move.w d0,d2		;duplicate in d2
and.w #7,d2		;mask off blue component in d2
move.w d0,d1		;get color in d1
lsr.w #4,d1		;shift it right
and.w #7,d1		;mask off green component in d1
add.w d1,d2		;add it to d2
move.w d0,d1		;get color in d1 again
lsr.w #8,d1		;shift right
and.w #7,d1		;mask off red component in d1
add.w d1,d2		;add to d2 (now d2 = 0-21)
and.l #$000000ff,d2	;mask off garbage in register
divu #3,d2		;divide total colors by 3 (for 0-7 result)
lsl.w #2,d2		;multiply by 4 for index into gray scales
lea 0(a0,d2.w),a5	;point a5 to work area base + gray scale index
adda.l #1000,a5		;increment by 1000 to point to gray scale table

* handle pixel color *

move.b (a5),d1		;get gray-scale byte
and.b 64(a0),d1		;mask with printer pixel mask
or.b (a4),d1		;or with printer buffer data
move.b d1,(a4)+		;put result back into printer buffer
move.b 1(a5),d1		;repeat for second printer pixel
and.b 64(a0),d1
or.b (a4),d1
move.b d1,(a4)+
move.b 2(a5),d1		;repeat for third printer pixel
and.b 64(a0),d1
or.b (a4),d1
move.b d1,(a4)+
move.b 3(a5),d1		;repeat for third printer pixel
and.b 64(a0),d1
or.b (a4),d1
move.b d1,(a4)+
rts			;all done, exit!

filler .ds.l 500	;filler to make sure we have 2000 bytes
