diff --git a/external/cic_nus_6105/n64_cic_nus_6105.cpp b/external/cic_nus_6105/n64_cic_nus_6105.cpp new file mode 100644 index 00000000..ced0f0dd --- /dev/null +++ b/external/cic_nus_6105/n64_cic_nus_6105.cpp @@ -0,0 +1,118 @@ +/* + * Copyright 2011 X-Scale. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY X-Scale ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL X-Scale OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of X-Scale. + * + * This software provides an algorithm that emulates the protection scheme of + * N64 PIF/CIC-NUS-6105, by determining the proper response to each challenge. + * It was synthesized after a careful, exhaustive and detailed analysis of the + * challenge/response pairs stored in the 'pif2.dat' file from Project 64. + * These challenge/response pairs were the only resource used during this + * project. There was no kind of physical access to N64 hardware. + * + * This project would have never been possible without the contribuitions of + * the following individuals and organizations: + * + * - Oman: For being at the right place at the right time and being brave + * enough to pay a personal price so we could understand in a much deeper + * way how this magical console really works. We owe you so much. + * + * - Jovis: For all the positive energy and impressive hacking spirit that you + * shared with the N64 community. You were absolutely instrumental in + * several key events that shaped the N64 community in the last 14 years. + * Even if you're not physically with us anymore, your heritage, your + * knowledge and your attitude will never be forgotten. + * + * 'The candle that burns twice as bright burns half as long.' + * + * - LaC: For the endless contributions that you've given to the N64 community + * since the early days, when N64 was the next big thing. I've always + * admired the deep knowledge that you've gathered about the most little + * hardware details. Recently, you challanged us to find a small and + * concise algorithm that would emulate the behaviour of CIC-NUS-6105 + * challenge/response protection scheme and here is the final result. + * LaC, Oman and Jovis were definitly the dream team of N64 reversing in + * the late 90's. Without your contributions, we would be much poorer. + * + * - marshall: For keeping the N64 scene alive during the last decade, when + * most people lost interest and moved along to different projects. You + * are the force that has been keeping us all together in the later + * years. When almost nobody cared about N64 anymore, you were always + * there, spreading the word, developing in the console, and lately, + * making impressive advances on the hardware side. I wish the best + * success to your new 64drive project. + * + * - hcs: For your contributions to the better understanding of the inner + * workings of the Reality Co-Processor (RCP). Your skills have impressed + * me for a long time now. And without your precious help by sharing your + * kownledge, I would have never understood the immense importance of + * Oman, Jovis and LaC achievements. Thank you ! + * + * - Azimer & Tooie: For sharing with the N64 community your findings about the + * challenge/response pair used in 'Jet Force Gemini' and the 267 + * challenge/response pairs used in 'Banjo Tooie', all stored in the + * 'pif2.dat' file of Project 64. They were instrumental to the final + * success of this endeavour. + * + * - Silicon Graphics, Inc. (SGI): For creating MIPS R4000, MIPS R4300 and + * Reality Co-Processor (RCP). You were the ultimate dream creator during + * the late 80's and early 90's. A very special word of gratitude goes to + * the two teams that during those years created RCP and MIPS R4300. They + * were technological breakthroughs back then. + * + * On a personal note, I would like to show my deepest gratitude to _Bijou_, + * for being always a source of endless hope and inspiration. + * + * -= X-Scale =- (#n64dev@EFnet) + */ + +#include "n64_cic_nus_6105.hpp" + +void n64_cic_nus_6105(char chl[], char rsp[], int len) +{ + static char lut0[0x10] = { + 0x4, 0x7, 0xA, 0x7, 0xE, 0x5, 0xE, 0x1, + 0xC, 0xF, 0x8, 0xF, 0x6, 0x3, 0x6, 0x9 + }; + static char lut1[0x10] = { + 0x4, 0x1, 0xA, 0x7, 0xE, 0x5, 0xE, 0x1, + 0xC, 0x9, 0x8, 0x5, 0x6, 0x3, 0xC, 0x9 + }; + char key, *lut; + int i, sgn, mag, mod; + + for (key = 0xB, lut = lut0, i = 0; i < len; i++) { + rsp[i] = (key + 5 * chl[i]) & 0xF; + key = lut[rsp[i]]; + sgn = (rsp[i] >> 3) & 0x1; + mag = ((sgn == 1) ? ~rsp[i] : rsp[i]) & 0x7; + mod = (mag % 3 == 1) ? sgn : 1 - sgn; + if (lut == lut1 && (rsp[i] == 0x1 || rsp[i] == 0x9)) + mod = 1; + if (lut == lut1 && (rsp[i] == 0xB || rsp[i] == 0xE)) + mod = 0; + lut = (mod == 1) ? lut1 : lut0; + } +} diff --git a/external/cic_nus_6105/n64_cic_nus_6105.hpp b/external/cic_nus_6105/n64_cic_nus_6105.hpp new file mode 100644 index 00000000..2136ab90 --- /dev/null +++ b/external/cic_nus_6105/n64_cic_nus_6105.hpp @@ -0,0 +1,95 @@ +/* + * Copyright 2011 X-Scale. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY X-Scale ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL X-Scale OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of X-Scale. + * + * This software provides an algorithm that emulates the protection scheme of + * N64 PIF/CIC-NUS-6105, by determining the proper response to each challenge. + * It was synthesized after a careful, exhaustive and detailed analysis of the + * challenge/response pairs stored in the 'pif2.dat' file from Project 64. + * These challenge/response pairs were the only resource used during this + * project. There was no kind of physical access to N64 hardware. + * + * This project would have never been possible without the contribuitions of + * the following individuals and organizations: + * + * - Oman: For being at the right place at the right time and being brave + * enough to pay a personal price so we could understand in a much deeper + * way how this magical console really works. We owe you so much. + * + * - Jovis: For all the positive energy and impressive hacking spirit that you + * shared with the N64 community. You were absolutely instrumental in + * several key events that shaped the N64 community in the last 14 years. + * Even if you're not physically with us anymore, your heritage, your + * knowledge and your attitude will never be forgotten. + * + * 'The candle that burns twice as bright burns half as long.' + * + * - LaC: For the endless contributions that you've given to the N64 community + * since the early days, when N64 was the next big thing. I've always + * admired the deep knowledge that you've gathered about the most little + * hardware details. Recently, you challanged us to find a small and + * concise algorithm that would emulate the behaviour of CIC-NUS-6105 + * challenge/response protection scheme and here is the final result. + * LaC, Oman and Jovis were definitly the dream team of N64 reversing in + * the late 90's. Without your contributions, we would be much poorer. + * + * - marshall: For keeping the N64 scene alive during the last decade, when + * most people lost interest and moved along to different projects. You + * are the force that has been keeping us all together in the later + * years. When almost nobody cared about N64 anymore, you were always + * there, spreading the word, developing in the console, and lately, + * making impressive advances on the hardware side. I wish the best + * success to your new 64drive project. + * + * - hcs: For your contributions to the better understanding of the inner + * workings of the Reality Co-Processor (RCP). Your skills have impressed + * me for a long time now. And without your precious help by sharing your + * kownledge, I would have never understood the immense importance of + * Oman, Jovis and LaC achievements. Thank you ! + * + * - Azimer & Tooie: For sharing with the N64 community your findings about the + * challenge/response pair used in 'Jet Force Gemini' and the 267 + * challenge/response pairs used in 'Banjo Tooie', all stored in the + * 'pif2.dat' file of Project 64. They were instrumental to the final + * success of this endeavour. + * + * - Silicon Graphics, Inc. (SGI): For creating MIPS R4000, MIPS R4300 and + * Reality Co-Processor (RCP). You were the ultimate dream creator during + * the late 80's and early 90's. A very special word of gratitude goes to + * the two teams that during those years created RCP and MIPS R4300. They + * were technological breakthroughs back then. + * + * On a personal note, I would like to show my deepest gratitude to _Bijou_, + * for being always a source of endless hope and inspiration. + * + * -= X-Scale =- (#n64dev@EFnet) + */ + +#pragma once + +#define CHL_LEN 0x20 + +void n64_cic_nus_6105(char chl[], char rsp[], int len); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a7953d83..491fc499 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -38,8 +38,6 @@ include_directories( ${SDL2_INCLUDE_DIRS} ) -set(NFD_PORTAL ON CACHE BOOL "Use dbus for native file dialog instead of gtk") - add_compile_definitions(SIMD_SUPPORT) add_compile_options(-mssse3 -msse4.1) diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index 04ebd54d..efa61d31 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -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); } diff --git a/src/backend/MupenMovie.cpp b/src/backend/MupenMovie.cpp index 0093ea33..524de64c 100644 --- a/src/backend/MupenMovie.cpp +++ b/src/backend/MupenMovie.cpp @@ -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{}; diff --git a/src/backend/MupenMovie.hpp b/src/backend/MupenMovie.hpp index edac585b..5cf8f79e 100644 --- a/src/backend/MupenMovie.hpp +++ b/src/backend/MupenMovie.hpp @@ -3,5 +3,5 @@ void LoadTAS(const char* filename); void UnloadTAS(); -n64::Controller tas_next_inputs(); -bool tas_movie_loaded(); \ No newline at end of file +n64::Controller TasNextInputs(); +bool TasMovieLoaded(); \ No newline at end of file diff --git a/src/backend/core/mmio/CMakeLists.txt b/src/backend/core/mmio/CMakeLists.txt index 6551e557..0a02854f 100644 --- a/src/backend/core/mmio/CMakeLists.txt +++ b/src/backend/core/mmio/CMakeLists.txt @@ -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}) \ No newline at end of file +add_library(mmio ${SOURCES} ${HEADERS} ../../../../external/cic_nus_6105/n64_cic_nus_6105.cpp) \ No newline at end of file diff --git a/src/backend/core/mmio/PIF.cpp b/src/backend/core/mmio/PIF.cpp index 94f8aa15..cc7fb4cc 100644 --- a/src/backend/core/mmio/PIF.cpp +++ b/src/backend/core/mmio/PIF.cpp @@ -2,84 +2,98 @@ #include #include #include -#include #include +#include +#include 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(GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_LEFTX), -86, 86); + yaxis = (s8) std::clamp(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; } } } diff --git a/src/backend/core/mmio/PIF.hpp b/src/backend/core/mmio/PIF.hpp index 2f89c7a4..d92ac552 100644 --- a/src/backend/core/mmio/PIF.hpp +++ b/src/backend/core/mmio/PIF.hpp @@ -1,9 +1,16 @@ #pragma once #include #include +#include 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; } }; } \ No newline at end of file diff --git a/src/backend/core/mmio/PIF/Device.cpp b/src/backend/core/mmio/PIF/Device.cpp new file mode 100644 index 00000000..13c62c64 --- /dev/null +++ b/src/backend/core/mmio/PIF/Device.cpp @@ -0,0 +1,125 @@ +#include +#include +#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! +} +} \ No newline at end of file diff --git a/src/backend/core/mmio/SI.cpp b/src/backend/core/mmio/SI.cpp index 1d08a609..d6b49578 100644 --- a/src/backend/core/mmio/SI.cpp +++ b/src/backend/core/mmio/SI.cpp @@ -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); diff --git a/src/backend/core/mmio/SI.hpp b/src/backend/core/mmio/SI.hpp index c7358c24..11c2a2bf 100644 --- a/src/backend/core/mmio/SI.hpp +++ b/src/backend/core/mmio/SI.hpp @@ -26,7 +26,6 @@ struct SI { SIStatus status{}; u32 dramAddr{}; u32 pifAddr{}; - Controller controller{}; bool toDram = false;