diff --git a/MMRecomp.sln b/MMRecomp.sln
index 90b65d5..938537c 100644
--- a/MMRecomp.sln
+++ b/MMRecomp.sln
@@ -5,8 +5,6 @@ VisualStudioVersion = 16.0.33027.164
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MMRecomp", "MMRecomp.vcxproj", "{14B47028-6A86-4660-A86D-2E69F229E110}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RecompiledFuncs", "RecompiledFuncs.vcxproj", "{7BF5E3F9-C49F-4C84-AB64-7681F028CAC2}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -23,14 +21,6 @@ Global
{14B47028-6A86-4660-A86D-2E69F229E110}.Release|x64.Build.0 = Release|x64
{14B47028-6A86-4660-A86D-2E69F229E110}.Release|x86.ActiveCfg = Release|Win32
{14B47028-6A86-4660-A86D-2E69F229E110}.Release|x86.Build.0 = Release|Win32
- {7BF5E3F9-C49F-4C84-AB64-7681F028CAC2}.Debug|x64.ActiveCfg = Debug|x64
- {7BF5E3F9-C49F-4C84-AB64-7681F028CAC2}.Debug|x64.Build.0 = Debug|x64
- {7BF5E3F9-C49F-4C84-AB64-7681F028CAC2}.Debug|x86.ActiveCfg = Debug|Win32
- {7BF5E3F9-C49F-4C84-AB64-7681F028CAC2}.Debug|x86.Build.0 = Debug|Win32
- {7BF5E3F9-C49F-4C84-AB64-7681F028CAC2}.Release|x64.ActiveCfg = Release|x64
- {7BF5E3F9-C49F-4C84-AB64-7681F028CAC2}.Release|x64.Build.0 = Release|x64
- {7BF5E3F9-C49F-4C84-AB64-7681F028CAC2}.Release|x86.ActiveCfg = Release|Win32
- {7BF5E3F9-C49F-4C84-AB64-7681F028CAC2}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/MMRecomp.vcxproj b/MMRecomp.vcxproj
index d3b303c..959b67b 100644
--- a/MMRecomp.vcxproj
+++ b/MMRecomp.vcxproj
@@ -40,13 +40,13 @@
Unicode
- Application
+ Makefile
true
v142
Unicode
- Application
+ Makefile
false
v142
true
@@ -78,9 +78,15 @@
true
+ make TARGET=$(Configuration) LIB_DIR="$(VC_LibraryPath_VC_x64_Desktop)" UCRT_DIR="$(UniversalCRT_LibraryPath_x64)" SDK_DIR="$(WindowsSDK_LibraryPath)\x64" -j$(NUMBER_OF_PROCESSORS)
+ make TARGET=$(Configuration) clean
+ $(SolutionDir)build\$(Configuration)\
false
+ make TARGET=$(Configuration) LIB_DIR="$(VC_LibraryPath_VC_x64_Desktop)" UCRT_DIR="$(UniversalCRT_LibraryPath_x64)" SDK_DIR="$(WindowsSDK_LibraryPath)\x64" -j$(NUMBER_OF_PROCESSORS)
+ make TARGET=$(Configuration) clean
+ $(SolutionDir)build\$(Configuration)\
@@ -225,11 +231,6 @@ XCOPY "$(ProjectDir)lib\SDL2-2.24.0\lib\$(Platform)\SDL2.dll" "$(TargetDir)" /S
-
-
- {7bf5e3f9-c49f-4c84-ab64-7681f028cac2}
-
-
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..19e4c14
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,108 @@
+CONFIG ?= Debug
+LIB ?= 0
+
+ifeq ($(CONFIG),Debug)
+BUILD_DIR := build/debug
+FUNC_OPTFLAGS := -Og -g -fno-strict-aliasing
+OPTFLAGS := -Og -g -fno-strict-aliasing
+# Static C runtime linking
+LIBS := -Wl,/nodefaultlib:libcmt -Wl,/nodefaultlib:ucrt -Wl,/nodefaultlib:libucrt -llibcmtd -llibvcruntimed -llibucrtd
+# Dynamic
+# LIBS := -Wl,/nodefaultlib:libcmt -Wl,/nodefaultlib:ucrt -Wl,/nodefaultlib:libucrt -lmsvcrtd -lvcruntimed -lucrtd
+else ifeq ($(CONFIG),Release)
+BUILD_DIR := build/release
+FUNC_OPTFLAGS := -O2 -g -fno-strict-aliasing
+OPTFLAGS := -O2 -g -fno-strict-aliasing
+# Static C runtime linking
+LIBS := -Wl,/nodefaultlib:libcmt -Wl,/nodefaultlib:ucrt -Wl,/nodefaultlib:libucrt -llibcmt -llibvcruntime -llibucrt
+# Dynamic
+# LIBS := -Wl,/nodefaultlib:libcmt -Wl,/nodefaultlib:ucrt -Wl,/nodefaultlib:libucrt -lmsvcrt -lvcruntime -lucrt
+else
+$(error "Invalid build configuration: $(CONFIG)")
+endif
+
+SRC_DIRS := portultra src rsp
+
+FUNCS_DIR := RecompiledFuncs
+FUNCS_LIB := $(BUILD_DIR)/RecompiledFuncs.lib
+FUNCS_C_SRCS := $(wildcard $(FUNCS_DIR)/*.c)
+FUNCS_CXX_SRCS := $(wildcard $(FUNCS_DIR)/*.cpp)
+
+FUNC_BUILD_DIR := $(BUILD_DIR)/RecompiledFuncs
+FUNCS_C_OBJS := $(addprefix $(BUILD_DIR)/,$(FUNCS_C_SRCS:.c=.o))
+FUNCS_CXX_OBJS := $(addprefix $(BUILD_DIR)/,$(FUNCS_CXX_SRCS:.cpp=.o))
+ALL_FUNC_OBJS := $(FUNCS_C_OBJS) $(FUNCS_CXX_OBJS)
+
+BUILD_SRC_DIRS := $(addprefix $(BUILD_DIR)/,$(SRC_DIRS))
+C_SRCS := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.c))
+CXX_SRCS := $(foreach dir,$(SRC_DIRS),$(wildcard $(dir)/*.cpp))
+
+C_OBJS := $(addprefix $(BUILD_DIR)/,$(C_SRCS:.c=.o))
+CXX_OBJS := $(addprefix $(BUILD_DIR)/,$(CXX_SRCS:.cpp=.o))
+ALL_OBJS := $(C_OBJS) $(CXX_OBJS)
+
+
+CC := clang
+CXX := clang++
+LIB := clang++
+LD := clang++
+
+FUNC_CFLAGS := $(FUNC_OPTFLAGS) -c -Wno-unused-but-set-variable
+FUNC_CXXFLAGS := $(FUNC_OPTFLAGS) -std=c++20 -c
+FUNC_CPPFLAGS := -Iinclude
+LIBFLAGS := $(OPTFLAGS) -fuse-ld=llvm-lib
+LIB_DIR ?= C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\lib\x64
+UCRT_DIR ?= C:\Program Files (x86)\Windows Kits\10\lib\10.0.22000.0\ucrt\x64;
+SDK_DIR ?= C:\Program Files (x86)\Windows Kits\10\lib\10.0.22000.0\um\x64
+
+WARNFLAGS := -Wall -Wextra -Wpedantic -Wno-gnu-anonymous-struct
+CFLAGS := -ffunction-sections -fdata-sections $(OPTFLAGS) $(WARNFLAGS) -c
+CXXFLAGS := -ffunction-sections -fdata-sections $(OPTFLAGS) $(WARNFLAGS) -std=c++20 -c
+CPPFLAGS := -Iinclude -Ithirdparty
+LDFLAGS := -v -Wl,/OPT:REF $(OPTFLAGS) $(LIBS) -L"$(LIB_DIR:;=)" -L"$(UCRT_DIR:;=)" -L"$(SDK_DIR:;=)" lib/RT64/$(CONFIG)/RT64.lib
+
+ifeq ($(LIB),1)
+TARGET := $(BUILD_DIR)/MMRecomp.dll
+LDFLAGS += -shared
+else
+TARGET := $(BUILD_DIR)/MMRecomp.exe
+endif
+
+default: $(TARGET)
+
+clean:
+ rmdir /S /Q $(subst /,\\,$(BUILD_DIR))
+
+cleanfuncs:
+
+
+$(FUNCS_CXX_OBJS) : $(BUILD_DIR)/%.o : %.cpp | $(FUNC_BUILD_DIR)
+ @$(CXX) $(FUNC_CXXFLAGS) $(FUNC_CPPFLAGS) $^ -o $@
+
+$(FUNCS_C_OBJS) : $(BUILD_DIR)/%.o : %.c | $(FUNC_BUILD_DIR)
+ @$(CC) $(FUNC_CFLAGS) $(FUNC_CPPFLAGS) $^ -o $@
+
+$(FUNCS_LIB): $(ALL_FUNC_OBJS) | $(BUILD_DIR)
+ $(LIB) $(LIBFLAGS) $(FUNC_BUILD_DIR)/*.o -o $@
+
+
+
+$(CXX_OBJS) : $(BUILD_DIR)/%.o : %.cpp | $(BUILD_SRC_DIRS)
+ $(CXX) -MMD -MF $(@:.o=.d) $(CXXFLAGS) $(CPPFLAGS) $< -o $@
+
+$(C_OBJS) : $(BUILD_DIR)/%.o : %.c | $(BUILD_SRC_DIRS)
+ $(CC) -MMD -MF $(@:.o=.d) $(CFLAGS) $(CPPFLAGS) $< -o $@
+
+$(TARGET): $(FUNCS_LIB) $(ALL_OBJS) | $(BUILD_SRC_DIRS)
+ $(LD) $(LDFLAGS) -o $@ $^
+
+$(BUILD_SRC_DIRS) $(FUNC_BUILD_DIR) $(BUILD_DIR):
+ mkdir $(subst /,\\,$@)
+
+-include $(ALL_OBJS:.o=.d)
+
+MAKEFLAGS += --no-builtin-rules
+.SUFFIXES:
+.PHONY: default clean cleanfuncs
+
+print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true
diff --git a/include/recomp.h b/include/recomp.h
index 5768cb2..e627a8c 100644
--- a/include/recomp.h
+++ b/include/recomp.h
@@ -66,27 +66,68 @@ static inline uint64_t load_doubleword(uint8_t* rdram, gpr reg, gpr offset) {
#define LD(offset, reg) \
load_doubleword(rdram, offset, reg)
-// TODO proper lwl/lwr/swl/swr
-static inline void do_swl(uint8_t* rdram, gpr offset, gpr reg, gpr val) {
- uint8_t byte0 = (uint8_t)(val >> 24);
- uint8_t byte1 = (uint8_t)(val >> 16);
- uint8_t byte2 = (uint8_t)(val >> 8);
- uint8_t byte3 = (uint8_t)(val >> 0);
+static inline gpr do_lwl(uint8_t* rdram, gpr initial_value, gpr offset, gpr reg) {
+ // Calculate the overall address
+ gpr address = (offset + reg);
- MEM_B(offset + 0, reg) = byte0;
- MEM_B(offset + 1, reg) = byte1;
- MEM_B(offset + 2, reg) = byte2;
- MEM_B(offset + 3, reg) = byte3;
-}
+ // Load the aligned word
+ gpr word_address = address & ~0x3;
+ uint32_t loaded_value = MEM_W(0, word_address);
-static inline gpr do_lwl(uint8_t* rdram, gpr offset, gpr reg) {
- uint8_t byte0 = MEM_B(offset + 0, reg);
- uint8_t byte1 = MEM_B(offset + 1, reg);
- uint8_t byte2 = MEM_B(offset + 2, reg);
- uint8_t byte3 = MEM_B(offset + 3, reg);
+ // Mask the existing value and shift the loaded value appropriately
+ gpr misalignment = address & 0x3;
+ gpr masked_value = initial_value & ~(0xFFFFFFFFu << (misalignment * 8));
+ loaded_value <<= (misalignment * 8);
// Cast to int32_t to sign extend first
- return (gpr)(int32_t)((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | (byte3 << 0));
+ return (gpr)(int32_t)(masked_value | loaded_value);
+}
+
+static inline gpr do_lwr(uint8_t* rdram, gpr initial_value, gpr offset, gpr reg) {
+ // Calculate the overall address
+ gpr address = (offset + reg);
+
+ // Load the aligned word
+ gpr word_address = address & ~0x3;
+ uint32_t loaded_value = MEM_W(0, word_address);
+
+ // Mask the existing value and shift the loaded value appropriately
+ gpr misalignment = address & 0x3;
+ gpr masked_value = initial_value & ~(0xFFFFFFFFu >> (24 - misalignment * 8));
+ loaded_value >>= (24 - misalignment * 8);
+
+ // Cast to int32_t to sign extend first
+ return (gpr)(int32_t)(masked_value | loaded_value);
+}
+
+static inline void do_swl(uint8_t* rdram, gpr offset, gpr reg, gpr val) {
+ // Calculate the overall address
+ gpr address = (offset + reg);
+
+ // Get the initial value of the aligned word
+ gpr word_address = address & ~0x3;
+ uint32_t initial_value = MEM_W(0, word_address);
+
+ // Mask the initial value and shift the input value appropriately
+ gpr misalignment = address & 0x3;
+ uint32_t masked_initial_value = initial_value & ~(0xFFFFFFFFu >> (misalignment * 8));
+ uint32_t shifted_input_value = ((uint32_t)val) >> (misalignment * 8);
+ MEM_W(0, word_address) = masked_initial_value | shifted_input_value;
+}
+
+static inline void do_swr(uint8_t* rdram, gpr offset, gpr reg, gpr val) {
+ // Calculate the overall address
+ gpr address = (offset + reg);
+
+ // Get the initial value of the aligned word
+ gpr word_address = address & ~0x3;
+ uint32_t initial_value = MEM_W(0, word_address);
+
+ // Mask the initial value and shift the input value appropriately
+ gpr misalignment = address & 0x3;
+ uint32_t masked_initial_value = initial_value & ~(0xFFFFFFFFu << (24 - misalignment * 8));
+ uint32_t shifted_input_value = ((uint32_t)val) << (24 - misalignment * 8);
+ MEM_W(0, word_address) = masked_initial_value | shifted_input_value;
}
#define S32(val) \
diff --git a/include/rt64_layer.h b/include/rt64_layer.h
index b60d667..b38d4f1 100644
--- a/include/rt64_layer.h
+++ b/include/rt64_layer.h
@@ -2,6 +2,11 @@
#define __RT64_LAYER_H__
typedef struct {
+ void* hWnd;
+ void* hStatusBar;
+
+ int Reserved;
+
unsigned char* HEADER; /* This is the rom header (first 40h bytes of the rom) */
unsigned char* RDRAM;
unsigned char* DMEM;
@@ -35,9 +40,9 @@ typedef struct {
void (*CheckInterrupts)(void);
- unsigned int version;
- unsigned int* SP_STATUS_REG;
- const unsigned int* RDRAM_SIZE;
+ // unsigned int version;
+ // unsigned int* SP_STATUS_REG;
+ // const unsigned int* RDRAM_SIZE;
} GFX_INFO;
#define DLLEXPORT extern "C" __declspec(dllexport)
@@ -56,6 +61,7 @@ DLLIMPORT void ProcessRDPList(void);
DLLIMPORT void ProcessDList(void);
DLLIMPORT void UpdateScreen(void);
DLLIMPORT void PumpEvents(void);
+DLLIMPORT void ChangeWindow(void);
#endif
diff --git a/portultra/audio.cpp b/portultra/audio.cpp
index d553fc6..3dca8ad 100644
--- a/portultra/audio.cpp
+++ b/portultra/audio.cpp
@@ -1,55 +1,39 @@
#include "ultra64.h"
#include "multilibultra.hpp"
-#include "SDL.h"
-#include "SDL_audio.h"
#include
-static SDL_AudioDeviceID audio_device = 0;
static uint32_t sample_rate = 48000;
+static Multilibultra::audio_callbacks_t audio_callbacks;
+
+void Multilibultra::set_audio_callbacks(const audio_callbacks_t* callbacks) {
+ if (callbacks != nullptr) {
+ audio_callbacks = *callbacks;
+ }
+}
+
void Multilibultra::init_audio() {
- // Initialize SDL audio.
- SDL_InitSubSystem(SDL_INIT_AUDIO);
// Pick an initial dummy sample rate; this will be set by the game later to the true sample rate.
set_audio_frequency(48000);
}
void Multilibultra::set_audio_frequency(uint32_t freq) {
- if (audio_device != 0) {
- SDL_CloseAudioDevice(audio_device);
+ if (audio_callbacks.set_frequency) {
+ audio_callbacks.set_frequency(freq);
}
- SDL_AudioSpec spec_desired{
- .freq = (int)freq,
- .format = AUDIO_S16,
- .channels = 2,
- .silence = 0, // calculated
- .samples = 0x100, // Fairly small sample count to reduce the latency of internal buffering
- .padding = 0, // unused
- .size = 0, // calculated
- .callback = nullptr,//feed_audio, // Use a callback as QueueAudio causes popping
- .userdata = nullptr
- };
-
- audio_device = SDL_OpenAudioDevice(nullptr, false, &spec_desired, nullptr, 0);
- if (audio_device == 0) {
- printf("SDL Error: %s\n", SDL_GetError());
- fflush(stdout);
- assert(false);
- }
- SDL_PauseAudioDevice(audio_device, 0);
sample_rate = freq;
}
-void Multilibultra::queue_audio_buffer(RDRAM_ARG PTR(s16) audio_data_, uint32_t byte_count) {
+void Multilibultra::queue_audio_buffer(RDRAM_ARG PTR(int16_t) audio_data_, uint32_t byte_count) {
// Buffer for holding the output of swapping the audio channels. This is reused across
// calls to reduce runtime allocations.
- static std::vector swap_buffer;
+ static std::vector swap_buffer;
// Ensure that the byte count is an integer multiple of samples.
assert((byte_count & 1) == 0);
// Calculate the number of samples from the number of bytes.
- uint32_t sample_count = byte_count / sizeof(s16);
+ uint32_t sample_count = byte_count / sizeof(int16_t);
// Make sure the swap buffer is large enough to hold all the incoming audio data.
if (sample_count > swap_buffer.size()) {
@@ -57,34 +41,45 @@ void Multilibultra::queue_audio_buffer(RDRAM_ARG PTR(s16) audio_data_, uint32_t
}
// Swap the audio channels into the swap buffer to correct for the address xor caused by endianness handling.
- s16* audio_data = TO_PTR(s16, audio_data_);
+ int16_t* audio_data = TO_PTR(int16_t, audio_data_);
for (size_t i = 0; i < sample_count; i += 2) {
swap_buffer[i + 0] = audio_data[i + 1];
swap_buffer[i + 1] = audio_data[i + 0];
}
// Queue the swapped audio data.
- SDL_QueueAudio(audio_device, swap_buffer.data(), byte_count);
+ if (audio_callbacks.queue_samples) {
+ audio_callbacks.queue_samples(swap_buffer.data(), sample_count);
+ }
}
-uint32_t buffer_offset_frames = 1;
+// For SDL2
+//uint32_t buffer_offset_frames = 1;
+// For Godot
+float buffer_offset_frames = 0.5f;
// If there's ever any audio popping, check here first. Some games are very sensitive to
// the remaining sample count and reporting a number that's too high here can lead to issues.
// Reporting a number that's too low can lead to audio lag in some games.
uint32_t Multilibultra::get_remaining_audio_bytes() {
// Get the number of remaining buffered audio bytes.
- uint32_t buffered_byte_count = SDL_GetQueuedAudioSize(audio_device);
-
- // Adjust the reported count to be some number of refreshes in the future, which helps ensure that
- // there are enough samples even if the audio thread experiences a small amount of lag. This prevents
- // audio popping on games that use the buffered audio byte count to determine how many samples
- // to generate.
- uint32_t samples_per_vi = (sample_rate / 60);
- if (buffered_byte_count > (buffer_offset_frames * sizeof(int16_t) * samples_per_vi)) {
- buffered_byte_count -= (buffer_offset_frames * sizeof(int16_t) * samples_per_vi);
- } else {
- buffered_byte_count = 0;
+ uint32_t buffered_byte_count;
+ if (audio_callbacks.get_samples_remaining()) {
+ buffered_byte_count = audio_callbacks.get_samples_remaining() * sizeof(int16_t);
}
- return buffered_byte_count;
+ else {
+ buffered_byte_count = 100;
+ }
+ // Adjust the reported count to be some number of refreshes in the future, which helps ensure that
+ // there are enough samples even if the audio thread experiences a small amount of lag. This prevents
+ // audio popping on games that use the buffered audio byte count to determine how many samples
+ // to generate.
+ uint32_t samples_per_vi = (sample_rate / 60);
+ if (buffered_byte_count > static_cast(buffer_offset_frames * sizeof(int16_t) * samples_per_vi)) {
+ buffered_byte_count -= static_cast(buffer_offset_frames * sizeof(int16_t) * samples_per_vi);
+ }
+ else {
+ buffered_byte_count = 0;
+ }
+ return buffered_byte_count;
}
diff --git a/portultra/events.cpp b/portultra/events.cpp
index 988698b..f825f05 100644
--- a/portultra/events.cpp
+++ b/portultra/events.cpp
@@ -9,7 +9,6 @@
#include
#include
-#include "SDL.h"
#include "blockingconcurrentqueue.h"
#include "ultra64.h"
@@ -154,57 +153,10 @@ void dp_complete() {
osSendMesg(PASS_RDRAM events_context.dp.mq, events_context.dp.msg, OS_MESG_NOBLOCK);
}
-void RT64Init(uint8_t* rom, uint8_t* rdram);
+void RT64Init(uint8_t* rom, uint8_t* rdram, void* window_handle);
void RT64SendDL(uint8_t* rdram, const OSTask* task);
void RT64UpdateScreen(uint32_t vi_origin);
-
-std::unordered_map button_map{
- { SDL_Scancode::SDL_SCANCODE_LEFT, 0x0002 }, // c left
- { SDL_Scancode::SDL_SCANCODE_RIGHT, 0x0001 }, // c right
- { SDL_Scancode::SDL_SCANCODE_UP, 0x0008 }, // c up
- { SDL_Scancode::SDL_SCANCODE_DOWN, 0x0004 }, // c down
- { SDL_Scancode::SDL_SCANCODE_RETURN, 0x1000 }, // start
- { SDL_Scancode::SDL_SCANCODE_SPACE, 0x8000 }, // a
- { SDL_Scancode::SDL_SCANCODE_LSHIFT, 0x4000 }, // b
- { SDL_Scancode::SDL_SCANCODE_Q, 0x2000 }, // z
- { SDL_Scancode::SDL_SCANCODE_E, 0x0020 }, // l
- { SDL_Scancode::SDL_SCANCODE_R, 0x0010 }, // r
- { SDL_Scancode::SDL_SCANCODE_J, 0x0200 }, // dpad left
- { SDL_Scancode::SDL_SCANCODE_L, 0x0100 }, // dpad right
- { SDL_Scancode::SDL_SCANCODE_I, 0x0800 }, // dpad up
- { SDL_Scancode::SDL_SCANCODE_K, 0x0400 }, // dpad down
-};
-
-extern int button;
-extern int stick_x;
-extern int stick_y;
-
-int sdl_event_filter(void* userdata, SDL_Event* event) {
- switch (event->type) {
- case SDL_EventType::SDL_KEYUP:
- case SDL_EventType::SDL_KEYDOWN:
- {
- const Uint8* key_states = SDL_GetKeyboardState(nullptr);
- int new_button = 0;
-
- for (const auto& mapping : button_map) {
- if (key_states[mapping.first]) {
- new_button |= mapping.second;
- }
- }
-
- button = new_button;
-
- stick_x = 85 * (key_states[SDL_Scancode::SDL_SCANCODE_D] - key_states[SDL_Scancode::SDL_SCANCODE_A]);
- stick_y = 85 * (key_states[SDL_Scancode::SDL_SCANCODE_W] - key_states[SDL_Scancode::SDL_SCANCODE_S]);
- }
- break;
- case SDL_EventType::SDL_QUIT:
- std::quick_exit(ERROR_SUCCESS);
- break;
- }
- return 1;
-}
+void RT64ChangeWindow();
uint8_t dmem[0x1000];
uint16_t rspReciprocals[512];
@@ -276,19 +228,10 @@ void task_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_rea
}
}
-void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_ready) {
+void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_ready, void* window_handle) {
using namespace std::chrono_literals;
- if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
- fprintf(stderr, "Failed to initialize SDL2: %s\n", SDL_GetError());
- std::quick_exit(EXIT_FAILURE);
- }
- RT64Init(rom, rdram);
- SDL_Window* window = SDL_GetWindowFromID(1);
- // TODO set this window title in RT64, create the window here and send it to RT64, or something else entirely
- // as the current window name visibly changes as RT64 is initialized
- SDL_SetWindowTitle(window, "Recomp");
- //SDL_SetEventFilter(sdl_event_filter, nullptr);
-
+ RT64Init(rom, rdram, window_handle);
+
rsp_constants_init();
// Notify the caller thread that this thread is ready.
@@ -318,15 +261,6 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read
RT64UpdateScreen(swap_action->origin);
}
}
-
- // Handle events
- constexpr int max_events_per_frame = 16;
- SDL_Event cur_event;
- int i = 0;
- while (i++ < max_events_per_frame && SDL_PollEvent(&cur_event)) {
- sdl_event_filter(nullptr, &cur_event);
- }
- //SDL_PumpEvents();
}
}
@@ -483,11 +417,11 @@ void Multilibultra::send_si_message() {
osSendMesg(PASS_RDRAM events_context.si.mq, events_context.si.msg, OS_MESG_NOBLOCK);
}
-void Multilibultra::init_events(uint8_t* rdram, uint8_t* rom) {
+void Multilibultra::init_events(uint8_t* rdram, uint8_t* rom, void* window_handle) {
std::atomic_flag gfx_thread_ready;
std::atomic_flag task_thread_ready;
events_context.rdram = rdram;
- events_context.sp.gfx_thread = std::thread{ gfx_thread_func, rdram, rom, &gfx_thread_ready };
+ events_context.sp.gfx_thread = std::thread{ gfx_thread_func, rdram, rom, &gfx_thread_ready, window_handle };
events_context.sp.task_thread = std::thread{ task_thread_func, rdram, rom, &task_thread_ready };
// Wait for the two sp threads to be ready before continuing to prevent the game from
diff --git a/portultra/multilibultra.hpp b/portultra/multilibultra.hpp
index b694a6c..2f11f3d 100644
--- a/portultra/multilibultra.hpp
+++ b/portultra/multilibultra.hpp
@@ -21,10 +21,10 @@ constexpr int32_t cart_handle = 0x80800000;
constexpr int32_t flash_handle = (int32_t)(cart_handle + sizeof(OSPiHandle));
constexpr uint32_t save_size = 1024 * 1024 / 8; // Maximum save size, 1Mbit for flash
-void preinit(uint8_t* rdram, uint8_t* rom);
+void preinit(uint8_t* rdram, uint8_t* rom, void* window_handle);
void save_init();
void init_scheduler();
-void init_events(uint8_t* rdram, uint8_t* rom);
+void init_events(uint8_t* rdram, uint8_t* rom, void* window_handle);
void init_timers(RDRAM_ARG1);
void set_self_paused(RDRAM_ARG1);
void wait_for_resumed(RDRAM_ARG1);
@@ -46,11 +46,30 @@ void send_si_message();
uint32_t get_speed_multiplier();
std::chrono::system_clock::time_point get_start();
std::chrono::system_clock::duration time_since_start();
+
+// Audio
void init_audio();
void set_audio_frequency(uint32_t freq);
void queue_audio_buffer(RDRAM_ARG PTR(s16) audio_data, uint32_t byte_count);
uint32_t get_remaining_audio_bytes();
+struct audio_callbacks_t {
+ using queue_samples_t = void(int16_t*, size_t);
+ using get_samples_remaining_t = size_t();
+ using set_frequency_t = void(uint32_t);
+ queue_samples_t* queue_samples;
+ get_samples_remaining_t* get_samples_remaining;
+ set_frequency_t* set_frequency;
+};
+void set_audio_callbacks(const audio_callbacks_t* callbacks);
+
+// Input
+struct input_callbacks_t {
+ using get_input_t = void(uint16_t*, float*, float*);
+ get_input_t* get_input;
+};
+void set_input_callbacks(const input_callbacks_t* callback);
+
class preemption_guard {
public:
preemption_guard();
diff --git a/portultra/ultrainit.cpp b/portultra/ultrainit.cpp
index 7d23675..1abfbac 100644
--- a/portultra/ultrainit.cpp
+++ b/portultra/ultrainit.cpp
@@ -1,9 +1,9 @@
#include "ultra64.h"
#include "multilibultra.hpp"
-void Multilibultra::preinit(uint8_t* rdram, uint8_t* rom) {
+void Multilibultra::preinit(uint8_t* rdram, uint8_t* rom, void* window_handle) {
Multilibultra::set_main_thread();
- Multilibultra::init_events(rdram, rom);
+ Multilibultra::init_events(rdram, rom, window_handle);
Multilibultra::init_timers(rdram);
Multilibultra::init_audio();
Multilibultra::save_init();
@@ -11,5 +11,4 @@ void Multilibultra::preinit(uint8_t* rdram, uint8_t* rom) {
extern "C" void osInitialize() {
Multilibultra::init_scheduler();
- //Multilibultra::native_init();
}
diff --git a/src/cont.cpp b/src/cont.cpp
index 52283a2..86ca632 100644
--- a/src/cont.cpp
+++ b/src/cont.cpp
@@ -1,6 +1,14 @@
#include "../portultra/multilibultra.hpp"
#include "recomp.h"
+static Multilibultra::input_callbacks_t input_callbacks;
+
+void Multilibultra::set_input_callbacks(const input_callbacks_t* callbacks) {
+ if (callbacks != nullptr) {
+ input_callbacks = *callbacks;
+ }
+}
+
static int max_controllers = 0;
extern "C" void osContInit_recomp(uint8_t* rdram, recomp_context* ctx) {
@@ -37,28 +45,24 @@ struct OSContPad {
u8 errno_;
};
-int button = 0;
-int stick_x = 0;
-int stick_y = 0;
-
-void press_button(int button) {
-
-}
-
-void release_button(int button) {
-
-}
-
extern "C" void osContGetReadData_recomp(uint8_t* rdram, recomp_context* ctx) {
int32_t pad = (int32_t)ctx->r4;
+ uint16_t buttons = 0;
+ float x = 0.0f;
+ float y = 0.0f;
+
+ if (input_callbacks.get_input) {
+ input_callbacks.get_input(&buttons, &x, &y);
+ }
+
if (max_controllers > 0) {
// button
- MEM_H(0, pad) = button;
+ MEM_H(0, pad) = buttons;
// stick_x
- MEM_B(2, pad) = stick_x;
+ MEM_B(2, pad) = (int8_t)(127 * x);
// stick_y
- MEM_B(3, pad) = stick_y;
+ MEM_B(3, pad) = (int8_t)(127 * y);
// errno
MEM_B(4, pad) = 0;
}
diff --git a/src/recomp.cpp b/src/recomp.cpp
index 957311c..9fc34ae 100644
--- a/src/recomp.cpp
+++ b/src/recomp.cpp
@@ -69,30 +69,10 @@ extern "C" void unload_overlays(int32_t ram_addr, uint32_t size);
#include
#endif
-int main(int argc, char **argv) {
- //if (argc != 2) {
- // printf("Usage: %s [baserom]\n", argv[0]);
- // exit(EXIT_SUCCESS);
- //}
-
-#ifdef _WIN32
- // Set up console output to accept UTF-8 on windows
- SetConsoleOutputCP(CP_UTF8);
-
- // Change to a font that supports Japanese characters
- CONSOLE_FONT_INFOEX cfi;
- cfi.cbSize = sizeof cfi;
- cfi.nFont = 0;
- cfi.dwFontSize.X = 0;
- cfi.dwFontSize.Y = 16;
- cfi.FontFamily = FF_DONTCARE;
- cfi.FontWeight = FW_NORMAL;
- wcscpy_s(cfi.FaceName, L"NSimSun");
- SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &cfi);
-#else
- std::setlocale(LC_ALL, "en_US.UTF-8");
-#endif
+std::unique_ptr rdram_buffer;
+recomp_context context{};
+__declspec(dllexport) extern "C" void init() {
{
std::basic_ifstream rom_file{ get_rom_name(), std::ios::binary };
@@ -128,9 +108,8 @@ int main(int argc, char **argv) {
load_overlays(0x1000, (int32_t)entrypoint, 1024 * 1024);
// Allocate rdram_buffer (16MB to give room for any extra addressable data used by recomp)
- std::unique_ptr rdram_buffer = std::make_unique(16 * 1024 * 1024);
+ rdram_buffer = std::make_unique(16 * 1024 * 1024);
std::memset(rdram_buffer.get(), 0, 8 * 1024 * 1024);
- recomp_context context{};
// Initial 1MB DMA (rom address 0x1000 = physical address 0x10001000)
do_rom_read(rdram_buffer.get(), entrypoint, 0x10001000, 0x100000);
@@ -152,14 +131,46 @@ int main(int argc, char **argv) {
MEM_W(osRomBase, 0) = 0xB0000000u; // standard rom base
MEM_W(osResetType, 0) = 0; // cold reset
MEM_W(osMemSize, 0) = 8 * 1024 * 1024; // 8MB
+}
- debug_printf("[Recomp] Starting\n");
+__declspec(dllexport) extern "C" void start(void* window_handle, const Multilibultra::audio_callbacks_t* audio_callbacks, const Multilibultra::input_callbacks_t* input_callbacks) {
+ Multilibultra::set_audio_callbacks(audio_callbacks);
+ Multilibultra::set_input_callbacks(input_callbacks);
+ std::thread game_thread{[](void* window_handle) {
+ debug_printf("[Recomp] Starting\n");
- Multilibultra::preinit(rdram_buffer.get(), rom.get());
+ Multilibultra::preinit(rdram_buffer.get(), rom.get(), window_handle);
- recomp_entrypoint(rdram_buffer.get(), &context);
+ recomp_entrypoint(rdram_buffer.get(), &context);
+
+ debug_printf("[Recomp] Quitting\n");
+ }, window_handle};
- debug_printf("[Recomp] Quitting\n");
+ game_thread.detach();
+}
+
+int main(int argc, char **argv) {
+
+#ifdef _WIN32
+ // Set up console output to accept UTF-8 on windows
+ SetConsoleOutputCP(CP_UTF8);
+
+ // Change to a font that supports Japanese characters
+ CONSOLE_FONT_INFOEX cfi;
+ cfi.cbSize = sizeof cfi;
+ cfi.nFont = 0;
+ cfi.dwFontSize.X = 0;
+ cfi.dwFontSize.Y = 16;
+ cfi.FontFamily = FF_DONTCARE;
+ cfi.FontWeight = FW_NORMAL;
+ wcscpy_s(cfi.FaceName, L"NSimSun");
+ SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &cfi);
+#else
+ std::setlocale(LC_ALL, "en_US.UTF-8");
+#endif
+
+ init();
+ start(nullptr, nullptr, nullptr);
return EXIT_SUCCESS;
}
diff --git a/src/rt64_layer.cpp b/src/rt64_layer.cpp
index 7acbcfe..192183b 100644
--- a/src/rt64_layer.cpp
+++ b/src/rt64_layer.cpp
@@ -3,7 +3,6 @@
#include "../portultra/multilibultra.hpp"
#include "rt64_layer.h"
-#include "SDL.h"
static uint8_t DMEM[0x1000];
static uint8_t IMEM[0x1000];
@@ -44,7 +43,7 @@ void dummy_check_interrupts() {
}
-void RT64Init(uint8_t* rom, uint8_t* rdram) {
+void RT64Init(uint8_t* rom, uint8_t* rdram, void* window_handle) {
// Dynamic loading
//auto RT64 = LoadLibrary("RT64.dll");
//if (RT64 == 0) {
@@ -57,6 +56,9 @@ void RT64Init(uint8_t* rom, uint8_t* rdram) {
//GET_FUNC(RT64, UpdateScreen);
GFX_INFO gfx_info{};
+ gfx_info.hWnd = window_handle;
+ gfx_info.hStatusBar = nullptr;
+
gfx_info.HEADER = rom;
gfx_info.RDRAM = rdram;
gfx_info.DMEM = DMEM;
@@ -89,9 +91,6 @@ void RT64Init(uint8_t* rom, uint8_t* rdram) {
gfx_info.VI_Y_SCALE_REG = &VI_Y_SCALE_REG;
gfx_info.CheckInterrupts = dummy_check_interrupts;
- gfx_info.version = 2;
- gfx_info.SP_STATUS_REG = &SP_STATUS_REG;
- gfx_info.RDRAM_SIZE = &RDRAM_SIZE;
InitiateGFX(gfx_info);
}
@@ -112,3 +111,7 @@ void RT64UpdateScreen(uint32_t vi_origin) {
UpdateScreen();
}
+
+void RT64ChangeWindow() {
+ ChangeWindow();
+}
diff --git a/us.rev1.toml b/us.rev1.toml
new file mode 100644
index 0000000..2fe8087
--- /dev/null
+++ b/us.rev1.toml
@@ -0,0 +1,84 @@
+# Config file for Majora's Mask NTSC 1.0 Recompilation.
+
+[input]
+entrypoint = 0x80080000
+# Paths are relative to the location of this config file.
+elf_path = "mm.us.rev1.elf"
+output_func_path = "RecompiledFuncs"
+relocatable_sections_path = "overlays.us.rev1.txt"
+
+[patches]
+stubs = [
+ # Stub out unused functions that directly manipulate RCP status.
+ "func_80084940",
+ "func_80084968",
+ # Stub out an unnecessary function that accesses kseg1 addresses.
+ "func_800818F4"
+]
+
+# Hooks
+
+# Function definition for the overlay loading function.
+[[patches.func]]
+name = "load_overlays"
+args = ["u32", "u32", "u32"]
+
+# Function hooks for overlay loading.
+[[patches.hook]]
+func = "Idle_InitCodeAndMemory"
+calls = "load_overlays"
+args = ["a2", "a1", "a3"]
+after_vram = 0x800802A4
+
+[[patches.hook]]
+func = "Load2_LoadOverlay"
+calls = "load_overlays"
+# args = [
+# "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
+
+# Write audio dlists to kseg0 (cached, 0x80...) addresses instead of kseg1 (uncached, 0xA0...) ones.
+# This saves the recompiler from needing to handle checking for kseg1 addresses on every memory operation.
+[[patches.instruction]]
+func = "AudioHeap_WritebackDCache"
+vram = 0x8018B510
+value = 0x3C010000 # lui $at, 0x2000 -> lui $at, 0x0000
+
+# These two may not be needed with RCP timeout detection disabled
+# # Pretend the RSP has already halted so that the game doesn't attempt to directly manipulate RSP registers.
+# [[patches.instruction]]
+# func = "Sched_HandleAudioCancel"
+# vram = 0x801763D8
+# value = 0x240F0001 # lw $t7, 0x0($s1) -> li $t7, 1
+
+# # Ditto.
+# [[patches.instruction]]
+# func = "Sched_HandleGfxCancel"
+# vram = 0x80176534
+# value = 0x240F0001 # lw $t7, 0x0($s1) -> li $t7, 1
+
+# Disable RCP timeout detection (sometimes throws false positives, not needed)
+[[patches.instruction]]
+func = "AudioMgr_HandleRetrace"
+vram = 0x80172DBC
+value = 0x00000000 # jal osSetTimer -> nop
+
+# Ditto.
+[[patches.instruction]]
+func = "Graph_TaskSet00"
+vram = 0x80174218
+value = 0x00000000 # jal osSetTimer -> nop
+
+# Prevent the minimap from drawing at a point where it can cause a crash when pausing.
+[[patches.instruction]]
+func = "func_80106644"
+vram = 0x80106684
+value = 0x29E10003 # slti $at, $t7, 0x4 -> slti $at, $t7, 0x3