Update to ares v114r07 release.

- PlayStation: started implementing the CPU SCC (missing some
    registers still)
  - PlayStation: implemented basic DMA support
このコミットが含まれているのは:
Near 2020-06-22 21:49:00 +00:00
コミット d9ea05c4c4
32個のファイルの変更1251行の追加197行の削除

ファイルの表示

@ -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 = &target;
delay.fetch.source = source;
}
auto write(u32& target, u32 source) -> void {
delay.write.target = &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 ノーマルファイル
ファイルの表示

@ -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 ノーマルファイル
ファイルの表示

@ -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 ノーマルファイル
ファイルの表示

@ -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 ノーマルファイル
ファイルの表示

@ -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 {};
}