From d9ea05c4c4d4cc31f418220ab621ba7f911fbbfb Mon Sep 17 00:00:00 2001 From: Near <77224854+near-san@users.noreply.github.com> Date: Mon, 22 Jun 2020 21:49:00 +0000 Subject: [PATCH] Update to ares v114r07 release. - PlayStation: started implementing the CPU SCC (missing some registers still) - PlayStation: implemented basic DMA support --- ares/ares/information.hpp | 2 +- ares/n64/cpu/core/core.cpp | 8 +- ares/n64/cpu/core/disassembler.cpp | 2 +- ares/n64/rdram/rdram.cpp | 3 +- ares/ps1/cpu/core/core.cpp | 68 +++++- ares/ps1/cpu/core/core.hpp | 25 +- ares/ps1/cpu/core/cpu-instructions.cpp | 118 +++++----- ares/ps1/cpu/core/cpu.hpp | 45 +++- ares/ps1/cpu/core/decoder.cpp | 10 +- ares/ps1/cpu/core/decoder.hpp | 116 ++++++++- ares/ps1/cpu/core/disassembler.cpp | 313 +++++++++++++++++++++++++ ares/ps1/cpu/core/disassembler.hpp | 29 +++ ares/ps1/cpu/core/memory.cpp | 44 ++-- ares/ps1/cpu/core/scc-instructions.cpp | 13 + ares/ps1/cpu/core/scc-registers.cpp | 122 ++++++---- ares/ps1/cpu/core/scc.hpp | 68 ++++-- ares/ps1/cpu/cpu.cpp | 13 + ares/ps1/cpu/cpu.hpp | 52 +++- ares/ps1/cpu/dma.cpp | 217 +++++++++++++++++ ares/ps1/gpu/gpu.cpp | 21 ++ ares/ps1/gpu/gpu.hpp | 17 +- ares/ps1/gpu/io.cpp | 28 +++ ares/ps1/memory/bus.hpp | 33 ++- ares/ps1/memory/memory.cpp | 1 + ares/ps1/memory/memory.hpp | 42 +++- ares/ps1/ps1.hpp | 1 + ares/ps1/spu/spu.cpp | 11 + ares/ps1/spu/spu.hpp | 2 + ares/ps1/system/system.cpp | 8 + hiro/cocoa/widget/hex-edit.cpp | 2 +- hiro/cocoa/widget/widget.cpp | 3 +- lucia/emulator/playstation.cpp | 11 + 32 files changed, 1251 insertions(+), 197 deletions(-) create mode 100644 ares/ps1/cpu/core/disassembler.cpp create mode 100644 ares/ps1/cpu/core/disassembler.hpp create mode 100644 ares/ps1/cpu/dma.cpp create mode 100644 ares/ps1/gpu/io.cpp diff --git a/ares/ares/information.hpp b/ares/ares/information.hpp index 77bb4c16d..6778e2f34 100644 --- a/ares/ares/information.hpp +++ b/ares/ares/information.hpp @@ -2,7 +2,7 @@ namespace ares { static const string Name = "ares"; - static const string Version = "114.6"; + static const string Version = "114.7"; static const string Copyright = "ares team"; static const string License = "BY-NC-ND 4.0"; static const string LicenseURI = "https://creativecommons.org/licenses/by-nc-nd/4.0/"; diff --git a/ares/n64/cpu/core/core.cpp b/ares/n64/cpu/core/core.cpp index 08abd92bc..44995e51e 100644 --- a/ares/n64/cpu/core/core.cpp +++ b/ares/n64/cpu/core/core.cpp @@ -89,9 +89,9 @@ auto CPU::instructionDebug() -> void { pipeline.instruction = readWord(pipeline.address)(0); static vector mask; - if(!mask) mask.resize(0x08000000); - if(mask[(PC & 0x1fffffff) >> 2]) return; - mask[(PC & 0x1fffffff) >> 2] = 1; + if(!mask) mask.resize(0x0800'0000); + if(mask[(PC & 0x1fff'ffff) >> 2]) return; + mask[(PC & 0x1fff'ffff) >> 2] = 1; static uint counter = 0; //if(++counter > 100) return; @@ -110,7 +110,7 @@ auto CPU::powerR4300(bool reset) -> void { LO.u64 = 0; HI.u64 = 0; GPR[Core::Register::SP].u64 = u32(0xa400'1ff0); - PC = u32(0xbfc00000); + PC = u32(0xbfc0'0000); branch.reset(); fesetround(FE_TONEAREST); context.setMode(); diff --git a/ares/n64/cpu/core/disassembler.cpp b/ares/n64/cpu/core/disassembler.cpp index d0c90e83c..b0ffc101c 100644 --- a/ares/n64/cpu/core/disassembler.cpp +++ b/ares/n64/cpu/core/disassembler.cpp @@ -445,7 +445,7 @@ auto CPU::Disassembler::sccRegisterName(uint index) const -> string { } auto CPU::Disassembler::sccRegisterValue(uint index) const -> string { - if(showValues) return {sccRegisterName(index), hint("{$", hex(self.getControlRegister(index)), "}")}; + if(showValues) return {sccRegisterName(index), hint("{$", hex(self.getControlRegister(index), 8L), "}")}; return sccRegisterName(index); } diff --git a/ares/n64/rdram/rdram.cpp b/ares/n64/rdram/rdram.cpp index 8777bae61..0e0b071a3 100644 --- a/ares/n64/rdram/rdram.cpp +++ b/ares/n64/rdram/rdram.cpp @@ -19,7 +19,8 @@ auto RDRAM::load(Node::Object parent) -> void { auto RDRAM::unload() -> void { ram.reset(); - debugger = {}; + debugger.reset(); + node.reset(); } auto RDRAM::power() -> void { diff --git a/ares/ps1/cpu/core/core.cpp b/ares/ps1/cpu/core/core.cpp index 05eec5dc3..068ff671f 100644 --- a/ares/ps1/cpu/core/core.cpp +++ b/ares/ps1/cpu/core/core.cpp @@ -4,34 +4,90 @@ #include "cpu-instructions.cpp" #include "scc-instructions.cpp" #include "gte-instructions.cpp" +#include "disassembler.cpp" #include "serialization.cpp" +#define PC core.pc + auto CPU::raiseException(uint code, uint coprocessor) -> void { + scc.status.frame[2] = scc.status.frame[1]; + scc.status.frame[1] = scc.status.frame[0]; + scc.status.frame[0] = {}; + + scc.epc = PC; + scc.cause.exceptionCode = code; + scc.cause.coprocessorError = coprocessor; + if(scc.cause.branchDelay = delay.branch.inDelaySlot()) scc.epc -= 4; + + PC = !scc.status.vectorLocation ? 0x8000'0080 : 0xbfc0'0180; + delay.branch.exception(); } auto CPU::instruction() -> void { + auto address = PC; + + if constexpr(1) { + pipeline.address = address; + pipeline.instruction = bus.readWord(address); + //instructionDebug(); + decoderEXECUTE(); + instructionEpilogue(); + step(1); + } } auto CPU::instructionEpilogue() -> bool { + if(delay.load.target) { + *delay.load.target = delay.load.source; + delay.load.target = nullptr; + } + + if(delay.fetch.target) { + delay.load = delay.fetch; + delay.fetch.target = nullptr; + } + + if(delay.write.target) { + *delay.write.target = delay.write.source; + delay.write.target = nullptr; + } + core.r[0] = 0; - switch(branch.state) { - case Branch::Step: core.pc += 4; return 0; - case Branch::Take: core.pc += 4; branch.delaySlot(); return 0; - case Branch::DelaySlot: core.pc = branch.pc; branch.reset(); return 1; - case Branch::Exception: branch.reset(); return 1; + switch(delay.branch.state) { + case Delay::Branch::Step: PC += 4; return 0; + case Delay::Branch::Take: PC += 4; delay.branch.delaySlot(); return 0; + case Delay::Branch::DelaySlot: PC = delay.branch.pc; delay.branch.reset(); return 1; + case Delay::Branch::Exception: delay.branch.reset(); return 1; } unreachable; } auto CPU::instructionDebug() -> void { + pipeline.address = PC; + pipeline.instruction = bus.readWord(pipeline.address); + + static vector mask; + if(!mask) mask.resize(0x0800'0000); +//if(mask[(PC & 0x1fff'ffff) >> 2]) return; + mask[(PC & 0x1fff'ffff) >> 2] = 1; + + static uint counter = 0; +//if(++counter > 100) return; + print( + disassembler.hint(hex(pipeline.address, 8L)), " ", + disassembler.hint(hex(pipeline.instruction, 8L)), " ", + disassembler.disassemble(pipeline.address, pipeline.instruction), "\n" + ); } +#undef PC + auto CPU::powerCore(bool reset) -> void { for(uint n : range(32)) core.r[n] = 0; core.lo = 0; core.hi = 0; core.pc = 0xbfc0'0000; - branch.reset(); + delay = {}; } diff --git a/ares/ps1/cpu/core/core.hpp b/ares/ps1/cpu/core/core.hpp index 447306276..80fba4819 100644 --- a/ares/ps1/cpu/core/core.hpp +++ b/ares/ps1/cpu/core/core.hpp @@ -7,23 +7,20 @@ auto powerCore(bool reset) -> void; //memory.cpp - auto readAddress (u32 address) -> maybe; - auto writeAddress(u32 address) -> maybe; + auto readByte(u32 address) -> u8; + auto readHalf(u32 address) -> u16; + auto readWord(u32 address) -> u32; - auto readByte(u32 address) -> maybe< u8>; - auto readHalf(u32 address) -> maybe; - auto readWord(u32 address) -> maybe; - - auto writeByte(u32 address, u8 data) -> bool; - auto writeHalf(u32 address, u16 data) -> bool; - auto writeWord(u32 address, u32 data) -> bool; + auto writeByte(u32 address, u8 data) -> void; + auto writeHalf(u32 address, u16 data) -> void; + auto writeWord(u32 address, u32 data) -> void; //decoder.cpp auto decoderEXECUTE() -> void; auto decoderSPECIAL() -> void; auto decoderREGIMM() -> void; - auto decoderCOP0() -> void; - auto decoderCOP2() -> void; + auto decoderSCC() -> void; + auto decoderGTE() -> void; auto instructionCOP1() -> void; auto instructionCOP3() -> void; @@ -36,6 +33,12 @@ #include "cpu.hpp" #include "scc.hpp" #include "gte.hpp" + #include "disassembler.hpp" + + struct Pipeline { + u32 address; + u32 instruction; + } pipeline; static constexpr bool Endian = 0; //0 = little, 1 = big static constexpr uint FlipLE = (Endian == 0 ? 3 : 0); diff --git a/ares/ps1/cpu/core/cpu-instructions.cpp b/ares/ps1/cpu/core/cpu-instructions.cpp index 2091f8604..7690339ab 100644 --- a/ares/ps1/cpu/core/cpu-instructions.cpp +++ b/ares/ps1/cpu/core/cpu-instructions.cpp @@ -4,61 +4,63 @@ #define LO core.lo auto CPU::instructionADD(u32& rd, cu32& rs, cu32& rt) -> void { - rd = rs + rt; + if(~(rs ^ rt) & (rs ^ rs + rt) & 0x8000'0000) return exception.arithmeticOverflow(); + write(rd, rs + rt); } auto CPU::instructionADDI(u32& rt, cu32& rs, i16 imm) -> void { - rt = rs + imm; + if(~(rs ^ rt) & (rs ^ rs + imm) & 0x8000'0000) return exception.arithmeticOverflow(); + write(rt, rs + imm); } auto CPU::instructionADDIU(u32& rt, cu32& rs, i16 imm) -> void { - rt = rs + imm; + write(rt, rs + imm); } auto CPU::instructionADDU(u32& rd, cu32& rs, cu32& rt) -> void { - rd = rs + rt; + write(rd, rs + rt); } auto CPU::instructionAND(u32& rd, cu32& rs, cu32& rt) -> void { - rd = rs & rt; + write(rd, rs & rt); } auto CPU::instructionANDI(u32& rt, cu32& rs, u16 imm) -> void { - rt = rs & imm; + write(rt, rs & imm); } auto CPU::instructionBEQ(cu32& rs, cu32& rt, i16 imm) -> void { - if(rs == rt) branch.take(PC + 4 + (imm << 2)); + if(rs == rt) branch(PC + 4 + (imm << 2)); } auto CPU::instructionBGEZ(ci32& rs, i16 imm) -> void { - if(rs >= 0) branch.take(PC + 4 + (imm << 2)); + if(rs >= 0) branch(PC + 4 + (imm << 2)); } auto CPU::instructionBGEZAL(ci32& rs, i16 imm) -> void { RA = PC + 8; - if(rs >= 0) branch.take(PC + 4 + (imm << 2)); + if(rs >= 0) branch(PC + 4 + (imm << 2)); } auto CPU::instructionBGTZ(ci32& rs, i16 imm) -> void { - if(rs > 0) branch.take(PC + 4 + (imm << 2)); + if(rs > 0) branch(PC + 4 + (imm << 2)); } auto CPU::instructionBLEZ(ci32& rs, i16 imm) -> void { - if(rs <= 0) branch.take(PC + 4 + (imm << 2)); + if(rs <= 0) branch(PC + 4 + (imm << 2)); } auto CPU::instructionBLTZ(ci32& rs, i16 imm) -> void { - if(rs < 0) branch.take(PC + 4 + (imm << 2)); + if(rs < 0) branch(PC + 4 + (imm << 2)); } auto CPU::instructionBLTZAL(ci32& rs, i16 imm) -> void { RA = PC + 8; - if(rs < 0) branch.take(PC + 4 + (imm << 2)); + if(rs < 0) branch(PC + 4 + (imm << 2)); } auto CPU::instructionBNE(cu32& rs, cu32& rt, i16 imm) -> void { - if(rs != rt) branch.take(PC + 4 + (imm << 2)); + if(rs != rt) branch(PC + 4 + (imm << 2)); } auto CPU::instructionBREAK() -> void { @@ -87,71 +89,74 @@ auto CPU::instructionDIVU(cu32& rs, cu32& rt) -> void { } auto CPU::instructionJ(u32 imm) -> void { - branch.take((PC + 4 & 0xf000'0000) | (imm << 2)); + branch((PC + 4 & 0xf000'0000) | (imm << 2)); } auto CPU::instructionJAL(u32 imm) -> void { - RA = PC + 8; - branch.take((PC + 4 & 0xf000'0000) | (imm << 2)); + write(RA, PC + 8); + branch((PC + 4 & 0xf000'0000) | (imm << 2)); } auto CPU::instructionJALR(u32& rd, cu32& rs) -> void { - rd = PC + 8; - branch.take(rs); + write(rd, PC + 8); + branch(rs); } auto CPU::instructionJR(cu32& rs) -> void { - branch.take(rs); + branch(rs); } auto CPU::instructionLB(u32& rt, cu32& rs, i16 imm) -> void { - if(auto data = readByte(rs + imm)) rt = i8(*data); + auto data = readByte(rs + imm); + fetch(rt, i8(data)); } auto CPU::instructionLBU(u32& rt, cu32& rs, i16 imm) -> void { - if(auto data = readByte(rs + imm)) rt = u8(*data); + auto data = readByte(rs + imm); + fetch(rt, u8(data)); } auto CPU::instructionLH(u32& rt, cu32& rs, i16 imm) -> void { - if(auto data = readHalf(rs + imm)) rt = i16(*data); + auto data = readHalf(rs + imm); + fetch(rt, i16(data)); } auto CPU::instructionLHU(u32& rt, cu32& rs, i16 imm) -> void { - if(auto data = readHalf(rs + imm)) rt = u16(*data); + auto data = readHalf(rs + imm); + fetch(rt, u16(data)); } auto CPU::instructionLUI(u32& rt, u16 imm) -> void { - rt = imm << 16; + write(rt, imm << 16); } auto CPU::instructionLW(u32& rt, cu32& rs, i16 imm) -> void { - if(auto data = readWord(rs + imm)) rt = i32(*data); + auto data = readWord(rs + imm); + fetch(rt, i32(data)); } auto CPU::instructionLWL(u32& rt, cu32& rs, i16 imm) -> void { auto address = rs + imm; auto shift = 8 * ((address ^ FlipLE) & 3); auto mask = u32(0) - 1 << shift; - if(auto data = readWord(address & ~3)) { - rt = rt & ~mask | *data << shift; - } + auto data = readWord(address & ~3); + fetch(rt, rt & ~mask | data << shift); } auto CPU::instructionLWR(u32& rt, cu32& rs, i16 imm) -> void { auto address = rs + imm; auto shift = 8 * ((address ^ FlipBE) & 3); auto mask = u32(0) - 1 >> shift; - if(auto data = readWord(address & ~3)) { - rt = rt & ~mask | *data >> shift; - } + auto data = readWord(address & ~3); + fetch(rt, rt & ~mask | data >> shift); } auto CPU::instructionMFHI(u32& rd) -> void { - rd = HI; + write(rd, HI); } auto CPU::instructionMFLO(u32& rd) -> void { - rd = LO; + write(rd, LO); } auto CPU::instructionMTHI(cu32& rs) -> void { @@ -175,15 +180,15 @@ auto CPU::instructionMULTU(cu32& rs, cu32& rt) -> void { } auto CPU::instructionNOR(u32& rd, cu32& rs, cu32& rt) -> void { - rd = ~(rs | rt); + write(rd, ~(rs | rt)); } auto CPU::instructionOR(u32& rd, cu32& rs, cu32& rt) -> void { - rd = rs | rt; + write(rd, rs | rt); } auto CPU::instructionORI(u32& rt, cu32& rs, u16 imm) -> void { - rt = rs | imm; + write(rt, rs | imm); } auto CPU::instructionSB(cu32& rt, cu32& rs, i16 imm) -> void { @@ -195,51 +200,52 @@ auto CPU::instructionSH(cu32& rt, cu32& rs, i16 imm) -> void { } auto CPU::instructionSLL(u32& rd, cu32& rt, u8 sa) -> void { - rd = rt << sa; + write(rd, rt << sa); } auto CPU::instructionSLLV(u32& rd, cu32& rt, cu32& rs) -> void { - rd = rt << (rs & 31); + write(rd, rt << (rs & 31)); } auto CPU::instructionSLT(u32& rd, ci32& rs, ci32& rt) -> void { - rd = rs < rt; + write(rd, rs < rt); } auto CPU::instructionSLTI(u32& rt, ci32& rs, i16 imm) -> void { - rt = rs < imm; + write(rt, rs < imm); } auto CPU::instructionSLTIU(u32& rt, cu32& rs, i16 imm) -> void { - rt = rs < imm; + write(rt, rs < imm); } auto CPU::instructionSLTU(u32& rd, cu32& rs, cu32& rt) -> void { - rd = rs < rt; + write(rd, rs < rt); } auto CPU::instructionSRA(u32& rd, ci32& rt, u8 sa) -> void { - rd = rt >> sa; + write(rd, rt >> sa); } auto CPU::instructionSRAV(u32& rd, ci32& rt, cu32& rs) -> void { - rd = rt >> (rs & 31); + write(rd, rt >> (rs & 31)); } auto CPU::instructionSRL(u32& rd, cu32& rt, u8 sa) -> void { - rd = rt >> sa; + write(rd, rt >> sa); } auto CPU::instructionSRLV(u32& rd, cu32& rt, cu32& rs) -> void { - rd = rt >> (rs & 31); + write(rd, rt >> (rs & 31)); } auto CPU::instructionSUB(u32& rd, cu32& rs, cu32& rt) -> void { - rd = rs - rt; + if((rs ^ rt) & (rs ^ rs + rt) & 0x8000'0000) return exception.arithmeticOverflow(); + write(rd, rs - rt); } auto CPU::instructionSUBU(u32& rd, cu32& rs, cu32& rt) -> void { - rd = rs - rt; + write(rd, rs - rt); } auto CPU::instructionSW(cu32& rt, cu32& rs, i16 imm) -> void { @@ -250,18 +256,16 @@ auto CPU::instructionSWL(cu32& rt, cu32& rs, i16 imm) -> void { auto address = rs + imm; auto shift = 8 * ((address ^ FlipLE) & 3); auto mask = u32(0) - 1 >> shift; - if(auto data = readWord(address & ~3)) { - writeWord(address & ~3, *data & ~mask | rt >> shift); - } + auto data = readWord(address & ~3); + writeWord(address & ~3, data & ~mask | rt >> shift); } auto CPU::instructionSWR(cu32& rt, cu32& rs, i16 imm) -> void { auto address = rs + imm; auto shift = 8 * ((address ^ FlipBE) & 3); auto mask = u32(0) - 1 << shift; - if(auto data = readWord(address & ~3)) { - writeWord(address & ~3, *data & ~mask | rt << shift); - } + auto data = readWord(address & ~3); + writeWord(address & ~3, data & ~mask | rt << shift); } auto CPU::instructionSYSCALL() -> void { @@ -269,11 +273,11 @@ auto CPU::instructionSYSCALL() -> void { } auto CPU::instructionXOR(u32& rd, cu32& rs, cu32& rt) -> void { - rd = rs ^ rt; + write(rd, rs ^ rt); } auto CPU::instructionXORI(u32& rt, cu32& rs, u16 imm) -> void { - rt = rs ^ imm; + write(rt, rs ^ imm); } #undef PC diff --git a/ares/ps1/cpu/core/cpu.hpp b/ares/ps1/cpu/core/cpu.hpp index 20edfb3fd..a2bcdd5a3 100644 --- a/ares/ps1/cpu/core/cpu.hpp +++ b/ares/ps1/cpu/core/cpu.hpp @@ -14,7 +14,7 @@ K0, K1, //kernel registers GP, //global pointer SP, //stack pointer - S8, //saved register + FP, //frame pointer RA, //return address }; @@ -24,18 +24,41 @@ u32 pc; } core; - struct Branch { - enum : u32 { Step, Take, DelaySlot, Exception }; + struct Delay { + //load delay slot + struct { + u32* target = nullptr; + u32 source = 0; + } load, fetch, write; - auto inDelaySlot() const -> bool { return state == DelaySlot; } - auto reset() -> void { state = Step; } - auto take(u32 address) -> void { state = Take; pc = address; } - auto delaySlot() -> void { state = DelaySlot; } - auto exception() -> void { state = Exception; } + //branch delay slot + struct Branch { + enum : u32 { Step, Take, DelaySlot, Exception }; - u32 pc; - u32 state; - } branch; + auto inDelaySlot() const -> bool { return state == DelaySlot; } + auto reset() -> void { state = Step; } + auto take(u32 address) -> void { state = Take; pc = address; } + auto delaySlot() -> void { state = DelaySlot; } + auto exception() -> void { state = Exception; } + + u32 pc = 0; + u32 state = Step; + } branch; + } delay; + + auto fetch(u32& target, u32 source) -> void { + delay.fetch.target = ⌖ + delay.fetch.source = source; + } + + auto write(u32& target, u32 source) -> void { + delay.write.target = ⌖ + delay.write.source = source; + } + + auto branch(u32 address) -> void { + delay.branch.take(address); + } //cpu-instructions.cpp auto instructionADD(u32& rd, cu32& rs, cu32& rt) -> void; diff --git a/ares/ps1/cpu/core/decoder.cpp b/ares/ps1/cpu/core/decoder.cpp index 38c19d70b..33a295dc2 100644 --- a/ares/ps1/cpu/core/decoder.cpp +++ b/ares/ps1/cpu/core/decoder.cpp @@ -1,4 +1,4 @@ -#define OP 0 +#define OP pipeline.instruction #define RD core.r[RDn] #define RT core.r[RTn] #define RS core.r[RSn] @@ -22,13 +22,13 @@ auto CPU::decoderREGIMM() -> void { #include "decoder.hpp" } -auto CPU::decoderCOP0() -> void { - #define DECODER_COP0 +auto CPU::decoderSCC() -> void { + #define DECODER_SCC #include "decoder.hpp" } -auto CPU::decoderCOP2() -> void { - #define DECODER_COP2 +auto CPU::decoderGTE() -> void { + #define DECODER_GTE #include "decoder.hpp" } diff --git a/ares/ps1/cpu/core/decoder.hpp b/ares/ps1/cpu/core/decoder.hpp index e6ee52c0f..62066eb9b 100644 --- a/ares/ps1/cpu/core/decoder.hpp +++ b/ares/ps1/cpu/core/decoder.hpp @@ -27,6 +27,22 @@ op(0x0d, ORI, RT, RS, IMMu16); op(0x0e, XORI, RT, RS, IMMu16); op(0x0f, LUI, RT, IMMu16); + jp(0x10, SCC); + op(0x11, COP1); + jp(0x12, GTE); + op(0x13, COP3); + op(0x14, INVALID); + op(0x15, INVALID); + op(0x16, INVALID); + op(0x17, INVALID); + op(0x18, INVALID); + op(0x19, INVALID); + op(0x1a, INVALID); + op(0x1b, INVALID); + op(0x1c, INVALID); + op(0x1d, INVALID); + op(0x1e, INVALID); + op(0x1f, INVALID); op(0x20, LB, RT, RS, IMMi16); op(0x21, LH, RT, RS, IMMi16); op(0x22, LWL, RT, RS, IMMi16); @@ -34,11 +50,31 @@ op(0x24, LBU, RT, RS, IMMi16); op(0x25, LHU, RT, RS, IMMi16); op(0x26, LWR, RT, RS, IMMi16); + op(0x27, INVALID); op(0x28, SB, RT, RS, IMMi16); op(0x29, SH, RT, RS, IMMi16); op(0x2a, SWL, RT, RS, IMMi16); op(0x2b, SW, RT, RS, IMMi16); + op(0x2c, INVALID); + op(0x2d, INVALID); op(0x2e, SWR, RT, RS, IMMi16); + op(0x2f, INVALID); + op(0x30, INVALID); + op(0x31, INVALID); +//LWC2 + op(0x33, INVALID); + op(0x34, INVALID); + op(0x35, INVALID); + op(0x36, INVALID); + op(0x37, INVALID); + op(0x38, INVALID); + op(0x39, INVALID); +//SWC2 + op(0x3b, INVALID); + op(0x3c, INVALID); + op(0x3d, INVALID); + op(0x3e, INVALID); + op(0x3f, INVALID); } } #undef DECODER_EXECUTE @@ -48,23 +84,37 @@ { switch(OP & 0x3f) { op(0x00, SLL, RD, RT, SA); + op(0x01, INVALID); op(0x02, SRL, RD, RT, SA); op(0x03, SRA, RD, RT, SA); op(0x04, SLLV, RD, RT, SA); + op(0x05, INVALID); op(0x06, SRLV, RD, RT, RS); op(0x07, SRAV, RD, RT, RS); br(0x08, JR, RS); br(0x09, JALR, RD, RS); + op(0x0a, INVALID); + op(0x0b, INVALID); br(0x0c, SYSCALL); br(0x0d, BREAK); + op(0x0e, INVALID); + op(0x0f, INVALID); op(0x10, MFHI, RD); op(0x11, MTHI, RS); op(0x12, MFLO, RD); op(0x13, MTLO, RS); + op(0x14, INVALID); + op(0x15, INVALID); + op(0x16, INVALID); + op(0x17, INVALID); op(0x18, MULT, RS, RT); op(0x19, MULTU, RS, RT); op(0x1a, DIV, RS, RT); op(0x1b, DIVU, RS, RT); + op(0x1c, INVALID); + op(0x1d, INVALID); + op(0x1e, INVALID); + op(0x1f, INVALID); op(0x20, ADD, RD, RS, RT); op(0x21, ADDU, RD, RS, RT); op(0x22, SUB, RD, RS, RT); @@ -73,8 +123,30 @@ op(0x25, OR, RD, RS, RT); op(0x26, XOR, RD, RS, RT); op(0x27, NOR, RD, RS, RT); + op(0x28, INVALID); + op(0x29, INVALID); op(0x2a, SLT, RD, RS, RT); op(0x2b, SLTU, RD, RS, RT); + op(0x2c, INVALID); + op(0x2d, INVALID); + op(0x2e, INVALID); + op(0x2f, INVALID); + op(0x30, INVALID); + op(0x31, INVALID); + op(0x32, INVALID); + op(0x33, INVALID); + op(0x34, INVALID); + op(0x35, INVALID); + op(0x36, INVALID); + op(0x37, INVALID); + op(0x38, INVALID); + op(0x39, INVALID); + op(0x3a, INVALID); + op(0x3b, INVALID); + op(0x3c, INVALID); + op(0x3d, INVALID); + op(0x3e, INVALID); + op(0x3f, INVALID); } } #undef DECODER_SPECIAL @@ -85,23 +157,59 @@ switch(OP >> 16 & 0x1f) { br(0x00, BLTZ, RS, IMMi16); br(0x01, BGEZ, RS, IMMi16); + op(0x02, INVALID); + op(0x03, INVALID); + op(0x04, INVALID); + op(0x05, INVALID); + op(0x06, INVALID); + op(0x07, INVALID); + op(0x08, INVALID); + op(0x09, INVALID); + op(0x0a, INVALID); + op(0x0b, INVALID); + op(0x0c, INVALID); + op(0x0d, INVALID); + op(0x0e, INVALID); + op(0x0f, INVALID); br(0x10, BLTZAL, RS, IMMi16); br(0x11, BGEZAL, RS, IMMi16); + op(0x12, INVALID); + op(0x13, INVALID); + op(0x14, INVALID); + op(0x15, INVALID); + op(0x16, INVALID); + op(0x17, INVALID); + op(0x18, INVALID); + op(0x19, INVALID); + op(0x1a, INVALID); + op(0x1b, INVALID); + op(0x1c, INVALID); + op(0x1d, INVALID); + op(0x1e, INVALID); + op(0x1f, INVALID); } } #undef DECODER_REGIMM #endif -#ifdef DECODER_COP0 +#ifdef DECODER_SCC { + switch(OP >> 21 & 0x1f) { + op(0x00, MFC0, RT, RDn); + op(0x04, MTC0, RT, RDn); + } + + switch(OP & 0x3f) { + op(0x10, RFE); + } } -#undef DECODER_COP0 +#undef DECODER_SCC #endif -#ifdef DECODER_COP2 +#ifdef DECODER_GTE { } -#undef DECODER_COP2 +#undef DECODER_GTE #endif #undef SA diff --git a/ares/ps1/cpu/core/disassembler.cpp b/ares/ps1/cpu/core/disassembler.cpp new file mode 100644 index 000000000..afb66e628 --- /dev/null +++ b/ares/ps1/cpu/core/disassembler.cpp @@ -0,0 +1,313 @@ +auto CPU::Disassembler::disassemble(u32 address, u32 instruction) -> string { + this->address = address; + this->instruction = instruction; + + auto v = EXECUTE(); + if(!v) v.append("invalid", string{"$", hex(instruction, 8L)}); + if(!instruction) v = {"nop"}; + auto s = pad(v.takeFirst(), -8L); + return {s, v.merge(",")}; +} + +auto CPU::Disassembler::EXECUTE() -> vector { + auto rtName = [&] { return cpuRegisterName (instruction >> 16 & 31); }; + auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); }; + auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); }; + auto jump = [&] { return immediate(address + 4 & 0xf000'0000 | (instruction & 0x03ff'ffff) << 2, 32L); }; + auto branch = [&] { return immediate(address + 4 + (i16(instruction) << 2), 32L); }; + auto offset = [&] { return cpuRegisterIndex(instruction >> 21 & 31, i16(instruction)); }; + + auto ALU = [&](string_view name) -> vector { + return {name, rtName(), rsValue(), immediate(u16(instruction))}; + }; + + auto ADDI = [&](string_view add, string_view sub, string_view mov) -> vector { + if(!(instruction >> 21 & 31)) return {mov, rtName(), immediate(i16(instruction))}; + return {i16(instruction) >= 0 ? add : sub, rtName(), rsValue(), immediate(abs(i16(instruction)))}; + }; + + auto BRANCH1 = [&](string_view name) -> vector { + return {name, rsValue(), branch()}; + }; + + auto BRANCH2 = [&](string_view name) -> vector { + return {name, rsValue(), rtValue(), branch()}; + }; + + auto JUMP = [&](string_view name) -> vector { + return {name, jump()}; + }; + + auto LOAD = [&](string_view name) -> vector { + return {name, rtName(), offset()}; + }; + + auto STORE = [&](string_view name) -> vector { + return {name, rtValue(), offset()}; + }; + + switch(instruction >> 26) { + case 0x00: return SPECIAL(); + case 0x01: return REGIMM(); + case 0x02: return JUMP("j"); + case 0x03: return JUMP("jal"); + case 0x04: return BRANCH2("beq"); + case 0x05: return BRANCH2("bne"); + case 0x06: return BRANCH1("blez"); + case 0x07: return BRANCH1("bgtz"); + case 0x08: return ADDI("addi", "subi", "li"); + case 0x09: return ADDI("addiu", "subiu", "liu"); + case 0x0a: return ALU("slti"); + case 0x0b: return ALU("sltiu"); + case 0x0c: return ALU("andi"); + case 0x0d: return ALU("ori"); + case 0x0e: return ALU("xori"); + case 0x0f: return {"lui", rtName(), immediate(u16(instruction), 16L)}; + case 0x10: return SCC(); + case 0x11: break; //COP1 + case 0x12: return GTE(); + case 0x13: break; //COP3 + case 0x14: break; + case 0x15: break; + case 0x16: break; + case 0x17: break; + case 0x18: break; + case 0x19: break; + case 0x1a: break; + case 0x1b: break; + case 0x1c: break; + case 0x1d: break; + case 0x1e: break; + case 0x1f: break; + case 0x20: return LOAD("lb"); + case 0x21: return LOAD("lh"); + case 0x22: return LOAD("lwl"); + case 0x23: return LOAD("lw"); + case 0x24: return LOAD("lbu"); + case 0x25: return LOAD("lhu"); + case 0x26: return LOAD("lwr"); + case 0x27: break; + case 0x28: return STORE("sb"); + case 0x29: return STORE("sh"); + case 0x2a: return STORE("swl"); + case 0x2b: return STORE("sw"); + case 0x2c: break; + case 0x2d: break; + case 0x2e: return STORE("swr"); + case 0x2f: break; + case 0x30: break; + } + + return {}; +} + +auto CPU::Disassembler::SPECIAL() -> vector { + auto shift = [&] { return string{instruction >> 6 & 31}; }; + auto rdName = [&] { return cpuRegisterName (instruction >> 11 & 31); }; + auto rdValue = [&] { return cpuRegisterValue(instruction >> 11 & 31); }; + auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); }; + auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); }; + + auto ALU = [&](string_view name, string_view by) -> vector { + return {name, rdName(), rtValue(), by}; + }; + + auto JALR = [&](string_view name) -> vector { + if((instruction >> 11 & 31) == 31) return {name, rsValue()}; + return {name, rdName(), rsValue()}; + }; + + auto REG = [&](string_view name) -> vector { + return {name, rdName(), rsValue(), rtValue()}; + }; + + auto ST = [&](string_view name) -> vector { + return {name, rsValue(), rtValue()}; + }; + + switch(instruction & 0x3f) { + case 0x00: return ALU("sll", shift()); + case 0x01: break; + case 0x02: return ALU("srl", shift()); + case 0x03: return ALU("sra", shift()); + case 0x04: return ALU("sllv", rsValue()); + case 0x05: break; + case 0x06: return ALU("srlv", rsValue()); + case 0x07: return ALU("srav", rsValue()); + case 0x08: return {"jr", rsValue()}; + case 0x09: return JALR("jalr"); + case 0x0a: break; + case 0x0b: break; + case 0x0c: return {"syscall"}; + case 0x0d: return {"break"}; + case 0x0e: break; + case 0x0f: break; + case 0x10: return {"mfhi", rdName(), {"hi", hint("{$", hex(self.core.hi, 8L), "}")}}; + case 0x11: return {"mthi", rsValue(), "hi"}; + case 0x12: return {"mflo", rdName(), {"lo", hint("{$", hex(self.core.lo, 8L), "}")}}; + case 0x13: return {"mtlo", rsValue(), "lo"}; + case 0x14: break; + case 0x15: break; + case 0x16: break; + case 0x17: break; + case 0x18: return ST("mult"); + case 0x19: return ST("multu"); + case 0x1a: return ST("div"); + case 0x1b: return ST("divu"); + case 0x1c: break; + case 0x1d: break; + case 0x1e: break; + case 0x1f: break; + case 0x20: return REG("add"); + case 0x21: return REG("addu"); + case 0x22: return REG("sub"); + case 0x23: return REG("subu"); + case 0x24: return REG("and"); + case 0x25: return REG("or"); + case 0x26: return REG("xor"); + case 0x27: return REG("nor"); + case 0x28: break; + case 0x29: break; + case 0x2a: return REG("slt"); + case 0x2b: return REG("sltu"); + case 0x2c: break; + case 0x2d: break; + case 0x2e: break; + case 0x2f: break; + case 0x30: break; + case 0x31: break; + case 0x32: break; + case 0x33: break; + case 0x34: break; + case 0x35: break; + case 0x36: break; + case 0x37: break; + case 0x38: break; + case 0x39: break; + case 0x3a: break; + case 0x3b: break; + case 0x3c: break; + case 0x3d: break; + case 0x3e: break; + case 0x3f: break; + } + + return {}; +} + +auto CPU::Disassembler::REGIMM() -> vector { + auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); }; + auto branch = [&] { return immediate(address + 4 + (i16(instruction) << 2)); }; + + auto BRANCH = [&](string_view name) -> vector { + return {name, rsValue(), branch()}; + }; + + switch(instruction >> 16 & 0x1f) { + case 0x00: return BRANCH("bltz"); + case 0x01: return BRANCH("bgez"); + case 0x02: break; + case 0x03: break; + case 0x04: break; + case 0x05: break; + case 0x06: break; + case 0x07: break; + case 0x08: break; + case 0x09: break; + case 0x0a: break; + case 0x0b: break; + case 0x0c: break; + case 0x0d: break; + case 0x0e: break; + case 0x0f: break; + case 0x10: return BRANCH("bltzal"); + case 0x11: return BRANCH("bgezal"); + case 0x12: break; + case 0x13: break; + case 0x14: break; + case 0x15: break; + case 0x16: break; + case 0x17: break; + case 0x18: break; + case 0x19: break; + case 0x1a: break; + case 0x1b: break; + case 0x1c: break; + case 0x1d: break; + case 0x1e: break; + case 0x1f: break; + } + + return {}; +} + +auto CPU::Disassembler::SCC() -> vector { + auto rtName = [&] { return cpuRegisterName (instruction >> 16 & 31); }; + auto rtValue = [&] { return cpuRegisterValue(instruction >> 16 & 31); }; + auto sdName = [&] { return sccRegisterName (instruction >> 11 & 31); }; + auto sdValue = [&] { return sccRegisterValue(instruction >> 11 & 31); }; + + switch(instruction >> 21 & 0x1f) { + case 0x00: return {"mfc0", rtName(), sdValue()}; + case 0x04: return {"mtc0", rtValue(), sdName()}; + } + if(!(instruction >> 25 & 1)) return {}; + switch(instruction & 0x3f) { + case 0x10: return {"rfe"}; + } + + return {}; +} + +auto CPU::Disassembler::GTE() -> vector { + return {}; +} + +auto CPU::Disassembler::immediate(i64 value, u8 bits) const -> string { + if(value < 0) return {"-$", hex(-value, bits >> 2)}; + return {"$", hex(value, bits >> 2)}; +} + +auto CPU::Disassembler::cpuRegisterName(u8 index) const -> string { + static const string registers[32] = { + "0", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra", + }; + return registers[index]; +} + +auto CPU::Disassembler::cpuRegisterValue(u8 index) const -> string { + if(index && showValues) return {cpuRegisterName(index), hint("{$", hex(self.core.r[index], 8L), "}")}; + return cpuRegisterName(index); +} + +auto CPU::Disassembler::cpuRegisterIndex(u8 index, i16 offset) const -> string { + string adjust; + if(offset >= 0) adjust = {"+$", hex( offset)}; + if(offset < 0) adjust = {"-$", hex(-offset)}; + if(index && showValues) return {cpuRegisterName(index), adjust, hint("{$", hex(self.core.r[index] + offset, 8L), "}")}; + return {cpuRegisterName(index), adjust}; +} + +auto CPU::Disassembler::sccRegisterName(u8 index) const -> string { + static const string registers[32] = { + "scc0", "scc1", "scc2", "bpc", "scc4", "bda", "tar", "dcic", + "bada", "bdam", "scc10", "bpcm", "sr", "cause", "epc", "prid", + "scc16", "scc17", "scc18", "scc19", "scc20", "scc21", "scc22", "scc23", + "scc24", "scc25", "scc26", "scc27", "scc28", "scc29", "scc30", "scc31", + }; + return registers[index]; +} + +auto CPU::Disassembler::sccRegisterValue(u8 index) const -> string { + if(showValues) return {sccRegisterName(index), hint("{$", hex(self.getControlRegister(index), 8L), "}")}; + return sccRegisterName(index); +} + +template +auto CPU::Disassembler::hint(P&&... p) const -> string { + if(showColors) return {"\e[0m\e[37m", forward

(p)..., "\e[0m"}; + return {forward

(p)...}; +} diff --git a/ares/ps1/cpu/core/disassembler.hpp b/ares/ps1/cpu/core/disassembler.hpp new file mode 100644 index 000000000..5ac535c6e --- /dev/null +++ b/ares/ps1/cpu/core/disassembler.hpp @@ -0,0 +1,29 @@ +//{ + struct Disassembler { + CPU& self; + Disassembler(CPU& self) : self(self) {} + + //disassembler.cpp + auto disassemble(u32 address, u32 instruction) -> string; + template auto hint(P&&... p) const -> string; + + bool showColors = true; + bool showValues = true; + + //private: + auto EXECUTE() -> vector; + auto SPECIAL() -> vector; + auto REGIMM() -> vector; + auto SCC() -> vector; + auto GTE() -> vector; + auto immediate(i64 value, u8 bits = 0) const -> string; + auto cpuRegisterName(u8 index) const -> string; + auto cpuRegisterValue(u8 index) const -> string; + auto cpuRegisterIndex(u8 index, i16 offset) const -> string; + auto sccRegisterName(u8 index) const -> string; + auto sccRegisterValue(u8 index) const -> string; + + u32 address; + u32 instruction; + } disassembler{*this}; +//}; diff --git a/ares/ps1/cpu/core/memory.cpp b/ares/ps1/cpu/core/memory.cpp index 0f4ee821e..9573be322 100644 --- a/ares/ps1/cpu/core/memory.cpp +++ b/ares/ps1/cpu/core/memory.cpp @@ -1,37 +1,29 @@ -auto CPU::readAddress(u32 address) -> maybe { - return nothing; +inline auto CPU::readByte(u32 address) -> u8 { + if(scc.status.cache.isolate) return cache.readByte(address); + return bus.readByte(address); } -auto CPU::writeAddress(u32 address) -> maybe { - return nothing; +inline auto CPU::readHalf(u32 address) -> u16 { + if(scc.status.cache.isolate) return cache.readHalf(address); + return bus.readHalf(address); } -auto CPU::readByte(u32 address) -> maybe { - if(auto physical = readAddress(address)) return bus.readByte(physical()); - return nothing; +inline auto CPU::readWord(u32 address) -> u32 { + if(scc.status.cache.isolate) return cache.readWord(address); + return bus.readWord(address); } -auto CPU::readHalf(u32 address) -> maybe { - if(auto physical = readAddress(address)) return bus.readHalf(physical()); - return nothing; +inline auto CPU::writeByte(u32 address, u8 data) -> void { + if(scc.status.cache.isolate) return cache.writeByte(address, data); + return bus.writeByte(address, data); } -auto CPU::readWord(u32 address) -> maybe { - if(auto physical = readAddress(address)) return bus.readWord(physical()); - return nothing; +inline auto CPU::writeHalf(u32 address, u16 data) -> void { + if(scc.status.cache.isolate) return cache.writeHalf(address, data); + return bus.writeHalf(address, data); } -auto CPU::writeByte(u32 address, u8 data) -> bool { - if(auto physical = writeAddress(address)) return bus.writeByte(physical(), data), true; - return false; -} - -auto CPU::writeHalf(u32 address, u16 data) -> bool { - if(auto physical = writeAddress(address)) return bus.writeHalf(physical(), data), true; - return false; -} - -auto CPU::writeWord(u32 address, u32 data) -> bool { - if(auto physical = writeAddress(address)) return bus.writeWord(physical(), data), true; - return false; +inline auto CPU::writeWord(u32 address, u32 data) -> void { + if(scc.status.cache.isolate) return cache.writeWord(address, data); + return bus.writeWord(address, data); } diff --git a/ares/ps1/cpu/core/scc-instructions.cpp b/ares/ps1/cpu/core/scc-instructions.cpp index e69de29bb..4715d16e0 100644 --- a/ares/ps1/cpu/core/scc-instructions.cpp +++ b/ares/ps1/cpu/core/scc-instructions.cpp @@ -0,0 +1,13 @@ +auto CPU::instructionMFC0(u32& rt, u8 rd) -> void { + fetch(rt, getControlRegister(rd)); +} + +auto CPU::instructionMTC0(cu32& rt, u8 rd) -> void { + setControlRegister(rd, rt); +} + +auto CPU::instructionRFE() -> void { + scc.status.frame[0] = scc.status.frame[1]; + scc.status.frame[1] = scc.status.frame[2]; + scc.status.frame[2] = {}; +} diff --git a/ares/ps1/cpu/core/scc-registers.cpp b/ares/ps1/cpu/core/scc-registers.cpp index 21f368fe6..d2f197294 100644 --- a/ares/ps1/cpu/core/scc-registers.cpp +++ b/ares/ps1/cpu/core/scc-registers.cpp @@ -3,47 +3,66 @@ auto CPU::getControlRegister(u8 index) -> u32 { switch(index & 15) { - case 0: //Index + case 3: //Breakpoint Execute Address + data.bit(0,31) = scc.breakpointExecuteAddress; break; - case 1: //Random + case 5: //Breakpoint Data Address + data.bit(0,31) = scc.breakpointDataAddress; break; - case 2: //BusControl -//case 2: //EntryLo + case 7: //Breakpoint Control + //todo break; - case 3: //Config + case 8: //Bad Virtual Address + data.bit(0,31) = scc.badVirtualAddress; break; - case 4: //Context + case 9: //Breakpoint Data Mask + data.bit(0,31) = scc.breakpointDataMask; break; - case 8: //BadVirtualAddress + case 11: //Breakpoint Execute Mask + data.bit(0,31) = scc.breakpointExecuteMask; break; - case 9: //Count - break; - - case 10: //PortSize -//case 10: //EntryHi - break; - - case 11: //Compare - break; - - case 12: //StatusRegister + case 12: //Status + data.bit( 0) = scc.status.frame[0].interruptEnable; + data.bit( 1) = scc.status.frame[0].userMode; + data.bit( 2) = scc.status.frame[1].interruptEnable; + data.bit( 3) = scc.status.frame[1].userMode; + data.bit( 4) = scc.status.frame[2].interruptEnable; + data.bit( 5) = scc.status.frame[2].userMode; + data.bit( 8,15) = scc.status.interruptMask; + data.bit(16) = scc.status.cache.isolate; + data.bit(17) = scc.status.cache.swap; + data.bit(18) = scc.status.cache.parityZero; + data.bit(19) = scc.status.cache.loadWasData; + data.bit(20) = scc.status.cache.parityError; + data.bit(21) = scc.status.tlbShutdown; + data.bit(22) = scc.status.vectorLocation; + data.bit(25) = scc.status.reverseEndian; + data.bit(28) = scc.status.enable.coprocessor0; + data.bit(29) = scc.status.enable.coprocessor1; + data.bit(30) = scc.status.enable.coprocessor2; + data.bit(31) = scc.status.enable.coprocessor3; break; case 13: //Cause + data.bit( 2, 6) = scc.cause.exceptionCode; + data.bit( 8,15) = scc.cause.interruptPending; + data.bit(28,29) = scc.cause.coprocessorError; + data.bit(31) = scc.cause.branchDelay; break; - case 14: //EPC + case 14: + data.bit(0,31) = scc.epc; break; - case 15: //ProductID - data.bit(0, 7) = 0x00; //revision (unverified) - data.bit(8,15) = 0x03; //implementation (unverified) + case 15: //Product ID + data.bit(0, 7) = scc.productID.revision; + data.bit(8,15) = scc.productID.implementation; break; } @@ -56,45 +75,64 @@ auto CPU::setControlRegister(u8 index, u32 value) -> void { switch(index & 15) { - case 0: //Index + case 3: //Breakpoint Execute Address + scc.breakpointExecuteAddress = data; break; - case 1: //Random + case 5: //Breakpoint Data Address + scc.breakpointDataAddress = data; break; - case 2: //BusControl -//case 2: //EntryLo + case 7: //Breakpoint Control + //todo break; - case 3: //Config + case 8: //Bad Virtual Address + //scc.badVirtualAddress = data; //read-only break; - case 4: //Context + case 9: //Breakpoint Data Mask + scc.breakpointDataMask = data; break; - case 8: //BadVirtualAddress + case 11: //Breakpoint Execute Mask + scc.breakpointExecuteMask = data; break; - case 9: //Count - break; - - case 10: //PortSize -//case 10: //EntryHi - break; - - case 11: //Compare - break; - - case 12: //StatusRegister + case 12: //Status + scc.status.frame[0].interruptEnable = data.bit( 0); + scc.status.frame[0].userMode = data.bit( 1); + scc.status.frame[1].interruptEnable = data.bit( 2); + scc.status.frame[1].userMode = data.bit( 3); + scc.status.frame[2].interruptEnable = data.bit( 4); + scc.status.frame[2].userMode = data.bit( 5); + scc.status.interruptMask = data.bit( 8,15); + scc.status.cache.isolate = data.bit(16); + scc.status.cache.swap = data.bit(17); + scc.status.cache.parityZero = data.bit(18); + scc.status.cache.loadWasData = data.bit(19); + scc.status.cache.parityError = data.bit(20); + //scc.status.tlbShutdown = data.bit(21); //read-only + scc.status.vectorLocation = data.bit(22); + scc.status.reverseEndian = data.bit(25); + scc.status.enable.coprocessor0 = data.bit(28); + scc.status.enable.coprocessor1 = data.bit(29); + scc.status.enable.coprocessor2 = data.bit(30); + scc.status.enable.coprocessor3 = data.bit(31); break; case 13: //Cause + scc.cause.interruptPending.bit(0) = data.bit(8); + scc.cause.interruptPending.bit(1) = data.bit(9); break; - case 14: //EPC + case 14: //Exception Program Counter + scc.epc = data; break; - case 15: //ProductID (read-only) + case 15: //Product ID + //scc.productID.revision = data.bit(0, 7); //read-only + //scc.productID.implementation = data.bit(8,15); //read-only break; } diff --git a/ares/ps1/cpu/core/scc.hpp b/ares/ps1/cpu/core/scc.hpp index 5d14291b6..3dab5b998 100644 --- a/ares/ps1/cpu/core/scc.hpp +++ b/ares/ps1/cpu/core/scc.hpp @@ -5,34 +5,68 @@ //System Control Coprocessor struct SCC { - // 0: Index + // 3: Breakpoint Execute Address + u32 breakpointExecuteAddress = 0; - // 1: Random + // 5: Breakpoint Data Address + u32 breakpointDataAddress = 0; - // 2: BusControl - // 2: EntryLo + // 7: Breakpoint Control - // 3: Config + // 8: Bad Virtual Address + u32 badVirtualAddress = 0; - // 4: Context + // 9: Breakpoint Data Mask + u32 breakpointDataMask = 0; - // 8: BadVirtualAddress + //11: Breakpoint Execute Mask + u32 breakpointExecuteMask = 0; - // 9: Count - - //10: PortSize - //10: EntryHi - - //11: Compare - - //12: StatusRegister + //12: Status + struct Status { + struct Frame { + uint1 interruptEnable = 0; + uint1 userMode = 0; + } frame[3]; + uint8 interruptMask = 0; + struct Cache { + uint1 isolate = 0; + uint1 swap = 0; + uint1 parityZero = 0; + uint1 loadWasData = 0; + uint1 parityError = 0; + } cache; + uint1 tlbShutdown = 0; + uint1 vectorLocation = 0; + uint1 reverseEndian = 0; + struct Enable { + uint1 coprocessor0 = 1; + uint1 coprocessor1 = 0; + uint1 coprocessor2 = 1; + uint1 coprocessor3 = 0; + } enable; + } status; //13: Cause + struct Cause { + uint5 exceptionCode = 0; + uint8 interruptPending = 0x00; + uint2 coprocessorError = 0; + uint1 branchDelay = 0; + } cause; - //14: EPC + //14: Exception Program Counter + u32 epc = 0; - //15: ProductID + //15: Product ID + struct ProductID { + u8 implementation = 0x00; + u8 revision = 0x02; + } productID; } scc; //scc-instructions.cpp + auto instructionMFC0(u32& rt, u8 rd) -> void; + auto instructionMTC0(cu32& rt, u8 rd) -> void; + auto instructionRFE() -> void; //}; diff --git a/ares/ps1/cpu/cpu.cpp b/ares/ps1/cpu/cpu.cpp index 0bf1a683c..21b52d067 100644 --- a/ares/ps1/cpu/cpu.cpp +++ b/ares/ps1/cpu/cpu.cpp @@ -4,25 +4,38 @@ namespace ares::PlayStation { CPU cpu; #include "core/core.cpp" +#include "dma.cpp" #include "serialization.cpp" auto CPU::load(Node::Object parent) -> void { node = parent->append("CPU"); + ram.allocate(2_MiB); + cache.allocate(1_KiB); } auto CPU::unload() -> void { + ram.reset(); + cache.reset(); node.reset(); } auto CPU::main() -> void { + instruction(); } auto CPU::step(uint clocks) -> void { + gpu.clock -= clocks; + spu.clock -= clocks; + while(gpu.clock < 0) gpu.main(); + while(spu.clock < 0) spu.main(); } auto CPU::power(bool reset) -> void { Thread::reset(); powerCore(reset); + ram.fill(); + cache.fill(); + dma.power(reset); } } diff --git a/ares/ps1/cpu/cpu.hpp b/ares/ps1/cpu/cpu.hpp index 8eb785431..ea389509f 100644 --- a/ares/ps1/cpu/cpu.hpp +++ b/ares/ps1/cpu/cpu.hpp @@ -1,7 +1,9 @@ -//LSI CoreWare CW33300 (MIPS R3000A) +//LSI CoreWare CW33300 (MIPS R3000A core) struct CPU : Thread { Node::Component node; + Memory::Writable ram; + Memory::Writable cache; //cpu.cpp auto load(Node::Object) -> void; @@ -16,6 +18,54 @@ struct CPU : Thread { auto serialize(serializer&) -> void; #include "core/core.hpp" + + struct DMA : Memory::IO { + enum : uint { MDECIN, MDECOUT, GPU, CDROM, SPU, PIO, OTC }; //channels + + CPU& self; + DMA(CPU& self) : self(self) {} + + //dma.cpp + auto readWord(u32 address) -> u32; + auto writeWord(u32 address, u32 data) -> void; + auto transferLinear(uint c) -> void; + auto transferLinked(uint c) -> void; + auto pollIRQ() -> void; + auto power(bool reset) -> void; + + struct IRQ { + uint1 force; + uint1 enable; + uint1 flag; + uint6 unknown; + } irq; + + struct Channel { + //dma.cpp + auto active() const -> bool; + + uint1 masterEnable; + uint3 priority; + uint24 address; + uint16 length; + uint16 blocks; + uint1 direction; + uint1 step; //0 = increment; 1 = decrement + uint2 synchronization; + struct Chopping { + uint1 enable; + uint3 dmaWindow; + uint3 cpuWindow; + } chopping; + uint1 enable; + uint1 trigger; + uint2 unknown; + struct IRQ { + uint1 enable; + uint1 flag; + } irq; + } channel[7]; + } dma{*this}; }; extern CPU cpu; diff --git a/ares/ps1/cpu/dma.cpp b/ares/ps1/cpu/dma.cpp new file mode 100644 index 000000000..2ddef3045 --- /dev/null +++ b/ares/ps1/cpu/dma.cpp @@ -0,0 +1,217 @@ +auto CPU::DMA::readWord(u32 address) -> u32 { + uint32 data = 0; + uint32 c = address >> 4 & 7; + + //DnMADR: DMA Base Address + if((address & 0xffff'ff0f) == 0x1f80'1080 && c < 7) { + data.bit(0,23) = channel[c].address; + } + + //DnBCR: DMA Block Control + if((address & 0xffff'ff0f) == 0x1f80'1084 && c < 7) { + data.bit( 0,15) = channel[c].length; + data.bit(16,31) = channel[c].blocks; + } + + //DnCHCR: DMA Channel Control + if((address & 0xffff'ff0f) == 0x1f80'1088 && c < 7) { + data.bit( 0) = channel[c].direction; + data.bit( 1) = channel[c].step; + data.bit( 2) = channel[c].chopping.enable; + data.bit( 9,10) = channel[c].synchronization; + data.bit(16,18) = channel[c].chopping.dmaWindow; + data.bit(20,22) = channel[c].chopping.cpuWindow; + data.bit(24) = channel[c].enable; + data.bit(28) = channel[c].trigger; + data.bit(29,30) = channel[c].unknown; + } + + //DPCR: DMA Control + if(address == 0x1f80'10f0) { + data.bit( 0, 2) = channel[0].priority; + data.bit( 3) = channel[0].masterEnable; + data.bit( 4, 6) = channel[1].priority; + data.bit( 7) = channel[1].masterEnable; + data.bit( 8,10) = channel[2].priority; + data.bit(11) = channel[2].masterEnable; + data.bit(12,14) = channel[3].priority; + data.bit(15) = channel[3].masterEnable; + data.bit(16,18) = channel[4].priority; + data.bit(19) = channel[4].masterEnable; + data.bit(20,22) = channel[5].priority; + data.bit(23) = channel[5].masterEnable; + data.bit(24,26) = channel[6].priority; + data.bit(27) = channel[6].masterEnable; + } + + //DICR: DMA Interrupt + if(address == 0x1f80'10f4) { + data.bit( 0, 5) = irq.unknown; + data.bit(16) = channel[0].irq.enable; + data.bit(17) = channel[1].irq.enable; + data.bit(18) = channel[2].irq.enable; + data.bit(19) = channel[3].irq.enable; + data.bit(20) = channel[4].irq.enable; + data.bit(21) = channel[5].irq.enable; + data.bit(22) = channel[6].irq.enable; + data.bit(23) = irq.enable; + data.bit(24) = channel[0].irq.flag; + data.bit(25) = channel[1].irq.flag; + data.bit(26) = channel[2].irq.flag; + data.bit(27) = channel[3].irq.flag; + data.bit(28) = channel[4].irq.flag; + data.bit(29) = channel[5].irq.flag; + data.bit(30) = channel[6].irq.flag; + data.bit(31) = irq.flag; + } + + return data; +} + +auto CPU::DMA::writeWord(u32 address, u32 value) -> void { + uint32 data = value; + uint32 c = address >> 4 & 7; + + //DnMADR: DMA Base Address + if((address & 0xffff'ff8f) == 0x1f80'1080 && c < 7) { + channel[c].address = data.bit(0,23); + } + + //DnBCR: DMA Block Control + if((address & 0xffff'ff8f) == 0x1f80'1084 && c < 7) { + channel[c].length = data.bit( 0,15); + channel[c].blocks = data.bit(16,31); + } + + //DnCHCR: DMA Channel Control + if((address & 0xffff'ff8f) == 0x1f80'1088 && c < 7) { + channel[c].direction = data.bit( 0); + channel[c].step = data.bit( 1); + channel[c].chopping.enable = data.bit( 2); + channel[c].synchronization = data.bit( 9,10); + channel[c].chopping.dmaWindow = data.bit(16,18); + channel[c].chopping.cpuWindow = data.bit(20,22); + channel[c].enable = data.bit(24); + channel[c].trigger = data.bit(28); + channel[c].unknown = data.bit(29,30); + + //print(c, " ", channel[c].direction, " ", channel[c].synchronization, " ", hex(channel[c].address, 8L), "\n"); + + if(channel[c].active()) { + if(channel[c].synchronization == 0) transferLinear(c); + if(channel[c].synchronization == 1) transferLinear(c); + if(channel[c].synchronization == 2) transferLinked(c); + } + } + + //DPCR: DMA Control + if(address == 0x1f80'10f0) { + channel[0].priority = data.bit( 0, 2); + channel[0].masterEnable = data.bit( 3); + channel[1].priority = data.bit( 4, 6); + channel[1].masterEnable = data.bit( 7); + channel[2].priority = data.bit( 8,10); + channel[2].masterEnable = data.bit(11); + channel[3].priority = data.bit(12,14); + channel[3].masterEnable = data.bit(15); + channel[4].priority = data.bit(16,18); + channel[4].masterEnable = data.bit(19); + channel[5].priority = data.bit(20,22); + channel[5].masterEnable = data.bit(23); + channel[6].priority = data.bit(24,26); + channel[6].masterEnable = data.bit(27); + } + + //DICR: DMA Interrupt + if(address == 0x1f80'10f4) { + irq.unknown = data.bit( 0,5); + irq.force = data.bit(15); + channel[0].irq.enable = data.bit(16); + channel[1].irq.enable = data.bit(17); + channel[2].irq.enable = data.bit(18); + channel[3].irq.enable = data.bit(19); + channel[4].irq.enable = data.bit(20); + channel[5].irq.enable = data.bit(21); + channel[6].irq.enable = data.bit(22); + irq.enable = data.bit(23); + if(data.bit(24)) channel[0].irq.flag = 0; + if(data.bit(25)) channel[1].irq.flag = 0; + if(data.bit(26)) channel[2].irq.flag = 0; + if(data.bit(27)) channel[3].irq.flag = 0; + if(data.bit(28)) channel[4].irq.flag = 0; + if(data.bit(29)) channel[5].irq.flag = 0; + if(data.bit(30)) channel[6].irq.flag = 0; + pollIRQ(); + } +} + +auto CPU::DMA::transferLinear(uint c) -> void { + u32 address = channel[c].address; + u16 blocks = channel[c].synchronization == 0 ? uint16(1) : channel[c].blocks; + + do { + u16 length = channel[c].length; + do { + if(channel[c].direction == 0) { + u32 data = 0; + if(c == 6) { + data = address - 4 & 0xfffffc; //point to previous entry + if(length == 1) data = 0xffffff; //end of ordering table + } + bus.writeWord(address, data); + } + + if(channel[c].direction == 1) { + u32 data = bus.readWord(address); + address += 4; + } + + if(channel[c].step == 0) address += 4; + if(channel[c].step == 1) address -= 4; + } while(--length); + } while(--blocks); + channel[c].enable = 0; + channel[c].trigger = 0; +} + +auto CPU::DMA::transferLinked(uint c) -> void { + if(channel[c].direction == 0) return; //invalid direction for this mode + + u32 address = channel[c].address; + u32 timeout = 0x40000; //unverified; prevents potential infinite loop + do { + u32 header = bus.readWord(address); + + for(uint count : range(header >> 24)) { + address += 4; + u32 data = bus.readWord(address); + + if(c == 2) { + //print("gpu ", hex(data, 8L), "\n"); + } + } + + address = header & 0xffffff; + if(address & 0x800000) break; + } while(--timeout); +} + +auto CPU::DMA::pollIRQ() -> void { + irq.flag = irq.force; + if(!irq.enable) return; + for(uint n : range(7)) { + if(channel[n].irq.flag & channel[n].irq.enable) irq.flag = 1; + } +} + +auto CPU::DMA::power(bool reset) -> void { + for(uint n : range(7)) { + channel[n].priority = 1 + n; + channel[n].masterEnable = 0; + } +} + +auto CPU::DMA::Channel::active() const -> bool { + if(synchronization == 0) return enable && trigger; + return enable; +} diff --git a/ares/ps1/gpu/gpu.cpp b/ares/ps1/gpu/gpu.cpp index 555aaa033..9f63ad213 100644 --- a/ares/ps1/gpu/gpu.cpp +++ b/ares/ps1/gpu/gpu.cpp @@ -3,25 +3,46 @@ namespace ares::PlayStation { GPU gpu; +#include "io.cpp" #include "serialization.cpp" auto GPU::load(Node::Object parent) -> void { node = parent->append("GPU"); + + screen = node->append("Screen"); + screen->colors(1, [&](uint32 color) -> uint64 { + return -1ll; + }); + screen->setSize(320, 240); } auto GPU::unload() -> void { + screen.reset(); node.reset(); } auto GPU::main() -> void { + if(++io.vcounter == 262) { + io.vcounter = 0; + refreshed = true; + } + + step(33'868'800 / 60 / 262); } auto GPU::step(uint clocks) -> void { Thread::clock += clocks; } +auto GPU::refresh() -> void { + memory::fill(output, 320 * 240); + screen->refresh((uint32*)output, 320 * sizeof(uint32), 320, 240); +} + auto GPU::power(bool reset) -> void { Thread::reset(); + refreshed = false; + io = {}; } } diff --git a/ares/ps1/gpu/gpu.hpp b/ares/ps1/gpu/gpu.hpp index 4180be66c..4a107108b 100644 --- a/ares/ps1/gpu/gpu.hpp +++ b/ares/ps1/gpu/gpu.hpp @@ -1,7 +1,8 @@ //Graphics Processing Unit -struct GPU : Thread { +struct GPU : Thread, Memory::IO { Node::Component node; + Node::Screen screen; //gpu.cpp auto load(Node::Object) -> void; @@ -9,11 +10,23 @@ struct GPU : Thread { auto main() -> void; auto step(uint clocks) -> void; - + auto refresh() -> void; auto power(bool reset) -> void; + //io.cpp + auto readWord(u32 address) -> u32; + auto writeWord(u32 address, u32 data) -> void; + //serialization.cpp auto serialize(serializer&) -> void; + + struct IO { + u16 vcounter = 0; + } io; + +//unserialized: + u32 output[320 * 240]; + bool refreshed; }; extern GPU gpu; diff --git a/ares/ps1/gpu/io.cpp b/ares/ps1/gpu/io.cpp new file mode 100644 index 000000000..0aaa4b1d3 --- /dev/null +++ b/ares/ps1/gpu/io.cpp @@ -0,0 +1,28 @@ +auto GPU::readWord(u32 address) -> u32 { + uint32 data = 0; + + //GPUREAD + if(address == 0x1f80'1810) { + } + + //GPUSTAT + if(address == 0x1f80'1814) { + data.bit(26) = 1; //ready to receive command + data.bit(27) = 1; //ready to send VRAM to CPU + data.bit(28) = 1; //ready to receive DMA block + } + + return data; +} + +auto GPU::writeWord(u32 address, u32 value) -> void { + uint32 data = value; + + //GP0 + if(address == 0x1f80'1810) { + } + + //GP1 + if(address == 0x1f80'1814) { + } +} diff --git a/ares/ps1/memory/bus.hpp b/ares/ps1/memory/bus.hpp index 06deeebd3..83270b7e6 100644 --- a/ares/ps1/memory/bus.hpp +++ b/ares/ps1/memory/bus.hpp @@ -1,20 +1,47 @@ +#define decode(write, access, ...) \ + if(address >= 0xfffe'0000) return unmapped; \ + address &= 0x1fff'ffff; \ + if(address <= 0x001f'ffff) return cpu.ram.access(__VA_ARGS__); \ + if(address <= 0x1eff'ffff) return unmapped; \ + if(address <= 0x1f7f'ffff) return unmapped; \ + if(address <= 0x1f80'03ff) return cpu.cache.access(__VA_ARGS__); \ + if(address <= 0x1f80'107f) return unmapped; \ + if(address <= 0x1f80'10ff) return cpu.dma.access(__VA_ARGS__); \ + if(address <= 0x1f80'17ff) return unmapped; \ + if(address <= 0x1f80'180f) return unmapped; \ + if(address <= 0x1f80'181f) return gpu.access(__VA_ARGS__); \ + if(address <= 0x1fbf'ffff) return unmapped; \ + if(address <= 0x1fff'ffff) return bios.access(__VA_ARGS__); \ + return unmapped; \ + +#define unmapped 0 + inline auto Bus::readByte(u32 address) -> u8 { - return 0; + decode(0, readByte, address); } inline auto Bus::readHalf(u32 address) -> u16 { - return 0; + decode(0, readHalf, address); } inline auto Bus::readWord(u32 address) -> u32 { - return 0; + decode(0, readWord, address); } +#undef unmapped +#define unmapped + inline auto Bus::writeByte(u32 address, u8 data) -> void { + decode(1, writeByte, address, data); } inline auto Bus::writeHalf(u32 address, u16 data) -> void { + decode(1, writeHalf, address, data); } inline auto Bus::writeWord(u32 address, u32 data) -> void { + decode(1, writeWord, address, data); } + +#undef unmapped +#undef decode diff --git a/ares/ps1/memory/memory.cpp b/ares/ps1/memory/memory.cpp index 94d2a5e43..b24447665 100644 --- a/ares/ps1/memory/memory.cpp +++ b/ares/ps1/memory/memory.cpp @@ -3,5 +3,6 @@ namespace ares::PlayStation { Bus bus; +Memory::Readable bios; } diff --git a/ares/ps1/memory/memory.hpp b/ares/ps1/memory/memory.hpp index 0924c9ac5..9cf1f3a2b 100644 --- a/ares/ps1/memory/memory.hpp +++ b/ares/ps1/memory/memory.hpp @@ -68,13 +68,13 @@ struct Writable { auto fill(u32 value = 0) -> void { for(u32 address = 0; address < size; address += 4) { - data[address & maskWord] = value; + *(u32*)&data[address & maskWord] = value; } } auto load(Shared::File fp) -> void { for(u32 address = 0; address < min(size, fp->size()); address += 4) { - data[address & maskWord] = fp->readl(4); + *(u32*)&data[address & maskWord] = fp->readl(4); } } @@ -94,6 +94,43 @@ struct Writable { u32 maskWord = 0; }; +template +struct IO { + auto readByte(u32 address) -> u8 { + auto data = ((T*)this)->readWord(address); + switch(address & 3) { + case 0: return data >> 0; + case 1: return data >> 8; + case 2: return data >> 16; + case 3: return data >> 24; + } unreachable; + } + + auto readHalf(u32 address) -> u16 { + auto data = ((T*)this)->readWord(address); + switch(address & 2) { + case 0: return data >> 0; + case 2: return data >> 16; + } unreachable; + } + + auto writeByte(u32 address, u8 data) -> void { + switch(address & 3) { + case 0: return ((T*)this)->writeWord(address, data << 0); + case 1: return ((T*)this)->writeWord(address, data << 8); + case 2: return ((T*)this)->writeWord(address, data << 16); + case 3: return ((T*)this)->writeWord(address, data << 24); + } + } + + auto writeHalf(u32 address, u16 data) -> void { + switch(address & 2) { + case 0: return ((T*)this)->writeWord(address, data << 0); + case 2: return ((T*)this)->writeWord(address, data << 16); + } + } +}; + } struct Bus { @@ -107,3 +144,4 @@ struct Bus { }; extern Bus bus; +extern Memory::Readable bios; diff --git a/ares/ps1/ps1.hpp b/ares/ps1/ps1.hpp index c02c2691b..3a12be3d6 100644 --- a/ares/ps1/ps1.hpp +++ b/ares/ps1/ps1.hpp @@ -23,6 +23,7 @@ namespace ares::PlayStation { #include #include #include + #include } #include diff --git a/ares/ps1/spu/spu.cpp b/ares/ps1/spu/spu.cpp index e4217f5d4..d144224f7 100644 --- a/ares/ps1/spu/spu.cpp +++ b/ares/ps1/spu/spu.cpp @@ -7,13 +7,24 @@ SPU spu; auto SPU::load(Node::Object parent) -> void { node = parent->append("SPU"); + + stream = node->append("SPU"); + stream->setChannels(2); + stream->setFrequency(44100.0); } auto SPU::unload() -> void { + stream.reset(); node.reset(); } auto SPU::main() -> void { + sample(); + step(33'868'800 / 44'100); +} + +auto SPU::sample() -> void { + stream->sample(0.0, 0.0); } auto SPU::step(uint clocks) -> void { diff --git a/ares/ps1/spu/spu.hpp b/ares/ps1/spu/spu.hpp index cb1a12116..b30bf70fb 100644 --- a/ares/ps1/spu/spu.hpp +++ b/ares/ps1/spu/spu.hpp @@ -2,12 +2,14 @@ struct SPU : Thread { Node::Component node; + Node::Stream stream; //spu.cpp auto load(Node::Object) -> void; auto unload() -> void; auto main() -> void; + auto sample() -> void; auto step(uint clocks) -> void; auto power(bool reset) -> void; diff --git a/ares/ps1/system/system.cpp b/ares/ps1/system/system.cpp index 1db564c5c..ce06db15c 100644 --- a/ares/ps1/system/system.cpp +++ b/ares/ps1/system/system.cpp @@ -6,6 +6,9 @@ System system; #include "serialization.cpp" auto System::run() -> void { + while(!gpu.refreshed) cpu.main(); + gpu.refreshed = false; + gpu.refresh(); } auto System::load(Node::Object& root) -> void { @@ -37,6 +40,11 @@ auto System::save() -> void { auto System::power(bool reset) -> void { for(auto& setting : node->find()) setting->setLatch(); + bios.allocate(512_KiB); + if(auto fp = platform->open(node, "bios.rom", File::Read, File::Required)) { + bios.load(fp); + } + cpu.power(reset); gpu.power(reset); spu.power(reset); diff --git a/hiro/cocoa/widget/hex-edit.cpp b/hiro/cocoa/widget/hex-edit.cpp index 08f67b4b6..767dd7ce2 100644 --- a/hiro/cocoa/widget/hex-edit.cpp +++ b/hiro/cocoa/widget/hex-edit.cpp @@ -15,7 +15,7 @@ namespace hiro { auto pHexEdit::construct() -> void { @autoreleasepool { - cocoaView = cocoaHexEdit = [[CocoaHexEdit alloc] initWith:hexEdit]; + cocoaView = cocoaHexEdit = [[CocoaHexEdit alloc] initWith:self()]; } } diff --git a/hiro/cocoa/widget/widget.cpp b/hiro/cocoa/widget/widget.cpp index 81b531053..0c0ddbdea 100644 --- a/hiro/cocoa/widget/widget.cpp +++ b/hiro/cocoa/widget/widget.cpp @@ -70,13 +70,12 @@ auto pWidget::setFont(const Font& font) -> void { auto pWidget::setGeometry(Geometry geometry) -> void { @autoreleasepool { + CGFloat windowHeight = [[cocoaView superview] frame].size.height; //round coordinates uint x = geometry.x(); uint y = windowHeight - geometry.y() - geometry.height(); uint width = geometry.width(); uint height = geometry.height(); - - CGFloat windowHeight = [[cocoaView superview] frame].size.height; [cocoaView setFrame:NSMakeRect(x, y, width, height)]; [[cocoaView superview] setNeedsDisplay:YES]; } diff --git a/lucia/emulator/playstation.cpp b/lucia/emulator/playstation.cpp index 95ffe5e73..91bf5e629 100644 --- a/lucia/emulator/playstation.cpp +++ b/lucia/emulator/playstation.cpp @@ -5,6 +5,8 @@ struct PlayStation : Emulator { auto load() -> bool override; auto open(ares::Node::Object, string name, vfs::file::mode mode, bool required) -> shared_pointer override; auto input(ares::Node::Input) -> void override; + + uint regionID = 0; }; PlayStation::PlayStation() { @@ -19,10 +21,19 @@ PlayStation::PlayStation() { } auto PlayStation::load() -> bool { + if(!file::exists(firmware[regionID].location)) { + errorFirmwareRequired(firmware[regionID]); + return false; + } + return true; } auto PlayStation::open(ares::Node::Object node, string name, vfs::file::mode mode, bool required) -> shared_pointer { + if(name == "bios.rom") { + return Emulator::loadFirmware(firmware[regionID]); + } + return {}; }