project restructure

This commit is contained in:
SimoneN64
2023-08-11 15:16:30 +02:00
parent 74dccb6ac6
commit 79c7b070eb
10 changed files with 1200 additions and 88 deletions

83
CMakeLists.txt Normal file
View File

@@ -0,0 +1,83 @@
cmake_minimum_required(VERSION 3.20)
project(kaizen)
set(BUILD_SHARED_LIBS OFF)
set(CMAKE_CXX_STANDARD 17)
find_package(SDL2 REQUIRED)
find_package(fmt REQUIRED)
find_package(mio REQUIRED)
find_package(nlohmann_json REQUIRED)
option(RAPIDJSON_BUILD_DOC "Build rapidjson documentation." OFF)
option(RAPIDJSON_BUILD_EXAMPLES "Build rapidjson examples." OFF)
option(RAPIDJSON_BUILD_TESTS "Build rapidjson perftests and unittests." OFF)
include_directories(
src
src/utils
src/frontend
src/frontend/imgui
src/backend
src/backend/core
src/backend/core/mmio
src/backend/core/registers
src/backend/core/rsp
external
external/xbyak
external/parallel-rdp
external/parallel-rdp/parallel-rdp-standalone/parallel-rdp
external/parallel-rdp/parallel-rdp-standalone/volk
external/parallel-rdp/parallel-rdp-standalone/spirv-cross
external/parallel-rdp/parallel-rdp-standalone/vulkan
external/parallel-rdp/parallel-rdp-standalone/vulkan-headers/include
external/parallel-rdp/parallel-rdp-standalone/util
external/nativefiledialog-extended/src/include
external/imgui/imgui
external/imgui/imgui/backends
external/discord-rpc/include
external/unarr
${SDL2_INCLUDE_DIRS}
)
add_compile_definitions(SIMD_SUPPORT)
if(WIN32)
add_definitions(-DNOMINMAX)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif ()
add_compile_options(-mssse3 -msse4.1)
if(${CMAKE_BUILD_TYPE} MATCHES Debug)
#add_compile_options(-fsanitize=address -fsanitize=undefined)
#add_link_options(-fsanitize=address -fsanitize=undefined)
endif()
add_subdirectory(src/frontend)
add_subdirectory(src/frontend/imgui)
add_subdirectory(src/backend)
add_subdirectory(src/backend/netplay)
add_subdirectory(src/backend/core)
add_subdirectory(src/backend/core/interpreter)
add_subdirectory(src/backend/core/mem)
add_subdirectory(src/backend/core/mmio)
add_subdirectory(src/backend/core/registers)
add_subdirectory(src/backend/core/rsp)
add_subdirectory(external/discord-rpc)
add_subdirectory(external/imgui)
add_subdirectory(external/nativefiledialog-extended)
add_subdirectory(external/parallel-rdp)
add_subdirectory(external/unarr)
add_executable(kaizen main.cpp)
if(MSVC)
target_compile_options(parallel-rdp PUBLIC /EHa)
endif()
file(COPY ${PROJECT_SOURCE_DIR}/resources/ DESTINATION ${PROJECT_BINARY_DIR}/resources/)
file(REMOVE
${PROJECT_BINARY_DIR}/resources/mario.png
${PROJECT_BINARY_DIR}/resources/shader.frag
${PROJECT_BINARY_DIR}/resources/shader.vert)
target_link_libraries(kaizen PUBLIC frontend frontend-imgui
discord-rpc imgui nfd parallel-rdp backend fmt::fmt mio::mio nlohmann_json::nlohmann_json core registers interpreter mem unarr mmio rsp SDL2::SDL2main SDL2::SDL2)

View File

@@ -1,83 +0,0 @@
cmake_minimum_required(VERSION 3.20)
project(kaizen)
set(BUILD_SHARED_LIBS OFF)
set(CMAKE_CXX_STANDARD 17)
find_package(SDL2 REQUIRED)
find_package(fmt REQUIRED)
find_package(mio REQUIRED)
find_package(nlohmann_json REQUIRED)
option(RAPIDJSON_BUILD_DOC "Build rapidjson documentation." OFF)
option(RAPIDJSON_BUILD_EXAMPLES "Build rapidjson examples." OFF)
option(RAPIDJSON_BUILD_TESTS "Build rapidjson perftests and unittests." OFF)
include_directories(
.
utils
frontend
frontend/imgui
backend
backend/core
backend/core/mmio
backend/core/registers
backend/core/rsp
../external
../external/xbyak
../external/parallel-rdp
../external/parallel-rdp/parallel-rdp-standalone/parallel-rdp
../external/parallel-rdp/parallel-rdp-standalone/volk
../external/parallel-rdp/parallel-rdp-standalone/spirv-cross
../external/parallel-rdp/parallel-rdp-standalone/vulkan
../external/parallel-rdp/parallel-rdp-standalone/vulkan-headers/include
../external/parallel-rdp/parallel-rdp-standalone/util
../external/nativefiledialog-extended/src/include
../external/imgui/imgui
../external/imgui/imgui/backends
../external/discord-rpc/include
../external/unarr
${SDL2_INCLUDE_DIRS}
)
add_compile_definitions(SIMD_SUPPORT)
if(WIN32)
add_definitions(-DNOMINMAX)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif ()
add_compile_options(-mssse3 -msse4.1)
if(${CMAKE_BUILD_TYPE} MATCHES Debug)
#add_compile_options(-fsanitize=address -fsanitize=undefined)
#add_link_options(-fsanitize=address -fsanitize=undefined)
endif()
add_subdirectory(frontend)
add_subdirectory(frontend/imgui)
add_subdirectory(backend)
add_subdirectory(backend/netplay)
add_subdirectory(backend/core)
add_subdirectory(backend/core/interpreter)
add_subdirectory(backend/core/mem)
add_subdirectory(backend/core/mmio)
add_subdirectory(backend/core/registers)
add_subdirectory(backend/core/rsp)
add_subdirectory(../external/discord-rpc discord-rpc)
add_subdirectory(../external/imgui imgui)
add_subdirectory(../external/nativefiledialog-extended nfd)
add_subdirectory(../external/parallel-rdp parallel-rdp)
add_subdirectory(../external/unarr unarr)
add_executable(kaizen main.cpp)
if(MSVC)
target_compile_options(parallel-rdp PUBLIC /EHa)
endif()
file(COPY ${PROJECT_SOURCE_DIR}/../resources/ DESTINATION ${PROJECT_BINARY_DIR}/resources/)
file(REMOVE
${PROJECT_BINARY_DIR}/resources/mario.png
${PROJECT_BINARY_DIR}/resources/shader.frag
${PROJECT_BINARY_DIR}/resources/shader.vert)
target_link_libraries(kaizen PUBLIC frontend frontend-imgui
discord-rpc imgui nfd parallel-rdp backend fmt::fmt mio::mio nlohmann_json::nlohmann_json core registers interpreter mem unarr mmio rsp SDL2::SDL2main SDL2::SDL2)

View File

@@ -21,6 +21,7 @@ private:
void cop2Decode(u32); void cop2Decode(u32);
void special(u32); void special(u32);
void regimm(u32); void regimm(u32);
void Emit(u32);
void add(u32); void add(u32);
void addu(u32); void addu(u32);
void addi(u32); void addi(u32);
@@ -114,4 +115,4 @@ private:
void ctc2(u32); void ctc2(u32);
void cfc2(u32); void cfc2(u32);
}; };
} }

View File

@@ -0,0 +1,175 @@
#include <core/JIT.hpp>
#include <log.hpp>
namespace n64 {
void JIT::special(u32 instr) {
u8 mask = (instr & 0x3F);
// 00rr_rccc
switch (mask) { // TODO: named constants for clearer code
case 0:
if (instr != 0) {
sll(instr);
}
break;
case 0x02: srl(instr); break;
case 0x03: sra(instr); break;
case 0x04: sllv(instr); break;
case 0x06: srlv(instr); break;
case 0x07: srav(instr); break;
case 0x08: jr(instr); break;
case 0x09: jalr(instr); break;
case 0x0C: FireException(regs, ExceptionCode::Syscall, 0, true); break;
case 0x0D: FireException(regs, ExceptionCode::Breakpoint, 0, true); break;
case 0x0F: break; // SYNC
case 0x10: mfhi(instr); break;
case 0x11: mthi(instr); break;
case 0x12: mflo(instr); break;
case 0x13: mtlo(instr); break;
case 0x14: dsllv(instr); break;
case 0x16: dsrlv(instr); break;
case 0x17: dsrav(instr); break;
case 0x18: mult(instr); break;
case 0x19: multu(instr); break;
case 0x1A: div(instr); break;
case 0x1B: divu(instr); break;
case 0x1C: dmult(instr); break;
case 0x1D: dmultu(instr); break;
case 0x1E: ddiv(instr); break;
case 0x1F: ddivu(instr); break;
case 0x20: add(instr); break;
case 0x21: addu(instr); break;
case 0x22: sub(instr); break;
case 0x23: subu(instr); break;
case 0x24: and_(instr); break;
case 0x25: or_(instr); break;
case 0x26: xor_(instr); break;
case 0x27: nor(instr); break;
case 0x2A: slt(instr); break;
case 0x2B: sltu(instr); break;
case 0x2C: dadd(instr); break;
case 0x2D: daddu(instr); break;
case 0x2E: dsub(instr); break;
case 0x2F: dsubu(instr); break;
case 0x30: trap(regs.gpr[RS(instr)] >= regs.gpr[RT(instr)]); break;
case 0x31: trap((u64)regs.gpr[RS(instr)] >= (u64)regs.gpr[RT(instr)]); break;
case 0x32: trap(regs.gpr[RS(instr)] < regs.gpr[RT(instr)]); break;
case 0x33: trap((u64)regs.gpr[RS(instr)] < (u64)regs.gpr[RT(instr)]); break;
case 0x34: trap(regs.gpr[RS(instr)] == regs.gpr[RT(instr)]); break;
case 0x36: trap(regs.gpr[RS(instr)] != regs.gpr[RT(instr)]); break;
case 0x38: dsll(instr); break;
case 0x3A: dsrl(instr); break;
case 0x3B: dsra(instr); break;
case 0x3C: dsll32(instr); break;
case 0x3E: dsrl32(instr); break;
case 0x3F: dsra32(instr); break;
default:
Util::panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})", (mask >> 3) & 7, mask & 7, instr, (u64)regs.oldPC);
}
}
void JIT::regimm(u32 instr) {
u8 mask = ((instr >> 16) & 0x1F);
// 000r_rccc
switch (mask) { // TODO: named constants for clearer code
case 0x00: b(instr, regs.gpr[RS(instr)] < 0); break;
case 0x01: b(instr, regs.gpr[RS(instr)] >= 0); break;
case 0x02: bl(instr, regs.gpr[RS(instr)] < 0); break;
case 0x03: bl(instr, regs.gpr[RS(instr)] >= 0); break;
case 0x08: trap(regs.gpr[RS(instr)] >= s64(s16(instr))); break;
case 0x09: trap(u64(regs.gpr[RS(instr)]) >= u64(s64(s16(instr)))); break;
case 0x0A: trap(regs.gpr[RS(instr)] < s64(s16(instr))); break;
case 0x0B: trap(u64(regs.gpr[RS(instr)]) < u64(s64(s16(instr)))); break;
case 0x0C: trap(regs.gpr[RS(instr)] == s64(s16(instr))); break;
case 0x0E: trap(regs.gpr[RS(instr)] != s64(s16(instr))); break;
case 0x10: blink(instr, regs.gpr[RS(instr)] < 0); break;
case 0x11: blink(instr, regs.gpr[RS(instr)] >= 0); break;
case 0x12: bllink(instr, regs.gpr[RS(instr)] < 0); break;
case 0x13: bllink(instr, regs.gpr[RS(instr)] >= 0); break;
default:
Util::panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})", (mask >> 3) & 3, mask & 7, instr, (u64)regs.oldPC);
}
}
void JIT::cop2Decode(u32 instr) {
if(!regs.cop0.status.cu2) {
FireException(regs, ExceptionCode::CoprocessorUnusable, 2, true);
return;
}
switch(RS(instr)) {
case 0x00: mfc2(instr); break;
case 0x01: dmfc2(instr); break;
case 0x02: cfc2(instr); break;
case 0x04: mtc2(instr); break;
case 0x05: dmtc2(instr); break;
case 0x06: ctc2(instr); break;
default:
FireException(regs, ExceptionCode::ReservedInstruction, 2, true);
}
}
void JIT::Emit(u32 instr) {
u8 mask = (instr >> 26) & 0x3f;
// 00rr_rccc
switch(mask) { // TODO: named constants for clearer code
case 0x00: special(instr); break;
case 0x01: regimm(instr); break;
case 0x02: j(instr); break;
case 0x03: jal(instr); break;
case 0x04: b(instr, regs.gpr[RS(instr)] == regs.gpr[RT(instr)]); break;
case 0x05: {
//fmt::print("RS: {:016X}, RT: {:016X}", (u64)regs.gpr[RS(instr)], (u64)regs.gpr[RT(instr)]);
b(instr, regs.gpr[RS(instr)] != regs.gpr[RT(instr)]);
} break;
case 0x06: b(instr, regs.gpr[RS(instr)] <= 0); break;
case 0x07: b(instr, regs.gpr[RS(instr)] > 0); break;
case 0x08: addi(instr); break;
case 0x09: addiu(instr); break;
case 0x0A: slti(instr); break;
case 0x0B: sltiu(instr); break;
case 0x0C: andi(instr); break;
case 0x0D: ori(instr); break;
case 0x0E: xori(instr); break;
case 0x0F: lui(instr); break;
case 0x10: regs.cop0.decode(regs, instr); break;
case 0x11: regs.cop1.decode(regs, *this, instr); break;
case 0x12: cop2Decode(instr); break;
case 0x14: bl(instr, regs.gpr[RS(instr)] == regs.gpr[RT(instr)]); break;
case 0x15: bl(instr, regs.gpr[RS(instr)] != regs.gpr[RT(instr)]); break;
case 0x16: bl(instr, regs.gpr[RS(instr)] <= 0); break;
case 0x17: bl(instr, regs.gpr[RS(instr)] > 0); break;
case 0x18: daddi(instr); break;
case 0x19: daddiu(instr); break;
case 0x1A: ldl(instr); break;
case 0x1B: ldr(instr); break;
case 0x1F: FireException(regs, ExceptionCode::ReservedInstruction, 0, true); break;
case 0x20: lb(instr); break;
case 0x21: lh(instr); break;
case 0x22: lwl(instr); break;
case 0x23: lw(instr); break;
case 0x24: lbu(instr); break;
case 0x25: lhu(instr); break;
case 0x26: lwr(instr); break;
case 0x27: lwu(instr); break;
case 0x28: sb(instr); break;
case 0x29: sh(instr); break;
case 0x2A: swl(instr); break;
case 0x2B: sw(instr); break;
case 0x2C: sdl(instr); break;
case 0x2D: sdr(instr); break;
case 0x2E: swr(instr); break;
case 0x2F: break; // CACHE
case 0x30: ll(instr); break;
case 0x31: regs.cop1.lwc1(regs, mem, instr); break;
case 0x34: lld(instr); break;
case 0x35: regs.cop1.ldc1(regs, mem, instr); break;
case 0x37: ld(instr); break;
case 0x38: sc(instr); break;
case 0x39: regs.cop1.swc1(regs, mem, instr); break;
case 0x3C: scd(instr); break;
case 0x3D: regs.cop1.sdc1(regs, mem, instr); break;
case 0x3F: sd(instr); break;
default:
Util::panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", mask, instr, (u64)regs.oldPC);
}
}
}

View File

@@ -0,0 +1,933 @@
#include <core/Interpreter.hpp>
#define check_address_error(mask, vaddr) (((!regs.cop0.is_64bit_addressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
#define check_signed_overflow(op1, op2, res) (((~((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1)
#define check_signed_underflow(op1, op2, res) (((((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1)
namespace n64 {
void Interpreter::add(u32 instr) {
u32 rs = (s32)regs.gpr[RS(instr)];
u32 rt = (s32)regs.gpr[RT(instr)];
u32 result = rs + rt;
if(check_signed_overflow(rs, rt, result)) {
FireException(regs, ExceptionCode::Overflow, 0, true);
} else {
if(likely(RD(instr) != 0)) {
regs.gpr[RD(instr)] = s32(result);
}
}
}
void Interpreter::addu(u32 instr) {
if(likely(RD(instr) != 0)) {
s32 rs = (s32)regs.gpr[RS(instr)];
s32 rt = (s32)regs.gpr[RT(instr)];
s32 result = rs + rt;
regs.gpr[RD(instr)] = result;
}
}
void Interpreter::addi(u32 instr) {
u32 rs = regs.gpr[RS(instr)];
u32 imm = s32(s16(instr));
u32 result = rs + imm;
if(check_signed_overflow(rs, imm, result)) {
FireException(regs, ExceptionCode::Overflow, 0, true);
} else {
regs.gpr[RT(instr)] = s32(result);
}
}
void Interpreter::addiu(u32 instr) {
s32 rs = (s32)regs.gpr[RS(instr)];
s16 imm = (s16)(instr);
s32 result = rs + imm;
regs.gpr[RT(instr)] = result;
}
void Interpreter::dadd(u32 instr) {
u64 rs = regs.gpr[RS(instr)];
u64 rt = regs.gpr[RT(instr)];
u64 result = rt + rs;
if(check_signed_overflow(rs, rt, result)) {
FireException(regs, ExceptionCode::Overflow, 0, true);
} else {
if(likely(RD(instr) != 0)) {
regs.gpr[RD(instr)] = result;
}
}
}
void Interpreter::daddu(u32 instr) {
if(likely(RD(instr) != 0)) {
s64 rs = regs.gpr[RS(instr)];
s64 rt = regs.gpr[RT(instr)];
regs.gpr[RD(instr)] = rs + rt;
}
}
void Interpreter::daddi(u32 instr) {
u64 imm = s64(s16(instr));
u64 rs = regs.gpr[RS(instr)];
u64 result = imm + rs;
if(check_signed_overflow(rs, imm, result)) {
FireException(regs, ExceptionCode::Overflow, 0, true);
} else {
regs.gpr[RT(instr)] = result;
}
}
void Interpreter::daddiu(u32 instr) {
s16 imm = (s16)(instr);
s64 rs = regs.gpr[RS(instr)];
regs.gpr[RT(instr)] = rs + imm;
}
void Interpreter::div(u32 instr) {
s64 dividend = (s32)regs.gpr[RS(instr)];
s64 divisor = (s32)regs.gpr[RT(instr)];
if(divisor == 0) {
regs.hi = dividend;
if(dividend >= 0) {
regs.lo = s64(-1);
} else {
regs.lo = s64(1);
}
} else {
s32 quotient = dividend / divisor;
s32 remainder = dividend % divisor;
regs.lo = quotient;
regs.hi = remainder;
}
}
void Interpreter::divu(u32 instr) {
u32 dividend = regs.gpr[RS(instr)];
u32 divisor = regs.gpr[RT(instr)];
if(divisor == 0) {
regs.lo = -1;
regs.hi = (s32)dividend;
} else {
s32 quotient = (s32)(dividend / divisor);
s32 remainder = (s32)(dividend % divisor);
regs.lo = quotient;
regs.hi = remainder;
}
}
void Interpreter::ddiv(u32 instr) {
s64 dividend = regs.gpr[RS(instr)];
s64 divisor = regs.gpr[RT(instr)];
if (dividend == 0x8000000000000000 && divisor == 0xFFFFFFFFFFFFFFFF) {
regs.lo = dividend;
regs.hi = 0;
} else if(divisor == 0) {
regs.hi = dividend;
if(dividend >= 0) {
regs.lo = -1;
} else {
regs.lo = 1;
}
} else {
s64 quotient = dividend / divisor;
s64 remainder = dividend % divisor;
regs.lo = quotient;
regs.hi = remainder;
}
}
void Interpreter::ddivu(u32 instr) {
u64 dividend = regs.gpr[RS(instr)];
u64 divisor = regs.gpr[RT(instr)];
if(divisor == 0) {
regs.lo = -1;
regs.hi = (s64)dividend;
} else {
u64 quotient = dividend / divisor;
u64 remainder = dividend % divisor;
regs.lo = (s64)quotient;
regs.hi = (s64)remainder;
}
}
void Interpreter::branch(bool cond, s64 address) {
regs.delaySlot = true;
if (cond) {
regs.nextPC = address;
}
}
void Interpreter::branch_likely(bool cond, s64 address) {
if (cond) {
regs.delaySlot = true;
regs.nextPC = address;
} else {
regs.SetPC64(regs.nextPC);
}
}
void Interpreter::b(u32 instr, bool cond) {
s16 imm = instr;
s64 offset = u64((s64)imm) << 2;
s64 address = regs.pc + offset;
branch(cond, address);
}
void Interpreter::blink(u32 instr, bool cond) {
regs.gpr[31] = regs.nextPC;
s16 imm = instr;
s64 offset = u64((s64)imm) << 2;
s64 address = regs.pc + offset;
branch(cond, address);
}
void Interpreter::bl(u32 instr, bool cond) {
s16 imm = instr;
s64 offset = u64((s64)imm) << 2;
s64 address = regs.pc + offset;
branch_likely(cond, address);
}
void Interpreter::bllink(u32 instr, bool cond) {
regs.gpr[31] = regs.nextPC;
s16 imm = instr;
s64 offset = u64((s64)imm) << 2;
s64 address = regs.pc + offset;
branch_likely(cond, address);
}
void Interpreter::lui(u32 instr) {
u64 val = s64((s16)instr);
val <<= 16;
regs.gpr[RT(instr)] = val;
}
void Interpreter::lb(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr = 0;
if(!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else {
regs.gpr[RT(instr)] = (s8)mem.Read8(regs, paddr);
}
}
void Interpreter::lh(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr;
if ((address & 0b1) > 0) {
HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorLoad, 0, true);
return;
}
u32 paddr = 0;
if(!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else {
regs.gpr[RT(instr)] = (s16)mem.Read16(regs, paddr);
}
}
void Interpreter::lw(u32 instr) {
s16 offset = instr;
u64 address = regs.gpr[RS(instr)] + offset;
if (check_address_error(0b11, address)) {
HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorLoad, 0, true);
return;
}
u32 physical = 0;
if (!MapVAddr(regs, LOAD, address, physical)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else {
regs.gpr[RT(instr)] = (s32)mem.Read32(regs, physical);
}
}
void Interpreter::ll(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 physical;
if (!MapVAddr(regs, LOAD, address, physical)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else {
if ((address & 0b11) > 0) {
FireException(regs, ExceptionCode::AddressErrorLoad, 0, true);
return;
} else {
regs.gpr[RT(instr)] = (s32)mem.Read32(regs, physical);
}
}
regs.cop0.llbit = true;
regs.cop0.LLAddr = physical >> 4;
}
void Interpreter::lwl(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr = 0;
if(!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else {
u32 shift = 8 * ((address ^ 0) & 3);
u32 mask = 0xFFFFFFFF << shift;
u32 data = mem.Read32(regs, paddr & ~3);
s32 result = s32((regs.gpr[RT(instr)] & ~mask) | (data << shift));
regs.gpr[RT(instr)] = result;
}
}
void Interpreter::lwr(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr = 0;
if(!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else {
u32 shift = 8 * ((address ^ 3) & 3);
u32 mask = 0xFFFFFFFF >> shift;
u32 data = mem.Read32(regs, paddr & ~3);
s32 result = s32((regs.gpr[RT(instr)] & ~mask) | (data >> shift));
regs.gpr[RT(instr)] = result;
}
}
void Interpreter::ld(u32 instr) {
s64 address = regs.gpr[RS(instr)] + (s16)instr;
if (check_address_error(0b111, address)) {
HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorLoad, 0, true);
return;
}
u32 paddr = 0;
if(!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else {
s64 value = mem.Read64(regs, paddr);
regs.gpr[RT(instr)] = value;
}
}
void Interpreter::lld(u32 instr) {
if (!regs.cop0.is_64bit_addressing && !regs.cop0.kernel_mode) {
FireException(regs, ExceptionCode::ReservedInstruction, 0, true);
return;
}
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr;
if (!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else {
if ((address & 0b111) > 0) {
FireException(regs, ExceptionCode::AddressErrorLoad, 0, true);
} else {
regs.gpr[RT(instr)] = mem.Read64(regs, paddr);
regs.cop0.llbit = true;
regs.cop0.LLAddr = paddr >> 4;
}
}
}
void Interpreter::ldl(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr = 0;
if (!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else {
s32 shift = 8 * ((address ^ 0) & 7);
u64 mask = 0xFFFFFFFFFFFFFFFF << shift;
u64 data = mem.Read64(regs, paddr & ~7);
s64 result = (s64) ((regs.gpr[RT(instr)] & ~mask) | (data << shift));
regs.gpr[RT(instr)] = result;
}
}
void Interpreter::ldr(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr;
if (!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else {
s32 shift = 8 * ((address ^ 7) & 7);
u64 mask = 0xFFFFFFFFFFFFFFFF >> shift;
u64 data = mem.Read64(regs, paddr & ~7);
s64 result = (s64) ((regs.gpr[RT(instr)] & ~mask) | (data >> shift));
regs.gpr[RT(instr)] = result;
}
}
void Interpreter::lbu(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr;
if (!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else {
u8 value = mem.Read8(regs, paddr);
regs.gpr[RT(instr)] = value;
}
}
void Interpreter::lhu(u32 instr) {
s64 address = regs.gpr[RS(instr)] + (s16)instr;
if ((address & 0b1) > 0) {
HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorLoad, 0, true);
return;
}
u32 paddr;
if (!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else {
u16 value = mem.Read16(regs, paddr);
regs.gpr[RT(instr)] = value;
}
}
void Interpreter::lwu(u32 instr) {
s64 address = regs.gpr[RS(instr)] + (s16)instr;
if ((address & 0b11) > 0) {
HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorLoad, 0, true);
return;
}
u32 paddr;
if (!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, true);
} else {
u32 value = mem.Read32(regs, paddr);
regs.gpr[RT(instr)] = value;
}
}
void Interpreter::sb(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr;
if (!MapVAddr(regs, STORE, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else {
mem.Write8(regs, paddr, regs.gpr[RT(instr)]);
}
}
void Interpreter::sc(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr;
if ((address & 0b11) > 0) {
FireException(regs, ExceptionCode::AddressErrorStore, 0, true);
return;
}
if(regs.cop0.llbit) {
regs.cop0.llbit = false;
u32 paddr = 0;
if(!MapVAddr(regs, STORE, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else {
mem.Write32(regs, paddr, regs.gpr[RT(instr)]);
regs.gpr[RT(instr)] = 1;
}
} else {
regs.gpr[RT(instr)] = 0;
}
}
void Interpreter::scd(u32 instr) {
if (!regs.cop0.is_64bit_addressing && !regs.cop0.kernel_mode) {
FireException(regs, ExceptionCode::ReservedInstruction, 0, true);
return;
}
s64 address = regs.gpr[RS(instr)] + (s16)instr;
if ((address & 0b111) > 0) {
HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorStore, 0, true);
return;
}
if(regs.cop0.llbit) {
regs.cop0.llbit = false;
u32 paddr = 0;
if(!MapVAddr(regs, STORE, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else {
mem.Write32(regs, paddr, regs.gpr[RT(instr)]);
regs.gpr[RT(instr)] = 1;
}
} else {
regs.gpr[RT(instr)] = 0;
}
}
void Interpreter::sh(u32 instr) {
s64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 physical;
if(!MapVAddr(regs, STORE, address, physical)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else {
mem.Write16(regs, physical, regs.gpr[RT(instr)]);
}
}
void Interpreter::sw(u32 instr) {
s16 offset = instr;
u64 address = regs.gpr[RS(instr)] + offset;
if (check_address_error(0b11, address)) {
HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorStore, 0, true);
return;
}
u32 physical;
if(!MapVAddr(regs, STORE, address, physical)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else {
mem.Write32(regs, physical, regs.gpr[RT(instr)]);
}
}
void Interpreter::sd(u32 instr) {
s64 address = regs.gpr[RS(instr)] + (s16)instr;
if (check_address_error(0b111, address)) {
HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorStore, 0, true);
return;
}
u32 physical;
if(!MapVAddr(regs, STORE, address, physical)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else {
mem.Write64(regs, physical, regs.gpr[RT(instr)]);
}
}
void Interpreter::sdl(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr;
if (!MapVAddr(regs, STORE, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else {
s32 shift = 8 * ((address ^ 0) & 7);
u64 mask = 0xFFFFFFFFFFFFFFFF >> shift;
u64 data = mem.Read64(regs, paddr & ~7);
u64 rt = regs.gpr[RT(instr)];
mem.Write64(regs, paddr & ~7, (data & ~mask) | (rt >> shift));
}
}
void Interpreter::sdr(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr;
if (!MapVAddr(regs, STORE, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else {
s32 shift = 8 * ((address ^ 7) & 7);
u64 mask = 0xFFFFFFFFFFFFFFFF << shift;
u64 data = mem.Read64(regs, paddr & ~7);
u64 rt = regs.gpr[RT(instr)];
mem.Write64(regs, paddr & ~7, (data & ~mask) | (rt << shift));
}
}
void Interpreter::swl(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr;
if (!MapVAddr(regs, STORE, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else {
u32 shift = 8 * ((address ^ 0) & 3);
u32 mask = 0xFFFFFFFF >> shift;
u32 data = mem.Read32(regs, paddr & ~3);
u32 rt = regs.gpr[RT(instr)];
mem.Write32(regs, paddr & ~3, (data & ~mask) | (rt >> shift));
}
}
void Interpreter::swr(u32 instr) {
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr;
if (!MapVAddr(regs, STORE, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, true);
} else {
u32 shift = 8 * ((address ^ 3) & 3);
u32 mask = 0xFFFFFFFF << shift;
u32 data = mem.Read32(regs, paddr & ~3);
u32 rt = regs.gpr[RT(instr)];
mem.Write32(regs, paddr & ~3, (data & ~mask) | (rt << shift));
}
}
void Interpreter::ori(u32 instr) {
s64 imm = (u16)instr;
s64 result = imm | regs.gpr[RS(instr)];
regs.gpr[RT(instr)] = result;
}
void Interpreter::or_(u32 instr) {
if(likely(RD(instr) != 0)) {
regs.gpr[RD(instr)] = regs.gpr[RS(instr)] | regs.gpr[RT(instr)];
}
}
void Interpreter::nor(u32 instr) {
if(likely(RD(instr) != 0)) {
regs.gpr[RD(instr)] = ~(regs.gpr[RS(instr)] | regs.gpr[RT(instr)]);
}
}
void Interpreter::j(u32 instr) {
s32 target = (instr & 0x3ffffff) << 2;
s64 address = (regs.oldPC & ~0xfffffff) | target;
branch(true, address);
}
void Interpreter::jal(u32 instr) {
regs.gpr[31] = regs.nextPC;
j(instr);
}
void Interpreter::jalr(u32 instr) {
branch(true, regs.gpr[RS(instr)]);
if(likely(RD(instr) != 0)) {
regs.gpr[RD(instr)] = regs.pc + 4;
}
}
void Interpreter::slti(u32 instr) {
s16 imm = instr;
regs.gpr[RT(instr)] = regs.gpr[RS(instr)] < imm;
}
void Interpreter::sltiu(u32 instr) {
s16 imm = instr;
regs.gpr[RT(instr)] = (u64)regs.gpr[RS(instr)] < imm;
}
void Interpreter::slt(u32 instr) {
if(likely(RD(instr) != 0)) {
regs.gpr[RD(instr)] = regs.gpr[RS(instr)] < regs.gpr[RT(instr)];
}
}
void Interpreter::sltu(u32 instr) {
if(likely(RD(instr) != 0)) {
regs.gpr[RD(instr)] = (u64) regs.gpr[RS(instr)] < (u64) regs.gpr[RT(instr)];
}
}
void Interpreter::xori(u32 instr) {
s64 imm = (u16)instr;
regs.gpr[RT(instr)] = regs.gpr[RS(instr)] ^ imm;
}
void Interpreter::xor_(u32 instr) {
if(likely(RD(instr) != 0)) {
regs.gpr[RD(instr)] = regs.gpr[RT(instr)] ^ regs.gpr[RS(instr)];
}
}
void Interpreter::andi(u32 instr) {
s64 imm = (u16)instr;
regs.gpr[RT(instr)] = regs.gpr[RS(instr)] & imm;
}
void Interpreter::and_(u32 instr) {
if(likely(RD(instr) != 0)) {
regs.gpr[RD(instr)] = regs.gpr[RS(instr)] & regs.gpr[RT(instr)];
}
}
void Interpreter::sll(u32 instr) {
if(likely(RD(instr) != 0)) {
u8 sa = ((instr >> 6) & 0x1f);
s32 result = regs.gpr[RT(instr)] << sa;
regs.gpr[RD(instr)] = (s64) result;
}
}
void Interpreter::sllv(u32 instr) {
if(likely(RD(instr) != 0)) {
u8 sa = (regs.gpr[RS(instr)]) & 0x1F;
u32 rt = regs.gpr[RT(instr)];
s32 result = rt << sa;
regs.gpr[RD(instr)] = (s64) result;
}
}
void Interpreter::dsll32(u32 instr) {
if(likely(RD(instr) != 0)) {
u8 sa = ((instr >> 6) & 0x1f);
s64 result = regs.gpr[RT(instr)] << (sa + 32);
regs.gpr[RD(instr)] = result;
}
}
void Interpreter::dsll(u32 instr) {
if(likely(RD(instr) != 0)) {
u8 sa = ((instr >> 6) & 0x1f);
s64 result = regs.gpr[RT(instr)] << sa;
regs.gpr[RD(instr)] = result;
}
}
void Interpreter::dsllv(u32 instr) {
if(likely(RD(instr) != 0)) {
s64 sa = regs.gpr[RS(instr)] & 63;
s64 result = regs.gpr[RT(instr)] << sa;
regs.gpr[RD(instr)] = result;
}
}
void Interpreter::srl(u32 instr) {
if(likely(RD(instr) != 0)) {
u32 rt = regs.gpr[RT(instr)];
u8 sa = ((instr >> 6) & 0x1f);
u32 result = rt >> sa;
regs.gpr[RD(instr)] = (s32) result;
}
}
void Interpreter::srlv(u32 instr) {
if(likely(RD(instr) != 0)) {
u8 sa = (regs.gpr[RS(instr)] & 0x1F);
u32 rt = regs.gpr[RT(instr)];
s32 result = rt >> sa;
regs.gpr[RD(instr)] = (s64) result;
}
}
void Interpreter::dsrl(u32 instr) {
if(likely(RD(instr) != 0)) {
u64 rt = regs.gpr[RT(instr)];
u8 sa = ((instr >> 6) & 0x1f);
u64 result = rt >> sa;
regs.gpr[RD(instr)] = s64(result);
}
}
void Interpreter::dsrlv(u32 instr) {
if(likely(RD(instr) != 0)) {
u8 amount = (regs.gpr[RS(instr)] & 63);
u64 rt = regs.gpr[RT(instr)];
u64 result = rt >> amount;
regs.gpr[RD(instr)] = s64(result);
}
}
void Interpreter::dsrl32(u32 instr) {
if(likely(RD(instr) != 0)) {
u64 rt = regs.gpr[RT(instr)];
u8 sa = ((instr >> 6) & 0x1f);
u64 result = rt >> (sa + 32);
regs.gpr[RD(instr)] = s64(result);
}
}
void Interpreter::sra(u32 instr) {
if(likely(RD(instr) != 0)) {
s64 rt = regs.gpr[RT(instr)];
u8 sa = ((instr >> 6) & 0x1f);
s32 result = rt >> sa;
regs.gpr[RD(instr)] = result;
}
}
void Interpreter::srav(u32 instr) {
s64 rt = regs.gpr[RT(instr)];
if(likely(RD(instr) != 0)) {
s64 rs = regs.gpr[RS(instr)];
u8 sa = rs & 0x1f;
s32 result = rt >> sa;
regs.gpr[RD(instr)] = result;
}
}
void Interpreter::dsra(u32 instr) {
if(likely(RD(instr) != 0)) {
s64 rt = regs.gpr[RT(instr)];
u8 sa = ((instr >> 6) & 0x1f);
s64 result = rt >> sa;
regs.gpr[RD(instr)] = result;
}
}
void Interpreter::dsrav(u32 instr) {
if(likely(RD(instr) != 0)) {
s64 rt = regs.gpr[RT(instr)];
s64 rs = regs.gpr[RS(instr)];
s64 sa = rs & 63;
s64 result = rt >> sa;
regs.gpr[RD(instr)] = result;
}
}
void Interpreter::dsra32(u32 instr) {
if(likely(RD(instr) != 0)) {
s64 rt = regs.gpr[RT(instr)];
u8 sa = ((instr >> 6) & 0x1f);
s64 result = rt >> (sa + 32);
regs.gpr[RD(instr)] = result;
}
}
void Interpreter::jr(u32 instr) {
s64 address = regs.gpr[RS(instr)];
branch(true, address);
}
void Interpreter::dsub(u32 instr) {
s64 rt = regs.gpr[RT(instr)];
s64 rs = regs.gpr[RS(instr)];
s64 result = rs - rt;
if(check_signed_underflow(rs, rt, result)) {
FireException(regs, ExceptionCode::Overflow, 0, true);
} else {
if(likely(RD(instr) != 0)) {
regs.gpr[RD(instr)] = result;
}
}
}
void Interpreter::dsubu(u32 instr) {
if(likely(RD(instr) != 0)) {
u64 rt = regs.gpr[RT(instr)];
u64 rs = regs.gpr[RS(instr)];
u64 result = rs - rt;
regs.gpr[RD(instr)] = s64(result);
}
}
void Interpreter::sub(u32 instr) {
s32 rt = regs.gpr[RT(instr)];
s32 rs = regs.gpr[RS(instr)];
s32 result = rs - rt;
if(check_signed_underflow(rs, rt, result)) {
FireException(regs, ExceptionCode::Overflow, 0, true);
} else {
if(likely(RD(instr) != 0)) {
regs.gpr[RD(instr)] = result;
}
}
}
void Interpreter::subu(u32 instr) {
if(likely(RD(instr) != 0)) {
u32 rt = regs.gpr[RT(instr)];
u32 rs = regs.gpr[RS(instr)];
u32 result = rs - rt;
regs.gpr[RD(instr)] = (s64) ((s32) result);
}
}
void Interpreter::dmultu(u32 instr) {
u64 rt = regs.gpr[RT(instr)];
u64 rs = regs.gpr[RS(instr)];
u128 result = (u128)rt * (u128)rs;
regs.lo = (s64)(result & 0xFFFFFFFFFFFFFFFF);
regs.hi = (s64)(result >> 64);
}
void Interpreter::dmult(u32 instr) {
s64 rt = regs.gpr[RT(instr)];
s64 rs = regs.gpr[RS(instr)];
s128 result = (s128)rt * (s128)rs;
regs.lo = result & 0xFFFFFFFFFFFFFFFF;
regs.hi = result >> 64;
}
void Interpreter::multu(u32 instr) {
u32 rt = regs.gpr[RT(instr)];
u32 rs = regs.gpr[RS(instr)];
u64 result = (u64)rt * (u64)rs;
regs.lo = (s64)((s32)result);
regs.hi = (s64)((s32)(result >> 32));
}
void Interpreter::mult(u32 instr) {
s32 rt = regs.gpr[RT(instr)];
s32 rs = regs.gpr[RS(instr)];
s64 result = (s64)rt * (s64)rs;
regs.lo = (s64)((s32)result);
regs.hi = (s64)((s32)(result >> 32));
}
void Interpreter::mflo(u32 instr) {
if(likely(RD(instr) != 0)) {
regs.gpr[RD(instr)] = regs.lo;
}
}
void Interpreter::mfhi(u32 instr) {
if(likely(RD(instr) != 0)) {
regs.gpr[RD(instr)] = regs.hi;
}
}
void Interpreter::mtlo(u32 instr) {
regs.lo = regs.gpr[RS(instr)];
}
void Interpreter::mthi(u32 instr) {
regs.hi = regs.gpr[RS(instr)];
}
void Interpreter::trap(bool cond) {
if(cond) {
FireException(regs, ExceptionCode::Trap, 0, true);
}
}
void Interpreter::mtc2(u32 instr) {
cop2Latch = regs.gpr[RT(instr)];
}
void Interpreter::mfc2(u32 instr) {
s32 value = cop2Latch;
regs.gpr[RT(instr)] = value;
}
void Interpreter::dmtc2(u32 instr) {
cop2Latch = regs.gpr[RT(instr)];
}
void Interpreter::dmfc2(u32 instr) {
regs.gpr[RT(instr)] = cop2Latch;
}
void Interpreter::ctc2(u32) {
}
void Interpreter::cfc2(u32) {
}
}

View File

@@ -1,4 +1,4 @@
file(GLOB SOURCES *.cpp) file(GLOB_RECURSE SOURCES *.cpp)
file(GLOB HEADERS *.cpp) file(GLOB_RECURSE HEADERS *.cpp)
add_library(registers ${SOURCES} ${HEADERS}) add_library(registers ${SOURCES} ${HEADERS})

View File

@@ -64,7 +64,8 @@ struct Cop1 {
FCR31 fcr31{}; FCR31 fcr31{};
FGR fgr[32]{}; FGR fgr[32]{};
void Reset(); void Reset();
void decode(Registers&, Interpreter&, u32); template <class T> // either JIT or Interpreter
void decode(Registers&, T&, u32);
friend struct Interpreter; friend struct Interpreter;
template <typename T> template <typename T>
@@ -135,6 +136,8 @@ struct Cop1 {
return value; return value;
} }
private: private:
void decodeInterp(Registers&, Interpreter&, u32);
void decodeJIT(Registers&, JIT&, u32);
void absd(Registers&, u32 instr); void absd(Registers&, u32 instr);
void abss(Registers&, u32 instr); void abss(Registers&, u32 instr);
void absw(Registers&, u32 instr); void absw(Registers&, u32 instr);