SPC music playback tools for real snes apu

Dependencies:   mbed

apu.cpp

Committer:
akkera102
Date:
2017-01-19
Revision:
8:072621697467
Parent:
2:62e6e22f8be2

File content as of revision 8:072621697467:

#include "apu.h"
#include "mbed.h"
#include "gpio.h"

// cmd.cpp
extern int g_verbose;

static int port0 = 0;

void apu_init(void)
{
    port0 = 0;
    gpio_init();
}

// Reset the APU by whatever means it needs reset.
void apu_reset(void)
{
    gpio_reset();
}

unsigned char apu_read(int address)
{
    unsigned char tmp = gpio_read(address);

//  printf("apu_read: a=%d -> %02x\n", address, tmp);

    return tmp;
}

void apu_write(int address, unsigned char data)
{
//  printf("apu_write: a=%d, %02x\n", address, data);

    gpio_write(address, data);  
}

// Write many bytes using handshake (see apu_writeHandshake)
int apu_writeBytes(unsigned char *data, int len)
{
//  printf("apu_writeBytes: %d...\n", len);

    for(int i=0; i<len; i++)
    {
        if(apu_writeHandshake(1, data[i]))
        {
            return 1;
        }
    }

    return 0;
}

// Write to address 'address', write the previously
// read value from port0 back to port 0 and wait
// for port0 value to be different from the written one.
int apu_writeHandshake(int address, int data)
{
    apu_write(address, data);
    apu_write(0, port0);
    
    if(!apu_waitInport(0, port0, 500))
    {
        return 1;
    }

    if(++port0 == 256)
    {
        port0 = 0;
    }

    return 0;
}

// return false on timeout, otherwise true
int apu_waitInport(int port, unsigned char data, int timeout_ms)
{
//  printf("apu_waitInport: addr: %d, data: %02x\n", port, data);

    int ms;
    Timer t;
    t.start();

    while(apu_read(port) != data)
    {
        ms = t.read_ms();
        if(ms > timeout_ms)
        {
            if(g_verbose)
            { 
                printf("timeout after %d milli\n", ms);
            }

            return 0;
        }
    }

    return 1;
}

// Initialise an spc transfer at 'address'. 
// To be used after reset of after jumping to rom
//
// Use apu_newTransfer for additional transfers
// 
// return -1 on error, 0 if success
int apu_initTransfer(unsigned short address)
{
    // Initializing the transfer
    // Wait for port 2140 to be $aa
    if(!apu_waitInport(0, 0xaa, 500))
    {
        if(g_verbose)
        { 
            printf("Should read 0xaa, but reads %02x\n", apu_read(0));
        }

        return -1;
    }

    // and Wait for port 2141 to be $bb
    if(!apu_waitInport(1, 0xbb, 500))
    {
        if(g_verbose)
        { 
            printf("Should read 0xaa, but reads %02x\n", apu_read(0));
        }

        return -1;
    }

    // The spc is now ready

    // Write any value other than 0 to 2141
    apu_write(1, 1);

    // Write the destination address to poirt $2142 and $2143, with the
    // low byte written at $2142
    apu_write(2, (address & 0x00ff));      // low
    apu_write(3, (address & 0xff00) >> 8); // high So our code will go at $0002

    // Write $CC to port $2140
    apu_write(0, 0xCC);

    // Wait for $2140 to be $CC
    if(!apu_waitInport(0, 0xcc, 500))
    {
        if(g_verbose)
        { 
            printf("Should read 0xcc, but reads %02x\n", apu_read(0));
        }

        return -1;
    }

    port0 = 0;
    return 0;
}

// initialise a new transfer after the first transfer.
//
// returns 0 on success, -1 on error
int apu_newTransfer(unsigned short address)
{
    int i;
    apu_write(1, 1);
    apu_write(3, (address & 0xff00) >> 8);
    apu_write(2, (address & 0x00ff));

    i  = apu_read(0);
    i += 2;
    i &= 0xff;

    // if it's 0, increase it again
    if(!i)
    {
        i += 2;
    }
    apu_write(0, i);

    if(!apu_waitInport(0, i, 500))
    {
        fprintf(stderr, "apu_newTransfer: timeout\n");
        return -1;
    }
    
    port0 = 0;
    return 0;
}

// End transfer and jump to address
void apu_endTransfer(unsigned short start_address)
{
    int i;
    
    apu_write(1, 0);
    apu_write(3, (start_address & 0xff00) >> 8);
    apu_write(2, (start_address & 0x00ff));

    i  = apu_read(0);
    i += 2;
    i &= 0xff;

    // if it's 0, increase it again
    if(!i)
    {
        i+=2;
    }
    apu_write(0, i);
}