diff --git a/librecomp/include/rsp.h b/librecomp/include/rsp.h index 17f8f90..a6b7107 100644 --- a/librecomp/include/rsp.h +++ b/librecomp/include/rsp.h @@ -1,21 +1,11 @@ #ifndef __RSP_H__ #define __RSP_H__ -#include "rsp_vu.h" -#include "recomp.h" #include -enum class RspExitReason { - Invalid, - Broke, - ImemOverrun, - UnhandledJumpTarget, - Unsupported -}; - -extern uint8_t dmem[]; -extern uint16_t rspReciprocals[512]; -extern uint16_t rspInverseSquareRoots[512]; +#include "rsp_vu.h" +#include "recomp.h" +#include "ultramodern/rsp_stuff.hpp" #define RSP_MEM_B(offset, addr) \ (*reinterpret_cast(dmem + (0xFFF & (((offset) + (addr)) ^ 3)))) @@ -61,7 +51,7 @@ static inline void RSP_MEM_H_STORE(uint32_t offset, uint32_t addr, uint32_t val) #define RSP_ADD32(a, b) \ ((int32_t)((a) + (b))) - + #define RSP_SUB32(a, b) \ ((int32_t)((a) - (b))) diff --git a/ultramodern/include/ultramodern/rsp_stuff.hpp b/ultramodern/include/ultramodern/rsp_stuff.hpp new file mode 100644 index 0000000..01123fe --- /dev/null +++ b/ultramodern/include/ultramodern/rsp_stuff.hpp @@ -0,0 +1,24 @@ +#ifndef __RSP_STUFF_HPP__ +#define __RSP_STUFF_HPP__ + +// TODO: rename + +#include + +// TODO: Move these to ultramodern namespace? + +enum class RspExitReason { + Invalid, + Broke, + ImemOverrun, + UnhandledJumpTarget, + Unsupported +}; + +using RspUcodeFunc = RspExitReason(uint8_t* rdram); + +extern uint8_t dmem[]; +extern uint16_t rspReciprocals[512]; +extern uint16_t rspInverseSquareRoots[512]; + +#endif diff --git a/ultramodern/include/ultramodern/user_callbacks.hpp b/ultramodern/include/ultramodern/user_callbacks.hpp index 6c702b7..7a1229b 100644 --- a/ultramodern/include/ultramodern/user_callbacks.hpp +++ b/ultramodern/include/ultramodern/user_callbacks.hpp @@ -1,6 +1,9 @@ #ifndef __USER_CALLBACKS_HPP__ #define __USER_CALLBACKS_HPP__ +#include "rsp_stuff.hpp" +#include "ultra64.h" + namespace ultramodern { struct UserCallbacks { // TODO: Do we want those functions to take a generic `void *arg` for user data? @@ -17,12 +20,41 @@ namespace ultramodern { /** * Show an OS dialog with the given `msg`. - * `msg` is non-NULL. + * `msg` is non-`nullptr`. */ void (*message_box)(const char* msg); + + // RSP + + /** + * Simulate a DMA copy from RDRAM (CPU) to DMEM (RSP). + * + * This function should fill the ultramodern's `dmem` by reading from the `rdram` parameter. + * + * IMPORTANTE: This callback is required and must be non-`nullptr` when initializing the user callbacks. + */ + void (*dma_rdram_to_dmem)(uint8_t* rdram, uint32_t dmem_addr, uint32_t dram_addr, uint32_t rd_len); + + /** + * Return a function pointer to the corresponding RSP microcode function for the given `task_type`. + * + * The full OSTask (`task` parameter) is passed in case the `task_type` number is not enough information to distinguish out the exact microcode function. + * + * This function is allowed to return `nullptr` if no microcode matches the specified task. In this case a message will be printed to stderr and the program will exit. + * + * IMPORTANTE: This callback is required and must be non-`nullptr` when initializing the user callbacks. + */ + RspUcodeFunc* (*get_rsp_microcode)(uint32_t task_type, OSTask* task); }; + /** + * + */ void register_user_callbacks(UserCallbacks& callbacks); + + /** + * + */ const UserCallbacks& get_user_callbacks(); }; diff --git a/ultramodern/src/events.cpp b/ultramodern/src/events.cpp index 5afd05b..7f299d9 100644 --- a/ultramodern/src/events.cpp +++ b/ultramodern/src/events.cpp @@ -16,6 +16,7 @@ #include "config.hpp" #include "rt64_layer.h" #include "user_callbacks.hpp" +#include "rsp_stuff.hpp" struct SpTaskAction { OSTask task; @@ -196,12 +197,6 @@ uint8_t dmem[0x1000]; uint16_t rspReciprocals[512]; uint16_t rspInverseSquareRoots[512]; -#if 0 -using RspUcodeFunc = RspExitReason(uint8_t* rdram); -extern RspUcodeFunc njpgdspMain; -extern RspUcodeFunc aspMain; -#endif - // From Ares emulator. For license details, see rsp_vu.h void rsp_constants_init() { rspReciprocals[0] = u16(~0); @@ -219,19 +214,23 @@ void rsp_constants_init() { rspInverseSquareRoots[index] = u16(b >> 1); } } -#if 0 + // Runs a recompiled RSP microcode void run_rsp_microcode(uint8_t* rdram, const OSTask* task, RspUcodeFunc* ucode_func) { // Load the OSTask into DMEM memcpy(&dmem[0xFC0], task, sizeof(OSTask)); + + auto& user_callbacks = ultramodern::get_user_callbacks(); + assert(user_callbacks.dma_rdram_to_dmem != nullptr); + // Load the ucode data into DMEM - dma_rdram_to_dmem(rdram, 0x0000, task->t.ucode_data, 0xF80 - 1); + user_callbacks.dma_rdram_to_dmem(rdram, 0x0000, task->t.ucode_data, 0xF80 - 1); + // Run the ucode RspExitReason exit_reason = ucode_func(rdram); // Ensure that the ucode exited correctly assert(exit_reason == RspExitReason::Broke); } -#endif void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_ready) { ultramodern::set_native_thread_name("SP Task Thread"); @@ -240,6 +239,8 @@ void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_r // Notify the caller thread that this thread is ready. thread_ready->signal(); + auto& user_callbacks = ultramodern::get_user_callbacks(); + while (true) { // Wait until an RSP task has been sent OSTask* task; @@ -249,16 +250,12 @@ void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_r return; } - // Run the correct function based on the task type - if (task->t.type == M_AUDTASK) { - #if 0 - run_rsp_microcode(rdram, task, aspMain); - #endif - } - else if (task->t.type == M_NJPEGTASK) { - #if 0 - run_rsp_microcode(rdram, task, njpgdspMain); - #endif + // Ask the user what the correct ucode function is this. + assert(user_callbacks.get_rsp_microcode != nullptr); + RspUcodeFunc* ucode_func = user_callbacks.get_rsp_microcode(task->t.type, task); + + if (ucode_func != nullptr) { + run_rsp_microcode(rdram, task, ucode_func); } else { fprintf(stderr, "Unknown task type: %" PRIu32 "\n", task->t.type); diff --git a/ultramodern/src/user_callbacks.cpp b/ultramodern/src/user_callbacks.cpp index 677e012..1ff201f 100644 --- a/ultramodern/src/user_callbacks.cpp +++ b/ultramodern/src/user_callbacks.cpp @@ -1,11 +1,35 @@ +#include +#include +#include + #include "ultramodern/user_callbacks.hpp" static ultramodern::UserCallbacks s_user_callbacks {}; +static bool s_callbacks_initialized = false; void ultramodern::register_user_callbacks(UserCallbacks& callbacks) { s_user_callbacks = callbacks; + + if (s_user_callbacks.dma_rdram_to_dmem == nullptr) { + fprintf(stderr, "%s: `dma_rdram_to_dmem` is a required callback, it can't be `nullptr`\n", __func__); + assert(false); + std::quick_exit(EXIT_FAILURE); + } + if (s_user_callbacks.get_rsp_microcode == nullptr) { + fprintf(stderr, "%s: `get_rsp_microcode` is a required callback, it can't be `nullptr`\n", __func__); + assert(false); + std::quick_exit(EXIT_FAILURE); + } + + s_callbacks_initialized = true; } const ultramodern::UserCallbacks& ultramodern::get_user_callbacks() { + if (!s_callbacks_initialized) { + fprintf(stderr, "%s: User callbacks have not been initialized.\n", __func__); + assert(false); + std::quick_exit(EXIT_FAILURE); + } + return s_user_callbacks; }