mirror of
				https://github.com/Zelda64Recomp/Zelda64Recomp.git
				synced 2025-10-30 08:03:03 +00:00 
			
		
		
		
	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
					
				
					 21 changed files with 264 additions and 60 deletions
				
			
		
							
								
								
									
										46
									
								
								include/recomp_helpers.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								include/recomp_helpers.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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) { | ||||||
|  |  | ||||||
							
								
								
									
										24
									
								
								patches/input.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								patches/input.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								patches/input.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								patches/input.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||||
							
								
								
									
										6
									
								
								patches/patches.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								patches/patches.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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…
	
	Add table
		
		Reference in a new issue
	
	 Mr-Wiseguy
						Mr-Wiseguy