move RSP callbacks (and related RSP stuff) to its own file

This commit is contained in:
Angie 2024-05-22 23:28:53 -04:00 committed by angie
parent 57b83acb69
commit 0da09d45c1
7 changed files with 99 additions and 76 deletions

View file

@ -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

View file

@ -5,6 +5,8 @@
#include <cstdint>
#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

View file

@ -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();

View file

@ -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);
};
/**

View file

@ -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();

57
ultramodern/src/rsp.cpp Normal file
View file

@ -0,0 +1,57 @@
#include <cassert>
#include <cstring>
#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);
}

View file

@ -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;
}