Update to v075r04 release.

byuu says:

Changelog:
- hooked up everything necessary for Game Boy sound emulation ...
- bgameboy and bsnes/SGB input 4MHz frequency, and output 44.1KHz
  frequency (produces soft static for now, to verify it is working)
- rewrote all of gameboy/apu, it now has a 4MHz worker thread, and
  separate classes/folders for each channel+master, and serializes

So it's basically all I can do without actual emulation code or
human-readable documentation/example code.
This commit is contained in:
Tim Allen 2011-02-02 21:37:31 +11:00
parent a136378a7b
commit f88ef9e9a2
23 changed files with 507 additions and 416 deletions

View File

@ -1,85 +1,53 @@
#include <gameboy/gameboy.hpp>
#include <nall/random.hpp>
#define APU_CPP
namespace GameBoy {
#include "mmio/mmio.cpp"
#include "square/square.cpp"
#include "wave/wave.cpp"
#include "noise/noise.cpp"
#include "master/master.cpp"
#include "serialization.cpp"
APU apu;
void APU::Main() {
apu.main();
}
void APU::main() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
square1.run();
square2.run();
wave.run();
noise.run();
master.run();
system.interface->audio_sample((uint16)prng() >> 3, (uint16)prng() >> 3);
if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
}
}
void APU::power() {
create(Main, 4 * 1024 * 1024);
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
channel1.sweep_time = 0;
channel1.sweep_direction = 0;
channel1.sweep_shift = 0;
square1.has_sweep = true;
square2.has_sweep = false;
channel1.wave_pattern_duty = 0;
channel1.sound_length = 0;
channel1.initial_envelope_volume = 0;
channel1.envelope_direction = 0;
channel1.envelope_sweep = 0;
channel1.frequency = 0;
channel1.initialize = 0;
channel1.consecutive_selection = 0;
channel2.wave_pattern_duty = 0;
channel2.sound_length = 0;
channel2.initial_envelope_volume = 0;
channel2.envelope_direction = 0;
channel2.envelope_sweep = 0;
channel2.frequency = 0;
channel2.initialize = 0;
channel2.consecutive_selection = 0;
channel3.off = 0;
channel3.sound_length = 0;
channel3.output_level = 0;
channel3.frequency = 0;
channel3.initialize = 0;
channel3.consecutive_selection = 0;
for(unsigned n = 0; n < 16; n++) channel3.pattern[n] = 0;
channel4.sound_length = 0;
channel4.initial_envelope_volume = 0;
channel4.envelope_direction = 0;
channel4.envelope_sweep = 0;
channel4.shift_clock_frequency = 0;
channel4.counter_step_width = 0;
channel4.dividing_ratio = 0;
channel4.initialize = 0;
channel4.consecutive_selection = 0;
control.output_vin_to_so2 = 0;
control.so2_output_level = 0;
control.output_vin_to_so1 = 0;
control.so1_output_level = 0;
control.output_channel4_to_so2 = 0;
control.output_channel3_to_so2 = 0;
control.output_channel2_to_so2 = 0;
control.output_channel1_to_so2 = 0;
control.output_channel4_to_so1 = 0;
control.output_channel3_to_so1 = 0;
control.output_channel2_to_so1 = 0;
control.output_channel1_to_so1 = 0;
control.sound_on = 0;
control.channel4_on = 0;
control.channel3_on = 0;
control.channel2_on = 0;
control.channel1_on = 0;
square1.power();
square2.power();
wave.power();
noise.power();
master.power();
}
}

View File

@ -1,6 +1,18 @@
struct APU : Processor, MMIO {
#include "mmio/mmio.hpp"
#include "square/square.hpp"
#include "wave/wave.hpp"
#include "noise/noise.hpp"
#include "master/master.hpp"
Square square1;
Square square2;
Wave wave;
Noise noise;
Master master;
static void Main();
void main();
void power();
void serialize(serializer&);

View File

@ -0,0 +1,99 @@
#ifdef APU_CPP
void APU::Master::run() {
}
uint8 APU::Master::read(unsigned r) {
if(r == 0) {
return (so2_enable << 7) | (so2_volume << 4) | (so1_enable << 3) | (so1_volume << 0);
}
if(r == 1) {
return (channel4_so2_enable << 7)
| (channel3_so2_enable << 6)
| (channel2_so2_enable << 5)
| (channel1_so2_enable << 4)
| (channel4_so1_enable << 3)
| (channel3_so1_enable << 2)
| (channel2_so1_enable << 1)
| (channel1_so1_enable << 0);
}
if(r == 2) {
return (enable << 7)
| (channel4_enable << 3)
| (channel3_enable << 2)
| (channel2_enable << 1)
| (channel1_enable << 0);
}
}
void APU::Master::write(unsigned r, uint8 data) {
if(r == 0) {
so2_enable = data & 0x80;
so2_volume = (data >> 4) & 7;
so1_enable = data & 0x08;
so1_volume = (data >> 0) & 7;
}
if(r == 1) {
channel4_so2_enable = data & 0x80;
channel3_so2_enable = data & 0x40;
channel2_so2_enable = data & 0x20;
channel1_so2_enable = data & 0x10;
channel4_so1_enable = data & 0x08;
channel3_so1_enable = data & 0x04;
channel2_so1_enable = data & 0x02;
channel1_so1_enable = data & 0x01;
}
if(r == 2) {
enable = data & 0x80;
channel4_enable = data & 0x08;
channel3_enable = data & 0x04;
channel2_enable = data & 0x02;
channel1_enable = data & 0x01;
}
}
void APU::Master::power() {
so2_enable = 0;
so2_volume = 0;
so1_enable = 0;
so1_volume = 0;
channel4_so2_enable = 0;
channel3_so2_enable = 0;
channel2_so2_enable = 0;
channel1_so2_enable = 0;
channel4_so1_enable = 0;
channel3_so1_enable = 0;
channel2_so1_enable = 0;
channel1_so1_enable = 0;
enable = 0;
channel4_enable = 0;
channel3_enable = 0;
channel2_enable = 0;
channel1_enable = 0;
}
void APU::Master::serialize(serializer &s) {
s.integer(so2_enable);
s.integer(so2_volume);
s.integer(so1_enable);
s.integer(so1_volume);
s.integer(channel4_so2_enable);
s.integer(channel3_so2_enable);
s.integer(channel2_so2_enable);
s.integer(channel1_so2_enable);
s.integer(channel4_so1_enable);
s.integer(channel3_so1_enable);
s.integer(channel2_so1_enable);
s.integer(channel1_so1_enable);
s.integer(enable);
s.integer(channel4_enable);
s.integer(channel3_enable);
s.integer(channel2_enable);
s.integer(channel1_enable);
}
#endif

View File

@ -0,0 +1,25 @@
struct Master {
bool so2_enable;
unsigned so2_volume;
bool so1_enable;
unsigned so1_volume;
bool channel4_so2_enable;
bool channel3_so2_enable;
bool channel2_so2_enable;
bool channel1_so2_enable;
bool channel4_so1_enable;
bool channel3_so1_enable;
bool channel2_so1_enable;
bool channel1_so1_enable;
bool enable;
bool channel4_enable;
bool channel3_enable;
bool channel2_enable;
bool channel1_enable;
void run();
uint8 read(unsigned r);
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);
};

View File

@ -1,248 +1,22 @@
#ifdef APU_CPP
uint8 APU::mmio_read(uint16 addr) {
if(addr == 0xff10) { //NR10
return (channel1.sweep_time << 4)
| (channel1.sweep_direction << 3)
| (channel1.sweep_shift << 0);
}
if(addr == 0xff11) { //NR11
return (channel1.wave_pattern_duty << 6);
}
if(addr == 0xff12) { //NR12
return (channel1.initial_envelope_volume << 4)
| (channel1.envelope_direction << 3)
| (channel1.envelope_sweep << 0);
}
if(addr == 0xff14) { //NR14
return (channel1.consecutive_selection << 6);
}
if(addr == 0xff16) { //NR21
return (channel2.wave_pattern_duty << 6);
}
if(addr == 0xff17) { //NR22
return (channel2.initial_envelope_volume << 4)
| (channel2.envelope_direction << 3)
| (channel2.envelope_sweep << 0);
}
if(addr == 0xff19) { //NR24
return (channel2.consecutive_selection << 6);
}
if(addr == 0xff1a) { //NR30
return (channel3.off << 7);
}
if(addr == 0xff1b) { //NR31
return (channel3.sound_length << 0);
}
if(addr == 0xff1c) { //NR32
return (channel3.output_level << 5);
}
if(addr == 0xff1e) { //NR34
return (channel3.consecutive_selection << 6);
}
if(addr == 0xff20) { //NR41
return (channel4.sound_length << 0);
}
if(addr == 0xff21) { //NR42
return (channel4.initial_envelope_volume << 4)
| (channel4.envelope_direction << 3)
| (channel4.envelope_sweep << 0);
}
if(addr == 0xff22) { //NR43
return (channel4.shift_clock_frequency << 4)
| (channel4.counter_step_width << 3)
| (channel4.dividing_ratio << 0);
}
if(addr == 0xff23) { //NR44
return (channel4.consecutive_selection << 6);
}
if(addr == 0xff24) { //NR50
return (control.output_vin_to_so2 << 7)
| (control.so2_output_level << 4)
| (control.output_vin_to_so1 << 3)
| (control.so1_output_level << 0);
}
if(addr == 0xff25) { //NR51
return (control.output_channel4_to_so2 << 7)
| (control.output_channel3_to_so2 << 6)
| (control.output_channel2_to_so2 << 5)
| (control.output_channel1_to_so2 << 4)
| (control.output_channel4_to_so1 << 3)
| (control.output_channel3_to_so1 << 2)
| (control.output_channel2_to_so1 << 1)
| (control.output_channel1_to_so1 << 0);
}
if(addr == 0xff26) { //NR52
return (control.sound_on << 7);
}
if(addr >= 0xff30 && addr <= 0xff3f) {
return channel3.pattern[addr & 15];
}
if(addr >= 0xff10 && addr <= 0xff14) return square1.read(addr - 0xff10);
if(addr >= 0xff15 && addr <= 0xff19) return square2.read(addr - 0xff15);
if(addr >= 0xff1a && addr <= 0xff1e) return wave.read(addr - 0xff1a);
if(addr >= 0xff1f && addr <= 0xff23) return noise.read(addr - 0xff1f);
if(addr >= 0xff24 && addr <= 0xff26) return master.read(addr - 0xff24);
if(addr >= 0xff30 && addr <= 0xff3f) return wave.read_pattern(addr - 0xff30);
return 0x00;
}
void APU::mmio_write(uint16 addr, uint8 data) {
if(addr == 0xff10) { //NR10
channel1.sweep_time = (data >> 4) & 7;
channel1.sweep_direction = data & 0x08;
channel1.sweep_shift = data & 0x07;
return;
}
if(addr == 0xff11) { //NR11
channel1.wave_pattern_duty = (data >> 6) & 3;
channel1.sound_length = data & 0x3f;
return;
}
if(addr == 0xff12) { //NR12
channel1.initial_envelope_volume = (data >> 4) & 15;
channel1.envelope_direction = data & 0x08;
channel1.envelope_sweep = data & 0x07;
return;
}
if(addr == 0xff13) { //NR13
channel1.frequency = (channel1.frequency & 0x0700) | (data << 0);
return;
}
if(addr == 0xff14) { //NR14
channel1.initialize = data & 0x80;
channel1.consecutive_selection = data & 0x40;
channel1.frequency = ((data & 7) << 8) | (channel1.frequency & 0x00ff);
return;
}
if(addr == 0xff16) { //NR21
channel2.wave_pattern_duty = (data >> 6) & 3;
channel2.sound_length = data & 0x3f;
return;
}
if(addr == 0xff17) { //NR22
channel2.initial_envelope_volume = (data >> 4) & 15;
channel2.envelope_direction = data & 0x08;
channel2.envelope_sweep = data & 0x07;
return;
}
if(addr == 0xff18) { //NR23
channel2.frequency = (channel2.frequency & 0x0700) | (data << 0);
return;
}
if(addr == 0xff19) { //NR24
channel2.initialize = data & 0x80;
channel2.consecutive_selection = data & 0x40;
channel2.frequency = ((data & 7) << 8) | (channel2.frequency & 0x00ff);
return;
}
if(addr == 0xff1a) { //NR30
channel3.off = data & 0x80;
return;
}
if(addr == 0xff1b) { //NR31
channel3.sound_length = data;
return;
}
if(addr == 0xff1c) { //NR32
channel3.output_level = (data >> 5) & 3;
return;
}
if(addr == 0xff1d) { //NR33
channel3.frequency = (channel3.frequency & 0x0700) | (data << 0);
return;
}
if(addr == 0xff1e) { //NR34
channel3.initialize = data & 0x80;
channel3.consecutive_selection = data & 0x40;
channel3.frequency = ((data & 7) << 8) | (channel3.frequency & 0x00ff);
return;
}
if(addr == 0xff20) { //NR41
channel4.sound_length = data & 0x3f;
return;
}
if(addr == 0xff21) { //NR42
channel4.initial_envelope_volume = (data >> 3) & 15;
channel4.envelope_direction = data & 0x08;
channel4.envelope_sweep = data & 0x07;
return;
}
if(addr == 0xff22) { //NR43
channel4.shift_clock_frequency = (data >> 4) & 15;
channel4.counter_step_width = data & 0x08;
channel4.dividing_ratio = data & 0x07;
return;
}
if(addr == 0xff23) { //NR44
channel4.initialize = data & 0x80;
channel4.consecutive_selection = data & 0x40;
return;
}
if(addr == 0xff24) { //NR50
control.output_vin_to_so2 = data & 0x80;
control.so2_output_level = (data >> 4) & 7;
control.output_vin_to_so1 = data & 0x08;
control.so1_output_level = (data >> 0) & 7;
return;
}
if(addr == 0xff25) { //NR51
control.output_channel4_to_so2 = data & 0x80;
control.output_channel3_to_so2 = data & 0x40;
control.output_channel2_to_so2 = data & 0x20;
control.output_channel1_to_so2 = data & 0x10;
control.output_channel4_to_so1 = data & 0x08;
control.output_channel3_to_so1 = data & 0x04;
control.output_channel2_to_so1 = data & 0x02;
control.output_channel1_to_so1 = data & 0x01;
return;
}
if(addr == 0xff26) { //NR52
control.sound_on = data & 0x80;
control.channel4_on = data & 0x08;
control.channel3_on = data & 0x04;
control.channel2_on = data & 0x02;
control.channel1_on = data & 0x01;
return;
}
if(addr >= 0xff30 && addr <= 0xff3f) {
channel3.pattern[addr & 15] = data;
return;
}
if(addr >= 0xff10 && addr <= 0xff14) return square1.write(addr - 0xff10, data);
if(addr >= 0xff15 && addr <= 0xff19) return square2.write(addr - 0xff15, data);
if(addr >= 0xff1a && addr <= 0xff1e) return wave.write(addr - 0xff1a, data);
if(addr >= 0xff1f && addr <= 0xff23) return noise.write(addr - 0xff1f, data);
if(addr >= 0xff24 && addr <= 0xff26) return master.write(addr - 0xff24, data);
if(addr >= 0xff30 && addr <= 0xff3f) return wave.write_pattern(addr - 0xff30, data);
}
#endif

View File

@ -1,102 +1,2 @@
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
struct Channel1 { //tone and sweep
//$ff10 NR10
unsigned sweep_time;
bool sweep_direction;
unsigned sweep_shift;
//$ff11 NR11
unsigned wave_pattern_duty;
unsigned sound_length;
//$ff12 NR12
unsigned initial_envelope_volume;
bool envelope_direction;
unsigned envelope_sweep;
//$ff13,$ff14 NR13,NR14
unsigned frequency;
bool initialize;
bool consecutive_selection;
} channel1;
struct Channel2 { //tone
//$ff16 NR21
unsigned wave_pattern_duty;
unsigned sound_length;
//$ff17 NR22
unsigned initial_envelope_volume;
bool envelope_direction;
unsigned envelope_sweep;
//$ff18,$ff19 NR23,NR24
unsigned frequency;
bool initialize;
bool consecutive_selection;
} channel2;
struct Channel3 { //wave output
//$ff1a NR30
bool off;
//$ff1b NR31
unsigned sound_length;
//$ff1c NR32
unsigned output_level;
//$ff1d,$ff1e NR33,NR34
unsigned frequency;
bool initialize;
bool consecutive_selection;
//$ff30-ff3f
uint8 pattern[16];
} channel3;
struct Channel4 { //noise
//$ff20 NR41
unsigned sound_length;
//$ff21 NR42
unsigned initial_envelope_volume;
bool envelope_direction;
unsigned envelope_sweep;
//$ff22 NR43
unsigned shift_clock_frequency;
bool counter_step_width;
unsigned dividing_ratio;
//$ff23 NR44
bool initialize;
bool consecutive_selection;
} channel4;
struct Control {
//$ff24 NR50
bool output_vin_to_so2;
unsigned so2_output_level;
bool output_vin_to_so1;
unsigned so1_output_level;
//$ff25 NR51
bool output_channel4_to_so2;
bool output_channel3_to_so2;
bool output_channel2_to_so2;
bool output_channel1_to_so2;
bool output_channel4_to_so1;
bool output_channel3_to_so1;
bool output_channel2_to_so1;
bool output_channel1_to_so1;
//$ff26 NR52
bool sound_on;
bool channel4_on;
bool channel3_on;
bool channel2_on;
bool channel1_on;
} control;

View File

@ -0,0 +1,66 @@
#ifdef APU_CPP
void APU::Noise::run() {
}
uint8 APU::Noise::read(unsigned r) {
if(r == 2) {
return (envelope_volume << 4) | (envelope_direction << 3) | (envelope_period << 0);
}
if(r == 3) {
return (period << 4) | (step << 3) | (divisor);
}
if(r == 4) {
return (counter << 6);
}
return 0x00;
}
void APU::Noise::write(unsigned r, uint8 data) {
if(r == 1) {
length = data & 0x3f;
}
if(r == 2) {
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_period = data & 0x07;
}
if(r == 3) {
period = data >> 4;
step = data & 0x08;
divisor = data & 0x07;
}
if(r == 4) {
counter = data & 0x40;
}
}
void APU::Noise::power() {
length = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_period = 0;
period = 0;
step = 0;
divisor = 0;
counter = 0;
}
void APU::Noise::serialize(serializer &s) {
s.integer(length);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_period);
s.integer(period);
s.integer(step);
s.integer(divisor);
s.integer(counter);
}
#endif

View File

@ -0,0 +1,16 @@
struct Noise {
unsigned length;
unsigned envelope_volume;
bool envelope_direction;
unsigned envelope_period;
unsigned period;
bool step;
unsigned divisor;
bool counter;
void run();
uint8 read(unsigned r);
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);
};

View File

@ -1,6 +1,11 @@
#ifdef APU_CPP
void APU::serialize(serializer &s) {
square1.serialize(s);
square2.serialize(s);
wave.serialize(s);
noise.serialize(s);
master.serialize(s);
}
#endif

View File

@ -0,0 +1,85 @@
#ifdef APU_CPP
void APU::Square::run() {
}
uint8 APU::Square::read(unsigned r) {
if(r == 0) {
return (sweep_period << 4) | (sweep_direction << 3) | (sweep_shift << 0);
}
if(r == 1) {
return (duty << 6);
}
if(r == 2) {
return (envelope_volume << 4) | (envelope_direction << 3) | (envelope_period << 0);
}
if(r == 4) {
return (counter << 6);
}
return 0x00;
}
void APU::Square::write(unsigned r, uint8 data) {
if(r == 0) {
sweep_period = (data >> 4) & 7;
sweep_direction = data & 0x08;
sweep_shift = data & 0x07;
}
if(r == 1) {
duty = data >> 6;
length = data & 0x3f;
}
if(r == 2) {
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_period = data & 0x07;
}
if(r == 3) {
frequency = (frequency & 0x0700) | data;
}
if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
}
}
void APU::Square::power() {
sweep_period = 0;
sweep_direction = 0;
sweep_shift = 0;
duty = 0;
length = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_period = 0;
frequency = 0;
counter = 0;
}
void APU::Square::serialize(serializer &s) {
s.integer(sweep_period);
s.integer(sweep_direction);
s.integer(sweep_shift);
s.integer(duty);
s.integer(length);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_period);
s.integer(frequency);
s.integer(counter);
}
#endif

View File

@ -0,0 +1,20 @@
struct Square {
bool has_sweep;
unsigned sweep_period;
unsigned sweep_direction;
unsigned sweep_shift;
unsigned duty;
unsigned length;
unsigned envelope_volume;
unsigned envelope_direction;
unsigned envelope_period;
unsigned frequency;
unsigned counter;
void run();
uint8 read(unsigned r);
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);
};

75
bsnes/gameboy/apu/wave/wave.cpp Executable file
View File

@ -0,0 +1,75 @@
#ifdef APU_CPP
void APU::Wave::run() {
}
uint8 APU::Wave::read(unsigned r) {
if(r == 0) {
return (off << 7);
}
if(r == 2) {
return (volume << 5);
}
if(r == 4) {
return (counter << 6);
}
return 0x00;
}
void APU::Wave::write(unsigned r, uint8 data) {
if(r == 0) {
off = data & 0x80;
}
if(r == 1) {
length = data;
}
if(r == 2) {
volume = (data >> 5) & 3;
}
if(r == 3) {
frequency = (frequency & 0x0700) | data;
}
if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
}
}
uint8 APU::Wave::read_pattern(unsigned p) {
p <<= 1;
return (pattern[p + 0] << 4) | (pattern[p + 1] << 0);
}
void APU::Wave::write_pattern(unsigned p, uint8 data) {
p <<= 1;
pattern[p + 0] = data >> 4;
pattern[p + 1] = data >> 0;
}
void APU::Wave::power() {
off = 0;
length = 0;
volume = 0;
frequency = 0;
counter = 0;
foreach(n, pattern) n = 0;
}
void APU::Wave::serialize(serializer &s) {
s.integer(off);
s.integer(length);
s.integer(volume);
s.integer(frequency);
s.integer(counter);
s.array(pattern);
}
#endif

16
bsnes/gameboy/apu/wave/wave.hpp Executable file
View File

@ -0,0 +1,16 @@
struct Wave {
bool off;
unsigned length;
unsigned volume;
unsigned frequency;
bool counter;
uint8 pattern[32];
void run();
uint8 read(unsigned r);
void write(unsigned r, uint8 data);
uint8 read_pattern(unsigned p);
void write_pattern(unsigned p, uint8 data);
void power();
void serialize(serializer&);
};

View File

@ -25,8 +25,11 @@ void CPU::add_clocks(unsigned clocks) {
status.timer0 += clocks;
if(status.timer0 >= 16) timer_stage0();
cpu.clock += clocks;
if(cpu.clock >= 0) co_switch(scheduler.active_thread = lcd.thread);
lcd.clock -= clocks;
if(lcd.clock <= 0) co_switch(scheduler.active_thread = lcd.thread);
apu.clock -= clocks;
if(apu.clock <= 0) co_switch(scheduler.active_thread = apu.thread);
}
void CPU::timer_stage0() { //262144hz

View File

@ -5,7 +5,7 @@
namespace GameBoy {
namespace Info {
static const char Name[] = "bgameboy";
static const char Version[] = "000.14";
static const char Version[] = "000.15";
static unsigned SerializerVersion = 1;
}
}

View File

@ -33,8 +33,8 @@ void LCD::add_clocks(unsigned clocks) {
status.lx += clocks;
if(status.lx >= 456) scanline();
cpu.clock -= clocks;
if(cpu.clock <= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
clock += clocks;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
co_switch(scheduler.active_thread = cpu.thread);
}
}

View File

@ -22,7 +22,8 @@ void ICD2::enter() {
step(GameBoy::system.clocks_executed);
GameBoy::system.clocks_executed = 0;
} else { //DMG halted
step(4);
audio.coprocessor_sample(0x0000, 0x0000);
step(1);
}
synchronize_cpu();
}
@ -42,6 +43,9 @@ void ICD2::unload() {
}
void ICD2::power() {
audio.coprocessor_enable(true);
audio.coprocessor_frequency(4 * 1024 * 1024);
reset();
}

View File

@ -74,6 +74,7 @@ void ICD2::video_refresh(const uint8_t *data) {
}
void ICD2::audio_sample(signed left, signed right) {
audio.coprocessor_sample(left, right);
}
void ICD2::input_poll() {

View File

@ -1,7 +1,7 @@
namespace SNES {
namespace Info {
static const char Name[] = "bsnes";
static const char Version[] = "075.03";
static const char Version[] = "075.04";
static const unsigned SerializerVersion = 18;
}
}

View File

@ -12,7 +12,9 @@ void MainWindow::create() {
settings.create(*this, "Settings");
settingsVideoSync.create(settings, "Synchronize Video");
settingsVideoSync.setChecked(true);
settingsVideoSync.setChecked(false);
settingsAudioSync.create(settings, "Synchronize Audio");
settingsAudioSync.setChecked(true);
tools.create(*this, "Tools");
toolsSaveState.create(tools, "Save State");
@ -56,6 +58,10 @@ void MainWindow::create() {
video.set(Video::Synchronize, mainWindow.settingsVideoSync.checked());
};
settingsAudioSync.onTick = []() {
audio.set(Audio::Synchronize, mainWindow.settingsAudioSync.checked());
};
toolsSaveState1.onTick = []() { utility.saveState(1); };
toolsSaveState2.onTick = []() { utility.saveState(2); };
toolsSaveState3.onTick = []() { utility.saveState(3); };

View File

@ -6,6 +6,7 @@ struct MainWindow : Window {
Menu settings;
MenuCheckItem settingsVideoSync;
MenuCheckItem settingsAudioSync;
Menu tools;
Menu toolsSaveState;

View File

@ -29,6 +29,7 @@ void Interface::video_refresh(const uint8_t *data) {
}
void Interface::audio_sample(signed left, signed right) {
audio.sample(left, right);
}
void Interface::input_poll() {

View File

@ -28,10 +28,24 @@ void Application::main(int argc, char **argv) {
video.driver("OpenGL");
#endif
video.set(Video::Handle, (uintptr_t)mainWindow.viewport.handle());
video.set(Video::Synchronize, true);
video.set(Video::Synchronize, false);
video.set(Video::Filter, (unsigned)0);
video.init();
#if defined(PHOENIX_WINDOWS)
audio.driver("XAudio2");
#else
audio.driver("ALSA");
#endif
audio.set(Audio::Handle, (uintptr_t)mainWindow.viewport.handle());
audio.set(Audio::Synchronize, true);
audio.set(Audio::Volume, 100U);
audio.set(Audio::Latency, 80U);
audio.set(Audio::Frequency, 44100U);
audio.set(Audio::Resample, true);
audio.set(Audio::ResampleRatio, (4.0 * 1024.0 * 1024.0) / 44100.0);
audio.init();
#if defined(PHOENIX_WINDOWS)
input.driver("RawInput");
#else