From 3d34517f3eb3594dc2cf48821e270f7cc4d29ae4 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Tue, 4 Sep 2018 15:44:35 +1000 Subject: [PATCH] Update to v106r61 release. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit byuu says: This release adds ikari's Cx4 notes to bsnes. It fixes the MMX2 intro's boss fight sequence to be frame perfect to real hardware. It's also very slightly faster than before. I've also added an option to toggle the CPU↔coprocessor cycle synchronization to the emulation settings panel, so you don't have to recompile to get the more accurate SA1 timings. I'm most likely going to default this to disabled in bsnes, and *maybe* enabled in higan out of the box. StaticRAM (wasn't used) and MappedRAM are gone from the Super Famicom core. Instead, there's now ReadableMemory, WritableMemory, and ProtectedMemory (WritableMemory with a toggle for write protection.) Cartridge::loadMap now takes a template Memory object, which bypasses an extra virtual function call on memory accesses, but it doesn't really impact speed much. Whatever. --- higan/emulator/emulator.hpp | 2 +- higan/processor/hg51b/hg51b.cpp | 117 +++++- higan/processor/hg51b/hg51b.hpp | 97 ++++- higan/processor/hg51b/instructions.cpp | 217 ++++++----- higan/processor/hg51b/registers.cpp | 130 ++++--- higan/processor/hg51b/serialization.cpp | 73 +++- higan/processor/wdc65816/instruction.cpp | 1 + higan/sfc/cartridge/cartridge.cpp | 2 - higan/sfc/cartridge/cartridge.hpp | 8 +- higan/sfc/cartridge/load.cpp | 20 +- higan/sfc/coprocessor/event/event.cpp | 5 - higan/sfc/coprocessor/event/event.hpp | 2 +- .../sfc/coprocessor/hitachidsp/hitachidsp.cpp | 37 +- .../sfc/coprocessor/hitachidsp/hitachidsp.hpp | 55 ++- higan/sfc/coprocessor/hitachidsp/memory.cpp | 348 +++++++++++------- .../coprocessor/hitachidsp/serialization.cpp | 14 - higan/sfc/coprocessor/mcc/mcc.cpp | 3 - higan/sfc/coprocessor/mcc/mcc.hpp | 4 +- higan/sfc/coprocessor/obc1/obc1.cpp | 2 - higan/sfc/coprocessor/obc1/obc1.hpp | 2 +- higan/sfc/coprocessor/sa1/bwram.cpp | 6 +- higan/sfc/coprocessor/sa1/iram.cpp | 8 +- higan/sfc/coprocessor/sa1/rom.cpp | 4 +- higan/sfc/coprocessor/sa1/sa1.cpp | 4 - higan/sfc/coprocessor/sa1/sa1.hpp | 6 +- higan/sfc/coprocessor/sdd1/sdd1.cpp | 2 - higan/sfc/coprocessor/sdd1/sdd1.hpp | 2 +- higan/sfc/coprocessor/spc7110/spc7110.cpp | 4 - higan/sfc/coprocessor/spc7110/spc7110.hpp | 6 +- higan/sfc/coprocessor/superfx/superfx.cpp | 3 - higan/sfc/coprocessor/superfx/superfx.hpp | 4 +- higan/sfc/cpu/cpu.hpp | 5 +- higan/sfc/cpu/dma.cpp | 51 +-- higan/sfc/cpu/timing.cpp | 12 +- higan/sfc/interface/configuration.cpp | 1 + higan/sfc/interface/configuration.hpp | 3 + higan/sfc/memory/memory-inline.hpp | 38 -- higan/sfc/memory/memory.cpp | 6 +- higan/sfc/memory/memory.hpp | 64 +--- higan/sfc/memory/protectable.hpp | 48 +++ higan/sfc/memory/readable.hpp | 37 ++ higan/sfc/memory/writable.hpp | 38 ++ higan/sfc/sfc.hpp | 2 - higan/sfc/slot/bsmemory/bsmemory.cpp | 6 +- higan/sfc/slot/bsmemory/bsmemory.hpp | 2 +- higan/sfc/slot/sufamiturbo/sufamiturbo.cpp | 2 - higan/sfc/slot/sufamiturbo/sufamiturbo.hpp | 4 +- higan/target-bsnes/program/hacks.cpp | 11 +- higan/target-bsnes/settings/emulator.cpp | 4 + higan/target-bsnes/settings/settings.cpp | 23 +- higan/target-bsnes/settings/settings.hpp | 4 + nall/inode.hpp | 9 +- 52 files changed, 958 insertions(+), 600 deletions(-) create mode 100644 higan/sfc/memory/protectable.hpp create mode 100644 higan/sfc/memory/readable.hpp create mode 100644 higan/sfc/memory/writable.hpp diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 14b45d1e..29ff2cab 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -28,7 +28,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "106.60"; + static const string Version = "106.61"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org/"; diff --git a/higan/processor/hg51b/hg51b.cpp b/higan/processor/hg51b/hg51b.cpp index 15038909..7e1f5091 100644 --- a/higan/processor/hg51b/hg51b.cpp +++ b/higan/processor/hg51b/hg51b.cpp @@ -7,21 +7,116 @@ namespace Processor { #include "instructions.cpp" #include "serialization.cpp" -auto HG51B::exec(uint24 addr) -> void { - if(regs.halt) return; - addr = addr + regs.pc * 2; - opcode = read(addr++) << 0; - opcode |= read(addr++) << 8; - regs.pc = (regs.pc & 0xffff00) | ((regs.pc + 1) & 0x0000ff); +auto HG51B::lock() -> void { + io.lock = 1; +} + +auto HG51B::halt() -> void { + io.halt = 1; +} + +auto HG51B::wait(uint24 address) -> uint { + if(isROM(address)) return 1 + io.wait.rom; + if(isRAM(address)) return 1 + io.wait.ram; + return 1; +} + +auto HG51B::main() -> void { + if(io.lock) return step(1); + if(io.suspend.enable) return suspend(); + if(io.cache.enable) return cache(); + if(io.dma.enable) return dma(); + if(io.halt) return step(1); + return execute(); +} + +auto HG51B::step(uint clocks) -> void { + if(io.bus.enable) { + if(io.bus.pending > clocks) { + io.bus.pending -= clocks; + } else { + io.bus.enable = 0; + io.bus.pending = 0; + if(io.bus.reading) io.bus.reading = 0, r.busData = read(io.bus.address); + if(io.bus.writing) io.bus.writing = 0, write(io.bus.address, r.busData); + } + } +} + +auto HG51B::execute() -> void { + uint24 address = io.cache.base + r.pb * 512; + if(io.cache.address[io.cache.page] != address) { + io.cache.page ^= 1; + if(io.cache.address[io.cache.page] != address) { + if(io.cache.lock[io.cache.page]) { + io.cache.page ^= 1; + if(io.cache.lock[io.cache.page]) return halt(); + } + cache(); + } + } + + opcode = programRAM[io.cache.page][r.pc]; + advance(); + + step(1); instruction(); } +auto HG51B::advance() -> void { + if(++r.pc == 0) { + if(io.cache.page == 1) return halt(); + io.cache.page = 1; + r.pb = r.p; + } +} + +auto HG51B::suspend() -> void { + if(!io.suspend.duration) return step(1); //indefinite + step(io.suspend.duration); + io.suspend.enable = 0; + io.suspend.duration = 0; +} + +auto HG51B::cache() -> void { + uint24 address = io.cache.base + r.pb * 512; + io.cache.address[io.cache.page] = address; + for(uint offset : range(256)) { + step(wait(address)); programRAM[io.cache.page][offset].byte(0) = read(address++); + step(wait(address)); programRAM[io.cache.page][offset].byte(1) = read(address++); + } + io.cache.enable = 0; +} + +auto HG51B::dma() -> void { + for(uint offset : range(io.dma.length)) { + uint24 source = io.dma.source + offset; + uint24 target = io.dma.target + offset; + + if(isROM(source) && isROM(target)) return lock(); + if(isRAM(source) && isRAM(target)) return lock(); + + step(wait(source)); + auto data = read(source); + + step(wait(target)); + write(target, data); + } + + io.dma.enable = 0; +} + +auto HG51B::running() const -> bool { + return io.cache.enable || io.dma.enable || io.bus.pending || !io.halt; +} + +auto HG51B::busy() const -> bool { + return io.cache.enable || io.dma.enable || io.bus.pending; +} + auto HG51B::power() -> void { - regs.halt = true; - - regs.n = 0; - regs.z = 0; - regs.c = 0; + r = {}; + io = {}; } } diff --git a/higan/processor/hg51b/hg51b.hpp b/higan/processor/hg51b/hg51b.hpp index 94c60ae8..b0b3fb9c 100644 --- a/higan/processor/hg51b/hg51b.hpp +++ b/higan/processor/hg51b/hg51b.hpp @@ -5,14 +5,27 @@ namespace Processor { struct HG51B { - auto exec(uint24 addr) -> void; - virtual auto read(uint24 addr) -> uint8 = 0; - virtual auto write(uint24 addr, uint8 data) -> void = 0; + virtual auto step(uint clocks) -> void; + virtual auto isROM(uint24 address) -> bool = 0; + virtual auto isRAM(uint24 address) -> bool = 0; + virtual auto read(uint24 address) -> uint8 = 0; + virtual auto write(uint24 address, uint8 data) -> void = 0; + virtual auto lock() -> void; + virtual auto halt() -> void; + auto wait(uint24 address) -> uint; + auto main() -> void; + auto execute() -> void; + auto advance() -> void; + auto suspend() -> void; + auto cache() -> void; + auto dma() -> void; + auto running() const -> bool; + auto busy() const -> bool; auto power() -> void; auto serialize(serializer&) -> void; -//uint16 programROM[2][256]; + uint16 programRAM[2][256]; uint24 dataROM[1024]; uint8 dataRAM[3072]; @@ -21,32 +34,76 @@ protected: auto pull() -> void; auto sa() -> uint; auto ri() -> uint; - auto np() -> uint; + auto np() -> void; auto instruction() -> void; //registers.cpp - auto registerRead(uint8 addr) const -> uint24; - auto registerWrite(uint8 addr, uint24 data) -> void; + auto readRegister(uint7 address) -> uint24; + auto writeRegister(uint7 address, uint24 data) -> void; struct Registers { - bool halt; - - uint24 pc; uint16 p; - bool n; - bool z; - bool c; + uint16 pb; //program bank + uint8 pc; //program counter - uint24 a; + boolean n; //negative + boolean z; //zero + boolean c; //carry + boolean i; //interrupt + + uint24 a; //accumulator uint24 acch; uint24 accl; - uint24 busdata; - uint24 romdata; - uint24 ramdata; - uint24 busaddr; - uint24 ramaddr; + uint24 busData; + uint24 romData; + uint24 ramData; + uint24 busAddress; + uint24 ramAddress; uint24 gpr[16]; - } regs; + } r; + + struct IO { + uint1 lock; + uint1 halt = 1; + uint1 irq; //0 = enable, 1 = disable + uint1 rom = 1; //0 = 2 ROMs, 1 = 1 ROM + uint8 vector[32]; + + struct Wait { + uint3 rom = 3; + uint3 ram = 3; + } wait; + + struct Suspend { + uint1 enable; + uint8 duration; + } suspend; + + struct Cache { + uint1 enable; + uint1 page; + uint1 lock[2]; + uint24 address[2]; + uint24 base; + uint16 pb; + uint8 pc; + } cache; + + struct DMA { + uint1 enable; + uint24 source; + uint24 target; + uint16 length; + } dma; + + struct Bus { + uint1 enable; + uint1 reading; + uint1 writing; + uint4 pending; + uint24 address; + } bus; + } io; uint24 stack[8]; uint16 opcode; diff --git a/higan/processor/hg51b/instructions.cpp b/higan/processor/hg51b/instructions.cpp index 58e0d0f8..5028d878 100644 --- a/higan/processor/hg51b/instructions.cpp +++ b/higan/processor/hg51b/instructions.cpp @@ -6,11 +6,11 @@ auto HG51B::push() -> void { stack[3] = stack[2]; stack[2] = stack[1]; stack[1] = stack[0]; - stack[0] = regs.pc; + stack[0] = r.pb << 8 | r.pc << 0; } auto HG51B::pull() -> void { - regs.pc = stack[0]; + auto pc = stack[0]; stack[0] = stack[1]; stack[1] = stack[2]; stack[2] = stack[3]; @@ -19,28 +19,27 @@ auto HG51B::pull() -> void { stack[5] = stack[6]; stack[6] = stack[7]; stack[7] = 0x0000; + + r.pb = pc >> 8; + r.pc = pc >> 0; } //Shift-A: math opcodes can shift A register prior to ALU operation auto HG51B::sa() -> uint { - switch(opcode & 0x0300) { default: - case 0x0000: return regs.a << 0; - case 0x0100: return regs.a << 1; - case 0x0200: return regs.a << 8; - case 0x0300: return regs.a << 16; - } + static const uint shift[] = {0, 1, 8, 16}; + return r.a << shift[opcode.bits(8,9)]; } //Register-or-Immediate: most opcodes can load from a register or immediate auto HG51B::ri() -> uint { - if(opcode & 0x0400) return opcode & 0xff; - return registerRead(opcode & 0xff); + if(opcode.bit(10)) return opcode.bits(0,7); + return readRegister(opcode.bits(0,7)); } //New-PC: determine jump target address; opcode.d9 = long jump flag (1 = yes) -auto HG51B::np() -> uint { - if(opcode & 0x0200) return (regs.p << 8) | (opcode & 0xff); - return (regs.pc & 0xffff00) | (opcode & 0xff); +auto HG51B::np() -> void { + if(opcode.bit(9)) r.pb = r.p; + r.pc = opcode.bits(0,7); } auto HG51B::instruction() -> void { @@ -53,300 +52,322 @@ auto HG51B::instruction() -> void { //00.0 10.0 .... .... //jump i if(opcode & 0x2000) push(); - regs.pc = np(); + np(); + step(2); } else if((opcode & 0xdd00) == 0x0c00) { //00.0 11.0 .... .... //jumpeq i - if(regs.z) { + if(r.z) { if(opcode & 0x2000) push(); - regs.pc = np(); + np(); + step(2); } } else if((opcode & 0xdd00) == 0x1000) { //00.1 00.0 .... .... //jumpge i - if(regs.c) { + if(r.c) { if(opcode & 0x2000) push(); - regs.pc = np(); + np(); + step(2); } } else if((opcode & 0xdd00) == 0x1400) { //00.1 01.0 .... .... //jumpmi i - if(regs.n) { + if(r.n) { if(opcode & 0x2000) push(); - regs.pc = np(); + np(); + step(2); } } else if((opcode & 0xffff) == 0x1c00) { //0001 1100 0000 0000 - //loop? + //wait + if(io.bus.enable) step(io.bus.pending); } else if((opcode & 0xfffe) == 0x2500) { //0010 0101 0000 000. //skiplt/skipge - if(regs.c == (opcode & 1)) regs.pc++; + if(r.c == (opcode & 1)) { + advance(); + step(1); + } } else if((opcode & 0xfffe) == 0x2600) { //0010 0110 0000 000. //skipne/skipeq - if(regs.z == (opcode & 1)) regs.pc++; + if(r.z == (opcode & 1)) { + advance(); + step(1); + } } else if((opcode & 0xfffe) == 0x2700) { //0010 0111 0000 000. //skipmi/skippl - if(regs.n == (opcode & 1)) regs.pc++; + if(r.n == (opcode & 1)) { + advance(); + step(1); + } } else if((opcode & 0xffff) == 0x3c00) { //0011 1100 0000 0000 //ret pull(); + step(2); } else if((opcode & 0xffff) == 0x4000) { //0100 0000 0000 0000 - //rdbus - regs.busdata = read(regs.busaddr++); + //??? + r.busAddress++; } else if((opcode & 0xf800) == 0x4800) { //0100 1... .... .... //cmpr a<= 0; + r.n = result & 0x800000; + r.z = (uint24)result == 0; + r.c = result >= 0; } else if((opcode & 0xf800) == 0x5000) { //0101 0... .... .... //cmp a<= 0; + r.n = result & 0x800000; + r.z = (uint24)result == 0; + r.c = result >= 0; } else if((opcode & 0xfb00) == 0x5900) { //0101 1.01 .... .... //sxb - regs.a = (int8)ri(); + r.a = (int8)ri(); } else if((opcode & 0xfb00) == 0x5a00) { //0101 1.10 .... .... //sxw - regs.a = (int16)ri(); + r.a = (int16)ri(); } else if((opcode & 0xfb00) == 0x6000) { //0110 0.00 .... .... //ld a,ri - regs.a = ri(); + r.a = ri(); } else if((opcode & 0xfb00) == 0x6100) { //0110 0.01 .... .... - //ld ?,ri + //ld bus,ri + r.busData = ri(); } else if((opcode & 0xfb00) == 0x6300) { //0110 0.11 .... .... //ld p,ri - regs.p = ri(); + r.p = ri(); } else if((opcode & 0xfb00) == 0x6800) { //0110 1.00 .... .... //rdraml - uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); - if(target < 0xc00) regs.ramdata = (regs.ramdata & 0xffff00) | (dataRAM[target] << 0); + uint24 target = ri() + (opcode.bit(10) ? r.ramAddress : (uint24)0); + if(target < 0xc00) r.ramData.byte(0) = dataRAM[target]; } else if((opcode & 0xfb00) == 0x6900) { //0110 1.01 .... .... //rdramh - uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); - if(target < 0xc00) regs.ramdata = (regs.ramdata & 0xff00ff) | (dataRAM[target] << 8); + uint24 target = ri() + (opcode.bit(10) ? r.ramAddress : (uint24)0); + if(target < 0xc00) r.ramData.byte(1) = dataRAM[target]; } else if((opcode & 0xfb00) == 0x6a00) { //0110 1.10 .... .... //rdramb - uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); - if(target < 0xc00) regs.ramdata = (regs.ramdata & 0x00ffff) | (dataRAM[target] << 16); + uint24 target = ri() + (opcode.bit(10) ? r.ramAddress : (uint24)0); + if(target < 0xc00) r.ramData.byte(2) = dataRAM[target]; } else if((opcode & 0xffff) == 0x7000) { //0111 0000 0000 0000 //rdrom - regs.romdata = dataROM[regs.a & 0x3ff]; + r.romData = dataROM[(uint10)r.a]; } else if((opcode & 0xff00) == 0x7c00) { //0111 1100 .... .... //ld pl,i - regs.p = (regs.p & 0xff00) | ((opcode & 0xff) << 0); + r.p.byte(0) = opcode.bits(0,7); } else if((opcode & 0xff00) == 0x7d00) { //0111 1101 .... .... //ld ph,i - regs.p = (regs.p & 0x00ff) | ((opcode & 0xff) << 8); + r.p.byte(1) = opcode.bits(0,7); } else if((opcode & 0xf800) == 0x8000) { //1000 0... .... .... //add a< 0xffffff; + r.a = result; + r.n = r.a & 0x800000; + r.z = r.a == 0; + r.c = result > 0xffffff; } else if((opcode & 0xf800) == 0x8800) { //1000 1... .... .... //subr a<= 0; + r.a = result; + r.n = r.a & 0x800000; + r.z = r.a == 0; + r.c = result >= 0; } else if((opcode & 0xf800) == 0x9000) { //1001 0... .... .... //sub a<= 0; + r.a = result; + r.n = r.a & 0x800000; + r.z = r.a == 0; + r.c = result >= 0; } else if((opcode & 0xfb00) == 0x9800) { //1001 1.00 .... .... //mul a,ri - int64 x = (int24)regs.a; + int64 x = (int24)r.a; int64 y = (int24)ri(); x *= y; - regs.accl = x >> 0ull; - regs.acch = x >> 24ull; - regs.n = regs.acch & 0x800000; - regs.z = x == 0; + r.accl = x >> 0ull; + r.acch = x >> 24ull; + r.n = r.acch & 0x800000; + r.z = x == 0; } else if((opcode & 0xf800) == 0xa800) { //1010 1... .... .... //xor a<> ri(); - regs.n = regs.a & 0x800000; - regs.z = regs.a == 0; + r.a = r.a >> ri(); + r.n = r.a & 0x800000; + r.z = r.a == 0; } else if((opcode & 0xfb00) == 0xc800) { //1100 1.00 .... .... //asr a,ri - regs.a = (int24)regs.a >> ri(); - regs.n = regs.a & 0x800000; - regs.z = regs.a == 0; + r.a = (int24)r.a >> ri(); + r.n = r.a & 0x800000; + r.z = r.a == 0; } else if((opcode & 0xfb00) == 0xd000) { //1101 0.00 .... .... //ror a,ri uint24 length = ri(); - regs.a = (regs.a >> length) | (regs.a << (24 - length)); - regs.n = regs.a & 0x800000; - regs.z = regs.a == 0; + r.a = (r.a >> length) | (r.a << (24 - length)); + r.n = r.a & 0x800000; + r.z = r.a == 0; } else if((opcode & 0xfb00) == 0xd800) { //1101 1.00 .... .... //shl a,ri - regs.a = regs.a << ri(); - regs.n = regs.a & 0x800000; - regs.z = regs.a == 0; + r.a = r.a << ri(); + r.n = r.a & 0x800000; + r.z = r.a == 0; } else if((opcode & 0xff00) == 0xe000) { //1110 0000 .... .... //st r,a - registerWrite(opcode & 0xff, regs.a); + writeRegister(opcode & 0xff, r.a); + } + + else if((opcode & 0xff00) == 0xe100) { + //1110 0001 .... .... + //st r,bus + writeRegister(opcode & 0xff, r.busData); } else if((opcode & 0xfb00) == 0xe800) { //1110 1.00 .... .... //wrraml - uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); - if(target < 0xc00) dataRAM[target] = regs.ramdata >> 0; + uint24 target = ri() + (opcode.bit(10) ? r.ramAddress : (uint24)0); + if(target < 0xc00) dataRAM[target] = r.ramData.byte(0); } else if((opcode & 0xfb00) == 0xe900) { //1110 1.01 .... .... //wrramh - uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); - if(target < 0xc00) dataRAM[target] = regs.ramdata >> 8; + uint24 target = ri() + (opcode.bit(10) ? r.ramAddress : (uint24)0); + if(target < 0xc00) dataRAM[target] = r.ramData.byte(1); } else if((opcode & 0xfb00) == 0xea00) { //1110 1.10 .... .... //wrramb - uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); - if(target < 0xc00) dataRAM[target] = regs.ramdata >> 16; + uint24 target = ri() + (opcode.bit(10) ? r.ramAddress : (uint24)0); + if(target < 0xc00) dataRAM[target] = r.ramData.byte(2); } else if((opcode & 0xff00) == 0xf000) { //1111 0000 .... .... //swap a,r - uint24 source = registerRead(opcode & 0xff); - uint24 target = regs.a; - regs.a = source; - registerWrite(opcode & 0xff, target); + uint24 source = readRegister(opcode & 0xff); + uint24 target = r.a; + r.a = source; + writeRegister(opcode & 0xff, target); } else if((opcode & 0xffff) == 0xfc00) { //1111 1100 0000 0000 //halt - regs.halt = true; + halt(); } else { - print("Hitachi DSP: unknown opcode @ ", hex(regs.pc - 1, 4L), " = ", hex(opcode, 4L), "\n"); - regs.halt = true; + print("Hitachi DSP: unknown opcode @ ", hex((r.pb << 8 | r.pc << 0) - 1, 6L), " = ", hex(opcode, 4L), "\n"); + halt(); } } diff --git a/higan/processor/hg51b/registers.cpp b/higan/processor/hg51b/registers.cpp index 2a3ae5d5..b7362748 100644 --- a/higan/processor/hg51b/registers.cpp +++ b/higan/processor/hg51b/registers.cpp @@ -1,13 +1,26 @@ -auto HG51B::registerRead(uint8 addr) const -> uint24 { - switch(addr) { - case 0x00: return regs.a; - case 0x01: return regs.acch; - case 0x02: return regs.accl; - case 0x03: return regs.busdata; - case 0x08: return regs.romdata; - case 0x0c: return regs.ramdata; - case 0x13: return regs.busaddr; - case 0x1c: return regs.ramaddr; +auto HG51B::readRegister(uint7 address) -> uint24 { + switch(address) { + case 0x00: return r.a; + case 0x01: return r.acch; + case 0x02: return r.accl; + case 0x03: return r.busData; + case 0x08: return r.romData; + case 0x0c: return r.ramData; + case 0x13: return r.busAddress; + case 0x1c: return r.ramAddress; + case 0x20: return r.pc; + case 0x2e: + io.bus.enable = 1; + io.bus.reading = 1; + io.bus.pending = 1 + io.wait.rom; + io.bus.address = r.busAddress; + return 0x000000; + case 0x2f: + io.bus.enable = 1; + io.bus.reading = 1; + io.bus.pending = 1 + io.wait.ram; + io.bus.address = r.busAddress; + return 0x000000; case 0x50: return 0x000000; case 0x51: return 0xffffff; case 0x52: return 0x00ff00; @@ -24,51 +37,64 @@ auto HG51B::registerRead(uint8 addr) const -> uint24 { case 0x5d: return 0xfeffff; case 0x5e: return 0x000100; case 0x5f: return 0x00feff; - case 0x60: return regs.gpr[ 0]; - case 0x61: return regs.gpr[ 1]; - case 0x62: return regs.gpr[ 2]; - case 0x63: return regs.gpr[ 3]; - case 0x64: return regs.gpr[ 4]; - case 0x65: return regs.gpr[ 5]; - case 0x66: return regs.gpr[ 6]; - case 0x67: return regs.gpr[ 7]; - case 0x68: return regs.gpr[ 8]; - case 0x69: return regs.gpr[ 9]; - case 0x6a: return regs.gpr[10]; - case 0x6b: return regs.gpr[11]; - case 0x6c: return regs.gpr[12]; - case 0x6d: return regs.gpr[13]; - case 0x6e: return regs.gpr[14]; - case 0x6f: return regs.gpr[15]; + case 0x60: case 0x70: return r.gpr[ 0]; + case 0x61: case 0x71: return r.gpr[ 1]; + case 0x62: case 0x72: return r.gpr[ 2]; + case 0x63: case 0x73: return r.gpr[ 3]; + case 0x64: case 0x74: return r.gpr[ 4]; + case 0x65: case 0x75: return r.gpr[ 5]; + case 0x66: case 0x76: return r.gpr[ 6]; + case 0x67: case 0x77: return r.gpr[ 7]; + case 0x68: case 0x78: return r.gpr[ 8]; + case 0x69: case 0x79: return r.gpr[ 9]; + case 0x6a: case 0x7a: return r.gpr[10]; + case 0x6b: case 0x7b: return r.gpr[11]; + case 0x6c: case 0x7c: return r.gpr[12]; + case 0x6d: case 0x7d: return r.gpr[13]; + case 0x6e: case 0x7e: return r.gpr[14]; + case 0x6f: case 0x7f: return r.gpr[15]; } return 0x000000; } -auto HG51B::registerWrite(uint8 addr, uint24 data) -> void { - switch(addr) { - case 0x00: regs.a = data; return; - case 0x01: regs.acch = data; return; - case 0x02: regs.accl = data; return; - case 0x03: regs.busdata = data; return; - case 0x08: regs.romdata = data; return; - case 0x0c: regs.ramdata = data; return; - case 0x13: regs.busaddr = data; return; - case 0x1c: regs.ramaddr = data; return; - case 0x60: regs.gpr[ 0] = data; return; - case 0x61: regs.gpr[ 1] = data; return; - case 0x62: regs.gpr[ 2] = data; return; - case 0x63: regs.gpr[ 3] = data; return; - case 0x64: regs.gpr[ 4] = data; return; - case 0x65: regs.gpr[ 5] = data; return; - case 0x66: regs.gpr[ 6] = data; return; - case 0x67: regs.gpr[ 7] = data; return; - case 0x68: regs.gpr[ 8] = data; return; - case 0x69: regs.gpr[ 9] = data; return; - case 0x6a: regs.gpr[10] = data; return; - case 0x6b: regs.gpr[11] = data; return; - case 0x6c: regs.gpr[12] = data; return; - case 0x6d: regs.gpr[13] = data; return; - case 0x6e: regs.gpr[14] = data; return; - case 0x6f: regs.gpr[15] = data; return; +auto HG51B::writeRegister(uint7 address, uint24 data) -> void { + switch(address) { + case 0x00: r.a = data; return; + case 0x01: r.acch = data; return; + case 0x02: r.accl = data; return; + case 0x03: r.busData = data; return; + case 0x08: r.romData = data; return; + case 0x0c: r.ramData = data; return; + case 0x13: r.busAddress = data; return; + case 0x1c: r.ramAddress = data; return; + case 0x20: r.pc = data; return; + case 0x2e: + io.bus.enable = 1; + io.bus.writing = 1; + io.bus.pending = 1 + io.wait.rom; + io.bus.address = r.busAddress; + return; + case 0x2f: + io.bus.enable = 1; + io.bus.writing = 1; + io.bus.pending = 1 + io.wait.ram; + io.bus.address = r.busAddress; + return; + case 0x60: case 0x70: r.gpr[ 0] = data; return; + case 0x61: case 0x71: r.gpr[ 1] = data; return; + case 0x62: case 0x72: r.gpr[ 2] = data; return; + case 0x63: case 0x73: r.gpr[ 3] = data; return; + case 0x64: case 0x74: r.gpr[ 4] = data; return; + case 0x65: case 0x75: r.gpr[ 5] = data; return; + case 0x66: case 0x76: r.gpr[ 6] = data; return; + case 0x67: case 0x77: r.gpr[ 7] = data; return; + case 0x68: case 0x78: r.gpr[ 8] = data; return; + case 0x69: case 0x79: r.gpr[ 9] = data; return; + case 0x6a: case 0x7a: r.gpr[10] = data; return; + case 0x6b: case 0x7b: r.gpr[11] = data; return; + case 0x6c: case 0x7c: r.gpr[12] = data; return; + case 0x6d: case 0x7d: r.gpr[13] = data; return; + case 0x6e: case 0x7e: r.gpr[14] = data; return; + case 0x6f: case 0x7f: r.gpr[15] = data; return; } } diff --git a/higan/processor/hg51b/serialization.cpp b/higan/processor/hg51b/serialization.cpp index b79cc7c3..74f148d3 100644 --- a/higan/processor/hg51b/serialization.cpp +++ b/higan/processor/hg51b/serialization.cpp @@ -1,23 +1,58 @@ auto HG51B::serialize(serializer& s) -> void { + s.array(programRAM[0]); + s.array(programRAM[1]); s.array(dataRAM); - for(auto& n : stack) s.integer(n); + + s.integer(r.p); + s.integer(r.pb); + s.integer(r.pc); + + s.boolean(r.n); + s.boolean(r.z); + s.boolean(r.c); + s.boolean(r.i); + + s.integer(r.a); + s.integer(r.acch); + s.integer(r.accl); + s.integer(r.busData); + s.integer(r.romData); + s.integer(r.ramData); + s.integer(r.busAddress); + s.integer(r.ramAddress); + s.array(r.gpr); + + s.integer(io.lock); + s.integer(io.halt); + s.integer(io.irq); + s.integer(io.rom); + s.array(io.vector); + + s.integer(io.wait.rom); + s.integer(io.wait.ram); + + s.integer(io.suspend.enable); + s.integer(io.suspend.duration); + + s.integer(io.cache.enable); + s.integer(io.cache.page); + s.array(io.cache.lock); + s.array(io.cache.address); + s.integer(io.cache.base); + s.integer(io.cache.pb); + s.integer(io.cache.pc); + + s.integer(io.dma.enable); + s.integer(io.dma.source); + s.integer(io.dma.target); + s.integer(io.dma.length); + + s.integer(io.bus.enable); + s.integer(io.bus.reading); + s.integer(io.bus.writing); + s.integer(io.bus.pending); + s.integer(io.bus.address); + + s.array(stack); s.integer(opcode); - - s.integer(regs.halt); - - s.integer(regs.pc); - s.integer(regs.p); - s.integer(regs.n); - s.integer(regs.z); - s.integer(regs.c); - - s.integer(regs.a); - s.integer(regs.acch); - s.integer(regs.accl); - s.integer(regs.busdata); - s.integer(regs.romdata); - s.integer(regs.ramdata); - s.integer(regs.busaddr); - s.integer(regs.ramaddr); - for(auto& n : regs.gpr) s.integer(n); } diff --git a/higan/processor/wdc65816/instruction.cpp b/higan/processor/wdc65816/instruction.cpp index 75ac81d8..8d56b7c0 100644 --- a/higan/processor/wdc65816/instruction.cpp +++ b/higan/processor/wdc65816/instruction.cpp @@ -10,6 +10,7 @@ N push(db(PC)); lo(PC) = read(r.vector + 0); hi(PC) = read(r.vector + 1); db(PC) = 0x00; + idleJump(); } //both the accumulator and index registers can independently be in either 8-bit or 16-bit mode. diff --git a/higan/sfc/cartridge/cartridge.cpp b/higan/sfc/cartridge/cartridge.cpp index 0e6db152..8bd5572e 100644 --- a/higan/sfc/cartridge/cartridge.cpp +++ b/higan/sfc/cartridge/cartridge.cpp @@ -98,8 +98,6 @@ auto Cartridge::load() -> bool { information.sha256 = sha.digest(); } - rom.writeProtect(true); - ram.writeProtect(false); return true; } diff --git a/higan/sfc/cartridge/cartridge.hpp b/higan/sfc/cartridge/cartridge.hpp index 5d937849..741727b7 100644 --- a/higan/sfc/cartridge/cartridge.hpp +++ b/higan/sfc/cartridge/cartridge.hpp @@ -12,8 +12,8 @@ struct Cartridge { auto serialize(serializer&) -> void; - MappedRAM rom; - MappedRAM ram; + ReadableMemory rom; + WritableMemory ram; struct Information { uint pathID = 0; @@ -67,8 +67,8 @@ private: auto loadCartridgeSufamiTurboB(Markup::Node) -> void; auto loadMemory(Memory&, Markup::Node, bool required) -> void; - auto loadMap(Markup::Node, SuperFamicom::Memory&) -> void; - auto loadMap(Markup::Node, const function&, const function&) -> void; + template auto loadMap(Markup::Node, T&) -> uint; + auto loadMap(Markup::Node, const function&, const function&) -> uint; auto loadROM(Markup::Node) -> void; auto loadRAM(Markup::Node) -> void; diff --git a/higan/sfc/cartridge/load.cpp b/higan/sfc/cartridge/load.cpp index 0765926e..741188ad 100644 --- a/higan/sfc/cartridge/load.cpp +++ b/higan/sfc/cartridge/load.cpp @@ -130,26 +130,27 @@ auto Cartridge::loadMemory(Memory& ram, Markup::Node node, bool required) -> voi } } -auto Cartridge::loadMap(Markup::Node map, SuperFamicom::Memory& memory) -> void { +template //T = ReadableMemory, WritableMemory, ProtectableMemory +auto Cartridge::loadMap(Markup::Node map, T& memory) -> uint { auto addr = map["address"].text(); auto size = map["size"].natural(); auto base = map["base"].natural(); auto mask = map["mask"].natural(); if(size == 0) size = memory.size(); - if(size == 0) return; - bus.map({&SuperFamicom::Memory::read, &memory}, {&SuperFamicom::Memory::write, &memory}, addr, size, base, mask); + if(size == 0) return print("loadMap(): size=0\n"), 0; //does this ever actually occur? + return bus.map({&T::read, &memory}, {&T::write, &memory}, addr, size, base, mask); } auto Cartridge::loadMap( Markup::Node map, const function& reader, const function& writer -) -> void { +) -> uint { auto addr = map["address"].text(); auto size = map["size"].natural(); auto base = map["base"].natural(); auto mask = map["mask"].natural(); - bus.map(reader, writer, addr, size, base, mask); + return bus.map(reader, writer, addr, size, base, mask); } //memory(type=ROM,content=Program) @@ -416,22 +417,23 @@ auto Cartridge::loadHitachiDSP(Markup::Node node, uint roms) -> void { hitachidsp.Frequency = 20'000'000; } hitachidsp.Roms = roms; //1 or 2 + hitachidsp.Mapping = 0; //0 or 1 for(auto map : node.find("map")) { - loadMap(map, {&HitachiDSP::dspRead, &hitachidsp}, {&HitachiDSP::dspWrite, &hitachidsp}); + loadMap(map, {&HitachiDSP::readIO, &hitachidsp}, {&HitachiDSP::writeIO, &hitachidsp}); } if(auto memory = node["memory(type=ROM,content=Program)"]) { loadMemory(hitachidsp.rom, memory, File::Required); for(auto map : memory.find("map")) { - loadMap(map, {&HitachiDSP::romRead, &hitachidsp}, {&HitachiDSP::romWrite, &hitachidsp}); + loadMap(map, {&HitachiDSP::readROM, &hitachidsp}, {&HitachiDSP::writeROM, &hitachidsp}); } } if(auto memory = node["memory(type=RAM,content=Save)"]) { loadMemory(hitachidsp.ram, memory, File::Optional); for(auto map : memory.find("map")) { - loadMap(map, {&HitachiDSP::ramRead, &hitachidsp}, {&HitachiDSP::ramWrite, &hitachidsp}); + loadMap(map, {&HitachiDSP::readRAM, &hitachidsp}, {&HitachiDSP::writeRAM, &hitachidsp}); } } @@ -450,7 +452,7 @@ auto Cartridge::loadHitachiDSP(Markup::Node node, uint roms) -> void { } } for(auto map : memory.find("map")) { - loadMap(map, {&HitachiDSP::dramRead, &hitachidsp}, {&HitachiDSP::dramWrite, &hitachidsp}); + loadMap(map, {&HitachiDSP::readDRAM, &hitachidsp}, {&HitachiDSP::writeDRAM, &hitachidsp}); } } } diff --git a/higan/sfc/coprocessor/event/event.cpp b/higan/sfc/coprocessor/event/event.cpp index 077dbe20..42bd2e55 100644 --- a/higan/sfc/coprocessor/event/event.cpp +++ b/higan/sfc/coprocessor/event/event.cpp @@ -39,11 +39,6 @@ auto Event::unload() -> void { auto Event::power() -> void { create(Event::Enter, 1); - rom[0].writeProtect(true); - rom[1].writeProtect(true); - rom[2].writeProtect(true); - rom[3].writeProtect(true); - //DIP switches 0-3 control the time: 3 minutes + 0-15 extra minutes timer = (3 + dip.value.bits(0,3)) * 60; //in seconds //DIP switches 4-5 serve an unknown purpose diff --git a/higan/sfc/coprocessor/event/event.hpp b/higan/sfc/coprocessor/event/event.hpp index 254e7f05..1ecf7a89 100644 --- a/higan/sfc/coprocessor/event/event.hpp +++ b/higan/sfc/coprocessor/event/event.hpp @@ -30,7 +30,7 @@ struct Event : Thread { auto serialize(serializer&) -> void; public: - MappedRAM rom[4]; + ReadableMemory rom[4]; enum class Board : uint { Unknown, CampusChallenge92, PowerFest94 } board; uint timer; diff --git a/higan/sfc/coprocessor/hitachidsp/hitachidsp.cpp b/higan/sfc/coprocessor/hitachidsp/hitachidsp.cpp index ed164e1c..7ea055d2 100644 --- a/higan/sfc/coprocessor/hitachidsp/hitachidsp.cpp +++ b/higan/sfc/coprocessor/hitachidsp/hitachidsp.cpp @@ -10,21 +10,17 @@ auto HitachiDSP::Enter() -> void { while(true) scheduler.synchronize(), hitachidsp.main(); } -auto HitachiDSP::main() -> void { - if(mmio.dma) { - for(auto n : range(mmio.dmaLength)) { - write(mmio.dmaTarget + n, read(mmio.dmaSource + n)); - step(2); - synchronize(cpu); - } - mmio.dma = false; - } - - exec(mmio.programOffset); - step(1); +auto HitachiDSP::step(uint clocks) -> void { + HG51B::step(clocks); + Thread::step(clocks); synchronize(cpu); } +auto HitachiDSP::halt() -> void { + HG51B::halt(); + if(io.irq == 0) r.i = 1, cpu.r.irq = 1; +} + auto HitachiDSP::unload() -> void { rom.reset(); ram.reset(); @@ -33,23 +29,6 @@ auto HitachiDSP::unload() -> void { auto HitachiDSP::power() -> void { HG51B::power(); create(HitachiDSP::Enter, Frequency); - - rom.writeProtect(true); - ram.writeProtect(false); - - mmio.dma = false; - - mmio.dmaSource = 0x000000; - mmio.dmaLength = 0x0000; - mmio.dmaTarget = 0x000000; - mmio.r1f48 = 0x00; - mmio.programOffset = 0x000000; - mmio.r1f4c = 0x00; - mmio.pageNumber = 0x0000; - mmio.programCounter = 0x00; - mmio.r1f50 = 0x33; - mmio.r1f51 = 0x00; - mmio.r1f52 = 0x01; } } diff --git a/higan/sfc/coprocessor/hitachidsp/hitachidsp.hpp b/higan/sfc/coprocessor/hitachidsp/hitachidsp.hpp index 9e23d858..5ae9260f 100644 --- a/higan/sfc/coprocessor/hitachidsp/hitachidsp.hpp +++ b/higan/sfc/coprocessor/hitachidsp/hitachidsp.hpp @@ -1,55 +1,48 @@ struct HitachiDSP : Processor::HG51B, Thread { - MappedRAM rom; - MappedRAM ram; + ReadableMemory rom; + WritableMemory ram; + //hitachidsp.cpp static auto Enter() -> void; - auto main() -> void; + auto step(uint clocks) -> void override; + auto halt() -> void override; auto unload() -> void; auto power() -> void; + auto isROM(uint24 address) -> bool override; + auto isRAM(uint24 address) -> bool override; + //HG51B read/write - auto read(uint24 addr) -> uint8 override; - auto write(uint24 addr, uint8 data) -> void override; + auto read(uint24 address) -> uint8 override; + auto write(uint24 address, uint8 data) -> void override; //CPU ROM read/write - auto romRead(uint24 addr, uint8 data) -> uint8; - auto romWrite(uint24 addr, uint8 data) -> void; + auto addressROM(uint24 address) const -> maybe; + auto readROM(uint24 address, uint8 data = 0) -> uint8; + auto writeROM(uint24 address, uint8 data) -> void; //CPU RAM read/write - auto ramRead(uint24 addr, uint8 data) -> uint8; - auto ramWrite(uint24 addr, uint8 data) -> void; + auto addressRAM(uint24 address) const -> maybe; + auto readRAM(uint24 address, uint8 data = 0) -> uint8; + auto writeRAM(uint24 address, uint8 data) -> void; //HG51B data RAM read/write - auto dramRead(uint24 addr, uint8 data) -> uint8; - auto dramWrite(uint24 addr, uint8 data) -> void; + auto addressDRAM(uint24 address) const -> maybe; + auto readDRAM(uint24 address, uint8 data = 0) -> uint8; + auto writeDRAM(uint24 address, uint8 data) -> void; - //CPU MMIO read/write - auto dspRead(uint24 addr, uint8 data) -> uint8; - auto dspWrite(uint24 addr, uint8 data) -> void; + //CPU IO read/write + auto addressIO(uint24 address) const -> maybe; + auto readIO(uint24 address, uint8 data = 0) -> uint8; + auto writeIO(uint24 address, uint8 data) -> void; auto firmware() const -> vector; auto serialize(serializer&) -> void; uint Frequency; uint Roms; - - struct MMIO { - bool dma; //true during DMA transfers - - uint24 dmaSource; //$1f40-$1f42 - uint24 dmaLength; //$1f43-$1f44 - uint24 dmaTarget; //$1f45-$1f47 - uint8 r1f48; //$1f48 - uint24 programOffset; //$1f49-$1f4b - uint8 r1f4c; //$1f4c - uint16 pageNumber; //$1f4d-$1f4e - uint8 programCounter; //$1f4f - uint8 r1f50; //$1f50 - uint8 r1f51; //$1f51 - uint8 r1f52; //$1f52 - uint8 vector[32]; //$1f60-$1f7f - } mmio; + bool Mapping; }; extern HitachiDSP hitachidsp; diff --git a/higan/sfc/coprocessor/hitachidsp/memory.cpp b/higan/sfc/coprocessor/hitachidsp/memory.cpp index 167c9f33..4c180cff 100644 --- a/higan/sfc/coprocessor/hitachidsp/memory.cpp +++ b/higan/sfc/coprocessor/hitachidsp/memory.cpp @@ -1,166 +1,264 @@ -auto HitachiDSP::read(uint24 addr) -> uint8 { - if((addr & 0x40ec00) == 0x006c00) { //$00-3f,80-bf:6c00-6cff,7c00-7cff - return dspRead(addr, 0x00); - } - if((addr & 0x40e000) == 0x006000) { //$00-3f,80-bf:6000-6bff,7000-7bff - return dramRead(addr, 0x00); - } - if((addr & 0x408000) == 0x008000) { //$00-3f,80-bf:8000-ffff - if(rom.size() == 0) return 0x00; - addr = ((addr & 0x3f0000) >> 1) | (addr & 0x7fff); - addr = Bus::mirror(addr, rom.size()); - return rom.read(addr, 0); - } - if((addr & 0xf88000) == 0x700000) { //$70-77:0000-7fff - if(ram.size() == 0) return 0x00; - addr = ((addr & 0x070000) >> 1) | (addr & 0x7fff); - addr = Bus::mirror(addr, ram.size()); - return ram.read(addr); - } +auto HitachiDSP::isROM(uint24 address) -> bool { + return (bool)addressROM(address); +} + +auto HitachiDSP::isRAM(uint24 address) -> bool { + return (bool)addressRAM(address); +} + +auto HitachiDSP::read(uint24 address) -> uint8 { + if(auto linear = addressROM (address)) return readROM (*linear); + if(auto linear = addressRAM (address)) return readRAM (*linear); + if(auto linear = addressDRAM(address)) return readDRAM(*linear); + if(auto linear = addressIO (address)) return readIO (*linear); return 0x00; } -auto HitachiDSP::write(uint24 addr, uint8 data) -> void { - if((addr & 0x40ec00) == 0x006c00) { //$00-3f,80-bf:6c00-6fff,7c00-7fff - return dspWrite(addr, data); - } - if((addr & 0x40e000) == 0x006000) { //$00-3f,80-bf:6000-6bff,7000-7bff - return dramWrite(addr, data); - } - if((addr & 0xf88000) == 0x700000) { //$70-77:0000-7fff - if(ram.size() == 0) return; - addr = ((addr & 0x070000) >> 1) | (addr & 0x7fff); - addr = Bus::mirror(addr, ram.size()); - return ram.write(addr, data); - } +auto HitachiDSP::write(uint24 address, uint8 data) -> void { + if(auto linear = addressROM (address)) return writeROM (*linear, data); + if(auto linear = addressRAM (address)) return writeRAM (*linear, data); + if(auto linear = addressDRAM(address)) return writeDRAM(*linear, data); + if(auto linear = addressIO (address)) return writeIO (*linear, data); } -auto HitachiDSP::romRead(uint24 addr, uint8 data) -> uint8 { - if(hitachidsp.active() || regs.halt) { - addr = Bus::mirror(addr, rom.size()); - //if(Roms == 2 && mmio.r1f52 == 1 && addr >= (bit::round(rom.size()) >> 1)) return 0x00; - return rom.read(addr, data); +// + +auto HitachiDSP::addressROM(uint24 address) const -> maybe { + if(Mapping == 0) { + //00-3f,80-bf:8000-ffff; c0-ff:0000-ffff + if((address & 0x408000) == 0x008000 || (address & 0xc00000) == 0xc00000) { + address = (address & 0x3f0000) >> 1 | (address & 0x7fff); + return {address & 0x1fffff}; + } + } else { + //00-3f,80-bf:8000-ffff; c0-ff:0000-ffff + if((address & 0x408000) == 0x008000 || (address & 0xc00000) == 0xc00000) { + return {address & 0x3fffff}; + } } - if((addr & 0x40ffe0) == 0x00ffe0) return mmio.vector[addr & 0x1f]; + return {}; +} + +auto HitachiDSP::readROM(uint24 address, uint8 data) -> uint8 { + if(hitachidsp.active() || !busy()) { + address = bus.mirror(address, rom.size()); + //if(Roms == 2 && mmio.r1f52 == 1 && address >= (bit::round(rom.size()) >> 1)) return 0x00; + return rom.read(address, data); + } + //DSP has the bus acquired: CPU reads from 00:ffc0-ffff return IO registers (including reset vector overrides) + if(Mapping == 0 && (address & 0xbfffc0) == 0x007fc0) return readIO(0x7f40 | address & 0x3f); + if(Mapping == 1 && (address & 0xbfffc0) == 0x00ffc0) return readIO(0x7f40 | address & 0x3f); return data; } -auto HitachiDSP::romWrite(uint24 addr, uint8 data) -> void { +auto HitachiDSP::writeROM(uint24 address, uint8 data) -> void { } -auto HitachiDSP::ramRead(uint24 addr, uint8 data) -> uint8 { +// + +auto HitachiDSP::addressRAM(uint24 address) const -> maybe { + if(Mapping == 0) { + //70-77:0000-7fff + if((address & 0xf88000) == 0x700000) { + address = (address & 0x070000) >> 1 | (address & 0x7fff); + return {address & 0x03ffff}; + } + } else { + //30-3f,b0-bf:6000-7fff + if((address & 0x70e000) == 0x306000) { + address = (address & 0x0f0000) >> 3 | (address & 0x1fff); + return {address & 0x01ffff}; + } + } + return {}; +} + +auto HitachiDSP::readRAM(uint24 address, uint8 data) -> uint8 { if(ram.size() == 0) return 0x00; //not open bus - return ram.read(Bus::mirror(addr, ram.size()), data); + return ram.read(bus.mirror(address, ram.size()), data); } -auto HitachiDSP::ramWrite(uint24 addr, uint8 data) -> void { +auto HitachiDSP::writeRAM(uint24 address, uint8 data) -> void { if(ram.size() == 0) return; - return ram.write(Bus::mirror(addr, ram.size()), data); + return ram.write(bus.mirror(address, ram.size()), data); } -auto HitachiDSP::dramRead(uint24 addr, uint8 data) -> uint8 { - addr &= 0xfff; - if(addr >= 0xc00) return data; - return dataRAM[addr]; +// + +auto HitachiDSP::addressDRAM(uint24 address) const -> maybe { + if(Mapping == 0) { + //00-3f,80-bf:6000-6bff,7000-7bff + if((address & 0x40e000) == 0x006000 && (address & 0x0c00) != 0x0c00) { + return {address & 0x0fff}; + } + } else { + //00-2f,80-af:6000-6bff,7000-7bff + if((address & 0x40e000) == 0x006000 && (address & 0x0c00) != 0x0c00 && (address & 0x300000) != 0x300000) { + return {address & 0x0fff}; + } + } + return {}; } -auto HitachiDSP::dramWrite(uint24 addr, uint8 data) -> void { - addr &= 0xfff; - if(addr >= 0xc00) return; - dataRAM[addr] = data; +auto HitachiDSP::readDRAM(uint24 address, uint8 data) -> uint8 { + address &= 0xfff; + if(address >= 0xc00) return data; + return dataRAM[address]; } -auto HitachiDSP::dspRead(uint24 addr, uint8) -> uint8 { - addr = 0x7c00 | (addr & 0x03ff); +auto HitachiDSP::writeDRAM(uint24 address, uint8 data) -> void { + address &= 0xfff; + if(address >= 0xc00) return; + dataRAM[address] = data; +} - //MMIO - switch(addr) { - case 0x7f40: return mmio.dmaSource >> 0; - case 0x7f41: return mmio.dmaSource >> 8; - case 0x7f42: return mmio.dmaSource >> 16; - case 0x7f43: return mmio.dmaLength >> 0; - case 0x7f44: return mmio.dmaLength >> 8; - case 0x7f45: return mmio.dmaTarget >> 0; - case 0x7f46: return mmio.dmaTarget >> 8; - case 0x7f47: return mmio.dmaTarget >> 16; - case 0x7f48: return mmio.r1f48; - case 0x7f49: return mmio.programOffset >> 0; - case 0x7f4a: return mmio.programOffset >> 8; - case 0x7f4b: return mmio.programOffset >> 16; - case 0x7f4c: return mmio.r1f4c; - case 0x7f4d: return mmio.pageNumber >> 0; - case 0x7f4e: return mmio.pageNumber >> 8; - case 0x7f4f: return mmio.programCounter; - case 0x7f50: return mmio.r1f50; - case 0x7f51: return mmio.r1f51; - case 0x7f52: return mmio.r1f52; - case 0x7f53: case 0x7f54: case 0x7f55: case 0x7f56: - case 0x7f57: case 0x7f58: case 0x7f59: case 0x7f5a: - case 0x7f5b: case 0x7f5c: case 0x7f5d: case 0x7f5e: - case 0x7f5f: return ((regs.halt == false) << 6) | ((regs.halt == true) << 1); +// + +auto HitachiDSP::addressIO(uint24 address) const -> maybe { + if(Mapping == 0) { + //00-3f,80-bf:6c00-6fff,7c00-7fff + if((address & 0x40ec00) == 0x006c00) { + return {address & 0x03ff}; + } + } else { + //00-2f,80-af:6c00-6fff,7c00-7fff + if((address & 0x40ec00) == 0x006c00 && (address & 0x300000) != 0x300000) { + return {address & 0x03ff}; + } + } + return {}; +} + +auto HitachiDSP::readIO(uint24 address, uint8 data) -> uint8 { + address = 0x7c00 | (address & 0x03ff); + + //IO + switch(address) { + case 0x7f40: return io.dma.source.byte(0); + case 0x7f41: return io.dma.source.byte(1); + case 0x7f42: return io.dma.source.byte(2); + case 0x7f43: return io.dma.length.byte(0); + case 0x7f44: return io.dma.length.byte(1); + case 0x7f45: return io.dma.target.byte(0); + case 0x7f46: return io.dma.target.byte(1); + case 0x7f47: return io.dma.target.byte(2); + case 0x7f48: return io.cache.page; + case 0x7f49: return io.cache.base.byte(0); + case 0x7f4a: return io.cache.base.byte(1); + case 0x7f4b: return io.cache.base.byte(2); + case 0x7f4c: return io.cache.lock[0] << 0 | io.cache.lock[1] << 1; + case 0x7f4d: return io.cache.pb.byte(0); + case 0x7f4e: return io.cache.pb.byte(1); + case 0x7f4f: return io.cache.pc; + case 0x7f50: return io.wait.ram << 0 | io.wait.rom << 4; + case 0x7f51: return io.irq; + case 0x7f52: return io.rom; + case 0x7f53: case 0x7f54: case 0x7f55: case 0x7f56: case 0x7f57: + case 0x7f59: case 0x7f5b: case 0x7f5c: case 0x7f5d: case 0x7f5e: + case 0x7f5f: return io.suspend.enable << 0 | r.i << 1 | running() << 6 | busy() << 7; } - //Vector - if(addr >= 0x7f60 && addr <= 0x7f7f) { - return mmio.vector[addr & 0x1f]; + //vectors + if(address >= 0x7f60 && address <= 0x7f7f) { + return io.vector[address & 0x1f]; } - //GPRs - if((addr >= 0x7f80 && addr <= 0x7faf) || (addr >= 0x7fc0 && addr <= 0x7fef)) { - uint index = (addr & 0x3f) / 3; //0..15 - uint shift = ((addr & 0x3f) % 3) * 8; //0, 8, 16 - return regs.gpr[index] >> shift; + //registers + if((address >= 0x7f80 && address <= 0x7faf) || (address >= 0x7fc0 && address <= 0x7fef)) { + address &= 0x3f; + return r.gpr[address / 3].byte(address % 3); } return 0x00; } -auto HitachiDSP::dspWrite(uint24 addr, uint8 data) -> void { - addr = 0x7c00 | (addr & 0x03ff); +auto HitachiDSP::writeIO(uint24 address, uint8 data) -> void { + address = 0x7c00 | (address & 0x03ff); - //MMIO - switch(addr) { - case 0x7f40: mmio.dmaSource = (mmio.dmaSource & 0xffff00) | (data << 0); return; - case 0x7f41: mmio.dmaSource = (mmio.dmaSource & 0xff00ff) | (data << 8); return; - case 0x7f42: mmio.dmaSource = (mmio.dmaSource & 0x00ffff) | (data << 16); return; - case 0x7f43: mmio.dmaLength = (mmio.dmaLength & 0xff00) | (data << 0); return; - case 0x7f44: mmio.dmaLength = (mmio.dmaLength & 0x00ff) | (data << 8); return; - case 0x7f45: mmio.dmaTarget = (mmio.dmaTarget & 0xffff00) | (data << 0); return; - case 0x7f46: mmio.dmaTarget = (mmio.dmaTarget & 0xff00ff) | (data << 8); return; - case 0x7f47: mmio.dmaTarget = (mmio.dmaTarget & 0x00ffff) | (data << 16); - if(regs.halt) mmio.dma = true; + //IO + switch(address) { + case 0x7f40: io.dma.source.byte(0) = data; return; + case 0x7f41: io.dma.source.byte(1) = data; return; + case 0x7f42: io.dma.source.byte(2) = data; return; + + case 0x7f43: io.dma.length.byte(0) = data; return; + case 0x7f44: io.dma.length.byte(1) = data; return; + + case 0x7f45: io.dma.target.byte(0) = data; return; + case 0x7f46: io.dma.target.byte(1) = data; return; + case 0x7f47: io.dma.target.byte(2) = data; + if(io.halt) io.dma.enable = 1; return; - case 0x7f48: mmio.r1f48 = data & 0x01; return; - case 0x7f49: mmio.programOffset = (mmio.programOffset & 0xffff00) | (data << 0); return; - case 0x7f4a: mmio.programOffset = (mmio.programOffset & 0xff00ff) | (data << 8); return; - case 0x7f4b: mmio.programOffset = (mmio.programOffset & 0x00ffff) | (data << 16); return; - case 0x7f4c: mmio.r1f4c = data & 0x03; return; - case 0x7f4d: mmio.pageNumber = (mmio.pageNumber & 0x7f00) | ((data & 0xff) << 0); return; - case 0x7f4e: mmio.pageNumber = (mmio.pageNumber & 0x00ff) | ((data & 0x7f) << 8); return; - case 0x7f4f: mmio.programCounter = data; - if(regs.halt) { - regs.pc = mmio.pageNumber * 256 + mmio.programCounter; - regs.halt = false; + + case 0x7f48: + io.cache.page = data.bit(0); + if(io.halt) io.cache.enable = 1; + return; + + case 0x7f49: io.cache.base.byte(0) = data; return; + case 0x7f4a: io.cache.base.byte(1) = data; return; + case 0x7f4b: io.cache.base.byte(2) = data; return; + + case 0x7f4c: + io.cache.lock[0] = data.bit(0); + io.cache.lock[1] = data.bit(1); + return; + + case 0x7f4d: io.cache.pb.byte(0) = data; return; + case 0x7f4e: io.cache.pb.byte(1) = data; return; + + case 0x7f4f: + io.cache.pc = data; + if(io.halt) { + io.halt = 0; + r.pb = io.cache.pb; + r.pc = io.cache.pc; } return; - case 0x7f50: mmio.r1f50 = data & 0x77; return; - case 0x7f51: mmio.r1f51 = data & 0x01; return; - case 0x7f52: mmio.r1f52 = data & 0x01; return; - } - //Vector - if(addr >= 0x7f60 && addr <= 0x7f7f) { - mmio.vector[addr & 0x1f] = data; + case 0x7f50: + io.wait.ram = data.bits(0,2); + io.wait.rom = data.bits(4,6); + return; + + case 0x7f51: + io.irq = data.bit(0); + if(io.irq == 1) r.i = 0, cpu.r.irq = 0; + return; + + case 0x7f52: + io.rom = data.bit(0); + return; + + case 0x7f53: + io.lock = 0; + io.halt = 1; + return; + + case 0x7f55: io.suspend.enable = 1; io.suspend.duration = 0; return; //indefinite + case 0x7f56: io.suspend.enable = 1; io.suspend.duration = 32; return; + case 0x7f57: io.suspend.enable = 1; io.suspend.duration = 64; return; + case 0x7f58: io.suspend.enable = 1; io.suspend.duration = 96; return; + case 0x7f59: io.suspend.enable = 1; io.suspend.duration = 128; return; + case 0x7f5a: io.suspend.enable = 1; io.suspend.duration = 160; return; + case 0x7f5b: io.suspend.enable = 1; io.suspend.duration = 192; return; + case 0x7f5c: io.suspend.enable = 1; io.suspend.duration = 224; return; + case 0x7f5d: io.suspend.enable = 0; return; //resume + + case 0x7f5e: + r.i = 0; //does not deassert CPU IRQ line return; } - //GPRs - if((addr >= 0x7f80 && addr <= 0x7faf) || (addr >= 0x7fc0 && addr <= 0x7fef)) { - uint index = (addr & 0x3f) / 3; - switch((addr & 0x3f) % 3) { - case 0: regs.gpr[index] = (regs.gpr[index] & 0xffff00) | (data << 0); return; - case 1: regs.gpr[index] = (regs.gpr[index] & 0xff00ff) | (data << 8); return; - case 2: regs.gpr[index] = (regs.gpr[index] & 0x00ffff) | (data << 16); return; - } + //vectors + if(address >= 0x7f60 && address <= 0x7f7f) { + io.vector[address & 0x1f] = data; + return; + } + + //registers + if((address >= 0x7f80 && address <= 0x7faf) || (address >= 0x7fc0 && address <= 0x7fef)) { + address &= 0x3f; + r.gpr[address / 3].byte(address % 3) = data; } } diff --git a/higan/sfc/coprocessor/hitachidsp/serialization.cpp b/higan/sfc/coprocessor/hitachidsp/serialization.cpp index 6ac657ba..b4b85213 100644 --- a/higan/sfc/coprocessor/hitachidsp/serialization.cpp +++ b/higan/sfc/coprocessor/hitachidsp/serialization.cpp @@ -13,18 +13,4 @@ auto HitachiDSP::firmware() const -> vector { auto HitachiDSP::serialize(serializer& s) -> void { HG51B::serialize(s); Thread::serialize(s); - - s.integer(mmio.dma); - s.integer(mmio.dmaSource); - s.integer(mmio.dmaLength); - s.integer(mmio.dmaTarget); - s.integer(mmio.r1f48); - s.integer(mmio.programOffset); - s.integer(mmio.r1f4c); - s.integer(mmio.pageNumber); - s.integer(mmio.programCounter); - s.integer(mmio.r1f50); - s.integer(mmio.r1f51); - s.integer(mmio.r1f52); - s.array(mmio.vector); } diff --git a/higan/sfc/coprocessor/mcc/mcc.cpp b/higan/sfc/coprocessor/mcc/mcc.cpp index b865f283..0858094e 100644 --- a/higan/sfc/coprocessor/mcc/mcc.cpp +++ b/higan/sfc/coprocessor/mcc/mcc.cpp @@ -11,9 +11,6 @@ auto MCC::unload() -> void { } auto MCC::power() -> void { - rom.writeProtect(true); - psram.writeProtect(false); - irq.flag = 0; irq.enable = 0; w.mapping = 1; diff --git a/higan/sfc/coprocessor/mcc/mcc.hpp b/higan/sfc/coprocessor/mcc/mcc.hpp index eba1c7b0..ec4b9725 100644 --- a/higan/sfc/coprocessor/mcc/mcc.hpp +++ b/higan/sfc/coprocessor/mcc/mcc.hpp @@ -2,8 +2,8 @@ //Custom logic chip inside the BS-X Satellaview base cartridge struct MCC { - MappedRAM rom; - MappedRAM psram; + ReadableMemory rom; + WritableMemory psram; //mcc.cpp auto unload() -> void; diff --git a/higan/sfc/coprocessor/obc1/obc1.cpp b/higan/sfc/coprocessor/obc1/obc1.cpp index 355a3411..691822e9 100644 --- a/higan/sfc/coprocessor/obc1/obc1.cpp +++ b/higan/sfc/coprocessor/obc1/obc1.cpp @@ -10,8 +10,6 @@ auto OBC1::unload() -> void { } auto OBC1::power() -> void { - ram.writeProtect(false); - status.baseptr = (ramRead(0x1ff5) & 1) ? 0x1800 : 0x1c00; status.address = (ramRead(0x1ff6) & 0x7f); status.shift = (ramRead(0x1ff6) & 3) << 1; diff --git a/higan/sfc/coprocessor/obc1/obc1.hpp b/higan/sfc/coprocessor/obc1/obc1.hpp index 527666d6..ab104b46 100644 --- a/higan/sfc/coprocessor/obc1/obc1.hpp +++ b/higan/sfc/coprocessor/obc1/obc1.hpp @@ -7,7 +7,7 @@ struct OBC1 { auto serialize(serializer&) -> void; - MappedRAM ram; + WritableMemory ram; private: auto ramRead(uint addr) -> uint8; diff --git a/higan/sfc/coprocessor/sa1/bwram.cpp b/higan/sfc/coprocessor/sa1/bwram.cpp index e4a23dd8..420aad4f 100644 --- a/higan/sfc/coprocessor/sa1/bwram.cpp +++ b/higan/sfc/coprocessor/sa1/bwram.cpp @@ -1,4 +1,6 @@ auto SA1::BWRAM::conflict() const -> bool { + if(configuration.hacks.coprocessors.delayedSync) return false; + if(!cpu.r.rwb) return false; if((cpu.r.mar & 0x40e000) == 0x006000) return true; //00-3f,80-bf:6000-7fff if((cpu.r.mar & 0xf00000) == 0x400000) return true; //40-4f:0000-ffff @@ -8,13 +10,13 @@ auto SA1::BWRAM::conflict() const -> bool { auto SA1::BWRAM::read(uint24 address, uint8 data) -> uint8 { if(!size()) return data; address = bus.mirror(address, size()); - return _data[address]; + return WritableMemory::read(address, data); } auto SA1::BWRAM::write(uint24 address, uint8 data) -> void { if(!size()) return; address = bus.mirror(address, size()); - _data[address] = data; + return WritableMemory::write(address, data); } //note: addresses are translated prior to invoking this function: diff --git a/higan/sfc/coprocessor/sa1/iram.cpp b/higan/sfc/coprocessor/sa1/iram.cpp index fdeaeabc..3a41ca47 100644 --- a/higan/sfc/coprocessor/sa1/iram.cpp +++ b/higan/sfc/coprocessor/sa1/iram.cpp @@ -1,4 +1,6 @@ auto SA1::IRAM::conflict() const -> bool { + if(configuration.hacks.coprocessors.delayedSync) return false; + if(!cpu.r.rwb) return false; if((cpu.r.mar & 0x40f800) == 0x003000) return true; //00-3f,80-bf:3000-37ff return false; @@ -6,12 +8,14 @@ auto SA1::IRAM::conflict() const -> bool { auto SA1::IRAM::read(uint24 address, uint8 data) -> uint8 { if(!size()) return data; - return _data[address & size() - 1]; + address = bus.mirror(address, size()); + return WritableMemory::read(address, data); } auto SA1::IRAM::write(uint24 address, uint8 data) -> void { if(!size()) return; - _data[address & size() - 1] = data; + address = bus.mirror(address, size()); + return WritableMemory::write(address, data); } auto SA1::IRAM::readCPU(uint24 address, uint8 data) -> uint8 { diff --git a/higan/sfc/coprocessor/sa1/rom.cpp b/higan/sfc/coprocessor/sa1/rom.cpp index b1b94950..87f7e20d 100644 --- a/higan/sfc/coprocessor/sa1/rom.cpp +++ b/higan/sfc/coprocessor/sa1/rom.cpp @@ -1,4 +1,6 @@ auto SA1::ROM::conflict() const -> bool { + if(configuration.hacks.coprocessors.delayedSync) return false; + if(!cpu.r.rwb) return false; if((cpu.r.mar & 0x408000) == 0x008000) return true; //00-3f,80-bf:8000-ffff if((cpu.r.mar & 0xc00000) == 0xc00000) return true; //c0-ff:0000-ffff @@ -7,7 +9,7 @@ auto SA1::ROM::conflict() const -> bool { auto SA1::ROM::read(uint24 address, uint8 data) -> uint8 { address = bus.mirror(address, size()); - return _data[address]; + return ReadableMemory::read(address, data); } auto SA1::ROM::write(uint24 address, uint8 data) -> void { diff --git a/higan/sfc/coprocessor/sa1/sa1.cpp b/higan/sfc/coprocessor/sa1/sa1.cpp index 2bb2cf23..553af4de 100644 --- a/higan/sfc/coprocessor/sa1/sa1.cpp +++ b/higan/sfc/coprocessor/sa1/sa1.cpp @@ -128,10 +128,6 @@ auto SA1::power() -> void { WDC65816::power(); create(SA1::Enter, system.cpuFrequency()); - rom.writeProtect(true); - bwram.writeProtect(false); - iram.writeProtect(false); - bwram.dma = false; for(auto addr : range(iram.size())) { iram.write(addr, 0x00); diff --git a/higan/sfc/coprocessor/sa1/sa1.hpp b/higan/sfc/coprocessor/sa1/sa1.hpp index 04302d35..f5168009 100644 --- a/higan/sfc/coprocessor/sa1/sa1.hpp +++ b/higan/sfc/coprocessor/sa1/sa1.hpp @@ -47,7 +47,7 @@ struct SA1 : Processor::WDC65816, Thread { //serialization.cpp auto serialize(serializer&) -> void; - struct ROM : MappedRAM { + struct ROM : ReadableMemory { //rom.cpp alwaysinline auto conflict() const -> bool; @@ -61,7 +61,7 @@ struct SA1 : Processor::WDC65816, Thread { auto writeSA1(uint24 address, uint8 data) -> void; } rom; - struct BWRAM : MappedRAM { + struct BWRAM : WritableMemory { //bwram.cpp alwaysinline auto conflict() const -> bool; @@ -83,7 +83,7 @@ struct SA1 : Processor::WDC65816, Thread { bool dma; } bwram; - struct IRAM : MappedRAM { + struct IRAM : WritableMemory { //iram.cpp alwaysinline auto conflict() const -> bool; diff --git a/higan/sfc/coprocessor/sdd1/sdd1.cpp b/higan/sfc/coprocessor/sdd1/sdd1.cpp index 45efc151..7430eb1d 100644 --- a/higan/sfc/coprocessor/sdd1/sdd1.cpp +++ b/higan/sfc/coprocessor/sdd1/sdd1.cpp @@ -12,8 +12,6 @@ auto SDD1::unload() -> void { } auto SDD1::power() -> void { - rom.writeProtect(true); - //hook S-CPU DMA MMIO registers to gather information for struct dma[]; //buffer address and transfer size information for use in SDD1::mcu_read() bus.map({&SDD1::dmaRead, &sdd1}, {&SDD1::dmaWrite, &sdd1}, "00-3f,80-bf:4300-437f"); diff --git a/higan/sfc/coprocessor/sdd1/sdd1.hpp b/higan/sfc/coprocessor/sdd1/sdd1.hpp index 4b23f9d7..9003170b 100644 --- a/higan/sfc/coprocessor/sdd1/sdd1.hpp +++ b/higan/sfc/coprocessor/sdd1/sdd1.hpp @@ -15,7 +15,7 @@ struct SDD1 { auto serialize(serializer&) -> void; - MappedRAM rom; + ReadableMemory rom; private: uint8 r4800; //hard enable diff --git a/higan/sfc/coprocessor/spc7110/spc7110.cpp b/higan/sfc/coprocessor/spc7110/spc7110.cpp index 3fb38c31..14425d8f 100644 --- a/higan/sfc/coprocessor/spc7110/spc7110.cpp +++ b/higan/sfc/coprocessor/spc7110/spc7110.cpp @@ -41,10 +41,6 @@ auto SPC7110::unload() -> void { auto SPC7110::power() -> void { create(SPC7110::Enter, 21'477'272); - prom.writeProtect(true); - drom.writeProtect(true); - ram.writeProtect(false); - r4801 = 0x00; r4802 = 0x00; r4803 = 0x00; diff --git a/higan/sfc/coprocessor/spc7110/spc7110.hpp b/higan/sfc/coprocessor/spc7110/spc7110.hpp index 9f2d1d15..8f3b688f 100644 --- a/higan/sfc/coprocessor/spc7110/spc7110.hpp +++ b/higan/sfc/coprocessor/spc7110/spc7110.hpp @@ -52,9 +52,9 @@ struct SPC7110 : Thread { auto aluMultiply() -> void; auto aluDivide() -> void; - MappedRAM prom; //program ROM - MappedRAM drom; //data ROM - MappedRAM ram; + ReadableMemory prom; //program ROM + ReadableMemory drom; //data ROM + WritableMemory ram; private: //decompression unit diff --git a/higan/sfc/coprocessor/superfx/superfx.cpp b/higan/sfc/coprocessor/superfx/superfx.cpp index 55926c41..87690eeb 100644 --- a/higan/sfc/coprocessor/superfx/superfx.cpp +++ b/higan/sfc/coprocessor/superfx/superfx.cpp @@ -40,9 +40,6 @@ auto SuperFX::power() -> void { GSU::power(); create(SuperFX::Enter, Frequency); - rom.writeProtect(true); - ram.writeProtect(false); - romMask = rom.size() - 1; ramMask = ram.size() - 1; diff --git a/higan/sfc/coprocessor/superfx/superfx.hpp b/higan/sfc/coprocessor/superfx/superfx.hpp index 48da52a2..c590834a 100644 --- a/higan/sfc/coprocessor/superfx/superfx.hpp +++ b/higan/sfc/coprocessor/superfx/superfx.hpp @@ -1,6 +1,6 @@ struct SuperFX : Processor::GSU, Thread { - MappedRAM rom; - MappedRAM ram; + ReadableMemory rom; + WritableMemory ram; //superfx.cpp static auto Enter() -> void; diff --git a/higan/sfc/cpu/cpu.hpp b/higan/sfc/cpu/cpu.hpp index 13412d34..2b3a12a4 100644 --- a/higan/sfc/cpu/cpu.hpp +++ b/higan/sfc/cpu/cpu.hpp @@ -16,6 +16,7 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter { inline auto dmaStep(uint clocks) -> void; inline auto dmaFlush() -> void; + inline auto dmaWrite() -> void; auto dmaRun() -> void; auto hdmaReset() -> void; @@ -166,11 +167,13 @@ private: //dma.cpp inline auto step(uint clocks) -> void; inline auto edge() -> void; + inline auto flush() -> void; + inline auto write() -> void; + inline auto validA(uint24 address) -> bool; inline auto readA(uint24 address) -> uint8; inline auto readA(uint24 address, bool valid) -> uint8; inline auto readB(uint8 address, bool valid) -> uint8; - inline auto flush() -> void; inline auto writeA(uint24 address, uint8 data) -> void; inline auto writeA(uint24 address, uint8 data, bool valid) -> void; inline auto writeB(uint8 address, uint8 data, bool valid) -> void; diff --git a/higan/sfc/cpu/dma.cpp b/higan/sfc/cpu/dma.cpp index 62baa7fa..f456f406 100644 --- a/higan/sfc/cpu/dma.cpp +++ b/higan/sfc/cpu/dma.cpp @@ -1,3 +1,18 @@ +/* DMA timing: + The general idea is that DMA moves values between the A-bus and B-bus in parallel. + Obviously, a write can't happen at the same time as a read, as the read needs 8 cycles to complete. + My theory is that the accesses are staggered, like so: + + Cycle 0: read 0 + Cycle 1: read 1, write 0 + Cycle 2: read 2, write 1 + ... + Cycle n: read n, write n-1 + Cycle n+1: write n + + The staggered writes are implemented below using the pipe/flush concept. +*/ + auto CPU::dmaEnable() -> bool { for(auto& channel : channels) if(channel.dmaEnable) return true; return false; @@ -24,10 +39,15 @@ auto CPU::dmaFlush() -> void { bus.write(pipe.address, pipe.data); } -auto CPU::dmaRun() -> void { - r.rwb = 0; +auto CPU::dmaWrite() -> void { + r.rwb = pipe.valid; + r.mar = pipe.address; dmaStep(8); dmaFlush(); +} + +auto CPU::dmaRun() -> void { + dmaWrite(); dmaEdge(); for(auto& channel : channels) channel.dmaRun(); dmaFlush(); @@ -39,18 +59,14 @@ auto CPU::hdmaReset() -> void { } auto CPU::hdmaSetup() -> void { - r.rwb = 0; - dmaStep(8); - dmaFlush(); + dmaWrite(); for(auto& channel : channels) channel.hdmaSetup(); dmaFlush(); status.irqLock = true; } auto CPU::hdmaRun() -> void { - r.rwb = 0; - dmaStep(8); - dmaFlush(); + dmaWrite(); for(auto& channel : channels) channel.hdmaTransfer(); for(auto& channel : channels) channel.hdmaAdvance(); dmaFlush(); @@ -59,13 +75,10 @@ auto CPU::hdmaRun() -> void { // -auto CPU::Channel::step(uint clocks) -> void { - return cpu.dmaStep(clocks); -} - -auto CPU::Channel::edge() -> void { - return cpu.dmaEdge(); -} +auto CPU::Channel::step(uint clocks) -> void { return cpu.dmaStep(clocks); } +auto CPU::Channel::edge() -> void { return cpu.dmaEdge(); } +auto CPU::Channel::flush() -> void { return cpu.dmaFlush(); } +auto CPU::Channel::write() -> void { return cpu.dmaWrite(); } auto CPU::Channel::validA(uint24 address) -> bool { //A-bus cannot access the B-bus or CPU I/O registers @@ -96,10 +109,6 @@ auto CPU::Channel::readB(uint8 address, bool valid) -> uint8 { return cpu.r.mdr; } -auto CPU::Channel::flush() -> void { - return cpu.dmaFlush(); -} - auto CPU::Channel::writeA(uint24 address, uint8 data) -> void { return writeA(address, data, validA(address)); } @@ -148,9 +157,7 @@ auto CPU::Channel::dmaRun() -> void { edge(); } while(dmaEnable && --transferSize); - cpu.r.rwb = 0; - step(8); - flush(); + write(); edge(); dmaEnable = false; } diff --git a/higan/sfc/cpu/timing.cpp b/higan/sfc/cpu/timing.cpp index 21a007c4..b75b010e 100644 --- a/higan/sfc/cpu/timing.cpp +++ b/higan/sfc/cpu/timing.cpp @@ -23,14 +23,8 @@ auto CPU::step(uint clocks) -> void { } } - #if defined(DEBUGGER) - synchronize(smp); - synchronize(ppu); - #endif - - #if defined(DEBUGGER) || defined(ACCURATE_SA1) + if(configuration.hacks.coprocessors.delayedSync) return; for(auto coprocessor : coprocessors) synchronize(*coprocessor); - #endif } //called by ppu.tick() when Hcounter=0 @@ -94,10 +88,12 @@ auto CPU::dmaEdge() -> void { status.hdmaPending = false; if(hdmaEnable()) { if(!dmaEnable()) { + r.rwb = 0; dmaStep(8 - dmaCounter()); } status.hdmaMode == 0 ? hdmaSetup() : hdmaRun(); if(!dmaEnable()) { + r.rwb = 0; //unverified step(status.clockCount - (status.dmaClocks % status.clockCount)); status.dmaActive = false; } @@ -107,8 +103,10 @@ auto CPU::dmaEdge() -> void { if(status.dmaPending) { status.dmaPending = false; if(dmaEnable()) { + r.rwb = 0; dmaStep(8 - dmaCounter()); dmaRun(); + r.rwb = 0; //unverified step(status.clockCount - (status.dmaClocks % status.clockCount)); status.dmaActive = false; } diff --git a/higan/sfc/interface/configuration.cpp b/higan/sfc/interface/configuration.cpp index 03d2f807..186d895f 100644 --- a/higan/sfc/interface/configuration.cpp +++ b/higan/sfc/interface/configuration.cpp @@ -20,6 +20,7 @@ auto Configuration::process(Markup::Node document, bool load) -> void { bind(boolean, "Hacks/FastPPU/NoSpriteLimit", hacks.ppuFast.noSpriteLimit); bind(boolean, "Hacks/FastPPU/HiresMode7", hacks.ppuFast.hiresMode7); bind(boolean, "Hacks/FastDSP/Enable", hacks.dspFast.enable); + bind(boolean, "Hacks/Coprocessors/DelayedSync", hacks.coprocessors.delayedSync); #undef bind } diff --git a/higan/sfc/interface/configuration.hpp b/higan/sfc/interface/configuration.hpp index 53aafce6..f85f283f 100644 --- a/higan/sfc/interface/configuration.hpp +++ b/higan/sfc/interface/configuration.hpp @@ -33,6 +33,9 @@ struct Configuration { struct DSPFast { bool enable = false; } dspFast; + struct Coprocessors { + bool delayedSync = false; + } coprocessors; } hacks; private: diff --git a/higan/sfc/memory/memory-inline.hpp b/higan/sfc/memory/memory-inline.hpp index c1eb8f60..1eccb359 100644 --- a/higan/sfc/memory/memory-inline.hpp +++ b/higan/sfc/memory/memory-inline.hpp @@ -1,41 +1,3 @@ -//StaticRAM - -StaticRAM::StaticRAM(uint size) : _size(size) { _data = new uint8[_size]; } -StaticRAM::~StaticRAM() { delete[] _data; } - -auto StaticRAM::data() -> uint8* { return _data; } -auto StaticRAM::size() const -> uint { return _size; } - -auto StaticRAM::read(uint24 addr, uint8) -> uint8 { return _data[addr]; } -auto StaticRAM::write(uint24 addr, uint8 data) -> void { _data[addr] = data; } -auto StaticRAM::operator[](uint24 addr) -> uint8& { return _data[addr]; } -auto StaticRAM::operator[](uint24 addr) const -> const uint8& { return _data[addr]; } - -//MappedRAM - -auto MappedRAM::reset() -> void { - delete[] _data; - _data = nullptr; - _size = 0; - _writeProtect = false; -} - -auto MappedRAM::allocate(uint size) -> void { - reset(); - _data = new uint8[_size = size]; - memory::fill(_data, _size, 0xff); -} - -auto MappedRAM::writeProtect(bool writeProtect) -> void { _writeProtect = writeProtect; } -auto MappedRAM::data() -> uint8* { return _data; } -auto MappedRAM::size() const -> uint { return _size; } - -auto MappedRAM::read(uint24 addr, uint8) -> uint8 { return _data[addr]; } -auto MappedRAM::write(uint24 addr, uint8 data) -> void { if(!_writeProtect) _data[addr] = data; } -auto MappedRAM::operator[](uint24 addr) const -> const uint8& { return _data[addr]; } - -//Bus - auto Bus::mirror(uint addr, uint size) -> uint { if(size == 0) return 0; uint base = 0; diff --git a/higan/sfc/memory/memory.cpp b/higan/sfc/memory/memory.cpp index 06f1d46f..ca4fffc8 100644 --- a/higan/sfc/memory/memory.cpp +++ b/higan/sfc/memory/memory.cpp @@ -30,10 +30,10 @@ auto Bus::map( const function& read, const function& write, const string& addr, uint size, uint base, uint mask -) -> void { +) -> uint { uint id = 1; while(counter[id]) { - if(++id >= 256) return print("SFC error: bus map exhausted\n"); + if(++id >= 256) return print("SFC error: bus map exhausted\n"), 0; } reader[id] = read; @@ -68,6 +68,8 @@ auto Bus::map( } } } + + return id; } auto Bus::unmap(const string& addr) -> void { diff --git a/higan/sfc/memory/memory.hpp b/higan/sfc/memory/memory.hpp index f65b3423..7ef71b0c 100644 --- a/higan/sfc/memory/memory.hpp +++ b/higan/sfc/memory/memory.hpp @@ -1,74 +1,46 @@ struct Memory { + virtual ~Memory() { reset(); } inline explicit operator bool() const { return size() > 0; } virtual auto reset() -> void {} - virtual auto allocate(uint) -> void {} + virtual auto allocate(uint, uint8 = 0xff) -> void {} virtual auto data() -> uint8* = 0; virtual auto size() const -> uint = 0; - virtual auto read(uint24 addr, uint8 data = 0) -> uint8 = 0; - virtual auto write(uint24 addr, uint8 data) -> void = 0; + virtual auto read(uint24 address, uint8 data = 0) -> uint8 = 0; + virtual auto write(uint24 address, uint8 data) -> void = 0; + + uint id = 0; }; -struct StaticRAM : Memory { - inline StaticRAM(uint size); - inline ~StaticRAM(); - - inline auto data() -> uint8*; - inline auto size() const -> uint; - - inline auto read(uint24 addr, uint8 data = 0) -> uint8; - inline auto write(uint24 addr, uint8 data) -> void; - inline auto operator[](uint24 addr) -> uint8&; - inline auto operator[](uint24 addr) const -> const uint8&; - -protected: - uint8* _data = nullptr; - uint _size = 0; -}; - -struct MappedRAM : Memory { - inline auto reset() -> void; - inline auto allocate(uint) -> void; - - inline auto writeProtect(bool writeProtect) -> void; - inline auto data() -> uint8*; - inline auto size() const -> uint; - - inline auto read(uint24 addr, uint8 data = 0) -> uint8; - inline auto write(uint24 addr, uint8 data) -> void; - inline auto operator[](uint24 addr) const -> const uint8&; - -protected: - uint8* _data = nullptr; - uint _size = 0; - bool _writeProtect = false; -}; +#include "readable.hpp" +#include "writable.hpp" +#include "protectable.hpp" struct Bus { - alwaysinline static auto mirror(uint addr, uint size) -> uint; - alwaysinline static auto reduce(uint addr, uint mask) -> uint; + alwaysinline static auto mirror(uint address, uint size) -> uint; + alwaysinline static auto reduce(uint address, uint mask) -> uint; ~Bus(); - alwaysinline auto read(uint24 addr, uint8 data) -> uint8; - alwaysinline auto write(uint24 addr, uint8 data) -> void; + alwaysinline auto read(uint24 address, uint8 data) -> uint8; + alwaysinline auto write(uint24 address, uint8 data) -> void; auto reset() -> void; auto map( const function& read, const function& write, - const string& addr, uint size = 0, uint base = 0, uint mask = 0 - ) -> void; - auto unmap(const string& addr) -> void; + const string& address, uint size = 0, uint base = 0, uint mask = 0 + ) -> uint; + auto unmap(const string& address) -> void; private: uint8* lookup = nullptr; uint32* target = nullptr; - function uint8> reader[256]; - function void> writer[256]; + function reader[256]; + function writer[256]; uint24 counter[256]; }; diff --git a/higan/sfc/memory/protectable.hpp b/higan/sfc/memory/protectable.hpp new file mode 100644 index 00000000..47034bc4 --- /dev/null +++ b/higan/sfc/memory/protectable.hpp @@ -0,0 +1,48 @@ +struct ProtectableMemory : Memory { + inline auto reset() -> void override { + delete[] self.data; + self.data = nullptr; + } + + inline auto allocate(uint size, uint8 fill = 0xff) -> void override { + delete[] self.data; + self.data = new uint8[self.size = size]; + for(uint address : range(size)) self.data[address] = fill; + } + + inline auto data() -> uint8* override { + return self.data; + } + + inline auto size() const -> uint override { + return self.size; + } + + inline auto writable() const -> bool { + return self.writable; + } + + inline auto writable(bool writable) -> void { + self.writable = writable; + } + + inline auto read(uint24 address, uint8 data = 0) -> uint8 override { + return self.data[address]; + } + + inline auto write(uint24 address, uint8 data) -> void override { + if(!self.writable) return; + self.data[address] = data; + } + + inline auto operator[](uint24 address) const -> uint8 { + return self.data[address]; + } + +private: + struct { + uint8* data = nullptr; + uint size = 0; + bool writable = false; + } self; +}; diff --git a/higan/sfc/memory/readable.hpp b/higan/sfc/memory/readable.hpp new file mode 100644 index 00000000..9e958352 --- /dev/null +++ b/higan/sfc/memory/readable.hpp @@ -0,0 +1,37 @@ +struct ReadableMemory : Memory { + inline auto reset() -> void override { + delete[] self.data; + self.data = nullptr; + } + + inline auto allocate(uint size, uint8 fill = 0xff) -> void override { + delete[] self.data; + self.data = new uint8[self.size = size]; + for(uint address : range(size)) self.data[address] = fill; + } + + inline auto data() -> uint8* override { + return self.data; + } + + inline auto size() const -> uint override { + return self.size; + } + + inline auto read(uint24 address, uint8 data = 0) -> uint8 override { + return self.data[address]; + } + + inline auto write(uint24 address, uint8 data) -> void override { + } + + inline auto operator[](uint24 address) const -> uint8 { + return self.data[address]; + } + +private: + struct { + uint8* data = nullptr; + uint size = 0; + } self; +}; diff --git a/higan/sfc/memory/writable.hpp b/higan/sfc/memory/writable.hpp new file mode 100644 index 00000000..dbac10d2 --- /dev/null +++ b/higan/sfc/memory/writable.hpp @@ -0,0 +1,38 @@ +struct WritableMemory : Memory { + inline auto reset() -> void override { + delete[] self.data; + self.data = nullptr; + } + + inline auto allocate(uint size, uint8 fill = 0xff) -> void override { + delete[] self.data; + self.data = new uint8[self.size = size]; + for(uint address : range(size)) self.data[address] = fill; + } + + inline auto data() -> uint8* override { + return self.data; + } + + inline auto size() const -> uint override { + return self.size; + } + + inline auto read(uint24 address, uint8 data = 0) -> uint8 override { + return self.data[address]; + } + + inline auto write(uint24 address, uint8 data) -> void override { + self.data[address] = data; + } + + inline auto operator[](uint24 address) -> uint8& { + return self.data[address]; + } + +private: + struct { + uint8* data = nullptr; + uint size = 0; + } self; +}; diff --git a/higan/sfc/sfc.hpp b/higan/sfc/sfc.hpp index 9c99458c..07aea368 100644 --- a/higan/sfc/sfc.hpp +++ b/higan/sfc/sfc.hpp @@ -20,8 +20,6 @@ #include #endif -//#define ACCURATE_SA1 - namespace SuperFamicom { #define platform Emulator::platform namespace File = Emulator::File; diff --git a/higan/sfc/slot/bsmemory/bsmemory.cpp b/higan/sfc/slot/bsmemory/bsmemory.cpp index c265f5a5..278da8cc 100644 --- a/higan/sfc/slot/bsmemory/bsmemory.cpp +++ b/higan/sfc/slot/bsmemory/bsmemory.cpp @@ -14,14 +14,14 @@ auto BSMemory::unload() -> void { } auto BSMemory::power() -> void { - regs.command = 0; + regs.command = 0; regs.writeOld = 0x00; regs.writeNew = 0x00; regs.flashEnable = false; regs.readEnable = false; regs.writeEnable = false; - memory.writeProtect(!regs.writeEnable); + memory.writable(regs.writeEnable); } auto BSMemory::data() -> uint8* { @@ -117,7 +117,7 @@ auto BSMemory::write(uint24 addr, uint8 data) -> void { regs.writeEnable = false; } - memory.writeProtect(!regs.writeEnable); + memory.writable(regs.writeEnable); } } diff --git a/higan/sfc/slot/bsmemory/bsmemory.hpp b/higan/sfc/slot/bsmemory/bsmemory.hpp index 28d23c5f..3024c56a 100644 --- a/higan/sfc/slot/bsmemory/bsmemory.hpp +++ b/higan/sfc/slot/bsmemory/bsmemory.hpp @@ -13,7 +13,7 @@ struct BSMemory : Memory { auto serialize(serializer&) -> void; uint pathID = 0; - MappedRAM memory; + ProtectableMemory memory; bool readonly; private: diff --git a/higan/sfc/slot/sufamiturbo/sufamiturbo.cpp b/higan/sfc/slot/sufamiturbo/sufamiturbo.cpp index e1469b32..5c6fc058 100644 --- a/higan/sfc/slot/sufamiturbo/sufamiturbo.cpp +++ b/higan/sfc/slot/sufamiturbo/sufamiturbo.cpp @@ -12,8 +12,6 @@ auto SufamiTurboCartridge::unload() -> void { } auto SufamiTurboCartridge::power() -> void { - rom.writeProtect(true); - ram.writeProtect(false); } } diff --git a/higan/sfc/slot/sufamiturbo/sufamiturbo.hpp b/higan/sfc/slot/sufamiturbo/sufamiturbo.hpp index ea73942f..6f196bf7 100644 --- a/higan/sfc/slot/sufamiturbo/sufamiturbo.hpp +++ b/higan/sfc/slot/sufamiturbo/sufamiturbo.hpp @@ -4,8 +4,8 @@ struct SufamiTurboCartridge { auto serialize(serializer&) -> void; uint pathID = 0; - MappedRAM rom; - MappedRAM ram; + ReadableMemory rom; + WritableMemory ram; }; extern SufamiTurboCartridge sufamiturboA; diff --git a/higan/target-bsnes/program/hacks.cpp b/higan/target-bsnes/program/hacks.cpp index 13e3ec1d..47b4a0a0 100644 --- a/higan/target-bsnes/program/hacks.cpp +++ b/higan/target-bsnes/program/hacks.cpp @@ -3,17 +3,18 @@ auto Program::hackCompatibility() -> void { bool fastPPUNoSpriteLimit = emulatorSettings.noSpriteLimit.checked(); bool fastPPUHiresMode7 = emulatorSettings.hiresMode7.checked(); bool fastDSP = emulatorSettings.fastDSPOption.checked(); + bool coprocessorsDelayedSync = emulatorSettings.coprocessorsDelayedSyncOption.checked(); auto title = superFamicom.title; if(title == "AIR STRIKE PATROL" || title == "DESERT FIGHTER") fastPPU = false; if(title == "KOUSHIEN_2") fastDSP = false; if(title == "RENDERING RANGER R2") fastDSP = false; - //todo: update to new emulator->configuration API - emulator->set("Fast PPU", fastPPU); - emulator->set("Fast PPU/No Sprite Limit", fastPPUNoSpriteLimit); - emulator->set("Fast PPU/Hires Mode 7", fastPPUHiresMode7); - emulator->set("Fast DSP", fastDSP); + emulator->configure("Hacks/FastPPU/Enable", fastPPU); + emulator->configure("Hacks/FastPPU/NoSpriteLimit", fastPPUNoSpriteLimit); + emulator->configure("Hacks/FastPPU/HiresMode7", fastPPUHiresMode7); + emulator->configure("Hacks/FastDSP/Enable", fastDSP); + emulator->configure("Hacks/Coprocessors/DelayedSync", coprocessorsDelayedSync); } auto Program::hackPatchMemory(vector& data) -> void { diff --git a/higan/target-bsnes/settings/emulator.cpp b/higan/target-bsnes/settings/emulator.cpp index 5fb04ca3..6ce69274 100644 --- a/higan/target-bsnes/settings/emulator.cpp +++ b/higan/target-bsnes/settings/emulator.cpp @@ -57,6 +57,9 @@ auto EmulatorSettings::create() -> void { fastDSPOption.setText("Fast DSP").setChecked(settings.emulator.hack.fastDSP.enable).onToggle([&] { settings.emulator.hack.fastDSP.enable = fastDSPOption.checked(); }); + coprocessorsDelayedSyncOption.setText("Fast coprocessors (delayed sync)").setChecked(settings.emulator.hack.coprocessors.delayedSync).onToggle([&] { + settings.emulator.hack.coprocessors.delayedSync = coprocessorsDelayedSyncOption.checked(); + }); superFXLabel.setText("SuperFX clock speed:"); superFXValue.setAlignment(0.5); superFXClock.setLength(71).setPosition((settings.emulator.hack.fastSuperFX - 100) / 10).onChange([&] { @@ -71,4 +74,5 @@ auto EmulatorSettings::updateConfiguration() -> void { emulator->configure("Hacks/FastPPU/NoSpriteLimit", noSpriteLimit.checked()); emulator->configure("Hacks/FastPPU/HiresMode7", hiresMode7.checked()); emulator->configure("Hacks/FastDSP/Enable", fastDSPOption.checked()); + emulator->configure("Hacks/Coprocessor/DelayedSync", coprocessorsDelayedSyncOption.checked()); } diff --git a/higan/target-bsnes/settings/settings.cpp b/higan/target-bsnes/settings/settings.cpp index a56e82a5..0f6c3dd5 100644 --- a/higan/target-bsnes/settings/settings.cpp +++ b/higan/target-bsnes/settings/settings.cpp @@ -90,17 +90,18 @@ auto Settings::process(bool load) -> void { bind(text, "Path/Recent/SufamiTurboA", path.recent.sufamiTurboA); bind(text, "Path/Recent/SufamiTurboB", path.recent.sufamiTurboB); - bind(boolean, "Emulator/WarnOnUnverifiedGames", emulator.warnOnUnverifiedGames); - bind(boolean, "Emulator/AutoSaveMemory/Enable", emulator.autoSaveMemory.enable); - bind(natural, "Emulator/AutoSaveMemory/Interval", emulator.autoSaveMemory.interval); - bind(boolean, "Emulator/AutoSaveStateOnUnload", emulator.autoSaveStateOnUnload); - bind(boolean, "Emulator/AutoLoadStateOnLoad", emulator.autoLoadStateOnLoad); - bind(boolean, "Emulator/Hack/FastPPU/Enable", emulator.hack.fastPPU.enable); - bind(boolean, "Emulator/Hack/FastPPU/NoSpriteLimit", emulator.hack.fastPPU.noSpriteLimit); - bind(boolean, "Emulator/Hack/FastPPU/HiresMode7", emulator.hack.fastPPU.hiresMode7); - bind(boolean, "Emulator/Hack/FastDSP/Enable", emulator.hack.fastDSP.enable); - bind(natural, "Emulator/Hack/FastSuperFX", emulator.hack.fastSuperFX); - bind(boolean, "Emulator/Cheats/Enable", emulator.cheats.enable); + bind(boolean, "Emulator/WarnOnUnverifiedGames", emulator.warnOnUnverifiedGames); + bind(boolean, "Emulator/AutoSaveMemory/Enable", emulator.autoSaveMemory.enable); + bind(natural, "Emulator/AutoSaveMemory/Interval", emulator.autoSaveMemory.interval); + bind(boolean, "Emulator/AutoSaveStateOnUnload", emulator.autoSaveStateOnUnload); + bind(boolean, "Emulator/AutoLoadStateOnLoad", emulator.autoLoadStateOnLoad); + bind(boolean, "Emulator/Hack/FastPPU/Enable", emulator.hack.fastPPU.enable); + bind(boolean, "Emulator/Hack/FastPPU/NoSpriteLimit", emulator.hack.fastPPU.noSpriteLimit); + bind(boolean, "Emulator/Hack/FastPPU/HiresMode7", emulator.hack.fastPPU.hiresMode7); + bind(boolean, "Emulator/Hack/FastDSP/Enable", emulator.hack.fastDSP.enable); + bind(boolean, "Emulator/Hack/Coprocessors/DelayedSync", emulator.hack.coprocessors.delayedSync); + bind(natural, "Emulator/Hack/FastSuperFX", emulator.hack.fastSuperFX); + bind(boolean, "Emulator/Cheats/Enable", emulator.cheats.enable); bind(boolean, "General/StatusBar", general.statusBar); bind(boolean, "General/ScreenSaver", general.screenSaver); diff --git a/higan/target-bsnes/settings/settings.hpp b/higan/target-bsnes/settings/settings.hpp index b87ef701..7e2d65f3 100644 --- a/higan/target-bsnes/settings/settings.hpp +++ b/higan/target-bsnes/settings/settings.hpp @@ -81,6 +81,9 @@ struct Settings : Markup::Node { struct FastDSP { bool enable = true; } fastDSP; + struct Coprocessors { + bool delayedSync = true; + } coprocessors; uint fastSuperFX = 100; } hack; struct Cheats { @@ -256,6 +259,7 @@ public: CheckLabel noSpriteLimit{&fastPPULayout, Size{0, 0}}; CheckLabel hiresMode7{&fastPPULayout, Size{0, 0}}; CheckLabel fastDSPOption{&layout, Size{~0, 0}}; + CheckLabel coprocessorsDelayedSyncOption{&layout, Size{~0, 0}}; HorizontalLayout superFXLayout{&layout, Size{~0, 0}}; Label superFXLabel{&superFXLayout, Size{0, 0}}; Label superFXValue{&superFXLayout, Size{50, 0}}; diff --git a/nall/inode.hpp b/nall/inode.hpp index 5eeb59dc..3d64a05a 100644 --- a/nall/inode.hpp +++ b/nall/inode.hpp @@ -64,11 +64,16 @@ struct inode { stat(name, &data); switch(mode) { #if defined(PLATFORM_WINDOWS) + //on Windows, the last status change time (ctime) holds the file creation time instead case time::create: return data.st_ctime; - #else - //st_birthtime may return -1 or st_atime if it is not supported + #elif defined(PLATFORM_BSD) || defined(PLATFORM_MACOS) + //st_birthtime may return -1 or st_atime if it is not supported by the file system //the best that can be done in this case is to return st_mtime if it's older case time::create: return min((uint)data.st_birthtime, (uint)data.st_mtime); + #else + //Linux simply doesn't support file creation time at all + //this is also our fallback case for unsupported operating systems + case time::create: return data.st_mtime; #endif case time::modify: return data.st_mtime; //for performance reasons, last access time is usually not enabled on various filesystems