diff --git a/art/pack/dragon.kra b/art/pack/dragon.kra index 7d06b6c..b87825d 100644 Binary files a/art/pack/dragon.kra and b/art/pack/dragon.kra differ diff --git a/art/pack/dragon.png b/art/pack/dragon.png new file mode 100644 index 0000000..eb88458 Binary files /dev/null and b/art/pack/dragon.png differ diff --git a/software/CMakeLists.txt b/software/CMakeLists.txt index 90f80f2..aa26966 100644 --- a/software/CMakeLists.txt +++ b/software/CMakeLists.txt @@ -33,12 +33,14 @@ set(OST_SRCS system/sdcard.c system/ff_diskio_sdcard.c system/debug.c - system/picture.c system/filesystem.c system/sdcard.c system/qor.c system/qor_armv6m.s system/audio_player.c + system/vm_task.c + system/hmi_task.c + system/fs_task.c system/ff/ff.c system/ff/ffsystem.c system/ff/ff_stubs.c diff --git a/software/system/picture.c b/software/library/bitmap.c similarity index 100% rename from software/system/picture.c rename to software/library/bitmap.c diff --git a/software/system/picture.h b/software/library/bitmap.h similarity index 100% rename from software/system/picture.h rename to software/library/bitmap.h diff --git a/software/system/filesystem.c b/software/system/filesystem.c index 449777b..fe6e3cc 100644 --- a/software/system/filesystem.c +++ b/software/system/filesystem.c @@ -4,6 +4,19 @@ #include "ost_hal.h" #include "filesystem.h" +#include "mini_qoi.h" + +#ifdef OST_USE_FF_LIBRARY +#include "ff.h" +#include "diskio.h" +typedef FIL file_t; +#else + +// Use standard library +typedef FILE *file_t; +typedef int FRESULT; +#define F_OK +#endif // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h, // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT. @@ -151,3 +164,130 @@ bool filesystem_read_index_file(ost_context_t *ctx) debug_printf("ERROR: index.ost not found\r\n"); } } + +static mqoi_dec_t dec; +static mqoi_desc_t desc; + +static uint8_t bmpImage[256]; + +static color_t line[320]; + +file_t file_open(const char *filename) +{ +#ifdef OST_USE_FF_LIBRARY + file_t fil; + FRESULT fr = f_open(&fil, filename, FA_READ); + if (fr != FR_OK) + { + debug_printf("ERROR: f_open %d\n\r", (int)fr); + } + return fil; +#else + return fopen(filename, "r"); +#endif +} + +void filesystem_display_image(const char *filename) +{ + file_t fil; + unsigned int br; + + fil = file_open(filename); + uint32_t imgW, imgH; + + mqoi_desc_init(&desc); + + // 1. Read header + f_read(&fil, &desc.magic[0], sizeof(mqoi_desc_t) - 1, &br); + + uint8_t errn = mqoi_desc_verify(&desc, &imgW, &imgH); + + if (errn) + { + debug_printf("Invalid image, code %d\n", errn); + return; + } + + debug_printf("Image dimensions: %d, %d\n", imgW, imgH); + + uint32_t start, end, pxCount = 0; + + volatile mqoi_rgba_t *px; + + mqoi_dec_init(&dec, imgW * imgH); + + // Serial.println("starting decode..."); + int index = 256; // force refill first time + int x = 0; + int y = 0; + while (!mqoi_dec_done(&dec)) + { + if (index >= sizeof(bmpImage)) + { + // refill buffer + f_read(&fil, bmpImage, sizeof(bmpImage), &br); + index = 0; + } + + mqoi_dec_push(&dec, bmpImage[index++]); + + while ((px = mqoi_dec_pop(&dec)) != NULL) + { + pxCount++; + + line[x].r = px->r; + line[x].g = px->g; + line[x].b = px->b; + + x++; + if (x >= 320) + { + ost_display_draw_h_line_rgb888(y, line); + x = 0; + y++; + } + } + } + + f_close(&fil); +} + +#define FS_MAX_SIZE_READ 256 + +void filesystem_load_rom(uint8_t *mem, const char *filename) +{ + file_t fil; + FILINFO fno; + unsigned int br; + + FRESULT fr = f_stat(filename, &fno); + + if (fr == FR_OK) + { + int total_size = fno.fsize; + fil = file_open(filename); + int copied_size = 0; + + do + { + int read_size = total_size > FS_MAX_SIZE_READ ? FS_MAX_SIZE_READ : total_size; + + f_read(&fil, &mem[copied_size], read_size, &br); + + if (br == read_size) + { + total_size -= read_size; + copied_size += read_size; + } + else + { + debug_printf("Read file error\n"); + total_size = 0; // force exit + return; + } + + } while (total_size > 0); + + f_close(&fil); + } +} diff --git a/software/system/filesystem.h b/software/system/filesystem.h index 8691cc6..88ff402 100644 --- a/software/system/filesystem.h +++ b/software/system/filesystem.h @@ -11,5 +11,7 @@ typedef struct bool filesystem_read_index_file(ost_context_t *ctx); void filesystem_mount(); +void filesystem_display_image(const char *filename); +void filesystem_load_rom(uint8_t *mem, const char *filename); #endif // FILESYSTEM_H diff --git a/software/system/fs_task.c b/software/system/fs_task.c new file mode 100644 index 0000000..77847f3 --- /dev/null +++ b/software/system/fs_task.c @@ -0,0 +1,192 @@ +/** + * @file fs_task.c + * + * @author your name (you@domain.com) + * @brief + * @version 0.1 + * @date 2023-07-29 + * + * @copyright Copyright (c) 2023 + * + */ + +#include +#include +#include + +#include "ost_hal.h" +#include "debug.h" +#include "qor.h" +#include "audio_player.h" +#include "filesystem.h" +#include "system.h" +#include "vm_task.h" +#include "fs_task.h" + +// =========================================================================================================== +// DEFINITIONS +// =========================================================================================================== +typedef struct +{ + uint8_t ev; +} ost_audio_event_t; + +typedef enum +{ + FS_WAIT_FOR_EVENT, + FS_PLAY_SOUND, + FS_DISPLAY_IMAGE, + FS_LOAD_INDEX, + FS_LOAD_STORY +} fs_state_t; + +typedef struct +{ + fs_state_t ev; + uint8_t *mem; + char *filename; +} ost_fs_event_t; + +// =========================================================================================================== +// PRIVATE GLOBAL VARIABLES +// =========================================================================================================== +static qor_tcb_t FsTcb; +static uint32_t FsStack[4096]; + +static qor_mbox_t AudioMailBox; + +static ost_audio_event_t wake_up; + +static ost_audio_event_t AudioQueue[10]; + +static int dbg_state = 0; + +static fs_state_t FsState = FS_WAIT_FOR_EVENT; + +static qor_mbox_t FsMailBox; + +static ost_fs_event_t FsEventQueue[10]; + +static ost_context_t OstContext; + +// =========================================================================================================== +// FILE SYSTEM TASK +// =========================================================================================================== + +// End of DMA transfer callback +static void audio_callback(void) +{ + dbg_state = 1 - dbg_state; + qor_mbox_notify(&AudioMailBox, (void **)&wake_up, QOR_MBOX_OPTION_SEND_BACK); +} + +void show_duration(uint32_t millisecondes) +{ + uint32_t minutes, secondes, reste; + + // Calcul des minutes, secondes et millisecondes + minutes = millisecondes / (60 * 1000); + reste = millisecondes % (60 * 1000); + secondes = reste / 1000; + reste = reste % 1000; + + // Affichage du temps + debug_printf("Temps : %d minutes, %d secondes, %d millisecondes\r\n", minutes, secondes, reste); +} + +void play_sound_file(const char *filename) +{ + debug_printf("\r\n-------------------------------------------------------\r\nPlaying: out2.wav\r\n"); + ost_system_stopwatch_start(); + ost_audio_play(filename); + + ost_audio_event_t *e = NULL; + + int isPlaying = 0; + int count = 0; + uint32_t res = 0; + do + { + uint32_t res = qor_mbox_wait(&AudioMailBox, (void **)&e, 300); // On devrait recevoir un message toutes les 3ms (durée d'envoi d'un buffer I2S) + + if (res == QOR_MBOX_OK) + { + isPlaying = ost_audio_process(); + } + + count++; + + } while (isPlaying); + + uint32_t executionTime = ost_system_stopwatch_stop(); + ost_audio_stop(); + + debug_printf("\r\nPackets: %d\r\n", count); + show_duration(executionTime); +} + +void FsTask(void *args) +{ + ost_fs_event_t *fs_ev = NULL; + uint32_t res = 0; + while (1) + { + switch (FsState) + { + FS_PLAY_SOUND: + play_sound_file(fs_ev->filename); + FsState = FS_WAIT_FOR_EVENT; + break; + FS_DISPLAY_IMAGE: + filesystem_display_image(fs_ev->filename); + FsState = FS_WAIT_FOR_EVENT; + break; + FS_LOAD_INDEX: + filesystem_read_index_file(&OstContext); + FsState = FS_WAIT_FOR_EVENT; + break; + FS_LOAD_STORY: + filesystem_load_rom(fs_ev->mem, fs_ev->filename); + // ROM loaded, execute story + FsState = FS_WAIT_FOR_EVENT; + break; + + FS_WAIT_FOR_EVENT: + default: + res = qor_mbox_wait(&FsMailBox, (void **)&fs_ev, 1000); + if (res == QOR_MBOX_OK) + { + // valid event, accept it + FsState = fs_ev->ev; + } + break; + } + + // for (;;) + // { + // ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 0); + // qor_sleep(500); + // ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 1); + // qor_sleep(500); + // } + } +} + +void fs_task_scan_index() +{ + static ost_fs_event_t ScanIndexEv = { + .ev = FS_LOAD_INDEX, + .filename = NULL}; + + qor_mbox_notify(&FsMailBox, (void **)&ScanIndexEv, QOR_MBOX_OPTION_SEND_BACK); +} + +void fs_task_initialize() +{ + qor_mbox_init(&AudioMailBox, (void **)&AudioQueue, 10); + qor_mbox_init(&FsMailBox, (void **)&FsEventQueue, 10); + + ost_audio_register_callback(audio_callback); + + qor_create_thread(&FsTcb, FsTask, FsStack, sizeof(FsStack) / sizeof(FsStack[0]), FS_TASK_PRIORITY, "FsTask"); +} diff --git a/software/system/fs_task.h b/software/system/fs_task.h new file mode 100644 index 0000000..8c6dc86 --- /dev/null +++ b/software/system/fs_task.h @@ -0,0 +1,5 @@ +#ifndef FS_TASK_H +#define FS_TASK_H + +void fs_task_initialize(); +#endif // FS_TASK_H diff --git a/software/system/hmi_task.c b/software/system/hmi_task.c new file mode 100644 index 0000000..34953f0 --- /dev/null +++ b/software/system/hmi_task.c @@ -0,0 +1,78 @@ +/** + * @file hmi_task.c + * + * @author your name (you@domain.com) + * @brief + * @version 0.1 + * @date 2023-07-29 + * + * @copyright Copyright (c) 2023 + * + */ + +#include +#include +#include + +#include "ost_hal.h" +#include "debug.h" +#include "qor.h" +#include "audio_player.h" +#include "filesystem.h" +#include "system.h" +#include "vm_task.h" +#include "fs_task.h" + +// =========================================================================================================== +// DEFINITIONS +// =========================================================================================================== +typedef struct +{ + uint8_t ev; +} ost_hmi_event_t; + +// =========================================================================================================== +// GLOBAL STORY VARIABLES +// =========================================================================================================== + +static qor_tcb_t HmiTcb; +static uint32_t HmiStack[4096]; + +static qor_mbox_t HmiMailBox; + +static ost_hmi_event_t HmiEvent; + +static ost_hmi_event_t HmiQueue[10]; + +// =========================================================================================================== +// HMI TASK (user interface, buttons manager, LCD) +// =========================================================================================================== +void HmiTask(void *args) +{ + + ost_hmi_event_t *e = NULL; + + // filesystem_display_image("/ba869e4b-03d6-4249-9202-85b4cec767a7/images/bird.qoi"); + + // Start by scanning the index file + + while (1) + { + uint32_t res = qor_mbox_wait(&HmiMailBox, (void **)&e, 1000); + + if (res == QOR_MBOX_OK) + { + } + else + { + debug_printf("H"); // pour le debug only + } + } +} + +void hmi_task_initialize() +{ + qor_mbox_init(&HmiMailBox, (void **)&HmiQueue, 10); + + qor_create_thread(&HmiTcb, HmiTask, HmiStack, sizeof(HmiStack) / sizeof(HmiStack[0]), HMI_TASK_PRIORITY, "HmiTask"); // less priority is the HMI (user inputs and LCD) +} diff --git a/software/system/hmi_task.h b/software/system/hmi_task.h new file mode 100644 index 0000000..3e8efdb --- /dev/null +++ b/software/system/hmi_task.h @@ -0,0 +1,6 @@ +#ifndef HMI_TASK_H +#define HMI_TASK_H + +void hmi_task_initialize(); + +#endif // HMI_TASK_H diff --git a/software/system/main.c b/software/system/main.c index 3305c11..fa531b5 100644 --- a/software/system/main.c +++ b/software/system/main.c @@ -2,7 +2,7 @@ #include "ost_hal.h" #include "debug.h" #include "filesystem.h" -#include "picture.h" + #include "qor.h" #include "rotary-button.h" @@ -12,331 +12,16 @@ #include #include -#include "audio_player.h" #include "chip32_vm.h" -#include "mini_qoi.h" - -#ifdef OST_USE_FF_LIBRARY -#include "ff.h" -#include "diskio.h" -typedef FIL file_t; -#else - -// Use standard library -typedef FILE *file_t; -typedef int FRESULT; -#define F_OK -#endif - -file_t file_open(const char *filename) -{ -#ifdef OST_USE_FF_LIBRARY - file_t fil; - FRESULT fr = f_open(&fil, filename, FA_READ); - if (fr != FR_OK) - { - debug_printf("ERROR: f_open %d\n\r", (int)fr); - } - return fil; -#else - return fopen(filename, "r"); -#endif -} +#include "system.h" +#include "hmi_task.h" +#include "vm_task.h" +#include "fs_task.h" void ost_hal_panic() { } -// =========================================================================================================== -// GLOBAL STORY VARIABLES -// =========================================================================================================== -static ost_context_t OstContext; - -static mqoi_dec_t dec; -static mqoi_desc_t desc; -/* -if (pixel == info_header.width) -{ - // enough pixels to write a line to the screen - ost_display_draw_h_line(pos.y, decompressed, palette); - // debug_printf("POS Y: %d", pos.y); - - memset(decompressed, 0, sizeof(decompressed)); - // ili9341_write(&pos, decompressed); - // next line... - pos.y++; - totalPixels += info_header.width; - pixel = 0; - nblines++; -} -*/ - -static uint8_t bmpImage[256]; - -static color_t line[320]; - -void display_image(const char *filename) -{ - file_t fil; - unsigned int br; - - fil = file_open(filename); - uint32_t imgW, imgH; - - mqoi_desc_init(&desc); - - // 1. Read header - f_read(&fil, &desc.magic[0], sizeof(mqoi_desc_t) - 1, &br); - - uint8_t errn = mqoi_desc_verify(&desc, &imgW, &imgH); - - if (errn) - { - debug_printf("Invalid image, code %d\n", errn); - return; - } - - debug_printf("Image dimensions: %d, %d\n", imgW, imgH); - - uint32_t start, end, pxCount = 0; - - volatile mqoi_rgba_t *px; - - mqoi_dec_init(&dec, imgW * imgH); - - // Serial.println("starting decode..."); - int index = 256; // force refill first time - int x = 0; - int y = 0; - while (!mqoi_dec_done(&dec)) - { - if (index >= sizeof(bmpImage)) - { - // refill buffer - f_read(&fil, bmpImage, sizeof(bmpImage), &br); - index = 0; - } - - mqoi_dec_push(&dec, bmpImage[index++]); - - while ((px = mqoi_dec_pop(&dec)) != NULL) - { - pxCount++; - - line[x].r = px->r; - line[x].g = px->g; - line[x].b = px->b; - - x++; - if (x >= 320) - { - ost_display_draw_h_line_rgb888(y, line); - x = 0; - y++; - } - } - } - - f_close(&fil); -} - -// =========================================================================================================== -// HMI TASK (user interface, buttons manager, LCD) -// =========================================================================================================== -static qor_tcb_t HmiTcb; -static uint32_t HmiStack[4096]; - -static qor_mbox_t HmiMailBox; - -typedef struct -{ - uint8_t ev; -} ost_hmi_event_t; - -static ost_hmi_event_t HmiEvent; - -ost_hmi_event_t HmiQueue[10]; - -void HmiTask(void *args) -{ - qor_mbox_init(&HmiMailBox, (void **)&HmiQueue, 10); - - ost_hmi_event_t *e = NULL; - - filesystem_read_index_file(&OstContext); - - display_image("/ba869e4b-03d6-4249-9202-85b4cec767a7/images/bird.qoi"); - - while (1) - { - uint32_t res = qor_mbox_wait(&HmiMailBox, (void **)&e, 1000); - - if (res == QOR_MBOX_OK) - { - } - else - { - debug_printf("H"); // pour le debug only - } - } -} - -// =========================================================================================================== -// VIRTUAL MACHINE TASK -// =========================================================================================================== -static qor_tcb_t VmTcb; -static uint32_t VmStack[4096]; - -static qor_mbox_t VmMailBox; - -typedef struct -{ - uint8_t ev; -} ost_vm_event_t; -ost_vm_event_t VmQueue[10]; - -static ost_vm_event_t VmEvent; - -static uint8_t m_rom_data[16 * 1024]; -static uint8_t m_ram_data[16 * 1024]; -static chip32_ctx_t m_chip32_ctx; - -// Index file parameter, reference an index in the file - -uint8_t vm_syscall(chip32_ctx_t *ctx, uint8_t signum) -{ -} - -void VmTask(void *args) -{ - // VM Initialize - m_chip32_ctx.stack_size = 512; - - m_chip32_ctx.rom.mem = m_rom_data; - m_chip32_ctx.rom.addr = 0; - m_chip32_ctx.rom.size = sizeof(m_rom_data); - - m_chip32_ctx.ram.mem = m_ram_data; - m_chip32_ctx.ram.addr = sizeof(m_rom_data); - m_chip32_ctx.ram.size = sizeof(m_ram_data); - - m_chip32_ctx.syscall = vm_syscall; - - chip32_initialize(&m_chip32_ctx); - - qor_mbox_init(&VmMailBox, (void **)&VmQueue, 10); - - chip32_result_t run_result; - ost_vm_event_t *e = NULL; - - while (1) - { - uint32_t res = qor_mbox_wait(&VmMailBox, (void **)&e, 300); // On devrait recevoir un message toutes les 3ms (durée d'envoi d'un buffer I2S) - - if (res == QOR_MBOX_OK) - { - if (VmEvent.ev == 1) - { - do - { - run_result = chip32_step(&m_chip32_ctx); - } while (run_result != VM_OK); - } - } - } -} - -// =========================================================================================================== -// AUDIO TASK -// =========================================================================================================== -static qor_tcb_t AudioTcb; -static uint32_t AudioStack[4096]; - -static qor_mbox_t AudioMailBox; - -typedef struct -{ - uint8_t ev; -} ost_audio_event_t; - -static ost_audio_event_t wake_up; - -ost_audio_event_t audio_queue[10]; - -static int dbg_state = 0; - -// End of DMA transfer callback -static void audio_callback(void) -{ - dbg_state = 1 - dbg_state; - qor_mbox_notify(&AudioMailBox, (void **)&wake_up, QOR_MBOX_OPTION_SEND_BACK); -} - -void show_duration(uint32_t millisecondes) -{ - uint32_t minutes, secondes, reste; - - // Calcul des minutes, secondes et millisecondes - minutes = millisecondes / (60 * 1000); - reste = millisecondes % (60 * 1000); - secondes = reste / 1000; - reste = reste % 1000; - - // Affichage du temps - debug_printf("Temps : %d minutes, %d secondes, %d millisecondes\r\n", minutes, secondes, reste); -} - -void AudioTask(void *args) -{ - // picture_show("example.bmp"); - - wake_up.ev = 34; - - qor_mbox_init(&AudioMailBox, (void **)&audio_queue, 10); - - ost_audio_register_callback(audio_callback); - - static bool onetime = true; - - while (1) - { - debug_printf("\r\n-------------------------------------------------------\r\nPlaying: out2.wav\r\n"); - ost_system_stopwatch_start(); - ost_audio_play("out2.wav"); - - ost_audio_event_t *e = NULL; - - int isPlaying = 0; - int count = 0; - do - { - uint32_t res = qor_mbox_wait(&AudioMailBox, (void **)&e, 300); // On devrait recevoir un message toutes les 3ms (durée d'envoi d'un buffer I2S) - - if (res == QOR_MBOX_OK) - { - isPlaying = ost_audio_process(); - } - - count++; - - } while (isPlaying); - - uint32_t executionTime = ost_system_stopwatch_stop(); - ost_audio_stop(); - - debug_printf("\r\nPackets: %d\r\n", count); - show_duration(executionTime); - - for (;;) - { - ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 0); - qor_sleep(500); - ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 1); - qor_sleep(500); - } - } -} - // =========================================================================================================== // IDLE TASK // =========================================================================================================== @@ -369,9 +54,9 @@ int main() qor_init(125000000UL); // 5. Initialize the tasks - qor_create_thread(&HmiTcb, HmiTask, HmiStack, sizeof(HmiStack) / sizeof(HmiStack[0]), 1, "HmiTask"); // less priority is the HMI (user inputs and LCD) - qor_create_thread(&VmTcb, VmTask, VmStack, sizeof(VmStack) / sizeof(VmStack[0]), 2, "VmTask"); - qor_create_thread(&AudioTcb, AudioTask, AudioStack, sizeof(AudioStack) / sizeof(AudioStack[0]), 3, "AudioTask"); ///< High priority for audio + hmi_task_initialize(); + vm_task_initialize(); + fs_task_initialize(); // 6. Start the operating system! qor_start(&IdleTcb, IdleTask, IdleStack, 1024); diff --git a/software/system/system.h b/software/system/system.h new file mode 100644 index 0000000..5987487 --- /dev/null +++ b/software/system/system.h @@ -0,0 +1,9 @@ +#ifndef SYSTEM_H +#define SYSTEM_H + +// On regroupe ici au les priorités des différents threads afin d'avoir une vision plus large +#define HMI_TASK_PRIORITY 1 +#define VM_TASK_PRIORITY 2 +#define FS_TASK_PRIORITY 3 ///< High priority for audio / file system access + +#endif // SYSTEM_H diff --git a/software/system/vm_task.c b/software/system/vm_task.c new file mode 100644 index 0000000..435c030 --- /dev/null +++ b/software/system/vm_task.c @@ -0,0 +1,156 @@ + +#include +#include +#include + +#include "ost_hal.h" +#include "debug.h" +#include "qor.h" +#include "chip32_vm.h" +#include "system.h" +#include "vm_task.h" +#include "fs_task.h" + +// =========================================================================================================== +// DEFINITIONS +// =========================================================================================================== +#define VM_EV_NO_EVENT 0 +#define VM_EV_START_STORY_EVENT 0xA1 + +typedef struct +{ + uint8_t ev; + const char *story_dir; +} ost_vm_event_t; + +// =========================================================================================================== +// PRIVATE GLOBAL VARIABLES +// =========================================================================================================== +static qor_tcb_t VmTcb; +static uint32_t VmStack[4096]; +static qor_mbox_t VmMailBox; +static ost_vm_event_t VmQueue[10]; + +static ost_vm_event_t VmStartEvent; +static uint8_t m_rom_data[16 * 1024]; +static uint8_t m_ram_data[16 * 1024]; +static chip32_ctx_t m_chip32_ctx; +static char CurrentStory[260]; // Current story path +static char ImageFile[260]; +static char SoundFile[260]; + +// =========================================================================================================== +// VIRTUAL MACHINE TASK +// =========================================================================================================== + +void get_file_from_memory(char *mem, uint32_t addr) +{ + bool isRam = addr & 0x80000000; + addr &= 0xFFFF; // mask the RAM/ROM bit, ensure 16-bit addressing + if (isRam) + { + strcpy(&mem[0], (const char *)&m_chip32_ctx.ram.mem[addr]); + } + else + { + strcpy(&mem[0], (const char *)&m_chip32_ctx.rom.mem[addr]); + } +} + +// Callback from the VM +// Called inside the thread context +uint8_t vm_syscall(chip32_ctx_t *ctx, uint8_t code) +{ + uint8_t retCode = SYSCALL_RET_OK; + + // Media + if (code == 1) // Execute media + { + if (m_chip32_ctx.registers[R0] != 0) + { + // image file name address is in R0 + // QString imageFile = m_model.BuildFullImagePath(GetFileNameFromMemory(m_chip32_ctx.registers[R0])); + // m_ostHmiDock->SetImage(imageFile); + } + else + { + // m_ostHmiDock->ClearImage(); + } + + if (m_chip32_ctx.registers[R1] != 0) + { + // sound file name address is in R1 + // QString soundFile = m_model.BuildFullSoundPath(GetFileNameFromMemory(m_chip32_ctx.registers[R1])); + // qDebug() << ", Sound: " << soundFile; + // m_model.PlaySoundFile(soundFile); + } + retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause + } + // WAIT EVENT bits: + // 0: block + // 1: OK button + // 2: home button + // 3: pause button + // 4: rotary left + // 5: rotary right + else if (code == 2) // Wait for event + { + // Event mask is located in R0 + // optional timeout is located in R1 + // if timeout is set to zero, wait for infinite and beyond + retCode = SYSCALL_RET_WAIT_EV; // set the VM in pause + } + + return retCode; +} + +void VmTask(void *args) +{ + // VM Initialize + m_chip32_ctx.stack_size = 512; + + m_chip32_ctx.rom.mem = m_rom_data; + m_chip32_ctx.rom.addr = 0; + m_chip32_ctx.rom.size = sizeof(m_rom_data); + + m_chip32_ctx.ram.mem = m_ram_data; + m_chip32_ctx.ram.addr = sizeof(m_rom_data); + m_chip32_ctx.ram.size = sizeof(m_ram_data); + + m_chip32_ctx.syscall = vm_syscall; + + chip32_result_t run_result; + ost_vm_event_t *e = NULL; + + while (1) + { + uint32_t res = qor_mbox_wait(&VmMailBox, (void **)&e, 300); // On devrait recevoir un message toutes les 3ms (durée d'envoi d'un buffer I2S) + + if (res == QOR_MBOX_OK) + { + if (VmStartEvent.ev == VM_EV_START_STORY_EVENT) + { + // Launch the execution of a story + chip32_initialize(&m_chip32_ctx); + + do + { + run_result = chip32_step(&m_chip32_ctx); + } while (run_result != VM_OK); + } + } + } +} + +void vm_task_start_story(const char *story_directory) +{ + VmStartEvent.story_dir = story_directory; + qor_mbox_notify(&VmMailBox, (void **)&VmStartEvent, QOR_MBOX_OPTION_SEND_BACK); +} + +void vm_task_initialize() +{ + VmStartEvent.ev = VM_EV_START_STORY_EVENT; + qor_mbox_init(&VmMailBox, (void **)&VmQueue, 10); + qor_create_thread(&VmTcb, VmTask, VmStack, sizeof(VmStack) / sizeof(VmStack[0]), VM_TASK_PRIORITY, "VmTask"); +} diff --git a/software/system/vm_task.h b/software/system/vm_task.h new file mode 100644 index 0000000..c1d470e --- /dev/null +++ b/software/system/vm_task.h @@ -0,0 +1,7 @@ +#ifndef VM_TASK_H +#define VM_TASK_H + +void vm_task_start_story(const char *story_directory); +void vm_task_initialize(); + +#endif // VM_TASK_H diff --git a/story-editor/src/main_window.cpp b/story-editor/src/main_window.cpp index db381e1..1bbd014 100644 --- a/story-editor/src/main_window.cpp +++ b/story-editor/src/main_window.cpp @@ -202,6 +202,34 @@ MainWindow::MainWindow() } }); + connect(m_resourcesDock, &ResourcesDock::sigChooseTitle, [&](bool isImage) { + m_chooseFileUi.tableView->setModel(&m_resourcesDock->getModel()); + m_chooseFileDialog->exec(); + + // Get the file choosen + QModelIndexList selection = m_chooseFileUi.tableView->selectionModel()->selectedRows(); + + if (selection.count() > 0) + { + // Take first + QModelIndex index = selection.at(0); + Resource res; + if (m_project.GetResourceAt(index.row(), res)) + { + if (isImage) + { + m_project.SetTitleImage(res.file); + m_resourcesDock->SetTitleImage(res.file.c_str()); + } + else + { + m_project.SetTitleSound(res.file); + m_resourcesDock->SetTitleSound(res.file.c_str()); + } + } + } + }); + connect(m_toolbar, &ToolBar::sigNew, this, [&]() { NewProject(); }); @@ -366,7 +394,7 @@ void MainWindow::BuildAll() // 2. Compile the assembly to machine binary GenerateBinary(); - // 3. Conert all media + // 3. Convert all media to desired type format ConvertResources(); } @@ -422,6 +450,9 @@ void MainWindow::ExitProgram() void MainWindow::RefreshProjectInformation() { setWindowTitle(QString("StoryTeller Editor - ") + m_project.GetProjectFilePath().c_str()); + + m_resourcesDock->SetTitleImage(m_project.GetTitleImage().c_str()); + m_resourcesDock->SetTitleSound(m_project.GetTitleSound().c_str()); } void MainWindow::MessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) diff --git a/story-editor/src/ost-resources.ui b/story-editor/src/ost-resources.ui index 4083aa2..85bdb26 100644 --- a/story-editor/src/ost-resources.ui +++ b/story-editor/src/ost-resources.ui @@ -14,8 +14,8 @@ Story Resources - - + + @@ -40,7 +40,57 @@ - + + + + + + Title image + + + + + + + <no file !> + + + + + + + Choose file + + + + + + + + + + + Title sound + + + + + + + <no file !> + + + + + + + Choose file + + + + + + QAbstractItemView::SingleSelection diff --git a/story-editor/src/resources_dock.cpp b/story-editor/src/resources_dock.cpp index 8304a9c..11d0a8f 100644 --- a/story-editor/src/resources_dock.cpp +++ b/story-editor/src/resources_dock.cpp @@ -67,6 +67,24 @@ ResourcesDock::ResourcesDock(StoryProject &project, ResourceModel &model) } } }); + + connect(m_uiOstResources.chooseTitleImageButton, &QPushButton::clicked, [&](bool enable) { + emit sigChooseTitle(true); + }); + + connect(m_uiOstResources.chooseTitleSoundButton, &QPushButton::clicked, [&](bool enable) { + emit sigChooseTitle(false); + }); +} + +void ResourcesDock::SetTitleImage(const QString &name) +{ + m_uiOstResources.titleImage->setText(name); +} + +void ResourcesDock::SetTitleSound(const QString &name) +{ + m_uiOstResources.titleSound->setText(name); } diff --git a/story-editor/src/resources_dock.h b/story-editor/src/resources_dock.h index 50aecac..e341bd2 100644 --- a/story-editor/src/resources_dock.h +++ b/story-editor/src/resources_dock.h @@ -19,6 +19,12 @@ public: void SetFilterType(const QString &type) { m_proxyModel.setFilterType(type); } + void SetTitleImage(const QString &name); + void SetTitleSound(const QString &name); + +signals: + void sigChooseTitle(bool isImage); + private: StoryProject &m_project; Ui::ostResources m_uiOstResources; diff --git a/story-editor/src/story_project.cpp b/story-editor/src/story_project.cpp index d46cd16..a209d9f 100644 --- a/story-editor/src/story_project.cpp +++ b/story-editor/src/story_project.cpp @@ -17,6 +17,13 @@ void StoryProject::SaveStory(const std::vector &m_program) std::ofstream o(m_working_dir + std::filesystem::path::preferred_separator + "story.c32", std::ios::out | std::ios::binary); o.write(reinterpret_cast(m_program.data()), m_program.size()); o.close(); + + // Generate title files + std::ofstream index(m_working_dir + std::filesystem::path::preferred_separator + "index.ost"); + index << "/" << m_uuid << "/images/" << m_titleImage << "\n"; + index << "/" << m_uuid << "/sounds/" << m_titleSound << "\n"; + index.close(); + } void StoryProject::Initialize(const std::string &file_path) @@ -63,6 +70,8 @@ bool StoryProject::Load(const std::string &file_path, nlohmann::json &model) m_name = projectData["name"].get(); m_uuid = projectData["uuid"].get(); + m_titleImage = projectData.value("title_image", ""); + m_titleSound = projectData.value("title_sound", ""); if (j.contains("resources")) { @@ -159,7 +168,7 @@ bool StoryProject::Load(const std::string &file_path, nlohmann::json &model) void StoryProject::Save(const nlohmann::json &model) { nlohmann::json j; - j["project"] = { {"name", m_name}, {"uuid", m_uuid} }; + j["project"] = { {"name", m_name}, {"uuid", m_uuid}, { "title_image", m_titleImage }, { "title_sound", m_titleSound } }; { nlohmann::json resourcesData; @@ -267,6 +276,16 @@ std::string StoryProject::FileToConstant(const std::string &FileName, const std: return "$" + f + " DC8 \"" + f + extension + "\", 8\r\n"; } +void StoryProject::SetTitleImage(const std::string &titleImage) +{ + m_titleImage = titleImage; +} + +void StoryProject::SetTitleSound(const std::string &titleSound) +{ + m_titleSound = titleSound; +} + void StoryProject::AppendResource(const Resource &res) { m_resources.push_back(res); diff --git a/story-editor/src/story_project.h b/story-editor/src/story_project.h index 3a13252..9e938e9 100644 --- a/story-editor/src/story_project.h +++ b/story-editor/src/story_project.h @@ -88,6 +88,12 @@ struct StoryProject static void ReplaceCharacter(std::string &theString, const std::string &toFind, const std::string &toReplace); static std::string FileToConstant(const std::string &FileName, const std::string &extension); + void SetTitleImage(const std::string &titleImage); + void SetTitleSound(const std::string &titleSound); + + std::string GetTitleImage() const { return m_titleImage; } + std::string GetTitleSound() const { return m_titleSound; } + // ------------- Resources Management void AppendResource(const Resource &res); bool GetResourceAt(int index, Resource &resOut); @@ -117,6 +123,9 @@ private: std::filesystem::path m_soundsPath; bool m_initialized{false}; + std::string m_titleImage; + std::string m_titleSound; + std::vector m_resources; std::string m_working_dir; /// Temporary folder based on the uuid, where the archive is unzipped std::string m_project_file_path; /// JSON project file