diff --git a/README.md b/README.md index 0afc7b4..6e43630 100644 --- a/README.md +++ b/README.md @@ -55,9 +55,9 @@ ## 5. Software HAL & FreeRTOS Architecture * **Hardware Abstraction (I/O Callbacks):** * Core engine defines function pointer contracts: `display_flush`, `audio_output`, `sd_read_sector`, and `cartridge_status` (with `slot_idx` support for multi-slot setups). - * System initialized via `nova64_init_io()`. + * System initialized via `nova64_init()`. ---- +--- ## 6. Display, Framebuffer & Core Allocation Strategy * **Dual-Core Processor Allocation (ESP32-P4):** diff --git a/core/include/nova64_core.h b/core/include/nova64_core.h index 58bf598..582bd11 100644 --- a/core/include/nova64_core.h +++ b/core/include/nova64_core.h @@ -61,6 +61,12 @@ typedef bool (*nova64_sd_read_sector_cb_t)(uint32_t sector_idx, typedef void (*nova64_cartridge_status_cb_t)(uint32_t slot_idx, bool is_inserted); +/** + * @brief Callback for core log output. + * @param message Null-terminated UTF-8 log string. + */ +typedef void (*nova64_log_cb_t)(const char *message); + /* ========================================================================== */ /* I/O Interface Configuration Struct */ /* ========================================================================== */ @@ -74,6 +80,7 @@ typedef struct { nova64_audio_output_cb_t audio_output; nova64_sd_read_sector_cb_t sd_read_sector; nova64_cartridge_status_cb_t cartridge_status; + nova64_log_cb_t log_message; } nova64_io_interface_t; /* ========================================================================== */ @@ -81,21 +88,26 @@ typedef struct { /* ========================================================================== */ /** - * @brief Initializes the core engine and registers host I/O interface - * callbacks. + * @brief Initializes the core engine, registers host I/O interface callbacks, + * and starts the built-in launcher from internal storage in the WASM runtime. * @param io_interface Pointer to filled struct containing I/O function * pointers. * @return True if initialized successfully, false if already initialized or * invalid. */ -NOVA64_API bool nova64_init_io(const nova64_io_interface_t *io_interface); +NOVA64_API bool nova64_init(const nova64_io_interface_t *io_interface); /** - * @brief Executes one engine processing tick. - * @param delta_ms Elapsed time in milliseconds since the last tick. + * @brief Starts the main engine runtime and launches the FreeRTOS scheduler. * @return 0 on success, negative error code on failure. */ -NOVA64_API int32_t nova64_process_tick(uint32_t delta_ms); +NOVA64_API int32_t nova64_main(); + +/** + * @brief Requests shutdown of the FreeRTOS scheduler. + * @return True if the shutdown request was issued, false otherwise. + */ +NOVA64_API bool nova64_shutdown(void); /** * @brief Retrieves the engine version integer. diff --git a/core/src/nova64_core.c b/core/src/nova64_core.c index 4e56401..151696c 100644 --- a/core/src/nova64_core.c +++ b/core/src/nova64_core.c @@ -2,6 +2,7 @@ #include "FreeRTOS.h" #include "queue.h" #include "task.h" +#include #include #if defined(ESP_PLATFORM) @@ -19,23 +20,179 @@ #endif static bool is_initialized = false; +static nova64_io_interface_t g_io_interface_storage; +static const nova64_io_interface_t *g_io_interface = NULL; static QueueHandle_t event_queue = NULL; +static TaskHandle_t boot_task_handle = NULL; +static TaskHandle_t launcher_task_handle = NULL; +static TaskHandle_t shutdown_task_handle = NULL; +static volatile bool shutdown_requested = false; +static const char *const kInternalLauncherPath = "SYS:/launcher.wasm"; -NOVA64_API bool nova64_init_io(const nova64_io_interface_t *io_interface) { - printf("[Nova64 Core] System Initializing...\n"); +static void nova64_log(const char *format, ...) { + char buffer[512]; + va_list args; + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + + if (g_io_interface && g_io_interface->log_message) { + g_io_interface->log_message(buffer); + } else { + fputs(buffer, stdout); + } +} + +static bool initialize_wasm_runtime(void) { + nova64_log("[Core] Initializing WASM runtime...\n"); + // TODO: Replace this stub with real WAMR initialization. + return true; +} + +static bool load_launcher_from_internal_storage(void) { + nova64_log("[Core] Loading launcher from internal storage (%s)...\n", + kInternalLauncherPath); + // TODO: Implement the actual internal storage filesystem and launcher load. + return true; +} + +static void nova64_wasm_launcher_task(void *params) { + (void)params; + nova64_log("[Core] WASM launcher task started.\n"); + + // TODO: Execute the loaded WASM launcher module here with the WAMR engine. + for (;;) { + vTaskDelay(pdMS_TO_TICKS(1000)); + } +} + +static bool start_launcher_task(void) { + BaseType_t result = xTaskCreate(nova64_wasm_launcher_task, "Nova64Launcher", + 4096 / sizeof(StackType_t), NULL, + tskIDLE_PRIORITY + 1, &launcher_task_handle); + + if (result != pdPASS) { + nova64_log("[Core] Failed to create launcher task.\n"); + return false; + } + + nova64_log("[Core] Launcher task created successfully.\n"); + return true; +} + +static void nova64_shutdown_task(void *params) { + (void)params; + + nova64_log("[Core] Shutdown task started.\n"); + for (;;) { + if (shutdown_requested) { + nova64_log("[Core] Shutdown requested; ending scheduler.\n"); + vTaskEndScheduler(); + return; + } + vTaskDelay(pdMS_TO_TICKS(100)); + } +} + +static bool start_shutdown_task(void) { + BaseType_t result = xTaskCreate(nova64_shutdown_task, "Nova64Shutdown", + 2048 / sizeof(StackType_t), NULL, + tskIDLE_PRIORITY + 2, &shutdown_task_handle); + + if (result != pdPASS) { + nova64_log("[Core] Failed to create shutdown task.\n"); + return false; + } + + nova64_log("[Core] Shutdown task created successfully.\n"); + return true; +} + +static void nova64_boot_task(void *params) { + (void)params; + nova64_log("[Core] Boot task started.\n"); + + if (!initialize_wasm_runtime()) { + nova64_log("[Core] WASM runtime initialization failed.\n"); + vTaskDelete(NULL); + return; + } + + if (!load_launcher_from_internal_storage()) { + nova64_log("[Core] Launcher loading failed.\n"); + vTaskDelete(NULL); + return; + } + + if (!start_launcher_task()) { + nova64_log("[Core] Launcher startup failed.\n"); + vTaskDelete(NULL); + return; + } + + nova64_log("[Core] Boot process completed.\n"); + vTaskDelete(NULL); +} + +NOVA64_API bool nova64_init(const nova64_io_interface_t *io_interface) { + if (is_initialized || io_interface == NULL) { + return false; + } + + g_io_interface_storage = *io_interface; + g_io_interface = &g_io_interface_storage; + shutdown_requested = false; + + nova64_log("[Core] System Initializing...\n"); event_queue = xQueueCreate(10, sizeof(uint32_t)); + if (event_queue == NULL) { + nova64_log("[Core] Failed to create event queue.\n"); + return false; + } + + if (!start_shutdown_task()) { + return false; + } + + nova64_log("[Core] Starting boot process...\n"); + BaseType_t created = + xTaskCreate(nova64_boot_task, "Nova64Boot", 4096 / sizeof(StackType_t), + NULL, tskIDLE_PRIORITY + 2, &boot_task_handle); + if (created != pdPASS) { + nova64_log("[Core] Failed to create boot task.\n"); + return false; + } is_initialized = true; return true; } -NOVA64_API int32_t nova64_process_tick(uint32_t delta_ms) { - if (!is_initialized) +NOVA64_API int32_t nova64_main() { + if (!is_initialized) { return -1; - // Core Game-Logic & State Machine Ticks - return 0; // OK + } + + nova64_log( + "[Core] Entering main runtime and starting FreeRTOS scheduler...\n"); + vTaskStartScheduler(); + + // vTaskStartScheduler only returns if the scheduler fails to start or if + // the scheduler is shut down by vTaskEndScheduler(). + nova64_log("[Core] vTaskStartScheduler returned.\n"); + is_initialized = false; + return -1; +} + +NOVA64_API bool nova64_shutdown(void) { + if (!is_initialized) { + return false; + } + + nova64_log("[Core] Scheduler shutdown requested from host.\n"); + shutdown_requested = true; + return true; } NOVA64_API uint32_t nova64_get_version(void) { diff --git a/simulator/Nova64Simulator.csproj b/simulator/Nova64Simulator.csproj index 146ae56..ac07e93 100644 --- a/simulator/Nova64Simulator.csproj +++ b/simulator/Nova64Simulator.csproj @@ -12,19 +12,10 @@ $(NativeLibDir) - - - - + + + + - - - - - - - - - diff --git a/simulator/Program.cs b/simulator/Program.cs index 662da8b..376b2b6 100644 --- a/simulator/Program.cs +++ b/simulator/Program.cs @@ -1,15 +1,44 @@ using System; using System.Runtime.InteropServices; +using System.Threading; internal static class Nova64Core { private const string LibName = "nova64_core"; - [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - public static extern bool nova64_init_io(IntPtr io_interface); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void DisplayFlushDelegate(IntPtr buffer, ushort width, ushort height); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void AudioOutputDelegate(IntPtr samples, uint count); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool SdReadSectorDelegate(uint sectorIdx, IntPtr destinationBuffer, uint sectorCount); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void CartridgeStatusDelegate(uint slotIdx, bool isInserted); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + public delegate void LogMessageDelegate([MarshalAs(UnmanagedType.LPStr)] string message); + + [StructLayout(LayoutKind.Sequential)] + public struct Nova64IoInterface + { + public IntPtr display_flush; + public IntPtr audio_output; + public IntPtr sd_read_sector; + public IntPtr cartridge_status; + public IntPtr log_message; + } [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] - public static extern int nova64_process_tick(uint delta_ms); + public static extern bool nova64_init(IntPtr io_interface); + + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] + public static extern int nova64_main(); + + [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] + public static extern bool nova64_shutdown(); [DllImport(LibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint nova64_get_version(); @@ -17,6 +46,40 @@ internal static class Nova64Core internal static class Program { + private static readonly Nova64Core.DisplayFlushDelegate DisplayFlush = (buffer, width, height) => { }; + private static readonly Nova64Core.AudioOutputDelegate AudioOutput = (samples, count) => { }; + private static readonly Nova64Core.SdReadSectorDelegate SdReadSector = (sectorIdx, destinationBuffer, sectorCount) => false; + private static readonly Nova64Core.CartridgeStatusDelegate CartridgeStatus = (slotIdx, isInserted) => { }; + private static readonly Nova64Core.LogMessageDelegate LogMessage = (message) => + { + Console.Write($"[Nova64] {message}"); + }; + + private static GCHandle? _ioHandle; + private static Thread? _coreThread; + private static readonly ManualResetEventSlim _coreStartedEvent = new(false); + + private static void CoreThreadProc() + { + if (_ioHandle == null) + { + Console.WriteLine("[Nova64] Core thread failed: IO handle missing."); + return; + } + + if (!Nova64Core.nova64_init(_ioHandle.Value.AddrOfPinnedObject())) + { + Console.WriteLine("[Nova64] nova64_init failed in core thread."); + _coreStartedEvent.Set(); + return; + } + + _coreStartedEvent.Set(); + + var result = Nova64Core.nova64_main(); + Console.WriteLine($"[Nova64] nova64_main returned: {result}"); + } + private static void Main() { Console.WriteLine("Nova64 simulator starting..."); @@ -25,6 +88,51 @@ internal static class Program { var version = Nova64Core.nova64_get_version(); Console.WriteLine($"Loaded nova64_core native library. Version: 0x{version:X}"); + + var ioInterface = new Nova64Core.Nova64IoInterface + { + display_flush = Marshal.GetFunctionPointerForDelegate(DisplayFlush), + audio_output = Marshal.GetFunctionPointerForDelegate(AudioOutput), + sd_read_sector = Marshal.GetFunctionPointerForDelegate(SdReadSector), + cartridge_status = Marshal.GetFunctionPointerForDelegate(CartridgeStatus), + log_message = Marshal.GetFunctionPointerForDelegate(LogMessage) + }; + + _ioHandle = GCHandle.Alloc(ioInterface, GCHandleType.Pinned); + _coreThread = new Thread(CoreThreadProc) + { + IsBackground = true, + Name = "Nova64CoreThread" + }; + _coreThread.Start(); + + if (_coreStartedEvent.Wait(TimeSpan.FromSeconds(5))) + { + Console.WriteLine("nova64 core thread started."); + } + else + { + Console.WriteLine("nova64 core thread did not signal startup in time."); + } + + Console.WriteLine("Press Enter to stop the simulator."); + Console.ReadLine(); + + if (_coreThread != null && _coreThread.IsAlive) + { + Console.WriteLine("[Nova64] Requesting core shutdown..."); + if (Nova64Core.nova64_shutdown()) + { + if (!_coreThread.Join(TimeSpan.FromSeconds(5))) + { + Console.WriteLine("[Nova64] Core thread did not exit in time."); + } + } + else + { + Console.WriteLine("[Nova64] Core shutdown request failed."); + } + } } catch (DllNotFoundException ex) { @@ -38,5 +146,16 @@ internal static class Program { Console.WriteLine($"Failed to initialize native library: {ex.Message}"); } + finally + { + if (_coreThread != null && _coreThread.IsAlive) + { + Console.WriteLine("[Nova64] Core thread still running; leaving native handle pinned until process exit."); + } + else if (_ioHandle.HasValue && _ioHandle.Value.IsAllocated) + { + _ioHandle.Value.Free(); + } + } } }