diff --git a/common/listener_common.h b/common/listener_common.h index 979a34b491..c573388e33 100644 --- a/common/listener_common.h +++ b/common/listener_common.h @@ -41,7 +41,9 @@ enum ListenerToTargetMsgKind : u16 { LTT_MSG_PRINT = 6, //! Print an object LTT_MSG_PRINT_SYMBOLS = 7, //! Print all symbols LTT_MSG_RESET = 8, //! Reset the game - LTT_MSG_CODE = 9 //! Send code to patch into the game + LTT_MSG_CODE = 9, //! Send code to patch into the game + // below here are added + LTT_MSG_SHUTDOWN = 10 //! Shut down the runtime. }; /*! diff --git a/game/kernel/Ptr.h b/game/kernel/Ptr.h index 225a23f579..50d327aa89 100644 --- a/game/kernel/Ptr.h +++ b/game/kernel/Ptr.h @@ -8,7 +8,7 @@ #ifndef JAK_PTR_H #define JAK_PTR_H -#include +#include #include "game/runtime.h" #include "common/common_types.h" @@ -36,25 +36,19 @@ struct Ptr { explicit Ptr(u32 v) { offset = v; } /*! - * Dereference a pointer. Will throw if you do this on a null pointer. + * Dereference a pointer. Will error if you do this on a null pointer. */ T* operator->() { - if (offset) { - return (T*)(g_ee_main_mem + offset); - } else { - throw std::runtime_error("Ptr null dereference!"); - } + assert(offset); + return (T*)(g_ee_main_mem + offset); } /*! - * Dereference a pointer. Will throw if you do this on a null pointer. + * Dereference a pointer. Will error if you do this on a null pointer. */ T& operator*() { - if (offset) { - return *(T*)(g_ee_main_mem + offset); - } else { - throw std::runtime_error("Ptr null dereference!"); - } + assert(offset); + return *(T*)(g_ee_main_mem + offset); } // pointer math diff --git a/game/kernel/kboot.cpp b/game/kernel/kboot.cpp index 3eb61fa121..59a6a4231c 100644 --- a/game/kernel/kboot.cpp +++ b/game/kernel/kboot.cpp @@ -8,7 +8,6 @@ #include #include -#include "common/cross_os_debug/xdbg.h" #include "common/common_types.h" #include "game/sce/libscf.h" #include "kboot.h" @@ -77,9 +76,6 @@ void kboot_init_globals() { * Add call to sceDeci2Reset when GOAL shuts down. */ s32 goal_main(int argc, const char* const* argv) { - // Added for OpenGOAL's debugger - xdbg::allow_debugging(); - // Initialize global variables based on command line parameters // This call is not present in the retail version of the game // but the function is, and it likely goes here. diff --git a/game/kernel/kdsnetm.cpp b/game/kernel/kdsnetm.cpp index a45e965c95..9abcf5c1fe 100644 --- a/game/kernel/kdsnetm.cpp +++ b/game/kernel/kdsnetm.cpp @@ -161,7 +161,7 @@ void GoalProtoHandler(int event, int param, void* opt) { * DONE, original version used an uncached address and had a FlushCache call, which were both * removed */ -s32 SendFromBufferD(s32 msg_kind, u64 p2, char* data, s32 size) { +s32 SendFromBufferD(s32 msg_kind, u64 msg_id, char* data, s32 size) { // wait for send to finish or error first... while (protoBlock.send_status > 0) { // on actual PS2, the kernel will run this in another thread. @@ -190,7 +190,7 @@ s32 SendFromBufferD(s32 msg_kind, u64 p2, char* data, s32 size) { header->msg_kind = (ListenerMessageKind)msg_kind; header->u6 = 0; header->msg_size = size; - header->msg_id = p2; + header->msg_id = msg_id; // start send! auto rv = sceDeci2ReqSend(protoBlock.socket, header->deci2_header.dst); diff --git a/game/kernel/kdsnetm.h b/game/kernel/kdsnetm.h index 1ebc21d05e..3e4f46bdea 100644 --- a/game/kernel/kdsnetm.h +++ b/game/kernel/kdsnetm.h @@ -68,7 +68,7 @@ void GoalProtoHandler(int event, int param, void* data); * DONE, original version used an uncached address and had a FlushCache call, which were both * removed */ -s32 SendFromBufferD(s32 p1, u64 p2, char* data, s32 size); +s32 SendFromBufferD(s32 p1, u64 msg_id, char* data, s32 size); /*! * Print GOAL Protocol status diff --git a/game/kernel/klink.cpp b/game/kernel/klink.cpp index 0682d4d441..b7e0260b48 100644 --- a/game/kernel/klink.cpp +++ b/game/kernel/klink.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include "klink.h" #include "fileio.h" @@ -81,7 +82,7 @@ void link_control::begin(Ptr object_file, "VERSION ERROR: C Kernel built from GOAL %d.%d, but object file %s is from GOAL %d.%d\n", versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR, name, ofh->goal_version_major, ofh->goal_version_minor); - exit(0); + assert(false); } if (link_debug_printfs) { printf("Object file header:\n"); @@ -352,11 +353,20 @@ uint32_t cross_seg_dist_link_v3(Ptr link, // target seg, dist into mine, dist into target, patch loc in mine uint8_t target_seg = *link; assert(target_seg < ofh->segment_count); + uint32_t* link_data = (link + 1).cast().c(); int32_t mine = link_data[0] + ofh->code_infos[current_seg].offset; int32_t tgt = link_data[1] + ofh->code_infos[target_seg].offset; int32_t diff = tgt - mine; uint32_t offset_of_patch = link_data[2] + ofh->code_infos[current_seg].offset; + + if (!ofh->code_infos[target_seg].offset) { + // we want to address GOAL 0. In the case where this is a rip-relative load or store, this + // will crash, which is what we want. If it's an lea and just getting an address, this will get + // us a nullptr. If you do a method-set! with a null pointer it does nothing, so it's safe to + // method-set! to things that are in unloaded segments and it'll just keep the old method. + diff = -mine; + } // printf("link object in seg %d diff %d at %d (%d + %d)\n", target_seg, diff, offset_of_patch, // link_data[2], ofh->code_infos[current_seg].offset); @@ -367,7 +377,7 @@ uint32_t cross_seg_dist_link_v3(Ptr link, } else if (size == 8) { *Ptr(offset_of_patch).c() = diff; } else { - throw std::runtime_error("unknown size in cross_seg_dist_link_v3"); + assert(false); } return 1 + 3 * 4; @@ -463,36 +473,38 @@ uint32_t link_control::work_v3() { // state 1: linking. For now all links are done at once. This is probably going to be fine on a // modern computer. But the game broke this into multiple steps. if (m_segment_process < ofh->segment_count) { - Ptr lp(ofh->link_infos[m_segment_process].offset); + if (ofh->code_infos[m_segment_process].offset) { + Ptr lp(ofh->link_infos[m_segment_process].offset); - while (*lp) { - switch (*lp) { - case LINK_TABLE_END: - break; - case LINK_SYMBOL_OFFSET: - lp = lp + 1; - lp = lp + symlink_v3(lp, Ptr(ofh->code_infos[m_segment_process].offset)); - break; - case LINK_TYPE_PTR: - lp = lp + 1; // seek past id - lp = lp + typelink_v3(lp, Ptr(ofh->code_infos[m_segment_process].offset)); - break; - case LINK_DISTANCE_TO_OTHER_SEG_64: - lp = lp + 1; - lp = lp + cross_seg_dist_link_v3(lp, ofh, m_segment_process, 8); - break; - case LINK_DISTANCE_TO_OTHER_SEG_32: - lp = lp + 1; - lp = lp + cross_seg_dist_link_v3(lp, ofh, m_segment_process, 4); - break; - case LINK_PTR: - lp = lp + 1; - lp = lp + ptr_link_v3(lp, ofh, m_segment_process); - break; - default: - printf("unknown link table thing %d\n", *lp); - exit(0); - break; + while (*lp) { + switch (*lp) { + case LINK_TABLE_END: + break; + case LINK_SYMBOL_OFFSET: + lp = lp + 1; + lp = lp + symlink_v3(lp, Ptr(ofh->code_infos[m_segment_process].offset)); + break; + case LINK_TYPE_PTR: + lp = lp + 1; // seek past id + lp = lp + typelink_v3(lp, Ptr(ofh->code_infos[m_segment_process].offset)); + break; + case LINK_DISTANCE_TO_OTHER_SEG_64: + lp = lp + 1; + lp = lp + cross_seg_dist_link_v3(lp, ofh, m_segment_process, 8); + break; + case LINK_DISTANCE_TO_OTHER_SEG_32: + lp = lp + 1; + lp = lp + cross_seg_dist_link_v3(lp, ofh, m_segment_process, 4); + break; + case LINK_PTR: + lp = lp + 1; + lp = lp + ptr_link_v3(lp, ofh, m_segment_process); + break; + default: + printf("unknown link table thing %d\n", *lp); + assert(false); + break; + } } } diff --git a/game/kernel/klisten.cpp b/game/kernel/klisten.cpp index 9b667c2126..f5825d6e5a 100644 --- a/game/kernel/klisten.cpp +++ b/game/kernel/klisten.cpp @@ -5,6 +5,8 @@ */ #include +#include +#include #include "klisten.h" #include "kboot.h" #include "kprint.h" @@ -133,9 +135,9 @@ void ProcessListenerMessage(Ptr msg) { break; case LTT_MSG_RESET: MasterExit = 1; - if (protoBlock.msg_id == UINT64_MAX) { - MasterExit = 2; - } + break; + case LTT_MSG_SHUTDOWN: + MasterExit = 2; break; case LTT_MSG_CODE: { auto buffer = kmalloc(kdebugheap, MessCount, 0, "listener-link-block"); diff --git a/game/kernel/kmachine.cpp b/game/kernel/kmachine.cpp index 43b27d187f..c7552cdd35 100644 --- a/game/kernel/kmachine.cpp +++ b/game/kernel/kmachine.cpp @@ -366,7 +366,7 @@ int ShutdownMachine() { CloseListener(); ShutdownSound(); ShutdownGoalProto(); - Msg(6, "kernel: machine shutdown"); + Msg(6, "kernel: machine shutdown\n"); return 0; } diff --git a/game/kernel/kmalloc.cpp b/game/kernel/kmalloc.cpp index 0dc84e9c7b..54375c4e47 100644 --- a/game/kernel/kmalloc.cpp +++ b/game/kernel/kmalloc.cpp @@ -6,6 +6,7 @@ */ #include +#include #include "common/goal_constants.h" #include "kmalloc.h" #include "kprint.h" diff --git a/game/kernel/kprint.cpp b/game/kernel/kprint.cpp index 29fbfb56c9..4e228dc9c1 100644 --- a/game/kernel/kprint.cpp +++ b/game/kernel/kprint.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "common/goal_constants.h" #include "common/common_types.h" @@ -348,10 +349,9 @@ s32 cvt_float(float x, s32 precision, s32* lead_char, char* buff_start, char* bu value = (char)rounder; } else if (!(ru32 >> 31)) { // sign bit value = 0; - throw std::runtime_error("got very large exponent in rounding calculation"); + assert(false); // not sure what happens here. } else { value = -1; // happens on NaN's - // throw std::runtime_error("got negative sign bit in rounding calculation"); } // place number at the end of the buffer and move pointer back @@ -396,10 +396,9 @@ s32 cvt_float(float x, s32 precision, s32* lead_char, char* buff_start, char* bu value = (char)next_int; } else if (!(ru32 >> 0x1f)) { value = 0; - throw std::runtime_error("got very large exponent in rounding calculation"); + assert(false); // not sure what happens here. } else { value = -1; // happens on NaN's - // throw std::runtime_error("got negative sign bit in rounding calculation"); } *count_chrp = value + '0'; count_chrp++; @@ -411,7 +410,7 @@ s32 cvt_float(float x, s32 precision, s32* lead_char, char* buff_start, char* bu // however, the rounding flag is always disabled and the rounding code doesn't work. if ((fraction_part != 0.f) && ((flags & 1) != 0)) { start_ptr = round(fraction_part, nullptr, start_ptr, count_chrp - 1, 0, lead_char); - throw std::runtime_error("cvt_float called round!"); + assert(false); } } @@ -563,7 +562,7 @@ char* kitoa(char* buffer, s64 value, u64 base, s32 length, char pad, u32 flag) { * uses C varags, but 128-bit varags don't work, so "format" always passes 0 for quadword printing. */ void kqtoa() { - throw std::runtime_error("kqtoa not implemented"); + assert(false); } struct format_struct { @@ -801,7 +800,7 @@ s32 format_impl(uint64_t* args) { } kstrinsert(output_ptr, pad, desired_length - print_len); } else { - throw std::runtime_error("unsupported justify in format"); + assert(false); // output_ptr = strend(output_ptr); // while(0 < (desired_length - print_len)) { // char pad = ' '; @@ -852,7 +851,7 @@ s32 format_impl(uint64_t* args) { kstrinsert(output_ptr, pad, desired_length - print_len); } else { - throw std::runtime_error("unsupported justify in format"); + assert(false); // output_ptr = strend(output_ptr); // u32 l140 = 0; // while(l140 < (desired_length - print_len)) { @@ -893,9 +892,7 @@ s32 format_impl(uint64_t* args) { call_method_of_type(in, type, GOAL_PRINT_METHOD); } } else { - // TODO - if we can't throw exceptions, what is the option? - // log, break and continue? - throw std::runtime_error("failed to find symbol in format!"); + assert(false); // bad type. } } output_ptr = strend(output_ptr); @@ -916,7 +913,7 @@ s32 format_impl(uint64_t* args) { call_method_of_type(in, type, GOAL_INSPECT_METHOD); } } else { - throw std::runtime_error("failed to find symbol in format!"); + assert(false); // bad type } } output_ptr = strend(output_ptr); @@ -924,7 +921,7 @@ s32 format_impl(uint64_t* args) { case 'Q': // not yet implemented. hopefully andy gavin finishes this one soon. case 'q': - throw std::runtime_error("nyi q format string"); + assert(false); break; case 'X': // hex, 64 bit, pad padchar @@ -1021,7 +1018,7 @@ s32 format_impl(uint64_t* args) { precision = 4; float value; if (in < 0) { - throw std::runtime_error("time seconds format error negative.\n"); + assert(false); // i don't get this one } else { value = in; } @@ -1037,7 +1034,7 @@ s32 format_impl(uint64_t* args) { default: MsgErr("format: unknown code 0x%02x\n", format_ptr[1]); - throw std::runtime_error("format error"); + assert(false); break; } format_ptr++; @@ -1079,13 +1076,13 @@ s32 format_impl(uint64_t* args) { *PrintPendingLocal3 = 0; return 0; } else if (type == *Ptr>(s7.offset + FIX_SYM_FILE_STREAM_TYPE)) { - throw std::runtime_error("FORMAT into a file stream not supported"); + assert(false); // file stream nyi } } - throw std::runtime_error("unknown format destination"); + assert(false); // unknown destination return 0; } - throw std::runtime_error("how did we get here?"); + assert(false); // ?????? return 7; } \ No newline at end of file diff --git a/game/kernel/kscheme.cpp b/game/kernel/kscheme.cpp index ebe3164fdc..e85ad1479a 100644 --- a/game/kernel/kscheme.cpp +++ b/game/kernel/kscheme.cpp @@ -126,9 +126,7 @@ u64 goal_malloc(u32 heap, u32 size, u32 flags, u32 name) { * completely defined. */ u64 alloc_from_heap(u32 heapSymbol, u32 type, s32 size) { - if (size <= 0) { - throw std::runtime_error("got <= 0 size allocation in alloc_from_heap!"); - } + assert(size > 0); // align to 16 bytes (part one) s32 alignedSize = size + 0xf; @@ -165,7 +163,7 @@ u64 alloc_from_heap(u32 heapSymbol, u32 type, s32 size) { return kmalloc(*Ptr>(heapSymbol), size, KMALLOC_MEMSET, gstr->data()).offset; } else if (heapOffset == FIX_SYM_PROCESS_TYPE) { - throw std::runtime_error("this type of process allocation is not supported yet!\n"); + assert(false); // nyi // allocate on current process heap // Ptr start = *ptr(getS6() + 0x4c + 8); // Ptr heapEnd = *ptr(getS6() + 0x4c + 4); @@ -181,7 +179,7 @@ u64 alloc_from_heap(u32 heapSymbol, u32 type, s32 size) { // alignedSize); return 0; // } } else if (heapOffset == FIX_SYM_SCRATCH) { - throw std::runtime_error("this type of scratchpad allocation is not used!\n"); + assert(false); // nyi, I think unused. } else { memset(Ptr(heapSymbol).c(), 0, (size_t)alignedSize); // treat it as a stack address return heapSymbol; @@ -1122,8 +1120,8 @@ u64 call_method_of_type(u32 arg, Ptr type, u32 method_id) { (*type_tag).offset); } } - // throw std::runtime_error("call_method_of_type failed!\n"); printf("[ERROR] call_method_of_type failed!\n"); + assert(false); return arg; } @@ -1159,7 +1157,8 @@ u64 call_method_of_type_arg2(u32 arg, Ptr type, u32 method_id, u32 a1, u32 (*type_tag).offset); } } - throw std::runtime_error("call_method_of_type failed!\n"); + printf("[ERROR] call_method_of_type_arg2 failed!\n"); + assert(false); return arg; } diff --git a/game/main.cpp b/game/main.cpp index ed189cb233..3687634ebe 100644 --- a/game/main.cpp +++ b/game/main.cpp @@ -16,7 +16,6 @@ void setup_logging(bool verbose) { auto game_logger = spdlog::stdout_color_mt("GOAL Runtime"); spdlog::set_default_logger(game_logger); spdlog::flush_on(spdlog::level::info); - spdlog::set_pattern("%v"); spdlog::info("Verbose logging enabled"); } else { auto game_logger = spdlog::basic_logger_mt("GOAL Runtime", "logs/runtime.log"); diff --git a/game/overlord/fake_iso.cpp b/game/overlord/fake_iso.cpp index 6971de0c33..fbf1831f7b 100644 --- a/game/overlord/fake_iso.cpp +++ b/game/overlord/fake_iso.cpp @@ -273,6 +273,9 @@ uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len) { const char* path = get_file_path(fd->fr); FILE* fp = fopen(path, "rb"); + if (!fp) { + spdlog::error("[OVERLORD] fake iso could not open the file \"{}\"", path); + } assert(fp); fseek(fp, 0, SEEK_END); uint32_t file_len = ftell(fp); diff --git a/game/runtime.cpp b/game/runtime.cpp index 37bfbf2587..002ede5b44 100644 --- a/game/runtime.cpp +++ b/game/runtime.cpp @@ -47,6 +47,7 @@ #include "game/overlord/stream.h" #include "common/goal_constants.h" +#include "common/cross_os_debug/xdbg.h" u8* g_ee_main_mem = nullptr; @@ -75,7 +76,7 @@ void deci2_runner(SystemThreadInterface& iface) { server.wait_for_protos_ready(); // then allow the server to accept connections if (!server.init()) { - throw std::runtime_error("DECI2 server init failed"); + assert(false); } spdlog::debug("[DECI2] Waiting for listener..."); @@ -148,6 +149,9 @@ void ee_runner(SystemThreadInterface& iface) { kmemcard_init_globals(); kprint_init_globals(); + // Added for OpenGOAL's debugger + xdbg::allow_debugging(); + goal_main(g_argc, g_argv); spdlog::debug("[EE] Done!"); diff --git a/game/sce/iop.cpp b/game/sce/iop.cpp index dc984c3f00..4546eec398 100644 --- a/game/sce/iop.cpp +++ b/game/sce/iop.cpp @@ -203,12 +203,14 @@ s32 CreateSema(SemaParam* param) { s32 WaitSema(s32 sema) { (void)sema; - throw std::runtime_error("NYI"); + assert(false); // nyi + return 0; } s32 SignalSema(s32 sema) { (void)sema; - throw std::runtime_error("NYI"); + assert(false); // nyi + return 0; } s32 WakeupThread(s32 thid) { diff --git a/game/system/Deci2Server.cpp b/game/system/Deci2Server.cpp index 665beb467f..d9c5d86d96 100644 --- a/game/system/Deci2Server.cpp +++ b/game/system/Deci2Server.cpp @@ -217,7 +217,6 @@ void Deci2Server::run() { printf("[DECI2] Warning: no handler for this message, ignoring...\n"); unlock(); return; - // throw std::runtime_error("no handler!"); } auto& driver = d2_drivers[handler]; diff --git a/game/system/IOP_Kernel.cpp b/game/system/IOP_Kernel.cpp index 21ecdb47fe..65db7d6a10 100644 --- a/game/system/IOP_Kernel.cpp +++ b/game/system/IOP_Kernel.cpp @@ -7,17 +7,17 @@ * Create a new thread. Will not run the thread. */ s32 IOP_Kernel::CreateThread(std::string name, u32 (*func)()) { - if (_currentThread != -1) - throw std::runtime_error("tried to create thread from thread"); + assert(_currentThread == -1); // can only create thread from kernel thread. + u32 ID = (u32)_nextThID++; - if (threads.size() != ID) - throw std::runtime_error("thread number error?"); + assert(ID == threads.size()); + // add entry threads.emplace_back(name, func, ID, this); // setup the thread! // printf("[IOP Kernel] SetupThread %s...\n", name.c_str()); - // hack to allow creating a "null thread" which doesn't/can't run but occupies slot 0. + // allow creating a "null thread" which doesn't/can't run but occupies slot 0. if (func) { _currentThread = ID; // create OS thread, will run the setupThread function @@ -47,11 +47,9 @@ void IOP_Kernel::setupThread(s32 id) { returnToKernel(); threads.at(id).waitForDispatch(); // printf("[IOP Kernel] Thread %s first dispatch!\n", threads.at(id).name.c_str()); - if (_currentThread != id) { - throw std::runtime_error("the wrong thread has run!\n"); - } + assert(_currentThread == id); // should run in the thread. (threads.at(id).function)(); - printf("Thread %s has returned!\n", threads.at(id).name.c_str()); + // printf("Thread %s has returned!\n", threads.at(id).name.c_str()); threads.at(id).done = true; returnToKernel(); } @@ -60,8 +58,7 @@ void IOP_Kernel::setupThread(s32 id) { * Run a thread (call from kernel) */ void IOP_Kernel::runThread(s32 id) { - if (_currentThread != -1) - throw std::runtime_error("tried to runThread in a thread"); + assert(_currentThread == -1); // should run in the kernel thread _currentThread = id; threads.at(id).dispatch(); threads.at(id).waitForReturnToKernel(); @@ -77,9 +74,8 @@ void IOP_Kernel::SuspendThread() { s32 oldThread = getCurrentThread(); threads.at(oldThread).returnToKernel(); threads.at(oldThread).waitForDispatch(); - if (_currentThread != oldThread) { - throw std::runtime_error("bad resume"); - } + // check kernel resumed us correctly + assert(_currentThread == oldThread); } /*! @@ -130,8 +126,8 @@ void IOP_Kernel::dispatchAll() { */ void IopThreadRecord::returnToKernel() { runThreadReady = false; - if (kernel->getCurrentThread() != thID) - throw std::runtime_error("tried to sleep the wrong thread!"); + // should be called from the correct thread + assert(kernel->getCurrentThread() == thID); { std::lock_guard lck(*threadToKernelMutex); @@ -145,8 +141,8 @@ void IopThreadRecord::returnToKernel() { */ void IopThreadRecord::dispatch() { syscallReady = false; - if (kernel->getCurrentThread() != thID) - throw std::runtime_error("tried to dispatch the wrong thread!"); + assert(kernel->getCurrentThread() == thID); + { std::lock_guard lck(*kernelToThreadMutex); runThreadReady = true; @@ -160,17 +156,14 @@ void IopThreadRecord::dispatch() { void IopThreadRecord::waitForReturnToKernel() { std::unique_lock lck(*threadToKernelMutex); threadToKernelCV->wait(lck, [this] { return syscallReady; }); - // syscallReady = false; } /*! * Thread waits for kernel to dispatch it. */ void IopThreadRecord::waitForDispatch() { - // if(kernel->getCurrentThread() == -1) throw std::runtime_error("tried to suspend main!\n"); std::unique_lock lck(*kernelToThreadMutex); kernelToThreadCV->wait(lck, [this] { return runThreadReady; }); - // runThreadReady = false; } void IOP_Kernel::set_rpc_queue(iop::sceSifQueueData* qd, u32 thread) { diff --git a/game/system/IOP_Kernel.h b/game/system/IOP_Kernel.h index c25d312322..ed74e55fb1 100644 --- a/game/system/IOP_Kernel.h +++ b/game/system/IOP_Kernel.h @@ -10,6 +10,7 @@ #include #include #include +#include #include "common/common_types.h" class IOP_Kernel; @@ -98,8 +99,7 @@ class IOP_Kernel { * Resume the kernel. */ void returnToKernel() { - if (_currentThread < 0) - throw std::runtime_error("tried to return to kernel not in a thread"); + assert(_currentThread >= 0); // must be in a thread threads[_currentThread].returnToKernel(); } @@ -126,15 +126,16 @@ class IOP_Kernel { // total hack - returning this value causes the ISO thread to error out and quit. return -0x1a9; } - // printf("poll %d %ld\n", mbx, mbxs.size()); - if (mbx >= (s32)mbxs.size()) - throw std::runtime_error("invalid PollMbx"); + + assert(mbx < (s32)mbxs.size()); s32 gotSomething = mbxs[mbx].empty() ? 0 : 1; if (gotSomething) { void* thing = mbxs[mbx].front(); - // printf("pop from msgbox %d %p\n", mbx, thing); - if (msg) + + if (msg) { *msg = thing; + } + mbxs[mbx].pop(); } @@ -145,11 +146,8 @@ class IOP_Kernel { * Push something into a mbx */ s32 SendMbx(s32 mbx, void* value) { - if (mbx >= (s32)mbxs.size()) - throw std::runtime_error("invalid SendMbx"); + assert(mbx < (s32)mbxs.size()); mbxs[mbx].push(value); - // printf("push into messagebox %d %p\n", mbx, value); - // printf("mbx size %ld\n", mbxs.size()); return 0; } diff --git a/game/system/SystemThread.cpp b/game/system/SystemThread.cpp index 790dca6154..f7089f2c19 100644 --- a/game/system/SystemThread.cpp +++ b/game/system/SystemThread.cpp @@ -15,7 +15,7 @@ SystemThread& SystemThreadManager::create_thread(const std::string& name) { if (thread_count >= MAX_SYSTEM_THREADS) { spdlog::critical("Out of System Threads! MAX_SYSTEM_THREADS is ", MAX_SYSTEM_THREADS); - throw std::runtime_error("Out of System Threads! Please increase MAX_SYSTEM_THREADS"); + assert(false); } auto& thread = threads[thread_count]; diff --git a/goalc/listener/Listener.cpp b/goalc/listener/Listener.cpp index 50338f6fd6..b724dcdc1c 100644 --- a/goalc/listener/Listener.cpp +++ b/goalc/listener/Listener.cpp @@ -182,6 +182,9 @@ bool Listener::connect_to_target(int n_tries, const std::string& ip, int port) { listen_socket = -1; return false; } + + last_recvd_id = 0; + last_sent_id = 0; } /*! @@ -218,7 +221,17 @@ void Listener::receive_func() { case ListenerMessageKind::MSG_ACK: // an "ack" message, sent by the target to indicate it got something. if (!waiting_for_ack) { - printf("[Listener] Got an ack message when we weren't expecting one."); + if (hdr->msg_id == last_sent_id) { + printf("[Listener] Received ACK for most recent message late.\n"); + if (last_recvd_id != hdr->msg_id - 1) { + printf( + "[Listener] WARNING: message ID jumped from %ld to %ld. Some messages may have " + "been lost. You must wait for an ACK before sending the next message.\n", + last_recvd_id, hdr->msg_id); + } + } else { + printf("[Listener] Got an unexpcted ACK message."); + } } if (hdr->deci2_header.len < 512) { @@ -236,6 +249,13 @@ void Listener::receive_func() { ack_recv_buff[ack_recv_prog] = '\0'; assert(ack_recv_prog < 512); got_ack = true; + last_recvd_id = hdr->msg_id; + if (last_recvd_id > last_sent_id) { + printf( + "[Listener] ERROR: Got an ack message with id of %ld, but the last message sent " + "had an ID of %ld.\n", + last_recvd_id, last_sent_id); + } } else { printf("[Listener] got invalid ack!\n"); } @@ -245,6 +265,7 @@ void Listener::receive_func() { case ListenerMessageKind::MSG_PRINT: { auto* str_buff = new char[hdr->msg_size + 1]; // plus one for the null terminator int msg_prog = 0; + assert(hdr->msg_id == 0); while (rcvd < hdr->deci2_header.len) { if (!m_connected) { return; @@ -338,7 +359,8 @@ void Listener::send_code(std::vector& code) { header->msg_size = code.size(); header->ltt_msg_kind = LTT_MSG_CODE; header->u6 = 0; - header->msg_id = 0; + last_sent_id++; + header->msg_id = last_sent_id; memcpy(buffer_data, code.data(), code.size()); send_buffer(total_size); } @@ -365,9 +387,10 @@ void Listener::send_reset(bool shutdown) { header->deci2_header.src = 'H'; header->deci2_header.dst = 'E'; header->msg_size = 0; - header->ltt_msg_kind = LTT_MSG_RESET; + header->ltt_msg_kind = shutdown ? LTT_MSG_SHUTDOWN : LTT_MSG_RESET; header->u6 = 0; - header->msg_id = shutdown ? UINT64_MAX : 0; + last_sent_id++; + header->msg_id = last_sent_id; send_buffer(sizeof(ListenerMessageHeader)); disconnect(); close_socket(listen_socket); @@ -392,7 +415,8 @@ void Listener::send_poke() { header->msg_size = 0; header->ltt_msg_kind = LTT_MSG_POKE; header->u6 = 0; - header->msg_id = 0; + last_sent_id++; + header->msg_id = last_sent_id; send_buffer(sizeof(ListenerMessageHeader)); } @@ -426,8 +450,7 @@ void Listener::send_buffer(int sz) { printf(" OK\n"); } } else { - printf( - " Error - target has timed out. If it is stuck in a loop, it must be manually killed.\n"); + printf(" Timed out waiting for ack.\n"); } } diff --git a/goalc/listener/Listener.h b/goalc/listener/Listener.h index f8fe829ad9..156df1a36e 100644 --- a/goalc/listener/Listener.h +++ b/goalc/listener/Listener.h @@ -65,6 +65,8 @@ class Listener { std::vector message_record; std::unordered_map m_load_entries; char ack_recv_buff[512]; + uint64_t last_sent_id = 0; + uint64_t last_recvd_id = 0; }; } // namespace listener diff --git a/test/goalc/CMakeLists.txt b/test/goalc/CMakeLists.txt index 01a8d14bde..fdbc5728cc 100644 --- a/test/goalc/CMakeLists.txt +++ b/test/goalc/CMakeLists.txt @@ -3,6 +3,7 @@ set(GOALC_TEST_CASES "goalc/all_goalc_template_tests.cpp" goalc/test_debugger.cpp + goalc/test_game_no_debug.cpp ) set(GOALC_TEST_FRAMEWORK_SOURCES diff --git a/test/goalc/framework/test_runner.cpp b/test/goalc/framework/test_runner.cpp index 41447e323d..63de48aad1 100644 --- a/test/goalc/framework/test_runner.cpp +++ b/test/goalc/framework/test_runner.cpp @@ -102,6 +102,12 @@ void runtime_with_kernel() { exec_runtime(argc, const_cast(argv)); } +void runtime_with_kernel_no_debug_segment() { + constexpr int argc = 3; + const char* argv[argc] = {"", "-fakeiso", "-debug-mem"}; + exec_runtime(argc, const_cast(argv)); +} + void createDirIfAbsent(const std::string& path) { if (!std::filesystem::is_directory(path) || !std::filesystem::exists(path)) { std::filesystem::create_directory(path); diff --git a/test/goalc/framework/test_runner.h b/test/goalc/framework/test_runner.h index d5fb13a4ba..63ec3bbc5c 100644 --- a/test/goalc/framework/test_runner.h +++ b/test/goalc/framework/test_runner.h @@ -41,6 +41,7 @@ struct CompilerTestRunner { void runtime_no_kernel(); void runtime_with_kernel(); +void runtime_with_kernel_no_debug_segment(); void createDirIfAbsent(const std::string& path); std::string getTemplateDir(const std::string& category); diff --git a/test/goalc/test_game_no_debug.cpp b/test/goalc/test_game_no_debug.cpp new file mode 100644 index 0000000000..331f4d4c8a --- /dev/null +++ b/test/goalc/test_game_no_debug.cpp @@ -0,0 +1,32 @@ +// Test the game running without loading debug segments. + +#include "gtest/gtest.h" +#include "goalc/compiler/Compiler.h" +#include "test/goalc/framework/test_runner.h" + +TEST(GameNoDebugSegment, Init) { + Compiler compiler; + compiler.run_front_end_on_string("(build-kernel)"); + std::thread runtime_thread = std::thread(GoalTest::runtime_with_kernel_no_debug_segment); + + // this shouldn't crash + compiler.run_test_from_string("(inspect *kernel-context*)"); + + // these should be equal, both the fallback inspect method + EXPECT_TRUE(compiler.run_test_from_string( + "(print (eq? (method kernel-context inspect) (method cpu-thread inspect))) 0") == + std::vector{"#t\n0\n"}); + + // should be below the debug heap. + EXPECT_TRUE( + compiler.run_test_from_string( + "(print (< (the uint (method kernel-context inspect)) (the uint (-> debug base)))) 0") == + std::vector{"#t\n0\n"}); + + // debug segment flag should be disabled. + EXPECT_TRUE(compiler.run_test_from_string("(print *debug-segment*) 0") == + std::vector{"#f\n0\n"}); + + compiler.shutdown_target(); + runtime_thread.join(); +} \ No newline at end of file