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
gameboy := gameboy
profile := compatibility
ui := ui
ui := ui-gameboy
# compiler
c := $(compiler) -std=gnu99

View File

@ -1,12 +1,10 @@
#include <gameboy/gameboy.hpp>
#include <nall/random.hpp>
#define APU_CPP
namespace GameBoy {
#include "mmio/mmio.cpp"
#include "square/square.cpp"
#include "square1/square1.cpp"
#include "square2/square2.cpp"
#include "wave/wave.cpp"
#include "noise/noise.cpp"
#include "master/master.cpp"
@ -23,25 +21,45 @@ void APU::main() {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if((counter & 8191) == 0) { //512hz
if(sequencer == 0 || sequencer == 2 || sequencer == 4 || sequencer == 6) { //256hz
square1.clock_length();
square2.clock_length();
wave.clock_length();
noise.clock_length();
}
if(sequencer == 2 || sequencer == 6) { //128hz
square1.clock_sweep();
}
if(sequencer == 7) { //64hz
square1.clock_envelope();
square2.clock_envelope();
noise.clock_envelope();
}
sequencer = (sequencer + 1) & 7;
}
square1.run();
square2.run();
wave.run();
noise.run();
master.run();
system.interface->audio_sample((uint16)prng() >> 3, (uint16)prng() >> 3);
system.interface->audio_sample(master.center, master.left, master.right);
if(++counter == 4194304) counter = 0;
if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
}
}
void APU::power() {
create(Main, 4 * 1024 * 1024);
create(Main, 4194304);
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
square1.has_sweep = true;
square2.has_sweep = false;
foreach(n, mmio_data) n = 0x00;
counter = 0;
sequencer = 0;
square1.power();
square2.power();
@ -50,4 +68,40 @@ void APU::power() {
master.power();
}
uint8 APU::mmio_read(uint16 addr) {
static const uint8 table[48] = {
0x80, 0x3f, 0x00, 0xff, 0xbf, //square1
0xff, 0x3f, 0x00, 0xff, 0xbf, //square2
0x7f, 0xff, 0x9f, 0xff, 0xbf, //wave
0xff, 0xff, 0x00, 0x00, 0xbf, //noise
0x00, 0x00, 0x70, //master
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //unmapped
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
};
if(addr == 0xff26) {
uint8 data = master.enable << 7;
if(square1.counter && square1.length) data |= 0x01;
if(square2.counter && square2.length) data |= 0x02;
if( wave.counter && wave.length) data |= 0x04;
if( noise.counter && noise.length) data |= 0x08;
return data | table[addr - 0xff10];
}
if(addr >= 0xff10 && addr <= 0xff3f) return mmio_data[addr - 0xff10] | table[addr - 0xff10];
return 0xff;
}
void APU::mmio_write(uint16 addr, uint8 data) {
if(addr >= 0xff10 && addr <= 0xff3f) mmio_data[addr - 0xff10] = data;
if(addr >= 0xff10 && addr <= 0xff14) return square1.write (addr - 0xff10, data);
if(addr >= 0xff15 && addr <= 0xff19) return square2.write (addr - 0xff15, data);
if(addr >= 0xff1a && addr <= 0xff1e) return wave.write (addr - 0xff1a, data);
if(addr >= 0xff1f && addr <= 0xff23) return noise.write (addr - 0xff1f, data);
if(addr >= 0xff24 && addr <= 0xff26) return master.write (addr - 0xff24, data);
if(addr >= 0xff30 && addr <= 0xff3f) return wave.write_pattern(addr - 0xff30, data);
}
}

View File

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

View File

@ -1,99 +1,132 @@
#ifdef APU_CPP
void APU::Master::run() {
}
signed sample = 0, channels = 4;
sample += apu.square1.output;
sample += apu.square2.output;
sample += apu.wave.output;
sample += apu.noise.output;
sample /= channels;
if(sample < -32768) sample = -32768;
if(sample > +32767) sample = +32767;
center = sample;
uint8 APU::Master::read(unsigned r) {
if(r == 0) {
return (so2_enable << 7) | (so2_volume << 4) | (so1_enable << 3) | (so1_volume << 0);
sample = 0;
channels = 0;
if(channel1_left_enable) { sample += apu.square1.output; channels++; }
if(channel2_left_enable) { sample += apu.square2.output; channels++; }
if(channel3_left_enable) { sample += apu.wave.output; channels++; }
if(channel4_left_enable) { sample += apu.noise.output; channels++; }
if(channels) sample /= channels;
left = sample;
switch(left_volume) {
case 0: left *= 0.125; break;
case 1: left *= 0.250; break;
case 2: left *= 0.375; break;
case 3: left *= 0.500; break;
case 4: left *= 0.625; break;
case 5: left *= 0.750; break;
case 6: left *= 0.875; break;
case 7: left *= 1.000; break;
}
if(left_enable == false) left = 0;
sample = 0;
channels = 0;
if(channel1_right_enable) { sample += apu.square1.output; channels++; }
if(channel2_right_enable) { sample += apu.square2.output; channels++; }
if(channel3_right_enable) { sample += apu.wave.output; channels++; }
if(channel4_right_enable) { sample += apu.noise.output; channels++; }
if(channels) sample /= channels;
right = sample;
switch(right_volume) {
case 0: right *= 0.125; break;
case 1: right *= 0.250; break;
case 2: right *= 0.375; break;
case 3: right *= 0.500; break;
case 4: right *= 0.625; break;
case 5: right *= 0.750; break;
case 6: right *= 0.875; break;
case 7: right *= 1.000; break;
}
if(right_enable == false) right = 0;
if(left_enable == false && right_enable == false) {
left = center;
right = center;
}
if(r == 1) {
return (channel4_so2_enable << 7)
| (channel3_so2_enable << 6)
| (channel2_so2_enable << 5)
| (channel1_so2_enable << 4)
| (channel4_so1_enable << 3)
| (channel3_so1_enable << 2)
| (channel2_so1_enable << 1)
| (channel1_so1_enable << 0);
}
if(r == 2) {
return (enable << 7)
| (channel4_enable << 3)
| (channel3_enable << 2)
| (channel2_enable << 1)
| (channel1_enable << 0);
if(enable == false) {
center = 0;
left = 0;
right = 0;
}
}
void APU::Master::write(unsigned r, uint8 data) {
if(r == 0) {
so2_enable = data & 0x80;
so2_volume = (data >> 4) & 7;
so1_enable = data & 0x08;
so1_volume = (data >> 0) & 7;
left_enable = data & 0x80;
left_volume = (data >> 4) & 7;
right_enable = data & 0x08;
right_volume = (data >> 0) & 7;
}
if(r == 1) {
channel4_so2_enable = data & 0x80;
channel3_so2_enable = data & 0x40;
channel2_so2_enable = data & 0x20;
channel1_so2_enable = data & 0x10;
channel4_so1_enable = data & 0x08;
channel3_so1_enable = data & 0x04;
channel2_so1_enable = data & 0x02;
channel1_so1_enable = data & 0x01;
channel4_left_enable = data & 0x80;
channel3_left_enable = data & 0x40;
channel2_left_enable = data & 0x20;
channel1_left_enable = data & 0x10;
channel4_right_enable = data & 0x08;
channel3_right_enable = data & 0x04;
channel2_right_enable = data & 0x02;
channel1_right_enable = data & 0x01;
}
if(r == 2) {
enable = data & 0x80;
channel4_enable = data & 0x08;
channel3_enable = data & 0x04;
channel2_enable = data & 0x02;
channel1_enable = data & 0x01;
}
}
void APU::Master::power() {
so2_enable = 0;
so2_volume = 0;
so1_enable = 0;
so1_volume = 0;
channel4_so2_enable = 0;
channel3_so2_enable = 0;
channel2_so2_enable = 0;
channel1_so2_enable = 0;
channel4_so1_enable = 0;
channel3_so1_enable = 0;
channel2_so1_enable = 0;
channel1_so1_enable = 0;
left_enable = 0;
left_volume = 0;
right_enable = 0;
right_volume = 0;
channel4_left_enable = 0;
channel3_left_enable = 0;
channel2_left_enable = 0;
channel1_left_enable = 0;
channel4_right_enable = 0;
channel3_right_enable = 0;
channel2_right_enable = 0;
channel1_right_enable = 0;
enable = 0;
channel4_enable = 0;
channel3_enable = 0;
channel2_enable = 0;
channel1_enable = 0;
center = 0;
left = 0;
right = 0;
}
void APU::Master::serialize(serializer &s) {
s.integer(so2_enable);
s.integer(so2_volume);
s.integer(so1_enable);
s.integer(so1_volume);
s.integer(channel4_so2_enable);
s.integer(channel3_so2_enable);
s.integer(channel2_so2_enable);
s.integer(channel1_so2_enable);
s.integer(channel4_so1_enable);
s.integer(channel3_so1_enable);
s.integer(channel2_so1_enable);
s.integer(channel1_so1_enable);
s.integer(left_enable);
s.integer(left_volume);
s.integer(right_enable);
s.integer(right_volume);
s.integer(channel4_left_enable);
s.integer(channel3_left_enable);
s.integer(channel2_left_enable);
s.integer(channel1_left_enable);
s.integer(channel4_right_enable);
s.integer(channel3_right_enable);
s.integer(channel2_right_enable);
s.integer(channel1_right_enable);
s.integer(enable);
s.integer(channel4_enable);
s.integer(channel3_enable);
s.integer(channel2_enable);
s.integer(channel1_enable);
s.integer(center);
s.integer(left);
s.integer(right);
}
#endif

View File

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

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
void APU::Noise::run() {
if(period && --period == 0) {
period = divisor << frequency;
if(frequency < 14) {
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
lfsr = (lfsr >> 1) ^ (bit << 14);
if(narrow_lfsr) lfsr |= (bit << 6);
}
}
uint4 sample = (lfsr & 1) ? -volume : volume;
if(counter && length == 0) sample = 0;
output = (sample * 4369) - 32768;
}
uint8 APU::Noise::read(unsigned r) {
if(r == 2) {
return (envelope_volume << 4) | (envelope_direction << 3) | (envelope_period << 0);
}
void APU::Noise::clock_length() {
if(counter && length) length--;
}
if(r == 3) {
return (period << 4) | (step << 3) | (divisor);
void APU::Noise::clock_envelope() {
if(envelope_period && --envelope_period == 0) {
envelope_period = envelope_frequency;
if(envelope_direction == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++;
}
if(r == 4) {
return (counter << 6);
}
return 0x00;
}
void APU::Noise::write(unsigned r, uint8 data) {
if(r == 1) {
length = data & 0x3f;
initial_length = 64 - (data & 0x3f);
length = initial_length;
}
if(r == 2) {
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_period = data & 0x07;
envelope_frequency = data & 0x07;
}
if(r == 3) {
period = data >> 4;
step = data & 0x08;
divisor = data & 0x07;
frequency = data >> 4;
narrow_lfsr = data & 0x08;
divisor = (data & 0x07) << 4;
if(divisor == 0) divisor = 8;
period = divisor << frequency;
}
if(r == 4) {
bool initialize = data & 0x80;
counter = data & 0x40;
if(initialize) {
lfsr = ~0U;
length = initial_length;
envelope_period = envelope_frequency;
volume = envelope_volume;
}
}
}
void APU::Noise::power() {
length = 0;
initial_length = 0;
envelope_volume = 0;
envelope_direction = 0;
envelope_period = 0;
period = 0;
step = 0;
envelope_frequency = 0;
frequency = 0;
narrow_lfsr = 0;
divisor = 0;
counter = 0;
output = 0;
length = 0;
envelope_period = 0;
volume = 0;
period = 0;
lfsr = 0;
}
void APU::Noise::serialize(serializer &s) {
s.integer(length);
s.integer(initial_length);
s.integer(envelope_volume);
s.integer(envelope_direction);
s.integer(envelope_period);
s.integer(period);
s.integer(step);
s.integer(envelope_frequency);
s.integer(frequency);
s.integer(narrow_lfsr);
s.integer(divisor);
s.integer(counter);
s.integer(output);
s.integer(length);
s.integer(envelope_period);
s.integer(volume);
s.integer(period);
s.integer(lfsr);
}
#endif

View File

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

View File

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

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 {
bool has_sweep;
unsigned sweep_period;
unsigned sweep_direction;
unsigned sweep_shift;
struct Square2 {
unsigned duty;
unsigned length;
unsigned envelope_volume;
unsigned envelope_direction;
unsigned envelope_period;
unsigned envelope_frequency;
unsigned frequency;
unsigned counter;
int16 output;
bool duty_output;
unsigned phase;
unsigned period;
unsigned envelope_period;
unsigned volume;
void run();
uint8 read(unsigned r);
void clock_length();
void clock_envelope();
void write(unsigned r, uint8 data);
void power();
void serialize(serializer&);

View File

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

View File

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

View File

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

View File

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

View File

@ -21,13 +21,7 @@ void CPU::serialize(serializer &s) {
s.integer(status.clock);
s.integer(status.halt);
s.integer(status.stop);
s.integer(status.ime);
s.integer(status.timer0);
s.integer(status.timer1);
s.integer(status.timer2);
s.integer(status.timer3);
s.integer(status.timer4);
s.integer(status.p15);
s.integer(status.p14);

View File

@ -22,8 +22,12 @@ void CPU::add_clocks(unsigned clocks) {
cartridge.mbc3.second();
}
status.timer0 += clocks;
if(status.timer0 >= 16) timer_stage0();
//4194304 / N(hz) - 1 = mask
if((status.clock & 15) == 0) timer_262144hz();
if((status.clock & 63) == 0) timer_65536hz();
if((status.clock & 255) == 0) timer_16384hz();
if((status.clock & 511) == 0) timer_8192hz();
if((status.clock & 1023) == 0) timer_4096hz();
lcd.clock -= clocks;
if(lcd.clock <= 0) co_switch(scheduler.active_thread = lcd.thread);
@ -32,31 +36,25 @@ void CPU::add_clocks(unsigned clocks) {
if(apu.clock <= 0) co_switch(scheduler.active_thread = apu.thread);
}
void CPU::timer_stage0() { //262144hz
void CPU::timer_262144hz() {
if(status.timer_enable && status.timer_clock == 1) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
}
}
status.timer0 -= 16;
if(++status.timer1 >= 4) timer_stage1();
}
void CPU::timer_stage1() { // 65536hz
void CPU::timer_65536hz() {
if(status.timer_enable && status.timer_clock == 2) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
}
}
status.timer1 -= 4;
if(++status.timer2 >= 4) timer_stage2();
}
void CPU::timer_stage2() { // 16384hz
void CPU::timer_16384hz() {
if(status.timer_enable && status.timer_clock == 3) {
if(++status.tima == 0) {
status.tima = status.tma;
@ -65,32 +63,24 @@ void CPU::timer_stage2() { // 16384hz
}
status.div++;
status.timer2 -= 4;
if(++status.timer3 >= 2) timer_stage3();
}
void CPU::timer_stage3() { // 8192hz
void CPU::timer_8192hz() {
if(status.serial_transfer && status.serial_clock) {
if(--status.serial_bits == 0) {
status.serial_transfer = 0;
interrupt_raise(Interrupt::Serial);
}
}
status.timer3 -= 2;
if(++status.timer4 >= 2) timer_stage4();
}
void CPU::timer_stage4() { // 4096hz
void CPU::timer_4096hz() {
if(status.timer_enable && status.timer_clock == 0) {
if(++status.tima == 0) {
status.tima = status.tma;
interrupt_raise(Interrupt::Timer);
}
}
status.timer4 -= 2;
}
#endif

View File

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

View File

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

View File

@ -3,7 +3,7 @@ public:
virtual void joyp_write(bool p15, bool p14) {}
virtual void video_refresh(const uint8_t *data) {}
virtual void audio_sample(signed left, signed right) {}
virtual void audio_sample(int16_t center, int16_t left, int16_t right) {}
virtual void input_poll() {}
virtual bool input_poll(unsigned id) {}

View File

@ -73,7 +73,7 @@ void ICD2::joyp_write(bool p15, bool p14) {
void ICD2::video_refresh(const uint8_t *data) {
}
void ICD2::audio_sample(signed left, signed right) {
void ICD2::audio_sample(int16_t center, int16_t left, int16_t right) {
audio.coprocessor_sample(left, right);
}

View File

@ -1,6 +1,6 @@
void joyp_write(bool p15, bool p14);
void video_refresh(const uint8_t *data);
void audio_sample(signed left, signed right);
void audio_sample(int16_t center, int16_t left, int16_t right);
void input_poll();
bool input_poll(unsigned id);

View File

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

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

View File

@ -2,7 +2,7 @@ struct Interface : public GameBoy::Interface {
int16_t inputState[Scancode::Limit];
void video_refresh(const uint8_t *data);
void audio_sample(signed left, signed right);
void audio_sample(int16_t center, int16_t left, int16_t right);
void input_poll();
bool input_poll(unsigned id);

View File

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