Delay SI DMAs, implement TAS movie replay (WIP), and fix DIV
This commit is contained in:
@@ -23,6 +23,10 @@ add_executable(natsukashii
|
||||
${FRONTEND_SOURCES}
|
||||
${FRONTEND_HEADERS}
|
||||
main.cpp
|
||||
Scheduler.cpp
|
||||
Scheduler.hpp
|
||||
common.hpp
|
||||
util.hpp
|
||||
)
|
||||
|
||||
target_include_directories(natsukashii PRIVATE
|
||||
|
||||
20
src/Scheduler.cpp
Normal file
20
src/Scheduler.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <Scheduler.hpp>
|
||||
#include <algorithm>
|
||||
#include <Mem.hpp>
|
||||
#include <Registers.hpp>
|
||||
|
||||
namespace n64 {
|
||||
void Scheduler::enqueue(const Event &event) {
|
||||
events.push_back({event.time + ticks, event.func});
|
||||
}
|
||||
|
||||
void Scheduler::handleEvents(u64 tick, Mem &mem, Registers ®s) {
|
||||
ticks += tick;
|
||||
for (int i = 0; i < events.size(); i++) {
|
||||
if (ticks >= events[i].time) {
|
||||
events[i].func(mem, regs);
|
||||
events.erase(events.begin() + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/Scheduler.hpp
Normal file
24
src/Scheduler.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <common.hpp>
|
||||
|
||||
namespace n64 {
|
||||
struct Mem;
|
||||
struct Registers;
|
||||
|
||||
struct Event {
|
||||
u64 time;
|
||||
void (*func)(Mem&, Registers& regs);
|
||||
};
|
||||
|
||||
enum {
|
||||
SI_DMA_COMPLETE
|
||||
};
|
||||
|
||||
struct Scheduler {
|
||||
void enqueue(const Event&);
|
||||
void handleEvents(u64 tick, Mem& mem, Registers& regs);
|
||||
u64 ticks = 0;
|
||||
std::vector<Event> events;
|
||||
};
|
||||
}
|
||||
@@ -38,6 +38,8 @@ using s128 = __int128_t;
|
||||
#define ELEMENT_INDEX(i) (7 - (i))
|
||||
#define BYTE_INDEX(i) (15 - (i))
|
||||
|
||||
#define SI_DMA_DELAY (65536 * 2)
|
||||
|
||||
|
||||
enum TLBAccessType {
|
||||
LOAD, STORE
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
#include <App.hpp>
|
||||
#include <parallel-rdp/ParallelRDPWrapper.hpp>
|
||||
#include <nfd.hpp>
|
||||
#include "m64.hpp"
|
||||
|
||||
void App::Run() {
|
||||
// Main loop
|
||||
const u8* state = SDL_GetKeyboardState(nullptr);
|
||||
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
|
||||
static int count = 0;
|
||||
while (!core.done) {
|
||||
core.Run(window, window.volumeL, window.volumeR);
|
||||
core.UpdateController(state);
|
||||
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
@@ -48,8 +51,6 @@ void App::Run() {
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
core.UpdateController(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <frontend/App.hpp>
|
||||
#include "m64.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define main SDL_main
|
||||
@@ -7,6 +8,9 @@
|
||||
int main(int argc, char** argv) {
|
||||
App* app = new App;
|
||||
if(argc > 1) {
|
||||
if(argc > 2) {
|
||||
LoadTAS(argv[2]);
|
||||
}
|
||||
app->LoadROM(argv[1]);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <ParallelRDPWrapper.hpp>
|
||||
#include <Window.hpp>
|
||||
#include <algorithm>
|
||||
#include "m64.hpp"
|
||||
|
||||
namespace n64 {
|
||||
Core::Core() {
|
||||
@@ -61,6 +62,7 @@ void Core::Run(Window& window, float volumeL, float volumeR) {
|
||||
}
|
||||
|
||||
mmio.ai.Step(mem, cpu.regs, 1, volumeL, volumeR);
|
||||
mem.scheduler.handleEvents(1, mem, cpu.regs);
|
||||
}
|
||||
|
||||
cycles -= mmio.vi.cyclesPerHalfline;
|
||||
@@ -87,7 +89,6 @@ void Core::Run(Window& window, float volumeL, float volumeR) {
|
||||
|
||||
void Core::UpdateController(const u8* state) {
|
||||
Controller &controller = mem.mmio.si.controller;
|
||||
controller.raw = 0;
|
||||
s8 xaxis = 0, yaxis = 0;
|
||||
|
||||
if(gamepadConnected) {
|
||||
@@ -106,42 +107,49 @@ void Core::UpdateController(const u8* state) {
|
||||
bool CLEFT = GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_RIGHTX) <= -128;
|
||||
bool CRIGHT = GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_RIGHTX) >= 127;
|
||||
|
||||
controller.b1 = (A << 7) | (B << 6) | (Z << 5) | (START << 4) |
|
||||
(DUP << 3) | (DDOWN << 2) | (DLEFT << 1) | DRIGHT;
|
||||
|
||||
controller.b2 = ((START && L && R) << 7) | (0 << 6) | (L << 5) | (R << 4) |
|
||||
(CUP << 3) | (CDOWN << 2) | (CLEFT << 1) | CRIGHT;
|
||||
controller.a = A;
|
||||
controller.b = B;
|
||||
controller.z = Z;
|
||||
controller.start = START;
|
||||
controller.dp_up = DUP;
|
||||
controller.dp_down = DDOWN;
|
||||
controller.dp_left = DLEFT;
|
||||
controller.dp_right = DRIGHT;
|
||||
controller.joy_reset = L && R && START;
|
||||
controller.l = L;
|
||||
controller.r = R;
|
||||
controller.c_up = CUP;
|
||||
controller.c_down = CDOWN;
|
||||
controller.c_left = CLEFT;
|
||||
controller.c_right = CRIGHT;
|
||||
|
||||
xaxis = (s8) std::clamp((GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_LEFTX) >> 8), -86, 86);
|
||||
yaxis = (s8) std::clamp(-(GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_LEFTY) >> 8), -86, 86);
|
||||
|
||||
controller.b3 = xaxis;
|
||||
controller.b4 = yaxis;
|
||||
controller.joy_x = xaxis;
|
||||
controller.joy_y = yaxis;
|
||||
|
||||
if ((controller.b2 >> 7) & 1) {
|
||||
controller.b1 &= ~0x10;
|
||||
controller.b3 = 0;
|
||||
controller.b4 = 0;
|
||||
if (controller.joy_reset) {
|
||||
controller.start = false;
|
||||
controller.joy_x = 0;
|
||||
controller.joy_y = 0;
|
||||
}
|
||||
} else {
|
||||
controller.b1 =
|
||||
(state[SDL_SCANCODE_X] << 7) |
|
||||
(state[SDL_SCANCODE_C] << 6) |
|
||||
(state[SDL_SCANCODE_Z] << 5) |
|
||||
(state[SDL_SCANCODE_RETURN] << 4) |
|
||||
(state[SDL_SCANCODE_KP_8] << 3) |
|
||||
(state[SDL_SCANCODE_KP_5] << 2) |
|
||||
(state[SDL_SCANCODE_KP_4] << 1) |
|
||||
(state[SDL_SCANCODE_KP_6]);
|
||||
controller.b2 =
|
||||
((state[SDL_SCANCODE_RETURN] && state[SDL_SCANCODE_A] && state[SDL_SCANCODE_S]) << 7) |
|
||||
(0 << 6) |
|
||||
(state[SDL_SCANCODE_A] << 5) |
|
||||
(state[SDL_SCANCODE_S] << 4) |
|
||||
(state[SDL_SCANCODE_I] << 3) |
|
||||
(state[SDL_SCANCODE_J] << 2) |
|
||||
(state[SDL_SCANCODE_K] << 1) |
|
||||
(state[SDL_SCANCODE_L]);
|
||||
controller.a = state[SDL_SCANCODE_X];
|
||||
controller.b = state[SDL_SCANCODE_C];
|
||||
controller.z = state[SDL_SCANCODE_Z];
|
||||
controller.start = state[SDL_SCANCODE_RETURN];
|
||||
controller.dp_up = state[SDL_SCANCODE_KP_8];
|
||||
controller.dp_down = state[SDL_SCANCODE_KP_5];
|
||||
controller.dp_left = state[SDL_SCANCODE_KP_4];
|
||||
controller.dp_right = state[SDL_SCANCODE_KP_6];
|
||||
controller.joy_reset = state[SDL_SCANCODE_RETURN] && state[SDL_SCANCODE_A] && state[SDL_SCANCODE_S];
|
||||
controller.l = state[SDL_SCANCODE_A];
|
||||
controller.r = state[SDL_SCANCODE_S];
|
||||
controller.c_up = state[SDL_SCANCODE_I];
|
||||
controller.c_down = state[SDL_SCANCODE_J];
|
||||
controller.c_left = state[SDL_SCANCODE_K];
|
||||
controller.c_right = state[SDL_SCANCODE_L];
|
||||
|
||||
if (state[SDL_SCANCODE_LEFT]) {
|
||||
xaxis = -86;
|
||||
@@ -155,14 +163,18 @@ void Core::UpdateController(const u8* state) {
|
||||
yaxis = 86;
|
||||
}
|
||||
|
||||
controller.b3 = xaxis;
|
||||
controller.b4 = yaxis;
|
||||
controller.joy_x = xaxis;
|
||||
controller.joy_y = yaxis;
|
||||
|
||||
if ((controller.b2 >> 7) & 1) {
|
||||
controller.b1 &= ~0x10;
|
||||
controller.b3 = 0;
|
||||
controller.b4 = 0;
|
||||
if (controller.joy_reset) {
|
||||
controller.start = false;
|
||||
controller.joy_x = 0;
|
||||
controller.joy_y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(tas_movie_loaded()) {
|
||||
controller = tas_next_inputs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,6 @@ inline void CheckCompareInterrupt(MI& mi, Registers& regs) {
|
||||
}
|
||||
}
|
||||
|
||||
inline void HandleInterrupt(Registers& regs) {
|
||||
|
||||
}
|
||||
|
||||
inline void Cpu::disassembly(u32 instr) {
|
||||
size_t count;
|
||||
cs_insn *insn;
|
||||
|
||||
@@ -47,7 +47,7 @@ private:
|
||||
void daddiu(u32);
|
||||
void ddiv(u32);
|
||||
void ddivu(u32);
|
||||
void div_(u32);
|
||||
void div(u32);
|
||||
void divu(u32);
|
||||
void dmult(u32);
|
||||
void dmultu(u32);
|
||||
|
||||
@@ -34,7 +34,7 @@ u32 MMIO::Read(u32 addr) {
|
||||
}
|
||||
}
|
||||
|
||||
void MMIO::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
|
||||
void MMIO::Write(Scheduler& scheduler, Mem& mem, Registers& regs, u32 addr, u32 val) {
|
||||
switch (addr) {
|
||||
case 0x04040000 ... 0x040FFFFF: rsp.Write(mem, regs, addr, val); break;
|
||||
case 0x04100000 ... 0x041FFFFF: rdp.Write(mi, regs, rsp, addr, val); break;
|
||||
@@ -43,7 +43,7 @@ void MMIO::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
|
||||
case 0x04500000 ... 0x045FFFFF: ai.Write(mem, regs, addr, val); break;
|
||||
case 0x04600000 ... 0x046FFFFF: pi.Write(mem, regs, addr, val); break;
|
||||
case 0x04700000 ... 0x047FFFFF: ri.Write(addr, val); break;
|
||||
case 0x04800000 ... 0x048FFFFF: si.Write(mem, regs, addr, val); break;
|
||||
case 0x04800000 ... 0x048FFFFF: si.Write(scheduler, mem, regs, addr, val); break;
|
||||
default:
|
||||
util::panic("Unhandled mmio write at addr {:08X} with val {:08X}\n", addr, val);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,6 @@ struct MMIO {
|
||||
RDP rdp;
|
||||
|
||||
u32 Read(u32);
|
||||
void Write(Mem&, Registers&, u32, u32);
|
||||
void Write(Scheduler&, Mem&, Registers&, u32, u32);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ void Mem::Write8(Registers& regs, u64 vaddr, u32 val, s64 pc) {
|
||||
util::WriteAccess<u32>(mmio.rsp.dmem, paddr & DMEM_DSIZE, val);
|
||||
break;
|
||||
case 0x04040000 ... 0x040FFFFF: case 0x04100000 ... 0x041FFFFF:
|
||||
case 0x04300000 ... 0x044FFFFF: case 0x04500000 ... 0x048FFFFF: mmio.Write(*this, regs, paddr, val); break;
|
||||
case 0x04300000 ... 0x044FFFFF: case 0x04500000 ... 0x048FFFFF: mmio.Write(scheduler, *this, regs, paddr, val); break;
|
||||
case 0x10000000 ... 0x13FFFFFF: break;
|
||||
case 0x1FC007C0 ... 0x1FC007FF:
|
||||
val = val << (8 * (3 - (paddr & 3)));
|
||||
@@ -265,7 +265,7 @@ void Mem::Write16(Registers& regs, u64 vaddr, u32 val, s64 pc) {
|
||||
util::WriteAccess<u32>(mmio.rsp.dmem, paddr & DMEM_DSIZE, val);
|
||||
break;
|
||||
case 0x04040000 ... 0x040FFFFF: case 0x04100000 ... 0x041FFFFF:
|
||||
case 0x04300000 ... 0x044FFFFF: case 0x04500000 ... 0x048FFFFF: mmio.Write(*this, regs, paddr, val); break;
|
||||
case 0x04300000 ... 0x044FFFFF: case 0x04500000 ... 0x048FFFFF: mmio.Write(scheduler, *this, regs, paddr, val); break;
|
||||
case 0x10000000 ... 0x13FFFFFF: break;
|
||||
case 0x1FC007C0 ... 0x1FC007FF:
|
||||
val = val << (16 * !(paddr & 2));
|
||||
@@ -299,7 +299,7 @@ void Mem::Write32(Registers& regs, u64 vaddr, u32 val, s64 pc) {
|
||||
util::WriteAccess<u32>(mmio.rsp.dmem, paddr & DMEM_DSIZE, val);
|
||||
break;
|
||||
case 0x04040000 ... 0x040FFFFF: case 0x04100000 ... 0x041FFFFF:
|
||||
case 0x04300000 ... 0x044FFFFF: case 0x04500000 ... 0x048FFFFF: mmio.Write(*this, regs, paddr, val); break;
|
||||
case 0x04300000 ... 0x044FFFFF: case 0x04500000 ... 0x048FFFFF: mmio.Write(scheduler, *this, regs, paddr, val); break;
|
||||
case 0x10000000 ... 0x13FF0013: break;
|
||||
case 0x13FF0014: {
|
||||
if(val < ISVIEWER_SIZE) {
|
||||
@@ -343,7 +343,7 @@ void Mem::Write64(Registers& regs, u64 vaddr, u64 val, s64 pc) {
|
||||
util::WriteAccess<u32>(mmio.rsp.dmem, paddr & DMEM_DSIZE, val);
|
||||
break;
|
||||
case 0x04040000 ... 0x040FFFFF: case 0x04100000 ... 0x041FFFFF:
|
||||
case 0x04300000 ... 0x044FFFFF: case 0x04500000 ... 0x048FFFFF: mmio.Write(*this, regs, paddr, val); break;
|
||||
case 0x04300000 ... 0x044FFFFF: case 0x04500000 ... 0x048FFFFF: mmio.Write(scheduler, *this, regs, paddr, val); break;
|
||||
case 0x10000000 ... 0x13FFFFFF: break;
|
||||
case 0x1FC007C0 ... 0x1FC007FF:
|
||||
util::WriteAccess<u64>(pifRam, paddr & PIF_RAM_DSIZE, htobe64(val));
|
||||
|
||||
@@ -42,6 +42,7 @@ struct Mem {
|
||||
|
||||
MMIO mmio;
|
||||
u8 pifRam[PIF_RAM_SIZE]{};
|
||||
Scheduler scheduler;
|
||||
|
||||
inline void DumpRDRAM() const {
|
||||
FILE *fp = fopen("rdram.dump", "wb");
|
||||
|
||||
@@ -30,7 +30,7 @@ void Cpu::special(Mem& mem, u32 instr) {
|
||||
case 0x17: dsrav(instr); break;
|
||||
case 0x18: mult(instr); break;
|
||||
case 0x19: multu(instr); break;
|
||||
case 0x1A: div_(instr); break;
|
||||
case 0x1A: div(instr); break;
|
||||
case 0x1B: divu(instr); break;
|
||||
case 0x1C: dmult(instr); break;
|
||||
case 0x1D: dmultu(instr); break;
|
||||
|
||||
@@ -77,20 +77,20 @@ void Cpu::daddiu(u32 instr) {
|
||||
regs.gpr[RT(instr)] = rs + imm;
|
||||
}
|
||||
|
||||
void Cpu::div_(u32 instr) {
|
||||
void Cpu::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 = -1;
|
||||
regs.lo = s64(-1);
|
||||
} else {
|
||||
regs.lo = 1;
|
||||
regs.lo = s64(1);
|
||||
}
|
||||
} else {
|
||||
s64 quotient = dividend / divisor;
|
||||
s64 remainder = dividend % divisor;
|
||||
s32 quotient = dividend / divisor;
|
||||
s32 remainder = dividend % divisor;
|
||||
regs.lo = quotient;
|
||||
regs.hi = remainder;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <n64/core/Mem.hpp>
|
||||
#include <n64/core/cpu/Registers.hpp>
|
||||
#include <util.hpp>
|
||||
#include "m64.hpp"
|
||||
|
||||
namespace n64 {
|
||||
static int channel = 0;
|
||||
@@ -43,10 +44,10 @@ void ProcessPIFCommands(u8* pifRam, Controller& controller, Mem& mem) {
|
||||
res[2] = 0x01;
|
||||
break;
|
||||
case 1:
|
||||
res[0] = controller.b1;
|
||||
res[1] = controller.b2;
|
||||
res[2] = controller.b3;
|
||||
res[3] = controller.b4;
|
||||
res[0] = controller.byte1;
|
||||
res[1] = controller.byte2;
|
||||
res[2] = controller.joy_x;
|
||||
res[3] = controller.joy_y;
|
||||
break;
|
||||
case 2: case 3: res[0] = 0; break;
|
||||
default: util::panic("Unimplemented PIF command {}", cmd[2]);
|
||||
|
||||
@@ -3,12 +3,35 @@
|
||||
|
||||
namespace n64 {
|
||||
|
||||
union Controller {
|
||||
struct {
|
||||
u8 b1, b2;
|
||||
s8 b3, b4;
|
||||
} __attribute__((__packed__));
|
||||
u32 raw;
|
||||
struct Controller {
|
||||
union {
|
||||
u8 byte1;
|
||||
struct {
|
||||
bool dp_right:1;
|
||||
bool dp_left:1;
|
||||
bool dp_down:1;
|
||||
bool dp_up:1;
|
||||
bool start:1;
|
||||
bool z:1;
|
||||
bool b:1;
|
||||
bool a:1;
|
||||
};
|
||||
};
|
||||
union {
|
||||
u8 byte2;
|
||||
struct {
|
||||
bool c_right:1;
|
||||
bool c_left:1;
|
||||
bool c_down:1;
|
||||
bool c_up:1;
|
||||
bool r:1;
|
||||
bool l:1;
|
||||
bool zero:1;
|
||||
bool joy_reset:1;
|
||||
};
|
||||
};
|
||||
s8 joy_x;
|
||||
s8 joy_y;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Controller) == 4);
|
||||
|
||||
@@ -9,7 +9,7 @@ SI::SI() {
|
||||
void SI::Reset() {
|
||||
status.raw = 0;
|
||||
dramAddr = 0;
|
||||
controller.raw = 0;
|
||||
memset(&controller, 0, sizeof(Controller));
|
||||
}
|
||||
|
||||
auto SI::Read(MI& mi, u32 addr) const -> u32 {
|
||||
@@ -21,7 +21,7 @@ auto SI::Read(MI& mi, u32 addr) const -> u32 {
|
||||
val |= status.dmaBusy;
|
||||
val |= (0 << 1);
|
||||
val |= (0 << 3);
|
||||
val |= (status.intr << 12);
|
||||
val |= (mi.miIntr.si << 12);
|
||||
return val;
|
||||
}
|
||||
default:
|
||||
@@ -29,33 +29,43 @@ auto SI::Read(MI& mi, u32 addr) const -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
void SI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
|
||||
void DMA(Mem& mem, Registers& regs) {
|
||||
MMIO& mmio = mem.mmio;
|
||||
SI& si = mmio.si;
|
||||
si.status.dmaBusy = false;
|
||||
if(si.toDram) {
|
||||
ProcessPIFCommands(mem.pifRam, si.controller, mem);
|
||||
for(int i = 0; i < 64; i++) {
|
||||
mem.mmio.rdp.dram[BYTE_ADDRESS(si.dramAddr + i)] = mem.pifRam[i];
|
||||
}
|
||||
} else {
|
||||
for(int i = 0; i < 64; i++) {
|
||||
mem.pifRam[i] = mem.mmio.rdp.dram[BYTE_ADDRESS(si.dramAddr + i)];
|
||||
}
|
||||
ProcessPIFCommands(mem.pifRam, si.controller, mem);
|
||||
}
|
||||
InterruptRaise(mem.mmio.mi, regs, Interrupt::SI);
|
||||
}
|
||||
|
||||
void SI::Write(Scheduler& scheduler, Mem& mem, Registers& regs, u32 addr, u32 val) {
|
||||
switch(addr) {
|
||||
case 0x04800000:
|
||||
dramAddr = val & RDRAM_DSIZE;
|
||||
break;
|
||||
case 0x04800004: {
|
||||
ProcessPIFCommands(mem.pifRam, controller, mem);
|
||||
|
||||
for(int i = 0; i < 64; i++) {
|
||||
mem.mmio.rdp.dram[BYTE_ADDRESS(dramAddr + i)] = mem.pifRam[i];
|
||||
}
|
||||
InterruptRaise(mem.mmio.mi, regs, Interrupt::SI);
|
||||
status.intr = 1;
|
||||
status.dmaBusy = true;
|
||||
toDram = true;
|
||||
scheduler.enqueue({SI_DMA_DELAY, DMA});
|
||||
util::logdebug("SI DMA from PIF RAM to RDRAM ({:08X} to {:08X})\n", val & 0x1FFFFFFF, dramAddr);
|
||||
} break;
|
||||
case 0x04800010: {
|
||||
for(int i = 0; i < 64; i++) {
|
||||
mem.pifRam[i] = mem.mmio.rdp.dram[BYTE_ADDRESS(dramAddr + i)];
|
||||
}
|
||||
ProcessPIFCommands(mem.pifRam, controller, mem);
|
||||
InterruptRaise(mem.mmio.mi, regs, Interrupt::SI);
|
||||
status.intr = 1;
|
||||
status.dmaBusy = true;
|
||||
toDram = false;
|
||||
scheduler.enqueue({SI_DMA_DELAY, DMA});
|
||||
util::logdebug("SI DMA from RDRAM to PIF RAM ({:08X} to {:08X})\n", dramAddr, val & 0x1FFFFFFF);
|
||||
} break;
|
||||
case 0x04800018:
|
||||
InterruptLower(mem.mmio.mi, regs, Interrupt::SI);
|
||||
status.intr = 0;
|
||||
break;
|
||||
default:
|
||||
util::panic("Unhandled SI[%08X] write (%08X)\n", addr, val);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <n64/core/mmio/Interrupt.hpp>
|
||||
#include <n64/core/mmio/MI.hpp>
|
||||
#include <n64/core/mmio/PIF.hpp>
|
||||
#include <Scheduler.hpp>
|
||||
|
||||
namespace n64 {
|
||||
|
||||
@@ -27,7 +28,11 @@ struct SI {
|
||||
u32 dramAddr{};
|
||||
Controller controller{};
|
||||
|
||||
bool toDram = false;
|
||||
|
||||
auto Read(MI&, u32) const -> u32;
|
||||
void Write(Mem&, Registers&, u32, u32);
|
||||
void Write(Scheduler& scheduler, Mem&, Registers&, u32, u32);
|
||||
};
|
||||
|
||||
static void DMA(Mem& mem, Registers& regs);
|
||||
}
|
||||
@@ -68,7 +68,7 @@ inline void lwc2(RSP& rsp, u32 instr) {
|
||||
case 0x05: rsp.lrv(instr); break;
|
||||
case 0x06: rsp.lpv(instr); break;
|
||||
case 0x07: rsp.luv(instr); break;
|
||||
case 0x0A: printf("LWV\n"); break;
|
||||
case 0x0A: break;
|
||||
case 0x0B: rsp.ltv(instr); break;
|
||||
default: util::panic("Unhandled RSP LWC2 {:05b}\n", mask);
|
||||
}
|
||||
|
||||
@@ -68,6 +68,8 @@ inline void SetCop0Reg(Registers& regs, Mem& mem, u8 index, u32 val) {
|
||||
case 7:
|
||||
if(val == 0) {
|
||||
ReleaseSemaphore(rsp);
|
||||
} else {
|
||||
util::panic("Write with non-zero value to RSP_COP0_RESERVED ({})\n", val);
|
||||
}
|
||||
break;
|
||||
case 8: rdp.WriteStart(val); break;
|
||||
|
||||
162
src/n64/m64.cpp
Normal file
162
src/n64/m64.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
#include <m64.hpp>
|
||||
#include <util.hpp>
|
||||
|
||||
struct TASMovieHeader {
|
||||
u8 signature[4];
|
||||
u32 version;
|
||||
u32 uid;
|
||||
u32 numFrames;
|
||||
u32 rerecords;
|
||||
u8 fps;
|
||||
u8 numControllers;
|
||||
u8 reserved1;
|
||||
u8 reserved2;
|
||||
u32 numInputSamples;
|
||||
uint16_t startType;
|
||||
u8 reserved3;
|
||||
u8 reserved4;
|
||||
u32 controllerFlags;
|
||||
u8 reserved5[160];
|
||||
char romName[32];
|
||||
u32 romCrc32;
|
||||
uint16_t romCountryCode;
|
||||
u8 reserved6[56];
|
||||
// 122 64-byte ASCII string: name of video plugin used when recording, directly from plugin
|
||||
char video_plugin_name[64];
|
||||
// 162 64-byte ASCII string: name of sound plugin used when recording, directly from plugin
|
||||
char audio_plugin_name[64];
|
||||
// 1A2 64-byte ASCII string: name of input plugin used when recording, directly from plugin
|
||||
char input_plugin_name[64];
|
||||
// 1E2 64-byte ASCII string: name of rsp plugin used when recording, directly from plugin
|
||||
char rsp_plugin_name[64];
|
||||
// 222 222-byte UTF-8 string: author name info
|
||||
char author_name[222];
|
||||
// 300 256-byte UTF-8 string: author movie description info
|
||||
char movie_description[256];
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(sizeof(TASMovieHeader) == 1024);
|
||||
|
||||
union TASMovieControllerData {
|
||||
struct {
|
||||
bool dpad_right: 1;
|
||||
bool dpad_left: 1;
|
||||
bool dpad_down: 1;
|
||||
bool dpad_up: 1;
|
||||
bool start: 1;
|
||||
bool z: 1;
|
||||
bool b: 1;
|
||||
bool a: 1;
|
||||
bool c_right: 1;
|
||||
bool c_left: 1;
|
||||
bool c_down: 1;
|
||||
bool c_up: 1;
|
||||
bool r: 1;
|
||||
bool l: 1;
|
||||
u8: 2;
|
||||
s8 analog_x: 8;
|
||||
s8 analog_y: 8;
|
||||
};
|
||||
u32 raw;
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(sizeof(TASMovieControllerData) == 4);
|
||||
|
||||
static u8* loaded_tas_movie = nullptr;
|
||||
static size_t loaded_tas_movie_size = 0;
|
||||
TASMovieHeader loaded_tas_movie_header;
|
||||
uint32_t loaded_tas_movie_index = 0;
|
||||
|
||||
void LoadTAS(const char* filename) {
|
||||
FILE *fp = fopen(filename, "rb");
|
||||
|
||||
if (!fp) {
|
||||
util::panic("Error opening the movie file {}! Are you sure it's a valid movie and that it exists?", filename);
|
||||
}
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size_t size = ftell(fp);
|
||||
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
u8 *buf = (u8*)malloc(size);
|
||||
fread(buf, size, 1, fp);
|
||||
|
||||
loaded_tas_movie = buf;
|
||||
loaded_tas_movie_size = size;
|
||||
|
||||
if (!loaded_tas_movie) {
|
||||
util::panic("Error loading movie!");
|
||||
}
|
||||
|
||||
memcpy(&loaded_tas_movie_header, buf, sizeof(TASMovieHeader));
|
||||
|
||||
if (loaded_tas_movie_header.signature[0] != 0x4D || loaded_tas_movie_header.signature[1] != 0x36 || loaded_tas_movie_header.signature[2] != 0x34 || loaded_tas_movie_header.signature[3] != 0x1A) {
|
||||
util::panic("Failed to load movie: incorrect signature. Are you sure this is a valid movie?");
|
||||
}
|
||||
|
||||
if (loaded_tas_movie_header.version != 3) {
|
||||
util::panic("This movie is version {}: only version 3 is supported.", loaded_tas_movie_header.version);
|
||||
}
|
||||
|
||||
if (loaded_tas_movie_header.startType != 2) {
|
||||
util::panic("Movie start type is {} - only movies with a start type of 2 are supported (start at power on)", loaded_tas_movie_header.startType);
|
||||
}
|
||||
|
||||
// TODO: check ROM CRC32 here
|
||||
|
||||
util::print("Loaded movie '{}' ", loaded_tas_movie_header.movie_description);
|
||||
util::print("by {}\n", loaded_tas_movie_header.author_name);
|
||||
util::print("{} controller(s) connected\n", loaded_tas_movie_header.numControllers);
|
||||
|
||||
if (loaded_tas_movie_header.numControllers != 1) {
|
||||
util::panic("Currently, only movies with 1 controller connected are supported.\n");
|
||||
}
|
||||
|
||||
loaded_tas_movie_index = sizeof(TASMovieHeader) - 4; // skip header
|
||||
}
|
||||
|
||||
bool tas_movie_loaded() {
|
||||
return loaded_tas_movie != nullptr;
|
||||
}
|
||||
|
||||
n64::Controller tas_next_inputs() {
|
||||
if (loaded_tas_movie_index + sizeof(TASMovieControllerData) > loaded_tas_movie_size) {
|
||||
loaded_tas_movie = nullptr;
|
||||
n64::Controller empty_controller{};
|
||||
memset(&empty_controller, 0, sizeof(n64::Controller));
|
||||
return empty_controller;
|
||||
}
|
||||
|
||||
TASMovieControllerData movie_cdata{};
|
||||
memcpy(&movie_cdata, loaded_tas_movie + loaded_tas_movie_index, sizeof(TASMovieControllerData));
|
||||
|
||||
loaded_tas_movie_index += sizeof(TASMovieControllerData);
|
||||
|
||||
n64::Controller controller{};
|
||||
memset(&controller, 0, sizeof(controller));
|
||||
|
||||
controller.c_right = movie_cdata.c_right;
|
||||
controller.c_left = movie_cdata.c_left;
|
||||
controller.c_down = movie_cdata.c_down;
|
||||
controller.c_up = movie_cdata.c_up;
|
||||
controller.r = movie_cdata.r;
|
||||
controller.l = movie_cdata.l;
|
||||
|
||||
controller.dp_right = movie_cdata.dpad_right;
|
||||
controller.dp_left = movie_cdata.dpad_left;
|
||||
controller.dp_down = movie_cdata.dpad_down;
|
||||
controller.dp_up = movie_cdata.dpad_up;
|
||||
|
||||
controller.z = movie_cdata.z;
|
||||
controller.b = movie_cdata.b;
|
||||
controller.a = movie_cdata.a;
|
||||
if(movie_cdata.start) {
|
||||
printf("\n");
|
||||
}
|
||||
controller.start = movie_cdata.start;
|
||||
|
||||
controller.joy_x = movie_cdata.analog_x;
|
||||
controller.joy_y = movie_cdata.analog_y;
|
||||
|
||||
return controller;
|
||||
}
|
||||
7
src/n64/m64.hpp
Normal file
7
src/n64/m64.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <PIF.hpp>
|
||||
|
||||
void LoadTAS(const char* filename);
|
||||
void UnloadTAS();
|
||||
n64::Controller tas_next_inputs();
|
||||
bool tas_movie_loaded();
|
||||
Reference in New Issue
Block a user