*********************************************************
*							*
* FurLess.a - Produced for an entry to the 'neatest	*
*	program under 2K' contest at the ASDF 010th	*
*	annual GURU meditation by:			*
*							*
*		Wesley Howe				*
*		10-Jun-90				*
*							*
* Assembled with CAPE (of course.)			*
*							*
*********************************************************
*
	EXEOBJ			;generate free-standing executable
	OBJFILE	"FurLess"	;output filename
	INCLUDE	"FurLess.i"	;definitions
*
* some register equates
*
BR	EQUR	A5		;data area
WN	EQUR	A4		;Window Pointer
CST	EQUR	A3		;CUSTOM chips
*
* some specific equates for this program
*
TEMPO1		EQU	48000
TEMPO2		EQU	42000
TEMPO3		EQU	40000
TEMPO4		EQU	40000
TEMPO5		EQU	30000
TEMPO6		EQU	40000
NUMSONGS	EQU	6
SAMPLELEN	EQU	32
BEETLEN 	EQU	(124*10)*4
BACHLEN		EQU	(124*10)*4
SCARLEN		EQU	(108*10)*4
LARLEN		EQU	(96*10)*4
RSUNLEN		EQU	(192*10)*4 ;9680 (192beats*10pairs/beat)*4bytes/pair
TEARLEN		EQU	(128*10)*4
*
* define our general data memory offsets
*
DDEF	MACRO	;name,size
\1	EQU	stemp
stemp	SET	stemp+\2
	ENDM
*
stemp	SET	0			;for DDEF macro
	DDEF	IntuitionBase,4		;to hold a pointer
	DDEF	GfxBase,4		;to hold a pointer
	DDEF	Screen,4		;to hold a pointer
	DDEF	Window,4		;to hold a pointer
	DDEF	NewPointer,12		;used for an empty sprite structure
	DDEF	MyScrn,NewScreen.SIZE	;we'll build a NewScreen here
	DDEF	MyWin,NewWindow.SIZE	;and a NewWindow here
	DDEF	Tempo,2			;runtime copy of Tempo in use
	DDEF	CurSong,2		;becomes an array index
	DDEF	seed,4			;random number seed goes here
	DDEF	NewX,4			;vars for screen fill routine
	DDEF	NewY,4
	DDEF	NewH,4
	DDEF	NewV,4
	DDEF	NewF,4
	DDEF	NewB,4
	DDEF	WBenchMsg,4		;saved from startup
	DDEF	FMAudio,IOAudio.SIZE	;build an IOAudio struct here
	DDEF	BodyIText,IntuiText.SIZE ;and a couple IntuiText structs
	DDEF	PosIText,IntuiText.SIZE
	DDEF	Note1,SAMPLELEN		;our waveform gets copied here
	DDEF	Data0,RSUNLEN*2		;twice the size of the LARGEST one
ChipSize	EQU	stemp
	IFGE	ChipSize-32768		;make sure we don't exceed signed
	FAIL	"DataSize too Large"	;limits for addressing mode used
	ENDC
*
*-------------------------------------------------------------------------
* This is our startup initialization and main program loop
*-------------------------------------------------------------------------
*
Strt:	movem.l	a2-a6/d2-d7,-(sp) ;save the regs in case someone calls us
	movea.l	4,A6			;good ole execbase
	move.l	#ChipSize,d0		;size defined by the DDEF macros
	move.l	#MEMF_CHIP!MEMF_CLEAR,d1 ;put it in chip for the audio stuff
	jsr	AllocMem(A6)		;but do it all so we only need one
	movea.l	d0,BR			;data area for baseregister addressing
	tst.l	d0
	bne.s	10$
	moveq	#103,d0			;insufficient free store error code
	bra	999$
10$:	movea.l	ExecBase.ThisTask(a6),a2 ;our task address
	tst.l	Process.pr_CLI(a2)	;see if we came from a CLI
	bne.s	40$
	lea	Process.pr_MsgPort(a2),a0 ;else get the WorkBench message
	jsr	WaitPort(a6)		;so we can reply it later
	lea	Process.pr_MsgPort(a2),a0 ;or else we'll eat memory
	jsr	GetMsg(a6)		;as WorkBench won't unload us
	move.l	d0,WBenchMsg(BR)	;if we don't reply
40$:	lea	CUSTOM,CST		;for the hardware hacking
	lea	IntuitionName(pc),a1	;open needed libs
	moveq	#33,d0			;version 33
	jsr	OpenLibrary(a6)
	move.l	d0,IntuitionBase(BR)
	beq	980$			;handle unlikely failure
	lea	GfxName(pc),a1
	moveq	#33,d0
	jsr	OpenLibrary(a6)
	move.l	d0,GfxBase(BR)
	beq	980$			;handle unlikely failure
	bsr	ScrnSet			;this routine initializes the screen
	beq	900$
	bsr	HogAudio		;this allocates all the audio chans
	tst.l	d0			;so we can hack on the hardware
	bne	900$
	lea	wavedata(pc),a0		;build a waveform in chip ram
	lea	Note1(BR),a1
	moveq	#(SAMPLELEN/2)-1,d1	;samples in note
70$:	move.b	(a0)+,d0
	move.b	d0,(a1)+		;copy of first half
	neg.b	d0			;second half is mirror image
	move.b	d0,((SAMPLELEN/2)-1)(a1) ;we preincremented this!
	dbra	d1,70$
	bsr	NewSong			;start the music
100$:	bsr	ColorOne		;draw a pretty square
	movea.l	Window.UserPort(WN),a0	;and see if we have any key-presses
	jsr	GetMsg(a6)
	tst.l	d0
	beq.s	100$			;nada, reloop
	movea.l	d0,a1
	move.l	IntuiMessage.Code(a1),d2 ;stash Code & Qualifier
	jsr	ReplyMsg(a6)
	andi.b	#$3f,d2		;LSHIFT!RSHIFT!CAPSLOCK!CONTROL!LALT!RALT
	bne.s	100$		;if any of above, reject 'em
	swap	d2		;we got two words with one long read
	cmpi.b	#$45,d2		;ESC raw keycode
	beq.s	200$
	cmpi.b	#$4c,d2		;UP ARROW raw keycode
	bne.s	120$
	move.w	Tempo(BR),d2	;to go faster, we make the period smaller
	cmpi.w	#5000,d2	;but let's not get too small
	bls	100$
	sub.w	#400,d2		;a handy step size
	move.w	d2,Tempo(BR)
	jsr	Disable(a6)	;so the next 2 instructions aren't apart
	move.w	d2,AUD0PER(CST)	;note here the order.. when we step the
	move.w	d2,AUD2PER(CST)	;other way, we'll reverse this order
	jsr	Enable(a6)	;restore normal state
	bra	100$
120$:	cmpi.b	#$4d,d2		;DOWN ARROW raw keycode
	bne.s	130$
	move.w	Tempo(BR),d2
	cmpi.w	#65000,d2	;we can't get larger than 64K
	bhi	100$
	add.w	#400,d2
	move.w	d2,Tempo(BR)
	jsr	Disable(a6)	;the order is changed because the audio
	move.w	d2,AUD2PER(CST)	;state machine will keep going and we can't
	move.w	d2,AUD0PER(CST)	;actually write both at once. This way we
	jsr	Enable(a6)	;will erase the small accumulated error.
	bra	100$
130$:	cmpi.b	#$5f,d2		;HELP raw keycode
	bne.s	150$
	bsr	DoRequest	;routine to pop up our Auto Requester
	bra	100$
150$:	cmpi.b	#$50,d2		;F1 raw keycode
	bne	100$
	bsr	musoff	;stop the old song
	bsr	Slower	;make sure audio stays shut off a while so it won't
	bsr	NewSong	;restart at the same location it was at.
	bra	100$
200$:	bsr	musoff
	bsr	ShareAudio	;return the resources
900$:	movea.l	IntuitionBase(BR),a6	;close the window
	move.l	Window(BR),d0
	beq.s	910$
	movea.l	d0,a0
	jsr	CloseWindow(a6)
910$:	move.l	Screen(BR),d0		;and the screen
	beq.s	980$
	movea.l	d0,a0
	jsr	CloseScreen(a6)
980$:	movea.l	4,a6			;and the libraries we opened
	move.l	IntuitionBase(BR),d0
	beq.s	990$
	movea.l	d0,a1
	jsr	CloseLibrary(a6)
	move.l	GfxBase(BR),d0
	beq.s	990$
	movea.l	d0,a1
	jsr	CloseLibrary(a6)
990$:	move.l	WBenchMsg(BR),d2	;if we came from WorkBench
	beq.s	995$			;we need to reply our startup msg
	jsr	Forbid(a6)		;so we don't get unloaded early
	movea.l	d2,a1
	jsr	ReplyMsg(a6)
995$:	movea.l BR,a1		;return the memory to the system
	move.l	#ChipSize,d0
	jsr	FreeMem(A6)
	moveq	#0,d0
999$:	movem.l	(sp)+,a2-a6/d2-d7	;restore the saved registers
	rts			;this send us back to our caller
*
*------------------------------------------------------------------------
* This routine is based on code developed by Erik Quackenbush. I just
* shamelessly expropriated it for my own use, by permission. Thanks, Erik.
* It uses a dithering pattern to get many effective colors out of a 3 plane
* (8-color) screen.
*------------------------------------------------------------------------
*
MyFlags	EQU	BORDERLESS!BACKDROP!ACTIVATE!SMART_REFRESH!NOCAREREFRESH
ColorOne:
	movem.l	a6/d2-d3,-(sp)	;save the regs we use
	movea.l	GfxBase(BR),a6	;we need this here
	bsr	Rand		;get a new number
	move.l	d0,d1		;here we chop it up into the pieces we
	andi.l	#7,d0		;will need later
	move.l	d0,NewF(BR)
	lsr.l	#3,d1
	move.l	d1,d0
	andi.l	#7,d0
	move.l	d0,NewB(BR)
	lsr.l	#3,d1
	move.l	d1,d0
	andi.l	#127,d0
	move.l	d0,NewH(BR)
	lsr.l	#7,d1
	move.l	d1,d0
	andi.l	#127,d0
	move.l	d0,NewV(BR)
	lsr.l	#7,d1
	move.l	d1,d0
	andi.l	#1023,d0
	subi.l	#200,d0
	move.l	d0,NewX(BR)
	move.l	NewF(BR),d0
	add.l	NewB(BR),d0
	move.l	seed(BR),d1
	lsr.l	d0,d1
	andi.l	#511,d1
	subi.l	#75,d1
	move.l	d1,NewY(BR)
	movea.l	Window.RPort(WN),a1
	move.l	NewF(BR),d0
	jsr	SetAPen(a6)		;set a new Front Pen color
	movea.l	Window.RPort(WN),a1
	move.l	NewB(BR),d0
	jsr	SetBPen(a6)		;and a new Back Pen color
	movea.l	Window.RPort(WN),a1
	move.l	NewX(BR),d0
	move.l	NewY(BR),d1
	move.l	d0,d2
	add.l	NewH(BR),d2
	move.l	d1,d3
	add.l	NewV(BR),d3
	jsr	RectFill(a6)		;this does the coloring for us
	movem.l	(sp)+,a6/d2-d3
	rts
*
*---------------------------------------------------------------------------
* This routine opens the screen and window. If it returns a zero (NULL)
* the calling code will know that we had a failure to open one or the
* other. The cleanup code will only close what was opened, so all resources
* will get returned even if only the OpenWindow fails. In order to stay
* pure, we initialize everything on the fly here, and some of the word sized
* fields that are adjacent get initialized as a single longword write.
*---------------------------------------------------------------------------
*
ScrnSet:
	movem.l	a6/d2-d3,-(sp)
	lea	MyScrn(BR),a0
	move.l	#(640<<16)!400,NewScreen.Width(a0)
	move.l	#(3<<16)!1,NewScreen.Depth(a0)
	move.l	#((HIRES!LACE)<<16)!CUSTOMSCREEN,NewScreen.ViewModes(a0)
	lea	furname(pc),a1
	move.l	a1,NewScreen.DefaultTitle(a0)
	movea.l	IntuitionBase(BR),a6
	jsr	OpenScreen(A6)
	move.l	d0,Screen(BR)		;sets flags
	beq	999$			;a failure if zero
	lea	MyWin(BR),a0
	move.l	d0,NewWindow.Screen(a0)
	move.l	#(640<<16)!400,NewWindow.Width(a0)
	move.b	#1,NewWindow.DetailPen(a0)
	move.l	#RAWKEY,NewWindow.IDCMPFlags(a0)
	move.l	#MyFlags,NewWindow.Flags(a0)
	move.l	#(256<<16)!20,NewWindow.MinWidth(a0)
	move.l	#(640<<16)!400,NewWindow.MaxWidth(a0)
	move.w	#CUSTOMSCREEN,NewWindow.Type(a0)
	jsr	OpenWindow(a6)
	move.l	d0,Window(BR)		;sets flags
	beq.s	999$			;a failure if zero
	movea.l	d0,WN			;Window
	movea.l	WN,a0
	lea	NewPointer(BR),a1
	moveq	#1,d0
	moveq	#16,d1
	moveq	#0,d2
	move.l	#0,d3
	jsr	SetPointer(a6)		;sets an invisible pointer up
	movea.l	Window.RPort(WN),a1	;this is the same as the
	lea	pattern(pc),a0		;SetAfPt macro
	move.l	a0,RastPort.AreaPtrn(a1)
	move.b	#1,RastPort.AreaPtSz(a1)
	movea.l	WN,a0
	jsr	ViewPortAddress(a6)
	movea.l	d0,a0
	lea	palette(pc),a1
	moveq	#8,d0
	movea.l	GfxBase(BR),a6
	jsr	LoadRGB4(a6)		;our colors
	moveq	#1,d0			;flag set! shows we were successful
999$:	movem.l	(sp)+,a6/d2-d3		;don't disturb the flags here
	rts
*
*--------------------------------------------------------------------------
* The next routine initializes an IOAudio structure and uses it to
* allocate all 4 audio channels for us. It returns NON-ZERO if we failed
* to get all 4 channels. Once we have allocated all the channels, we can
* ignore the audio.device and bang on the hardware registers ourselves
* for our own magic stuff.
*--------------------------------------------------------------------------
*
HogAudio:			;Err:D0 = HogAudio(execBase:A6)
	lea	FMAudio(BR),a1
	lea	AllUnits(pc),a0
	move.l	a0,IOAudio.ioa_Data(a1)	;allocation map
	moveq	#1,d0			;map size
	move.l	d0,IOAudio.ioa_Length(a1)
	move.b	#127,Node.ln_Pri(a1)	;full priority.. no stealing!
	move.w	#ADCMD_ALLOCATE,IORequest.io_Command(a1)
	move.b	#ADIOF_NOWAIT,IORequest.io_Flags(a1)
	lea	AudName(pc),a0		;devname
	moveq	#0,d0			;unit
	moveq	#0,d1			;flags
	jmp	OpenDevice(a6)		;this will rts for us
*
*--------------------------------------------------------------------------
* Pretty simple, just closes the device. Just be sure not to call this
* if the open wasn't successful.
*--------------------------------------------------------------------------
*
ShareAudio:
	lea	FMAudio(BR),a1
	jmp	CloseDevice(a6)		;this will rts for us
*
*--------------------------------------------------------------------------
* A quick and dirty pseudo-random number generator.
*--------------------------------------------------------------------------
*
Rand:	move.l	seed(BR),d0	;get the old seed
	add.l	d0,d0		;double it
	bhi.s	999$		;branch if no carry OR not zero
	eori.l	#$2de1ad29,d0	;somewhat random bits flipped
999$:	move.l	d0,seed(BR)	;save the new value as the next seed
	rts
*
*--------------------------------------------------------------------------
* This initializes and pops up an Auto Requester, giving information about
* the program usage. In order to stay pure, all the initialization of the
* IntuiText structures is done on the fly here.
*--------------------------------------------------------------------------
*
DoRequest:
	movem.l	a2-a3/a6/d2-d3,-(sp)
	lea	BodyIText(BR),a1
	lea	BodyText(pc),a0
	move.l	a0,IntuiText.IText(a1)
	lea	PosIText(BR),a2
	movea.l	a2,a3
	lea	PosText(pc),a0
	move.l	a0,IntuiText.IText(a2)
	moveq	#2,d0
	move.b	d0,IntuiText.FrontPen(a1)
	move.b	d0,IntuiText.FrontPen(a2)
	move.b	d0,IntuiText.FrontPen(a3)
	moveq	#4,d0
	move.w	d0,IntuiText.LeftEdge(a2)
	move.w	d0,IntuiText.LeftEdge(a3)
	move.w	d0,IntuiText.TopEdge(a2)
	move.w	d0,IntuiText.TopEdge(a3)
	moveq	#10,d0
	move.w	d0,IntuiText.LeftEdge(a1)
	move.w	d0,IntuiText.TopEdge(a1)
	movea.l	WN,a0
	moveq	#0,d0
	move.l	d0,d1
	move.w	#640,d2			;width
	moveq	#66,d3			;height
	movea.l	IntuitionBase(BR),a6
	jsr	AutoRequest(a6)		;won't return until 'OK' is clicked
	movem.l	(sp)+,a2-a3/a6/d2-d3
	rts
*
*--------------------------------------------------------------------------
* This routine is here merely to ensure that more than two cycle times for
* the audio channels passes so that when we restart we will be starting at
* the beginning instead of part way through a previous song. This would be
* bad if the new song was shorter than the old one was.
*--------------------------------------------------------------------------
*
Slower	move.l	a6,-(sp)
	movea.l	GfxBase(BR),a6
	jsr	WaitTOF(a6)	;waits for the next VBlank interval
	jsr	WaitTOF(a6)	;twice to be sure of one whole frame time
	movea.l	(sp)+,a6
	rts
*
*-----------------------------------------------------------------------
* This is the workhorse that builds the sequence tables that will be used
* by audio channels 0 and 2 (which we will set up to control channels
* 1 and 3). These table will become pairs of words alternating volume
* and period. The volume is computed by steadily decreasing the initial
* value used, while the period comes from a table which was indexed by
* a note number in the lower nibble of the song table. The upper nibble
* is the number of 'beats' for this note. The routine executes the code
* twice, once to build the right channels, and the second for the left.
*-----------------------------------------------------------------------
*
buildtab:		;newbuff:a1 = buildtab(nibtab:a0, buff:a1)
	movem.l	a2/d2-d5,-(sp)
	lea	note_table(pc),a2	;used for the whole routine
	moveq	#0,d1		;controls the execution path for left
10$:	move.b	(a0)+,d2	;null-term tables for each side
	beq.s	990$
	moveq	#64,d5		;full volume for right
	tst.w	d1
	beq.s	12$
	moveq	#44,d5		;a little softer for the accompaniement
12$:	move.b	d2,d3
	lsr.b	#4,d3		;upper nibble to lower nibble
	ext.w	d3		;zeroes upper byte of word
	mulu	#10,d3		;times ten gives beats for this note
	andi.w	#15,d2		;extract lower nibble
	add.w	d2,d2		;time 2 for word sized table
	move.w	0(a2,d2.w),d4	;get the period from the table
	bne.s	20$
	moveq	#0,d5		;silence indicated by note nibble of zero
	moveq	#127,d4		;some period needed for audio chip
20$:	cmpi.w	#1,d3		;make the last sample always a lower volume
	bne.s	25$		;to provide some wave shaping
	lsr.w	#1,d5		;by cutting it in half
25$:	move.w	d5,(a1)+	;write the volume
	beq.s	30$		;never go below zero
	subq.w	#1,d5		;volume decremented here
30$:	move.w	d4,d0		;retreive the period we got
	tst.b	d1		;for left half we use a lower octave
	beq.s	40$
	add.w	d0,d0		;which we get by doubling the period here
40$:	move.w	d0,(a1)+	;we write the period
	subq.w	#1,d3		;reduce the count
	bne.s	20$		;needs more beats to complete this note
	bra.s	10$		;else do next note
990$:	tst.w	d1
	bne.s	999$		;if nonzero we did two sides
	moveq	#1,d1		;otherwise, make nonzero and reloop
	bra.s	10$
999$:	movem.l	(sp)+,a2/d2-d5
	rts
*
*-------------------------------------------------------------------------
* This builds a table for the next song, wrapping from the last song back
* to the first one. When initially called, the song number is zero, and we
* start out on song number one. Thereafter, every time it is called it
* increments the current song.
*-------------------------------------------------------------------------
*
NewSong:
	move.l	d2,-(sp)
	move.w	CurSong(BR),d2	;get former song number
	addq.w	#1,d2		;and bump it up one
	cmpi.w	#NUMSONGS,d2	;check our limits
	bls.s	10$
	moveq	#1,d2		;wrap back to the first one after the last
10$:	move.w	d2,CurSong(BR)	;save computed number
	add.w	d2,d2		;double it.. we're using word-sized tables
	lea	TempoTab(pc),a0
	move.w	-2(a0,d2.w),Tempo(BR) ;lets us have element 1 in slot 0
	lea	Data0(BR),a1	;our buffer (in chip ram!)
	lea	SongTab(pc),a0	;some address arithmetic
	adda.w	-2(a0,d2.w),a0
	bsr	buildtab	;build the table
	lea	LenTab(pc),a0	;lookup the length
	move.w	-2(a0,d2.w),d1
	bsr.s	muson		;start (or restart) the music
	move.l	(sp)+,d2
	rts
*
*------------------------------------------------------------------------
* Way down here is where we write to the hardware registers. What we will
* do is attach channels 0 and 2 to channels 1 and 3 for period and volume.
* This pairing makes one channel write the volume and period alternately
* to the next numbered channel. Channels 1 and 3 always play the same
* waveform, but by being controlled by channels 0 and 2 the actual pitch
* and volume vary depending on the data used by channels 0 and 2. This data
* came from the buildtab() routine, which expanded the compressed stuff
* in the nibbleized song tables.
*------------------------------------------------------------------------
*
muson:				;muson(LEN:D1)
	lea	Data0(BR),a0	;the chip ram buffer we used
	move.l	a0,AUD0LC(CST)	;right table starts at the start
	adda.w	d1,a0		;while the left table is right after it
	move.l	a0,AUD2LC(CST)
	lsr.w	#1,d1		;half for len_in_words (chip requirement)
	move.w	d1,AUD0LEN(CST)	;set length to channels
	move.w	d1,AUD2LEN(CST)
	move.w	Tempo(BR),d0	;speed for our particular song
	move.w	d0,AUD0PER(CST)
	move.w	d0,AUD2PER(CST)
	lea	Note1(BR),a0	;the modified sine wave we used
	move.l	a0,AUD1LC(CST)	;initialize registers
	move.l	a0,AUD3LC(CST)
	moveq	#SAMPLELEN/2,d0	;half for len_in_words (chip requirement)
	move.w	d0,AUD1LEN(CST)	;length of wave data to chip registers
	move.w	d0,AUD3LEN(CST)
	move.w	#MSET!ATVOL0!ATPER0!ATVOL2!ATPER2,ADKCONW(CST) ;attach stuff
	move.w	#MSET!DMAEN!AUD3EN!AUD2EN!AUD1EN!AUD0EN,DMACONW(CST) ;Play it!
	rts
*
*--------------------------------------------------------------------------
* This just shuts the sound channels off and unattaches them from each
* other, to restore things to more or less normal.
*--------------------------------------------------------------------------
*
musoff:	move.w	#CLEAR!AUD3EN!AUD2EN!AUD1EN!AUD0EN,DMACONW(CST) ;sound off
	move.w	#CLEAR!ATVOL0!ATPER0!ATVOL2!ATPER2,ADKCONW(CST) ;unattach
	rts
*
*--------------------------------------------------------------------------
* From here on are all static data tables used by various of the routines.
*--------------------------------------------------------------------------
*
*--------------------------------------------------------------------------
* The addresses of these two arrays are passed to the graphics routines.
*--------------------------------------------------------------------------
*
palette	DC.W	$888,$111,$f11,$1f1,$11f,$ff1,$f1f,$1ff
pattern	DC.L	$AAAA5555
*
*--------------------------------------------------------------------------
* The wave was computed by creating a sine wave, and adding 10% third
* harmonic and 1% fifth harmonic content. Only the positive half of the
* wave form is here, the negative half is computed at the time we copy
* this data to the chip ram area.
*--------------------------------------------------------------------------
*
wavedata
	DC.B	0,32,61,84,98,106,112,114,115,114,112,106,98,84,61,32
*
*--------------------------------------------------------------------------
* This table is the lookup for each note's proper period value. We shift
* this value left once to double the duration for a lower octave when we
* are building the left channel (accompaniement voice).
*--------------------------------------------------------------------------
*
note_table
	DC.W	0	;dummy note = silence
	DC.W	285	;G  1
	DC.W	269	;G# 2
	DC.W	254	;A  3
	DC.W	240	;A# 4
	DC.W	226	;B  5
	DC.W	214	;C  6
	DC.W	202	;C# 7
	DC.W	190	;D  8
	DC.W	180	;D# 9
	DC.W	170	;E  A
	DC.W	160	;F  B
	DC.W	151	;F# C
	DC.W	144	;G  D
	DC.W	135	;G# E
	DC.W	127	;A  F
*
*--------------------------------------------------------------------------
* These lengths are used in address arithmetic to split the larger buffer
* into two halves and to calculate the correct length for the chip regs.
*--------------------------------------------------------------------------
*
LenTab:	DC.W	BEETLEN
	DC.W	BACHLEN
	DC.W	SCARLEN
	DC.W	LARLEN
	DC.W	RSUNLEN
	DC.W	TEARLEN
*
*--------------------------------------------------------------------------
* These are the default speeds for each song.
*--------------------------------------------------------------------------
*
TempoTab:
	DC.W	TEMPO1
	DC.W	TEMPO2
	DC.W	TEMPO3
	DC.W	TEMPO4
	DC.W	TEMPO5
	DC.W	TEMPO6
*
*--------------------------------------------------------------------------
* The address of the base of this table plus the difference stored at the
* proper offset gives us the start of the proper nibble table.
*--------------------------------------------------------------------------
*
SongTab	DC.W	beettab-SongTab
	DC.W	bachtab-SongTab
	DC.W	scbftab-SongTab
	DC.W	lrdotab-SongTab
	DC.W	rsuntab-SongTab
	DC.W	teartab-SongTab
*
*-------------------------------------------------------------------------
* This is where data for the actual songs is stored. It is compressed into
* bytes containing packed nibbles of duration:note. A note nibble of zero
* means silence, while the number in the duration is the number of beats
* each note is to play. Each beat will be multiplied by 10 samples when
* the sequences are used. Each song's data here is the right channel first,
* with a null byte marking the end, followed by the left channel, which is
* also null-terminated. It is important that the two halves contain the
* same number of beats. The number of beats in one side is counted and is
* used 'way up top to calculate the length needed for each song. The memory
* required is calculated from the largest song in these tables. For proper
* operation, this part should be right after the SongTab table.
*-------------------------------------------------------------------------
*
beettab	DC.B	$1A,$19,$1A,$19,$1A,$15,$18,$16,$63,$65,$46		;24
	DC.B	$1A,$19,$1A,$19,$1A,$15,$18,$16,$63,$45,$16,$15,$43	;24
	DC.B	$1A,$19,$1A,$19,$1A,$15,$18,$16,$63,$65,$46		;24
	DC.B	$1A,$19,$1A,$19,$1A,$15,$18,$16,$63,$45,$16,$15,$43 	;24
	DC.B	$18,$16,$18,$4A,$1B,$1A,$48,$1A,$18,$46,$18,$16,$75,0	;28
	DC.B	$A0,$13,$16,$2A,$20,$15,$1A,$2D,$20,$16,$1A		;24
	DC.B	$A0,$13,$16,$2A,$20,$15,$1A,$2D,$46			;24
	DC.B	$A0,$13,$16,$2A,$20,$15,$1A,$2D,$20,$16,$1A		;24
	DC.B	$A0,$13,$16,$2A,$20,$15,$1A,$2D,$46			;24
	DC.B	$50,$13,$16,$40,$1D,$15,$40,$1A,$1D,$40,$1A,$2E,$20,0	;28
bachtab	DC.B	$1D,$11,$13,$15,$18,$16,$16,$1A,$18,$18,$1D,$1C
	DC.B	$1D,$18,$15,$11,$13,$15,$16,$18,$1A,$18,$16,$15
	DC.B	$13,$15,$13,$13,$15,$18					;30
	DC.B	$1D,$11,$13,$15,$18,$16,$16,$1A,$18,$18,$1D,$1C
	DC.B	$1D,$18,$15,$11,$13,$25,$1A,$18,$16,$15,$13
	DC.B	$13,$11,$13,$31						;30
	DC.B	$65,$36,$38,$31,$38,$36,$35,$63				;30
	DC.B	$1D,$11,$13,$15,$18,$16,$16,$1A,$18,$18,$1D,$1C
	DC.B	$1D,$18,$15,$11,$13,$25,$1A,$18,$16,$15,$13
	DC.B	$11,$15,$18,$7D,0					;34
	DC.B	$30,$31,$3A,$38,$31,$35,$36,$38,$33,$33			;30
	DC.B	$38,$31,$3A,$38,$31,$35,$36,$35,$33,$31			;30
	DC.B	$65,$3A,$33,$35,$38,$36,$35,$63				;30
	DC.B	$38,$31,$3A,$38,$31,$35,$36,$35,$38,$71,0		;34
scbftab	DC.B	$33,$33,$2A,$2A,$2A,$25,$26,$25,$93			;27
	DC.B	$3A,$3D,$4F,$2D,$2A,$2C,$2A,$6A,$3F			;27
	DC.B	$3F,$3F,$2D,$2A,$2A,$2A,$28,$26,$28,$75			;27
	DC.B	$33,$3A,$38,$36,$25,$23,$21,$93,0			;27
	DC.B	$63,$61,$6A,$93,$66,$68,$6A,$9A
	DC.B	$63,$61,$6B,$9A,$63,$61,$6A,$93,0			;120
lrdotab	DC.B	$2d,$2d,$2b,$2a,$2b,$2d,$2b,$2a,$28,$26,$25,$31		;25
	DC.B	$11,$26,$26,$26,$28,$2a,$2b,$2a,$28,$26,$48		;23
	DC.B	$2d,$2d,$2b,$2a,$2b,$2d,$2b,$2a,$28,$26,$25,$31		;25
	DC.B	$11,$26,$26,$26,$28,$2a,$2b,$2a,$26,$26,$46,0		;23
	DC.B	$20,$66,$6f,$66,$61,$66,$61,$66,$61
	DC.B	$66,$6b,$66,$61,$66,$61,$66,$46,0			;96
rsuntab	DC.B	$30,$33,$93,$33,$96,$3a,$98,$23,$13,$96			;51
	DC.B	$3f,$9f,$3f,$9d,$2a,$18,$aa,$b0				;48
	DC.B	$2f,$1f,$9f,$3f,$2d,$7a,$38,$2a,$53,$23,$33,$96		;48
	DC.B	$33,$93,$33,$91,$33,$a3,$80,0				;45
	DC.B	$2a,$25,$22,$23,$26,$2A,$2f,$2A,$26		;18 E,Am
	DC.B	$26,$2a,$2d,$26,$2a,$2d				;12 C
	DC.B	$28,$2c,$2f,$28,$2f,$2c				;12 D
	DC.B	$2b,$23,$26,$2b,$26,$23				;12 F
	DC.B	$23,$26,$2A,$2f,$2A,$26				;12 Am
	DC.B	$26,$2a,$2d,$26,$2a,$2d				;12 C
	DC.B	$2a,$22,$25,$2a,$25,$22				;12 E
	DC.B	$2a,$22,$25,$2a,$25,$22				;12 E
	DC.B	$23,$26,$2A,$2f,$2A,$26				;12 Am
	DC.B	$26,$2a,$2d,$26,$2a,$2d				;12 C
	DC.B	$28,$2c,$2f,$28,$2f,$2c				;12 D
	DC.B	$2b,$23,$26,$2b,$26,$23				;12 F
	DC.B	$23,$26,$2A,$2f,$2A,$26				;12 Am
	DC.B	$2a,$22,$25,$2a,$25,$22				;12 E
	DC.B	$23,$26,$2A,$2f,$2A,$26				;12 Am
	DC.B	$2a,$22,$25,0					;6  E
teartab	DC.B	$20,$26,$28,$2a,$28,$28,$23,$26,$56,$13,$26,$a5		;34
	DC.B	$26,$28,$2a,$28,$28,$23,$26,$56,$13,$26,$a5		;32
	DC.B	$1b,$1b,$2b,$2a,$28,$28,$26,$48,$2a,$2a,$28,$26,$83	;32
	DC.B	$2f,$2f,$2f,$3f,$13,$25,$26,$58,$16,$18,$16,$85,0	;30
	DC.B	$46,$46,$48,$48,$4b,$4b,$4d,$4b				;32
	DC.B	$46,$46,$48,$48,$4b,$4b,$4d,$4b				;32
	DC.B	$4b,$4b,$4d,$4d,$46,$46,$43,$43				;32
	DC.B	$4b,$4b,$4b,$4b,$41,$41,$4d,$4b,0			;32
*
*---------------------------------------------------------------------------
* These are just strings for some of the routines.
*---------------------------------------------------------------------------
*
furname		CSTRING	'  FurLess by Wesley Howe  '
AudName		CSTRING 'audio.device'
IntuitionName	CSTRING	'intuition.library'
GfxName		CSTRING	'graphics.library'
BodyText	CSTRING	' ESC=QUIT, F1=Song, UP/DOWN=Chg Tempo'
PosText		CSTRING	'OK'
*
*---------------------------------------------------------------------------
* This is the allocation map for all four audio channelsthat we used to
* indicate we wanted all the channels when we opened the audio.device.
*---------------------------------------------------------------------------
AllUnits	DC.B	$0f
		DS.W	0
*
		END	;and that's all, folks!
*
