mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
72b6a8b32e
commit
82ec876302
|
@ -2,11 +2,17 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.byuu.higan</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>higan</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>higan</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>higan.icns</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -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/";
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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<auto () -> void> scanline;
|
||||
function<auto () -> void> run;
|
||||
|
||||
struct Status {
|
||||
uint lx;
|
||||
|
||||
|
|
Loading…
Reference in New Issue