#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[] = {31, 31, 31, 31, 33, 33, 33, 33, 33, 33, 33, 33, 31, 31, 31, 31, 33, 33, 33, 33, 33, 33, 34, 36, 43, 36, 43, 36, 43, 36, 43, 50, 43, 57, 43, 64, 43, 71, 43, 50, 43, 57, 33, 33, 33, 33, 33, 33, 34, 78, 43, 85, 43, 78, 92, 243, 112, 243, 119, 250, 124, 250, 149, 176, 190, 176, 204, 250};
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++;
        }          
    }
}
