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
#define EMULATOR_HPP
#pragma once
#include <nall/nall.hpp>
#include <nall/dsp.hpp>
@ -7,7 +6,7 @@ using namespace nall;
namespace Emulator {
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 License = "GPLv3";
static const string Website = "http://byuu.org/";
@ -55,5 +54,3 @@ template<typename R, typename... P> struct hook<auto (P...) -> R> {
#endif
using varuint = varuint_t<uint>;
#endif

View File

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

View File

@ -1,5 +1,4 @@
#ifndef FC_HPP
#define FC_HPP
#pragma once
#include <emulator/emulator.hpp>
#include <processor/r6502/r6502.hpp>
@ -53,7 +52,6 @@ namespace Famicom {
#include <fc/ppu/ppu.hpp>
#include <fc/cheat/cheat.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 {
#endif
struct ID {
enum : uint {
@ -58,6 +56,4 @@ private:
extern Interface* interface;
#ifndef FC_HPP
}
#endif

View File

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

View File

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

View File

@ -39,15 +39,42 @@ auto APU::Master::run() -> void {
right >>= 1;
}
auto APU::Master::write(uint r, uint8 data) -> void {
if(r == 0) { //$ff24 NR50
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;
}
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_volume = (data >> 4) & 7;
right_in_enable = data & 0x08;
right_volume = (data >> 0) & 7;
}
if(r == 1) { //$ff25 NR51
if(addr == 0xff25) { //NR51
channel4_left_enable = data & 0x80;
channel3_left_enable = data & 0x40;
channel2_left_enable = data & 0x20;
@ -58,8 +85,15 @@ auto APU::Master::write(uint r, uint8 data) -> void {
channel1_right_enable = data & 0x01;
}
if(r == 2) { //$ff26 NR52
if(addr == 0xff26) { //NR52
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 {
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 serialize(serializer&) -> void;

View File

@ -2,9 +2,14 @@ auto APU::Noise::dac_enable() const -> bool {
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 {
if(period && --period == 0) {
period = divisor << frequency;
period = get_period();
if(frequency < 14) {
bool bit = (lfsr ^ (lfsr >> 1)) & 1;
lfsr = (lfsr >> 1) ^ (bit << (narrow_lfsr ? 6 : 14));
@ -18,7 +23,7 @@ auto APU::Noise::run() -> void {
}
auto APU::Noise::clock_length() -> void {
if(enable && counter) {
if(counter) {
if(++length == 0) enable = false;
}
}
@ -31,27 +36,50 @@ auto APU::Noise::clock_envelope() -> void {
}
}
auto APU::Noise::write(uint r, uint8 data) -> void {
if(r == 1) { //$ff20 NR41
auto APU::Noise::read(uint16 addr) -> uint8 {
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;
}
if(r == 2) { //$ff21 NR42
if(addr == 0xff21) { //NR42
envelope_volume = data >> 4;
envelope_direction = data & 0x08;
envelope_frequency = data & 0x07;
if(dac_enable() == false) enable = false;
}
if(r == 3) { //$ff22 NR43
if(addr == 0xff22) { //NR43
frequency = data >> 4;
narrow_lfsr = data & 0x08;
divisor = (data & 0x07) << 3;
if(divisor == 0) divisor = 4;
period = divisor << frequency;
divisor = data & 0x07;
period = get_period();
}
if(r == 4) { //$ff34 NR44
if(addr == 0xff23) { //NR44
bool initialize = data & 0x80;
counter = data & 0x40;

View File

@ -1,10 +1,12 @@
struct Noise {
auto dac_enable() const -> bool;
auto get_period() const -> uint;
auto run() -> void;
auto clock_length() -> 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 serialize(serializer&) -> void;
@ -16,7 +18,7 @@ struct Noise {
uint3 envelope_frequency;
uint4 frequency;
bool narrow_lfsr;
uint divisor;
uint3 divisor;
bool counter;
int16 output;

View File

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

View File

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

View File

@ -6,7 +6,8 @@ struct Square1 {
auto clock_length() -> void;
auto clock_sweep() -> 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 serialize(serializer&) -> void;

View File

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

View File

@ -4,7 +4,8 @@ struct Square2 {
auto run() -> void;
auto clock_length() -> 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 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 {
if(period && --period == 0) {
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;
output = sample;
}
auto APU::Wave::clock_length() -> void {
if(enable && counter) {
if(counter) {
if(++length == 0) enable = false;
}
}
auto APU::Wave::write(uint r, uint8 data) -> void {
if(r == 0) { //$ff1a NR30
auto APU::Wave::read(uint16 addr) -> uint8 {
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;
if(dac_enable == false) enable = false;
}
if(r == 1) { //$ff1b NR31
if(addr == 0xff1b) { //NR31
length = data;
}
if(r == 2) { //$ff1c NR32
switch((data >> 5) & 3) {
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(addr == 0xff1c) { //NR32
volume = data >> 5;
}
if(r == 3) { //$ff1d NR33
if(addr == 0xff1d) { //NR33
frequency = (frequency & 0x0700) | data;
}
if(r == 4) { //$ff1e NR34
if(addr == 0xff1e) { //NR34
bool initialize = data & 0x80;
counter = data & 0x40;
frequency = ((data & 7) << 8) | (frequency & 0x00ff);
@ -50,25 +78,20 @@ auto APU::Wave::write(uint r, uint8 data) -> void {
pattern_offset = 0;
}
}
}
auto APU::Wave::write_pattern(uint p, uint8 data) -> void {
p <<= 1;
pattern[p + 0] = (data >> 4) & 15;
pattern[p + 1] = (data >> 0) & 15;
if(addr >= 0xff30 && addr <= 0xff3f) {
pattern[addr & 15] = data;
}
}
auto APU::Wave::power() -> void {
enable = 0;
dac_enable = 0;
volume_shift = 0;
volume = 0;
frequency = 0;
counter = 0;
LinearFeedbackShiftRegisterGenerator r;
for(auto& n : pattern) n = r() & 15;
output = 0;
length = 0;
period = 0;
@ -80,7 +103,7 @@ auto APU::Wave::serialize(serializer& s) -> void {
s.integer(enable);
s.integer(dac_enable);
s.integer(volume_shift);
s.integer(volume);
s.integer(frequency);
s.integer(counter);
s.array(pattern);

View File

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

View File

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

View File

@ -36,7 +36,7 @@ struct CPU : Processor::LR35902, Thread, MMIO {
auto hblank() -> void;
struct Status {
uint clock;
uint22 clock;
//$ff00 JOYP
bool p15;
@ -53,7 +53,7 @@ struct CPU : Processor::LR35902, Thread, MMIO {
bool serial_clock;
//$ff04 DIV
uint8 div;
uint16 div;
//$ff05 TIMA
uint8 tima;
@ -109,8 +109,8 @@ struct CPU : Processor::LR35902, Thread, MMIO {
struct OAMDMA {
bool active;
uint clock;
uint8 bank;
uint8 offset;
} oamdma;
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 {
cycle_edge();
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);
}
auto CPU::op_write(uint16 addr, uint8 data) -> void {
cycle_edge();
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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,4 @@
#ifndef GBA_HPP
namespace GameBoyAdvance {
#endif
struct ID {
enum : uint {
@ -55,6 +53,4 @@ private:
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() {
op_io();
op_write(--r[SP], r[x] >> 8);
op_write(--r[SP], r[x] >> 0);
op_io();
}
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() {
op_io();
op_io();
int n = (int8)op_read(r[PC]++);
r.f.z = 0;
r.f.n = 0;
r.f.h = ((r[SP] & 0x0f) + (n & 0x0f)) > 0x0f;
r.f.c = ((r[SP] & 0xff) + (n & 0xff)) > 0xff;
r[SP] += n;
op_io();
op_io();
}
auto LR35902::op_ld_hl_sp_n() {
op_io();
int n = (int8)op_read(r[PC]++);
r.f.z = 0;
r.f.n = 0;
r.f.h = ((r[SP] & 0x0f) + (n & 0x0f)) > 0x0f;
r.f.c = ((r[SP] & 0xff) + (n & 0xff)) > 0xff;
r[HL] = r[SP] + n;
op_io();
}
//rotate/shift commands
@ -618,20 +618,20 @@ template<uint x, bool y> auto LR35902::op_jr_f_n() {
auto LR35902::op_call_nn() {
uint8 lo = 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] >> 0);
r[PC] = (hi << 8) | (lo << 0);
op_io();
}
template<uint x, bool y> auto LR35902::op_call_f_nn() {
uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++);
if(r.f[x] == y) {
op_io();
op_write(--r[SP], r[PC] >> 8);
op_write(--r[SP], r[PC] >> 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() {
op_io();
op_write(--r[SP], r[PC] >> 8);
op_write(--r[SP], r[PC] >> 0);
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 {
}
//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 {
if((addr & 0x60e000) == 0x006000) { //$00-3f,80-bf:6000-7fff
return ram.read(addr & 0x1fff, data);
}
if((addr & 0xf08000) == 0x700000) { //$70-7f:0000-7fff
return ram.read(addr & 0x1fff, data);
}
return data;
return ram.read(addr & 0x1fff, data);
}
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);
}
if((addr & 0xf08000) == 0x700000) { //$70-7f:0000-7fff
return ram.write(addr & 0x1fff, data);
}
return ram.write(addr & 0x1fff, data);
}
}

View File

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

View File

@ -1,5 +1,4 @@
#ifndef SFC_HPP
#define SFC_HPP
#pragma once
#include <emulator/emulator.hpp>
#include <processor/arm/arm.hpp>
@ -71,10 +70,9 @@ namespace SuperFamicom {
#include <sfc/slot/slot.hpp>
#include <sfc/cartridge/cartridge.hpp>
#include <sfc/cheat/cheat.hpp>
#include <sfc/interface/interface.hpp>
#include <sfc/memory/memory-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 {
@autoreleasepool {
[[cocoaView content] setEditable:editable];
[[cocoaView content] setEditable:(editable && self().enabled(true))];
}
}
auto pTextEdit::setEnabled(bool enabled) -> void {
pWidget::setEnabled(enabled);
@autoreleasepool {
[[cocoaView content] setEnabled:enabled];
}
setEditable(self().editable); //Cocoa lacks NSTextView::setEnabled; simulate via setEnabled()
}
auto pTextEdit::setFont(const Font& font) -> void {