- Implementat un canal de só en crú, sense envelope, sweep ni res

This commit is contained in:
2025-01-28 22:41:32 +01:00
parent ab7b26c5e6
commit 91a230ee44
4 changed files with 133 additions and 12 deletions

103
APU.cpp
View File

@@ -1,19 +1,48 @@
#include "APU.h"
#include <SDL2/SDL.h>
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<len;++i)
{
stream[i] = sound_buffer[(sound_start++)&(AUDIO_BUFFER_SIZE-1)];
}
}
void init()
{
SDL_AudioSpec audioSpec{11025, AUDIO_U8, 1, 0, AUDIO_BUFFER_SIZE>>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;
}
}
}

5
APU.h
View File

@@ -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);
}

View File

@@ -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();

33
mem.cpp
View File

@@ -3,6 +3,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <SDL2/SDL.h>
#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;