Added IOP

This commit is contained in:
Your Name 2023-01-20 05:49:27 -05:00
parent ea6c7c1a1f
commit e7e728bd1e
23 changed files with 4944 additions and 58 deletions

2544
3party/robin_hood.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -14,9 +14,14 @@ set(SOURCES src/main.cpp
src/emu/cpu/ee/vif.cpp
src/emu/cpu/ee/x64/reg_alloc.cpp
src/emu/cpu/ee/x64/emit.cpp
src/emu/cpu/iop/cpu.cpp
src/emu/cpu/iop/opcodes.cpp
src/emu/cpu/iop/dma.cpp
src/emu/sched/scheduler.cpp
src/emu/gpu/gif.cpp
src/emu/gpu/gs.cpp)
src/emu/gpu/gs.cpp
src/emu/dev/sif.cpp
src/emu/dev/cdvd.cpp)
set(CMAKE_BUILD_TYPE Debug)

View file

@ -25,8 +25,8 @@ bool Application::Init(int argc, char** argv)
printf("[app/App]: %s: Initializing System\n", __FUNCTION__);
System::Reset();
System::LoadBios(argv[1]);
System::Reset();
std::atexit(Application::Exit);
// signal(SIGSEGV, Sig);

View file

@ -6,6 +6,7 @@
#include <emu/cpu/ee/EmotionEngine.h>
#include <emu/sched/scheduler.h>
#include <emu/cpu/ee/vu.h>
#include <emu/cpu/iop/cpu.h>
Scheduler::Event vsync_event;
@ -69,6 +70,8 @@ void System::Reset()
Scheduler::InitScheduler();
EmotionEngine::Reset();
IOP_MANAGEMENT::Reset();
vsync_event.func = HandleVsync;
vsync_event.name = "VSYNC handler";
vsync_event.cycles_from_now = 4920115;
@ -87,4 +90,5 @@ void System::Dump()
EmotionEngine::Dump();
Bus::Dump();
VectorUnit::Dump();
IOP_MANAGEMENT::Dump();
}

View file

@ -10,6 +10,8 @@ namespace EmotionEngine
ProcessorState state;
}
EE_JIT::Block* top_level_cache[0xFF];
namespace EE_JIT
{
@ -255,7 +257,7 @@ void JIT::EmitLUI(uint32_t instr, EE_JIT::IRInstruction &i)
IRValue dest = IRValue(IRValue::Reg);
IRValue imm = IRValue(IRValue::Imm);
dest.SetReg(rt);
imm.SetImm32(((int32_t)(int16_t)(instr & 0xffff)) << 16);
imm.SetImm32(((int32_t)(((int16_t)(instr & 0xffff))) << 16));
i = IRInstruction::Build({dest, imm}, IRInstrs::MOV);
@ -370,6 +372,7 @@ void JIT::EmitLDL(uint32_t instr, EE_JIT::IRInstruction &i)
offset.SetImm(_offset);
i = IRInstruction::Build({rt, offset, base}, IRInstrs::LDL);
i.opcode = instr;
cur_block->ir.push_back(i);
}
@ -391,6 +394,7 @@ void JIT::EmitLDR(uint32_t instr, EE_JIT::IRInstruction &i)
offset.SetImm(_offset);
i = IRInstruction::Build({rt, offset, base}, IRInstrs::LDR);
i.opcode = instr;
cur_block->ir.push_back(i);
}
@ -492,8 +496,6 @@ void JIT::EmitLW(uint32_t instr, EE_JIT::IRInstruction &i)
int _base = (instr >> 21) & 0x1F;
int32_t _offset = (int16_t)(instr & 0xffff);
// printf("lw %s, %d(%s)\n", EmotionEngine::Reg(_rt), _offset, EmotionEngine::Reg(_base));
IRValue base = IRValue(IRValue::Reg);
IRValue rt = IRValue(IRValue::Reg);
IRValue offset = IRValue(IRValue::Imm);
@ -662,6 +664,7 @@ void JIT::EmitSDL(uint32_t instr, EE_JIT::IRInstruction &i)
offset.SetImm(_offset);
i = IRInstruction::Build({rt, offset, base}, IRInstrs::SDL);
i.opcode = instr;
cur_block->ir.push_back(i);
}
@ -683,6 +686,7 @@ void JIT::EmitSDR(uint32_t instr, EE_JIT::IRInstruction &i)
offset.SetImm(_offset);
i = IRInstruction::Build({rt, offset, base}, IRInstrs::SDR);
i.opcode = instr;
cur_block->ir.push_back(i);
}
@ -846,6 +850,29 @@ void JIT::EmitSLLV(uint32_t instr, EE_JIT::IRInstruction &i)
cur_block->ir.push_back(i);
}
void JIT::EmitSRLV(uint32_t instr, EE_JIT::IRInstruction &i)
{
int rd = (instr >> 11) & 0x1F;
int rt = (instr >> 16) & 0x1F;
int rs = (instr >> 21) & 0x1F;
// printf("srav %s, %s, %s\n", EmotionEngine::Reg(rd), EmotionEngine::Reg(rt), EmotionEngine::Reg(rs));
IRValue dest = IRValue(IRValue::Reg);
IRValue src = IRValue(IRValue::Reg);
IRValue shamt = IRValue(IRValue::Reg);
dest.SetReg(rd);
src.SetReg(rt);
shamt.SetReg(rs);
i = IRInstruction::Build({dest, src, shamt}, IRInstrs::Shift);
i.direction = IRInstruction::Direction::Right;
i.is_logical = true;
cur_block->ir.push_back(i);
}
void JIT::EmitSRAV(uint32_t instr, EE_JIT::IRInstruction &i)
{
int rd = (instr >> 11) & 0x1F;
@ -1278,6 +1305,29 @@ void JIT::EmitDSLL(uint32_t instr, EE_JIT::IRInstruction &i)
cur_block->ir.push_back(i);
}
void JIT::EmitDSRL(uint32_t instr, EE_JIT::IRInstruction &i)
{
int rd = (instr >> 11) & 0x1F;
int rt = (instr >> 16) & 0x1F;
int sa = (instr >> 6) & 0x1F;
// printf("dsrl %s, %s, %d\n", EmotionEngine::Reg(rd), EmotionEngine::Reg(rt), sa);
IRValue dest = IRValue(IRValue::Reg);
IRValue src = IRValue(IRValue::Reg);
IRValue shift = IRValue(IRValue::Imm);
dest.SetReg(rd);
src.SetReg(rt);
shift.SetImm32Unsigned(sa);
i = IRInstruction::Build({dest, src, shift}, IRInstrs::Shift64);
i.is_logical = true;
i.direction = IRInstruction::Right;
cur_block->ir.push_back(i);
}
void JIT::EmitDSLL32(uint32_t instr, EE_JIT::IRInstruction &i)
{
int rd = (instr >> 11) & 0x1F;
@ -1301,6 +1351,29 @@ void JIT::EmitDSLL32(uint32_t instr, EE_JIT::IRInstruction &i)
cur_block->ir.push_back(i);
}
void JIT::EmitDSRL32(uint32_t instr, EE_JIT::IRInstruction &i)
{
int rd = (instr >> 11) & 0x1F;
int rt = (instr >> 16) & 0x1F;
int sa = (instr >> 6) & 0x1F;
// printf("dsrl32 %s, %s, %d\n", EmotionEngine::Reg(rd), EmotionEngine::Reg(rt), sa);
IRValue dest = IRValue(IRValue::Reg);
IRValue src = IRValue(IRValue::Reg);
IRValue shift = IRValue(IRValue::Imm);
dest.SetReg(rd);
src.SetReg(rt);
shift.SetImm32Unsigned(sa+32);
i = IRInstruction::Build({dest, src, shift}, IRInstrs::Shift64);
i.is_logical = true;
i.direction = IRInstruction::Right;
cur_block->ir.push_back(i);
}
void JIT::EmitDSRA32(uint32_t instr, EE_JIT::IRInstruction &i)
{
int rd = (instr >> 11) & 0x1F;
@ -1519,6 +1592,9 @@ void JIT::EmitIR(uint32_t instr)
case 0x04:
EmitSLLV(instr, current_instruction);
break;
case 0x06:
EmitSRLV(instr, current_instruction);
break;
case 0x07:
EmitSRAV(instr, current_instruction);
break;
@ -1588,14 +1664,20 @@ void JIT::EmitIR(uint32_t instr)
case 0x38:
EmitDSLL(instr, current_instruction);
break;
case 0x3A:
EmitDSRL(instr, current_instruction);
break;
case 0x3C:
EmitDSLL32(instr, current_instruction);
break;
case 0x3E:
EmitDSRL32(instr, current_instruction);
break;
case 0x3F:
EmitDSRA32(instr, current_instruction);
break;
default:
// printf("[emu/CPU]: Cannot emit unknown special instruction 0x%02x\n", opcode);
printf("[emu/CPU]: Cannot emit unknown special instruction 0x%02x\n", opcode);
cur_block->ir.clear();
delete cur_block;
exit(1);
@ -1614,7 +1696,7 @@ void JIT::EmitIR(uint32_t instr)
EmitBGEZ(instr, current_instruction);
break;
default:
// printf("Unknown regimm opcode 0x%02x\n", opcode);
printf("Unknown regimm opcode 0x%02x\n", opcode);
exit(1);
}
break;
@ -1739,6 +1821,19 @@ void JIT::EmitIR(uint32_t instr)
case 0x1B:
EmitDIVU1(instr, current_instruction);
break;
case 0x28:
{
opcode = (instr >> 5) & 0x1F;
switch (opcode)
{
default:
printf("[emu/CPU]: Cannot emit unknown mmi1 instruction 0x%02x\n", opcode);
delete cur_block;
exit(1);
}
break;
}
case 0x29:
{
opcode = (instr >> 5) & 0x1F;
@ -1839,6 +1934,9 @@ JIT::EntryFunc JIT::EmitDone(size_t cycles_taken)
cur_block->ir.push_back(i);
blockCache[cur_block->guest_addr] = cur_block;
top_level_cache[cur_block->guest_addr >> 24] = cur_block;
EE_JIT::emit->TranslateBlock(cur_block);
cur_block->cycles = cycles_taken;
@ -1848,10 +1946,9 @@ JIT::EntryFunc JIT::EmitDone(size_t cycles_taken)
Block* JIT::GetExistingBlock(uint32_t start)
{
if (!blockCache[start])
if (top_level_cache[start >> 24] && top_level_cache[start >> 24]->guest_addr == start)
{
// printf("ERROR: Block doesn't exist at 0x%08x\n", start);
exit(1);
return top_level_cache[start >> 24];
}
return blockCache[start];
@ -1859,12 +1956,17 @@ Block* JIT::GetExistingBlock(uint32_t start)
bool JIT::DoesBlockExist(uint32_t addr)
{
if (top_level_cache[addr >> 24] && top_level_cache[addr >> 24]->guest_addr == addr)
{
return true;
}
return blockCache.find(addr) != blockCache.end();
}
void JIT::CheckCacheFull()
{
if (blockCache.size() >= (4096*1024))
if (blockCache.size() >= 75)
{
emit->ResetFreeBase();
// for (auto b : blockCache)

View file

@ -7,6 +7,7 @@
#include <vector>
#include <unordered_map>
#include <util/uint128.h>
#include <3party/robin_hood.h>
namespace EE_JIT
{
@ -150,6 +151,8 @@ struct IRInstruction
return i;
}
uint32_t opcode;
};
struct Block
@ -164,7 +167,7 @@ class JIT
{
private:
Block* cur_block;
std::unordered_map<uint32_t, Block*> blockCache;
robin_hood::unordered_flat_map<uint32_t, Block*> blockCache;
void EmitJ(uint32_t instr, EE_JIT::IRInstruction& i); // 0x02
void EmitJAL(uint32_t instr, EE_JIT::IRInstruction& i); // 0x03
@ -206,6 +209,7 @@ private:
void EmitSRL(uint32_t instr, EE_JIT::IRInstruction& i); // 0x02
void EmitSRA(uint32_t instr, EE_JIT::IRInstruction& i); // 0x03
void EmitSLLV(uint32_t instr, EE_JIT::IRInstruction& i); // 0x04
void EmitSRLV(uint32_t instr, EE_JIT::IRInstruction& i); // 0x06
void EmitSRAV(uint32_t instr, EE_JIT::IRInstruction& i); // 0x07
void EmitJR(uint32_t instr, EE_JIT::IRInstruction& i); // 0x08
void EmitJALR(uint32_t instr, EE_JIT::IRInstruction& i); // 0x09
@ -228,7 +232,9 @@ private:
void EmitSLTU(uint32_t instr, EE_JIT::IRInstruction& i); // 0x2B
void EmitDADDU(uint32_t instr, EE_JIT::IRInstruction& i); // 0x2D
void EmitDSLL(uint32_t instr, EE_JIT::IRInstruction& i); // 0x38
void EmitDSRL(uint32_t instr, EE_JIT::IRInstruction& i); // 0x3A
void EmitDSLL32(uint32_t instr, EE_JIT::IRInstruction& i); // 0x3C
void EmitDSRL32(uint32_t instr, EE_JIT::IRInstruction& i); // 0x3E
void EmitDSRA32(uint32_t instr, EE_JIT::IRInstruction& i); // 0x3F
void EmitBLTZ(uint32_t instr, EE_JIT::IRInstruction& i); // 0x00

View file

@ -5,6 +5,10 @@
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <emu/memory/Bus.h>
#include <emu/sched/scheduler.h>
#include <emu/dev/sif.h>
namespace DMAC
{
@ -42,9 +46,42 @@ struct Channel
DN_CHCR chcr;
uint32_t madr;
uint32_t tadr;
uint32_t qwc;
uint32_t asr[2];
} channels[10];
uint32_t ctrl;
union DMATag
{
__uint128_t value;
struct
{
__uint128_t qwc : 16;
__uint128_t : 10;
__uint128_t prio_control : 2;
__uint128_t tag_id : 3;
__uint128_t irq : 1;
__uint128_t addr : 31;
__uint128_t mem_sel : 1;
__uint128_t data_transfer : 64;
};
};
const char* REG_NAMES[] =
{
"CHCR",
"MADR",
"TADR",
"ASR0",
"ASR1",
"",
"",
"",
"SADR",
};
void WriteVIF0Channel(uint32_t addr, uint32_t data)
{
switch (addr & 0xff)
@ -176,18 +213,65 @@ void WriteIPUTOChannel(uint32_t addr, uint32_t data)
break;
}
}
void HandleSIF0Transfer()
{
auto& c = channels[5];
assert(c.chcr.mode == 1 && "Unhandled SIF0 mode!");
if (channels[5].qwc > 0)
{
}
else
{
DMATag tag;
if (SIF::FIFO0_size() >= 2)
{
printf("Oh crap, I gotta do stuff now\n");
exit(1);
}
else
{
// Try again in two cycles to see if SIF's fifo0 has filled
Scheduler::Event sif0_evt;
sif0_evt.cycles_from_now = 2;
sif0_evt.func = HandleSIF0Transfer;
sif0_evt.name = "SIF0 DMA transfer";
Scheduler::ScheduleEvent(sif0_evt);
}
}
}
void WriteSIF0Channel(uint32_t addr, uint32_t data)
{
printf("[emu/SIF1]: Writing 0x%08x to %s of SIF1 channel\n", data, REG_NAMES[(addr >> 4) & 0xf]);
switch (addr & 0xff)
{
case 0x00:
channels[5].chcr.data = data;
if (channels[5].chcr.start)
if (channels[5].chcr.start && (ctrl & 1))
{
printf("[emu/DMAC]: Starting SIF0 transfer\n");
// We schedule the transfer 1 cycle from now
// This is because the DMAC ticks at half the speed of the EE
Scheduler::Event sif0_evt;
sif0_evt.cycles_from_now = 1;
sif0_evt.func = HandleSIF0Transfer;
sif0_evt.name = "SIF0 DMA transfer";
Scheduler::ScheduleEvent(sif0_evt);
}
break;
case 0x10:
channels[5].madr = data;
break;
case 0x20:
channels[5].qwc = data & 0xffff;
break;
case 0x30:
channels[5].tadr = data;
break;
@ -202,6 +286,66 @@ void WriteSIF0Channel(uint32_t addr, uint32_t data)
break;
}
}
void WriteSIF1Channel(uint32_t addr, uint32_t data)
{
printf("[emu/SIF1]: Writing 0x%08x to %s of SIF1 channel\n", data, REG_NAMES[(addr >> 4) & 0xf]);
switch (addr & 0xff)
{
case 0x00:
channels[6].chcr.data = data;
if (channels[5].chcr.start)
printf("[emu/DMAC]: Starting SIF1 transfer\n");
break;
case 0x10:
channels[6].madr = data;
break;
case 0x20:
channels[6].qwc = data;
break;
case 0x30:
channels[6].tadr = data;
break;
case 0x40:
channels[6].asr[0] = data;
break;
case 0x50:
channels[6].asr[1] = data;
break;
case 0x80:
channels[6].sadr.data = data;
break;
}
}
void WriteSIF2Channel(uint32_t addr, uint32_t data)
{
switch (addr & 0xff)
{
case 0x00:
channels[7].chcr.data = data;
if (channels[7].chcr.start)
printf("[emu/DMAC]: Starting SIF2 transfer\n");
break;
case 0x10:
channels[7].madr = data;
break;
case 0x30:
channels[7].tadr = data;
break;
case 0x40:
channels[7].asr[0] = data;
break;
case 0x50:
channels[7].asr[1] = data;
break;
case 0x80:
channels[7].sadr.data = data;
break;
}
}
void WriteSPRFROMChannel(uint32_t addr, uint32_t data)
{
switch (addr & 0xff)
@ -255,6 +399,15 @@ void WriteSPRTOChannel(uint32_t addr, uint32_t data)
}
}
uint32_t ReadSIF0Channel(uint32_t addr)
{
switch (addr & 0xff)
{
case 0x00:
return channels[5].chcr.data;
}
}
union DSTAT
{
uint32_t value;
@ -281,7 +434,6 @@ union DSTAT
};
} stat;
uint32_t ctrl;
uint32_t dpcr;
uint32_t sqwc;
@ -301,6 +453,11 @@ void WriteDCTRL(uint32_t data)
ctrl = data;
}
uint32_t ReadDCTRL()
{
return ctrl;
}
void WriteDPCR(uint32_t data)
{
dpcr = data;

View file

@ -19,10 +19,13 @@ void WriteSIF2Channel(uint32_t addr, uint32_t data);
void WriteSPRFROMChannel(uint32_t addr, uint32_t data);
void WriteSPRTOChannel(uint32_t addr, uint32_t data);
uint32_t ReadSIF0Channel(uint32_t addr);
void WriteDSTAT(uint32_t data);
uint32_t ReadDSTAT();
void WriteDCTRL(uint32_t data);
uint32_t ReadDCTRL();
void WriteDPCR(uint32_t data);
uint32_t ReadDPCR();

View file

@ -422,6 +422,20 @@ void EE_JIT::Emitter::EmitJA(IRInstruction i)
}
}
void PrintAboutStrnchr()
{
if (EmotionEngine::GetState()->next_pc == 0x8000d638)
{
printf("strnchr(0x%08x, %d, %d)\n", EmotionEngine::GetState()->regs[4].u32[0], EmotionEngine::GetState()->regs[5].u32[0], EmotionEngine::GetState()->regs[6].u32[0]);
exit(1);
}
if (EmotionEngine::GetState()->next_pc == 0x8000d550)
{
printf("memcpy(0x%08x, 0x%08x, %d)\n", EmotionEngine::GetState()->regs[4].u32[0], EmotionEngine::GetState()->regs[5].u32[0], EmotionEngine::GetState()->regs[6].u32[0]);
exit(1);
}
}
void EE_JIT::Emitter::EmitJumpImm(IRInstruction i)
{
if (i.should_link)
@ -441,6 +455,9 @@ void EE_JIT::Emitter::EmitJumpImm(IRInstruction i)
cg->and_(cur_pc_value, 0xf0000000);
cg->or_(cur_pc_value, i.args[0].GetImm());
cg->mov(cg->dword[cg->rbp + offsetof(EmotionEngine::ProcessorState, next_pc)], cur_pc_value);
// cg->mov(cg->rax, reinterpret_cast<uint64_t>(PrintAboutStrnchr));
// cg->call(cg->rax);
}
void EE_JIT::Emitter::EmitAdd(IRInstruction i)
@ -497,10 +514,9 @@ void EE_JIT::Emitter::EmitAdd(IRInstruction i)
Xbyak::Reg32 ee_src1_value = Xbyak::Reg32(reg_alloc->AllocHostRegister());
Xbyak::Reg32 ee_src2_value = Xbyak::Reg32(reg_alloc->AllocHostRegister());
Xbyak::Reg64 ee_src2_ex_value = Xbyak::Reg64(reg_alloc->AllocHostRegister());
auto src1_offset = ((offsetof(EmotionEngine::ProcessorState, regs) + (i.args[2].GetReg() * sizeof(uint128_t)) + offsetof(uint128_t, u64)));
auto src2_offset = ((offsetof(EmotionEngine::ProcessorState, regs) + (i.args[1].GetReg() * sizeof(uint128_t)) + offsetof(uint128_t, u64)));
auto src1_offset = ((offsetof(EmotionEngine::ProcessorState, regs) + (i.args[1].GetReg() * sizeof(uint128_t)) + offsetof(uint128_t, u32)));
auto src2_offset = ((offsetof(EmotionEngine::ProcessorState, regs) + (i.args[2].GetReg() * sizeof(uint128_t)) + offsetof(uint128_t, u32)));
auto dest_offset = ((offsetof(EmotionEngine::ProcessorState, regs) + (i.args[0].GetReg() * sizeof(uint128_t)) + offsetof(uint128_t, u64)));
cg->mov(ee_src1_value, cg->dword[cg->rbp + src1_offset]);
@ -702,50 +718,110 @@ void EE_JIT::Emitter::EmitMemoryLoad(IRInstruction i)
cg->mov(cg->dword[cg->rbp + val_offset + sizeof(uint64_t)], cg->rdx);
}
static const uint64_t LDL_MASK[8] =
void _LDL(uint32_t instruction)
{
0x00ffffffffffffffULL, 0x0000ffffffffffffULL, 0x000000ffffffffffULL, 0x00000000ffffffffULL,
0x0000000000ffffffULL, 0x000000000000ffffULL, 0x00000000000000ffULL, 0x0000000000000000ULL
};
static const uint8_t LDL_SHIFT[8] = { 56, 48, 40, 32, 24, 16, 8, 0 };
static const uint64_t LDL_MASK[8] =
{ 0x00ffffffffffffffULL, 0x0000ffffffffffffULL, 0x000000ffffffffffULL, 0x00000000ffffffffULL,
0x0000000000ffffffULL, 0x000000000000ffffULL, 0x00000000000000ffULL, 0x0000000000000000ULL
};
static const uint8_t LDL_SHIFT[8] = { 56, 48, 40, 32, 24, 16, 8, 0 };
int16_t imm = (int16_t)(instruction & 0xFFFF);
uint64_t dest = (instruction >> 16) & 0x1F;
uint64_t base = (instruction >> 21) & 0x1F;
uint32_t addr = EmotionEngine::GetState()->regs[base].u32[0] + imm;
uint32_t shift = addr & 0x7;
uint64_t mem = Bus::Read64(addr & ~0x7);
uint64_t reg = EmotionEngine::GetState()->regs[dest].u64[0];
EmotionEngine::GetState()->regs[dest].u64[0] = (reg & LDL_MASK[shift]) | (mem << LDL_SHIFT[shift]);
}
void EE_JIT::Emitter::EmitLDL(IRInstruction i)
{
reg_alloc->MarkRegUsed(RegisterAllocator::RDI);
reg_alloc->MarkRegUsed(RegisterAllocator::RAX);
reg_alloc->MarkRegUsed(RegisterAllocator::RDX);
reg_alloc->MarkRegUsed(RegisterAllocator::RCX);
cg->mov(cg->edi, i.opcode);
cg->mov(cg->rax, reinterpret_cast<uint64_t>(_LDL));
cg->call(cg->rax);
}
Xbyak::Reg64 ee_base_value = Xbyak::Reg64(reg_alloc->AllocHostRegister());
Xbyak::Reg64 reg_val = Xbyak::Reg64(reg_alloc->AllocHostRegister());
auto base_offset = ((offsetof(EmotionEngine::ProcessorState, regs) + (i.args[2].GetReg() * sizeof(uint128_t)) + offsetof(uint128_t, u32)));
auto val_offset = ((offsetof(EmotionEngine::ProcessorState, regs) + (i.args[0].GetReg() * sizeof(uint128_t)) + offsetof(uint128_t, u64)));
cg->mov(ee_base_value, cg->qword[cg->rbp + base_offset]);
cg->add(ee_base_value, i.args[1].GetImm());
cg->mov(cg->ecx, ee_base_value);
cg->and_(cg->ecx, ~0x7);
cg->mov(cg->rdi, cg->ecx);
cg->mov(reg_val, reinterpret_cast<uint64_t>(Bus::Read64));
cg->call(reg_val);
void _LDR(uint32_t instruction)
{
static const uint64_t LDR_MASK[8] =
{ 0x0000000000000000ULL, 0xff00000000000000ULL, 0xffff000000000000ULL, 0xffffff0000000000ULL,
0xffffffff00000000ULL, 0xffffffffff000000ULL, 0xffffffffffff0000ULL, 0xffffffffffffff00ULL
};
static const uint8_t LDR_SHIFT[8] = { 0, 8, 16, 24, 32, 40, 48, 56 };
int16_t imm = (int16_t)(instruction & 0xFFFF);
uint64_t dest = (instruction >> 16) & 0x1F;
uint64_t base = (instruction >> 21) & 0x1F;
cg->and_(ee_base_value, 0x7);
cg->shl(ee_base_value, 0x3);
cg->mov(cg->ecx, 56);
cg->sub(ee_base_value, cg->ecx);
cg->shl(cg->rax, cg->cl);
uint32_t addr = EmotionEngine::GetState()->regs[base].u32[0] + imm;
uint32_t shift = addr & 0x7;
cg->sub(cg->cx, 0x40);
cg->neg(cg->cx);
cg->xor_(ee_base_value, ee_base_value);
cg->cmp(cg->cx, 0x40);
cg->cmove(reg_val, ee_base_value);
cg->shl(reg_val, cg->cl);
cg->shr(reg_val, cg->cl);
cg->or_(reg_val, cg->rax);
uint64_t mem = Bus::Read64(addr & ~0x7);
uint64_t reg = EmotionEngine::GetState()->regs[dest].u64[0];
EmotionEngine::GetState()->regs[dest].u64[0] = (reg & LDR_MASK[shift]) | (mem >> LDR_SHIFT[shift]);
}
cg->mov(cg->qword[cg->rbp + val_offset], reg_val);
void EE_JIT::Emitter::EmitLDR(IRInstruction i)
{
cg->mov(cg->edi, i.opcode);
cg->mov(cg->rax, reinterpret_cast<uint64_t>(_LDR));
cg->call(cg->rax);
}
void _SDL(uint32_t cur_instr)
{
static const uint64_t SDL_MASK[8] =
{ 0xffffffffffffff00ULL, 0xffffffffffff0000ULL, 0xffffffffff000000ULL, 0xffffffff00000000ULL,
0xffffff0000000000ULL, 0xffff000000000000ULL, 0xff00000000000000ULL, 0x0000000000000000ULL
};
static const uint8_t SDL_SHIFT[8] = { 56, 48, 40, 32, 24, 16, 8, 0 };
int16_t imm = (int16_t)(cur_instr & 0xFFFF);
uint64_t source = (cur_instr >> 16) & 0x1F;
uint64_t base = (cur_instr >> 21) & 0x1F;
uint32_t addr = EmotionEngine::GetState()->regs[base].u32[0] + imm;
uint32_t shift = addr & 0x7;
uint64_t mem = Bus::Read64(addr & ~0x7);
mem = (EmotionEngine::GetState()->regs[source].u64[0] >> SDL_SHIFT[shift]) | (mem & SDL_MASK[shift]);
Bus::Write64(addr & ~0x7, mem);
}
void EE_JIT::Emitter::EmitSDL(IRInstruction i)
{
cg->mov(cg->edi, i.opcode);
cg->mov(cg->rax, reinterpret_cast<uint64_t>(_SDL));
cg->call(cg->rax);
}
void _SDR(uint32_t instruction)
{
static const uint64_t SDR_MASK[8] =
{ 0x0000000000000000ULL, 0x00000000000000ffULL, 0x000000000000ffffULL, 0x0000000000ffffffULL,
0x00000000ffffffffULL, 0x000000ffffffffffULL, 0x0000ffffffffffffULL, 0x00ffffffffffffffULL
};
static const uint8_t SDR_SHIFT[8] = { 0, 8, 16, 24, 32, 40, 48, 56 };
int16_t imm = (int16_t)(instruction & 0xFFFF);
uint64_t source = (instruction >> 16) & 0x1F;
uint64_t base = (instruction >> 21) & 0x1F;
uint32_t addr = EmotionEngine::GetState()->regs[base].u32[0] + imm;
uint32_t shift = addr & 0x7;
uint64_t mem = Bus::Read64(addr & ~0x7);
mem = (EmotionEngine::GetState()->regs[source].u64[0] << SDR_SHIFT[shift]) |
(mem & SDR_MASK[shift]);
Bus::Write64(addr & ~0x7, mem);
}
void EE_JIT::Emitter::EmitSDR(IRInstruction i)
{
cg->mov(cg->edi, i.opcode);
cg->mov(cg->rax, reinterpret_cast<uint64_t>(_SDR));
cg->call(cg->rax);
}
void EE_JIT::Emitter::EmitShift(IRInstruction i)
@ -1212,6 +1288,15 @@ void EE_JIT::Emitter::EmitIR(IRInstruction i)
case LDL:
EmitLDL(i);
break;
case LDR:
EmitLDR(i);
break;
case SDL:
EmitSDL(i);
break;
case SDR:
EmitSDR(i);
break;
default:
printf("[JIT/Emit]: Unknown IR instruction %d\n", i.instr);
exit(1);
@ -1257,11 +1342,12 @@ EE_JIT::Emitter::Emitter()
cg->mov(cg->dword[cg->rsp + 4], 0);
cg->mov(cg->dword[cg->rsp + 8], 0);
cg->mov(cg->dword[cg->rsp + 16], 0);
cg->mov(cg->dword[cg->rsp + 20], 0);
cg->mov(cg->dword[cg->rsp + 24], 0);
Xbyak::Label begin;
cg->L(begin);
cg->mov(cg->dword[cg->rsp + 20], 0);
cg->mov(func_ptr, reinterpret_cast<uint64_t>(EmotionEngine::CheckCacheFull));
cg->call(func_ptr);
@ -1334,7 +1420,8 @@ EE_JIT::Emitter::Emitter()
cg->call(func_ptr);
cg->call(cg->rax);
cg->mov(cg->rdi, cg->dword[cg->rsp + 20]);
cg->mov(cg->edi, cg->dword[cg->rsp + 20]);
cg->mov(func_ptr, reinterpret_cast<uint64_t>(Scheduler::CheckScheduler));
cg->call(func_ptr);

View file

@ -42,6 +42,9 @@ private:
void EmitMemoryStore(IRInstruction i);
void EmitMemoryLoad(IRInstruction i);
void EmitLDL(IRInstruction i);
void EmitLDR(IRInstruction i);
void EmitSDL(IRInstruction i);
void EmitSDR(IRInstruction i);
void EmitShift(IRInstruction i);
void EmitShift64(IRInstruction i);
void EmitMULT(IRInstruction i);

242
src/emu/cpu/iop/cpu.cpp Normal file
View file

@ -0,0 +1,242 @@
#include <emu/memory/Bus.h>
#include <app/Application.h>
#include <cstring>
#include "cpu.h"
uint32_t exception_addr[2] = { 0x80000080, 0xBFC00180 };
void CPU::exception(Exception cause, uint32_t cop)
{
// printf("[IOP] Exception of type %d\n", (int)cause);
uint32_t mode = Cop0.status.value;
Cop0.status.value &= ~(uint32_t)0x3F;
Cop0.status.value |= (mode << 2) & 0x3F;
Cop0.cause.excode = (uint32_t)cause;
Cop0.cause.CE = cop;
bool is_delay_slot = i.is_delay_slot;
bool branch_taken = i.branch_taken;
if (cause == Exception::Interrupt)
{
Cop0.epc = next_instr.pc;
is_delay_slot = next_instr.is_delay_slot;
branch_taken = next_instr.branch_taken;
}
else
{
Cop0.epc = i.pc;
}
if (is_delay_slot)
{
Cop0.epc -= 4;
Cop0.cause.BD = true;
Cop0.TAR = next_instr.pc;
if (branch_taken)
{
Cop0.cause.BT = true;
}
}
pc = exception_addr[Cop0.status.BEV];
direct_jump();
}
void CPU::Reset()
{
memset(regs, 0, sizeof(regs));
Cop0 = {};
Cop0.regs[15] = 0x1f;
pc = 0xbfc00000;
direct_jump();
console.open("iop_console.txt");
}
CPU::~CPU()
{
console.flush();
console.close();
}
void CPU::direct_jump()
{
next_instr = {};
next_instr.full = Bus::iop_read<uint32_t>(pc);
next_instr.pc = pc;
pc += 4;
}
void CPU::handle_load_delay()
{
if (delayed_memory_load.reg != memory_load.reg)
{
regs[memory_load.reg] = memory_load.data;
}
memory_load = delayed_memory_load;
delayed_memory_load.reg = 0;
regs[write_back.reg] = write_back.data;
write_back.reg = 0;
regs[0] = 0;
}
void CPU::branch(std::string op)
{
int32_t imm = (int16_t)i.i_type.imm;
next_instr.branch_taken = true;
pc = next_instr.pc + (imm << 2);
if (can_disassemble) printf("%s, 0x%08x\n", op.c_str(), pc);
}
void CPU::branch()
{
int32_t imm = (int16_t)i.i_type.imm;
next_instr.branch_taken = true;
pc = next_instr.pc + (imm << 2);
}
void CPU::load(uint32_t regN, uint32_t value)
{
delayed_memory_load.reg = regN;
delayed_memory_load.data = value;
}
void CPU::Clock(int cycles)
{
for (int cycle = cycles; cycle > 0; cycle--)
{
if (singleStep)
{
getc(stdin);
}
i = next_instr;
if (i.pc == 0x12C48 || i.pc == 0x1420C || i.pc == 0x1230C)
{
uint32_t ptr = regs[5];
uint32_t text_size = regs[6];
while (text_size)
{
auto c = (char)Bus::iop_read<uint8_t>(ptr & 0x1FFFFF);
console << c;
console.flush();
ptr++;
text_size--;
}
}
if (pc & 0x3)
{
printf("Error: Unaligned address at 0x%08x\n", pc);
exit(1);
}
if (can_disassemble)
printf("0x%08x: ", i.pc);
direct_jump();
switch (i.opcode)
{
case 0b000000: op_special(); break;
case 0b000001: op_bcond(); break;
case 0b001111: op_lui(); break;
case 0b001101: op_ori(); break;
case 0b001110: op_xori(); break;
case 0b101011: op_sw(); break;
case 0b001001: op_addiu(); break;
case 0b001000: op_addi(); break;
case 0b000010: op_j(); break;
case 0b010000: op_cop0(); break;
case 0b100011: op_lw(); break;
case 0b000101: op_bne(); break;
case 0b101001: op_sh(); break;
case 0b000011: op_jal(); break;
case 0b001100: op_andi(); break;
case 0b101000: op_sb(); break;
case 0b100000: op_lb(); break;
case 0b000100: op_beq(); break;
case 0b000111: op_bgtz(); break;
case 0b000110: op_blez(); break;
case 0b100100: op_lbu(); break;
case 0b001010: op_slti(); break;
case 0b001011: op_sltiu(); break;
case 0b100101: op_lhu(); break;
case 0b100001: op_lh(); break;
case 0b100010: op_lwl(); break;
case 0b100110: op_lwr(); break;
case 0b101010: op_swl(); break;
case 0b101110: op_swr(); break;
case 0x12: op_cop2(); break;
default:
printf("[emu/IOP]: Unknown instruction 0x%02x\n", i.opcode);
exit(1);
}
/* Apply pending load delays. */
handle_load_delay();
}
if (i.opcode != 0b100010)
{
last_instruction_was_lwl = false;
}
if (i.opcode != 0b100110)
{
last_instruction_was_lwr = false;
}
if (IntPending())
{
exception(Exception::Interrupt);
}
}
void CPU::Dump()
{
for (int i = 0; i < 32; i++)
printf("[emu/IOP]: %s: %s\t->\t0x%08x\n", __FUNCTION__, Reg(i), regs[i]);
printf("[emu/IOP]: %s: pc\t->\t0x%08x\n", __FUNCTION__, pc - 4);
}
bool CPU::IntPending()
{
bool pending = false;
Cop0.cause.IP = (Cop0.cause.IP & ~0x4) | (pending << 2);
bool enabled = Cop0.status.IEc && (Cop0.status.Im & Cop0.cause.IP);
return pending && enabled;
}
static CPU iop;
void IOP_MANAGEMENT::Clock(int cycles)
{
iop.Clock(cycles);
}
void IOP_MANAGEMENT::Reset()
{
iop.Reset();
}
void IOP_MANAGEMENT::Dump()
{
iop.Dump();
}

281
src/emu/cpu/iop/cpu.h Normal file
View file

@ -0,0 +1,281 @@
#pragma once
#include <cstdint>
#include <emu/cpu/iop/opcode.h>
#include <string>
#include <fstream>
class CPU
{
private:
uint32_t pc;
uint32_t regs[32];
uint32_t hi, lo;
bool last_instruction_was_lwl = false;
bool last_instruction_was_lwr = false;
union COP0STAT
{
uint32_t value;
struct
{
uint32_t IEc : 1; /* Interrupt Enable (current) */
uint32_t KUc : 1; /* Kernel-User Mode (current) */
uint32_t IEp : 1; /* Interrupt Enable (previous) */
uint32_t KUp : 1; /* Kernel-User Mode (previous) */
uint32_t IEo : 1; /* Interrupt Enable (old) */
uint32_t KUo : 1; /* Kernel-User Mode (old) */
uint32_t : 2;
uint32_t Im : 8; /* Hardware Interrupt Mask */
uint32_t IsC : 1; /* Isolate Cache */
uint32_t : 1;
uint32_t PZ : 1; /* Parity Zero */
uint32_t CM : 1;
uint32_t PE : 1; /* Parity Error */
uint32_t TS : 1; /* TLB Shutdown */
uint32_t BEV : 1; /* Bootstrap Exception Vectors */
uint32_t : 5;
uint32_t Cu : 4; /* Coprocessor Usability */
};
};
union COP0CAUSE
{
uint32_t value;
struct
{
uint32_t : 2;
uint32_t excode : 5; /* Exception Code */
uint32_t : 1;
uint32_t IP : 8; /* Interrupt Pending */
uint32_t : 12;
uint32_t CE : 2; /* Coprocessor Error */
uint32_t BT : 1; /* Branch Taken */
uint32_t BD : 1; /* Branch Delay */
};
};
union COP0
{
uint32_t regs[32] = {};
struct
{
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t Bpc; /* Breakpoint Program Counter */
uint32_t r4;
uint32_t BDA; /* Breakpoint Data Address */
uint32_t TAR; /* Target Address */
uint32_t DCIC; /* Debug and Cache Invalidate Control */
uint32_t BadA; /* Bad Address */
uint32_t BDAM; /* Breakpoint Data Address Mask */
uint32_t r10;
uint32_t BpcM; /* Breakpoint Program Counter Mask */
COP0STAT status; /* Status */
COP0CAUSE cause; /* Cause */
uint32_t epc; /* Exception Program Counter */
uint32_t PRId; /* Processor Revision Identifier */
};
} Cop0;
const char* Reg(int index)
{
switch (index)
{
case 0:
return "$zero";
case 1:
return "$at";
case 2:
return "$v0";
case 3:
return "$v1";
case 4:
return "$a0";
case 5:
return "$a1";
case 6:
return "$a2";
case 7:
return "$a3";
case 8:
return "$t0";
case 9:
return "$t1";
case 10:
return "$t2";
case 11:
return "$t3";
case 12:
return "$t4";
case 13:
return "$t5";
case 14:
return "$t6";
case 15:
return "$t7";
case 16:
return "$s0";
case 17:
return "$s1";
case 18:
return "$s2";
case 19:
return "$s3";
case 20:
return "$s4";
case 21:
return "$s5";
case 22:
return "$s6";
case 23:
return "$s7";
case 24:
return "$t8";
case 25:
return "$t9";
case 26:
return "$k0";
case 27:
return "$k1";
case 28:
return "$gp";
case 29:
return "$sp";
case 30:
return "$fp";
case 31:
return "$ra";
default:
return "";
}
}
struct LoadDelay
{
int reg = 0;
uint32_t data = 0;
} write_back, memory_load, delayed_memory_load;
bool singleStep = false;
void set_reg(uint32_t regN, uint32_t value)
{
write_back.reg = regN;
write_back.data = value;
}
void op_special(); // 0x00
void op_bcond(); // 0x01
void op_j(); // 0x02
void op_jal(); // 0x03
void op_beq(); // 0x04
void op_bne(); // 0x05
void op_blez(); // 0x06
void op_bgtz(); // 0x07
void op_addi(); // 0x08
void op_addiu(); // 0x09
void op_slti(); // 0x0A
void op_sltiu(); // 0x0B
void op_andi(); // 0x0C
void op_ori(); // 0x0D
void op_xori(); // 0x0E
void op_lui(); // 0x0F
void op_cop0(); // 0x10
void op_cop2(); // 0x12
void op_lb(); // 0x20
void op_lh(); // 0x21
void op_lwl(); // 0x22
void op_lw(); // 0x23
void op_lbu(); // 0x24
void op_lhu(); // 0x25
void op_lwr(); // 0x26
void op_sb(); // 0x28
void op_sh(); // 0x29
void op_swl(); // 0x2A
void op_sw(); // 0x2B
void op_swr(); // 0x2E
// special
void op_sll(); // 0x00
void op_srl(); // 0x02
void op_sra(); // 0x03
void op_sllv(); // 0x04
void op_srlv(); // 0x06
void op_srav(); // 0x07
void op_jr(); // 0x08
void op_jalr(); // 0x09
void op_syscall(); // 0x0C
void op_mfhi(); // 0x10
void op_mthi(); // 0x11
void op_mflo(); // 0x12
void op_mtlo(); // 0x13
void op_mult(); // 0x18
void op_multu(); // 0x19
void op_div(); // 0x1A
void op_divu(); // 0x1B
void op_add(); // 0x20
void op_addu(); // 0x21
void op_sub(); // 0x22
void op_subu(); // 0x23
void op_and(); // 0x24
void op_or(); // 0x25
void op_xor(); // 0x26
void op_nor(); // 0x27
void op_slt(); // 0x2A
void op_sltu(); // 0x2B
// cop0
void op_mfc0(); // 0x00
void op_mtc0(); // 0x04
void op_rfe(); // 0x10
void direct_jump();
void handle_load_delay();
void branch(std::string op);
void branch();
void load(uint32_t regN, uint32_t value);
bool isCacheIsolated() {return Cop0.status.IsC;}
bool can_disassemble = false;
std::ofstream console;
enum class Exception
{
Interrupt = 0x0,
ReadError = 0x4,
WriteError = 0x5,
BusError = 0x6,
Syscall = 0x8,
Break = 0x9,
IllegalInstr = 0xA,
CoprocessorError = 0xB,
Overflow = 0xC
};
void exception(Exception cause, uint32_t cop = 0);
Opcode i, next_instr;
public:
void Reset();
~CPU();
void Clock(int cycles);
void Dump();
bool IntPending();
};
namespace IOP_MANAGEMENT
{
void Clock(int cycles);
void Reset();
void Dump();
}

161
src/emu/cpu/iop/dma.cpp Normal file
View file

@ -0,0 +1,161 @@
#include "dma.h"
#include <cstdio>
uint32_t dpcr;
uint32_t dpcr2;
bool dmacen = true;
uint32_t dicr = 0;
uint32_t dicr2 = 0;
union DN_CHCR
{
uint32_t data;
struct
{
uint32_t direction : 1;
uint32_t madr_inc : 1;
uint32_t : 6;
uint32_t mode_specific : 1;
uint32_t mode : 2;
uint32_t : 5;
uint32_t chopping_dma_size : 3;
uint32_t : 1;
uint32_t chopping_cpu_size : 3;
uint32_t : 1;
uint32_t start : 1;
uint32_t : 3;
uint32_t force_transfer : 1;
uint32_t forced_burst_pause : 1;
uint32_t bus_snooping : 1;
uint32_t iLink_thingy : 1;
};
};
struct DMAChannels
{
uint32_t madr;
uint32_t bcr;
DN_CHCR chcr;
uint32_t tadr;
} channels[13];
void IopDma::WriteDPCR(uint32_t data)
{
dpcr = data;
printf("[emu/IopDma]: Writing 0x%08x to DPCR\n", data);
}
void IopDma::WriteDPCR2(uint32_t data)
{
dpcr2 = data;
printf("[emu/IopDma]: Writing 0x%08x to DPCR2\n", data);
}
void IopDma::WriteDMACEN(uint32_t data)
{
printf("[emu/IopDma]: Writing 0x%08x to DMACEN\n", data);
dmacen = data & 1;
}
void IopDma::WriteDICR(uint32_t data)
{
dicr = data;
}
void IopDma::WriteDICR2(uint32_t data)
{
dicr2 = data;
}
const char* REGS[] =
{
"MADR",
"BCR",
"CHCR",
"TADR"
};
void IopDma::WriteChannel(uint32_t addr, uint32_t data)
{
int channel = (addr >> 4) & 0xf;
int reg = addr & 0xf;
reg /= 4;
channel -= 0x8;
printf("Writing 0x%08x to %s of channel %d\n", data, REGS[reg], channel);
switch (addr)
{
case 0x0:
channels[channel].madr = data;
return;
case 0x1:
channels[channel].bcr = data;
return;
case 0x2:
channels[channel].chcr.data = data;
return;
case 0x3:
channels[channel].tadr = data;
return;
}
if (channels[channel].chcr.start && dmacen)
printf("[emu/IopDma]: Starting transfer on channel %d\n", channel);
}
void IopDma::WriteNewChannel(uint32_t addr, uint32_t data)
{
int channel = (addr >> 4) & 0xf;
int reg = addr & 0xf;
reg /= 4;
channel += 8;
printf("Writing 0x%08x to %s of channel %d\n", data, REGS[reg], channel);
switch (addr)
{
case 0x0:
channels[channel].madr = data;
return;
case 0x1:
channels[channel].bcr = data;
return;
case 0x2:
channels[channel].chcr.data = data;
return;
case 0x3:
channels[channel].tadr = data;
return;
}
if (channels[channel].chcr.start && dmacen)
printf("[emu/IopDma]: Starting transfer on channel %d\n", channel);
}
uint32_t IopDma::ReadDMACEN()
{
return dmacen;
}
uint32_t IopDma::ReadDICR2()
{
return dicr2;
}
uint32_t IopDma::ReadDICR()
{
return dicr;
}
uint32_t IopDma::ReadDPCR2()
{
return dpcr2;
}
uint32_t IopDma::ReadDPCR()
{
return dpcr;
}

23
src/emu/cpu/iop/dma.h Normal file
View file

@ -0,0 +1,23 @@
#pragma once
#include <cstdint>
namespace IopDma
{
void WriteDPCR(uint32_t data);
void WriteDPCR2(uint32_t data);
void WriteDMACEN(uint32_t data);
void WriteDICR(uint32_t data);
void WriteDICR2(uint32_t data);
void WriteChannel(uint32_t addr, uint32_t data);
void WriteNewChannel(uint32_t addr, uint32_t data);
uint32_t ReadDMACEN();
uint32_t ReadDICR2();
uint32_t ReadDICR();
uint32_t ReadDPCR2();
uint32_t ReadDPCR();
}

41
src/emu/cpu/iop/opcode.h Normal file
View file

@ -0,0 +1,41 @@
#pragma once
#include <bits/stdint-uintn.h>
struct Opcode
{
union
{
uint32_t full;
struct
{ /* Used when polling for the opcode */
uint32_t : 26;
uint32_t opcode : 6;
};
struct
{
uint32_t imm : 16;
uint32_t rt : 5;
uint32_t rs : 5;
uint32_t opcode : 6;
} i_type;
struct
{
uint32_t target : 26;
uint32_t opcode : 6;
} j_type;
struct
{
uint32_t func : 6;
uint32_t sa : 5;
uint32_t rd : 5;
uint32_t rt : 5;
uint32_t rs : 5;
uint32_t opcode : 6;
} r_type;
};
uint32_t pc;
bool is_delay_slot = false;
bool branch_taken = false;
};

867
src/emu/cpu/iop/opcodes.cpp Normal file
View file

@ -0,0 +1,867 @@
#include <emu/cpu/iop/cpu.h>
#include <cstdio>
#include <app/Application.h>
#include <emu/memory/Bus.h>
#include "cpu.h"
void CPU::op_special()
{
switch (i.r_type.func)
{
case 0b000000: op_sll(); break;
case 0b100101: op_or(); break;
case 0b101011: op_sltu(); break;
case 0b100001: op_addu(); break;
case 0b001000: op_jr(); break;
case 0b100100: op_and(); break;
case 0b100000: op_add(); break;
case 0b001001: op_jalr(); break;
case 0b100011: op_subu(); break;
case 0b000011: op_sra(); break;
case 0b011010: op_div(); break;
case 0b010010: op_mflo(); break;
case 0b000010: op_srl(); break;
case 0b011011: op_divu(); break;
case 0b010000: op_mfhi(); break;
case 0b101010: op_slt(); break;
case 0b001100: op_syscall(); break;
case 0b010011: op_mtlo(); break;
case 0b010001: op_mthi(); break;
case 0b000100: op_sllv(); break;
case 0b100111: op_nor(); break;
case 0b000110: op_srlv(); break;
case 0b011001: op_multu(); break;
case 0b100110: op_xor(); break;
case 0b011000: op_mult(); break;
case 0b000111: op_srav(); break;
case 0b100010: op_sub(); break;
default:
printf("[emu/IOP]: Unknown special opcode 0x%02x\n", i.r_type.func);
exit(1);
}
}
void CPU::op_bcond()
{
int rt = i.i_type.rt;
int rs = i.i_type.rs;
next_instr.is_delay_slot = true;
bool should_link = (rt & 0x1E) == 0x10;
bool should_branch = (int)(regs[rs] ^ (rt << 31)) < 0;
if (should_link) regs[31] = i.pc + 8;
if (should_branch) branch();
if (can_disassemble) printf("b%sz %s, 0x%08x\n", rt & 1 ? "lt" : "gt", Reg(rs), pc + ((int16_t)i.i_type.imm << 2));
}
void CPU::op_j()
{
pc = (pc & 0xF0000000) | (i.j_type.target << 2);
next_instr.is_delay_slot = true;
next_instr.branch_taken = true;
if ((pc & 0xFFFF) == 0x1EC8 || (pc & 0xFFFF) == 0x1F64)
{
uint32_t struct_ptr = regs[4];
uint16_t version = Bus::iop_read<uint16_t>(struct_ptr + 8);
char name[9];
name[8] = 0;
for (int i = 0; i < 8; i++)
name[i] = Bus::iop_read<uint8_t>(struct_ptr + 12 + i);
printf("[emu/IOP]: RegisterLibraryEntries: %s version %d.0%d\n", name, version >> 8, version & 0xff);
}
if (can_disassemble && i.opcode == 0b000010) printf("j 0x%08x\n", pc);
}
void CPU::op_jal()
{
set_reg(31, pc);
op_j();
if (can_disassemble) printf("jal 0x%08x\n", pc);
}
void CPU::op_beq()
{
int rt = i.i_type.rt;
int rs = i.i_type.rs;
next_instr.is_delay_slot = true;
std::string op = "beq " + std::string(Reg(rs)) + ", " + std::string(Reg(rt));
if (regs[rs] == regs[rt])
{
branch(op);
}
else if (can_disassemble)
{
int32_t imm = (int16_t)i.i_type.imm;
uint32_t dest = next_instr.pc + (imm << 2);
printf("%s, 0x%08x\n", op.c_str(), dest);
}
}
void CPU::op_bne()
{
int rt = i.i_type.rt;
int rs = i.i_type.rs;
next_instr.is_delay_slot = true;
std::string op = "bne " + std::string(Reg(rs)) + ", " + std::string(Reg(rt));
if (regs[rs] != regs[rt])
{
branch(op);
}
else if (can_disassemble)
{
int32_t imm = (int16_t)i.i_type.imm;
uint32_t dest = next_instr.pc + (imm << 2);
printf("%s, 0x%08x\n", op.c_str(), dest);
}
}
void CPU::op_blez()
{
int rs = i.r_type.rs;
int32_t reg = (int32_t)regs[rs];
if (can_disassemble) printf("blez %s, 0x%08x\n", Reg(rs), next_instr.pc + ((int16_t)i.i_type.imm << 2));
std::string op = "blez " + std::string(Reg(rs));
if (reg <= 0)
{
branch(op);
}
}
void CPU::op_bgtz()
{
int rs = i.r_type.rs;
int32_t reg = (int32_t)regs[rs];
std::string op = "bgez " + std::string(Reg(rs));
if (reg > 0)
{
branch(op);
}
}
void CPU::op_addi()
{
int rt = i.i_type.rt;
int rs = i.i_type.rs;
int16_t imm = (int16_t)i.i_type.imm;
int32_t reg = (int32_t)regs[rs];
set_reg(rt, reg + imm);
if (can_disassemble) printf("addi %s, %s, 0x%04x\n", Reg(rt), Reg(rs), imm);
}
void CPU::op_addiu()
{
int rt = i.i_type.rt;
int rs = i.i_type.rs;
int16_t imm = (int16_t)i.i_type.imm;
set_reg(rt, regs[rs] + imm);
if (can_disassemble) printf("addiu %s, %s, 0x%04x\n", Reg(rt), Reg(rs), imm);
}
void CPU::op_slti()
{
int rt = i.i_type.rt;
int rs = i.i_type.rs;
int32_t reg = (int32_t)regs[rs];
int16_t imm = (int16_t)i.i_type.imm;
bool condition = reg < imm;
set_reg(rt, condition);
if (can_disassemble) printf("slti %s, %s, 0x%04x\n", Reg(rt), Reg(rs), imm);
}
void CPU::op_sltiu()
{
int rt = i.i_type.rt;
int rs = i.i_type.rs;
uint32_t imm = (int32_t)(int16_t)i.i_type.imm;
bool condition = regs[rs] < imm;
set_reg(rt, condition);
if (can_disassemble) printf("sltiu %s, %s, 0x%04x\n", Reg(rt), Reg(rs), imm);
}
void CPU::op_andi()
{
int rt = i.i_type.rt;
int rs = i.i_type.rs;
uint16_t imm = i.i_type.imm;
if (can_disassemble) printf("andi %s, %s, 0x%04x\n", Reg(rt), Reg(rs), imm);
set_reg(rt, regs[rs] & imm);
}
void CPU::op_ori()
{
int rt = i.i_type.rt;
int rs = i.i_type.rs;
uint16_t imm = i.i_type.imm;
if (can_disassemble) printf("ori %s, %s, 0x%04x\n", Reg(rt), Reg(rs), imm);
set_reg(rt, regs[rs] | imm);
}
void CPU::op_xori()
{
int rt = i.i_type.rt;
int rs = i.i_type.rs;
uint16_t imm = i.i_type.imm;
if (can_disassemble) printf("xori %s, %s, 0x%04x\n", Reg(rt), Reg(rs), imm);
set_reg(rt, regs[rs] ^ imm);
}
void CPU::op_lui()
{
int rt = i.i_type.rt;
uint32_t imm = i.i_type.imm;
if (can_disassemble) printf("lui %s, 0x%08x\n", Reg(rt), imm);
set_reg(rt, imm << 16);
}
void CPU::op_cop0()
{
switch (i.r_type.rs)
{
case 0:
op_mfc0();
break;
case 4:
op_mtc0();
break;
case 0x10:
op_rfe();
break;
default:
printf("[emu/CPU]: %s: Unknown cop0 opcode 0x%08x (0x%02x)\n", __FUNCTION__, i.full, i.r_type.rs);
Application::Exit(1);
}
}
void CPU::op_cop2()
{
switch (i.r_type.rs)
{
default:
printf("[emu/CPU]: %s: Unknown cop2 opcode 0x%08x (0x%02x)\n", __FUNCTION__, i.full, i.r_type.rs);
Application::Exit(1);
}
}
void CPU::op_lb()
{
int rt = i.i_type.rt;
int base = i.i_type.rs;
int16_t off = (int16_t)i.i_type.imm;
uint32_t vaddr = regs[base] + off;
if (!isCacheIsolated())
{
uint32_t value = (int8_t)Bus::iop_read<uint8_t>(vaddr);
load(rt, value);
}
if (can_disassemble) printf("lb %s, %d(%s)\n", Reg(rt), off, Reg(base));
}
void CPU::op_lh()
{
int rt = i.i_type.rt;
int base = i.i_type.rs;
int16_t off = (int16_t)i.i_type.imm;
uint32_t vaddr = regs[base] + off;
if (!isCacheIsolated())
{
if (vaddr & 1)
{
printf("Unaligned LH!\n");
exit(1);
}
uint32_t data = (int16_t)Bus::iop_read<uint16_t>(vaddr);
load(rt, data);
}
if (can_disassemble) printf("lh %s, %d(%s)\n", Reg(rt), off, Reg(base));
}
void CPU::op_lwl()
{
static const uint32_t LWL_MASK[4] = { 0xffffff, 0x0000ffff, 0x000000ff, 0x00000000 };
static const uint8_t LWL_SHIFT[4] = { 24, 16, 8, 0 };
int16_t offset = (int16_t)(i.i_type.imm);
uint32_t dest = i.i_type.rt;
uint32_t base = i.i_type.rs;
uint32_t addr = regs[base] + offset;
int shift = addr & 0x3;
uint32_t data = regs[dest];
if (last_instruction_was_lwr)
{
data = delayed_memory_load.data;
}
uint32_t mem = Bus::iop_read<uint32_t>(addr & ~0x3);
load(dest, (regs[dest] & LWL_MASK[shift]) | (mem << LWL_SHIFT[shift]));
last_instruction_was_lwl = true;
if (can_disassemble) printf("lwl %s, %d(%s)\n", Reg(dest), offset, Reg(base));
}
void CPU::op_lw()
{
int rt = i.i_type.rt;
int base = i.i_type.rs;
int16_t off = (int16_t)i.i_type.imm;
uint32_t vaddr = regs[base] + off;
if (!isCacheIsolated())
{
if (vaddr & 3)
{
printf("Unaligned LW at address 0x%08x (lw %s, %d(%s))!\n", vaddr, Reg(rt), off, Reg(base));
exit(1);
}
uint32_t data = Bus::iop_read<uint32_t>(vaddr);
load(rt, data);
}
if (can_disassemble) printf("lw %s, %d(%s) (0x%08x)\n", Reg(rt), off, Reg(base), vaddr);
}
void CPU::op_lbu()
{
int rt = i.i_type.rt;
int base = i.i_type.rs;
int32_t off = (int16_t)i.i_type.imm;
uint32_t vaddr = regs[base] + off;
if (!isCacheIsolated())
{
uint32_t value = Bus::iop_read<uint8_t>(vaddr);
load(rt, value);
}
if (can_disassemble) printf("lbu %s, %d(%s)\n", Reg(rt), off, Reg(base));
}
void CPU::op_lhu()
{
int rt = i.i_type.rt;
int base = i.i_type.rs;
int16_t off = (int16_t)i.i_type.imm;
uint32_t vaddr = regs[base] + off;
if (!isCacheIsolated())
{
if (vaddr & 1)
{
printf("Unaligned LHU!\n");
exit(1);
}
uint32_t value = Bus::iop_read<uint16_t>(vaddr);
load(rt, value);
}
if (can_disassemble) printf("lhu %s, %d(%s)\n", Reg(rt), off, Reg(base));
}
void CPU::op_lwr()
{
static const uint32_t LWR_MASK[4] = { 0x000000, 0xff000000, 0xffff0000, 0xffffff00 };
static const uint8_t LWR_SHIFT[4] = { 0, 8, 16, 24 };
int16_t offset = (int16_t)(i.i_type.imm);
uint32_t dest = i.i_type.rt;
uint32_t base = i.i_type.rs;
uint32_t addr = regs[base] + offset;
int shift = addr & 0x3;
uint32_t data = regs[dest];
if (last_instruction_was_lwl)
{
data = delayed_memory_load.data;
}
uint32_t mem = Bus::iop_read<uint32_t>(addr & ~0x3);
mem = (data & LWR_MASK[shift]) | (mem >> LWR_SHIFT[shift]);
load(dest, mem);
last_instruction_was_lwr = true;
if (can_disassemble) printf("lwr %s, %d(%s)\n", Reg(dest), offset, Reg(base));
}
void CPU::op_sb()
{
int rt = i.i_type.rt;
int base = i.i_type.rs;
int16_t off = (int16_t)i.i_type.imm;
uint32_t addr = regs[base] + off;
if (can_disassemble) printf("sb %s, %d(%s)\n", Reg(rt), off, Reg(base));
if (!isCacheIsolated())
Bus::iop_write<uint8_t>(addr, regs[rt]);
}
void CPU::op_sh()
{
int rt = i.i_type.rt;
int base = i.i_type.rs;
int16_t off = (int16_t)i.i_type.imm;
uint32_t addr = regs[base] + off;
if (can_disassemble) printf("sh %s, %d(%s)\n", Reg(rt), off, Reg(base));
if (addr & 1)
{
printf("Unaligned SH!\n");
exit(1);
}
if (!isCacheIsolated())
Bus::iop_write<uint16_t>(addr, regs[rt]);
}
void CPU::op_swl()
{
static const uint32_t SWL_MASK[4] = { 0xffffff00, 0xffff0000, 0xff000000, 0x00000000 };
static const uint8_t SWL_SHIFT[4] = { 24, 16, 8, 0 };
int16_t offset = (int16_t)(i.i_type.imm);
uint32_t source = i.i_type.rt;
uint32_t base = i.i_type.rs;
uint32_t addr = regs[base] + offset;
int shift = addr & 0x3;
uint32_t mem = Bus::iop_read<uint32_t>(addr & ~3);
Bus::iop_write<uint32_t>(addr & ~0x3, (regs[source] >> SWL_SHIFT[shift]) | (mem & SWL_MASK[shift]));
}
void CPU::op_sw()
{
int rt = i.i_type.rt;
int base = i.i_type.rs;
int16_t off = (int16_t)i.i_type.imm;
uint32_t addr = regs[base] + off;
if (can_disassemble) printf("sw %s, %d(%s)\n", Reg(rt), off, Reg(base));
if (addr & 3)
{
printf("Unaligned SW!\n");
exit(1);
}
if (!isCacheIsolated())
Bus::iop_write(addr, regs[rt]);
}
void CPU::op_swr()
{
static const uint32_t SWR_MASK[4] = { 0x00000000, 0x000000ff, 0x0000ffff, 0x00ffffff };
static const uint8_t SWR_SHIFT[4] = { 0, 8, 16, 24 };
int16_t offset = (int16_t)(i.i_type.imm);
uint32_t source = i.i_type.rt;
uint32_t base = i.i_type.rs;
uint32_t addr = regs[base] + offset;
int shift = addr & 0x3;
uint32_t mem = Bus::iop_read<uint32_t>(addr & ~3);
Bus::iop_write<uint32_t>(addr & ~0x3, (regs[source] << SWR_SHIFT[shift]) | (mem & SWR_MASK[shift]));
}
void CPU::op_sll()
{
int rt = i.r_type.rt;
int rd = i.r_type.rd;
int sa = i.r_type.sa;
set_reg(rd, regs[rt] << sa);
if (can_disassemble)
{
if (i.full != 0)
printf("sll %s, %s, 0x%02x\n", Reg(rd), Reg(rt), sa);
else
printf("nop\n");
}
}
void CPU::op_srl()
{
int rt = i.r_type.rt;
int rd = i.r_type.rd;
int sa = i.r_type.sa;
set_reg(rd, regs[rt] >> sa);
if (can_disassemble) printf("srl %s, %s, 0x%02x\n", Reg(rd), Reg(rt), sa);
}
void CPU::op_sra()
{
int rt = i.r_type.rt;
int rd = i.r_type.rd;
int sa = i.r_type.sa;
int32_t reg = (int32_t)regs[rt];
set_reg(rd, reg >> sa);
if (can_disassemble) printf("sra %s, %s, 0x%02x\n", Reg(rd), Reg(rt), sa);
}
void CPU::op_sllv()
{
int rd = i.r_type.rd;
int rt = i.r_type.rt;
int rs = i.r_type.rs;
uint16_t sa = regs[rs] & 0x1F;
set_reg(rd, regs[rt] << sa);
if (can_disassemble) printf("sllv %s, %s, %s\n", Reg(rd), Reg(rt), Reg(rs));
}
void CPU::op_srlv()
{
int rd = i.r_type.rd;
int rt = i.r_type.rt;
int rs = i.r_type.rs;
uint16_t sa = regs[rs] & 0x1F;
set_reg(rd, regs[rt] >> sa);
if (can_disassemble) printf("sllv %s, %s, %s\n", Reg(rd), Reg(rt), Reg(rs));
}
void CPU::op_srav()
{
int rd = i.r_type.rd;
int rt = i.r_type.rt;
int rs = i.r_type.rs;
int32_t source = (int32_t)regs[rt];
source >>= (regs[rs] & 0x1F);
set_reg(rd, source);
if (can_disassemble) printf("srav %s, %s, %s\n", Reg(rd), Reg(rt), Reg(rs));
}
void CPU::op_jr()
{
uint16_t rs = i.i_type.rs;
pc = regs[rs];
next_instr.is_delay_slot = true;
next_instr.branch_taken = true;
if (can_disassemble && i.r_type.func == 0b001000) printf("jr %s\n", Reg(i.r_type.rs));
}
void CPU::op_jalr()
{
uint16_t rd = i.r_type.rd;
set_reg(rd, i.pc+8);
op_jr();
if (can_disassemble) printf("jalr %s, %s\n", Reg(i.r_type.rs), Reg(i.r_type.rd));
}
void CPU::op_syscall()
{
exception(Exception::Syscall, 0);
if (can_disassemble) printf("syscall\n");
}
void CPU::op_addu()
{
int rt = i.r_type.rt;
int rd = i.r_type.rd;
int rs = i.r_type.rs;
set_reg(rd, regs[rs] + regs[rt]);
if (can_disassemble) printf("addu %s, %s (0x%08x), %s (0x%08x)\n", Reg(rd), Reg(rs), regs[rs], Reg(rt), regs[rt]);
}
void CPU::op_sub()
{
int rd = i.r_type.rd;
int rt = i.r_type.rt;
int rs = i.r_type.rs;
uint32_t sub = regs[rs] - regs[rt];
set_reg(rd, sub);
if (can_disassemble) printf("sub %s, %s (0x%08x), %s (0x%08x)\n", Reg(rd), Reg(rs), regs[rs], Reg(rt), regs[rt]);
}
void CPU::op_add()
{
int rt = i.r_type.rt;
int rd = i.r_type.rd;
int rs = i.r_type.rs;
uint32_t add = regs[rs] + regs[rt];
set_reg(rd, add);
if (can_disassemble) printf("add %s, %s (0x%08x), %s (0x%08x)\n", Reg(rd), Reg(rs), regs[rs], Reg(rt), regs[rt]);
}
void CPU::op_mfhi()
{
int rd = i.r_type.rd;
set_reg(rd, hi);
if (can_disassemble) printf("mfhi %s\n", Reg(rd));
}
void CPU::op_mthi()
{
int rs = i.r_type.rs;
hi = regs[rs];
if (can_disassemble) printf("mthi %s\n", Reg(rs));
}
void CPU::op_mflo()
{
int rd = i.r_type.rd;
set_reg(rd, lo);
if (can_disassemble) printf("mflo %s\n", Reg(rd));
}
void CPU::op_mtlo()
{
int rs = i.r_type.rs;
lo = regs[rs];
if (can_disassemble) printf("mtlo %s\n", Reg(rs));
}
void CPU::op_divu()
{
int rt = i.i_type.rt;
int rs = i.i_type.rs;
uint32_t dividend = regs[rs];
uint32_t divisor = regs[rt];
if (divisor == 0) [[unlikely]]
{
hi = dividend;
lo = 0xFFFFFFFF;
}
else
{
hi = dividend % divisor;
lo = dividend / divisor;
}
if (can_disassemble) printf("divu %s, %s\n", Reg(rs), Reg(rt));
}
void CPU::op_mult()
{
int rt = i.r_type.rt;
int rs = i.r_type.rs;
int64_t result = (int64_t)(int32_t)regs[rs] * (int64_t)(int32_t)regs[rt];
hi = result >> 32;
lo = result;
if (can_disassemble) printf("multu %s, %s\n", Reg(rs), Reg(rt));
}
void CPU::op_multu()
{
int rt = i.r_type.rt;
int rs = i.r_type.rs;
uint64_t result = (uint64_t)regs[rs] * (uint64_t)regs[rt];
hi = result >> 32;
lo = result;
if (can_disassemble) printf("multu %s, %s\n", Reg(rs), Reg(rt));
}
void CPU::op_div()
{
int rt = i.i_type.rt;
int rs = i.i_type.rs;
int32_t dividend = (int32_t)regs[rs];
int32_t divisor = (int32_t)regs[rt];
if (divisor == 0) [[unlikely]]
{
hi = regs[rs];
lo = (dividend >= 0 ? 0xFFFFFFFF : 1);
}
else if (regs[rs] == 0x80000000 && divisor == -1) [[unlikely]]
{
hi = 0;
lo = 0x80000000;
}
else
{
hi = (uint32_t)(dividend % divisor);
lo = (uint32_t)(dividend / divisor);
}
if (can_disassemble) printf("div %s, %s\n", Reg(rt), Reg(rs));
}
void CPU::op_subu()
{
int rt = i.r_type.rt;
int rd = i.r_type.rd;
int rs = i.r_type.rs;
set_reg(rd, regs[rs] - regs[rt]);
if (can_disassemble) printf("subu %s, %s, %s\n", Reg(rd), Reg(rs), Reg(rt));
}
void CPU::op_or()
{
int rt = i.r_type.rt;
int rd = i.r_type.rd;
int rs = i.r_type.rs;
set_reg(rd, regs[rt] | regs[rs]);
if (can_disassemble) printf("or %s, %s, %s\n", Reg(rd), Reg(rt), Reg(rs));
}
void CPU::op_xor()
{
int rt = i.r_type.rt;
int rd = i.r_type.rd;
int rs = i.r_type.rs;
set_reg(rd, regs[rt] ^ regs[rs]);
if (can_disassemble) printf("xor %s, %s, %s\n", Reg(rd), Reg(rt), Reg(rs));
}
void CPU::op_nor()
{
int rt = i.r_type.rt;
int rd = i.r_type.rd;
int rs = i.r_type.rs;
set_reg(rd, ~(regs[rt] | regs[rs]));
if (can_disassemble) printf("nor %s, %s, %s\n", Reg(rd), Reg(rt), Reg(rs));
}
void CPU::op_and()
{
int rt = i.r_type.rt;
int rd = i.r_type.rd;
int rs = i.r_type.rs;
set_reg(rd, regs[rt] & regs[rs]);
if (can_disassemble) printf("and %s, %s, %s\n", Reg(rd), Reg(rt), Reg(rs));
}
void CPU::op_slt()
{
int rt = i.r_type.rt;
int rd = i.r_type.rd;
int rs = i.r_type.rs;
int32_t reg1 = (int32_t)regs[rs];
int32_t reg2 = (int32_t)regs[rt];
bool condition = reg1 < reg2;
set_reg(rd, condition);
if (can_disassemble) printf("slt %s, %s, %s\n", Reg(rd), Reg(rs), Reg(rt));
}
void CPU::op_sltu()
{
int rt = i.r_type.rt;
int rd = i.r_type.rd;
int rs = i.r_type.rs;
bool condition = regs[rs] < regs[rt];
set_reg(rd, condition);
if (can_disassemble) printf("sltu %s, %s, %s\n", Reg(rd), Reg(rs), Reg(rt));
}
void CPU::op_mfc0()
{
int rt = i.r_type.rt;
int rd = i.r_type.rd;
load(rt, Cop0.regs[rd]);
if (rd == 14)
{
//printf("Maybe returning from exception at 0x%08x?\n", Cop0.regs[rd]);
}
if (can_disassemble) printf("mfc0 %s, %d\n", Reg(rt), rd);
}
void CPU::op_mtc0()
{
int rt = i.r_type.rt;
int rd = i.r_type.rd;
Cop0.regs[rd] = regs[rt];
if (can_disassemble) printf("mtc0 %d, %s\n", rd, Reg(rt));
}
void CPU::op_rfe()
{
uint32_t mode = Cop0.status.value & 0x3F;
Cop0.status.value &= ~(uint32_t)0xF;
Cop0.status.value |= mode >> 2;
if (can_disassemble) printf("rfe\n");
}

8
src/emu/dev/cdvd.cpp Normal file
View file

@ -0,0 +1,8 @@
#include "cdvd.h"
uint8_t N_status = (1 << 6);
uint8_t CDVD::ReadNStatus()
{
return N_status;
}

8
src/emu/dev/cdvd.h Normal file
View file

@ -0,0 +1,8 @@
#pragma once
#include <cstdint>
namespace CDVD
{
uint8_t ReadNStatus();
};

87
src/emu/dev/sif.cpp Normal file
View file

@ -0,0 +1,87 @@
#include "sif.h"
uint32_t sif_ctrl;
uint32_t bd6;
uint32_t mscom;
uint32_t smcom;
uint32_t msflg;
uint32_t smflg;
void SIF::WriteMSCOM_EE(uint32_t data)
{
mscom = data;
}
void SIF::WriteMSFLG_EE(uint32_t data)
{
msflg |= data;
}
void SIF::WriteCTRL_EE(uint32_t data)
{
if (!(data & 0x100))
sif_ctrl &= ~0x100;
else
sif_ctrl |= 0x100;
}
void SIF::WriteBD6_EE(uint32_t data)
{
bd6 = data;
}
void SIF::WriteCTRL_IOP(uint32_t data)
{
uint8_t bark = data & 0xF0;
if (data & 0xA0)
{
sif_ctrl &= ~0xF000;
sif_ctrl |= 0x2000;
}
if (sif_ctrl & bark)
sif_ctrl &= ~bark;
else
sif_ctrl |= bark;
}
void SIF::WriteSMCOM_IOP(uint32_t data)
{
smcom = data;
}
void SIF::WriteSMFLG_IOP(uint32_t data)
{
smflg = data;
}
uint32_t SIF::ReadMSCOM_EE()
{
return mscom;
}
uint32_t SIF::ReadSMCOM()
{
return smcom;
}
uint32_t SIF::ReadMSFLG()
{
return msflg;
}
uint32_t SIF::ReadSMFLG()
{
return smflg;
}
uint32_t SIF::ReadCTRL()
{
return sif_ctrl;
}
size_t SIF::FIFO0_size()
{
return 0;
}

25
src/emu/dev/sif.h Normal file
View file

@ -0,0 +1,25 @@
#pragma once
#include <util/uint128.h>
namespace SIF
{
void WriteMSCOM_EE(uint32_t data);
void WriteMSFLG_EE(uint32_t data);
void WriteCTRL_EE(uint32_t data);
void WriteBD6_EE(uint32_t data);
void WriteCTRL_IOP(uint32_t data);
void WriteSMCOM_IOP(uint32_t data);
void WriteSMFLG_IOP(uint32_t data);
uint32_t ReadMSCOM_EE();
uint32_t ReadSMCOM();
uint32_t ReadMSFLG();
uint32_t ReadSMFLG();
uint32_t ReadCTRL();
size_t FIFO0_size();
}

View file

@ -12,18 +12,20 @@
#include <emu/cpu/ee/dmac.hpp>
#include <emu/cpu/ee/vu.h>
#include <emu/cpu/ee/vif.h>
#include <emu/dev/sif.h>
uint8_t BiosRom[0x400000];
uint8_t* BiosRom;
uint8_t spr[0x4000];
uint8_t ram[0x2000000];
uint8_t iop_ram[0x200000];
uint8_t* iop_ram;
uint32_t MCH_DRD, MCH_RICM;
uint32_t rdram_sdevid;
uint32_t INTC_MASK = 0, INTC_STAT = 0;
uint32_t Bus::I_MASK = 0, Bus::I_STAT = 0, Bus::I_CTRL = 0;
static uint32_t Translate(uint32_t addr)
uint32_t Translate(uint32_t addr)
{
constexpr uint32_t KUSEG_MASKS[8] =
{
@ -51,6 +53,9 @@ std::ofstream console;
void Bus::LoadBios(uint8_t *data)
{
BiosRom = new uint8_t[0x400000];
iop_ram = new uint8_t[0x200000];
memcpy(BiosRom, data, 0x400000);
console.open("console.txt");
}
@ -74,6 +79,15 @@ void Bus::Dump()
}
dump.close();
dump.open("psx_cpu_ram.dump");
for (int i = 0; i < 0x200000; i++)
{
dump << (char)iop_ram[i];
}
dump.close();
}
uint128_t Bus::Read128(uint32_t addr)
@ -154,10 +168,22 @@ uint32_t Bus::Read32(uint32_t addr)
}
case 0x10003020:
return GIF::ReadStat();
case 0x1000C000:
return DMAC::ReadSIF0Channel(addr);
case 0x1000E000:
return DMAC::ReadDCTRL();
case 0x1000E010:
return DMAC::ReadDSTAT();
case 0x1000E020:
return DMAC::ReadDPCR();
case 0x1000F200:
return SIF::ReadMSCOM_EE();
case 0x1000F210:
return SIF::ReadSMCOM();
case 0x1000F220:
return SIF::ReadMSFLG();
case 0x1000F230:
return SIF::ReadSMFLG();
case 0x10002010:
return 0;
}
@ -328,6 +354,11 @@ void Bus::Write32(uint32_t addr, uint32_t data)
*(uint32_t*)&ram[addr] = data;
return;
}
if (addr >= 0x1C000000 && addr < 0x1C200000)
{
*(uint32_t*)&iop_ram[addr-0x1C000000] = data;
return;
}
switch (addr)
{
@ -431,12 +462,30 @@ void Bus::Write32(uint32_t addr, uint32_t data)
return;
case 0x1000C000:
case 0x1000C010:
case 0x1000C020:
case 0x1000C030:
case 0x1000C040:
case 0x1000C050:
case 0x1000C080:
DMAC::WriteSIF0Channel(addr, data);
return;
case 0x1000C400:
case 0x1000C410:
case 0x1000C420:
case 0x1000C430:
case 0x1000C440:
case 0x1000C450:
case 0x1000C480:
DMAC::WriteSIF1Channel(addr, data);
return;
case 0x1000C800:
case 0x1000C810:
case 0x1000C830:
case 0x1000C840:
case 0x1000C850:
case 0x1000C880:
DMAC::WriteSIF2Channel(addr, data);
return;
case 0x1000D000:
case 0x1000D010:
case 0x1000D030:
@ -482,6 +531,18 @@ void Bus::Write32(uint32_t addr, uint32_t data)
case 0x10002000:
case 0x10002010:
return;
case 0x1000F200:
SIF::WriteMSCOM_EE(data);
return;
case 0x1000F220:
SIF::WriteMSFLG_EE(data);
return;
case 0x1000F240:
SIF::WriteCTRL_EE(data);
return;
case 0x1000F260:
SIF::WriteBD6_EE(data);
return;
}
printf("Write32 0x%08x to unknown address 0x%08x\n", data, addr);

View file

@ -6,6 +6,14 @@
#include <cstdint>
#include <util/uint128.h>
#include <emu/cpu/iop/dma.h>
#include <emu/dev/cdvd.h>
#include <emu/dev/sif.h>
uint32_t Translate(uint32_t addr);
extern uint8_t* BiosRom;
extern uint8_t* iop_ram;
namespace Bus
{
@ -25,4 +33,162 @@ void Write32(uint32_t addr, uint32_t data);
void Write16(uint32_t addr, uint16_t data);
void Write8(uint32_t addr, uint8_t data);
extern uint32_t I_MASK, I_STAT, I_CTRL;
template<typename T>
T iop_read(uint32_t addr)
{
addr = Translate(addr);
if (addr >= 0x1fc00000 && addr < 0x20000000)
return *(T*)&BiosRom[addr - 0x1fc00000];
if (addr < 0x200000)
return *(T*)&iop_ram[addr];
if (addr >= 0x1E000000 && addr < 0x1F000000)
return 0;
switch (addr)
{
case 0x1d000010:
return SIF::ReadSMCOM();
case 0x1d000020:
return SIF::ReadMSFLG();
case 0x1d000030:
return SIF::ReadSMFLG();
case 0x1d000040:
return SIF::ReadCTRL();
case 0x1d000060:
return 0;
case 0x1f402005:
return CDVD::ReadNStatus();
case 0x1f80100C:
case 0x1f801010:
case 0x1f801400:
case 0x1f801450:
return 0;
case 0x1f801074:
return I_MASK;
case 0x1f8010f0:
return IopDma::ReadDPCR();
case 0x1f8010f4:
return IopDma::ReadDICR();
case 0x1f801078:
return I_CTRL;
case 0x1f801570:
return IopDma::ReadDPCR2();
case 0x1f801574:
return IopDma::ReadDICR2();
case 0x1f801578:
return IopDma::ReadDMACEN();
}
printf("[emu/IopBus]: Read from unknown addr 0x%08x\n", addr);
exit(1);
}
template<typename T>
void iop_write(uint32_t addr, T data)
{
addr = Translate(addr);
if (addr < 0x200000)
{
*(T*)&iop_ram[addr] = data;
return;
}
switch (addr)
{
case 0x1d000010:
SIF::WriteSMCOM_IOP(data);
return;
case 0x1d000030:
SIF::WriteSMFLG_IOP(data);
return;
case 0x1d000040:
SIF::WriteCTRL_IOP(data);
return;
case 0x1f801004:
case 0x1f801008:
case 0x1f80100C:
case 0x1f801010:
case 0x1f801014:
case 0x1f801018:
case 0x1f80101C:
case 0x1f801020:
case 0x1f801060:
case 0x1f802070:
case 0x1f801400:
case 0x1f801404:
case 0x1f801408:
case 0x1f80140C:
case 0x1f801410:
case 0x1f801414:
case 0x1f801418:
case 0x1f80141C:
case 0x1f801420:
case 0x1f801450:
case 0x1f801560:
case 0x1f801564:
case 0x1f801568:
case 0x1f80156C:
case 0x1f8015f0:
case 0x1ffe0130:
case 0x1ffe0140:
return;
case 0x1ffe0144:
printf("[emu/IOP]: Scratchpad start 0x%08x\n", data);
return;
case 0x1f801074:
I_MASK = data;
return;
case 0x1f801078:
I_CTRL = data;
return;
case 0x1f8010f0:
IopDma::WriteDPCR(data);
return;
case 0x1f8010f4:
IopDma::WriteDICR(data);
return;
case 0x1f801570:
IopDma::WriteDPCR2(data);
return;
case 0x1F801574:
IopDma::WriteDICR2(data);
return;
case 0x1f801578:
IopDma::WriteDMACEN(data);
return;
case 0x1f801080 ... 0x1f80108C:
case 0x1f801090 ... 0x1f80109C:
case 0x1f8010A0 ... 0x1f8010AC:
case 0x1f8010B0 ... 0x1f8010BC:
case 0x1f8010C0 ... 0x1f8010CC:
case 0x1f8010D0 ... 0x1f8010DC:
case 0x1f8010E0 ... 0x1f8010EC:
IopDma::WriteChannel(addr, data);
return;
case 0x1f801500 ... 0x1f80150C:
case 0x1f801510 ... 0x1f80151C:
case 0x1f801520 ... 0x1f80152C:
case 0x1f801530 ... 0x1f80153C:
case 0x1f801540 ... 0x1f80154C:
case 0x1f801550 ... 0x1f80155C:
IopDma::WriteNewChannel(addr, data);
return;
// Timers
case 0x1F801100 ... 0x1F801108:
case 0x1F801110 ... 0x1F801118:
case 0x1F801120 ... 0x1F801128:
case 0x1F801480 ... 0x1F801488:
case 0x1F801490 ... 0x1F801498:
case 0x1F8014A0 ... 0x1F8014A8:
return;
}
printf("[emu/IopBus]: Write to unknown addr 0x%08x\n", addr);
exit(1);
}
}

View file

@ -3,6 +3,7 @@
#include <algorithm>
#include <limits>
#include <cinttypes>
#include <emu/cpu/iop/cpu.h>
namespace Scheduler
{
@ -41,10 +42,14 @@ void ScheduleEvent(Event event)
std::push_heap(event_queue.begin(), event_queue.end(), CompareEvents);
}
int iop_cycles = 0;
void CheckScheduler(int cycles)
{
global_cycles += cycles;
IOP_MANAGEMENT::Clock(cycles / 4);
if (event_queue.empty())
return;