Prototype quicksave functionality, disabled for now
This commit is contained in:
parent
a25f967094
commit
ecfe2381b0
|
@ -63,6 +63,8 @@ target_sources(PatchesLib PRIVATE
|
|||
${CMAKE_SOURCE_DIR}/RecompiledPatches/patches.c
|
||||
)
|
||||
|
||||
set_source_files_properties(${CMAKE_SOURCE_DIR}/RecompiledPatches/patches.c PROPERTIES COMPILE_FLAGS -fno-strict-aliasing)
|
||||
|
||||
# Build patches elf
|
||||
add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/patches/patches.elf
|
||||
COMMAND make
|
||||
|
@ -119,6 +121,7 @@ set (SOURCES
|
|||
|
||||
${CMAKE_SOURCE_DIR}/src/game/input.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/game/controls.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/game/quicksaving.cpp
|
||||
|
||||
${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/ui_launcher.cpp
|
||||
|
|
|
@ -50,6 +50,10 @@ namespace recomp {
|
|||
void handle_events();
|
||||
|
||||
bool game_input_disabled();
|
||||
|
||||
// TODO move these
|
||||
void quicksave_save();
|
||||
void quicksave_load();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -48,6 +48,8 @@ DECLARE_FUNC(void, recomp_get_camera_inputs, float* x_out, float* y_out);
|
|||
// TODO move these
|
||||
DECLARE_FUNC(void, recomp_puts, const char* data, u32 size);
|
||||
DECLARE_FUNC(void, recomp_exit);
|
||||
DECLARE_FUNC(void, recomp_handle_quicksave_actions, OSMesgQueue* enter_mq, OSMesgQueue* exit_mq);
|
||||
DECLARE_FUNC(void, recomp_handle_quicksave_actions_main, OSMesgQueue* enter_mq, OSMesgQueue* exit_mq);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#ifndef __PATCHES_H__
|
||||
#define __PATCHES_H__
|
||||
|
||||
// TODO fix renaming symbols in patch recompilation
|
||||
#define osRecvMesg osRecvMesg_recomp
|
||||
#define osSendMesg osSendMesg_recomp
|
||||
#include "global.h"
|
||||
#include "rt64_extended_gbi.h"
|
||||
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
// Quicksaving is disabled for now
|
||||
|
||||
/* To reenable it, a few changes to the recompiled functions are also needed:
|
||||
|
||||
in Main:
|
||||
L_80174E84:
|
||||
// or $a0, $s3, $zero
|
||||
ctx->r4 = ctx->r19 | 0;
|
||||
// addiu $a1, $sp, 0x38
|
||||
ctx->r5 = ADD32(ctx->r29, 0X38);
|
||||
// jal 0x80087ED0
|
||||
// addiu $a2, $zero, 0x1
|
||||
ctx->r6 = ADD32(0, 0X1);
|
||||
osRecvMesg_recomp(rdram, ctx);
|
||||
+handle_quicksave_actions(rdram, ctx);
|
||||
|
||||
in Graph_ThreadEntry:
|
||||
L_801749C8:
|
||||
+handle_quicksave_actions_main(rdram, ctx);
|
||||
+ctx->r4 = ctx->r17 | 0;
|
||||
// jal 0x80174868
|
||||
// or $a1, $s0, $zero
|
||||
ctx->r5 = ctx->r16 | 0;
|
||||
Graph_Update(rdram, ctx);
|
||||
|
||||
in PadMgr_ThreadEntry:
|
||||
L_801760A0:
|
||||
// or $a1, $s2, $zero
|
||||
ctx->r5 = ctx->r18 | 0;
|
||||
// jal 0x80087ED0
|
||||
// or $a2, $s3, $zero
|
||||
ctx->r6 = ctx->r19 | 0;
|
||||
osRecvMesg_recomp(rdram, ctx);
|
||||
+handle_quicksave_actions(rdram, ctx);
|
||||
|
||||
in AudioMgr_ThreadEntry:
|
||||
L_80172F58:
|
||||
+handle_quicksave_actions(rdram, ctx);
|
||||
+ctx->r4 = ctx->r22 | 0;
|
||||
// or $a1, $s2, $zero
|
||||
ctx->r5 = ctx->r18 | 0;
|
||||
// jal 0x80087ED0
|
||||
// or $a2, $s3, $zero
|
||||
ctx->r6 = ctx->r19 | 0;
|
||||
osRecvMesg_recomp(rdram, ctx);
|
||||
|
||||
*/
|
||||
|
||||
#if 0
|
||||
|
||||
#include "patches.h"
|
||||
#include "input.h"
|
||||
#include "audiomgr.h"
|
||||
|
||||
// ultramodern's message queues can be statically initialized, so we don't actually need to call osCreateMesgQueue for these.
|
||||
OSMesg quicksave_enter_mq_buffer[20];
|
||||
OSMesgQueue quicksave_enter_mq = {
|
||||
.msgCount = ARRAY_COUNT(quicksave_enter_mq_buffer),
|
||||
.msg = quicksave_enter_mq_buffer,
|
||||
.validCount = 0,
|
||||
.first = 0
|
||||
};
|
||||
|
||||
OSMesg quicksave_exit_mq_buffer[20];
|
||||
OSMesgQueue quicksave_exit_mq = {
|
||||
.msgCount = ARRAY_COUNT(quicksave_exit_mq_buffer),
|
||||
.msg = quicksave_exit_mq_buffer,
|
||||
.validCount = 0,
|
||||
.first = 0
|
||||
};
|
||||
|
||||
void handle_quicksave_actions() {
|
||||
recomp_handle_quicksave_actions(&quicksave_enter_mq, &quicksave_exit_mq);
|
||||
}
|
||||
|
||||
extern OSMesgQueue sIrqMgrMsgQueue; // For Main thread
|
||||
s16 main_irq_message = 0; // dummy value for sending a pointer to the main thread
|
||||
|
||||
#define RECOMP_IRQ_QUICKSAVE 0x2A2
|
||||
|
||||
void IrqMgr_ThreadEntry(IrqMgr* irqmgr) {
|
||||
u32 interrupt;
|
||||
u32 stop;
|
||||
|
||||
interrupt = 0;
|
||||
stop = 0;
|
||||
while (stop == 0) {
|
||||
if (stop) {
|
||||
;
|
||||
}
|
||||
|
||||
osRecvMesg(&irqmgr->irqQueue, (OSMesg*)&interrupt, OS_MESG_BLOCK);
|
||||
switch (interrupt) {
|
||||
case 0x29A:
|
||||
IrqMgr_HandleRetrace(irqmgr);
|
||||
break;
|
||||
case 0x29D:
|
||||
IrqMgr_HandlePreNMI(irqmgr);
|
||||
break;
|
||||
case 0x29F:
|
||||
IrqMgr_HandlePRENMI450(irqmgr);
|
||||
break;
|
||||
case 0x2A0:
|
||||
IrqMgr_HandlePRENMI480(irqmgr);
|
||||
break;
|
||||
case 0x2A1:
|
||||
IrqMgr_HandlePRENMI500(irqmgr);
|
||||
break;
|
||||
case RECOMP_IRQ_QUICKSAVE:
|
||||
handle_quicksave_actions();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern IrqMgr gIrqMgr;
|
||||
|
||||
#define RSP_DONE_MSG 667
|
||||
#define RDP_DONE_MSG 668
|
||||
#define ENTRY_MSG 670
|
||||
#define RDP_AUDIO_CANCEL_MSG 671
|
||||
#define RSP_GFX_CANCEL_MSG 672
|
||||
#define RECOMP_QUICKSAVE_ACTION 673 // @recomp
|
||||
|
||||
|
||||
void Sched_ThreadEntry(void* arg) {
|
||||
s32 msg = 0;
|
||||
SchedContext* sched = (SchedContext*)arg;
|
||||
|
||||
while (true) {
|
||||
osRecvMesg(&sched->interruptQ, (OSMesg*)&msg, OS_MESG_BLOCK);
|
||||
|
||||
// Check if it's a message from another thread or the OS
|
||||
switch (msg) {
|
||||
case RDP_AUDIO_CANCEL_MSG:
|
||||
Sched_HandleAudioCancel(sched);
|
||||
continue;
|
||||
|
||||
case RSP_GFX_CANCEL_MSG:
|
||||
Sched_HandleGfxCancel(sched);
|
||||
continue;
|
||||
|
||||
case ENTRY_MSG:
|
||||
Sched_HandleEntry(sched);
|
||||
continue;
|
||||
|
||||
case RSP_DONE_MSG:
|
||||
Sched_HandleRSPDone(sched);
|
||||
continue;
|
||||
|
||||
case RDP_DONE_MSG:
|
||||
Sched_HandleRDPDone(sched);
|
||||
continue;
|
||||
|
||||
case RECOMP_QUICKSAVE_ACTION:
|
||||
handle_quicksave_actions();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if it's a message from the IrqMgr
|
||||
switch (((OSScMsg*)msg)->type) {
|
||||
case OS_SC_RETRACE_MSG:
|
||||
Sched_HandleRetrace(sched);
|
||||
continue;
|
||||
|
||||
case OS_SC_PRE_NMI_MSG:
|
||||
Sched_HandleReset(sched);
|
||||
continue;
|
||||
|
||||
case OS_SC_NMI_MSG:
|
||||
Sched_HandleStop(sched);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern PadMgr* sPadMgrInstance;
|
||||
s16 padmgr_dummy_message = 0;
|
||||
|
||||
extern AudioMgr sAudioMgr;
|
||||
s16 audiomgr_dummy_message = 0;
|
||||
|
||||
extern OSMesgQueue sDmaMgrMsgQueue;
|
||||
#define RECOMP_DMAMGR_QUICKSAVE_MESSAGE 1
|
||||
|
||||
void DmaMgr_ThreadEntry(void* a0) {
|
||||
OSMesg msg;
|
||||
DmaRequest* req;
|
||||
|
||||
while (1) {
|
||||
osRecvMesg(&sDmaMgrMsgQueue, &msg, OS_MESG_BLOCK);
|
||||
|
||||
if (msg == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (msg == (OSMesg)RECOMP_DMAMGR_QUICKSAVE_MESSAGE) {
|
||||
handle_quicksave_actions();
|
||||
continue;
|
||||
}
|
||||
|
||||
req = (DmaRequest*)msg;
|
||||
|
||||
DmaMgr_ProcessMsg(req);
|
||||
if (req->notifyQueue) {
|
||||
osSendMesg(req->notifyQueue, req->notifyMsg, OS_MESG_NOBLOCK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern SchedContext gSchedContext;
|
||||
|
||||
void handle_quicksave_actions_main() {
|
||||
recomp_handle_quicksave_actions_main(&quicksave_enter_mq, &quicksave_exit_mq);
|
||||
}
|
||||
|
||||
void wake_threads_for_quicksave_action() {
|
||||
// Wake up the Main thread
|
||||
osSendMesg(&sIrqMgrMsgQueue, &main_irq_message, OS_MESG_BLOCK);
|
||||
// Wake up the IrqMgr thread
|
||||
osSendMesg(&gIrqMgr.irqQueue, (OSMesg)RECOMP_IRQ_QUICKSAVE, OS_MESG_BLOCK);
|
||||
// Wake up the PadMgr thread
|
||||
osSendMesg(&sPadMgrInstance->interruptQueue, &padmgr_dummy_message, OS_MESG_BLOCK);
|
||||
// Wake up the AudioMgr thread
|
||||
osSendMesg(&sAudioMgr.interruptQueue, &audiomgr_dummy_message, OS_MESG_BLOCK);
|
||||
// Wake up the DmaMgr thread
|
||||
osSendMesg(&sDmaMgrMsgQueue, (OSMesg)RECOMP_DMAMGR_QUICKSAVE_MESSAGE, OS_MESG_BLOCK);
|
||||
// Wake up the Sched thread
|
||||
osSendMesg(&gSchedContext.interruptQ, (OSMesg)RECOMP_QUICKSAVE_ACTION, OS_MESG_BLOCK);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -5,3 +5,7 @@ recomp_get_item_inputs = 0x81000000;
|
|||
recomp_puts = 0x81000004;
|
||||
recomp_get_camera_inputs = 0x81000008;
|
||||
recomp_exit = 0x8100000C;
|
||||
recomp_handle_quicksave_actions = 0x81000010;
|
||||
recomp_handle_quicksave_actions_main = 0x81000014;
|
||||
osRecvMesg_recomp = 0x81000018;
|
||||
osSendMesg_recomp = 0x8100001C;
|
||||
|
|
|
@ -210,6 +210,24 @@ void recomp::poll_inputs() {
|
|||
InputState.cur_controllers.push_back(controller);
|
||||
}
|
||||
}
|
||||
|
||||
// Quicksaving is disabled for now and will likely have more limited functionality
|
||||
// when restored, rather than allowing saving and loading at any point in time.
|
||||
#if 0
|
||||
if (InputState.keys) {
|
||||
static bool save_was_held = false;
|
||||
static bool load_was_held = false;
|
||||
bool save_is_held = InputState.keys[SDL_SCANCODE_F5] != 0;
|
||||
bool load_is_held = InputState.keys[SDL_SCANCODE_F7] != 0;
|
||||
if (save_is_held && !save_was_held) {
|
||||
recomp::quicksave_save();
|
||||
}
|
||||
else if (load_is_held && !load_was_held) {
|
||||
recomp::quicksave_load();
|
||||
}
|
||||
save_was_held = save_is_held;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool controller_button_state(int32_t input_id) {
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
// Quicksaving is disabled for now
|
||||
|
||||
#if 0
|
||||
|
||||
#include "recomp_helpers.h"
|
||||
#include "recomp_input.h"
|
||||
#include "../ultramodern/ultramodern.hpp"
|
||||
|
||||
enum class QuicksaveAction {
|
||||
None,
|
||||
Save,
|
||||
Load
|
||||
};
|
||||
|
||||
std::atomic<QuicksaveAction> cur_quicksave_action = QuicksaveAction::None;
|
||||
|
||||
void recomp::quicksave_save() {
|
||||
cur_quicksave_action.store(QuicksaveAction::Save);
|
||||
}
|
||||
|
||||
void recomp::quicksave_load() {
|
||||
cur_quicksave_action.store(QuicksaveAction::Load);
|
||||
}
|
||||
|
||||
uint8_t saved_rdram[ultramodern::rdram_size];
|
||||
|
||||
thread_local recomp_context saved_context;
|
||||
|
||||
void save_context(recomp_context* ctx) {
|
||||
saved_context = *ctx;
|
||||
}
|
||||
|
||||
void load_context(recomp_context* ctx) {
|
||||
*ctx = saved_context;
|
||||
|
||||
// Restore the pointer to the odd floats for correctly handling mips3 float mode.
|
||||
if (ctx->mips3_float_mode) {
|
||||
// FR = 1, odd single floats point to their own registers
|
||||
ctx->f_odd = &ctx->f1.u32l;
|
||||
}
|
||||
else {
|
||||
// FR = 0, odd single floats point to the upper half of the previous register
|
||||
ctx->f_odd = &ctx->f0.u32h;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void recomp_handle_quicksave_actions(uint8_t* rdram, recomp_context* ctx) {
|
||||
QuicksaveAction action = cur_quicksave_action.load();
|
||||
|
||||
if (action != QuicksaveAction::None) {
|
||||
PTR(OSMesgQueue) quicksave_enter_mq = _arg<0, PTR(OSMesgQueue)>(rdram, ctx);
|
||||
PTR(OSMesgQueue) quicksave_exit_mq = _arg<1, PTR(OSMesgQueue)>(rdram, ctx);
|
||||
|
||||
printf("saving context for thread %d\n", TO_PTR(OSThread, ultramodern::this_thread())->id);
|
||||
|
||||
// Save or load the thread's context as needed based on the action.
|
||||
if (action == QuicksaveAction::Save) {
|
||||
save_context(ctx);
|
||||
}
|
||||
else if (action == QuicksaveAction::Load) {
|
||||
load_context(ctx);
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// Tell the main thread that one of the other permanent threads is ready for performing a quicksave action.
|
||||
osSendMesg(rdram, quicksave_enter_mq, NULLPTR, OS_MESG_NOBLOCK);
|
||||
// Wait for the main thread to signal that other permanent threads are safe to continue.
|
||||
osRecvMesg(rdram, quicksave_exit_mq, NULLPTR, OS_MESG_BLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void wake_threads_for_quicksave_action(uint8_t* rdram, recomp_context* ctx);
|
||||
|
||||
extern "C" void recomp_handle_quicksave_actions_main(uint8_t* rdram, recomp_context* ctx) {
|
||||
QuicksaveAction action = cur_quicksave_action.load();
|
||||
|
||||
if (action != QuicksaveAction::None) {
|
||||
PTR(OSMesgQueue) quicksave_enter_mq = _arg<0, PTR(OSMesgQueue)>(rdram, ctx);
|
||||
PTR(OSMesgQueue) quicksave_exit_mq = _arg<1, PTR(OSMesgQueue)>(rdram, ctx);
|
||||
|
||||
wake_threads_for_quicksave_action(rdram, ctx);
|
||||
|
||||
// Wait for all other permanent threads (hence the minus 1) to signal that they're ready for a quicksave action.
|
||||
for (uint32_t i = 0; i < ultramodern::permanent_thread_count() - 1; i++) {
|
||||
osRecvMesg(rdram, quicksave_enter_mq, NULLPTR, OS_MESG_BLOCK);
|
||||
}
|
||||
|
||||
// Allow any temporary threads to complete by lowering this thread's priority to 0.
|
||||
// TODO this won't cause all temporary threads to complete if any are blocked by permanent threads
|
||||
// or events like timers. Situations like that will need to be handed on a case-by-case basis for a given game.
|
||||
if (ultramodern::temporary_thread_count() != 0) {
|
||||
OSPri old_pri = osGetThreadPri(rdram, NULLPTR);
|
||||
osSetThreadPri(rdram, NULLPTR, 0);
|
||||
|
||||
osSetThreadPri(rdram, NULLPTR, old_pri);
|
||||
}
|
||||
|
||||
if (action == QuicksaveAction::Save) {
|
||||
std::copy(rdram, rdram + ultramodern::rdram_size, saved_rdram);
|
||||
}
|
||||
else if (action == QuicksaveAction::Load) {
|
||||
std::copy(saved_rdram, saved_rdram + ultramodern::rdram_size, rdram);
|
||||
}
|
||||
else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
printf("Quicksave action complete\n");
|
||||
|
||||
cur_quicksave_action.store(QuicksaveAction::None);
|
||||
|
||||
// Tell all other permanent threads that they're good to continue.
|
||||
for (uint32_t i = 0; i < ultramodern::permanent_thread_count() - 1; i++) {
|
||||
osSendMesg(rdram, quicksave_exit_mq, NULLPTR, OS_MESG_BLOCK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -175,9 +175,9 @@ EXPORT extern "C" void init() {
|
|||
// Load overlays in the first 1MB
|
||||
load_overlays(0x1000, (int32_t)entrypoint, 1024 * 1024);
|
||||
|
||||
// Allocate rdram_buffer (16MB to give room for any extra addressable data used by recomp)
|
||||
rdram_buffer = std::make_unique<uint8_t[]>(16 * 1024 * 1024);
|
||||
std::memset(rdram_buffer.get(), 0, 8 * 1024 * 1024);
|
||||
// Allocate rdram_buffer
|
||||
rdram_buffer = std::make_unique<uint8_t[]>(ultramodern::rdram_size);
|
||||
std::memset(rdram_buffer.get(), 0, ultramodern::rdram_size);
|
||||
|
||||
// Initial 1MB DMA (rom address 0x1000 = physical address 0x10001000)
|
||||
do_rom_read(rdram_buffer.get(), entrypoint, 0x10001000, 0x100000);
|
||||
|
|
|
@ -150,6 +150,9 @@ void scheduler_func() {
|
|||
thread_queue_t running_thread_queue{};
|
||||
OSThread* cur_running_thread = nullptr;
|
||||
|
||||
ultramodern::set_native_thread_name("Scheduler Thread");
|
||||
ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Critical);
|
||||
|
||||
while (true) {
|
||||
OSThread* old_running_thread = cur_running_thread;
|
||||
scheduler_context.action_count.wait(0);
|
||||
|
|
|
@ -114,6 +114,9 @@ void ultramodern::set_native_thread_priority(ThreadPriority pri) {
|
|||
}
|
||||
#endif
|
||||
|
||||
std::atomic_int temporary_threads = 0;
|
||||
std::atomic_int permanent_threads = 0;
|
||||
|
||||
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);
|
||||
|
@ -124,6 +127,14 @@ static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entry
|
|||
ultramodern::set_native_thread_name("Game Thread " + std::to_string(self->id));
|
||||
ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::High);
|
||||
|
||||
// TODO fix these being hardcoded (this is only used for quicksaving)
|
||||
if ((self->id == 2 && self->priority == 5) || self->id == 13) { // slowly, flashrom
|
||||
temporary_threads.fetch_add(1);
|
||||
}
|
||||
else if (self->id != 1 && self->id != 2) { // ignore idle and fault
|
||||
permanent_threads.fetch_add(1);
|
||||
}
|
||||
|
||||
// Set initialized to false to indicate that this thread can be started.
|
||||
self->context->initialized.store(true);
|
||||
self->context->initialized.notify_all();
|
||||
|
@ -145,6 +156,19 @@ static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entry
|
|||
|
||||
// Dispose of this thread after it completes.
|
||||
ultramodern::cleanup_thread(self);
|
||||
|
||||
// TODO fix these being hardcoded (this is only used for quicksaving)
|
||||
if ((self->id == 2 && self->priority == 5) || self->id == 13) { // slowly, flashrom
|
||||
temporary_threads.fetch_sub(1);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ultramodern::permanent_thread_count() {
|
||||
return permanent_threads.load();
|
||||
}
|
||||
|
||||
uint32_t ultramodern::temporary_thread_count() {
|
||||
return temporary_threads.load();
|
||||
}
|
||||
|
||||
extern "C" void osStartThread(RDRAM_ARG PTR(OSThread) t_) {
|
||||
|
|
|
@ -46,6 +46,7 @@ namespace ultramodern {
|
|||
#endif
|
||||
|
||||
// We need a place in rdram to hold the PI handles, so pick an address in extended rdram
|
||||
constexpr uint32_t rdram_size = 1024 * 1024 * 16; // 16MB to give extra room for anything custom
|
||||
constexpr int32_t cart_handle = 0x80800000;
|
||||
constexpr int32_t flash_handle = (int32_t)(cart_handle + sizeof(OSPiHandle));
|
||||
constexpr uint32_t save_size = 1024 * 1024 / 8; // Maximum save size, 1Mbit for flash
|
||||
|
@ -68,6 +69,8 @@ void pause_self(RDRAM_ARG1);
|
|||
void halt_self(RDRAM_ARG1);
|
||||
void stop_thread(OSThread *t);
|
||||
void cleanup_thread(OSThread *t);
|
||||
uint32_t permanent_thread_count();
|
||||
uint32_t temporary_thread_count();
|
||||
|
||||
enum class ThreadPriority {
|
||||
Low,
|
||||
|
|
Loading…
Reference in New Issue