diff --git a/bsnes/base/base.hpp b/bsnes/base/base.hpp index cb1edb92..e282b678 100755 --- a/bsnes/base/base.hpp +++ b/bsnes/base/base.hpp @@ -1,7 +1,7 @@ #ifndef BASE_HPP #define BASE_HPP -static const char Version[] = "087.16"; +static const char Version[] = "087.17"; #include #include diff --git a/bsnes/gb/apu/noise/noise.cpp b/bsnes/gb/apu/noise/noise.cpp index c0def117..f7c476e6 100755 --- a/bsnes/gb/apu/noise/noise.cpp +++ b/bsnes/gb/apu/noise/noise.cpp @@ -20,8 +20,11 @@ void APU::Noise::run() { } void APU::Noise::clock_length() { - if(counter && length) { - if(--length == 0) enable = false; +//if(counter && length) { +// if(--length == 0) enable = false; +//} + if(enable && counter) { + if(++length == 0) enable = false; } } @@ -35,7 +38,8 @@ void APU::Noise::clock_envelope() { void APU::Noise::write(unsigned r, uint8 data) { if(r == 1) { //$ff20 NR41 - length = 64 - (data & 0x3f); + //length = 64 - (data & 0x3f); + length = data & 0x3f; } if(r == 2) { //$ff21 NR42 @@ -62,7 +66,7 @@ void APU::Noise::write(unsigned r, uint8 data) { lfsr = ~0U; envelope_period = envelope_frequency; volume = envelope_volume; - if(length == 0) length = 64; + //if(length == 0) length = 64; } } } diff --git a/bsnes/gb/apu/noise/noise.hpp b/bsnes/gb/apu/noise/noise.hpp index f92efbf3..0f512610 100755 --- a/bsnes/gb/apu/noise/noise.hpp +++ b/bsnes/gb/apu/noise/noise.hpp @@ -10,7 +10,7 @@ struct Noise { bool counter; int16 output; - unsigned length; + uint6 length; uint3 envelope_period; uint4 volume; unsigned period; diff --git a/bsnes/gb/apu/square1/square1.cpp b/bsnes/gb/apu/square1/square1.cpp index 0a9e0132..ae309b8e 100755 --- a/bsnes/gb/apu/square1/square1.cpp +++ b/bsnes/gb/apu/square1/square1.cpp @@ -39,8 +39,12 @@ void APU::Square1::sweep(bool update) { } void APU::Square1::clock_length() { - if(counter && length) { - if(--length == 0) enable = false; +//if(counter && length) { +// if(--length == 0) enable = false; +//} + + if(counter && enable) { + if(++length == 0) enable = false; } } @@ -70,7 +74,8 @@ void APU::Square1::write(unsigned r, uint8 data) { if(r == 1) { //$ff11 NR11 duty = data >> 6; - length = 64 - (data & 0x3f); + //length = 64 - (data & 0x3f); + length = data & 0x3f; } if(r == 2) { //$ff12 NR12 @@ -98,7 +103,7 @@ void APU::Square1::write(unsigned r, uint8 data) { sweep_enable = sweep_period || sweep_shift; sweep_negate = false; if(sweep_shift) sweep(0); - if(length == 0) length = 64; + //if(length == 0) length = 64; } } diff --git a/bsnes/gb/apu/square1/square1.hpp b/bsnes/gb/apu/square1/square1.hpp index 2b45676f..50007f2c 100755 --- a/bsnes/gb/apu/square1/square1.hpp +++ b/bsnes/gb/apu/square1/square1.hpp @@ -6,7 +6,7 @@ struct Square1 { uint3 sweep_shift; bool sweep_negate; uint2 duty; - unsigned length; + uint6 length; uint4 envelope_volume; bool envelope_direction; uint3 envelope_frequency; diff --git a/bsnes/gb/apu/square2/square2.cpp b/bsnes/gb/apu/square2/square2.cpp index 09e50678..3636e9ed 100755 --- a/bsnes/gb/apu/square2/square2.cpp +++ b/bsnes/gb/apu/square2/square2.cpp @@ -23,8 +23,12 @@ void APU::Square2::run() { } void APU::Square2::clock_length() { - if(counter && length) { - if(--length == 0) enable = false; +//if(counter && length) { +// if(--length == 0) enable = false; +//} + + if(counter && enable) { + if(++length == 0) enable = false; } } @@ -39,7 +43,8 @@ void APU::Square2::clock_envelope() { void APU::Square2::write(unsigned r, uint8 data) { if(r == 1) { //$ff16 NR21 duty = data >> 6; - length = 64 - (data & 0x3f); + //length = 64 - (data & 0x3f); + length = (data & 0x3f); } if(r == 2) { //$ff17 NR22 @@ -62,7 +67,7 @@ void APU::Square2::write(unsigned r, uint8 data) { enable = dac_enable(); envelope_period = envelope_frequency; volume = envelope_volume; - if(length == 0) length = 64; + //if(length == 0) length = 64; } } diff --git a/bsnes/gb/apu/square2/square2.hpp b/bsnes/gb/apu/square2/square2.hpp index 0a473348..c8923ebd 100755 --- a/bsnes/gb/apu/square2/square2.hpp +++ b/bsnes/gb/apu/square2/square2.hpp @@ -2,7 +2,7 @@ struct Square2 { bool enable; uint2 duty; - unsigned length; + uint6 length; uint4 envelope_volume; bool envelope_direction; uint3 envelope_frequency; diff --git a/bsnes/gb/apu/wave/wave.cpp b/bsnes/gb/apu/wave/wave.cpp index 498cd8f6..4b17142d 100755 --- a/bsnes/gb/apu/wave/wave.cpp +++ b/bsnes/gb/apu/wave/wave.cpp @@ -13,8 +13,11 @@ void APU::Wave::run() { } void APU::Wave::clock_length() { - if(counter && length) { - if(--length == 0) enable = false; +//if(counter && length) { +// if(--length == 0) enable = false; +//} + if(enable && counter) { + if(++length == 0) enable = false; } } @@ -25,7 +28,8 @@ void APU::Wave::write(unsigned r, uint8 data) { } if(r == 1) { //$ff1b NR31 - length = 256 - data; + //length = 256 - data; + length = data; } if(r == 2) { //$ff1c NR32 @@ -49,7 +53,7 @@ void APU::Wave::write(unsigned r, uint8 data) { if(initialize) { enable = dac_enable; pattern_offset = 0; - if(length == 0) length = 256; + //if(length == 0) length = 256; } } diff --git a/bsnes/gb/apu/wave/wave.hpp b/bsnes/gb/apu/wave/wave.hpp index 7f8d65d3..edeaa278 100755 --- a/bsnes/gb/apu/wave/wave.hpp +++ b/bsnes/gb/apu/wave/wave.hpp @@ -8,7 +8,7 @@ struct Wave { uint8 pattern[32]; int16 output; - unsigned length; + uint8 length; unsigned period; uint5 pattern_offset; uint4 pattern_sample; diff --git a/bsnes/gba/apu/apu.cpp b/bsnes/gba/apu/apu.cpp index 32d68767..614a9441 100755 --- a/bsnes/gba/apu/apu.cpp +++ b/bsnes/gba/apu/apu.cpp @@ -4,14 +4,21 @@ namespace GBA { #include "registers.cpp" #include "mmio.cpp" +#include "square.cpp" +#include "square1.cpp" +#include "square2.cpp" +#include "wave.cpp" +#include "noise.cpp" +#include "sequencer.cpp" APU apu; void APU::Enter() { apu.enter(); } void APU::enter() { while(true) { - interface->audioSample(0, 0); - step(512); + runsequencer(); + interface->audioSample(sequencer.lsample, sequencer.rsample); + step(4); } } @@ -23,6 +30,12 @@ void APU::step(unsigned clocks) { void APU::power() { create(APU::Enter, 16777216); + square1.power(); + square2.power(); + wave.power(); + noise.power(); + sequencer.power(); + regs.bias = 0x0200; for(unsigned n = 0x060; n <= 0x0a7; n++) bus.mmio[n] = this; diff --git a/bsnes/gba/apu/apu.hpp b/bsnes/gba/apu/apu.hpp index 47afb9b3..daff89a0 100755 --- a/bsnes/gba/apu/apu.hpp +++ b/bsnes/gba/apu/apu.hpp @@ -8,6 +8,8 @@ struct APU : Thread, MMIO { uint8 read(uint32 addr); void write(uint32 addr, uint8 byte); void power(); + + void runsequencer(); }; extern APU apu; diff --git a/bsnes/gba/apu/mmio.cpp b/bsnes/gba/apu/mmio.cpp index 8088004a..901e6957 100755 --- a/bsnes/gba/apu/mmio.cpp +++ b/bsnes/gba/apu/mmio.cpp @@ -1,10 +1,90 @@ uint8 APU::read(uint32 addr) { switch(addr) { + //NR10 + case 0x04000060: return square1.read(0); + case 0x04000061: return 0u; + + //NR11 + NR12 + case 0x04000062: return square1.read(1); + case 0x04000063: return square1.read(2); + + //NR13 + NR14 + case 0x04000064: return square1.read(3); + case 0x04000065: return square1.read(4); + + //NR21 + NR22 + case 0x04000068: return square2.read(1); + case 0x04000069: return square2.read(2); + + //NR23 + NR24 + case 0x0400006c: return square2.read(3); + case 0x0400006d: return square2.read(4); + + //NR30 + case 0x04000070: return wave.read(0); + case 0x04000071: return 0u; + + //NR31 + NR32 + case 0x04000072: return wave.read(1); + case 0x04000073: return wave.read(2); + + //NR33 + NR34 + case 0x04000074: return wave.read(3); + case 0x04000075: return wave.read(4); + + //NR41 + NR42 + case 0x04000078: return noise.read(1); + case 0x04000079: return noise.read(2); + + //NR43 + NR44 + case 0x0400007c: return noise.read(3); + case 0x0400007d: return noise.read(4); + + //NR50 + NR51 + case 0x04000080: return sequencer.read(0); + case 0x04000081: return sequencer.read(1); + + //NR52 + case 0x04000084: return sequencer.read(2); + case 0x04000085: return 0u; + //SOUNDBIAS case 0x04000088: return regs.bias >> 0; case 0x04000089: return regs.bias >> 8; + //WAVE_RAM0_L + case 0x04000090: return wave.readram( 0); + case 0x04000091: return wave.readram( 1); + + //WAVE_RAM0_H + case 0x04000092: return wave.readram( 2); + case 0x04000093: return wave.readram( 3); + + //WAVE_RAM1_L + case 0x04000094: return wave.readram( 4); + case 0x04000095: return wave.readram( 5); + + //WAVE_RAM1_H + case 0x04000096: return wave.readram( 6); + case 0x04000097: return wave.readram( 7); + + //WAVE_RAM2_L + case 0x04000098: return wave.readram( 8); + case 0x04000099: return wave.readram( 9); + + //WAVE_RAM2_H + case 0x0400009a: return wave.readram(10); + case 0x0400009b: return wave.readram(11); + + //WAVE_RAM3_L + case 0x0400009c: return wave.readram(12); + case 0x0400009d: return wave.readram(13); + + //WAVE_RAM3_H + case 0x0400009e: return wave.readram(14); + case 0x0400009f: return wave.readram(15); + } return 0u; @@ -13,6 +93,90 @@ uint8 APU::read(uint32 addr) { void APU::write(uint32 addr, uint8 byte) { switch(addr) { + //NR10 + case 0x04000060: return square1.write(0, byte); + case 0x04000061: return; + + //NR11 + NR12 + case 0x04000062: return square1.write(1, byte); + case 0x04000063: return square1.write(2, byte); + + //NR13 + NR14 + case 0x04000064: return square1.write(3, byte); + case 0x04000065: return square1.write(4, byte); + + //NR21 + NR22 + case 0x04000068: return square2.write(1, byte); + case 0x04000069: return square2.write(2, byte); + + //NR23 + NR24 + case 0x0400006c: return square2.write(3, byte); + case 0x0400006d: return square2.write(4, byte); + + //NR30 + case 0x04000070: return wave.write(0, byte); + case 0x04000071: return; + + //NR31 + NR32 + case 0x04000072: return wave.write(1, byte); + case 0x04000073: return wave.write(2, byte); + + //NR33 + NR34 + case 0x04000074: return wave.write(3, byte); + case 0x04000075: return wave.write(4, byte); + + //NR41 + NR42 + case 0x04000078: return noise.write(1, byte); + case 0x04000079: return noise.write(2, byte); + + //NR43 + NR44 + case 0x0400007c: return noise.write(3, byte); + case 0x0400007d: return noise.write(4, byte); + + //NR50 + NR51 + case 0x04000080: return sequencer.write(0, byte); + case 0x04000081: return sequencer.write(1, byte); + + //SOUND_CNT_H + case 0x04000082: return; + case 0x04000083: return; + + //NR52 + case 0x04000084: return sequencer.write(2, byte); + case 0x04000085: return; + + //WAVE_RAM0_L + case 0x04000090: return wave.writeram( 0, byte); + case 0x04000091: return wave.writeram( 1, byte); + + //WAVE_RAM0_H + case 0x04000092: return wave.writeram( 2, byte); + case 0x04000093: return wave.writeram( 3, byte); + + //WAVE_RAM1_L + case 0x04000094: return wave.writeram( 4, byte); + case 0x04000095: return wave.writeram( 5, byte); + + //WAVE_RAM1_H + case 0x04000096: return wave.writeram( 6, byte); + case 0x04000097: return wave.writeram( 7, byte); + + //WAVE_RAM2_L + case 0x04000098: return wave.writeram( 8, byte); + case 0x04000099: return wave.writeram( 9, byte); + + //WAVE_RAM2_H + case 0x0400009a: return wave.writeram(10, byte); + case 0x0400009b: return wave.writeram(11, byte); + + //WAVE_RAM3_L + case 0x0400009c: return wave.writeram(12, byte); + case 0x0400009d: return wave.writeram(13, byte); + + //WAVE_RAM3_H + case 0x0400009e: return wave.writeram(14, byte); + case 0x0400009f: return wave.writeram(15, byte); + //SOUNDBIAS case 0x04000088: regs.bias = (regs.bias & 0xff00) | (byte << 0); return; case 0x04000089: regs.bias = (regs.bias & 0x00ff) | (byte << 8); return; diff --git a/bsnes/gba/apu/noise.cpp b/bsnes/gba/apu/noise.cpp new file mode 100755 index 00000000..428542c1 --- /dev/null +++ b/bsnes/gba/apu/noise.cpp @@ -0,0 +1,93 @@ +unsigned APU::Noise::divider() const { + if(divisor == 0) return 8; + return divisor * 16; +} + +void APU::Noise::run() { + if(period && --period == 0) { + period = divider() << frequency; + if(frequency < 14) { + bool bit = (lfsr ^ (lfsr >> 1)) & 1; + lfsr = (lfsr >> 1) ^ (bit << (narrowlfsr ? 6 : 14)); + } + } + + output = volume; + if(enable == false || (lfsr & 1)) output = 0; +} + +void APU::Noise::clocklength() { + if(enable && counter) { + if(++length == 0) enable = false; + } +} + +void APU::Noise::clockenvelope() { + if(enable && envelope.frequency && --envelope.period == 0) { + envelope.period = envelope.frequency; + if(envelope.direction == 0 && volume > 0) volume--; + if(envelope.direction == 1 && volume < 15) volume++; + } +} + +uint8 APU::Noise::read(unsigned addr) const { + switch(addr) { + case 1: return (length << 0); + case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4); + case 3: return (divisor << 0) | (narrowlfsr << 3) | (frequency << 4); + case 4: return (counter << 6) | (initialize << 7); + } +} + +void APU::Noise::write(unsigned addr, uint8 byte) { + switch(addr) { + case 1: //NR41 + length = byte >> 0; + break; + + case 2: //NR42 + envelope.frequency = byte >> 0; + envelope.direction = byte >> 3; + envelope.volume = byte >> 4; + if(envelope.dacenable() == false) enable = false; + break; + + case 3: //NR43 + divisor = byte >> 0; + narrowlfsr = byte >> 3; + frequency = byte >> 4; + period = divider() << frequency; + break; + + case 4: //NR44 + counter = byte >> 6; + initialize = byte >> 7; + + if(initialize) { + enable = envelope.dacenable(); + lfsr = ~0u; + envelope.period = envelope.frequency; + volume = envelope.volume; + } + + break; + } +} + +void APU::Noise::power() { + envelope.frequency = 0; + envelope.direction = 0; + envelope.volume = 0; + envelope.period = 0; + length = 0; + divisor = 0; + narrowlfsr = 0; + frequency = 0; + counter = 0; + initialize = 0; + enable = 0; + lfsr = 0; + output = 0; + period = 0; + volume = 0; +} diff --git a/bsnes/gba/apu/registers.hpp b/bsnes/gba/apu/registers.hpp index ddb6d6ff..5b7fe807 100755 --- a/bsnes/gba/apu/registers.hpp +++ b/bsnes/gba/apu/registers.hpp @@ -8,3 +8,130 @@ struct Registers { SoundBias& operator=(const SoundBias&) = delete; } bias; } regs; + +struct Sweep { + uint3 shift; + uint1 direction; + uint3 frequency; + + uint1 enable; + uint1 negate; + uint3 period; +}; + +struct Envelope { + uint3 frequency; + uint1 direction; + uint4 volume; + + uint3 period; + + inline bool dacenable() const { return volume || direction; } +}; + +struct Square { + Envelope envelope; + uint1 enable; + uint6 length; + uint2 duty; + uint11 frequency; + uint1 counter; + uint1 initialize; + + signed shadowfrequency; + uint1 signal; + uint4 output; + unsigned period; + uint3 phase; + uint4 volume; + + void run(); + void clocklength(); + void clockenvelope(); +}; + +struct Square1 : Square { + Sweep sweep; + + void runsweep(bool update); + void clocksweep(); + uint8 read(unsigned addr) const; + void write(unsigned addr, uint8 byte); + void power(); +} square1; + +struct Square2 : Square { + uint8 read(unsigned addr) const; + void write(unsigned addr, uint8 byte); + void power(); +} square2; + +struct Wave { + uint1 mode; + uint1 bank; + uint1 dacenable; + uint8 length; + uint3 volume; + uint11 frequency; + uint1 counter; + uint1 initialize; + uint4 pattern[32]; + + uint1 enable; + uint4 output; + uint4 patternaddr; + uint1 patternbank; + uint4 patternsample; + unsigned period; + + void run(); + void clocklength(); + uint8 read(unsigned addr) const; + void write(unsigned addr, uint8 byte); + uint8 readram(unsigned addr) const; + void writeram(unsigned addr, uint8 byte); + void power(); +} wave; + +struct Noise { + Envelope envelope; + uint6 length; + uint3 divisor; + uint1 narrowlfsr; + uint4 frequency; + uint1 counter; + uint1 initialize; + + uint1 enable; + uint15 lfsr; + uint4 output; + unsigned period; + uint4 volume; + + unsigned divider() const; + void run(); + void clocklength(); + void clockenvelope(); + uint8 read(unsigned addr) const; + void write(unsigned addr, uint8 byte); + void power(); +} noise; + +struct Sequencer { + uint3 lvolume; + uint3 rvolume; + uint1 lenable[4]; + uint1 renable[4]; + uint1 enable[4]; + uint1 masterenable; + + uint13 base; + uint3 step; + int16 lsample; + int16 rsample; + + signed volumeadjust(signed sample, uint3 volume); + uint8 read(unsigned addr) const; + void write(unsigned addr, uint8 byte); + void power(); +} sequencer; diff --git a/bsnes/gba/apu/sequencer.cpp b/bsnes/gba/apu/sequencer.cpp new file mode 100755 index 00000000..7f9722d7 --- /dev/null +++ b/bsnes/gba/apu/sequencer.cpp @@ -0,0 +1,127 @@ +void APU::runsequencer() { + auto &r = sequencer; + + if(r.base == 0) { //512hz + if(r.step == 0 || r.step == 2 || r.step == 4 || r.step == 6) { //256hz + square1.clocklength(); + square2.clocklength(); + wave.clocklength(); + noise.clocklength(); + } + if(r.step == 2 || r.step == 6) { //128hz + square1.clocksweep(); + } + if(r.step == 7) { //64hz + square1.clockenvelope(); + square2.clockenvelope(); + noise.clockenvelope(); + } + r.step++; + } + r.base++; + + if(r.enable[0]) square1.run(); + if(r.enable[1]) square2.run(); + if(r.enable[2]) wave.run(); + if(r.enable[3]) noise.run(); + + signed lsample = 0; + if(r.lenable[0]) lsample += square1.output; + if(r.lenable[1]) lsample += square2.output; + if(r.lenable[2]) lsample += wave.output; + if(r.lenable[3]) lsample += noise.output; + lsample = (lsample * 512) - 15360; + lsample = r.volumeadjust(lsample, r.lvolume); + r.lsample = lsample; + + signed rsample = 0; + if(r.renable[0]) rsample += square1.output; + if(r.renable[1]) rsample += square2.output; + if(r.renable[2]) rsample += wave.output; + if(r.renable[3]) rsample += noise.output; + rsample = (rsample * 512) - 15360; + rsample = r.volumeadjust(rsample, r.rvolume); + r.rsample = rsample; + + if(r.masterenable == false) { + r.lsample = 0; + r.rsample = 0; + } +} + +signed APU::Sequencer::volumeadjust(signed sample, uint3 volume) { + switch(volume) { + case 0: return (sample >> 3); // 12.5% + case 1: return (sample >> 2); // 25.0% + case 2: return (sample >> 2) + (sample >> 3); // 37.5% + case 3: return (sample >> 1); // 50.0% + case 4: return (sample >> 1) + (sample >> 3); // 62.5% + case 5: return (sample >> 0) - (sample >> 2); // 75.0% + case 6: return (sample >> 0) - (sample >> 3); // 87.5% + case 7: return (sample >> 0); //100.0% + } +} + +uint8 APU::Sequencer::read(unsigned addr) const { + switch(addr) { + case 0: return (rvolume << 0) | (lvolume << 4); + case 1: return ( + (renable[0] << 0) + | (renable[1] << 1) + | (renable[2] << 2) + | (renable[3] << 3) + | (lenable[0] << 4) + | (lenable[1] << 5) + | (lenable[2] << 6) + | (lenable[3] << 7) + ); + case 2: return ( + (enable[0] << 0) + | (enable[1] << 1) + | (enable[2] << 2) + | (enable[3] << 3) + | (masterenable << 7) + ); + } +} + +void APU::Sequencer::write(unsigned addr, uint8 byte) { + switch(addr) { + case 0: //NR50 + rvolume = byte >> 0; + lvolume = byte >> 4; + break; + + case 1: //NR51 + renable[0] = byte >> 0; + renable[1] = byte >> 1; + renable[2] = byte >> 2; + renable[3] = byte >> 3; + lenable[0] = byte >> 4; + lenable[1] = byte >> 5; + lenable[2] = byte >> 6; + lenable[3] = byte >> 7; + break; + + case 2: //NR52 + enable[0] = byte >> 0; + enable[1] = byte >> 1; + enable[2] = byte >> 2; + enable[3] = byte >> 3; + masterenable = byte >> 7; + break; + } +} + +void APU::Sequencer::power() { + lvolume = 0; + rvolume = 0; + for(auto &n : lenable) n = 0; + for(auto &n : renable) n = 0; + for(auto &n : enable) n = 0; + masterenable = 0; + base = 0; + step = 0; + lsample = 0; + rsample = 0; +} diff --git a/bsnes/gba/apu/square.cpp b/bsnes/gba/apu/square.cpp new file mode 100755 index 00000000..6152d7d3 --- /dev/null +++ b/bsnes/gba/apu/square.cpp @@ -0,0 +1,30 @@ +void APU::Square::run() { + if(period && --period == 0) { + period = 4 * (2048 - frequency); + phase++; + switch(duty) { + case 0: signal = (phase == 6); break; //_____-_ + case 1: signal = (phase >= 6); break; //______-- + case 2: signal = (phase >= 4); break; //____---- + case 3: signal = (phase <= 5); break; //------__ + } + } + + uint4 sample = volume; + if(enable == false || signal == false) sample = 0; + output = sample; +} + +void APU::Square::clocklength() { + if(enable && counter) { + if(++length == 0) enable = false; + } +} + +void APU::Square::clockenvelope() { + if(enable && envelope.frequency && --envelope.period == 0) { + envelope.period = envelope.frequency; + if(envelope.direction == 0 && volume > 0) volume--; + if(envelope.direction == 1 && volume < 15) volume++; + } +} diff --git a/bsnes/gba/apu/square1.cpp b/bsnes/gba/apu/square1.cpp new file mode 100755 index 00000000..606b77c6 --- /dev/null +++ b/bsnes/gba/apu/square1.cpp @@ -0,0 +1,105 @@ +void APU::Square1::runsweep(bool update) { + if(sweep.enable == false) return; + + sweep.negate = sweep.direction; + unsigned delta = shadowfrequency >> sweep.shift; + signed updatefrequency = shadowfrequency + (sweep.negate ? -delta : delta); + + if(updatefrequency > 2047) { + enable = false; + } else if(sweep.shift && update) { + shadowfrequency = updatefrequency; + frequency = updatefrequency; + period = 4 * (2048 - frequency); + } +} + +void APU::Square1::clocksweep() { + if(enable && sweep.frequency && --sweep.period == 0) { + sweep.period = sweep.frequency; + runsweep(1); + runsweep(0); + } +} + +uint8 APU::Square1::read(unsigned addr) const { + switch(addr) { + case 0: return (sweep.shift << 0) | (sweep.direction << 3) | (sweep.frequency << 4); + case 1: return (length << 0) | (duty << 6); + case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4); + case 3: return (frequency << 0); + case 4: return (frequency >> 8) | (counter << 6) | (initialize << 7); + } +} + +void APU::Square1::write(unsigned addr, uint8 byte) { + switch(addr) { + case 0: //NR10 + if(sweep.negate && sweep.direction && !(byte & 0x08)) enable = false; + sweep.shift = byte >> 0; + sweep.direction = byte >> 3; + sweep.frequency = byte >> 4; + break; + + case 1: //NR11 + length = byte >> 0; + duty = byte >> 6; + break; + + case 2: //NR12 + envelope.frequency = byte >> 0; + envelope.direction = byte >> 3; + envelope.volume = byte >> 4; + if(envelope.dacenable() == false) enable = false; + break; + + case 3: //NR13 + frequency = (frequency & 0xff00) | (byte << 0); + break; + + case 4: //NR14 + frequency = (frequency & 0x00ff) | (byte << 8); + counter = byte >> 6; + initialize = byte >> 7; + + if(initialize) { + enable = envelope.dacenable(); + envelope.period = envelope.frequency; + volume = envelope.volume; + shadowfrequency = frequency; + sweep.period = sweep.frequency; + sweep.enable = sweep.period || sweep.shift; + sweep.negate = false; + if(sweep.shift) runsweep(0); + } + + break; + } + + period = 4 * (2048 - frequency); +} + +void APU::Square1::power() { + envelope.frequency = 0; + envelope.direction = 0; + envelope.direction = 0; + envelope.period = 0; + sweep.shift = 0; + sweep.direction = 0; + sweep.frequency = 0; + sweep.enable = 0; + sweep.negate = 0; + sweep.period = 0; + enable = 0; + length = 0; + duty = 0; + frequency = 0; + counter = 0; + initialize = 0; + shadowfrequency = 0; + signal = 0; + output = 0; + period = 0; + phase = 0; + volume = 0; +} diff --git a/bsnes/gba/apu/square2.cpp b/bsnes/gba/apu/square2.cpp new file mode 100755 index 00000000..5ece02f3 --- /dev/null +++ b/bsnes/gba/apu/square2.cpp @@ -0,0 +1,62 @@ +uint8 APU::Square2::read(unsigned addr) const { + switch(addr) { + case 1: return (length << 0) | (duty << 6); + case 2: return (envelope.frequency << 0) | (envelope.direction << 3) | (envelope.volume << 4); + case 3: return (frequency >> 0); + case 4: return (frequency >> 8) | (counter << 6) | (initialize << 7); + } +} + +void APU::Square2::write(unsigned addr, uint8 byte) { + switch(addr) { + case 1: //NR21 + length = byte >> 0; + duty = byte >> 6; + break; + + case 2: //NR22 + envelope.frequency = byte >> 0; + envelope.direction = byte >> 3; + envelope.volume = byte >> 4; + if(envelope.dacenable() == false) enable = false; + break; + + case 3: //NR23 + frequency = (frequency & 0xff00) | (byte << 0); + break; + + case 4: //NR24 + frequency = (frequency & 0x00ff) | (byte << 8); + counter = byte >> 6; + initialize = byte >> 7; + + if(initialize) { + enable = envelope.dacenable(); + envelope.period = envelope.frequency; + volume = envelope.volume; + } + + break; + } + + period = 4 * (2048 - frequency); +} + +void APU::Square2::power() { + envelope.frequency = 0; + envelope.direction = 0; + envelope.direction = 0; + envelope.period = 0; + enable = 0; + length = 0; + duty = 0; + frequency = 0; + counter = 0; + initialize = 0; + shadowfrequency = 0; + signal = 0; + output = 0; + period = 0; + phase = 0; + volume = 0; +} diff --git a/bsnes/gba/apu/wave.cpp b/bsnes/gba/apu/wave.cpp new file mode 100755 index 00000000..8f249c0e --- /dev/null +++ b/bsnes/gba/apu/wave.cpp @@ -0,0 +1,104 @@ +void APU::Wave::run() { + if(period && --period == 0) { + period = 2 * (2048 - frequency); + patternsample = pattern[patternbank * 16 + patternaddr++]; + if(patternaddr == 0) patternbank ^= mode; + } + + output = patternsample; + switch(volume) { + case 0: output = 0; // 0% + case 1: break; //100% + case 2: output >>= 1; // 50% + case 3: output >>= 2; // 25% + case 4: output -= output >> 2; // 75% + case 5: output -= output >> 2; // 75% + case 6: output -= output >> 2; // 75% + case 7: output -= output >> 2; // 75% + } + if(enable == false) output = 0; +} + +void APU::Wave::clocklength() { + if(enable && counter) { + if(++length == 0) enable = false; + } +} + +uint8 APU::Wave::read(unsigned addr) const { + switch(addr) { + case 0: return (mode << 5) | (bank << 6) | (dacenable << 7); + case 1: return (length << 0); + case 2: return (volume << 5); + case 3: return (frequency >> 0); + case 4: return (frequency >> 8) | (counter << 6) | (initialize << 7); + } +} + +void APU::Wave::write(unsigned addr, uint8 byte) { + switch(addr) { + case 0: //NR30 + mode = byte >> 5; + bank = byte >> 6; + dacenable = byte >> 7; + if(dacenable == false) enable = false; + break; + + case 1: //NR31 + length = byte >> 0; + break; + + case 2: //NR32 + volume = byte >> 5; + break; + + case 3: //NR33 + frequency = (frequency & 0xff00) | (byte << 0); + break; + + case 4: //NR34 + frequency = (frequency & 0x00ff) | (byte << 8); + counter = byte >> 6; + initialize = byte >> 7; + + if(initialize) { + enable = dacenable; + patternaddr = 0; + patternbank = mode ? (uint1)0 : bank; + } + + break; + } + + period = 2 * (2048 - frequency); +} + +uint8 APU::Wave::readram(unsigned addr) const { + uint8 byte = 0; + byte |= pattern[addr * 2 + 0] << 0; + byte |= pattern[addr * 2 + 1] << 4; + return byte; +} + +void APU::Wave::writeram(unsigned addr, uint8 byte) { + pattern[addr * 2 + 0] = byte >> 0; + pattern[addr * 2 + 1] = byte >> 4; +} + +void APU::Wave::power() { + mode = 0; + bank = 0; + dacenable = 0; + length = 0; + volume = 0; + frequency = 0; + counter = 0; + initialize = 0; + for(auto &sample : pattern) sample = 0; + enable = 0; + output = 0; + patternaddr = 0; + patternbank = 0; + patternsample = 0; + period = 0; +} diff --git a/bsnes/target-ui/config/config.cpp b/bsnes/target-ui/config/config.cpp index 7f16707d..c84a1128 100755 --- a/bsnes/target-ui/config/config.cpp +++ b/bsnes/target-ui/config/config.cpp @@ -32,7 +32,7 @@ Config::Config() { append(audio.frequencyNES = 1789772, "Audio::Frequency::NES"); append(audio.frequencySNES = 32000, "Audio::Frequency::SNES"); append(audio.frequencyGB = 4194304, "Audio::Frequency::GB"); - append(audio.frequencyGBA = 32768, "Audio::Frequency::GBA"); + append(audio.frequencyGBA = 4194304, "Audio::Frequency::GBA"); append(input.driver = "", "Input::Driver"); append(input.focusPolicy = 1, "Input::FocusPolicy"); diff --git a/bsnes/target-ui/settings/audio.cpp b/bsnes/target-ui/settings/audio.cpp index 62938c05..15676d71 100755 --- a/bsnes/target-ui/settings/audio.cpp +++ b/bsnes/target-ui/settings/audio.cpp @@ -70,8 +70,8 @@ AudioSettings::AudioSettings() { gba.name.setText("GBA:"); gba.slider.setLength(2001); - gba.base = 32768; - gba.step = 1; + gba.base = 4194304; + gba.step = 131; append(title, { ~0, 0 }, 5); append(outputLabel, { ~0, 0 }, 0);