Removed use of std::exit and changed recomp runtime to exit normally, added helpers for getting recompiled args and returning values, added example of patch code calling native code
This commit is contained in:
parent
398988a961
commit
8188aee2c1
|
@ -0,0 +1,46 @@
|
||||||
|
#ifndef __RECOMP_HELPERS__
|
||||||
|
#define __RECOMP_HELPERS__
|
||||||
|
|
||||||
|
#include "recomp.h"
|
||||||
|
|
||||||
|
template<int index, typename T>
|
||||||
|
T _arg(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
static_assert(index < 4, "Only args 0 through 3 supported");
|
||||||
|
gpr raw_arg = (&ctx->r4)[index];
|
||||||
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
|
if constexpr (index < 2) {
|
||||||
|
static_assert(index != 1, "Floats in arg 1 not supported");
|
||||||
|
return ctx->f12.fl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return std::bit_cast<T>(raw_arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_pointer_v<T>) {
|
||||||
|
static_assert (!std::is_pointer_v<std::remove_pointer_t<T>>, "Double pointers not supported");
|
||||||
|
return TO_PTR(std::remove_pointer_t<T>, raw_arg);
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_integral_v<T>) {
|
||||||
|
static_assert(sizeof(T) <= 4, "64-bit args not supported");
|
||||||
|
return static_cast<T>(raw_arg);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// static_assert in else workaround
|
||||||
|
[] <bool flag = false>() {
|
||||||
|
static_assert(flag, "Unsupported type");
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void _return(recomp_context* ctx, T val) {
|
||||||
|
static_assert(sizeof(T) <= 4 && "Only 32-bit value returns supported currently");
|
||||||
|
if (std::is_same_v<T, float>) {
|
||||||
|
ctx->f0.fl = val;
|
||||||
|
}
|
||||||
|
else if (std::is_integral_v<T> && sizeof(T) <= 4) {
|
||||||
|
ctx->r2 = int32_t(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -21,5 +21,6 @@ enum class Menu {
|
||||||
};
|
};
|
||||||
|
|
||||||
void set_current_menu(Menu menu);
|
void set_current_menu(Menu menu);
|
||||||
|
void destroy_ui();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -76,6 +76,7 @@ DLLIMPORT void ProcessRDPList(void);
|
||||||
DLLIMPORT void ProcessDList(void);
|
DLLIMPORT void ProcessDList(void);
|
||||||
DLLIMPORT void UpdateScreen(void);
|
DLLIMPORT void UpdateScreen(void);
|
||||||
DLLIMPORT void ChangeWindow(void);
|
DLLIMPORT void ChangeWindow(void);
|
||||||
|
DLLIMPORT void PluginShutdown(void);
|
||||||
|
|
||||||
void set_rt64_hooks();
|
void set_rt64_hooks();
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ LD := ld.lld
|
||||||
OBJCOPY := llvm-objcopy
|
OBJCOPY := llvm-objcopy
|
||||||
|
|
||||||
CFLAGS := -target mips -mips2 -mabi=32 -O2 -mno-odd-spreg -fomit-frame-pointer -G0 -Wall -Wextra -Wno-incompatible-library-redeclaration -Wno-unused-parameter -Wno-unknown-pragmas -Wno-unused-variable
|
CFLAGS := -target mips -mips2 -mabi=32 -O2 -mno-odd-spreg -fomit-frame-pointer -G0 -Wall -Wextra -Wno-incompatible-library-redeclaration -Wno-unused-parameter -Wno-unknown-pragmas -Wno-unused-variable
|
||||||
CPPFLAGS := -nostdinc -D_LANGUAGE_C -I ../../mm/include -I ../../mm/src -I ../../mm/build -I ../../mm/assets
|
CPPFLAGS := -nostdinc -D_LANGUAGE_C -DMIPS -I ../../mm/include -I ../../mm/src -I ../../mm/build -I ../../mm/assets
|
||||||
LDFLAGS := -nostdlib -T patches.ld -T syms.ld
|
LDFLAGS := -nostdlib -T patches.ld -T syms.ld
|
||||||
BINFLAGS := -O binary
|
BINFLAGS := -O binary
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#define Audio_PlaySfx play_sound
|
#include "patches.h"
|
||||||
#include "global.h"
|
|
||||||
|
|
||||||
// Infinite magic
|
// Infinite magic
|
||||||
s32 Magic_Consume(PlayState* play, s16 magicToConsume, s16 type) {
|
s32 Magic_Consume(PlayState* play, s16 magicToConsume, s16 type) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "global.h"
|
#include "patches.h"
|
||||||
|
|
||||||
// Disable frustum culling for actors, but leave distance culling intact
|
// Disable frustum culling for actors, but leave distance culling intact
|
||||||
s32 func_800BA2FC(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projectedW) {
|
s32 func_800BA2FC(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projectedW) {
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
#include "patches.h"
|
||||||
|
#include "input.h"
|
||||||
|
|
||||||
|
u32 sPlayerItemButtons[] = {
|
||||||
|
BTN_B,
|
||||||
|
BTN_CLEFT,
|
||||||
|
BTN_CDOWN,
|
||||||
|
BTN_CRIGHT,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return currently-pressed button, in order of priority B, CLEFT, CDOWN, CRIGHT.
|
||||||
|
EquipSlot func_8082FDC4(void) {
|
||||||
|
EquipSlot i;
|
||||||
|
RecompInputs cur_inputs;
|
||||||
|
recomp_get_item_inputs(&cur_inputs);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_COUNT(sPlayerItemButtons); i++) {
|
||||||
|
if (CHECK_BTN_ALL(cur_inputs.buttons, sPlayerItemButtons[i])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef __INPUT_H__
|
||||||
|
#define __INPUT_H__
|
||||||
|
|
||||||
|
#ifdef MIPS
|
||||||
|
#include "ultra64.h"
|
||||||
|
#else
|
||||||
|
#include "recomp.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct RecompInputs {
|
||||||
|
u32 buttons;
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
} RecompInputs;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef MIPS
|
||||||
|
# define DECLARE_FUNC(type, name, ...) \
|
||||||
|
type name(__VA_ARGS__);
|
||||||
|
#else
|
||||||
|
# define DECLARE_FUNC(type, name, ...) \
|
||||||
|
void name(uint8_t* rdram, recomp_context* ctx);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DECLARE_FUNC(void, recomp_get_item_inputs, RecompInputs* inputs);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef __PATCHES_H__
|
||||||
|
#define __PATCHES_H__
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,7 +1,13 @@
|
||||||
__start = 0x80000000;
|
__start = 0x80000000;
|
||||||
|
|
||||||
|
/* Dummy addresses that get recompiled into function calls */
|
||||||
|
recomp_get_item_inputs = 0x81000000;
|
||||||
|
|
||||||
/* TODO pull these symbols from the elf file directly */
|
/* TODO pull these symbols from the elf file directly */
|
||||||
Player_PostLimbDrawGameplay = 0x80128BD0;
|
Player_PostLimbDrawGameplay = 0x80128BD0;
|
||||||
Player_DrawImpl = 0x801246F4;
|
Player_DrawImpl = 0x801246F4;
|
||||||
gRegEditor = 0x801f3f60;
|
gRegEditor = 0x801f3f60;
|
||||||
play_sound = 0x8019f0c8;
|
Audio_PlaySfx = 0x8019f0c8;
|
||||||
gSaveContext = 0x801ef670;
|
gSaveContext = 0x801ef670;
|
||||||
|
Interface_SetHudVisibility = 0x8010ef68;
|
||||||
|
Player_GetItemOnButton = 0x8012364C;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "ultra64.h"
|
#include "ultra64.h"
|
||||||
#include "multilibultra.hpp"
|
#include "multilibultra.hpp"
|
||||||
#include "recomp.h"
|
#include "recomp.h"
|
||||||
|
#include "recomp_ui.h"
|
||||||
#include "rsp.h"
|
#include "rsp.h"
|
||||||
|
|
||||||
struct SpTaskAction {
|
struct SpTaskAction {
|
||||||
|
@ -42,17 +43,14 @@ static struct {
|
||||||
OSMesg msg = (OSMesg)0;
|
OSMesg msg = (OSMesg)0;
|
||||||
} sp;
|
} sp;
|
||||||
struct {
|
struct {
|
||||||
std::thread thread;
|
|
||||||
PTR(OSMesgQueue) mq = NULLPTR;
|
PTR(OSMesgQueue) mq = NULLPTR;
|
||||||
OSMesg msg = (OSMesg)0;
|
OSMesg msg = (OSMesg)0;
|
||||||
} dp;
|
} dp;
|
||||||
struct {
|
struct {
|
||||||
std::thread thread;
|
|
||||||
PTR(OSMesgQueue) mq = NULLPTR;
|
PTR(OSMesgQueue) mq = NULLPTR;
|
||||||
OSMesg msg = (OSMesg)0;
|
OSMesg msg = (OSMesg)0;
|
||||||
} ai;
|
} ai;
|
||||||
struct {
|
struct {
|
||||||
std::thread thread;
|
|
||||||
PTR(OSMesgQueue) mq = NULLPTR;
|
PTR(OSMesgQueue) mq = NULLPTR;
|
||||||
OSMesg msg = (OSMesg)0;
|
OSMesg msg = (OSMesg)0;
|
||||||
} si;
|
} si;
|
||||||
|
@ -95,6 +93,9 @@ extern "C" void osViSetEvent(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, u32 ret
|
||||||
|
|
||||||
uint64_t total_vis = 0;
|
uint64_t total_vis = 0;
|
||||||
|
|
||||||
|
|
||||||
|
extern std::atomic_bool exited;
|
||||||
|
|
||||||
void set_dummy_vi();
|
void set_dummy_vi();
|
||||||
|
|
||||||
void vi_thread_func() {
|
void vi_thread_func() {
|
||||||
|
@ -106,7 +107,7 @@ void vi_thread_func() {
|
||||||
|
|
||||||
int remaining_retraces = events_context.vi.retrace_count;
|
int remaining_retraces = events_context.vi.retrace_count;
|
||||||
|
|
||||||
while (true) {
|
while (!exited) {
|
||||||
// Determine the next VI time (more accurate than adding 16ms each VI interrupt)
|
// Determine the next VI time (more accurate than adding 16ms each VI interrupt)
|
||||||
auto next = Multilibultra::get_start() + (total_vis * 1000000us) / (60 * Multilibultra::get_speed_multiplier());
|
auto next = Multilibultra::get_start() + (total_vis * 1000000us) / (60 * Multilibultra::get_speed_multiplier());
|
||||||
//if (next > std::chrono::system_clock::now()) {
|
//if (next > std::chrono::system_clock::now()) {
|
||||||
|
@ -177,6 +178,7 @@ void RT64Init(uint8_t* rom, uint8_t* rdram, Multilibultra::WindowHandle window_h
|
||||||
void RT64SendDL(uint8_t* rdram, const OSTask* task);
|
void RT64SendDL(uint8_t* rdram, const OSTask* task);
|
||||||
void RT64UpdateScreen(uint32_t vi_origin);
|
void RT64UpdateScreen(uint32_t vi_origin);
|
||||||
void RT64ChangeWindow();
|
void RT64ChangeWindow();
|
||||||
|
void RT64Shutdown();
|
||||||
|
|
||||||
uint8_t dmem[0x1000];
|
uint8_t dmem[0x1000];
|
||||||
uint16_t rspReciprocals[512];
|
uint16_t rspReciprocals[512];
|
||||||
|
@ -225,10 +227,14 @@ void task_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_rea
|
||||||
thread_ready->test_and_set();
|
thread_ready->test_and_set();
|
||||||
thread_ready->notify_all();
|
thread_ready->notify_all();
|
||||||
|
|
||||||
while (1) {
|
while (true) {
|
||||||
// Wait until an RSP task has been sent
|
// Wait until an RSP task has been sent
|
||||||
events_context.sp_task.wait(nullptr);
|
events_context.sp_task.wait(nullptr);
|
||||||
|
|
||||||
|
if (exited) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve the task pointer and clear the pending RSP task
|
// Retrieve the task pointer and clear the pending RSP task
|
||||||
OSTask* task = events_context.sp_task;
|
OSTask* task = events_context.sp_task;
|
||||||
events_context.sp_task.store(nullptr);
|
events_context.sp_task.store(nullptr);
|
||||||
|
@ -265,7 +271,7 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read
|
||||||
thread_ready->test_and_set();
|
thread_ready->test_and_set();
|
||||||
thread_ready->notify_all();
|
thread_ready->notify_all();
|
||||||
|
|
||||||
while (true) {
|
while (!exited) {
|
||||||
// Try to pull an action from the queue
|
// Try to pull an action from the queue
|
||||||
Action action;
|
Action action;
|
||||||
if (events_context.action_queue.wait_dequeue_timed(action, 1ms)) {
|
if (events_context.action_queue.wait_dequeue_timed(action, 1ms)) {
|
||||||
|
@ -284,6 +290,9 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
destroy_ui();
|
||||||
|
// TODO restore this call once the RT64 shutdown issue is fixed.
|
||||||
|
// RT64Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern unsigned int VI_STATUS_REG;
|
extern unsigned int VI_STATUS_REG;
|
||||||
|
@ -469,3 +478,21 @@ void Multilibultra::init_events(uint8_t* rdram, uint8_t* rom, Multilibultra::Win
|
||||||
|
|
||||||
events_context.vi.thread = std::thread{ vi_thread_func };
|
events_context.vi.thread = std::thread{ vi_thread_func };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Multilibultra::join_event_threads() {
|
||||||
|
events_context.sp.gfx_thread.join();
|
||||||
|
events_context.vi.thread.join();
|
||||||
|
|
||||||
|
// Send a dummy RSP task so that the task thread is able to exit it's atomic wait and terminate.
|
||||||
|
OSTask dummy_task{};
|
||||||
|
OSTask* expected = nullptr;
|
||||||
|
|
||||||
|
// Attempt to exchange the task with the dummy task one until it was nullptr, as that indicates the
|
||||||
|
// task thread was ready for a new task.
|
||||||
|
do {
|
||||||
|
expected = nullptr;
|
||||||
|
} while (!events_context.sp_task.compare_exchange_weak(expected, &dummy_task));
|
||||||
|
events_context.sp_task.notify_all();
|
||||||
|
|
||||||
|
events_context.sp.task_thread.join();
|
||||||
|
}
|
||||||
|
|
|
@ -125,6 +125,8 @@ struct gfx_callbacks_t {
|
||||||
void start(WindowHandle window_handle, const audio_callbacks_t& audio_callbacks, const input_callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks);
|
void start(WindowHandle window_handle, const audio_callbacks_t& audio_callbacks, const input_callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks);
|
||||||
void start_game(int game);
|
void start_game(int game);
|
||||||
bool is_game_started();
|
bool is_game_started();
|
||||||
|
void quit();
|
||||||
|
void join_event_threads();
|
||||||
|
|
||||||
} // namespace Multilibultra
|
} // namespace Multilibultra
|
||||||
|
|
||||||
|
|
|
@ -144,6 +144,8 @@ void swap_running_thread(thread_queue_t& running_thread_queue, OSThread*& cur_ru
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern std::atomic_bool exited;
|
||||||
|
|
||||||
void scheduler_func() {
|
void scheduler_func() {
|
||||||
thread_queue_t running_thread_queue{};
|
thread_queue_t running_thread_queue{};
|
||||||
OSThread* cur_running_thread = nullptr;
|
OSThread* cur_running_thread = nullptr;
|
||||||
|
@ -169,8 +171,13 @@ void scheduler_func() {
|
||||||
// Handle threads that have changed priority
|
// Handle threads that have changed priority
|
||||||
handle_thread_reprioritization(running_thread_queue);
|
handle_thread_reprioritization(running_thread_queue);
|
||||||
|
|
||||||
// Determine which thread to run, stopping the current running thread if necessary
|
if (!exited) {
|
||||||
swap_running_thread(running_thread_queue, cur_running_thread);
|
// Determine which thread to run, stopping the current running thread if necessary
|
||||||
|
swap_running_thread(running_thread_queue, cur_running_thread);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
if (old_running_thread != cur_running_thread && old_running_thread && cur_running_thread) {
|
if (old_running_thread != cur_running_thread && old_running_thread && cur_running_thread) {
|
||||||
|
|
|
@ -161,12 +161,6 @@ extern "C" void osStartThread(RDRAM_ARG PTR(OSThread) t_) {
|
||||||
} else {
|
} else {
|
||||||
Multilibultra::schedule_running_thread(t);
|
Multilibultra::schedule_running_thread(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The main thread "becomes" the first thread started, so join on it and exit after it completes.
|
|
||||||
if (is_main_thread) {
|
|
||||||
t->context->host_thread.join();
|
|
||||||
std::exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void osCreateThread(RDRAM_ARG PTR(OSThread) t_, OSId id, PTR(thread_func_t) entrypoint, PTR(void) arg, PTR(void) sp, OSPri pri) {
|
extern "C" void osCreateThread(RDRAM_ARG PTR(OSThread) t_, OSId id, PTR(thread_func_t) entrypoint, PTR(void) arg, PTR(void) sp, OSPri pri) {
|
||||||
|
|
|
@ -121,7 +121,8 @@ void timer_thread(RDRAM_ARG1) {
|
||||||
active_timers.insert(cur_timer_);
|
active_timers.insert(cur_timer_);
|
||||||
// Process the new action
|
// Process the new action
|
||||||
process_timer_action(cur_action);
|
process_timer_action(cur_action);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// Waiting for the timer completed, so send the timer's message to its message queue
|
// Waiting for the timer completed, so send the timer's message to its message queue
|
||||||
osSendMesg(PASS_RDRAM cur_timer->mq, cur_timer->msg, OS_MESG_NOBLOCK);
|
osSendMesg(PASS_RDRAM cur_timer->mq, cur_timer->msg, OS_MESG_NOBLOCK);
|
||||||
// If the timer has a specified interval then reload it with that value
|
// If the timer has a specified interval then reload it with that value
|
||||||
|
@ -135,6 +136,7 @@ void timer_thread(RDRAM_ARG1) {
|
||||||
|
|
||||||
void Multilibultra::init_timers(RDRAM_ARG1) {
|
void Multilibultra::init_timers(RDRAM_ARG1) {
|
||||||
timer_context.thread = std::thread{ timer_thread, PASS_RDRAM1 };
|
timer_context.thread = std::thread{ timer_thread, PASS_RDRAM1 };
|
||||||
|
timer_context.thread.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Multilibultra::get_speed_multiplier() {
|
uint32_t Multilibultra::get_speed_multiplier() {
|
||||||
|
|
36
src/cont.cpp
36
src/cont.cpp
|
@ -1,5 +1,5 @@
|
||||||
#include "../portultra/multilibultra.hpp"
|
#include "../portultra/multilibultra.hpp"
|
||||||
#include "recomp.h"
|
#include "recomp_helpers.h"
|
||||||
|
|
||||||
static Multilibultra::input_callbacks_t input_callbacks;
|
static Multilibultra::input_callbacks_t input_callbacks;
|
||||||
|
|
||||||
|
@ -10,8 +10,8 @@ void set_input_callbacks(const Multilibultra::input_callbacks_t& callbacks) {
|
||||||
static int max_controllers = 0;
|
static int max_controllers = 0;
|
||||||
|
|
||||||
extern "C" void osContInit_recomp(uint8_t* rdram, recomp_context* ctx) {
|
extern "C" void osContInit_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||||
gpr bitpattern = ctx->r5;
|
PTR(void) bitpattern = _arg<1, PTR(void)>(rdram, ctx);
|
||||||
gpr status = ctx->r6;
|
PTR(void) status = _arg<2, PTR(void)>(rdram, ctx);
|
||||||
|
|
||||||
// Set bit 0 to indicate that controller 0 is present
|
// Set bit 0 to indicate that controller 0 is present
|
||||||
MEM_B(0, bitpattern) = 0x01;
|
MEM_B(0, bitpattern) = 0x01;
|
||||||
|
@ -29,22 +29,15 @@ extern "C" void osContInit_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||||
MEM_B(4 * controller + 3, status) = 0x80 >> 4; // errno: CONT_NO_RESPONSE_ERROR >> 4
|
MEM_B(4 * controller + 3, status) = 0x80 >> 4; // errno: CONT_NO_RESPONSE_ERROR >> 4
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->r2 = 0;
|
_return<s32>(ctx, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void osContStartReadData_recomp(uint8_t* rdram, recomp_context* ctx) {
|
extern "C" void osContStartReadData_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||||
Multilibultra::send_si_message();
|
Multilibultra::send_si_message();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OSContPad {
|
|
||||||
u16 button;
|
|
||||||
s8 stick_x; /* -80 <= stick_x <= 80 */
|
|
||||||
s8 stick_y; /* -80 <= stick_y <= 80 */
|
|
||||||
u8 errno_;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern "C" void osContGetReadData_recomp(uint8_t* rdram, recomp_context* ctx) {
|
extern "C" void osContGetReadData_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||||
int32_t pad = (int32_t)ctx->r4;
|
PTR(void) pad = _arg<0, PTR(void)>(rdram, ctx);
|
||||||
|
|
||||||
uint16_t buttons = 0;
|
uint16_t buttons = 0;
|
||||||
float x = 0.0f;
|
float x = 0.0f;
|
||||||
|
@ -74,7 +67,7 @@ extern "C" void osContStartQuery_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void osContGetQuery_recomp(uint8_t * rdram, recomp_context * ctx) {
|
extern "C" void osContGetQuery_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||||
gpr status = ctx->r4;
|
PTR(void) status = _arg<0, PTR(void)>(rdram, ctx);
|
||||||
|
|
||||||
// Mark controller 0 as present
|
// Mark controller 0 as present
|
||||||
MEM_H(0, status) = 0x0005; // type: CONT_TYPE_NORMAL (from joybus)
|
MEM_H(0, status) = 0x0005; // type: CONT_TYPE_NORMAL (from joybus)
|
||||||
|
@ -89,8 +82,8 @@ extern "C" void osContGetQuery_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void osContSetCh_recomp(uint8_t* rdram, recomp_context* ctx) {
|
extern "C" void osContSetCh_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||||
max_controllers = std::min((unsigned int)ctx->r4, 4u);
|
max_controllers = std::min(_arg<0, u8>(rdram, ctx), u8(4));
|
||||||
ctx->r2 = 0;
|
_return<s32>(ctx, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void __osMotorAccess_recomp(uint8_t* rdram, recomp_context* ctx) {
|
extern "C" void __osMotorAccess_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
@ -108,3 +101,16 @@ extern "C" void osMotorStart_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||||
extern "C" void osMotorStop_recomp(uint8_t* rdram, recomp_context* ctx) {
|
extern "C" void osMotorStop_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "../patches/input.h"
|
||||||
|
|
||||||
|
extern "C" void recomp_get_item_inputs(uint8_t* rdram, recomp_context* ctx) {
|
||||||
|
RecompInputs* inputs = _arg<0, RecompInputs*>(rdram, ctx);
|
||||||
|
|
||||||
|
if (input_callbacks.get_input) {
|
||||||
|
u16 buttons;
|
||||||
|
input_callbacks.get_input(&buttons, &inputs->x, &inputs->y);
|
||||||
|
// TODO remap the inputs for items here
|
||||||
|
inputs->buttons = buttons;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ std::vector<GameControllerButtonMapping> controller_button_map{
|
||||||
|
|
||||||
std::vector<SDL_JoystickID> controllers{};
|
std::vector<SDL_JoystickID> controllers{};
|
||||||
|
|
||||||
int sdl_event_filter(void* userdata, SDL_Event* event) {
|
bool sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
//case SDL_EventType::SDL_KEYUP:
|
//case SDL_EventType::SDL_KEYUP:
|
||||||
//case SDL_EventType::SDL_KEYDOWN:
|
//case SDL_EventType::SDL_KEYDOWN:
|
||||||
|
@ -138,13 +138,13 @@ int sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_EventType::SDL_QUIT:
|
case SDL_EventType::SDL_QUIT:
|
||||||
std::quick_exit(EXIT_SUCCESS);
|
Multilibultra::quit();
|
||||||
break;
|
return true;
|
||||||
default:
|
default:
|
||||||
queue_event(*event);
|
queue_event(*event);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return 1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Multilibultra::gfx_callbacks_t::gfx_data_t create_gfx() {
|
Multilibultra::gfx_callbacks_t::gfx_data_t create_gfx() {
|
||||||
|
@ -185,8 +185,9 @@ void update_gfx(void*) {
|
||||||
constexpr int max_events_per_frame = 16;
|
constexpr int max_events_per_frame = 16;
|
||||||
SDL_Event cur_event;
|
SDL_Event cur_event;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (i++ < max_events_per_frame && SDL_PollEvent(&cur_event)) {
|
static bool exited = false;
|
||||||
sdl_event_filter(nullptr, &cur_event);
|
while (i++ < max_events_per_frame && SDL_PollEvent(&cur_event) && !exited) {
|
||||||
|
exited = sdl_event_filter(nullptr, &cur_event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,6 +122,28 @@ extern "C" void unload_overlays(int32_t ram_addr, uint32_t size);
|
||||||
std::unique_ptr<uint8_t[]> rdram_buffer;
|
std::unique_ptr<uint8_t[]> rdram_buffer;
|
||||||
recomp_context context{};
|
recomp_context context{};
|
||||||
|
|
||||||
|
void read_patch_data(uint8_t* rdram, gpr patch_data_address) {
|
||||||
|
const char patches_data_file_path[] = "patches/patches.bin";
|
||||||
|
std::ifstream patches_data_file{ patches_data_file_path, std::ios::binary };
|
||||||
|
|
||||||
|
if (!patches_data_file) {
|
||||||
|
fprintf(stderr, "Failed to open patches data file: %s\n", patches_data_file_path);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
patches_data_file.seekg(0, std::ios::end);
|
||||||
|
size_t patches_data_size = patches_data_file.tellg();
|
||||||
|
patches_data_file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
std::unique_ptr<uint8_t[]> patches_data = std::make_unique<uint8_t[]>(patches_data_size);
|
||||||
|
|
||||||
|
patches_data_file.read(reinterpret_cast<char*>(patches_data.get()), patches_data_size);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < patches_data_size; i++) {
|
||||||
|
MEM_B(i, patch_data_address) = patches_data[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EXPORT extern "C" void init() {
|
EXPORT extern "C" void init() {
|
||||||
{
|
{
|
||||||
std::ifstream rom_file{ get_rom_name(), std::ios::binary };
|
std::ifstream rom_file{ get_rom_name(), std::ios::binary };
|
||||||
|
@ -160,6 +182,9 @@ EXPORT extern "C" void init() {
|
||||||
// Initial 1MB DMA (rom address 0x1000 = physical address 0x10001000)
|
// Initial 1MB DMA (rom address 0x1000 = physical address 0x10001000)
|
||||||
do_rom_read(rdram_buffer.get(), entrypoint, 0x10001000, 0x100000);
|
do_rom_read(rdram_buffer.get(), entrypoint, 0x10001000, 0x100000);
|
||||||
|
|
||||||
|
// Read in any extra data from patches
|
||||||
|
read_patch_data(rdram_buffer.get(), (gpr)(s32)0x80800100);
|
||||||
|
|
||||||
// Set up stack pointer
|
// Set up stack pointer
|
||||||
context.r29 = 0xFFFFFFFF803FFFF0u;
|
context.r29 = 0xFFFFFFFF803FFFF0u;
|
||||||
|
|
||||||
|
@ -197,6 +222,15 @@ bool Multilibultra::is_game_started() {
|
||||||
void set_audio_callbacks(const Multilibultra::audio_callbacks_t& callbacks);
|
void set_audio_callbacks(const Multilibultra::audio_callbacks_t& callbacks);
|
||||||
void set_input_callbacks(const Multilibultra::input_callbacks_t& callback);
|
void set_input_callbacks(const Multilibultra::input_callbacks_t& callback);
|
||||||
|
|
||||||
|
std::atomic_bool exited = false;
|
||||||
|
|
||||||
|
void Multilibultra::quit() {
|
||||||
|
exited.store(true);
|
||||||
|
int desired = -1;
|
||||||
|
game_started.compare_exchange_strong(desired, -2);
|
||||||
|
game_started.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
void Multilibultra::start(WindowHandle window_handle, const audio_callbacks_t& audio_callbacks, const input_callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks_) {
|
void Multilibultra::start(WindowHandle window_handle, const audio_callbacks_t& audio_callbacks, const input_callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks_) {
|
||||||
set_audio_callbacks(audio_callbacks);
|
set_audio_callbacks(audio_callbacks);
|
||||||
set_input_callbacks(input_callbacks);
|
set_input_callbacks(input_callbacks);
|
||||||
|
@ -231,16 +265,20 @@ void Multilibultra::start(WindowHandle window_handle, const audio_callbacks_t& a
|
||||||
case 0:
|
case 0:
|
||||||
recomp_entrypoint(rdram_buffer.get(), &context);
|
recomp_entrypoint(rdram_buffer.get(), &context);
|
||||||
break;
|
break;
|
||||||
|
case -2:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_printf("[Recomp] Quitting\n");
|
debug_printf("[Recomp] Quitting\n");
|
||||||
}, window_handle};
|
}, window_handle};
|
||||||
|
|
||||||
while (true) {
|
while (!exited) {
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
std::this_thread::sleep_for(1ms);
|
std::this_thread::sleep_for(1ms);
|
||||||
if (gfx_callbacks.update_gfx != nullptr) {
|
if (gfx_callbacks.update_gfx != nullptr) {
|
||||||
gfx_callbacks.update_gfx(gfx_data);
|
gfx_callbacks.update_gfx(gfx_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
game_thread.join();
|
||||||
|
Multilibultra::join_event_threads();
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,3 +130,7 @@ void RT64UpdateScreen(uint32_t vi_origin) {
|
||||||
void RT64ChangeWindow() {
|
void RT64ChangeWindow() {
|
||||||
ChangeWindow();
|
ChangeWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RT64Shutdown() {
|
||||||
|
PluginShutdown();
|
||||||
|
}
|
||||||
|
|
|
@ -587,6 +587,10 @@ struct {
|
||||||
Rml::Context* context;
|
Rml::Context* context;
|
||||||
std::unique_ptr<Rml::EventListenerInstancer> event_listener_instancer;
|
std::unique_ptr<Rml::EventListenerInstancer> event_listener_instancer;
|
||||||
|
|
||||||
|
void unload() {
|
||||||
|
render_interface.reset();
|
||||||
|
}
|
||||||
|
|
||||||
void swap_document(Menu menu) {
|
void swap_document(Menu menu) {
|
||||||
if (current_document != nullptr) {
|
if (current_document != nullptr) {
|
||||||
current_document->Hide();
|
current_document->Hide();
|
||||||
|
@ -751,4 +755,9 @@ void set_rt64_hooks() {
|
||||||
|
|
||||||
void set_current_menu(Menu menu) {
|
void set_current_menu(Menu menu) {
|
||||||
open_menu.store(menu);
|
open_menu.store(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void destroy_ui() {
|
||||||
|
Rml::Shutdown();
|
||||||
|
UIContext.rml.unload();
|
||||||
|
}
|
||||||
|
|
30
us.rev1.toml
30
us.rev1.toml
|
@ -10,10 +10,15 @@ relocatable_sections_path = "overlays.us.rev1.txt"
|
||||||
[patches]
|
[patches]
|
||||||
stubs = [
|
stubs = [
|
||||||
# Stub out unused functions that directly manipulate RCP status.
|
# Stub out unused functions that directly manipulate RCP status.
|
||||||
"func_80084940",
|
"RcpUtils_PrintRegisterStatus",
|
||||||
"func_80084968",
|
"RcpUtils_Reset",
|
||||||
# Stub out an unnecessary function that accesses kseg1 addresses.
|
# Stub out an unnecessary function that accesses kseg1 addresses.
|
||||||
"func_800818F4"
|
"CIC6105_Init"
|
||||||
|
]
|
||||||
|
|
||||||
|
ignored = [
|
||||||
|
# Not actually a function
|
||||||
|
"D_80186028"
|
||||||
]
|
]
|
||||||
|
|
||||||
# Hooks
|
# Hooks
|
||||||
|
@ -25,23 +30,14 @@ args = ["u32", "u32", "u32"]
|
||||||
|
|
||||||
# Function hooks for overlay loading.
|
# Function hooks for overlay loading.
|
||||||
[[patches.hook]]
|
[[patches.hook]]
|
||||||
func = "Idle_InitCodeAndMemory"
|
func = "Main_Init"
|
||||||
calls = "load_overlays"
|
text = " load_overlays((uint32_t)ctx->r6, (uint32_t)ctx->r5, (uint32_t)ctx->r7);"
|
||||||
args = ["a2", "a1", "a3"]
|
|
||||||
after_vram = 0x800802A4
|
after_vram = 0x800802A4
|
||||||
|
|
||||||
[[patches.hook]]
|
[[patches.hook]]
|
||||||
func = "Load2_LoadOverlay"
|
func = "Overlay_Load"
|
||||||
calls = "load_overlays"
|
text = " load_overlays((uint32_t)ctx->r4, MEM_W(0x10, ctx->r29), (uint32_t)(ctx->r5 - ctx->r4));"
|
||||||
# args = [
|
# No after_vram means this will be placed at the start of the function
|
||||||
# "a0", # $a0 contains rom start
|
|
||||||
# {operation = "load", type = "u32", base = "sp", offset = 0x10}, # sp + 10 contains the ram address
|
|
||||||
# {operation = "subtract", arguments = ["a1", "a0"]} # Calculate size from rom end - rom start
|
|
||||||
# ]
|
|
||||||
args = ["a1", "a0", "a2"]
|
|
||||||
# This vram address is an instruction in a delay slot. In that case, the recompiler will emit the
|
|
||||||
# hook call after this instruction is run and before the function is called.
|
|
||||||
after_vram = 0x80085048
|
|
||||||
|
|
||||||
# Single-instruction patches
|
# Single-instruction patches
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue