mirror of
https://github.com/open-goal/jak-project
synced 2026-05-23 06:54:31 -04:00
4f537d4a71
This sets up the C Kernel for Jak 3, and makes it possible to build and load code built with `goalc --jak3`. There's not too much interesting here, other than they switched to a system where symbol IDs (unique numbers less than 2^14) are generated at compile time, and those get included in the object file itself. This is kind of annoying, since it means all tools that produce a GOAL object file need to work together to assign unique symbol IDs. And since the symbol IDs can't conflict, and are only a number between 0 and 2^14, you can't just hash and hope for no collisions. We work around this by ignoring the IDs and re-assigning our own. I think this is very similar to what the C Kernel did on early builds of Jak 3 which supported loading old format level files, which didn't have the IDs included. As far as I can tell, this shouldn't cause any problems. It defeats all of their fancy tricks to save memory by not storing the symbol string, but we don't care.
163 lines
4.7 KiB
C++
163 lines
4.7 KiB
C++
#include "kboot.h"
|
|
|
|
#include <cstring>
|
|
|
|
#include "common/repl/util.h"
|
|
#include "common/util/Timer.h"
|
|
|
|
#include "game/common/game_common_types.h"
|
|
#include "game/kernel/common/Ptr.h"
|
|
#include "game/kernel/common/Symbol4.h"
|
|
#include "game/kernel/common/kboot.h"
|
|
#include "game/kernel/common/klisten.h"
|
|
#include "game/kernel/common/kprint.h"
|
|
#include "game/kernel/common/kscheme.h"
|
|
#include "game/kernel/common/ksocket.h"
|
|
#include "game/kernel/jak3/klisten.h"
|
|
#include "game/kernel/jak3/kmachine.h"
|
|
#include "game/sce/libscf.h"
|
|
|
|
// KernelDispatch__3ndiPFv_x
|
|
// KernelCheckAndDispatch__3ndiv
|
|
// KernelShutdown__3ndii
|
|
// main
|
|
|
|
namespace jak3 {
|
|
void KernelCheckAndDispatch();
|
|
|
|
char DebugBootUser[64];
|
|
char DebugBootArtGroup[64];
|
|
|
|
void kboot_init_globals() {
|
|
memset(DebugBootUser, 0, sizeof(DebugBootUser));
|
|
memset(DebugBootArtGroup, 0, sizeof(DebugBootArtGroup));
|
|
// strcpy(DebugBootUser, "unknown");
|
|
// CHANGED : let's just try to find the username automatically by default!
|
|
// the default is still "unknown"
|
|
auto username = REPL::find_repl_username();
|
|
strcpy(DebugBootUser, username.c_str());
|
|
}
|
|
|
|
s32 goal_main(int argc, const char* const* argv) {
|
|
// only in PC port
|
|
InitParms(argc, argv);
|
|
|
|
masterConfig.aspect = ee::sceScfGetAspect();
|
|
auto sony_language = ee::sceScfGetLanguage();
|
|
masterConfig.inactive_timeout = 0;
|
|
masterConfig.volume = 100;
|
|
masterConfig.timeout = 0;
|
|
switch (sony_language) {
|
|
case SCE_JAPANESE_LANGUAGE:
|
|
masterConfig.language = 6; // ???
|
|
break;
|
|
case SCE_FRENCH_LANGUAGE: // 2 -> 1
|
|
masterConfig.language = (u16)Language::French;
|
|
break;
|
|
case SCE_SPANISH_LANGUAGE: // 3 -> 3
|
|
masterConfig.language = (u16)Language::Spanish;
|
|
break;
|
|
case SCE_GERMAN_LANGUAGE: // 4 -> 2
|
|
masterConfig.language = (u16)Language::German;
|
|
break;
|
|
case SCE_ITALIAN_LANGUAGE: // 5 -> 4
|
|
masterConfig.language = (u16)Language::Italian;
|
|
break;
|
|
case SCE_PORTUGUESE_LANGUAGE:
|
|
masterConfig.language = (u16)Language::Portuguese;
|
|
break;
|
|
default:
|
|
masterConfig.language = (u16)Language::English;
|
|
break;
|
|
}
|
|
// Set up aspect ratio override in demo
|
|
if (!strcmp(DebugBootMessage, "demo") || !strcmp(DebugBootMessage, "demo-shared")) {
|
|
masterConfig.aspect = SCE_ASPECT_FULL;
|
|
}
|
|
// removed in PC port
|
|
// DiskBoot = 1;
|
|
// MasterDebug = 0;
|
|
// DebugSegment = 0;
|
|
|
|
// Launch GOAL!
|
|
if (InitMachine() >= 0) { // init kernel
|
|
KernelCheckAndDispatch(); // run kernel
|
|
ShutdownMachine(); // kernel died, we should too.
|
|
// movie playback stuff removed.
|
|
} else {
|
|
fprintf(stderr, "InitMachine failed\n");
|
|
exit(1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void KernelDispatch(u32 dispatcher_func) {
|
|
// place our stack at the end of EE memory
|
|
u64 goal_stack = u64(g_ee_main_mem) + EE_MAIN_MEM_SIZE - 8;
|
|
|
|
// try to get a message from the listener, and process it if needed
|
|
Ptr<char> new_message = WaitForMessageAndAck();
|
|
if (new_message.offset) {
|
|
ProcessListenerMessage(new_message);
|
|
}
|
|
|
|
// remember the old listener
|
|
auto old_listener_function = ListenerFunction->value();
|
|
|
|
// run the kernel!
|
|
Timer dispatch_timer;
|
|
if (MasterUseKernel) {
|
|
call_goal_on_stack(Ptr<Function>(dispatcher_func), goal_stack, s7.offset, g_ee_main_mem);
|
|
} else {
|
|
// added, just calls the listener function
|
|
if (ListenerFunction->value() != s7.offset) {
|
|
auto result = call_goal_on_stack(Ptr<Function>(ListenerFunction->value()), goal_stack,
|
|
s7.offset, g_ee_main_mem);
|
|
#ifdef __linux__
|
|
cprintf("%ld\n", result);
|
|
#else
|
|
cprintf("%lld\n", result);
|
|
#endif
|
|
ListenerFunction->value() = s7.offset;
|
|
}
|
|
}
|
|
|
|
float time_ms = dispatch_timer.getMs();
|
|
if (time_ms > 50) {
|
|
printf("Kernel dispatch time: %.3f ms\n", time_ms);
|
|
}
|
|
|
|
// flush stdout
|
|
ClearPending();
|
|
|
|
// now run the extra "kernel function"
|
|
auto bonus_function = KernelFunction->value();
|
|
if (bonus_function != s7.offset) {
|
|
// clear the pending kernel function
|
|
KernelFunction->value() = s7.offset;
|
|
// and run
|
|
call_goal_on_stack(Ptr<Function>(bonus_function), goal_stack, s7.offset, g_ee_main_mem);
|
|
}
|
|
|
|
// send ack to indicate that the listener function has been processed and the result printed
|
|
if (MasterDebug && ListenerFunction->value() != old_listener_function) {
|
|
SendAck();
|
|
}
|
|
|
|
// prevent crazy spinning if we're not vsyncing (added)
|
|
if (time_ms < 4) {
|
|
std::this_thread::sleep_for(std::chrono::microseconds(1000));
|
|
}
|
|
}
|
|
|
|
void KernelShutdown(u32 reason) {
|
|
MasterExit = (RuntimeExitStatus)reason;
|
|
}
|
|
|
|
void KernelCheckAndDispatch() {
|
|
while (MasterExit == RuntimeExitStatus::RUNNING) {
|
|
KernelDispatch(kernel_dispatcher->value());
|
|
}
|
|
}
|
|
|
|
} // namespace jak3
|