Implement file system API and add shutdown functionality for desktop builds
This commit is contained in:
@@ -31,6 +31,7 @@ else()
|
|||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
|
|
||||||
message(STATUS "[Nova64] Building as Desktop Shared Library...")
|
message(STATUS "[Nova64] Building as Desktop Shared Library...")
|
||||||
|
add_compile_definitions(NOVA64_SHUTDOWN_AVAILABLE) # Enable shutdown functionality for desktop builds
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# FreeRTOS Configuration
|
# FreeRTOS Configuration
|
||||||
|
|||||||
+27
-12
@@ -1,9 +1,10 @@
|
|||||||
#ifndef NOVA64_CORE_H
|
#ifndef NOVA64_CORE_H
|
||||||
#define NOVA64_CORE_H
|
#define NOVA64_CORE_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
// Cross-platform DLL export/import visibility macros
|
// Cross-platform DLL export/import visibility macros
|
||||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||||
@@ -42,16 +43,27 @@ typedef void (*nova64_audio_output_cb_t)(const int16_t *samples,
|
|||||||
uint32_t count);
|
uint32_t count);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback to read sectors from the SD card.
|
* @brief Callback to read data from the SD card.
|
||||||
* @param sector_idx The zero-based sector index to read.
|
* @param slot_idx Zero-based cartridge slot index.
|
||||||
* @param destination_buffer Pointer to memory where sector data should be
|
* @param file_path Null-terminated path to the file to read.
|
||||||
* copied.
|
* @param destination_buffer Pointer to memory where file data should be copied.
|
||||||
* @param sector_count Number of 512-byte sectors to read.
|
* @param destination_length Maximum number of bytes available in the
|
||||||
* @return True on success, false on read error.
|
* destination buffer.
|
||||||
|
* @return Number of bytes read on success, negative value on read error.
|
||||||
*/
|
*/
|
||||||
typedef bool (*nova64_sd_read_sector_cb_t)(uint32_t sector_idx,
|
typedef int32_t (*nova64_sd_read_file_cb_t)(uint32_t slot_idx,
|
||||||
uint8_t *destination_buffer,
|
const char *file_path,
|
||||||
uint32_t sector_count);
|
uint8_t *destination_buffer,
|
||||||
|
uint32_t destination_length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback to retrieve the size of a file on the SD card.
|
||||||
|
* @param slot_idx Zero-based cartridge slot index.
|
||||||
|
* @param file_path Null-terminated path to the file.
|
||||||
|
* @return File size in bytes on success, negative value on error.
|
||||||
|
*/
|
||||||
|
typedef int32_t (*nova64_sd_get_file_size_cb_t)(uint32_t slot_idx,
|
||||||
|
const char *file_path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback triggered when a cartridge state change occurs (e.g.
|
* @brief Callback triggered when a cartridge state change occurs (e.g.
|
||||||
@@ -60,7 +72,7 @@ typedef bool (*nova64_sd_read_sector_cb_t)(uint32_t sector_idx,
|
|||||||
* @param is_inserted True if a cartridge is detected, false if ejected.
|
* @param is_inserted True if a cartridge is detected, false if ejected.
|
||||||
*/
|
*/
|
||||||
typedef void (*nova64_cartridge_status_cb_t)(uint32_t slot_idx,
|
typedef void (*nova64_cartridge_status_cb_t)(uint32_t slot_idx,
|
||||||
bool is_inserted);
|
bool is_inserted);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback for core log output.
|
* @brief Callback for core log output.
|
||||||
@@ -79,7 +91,8 @@ typedef void (*nova64_log_cb_t)(const char *message);
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
nova64_display_flush_cb_t display_flush;
|
nova64_display_flush_cb_t display_flush;
|
||||||
nova64_audio_output_cb_t audio_output;
|
nova64_audio_output_cb_t audio_output;
|
||||||
nova64_sd_read_sector_cb_t sd_read_sector;
|
nova64_sd_read_file_cb_t sd_read_file;
|
||||||
|
nova64_sd_get_file_size_cb_t sd_get_file_size;
|
||||||
nova64_cartridge_status_cb_t cartridge_status;
|
nova64_cartridge_status_cb_t cartridge_status;
|
||||||
nova64_log_cb_t log_message;
|
nova64_log_cb_t log_message;
|
||||||
} nova64_io_interface_t;
|
} nova64_io_interface_t;
|
||||||
@@ -104,11 +117,13 @@ NOVA64_API bool nova64_init(const nova64_io_interface_t *io_interface);
|
|||||||
*/
|
*/
|
||||||
NOVA64_API int32_t nova64_main();
|
NOVA64_API int32_t nova64_main();
|
||||||
|
|
||||||
|
#if defined(NOVA64_SHUTDOWN_AVAILABLE)
|
||||||
/**
|
/**
|
||||||
* @brief Requests shutdown of the FreeRTOS scheduler.
|
* @brief Requests shutdown of the FreeRTOS scheduler.
|
||||||
* @return True if the shutdown request was issued, false otherwise.
|
* @return True if the shutdown request was issued, false otherwise.
|
||||||
*/
|
*/
|
||||||
NOVA64_API bool nova64_shutdown(void);
|
NOVA64_API bool nova64_shutdown(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieves the engine version integer.
|
* @brief Retrieves the engine version integer.
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#ifndef NOVA64_FS_H
|
||||||
|
#define NOVA64_FS_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* ========================================================================== */
|
||||||
|
/* File System API */
|
||||||
|
/* ========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads a file from the filesystem into a buffer.
|
||||||
|
* Supports drive identifiers: "SYS:" (internal system store),
|
||||||
|
* "EXA:" (cartridge slot 0), "EXB:" (cartridge slot 1), etc.
|
||||||
|
* @param file_path Null-terminated file path with drive identifier
|
||||||
|
* (e.g., "SYS:/init.wasm" or "EXA:/game.wasm").
|
||||||
|
* @param buffer Pointer to destination buffer where file contents will be
|
||||||
|
* copied.
|
||||||
|
* @param buffer_size Size in bytes of the destination buffer.
|
||||||
|
* @return True if file was successfully read into buffer, false on error
|
||||||
|
* (file not found, read error, or buffer too small).
|
||||||
|
* @note Uses the sd_read_sector callback from nova64_io_interface_t.
|
||||||
|
* The callback must be registered via nova64_init() before calling
|
||||||
|
* this function.
|
||||||
|
*/
|
||||||
|
bool nova64_read_file_to_buffer(const char *file_path, uint8_t *buffer,
|
||||||
|
size_t buffer_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the size of a file in the filesystem.
|
||||||
|
* @param file_path Null-terminated file path with drive identifier
|
||||||
|
* (e.g., "SYS:/init.wasm" or "EXA:/game.wasm").
|
||||||
|
* @return Size of the file in bytes on success, or -1 on error
|
||||||
|
* (file not found, read error, etc.).
|
||||||
|
*/
|
||||||
|
int32_t nova64_get_file_size(const char *file_path);
|
||||||
|
|
||||||
|
#endif // NOVA64_FS_H
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
#ifndef NOVA64_INTERNAL_H
|
#ifndef NOVA64_INTERNAL_H
|
||||||
#define NOVA64_INTERNAL_H
|
#define NOVA64_INTERNAL_H
|
||||||
|
|
||||||
|
#include "nova64_core.h"
|
||||||
|
|
||||||
void nova64_log(const char *format, ...);
|
void nova64_log(const char *format, ...);
|
||||||
|
|
||||||
|
const nova64_io_interface_t *nova64_get_io_interface(void);
|
||||||
|
|
||||||
#endif // NOVA64_INTERNAL_H
|
#endif // NOVA64_INTERNAL_H
|
||||||
@@ -4,12 +4,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
wasm_runner_role_main = 0,
|
|
||||||
wasm_runner_role_status,
|
|
||||||
wasm_runner_role_background,
|
|
||||||
} wasm_runner_role_t;
|
|
||||||
|
|
||||||
typedef struct wasm_runner_instance wasm_runner_instance_t;
|
typedef struct wasm_runner_instance wasm_runner_instance_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
+17
-2
@@ -1,13 +1,12 @@
|
|||||||
#include "nova64_core.h"
|
#include "nova64_core.h"
|
||||||
#include "nova64_internal.h"
|
|
||||||
#include "FreeRTOS.h"
|
#include "FreeRTOS.h"
|
||||||
|
#include "nova64_internal.h"
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
#include "wasm_runner.h"
|
#include "wasm_runner.h"
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
#if defined(ESP_PLATFORM)
|
#if defined(ESP_PLATFORM)
|
||||||
#include "esp_pm.h"
|
#include "esp_pm.h"
|
||||||
#include "esp_rom_sys.h"
|
#include "esp_rom_sys.h"
|
||||||
@@ -26,8 +25,11 @@ static const nova64_io_interface_t *g_io_interface = NULL;
|
|||||||
static QueueHandle_t event_queue = NULL;
|
static QueueHandle_t event_queue = NULL;
|
||||||
static TaskHandle_t boot_task_handle = NULL;
|
static TaskHandle_t boot_task_handle = NULL;
|
||||||
static TaskHandle_t launcher_task_handle = NULL;
|
static TaskHandle_t launcher_task_handle = NULL;
|
||||||
|
|
||||||
|
#if defined(NOVA64_SHUTDOWN_AVAILABLE)
|
||||||
static TaskHandle_t shutdown_task_handle = NULL;
|
static TaskHandle_t shutdown_task_handle = NULL;
|
||||||
static volatile bool shutdown_requested = false;
|
static volatile bool shutdown_requested = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
static const char *const kInternalLauncherPath = "SYS:/launcher.wasm";
|
static const char *const kInternalLauncherPath = "SYS:/launcher.wasm";
|
||||||
|
|
||||||
@@ -83,6 +85,7 @@ static bool start_launcher_task(void) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(NOVA64_SHUTDOWN_AVAILABLE)
|
||||||
static void nova64_shutdown_task(void *params) {
|
static void nova64_shutdown_task(void *params) {
|
||||||
(void)params;
|
(void)params;
|
||||||
|
|
||||||
@@ -110,6 +113,7 @@ static bool start_shutdown_task(void) {
|
|||||||
nova64_log("Shutdown task created successfully.\n");
|
nova64_log("Shutdown task created successfully.\n");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void nova64_boot_task(void *params) {
|
static void nova64_boot_task(void *params) {
|
||||||
(void)params;
|
(void)params;
|
||||||
@@ -137,6 +141,10 @@ static void nova64_boot_task(void *params) {
|
|||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nova64_io_interface_t *nova64_get_io_interface(void) {
|
||||||
|
return g_io_interface;
|
||||||
|
}
|
||||||
|
|
||||||
NOVA64_API bool nova64_init(const nova64_io_interface_t *io_interface) {
|
NOVA64_API bool nova64_init(const nova64_io_interface_t *io_interface) {
|
||||||
if (is_initialized || io_interface == NULL) {
|
if (is_initialized || io_interface == NULL) {
|
||||||
return false;
|
return false;
|
||||||
@@ -144,7 +152,10 @@ NOVA64_API bool nova64_init(const nova64_io_interface_t *io_interface) {
|
|||||||
|
|
||||||
g_io_interface_storage = *io_interface;
|
g_io_interface_storage = *io_interface;
|
||||||
g_io_interface = &g_io_interface_storage;
|
g_io_interface = &g_io_interface_storage;
|
||||||
|
|
||||||
|
#if defined(NOVA64_SHUTDOWN_AVAILABLE)
|
||||||
shutdown_requested = false;
|
shutdown_requested = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
nova64_log("System Initializing...\n");
|
nova64_log("System Initializing...\n");
|
||||||
|
|
||||||
@@ -154,9 +165,11 @@ NOVA64_API bool nova64_init(const nova64_io_interface_t *io_interface) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(NOVA64_SHUTDOWN_AVAILABLE)
|
||||||
if (!start_shutdown_task()) {
|
if (!start_shutdown_task()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
nova64_log("Starting boot process...\n");
|
nova64_log("Starting boot process...\n");
|
||||||
BaseType_t created =
|
BaseType_t created =
|
||||||
@@ -186,6 +199,7 @@ NOVA64_API int32_t nova64_main() {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(NOVA64_SHUTDOWN_AVAILABLE)
|
||||||
NOVA64_API bool nova64_shutdown(void) {
|
NOVA64_API bool nova64_shutdown(void) {
|
||||||
if (!is_initialized) {
|
if (!is_initialized) {
|
||||||
return false;
|
return false;
|
||||||
@@ -195,6 +209,7 @@ NOVA64_API bool nova64_shutdown(void) {
|
|||||||
shutdown_requested = true;
|
shutdown_requested = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
NOVA64_API uint32_t nova64_get_version(void) {
|
NOVA64_API uint32_t nova64_get_version(void) {
|
||||||
return 0x010000; // v1.0.0
|
return 0x010000; // v1.0.0
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "nova64_fs.h"
|
||||||
|
#include "nova64_core.h"
|
||||||
|
#include "nova64_internal.h"
|
||||||
|
|
||||||
|
/* Helper for parsing the drive prefix from a path like "SYS:/file" or "EXA:/file".
|
||||||
|
* Returns the drive index on success, or -1 on failure. */
|
||||||
|
static int parse_drive_index(const char *path)
|
||||||
|
{
|
||||||
|
if (!path || path[0] == '\0') {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(path, "SYS:/", 5) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(path, "EX", 2) == 0 && path[3] == ':' && path[4] == '/') {
|
||||||
|
char drive_letter = path[2];
|
||||||
|
if (drive_letter >= 'A' && drive_letter <= 'Z') {
|
||||||
|
return drive_letter - ('A' - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nova64_read_file_to_buffer(const char *file_path,
|
||||||
|
uint8_t *buffer,
|
||||||
|
size_t buffer_size)
|
||||||
|
{
|
||||||
|
if (!file_path || !buffer || buffer_size == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int drive_index = parse_drive_index(file_path);
|
||||||
|
if (drive_index < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *relative_path = file_path + 5;
|
||||||
|
if (*relative_path == '\0') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nova64_io_interface_t *io = nova64_get_io_interface();
|
||||||
|
if (!io || !io->sd_read_file || !io->sd_get_file_size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the file size first */
|
||||||
|
int32_t file_size = io->sd_get_file_size(drive_index, relative_path);
|
||||||
|
if (file_size < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine how much to read */
|
||||||
|
size_t bytes_to_read = (size_t)file_size;
|
||||||
|
if (bytes_to_read > buffer_size) {
|
||||||
|
bytes_to_read = buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the entire file into the buffer */
|
||||||
|
int32_t bytes_read = io->sd_read_file(drive_index, relative_path, buffer, bytes_to_read);
|
||||||
|
if (bytes_read < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes_read > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32_t nova64_get_file_size(const char *file_path)
|
||||||
|
{
|
||||||
|
if (!file_path) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int drive_index = parse_drive_index(file_path);
|
||||||
|
if (drive_index < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *relative_path = file_path + 5;
|
||||||
|
if (*relative_path == '\0') {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nova64_io_interface_t *io = nova64_get_io_interface();
|
||||||
|
if (!io || !io->sd_get_file_size) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return io->sd_get_file_size(drive_index, relative_path);
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "wasm_runner.h"
|
#include "wasm_runner.h"
|
||||||
|
#include "nova64_fs.h"
|
||||||
#include "nova64_internal.h"
|
#include "nova64_internal.h"
|
||||||
#include "wasm_export.h"
|
#include "wasm_export.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
struct wasm_runner_instance {
|
struct wasm_runner_instance {
|
||||||
void *native_instance;
|
void *native_instance;
|
||||||
@@ -9,8 +11,40 @@ struct wasm_runner_instance {
|
|||||||
wasm_runner_instance_t *next;
|
wasm_runner_instance_t *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
char error_buf[128];
|
||||||
|
uint32_t stack_size = 8092, heap_size = 8092;
|
||||||
|
|
||||||
bool wasm_runner_initialize(void) {
|
bool wasm_runner_initialize(void) {
|
||||||
|
|
||||||
wasm_runtime_init();
|
wasm_runtime_init();
|
||||||
nova64_log("WASM runtime initialized successfully.\n");
|
nova64_log("WASM runtime initialized successfully.\n");
|
||||||
|
|
||||||
|
const char *module_file = "SYS:/init.wasm";
|
||||||
|
int32_t file_size = nova64_get_file_size(module_file);
|
||||||
|
if (file_size <= 0) {
|
||||||
|
nova64_log("Failed to determine WASM module size.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *buffer = malloc((size_t)file_size);
|
||||||
|
if (!buffer) {
|
||||||
|
nova64_log("Failed to allocate WASM module buffer.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nova64_read_file_to_buffer(module_file, buffer, (size_t)file_size)) {
|
||||||
|
nova64_log("Failed to read WASM module into buffer.\n");
|
||||||
|
free(buffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
wasm_module_t module = wasm_runtime_load(buffer, (uint32_t)file_size,
|
||||||
|
error_buf, sizeof(error_buf));
|
||||||
|
|
||||||
|
wasm_module_inst_t module_inst = wasm_runtime_instantiate(
|
||||||
|
module, stack_size, heap_size, error_buf, sizeof(error_buf));
|
||||||
|
|
||||||
|
wasm_exec_env_t exec_env = wasm_runtime_create_exec_env(module_inst, stack_size);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,11 @@ internal static class Nova64Core
|
|||||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
public delegate void AudioOutputDelegate(IntPtr samples, uint count);
|
public delegate void AudioOutputDelegate(IntPtr samples, uint count);
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||||
public delegate bool SdReadSectorDelegate(uint sectorIdx, IntPtr destinationBuffer, uint sectorCount);
|
public delegate int SdReadFileDelegate(uint slotIdx, string filePath, IntPtr destinationBuffer, uint destinationLength);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||||
|
public delegate int SdGetFileSizeDelegate(uint slotIdx, string filePath);
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
public delegate void CartridgeStatusDelegate(uint slotIdx, bool isInserted);
|
public delegate void CartridgeStatusDelegate(uint slotIdx, bool isInserted);
|
||||||
@@ -24,7 +27,8 @@ internal static class Nova64Core
|
|||||||
{
|
{
|
||||||
public IntPtr display_flush;
|
public IntPtr display_flush;
|
||||||
public IntPtr audio_output;
|
public IntPtr audio_output;
|
||||||
public IntPtr sd_read_sector;
|
public IntPtr sd_read_file;
|
||||||
|
public IntPtr sd_get_file_size;
|
||||||
public IntPtr cartridge_status;
|
public IntPtr cartridge_status;
|
||||||
public IntPtr log_message;
|
public IntPtr log_message;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,14 @@
|
|||||||
<NativeLibDir>$(NativeLibDir)</NativeLibDir>
|
<NativeLibDir>$(NativeLibDir)</NativeLibDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Zio" Version="0.24.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="sd\**\*" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(NativeLibDir)' != ''">
|
<ItemGroup Condition="'$(NativeLibDir)' != ''">
|
||||||
<Content Include="$(NativeLibDir)\nova64_core.dll" Condition="Exists('$(NativeLibDir)\nova64_core.dll')" CopyToOutputDirectory="IfDifferent" />
|
<Content Include="$(NativeLibDir)\nova64_core.dll" Condition="Exists('$(NativeLibDir)\nova64_core.dll')" CopyToOutputDirectory="IfDifferent" />
|
||||||
<Content Include="$(NativeLibDir)\libnova64_core.so" Condition="Exists('$(NativeLibDir)\libnova64_core.so')" CopyToOutputDirectory="IfDifferent" />
|
<Content Include="$(NativeLibDir)\libnova64_core.so" Condition="Exists('$(NativeLibDir)\libnova64_core.so')" CopyToOutputDirectory="IfDifferent" />
|
||||||
|
|||||||
+68
-2
@@ -1,12 +1,77 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using Zio;
|
||||||
|
using Zio.FileSystems;
|
||||||
|
|
||||||
internal static class Program
|
internal static class Program
|
||||||
{
|
{
|
||||||
|
private static readonly IFileSystem SdFileSystem = new PhysicalFileSystem();
|
||||||
|
private static readonly UPath SdRoot = new UPath(Path.Combine(AppContext.BaseDirectory, "sd").Replace('\\', '/').Replace("C:", "/mnt/c"));
|
||||||
|
|
||||||
|
private static UPath GetSlotFilePath(uint slotIdx, string filePath)
|
||||||
|
{
|
||||||
|
var normalizedPath = (filePath ?? string.Empty).Replace('\\', '/').TrimStart('/');
|
||||||
|
return SdRoot / $"slot{slotIdx}" / normalizedPath;
|
||||||
|
}
|
||||||
|
|
||||||
private static readonly Nova64Core.DisplayFlushDelegate DisplayFlush = (buffer, width, height) => { };
|
private static readonly Nova64Core.DisplayFlushDelegate DisplayFlush = (buffer, width, height) => { };
|
||||||
private static readonly Nova64Core.AudioOutputDelegate AudioOutput = (samples, count) => { };
|
private static readonly Nova64Core.AudioOutputDelegate AudioOutput = (samples, count) => { };
|
||||||
private static readonly Nova64Core.SdReadSectorDelegate SdReadSector = (sectorIdx, destinationBuffer, sectorCount) => false;
|
private static readonly Nova64Core.SdReadFileDelegate SdReadFile = (slotIdx, filePath, destinationBuffer, destinationLength) =>
|
||||||
|
{
|
||||||
|
var path = GetSlotFilePath(slotIdx, filePath);
|
||||||
|
Console.WriteLine($"[Core] SD read request: slot {slotIdx}, file '{filePath}', resolved path '{path}' destination length {destinationLength}");
|
||||||
|
|
||||||
|
if (destinationBuffer == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Console.WriteLine("[Core] SD read request failed: destination buffer is null.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SdFileSystem.FileExists(path))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[Core] SD read request failed: file not found '{path}'");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var stream = SdFileSystem.OpenFile(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
var bytesToRead = (int)Math.Min(destinationLength, stream.Length);
|
||||||
|
var buffer = new byte[bytesToRead];
|
||||||
|
var bytesRead = stream.Read(buffer, 0, bytesToRead);
|
||||||
|
Marshal.Copy(buffer, 0, destinationBuffer, bytesRead);
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[Core] SD read request failed: {ex.Message}");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private static readonly Nova64Core.SdGetFileSizeDelegate SdGetFileSize = (slotIdx, filePath) =>
|
||||||
|
{
|
||||||
|
var path = GetSlotFilePath(slotIdx, filePath);
|
||||||
|
Console.WriteLine($"[Core] SD get file size request: slot {slotIdx}, file '{filePath}', resolved path '{path}'");
|
||||||
|
|
||||||
|
if (!SdFileSystem.FileExists(path))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[Core] SD get file size failed: file not found '{path}'");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var length = SdFileSystem.GetFileLength(path);
|
||||||
|
return length > int.MaxValue ? -1 : (int)length;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[Core] SD get file size failed: {ex.Message}");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
private static readonly Nova64Core.CartridgeStatusDelegate CartridgeStatus = (slotIdx, isInserted) => { };
|
private static readonly Nova64Core.CartridgeStatusDelegate CartridgeStatus = (slotIdx, isInserted) => { };
|
||||||
private static readonly Nova64Core.LogMessageDelegate LogMessage = (message) =>
|
private static readonly Nova64Core.LogMessageDelegate LogMessage = (message) =>
|
||||||
{
|
{
|
||||||
@@ -51,7 +116,8 @@ internal static class Program
|
|||||||
{
|
{
|
||||||
display_flush = Marshal.GetFunctionPointerForDelegate(DisplayFlush),
|
display_flush = Marshal.GetFunctionPointerForDelegate(DisplayFlush),
|
||||||
audio_output = Marshal.GetFunctionPointerForDelegate(AudioOutput),
|
audio_output = Marshal.GetFunctionPointerForDelegate(AudioOutput),
|
||||||
sd_read_sector = Marshal.GetFunctionPointerForDelegate(SdReadSector),
|
sd_read_file = Marshal.GetFunctionPointerForDelegate(SdReadFile),
|
||||||
|
sd_get_file_size = Marshal.GetFunctionPointerForDelegate(SdGetFileSize),
|
||||||
cartridge_status = Marshal.GetFunctionPointerForDelegate(CartridgeStatus),
|
cartridge_status = Marshal.GetFunctionPointerForDelegate(CartridgeStatus),
|
||||||
log_message = Marshal.GetFunctionPointerForDelegate(LogMessage)
|
log_message = Marshal.GetFunctionPointerForDelegate(LogMessage)
|
||||||
};
|
};
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// Declare a main function, this is the entrypoint into our go module
|
||||||
|
// That will be run. In our example, we won't need this
|
||||||
|
func main() {}
|
||||||
|
|
||||||
|
// This exports an add function.
|
||||||
|
// It takes in two 32-bit integer values
|
||||||
|
// And returns a 32-bit integer value.
|
||||||
|
// To make this function callable from JavaScript,
|
||||||
|
// we need to add the: "export add" comment above the function
|
||||||
|
//export add
|
||||||
|
func add(x int, y int) int {
|
||||||
|
return x + y
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user