Move most RSP stuff to librecomp

This commit is contained in:
Angie 2024-05-25 19:22:53 -04:00 committed by angie
parent 06a8c5070a
commit 63d0c7f85a
9 changed files with 131 additions and 94 deletions

View file

@ -5,6 +5,7 @@
#include <filesystem>
#include "recomp.h"
#include "rsp.h"
#include <ultramodern/ultramodern.hpp>
namespace recomp {
@ -38,7 +39,7 @@ namespace recomp {
void set_rom_contents(std::vector<uint8_t>&& new_rom);
void do_rom_read(uint8_t* rdram, gpr ram_address, uint32_t physical_addr, size_t num_bytes);
void do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr);
void start(ultramodern::WindowHandle window_handle, const ultramodern::audio_callbacks_t& audio_callbacks, const ultramodern::input_callbacks_t& input_callbacks, const ultramodern::gfx_callbacks_t& gfx_callbacks, const ultramodern::rsp::callbacks_t& rsp_callbacks, const ultramodern::events::callbacks_t& thread_callbacks, const ultramodern::error_handling::callbacks_t& error_handling_callbacks_);
void start(ultramodern::WindowHandle window_handle, const recomp::rsp::callbacks_t& rsp_callbacks, const ultramodern::audio_callbacks_t& audio_callbacks, const ultramodern::input_callbacks_t& input_callbacks, const ultramodern::gfx_callbacks_t& gfx_callbacks, const ultramodern::events::callbacks_t& thread_callbacks, const ultramodern::error_handling::callbacks_t& error_handling_callbacks_);
void start_game(const std::u8string& game_id);
void message_box(const char* message);
std::filesystem::path get_app_folder_path();

View file

@ -5,7 +5,23 @@
#include "rsp_vu.h"
#include "recomp.h"
#include "ultramodern/rsp_stuff.hpp"
#include "ultramodern/ultra64.h"
// TODO: Move these to recomp 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];
#define RSP_MEM_B(offset, addr) \
(*reinterpret_cast<int8_t*>(dmem + (0xFFF & (((offset) + (addr)) ^ 3))))
@ -81,4 +97,27 @@ static inline void dma_dmem_to_rdram(uint8_t* rdram, uint32_t dmem_addr, uint32_
}
}
namespace recomp {
namespace rsp {
struct callbacks_t {
using get_rsp_microcode_t = RspUcodeFunc*(const OSTask* task);
/**
* 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.
*/
get_rsp_microcode_t* get_rsp_microcode;
};
void set_callbacks(const callbacks_t& callbacks);
void constants_init();
bool run_microcode(uint8_t* rdram, const OSTask* task);
}
}
#endif

View file

@ -425,10 +425,15 @@ void ultramodern::quit() {
current_game.reset();
}
void recomp::start(ultramodern::WindowHandle window_handle, const ultramodern::audio_callbacks_t& audio_callbacks, const ultramodern::input_callbacks_t& input_callbacks, const ultramodern::gfx_callbacks_t& gfx_callbacks_, const ultramodern::rsp::callbacks_t& rsp_callbacks_, const ultramodern::events::callbacks_t& thread_callbacks_, const ultramodern::error_handling::callbacks_t& error_handling_callbacks_) {
void recomp::start(ultramodern::WindowHandle window_handle, const recomp::rsp::callbacks_t& rsp_callbacks, const ultramodern::audio_callbacks_t& audio_callbacks, const ultramodern::input_callbacks_t& input_callbacks, const ultramodern::gfx_callbacks_t& gfx_callbacks_, const ultramodern::events::callbacks_t& thread_callbacks_, const ultramodern::error_handling::callbacks_t& error_handling_callbacks_) {
recomp::check_all_stored_roms();
ultramodern::set_callbacks(audio_callbacks, input_callbacks, gfx_callbacks_, rsp_callbacks_, thread_callbacks_, error_handling_callbacks_);
recomp::rsp::set_callbacks(rsp_callbacks);
ultramodern::set_callbacks(ultramodern::rsp::callbacks_t {
.init = recomp::rsp::constants_init,
.run_microcode = recomp::rsp::run_microcode,
}, audio_callbacks, input_callbacks, gfx_callbacks_, thread_callbacks_, error_handling_callbacks_);
set_input_callbacks(input_callbacks);

61
librecomp/src/rsp.cpp Normal file
View file

@ -0,0 +1,61 @@
#include <cassert>
#include <cstring>
#include <cinttypes>
#include "rsp.h"
static recomp::rsp::callbacks_t rsp_callbacks {};
void recomp::rsp::set_callbacks(const 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 recomp::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
bool recomp::rsp::run_microcode(uint8_t* rdram, const OSTask* task) {
assert(rsp_callbacks.get_rsp_microcode != nullptr);
RspUcodeFunc* ucode_func = rsp_callbacks.get_rsp_microcode(task);
if (ucode_func == nullptr) {
return false;
}
// Load the OSTask into DMEM
memcpy(&dmem[0xFC0], task, sizeof(OSTask));
// Load the ucode data into DMEM
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
if (exit_reason != RspExitReason::Broke) {
fprintf(stderr, "RSP ucode %" PRIu32 " exited unexpectedly. exit_reason: %i\n", task->t.type, exit_reason);
assert(exit_reason == RspExitReason::Broke);
return false;
}
return true;
}

View file

@ -9,49 +9,20 @@
// 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];
namespace ultramodern {
namespace rsp {
struct callbacks_t {
using dma_rdram_to_dmem_t = void(uint8_t* rdram, uint32_t dmem_addr, uint32_t dram_addr, uint32_t rd_len);
using get_rsp_microcode_t = RspUcodeFunc*(uint32_t task_type, OSTask* task);
using init_t = void();
using run_microcode_t = bool(RDRAM_ARG const OSTask* task);
/**
* Simulate a DMA copy from RDRAM (CPU) to DMEM (RSP).
*
* This function should fill the ultramodern's `dmem` by reading from the `rdram` parameter.
*/
dma_rdram_to_dmem_t* dma_rdram_to_dmem;
/**
* 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.
*/
get_rsp_microcode_t* get_rsp_microcode;
init_t* init;
run_microcode_t* run_microcode;
};
void set_callbacks(const 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);
void init();
bool run_microcode(RDRAM_ARG const OSTask* task);
};
} // namespace ultramodern

View file

@ -173,11 +173,11 @@ void join_saving_thread();
void set_audio_callbacks(const audio_callbacks_t& callbacks);
/**
* Register all the callbacks required by `ultramodern`.
* Register all the callbacks used by `ultramodern`, most of them being optional.
*
* It must be called only once and it must be called before `ultramodern::preinit`.
*/
void set_callbacks(const audio_callbacks_t& audio_callbacks, const input_callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks_, const rsp::callbacks_t& rsp_callbacks_, const events::callbacks_t& thread_callbacks, const error_handling::callbacks_t& error_handling_callbacks);
void set_callbacks(const rsp::callbacks_t& rsp_callbacks, const audio_callbacks_t& audio_callbacks, const input_callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks, const events::callbacks_t& thread_callbacks, const error_handling::callbacks_t& error_handling_callbacks);
} // namespace ultramodern
#define MIN(a, b) ((a) < (b) ? (a) : (b))

View file

@ -213,14 +213,9 @@ void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_r
return;
}
// Ask the user what the correct ucode function is this.
RspUcodeFunc* ucode_func = ultramodern::rsp::get_microcode(task->t.type, task);
if (ucode_func != nullptr) {
ultramodern::rsp::run_microcode(rdram, task, ucode_func);
}
else {
fprintf(stderr, "Unknown task type: %" PRIu32 "\n", task->t.type);
if (!ultramodern::rsp::run_microcode(PASS_RDRAM task)) {
fprintf(stderr, "Failed to execute task type: %" PRIu32 "\n", task->t.type);
assert(false);
std::quick_exit(EXIT_FAILURE);
}
@ -290,7 +285,7 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re
threads_callbacks.gfx_init_callback();
}
ultramodern::rsp::constants_init();
ultramodern::rsp::init();
// Notify the caller thread that this thread is ready.
thread_ready->signal();

View file

@ -3,55 +3,20 @@
#include "rsp_stuff.hpp"
static ultramodern::rsp::callbacks_t rsp_callbacks {};
static ultramodern::rsp::callbacks_t rsp_callbacks;
void ultramodern::rsp::set_callbacks(const ultramodern::rsp::callbacks_t& callbacks) {
void ultramodern::rsp::set_callbacks(const 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);
void ultramodern::rsp::init() {
if (rsp_callbacks.init != nullptr) {
rsp_callbacks.init();
}
}
RspUcodeFunc* ultramodern::rsp::get_microcode(uint32_t task_type, OSTask* task) {
assert(rsp_callbacks.get_rsp_microcode != nullptr);
bool ultramodern::rsp::run_microcode(RDRAM_ARG const OSTask* task) {
assert(rsp_callbacks.run_microcode != nullptr);
return rsp_callbacks.get_rsp_microcode(task_type, task);
return rsp_callbacks.run_microcode(PASS_RDRAM 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

@ -2,17 +2,17 @@
#include "ultramodern.hpp"
void ultramodern::set_callbacks(
const rsp::callbacks_t& rsp_callbacks,
const audio_callbacks_t& audio_callbacks,
const input_callbacks_t& input_callbacks,
const gfx_callbacks_t& gfx_callbacks,
const rsp::callbacks_t& rsp_callbacks,
const events::callbacks_t& thread_callbacks,
const error_handling::callbacks_t& error_handling_callbacks
) {
ultramodern::rsp::set_callbacks(rsp_callbacks);
ultramodern::set_audio_callbacks(audio_callbacks);
(void)input_callbacks; // nothing yet
(void)gfx_callbacks; // nothing yet
ultramodern::rsp::set_callbacks(rsp_callbacks);
ultramodern::events::set_callbacks(thread_callbacks);
ultramodern::error_handling::set_callbacks(error_handling_callbacks);
}