diff --git a/ultramodern/CMakeLists.txt b/ultramodern/CMakeLists.txt index 720b8cc..ac1662c 100644 --- a/ultramodern/CMakeLists.txt +++ b/ultramodern/CMakeLists.txt @@ -14,12 +14,15 @@ add_library(ultramodern STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/events.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/mesgqueue.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/misc_ultra.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/rsp.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/rt64_layer.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/scheduling.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/task_win32.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/threadqueue.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/threads.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/timer.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/ultrainit.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/user_callbacks.cpp" ) target_include_directories(ultramodern PUBLIC diff --git a/ultramodern/include/ultramodern/rsp_stuff.hpp b/ultramodern/include/ultramodern/rsp_stuff.hpp index 01123fe..8372990 100644 --- a/ultramodern/include/ultramodern/rsp_stuff.hpp +++ b/ultramodern/include/ultramodern/rsp_stuff.hpp @@ -5,6 +5,8 @@ #include +#include "ultra64.h" + // TODO: Move these to ultramodern namespace? enum class RspExitReason { @@ -21,4 +23,37 @@ extern uint8_t dmem[]; extern uint16_t rspReciprocals[512]; extern uint16_t rspInverseSquareRoots[512]; +namespace ultramodern { + namespace rsp { + struct rsp_callbacks_t { + /** + * 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 set_callbacks(const rsp_callbacks_t& callbacks); + + void constants_init(); + + RspUcodeFunc* get_microcode(uint32_t task_type, OSTask* task); + void run_microcode(uint8_t* rdram, const OSTask* task, RspUcodeFunc* ucode_func); + }; +} // namespace ultramodern + #endif diff --git a/ultramodern/include/ultramodern/ultramodern.hpp b/ultramodern/include/ultramodern/ultramodern.hpp index af2100d..785c4f3 100644 --- a/ultramodern/include/ultramodern/ultramodern.hpp +++ b/ultramodern/include/ultramodern/ultramodern.hpp @@ -151,6 +151,7 @@ struct gfx_callbacks_t { create_window_t* create_window; update_gfx_t* update_gfx; }; + bool is_game_started(); void quit(); void join_event_threads(); diff --git a/ultramodern/include/ultramodern/user_callbacks.hpp b/ultramodern/include/ultramodern/user_callbacks.hpp index 7a1229b..a3f5d0c 100644 --- a/ultramodern/include/ultramodern/user_callbacks.hpp +++ b/ultramodern/include/ultramodern/user_callbacks.hpp @@ -23,28 +23,6 @@ namespace ultramodern { * `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); }; /** diff --git a/ultramodern/src/events.cpp b/ultramodern/src/events.cpp index 7f299d9..48db4dc 100644 --- a/ultramodern/src/events.cpp +++ b/ultramodern/src/events.cpp @@ -193,45 +193,6 @@ void dp_complete() { osSendMesg(PASS_RDRAM events_context.dp.mq, events_context.dp.msg, OS_MESG_NOBLOCK); } -uint8_t dmem[0x1000]; -uint16_t rspReciprocals[512]; -uint16_t rspInverseSquareRoots[512]; - -// From Ares emulator. For license details, see rsp_vu.h -void rsp_constants_init() { - rspReciprocals[0] = u16(~0); - for (u16 index = 1; index < 512; index++) { - u64 a = index + 512; - u64 b = (u64(1) << 34) / a; - rspReciprocals[index] = u16((b + 1) >> 8); - } - - for (u16 index = 0; index < 512; index++) { - u64 a = (index + 512) >> ((index % 2 == 1) ? 1 : 0); - u64 b = 1 << 17; - //find the largest b where b < 1.0 / sqrt(a) - while (a * (b + 1) * (b + 1) < (u64(1) << 44)) b++; - rspInverseSquareRoots[index] = u16(b >> 1); - } -} - -// 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 - 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); -} - void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_ready) { ultramodern::set_native_thread_name("SP Task Thread"); ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Normal); @@ -251,11 +212,10 @@ void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_r } // 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); + RspUcodeFunc* ucode_func = ultramodern::rsp::get_microcode(task->t.type, task); if (ucode_func != nullptr) { - run_rsp_microcode(rdram, task, ucode_func); + ultramodern::rsp::run_microcode(rdram, task, ucode_func); } else { fprintf(stderr, "Unknown task type: %" PRIu32 "\n", task->t.type); @@ -330,7 +290,7 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re user_callbacks.update_supported_options(); } - rsp_constants_init(); + ultramodern::rsp::constants_init(); // Notify the caller thread that this thread is ready. thread_ready->signal(); diff --git a/ultramodern/src/rsp.cpp b/ultramodern/src/rsp.cpp new file mode 100644 index 0000000..346400e --- /dev/null +++ b/ultramodern/src/rsp.cpp @@ -0,0 +1,57 @@ +#include +#include + +#include "rsp_stuff.hpp" + + +static ultramodern::rsp::rsp_callbacks_t rsp_callbacks; + +void ultramodern::rsp::set_callbacks(const ultramodern::rsp::rsp_callbacks_t& callbacks) { + rsp_callbacks = callbacks; +} + + +uint8_t dmem[0x1000]; +uint16_t rspReciprocals[512]; +uint16_t rspInverseSquareRoots[512]; + +// From Ares emulator. For license details, see rsp_vu.h +void ultramodern::rsp::constants_init() { + rspReciprocals[0] = u16(~0); + for (u16 index = 1; index < 512; index++) { + u64 a = index + 512; + u64 b = (u64(1) << 34) / a; + rspReciprocals[index] = u16((b + 1) >> 8); + } + + for (u16 index = 0; index < 512; index++) { + u64 a = (index + 512) >> ((index % 2 == 1) ? 1 : 0); + u64 b = 1 << 17; + //find the largest b where b < 1.0 / sqrt(a) + while (a * (b + 1) * (b + 1) < (u64(1) << 44)) b++; + rspInverseSquareRoots[index] = u16(b >> 1); + } +} + +RspUcodeFunc* ultramodern::rsp::get_microcode(uint32_t task_type, OSTask* task) { + assert(rsp_callbacks.get_rsp_microcode != nullptr); + + return rsp_callbacks.get_rsp_microcode(task_type, task); +} + +// Runs a recompiled RSP microcode +void ultramodern::rsp::run_microcode(uint8_t* rdram, const OSTask* task, RspUcodeFunc* ucode_func) { + // Load the OSTask into DMEM + memcpy(&dmem[0xFC0], task, sizeof(OSTask)); + + assert(rsp_callbacks.dma_rdram_to_dmem != nullptr); + + // Load the ucode data into DMEM + rsp_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); +} + diff --git a/ultramodern/src/user_callbacks.cpp b/ultramodern/src/user_callbacks.cpp index 1ff201f..e2de5fb 100644 --- a/ultramodern/src/user_callbacks.cpp +++ b/ultramodern/src/user_callbacks.cpp @@ -10,17 +10,6 @@ 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; }