Files
jak-project/game/kernel/jak3/kboot.cpp
T
water111 4f537d4a71 [jak3] Set up ckernel (#3308)
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.
2024-01-16 19:24:02 -05:00

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