diff --git a/components/pax-graphics b/components/pax-graphics index fc1f1f6..8659d42 160000 --- a/components/pax-graphics +++ b/components/pax-graphics @@ -1 +1 @@ -Subproject commit fc1f1f634420f0b5baffb07bdef422e2280d207f +Subproject commit 8659d4201508875a46102a083efda191afeca0a3 diff --git a/components/spi-ice40 b/components/spi-ice40 index 9a42035..1c2b524 160000 --- a/components/spi-ice40 +++ b/components/spi-ice40 @@ -1 +1 @@ -Subproject commit 9a42035f026f928e272f8e8647880475c84881c9 +Subproject commit 1c2b5241dd00d2fddb45d20f3c4a426fa379fa95 diff --git a/components/spi-ili9341 b/components/spi-ili9341 index 7929cf7..2d5e816 160000 --- a/components/spi-ili9341 +++ b/components/spi-ili9341 @@ -1 +1 @@ -Subproject commit 7929cf7d34a60d5c8c91d06ac727f825aa310852 +Subproject commit 2d5e8168d8604867c0323d320f94ca2c6fa2976d diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 0c05b5f..598ff77 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( - SRCS "main.c" - INCLUDE_DIRS "." + SRCS "main.c" "menu.c" "fpga_test.c" + INCLUDE_DIRS "." "include" ) diff --git a/main/fpga_test.c b/main/fpga_test.c new file mode 100644 index 0000000..113797f --- /dev/null +++ b/main/fpga_test.c @@ -0,0 +1,166 @@ +#include "fpga_test.h" +#include +#include +#include +#include +#include +#include "fpga.h" +#include "ili9341.h" +#include "ice40.h" +#include "hardware.h" +#include "button_message.h" + +static const char *TAG = "fpga_test"; + +esp_err_t load_file_into_psram(ICE40* ice40, FILE* fd) { + fseek(fd, 0, SEEK_SET); + const uint8_t write_cmd = 0x02; + uint32_t amount_read; + uint32_t position = 0; + uint8_t* tx_buffer = malloc(SPI_MAX_TRANSFER_SIZE); + if (tx_buffer == NULL) return ESP_FAIL; + + while(1) { + tx_buffer[0] = write_cmd; + tx_buffer[1] = (position >> 16); + tx_buffer[2] = (position >> 8) & 0xFF; + tx_buffer[3] = position & 0xFF; + amount_read = fread(&tx_buffer[4], 1, SPI_MAX_TRANSFER_SIZE - 4, fd); + if (amount_read < 1) break; + ESP_LOGI(TAG, "Writing PSRAM @ %u (%u bytes)", position, amount_read); + esp_err_t res = ice40_transaction(ice40, tx_buffer, amount_read + 4, NULL, 0); + if (res != ESP_OK) { + ESP_LOGE(TAG, "Write transaction failed @ %u", position); + free(tx_buffer); + return res; + } + position += amount_read; + }; + free(tx_buffer); + return ESP_OK; +} + +esp_err_t load_buffer_into_psram(ICE40* ice40, uint8_t* buffer, uint32_t buffer_length) { + const uint8_t write_cmd = 0x02; + uint32_t position = 0; + uint8_t* tx_buffer = malloc(SPI_MAX_TRANSFER_SIZE); + if (tx_buffer == NULL) return ESP_FAIL; + while(1) { + tx_buffer[0] = write_cmd; + tx_buffer[1] = (position >> 16); + tx_buffer[2] = (position >> 8) & 0xFF; + tx_buffer[3] = position & 0xFF; + uint32_t length = buffer_length - position; + if (length > SPI_MAX_TRANSFER_SIZE - 4) length = SPI_MAX_TRANSFER_SIZE - 4; + memcpy(&tx_buffer[4], &buffer[position], length); + if (length == 0) break; + ESP_LOGI(TAG, "Writing PSRAM @ %u (%u bytes)", position, length); + esp_err_t res = ice40_transaction(ice40, tx_buffer, length + 4, NULL, 0); + if (res != ESP_OK) { + ESP_LOGE(TAG, "Write transaction failed @ %u", position); + free(tx_buffer); + return res; + } + position += length; + }; + free(tx_buffer); + return ESP_OK; +} + +esp_err_t verify_file_in_psram(ICE40* ice40, FILE* fd) { + fseek(fd, 0, SEEK_SET); + const uint8_t read_cmd = 0x03; + uint32_t amount_read; + uint32_t position = 0; + uint8_t* tx_buffer = malloc(SPI_MAX_TRANSFER_SIZE); + if (tx_buffer == NULL) return ESP_FAIL; + memset(tx_buffer, 0, SPI_MAX_TRANSFER_SIZE); + uint8_t* verify_buffer = malloc(SPI_MAX_TRANSFER_SIZE); + if (verify_buffer == NULL) return ESP_FAIL; + uint8_t* rx_buffer = malloc(SPI_MAX_TRANSFER_SIZE); + if (rx_buffer == NULL) return ESP_FAIL; + + while(1) { + tx_buffer[0] = read_cmd; + tx_buffer[1] = (position >> 16); + tx_buffer[2] = (position >> 8) & 0xFF; + tx_buffer[3] = position & 0xFF; + amount_read = fread(&verify_buffer[4], 1, SPI_MAX_TRANSFER_SIZE - 4, fd); + if (amount_read < 1) break; + ESP_LOGI(TAG, "Reading PSRAM @ %u (%u bytes)", position, amount_read); + esp_err_t res = ice40_transaction(ice40, tx_buffer, amount_read + 4, rx_buffer, amount_read + 4); + if (res != ESP_OK) { + ESP_LOGE(TAG, "Read transaction failed @ %u", position); + free(tx_buffer); + return res; + } + position += amount_read; + ESP_LOGI(TAG, "Verifying PSRAM @ %u (%u bytes)", position, amount_read); + for (uint32_t i = 4; i < amount_read; i++) { + if (rx_buffer[i] != verify_buffer[i]) { + ESP_LOGE(TAG, "Verifying PSRAM @ %u failed: %02X != %02X", position + i, rx_buffer[i], verify_buffer[i]); + free(tx_buffer); + free(rx_buffer); + free(verify_buffer); + return ESP_FAIL; + } + } + }; + free(tx_buffer); + free(rx_buffer); + free(verify_buffer); + ESP_LOGI(TAG, "PSRAM contents verified!"); + return ESP_OK; +} + +void fpga_test(ILI9341* ili9341, ICE40* ice40, xQueueHandle buttonQueue) { + esp_err_t res; + bool reload_fpga = false; + do { + printf("Start FPGA test...\n"); + reload_fpga = false; + printf("LCD deinit...\n"); + ili9341_deinit(ili9341); + printf("LCD deselect...\n"); + ili9341_select(ili9341, false); + printf("Wait...\n"); + vTaskDelay(200 / portTICK_PERIOD_MS); + printf("LCD select...\n"); + ili9341_select(ili9341, true); + + printf("FPGA load...\n"); + res = ice40_load_bitstream(ice40, proto2_bin, proto2_bin_len); + if (res != ESP_OK) { + printf("Failed to load app bitstream into FPGA (%d)\n", res); + return; + } else { + printf("Bitstream loaded succesfully!\n"); + } + + bool waitForChoice = true; + while (waitForChoice) { + button_message_t buttonMessage = {0}; + printf("Waiting for button press...\n"); + if (xQueueReceive(buttonQueue, &buttonMessage, portMAX_DELAY) == pdTRUE) { + printf("Button: %u, %u\n", buttonMessage.button, buttonMessage.state); + if (buttonMessage.state) { + switch(buttonMessage.button) { + case PCA9555_PIN_BTN_HOME: + case PCA9555_PIN_BTN_MENU: + case PCA9555_PIN_BTN_BACK: + waitForChoice = false; + break; + case PCA9555_PIN_BTN_ACCEPT: + reload_fpga = true; + waitForChoice = false; + break; + default: + break; + } + } + } + } + ice40_disable(ice40); + ili9341_init(ili9341); + } while (reload_fpga); +} diff --git a/main/include/button_message.h b/main/include/button_message.h new file mode 100644 index 0000000..7003afb --- /dev/null +++ b/main/include/button_message.h @@ -0,0 +1,6 @@ +#pragma once + +typedef struct _button_message { + uint8_t button; + bool state; +} button_message_t; diff --git a/main/include/fpga_test.h b/main/include/fpga_test.h new file mode 100644 index 0000000..c4cab5c --- /dev/null +++ b/main/include/fpga_test.h @@ -0,0 +1,8 @@ +#pragma once + +#include "hardware.h" +#include "rp2040.h" +#include "ili9341.h" +#include "ice40.h" + +void fpga_test(ILI9341* ili9341, ICE40* ice40, xQueueHandle buttonQueue); diff --git a/main/include/menu.h b/main/include/menu.h new file mode 100644 index 0000000..398f0ea --- /dev/null +++ b/main/include/menu.h @@ -0,0 +1,46 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + +#include +#include +#include + +#include "pax_gfx.h" + +typedef bool (*menu_callback_t)(); + +typedef struct _menu_item { + char* label; + menu_callback_t callback; + void* callbackArgs; + + // Linked list + struct _menu_item* previousItem; + struct _menu_item* nextItem; +} menu_item_t; + +typedef struct menu { + char* title; + menu_item_t* firstItem; + size_t length; + size_t position; +} menu_t; + +menu_t* menu_alloc(const char* aTitle); +void menu_free(menu_t* aMenu); +bool menu_insert_item(menu_t* aMenu, const char* aLabel, menu_callback_t aCallback, void* aCallbackArgs, size_t aPosition); +bool menu_remove_item(menu_t* aMenu, size_t aPosition); +bool menu_navigate_to(menu_t* aMenu, size_t aPosition); +void menu_navigate_previous(menu_t* aMenu); +void menu_navigate_next(menu_t* aMenu); +size_t menu_get_position(menu_t* aMenu); +void* menu_get_callback_args(menu_t* aMenu, size_t aPosition); +void menu_debug(menu_t* aMenu); +void menu_render(pax_buf_t *aBuffer, menu_t *aMenu, float aPosX, float aPosY, float aWidth, float aHeight); + +#ifdef __cplusplus +} +#endif //__cplusplus diff --git a/main/main.c b/main/main.c index 790e71e..43c48fa 100644 --- a/main/main.c +++ b/main/main.c @@ -3,6 +3,7 @@ #include #include #include +#include #include //#include #include @@ -20,7 +21,10 @@ #include "rp2040.h" -#include "fpga.h" +#include "fpga_test.h" + +#include "menu.h" +#include "button_message.h" static const char *TAG = "main"; @@ -30,107 +34,27 @@ ILI9341* ili9341 = NULL; ICE40* ice40 = NULL; BNO055* bno055 = NULL; RP2040* rp2040 = NULL; - uint8_t* framebuffer = NULL; pax_buf_t* pax_buffer = NULL; - -bno055_vector_t rotation_offset = {.x = 0, .y = 0, .z = 0}; - -bno055_vector_t acceleration, magnetism, orientation, rotation, linear_acceleration, gravity; +xQueueHandle buttonQueue; typedef enum action { ACTION_NONE, + ACTION_APPFS, ACTION_INSTALLER, ACTION_FPGA } menu_action_t; -typedef struct _menu_item { - const char* name; +typedef struct _menu_args { appfs_handle_t fd; menu_action_t action; - struct _menu_item* next; -} menu_item_t; - -uint8_t selected_item = 0; -uint8_t amount_of_items = 0; -bool start_selected = false; -menu_item_t* first_menu_item = NULL; - -bool reset_to_menu = false; -bool reload_fpga = false; - -const char installer_name[] = "Install app..."; -const char fpga_name[] = "Test FPGA"; +} menu_args_t; void button_handler(uint8_t pin, bool value) { - switch(pin) { - case PCA9555_PIN_BTN_JOY_LEFT: - printf("Joystick horizontal %s\n", value ? "left" : "center"); - if (value) { - for (uint8_t led = 0; led < 5; led++) { - rp2040_set_led_value(rp2040, led, 0, 255, 0); - } - } - break; - case PCA9555_PIN_BTN_JOY_PRESS: - printf("Joystick %s\n", value ? "pressed" : "released"); - if (value) { - for (uint8_t led = 0; led < 5; led++) { - rp2040_set_led_value(rp2040, led, 0, 0, 255); - } - } - break; - case PCA9555_PIN_BTN_JOY_DOWN: - printf("Joystick vertical %s\n", value ? "down" : "center"); - //ili9341_set_partial_scanning(ili9341, 0, ILI9341_WIDTH / 2 - 1); - if (value && (!start_selected)) { - if (selected_item < amount_of_items - 1) { - selected_item += 1; - } - } - break; - case PCA9555_PIN_BTN_JOY_UP: - printf("Joy vertical %s\n", value ? "up" : "center"); - if (value && (!start_selected)) { - if (selected_item > 0) { - selected_item -= 1; - } - } - break; - case PCA9555_PIN_BTN_JOY_RIGHT: - printf("Joy horizontal %s\n", value ? "right" : "center"); - if (value) { - for (uint8_t led = 0; led < 5; led++) { - rp2040_set_led_value(rp2040, led, 255, 0, 0); - } - } - break; - case PCA9555_PIN_BTN_HOME: - printf("Home button %s\n", value ? "pressed" : "released"); - break; - case PCA9555_PIN_BTN_MENU: - printf("Menu button %s\n", value ? "pressed" : "released"); - if (value) reset_to_menu = true; - break; - case PCA9555_PIN_BTN_START: { - printf("Start button %s\n", value ? "pressed" : "released"); - if (value) reload_fpga = true; - break; - } - case PCA9555_PIN_BTN_SELECT: { - printf("Select button %s\n", value ? "pressed" : "released"); - break; - } - case PCA9555_PIN_BTN_BACK: - printf("Back button %s\n", value ? "pressed" : "released"); - break; - case PCA9555_PIN_BTN_ACCEPT: - printf("Accept button %s\n", value ? "pressed" : "released"); - if (value) start_selected = true; - break; - default: - printf("Unknown button %d %s\n", pin, value ? "pressed" : "released"); - } + button_message_t message; + message.button = pin; + message.state = value; + xQueueSend(buttonQueue, &message, portMAX_DELAY); } void button_init() { @@ -158,145 +82,28 @@ void restart() { esp_restart(); } -void bno055_task(BNO055* bno055) { - esp_err_t res; +void message_render(pax_buf_t *aBuffer, char* message, float aPosX, float aPosY, float aWidth, float aHeight) { + pax_col_t fgColor = 0xFFFF0000; + pax_col_t bgColor = 0xFFFFD4D4; + pax_clip(aBuffer, aPosX, aPosY, aWidth, aHeight); + pax_simple_rect(aBuffer, bgColor, aPosX, aPosY, aWidth, aHeight); + pax_outline_rect(aBuffer, fgColor, aPosX, aPosY, aWidth, aHeight); + pax_clip(aBuffer, aPosX + 1, aPosY + 1, aWidth - 2, aHeight - 2); + pax_draw_text(aBuffer, fgColor, NULL, 18, aPosX + 1, aPosY + 1, message); + pax_noclip(aBuffer); +} - res = bno055_get_vector(bno055, BNO055_VECTOR_ACCELEROMETER, &acceleration); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Acceleration failed to read %d\n", res); - return; - } - res = bno055_get_vector(bno055, BNO055_VECTOR_MAGNETOMETER, &magnetism); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Magnetic field to read %d\n", res); - return; - } - - res = bno055_get_vector(bno055, BNO055_VECTOR_GYROSCOPE, &orientation); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Orientation failed to read %d\n", res); - return; - } - - res = bno055_get_vector(bno055, BNO055_VECTOR_EULER, &rotation); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Rotation failed to read %d\n", res); - return; - } - - res = bno055_get_vector(bno055, BNO055_VECTOR_LINEARACCEL, &linear_acceleration); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Linear acceleration failed to read %d\n", res); - return; - } - - res = bno055_get_vector(bno055, BNO055_VECTOR_GRAVITY, &gravity); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Gravity failed to read %d\n", res); - return; +esp_err_t graphics_task(pax_buf_t* buffer, ILI9341* ili9341, uint8_t* framebuffer, menu_t* menu, char* message) { + pax_background(pax_buffer, 0xCCCCCC); + if (menu != NULL) { + menu_render(pax_buffer, menu, 10, 10, 320-20, 240-20); } - /*if (calibrate) { - rotation_offset.x = rotation.x; - rotation_offset.y = rotation.y; - rotation_offset.z = rotation.z; - calibrate = false; + if (message != NULL) { + message_render(pax_buffer, message, 20, 110, 320-40, 20); } - - rotation.x -= rotation_offset.x; - rotation.y -= rotation_offset.y; - rotation.z -= rotation_offset.z; - - if (rotation.x < 0) rotation.x = 360.0 - rotation.x; - if (rotation.y < 0) rotation.y = 360.0 - rotation.y; - if (rotation.z < 0) rotation.z = 360.0 - rotation.z;*/ - - /*printf("\n\n"); - printf("Acceleration (m/s²) x = %5.8f y = %5.8f z = %5.8f\n", acceleration.x, acceleration.y, acceleration.z); - printf("Magnetic field (uT) x = %5.8f y = %5.8f z = %5.8f\n", magnetism.x, magnetism.y, magnetism.z); - printf("Orientation (dps) x = %5.8f y = %5.8f z = %5.8f\n", orientation.x, orientation.y, orientation.z); - printf("Rotation (degrees) x = %5.8f y = %5.8f z = %5.8f\n", rotation.x, rotation.y, rotation.z); - printf("Linear acceleration (m/s²) x = %5.8f y = %5.8f z = %5.8f\n", linear_acceleration.x, linear_acceleration.y, linear_acceleration.z); - printf("Gravity (m/s²) x = %5.8f y = %5.8f z = %5.8f\n", gravity.x, gravity.y, gravity.z);*/ - if (display_bno_value) { - printf("Magnetic (uT) x: %5.4f y: %5.4f z: %5.4f Rotation (deg): x: %5.4f y: %5.4f z: %5.4f \n", magnetism.x, magnetism.y, magnetism.z, rotation.x, rotation.y, rotation.z); - } -} - -void draw_cursor(pax_buf_t* buffer, float x, float y) { - uint64_t millis = esp_timer_get_time() / 1000; - pax_col_t color = pax_col_hsv(millis * 255 / 8000, 255, 255); - pax_push_2d(buffer); - pax_apply_2d(buffer, matrix_2d_translate(x, y)); - pax_apply_2d(buffer, matrix_2d_scale(10, 10)); - pax_draw_tri(buffer, color, -1, -1, -1, 1, 1, 0); - pax_pop_2d(buffer); -} - -void draw_menu_item(pax_buf_t* buffer, uint8_t position, bool selected, char* text) { - float y = 24 + position * 20; - if (selected) draw_cursor(buffer, 15, y + 9); - pax_draw_text(buffer, pax_col_rgb(0,0,0), PAX_FONT_DEFAULT, 18, 24, y, text); -} - -esp_err_t draw_menu(pax_buf_t* buffer) { - pax_push_2d(buffer); - //pax_apply_2d(buffer, matrix_2d_translate(0, 0)); - //pax_apply_2d(buffer, matrix_2d_scale(1, 1)); - pax_simple_line(buffer, pax_col_rgb(0,0,0), 0, 20, 320, 20); - pax_draw_text(buffer, pax_col_rgb(0,0,0), PAX_FONT_DEFAULT, 18, 0, 0, "Launcher"); - menu_item_t* item = first_menu_item; - for (uint8_t index = 0; index < amount_of_items; index++) { - if (item != NULL) { - draw_menu_item(buffer, index, (selected_item == index), item->name); - } - item = item->next; - } - pax_pop_2d(buffer); - return ESP_OK; -} - -pax_col_t regenboogkots(pax_col_t tint, int x, int y, float u, float v, void *args) { - return pax_col_hsv(x / 50.0 * 255.0 + y / 150.0 * 255.0, 255, 255); -} - -pax_shader_t kots = { - .callback = regenboogkots -}; - -esp_err_t graphics_task(pax_buf_t* buffer, ILI9341* ili9341, uint8_t* framebuffer) { - pax_background(buffer, 0xFFFFFF); - //pax_shade_rect(buffer, 0, &kots, NULL, 0, 0, 320, 240); - pax_push_2d(buffer); - pax_apply_2d(buffer, matrix_2d_translate(buffer->width / 2.0, buffer->height / 2.0 + 10)); - pax_apply_2d(buffer, matrix_2d_scale(50, 50)); - uint64_t millis = esp_timer_get_time() / 1000; - pax_col_t color0 = pax_col_hsv(millis * 255 / 8000, 255, 255); - //pax_col_t color1 = pax_col_hsv(millis * 255 / 8000 + 127, 255, 255); - float a0 = rotation.y * (M_PI / 360.0);//millis / 3000.0 * M_PI;//0;// - //printf("%f from %f\n", a0, rotation.y); - //float a1 = fmodf(a0, M_PI * 4) - M_PI * 2;//// - //pax_draw_arc(buffer, color0, 0, 0, 2, a0 + M_PI, a0); - /*pax_push_2d(buffer); - pax_apply_2d(buffer, matrix_2d_rotate(a0)); - pax_push_2d(buffer); - pax_apply_2d(buffer, matrix_2d_translate(1, 0)); - pax_draw_rect(buffer, color1, -0.25, -0.25, 0.5, 0.5); - pax_pop_2d(buffer); - pax_apply_2d(buffer, matrix_2d_rotate(a1)); - pax_push_2d(buffer); - pax_apply_2d(buffer, matrix_2d_translate(1, 0)); - pax_apply_2d(buffer, matrix_2d_rotate(-a0 - a1 + M_PI * 0.5)); - pax_draw_tri(buffer, color1, 0.25, 0, -0.125, 0.2165, -0.125, -0.2165); - pax_pop_2d(buffer); - pax_pop_2d(buffer);*/ - pax_pop_2d(buffer); - - draw_menu(buffer); - - //driver_framebuffer_print(NULL, "Hello world", 0, 0, 1, 1, 0xFF00FF, &ocra_22pt7b); return ili9341_write(ili9341, framebuffer); } @@ -306,21 +113,8 @@ esp_err_t draw_message(char* message) { return ili9341_write(ili9341, framebuffer); } -void print_chip_info(void) { - esp_chip_info_t chip_info; - esp_chip_info(&chip_info); - printf("This is %s chip with %d CPU core(s), WiFi%s%s, ", - CONFIG_IDF_TARGET, - chip_info.cores, - (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "", - (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : ""); - - printf("silicon revision %d, ", chip_info.revision); - - printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024), - (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); - - printf("Minimum free heap size: %d bytes\n", esp_get_minimum_free_heap_size()); +esp_err_t appfs_init(void) { + return appfsInit(APPFS_PART_TYPE, APPFS_PART_SUBTYPE); } uint8_t* load_file_to_ram(FILE* fd, size_t* fsize) { @@ -333,206 +127,23 @@ uint8_t* load_file_to_ram(FILE* fd, size_t* fsize) { return file; } -esp_err_t load_file_into_psram(FILE* fd) { - fseek(fd, 0, SEEK_SET); - const uint8_t write_cmd = 0x02; - uint32_t amount_read; - uint32_t position = 0; - uint8_t* tx_buffer = malloc(SPI_MAX_TRANSFER_SIZE); - if (tx_buffer == NULL) return ESP_FAIL; - - while(1) { - tx_buffer[0] = write_cmd; - tx_buffer[1] = (position >> 16); - tx_buffer[2] = (position >> 8) & 0xFF; - tx_buffer[3] = position & 0xFF; - amount_read = fread(&tx_buffer[4], 1, SPI_MAX_TRANSFER_SIZE - 4, fd); - if (amount_read < 1) break; - ESP_LOGI(TAG, "Writing PSRAM @ %u (%u bytes)", position, amount_read); - esp_err_t res = ice40_transaction(ice40, tx_buffer, amount_read + 4, NULL, 0); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Write transaction failed @ %u", position); - free(tx_buffer); - return res; - } - position += amount_read; - }; - free(tx_buffer); - return ESP_OK; -} - -esp_err_t load_buffer_into_psram(uint8_t* buffer, uint32_t buffer_length) { - const uint8_t write_cmd = 0x02; - uint32_t position = 0; - uint8_t* tx_buffer = malloc(SPI_MAX_TRANSFER_SIZE); - if (tx_buffer == NULL) return ESP_FAIL; - while(1) { - tx_buffer[0] = write_cmd; - tx_buffer[1] = (position >> 16); - tx_buffer[2] = (position >> 8) & 0xFF; - tx_buffer[3] = position & 0xFF; - uint32_t length = buffer_length - position; - if (length > SPI_MAX_TRANSFER_SIZE - 4) length = SPI_MAX_TRANSFER_SIZE - 4; - memcpy(&tx_buffer[4], &buffer[position], length); - if (length == 0) break; - ESP_LOGI(TAG, "Writing PSRAM @ %u (%u bytes)", position, length); - esp_err_t res = ice40_transaction(ice40, tx_buffer, length + 4, NULL, 0); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Write transaction failed @ %u", position); - free(tx_buffer); - return res; - } - position += length; - }; - free(tx_buffer); - return ESP_OK; -} - -esp_err_t verify_file_in_psram(FILE* fd) { - fseek(fd, 0, SEEK_SET); - const uint8_t read_cmd = 0x03; - uint32_t amount_read; - uint32_t position = 0; - uint8_t* tx_buffer = malloc(SPI_MAX_TRANSFER_SIZE); - if (tx_buffer == NULL) return ESP_FAIL; - memset(tx_buffer, 0, SPI_MAX_TRANSFER_SIZE); - uint8_t* verify_buffer = malloc(SPI_MAX_TRANSFER_SIZE); - if (verify_buffer == NULL) return ESP_FAIL; - uint8_t* rx_buffer = malloc(SPI_MAX_TRANSFER_SIZE); - if (rx_buffer == NULL) return ESP_FAIL; - - while(1) { - tx_buffer[0] = read_cmd; - tx_buffer[1] = (position >> 16); - tx_buffer[2] = (position >> 8) & 0xFF; - tx_buffer[3] = position & 0xFF; - amount_read = fread(&verify_buffer[4], 1, SPI_MAX_TRANSFER_SIZE - 4, fd); - if (amount_read < 1) break; - ESP_LOGI(TAG, "Reading PSRAM @ %u (%u bytes)", position, amount_read); - esp_err_t res = ice40_transaction(ice40, tx_buffer, amount_read + 4, rx_buffer, amount_read + 4); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Read transaction failed @ %u", position); - free(tx_buffer); - return res; - } - position += amount_read; - ESP_LOGI(TAG, "Verifying PSRAM @ %u (%u bytes)", position, amount_read); - for (uint32_t i = 4; i < amount_read; i++) { - if (rx_buffer[i] != verify_buffer[i]) { - ESP_LOGE(TAG, "Verifying PSRAM @ %u failed: %02X != %02X", position + i, rx_buffer[i], verify_buffer[i]); - free(tx_buffer); - free(rx_buffer); - free(verify_buffer); - return ESP_FAIL; - } - } - }; - free(tx_buffer); - free(rx_buffer); - free(verify_buffer); - ESP_LOGI(TAG, "PSRAM contents verified!"); - return ESP_OK; -} - -void fpga_test(void) { - esp_err_t res; - /*draw_message("Loading passthrough..."); - FILE* fpga_passthrough = fopen("/sd/pt.bin", "rb"); - if (fpga_passthrough == NULL) { - ESP_LOGE(TAG, "Failed to open passthrough firmware (pt.bin) from the SD card"); - return; - } - - draw_message("Loading RAM..."); - ESP_LOGI(TAG, "Loading passthrough bitstream into RAM buffer..."); - size_t fpga_passthrough_bitstream_length; - uint8_t* fpga_passthrough_bitstream = load_file_to_ram(fpga_passthrough, &fpga_passthrough_bitstream_length); - fclose(fpga_passthrough); - ESP_LOGI(TAG, "Loading passthrough bitstream into FPGA..."); - res = ice40_load_bitstream(ice40, fpga_passthrough_bitstream, fpga_passthrough_bitstream_length); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Failed to load passthrough bitstream into FPGA (%d)", res); - return; - } - free(fpga_passthrough_bitstream); - - FILE* ram_contents = fopen("/sd/ram.bin", "rb"); - if (ram_contents == NULL) { - ESP_LOGE(TAG, "Failed to open ram.bin"); - return; - } - - res = load_file_into_psram(ram_contents); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Failed to load RAM contents into PSRAM (%d)", res); - fclose(ram_contents); - return; - } - - res = verify_file_in_psram(ram_contents); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Failed to verify PSRAM contents (%d)", res); - fclose(ram_contents); - return; - }*/ - - //draw_message("Loading app..."); - /*FILE* fpga_app = fopen("/sd/app.bin", "rb"); - if (fpga_app == NULL) { - ESP_LOGE(TAG, "Failed to open app.bin"); - return; - } - - ESP_LOGI(TAG, "Loading app bitstream into RAM buffer..."); - size_t fpga_app_bitstream_length; - uint8_t* fpga_app_bitstream = load_file_to_ram(fpga_app, &fpga_app_bitstream_length); - fclose(fpga_app);*/ - - do { - reload_fpga = false; - ili9341_deinit(ili9341); - ili9341_select(ili9341, false); - vTaskDelay(200 / portTICK_PERIOD_MS); - ili9341_select(ili9341, true); - - ESP_LOGI(TAG, "Loading app bitstream into FPGA..."); - //res = ice40_load_bitstream(ice40, fpga_app_bitstream, fpga_app_bitstream_length); - res = ice40_load_bitstream(ice40, proto2_bin, proto2_bin_len); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Failed to load app bitstream into FPGA (%d)", res); - return; - } - - //free(fpga_app_bitstream); - - reset_to_menu = false; - - while (!reset_to_menu) { - vTaskDelay(100 / portTICK_PERIOD_MS); - if (reload_fpga) break; - } - - ice40_disable(ice40); - ili9341_init(ili9341); - } while (reload_fpga); -} - -esp_err_t appfs_init(void) { - return appfsInit(APPFS_PART_TYPE, APPFS_PART_SUBTYPE); -} - void appfs_store_app(void) { + draw_message("Installing app..."); esp_err_t res; appfs_handle_t handle; FILE* app_fd = fopen("/sd/gnuboy.bin", "rb"); if (app_fd == NULL) { + draw_message("Failed to open gnuboy.bin"); ESP_LOGE(TAG, "Failed to open gnuboy.bin"); + vTaskDelay(100 / portTICK_PERIOD_MS); return; } size_t app_size; uint8_t* app = load_file_to_ram(app_fd, &app_size); if (app == NULL) { + draw_message("Failed to load app to RAM"); ESP_LOGE(TAG, "Failed to load application into RAM"); + vTaskDelay(100 / portTICK_PERIOD_MS); return; } @@ -540,18 +151,24 @@ void appfs_store_app(void) { res = appfsCreateFile("gnuboy", app_size, &handle); if (res != ESP_OK) { + draw_message("Failed to create on AppFS"); ESP_LOGE(TAG, "Failed to create file on AppFS (%d)", res); + vTaskDelay(100 / portTICK_PERIOD_MS); free(app); return; } res = appfsWrite(handle, 0, app, app_size); if (res != ESP_OK) { + draw_message("Failed to write to AppFS"); ESP_LOGE(TAG, "Failed to write to file on AppFS (%d)", res); + vTaskDelay(100 / portTICK_PERIOD_MS); free(app); return; } free(app); ESP_LOGI(TAG, "Application is now stored in AppFS"); + draw_message("App installed!"); + vTaskDelay(100 / portTICK_PERIOD_MS); return; } @@ -571,11 +188,13 @@ void appfs_test(bool sdcard_ready) { appfs_handle_t fd = appfsOpen("gnuboy"); if (fd < 0) { ESP_LOGW(TAG, "gnuboy not found in appfs"); - if (sdcard_ready) { + draw_message("gnuboy not found in fs!"); + /*if (sdcard_ready) { appfs_store_app(); appfs_test(false); // Recursive, but who cares :D - } + }*/ } else { + draw_message("Booting gnuboy..."); ESP_LOGE(TAG, "booting gnuboy from appfs (%d)", fd); appfs_boot_app(fd); } @@ -584,6 +203,14 @@ void appfs_test(bool sdcard_ready) { void app_main(void) { esp_err_t res; + buttonQueue = xQueueCreate(10, sizeof(button_message_t)); + + if (buttonQueue == NULL) { + ESP_LOGE(TAG, "Failed to allocate queue"); + restart(); + } + + framebuffer = heap_caps_malloc(ILI9341_BUFFER_SIZE, MALLOC_CAP_8BIT); if (framebuffer == NULL) { ESP_LOGE(TAG, "Failed to allocate framebuffer"); @@ -612,15 +239,13 @@ void app_main(void) { ice40 = get_ice40(); bno055 = get_bno055(); rp2040 = get_rp2040(); - - //print_chip_info(); - - draw_message("Button init..."); + + graphics_task(pax_buffer, ili9341, framebuffer, NULL, "Button init..."); button_init(); rp2040_set_led_mode(rp2040, true, true); - draw_message("AppFS init..."); + graphics_task(pax_buffer, ili9341, framebuffer, NULL, "AppFS init..."); res = appfs_init(); if (res != ESP_OK) { ESP_LOGE(TAG, "AppFS init failed: %d", res); @@ -628,244 +253,121 @@ void app_main(void) { } ESP_LOGI(TAG, "AppFS initialized"); - draw_message("Mount SD card..."); + graphics_task(pax_buffer, ili9341, framebuffer, NULL, "Mount SD card..."); res = mount_sd(SD_CMD, SD_CLK, SD_D0, SD_PWR, "/sd", false, 5); bool sdcard_ready = (res == ESP_OK); + + if (sdcard_ready) { + graphics_task(pax_buffer, ili9341, framebuffer, NULL, "SD card mounted"); + } else { + graphics_task(pax_buffer, ili9341, framebuffer, NULL, "No SD card"); + } + + menu_t* menu = menu_alloc("Launcher"); - /*if (sdcard_ready) { - ESP_LOGI(TAG, "SD card mounted"); - //draw_message("AppFS test..."); - //appfs_test(sdcard_ready); - draw_message("FPGA init..."); - fpga_test(); - ESP_LOGW(TAG, "End of main function, goodbye!"); - return; - }*/ - - // - - menu_item_t* current_menu_item = NULL; appfs_handle_t current_fd = APPFS_INVALID_FD; - amount_of_items = 0; - selected_item = 0; - - do { + while (1) { current_fd = appfsNextEntry(current_fd); - if (current_fd != APPFS_INVALID_FD) { - menu_item_t* next_menu_item = malloc(sizeof(menu_item_t)); - if (current_menu_item != NULL) { - current_menu_item->next = next_menu_item; - } else { - first_menu_item = next_menu_item; - } - current_menu_item = next_menu_item; - appfsEntryInfo(current_fd, ¤t_menu_item->name, NULL); - current_menu_item->fd = current_fd; - current_menu_item->action = ACTION_NONE; - current_menu_item->next = NULL; - printf("Building menu list %u: %s\r\n", amount_of_items, current_menu_item->name); - amount_of_items++; - } - } while (current_fd != APPFS_INVALID_FD); - printf("Building menu list done, %u items\r\n", amount_of_items); - - menu_item_t* next_menu_item; - - /*next_menu_item = malloc(sizeof(menu_item_t)); - if (current_menu_item != NULL) { - current_menu_item->next = next_menu_item; - } else { - first_menu_item = next_menu_item; + if (current_fd == APPFS_INVALID_FD) break; + + const char* name = NULL; + appfsEntryInfo(current_fd, &name, NULL); + menu_args_t* args = malloc(sizeof(menu_args_t)); + args->fd = current_fd; + args->action = ACTION_APPFS; + menu_insert_item(menu, name, NULL, (void*) args, -1); } - current_menu_item = next_menu_item; - current_menu_item->fd = APPFS_INVALID_FD; - current_menu_item->action = ACTION_INSTALLER; - current_menu_item->name = installer_name; - current_menu_item->next = NULL; - amount_of_items++;*/ - - next_menu_item = malloc(sizeof(menu_item_t)); - if (current_menu_item != NULL) { - current_menu_item->next = next_menu_item; - } else { - first_menu_item = next_menu_item; - } - current_menu_item = next_menu_item; - current_menu_item->fd = APPFS_INVALID_FD; - current_menu_item->action = ACTION_FPGA; - current_menu_item->name = fpga_name; - current_menu_item->next = NULL; - amount_of_items++; - + menu_args_t* fpga_args = malloc(sizeof(menu_args_t)); + fpga_args->action = ACTION_FPGA; + menu_insert_item(menu, "FPGA test", NULL, fpga_args, -1); + menu_args_t* install_args = malloc(sizeof(menu_args_t)); + install_args->action = ACTION_INSTALLER; + menu_insert_item(menu, "Install app...", NULL, install_args, -1); + + bool render = true; + menu_args_t* menuAction = NULL; while (1) { - //bno055_task(bno055); - graphics_task(pax_buffer, ili9341, framebuffer); - //printf("Selected: %u of %u\r\n", selected_item + 1, amount_of_items); - if (start_selected) { - current_menu_item = first_menu_item; - for (uint8_t index = 0; index < selected_item; index++) { - current_menu_item = current_menu_item->next; - } - if (current_menu_item->action == ACTION_INSTALLER) { - draw_message("Not yet implemented"); - vTaskDelay(1000 / portTICK_PERIOD_MS); - start_selected = false; - } else if (current_menu_item->action == ACTION_FPGA) { - fpga_test(); - start_selected = false; - } else { - draw_message("Starting app..."); - appfs_boot_app(current_menu_item->fd); + button_message_t buttonMessage = {0}; + if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) { + uint8_t pin = buttonMessage.button; + bool value = buttonMessage.state; + switch(pin) { + case PCA9555_PIN_BTN_JOY_LEFT: + printf("Joystick horizontal %s\n", value ? "left" : "center"); + break; + case PCA9555_PIN_BTN_JOY_PRESS: + printf("Joystick %s\n", value ? "pressed" : "released"); + break; + case PCA9555_PIN_BTN_JOY_DOWN: + printf("Joystick vertical %s\n", value ? "down" : "center"); + if (value) { + menu_navigate_next(menu); + render = true; + } + break; + case PCA9555_PIN_BTN_JOY_UP: + printf("Joystick vertical %s\n", value ? "up" : "center"); + if (value) { + menu_navigate_previous(menu); + render = true; + } + break; + case PCA9555_PIN_BTN_JOY_RIGHT: + printf("Joystick horizontal %s\n", value ? "right" : "center"); + break; + case PCA9555_PIN_BTN_HOME: + printf("Home button %s\n", value ? "pressed" : "released"); + break; + case PCA9555_PIN_BTN_MENU: + printf("Menu button %s\n", value ? "pressed" : "released"); + //if (value) reset_to_menu = true; + break; + case PCA9555_PIN_BTN_START: { + printf("Start button %s\n", value ? "pressed" : "released"); + break; + } + case PCA9555_PIN_BTN_SELECT: { + printf("Select button %s\n", value ? "pressed" : "released"); + break; + } + case PCA9555_PIN_BTN_BACK: + printf("Back button %s\n", value ? "pressed" : "released"); + break; + case PCA9555_PIN_BTN_ACCEPT: + printf("Accept button %s\n", value ? "pressed" : "released"); + if (value) { + menuAction = menu_get_callback_args(menu, menu_get_position(menu)); + printf("Position: %u\n", menu_get_position(menu)); + } + break; + default: + printf("Unknown button %d %s\n", pin, value ? "pressed" : "released"); } } - } - /* - uint8_t data_out, data_in; - - enum { - I2C_REGISTER_FW_VER, - I2C_REGISTER_GPIO_DIR, - I2C_REGISTER_GPIO_IN, - I2C_REGISTER_GPIO_OUT, - I2C_REGISTER_LCD_MODE, - I2C_REGISTER_LCD_BACKLIGHT, - }; - data_out = 1 << 2; // Proto 0 pin is output - res = i2c_write_reg_n(I2C_BUS_EXT, 0x17, I2C_REGISTER_GPIO_DIR, &data_out, 1); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Failed to set GPIO direction on Pico: %d", res); - return; - } - - bool blink_state = false; - - while (1) { - data_out = blink_state << 2; - res = i2c_write_reg_n(I2C_BUS_EXT, 0x17, I2C_REGISTER_GPIO_OUT, &data_out, 1); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Failed to set GPIO value on Pico: %d", res); - return; - } - blink_state = !blink_state; - - res = i2c_read_reg(I2C_BUS_EXT, 0x17, I2C_REGISTER_GPIO_IN, &data_in, 1); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Failed to read GPIO value from Pico %d", res); - return; - } else { - printf("GPIO status: %02x\n", data_in); - } - vTaskDelay(1000 / portTICK_PERIOD_MS); - } - - // FPGA RAM passthrough test - - res = ice40_load_bitstream(ice40, proto2_bin, proto2_bin_len); - if (res != ESP_OK) { - ESP_LOGE(TAG, "Failed to program the FPGA (%d)", res); - return; - } - - uint8_t* tx_buffer = malloc(SPI_MAX_TRANSFER_SIZE); - uint8_t* rx_buffer = malloc(SPI_MAX_TRANSFER_SIZE); - - const uint8_t write_cmd = 0x02; - const uint8_t read_cmd = 0x03; - - uint32_t size_of_ram = 8388608; - uint32_t position = 0; - - ESP_LOGI(TAG, "Writing to PSRAM..."); - int64_t tx_start_time = esp_timer_get_time(); - while (position < size_of_ram) { - // First 4 bytes of the transmit buffer are used for CMD and 24-bit address - tx_buffer[0] = write_cmd; - tx_buffer[1] = (position >> 16); - tx_buffer[2] = (position >> 8) & 0xFF; - tx_buffer[3] = position & 0xFF; - - uint32_t remaining = size_of_ram - position; - uint32_t data_length = SPI_MAX_TRANSFER_SIZE - 4; - if (data_length > remaining) data_length = remaining; - - // - for (uint32_t index = 0; index < data_length; index++) { - tx_buffer[index + 4] = ((position + (index)) & 0xFF); // Generate a test pattern - } - if (ice40_transaction(ice40, tx_buffer, data_length + 4, rx_buffer, data_length + 4) != ESP_OK) { - ESP_LOGE(TAG, "Write transaction failed @ %u", remaining); - return; + if (render) { + graphics_task(pax_buffer, ili9341, framebuffer, menu, NULL); + render = false; } - position += data_length; - } - int64_t tx_done_time = esp_timer_get_time(); - printf("Write took %lld microseconds\r\n", tx_done_time - tx_start_time); - uint64_t result = (((size_of_ram) / (tx_done_time - tx_start_time))*1000*1000)/1024; - printf("%u bytes in %lld microseconds = %llu kB/s\r\n", size_of_ram, tx_done_time - tx_start_time, result); - - position = 0; // Reset position - memset(tx_buffer, 0, SPI_MAX_TRANSFER_SIZE); // Clear TX buffer - - ESP_LOGI(TAG, "Verifying PSRAM contents..."); - int64_t rx_start_time = esp_timer_get_time(); - while (position < size_of_ram) { - tx_buffer[0] = read_cmd; - tx_buffer[1] = (position >> 16); - tx_buffer[2] = (position >> 8) & 0xFF; - tx_buffer[3] = position & 0xFF; - - uint32_t remaining = size_of_ram - position; - uint32_t data_length = SPI_MAX_TRANSFER_SIZE - 4; - if (data_length > remaining) data_length = remaining; - - if (ice40_transaction(ice40, tx_buffer, data_length + 4, rx_buffer, data_length + 4) != ESP_OK) { - ESP_LOGE(TAG, "Transaction failed"); - return; - } - - for (uint32_t index = 0; index < data_length; index++) { - if (rx_buffer[index + 4] != ((position + (index)) & 0xFF)) { // Verify the test pattern - ESP_LOGE(TAG, "Verification failed @ %u + %u: %u != %u", position, index, rx_buffer[index + 4], (position + (index)) & 0xFF); + if (menuAction != NULL) { + graphics_task(pax_buffer, ili9341, framebuffer, menu, "Please wait..."); + if (menuAction->action == ACTION_APPFS) { + appfs_boot_app(menuAction->fd); + } else if (menuAction->action == ACTION_FPGA) { + graphics_task(pax_buffer, ili9341, framebuffer, menu, "FPGA TEST"); + fpga_test(ili9341, ice40, buttonQueue); + }else if (menuAction->action == ACTION_INSTALLER) { + graphics_task(pax_buffer, ili9341, framebuffer, menu, "INSTALLER"); + appfs_store_app(); } + menuAction = NULL; + render = true; } - - position += data_length; } - int64_t rx_done_time = esp_timer_get_time(); - printf("Read took %lld microseconds\r\n", rx_done_time - rx_start_time); - result = (((size_of_ram) / (rx_done_time - rx_start_time))*1000*1000)/1024; - printf("%u bytes in %lld microseconds = %llu kB/s\r\n", size_of_ram, rx_done_time - rx_start_time, result);*/ + free(framebuffer); - //ESP_LOGW(TAG, "End of main function, goodbye!"); - - rp2040_set_led_mode(rp2040, true, true); - - for (uint8_t led = 0; led < 5; led++) { - rp2040_set_led_value(rp2040, led, 0, 0, 0); - } - - for (uint8_t value = 0; value < 255; value++) { - rp2040_set_lcd_backlight(rp2040, 254 - value); - } - - for (uint8_t value = 0; value < 255; value++) { - rp2040_set_lcd_backlight(rp2040, value); - } - - while (1) { - for (uint8_t led = 0; led < 5; led++) { - rp2040_set_led_value(rp2040, led, 255, 0, 0 ); - vTaskDelay(50 / portTICK_PERIOD_MS); - rp2040_set_led_value(rp2040, led, 0, 255, 0 ); - vTaskDelay(50 / portTICK_PERIOD_MS); - rp2040_set_led_value(rp2040, led, 0, 0, 255); - vTaskDelay(50 / portTICK_PERIOD_MS); - rp2040_set_led_value(rp2040, led, 0, 0, 0 ); - } - } } diff --git a/main/menu.c b/main/menu.c new file mode 100644 index 0000000..2063c41 --- /dev/null +++ b/main/menu.c @@ -0,0 +1,228 @@ +#include +#include +#include "pax_gfx.h" +#include "menu.h" + +menu_t* menu_alloc(const char* aTitle) { + if (aTitle == NULL) return NULL; + menu_t* menu = malloc(sizeof(menu_t)); + if (menu == NULL) return NULL; + size_t titleSize = strlen(aTitle) + 1; + menu->title = malloc(titleSize); + if (menu->title == NULL) { + free(menu); + return NULL; + } + memcpy(menu->title, aTitle, titleSize); + menu->firstItem = NULL; + menu->length = 0; + menu->position = 0; + return menu; +} + +void _menu_free_item(menu_item_t* aMenuItem) { + free(aMenuItem->label); + if (aMenuItem->callbackArgs != NULL) { + free(aMenuItem->callbackArgs); + } + free(aMenuItem); +} + +void menu_free(menu_t* aMenu) { + if (aMenu == NULL) return; + free(aMenu->title); + menu_item_t* currentItem = aMenu->firstItem; + while (currentItem != NULL) { + menu_item_t* nextItem = currentItem->nextItem; + _menu_free_item(currentItem); + currentItem = nextItem; + } + free(aMenu); +} + +menu_item_t* _menu_find_item(menu_t* aMenu, size_t aPosition) { + menu_item_t* currentItem = aMenu->firstItem; + if (currentItem == NULL) return NULL; + size_t index = 0; + while (index < aPosition) { + if (currentItem->nextItem == NULL) break; + currentItem = currentItem->nextItem; + index++; + } + return currentItem; +} + +menu_item_t* _menu_find_last_item(menu_t* aMenu) { + menu_item_t* lastItem = aMenu->firstItem; + if (lastItem == NULL) return NULL; + while (lastItem->nextItem != NULL) { + lastItem = lastItem->nextItem; + } + return lastItem; +} + +bool menu_insert_item(menu_t* aMenu, const char* aLabel, menu_callback_t aCallback, void* aCallbackArgs, size_t aPosition) { + if (aMenu == NULL) return false; + menu_item_t* newItem = malloc(sizeof(menu_item_t)); + if (newItem == NULL) return false; + size_t labelSize = strlen(aLabel) + 1; + newItem->label = malloc(labelSize); + if (newItem->label == NULL) { + free(newItem); + return NULL; + } + memcpy(newItem->label, aLabel, labelSize); + newItem->callback = aCallback; + newItem->callbackArgs = aCallbackArgs; + if (aMenu->firstItem == NULL) { + newItem->nextItem = NULL; + newItem->previousItem = NULL; + aMenu->firstItem = newItem; + } else { + if (aPosition >= aMenu->length) { + newItem->previousItem = _menu_find_last_item(aMenu); + newItem->nextItem = NULL; + newItem->previousItem->nextItem = newItem; + } else { + newItem->nextItem = _menu_find_item(aMenu, aPosition); + newItem->previousItem = newItem->nextItem->previousItem; // Copy pointer to previous item to new item + if (newItem->nextItem != NULL) newItem->nextItem->previousItem = newItem; // Replace pointer to previous item with new item + if (newItem->previousItem != NULL) newItem->previousItem->nextItem = newItem; // Replace pointer to next item in previous item + } + } + aMenu->length++; + return true; +} + +bool menu_remove_item(menu_t* aMenu, size_t aPosition) { + if (aMenu == NULL) return false; // Can't delete an item from a menu that doesn't exist + if (aMenu->length <= aPosition) return false; // Can't delete an item that doesn't exist + menu_item_t* item; + + if (aPosition == 0) { + item = aMenu->firstItem; + if (item == NULL) return false; // Can't delete if no linked list is allocated + if (item->nextItem != NULL) { + aMenu->firstItem = item->nextItem; + aMenu->firstItem->previousItem = NULL; + } else { + aMenu->firstItem = NULL; + } + } else { + item = _menu_find_item(aMenu, aPosition); + if (item == NULL) return false; + if (item->previousItem != NULL) item->previousItem->nextItem = item->nextItem; + if (item->nextItem != NULL) item->nextItem->previousItem = item->previousItem; + } + free(item->label); + free(item); + aMenu->length--; + return true; +} + +bool menu_navigate_to(menu_t* aMenu, size_t aPosition) { + if (aMenu == NULL) return false; + if (aMenu->length < 1) return false; + aMenu->position = aPosition; + if (aMenu->position >= aMenu->length) aMenu->position = aMenu->length - 1; + return true; +} + +void menu_navigate_previous(menu_t* aMenu) { + if (aMenu == NULL) return; + if (aMenu->length < 1) return; + aMenu->position--; + if (aMenu->position > aMenu->length) { + aMenu->position = aMenu->length - 1; + } +} + +void menu_navigate_next(menu_t* aMenu) { + if (aMenu == NULL) return; + if (aMenu->length < 1) return; + aMenu->position = (aMenu->position + 1) % aMenu->length; +} + +size_t menu_get_position(menu_t* aMenu) { + return aMenu->position; +} + +void* menu_get_callback_args(menu_t* aMenu, size_t aPosition) { + menu_item_t* item = _menu_find_item(aMenu, aPosition); + if (item == NULL) return NULL; + return item->callbackArgs; +} + +void menu_debug(menu_t* aMenu) { + if (aMenu == NULL) { + printf("Menu pointer is NULL\n"); + return; + } + printf("Title: %s\n", aMenu->title); + printf("Length: %u\n", aMenu->length); + printf("Position: %u\n", aMenu->position); + menu_item_t* item = aMenu->firstItem; + if (item == NULL) { + printf("Menu contains no items\n"); + } else { + while (item != NULL) { + printf("> %s\n", item->label); + item = item->nextItem; + } + } + printf("------\n"); +} + +void menu_render(pax_buf_t *aBuffer, menu_t* aMenu, float aPosX, float aPosY, float aWidth, float aHeight) { + size_t itemOffset = 0; + pax_col_t fgColor = 0xFF000000; + pax_col_t bgColor = 0xFFFFFFFF; + pax_col_t borderColor = 0xFF000000; + pax_col_t titleColor = 0xFFFFFFFF; + float scroll = 0; + float entry_height = 18 + 2; + size_t maxItems = aBuffer->height / entry_height; + + size_t entry_offset = scroll / entry_height; + scroll -= entry_offset * entry_height; + + float posY = aPosY; + + pax_clip(aBuffer, aPosX, aPosY, aWidth, aHeight); + pax_simple_rect(aBuffer, bgColor, aPosX, aPosY, aWidth, aHeight); + + if (maxItems > 1) { + maxItems--; + pax_simple_rect(aBuffer, borderColor, aPosX, posY, aWidth, entry_height); + pax_simple_line(aBuffer, titleColor, aPosX + 1, aPosY + entry_height, aPosX + aWidth - 2, aPosY + entry_height - 1); + pax_clip(aBuffer, aPosX + 1, posY + 1, aWidth - 2, entry_height - 2); + pax_draw_text(aBuffer, titleColor, NULL, entry_height - 2, aPosX + 1, posY + 1, aMenu->title); + posY += entry_height; + } + pax_clip(aBuffer, aPosX, posY, aWidth, aHeight); + pax_outline_rect(aBuffer, borderColor, aPosX, aPosY, aWidth, aHeight); + + + posY -= scroll; + for (size_t index = itemOffset; (index < itemOffset + maxItems) && (index < aMenu->length); index++) { + menu_item_t* item = _menu_find_item(aMenu, index); + if (item == NULL) { + printf("Render error: item is NULL at %u\n", index); + break; + } + if (index == aMenu->position) { + pax_clip(aBuffer, aPosX, posY, aWidth, entry_height); + pax_simple_rect(aBuffer, fgColor, aPosX + 1, posY, aWidth - 2, entry_height); + pax_clip(aBuffer, aPosX + 1, posY + 1, aWidth - 2, entry_height - 2); + pax_draw_text(aBuffer, bgColor, NULL, entry_height - 2, aPosX + 1, posY + 1, item->label); + } else { + pax_clip(aBuffer, aPosX, posY, aWidth, entry_height); + pax_simple_rect(aBuffer, bgColor, aPosX + 1, posY, aWidth - 2, entry_height); + pax_clip(aBuffer, aPosX + 1, posY + 1, aWidth - 2, entry_height - 2); + pax_draw_text(aBuffer, fgColor, NULL, entry_height - 2, aPosX + 1, posY + 1, item->label); + } + posY += entry_height; + } + + pax_noclip(aBuffer); +} diff --git a/qemu.sh b/qemu.sh index fc4ef78..8f92b67 100755 --- a/qemu.sh +++ b/qemu.sh @@ -16,7 +16,7 @@ dd if=/dev/zero bs=1M count=16 of=flash.bin dd if=bootloader/bootloader.bin bs=1 seek=$((0x1000)) of=flash.bin conv=notrunc # Copy the partition table into the file -dd if=partition_table/partition-table.bin bs=1 seek=$((0x8000)) of=flash.bin conv=notrunc +dd if=partition_table/partition-table.bin bs=1 seek=$((0x9000)) of=flash.bin conv=notrunc # Copy the firmware into the file dd if=main.bin bs=1 seek=$((0x10000)) of=flash.bin conv=notrunc