mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
a136378a7b
commit
f88ef9e9a2
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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
|
|
@ -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&);
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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&);
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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&);
|
||||
};
|
|
@ -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
|
|
@ -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&);
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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); };
|
||||
|
|
|
@ -6,6 +6,7 @@ struct MainWindow : Window {
|
|||
|
||||
Menu settings;
|
||||
MenuCheckItem settingsVideoSync;
|
||||
MenuCheckItem settingsAudioSync;
|
||||
|
||||
Menu tools;
|
||||
Menu toolsSaveState;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue