Created solution for testing output

This commit is contained in:
Mr-Wiseguy 2022-11-15 19:55:48 -05:00
parent 5d9ea96abc
commit b94fe6f5fb
24 changed files with 9225 additions and 2 deletions

5
.gitignore vendored
View file

@ -1,11 +1,12 @@
# VSCode file settings # VSCode file settings
.vscode/settings.json .vscode/settings.json
# Input elf files # Input elf and rom files
*.elf *.elf
*.z64
# Output C files # Output C files
out/ test/funcs
# Linux build output # Linux build output
build/ build/

31
test/RecompTest.sln Normal file
View file

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.32929.386
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RecompTest", "RecompTest.vcxproj", "{73819ED8-8A5B-4554-B3F3-60257A43F296}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{73819ED8-8A5B-4554-B3F3-60257A43F296}.Debug|x64.ActiveCfg = Debug|x64
{73819ED8-8A5B-4554-B3F3-60257A43F296}.Debug|x64.Build.0 = Debug|x64
{73819ED8-8A5B-4554-B3F3-60257A43F296}.Debug|x86.ActiveCfg = Debug|Win32
{73819ED8-8A5B-4554-B3F3-60257A43F296}.Debug|x86.Build.0 = Debug|Win32
{73819ED8-8A5B-4554-B3F3-60257A43F296}.Release|x64.ActiveCfg = Release|x64
{73819ED8-8A5B-4554-B3F3-60257A43F296}.Release|x64.Build.0 = Release|x64
{73819ED8-8A5B-4554-B3F3-60257A43F296}.Release|x86.ActiveCfg = Release|Win32
{73819ED8-8A5B-4554-B3F3-60257A43F296}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DD740FE2-99F7-4C80-B124-06D14447F873}
EndGlobalSection
EndGlobal

2028
test/RecompTest.vcxproj Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

7
test/portultra/init.cpp Normal file
View file

@ -0,0 +1,7 @@
#include "ultra64.h"
#include "multilibultra.hpp"
extern "C" void osInitialize() {
Multilibultra::init_scheduler();
Multilibultra::native_init();
}

83
test/portultra/main.c Normal file
View file

@ -0,0 +1,83 @@
#if 0
#include <stdio.h>
#include <stdlib.h>
#include "ultra64.h"
#define THREAD_STACK_SIZE 0x1000
u8 idle_stack[THREAD_STACK_SIZE] ALIGNED(16);
u8 main_stack[THREAD_STACK_SIZE] ALIGNED(16);
u8 thread3_stack[THREAD_STACK_SIZE] ALIGNED(16);
u8 thread4_stack[THREAD_STACK_SIZE] ALIGNED(16);
OSThread idle_thread;
OSThread main_thread;
OSThread thread3;
OSThread thread4;
OSMesgQueue queue;
OSMesg buf[1];
void thread3_func(UNUSED void *arg) {
OSMesg val;
printf("Thread3 recv\n");
fflush(stdout);
osRecvMesg(&queue, &val, OS_MESG_BLOCK);
printf("Thread3 complete: %d\n", (int)(intptr_t)val);
fflush(stdout);
}
void thread4_func(void *arg) {
printf("Thread4 send %d\n", (int)(intptr_t)arg);
fflush(stdout);
osSendMesg(&queue, arg, OS_MESG_BLOCK);
printf("Thread4 complete\n");
fflush(stdout);
}
void main_thread_func(UNUSED void* arg) {
osCreateMesgQueue(&queue, buf, sizeof(buf) / sizeof(buf[0]));
printf("main thread creating thread 3\n");
osCreateThread(&thread3, 3, thread3_func, NULL, &thread3_stack[THREAD_STACK_SIZE], 14);
printf("main thread starting thread 3\n");
osStartThread(&thread3);
printf("main thread creating thread 4\n");
osCreateThread(&thread4, 4, thread4_func, (void*)10, &thread4_stack[THREAD_STACK_SIZE], 13);
printf("main thread starting thread 4\n");
osStartThread(&thread4);
while (1) {
printf("main thread doin stuff\n");
sleep(1);
}
}
void idle_thread_func(UNUSED void* arg) {
printf("idle thread\n");
printf("creating main thread\n");
osCreateThread(&main_thread, 2, main_thread_func, NULL, &main_stack[THREAD_STACK_SIZE], 11);
printf("starting main thread\n");
osStartThread(&main_thread);
// Set this thread's priority to 0, making it the idle thread
osSetThreadPri(NULL, 0);
// idle
while (1) {
printf("idle thread doin stuff\n");
sleep(1);
}
}
void bootproc(void) {
osInitialize();
osCreateThread(&idle_thread, 1, idle_thread_func, NULL, &idle_stack[THREAD_STACK_SIZE], 127);
printf("Starting idle thread\n");
osStartThread(&idle_thread);
}
#endif

View file

@ -0,0 +1,188 @@
#include <thread>
#include <atomic>
#include "ultra64.h"
#include "multilibultra.hpp"
#include "recomp.h"
extern "C" void osCreateMesgQueue(RDRAM_ARG PTR(OSMesgQueue) mq_, PTR(OSMesg) msg, s32 count) {
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
mq->blocked_on_recv = NULLPTR;
mq->blocked_on_send = NULLPTR;
mq->msgCount = count;
mq->msg = msg;
mq->validCount = 0;
mq->first = 0;
}
s32 MQ_GET_COUNT(OSMesgQueue *mq) {
return mq->validCount;
}
s32 MQ_IS_EMPTY(OSMesgQueue *mq) {
return mq->validCount == 0;
}
s32 MQ_IS_FULL(OSMesgQueue* mq) {
return MQ_GET_COUNT(mq) >= mq->msgCount;
}
void thread_queue_insert(RDRAM_ARG PTR(OSThread)* queue, PTR(OSThread) toadd_) {
PTR(OSThread)* cur = queue;
OSThread* toadd = TO_PTR(OSThread, toadd_);
while (*cur && TO_PTR(OSThread, *cur)->priority > toadd->priority) {
cur = &TO_PTR(OSThread, *cur)->next;
}
toadd->next = (*cur);
*cur = toadd_;
}
OSThread* thread_queue_pop(RDRAM_ARG PTR(OSThread)* queue) {
PTR(OSThread) ret = *queue;
*queue = TO_PTR(OSThread, ret)->next;
return TO_PTR(OSThread, ret);
}
bool thread_queue_empty(RDRAM_ARG PTR(OSThread)* queue) {
return *queue == NULLPTR;
}
extern "C" s32 osSendMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags) {
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
Multilibultra::disable_preemption();
if (flags == OS_MESG_NOBLOCK) {
// If non-blocking, fail if the queue is full
if (MQ_IS_FULL(mq)) {
Multilibultra::enable_preemption();
return -1;
}
} else {
// Otherwise, yield this thread until the queue has room
while (MQ_IS_FULL(mq)) {
debug_printf("[Message Queue] Thread %d is blocked on send\n", TO_PTR(OSThread, Multilibultra::this_thread())->id);
thread_queue_insert(PASS_RDRAM &mq->blocked_on_send, Multilibultra::this_thread());
Multilibultra::enable_preemption();
Multilibultra::pause_self(PASS_RDRAM1);
Multilibultra::disable_preemption();
}
}
s32 last = (mq->first + mq->validCount) % mq->msgCount;
TO_PTR(OSMesg, mq->msg)[last] = msg;
mq->validCount++;
OSThread* to_run = nullptr;
if (!thread_queue_empty(PASS_RDRAM &mq->blocked_on_recv)) {
to_run = thread_queue_pop(PASS_RDRAM &mq->blocked_on_recv);
}
Multilibultra::enable_preemption();
if (to_run) {
debug_printf("[Message Queue] Thread %d is unblocked\n", to_run->id);
OSThread* self = TO_PTR(OSThread, Multilibultra::this_thread());
if (to_run->priority > self->priority) {
Multilibultra::swap_to_thread(PASS_RDRAM to_run);
} else {
Multilibultra::schedule_running_thread(to_run);
}
}
return 0;
}
extern "C" s32 osJamMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags) {
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
Multilibultra::disable_preemption();
if (flags == OS_MESG_NOBLOCK) {
// If non-blocking, fail if the queue is full
if (MQ_IS_FULL(mq)) {
Multilibultra::enable_preemption();
return -1;
}
} else {
// Otherwise, yield this thread in a loop until the queue is no longer full
while (MQ_IS_FULL(mq)) {
debug_printf("[Message Queue] Thread %d is blocked on jam\n", TO_PTR(OSThread, Multilibultra::this_thread())->id);
thread_queue_insert(PASS_RDRAM &mq->blocked_on_send, Multilibultra::this_thread());
Multilibultra::enable_preemption();
Multilibultra::pause_self(PASS_RDRAM1);
Multilibultra::disable_preemption();
}
}
mq->first = (mq->first + mq->msgCount - 1) % mq->msgCount;
TO_PTR(OSMesg, mq->msg)[mq->first] = msg;
mq->validCount++;
OSThread *to_run = nullptr;
if (!thread_queue_empty(PASS_RDRAM &mq->blocked_on_recv)) {
to_run = thread_queue_pop(PASS_RDRAM &mq->blocked_on_recv);
}
Multilibultra::enable_preemption();
if (to_run) {
debug_printf("[Message Queue] Thread %d is unblocked\n", to_run->id);
OSThread *self = TO_PTR(OSThread, Multilibultra::this_thread());
if (to_run->priority > self->priority) {
Multilibultra::swap_to_thread(PASS_RDRAM to_run);
} else {
Multilibultra::schedule_running_thread(to_run);
}
}
return 0;
}
extern "C" s32 osRecvMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, PTR(OSMesg) msg_, s32 flags) {
OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_);
OSMesg *msg = TO_PTR(OSMesg, msg_);
Multilibultra::disable_preemption();
if (flags == OS_MESG_NOBLOCK) {
// If non-blocking, fail if the queue is empty
if (MQ_IS_EMPTY(mq)) {
Multilibultra::enable_preemption();
return -1;
}
} else {
// Otherwise, yield this thread in a loop until the queue is no longer full
while (MQ_IS_EMPTY(mq)) {
debug_printf("[Message Queue] Thread %d is blocked on receive\n", TO_PTR(OSThread, Multilibultra::this_thread())->id);
thread_queue_insert(PASS_RDRAM &mq->blocked_on_recv, Multilibultra::this_thread());
Multilibultra::enable_preemption();
Multilibultra::pause_self(PASS_RDRAM1);
Multilibultra::disable_preemption();
}
}
if (msg != nullptr) {
*msg = TO_PTR(OSMesg, mq->msg)[mq->first];
}
mq->first = (mq->first + 1) % mq->msgCount;
mq->validCount--;
OSThread *to_run = nullptr;
if (!thread_queue_empty(PASS_RDRAM &mq->blocked_on_send)) {
to_run = thread_queue_pop(PASS_RDRAM &mq->blocked_on_send);
}
Multilibultra::enable_preemption();
if (to_run) {
debug_printf("[Message Queue] Thread %d is unblocked\n", to_run->id);
OSThread *self = TO_PTR(OSThread, Multilibultra::this_thread());
if (to_run->priority > self->priority) {
Multilibultra::swap_to_thread(PASS_RDRAM to_run);
} else {
Multilibultra::schedule_running_thread(to_run);
}
}
return 0;
}
extern "C" void osSetEventMesg(RDRAM_ARG OSEvent, PTR(OSMesgQueue), OSMesg) {
}

View file

@ -0,0 +1,53 @@
#ifndef __MULTILIBULTRA_HPP__
#define __MULTILIBULTRA_HPP__
#include <thread>
#include <atomic>
#include <mutex>
#include "ultra64.h"
#include "platform_specific.h"
struct UltraThreadContext {
std::thread host_thread;
std::atomic_bool running;
std::atomic_bool initialized;
};
namespace Multilibultra {
void native_init();
void init_scheduler();
void native_thread_init(OSThread *t);
void set_self_paused(RDRAM_ARG1);
void wait_for_resumed(RDRAM_ARG1);
void swap_to_thread(RDRAM_ARG OSThread *to);
void pause_thread_impl(OSThread *t);
void pause_thread_native_impl(OSThread *t);
void resume_thread_impl(OSThread *t);
void resume_thread_native_impl(OSThread *t);
void schedule_running_thread(OSThread *t);
void stop_thread(OSThread *t);
void pause_self(RDRAM_ARG1);
void cleanup_thread(OSThread *t);
PTR(OSThread) this_thread();
void disable_preemption();
void enable_preemption();
void notify_scheduler();
void reprioritize_thread(OSThread *t, OSPri pri);
void set_main_thread();
class preemption_guard {
public:
preemption_guard();
~preemption_guard();
private:
std::lock_guard<std::mutex> lock;
};
} // namespace Multilibultra
#define debug_printf(...) printf(__VA_ARGS__);
//#define debug_printf(...)
#endif

View file

@ -0,0 +1,32 @@
#ifndef __PLATFORM_SPECIFIC_H__
#define __PLATFORM_SPECIFIC_H__
#if defined(__linux__)
//#include <pthread.h>
//
//typedef struct {
// pthread_t t;
// pthread_barrier_t pause_barrier;
// pthread_mutex_t pause_mutex;
// pthread_cond_t pause_cond;
// void (*entrypoint)(void *);
// void *arg;
//} OSThreadNative;
#elif defined(_WIN32)
//#include <pthread.h>
//
//typedef struct {
// pthread_t t;
// pthread_barrier_t pause_barrier;
// pthread_mutex_t pause_mutex;
// pthread_cond_t pause_cond;
// void (*entrypoint)(void*);
// void* arg;
//} OSThreadNative;
#endif
#endif

View file

@ -0,0 +1,268 @@
#include <thread>
#include <queue>
#include <atomic>
#include <vector>
#include "multilibultra.hpp"
class OSThreadComparator {
public:
bool operator() (OSThread *a, OSThread *b) const {
return a->priority < b->priority;
}
};
class thread_queue_t : public std::priority_queue<OSThread*, std::vector<OSThread*>, OSThreadComparator> {
public:
// TODO comment this
bool remove(const OSThread* value) {
auto it = std::find(this->c.begin(), this->c.end(), value);
if (it == this->c.end()) {
return false;
}
if (it == this->c.begin()) {
// deque the top element
this->pop();
} else {
// remove element and re-heap
this->c.erase(it);
std::make_heap(this->c.begin(), this->c.end(), this->comp);
}
return true;
}
};
static struct {
std::vector<OSThread*> to_schedule;
std::vector<OSThread*> to_stop;
std::vector<OSThread*> to_cleanup;
std::vector<std::pair<OSThread*, OSPri>> to_reprioritize;
std::mutex mutex;
// OSThread* running_thread;
std::atomic_int notify_count;
std::atomic_int action_count;
bool can_preempt;
std::mutex premption_mutex;
} scheduler_context{};
void handle_thread_queueing(thread_queue_t& running_thread_queue) {
std::lock_guard lock{scheduler_context.mutex};
if (!scheduler_context.to_schedule.empty()) {
OSThread* to_schedule = scheduler_context.to_schedule.back();
scheduler_context.to_schedule.pop_back();
scheduler_context.action_count.fetch_sub(1);
debug_printf("[Scheduler] Scheduling thread %d\n", to_schedule->id);
running_thread_queue.push(to_schedule);
}
}
void handle_thread_stopping(thread_queue_t& running_thread_queue) {
std::lock_guard lock{scheduler_context.mutex};
while (!scheduler_context.to_stop.empty()) {
OSThread* to_stop = scheduler_context.to_stop.back();
scheduler_context.to_stop.pop_back();
scheduler_context.action_count.fetch_sub(1);
debug_printf("[Scheduler] Stopping thread %d\n", to_stop->id);
running_thread_queue.remove(to_stop);
}
}
void handle_thread_cleanup(thread_queue_t& running_thread_queue) {
std::lock_guard lock{scheduler_context.mutex};
while (!scheduler_context.to_cleanup.empty()) {
OSThread* to_cleanup = scheduler_context.to_cleanup.back();
scheduler_context.to_cleanup.pop_back();
scheduler_context.action_count.fetch_sub(1);
debug_printf("[Scheduler] Destroying thread %d\n", to_cleanup->id);
running_thread_queue.remove(to_cleanup);
to_cleanup->context->host_thread.join();
delete to_cleanup->context;
}
}
void handle_thread_reprioritization(thread_queue_t& running_thread_queue) {
std::lock_guard lock{scheduler_context.mutex};
while (!scheduler_context.to_reprioritize.empty()) {
const std::pair<OSThread*, OSPri> to_reprioritize = scheduler_context.to_reprioritize.back();
scheduler_context.to_reprioritize.pop_back();
scheduler_context.action_count.fetch_sub(1);
debug_printf("[Scheduler] Reprioritizing thread %d to %d\n", to_reprioritize.first->id, to_reprioritize.second);
running_thread_queue.remove(to_reprioritize.first);
to_reprioritize.first->priority = to_reprioritize.second;
running_thread_queue.push(to_reprioritize.first);
}
}
void handle_scheduler_notifications() {
std::lock_guard lock{scheduler_context.mutex};
int32_t notify_count = scheduler_context.notify_count.exchange(0);
if (notify_count) {
debug_printf("Received %d notifications\n", notify_count);
scheduler_context.action_count.fetch_sub(notify_count);
}
}
void swap_running_thread(thread_queue_t& running_thread_queue, OSThread*& cur_running_thread) {
OSThread* new_running_thread = running_thread_queue.top();
if (cur_running_thread != new_running_thread) {
if (cur_running_thread && cur_running_thread->state == OSThreadState::RUNNING) {
debug_printf("[Scheduler] Switching execution from thread %d (%d) to thread %d (%d)\n",
cur_running_thread->id, cur_running_thread->priority,
new_running_thread->id, new_running_thread->priority);
Multilibultra::pause_thread_impl(cur_running_thread);
} else {
debug_printf("[Scheduler] Switching execution to thread %d (%d)\n", new_running_thread->id, new_running_thread->priority);
}
Multilibultra::resume_thread_impl(new_running_thread);
cur_running_thread = new_running_thread;
} else if (cur_running_thread && cur_running_thread->state != OSThreadState::RUNNING) {
Multilibultra::resume_thread_impl(cur_running_thread);
}
}
void scheduler_func() {
thread_queue_t running_thread_queue{};
OSThread* cur_running_thread = nullptr;
while (true) {
OSThread* old_running_thread = cur_running_thread;
scheduler_context.action_count.wait(0);
std::lock_guard lock{scheduler_context.premption_mutex};
// Handle notifications
handle_scheduler_notifications();
// Handle stopping threads
handle_thread_stopping(running_thread_queue);
// Handle cleaning up threads
handle_thread_cleanup(running_thread_queue);
// Handle queueing threads to run
handle_thread_queueing(running_thread_queue);
// Handle threads that have changed priority
handle_thread_reprioritization(running_thread_queue);
// Determine which thread to run, stopping the current running thread if necessary
swap_running_thread(running_thread_queue, cur_running_thread);
std::this_thread::yield();
if (old_running_thread != cur_running_thread && old_running_thread && cur_running_thread) {
debug_printf("[Scheduler] Swapped from Thread %d (%d) to Thread %d (%d)\n",
old_running_thread->id, old_running_thread->priority, cur_running_thread->id, cur_running_thread->priority);
}
}
}
extern "C" void do_yield() {
std::this_thread::yield();
}
namespace Multilibultra {
void init_scheduler() {
scheduler_context.can_preempt = true;
std::thread scheduler_thread{scheduler_func};
scheduler_thread.detach();
}
void schedule_running_thread(OSThread *t) {
debug_printf("[Scheduler] Queuing Thread %d to be scheduled\n", t->id);
std::lock_guard lock{scheduler_context.mutex};
scheduler_context.to_schedule.push_back(t);
scheduler_context.action_count.fetch_add(1);
scheduler_context.action_count.notify_all();
}
void swap_to_thread(RDRAM_ARG OSThread *to) {
OSThread *self = TO_PTR(OSThread, Multilibultra::this_thread());
debug_printf("[Scheduler] Scheduling swap from thread %d to %d\n", self->id, to->id);
{
std::lock_guard lock{scheduler_context.mutex};
scheduler_context.to_schedule.push_back(to);
Multilibultra::set_self_paused(PASS_RDRAM1);
scheduler_context.action_count.fetch_add(1);
scheduler_context.action_count.notify_all();
}
Multilibultra::wait_for_resumed(PASS_RDRAM1);
}
void reprioritize_thread(OSThread *t, OSPri pri) {
debug_printf("[Scheduler] Adjusting Thread %d priority to %d\n", t->id, pri);
std::lock_guard lock{scheduler_context.mutex};
scheduler_context.to_reprioritize.emplace_back(t, pri);
scheduler_context.action_count.fetch_add(1);
scheduler_context.action_count.notify_all();
}
void pause_self(RDRAM_ARG1) {
OSThread *self = TO_PTR(OSThread, Multilibultra::this_thread());
debug_printf("[Scheduler] Thread %d pausing itself\n", self->id);
{
std::lock_guard lock{scheduler_context.mutex};
Multilibultra::set_self_paused(PASS_RDRAM1);
scheduler_context.to_stop.push_back(self);
scheduler_context.action_count.fetch_add(1);
scheduler_context.action_count.notify_all();
}
Multilibultra::wait_for_resumed(PASS_RDRAM1);
}
void stop_thread(OSThread *t) {
debug_printf("[Scheduler] Queuing Thread %d to be stopped\n", t->id);
{
std::lock_guard lock{scheduler_context.mutex};
scheduler_context.to_stop.push_back(t);
scheduler_context.action_count.fetch_add(1);
scheduler_context.action_count.notify_all();
}
Multilibultra::pause_thread_impl(t);
}
void cleanup_thread(OSThread *t) {
std::lock_guard lock{scheduler_context.mutex};
scheduler_context.to_cleanup.push_back(t);
scheduler_context.action_count.fetch_add(1);
scheduler_context.action_count.notify_all();
}
void disable_preemption() {
scheduler_context.premption_mutex.lock();
scheduler_context.can_preempt = false;
}
void enable_preemption() {
scheduler_context.can_preempt = true;
scheduler_context.premption_mutex.unlock();
}
// lock's constructor is called first, so can_preempt is set after locking
preemption_guard::preemption_guard() : lock{scheduler_context.premption_mutex} {
scheduler_context.can_preempt = false;
}
// lock's destructor is called last, so can_preempt is set before unlocking
preemption_guard::~preemption_guard() {
scheduler_context.can_preempt = true;
}
void notify_scheduler() {
std::lock_guard lock{scheduler_context.mutex};
scheduler_context.notify_count.fetch_add(1);
scheduler_context.action_count.fetch_add(1);
scheduler_context.action_count.notify_all();
}
}

View file

@ -0,0 +1,60 @@
#ifndef _WIN32
// #include <thread>
// #include <stdexcept>
// #include <atomic>
#include <pthread.h>
#include <signal.h>
#include <limits.h>
#include "ultra64.h"
#include "multilibultra.hpp"
constexpr int pause_thread_signum = SIGUSR1;
// void cleanup_current_thread(OSThread *t) {
// debug_printf("Thread cleanup %d\n", t->id);
// // delete t->context;
// }
void sig_handler(int signum, siginfo_t *info, void *context) {
if (signum == pause_thread_signum) {
OSThread *t = Multilibultra::this_thread();
debug_printf("[Sig] Thread %d paused\n", t->id);
// Wait until the thread is marked as running again
t->context->running.wait(false);
debug_printf("[Sig] Thread %d resumed\n", t->id);
}
}
void Multilibultra::native_init(void) {
// Set up a signal handler to capture pause signals
struct sigaction sigact;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = SA_SIGINFO | SA_RESTART;
sigact.sa_sigaction = sig_handler;
sigaction(pause_thread_signum, &sigact, nullptr);
}
void Multilibultra::native_thread_init(OSThread *t) {
debug_printf("[Native] Init thread %d\n", t->id);
}
void Multilibultra::pause_thread_native_impl(OSThread *t) {
debug_printf("[Native] Pause thread %d\n", t->id);
// Send a pause signal to the thread, which will trigger it to wait on its pause barrier in the signal handler
pthread_kill(t->context->host_thread.native_handle(), pause_thread_signum);
}
void Multilibultra::resume_thread_native_impl(UNUSED OSThread *t) {
debug_printf("[Native] Resume thread %d\n", t->id);
// Nothing to do here
}
#endif

View file

@ -0,0 +1,32 @@
#include <Windows.h>
#include "ultra64.h"
#include "multilibultra.hpp"
extern "C" unsigned int sleep(unsigned int seconds) {
Sleep(seconds * 1000);
return 0;
}
void Multilibultra::native_init(void) {
}
void Multilibultra::native_thread_init(OSThread *t) {
debug_printf("[Native] Init thread %d\n", t->id);
}
void Multilibultra::pause_thread_native_impl(OSThread *t) {
debug_printf("[Native] Pause thread %d\n", t->id);
// Pause the thread via the win32 API
SuspendThread(t->context->host_thread.native_handle());
// Perform a synchronous action to ensure that the thread is suspended
// see: https://devblogs.microsoft.com/oldnewthing/20150205-00/?p=44743
CONTEXT threadContext;
GetThreadContext(t->context->host_thread.native_handle(), &threadContext);
}
void Multilibultra::resume_thread_native_impl(UNUSED OSThread *t) {
debug_printf("[Native] Resume thread %d\n", t->id);
// Resume the thread
ResumeThread(t->context->host_thread.native_handle());
}

143
test/portultra/threads.cpp Normal file
View file

@ -0,0 +1,143 @@
#include <cstdio>
#include <thread>
#include "ultra64.h"
#include "multilibultra.hpp"
extern "C" void bootproc();
thread_local bool is_main_thread = false;
thread_local PTR(OSThread) thread_self = NULLPTR;
void Multilibultra::set_main_thread() {
is_main_thread = true;
}
#if 0
int main(int argc, char** argv) {
Multilibultra::set_main_thread();
bootproc();
}
#endif
#if 1
void run_thread_function(uint8_t* rdram, uint32_t addr, uint32_t sp, uint32_t arg);
#else
#define run_thread_function(func, sp, arg) func(arg)
#endif
static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entrypoint, PTR(void) arg) {
OSThread *self = TO_PTR(OSThread, self_);
debug_printf("[Thread] Thread created: %d\n", self->id);
thread_self = self_;
// Perform any necessary native thread initialization.
Multilibultra::native_thread_init(self);
// Set initialized to false to indicate that this thread can be started.
self->context->initialized.store(true);
self->context->initialized.notify_all();
debug_printf("[Thread] Thread waiting to be started: %d\n", self->id);
// Wait until the thread is marked as running.
Multilibultra::set_self_paused(PASS_RDRAM1);
Multilibultra::wait_for_resumed(PASS_RDRAM1);
debug_printf("[Thread] Thread started: %d\n", self->id);
// Run the thread's function with the provided argument.
run_thread_function(PASS_RDRAM entrypoint, self->sp, arg);
// Dispose of this thread after it completes.
Multilibultra::cleanup_thread(self);
}
extern "C" void osStartThread(RDRAM_ARG PTR(OSThread) t) {
debug_printf("[os] Start Thread %d\n", TO_PTR(OSThread, t)->id);
// Wait until the thread is initialized to indicate that it's ready to be started.
TO_PTR(OSThread, t)->context->initialized.wait(false);
debug_printf("[os] Thread %d is ready to be started\n", TO_PTR(OSThread, t)->id);
if (thread_self && (TO_PTR(OSThread, t)->priority > TO_PTR(OSThread, thread_self)->priority)) {
Multilibultra::swap_to_thread(PASS_RDRAM TO_PTR(OSThread, t));
} else {
Multilibultra::schedule_running_thread(TO_PTR(OSThread, t));
}
// The main thread "becomes" the first thread started, so join on it and exit after it completes.
if (is_main_thread) {
TO_PTR(OSThread, t)->context->host_thread.join();
std::exit(EXIT_SUCCESS);
}
}
extern "C" void osCreateThread(RDRAM_ARG PTR(OSThread) t_, OSId id, PTR(thread_func_t) entrypoint, PTR(void) arg, PTR(void) sp, OSPri pri) {
debug_printf("[os] Create Thread %d\n", id);
OSThread *t = TO_PTR(OSThread, t_);
t->next = NULLPTR;
t->priority = pri;
t->id = id;
t->state = OSThreadState::PAUSED;
t->sp = sp;
// Spawn a new thread, which will immediately pause itself and wait until it's been started.
t->context = new UltraThreadContext{};
t->context->initialized.store(false);
t->context->running.store(false);
t->context->host_thread = std::thread{_thread_func, PASS_RDRAM t_, entrypoint, arg};
}
extern "C" void osSetThreadPri(RDRAM_ARG PTR(OSThread) t, OSPri pri) {
if (t == NULLPTR) {
t = thread_self;
}
bool pause_self = false;
if (pri > TO_PTR(OSThread, thread_self)->priority) {
pause_self = true;
Multilibultra::set_self_paused(PASS_RDRAM1);
} else if (t == thread_self && pri < TO_PTR(OSThread, thread_self)->priority) {
pause_self = true;
Multilibultra::set_self_paused(PASS_RDRAM1);
}
Multilibultra::reprioritize_thread(TO_PTR(OSThread, t), pri);
if (pause_self) {
Multilibultra::wait_for_resumed(PASS_RDRAM1);
}
}
// TODO yield thread, need a stable priority queue in the scheduler
void Multilibultra::set_self_paused(RDRAM_ARG1) {
debug_printf("[Thread] Thread pausing itself: %d\n", TO_PTR(OSThread, thread_self)->id);
TO_PTR(OSThread, thread_self)->state = OSThreadState::PAUSED;
TO_PTR(OSThread, thread_self)->context->running.store(false);
TO_PTR(OSThread, thread_self)->context->running.notify_all();
}
void Multilibultra::wait_for_resumed(RDRAM_ARG1) {
TO_PTR(OSThread, thread_self)->context->running.wait(false);
}
void Multilibultra::pause_thread_impl(OSThread* t) {
t->state = OSThreadState::PREEMPTED;
t->context->running.store(false);
t->context->running.notify_all();
Multilibultra::pause_thread_native_impl(t);
}
void Multilibultra::resume_thread_impl(OSThread *t) {
t->state = OSThreadState::RUNNING;
t->context->running.store(true);
t->context->running.notify_all();
Multilibultra::resume_thread_native_impl(t);
}
PTR(OSThread) Multilibultra::this_thread() {
return thread_self;
}

133
test/portultra/ultra64.h Normal file
View file

@ -0,0 +1,133 @@
#ifndef __ULTRA64_MULTILIBULTRA_H__
#define __ULTRA64_MULTILIBULTRA_H__
#include <stdint.h>
#include "platform_specific.h"
#ifdef __cplusplus
#include <queue>
#endif
#ifdef __GNUC__
#define UNUSED __attribute__((unused))
#define ALIGNED(x) __attribute__((aligned(x)))
#else
#define UNUSED
#define ALIGNED(x)
#endif
typedef int64_t s64;
typedef uint64_t u64;
typedef int32_t s32;
typedef uint32_t u32;
typedef int16_t s16;
typedef uint16_t u16;
typedef int8_t s8;
typedef uint8_t u8;
#define PTR(x) uint32_t
#define RDRAM_ARG uint8_t *rdram,
#define RDRAM_ARG1 uint8_t *rdram
#define PASS_RDRAM rdram,
#define PASS_RDRAM1 rdram
#define TO_PTR(type, var) ((type*)(&rdram[var & 0x3FFFFFF]))
#ifdef __cplusplus
#define NULLPTR (PTR(void))0
#endif
#ifndef NULL
#define NULL (PTR(void) 0)
#endif
#define OS_MESG_NOBLOCK 0
#define OS_MESG_BLOCK 1
typedef s32 OSPri;
typedef s32 OSId;
/////////////
// Structs //
/////////////
typedef struct UltraThreadContext UltraThreadContext;
typedef enum {
RUNNING,
PAUSED,
PREEMPTED
} OSThreadState;
typedef struct OSThread_t {
PTR(struct OSThread_t) next; // Next thread in the given queue
OSPri priority;
uint32_t pad1;
uint32_t pad2;
uint16_t flags; // These two are swapped to reflect rdram byteswapping
uint16_t state;
OSId id;
int32_t pad3;
UltraThreadContext* context; // An actual pointer regardless of platform
uint32_t sp;
} OSThread;
typedef u32 OSEvent;
typedef PTR(void) OSMesg;
// This union holds C++ members along with a padding array. Those members are guarded by an ifdef for C++
// so that they don't cause compilation errors in C. The padding array reserves the necessary space to
// hold the atomic members in C and a static assert is used to ensure that the union is large enough.
// typedef union UltraQueueContext {
// u64 pad[1];
// #ifdef __cplusplus
// struct {
// } atomics;
// // Construct pad instead of the atomics, which get constructed in-place in osCreateMesgQueue
// UltraQueueContext() : pad{} {}
// #endif
// } UltraQueueContext;
// #ifdef __cplusplus
// static_assert(sizeof(UltraQueueContext::pad) == sizeof(UltraQueueContext),
// "UltraQueueContext does not have enough padding to hold C++ members!");
// #endif
typedef struct OSMesgQueue {
PTR(OSThread) blocked_on_recv; /* Linked list of threads blocked on receiving from this queue */
PTR(OSThread) blocked_on_send; /* Linked list of threads blocked on sending to this queue */
s32 validCount; /* Number of messages in the queue */
s32 first; /* Index of the first message in the ring buffer */
s32 msgCount; /* Size of message buffer */
PTR(OSMesg) msg; /* Pointer to circular buffer to store messages */
} OSMesgQueue;
///////////////
// Functions //
///////////////
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void osInitialize(void);
typedef void (thread_func_t)(PTR(void));
void osCreateThread(RDRAM_ARG PTR(OSThread) t, OSId id, PTR(thread_func_t) entry, PTR(void) arg, PTR(void) sp, OSPri p);
void osStartThread(RDRAM_ARG PTR(OSThread) t);
void osSetThreadPri(RDRAM_ARG PTR(OSThread) t, OSPri pri);
s32 MQ_GET_COUNT(RDRAM_ARG PTR(OSMesgQueue));
s32 MQ_IS_EMPTY(RDRAM_ARG PTR(OSMesgQueue));
s32 MQ_IS_FULL(RDRAM_ARG PTR(OSMesgQueue));
void osCreateMesgQueue(RDRAM_ARG PTR(OSMesgQueue), PTR(OSMesg), s32);
s32 osSendMesg(RDRAM_ARG PTR(OSMesgQueue), OSMesg, s32);
s32 osJamMesg(RDRAM_ARG PTR(OSMesgQueue), OSMesg, s32);
s32 osRecvMesg(RDRAM_ARG PTR(OSMesgQueue), PTR(OSMesg), s32);
void osSetEventMesg(RDRAM_ARG OSEvent, PTR(OSMesgQueue), OSMesg);
#ifdef __cplusplus
} // extern "C"
#endif
#endif

17
test/src/ai.cpp Normal file
View file

@ -0,0 +1,17 @@
#include "recomp.h"
extern "C" void osAiSetFrequency_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osAiSetNextBuffer_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osAiGetLength_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osAiGetStatus_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}

25
test/src/cont.cpp Normal file
View file

@ -0,0 +1,25 @@
#include "recomp.h"
extern "C" void osContInit_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osContStartReadData_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osContGetReadData_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osMotorInit_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osMotorStart_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osMotorStop_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}

5
test/src/dp.cpp Normal file
View file

@ -0,0 +1,5 @@
#include "recomp.h"
extern "C" void osDpSetNextBuffer_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}

21
test/src/eep.cpp Normal file
View file

@ -0,0 +1,21 @@
#include "recomp.h"
extern "C" void osEepromProbe_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osEepromWrite_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osEepromLongWrite_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osEepromRead_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osEepromLongRead_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}

65
test/src/misc_ultra.cpp Normal file
View file

@ -0,0 +1,65 @@
#ifdef _WIN32
#include <Windows.h>
#endif
#include <cstdio>
#include "recomp.h"
extern uint64_t start_time;
extern "C" void osVirtualToPhysical_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
uint32_t virtual_addr = ctx->r4;
// TODO handle TLB mappings
ctx->r2 = virtual_addr - 0x80000000;
}
extern "C" void osInvalDCache_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osInvalICache_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osWritebackDCache_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osWritebackDCacheAll_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
// Ticks per second
constexpr uint32_t counter_rate = 46'875'000;
extern "C" void osGetCount_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
// TODO move this to a more appropriate place
int32_t count = 0;
#ifdef _WIN32
SYSTEMTIME st;
FILETIME ft;
GetSystemTime(&st);
SystemTimeToFileTime(&st, &ft);
uint64_t cur_time = ((uint64_t)ft.dwHighDateTime << 32) + ft.dwLowDateTime;
uint64_t delta_100ns = cur_time - start_time;
count = (delta_100ns * counter_rate) / (1'000'000'000 / 100);
#endif
ctx->r2 = count;
;
}
extern "C" void osSetIntMask_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void __osDisableInt_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void __osRestoreInt_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}

48
test/src/pi.cpp Normal file
View file

@ -0,0 +1,48 @@
#include <memory>
#include "recomp.h"
#include "../portultra/ultra64.h"
extern std::unique_ptr<uint8_t[]> rom;
extern size_t rom_size;
extern "C" void osCartRomInit_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osCreatePiManager_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
constexpr uint32_t rom_base = 0xB0000000;
extern "C" void osPiStartDma_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
uint32_t mb = ctx->r4;
uint32_t pri = ctx->r5;
uint32_t direction = ctx->r6;
uint32_t devAddr = ctx->r7;
uint32_t dramAddr = MEM_W(0x10, ctx->r29);
uint32_t size = MEM_W(0x14, ctx->r29);
uint32_t mq_ = MEM_W(0x18, ctx->r29);
OSMesgQueue* mq = TO_PTR(OSMesgQueue, mq_);
printf("[pi] DMA from 0x%08X into 0x%08X of size 0x%08X\n", devAddr, dramAddr, size);
// TODO asynchronous transfer (will require preemption in the scheduler)
// TODO this won't handle unaligned DMA
memcpy(rdram + (dramAddr & 0x3FFFFFF), rom.get() + (devAddr | rom_base) - rom_base, size);
// Send a message to the mq to indicate that the transfer completed
osSendMesg(rdram, mq_, 0, OS_MESG_NOBLOCK);
}
extern "C" void osEPiStartDma_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osPiGetStatus_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
ctx->r2 = 0;
}
extern "C" void osPiRawStartDma_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
;
}

View file

@ -0,0 +1,41 @@
#include "../portultra/ultra64.h"
#include "recomp.h"
extern "C" void osInitialize_recomp(uint8_t * restrict rdram, recomp_context * restrict ctx) {
osInitialize();
}
extern "C" void osCreateThread_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
//printf("Creating thread 0x%08X\n", (uint32_t)ctx->r4);
osCreateThread(rdram, (uint32_t)ctx->r4, (OSId)ctx->r5, (uint32_t)ctx->r6, (uint32_t)ctx->r7,
(uint32_t)MEM_W(0x10, ctx->r29), (OSPri)MEM_W(0x14, ctx->r29));
}
extern "C" void osStartThread_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
//printf("Starting thread 0x%08X\n", (uint32_t)ctx->r4);
osStartThread(rdram, (uint32_t)ctx->r4);
}
extern "C" void osSetThreadPri_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
osSetThreadPri(rdram, (uint32_t)ctx->r4, (OSPri)ctx->r5);
}
extern "C" void osCreateMesgQueue_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
osCreateMesgQueue(rdram, (uint32_t)ctx->r4, (uint32_t)ctx->r5, (s32)ctx->r6);
}
extern "C" void osRecvMesg_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
ctx->r2 = osRecvMesg(rdram, (uint32_t)ctx->r4, (uint32_t)ctx->r5, (s32)ctx->r6);
}
extern "C" void osSendMesg_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
ctx->r2 = osSendMesg(rdram, (uint32_t)ctx->r4, (OSMesg)ctx->r5, (s32)ctx->r6);
}
extern "C" void osJamMesg_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
ctx->r2 = osJamMesg(rdram, (uint32_t)ctx->r4, (OSMesg)ctx->r5, (s32)ctx->r6);
}
extern "C" void osSetEventMesg_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
osSetEventMesg(rdram, (OSEvent)ctx->r4, (uint32_t)ctx->r5, (OSMesg)ctx->r6);
}

169
test/src/recomp.cpp Normal file
View file

@ -0,0 +1,169 @@
#ifdef _WIN32
#include <Windows.h>
#endif
#include <cstdio>
#include <cstdlib>
#include <memory>
#include <cmath>
#include <unordered_map>
#include <fstream>
#include "recomp.h"
#include "../portultra/multilibultra.hpp"
#ifdef _MSC_VER
inline uint32_t byteswap(uint32_t val) {
return _byteswap_ulong(val);
}
#else
constexpr uint32_t byteswap(uint32_t val) {
return __builtin_bswap32(val);
}
#endif
void test_func(uint8_t* restrict rdram, recomp_context* restrict ctx) {
printf("in test_func\n");
exit(EXIT_FAILURE);
}
extern std::pair<uint32_t, recomp_func_t*> funcs[];
extern const size_t num_funcs;
std::unordered_map<uint32_t, recomp_func_t*> func_map{};
extern "C" recomp_func_t* get_function(uint32_t addr) {
auto func_find = func_map.find(addr);
if (func_find == func_map.end()) {
fprintf(stderr, "Failed to find function at 0x%08X\n", addr);
std::exit(EXIT_FAILURE);
}
return func_find->second;
}
extern "C" void bzero(uint8_t* restrict rdram, recomp_context* restrict ctx) {
uint32_t start_addr = ctx->r4;
uint32_t size = ctx->r5;
for (uint32_t i = 0; i < size; i++) {
MEM_B(start_addr, i) = 0;
}
}
extern "C" void switch_error(const char* func, uint32_t vram, uint32_t jtbl) {
printf("Switch-case out of bounds in %s at 0x%08X for jump table at 0x%08X\n", func, vram, jtbl);
exit(EXIT_FAILURE);
}
extern "C" void do_break(uint32_t vram) {
printf("Encountered break at original vram 0x%08X\n", vram);
exit(EXIT_FAILURE);
}
void run_thread_function(uint8_t* rdram, uint32_t addr, uint32_t sp, uint32_t arg) {
recomp_context ctx{};
ctx.r29 = sp;
recomp_func_t* func = get_function(addr);
func(rdram, &ctx);
}
extern "C" void game_init(uint8_t* restrict rdram, recomp_context* restrict ctx);
std::unique_ptr<uint8_t[]> rom;
size_t rom_size;
uint64_t start_time;
int main(int argc, char **argv) {
if (argc != 2) {
printf("Usage: %s [baserom]\n", argv[0]);
exit(EXIT_SUCCESS);
}
{
std::basic_ifstream<uint8_t> rom_file{ argv[1], std::ios::binary };
size_t iobuf_size = 0x100000;
std::unique_ptr<uint8_t[]> iobuf = std::make_unique<uint8_t[]>(iobuf_size);
rom_file.rdbuf()->pubsetbuf(iobuf.get(), iobuf_size);
if (!rom_file) {
fprintf(stderr, "Failed to open rom: %s\n", argv[1]);
exit(EXIT_FAILURE);
}
rom_file.seekg(0, std::ios::end);
rom_size = rom_file.tellg();
rom_file.seekg(0, std::ios::beg);
rom = std::make_unique<uint8_t[]>(rom_size);
rom_file.read(rom.get(), rom_size);
}
// Byteswap the rom
for (size_t rom_addr = 0; rom_addr < rom_size; rom_addr += 4) {
uint32_t word = *reinterpret_cast<uint32_t*>(rom.get() + rom_addr);
word = byteswap(word);
*reinterpret_cast<uint32_t*>(rom.get() + rom_addr) = word;
}
// Get entrypoint from ROM
// TODO fix this for other IPL3 versions
uint32_t entrypoint = *reinterpret_cast<uint32_t*>(rom.get() + 0x8);
// Allocate rdram_buffer
std::unique_ptr<uint8_t[]> rdram_buffer = std::make_unique<uint8_t[]>(8 * 1024 * 1024);
std::memset(rdram_buffer.get(), 0, 8 * 1024 * 1024);
recomp_context context{};
// Initial 1MB DMA
std::copy_n(rom.get() + 0x1000, 0x100000, rdram_buffer.get() + entrypoint - 0x80000000);
// Initialize function address map
for (size_t i = 0; i < num_funcs; i++) {
func_map[funcs[i].first] = funcs[i].second;
}
// TODO move this to a more appropriate place
#ifdef _WIN32
{
SYSTEMTIME st;
FILETIME ft;
GetSystemTime(&st);
SystemTimeToFileTime(&st, &ft);
start_time = ((uint64_t)ft.dwHighDateTime << 32) + ft.dwLowDateTime;
}
#endif
// Set up stack pointer
context.r29 = 0x803FFFF0u;
// Initialize variables normally set by IPL3
constexpr uint32_t osTvType = 0x80000300;
constexpr uint32_t osRomType = 0x80000304;
constexpr uint32_t osRomBase = 0x80000308;
constexpr uint32_t osResetType = 0x8000030c;
constexpr uint32_t osCicId = 0x80000310;
constexpr uint32_t osVersion = 0x80000314;
constexpr uint32_t osMemSize = 0x80000318;
constexpr uint32_t osAppNMIBuffer = 0x8000031c;
uint8_t *rdram = rdram_buffer.get();
MEM_W(osTvType, 0) = 1; // NTSC
MEM_W(osRomBase, 0) = 0xB0000000u; // standard rom base
MEM_W(osResetType, 0) = 0; // cold reset
MEM_W(osMemSize, 0) = 8 * 1024 * 1024; // 8MB
// Clear bss
// TODO run the entrypoint instead
memset(rdram_buffer.get() + 0XAF860, 0, 0xC00A0u - 0XAF860);
printf("[Recomp] Starting\n");
Multilibultra::set_main_thread();
game_init(rdram_buffer.get(), &context);
printf("[Recomp] Quitting\n");
return EXIT_SUCCESS;
}

22
test/src/sp.cpp Normal file
View file

@ -0,0 +1,22 @@
#include <cstdio>
#include "recomp.h"
extern "C" void osSpTaskLoad_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osSpTaskStartGo_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
printf("[sp] osSpTaskStartGo(0x%08X)\n", (uint32_t)ctx->r4);
}
extern "C" void osSpTaskYield_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osSpTaskYielded_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void __osSpSetPc_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}

33
test/src/vi.cpp Normal file
View file

@ -0,0 +1,33 @@
#include "recomp.h"
extern "C" void osCreateViManager_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osViBlack_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osViSetSpecialFeatures_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osViGetCurrentFramebuffer_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osViGetNextFramebuffer_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osViSwapBuffer_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osViSetMode_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}
extern "C" void osViSetEvent_recomp(uint8_t* restrict rdram, recomp_context* restrict ctx) {
;
}