[comp.sys.amiga] mysteries of printer drivers LONG!

rico@oscvax.UUCP (10/28/87)

In the spirit of previous GURU oriented postings, here's another one
that I hope will help someone out there.   It's a printer driver
for the toshiba 3 in one printer in its Qume (best) mode.  I hadn't
ever seen the source for a printer driver posted before so hopefully
this will make a good example for everyone out there.  It's implemented
in Aztec C + assembler but porting it to Lattice + assembler should be
simple.  The rendering function is a *minimal* graphics output generating 
function, no attempt was made to optimize out unecessary print passes
etc.  But this could be easily added.

This code was developed by me here at the Ontario Science Centre as part
of my work (i.e. we needed this driver) and I've been authorized to
release it to the public domain.  You may slice dice or whatever.  Needless
to say there's no warranty etc. etc.  All usual disclaimers apply.


Noteworthy items:

   - Aztec C doesn't preserve a6 across function calls.  This is required
     in the printer driver code so special inline assembler was added
     to the render() and dospecial() functions.  Look out for this 
     Aztec users!

   - The toshiba is actually capable of printing 24 pixel rows at 360
     dots/inch in a print pass however the printer.device didn't seem
     to cope with this very well.  With these numbers in the printertag
     the printer device would simply call "case 5" (pre initialization)
     of the render function and then not send any other data.  No fuss,
     no muss, no data.  When I reduced the 360 to the next best print
     mode (180) all was well.  Sigh.  If anyone can shed some light on
     this I'd appreciate it.

   - I've stolen the debugging code from the dos handler that Matt Dillon
     recently posted and used this in my driver for debugging output. You
     folks out there should think about using it to get debugging output
     where you never dreamed it was possible.  The debugging package is a
     really terrific little subsytem.  Thanks Matt!

   - I've also stolen the flush program from the dos handler, you can use
     this for devoloping printer drivers as it will cause your Expunge
     code to be run.  The next time you use the printer device your code
     will be re-loaded, hence you can make recompile etc. and test your
     new driver without resorting to preferences trickery.  Not to mention
     testing your Expunge code...

   - Speaking of Expunge code, mine doesn't work when the debugging is
     enabled.  Works perfectly in normal use though... oh well :-(
     If I ever figure it out I'll post the fix.  Meanwhile if any of
     you figure it out...

   - If you're writing a printer driver do whatever it takes but get
     Carolyn's Cmd program.  This lets you easily "see" your driver's
     output which is a must for testing.  I used this for my debugging
     output before I installed Matt's code.  If you're checking the
     output by eye then it sometimes helps if there's readable diagnostics
     intermixed.   Thanks Carolyn!

   - To compile with debugging enabled just supply the -DDEBUG flag to
     "cc" and -EDEBUG to "as".  Lattice people will have to use whatever
     flags it is they use to predefine symbols in the compiler/assembler.

Enough babbling... here's the source + binary (uuencoded + shar'd)

: -------cut here-----------cut here------------cut here--------
: This is a shar archive.  Extract with sh, not csh.
: Extraction terminated by end of archive message.
: The rest of this file will extract:
: Makefile data.c debug.c dospecial.c flush.c flush.uu init.asm printertag.asm pwait.asm render.c toshiba.uu
echo x - Makefile
sed 's/^X//' > Makefile << '/*EOF'
X.asm.o:
X	as -C -D $*.asm
X
X.c.o:
X	cc +pB $*.c
X
XOBJS =  printertag.o init.o data.o dospecial.o render.o pwait.o debug.o
X
Xtoshiba: $(OBJS)
X	ln -o toshiba $(OBJS) -lcl32
/*EOF
echo x - data.c
sed 's/^X//' > data.c << '/*EOF'
X
X/* toshiba p351c command data table */
X
Xchar *CommandTable[] = {
X	"\x1b\x1a\x49",		/* reset 		RIS 	*/
X	"\x1b\x00",		/* initialize   		*/
X	"\x0a",			/* line feed		IND	*/
X	"\x0d\x0a",		/* CRLF			NEL	*/
X	"\x0d\x1a",		/* reverse LF		RI	*/
X
X	"\x1b\x14\x1bJ\x1bM",	/* normal attributes	SGR 0	*/
X	"\x1b\x12",		/* italics on		SGR 3	*/
X	"\x1b\x14",		/* italics off		SGR 23  */
X	"\x1bI",		/* underline on 	SGR 4	*/
X	"\x1bJ",		/* underline off	SGR 24	*/
X	"\x1bK\x02",		/* boldface on		SGR 1	*/
X	"\x1bM",		/* boldface off		SGR 22	*/
X	"\xff",			/* set foreground colour 	*/
X	"\xff",			/* set background colour 	*/
X
X	"\x1b*0\x1b]\x1b\x22",	/* normal font 		SHORP	*/
X	"\x1b*1",		/* elite on		SHORP	*/
X	"\x1b*0",		/* elite off		SHORP	*/
X	"\x1b[",		/* condensed on		SHORP	*/
X	"\x1b]",		/* condensed off	SHORP	*/
X	"\x1b!",		/* elongated on		SHORP	*/
X	"\x1b\x22",		/* elongated off	SHORP	*/
X
X	"\x1bQ",		/* shadow print on	DEN6	*/
X	"\x1bM",		/* shadow print on	DEN5	*/
X	"\x1bK\x02",		/* double strike on	DEN4	*/
X	"\x1bM",		/* double strike off	DEN3	*/
X	"\x1b*2",		/* NLQ on		DEN2	*/
X	"\x1b*0",		/* NLQ off		DEN1	*/
X
X	"\x1bD",		/* superscript on		*/
X	"\x1bU",		/* superscript off		*/
X	"\x1bU",		/* subscript off		*/
X	"\x1bD",		/* subscript on			*/
X	"\xff",			/* normalize			*/
X
X	"\xff",			/* US Char Set			*/
X	"\xff",			/* French Char Set		*/
X	"\xff",			/* German Char Set		*/
X	"\xff",			/* UK Char Set			*/
X	"\xff",			/* Danish I Char Set		*/
X	"\xff",			/* Swedish Char Set		*/
X	"\xff",			/* Italian Char Set		*/
X	"\xff",			/* Spanish Char Set		*/
X	"\xff",			/* Japanese Char Set		*/
X	"\xff",			/* Norweign Char Set		*/
X	"\xff",			/* Danish II Char Set		*/
X
X	"\x1b$",		/* Prop. Spacing on	PROP	*/
X	"\x1b%",		/* Prop. Spacing off	PROP	*/
X	"\xff",			/* Prop. Clear		PROP	*/
X	"\xff",			/* Prop. Offset		TSS	*/
X	"\xff",			/* Auto right justify	JFY5	*/	
X	"\xff",			/* Auto left justify	JFY7	*/	
X	"\xff",			/* Auto full justify	JFY6	*/	
X	"\xff",			/* justify/centre off		*/	
X	"\xff",			/* place holder		JFY3	*/	
X	"\xff",			/* Auto Centre on	JFY2	*/	
X
X	"\x1b\x1e\x06",		/* 1/8" line spacing	VERP	*/
X	"\x1b\x1e\x08",		/* 1/6" line spacing	VERP	*/
X	"\xff",			/* set form length	SLPP	*/
X	"\xff",			/* skip perf on			*/
X	"\xff",			/* skip perf off		*/
X
X	"\x1b9",		/* left margin set 		*/
X	"\x1b0",		/* right margin set		*/
X	"\x1b+",		/* top margin set 		*/
X	"\x1b-",		/* bottom margin set		*/
X	"\xff",			/* T & B margin set	STBM	*/
X	"\xff",			/* L & R margin set	SLRM	*/
X	"\xff",			/* Clear margins		*/
X	
X	"\x1b1",		/* Set Horizontal Tab	HTS	*/
X	"\xff",			/* Set Vertical tab	VTS	*/
X	"\x1b8",		/* Clr Horizontal Tab	TBC 0	*/
X	"\x1b2",		/* Clr All Horiz. Tabs	TBC 3	*/
X	"\xff",			/* Clr Vertical Tab	TBC 1	*/
X	"\xff",			/*. Clr All Vert. Tabs	TBC 4	*/
X	"\x1b8",		/* Clr All V & H Tabs		*/
X				/* Set Default Tabs		*/
X	"\x1b(08,16,24,32,40,48,56,64,72.",
X	"\xff",			/* Extended Command		*/
X};
/*EOF
echo x - debug.c
sed 's/^X//' > debug.c << '/*EOF'
X/*	DEBUGGING-- This is Matt Dillon's debugger code,
X *	which is *real* handy when you're paranoid about what
X *	you can and can't do because of your task/process state
X *
X *	It's even better than kprintf :-)
X */
X
X#include <exec/types.h>
X#include <exec/nodes.h>
X#include <exec/lists.h>
X#include <exec/memory.h>
X#include <devices/printer.h>
X#include <devices/prtbase.h>
X
X#ifndef DEBUG
X
Xdbprintf() {}	/* empty function declaration */
X
X#else
X
X#define CTOB(x) (void *)(((long)(x))>>2)
X
Xstruct Task *FindTask();
Xstruct MsgPort *CreatePort();
Xstruct FileHandle *Open();
Xstruct Message *GetMsg();
X
X
Xstruct MsgPort  *Dbport;    /*	owned by the debug process	*/
Xstruct MsgPort  *Dback;	    /*	owned by the DOS device driver	*/
Xshort DBDisable;
Xstruct Message  DummyMsg;
X
Xvoid dbstart()
X{
X
X    DBDisable = 0;
X    Dbport = Dback = NULL;
X
X    /* need DosBase and SysBase to use debugger */
X    /*	DEBUGGING   */
X
X    dbinit();	/* to close off debugger call dbuninit() */
X}
X
X/*
X *  DEBUGGING CODE.	You cannot make DOS library calls that access other
X *  devices from within a DOS device driver because they use the same
X *  message port as the driver.  If you need to make such calls you must
X *  create a port and construct the DOS messages yourself.  I do not
X *  do this.  To get debugging info out another PROCESS is created to which
X *  debugging messages can be sent.
X *
X *  You want the priority of the debug process to be larger than the
X *  priority of your DOS handler.  This is so if your DOS handler crashes
X *  you have a better idea of where it died from the debugging messages
X *  (remember that the two processes are asyncronous from each other).
X */
X
Xextern void debugproc();
X
Xdbinit()
X{
X    struct Task *task = FindTask(NULL);
X
X    Dback = CreatePort(NULL,NULL);
X    CreateProc("DEV_DB", task->tc_Node.ln_Pri+1, CTOB(debugproc), 4096);
X    WaitPort(Dback);				/* handshake startup    */
X    GetMsg(Dback);				/* remove dummy msg     */
X    dbprintf("Debugger running V1.00\n");
X}
X
Xdbuninit()
X{
X    struct Message killmsg;
X
X    if (Dbport) {
X	killmsg.mn_Length = 0;	    /*	0 means die	    */
X	PutMsg(Dbport,&killmsg);
X	WaitPort(Dback);	    /*	He's dead jim!      */
X	GetMsg(Dback);
X	DeletePort(Dback);
X
X	/*
X	 *  Since the debug process is running at a greater priority, I
X	 *  am pretty sure that it is guarenteed to be completely removed
X	 *  before this task gets control again.  Still, it doesn't hurt...
X	 */
X
X	/* Delay(50); */	    /*	ensure he's dead    */
X    }
X}
X
Xdbprintf(a,b,c,d,e,f,g,h,i,j)
X{
X    static char buf[256];
X    struct Message *msg;
X
X    if (Dbport && !DBDisable) {
X	sprintf(buf,a,b,c,d,e,f,g,h,i,j);
X	msg = AllocMem(sizeof(struct Message)+strlen(buf)+1,
X		MEMF_PUBLIC|MEMF_CLEAR);
X	msg->mn_Length = strlen(buf)+1;     /*	Length NEVER 0	*/
X	strcpy(msg+1,buf);
X	PutMsg(Dbport,msg);
X    }
X}
X
X/*
X *  BTW, the DOS library used by debugmain() was actually openned by
X *  the _Init routine.	Note: DummyMsg cannot be on debugmain()'s stack
X *  since debugmain() goes away on the final handshake.
X */
X
Xdebugmain()
X{
X    
X    struct Message *msg;
X    short len;
X    void *fh;
X
X    Dbport = CreatePort(NULL,NULL);
X    fh = Open("con:0/0/640/100/debugwindow", 1006);
X    PutMsg(Dback, &DummyMsg);
X    for (;;) {
X	WaitPort(Dbport);
X	msg = GetMsg(Dbport);
X	len = msg->mn_Length;
X	if (len == 0)
X	    break;
X	--len;			    /*	Fix length up	*/
X	Write(fh, msg+1, len);
X	FreeMem(msg,sizeof(struct Message)+len+1);
X    }
X    Close(fh);
X    DeletePort(Dbport);
X    PutMsg(Dback,&DummyMsg);	      /*  Kill handshake  */
X}
X
X/*
X *  The assembly tag for the DOS process:  CNOP causes alignment problems
X *  with the Aztec assembler for some reason.  I assume then, that the
X *  alignment is unknown.  Since the BCPL conversion basically zero's the
X *  lower two bits of the address the actual code may start anywhere
X *  within 8 bytes of address (remember the first longword is a segment
X *  pointer and skipped).  Sigh....  (see CreatProc() above).
X */
X
X#asm
X	public	_debugproc
X	public	_debugmain
X
X	cseg
X_debugproc:
X	nop
X	nop
X	nop
X	nop
X	nop
X	movem.l D2-D7/A2-A6,-(sp)
X	jsr	_debugmain
X	movem.l (sp)+,D2-D7/A2-A6
X	rts
X#endasm
X
X#endif
/*EOF
echo x - dospecial.c
sed 's/^X//' > dospecial.c << '/*EOF'
X/* toshiba special commands */
X
X#include <exec/types.h>
X#include <devices/printer.h>
X#include <devices/prtbase.h>
X
Xextern struct PrinterData *PD;
Xextern struct PrinterExtendedData *PED;
X
XDoSpecial(command,outputBuffer,vline,currentVMI,crlfFlag,Parms)
Xregister UWORD *command;
Xregister char *outputBuffer;
Xregister BYTE *vline;
Xregister BYTE *currentVMI;
Xregister BYTE *crlfFlag;
Xregister UBYTE *Parms;
X{
X	/* Aztec doesn't preserve a6... we need this in a printer driver */
X#asm
X	move.l	a6,-(sp)
X#endasm
X	int result;
X
X	dbprintf("DoSpecial:");
X
X	switch (*command) {
X
X	case aRIN: 
X		/* start with initialization string */
X
X		strcpy(outputBuffer,"\x1b\x1aI");
X
X		/* set to ELITE mode on LETTER or ELITE spacing... */
X
X		if (
X		    (PD->pd_Preferences.PrintQuality == LETTER) ||
X		    (PD->pd_Preferences.PrintPitch == ELITE)
X		   )  strcat(outputBuffer,"\x1b*1");
X
X		/* set to condensed mode if req'd */
X
X	    	if (PD->pd_Preferences.PrintPitch == FINE)
X			strcat(outputBuffer,"\x1b[");
X
X		/* this might look backwards but it's not */
X		/* the toshiba allows you to specify the spacing */
X		/* in 48ths of an inch, 6/48 == 1/8, 8/48 == 1/6 */
X
X		if (PD->pd_Preferences.PrintSpacing == SIX_LPI) {
X			strcat(outputBuffer,"\x1b\x1e\x08");	/* 8 48ths */
X			*currentVMI = 36;
X			dbprintf(" VMI=36");
X		}
X		else {
X			strcat(outputBuffer,"\x1b\x1e\x06");	/* 6 48ths */
X			*currentVMI = 27;
X			dbprintf(" VMI=27");
X		}
X		dbprintf(" aRIN\n");
X		result = strlen(outputBuffer);
X		break;
X	
X
X	case aVERP0:
X		*currentVMI=27;
X		dbprintf(" VMI=27\n");
X		break;
X
X	case aVERP1:
X		*currentVMI=36;
X		dbprintf(" VMI=36\n");
X		break;
X
X	default:
X		dbprintf("req:%d\n",*command);
X		break;
X	}
X#asm
X	move.l	(sp)+,a6
X#endasm
X	return(result);
X}
/*EOF
echo x - flush.c
sed 's/^X//' > flush.c << '/*EOF'
X#include <exec/memory.h>
X
X/*
X *  Allocate memory until we can't ... force the system to flush all
X *  non-active DOS devices, libraries, fonts, etc....
X */
X
Xextern void *AllocMem();
X
Xmain()
X{
X    char *ptr;
X    long bytes = 1 << 9;
X
X    while (ptr = AllocMem(bytes, MEMF_PUBLIC)) {
X	FreeMem(ptr, bytes);
X	bytes <<= 1;
X    }
X}
X
/*EOF
echo x - flush.uu
sed 's/^X//' > flush.uu << '/*EOF'
Xbegin 644 flush
XM   #\P         #          (   (:     0   !(   /I   "&F!"3E7_
XM^"M\   " /_X2'@  2\M__A.N0  ![A03RM __QG'"\M__@O+?_\3KD   ?F
XM4$\@+?_XXX K0/_X8,Y.74YU87Q#^0    1%^0    "UR68.,CP $6L(=  B
XMPE')__PCSP     L>  $(\X    $2.> @ @N  0!*6<02_H "$ZN_^)@!D*G
XM\U].<T/Z "1.KOYH(\     (9@PN/  #@ =.KO^48 9.N0   ,I03TYU9&]S
XM+FQI8G)A<GD 2?D  '_^3G5.50  2.<P($AY  $  # Y     ,'\  8O $ZY
XM   'OE!/(\     ,9AA"ITAY  $  $ZY   'BE!/+GD     3G4@>0    Q"
XM:  $('D    ,,7P  0 0(GD    ,,WP  0 *('D     (#D     D*@ !%" 
XM(\     0('D    0(+Q-04Y80J=.N0  !\Y83R1 2JH K&<X+RT #"\M  @O
XM"DZY   "5D_O  PC_     $    4('D    , &B    $('D    , &B    *
XM8%A(:@!<3KD   @L6$](:@!<3KD   ?^6$\CP    !@@>0   !A*J  D9Q0@
XM>0   !@B:  D+Q%.N0  !UA83R\Y    &"\*3KD   284$\C^0   !@    <
XM3KD   =F('D    ,((!.N0  !X @>0    PA0  &9QI(> /M2'H .DZY   '
XM<%!/('D    ,(4  #"\Y    '"\Y    ($ZY     E!/0J=.N0  !7183TS?
XM! Q.74YU*@!.50  2.<\,"1M ! @;0 (("@ K.6 *  @1" H !#E@"9 $!-(
XM@$C T*T #%2 (\     D0J<O.0   "1.N0  ![Y03R/     *&8(3-\,/$Y=
XM3G40$TB 2, O "!+4H@O""\Y    *$ZY   $/$_O  Q(>@%P$!-(@$C T+D 
XM   H+P!.N0  !(A03R\M  PO"B\Y    *$ZY   $9$_O  Q"N0   " F>0  
XM "@D2Q 32(!(P"H L+P    @9R"ZO     EG&+J\    #&<0NKP    -9PBZ
XMO     IF!%*+8,P,$P @;0  C@P3 ")F,E*+($M2BQ 02(!(P"H 9R @2E**
XM$(6ZO    ")F$ P3 ")F!%*+8 9"*O__8 )@TF!$($M2BQ 02(!(P"H 9S"Z
XMO    "!G*+J\    "6<@NKP    ,9QBZO     UG$+J\    "F<(($I2BA"%
XM8,(@2E**0A!*A68"4XM2N0   "!@ /\Z0A)"IR Y    (%* Y8 O $ZY   '
XMOE!/(\     <9@I"N0   "!@ /ZL>@ F>0   "A@'B %Y8 @>0   !PABP@ 
XM+PM.N0  !6)83U* U\!2A;JY    (&W:( 7E@"!Y    '$*P" !@ /YJ( !,
XM[P,   0@""(O  Q@ A#95\G__&<&4D%@ D(84<G__$YU,#Q__V $,"\ #B!O
XM  1*&&;\4T@B;P (4T 0V5?(__QG D(0("\ !$YU(&\ !" ((F\ "!#99OQ.
XM=4Y5  !(YSXP)&T "$*G2'H I$ZY   (#%!/(\     \9@A,WPQ\3EU.=2!M
XM  PB:  D+RD !$ZY   (6%A/* !G6DAZ 'T@1"\H #9.N0  "#I03R9 2H!G
XM.$AX ^TO"TZY   '<%!/+ !G)B &Y8 J "!%)6@ " "D)48 G$AX ^U(>@!$
XM3KD   =P4$\E0 "@+P1.N0  "$I83R\Y    /$ZY   'I%A/0KD    \8 #_
XM<&EC;VXN;&EB<F%R>0!724Y$3U< *@ @;P $( A*&&;\D< @"%. 3G5.50  
XM2.<P $JY    0&<(('D   ! 3I O+0 (3KD   6@6$],WP ,3EU.=4Y5__Q(
XMYS@ *VT "/_\2KD    ,9S9X & ,+P1.N0  !L983U*$,#D     2,"X@&WH
XM,#D     P?P !B\ +SD    ,3KD   ?L4$]*N0   $1G""!Y    1$Z02KD 
XM   L9PXO.0   "Q.N0  !ZI83TJY    ,&<.+SD    P3KD   >J6$]*N0  
XM #1G#B\Y    -$ZY   'JEA/+'@ ! @N  0!*6<4+PU+^@ *3J[_XBI?8 9"
XMI_-?3G-*N0   !AF.$JY    *&<N+SD    D+SD    H3KD   ?L4$\@.0  
XM "!2@.6 +P O.0   !Q.N0  !^Q03V 43KD   ?<+SD    83KD   @>6$\@
XM+?_\+GD     3G5,WP <3EU.=4Y5  !(YSX@*"T "'(&( 1.N0  !R8D0-7Y
XM    #$J$;1 P.0    !(P+B ; 1*DF84(_P    "    .'#_3-\$?$Y=3G4P
XM*@ $P'R  &8*+Q).N0  !TI83T*2< !@WDCG<  T <3 )@%(0\; 2$-"0]2#
XM2$# P4A 0D#0@DS?  Y.=2(O  0L>0    A.[O_<(B\ !"QY    "$[N_X(L
XM>0    A.[O_*3.\ !@ $+'D    (3N[_XBQY    "$[N_\1(YP$$3.\@@  ,
XM+'D    $3J[_E$S?((!.=4[Y   'JB)O  0L>0    1.[OYB3OD   >^3.\ 
XM P $+'D    $3N[_.B)O  0L>0    1.[O[:+'D    $3N[_?$[Y   '[")O
XM  0@+P (+'D    $3N[_+B!O  0L>0    1.[OZ,+'D    $(F\ !" O  A.
XM[OW8(F\ !"QY    !$[N_H8@;P $+'D    $3N[^@$SO P  !"QY    /$[N
XM_Z @;P $+'D    \3N[_IB!O  0L>0   #Q.[O^R      /L    !@    $ 
XM  !(    Q    -H   7(   %U   !NH    L         !@    N    K@  
XM .8   #^   !5@   7    &B   !K@   =0   'D   !]@   @0   (>   "
XM/    D8   *4   "P    MP   +P   #X   !!0   2L   $T   !.8   3Z
XM   %(   !2X   4\   %D@  !;X   7F   &#   !B(   8X   &?   !I8 
XM  :@   &K   !M@   <:   'I@  ![H   ?H    5@    (   !.    9@  
XM '    ":    [@   08   $.   !&    20   $P   !-@   4(   %(   !
XM?@   80   &0   !M@   ;P   '(   !W    >P   'P   !_    @H   (F
XM   ",    C8   *&   "C@   IP   *Z   "U    NH   +Z   #     \8 
XM  /4   #Z    _    /\   $"   !"(   0N   $M   !38   5$   %?@  
XM!88   6P   %X   !>X   7V   %_@  !@8   84   &'   !BH   8R   &
XM8   !F@   9P   &=@  !H0   :0   &I@  !K@   ;@   &_@  !U    =>
XM   ':   !W@   >"   'E@  ![    ?&   'U   !]X   ?V   (!   " X 
XM  @D   (,@  "$(   A0   (7@        /R   #Z@    $ %      #\@  
X* ^L    2   #\D( 
X 
Xend
/*EOF
echo x - init.asm
sed 's/^X//' > init.asm << '/*EOF'
X*
X* initialization stub for the toshiba printer driver
X*
X
X	INCLUDE "exec/types.i"
X	INCLUDE "exec/nodes.i"
X	INCLUDE "exec/lists.i"
X	INCLUDE "exec/memory.i"
X	INCLUDE "exec/ports.i"
X	INCLUDE "exec/libraries.i"
X
X	INCLUDE "macros.i"
X	INCLUDE "devices/prtbase.i"
X
X	XREF_EXE	CloseLibrary
X	XREF_EXE	OldOpenLibrary
X	XREF_EXE	OpenLibrary
X
X	XREF		_AbsExecBase	;these are in the Aztec Library
X	XREF		_SysBase	;and this
X
X	XREF		_PEDData
X
X	ifd		DEBUG
X	XREF		_dbstart
X	XREF		_dbuninit
X	XREF		_dbprintf
X	XREF		_PWait
X	endc
X
X	XDEF		_Init
X	XDEF		_Expunge
X	XDEF		_PrOpen
X	XDEF		_PrClose
X	XDEF		_PD
X	XDEF		_PED
X	XDEF		_DOSBase
X	XDEF		_GfxBase
X	XDEF		_IntuitionBase
X
X*------------------------------------------------------
X	section	printer,data
X_PD		dc.l	0
X_PED		dc.l	0
X_DosBase	dc.l	0
X_GfxBase	dc.l	0
X_IntuitionBase	dc.l	0
X
X*------------------------------------------------------
X	section printer,code
X_Init
X		move.l	4(sp),_PD
X
X		lea	_PEDData(pc),a0
X		move.l	a0,_PED
X		move.l	a6,-(a7)
X		move.l	_AbsExecBase,a6
X		move.l	a6,_SysBase
X
X* open dos library
X
X		lea	DLName,a1
X		moveq	#1,d0
X		callexe	OpenLibrary
X		move.l	d0,_DOSBase
X		beq	initDLErr
X
X* open gfx libarary
X
X		lea	GLName,a1
X		moveq	#1,d0
X		callexe	OpenLibrary
X		move.l	d0,_GfxBase
X		beq	initGLErr
X
X* open intuition libarary
X
X		lea	ILName,a1
X		moveq	#1,d0
X		callexe	OpenLibrary
X		move.l	d0,_IntuitionBase
X		beq	initILErr
X
X		ifd	DEBUG
X		jsr	_dbstart
X		endc
X
X		moveq	#0,d0
XpdiRts:
X		move.l	(a7)+,a6
X		rts
X
XinitPAErr:
X		move.l	_IntuitionBase,a1
X		LINKEXE	CloseLibrary
X
XinitILErr:
X		move.l	_GfxBase,a1
X		LINKEXE	CloseLibrary
X
XinitGLErr:
X		move.l	_DOSBase,a1
X		LINKEXE	CloseLibrary
X
XinitDLErr:
X		moveq	#-1,d0
X		bra.s	pdiRts
X
XILName:		dc.b	"intuition.library",0
XDLName:		dc.b	"dos.library",0
XGLName:		dc.b	"graphics.library",0
XDeathMsg:	dc.b	"I'm being rubbed out!",0
X		ds.w	0
X
X
X
X*------------------------------------------------------
X
X_Expunge:
X		move.l	a6,-(sp)
X
X		ifd	DEBUG
X		pea.l	DeathMsg
X		jsr	_dbprintf
X		addq.l	#4,sp
X		pea	0
X		pea	1
X		jsr	_PWait
X		addq.l	#8,sp
X		jsr	_dbuninit
X		endc
X
X		move.l	_SysBase,a6
X
X		move.l	_IntuitionBase,a1
X		callexe	CloseLibrary
X
X		move.l	_GfxBase,a1
X		callexe	CloseLibrary
X
X		move.l	_DOSBase,a1
X		callexe	CloseLibrary
X
X		move.l	(sp)+,a6
X		moveq	#0,d0
X		rts
X
X*------------------------------------------------------
X_PrOpen:
X		moveq	#0,d0
X		rts
X
X*------------------------------------------------------
X_PrClose:
X		moveq	#0,d0
X		rts
/*EOF
echo x - printertag.asm
sed 's/^X//' > printertag.asm << '/*EOF'
X*
X* printer tag file for the toshiba printer
X*
X
X	INCLUDE	"exec/types.i"
X	INCLUDE	"exec/nodes.i"
X	INCLUDE "exec/strings.i"
X	INCLUDE "devices/prtbase.i"
X
X	XREF _Init
X	XREF _Expunge
X	XREF _PrOpen		; called PrOpen and PrClose so as not to
X	XREF _PrClose		; override the Dos Open and Close calls
X	XREF _CommandTable
X	XREF _DoSpecial
X	XREF _Render
X
X	XDEF _PEDData
X
X
XVERSION		EQU 	$21
XREVISION	EQU	$24
X
X
X	section printer,code
X
X	moveq	#0,d0			;in case anyone tries to run this
X	rts
X
X	dc.w	VERSION
X	dc.w	REVISION
X
X_PEDData:
X
X	dc.l	printerName
X	dc.l	_Init
X	dc.l	_Expunge
X	dc.l	_PrOpen
X	dc.l	_PrClose
X	dc.b	PPC_COLORGFX		; PrinterClass
X	dc.b	PCC_YMCB		; ColorClass
X	dc.b	80			; max columns
X	dc.b	1			; num char sets
X	dc.w	24			; num rows
X	dc.l	180*8			; max x dots
X	dc.l	0			; max y dots
X	dc.w	180			; x dots/inch
X	dc.w	180			; y dots/inch
X	dc.l	_CommandTable
X	dc.l	_DoSpecial
X	dc.l	_Render
X	dc.l	30
X	dc.l	0
X
XprinterName:
X	dc.b	'toshiba',0
/*EOF
echo x - pwait.asm
sed 's/^X//' > pwait.asm << '/*EOF'
X*
X* PWait - wait for a time
X* 
X* PWait(seconds, micros)
X*
X
X	INCLUDE "exec/types.i"
X	INCLUDE "exec/ports.i"
X	INCLUDE "exec/devices.i"
X	INCLUDE "exec/io.i"
X
X	INCLUDE	"devices/timer.i"
X	INCLUDE	"macros.i"
X	INCLUDE	"devices/prtbase.i"
X
X	XREF_EXE	Forbid
X	XREF_EXE	Permit
X	XREF_EXE	WaitIO
X
X	XREF		_SysBase
X	XREF		_PD
X
X	XDEF		_PWait
X
X_PWait:
X		movem.l a4/a6, -(a7)
X		move.l	_PD,a4
X		move.l	pd_PBothReady(a4),a0
X		jsr 	(a0)
X		tst.l	d0
X		bne.s	error
X
X		lea	pd_TIOR(a4),a1
X		move.w	#TR_ADDREQUEST,IO_COMMAND(a1)
X		move.l	12(a7),IOTV_TIME+TV_SECS(a1)
X		move.l	16(a7),IOTV_TIME+TV_MICRO(a1)
X		clr.b	IO_FLAGS(a1)
X		move.l	IO_DEVICE(a1),a6
X		LINKEXE	Forbid
X		lea	pd_TIOR(a4),a1
X		LINKEXE	WaitIO
X		LINKEXE	Permit
X		moveq	#0,d0
X		tst.l	d0
Xerror:
X		movem.l	(a7)+,a4/a6
X		rts
/*EOF
echo x - render.c
sed 's/^X//' > render.c << '/*EOF'
X#include <exec/types.h>
X#include <exec/nodes.h>
X#include <exec/lists.h>
X#include <exec/memory.h>
X#include <devices/printer.h>
X#include <devices/prtbase.h>
X
Xextern struct PrinterData *PD;
Xextern struct PrinterExtendedData *PED;
X	
X#define PWrite (*(PD->pd_PWrite))	/* write data to device */
X#define PReady (*(PD->pd_PBothReady))	/* wait for both buffers ready */
X
X#define LEAD   4		/* bytes to start each complete cycle */
X#define EXTRA  9		/* bytes to prefix each pass of the head */
X#define TAIL   2		/* bytes to end each complete cycle */
X
Xvoid *AllocMem();
X
X/* these two tables wouldn't normally be bothered with but in light of
X * the number of times that these operations need to be performed
X * for each pass of the print head I thought they'd be worthwhile
X */
X
X/*
X * this table simulates the operation   (1<<(5-(y%6)))
X * for values of y between 0 and 23 (the number of pixels in a printer pass)
X *
X */
X
XUBYTE bit_table[] = {
X		    32,16,8,4,2,1,
X		    32,16,8,4,2,1,
X		    32,16,8,4,2,1,
X		    32,16,8,4,2,1
X};
X
X/* this table simulates  y/6 for values of y between 0 and 23 */
X/* saves a long division opeation */
XUBYTE div_table[] = {
X		    0,0,0,0,0,0,
X		    1,1,1,1,1,1,
X		    2,2,2,2,2,2,
X		    3,3,3,3,3,3
X};
X
XUBYTE *GfxBuffer;
X
XULONG Render(ct,x,y,status)
Xregister UBYTE ct;
Xregister UWORD x,y;
Xregister UBYTE status;
X{
X	static ULONG ROWSIZE;
X	static ULONG COLOURSIZE;
X	static ULONG BUFSIZE;
X	static ULONG colours[4];
X	static ULONG centre;
X	static ULONG bufptr;
X	static UBYTE *ptr;
X	static ULONG psize;
X	int i,filler;
X	int err;
X
X/* Aztec doesn't preserve a6, we need this in a printer driver */
X#asm
X	move.l	a6,-(sp)
X#endasm
X	err = 0;
X
X	switch (status) {
X
X	case 0:	/* alloc memory for printer buffer */
X
X		filler 	   = (centre) ? ((PED->ped_MaxXDots -x ) / 2 ) : 0;
X		ROWSIZE    = ((x+filler)<<2) + EXTRA;
X		COLOURSIZE = ROWSIZE<<2;
X		BUFSIZE	   = COLOURSIZE+LEAD+TAIL+1;	/* plus 1 for NULL */
X
X		colours[0] = LEAD +  EXTRA + filler + ROWSIZE*0;
X		colours[1] = LEAD +  EXTRA + filler + ROWSIZE*1;
X		colours[2] = LEAD +  EXTRA + filler + ROWSIZE*2;
X		colours[3] = LEAD +  EXTRA + filler + ROWSIZE*3;
X		psize = x+filler;
X
X		dbprintf("Size Data:\n");
X		dbprintf("\tfiller: %d\n",filler);
X		dbprintf("\tROWSIZE: %d\n",ROWSIZE);
X		dbprintf("\tCOLOURSIZE: %d\n",COLOURSIZE);
X		dbprintf("\tBUFSIZE: %d\n",BUFSIZE);
X		dbprintf("\tcolours: %d %d %d %d\n",
X			colours[0],colours[1],colours[2],colours[3]);
X
X		GfxBuffer =  AllocMem(BUFSIZE*2,MEMF_PUBLIC);
X
X		if (err = (GfxBuffer==0))	    break;
X		if (err = PWrite("\x1b\x1a\x49",3)) break;
X		if (err = PWait(1,0))		    break;
X
X		/* set to first buffer */
X		bufptr  = 0;
X		break;
X
X	case 1:	/* render pixel */
X		y %= 24; /* blecch, I wanted to avoid this calculation */
X		i = bufptr + (x<<2) + div_table[y] + colours[3-ct];
X		GfxBuffer[i] |= bit_table[y];
X		break;
X
X	case 2: /* write a buffer */
X		dbprintf("Writing a buffer\n");
X
X		/* BUFSIZE-1 is so that we don't send the trailing null */
X		if (err=PWrite(&GfxBuffer[bufptr],BUFSIZE-1)) break;
X		bufptr = BUFSIZE-bufptr;
X		break;
X
X	case 3: /* initialize the current buffer */
X		dbprintf("Performing initialization\n");
X
X		ptr = &GfxBuffer[bufptr];
X
X		for (i=BUFSIZE;i--;) *ptr++ = 0;	/* clear buffer */
X
X		ptr = &GfxBuffer[bufptr];
X		sprintf(ptr,"\x1bL07");
X		sprintf(ptr+LEAD     	  ,"\r\x1bc\x1b;%04d",psize); /* c */
X		sprintf(ptr+LEAD+ROWSIZE  ,"\r\x1bm\x1b;%04d",psize); /* m */
X		sprintf(ptr+LEAD+ROWSIZE*2,"\r\x1by\x1b;%04d",psize); /* y */
X		sprintf(ptr+LEAD+ROWSIZE*3,"\r\x1bb\x1b;%04d",psize); /* b */
X		sprintf(ptr+LEAD+ROWSIZE*4,"\r\n");	/* crlf + NULL */
X
X		break;
X
X	case 4: /* cleanup -- release memory */
X		dbprintf("Print finished -- cleanup\n");
X		dbprintf("waiting for printer\n");
X
X		err = PReady();
X
X		dbprintf("Freeing memory\n");
X
X		FreeMem(GfxBuffer,BUFSIZE*2);
X
X		dbprintf("Returning\n");
X
X		break;
X
X	case 5:	/* handle any special commands */
X		centre = x & SPECIAL_CENTER;
X		dbprintf("special: %d %d %d %d\n",ct,x,y,status);
X
X		break;
X
X	default:
X		break;
X	}
X#asm
X	move.l	(sp)+,a6
X#endasm
X	return(err);
X}
/*EOF
echo x - toshiba.uu
sed 's/^X//' > toshiba.uu << '/*EOF'
Xbegin 644 toshiba
XM   #\P         #          (   /A    @0   !P   /I   #X7  3G4 
XM(0 D    0@   $H   $N   !6@   5X#!% ! !@   6@      "T +0    4
XM   "3    ^X    >     '1O<VAI8F$ (^\ !     !!^0    @CR     0O
XM#BQY    !"/.    ,$/Z (QP 4ZN_=@CP    #1G9D/Z (9P 4ZN_=@CP   
XM  QG0$/Z %9P 4ZN_=@CP    !!G&G  +%].=2)Y    $"\.+'D    P3J[^
XM8BQ?(GD    ,+PXL>0   #!.KOYB+%\B>0   #0O#BQY    ,$ZN_F(L7W#_
XM8+QI;G1U:71I;VXN;&EB<F%R>0!D;W,N;&EB<F%R>0!G<F%P:&EC<RYL:6)R
XM87)Y $DG;2!B96EN9R!R=6)B960@;W5T(0  +PXL>0   # B>0   !!.KOYB
XM(GD    ,3J[^8B)Y    -$ZN_F(L7W  3G5P $YU< !.=1L:20 ;   *  T*
XM  T: !L4&TH;30 ;$@ ;%  ;20 ;2@ ;2P( &TT _P#_ !LJ,!M=&R( &RHQ
XM !LJ,  ;6P ;70 ;(0 ;(@ ;40 ;30 ;2P( &TT &RHR !LJ,  ;1  ;50 ;
XM50 ;1 #_ /\ _P#_ /\ _P#_ /\ _P#_ /\ _P ;)  ;)0#_ /\ _P#_ /\ 
XM_P#_ /\ &QX& !L>" #_ /\ _P ;.0 ;,  ;*P ;+0#_ /\ _P ;,0#_ !LX
XM !LR /\ _P ;.  ;*# X+#$V+#(T+#,R+#0P+#0X+#4V+#8T+#<R+@#_ $Y5
XM__Q(YS\P)&T ""9M  PH+0 0*BT %"PM !@N+0 <+PY(>@$N3KD   G06$]P
XM # 28   _$AZ 24O"TZY   *"E!/('D     #&@!  I89PXB>0     ,:00 
XM"E9F#DAZ /\O"TZY   )X%!/('D     #&@(  I69@Y(>@#G+PM.N0  ">!0
XM3R!Y     $IH"EIF(DAZ - O"TZY   )X%!/($40O  D2'H P$ZY   )T%A/
XM8"!(>@"Z+PM.N0  ">!03R!%$+P &TAZ *I.N0  "=!83TAZ *9.N0  "=!8
XM3R\+3KD   \@6$\K0/_\8% @11"\ !M(>@"+3KD   G06$]@/"!%$+P )$AZ
XM (!.N0  "=!83V H<  P$B\ 2'H =4ZY   )T%!/8!13@&< _P*0O    #9G
XMME. 9\9@V"Q?("W__$S?#/Q.74YU1&]3<&5C:6%L.@ ;&DD &RHQ !M; !L>
XM"  @5DU)/3,V !L>!@ @5DU)/3(W "!A4DE."@ @5DU)/3(W"@ @5DU)/3,V
XM"@!R97$Z)60*  !.5?_T2.<_ !@M  LZ+0 ./"T $AXM !<O#D*M__1P ! '
XM8  $%$JY    '&<4('D    $("@ &G( ,@60@>*(8 )P "M __AP # %T*W_
XM^.6 T+P    )(\      (#D     Y8 CP     0@.0    1>@"/     "" M
XM__C0O     TCP     P@+?_XT+D     T+P    -(\     0(#D     XX#0
XMK?_XT+P    -(\     4<@,@.0    !.N0  #S+0K?_XT+P    -(\     8
XM<  P!="M__@CP    "A(>@-R3KD   G06$\O+?_X2'H#;DZY   )T%!/+SD 
XM    2'H#:4ZY   )T%!/+SD    $2'H#94ZY   )T%!/+SD    (2'H#9$ZY
XM   )T%!/+SD    8+SD    4+SD    0+SD    ,2'H#3DZY   )T$_O !1(
XM>  !(#D    (XX O $ZY   /5E!/(\     X2KD    X9@HK?     '_]& $
XM0JW_]&8  KI(>  #2'H#'2!Y     ")H &1.D5!/*T#_]&8  IQ"ITAX  %.
XMN0  "6903RM __1F  *&0KD    @8  "?$A&0D9(1HS\ !A(1G #<@ 2!)"!
XMY8!!^0    QT #0%Y8(F, @ UH)#^0   51T !0Q8 #6@M:Y    ("M#__P@
XM+?_\('D    XT<!#^0   3P2,6  @Q!@  (@2'H"BTZY   )T%A/(#D    (
XM4X O "!Y    .-'Y    ("\(('D     (F@ 9$Z14$\K0/_T9@ !YB Y    
XM")"Y    ("/     (&   =!(>@)-3KD   G06$\@.0   #C0N0   " CP   
XM "0K>0    C__& .('D    D4KD    D0A @+?_\4ZW__$J 9N8@.0   #C0
XMN0   " CP    "1(>@(4+SD    D3KD   H:4$\O.0   "A(>@(!('D    D
XM6(@O"$ZY   *&D_O  PO.0   "A(>@'M('D    DT?D     6(@O"$ZY   *
XM&D_O  PO.0   "A(>@'3(#D     XX#0N0   "18@"\ 3KD   H:3^\ #"\Y
XM    *$AZ ;=R R Y     $ZY   /,M"Y    )%B +P!.N0  "AI/[P ,2'H!
XMFR Y     .6 T+D    D6( O $ZY   *&E!/8   M$AZ 7Q.N0  "=!83TAZ
XM 8M.N0  "=!83R!Y     ")H &A.D2M __1(>@&$3KD   G06$\@.0    CC
XM@"\ +SD    X3KD   ]L4$](>@%P3KD   G06$]@6'  , 7 O    $ CP   
XM !QP ! '+P!R #(&+P%T #0%+P)V !8$+P-(>@%%3KD   G03^\ %& @8![[
XMWOV(_>3^-/]0_ZJPO     9DZN. ,#L Z$[[   L7R M__1,WP#\3EU.=5-I
XM>F4@1&%T83H*  EF:6QL97(Z("5D"@ )4D]74TE:13H@)60*  E#3TQ/55)3
XM25I%.B E9 H "4)51E-)6D4Z("5D"@ )8V]L;W5R<SH@)60@)60@)60@)60*
XM !L:20!7<FET:6YG(&$@8G5F9F5R"@!097)F;W)M:6YG(&EN:71I86QI>F%T
XM:6]N"@ ;3# W  T;8QL[)3 T9  -&VT;.R4P-&0 #1MY&SLE,#1D  T;8AL[
XM)3 T9  -"@!0<FEN="!F:6YI<VAE9" M+2!C;&5A;G5P"@!W86ET:6YG(&9O
XM<B!P<FEN=&5R"@!&<F5E:6YG(&UE;6]R>0H 4F5T=7)N:6YG"@!S<&5C:6%L
XM.B E9" E9" E9" E9 H  $CG  HH>0     @; !H3I!*@&900^P!$#-\  D 
XM'"-O  P ("-O !  )$(I !XL:0 4+PXL>0   #!.KO]\+%]#[ $0+PXL>0  
XM #!.KOXF+%\O#BQY    ,$ZN_W8L7W  2H!,WU  3G5.50  2.<P $S?  Q.
XM74YU,#Q__V $,"\ #B!O  1*&&;\4T@B;P (4T 0V5?(__QG D(0("\ !$YU
XM(&\ !" ((F\ "!#99OQ.=4Y5  !(YS@ (^T "    "Q(;0 0+RT #$AZ "!.
XMN0  "PQ/[P ,*  @>0   "Q"$" $3-\ '$Y=3G5.50  2.<P "!Y    +%*Y
XM    +! M  L0@$B 2,# O    /],WP ,3EU.=4Y5  !(YS@@)&T $ RM    
XM!  49@@@;0 (*!!@%$JM  QO""!M  @H$& &(&T ""@00JT %$JM  QL$D2M
XM  Q*A&P*1(0K?     $ %"(M  P@!$ZY   .ND'Y   !;%.*%+ ( "(M  P@
XM!$ZY   .QB@ 9MA*K0 49P93BA2\ "T@"DS?!!Q.74YU3E7_%$CG.# D;0 (
XM)FT #$*M__@K;0 0__P@2U*+$!!(@$C * !G  ,\N+P    E9@ #%D(M_R(K
XM?     '_]"M\    (/_P*WP  "<0_^P@2U*+$!!(@$C * "PO    "UF$$*M
XM__0@2U*+$!!(@$C * "XO    #!F%"M\    ,/_P($M2BQ 02(!(P"@ N+P 
XM   J9AH@;?_\6*W__"M0_^@@2U*+$!!(@$C * !@.$*M_^A@)'(*("W_Z$ZY
XM   /,M"$D+P    P*T#_Z"!+4HL0$$B 2, H $'Y   !?P@P  )( &;.N+P 
XM   N9F8@2U*+$!!(@$C * "PO    "IF&B!M__Q8K?_\*U#_["!+4HL0$$B 
XM2, H & X0JW_[& D<@H@+?_L3KD   \RT(20O    # K0/_L($M2BQ 02(!(
XMP"@ 0?D   %_"#   D@ 9LXK?     3_Y+B\    ;&86($M2BQ 02(!(P"@ 
XM*WP    $_^1@%+B\    :&8,($M2BQ 02(!(P"@ ( 1@  ""*WP    (_^!@
XM'"M\    "O_@8!(K?    !#_X& (*WS____V_^ O+?_D2&W_(B\M_^ O+?_\
XM3KK]I$_O ! K0/_<("W_Y-&M__Q@7"!M__Q8K?_\*U#_W"\M_]Q.N0  #R!8
XM3RM _^1@2B!M__Q8K?_\*!!![?\A*TC_W!"$8"B0O    &-GXE. 9Y*0O   
XM  MG /]L68!GLE6 9P#_;%> 9P#_<&#,0>W_(I'M_]PK2/_D("W_Y+"M_^QO
XM!BMM_^S_Y$JM__1G<"!M_]P,$  M9PHB;?_<#!$ *V8T#*T    P__!F*E.M
XM_^@@;?_<4JW_W! 02(!(P"\ 3I)83["\_____V8*</],WPP<3EU.=6 8+RW_
XM\$Z26$^PO/____]F!'#_8.)2K?_X("W_Z%.M_^BPK?_D;MI"K?_@8"0@;?_<
XM4JW_W! 02(!(P"\ 3I)83["\_____V8$</]@JE*M_^ @;?_<2A!G"B M_^"P
XMK?_L;<H@+?_@T:W_^$JM__1F*F :2'@ ($Z26$^PO/____]F!G#_8 #_<%*M
XM__@@+?_H4ZW_Z+"M_^1NV& 8+P1.DEA/L+S_____9@9P_V  _TA2K?_X8 #\
XMN" M__A@ /\X2.=( $*$2H!J!$2 4D1*@6H&1($*1  !83Y*1&<"1(!,WP 2
XM2H!.=4CG2 !"A$J :@1$@%)$2H%J D2!81H@ 6#8+P%A$B !(A]*@$YU+P%A
XM!B(?2H!.=4CG, !(04I!9B!(038!- !"0$A @,,B $A ,@*"PS !0D%(04S?
XM  Q.=4A!)@$B $)!2$%(0$) = _0@-.!MH%B!)*#4D!1RO_R3-\ #$YU(&\ 
XM!" (2AAF_)' ( A3@$YU2.=P #0!Q, F 4A#QL!(0T)#U(-(0,#!2$!"0-""
XM3-\ #DYU3OD   ]<3.\  P $+'D    P3N[_.D[Y   /<B)O  0@+P (+'D 
XM   P3N[_+@   ^P    7     0   "X   !.    6@   (H   "<    J@  
XM +X   $X   !0@   I(   *@   "O    M@   0>   %B@  !>@   8,   &
XM0   !Z    EL   *W   "^H   Q8    -0         (    #    !     4
XM    &    #(    V    5    G0   **   "M    M    +J   "_    PP 
XM  ,>   #*@   S0   -,   #8    W0   2P   $V   !.@   3Z   %#   
XM!1X   5"   %6@  !:8   8@   &<   !LH   ;F   '"@  !S    =,   '
XM7   !WP   >,   'F   ![0   ?,   'V   " X   HX   *U@  "NX   O,
XM   ,.@  #0(   ]8   /;@   $T    "    :    '@   "R    Q@   -( 
XM  #:   !,@   4P   06   $2   !$X   16   $7   !&0   1T   $?@  
XM!(H   20   $H@  !*H   3    $S@  !/    4"   %%   !28   4L   %
XM,@  !3@   50   %8@  !6@   6V   %U@  !?8   8$   &*   !C(   8X
XM   &5@  !EP   9B   &>   !GX   :$   &B@  !I0   ::   &K@  !K0 
XM  :Z   &Q   !M(   ;<   &\   !OH   <    '%   !QX   <F   '.@  
XM!T8   =2   ':@  !W(   >\   'Q@  !^P   F<   )K@  ";P   HF   *
XM1   "EX   ID   /9   #WP        #\@   ^H   "!                
XM              %B   !9@   6D   %K   !;@   7$   %X   !>P   7X 
XM  &!   !A    8@   &+   !C0   8\   &7   !FP   9\   &B   !I0  
XM :@   &K   !K@   ;$   &U   !N    ;P   '    !PP   <8   ')   !
XMS    <X   '0   !T@   =0   '6   !V    =H   '<   !W@   >    'B
XM   !Y    ><   'J   ![    >X   'P   !\@   ?0   'V   !^    ?H 
XM  '^   " @   @0   (&   ""    @L   (.   "$0   A0   (6   "&   
XM AH   (=   "'P   B(   (E   ")P   BD   (L   "2B 0" 0" 2 0" 0"
XM 2 0" 0" 2 0" 0" 0        $! 0$! 0(" @(" @,# P,# S Q,C,T-38W
XM.#EA8F-D968    @(" @(" @(" P,# P," @(" @(" @(" @(" @(" @()! 
XM0$! 0$! 0$! 0$! 0$ ,# P,# P,# P,0$! 0$! 0 D)"0D)"0$! 0$! 0$!
XM 0$! 0$! 0$! 0$!0$! 0$! "@H*"@H* @(" @(" @(" @(" @(" @(" @) 
XM0$! (   %      #[    $H         %    !@    <    (    "0    H
XM    +    #     T    .    #P   !     1    $@   !,    4    %0 
XM  !8    7    &    !D    :    &P   !P    =    '@   !\    @   
XM (0   "(    C    )    "4    F    )P   "@    I    *@   "L    
XML    +0   "X    O    ,    #$    R    ,P   #0    U    -@   #<
XM    X    .0   #H    [    /    #T    ^    /P   $    !!    0@ 
XM  $,   !$    10   $8   !'    2    $D   !*    2P   $P   !-   
X6 3@        #\@   ^L    <   #\B  
X 
Xend
/*EOF
echo end of archive
exit 0
-- 
{watmath|allegra|decvax|ihnp4|linus}!utzoo!oscvax!rico
or just rico@oscvax.UUCP if you're lucky

[NSA food: terrorist, cryptography, DES, drugs, CIA, secret, decode]
[CSIS food: supermailbox, tuna, fiberglass coffins, Mirabel, microfiche]
[Cat food: Nine Lives, Cat Chow, Meow Mix, Crave]

stroyan@hpfcdc.HP.COM (Mike Stroyan) (11/03/87)

Thank you
Thank you
Thank you
Thank you
Thank you

Mike Stroyan, [hplabs!]hpfcla!stroyan