forgot to increment v_current. Fuck me, thanks @wheremyfoodat
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -9,4 +9,6 @@ roms/
|
||||
.cache/
|
||||
.vscode/
|
||||
vgcore.*
|
||||
*.data
|
||||
*.data
|
||||
disasm.txt
|
||||
log.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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <SDL2/SDL.h>
|
||||
#include "util.hpp"
|
||||
|
||||
namespace natsukashii::core {
|
||||
namespace n64 {
|
||||
#define AUDIO_SAMPLE_RATE 48000
|
||||
#define SYSTEM_SAMPLE_FORMAT AUDIO_F32SYS
|
||||
#define SYSTEM_SAMPLE_SIZE 4
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include "common.hpp"
|
||||
|
||||
namespace natsukashii::core {
|
||||
namespace n64 {
|
||||
void PushSample(s16, s16);
|
||||
void InitAudio();
|
||||
void AdjustSampleRate(int);
|
||||
|
||||
@@ -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<u32>(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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<u32>(imem, pc & IMEM_DSIZE);
|
||||
//oldPC = pc & 0xFFF;
|
||||
//pc = nextPC & 0xFFF;
|
||||
//nextPC += 4;
|
||||
//Exec(mi, regs, rdp, instr);
|
||||
gpr[0] = 0;
|
||||
u32 instr = util::ReadAccess<u32>(imem, pc & IMEM_DSIZE);
|
||||
oldPC = pc & 0xFFF;
|
||||
pc = nextPC & 0xFFF;
|
||||
nextPC += 4;
|
||||
Exec(mi, regs, rdp, instr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -9,7 +9,7 @@ Cop1::Cop1() {
|
||||
}
|
||||
|
||||
void Cop1::Reset() {
|
||||
fcr0 = 0xa00;
|
||||
fcr0 = 0;
|
||||
fcr31.raw = 0;
|
||||
memset(fgr, 0, 32 * sizeof(FGR));
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user