Update to v096r05 release.

byuu says:

Changelog:
- GB: re-enabling the LCD resets the display to LY=0,LX=0 [1]
- GB: emulated new findings (as of today!) for a DMG quirk that triggers
  an extra OAM STAT IRQ when Vblank STAT IRQs are off
- GB: made VBK, BGPI, OBPI readable
- GB: fixed APU length operations
- GB: fixed APU sweep operations
- NES: fixed cartridge/ -> board/ manifest lookups for mirroring/pinous
- hiro/Cocoa: added endrift's plist keys

Fixed:
- Astro Rabby is fully playable, even the title screen works correctly
- Bomb Jack is fully playable
- Kirby's Dream Land 2 intro scrolling first scanline of Rick is now fixed
- GBVideoPlayer functions correctly [2]
- Shin Megami Tensei: Devichil series regression fixed

[1] doesn't pass oam_bug-2/1-lcd_sync; because it seems to want
LY=0,LX>0, and I can't step the PPU in a register write as it's not
a state machine; the effect is emulated, it just starts the frame a tiny
bit sooner. blargg's testing is brutal, you can't be even one cycle off
or the test will fail.

[2] note that you will need the GBC Display Emulation shader from
hunterk's repository, or it will look like absolute shit. The
inter-frame blending is absolutely critical here.
This commit is contained in:
Tim Allen
2016-01-12 22:08:34 +11:00
parent 72b6a8b32e
commit 82ec876302
26 changed files with 442 additions and 364 deletions

View File

@@ -2,11 +2,17 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleIdentifier</key>
<string>org.byuu.higan</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>higan</string> <string>higan</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>higan</string> <string>higan</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>higan.icns</string> <string>higan.icns</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

@@ -6,7 +6,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; static const string Name = "higan";
static const string Version = "096.04"; static const string Version = "096.05";
static const string Author = "byuu"; static const string Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "http://byuu.org/"; static const string Website = "http://byuu.org/";

View File

@@ -1,7 +1,7 @@
struct KonamiVRC2 : Board { struct KonamiVRC2 : Board {
KonamiVRC2(Markup::Node& document) : Board(document), vrc2(*this) { KonamiVRC2(Markup::Node& document) : Board(document), vrc2(*this) {
settings.pinout.a0 = 1 << document["cartridge/chip/pinout/a0"].natural(); settings.pinout.a0 = 1 << document["board/chip/pinout/a0"].natural();
settings.pinout.a1 = 1 << document["cartridge/chip/pinout/a1"].natural(); settings.pinout.a1 = 1 << document["board/chip/pinout/a1"].natural();
} }
auto prg_read(uint addr) -> uint8 { auto prg_read(uint addr) -> uint8 {

View File

@@ -1,6 +1,6 @@
struct KonamiVRC3 : Board { struct KonamiVRC3 : Board {
KonamiVRC3(Markup::Node& document) : Board(document), vrc3(*this) { KonamiVRC3(Markup::Node& document) : Board(document), vrc3(*this) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0; settings.mirror = document["board/mirror/mode"].text() == "vertical" ? 1 : 0;
} }
auto main() -> void { auto main() -> void {

View File

@@ -1,7 +1,7 @@
struct KonamiVRC4 : Board { struct KonamiVRC4 : Board {
KonamiVRC4(Markup::Node& document) : Board(document), vrc4(*this) { KonamiVRC4(Markup::Node& document) : Board(document), vrc4(*this) {
settings.pinout.a0 = 1 << document["cartridge/chip/pinout/a0"].natural(); settings.pinout.a0 = 1 << document["board/chip/pinout/a0"].natural();
settings.pinout.a1 = 1 << document["cartridge/chip/pinout/a1"].natural(); settings.pinout.a1 = 1 << document["board/chip/pinout/a1"].natural();
} }
auto main() -> void { auto main() -> void {

View File

@@ -2,7 +2,7 @@
struct NES_BNROM : Board { struct NES_BNROM : Board {
NES_BNROM(Markup::Node& document) : Board(document) { NES_BNROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0; settings.mirror = document["board/mirror/mode"].text() == "vertical" ? 1 : 0;
} }
auto prg_read(uint addr) -> uint8 { auto prg_read(uint addr) -> uint8 {

View File

@@ -2,7 +2,7 @@
struct NES_CNROM : Board { struct NES_CNROM : Board {
NES_CNROM(Markup::Node& document) : Board(document) { NES_CNROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0; settings.mirror = document["board/mirror/mode"].text() == "vertical" ? 1 : 0;
} }
auto prg_read(uint addr) -> uint8 { auto prg_read(uint addr) -> uint8 {

View File

@@ -3,7 +3,7 @@
struct NES_GxROM : Board { struct NES_GxROM : Board {
NES_GxROM(Markup::Node& document) : Board(document) { NES_GxROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0; settings.mirror = document["board/mirror/mode"].text() == "vertical" ? 1 : 0;
} }
auto prg_read(uint addr) -> uint8 { auto prg_read(uint addr) -> uint8 {

View File

@@ -3,7 +3,7 @@
struct NES_NROM : Board { struct NES_NROM : Board {
NES_NROM(Markup::Node& document) : Board(document) { NES_NROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0; settings.mirror = document["board/mirror/mode"].text() == "vertical" ? 1 : 0;
} }
auto prg_read(uint addr) -> uint8 { auto prg_read(uint addr) -> uint8 {

View File

@@ -3,7 +3,7 @@
struct NES_UxROM : Board { struct NES_UxROM : Board {
NES_UxROM(Markup::Node& document) : Board(document) { NES_UxROM(Markup::Node& document) : Board(document) {
settings.mirror = document["cartridge/mirror/mode"].text() == "vertical" ? 1 : 0; settings.mirror = document["board/mirror/mode"].text() == "vertical" ? 1 : 0;
} }
auto prg_read(uint addr) -> uint8 { auto prg_read(uint addr) -> uint8 {

View File

@@ -20,24 +20,24 @@ auto APU::main() -> void {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
} }
if(sequencer_base == 0) { //512hz if(stage == 0) { //512hz
if(sequencer_step == 0 || sequencer_step == 2 || sequencer_step == 4 || sequencer_step == 6) { //256hz if(phase == 0 || phase == 2 || phase == 4 || phase == 6) { //256hz
square1.clock_length(); square1.clockLength();
square2.clock_length(); square2.clockLength();
wave.clock_length(); wave.clockLength();
noise.clock_length(); noise.clockLength();
} }
if(sequencer_step == 2 || sequencer_step == 6) { //128hz if(phase == 2 || phase == 6) { //128hz
square1.clock_sweep(); square1.clockSweep();
} }
if(sequencer_step == 7) { //64hz if(phase == 7) { //64hz
square1.clock_envelope(); square1.clockEnvelope();
square2.clock_envelope(); square2.clockEnvelope();
noise.clock_envelope(); noise.clockEnvelope();
} }
sequencer_step++; phase++;
} }
sequencer_base++; stage++;
square1.run(); square1.run();
square2.run(); square2.run();
@@ -45,9 +45,9 @@ auto APU::main() -> void {
noise.run(); noise.run();
master.run(); master.run();
hipass(master.center, master.center_bias); hipass(master.center, master.centerBias);
hipass(master.left, master.left_bias); hipass(master.left, master.leftBias);
hipass(master.right, master.right_bias); hipass(master.right, master.rightBias);
interface->audioSample(master.left, master.right); interface->audioSample(master.left, master.right);
@@ -65,8 +65,8 @@ auto APU::power() -> void {
create(Main, 2 * 1024 * 1024); create(Main, 2 * 1024 * 1024);
for(uint n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this; for(uint n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
sequencer_base = 0; stage = 0;
sequencer_step = 0; phase = 0;
square1.power(); square1.power();
square2.power(); square2.power();

View File

@@ -15,8 +15,8 @@ struct APU : Thread, MMIO {
#include "noise/noise.hpp" #include "noise/noise.hpp"
#include "master/master.hpp" #include "master/master.hpp"
uint12 sequencer_base; uint12 stage;
uint3 sequencer_step; uint3 phase;
Square1 square1; Square1 square1;
Square2 square2; Square2 square2;

View File

@@ -4,7 +4,7 @@ auto APU::Master::run() -> void {
left = 0; left = 0;
right = 0; right = 0;
center_bias = left_bias = right_bias = 0; centerBias = leftBias = rightBias = 0;
return; return;
} }
@@ -16,21 +16,21 @@ auto APU::Master::run() -> void {
center = (sample * 512) - 16384; center = (sample * 512) - 16384;
sample = 0; sample = 0;
if(channel1_left_enable) sample += apu.square1.output; if(square1.leftEnable) sample += apu.square1.output;
if(channel2_left_enable) sample += apu.square2.output; if(square2.leftEnable) sample += apu.square2.output;
if(channel3_left_enable) sample += apu.wave.output; if( wave.leftEnable) sample += apu.wave.output;
if(channel4_left_enable) sample += apu.noise.output; if( noise.leftEnable) sample += apu.noise.output;
sample = (sample * 512) - 16384; sample = (sample * 512) - 16384;
sample = (sample * (left_volume + 1)) / 8; sample = (sample * (leftVolume + 1)) / 8;
left = sample; left = sample;
sample = 0; sample = 0;
if(channel1_right_enable) sample += apu.square1.output; if(square1.rightEnable) sample += apu.square1.output;
if(channel2_right_enable) sample += apu.square2.output; if(square2.rightEnable) sample += apu.square2.output;
if(channel3_right_enable) sample += apu.wave.output; if( wave.rightEnable) sample += apu.wave.output;
if(channel4_right_enable) sample += apu.noise.output; if( noise.rightEnable) sample += apu.noise.output;
sample = (sample * 512) - 16384; sample = (sample * 512) - 16384;
sample = (sample * (right_volume + 1)) / 8; sample = (sample * (rightVolume + 1)) / 8;
right = sample; right = sample;
//reduce audio volume //reduce audio volume
@@ -41,18 +41,18 @@ auto APU::Master::run() -> void {
auto APU::Master::read(uint16 addr) -> uint8 { auto APU::Master::read(uint16 addr) -> uint8 {
if(addr == 0xff24) { //NR50 if(addr == 0xff24) { //NR50
return left_in_enable << 7 | left_volume << 4 | right_in_enable << 3 | right_volume; return leftEnable << 7 | leftVolume << 4 | rightEnable << 3 | rightVolume;
} }
if(addr == 0xff25) { //NR51 if(addr == 0xff25) { //NR51
return channel4_left_enable << 7 return noise.leftEnable << 7
| channel3_left_enable << 6 | wave.leftEnable << 6
| channel2_left_enable << 5 | square2.leftEnable << 5
| channel1_left_enable << 4 | square1.leftEnable << 4
| channel4_right_enable << 3 | noise.rightEnable << 3
| channel3_right_enable << 2 | wave.rightEnable << 2
| channel2_right_enable << 1 | square2.rightEnable << 1
| channel1_right_enable << 0; | square1.rightEnable << 0;
} }
if(addr == 0xff26) { //NR52 if(addr == 0xff26) { //NR52
@@ -68,79 +68,80 @@ auto APU::Master::read(uint16 addr) -> uint8 {
auto APU::Master::write(uint16 addr, uint8 data) -> void { auto APU::Master::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff24) { //NR50 if(addr == 0xff24) { //NR50
left_in_enable = data & 0x80; leftEnable = data & 0x80;
left_volume = (data >> 4) & 7; leftVolume = (data >> 4) & 7;
right_in_enable = data & 0x08; rightEnable = data & 0x08;
right_volume = (data >> 0) & 7; rightVolume = (data >> 0) & 7;
} }
if(addr == 0xff25) { //NR51 if(addr == 0xff25) { //NR51
channel4_left_enable = data & 0x80; noise.leftEnable = data & 0x80;
channel3_left_enable = data & 0x40; wave.leftEnable = data & 0x40;
channel2_left_enable = data & 0x20; square2.leftEnable = data & 0x20;
channel1_left_enable = data & 0x10; square1.leftEnable = data & 0x10;
channel4_right_enable = data & 0x08; noise.rightEnable = data & 0x08;
channel3_right_enable = data & 0x04; wave.rightEnable = data & 0x04;
channel2_right_enable = data & 0x02; square2.rightEnable = data & 0x02;
channel1_right_enable = data & 0x01; square1.rightEnable = data & 0x01;
} }
if(addr == 0xff26) { //NR52 if(addr == 0xff26) { //NR52
enable = data & 0x80; enable = data & 0x80;
if(!enable) { if(!enable) {
apu.square1.power(); //power(bool) resets length counters when true (eg for CGB only)
apu.square2.power(); apu.square1.power(system.cgb());
apu.wave.power(); apu.square2.power(system.cgb());
apu.noise.power(); apu.wave.power(system.cgb());
apu.noise.power(system.cgb());
power(); power();
} }
} }
} }
auto APU::Master::power() -> void { auto APU::Master::power() -> void {
left_in_enable = 0; leftEnable = 0;
left_volume = 0; leftVolume = 0;
right_in_enable = 0; rightEnable = 0;
right_volume = 0; rightVolume = 0;
channel4_left_enable = 0; noise.leftEnable = 0;
channel3_left_enable = 0; wave.leftEnable = 0;
channel2_left_enable = 0; square2.leftEnable = 0;
channel1_left_enable = 0; square1.leftEnable = 0;
channel4_right_enable = 0; noise.rightEnable = 0;
channel3_right_enable = 0; wave.rightEnable = 0;
channel2_right_enable = 0; square2.rightEnable = 0;
channel1_right_enable = 0; square1.rightEnable = 0;
enable = 0; enable = 0;
center = 0; center = 0;
left = 0; left = 0;
right = 0; right = 0;
center_bias = 0; centerBias = 0;
left_bias = 0; leftBias = 0;
right_bias = 0; rightBias = 0;
} }
auto APU::Master::serialize(serializer& s) -> void { auto APU::Master::serialize(serializer& s) -> void {
s.integer(left_in_enable); s.integer(leftEnable);
s.integer(left_volume); s.integer(leftVolume);
s.integer(right_in_enable); s.integer(rightEnable);
s.integer(right_volume); s.integer(rightVolume);
s.integer(channel4_left_enable); s.integer(noise.leftEnable);
s.integer(channel3_left_enable); s.integer(wave.leftEnable);
s.integer(channel2_left_enable); s.integer(square2.leftEnable);
s.integer(channel1_left_enable); s.integer(square1.leftEnable);
s.integer(channel4_right_enable); s.integer(noise.rightEnable);
s.integer(channel3_right_enable); s.integer(wave.rightEnable);
s.integer(channel2_right_enable); s.integer(square2.rightEnable);
s.integer(channel1_right_enable); s.integer(square1.rightEnable);
s.integer(enable); s.integer(enable);
s.integer(center); s.integer(center);
s.integer(left); s.integer(left);
s.integer(right); s.integer(right);
s.integer(center_bias); s.integer(centerBias);
s.integer(left_bias); s.integer(leftBias);
s.integer(right_bias); s.integer(rightBias);
} }

View File

@@ -6,25 +6,23 @@ struct Master {
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
bool left_in_enable; bool leftEnable;
uint3 left_volume; uint3 leftVolume;
bool right_in_enable; bool rightEnable;
uint3 right_volume; uint3 rightVolume;
bool channel4_left_enable;
bool channel3_left_enable; struct Channel {
bool channel2_left_enable; bool leftEnable;
bool channel1_left_enable; bool rightEnable;
bool channel4_right_enable; } square1, square2, wave, noise;
bool channel3_right_enable;
bool channel2_right_enable;
bool channel1_right_enable;
bool enable; bool enable;
int16 center; int16 center;
int16 left; int16 left;
int16 right; int16 right;
int64 center_bias; int64 centerBias;
int64 left_bias; int64 leftBias;
int64 right_bias; int64 rightBias;
}; };

View File

@@ -1,38 +1,38 @@
auto APU::Noise::dac_enable() const -> bool { auto APU::Noise::dacEnable() const -> bool {
return (envelope_volume || envelope_direction); return (envelopeVolume || envelopeDirection);
} }
auto APU::Noise::get_period() const -> uint { auto APU::Noise::getPeriod() const -> uint {
static const uint table[] = {4, 8, 16, 24, 32, 40, 48, 56}; static const uint table[] = {4, 8, 16, 24, 32, 40, 48, 56};
return table[divisor] << frequency; return table[divisor] << frequency;
} }
auto APU::Noise::run() -> void { auto APU::Noise::run() -> void {
if(period && --period == 0) { if(period && --period == 0) {
period = get_period(); period = getPeriod();
if(frequency < 14) { if(frequency < 14) {
bool bit = (lfsr ^ (lfsr >> 1)) & 1; bool bit = (lfsr ^ (lfsr >> 1)) & 1;
lfsr = (lfsr >> 1) ^ (bit << (narrow_lfsr ? 6 : 14)); lfsr = (lfsr >> 1) ^ (bit << (narrow ? 6 : 14));
} }
} }
uint4 sample = (lfsr & 1) ? (uint4)0 : volume; uint4 sample = (lfsr & 1) ? (uint4)0 : volume;
if(enable == false) sample = 0; if(!enable) sample = 0;
output = sample; output = sample;
} }
auto APU::Noise::clock_length() -> void { auto APU::Noise::clockLength() -> void {
if(counter) { if(counter) {
if(++length == 0) enable = false; if(length && --length == 0) enable = false;
} }
} }
auto APU::Noise::clock_envelope() -> void { auto APU::Noise::clockEnvelope() -> void {
if(enable && envelope_frequency && --envelope_period == 0) { if(enable && envelopeFrequency && --envelopePeriod == 0) {
envelope_period = envelope_frequency; envelopePeriod = envelopeFrequency;
if(envelope_direction == 0 && volume > 0) volume--; if(envelopeDirection == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++; if(envelopeDirection == 1 && volume < 15) volume++;
} }
} }
@@ -46,11 +46,11 @@ auto APU::Noise::read(uint16 addr) -> uint8 {
} }
if(addr == 0xff21) { //NR42 if(addr == 0xff21) { //NR42
return envelope_volume << 4 | envelope_direction << 3 | envelope_frequency; return envelopeVolume << 4 | envelopeDirection << 3 | envelopeFrequency;
} }
if(addr == 0xff22) { //NR43 if(addr == 0xff22) { //NR43
return frequency << 4 | narrow_lfsr << 3 | divisor; return frequency << 4 | narrow << 3 | divisor;
} }
if(addr == 0xff23) { //NR44 if(addr == 0xff23) { //NR44
@@ -62,69 +62,79 @@ auto APU::Noise::read(uint16 addr) -> uint8 {
auto APU::Noise::write(uint16 addr, uint8 data) -> void { auto APU::Noise::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff20) { //NR41 if(addr == 0xff20) { //NR41
length = data & 0x3f; length = 64 - (data & 0x3f);
} }
if(addr == 0xff21) { //NR42 if(addr == 0xff21) { //NR42
envelope_volume = data >> 4; envelopeVolume = data >> 4;
envelope_direction = data & 0x08; envelopeDirection = data & 0x08;
envelope_frequency = data & 0x07; envelopeFrequency = data & 0x07;
if(dac_enable() == false) enable = false; if(!dacEnable()) enable = false;
} }
if(addr == 0xff22) { //NR43 if(addr == 0xff22) { //NR43
frequency = data >> 4; frequency = data >> 4;
narrow_lfsr = data & 0x08; narrow = data & 0x08;
divisor = data & 0x07; divisor = data & 0x07;
period = get_period(); period = getPeriod();
} }
if(addr == 0xff23) { //NR44 if(addr == 0xff23) { //NR44
if((apu.phase & 1) && !counter && (data & 0x40)) {
if(length && --length == 0) enable = false;
}
bool initialize = data & 0x80; bool initialize = data & 0x80;
counter = data & 0x40; counter = data & 0x40;
if(initialize) { if(initialize) {
enable = dac_enable(); enable = dacEnable();
lfsr = -1; lfsr = -1;
envelope_period = envelope_frequency; envelopePeriod = envelopeFrequency;
volume = envelope_volume; volume = envelopeVolume;
if(!length) {
length = 64;
if((apu.phase & 1) && counter) length--;
}
} }
} }
} }
auto APU::Noise::power() -> void { auto APU::Noise::power(bool initializeLength) -> void {
enable = 0; enable = 0;
envelope_volume = 0; envelopeVolume = 0;
envelope_direction = 0; envelopeDirection = 0;
envelope_frequency = 0; envelopeFrequency = 0;
frequency = 0; frequency = 0;
narrow_lfsr = 0; narrow = 0;
divisor = 0; divisor = 0;
counter = 0; counter = 0;
output = 0; output = 0;
length = 0; envelopePeriod = 0;
envelope_period = 0;
volume = 0; volume = 0;
period = 0; period = 0;
lfsr = 0; lfsr = 0;
if(initializeLength) length = 64;
} }
auto APU::Noise::serialize(serializer& s) -> void { auto APU::Noise::serialize(serializer& s) -> void {
s.integer(enable); s.integer(enable);
s.integer(envelope_volume); s.integer(envelopeVolume);
s.integer(envelope_direction); s.integer(envelopeDirection);
s.integer(envelope_frequency); s.integer(envelopeFrequency);
s.integer(frequency); s.integer(frequency);
s.integer(narrow_lfsr); s.integer(narrow);
s.integer(divisor); s.integer(divisor);
s.integer(counter); s.integer(counter);
s.integer(output); s.integer(output);
s.integer(length); s.integer(length);
s.integer(envelope_period); s.integer(envelopePeriod);
s.integer(volume); s.integer(volume);
s.integer(period); s.integer(period);
s.integer(lfsr); s.integer(lfsr);

View File

@@ -1,29 +1,29 @@
struct Noise { struct Noise {
auto dac_enable() const -> bool; auto dacEnable() const -> bool;
auto get_period() const -> uint; auto getPeriod() const -> uint;
auto run() -> void; auto run() -> void;
auto clock_length() -> void; auto clockLength() -> void;
auto clock_envelope() -> void; auto clockEnvelope() -> void;
auto read(uint16 addr) -> uint8; auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void; auto write(uint16 addr, uint8 data) -> void;
auto power() -> void; auto power(bool initializeLength = true) -> void;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
bool enable; bool enable;
uint4 envelope_volume; uint4 envelopeVolume;
bool envelope_direction; bool envelopeDirection;
uint3 envelope_frequency; uint3 envelopeFrequency;
uint4 frequency; uint4 frequency;
bool narrow_lfsr; bool narrow;
uint3 divisor; uint3 divisor;
bool counter; bool counter;
int16 output; int16 output;
uint6 length; uint length;
uint3 envelope_period; uint3 envelopePeriod;
uint4 volume; uint4 volume;
uint period; uint period;
uint15 lfsr; uint15 lfsr;

View File

@@ -1,8 +1,8 @@
auto APU::serialize(serializer& s) -> void { auto APU::serialize(serializer& s) -> void {
Thread::serialize(s); Thread::serialize(s);
s.integer(sequencer_base); s.integer(stage);
s.integer(sequencer_step); s.integer(phase);
square1.serialize(s); square1.serialize(s);
square2.serialize(s); square2.serialize(s);

View File

@@ -1,5 +1,5 @@
auto APU::Square1::dac_enable() const -> bool { auto APU::Square1::dacEnable() const -> bool {
return (envelope_volume || envelope_direction); return (envelopeVolume || envelopeDirection);
} }
auto APU::Square1::run() -> void { auto APU::Square1::run() -> void {
@@ -7,60 +7,62 @@ auto APU::Square1::run() -> void {
period = 2 * (2048 - frequency); period = 2 * (2048 - frequency);
phase++; phase++;
switch(duty) { switch(duty) {
case 0: duty_output = (phase == 6); break; //______-_ case 0: dutyOutput = (phase == 6); break; //______-_
case 1: duty_output = (phase >= 6); break; //______-- case 1: dutyOutput = (phase >= 6); break; //______--
case 2: duty_output = (phase >= 4); break; //____---- case 2: dutyOutput = (phase >= 4); break; //____----
case 3: duty_output = (phase <= 5); break; //------__ case 3: dutyOutput = (phase <= 5); break; //------__
} }
} }
uint4 sample = (duty_output ? volume : (uint4)0); uint4 sample = (dutyOutput ? volume : (uint4)0);
if(enable == false) sample = 0; if(!enable) sample = 0;
output = sample; output = sample;
} }
auto APU::Square1::sweep(bool update) -> void { auto APU::Square1::sweep(bool update) -> void {
if(sweep_enable == false) return; if(!sweepEnable) return;
sweep_negate = sweep_direction; sweepNegate = sweepDirection;
uint delta = frequency_shadow >> sweep_shift; uint delta = frequencyShadow >> sweepShift;
int freq = frequency_shadow + (sweep_negate ? -delta : delta); int freq = frequencyShadow + (sweepNegate ? -delta : delta);
if(freq > 2047) { if(freq > 2047) {
enable = false; enable = false;
} else if(sweep_shift && update) { } else if(sweepShift && update) {
frequency_shadow = freq; frequencyShadow = freq;
frequency = freq & 2047; frequency = freq & 2047;
period = 2 * (2048 - frequency); period = 2 * (2048 - frequency);
} }
} }
auto APU::Square1::clock_length() -> void { auto APU::Square1::clockLength() -> void {
if(counter) { if(counter) {
if(++length == 0) enable = false; if(length && --length == 0) enable = false;
} }
} }
auto APU::Square1::clock_sweep() -> void { auto APU::Square1::clockSweep() -> void {
if(enable && sweep_frequency && --sweep_period == 0) { if(--sweepPeriod == 0) {
sweep_period = sweep_frequency; sweepPeriod = sweepFrequency ? (uint)sweepFrequency : 8;
sweep(1); if(sweepEnable && sweepFrequency) {
sweep(0); sweep(1);
sweep(0);
}
} }
} }
auto APU::Square1::clock_envelope() -> void { auto APU::Square1::clockEnvelope() -> void {
if(enable && envelope_frequency && --envelope_period == 0) { if(enable && envelopeFrequency && --envelopePeriod == 0) {
envelope_period = envelope_frequency; envelopePeriod = envelopeFrequency;
if(envelope_direction == 0 && volume > 0) volume--; if(envelopeDirection == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++; if(envelopeDirection == 1 && volume < 15) volume++;
} }
} }
auto APU::Square1::read(uint16 addr) -> uint8 { auto APU::Square1::read(uint16 addr) -> uint8 {
if(addr == 0xff10) { //NR10 if(addr == 0xff10) { //NR10
return 0x80 | sweep_frequency << 4 | sweep_direction << 3 | sweep_shift; return 0x80 | sweepFrequency << 4 | sweepDirection << 3 | sweepShift;
} }
if(addr == 0xff11) { //NR11 if(addr == 0xff11) { //NR11
@@ -68,7 +70,7 @@ auto APU::Square1::read(uint16 addr) -> uint8 {
} }
if(addr == 0xff12) { //NR12 if(addr == 0xff12) { //NR12
return envelope_volume << 4 | envelope_direction << 3 | envelope_frequency; return envelopeVolume << 4 | envelopeDirection << 3 | envelopeFrequency;
} }
if(addr == 0xff13) { //NR13 if(addr == 0xff13) { //NR13
@@ -84,22 +86,22 @@ auto APU::Square1::read(uint16 addr) -> uint8 {
auto APU::Square1::write(uint16 addr, uint8 data) -> void { auto APU::Square1::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff10) { //NR10 if(addr == 0xff10) { //NR10
if(sweep_negate && sweep_direction && !(data & 0x08)) enable = false; if(sweepEnable && sweepNegate && !(data & 0x08)) enable = false;
sweep_frequency = (data >> 4) & 7; sweepFrequency = (data >> 4) & 7;
sweep_direction = data & 0x08; sweepDirection = data & 0x08;
sweep_shift = data & 0x07; sweepShift = data & 0x07;
} }
if(addr == 0xff11) { //NR11 if(addr == 0xff11) { //NR11
duty = data >> 6; duty = data >> 6;
length = data & 0x3f; length = 64 - (data & 0x3f);
} }
if(addr == 0xff12) { //NR12 if(addr == 0xff12) { //NR12
envelope_volume = data >> 4; envelopeVolume = data >> 4;
envelope_direction = data & 0x08; envelopeDirection = data & 0x08;
envelope_frequency = data & 0x07; envelopeFrequency = data & 0x07;
if(dac_enable() == false) enable = false; if(!dacEnable()) enable = false;
} }
if(addr == 0xff13) { //NR13 if(addr == 0xff13) { //NR13
@@ -107,72 +109,83 @@ auto APU::Square1::write(uint16 addr, uint8 data) -> void {
} }
if(addr == 0xff14) { //NR14 if(addr == 0xff14) { //NR14
if((apu.phase & 1) && !counter && (data & 0x40)) {
if(length && --length == 0) enable = false;
}
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);
if(initialize) { if(initialize) {
enable = dac_enable(); enable = dacEnable();
period = 2 * (2048 - frequency); period = 2 * (2048 - frequency);
envelope_period = envelope_frequency; envelopePeriod = envelopeFrequency;
volume = envelope_volume; volume = envelopeVolume;
frequency_shadow = frequency;
sweep_period = sweep_frequency; if(!length) {
sweep_enable = sweep_period || sweep_shift; length = 64;
sweep_negate = false; if((apu.phase & 1) && counter) length--;
if(sweep_shift) sweep(0); }
frequencyShadow = frequency;
sweepNegate = false;
sweepPeriod = sweepFrequency ? (uint)sweepFrequency : 8;
sweepEnable = sweepPeriod || sweepShift;
if(sweepShift) sweep(0);
} }
} }
} }
auto APU::Square1::power() -> void { auto APU::Square1::power(bool initializeLength) -> void {
enable = 0; enable = 0;
sweep_frequency = 0; sweepFrequency = 0;
sweep_direction = 0; sweepDirection = 0;
sweep_shift = 0; sweepShift = 0;
sweep_negate = 0; sweepNegate = 0;
duty = 0; duty = 0;
length = 0; envelopeVolume = 0;
envelope_volume = 0; envelopeDirection = 0;
envelope_direction = 0; envelopeFrequency = 0;
envelope_frequency = 0;
frequency = 0; frequency = 0;
counter = 0; counter = 0;
output = 0; output = 0;
duty_output = 0; dutyOutput = 0;
phase = 0; phase = 0;
period = 0; period = 0;
envelope_period = 0; envelopePeriod = 0;
sweep_period = 0; sweepPeriod = 0;
frequency_shadow = 0; frequencyShadow = 0;
sweep_enable = 0; sweepEnable = 0;
volume = 0; volume = 0;
if(initializeLength) length = 64;
} }
auto APU::Square1::serialize(serializer& s) -> void { auto APU::Square1::serialize(serializer& s) -> void {
s.integer(enable); s.integer(enable);
s.integer(sweep_frequency); s.integer(sweepFrequency);
s.integer(sweep_direction); s.integer(sweepDirection);
s.integer(sweep_shift); s.integer(sweepShift);
s.integer(sweep_negate); s.integer(sweepNegate);
s.integer(duty); s.integer(duty);
s.integer(length); s.integer(length);
s.integer(envelope_volume); s.integer(envelopeVolume);
s.integer(envelope_direction); s.integer(envelopeDirection);
s.integer(envelope_frequency); s.integer(envelopeFrequency);
s.integer(frequency); s.integer(frequency);
s.integer(counter); s.integer(counter);
s.integer(output); s.integer(output);
s.integer(duty_output); s.integer(dutyOutput);
s.integer(phase); s.integer(phase);
s.integer(period); s.integer(period);
s.integer(envelope_period); s.integer(envelopePeriod);
s.integer(sweep_period); s.integer(sweepPeriod);
s.integer(frequency_shadow); s.integer(frequencyShadow);
s.integer(sweep_enable); s.integer(sweepEnable);
s.integer(volume); s.integer(volume);
} }

View File

@@ -1,38 +1,38 @@
struct Square1 { struct Square1 {
auto dac_enable() const -> bool; auto dacEnable() const -> bool;
auto run() -> void; auto run() -> void;
auto sweep(bool update) -> void; auto sweep(bool update) -> void;
auto clock_length() -> void; auto clockLength() -> void;
auto clock_sweep() -> void; auto clockSweep() -> void;
auto clock_envelope() -> void; auto clockEnvelope() -> void;
auto read(uint16 addr) -> uint8; auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void; auto write(uint16 addr, uint8 data) -> void;
auto power() -> void; auto power(bool initializeLength = true) -> void;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
bool enable; bool enable;
uint3 sweep_frequency; uint3 sweepFrequency;
bool sweep_direction; bool sweepDirection;
uint3 sweep_shift; uint3 sweepShift;
bool sweep_negate; bool sweepNegate;
uint2 duty; uint2 duty;
uint6 length; uint length;
uint4 envelope_volume; uint4 envelopeVolume;
bool envelope_direction; bool envelopeDirection;
uint3 envelope_frequency; uint3 envelopeFrequency;
uint11 frequency; uint11 frequency;
bool counter; bool counter;
int16 output; int16 output;
bool duty_output; bool dutyOutput;
uint3 phase; uint3 phase;
uint period; uint period;
uint3 envelope_period; uint3 envelopePeriod;
uint3 sweep_period; uint3 sweepPeriod;
int frequency_shadow; int frequencyShadow;
bool sweep_enable; bool sweepEnable;
uint4 volume; uint4 volume;
}; };

View File

@@ -1,5 +1,5 @@
auto APU::Square2::dac_enable() const -> bool { auto APU::Square2::dacEnable() const -> bool {
return (envelope_volume || envelope_direction); return (envelopeVolume || envelopeDirection);
} }
auto APU::Square2::run() -> void { auto APU::Square2::run() -> void {
@@ -7,30 +7,30 @@ auto APU::Square2::run() -> void {
period = 2 * (2048 - frequency); period = 2 * (2048 - frequency);
phase++; phase++;
switch(duty) { switch(duty) {
case 0: duty_output = (phase == 6); break; //______-_ case 0: dutyOutput = (phase == 6); break; //______-_
case 1: duty_output = (phase >= 6); break; //______-- case 1: dutyOutput = (phase >= 6); break; //______--
case 2: duty_output = (phase >= 4); break; //____---- case 2: dutyOutput = (phase >= 4); break; //____----
case 3: duty_output = (phase <= 5); break; //------__ case 3: dutyOutput = (phase <= 5); break; //------__
} }
} }
uint4 sample = (duty_output ? volume : (uint4)0); uint4 sample = (dutyOutput ? volume : (uint4)0);
if(enable == false) sample = 0; if(!enable) sample = 0;
output = sample; output = sample;
} }
auto APU::Square2::clock_length() -> void { auto APU::Square2::clockLength() -> void {
if(counter) { if(counter) {
if(++length == 0) enable = false; if(length && --length == 0) enable = false;
} }
} }
auto APU::Square2::clock_envelope() -> void { auto APU::Square2::clockEnvelope() -> void {
if(enable && envelope_frequency && --envelope_period == 0) { if(enable && envelopeFrequency && --envelopePeriod == 0) {
envelope_period = envelope_frequency; envelopePeriod = envelopeFrequency;
if(envelope_direction == 0 && volume > 0) volume--; if(envelopeDirection == 0 && volume > 0) volume--;
if(envelope_direction == 1 && volume < 15) volume++; if(envelopeDirection == 1 && volume < 15) volume++;
} }
} }
@@ -44,7 +44,7 @@ auto APU::Square2::read(uint16 addr) -> uint8 {
} }
if(addr == 0xff17) { //NR22 if(addr == 0xff17) { //NR22
return envelope_volume << 4 | envelope_direction << 3 | envelope_frequency; return envelopeVolume << 4 | envelopeDirection << 3 | envelopeFrequency;
} }
if(addr == 0xff18) { //NR23 if(addr == 0xff18) { //NR23
@@ -61,14 +61,14 @@ auto APU::Square2::read(uint16 addr) -> uint8 {
auto APU::Square2::write(uint16 addr, uint8 data) -> void { auto APU::Square2::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff16) { //NR21 if(addr == 0xff16) { //NR21
duty = data >> 6; duty = data >> 6;
length = (data & 0x3f); length = 64 - (data & 0x3f);
} }
if(addr == 0xff17) { //NR22 if(addr == 0xff17) { //NR22
envelope_volume = data >> 4; envelopeVolume = data >> 4;
envelope_direction = data & 0x08; envelopeDirection = data & 0x08;
envelope_frequency = data & 0x07; envelopeFrequency = data & 0x07;
if(dac_enable() == false) enable = false; if(!dacEnable()) enable = false;
} }
if(addr == 0xff18) { //NR23 if(addr == 0xff18) { //NR23
@@ -76,36 +76,46 @@ auto APU::Square2::write(uint16 addr, uint8 data) -> void {
} }
if(addr == 0xff19) { //NR24 if(addr == 0xff19) { //NR24
if((apu.phase & 1) && !counter && (data & 0x40)) {
if(length && --length == 0) enable = false;
}
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);
if(initialize) { if(initialize) {
enable = dac_enable(); enable = dacEnable();
period = 2 * (2048 - frequency); period = 2 * (2048 - frequency);
envelope_period = envelope_frequency; envelopePeriod = envelopeFrequency;
volume = envelope_volume; volume = envelopeVolume;
if(!length) {
length = 64;
if((apu.phase & 1) && counter) length--;
}
} }
} }
} }
auto APU::Square2::power() -> void { auto APU::Square2::power(bool initializeLength) -> void {
enable = 0; enable = 0;
duty = 0; duty = 0;
length = 0; envelopeVolume = 0;
envelope_volume = 0; envelopeDirection = 0;
envelope_direction = 0; envelopeFrequency = 0;
envelope_frequency = 0;
frequency = 0; frequency = 0;
counter = 0; counter = 0;
output = 0; output = 0;
duty_output = 0; dutyOutput = 0;
phase = 0; phase = 0;
period = 0; period = 0;
envelope_period = 0; envelopePeriod = 0;
volume = 0; volume = 0;
if(initializeLength) length = 64;
} }
auto APU::Square2::serialize(serializer& s) -> void { auto APU::Square2::serialize(serializer& s) -> void {
@@ -113,16 +123,16 @@ auto APU::Square2::serialize(serializer& s) -> void {
s.integer(duty); s.integer(duty);
s.integer(length); s.integer(length);
s.integer(envelope_volume); s.integer(envelopeVolume);
s.integer(envelope_direction); s.integer(envelopeDirection);
s.integer(envelope_frequency); s.integer(envelopeFrequency);
s.integer(frequency); s.integer(frequency);
s.integer(counter); s.integer(counter);
s.integer(output); s.integer(output);
s.integer(duty_output); s.integer(dutyOutput);
s.integer(phase); s.integer(phase);
s.integer(period); s.integer(period);
s.integer(envelope_period); s.integer(envelopePeriod);
s.integer(volume); s.integer(volume);
} }

View File

@@ -1,29 +1,29 @@
struct Square2 { struct Square2 {
auto dac_enable() const -> bool; auto dacEnable() const -> bool;
auto run() -> void; auto run() -> void;
auto clock_length() -> void; auto clockLength() -> void;
auto clock_envelope() -> void; auto clockEnvelope() -> void;
auto read(uint16 addr) -> uint8; auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void; auto write(uint16 addr, uint8 data) -> void;
auto power() -> void; auto power(bool initializeLength = true) -> void;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
bool enable; bool enable;
uint2 duty; uint2 duty;
uint6 length; uint length;
uint4 envelope_volume; uint4 envelopeVolume;
bool envelope_direction; bool envelopeDirection;
uint3 envelope_frequency; uint3 envelopeFrequency;
uint11 frequency; uint11 frequency;
bool counter; bool counter;
int16 output; int16 output;
bool duty_output; bool dutyOutput;
uint3 phase; uint3 phase;
uint period; uint period;
uint3 envelope_period; uint3 envelopePeriod;
uint4 volume; uint4 volume;
}; };

View File

@@ -1,29 +1,29 @@
auto APU::Wave::get_pattern(uint5 offset) const -> uint4 { auto APU::Wave::getPattern(uint5 offset) const -> uint4 {
return pattern[offset >> 1] >> (offset & 1 ? 0 : 4); return pattern[offset >> 1] >> (offset & 1 ? 0 : 4);
} }
auto APU::Wave::run() -> void { auto APU::Wave::run() -> void {
if(period && --period == 0) { if(period && --period == 0) {
period = 1 * (2048 - frequency); period = 1 * (2048 - frequency);
pattern_sample = get_pattern(++pattern_offset); patternSample = getPattern(++patternOffset);
} }
static const uint shift[] = {4, 0, 1, 2}; //0%, 100%, 50%, 25% static const uint shift[] = {4, 0, 1, 2}; //0%, 100%, 50%, 25%
uint4 sample = pattern_sample >> shift[volume]; uint4 sample = patternSample >> shift[volume];
if(enable == false) sample = 0; if(enable == false) sample = 0;
output = sample; output = sample;
} }
auto APU::Wave::clock_length() -> void { auto APU::Wave::clockLength() -> void {
if(counter) { if(counter) {
if(++length == 0) enable = false; if(length && --length == 0) enable = false;
} }
} }
auto APU::Wave::read(uint16 addr) -> uint8 { auto APU::Wave::read(uint16 addr) -> uint8 {
if(addr == 0xff1a) { //NR30 if(addr == 0xff1a) { //NR30
return dac_enable << 7 | 0x7f; return dacEnable << 7 | 0x7f;
} }
if(addr == 0xff1b) { //NR31 if(addr == 0xff1b) { //NR31
@@ -51,12 +51,12 @@ auto APU::Wave::read(uint16 addr) -> uint8 {
auto APU::Wave::write(uint16 addr, uint8 data) -> void { auto APU::Wave::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff1a) { //NR30 if(addr == 0xff1a) { //NR30
dac_enable = data & 0x80; dacEnable = data & 0x80;
if(dac_enable == false) enable = false; if(!dacEnable) enable = false;
} }
if(addr == 0xff1b) { //NR31 if(addr == 0xff1b) { //NR31
length = data; length = 256 - data;
} }
if(addr == 0xff1c) { //NR32 if(addr == 0xff1c) { //NR32
@@ -68,14 +68,23 @@ auto APU::Wave::write(uint16 addr, uint8 data) -> void {
} }
if(addr == 0xff1e) { //NR34 if(addr == 0xff1e) { //NR34
if((apu.phase & 1) && !counter && (data & 0x40)) {
if(length && --length == 0) enable = false;
}
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);
if(initialize) { if(initialize) {
enable = dac_enable; enable = dacEnable;
period = 1 * (2048 - frequency); period = 1 * (2048 - frequency);
pattern_offset = 0; patternOffset = 0;
if(!length) {
length = 256;
if((apu.phase & 1) && counter) length--;
}
} }
} }
@@ -84,25 +93,26 @@ auto APU::Wave::write(uint16 addr, uint8 data) -> void {
} }
} }
auto APU::Wave::power() -> void { auto APU::Wave::power(bool initializeLength) -> void {
enable = 0; enable = 0;
dac_enable = 0; dacEnable = 0;
volume = 0; volume = 0;
frequency = 0; frequency = 0;
counter = 0; counter = 0;
output = 0; output = 0;
length = 0;
period = 0; period = 0;
pattern_offset = 0; patternOffset = 0;
pattern_sample = 0; patternSample = 0;
if(initializeLength) length = 256;
} }
auto APU::Wave::serialize(serializer& s) -> void { auto APU::Wave::serialize(serializer& s) -> void {
s.integer(enable); s.integer(enable);
s.integer(dac_enable); s.integer(dacEnable);
s.integer(volume); s.integer(volume);
s.integer(frequency); s.integer(frequency);
s.integer(counter); s.integer(counter);
@@ -111,6 +121,6 @@ auto APU::Wave::serialize(serializer& s) -> void {
s.integer(output); s.integer(output);
s.integer(length); s.integer(length);
s.integer(period); s.integer(period);
s.integer(pattern_offset); s.integer(patternOffset);
s.integer(pattern_sample); s.integer(patternSample);
} }

View File

@@ -1,25 +1,25 @@
struct Wave { struct Wave {
auto get_pattern(uint5 offset) const -> uint4; auto getPattern(uint5 offset) const -> uint4;
auto run() -> void; auto run() -> void;
auto clock_length() -> void; auto clockLength() -> void;
auto read(uint16 addr) -> uint8; auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void; auto write(uint16 addr, uint8 data) -> void;
auto power() -> void; auto power(bool initializeLength = true) -> void;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
bool enable; bool enable;
bool dac_enable; bool dacEnable;
uint2 volume; uint2 volume;
uint11 frequency; uint11 frequency;
bool counter; bool counter;
uint8 pattern[16]; uint8 pattern[16];
int16 output; int16 output;
uint8 length; uint length;
uint period; uint period;
uint5 pattern_offset; uint5 patternOffset;
uint4 pattern_sample; uint4 patternSample;
}; };

View File

@@ -77,15 +77,27 @@ auto PPU::mmio_read(uint16 addr) -> uint8 {
return status.wx; return status.wx;
} }
if(addr == 0xff69) { //BGPD if(addr == 0xff4f) { //VBK
return status.vram_bank;
}
if(addr == 0xff68) { //BGPI
return status.bgpi_increment << 7 | status.bgpi;
}
if(addr == 0xff69) { //BGPD
return bgpd[status.bgpi]; return bgpd[status.bgpi];
} }
if(addr == 0xff6a) { //OBPI
return status.obpi_increment << 7 | status.obpi;
}
if(addr == 0xff6b) { //OBPD if(addr == 0xff6b) { //OBPD
return obpd[status.obpi]; return obpd[status.obpi];
} }
return 0xff; return 0xff; //should never occur
} }
auto PPU::mmio_write(uint16 addr, uint8 data) -> void { auto PPU::mmio_write(uint16 addr, uint8 data) -> void {
@@ -94,7 +106,13 @@ auto PPU::mmio_write(uint16 addr, uint8 data) -> void {
if(addr == 0xff40) { //LCDC if(addr == 0xff40) { //LCDC
if(status.display_enable == false && (data & 0x80)) { if(status.display_enable == false && (data & 0x80)) {
status.lx = 0; //unverified behavior; fixes Super Mario Land 2 - Tree Zone status.ly = 0;
status.lx = 0;
//restart cothread to begin new frame
auto clock = this->clock;
create(Main, 4 * 1024 * 1024);
this->clock = clock;
} }
status.display_enable = data & 0x80; status.display_enable = data & 0x80;

View File

@@ -24,23 +24,49 @@ auto PPU::main() -> void {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
} }
status.lx = 0;
interface->lcdScanline(); //Super Game Boy notification interface->lcdScanline(); //Super Game Boy notification
if(status.display_enable && status.ly < 144) { if(status.display_enable) {
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat); //LYC of zero triggers on LY==153
add_clocks(92); if((status.lyc && status.ly == status.lyc) || (!status.lyc && status.ly == 153)) {
for(auto n : range(160)) { if(status.interrupt_lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
system.cgb() ? cgb_run() : dmg_run(); }
add_clocks(1);
if(status.ly <= 143) {
scanline();
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.ly == 144) {
if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
else if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat); //hardware quirk
cpu.interrupt_raise(CPU::Interrupt::Vblank);
} }
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
cpu.hblank();
add_clocks(204);
} else {
add_clocks(456);
} }
scanline(); add_clocks(92);
if(status.ly <= 143) {
for(auto n : range(160)) {
if(status.display_enable) run();
add_clocks(1);
}
if(status.display_enable) {
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
cpu.hblank();
}
} else {
add_clocks(160);
}
add_clocks(204);
if(++status.ly == 154) {
status.ly = 0;
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
} }
} }
@@ -54,29 +80,6 @@ auto PPU::add_clocks(uint clocks) -> void {
} }
} }
auto PPU::scanline() -> void {
status.lx = 0;
if(++status.ly == 154) frame();
if(status.ly < 144) {
system.cgb() ? cgb_scanline() : dmg_scanline();
}
if(status.display_enable && status.interrupt_lyc == true) {
if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.display_enable && status.ly == 144) {
cpu.interrupt_raise(CPU::Interrupt::Vblank);
if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
}
auto PPU::frame() -> void {
status.ly = 0;
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
auto PPU::hflip(uint data) const -> uint { auto PPU::hflip(uint data) const -> uint {
return ((data & 0x8080) >> 7) | ((data & 0x4040) >> 5) return ((data & 0x8080) >> 7) | ((data & 0x4040) >> 5)
| ((data & 0x2020) >> 3) | ((data & 0x1010) >> 1) | ((data & 0x2020) >> 3) | ((data & 0x1010) >> 1)
@@ -87,6 +90,14 @@ auto PPU::hflip(uint data) const -> uint {
auto PPU::power() -> void { auto PPU::power() -> void {
create(Main, 4 * 1024 * 1024); create(Main, 4 * 1024 * 1024);
if(system.cgb()) {
scanline = {&PPU::cgb_scanline, this};
run = {&PPU::cgb_run, this};
} else {
scanline = {&PPU::dmg_scanline, this};
run = {&PPU::dmg_run, this};
}
for(uint n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM for(uint n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM
for(uint n = 0xfe00; n <= 0xfe9f; n++) bus.mmio[n] = this; //OAM for(uint n = 0xfe00; n <= 0xfe9f; n++) bus.mmio[n] = this; //OAM

View File

@@ -2,8 +2,6 @@ struct PPU : Thread, MMIO {
static auto Main() -> void; static auto Main() -> void;
auto main() -> void; auto main() -> void;
auto add_clocks(uint clocks) -> void; auto add_clocks(uint clocks) -> void;
auto scanline() -> void;
auto frame() -> void;
auto hflip(uint data) const -> uint; auto hflip(uint data) const -> uint;
@@ -39,6 +37,9 @@ struct PPU : Thread, MMIO {
uint8 bgpd[64]; uint8 bgpd[64];
uint8 obpd[64]; uint8 obpd[64];
function<auto () -> void> scanline;
function<auto () -> void> run;
struct Status { struct Status {
uint lx; uint lx;