*************************************************************************
*									*
*	The 81st Track MusicDisk 1 Loader	by The 81st Track	*
*									*
*************************************************************************


		incdir	"asrc:include/"

		include	"exec/exec_lib.i"
		include	"hardware/custom.i"
		include	"hardware/blit.i"
		include	"devices/keyboard.i"
		include	"81/my_macros.i"

		org	$10000		; loader starts at $10000

BBa1save	equ	$7f400		; long for passing BB->Loader
BBLoadMemAddr	equ	$7f404		; long for passing BB->Loader
mt_data		equ	$28000		; address of uncrunched module
Hardware	equ	$dff000		; base of custom chip registers
PortA		equ	$bfe001		; CIA reg to test left mouse
LeftMouse	equ	6		; left mouse button bit number
NumOfTunes	equ	12		; number of tunes on the disk

*************************************************************************


MainCode	move.l	BBa1save,a1save		; get values passed from BB
		move.l	BBLoadMemAddr,LoadMemAddr
		bsr	OpenKeyboard		; setup keyboard system
		move.l	#Hardware,a6
		moveq.l	#0,d0			; read first tune before
		jsr	ReadTune		;   the main section runs
		bsr	SetupAll		; setup main data
		move.l	$6c.w,oldlevel3		; set up frame based stuff
		move.l	#newlevel3,$6c.w
		move.w	#$8010,intena(a6)
		move.w	#$0001,intena(a6)	; serial stuff
		move.w	#368,serper(a6)		; 9600 baud (PAL)
		jsr	mt_init			; start tune
		bsr	DrawTrack		; draw initial track number
		bsr	DrawTime		; draw initial time value
MainCodeLoop	bsr	WaitForFrame		; the main loop
		bsr	DoTimeDisplay		; update and/or display time
		bsr	ReadKeyboard		; look at keyboard
		jsr	mt_music		; play the music
		bsr	DecodeKeyboard		; find what keys are pressed
		lea	DecodedMatrix,a0
		tst.b	$4e(a0)			; Right Arrow?
		bne.s	NextTune		;   if so, goto next tune
		tst.b	$4f(a0)			; Left Arrow
		bne	PrevTune		;   if so, goto previous tune
		tst.b	$46(a0)			; DEL key
		bne	ClearNumEntry		;   if so, clear digit entry
		bsr	CheckNumKeys		; number keys (n.pad & main)
		tst.b	newtrack
		bne.s	TuneNumberOkay
		btst	#LeftMouse,PortA	; Left mouse?
		beq.s	PrevTune		;   if so, goto previous tune
		move.w	#0,potgo(a6)		; Right mouse?
		move.w	potinp(a6),d0
		and.w	#$0400,d0
		beq.s	NextTune		;   if so, goto next tune
		tst.w	mt_restartflag		; Tune at end?
		beq.s	MainCodeLoop		;   if not, continue loop
NextTune	move.b	TuneNumber,d0		;   else, goto next tune
		addq.b	#1,d0
		cmp.b	#NumOfTunes,d0
		bcc.s	NextTuneWrap		; if last tune, goto first
TuneNumberOkay	move.b	d0,TuneNumber		; get new tune and re-init
		move.l	#0,TimeMinutes		;   time display
		move.b	#50,TimeFramesTick
		bsr	DrawTime
		bsr	SetDots
		bsr	DrawTrack
		move.b	TuneNumber,d0
		jsr	ReadTune		; okay then, get the tune!
		move.w	#0,mt_restartflag
		bra	MainCodeLoop
NextTuneWrap	moveq.b	#0,d0
		bra.s	TuneNumberOkay
PrevTune	move.b	TuneNumber,d0
		subq.b	#1,d0
		bcc.s	TuneNumberOkay
		move.b	#NumOfTunes-1,d0
		bra.s	TuneNumberOkay

ClearNumEntry	move.b	#$ff,numdig1
		move.b	#0,newtrack
		bra	MainCodeLoop

CheckNumKeys	move.b	#0,newtrack
		lea	DecodedMatrix,a0
		tst.b	$0a(a0)			; 0 main keyboard
		bne.s	num0
		tst.b	$01(a0)			; 1 m k
		bne.s	num1
		tst.b	$02(a0)			; 2 m k
		bne.s	num2
		tst.b	$03(a0)
		bne.s	num3
		tst.b	$04(a0)
		bne.s	num4
		tst.b	$05(a0)
		bne.s	num5
		tst.b	$06(a0)
		bne.s	num6
		tst.b	$07(a0)
		bne.s	num7
		tst.b	$08(a0)
		bne.s	num8
		tst.b	$09(a0)			; 9 m k
		bne.s	num9
		tst.b	$0f(a0)			; 0 numeric keypad
		bne.s	num0
		tst.b	$1d(a0)			; 1 n k
		bne.s	num1
		tst.b	$1e(a0)			; 2 n k
		bne.s	num2
		tst.b	$1f(a0)			; 3 n k
		bne.s	num3
		tst.b	$2d(a0)
		bne.s	num4
		tst.b	$2e(a0)
		bne.s	num5
		tst.b	$2f(a0)
		bne.s	num6
		tst.b	$3d(a0)
		bne.s	num7
		tst.b	$3e(a0)
		bne.s	num8
		tst.b	$3f(a0)			; 9 n k
		bne.s	num9
		rts
num0		move.b	#0,d0
		bra.s	numokay
num1		move.b	#1,d0
		bra.s	numokay
num2		move.b	#2,d0
		bra.s	numokay
num3		move.b	#3,d0
		bra.s	numokay
num4		move.b	#4,d0
		bra.s	numokay
num5		move.b	#5,d0
		bra.s	numokay
num6		move.b	#6,d0
		bra.s	numokay
num7		move.b	#7,d0
		bra.s	numokay
num8		move.b	#8,d0
		bra.s	numokay
num9		move.b	#9,d0
numokay		cmp.b	#$ff,numdig1
		bne.s	numgotdig1
		move.b	d0,numdig1
		rts
numgotdig1	move.w	#0,d1
		move.b	numdig1,d1
		mulu	#10,d1
		add.b	d1,d0
		beq.s	numnottrack
		cmp.b	#NumOfTunes+1,d0
		bcc.s	numnottrack
		move.b	#1,newtrack
		move.b	#$ff,numdig1
		subq.b	#1,d0
		rts
numnottrack	move.b	#$ff,numdig1
		rts

newlevel3	movem.l	d0-d7/a0-a6,-(sp)
		move.l	#Hardware,a6
		btst	#5,intreqr+1(a6)
		beq.s	notvblank
		move.w	#1,vblankflag
notvblank	btst	#4,intreqr+1(a6)	; copper - level 3
		beq.s	notcopper
		move.w	#$10,intreq(a6)
		bsr	DoScroll		; the scroll text
		bsr	DoHMsg
notcopper	movem.l	(sp)+,d0-d7/a0-a6
		dc.w	$4ef9
oldlevel3	dc.l	0

*************************************************************************

OpenKeyboard	lea	keyio,a1
		move.l	#keyport,14(a1)
		lea	KeyName,a0
		moveq.l	#0,d0
		moveq.l	#0,d1
		move.l	4.w,a6
		jsr	_LVOOpenDevice(a6)
		rts

ReadKeyboard	move.l	4.w,a6
		lea	keyio,a1
		move.w	#KBD_READMATRIX,28(a1)
		move.l	#KeyMatrix,40(a1)
		move.l	#13,36(a1)
		jsr	_LVOSendIO(a6)
		move.l	#Hardware,a6
		rts

DecodeKeyboard	lea	KeyMatrix,a1
		lea	DecodedMatrix,a2
		moveq.b	#0,d4
		moveq.l	#15,d1
DK1		move.b	(a1)+,d0
		moveq.l	#7,d2
DK2		moveq.b	#0,d3
		roxr.b	#1,d0
		roxl.b	#1,d3
		move.b	d3,(a2)+
		add.b	d3,d4
		dbf	d2,DK2
		dbf	d1,DK1
		tst.b	prevkeys
		bne.s	holding
		move.b	d4,prevkeys
		rts
holding		lea	DecodedMatrix,a2
		moveq.l	#31,d0
holdloop	move.l	#0,(a2)+
		dbf	d0,holdloop
		move.b	d4,prevkeys
		rts

*************************************************************************

SetupAll	move.l	#NewCopper,cop1lc(a6)	; setup main copper list
		move.w	#0,copjmp1(a6)
		lea	screencolors,a0		; set them colour registers!
		lea	color(a6),a1
		moveq.l	#31,d0
SetupAllLoop	move.w	(a0)+,(a1)+
		dbf	d0,SetupAllLoop
		move.w	#$87e0,dmacon(a6)	; enable DMA
		bra.s	WaitForBlit		; wait for any blits to finish
						; (uses WaitForBlit rts!)

WaitForFrame	tst.w	vblankflag		; Wait for VBlank
		beq.s	WaitForFrame		;  (my own internal flag)
		move.w	#0,vblankflag
		rts

WaitForBlit	btst	#14,dmaconr(a6)		; Wait for Blit finished flag
WFBLoop		btst	#14,dmaconr(a6)
		bne.s	WFBLoop
		rts

*************************************************************************

DoScroll	bsr.s	WaitForBlit		; wait for blitter
		move.l	#$7fffffff,bltafwm(a6)	; then move it:	mask left bit
		move.l	#$f9f00000,bltcon0(a6)	;		shift 15 right
		move.w	#0,bltamod(a6)
		move.w	#0,bltdmod(a6)
		move.l	#scrollarea,bltapt(a6)
		move.l	#scrollarea-2,bltdpt(a6)	;	dest 1 word left
		move.w	#$0256,bltsize(a6)	; was 216 (bottom row missing)
		bsr.s	WaitForBlit		; wait for blit to finish
		sub.b	#1,scrollpixel		; 1 less blit to next char
		tst.b	scrollpixel		; if no more,
		beq.s	scrollnewchar		;   get new character
scrolldrawright	lea	charimage,a0		; draw right pixel of scroll
		lea	scrollarea+43,a1
		move.l	#7,d0
drawrightloop	move.b	(a1),d1
		move.b	(a0),d2
		lsr.b	#1,d1
		roxl.b	#1,d2
		roxl.b	#1,d1
		move.b	d1,(a1)
		move.b	d2,(a0)+
		add.l	#44,a1
		dbf	d0,drawrightloop
		rts
scrollnewchar	add.l	#1,scrollptr		; next character in message
		move.l	scrollptr,a0		; get message pointer
		tst.b	(a0)			; if char=0(NUL), restart
		beq.s	scrollrestart
		cmp.b	#32,(a0)		; if char<32, skip char
		bcs.s	scrollnewchar
		move.l	#0,d0			; get char (into long)
		move.b	(a0),d0
		sub.b	#32,d0			; get address of char def
		lsl.w	#3,d0
		add.l	#scrollfont,d0
		move.l	d0,a0
		lea	charimage,a1		; get address of bitmap
		move.l	(a0)+,(a1)+		; copy it!
		move.l	(a0)+,(a1)+
		move.b	#8,scrollpixel		; 8 blits until next char
		bra.s	scrolldrawright
scrollrestart	move.l	#scrollmessage,scrollptr	; pretty obvious!
		bra.s	scrollnewchar

DoHMsg		sub.b	#1,HCounter		; HCounter says when to send
		tst.b	HCounter		;   another character. It's
		beq.s	HSendChar		;   used as a speed control
		rts
HSendChar	move.l	hmsgptr,a0		; get character
HSendChar2	move.b	(a0)+,d0
		beq.s	HRestart		; if NULL, restart message
		cmp.b	#1,d0			; if CHR$(1), set new speed
		beq.s	HSetSpeed
		and.w	#$ff,d0			; add stop bit
		or.w	#$0100,d0
		move.w	d0,serdat(a6)		; send it!
		move.l	a0,hmsgptr
		move.b	HSpeed,HCounter		; set 'speed' counter
		rts
HRestart	move.l	#hmessage,hmsgptr	; pretty obvious!
		bra.s	HSendChar
HSetSpeed	move.b	(a0)+,HSpeed		; get speed
		bra.s	HSendChar2

*************************************************************************

DoTimeDisplay	cmp.b	#26,TimeFramesTick	; turn dots off?
		beq.s	DotsOff
		cmp.b	#1,TimeFramesTick	; turn dots on?
		beq.s	DotsOn
DotsOkay	subq.b	#1,TimeFramesTick	; decrement 50ths second
		beq.s	NewTimeDisplay		; if 0, need new time display
		rts
DotsOff		lea	TimeDImg,a1
		bra.s	DrawDots
DotsOn		lea	TimeDImg+TimeDImgSize,a1
DrawDots	lea	TimeDots,a0
		lea	TimeDMask,a2
		move.l	#TimeDImgRows,d0
		bsr	DrawDigit
		bra.s	DotsOkay
SetDots		lea	TimeDImg+TimeDImgSize,a1
		lea	TimeDots,a0
		lea	TimeDMask,a2
		move.l	#TimeDImgRows,d0
		bsr	DrawDigit
		rts
NewTimeDisplay	move.b	#50,TimeFramesTick	; reset 50ths second value
		lea	TimeSeconds+1,a0
		addq.b	#1,(a0)			; add 1 to low seconds
		cmp.b	#10,(a0)		; if not 10, display time
		bne.s	NewTimeOkay
		move.b	#0,(a0)			; zero low seconds
		addq.b	#1,-(a0)		; add 1 to high seconds
		cmp.b	#6,(a0)			; if not 6, display time
		bne.s	NewTimeOkay
		move.b	#0,(a0)			; zero high seconds
		addq.b	#1,-(a0)		; add 1 to low minutes
		cmp.b	#10,(a0)		; if not 10, display time
		bne.s	NewTimeOkay
		move.b	#0,(a0)			; zero low minutes
		addq.b	#1,-(a0)		; add 1 to high minutes
		cmp.b	#6,(a0)			; if not 6, display time
		bne.s	NewTimeOkay
		move.b	#0,(a0)			; zero high minutes
NewTimeOkay	bsr	DrawTime		; Draw Time
		rts

*************************************************************************

; This section reads a tune when given a number from 0 to 5. It 'knows' where
; the tunes are on disk by looking in a table

ReadTune	jsr	mt_end			; first, stop current tune
		move.l	a1save,a1		; get trackdisk IO req
		lea	TuneParamTable,a0	; get tune offset&length
		and.l	#$f,d0
		asl.l	#3,d0
		add.l	d0,a0
		move.w	#2,28(a1)		; "read"
		move.l	#$28000,d2		; target address
		move.l	(a0)+,d3
		move.l	(a0),d4
		move.l	d4,TuneLength
ReadLoop	move.l	d2,40(a1)
		move.l	d3,44(a1)
		move.l	#$200,36(a1)
		tst.b	loaderstyle
		beq	rtnowait
		bsr	WaitForFrame
rtnowait	move.l	4.w,a6
		jsr	_LVODoIO(a6)
		move.l	a1save,a1
		add.l	#$200,d2		; next block buffer address
		add.l	#$200,d3		; next block disk offset
		sub.l	#$200,d4		; one block less to load
		bne.s	ReadLoop		; if not all done, next block
		move.l	a1save,a1
		move.w	#9,28(a1)		; motor off (tidy!)
		move.l	#0,36(a1)
		jsr	_LVODoIO(a6)
		move.l	#Hardware,a6
		move.l	#$28000,a0		; relocate to loader mem
		move.l	LoadMemAddr,a1
		move.l	TuneLength,d0
		lsr.l	#2,d0
RelocTuneLoop	move.l	(a0)+,(a1)+
		dbf	d0,RelocTuneLoop
		move.l	LoadMemAddr,a1
		jsr	(a1)			; tune decrunches to $28000
		jsr	mt_init			; setup new tune
		move.b	#1,loaderstyle
		rts

*************************************************************************

	; This section draws the 'led' displays

; Routine to draw track number
DrawTrack	lea	TuneDigits,a0
		move.b	TuneNumber,d0
		addq.b	#1,d0
		and.l	#$ff,d0
		divu	#10,d0
		move.b	d0,d1
		swap	d0
		move.b	d0,d2
		lea	TrackImg,a1
		and.w	#$ff,d1
		and.w	#$ff,d2
		mulu	#TrackImgSize,d1
		mulu	#TrackImgSize,d2
		add.l	d1,a1
		lea	TrackMask,a2
		move.l	#TrackImgRows,d0
		bsr.s	DrawDigit
		lea	TrackImg,a1
		add.l	d2,a1
		bsr.s	DrawDigit
		rts

; routine to draw time value
DrawTime	lea	TimeDigits,a0
		lea	TimeMask,a2
		lea	TimeMinutes,a3
		move.l	#TimeImgRows,d0
		moveq.l	#3,d1
DrawTimeLoop	move.b	(a3)+,d2
		and.l	#$ff,d2
		mulu	#TimeImgSize,d2
		lea	TimeImg,a1
		add.l	d2,a1
		bsr.s	DrawDigit
		dbf	d1,DrawTimeLoop
		rts

; routine to draw digits (actually blits the image to the screen)
DrawDigit	move.l	a2,a5
		move.l	d0,d7
		lea	MaskBuffer,a4
		subq.l	#1,d0
DrawDigitLoop1	move.w	(a2)+,(a4)+
		addq.l	#2,a4
		dbf	d0,DrawDigitLoop1
		move.l	a5,a2
		move.l	d7,d0
		move.l	#loadscreen,d7
		moveq.l	#5,d6
DrawDigitLoop2	bsr	WaitForBlit
		move.w	#-2,bltamod(a6)
		move.w	#0,bltbmod(a6)
		move.w	#40,bltcmod(a6)
		move.w	#40,bltdmod(a6)
		move.b	(a0),d5
		and.l	#$ff,d5
		mulu	#44,d5
		add.l	d7,d5
		move.b	1(a0),d4
		and.l	#$ff,d4
		add.l	d4,d5
		move.l	a1,bltapt(a6)
		move.l	#MaskBuffer,bltbpt(a6)
		move.l	d5,bltcpt(a6)
		move.l	d5,bltdpt(a6)
		move.l	#-1,bltafwm(a6)
		move.b	2(a0),d4
		lsl.w	#8,d4
		lsl.w	#4,d4
		move.w	d4,bltcon1(a6)
		or.w	#$fe2,d4
		move.w	d4,bltcon0(a6)
		move.w	d0,d4
		lsl.w	#6,d4
		addq.w	#2,d4
		move.w	d4,bltsize(a6)
		add.l	#12760,d7
		add.l	d0,a1
		add.l	d0,a1
		dbf	d6,DrawDigitLoop2
		addq.l	#3,a0
		rts

*************************************************************************

; This is the copper list for the display

NewCopper	cmove	$6200,bplcon0		; some general screen stuff
		cmove	$0000,bplcon1
		cmove	$1b71,diwstrt
		cmove	$3dd1,diwstop
		cmove	$0030,ddfstrt
		cmove	$00d8,ddfstop
		cmove	$0000,bpl1mod
		cmove	$0000,bpl2mod

	; set up all sprites as null

		cmove	((nullsprite&$ffff0000)>>16),sprpt
		cmove	(nullsprite&$ffff),sprpt+$02
		cmove	((nullsprite&$ffff0000)>>16),sprpt+$04
		cmove	(nullsprite&$ffff),sprpt+$06
		cmove	((nullsprite&$ffff0000)>>16),sprpt+$08
		cmove	(nullsprite&$ffff),sprpt+$0a
		cmove	((nullsprite&$ffff0000)>>16),sprpt+$0c
		cmove	(nullsprite&$ffff),sprpt+$0e
		cmove	((nullsprite&$ffff0000)>>16),sprpt+$10
		cmove	(nullsprite&$ffff),sprpt+$12
		cmove	((nullsprite&$ffff0000)>>16),sprpt+$14
		cmove	(nullsprite&$ffff),sprpt+$16
		cmove	((nullsprite&$ffff0000)>>16),sprpt+$18
		cmove	(nullsprite&$ffff),sprpt+$1a
		cmove	((nullsprite&$ffff0000)>>16),sprpt+$1c
		cmove	(nullsprite&$ffff),sprpt+$1e

	; set up bitplane pointers

		cmove	((screenplane1&$ffff0000)>>16),bplpt
		cmove	(screenplane1&$ffff),bplpt+$02
		cmove	((screenplane2&$ffff0000)>>16),bplpt+$04
		cmove	(screenplane2&$ffff),bplpt+$06
		cmove	((screenplane3&$ffff0000)>>16),bplpt+$08
		cmove	(screenplane3&$ffff),bplpt+$0a
		cmove	((screenplane4&$ffff0000)>>16),bplpt+$0c
		cmove	(screenplane4&$ffff),bplpt+$0e
		cmove	((screenplane5&$ffff0000)>>16),bplpt+$10
		cmove	(screenplane5&$ffff),bplpt+$12
		cmove	((screenplane6&$ffff0000)>>16),bplpt+$14
		cmove	(screenplane6&$ffff),bplpt+$16

	; wait till low part of screen (end of scroll)

		dc.w	$ff09,$fffe,$ffdd,$fffe
		cwait	$21,$09

	; cause a copper interrupt request

		cmove	$8010,intreq

	; end of copper list

		cend

; This NULL long word is used to blank all the sprites
nullsprite	dc.l	0

*************************************************************************

a1save		dc.l	0
LoadMemAddr	dc.l	0

vblankflag	dc.w	0

; The data for where the tunes are held on the disk
TuneParamTable	dc.l	$07400,$1fc00,$27000,$21200	; tunes 1 and 2
		dc.l	$48200,$0c600,$54800,$18e00	; 3 and 4
		dc.l	$6d600,$06400,$73a00,$13000	; 5 and 6
		dc.l	$86a00,$0c200,$92c00,$15a00	; 7 and 8
		dc.l	$a8600,$04600,$acc00,$18600	; 9 and 10
		dc.l	$c5200,$0e800,$d3a00,$08200	; 11 and 12

; How big is the crunched tune? (temp for relocation)
TuneLength	dc.l	0

; What tune is playing?
TuneNumber	dc.b	0

; Loader style (wait4frame[1] or normal[0])
loaderstyle	dc.b	0

; Data for screen (and associated stuff)
loadscreen	incbin	"cdtv-audio.raw"
screenplane1	equ	loadscreen
screenplane2	equ	screenplane1+12760
screenplane3	equ	screenplane2+12760
screenplane4	equ	screenplane3+12760
screenplane5	equ	screenplane4+12760
screenplane6	equ	screenplane5+12760
screencolors	equ	screenplane6+12760

; Tune Number Display Data
; digit positions (Y Pos, X Byte, X Shift)
TuneDigits	dc.b	51,10,11,51,14,3

; Time Display Data
; digit positions (Y Pos, X Byte, X Shift)
TimeDigits	dc.b	145,6,2,145,8,4,145,10,10,145,12,12
; dot positions (Y Pos, X Byte, X Shift)
TimeDots	dc.b	148,10,4
; frames before next tick
TimeFramesTick	dc.b	50
; time data
TimeMinutes	dc.b	0,0	; first digit (0-5), second digit (0-9)
TimeSeconds	dc.b	0,0	; first digit (0-5), second digit (0-9)

; LED graphics data
ledgfx		incbin	"raw-led-gfx"
TimeImg		equ	ledgfx
TimeMask	equ	ledgfx+2280
TimeDImg	equ	ledgfx+2318
TimeDMask	equ	ledgfx+2630
TrackImg	equ	ledgfx+2656
TrackMask	equ	ledgfx+5416
TimeImgSize	equ	228
TimeDImgSize	equ	156
TrackImgSize	equ	276
TimeImgRows	equ	19
TimeDImgRows	equ	13
TrackImgRows	equ	23
MaskBuffer	ds.b	92

; The data for the messages
scrollarea	equ	screenplane1+11308	; pixel line 257
charimage	ds.b	8
scrollfont	include	"asrc:fontsrc/hsspace.fontsrc"
scrollptr	dc.l	scrollmessage
hmsgptr		dc.l	hmessage
scrollmessage	incbin	"load.msg"
		dc.b	0
hmessage	incbin	"hidden.txt"
		dc.b	0
scrollpixel	dc.b	1
HSpeed		dc.b	2
HCounter	dc.b	1

; If user types in new tune number, the data goes here
numdig1		dc.b	$ff
newtrack	dc.b	0

; Keyboard stuff
KeyName		dc.b	'keyboard.device',0
prevkeys	dc.b	0
		even
keyio		dcb.b	48,0
keyport		dcb.l	8,0
KeyMatrix	dcb.b	16,0
DecodedMatrix	dcb.b	128,0

*************************************************************************

		include	"nt20play.s"	; my own slightly modified player

fillersize	SET	$27ffe-*

		IFLT	fillersize
		FAIL	!!!	Loader overflowing module area
		ELSE
		ds.b	fillersize
		ENDC

; *** This rts has to go at $27ffe - the tunes JMP $27FFE after decrunch

		rts

		end