From 81295683b437c19775df07031db684818b07cbea Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Mon, 17 Jul 2023 17:38:21 +0100 Subject: [PATCH 01/70] pico: don't attempt picovision render before a mode is set Which currently doesn't happen --- 32blit-pico/display/picovision.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/32blit-pico/display/picovision.cpp b/32blit-pico/display/picovision.cpp index e8f52702b..8d20bd8c0 100644 --- a/32blit-pico/display/picovision.cpp +++ b/32blit-pico/display/picovision.cpp @@ -40,7 +40,7 @@ static bool display_enabled = false; static uint8_t need_mode_change = 2; static int cur_resolution = 0; -static volatile bool do_render = true; +static volatile bool do_render = false; static uint16_t blend_buf[512]; @@ -464,4 +464,7 @@ void display_mode_changed(blit::ScreenMode new_mode, blit::SurfaceTemplate &new_ } need_mode_change = 2; // make sure to update both banks + + if(!display_enabled) + do_render = true; } From bb9d7f6310454a9b380bb10af93008abaf4f677c Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 7 Mar 2023 16:41:30 +0000 Subject: [PATCH 02/70] Header function ptrs for pico builds too --- launcher-shared/executable.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher-shared/executable.hpp b/launcher-shared/executable.hpp index c8b6219a3..f34cd54d7 100644 --- a/launcher-shared/executable.hpp +++ b/launcher-shared/executable.hpp @@ -3,7 +3,7 @@ constexpr uint32_t blit_game_magic = 0x54494C42; // "BLIT" -#ifdef TARGET_32BLIT_HW +#if defined(TARGET_32BLIT_HW) || defined(PICO_BUILD) // TODO: generic "is hardware" define? using BlitRenderFunction = void(*)(uint32_t); using BlitTickFunction = int(*)(uint32_t); using BlitInitFunction = bool(*)(uint32_t); From 03b38f2f1665881dd1bfba247449b25d73c489e7 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 3 Mar 2023 15:12:48 +0000 Subject: [PATCH 03/70] pico: output a sort-of .blit file as well as a standalone .uf2 Right now it's just a BLIT header instead of boot2, built at a 512k offset to allow launching one thing without position independent code --- 32blit-pico/CMakeLists.txt | 61 ++++++++- 32blit-pico/memmap_blit.ld | 261 +++++++++++++++++++++++++++++++++++++ 32blit-pico/startup.S | 11 ++ 3 files changed, 327 insertions(+), 6 deletions(-) create mode 100644 32blit-pico/memmap_blit.ld create mode 100644 32blit-pico/startup.S diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index 28053a1d0..94c192904 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -170,21 +170,55 @@ endif() target_compile_definitions(BlitHalPico INTERFACE ${BLIT_BOARD_DEFINITIONS}) target_link_libraries(BlitHalPico INTERFACE ${BLIT_BOARD_LIBRARIES}) +# some file paths we need later +set(LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_blit.ld PARENT_SCOPE) +set(STARTUP_SRC ${CMAKE_CURRENT_LIST_DIR}/startup.S PARENT_SCOPE) + # functions function(blit_executable NAME) message(STATUS "Processing ${NAME}") blit_executable_args(${ARGN}) - add_executable(${NAME} ${SOURCES}) + add_library(${NAME} STATIC ${SOURCES}) target_compile_definitions(${NAME} PRIVATE PICO_EMBED_XIP_SETUP=1) target_link_libraries(${NAME} BlitHalPico BlitEngine) - pico_enable_stdio_uart(${NAME} 1) - pico_enable_stdio_usb(${NAME} 0) + # standalone .uf2 + add_executable(${NAME}-standalone) + target_link_libraries(${NAME}-standalone ${NAME}) + pico_enable_stdio_uart(${NAME}-standalone 1) + pico_enable_stdio_usb(${NAME}-standalone 0) + + pico_add_extra_outputs(${NAME}-standalone) + + # loadable .blit + add_executable(${NAME}-bl) + target_link_libraries(${NAME}-bl ${NAME}) + + pico_set_linker_script(${NAME}-bl ${LINKER_SCRIPT}) + target_sources(${NAME}-bl PRIVATE ${STARTUP_SRC}) + + pico_enable_stdio_uart(${NAME}-bl 1) + pico_enable_stdio_usb(${NAME}-bl 0) - pico_add_extra_outputs(${NAME}) + pico_add_bin_output(${NAME}-bl) - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${NAME}.uf2 + # Ideally we want the .blit filename to match the .elf, but TARGET_FILE_BASE_NAME isn't always available + # (This only affects the firmware updater as it's the only thing setting a custom OUTPUT_NAME) + if(${CMAKE_VERSION} VERSION_LESS "3.15.0") + set(BLIT_FILENAME ${NAME}.blit) + else() + set(BLIT_FILENAME $.blit) + endif() + + # no relocs, just copy it + set(BIN_NAME "$>,$,$>.bin") + add_custom_command(TARGET ${NAME}-bl POST_BUILD + COMMENT "Building ${NAME}.blit" + COMMAND cp ${BIN_NAME} ${BLIT_FILENAME} + ) + + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${NAME}-standalone.uf2 DESTINATION bin ) endfunction() @@ -219,7 +253,22 @@ function(blit_metadata TARGET FILE) ) # add the generated source - target_sources(${TARGET} PRIVATE ${METADATA_SOURCE}) + target_sources(${TARGET}-standalone PRIVATE ${METADATA_SOURCE}) + + # real .blit metadata + if(${CMAKE_VERSION} VERSION_LESS "3.15.0") + set(BLIT_FILENAME ${TARGET}.blit) + else() + set(BLIT_FILENAME $.blit) + endif() + + add_custom_command( + TARGET ${TARGET}-bl POST_BUILD + COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${32BLIT_TOOLS_EXECUTABLE} metadata --config ${FILE} --file ${CMAKE_CURRENT_BINARY_DIR}/${BLIT_FILENAME} + ) + + # force relink on change so that the post-build commands are rerun + set_property(TARGET ${TARGET}-bl APPEND PROPERTY LINK_DEPENDS ${FILE} ${METADATA_DEPENDS}) # avoid the fallback to target name target_compile_definitions(${TARGET} PRIVATE PICO_NO_BI_PROGRAM_NAME=1) diff --git a/32blit-pico/memmap_blit.ld b/32blit-pico/memmap_blit.ld new file mode 100644 index 000000000..846d2ff3c --- /dev/null +++ b/32blit-pico/memmap_blit.ld @@ -0,0 +1,261 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + FLASH(rx) : ORIGIN = 0x10080000, LENGTH = 2048k + RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k + SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k + SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k +} + +ENTRY(_entry_point) + +SECTIONS +{ + /* No second stage bootloader as we're using a seperate loader + Instead, there's a header here + */ + + .flash_begin : { + __flash_binary_start = .; + } > FLASH + + /*.boot2 : { + __boot2_start__ = .; + KEEP (*(.boot2)) + __boot2_end__ = .; + } > FLASH + + + ASSERT(__boot2_end__ - __boot2_start__ == 256, + "ERROR: Pico second stage bootloader must be 256 bytes in size") + */ + + .header : { + KEEP (*(.header)) + . = ALIGN(256); + } > FLASH + + /* The second stage will always enter the image at the start of .text. + The debugger will use the ELF entry point, which is the _entry_point + symbol if present, otherwise defaults to start of .text. + This can be used to transfer control back to the bootrom on debugger + launches only, to perform proper flash setup. + */ + + .text : { + __logical_binary_start = .; + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + KEEP (*(.reset)) + /* TODO revisit this now memset/memcpy/float in ROM */ + /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from + * FLASH ... we will include any thing excluded here in .data below by default */ + *(.init) + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*) + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.eh_frame*) + . = ALIGN(4); + } > FLASH + + .rodata : { + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH + __binary_info_end = .; + . = ALIGN(4); + + /* End of .text-like segments */ + __etext = .; + + .ram_vector_table (COPY): { + *(.ram_vector_table) + } > RAM + + .data : { + __data_start__ = .; + *(vtable) + + *(.time_critical*) + + /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(4); + + *(.data*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + + *(.jcr) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + } > RAM AT> FLASH + + .uninitialized_data (COPY): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X AT > FLASH + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y AT > FLASH + __scratch_y_source__ = LOADADDR(.scratch_y); + + .bss : { + . = ALIGN(4); + __bss_start__ = .; + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __end__ = .; + end = __end__; + *(.heap*) + __HeapLimit = .; + } > RAM + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + .stack1_dummy (COPY): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (COPY): + { + *(.stack*) + } > SCRATCH_Y + + .flash_end : { + LONG(0x12345678) /*workaround for __flash_binary_end pointing past the end*/ + __flash_binary_end = .; + } > FLASH + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") + /* todo assert on extra code */ + + __flash_binary_size = __flash_binary_end - __flash_binary_start; +} + diff --git a/32blit-pico/startup.S b/32blit-pico/startup.S new file mode 100644 index 000000000..68eb6fa72 --- /dev/null +++ b/32blit-pico/startup.S @@ -0,0 +1,11 @@ +.cpu cortex-m0 +.thumb + +.section .header, "a" + +.word 0x54494C42 +.word 0 // render +.word 0 // tick +.word 0 // init +.word __flash_binary_size +.word __flash_binary_start // unused From 7d9673df555b905e018f10fc702bcac7953a8976 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 7 Mar 2023 23:53:27 +0000 Subject: [PATCH 04/70] pico: pass board defs to blit builds Seems a bit weird, but needed to get the right framebuffer size --- 32blit-pico/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index 94c192904..e5aae59e2 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -170,6 +170,8 @@ endif() target_compile_definitions(BlitHalPico INTERFACE ${BLIT_BOARD_DEFINITIONS}) target_link_libraries(BlitHalPico INTERFACE ${BLIT_BOARD_LIBRARIES}) +set(BLIT_BOARD_DEFINITIONS ${BLIT_BOARD_DEFINITIONS} PARENT_SCOPE) + # some file paths we need later set(LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_blit.ld PARENT_SCOPE) set(STARTUP_SRC ${CMAKE_CURRENT_LIST_DIR}/startup.S PARENT_SCOPE) @@ -193,6 +195,7 @@ function(blit_executable NAME) # loadable .blit add_executable(${NAME}-bl) + target_compile_definitions(${NAME}-bl PRIVATE ${BLIT_BOARD_DEFINITIONS}) # need these for framebuffer config target_link_libraries(${NAME}-bl ${NAME}) pico_set_linker_script(${NAME}-bl ${LINKER_SCRIPT}) From 3bd92b6f63041d7698febf717c9cf2d6c792e601 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 3 Mar 2023 16:23:12 +0000 Subject: [PATCH 05/70] pico: add list_installed_games --- 32blit-pico/CMakeLists.txt | 7 +++++++ 32blit-pico/main.cpp | 35 ++++++++++++++++++++++++++++++++++- CMakeLists.txt | 3 ++- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index e5aae59e2..1acc16f4d 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -47,6 +47,7 @@ target_link_libraries(BlitHalPico INTERFACE pico_multicore pico_stdlib pico_unique_id pico_rand tinyusb_device FatFsBlitAPI + LauncherShared ) target_include_directories(BlitHalPico INTERFACE @@ -175,6 +176,7 @@ set(BLIT_BOARD_DEFINITIONS ${BLIT_BOARD_DEFINITIONS} PARENT_SCOPE) # some file paths we need later set(LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_blit.ld PARENT_SCOPE) set(STARTUP_SRC ${CMAKE_CURRENT_LIST_DIR}/startup.S PARENT_SCOPE) +set(LAUNCHERSHARED_DIR ${CMAKE_CURRENT_LIST_DIR}/../launcher-shared PARENT_SCOPE) # functions function(blit_executable NAME) @@ -186,6 +188,11 @@ function(blit_executable NAME) target_link_libraries(${NAME} BlitHalPico BlitEngine) # standalone .uf2 + # TODO: import pico hal if standalone build + if(NOT TARGET LauncherShared) + add_subdirectory(${LAUNCHERSHARED_DIR} launcher-shared) + endif() + add_executable(${NAME}-standalone) target_link_libraries(${NAME}-standalone ${NAME}) pico_enable_stdio_uart(${NAME}-standalone 1) diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index 2f33434ae..f55926992 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -1,4 +1,5 @@ #include +#include #include "hardware/clocks.h" #include "hardware/structs/rosc.h" @@ -20,6 +21,8 @@ #include "storage.hpp" #include "usb.hpp" +#include "executable.hpp" + #include "engine/api_private.hpp" using namespace blit; @@ -101,6 +104,36 @@ static GameMetadata get_metadata() { return ret; } +static void list_installed_games(std::function callback) { + const unsigned int game_block_size = 64 * 1024; // this is the 32blit's flash erase size, some parts of the API depend on this... + + for(uint32_t off = 0; off < PICO_FLASH_SIZE_BYTES;) { + auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + off); + + // check header magic + if(header->magic != blit_game_magic) { + off += game_block_size; + continue; + } + + auto size = header->end; + + // check metadata + auto meta_offset = off + size; + if(memcmp((char *)(XIP_NOCACHE_NOALLOC_BASE + meta_offset), "BLITMETA", 8) != 0) { + off += ((size - 1) / game_block_size + 1) * game_block_size; + continue; + } + + // add metadata size + size += *(uint16_t *)(XIP_NOCACHE_NOALLOC_BASE + meta_offset + 8) + 10; + + callback((const uint8_t *)(XIP_NOCACHE_NOALLOC_BASE + off), off / game_block_size, size); + + off += ((size - 1) / game_block_size + 1) * game_block_size; + } +} + // blit API static const blit::APIConst blit_api_const { blit::api_version_major, blit::api_version_minor, @@ -160,7 +193,7 @@ static const blit::APIConst blit_api_const { nullptr, // cdc_write nullptr, // cdc_read - nullptr, // list_installed_games + ::list_installed_games, nullptr, // can_launch }; diff --git a/CMakeLists.txt b/CMakeLists.txt index 62ca9dd83..f587669f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,10 +12,11 @@ else() endif() find_package (32BLIT CONFIG REQUIRED PATHS .) -add_subdirectory(utilities) add_subdirectory(launcher-shared) +add_subdirectory(utilities) + if(32BLIT_HW) add_subdirectory(32blit-stm32) add_subdirectory(firmware) From 402e32010cac96cef0e7c2e91798422375536ea9 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 3 Mar 2023 17:01:48 +0000 Subject: [PATCH 06/70] pico: Add some launch support --- 32blit-pico/main.cpp | 46 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index f55926992..0e5b30bca 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -3,6 +3,7 @@ #include "hardware/clocks.h" #include "hardware/structs/rosc.h" +#include "hardware/structs/scb.h" #include "hardware/vreg.h" #include "hardware/timer.h" #include "pico/binary_info.h" @@ -27,6 +28,8 @@ using namespace blit; +const unsigned int game_block_size = 64 * 1024; // this is the 32blit's flash erase size, some parts of the API depend on this... + static blit::AudioChannel channels[CHANNEL_COUNT]; // override terminate handler to save ~20-30k @@ -104,9 +107,44 @@ static GameMetadata get_metadata() { return ret; } -static void list_installed_games(std::function callback) { - const unsigned int game_block_size = 64 * 1024; // this is the 32blit's flash erase size, some parts of the API depend on this... +static bool launch(const char *path) { + if(strncmp(path, "flash:/", 7) == 0) { + int offset = atoi(path + 7) * game_block_size; + + // TODO: check valid + auto addr = XIP_BASE + offset + 256; + + // disable all irqs + irq_set_mask_enabled(~0u, false); + + // set VTOR + scb_hw->vtor = addr; + + asm volatile( + "ldr r0, [%0]\n" + "ldr r1, [%0, #4]\n" + "msr msp, r0\n" // set SP + "bx r1" // branch to reset + : + : "r" (addr) + : "r0", "r1" + ); + // not reached + } + + return false; +} +static blit::CanLaunchResult can_launch(const char *path) { + if(strncmp(path, "flash:/", 7) == 0) { + // assume anything flashed is compatible for now + return blit::CanLaunchResult::Success; + } + + return blit::CanLaunchResult::UnknownType; +} + +static void list_installed_games(std::function callback) { for(uint32_t off = 0; off < PICO_FLASH_SIZE_BYTES;) { auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + off); @@ -169,7 +207,7 @@ static const blit::APIConst blit_api_const { nullptr, // decode_jpeg_buffer nullptr, // decode_jpeg_file - nullptr, // launch + ::launch, nullptr, // erase_game nullptr, // get_type_handler_metadata @@ -194,7 +232,7 @@ static const blit::APIConst blit_api_const { nullptr, // cdc_read ::list_installed_games, - nullptr, // can_launch + ::can_launch, }; static blit::APIData blit_api_data; From 3cf8ccdc623084ae971721be58286277be6866ac Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 3 Mar 2023 17:19:19 +0000 Subject: [PATCH 07/70] Build launcher for pico It's... not unusable --- CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f587669f3..635161455 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,9 +23,7 @@ if(32BLIT_HW) add_subdirectory(firmware-update) endif() -if(NOT 32BLIT_PICO) - add_subdirectory(launcher) -endif() +add_subdirectory(launcher) # if the examples are there, build them too if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/examples/CMakeLists.txt) From 6585be6e59f8684e7da3488970e9ed217497d477 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Mon, 6 Mar 2023 22:46:11 +0000 Subject: [PATCH 08/70] pico: some kind of basic loader Launches the first thing it finds. has some fixed locations for API consts/data. --- 32blit-pico/CMakeLists.txt | 2 + 32blit-pico/loader/CMakeLists.txt | 19 +++ 32blit-pico/loader/loader.cpp | 22 +++ 32blit-pico/main.cpp | 4 + 32blit-pico/memmap_blit_loader.ld | 267 ++++++++++++++++++++++++++++++ 5 files changed, 314 insertions(+) create mode 100644 32blit-pico/loader/CMakeLists.txt create mode 100644 32blit-pico/loader/loader.cpp create mode 100644 32blit-pico/memmap_blit_loader.ld diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index 1acc16f4d..cc3ddf0dc 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -284,3 +284,5 @@ function(blit_metadata TARGET FILE) target_compile_definitions(${TARGET} PRIVATE PICO_NO_BI_PROGRAM_NAME=1) endfunction() + +add_subdirectory(loader) diff --git a/32blit-pico/loader/CMakeLists.txt b/32blit-pico/loader/CMakeLists.txt new file mode 100644 index 000000000..c5e563165 --- /dev/null +++ b/32blit-pico/loader/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(blit-loader loader.cpp) +target_link_libraries(blit-loader BlitHalPico BlitEngine) +target_link_options(blit-loader PUBLIC -specs=nano.specs -u _printf_float) +target_compile_definitions(blit-loader PRIVATE BUILD_LOADER PICO_EMBED_XIP_SETUP=1) + +pico_set_linker_script(blit-loader ${CMAKE_CURRENT_LIST_DIR}/../memmap_blit_loader.ld) + +pico_enable_stdio_uart(blit-loader 1) +pico_enable_stdio_usb(blit-loader 0) + +pico_add_extra_outputs(blit-loader) + +pico_set_program_name(blit-loader "32blit loader") +pico_set_program_description(blit-loader "Loads 32blit games") +pico_set_program_url(blit-loader "https://github.com/32blit/32blit-sdk") + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/blit-loader.uf2 + DESTINATION bin +) diff --git a/32blit-pico/loader/loader.cpp b/32blit-pico/loader/loader.cpp new file mode 100644 index 000000000..65b908c56 --- /dev/null +++ b/32blit-pico/loader/loader.cpp @@ -0,0 +1,22 @@ +#include "32blit.hpp" +#include "engine/api_private.hpp" + +using namespace blit; + +void init() { + bool done = false; + // launch the first thing we find + // TODO: find launcher + api.list_installed_games([&done](const uint8_t *ptr, uint32_t block, uint32_t size){ + if(!done) { + auto path = "flash:/" + std::to_string(block) + ".blit"; + done = api.launch(path.c_str()); + } + }); +} + +void render(uint32_t time) { +} + +void update(uint32_t time) { +} diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index 0e5b30bca..8b8baa424 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -173,6 +173,7 @@ static void list_installed_games(std::function FLASH + + .boot2 : { + __boot2_start__ = .; + KEEP (*(.boot2)) + __boot2_end__ = .; + } > FLASH + + ASSERT(__boot2_end__ - __boot2_start__ == 256, + "ERROR: Pico second stage bootloader must be 256 bytes in size") + + /* The second stage will always enter the image at the start of .text. + The debugger will use the ELF entry point, which is the _entry_point + symbol if present, otherwise defaults to start of .text. + This can be used to transfer control back to the bootrom on debugger + launches only, to perform proper flash setup. + */ + + .text : { + __logical_binary_start = .; + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + + . = ALIGN(256); + __api_const_start = .; + KEEP (*(*.api_const)) + __api_const_end = .; + + KEEP (*(.reset)) + /* TODO revisit this now memset/memcpy/float in ROM */ + /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from + * FLASH ... we will include any thing excluded here in .data below by default */ + *(.init) + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*) + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.eh_frame*) + . = ALIGN(4); + } > FLASH + + .rodata : { + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH + __binary_info_end = .; + . = ALIGN(4); + + /* End of .text-like segments */ + __etext = .; + + .api_data : { + __api_data_start = .; + KEEP (*(*.api_data)) + } + + .ram_vector_table (COPY): { + . = ALIGN(256); + *(.ram_vector_table) + } > RAM + + .data : { + __data_start__ = .; + *(vtable) + + *(.time_critical*) + + /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(4); + + *(.data*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + + *(.jcr) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + } > RAM AT> FLASH + + .uninitialized_data (COPY): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X AT > FLASH + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y AT > FLASH + __scratch_y_source__ = LOADADDR(.scratch_y); + + .bss : { + . = ALIGN(4); + __bss_start__ = .; + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __end__ = .; + end = __end__; + *(.heap*) + __HeapLimit = .; + } > RAM + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + .stack1_dummy (COPY): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (COPY): + { + *(.stack*) + } > SCRATCH_Y + + .flash_end : { + __flash_binary_end = .; + } > FLASH + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") + /* todo assert on extra code */ + + ASSERT(__api_const_start == ORIGIN(FLASH) + 512, "api_const must be 512 bytes from the start of flash") + ASSERT(__api_data_start == ORIGIN(RAM), "api_data must be at the start of RAM") +} + From f8cea513a92197138781796ea20de30547a7ea29 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 7 Mar 2023 12:29:15 +0000 Subject: [PATCH 09/70] HACK: disable picosystem-hardware-test --- utilities/picosystem-hardware-test/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/utilities/picosystem-hardware-test/CMakeLists.txt b/utilities/picosystem-hardware-test/CMakeLists.txt index 465ea66bd..3f75b0cbe 100644 --- a/utilities/picosystem-hardware-test/CMakeLists.txt +++ b/utilities/picosystem-hardware-test/CMakeLists.txt @@ -2,10 +2,11 @@ cmake_minimum_required(VERSION 3.9...3.26) project (picosystem-hardware-test) find_package (32BLIT CONFIG REQUIRED PATHS ../..) -if(NOT PICO_BOARD STREQUAL "pimoroni_picosystem") +# FIXME +#if(NOT PICO_BOARD STREQUAL "pimoroni_picosystem") # Hooks into the Pico SDK to get battery charge and VBUS status return() -endif() +#endif() blit_executable (picosystem-hardware-test hardware-test.cpp) blit_metadata (picosystem-hardware-test metadata.yml) From e92536debdc69f1bd94d01a9240a5e1df34fb904 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 7 Mar 2023 16:49:54 +0000 Subject: [PATCH 10/70] pico: WIP user split No update/tick, page-flipping doesn't work, firmware uses 130k RAM --- 32blit-pico/CMakeLists.txt | 12 +++--- 32blit-pico/main.cpp | 19 ++++++++- 32blit-pico/memmap_blit.ld | 20 +++++----- 32blit-pico/startup.S | 80 ++++++++++++++++++++++++++++++++++++-- 32blit-pico/startup.cpp | 56 ++++++++++++++++++++++++++ 5 files changed, 167 insertions(+), 20 deletions(-) create mode 100644 32blit-pico/startup.cpp diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index cc3ddf0dc..4e480cbd5 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -175,7 +175,7 @@ set(BLIT_BOARD_DEFINITIONS ${BLIT_BOARD_DEFINITIONS} PARENT_SCOPE) # some file paths we need later set(LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_blit.ld PARENT_SCOPE) -set(STARTUP_SRC ${CMAKE_CURRENT_LIST_DIR}/startup.S PARENT_SCOPE) +set(STARTUP_SRC ${CMAKE_CURRENT_LIST_DIR}/startup.S ${CMAKE_CURRENT_LIST_DIR}/startup.cpp PARENT_SCOPE) set(LAUNCHERSHARED_DIR ${CMAKE_CURRENT_LIST_DIR}/../launcher-shared PARENT_SCOPE) # functions @@ -184,8 +184,8 @@ function(blit_executable NAME) blit_executable_args(${ARGN}) add_library(${NAME} STATIC ${SOURCES}) + target_link_libraries(${NAME} BlitEngine pico_platform_headers) target_compile_definitions(${NAME} PRIVATE PICO_EMBED_XIP_SETUP=1) - target_link_libraries(${NAME} BlitHalPico BlitEngine) # standalone .uf2 # TODO: import pico hal if standalone build @@ -194,7 +194,7 @@ function(blit_executable NAME) endif() add_executable(${NAME}-standalone) - target_link_libraries(${NAME}-standalone ${NAME}) + target_link_libraries(${NAME}-standalone ${NAME} BlitHalPico) pico_enable_stdio_uart(${NAME}-standalone 1) pico_enable_stdio_usb(${NAME}-standalone 0) @@ -203,9 +203,11 @@ function(blit_executable NAME) # loadable .blit add_executable(${NAME}-bl) target_compile_definitions(${NAME}-bl PRIVATE ${BLIT_BOARD_DEFINITIONS}) # need these for framebuffer config - target_link_libraries(${NAME}-bl ${NAME}) + target_compile_options(${NAME}-bl PRIVATE -ffunction-sections -fdata-sections) + target_link_libraries(${NAME}-bl ${NAME} pico_bit_ops pico_cxx_options pico_divider pico_double pico_int64_ops pico_float pico_malloc pico_mem_ops) + target_link_options(${NAME}-bl PRIVATE --specs=nosys.specs LINKER:--script=${LINKER_SCRIPT} LINKER:--gc-sections) + set_property(TARGET ${NAME}-bl APPEND PROPERTY LINK_DEPENDS ${LINKER_SCRIPT}) - pico_set_linker_script(${NAME}-bl ${LINKER_SCRIPT}) target_sources(${NAME}-bl PRIVATE ${STARTUP_SRC}) pico_enable_stdio_uart(${NAME}-bl 1) diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index 8b8baa424..592392727 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -111,6 +111,21 @@ static bool launch(const char *path) { if(strncmp(path, "flash:/", 7) == 0) { int offset = atoi(path + 7) * game_block_size; + auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + offset); + // check header magic + device + if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) + return false; + + if(header->init) { + if(!header->init(0)) + return false; + + blit::render = header->render; + // FIXME: tick + + return true; + } + // TODO: check valid auto addr = XIP_BASE + offset + 256; @@ -148,8 +163,8 @@ static void list_installed_games(std::functionmagic != blit_game_magic) { + // check header magic + device + if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) { off += game_block_size; continue; } diff --git a/32blit-pico/memmap_blit.ld b/32blit-pico/memmap_blit.ld index 846d2ff3c..e48767bc0 100644 --- a/32blit-pico/memmap_blit.ld +++ b/32blit-pico/memmap_blit.ld @@ -24,9 +24,9 @@ MEMORY { FLASH(rx) : ORIGIN = 0x10080000, LENGTH = 2048k - RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k - SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k - SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k + RAM(rwx) : ORIGIN = 0x20020800, LENGTH = 126k + /*SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k + SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k*/ } ENTRY(_entry_point) @@ -186,7 +186,7 @@ SECTIONS } > RAM /* Start and end symbols must be word-aligned */ - .scratch_x : { + /*.scratch_x : { __scratch_x_start__ = .; *(.scratch_x.*) . = ALIGN(4); @@ -200,7 +200,7 @@ SECTIONS . = ALIGN(4); __scratch_y_end__ = .; } > SCRATCH_Y AT > FLASH - __scratch_y_source__ = LOADADDR(.scratch_y); + __scratch_y_source__ = LOADADDR(.scratch_y);*/ .bss : { . = ALIGN(4); @@ -228,14 +228,14 @@ SECTIONS /* by default we put core 0 stack at the end of scratch Y, so that if core 1 * stack is not used then all of SCRATCH_X is free. */ - .stack1_dummy (COPY): + /*.stack1_dummy (COPY): { *(.stack1*) } > SCRATCH_X .stack_dummy (COPY): { *(.stack*) - } > SCRATCH_Y + } > SCRATCH_Y*/ .flash_end : { LONG(0x12345678) /*workaround for __flash_binary_end pointing past the end*/ @@ -244,14 +244,14 @@ SECTIONS /* stack limit is poorly named, but historically is maximum heap ptr */ __StackLimit = ORIGIN(RAM) + LENGTH(RAM); - __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + /*__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); __StackBottom = __StackTop - SIZEOF(.stack_dummy); - PROVIDE(__stack = __StackTop); + PROVIDE(__stack = __StackTop);*/ /* Check if data + heap + stack exceeds RAM limit */ - ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + /*ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")*/ ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") /* todo assert on extra code */ diff --git a/32blit-pico/startup.S b/32blit-pico/startup.S index 68eb6fa72..5e329597e 100644 --- a/32blit-pico/startup.S +++ b/32blit-pico/startup.S @@ -1,11 +1,85 @@ +#include "engine/api_version.h" + .cpu cortex-m0 .thumb .section .header, "a" .word 0x54494C42 -.word 0 // render -.word 0 // tick -.word 0 // init +.word _Z6renderm // render +.word _ZN4blit4tickEm // tick +.word _entry_point // init .word __flash_binary_size +.word 2 // device_id = 2 + padding +.hword BLIT_API_VERSION_MAJOR +.hword BLIT_API_VERSION_MINOR .word __flash_binary_start // unused + +.section .rodata, "a" +.align 4 +.global _ZN4blit3apiE +_ZN4blit3apiE: + .word 0x10000200 + +.global _ZN4blit8api_dataE +_ZN4blit8api_dataE: + .word 0x20000000 + +// based on pico-sdk crt0.S +.section .reset, "ax" + +.type _entry_point,%function +.thumb_func +.global _entry_point +_entry_point: + push {r4, lr} + // .data copy + adr r4, data_cpy_table + + // assume there is at least one entry +1: + ldmia r4!, {r1-r3} + cmp r1, #0 + beq 2f + bl data_cpy + b 1b +2: + + // Zero out the BSS + ldr r1, =__bss_start__ + ldr r2, =__bss_end__ + movs r0, #0 + b bss_fill_test +bss_fill_loop: + stm r1!, {r0} +bss_fill_test: + cmp r1, r2 + bne bss_fill_loop + +platform_entry: // symbol for stack traces + bl do_init + pop {r4, pc} + +data_cpy_loop: + ldm r1!, {r0} + stm r2!, {r0} +data_cpy: + cmp r2, r3 + blo data_cpy_loop + bx lr + +.align 2 +data_cpy_table: +.word __etext +.word __data_start__ +.word __data_end__ + +.word 0 // null terminator + +// panic stub +.type panic,%function +.thumb_func +.global panic +panic: + bkpt + bx lr diff --git a/32blit-pico/startup.cpp b/32blit-pico/startup.cpp new file mode 100644 index 000000000..d51e4bcc3 --- /dev/null +++ b/32blit-pico/startup.cpp @@ -0,0 +1,56 @@ +#include "engine/engine.hpp" +#include "engine/api_private.hpp" + +extern void init(); +extern void update(uint32_t time); +extern void render(uint32_t time); + +// override terminate handler to save ~20-30k +namespace __cxxabiv1 { + std::terminate_handler __terminate_handler = std::abort; +} + +extern "C" bool do_init() { +#ifndef IGNORE_API_VERSION + if(blit::api.version_major != blit::api_version_major) + return false; + + if(blit::api.version_minor < blit::api_version_minor) + return false; +#endif + + // preinit/init funcs (based on pico-sdk runtime.c) + + // Start and end points of the constructor list, + // defined by the linker script. + extern void (*__preinit_array_start)(); + extern void (*__preinit_array_end)(); + + // Call each function in the list. + // We have to take the address of the symbols, as __preinit_array_start *is* + // the first function pointer, not the address of it. + for (void (**p)(void) = &__preinit_array_start; p < &__preinit_array_end; ++p) { + (*p)(); + } + + // Start and end points of the constructor list, + // defined by the linker script. + extern void (*__init_array_start)(void); + extern void (*__init_array_end)(void); + + // Call each function in the list. + // We have to take the address of the symbols, as __init_array_start *is* + // the first function pointer, not the address of it. + for (void (**p)(void) = &__init_array_start; p < &__init_array_end; ++p) { + (*p)(); + } + + blit::update = update; + blit::render = render; + + blit::set_screen_mode(blit::ScreenMode::lores); + + init(); + + return true; +} From 6b610021f6577f7b46abc02ab6308f61f9a12979 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 10 Mar 2023 20:30:35 +0000 Subject: [PATCH 11/70] pico: rework standalone/blit builds Only builds one at a time, which causes less problems. Controlled by PICO_STANDALONE_UF2/PICO_BLIT in blit_executable or BLIT_EXECUTABLE_PICO_STANDALONE_UF2/BLIT_EXECUTABLE_PICO_BLIT globally --- 32blit-config.cmake | 26 +++ 32blit-pico/CMakeLists.txt | 154 ++++++++++-------- .../picosystem-hardware-test/CMakeLists.txt | 2 +- 3 files changed, 109 insertions(+), 73 deletions(-) diff --git a/32blit-config.cmake b/32blit-config.cmake index 18bfb9402..1beb72166 100644 --- a/32blit-config.cmake +++ b/32blit-config.cmake @@ -75,15 +75,39 @@ if (NOT DEFINED BLIT_ONCE) function(blit_executable_args) set(SOURCES) + # enable one of the pico builds if neither set + if(NOT BLIT_EXECUTABLE_PICO_STANDALONE_UF2 AND NOT BLIT_EXECUTABLE_PICO_BLIT) + set(BLIT_EXECUTABLE_PICO_STANDALONE_UF2 TRUE) + endif() + + # global overrides if(DEFINED BLIT_EXECUTABLE_INTERNAL_FLASH) set(INTERNAL_FLASH ${BLIT_EXECUTABLE_INTERNAL_FLASH}) else() set(INTERNAL_FLASH FALSE) endif() + if(DEFINED BLIT_EXECUTABLE_PICO_STANDALONE_UF2) + set(PICO_STANDALONE_UF2 ${BLIT_EXECUTABLE_PICO_STANDALONE_UF2}) + else() + set(PICO_STANDALONE_UF2 FALSE) + endif() + + if(DEFINED BLIT_EXECUTABLE_PICO_BLIT) + set(PICO_BLIT ${BLIT_EXECUTABLE_PICO_BLIT}) + else() + set(PICO_BLIT FALSE) + endif() + foreach(arg IN LISTS ARGN) if(arg STREQUAL "INTERNAL_FLASH") set(${arg} TRUE) + elseif(arg STREQUAL "PICO_STANDALONE_UF2") + set(${arg} TRUE) + set(PICO_BLIT FALSE) # can't build both + elseif(arg STREQUAL "PICO_BLIT") + set(${arg} TRUE) + set(PICO_STANDALONE_UF2 FALSE) else() list(APPEND SOURCES ${arg}) endif() @@ -91,6 +115,8 @@ if (NOT DEFINED BLIT_ONCE) set(SOURCES ${SOURCES} PARENT_SCOPE) set(INTERNAL_FLASH ${INTERNAL_FLASH} PARENT_SCOPE) + set(PICO_STANDALONE_UF2 ${PICO_STANDALONE_UF2} PARENT_SCOPE) + set(PICO_BLIT ${PICO_BLIT} PARENT_SCOPE) endfunction() if (32BLIT_HW) diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index 4e480cbd5..3ffac80f0 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -183,56 +183,61 @@ function(blit_executable NAME) message(STATUS "Processing ${NAME}") blit_executable_args(${ARGN}) - add_library(${NAME} STATIC ${SOURCES}) + add_executable(${NAME} ${SOURCES}) target_link_libraries(${NAME} BlitEngine pico_platform_headers) - target_compile_definitions(${NAME} PRIVATE PICO_EMBED_XIP_SETUP=1) - # standalone .uf2 - # TODO: import pico hal if standalone build - if(NOT TARGET LauncherShared) - add_subdirectory(${LAUNCHERSHARED_DIR} launcher-shared) + pico_enable_stdio_uart(${NAME} 1) + pico_enable_stdio_usb(${NAME} 0) + + if(PICO_STANDALONE_UF2) + # standalone .uf2 + # TODO: import pico hal if standalone build + if(NOT TARGET LauncherShared) + add_subdirectory(${LAUNCHERSHARED_DIR} launcher-shared) + endif() + + target_compile_definitions(${NAME} PRIVATE BLIT_PICO_STANDALONE=1 PICO_EMBED_XIP_SETUP=1) + target_link_libraries(${NAME} BlitHalPico) + + pico_add_extra_outputs(${NAME}) + + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${NAME}.uf2 + DESTINATION bin + ) + elseif(PICO_BLIT) + # loadable .blit + set_property(TARGET ${NAME} PROPERTY BLIT_PICO_BLIT TRUE) + + target_compile_definitions(${NAME} PRIVATE ${BLIT_BOARD_DEFINITIONS}) # need these for framebuffer config + target_compile_options(${NAME} PRIVATE -ffunction-sections -fdata-sections) + target_link_libraries(${NAME} pico_bit_ops pico_cxx_options pico_divider pico_double pico_int64_ops pico_float pico_malloc pico_mem_ops) + target_link_options(${NAME} PRIVATE --specs=nosys.specs LINKER:--script=${LINKER_SCRIPT} LINKER:--gc-sections) + set_property(TARGET ${NAME} APPEND PROPERTY LINK_DEPENDS ${LINKER_SCRIPT}) + + target_sources(${NAME} PRIVATE ${STARTUP_SRC}) + + pico_add_bin_output(${NAME}) + + # Ideally we want the .blit filename to match the .elf, but TARGET_FILE_BASE_NAME isn't always available + # (This only affects the firmware updater as it's the only thing setting a custom OUTPUT_NAME) + if(${CMAKE_VERSION} VERSION_LESS "3.15.0") + set(BLIT_FILENAME ${NAME}.blit) + else() + set(BLIT_FILENAME $.blit) + endif() + + # no relocs, just copy it + set(BIN_NAME "$>,$,$>.bin") + add_custom_command(TARGET ${NAME} POST_BUILD + VERBATIM + COMMENT "Building ${NAME}.blit" + COMMAND cp ${BIN_NAME} ${BLIT_FILENAME} + ) + + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${BLIT_FILENAME} + DESTINATION bin + ) endif() - - add_executable(${NAME}-standalone) - target_link_libraries(${NAME}-standalone ${NAME} BlitHalPico) - pico_enable_stdio_uart(${NAME}-standalone 1) - pico_enable_stdio_usb(${NAME}-standalone 0) - - pico_add_extra_outputs(${NAME}-standalone) - - # loadable .blit - add_executable(${NAME}-bl) - target_compile_definitions(${NAME}-bl PRIVATE ${BLIT_BOARD_DEFINITIONS}) # need these for framebuffer config - target_compile_options(${NAME}-bl PRIVATE -ffunction-sections -fdata-sections) - target_link_libraries(${NAME}-bl ${NAME} pico_bit_ops pico_cxx_options pico_divider pico_double pico_int64_ops pico_float pico_malloc pico_mem_ops) - target_link_options(${NAME}-bl PRIVATE --specs=nosys.specs LINKER:--script=${LINKER_SCRIPT} LINKER:--gc-sections) - set_property(TARGET ${NAME}-bl APPEND PROPERTY LINK_DEPENDS ${LINKER_SCRIPT}) - - target_sources(${NAME}-bl PRIVATE ${STARTUP_SRC}) - - pico_enable_stdio_uart(${NAME}-bl 1) - pico_enable_stdio_usb(${NAME}-bl 0) - - pico_add_bin_output(${NAME}-bl) - - # Ideally we want the .blit filename to match the .elf, but TARGET_FILE_BASE_NAME isn't always available - # (This only affects the firmware updater as it's the only thing setting a custom OUTPUT_NAME) - if(${CMAKE_VERSION} VERSION_LESS "3.15.0") - set(BLIT_FILENAME ${NAME}.blit) - else() - set(BLIT_FILENAME $.blit) - endif() - - # no relocs, just copy it - set(BIN_NAME "$>,$,$>.bin") - add_custom_command(TARGET ${NAME}-bl POST_BUILD - COMMENT "Building ${NAME}.blit" - COMMAND cp ${BIN_NAME} ${BLIT_FILENAME} - ) - - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${NAME}-standalone.uf2 - DESTINATION bin - ) endfunction() function(blit_metadata TARGET FILE) @@ -254,37 +259,42 @@ function(blit_metadata TARGET FILE) include(${CMAKE_CURRENT_BINARY_DIR}/metadata.cmake) - # create metadata/binary info source at build time - set(METADATA_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_binary_info.cpp") + get_property(PICO_BLIT TARGET ${TARGET} PROPERTY BLIT_PICO_BLIT) - add_custom_command( - OUTPUT ${METADATA_SOURCE} - COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${32BLIT_TOOLS_EXECUTABLE} metadata --force --config ${FILE} --pico-bi ${METADATA_SOURCE} - DEPENDS ${FILE} - VERBATIM - ) + if(NOT PICO_BLIT) + # create metadata/binary info source at build time + set(METADATA_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_binary_info.cpp") + + add_custom_command( + OUTPUT ${METADATA_SOURCE} + COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${32BLIT_TOOLS_EXECUTABLE} metadata --force --config ${FILE} --pico-bi ${METADATA_SOURCE} + DEPENDS ${FILE} + VERBATIM + ) - # add the generated source - target_sources(${TARGET}-standalone PRIVATE ${METADATA_SOURCE}) + # add the generated source + target_sources(${TARGET} PRIVATE ${METADATA_SOURCE}) - # real .blit metadata - if(${CMAKE_VERSION} VERSION_LESS "3.15.0") - set(BLIT_FILENAME ${TARGET}.blit) + # avoid the fallback to target name + target_compile_definitions(${TARGET} PRIVATE PICO_NO_BI_PROGRAM_NAME=1) else() - set(BLIT_FILENAME $.blit) + # real .blit metadata + if(${CMAKE_VERSION} VERSION_LESS "3.15.0") + set(BLIT_FILENAME ${TARGET}.blit) + else() + set(BLIT_FILENAME $.blit) + endif() + + add_custom_command( + TARGET ${TARGET} POST_BUILD + COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${32BLIT_TOOLS_EXECUTABLE} metadata --config ${FILE} --file ${CMAKE_CURRENT_BINARY_DIR}/${BLIT_FILENAME} + VERBATIM + ) + + # force relink on change so that the post-build commands are rerun + set_property(TARGET ${TARGET} APPEND PROPERTY LINK_DEPENDS ${FILE} ${METADATA_DEPENDS}) endif() - add_custom_command( - TARGET ${TARGET}-bl POST_BUILD - COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${32BLIT_TOOLS_EXECUTABLE} metadata --config ${FILE} --file ${CMAKE_CURRENT_BINARY_DIR}/${BLIT_FILENAME} - ) - - # force relink on change so that the post-build commands are rerun - set_property(TARGET ${TARGET}-bl APPEND PROPERTY LINK_DEPENDS ${FILE} ${METADATA_DEPENDS}) - - # avoid the fallback to target name - target_compile_definitions(${TARGET} PRIVATE PICO_NO_BI_PROGRAM_NAME=1) - endfunction() add_subdirectory(loader) diff --git a/utilities/picosystem-hardware-test/CMakeLists.txt b/utilities/picosystem-hardware-test/CMakeLists.txt index 3f75b0cbe..0b83077b3 100644 --- a/utilities/picosystem-hardware-test/CMakeLists.txt +++ b/utilities/picosystem-hardware-test/CMakeLists.txt @@ -8,5 +8,5 @@ find_package (32BLIT CONFIG REQUIRED PATHS ../..) return() #endif() -blit_executable (picosystem-hardware-test hardware-test.cpp) +blit_executable (picosystem-hardware-test hardware-test.cpp PICO_STANDALONE_UF2) blit_metadata (picosystem-hardware-test metadata.yml) From 713222e1798223780a7c7e8c3f439ead3c8936ee Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 13 Jul 2023 12:58:57 +0100 Subject: [PATCH 12/70] Revert "HACK: disable picosystem-hardware-test" This reverts commit dd0fec97af51a9906916d0e3884dc88b3d0745dc. --- utilities/picosystem-hardware-test/CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/utilities/picosystem-hardware-test/CMakeLists.txt b/utilities/picosystem-hardware-test/CMakeLists.txt index 0b83077b3..3f2d58443 100644 --- a/utilities/picosystem-hardware-test/CMakeLists.txt +++ b/utilities/picosystem-hardware-test/CMakeLists.txt @@ -2,11 +2,10 @@ cmake_minimum_required(VERSION 3.9...3.26) project (picosystem-hardware-test) find_package (32BLIT CONFIG REQUIRED PATHS ../..) -# FIXME -#if(NOT PICO_BOARD STREQUAL "pimoroni_picosystem") +if(NOT PICO_BOARD STREQUAL "pimoroni_picosystem") # Hooks into the Pico SDK to get battery charge and VBUS status return() -#endif() +endif() blit_executable (picosystem-hardware-test hardware-test.cpp PICO_STANDALONE_UF2) blit_metadata (picosystem-hardware-test metadata.yml) From 2098ba33d92f34b3ed14a5a5c486f06b38289ca4 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 7 Mar 2023 20:46:49 +0000 Subject: [PATCH 13/70] Add an API to get screen.data to fix pico page-flipping --- 32blit-pico/main.cpp | 6 ++++++ 32blit-pico/startup.S | 2 +- 32blit-pico/startup.cpp | 5 +++++ 32blit-sdl/System.cpp | 2 ++ 32blit/engine/api_private.hpp | 5 ++++- 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index 592392727..3490633c1 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -187,6 +187,10 @@ static void list_installed_games(std::function Date: Tue, 7 Mar 2023 23:19:17 +0000 Subject: [PATCH 14/70] pico: move framebuffer allocation to the other side of teh API firmware now reserves 32k of RAM (may need less) --- 32blit-pico/display.cpp | 26 ++++++++++++++++++-------- 32blit-pico/display.hpp | 6 +++++- 32blit-pico/main.cpp | 3 ++- 32blit-pico/memmap_blit.ld | 2 +- 32blit-pico/memmap_blit_loader.ld | 2 +- 32blit-pico/startup.cpp | 20 ++++++++++++++++++++ 32blit-sdl/System.cpp | 1 + 32blit/engine/api_private.hpp | 3 ++- 8 files changed, 50 insertions(+), 13 deletions(-) diff --git a/32blit-pico/display.cpp b/32blit-pico/display.cpp index fafd4570e..e78995ab6 100644 --- a/32blit-pico/display.cpp +++ b/32blit-pico/display.cpp @@ -20,10 +20,12 @@ SurfaceInfo cur_surf_info; bool fb_double_buffer = true; -#if defined(BLIT_BOARD_PIMORONI_PICOVISION) -static const uint16_t *screen_fb = nullptr; +#if defined(BUILD_LOADER) || defined(BLIT_BOARD_PIMORONI_PICOVISION) +uint16_t *screen_fb = nullptr; +static uint32_t max_fb_size = 0; #else uint16_t screen_fb[fb_size]; +static const uint32_t max_fb_size = fb_size; #endif static const Size lores_screen_size(DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2); @@ -65,23 +67,23 @@ bool set_screen_mode_format(ScreenMode new_mode, SurfaceTemplate &new_surf_templ break; case ScreenMode::hires: case ScreenMode::hires_palette: -#if ALLOW_HIRES if(new_surf_template.bounds.empty()) new_surf_template.bounds = hires_screen_size; - break; -#else - return false; // no hires for scanvideo -#endif } // check the framebuffer is large enough for mode auto fb_size = uint32_t(new_surf_template.bounds.area()) * pixel_format_stride[int(new_surf_template.format)]; +// TODO: more generic "doesn't have a framebuffer"? +#ifndef BLIT_BOARD_PIMORONI_PICOVISION + if(max_fb_size < fb_size) + return false; +#endif if(!display_mode_supported(new_mode, new_surf_template)) return false; - fb_double_buffer = fb_size * 2 <= sizeof(screen_fb); + fb_double_buffer = fb_size * 2 <= max_fb_size; if(!fb_double_buffer) screen.data = new_surf_template.data; @@ -98,3 +100,11 @@ bool set_screen_mode_format(ScreenMode new_mode, SurfaceTemplate &new_surf_templ void set_screen_palette(const Pen *colours, int num_cols) { } + +void set_framebuffer(uint8_t *data, uint32_t max_size) { +#if defined(BUILD_LOADER) && !defined(BLIT_BOARD_PIMORONI_PICOVISION) + screen_fb = (uint16_t *)data; + screen.data = data; + max_fb_size = max_size; +#endif +} diff --git a/32blit-pico/display.hpp b/32blit-pico/display.hpp index 1cbed84c8..c6821b274 100644 --- a/32blit-pico/display.hpp +++ b/32blit-pico/display.hpp @@ -9,7 +9,9 @@ extern blit::ScreenMode cur_screen_mode; extern bool fb_double_buffer; -#ifndef BLIT_BOARD_PIMORONI_PICOVISION +#if defined(BUILD_LOADER) || defined(BLIT_BOARD_PIMORONI_PICOVISION) +extern uint16_t *screen_fb; +#else extern uint16_t screen_fb[]; #endif @@ -30,3 +32,5 @@ blit::SurfaceInfo &set_screen_mode(blit::ScreenMode mode); bool set_screen_mode_format(blit::ScreenMode new_mode, blit::SurfaceTemplate &new_surf_template); void set_screen_palette(const blit::Pen *colours, int num_cols); + +void set_framebuffer(uint8_t *data, uint32_t max_size); diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index 3490633c1..34e738ec9 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -254,7 +254,8 @@ static const blit::APIConst blit_api_const { ::list_installed_games, ::can_launch, - ::get_screen_data + ::get_screen_data, + ::set_framebuffer, }; [[gnu::section(".bss.api_data")]] diff --git a/32blit-pico/memmap_blit.ld b/32blit-pico/memmap_blit.ld index e48767bc0..9b9e183f8 100644 --- a/32blit-pico/memmap_blit.ld +++ b/32blit-pico/memmap_blit.ld @@ -24,7 +24,7 @@ MEMORY { FLASH(rx) : ORIGIN = 0x10080000, LENGTH = 2048k - RAM(rwx) : ORIGIN = 0x20020800, LENGTH = 126k + RAM(rwx) : ORIGIN = 0x20008000, LENGTH = 224k /*SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k*/ } diff --git a/32blit-pico/memmap_blit_loader.ld b/32blit-pico/memmap_blit_loader.ld index 551e1235a..d168e77e6 100644 --- a/32blit-pico/memmap_blit_loader.ld +++ b/32blit-pico/memmap_blit_loader.ld @@ -24,7 +24,7 @@ MEMORY { FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 256k - RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 130k + RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 32k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k } diff --git a/32blit-pico/startup.cpp b/32blit-pico/startup.cpp index 6274866a2..959894033 100644 --- a/32blit-pico/startup.cpp +++ b/32blit-pico/startup.cpp @@ -1,6 +1,22 @@ #include "engine/engine.hpp" #include "engine/api_private.hpp" +#include "config.h" + +#ifndef BLIT_BOARD_PIMORONI_PICOVISION + +#if ALLOW_HIRES && DOUBLE_BUFFERED_HIRES +static const int fb_size = DISPLAY_WIDTH * DISPLAY_HEIGHT * 2; +#elif ALLOW_HIRES +static const int fb_size = DISPLAY_WIDTH * DISPLAY_HEIGHT; +#else +// height rounded up to handle the 135px display +static const int fb_size = (DISPLAY_WIDTH / 2) * ((DISPLAY_HEIGHT + 1) / 2) * 2; // double-buffered +#endif + +static uint16_t screen_fb[fb_size]; +#endif + extern void init(); extern void update(uint32_t time); extern void render(uint32_t time); @@ -48,6 +64,10 @@ extern "C" bool do_init() { blit::update = update; blit::render = render; +#ifndef BLIT_BOARD_PIMORONI_PICOVISION + blit::api.set_framebuffer((uint8_t *)screen_fb, sizeof(screen_fb)); +#endif + blit::set_screen_mode(blit::ScreenMode::lores); init(); diff --git a/32blit-sdl/System.cpp b/32blit-sdl/System.cpp index 21ee40464..9525d102e 100644 --- a/32blit-sdl/System.cpp +++ b/32blit-sdl/System.cpp @@ -233,6 +233,7 @@ static const blit::APIConst blit_api_const { nullptr, // can_launch nullptr, // get_screen_data + nullptr, // set_framebuffer }; static blit::APIData blit_api_data; diff --git a/32blit/engine/api_private.hpp b/32blit/engine/api_private.hpp index d3e91d0fc..fa3aadac0 100644 --- a/32blit/engine/api_private.hpp +++ b/32blit/engine/api_private.hpp @@ -149,6 +149,7 @@ namespace blit { // low level framebuffer uint8_t *(*get_screen_data)(); // used to get current screen.data before render if firmware does page-flipping + void (*set_framebuffer)(uint8_t *data, uint32_t max_size); // pass framebuffer over if allocated on the "user" side of the API }; struct APIData { @@ -176,7 +177,7 @@ namespace blit { // raw i2c access void (*i2c_completed)(uint8_t address, uint8_t reg, const uint8_t *data, uint16_t len); // callback when done - COMPAT_PAD(uintptr_t, pad5, 6); + COMPAT_PAD(uintptr_t, pad5, 7); }; #ifdef BLIT_API_SPLIT_COMPAT From 4007c2601028ebe78d6dcbd8e42c358f838f36cf Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 7 Mar 2023 23:57:19 +0000 Subject: [PATCH 15/70] pico: change tick in launch API --- 32blit-pico/main.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index 34e738ec9..6087c2bfb 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -32,6 +32,8 @@ const unsigned int game_block_size = 64 * 1024; // this is the 32blit's flash er static blit::AudioChannel channels[CHANNEL_COUNT]; +static int (*do_tick)(uint32_t time) = blit::tick; + // override terminate handler to save ~20-30k namespace __cxxabiv1 { std::terminate_handler __terminate_handler = std::abort; @@ -121,7 +123,7 @@ static bool launch(const char *path) { return false; blit::render = header->render; - // FIXME: tick + do_tick = header->tick; return true; } @@ -348,7 +350,7 @@ int main() { auto now = ::now(); update_display(now); update_input(); - int ms_to_next_update = tick(::now()); + int ms_to_next_update = do_tick(::now()); update_led(); update_usb(); update_multiplayer(); From 27f523297b51e7ed05023140f9c1b68e17b158ea Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Wed, 8 Mar 2023 00:06:08 +0000 Subject: [PATCH 16/70] pico: delay launches --- 32blit-pico/main.cpp | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index 6087c2bfb..bdf9aee50 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -34,6 +34,8 @@ static blit::AudioChannel channels[CHANNEL_COUNT]; static int (*do_tick)(uint32_t time) = blit::tick; +static uint32_t requested_launch_offset = 0; + // override terminate handler to save ~20-30k namespace __cxxabiv1 { std::terminate_handler __terminate_handler = std::abort; @@ -111,7 +113,7 @@ static GameMetadata get_metadata() { static bool launch(const char *path) { if(strncmp(path, "flash:/", 7) == 0) { - int offset = atoi(path + 7) * game_block_size; + uint32_t offset = atoi(path + 7) * game_block_size; auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + offset); // check header magic + device @@ -119,12 +121,7 @@ static bool launch(const char *path) { return false; if(header->init) { - if(!header->init(0)) - return false; - - blit::render = header->render; - do_tick = header->tick; - + requested_launch_offset = offset; return true; } @@ -161,6 +158,19 @@ static blit::CanLaunchResult can_launch(const char *path) { return blit::CanLaunchResult::UnknownType; } +static void delayed_launch() { + auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + requested_launch_offset); + requested_launch_offset = 0; + + if(!header->init(0)) { + debugf("failed to init game!\n"); + return; + } + + blit::render = header->render; + do_tick = header->tick; +} + static void list_installed_games(std::function callback) { for(uint32_t off = 0; off < PICO_FLASH_SIZE_BYTES;) { auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + off); @@ -355,6 +365,10 @@ int main() { update_usb(); update_multiplayer(); + // do requested launch when no user code is running + if(requested_launch_offset) + delayed_launch(); + if(ms_to_next_update > 1 && !display_render_needed()) best_effort_wfe_or_timeout(make_timeout_time_ms(ms_to_next_update - 1)); } From a033e0f9e0c8993b42030b3e4c3f81396e4db685 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 10 Mar 2023 11:52:31 +0000 Subject: [PATCH 17/70] pico: remove reset-based launch --- 32blit-pico/main.cpp | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index bdf9aee50..41b44e86a 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -3,7 +3,6 @@ #include "hardware/clocks.h" #include "hardware/structs/rosc.h" -#include "hardware/structs/scb.h" #include "hardware/vreg.h" #include "hardware/timer.h" #include "pico/binary_info.h" @@ -120,30 +119,11 @@ static bool launch(const char *path) { if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) return false; - if(header->init) { - requested_launch_offset = offset; - return true; - } + if(!header->init || !header->render || !header->tick) + return false; - // TODO: check valid - auto addr = XIP_BASE + offset + 256; - - // disable all irqs - irq_set_mask_enabled(~0u, false); - - // set VTOR - scb_hw->vtor = addr; - - asm volatile( - "ldr r0, [%0]\n" - "ldr r1, [%0, #4]\n" - "msr msp, r0\n" // set SP - "bx r1" // branch to reset - : - : "r" (addr) - : "r0", "r1" - ); - // not reached + requested_launch_offset = offset; + return true; } return false; From 0d61c84131a5101f15e2936bcffcc4bd50fcedce Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 10 Mar 2023 14:30:05 +0000 Subject: [PATCH 18/70] pico: refactor multiplayer code into generic CDC command handling --- 32blit-pico/CMakeLists.txt | 1 + 32blit-pico/multiplayer.cpp | 97 +++++++++++++++---------------------- 32blit-pico/multiplayer.hpp | 12 +++++ 32blit-pico/usb.cpp | 68 ++++++++++++++++++++++++++ 32blit-pico/usb.hpp | 14 ++++++ 32blit-pico/usb/device.cpp | 2 + 32blit-pico/usb/host.cpp | 2 + 7 files changed, 139 insertions(+), 57 deletions(-) create mode 100644 32blit-pico/usb.cpp diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index 3ffac80f0..bc605f3ee 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -39,6 +39,7 @@ target_sources(BlitHalPico INTERFACE ${CMAKE_CURRENT_LIST_DIR}/led.cpp ${CMAKE_CURRENT_LIST_DIR}/main.cpp ${CMAKE_CURRENT_LIST_DIR}/multiplayer.cpp + ${CMAKE_CURRENT_LIST_DIR}/usb.cpp ${CMAKE_CURRENT_LIST_DIR}/usb_descriptors.c ) diff --git a/32blit-pico/multiplayer.cpp b/32blit-pico/multiplayer.cpp index 32cacf3f9..ce3eb198e 100644 --- a/32blit-pico/multiplayer.cpp +++ b/32blit-pico/multiplayer.cpp @@ -8,12 +8,49 @@ static bool multiplayer_enabled = false; static bool peer_connected = false; -static uint8_t cur_header[8]; -static int header_pos = 0; - static uint16_t mp_buffer_len, mp_buffer_off; static uint8_t *mp_buffer = nullptr; +CDCCommand::Status CDCHandshakeCommand::update() { + uint8_t b; + usb_cdc_read(&b, 1); + peer_connected = b != 0; + + if(peer_connected) + send_multiplayer_handshake(true); + + return Status::Done; +} + +void CDCUserCommand::init() { + mp_buffer_off = 0; +} + +CDCCommand::Status CDCUserCommand::update() { + // read length + if(!mp_buffer) { + if(usb_cdc_read_available() < 2) + return Status::Continue; + + usb_cdc_read((uint8_t *)&mp_buffer_len, 2); + mp_buffer = new uint8_t[mp_buffer_len]; + } + + // read data + mp_buffer_off += usb_cdc_read(mp_buffer + mp_buffer_off, mp_buffer_len - mp_buffer_off); + + if(mp_buffer_off == mp_buffer_len) { + if(blit::api_data.message_received) + blit::api_data.message_received(mp_buffer, mp_buffer_len); + + delete[] mp_buffer; + mp_buffer = nullptr; + return Status::Done; + } + + return Status::Continue; +} + void send_multiplayer_handshake(bool is_reply) { uint8_t val = 0; if(multiplayer_enabled) @@ -28,60 +65,6 @@ void update_multiplayer() { if(!usb_cdc_connected()) { peer_connected = false; } - - while(usb_cdc_read_available()) { - // match header - if(header_pos < 8) { - usb_cdc_read(cur_header + header_pos, 1); - - const char *expected = "32BL"; - if(header_pos >= 4 || cur_header[header_pos] == expected[header_pos]) - header_pos++; - else - header_pos = 0; - } else { - - // get USER packet - if(mp_buffer) { - mp_buffer_off += usb_cdc_read(mp_buffer + mp_buffer_off, mp_buffer_len - mp_buffer_off); - - if(mp_buffer_off == mp_buffer_len) { - if(blit::api_data.message_received) - blit::api_data.message_received(mp_buffer, mp_buffer_len); - - delete[] mp_buffer; - mp_buffer = nullptr; - header_pos = 0; - } - continue; - } - - // got header - if(memcmp(cur_header + 4, "MLTI", 4) == 0) { - // handshake packet - uint8_t b; - usb_cdc_read(&b, 1); - peer_connected = b != 0; - - if(peer_connected) - send_multiplayer_handshake(true); - - // done - header_pos = 0; - } else if(memcmp(cur_header + 4, "USER", 4) == 0) { - if(usb_cdc_read_available() < 2) - break; - - usb_cdc_read((uint8_t *)&mp_buffer_len, 2); - mp_buffer_off = 0; - mp_buffer = new uint8_t[mp_buffer_len]; - - } else { - printf("got: %c%c%c%c%c%c%c%c\n", cur_header[0], cur_header[1], cur_header[2], cur_header[3], cur_header[4], cur_header[5], cur_header[6], cur_header[7]); - header_pos = 0; - } - } - } } bool is_multiplayer_connected() { diff --git a/32blit-pico/multiplayer.hpp b/32blit-pico/multiplayer.hpp index af6639ef6..4174bb45e 100644 --- a/32blit-pico/multiplayer.hpp +++ b/32blit-pico/multiplayer.hpp @@ -1,6 +1,18 @@ #pragma once #include +#include "usb.hpp" + +class CDCHandshakeCommand final : public CDCCommand { + void init() override {} + Status update() override; +}; + +class CDCUserCommand final : public CDCCommand { + void init() override; + Status update() override; +}; + void update_multiplayer(); void send_multiplayer_handshake(bool is_reply = false); diff --git a/32blit-pico/usb.cpp b/32blit-pico/usb.cpp new file mode 100644 index 000000000..df00ee992 --- /dev/null +++ b/32blit-pico/usb.cpp @@ -0,0 +1,68 @@ +#include +#include + +#include "usb.hpp" +#include "multiplayer.hpp" + +static uint8_t cur_header[8]; +static int header_pos = 0; +static CDCCommand *cur_command = nullptr; + +static constexpr uint32_t to_cmd_id(const char str[4]) { + return str[0] | str[1] << 8 | str[2] << 16 | str[3] << 24; +} + +static CDCHandshakeCommand handshake_command; +static CDCUserCommand user_command; + +const std::tuple cdc_commands[]{ + {to_cmd_id("MLTI"), &handshake_command}, + {to_cmd_id("USER"), &user_command} +}; + +void usb_cdc_update() { + while(usb_cdc_read_available()) { + // command in progress + if(cur_command) { + auto res = cur_command->update(); + + if(res == CDCCommand::Status::Continue) { + break; + } else { + // done/error + cur_command = nullptr; + } + + continue; + } + + // match header + if(header_pos < 8) { + usb_cdc_read(cur_header + header_pos, 1); + + const char *expected = "32BL"; + if(header_pos >= 4 || cur_header[header_pos] == expected[header_pos]) + header_pos++; + else + header_pos = 0; + } else { + + // got header + auto command_id = to_cmd_id((char *)cur_header + 4); + + // find command + for(auto &cmd : cdc_commands) { + if(std::get<0>(cmd) == command_id) { + cur_command = std::get<1>(cmd); + break; + } + } + + if(!cur_command) + printf("got: %c%c%c%c%c%c%c%c\n", cur_header[0], cur_header[1], cur_header[2], cur_header[3], cur_header[4], cur_header[5], cur_header[6], cur_header[7]); + + cur_command->init(); + header_pos = 0; + } + } +} \ No newline at end of file diff --git a/32blit-pico/usb.hpp b/32blit-pico/usb.hpp index 59e7016ef..49cecb545 100644 --- a/32blit-pico/usb.hpp +++ b/32blit-pico/usb.hpp @@ -2,6 +2,18 @@ #include +class CDCCommand { +public: + enum class Status { + Done = 0, + Continue, + Error + }; + + virtual void init() = 0; + virtual Status update() = 0; +}; + void init_usb(); void update_usb(); @@ -12,3 +24,5 @@ uint16_t usb_cdc_read(uint8_t *data, uint16_t len); uint32_t usb_cdc_read_available(); void usb_cdc_write(const uint8_t *data, uint16_t len); void usb_cdc_flush_write(); + +void usb_cdc_update(); diff --git a/32blit-pico/usb/device.cpp b/32blit-pico/usb/device.cpp index 9675351d7..e507286f9 100644 --- a/32blit-pico/usb/device.cpp +++ b/32blit-pico/usb/device.cpp @@ -97,6 +97,8 @@ void init_usb() { void update_usb() { tud_task(); + + usb_cdc_update(); } void usb_debug(const char *message) { diff --git a/32blit-pico/usb/host.cpp b/32blit-pico/usb/host.cpp index 200910c55..089408298 100644 --- a/32blit-pico/usb/host.cpp +++ b/32blit-pico/usb/host.cpp @@ -178,6 +178,8 @@ void init_usb() { void update_usb() { tuh_task(); + usb_cdc_update(); + // TODO: resend multiplayer handshake } From 090ef71c10c00d92e463dab3a7cb5f9d6ce81f76 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 10 Mar 2023 18:51:27 +0000 Subject: [PATCH 19/70] pico: CDC flash command larger project for the future: share this code --- 32blit-pico/main.cpp | 6 ++ 32blit-pico/usb.cpp | 183 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 187 insertions(+), 2 deletions(-) diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index 41b44e86a..ee6fcc525 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -183,6 +183,12 @@ static uint8_t *get_screen_data() { return screen.data; } +void disable_user_code() { + // TODO: handle re-enabling + do_tick = blit::tick; + blit::render = ::render; +} + // blit API [[gnu::section(".rodata.api_const")]] static const blit::APIConst blit_api_const { diff --git a/32blit-pico/usb.cpp b/32blit-pico/usb.cpp index df00ee992..84d70264c 100644 --- a/32blit-pico/usb.cpp +++ b/32blit-pico/usb.cpp @@ -1,9 +1,23 @@ #include +#include #include +#include "hardware/flash.h" +#include "hardware/sync.h" +#include "pico/multicore.h" + #include "usb.hpp" #include "multiplayer.hpp" +#include "engine/engine.hpp" +#include "executable.hpp" + +void init(); + +void disable_user_code(); + +extern bool core1_started; + static uint8_t cur_header[8]; static int header_pos = 0; static CDCCommand *cur_command = nullptr; @@ -12,12 +26,177 @@ static constexpr uint32_t to_cmd_id(const char str[4]) { return str[0] | str[1] << 8 | str[2] << 16 | str[3] << 24; } +#define MAX_FILENAME 256+1 +#define MAX_FILELEN 16+1 + +class CDCProgCommand final : public CDCCommand { + void init() override { + parse_state = ParseState::Filename; + buf_off = 0; + file_offset = flash_offset = 0; + } + + Status update() override { + while(true) { + switch(parse_state) { + case ParseState::Filename: { + if(!usb_cdc_read(buf + buf_off, 1)) + return Status::Continue; + + // end of string + if(buf[buf_off] == 0) { + printf("PROG: file %s\n", buf); + parse_state = ParseState::Length; + buf_off = 0; + continue; + } + + buf_off++; + + // too long + if(buf_off == MAX_FILENAME) + return Status::Error; + + break; + } + + case ParseState::Length: { + if(!usb_cdc_read(buf + buf_off, 1)) + return Status::Continue; + + // end of string + if(buf[buf_off] == 0) { + file_len = strtoul((const char *)buf, nullptr, 10); + printf("PROG: len %lu\n", file_len); + parse_state = ParseState::Data; + buf_off = 0; + continue; + } + + buf_off++; + + // too long + if(buf_off == MAX_FILELEN) + return Status::Error; + + break; + } + + case ParseState::Data: { + // read data + auto max = std::min(FLASH_PAGE_SIZE - buf_off, file_len - file_offset); + auto read = usb_cdc_read(buf + buf_off, max); + + if(!read) + return Status::Continue; + + buf_off += read; + + // got full page or final part of file + if(buf_off == FLASH_PAGE_SIZE || file_offset + buf_off == file_len) { + if(!flash_offset) { + if(!prepare_write()) + return Status::Error; + } + + // write page + auto status = save_and_disable_interrupts(); + + if(core1_started) + multicore_lockout_start_blocking(); // pause core1 + + flash_range_program(flash_offset + file_offset, buf, FLASH_PAGE_SIZE); + + if(core1_started) + multicore_lockout_end_blocking(); // resume core1 + + restore_interrupts(status); + + file_offset += buf_off; + buf_off = 0; + } + + // end of file + if(file_offset == file_len) { + // send response + auto block = flash_offset >> 16; + uint8_t res_data[]{'3', '2', 'B', 'L', '_', '_', 'O', 'K', uint8_t(block), uint8_t(block >> 8)}; + usb_cdc_write(res_data, sizeof(res_data)); + usb_cdc_flush_write(); + + // reinit loader + ::init(); + + return Status::Done; + } + + break; + } + } + } + + return Status::Continue; + } + + + bool prepare_write() { + auto header = (BlitGameHeader *)buf; + + if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) { + blit::debugf("Invalid blit header!"); + return false; + } + + // corrently non-relocatable, so base address is stored after header + flash_offset = *(uint32_t *)(buf + sizeof(BlitGameHeader)); + flash_offset &= 0xFFFFFF; + + printf("PROG: flash off %lu\n", flash_offset); + + disable_user_code(); + + // erase flash + auto status = save_and_disable_interrupts(); + + if(core1_started) + multicore_lockout_start_blocking(); // pause core1 + + auto erase_size = ((file_len - 1) / FLASH_SECTOR_SIZE) + 1; + flash_range_erase(flash_offset, erase_size * FLASH_SECTOR_SIZE); + + if(core1_started) + multicore_lockout_end_blocking(); // resume core1 + + restore_interrupts(status); + + return true; + } + + enum class ParseState { + Filename, + Length, + Data + } parse_state = ParseState::Filename; + + static const size_t buf_size = std::max((unsigned)std::max(MAX_FILELEN, MAX_FILENAME), FLASH_PAGE_SIZE); + uint8_t buf[buf_size]; + uint32_t buf_off = 0; + + uint32_t file_len; + uint32_t file_offset; + + uint32_t flash_offset; +}; + static CDCHandshakeCommand handshake_command; static CDCUserCommand user_command; +static CDCProgCommand prog_command; + const std::tuple cdc_commands[]{ {to_cmd_id("MLTI"), &handshake_command}, - {to_cmd_id("USER"), &user_command} + {to_cmd_id("USER"), &user_command}, + {to_cmd_id("PROG"), &prog_command}, }; void usb_cdc_update() { @@ -65,4 +244,4 @@ void usb_cdc_update() { header_pos = 0; } } -} \ No newline at end of file +} From dca997c02538576404bb434c9ab2706da1003029 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 10 Mar 2023 20:42:47 +0000 Subject: [PATCH 20/70] pico: move disable_user_code after render decl So that it actually does the thing it's supposed to do --- 32blit-pico/main.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index ee6fcc525..b24dbe9fc 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -183,12 +183,6 @@ static uint8_t *get_screen_data() { return screen.data; } -void disable_user_code() { - // TODO: handle re-enabling - do_tick = blit::tick; - blit::render = ::render; -} - // blit API [[gnu::section(".rodata.api_const")]] static const blit::APIConst blit_api_const { @@ -269,6 +263,12 @@ void init(); void render(uint32_t); void update(uint32_t); +void disable_user_code() { + // TODO: handle re-enabling + do_tick = blit::tick; + blit::render = ::render; +} + bool core1_started = false; #ifdef ENABLE_CORE1 From 0e32044344631797dc169a074bb568b52cd46ec5 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Mon, 13 Mar 2023 20:53:24 +0000 Subject: [PATCH 21/70] pico: pass max hires bounds through set_framebuffer --- 32blit-pico/display.cpp | 4 +++- 32blit-pico/display.hpp | 2 +- 32blit-pico/startup.cpp | 2 +- 32blit/engine/api_private.hpp | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/32blit-pico/display.cpp b/32blit-pico/display.cpp index e78995ab6..69ef68c58 100644 --- a/32blit-pico/display.cpp +++ b/32blit-pico/display.cpp @@ -23,6 +23,7 @@ bool fb_double_buffer = true; #if defined(BUILD_LOADER) || defined(BLIT_BOARD_PIMORONI_PICOVISION) uint16_t *screen_fb = nullptr; static uint32_t max_fb_size = 0; +static Size max_fb_bounds(DISPLAY_WIDTH, DISPLAY_HEIGHT); #else uint16_t screen_fb[fb_size]; static const uint32_t max_fb_size = fb_size; @@ -101,10 +102,11 @@ void set_screen_palette(const Pen *colours, int num_cols) { } -void set_framebuffer(uint8_t *data, uint32_t max_size) { +void set_framebuffer(uint8_t *data, uint32_t max_size, Size max_bounds) { #if defined(BUILD_LOADER) && !defined(BLIT_BOARD_PIMORONI_PICOVISION) screen_fb = (uint16_t *)data; screen.data = data; max_fb_size = max_size; + max_fb_bounds = max_bounds; #endif } diff --git a/32blit-pico/display.hpp b/32blit-pico/display.hpp index c6821b274..2d1338373 100644 --- a/32blit-pico/display.hpp +++ b/32blit-pico/display.hpp @@ -33,4 +33,4 @@ bool set_screen_mode_format(blit::ScreenMode new_mode, blit::SurfaceTemplate &ne void set_screen_palette(const blit::Pen *colours, int num_cols); -void set_framebuffer(uint8_t *data, uint32_t max_size); +void set_framebuffer(uint8_t *data, uint32_t max_size, blit::Size max_bounds); diff --git a/32blit-pico/startup.cpp b/32blit-pico/startup.cpp index 959894033..0c6854127 100644 --- a/32blit-pico/startup.cpp +++ b/32blit-pico/startup.cpp @@ -65,7 +65,7 @@ extern "C" bool do_init() { blit::render = render; #ifndef BLIT_BOARD_PIMORONI_PICOVISION - blit::api.set_framebuffer((uint8_t *)screen_fb, sizeof(screen_fb)); + blit::api.set_framebuffer((uint8_t *)screen_fb, sizeof(screen_fb), {DISPLAY_WIDTH, DISPLAY_HEIGHT}); #endif blit::set_screen_mode(blit::ScreenMode::lores); diff --git a/32blit/engine/api_private.hpp b/32blit/engine/api_private.hpp index fa3aadac0..bfd6ea2ef 100644 --- a/32blit/engine/api_private.hpp +++ b/32blit/engine/api_private.hpp @@ -149,7 +149,7 @@ namespace blit { // low level framebuffer uint8_t *(*get_screen_data)(); // used to get current screen.data before render if firmware does page-flipping - void (*set_framebuffer)(uint8_t *data, uint32_t max_size); // pass framebuffer over if allocated on the "user" side of the API + void (*set_framebuffer)(uint8_t *data, uint32_t max_size, Size max_bounds); // pass framebuffer over if allocated on the "user" side of the API }; struct APIData { From f7375e32ca2f9679c01c2cb4bd2eb391d61f2291 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Mon, 13 Mar 2023 20:41:37 +0000 Subject: [PATCH 22/70] pico: clamp display width to max game was built for --- 32blit-pico/display.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/32blit-pico/display.cpp b/32blit-pico/display.cpp index 69ef68c58..8e74a62ef 100644 --- a/32blit-pico/display.cpp +++ b/32blit-pico/display.cpp @@ -58,6 +58,8 @@ bool set_screen_mode_format(ScreenMode new_mode, SurfaceTemplate &new_surf_templ if(new_surf_template.format == (PixelFormat)-1) new_surf_template.format = DEFAULT_SCREEN_FORMAT; + int min_buffers = 1; + switch(new_mode) { case ScreenMode::lores: if(new_surf_template.bounds.empty()) @@ -65,11 +67,21 @@ bool set_screen_mode_format(ScreenMode new_mode, SurfaceTemplate &new_surf_templ else new_surf_template.bounds /= 2; +#ifdef BUILD_LOADER + if(new_surf_template.bounds.w > max_fb_bounds.w / 2) + new_surf_template.bounds.w = max_fb_bounds.w / 2; +#endif + min_buffers = 2; break; case ScreenMode::hires: case ScreenMode::hires_palette: if(new_surf_template.bounds.empty()) new_surf_template.bounds = hires_screen_size; + +#ifdef BUILD_LOADER + if(new_surf_template.bounds.w > max_fb_bounds.w) + new_surf_template.bounds.w = max_fb_bounds.w; +#endif break; } @@ -77,7 +89,7 @@ bool set_screen_mode_format(ScreenMode new_mode, SurfaceTemplate &new_surf_templ auto fb_size = uint32_t(new_surf_template.bounds.area()) * pixel_format_stride[int(new_surf_template.format)]; // TODO: more generic "doesn't have a framebuffer"? #ifndef BLIT_BOARD_PIMORONI_PICOVISION - if(max_fb_size < fb_size) + if(max_fb_size < fb_size * min_buffers) return false; #endif From 8e43e30b391a8e2ebe29bd63d43789e7df21f3e8 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 14 Mar 2023 23:23:24 +0000 Subject: [PATCH 23/70] pico: only include PROG command if building a loader with USB in device mode --- 32blit-pico/usb.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/32blit-pico/usb.cpp b/32blit-pico/usb.cpp index 84d70264c..0f7b827ac 100644 --- a/32blit-pico/usb.cpp +++ b/32blit-pico/usb.cpp @@ -191,12 +191,18 @@ class CDCProgCommand final : public CDCCommand { static CDCHandshakeCommand handshake_command; static CDCUserCommand user_command; +#if defined(BUILD_LOADER) && !defined(USB_HOST) +#define FLASH_COMMANDS static CDCProgCommand prog_command; +#endif const std::tuple cdc_commands[]{ {to_cmd_id("MLTI"), &handshake_command}, {to_cmd_id("USER"), &user_command}, + +#ifdef FLASH_COMMANDS {to_cmd_id("PROG"), &prog_command}, +#endif }; void usb_cdc_update() { From 16b891d7ed4247e2ece78dad94a729a2aa4c45a5 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 18 Jul 2023 12:56:57 +0100 Subject: [PATCH 24/70] pico: handle metadata for blits Instead of returning the loader's data --- 32blit-pico/main.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index b24dbe9fc..df848d3c8 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -34,6 +34,7 @@ static blit::AudioChannel channels[CHANNEL_COUNT]; static int (*do_tick)(uint32_t time) = blit::tick; static uint32_t requested_launch_offset = 0; +static uint32_t current_game_offset = 0; // override terminate handler to save ~20-30k namespace __cxxabiv1 { @@ -68,9 +69,47 @@ const char *get_launch_path() { return nullptr; } +RawMetadata *get_running_game_metadata() { +#ifdef BUILD_LOADER + if(!current_game_offset) + return nullptr; + + auto game_ptr = reinterpret_cast(XIP_NOCACHE_NOALLOC_BASE + current_game_offset); + + auto header = reinterpret_cast(game_ptr); + + if(header->magic == blit_game_magic) { + auto end_ptr = game_ptr + header->end; + if(memcmp(end_ptr, "BLITMETA", 8) == 0) + return reinterpret_cast(end_ptr + 10); + } + +#endif + return nullptr; +} + static GameMetadata get_metadata() { GameMetadata ret; + // check for blit metadata + if(auto meta = get_running_game_metadata()) { + // this is identical to the 32blit-stm code + ret.title = meta->title; + ret.author = meta->author; + ret.description = meta->description; + ret.version = meta->version; + + if(memcmp(meta + 1, "BLITTYPE", 8) == 0) { + auto type_meta = reinterpret_cast(reinterpret_cast(meta) + sizeof(*meta) + 8); + ret.url = type_meta->url; + ret.category = type_meta->category; + } else { + ret.url = ""; + ret.category = "none"; + } + return ret; + } + // parse binary info extern binary_info_t *__binary_info_start, *__binary_info_end; @@ -140,10 +179,16 @@ static blit::CanLaunchResult can_launch(const char *path) { static void delayed_launch() { auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + requested_launch_offset); + + // save in case launch fails + uint32_t last_game_offset = current_game_offset; + + current_game_offset = requested_launch_offset; requested_launch_offset = 0; if(!header->init(0)) { debugf("failed to init game!\n"); + current_game_offset = last_game_offset; return; } From b14f9e7884aede6f658da0a5040d4280a8281793 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 18 Jul 2023 13:00:52 +0100 Subject: [PATCH 25/70] pico: fix save path for blits Thought I had a corrupt SD card, but it was just every game writing their saves to the same place --- 32blit-pico/file.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/32blit-pico/file.cpp b/32blit-pico/file.cpp index ba43d01c2..f025790e4 100644 --- a/32blit-pico/file.cpp +++ b/32blit-pico/file.cpp @@ -10,6 +10,7 @@ #include "file.hpp" #include "storage.hpp" +#include "executable.hpp" static FATFS fs; static bool initialised = false; @@ -81,6 +82,8 @@ void init_fs() { static char save_path[32]; // max game title length is 24 + ".blit/" + "/" +RawMetadata *get_running_game_metadata(); + const char *get_save_path() { const char *app_name = "_unknown"; @@ -89,18 +92,22 @@ const char *get_save_path() { app_name = "_unknown"; - // fint the program name in the binary info - extern binary_info_t *__binary_info_start, *__binary_info_end; + if(auto meta = get_running_game_metadata()) + app_name = meta->title; + else { + // find the program name in the binary info + extern binary_info_t *__binary_info_start, *__binary_info_end; - for(auto tag_ptr = &__binary_info_start; tag_ptr != &__binary_info_end ; tag_ptr++) { - if((*tag_ptr)->type != BINARY_INFO_TYPE_ID_AND_STRING || (*tag_ptr)->tag != BINARY_INFO_TAG_RASPBERRY_PI) - continue; + for(auto tag_ptr = &__binary_info_start; tag_ptr != &__binary_info_end ; tag_ptr++) { + if((*tag_ptr)->type != BINARY_INFO_TYPE_ID_AND_STRING || (*tag_ptr)->tag != BINARY_INFO_TAG_RASPBERRY_PI) + continue; - auto id_str_tag = (binary_info_id_and_string_t *)*tag_ptr; + auto id_str_tag = (binary_info_id_and_string_t *)*tag_ptr; - if(id_str_tag->id == BINARY_INFO_ID_RP_PROGRAM_NAME) { - app_name = id_str_tag->value; - break; + if(id_str_tag->id == BINARY_INFO_ID_RP_PROGRAM_NAME) { + app_name = id_str_tag->value; + break; + } } } From 799f91218feb7991f0a0944f923692bd377393d8 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 27 Jul 2023 12:38:45 +0100 Subject: [PATCH 26/70] pico: move code related to blit files and launching to its own file --- 32blit-pico/CMakeLists.txt | 1 + 32blit-pico/blit-launch.cpp | 114 ++++++++++++++++++++++++++++++++++++ 32blit-pico/blit-launch.hpp | 14 +++++ 32blit-pico/file.cpp | 3 +- 32blit-pico/main.cpp | 109 ++-------------------------------- 5 files changed, 134 insertions(+), 107 deletions(-) create mode 100644 32blit-pico/blit-launch.cpp create mode 100644 32blit-pico/blit-launch.hpp diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index bc605f3ee..e15aea815 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -34,6 +34,7 @@ endfunction() add_library(BlitHalPico INTERFACE) target_sources(BlitHalPico INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/blit-launch.cpp ${CMAKE_CURRENT_LIST_DIR}/display.cpp ${CMAKE_CURRENT_LIST_DIR}/file.cpp ${CMAKE_CURRENT_LIST_DIR}/led.cpp diff --git a/32blit-pico/blit-launch.cpp b/32blit-pico/blit-launch.cpp new file mode 100644 index 000000000..deffb0930 --- /dev/null +++ b/32blit-pico/blit-launch.cpp @@ -0,0 +1,114 @@ +#include + +#include "pico/stdlib.h" + +#include "blit-launch.hpp" +#include "file.hpp" + +#include "engine/engine.hpp" + +// code related to blit files and launching + +extern int (*do_tick)(uint32_t time); + +static const unsigned int game_block_size = 64 * 1024; // this is the 32blit's flash erase size, some parts of the API depend on this... + +static uint32_t requested_launch_offset = 0; +static uint32_t current_game_offset = 0; + +RawMetadata *get_running_game_metadata() { +#ifdef BUILD_LOADER + if(!current_game_offset) + return nullptr; + + auto game_ptr = reinterpret_cast(XIP_NOCACHE_NOALLOC_BASE + current_game_offset); + + auto header = reinterpret_cast(game_ptr); + + if(header->magic == blit_game_magic) { + auto end_ptr = game_ptr + header->end; + if(memcmp(end_ptr, "BLITMETA", 8) == 0) + return reinterpret_cast(end_ptr + 10); + } + +#endif + return nullptr; +} + +bool launch_file(const char *path) { + if(strncmp(path, "flash:/", 7) == 0) { + uint32_t offset = atoi(path + 7) * game_block_size; + + auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + offset); + // check header magic + device + if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) + return false; + + if(!header->init || !header->render || !header->tick) + return false; + + requested_launch_offset = offset; + return true; + } + + return false; +} + +blit::CanLaunchResult can_launch(const char *path) { + if(strncmp(path, "flash:/", 7) == 0) { + // assume anything flashed is compatible for now + return blit::CanLaunchResult::Success; + } + + return blit::CanLaunchResult::UnknownType; +} + +void delayed_launch() { + if(!requested_launch_offset) + return; + + auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + requested_launch_offset); + + // save in case launch fails + uint32_t last_game_offset = current_game_offset; + + current_game_offset = requested_launch_offset; + requested_launch_offset = 0; + + if(!header->init(0)) { + blit::debugf("failed to init game!\n"); + current_game_offset = last_game_offset; + return; + } + + blit::render = header->render; + do_tick = header->tick; +} + +void list_installed_games(std::function callback) { + for(uint32_t off = 0; off < PICO_FLASH_SIZE_BYTES;) { + auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + off); + + // check header magic + device + if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) { + off += game_block_size; + continue; + } + + auto size = header->end; + + // check metadata + auto meta_offset = off + size; + if(memcmp((char *)(XIP_NOCACHE_NOALLOC_BASE + meta_offset), "BLITMETA", 8) != 0) { + off += ((size - 1) / game_block_size + 1) * game_block_size; + continue; + } + + // add metadata size + size += *(uint16_t *)(XIP_NOCACHE_NOALLOC_BASE + meta_offset + 8) + 10; + + callback((const uint8_t *)(XIP_NOCACHE_NOALLOC_BASE + off), off / game_block_size, size); + + off += ((size - 1) / game_block_size + 1) * game_block_size; + } +} \ No newline at end of file diff --git a/32blit-pico/blit-launch.hpp b/32blit-pico/blit-launch.hpp new file mode 100644 index 000000000..67981b6ff --- /dev/null +++ b/32blit-pico/blit-launch.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "executable.hpp" +#include "engine/api_private.hpp" + +RawMetadata *get_running_game_metadata(); + +bool launch_file(const char *path); +blit::CanLaunchResult can_launch(const char *path); +void delayed_launch(); + +void list_installed_games(std::function callback); \ No newline at end of file diff --git a/32blit-pico/file.cpp b/32blit-pico/file.cpp index f025790e4..b79bd4666 100644 --- a/32blit-pico/file.cpp +++ b/32blit-pico/file.cpp @@ -8,6 +8,7 @@ #include "ff.h" #include "diskio.h" +#include "blit-launch.hpp" #include "file.hpp" #include "storage.hpp" #include "executable.hpp" @@ -82,8 +83,6 @@ void init_fs() { static char save_path[32]; // max game title length is 24 + ".blit/" + "/" -RawMetadata *get_running_game_metadata(); - const char *get_save_path() { const char *app_name = "_unknown"; diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index df848d3c8..a55e742db 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -12,6 +12,7 @@ #include "audio.hpp" #include "binary_info.hpp" +#include "blit-launch.hpp" #include "config.h" #include "display.hpp" #include "file.hpp" @@ -21,20 +22,13 @@ #include "storage.hpp" #include "usb.hpp" -#include "executable.hpp" - #include "engine/api_private.hpp" using namespace blit; -const unsigned int game_block_size = 64 * 1024; // this is the 32blit's flash erase size, some parts of the API depend on this... - static blit::AudioChannel channels[CHANNEL_COUNT]; -static int (*do_tick)(uint32_t time) = blit::tick; - -static uint32_t requested_launch_offset = 0; -static uint32_t current_game_offset = 0; +int (*do_tick)(uint32_t time) = blit::tick; // override terminate handler to save ~20-30k namespace __cxxabiv1 { @@ -69,25 +63,6 @@ const char *get_launch_path() { return nullptr; } -RawMetadata *get_running_game_metadata() { -#ifdef BUILD_LOADER - if(!current_game_offset) - return nullptr; - - auto game_ptr = reinterpret_cast(XIP_NOCACHE_NOALLOC_BASE + current_game_offset); - - auto header = reinterpret_cast(game_ptr); - - if(header->magic == blit_game_magic) { - auto end_ptr = game_ptr + header->end; - if(memcmp(end_ptr, "BLITMETA", 8) == 0) - return reinterpret_cast(end_ptr + 10); - } - -#endif - return nullptr; -} - static GameMetadata get_metadata() { GameMetadata ret; @@ -149,81 +124,6 @@ static GameMetadata get_metadata() { return ret; } -static bool launch(const char *path) { - if(strncmp(path, "flash:/", 7) == 0) { - uint32_t offset = atoi(path + 7) * game_block_size; - - auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + offset); - // check header magic + device - if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) - return false; - - if(!header->init || !header->render || !header->tick) - return false; - - requested_launch_offset = offset; - return true; - } - - return false; -} - -static blit::CanLaunchResult can_launch(const char *path) { - if(strncmp(path, "flash:/", 7) == 0) { - // assume anything flashed is compatible for now - return blit::CanLaunchResult::Success; - } - - return blit::CanLaunchResult::UnknownType; -} - -static void delayed_launch() { - auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + requested_launch_offset); - - // save in case launch fails - uint32_t last_game_offset = current_game_offset; - - current_game_offset = requested_launch_offset; - requested_launch_offset = 0; - - if(!header->init(0)) { - debugf("failed to init game!\n"); - current_game_offset = last_game_offset; - return; - } - - blit::render = header->render; - do_tick = header->tick; -} - -static void list_installed_games(std::function callback) { - for(uint32_t off = 0; off < PICO_FLASH_SIZE_BYTES;) { - auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + off); - - // check header magic + device - if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) { - off += game_block_size; - continue; - } - - auto size = header->end; - - // check metadata - auto meta_offset = off + size; - if(memcmp((char *)(XIP_NOCACHE_NOALLOC_BASE + meta_offset), "BLITMETA", 8) != 0) { - off += ((size - 1) / game_block_size + 1) * game_block_size; - continue; - } - - // add metadata size - size += *(uint16_t *)(XIP_NOCACHE_NOALLOC_BASE + meta_offset + 8) + 10; - - callback((const uint8_t *)(XIP_NOCACHE_NOALLOC_BASE + off), off / game_block_size, size); - - off += ((size - 1) / game_block_size + 1) * game_block_size; - } -} - static uint8_t *get_screen_data() { return screen.data; } @@ -264,7 +164,7 @@ static const blit::APIConst blit_api_const { nullptr, // decode_jpeg_buffer nullptr, // decode_jpeg_file - ::launch, + ::launch_file, nullptr, // erase_game nullptr, // get_type_handler_metadata @@ -397,8 +297,7 @@ int main() { update_multiplayer(); // do requested launch when no user code is running - if(requested_launch_offset) - delayed_launch(); + delayed_launch(); if(ms_to_next_update > 1 && !display_render_needed()) best_effort_wfe_or_timeout(make_timeout_time_ms(ms_to_next_update - 1)); From f1bf270a891366ef5fdd8cc2ba250955672191dd Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Sat, 29 Jul 2023 18:46:26 +0100 Subject: [PATCH 27/70] pico: refactor the flash writing part of the prog command into a class --- 32blit-pico/blit-launch.cpp | 81 +++++++++++++++++++++++++++++++++++++ 32blit-pico/blit-launch.hpp | 20 ++++++++- 32blit-pico/usb.cpp | 79 +++++------------------------------- 3 files changed, 111 insertions(+), 69 deletions(-) diff --git a/32blit-pico/blit-launch.cpp b/32blit-pico/blit-launch.cpp index deffb0930..e55831cf4 100644 --- a/32blit-pico/blit-launch.cpp +++ b/32blit-pico/blit-launch.cpp @@ -1,6 +1,9 @@ #include #include "pico/stdlib.h" +#include "hardware/flash.h" +#include "hardware/sync.h" +#include "pico/multicore.h" #include "blit-launch.hpp" #include "file.hpp" @@ -11,6 +14,10 @@ extern int (*do_tick)(uint32_t time); +void disable_user_code(); + +extern bool core1_started; + static const unsigned int game_block_size = 64 * 1024; // this is the 32blit's flash erase size, some parts of the API depend on this... static uint32_t requested_launch_offset = 0; @@ -111,4 +118,78 @@ void list_installed_games(std::functionfile_len = file_len; + file_offset = flash_offset = 0; +} + +bool BlitWriter::write(const uint8_t *buf, uint32_t len) { + if(!flash_offset) { + if(!prepare_write(buf)) + return false; + } + + if(file_offset >= file_len) + return false; + + // write page + auto status = save_and_disable_interrupts(); + + if(core1_started) + multicore_lockout_start_blocking(); // pause core1 + + // assuming len <= page size and buf size == page size + flash_range_program(flash_offset + file_offset, buf, FLASH_PAGE_SIZE); + + if(core1_started) + multicore_lockout_end_blocking(); // resume core1 + + restore_interrupts(status); + + file_offset += len; + + return true; +} + +uint32_t BlitWriter::get_remaining() const { + return file_len - file_offset; +} + +uint32_t BlitWriter::get_flash_offset() const { + return flash_offset; +} + +bool BlitWriter::prepare_write(const uint8_t *buf) { + auto header = (BlitGameHeader *)buf; + + if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) { + blit::debugf("Invalid blit header!"); + return false; + } + + // currently non-relocatable, so base address is stored after header + flash_offset = *(uint32_t *)(buf + sizeof(BlitGameHeader)); + flash_offset &= 0xFFFFFF; + + printf("PROG: flash off %lu\n", flash_offset); + + disable_user_code(); + + // erase flash + auto status = save_and_disable_interrupts(); + + if(core1_started) + multicore_lockout_start_blocking(); // pause core1 + + auto erase_size = ((file_len - 1) / FLASH_SECTOR_SIZE) + 1; + flash_range_erase(flash_offset, erase_size * FLASH_SECTOR_SIZE); + + if(core1_started) + multicore_lockout_end_blocking(); // resume core1 + + restore_interrupts(status); + + return true; } \ No newline at end of file diff --git a/32blit-pico/blit-launch.hpp b/32blit-pico/blit-launch.hpp index 67981b6ff..70d94f7cf 100644 --- a/32blit-pico/blit-launch.hpp +++ b/32blit-pico/blit-launch.hpp @@ -11,4 +11,22 @@ bool launch_file(const char *path); blit::CanLaunchResult can_launch(const char *path); void delayed_launch(); -void list_installed_games(std::function callback); \ No newline at end of file +void list_installed_games(std::function callback); + +class BlitWriter final { +public: + void init(uint32_t file_len); + + bool write(const uint8_t *buf, uint32_t len); + + uint32_t get_remaining() const; + uint32_t get_flash_offset() const; + +private: + bool prepare_write(const uint8_t *buf); + + uint32_t file_len; + uint32_t file_offset; + + uint32_t flash_offset; +}; \ No newline at end of file diff --git a/32blit-pico/usb.cpp b/32blit-pico/usb.cpp index 0f7b827ac..ad2f3dbfb 100644 --- a/32blit-pico/usb.cpp +++ b/32blit-pico/usb.cpp @@ -3,10 +3,9 @@ #include #include "hardware/flash.h" -#include "hardware/sync.h" -#include "pico/multicore.h" #include "usb.hpp" +#include "blit-launch.hpp" #include "multiplayer.hpp" #include "engine/engine.hpp" @@ -14,10 +13,6 @@ void init(); -void disable_user_code(); - -extern bool core1_started; - static uint8_t cur_header[8]; static int header_pos = 0; static CDCCommand *cur_command = nullptr; @@ -33,7 +28,6 @@ class CDCProgCommand final : public CDCCommand { void init() override { parse_state = ParseState::Filename; buf_off = 0; - file_offset = flash_offset = 0; } Status update() override { @@ -66,10 +60,12 @@ class CDCProgCommand final : public CDCCommand { // end of string if(buf[buf_off] == 0) { - file_len = strtoul((const char *)buf, nullptr, 10); + auto file_len = strtoul((const char *)buf, nullptr, 10); printf("PROG: len %lu\n", file_len); parse_state = ParseState::Data; buf_off = 0; + + writer.init(file_len); continue; } @@ -84,7 +80,7 @@ class CDCProgCommand final : public CDCCommand { case ParseState::Data: { // read data - auto max = std::min(FLASH_PAGE_SIZE - buf_off, file_len - file_offset); + auto max = std::min(uint32_t(FLASH_PAGE_SIZE), writer.get_remaining()) - buf_off; auto read = usb_cdc_read(buf + buf_off, max); if(!read) @@ -93,33 +89,17 @@ class CDCProgCommand final : public CDCCommand { buf_off += read; // got full page or final part of file - if(buf_off == FLASH_PAGE_SIZE || file_offset + buf_off == file_len) { - if(!flash_offset) { - if(!prepare_write()) - return Status::Error; - } - - // write page - auto status = save_and_disable_interrupts(); - - if(core1_started) - multicore_lockout_start_blocking(); // pause core1 - - flash_range_program(flash_offset + file_offset, buf, FLASH_PAGE_SIZE); + if(buf_off == FLASH_PAGE_SIZE || buf_off == writer.get_remaining()) { + if(!writer.write(buf, buf_off)) + return Status::Error; - if(core1_started) - multicore_lockout_end_blocking(); // resume core1 - - restore_interrupts(status); - - file_offset += buf_off; buf_off = 0; } // end of file - if(file_offset == file_len) { + if(writer.get_remaining() == 0) { // send response - auto block = flash_offset >> 16; + auto block = writer.get_flash_offset() >> 16; uint8_t res_data[]{'3', '2', 'B', 'L', '_', '_', 'O', 'K', uint8_t(block), uint8_t(block >> 8)}; usb_cdc_write(res_data, sizeof(res_data)); usb_cdc_flush_write(); @@ -138,40 +118,6 @@ class CDCProgCommand final : public CDCCommand { return Status::Continue; } - - bool prepare_write() { - auto header = (BlitGameHeader *)buf; - - if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) { - blit::debugf("Invalid blit header!"); - return false; - } - - // corrently non-relocatable, so base address is stored after header - flash_offset = *(uint32_t *)(buf + sizeof(BlitGameHeader)); - flash_offset &= 0xFFFFFF; - - printf("PROG: flash off %lu\n", flash_offset); - - disable_user_code(); - - // erase flash - auto status = save_and_disable_interrupts(); - - if(core1_started) - multicore_lockout_start_blocking(); // pause core1 - - auto erase_size = ((file_len - 1) / FLASH_SECTOR_SIZE) + 1; - flash_range_erase(flash_offset, erase_size * FLASH_SECTOR_SIZE); - - if(core1_started) - multicore_lockout_end_blocking(); // resume core1 - - restore_interrupts(status); - - return true; - } - enum class ParseState { Filename, Length, @@ -182,10 +128,7 @@ class CDCProgCommand final : public CDCCommand { uint8_t buf[buf_size]; uint32_t buf_off = 0; - uint32_t file_len; - uint32_t file_offset; - - uint32_t flash_offset; + BlitWriter writer; }; static CDCHandshakeCommand handshake_command; From 22380c4c378558a97b042dfe25e1b2f17c89c9be Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Sun, 30 Jul 2023 18:43:58 +0100 Subject: [PATCH 28/70] pico: handle launching files from storage (SD card) Technically it would work from the flash-based storage as well, but that seems a bit silly --- 32blit-pico/blit-launch.cpp | 83 +++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/32blit-pico/blit-launch.cpp b/32blit-pico/blit-launch.cpp index e55831cf4..72613a7e0 100644 --- a/32blit-pico/blit-launch.cpp +++ b/32blit-pico/blit-launch.cpp @@ -9,6 +9,7 @@ #include "file.hpp" #include "engine/engine.hpp" +#include "engine/file.hpp" // code related to blit files and launching @@ -43,30 +44,94 @@ RawMetadata *get_running_game_metadata() { } bool launch_file(const char *path) { - if(strncmp(path, "flash:/", 7) == 0) { - uint32_t offset = atoi(path + 7) * game_block_size; + uint32_t flash_offset; - auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + offset); - // check header magic + device - if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) + if(strncmp(path, "flash:/", 7) == 0) // from flash + flash_offset = atoi(path + 7) * game_block_size; + else { + // from storage + // TODO: check if already in flash + + auto file = open_file(path, blit::OpenMode::read); + + if(!file) return false; - if(!header->init || !header->render || !header->tick) + BlitWriter writer; + + uint32_t file_offset = 0; + uint32_t len = get_file_length(file); + + writer.init(len); + + // read in small chunks + uint8_t buf[FLASH_PAGE_SIZE]; + + while(file_offset < len) { + auto bytes_read = read_file(file, file_offset, FLASH_PAGE_SIZE, (char *)buf); + if(bytes_read <= 0) + break; + + if(!writer.write(buf, bytes_read)) + break; + + file_offset += bytes_read; + } + + close_file(file); + + // didn't write everything, fail launch + if(writer.get_remaining() > 0) return false; - requested_launch_offset = offset; - return true; + flash_offset = writer.get_flash_offset(); } - return false; + auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + flash_offset); + // check header magic + device + if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) + return false; + + if(!header->init || !header->render || !header->tick) + return false; + + requested_launch_offset = flash_offset; + return true; } blit::CanLaunchResult can_launch(const char *path) { +#ifdef BUILD_LOADER if(strncmp(path, "flash:/", 7) == 0) { // assume anything flashed is compatible for now return blit::CanLaunchResult::Success; } + // get the extension + std::string_view sv(path); + auto last_dot = sv.find_last_of('.'); + auto ext = last_dot == std::string::npos ? "" : std::string(sv.substr(last_dot + 1)); + for(auto &c : ext) + c = tolower(c); + + if(ext == "blit") { + BlitGameHeader header; + auto file = open_file(path, blit::OpenMode::read); + + if(!file) + return blit::CanLaunchResult::InvalidFile; + + auto bytes_read = read_file(file, 0, sizeof(header), (char *)&header); + + if(bytes_read == sizeof(header) && header.magic == blit_game_magic && header.device_id == BlitDevice::RP2040) { + close_file(file); + return blit::CanLaunchResult::Success; + } + + close_file(file); + return blit::CanLaunchResult::IncompatibleBlit; + } +#endif + return blit::CanLaunchResult::UnknownType; } From 95cae8bd43eb44fa635c109f9788b2434e13f318 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Mon, 31 Jul 2023 13:53:45 +0100 Subject: [PATCH 29/70] pico: auto install launcher from storage This should be replaced with the auto-update logig from the STM32 firmware at some point --- 32blit-pico/loader/loader.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/32blit-pico/loader/loader.cpp b/32blit-pico/loader/loader.cpp index 65b908c56..a8703ab0b 100644 --- a/32blit-pico/loader/loader.cpp +++ b/32blit-pico/loader/loader.cpp @@ -13,6 +13,14 @@ void init() { done = api.launch(path.c_str()); } }); + + if(!done) { + // fall back to launcher in storage + // TODO: auto-update + if(file_exists("launcher.blit")) { + api.launch("launcher.blit"); + } + } } void render(uint32_t time) { From 2cedc32335b9d8c04a93cae9576d6d45d1dd80a4 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 13 Jul 2023 12:58:45 +0100 Subject: [PATCH 30/70] ci: test building pico-blits for picosystem --- .github/workflows/cmake.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 9caf24af4..94918c939 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -41,6 +41,14 @@ jobs: apt-packages: ccache gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib pipx python3-requests - os: ubuntu-22.04 + pico-sdk: true + name: PicoSystem (.blit) + cache-key: picosystem-bl + release-suffix: PicoSystem-blit + cmake-args: -D32BLIT_DIR=$GITHUB_WORKSPACE -DPICO_SDK_PATH=$GITHUB_WORKSPACE/pico-sdk -DPICO_BOARD=pimoroni_picosystem -DBLIT_EXECUTABLE_PICO_BLIT=1 -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + apt-packages: ccache gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib python3-setuptools + + - os: ubuntu-20.04 pico-sdk: true name: PicoVision cache-key: picovision From 169bc98f6785d88e061c08585f1e8c35be91d116 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Mon, 24 Jun 2024 16:21:50 +0100 Subject: [PATCH 31/70] pico: don't init null command Good way to fault... --- 32blit-pico/usb.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/32blit-pico/usb.cpp b/32blit-pico/usb.cpp index ad2f3dbfb..1180e09fe 100644 --- a/32blit-pico/usb.cpp +++ b/32blit-pico/usb.cpp @@ -188,8 +188,9 @@ void usb_cdc_update() { if(!cur_command) printf("got: %c%c%c%c%c%c%c%c\n", cur_header[0], cur_header[1], cur_header[2], cur_header[3], cur_header[4], cur_header[5], cur_header[6], cur_header[7]); + else + cur_command->init(); - cur_command->init(); header_pos = 0; } } From 8fb441ed6fa084cc5d9e6ed48bdb0a07ab70deeb Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Mon, 24 Jun 2024 17:00:11 +0100 Subject: [PATCH 32/70] pico: allow overriding flash offset for blits --- 32blit-config.cmake | 14 ++++++++++++++ 32blit-pico/CMakeLists.txt | 18 +++++++++++++++--- .../{memmap_blit.ld => memmap_blit.ld.in} | 2 +- 3 files changed, 30 insertions(+), 4 deletions(-) rename 32blit-pico/{memmap_blit.ld => memmap_blit.ld.in} (98%) diff --git a/32blit-config.cmake b/32blit-config.cmake index 1beb72166..052515ca0 100644 --- a/32blit-config.cmake +++ b/32blit-config.cmake @@ -99,6 +99,12 @@ if (NOT DEFINED BLIT_ONCE) set(PICO_BLIT FALSE) endif() + if(DEFINED BLIT_EXECUTABLE_PICO_BLIT_OFFSET_KB) + set(PICO_BLIT_OFFSET_KB ${BLIT_EXECUTABLE_PICO_BLIT_OFFSET_KB}) + else() + set(PICO_BLIT_OFFSET_KB 512) + endif() + foreach(arg IN LISTS ARGN) if(arg STREQUAL "INTERNAL_FLASH") set(${arg} TRUE) @@ -108,6 +114,13 @@ if (NOT DEFINED BLIT_ONCE) elseif(arg STREQUAL "PICO_BLIT") set(${arg} TRUE) set(PICO_STANDALONE_UF2 FALSE) + # args with value + elseif(arg STREQUAL "PICO_BLIT_OFFSET_KB") + set(prev_arg ${arg}) + # value for args with one + elseif(prev_arg STREQUAL "PICO_BLIT_OFFSET_KB") + set(${prev_arg} ${arg}) + unset(prev_arg) else() list(APPEND SOURCES ${arg}) endif() @@ -117,6 +130,7 @@ if (NOT DEFINED BLIT_ONCE) set(INTERNAL_FLASH ${INTERNAL_FLASH} PARENT_SCOPE) set(PICO_STANDALONE_UF2 ${PICO_STANDALONE_UF2} PARENT_SCOPE) set(PICO_BLIT ${PICO_BLIT} PARENT_SCOPE) + set(PICO_BLIT_OFFSET_KB ${PICO_BLIT_OFFSET_KB} PARENT_SCOPE) endfunction() if (32BLIT_HW) diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index e15aea815..2dd3e78fe 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -176,7 +176,7 @@ target_link_libraries(BlitHalPico INTERFACE ${BLIT_BOARD_LIBRARIES}) set(BLIT_BOARD_DEFINITIONS ${BLIT_BOARD_DEFINITIONS} PARENT_SCOPE) # some file paths we need later -set(LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_blit.ld PARENT_SCOPE) +set(LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_blit.ld.in PARENT_SCOPE) set(STARTUP_SRC ${CMAKE_CURRENT_LIST_DIR}/startup.S ${CMAKE_CURRENT_LIST_DIR}/startup.cpp PARENT_SCOPE) set(LAUNCHERSHARED_DIR ${CMAKE_CURRENT_LIST_DIR}/../launcher-shared PARENT_SCOPE) @@ -210,11 +210,23 @@ function(blit_executable NAME) # loadable .blit set_property(TARGET ${NAME} PROPERTY BLIT_PICO_BLIT TRUE) + # calculate and verify flash offset + math(EXPR offset_mod "${PICO_BLIT_OFFSET_KB} % 64") + if(${PICO_BLIT_OFFSET_KB} LESS 256 OR offset_mod) + message(FATAL_ERROR "Blit offset should be >= 256k and a multiple of 64k") + endif() + + math(EXPR FLASH_OFFSET_BYTES "${PICO_BLIT_OFFSET_KB} * 1024") + + # customise linker script + configure_file(${LINKER_SCRIPT} memmap_blit.ld) + set(LINKER_SCRIPT_OUT ${CMAKE_CURRENT_BINARY_DIR}/memmap_blit.ld) + target_compile_definitions(${NAME} PRIVATE ${BLIT_BOARD_DEFINITIONS}) # need these for framebuffer config target_compile_options(${NAME} PRIVATE -ffunction-sections -fdata-sections) target_link_libraries(${NAME} pico_bit_ops pico_cxx_options pico_divider pico_double pico_int64_ops pico_float pico_malloc pico_mem_ops) - target_link_options(${NAME} PRIVATE --specs=nosys.specs LINKER:--script=${LINKER_SCRIPT} LINKER:--gc-sections) - set_property(TARGET ${NAME} APPEND PROPERTY LINK_DEPENDS ${LINKER_SCRIPT}) + target_link_options(${NAME} PRIVATE --specs=nosys.specs LINKER:--script=${LINKER_SCRIPT_OUT} LINKER:--gc-sections) + set_property(TARGET ${NAME} APPEND PROPERTY LINK_DEPENDS ${LINKER_SCRIPT_OUT}) target_sources(${NAME} PRIVATE ${STARTUP_SRC}) diff --git a/32blit-pico/memmap_blit.ld b/32blit-pico/memmap_blit.ld.in similarity index 98% rename from 32blit-pico/memmap_blit.ld rename to 32blit-pico/memmap_blit.ld.in index 9b9e183f8..b7ee087a9 100644 --- a/32blit-pico/memmap_blit.ld +++ b/32blit-pico/memmap_blit.ld.in @@ -23,7 +23,7 @@ MEMORY { - FLASH(rx) : ORIGIN = 0x10080000, LENGTH = 2048k + FLASH(rx) : ORIGIN = 0x10000000 + ${FLASH_OFFSET_BYTES}, LENGTH = 2048k RAM(rwx) : ORIGIN = 0x20008000, LENGTH = 224k /*SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k*/ From af9289060de608f40028ddeac1aded0b0e3fc520 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Mon, 24 Jun 2024 17:00:36 +0100 Subject: [PATCH 33/70] launcher: build at a lower offset for pico --- launcher/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 592509a73..7f0387285 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -2,7 +2,11 @@ cmake_minimum_required(VERSION 3.9...3.26) project (launcher) find_package (32BLIT CONFIG REQUIRED PATHS ..) -blit_executable(launcher launcher.cpp theme.cpp credits.cpp) +blit_executable(launcher + PICO_BLIT_OFFSET_KB 256 + launcher.cpp theme.cpp credits.cpp +) + target_link_libraries(launcher LauncherShared) blit_assets_yaml(launcher assets.yml) blit_metadata(launcher metadata.yml) From 34e2d43a161243730f06d474a9a76dfc8481b131 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 25 Jun 2024 20:15:10 +0100 Subject: [PATCH 34/70] pico: list games with missing metadata This is needed to run things loaded with gdb/openocd and also consistent with the stm32 firmware --- 32blit-pico/blit-launch.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/32blit-pico/blit-launch.cpp b/32blit-pico/blit-launch.cpp index 72613a7e0..9fd75e2f6 100644 --- a/32blit-pico/blit-launch.cpp +++ b/32blit-pico/blit-launch.cpp @@ -171,14 +171,11 @@ void list_installed_games(std::function Date: Tue, 25 Jun 2024 20:52:27 +0100 Subject: [PATCH 35/70] pico: move game block size to header --- 32blit-pico/blit-launch.cpp | 2 -- 32blit-pico/blit-launch.hpp | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/32blit-pico/blit-launch.cpp b/32blit-pico/blit-launch.cpp index 9fd75e2f6..584732b85 100644 --- a/32blit-pico/blit-launch.cpp +++ b/32blit-pico/blit-launch.cpp @@ -19,8 +19,6 @@ void disable_user_code(); extern bool core1_started; -static const unsigned int game_block_size = 64 * 1024; // this is the 32blit's flash erase size, some parts of the API depend on this... - static uint32_t requested_launch_offset = 0; static uint32_t current_game_offset = 0; diff --git a/32blit-pico/blit-launch.hpp b/32blit-pico/blit-launch.hpp index 70d94f7cf..ab9ad2e2b 100644 --- a/32blit-pico/blit-launch.hpp +++ b/32blit-pico/blit-launch.hpp @@ -5,6 +5,9 @@ #include "executable.hpp" #include "engine/api_private.hpp" +// this is the 32blit's flash erase size, some parts of the API depend on this... +static constexpr unsigned int game_block_size = 64 * 1024; + RawMetadata *get_running_game_metadata(); bool launch_file(const char *path); From 6dfd0b695166ec21c54be101d9de347d10257fb7 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 25 Jun 2024 20:53:28 +0100 Subject: [PATCH 36/70] pico: handle __LS command This can be a wrapper around the list_installed_games API --- 32blit-pico/usb.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/32blit-pico/usb.cpp b/32blit-pico/usb.cpp index 1180e09fe..7b90d5381 100644 --- a/32blit-pico/usb.cpp +++ b/32blit-pico/usb.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include "hardware/flash.h" @@ -131,12 +132,50 @@ class CDCProgCommand final : public CDCCommand { BlitWriter writer; }; +class CDCListCommand final : public CDCCommand { + void init() override { + } + + Status update() override { + blit::api.list_installed_games([](const uint8_t *ptr, uint32_t block, uint32_t size) { + // bit of a mismatch between the API and the CDC API + // this wants offset in bytes and the size WITHOUT metadata + uint32_t offset_bytes = block * game_block_size; + uint32_t header_size = ((BlitGameHeader *)ptr)->end & 0x1FFFFFF; + + usb_cdc_write((uint8_t *)&offset_bytes, sizeof(uint32_t)); + usb_cdc_write((uint8_t *)&header_size, sizeof(uint32_t)); + usb_cdc_flush_write(); + + // write metadata if found + auto meta = ptr + header_size; + + if(memcmp(meta, "BLITMETA", 8) == 0) { + auto meta_size = *(uint16_t *)(meta + 8); + usb_cdc_write(meta, meta_size + 10); + } else { + // no meta, write header + 0 len + usb_cdc_write((const uint8_t *)"BLITMETA\0", 10); + } + usb_cdc_flush_write(); + }); + + // end marker + uint32_t end = 0xFFFFFFFF; + usb_cdc_write((uint8_t *)&end, sizeof(uint32_t)); + usb_cdc_flush_write(); + + return Status::Done; + } +}; + static CDCHandshakeCommand handshake_command; static CDCUserCommand user_command; #if defined(BUILD_LOADER) && !defined(USB_HOST) #define FLASH_COMMANDS static CDCProgCommand prog_command; +static CDCListCommand list_command; #endif const std::tuple cdc_commands[]{ @@ -145,6 +184,7 @@ const std::tuple cdc_commands[]{ #ifdef FLASH_COMMANDS {to_cmd_id("PROG"), &prog_command}, + {to_cmd_id("__LS"), &list_command}, #endif }; From 7c5fdb33da3da5292dd9ee63ff0704ff5ebfc882 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Wed, 26 Jun 2024 15:28:21 +0100 Subject: [PATCH 37/70] pico: move filename/size parsing buffer out of PROG command I'm going to need to parse a filename in other commands and don't want to use more memory. --- 32blit-pico/usb.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/32blit-pico/usb.cpp b/32blit-pico/usb.cpp index 7b90d5381..03285053d 100644 --- a/32blit-pico/usb.cpp +++ b/32blit-pico/usb.cpp @@ -12,19 +12,24 @@ #include "engine/engine.hpp" #include "executable.hpp" +#define MAX_FILENAME 256+1 +#define MAX_FILELEN 16+1 + void init(); static uint8_t cur_header[8]; static int header_pos = 0; static CDCCommand *cur_command = nullptr; +// shared between multiple command handlers +// which is okay as only one will be active at once +static const size_t cdc_parse_buffer_size = std::max((unsigned)std::max(MAX_FILELEN, MAX_FILENAME), FLASH_PAGE_SIZE); +static uint8_t cdc_parse_buffer[cdc_parse_buffer_size]; + static constexpr uint32_t to_cmd_id(const char str[4]) { return str[0] | str[1] << 8 | str[2] << 16 | str[3] << 24; } -#define MAX_FILENAME 256+1 -#define MAX_FILELEN 16+1 - class CDCProgCommand final : public CDCCommand { void init() override { parse_state = ParseState::Filename; @@ -32,6 +37,8 @@ class CDCProgCommand final : public CDCCommand { } Status update() override { + auto buf = cdc_parse_buffer; + while(true) { switch(parse_state) { case ParseState::Filename: { @@ -125,8 +132,6 @@ class CDCProgCommand final : public CDCCommand { Data } parse_state = ParseState::Filename; - static const size_t buf_size = std::max((unsigned)std::max(MAX_FILELEN, MAX_FILENAME), FLASH_PAGE_SIZE); - uint8_t buf[buf_size]; uint32_t buf_off = 0; BlitWriter writer; From 240935c2e22c948fec7eec81a14356c0b39c9a5e Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Wed, 26 Jun 2024 15:53:49 +0100 Subject: [PATCH 38/70] pico: handle LNCH command This is a wrapper around api.launch --- 32blit-pico/usb.cpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/32blit-pico/usb.cpp b/32blit-pico/usb.cpp index 03285053d..83369585b 100644 --- a/32blit-pico/usb.cpp +++ b/32blit-pico/usb.cpp @@ -174,6 +174,38 @@ class CDCListCommand final : public CDCCommand { } }; +class CDCLaunchCommand final : public CDCCommand { + void init() override { + buf_off = 0; + } + + Status update() override { + auto buf = cdc_parse_buffer; + + while(true) { + if(!usb_cdc_read(buf + buf_off, 1)) + return Status::Continue; + + // end of string + if(buf[buf_off] == 0) { + buf_off = 0; + blit::api.launch((const char *)buf); + return Status::Done; + } + + buf_off++; + + // too long + if(buf_off == MAX_FILENAME) + return Status::Error; + } + + return Status::Continue; + } + + uint32_t buf_off = 0; +}; + static CDCHandshakeCommand handshake_command; static CDCUserCommand user_command; @@ -181,6 +213,7 @@ static CDCUserCommand user_command; #define FLASH_COMMANDS static CDCProgCommand prog_command; static CDCListCommand list_command; +static CDCLaunchCommand launch_command; #endif const std::tuple cdc_commands[]{ @@ -190,6 +223,7 @@ const std::tuple cdc_commands[]{ #ifdef FLASH_COMMANDS {to_cmd_id("PROG"), &prog_command}, {to_cmd_id("__LS"), &list_command}, + {to_cmd_id("LNCH"), &launch_command}, #endif }; From 6a9825fda2c4b97eadeb05eb1dc09d6b99b169aa Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 27 Jun 2024 15:59:18 +0100 Subject: [PATCH 39/70] pico: refactor some helpers out of list_installed_games --- 32blit-pico/blit-launch.cpp | 39 ++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/32blit-pico/blit-launch.cpp b/32blit-pico/blit-launch.cpp index 584732b85..e9820904c 100644 --- a/32blit-pico/blit-launch.cpp +++ b/32blit-pico/blit-launch.cpp @@ -155,28 +155,41 @@ void delayed_launch() { do_tick = header->tick; } +static uint32_t get_installed_file_size(uint32_t offset) { + auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + offset); + + // check header magic + device + if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) + return 0; + + auto size = header->end; + + // check metadata + auto meta_offset = offset + size; + if(memcmp((char *)(XIP_NOCACHE_NOALLOC_BASE + meta_offset), "BLITMETA", 8) == 0) { + // add metadata size + size += *(uint16_t *)(XIP_NOCACHE_NOALLOC_BASE + meta_offset + 8) + 10; + } + + return size; +} + +static uint32_t calc_num_blocks(uint32_t size) { + return (size - 1) / game_block_size + 1; +} + void list_installed_games(std::function callback) { for(uint32_t off = 0; off < PICO_FLASH_SIZE_BYTES;) { - auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + off); + auto size = get_installed_file_size(off); - // check header magic + device - if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) { + if(!size) { off += game_block_size; continue; } - auto size = header->end; - - // check metadata - auto meta_offset = off + size; - if(memcmp((char *)(XIP_NOCACHE_NOALLOC_BASE + meta_offset), "BLITMETA", 8) == 0) { - // add metadata size - size += *(uint16_t *)(XIP_NOCACHE_NOALLOC_BASE + meta_offset + 8) + 10; - } - callback((const uint8_t *)(XIP_NOCACHE_NOALLOC_BASE + off), off / game_block_size, size); - off += ((size - 1) / game_block_size + 1) * game_block_size; + off += calc_num_blocks(size) * game_block_size; } } From 87d9880ac32dbad6a8f5e28c86e2b295fd2d6c39 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 27 Jun 2024 20:29:11 +0100 Subject: [PATCH 40/70] pico: implement erase_game API --- 32blit-pico/blit-launch.cpp | 33 +++++++++++++++++++++++++++++++++ 32blit-pico/blit-launch.hpp | 2 ++ 32blit-pico/main.cpp | 2 +- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/32blit-pico/blit-launch.cpp b/32blit-pico/blit-launch.cpp index e9820904c..e2a753779 100644 --- a/32blit-pico/blit-launch.cpp +++ b/32blit-pico/blit-launch.cpp @@ -193,6 +193,39 @@ void list_installed_games(std::function= PICO_FLASH_SIZE_BYTES) + return; + + auto size = get_installed_file_size(offset); + + // fall back to one block if size unknown + auto num_blocks = size == 0 ? 1 : calc_num_blocks(size); + + // do erase + auto status = save_and_disable_interrupts(); + + if(core1_started) + multicore_lockout_start_blocking(); // pause core1 + + // the real erase size is smaller than the one baked into the API... + static_assert(game_block_size % FLASH_SECTOR_SIZE == 0); + flash_range_erase(offset, num_blocks * game_block_size); + + if(core1_started) + multicore_lockout_end_blocking(); // resume core1 + + restore_interrupts(status); +#endif +} + void BlitWriter::init(uint32_t file_len) { this->file_len = file_len; file_offset = flash_offset = 0; diff --git a/32blit-pico/blit-launch.hpp b/32blit-pico/blit-launch.hpp index ab9ad2e2b..0b6a36398 100644 --- a/32blit-pico/blit-launch.hpp +++ b/32blit-pico/blit-launch.hpp @@ -16,6 +16,8 @@ void delayed_launch(); void list_installed_games(std::function callback); +void erase_game(uint32_t offset); + class BlitWriter final { public: void init(uint32_t file_len); diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index a55e742db..2b2e98a03 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -165,7 +165,7 @@ static const blit::APIConst blit_api_const { nullptr, // decode_jpeg_file ::launch_file, - nullptr, // erase_game + ::erase_game, nullptr, // get_type_handler_metadata ::get_launch_path, From 5613f2bfb5b4ca6187c72348a33f7436c92441cf Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 28 Jun 2024 16:27:08 +0100 Subject: [PATCH 41/70] pico: handle ERSE command Another api wrapper --- 32blit-pico/usb.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/32blit-pico/usb.cpp b/32blit-pico/usb.cpp index 83369585b..89c808740 100644 --- a/32blit-pico/usb.cpp +++ b/32blit-pico/usb.cpp @@ -206,6 +206,35 @@ class CDCLaunchCommand final : public CDCCommand { uint32_t buf_off = 0; }; +class CDCEraseCommand final : public CDCCommand { + void init() override { + buf_off = 0; + } + + Status update() override { + auto buf = cdc_parse_buffer; + + while(true) { + if(!usb_cdc_read(buf + buf_off, 1)) + return Status::Continue; + + buf_off++; + + // end of word + if(buf_off == 4) { + buf_off = 0; + blit::api.erase_game(*(uint32_t *)buf); + return Status::Done; + } + } + + return Status::Continue; + } + + uint32_t buf_off = 0; +}; + + static CDCHandshakeCommand handshake_command; static CDCUserCommand user_command; @@ -214,6 +243,7 @@ static CDCUserCommand user_command; static CDCProgCommand prog_command; static CDCListCommand list_command; static CDCLaunchCommand launch_command; +static CDCEraseCommand erase_command; #endif const std::tuple cdc_commands[]{ @@ -224,6 +254,7 @@ const std::tuple cdc_commands[]{ {to_cmd_id("PROG"), &prog_command}, {to_cmd_id("__LS"), &list_command}, {to_cmd_id("LNCH"), &launch_command}, + {to_cmd_id("ERSE"), &erase_command}, #endif }; From 10fa85e854f37430bc02ac5870bcfa2714c22706 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Sat, 29 Jun 2024 20:06:55 +0100 Subject: [PATCH 42/70] pico: refactor cdc buffer Now a class with some helpers instead of a global uint8_t array --- 32blit-pico/usb.cpp | 129 +++++++++++++++++++++++++++----------------- 1 file changed, 81 insertions(+), 48 deletions(-) diff --git a/32blit-pico/usb.cpp b/32blit-pico/usb.cpp index 89c808740..c62e915a7 100644 --- a/32blit-pico/usb.cpp +++ b/32blit-pico/usb.cpp @@ -21,66 +21,95 @@ static uint8_t cur_header[8]; static int header_pos = 0; static CDCCommand *cur_command = nullptr; -// shared between multiple command handlers -// which is okay as only one will be active at once -static const size_t cdc_parse_buffer_size = std::max((unsigned)std::max(MAX_FILELEN, MAX_FILENAME), FLASH_PAGE_SIZE); -static uint8_t cdc_parse_buffer[cdc_parse_buffer_size]; - static constexpr uint32_t to_cmd_id(const char str[4]) { return str[0] | str[1] << 8 | str[2] << 16 | str[3] << 24; } +class CDCParseBuffer final { +public: + void reset() { + offset = 0; + } + + uint8_t *get_data() { + return buffer; + } + + uint8_t *get_current_ptr() { + return buffer + offset; + } + + void add_read(uint32_t count) { + offset += count; + assert(offset <= buffer_size); + } + + uint32_t get_offset() const { + return offset; + } + +private: + static const size_t buffer_size = std::max((unsigned)std::max(MAX_FILELEN, MAX_FILENAME), FLASH_PAGE_SIZE); + uint8_t buffer[buffer_size]; + + uint32_t offset = 0; +}; + class CDCProgCommand final : public CDCCommand { +public: + CDCProgCommand(CDCParseBuffer &buf) : buf(buf) {} + void init() override { parse_state = ParseState::Filename; - buf_off = 0; + buf.reset(); } Status update() override { - auto buf = cdc_parse_buffer; - + while(true) { switch(parse_state) { case ParseState::Filename: { - if(!usb_cdc_read(buf + buf_off, 1)) + auto buf_ptr = buf.get_current_ptr(); + if(!usb_cdc_read(buf_ptr, 1)) return Status::Continue; // end of string - if(buf[buf_off] == 0) { - printf("PROG: file %s\n", buf); + if(*buf_ptr == 0) { + printf("PROG: file %s\n", buf.get_data()); parse_state = ParseState::Length; - buf_off = 0; + buf.reset(); continue; } - buf_off++; + buf.add_read(1); // too long - if(buf_off == MAX_FILENAME) + if(buf.get_offset() == MAX_FILENAME) return Status::Error; break; } case ParseState::Length: { - if(!usb_cdc_read(buf + buf_off, 1)) + auto buf_ptr = buf.get_current_ptr(); + if(!usb_cdc_read(buf_ptr, 1)) return Status::Continue; // end of string - if(buf[buf_off] == 0) { - auto file_len = strtoul((const char *)buf, nullptr, 10); + if(*buf_ptr == 0) { + auto file_len = strtoul((const char *)buf.get_data(), nullptr, 10); printf("PROG: len %lu\n", file_len); parse_state = ParseState::Data; - buf_off = 0; + buf.reset(); writer.init(file_len); continue; } - buf_off++; + buf.add_read(1); // too long - if(buf_off == MAX_FILELEN) + if(buf.get_offset() == MAX_FILELEN) return Status::Error; break; @@ -88,20 +117,21 @@ class CDCProgCommand final : public CDCCommand { case ParseState::Data: { // read data - auto max = std::min(uint32_t(FLASH_PAGE_SIZE), writer.get_remaining()) - buf_off; - auto read = usb_cdc_read(buf + buf_off, max); + auto max = std::min(uint32_t(FLASH_PAGE_SIZE), writer.get_remaining()) - buf.get_offset(); + auto read = usb_cdc_read(buf.get_current_ptr(), max); if(!read) return Status::Continue; - buf_off += read; + buf.add_read(read); // got full page or final part of file + auto buf_off = buf.get_offset(); if(buf_off == FLASH_PAGE_SIZE || buf_off == writer.get_remaining()) { - if(!writer.write(buf, buf_off)) + if(!writer.write(buf.get_data(), buf_off)) return Status::Error; - buf_off = 0; + buf.reset(); } // end of file @@ -132,7 +162,7 @@ class CDCProgCommand final : public CDCCommand { Data } parse_state = ParseState::Filename; - uint32_t buf_off = 0; + CDCParseBuffer &buf; BlitWriter writer; }; @@ -175,55 +205,57 @@ class CDCListCommand final : public CDCCommand { }; class CDCLaunchCommand final : public CDCCommand { +public: + CDCLaunchCommand(CDCParseBuffer &buf) : buf(buf) {} + void init() override { - buf_off = 0; + buf.reset(); } Status update() override { - auto buf = cdc_parse_buffer; - while(true) { - if(!usb_cdc_read(buf + buf_off, 1)) + auto buf_ptr = buf.get_current_ptr(); + if(!usb_cdc_read(buf_ptr, 1)) return Status::Continue; // end of string - if(buf[buf_off] == 0) { - buf_off = 0; - blit::api.launch((const char *)buf); + if(*buf_ptr == 0) { + blit::api.launch((const char *)buf.get_data()); return Status::Done; } - buf_off++; + buf.add_read(1); // too long - if(buf_off == MAX_FILENAME) + if(buf.get_offset() == MAX_FILENAME) return Status::Error; } return Status::Continue; } - uint32_t buf_off = 0; + CDCParseBuffer &buf; }; class CDCEraseCommand final : public CDCCommand { +public: + CDCEraseCommand(CDCParseBuffer &buf) : buf(buf) {} + void init() override { - buf_off = 0; + buf.reset(); } Status update() override { - auto buf = cdc_parse_buffer; - while(true) { - if(!usb_cdc_read(buf + buf_off, 1)) + auto buf_ptr = buf.get_current_ptr(); + if(!usb_cdc_read(buf_ptr, 1)) return Status::Continue; - buf_off++; + buf.add_read(1); // end of word - if(buf_off == 4) { - buf_off = 0; - blit::api.erase_game(*(uint32_t *)buf); + if(buf.get_offset() == 4) { + blit::api.erase_game(*(uint32_t *)buf.get_data()); return Status::Done; } } @@ -231,7 +263,7 @@ class CDCEraseCommand final : public CDCCommand { return Status::Continue; } - uint32_t buf_off = 0; + CDCParseBuffer &buf; }; @@ -240,10 +272,11 @@ static CDCUserCommand user_command; #if defined(BUILD_LOADER) && !defined(USB_HOST) #define FLASH_COMMANDS -static CDCProgCommand prog_command; +static CDCParseBuffer parse_buffer; +static CDCProgCommand prog_command(parse_buffer); static CDCListCommand list_command; -static CDCLaunchCommand launch_command; -static CDCEraseCommand erase_command; +static CDCLaunchCommand launch_command(parse_buffer); +static CDCEraseCommand erase_command(parse_buffer); #endif const std::tuple cdc_commands[]{ From a0f532f877c2b219a61d1a3adcbab815e79240ab Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Wed, 4 Sep 2024 16:00:06 +0100 Subject: [PATCH 43/70] pico: update loader linker script --- 32blit-pico/memmap_blit_loader.ld | 136 +++++++++++++++++++----------- 1 file changed, 85 insertions(+), 51 deletions(-) diff --git a/32blit-pico/memmap_blit_loader.ld b/32blit-pico/memmap_blit_loader.ld index d168e77e6..b1c4e4c35 100644 --- a/32blit-pico/memmap_blit_loader.ld +++ b/32blit-pico/memmap_blit_loader.ld @@ -63,6 +63,8 @@ SECTIONS KEEP (*(.vectors)) KEEP (*(.binary_info_header)) __binary_info_header_end = .; + KEEP (*(.embedded_block)) + __embedded_block_end = .; . = ALIGN(256); __api_const_start = .; @@ -89,6 +91,27 @@ SECTIONS *(SORT(.dtors.*)) *(.dtors) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + *(.eh_frame*) . = ALIGN(4); } > FLASH @@ -123,19 +146,21 @@ SECTIONS __binary_info_end = .; . = ALIGN(4); - /* End of .text-like segments */ - __etext = .; - .api_data : { - __api_data_start = .; - KEEP (*(*.api_data)) + __api_data_start = .; + KEEP (*(*.api_data)) } - .ram_vector_table (COPY): { - . = ALIGN(256); + .ram_vector_table (NOLOAD): { + . = ALIGN(256); *(.ram_vector_table) } > RAM + .uninitialized_data (NOLOAD): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + .data : { __data_start__ = .; *(vtable) @@ -160,35 +185,50 @@ SECTIONS PROVIDE_HIDDEN (__mutex_array_end = .); . = ALIGN(4); - /* preinit data */ - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP(*(SORT(.preinit_array.*))) - KEEP(*(.preinit_array)) - PROVIDE_HIDDEN (__preinit_array_end = .); + *(.jcr) + . = ALIGN(4); + } > RAM AT> FLASH + .tdata : { . = ALIGN(4); - /* init data */ - PROVIDE_HIDDEN (__init_array_start = .); - KEEP(*(SORT(.init_array.*))) - KEEP(*(.init_array)) - PROVIDE_HIDDEN (__init_array_end = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + /* All data end */ + __tdata_end = .; + } > RAM AT> FLASH + PROVIDE(__data_end__ = .); + + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + .tbss (NOLOAD) : { . = ALIGN(4); - /* finit data */ - PROVIDE_HIDDEN (__fini_array_start = .); - *(SORT(.fini_array.*)) - *(.fini_array) - PROVIDE_HIDDEN (__fini_array_end = .); + __bss_start__ = .; + __tls_base = .; + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) - *(.jcr) + __tls_end = .; + } > RAM + + .bss (NOLOAD) : { . = ALIGN(4); - /* All data end */ - __data_end__ = .; - } > RAM AT> FLASH + __tbss_end = .; - .uninitialized_data (COPY): { + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) . = ALIGN(4); - *(.uninitialized_data*) + __bss_end__ = .; + } > RAM + + .heap (NOLOAD): + { + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however + to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */ + . = ORIGIN(RAM) + LENGTH(RAM); + __HeapLimit = .; } > RAM /* Start and end symbols must be word-aligned */ @@ -208,23 +248,6 @@ SECTIONS } > SCRATCH_Y AT > FLASH __scratch_y_source__ = LOADADDR(.scratch_y); - .bss : { - . = ALIGN(4); - __bss_start__ = .; - *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) - *(COMMON) - . = ALIGN(4); - __bss_end__ = .; - } > RAM - - .heap (COPY): - { - __end__ = .; - end = __end__; - *(.heap*) - __HeapLimit = .; - } > RAM - /* .stack*_dummy section doesn't contains any symbols. It is only * used for linker to calculate size of stack sections, and assign * values to stack symbols later @@ -234,17 +257,18 @@ SECTIONS /* by default we put core 0 stack at the end of scratch Y, so that if core 1 * stack is not used then all of SCRATCH_X is free. */ - .stack1_dummy (COPY): + .stack1_dummy (NOLOAD): { *(.stack1*) } > SCRATCH_X - .stack_dummy (COPY): + .stack_dummy (NOLOAD): { - *(.stack*) + KEEP(*(.stack*)) } > SCRATCH_Y .flash_end : { - __flash_binary_end = .; + KEEP(*(.embedded_end_block*)) + PROVIDE(__flash_binary_end = .); } > FLASH /* stack limit is poorly named, but historically is maximum heap ptr */ @@ -255,6 +279,17 @@ SECTIONS __StackBottom = __StackTop - SIZEOF(.stack_dummy); PROVIDE(__stack = __StackTop); + /* picolibc and LLVM */ + PROVIDE (__heap_start = __end__); + PROVIDE (__heap_end = __HeapLimit); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); + PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1)); + PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); + + /* llvm-libc */ + PROVIDE (_end = __end__); + PROVIDE (__llvm_libc_heap_limit = __HeapLimit); + /* Check if data + heap + stack exceeds RAM limit */ ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") @@ -263,5 +298,4 @@ SECTIONS ASSERT(__api_const_start == ORIGIN(FLASH) + 512, "api_const must be 512 bytes from the start of flash") ASSERT(__api_data_start == ORIGIN(RAM), "api_data must be at the start of RAM") -} - +} \ No newline at end of file From 362e266fd1b1fa4dcd4bc9b4cf5719691017ae9a Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Wed, 4 Sep 2024 17:06:06 +0100 Subject: [PATCH 44/70] pico: fixup blit build and update linker script --- 32blit-pico/CMakeLists.txt | 6 +- 32blit-pico/memmap_blit.ld.in | 121 +++++++++++++++++++++------------- 32blit-pico/startup.S | 8 --- 3 files changed, 82 insertions(+), 53 deletions(-) diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index 2dd3e78fe..336674af5 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -224,7 +224,11 @@ function(blit_executable NAME) target_compile_definitions(${NAME} PRIVATE ${BLIT_BOARD_DEFINITIONS}) # need these for framebuffer config target_compile_options(${NAME} PRIVATE -ffunction-sections -fdata-sections) - target_link_libraries(${NAME} pico_bit_ops pico_cxx_options pico_divider pico_double pico_int64_ops pico_float pico_malloc pico_mem_ops) + + # minimal pico-sdk libs + target_link_libraries(${NAME} boot_picobin_headers pico_bit_ops pico_cxx_options pico_divider pico_double pico_int64_ops pico_float pico_malloc pico_mem_ops pico_runtime_headers) + target_compile_definitions(${NAME} PRIVATE PICO_TIME_DEFAULT_ALARM_POOL_DISABLED) # avoid pulling timer and irq code + target_link_options(${NAME} PRIVATE --specs=nosys.specs LINKER:--script=${LINKER_SCRIPT_OUT} LINKER:--gc-sections) set_property(TARGET ${NAME} APPEND PROPERTY LINK_DEPENDS ${LINKER_SCRIPT_OUT}) diff --git a/32blit-pico/memmap_blit.ld.in b/32blit-pico/memmap_blit.ld.in index b7ee087a9..6af2bd5bb 100644 --- a/32blit-pico/memmap_blit.ld.in +++ b/32blit-pico/memmap_blit.ld.in @@ -89,6 +89,27 @@ SECTIONS *(SORT(.dtors.*)) *(.dtors) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + *(.eh_frame*) . = ALIGN(4); } > FLASH @@ -123,13 +144,16 @@ SECTIONS __binary_info_end = .; . = ALIGN(4); - /* End of .text-like segments */ - __etext = .; - .ram_vector_table (COPY): { + .ram_vector_table (NOLOAD): { *(.ram_vector_table) } > RAM + .uninitialized_data (NOLOAD): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + .data : { __data_start__ = .; *(vtable) @@ -154,35 +178,50 @@ SECTIONS PROVIDE_HIDDEN (__mutex_array_end = .); . = ALIGN(4); - /* preinit data */ - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP(*(SORT(.preinit_array.*))) - KEEP(*(.preinit_array)) - PROVIDE_HIDDEN (__preinit_array_end = .); + *(.jcr) + . = ALIGN(4); + } > RAM AT> FLASH + .tdata : { . = ALIGN(4); - /* init data */ - PROVIDE_HIDDEN (__init_array_start = .); - KEEP(*(SORT(.init_array.*))) - KEEP(*(.init_array)) - PROVIDE_HIDDEN (__init_array_end = .); + *(.tdata .tdata.* .gnu.linkonce.td.*) + /* All data end */ + __tdata_end = .; + } > RAM AT> FLASH + PROVIDE(__data_end__ = .); + + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + .tbss (NOLOAD) : { . = ALIGN(4); - /* finit data */ - PROVIDE_HIDDEN (__fini_array_start = .); - *(SORT(.fini_array.*)) - *(.fini_array) - PROVIDE_HIDDEN (__fini_array_end = .); + __bss_start__ = .; + __tls_base = .; + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) - *(.jcr) + __tls_end = .; + } > RAM + + .bss (NOLOAD) : { . = ALIGN(4); - /* All data end */ - __data_end__ = .; - } > RAM AT> FLASH + __tbss_end = .; - .uninitialized_data (COPY): { + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) . = ALIGN(4); - *(.uninitialized_data*) + __bss_end__ = .; + } > RAM + + .heap (NOLOAD): + { + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however + to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */ + . = ORIGIN(RAM) + LENGTH(RAM); + __HeapLimit = .; } > RAM /* Start and end symbols must be word-aligned */ @@ -202,23 +241,6 @@ SECTIONS } > SCRATCH_Y AT > FLASH __scratch_y_source__ = LOADADDR(.scratch_y);*/ - .bss : { - . = ALIGN(4); - __bss_start__ = .; - *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) - *(COMMON) - . = ALIGN(4); - __bss_end__ = .; - } > RAM - - .heap (COPY): - { - __end__ = .; - end = __end__; - *(.heap*) - __HeapLimit = .; - } > RAM - /* .stack*_dummy section doesn't contains any symbols. It is only * used for linker to calculate size of stack sections, and assign * values to stack symbols later @@ -228,18 +250,18 @@ SECTIONS /* by default we put core 0 stack at the end of scratch Y, so that if core 1 * stack is not used then all of SCRATCH_X is free. */ - /*.stack1_dummy (COPY): + /*.stack1_dummy (NOLOAD): { *(.stack1*) } > SCRATCH_X - .stack_dummy (COPY): + .stack_dummy (NOLOAD): { *(.stack*) } > SCRATCH_Y*/ .flash_end : { LONG(0x12345678) /*workaround for __flash_binary_end pointing past the end*/ - __flash_binary_end = .; + PROVIDE(__flash_binary_end = .); } > FLASH /* stack limit is poorly named, but historically is maximum heap ptr */ @@ -250,6 +272,17 @@ SECTIONS __StackBottom = __StackTop - SIZEOF(.stack_dummy); PROVIDE(__stack = __StackTop);*/ + /* picolibc and LLVM */ + PROVIDE (__heap_start = __end__); + PROVIDE (__heap_end = __HeapLimit); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); + PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1)); + PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); + + /* llvm-libc */ + PROVIDE (_end = __end__); + PROVIDE (__llvm_libc_heap_limit = __HeapLimit); + /* Check if data + heap + stack exceeds RAM limit */ /*ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")*/ diff --git a/32blit-pico/startup.S b/32blit-pico/startup.S index 9cf03d656..8fe6d1a33 100644 --- a/32blit-pico/startup.S +++ b/32blit-pico/startup.S @@ -75,11 +75,3 @@ data_cpy_table: .word __data_end__ .word 0 // null terminator - -// panic stub -.type panic,%function -.thumb_func -.global panic -panic: - bkpt - bx lr From e86cdbe5c031d03715348f6b2e42881ca1eef720 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Wed, 4 Sep 2024 18:04:57 +0100 Subject: [PATCH 45/70] pico: link blits against pico_clib_interface Gets rid of a bunch of linker warnings --- 32blit-pico/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index 336674af5..3b86f700f 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -226,7 +226,7 @@ function(blit_executable NAME) target_compile_options(${NAME} PRIVATE -ffunction-sections -fdata-sections) # minimal pico-sdk libs - target_link_libraries(${NAME} boot_picobin_headers pico_bit_ops pico_cxx_options pico_divider pico_double pico_int64_ops pico_float pico_malloc pico_mem_ops pico_runtime_headers) + target_link_libraries(${NAME} boot_picobin_headers pico_bit_ops pico_clib_interface pico_cxx_options pico_divider pico_double pico_int64_ops pico_float pico_malloc pico_mem_ops pico_runtime_headers) target_compile_definitions(${NAME} PRIVATE PICO_TIME_DEFAULT_ALARM_POOL_DISABLED) # avoid pulling timer and irq code target_link_options(${NAME} PRIVATE --specs=nosys.specs LINKER:--script=${LINKER_SCRIPT_OUT} LINKER:--gc-sections) From e34f89e6369a19ff28b8d07eb46f3b86dc0c2d08 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Wed, 4 Sep 2024 20:57:18 +0100 Subject: [PATCH 46/70] pico: early out from dbi update if framebuffer not set yet In the transpose case this'll fault hard... Instead of just messing up some DMA --- 32blit-pico/display/dbi.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/32blit-pico/display/dbi.cpp b/32blit-pico/display/dbi.cpp index 4b6d3be6b..66e6dc41c 100644 --- a/32blit-pico/display/dbi.cpp +++ b/32blit-pico/display/dbi.cpp @@ -289,6 +289,9 @@ static void prepare_write() { static void update() { dma_channel_wait_for_finish_blocking(dma_channel); + if(!frame_buffer) + return; + if(!write_mode) prepare_write(); From e4bd771a290c441fe87da7b31bdef13601c420d8 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Wed, 4 Sep 2024 20:58:02 +0100 Subject: [PATCH 47/70] pico: add linker scripts for rp2530 loader/blits --- 32blit-pico/CMakeLists.txt | 6 +- 32blit-pico/loader/CMakeLists.txt | 6 +- 32blit-pico/memmap_blit_2350.ld.in | 289 ++++++++++++++++++++++ 32blit-pico/memmap_blit_loader_2350.ld | 317 +++++++++++++++++++++++++ 4 files changed, 616 insertions(+), 2 deletions(-) create mode 100644 32blit-pico/memmap_blit_2350.ld.in create mode 100644 32blit-pico/memmap_blit_loader_2350.ld diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index 3b86f700f..7a3a9f557 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -176,7 +176,11 @@ target_link_libraries(BlitHalPico INTERFACE ${BLIT_BOARD_LIBRARIES}) set(BLIT_BOARD_DEFINITIONS ${BLIT_BOARD_DEFINITIONS} PARENT_SCOPE) # some file paths we need later -set(LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_blit.ld.in PARENT_SCOPE) +if(PICO_PLATFORM MATCHES "^rp2350") + set(LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_blit_2350.ld.in PARENT_SCOPE) +else() + set(LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_blit.ld.in PARENT_SCOPE) +endif() set(STARTUP_SRC ${CMAKE_CURRENT_LIST_DIR}/startup.S ${CMAKE_CURRENT_LIST_DIR}/startup.cpp PARENT_SCOPE) set(LAUNCHERSHARED_DIR ${CMAKE_CURRENT_LIST_DIR}/../launcher-shared PARENT_SCOPE) diff --git a/32blit-pico/loader/CMakeLists.txt b/32blit-pico/loader/CMakeLists.txt index c5e563165..00197c234 100644 --- a/32blit-pico/loader/CMakeLists.txt +++ b/32blit-pico/loader/CMakeLists.txt @@ -3,7 +3,11 @@ target_link_libraries(blit-loader BlitHalPico BlitEngine) target_link_options(blit-loader PUBLIC -specs=nano.specs -u _printf_float) target_compile_definitions(blit-loader PRIVATE BUILD_LOADER PICO_EMBED_XIP_SETUP=1) -pico_set_linker_script(blit-loader ${CMAKE_CURRENT_LIST_DIR}/../memmap_blit_loader.ld) +if(PICO_PLATFORM MATCHES "^rp2350") + pico_set_linker_script(blit-loader ${CMAKE_CURRENT_LIST_DIR}/../memmap_blit_loader_2350.ld) +else() + pico_set_linker_script(blit-loader ${CMAKE_CURRENT_LIST_DIR}/../memmap_blit_loader.ld) +endif() pico_enable_stdio_uart(blit-loader 1) pico_enable_stdio_usb(blit-loader 0) diff --git a/32blit-pico/memmap_blit_2350.ld.in b/32blit-pico/memmap_blit_2350.ld.in new file mode 100644 index 000000000..a180b05c3 --- /dev/null +++ b/32blit-pico/memmap_blit_2350.ld.in @@ -0,0 +1,289 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + FLASH(rx) : ORIGIN = 0x10000000 + ${FLASH_OFFSET_BYTES}, LENGTH = 2048k + RAM(rwx) : ORIGIN = 0x20008000, LENGTH = 480k + /*SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k + SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k*/ +} + +ENTRY(_entry_point) + +SECTIONS +{ + .flash_begin : { + __flash_binary_start = .; + } > FLASH + + /* 32blit header */ + .header : { + KEEP (*(.header)) + . = ALIGN(256); + } > FLASH + + /* The bootrom will enter the image at the point indicated in your + IMAGE_DEF, which is usually the reset handler of your vector table. + + The debugger will use the ELF entry point, which is the _entry_point + symbol, and in our case is *different from the bootrom's entry point.* + This is used to go back through the bootrom on debugger launches only, + to perform the same initial flash setup that would be performed on a + cold boot. + */ + + .text : { + __logical_binary_start = .; + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + KEEP (*(.embedded_block)) + __embedded_block_end = .; + KEEP (*(.reset)) + /* TODO revisit this now memset/memcpy/float in ROM */ + /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from + * FLASH ... we will include any thing excluded here in .data below by default */ + *(.init) + *libgcc.a:cmse_nonsecure_call.o + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*) + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + + *(.eh_frame*) + . = ALIGN(4); + } > FLASH + + .rodata : { + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) + *(.srodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH + __binary_info_end = .; + . = ALIGN(4); + + .ram_vector_table (NOLOAD): { + *(.ram_vector_table) + } > RAM + + .uninitialized_data (NOLOAD): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + + .data : { + __data_start__ = .; + *(vtable) + + *(.time_critical*) + + /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(4); + + *(.data*) + *(.sdata*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + *(.jcr) + . = ALIGN(4); + } > RAM AT> FLASH + + .tdata : { + . = ALIGN(4); + *(.tdata .tdata.* .gnu.linkonce.td.*) + /* All data end */ + __tdata_end = .; + } > RAM AT> FLASH + PROVIDE(__data_end__ = .); + + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + + .tbss (NOLOAD) : { + . = ALIGN(4); + __bss_start__ = .; + __tls_base = .; + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + + __tls_end = .; + } > RAM + + .bss (NOLOAD) : { + . = ALIGN(4); + __tbss_end = .; + + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + PROVIDE(__global_pointer$ = . + 2K); + *(.sbss*) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (NOLOAD): + { + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however + to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */ + . = ORIGIN(RAM) + LENGTH(RAM); + __HeapLimit = .; + } > RAM + + /* Start and end symbols must be word-aligned */ + /*.scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X AT > FLASH + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y AT > FLASH + __scratch_y_source__ = LOADADDR(.scratch_y);*/ + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + /*.stack1_dummy (NOLOAD): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > SCRATCH_Y*/ + + .flash_end : { + LONG(0x12345678) /*workaround for __flash_binary_end pointing past the end*/ + PROVIDE(__flash_binary_end = .); + } > FLASH =0xaa + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + /*__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop);*/ + + /* picolibc and LLVM */ + PROVIDE (__heap_start = __end__); + PROVIDE (__heap_end = __HeapLimit); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); + PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1)); + PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); + + /* llvm-libc */ + PROVIDE (_end = __end__); + PROVIDE (__llvm_libc_heap_limit = __HeapLimit); + + /* Check if data + heap + stack exceeds RAM limit */ + /*ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")*/ + + ASSERT( __binary_info_header_end - __logical_binary_start <= 1024, "Binary info must be in first 1024 bytes of the binary") + ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary") + + /* todo assert on extra code */ + + __flash_binary_size = __flash_binary_end - __flash_binary_start; +} \ No newline at end of file diff --git a/32blit-pico/memmap_blit_loader_2350.ld b/32blit-pico/memmap_blit_loader_2350.ld new file mode 100644 index 000000000..adddbbd71 --- /dev/null +++ b/32blit-pico/memmap_blit_loader_2350.ld @@ -0,0 +1,317 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 256k + RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 32k + SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k + SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k +} + +ENTRY(_entry_point) + +SECTIONS +{ + .flash_begin : { + __flash_binary_start = .; + } > FLASH + + /* The bootrom will enter the image at the point indicated in your + IMAGE_DEF, which is usually the reset handler of your vector table. + + The debugger will use the ELF entry point, which is the _entry_point + symbol, and in our case is *different from the bootrom's entry point.* + This is used to go back through the bootrom on debugger launches only, + to perform the same initial flash setup that would be performed on a + cold boot. + */ + + .text : { + __logical_binary_start = .; + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + KEEP (*(.embedded_block)) + __embedded_block_end = .; + + . = ALIGN(256); + __api_const_start = .; + KEEP (*(*.api_const)) + __api_const_end = .; + + KEEP (*(.reset)) + /* TODO revisit this now memset/memcpy/float in ROM */ + /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from + * FLASH ... we will include any thing excluded here in .data below by default */ + *(.init) + *libgcc.a:cmse_nonsecure_call.o + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*) + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + + *(.eh_frame*) + . = ALIGN(4); + } > FLASH + + /* Note the boot2 section is optional, and should be discarded if there is + no reference to it *inside* the binary, as it is not called by the + bootrom. (The bootrom performs a simple best-effort XIP setup and + leaves it to the binary to do anything more sophisticated.) However + there is still a size limit of 256 bytes, to ensure the boot2 can be + stored in boot RAM. + + Really this is a "XIP setup function" -- the name boot2 is historic and + refers to its dual-purpose on RP2040, where it also handled vectoring + from the bootrom into the user image. + */ + + .boot2 : { + __boot2_start__ = .; + *(.boot2) + __boot2_end__ = .; + } > FLASH + + ASSERT(__boot2_end__ - __boot2_start__ <= 256, + "ERROR: Pico second stage bootloader must be no more than 256 bytes in size") + + .rodata : { + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) + *(.srodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH + __binary_info_end = .; + . = ALIGN(4); + + .api_data : { + __api_data_start = .; + KEEP (*(*.api_data)) + } + + .ram_vector_table (NOLOAD): { + . = ALIGN(256); + *(.ram_vector_table) + } > RAM + + .uninitialized_data (NOLOAD): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + + .data : { + __data_start__ = .; + *(vtable) + + *(.time_critical*) + + /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(4); + + *(.data*) + *(.sdata*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + *(.jcr) + . = ALIGN(4); + } > RAM AT> FLASH + + .tdata : { + . = ALIGN(4); + *(.tdata .tdata.* .gnu.linkonce.td.*) + /* All data end */ + __tdata_end = .; + } > RAM AT> FLASH + PROVIDE(__data_end__ = .); + + /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ + __etext = LOADADDR(.data); + + .tbss (NOLOAD) : { + . = ALIGN(4); + __bss_start__ = .; + __tls_base = .; + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + + __tls_end = .; + } > RAM + + .bss (NOLOAD) : { + . = ALIGN(4); + __tbss_end = .; + + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + PROVIDE(__global_pointer$ = . + 2K); + *(.sbss*) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (NOLOAD): + { + __end__ = .; + end = __end__; + KEEP(*(.heap*)) + /* historically on GCC sbrk was growing past __HeapLimit to __StackLimit, however + to be more compatible, we now set __HeapLimit explicitly to where the end of the heap is */ + . = ORIGIN(RAM) + LENGTH(RAM); + __HeapLimit = .; + } > RAM + + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X AT > FLASH + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y AT > FLASH + __scratch_y_source__ = LOADADDR(.scratch_y); + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + .stack1_dummy (NOLOAD): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (NOLOAD): + { + KEEP(*(.stack*)) + } > SCRATCH_Y + + .flash_end : { + KEEP(*(.embedded_end_block*)) + PROVIDE(__flash_binary_end = .); + } > FLASH =0xaa + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* picolibc and LLVM */ + PROVIDE (__heap_start = __end__); + PROVIDE (__heap_end = __HeapLimit); + PROVIDE( __tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)) ); + PROVIDE( __tls_size_align = (__tls_size + __tls_align - 1) & ~(__tls_align - 1)); + PROVIDE( __arm32_tls_tcb_offset = MAX(8, __tls_align) ); + + /* llvm-libc */ + PROVIDE (_end = __end__); + PROVIDE (__llvm_libc_heap_limit = __HeapLimit); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + ASSERT( __binary_info_header_end - __logical_binary_start <= 1024, "Binary info must be in first 1024 bytes of the binary") + ASSERT( __embedded_block_end - __logical_binary_start <= 4096, "Embedded block must be in first 4096 bytes of the binary") + + /* todo assert on extra code */ + + ASSERT(__api_const_start == ORIGIN(FLASH) + 512, "api_const must be 512 bytes from the start of flash") + ASSERT(__api_data_start == ORIGIN(RAM), "api_data must be at the start of RAM") +} \ No newline at end of file From e064e4c8526b82828154c7df3f6e423469f6328c Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 5 Sep 2024 14:57:29 +0100 Subject: [PATCH 48/70] pico: add define for device id We've got two incompatible devices here now --- 32blit-pico/CMakeLists.txt | 1 + 32blit-pico/startup.S | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index 7a3a9f557..d61e087b8 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -228,6 +228,7 @@ function(blit_executable NAME) target_compile_definitions(${NAME} PRIVATE ${BLIT_BOARD_DEFINITIONS}) # need these for framebuffer config target_compile_options(${NAME} PRIVATE -ffunction-sections -fdata-sections) + set_source_files_properties(${STARTUP_SRC} PROPERTIES COMPILE_DEFINITIONS DEVICE_ID=2) # minimal pico-sdk libs target_link_libraries(${NAME} boot_picobin_headers pico_bit_ops pico_clib_interface pico_cxx_options pico_divider pico_double pico_int64_ops pico_float pico_malloc pico_mem_ops pico_runtime_headers) diff --git a/32blit-pico/startup.S b/32blit-pico/startup.S index 8fe6d1a33..26a637ddf 100644 --- a/32blit-pico/startup.S +++ b/32blit-pico/startup.S @@ -10,7 +10,7 @@ .word _ZN4blit4tickEm // tick .word _entry_point // init .word __flash_binary_size -.word 2 // device_id = 2 + padding +.word DEVICE_ID // device_id + padding .hword BLIT_API_VERSION_MAJOR .hword BLIT_API_VERSION_MINOR .word __flash_binary_start // unused From 663945fab37cf1389af4d3ffd1ff7ce694f8c8d9 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 5 Sep 2024 15:40:10 +0100 Subject: [PATCH 49/70] pico: add 2350 device id --- 32blit-pico/CMakeLists.txt | 15 +++++++++++++-- 32blit-pico/blit-launch.cpp | 14 ++++++++++---- launcher-shared/executable.hpp | 1 + 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index d61e087b8..89072c956 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -8,6 +8,11 @@ include (pico_sdk_import.cmake) set(32BLIT_PICO 1 PARENT_SCOPE) +if(PICO_PLATFORM MATCHES "^rp2350") + set(32BLIT_PICO_2350 1) + set(32BLIT_PICO_2350 ${32BLIT_PICO_2350} PARENT_SCOPE) +endif() + # prevent find_package errors in pico_add_uf2_output later set(PICO_SDK_VERSION_MAJOR ${PICO_SDK_VERSION_MAJOR} PARENT_SCOPE) set(PICO_SDK_VERSION_MINOR ${PICO_SDK_VERSION_MINOR} PARENT_SCOPE) @@ -176,7 +181,7 @@ target_link_libraries(BlitHalPico INTERFACE ${BLIT_BOARD_LIBRARIES}) set(BLIT_BOARD_DEFINITIONS ${BLIT_BOARD_DEFINITIONS} PARENT_SCOPE) # some file paths we need later -if(PICO_PLATFORM MATCHES "^rp2350") +if(32BLIT_PICO_2350) set(LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_blit_2350.ld.in PARENT_SCOPE) else() set(LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_blit.ld.in PARENT_SCOPE) @@ -228,7 +233,13 @@ function(blit_executable NAME) target_compile_definitions(${NAME} PRIVATE ${BLIT_BOARD_DEFINITIONS}) # need these for framebuffer config target_compile_options(${NAME} PRIVATE -ffunction-sections -fdata-sections) - set_source_files_properties(${STARTUP_SRC} PROPERTIES COMPILE_DEFINITIONS DEVICE_ID=2) + + # set device id in header, these should match the BlitDevice enum + if(32BLIT_PICO_2350) + set_source_files_properties(${STARTUP_SRC} PROPERTIES COMPILE_DEFINITIONS DEVICE_ID=3) + else() + set_source_files_properties(${STARTUP_SRC} PROPERTIES COMPILE_DEFINITIONS DEVICE_ID=2) + endif() # minimal pico-sdk libs target_link_libraries(${NAME} boot_picobin_headers pico_bit_ops pico_clib_interface pico_cxx_options pico_divider pico_double pico_int64_ops pico_float pico_malloc pico_mem_ops pico_runtime_headers) diff --git a/32blit-pico/blit-launch.cpp b/32blit-pico/blit-launch.cpp index e2a753779..27f4a15e9 100644 --- a/32blit-pico/blit-launch.cpp +++ b/32blit-pico/blit-launch.cpp @@ -11,6 +11,12 @@ #include "engine/engine.hpp" #include "engine/file.hpp" +#ifdef PICO_RP2350 +#define DEVICE_ID BlitDevice::RP2350 +#else +#define DEVICE_ID BlitDevice::RP2040 +#endif + // code related to blit files and launching extern int (*do_tick)(uint32_t time); @@ -87,7 +93,7 @@ bool launch_file(const char *path) { auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + flash_offset); // check header magic + device - if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) + if(header->magic != blit_game_magic || header->device_id != DEVICE_ID) return false; if(!header->init || !header->render || !header->tick) @@ -120,7 +126,7 @@ blit::CanLaunchResult can_launch(const char *path) { auto bytes_read = read_file(file, 0, sizeof(header), (char *)&header); - if(bytes_read == sizeof(header) && header.magic == blit_game_magic && header.device_id == BlitDevice::RP2040) { + if(bytes_read == sizeof(header) && header.magic == blit_game_magic && header.device_id == DEVICE_ID) { close_file(file); return blit::CanLaunchResult::Success; } @@ -159,7 +165,7 @@ static uint32_t get_installed_file_size(uint32_t offset) { auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + offset); // check header magic + device - if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) + if(header->magic != blit_game_magic || header->device_id != DEVICE_ID) return 0; auto size = header->end; @@ -270,7 +276,7 @@ uint32_t BlitWriter::get_flash_offset() const { bool BlitWriter::prepare_write(const uint8_t *buf) { auto header = (BlitGameHeader *)buf; - if(header->magic != blit_game_magic || header->device_id != BlitDevice::RP2040) { + if(header->magic != blit_game_magic || header->device_id != DEVICE_ID) { blit::debugf("Invalid blit header!"); return false; } diff --git a/launcher-shared/executable.hpp b/launcher-shared/executable.hpp index f34cd54d7..428b57549 100644 --- a/launcher-shared/executable.hpp +++ b/launcher-shared/executable.hpp @@ -17,6 +17,7 @@ enum class BlitDevice : uint8_t { STM32H7_32BlitOld = 0, // 32blit hw, old header STM32H7_32Blit = 1, // 32blit hw RP2040 = 2, // any RP2040-based device + RP2350 = 3, }; // should match the layout in startup_user.s From 743992da99381d06498cda2f9bef4f99cf309cb3 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 5 Sep 2024 15:53:57 +0100 Subject: [PATCH 50/70] pico: use untranslated flash addr for scanning and metadata Will be useful when we're using address translation --- 32blit-pico/blit-launch.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/32blit-pico/blit-launch.cpp b/32blit-pico/blit-launch.cpp index 27f4a15e9..e916a68ea 100644 --- a/32blit-pico/blit-launch.cpp +++ b/32blit-pico/blit-launch.cpp @@ -13,8 +13,10 @@ #ifdef PICO_RP2350 #define DEVICE_ID BlitDevice::RP2350 +#define FLASH_BASE XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE #else #define DEVICE_ID BlitDevice::RP2040 +#define FLASH_BASE XIP_NOCACHE_NOALLOC_BASE #endif // code related to blit files and launching @@ -33,7 +35,7 @@ RawMetadata *get_running_game_metadata() { if(!current_game_offset) return nullptr; - auto game_ptr = reinterpret_cast(XIP_NOCACHE_NOALLOC_BASE + current_game_offset); + auto game_ptr = reinterpret_cast(FLASH_BASE + current_game_offset); auto header = reinterpret_cast(game_ptr); @@ -91,7 +93,7 @@ bool launch_file(const char *path) { flash_offset = writer.get_flash_offset(); } - auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + flash_offset); + auto header = (BlitGameHeader *)(FLASH_BASE + flash_offset); // check header magic + device if(header->magic != blit_game_magic || header->device_id != DEVICE_ID) return false; @@ -143,7 +145,7 @@ void delayed_launch() { if(!requested_launch_offset) return; - auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + requested_launch_offset); + auto header = (BlitGameHeader *)(FLASH_BASE + requested_launch_offset); // save in case launch fails uint32_t last_game_offset = current_game_offset; @@ -162,7 +164,7 @@ void delayed_launch() { } static uint32_t get_installed_file_size(uint32_t offset) { - auto header = (BlitGameHeader *)(XIP_NOCACHE_NOALLOC_BASE + offset); + auto header = (BlitGameHeader *)(FLASH_BASE + offset); // check header magic + device if(header->magic != blit_game_magic || header->device_id != DEVICE_ID) @@ -172,9 +174,9 @@ static uint32_t get_installed_file_size(uint32_t offset) { // check metadata auto meta_offset = offset + size; - if(memcmp((char *)(XIP_NOCACHE_NOALLOC_BASE + meta_offset), "BLITMETA", 8) == 0) { + if(memcmp((char *)(FLASH_BASE + meta_offset), "BLITMETA", 8) == 0) { // add metadata size - size += *(uint16_t *)(XIP_NOCACHE_NOALLOC_BASE + meta_offset + 8) + 10; + size += *(uint16_t *)(FLASH_BASE + meta_offset + 8) + 10; } return size; @@ -193,7 +195,7 @@ void list_installed_games(std::function Date: Thu, 5 Sep 2024 16:07:05 +0100 Subject: [PATCH 51/70] Move default value for blit offset to 32blit-pico --- 32blit-config.cmake | 2 -- 32blit-pico/CMakeLists.txt | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/32blit-config.cmake b/32blit-config.cmake index 052515ca0..92fa55fe6 100644 --- a/32blit-config.cmake +++ b/32blit-config.cmake @@ -101,8 +101,6 @@ if (NOT DEFINED BLIT_ONCE) if(DEFINED BLIT_EXECUTABLE_PICO_BLIT_OFFSET_KB) set(PICO_BLIT_OFFSET_KB ${BLIT_EXECUTABLE_PICO_BLIT_OFFSET_KB}) - else() - set(PICO_BLIT_OFFSET_KB 512) endif() foreach(arg IN LISTS ARGN) diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index 89072c956..f4fae652c 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -220,6 +220,10 @@ function(blit_executable NAME) set_property(TARGET ${NAME} PROPERTY BLIT_PICO_BLIT TRUE) # calculate and verify flash offset + if(NOT PICO_BLIT_OFFSET_KB) + set(PICO_BLIT_OFFSET_KB 512) + endif() + math(EXPR offset_mod "${PICO_BLIT_OFFSET_KB} % 64") if(${PICO_BLIT_OFFSET_KB} LESS 256 OR offset_mod) message(FATAL_ERROR "Blit offset should be >= 256k and a multiple of 64k") From e511917f23d2fa7b787e8fc9f8ec20afca9e077d Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 5 Sep 2024 16:14:35 +0100 Subject: [PATCH 52/70] pico: fix indentation in BlitWriter::prepare_write --- 32blit-pico/blit-launch.cpp | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/32blit-pico/blit-launch.cpp b/32blit-pico/blit-launch.cpp index e916a68ea..2fa30f1c0 100644 --- a/32blit-pico/blit-launch.cpp +++ b/32blit-pico/blit-launch.cpp @@ -276,34 +276,34 @@ uint32_t BlitWriter::get_flash_offset() const { } bool BlitWriter::prepare_write(const uint8_t *buf) { - auto header = (BlitGameHeader *)buf; + auto header = (BlitGameHeader *)buf; - if(header->magic != blit_game_magic || header->device_id != DEVICE_ID) { - blit::debugf("Invalid blit header!"); - return false; - } + if(header->magic != blit_game_magic || header->device_id != DEVICE_ID) { + blit::debugf("Invalid blit header!"); + return false; + } - // currently non-relocatable, so base address is stored after header - flash_offset = *(uint32_t *)(buf + sizeof(BlitGameHeader)); - flash_offset &= 0xFFFFFF; + // currently non-relocatable, so base address is stored after header + flash_offset = *(uint32_t *)(buf + sizeof(BlitGameHeader)); + flash_offset &= 0xFFFFFF; - printf("PROG: flash off %lu\n", flash_offset); + printf("PROG: flash off %lu\n", flash_offset); - disable_user_code(); + disable_user_code(); - // erase flash - auto status = save_and_disable_interrupts(); + // erase flash + auto status = save_and_disable_interrupts(); - if(core1_started) - multicore_lockout_start_blocking(); // pause core1 + if(core1_started) + multicore_lockout_start_blocking(); // pause core1 - auto erase_size = ((file_len - 1) / FLASH_SECTOR_SIZE) + 1; - flash_range_erase(flash_offset, erase_size * FLASH_SECTOR_SIZE); + auto erase_size = ((file_len - 1) / FLASH_SECTOR_SIZE) + 1; + flash_range_erase(flash_offset, erase_size * FLASH_SECTOR_SIZE); - if(core1_started) - multicore_lockout_end_blocking(); // resume core1 + if(core1_started) + multicore_lockout_end_blocking(); // resume core1 - restore_interrupts(status); + restore_interrupts(status); - return true; + return true; } \ No newline at end of file From 4b78d88fa671dc1a8e25bf033cc5dd8939afb91f Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 5 Sep 2024 16:30:42 +0100 Subject: [PATCH 53/70] pico: move helpers above api impl in blit-launch --- 32blit-pico/blit-launch.cpp | 49 ++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/32blit-pico/blit-launch.cpp b/32blit-pico/blit-launch.cpp index 2fa30f1c0..0296a4040 100644 --- a/32blit-pico/blit-launch.cpp +++ b/32blit-pico/blit-launch.cpp @@ -30,6 +30,31 @@ extern bool core1_started; static uint32_t requested_launch_offset = 0; static uint32_t current_game_offset = 0; +static uint32_t get_installed_file_size(uint32_t offset) { + auto header = (BlitGameHeader *)(FLASH_BASE + offset); + + // check header magic + device + if(header->magic != blit_game_magic || header->device_id != DEVICE_ID) + return 0; + + auto size = header->end; + + // check metadata + auto meta_offset = offset + size; + if(memcmp((char *)(FLASH_BASE + meta_offset), "BLITMETA", 8) == 0) { + // add metadata size + size += *(uint16_t *)(FLASH_BASE + meta_offset + 8) + 10; + } + + return size; +} + +static uint32_t calc_num_blocks(uint32_t size) { + return (size - 1) / game_block_size + 1; +} + +// 32blit API + RawMetadata *get_running_game_metadata() { #ifdef BUILD_LOADER if(!current_game_offset) @@ -163,29 +188,6 @@ void delayed_launch() { do_tick = header->tick; } -static uint32_t get_installed_file_size(uint32_t offset) { - auto header = (BlitGameHeader *)(FLASH_BASE + offset); - - // check header magic + device - if(header->magic != blit_game_magic || header->device_id != DEVICE_ID) - return 0; - - auto size = header->end; - - // check metadata - auto meta_offset = offset + size; - if(memcmp((char *)(FLASH_BASE + meta_offset), "BLITMETA", 8) == 0) { - // add metadata size - size += *(uint16_t *)(FLASH_BASE + meta_offset + 8) + 10; - } - - return size; -} - -static uint32_t calc_num_blocks(uint32_t size) { - return (size - 1) / game_block_size + 1; -} - void list_installed_games(std::function callback) { for(uint32_t off = 0; off < PICO_FLASH_SIZE_BYTES;) { auto size = get_installed_file_size(off); @@ -234,6 +236,7 @@ void erase_game(uint32_t offset) { #endif } +// .blit file writer void BlitWriter::init(uint32_t file_len) { this->file_len = file_len; file_offset = flash_offset = 0; From 281fa190d9b472d78ca7f50e939f186de31b7c96 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 5 Sep 2024 17:11:41 +0100 Subject: [PATCH 54/70] pico: support .blits flashed at any offset using address translation This requires compiling them for a 4MB offset --- 32blit-pico/blit-launch.cpp | 61 +++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/32blit-pico/blit-launch.cpp b/32blit-pico/blit-launch.cpp index 0296a4040..a9c3446e3 100644 --- a/32blit-pico/blit-launch.cpp +++ b/32blit-pico/blit-launch.cpp @@ -5,6 +5,10 @@ #include "hardware/sync.h" #include "pico/multicore.h" +#ifdef PICO_RP2350 +#include "hardware/structs/qmi.h" +#endif + #include "blit-launch.hpp" #include "file.hpp" @@ -53,6 +57,44 @@ static uint32_t calc_num_blocks(uint32_t size) { return (size - 1) / game_block_size + 1; } +static uint32_t find_flash_offset(uint32_t requested_size) { + uint32_t free_off = 0; // 0 is invalid as that's where the loader is + + // FIXME: avoid flash storage + + for(uint32_t off = 256 * 1024; off < PICO_FLASH_SIZE_BYTES;) { + auto size = get_installed_file_size(off); + + if(!size) { + // empty, store offset + if(!free_off) + free_off = off; + + off += game_block_size; + continue; + } + + if(free_off) { + // end of free space, check if large enough + auto found_space = off - free_off; + + if(found_space >= requested_size) + return free_off; + + free_off = 0; + } + + // skip to end + off += calc_num_blocks(size) * game_block_size; + } + + // last chance + if(free_off && PICO_FLASH_SIZE_BYTES - free_off >= requested_size) + return free_off; + + return 0; +} + // 32blit API RawMetadata *get_running_game_metadata() { @@ -172,6 +214,18 @@ void delayed_launch() { auto header = (BlitGameHeader *)(FLASH_BASE + requested_launch_offset); +#ifdef PICO_RP2350 + uint32_t header_offset = *(uint32_t *)(FLASH_BASE + requested_launch_offset + sizeof(BlitGameHeader)); + if(header_offset != requested_launch_offset) { + // setup translation + qmi_hw->atrans[1] = 0x400 << QMI_ATRANS1_SIZE_LSB // TODO: use (rounded) blit size + | (requested_launch_offset >> 12) << QMI_ATRANS1_BASE_LSB; + + // FIXME: invalidate cache + // FIXME: handle previous blit also using translation on failure + } +#endif + // save in case launch fails uint32_t last_game_offset = current_game_offset; @@ -290,6 +344,13 @@ bool BlitWriter::prepare_write(const uint8_t *buf) { flash_offset = *(uint32_t *)(buf + sizeof(BlitGameHeader)); flash_offset &= 0xFFFFFF; +#ifdef PICO_RP2350 + if(flash_offset == 4 * 1024 * 1024) { + // we can use address translation for this, so flash in any free space + flash_offset = find_flash_offset(file_len); + } +#endif + printf("PROG: flash off %lu\n", flash_offset); disable_user_code(); From 565348a25fe1ed1b0c3f9c2b226f4c8dfbde29dd Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 5 Sep 2024 17:12:11 +0100 Subject: [PATCH 55/70] pico: set default flash offset for RP2350 to 4MB --- 32blit-pico/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index f4fae652c..0fb531f93 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -221,7 +221,12 @@ function(blit_executable NAME) # calculate and verify flash offset if(NOT PICO_BLIT_OFFSET_KB) - set(PICO_BLIT_OFFSET_KB 512) + if(32BLIT_PICO_2350) + # if we build the file for a 4MB offset, we can use address translation + set(PICO_BLIT_OFFSET_KB 4096) + else() + set(PICO_BLIT_OFFSET_KB 512) + endif() endif() math(EXPR offset_mod "${PICO_BLIT_OFFSET_KB} % 64") From e07d2c5fa6ee7157b379210f77394efafe8e3f9e Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 6 Sep 2024 15:39:17 +0100 Subject: [PATCH 56/70] pico: invalidate cache after setting up translation I think this is it, doesn't seem to be any helpers in the SDK... --- 32blit-pico/blit-launch.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/32blit-pico/blit-launch.cpp b/32blit-pico/blit-launch.cpp index a9c3446e3..68fa790eb 100644 --- a/32blit-pico/blit-launch.cpp +++ b/32blit-pico/blit-launch.cpp @@ -218,10 +218,14 @@ void delayed_launch() { uint32_t header_offset = *(uint32_t *)(FLASH_BASE + requested_launch_offset + sizeof(BlitGameHeader)); if(header_offset != requested_launch_offset) { // setup translation - qmi_hw->atrans[1] = 0x400 << QMI_ATRANS1_SIZE_LSB // TODO: use (rounded) blit size + uint32_t size = 4 * 1024 * 1024; // TODO: use (rounded) blit size + qmi_hw->atrans[1] = (size >> 12) << QMI_ATRANS1_SIZE_LSB | (requested_launch_offset >> 12) << QMI_ATRANS1_BASE_LSB; - // FIXME: invalidate cache + // invalidate cache + for(uint32_t off = 0; off < size; off += 8) + *(uint8_t *)(XIP_MAINTENANCE_BASE + 4 * 1024 * 1024 + off + 2/*invalidate by addr*/) = 0; + // FIXME: handle previous blit also using translation on failure } #endif From 3e8d780fb49d59941763207c6d22d1ad39aee50c Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 6 Sep 2024 15:42:34 +0100 Subject: [PATCH 57/70] pico: move fb size calcs to config.h and dedup --- 32blit-pico/config.h | 9 +++++++++ 32blit-pico/display.cpp | 16 ++-------------- 32blit-pico/startup.cpp | 12 +----------- 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/32blit-pico/config.h b/32blit-pico/config.h index a5f948d27..d7df19cbd 100644 --- a/32blit-pico/config.h +++ b/32blit-pico/config.h @@ -164,6 +164,15 @@ #define DISPLAY_HEIGHT 240 #endif +#if ALLOW_HIRES && DOUBLE_BUFFERED_HIRES +#define FRAMEBUFFER_SIZE (DISPLAY_WIDTH * DISPLAY_HEIGHT * 2) +#elif ALLOW_HIRES +#define FRAMEBUFFER_SIZE (DISPLAY_WIDTH * DISPLAY_HEIGHT) +#else +// height rounded up to handle the 135px display +#define FRAMEBUFFER_SIZE ((DISPLAY_WIDTH / 2) * ((DISPLAY_HEIGHT + 1) / 2) * 2) // double-buffered +#endif + #ifndef LCD_CS_PIN #define LCD_CS_PIN PICO_DEFAULT_SPI_CSN_PIN #endif diff --git a/32blit-pico/display.cpp b/32blit-pico/display.cpp index 8e74a62ef..584c6c694 100644 --- a/32blit-pico/display.cpp +++ b/32blit-pico/display.cpp @@ -4,18 +4,6 @@ using namespace blit; -// height rounded up to handle the 135px display -// this is in bytes -static const int lores_page_size = (DISPLAY_WIDTH / 2) * ((DISPLAY_HEIGHT + 1) / 2) * 2; - -#if ALLOW_HIRES && DOUBLE_BUFFERED_HIRES -static const int fb_size = DISPLAY_WIDTH * DISPLAY_HEIGHT * 2; -#elif ALLOW_HIRES -static const int fb_size = DISPLAY_WIDTH * DISPLAY_HEIGHT; -#else -static const int fb_size = lores_page_size; // double-buffered -#endif - SurfaceInfo cur_surf_info; bool fb_double_buffer = true; @@ -25,8 +13,8 @@ uint16_t *screen_fb = nullptr; static uint32_t max_fb_size = 0; static Size max_fb_bounds(DISPLAY_WIDTH, DISPLAY_HEIGHT); #else -uint16_t screen_fb[fb_size]; -static const uint32_t max_fb_size = fb_size; +uint16_t screen_fb[FRAMEBUFFER_SIZE]; +static const uint32_t max_fb_size = FRAMEBUFFER_SIZE; #endif static const Size lores_screen_size(DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2); diff --git a/32blit-pico/startup.cpp b/32blit-pico/startup.cpp index 0c6854127..a79cdb8de 100644 --- a/32blit-pico/startup.cpp +++ b/32blit-pico/startup.cpp @@ -4,17 +4,7 @@ #include "config.h" #ifndef BLIT_BOARD_PIMORONI_PICOVISION - -#if ALLOW_HIRES && DOUBLE_BUFFERED_HIRES -static const int fb_size = DISPLAY_WIDTH * DISPLAY_HEIGHT * 2; -#elif ALLOW_HIRES -static const int fb_size = DISPLAY_WIDTH * DISPLAY_HEIGHT; -#else -// height rounded up to handle the 135px display -static const int fb_size = (DISPLAY_WIDTH / 2) * ((DISPLAY_HEIGHT + 1) / 2) * 2; // double-buffered -#endif - -static uint16_t screen_fb[fb_size]; +static uint16_t screen_fb[FRAMEBUFFER_SIZE]; #endif extern void init(); From 09461176ef14d357deea9ed600b0ed122b4c0a62 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 6 Sep 2024 16:21:56 +0100 Subject: [PATCH 58/70] pico: implement the HOME button exit/reset --- 32blit-pico/main.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index 2b2e98a03..566509132 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -30,6 +30,8 @@ static blit::AudioChannel channels[CHANNEL_COUNT]; int (*do_tick)(uint32_t time) = blit::tick; +static alarm_id_t home_hold_alarm_id = 0; + // override terminate handler to save ~20-30k namespace __cxxabiv1 { std::terminate_handler __terminate_handler = std::abort; @@ -214,6 +216,30 @@ void disable_user_code() { blit::render = ::render; } +[[maybe_unused]] +static int64_t home_hold_callback(alarm_id_t id, void *user_data) { + home_hold_alarm_id = 0; + + ::init(); // re-initialising the loader is effectively a reset + + return 0; +} + +static void check_home_button() { +#ifdef BUILD_LOADER + if((api_data.buttons & Button::HOME) && !home_hold_alarm_id) { + // start timer for exit/reset + home_hold_alarm_id = add_alarm_in_ms(1000, home_hold_callback, nullptr, false); + debugf("home down at %i alarm %i\n", ::now(), home_hold_alarm_id); + } else if(!(api_data.buttons & Button::HOME) && home_hold_alarm_id) { + // released, cancel timer + debugf("home up at %i alarm %i\n", ::now(), home_hold_alarm_id); + cancel_alarm(home_hold_alarm_id); + home_hold_alarm_id = 0; + } +#endif +} + bool core1_started = false; #ifdef ENABLE_CORE1 @@ -290,8 +316,12 @@ int main() { while(true) { auto now = ::now(); update_display(now); + update_input(); + check_home_button(); + int ms_to_next_update = do_tick(::now()); + update_led(); update_usb(); update_multiplayer(); From fb0eda9b69d116cb31b97b633f2addef81900d99 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 22 Oct 2024 18:48:56 +0100 Subject: [PATCH 59/70] pico: fix non-loader fb size This has been broken since set_framebuffer... --- 32blit-pico/display.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/32blit-pico/display.cpp b/32blit-pico/display.cpp index 584c6c694..996d3f0af 100644 --- a/32blit-pico/display.cpp +++ b/32blit-pico/display.cpp @@ -14,7 +14,7 @@ static uint32_t max_fb_size = 0; static Size max_fb_bounds(DISPLAY_WIDTH, DISPLAY_HEIGHT); #else uint16_t screen_fb[FRAMEBUFFER_SIZE]; -static const uint32_t max_fb_size = FRAMEBUFFER_SIZE; +static const uint32_t max_fb_size = FRAMEBUFFER_SIZE * sizeof(uint16_t); #endif static const Size lores_screen_size(DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2); From b5aa475d451270156b80ddbf448566bce40bab4c Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Wed, 30 Oct 2024 23:50:14 +0000 Subject: [PATCH 60/70] pico: always use half fb size as page size This is the only way for switching between two double-buffered modes to work --- 32blit-pico/display.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/32blit-pico/display.cpp b/32blit-pico/display.cpp index 996d3f0af..3950e021f 100644 --- a/32blit-pico/display.cpp +++ b/32blit-pico/display.cpp @@ -23,7 +23,7 @@ static const Size hires_screen_size(DISPLAY_WIDTH, DISPLAY_HEIGHT); ScreenMode cur_screen_mode = ScreenMode::lores; int get_display_page_size() { - return cur_surf_info.bounds.area() * pixel_format_stride[int(cur_surf_info.format)]; + return max_fb_size / 2; } // blit api From f1d48a38a535486fa46befb375d06e0879caa17d Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 31 Oct 2024 15:39:28 +0000 Subject: [PATCH 61/70] pico: rename blit-launch to blit_launch for consistency --- 32blit-pico/CMakeLists.txt | 2 +- 32blit-pico/{blit-launch.cpp => blit_launch.cpp} | 8 ++++---- 32blit-pico/{blit-launch.hpp => blit_launch.hpp} | 0 32blit-pico/file.cpp | 2 +- 32blit-pico/main.cpp | 2 +- 32blit-pico/usb.cpp | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) rename 32blit-pico/{blit-launch.cpp => blit_launch.cpp} (99%) rename 32blit-pico/{blit-launch.hpp => blit_launch.hpp} (100%) diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index 0fb531f93..d1af87e6f 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -39,7 +39,7 @@ endfunction() add_library(BlitHalPico INTERFACE) target_sources(BlitHalPico INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/blit-launch.cpp + ${CMAKE_CURRENT_LIST_DIR}/blit_launch.cpp ${CMAKE_CURRENT_LIST_DIR}/display.cpp ${CMAKE_CURRENT_LIST_DIR}/file.cpp ${CMAKE_CURRENT_LIST_DIR}/led.cpp diff --git a/32blit-pico/blit-launch.cpp b/32blit-pico/blit_launch.cpp similarity index 99% rename from 32blit-pico/blit-launch.cpp rename to 32blit-pico/blit_launch.cpp index 68fa790eb..2a6738826 100644 --- a/32blit-pico/blit-launch.cpp +++ b/32blit-pico/blit_launch.cpp @@ -9,7 +9,7 @@ #include "hardware/structs/qmi.h" #endif -#include "blit-launch.hpp" +#include "blit_launch.hpp" #include "file.hpp" #include "engine/engine.hpp" @@ -150,7 +150,7 @@ bool launch_file(const char *path) { file_offset += bytes_read; } - + close_file(file); // didn't write everything, fail launch @@ -266,7 +266,7 @@ void erase_game(uint32_t offset) { // check alignment if(offset & (game_block_size - 1)) return; - + // check in bounds // TODO: prevent erasing fs if flash storage is used? if(offset >= PICO_FLASH_SIZE_BYTES) @@ -374,4 +374,4 @@ bool BlitWriter::prepare_write(const uint8_t *buf) { restore_interrupts(status); return true; -} \ No newline at end of file +} diff --git a/32blit-pico/blit-launch.hpp b/32blit-pico/blit_launch.hpp similarity index 100% rename from 32blit-pico/blit-launch.hpp rename to 32blit-pico/blit_launch.hpp diff --git a/32blit-pico/file.cpp b/32blit-pico/file.cpp index b79bd4666..f27196ba3 100644 --- a/32blit-pico/file.cpp +++ b/32blit-pico/file.cpp @@ -8,7 +8,7 @@ #include "ff.h" #include "diskio.h" -#include "blit-launch.hpp" +#include "blit_launch.hpp" #include "file.hpp" #include "storage.hpp" #include "executable.hpp" diff --git a/32blit-pico/main.cpp b/32blit-pico/main.cpp index 566509132..d323640a9 100644 --- a/32blit-pico/main.cpp +++ b/32blit-pico/main.cpp @@ -12,7 +12,7 @@ #include "audio.hpp" #include "binary_info.hpp" -#include "blit-launch.hpp" +#include "blit_launch.hpp" #include "config.h" #include "display.hpp" #include "file.hpp" diff --git a/32blit-pico/usb.cpp b/32blit-pico/usb.cpp index c62e915a7..7920eac47 100644 --- a/32blit-pico/usb.cpp +++ b/32blit-pico/usb.cpp @@ -6,7 +6,7 @@ #include "hardware/flash.h" #include "usb.hpp" -#include "blit-launch.hpp" +#include "blit_launch.hpp" #include "multiplayer.hpp" #include "engine/engine.hpp" @@ -199,7 +199,7 @@ class CDCListCommand final : public CDCCommand { uint32_t end = 0xFFFFFFFF; usb_cdc_write((uint8_t *)&end, sizeof(uint32_t)); usb_cdc_flush_write(); - + return Status::Done; } }; From e12dde0763b2877521646fa0ca76d495385a3934 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 31 Oct 2024 20:41:26 +0000 Subject: [PATCH 62/70] pico: avoid running pre-init code for blits It's all low-level stuff the loader should've done --- 32blit-pico/startup.cpp | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/32blit-pico/startup.cpp b/32blit-pico/startup.cpp index a79cdb8de..4bf98f98d 100644 --- a/32blit-pico/startup.cpp +++ b/32blit-pico/startup.cpp @@ -25,19 +25,8 @@ extern "C" bool do_init() { return false; #endif - // preinit/init funcs (based on pico-sdk runtime.c) - - // Start and end points of the constructor list, - // defined by the linker script. - extern void (*__preinit_array_start)(); - extern void (*__preinit_array_end)(); - - // Call each function in the list. - // We have to take the address of the symbols, as __preinit_array_start *is* - // the first function pointer, not the address of it. - for (void (**p)(void) = &__preinit_array_start; p < &__preinit_array_end; ++p) { - (*p)(); - } + // init funcs (based on pico-sdk runtime.c) + // we're not calling the preinit funcs as they're all low-level init that should've been done by the loader // Start and end points of the constructor list, // defined by the linker script. From c6353f90f66de237476bbde6caa109151f85c783 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 7 Nov 2024 12:03:01 +0000 Subject: [PATCH 63/70] pico: set our board directory as an extra search path for the SDK --- 32blit-pico/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index d1af87e6f..af09fb405 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -260,6 +260,7 @@ function(blit_executable NAME) target_sources(${NAME} PRIVATE ${STARTUP_SRC}) pico_add_bin_output(${NAME}) + pico_add_dis_output(${NAME}) # Ideally we want the .blit filename to match the .elf, but TARGET_FILE_BASE_NAME isn't always available # (This only affects the firmware updater as it's the only thing setting a custom OUTPUT_NAME) From 5ad2f214391d3d1351616cb7e67adc334b896a49 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Mon, 11 Nov 2024 20:42:36 +0000 Subject: [PATCH 64/70] pico: helper for reading file metadata --- 32blit-pico/blit_launch.cpp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/32blit-pico/blit_launch.cpp b/32blit-pico/blit_launch.cpp index 2a6738826..0708d9ee6 100644 --- a/32blit-pico/blit_launch.cpp +++ b/32blit-pico/blit_launch.cpp @@ -95,6 +95,42 @@ static uint32_t find_flash_offset(uint32_t requested_size) { return 0; } +static bool read_file_metadata(void *file, RawMetadata &meta, RawTypeMetadata &type_meta) { + // read header and check magic + BlitGameHeader header; + if(read_file(file, 0, sizeof(header), (char *)&header) != sizeof(header)) + return false; + + if(header.magic != blit_game_magic) + return false; + + // read and check metadata header + auto meta_offset = header.end; + + char meta_header[10]; + if(read_file(file, meta_offset, 10, meta_header) != 10) + return false; + + if(memcmp(meta_header, "BLITMETA", 8) != 0) + return false; + + // read the reset of the metadata header + if(read_file(file, meta_offset + 10, sizeof(RawMetadata), (char *)&meta) != sizeof(RawMetadata)) + return false; + + // check for type data + meta_offset += 10 + sizeof(RawMetadata); + if(read_file(file, meta_offset, 8, meta_header) != 8) + return false; + + if(memcmp(meta_header, "BLITTYPE", 8) == 0) { + if(read_file(file, meta_offset + 8, sizeof(RawTypeMetadata), (char *)&type_meta) != sizeof(RawTypeMetadata)) + return false; + } + + return true; +} + // 32blit API RawMetadata *get_running_game_metadata() { From 3d42048b59d52104ab51d6e1e588e91e7668663a Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Mon, 11 Nov 2024 22:31:58 +0000 Subject: [PATCH 65/70] pico: check for existing copy of game before flashing --- 32blit-pico/blit_launch.cpp | 78 ++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/32blit-pico/blit_launch.cpp b/32blit-pico/blit_launch.cpp index 0708d9ee6..bc5576df4 100644 --- a/32blit-pico/blit_launch.cpp +++ b/32blit-pico/blit_launch.cpp @@ -131,6 +131,29 @@ static bool read_file_metadata(void *file, RawMetadata &meta, RawTypeMetadata &t return true; } +static uint32_t find_installed_blit(RawMetadata &meta) { + for(uint32_t off = 0; off < PICO_FLASH_SIZE_BYTES;) { + auto size = get_installed_file_size(off); + + if(!size) { + off += game_block_size; + continue; + } + + auto header = (BlitGameHeader *)(FLASH_BASE + off); + auto flash_meta = (RawMetadata *)(FLASH_BASE + off + header->end + 10); + + // check CRC ant title + if(meta.crc32 == flash_meta->crc32 && strcmp(meta.title, flash_meta->title) == 0) { + return off; + } + + off += calc_num_blocks(size) * game_block_size; + } + + return ~0u; +} + // 32blit API RawMetadata *get_running_game_metadata() { @@ -153,47 +176,58 @@ RawMetadata *get_running_game_metadata() { } bool launch_file(const char *path) { - uint32_t flash_offset; + uint32_t flash_offset = ~0u; if(strncmp(path, "flash:/", 7) == 0) // from flash flash_offset = atoi(path + 7) * game_block_size; else { // from storage - // TODO: check if already in flash - auto file = open_file(path, blit::OpenMode::read); if(!file) return false; - BlitWriter writer; + // read file metadata and try to find matching installed gat + RawMetadata meta; + RawTypeMetadata type_meta = {}; - uint32_t file_offset = 0; - uint32_t len = get_file_length(file); + if(read_file_metadata(file, meta, type_meta)) + flash_offset = find_installed_blit(meta); - writer.init(len); + // flash if not found + if(flash_offset == ~0u) { + BlitWriter writer; - // read in small chunks - uint8_t buf[FLASH_PAGE_SIZE]; + uint32_t file_offset = 0; + uint32_t len = get_file_length(file); - while(file_offset < len) { - auto bytes_read = read_file(file, file_offset, FLASH_PAGE_SIZE, (char *)buf); - if(bytes_read <= 0) - break; + writer.init(len); - if(!writer.write(buf, bytes_read)) - break; + // read in small chunks + uint8_t buf[FLASH_PAGE_SIZE]; - file_offset += bytes_read; - } + while(file_offset < len) { + auto bytes_read = read_file(file, file_offset, FLASH_PAGE_SIZE, (char *)buf); + if(bytes_read <= 0) + break; - close_file(file); + if(!writer.write(buf, bytes_read)) + break; - // didn't write everything, fail launch - if(writer.get_remaining() > 0) - return false; + file_offset += bytes_read; + } + + close_file(file); - flash_offset = writer.get_flash_offset(); + // didn't write everything, fail launch + if(writer.get_remaining() > 0) + return false; + + flash_offset = writer.get_flash_offset(); + + // TODO: cleanup duplicates + } else + close_file(file); } auto header = (BlitGameHeader *)(FLASH_BASE + flash_offset); From 55053fe6dc9a166cf9de200408261135708bae93 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 12 Nov 2024 12:26:07 +0000 Subject: [PATCH 66/70] pico: cleanup duplicates after flashing new game --- 32blit-pico/blit_launch.cpp | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/32blit-pico/blit_launch.cpp b/32blit-pico/blit_launch.cpp index bc5576df4..9a166c9fa 100644 --- a/32blit-pico/blit_launch.cpp +++ b/32blit-pico/blit_launch.cpp @@ -154,6 +154,33 @@ static uint32_t find_installed_blit(RawMetadata &meta) { return ~0u; } +static bool cleanup_duplicates(RawMetadata &meta, RawTypeMetadata &type_meta, uint32_t new_offset) { + bool ret = false; + for(uint32_t off = 0; off < PICO_FLASH_SIZE_BYTES;) { + auto size = get_installed_file_size(off); + + if(!size) { + off += game_block_size; + continue; + } + + auto header = (BlitGameHeader *)(FLASH_BASE + off); + auto flash_meta = (RawMetadata *)(FLASH_BASE + off + header->end + 10); + + // check title and author, ignore the current copy of the game + if(off != new_offset && strcmp(meta.title, flash_meta->title) == 0 && strcmp(meta.author, flash_meta->author) == 0) { + erase_game(off); + + if(off == current_game_offset) + ret = true; + } + + off += calc_num_blocks(size) * game_block_size; + } + + return ret; +} + // 32blit API RawMetadata *get_running_game_metadata() { @@ -225,7 +252,7 @@ bool launch_file(const char *path) { flash_offset = writer.get_flash_offset(); - // TODO: cleanup duplicates + cleanup_duplicates(meta, type_meta, flash_offset); } else close_file(file); } From aed25f737516b2cd574af19e3aff9a007bd38ed3 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 26 Nov 2024 15:02:32 +0000 Subject: [PATCH 67/70] pico: restore pre-init on RP2040 It does things we need to not crash and doesn't do any of the things that caused problems on 2350 --- 32blit-pico/startup.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/32blit-pico/startup.cpp b/32blit-pico/startup.cpp index 4bf98f98d..5e266f1c0 100644 --- a/32blit-pico/startup.cpp +++ b/32blit-pico/startup.cpp @@ -25,8 +25,21 @@ extern "C" bool do_init() { return false; #endif - // init funcs (based on pico-sdk runtime.c) - // we're not calling the preinit funcs as they're all low-level init that should've been done by the loader + // preinit/init funcs (based on pico-sdk runtime.c) + // we're not calling the preinit funcs on RP2350 as they're all low-level init that should've been done by the loader + // (on RP2040 they init the ROM functions, so we do need to call them) +#ifndef PICO_RP2350 + // Start and end points of the constructor list, + // defined by the linker script. + extern void (*__preinit_array_start)(); + extern void (*__preinit_array_end)(); + // Call each function in the list. + // We have to take the address of the symbols, as __preinit_array_start *is* + // the first function pointer, not the address of it. + for (void (**p)(void) = &__preinit_array_start; p < &__preinit_array_end; ++p) { + (*p)(); + } +#endif // Start and end points of the constructor list, // defined by the linker script. From 2d1e63db152364b63793409d016f47093edbe15a Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Mon, 30 Dec 2024 11:58:03 +0000 Subject: [PATCH 68/70] pico: use cache API to invalidate after setting up translation --- 32blit-pico/blit_launch.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/32blit-pico/blit_launch.cpp b/32blit-pico/blit_launch.cpp index 9a166c9fa..2a42e0b9f 100644 --- a/32blit-pico/blit_launch.cpp +++ b/32blit-pico/blit_launch.cpp @@ -7,6 +7,7 @@ #ifdef PICO_RP2350 #include "hardware/structs/qmi.h" +#include "hardware/xip_cache.h" #endif #include "blit_launch.hpp" @@ -320,8 +321,7 @@ void delayed_launch() { | (requested_launch_offset >> 12) << QMI_ATRANS1_BASE_LSB; // invalidate cache - for(uint32_t off = 0; off < size; off += 8) - *(uint8_t *)(XIP_MAINTENANCE_BASE + 4 * 1024 * 1024 + off + 2/*invalidate by addr*/) = 0; + xip_cache_invalidate_range(4 * 1024 * 1024, size); // FIXME: handle previous blit also using translation on failure } From d37f1fa4eef74173ef867a23135aa119a36656f6 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Mon, 13 Jan 2025 16:03:55 +0000 Subject: [PATCH 69/70] pico: set flash size to 4M for 2350 blits This is as big as we can handle with the current translation setup --- 32blit-pico/memmap_blit_2350.ld.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/32blit-pico/memmap_blit_2350.ld.in b/32blit-pico/memmap_blit_2350.ld.in index a180b05c3..98526cab8 100644 --- a/32blit-pico/memmap_blit_2350.ld.in +++ b/32blit-pico/memmap_blit_2350.ld.in @@ -23,7 +23,7 @@ MEMORY { - FLASH(rx) : ORIGIN = 0x10000000 + ${FLASH_OFFSET_BYTES}, LENGTH = 2048k + FLASH(rx) : ORIGIN = 0x10000000 + ${FLASH_OFFSET_BYTES}, LENGTH = 4096k RAM(rwx) : ORIGIN = 0x20008000, LENGTH = 480k /*SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k*/ @@ -286,4 +286,4 @@ SECTIONS /* todo assert on extra code */ __flash_binary_size = __flash_binary_end - __flash_binary_start; -} \ No newline at end of file +} From 243e6c7a4a038605aa320cae7496684747ec881a Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Mon, 13 Jan 2025 16:47:36 +0000 Subject: [PATCH 70/70] pico: put fb in uninitialzed data --- 32blit-pico/startup.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/32blit-pico/startup.cpp b/32blit-pico/startup.cpp index 5e266f1c0..83002e921 100644 --- a/32blit-pico/startup.cpp +++ b/32blit-pico/startup.cpp @@ -4,6 +4,7 @@ #include "config.h" #ifndef BLIT_BOARD_PIMORONI_PICOVISION +[[gnu::section(".uninitialized_data.framebuffer")]] static uint16_t screen_fb[FRAMEBUFFER_SIZE]; #endif