Integrated RT64 (not included in repo), sign extended addresses
This commit is contained in:
parent
39b67c8468
commit
d0c3eb73ec
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -38,4 +38,7 @@ bld/
|
||||||
[Ll]ogs/
|
[Ll]ogs/
|
||||||
|
|
||||||
# Visual Studio 2015/2017 cache/options directory
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
.vs/
|
.vs/
|
||||||
|
|
||||||
|
# RT64 (since it's not public yet)
|
||||||
|
test/RT64
|
||||||
|
|
29
recomp.h
29
recomp.h
|
@ -24,25 +24,35 @@ typedef uint64_t gpr;
|
||||||
((gpr)(int32_t)((a) - (b)))
|
((gpr)(int32_t)((a) - (b)))
|
||||||
|
|
||||||
#define MEM_W(offset, reg) \
|
#define MEM_W(offset, reg) \
|
||||||
(*(int32_t*)(rdram + ((((reg) + (offset))) & 0x3FFFFFF)))
|
(*(int32_t*)(rdram + ((((reg) + (offset))) - 0xFFFFFFFF80000000)))
|
||||||
|
//(*(int32_t*)(rdram + ((((reg) + (offset))) & 0x3FFFFFF)))
|
||||||
|
|
||||||
#define MEM_H(offset, reg) \
|
#define MEM_H(offset, reg) \
|
||||||
(*(int16_t*)(rdram + ((((reg) + (offset)) ^ 2) & 0x3FFFFFF)))
|
(*(int16_t*)(rdram + ((((reg) + (offset)) ^ 2) - 0xFFFFFFFF80000000)))
|
||||||
|
//(*(int16_t*)(rdram + ((((reg) + (offset)) ^ 2) & 0x3FFFFFF)))
|
||||||
|
|
||||||
#define MEM_B(offset, reg) \
|
#define MEM_B(offset, reg) \
|
||||||
(*(int8_t*)(rdram + ((((reg) + (offset)) ^ 3) & 0x3FFFFFF)))
|
(*(int8_t*)(rdram + ((((reg) + (offset)) ^ 3) - 0xFFFFFFFF80000000)))
|
||||||
|
//(*(int8_t*)(rdram + ((((reg) + (offset)) ^ 3) & 0x3FFFFFF)))
|
||||||
|
|
||||||
#define MEM_HU(offset, reg) \
|
#define MEM_HU(offset, reg) \
|
||||||
(*(uint16_t*)(rdram + ((((reg) + (offset)) ^ 2) & 0x3FFFFFF)))
|
(*(uint16_t*)(rdram + ((((reg) + (offset)) ^ 2) - 0xFFFFFFFF80000000)))
|
||||||
|
//(*(uint16_t*)(rdram + ((((reg) + (offset)) ^ 2) & 0x3FFFFFF)))
|
||||||
|
|
||||||
#define MEM_BU(offset, reg) \
|
#define MEM_BU(offset, reg) \
|
||||||
(*(uint8_t*)(rdram + ((((reg) + (offset)) ^ 3) & 0x3FFFFFF)))
|
(*(uint8_t*)(rdram + ((((reg) + (offset)) ^ 3) - 0xFFFFFFFF80000000)))
|
||||||
|
//(*(uint8_t*)(rdram + ((((reg) + (offset)) ^ 3) & 0x3FFFFFF)))
|
||||||
|
|
||||||
#define SD(val, offset, reg) { \
|
#define SD(val, offset, reg) { \
|
||||||
*(uint32_t*)(rdram + ((((reg) + (offset) + 4)) & 0x3FFFFFF)) = (uint32_t)((val) >> 32); \
|
*(uint32_t*)(rdram + ((((reg) + (offset) + 4)) - 0xFFFFFFFF80000000)) = (uint32_t)((val) >> 0); \
|
||||||
*(uint32_t*)(rdram + ((((reg) + (offset) + 0)) & 0x3FFFFFF)) = (uint32_t)((val) >> 0); \
|
*(uint32_t*)(rdram + ((((reg) + (offset) + 0)) - 0xFFFFFFFF80000000)) = (uint32_t)((val) >> 32); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//#define SD(val, offset, reg) { \
|
||||||
|
// *(uint32_t*)(rdram + ((((reg) + (offset) + 4)) & 0x3FFFFFF)) = (uint32_t)((val) >> 32); \
|
||||||
|
// *(uint32_t*)(rdram + ((((reg) + (offset) + 0)) & 0x3FFFFFF)) = (uint32_t)((val) >> 0); \
|
||||||
|
//}
|
||||||
|
|
||||||
static inline uint64_t load_doubleword(uint8_t* rdram, gpr reg, gpr offset) {
|
static inline uint64_t load_doubleword(uint8_t* rdram, gpr reg, gpr offset) {
|
||||||
uint64_t ret = 0;
|
uint64_t ret = 0;
|
||||||
uint64_t lo = (uint64_t)(uint32_t)MEM_W(reg, offset + 4);
|
uint64_t lo = (uint64_t)(uint32_t)MEM_W(reg, offset + 4);
|
||||||
|
@ -56,7 +66,8 @@ static inline uint64_t load_doubleword(uint8_t* rdram, gpr reg, gpr offset) {
|
||||||
|
|
||||||
// TODO proper lwl/lwr/swl/swr
|
// TODO proper lwl/lwr/swl/swr
|
||||||
#define MEM_WL(offset, reg) \
|
#define MEM_WL(offset, reg) \
|
||||||
(*(int32_t*)(rdram + ((((reg) + (offset))) & 0x3FFFFFF)))
|
(*(int32_t*)(rdram + ((((reg) + (offset))) - 0xFFFFFFFF80000000)))
|
||||||
|
//(*(int32_t*)(rdram + ((((reg) + (offset))) & 0x3FFFFFF)))
|
||||||
|
|
||||||
#define S32(val) \
|
#define S32(val) \
|
||||||
((int32_t)(val))
|
((int32_t)(val))
|
||||||
|
@ -103,6 +114,8 @@ static inline uint64_t load_doubleword(uint8_t* rdram, gpr reg, gpr offset) {
|
||||||
#define NAN_CHECK(val) \
|
#define NAN_CHECK(val) \
|
||||||
assert(val == val)
|
assert(val == val)
|
||||||
|
|
||||||
|
//#define NAN_CHECK(val)
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
double d;
|
double d;
|
||||||
struct {
|
struct {
|
||||||
|
|
|
@ -104,7 +104,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
||||||
break;
|
break;
|
||||||
// Arithmetic
|
// Arithmetic
|
||||||
case InstrId::cpu_lui:
|
case InstrId::cpu_lui:
|
||||||
print_line("{}{} = {:#X} << 16", ctx_gpr_prefix(rt), rt, imm);
|
print_line("{}{} = S32({:#X} << 16)", ctx_gpr_prefix(rt), rt, imm);
|
||||||
break;
|
break;
|
||||||
case InstrId::cpu_addu:
|
case InstrId::cpu_addu:
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,7 +76,7 @@
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level3</WarningLevel>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)..;$(ProjectDir)thirdparty;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
|
@ -91,7 +91,7 @@
|
||||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level3</WarningLevel>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)..;$(ProjectDir)thirdparty;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
|
@ -104,21 +104,28 @@
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)..;$(ProjectDir)thirdparty;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
|
<AdditionalDependencies>$(ProjectDir)RT64\Debug\RT64.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Link>
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>XCOPY "$(ProjectDir)RT64\$(Configuration)\*" "$(TargetDir)" /S /Y</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)..;$(ProjectDir)thirdparty;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Console</SubSystem>
|
<SubSystem>Console</SubSystem>
|
||||||
</Link>
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>XCOPY "$(ProjectDir)RT64\$(Configuration)\*" "$(TargetDir)" /S /Y</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="funcs\adjust_texture_table.c" />
|
<ClCompile Include="funcs\adjust_texture_table.c" />
|
||||||
|
@ -2005,6 +2012,7 @@
|
||||||
<ClCompile Include="portultra\task_pthreads.cpp" />
|
<ClCompile Include="portultra\task_pthreads.cpp" />
|
||||||
<ClCompile Include="portultra\task_win32.cpp" />
|
<ClCompile Include="portultra\task_win32.cpp" />
|
||||||
<ClCompile Include="portultra\threads.cpp" />
|
<ClCompile Include="portultra\threads.cpp" />
|
||||||
|
<ClCompile Include="RT64\rt64_layer.cpp" />
|
||||||
<ClCompile Include="src\ai.cpp" />
|
<ClCompile Include="src\ai.cpp" />
|
||||||
<ClCompile Include="src\cont.cpp" />
|
<ClCompile Include="src\cont.cpp" />
|
||||||
<ClCompile Include="src\dp.cpp" />
|
<ClCompile Include="src\dp.cpp" />
|
||||||
|
@ -2020,7 +2028,11 @@
|
||||||
<ClInclude Include="..\recomp.h" />
|
<ClInclude Include="..\recomp.h" />
|
||||||
<ClInclude Include="portultra\multilibultra.hpp" />
|
<ClInclude Include="portultra\multilibultra.hpp" />
|
||||||
<ClInclude Include="portultra\platform_specific.h" />
|
<ClInclude Include="portultra\platform_specific.h" />
|
||||||
|
<ClInclude Include="RT64\rt64_layer.h" />
|
||||||
<ClInclude Include="portultra\ultra64.h" />
|
<ClInclude Include="portultra\ultra64.h" />
|
||||||
|
<ClInclude Include="thirdparty\blockingconcurrentqueue.h" />
|
||||||
|
<ClInclude Include="thirdparty\concurrentqueue.h" />
|
||||||
|
<ClInclude Include="thirdparty\lightweightsemaphore.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
|
|
@ -5700,6 +5700,9 @@
|
||||||
<ClCompile Include="portultra\events.cpp">
|
<ClCompile Include="portultra\events.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="RT64\rt64_layer.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="portultra\platform_specific.h">
|
<ClInclude Include="portultra\platform_specific.h">
|
||||||
|
@ -5714,5 +5717,17 @@
|
||||||
<ClInclude Include="..\recomp.h">
|
<ClInclude Include="..\recomp.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="RT64\rt64_layer.h">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="thirdparty\blockingconcurrentqueue.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="thirdparty\concurrentqueue.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="thirdparty\lightweightsemaphore.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -2,11 +2,25 @@
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include "blockingconcurrentqueue.h"
|
||||||
|
|
||||||
#include "ultra64.h"
|
#include "ultra64.h"
|
||||||
#include "multilibultra.hpp"
|
#include "multilibultra.hpp"
|
||||||
#include "recomp.h"
|
#include "recomp.h"
|
||||||
|
|
||||||
|
struct SpTaskAction {
|
||||||
|
OSTask task;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SwapBuffersAction {
|
||||||
|
int32_t origin;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Action = std::variant<SpTaskAction, SwapBuffersAction>;
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
struct {
|
struct {
|
||||||
std::thread thread;
|
std::thread thread;
|
||||||
|
@ -18,8 +32,6 @@ static struct {
|
||||||
std::thread thread;
|
std::thread thread;
|
||||||
PTR(OSMesgQueue) mq = NULLPTR;
|
PTR(OSMesgQueue) mq = NULLPTR;
|
||||||
OSMesg msg = (OSMesg)0;
|
OSMesg msg = (OSMesg)0;
|
||||||
OSTask task;
|
|
||||||
std::atomic_flag task_queued;
|
|
||||||
} sp;
|
} sp;
|
||||||
struct {
|
struct {
|
||||||
std::thread thread;
|
std::thread thread;
|
||||||
|
@ -35,12 +47,12 @@ static struct {
|
||||||
std::thread thread;
|
std::thread thread;
|
||||||
PTR(OSMesgQueue) mq = NULLPTR;
|
PTR(OSMesgQueue) mq = NULLPTR;
|
||||||
OSMesg msg = (OSMesg)0;
|
OSMesg msg = (OSMesg)0;
|
||||||
std::atomic_flag read_queued;
|
|
||||||
} si;
|
} si;
|
||||||
// The same message queue may be used for multiple events, so share a mutex for all of them
|
// The same message queue may be used for multiple events, so share a mutex for all of them
|
||||||
std::mutex message_mutex;
|
std::mutex message_mutex;
|
||||||
uint8_t* rdram;
|
uint8_t* rdram;
|
||||||
std::chrono::system_clock::time_point start;
|
std::chrono::system_clock::time_point start;
|
||||||
|
moodycamel::BlockingConcurrentQueue<Action> action_queue{};
|
||||||
} events_context{};
|
} events_context{};
|
||||||
|
|
||||||
extern "C" void osSetEventMesg(RDRAM_ARG OSEvent event_id, PTR(OSMesgQueue) mq_, OSMesg msg) {
|
extern "C" void osSetEventMesg(RDRAM_ARG OSEvent event_id, PTR(OSMesgQueue) mq_, OSMesg msg) {
|
||||||
|
@ -73,8 +85,10 @@ extern "C" void osViSetEvent(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, u32 ret
|
||||||
events_context.vi.retrace_count = retrace_count;
|
events_context.vi.retrace_count = retrace_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr uint32_t speed_multiplier = 10;
|
||||||
|
|
||||||
// N64 CPU counter ticks per millisecond
|
// N64 CPU counter ticks per millisecond
|
||||||
constexpr uint32_t counter_per_ms = 46'875;
|
constexpr uint32_t counter_per_ms = 46'875 * speed_multiplier;
|
||||||
|
|
||||||
uint64_t duration_to_count(std::chrono::system_clock::duration duration) {
|
uint64_t duration_to_count(std::chrono::system_clock::duration duration) {
|
||||||
uint64_t delta_micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
|
uint64_t delta_micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
|
||||||
|
@ -107,7 +121,7 @@ void vi_thread_func() {
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// 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 = events_context.start + (total_vis * 1000000us) / 60;
|
auto next = events_context.start + (total_vis * 1000000us) / (60 * speed_multiplier);
|
||||||
//if (next > std::chrono::system_clock::now()) {
|
//if (next > std::chrono::system_clock::now()) {
|
||||||
// printf("Sleeping for %" PRIu64 " us to get from %" PRIu64 " us to %" PRIu64 " us \n",
|
// printf("Sleeping for %" PRIu64 " us to get from %" PRIu64 " us to %" PRIu64 " us \n",
|
||||||
// (next - std::chrono::system_clock::now()) / 1us,
|
// (next - std::chrono::system_clock::now()) / 1us,
|
||||||
|
@ -118,22 +132,29 @@ void vi_thread_func() {
|
||||||
//}
|
//}
|
||||||
std::this_thread::sleep_until(next);
|
std::this_thread::sleep_until(next);
|
||||||
// Calculate how many VIs have passed
|
// Calculate how many VIs have passed
|
||||||
uint64_t new_total_vis = ((std::chrono::system_clock::now() - events_context.start) * 60 / 1000ms) + 1;
|
uint64_t new_total_vis = ((std::chrono::system_clock::now() - events_context.start) * (60 * speed_multiplier) / 1000ms) + 1;
|
||||||
if (new_total_vis > total_vis + 1) {
|
if (new_total_vis > total_vis + 1) {
|
||||||
printf("Skipped % " PRId64 " frames in VI interupt thread!\n", new_total_vis - total_vis - 1);
|
//printf("Skipped % " PRId64 " frames in VI interupt thread!\n", new_total_vis - total_vis - 1);
|
||||||
}
|
}
|
||||||
total_vis = new_total_vis;
|
total_vis = new_total_vis;
|
||||||
|
|
||||||
remaining_retraces--;
|
remaining_retraces--;
|
||||||
|
|
||||||
if (remaining_retraces == 0) {
|
{
|
||||||
std::lock_guard lock{ events_context.message_mutex };
|
std::lock_guard lock{ events_context.message_mutex };
|
||||||
remaining_retraces = events_context.vi.retrace_count;
|
|
||||||
uint8_t* rdram = events_context.rdram;
|
uint8_t* rdram = events_context.rdram;
|
||||||
|
if (remaining_retraces == 0) {
|
||||||
|
remaining_retraces = events_context.vi.retrace_count;
|
||||||
|
|
||||||
if (events_context.vi.mq != NULLPTR) {
|
if (events_context.vi.mq != NULLPTR) {
|
||||||
if (osSendMesg(PASS_RDRAM events_context.vi.mq, events_context.vi.msg, OS_MESG_NOBLOCK) == -1) {
|
if (osSendMesg(PASS_RDRAM events_context.vi.mq, events_context.vi.msg, OS_MESG_NOBLOCK) == -1) {
|
||||||
//printf("Game skipped a VI frame!\n");
|
//printf("Game skipped a VI frame!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (events_context.ai.mq != NULLPTR) {
|
||||||
|
if (osSendMesg(PASS_RDRAM events_context.ai.mq, events_context.ai.msg, OS_MESG_NOBLOCK) == -1) {
|
||||||
|
//printf("Game skipped a AI frame!\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,41 +173,49 @@ void dp_complete() {
|
||||||
osSendMesg(PASS_RDRAM events_context.dp.mq, events_context.dp.msg, OS_MESG_NOBLOCK);
|
osSendMesg(PASS_RDRAM events_context.dp.mq, events_context.dp.msg, OS_MESG_NOBLOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gfx_thread_func() {
|
void RT64Init(uint8_t* rom, uint8_t* rdram);
|
||||||
|
void RT64SendDL(uint8_t* rdram, const OSTask* task);
|
||||||
|
void RT64UpdateScreen(uint32_t vi_origin);
|
||||||
|
void RT64PumpEvents();
|
||||||
|
|
||||||
|
void gfx_thread_func(uint8_t* rdram, uint8_t* rom) {
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
RT64Init(rom, rdram);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// Wait for a sp task to be queued
|
// Try to pull an action from the queue
|
||||||
events_context.sp.task_queued.wait(false);
|
Action action;
|
||||||
|
if (events_context.action_queue.wait_dequeue_timed(action, 1ms)) {
|
||||||
// Grab the task and inform the game that it's free to queue up a new task
|
// Determine the action type and act on it
|
||||||
OSTask current_task = events_context.sp.task;
|
if (const auto* task_action = std::get_if<SpTaskAction>(&action)) {
|
||||||
events_context.sp.task_queued.clear();
|
if (task_action->task.t.type == M_GFXTASK) {
|
||||||
events_context.sp.task_queued.notify_all();
|
// (TODO let RT64 do this) Tell the game that the RSP and RDP tasks are complete
|
||||||
|
RT64SendDL(rdram, &task_action->task);
|
||||||
// Process the task
|
sp_complete();
|
||||||
if (current_task.t.type = M_GFXTASK) {
|
dp_complete();
|
||||||
// TODO interface with RT64 here
|
} else if (task_action->task.t.type == M_AUDTASK) {
|
||||||
|
sp_complete();
|
||||||
// (TODO let RT64 do this) Tell the game that the RSP and RDP tasks are complete
|
} else {
|
||||||
sp_complete();
|
fprintf(stderr, "Unknown task type: %" PRIu32 "\n", task_action->task.t.type);
|
||||||
dp_complete();
|
std::exit(EXIT_FAILURE);
|
||||||
} else if (current_task.t.type == M_AUDTASK) {
|
}
|
||||||
sp_complete();
|
} else if (const auto* swap_action = std::get_if<SwapBuffersAction>(&action)) {
|
||||||
} else {
|
RT64UpdateScreen(swap_action->origin);
|
||||||
fprintf(stderr, "Unknown task type: %" PRIu32 "\n", current_task.t.type);
|
}
|
||||||
std::exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle events
|
||||||
|
RT64PumpEvents();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr) {
|
||||||
|
events_context.action_queue.enqueue(SwapBuffersAction{ frameBufPtr + 640 });
|
||||||
|
}
|
||||||
|
|
||||||
void Multilibultra::submit_rsp_task(RDRAM_ARG PTR(OSTask) task_) {
|
void Multilibultra::submit_rsp_task(RDRAM_ARG PTR(OSTask) task_) {
|
||||||
OSTask* task = TO_PTR(OSTask, task_);
|
OSTask* task = TO_PTR(OSTask, task_);
|
||||||
// Wait for the sp thread clear the old task
|
events_context.action_queue.enqueue(SpTaskAction{ *task });
|
||||||
events_context.sp.task_queued.wait(true);
|
|
||||||
// Make a full copy of the task instead of just recording a pointer to it, since that's what osSpTaskLoad does
|
|
||||||
events_context.sp.task = *task;
|
|
||||||
|
|
||||||
events_context.sp.task_queued.test_and_set();
|
|
||||||
events_context.sp.task_queued.notify_all();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Multilibultra::send_si_message() {
|
void Multilibultra::send_si_message() {
|
||||||
|
@ -194,8 +223,8 @@ void Multilibultra::send_si_message() {
|
||||||
osSendMesg(PASS_RDRAM events_context.si.mq, events_context.si.msg, OS_MESG_NOBLOCK);
|
osSendMesg(PASS_RDRAM events_context.si.mq, events_context.si.msg, OS_MESG_NOBLOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Multilibultra::init_events(uint8_t* rdram) {
|
void Multilibultra::init_events(uint8_t* rdram, uint8_t* rom) {
|
||||||
events_context.rdram = rdram;
|
events_context.rdram = rdram;
|
||||||
events_context.vi.thread = std::thread{ vi_thread_func };
|
events_context.vi.thread = std::thread{ vi_thread_func };
|
||||||
events_context.sp.thread = std::thread{ gfx_thread_func };
|
events_context.sp.thread = std::thread{ gfx_thread_func, rdram, rom };
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#include "ultra64.h"
|
#include "ultra64.h"
|
||||||
#include "multilibultra.hpp"
|
#include "multilibultra.hpp"
|
||||||
|
|
||||||
void Multilibultra::preinit(uint8_t* rdram) {
|
void Multilibultra::preinit(uint8_t* rdram, uint8_t* rom) {
|
||||||
Multilibultra::set_main_thread();
|
Multilibultra::set_main_thread();
|
||||||
Multilibultra::init_events(rdram);
|
Multilibultra::init_events(rdram, rom);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void osInitialize() {
|
extern "C" void osInitialize() {
|
||||||
|
|
|
@ -167,7 +167,7 @@ extern "C" s32 osRecvMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, PTR(OSMesg) msg_, s32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg != nullptr) {
|
if (msg_ != NULLPTR) {
|
||||||
*msg = TO_PTR(OSMesg, mq->msg)[mq->first];
|
*msg = TO_PTR(OSMesg, mq->msg)[mq->first];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,10 @@ struct UltraThreadContext {
|
||||||
|
|
||||||
namespace Multilibultra {
|
namespace Multilibultra {
|
||||||
|
|
||||||
void preinit(uint8_t* rdram);
|
void preinit(uint8_t* rdram, uint8_t* rom);
|
||||||
void native_init();
|
void native_init();
|
||||||
void init_scheduler();
|
void init_scheduler();
|
||||||
void init_events(uint8_t* rdram);
|
void init_events(uint8_t* rdram, uint8_t* rom);
|
||||||
void native_thread_init(OSThread *t);
|
void native_thread_init(OSThread *t);
|
||||||
void set_self_paused(RDRAM_ARG1);
|
void set_self_paused(RDRAM_ARG1);
|
||||||
void wait_for_resumed(RDRAM_ARG1);
|
void wait_for_resumed(RDRAM_ARG1);
|
||||||
|
|
|
@ -29,7 +29,7 @@ int main(int argc, char** argv) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
void run_thread_function(uint8_t* rdram, uint32_t addr, uint32_t sp, uint32_t arg);
|
void run_thread_function(uint8_t* rdram, uint64_t addr, uint64_t sp, uint64_t arg);
|
||||||
#else
|
#else
|
||||||
#define run_thread_function(func, sp, arg) func(arg)
|
#define run_thread_function(func, sp, arg) func(arg)
|
||||||
#endif
|
#endif
|
||||||
|
@ -66,7 +66,7 @@ extern "C" void osStartThread(RDRAM_ARG PTR(OSThread) t_) {
|
||||||
OSThread* t = TO_PTR(OSThread, t_);
|
OSThread* t = TO_PTR(OSThread, t_);
|
||||||
debug_printf("[os] Start Thread %d\n", t->id);
|
debug_printf("[os] Start Thread %d\n", t->id);
|
||||||
|
|
||||||
// Wait until the thread is initialized to indicate that it's task_queued to be started.
|
// Wait until the thread is initialized to indicate that it's action_queued to be started.
|
||||||
t->context->initialized.wait(false);
|
t->context->initialized.wait(false);
|
||||||
|
|
||||||
debug_printf("[os] Thread %d is ready to be started\n", t->id);
|
debug_printf("[os] Thread %d is ready to be started\n", t->id);
|
||||||
|
|
|
@ -25,12 +25,12 @@ typedef uint16_t u16;
|
||||||
typedef int8_t s8;
|
typedef int8_t s8;
|
||||||
typedef uint8_t u8;
|
typedef uint8_t u8;
|
||||||
|
|
||||||
#define PTR(x) uint32_t
|
#define PTR(x) int32_t
|
||||||
#define RDRAM_ARG uint8_t *rdram,
|
#define RDRAM_ARG uint8_t *rdram,
|
||||||
#define RDRAM_ARG1 uint8_t *rdram
|
#define RDRAM_ARG1 uint8_t *rdram
|
||||||
#define PASS_RDRAM rdram,
|
#define PASS_RDRAM rdram,
|
||||||
#define PASS_RDRAM1 rdram
|
#define PASS_RDRAM1 rdram
|
||||||
#define TO_PTR(type, var) ((type*)(&rdram[var & 0x3FFFFFF]))
|
#define TO_PTR(type, var) ((type*)(&rdram[(uint64_t)var - 0xFFFFFFFF80000000]))
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#define NULLPTR (PTR(void))0
|
#define NULLPTR (PTR(void))0
|
||||||
#endif
|
#endif
|
||||||
|
@ -89,7 +89,7 @@ typedef struct OSThread_t {
|
||||||
OSId id;
|
OSId id;
|
||||||
int32_t pad3;
|
int32_t pad3;
|
||||||
UltraThreadContext* context; // An actual pointer regardless of platform
|
UltraThreadContext* context; // An actual pointer regardless of platform
|
||||||
uint32_t sp;
|
int32_t sp;
|
||||||
} OSThread;
|
} OSThread;
|
||||||
|
|
||||||
typedef u32 OSEvent;
|
typedef u32 OSEvent;
|
||||||
|
@ -161,6 +161,7 @@ s32 osJamMesg(RDRAM_ARG PTR(OSMesgQueue), OSMesg, s32);
|
||||||
s32 osRecvMesg(RDRAM_ARG PTR(OSMesgQueue), PTR(OSMesg), s32);
|
s32 osRecvMesg(RDRAM_ARG PTR(OSMesgQueue), PTR(OSMesg), s32);
|
||||||
void osSetEventMesg(RDRAM_ARG OSEvent, PTR(OSMesgQueue), OSMesg);
|
void osSetEventMesg(RDRAM_ARG OSEvent, PTR(OSMesgQueue), OSMesg);
|
||||||
void osViSetEvent(RDRAM_ARG PTR(OSMesgQueue), OSMesg, u32);
|
void osViSetEvent(RDRAM_ARG PTR(OSMesgQueue), OSMesg, u32);
|
||||||
|
void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr);
|
||||||
u32 osGetCount();
|
u32 osGetCount();
|
||||||
OSTime osGetTime();
|
OSTime osGetTime();
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
#include "recomp.h"
|
#include "recomp.h"
|
||||||
|
|
||||||
extern "C" void osAiSetFrequency_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
extern "C" void osAiSetFrequency_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||||
;
|
ctx->r2 = ctx->r4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t ai_length = 0;
|
||||||
|
|
||||||
extern "C" void osAiSetNextBuffer_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
extern "C" void osAiSetNextBuffer_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||||
;
|
ai_length = (uint32_t)ctx->r5;
|
||||||
|
ctx->r2 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void osAiGetLength_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
extern "C" void osAiGetLength_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||||
;
|
ctx->r2 = ai_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void osAiGetStatus_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
extern "C" void osAiGetStatus_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||||
;
|
ctx->r2 = 0x80000000;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,24 @@ extern "C" void osContStartReadData_recomp(uint8_t* restrict rdram, recomp_conte
|
||||||
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* restrict rdram, recomp_context* restrict ctx) {
|
extern "C" void osContGetReadData_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||||
;
|
int32_t pad = (uint32_t)ctx->r4;
|
||||||
|
|
||||||
|
// button
|
||||||
|
MEM_H(0, pad) = 0;
|
||||||
|
// stick_x
|
||||||
|
MEM_B(2, pad) = 0;
|
||||||
|
// stick_y
|
||||||
|
MEM_B(3, pad) = 0;
|
||||||
|
// errno
|
||||||
|
MEM_B(4, pad) = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void osMotorInit_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
extern "C" void osMotorInit_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||||
|
|
|
@ -16,7 +16,7 @@ extern "C" void osCreatePiManager_recomp(uint8_t* restrict rdram, recomp_context
|
||||||
|
|
||||||
constexpr uint32_t rom_base = 0xB0000000;
|
constexpr uint32_t rom_base = 0xB0000000;
|
||||||
|
|
||||||
void do_rom_read(uint8_t* rdram, uint32_t ram_address, uint32_t dev_address, size_t num_bytes) {
|
void do_rom_read(uint8_t* rdram, int32_t ram_address, uint32_t dev_address, size_t num_bytes) {
|
||||||
// TODO use word copies when possible
|
// TODO use word copies when possible
|
||||||
uint8_t* rom_addr = rom.get() + (dev_address | rom_base) - rom_base;
|
uint8_t* rom_addr = rom.get() + (dev_address | rom_base) - rom_base;
|
||||||
for (size_t i = 0; i < num_bytes; i++) {
|
for (size_t i = 0; i < num_bytes; i++) {
|
||||||
|
@ -30,7 +30,7 @@ extern "C" void osPiStartDma_recomp(uint8_t* restrict rdram, recomp_context* res
|
||||||
uint32_t pri = ctx->r5;
|
uint32_t pri = ctx->r5;
|
||||||
uint32_t direction = ctx->r6;
|
uint32_t direction = ctx->r6;
|
||||||
uint32_t devAddr = ctx->r7;
|
uint32_t devAddr = ctx->r7;
|
||||||
uint32_t dramAddr = MEM_W(0x10, ctx->r29);
|
int32_t dramAddr = MEM_W(0x10, ctx->r29);
|
||||||
uint32_t size = MEM_W(0x14, ctx->r29);
|
uint32_t size = MEM_W(0x14, ctx->r29);
|
||||||
uint32_t mq_ = MEM_W(0x18, ctx->r29);
|
uint32_t mq_ = MEM_W(0x18, ctx->r29);
|
||||||
OSMesgQueue* mq = TO_PTR(OSMesgQueue, mq_);
|
OSMesgQueue* mq = TO_PTR(OSMesgQueue, mq_);
|
||||||
|
|
|
@ -35,8 +35,8 @@ extern "C" recomp_func_t* get_function(uint32_t addr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void bzero(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
extern "C" void bzero(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||||
uint32_t start_addr = ctx->r4;
|
gpr start_addr = ctx->r4;
|
||||||
uint32_t size = ctx->r5;
|
gpr size = ctx->r5;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < size; i++) {
|
for (uint32_t i = 0; i < size; i++) {
|
||||||
MEM_B(start_addr, i) = 0;
|
MEM_B(start_addr, i) = 0;
|
||||||
|
@ -53,7 +53,7 @@ extern "C" void do_break(uint32_t vram) {
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void run_thread_function(uint8_t* rdram, uint32_t addr, uint32_t sp, uint32_t arg) {
|
void run_thread_function(uint8_t* rdram, uint64_t addr, uint64_t sp, uint64_t arg) {
|
||||||
recomp_context ctx{};
|
recomp_context ctx{};
|
||||||
ctx.r29 = sp;
|
ctx.r29 = sp;
|
||||||
ctx.r4 = arg;
|
ctx.r4 = arg;
|
||||||
|
@ -62,7 +62,7 @@ void run_thread_function(uint8_t* rdram, uint32_t addr, uint32_t sp, uint32_t ar
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void game_init(uint8_t* restrict rdram, recomp_context* restrict ctx);
|
extern "C" void game_init(uint8_t* restrict rdram, recomp_context* restrict ctx);
|
||||||
void do_rom_read(uint8_t* rdram, uint32_t ram_address, uint32_t dev_address, size_t num_bytes);
|
void do_rom_read(uint8_t* rdram, int32_t ram_address, uint32_t dev_address, size_t num_bytes);
|
||||||
|
|
||||||
std::unique_ptr<uint8_t[]> rom;
|
std::unique_ptr<uint8_t[]> rom;
|
||||||
size_t rom_size;
|
size_t rom_size;
|
||||||
|
@ -105,7 +105,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
// Get entrypoint from ROM
|
// Get entrypoint from ROM
|
||||||
// TODO fix this for other IPL3 versions
|
// TODO fix this for other IPL3 versions
|
||||||
uint32_t entrypoint = byteswap(*reinterpret_cast<uint32_t*>(rom.get() + 0x8));
|
int32_t entrypoint = byteswap(*reinterpret_cast<uint32_t*>(rom.get() + 0x8));
|
||||||
|
|
||||||
// Allocate rdram_buffer
|
// Allocate rdram_buffer
|
||||||
std::unique_ptr<uint8_t[]> rdram_buffer = std::make_unique<uint8_t[]>(8 * 1024 * 1024);
|
std::unique_ptr<uint8_t[]> rdram_buffer = std::make_unique<uint8_t[]>(8 * 1024 * 1024);
|
||||||
|
@ -134,17 +134,17 @@ int main(int argc, char **argv) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Set up stack pointer
|
// Set up stack pointer
|
||||||
context.r29 = 0x803FFFF0u;
|
context.r29 = 0xFFFFFFFF803FFFF0u;
|
||||||
|
|
||||||
// Initialize variables normally set by IPL3
|
// Initialize variables normally set by IPL3
|
||||||
constexpr uint32_t osTvType = 0x80000300;
|
constexpr int32_t osTvType = 0x80000300;
|
||||||
constexpr uint32_t osRomType = 0x80000304;
|
constexpr int32_t osRomType = 0x80000304;
|
||||||
constexpr uint32_t osRomBase = 0x80000308;
|
constexpr int32_t osRomBase = 0x80000308;
|
||||||
constexpr uint32_t osResetType = 0x8000030c;
|
constexpr int32_t osResetType = 0x8000030c;
|
||||||
constexpr uint32_t osCicId = 0x80000310;
|
constexpr int32_t osCicId = 0x80000310;
|
||||||
constexpr uint32_t osVersion = 0x80000314;
|
constexpr int32_t osVersion = 0x80000314;
|
||||||
constexpr uint32_t osMemSize = 0x80000318;
|
constexpr int32_t osMemSize = 0x80000318;
|
||||||
constexpr uint32_t osAppNMIBuffer = 0x8000031c;
|
constexpr int32_t osAppNMIBuffer = 0x8000031c;
|
||||||
uint8_t *rdram = rdram_buffer.get();
|
uint8_t *rdram = rdram_buffer.get();
|
||||||
MEM_W(osTvType, 0) = 1; // NTSC
|
MEM_W(osTvType, 0) = 1; // NTSC
|
||||||
MEM_W(osRomBase, 0) = 0xB0000000u; // standard rom base
|
MEM_W(osRomBase, 0) = 0xB0000000u; // standard rom base
|
||||||
|
@ -157,7 +157,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
debug_printf("[Recomp] Starting\n");
|
debug_printf("[Recomp] Starting\n");
|
||||||
|
|
||||||
Multilibultra::preinit(rdram_buffer.get());
|
Multilibultra::preinit(rdram_buffer.get(), rom.get());
|
||||||
|
|
||||||
game_init(rdram_buffer.get(), &context);
|
game_init(rdram_buffer.get(), &context);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ extern "C" void osSpTaskStartGo_recomp(uint8_t* restrict rdram, recomp_context*
|
||||||
//printf("[sp] osSpTaskStartGo(0x%08X)\n", (uint32_t)ctx->r4);
|
//printf("[sp] osSpTaskStartGo(0x%08X)\n", (uint32_t)ctx->r4);
|
||||||
OSTask* task = TO_PTR(OSTask, ctx->r4);
|
OSTask* task = TO_PTR(OSTask, ctx->r4);
|
||||||
if (task->t.type == M_GFXTASK) {
|
if (task->t.type == M_GFXTASK) {
|
||||||
printf("[sp] Gfx task: %08X\n", (uint32_t)ctx->r4);
|
//printf("[sp] Gfx task: %08X\n", (uint32_t)ctx->r4);
|
||||||
} else if (task->t.type == M_AUDTASK) {
|
} else if (task->t.type == M_AUDTASK) {
|
||||||
printf("[sp] Audio task: %08X\n", (uint32_t)ctx->r4);
|
printf("[sp] Audio task: %08X\n", (uint32_t)ctx->r4);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include "../portultra/multilibultra.hpp"
|
||||||
#include "recomp.h"
|
#include "recomp.h"
|
||||||
|
|
||||||
extern "C" void osCreateViManager_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
extern "C" void osCreateViManager_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||||
|
@ -21,7 +22,7 @@ extern "C" void osViGetNextFramebuffer_recomp(uint8_t* restrict rdram, recomp_co
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void osViSwapBuffer_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
extern "C" void osViSwapBuffer_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||||
;
|
osViSwapBuffer(rdram, ctx->r4);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void osViSetMode_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
extern "C" void osViSetMode_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
|
||||||
|
|
Loading…
Reference in a new issue