#include <SPI.h>
#include <SdFat.h>
#include <SPIFlash.h>
#include "wiring_private.h"
#define ENABLE_EXTENDED_TRANSFER_CLASS 1
#define CPU_HZ 48000000
#define TIMER_PRESCALER_DIV 16
#define MANID 0x90
#define PAGEPROG 0x02
#define READDATA 0x03
#define FASTREAD 0x0B
#define WRITEDISABLE 0x04
#define READSTAT1 0x05
#define READSTAT2 0x35
#define WRITEENABLE 0x06
#define SECTORERASE 0x20
#define BLOCK32ERASE 0x52
#define CHIPERASE 0x60
#define SUSPEND 0x75
#define ID 0x90
#define RESUME 0x7A
#define JEDECID 0x9f
#define RELEASE 0xAB
#define POWERDOWN 0xB9
#define BLOCK64ERASE 0xD8
#define CARD_CS ( 1 << 11 )
#define MISO ( 1 << 12 )
#define SN_WE ( 1 << 13 )
#define YM_IC ( 1 << 14 )
#define YM_A0 ( 1 << 15 )
#define YM_A1 ( 1 << 16 )
#define YM_RD ( 1 << 17 )
#define YM_WR ( 1 << 18 )
#define SN_CS ( 1 << 19 )
#define YM_CS ( 1 << 20 )
#define MEM_CS ( 1 << 21 )
#define SDA ( 1 << 22 )
#define SCL ( 1 << 23 )
#define SN_RDY ( 1 << 27 )
#define YM_IRQ ( 1 << 28 )
#define BACKBUTTON ( 1 << 2 )
#define PLAYBUTTON ( 1 << 3 )
#define OSC_IN ( 1 << 8 )
#define NEXTBUTTON ( 1 << 9 )
#define MOSI ( 1 << 10 )
#define SCK ( 1 << 11 )
#define MIDI_IN ( 1 << 23 )
#ifdef DEBUG
#define DEBUG_PRINT(...) Serial.print(__VA_ARGS__)
#define DEBUG_PRINTLN(...) Serial.println(__VA_ARGS__)
#else
#define DEBUG_PRINT(...)
#define DEBUG_PRINTLN(...)
#endif
#define PCMSIZE 20000
byte pcmData[ PCMSIZE ];
int numFiles = 0;
SdFat SD;
File vgmFile;
StdioStream vgmStream;
SPIClass mySPI ( &sercom1, 12, 13, 11, SPI_PAD_0_SCK_1, SERCOM_RX_PAD_3 );
volatile byte done = 0;
uint32_t fileSize;
byte readPcmFromFlash = 0;
volatile uint32_t commandPosition;
volatile uint32_t pcmPosition;
void waitForFlashReady() {
PORT->Group[PORTA].OUTCLR.reg = MEM_CS;
SPI.transfer( READSTAT1 );
byte s = SPI.transfer( 0x00 );
while ( s & 0x01 ) {
s = SPI.transfer( 0x00 );
DEBUG_PRINT( "." );
}
PORT->Group[PORTA].OUTSET.reg = MEM_CS;
}
void getNumFiles() {
SD.vwd()->rewind();
Serial.println( "Looking for files");
while (vgmFile.openNext(SD.vwd(), O_READ)) {
numFiles++;
vgmFile.close();
}
}
void writeEnable() {
PORT->Group[PORTA].OUTCLR.reg = MEM_CS;
SPI.transfer( WRITEENABLE );
PORT->Group[PORTA].OUTSET.reg = MEM_CS;
PORT->Group[PORTA].OUTCLR.reg = MEM_CS;
SPI.transfer( READSTAT1 );
byte s = SPI.transfer( 0x00 );
while ( s & 0x01 ) {
s = SPI.transfer( 0x00 );
DEBUG_PRINT( "." );
}
PORT->Group[PORTA].OUTSET.reg = MEM_CS;
}
void dumpToPcm() {
for ( uint32_t addr = 0; addr < fileSize; addr++ ) {
byte data = readFromFlash( addr );
uint32_t s = 0;
if ( data == 0x67 ) {
addr++;
data = readFromFlash( addr );
if ( data == 0x66 ) {
addr++;
data = readFromFlash( addr++ );
for ( uint32_t j = 0; j < 4; j++ ) s += ( uint32_t( readFromFlash( addr++ ) ) << ( 8 * j ));
if ( s > PCMSIZE ) {
Serial.println( "PCM Data too large!" );
readPcmFromFlash = 1;
return;
}
readPcmFromFlash = 0;
for ( uint32_t j = 0; j < s; j++ ) {
pcmData[ j ] = readFromFlash( addr++ );
DEBUG_PRINTLN( "Writing PCM Data to " + String( j, HEX ));
}
}
}
}
}
void dumpToFlash() {
uint8_t buf[ 256 ];
uint32_t addr = 0;
while ( !vgmStream.feof() ) {
writeEnable();
DEBUG_PRINTLN( "Erasing 64K Block Starting At " + String( addr, HEX ));
PORT->Group[PORTA].OUTCLR.reg = MEM_CS;
SPI.transfer( BLOCK64ERASE );
SPI.transfer( addr >> 16 );
SPI.transfer( addr >> 8 );
SPI.transfer( 0 );
PORT->Group[PORTA].OUTSET.reg = MEM_CS;
waitForFlashReady();
for ( int page = 0; page < 256; page++ ) {
for ( int i = 0; i < 256; i++ ) {
byte data = 0;
if ( !vgmStream.feof() ) data = vgmStream.getc();
buf[ i ] = data;
}
writeEnable();
PORT->Group[PORTA].OUTCLR.reg = MEM_CS;
SPI.transfer( 0x02 );
SPI.transfer( addr >> 16 );
SPI.transfer( addr >> 8 );
SPI.transfer( addr );
for (int i = 0; i < 256; i++) {
SPI.transfer( buf[i] );
addr++;
}
PORT->Group[PORTA].OUTSET.reg = MEM_CS;
waitForFlashReady();
DEBUG_PRINTLN( "Wrote to flash page " + String( addr, HEX ));
}
}
}
void dumpFlash() {
PORT->Group[PORTA].OUTCLR.reg = MEM_CS;
SPI.transfer( 0x03 );
SPI.transfer( 0x00 );
SPI.transfer( 0x00 );
SPI.transfer( 0x00 );
byte dat = 0;
for ( long i = 0; i < 0xFFFFFF; i++ ) {
dat = SPI.transfer( 0x00 );
DEBUG_PRINTLN( "Reading from flash " + String( SPI.transfer( 0x00 ), HEX ));
}
PORT->Group[PORTA].OUTSET.reg = MEM_CS;
}
byte readFromFlash( uint32_t addr ) {
static uint32_t lastAddr = 0xFFFFFFF0;
if ( addr != ( lastAddr + 1 )) {
DEBUG_PRINTLN( "Last Address was " + String( lastAddr, HEX ) + " Changing Address to " + String( addr, HEX ));
PORT->Group[PORTA].OUTSET.reg = MEM_CS;
PORT->Group[PORTA].OUTCLR.reg = MEM_CS;
SPI.transfer16(( 0x03 << 8 ) | (addr >> 16));
SPI.transfer16( addr );
}
lastAddr = addr;
byte data = SPI.transfer( 0x00 );
return data;
}
void playSN( uint8_t data ) {
PORT->Group[PORTA].OUTCLR.reg = SN_CS | ((uint32_t)~(data) << 3 ) & 0x7F8;
PORT->Group[PORTA].OUTSET.reg = ((uint32_t)data << 3 ) & 0x7F8;
PORT->Group[PORTA].OUTCLR.reg = SN_WE;
delayMicroseconds( 7 );
while ( !(PORT->Group[PORTA].IN.reg & SN_RDY ));
PORT->Group[PORTA].OUTSET.reg = SN_CS | SN_WE;
}
void playYM( uint8_t port, uint8_t addr, uint8_t data ) {
PORT->Group[PORTA].OUTCLR.reg = YM_CS | YM_A0 | ((uint32_t)~(addr) << 3 ) & 0x7F8;
PORT->Group[PORTA].OUTSET.reg = ((uint32_t)addr << 3 ) & 0x7F8;
if ( port ) PORT->Group[PORTA].OUTSET.reg = YM_A1;
else PORT->Group[PORTA].OUTCLR.reg = YM_A1;
PORT->Group[PORTA].OUTCLR.reg = YM_WR;
delayMicroseconds( 1 );
PORT->Group[PORTA].OUTSET.reg = YM_WR;
PORT->Group[PORTA].OUTSET.reg = YM_A0 | ((uint32_t)data << 3 ) & 0x7F8;
PORT->Group[PORTA].OUTCLR.reg = ((uint32_t)~(data) << 3 ) & 0x7F8;
PORT->Group[PORTA].OUTCLR.reg = YM_WR;
PORT->Group[PORTA].OUTSET.reg = YM_CS | YM_WR;
}
void silence() {
playSN( 0x9F );
playSN( 0xBF );
playSN( 0xDF );
playSN( 0xFF );
playYM(0, 0x22, 0x00);
playYM(0, 0x27, 0x00);
playYM(0, 0x28, 0x01);
playYM(0, 0x28, 0x02);
playYM(0, 0x28, 0x04);
playYM(0, 0x28, 0x05);
playYM(0, 0x28, 0x06);
playYM(0, 0x2B, 0x00);
}
void TC3_Handler(void) {
TcCount16* TC = (TcCount16*) TC3;
if (( TC->INTFLAG.bit.MC0 == 1 ) && ( done == 0 )) {
TC->INTFLAG.bit.MC0 = 1;
static uint32_t pause = 0;
uint8_t bufferByte;
if ( pause > 0 ) {
pause--;
DEBUG_PRINTLN( "Pausing for " + String( pause ));
}
else {
byte command = readFromFlash( commandPosition );
DEBUG_PRINT( "Addr:0x" + String( ( commandPosition ), HEX ) + " Cmd:0x" + String( command, HEX ) + " | " );
commandPosition++;
uint32_t addr;
uint32_t data;
uint32_t temp;
uint32_t s = 0;
switch ( command ) {
case 0x4F:
data = readFromFlash( commandPosition++ );
DEBUG_PRINTLN( "W - SN? Data 0x" + String( data, HEX ));
playSN( data );
break;
case 0x50:
data = readFromFlash( commandPosition++ );
DEBUG_PRINTLN( "W - SN Data 0x" + String( data, HEX ));
playSN( data );
break;
case 0x52:
addr = readFromFlash( commandPosition++ );
data = readFromFlash( commandPosition++ );
DEBUG_PRINTLN( "W - YM 0 Address 0x" + String( addr, HEX ) + " Data 0x" + String( data, HEX ) );
playYM( 0, addr, data );
break;
case 0x53:
addr = readFromFlash( commandPosition++ );
data = readFromFlash( commandPosition++ );
DEBUG_PRINTLN( "W - YM 1 Address 0x" + String( addr, HEX ) + " Data 0x" + String( data, HEX ) );
playYM( 1, addr, data );
break;
case 0x61:
pause = readFromFlash( commandPosition++ );
pause += (((uint16_t)readFromFlash( commandPosition++ )) << 8 );
DEBUG_PRINTLN( "W - Delay " + String( pause ));
break;
case 0x62:
DEBUG_PRINTLN( "W - Delay 735" );
pause = 735;
break;
case 0x63:
DEBUG_PRINTLN( "W - Delay 882" );
pause = 882;
break;
case 0x66:
done = 1;
DEBUG_PRINTLN( "End of Sound Data" );
break;
case 0x67:
temp = readFromFlash( commandPosition++ );
temp = readFromFlash( commandPosition++ );
for ( uint32_t j = 0; j < 4; j++ ) s += ( uint32_t( readFromFlash( commandPosition++ ) ) << ( 8 * j ));
pcmPosition = commandPosition;
commandPosition += s;
DEBUG_PRINTLN( "Data Block Type " + String( temp, HEX ) + " Space Needed: " + s + " Set Command Pointer to " + commandPosition );
break;
case 0x70:
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x76:
case 0x77:
case 0x78:
case 0x79:
case 0x7A:
case 0x7B:
case 0x7C:
case 0x7D:
case 0x7E:
case 0x7F:
DEBUG_PRINTLN( "W - Delaying " + String( ( command & 0x0F ) + 1 ));
pause = ( command & 0x0F ) + 1;
break;
case 0x80:
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0x88:
case 0x89:
case 0x8A:
case 0x8B:
case 0x8C:
case 0x8D:
case 0x8E:
case 0x8F:
addr = 0x2A;
if ( readPcmFromFlash ) data = readFromFlash( pcmPosition++ );
else data = pcmData[ pcmPosition ];
DEBUG_PRINTLN( "W - YM Play 0x" + String( pcmPosition, HEX ) + " > 0x" + String ( data, HEX ));
playYM( 0, addr, data );
pause = (command & 0x0F);
if ( pause > 0 ) pause--;
break;
case 0xE0:
for ( int j = 0; j < 4; j++ ) s += ( readFromFlash( commandPosition++ ) << ( 8 * ( j )));
pcmPosition = s;
DEBUG_PRINTLN( "Seeking PCM Pointer to Offset " + String( pcmPosition, HEX ));
break;
default:
DEBUG_PRINTLN( "Unknown File Command: 0x" + String( command, HEX ));
break;
}
}
}
}
void startTimer(int frequencyHz) {
REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID (GCM_TCC2_TC3)) ;
while ( GCLK->STATUS.bit.SYNCBUSY == 1 );
TcCount16* TC = (TcCount16*) TC3;
TC->CTRLA.reg &= ~TC_CTRLA_ENABLE;
TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
while (TC->STATUS.bit.SYNCBUSY == 1);
TC->CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;
while (TC->STATUS.bit.SYNCBUSY == 1);
TC->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV16;
while (TC->STATUS.bit.SYNCBUSY == 1);
int compareValue = ( CPU_HZ / ( TIMER_PRESCALER_DIV * frequencyHz )) - 1;
TC->COUNT.reg = map( TC->COUNT.reg, 0, TC->CC[ 0 ].reg, 0, compareValue );
TC->CC[ 0 ].reg = compareValue;
while ( TC->STATUS.bit.SYNCBUSY == 1 );
TC->INTENSET.reg = 0;
TC->INTENSET.bit.MC0 = 1;
NVIC_EnableIRQ(TC3_IRQn);
TC->CTRLA.reg |= TC_CTRLA_ENABLE;
while (TC->STATUS.bit.SYNCBUSY == 1);
}
void setTimerFrequency(int frequencyHz) {
int compareValue = ( CPU_HZ / ( TIMER_PRESCALER_DIV * frequencyHz )) - 1;
TcCount16* TC = ( TcCount16* ) TC3;
TC->COUNT.reg = map( TC->COUNT.reg, 0, TC->CC[ 0 ].reg, 0, compareValue );
TC->CC[ 0 ].reg = compareValue;
while ( TC->STATUS.bit.SYNCBUSY == 1 );
}
void setup() {
Serial.begin( 115200 );
Serial.println( "Serial Online" );
randomSeed( micros() / millis() );
PORT->Group[PORTA].DIRSET.reg = ( 0xFF << 3 ) | CARD_CS | SN_WE | YM_IC | YM_A0 | YM_A1 | YM_RD | YM_WR | SN_CS | YM_CS | MEM_CS | SDA | SCL;
PORT->Group[PORTA].DIRCLR.reg = SN_RDY | YM_IRQ;
PORT->Group[PORTA].OUTSET.reg = YM_IC | YM_RD | YM_WR | YM_CS | MEM_CS | SN_WE | SN_CS;
PORT->Group[PORTB].DIRSET.reg = MOSI | SCK;
PORT->Group[PORTB].DIRCLR.reg = OSC_IN | BACKBUTTON | PLAYBUTTON | NEXTBUTTON | MIDI_IN;
PORT->Group[PORTB].OUTSET.reg = BACKBUTTON | PLAYBUTTON | NEXTBUTTON;
delay( 10 );
PORT->Group[PORTA].OUTCLR.reg = YM_IC;
delay( 10 );
PORT->Group[PORTA].OUTSET.reg = YM_IC;
delay( 10 );
silence();
SPI.begin();
SPI.beginTransaction(SPISettings(50000000, MSBFIRST, SPI_MODE0));
PORT->Group[PORTA].OUTCLR.reg = MEM_CS;
SPI.transfer( 0xab );
PORT->Group[PORTA].OUTSET.reg = MEM_CS;
delayMicroseconds( 5 );
DEBUG_PRINTLN( "Flash Chip Released" );
PORT->Group[PORTA].OUTCLR.reg = MEM_CS;
SPI.transfer( 0x9f );
uint8_t manuf = SPI.transfer(0x00);
uint16_t id = ((uint16_t)SPI.transfer(0x00)) << 8;
id |= SPI.transfer(0x00);
PORT->Group[PORTA].OUTSET.reg = MEM_CS;
DEBUG_PRINTLN("MANUF=0x" + String(manuf, HEX) + ",ID=0x" + String(id, HEX));
PORT->Group[PORTA].OUTCLR.reg = MEM_CS;
SPI.transfer( 0x06 );
PORT->Group[PORTA].OUTSET.reg = MEM_CS;
PORT->Group[PORTA].OUTCLR.reg = MEM_CS;
SPI.transfer( 0x98 );
PORT->Group[PORTA].OUTSET.reg = MEM_CS;
SD.begin( 0, SPI_FULL_SPEED );
DEBUG_PRINTLN( "Setup Done" );
delay( 5000 );
}
void loop() {
getNumFiles();
static int fileCount = 0;
SD.vwd()->rewind();
int randomFile = fileCount;
fileCount++;
char buf[256];
for ( int i = 0; i < randomFile; i++ ) {
vgmFile.openNext(SD.vwd(), O_READ);
vgmFile.getName( buf, sizeof( buf ) );
Serial.println( String( buf ));
vgmFile.close();
}
Serial.println("try");
if ( vgmStream.fopen( buf , "r" ) ) {
Serial.println("fileopen");
DEBUG_PRINTLN( "VGM File Open" );
uint32_t ident = 0;
for ( int i = 0; i < 4; i++ ) ident += uint32_t( vgmStream.getc() ) << ( 8 * i );
if ( ident != 0x206d6756 ) {
DEBUG_PRINTLN( "Ident Match Failed!" );
return;
}
fileSize = 0;
for ( int i = 0; i < 4; i++ ) fileSize += uint32_t( vgmStream.getc() ) << ( 8 * i );
DEBUG_PRINTLN( "EOF:" + String( fileSize ) );
vgmStream.fseek( 0x40, SEEK_SET );
DEBUG_PRINTLN( "--------------VGM File Data Start--------------" );
Serial.println("dumping");
dumpToFlash();
dumpToPcm();
Serial.println("done");
vgmStream.fclose();
}
else Serial.println(" File read error" );
DEBUG_PRINTLN( "Closing VGM File" );
done = 0;
startTimer(44100);
interrupts();
while ( done == 0 ) DEBUG_PRINTLN( "Doing" );
noInterrupts();
DEBUG_PRINTLN( "Done" );
PORT->Group[PORTA].OUTSET.reg = MEM_CS;
silence();
fileSize = 0;
commandPosition = 0;
pcmPosition = 0;
Serial.println("redo");
}
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.