This simple Arduino sketch is for playing YRG and RSF files from SD card root folder using AY-3-8910/12 YM2149F chips or emulator (in parallel mode) from this site. Sketch contains 2MHz generator for AY/YM chip.
(Updated 12.12.2015 updated RSF to v3)
Connections:
Arduino pins to AY/YM chip:
A0 – DA0, A1 – DA1, A2 – DA2, A3 – DA3, A4 – DA4, 5 – DA5, 6 – DA6, 7 – DA7
3 – BC1, 2 – BDIR
9 – frequency generator 2MHz
SD card:
MOSI – 11, MISO – 12, CLK(SCK) – 13, CS – 4
#include <SPI.h> #include <SD.h> const int CS_Pin = 4; // PC0 PC1 PC2 PC3 PC4 PD5 PD6 PD7 const byte ad[8] = {A0, A1, A2, A3, A4, 5, 6, 7 }; // connect to DA0,1,...,7 const int pinBC1 = 3; const int pinBDIR = 2; const byte freqOutputPin = 9; // OCR1A output pin for ATmega328, 2MHz output frequency for AY/YM chip //Port at which pins BC1/BDIR is #define __BCPORT__ PORTD #define __BC1__ 3 // PORT PIN (PD3) #define __BDIR__ 2 // PORT PIN (PD2) void initFrequencyGenerator() { // Set Timer 1 CTC mode OCR1A toggles on compare match TCCR1A = 0x40; TCCR1B = 0x09; // prescaller // This value determines the output frequency: 0 - 16MHz, 1 - 8MHz, 2 - 4MHz, 3 - 2MHz, 4 - 1MHz OCR1A = 3; } File dataFile; File entry,root; void setup() { //init pins for(byte i=0; i < 8; i++) pinMode(ad[i], OUTPUT); pinMode(CS_Pin, OUTPUT); pinMode(pinBC1, OUTPUT); pinMode(pinBDIR, OUTPUT); //inactive mode digitalWrite(pinBC1, LOW); digitalWrite(pinBDIR, LOW); Serial.begin(57600); // see if the card is present and can be initialized: if (!SD.begin(CS_Pin)) { Serial.println("Card failed, or not present"); return; } Serial.println("card initialized."); root = SD.open("/",FILE_READ); initFrequencyGenerator(); pinMode(freqOutputPin, OUTPUT); } void send_data(byte address, byte data) { // WRITE REGISTER NUMBER //write address to DA0-DA7 pins PORTC |= address & 0x1F; // DA0-DA4 PORTD |= address & 0xE0; // DA5-DA7 //validate addess //set BC1+BDIR bits, latch address mode __BCPORT__ |= (1 << __BDIR__) + (1 << __BC1__); asm("nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop"); //set+hold address delay 558ns (400+100 min) //clear BC1+BDIR bits, inactive mode __BCPORT__ &= ~((1 << __BDIR__) + (1 << __BC1__)); // reset pins to tristate mode PORTC &= ~(address & 0x1F); PORTD &= ~(address & 0xE0); // WRITE REGISTER DATA //write data to pins PORTC |= data & 0x1F; PORTD |= data & 0xE0; //validate data //set BDIR bit, write to reg mode __BCPORT__ |= ( 1 << __BDIR__); asm("nop\nnop\nnop\nnop\nnop"); //310ns delay (250min-500max) nop=62ns on 16MHz //clear BDIR bit, inactive mode __BCPORT__ &= ~( 1 << __BDIR__); // reset pins to tristate mode PORTC &= ~(data & 0x1F); PORTD &= ~(data & 0xE0); } byte buf[128]; byte buf2[14]; unsigned long t; void play_yrg() { while(dataFile.available()) { // read 16 x16 regs byte bframes = dataFile.read(buf,128)/16; for (byte frame = 0; frame < bframes; frame++) { // send diff registers from current frame for (byte reg = 0; reg < 14; reg++) { if (reg == 13 && buf[frame*16+reg]==255) break; if(reg == 13 || buf2[reg] != buf[frame*16+reg]) send_data(reg, buf[frame*16+reg]); } memcpy(buf2,&buf[frame*16],14); //delay(20); while(millis() - t < 20); t = millis(); } } } void play_rsf() { //unsigned long frames, loopframe, offset; word freq, offset; byte val, zeroes, ptr = 0, count, mask2, mask1, delay_time, skip; if( dataFile.read(buf,4) <= 0 ) return; // short file if(buf[0] != 'R' || buf[1] != 'S' || buf[2] != 'F') return; //not RSF switch(buf[3]) { // reading RSF HEADER v3 only supported! case 3: // RSF ver.3 if( dataFile.read(buf,14) == 0 ) return; // short file memcpy(&freq,&buf[0],sizeof(word)); memcpy(&offset,&buf[2],sizeof(word)); //memcpy(&frames,&buf[4],sizeof(unsigned long)); //memcpy(&loopframe,&buf[8],sizeof(unsigned long)); break; default: return; } if( freq > 1000 ) return; // frequency is too fast delay_time = 1000 / freq; // skip text info dataFile.seek(offset); // play song data if( (count = dataFile.read(buf,128)) <= 0 ) return; for(;;) { if(ptr > count>>1) { // half buffer is already played move it and load more byte msize = count - ptr; memmove(buf,&buf[ptr], msize); if( (count = dataFile.read(&buf[msize],ptr)) == 0 ) return; count += msize; ptr = 0; } skip = 1; val = buf[ptr++]; switch(val) { case 255: break; case 254: skip = buf[ptr++]; if(ptr >= count) return; break; default: mask2 = val; mask1 = buf[ptr++]; byte reg = 0; while( mask1 != 0) { if(mask1 & 1) send_data(reg,buf[ptr++]); mask1 >>= 1; reg++; } reg = 8; while( mask2 != 0) { if(mask2 & 1) send_data(reg,buf[ptr++]); mask2 >>= 1; reg++; } } //delay(delay_time); while(millis() - t < delay_time * skip); t = millis(); } } void loop() { byte file_type; // reset AY for(int i=0;i<14;i++) send_data(i, 0); memset(buf2,0,14); for(;;) { // find file file_type = 0; if(entry) entry.close(); entry = root.openNextFile(); if(!entry) { // end of files on SD root.rewindDirectory(); entry = root.openNextFile(); } if(entry.isDirectory()) continue; if(strcasestr(entry.name(),".yrg")) { file_type = 1; break; } if(strcasestr(entry.name(),".rsf")) { file_type = 2; break; } } dataFile = SD.open(entry.name(),FILE_READ); Serial.print("File: "); Serial.println(entry.name()); t = millis(); switch(file_type) { case 1: play_yrg(); break; case 2: play_rsf(); break; } dataFile.close(); entry.close(); }
[ad name=”HTML”]