Build system & dependency refactoring
Switched to a makefile + clang build system Now builds as a library by default Removed SDL2 dependency Changed RT64 integration to use zilmar spec in order to pass a window handle Proper unaligned loads and stores implementation
This commit is contained in:
parent
73308e3500
commit
0fdfc5f7fe
10
MMRecomp.sln
10
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
|
||||
|
|
|
@ -40,13 +40,13 @@
|
|||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
|
@ -78,9 +78,15 @@
|
|||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<NMakeBuildCommandLine>make TARGET=$(Configuration) LIB_DIR="$(VC_LibraryPath_VC_x64_Desktop)" UCRT_DIR="$(UniversalCRT_LibraryPath_x64)" SDK_DIR="$(WindowsSDK_LibraryPath)\x64" -j$(NUMBER_OF_PROCESSORS)</NMakeBuildCommandLine>
|
||||
<NMakeCleanCommandLine>make TARGET=$(Configuration) clean</NMakeCleanCommandLine>
|
||||
<OutDir>$(SolutionDir)build\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<NMakeBuildCommandLine>make TARGET=$(Configuration) LIB_DIR="$(VC_LibraryPath_VC_x64_Desktop)" UCRT_DIR="$(UniversalCRT_LibraryPath_x64)" SDK_DIR="$(WindowsSDK_LibraryPath)\x64" -j$(NUMBER_OF_PROCESSORS)</NMakeBuildCommandLine>
|
||||
<NMakeCleanCommandLine>make TARGET=$(Configuration) clean</NMakeCleanCommandLine>
|
||||
<OutDir>$(SolutionDir)build\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
|
@ -225,11 +231,6 @@ XCOPY "$(ProjectDir)lib\SDL2-2.24.0\lib\$(Platform)\SDL2.dll" "$(TargetDir)" /S
|
|||
<ClInclude Include="portultra\ultra64.h" />
|
||||
<ClInclude Include="src\euc-jp.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="RecompiledFuncs.vcxproj">
|
||||
<Project>{7bf5e3f9-c49f-4c84-ab64-7681f028cac2}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
|
|
|
@ -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
|
|
@ -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) \
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,55 +1,39 @@
|
|||
#include "ultra64.h"
|
||||
#include "multilibultra.hpp"
|
||||
#include "SDL.h"
|
||||
#include "SDL_audio.h"
|
||||
#include <cassert>
|
||||
|
||||
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<uint16_t> swap_buffer;
|
||||
static std::vector<int16_t> 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<uint32_t>(buffer_offset_frames * sizeof(int16_t) * samples_per_vi)) {
|
||||
buffered_byte_count -= static_cast<uint32_t>(buffer_offset_frames * sizeof(int16_t) * samples_per_vi);
|
||||
}
|
||||
else {
|
||||
buffered_byte_count = 0;
|
||||
}
|
||||
return buffered_byte_count;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <queue>
|
||||
|
||||
#include <Windows.h>
|
||||
#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<SDL_Scancode, int> 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,18 +228,9 @@ 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();
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
34
src/cont.cpp
34
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;
|
||||
}
|
||||
|
|
|
@ -69,30 +69,10 @@ extern "C" void unload_overlays(int32_t ram_addr, uint32_t size);
|
|||
#include <Windows.h>
|
||||
#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<uint8_t[]> rdram_buffer;
|
||||
recomp_context context{};
|
||||
|
||||
__declspec(dllexport) extern "C" void init() {
|
||||
{
|
||||
std::basic_ifstream<uint8_t> 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<uint8_t[]> rdram_buffer = std::make_unique<uint8_t[]>(16 * 1024 * 1024);
|
||||
rdram_buffer = std::make_unique<uint8_t[]>(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");
|
||||
debug_printf("[Recomp] Quitting\n");
|
||||
}, window_handle};
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue