diff --git a/bsnes/Makefile b/bsnes/Makefile index 3b20cc03..88a4caaf 100755 --- a/bsnes/Makefile +++ b/bsnes/Makefile @@ -2,7 +2,7 @@ include nall/Makefile snes := snes gameboy := gameboy profile := compatibility -ui := ui +ui := ui-gameboy # compiler c := $(compiler) -std=gnu99 diff --git a/bsnes/gameboy/apu/apu.cpp b/bsnes/gameboy/apu/apu.cpp index b4f4cd92..2b5ce1dd 100755 --- a/bsnes/gameboy/apu/apu.cpp +++ b/bsnes/gameboy/apu/apu.cpp @@ -1,12 +1,10 @@ #include -#include - #define APU_CPP namespace GameBoy { -#include "mmio/mmio.cpp" -#include "square/square.cpp" +#include "square1/square1.cpp" +#include "square2/square2.cpp" #include "wave/wave.cpp" #include "noise/noise.cpp" #include "master/master.cpp" @@ -23,25 +21,45 @@ void APU::main() { scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); } + if((counter & 8191) == 0) { //512hz + if(sequencer == 0 || sequencer == 2 || sequencer == 4 || sequencer == 6) { //256hz + square1.clock_length(); + square2.clock_length(); + wave.clock_length(); + noise.clock_length(); + } + if(sequencer == 2 || sequencer == 6) { //128hz + square1.clock_sweep(); + } + if(sequencer == 7) { //64hz + square1.clock_envelope(); + square2.clock_envelope(); + noise.clock_envelope(); + } + sequencer = (sequencer + 1) & 7; + } + square1.run(); square2.run(); wave.run(); noise.run(); master.run(); - system.interface->audio_sample((uint16)prng() >> 3, (uint16)prng() >> 3); + system.interface->audio_sample(master.center, master.left, master.right); + + if(++counter == 4194304) counter = 0; if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread); } } void APU::power() { - create(Main, 4 * 1024 * 1024); - + create(Main, 4194304); for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this; - square1.has_sweep = true; - square2.has_sweep = false; + foreach(n, mmio_data) n = 0x00; + counter = 0; + sequencer = 0; square1.power(); square2.power(); @@ -50,4 +68,40 @@ void APU::power() { master.power(); } +uint8 APU::mmio_read(uint16 addr) { + static const uint8 table[48] = { + 0x80, 0x3f, 0x00, 0xff, 0xbf, //square1 + 0xff, 0x3f, 0x00, 0xff, 0xbf, //square2 + 0x7f, 0xff, 0x9f, 0xff, 0xbf, //wave + 0xff, 0xff, 0x00, 0x00, 0xbf, //noise + 0x00, 0x00, 0x70, //master + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //unmapped + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern + }; + + if(addr == 0xff26) { + uint8 data = master.enable << 7; + if(square1.counter && square1.length) data |= 0x01; + if(square2.counter && square2.length) data |= 0x02; + if( wave.counter && wave.length) data |= 0x04; + if( noise.counter && noise.length) data |= 0x08; + return data | table[addr - 0xff10]; + } + + if(addr >= 0xff10 && addr <= 0xff3f) return mmio_data[addr - 0xff10] | table[addr - 0xff10]; + return 0xff; +} + +void APU::mmio_write(uint16 addr, uint8 data) { + if(addr >= 0xff10 && addr <= 0xff3f) mmio_data[addr - 0xff10] = data; + + 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); +} + } diff --git a/bsnes/gameboy/apu/apu.hpp b/bsnes/gameboy/apu/apu.hpp index 89159c44..935d92fe 100755 --- a/bsnes/gameboy/apu/apu.hpp +++ b/bsnes/gameboy/apu/apu.hpp @@ -1,12 +1,16 @@ struct APU : Processor, MMIO { - #include "mmio/mmio.hpp" - #include "square/square.hpp" + #include "square1/square1.hpp" + #include "square2/square2.hpp" #include "wave/wave.hpp" #include "noise/noise.hpp" #include "master/master.hpp" - Square square1; - Square square2; + uint8 mmio_data[48]; + unsigned counter; + unsigned sequencer; + + Square1 square1; + Square2 square2; Wave wave; Noise noise; Master master; @@ -15,6 +19,9 @@ struct APU : Processor, MMIO { void main(); void power(); + uint8 mmio_read(uint16 addr); + void mmio_write(uint16 addr, uint8 data); + void serialize(serializer&); }; diff --git a/bsnes/gameboy/apu/master/master.cpp b/bsnes/gameboy/apu/master/master.cpp index 1f565056..591d52d6 100755 --- a/bsnes/gameboy/apu/master/master.cpp +++ b/bsnes/gameboy/apu/master/master.cpp @@ -1,99 +1,132 @@ #ifdef APU_CPP void APU::Master::run() { -} + signed sample = 0, channels = 4; + sample += apu.square1.output; + sample += apu.square2.output; + sample += apu.wave.output; + sample += apu.noise.output; + sample /= channels; + if(sample < -32768) sample = -32768; + if(sample > +32767) sample = +32767; + center = sample; -uint8 APU::Master::read(unsigned r) { - if(r == 0) { - return (so2_enable << 7) | (so2_volume << 4) | (so1_enable << 3) | (so1_volume << 0); + sample = 0; + channels = 0; + if(channel1_left_enable) { sample += apu.square1.output; channels++; } + if(channel2_left_enable) { sample += apu.square2.output; channels++; } + if(channel3_left_enable) { sample += apu.wave.output; channels++; } + if(channel4_left_enable) { sample += apu.noise.output; channels++; } + if(channels) sample /= channels; + left = sample; + + switch(left_volume) { + case 0: left *= 0.125; break; + case 1: left *= 0.250; break; + case 2: left *= 0.375; break; + case 3: left *= 0.500; break; + case 4: left *= 0.625; break; + case 5: left *= 0.750; break; + case 6: left *= 0.875; break; + case 7: left *= 1.000; break; + } + if(left_enable == false) left = 0; + + sample = 0; + channels = 0; + if(channel1_right_enable) { sample += apu.square1.output; channels++; } + if(channel2_right_enable) { sample += apu.square2.output; channels++; } + if(channel3_right_enable) { sample += apu.wave.output; channels++; } + if(channel4_right_enable) { sample += apu.noise.output; channels++; } + if(channels) sample /= channels; + right = sample; + + switch(right_volume) { + case 0: right *= 0.125; break; + case 1: right *= 0.250; break; + case 2: right *= 0.375; break; + case 3: right *= 0.500; break; + case 4: right *= 0.625; break; + case 5: right *= 0.750; break; + case 6: right *= 0.875; break; + case 7: right *= 1.000; break; + } + if(right_enable == false) right = 0; + + if(left_enable == false && right_enable == false) { + left = center; + right = center; } - 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); + if(enable == false) { + center = 0; + left = 0; + right = 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; + left_enable = data & 0x80; + left_volume = (data >> 4) & 7; + right_enable = data & 0x08; + right_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; + channel4_left_enable = data & 0x80; + channel3_left_enable = data & 0x40; + channel2_left_enable = data & 0x20; + channel1_left_enable = data & 0x10; + channel4_right_enable = data & 0x08; + channel3_right_enable = data & 0x04; + channel2_right_enable = data & 0x02; + channel1_right_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; + left_enable = 0; + left_volume = 0; + right_enable = 0; + right_volume = 0; + channel4_left_enable = 0; + channel3_left_enable = 0; + channel2_left_enable = 0; + channel1_left_enable = 0; + channel4_right_enable = 0; + channel3_right_enable = 0; + channel2_right_enable = 0; + channel1_right_enable = 0; enable = 0; - channel4_enable = 0; - channel3_enable = 0; - channel2_enable = 0; - channel1_enable = 0; + + center = 0; + left = 0; + right = 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(left_enable); + s.integer(left_volume); + s.integer(right_enable); + s.integer(right_volume); + s.integer(channel4_left_enable); + s.integer(channel3_left_enable); + s.integer(channel2_left_enable); + s.integer(channel1_left_enable); + s.integer(channel4_right_enable); + s.integer(channel3_right_enable); + s.integer(channel2_right_enable); + s.integer(channel1_right_enable); s.integer(enable); - s.integer(channel4_enable); - s.integer(channel3_enable); - s.integer(channel2_enable); - s.integer(channel1_enable); + + s.integer(center); + s.integer(left); + s.integer(right); } #endif diff --git a/bsnes/gameboy/apu/master/master.hpp b/bsnes/gameboy/apu/master/master.hpp index 86982de4..fb079891 100755 --- a/bsnes/gameboy/apu/master/master.hpp +++ b/bsnes/gameboy/apu/master/master.hpp @@ -1,24 +1,23 @@ 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 left_enable; + unsigned left_volume; + bool right_enable; + unsigned right_volume; + bool channel4_left_enable; + bool channel3_left_enable; + bool channel2_left_enable; + bool channel1_left_enable; + bool channel4_right_enable; + bool channel3_right_enable; + bool channel2_right_enable; + bool channel1_right_enable; bool enable; - bool channel4_enable; - bool channel3_enable; - bool channel2_enable; - bool channel1_enable; + + int16 center; + int16 left; + int16 right; 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 deleted file mode 100755 index 06cc23e8..00000000 --- a/bsnes/gameboy/apu/mmio/mmio.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifdef APU_CPP - -uint8 APU::mmio_read(uint16 addr) { - 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 && 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 deleted file mode 100755 index 92b8f614..00000000 --- a/bsnes/gameboy/apu/mmio/mmio.hpp +++ /dev/null @@ -1,2 +0,0 @@ -uint8 mmio_read(uint16 addr); -void mmio_write(uint16 addr, uint8 data); diff --git a/bsnes/gameboy/apu/noise/noise.cpp b/bsnes/gameboy/apu/noise/noise.cpp index 1d65a694..31855373 100755 --- a/bsnes/gameboy/apu/noise/noise.cpp +++ b/bsnes/gameboy/apu/noise/noise.cpp @@ -1,66 +1,102 @@ #ifdef APU_CPP void APU::Noise::run() { + if(period && --period == 0) { + period = divisor << frequency; + if(frequency < 14) { + bool bit = (lfsr ^ (lfsr >> 1)) & 1; + lfsr = (lfsr >> 1) ^ (bit << 14); + if(narrow_lfsr) lfsr |= (bit << 6); + } + } + + uint4 sample = (lfsr & 1) ? -volume : volume; + if(counter && length == 0) sample = 0; + + output = (sample * 4369) - 32768; } -uint8 APU::Noise::read(unsigned r) { - if(r == 2) { - return (envelope_volume << 4) | (envelope_direction << 3) | (envelope_period << 0); - } +void APU::Noise::clock_length() { + if(counter && length) length--; +} - if(r == 3) { - return (period << 4) | (step << 3) | (divisor); +void APU::Noise::clock_envelope() { + if(envelope_period && --envelope_period == 0) { + envelope_period = envelope_frequency; + if(envelope_direction == 0 && volume > 0) volume--; + if(envelope_direction == 1 && volume < 15) volume++; } - - if(r == 4) { - return (counter << 6); - } - - return 0x00; } void APU::Noise::write(unsigned r, uint8 data) { if(r == 1) { - length = data & 0x3f; + initial_length = 64 - (data & 0x3f); + + length = initial_length; } if(r == 2) { envelope_volume = data >> 4; envelope_direction = data & 0x08; - envelope_period = data & 0x07; + envelope_frequency = data & 0x07; } if(r == 3) { - period = data >> 4; - step = data & 0x08; - divisor = data & 0x07; + frequency = data >> 4; + narrow_lfsr = data & 0x08; + divisor = (data & 0x07) << 4; + if(divisor == 0) divisor = 8; + + period = divisor << frequency; } if(r == 4) { + bool initialize = data & 0x80; counter = data & 0x40; + + if(initialize) { + lfsr = ~0U; + length = initial_length; + envelope_period = envelope_frequency; + volume = envelope_volume; + } } } void APU::Noise::power() { - length = 0; + initial_length = 0; envelope_volume = 0; envelope_direction = 0; - envelope_period = 0; - period = 0; - step = 0; + envelope_frequency = 0; + frequency = 0; + narrow_lfsr = 0; divisor = 0; counter = 0; + + output = 0; + length = 0; + envelope_period = 0; + volume = 0; + period = 0; + lfsr = 0; } void APU::Noise::serialize(serializer &s) { - s.integer(length); + s.integer(initial_length); s.integer(envelope_volume); s.integer(envelope_direction); - s.integer(envelope_period); - s.integer(period); - s.integer(step); + s.integer(envelope_frequency); + s.integer(frequency); + s.integer(narrow_lfsr); s.integer(divisor); s.integer(counter); + + s.integer(output); + s.integer(length); + s.integer(envelope_period); + s.integer(volume); + s.integer(period); + s.integer(lfsr); } #endif diff --git a/bsnes/gameboy/apu/noise/noise.hpp b/bsnes/gameboy/apu/noise/noise.hpp index a48c2787..2e6c7167 100755 --- a/bsnes/gameboy/apu/noise/noise.hpp +++ b/bsnes/gameboy/apu/noise/noise.hpp @@ -1,15 +1,23 @@ struct Noise { - unsigned length; + unsigned initial_length; unsigned envelope_volume; bool envelope_direction; - unsigned envelope_period; - unsigned period; - bool step; + unsigned envelope_frequency; + unsigned frequency; + bool narrow_lfsr; unsigned divisor; bool counter; + int16 output; + unsigned length; + unsigned envelope_period; + unsigned volume; + unsigned period; + uint15 lfsr; + void run(); - uint8 read(unsigned r); + void clock_length(); + void clock_envelope(); 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 74c61d03..e781d5a4 100755 --- a/bsnes/gameboy/apu/serialization.cpp +++ b/bsnes/gameboy/apu/serialization.cpp @@ -1,6 +1,10 @@ #ifdef APU_CPP void APU::serialize(serializer &s) { + s.array(mmio_data); + s.integer(counter); + s.integer(sequencer); + square1.serialize(s); square2.serialize(s); wave.serialize(s); diff --git a/bsnes/gameboy/apu/square/square.cpp b/bsnes/gameboy/apu/square/square.cpp deleted file mode 100755 index dbc228d9..00000000 --- a/bsnes/gameboy/apu/square/square.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#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/square1/square1.cpp b/bsnes/gameboy/apu/square1/square1.cpp new file mode 100755 index 00000000..6f31ded4 --- /dev/null +++ b/bsnes/gameboy/apu/square1/square1.cpp @@ -0,0 +1,146 @@ +#ifdef APU_CPP + +void APU::Square1::run() { + if(period && --period == 0) { + period = 4 * (2048 - frequency); + phase = (phase + 1) & 7; + switch(duty) { + case 0: duty_output = (phase == 6); break; //______-_ + case 1: duty_output = (phase >= 6); break; //______-- + case 2: duty_output = (phase >= 4); break; //____---- + case 3: duty_output = (phase <= 5); break; //------__ + } + } + + uint4 sample = (duty_output ? volume : 0); + if(counter && length == 0) sample = 0; + + output = (sample * 4369) - 32768; +} + +void APU::Square1::sweep() { + if(enable == false) return; + + signed offset = frequency_shadow >> sweep_shift; + if(sweep_direction) offset = -offset; + frequency_shadow += offset; + + if(frequency_shadow < 0) { + frequency_shadow = 0; + } else if(frequency_shadow > 2047) { + frequency_shadow = 2048; + enable = false; + } + + if(frequency_shadow <= 2047 && sweep_shift) { + frequency = frequency_shadow; + period = 4 * (2048 - frequency); + } +} + +void APU::Square1::clock_length() { + if(counter && length) length--; +} + +void APU::Square1::clock_sweep() { + if(sweep_frequency && sweep_period && --sweep_period == 0) { + sweep_period = sweep_frequency; + sweep(); + } +} + +void APU::Square1::clock_envelope() { + if(envelope_period && --envelope_period == 0) { + envelope_period = envelope_frequency; + if(envelope_direction == 0 && volume > 0) volume--; + if(envelope_direction == 1 && volume < 15) volume++; + } +} + +void APU::Square1::write(unsigned r, uint8 data) { + if(r == 0) { + sweep_frequency = (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_frequency = 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); + + if(initialize) { + envelope_period = envelope_frequency; + volume = envelope_volume; + frequency_shadow = frequency; + sweep_period = sweep_frequency; + enable = sweep_period || sweep_shift; + if(sweep_shift) sweep(); + } + } + + period = 4 * (2048 - frequency); +} + +void APU::Square1::power() { + sweep_frequency = 0; + sweep_direction = 0; + sweep_shift = 0; + duty = 0; + length = 0; + envelope_volume = 0; + envelope_direction = 0; + envelope_frequency = 0; + frequency = 0; + counter = 0; + + output = 0; + duty_output = 0; + phase = 0; + period = 0; + envelope_period = 0; + sweep_period = 0; + frequency_shadow = 0; + enable = 0; + volume = 0; +} + +void APU::Square1::serialize(serializer &s) { + s.integer(sweep_frequency); + 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_frequency); + s.integer(frequency); + s.integer(counter); + + s.integer(output); + s.integer(duty_output); + s.integer(phase); + s.integer(period); + s.integer(envelope_period); + s.integer(sweep_period); + s.integer(frequency_shadow); + s.integer(enable); + s.integer(volume); +} + +#endif diff --git a/bsnes/gameboy/apu/square1/square1.hpp b/bsnes/gameboy/apu/square1/square1.hpp new file mode 100755 index 00000000..eb45df49 --- /dev/null +++ b/bsnes/gameboy/apu/square1/square1.hpp @@ -0,0 +1,31 @@ +struct Square1 { + unsigned sweep_frequency; + unsigned sweep_direction; + unsigned sweep_shift; + unsigned duty; + unsigned length; + unsigned envelope_volume; + unsigned envelope_direction; + unsigned envelope_frequency; + unsigned frequency; + unsigned counter; + + int16 output; + bool duty_output; + unsigned phase; + unsigned period; + unsigned envelope_period; + unsigned sweep_period; + signed frequency_shadow; + bool enable; + unsigned volume; + + void run(); + void sweep(); + void clock_length(); + void clock_sweep(); + void clock_envelope(); + void write(unsigned r, uint8 data); + void power(); + void serialize(serializer&); +}; diff --git a/bsnes/gameboy/apu/square2/square2.cpp b/bsnes/gameboy/apu/square2/square2.cpp new file mode 100755 index 00000000..1191ce48 --- /dev/null +++ b/bsnes/gameboy/apu/square2/square2.cpp @@ -0,0 +1,97 @@ +#ifdef APU_CPP + +void APU::Square2::run() { + if(period && --period == 0) { + period = 4 * (2048 - frequency); + phase = (phase + 1) & 7; + switch(duty) { + case 0: duty_output = (phase == 6); break; //______-_ + case 1: duty_output = (phase >= 6); break; //______-- + case 2: duty_output = (phase >= 4); break; //____---- + case 3: duty_output = (phase <= 5); break; //------__ + } + } + + uint4 sample = (duty_output ? volume : 0); + if(counter && length == 0) sample = 0; + + output = (sample * 4369) - 32768; +} + +void APU::Square2::clock_length() { + if(counter && length) length--; +} + +void APU::Square2::clock_envelope() { + if(envelope_period && --envelope_period == 0) { + envelope_period = envelope_frequency; + if(envelope_direction == 0 && volume > 0) volume--; + if(envelope_direction == 1 && volume < 15) volume++; + } +} + +void APU::Square2::write(unsigned r, uint8 data) { + if(r == 1) { + duty = data >> 6; + length = data & 0x3f; + } + + if(r == 2) { + envelope_volume = data >> 4; + envelope_direction = data & 0x08; + envelope_frequency = 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); + + if(initialize) { + envelope_period = envelope_frequency; + volume = envelope_volume; + } + } + + period = 4 * (2048 - frequency); +} + +void APU::Square2::power() { + duty = 0; + length = 0; + envelope_volume = 0; + envelope_direction = 0; + envelope_frequency = 0; + frequency = 0; + counter = 0; + + output = 0; + duty_output = 0; + phase = 0; + period = 0; + envelope_period = 0; + volume = 0; +} + +void APU::Square2::serialize(serializer &s) { + s.integer(duty); + s.integer(length); + s.integer(envelope_volume); + s.integer(envelope_direction); + s.integer(envelope_frequency); + s.integer(frequency); + s.integer(counter); + + s.integer(output); + s.integer(duty_output); + s.integer(phase); + s.integer(period); + s.integer(envelope_period); + s.integer(volume); +} + +#endif diff --git a/bsnes/gameboy/apu/square/square.hpp b/bsnes/gameboy/apu/square2/square2.hpp similarity index 58% rename from bsnes/gameboy/apu/square/square.hpp rename to bsnes/gameboy/apu/square2/square2.hpp index 21068242..6f4a9e1a 100755 --- a/bsnes/gameboy/apu/square/square.hpp +++ b/bsnes/gameboy/apu/square2/square2.hpp @@ -1,19 +1,22 @@ -struct Square { - bool has_sweep; - - unsigned sweep_period; - unsigned sweep_direction; - unsigned sweep_shift; +struct Square2 { unsigned duty; unsigned length; unsigned envelope_volume; unsigned envelope_direction; - unsigned envelope_period; + unsigned envelope_frequency; unsigned frequency; unsigned counter; + int16 output; + bool duty_output; + unsigned phase; + unsigned period; + unsigned envelope_period; + unsigned volume; + void run(); - uint8 read(unsigned r); + void clock_length(); + void clock_envelope(); 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 index 2af1bf2d..449c99df 100755 --- a/bsnes/gameboy/apu/wave/wave.cpp +++ b/bsnes/gameboy/apu/wave/wave.cpp @@ -1,31 +1,40 @@ #ifdef APU_CPP void APU::Wave::run() { + if(period && --period == 0) { + period = 2 * (2048 - frequency); + pattern_offset = (pattern_offset + 1) & 31; + pattern_sample = pattern[pattern_offset]; + } + + uint4 sample = pattern_sample; + if(counter && length == 0) sample = 0; + if(enable == false) sample = 0; + + output = (sample * 4369) - 32768; + switch(volume) { + case 0: output *= 0.00; break; + case 1: output *= 1.00; break; + case 2: output *= 0.50; break; + case 3: output *= 0.25; break; + } } -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::clock_length() { + if(counter && length) length--; } void APU::Wave::write(unsigned r, uint8 data) { if(r == 0) { - off = data & 0x80; + dac_enable = data & 0x80; + + if(dac_enable == false) enable = false; } if(r == 1) { - length = data; + initial_length = 256 - data; + + length = initial_length; } if(r == 2) { @@ -40,36 +49,55 @@ void APU::Wave::write(unsigned r, uint8 data) { 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); + if(initialize && dac_enable) { + enable = true; + pattern_offset = 0; + length = initial_length; + } + } + + period = 2 * (2048 - frequency); } void APU::Wave::write_pattern(unsigned p, uint8 data) { p <<= 1; - pattern[p + 0] = data >> 4; - pattern[p + 1] = data >> 0; + pattern[p + 0] = (data >> 4) & 15; + pattern[p + 1] = (data >> 0) & 15; } void APU::Wave::power() { - off = 0; - length = 0; + dac_enable = 0; + initial_length = 0; volume = 0; frequency = 0; counter = 0; - foreach(n, pattern) n = 0; + + random_cyclic r; + foreach(n, pattern) n = r() & 15; + + output = 0; + enable = 0; + length = 0; + period = 0; + pattern_offset = 0; + pattern_sample = 0; } void APU::Wave::serialize(serializer &s) { - s.integer(off); - s.integer(length); + s.integer(dac_enable); + s.integer(initial_length); s.integer(volume); s.integer(frequency); s.integer(counter); s.array(pattern); + + s.integer(output); + s.integer(enable); + s.integer(length); + s.integer(period); + s.integer(pattern_offset); + s.integer(pattern_sample); } #endif diff --git a/bsnes/gameboy/apu/wave/wave.hpp b/bsnes/gameboy/apu/wave/wave.hpp index 73b87c46..d4f19ad1 100755 --- a/bsnes/gameboy/apu/wave/wave.hpp +++ b/bsnes/gameboy/apu/wave/wave.hpp @@ -1,15 +1,21 @@ struct Wave { - bool off; - unsigned length; + bool dac_enable; + unsigned initial_length; unsigned volume; unsigned frequency; bool counter; uint8 pattern[32]; + int16 output; + bool enable; + unsigned length; + unsigned period; + unsigned pattern_offset; + unsigned pattern_sample; + void run(); - uint8 read(unsigned r); + void clock_length(); 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/cpu.cpp b/bsnes/gameboy/cpu/cpu.cpp index b195da7c..0b30ba1f 100755 --- a/bsnes/gameboy/cpu/cpu.cpp +++ b/bsnes/gameboy/cpu/cpu.cpp @@ -114,13 +114,7 @@ void CPU::power() { status.clock = 0; status.halt = false; status.stop = false; - status.ime = 0; - status.timer0 = 0; - status.timer1 = 0; - status.timer2 = 0; - status.timer3 = 0; - status.timer4 = 0; status.p15 = 0; status.p14 = 0; diff --git a/bsnes/gameboy/cpu/cpu.hpp b/bsnes/gameboy/cpu/cpu.hpp index f06bb551..cbfe193a 100755 --- a/bsnes/gameboy/cpu/cpu.hpp +++ b/bsnes/gameboy/cpu/cpu.hpp @@ -17,13 +17,7 @@ struct CPU : Processor, MMIO { unsigned clock; bool halt; bool stop; - bool ime; - unsigned timer0; - unsigned timer1; - unsigned timer2; - unsigned timer3; - unsigned timer4; //$ff00 JOYP bool p15; diff --git a/bsnes/gameboy/cpu/serialization.cpp b/bsnes/gameboy/cpu/serialization.cpp index 990c1836..f122f97f 100755 --- a/bsnes/gameboy/cpu/serialization.cpp +++ b/bsnes/gameboy/cpu/serialization.cpp @@ -21,13 +21,7 @@ void CPU::serialize(serializer &s) { s.integer(status.clock); s.integer(status.halt); s.integer(status.stop); - s.integer(status.ime); - s.integer(status.timer0); - s.integer(status.timer1); - s.integer(status.timer2); - s.integer(status.timer3); - s.integer(status.timer4); s.integer(status.p15); s.integer(status.p14); diff --git a/bsnes/gameboy/cpu/timing/timing.cpp b/bsnes/gameboy/cpu/timing/timing.cpp index fca4d897..215e8977 100755 --- a/bsnes/gameboy/cpu/timing/timing.cpp +++ b/bsnes/gameboy/cpu/timing/timing.cpp @@ -22,8 +22,12 @@ void CPU::add_clocks(unsigned clocks) { cartridge.mbc3.second(); } - status.timer0 += clocks; - if(status.timer0 >= 16) timer_stage0(); + //4194304 / N(hz) - 1 = mask + if((status.clock & 15) == 0) timer_262144hz(); + if((status.clock & 63) == 0) timer_65536hz(); + if((status.clock & 255) == 0) timer_16384hz(); + if((status.clock & 511) == 0) timer_8192hz(); + if((status.clock & 1023) == 0) timer_4096hz(); lcd.clock -= clocks; if(lcd.clock <= 0) co_switch(scheduler.active_thread = lcd.thread); @@ -32,31 +36,25 @@ void CPU::add_clocks(unsigned clocks) { if(apu.clock <= 0) co_switch(scheduler.active_thread = apu.thread); } -void CPU::timer_stage0() { //262144hz +void CPU::timer_262144hz() { if(status.timer_enable && status.timer_clock == 1) { if(++status.tima == 0) { status.tima = status.tma; interrupt_raise(Interrupt::Timer); } } - - status.timer0 -= 16; - if(++status.timer1 >= 4) timer_stage1(); } -void CPU::timer_stage1() { // 65536hz +void CPU::timer_65536hz() { if(status.timer_enable && status.timer_clock == 2) { if(++status.tima == 0) { status.tima = status.tma; interrupt_raise(Interrupt::Timer); } } - - status.timer1 -= 4; - if(++status.timer2 >= 4) timer_stage2(); } -void CPU::timer_stage2() { // 16384hz +void CPU::timer_16384hz() { if(status.timer_enable && status.timer_clock == 3) { if(++status.tima == 0) { status.tima = status.tma; @@ -65,32 +63,24 @@ void CPU::timer_stage2() { // 16384hz } status.div++; - - status.timer2 -= 4; - if(++status.timer3 >= 2) timer_stage3(); } -void CPU::timer_stage3() { // 8192hz +void CPU::timer_8192hz() { if(status.serial_transfer && status.serial_clock) { if(--status.serial_bits == 0) { status.serial_transfer = 0; interrupt_raise(Interrupt::Serial); } } - - status.timer3 -= 2; - if(++status.timer4 >= 2) timer_stage4(); } -void CPU::timer_stage4() { // 4096hz +void CPU::timer_4096hz() { if(status.timer_enable && status.timer_clock == 0) { if(++status.tima == 0) { status.tima = status.tma; interrupt_raise(Interrupt::Timer); } } - - status.timer4 -= 2; } #endif diff --git a/bsnes/gameboy/cpu/timing/timing.hpp b/bsnes/gameboy/cpu/timing/timing.hpp index 1635f60c..9fead0ea 100755 --- a/bsnes/gameboy/cpu/timing/timing.hpp +++ b/bsnes/gameboy/cpu/timing/timing.hpp @@ -1,9 +1,9 @@ void add_clocks(unsigned clocks); -void timer_stage0(); -void timer_stage1(); -void timer_stage2(); -void timer_stage3(); -void timer_stage4(); +void timer_262144hz(); +void timer_65536hz(); +void timer_16384hz(); +void timer_8192hz(); +void timer_4096hz(); //opcode.cpp void op_io(); diff --git a/bsnes/gameboy/gameboy.hpp b/bsnes/gameboy/gameboy.hpp index 1658ac1f..9a47a5ba 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.15"; + static const char Version[] = "000.16"; static unsigned SerializerVersion = 1; } } @@ -15,22 +15,30 @@ namespace GameBoy { #include #include #include +#include #include #include #include +#include using namespace nall; namespace GameBoy { - typedef int8_t int8; - typedef int16_t int16; - typedef int32_t int32; - typedef int64_t int64; + typedef int8_t int8; + typedef int16_t int16; + typedef int32_t int32; + typedef int64_t int64; typedef uint8_t uint8; typedef uint16_t uint16; typedef uint32_t uint32; typedef uint64_t uint64; + typedef int_t< 4> int4; + typedef int_t<15> int15; + + typedef uint_t< 4> uint4; + typedef uint_t<15> uint15; + template alwaysinline bool within(uint16 addr) { static const uint16 mask = ~(hi ^ lo); diff --git a/bsnes/gameboy/interface/interface.hpp b/bsnes/gameboy/interface/interface.hpp index 372cbd7f..982fd989 100755 --- a/bsnes/gameboy/interface/interface.hpp +++ b/bsnes/gameboy/interface/interface.hpp @@ -3,7 +3,7 @@ public: virtual void joyp_write(bool p15, bool p14) {} virtual void video_refresh(const uint8_t *data) {} - virtual void audio_sample(signed left, signed right) {} + virtual void audio_sample(int16_t center, int16_t left, int16_t right) {} virtual void input_poll() {} virtual bool input_poll(unsigned id) {} diff --git a/bsnes/snes/chip/icd2/interface/interface.cpp b/bsnes/snes/chip/icd2/interface/interface.cpp index b654ca5d..2dcac5b3 100755 --- a/bsnes/snes/chip/icd2/interface/interface.cpp +++ b/bsnes/snes/chip/icd2/interface/interface.cpp @@ -73,7 +73,7 @@ void ICD2::joyp_write(bool p15, bool p14) { void ICD2::video_refresh(const uint8_t *data) { } -void ICD2::audio_sample(signed left, signed right) { +void ICD2::audio_sample(int16_t center, int16_t left, int16_t right) { audio.coprocessor_sample(left, right); } diff --git a/bsnes/snes/chip/icd2/interface/interface.hpp b/bsnes/snes/chip/icd2/interface/interface.hpp index 2227706f..2f88ca91 100755 --- a/bsnes/snes/chip/icd2/interface/interface.hpp +++ b/bsnes/snes/chip/icd2/interface/interface.hpp @@ -1,6 +1,6 @@ void joyp_write(bool p15, bool p14); void video_refresh(const uint8_t *data); -void audio_sample(signed left, signed right); +void audio_sample(int16_t center, int16_t left, int16_t right); void input_poll(); bool input_poll(unsigned id); diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index 8f76fb7d..f3498ee9 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.04"; + static const char Version[] = "075.05"; static const unsigned SerializerVersion = 18; } } diff --git a/bsnes/ui-gameboy/interface.cpp b/bsnes/ui-gameboy/interface.cpp index 05c557f6..15a1e0d5 100755 --- a/bsnes/ui-gameboy/interface.cpp +++ b/bsnes/ui-gameboy/interface.cpp @@ -28,7 +28,7 @@ void Interface::video_refresh(const uint8_t *data) { } } -void Interface::audio_sample(signed left, signed right) { +void Interface::audio_sample(int16_t center, int16_t left, int16_t right) { audio.sample(left, right); } diff --git a/bsnes/ui-gameboy/interface.hpp b/bsnes/ui-gameboy/interface.hpp index d6910bb9..efeeca5f 100755 --- a/bsnes/ui-gameboy/interface.hpp +++ b/bsnes/ui-gameboy/interface.hpp @@ -2,7 +2,7 @@ struct Interface : public GameBoy::Interface { int16_t inputState[Scancode::Limit]; void video_refresh(const uint8_t *data); - void audio_sample(signed left, signed right); + void audio_sample(int16_t center, int16_t left, int16_t right); void input_poll(); bool input_poll(unsigned id); diff --git a/bsnes/ui-gameboy/utility/utility.cpp b/bsnes/ui-gameboy/utility/utility.cpp index 02ade276..183ffa73 100755 --- a/bsnes/ui-gameboy/utility/utility.cpp +++ b/bsnes/ui-gameboy/utility/utility.cpp @@ -10,7 +10,6 @@ void Utility::loadCartridge(const char *filename) { fp.close(); cartridge.basename = nall::basename(filename); - print(cartridge.basename, "\n"); GameBoyCartridge info(data, size); GameBoy::cartridge.load(info.xml, data, size);