From f88ef9e9a22be82049095150e97017175893d4c7 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Wed, 2 Feb 2011 21:37:31 +1100 Subject: [PATCH] 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. --- bsnes/gameboy/apu/apu.cpp | 106 +++----- bsnes/gameboy/apu/apu.hpp | 12 + bsnes/gameboy/apu/master/master.cpp | 99 ++++++++ bsnes/gameboy/apu/master/master.hpp | 25 ++ bsnes/gameboy/apu/mmio/mmio.cpp | 250 +------------------ bsnes/gameboy/apu/mmio/mmio.hpp | 100 -------- bsnes/gameboy/apu/noise/noise.cpp | 66 +++++ bsnes/gameboy/apu/noise/noise.hpp | 16 ++ bsnes/gameboy/apu/serialization.cpp | 5 + bsnes/gameboy/apu/square/square.cpp | 85 +++++++ bsnes/gameboy/apu/square/square.hpp | 20 ++ bsnes/gameboy/apu/wave/wave.cpp | 75 ++++++ bsnes/gameboy/apu/wave/wave.hpp | 16 ++ bsnes/gameboy/cpu/timing/timing.cpp | 7 +- bsnes/gameboy/gameboy.hpp | 2 +- bsnes/gameboy/lcd/lcd.cpp | 4 +- bsnes/snes/chip/icd2/icd2.cpp | 6 +- bsnes/snes/chip/icd2/interface/interface.cpp | 1 + bsnes/snes/snes.hpp | 2 +- bsnes/ui-gameboy/general/main-window.cpp | 8 +- bsnes/ui-gameboy/general/main-window.hpp | 1 + bsnes/ui-gameboy/interface.cpp | 1 + bsnes/ui-gameboy/main.cpp | 16 +- 23 files changed, 507 insertions(+), 416 deletions(-) create mode 100755 bsnes/gameboy/apu/master/master.cpp create mode 100755 bsnes/gameboy/apu/master/master.hpp create mode 100755 bsnes/gameboy/apu/noise/noise.cpp create mode 100755 bsnes/gameboy/apu/noise/noise.hpp create mode 100755 bsnes/gameboy/apu/square/square.cpp create mode 100755 bsnes/gameboy/apu/square/square.hpp create mode 100755 bsnes/gameboy/apu/wave/wave.cpp create mode 100755 bsnes/gameboy/apu/wave/wave.hpp diff --git a/bsnes/gameboy/apu/apu.cpp b/bsnes/gameboy/apu/apu.cpp index 80efe0a2..b4f4cd92 100755 --- a/bsnes/gameboy/apu/apu.cpp +++ b/bsnes/gameboy/apu/apu.cpp @@ -1,85 +1,53 @@ #include +#include + #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(); } } diff --git a/bsnes/gameboy/apu/apu.hpp b/bsnes/gameboy/apu/apu.hpp index e9e5546f..89159c44 100755 --- a/bsnes/gameboy/apu/apu.hpp +++ b/bsnes/gameboy/apu/apu.hpp @@ -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&); diff --git a/bsnes/gameboy/apu/master/master.cpp b/bsnes/gameboy/apu/master/master.cpp new file mode 100755 index 00000000..1f565056 --- /dev/null +++ b/bsnes/gameboy/apu/master/master.cpp @@ -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 diff --git a/bsnes/gameboy/apu/master/master.hpp b/bsnes/gameboy/apu/master/master.hpp new file mode 100755 index 00000000..86982de4 --- /dev/null +++ b/bsnes/gameboy/apu/master/master.hpp @@ -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&); +}; diff --git a/bsnes/gameboy/apu/mmio/mmio.cpp b/bsnes/gameboy/apu/mmio/mmio.cpp index adb88c68..06cc23e8 100755 --- a/bsnes/gameboy/apu/mmio/mmio.cpp +++ b/bsnes/gameboy/apu/mmio/mmio.cpp @@ -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 diff --git a/bsnes/gameboy/apu/mmio/mmio.hpp b/bsnes/gameboy/apu/mmio/mmio.hpp index d28130a3..92b8f614 100755 --- a/bsnes/gameboy/apu/mmio/mmio.hpp +++ b/bsnes/gameboy/apu/mmio/mmio.hpp @@ -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; diff --git a/bsnes/gameboy/apu/noise/noise.cpp b/bsnes/gameboy/apu/noise/noise.cpp new file mode 100755 index 00000000..1d65a694 --- /dev/null +++ b/bsnes/gameboy/apu/noise/noise.cpp @@ -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 diff --git a/bsnes/gameboy/apu/noise/noise.hpp b/bsnes/gameboy/apu/noise/noise.hpp new file mode 100755 index 00000000..a48c2787 --- /dev/null +++ b/bsnes/gameboy/apu/noise/noise.hpp @@ -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&); +}; diff --git a/bsnes/gameboy/apu/serialization.cpp b/bsnes/gameboy/apu/serialization.cpp index e9fd915b..74c61d03 100755 --- a/bsnes/gameboy/apu/serialization.cpp +++ b/bsnes/gameboy/apu/serialization.cpp @@ -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 diff --git a/bsnes/gameboy/apu/square/square.cpp b/bsnes/gameboy/apu/square/square.cpp new file mode 100755 index 00000000..dbc228d9 --- /dev/null +++ b/bsnes/gameboy/apu/square/square.cpp @@ -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 diff --git a/bsnes/gameboy/apu/square/square.hpp b/bsnes/gameboy/apu/square/square.hpp new file mode 100755 index 00000000..21068242 --- /dev/null +++ b/bsnes/gameboy/apu/square/square.hpp @@ -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&); +}; diff --git a/bsnes/gameboy/apu/wave/wave.cpp b/bsnes/gameboy/apu/wave/wave.cpp new file mode 100755 index 00000000..2af1bf2d --- /dev/null +++ b/bsnes/gameboy/apu/wave/wave.cpp @@ -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 diff --git a/bsnes/gameboy/apu/wave/wave.hpp b/bsnes/gameboy/apu/wave/wave.hpp new file mode 100755 index 00000000..73b87c46 --- /dev/null +++ b/bsnes/gameboy/apu/wave/wave.hpp @@ -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&); +}; diff --git a/bsnes/gameboy/cpu/timing/timing.cpp b/bsnes/gameboy/cpu/timing/timing.cpp index bbdad880..fca4d897 100755 --- a/bsnes/gameboy/cpu/timing/timing.cpp +++ b/bsnes/gameboy/cpu/timing/timing.cpp @@ -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 diff --git a/bsnes/gameboy/gameboy.hpp b/bsnes/gameboy/gameboy.hpp index c939fff4..1658ac1f 100755 --- a/bsnes/gameboy/gameboy.hpp +++ b/bsnes/gameboy/gameboy.hpp @@ -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; } } diff --git a/bsnes/gameboy/lcd/lcd.cpp b/bsnes/gameboy/lcd/lcd.cpp index 19c57ea6..78e85ea2 100755 --- a/bsnes/gameboy/lcd/lcd.cpp +++ b/bsnes/gameboy/lcd/lcd.cpp @@ -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); } } diff --git a/bsnes/snes/chip/icd2/icd2.cpp b/bsnes/snes/chip/icd2/icd2.cpp index 1af431f8..e82704ec 100755 --- a/bsnes/snes/chip/icd2/icd2.cpp +++ b/bsnes/snes/chip/icd2/icd2.cpp @@ -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(); } diff --git a/bsnes/snes/chip/icd2/interface/interface.cpp b/bsnes/snes/chip/icd2/interface/interface.cpp index f4685d8b..b654ca5d 100755 --- a/bsnes/snes/chip/icd2/interface/interface.cpp +++ b/bsnes/snes/chip/icd2/interface/interface.cpp @@ -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() { diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index e8a9d661..8f76fb7d 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -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; } } diff --git a/bsnes/ui-gameboy/general/main-window.cpp b/bsnes/ui-gameboy/general/main-window.cpp index a93fa165..c32dfc28 100755 --- a/bsnes/ui-gameboy/general/main-window.cpp +++ b/bsnes/ui-gameboy/general/main-window.cpp @@ -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); }; diff --git a/bsnes/ui-gameboy/general/main-window.hpp b/bsnes/ui-gameboy/general/main-window.hpp index 6d752ba1..6a3b6770 100755 --- a/bsnes/ui-gameboy/general/main-window.hpp +++ b/bsnes/ui-gameboy/general/main-window.hpp @@ -6,6 +6,7 @@ struct MainWindow : Window { Menu settings; MenuCheckItem settingsVideoSync; + MenuCheckItem settingsAudioSync; Menu tools; Menu toolsSaveState; diff --git a/bsnes/ui-gameboy/interface.cpp b/bsnes/ui-gameboy/interface.cpp index 4cda5ac3..05c557f6 100755 --- a/bsnes/ui-gameboy/interface.cpp +++ b/bsnes/ui-gameboy/interface.cpp @@ -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() { diff --git a/bsnes/ui-gameboy/main.cpp b/bsnes/ui-gameboy/main.cpp index b0162ff6..e614b8ac 100755 --- a/bsnes/ui-gameboy/main.cpp +++ b/bsnes/ui-gameboy/main.cpp @@ -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