PIF stuff

This commit is contained in:
SimoneN64
2023-03-04 00:02:06 +01:00
parent 3ed891aba1
commit 8ac5386fd2
12 changed files with 490 additions and 109 deletions

View File

@@ -26,6 +26,7 @@ void Core::LoadROM(const std::string& rom_) {
cpu->mem.LoadROM(rom);
GameDB::match(cpu->mem);
cpu->mem.mmio.si.pif.InitDevices(cpu->mem.saveType);
isPAL = cpu->mem.IsROMPAL();
cpu->mem.mmio.si.pif.ExecutePIF(cpu->mem, cpu->regs);
}

View File

@@ -115,7 +115,7 @@ void LoadTAS(const char* filename) {
loaded_tas_movie_index = sizeof(TASMovieHeader) - 4; // skip header
}
bool tas_movie_loaded() {
bool TasMovieLoaded() {
return loaded_tas_movie != nullptr;
}
@@ -138,7 +138,7 @@ inline void LogController(const n64::Controller& controller) {
Util::print("joy_y: {}\n\n", controller.joy_y);
}
n64::Controller tas_next_inputs() {
n64::Controller TasNextInputs() {
if (loaded_tas_movie_index + sizeof(TASMovieControllerData) > loaded_tas_movie_size) {
loaded_tas_movie = nullptr;
n64::Controller empty_controller{};

View File

@@ -3,5 +3,5 @@
void LoadTAS(const char* filename);
void UnloadTAS();
n64::Controller tas_next_inputs();
bool tas_movie_loaded();
n64::Controller TasNextInputs();
bool TasMovieLoaded();

View File

@@ -1,4 +1,4 @@
file(GLOB SOURCES *.cpp)
file(GLOB HEADERS *.hpp)
file(GLOB_RECURSE SOURCES *.cpp)
file(GLOB_RECURSE HEADERS *.hpp)
add_library(mmio ${SOURCES} ${HEADERS})
add_library(mmio ${SOURCES} ${HEADERS} ../../../../external/cic_nus_6105/n64_cic_nus_6105.cpp)

View File

@@ -2,84 +2,98 @@
#include <core/Mem.hpp>
#include <core/registers/Registers.hpp>
#include <log.hpp>
#include <MupenMovie.hpp>
#include <SDL_keyboard.h>
#include <cic_nus_6105/n64_cic_nus_6105.hpp>
#include <cassert>
namespace n64 {
static int channel = 0;
void PIF::CICChallenge() {
u8 challenge[30];
u8 response[30];
void PIF::ProcessPIFCommands(Mem& mem) {
// Split 15 bytes into 30 nibbles
for (int i = 0; i < 15; i++) {
challenge[i * 2 + 0] = (pifRam[0x30 + i] >> 4) & 0x0F;
challenge[i * 2 + 1] = (pifRam[0x30 + i] >> 0) & 0x0F;
}
n64_cic_nus_6105((char*)challenge, (char*)response, CHL_LEN - 2);
for (int i = 0; i < 15; i++) {
pifRam[0x30 + i] = (response[i * 2] << 4) + response[i * 2 + 1];
}
}
void PIF::ProcessPIFCommands(Mem &mem) {
u8 control = pifRam[63];
if(control & 1) {
if (control & 1) {
channel = 0;
for(int i = 0; i < 63;) {
int i = 0;
while (i < 63) {
u8* cmd = &pifRam[i++];
u8 t = cmd[0] & 0x3f;
u8 cmdlen = cmd[0] & 0x3F;
if(t == 0 || t == 0x3D) {
if (cmdlen == 0) {
channel++;
} else if (t == 0x3E) {
} else if (cmdlen == 0x3D) { // 0xFD in PIF RAM = send reset signal to this pif channel
channel++;
} else if (cmdlen == 0x3E) { // 0xFE in PIF RAM = end of commands
break;
} else if (t == 0x3F) {
} else if (cmdlen == 0x3F) {
continue;
} else {
u8 r = pifRam[i++];
r |= (1 << 7);
if(r == 0xFE) {
if (r == 0xFE) { // 0xFE in PIF RAM = end of commands.
break;
}
u8 reslen = r & 0x3F; // TODO: out of bounds access possible on invalid data
u8* res = &pifRam[i + cmdlen];
u8 rlen = r & 0x3F;
u8* res = &pifRam[i + t];
switch(cmd[2]) {
switch (cmd[2]) {
case 0xff:
res[0] = 0x05;
res[1] = 0x00;
res[2] = 0x01;
channel++;
ControllerID(res);
break;
case 0:
res[0] = 0x05;
res[1] = 0x00;
res[2] = 0x01;
ControllerID(res);
break;
case 1:
if(tas_movie_loaded()) {
controller = tas_next_inputs();
} else {
UpdateController();
UpdateController();
if(!ReadButtons(res)) {
cmd[1] |= 0x80;
}
res[0] = controller.byte1;
res[1] = controller.byte2;
res[2] = controller.joy_x;
res[3] = controller.joy_y;
channel++;
break;
case 2:
Util::print("MEMPAK READ\n");
res[0] = 0;
break;
//pif_mempack_read(cmd, res);
//break;
case 3:
Util::print("MEMPAK WRITE\n");
res[0] = 0;
break;
//pif_mempack_write(cmd, res);
//break;
case 4:
Util::print("EEPROM READ\n");
res[0] = 0;
break;
//assert(mem.saveData != NULL && "EEPROM read when save data is uninitialized! Is this game in the game DB?");
//pif_eeprom_read(cmd, res);
//break;
case 5:
Util::print("EEPROM WRITE\n");
//assert(mem.saveData != NULL && "EEPROM write when save data is uninitialized! Is this game in the game DB?");
//pif_eeprom_write(cmd, res);
res[0] = 0;
break;
default: Util::panic("Unimplemented PIF command {}", cmd[2]);
default:
Util::panic("Invalid PIF command: {:X}", cmd[2]);
}
i += t + rlen;
i += cmdlen + reslen;
}
}
}
if(control & 8) {
//if (control & 2) {
// CICChallenge();
// pifRam[63] &= ~2;
//}
if (control & 0x08) {
pifRam[63] &= ~8;
}
@@ -92,13 +106,12 @@ void PIF::ProcessPIFCommands(Mem& mem) {
#define GET_AXIS(gamepad, axis) SDL_GameControllerGetAxis(gamepad, axis)
void PIF::UpdateController() {
const uint8_t* state = SDL_GetKeyboardState(nullptr);
s8 xaxis = 0, yaxis = 0;
if(gamepadConnected) {
bool A = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_A);
bool B = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_X);
bool Z = GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_TRIGGERLEFT) == 32767;
bool Z = GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_TRIGGERLEFT) == SDL_JOYSTICK_AXIS_MAX;
bool START = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_START);
bool DUP = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_DPAD_UP);
bool DDOWN = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_DPAD_DOWN);
@@ -106,54 +119,55 @@ void PIF::UpdateController() {
bool DRIGHT = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
bool L = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
bool R = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
bool CUP = GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_RIGHTY) <= -128;
bool CUP = GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_RIGHTY) <= -127;
bool CDOWN = GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_RIGHTY) >= 127;
bool CLEFT = GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_RIGHTX) <= -128;
bool CLEFT = GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_RIGHTX) <= -127;
bool CRIGHT = GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_RIGHTX) >= 127;
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;
joybusDevices[channel].controller.a = A;
joybusDevices[channel].controller.b = B;
joybusDevices[channel].controller.z = Z;
joybusDevices[channel].controller.start = START;
joybusDevices[channel].controller.dp_up = DUP;
joybusDevices[channel].controller.dp_down = DDOWN;
joybusDevices[channel].controller.dp_left = DLEFT;
joybusDevices[channel].controller.dp_right = DRIGHT;
joybusDevices[channel].controller.joy_reset = L && R && START;
joybusDevices[channel].controller.l = L;
joybusDevices[channel].controller.r = R;
joybusDevices[channel].controller.c_up = CUP;
joybusDevices[channel].controller.c_down = CDOWN;
joybusDevices[channel].controller.c_left = CLEFT;
joybusDevices[channel].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);
xaxis = (s8) std::clamp<s16>(GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_LEFTX), -86, 86);
yaxis = (s8) std::clamp<s16>(GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_LEFTY), -86, 86);
controller.joy_x = xaxis;
controller.joy_y = yaxis;
joybusDevices[channel].controller.joy_x = xaxis;
joybusDevices[channel].controller.joy_y = -yaxis;
if (controller.joy_reset) {
controller.start = false;
controller.joy_x = 0;
controller.joy_y = 0;
if (joybusDevices[channel].controller.joy_reset) {
joybusDevices[channel].controller.start = false;
joybusDevices[channel].controller.joy_x = 0;
joybusDevices[channel].controller.joy_y = 0;
}
} else {
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];
const uint8_t* state = SDL_GetKeyboardState(nullptr);
joybusDevices[channel].controller.a = state[SDL_SCANCODE_X];
joybusDevices[channel].controller.b = state[SDL_SCANCODE_C];
joybusDevices[channel].controller.z = state[SDL_SCANCODE_Z];
joybusDevices[channel].controller.start = state[SDL_SCANCODE_RETURN];
joybusDevices[channel].controller.dp_up = state[SDL_SCANCODE_KP_8];
joybusDevices[channel].controller.dp_down = state[SDL_SCANCODE_KP_5];
joybusDevices[channel].controller.dp_left = state[SDL_SCANCODE_KP_4];
joybusDevices[channel].controller.dp_right = state[SDL_SCANCODE_KP_6];
joybusDevices[channel].controller.joy_reset = state[SDL_SCANCODE_RETURN] && state[SDL_SCANCODE_A] && state[SDL_SCANCODE_S];
joybusDevices[channel].controller.l = state[SDL_SCANCODE_A];
joybusDevices[channel].controller.r = state[SDL_SCANCODE_S];
joybusDevices[channel].controller.c_up = state[SDL_SCANCODE_I];
joybusDevices[channel].controller.c_down = state[SDL_SCANCODE_J];
joybusDevices[channel].controller.c_left = state[SDL_SCANCODE_K];
joybusDevices[channel].controller.c_right = state[SDL_SCANCODE_L];
if (state[SDL_SCANCODE_LEFT]) {
xaxis = -86;
@@ -167,13 +181,13 @@ void PIF::UpdateController() {
yaxis = 86;
}
controller.joy_x = xaxis;
controller.joy_y = yaxis;
joybusDevices[channel].controller.joy_x = xaxis;
joybusDevices[channel].controller.joy_y = yaxis;
if (controller.joy_reset) {
controller.start = false;
controller.joy_x = 0;
controller.joy_y = 0;
if (joybusDevices[channel].controller.joy_reset) {
joybusDevices[channel].controller.start = false;
joybusDevices[channel].controller.joy_x = 0;
joybusDevices[channel].controller.joy_y = 0;
}
}
}

View File

@@ -1,9 +1,16 @@
#pragma once
#include <MemoryRegions.hpp>
#include <SDL_gamecontroller.h>
#include <GameDB.hpp>
namespace n64 {
enum AccessoryType {
ACCESSORY_NONE,
ACCESSORY_MEMPACK,
ACCESSORY_RUMBLE_PACK
};
struct Controller {
union {
u8 byte1;
@@ -31,12 +38,30 @@ struct Controller {
bool joy_reset:1;
};
};
s8 joy_x;
s8 joy_y;
s8 joy_x{};
s8 joy_y{};
};
static_assert(sizeof(Controller) == 4);
enum JoybusType {
JOYBUS_NONE,
JOYBUS_CONTROLLER,
JOYBUS_DANCEPAD,
JOYBUS_VRU,
JOYBUS_MOUSE,
JOYBUS_RANDNET_KEYBOARD,
JOYBUS_DENSHA_DE_GO,
JOYBUS_4KB_EEPROM,
JOYBUS_16KB_EEPROM
};
struct JoybusDevice {
JoybusType type{};
AccessoryType accessoryType{};
Controller controller{};
};
struct Mem;
struct Registers;
@@ -64,23 +89,29 @@ struct CartInfo;
struct PIF {
void ProcessPIFCommands(Mem&);
void InitDevices(SaveType);
void CICChallenge();
void ExecutePIF(Mem& mem, Registers& regs);
void DoPIFHLE(Mem& mem, Registers& regs, bool pal, CICType cicType);
void UpdateController();
bool ReadButtons(u8*);
bool gamepadConnected = false;
void ControllerID(u8* res);
SDL_GameController* gamepad;
Controller controller;
u8 pifBootrom[PIF_BOOTROM_SIZE]{}, pifRam[PIF_RAM_SIZE];
JoybusDevice joybusDevices[6]{};
u8 pifBootrom[PIF_BOOTROM_SIZE]{}, pifRam[PIF_RAM_SIZE]{};
int channel = 0;
u8 Read(u32 addr) {
addr &= 0x7FF;
if(addr < 0x7c0) return pifBootrom[addr];
return pifRam[addr];
return pifRam[addr & PIF_RAM_DSIZE];
}
void Write(u32 addr, u8 val) {
addr &= 0x7FF;
if(addr < 0x7c0) return;
pifRam[addr] = val;
pifRam[addr & PIF_RAM_DSIZE] = val;
}
};
}

View File

@@ -0,0 +1,125 @@
#include <PIF.hpp>
#include <MupenMovie.hpp>
#include "log.hpp"
namespace n64 {
void PIF::InitDevices(SaveType saveType) {
for (int i = 0; i < 4; i++) {
joybusDevices[i].type = JOYBUS_CONTROLLER; //TODO
if (joybusDevices[i].type) {
// TODO: make this configurable
joybusDevices[i].accessoryType = ACCESSORY_MEMPACK;
}
}
if (saveType == SAVE_EEPROM_4k) {
joybusDevices[4].type = JOYBUS_4KB_EEPROM;
} else if (saveType == SAVE_EEPROM_16k) {
joybusDevices[4].type = JOYBUS_16KB_EEPROM;
} else {
joybusDevices[4].type = JOYBUS_NONE;
}
joybusDevices[5].type = JOYBUS_NONE;
}
void PIF::ControllerID(u8 *res) {
if (channel < 6) {
switch (joybusDevices[channel].type) {
case JOYBUS_NONE:
res[0] = 0x00;
res[1] = 0x00;
res[2] = 0x00;
break;
case JOYBUS_CONTROLLER:
res[0] = 0x05;
res[1] = 0x00;
res[2] = joybusDevices[channel].accessoryType != ACCESSORY_NONE ? 0x01 : 0x02;
break;
case JOYBUS_DANCEPAD:
res[0] = 0x05;
res[1] = 0x00;
res[2] = 0x00;
break;
case JOYBUS_VRU:
res[0] = 0x00;
res[1] = 0x01;
res[2] = 0x00;
break;
case JOYBUS_MOUSE:
res[0] = 0x02;
res[1] = 0x00;
res[2] = 0x00;
break;
case JOYBUS_RANDNET_KEYBOARD:
res[0] = 0x00;
res[1] = 0x02;
res[2] = 0x00;
break;
case JOYBUS_DENSHA_DE_GO:
res[0] = 0x20;
res[1] = 0x04;
res[2] = 0x00;
break;
case JOYBUS_4KB_EEPROM:
res[0] = 0x00;
res[1] = 0x80;
res[2] = 0x00;
break;
case JOYBUS_16KB_EEPROM:
res[0] = 0x00;
res[1] = 0xC0;
res[2] = 0x00;
break;
}
} else {
Util::panic("Device ID on unknown channel {}", channel);
}
channel++;
}
bool PIF::ReadButtons(u8* res) {
if(channel >= 6) {
res[0] = 0;
res[1] = 0;
res[2] = 0;
res[3] = 0;
return false;
}
switch (joybusDevices[channel].type) {
case JOYBUS_NONE:
res[0] = 0x00;
res[1] = 0x00;
res[2] = 0x00;
res[3] = 0x00;
return false; // Device not present
case JOYBUS_CONTROLLER:
if (TasMovieLoaded()) {
// Load inputs from TAS movie
Controller controller = TasNextInputs();
res[0] = controller.byte1;
res[1] = controller.byte2;
res[2] = controller.joy_x;
res[3] = controller.joy_y;
} else {
// Load inputs normally
res[0] = joybusDevices[channel].controller.byte1;
res[1] = joybusDevices[channel].controller.byte2;
res[2] = joybusDevices[channel].controller.joy_x;
res[3] = joybusDevices[channel].controller.joy_y;
}
break;
case JOYBUS_DANCEPAD:
case JOYBUS_VRU:
case JOYBUS_MOUSE:
case JOYBUS_RANDNET_KEYBOARD:
case JOYBUS_DENSHA_DE_GO:
case JOYBUS_4KB_EEPROM:
case JOYBUS_16KB_EEPROM:
return false;
}
return true; // Success!
}
}

View File

@@ -11,7 +11,7 @@ void SI::Reset() {
status.raw = 0;
dramAddr = 0;
pifAddr = 0;
memset(&controller, 0, sizeof(Controller));
memset(&pif.joybusDevices, 0, sizeof(JoybusDevice) * 6);
}
auto SI::Read(MI& mi, u32 addr) const -> u32 {
@@ -40,10 +40,12 @@ void DMA(Mem& mem, Registers& regs) {
for(int i = 0; i < 64; i++) {
mem.mmio.rdp.rdram[BYTE_ADDRESS(si.dramAddr + i)] = si.pif.pifRam[i];
}
Util::debug("SI DMA from PIF RAM to RDRAM ({:08X} to {:08X})\n", si.pifAddr, si.dramAddr);
} else {
for(int i = 0; i < 64; i++) {
si.pif.pifRam[i] = mem.mmio.rdp.rdram[BYTE_ADDRESS(si.dramAddr + i)];
}
Util::debug("SI DMA from RDRAM to PIF RAM ({:08X} to {:08X})\n", si.dramAddr, si.pifAddr);
si.pif.ProcessPIFCommands(mem);
}
InterruptRaise(mem.mmio.mi, regs, Interrupt::SI);
@@ -59,14 +61,12 @@ void SI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
status.dmaBusy = true;
toDram = true;
scheduler.enqueueRelative({SI_DMA_DELAY, DMA});
Util::debug("SI DMA from PIF RAM to RDRAM ({:08X} to {:08X})\n", pifAddr, dramAddr);
} break;
case 0x04800010: {
pifAddr = val & 0x1FFFFFFF;
status.dmaBusy = true;
toDram = false;
scheduler.enqueueRelative({4065*3, DMA});
Util::debug("SI DMA from RDRAM to PIF RAM ({:08X} to {:08X})\n", dramAddr, pifAddr);
} break;
case 0x04800018:
InterruptLower(mem.mmio.mi, regs, Interrupt::SI);

View File

@@ -26,7 +26,6 @@ struct SI {
SIStatus status{};
u32 dramAddr{};
u32 pifAddr{};
Controller controller{};
bool toDram = false;