mirror of
https://github.com/google0101-ryan/Emotional.git
synced 2024-06-23 14:43:20 -04:00
Added all-asm JIT dispatcher, runs at ~140fps now
This commit is contained in:
parent
eece745982
commit
a20304b733
|
@ -11,6 +11,7 @@ set(SOURCES src/main.cpp
|
|||
src/emu/cpu/ee/EmotionEngine.cpp
|
||||
src/emu/cpu/ee/dmac.cpp
|
||||
src/emu/cpu/ee/vu.cpp
|
||||
src/emu/cpu/ee/vif.cpp
|
||||
src/emu/cpu/ee/x64/reg_alloc.cpp
|
||||
src/emu/cpu/ee/x64/emit.cpp
|
||||
src/emu/sched/scheduler.cpp
|
||||
|
@ -27,7 +28,7 @@ set(TARGET_NAME ps2)
|
|||
if(MSVC)
|
||||
target_compile_options(${TARGET_NAME} PRIVATE /W4 /WX)
|
||||
else()
|
||||
target_compile_options(${TARGET_NAME} PRIVATE -pg)
|
||||
target_compile_options(${TARGET_NAME} PRIVATE -pg -O0 -mincoming-stack-boundary=3)
|
||||
target_link_options(${TARGET_NAME} PRIVATE -pg)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -5,13 +5,39 @@
|
|||
#include <emu/memory/Bus.h>
|
||||
#include <emu/cpu/ee/EmotionEngine.h>
|
||||
#include <emu/sched/scheduler.h>
|
||||
#include <emu/cpu/ee/vu.h>
|
||||
|
||||
Scheduler::Event vsync_event;
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
std::chrono::steady_clock::time_point first_tp;
|
||||
unsigned long frame_count = 0;
|
||||
|
||||
std::chrono::duration<double> uptime()
|
||||
{
|
||||
if (first_tp == std::chrono::steady_clock::time_point{})
|
||||
return std::chrono::duration<double>{0};
|
||||
|
||||
return std::chrono::steady_clock::now() - first_tp;
|
||||
}
|
||||
|
||||
double fps()
|
||||
{
|
||||
const double uptime_sec = uptime().count();
|
||||
|
||||
if (uptime_sec == 0)
|
||||
return 0;
|
||||
|
||||
return frame_count / uptime_sec;
|
||||
}
|
||||
|
||||
void HandleVsync()
|
||||
{
|
||||
printf("Vsync\n");
|
||||
// printf("FPS: %f\n", fps());
|
||||
Scheduler::ScheduleEvent(vsync_event);
|
||||
frame_count++;
|
||||
}
|
||||
|
||||
void System::LoadBios(std::string biosName)
|
||||
|
@ -47,19 +73,18 @@ void System::Reset()
|
|||
vsync_event.name = "VSYNC handler";
|
||||
vsync_event.cycles_from_now = 4920115;
|
||||
Scheduler::ScheduleEvent(vsync_event);
|
||||
|
||||
first_tp = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
void System::Run()
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
int cycles = EmotionEngine::Clock();
|
||||
Scheduler::CheckScheduler(cycles);
|
||||
}
|
||||
EmotionEngine::Clock();
|
||||
}
|
||||
|
||||
void System::Dump()
|
||||
{
|
||||
EmotionEngine::Dump();
|
||||
Bus::Dump();
|
||||
VectorUnit::Dump();
|
||||
}
|
||||
|
|
|
@ -282,7 +282,7 @@ void JIT::EmitCOP0(uint32_t instr, EE_JIT::IRInstruction &i)
|
|||
// printf("tlbwi\n");
|
||||
break;
|
||||
default:
|
||||
printf("[emu/CPU]: Cannot emit unknown cop0 instruction 0x%02x\n", op);
|
||||
// printf("[emu/CPU]: Cannot emit unknown cop0 instruction 0x%02x\n", op);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -353,6 +353,48 @@ void JIT::EmitDADDIU(uint32_t instr, EE_JIT::IRInstruction &i)
|
|||
cur_block->ir.push_back(i);
|
||||
}
|
||||
|
||||
void JIT::EmitLDL(uint32_t instr, EE_JIT::IRInstruction &i)
|
||||
{
|
||||
int _rt = (instr >> 16) & 0x1F;
|
||||
int _base = (instr >> 21) & 0x1F;
|
||||
int32_t _offset = (int16_t)(instr & 0xffff);
|
||||
|
||||
// printf("ldl %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);
|
||||
|
||||
base.SetReg(_base);
|
||||
rt.SetReg(_rt);
|
||||
offset.SetImm(_offset);
|
||||
|
||||
i = IRInstruction::Build({rt, offset, base}, IRInstrs::LDL);
|
||||
|
||||
cur_block->ir.push_back(i);
|
||||
}
|
||||
|
||||
void JIT::EmitLDR(uint32_t instr, EE_JIT::IRInstruction &i)
|
||||
{
|
||||
int _rt = (instr >> 16) & 0x1F;
|
||||
int _base = (instr >> 21) & 0x1F;
|
||||
int32_t _offset = (int16_t)(instr & 0xffff);
|
||||
|
||||
// printf("ldr %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);
|
||||
|
||||
base.SetReg(_base);
|
||||
rt.SetReg(_rt);
|
||||
offset.SetImm(_offset);
|
||||
|
||||
i = IRInstruction::Build({rt, offset, base}, IRInstrs::LDR);
|
||||
|
||||
cur_block->ir.push_back(i);
|
||||
}
|
||||
|
||||
void JIT::EmitLQ(uint32_t instr, EE_JIT::IRInstruction &i)
|
||||
{
|
||||
int _rt = (instr >> 16) & 0x1F;
|
||||
|
@ -513,6 +555,29 @@ void JIT::EmitLHU(uint32_t instr, EE_JIT::IRInstruction &i)
|
|||
cur_block->ir.push_back(i);
|
||||
}
|
||||
|
||||
void JIT::EmitLWU(uint32_t instr, EE_JIT::IRInstruction &i)
|
||||
{
|
||||
int _rt = (instr >> 16) & 0x1F;
|
||||
int _base = (instr >> 21) & 0x1F;
|
||||
int32_t _offset = (int16_t)(instr & 0xffff);
|
||||
|
||||
// printf("lwu %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);
|
||||
|
||||
base.SetReg(_base);
|
||||
rt.SetReg(_rt);
|
||||
offset.SetImm(_offset);
|
||||
|
||||
i = IRInstruction::Build({rt, offset, base}, IRInstrs::MemoryLoad);
|
||||
i.access_size = IRInstruction::U32;
|
||||
i.is_unsigned = true;
|
||||
|
||||
cur_block->ir.push_back(i);
|
||||
}
|
||||
|
||||
void JIT::EmitSB(uint32_t instr, EE_JIT::IRInstruction &i)
|
||||
{
|
||||
int _rt = (instr >> 16) & 0x1F;
|
||||
|
@ -575,10 +640,53 @@ void JIT::EmitSW(uint32_t instr, EE_JIT::IRInstruction &i)
|
|||
|
||||
i = IRInstruction::Build({rt, offset, base}, IRInstrs::MemoryStore);
|
||||
i.access_size = IRInstruction::U32;
|
||||
i.is_unsigned = false;
|
||||
|
||||
cur_block->ir.push_back(i);
|
||||
}
|
||||
|
||||
void JIT::EmitSDL(uint32_t instr, EE_JIT::IRInstruction &i)
|
||||
{
|
||||
int _rt = (instr >> 16) & 0x1F;
|
||||
int _base = (instr >> 21) & 0x1F;
|
||||
int32_t _offset = (int16_t)(instr & 0xffff);
|
||||
|
||||
// printf("sdl %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);
|
||||
|
||||
base.SetReg(_base);
|
||||
rt.SetReg(_rt);
|
||||
offset.SetImm(_offset);
|
||||
|
||||
i = IRInstruction::Build({rt, offset, base}, IRInstrs::SDL);
|
||||
|
||||
cur_block->ir.push_back(i);
|
||||
}
|
||||
|
||||
void JIT::EmitSDR(uint32_t instr, EE_JIT::IRInstruction &i)
|
||||
{
|
||||
int _rt = (instr >> 16) & 0x1F;
|
||||
int _base = (instr >> 21) & 0x1F;
|
||||
int32_t _offset = (int16_t)(instr & 0xffff);
|
||||
|
||||
// printf("sdr %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);
|
||||
|
||||
base.SetReg(_base);
|
||||
rt.SetReg(_rt);
|
||||
offset.SetImm(_offset);
|
||||
|
||||
i = IRInstruction::Build({rt, offset, base}, IRInstrs::SDR);
|
||||
|
||||
cur_block->ir.push_back(i);
|
||||
}
|
||||
|
||||
void JIT::EmitLD(uint32_t instr, EE_JIT::IRInstruction &i)
|
||||
{
|
||||
int _rt = (instr >> 16) & 0x1F;
|
||||
|
@ -1487,7 +1595,7 @@ void JIT::EmitIR(uint32_t instr)
|
|||
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);
|
||||
|
@ -1506,7 +1614,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;
|
||||
|
@ -1553,6 +1661,17 @@ void JIT::EmitIR(uint32_t instr)
|
|||
case 0x10:
|
||||
EmitCOP0(instr, current_instruction);
|
||||
break;
|
||||
case 0x11:
|
||||
{
|
||||
opcode = (instr >> 21) & 0x1F;
|
||||
switch (opcode)
|
||||
{
|
||||
default:
|
||||
printf("[emu/EE]: Ignoring COP1 command 0x%08x\n", instr);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x12:
|
||||
{
|
||||
opcode = (instr >> 21) & 0x1F;
|
||||
|
@ -1600,6 +1719,12 @@ void JIT::EmitIR(uint32_t instr)
|
|||
case 0x19:
|
||||
EmitDADDIU(instr, current_instruction);
|
||||
break;
|
||||
case 0x1A:
|
||||
EmitLDL(instr, current_instruction);
|
||||
break;
|
||||
case 0x1B:
|
||||
EmitLDR(instr, current_instruction);
|
||||
break;
|
||||
case 0x1C:
|
||||
{
|
||||
opcode = instr & 0x3F;
|
||||
|
@ -1658,6 +1783,9 @@ void JIT::EmitIR(uint32_t instr)
|
|||
case 0x25:
|
||||
EmitLHU(instr, current_instruction);
|
||||
break;
|
||||
case 0x27:
|
||||
EmitLWU(instr, current_instruction);
|
||||
break;
|
||||
case 0x28:
|
||||
EmitSB(instr, current_instruction);
|
||||
break;
|
||||
|
@ -1667,6 +1795,12 @@ void JIT::EmitIR(uint32_t instr)
|
|||
case 0x2b:
|
||||
EmitSW(instr, current_instruction);
|
||||
break;
|
||||
case 0x2c:
|
||||
EmitSDL(instr, current_instruction);
|
||||
break;
|
||||
case 0x2d:
|
||||
EmitSDR(instr, current_instruction);
|
||||
break;
|
||||
case 0x2f:
|
||||
// printf("cache\n");
|
||||
break;
|
||||
|
@ -1692,22 +1826,18 @@ void JIT::EmitPrequel(uint32_t guest_start)
|
|||
cur_block = new Block;
|
||||
cur_block->guest_addr = guest_start;
|
||||
cur_block->entry = (uint8_t*)emit->GetFreeBase();
|
||||
|
||||
auto i = IRInstruction::Build({}, IRInstrs::SaveHostRegs);
|
||||
cur_block->ir.push_back(i);
|
||||
}
|
||||
|
||||
JIT::EntryFunc JIT::EmitDone(size_t cycles_taken)
|
||||
{
|
||||
// printf("Done.\n");
|
||||
|
||||
IRValue cycles = IRValue(IRValue::Imm);
|
||||
cycles.SetImm32Unsigned(cycles_taken);
|
||||
|
||||
auto i = IRInstruction::Build({cycles}, IRInstrs::UpdateCopCount);
|
||||
cur_block->ir.push_back(i);
|
||||
|
||||
i = IRInstruction::Build({}, IRInstrs::RestoreHostRegs);
|
||||
cur_block->ir.push_back(i);
|
||||
|
||||
blockCache[cur_block->guest_addr] = cur_block;
|
||||
EE_JIT::emit->TranslateBlock(cur_block);
|
||||
|
||||
|
@ -1718,6 +1848,12 @@ JIT::EntryFunc JIT::EmitDone(size_t cycles_taken)
|
|||
|
||||
Block* JIT::GetExistingBlock(uint32_t start)
|
||||
{
|
||||
if (!blockCache[start])
|
||||
{
|
||||
// printf("ERROR: Block doesn't exist at 0x%08x\n", start);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return blockCache[start];
|
||||
}
|
||||
|
||||
|
@ -1741,7 +1877,7 @@ void JIT::MarkDirty(uint32_t address, uint32_t size)
|
|||
{
|
||||
for (auto i = address; i < address+size; i++)
|
||||
{
|
||||
if (blockCache[i])
|
||||
if (blockCache.find(size) != blockCache.end())
|
||||
{
|
||||
delete blockCache[i];
|
||||
blockCache[i] = nullptr;
|
||||
|
@ -1799,73 +1935,92 @@ bool IsBranch(uint32_t instr)
|
|||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
EE_JIT::JIT::EntryFunc EmitDone(int cycles_taken)
|
||||
{
|
||||
return jit.EmitDone(cycles_taken);
|
||||
}
|
||||
|
||||
EE_JIT::JIT::EntryFunc GetExistingBlockFunc(uint32_t addr)
|
||||
{
|
||||
return (EE_JIT::JIT::EntryFunc)jit.GetExistingBlock(addr)->entry;
|
||||
}
|
||||
|
||||
uint64_t GetExistingBlockCycles(uint32_t addr)
|
||||
{
|
||||
return jit.GetExistingBlock(addr)->cycles;
|
||||
}
|
||||
|
||||
#define BLOCKCACHE_ENABLE
|
||||
|
||||
int Clock()
|
||||
{
|
||||
jit.CheckCacheFull();
|
||||
// jit.CheckCacheFull();
|
||||
|
||||
#ifdef BLOCKCACHE_ENABLE
|
||||
if (!jit.DoesBlockExist(state.pc))
|
||||
{
|
||||
#endif
|
||||
jit.EmitPrequel(state.pc);
|
||||
// #ifdef BLOCKCACHE_ENABLE
|
||||
// if (!jit.DoesBlockExist(state.pc))
|
||||
// {
|
||||
// #endif
|
||||
// jit.EmitPrequel(state.pc);
|
||||
|
||||
bool isBranch = false;
|
||||
bool isBranchDelayed = false;
|
||||
// bool isBranch = false;
|
||||
// bool isBranchDelayed = false;
|
||||
|
||||
uint32_t pc = state.pc;
|
||||
uint32_t next_pc = state.next_pc;
|
||||
// uint32_t pc = state.pc;
|
||||
// uint32_t next_pc = state.next_pc;
|
||||
|
||||
int cycles = 0;
|
||||
int instrs_emitted = 0;
|
||||
// int cycles = 0;
|
||||
// int instrs_emitted = 0;
|
||||
|
||||
do
|
||||
{
|
||||
instrs_emitted++;
|
||||
uint32_t instr = Bus::Read32(pc);
|
||||
pc = next_pc;
|
||||
next_pc += 4;
|
||||
// do
|
||||
// {
|
||||
// instrs_emitted++;
|
||||
// uint32_t instr = Bus::Read32(pc);
|
||||
// pc = next_pc;
|
||||
// next_pc += 4;
|
||||
|
||||
state.pc_at = pc - 4;
|
||||
// state.pc_at = pc - 4;
|
||||
|
||||
// printf("0x%08x (0x%08x): ", pc - 4, instr);
|
||||
// // printf("0x%08x (0x%08x): ", pc - 4, instr);
|
||||
|
||||
jit.EmitIR(instr);
|
||||
// jit.EmitIR(instr);
|
||||
|
||||
isBranch = isBranchDelayed;
|
||||
isBranchDelayed = IsBranch(instr);
|
||||
// isBranch = isBranchDelayed;
|
||||
// isBranchDelayed = IsBranch(instr);
|
||||
|
||||
if (instrs_emitted == 20 && !isBranchDelayed)
|
||||
break;
|
||||
// if (instrs_emitted == 20 && !isBranchDelayed)
|
||||
// break;
|
||||
|
||||
cycles += 2;
|
||||
} while (!isBranch);
|
||||
// cycles += 1;
|
||||
// } while (!isBranch);
|
||||
|
||||
auto func = jit.EmitDone(cycles);
|
||||
func();
|
||||
// auto func = jit.EmitDone(cycles);
|
||||
// func();
|
||||
|
||||
return cycles;
|
||||
#ifdef BLOCKCACHE_ENABLE
|
||||
}
|
||||
else
|
||||
{
|
||||
auto b = jit.GetExistingBlock(state.pc);
|
||||
// return cycles;
|
||||
// #ifdef BLOCKCACHE_ENABLE
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// auto b = jit.GetExistingBlock(state.pc);
|
||||
|
||||
if (!b)
|
||||
{
|
||||
printf("Something has gone seriously wrong!\n");
|
||||
exit(1);
|
||||
}
|
||||
// if (!b)
|
||||
// {
|
||||
// // printf("Something has gone seriously wrong!\n");
|
||||
// exit(1);
|
||||
// }
|
||||
|
||||
auto func = (EE_JIT::JIT::EntryFunc)b->entry;
|
||||
func();
|
||||
// auto func = (EE_JIT::JIT::EntryFunc)b->entry;
|
||||
// func();
|
||||
|
||||
return b->cycles;
|
||||
}
|
||||
#endif
|
||||
// return b->cycles;
|
||||
// }
|
||||
// #endif
|
||||
|
||||
EE_JIT::emit->EnterDispatcher();
|
||||
}
|
||||
|
||||
void Dump()
|
||||
|
@ -1890,4 +2045,21 @@ void MarkDirty(uint32_t address, uint32_t size)
|
|||
jit.MarkDirty(address, size);
|
||||
}
|
||||
|
||||
void EmitPrequel()
|
||||
{
|
||||
jit.EmitPrequel(state.pc);
|
||||
}
|
||||
|
||||
void CheckCacheFull()
|
||||
{
|
||||
jit.CheckCacheFull();
|
||||
}
|
||||
bool DoesBlockExit(uint32_t addr)
|
||||
{
|
||||
return jit.DoesBlockExist(addr);
|
||||
}
|
||||
void EmitIR(uint32_t instr)
|
||||
{
|
||||
jit.EmitIR(instr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,10 @@ enum IRInstrs
|
|||
UpdateCopCount = 26,
|
||||
POR = 27,
|
||||
NOR = 28,
|
||||
LDL = 29,
|
||||
LDR = 30,
|
||||
SDL = 31,
|
||||
SDR = 32,
|
||||
};
|
||||
|
||||
struct IRValue
|
||||
|
@ -179,6 +183,8 @@ private:
|
|||
void EmitBEQL(uint32_t instr, EE_JIT::IRInstruction& i); // 0x14
|
||||
void EmitBNEL(uint32_t instr, EE_JIT::IRInstruction& i); // 0x15
|
||||
void EmitDADDIU(uint32_t instr, EE_JIT::IRInstruction& i); // 0x19
|
||||
void EmitLDL(uint32_t instr, EE_JIT::IRInstruction& i); // 0x1a
|
||||
void EmitLDR(uint32_t instr, EE_JIT::IRInstruction& i); // 0x1b
|
||||
void EmitLQ(uint32_t instr, EE_JIT::IRInstruction& i); // 0x1f
|
||||
void EmitSQ(uint32_t instr, EE_JIT::IRInstruction& i); // 0x1f
|
||||
void EmitLB(uint32_t instr, EE_JIT::IRInstruction& i); // 0x20
|
||||
|
@ -186,9 +192,12 @@ private:
|
|||
void EmitLW(uint32_t instr, EE_JIT::IRInstruction& i); // 0x23
|
||||
void EmitLBU(uint32_t instr, EE_JIT::IRInstruction& i); // 0x24
|
||||
void EmitLHU(uint32_t instr, EE_JIT::IRInstruction& i); // 0x25
|
||||
void EmitLWU(uint32_t instr, EE_JIT::IRInstruction& i); // 0x27
|
||||
void EmitSB(uint32_t instr, EE_JIT::IRInstruction& i); // 0x28
|
||||
void EmitSH(uint32_t instr, EE_JIT::IRInstruction& i); // 0x29
|
||||
void EmitSW(uint32_t instr, EE_JIT::IRInstruction& i); // 0x2B
|
||||
void EmitSDL(uint32_t instr, EE_JIT::IRInstruction& i); // 0x2C
|
||||
void EmitSDR(uint32_t instr, EE_JIT::IRInstruction& i); // 0x2D
|
||||
void EmitLD(uint32_t instr, EE_JIT::IRInstruction& i); // 0x37
|
||||
void EmitSWC1(uint32_t instr, EE_JIT::IRInstruction& i); // 0x39
|
||||
void EmitSD(uint32_t instr, EE_JIT::IRInstruction& i); // 0x3f
|
||||
|
@ -280,6 +289,15 @@ int Clock();
|
|||
void Dump();
|
||||
ProcessorState* GetState();
|
||||
void MarkDirty(uint32_t address, uint32_t size);
|
||||
void EmitPrequel();
|
||||
|
||||
void CheckCacheFull();
|
||||
bool DoesBlockExit(uint32_t addr);
|
||||
void EmitIR(uint32_t instr);
|
||||
bool IsBranch(uint32_t instr);
|
||||
EE_JIT::JIT::EntryFunc EmitDone(int cycles_taken);
|
||||
EE_JIT::JIT::EntryFunc GetExistingBlockFunc(uint32_t addr);
|
||||
uint64_t GetExistingBlockCycles(uint32_t addr);
|
||||
|
||||
inline const char* Reg(int index)
|
||||
{
|
||||
|
|
153
src/emu/cpu/ee/vif.cpp
Normal file
153
src/emu/cpu/ee/vif.cpp
Normal file
|
@ -0,0 +1,153 @@
|
|||
#include "vif.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <queue>
|
||||
#include <emu/sched/scheduler.h>
|
||||
|
||||
bool vif1_event_scheduled = false;
|
||||
bool vif0_event_scheduled = false;
|
||||
|
||||
void VIF::WriteFBRST(int vif_num, uint32_t data)
|
||||
{
|
||||
if (data & 1)
|
||||
{
|
||||
printf("[emu/VIF%d]: Reset\n", vif_num);
|
||||
}
|
||||
}
|
||||
|
||||
void VIF::WriteMASK(int vif_num, uint32_t data)
|
||||
{
|
||||
}
|
||||
|
||||
std::queue<uint32_t> vif1_fifo, vif0_fifo;
|
||||
|
||||
struct VIF_DATA
|
||||
{
|
||||
uint16_t cycle;
|
||||
uint16_t mask;
|
||||
uint16_t mode;
|
||||
uint16_t itop;
|
||||
} vif1, vif0;
|
||||
|
||||
void HandleVIF1Data()
|
||||
{
|
||||
uint32_t data = vif1_fifo.front();
|
||||
vif1_fifo.pop();
|
||||
|
||||
uint8_t cmd = (data >> 24) & 0xff;
|
||||
uint16_t imm = data & 0xffff;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case 0x01:
|
||||
printf("[emu/VIF1]: STCYCL 0x%04x\n", imm);
|
||||
vif1.cycle = imm;
|
||||
break;
|
||||
default:
|
||||
printf("[emu/VIF1]: Unknown CMD 0x%02x\n", cmd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!vif1_fifo.empty())
|
||||
{
|
||||
Scheduler::Event event;
|
||||
event.cycles_from_now = 2;
|
||||
event.func = HandleVIF1Data;
|
||||
event.name = "VIF1 Data Handler";
|
||||
}
|
||||
else
|
||||
vif1_event_scheduled = false;
|
||||
}
|
||||
|
||||
void VIF::WriteVIF1FIFO(uint128_t data)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
vif1_fifo.push(data.u32[i]);
|
||||
}
|
||||
|
||||
if (!vif1_event_scheduled)
|
||||
{
|
||||
Scheduler::Event event;
|
||||
// Most of these events should be handled ASAP
|
||||
event.cycles_from_now = 0;
|
||||
event.func = HandleVIF1Data;
|
||||
event.name = "VIF1 Data Handler";
|
||||
|
||||
Scheduler::ScheduleEvent(event);
|
||||
vif1_event_scheduled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void HandleVIF0Data()
|
||||
{
|
||||
uint32_t data = vif0_fifo.front();
|
||||
vif0_fifo.pop();
|
||||
|
||||
uint8_t cmd = (data >> 24) & 0xff;
|
||||
uint16_t imm = data & 0xffff;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case 0x00:
|
||||
printf("[emu/VIF0]: NOP\n");
|
||||
break;
|
||||
case 0x01:
|
||||
printf("[emu/VIF0]: STCYCL 0x%04x\n", imm);
|
||||
vif0.cycle = imm;
|
||||
break;
|
||||
case 0x04:
|
||||
printf("[emu/VIF0]: ITOP 0x%04x\n", imm);
|
||||
vif0.itop = imm;
|
||||
break;
|
||||
case 0x05:
|
||||
printf("[emu/VIF0]: STMOD 0x%04x\n", imm);
|
||||
vif0.mode = imm;
|
||||
break;
|
||||
case 0x20:
|
||||
printf("[emu/VIF0]: STMASK 0x%04x\n", imm);
|
||||
vif0.mask = imm;
|
||||
break;
|
||||
default:
|
||||
printf("[emu/VIF0]: Unknown CMD 0x%02x\n", cmd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!vif0_fifo.empty())
|
||||
{
|
||||
Scheduler::Event event;
|
||||
event.cycles_from_now = 2;
|
||||
event.func = HandleVIF0Data;
|
||||
event.name = "VIF0 Data Handler";
|
||||
|
||||
Scheduler::ScheduleEvent(event);
|
||||
vif0_event_scheduled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
vif0_event_scheduled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void VIF::WriteVIF0FIFO(uint128_t data)
|
||||
{
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
printf("[emu/VIF0]: Adding 0x%08x to VIF0 FIFO\n", data.u32[i]);
|
||||
vif0_fifo.push(data.u32[i]);
|
||||
}
|
||||
|
||||
if (!vif0_event_scheduled)
|
||||
{
|
||||
Scheduler::Event event;
|
||||
// Most of these events should be handled ASAP
|
||||
event.cycles_from_now = 0;
|
||||
event.func = HandleVIF0Data;
|
||||
event.name = "VIF0 Data Handler";
|
||||
|
||||
Scheduler::ScheduleEvent(event);
|
||||
vif0_event_scheduled = true;
|
||||
}
|
||||
}
|
15
src/emu/cpu/ee/vif.h
Normal file
15
src/emu/cpu/ee/vif.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <util/uint128.h>
|
||||
|
||||
namespace VIF
|
||||
{
|
||||
|
||||
void WriteFBRST(int vif_num, uint32_t data);
|
||||
void WriteMASK(int vif_num, uint32_t data);
|
||||
|
||||
void WriteVIF1FIFO(uint128_t data);
|
||||
void WriteVIF0FIFO(uint128_t data);
|
||||
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
#include "vu.h"
|
||||
#include <fstream>
|
||||
|
||||
namespace VectorUnit
|
||||
{
|
||||
|
@ -9,7 +10,7 @@ uint32_t code_address_mask[2] = {0xfff, 0x3fff};
|
|||
uint8_t data[2][0x4000];
|
||||
uint8_t code[2][0x4000];
|
||||
|
||||
void WriteDataMem128(int vector, uint32_t addr, uint128_t data)
|
||||
void WriteDataMem128(int vector, uint32_t addr, uint128_t _data)
|
||||
{
|
||||
if (vector == 1)
|
||||
addr = (addr - 0x1100C000);
|
||||
|
@ -18,7 +19,47 @@ void WriteDataMem128(int vector, uint32_t addr, uint128_t data)
|
|||
|
||||
addr &= data_address_mask[vector];
|
||||
|
||||
*(uint128_t*)&data[vector][addr] = _data;
|
||||
}
|
||||
|
||||
void WriteCodeMem128(int vector, uint32_t addr, uint128_t data)
|
||||
{
|
||||
if (vector == 1)
|
||||
addr = (addr - 0x11008000);
|
||||
else
|
||||
addr = (addr - 0x11000000);
|
||||
|
||||
addr &= code_address_mask[vector];
|
||||
|
||||
*(uint128_t*)&code[vector][addr] = data;
|
||||
}
|
||||
|
||||
void WriteCodeMem64(int vector, uint32_t addr, uint64_t data)
|
||||
{
|
||||
if (vector == 1)
|
||||
addr = (addr - 0x11008000);
|
||||
else
|
||||
addr = (addr - 0x11000000);
|
||||
|
||||
addr &= code_address_mask[vector];
|
||||
|
||||
*(uint64_t*)&code[vector][addr] = data;
|
||||
}
|
||||
|
||||
void Dump()
|
||||
{
|
||||
std::ofstream out_file("vu0_code.bin");
|
||||
|
||||
for (int i = 0; i < 0x4000; i++)
|
||||
{
|
||||
out_file << code[0][i];
|
||||
}
|
||||
|
||||
out_file.close();
|
||||
out_file.open("vu1_code.bin");
|
||||
for (int i = 0; i < 0x4000; i++)
|
||||
{
|
||||
out_file << code[1][i];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,4 +8,16 @@ namespace VectorUnit
|
|||
|
||||
void WriteDataMem128(int vector, uint32_t addr, uint128_t data);
|
||||
|
||||
void WriteCodeMem128(int vector, uint32_t addr, uint128_t data);
|
||||
void WriteCodeMem64(int vector, uint32_t addr, uint64_t data);
|
||||
|
||||
void Dump();
|
||||
|
||||
namespace VU0
|
||||
{
|
||||
|
||||
// VU0 specific stuff, like COP2 opcodes
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
#include <sys/mman.h>
|
||||
#include <emu/cpu/ee/EmotionEngine.h>
|
||||
#include <emu/memory/Bus.h>
|
||||
#include <emu/sched/scheduler.h>
|
||||
#include <fstream>
|
||||
#include <cassert>
|
||||
|
||||
|
@ -202,7 +203,7 @@ void EE_JIT::Emitter::EmitBC(IRInstruction i)
|
|||
// cg->add(cg->dword[next_pc], 4);
|
||||
reg_alloc->Reset();
|
||||
EmitIncPC(i);
|
||||
EmitRestoreHostRegs();
|
||||
cg->ret();
|
||||
}
|
||||
|
||||
cg->L(end);
|
||||
|
@ -1183,6 +1184,136 @@ EE_JIT::Emitter::Emitter()
|
|||
cg = new Xbyak::CodeGenerator(0xffffff, (void*)base);
|
||||
reg_alloc = new RegisterAllocator();
|
||||
reg_alloc->Reset();
|
||||
|
||||
reg_alloc->MarkRegUsed(RegisterAllocator::RDI);
|
||||
reg_alloc->MarkRegUsed(RegisterAllocator::RAX);
|
||||
|
||||
Xbyak::Reg64 func_ptr = Xbyak::Reg64(reg_alloc->AllocHostRegister());
|
||||
Xbyak::Reg32 next_pc = Xbyak::Reg32(reg_alloc->AllocHostRegister());
|
||||
Xbyak::Reg32 pc = Xbyak::Reg32(reg_alloc->AllocHostRegister());
|
||||
Xbyak::Reg32 isBranchDelayed = Xbyak::Reg32(reg_alloc->AllocHostRegister());
|
||||
|
||||
cg->endbr64();
|
||||
EmitSaveHostRegs();
|
||||
|
||||
// RSP + 0: pc
|
||||
// RSP + 4: next_pc
|
||||
// RSP + 8: isBranch
|
||||
// RSP + 16: isBranchDelayed
|
||||
// RSP + 20: instrs_emitted
|
||||
// RSP + 24 .. RSP + 32: Wasted space for alignment reasons
|
||||
cg->sub(cg->rsp, 32);
|
||||
|
||||
cg->mov(cg->dword[cg->rsp + 0], 0);
|
||||
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(func_ptr, reinterpret_cast<uint64_t>(EmotionEngine::CheckCacheFull));
|
||||
cg->call(func_ptr);
|
||||
|
||||
auto next_pc_off = (offsetof(EmotionEngine::ProcessorState, next_pc));
|
||||
auto pc_off = (offsetof(EmotionEngine::ProcessorState, pc));
|
||||
|
||||
cg->mov(next_pc, cg->dword[cg->rbp + next_pc_off]);
|
||||
cg->mov(pc, cg->dword[cg->rbp + pc_off]);
|
||||
|
||||
cg->mov(cg->dword[cg->rsp], pc);
|
||||
cg->mov(cg->dword[cg->rsp + 4], next_pc);
|
||||
|
||||
cg->mov(cg->edi, cg->dword[cg->rsp]);
|
||||
cg->mov(func_ptr, reinterpret_cast<uint64_t>(EmotionEngine::DoesBlockExit));
|
||||
cg->call(func_ptr);
|
||||
|
||||
cg->test(cg->al, cg->al);
|
||||
|
||||
Xbyak::Label block;
|
||||
cg->jne(block, Xbyak::CodeGenerator::T_NEAR);
|
||||
|
||||
cg->mov(func_ptr, reinterpret_cast<uint64_t>(EmotionEngine::EmitPrequel));
|
||||
cg->call(func_ptr);
|
||||
|
||||
Xbyak::Label compile_instr;
|
||||
cg->L(compile_instr);
|
||||
|
||||
cg->add(cg->dword[cg->rsp + 20], 1);
|
||||
cg->mov(cg->edi, cg->dword[cg->rsp]);
|
||||
cg->mov(func_ptr, reinterpret_cast<uint64_t>(Bus::Read32));
|
||||
cg->call(func_ptr);
|
||||
|
||||
cg->mov(next_pc, cg->dword[cg->rsp + 4]);
|
||||
cg->mov(cg->dword[cg->rsp], next_pc);
|
||||
cg->add(cg->dword[cg->rsp + 4], 4);
|
||||
|
||||
cg->mov(cg->rdi, cg->rax);
|
||||
cg->mov(func_ptr, reinterpret_cast<uint64_t>(EmotionEngine::EmitIR));
|
||||
cg->call(func_ptr);
|
||||
|
||||
cg->mov(isBranchDelayed, cg->dword[cg->rsp + 16]);
|
||||
cg->mov(cg->dword[cg->rsp + 8], isBranchDelayed);
|
||||
|
||||
|
||||
cg->mov(cg->edi, cg->dword[cg->rsp]);
|
||||
cg->sub(cg->edi, 4);
|
||||
cg->mov(func_ptr, reinterpret_cast<uint64_t>(Bus::Read32));
|
||||
cg->call(func_ptr);
|
||||
cg->mov(cg->edi, cg->eax);
|
||||
cg->mov(cg->eax, 0);
|
||||
cg->mov(func_ptr, reinterpret_cast<uint64_t>(EmotionEngine::IsBranch));
|
||||
cg->call(func_ptr);
|
||||
cg->mov(cg->dword[cg->rsp + 16], cg->eax);
|
||||
|
||||
Xbyak::Label block_compiled;
|
||||
|
||||
cg->cmp(cg->dword[cg->rsp + 20], 20);
|
||||
cg->je(block_compiled);
|
||||
cg->mov(isBranchDelayed, cg->dword[cg->rsp + 8]);
|
||||
cg->cmp(isBranchDelayed, 1);
|
||||
cg->jge(block_compiled);
|
||||
|
||||
cg->jmp(compile_instr);
|
||||
|
||||
cg->L(block_compiled);
|
||||
|
||||
cg->mov(cg->edi, cg->dword[cg->rsp + 20]);
|
||||
cg->mov(func_ptr, reinterpret_cast<uint64_t>(EmotionEngine::EmitDone));
|
||||
cg->call(func_ptr);
|
||||
|
||||
cg->call(cg->rax);
|
||||
cg->mov(cg->rdi, cg->dword[cg->rsp + 20]);
|
||||
cg->mov(func_ptr, reinterpret_cast<uint64_t>(Scheduler::CheckScheduler));
|
||||
cg->call(func_ptr);
|
||||
|
||||
cg->jmp(begin);
|
||||
|
||||
cg->L(block);
|
||||
|
||||
cg->mov(cg->rdi, cg->dword[cg->rsp]);
|
||||
cg->mov(func_ptr, reinterpret_cast<uint64_t>(EmotionEngine::GetExistingBlockFunc));
|
||||
cg->call(func_ptr);
|
||||
cg->call(cg->rax);
|
||||
|
||||
cg->mov(cg->rdi, cg->dword[cg->rsp]);
|
||||
cg->mov(func_ptr, reinterpret_cast<uint64_t>(EmotionEngine::GetExistingBlockCycles));
|
||||
cg->call(func_ptr);
|
||||
cg->mov(cg->rdi, cg->rax);
|
||||
cg->mov(func_ptr, reinterpret_cast<uint64_t>(Scheduler::CheckScheduler));
|
||||
cg->call(func_ptr);
|
||||
|
||||
cg->jmp(begin);
|
||||
|
||||
cg->add(cg->rsp, 32);
|
||||
EmitRestoreHostRegs();
|
||||
|
||||
dispatcher_entry = (EE_JIT::JIT::EntryFunc)base;
|
||||
base += cg->getSize();
|
||||
|
||||
Dump();
|
||||
}
|
||||
|
||||
void EE_JIT::Emitter::Dump()
|
||||
|
@ -1206,6 +1337,13 @@ void EE_JIT::Emitter::TranslateBlock(Block *block)
|
|||
{
|
||||
EmitIR(i);
|
||||
}
|
||||
|
||||
cg->ret();
|
||||
}
|
||||
|
||||
void EE_JIT::Emitter::EnterDispatcher()
|
||||
{
|
||||
dispatcher_entry();
|
||||
}
|
||||
|
||||
const uint8_t *EE_JIT::Emitter::GetFreeBase()
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <cstdint>
|
||||
#include <emu/cpu/ee/x64/reg_alloc.h>
|
||||
#include <3rdparty/xbyak/xbyak.h>
|
||||
#include <emu/cpu/ee/EmotionEngine.h>
|
||||
#include <vector>
|
||||
|
||||
namespace EE_JIT
|
||||
|
@ -51,11 +52,15 @@ private:
|
|||
void EmitBranchRegImm(IRInstruction i);
|
||||
void EmitUpdateCopCount(IRInstruction i);
|
||||
void EmitPOR(IRInstruction i);
|
||||
|
||||
EE_JIT::JIT::EntryFunc dispatcher_entry;
|
||||
public:
|
||||
Emitter();
|
||||
void Dump();
|
||||
void TranslateBlock(Block* block);
|
||||
|
||||
void EnterDispatcher();
|
||||
|
||||
void ResetFreeBase()
|
||||
{
|
||||
free_base = base;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <emu/gpu/gs.h>
|
||||
#include <emu/cpu/ee/dmac.hpp>
|
||||
#include <emu/cpu/ee/vu.h>
|
||||
#include <emu/cpu/ee/vif.h>
|
||||
|
||||
uint8_t BiosRom[0x400000];
|
||||
uint8_t spr[0x4000];
|
||||
|
@ -157,6 +158,8 @@ uint32_t Bus::Read32(uint32_t addr)
|
|||
return DMAC::ReadDSTAT();
|
||||
case 0x1000E020:
|
||||
return DMAC::ReadDPCR();
|
||||
case 0x10002010:
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("Read32 from unknown address 0x%08x\n", addr);
|
||||
|
@ -171,6 +174,14 @@ uint16_t Bus::Read16(uint32_t addr)
|
|||
return *(uint16_t*)&BiosRom[addr - 0x1fc00000];
|
||||
if (addr >= 0x70000000 && addr < 0x70004000)
|
||||
return *(uint16_t*)&spr[addr - 0x70000000];
|
||||
if (addr < 0x2000000)
|
||||
return *(uint16_t*)&ram[addr];
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x1f803800:
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("Read16 from unknown address 0x%08x\n", addr);
|
||||
exit(1);
|
||||
|
@ -209,7 +220,22 @@ void Bus::Write128(uint32_t addr, uint128_t data)
|
|||
*(__uint128_t*)&ram[addr] = data.u128;
|
||||
return;
|
||||
}
|
||||
if (addr >= 0x70000000 && addr < 0x70004000)
|
||||
{
|
||||
*(__uint128_t*)&spr[addr - 0x70000000] = data.u128;
|
||||
return;
|
||||
}
|
||||
|
||||
if (addr >= 0x11000000 && addr < 0x11004000)
|
||||
{
|
||||
VectorUnit::WriteDataMem128(0, addr, data);
|
||||
return;
|
||||
}
|
||||
if (addr >= 0x11004000 && addr < 0x11008000)
|
||||
{
|
||||
VectorUnit::WriteDataMem128(0, addr, data);
|
||||
return;
|
||||
}
|
||||
if (addr >= 0x1100C000 && addr < 0x11010000)
|
||||
{
|
||||
VectorUnit::WriteDataMem128(1, addr, data);
|
||||
|
@ -218,9 +244,17 @@ void Bus::Write128(uint32_t addr, uint128_t data)
|
|||
|
||||
switch (addr)
|
||||
{
|
||||
case 0x10004000:
|
||||
VIF::WriteVIF0FIFO(data);
|
||||
return;
|
||||
case 0x10005000:
|
||||
VIF::WriteVIF1FIFO(data);
|
||||
return;
|
||||
case 0x10006000:
|
||||
GIF::WriteFIFO(data);
|
||||
return;
|
||||
case 0x10007010:
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Write128 0x%lx%016lx to unknown address 0x%08x\n", data.u64[1], data.u64[0], addr);
|
||||
|
@ -243,6 +277,11 @@ void Bus::Write64(uint32_t addr, uint64_t data)
|
|||
*(uint64_t*)&ram[addr] = data;
|
||||
return;
|
||||
}
|
||||
if (addr >= 0x11008000 && addr < 0x1100C000)
|
||||
{
|
||||
VectorUnit::WriteCodeMem64(1, addr, data);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
|
@ -348,6 +387,7 @@ void Bus::Write32(uint32_t addr, uint32_t data)
|
|||
case 0x10001810:
|
||||
case 0x10001820:
|
||||
case 0x10001830:
|
||||
case 0x1000f510:
|
||||
return;
|
||||
case 0x10008000:
|
||||
case 0x10008010:
|
||||
|
@ -427,6 +467,21 @@ void Bus::Write32(uint32_t addr, uint32_t data)
|
|||
case 0x1000E040:
|
||||
case 0x1000E050:
|
||||
return;
|
||||
case 0x10003810:
|
||||
VIF::WriteFBRST(0, data);
|
||||
return;
|
||||
case 0x10003820:
|
||||
case 0x10003830:
|
||||
VIF::WriteMASK(0, data);
|
||||
return;
|
||||
case 0x10003c00:
|
||||
return;
|
||||
case 0x10003c10:
|
||||
VIF::WriteFBRST(1, data);
|
||||
return;
|
||||
case 0x10002000:
|
||||
case 0x10002010:
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Write32 0x%08x to unknown address 0x%08x\n", data, addr);
|
||||
|
@ -444,6 +499,11 @@ void Bus::Write16(uint32_t addr, uint16_t data)
|
|||
*(uint16_t*)&spr[addr - 0x70000000] = data;
|
||||
return;
|
||||
}
|
||||
if (addr < 0x2000000)
|
||||
{
|
||||
*(uint16_t*)&ram[addr] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if (((addr & 0xFF000000) == 0x1A000000) || ((addr & 0xFFF00000) == 0x1F800000))
|
||||
return;
|
||||
|
|
Loading…
Reference in a new issue