Update to v087r17 release.

byuu says:

Emulated GBC sound plus the new extensions to it.
I am kind of surprised by how little developers utilized the GBC audio
portion.
Mr. Driller now has sound effects, and Pinobee no Daibouken has BGM.
I still have yet to emulate the GBA extra sound channels and PWM. Need
to emulate timers and DMA 2 refresh mode before I can do that.

Also, I moved both GBC and GBA audio to use length = data; if(++length
== 0); rather than length = 64 - data; if(--length == 0); so that
I could return literal values for register reads.
I thought there was a good reason we used the latter version, but
I can't hear any audible difference even in GBC games, so oh well.
Lastly, I think the pattern[++offset] in the wave channel was a bug in
the DMG/GBC only. I really, really hope it doesn't apply to the GBA,
because that will make bank selection a serious pain in the ass.
This commit is contained in:
Tim Allen 2012-04-06 14:29:50 +10:00
parent b8d0ec29b2
commit 1de484262c
21 changed files with 871 additions and 26 deletions

View File

@ -1,7 +1,7 @@
#ifndef BASE_HPP
#define BASE_HPP
static const char Version[] = "087.16";
static const char Version[] = "087.17";
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>

View File

@ -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;
}
}
}

View File

@ -10,7 +10,7 @@ struct Noise {
bool counter;
int16 output;
unsigned length;
uint6 length;
uint3 envelope_period;
uint4 volume;
unsigned period;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -2,7 +2,7 @@ struct Square2 {
bool enable;
uint2 duty;
unsigned length;
uint6 length;
uint4 envelope_volume;
bool envelope_direction;
uint3 envelope_frequency;

View File

@ -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;
}
}

View File

@ -8,7 +8,7 @@ struct Wave {
uint8 pattern[32];
int16 output;
unsigned length;
uint8 length;
unsigned period;
uint5 pattern_offset;
uint4 pattern_sample;

View File

@ -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;

View File

@ -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;

View File

@ -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;

93
bsnes/gba/apu/noise.cpp Executable file
View File

@ -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;
}

View File

@ -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;

127
bsnes/gba/apu/sequencer.cpp Executable file
View File

@ -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;
}

30
bsnes/gba/apu/square.cpp Executable file
View File

@ -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++;
}
}

105
bsnes/gba/apu/square1.cpp Executable file
View File

@ -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;
}

62
bsnes/gba/apu/square2.cpp Executable file
View File

@ -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;
}

104
bsnes/gba/apu/wave.cpp Executable file
View File

@ -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;
}

View File

@ -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");

View File

@ -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);