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