[comp.sources.amiga] v02i093: sendmorse - morse code generator

page@swan.ulowell.edu (Bob Page) (12/07/88)

Submitted-by: joe@dayton.dhdsc.mn.org (Joseph P. Larson)
Posting-number: Volume 2, Issue 93
Archive-name: sound/sendmorse.1

This program converts text files to morse code (out the audio port).

[uuencoded binary included here; it's small.  ..Bob]

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	readme
#	Makefile
#	SendMorse.c
#	SendMorse.h
#	SendMorse.uu
# This archive created: Tue Dec  6 17:56:12 1988
cat << \SHAR_EOF > readme

SendMorse is a little program I kinda "whipped up" for 2 purposes:

	1. Learn more about the audio.device (translate: learn something,
		*anything* about the audio.device)
	2. Practice my very ailing Morse code.

Thus, here it is in its glorious entirety.  (Gag).  Note that it's
very heavily based on Rob Peck's "audio.c" from Chapter 8 of his book.
Very heavily.....

For those who think I have a weird Makefile -- not ALL of us can have
hard disks.  This make works very quickly as it throws everything into
ram:.  Note the odd location of c.lib.  I have c32.lib on my SYS1 disk,
so I had to put c.lib on my work disk....


To use it: say "SendMorse [-sendrate] <filelist>".  It will send the
files in the file list at the given word rate.  Default is 5.

Joe Larson (that's still me -- joe@dayton)
November 12, 1988
Golden Valley, Mn.

-------------------Little Notes Follow Hereafter----------------------
A few little notes concerning timings for those who want to fiddle...  

There are a few items that may be worth fiddling with.

	1. The wave form.  I used a short sine wave sample that took me
		about 20 seconds on my calculator.  However, it clicks a little
		at the start of each dit or dah.  I don't know why.  I hope
		someone tells me.....

	2. The MORSE_PERIOD value.  This defines the period of the sample.
		(See the discussion on the audio.device for a definition of
		this.)  Higher values mean lower frequency.

	3. The calculations for ditlength, ditcycles and dahcycles.  I
		decided that I didn't want longer dits and dahs at slower speeds
		but would just add more spacing between letters and words.  Thus,
		ditcycles is a constant and dahcycles is 3*ditcycles.  You may
		want to fiddle with these, but I think it works out well.

		Ditlength, though, was originally how long a dit was supposed
		to be.  And I was using a ditlength pause between dits and
		dahs and 3*ditlength between letters.  I have a constant defined
		somewhere that is the number of ditlengths between words (currently
		6).  This item is in 20 millisecond increments, as that's what
		the Delay() function *seems* to use.

		Because of all this, the sendrate appears to be maybe a little
		higher than you ask for.  That is, the default of 5 WPM on a 20
		character sample SHOULD take about 48 seconds but seems to be a
		little closer to 35 or 40.  You can add a little fudge factor
		if you like -- I'm not too concerned about it.....

	4. Channel allocation.  I only use one channel.  I should probably
		have used Rob's "GetAnyStereoPair" rather than just grabbing
		a single channel, but that's the way it goes.
SHAR_EOF
cat << \SHAR_EOF > Makefile

SendMorse: ram:SendMorse
	copy ram:SendMorse SendMorse

ram:SendMorse.o: SendMorse.c SendMorse.h
	make ram:SendMorse.c
	make ram:SendMorse.syms
	cc +Iram:SendMorse.syms ram:SendMorse.c
	copy ram:SendMorse.o SendMorse.o

ram:SendMorse.c: SendMorse.c
	copy SendMorse.c ram:

ram:SendMorse.syms: SendMorse.h
	cc +Hram:SendMorse.syms SendMorse.h
	copy ram:SendMorse.syms SendMorse.syms

ram:SendMorse: ram:SendMorse.o
	make ram:c.lib
	ln -o ram:SendMorse ram:SendMorse.o -lram:c

ram:c.lib: df1:lib/c.lib
	copy df1:lib/c.lib ram:

SHAR_EOF
cat << \SHAR_EOF > SendMorse.c
#include "SendMorse.h"

#define		MORSE_C1000		3579
#define		MORSE_PERIOD	500
#define		MORSE_LENGTH	6
#define		MORSE_SPACE		6			/* # of dits between words */

BYTE sinewave[8] = {0, 90, 127, 90, 0, -90, -127, -90};

struct Device	*auDevice=0;
struct IOAudio	*audioIOB;
struct IOAudio	*aulockIOB;
struct IOAudio	*aufreeIOB;
BYTE			*chipaudio = 0;

struct MsgPort *auReplyPort, *auLockPort;

extern UBYTE			*AllocMem();
extern struct MsgPort	*CreatePort();

struct IOAudio			*CreateAudioIO();
UBYTE					GetAnyChannel();
void					FreeAudioIO();

#define isalpha(c)	(((c>='a') && (c<='z')) || ((c>='A') && (c<='Z')))
#define isdigit(c)	((c >= '0') && (c <= '9'))

long	sendrate = 5, ditcycles, dahcycles, ditlength;

main(argc,argv)
int argc;
char **argv;
{
	short i;

	GetSoundStuff();
	for (i = 1; i < argc; i++)
	{
		if (*argv[i] == '-')
		{
			sendrate = atoi(argv[i]+1);
			if (sendrate <= 0)
				finishup("usage: code [-sendrate] <filelist>");
		}
		else
			Work(argv[i]);
	}

	FreeAChannel(aufreeIOB);
	finishup("all done");
}


GetSoundStuff()
{
	int		i;
	UBYTE	chan;

	audioIOB  = CreateAudioIO();
	aulockIOB = CreateAudioIO();
	aufreeIOB = CreateAudioIO();

	if(audioIOB == 0 || aufreeIOB == 0 | aulockIOB == 0)
		finishup("out of memory!");

	if ((chipaudio = (BYTE *)AllocMem(8L, MEMF_CHIP)) == 0)
		finishup("out of memory!");
	for (i = 0; i < 8; i++)
		chipaudio[i] = sinewave[i];

	if (OpenDevice("audio.device",0L,audioIOB,0L))
		finishup ("audio device won't open!");

/* Get the device address for later use */
	auDevice = audioIOB->ioa_Request.io_Device;

/* Create ports for replies from the device */
	auReplyPort = CreatePort(0L,0L);
	auLockPort  = CreatePort(0L,0L);

	if(auReplyPort == 0 || auLockPort == 0) 
		finishup("cannot create a port!");

/*
 * initialize port addresses and device fields 
 * for all audio request blocks 
 */
	audioIOB->ioa_Request.io_Device = auDevice;
	aulockIOB->ioa_Request.io_Device = auDevice;

	audioIOB->ioa_Request.io_Message.mn_ReplyPort  = auReplyPort;
	aulockIOB->ioa_Request.io_Message.mn_ReplyPort  = auLockPort;

	chan = GetAnyChannel(audioIOB);

	/* Make the allocation keys match */
	aulockIOB->ioa_AllocKey = audioIOB->ioa_AllocKey;

	/* Make the unit numbers match */
	aulockIOB->ioa_Request.io_Unit = audioIOB->ioa_Request.io_Unit;

	LockAChannel(aulockIOB,chan);

/*
 * If checkio returns true, it means the request has
 * been returned.  This means the channel has been
 * stolen.
 */

	if(CheckIO(aulockIOB))
		finishup("A channel was stolen!");

/*
 * now assuming nothing stolen, setup and request
 * an output from that channel.
 */
	audioIOB->ioa_Data   = (UBYTE *)chipaudio;
	audioIOB->ioa_Length = 8/2; 		/* 4 WORDS in table */
	audioIOB->ioa_Period = MORSE_PERIOD;	/* from table */
	audioIOB->ioa_Volume = 64;			/* maximum */
	audioIOB->ioa_Cycles = 1000;		/* # of times */

	audioIOB->ioa_Request.io_Command = CMD_WRITE;
	audioIOB->ioa_Request.io_Flags = ADIOF_PERVOL;

	/* copy the audio block for freeing channels later */
	*aufreeIOB = *audioIOB;

}


finishup(string)
char *string;
{
	if(auDevice) CloseDevice(audioIOB);
	if(chipaudio) FreeMem(chipaudio,8L);
	if(audioIOB) FreeAudioIO(audioIOB);
	if(aulockIOB) FreeAudioIO(aulockIOB);
	if(auReplyPort) DeletePort(auReplyPort);
	if(auLockPort)  DeletePort(auLockPort);

	printf("%ls\n",string);
	exit(0);
}



/* newaudioblock.c */

struct IOAudio *
CreateAudioIO()
{
	struct IOAudio *iob;

	iob = (struct IOAudio *) AllocMem((long)sizeof(struct IOAudio),
			MEMF_CHIP | MEMF_CLEAR);

	return(iob);	/* returns 0 if out of memory */
}

void	
FreeAudioIO(iob)
struct IOAudio *iob;
{
	FreeMem(iob, (long)sizeof(struct IOAudio));
}



/* lockachannel.c */

LockAChannel(lockiob,channel)
struct IOAudio *lockiob;
UBYTE channel;
{
	/* tell it which channel to lock */

/*	lockiob->ioa_Request.io_Unit = (struct Unit *)channel;	*/

	lockiob->ioa_Request.io_Command = ADCMD_LOCK;
	lockiob->ioa_Request.io_Flags = 0;

	/* Send this command.  It does not return to 
	 * the reply port unless and until either this
	 * task frees the channel or another channel of
	 * higher precedence steals it.  Appropriate
	 * to keep two reply ports, perhaps... one for
	 * standard I/O replies, other for channel steal
	 * requests. 
	 */
	BeginIO(lockiob);
}

FreeAChannel(iob)
struct IOAudio *iob;
{
	/* allocation key and unit number must already be valid */
	iob->ioa_Request.io_Command = ADCMD_FREE;
	iob->ioa_Request.io_Flags = IOF_QUICK;
	BeginIO(iob);
	WaitIO(iob);
}

/* getaudio.c */


UBYTE
GetAnyChannel(iob)
struct IOAudio *iob;
{
	UBYTE anychan[4];
	UBYTE mychan;
	int error;

	anychan[0] = 1;
	anychan[1] = 2;
	anychan[2] = 4;
	anychan[3] = 8;

	iob->ioa_Request.io_Message.mn_Node.ln_Pri = 20;
	iob->ioa_Request.io_Command = ADCMD_ALLOCATE;
	iob->ioa_Data = (UBYTE *)anychan;
	iob->ioa_Length = 4;
	iob->ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK;
	BeginIO(iob); 
	error = WaitIO(iob);  /* returns nonzero if error */

	if(!(iob->ioa_Request.io_Flags & IOF_QUICK))
		GetMsg(iob->ioa_Request.io_Message.mn_ReplyPort);
	if(error)
		return(0);
	mychan = ((ULONG)(iob->ioa_Request.io_Unit)) & 0xFF;
	return(mychan);
}




#define		MAX_LETTERS		47
char *letters[MAX_LETTERS] =
{
	".-",				/* A */
	"-...",				/* B */
	"-.-.",				/* C */
	"-..",				/* D */
	".",				/* E */
	"..-.",				/* F */
	"--.",				/* G */
	"....",				/* H */
	"..",				/* I */
	".---",				/* J */
	"-.-",				/* K */
	".-..",				/* L */
	"--",				/* M */
	"-.",				/* N */
	"---",				/* O */
	".--.",				/* P */
	"--.-",				/* Q */
	".-.",				/* R */
	"...",				/* S */
	"-",				/* T */
	"..-",				/* U */
	"...-",				/* V */
	".--",				/* W */
	"-..-",				/* X */
	"-.--",				/* Y */
	"--..",				/* Z */
	"-----",				/* 0 */
	".----",				/* 1 */
	"..---",				/* 2 */
	"...--",				/* 3 */
	"....-",				/* 4 */
	".....",				/* 5 */
	"-....",				/* 6 */
	"--...",				/* 7 */
	"---..",				/* 8 */
	"----.",				/* 9 */
	"--..--",				/* , */
	".-.-.-",				/* . */
	"..--..",				/* ? */
	"-.-.-.",				/* ; */
	"---...",				/* : */
	"-..-.",				/* / */
	"-....-",				/* - */
	".----.",				/* ' */
	"-.--.-",				/* () */
	"..--.-",				/* _ */
};



Work(fname)
char *fname;
/*
 * Get strings and send them to the audio port.
 *
 * Dlen: 1000 millis/sec * 1/8 chars/dit * 1/25 min/chars * 1/60 secs/min
 */
{
	char	*cptr, line[132];
	FILE	*file;

	if ((file = fopen(fname, "r")) == NULL)
		finishup("can't open a file");
	ditlength = 75 / sendrate;		/* In 20 milliseconds */
 	ditcycles = 12 * (MORSE_C1000 / MORSE_PERIOD);
	dahcycles = ditcycles * 3;
	while (fgets(line, 132, file))
	{
		for (cptr = line; *cptr && (*cptr != '\n'); cptr++)
		{
			SendLetter(*cptr);
			PauseDit(3);
		}
	}
	fclose(file);
}


SendLetter(c)
char c;
/*
 * Send character c.
 */
{
	char	*cptr, *temp = ",.?;:;/-,{}_", *findchar();
	short	i;

	if (c == ' ')
	{
		PauseDit(MORSE_SPACE);
		return;
	}
	if (isalpha(c))
		c = tolower(c) - 'a';
	else if (isdigit(c))
		c = c - 'a' + 26;
	else
	{
		cptr = findchar(temp, c);
		c = 37 + cptr - temp;
	}
	cptr = letters[c];
	i = 0;
	for ( ; *cptr != 0; cptr++, i++)
	{
		if (i)
			PauseDit(1);
		/*
		 * We'll send each dit or dah.  We know it's done when WaitIO
		 *		returns.  I think.
		 */
		if (*cptr == '.')
			audioIOB->ioa_Cycles = ditcycles;
		else
			audioIOB->ioa_Cycles = dahcycles;
		BeginIO(audioIOB);
		WaitIO(audioIOB);
	}
}

char *findchar(s,c)
char *s,c;
{
	for ( ; *s; s++)
		if (*s == c)
			return(s);
	return(NULL);
}


PauseDit(ditcount)
short ditcount;
/*
 * Pause for ditcount dit periods.
 *
 *		Do this by setting the timer and waiting for it.
 */
{
	Delay((long)(ditcount * ditlength));
}
SHAR_EOF
cat << \SHAR_EOF > SendMorse.h
#include "stdio.h"
#include "exec/types.h"
#include "exec/memory.h"
#include "devices/audio.h"
SHAR_EOF
cat << \SHAR_EOF > SendMorse.uu

begin 644 SendMorse
M```#\P`````````#``````````(```>K````[@````$```/I```'JT[Z"G9.0
M5?_^3KH`NCM\``'__F!B,"W__DC`Y8`@;0`*(G`(``P1`"UF,C`M__Y(P.6`L
M(&T`"B)P"`!2B2\)3KH'>%A/2,`I0(`22JR`$FX*2'H`1DZZ`J!83V`6,"W_@
M_DC`Y8`@;0`*+S`(`$ZZ!2Q83U)M__XP+?_^L&T`"&V4+RR#GDZZ`TY83TAZ#
M`"].N@)F6$].74YU=7-A9V4Z(&-O9&4@6RUS96YD<F%T95T@/&9I;&5L:7-T1
M/@!A;&P@9&]N90!.5?_\3KH"L"E`@Y9.N@*H*4"#FDZZ`J`I0(.>2JR#EF<<5
M2JR#GF8$<`%@`G``2JR#FF8$<@%@`G(`@$%G"DAZ`8).N@'N6$](>``"2'@`?
M"$ZZ')Y03RE`@`YF"DAZ`7-.N@'06$]";?_^,"W__D'L@`(R+?_^(FR`#A.PV
M```0`%)M__X,;0`(__YMWD*G+RR#ED*G2'H!2DZZ',)/[P`02D!G"DAZ`4=.O
MN@&(6$\@;(.6*6@`%(`*0J="ITZZ&TA03RE`@Z)"IT*G3KH;.E!/*4"#IDJL^
M@Z)G!DJL@Z9F"DAZ`21.N@%,6$\@;(.6(6R`"@`4(&R#FB%L@`H`%"!L@Y8AN
M;(.B``X@;(.:(6R#I@`.+RR#EDZZ`BA83QM`__T@;(.6(FR#FC-H`"``("!LG
M@Y8B;(.:(V@`&``8<``0+?_]/P`O+(.:3KH!HEQ/+RR#FDZZ&HI83TI`9PI(#
M>@"^3KH`T%A/(&R#EB%L@`X`(B!L@Y8A?`````0`)B!L@Y8Q?`'T`"H@;(.6;
M,7P`0``L(&R#EC%\`^@`+B!L@Y8Q?``#`!P@;(.6$7P`$``>(&R#GB)L@Y9P9
M$"#94<C__$Y=3G5O=70@;V8@;65M;W)Y(0!O=70@;V8@;65M;W)Y(0!A=61I\
M;RYD979I8V4`875D:6\@9&5V:6-E('=O;B=T(&]P96XA`&-A;FYO="!C<F5AO
M=&4@82!P;W)T(0!!(&-H86YN96P@=V%S('-T;VQE;B$`3E4``$JL@`IG"B\L*
M@Y9.NAFH6$]*K(`.9PY(>``(+RR`#DZZ&L103TJL@Y9G""\L@Y9A<%A/2JR#;
MFF<(+RR#FF%B6$]*K(.B9PHO+(.B3KH:$%A/2JR#IF<*+RR#IDZZ&@!83R\M)
M``A(>@`43KH-NE!/0F=.NA<25$].74YU)6QS"@``3E7__$AY``$``DAX`$1.Y
MNAHJ4$\K0/_\("W__$Y=3G5.50``2'@`1"\M``A.NAHV4$].74YU3E4``"!M9
M``@Q?``-`!P@;0`(0B@`'B\M``A.NAC*6$].74YU3E4``"!M``@Q?``)`!P@(
M;0`($7P``0`>+RT`"$ZZ&*183R\M``A.NAIP6$].74YU3E7_^!M\``'__!M\_
M``+__1M\``3__AM\``C__R!M``@1?``4``D@;0`(,7P`(``<0>W__")M``@C3
M2``B(&T`""%\````!``F(&T`"!%\`$$`'B\M``A.NA@Z6$\O+0`(3KH:!EA/?
M.T#_^"!M``@(*````!YF#B!M``@O*``.3KH9>EA/2FW_^&<&<`!.74YU(&T`+
M""`H`!C`O````/\;0/_[$"W_^V#D+BT`+2XN+@`M+BTN`"TN+@`N`"XN+2X`I
M+2TN`"XN+BX`+BX`+BTM+0`M+BT`+BTN+@`M+0`M+@`M+2T`+BTM+@`M+2XMW
M`"XM+@`N+BX`+0`N+BT`+BXN+0`N+2T`+2XN+0`M+BTM`"TM+BX`+2TM+2T`*
M+BTM+2T`+BXM+2T`+BXN+2T`+BXN+BT`+BXN+BX`+2XN+BX`+2TN+BX`+2TM$
M+BX`+2TM+2X`+2TN+BTM`"XM+BTN+0`N+BTM+BX`+2XM+BTN`"TM+2XN+@`M`
M+BXM+@`M+BXN+BT`+BTM+2TN`"TN+2TN+0`N+BTM+BT``$Y5_W1(>@"@+RT`.
M"$ZZ!#103RM`_W1F"DAZ`(Y.NOT^6$\B+(`2<$M.N@\>*4"#LBE\````5(.J#
M<@,@+(.J3KH*;"E`@ZXO+?]T/SP`A$AM_WA.N@)L3^\`"DI`9SI![?]X*TC_`
M_&`<(&W__!`02(`_`&%&5$\_/``#3KH!A%1/4JW__"!M__Q*$&<*(FW__`P13
M``IFTF"N+RW_=$ZZ$"!83TY=3G5R`&-A;B=T(&]P96X@82!F:6QE`$Y5__9!7
M^@$(*TC_^`PM`"``"68./SP`!DZZ`2Q43TY=3G4,+0!A``EM"`PM`'H`"6\0=
M#"T`00`);2`,+0!:``EN&!`M``E(@#\`3KH!HE1/D'P`81M```E@0@PM`#``I
M"6T0#"T`.0`);@@$+0!'``E@*A`M``E(@#\`+RW_^$ZZ`)Q<3RM`__P@;?_\?
MT?P````ED>W_^#`(&T``"1`M``E(@$C`Y8!![(`6*W`(`/_\0FW_]F!,2FW_E
M]F<*/SP``4ZZ`(143R!M__P,$``N9@P@;(.6,6R#K``N8`H@;(.6,6R#L``N"
M+RR#EDZZ%5983R\L@Y9.NA<B6$]2K?_\4FW_]B!M__Q*$&:L8`#_$BPN/SLZ+
M.R\M+'M]7P``3E4``&`8(&T`"!`0L"T`#68(("T`"$Y=3G52K0`((&T`"$H0[
M9N!P`&#L3E4``#`M``A(P"(L@[).N@B<+P!.NA1`6$].74YU3E4``$CG#"`D#
M;0`(#!(`(&<&#!(`"68$4HI@\'H`#!(`+68&>@%2BF`(#!(`*V8"4HIX`&`6(
M($I2BA`02(`R!,+\``K003@`F'P`,!`22(!20$'L@1H(,``"``!FV$I%9P8PA
M!$1`8`(P!$S?!#!.74YU<``0+P`%L#P`8&,*L#P`>F($D#P`($YU<``0+P`%'
ML#P`0&,*L#P`6F($T#P`($YU3E4``$CG""`D;0`(4VT`#$IM``QO("\M``Y.=
MN@`X6$\X`+!\__]G#B!*4HH0A+A\``IG`F#60A*X?/__9A"U[0`(9@IP`$S?8
M!!!.74YU("T`"&#R3E4``$CG""`D;0`(+PI.N@`R6$\X`+!\__]G(C`$2,!@=
M%%.2".H``P`,</],WP003EU.=6#62H!G^EF`9^0P!&#J3E4``"\*)&T`""!2`
ML>H`!&4,+PIA%EA/)%].74YU(%)2DA`02(#`?`#_8.Q.50``2.<(,"1M``@00
M*@`,P#P`&&<*</],WPP03EU.=0BJ``(`#$JJ``AF""\*3KH.K%A/$"H`#$B`Y
M"```!V<P0>R!G"9($"L`#$B`P'P`A+!\`(1F##\\__\O"TZZ#61<3]?\````Y
M%D'L@U2WR&76/RH`$"\J``@0*@`-2(`_`$ZZ!;Q03S@`2D!N%$I$9@1P"&`"Z
M<!"!*@`,</]@`/]Z,`1(P"2J``C0J@`()4``!"!24I(0$$B`P'P`_V``_UI.\
M50``+PI.N@W<)$!*@&8(<``D7TY=3G4O"B\M``PO+0`(809/[P`,8.A.50``=
M2.<(("\M`!!.N@Q$6$]![(#2)$A*$F80.7P`!8-:<`!,WP003EU.=2\M``PO*
M"DZZ!<Q03TI`9P1<BF#8/RH`!"\M``A.N@.^7$\X`+!\__]F!'``8,H@;0`0=
M$40`#2!M`!`1?``!``P@+0`08+)A<$/L@U9%[(-6M<EF#C(\`!AK"'0`(L)1_
MR?_\*4^#7"QX``0I3H-@2.>`@`@N``0!*6<02_H`"$ZN_^)@!D*G\U].<T/ZP
M`"!.KOYH*4"#9&8,+CP``X`'3J[_E&`$3KH`&E!/3G5D;W,N;&EB<F%R>0!)A
M^0``?_Y.=4Y5```O"DAY``$``#`L@U3!_``&+P!.NA*N4$\I0(-H9A1"ITAY;
M``$``$ZZ$6!03RYL@UQ.=2!L@VA":``$(&R#:#%\``$`$")L@V@S?``!``H@"
M;(-<("R#7)"H``10@"E`@VP@;(-L(+Q-04Y80J=.NA)F6$\D0$JJ`*QG+B\MJ
M``PO+0`(+PI.N@"R3^\`##E\``&#<"!L@V@`:(````0@;(-H`&B````*8$1(.
M:@!<3KH2U%A/2&H`7$ZZ$E183RE`@W(@;(-R2J@`)&<0(&R#<B)H`"0O$4ZZA
M$!A83R\L@W(O"DZZ!))03REL@W*#=DZZ$"0@;(-H((!.NA!2(&R#:"%```9G:
M%DAX`^U(>@`L3KH0+E!/(&R#:"%```PO+(-V/RR#>DZZ\^I<3T)G3KH.7%1/'
M)%].74YU*@!.50``2.<,,"1M`!`@;0`(("@`K.6`*``@1"`H`!#E@"9`$!-(2
M@$C`T*T`#%2`.4"#?$*G,"R#?$C`+P!.NA%,4$\I0(-^9@A,WPPP3EU.=1`3L
M2(`_`"!+4H@O""\L@WY.N@%$3^\`"DAZ`3H0$TB`2,#0K(-^+P!.N@..4$\_'
M+0`.+PHO+(-^3KH#*D_O``I";(-Z)FR#?B1+$!-(@#H`L'P`(&<8NGP`"6<2B
MNGP`#&<,NGP`#6<&NGP`"F8$4HM@V`P3`"!M>@P3`")F+E*+($M2BQ`02(`ZF
M`&<>($I2BA"%NGP`(F80#!,`(F8$4HM@!D(J__]@`F#68#@@2U*+$!!(@#H`@
M9R:Z?``@9R"Z?``)9QJZ?``,9Q2Z?``-9PZZ?``*9P@@2E**$(5@SB!*4HI"?
M$$I%9@)3BU)L@WI@`/]:0A)"IS`L@WI20$C`Y8`O`$ZZ$#903RE`@W9F"$)L(
M@WI@`/[D>@`F;(-^8!XP!4C`Y8`@;(-V(8L(`"\+3KH'W%A/4D!(P-?`4D6ZS
M;(-Z;=PP!4C`Y8`@;(-V0K`(`&``_J8@`$SO`P``!"`(,B\`#&`"$-E7R?_\T
M9P9206`"0AA1R?_\3G5.50``/RT`##\\`P$O+0`(80903TY=3G5.50``2.</G
M,"1M``A.N@P&)FR#:'@`8`XP!,'\``9*LP@`9PY21+AL@U1M['H&8```R@@M`
M``$`#&<R2'C__R\*3KH-U%!/+`!G(B\&3KH."%A/+PI.N@V86$]*@&8.3KH-T
MHCH`L'P`S68``)!(>`/M+PI.N@VP4$\L`$J&9F0(+0````QF!'H!8'!(>`/N%
M+PI.N@V24$\L`&8(3KH-9CH`8%A(>``A2'H`EDZZ#WQ03RX`9PHO!TZZ#?A8K
M3V`B2'@``4AZ`(8O!DZZ#9A/[P`,2'C__T*G+P9.N@UN3^\`#&`F,"T`#,!\:
M!0"P?`4`9A@O!DZZ#-983WH$.46#6G#_3-\,\$Y=3G4P!,'\``8GA@@`,`3!X
M_``&($#1RS%M``P`!`@M``,`#&<02'@``4*G+P9.N@T43^\`##`$8,)D;W,N#
M;&EB<F%R>0```$Y5``!(YPP@."T`"$ZZ"KHP!,'\``8D0-7L@VA*1&T*N&R#?
M5&P$2I)F$#E\``*#6G#_3-\$,$Y=3G4P*@`$P'P``[!\``%F"CE\``6#6G#_F
M8.!P`#`M``XO`"\M``HO$DZZ#(Q/[P`,*@"PO/____]F#$ZZ#$(Y0(-:</]@:
MM"`%8+`P/'__8`0P+P`,(&\`!$H89OQ32")O``A30!#95\C__&<"0A`@+P`$1
M3G4P/'__8`0P+P`,4T!K%"!O``0B;P`(L0EF#%-(2AA7R/_V<`!.=6,$<`%.0
M=7#_3G4@;P`$(`@B;P`($-EF_$YU2.=P`#0!Q,`F`4A#QL!(0T)#U(-(0,#!)
M2$!"0-""3-\`#DYU3E4``$CG#C`D;0`(0J=(>@".3KH-N%!/*4"#MF8(3-\,M
M<$Y=3G4@;0`,(F@`)"\I``1.N@X`6$\H`&=22'H`;2!$+R@`-DZZ#=)03R9`K
M2H!G-$AX`^TO"TZZ"VY03RP`9R0@!N6`*@`@125H``@`I"5&`)Q(>`/M2'H`R
M.$ZZ"TI03R5``*`O!$ZZ#9Y83R\L@[9.N@N\6$]"K(.V8(!I8V]N+FQI8G)AX
M<GD`5TE.1$]7`"H`3E4``$AM``PO+0`(2'H$<$ZZ`)A/[P`,3EU.=4Y5``!(N
MYP@@)&T`#@QM``0`$F8((&T`""@08!Q*;0`,;PP@;0`(<``P$"@`8`H@;0`(>
M,!!(P"@`0FT`$DIM``QL$$1M``Q*A&P(1(0[?``!`!(R+0`,2,$@!$ZZ`XY!S
M[($(4XH4L```,BT`#$C!(`1.N@.$*`!FVDIM`!)G!E.*%+P`+2`*3-\$$$Y=)
M3G5.5?\B2.<(,"1M``@F;0`,0FW_^BMM`!#__"!+4HL0$$B`.`!G``+LN'P`B
M)68``LI"+?\P.WP``?_X.WP`(/_V.WPG$/_T($M2BQ`02(`X`+!\`"UF#D)MU
M__@@2U*+$!!(@#@`N'P`,&80.WP`,/_V($M2BQ`02(`X`+A\`"IF&"!M__Q4U
MK?_\.U#_\B!+4HL0$$B`.`!@,D)M__)@'#`M__+!_``*T$20?``P.T#_\B!+V
M4HL0$$B`.``P!%)`0>R!&@@P``(``&;4N'P`+F9:($M2BQ`02(`X`+!\`"IF%
M&"!M__Q4K?_\.U#_]"!+4HL0$$B`.`!@,D)M__1@'#`M__3!_``*T$20?``P9
M.T#_]"!+4HL0$$B`.``P!%)`0>R!&@@P``(``&;4.WP``O_PN'P`;&82($M25
MBQ`02(`X`#M\``3_\&`0N'P`:&8*($M2BQ`02(`X`#`$2,!@>#M\``C_[F`6O
M.WP`"O_N8`X[?``0_^Y@!CM\__;_[C\M__!(;?\P/RW_[B\M__Q.NOWD3^\`E
M#"M`_^HP+?_P2,#1K?_\8%H@;?_\6*W__"M0_^HO+?_J3KH"#%A/.T#_\&!*I
M(&W__%2M__PX$$'M_R\K2/_J$(1@*)"\````8V?B4X!GE)"\````"V<`_W19P
M@&>T58!G`/]R5X!G`/]T8,Q![?\PD>W_ZCM(__`P+?_PL&W_]&\&.VW_]/_PV
M2FW_^&=H(&W_Z@P0`"UG"B)M_^H,$0`K9BX,;0`P__9F)E-M__(@;?_J4JW_0
MZA`02(`_`$Z25$^P?/__9@IP_TS?#!!.74YU8!8_+?_V3I)43[!\__]F!'#_O
M8.12;?_Z,"W_\E-M__*P;?_P;MQ";?_N8"`@;?_J4JW_ZA`02(`_`$Z25$^PE
M?/__9@1P_V"P4FW_[B!M_^I*$&<*,"W_[K!M__1MSC`M_^[1;?_Z2FW_^&8HB
M8!@_/``@3I)43[!\__]F!G#_8`#_>%)M__HP+?_R4VW_\K!M__!NVF`6/P1.$
MDE1/L'S__V8&</]@`/]24FW_^F``_0HP+?_Z8`#_0DCG2`!"A$J`:@1$@%)$0
M2H%J!D2!"D0``6$^2D1G`D2`3-\`$DJ`3G5(YT@`0H1*@&H$1(!21$J!:@)$]
M@6$:(`%@V"\!81(@`2(?2H!.=2\!808B'TJ`3G5(YS``2$%*068@2$$V`30`Y
M0D!(0(##(@!(0#("@L,P`4)!2$%,WP`,3G5(028!(@!"04A!2$!"0'0/T(#3U
M@;:!8@22@U)`4<K_\DS?``Q.=2!O``0@"$H89OR1P"`(4X!.=4Y5``!(;(&RC
M/RT`"$ZZ``A<3TY=3G5.50``+P0X+0`(+RT`"C\$3KH`,%Q/N'P`"F8D(&T`!
M"A`H``Q(@`@```=G%#\\__\O+0`*3KH`]EQ/*!].74YU8/A.50``+PHD;0`*L
M(%*QZ@`$91@P+0`(P'P`_S\`+PI.N@#*7$\D7TY=3G4@4E*2$"T`"1"`2(#`(
M?`#_8.A.50``+PI![(&<)$@@2M7\````%B\(81!83T'L@U2UR&7J)%].74YUV
M3E4``$CG""`D;0`(>``@"F8*</],WP003EU.=4HJ``QG4@@J``(`#&<,/SS_G
M_R\*851<3S@`$"H`#4B`/P!.N@3R5$^(0`@J``$`#&<*+RH`"$ZZ`C!83P@JK
M``4`#&<4+RH`$DZZ`L)83R\J`!).N@(46$]"DD*J``1"J@`(0BH`##`$8(Y.Z
M5?_^2.<(("1M``A!^O]$*4B#@@@J``0`#&<*</],WP003EU.=0@J``(`#&<P<
M(!*0J@`(.``_!"\J``@0*@`-2(`_`$ZZ`H!03[!$9Q`(Z@`$``Q"DD*J``1PH
M_V#`#&W__P`,9A`(J@`"``Q"DD*J``1P`&"H2JH`"&8(+PI.N@":6$\,:@`!W
M`!!F*AMM``W__S\\``%(;?__$"H`#4B`/P!.N@(B4$^P?``!9J`P+0`,8`#_V
M:B2J``@P*@`02,#0J@`()4``!`CJ``(`#"!24I(0+0`-$(!(@,!\`/]@`/\^M
M3E4``"\*0>R!G"1(2BH`#&<8U?P````60>R#5+7(90AP`"1?3EU.=6#B0I)"?
MJ@`$0JH`""`*8.I.5?_\+PHD;0`(/SP$`$ZZ`,!43RM`__QF&#5\``$`$"`*`
MT+P````.)4``""1?3EU.=35\!```$`CJ``$`#"5M__P`"!`J``U(@#\`3KH`G
MXE1/2D!G!@`J`(``#&#.3E4``$CG`#`D;(-68!0F4B`J``10@"\`+PI.N@5PZ
M4$\D2R`*9NA"K(-63-\,`$Y=3G5.50``+PI!^O_&*4B#AD*G("T`"%"`+P!.3
MN@464$\D0$J`9@AP`"1?3EU.=22L@U8E;0`(``0I2H-6(`I0@&#F3E4``'``7
M,"T`""\`8;)83TY=3G5.50``2.<`,)?+)&R#5F`.(&T`"%&(L<IG$B9*)%(@?
M"F;N</],WPP`3EU.=2`+9P0FDF`$*5*#5B`J``10@"\`+PI.N@3"4$]P`&#83
M3E4``"\*,"T`",'\``8D0-7L@VA*;0`(;0XP+0`(L&R#5&P$2I)F#CE\``*#@
M6G#_)%].74YU,"T`",'\``8@;(-H+S`(`$ZZ`JA83TJ`9P1P`6`"<`!@V$Y5L
M```O+0`(3KH"<EA/2H!F#DZZ`GPY0(-:</].74YU<`!@^$Y5``!(YPP@."T`I
M"$ZZ`'`P!,'\``8D0-7L@VA*1&T*N&R#5&P$2I)F$#E\``*#6G#_3-\$,$Y=6
M3G4P*@`$P'P``V8*.7P`!8-:</]@Y'``,"T`#B\`+RT`"B\23KH";D_O``PJW
M`+"\_____V8,3KH!_#E`@UIP_V"X(`5@M$Y5__Q(>!``0J=.N@0P4$\K0/_\-
M"```#&<22FR#<&8(("W__$Y=3G5.N@`&<`!@]$Y5``!(>``$2'H`'DZZ`>`O+
M`$ZZ`@I/[P`,/SP``4ZZ``Q43TY=3G5>0PH`3E4``$JL@X)G!B!L@X).D#\M0
M``A.N@`(5$].74YU3E7__"\$,"T`"$C`*T#__$JL@VAG*'@`8`H_!$ZZ`-!4.
M3U)$N&R#5&WP,"R#5,'\``8O`"\L@VA.N@,,4$]*K(.&9P8@;(.&3I!*K(.*`
M9PHO+(.*3KH!S%A/2JR#CF<*+RR#CDZZ`;Q83TJL@Y)G"B\L@Y).N@&L6$\L9
M>``$""X`!`$I9Q0O#4OZ``I.KO_B*E]@!D*G\U].<TJL@W)F,$JL@WYG*#`L1
M@WQ(P"\`+RR#?DZZ`I)03S`L@WI20$C`Y8`O`"\L@W9.N@)\4$]@#DZZ`F@O<
M+(-R3KH"W%A/("W__"YL@UQ.=2@?3EU.=4Y5``!(YPX@."T`"#`$P?P`!B1`O
MU>R#:$I$;0JX;(-4;`1*DF80.7P``H-:</],WP1P3EU.=3`J``3`?(``9@@OU
M$DZZ``I83T*2<`!@X"(O``0L;(-D3N[_W"(O``0L;(-D3N[_@B(O``0L;(-D?
M3N[_.B(O``0L;(-D3N[_N"QL@V1.[O_*+&R#9$[N_WPB+P`$+&R#9$[N_RA,Y
M[P`&``0L;(-D3N[_K$SO``8`!"QL@V1.[O_B+&R#9$[N_\1,[P`.``0L;(-D*
M3N[_UDSO``X`!"QL@V1.[O^^(B\`!"QL@V1.[O^F3.\`#@`$+&R#9$[N_]!(J
MYP$$3.\@@``,+&R#8$ZN_Y1,WR"`3G4B;P`$+&D`%$[N_^(B;P`$+&R#8$[N2
M_BPB;P`$+&R#8$[N_CY.^@`"(F\`!"QL@V!.[OYB3E4``$CG""!(>/__3KH`H
MT%A/*`"PO/____]F"G``3-\$$$Y=3G5(>0`!``%(>``B3KH`N%!/)$!*@&8,X
M+P1.N@#H6$]P`&#6)6T`"``*%6T`#P`)%7P`!``(0BH`#A5$``]"ITZZ`)98,
M3R5``!!*K0`(9PHO"DZZ`%I83V`*2&H`%$ZZ`,!83R`*8)).50``+PHD;0`(F
M2JH`"F<(+PI.N@#<6$\5?`#_``@E?/____\`%'``$"H`#R\`3KH`;%A/2'@`]
M(B\*3KH`3%!/)%].74YU(F\`!"QL@V!.[OZ>("\`!"QL@V!.[OZV3OH``DSOJ
M``,`!"QL@V!.[O\Z3OH``B)O``0L;(-@3N[^VBQL@V!.[O]\3OH``B)O``0@6
M+P`(+&R#8$[N_RX@+P`$+&R#8$[N_K!.^@`"(&\`!"QL@V!.[OZ,(&\`!""(L
M6)!"J``$(4@`"$YU(&\`!$SO`@$`""(O`!`L;(-@3N[^1"QL@V`B;P`$("\`<
M"$[N_=@B;P`$+&R#8$[N_I@B;P`$+&R#8$[N_H9,[P`#``0L;(-@3N[^SB)OA
M``0L;(-@3N[^)B!O``0L;(-@3N[^@$SO`P``!"QL@[9.[O^@(&\`!"QL@[9.'
M[O^F(&\`!"QL@[9.[O^R``````/L`````0````$```KL`````````_(```/JM
M````U0!:?UH`IH&F```````````````%```$K```!*\```2T```$N0``!+T`S
M``2_```$Q```!,@```3-```$T```!-4```39```$W@``!.$```3D```$Z```-
M!.T```3R```$]@``!/H```3\```%````!04```4)```%#@``!1,```48```%)
M'@``!20```4J```%,```!38```4\```%0@``!4@```5.```%5```!5L```5BN
M```%:0``!7````5W```%?0``!80```6+```%D@````!R``````!R*P````)W9
M`````P%W*P```P)A````"0%A*P``"0)X````!0%X*P``!0(````````P,3(S;
M-#4V-S@Y86)C9&5F````("`@("`@("`@,#`P,#`@("`@("`@("`@("`@("`@L
M(""00$!`0$!`0$!`0$!`0$!`#`P,#`P,#`P,#$!`0$!`0$`)"0D)"0D!`0$!"
M`0$!`0$!`0$!`0$!`0$!`4!`0$!`0`H*"@H*"@("`@("`@("`@("`@("`@("N
M`@("0$!`0"```````````````````0`````!``````````````````````$!J
M`````0`````````````````````!`@````$`````````````````````````%
M`````````````````````````````````````````````````````````````
M`````````````````````````````````````````````````````````````
M`````````````````````````````````````````````````````````````
M`````````````````````````````````````````````````````````````
M`````````````````````````````````````````````````````````````
M`````````````````````````````````````````````````````````````
M`````````````````````````````````````````````````````````````
M`````````````````````````````````````````````````````````````
M%````^P````N`````````!0````8````'````"`````D````*````"P````P!
M````-````#@````\````0````$0```!(````3````%````!4````6````%P`8
M``!@````9````&@```!L````<````'0```!X````?````(````"$````B```\
M`(P```"0````E````)@```"<````H````*0```"H````K````+````"T````@
EN````+P```#`````Q````,@````````#\@```^L````!```#\K``)
``
end
size 8992
SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.