diff --git a/higan/data/higan.plist b/higan/data/higan.plist
index 44ee215b..6d59f131 100644
--- a/higan/data/higan.plist
+++ b/higan/data/higan.plist
@@ -2,11 +2,17 @@
+ CFBundleIdentifier
+ org.byuu.higan
CFBundleDisplayName
higan
CFBundleExecutable
higan
CFBundleIconFile
higan.icns
+ NSHighResolutionCapable
+
+ NSSupportsAutomaticGraphicsSwitching
+
diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp
index 5e949d23..f023a238 100644
--- a/higan/emulator/emulator.hpp
+++ b/higan/emulator/emulator.hpp
@@ -6,7 +6,7 @@ using namespace nall;
namespace Emulator {
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 License = "GPLv3";
static const string Website = "http://byuu.org/";
diff --git a/higan/fc/cartridge/board/konami-vrc2.cpp b/higan/fc/cartridge/board/konami-vrc2.cpp
index 01011890..d033d2b5 100644
--- a/higan/fc/cartridge/board/konami-vrc2.cpp
+++ b/higan/fc/cartridge/board/konami-vrc2.cpp
@@ -1,7 +1,7 @@
struct KonamiVRC2 : Board {
KonamiVRC2(Markup::Node& document) : Board(document), vrc2(*this) {
- settings.pinout.a0 = 1 << document["cartridge/chip/pinout/a0"].natural();
- settings.pinout.a1 = 1 << document["cartridge/chip/pinout/a1"].natural();
+ settings.pinout.a0 = 1 << document["board/chip/pinout/a0"].natural();
+ settings.pinout.a1 = 1 << document["board/chip/pinout/a1"].natural();
}
auto prg_read(uint addr) -> uint8 {
diff --git a/higan/fc/cartridge/board/konami-vrc3.cpp b/higan/fc/cartridge/board/konami-vrc3.cpp
index baa00525..5a6cd5da 100644
--- a/higan/fc/cartridge/board/konami-vrc3.cpp
+++ b/higan/fc/cartridge/board/konami-vrc3.cpp
@@ -1,6 +1,6 @@
struct KonamiVRC3 : Board {
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 {
diff --git a/higan/fc/cartridge/board/konami-vrc4.cpp b/higan/fc/cartridge/board/konami-vrc4.cpp
index ca7b0c14..13041ee6 100644
--- a/higan/fc/cartridge/board/konami-vrc4.cpp
+++ b/higan/fc/cartridge/board/konami-vrc4.cpp
@@ -1,7 +1,7 @@
struct KonamiVRC4 : Board {
KonamiVRC4(Markup::Node& document) : Board(document), vrc4(*this) {
- settings.pinout.a0 = 1 << document["cartridge/chip/pinout/a0"].natural();
- settings.pinout.a1 = 1 << document["cartridge/chip/pinout/a1"].natural();
+ settings.pinout.a0 = 1 << document["board/chip/pinout/a0"].natural();
+ settings.pinout.a1 = 1 << document["board/chip/pinout/a1"].natural();
}
auto main() -> void {
diff --git a/higan/fc/cartridge/board/nes-bnrom.cpp b/higan/fc/cartridge/board/nes-bnrom.cpp
index 48a2e657..c39c60c5 100644
--- a/higan/fc/cartridge/board/nes-bnrom.cpp
+++ b/higan/fc/cartridge/board/nes-bnrom.cpp
@@ -2,7 +2,7 @@
struct NES_BNROM : Board {
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 {
diff --git a/higan/fc/cartridge/board/nes-cnrom.cpp b/higan/fc/cartridge/board/nes-cnrom.cpp
index 3f1769ba..d0028f5d 100644
--- a/higan/fc/cartridge/board/nes-cnrom.cpp
+++ b/higan/fc/cartridge/board/nes-cnrom.cpp
@@ -2,7 +2,7 @@
struct NES_CNROM : Board {
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 {
diff --git a/higan/fc/cartridge/board/nes-gxrom.cpp b/higan/fc/cartridge/board/nes-gxrom.cpp
index 4603f4b1..d5d58aac 100644
--- a/higan/fc/cartridge/board/nes-gxrom.cpp
+++ b/higan/fc/cartridge/board/nes-gxrom.cpp
@@ -3,7 +3,7 @@
struct NES_GxROM : Board {
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 {
diff --git a/higan/fc/cartridge/board/nes-nrom.cpp b/higan/fc/cartridge/board/nes-nrom.cpp
index 462cf775..2969a5b9 100644
--- a/higan/fc/cartridge/board/nes-nrom.cpp
+++ b/higan/fc/cartridge/board/nes-nrom.cpp
@@ -3,7 +3,7 @@
struct NES_NROM : Board {
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 {
diff --git a/higan/fc/cartridge/board/nes-uxrom.cpp b/higan/fc/cartridge/board/nes-uxrom.cpp
index f5b55b29..305672f9 100644
--- a/higan/fc/cartridge/board/nes-uxrom.cpp
+++ b/higan/fc/cartridge/board/nes-uxrom.cpp
@@ -3,7 +3,7 @@
struct NES_UxROM : Board {
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 {
diff --git a/higan/gb/apu/apu.cpp b/higan/gb/apu/apu.cpp
index 21353885..2c8df0e6 100644
--- a/higan/gb/apu/apu.cpp
+++ b/higan/gb/apu/apu.cpp
@@ -20,24 +20,24 @@ auto APU::main() -> void {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
- if(sequencer_base == 0) { //512hz
- if(sequencer_step == 0 || sequencer_step == 2 || sequencer_step == 4 || sequencer_step == 6) { //256hz
- square1.clock_length();
- square2.clock_length();
- wave.clock_length();
- noise.clock_length();
+ if(stage == 0) { //512hz
+ if(phase == 0 || phase == 2 || phase == 4 || phase == 6) { //256hz
+ square1.clockLength();
+ square2.clockLength();
+ wave.clockLength();
+ noise.clockLength();
}
- if(sequencer_step == 2 || sequencer_step == 6) { //128hz
- square1.clock_sweep();
+ if(phase == 2 || phase == 6) { //128hz
+ square1.clockSweep();
}
- if(sequencer_step == 7) { //64hz
- square1.clock_envelope();
- square2.clock_envelope();
- noise.clock_envelope();
+ if(phase == 7) { //64hz
+ square1.clockEnvelope();
+ square2.clockEnvelope();
+ noise.clockEnvelope();
}
- sequencer_step++;
+ phase++;
}
- sequencer_base++;
+ stage++;
square1.run();
square2.run();
@@ -45,9 +45,9 @@ auto APU::main() -> void {
noise.run();
master.run();
- hipass(master.center, master.center_bias);
- hipass(master.left, master.left_bias);
- hipass(master.right, master.right_bias);
+ hipass(master.center, master.centerBias);
+ hipass(master.left, master.leftBias);
+ hipass(master.right, master.rightBias);
interface->audioSample(master.left, master.right);
@@ -65,8 +65,8 @@ auto APU::power() -> void {
create(Main, 2 * 1024 * 1024);
for(uint n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
- sequencer_base = 0;
- sequencer_step = 0;
+ stage = 0;
+ phase = 0;
square1.power();
square2.power();
diff --git a/higan/gb/apu/apu.hpp b/higan/gb/apu/apu.hpp
index 51d92e03..f30a6d15 100644
--- a/higan/gb/apu/apu.hpp
+++ b/higan/gb/apu/apu.hpp
@@ -15,8 +15,8 @@ struct APU : Thread, MMIO {
#include "noise/noise.hpp"
#include "master/master.hpp"
- uint12 sequencer_base;
- uint3 sequencer_step;
+ uint12 stage;
+ uint3 phase;
Square1 square1;
Square2 square2;
diff --git a/higan/gb/apu/master/master.cpp b/higan/gb/apu/master/master.cpp
index e47d4132..9157e86f 100644
--- a/higan/gb/apu/master/master.cpp
+++ b/higan/gb/apu/master/master.cpp
@@ -4,7 +4,7 @@ auto APU::Master::run() -> void {
left = 0;
right = 0;
- center_bias = left_bias = right_bias = 0;
+ centerBias = leftBias = rightBias = 0;
return;
}
@@ -16,21 +16,21 @@ auto APU::Master::run() -> void {
center = (sample * 512) - 16384;
sample = 0;
- if(channel1_left_enable) sample += apu.square1.output;
- if(channel2_left_enable) sample += apu.square2.output;
- if(channel3_left_enable) sample += apu.wave.output;
- if(channel4_left_enable) sample += apu.noise.output;
+ if(square1.leftEnable) sample += apu.square1.output;
+ if(square2.leftEnable) sample += apu.square2.output;
+ if( wave.leftEnable) sample += apu.wave.output;
+ if( noise.leftEnable) sample += apu.noise.output;
sample = (sample * 512) - 16384;
- sample = (sample * (left_volume + 1)) / 8;
+ sample = (sample * (leftVolume + 1)) / 8;
left = sample;
sample = 0;
- if(channel1_right_enable) sample += apu.square1.output;
- if(channel2_right_enable) sample += apu.square2.output;
- if(channel3_right_enable) sample += apu.wave.output;
- if(channel4_right_enable) sample += apu.noise.output;
+ if(square1.rightEnable) sample += apu.square1.output;
+ if(square2.rightEnable) sample += apu.square2.output;
+ if( wave.rightEnable) sample += apu.wave.output;
+ if( noise.rightEnable) sample += apu.noise.output;
sample = (sample * 512) - 16384;
- sample = (sample * (right_volume + 1)) / 8;
+ sample = (sample * (rightVolume + 1)) / 8;
right = sample;
//reduce audio volume
@@ -41,18 +41,18 @@ auto APU::Master::run() -> void {
auto APU::Master::read(uint16 addr) -> uint8 {
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
- return channel4_left_enable << 7
- | channel3_left_enable << 6
- | channel2_left_enable << 5
- | channel1_left_enable << 4
- | channel4_right_enable << 3
- | channel3_right_enable << 2
- | channel2_right_enable << 1
- | channel1_right_enable << 0;
+ return noise.leftEnable << 7
+ | wave.leftEnable << 6
+ | square2.leftEnable << 5
+ | square1.leftEnable << 4
+ | noise.rightEnable << 3
+ | wave.rightEnable << 2
+ | square2.rightEnable << 1
+ | square1.rightEnable << 0;
}
if(addr == 0xff26) { //NR52
@@ -68,79 +68,80 @@ auto APU::Master::read(uint16 addr) -> uint8 {
auto APU::Master::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff24) { //NR50
- left_in_enable = data & 0x80;
- left_volume = (data >> 4) & 7;
- right_in_enable = data & 0x08;
- right_volume = (data >> 0) & 7;
+ leftEnable = data & 0x80;
+ leftVolume = (data >> 4) & 7;
+ rightEnable = data & 0x08;
+ rightVolume = (data >> 0) & 7;
}
if(addr == 0xff25) { //NR51
- 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;
+ noise.leftEnable = data & 0x80;
+ wave.leftEnable = data & 0x40;
+ square2.leftEnable = data & 0x20;
+ square1.leftEnable = data & 0x10;
+ noise.rightEnable = data & 0x08;
+ wave.rightEnable = data & 0x04;
+ square2.rightEnable = data & 0x02;
+ square1.rightEnable = data & 0x01;
}
if(addr == 0xff26) { //NR52
enable = data & 0x80;
if(!enable) {
- apu.square1.power();
- apu.square2.power();
- apu.wave.power();
- apu.noise.power();
+ //power(bool) resets length counters when true (eg for CGB only)
+ apu.square1.power(system.cgb());
+ apu.square2.power(system.cgb());
+ apu.wave.power(system.cgb());
+ apu.noise.power(system.cgb());
power();
}
}
}
auto APU::Master::power() -> void {
- left_in_enable = 0;
- left_volume = 0;
- right_in_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;
+ leftEnable = 0;
+ leftVolume = 0;
+ rightEnable = 0;
+ rightVolume = 0;
+ noise.leftEnable = 0;
+ wave.leftEnable = 0;
+ square2.leftEnable = 0;
+ square1.leftEnable = 0;
+ noise.rightEnable = 0;
+ wave.rightEnable = 0;
+ square2.rightEnable = 0;
+ square1.rightEnable = 0;
enable = 0;
center = 0;
left = 0;
right = 0;
- center_bias = 0;
- left_bias = 0;
- right_bias = 0;
+ centerBias = 0;
+ leftBias = 0;
+ rightBias = 0;
}
auto APU::Master::serialize(serializer& s) -> void {
- s.integer(left_in_enable);
- s.integer(left_volume);
- s.integer(right_in_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(leftEnable);
+ s.integer(leftVolume);
+ s.integer(rightEnable);
+ s.integer(rightVolume);
+ s.integer(noise.leftEnable);
+ s.integer(wave.leftEnable);
+ s.integer(square2.leftEnable);
+ s.integer(square1.leftEnable);
+ s.integer(noise.rightEnable);
+ s.integer(wave.rightEnable);
+ s.integer(square2.rightEnable);
+ s.integer(square1.rightEnable);
s.integer(enable);
s.integer(center);
s.integer(left);
s.integer(right);
- s.integer(center_bias);
- s.integer(left_bias);
- s.integer(right_bias);
+ s.integer(centerBias);
+ s.integer(leftBias);
+ s.integer(rightBias);
}
diff --git a/higan/gb/apu/master/master.hpp b/higan/gb/apu/master/master.hpp
index 0cdfe8db..0c861610 100644
--- a/higan/gb/apu/master/master.hpp
+++ b/higan/gb/apu/master/master.hpp
@@ -6,25 +6,23 @@ struct Master {
auto serialize(serializer&) -> void;
- bool left_in_enable;
- uint3 left_volume;
- bool right_in_enable;
- uint3 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 leftEnable;
+ uint3 leftVolume;
+ bool rightEnable;
+ uint3 rightVolume;
+
+ struct Channel {
+ bool leftEnable;
+ bool rightEnable;
+ } square1, square2, wave, noise;
+
bool enable;
int16 center;
int16 left;
int16 right;
- int64 center_bias;
- int64 left_bias;
- int64 right_bias;
+ int64 centerBias;
+ int64 leftBias;
+ int64 rightBias;
};
diff --git a/higan/gb/apu/noise/noise.cpp b/higan/gb/apu/noise/noise.cpp
index 1796c92b..3042cbd4 100644
--- a/higan/gb/apu/noise/noise.cpp
+++ b/higan/gb/apu/noise/noise.cpp
@@ -1,38 +1,38 @@
-auto APU::Noise::dac_enable() const -> bool {
- return (envelope_volume || envelope_direction);
+auto APU::Noise::dacEnable() const -> bool {
+ 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};
return table[divisor] << frequency;
}
auto APU::Noise::run() -> void {
if(period && --period == 0) {
- period = get_period();
+ period = getPeriod();
if(frequency < 14) {
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;
- if(enable == false) sample = 0;
+ if(!enable) sample = 0;
output = sample;
}
-auto APU::Noise::clock_length() -> void {
+auto APU::Noise::clockLength() -> void {
if(counter) {
- if(++length == 0) enable = false;
+ if(length && --length == 0) enable = false;
}
}
-auto APU::Noise::clock_envelope() -> void {
- if(enable && envelope_frequency && --envelope_period == 0) {
- envelope_period = envelope_frequency;
- if(envelope_direction == 0 && volume > 0) volume--;
- if(envelope_direction == 1 && volume < 15) volume++;
+auto APU::Noise::clockEnvelope() -> void {
+ if(enable && envelopeFrequency && --envelopePeriod == 0) {
+ envelopePeriod = envelopeFrequency;
+ if(envelopeDirection == 0 && volume > 0) volume--;
+ if(envelopeDirection == 1 && volume < 15) volume++;
}
}
@@ -46,11 +46,11 @@ auto APU::Noise::read(uint16 addr) -> uint8 {
}
if(addr == 0xff21) { //NR42
- return envelope_volume << 4 | envelope_direction << 3 | envelope_frequency;
+ return envelopeVolume << 4 | envelopeDirection << 3 | envelopeFrequency;
}
if(addr == 0xff22) { //NR43
- return frequency << 4 | narrow_lfsr << 3 | divisor;
+ return frequency << 4 | narrow << 3 | divisor;
}
if(addr == 0xff23) { //NR44
@@ -62,69 +62,79 @@ auto APU::Noise::read(uint16 addr) -> uint8 {
auto APU::Noise::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff20) { //NR41
- length = data & 0x3f;
+ length = 64 - (data & 0x3f);
}
if(addr == 0xff21) { //NR42
- envelope_volume = data >> 4;
- envelope_direction = data & 0x08;
- envelope_frequency = data & 0x07;
- if(dac_enable() == false) enable = false;
+ envelopeVolume = data >> 4;
+ envelopeDirection = data & 0x08;
+ envelopeFrequency = data & 0x07;
+ if(!dacEnable()) enable = false;
}
if(addr == 0xff22) { //NR43
frequency = data >> 4;
- narrow_lfsr = data & 0x08;
+ narrow = data & 0x08;
divisor = data & 0x07;
- period = get_period();
+ period = getPeriod();
}
if(addr == 0xff23) { //NR44
+ if((apu.phase & 1) && !counter && (data & 0x40)) {
+ if(length && --length == 0) enable = false;
+ }
+
bool initialize = data & 0x80;
counter = data & 0x40;
if(initialize) {
- enable = dac_enable();
+ enable = dacEnable();
lfsr = -1;
- envelope_period = envelope_frequency;
- volume = envelope_volume;
+ envelopePeriod = envelopeFrequency;
+ 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;
- envelope_volume = 0;
- envelope_direction = 0;
- envelope_frequency = 0;
+ envelopeVolume = 0;
+ envelopeDirection = 0;
+ envelopeFrequency = 0;
frequency = 0;
- narrow_lfsr = 0;
+ narrow = 0;
divisor = 0;
counter = 0;
output = 0;
- length = 0;
- envelope_period = 0;
+ envelopePeriod = 0;
volume = 0;
period = 0;
lfsr = 0;
+
+ if(initializeLength) length = 64;
}
auto APU::Noise::serialize(serializer& s) -> void {
s.integer(enable);
- s.integer(envelope_volume);
- s.integer(envelope_direction);
- s.integer(envelope_frequency);
+ s.integer(envelopeVolume);
+ s.integer(envelopeDirection);
+ s.integer(envelopeFrequency);
s.integer(frequency);
- s.integer(narrow_lfsr);
+ s.integer(narrow);
s.integer(divisor);
s.integer(counter);
s.integer(output);
s.integer(length);
- s.integer(envelope_period);
+ s.integer(envelopePeriod);
s.integer(volume);
s.integer(period);
s.integer(lfsr);
diff --git a/higan/gb/apu/noise/noise.hpp b/higan/gb/apu/noise/noise.hpp
index 095cedf7..c3d8519c 100644
--- a/higan/gb/apu/noise/noise.hpp
+++ b/higan/gb/apu/noise/noise.hpp
@@ -1,29 +1,29 @@
struct Noise {
- auto dac_enable() const -> bool;
- auto get_period() const -> uint;
+ auto dacEnable() const -> bool;
+ auto getPeriod() const -> uint;
auto run() -> void;
- auto clock_length() -> void;
- auto clock_envelope() -> void;
+ auto clockLength() -> void;
+ auto clockEnvelope() -> void;
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
- auto power() -> void;
+ auto power(bool initializeLength = true) -> void;
auto serialize(serializer&) -> void;
bool enable;
- uint4 envelope_volume;
- bool envelope_direction;
- uint3 envelope_frequency;
+ uint4 envelopeVolume;
+ bool envelopeDirection;
+ uint3 envelopeFrequency;
uint4 frequency;
- bool narrow_lfsr;
+ bool narrow;
uint3 divisor;
bool counter;
int16 output;
- uint6 length;
- uint3 envelope_period;
+ uint length;
+ uint3 envelopePeriod;
uint4 volume;
uint period;
uint15 lfsr;
diff --git a/higan/gb/apu/serialization.cpp b/higan/gb/apu/serialization.cpp
index 58becd79..cfb2327d 100644
--- a/higan/gb/apu/serialization.cpp
+++ b/higan/gb/apu/serialization.cpp
@@ -1,8 +1,8 @@
auto APU::serialize(serializer& s) -> void {
Thread::serialize(s);
- s.integer(sequencer_base);
- s.integer(sequencer_step);
+ s.integer(stage);
+ s.integer(phase);
square1.serialize(s);
square2.serialize(s);
diff --git a/higan/gb/apu/square1/square1.cpp b/higan/gb/apu/square1/square1.cpp
index 70f7709f..adaacdc7 100644
--- a/higan/gb/apu/square1/square1.cpp
+++ b/higan/gb/apu/square1/square1.cpp
@@ -1,5 +1,5 @@
-auto APU::Square1::dac_enable() const -> bool {
- return (envelope_volume || envelope_direction);
+auto APU::Square1::dacEnable() const -> bool {
+ return (envelopeVolume || envelopeDirection);
}
auto APU::Square1::run() -> void {
@@ -7,60 +7,62 @@ auto APU::Square1::run() -> void {
period = 2 * (2048 - frequency);
phase++;
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; //------__
+ case 0: dutyOutput = (phase == 6); break; //______-_
+ case 1: dutyOutput = (phase >= 6); break; //______--
+ case 2: dutyOutput = (phase >= 4); break; //____----
+ case 3: dutyOutput = (phase <= 5); break; //------__
}
}
- uint4 sample = (duty_output ? volume : (uint4)0);
- if(enable == false) sample = 0;
+ uint4 sample = (dutyOutput ? volume : (uint4)0);
+ if(!enable) sample = 0;
output = sample;
}
auto APU::Square1::sweep(bool update) -> void {
- if(sweep_enable == false) return;
+ if(!sweepEnable) return;
- sweep_negate = sweep_direction;
- uint delta = frequency_shadow >> sweep_shift;
- int freq = frequency_shadow + (sweep_negate ? -delta : delta);
+ sweepNegate = sweepDirection;
+ uint delta = frequencyShadow >> sweepShift;
+ int freq = frequencyShadow + (sweepNegate ? -delta : delta);
if(freq > 2047) {
enable = false;
- } else if(sweep_shift && update) {
- frequency_shadow = freq;
+ } else if(sweepShift && update) {
+ frequencyShadow = freq;
frequency = freq & 2047;
period = 2 * (2048 - frequency);
}
}
-auto APU::Square1::clock_length() -> void {
+auto APU::Square1::clockLength() -> void {
if(counter) {
- if(++length == 0) enable = false;
+ if(length && --length == 0) enable = false;
}
}
-auto APU::Square1::clock_sweep() -> void {
- if(enable && sweep_frequency && --sweep_period == 0) {
- sweep_period = sweep_frequency;
- sweep(1);
- sweep(0);
+auto APU::Square1::clockSweep() -> void {
+ if(--sweepPeriod == 0) {
+ sweepPeriod = sweepFrequency ? (uint)sweepFrequency : 8;
+ if(sweepEnable && sweepFrequency) {
+ sweep(1);
+ sweep(0);
+ }
}
}
-auto APU::Square1::clock_envelope() -> void {
- if(enable && envelope_frequency && --envelope_period == 0) {
- envelope_period = envelope_frequency;
- if(envelope_direction == 0 && volume > 0) volume--;
- if(envelope_direction == 1 && volume < 15) volume++;
+auto APU::Square1::clockEnvelope() -> void {
+ if(enable && envelopeFrequency && --envelopePeriod == 0) {
+ envelopePeriod = envelopeFrequency;
+ if(envelopeDirection == 0 && volume > 0) volume--;
+ if(envelopeDirection == 1 && volume < 15) volume++;
}
}
auto APU::Square1::read(uint16 addr) -> uint8 {
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
@@ -68,7 +70,7 @@ auto APU::Square1::read(uint16 addr) -> uint8 {
}
if(addr == 0xff12) { //NR12
- return envelope_volume << 4 | envelope_direction << 3 | envelope_frequency;
+ return envelopeVolume << 4 | envelopeDirection << 3 | envelopeFrequency;
}
if(addr == 0xff13) { //NR13
@@ -84,22 +86,22 @@ auto APU::Square1::read(uint16 addr) -> uint8 {
auto APU::Square1::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff10) { //NR10
- if(sweep_negate && sweep_direction && !(data & 0x08)) enable = false;
- sweep_frequency = (data >> 4) & 7;
- sweep_direction = data & 0x08;
- sweep_shift = data & 0x07;
+ if(sweepEnable && sweepNegate && !(data & 0x08)) enable = false;
+ sweepFrequency = (data >> 4) & 7;
+ sweepDirection = data & 0x08;
+ sweepShift = data & 0x07;
}
if(addr == 0xff11) { //NR11
duty = data >> 6;
- length = data & 0x3f;
+ length = 64 - (data & 0x3f);
}
if(addr == 0xff12) { //NR12
- envelope_volume = data >> 4;
- envelope_direction = data & 0x08;
- envelope_frequency = data & 0x07;
- if(dac_enable() == false) enable = false;
+ envelopeVolume = data >> 4;
+ envelopeDirection = data & 0x08;
+ envelopeFrequency = data & 0x07;
+ if(!dacEnable()) enable = false;
}
if(addr == 0xff13) { //NR13
@@ -107,72 +109,83 @@ auto APU::Square1::write(uint16 addr, uint8 data) -> void {
}
if(addr == 0xff14) { //NR14
+ if((apu.phase & 1) && !counter && (data & 0x40)) {
+ if(length && --length == 0) enable = false;
+ }
+
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize) {
- enable = dac_enable();
+ enable = dacEnable();
period = 2 * (2048 - frequency);
- envelope_period = envelope_frequency;
- volume = envelope_volume;
- frequency_shadow = frequency;
- sweep_period = sweep_frequency;
- sweep_enable = sweep_period || sweep_shift;
- sweep_negate = false;
- if(sweep_shift) sweep(0);
+ envelopePeriod = envelopeFrequency;
+ volume = envelopeVolume;
+
+ if(!length) {
+ length = 64;
+ if((apu.phase & 1) && counter) length--;
+ }
+
+ 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;
- sweep_frequency = 0;
- sweep_direction = 0;
- sweep_shift = 0;
- sweep_negate = 0;
+ sweepFrequency = 0;
+ sweepDirection = 0;
+ sweepShift = 0;
+ sweepNegate = 0;
duty = 0;
- length = 0;
- envelope_volume = 0;
- envelope_direction = 0;
- envelope_frequency = 0;
+ envelopeVolume = 0;
+ envelopeDirection = 0;
+ envelopeFrequency = 0;
frequency = 0;
counter = 0;
output = 0;
- duty_output = 0;
+ dutyOutput = 0;
phase = 0;
period = 0;
- envelope_period = 0;
- sweep_period = 0;
- frequency_shadow = 0;
- sweep_enable = 0;
+ envelopePeriod = 0;
+ sweepPeriod = 0;
+ frequencyShadow = 0;
+ sweepEnable = 0;
volume = 0;
+
+ if(initializeLength) length = 64;
}
auto APU::Square1::serialize(serializer& s) -> void {
s.integer(enable);
- s.integer(sweep_frequency);
- s.integer(sweep_direction);
- s.integer(sweep_shift);
- s.integer(sweep_negate);
+ s.integer(sweepFrequency);
+ s.integer(sweepDirection);
+ s.integer(sweepShift);
+ s.integer(sweepNegate);
s.integer(duty);
s.integer(length);
- s.integer(envelope_volume);
- s.integer(envelope_direction);
- s.integer(envelope_frequency);
+ s.integer(envelopeVolume);
+ s.integer(envelopeDirection);
+ s.integer(envelopeFrequency);
s.integer(frequency);
s.integer(counter);
s.integer(output);
- s.integer(duty_output);
+ s.integer(dutyOutput);
s.integer(phase);
s.integer(period);
- s.integer(envelope_period);
- s.integer(sweep_period);
- s.integer(frequency_shadow);
- s.integer(sweep_enable);
+ s.integer(envelopePeriod);
+ s.integer(sweepPeriod);
+ s.integer(frequencyShadow);
+ s.integer(sweepEnable);
s.integer(volume);
}
diff --git a/higan/gb/apu/square1/square1.hpp b/higan/gb/apu/square1/square1.hpp
index 4bd8b344..24aab93c 100644
--- a/higan/gb/apu/square1/square1.hpp
+++ b/higan/gb/apu/square1/square1.hpp
@@ -1,38 +1,38 @@
struct Square1 {
- auto dac_enable() const -> bool;
+ auto dacEnable() const -> bool;
auto run() -> void;
auto sweep(bool update) -> void;
- auto clock_length() -> void;
- auto clock_sweep() -> void;
- auto clock_envelope() -> void;
+ auto clockLength() -> void;
+ auto clockSweep() -> void;
+ auto clockEnvelope() -> void;
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
- auto power() -> void;
+ auto power(bool initializeLength = true) -> void;
auto serialize(serializer&) -> void;
bool enable;
- uint3 sweep_frequency;
- bool sweep_direction;
- uint3 sweep_shift;
- bool sweep_negate;
+ uint3 sweepFrequency;
+ bool sweepDirection;
+ uint3 sweepShift;
+ bool sweepNegate;
uint2 duty;
- uint6 length;
- uint4 envelope_volume;
- bool envelope_direction;
- uint3 envelope_frequency;
+ uint length;
+ uint4 envelopeVolume;
+ bool envelopeDirection;
+ uint3 envelopeFrequency;
uint11 frequency;
bool counter;
int16 output;
- bool duty_output;
+ bool dutyOutput;
uint3 phase;
uint period;
- uint3 envelope_period;
- uint3 sweep_period;
- int frequency_shadow;
- bool sweep_enable;
+ uint3 envelopePeriod;
+ uint3 sweepPeriod;
+ int frequencyShadow;
+ bool sweepEnable;
uint4 volume;
};
diff --git a/higan/gb/apu/square2/square2.cpp b/higan/gb/apu/square2/square2.cpp
index af3ecccc..f07d5203 100644
--- a/higan/gb/apu/square2/square2.cpp
+++ b/higan/gb/apu/square2/square2.cpp
@@ -1,5 +1,5 @@
-auto APU::Square2::dac_enable() const -> bool {
- return (envelope_volume || envelope_direction);
+auto APU::Square2::dacEnable() const -> bool {
+ return (envelopeVolume || envelopeDirection);
}
auto APU::Square2::run() -> void {
@@ -7,30 +7,30 @@ auto APU::Square2::run() -> void {
period = 2 * (2048 - frequency);
phase++;
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; //------__
+ case 0: dutyOutput = (phase == 6); break; //______-_
+ case 1: dutyOutput = (phase >= 6); break; //______--
+ case 2: dutyOutput = (phase >= 4); break; //____----
+ case 3: dutyOutput = (phase <= 5); break; //------__
}
}
- uint4 sample = (duty_output ? volume : (uint4)0);
- if(enable == false) sample = 0;
+ uint4 sample = (dutyOutput ? volume : (uint4)0);
+ if(!enable) sample = 0;
output = sample;
}
-auto APU::Square2::clock_length() -> void {
+auto APU::Square2::clockLength() -> void {
if(counter) {
- if(++length == 0) enable = false;
+ if(length && --length == 0) enable = false;
}
}
-auto APU::Square2::clock_envelope() -> void {
- if(enable && envelope_frequency && --envelope_period == 0) {
- envelope_period = envelope_frequency;
- if(envelope_direction == 0 && volume > 0) volume--;
- if(envelope_direction == 1 && volume < 15) volume++;
+auto APU::Square2::clockEnvelope() -> void {
+ if(enable && envelopeFrequency && --envelopePeriod == 0) {
+ envelopePeriod = envelopeFrequency;
+ if(envelopeDirection == 0 && volume > 0) volume--;
+ if(envelopeDirection == 1 && volume < 15) volume++;
}
}
@@ -44,7 +44,7 @@ auto APU::Square2::read(uint16 addr) -> uint8 {
}
if(addr == 0xff17) { //NR22
- return envelope_volume << 4 | envelope_direction << 3 | envelope_frequency;
+ return envelopeVolume << 4 | envelopeDirection << 3 | envelopeFrequency;
}
if(addr == 0xff18) { //NR23
@@ -61,14 +61,14 @@ auto APU::Square2::read(uint16 addr) -> uint8 {
auto APU::Square2::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff16) { //NR21
duty = data >> 6;
- length = (data & 0x3f);
+ length = 64 - (data & 0x3f);
}
if(addr == 0xff17) { //NR22
- envelope_volume = data >> 4;
- envelope_direction = data & 0x08;
- envelope_frequency = data & 0x07;
- if(dac_enable() == false) enable = false;
+ envelopeVolume = data >> 4;
+ envelopeDirection = data & 0x08;
+ envelopeFrequency = data & 0x07;
+ if(!dacEnable()) enable = false;
}
if(addr == 0xff18) { //NR23
@@ -76,36 +76,46 @@ auto APU::Square2::write(uint16 addr, uint8 data) -> void {
}
if(addr == 0xff19) { //NR24
+ if((apu.phase & 1) && !counter && (data & 0x40)) {
+ if(length && --length == 0) enable = false;
+ }
+
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize) {
- enable = dac_enable();
+ enable = dacEnable();
period = 2 * (2048 - frequency);
- envelope_period = envelope_frequency;
- volume = envelope_volume;
+ envelopePeriod = envelopeFrequency;
+ 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;
duty = 0;
- length = 0;
- envelope_volume = 0;
- envelope_direction = 0;
- envelope_frequency = 0;
+ envelopeVolume = 0;
+ envelopeDirection = 0;
+ envelopeFrequency = 0;
frequency = 0;
counter = 0;
output = 0;
- duty_output = 0;
+ dutyOutput = 0;
phase = 0;
period = 0;
- envelope_period = 0;
+ envelopePeriod = 0;
volume = 0;
+
+ if(initializeLength) length = 64;
}
auto APU::Square2::serialize(serializer& s) -> void {
@@ -113,16 +123,16 @@ auto APU::Square2::serialize(serializer& s) -> void {
s.integer(duty);
s.integer(length);
- s.integer(envelope_volume);
- s.integer(envelope_direction);
- s.integer(envelope_frequency);
+ s.integer(envelopeVolume);
+ s.integer(envelopeDirection);
+ s.integer(envelopeFrequency);
s.integer(frequency);
s.integer(counter);
s.integer(output);
- s.integer(duty_output);
+ s.integer(dutyOutput);
s.integer(phase);
s.integer(period);
- s.integer(envelope_period);
+ s.integer(envelopePeriod);
s.integer(volume);
}
diff --git a/higan/gb/apu/square2/square2.hpp b/higan/gb/apu/square2/square2.hpp
index db9025c6..48577d9b 100644
--- a/higan/gb/apu/square2/square2.hpp
+++ b/higan/gb/apu/square2/square2.hpp
@@ -1,29 +1,29 @@
struct Square2 {
- auto dac_enable() const -> bool;
+ auto dacEnable() const -> bool;
auto run() -> void;
- auto clock_length() -> void;
- auto clock_envelope() -> void;
+ auto clockLength() -> void;
+ auto clockEnvelope() -> void;
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
- auto power() -> void;
+ auto power(bool initializeLength = true) -> void;
auto serialize(serializer&) -> void;
bool enable;
uint2 duty;
- uint6 length;
- uint4 envelope_volume;
- bool envelope_direction;
- uint3 envelope_frequency;
+ uint length;
+ uint4 envelopeVolume;
+ bool envelopeDirection;
+ uint3 envelopeFrequency;
uint11 frequency;
bool counter;
int16 output;
- bool duty_output;
+ bool dutyOutput;
uint3 phase;
uint period;
- uint3 envelope_period;
+ uint3 envelopePeriod;
uint4 volume;
};
diff --git a/higan/gb/apu/wave/wave.cpp b/higan/gb/apu/wave/wave.cpp
index cd04178f..b0032748 100644
--- a/higan/gb/apu/wave/wave.cpp
+++ b/higan/gb/apu/wave/wave.cpp
@@ -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);
}
auto APU::Wave::run() -> void {
if(period && --period == 0) {
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%
- uint4 sample = pattern_sample >> shift[volume];
+ uint4 sample = patternSample >> shift[volume];
if(enable == false) sample = 0;
output = sample;
}
-auto APU::Wave::clock_length() -> void {
+auto APU::Wave::clockLength() -> void {
if(counter) {
- if(++length == 0) enable = false;
+ if(length && --length == 0) enable = false;
}
}
auto APU::Wave::read(uint16 addr) -> uint8 {
if(addr == 0xff1a) { //NR30
- return dac_enable << 7 | 0x7f;
+ return dacEnable << 7 | 0x7f;
}
if(addr == 0xff1b) { //NR31
@@ -51,12 +51,12 @@ auto APU::Wave::read(uint16 addr) -> uint8 {
auto APU::Wave::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff1a) { //NR30
- dac_enable = data & 0x80;
- if(dac_enable == false) enable = false;
+ dacEnable = data & 0x80;
+ if(!dacEnable) enable = false;
}
if(addr == 0xff1b) { //NR31
- length = data;
+ length = 256 - data;
}
if(addr == 0xff1c) { //NR32
@@ -68,14 +68,23 @@ auto APU::Wave::write(uint16 addr, uint8 data) -> void {
}
if(addr == 0xff1e) { //NR34
+ if((apu.phase & 1) && !counter && (data & 0x40)) {
+ if(length && --length == 0) enable = false;
+ }
+
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
if(initialize) {
- enable = dac_enable;
+ enable = dacEnable;
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;
- dac_enable = 0;
+ dacEnable = 0;
volume = 0;
frequency = 0;
counter = 0;
output = 0;
- length = 0;
period = 0;
- pattern_offset = 0;
- pattern_sample = 0;
+ patternOffset = 0;
+ patternSample = 0;
+
+ if(initializeLength) length = 256;
}
auto APU::Wave::serialize(serializer& s) -> void {
s.integer(enable);
- s.integer(dac_enable);
+ s.integer(dacEnable);
s.integer(volume);
s.integer(frequency);
s.integer(counter);
@@ -111,6 +121,6 @@ auto APU::Wave::serialize(serializer& s) -> void {
s.integer(output);
s.integer(length);
s.integer(period);
- s.integer(pattern_offset);
- s.integer(pattern_sample);
+ s.integer(patternOffset);
+ s.integer(patternSample);
}
diff --git a/higan/gb/apu/wave/wave.hpp b/higan/gb/apu/wave/wave.hpp
index 9a39ef7d..732c6cc6 100644
--- a/higan/gb/apu/wave/wave.hpp
+++ b/higan/gb/apu/wave/wave.hpp
@@ -1,25 +1,25 @@
struct Wave {
- auto get_pattern(uint5 offset) const -> uint4;
+ auto getPattern(uint5 offset) const -> uint4;
auto run() -> void;
- auto clock_length() -> void;
+ auto clockLength() -> void;
auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
- auto power() -> void;
+ auto power(bool initializeLength = true) -> void;
auto serialize(serializer&) -> void;
bool enable;
- bool dac_enable;
+ bool dacEnable;
uint2 volume;
uint11 frequency;
bool counter;
uint8 pattern[16];
int16 output;
- uint8 length;
+ uint length;
uint period;
- uint5 pattern_offset;
- uint4 pattern_sample;
+ uint5 patternOffset;
+ uint4 patternSample;
};
diff --git a/higan/gb/ppu/mmio.cpp b/higan/gb/ppu/mmio.cpp
index d7d13e2d..e6af4bf9 100644
--- a/higan/gb/ppu/mmio.cpp
+++ b/higan/gb/ppu/mmio.cpp
@@ -77,15 +77,27 @@ auto PPU::mmio_read(uint16 addr) -> uint8 {
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];
}
+ if(addr == 0xff6a) { //OBPI
+ return status.obpi_increment << 7 | status.obpi;
+ }
+
if(addr == 0xff6b) { //OBPD
return obpd[status.obpi];
}
- return 0xff;
+ return 0xff; //should never occur
}
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(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;
diff --git a/higan/gb/ppu/ppu.cpp b/higan/gb/ppu/ppu.cpp
index d0bbf130..80a67406 100644
--- a/higan/gb/ppu/ppu.cpp
+++ b/higan/gb/ppu/ppu.cpp
@@ -24,23 +24,49 @@ auto PPU::main() -> void {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
+ status.lx = 0;
interface->lcdScanline(); //Super Game Boy notification
- if(status.display_enable && status.ly < 144) {
- if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
- add_clocks(92);
- for(auto n : range(160)) {
- system.cgb() ? cgb_run() : dmg_run();
- add_clocks(1);
+ if(status.display_enable) {
+ //LYC of zero triggers on LY==153
+ if((status.lyc && status.ly == status.lyc) || (!status.lyc && status.ly == 153)) {
+ if(status.interrupt_lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
+ }
+
+ 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 {
return ((data & 0x8080) >> 7) | ((data & 0x4040) >> 5)
| ((data & 0x2020) >> 3) | ((data & 0x1010) >> 1)
@@ -87,6 +90,14 @@ auto PPU::hflip(uint data) const -> uint {
auto PPU::power() -> void {
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 = 0xfe00; n <= 0xfe9f; n++) bus.mmio[n] = this; //OAM
diff --git a/higan/gb/ppu/ppu.hpp b/higan/gb/ppu/ppu.hpp
index 33e035af..7ffa8326 100644
--- a/higan/gb/ppu/ppu.hpp
+++ b/higan/gb/ppu/ppu.hpp
@@ -2,8 +2,6 @@ struct PPU : Thread, MMIO {
static auto Main() -> void;
auto main() -> void;
auto add_clocks(uint clocks) -> void;
- auto scanline() -> void;
- auto frame() -> void;
auto hflip(uint data) const -> uint;
@@ -39,6 +37,9 @@ struct PPU : Thread, MMIO {
uint8 bgpd[64];
uint8 obpd[64];
+ function void> scanline;
+ function void> run;
+
struct Status {
uint lx;