mirror of
https://github.com/google0101-ryan/Emotional.git
synced 2024-05-11 09:05:28 -04:00
Added IOP
This commit is contained in:
parent
ea6c7c1a1f
commit
e7e728bd1e
2544
3party/robin_hood.h
Normal file
2544
3party/robin_hood.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
242
src/emu/cpu/iop/cpu.cpp
Normal 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
281
src/emu/cpu/iop/cpu.h
Normal 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
161
src/emu/cpu/iop/dma.cpp
Normal 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
23
src/emu/cpu/iop/dma.h
Normal 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
41
src/emu/cpu/iop/opcode.h
Normal 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
867
src/emu/cpu/iop/opcodes.cpp
Normal 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
8
src/emu/dev/cdvd.cpp
Normal 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
8
src/emu/dev/cdvd.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace CDVD
|
||||
{
|
||||
uint8_t ReadNStatus();
|
||||
};
|
87
src/emu/dev/sif.cpp
Normal file
87
src/emu/dev/sif.cpp
Normal 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
25
src/emu/dev/sif.h
Normal 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();
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue