mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
b8d0ec29b2
commit
1de484262c
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ struct Noise {
|
|||
bool counter;
|
||||
|
||||
int16 output;
|
||||
unsigned length;
|
||||
uint6 length;
|
||||
uint3 envelope_period;
|
||||
uint4 volume;
|
||||
unsigned period;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ struct Square2 {
|
|||
bool enable;
|
||||
|
||||
uint2 duty;
|
||||
unsigned length;
|
||||
uint6 length;
|
||||
uint4 envelope_volume;
|
||||
bool envelope_direction;
|
||||
uint3 envelope_frequency;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ struct Wave {
|
|||
uint8 pattern[32];
|
||||
|
||||
int16 output;
|
||||
unsigned length;
|
||||
uint8 length;
|
||||
unsigned period;
|
||||
uint5 pattern_offset;
|
||||
uint4 pattern_sample;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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++;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue