Update to v096r04 release.

byuu says:

Changelog:
- fixed S-DD1 RAM writes (Star Ocean audio fixed)
- applied all of the DMG test ROM fixes discussed earlier; passes many
  more test ROMs now
- at least until the GBVideoPlayer is working: for debugging purposes,
  CPU/PPU single-step now instead of sync just-in-time (~30% slower)
- fixed OS X crash on NSTextView (hopefully, would be very odd if not)

Unfortunately passing these test ROMs caused my favorite GB/GBC game to
break all of its graphics =(
Shin Megami Tensei - Devichil - Kuro no Sho (Japan) is all garbled now.
I'm really quite bummed by this ... but I guess I'll go through and
revert r04's fixes one at a time until I find what's causing it.

On the plus side, Astro Rabby is playable now. Still acts weird when
pressing B/A on the first screen, but the start button will start the
game.

EDIT: got it. Shin Megami Tensei - Devichil requires FF4F (VBK) to be
readable. Before, it was always returning 0x00. With my return 0xFF
patch, that broke. But it should be returning the VBK value, which also
fixes it. Also need to handle FF68/FF6A reads. Was really hoping that'd
help GBVideoPlayer too, but nope. It doesn't read any of those three
registers.
This commit is contained in:
Tim Allen
2016-01-11 21:31:30 +11:00
parent 653bb378ee
commit 72b6a8b32e
33 changed files with 297 additions and 206 deletions

View File

@@ -1,5 +1,4 @@
#ifndef EMULATOR_HPP #pragma once
#define EMULATOR_HPP
#include <nall/nall.hpp> #include <nall/nall.hpp>
#include <nall/dsp.hpp> #include <nall/dsp.hpp>
@@ -7,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.03"; static const string Version = "096.04";
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/";
@@ -55,5 +54,3 @@ template<typename R, typename... P> struct hook<auto (P...) -> R> {
#endif #endif
using varuint = varuint_t<uint>; using varuint = varuint_t<uint>;
#endif

View File

@@ -1,5 +1,4 @@
#ifndef EMULATOR_INTERFACE_HPP #pragma once
#define EMULATOR_INTERFACE_HPP
namespace Emulator { namespace Emulator {
@@ -113,5 +112,3 @@ struct Interface {
}; };
} }
#endif

View File

@@ -1,5 +1,4 @@
#ifndef FC_HPP #pragma once
#define FC_HPP
#include <emulator/emulator.hpp> #include <emulator/emulator.hpp>
#include <processor/r6502/r6502.hpp> #include <processor/r6502/r6502.hpp>
@@ -53,7 +52,6 @@ namespace Famicom {
#include <fc/ppu/ppu.hpp> #include <fc/ppu/ppu.hpp>
#include <fc/cheat/cheat.hpp> #include <fc/cheat/cheat.hpp>
#include <fc/video/video.hpp> #include <fc/video/video.hpp>
#include <fc/interface/interface.hpp>
} }
#endif #include <fc/interface/interface.hpp>

View File

@@ -1,6 +1,4 @@
#ifndef FC_HPP
namespace Famicom { namespace Famicom {
#endif
struct ID { struct ID {
enum : uint { enum : uint {
@@ -58,6 +56,4 @@ private:
extern Interface* interface; extern Interface* interface;
#ifndef FC_HPP
} }
#endif

View File

@@ -65,7 +65,6 @@ 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;
for(auto& n : mmio_data) n = 0x00;
sequencer_base = 0; sequencer_base = 0;
sequencer_step = 0; sequencer_step = 0;
@@ -74,42 +73,30 @@ auto APU::power() -> void {
wave.power(); wave.power();
noise.power(); noise.power();
master.power(); master.power();
LinearFeedbackShiftRegisterGenerator r;
for(auto& n : wave.pattern) n = r();
} }
auto APU::mmio_read(uint16 addr) -> uint8 { auto APU::mmio_read(uint16 addr) -> uint8 {
static const uint8 table[48] = { //if(!master.enable && addr != 0xff26) return 0xff;
0x80, 0x3f, 0x00, 0xff, 0xbf, //square1 if(addr >= 0xff10 && addr <= 0xff14) return square1.read(addr);
0xff, 0x3f, 0x00, 0xff, 0xbf, //square2 if(addr >= 0xff15 && addr <= 0xff19) return square2.read(addr);
0x7f, 0xff, 0x9f, 0xff, 0xbf, //wave if(addr >= 0xff1a && addr <= 0xff1e) return wave.read(addr);
0xff, 0xff, 0x00, 0x00, 0xbf, //noise if(addr >= 0xff1f && addr <= 0xff23) return noise.read(addr);
0x00, 0x00, 0x70, //master if(addr >= 0xff24 && addr <= 0xff26) return master.read(addr);
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //unmapped if(addr >= 0xff30 && addr <= 0xff3f) return wave.read(addr);
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern
};
if(addr == 0xff26) {
uint8 data = master.enable << 7;
if(square1.enable) data |= 0x01;
if(square2.enable) data |= 0x02;
if( wave.enable) data |= 0x04;
if( noise.enable) data |= 0x08;
return data | table[addr - 0xff10];
}
if(addr >= 0xff10 && addr <= 0xff3f) return mmio_data[addr - 0xff10] | table[addr - 0xff10];
return 0xff; return 0xff;
} }
auto APU::mmio_write(uint16 addr, uint8 data) -> void { auto APU::mmio_write(uint16 addr, uint8 data) -> void {
if(addr >= 0xff10 && addr <= 0xff3f) mmio_data[addr - 0xff10] = data; if(!master.enable && addr != 0xff26) return;
if(addr >= 0xff10 && addr <= 0xff14) return square1.write(addr, data);
if(addr >= 0xff10 && addr <= 0xff14) return square1.write (addr - 0xff10, data); if(addr >= 0xff15 && addr <= 0xff19) return square2.write(addr, data);
if(addr >= 0xff15 && addr <= 0xff19) return square2.write (addr - 0xff15, data); if(addr >= 0xff1a && addr <= 0xff1e) return wave.write(addr, data);
if(addr >= 0xff1a && addr <= 0xff1e) return wave.write (addr - 0xff1a, data); if(addr >= 0xff1f && addr <= 0xff23) return noise.write(addr, data);
if(addr >= 0xff1f && addr <= 0xff23) return noise.write (addr - 0xff1f, data); if(addr >= 0xff24 && addr <= 0xff26) return master.write(addr, data);
if(addr >= 0xff24 && addr <= 0xff26) return master.write (addr - 0xff24, data); if(addr >= 0xff30 && addr <= 0xff3f) return wave.write(addr, data);
if(addr >= 0xff30 && addr <= 0xff3f) return wave.write_pattern(addr - 0xff30, data);
} }
} }

View File

@@ -15,7 +15,6 @@ struct APU : Thread, MMIO {
#include "noise/noise.hpp" #include "noise/noise.hpp"
#include "master/master.hpp" #include "master/master.hpp"
uint8 mmio_data[48];
uint12 sequencer_base; uint12 sequencer_base;
uint3 sequencer_step; uint3 sequencer_step;

View File

@@ -39,15 +39,42 @@ auto APU::Master::run() -> void {
right >>= 1; right >>= 1;
} }
auto APU::Master::write(uint r, uint8 data) -> void { auto APU::Master::read(uint16 addr) -> uint8 {
if(r == 0) { //$ff24 NR50 if(addr == 0xff24) { //NR50
return left_in_enable << 7 | left_volume << 4 | right_in_enable << 3 | right_volume;
}
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;
}
if(addr == 0xff26) { //NR52
return enable << 7 | 0x70
| apu.noise.enable << 3
| apu.wave.enable << 2
| apu.square2.enable << 1
| apu.square1.enable << 0;
}
return 0xff;
}
auto APU::Master::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff24) { //NR50
left_in_enable = data & 0x80; left_in_enable = data & 0x80;
left_volume = (data >> 4) & 7; left_volume = (data >> 4) & 7;
right_in_enable = data & 0x08; right_in_enable = data & 0x08;
right_volume = (data >> 0) & 7; right_volume = (data >> 0) & 7;
} }
if(r == 1) { //$ff25 NR51 if(addr == 0xff25) { //NR51
channel4_left_enable = data & 0x80; channel4_left_enable = data & 0x80;
channel3_left_enable = data & 0x40; channel3_left_enable = data & 0x40;
channel2_left_enable = data & 0x20; channel2_left_enable = data & 0x20;
@@ -58,8 +85,15 @@ auto APU::Master::write(uint r, uint8 data) -> void {
channel1_right_enable = data & 0x01; channel1_right_enable = data & 0x01;
} }
if(r == 2) { //$ff26 NR52 if(addr == 0xff26) { //NR52
enable = data & 0x80; enable = data & 0x80;
if(!enable) {
apu.square1.power();
apu.square2.power();
apu.wave.power();
apu.noise.power();
power();
}
} }
} }

View File

@@ -1,6 +1,7 @@
struct Master { struct Master {
auto run() -> void; auto run() -> void;
auto write(uint r, uint8 data) -> void; auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
auto power() -> void; auto power() -> void;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;

View File

@@ -2,9 +2,14 @@ auto APU::Noise::dac_enable() const -> bool {
return (envelope_volume || envelope_direction); return (envelope_volume || envelope_direction);
} }
auto APU::Noise::get_period() const -> uint {
static const uint table[] = {4, 8, 16, 24, 32, 40, 48, 56};
return table[divisor] << frequency;
}
auto APU::Noise::run() -> void { auto APU::Noise::run() -> void {
if(period && --period == 0) { if(period && --period == 0) {
period = divisor << frequency; period = get_period();
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_lfsr ? 6 : 14));
@@ -18,7 +23,7 @@ auto APU::Noise::run() -> void {
} }
auto APU::Noise::clock_length() -> void { auto APU::Noise::clock_length() -> void {
if(enable && counter) { if(counter) {
if(++length == 0) enable = false; if(++length == 0) enable = false;
} }
} }
@@ -31,27 +36,50 @@ auto APU::Noise::clock_envelope() -> void {
} }
} }
auto APU::Noise::write(uint r, uint8 data) -> void { auto APU::Noise::read(uint16 addr) -> uint8 {
if(r == 1) { //$ff20 NR41 if(addr == 0xff1f) { //NR40
return 0xff;
}
if(addr == 0xff20) { //NR41
return 0xff;
}
if(addr == 0xff21) { //NR42
return envelope_volume << 4 | envelope_direction << 3 | envelope_frequency;
}
if(addr == 0xff22) { //NR43
return frequency << 4 | narrow_lfsr << 3 | divisor;
}
if(addr == 0xff23) { //NR44
return 0x80 | counter << 6 | 0x3f;
}
return 0xff;
}
auto APU::Noise::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff20) { //NR41
length = data & 0x3f; length = data & 0x3f;
} }
if(r == 2) { //$ff21 NR42 if(addr == 0xff21) { //NR42
envelope_volume = data >> 4; envelope_volume = data >> 4;
envelope_direction = data & 0x08; envelope_direction = data & 0x08;
envelope_frequency = data & 0x07; envelope_frequency = data & 0x07;
if(dac_enable() == false) enable = false; if(dac_enable() == false) enable = false;
} }
if(r == 3) { //$ff22 NR43 if(addr == 0xff22) { //NR43
frequency = data >> 4; frequency = data >> 4;
narrow_lfsr = data & 0x08; narrow_lfsr = data & 0x08;
divisor = (data & 0x07) << 3; divisor = data & 0x07;
if(divisor == 0) divisor = 4; period = get_period();
period = divisor << frequency;
} }
if(r == 4) { //$ff34 NR44 if(addr == 0xff23) { //NR44
bool initialize = data & 0x80; bool initialize = data & 0x80;
counter = data & 0x40; counter = data & 0x40;

View File

@@ -1,10 +1,12 @@
struct Noise { struct Noise {
auto dac_enable() const -> bool; auto dac_enable() const -> bool;
auto get_period() const -> uint;
auto run() -> void; auto run() -> void;
auto clock_length() -> void; auto clock_length() -> void;
auto clock_envelope() -> void; auto clock_envelope() -> void;
auto write(uint r, uint8 data) -> void; auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
auto power() -> void; auto power() -> void;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
@@ -16,7 +18,7 @@ struct Noise {
uint3 envelope_frequency; uint3 envelope_frequency;
uint4 frequency; uint4 frequency;
bool narrow_lfsr; bool narrow_lfsr;
uint divisor; uint3 divisor;
bool counter; bool counter;
int16 output; int16 output;

View File

@@ -1,7 +1,6 @@
auto APU::serialize(serializer& s) -> void { auto APU::serialize(serializer& s) -> void {
Thread::serialize(s); Thread::serialize(s);
s.array(mmio_data);
s.integer(sequencer_base); s.integer(sequencer_base);
s.integer(sequencer_step); s.integer(sequencer_step);

View File

@@ -37,7 +37,7 @@ auto APU::Square1::sweep(bool update) -> void {
} }
auto APU::Square1::clock_length() -> void { auto APU::Square1::clock_length() -> void {
if(counter && enable) { if(counter) {
if(++length == 0) enable = false; if(++length == 0) enable = false;
} }
} }
@@ -58,31 +58,55 @@ auto APU::Square1::clock_envelope() -> void {
} }
} }
auto APU::Square1::write(uint r, uint8 data) -> void { auto APU::Square1::read(uint16 addr) -> uint8 {
if(r == 0) { //$ff10 NR10 if(addr == 0xff10) { //NR10
return 0x80 | sweep_frequency << 4 | sweep_direction << 3 | sweep_shift;
}
if(addr == 0xff11) { //NR11
return duty << 6 | 0x3f;
}
if(addr == 0xff12) { //NR12
return envelope_volume << 4 | envelope_direction << 3 | envelope_frequency;
}
if(addr == 0xff13) { //NR13
return 0xff;
}
if(addr == 0xff14) { //NR14
return 0x80 | counter << 6 | 0x3f;
}
return 0xff;
}
auto APU::Square1::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff10) { //NR10
if(sweep_negate && sweep_direction && !(data & 0x08)) enable = false; if(sweep_negate && sweep_direction && !(data & 0x08)) enable = false;
sweep_frequency = (data >> 4) & 7; sweep_frequency = (data >> 4) & 7;
sweep_direction = data & 0x08; sweep_direction = data & 0x08;
sweep_shift = data & 0x07; sweep_shift = data & 0x07;
} }
if(r == 1) { //$ff11 NR11 if(addr == 0xff11) { //NR11
duty = data >> 6; duty = data >> 6;
length = data & 0x3f; length = data & 0x3f;
} }
if(r == 2) { //$ff12 NR12 if(addr == 0xff12) { //NR12
envelope_volume = data >> 4; envelope_volume = data >> 4;
envelope_direction = data & 0x08; envelope_direction = data & 0x08;
envelope_frequency = data & 0x07; envelope_frequency = data & 0x07;
if(dac_enable() == false) enable = false; if(dac_enable() == false) enable = false;
} }
if(r == 3) { //$ff13 NR13 if(addr == 0xff13) { //NR13
frequency = (frequency & 0x0700) | data; frequency = (frequency & 0x0700) | data;
} }
if(r == 4) { //$ff14 NR14 if(addr == 0xff14) { //NR14
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);

View File

@@ -6,7 +6,8 @@ struct Square1 {
auto clock_length() -> void; auto clock_length() -> void;
auto clock_sweep() -> void; auto clock_sweep() -> void;
auto clock_envelope() -> void; auto clock_envelope() -> void;
auto write(uint r, uint8 data) -> void; auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
auto power() -> void; auto power() -> void;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;

View File

@@ -21,7 +21,7 @@ auto APU::Square2::run() -> void {
} }
auto APU::Square2::clock_length() -> void { auto APU::Square2::clock_length() -> void {
if(counter && enable) { if(counter) {
if(++length == 0) enable = false; if(++length == 0) enable = false;
} }
} }
@@ -34,24 +34,48 @@ auto APU::Square2::clock_envelope() -> void {
} }
} }
auto APU::Square2::write(uint r, uint8 data) -> void { auto APU::Square2::read(uint16 addr) -> uint8 {
if(r == 1) { //$ff16 NR21 if(addr == 0xff15) { //NR20
return 0xff;
}
if(addr == 0xff16) { //NR21
return duty << 6 | 0x3f;
}
if(addr == 0xff17) { //NR22
return envelope_volume << 4 | envelope_direction << 3 | envelope_frequency;
}
if(addr == 0xff18) { //NR23
return 0xff;
}
if(addr == 0xff19) { //NR24
return 0x80 | counter << 6 | 0x3f;
}
return 0xff;
}
auto APU::Square2::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff16) { //NR21
duty = data >> 6; duty = data >> 6;
length = (data & 0x3f); length = (data & 0x3f);
} }
if(r == 2) { //$ff17 NR22 if(addr == 0xff17) { //NR22
envelope_volume = data >> 4; envelope_volume = data >> 4;
envelope_direction = data & 0x08; envelope_direction = data & 0x08;
envelope_frequency = data & 0x07; envelope_frequency = data & 0x07;
if(dac_enable() == false) enable = false; if(dac_enable() == false) enable = false;
} }
if(r == 3) { //$ff18 NR23 if(addr == 0xff18) { //NR23
frequency = (frequency & 0x0700) | data; frequency = (frequency & 0x0700) | data;
} }
if(r == 4) { //$ff19 NR24 if(addr == 0xff19) { //NR24
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);

View File

@@ -4,7 +4,8 @@ struct Square2 {
auto run() -> void; auto run() -> void;
auto clock_length() -> void; auto clock_length() -> void;
auto clock_envelope() -> void; auto clock_envelope() -> void;
auto write(uint r, uint8 data) -> void; auto read(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void;
auto power() -> void; auto power() -> void;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;

View File

@@ -1,45 +1,73 @@
auto APU::Wave::get_pattern(uint5 offset) const -> uint4 {
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 = pattern[++pattern_offset]; pattern_sample = get_pattern(++pattern_offset);
} }
uint4 sample = pattern_sample >> volume_shift; static const uint shift[] = {4, 0, 1, 2}; //0%, 100%, 50%, 25%
uint4 sample = pattern_sample >> 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::clock_length() -> void {
if(enable && counter) { if(counter) {
if(++length == 0) enable = false; if(++length == 0) enable = false;
} }
} }
auto APU::Wave::write(uint r, uint8 data) -> void { auto APU::Wave::read(uint16 addr) -> uint8 {
if(r == 0) { //$ff1a NR30 if(addr == 0xff1a) { //NR30
return dac_enable << 7 | 0x7f;
}
if(addr == 0xff1b) { //NR31
return 0xff;
}
if(addr == 0xff1c) { //NR32
return 0x80 | volume << 5 | 0x1f;
}
if(addr == 0xff1d) { //NR33
return 0xff;
}
if(addr == 0xff1e) { //NR34
return 0x80 | counter << 6 | 0x3f;
}
if(addr >= 0xff30 && addr <= 0xff3f) {
return pattern[addr & 15];
}
return 0xff;
}
auto APU::Wave::write(uint16 addr, uint8 data) -> void {
if(addr == 0xff1a) { //NR30
dac_enable = data & 0x80; dac_enable = data & 0x80;
if(dac_enable == false) enable = false; if(dac_enable == false) enable = false;
} }
if(r == 1) { //$ff1b NR31 if(addr == 0xff1b) { //NR31
length = data; length = data;
} }
if(r == 2) { //$ff1c NR32 if(addr == 0xff1c) { //NR32
switch((data >> 5) & 3) { volume = data >> 5;
case 0: volume_shift = 4; break; // 0%
case 1: volume_shift = 0; break; //100%
case 2: volume_shift = 1; break; // 50%
case 3: volume_shift = 2; break; // 25%
}
} }
if(r == 3) { //$ff1d NR33 if(addr == 0xff1d) { //NR33
frequency = (frequency & 0x0700) | data; frequency = (frequency & 0x0700) | data;
} }
if(r == 4) { //$ff1e NR34 if(addr == 0xff1e) { //NR34
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);
@@ -50,25 +78,20 @@ auto APU::Wave::write(uint r, uint8 data) -> void {
pattern_offset = 0; pattern_offset = 0;
} }
} }
}
auto APU::Wave::write_pattern(uint p, uint8 data) -> void { if(addr >= 0xff30 && addr <= 0xff3f) {
p <<= 1; pattern[addr & 15] = data;
pattern[p + 0] = (data >> 4) & 15; }
pattern[p + 1] = (data >> 0) & 15;
} }
auto APU::Wave::power() -> void { auto APU::Wave::power() -> void {
enable = 0; enable = 0;
dac_enable = 0; dac_enable = 0;
volume_shift = 0; volume = 0;
frequency = 0; frequency = 0;
counter = 0; counter = 0;
LinearFeedbackShiftRegisterGenerator r;
for(auto& n : pattern) n = r() & 15;
output = 0; output = 0;
length = 0; length = 0;
period = 0; period = 0;
@@ -80,7 +103,7 @@ auto APU::Wave::serialize(serializer& s) -> void {
s.integer(enable); s.integer(enable);
s.integer(dac_enable); s.integer(dac_enable);
s.integer(volume_shift); s.integer(volume);
s.integer(frequency); s.integer(frequency);
s.integer(counter); s.integer(counter);
s.array(pattern); s.array(pattern);

View File

@@ -1,8 +1,10 @@
struct Wave { struct Wave {
auto get_pattern(uint5 offset) const -> uint4;
auto run() -> void; auto run() -> void;
auto clock_length() -> void; auto clock_length() -> void;
auto write(uint r, uint8 data) -> void; auto read(uint16 addr) -> uint8;
auto write_pattern(uint p, uint8 data) -> void; auto write(uint16 addr, uint8 data) -> void;
auto power() -> void; auto power() -> void;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
@@ -10,10 +12,10 @@ struct Wave {
bool enable; bool enable;
bool dac_enable; bool dac_enable;
uint volume_shift; uint2 volume;
uint11 frequency; uint11 frequency;
bool counter; bool counter;
uint8 pattern[32]; uint8 pattern[16];
int16 output; int16 output;
uint8 length; uint8 length;

View File

@@ -81,13 +81,13 @@ auto CPU::interrupt_test() -> void {
} }
auto CPU::interrupt_exec(uint16 pc) -> void { auto CPU::interrupt_exec(uint16 pc) -> void {
op_io();
op_io();
op_io();
r.ime = 0; r.ime = 0;
op_write(--r[SP], r[PC] >> 8); op_write(--r[SP], r[PC] >> 8);
op_write(--r[SP], r[PC] >> 0); op_write(--r[SP], r[PC] >> 0);
r[PC] = pc; r[PC] = pc;
op_io();
op_io();
op_io();
} }
auto CPU::stop() -> bool { auto CPU::stop() -> bool {
@@ -201,8 +201,8 @@ auto CPU::power() -> void {
status.interrupt_enable_vblank = 0; status.interrupt_enable_vblank = 0;
oamdma.active = false; oamdma.active = false;
oamdma.clock = 0;
oamdma.bank = 0; oamdma.bank = 0;
oamdma.offset = 0;
} }
} }

View File

@@ -36,7 +36,7 @@ struct CPU : Processor::LR35902, Thread, MMIO {
auto hblank() -> void; auto hblank() -> void;
struct Status { struct Status {
uint clock; uint22 clock;
//$ff00 JOYP //$ff00 JOYP
bool p15; bool p15;
@@ -53,7 +53,7 @@ struct CPU : Processor::LR35902, Thread, MMIO {
bool serial_clock; bool serial_clock;
//$ff04 DIV //$ff04 DIV
uint8 div; uint16 div;
//$ff05 TIMA //$ff05 TIMA
uint8 tima; uint8 tima;
@@ -109,8 +109,8 @@ struct CPU : Processor::LR35902, Thread, MMIO {
struct OAMDMA { struct OAMDMA {
bool active; bool active;
uint clock;
uint8 bank; uint8 bank;
uint8 offset;
} oamdma; } oamdma;
uint8 wram[32768]; //GB=8192, GBC=32768 uint8 wram[32768]; //GB=8192, GBC=32768

View File

@@ -6,14 +6,16 @@ auto CPU::op_io() -> void {
auto CPU::op_read(uint16 addr) -> uint8 { auto CPU::op_read(uint16 addr) -> uint8 {
cycle_edge(); cycle_edge();
add_clocks(4); add_clocks(4);
if(oamdma.active && (addr < 0xff80 || addr == 0xffff)) return 0xff; //OAM is inaccessible during OAMDMA transfer
if(oamdma.active && oamdma.clock >= 8 && addr >= 0xfe00 && addr <= 0xfe9f) return 0xff;
return bus.read(addr); return bus.read(addr);
} }
auto CPU::op_write(uint16 addr, uint8 data) -> void { auto CPU::op_write(uint16 addr, uint8 data) -> void {
cycle_edge(); cycle_edge();
add_clocks(4); add_clocks(4);
if(oamdma.active && (addr < 0xff80 || addr == 0xffff)) return; //OAM is inaccessible during OAMDMA transfer
if(oamdma.active && oamdma.clock >= 8 && addr >= 0xfe00 && addr <= 0xfe9f) return;
bus.write(addr, data); bus.write(addr, data);
} }

View File

@@ -53,7 +53,7 @@ auto CPU::mmio_read(uint16 addr) -> uint8 {
} }
if(addr == 0xff04) { //DIV if(addr == 0xff04) { //DIV
return status.div; return status.div >> 8;
} }
if(addr == 0xff05) { //TIMA if(addr == 0xff05) { //TIMA
@@ -188,8 +188,8 @@ auto CPU::mmio_write(uint16 addr, uint8 data) -> void {
if(addr == 0xff46) { //DMA if(addr == 0xff46) { //DMA
oamdma.active = true; oamdma.active = true;
oamdma.clock = 0;
oamdma.bank = data; oamdma.bank = data;
oamdma.offset = 0;
return; return;
} }
@@ -225,7 +225,7 @@ auto CPU::mmio_write(uint16 addr, uint8 data) -> void {
if(status.dma_mode == 0) { if(status.dma_mode == 0) {
do { do {
for(unsigned n = 0; n < 16; n++) { for(auto n : range(16)) {
dma_write(status.dma_target++, dma_read(status.dma_source++)); dma_write(status.dma_target++, dma_read(status.dma_source++));
} }
add_clocks(8 << status.speed_double); add_clocks(8 << status.speed_double);

View File

@@ -55,6 +55,6 @@ auto CPU::serialize(serializer& s) -> void {
s.integer(status.interrupt_enable_vblank); s.integer(status.interrupt_enable_vblank);
s.integer(oamdma.active); s.integer(oamdma.active);
s.integer(oamdma.clock);
s.integer(oamdma.bank); s.integer(oamdma.bank);
s.integer(oamdma.offset);
} }

View File

@@ -3,39 +3,46 @@
// 154 scanlines/frame // 154 scanlines/frame
auto CPU::add_clocks(uint clocks) -> void { auto CPU::add_clocks(uint clocks) -> void {
if(system.sgb()) system.clocks_executed += clocks;
while(clocks--) {
if(oamdma.active) { if(oamdma.active) {
for(uint n = 0; n < 4 * clocks; n++) { uint offset = oamdma.clock++;
bus.write(0xfe00 + oamdma.offset, bus.read((oamdma.bank << 8) + oamdma.offset)); if((offset & 3) == 0) {
if(++oamdma.offset == 160) { offset >>= 2;
if(offset == 0) {
//warm-up
} else if(offset == 161) {
//cool-down; disable
oamdma.active = false; oamdma.active = false;
break; } else {
bus.write(0xfe00 + offset - 1, bus.read((oamdma.bank << 8) + offset - 1));
} }
} }
} }
system.clocks_executed += clocks; if(++status.clock == 0) {
if(system.sgb()) scheduler.exit(Scheduler::ExitReason::StepEvent);
status.clock += clocks;
if(status.clock >= 4 * 1024 * 1024) {
status.clock -= 4 * 1024 * 1024;
cartridge.mbc3.second(); cartridge.mbc3.second();
} }
//4MHz / N(hz) - 1 = mask //4MHz / N(hz) - 1 = mask
if((status.clock & 15) == 0) timer_262144hz(); status.div++;
if((status.clock & 63) == 0) timer_65536hz(); if((status.div & 15) == 0) timer_262144hz();
if((status.clock & 255) == 0) timer_16384hz(); if((status.div & 63) == 0) timer_65536hz();
if((status.clock & 511) == 0) timer_8192hz(); if((status.div & 255) == 0) timer_16384hz();
if((status.clock & 1023) == 0) timer_4096hz(); if((status.div & 511) == 0) timer_8192hz();
if((status.div & 1023) == 0) timer_4096hz();
ppu.clock -= clocks * ppu.frequency; ppu.clock -= ppu.frequency;
if(ppu.clock < 0) co_switch(scheduler.active_thread = ppu.thread); if(ppu.clock < 0) co_switch(scheduler.active_thread = ppu.thread);
apu.clock -= clocks * apu.frequency; apu.clock -= apu.frequency;
if(apu.clock < 0) co_switch(scheduler.active_thread = apu.thread); if(apu.clock < 0) co_switch(scheduler.active_thread = apu.thread);
} }
if(system.sgb()) scheduler.exit(Scheduler::ExitReason::StepEvent);
}
auto CPU::timer_262144hz() -> void { auto CPU::timer_262144hz() -> void {
if(status.timer_enable && status.timer_clock == 1) { if(status.timer_enable && status.timer_clock == 1) {
if(++status.tima == 0) { if(++status.tima == 0) {
@@ -61,8 +68,6 @@ auto CPU::timer_16384hz() -> void {
interrupt_raise(Interrupt::Timer); interrupt_raise(Interrupt::Timer);
} }
} }
status.div++;
} }
auto CPU::timer_8192hz() -> void { auto CPU::timer_8192hz() -> void {

View File

@@ -1,5 +1,4 @@
#ifndef GB_HPP #pragma once
#define GB_HPP
#include <emulator/emulator.hpp> #include <emulator/emulator.hpp>
#include <processor/lr35902/lr35902.hpp> #include <processor/lr35902/lr35902.hpp>
@@ -52,6 +51,6 @@ namespace GameBoy {
#include <gb/apu/apu.hpp> #include <gb/apu/apu.hpp>
#include <gb/cheat/cheat.hpp> #include <gb/cheat/cheat.hpp>
#include <gb/video/video.hpp> #include <gb/video/video.hpp>
}; }
#endif #include <gb/interface/interface.hpp>

View File

@@ -1,6 +1,4 @@
#ifndef GB_HPP
namespace GameBoy { namespace GameBoy {
#endif
struct ID { struct ID {
enum : uint { enum : uint {
@@ -72,6 +70,4 @@ private:
extern Interface* interface; extern Interface* interface;
#ifndef GB_HPP
} }
#endif

View File

@@ -45,12 +45,14 @@ auto PPU::main() -> void {
} }
auto PPU::add_clocks(uint clocks) -> void { auto PPU::add_clocks(uint clocks) -> void {
status.lx += clocks; while(clocks--) {
clock += clocks * cpu.frequency; status.lx++;
clock += cpu.frequency;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) { if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
co_switch(scheduler.active_thread = cpu.thread); co_switch(scheduler.active_thread = cpu.thread);
} }
} }
}
auto PPU::scanline() -> void { auto PPU::scanline() -> void {
status.lx = 0; status.lx = 0;

View File

@@ -1,5 +1,4 @@
#ifndef GBA_HPP #pragma once
#define GBA_HPP
#include <emulator/emulator.hpp> #include <emulator/emulator.hpp>
#include <processor/arm/arm.hpp> #include <processor/arm/arm.hpp>
@@ -56,7 +55,6 @@ namespace GameBoyAdvance {
}; };
#include <gba/memory/memory.hpp> #include <gba/memory/memory.hpp>
#include <gba/interface/interface.hpp>
#include <gba/scheduler/scheduler.hpp> #include <gba/scheduler/scheduler.hpp>
#include <gba/system/system.hpp> #include <gba/system/system.hpp>
#include <gba/cartridge/cartridge.hpp> #include <gba/cartridge/cartridge.hpp>
@@ -67,4 +65,4 @@ namespace GameBoyAdvance {
#include <gba/video/video.hpp> #include <gba/video/video.hpp>
} }
#endif #include <gba/interface/interface.hpp>

View File

@@ -1,6 +1,4 @@
#ifndef GBA_HPP
namespace GameBoyAdvance { namespace GameBoyAdvance {
#endif
struct ID { struct ID {
enum : uint { enum : uint {
@@ -55,6 +53,4 @@ private:
extern Interface* interface; extern Interface* interface;
#ifndef GBA_HPP
} }
#endif

View File

@@ -103,9 +103,9 @@ auto LR35902::op_ld_sp_hl() {
} }
template<uint x> auto LR35902::op_push_rr() { template<uint x> auto LR35902::op_push_rr() {
op_io();
op_write(--r[SP], r[x] >> 8); op_write(--r[SP], r[x] >> 8);
op_write(--r[SP], r[x] >> 0); op_write(--r[SP], r[x] >> 0);
op_io();
} }
template<uint x> auto LR35902::op_pop_rr() { template<uint x> auto LR35902::op_pop_rr() {
@@ -297,24 +297,24 @@ template<uint x> auto LR35902::op_dec_rr() {
} }
auto LR35902::op_add_sp_n() { auto LR35902::op_add_sp_n() {
op_io();
op_io();
int n = (int8)op_read(r[PC]++); int n = (int8)op_read(r[PC]++);
r.f.z = 0; r.f.z = 0;
r.f.n = 0; r.f.n = 0;
r.f.h = ((r[SP] & 0x0f) + (n & 0x0f)) > 0x0f; r.f.h = ((r[SP] & 0x0f) + (n & 0x0f)) > 0x0f;
r.f.c = ((r[SP] & 0xff) + (n & 0xff)) > 0xff; r.f.c = ((r[SP] & 0xff) + (n & 0xff)) > 0xff;
r[SP] += n; r[SP] += n;
op_io();
op_io();
} }
auto LR35902::op_ld_hl_sp_n() { auto LR35902::op_ld_hl_sp_n() {
op_io();
int n = (int8)op_read(r[PC]++); int n = (int8)op_read(r[PC]++);
r.f.z = 0; r.f.z = 0;
r.f.n = 0; r.f.n = 0;
r.f.h = ((r[SP] & 0x0f) + (n & 0x0f)) > 0x0f; r.f.h = ((r[SP] & 0x0f) + (n & 0x0f)) > 0x0f;
r.f.c = ((r[SP] & 0xff) + (n & 0xff)) > 0xff; r.f.c = ((r[SP] & 0xff) + (n & 0xff)) > 0xff;
r[HL] = r[SP] + n; r[HL] = r[SP] + n;
op_io();
} }
//rotate/shift commands //rotate/shift commands
@@ -618,20 +618,20 @@ template<uint x, bool y> auto LR35902::op_jr_f_n() {
auto LR35902::op_call_nn() { auto LR35902::op_call_nn() {
uint8 lo = op_read(r[PC]++); uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++); uint8 hi = op_read(r[PC]++);
op_io();
op_write(--r[SP], r[PC] >> 8); op_write(--r[SP], r[PC] >> 8);
op_write(--r[SP], r[PC] >> 0); op_write(--r[SP], r[PC] >> 0);
r[PC] = (hi << 8) | (lo << 0); r[PC] = (hi << 8) | (lo << 0);
op_io();
} }
template<uint x, bool y> auto LR35902::op_call_f_nn() { template<uint x, bool y> auto LR35902::op_call_f_nn() {
uint8 lo = op_read(r[PC]++); uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++); uint8 hi = op_read(r[PC]++);
if(r.f[x] == y) { if(r.f[x] == y) {
op_io();
op_write(--r[SP], r[PC] >> 8); op_write(--r[SP], r[PC] >> 8);
op_write(--r[SP], r[PC] >> 0); op_write(--r[SP], r[PC] >> 0);
r[PC] = (hi << 8) | (lo << 0); r[PC] = (hi << 8) | (lo << 0);
op_io();
} }
} }
@@ -661,8 +661,8 @@ auto LR35902::op_reti() {
} }
template<uint n> auto LR35902::op_rst_n() { template<uint n> auto LR35902::op_rst_n() {
op_io();
op_write(--r[SP], r[PC] >> 8); op_write(--r[SP], r[PC] >> 8);
op_write(--r[SP], r[PC] >> 0); op_write(--r[SP], r[PC] >> 0);
r[PC] = n; r[PC] = n;
op_io();
} }

View File

@@ -146,26 +146,14 @@ auto SDD1::mcurom_read(uint addr, uint8) -> uint8 {
auto SDD1::mcurom_write(uint addr, uint8 data) -> void { auto SDD1::mcurom_write(uint addr, uint8 data) -> void {
} }
//map address=00-3f,80-bf:6000-7fff mask=0xe000
//map address=70-7d:0000-7fff mask=0x8000
auto SDD1::mcuram_read(uint addr, uint8 data) -> uint8 { auto SDD1::mcuram_read(uint addr, uint8 data) -> uint8 {
if((addr & 0x60e000) == 0x006000) { //$00-3f,80-bf:6000-7fff
return ram.read(addr & 0x1fff, data); return ram.read(addr & 0x1fff, data);
} }
if((addr & 0xf08000) == 0x700000) { //$70-7f:0000-7fff
return ram.read(addr & 0x1fff, data);
}
return data;
}
auto SDD1::mcuram_write(uint addr, uint8 data) -> void { auto SDD1::mcuram_write(uint addr, uint8 data) -> void {
if((addr & 0x60e000) == 0x006000) { //$00-3f,80-bf:6000-7fff
return ram.write(addr & 0x1fff, data); return ram.write(addr & 0x1fff, data);
} }
if((addr & 0xf08000) == 0x700000) { //$70-7f:0000-7fff
return ram.write(addr & 0x1fff, data);
}
}
} }

View File

@@ -1,6 +1,4 @@
#ifndef SFC_HPP
namespace SuperFamicom { namespace SuperFamicom {
#endif
struct ID { struct ID {
enum : uint { enum : uint {
@@ -126,6 +124,4 @@ struct Interface : Emulator::Interface {
extern Interface* interface; extern Interface* interface;
#ifndef SFC_HPP
} }
#endif

View File

@@ -1,5 +1,4 @@
#ifndef SFC_HPP #pragma once
#define SFC_HPP
#include <emulator/emulator.hpp> #include <emulator/emulator.hpp>
#include <processor/arm/arm.hpp> #include <processor/arm/arm.hpp>
@@ -71,10 +70,9 @@ namespace SuperFamicom {
#include <sfc/slot/slot.hpp> #include <sfc/slot/slot.hpp>
#include <sfc/cartridge/cartridge.hpp> #include <sfc/cartridge/cartridge.hpp>
#include <sfc/cheat/cheat.hpp> #include <sfc/cheat/cheat.hpp>
#include <sfc/interface/interface.hpp>
#include <sfc/memory/memory-inline.hpp> #include <sfc/memory/memory-inline.hpp>
#include <sfc/ppu/counter/counter-inline.hpp> #include <sfc/ppu/counter/counter-inline.hpp>
} }
#endif #include <sfc/interface/interface.hpp>

View File

@@ -78,15 +78,13 @@ auto pTextEdit::setCursor(Cursor cursor) -> void {
auto pTextEdit::setEditable(bool editable) -> void { auto pTextEdit::setEditable(bool editable) -> void {
@autoreleasepool { @autoreleasepool {
[[cocoaView content] setEditable:editable]; [[cocoaView content] setEditable:(editable && self().enabled(true))];
} }
} }
auto pTextEdit::setEnabled(bool enabled) -> void { auto pTextEdit::setEnabled(bool enabled) -> void {
pWidget::setEnabled(enabled); pWidget::setEnabled(enabled);
@autoreleasepool { setEditable(self().editable); //Cocoa lacks NSTextView::setEnabled; simulate via setEnabled()
[[cocoaView content] setEnabled:enabled];
}
} }
auto pTextEdit::setFont(const Font& font) -> void { auto pTextEdit::setFont(const Font& font) -> void {