diff --git a/.gitignore b/.gitignore index 2d8bd687..810f048f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ roms/ .cache/ .vscode/ vgcore.* -*.data \ No newline at end of file +*.data +disasm.txt +log.txt \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1c779575..88cd84f1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,6 +13,15 @@ find_package(fmt REQUIRED) add_executable(natsukashii main.cpp) +if (CMAKE_BUILD_TYPE MATCHES Debug) + set(OPTIMIZATIONS -g + #-fsanitize=address -fsanitize=undefined + ) + #target_link_libraries(natsukashii PUBLIC -fsanitize=address -fsanitize=undefined) +elseif(CMAKE_BUILD_TYPE MATCHES Release) + set(OPTIMIZATIONS -O3) +endif() + target_link_libraries(natsukashii PUBLIC frontend n64 fmt) target_include_directories(natsukashii PUBLIC . ../external) -target_compile_options(natsukashii PUBLIC -Wpedantic -Wimplicit-fallthrough -Wextra -Wall) +target_compile_options(natsukashii PUBLIC ${OPTIMIZATIONS} -Wpedantic -Wimplicit-fallthrough -Wextra -Wall) diff --git a/src/n64/Core.cpp b/src/n64/Core.cpp index a3bf3d0a..29a21dd4 100644 --- a/src/n64/Core.cpp +++ b/src/n64/Core.cpp @@ -43,6 +43,8 @@ void Core::Run(Window& window) { int frameCycles = 0; if(!pause && romLoaded) { for (int i = 0; i < mmio.vi.numHalflines; i++) { + mmio.vi.current = (i << 1) + field; + if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) { InterruptRaise(mmio.mi, cpu.regs, Interrupt::VI); } @@ -54,7 +56,7 @@ void Core::Run(Window& window) { mmio.rsp.Step(mmio.mi, cpu.regs, mmio.rdp); mmio.rsp.Step(mmio.mi, cpu.regs, mmio.rdp); - mmio.ai.Step(mem, cpu.regs, 1); + //mmio.ai.Step(mem, cpu.regs, 1); } cycles -= mmio.vi.cyclesPerHalfline; @@ -67,7 +69,7 @@ void Core::Run(Window& window) { UpdateScreenParallelRdp(*this, window, GetVI()); int missedCycles = N64_CYCLES_PER_FRAME - frameCycles; - mmio.ai.Step(mem, cpu.regs, missedCycles); + //mmio.ai.Step(mem, cpu.regs, missedCycles); } else if(pause && romLoaded) { UpdateScreenParallelRdp(*this, window, GetVI()); } else if(pause && !romLoaded) { diff --git a/src/n64/core/Audio.cpp b/src/n64/core/Audio.cpp index b3ffaa8d..15e10f3d 100644 --- a/src/n64/core/Audio.cpp +++ b/src/n64/core/Audio.cpp @@ -2,7 +2,7 @@ #include #include "util.hpp" -namespace natsukashii::core { +namespace n64 { #define AUDIO_SAMPLE_RATE 48000 #define SYSTEM_SAMPLE_FORMAT AUDIO_F32SYS #define SYSTEM_SAMPLE_SIZE 4 diff --git a/src/n64/core/Audio.hpp b/src/n64/core/Audio.hpp index 3bf44e64..e3878ec0 100644 --- a/src/n64/core/Audio.hpp +++ b/src/n64/core/Audio.hpp @@ -1,7 +1,7 @@ #pragma once #include "common.hpp" -namespace natsukashii::core { +namespace n64 { void PushSample(s16, s16); void InitAudio(); void AdjustSampleRate(int); diff --git a/src/n64/core/Cpu.cpp b/src/n64/core/Cpu.cpp index 7d128a2f..46e9fc27 100644 --- a/src/n64/core/Cpu.cpp +++ b/src/n64/core/Cpu.cpp @@ -41,17 +41,17 @@ void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc) { bool old_exl = regs.cop0.status.exl; if(!regs.cop0.status.exl) { - if(regs.prevDelaySlot) { // TODO: cached value of delay_slot should be used, but Namco Museum breaks! + if(regs.delaySlot) { // TODO: cached value of delay_slot should be used, but Namco Museum breaks! regs.cop0.cause.branchDelay = true; pc -= 4; } else { regs.cop0.cause.branchDelay = false; } + regs.cop0.status.exl = true; regs.cop0.EPC = pc; } - regs.cop0.status.exl = true; regs.cop0.cause.copError = cop; regs.cop0.cause.exceptionCode = code; @@ -59,12 +59,8 @@ void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc) { util::panic("BEV bit set!\n"); } else { switch(code) { - case Interrupt: case TLBModification: - case AddressErrorLoad: case AddressErrorStore: - case InstructionBusError: case DataBusError: - case Syscall: case Breakpoint: - case ReservedInstruction: case CoprocessorUnusable: - case Overflow: case Trap: + case Interrupt ... TLBModification: + case AddressErrorLoad ... Trap: case FloatingPointError: case Watch: regs.SetPC(s64(s32(0x80000180))); break; @@ -91,14 +87,28 @@ inline void HandleInterrupt(Registers& regs) { void Cpu::Step(Mem& mem) { regs.gpr[0] = 0; + CheckCompareInterrupt(mem.mmio.mi, regs); + HandleInterrupt(regs); + regs.prevDelaySlot = regs.delaySlot; regs.delaySlot = false; - CheckCompareInterrupt(mem.mmio.mi, regs); - u32 instruction = mem.Read(regs, regs.pc, regs.pc); - HandleInterrupt(regs); + /*cs_insn* insn; + u8 code[4]{}; + memcpy(code, &instruction, 4); + + u32 pc = regs.pc; + + size_t count = cs_disasm(handle, code, 4, (u64)pc, 0, &insn); + if(count > 0) { + for(int i = 0; i < count; i++) { + fprintf(log, "%s", fmt::format("0x{:016X}\t{}\t{}\n", insn[i].address, insn[i].mnemonic, insn[i].op_str).c_str()); + } + + cs_free(insn, count); + }*/ regs.oldPC = regs.pc; regs.pc = regs.nextPC; diff --git a/src/n64/core/Cpu.hpp b/src/n64/core/Cpu.hpp index 0222d93a..56e3a24b 100644 --- a/src/n64/core/Cpu.hpp +++ b/src/n64/core/Cpu.hpp @@ -6,12 +6,24 @@ namespace n64 { struct Cpu { - Cpu() { Reset(); } - ~Cpu() = default; + Cpu() { + log = fopen("disasm.txt", "w"); + if(cs_open(CS_ARCH_MIPS, CS_MODE_MIPS64, &handle) != CS_ERR_OK) { + util::panic("Could not initialize capstone!\n"); + } + Reset(); + } + + ~Cpu() { + fclose(log); + cs_close(&handle); + } void Reset(); void Step(Mem&); Registers regs; private: + csh handle; + FILE* log; friend struct Cop1; void special(Mem&, u32); diff --git a/src/n64/core/RSP.cpp b/src/n64/core/RSP.cpp index 8670753d..3b6f8234 100644 --- a/src/n64/core/RSP.cpp +++ b/src/n64/core/RSP.cpp @@ -31,13 +31,12 @@ void RSP::Reset() { void RSP::Step(MI& mi, Registers& regs, RDP& rdp) { if(!spStatus.halt) { - util::panic("RSP!\n"); - //gpr[0] = 0; - //u32 instr = util::ReadAccess(imem, pc & IMEM_DSIZE); - //oldPC = pc & 0xFFF; - //pc = nextPC & 0xFFF; - //nextPC += 4; - //Exec(mi, regs, rdp, instr); + gpr[0] = 0; + u32 instr = util::ReadAccess(imem, pc & IMEM_DSIZE); + oldPC = pc & 0xFFF; + pc = nextPC & 0xFFF; + nextPC += 4; + Exec(mi, regs, rdp, instr); } } diff --git a/src/n64/core/cpu/instructions.cpp b/src/n64/core/cpu/instructions.cpp index 07c602f0..ea1e8a8d 100644 --- a/src/n64/core/cpu/instructions.cpp +++ b/src/n64/core/cpu/instructions.cpp @@ -137,7 +137,7 @@ void Cpu::branch_likely(bool cond, s64 address) { if (cond) { regs.nextPC = address; } else { - regs.SetPC(regs.pc + 4); + regs.SetPC(regs.nextPC); } } @@ -580,7 +580,7 @@ void Cpu::dsra32(u32 instr) { void Cpu::jr(u32 instr) { s32 address = regs.gpr[RS(instr)]; if ((address & 3) != 0) { - HandleTLBException(regs, (s64)((s32)address)); + HandleTLBException(regs, address); FireException(regs, ExceptionCode::AddressErrorStore, 0, regs.oldPC); } diff --git a/src/n64/core/cpu/registers/Cop0.cpp b/src/n64/core/cpu/registers/Cop0.cpp index 0d9721d3..68ff10a7 100644 --- a/src/n64/core/cpu/registers/Cop0.cpp +++ b/src/n64/core/cpu/registers/Cop0.cpp @@ -11,7 +11,9 @@ Cop0::Cop0() { void Cop0::Reset() { cause.raw = 0xB000007C; status.raw = 0x241000E0; - PRId = 0x00000B22; + wired = 64; + index = 64; + PRId = 0x00000B00; Config = 0x7006E463; EPC = 0xFFFFFFFFFFFFFFFF; ErrorEPC = 0xFFFFFFFFFFFFFFFF; @@ -54,17 +56,17 @@ u32 Cop0::GetReg32(u8 addr) { u64 Cop0::GetReg64(u8 addr) { switch(addr) { - case 2: return entryLo0.raw; - case 3: return entryLo1.raw; - case 4: return context.raw; - case 8: return badVaddr; - case 10: return entryHi.raw; - case 12: return status.raw; - case 14: return EPC; - case 15: return PRId; - case 17: return LLAddr; - case 20: return xcontext.raw & 0xFFFFFFFFFFFFFFF0; - case 30: return ErrorEPC; + case COP0_REG_ENTRYLO0: return entryLo0.raw; + case COP0_REG_ENTRYLO1: return entryLo1.raw; + case COP0_REG_CONTEXT: return context.raw; + case COP0_REG_BADVADDR: return badVaddr; + case COP0_REG_ENTRYHI: return entryHi.raw; + case COP0_REG_STATUS: return status.raw; + case COP0_REG_EPC: return EPC; + case COP0_REG_PRID: return PRId; + case COP0_REG_LLADDR: return LLAddr; + case COP0_REG_XCONTEXT: return xcontext.raw & 0xFFFFFFFFFFFFFFF0; + case COP0_REG_ERROREPC: return ErrorEPC; default: util::panic("Unsupported word read from COP0 register {}\n", index); } @@ -104,7 +106,9 @@ void Cop0::SetReg32(u8 addr, u32 value) { cause.ip0 = temp.ip0; cause.ip1 = temp.ip1; } break; - case COP0_REG_EPC: EPC = s64(s32(value)); break; + case COP0_REG_EPC: + EPC = s64(s32(value)); + break; case COP0_REG_PRID: break; case COP0_REG_CONFIG: { Config &= ~CONFIG_MASK; @@ -121,8 +125,8 @@ void Cop0::SetReg32(u8 addr, u32 value) { case COP0_REG_TAGLO: TagLo = value; break; case COP0_REG_TAGHI: TagHi = value; break; case COP0_REG_ERROREPC: ErrorEPC = s64(s32(value)); break; - case 7: case 21: case 22: break; - case 23: case 24: case 25: break; + case 7: case 21: case 22: + case 23: case 24: case 25: case 31: break; default: util::panic("Unsupported word read from COP0 register {}\n", index); diff --git a/src/n64/core/cpu/registers/Cop1.cpp b/src/n64/core/cpu/registers/Cop1.cpp index 05c8b77d..f9b972ef 100644 --- a/src/n64/core/cpu/registers/Cop1.cpp +++ b/src/n64/core/cpu/registers/Cop1.cpp @@ -9,7 +9,7 @@ Cop1::Cop1() { } void Cop1::Reset() { - fcr0 = 0xa00; + fcr0 = 0; fcr31.raw = 0; memset(fgr, 0, 32 * sizeof(FGR)); } diff --git a/src/n64/core/mmio/AI.cpp b/src/n64/core/mmio/AI.cpp index 695d1b00..a298bc84 100644 --- a/src/n64/core/mmio/AI.cpp +++ b/src/n64/core/mmio/AI.cpp @@ -10,10 +10,10 @@ void AI::Reset() { dacRate = 0; bitrate = 0; dmaCount = 0; - memset(dmaLen, 0, 2); - memset(dmaAddr, 0, 2); dmaAddrCarry = false; cycles = 0; + memset(dmaLen, 0, 2); + memset(dmaAddr, 0, 2); dac = {44100, N64_CPU_FREQ / dac.freq, 16}; } @@ -36,8 +36,6 @@ auto AI::Read(u32 addr) const -> u32 { #define max(x, y) ((x) > (y) ? (x) : (y)) -using namespace natsukashii::core; - void AI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) { switch(addr) { case 0x04500000: diff --git a/src/n64/core/mmio/Interrupt.cpp b/src/n64/core/mmio/Interrupt.cpp index 786e1114..7ccc1dcd 100644 --- a/src/n64/core/mmio/Interrupt.cpp +++ b/src/n64/core/mmio/Interrupt.cpp @@ -27,6 +27,7 @@ void InterruptRaise(MI &mi, Registers ®s, Interrupt intr) { UpdateInterrupt(mi, regs); } + void InterruptLower(MI &mi, Registers ®s, Interrupt intr) { switch(intr) { case Interrupt::VI: diff --git a/src/n64/core/mmio/PI.cpp b/src/n64/core/mmio/PI.cpp index 633b3b2d..d28fb501 100644 --- a/src/n64/core/mmio/PI.cpp +++ b/src/n64/core/mmio/PI.cpp @@ -58,7 +58,7 @@ void PI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) { dramAddr = dram_addr + len; cartAddr = cart_addr + len; InterruptRaise(mi, regs, Interrupt::PI); - status = status & 0xFFFFFFFE; + status &= 0xFFFFFFFE; util::logdebug("PI DMA from rdram to cart (size: {:.2f} MiB)\n", (float)len / 1048576); } break; case 0x0460000C: { @@ -75,7 +75,7 @@ void PI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) { dramAddr = dram_addr + len; cartAddr = cart_addr + len; InterruptRaise(mi, regs, Interrupt::PI); - status = status & 0xFFFFFFFE; + status &= 0xFFFFFFFE; util::logdebug("PI DMA from cart to rdram (size: {:.2f} MiB)\n", (float)len / 1048576); } break; case 0x04600010: diff --git a/src/n64/core/mmio/VI.cpp b/src/n64/core/mmio/VI.cpp index 724a1a7f..66f68c50 100644 --- a/src/n64/core/mmio/VI.cpp +++ b/src/n64/core/mmio/VI.cpp @@ -10,7 +10,13 @@ VI::VI () { } void VI::Reset() { + status.raw = 0xF; intr = 256; + origin = 0; + width = 320; + current = 0; + vsync = 0; + hsync = 0; numHalflines = 262; numFields = 1; cyclesPerHalfline = 1000; @@ -35,7 +41,6 @@ u32 VI::Read(u32 paddr) const { default: util::panic("Unimplemented VI[%08X] read\n", paddr); } - return 0; } void VI::Write(MI& mi, Registers& regs, u32 paddr, u32 val) { diff --git a/src/n64/core/rsp/decode.cpp b/src/n64/core/rsp/decode.cpp index 7cce97b2..4b8744ac 100644 --- a/src/n64/core/rsp/decode.cpp +++ b/src/n64/core/rsp/decode.cpp @@ -81,15 +81,15 @@ void RSP::Exec(MI &mi, Registers ®s, RDP &rdp, u32 instr) { u8 mask = (instr >> 26) & 0x3F; switch(mask) { //case 0x00: special(*this, instr); break; - //case 0x01: regimm(*this, instr); break; - //case 0x02: j(instr); break; + case 0x01: regimm(*this, instr); break; + case 0x02: j(instr); break; //case 0x03: jal(instr); break; //case 0x04: b(instr, gpr[RT(instr)] == gpr[RS(instr)]); break; //case 0x05: b(instr, gpr[RT(instr)] != gpr[RS(instr)]); break; //case 0x07: b(instr, gpr[RS(instr)] > 0); break; - //case 0x08: case 0x09: addi(instr); break; + case 0x08: case 0x09: addi(instr); break; //case 0x0C: andi(instr); break; - //case 0x0D: ori(instr); break; + case 0x0D: ori(instr); break; //case 0x0F: lui(instr); break; //case 0x10: cop0(mi, regs, *this, rdp, instr); break; //case 0x12: cop2(*this, instr); break; @@ -100,7 +100,7 @@ void RSP::Exec(MI &mi, Registers ®s, RDP &rdp, u32 instr) { //case 0x2B: sw(instr); break; //case 0x32: lwc2(*this, instr); break; //case 0x3A: swc2(*this, instr); break; - default: util::panic("Unhandled RSP instruction {} {}\n", (mask >> 3) & 7, mask & 7); + default: util::panic("Unhandled RSP instruction ({:06b})\n", mask); } } } \ No newline at end of file diff --git a/src/n64/core/rsp/instructions.cpp b/src/n64/core/rsp/instructions.cpp index c39a4e2d..ad6a9a4b 100644 --- a/src/n64/core/rsp/instructions.cpp +++ b/src/n64/core/rsp/instructions.cpp @@ -91,7 +91,10 @@ void RSP::add(u32 instr) { } void RSP::addi(u32 instr) { - + s32 op1 = gpr[RS(instr)]; + s16 op2 = instr; + s32 result = op1 + op2; + gpr[RT(instr)] = result; } void RSP::and_(u32 instr) { @@ -128,7 +131,8 @@ void RSP::lqv(u32 instr) { } void RSP::j(u32 instr) { - + u32 target = (instr & 0x3ffffff) << 2; + nextPC = target; } void RSP::jal(u32 instr) { @@ -148,7 +152,8 @@ void RSP::or_(u32 instr) { } void RSP::ori(u32 instr) { - + s16 imm = instr; + gpr[RT(instr)] = imm | gpr[RS(instr)]; } void RSP::sb(u32 instr) {