diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ff53f62f..55aa39a2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -68,12 +68,13 @@ jobs: name: SC64 path: ./output/sc64menu.n64 - - name: Delete rolling-release tag and release - uses: dev-drprasad/delete-tag-and-release@v1.0 - if: github.ref == 'refs/heads/main' - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - tag_name: rolling-release + # - name: Delete rolling-release tag and release + # uses: dev-drprasad/delete-tag-and-release@v1.0 + # if: github.ref == 'refs/heads/main' + # with: + # github_token: ${{ secrets.GITHUB_TOKEN }} + # tag_name: rolling-release + # continue-on-error: true - name: Upload rolling release uses: softprops/action-gh-release@v0.1.15 @@ -88,6 +89,7 @@ jobs: ./output/OS64.v64 ./output/OS64P.v64 ./output/sc64menu.n64 + continue-on-error: true generate-docs: runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index 7cbbcf11..be770325 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,8 @@ SRCS = \ boot/boot.c \ boot/crc32.c \ boot/reboot.S \ + flashcart/64drive/64drive_ll.c \ + flashcart/64drive/64drive.c \ flashcart/flashcart_utils.c \ flashcart/flashcart.c \ flashcart/sc64/sc64_ll.c \ @@ -44,6 +46,7 @@ SRCS = \ menu/rom_database.c \ menu/settings.c \ menu/sound.c \ + menu/usb_comm.c \ menu/views/browser.c \ menu/views/credits.c \ menu/views/error.c \ diff --git a/README.md b/README.md index b725992b..22784228 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,13 @@ An open source menu for N64 flashcarts. ## Getting started +### 64drive +Ensure the cart has the latest [firmware](https://64drive.retroactive.be/support.php) installed. +Download the latest `menu.bin` file from the releases page, then put it in the root directory of your SD card. + ### SC64 -Ensure the cart is running the latest [firmware](https://github.com/Polprzewodnikowy/SummerCart64/releases/latest). -Download the `sc64menu.n64` ROM from the latest action run assets. -Add it to the root folder on your SD card. +Ensure the cart has the latest [firmware](https://github.com/Polprzewodnikowy/SummerCart64/releases/latest) installed. +Download the latest `sc64menu.n64` file from the releases page, then put it in the root directory of your SD card. ### ED64 Download the `ED64.v64` ROM from the latest action run assets and place it in the `/ED64` folder. @@ -39,8 +42,10 @@ Download the `ED64P.v64` ROM from the latest action run assets and place it in t #### ROM Boxart To use boxart, you need to place png files of size 158x112 in the folder `/menu/boxart` on the SD card. -Each file must be named according to the 2 letter ROM ID. e.g. for goldeneye, this would be `GE.png`. -A known set of PNG files can be downloaded [here](https://mega.nz/file/6cNGwSqI#8X5ukb65n3YMlGaUtSOGXkKo9HxVnnMOgqn94Epcr7w). +Each file must be named according to the 2 letter ROM ID, or 3 letter ROM ID including media type. +i.e. for GoldenEye 2 letters, this would be `GE.png`. +i.e. for GoldenEye 3 letters, this would be `NGE.png`. +A known set of PNG files using 2 letter ID's can be downloaded [here](https://mega.nz/file/6cNGwSqI#8X5ukb65n3YMlGaUtSOGXkKo9HxVnnMOgqn94Epcr7w). #### Emulator support Emulators should be added to the `/emulators` directory on the SD card. @@ -49,6 +54,7 @@ Menu currently supports the following emulators and associated ROM file names: - **NES**: [neon64v2](https://github.com/hcs64/neon64v2) by *hcs64* - `neon64bu.rom` - **SNES**: [sodium64](https://github.com/Hydr8gon/sodium64) by *Hydr8gon* - `sodium64.z64` - **Game Boy** / **GB Color**: [gb64](https://lambertjamesd.github.io/gb64/romwrapper/romwrapper.html) by *lambertjamesd* - `gb.v64` / `gbc.v64` + - **Sega Master System** / **Sega Game Gear** / **Sg1000**: [TotalSMS](https://github.com/ITotalJustice/TotalSMS) - `TotalSMS.z64` (Currently broken) # Developer documentation diff --git a/localdeploy.bat b/localdeploy.bat index f660783e..4f33fff4 100644 --- a/localdeploy.bat +++ b/localdeploy.bat @@ -21,8 +21,14 @@ echo !!! Now toggle power to the N64 !!! echo: echo: -if not "%1" == "/d" goto :exit +if not "%1" == "/d" goto :not_d %~dp0tools\sc64\sc64deployer debug --no-writeback -:exit +:not_d + +if not "%1" == "/du" goto :not_du + +%~dp0tools\sc64\sc64deployer debug --no-writeback --init "send-file /sc64menu.n64 @output/sc64menu.n64@;reboot" + +:not_du diff --git a/remotedeploy.sh b/remotedeploy.sh index 07a8036b..63504902 100755 --- a/remotedeploy.sh +++ b/remotedeploy.sh @@ -4,28 +4,12 @@ set -e REMOTE="--remote ${REMOTE:-host.docker.internal:9064}" -## FIXME: this does not work! -# Make sure we are connected -#echo Detecting SC64... -#sc64deployer $REMOTE list - -# Get the information -echo SC64 info...: -sc64deployer $REMOTE info -echo -echo - -# Load the ROM -echo Loading ROM...: sc64deployer $REMOTE upload ./output/N64FlashcartMenu.n64 -echo -echo -# Toggle the power of the N64 to boot the ROM. -echo !!! Now toggle power to the N64 !!! -echo -echo - if [ "$1" = "-d" ]; then sc64deployer $REMOTE debug --no-writeback fi + +if [ "$1" = "-du" ]; then + sc64deployer $REMOTE debug --no-writeback --init "send-file /sc64menu.n64 @output/sc64menu.n64@;reboot" +fi diff --git a/src/boot/boot.c b/src/boot/boot.c index da6035d5..b4ba7ce4 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -12,7 +12,6 @@ extern uint32_t reboot_start __attribute__((section(".text"))); extern size_t reboot_size __attribute__((section(".text"))); -extern int reboot_entry_offset __attribute__((section(".text"))); typedef struct { @@ -88,7 +87,23 @@ void boot (boot_params_t *params) { while (!(cpu_io_read(&SP->SR) & SP_SR_HALT)); - cpu_io_write(&SP->SR, SP_SR_CLR_INTR | SP_SR_SET_HALT); + cpu_io_write(&SP->SR, + SP_SR_CLR_SIG7 | + SP_SR_CLR_SIG6 | + SP_SR_CLR_SIG5 | + SP_SR_CLR_SIG4 | + SP_SR_CLR_SIG3 | + SP_SR_CLR_SIG2 | + SP_SR_CLR_SIG1 | + SP_SR_CLR_SIG0 | + SP_SR_CLR_INTR_BREAK | + SP_SR_CLR_SSTEP | + SP_SR_CLR_INTR | + SP_SR_CLR_BROKE | + SP_SR_SET_HALT + ); + cpu_io_write(&SP->SEMAPHORE, 0); + cpu_io_write(&SP->PC, 0); while (cpu_io_read(&SP->DMA_BUSY)); @@ -134,15 +149,12 @@ void boot (boot_params_t *params) { cpu_io_write(&ipl3_dst[i], io_read((uint32_t) (&ipl3_src[i]))); } - register void (*entry_point)(void) asm ("t3"); register uint32_t boot_device asm ("s3"); register uint32_t tv_type asm ("s4"); register uint32_t reset_type asm ("s5"); register uint32_t cic_seed asm ("s6"); register uint32_t version asm ("s7"); - void *stack_pointer; - entry_point = (void (*)(void)) UNCACHED(&SP_MEM->IMEM[(int) (&reboot_entry_offset)]); boot_device = (params->device_type & 0x01); tv_type = (params->tv_type & 0x03); reset_type = BOOT_RESET_TYPE_COLD; @@ -151,18 +163,16 @@ void boot (boot_params_t *params) { : (params->tv_type == BOOT_TV_TYPE_NTSC) ? 1 : (params->tv_type == BOOT_TV_TYPE_MPAL) ? 4 : 0; - stack_pointer = (void *) UNCACHED(&SP_MEM->IMEM[1020]); asm volatile ( - "move $sp, %[stack_pointer] \n" - "jr %[entry_point] \n" :: - [entry_point] "r" (entry_point), + "la $t3, reboot \n" + "jr $t3 \n" :: [boot_device] "r" (boot_device), [tv_type] "r" (tv_type), [reset_type] "r" (reset_type), [cic_seed] "r" (cic_seed), - [version] "r" (version), - [stack_pointer] "r" (stack_pointer) + [version] "r" (version) : + "t3" ); while (1); diff --git a/src/boot/boot_io.h b/src/boot/boot_io.h index 2eede3b5..547a6630 100644 --- a/src/boot/boot_io.h +++ b/src/boot/boot_io.h @@ -37,6 +37,8 @@ typedef struct { io32_t DMA_FULL; io32_t DMA_BUSY; io32_t SEMAPHORE; + io32_t __reserved[0xFFF8]; + io32_t PC; } sp_regs_t; #define SP_BASE (0x04040000UL) diff --git a/src/boot/reboot.S b/src/boot/reboot.S index 3e5c5c1a..ec6dc9f2 100644 --- a/src/boot/reboot.S +++ b/src/boot/reboot.S @@ -1,3 +1,7 @@ +#define IPL3_ENTRY 0xA4000040 +#define REBOOT_ADDRESS 0xA4001000 +#define STACK_ADDRESS 0xA4001FF0 + #define RI_ADDRESS 0xA4700000 #define RI_MODE 0x00 @@ -13,8 +17,6 @@ #define RDRAM_RESET_DELAY 1024 #define RDRAM_STANDBY_DELAY 512 -#define IPL3_ENTRY 0xA4000040 - .set noat .section .text.reboot, "ax", %progbits .type reboot, %object @@ -36,8 +38,10 @@ ipl2: .set reorder reboot_entry: - .equ reboot_entry_offset, ((. - reboot_start) / 4) - .global reboot_entry_offset + .set reboot, REBOOT_ADDRESS + (. - reboot_start) + .global reboot + + li $sp, STACK_ADDRESS bnez $s5, reset_rdram_skip @@ -66,6 +70,12 @@ reset_rdram: bnez $t2, 1b reset_rdram_skip: +prepare_registers: + la $t0, ra_table + sll $t1, $s4, 2 + add $t0, $t1 + lw $ra, ($t0) + move $at, $zero move $v0, $zero move $v1, $zero @@ -76,7 +86,6 @@ reset_rdram_skip: move $t0, $zero move $t1, $zero move $t2, $zero - move $t3, $zero move $t4, $zero move $t5, $zero move $t6, $zero @@ -90,13 +99,16 @@ reset_rdram_skip: move $k1, $zero move $gp, $zero move $s8, $zero - move $ra, $zero - - mtc0 $zero, $9 run_ipl3: li $t3, IPL3_ENTRY jr $t3 - .equ reboot_size, (. - reboot_start) +ra_values: + .set ra_table, REBOOT_ADDRESS + (. - reboot_start) + .word 0xA4001554 + .word 0xA4001550 + .word 0xA4001554 + + .set reboot_size, (. - reboot_start) .global reboot_size diff --git a/src/flashcart/64drive/64drive.c b/src/flashcart/64drive/64drive.c new file mode 100644 index 00000000..82a0e1ea --- /dev/null +++ b/src/flashcart/64drive/64drive.c @@ -0,0 +1,271 @@ +#include +#include +#include + +#include +#include + +#include "utils/fs.h" +#include "utils/utils.h" + +#include "../flashcart_utils.h" +#include "64drive_ll.h" +#include "64drive.h" + + +#define ROM_ADDRESS (0x10000000) +#define SAVE_ADDRESS_DEV_A (0x13FE0000) +#define SAVE_ADDRESS_DEV_A_PKST2 (0x11606560) +#define SAVE_ADDRESS_DEV_B (0x1FFC0000) + +#define SUPPORTED_FPGA_REVISION (205) + + +static d64_device_variant_t device_variant = DEVICE_VARIANT_UNKNOWN; +static d64_save_type_t current_save_type = SAVE_TYPE_NONE; + + +static flashcart_error_t d64_init (void) { + uint16_t fpga_revision; + uint32_t bootloader_version; + + if (d64_ll_enable_extended_mode(false)) { + return FLASHCART_ERROR_INT; + } + + if (d64_ll_get_version(&device_variant, &fpga_revision, &bootloader_version)) { + return FLASHCART_ERROR_INT; + } + + if (fpga_revision < SUPPORTED_FPGA_REVISION) { + return FLASHCART_ERROR_OUTDATED; + } + + if (d64_ll_enable_save_writeback(false)) { + return FLASHCART_ERROR_INT; + } + + if (d64_ll_set_save_type(SAVE_TYPE_NONE)) { + return FLASHCART_ERROR_INT; + } + + current_save_type = SAVE_TYPE_NONE; + + if (d64_ll_enable_cartrom_writes(true)) { + return FLASHCART_ERROR_INT; + } + + if (d64_ll_set_persistent_variable_storage(false, 0, 0)) { + return FLASHCART_ERROR_INT; + } + + return FLASHCART_OK; +} + +static flashcart_error_t d64_deinit (void) { + if (d64_ll_enable_cartrom_writes(false)) { + return FLASHCART_ERROR_INT; + } + + return FLASHCART_OK; +} + +static flashcart_error_t d64_load_rom (char *rom_path, flashcart_progress_callback_t *progress) { + FIL fil; + UINT br; + + if (f_open(&fil, strip_sd_prefix(rom_path), FA_READ) != FR_OK) { + return FLASHCART_ERROR_LOAD; + } + + fix_file_size(&fil); + + size_t rom_size = f_size(&fil); + + if (rom_size > MiB(64)) { + f_close(&fil); + return FLASHCART_ERROR_LOAD; + } + + size_t sdram_size = MiB(64); + + size_t chunk_size = MiB(1); + for (int offset = 0; offset < sdram_size; offset += chunk_size) { + size_t block_size = MIN(sdram_size - offset, chunk_size); + if (f_read(&fil, (void *) (ROM_ADDRESS + offset), block_size, &br) != FR_OK) { + f_close(&fil); + return FLASHCART_ERROR_LOAD; + } + if (progress) { + progress(f_tell(&fil) / (float) (f_size(&fil))); + } + } + if (f_tell(&fil) != rom_size) { + f_close(&fil); + return FLASHCART_ERROR_LOAD; + } + + if (f_close(&fil) != FR_OK) { + return FLASHCART_ERROR_LOAD; + } + + return FLASHCART_OK; +} + +static flashcart_error_t d64_load_file (char *file_path, uint32_t rom_offset, uint32_t file_offset) { + FIL fil; + UINT br; + + if (f_open(&fil, strip_sd_prefix(file_path), FA_READ) != FR_OK) { + return FLASHCART_ERROR_LOAD; + } + + fix_file_size(&fil); + + size_t file_size = f_size(&fil) - file_offset; + + if (file_size > (MiB(64) - rom_offset)) { + f_close(&fil); + return FLASHCART_ERROR_ARGS; + } + + if (f_lseek(&fil, file_offset) != FR_OK) { + f_close(&fil); + return FLASHCART_ERROR_LOAD; + } + + if (f_read(&fil, (void *) (ROM_ADDRESS + rom_offset), file_size, &br) != FR_OK) { + f_close(&fil); + return FLASHCART_ERROR_LOAD; + } + if (br != file_size) { + f_close(&fil); + return FLASHCART_ERROR_LOAD; + } + + if (f_close(&fil) != FR_OK) { + return FLASHCART_ERROR_LOAD; + } + + return FLASHCART_OK; +} + +static flashcart_error_t d64_load_save (char *save_path) { + uint8_t eeprom_contents[2048] __attribute__((aligned(8))); + FIL fil; + UINT br; + + if (f_open(&fil, strip_sd_prefix(save_path), FA_READ) != FR_OK) { + return FLASHCART_ERROR_LOAD; + } + + size_t save_size = f_size(&fil); + + bool is_eeprom_save = (current_save_type == SAVE_TYPE_EEPROM_4K || current_save_type == SAVE_TYPE_EEPROM_16K); + + void *address = (void *) (SAVE_ADDRESS_DEV_B); + if (is_eeprom_save) { + address = eeprom_contents; + } else if (device_variant == DEVICE_VARIANT_A) { + address = (void *) (SAVE_ADDRESS_DEV_A); + if (current_save_type == SAVE_TYPE_FLASHRAM_PKST2) { + address = (void *) (SAVE_ADDRESS_DEV_A_PKST2); + } + } + + if (f_read(&fil, address, save_size, &br) != FR_OK) { + f_close(&fil); + return FLASHCART_ERROR_LOAD; + } + + if (is_eeprom_save) { + if (d64_ll_write_eeprom_contents(eeprom_contents)) { + f_close(&fil); + return FLASHCART_ERROR_LOAD; + } + } + + if (f_close(&fil) != FR_OK) { + return FLASHCART_ERROR_LOAD; + } + + if (br != save_size) { + return FLASHCART_ERROR_LOAD; + } + + return FLASHCART_OK; +} + +static flashcart_error_t d64_set_save_type (flashcart_save_type_t save_type) { + d64_save_type_t type; + + switch (save_type) { + case FLASHCART_SAVE_TYPE_NONE: + type = SAVE_TYPE_NONE; + break; + case FLASHCART_SAVE_TYPE_EEPROM_4K: + type = SAVE_TYPE_EEPROM_4K; + break; + case FLASHCART_SAVE_TYPE_EEPROM_16K: + type = SAVE_TYPE_EEPROM_16K; + break; + case FLASHCART_SAVE_TYPE_SRAM: + type = SAVE_TYPE_SRAM; + break; + case FLASHCART_SAVE_TYPE_SRAM_BANKED: + type = SAVE_TYPE_SRAM_BANKED; + break; + case FLASHCART_SAVE_TYPE_SRAM_128K: + // NOTE: 64drive doesn't support 128 kiB SRAM save type, fallback to 32 kiB SRAM save type + type = SAVE_TYPE_SRAM; + break; + case FLASHCART_SAVE_TYPE_FLASHRAM: + type = SAVE_TYPE_FLASHRAM; + break; + case FLASHCART_SAVE_TYPE_FLASHRAM_PKST2: + type = (device_variant == DEVICE_VARIANT_A) ? SAVE_TYPE_FLASHRAM_PKST2 : SAVE_TYPE_FLASHRAM; + break; + default: + return FLASHCART_ERROR_ARGS; + } + + if (d64_ll_enable_save_writeback(false)) { + return FLASHCART_ERROR_INT; + } + + if (d64_ll_set_save_type(type)) { + return FLASHCART_ERROR_INT; + } + + current_save_type = type; + + return FLASHCART_OK; +} + +static flashcart_error_t d64_set_save_writeback (uint32_t *sectors) { + if (d64_ll_write_save_writeback_lba_list(sectors)) { + return FLASHCART_ERROR_INT; + } + + if (d64_ll_enable_save_writeback(true)) { + return FLASHCART_ERROR_INT; + } + + return FLASHCART_OK; +} + + +static flashcart_t flashcart_d64 = { + .init = d64_init, + .deinit = d64_deinit, + .load_rom = d64_load_rom, + .load_file = d64_load_file, + .load_save = d64_load_save, + .set_save_type = d64_set_save_type, + .set_save_writeback = d64_set_save_writeback, +}; + + +flashcart_t *d64_get_flashcart (void) { + return &flashcart_d64; +} diff --git a/src/flashcart/64drive/64drive.h b/src/flashcart/64drive/64drive.h new file mode 100644 index 00000000..4855b10f --- /dev/null +++ b/src/flashcart/64drive/64drive.h @@ -0,0 +1,24 @@ +/** + * @file 64drive.h + * @brief 64drive flashcart support + * @ingroup flashcart + */ + +#ifndef FLASHCART_64DRIVE_H__ +#define FLASHCART_64DRIVE_H__ + + +#include "../flashcart.h" + + +/** + * @addtogroup 64drive + * @{ + */ + +flashcart_t *d64_get_flashcart (void); + +/** @} */ /* 64drive */ + + +#endif diff --git a/src/flashcart/64drive/64drive_ll.c b/src/flashcart/64drive/64drive_ll.c new file mode 100644 index 00000000..fb00a89c --- /dev/null +++ b/src/flashcart/64drive/64drive_ll.c @@ -0,0 +1,110 @@ +#include + +#include "../flashcart_utils.h" +#include "64drive_ll.h" + + +#define CI_STATUS_BUSY (1 << 12) + +#define DEVICE_VARIANT_MASK (0xFFFF) +#define FPGA_REVISION_MASK (0xFFFF) + + +typedef enum { + CMD_ID_SET_SAVE_TYPE = 0xD0, + CMD_ID_DISABLE_SAVE_WRITEBACK = 0xD1, + CMD_ID_ENABLE_SAVE_WRITEBACK = 0xD2, + CMD_ID_DISABLE_BYTESWAP_ON_LOAD = 0xE0, + CMD_ID_ENABLE_BYTESWAP_ON_LOAD = 0xE1, + CMD_ID_ENABLE_CARTROM_WRITES = 0xF0, + CMD_ID_DISABLE_CARTROM_WRITES = 0xF1, + CMD_ID_ENABLE_EXTENDED_MODE = 0xF8, + CMD_ID_DISABLE_EXTENDED_MODE = 0xF9, +} d64_ci_cmd_id_t; + + +static d64_regs_t *d64_regs = D64_REGS; + + +static bool d64_ll_ci_wait (void) { + int timeout = 0; + do { + if (timeout++ >= 0x10000) { + return true; + } + } while (io_read((uint32_t) (&d64_regs->STATUS)) & CI_STATUS_BUSY); + return false; +} + +static bool d64_ll_ci_cmd (d64_ci_cmd_id_t id) { + io_write((uint32_t) (&d64_regs->COMMAND), id); + return d64_ll_ci_wait(); +} + + +bool d64_ll_get_version (d64_device_variant_t *device_variant, uint16_t *fpga_revision, uint32_t *bootloader_version) { + if (d64_ll_ci_wait()) { + return true; + } + *device_variant = (d64_device_variant_t) (io_read((uint32_t) (&d64_regs->VARIANT)) & DEVICE_VARIANT_MASK); + *fpga_revision = (io_read((uint32_t) (&d64_regs->REVISION)) & FPGA_REVISION_MASK); + *bootloader_version = io_read((uint32_t) (&d64_regs->PERSISTENT)); + return d64_ll_ci_wait(); +} + +bool d64_ll_set_persistent_variable_storage (bool quick_reboot, d64_tv_type_t force_tv_type, uint8_t cic_seed) { + if (d64_ll_ci_wait()) { + return true; + } + io_write((uint32_t) (&d64_regs->PERSISTENT), (quick_reboot << 16) | ((force_tv_type & 0x03) << 8) | (cic_seed & 0xFF)); + return d64_ll_ci_wait(); +} + +bool d64_ll_set_save_type (d64_save_type_t save_type) { + if (d64_ll_ci_wait()) { + return true; + } + io_write((uint32_t) (&d64_regs->BUFFER), save_type); + return d64_ll_ci_cmd(CMD_ID_SET_SAVE_TYPE); +} + +bool d64_ll_enable_save_writeback (bool enabled) { + if (d64_ll_ci_wait()) { + return true; + } + return d64_ll_ci_cmd(enabled ? CMD_ID_ENABLE_SAVE_WRITEBACK : CMD_ID_DISABLE_SAVE_WRITEBACK); +} + +bool d64_ll_enable_cartrom_writes (bool enabled) { + if (d64_ll_ci_wait()) { + return true; + } + return d64_ll_ci_cmd(enabled ? CMD_ID_ENABLE_CARTROM_WRITES : CMD_ID_DISABLE_CARTROM_WRITES); +} + +bool d64_ll_enable_extended_mode (bool enabled) { + d64_ll_ci_wait(); + if (enabled) { + io_write((uint32_t) (&D64_REGS->COMMAND), CMD_ID_ENABLE_EXTENDED_MODE); + } else { + io_write((uint32_t) (&D64_REGS_EXT->COMMAND), CMD_ID_DISABLE_EXTENDED_MODE); + } + d64_regs = enabled ? D64_REGS_EXT : D64_REGS; + return d64_ll_ci_wait(); +} + +bool d64_ll_write_eeprom_contents (void *contents) { + if (d64_ll_ci_wait()) { + return true; + } + pi_dma_write_data(contents, d64_regs->EEPROM, 2048); + return d64_ll_ci_wait(); +} + +bool d64_ll_write_save_writeback_lba_list (void *list) { + if (d64_ll_ci_wait()) { + return true; + } + pi_dma_write_data(list, d64_regs->WRITEBACK, 1024); + return d64_ll_ci_wait(); +} diff --git a/src/flashcart/64drive/64drive_ll.h b/src/flashcart/64drive/64drive_ll.h new file mode 100644 index 00000000..44d9ee5f --- /dev/null +++ b/src/flashcart/64drive/64drive_ll.h @@ -0,0 +1,99 @@ +/** + * @file 64drive_ll.h + * @brief 64drive flashcart low level access + * @ingroup flashcart + */ + +#ifndef FLASHCART_64DRIVE_LL_H__ +#define FLASHCART_64DRIVE_LL_H__ + + +#include +#include + + +/** + * @addtogroup 64drive + * @{ + */ + +typedef struct { + uint8_t BUFFER[512]; + uint32_t STATUS; + uint32_t __unused_1; + uint32_t COMMAND; + uint32_t __unused_2; + uint32_t LBA; + uint32_t __unused_3; + uint32_t LENGTH; + uint32_t __unused_4; + uint32_t RESULT; + + uint32_t __unused_5[49]; + + uint32_t SDRAM_SIZE; + uint32_t MAGIC; + uint32_t VARIANT; + uint32_t PERSISTENT; + uint32_t BUTTON_UPGRADE; + uint32_t REVISION; + + uint32_t __unused_6[64]; + + uint32_t USB_COMMAND_STATUS; + uint32_t USB_PARAM_RESULT[2]; + + uint32_t __unused_7[5]; + + uint32_t WIFI_COMMAND_STATUS; + uint32_t WIFI_PARAM_RESULT[2]; + + uint32_t __unused_8[757]; + + uint8_t EEPROM[2048]; + uint32_t WRITEBACK[256]; +} d64_regs_t; + +#define D64_REGS_BASE (0x18000000UL) +#define D64_REGS_BASE_EXT (0x1F800000UL) +#define D64_REGS ((d64_regs_t *) D64_REGS_BASE) +#define D64_REGS_EXT ((d64_regs_t *) D64_REGS_BASE_EXT) + + +typedef enum { + DEVICE_VARIANT_UNKNOWN = 0x0000, + DEVICE_VARIANT_A = 0x4100, + DEVICE_VARIANT_B = 0x4200, +} d64_device_variant_t; + +typedef enum { + TV_TYPE_PAL = 0, + TV_TYPE_NTSC = 1, + TV_TYPE_MPAL = 2, + TV_TYPE_UNKNOWN = 3, +} d64_tv_type_t; + +typedef enum { + SAVE_TYPE_NONE, + SAVE_TYPE_EEPROM_4K, + SAVE_TYPE_EEPROM_16K, + SAVE_TYPE_SRAM, + SAVE_TYPE_FLASHRAM, + SAVE_TYPE_SRAM_BANKED, + SAVE_TYPE_FLASHRAM_PKST2, +} d64_save_type_t; + + +bool d64_ll_get_version (d64_device_variant_t *device_variant, uint16_t *fpga_revision, uint32_t *bootloader_version); +bool d64_ll_set_persistent_variable_storage (bool quick_reboot, d64_tv_type_t force_tv_type, uint8_t cic_seed); +bool d64_ll_set_save_type (d64_save_type_t save_type); +bool d64_ll_enable_save_writeback (bool enabled); +bool d64_ll_enable_cartrom_writes (bool enabled); +bool d64_ll_enable_extended_mode (bool enabled); +bool d64_ll_write_eeprom_contents (void *contents); +bool d64_ll_write_save_writeback_lba_list (void *list); + +/** @} */ /* 64drive */ + + +#endif diff --git a/src/flashcart/64drive/README.md b/src/flashcart/64drive/README.md new file mode 100644 index 00000000..bd961b85 --- /dev/null +++ b/src/flashcart/64drive/README.md @@ -0,0 +1,49 @@ +## 64drive developer notes + +### Official documentation + +http://64drive.retroactive.be/64drive_hardware_spec.pdf + + +### Save location offset in SDRAM + +| Type | HW1 | HW2 | +| ---------------------------- | ------------ | ------------ | +| SRAM | `0x03FE0000` | `0x0FFC0000` | +| FlashRAM | `0x03FE0000` | `0x0FFC0000` | +| SRAM banked | `0x03FE0000` | `0x0FFC0000` | +| FlashRAM (Pokemon Stadium 2) | `0x01606560` | `0x0FFC0000` | + +EEPROM save types are stored in separate memory inside FPGA, rest of the save types are stored inside SDRAM memory. +EEPROM save types need manual load as this memory space can't be written with "Read multiple sectors to SDRAM" command. + + +### "Persistent variable storage" register + +| Bits | Meaning | +| --------- | ---------------------------------------------------------- | +| `[31:17]` | _Unused_ | +| `[16]` | Reset behavior: `0` - boot to menu / `1` - quick boot game | +| `[15:10]` | _Unused_ | +| `[9:8]` | Force TV type | +| `[7:0]` | CIC seed | + +It's used by the bootloader to quickly load game without running menu again. +Should contain bootloader version but it's zeroed if ROM wasn't ran through bootloader (e.g. ROM was loaded via USB). + + +### "Enable/disable save writeback" command + +Does not work when USB cable is connected - writeback is forced to be in disabled state. +Curiously, official 64drive menu never calls this command, save writeback might be enabled by default. + + +### "Enable/disable byteswap on load" command + +Annoyingly, this command affects both loading single sector into the buffer and loading multiple sectors to the SDRAM. + + +### "Enable/disable extended address mode" command + +As of latest available firmware version 2.05 this command is not implemented. +Documentation specifies it's supported on firmwares 2.06+ but this version (or anything newer) was never published. diff --git a/src/flashcart/flashcart.c b/src/flashcart/flashcart.c index 85e60eec..d7938356 100644 --- a/src/flashcart/flashcart.c +++ b/src/flashcart/flashcart.c @@ -8,11 +8,13 @@ #include "utils/utils.h" #include "flashcart.h" + +#include "64drive/64drive.h" #include "sc64/sc64.h" #include "ed64/ed64.h" -#define WRITEBACK_MAX_SECTORS (256) +#define SAVE_WRITEBACK_MAX_SECTORS (256) static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = { @@ -23,8 +25,12 @@ static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = { KiB(96), KiB(128), KiB(128), + KiB(128), }; +static uint32_t save_writeback_sectors[SAVE_WRITEBACK_MAX_SECTORS] __attribute__((aligned(8))); + + static flashcart_error_t dummy_init (void) { return FLASHCART_OK; } @@ -56,7 +62,7 @@ flashcart_error_t flashcart_init (void) { int (* libcart_init) (void); flashcart_t *(* get) (void); } flashcarts[CART_MAX] = { - { CART_CI, ci_init, NULL }, // 64drive + { CART_CI, ci_init, d64_get_flashcart }, // 64drive { CART_SC, sc_init, sc64_get_flashcart }, // SC64 { CART_EDX, edx_init, NULL }, // Series X EverDrive-64 { CART_ED, ed_init, ed64_get_flashcart }, // Original EverDrive-64 @@ -96,6 +102,7 @@ flashcart_error_t flashcart_deinit (void) { if (flashcart->deinit) { return flashcart->deinit(); } + return FLASHCART_OK; } @@ -125,9 +132,21 @@ flashcart_error_t flashcart_load_file (char *file_path, uint32_t rom_offset, uin return flashcart->load_file(file_path, rom_offset, file_offset); } +static void save_writeback_sectors_callback (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size) { + for (uint32_t i = 0; i < cluster_size; i++) { + uint32_t offset = file_sector + i; + uint32_t sector = cluster_sector + i; + + if ((offset > SAVE_WRITEBACK_MAX_SECTORS) || (offset > sector_count)) { + return; + } + + save_writeback_sectors[offset] = sector; + } +} + flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type) { flashcart_error_t error; - uint32_t sectors[WRITEBACK_MAX_SECTORS] __attribute__((aligned(8))); if (save_type >= __FLASHCART_SAVE_TYPE_END) { return FLASHCART_ERROR_ARGS; @@ -159,10 +178,13 @@ flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t sa } if (flashcart->set_save_writeback) { - if (file_get_sectors(save_path, sectors, WRITEBACK_MAX_SECTORS)) { + for (int i = 0; i < SAVE_WRITEBACK_MAX_SECTORS; i++) { + save_writeback_sectors[i] = 0; + } + if (file_get_sectors(save_path, save_writeback_sectors_callback)) { return FLASHCART_ERROR_LOAD; } - if ((error = flashcart->set_save_writeback(sectors)) != FLASHCART_OK) { + if ((error = flashcart->set_save_writeback(save_writeback_sectors)) != FLASHCART_OK) { return error; } } diff --git a/src/flashcart/flashcart.h b/src/flashcart/flashcart.h index 436aaf94..844ab3cb 100644 --- a/src/flashcart/flashcart.h +++ b/src/flashcart/flashcart.h @@ -32,6 +32,7 @@ typedef enum { FLASHCART_SAVE_TYPE_SRAM_BANKED, FLASHCART_SAVE_TYPE_SRAM_128K, FLASHCART_SAVE_TYPE_FLASHRAM, + FLASHCART_SAVE_TYPE_FLASHRAM_PKST2, __FLASHCART_SAVE_TYPE_END } flashcart_save_type_t; diff --git a/src/flashcart/sc64/sc64.c b/src/flashcart/sc64/sc64.c index 9cc554c4..1d7245d4 100644 --- a/src/flashcart/sc64/sc64.c +++ b/src/flashcart/sc64/sc64.c @@ -85,7 +85,7 @@ static flashcart_error_t sc64_init (void) { uint32_t value; } default_config[] = { { CFG_ID_BOOTLOADER_SWITCH, false }, - { CFG_ID_ROM_WRITE_ENABLE, false }, + { CFG_ID_ROM_WRITE_ENABLE, true }, { CFG_ID_ROM_SHADOW_ENABLE, false }, { CFG_ID_DD_MODE, DD_MODE_DISABLED }, { CFG_ID_ISV_ADDRESS, 0x00000000 }, @@ -110,6 +110,8 @@ static flashcart_error_t sc64_init (void) { } static flashcart_error_t sc64_deinit (void) { + sc64_ll_set_config(CFG_ID_ROM_WRITE_ENABLE, false); + sc64_ll_lock(); return FLASHCART_OK; @@ -309,6 +311,9 @@ static flashcart_error_t sc64_set_save_type (flashcart_save_type_t save_type) { case FLASHCART_SAVE_TYPE_FLASHRAM: type = SAVE_TYPE_FLASHRAM; break; + case FLASHCART_SAVE_TYPE_FLASHRAM_PKST2: + type = SAVE_TYPE_FLASHRAM; + break; default: return FLASHCART_ERROR_ARGS; } diff --git a/src/menu/cart_load.c b/src/menu/cart_load.c index a8f898dc..62f4bc73 100644 --- a/src/menu/cart_load.c +++ b/src/menu/cart_load.c @@ -91,7 +91,6 @@ cart_load_err_t cart_load_emulator (menu_t *menu, cart_load_emu_type_t emu_type, case CART_LOAD_EMU_TYPE_SNES: path_push(path, "sodium64.z64"); save_type = FLASHCART_SAVE_TYPE_SRAM; - emulated_rom_offset = 0x104000; break; case CART_LOAD_EMU_TYPE_GAMEBOY: path_push(path, "gb.v64"); @@ -101,6 +100,10 @@ cart_load_err_t cart_load_emulator (menu_t *menu, cart_load_emu_type_t emu_type, path_push(path, "gbc.v64"); save_type = FLASHCART_SAVE_TYPE_FLASHRAM; break; + case CART_LOAD_EMU_TYPE_SEGA_GENERIC_8BIT: + path_push(path, "TotalSMS.z64"); + save_type = FLASHCART_SAVE_TYPE_SRAM; + break; } if (!file_exists(path_get(path))) { @@ -120,6 +123,7 @@ cart_load_err_t cart_load_emulator (menu_t *menu, cart_load_emu_type_t emu_type, switch (emu_type) { case CART_LOAD_EMU_TYPE_SNES: + // The emulator expects the header to be removed from the ROM being uploaded. emulated_file_offset = ((file_get_size(path_get(path)) & 0x3FF) == 0x200) ? 0x200 : 0; break; default: diff --git a/src/menu/cart_load.h b/src/menu/cart_load.h index 3fde2d3c..e5359ddd 100644 --- a/src/menu/cart_load.h +++ b/src/menu/cart_load.h @@ -28,6 +28,7 @@ typedef enum { CART_LOAD_EMU_TYPE_SNES, CART_LOAD_EMU_TYPE_GAMEBOY, CART_LOAD_EMU_TYPE_GAMEBOY_COLOR, + CART_LOAD_EMU_TYPE_SEGA_GENERIC_8BIT, } cart_load_emu_type_t; diff --git a/src/menu/components.h b/src/menu/components.h index 858b9763..10a8437d 100644 --- a/src/menu/components.h +++ b/src/menu/components.h @@ -59,7 +59,7 @@ typedef struct { surface_t *image; } component_boxart_t; -component_boxart_t *component_boxart_init (uint16_t id); +component_boxart_t *component_boxart_init (uint8_t media_type, uint16_t id); void component_boxart_free (component_boxart_t *b); void component_boxart_draw (component_boxart_t *b); diff --git a/src/menu/components/boxart.c b/src/menu/components/boxart.c index 411e8bbb..6419c1cf 100644 --- a/src/menu/components/boxart.c +++ b/src/menu/components/boxart.c @@ -4,6 +4,7 @@ #include "../path.h" #include "../png_decoder.h" #include "constants.h" +#include "utils/fs.h" #define BOXART_DIRECTORY "sd:/menu/boxart" @@ -16,14 +17,18 @@ static void png_decoder_callback (png_err_t err, surface_t *decoded_image, void } -component_boxart_t *component_boxart_init (uint16_t id) { +component_boxart_t *component_boxart_init (uint8_t media_type, uint16_t id) { component_boxart_t *b = calloc(1, sizeof(component_boxart_t)); if (b) { b->loading = true; + char *path = alloca(strlen(BOXART_DIRECTORY) + 1 + 7 + 1); // allocate for the largest path. + sprintf(path, "%s/%c%c%c.png", BOXART_DIRECTORY, (media_type & 0xFF), ((id >> 8) & 0xFF), (id & 0xFF)); - char *path = alloca(strlen(BOXART_DIRECTORY) + 1 + 6 + 1); - sprintf(path, "%s/%c%c.png", BOXART_DIRECTORY, ((id >> 8) & 0xFF), (id & 0xFF)); + // if the file does not exist, also check for just the id. + if (!file_exists(path)) { + sprintf(path, "%s/%c%c.png", BOXART_DIRECTORY, ((id >> 8) & 0xFF), (id & 0xFF)); + } if (png_decoder_start(path, BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) != PNG_OK) { free(b); diff --git a/src/menu/menu.c b/src/menu/menu.c index f728d12e..3a25e98f 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -14,6 +14,7 @@ #include "png_decoder.h" #include "settings.h" #include "sound.h" +#include "usb_comm.h" #include "utils/fs.h" #include "views/views.h" @@ -28,7 +29,6 @@ static menu_t *menu; -static bool boot_pending; static tv_type_t tv_type; static volatile int frame_counter = 0; @@ -61,8 +61,6 @@ static void menu_init (boot_params_t *boot_params) { fonts_init(); sound_init_default(); - boot_pending = false; - menu = calloc(1, sizeof(menu_t)); assert(menu != NULL); @@ -147,7 +145,7 @@ static struct views_s { void menu_run (boot_params_t *boot_params) { menu_init(boot_params); - while (!boot_pending && (exception_reset_time() < RESET_TIME_LENGTH)) { + while (exception_reset_time() < RESET_TIME_LENGTH) { surface_t *display = (frame_counter >= FRAMERATE_DIVIDER) ? display_try_get() : NULL; if (display != NULL) { @@ -159,7 +157,12 @@ void menu_run (boot_params_t *boot_params) { views[menu->mode].show(menu, display); } else { rdpq_attach_clear(display, NULL); - rdpq_detach_show(); + rdpq_detach_wait(); + display_show(display); + } + + if (menu->mode == MENU_MODE_BOOT) { + break; } while (menu->mode != menu->next_mode) { @@ -168,10 +171,6 @@ void menu_run (boot_params_t *boot_params) { if (views[menu->mode].init) { views[menu->mode].init(menu); } - - if (menu->mode == MENU_MODE_BOOT) { - boot_pending = true; - } } time(&menu->current_time); @@ -180,6 +179,8 @@ void menu_run (boot_params_t *boot_params) { sound_poll(); png_decoder_poll(); + + usb_comm_poll(menu); } menu_deinit(menu); diff --git a/src/menu/usb_comm.c b/src/menu/usb_comm.c new file mode 100644 index 00000000..5f81d457 --- /dev/null +++ b/src/menu/usb_comm.c @@ -0,0 +1,163 @@ +// NOTE: This code doesn't implement EverDrive-64 USB protocol. +// Main use of these functions is to aid menu development +// (for example replace files on the SD card or reboot menu). + +#include +#include +#include + +#include "usb_comm.h" +#include "utils/utils.h" + + +#define MAX_FILE_SIZE MiB(4) + + +typedef struct { + const char *id; + void (*op) (menu_t *menu); +} usb_comm_command_t; + + +static int usb_comm_get_char (void) { + char c; + + if (USBHEADER_GETSIZE(usb_poll()) <= 0) { + return -1; + } + + usb_read(&c, sizeof(c)); + + return (int) (c); +} + +static bool usb_comm_read_string (char *string, int length, char end) { + for (int i = 0; i < length; i++) { + int c = usb_comm_get_char(); + + if (c < 0) { + return true; + } + + string[i] = (char) (c); + + if (c == '\0' || c == end) { + string[i] = '\0'; + break; + } + + if (i == (length - 1)) { + return true; + } + } + + return false; +} + +static void usb_comm_send_error (const char *message) { + usb_purge(); + usb_write(DATATYPE_TEXT, message, strlen(message)); +} + + +static void command_reboot (menu_t *menu) { + menu->next_mode = MENU_MODE_BOOT; + + menu->boot_params->device_type = BOOT_DEVICE_TYPE_ROM; + menu->boot_params->tv_type = BOOT_TV_TYPE_PASSTHROUGH; + menu->boot_params->detect_cic_seed = true; +}; + +static void command_send_file (menu_t *menu) { + char path[256]; + char length[8]; + + FIL f; + int remaining; + uint8_t data[8192]; + UINT bytes_written; + + if (usb_comm_read_string(path, sizeof(path), ' ')) { + return usb_comm_send_error("Invalid path argument\n"); + } + + if (usb_comm_get_char() != '@') { + return usb_comm_send_error("Invalid argument\n"); + } + + if (usb_comm_read_string(length, sizeof(length), '@')) { + return usb_comm_send_error("Invalid file length argument\n"); + } + + if (f_open(&f, path, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) { + return usb_comm_send_error("Couldn't create file\n"); + } + + remaining = atoi(length); + + if (remaining > MAX_FILE_SIZE) { + return usb_comm_send_error("File size too big\n"); + } + + while (remaining > 0) { + int block_size = MIN(remaining, sizeof(data)); + usb_read(data, block_size); + if (f_write(&f, data, block_size, &bytes_written) != FR_OK) { + f_close(&f); + return usb_comm_send_error("Couldn't write data to the file\n"); + } + if (bytes_written != block_size) { + f_close(&f); + return usb_comm_send_error("Couldn't write all required data to the file\n"); + } + remaining -= block_size; + } + + if (f_close(&f) != FR_OK) { + return usb_comm_send_error("Couldn't flush data to the file\n"); + } + + if (usb_comm_get_char() != '\0') { + return usb_comm_send_error("Invalid token at the end of data stream\n"); + } +} + +static usb_comm_command_t commands[] = { + { .id = "reboot", .op = command_reboot }, + { .id = "send-file", .op = command_send_file }, + { .id = NULL }, +}; + + +void usb_comm_poll (menu_t *menu) { + uint32_t header = usb_poll(); + + if (USBHEADER_GETTYPE(header) != DATATYPE_TEXT) { + usb_purge(); + return; + } + + if (USBHEADER_GETSIZE(header) > 0) { + char cmd_id[32]; + + if (usb_comm_read_string(cmd_id, sizeof(cmd_id), ' ')) { + usb_comm_send_error("Command id too long\n"); + } else { + usb_comm_command_t *cmd = commands; + + while (cmd->id != NULL) { + if (strcmp(cmd->id, cmd_id) == 0) { + cmd->op(menu); + break; + } + cmd++; + } + + usb_purge(); + + if (cmd->id == NULL) { + usb_comm_send_error("Unknown command\n"); + } + } + } +} diff --git a/src/menu/usb_comm.h b/src/menu/usb_comm.h new file mode 100644 index 00000000..c7f9a9ed --- /dev/null +++ b/src/menu/usb_comm.h @@ -0,0 +1,21 @@ +/** + * @file usb_comm.h + * @brief USB communication subsystem + * @ingroup menu + */ + +#ifndef USB_COMM_H__ +#define USB_COMM_H__ + + +#include "menu_state.h" + + +#ifndef NDEBUG +void usb_comm_poll (menu_t *menu); +#else +#define usb_comm_poll(menu) +#endif + + +#endif diff --git a/src/menu/views/browser.c b/src/menu/views/browser.c index 1e7e0811..7ed8d0b6 100644 --- a/src/menu/views/browser.c +++ b/src/menu/views/browser.c @@ -10,7 +10,7 @@ static const char *rom_extensions[] = { "z64", "n64", "v64", "rom", NULL }; -static const char *emulator_extensions[] = { "nes", "sfc", "smc", "gb", "gbc", NULL }; +static const char *emulator_extensions[] = { "nes", "sfc", "smc", "gb", "gbc", "sms", "gg", "sg", NULL }; static const char *save_extensions[] = { "sav", NULL }; // TODO: "eep", "sra", "srm", "fla" could be used if transfered from different flashcarts. static const char *image_extensions[] = { "png", NULL }; static const char *music_extensions[] = { "mp3", NULL }; diff --git a/src/menu/views/fault.c b/src/menu/views/fault.c index 95bf8ce3..e60b19f7 100644 --- a/src/menu/views/fault.c +++ b/src/menu/views/fault.c @@ -31,7 +31,7 @@ static void draw (menu_t *menu, surface_t *d) { const char *firmware_message = ( "Minimum supported versions:\n" "EverDrive-64: ?\n" - "64drive: ?\n" + "64drive: 2.05\n" "SC64: 2.16.0" ); diff --git a/src/menu/views/load_emulator.c b/src/menu/views/load_emulator.c index 8c8e6573..777bfa63 100644 --- a/src/menu/views/load_emulator.c +++ b/src/menu/views/load_emulator.c @@ -8,10 +8,28 @@ static const char *emu_nes_rom_extensions[] = { "nes", NULL }; static const char *emu_snes_rom_extensions[] = { "sfc", "smc", NULL }; static const char *emu_gameboy_rom_extensions[] = { "gb", NULL }; static const char *emu_gameboy_color_rom_extensions[] = { "gbc", NULL }; +static const char *emu_sega_8bit_rom_extensions[] = { "sms", "gg", "sg", NULL }; static bool load_pending; static cart_load_emu_type_t emu_type; +static char *format_emulator_name (cart_load_emu_type_t emulator_info) { + switch (emulator_info) { + case CART_LOAD_EMU_TYPE_NES: + return "Nintendo Famicom (NES)"; + case CART_LOAD_EMU_TYPE_SNES: + return "Nintendo Super Famicom (SNES)"; + case CART_LOAD_EMU_TYPE_GAMEBOY: + return "Nintendo GAMEBOY"; + case CART_LOAD_EMU_TYPE_GAMEBOY_COLOR: + return "Nintendo GAMEBOY Color"; + case CART_LOAD_EMU_TYPE_SEGA_GENERIC_8BIT: + return "SEGA 8bit system"; + default: + return "Unknown"; + } +} + static void process (menu_t *menu) { if (menu->actions.enter) { @@ -33,17 +51,22 @@ static void draw (menu_t *menu, surface_t *d) { component_main_text_draw( ALIGN_CENTER, VALIGN_TOP, - "Emulator information\n" - "THE EMULATOR\n" - "Rom Name\n" + "Load Emulated ROM\n" + ); + + component_main_text_draw( + ALIGN_LEFT, VALIGN_TOP, "\n" - "%s", + "\n" + "Emulated System: %s\n" + "Rom Name: %s", + format_emulator_name(emu_type), menu->browser.entry->name ); component_actions_bar_text_draw( ALIGN_LEFT, VALIGN_TOP, - "A: Load and run Emulator ROM\n" + "A: Load and run Emulated ROM\n" "B: Exit" ); } @@ -93,6 +116,8 @@ void view_load_emulator_init (menu_t *menu) { emu_type = CART_LOAD_EMU_TYPE_GAMEBOY; } else if (file_has_extensions(path_get(path), emu_gameboy_color_rom_extensions)) { emu_type = CART_LOAD_EMU_TYPE_GAMEBOY_COLOR; + } else if (file_has_extensions(path_get(path), emu_sega_8bit_rom_extensions)) { + emu_type = CART_LOAD_EMU_TYPE_SEGA_GENERIC_8BIT; } else { menu_show_error(menu, "Unsupported ROM"); } diff --git a/src/menu/views/load_rom.c b/src/menu/views/load_rom.c index d5828c1d..55d2c8b3 100644 --- a/src/menu/views/load_rom.c +++ b/src/menu/views/load_rom.c @@ -250,7 +250,7 @@ void view_load_rom_init (menu_t *menu) { rom_header = file_read_rom_header(path_get(path)); - boxart = component_boxart_init(rom_header.metadata.unique_identifier); + boxart = component_boxart_init(rom_header.metadata.media_type, rom_header.metadata.unique_identifier); path_free(path); } diff --git a/src/utils/fs.c b/src/utils/fs.c index 19c85814..648ecfdf 100644 --- a/src/utils/fs.c +++ b/src/utils/fs.c @@ -13,7 +13,7 @@ char *strip_sd_prefix (char *path) { char *found = strstr(path, prefix); if (found) { - return found + strlen(prefix); + return found + strlen(prefix) - 1; } return path; @@ -106,13 +106,13 @@ bool file_fill (char *path, uint8_t value) { return error; } -bool file_get_sectors (char *path, uint32_t *sectors, size_t entries) { +bool file_get_sectors (char *path, void (*callback) (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size)) { FATFS *fs; FIL fil; bool error = false; - for (int i = 0; i < entries; i++) { - sectors[i] = 0; + if (!callback) { + return true; } if (f_open(&fil, strip_sd_prefix(path), FA_READ) != FR_OK) { @@ -121,25 +121,22 @@ bool file_get_sectors (char *path, uint32_t *sectors, size_t entries) { fs = fil.obj.fs; - uint32_t file_sector_entries = (ALIGN(f_size(&fil), FS_SECTOR_SIZE) / FS_SECTOR_SIZE); - file_sector_entries = (file_sector_entries > entries) ? entries : file_sector_entries; + uint32_t sector_count = (ALIGN(f_size(&fil), FS_SECTOR_SIZE) / FS_SECTOR_SIZE); uint32_t cluster_sector = 0; - for (int file_sector = 0; file_sector < file_sector_entries; file_sector++) { - if ((file_sector % fs->csize) == 0) { - if ((f_lseek(&fil, (file_sector * FS_SECTOR_SIZE) + (FS_SECTOR_SIZE / 2))) != FR_OK) { - error = true; - break; - } - uint32_t cluster = fil.clust; - if (cluster >= fs->n_fatent) { - error = true; - break; - } - cluster_sector = (fs->database + ((LBA_t) (fs->csize) * (cluster - 2))); + for (int file_sector = 0; file_sector < sector_count; file_sector += fs->csize) { + if ((f_lseek(&fil, (file_sector * FS_SECTOR_SIZE) + (FS_SECTOR_SIZE / 2))) != FR_OK) { + error = true; + break; } - *sectors++ = (cluster_sector + (file_sector % fs->csize)); + uint32_t cluster = fil.clust; + if (cluster >= fs->n_fatent) { + error = true; + break; + } + cluster_sector = (fs->database + ((LBA_t) (fs->csize) * (cluster - 2))); + callback(sector_count, file_sector, cluster_sector, fs->csize); } if (f_close(&fil) != FR_OK) { diff --git a/src/utils/fs.h b/src/utils/fs.h index 25154437..7661ecb0 100644 --- a/src/utils/fs.h +++ b/src/utils/fs.h @@ -17,7 +17,7 @@ size_t file_get_size (char *path); bool file_delete (char *path); bool file_allocate (char *path, size_t size); bool file_fill (char *path, uint8_t value); -bool file_get_sectors (char *path, uint32_t *sectors, size_t entries); +bool file_get_sectors (char *path, void (*callback) (uint32_t sector_count, uint32_t file_sector, uint32_t cluster_sector, uint32_t cluster_size)); bool file_has_extensions (char *path, const char *extensions[]); bool directory_exists (char *path);