diff --git a/APU.cpp b/APU.cpp index 2a462dc..3e11bec 100644 --- a/APU.cpp +++ b/APU.cpp @@ -1,19 +1,48 @@ #include "APU.h" +#include namespace APU { + #define AUDIO_BUFFER_SIZE 2048 + const float cycles_per_sample = 4194304.0f / 11025.0f; + + SDL_AudioDeviceID sdlAudioDevice; + uint8_t sound_buffer[AUDIO_BUFFER_SIZE]; + uint16_t sound_pos=0; + uint16_t sound_start=0; + float t_sound = 0.0f; + uint32_t samples_generated=0; + uint32_t samples_time =0; + uint32_t samples_t=0; + + #define CH1 channels[0] + #define CH2 channels[1] + #define CH3 channels[2] + #define CH4 channels[3] + #define apu_enabled (NR52&0x80) #define DAC1_enabled ((NR12&0xf8)!=0) + uint8_t duty_cycles[4][8] = { + {1, 1, 1, 1, 1, 1, 1, 0}, + {1, 1, 1, 1, 1, 1, 0, 0}, + {1, 1, 1, 1, 0, 0, 0, 0}, + {1, 1, 0, 0, 0, 0, 0, 0}, + }; struct channel_t { bool enabled = false; uint8_t length_timer = 0; bool length_timer_expired = true; bool length_enable = false; + uint16_t period_divider = 0; + uint8_t duty_cycle=0; + uint8_t duty_step = 0; + uint8_t volume = 0; }; channel_t channels[4]; + uint8_t DIVAPU = 0; uint8_t NR10 = 0; // 0xff10 - Sound channel 1 sweep uint8_t NR11 = 0; // 0xff11 - Sound channel 1 length timer & duty cycle @@ -41,6 +70,23 @@ namespace APU // 0xff27-0xff2f - Not used? uint8_t WaveRAM[16]; // 0xff30-0xff3f + + void audioCallback(void * userdata, uint8_t * stream, int len) + { + for (int i=0;i>2, 0, 0, &audioCallback, NULL}; + sdlAudioDevice = SDL_OpenAudioDevice(NULL, 0, &audioSpec, NULL, 0); + SDL_PauseAudioDevice(sdlAudioDevice, 0); + //samples_time=SDL_GetTicks(); + } + void reset() { NR10 = 0; @@ -64,15 +110,16 @@ namespace APU NR50 = 0; NR51 = 0; NR52 = 0; + CH1.duty_cycle = 0; } void trigger(uint8_t channel) { channels[channel].enabled = true; - // length timer expired is reset - // period divider is set to the contents of NR13 and NR14 + channels[channel].length_timer_expired = false; + channels[channel].period_divider = NR13 | ((NR14 &0x7)<<8); // envelope timer is reset - // volume is set to contents of NR12 initial volume + channels[channel].volume = NR12>>4; // sweep does several things (check documentation) } @@ -90,6 +137,7 @@ namespace APU case 0xff25: return NR51; break; case 0xff26: return NR52; break; } + return 0x00; } void writeRegister(uint16_t address, uint8_t value) @@ -99,14 +147,59 @@ namespace APU switch(address) { case 0xff10: NR10 = value; break; - case 0xff11: NR11 = value; break; + case 0xff11: NR11 = value; CH1.duty_cycle = NR11>>6; break; case 0xff12: NR12 = value; break; case 0xff13: NR13 = value; break; - case 0xff14: NR14 = value; if (value&0x80) trigger(1); break; + case 0xff14: NR14 = value; if (value&0x80) trigger(0); break; case 0xff24: NR50 = value; break; case 0xff25: NR51 = value; break; case 0xff26: if (value&0x80) reset(); NR52 = (value&0x80) | (NR52 & 0x0f); break; } } + + void incDIVAPU() + { + DIVAPU++; + } + + uint32_t dots = 0; + void update(uint32_t dt) + { + dots += dt; + while (dots>=4) { + dots -= 4; + CH1.period_divider++; + if (CH1.period_divider==2048) { + CH1.period_divider = NR13 | ((NR14 &0x7)<<8); + CH1.duty_step++; + if (CH1.duty_step==8) CH1.duty_step=0; + } + } + + t_sound += dt; + samples_t += dt; + if (t_sound>=cycles_per_sample) { + t_sound-=cycles_per_sample; + //samples_generated++; + /*if (samples_t >=z80::getClock()) { + //printf("%i\n", samples_generated); + samples_generated=0; + samples_t = 0; + }*/ + /*if (SDL_GetTicks()>=samples_time+1000) { + printf("%i\n", samples_generated); + samples_generated=0; + samples_time = SDL_GetTicks(); + }*/ + //sound_pos = (sound_pos+1) & 0x3ff; + //sound_buffer[sound_pos] = ear*128; + + //if (sound_pos>=AUDIO_BUFFER_SIZE) sound_pos = last_1 = 0; + uint8_t sample = 0; + if (apu_enabled && DAC1_enabled) sample = (duty_cycles[CH1.duty_cycle][CH1.duty_step]*CH1.volume)<<2; + sound_buffer[(sound_pos++)&(AUDIO_BUFFER_SIZE-1)] = sample; + //if (ear) last_1 = sound_pos; + } + } } \ No newline at end of file diff --git a/APU.h b/APU.h index 3a3dd54..fbad62c 100644 --- a/APU.h +++ b/APU.h @@ -5,4 +5,9 @@ namespace APU { uint8_t readRegister(uint16_t address); void writeRegister(uint16_t address, uint8_t value); + + void init(); + void reset(); + void incDIVAPU(); + void update(uint32_t dt); } diff --git a/main.cpp b/main.cpp index 692b44d..80d60c5 100644 --- a/main.cpp +++ b/main.cpp @@ -65,6 +65,8 @@ int main(int argc, char *argv[]) const uint32_t clock = 4194304; const uint32_t update_freq = clock / 10; + SDL_Init(SDL_INIT_EVERYTHING); + FILE *f = fopen(argv[1], "rb"); if (!f) { printf("ABORTING: Rom not found.\n"); exit(1); } fseek(f, 0, SEEK_END); @@ -73,12 +75,12 @@ int main(int argc, char *argv[]) uint8_t *buffer = (uint8_t*)malloc(filesize); fread(buffer, filesize, 1, f); fclose(f); + mem::init(buffer, filesize); sm83dis::loadSymbols(); sm83::reset(); - SDL_Init(SDL_INIT_EVERYTHING); gbscreen::init(0); debug::init(); diff --git a/mem.cpp b/mem.cpp index ba2a132..9d34f4e 100644 --- a/mem.cpp +++ b/mem.cpp @@ -3,6 +3,7 @@ #include #include #include +#include "APU.h" #include "mbc_none.h" #include "mbc1.h" @@ -67,6 +68,8 @@ namespace mem mem::writeRam = mbc1::writeRam; break; }; + + APU::init(); } void reset() @@ -85,6 +88,7 @@ namespace mem for (int i=0; i<65536; ++i) { tags[i] = MEMTAG_NONE; } resetMbc(); + APU::reset(); } uint8_t getKeypad(uint8_t value) @@ -123,7 +127,7 @@ namespace mem } else { if (address==0xFF00) { hram[address - 0XFE00] = getKeypad(hram[address - 0XFE00]); - } + } else if (address>=0xFF10 && address<=0xFF26) { return APU::readRegister(address); } return hram[address - 0XFE00]; } } @@ -141,11 +145,23 @@ namespace mem } else if (address < 0xFE00) { wram[address - 0xE000] = value; } else { - if ( (address==0xFF50) && ((value&0x01) != 1) ) return; //Only allow disabling boot room - if ( (address==0xFF00) ) { value = value & 0x30; } - if ( (address==0xFF04) ) { hram[address-0xFE00] = 0; return; } - if ( (address==0xFF0F) ) { hram[address-0xFE00] = value; sm83::processInterrupts(); return; } - if ( (address==0xFF46) ) mem::init_dma_transfer(value); + if ( (address==0xFF50) && ((value&0x01) != 1) ) { + return; //Only allow disabling boot room + } else if ( (address==0xFF00) ) { + value = value & 0x30; + } else if ( (address==0xFF04) ) { + hram[address-0xFE00] = 0; + return; + } else if ( (address==0xFF0F) ) { // IF + hram[address-0xFE00] = value; + sm83::processInterrupts(); + return; + } else if (address>=0xFF10 && address<=0xFF26) { // APU + APU::writeRegister(address, value); + return; + } else if ( (address==0xFF46) ) { // OAM DMA + mem::init_dma_transfer(value); + } hram[address - 0xFE00] = value; } } @@ -190,7 +206,9 @@ namespace mem div_counter += dt; if (div_counter>=256) { div_counter -= 256; + bool bitset = DIV&0x10; DIV++; + if (bitset && !(DIV&0x10)) APU::incDIVAPU(); } // Timer @@ -209,6 +227,9 @@ namespace mem } + // APU + APU::update(dt); + // OAM DMA if (dma_pos<160) { dma_dots += dt;