Update to ares v114r07 release.
- PlayStation: started implementing the CPU SCC (missing some registers still) - PlayStation: implemented basic DMA support
このコミットが含まれているのは:
コミット
d9ea05c4c4
@ -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/";
|
||||
|
@ -89,9 +89,9 @@ auto CPU::instructionDebug() -> void {
|
||||
pipeline.instruction = readWord(pipeline.address)(0);
|
||||
|
||||
static vector<bool> 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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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<bool> 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 = {};
|
||||
}
|
||||
|
@ -7,23 +7,20 @@
|
||||
auto powerCore(bool reset) -> void;
|
||||
|
||||
//memory.cpp
|
||||
auto readAddress (u32 address) -> maybe<u32>;
|
||||
auto writeAddress(u32 address) -> maybe<u32>;
|
||||
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<u16>;
|
||||
auto readWord(u32 address) -> maybe<u32>;
|
||||
|
||||
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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
313
ares/ps1/cpu/core/disassembler.cpp
ノーマルファイル
313
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<string> {
|
||||
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<string> {
|
||||
return {name, rtName(), rsValue(), immediate(u16(instruction))};
|
||||
};
|
||||
|
||||
auto ADDI = [&](string_view add, string_view sub, string_view mov) -> vector<string> {
|
||||
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<string> {
|
||||
return {name, rsValue(), branch()};
|
||||
};
|
||||
|
||||
auto BRANCH2 = [&](string_view name) -> vector<string> {
|
||||
return {name, rsValue(), rtValue(), branch()};
|
||||
};
|
||||
|
||||
auto JUMP = [&](string_view name) -> vector<string> {
|
||||
return {name, jump()};
|
||||
};
|
||||
|
||||
auto LOAD = [&](string_view name) -> vector<string> {
|
||||
return {name, rtName(), offset()};
|
||||
};
|
||||
|
||||
auto STORE = [&](string_view name) -> vector<string> {
|
||||
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<string> {
|
||||
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<string> {
|
||||
return {name, rdName(), rtValue(), by};
|
||||
};
|
||||
|
||||
auto JALR = [&](string_view name) -> vector<string> {
|
||||
if((instruction >> 11 & 31) == 31) return {name, rsValue()};
|
||||
return {name, rdName(), rsValue()};
|
||||
};
|
||||
|
||||
auto REG = [&](string_view name) -> vector<string> {
|
||||
return {name, rdName(), rsValue(), rtValue()};
|
||||
};
|
||||
|
||||
auto ST = [&](string_view name) -> vector<string> {
|
||||
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<string> {
|
||||
auto rsValue = [&] { return cpuRegisterValue(instruction >> 21 & 31); };
|
||||
auto branch = [&] { return immediate(address + 4 + (i16(instruction) << 2)); };
|
||||
|
||||
auto BRANCH = [&](string_view name) -> vector<string> {
|
||||
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<string> {
|
||||
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<string> {
|
||||
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<typename... P>
|
||||
auto CPU::Disassembler::hint(P&&... p) const -> string {
|
||||
if(showColors) return {"\e[0m\e[37m", forward<P>(p)..., "\e[0m"};
|
||||
return {forward<P>(p)...};
|
||||
}
|
29
ares/ps1/cpu/core/disassembler.hpp
ノーマルファイル
29
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<typename... P> auto hint(P&&... p) const -> string;
|
||||
|
||||
bool showColors = true;
|
||||
bool showValues = true;
|
||||
|
||||
//private:
|
||||
auto EXECUTE() -> vector<string>;
|
||||
auto SPECIAL() -> vector<string>;
|
||||
auto REGIMM() -> vector<string>;
|
||||
auto SCC() -> vector<string>;
|
||||
auto GTE() -> vector<string>;
|
||||
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};
|
||||
//};
|
@ -1,37 +1,29 @@
|
||||
auto CPU::readAddress(u32 address) -> maybe<u32> {
|
||||
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<u32> {
|
||||
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<u8> {
|
||||
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<u16> {
|
||||
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<u32> {
|
||||
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);
|
||||
}
|
||||
|
@ -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] = {};
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
//};
|
||||
|
@ -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<Node::Component>("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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<DMA> {
|
||||
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;
|
||||
|
217
ares/ps1/cpu/dma.cpp
ノーマルファイル
217
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;
|
||||
}
|
@ -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<Node::Component>("GPU");
|
||||
|
||||
screen = node->append<Node::Screen>("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<u32>(output, 320 * 240);
|
||||
screen->refresh((uint32*)output, 320 * sizeof(uint32), 320, 240);
|
||||
}
|
||||
|
||||
auto GPU::power(bool reset) -> void {
|
||||
Thread::reset();
|
||||
refreshed = false;
|
||||
io = {};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
//Graphics Processing Unit
|
||||
|
||||
struct GPU : Thread {
|
||||
struct GPU : Thread, Memory::IO<GPU> {
|
||||
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;
|
||||
|
28
ares/ps1/gpu/io.cpp
ノーマルファイル
28
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) {
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -3,5 +3,6 @@
|
||||
namespace ares::PlayStation {
|
||||
|
||||
Bus bus;
|
||||
Memory::Readable bios;
|
||||
|
||||
}
|
||||
|
@ -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<typename T>
|
||||
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;
|
||||
|
@ -23,6 +23,7 @@ namespace ares::PlayStation {
|
||||
#include <ps1/cpu/cpu.hpp>
|
||||
#include <ps1/gpu/gpu.hpp>
|
||||
#include <ps1/spu/spu.hpp>
|
||||
#include <ps1/memory/bus.hpp>
|
||||
}
|
||||
|
||||
#include <ps1/interface/interface.hpp>
|
||||
|
@ -7,13 +7,24 @@ SPU spu;
|
||||
|
||||
auto SPU::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Component>("SPU");
|
||||
|
||||
stream = node->append<Node::Stream>("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 {
|
||||
|
@ -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;
|
||||
|
@ -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<Node::Setting>()) 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);
|
||||
|
@ -15,7 +15,7 @@ namespace hiro {
|
||||
|
||||
auto pHexEdit::construct() -> void {
|
||||
@autoreleasepool {
|
||||
cocoaView = cocoaHexEdit = [[CocoaHexEdit alloc] initWith:hexEdit];
|
||||
cocoaView = cocoaHexEdit = [[CocoaHexEdit alloc] initWith:self()];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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<vfs::file> 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<vfs::file> {
|
||||
if(name == "bios.rom") {
|
||||
return Emulator::loadFirmware(firmware[regionID]);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
読み込み中…
新しいイシューから参照
ユーザーをブロックする