;------------------------------------------------------------------------------------------------------------------------
; Copyright (c) 2007, Mukunda Johnson
; 
; All rights reserved.
; 
; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
; 
;     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
;     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
;     * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
;
;  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;-----------------------------------------------------------------------------------

.equ REG_APUI00	$2140
.equ REG_APUI01	$2141
.equ REG_APUI02	$2142
.equ REG_APUI03	$2143

.include "memmap.inc"		; replace with your memory definitions

.MACRO SPX_RECEIVE_MESSAGE
	sta spx_message
	and #15
	jsl MessageReceived		; SET THIS TO YOUR MESSAGE HANDLER
.ENDM

.define SPX_STACK_SIZE		$10		; increase stack size if neccesary

.define SPX_XMSOFFSET		$2100

.MACRO SPX_SYNC
	lda spx_validation		; load validation
-	cmp REG_APUI03			; sync
	bne -
.ENDM

.ramsection "spx_var" BANK 0 SLOT 1
spx_spvar:			db
spx_validation:		db
spx_package_adr:	dsb 3

spx_stack:			dsb SPX_STACK_SIZE*4	; FIFO stack
spx_stack_r:		dw						; stack read position
spx_stack_w:		dw						; stack write position

spx_var1:			dw
spx_var2:			dw
spx_var3:			dw
spx_var4:			dw

spx_message:		db

ddd:				db

.ends

.bank 0
.SECTION "SPX_SNES"

spx_binary:
.incbin "spx_binaries\spx_core.bin"
spx_lft:
.incbin "spx_binaries\spx_lft.bin"
spx_aft:
.incbin "spx_binaries\spx_aft.bin"
spx_aftf:
.incbin "spx_binaries\spx_aftf.bin"

.INDEX 16
	
;----------------------------------------------------------------------------------------------------
BootSPC:
	ldx #spx_binary
	lda #:spx_binary

	; x = source address	(word)	16-BIT INDEX
	; a = bank				(byte)	8-BIT AKKU
	
	sei						; disable interrupts during upload
	
	stx spx_var1			; store source address in var1
	sta spx_var1+2			; store bank

	REP #$20				; 16-bit akku
	lda #$BBAA				; Check if SPC is ready
-	cmp REG_APUI00			;
	bne -					;
	
	SEP #$20			; 8-bit akku
	ldy #0				; reset data counter
	lda #0				; reset port0 data (for protocol)
	xba					; swap out
	
	; load first block
	lda #$01				; port1 = NOT 0
	sta REG_APUI01			;
	
	REP #$20				; 16-bit akku
	lda [spx_var1], y		; load data transfer address
	iny						; increase data pointer
	iny						;
	sta REG_APUI02			; ports2/3 = TRANSFER ADDRESS
	
	SEP #$20				; 8-bit akku
	lda #$CC				; port0 = $CC (protocol)
	sta REG_APUI00			;
	
-	cmp REG_APUI00			; wait for SPC reply
	bne -					;
	
	REP #$20				; 16-bit akku
	lda [spx_var1], y		; load data LENGTH
	tax						; transfer to X
	SEP #$20				; 8-bit akku
	iny						; increase data pointer
	iny						; 
	lda [spx_var1], y		; load first byte (real data)
	iny						; increase pointer
	sta REG_APUI01			; port1 = data
	
	lda #0					; port0 = 0 (start sending data)
	sta REG_APUI00			; 
	
-	cmp REG_APUI00			; wait for SPC reply
	bne -					;
	
	dex						; prepare loop
	xba						; swap counter/data
	
scr_data_loop:
	lda [spx_var1], y		; load byte
	iny						; inc pointer
	xba						; swap to counter
	
-	cmp REG_APUI00			; check/wait for SPC reply
	bne -					;
	
	ina						; increase counter
	REP #$20				; port1 = data
	sta REG_APUI00			; port0 = counter
	SEP #$20				;
	
	xba						; swap to data
	
	dex						; decrease loop counter
	bne scr_data_loop		; loop
	
	xba						; swap to counter
	
-	cmp REG_APUI00			; check/wait for SPC reply
	bne -					;
	
	xba						; swap to data
	
	REP #$20				; 16-bit akku
	pha						; preserve
	lda [spx_var1], y		; load next block size
	tax						; transfer to X
	pla						; restore
	SEP #$20				; 8-bit akku
	
	iny						; increase data pointer
	iny						;
	cpx #0					; load another block? (if block size is 0, then no)
	beq scr_terminate		; ...
	
	REP #$20			; yes..
	pha					; preserve
	lda [spx_var1], y	; load something
	tax					; transfer to X
	pla					; restore
	SEP #$20			; 8-bit akku
	
	iny					; increase pointer
	iny					;
	lda #1				; port1 = NOT 0
	sta REG_APUI01		; 
	stx REG_APUI02		; port2/3 = transfer address
	xba					; swap to counter
	ina					; counter += 2, != 0
-	ina					;
	beq -				;

	sta REG_APUI00		; store in port0

-	cmp REG_APUI00		; wait for SPC reply
	bne -				;
	bra scr_data_loop	; jump to data loop
	
scr_terminate:				; no...
	stz REG_APUI01			; port1 = 0
	REP #$20				; 16-bit akku
	pha						; preserve
	lda [spx_var1], y		; load program start address
	tax						; transfer to X
	pla						; restore
	SEP #$20				; 8-bit akku
	
	stx REG_APUI02			; port2/3 = program start address
	xba						; swap to counter
	ina						; counter += 2, != 0
-	ina						; 
	beq -					;

	sta REG_APUI00			; port0 = counter
-	cmp REG_APUI00			; wait for SPC reply
	bne -					;
							; TRANSFER COMPLETE.
	cli						; enable interrupts

	jmp SPX_Init			; Initialize
;-----------------------------------------------------------------------------------------------------
SPX_Init:
	lda #0					; reset validation
	sta spx_validation
	sta spx_stack_w			; reset stack read/write
	sta spx_stack_r
	
	REP #$20
	lda #$FEED				; initial sync
-	cmp REG_APUI00			;
	bne -
	SEP #$20
	RTL

;-----------------------------------------------------------------------------------------------------
SPX_Transfer_XMS:
	; ayy = 24-bit address
	sty spx_var1			; save address
	sta spx_var2
	rep #$20				; 16bit everything
	rep #$10
	lda [spx_var1]			; load length
	tax						; x = length/3
	ldy #SPX_XMSOFFSET
	lda spx_var1
	pha
	sep #$20
	lda spx_var2
	jsl SPX_Transfer

	ply
	rtl
;-----------------------------------------------------------------------------------------------------
.accu 8
SPX_Transfer_LFT:
	sep		#$20				; 8-bit akku
	rep		#$10				; 16-bit index
	ldx		#(768/3)			; set transfer length (bytes/3)
	ldy		#(spx_lft & 65535)	; load snes offset
	phy							; push
	ldy		#$300				; $300-$5FF = linear frequency LUT
	lda		#:spx_lft			; get bank#

	JSL		SPX_Transfer		; transfer data

	ply							; free stack

	lda.b	#$1C				; set table
	sta		REG_APUI02			;
	stz		REG_APUI01			;
	JSL		SPX_SEND			;
	
	RTL							; return

;-----------------------------------------------------------------------------------------------------
SPX_Transfer_AFT:
	sep		#$20				; 8-bit akku
	rep		#$10				; 16-bit index
	ldx		#(768/3)			; set transfer length (bytes/3)
	ldy		#(spx_aft & 65535)	; load snes offset
	phy							; push
	ldy		#$300				; $300-$5FF = amiga period LUT
	lda		#:spx_aft			; get bank#
	JSL		SPX_Transfer		; transfer data
	ply							; free stack
	
	ldx		#1365				; 4096/3, rounded down
	ldy		#(spx_aftf & 65535)	; load snes offset
	phy							; push
	ldy		#$F000				; $F000-$FFFF = amiga->freq LUT
	lda		#:spx_aftf			; get bank#
	JSL		SPX_Transfer		; transfer data
	ply							; free stack
	lda		#$1C				; set table
	sta		REG_APUI02			;
	lda		#$01				;
	sta		REG_APUI01			;
	JSL		SPX_SEND			;
	RTL							; return
	
;-----------------------------------------------------------------------------------------------------
.INDEX 16
.ACCU 8

.MACRO SPX_TRANSFER_INCPOINTER
	iny					; increase pointer
	iny					;
	bpl ++				; check for overflow
	cpy #$8001			; check for an overflow reading
	bne +				; fix data if so
	dey					;
	dey					;
	lda [spx_var1], y	; read low byte
	sta REG_APUI00		; store
	ldy #$0000			; read high byte
	inc spx_var1+2		; next bank
	lda [spx_var1], y	;
	sta REG_APUI01		;
	iny					;
	lda spx_validation	;
	xba					;
	bra ++				;
+						; if not just increase bank#
	inc spx_var1+2		; increase bank#
	ldy #$0000			; reset counter
++
.ENDM

SPX_Transfer_SAMP:
	sei					; $14 = SAMPLE TRANSFER
	sta spx_var1+2		;
	SPX_SYNC			;
	lda #$14			;
	jmp SPX_TRANSFER_MOD;

SPX_Transfer:
	; SPX_Transfer
	; parameters:
	; a = file bank			:8
	; x = length/4			:16
	; y = spc offset		:16
	; stack:1 = snes_offset	:16
	
	; types
	;  0 = xms
	;  1 = freq table
	
	sei						; disable interrupts
	
	sta spx_var1+2			; store bank#
	
	SPX_SYNC				; sync with spc

	lda #$1A				; $1A = GENERIC TRANSFER
SPX_TRANSFER_MOD:
	sta REG_APUI02			; set message type
	
	REP #$20				; set spc write position
	tya						; 
	SEP #$20				;
	sta REG_APUI00			;
	xba						;
	sta REG_APUI01			;
	
	lda spx_validation		; validate data
	eor #128				;
	ora #1
	sta REG_APUI03			;
-	cmp REG_APUI03			; wait for spc to respond
	bne -
	
;	eor #128				; prepare transfer mode
	
	sta spx_validation		; save

	REP #$20		; 16-bit akku
	lda 4, S		; load file offset sp+4

	sec				; set carry
	sbc #$8000		; subtract
	tay				; transfer to y
	SEP #$20		; 8-bit akku
	lda #$80		; set offset in var1
	stz spx_var1	; reset mem pointers
	sta spx_var1+1	;

	REP #$20			; 16-bit akku
	
_stf_start:				; loop:
	
	lda [spx_var1], y	; load data
	sta spx_var3		; save
	
	SPX_TRANSFER_INCPOINTER
	
	sep #$20
	lda [spx_var1], y	; get third byte
	
	iny					; increase pointer
	bpl +
	ldy #$0000
	inc spx_var1+2
+
	sta spx_spvar		; store
	lda spx_validation	; get validation
	eor #128			;
	sta spx_validation	; update
	eor #128			; reverse

	phx					; preserve
	ldx spx_spvar		; get ready
	
-	cmp REG_APUI03		; sync with spc
	bne -				;

	lda spx_var3		; load byte1
	sta REG_APUI00		; store byte1
	lda spx_var3+1		; load byte2
	sta REG_APUI01		; store byte2
	stx REG_APUI02		; store byte3/validation
	plx					; restore
	rep #$20
	
	dex					; decrease counter
	bne _stf_start		; loop until finished

	sep #$20
	
	stz REG_APUI03		; send 0
	stz spx_validation
	lda #0
-	cmp REG_APUI03		; wait for reply
	bne -
	
	cli					; enable interrupts
	RTL					; return --make sure higher function frees stack space
	
;--------------------------------------------------------------------------------------------------------
.index 16
SPX_Queue:
	; a = $00/$01 message
	; x = $02/$03 params
	; accumulator can be 8 or 16 bit, do not read anything with it
	ldy spx_stack_w		; load stack position
	sta spx_stack, y	; store bytes0/1
	iny					; increase pointer
	iny
	txa					; get high word
	sta spx_stack, y	; store bytes 2/3
	iny					; increase pointer
	iny
	cpy #SPX_STACK_SIZE*4	; wrap to stack size
	bcc +
	ldy #0
+
	sty spx_stack_w		; save stack position
	rtl

;---------------------------------------------------------------------------------------------------------
.accu 8
SPX_Routine:
	; get messages
	lda REG_APUI00		; check if port0 is different
	cmp spx_message
	beq +
	SPX_RECEIVE_MESSAGE	; if so then a message was received
+
	lda spx_validation	; check if spc has processed last message
	cmp REG_APUI03
	beq +
	rtl			; not ready
+
	ldy spx_stack_r		; load stack read position
	cpy spx_stack_w		; exit function if it equals write position (no messages)
	bne +
	rtl
+
	lda spx_stack, y	; load byte0
	sta REG_APUI00		; store
	iny
	lda spx_stack, y	; load byte1
	sta REG_APUI01		; store
	iny
	lda spx_stack, y	; load byte2
	sta REG_APUI02		; store
	iny
	lda spx_stack, y	; load byte3
	lda spx_validation	; add validation
	and #128
	eor #128
	ora spx_stack, y	; store
	sta REG_APUI03		; message dispatched
	sta spx_validation	; save validation
	
	iny

	cpy #SPX_STACK_SIZE*4	; wrap read counter to stack size
	bcc +
	ldy #0
+
	sty spx_stack_r		; save

	rtl

SPX_Flush:				; flushes queue
	jsl SPX_Routine		; call routine
	ldy spx_stack_r		; check for more messages
	cpy spx_stack_w
	bne SPX_Flush		; loop
	rtl					; exit
	
SPXM_Play:
	ldx #$1E			; $1E = play message
	jmp SPX_Queue

SPXM_BuildDir:
	ldx #$1B				; $1B = build directory
	jmp SPX_Queue

SPXM_Reset:					; blocking function
	ldx #$1D				; $1D = RESET XMS
	jmp SPX_Queue

SPXM_SetVol:
	; a = volume
	ldx #$18				; $18 = set XM playback volume
	jmp SPX_Queue

SPX_SetVol:
	; a = volume L } 16bit akku
	; b = volume R } resets afterwards
	ldx #$19				; $19 = change main volume
	jmp SPX_Queue

.accu 8
.index 16
SPXS_Play:
	; a = volume [llllrrrr]
	; x = sample#/priority/frequency [0ccfffffssssssss]

	pha			; rearrange data
	rep #$20
	txa
	sep #$20
	pha
	rep #$20
	and #$FF00
	ora #$20	; $20 = play sample
	tax
	sep #$20
	pla
	xba
	pla
	rep #$20
	
	jsl SPX_Queue
	sep #$20
	rtl

SPXS_SetParam:
	; a = index
	; x = param
	xba				; rearrange data
	tax
	xba
	rep #$20
	ldx #$21		; $21 = message
	jsl SPX_Queue	; queue
	sep #$20
	rtl
		
SPX_SEND:					; blocking send
	lda spx_validation		; get validation
	eor #128				; change
	sta REG_APUI03			; set port data
	sta spx_validation		; save validation
-	cmp REG_APUI03			; wait for spc reply
	bne -

	RTL						; exit

SPXP_InstallPackage:
	; x = address (16-bit)	;
	; a = bank#
	stx spx_package_adr		; store offset
	sta spx_package_adr+2	; store bank
	rtl
	
SPXP_LoadSong:
	; x = index (16-bit)
	
	sep #$20						; 8bit akku
	txa								; a=x
	sta $4202						; multiply a*3
	lda #3							; 
	sta $4203		; 8 cycles...	; ...
	
	; setup send
	rep #$20						; read package address

	lda #$10
	nop
	nop
	nop								; small delay
	clc
	adc $4216						; add multiplication result
	tay								; y=result
	
	lda spx_package_adr				; read package address
	adc [spx_package_adr], y		; add song offset
	ora	#$8000						;
	pha								; save result
	sep #$20						; 8bit akku
	lda spx_package_adr+2			; load bank#
	iny
	iny
	adc [spx_package_adr], y		; add song offset
	ply								; restore address	
	
	jsl SPX_Transfer_XMS			; transfer song
	
	; transfer samples
	
	SPX_SYNC
	
	lda #$17						; ask for sample requests
	sta REG_APUI02
	jsl SPX_SEND
--
	
	; message received
	lda REG_APUI02					; satisfy request
	cmp #$9C						; 9c = no more samples
	beq +
	ldx REG_APUI00
	ldy #0
	jsl SPXP_LoadSample				; send sample
	
	jsl SPX_SEND					; sync
	
	jmp --
+
	rtl
	
SPXP_LoadSample:
	; x = index (16-bit)
	; y = spc address, 0=use next available
	
	rep #$20						; 16-bit akku
	txa								; a=x
	sta spx_var1					; save
	asl								; shift
	adc spx_var1					; add (a = index*3 now)
	phy
	ldy #4
	adc [spx_package_adr],y			; add sample table offset
	
	tay
	clc
	lda spx_package_adr
	adc [spx_package_adr], y
	ora #$8000						; wrap around ROM area if overflow
	sta spx_var1
	
	sep #$20
	lda spx_package_adr+2
	iny
	iny
	adc [spx_package_adr], y
	adc #0							; increase on overflow
	sta spx_var2
	
	rep #$20
	
	lda [spx_var1]					; load sample size/3
	tax								; transfer to x
	
	lda spx_var1					; load snes address
	
	ply
	pha
	sep #$20						; 8-bit akku
	lda spx_var2					; load bank#
	
	jsl SPX_Transfer_SAMP			; transfer data
	ply								; restore stack
	
	lda spx_validation				; get sample #
	eor #128
	sta REG_APUI03
-	cmp REG_APUI03
	bne -
	sta spx_validation
	
	lda REG_APUI00
	
	rtl								; return
	
.ENDS
