#include "audio\patches.h"
#include "audio\inttypes.h"
#include "audio\stdbool.h"
#include "audio\hardware.h"

bool Audio_BlockComplete;
uint8_t Audio_Page;
uint8_t *Audio_ReadPtrCopy;
uint8_t *Audio_WritePtrCopy;
uint8_t Audio_Sequence[] = {56, 56, 56, 56, 60, 60, 60, 60, 60, 60, 60, 60, 56, 56, 56, 56, 60, 60, 60, 60, 60, 60, 62, 66, 80, 66, 80, 66, 80, 66, 80, 94, 80, 108, 80, 122, 80, 136, 80, 94, 80, 108, 60, 60, 60, 60, 60, 60, 62, 150, 80, 164, 80, 150, 178};
uint8_t Audio_SeqStep;

#define Audio_BufferSize	4096
#define Audio_PageSize   	2048

#asm
Audio_BufferSize 		equ 4096
Audio_PageSize 			equ 2048 

	bsszp
Audio_ReadPtr  			ds 2
Audio_WritePtr 			ds 2
Audio_Sample 		 	ds 1
Audio_Phase			 	ds 1
Audio_RemainingBytes 	ds 2

	bss
Audio_Buffer   			ds Audio_BufferSize
Audio_BufferEnd			ds 1
		
	text
#endasm


Audio_TimerIRQ() interrupt
{
#asm
	lda Audio_Sample
	sta _AUDIO_OUTPUT_A
	sta _AUDIO_OUTPUT_B
	sta _AUDIO_OUTPUT_C
	sta _AUDIO_OUTPUT_D

	ldx Audio_ReadPtr
	ldy Audio_ReadPtr+1
	cpx Audio_WritePtr
	bne Audio_TimerIRQ_GetByteFromFIFO
	cpy Audio_WritePtr+1
	bne Audio_TimerIRQ_GetByteFromFIFO
	bra Audio_TimerIRQ_End 

Audio_TimerIRQ_GetByteFromFIFO:
	lda	Audio_Phase
	bne Audio_TimerIRQ_Phase1

Audio_TimerIRQ_Phase0:
	lda (Audio_ReadPtr)
	cmp	#$79
	beq Audio_TimerIRQ_Reset	
	and #$0F
	tax
	lda Audio_TimerIRQ_Table,x
 	clc
	adc Audio_Sample
	sta Audio_Sample
	lda #1
	sta Audio_Phase
	bra Audio_TimerIRQ_End	
	
Audio_TimerIRQ_Phase1:	
	lda (Audio_ReadPtr)
	lsr
	lsr
	lsr
	lsr
	tax
	lda Audio_TimerIRQ_Table,x
 	clc
	adc Audio_Sample
	sta Audio_Sample
	stz Audio_Phase
	bra Audio_IncReadPtr

Audio_TimerIRQ_Reset:
	stz Audio_Sample
	
Audio_IncReadPtr:
	ldx Audio_ReadPtr
	inx
	bne Audio_TimerIRQ_NoOverflow
	iny
Audio_TimerIRQ_NoOverflow:
	cpx #<Audio_BufferEnd
	bne Audio_TimerIRQ_NoWrap
	cpy #>Audio_BufferEnd
	bne Audio_TimerIRQ_NoWrap
	ldx #<Audio_Buffer
	ldy #>Audio_Buffer	
Audio_TimerIRQ_NoWrap:
	stx Audio_ReadPtr
	sty Audio_ReadPtr+1
	bra Audio_TimerIRQ_End

Audio_TimerIRQ_Table:
	dc.b -128,-64,-32,-16,-8,-4,-2,-1,0,1,2,4,8,16,32,64
	
Audio_TimerIRQ_End:
#endasm
}


void Audio_Init(void)
{	
	{
		uint16_t j;
		for (j = 0xFD20; j <= 0xFD3F; j++) POKE(j, 0x00);
	}
	
	MSTEREO = 0x00;	

	Audio_SeqStep = 0;
	Audio_Page = Audio_Sequence[Audio_SeqStep];
	
#asm
	pha

	stz Audio_Sample
	stz Audio_Phase
	
	lda #<Audio_Buffer
	sta Audio_ReadPtr
	sta Audio_WritePtr
	lda #>Audio_Buffer
	sta Audio_ReadPtr+1
	sta Audio_WritePtr+1
	
	pla
#endasm	

#asm
	php
	sei
#endasm
		
	InstallIRQ(6, Audio_TimerIRQ);
	TIM6CTLA = 0x40;
	TIM6CTLA = 0x00;
	TIM6CNT  = 0;
	TIM6BKUP = 143 - 1; /* 6993 Hz */
	INTRST   = 1 << 6; 
	TIM6CTLA = 0x98;
	
#asm
	plp
#endasm
}



void Audio_Play(void)
{	
	for (;;)
	{
#asm
	phx
	phy

	ldx Audio_WritePtr
	ldy Audio_WritePtr+1
	stx _Audio_WritePtrCopy
	sty _Audio_WritePtrCopy+1
	
	php
	sei
	ldx Audio_ReadPtr
	ldy Audio_ReadPtr+1
	plp
	stx _Audio_ReadPtrCopy
	sty _Audio_ReadPtrCopy+1	
	
	ply
	plx
#endasm	
	
		{
			int16_t UsedBytes;
			int16_t FreeBytes;
			
			UsedBytes = (Audio_WritePtrCopy >= Audio_ReadPtrCopy) ? 0 : Audio_BufferSize;
			UsedBytes += Audio_WritePtrCopy;
			UsedBytes -= Audio_ReadPtrCopy;
			FreeBytes = (Audio_BufferSize - 1) - UsedBytes;
			
			if (FreeBytes < Audio_PageSize) return;
		}
		
#asm
	pha
	phx
	phy

	stz _Audio_BlockComplete
	
	lda _Audio_Page
	
	ldx #%00000011
	ldy #%00000010		
	rept 8
		asl a
		bcs $+7
		stz _IODAT
		bra $+5
		sty _IODAT
		stx _SYSCTL1
		sty _SYSCTL1	
	endr
	stz _IODAT	
	
	ldx #<Audio_PageSize
	ldy #>Audio_PageSize
	stx Audio_RemainingBytes
	sty Audio_RemainingBytes+1
	
	ldx Audio_WritePtr
	ldy Audio_WritePtr+1 
Audio_Play_LoadByte:
	inx
	bne Audio_Play_NoOverflow
	iny
Audio_Play_NoOverflow:
	cpx #<Audio_BufferEnd
	bne Audio_Play_NoWrap
	cpy #>Audio_BufferEnd
	bne Audio_Play_NoWrap
	ldx #<Audio_Buffer
	ldy #>Audio_Buffer	
Audio_Play_NoWrap:
	
	lda _ROM0
	cmp #$97
	beq Audio_Play_BlockComplete
	sta (Audio_WritePtr)
	
	php
	sei
	stx Audio_WritePtr
	sty Audio_WritePtr+1	
	plp
	
	dec Audio_RemainingBytes
	bne Audio_Play_LoadByte
	dec Audio_RemainingBytes+1
	bne Audio_Play_LoadByte
	bra Audio_Play_End
	
Audio_Play_BlockComplete:	
	lda #1
	sta _Audio_BlockComplete
	
Audio_Play_End:
	ply
	plx
	pla
#endasm

		if (Audio_BlockComplete)
		{
			Audio_SeqStep++;
			if (Audio_SeqStep == sizeof(Audio_Sequence)) Audio_SeqStep = 0;
			Audio_Page = Audio_Sequence[Audio_SeqStep];
		}
		else
		{
			Audio_Page++;
		}			
	}
}
