Update to v075r05 release.

byuu says:

Added Game Boy sound emulation, all four channels.
It's really, really, really bad. Plenty of bugs, I don't even know what
the fuck a high-pass filter is so that isn't there. Hermite resampling
from 4MHz down to 44KHz. But it's tolerable.
I don't understand what sweep is for at all, and I'm sure I have that
insane recursive reload behavior wrong.

This is pretty much my own design. I referenced blargg's gb snd emu,
blargg's older gb apu ref, Cydrak's APU core, that lousy gbdev wiki
article, the completely and utterly worthless pandocs, and received
nothing but bad and wrong information that just wasted my time from

But I managed to pull it off. It's also painfully slow, like 250fps on
my machine slow. Countless optimizations are possible.
This commit is contained in:
Tim Allen 2011-02-02 21:38:28 +11:00
parent f88ef9e9a2
commit a3abe8ebaa
30 changed files with 657 additions and 335 deletions

View File

@ -2,7 +2,7 @@ include nall/Makefile
snes := snes snes := snes
gameboy := gameboy gameboy := gameboy
profile := compatibility profile := compatibility
ui := ui ui := ui-gameboy
# compiler # compiler
c := $(compiler) -std=gnu99 c := $(compiler) -std=gnu99

View File

@ -1,12 +1,10 @@
#include <gameboy/gameboy.hpp> #include <gameboy/gameboy.hpp>
#include <nall/random.hpp>
#define APU_CPP #define APU_CPP
namespace GameBoy { namespace GameBoy {
#include "mmio/mmio.cpp" #include "square1/square1.cpp"
#include "square/square.cpp" #include "square2/square2.cpp"
#include "wave/wave.cpp" #include "wave/wave.cpp"
#include "noise/noise.cpp" #include "noise/noise.cpp"
#include "master/master.cpp" #include "master/master.cpp"
@ -23,25 +21,45 @@ void APU::main() {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); 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(); square1.run();
square2.run(); square2.run();
wave.run(); wave.run();
noise.run(); noise.run();
master.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); if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
} }
} }
void APU::power() { void APU::power() {
create(Main, 4 * 1024 * 1024); create(Main, 4194304);
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this; for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
square1.has_sweep = true; foreach(n, mmio_data) n = 0x00;
square2.has_sweep = false; counter = 0;
sequencer = 0;
square1.power(); square1.power();
square2.power(); square2.power();
@ -50,4 +68,40 @@ void APU::power() {
master.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);
}
} }

View File

@ -1,12 +1,16 @@
struct APU : Processor, MMIO { struct APU : Processor, MMIO {
#include "mmio/mmio.hpp" #include "square1/square1.hpp"
#include "square/square.hpp" #include "square2/square2.hpp"
#include "wave/wave.hpp" #include "wave/wave.hpp"
#include "noise/noise.hpp" #include "noise/noise.hpp"
#include "master/master.hpp" #include "master/master.hpp"
Square square1; uint8 mmio_data[48];
Square square2; unsigned counter;
unsigned sequencer;
Square1 square1;
Square2 square2;
Wave wave; Wave wave;
Noise noise; Noise noise;
Master master; Master master;
@ -15,6 +19,9 @@ struct APU : Processor, MMIO {
void main(); void main();
void power(); void power();
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);
void serialize(serializer&); void serialize(serializer&);
}; };

View File

@ -1,99 +1,132 @@
#ifdef APU_CPP #ifdef APU_CPP
void APU::Master::run() { 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) { sample = 0;
if(r == 0) { channels = 0;
return (so2_enable << 7) | (so2_volume << 4) | (so1_enable << 3) | (so1_volume << 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) { if(enable == false) {
return (channel4_so2_enable << 7) center = 0;
| (channel3_so2_enable << 6) left = 0;
| (channel2_so2_enable << 5) right = 0;
| (channel1_so2_enable << 4)
| (channel4_so1_enable << 3)
| (channel3_so1_enable << 2)
| (channel2_so1_enable << 1)
| (channel1_so1_enable << 0);
}
if(r == 2) {
return (enable << 7)
| (channel4_enable << 3)
| (channel3_enable << 2)
| (channel2_enable << 1)
| (channel1_enable << 0);
} }
} }
void APU::Master::write(unsigned r, uint8 data) { void APU::Master::write(unsigned r, uint8 data) {
if(r == 0) { if(r == 0) {
so2_enable = data & 0x80; left_enable = data & 0x80;
so2_volume = (data >> 4) & 7; left_volume = (data >> 4) & 7;
so1_enable = data & 0x08; right_enable = data & 0x08;
so1_volume = (data >> 0) & 7; right_volume = (data >> 0) & 7;
} }
if(r == 1) { if(r == 1) {
channel4_so2_enable = data & 0x80; channel4_left_enable = data & 0x80;
channel3_so2_enable = data & 0x40; channel3_left_enable = data & 0x40;
channel2_so2_enable = data & 0x20; channel2_left_enable = data & 0x20;
channel1_so2_enable = data & 0x10; channel1_left_enable = data & 0x10;
channel4_so1_enable = data & 0x08; channel4_right_enable = data & 0x08;
channel3_so1_enable = data & 0x04; channel3_right_enable = data & 0x04;
channel2_so1_enable = data & 0x02; channel2_right_enable = data & 0x02;
channel1_so1_enable = data & 0x01; channel1_right_enable = data & 0x01;
} }
if(r == 2) { if(r == 2) {
enable = data & 0x80; enable = data & 0x80;
channel4_enable = data & 0x08;
channel3_enable = data & 0x04;
channel2_enable = data & 0x02;
channel1_enable = data & 0x01;
} }
} }
void APU::Master::power() { void APU::Master::power() {
so2_enable = 0; left_enable = 0;
so2_volume = 0; left_volume = 0;
so1_enable = 0; right_enable = 0;
so1_volume = 0; right_volume = 0;
channel4_so2_enable = 0; channel4_left_enable = 0;
channel3_so2_enable = 0; channel3_left_enable = 0;
channel2_so2_enable = 0; channel2_left_enable = 0;
channel1_so2_enable = 0; channel1_left_enable = 0;
channel4_so1_enable = 0; channel4_right_enable = 0;
channel3_so1_enable = 0; channel3_right_enable = 0;
channel2_so1_enable = 0; channel2_right_enable = 0;
channel1_so1_enable = 0; channel1_right_enable = 0;
enable = 0; enable = 0;
channel4_enable = 0;
channel3_enable = 0; center = 0;
channel2_enable = 0; left = 0;
channel1_enable = 0; right = 0;
} }
void APU::Master::serialize(serializer &s) { void APU::Master::serialize(serializer &s) {
s.integer(so2_enable); s.integer(left_enable);
s.integer(so2_volume); s.integer(left_volume);
s.integer(so1_enable); s.integer(right_enable);
s.integer(so1_volume); s.integer(right_volume);
s.integer(channel4_so2_enable); s.integer(channel4_left_enable);
s.integer(channel3_so2_enable); s.integer(channel3_left_enable);
s.integer(channel2_so2_enable); s.integer(channel2_left_enable);
s.integer(channel1_so2_enable); s.integer(channel1_left_enable);
s.integer(channel4_so1_enable); s.integer(channel4_right_enable);
s.integer(channel3_so1_enable); s.integer(channel3_right_enable);
s.integer(channel2_so1_enable); s.integer(channel2_right_enable);
s.integer(channel1_so1_enable); s.integer(channel1_right_enable);
s.integer(enable); s.integer(enable);
s.integer(channel4_enable);
s.integer(channel3_enable); s.integer(center);
s.integer(channel2_enable); s.integer(left);
s.integer(channel1_enable); s.integer(right);
} }
#endif #endif

View File

@ -1,24 +1,23 @@
struct Master { struct Master {
bool so2_enable; bool left_enable;
unsigned so2_volume; unsigned left_volume;
bool so1_enable; bool right_enable;
unsigned so1_volume; unsigned right_volume;
bool channel4_so2_enable; bool channel4_left_enable;
bool channel3_so2_enable; bool channel3_left_enable;
bool channel2_so2_enable; bool channel2_left_enable;
bool channel1_so2_enable; bool channel1_left_enable;
bool channel4_so1_enable; bool channel4_right_enable;
bool channel3_so1_enable; bool channel3_right_enable;
bool channel2_so1_enable; bool channel2_right_enable;
bool channel1_so1_enable; bool channel1_right_enable;
bool enable; bool enable;
bool channel4_enable;
bool channel3_enable; int16 center;
bool channel2_enable; int16 left;
bool channel1_enable; int16 right;
void run(); void run();
uint8 read(unsigned r);
void write(unsigned r, uint8 data); void write(unsigned r, uint8 data);
void power(); void power();
void serialize(serializer&); void serialize(serializer&);

View File

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

View File

@ -1,2 +0,0 @@
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);

View File

@ -1,66 +1,102 @@
#ifdef APU_CPP #ifdef APU_CPP
void APU::Noise::run() { 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) { void APU::Noise::clock_length() {
if(r == 2) { if(counter && length) length--;
return (envelope_volume << 4) | (envelope_direction << 3) | (envelope_period << 0); }
}
if(r == 3) { void APU::Noise::clock_envelope() {
return (period << 4) | (step << 3) | (divisor); 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) { void APU::Noise::write(unsigned r, uint8 data) {
if(r == 1) { if(r == 1) {
length = data & 0x3f; initial_length = 64 - (data & 0x3f);
length = initial_length;
} }
if(r == 2) { if(r == 2) {
envelope_volume = data >> 4; envelope_volume = data >> 4;
envelope_direction = data & 0x08; envelope_direction = data & 0x08;
envelope_period = data & 0x07; envelope_frequency = data & 0x07;
} }
if(r == 3) { if(r == 3) {
period = data >> 4; frequency = data >> 4;
step = data & 0x08; narrow_lfsr = data & 0x08;
divisor = data & 0x07; divisor = (data & 0x07) << 4;
if(divisor == 0) divisor = 8;
period = divisor << frequency;
} }
if(r == 4) { if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40; counter = data & 0x40;
if(initialize) {
lfsr = ~0U;
length = initial_length;
envelope_period = envelope_frequency;
volume = envelope_volume;
}
} }
} }
void APU::Noise::power() { void APU::Noise::power() {
length = 0; initial_length = 0;
envelope_volume = 0; envelope_volume = 0;
envelope_direction = 0; envelope_direction = 0;
envelope_period = 0; envelope_frequency = 0;
period = 0; frequency = 0;
step = 0; narrow_lfsr = 0;
divisor = 0; divisor = 0;
counter = 0; counter = 0;
output = 0;
length = 0;
envelope_period = 0;
volume = 0;
period = 0;
lfsr = 0;
} }
void APU::Noise::serialize(serializer &s) { void APU::Noise::serialize(serializer &s) {
s.integer(length); s.integer(initial_length);
s.integer(envelope_volume); s.integer(envelope_volume);
s.integer(envelope_direction); s.integer(envelope_direction);
s.integer(envelope_period); s.integer(envelope_frequency);
s.integer(period); s.integer(frequency);
s.integer(step); s.integer(narrow_lfsr);
s.integer(divisor); s.integer(divisor);
s.integer(counter); s.integer(counter);
s.integer(output);
s.integer(length);
s.integer(envelope_period);
s.integer(volume);
s.integer(period);
s.integer(lfsr);
} }
#endif #endif

View File

@ -1,15 +1,23 @@
struct Noise { struct Noise {
unsigned length; unsigned initial_length;
unsigned envelope_volume; unsigned envelope_volume;
bool envelope_direction; bool envelope_direction;
unsigned envelope_period; unsigned envelope_frequency;
unsigned period; unsigned frequency;
bool step; bool narrow_lfsr;
unsigned divisor; unsigned divisor;
bool counter; bool counter;
int16 output;
unsigned length;
unsigned envelope_period;
unsigned volume;
unsigned period;
uint15 lfsr;
void run(); void run();
uint8 read(unsigned r); void clock_length();
void clock_envelope();
void write(unsigned r, uint8 data); void write(unsigned r, uint8 data);
void power(); void power();
void serialize(serializer&); void serialize(serializer&);

View File

@ -1,6 +1,10 @@
#ifdef APU_CPP #ifdef APU_CPP
void APU::serialize(serializer &s) { void APU::serialize(serializer &s) {
s.array(mmio_data);
s.integer(counter);
s.integer(sequencer);
square1.serialize(s); square1.serialize(s);
square2.serialize(s); square2.serialize(s);
wave.serialize(s); wave.serialize(s);

View File

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

View File

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

View File

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

View File

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

View File

@ -1,19 +1,22 @@
struct Square { struct Square2 {
bool has_sweep;
unsigned sweep_period;
unsigned sweep_direction;
unsigned sweep_shift;
unsigned duty; unsigned duty;
unsigned length; unsigned length;
unsigned envelope_volume; unsigned envelope_volume;
unsigned envelope_direction; unsigned envelope_direction;
unsigned envelope_period; unsigned envelope_frequency;
unsigned frequency; unsigned frequency;
unsigned counter; unsigned counter;
int16 output;
bool duty_output;
unsigned phase;
unsigned period;
unsigned envelope_period;
unsigned volume;
void run(); void run();
uint8 read(unsigned r); void clock_length();
void clock_envelope();
void write(unsigned r, uint8 data); void write(unsigned r, uint8 data);
void power(); void power();
void serialize(serializer&); void serialize(serializer&);

View File

@ -1,31 +1,40 @@
#ifdef APU_CPP #ifdef APU_CPP
void APU::Wave::run() { 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) { void APU::Wave::clock_length() {
if(r == 0) { if(counter && length) length--;
return (off << 7);
}
if(r == 2) {
return (volume << 5);
}
if(r == 4) {
return (counter << 6);
}
return 0x00;
} }
void APU::Wave::write(unsigned r, uint8 data) { void APU::Wave::write(unsigned r, uint8 data) {
if(r == 0) { if(r == 0) {
off = data & 0x80; dac_enable = data & 0x80;
if(dac_enable == false) enable = false;
} }
if(r == 1) { if(r == 1) {
length = data; initial_length = 256 - data;
length = initial_length;
} }
if(r == 2) { if(r == 2) {
@ -40,36 +49,55 @@ void APU::Wave::write(unsigned r, uint8 data) {
bool initialize = data & 0x80; bool initialize = data & 0x80;
counter = data & 0x40; counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff); frequency = ((data & 7) << 8) | (frequency & 0x00ff);
}
}
uint8 APU::Wave::read_pattern(unsigned p) { if(initialize && dac_enable) {
p <<= 1; enable = true;
return (pattern[p + 0] << 4) | (pattern[p + 1] << 0); pattern_offset = 0;
length = initial_length;
}
}
period = 2 * (2048 - frequency);
} }
void APU::Wave::write_pattern(unsigned p, uint8 data) { void APU::Wave::write_pattern(unsigned p, uint8 data) {
p <<= 1; p <<= 1;
pattern[p + 0] = data >> 4; pattern[p + 0] = (data >> 4) & 15;
pattern[p + 1] = data >> 0; pattern[p + 1] = (data >> 0) & 15;
} }
void APU::Wave::power() { void APU::Wave::power() {
off = 0; dac_enable = 0;
length = 0; initial_length = 0;
volume = 0; volume = 0;
frequency = 0; frequency = 0;
counter = 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) { void APU::Wave::serialize(serializer &s) {
s.integer(off); s.integer(dac_enable);
s.integer(length); s.integer(initial_length);
s.integer(volume); s.integer(volume);
s.integer(frequency); s.integer(frequency);
s.integer(counter); s.integer(counter);
s.array(pattern); s.array(pattern);
s.integer(output);
s.integer(enable);
s.integer(length);
s.integer(period);
s.integer(pattern_offset);
s.integer(pattern_sample);
} }
#endif #endif

View File

@ -1,15 +1,21 @@
struct Wave { struct Wave {
bool off; bool dac_enable;
unsigned length; unsigned initial_length;
unsigned volume; unsigned volume;
unsigned frequency; unsigned frequency;
bool counter; bool counter;
uint8 pattern[32]; uint8 pattern[32];
int16 output;
bool enable;
unsigned length;
unsigned period;
unsigned pattern_offset;
unsigned pattern_sample;
void run(); void run();
uint8 read(unsigned r); void clock_length();
void write(unsigned r, uint8 data); void write(unsigned r, uint8 data);
uint8 read_pattern(unsigned p);
void write_pattern(unsigned p, uint8 data); void write_pattern(unsigned p, uint8 data);
void power(); void power();
void serialize(serializer&); void serialize(serializer&);

View File

@ -114,13 +114,7 @@ void CPU::power() {
status.clock = 0; status.clock = 0;
status.halt = false; status.halt = false;
status.stop = false; status.stop = false;
status.ime = 0; status.ime = 0;
status.timer0 = 0;
status.timer1 = 0;
status.timer2 = 0;
status.timer3 = 0;
status.timer4 = 0;
status.p15 = 0; status.p15 = 0;
status.p14 = 0; status.p14 = 0;

View File

@ -17,13 +17,7 @@ struct CPU : Processor, MMIO {
unsigned clock; unsigned clock;
bool halt; bool halt;
bool stop; bool stop;
bool ime; bool ime;
unsigned timer0;
unsigned timer1;
unsigned timer2;
unsigned timer3;
unsigned timer4;
//$ff00 JOYP //$ff00 JOYP
bool p15; bool p15;

View File

@ -21,13 +21,7 @@ void CPU::serialize(serializer &s) {
s.integer(status.clock); s.integer(status.clock);
s.integer(status.halt); s.integer(status.halt);
s.integer(status.stop); s.integer(status.stop);
s.integer(status.ime); 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.p15);
s.integer(status.p14); s.integer(status.p14);

View File

@ -22,8 +22,12 @@ void CPU::add_clocks(unsigned clocks) {
cartridge.mbc3.second(); cartridge.mbc3.second();
} }
status.timer0 += clocks; //4194304 / N(hz) - 1 = mask
if(status.timer0 >= 16) timer_stage0(); 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; lcd.clock -= clocks;
if(lcd.clock <= 0) co_switch(scheduler.active_thread = lcd.thread); 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); 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.timer_enable && status.timer_clock == 1) {
if(++status.tima == 0) { if(++status.tima == 0) {
status.tima = status.tma; status.tima = status.tma;
interrupt_raise(Interrupt::Timer); 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.timer_enable && status.timer_clock == 2) {
if(++status.tima == 0) { if(++status.tima == 0) {
status.tima = status.tma; status.tima = status.tma;
interrupt_raise(Interrupt::Timer); 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.timer_enable && status.timer_clock == 3) {
if(++status.tima == 0) { if(++status.tima == 0) {
status.tima = status.tma; status.tima = status.tma;
@ -65,32 +63,24 @@ void CPU::timer_stage2() { // 16384hz
} }
status.div++; 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_transfer && status.serial_clock) {
if(--status.serial_bits == 0) { if(--status.serial_bits == 0) {
status.serial_transfer = 0; status.serial_transfer = 0;
interrupt_raise(Interrupt::Serial); 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.timer_enable && status.timer_clock == 0) {
if(++status.tima == 0) { if(++status.tima == 0) {
status.tima = status.tma; status.tima = status.tma;
interrupt_raise(Interrupt::Timer); interrupt_raise(Interrupt::Timer);
} }
} }
status.timer4 -= 2;
} }
#endif #endif

View File

@ -1,9 +1,9 @@
void add_clocks(unsigned clocks); void add_clocks(unsigned clocks);
void timer_stage0(); void timer_262144hz();
void timer_stage1(); void timer_65536hz();
void timer_stage2(); void timer_16384hz();
void timer_stage3(); void timer_8192hz();
void timer_stage4(); void timer_4096hz();
//opcode.cpp //opcode.cpp
void op_io(); void op_io();

View File

@ -5,7 +5,7 @@
namespace GameBoy { namespace GameBoy {
namespace Info { namespace Info {
static const char Name[] = "bgameboy"; static const char Name[] = "bgameboy";
static const char Version[] = "000.15"; static const char Version[] = "000.16";
static unsigned SerializerVersion = 1; static unsigned SerializerVersion = 1;
} }
} }
@ -15,22 +15,30 @@ namespace GameBoy {
#include <nall/foreach.hpp> #include <nall/foreach.hpp>
#include <nall/platform.hpp> #include <nall/platform.hpp>
#include <nall/property.hpp> #include <nall/property.hpp>
#include <nall/random.hpp>
#include <nall/serializer.hpp> #include <nall/serializer.hpp>
#include <nall/stdint.hpp> #include <nall/stdint.hpp>
#include <nall/string.hpp> #include <nall/string.hpp>
#include <nall/varint.hpp>
using namespace nall; using namespace nall;
namespace GameBoy { namespace GameBoy {
typedef int8_t int8; typedef int8_t int8;
typedef int16_t int16; typedef int16_t int16;
typedef int32_t int32; typedef int32_t int32;
typedef int64_t int64; typedef int64_t int64;
typedef uint8_t uint8; typedef uint8_t uint8;
typedef uint16_t uint16; typedef uint16_t uint16;
typedef uint32_t uint32; typedef uint32_t uint32;
typedef uint64_t uint64; 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<uint16 lo, uint16 hi> template<uint16 lo, uint16 hi>
alwaysinline bool within(uint16 addr) { alwaysinline bool within(uint16 addr) {
static const uint16 mask = ~(hi ^ lo); static const uint16 mask = ~(hi ^ lo);

View File

@ -3,7 +3,7 @@ public:
virtual void joyp_write(bool p15, bool p14) {} virtual void joyp_write(bool p15, bool p14) {}
virtual void video_refresh(const uint8_t *data) {} 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 void input_poll() {}
virtual bool input_poll(unsigned id) {} virtual bool input_poll(unsigned id) {}

View File

@ -73,7 +73,7 @@ void ICD2::joyp_write(bool p15, bool p14) {
void ICD2::video_refresh(const uint8_t *data) { 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); audio.coprocessor_sample(left, right);
} }

View File

@ -1,6 +1,6 @@
void joyp_write(bool p15, bool p14); void joyp_write(bool p15, bool p14);
void video_refresh(const uint8_t *data); 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(); void input_poll();
bool input_poll(unsigned id); bool input_poll(unsigned id);

View File

@ -1,7 +1,7 @@
namespace SNES { namespace SNES {
namespace Info { namespace Info {
static const char Name[] = "bsnes"; static const char Name[] = "bsnes";
static const char Version[] = "075.04"; static const char Version[] = "075.05";
static const unsigned SerializerVersion = 18; static const unsigned SerializerVersion = 18;
} }
} }

View File

@ -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); audio.sample(left, right);
} }

View File

@ -2,7 +2,7 @@ struct Interface : public GameBoy::Interface {
int16_t inputState[Scancode::Limit]; int16_t inputState[Scancode::Limit];
void video_refresh(const uint8_t *data); 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(); void input_poll();
bool input_poll(unsigned id); bool input_poll(unsigned id);

View File

@ -10,7 +10,6 @@ void Utility::loadCartridge(const char *filename) {
fp.close(); fp.close();
cartridge.basename = nall::basename(filename); cartridge.basename = nall::basename(filename);
print(cartridge.basename, "\n");
GameBoyCartridge info(data, size); GameBoyCartridge info(data, size);
GameBoy::cartridge.load(info.xml, data, size); GameBoy::cartridge.load(info.xml, data, size);