diff --git a/Makefile b/Makefile index 7c0ec049..546da750 100644 --- a/Makefile +++ b/Makefile @@ -20,10 +20,12 @@ SRCS = \ boot/boot.c \ boot/cic.c \ boot/reboot.S \ - flashcart/64drive/64drive_ll.c \ - flashcart/64drive/64drive.c \ flashcart/flashcart_utils.c \ flashcart/flashcart.c \ + flashcart/64drive/64drive_ll.c \ + flashcart/64drive/64drive.c \ + flashcart/ed64/ed64_ll.c \ + flashcart/ed64/ed64.c \ flashcart/sc64/sc64_ll.c \ flashcart/sc64/sc64.c \ libs/libspng/spng/spng.c \ @@ -55,6 +57,7 @@ SRCS = \ menu/views/error.c \ menu/views/fault.c \ menu/views/file_info.c \ + menu/views/flashcart_info.c \ menu/views/image_viewer.c \ menu/views/text_viewer.c \ menu/views/load_disk.c \ diff --git a/README.md b/README.md index 0283dfe3..88ef1cb4 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,6 @@ An open source menu for N64 flashcarts. ### Video showcase (as of Oct 12 2023) - [![N64FlashcartMenu Showcase](http://img.youtube.com/vi/6CKImHTifDA/0.jpg)](http://www.youtube.com/watch?v=6CKImHTifDA "N64FlashcartMenu Showcase (Oct 12 2023)") @@ -61,6 +60,7 @@ Menu currently supports the following emulators and associated ROM file names: - **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) + ### Menu Settings The Menu creates a `config.ini` file in `sd:/menu/` which contains various settings that are used by the menu. Currently these are read-only (can be viewed in the menu by pressing `L` on the Joypad). @@ -70,6 +70,7 @@ If required, you can manually adjust the file on the SD card using your computer - 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. + ##### 64DD disk support For the ability to load and run 64DD disk images, you need to add the folder `/menu/64ddipl` on the SD card. Download and add the relevant ipl files and rename them before adding them to the folder: @@ -88,14 +89,28 @@ Note: to load an expansion disk (e.g. F-Zero X) browse to the N64 ROM and load i ### ED64 & ED64P Specific Currently not supported, but work is in progress (See [PR's](https://github.com/Polprzewodnikowy/N64FlashcartMenu/pulls)). -The aim is to replace [Altra64](https://github.com/networkfusion/altra64) and [ED64-UnofficialOS](https://github.com/n64-tools/ED64-UnofficialOS-binaries). +**WiP** The aim is to replace [Altra64](https://github.com/networkfusion/altra64) and [ED64-UnofficialOS](https://github.com/n64-tools/ED64-UnofficialOS-binaries). -# Developer documentation +#### ED64 +Download the `ED64.v64` ROM from the latest action run assets and place it in the `/ED64` folder. + +#### ED64P +Download the `ED64P.v64` ROM from the latest action run assets and place it in the `/ED64P` folder. + + +# Developer documentation You can use a dev container in VSCode to ease development. +## To Build: +`ms-vscode.makefile-tools` will help (installed automatically in dev container). +TODO: it does not yet work with `F5`: see https://devblogs.microsoft.com/cppblog/now-announcing-makefile-support-in-visual-studio-code/ +WORKAROUND: in the dev container terminal, use make directly, i.e.: `make all` +The ROMs can be found in the `output` directory. + + ## To deploy: ### SC64 * Download the deployer [here](https://github.com/Polprzewodnikowy/SummerCart64/releases/download/v2.18.0/sc64-deployer-windows-v2.18.0.zip) @@ -114,20 +129,24 @@ Then in the dev container, use `make run` or `make run-debug` #### From your host (Windows) OS - * Run `./localdeploy.bat` from the terminal Toggle the N64 power switch to load the ROM. -`ms-vscode.makefile-tools` will help (installed automatically in dev container). -TODO: it does not yet work with `F5`: see https://devblogs.microsoft.com/cppblog/now-announcing-makefile-support-in-visual-studio-code/ -WORKAROUND: in the dev container terminal, use make directly, i.e.: `make` -The ROM can be found in the `output` directory. -NOTE: a "release" version of the SC64 menu is called `sc64menu.n64` and can be created for when you want to add it directly to the SDCard. This is generated by running `make all` or running `make sc64`. +#### From the SD Card +A "release" version of the SC64 menu is called `sc64menu.n64` and can be created for when you want to add it directly to the root folder on the SDCard. This is generated by running `make all` or running `make sc64`. -### Others +### ED64 V3 over USB from your host (Windows) OS +* Download the UNFLoader [here](https://github.com/buu342/N64-UNFLoader/releases/download/v2.11/UNFLoader-Windows-x86.zip) +* Extract and place `UNFLoader.exe` in the `tools` directory. +* Ensure the current "official menu" is `V3.06`. +* Run the appropriate command from windows cmd prompt to ensure your current directory is the root project directory. +* Upload the ROM (e.g. `./tools/UNFLoader.exe -r ./output/OS64.v64`) + + +### Others (non USB) * Add the required file to the correct folder on your SD card. @@ -135,6 +154,7 @@ NOTE: a "release" version of the SC64 menu is called `sc64menu.n64` and can be c This repo currently uses the `unstable` branch as a submodule at a specific commit. To update to the latest version, use `git submodule update --remote ` from the terminal. + # Generate documentation Run `doxygen` from the dev container terminal. Make sure you fix the warnings before creating a PR! @@ -142,6 +162,7 @@ Generated documentation is located in `output/docs` folder and auto published to Once merged, they can be viewed [here](https://polprzewodnikowy.github.io/N64FlashcartMenu/) + # Open source software and licenses used - [libdragon](https://github.com/DragonMinded/libdragon) (UNLICENSE License) - [libspng](https://github.com/randy408/libspng) (BSD 2-Clause License) diff --git a/src/flashcart/64drive/64drive.c b/src/flashcart/64drive/64drive.c index 9b0b8b08..e416db06 100644 --- a/src/flashcart/64drive/64drive.c +++ b/src/flashcart/64drive/64drive.c @@ -275,6 +275,7 @@ static flashcart_t flashcart_d64 = { .load_64dd_disk = NULL, .set_save_type = d64_set_save_type, .set_save_writeback = d64_set_save_writeback, + .get_device_info = NULL, }; diff --git a/src/flashcart/ed64/README.md b/src/flashcart/ed64/README.md new file mode 100644 index 00000000..a8e90fd6 --- /dev/null +++ b/src/flashcart/ed64/README.md @@ -0,0 +1,12 @@ +This folder allows support for the ED64(P) flashcart when using V series hardware. + +Initially, this aims to work when loading the official ED64 OS menu, selecting the N64FlashcartMenu and then loading a ROM from there. This works around needing to load the correct firmware. To automate this, it is possible to set this menu to autoload (at least in the unofficial menu). i.e. You can set this ROM to auto load by placing a file called `autoexec.v64` in the `ED64` folder with the content of the file pointing to the path of this ROM. + +However, the current implementation is able to replace `/ED64/ED64.v64` and load ROM's using the `output/ED64.v64` ROM. + + +Notes: + +* `libcart` seems to use a different base register address `0x08040000`, but official implementation uses `0xA8040000` +* Loading saves is currently not available. + diff --git a/src/flashcart/ed64/ed64.c b/src/flashcart/ed64/ed64.c new file mode 100644 index 00000000..d8efd34b --- /dev/null +++ b/src/flashcart/ed64/ed64.c @@ -0,0 +1,265 @@ +#include +#include +#include + +#include +#include + +#include "utils/fs.h" +#include "utils/utils.h" + +#include "../flashcart_utils.h" +#include "ed64_ll.h" +#include "ed64.h" + +// static ed64_variant_t device_variant_id = ED64_VARIANT_UNKNOWN; +static ed64_save_type_t current_save_type = SAVE_TYPE_NONE; + +static flashcart_err_t ed64_init (void) { + + // FIXME: check FW compatible. + + // current_save_type = ed64_ll_get_save_type(); + // if (current_save_type < SAVE_TYPE_NONE || current_save_type > SAVE_TYPE_FLASHRAM) { + // return FLASHCART_ERR_INT; + // } + + // TODO: partly already done, see https://github.com/DragonMinded/libdragon/blob/4ec469d26b6dc4e308caf3d5b86c2b340b708bbd/src/libcart/cart.c#L1064 + + // ed64_ll_unlock_regs(); + + // ed64_ll_init(); // FIXME: this causes a crash! + // ed64_ll_reset_spx(); // only required if V2+ FW is updating! + + // ed64_ll_set_sram_bank(1); // Seemingly bank 0 (for SRAM 128k) is only supported on V3 and it is not the default. + + // If a V3 or X7 cart is detected (has a battery?), enable RTC, + // ed64_ll_gpio_mode_rtc(); + // otherwise: + // ed64_ll_gpio_mode_io(); + + // if (fpga_revision < SUPPORTED_FPGA_REVISION) { + // return FLASHCART_ERR_OUTDATED; + // } + + + return FLASHCART_OK; +} + +static flashcart_err_t ed64_deinit (void) { + + return FLASHCART_OK; +} + +static bool ed64_has_feature (flashcart_features_t feature) { + switch (feature) { + case FLASHCART_FEATURE_64DD: return false; + case FLASHCART_FEATURE_RTC: return false; // FIXME: if it is a V3 we should return true! + case FLASHCART_FEATURE_USB: return false; // FIXME: if it is a V3 we should return true (although virtual)! + default: return false; + } +} + +static flashcart_err_t ed64_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_ERR_LOAD; + } + + fix_file_size(&fil); + + size_t rom_size = f_size(&fil); + + // FIXME: if the cart is not V3 or X5 or X7, we probably need to - 128KiB + if (rom_size > MiB(64)) { + f_close(&fil); + return FLASHCART_ERR_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_ERR_LOAD; + } + if (progress) { + progress(f_tell(&fil) / (float) (f_size(&fil))); + } + } + if (f_tell(&fil) != rom_size) { + f_close(&fil); + return FLASHCART_ERR_LOAD; + } + + + if (f_close(&fil) != FR_OK) { + return FLASHCART_ERR_LOAD; + } + + return FLASHCART_OK; +} + +static flashcart_err_t ed64_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_ERR_LOAD; + } + + fix_file_size(&fil); + + size_t file_size = f_size(&fil) - file_offset; + + // FIXME: if the cart is not V3 or X5 or X7, we probably need to - 128KiB + if (file_size > (MiB(64) - rom_offset)) { + f_close(&fil); + return FLASHCART_ERR_ARGS; + } + + if (f_lseek(&fil, file_offset) != FR_OK) { + f_close(&fil); + return FLASHCART_ERR_LOAD; + } + + if (f_read(&fil, (void *) (ROM_ADDRESS + rom_offset), file_size, &br) != FR_OK) { + f_close(&fil); + return FLASHCART_ERR_LOAD; + } + if (br != file_size) { + f_close(&fil); + return FLASHCART_ERR_LOAD; + } + + if (f_close(&fil) != FR_OK) { + return FLASHCART_ERR_LOAD; + } + + return FLASHCART_OK; +} + +static flashcart_err_t ed64_load_save (char *save_path) { + // TODO: look at d64_load_save() and adjust. + uint8_t eeprom_contents[KiB(2)] __attribute__((aligned(8))); + void *address = NULL; + ed64_save_type_t current_save_type = ed64_ll_get_save_type(); + + FIL fil; + UINT br; + + if (f_open(&fil, strip_sd_prefix(save_path), FA_READ) != FR_OK) { + return FLASHCART_ERR_LOAD; + } + + size_t save_size = f_size(&fil); + + switch (current_save_type) { + case SAVE_TYPE_EEPROM_4K: + case SAVE_TYPE_EEPROM_16K: + address = eeprom_contents; + break; + case SAVE_TYPE_SRAM: + case SAVE_TYPE_SRAM_128K: + case SAVE_TYPE_FLASHRAM: + address = (void *) (SRAM_ADDRESS); + break; + case SAVE_TYPE_NONE: + default: + return FLASHCART_ERR_ARGS; + } + + if(f_read(&fil, address, save_size, &br) != FR_OK) { + f_close(&fil); + return FLASHCART_ERR_LOAD; + } + + if (current_save_type == SAVE_TYPE_EEPROM_4K || current_save_type == SAVE_TYPE_EEPROM_16K) { + if (current_save_type == SAVE_TYPE_EEPROM_16K) { // FIXME: could use save_size instead?! + eeprom_write_bytes(eeprom_contents, 0, KiB(2)); + } + else { + eeprom_write_bytes(eeprom_contents, 0, 512); + } + } + + if (f_close(&fil) != FR_OK) { + return FLASHCART_ERR_LOAD; + } + + if (br != save_size) { + return FLASHCART_ERR_LOAD; + } + + return FLASHCART_OK; +} + +static flashcart_err_t ed64_set_save_type (flashcart_save_type_t save_type) { + ed64_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: + case FLASHCART_SAVE_TYPE_SRAM_128K: + type = SAVE_TYPE_SRAM_128K; + break; + case FLASHCART_SAVE_TYPE_FLASHRAM_PKST2: + case FLASHCART_SAVE_TYPE_FLASHRAM: + type = SAVE_TYPE_FLASHRAM; + break; + default: + return FLASHCART_ERR_ARGS; + } + + ed64_ll_set_save_type(type); + + current_save_type = type; + + // FIXME: diagnose why this is not seemingly set! + + // TODO: set pseudo save writeback. + + return FLASHCART_OK; +} + +// static flashcart_info_t ed64_get_cart_info (void) { +// flashcart_info_t device_info; + +// return device_info; +// } + + +static flashcart_t flashcart_ed64 = { + .init = ed64_init, + .deinit = ed64_deinit, + .has_feature = ed64_has_feature, + .load_rom = ed64_load_rom, + .load_file = ed64_load_file, + .load_save = ed64_load_save, + .load_64dd_ipl = NULL, + .load_64dd_disk = NULL, + .set_save_type = ed64_set_save_type, + .set_save_writeback = NULL, + .get_device_info = NULL, //ed64_get_cart_info, +}; + + +flashcart_t *ed64_get_flashcart (void) { + return &flashcart_ed64; +} diff --git a/src/flashcart/ed64/ed64.h b/src/flashcart/ed64/ed64.h new file mode 100644 index 00000000..8680b592 --- /dev/null +++ b/src/flashcart/ed64/ed64.h @@ -0,0 +1,24 @@ +/** + * @file ed64.h + * @brief ED64 Flashcart Utilities + * @ingroup flashcart + */ + +#ifndef FLASHCART_ED64_H__ +#define FLASHCART_ED64_H__ + + +#include "../flashcart.h" + + +/** + * @addtogroup ed64 + * @{ + */ + +flashcart_t *ed64_get_flashcart (void); + +/** @} */ /* ed64 */ + + +#endif diff --git a/src/flashcart/ed64/ed64_ll.c b/src/flashcart/ed64/ed64_ll.c new file mode 100644 index 00000000..d2141c28 --- /dev/null +++ b/src/flashcart/ed64/ed64_ll.c @@ -0,0 +1,679 @@ +#include + +#include "../flashcart_utils.h" +#include "ed64_ll.h" + +// NOTE: these are a near replica of libcart cart.c defines and functions! + +#define PI_BASE_REG (0x04600000) +#define PI_STATUS_REG (PI_BASE_REG+0x10) + +#define PI_BSD_DOM1_LAT_REG (PI_BASE_REG+0x14) + +/* PI dom1 pulse width (R/W): [7:0] domain 1 device R/W strobe pulse width */ +#define PI_BSD_DOM1_PWD_REG (PI_BASE_REG+0x18) + +/* PI dom1 page size (R/W): [3:0] domain 1 device page size */ +#define PI_BSD_DOM1_PGS_REG (PI_BASE_REG+0x1C) /* page size */ + +/* PI dom1 release (R/W): [1:0] domain 1 device R/W release duration */ +#define PI_BSD_DOM1_RLS_REG (PI_BASE_REG+0x20) + +/* PI dom2 latency (R/W): [7:0] domain 2 device latency */ +#define PI_BSD_DOM2_LAT_REG (PI_BASE_REG+0x24) /* Domain 2 latency */ + +/* PI dom2 pulse width (R/W): [7:0] domain 2 device R/W strobe pulse width */ +#define PI_BSD_DOM2_PWD_REG (PI_BASE_REG+0x28) /* pulse width */ + +/* PI dom2 page size (R/W): [3:0] domain 2 device page size */ +#define PI_BSD_DOM2_PGS_REG (PI_BASE_REG+0x2C) /* page size */ + +/* PI dom2 release (R/W): [1:0] domain 2 device R/W release duration */ +#define PI_BSD_DOM2_RLS_REG (PI_BASE_REG+0x30) /* release duration */ + +#define IO_READ(addr) (*(volatile uint32_t *)PHYS_TO_K1(addr)) +#define IO_WRITE(addr,data) (*(volatile uint32_t *)PHYS_TO_K1(addr)=(uint32_t)(data)) + +#define PHYS_TO_K1(x) ((uint32_t)(x)|0xA0000000) /* physical to kseg1 */ + +#define CART_ABORT() {__cart_acs_rel(); return -1;} + +/* Temporary buffer aligned for DMA */ +//__attribute__((aligned(16))) static uint64_t __cart_buf[512/8]; + +static uint32_t __cart_dom1_rel; +static uint32_t __cart_dom2_rel; +uint32_t cart_dom1 = 0x8030FFFF; // FIXME: Added as should already be initialized. +uint32_t cart_dom2 = 0x80370404; // FIXME: Added as should already be initialized. + +//uint32_t cart_size; + +static void __cart_acs_get(void) +{ + /* Save PI BSD configuration and reconfigure */ + if (cart_dom1) + { + __cart_dom1_rel = + IO_READ(PI_BSD_DOM1_LAT_REG) << 0 | + IO_READ(PI_BSD_DOM1_PWD_REG) << 8 | + IO_READ(PI_BSD_DOM1_PGS_REG) << 16 | + IO_READ(PI_BSD_DOM1_RLS_REG) << 20 | + 1 << 31; + IO_WRITE(PI_BSD_DOM1_LAT_REG, cart_dom1 >> 0); + IO_WRITE(PI_BSD_DOM1_PWD_REG, cart_dom1 >> 8); + IO_WRITE(PI_BSD_DOM1_PGS_REG, cart_dom1 >> 16); + IO_WRITE(PI_BSD_DOM1_RLS_REG, cart_dom1 >> 20); + } + if (cart_dom2) + { + __cart_dom2_rel = + IO_READ(PI_BSD_DOM2_LAT_REG) << 0 | + IO_READ(PI_BSD_DOM2_PWD_REG) << 8 | + IO_READ(PI_BSD_DOM2_PGS_REG) << 16 | + IO_READ(PI_BSD_DOM2_RLS_REG) << 20 | + 1 << 31; + IO_WRITE(PI_BSD_DOM2_LAT_REG, cart_dom2 >> 0); + IO_WRITE(PI_BSD_DOM2_PWD_REG, cart_dom2 >> 8); + IO_WRITE(PI_BSD_DOM2_PGS_REG, cart_dom2 >> 16); + IO_WRITE(PI_BSD_DOM2_RLS_REG, cart_dom2 >> 20); + } +} + +static void __cart_acs_rel(void) +{ + /* Restore PI BSD configuration */ + if (__cart_dom1_rel) + { + IO_WRITE(PI_BSD_DOM1_LAT_REG, __cart_dom1_rel >> 0); + IO_WRITE(PI_BSD_DOM1_PWD_REG, __cart_dom1_rel >> 8); + IO_WRITE(PI_BSD_DOM1_PGS_REG, __cart_dom1_rel >> 16); + IO_WRITE(PI_BSD_DOM1_RLS_REG, __cart_dom1_rel >> 20); + __cart_dom1_rel = 0; + } + if (__cart_dom2_rel) + { + IO_WRITE(PI_BSD_DOM2_LAT_REG, __cart_dom2_rel >> 0); + IO_WRITE(PI_BSD_DOM2_PWD_REG, __cart_dom2_rel >> 8); + IO_WRITE(PI_BSD_DOM2_PGS_REG, __cart_dom2_rel >> 16); + IO_WRITE(PI_BSD_DOM2_RLS_REG, __cart_dom2_rel >> 20); + __cart_dom2_rel = 0; + } +} + +static void __cart_dma_rd(void *dram, uint32_t cart, uint32_t size) +{ + data_cache_hit_writeback_invalidate(dram, size); + dma_read_raw_async(dram, cart, size); + dma_wait(); +} + +static void __cart_dma_wr(const void *dram, uint32_t cart, uint32_t size) +{ + data_cache_hit_writeback((void *)dram, size); + dma_write_raw_async(dram, cart, size); + dma_wait(); +} + +/* ED64 registers base address */ +#define ED_BASE_REG (0xA8040000) // FIXME: Or should it be `0x08040000` like libcart? + +/* ED64 config register */ +#define ED_CFG_REG (ED_BASE_REG+0x00) +/* ED64 status register */ +#define ED_STATUS_REG (ED_BASE_REG+0x04) +/* ED64 DMA Length register */ +#define ED_DMA_LEN_REG (ED_BASE_REG+0x08) +/* ED64 DMA Address register */ +#define ED_DMA_ADDR_REG (ED_BASE_REG+0x0C) +/* ED64 Message/Data register */ +#define ED_MSG_REG (ED_BASE_REG+0x10) +/* ED64 DMA Config register */ +#define ED_DMA_CFG_REG (ED_BASE_REG+0x14) +/* ED64 SPI register */ +#define ED_SPI_REG (ED_BASE_REG+0x18) +/* ED64 SPI Config register */ +#define ED_SPI_CFG_REG (ED_BASE_REG+0x1C) +/* ED64 Unlock register */ +#define ED_KEY_REG (ED_BASE_REG+0x20) +/* ED64 Save Config register */ +#define ED_SAV_CFG_REG (ED_BASE_REG+0x24) +#define ED_SEC_REG (ED_BASE_REG+0x28) /* Sectors?? */ +#define ED_VER_REG (ED_BASE_REG+0x2C) /* Hardware series version + firmware rev */ +/* ED64 GPIO register */ +#define ED_GPIO_REG (ED_BASE_REG+0x30) + +// Unknown regs (if any) + +/* ED64 Config Count register */ +#define ED_CFG_CNT_REG (ED_BASE_REG+0x40) +/* ED64 Config Data register */ +#define ED_CFG_DAT_REG (ED_BASE_REG+0x44) +#define ED_CPLD_MSG_REG (ED_BASE_REG+0x48) // NOTE: was `ED_MAX_MSG_REG` /* Altera (Intel) MAX */ +#define ED_CPLD_VER_REG (ED_BASE_REG+0x4C) // NOTE: Was `ED_CRC_REG` +/* ED64 FlashRAM Address register (V3 series only) */ +#define ED_FLASHRAM_ADDR_REG (ED_BASE_REG+0x50) +/* ED64 FlashRAM Message/Data register (V3 series only) */ +#define ED_FLASHRAM_MSG_REG (ED_BASE_REG+0x54) + +// END NOTE. + + +/* Locks the ED64 registers */ +#define ED64_KEY_LOCK 0x0000 +/* Unlocks the ED64 registers */ +#define ED64_KEY_UNLOCK 0x1234 + +// #define ED_STATE_DMA_BUSY (1 << 0) +// #define ED_STATE_DMA_TOUT (1 << 1) +// #define ED_STATE_TXE (1 << 2) +// #define ED_STATE_RXF (1 << 3) +// #define ED_STATE_SPI (1 << 4) +typedef enum { + STATE_DMA_BUSY = 0x01, + STATE_DMA_TOUT = 0x02, + STATE_USB_TXE = 0x04, + STATE_USB_RXF = 0x08, + STATE_SPI = 0x10, + +} ed64_dma_state_t; + +#define ED_DMA_SD_TO_RAM 1 // SD Card +#define ED_DMA_RAM_TO_SD 2 // SD Card +#define ED_DMA_FIFO_TO_RAM 3 // USB +#define ED_DMA_RAM_TO_FIFO 4 // USB + +// #define ED_SAV_EEP_OFF (0 << 0) +// #define ED_SAV_EEP_ON (1 << 0) +// #define ED_SAV_SRM_OFF (0 << 1) +// #define ED_SAV_SRM_ON (1 << 1) +// #define ED_SAV_EEP_SMALL (0 << 2) +// #define ED_SAV_EEP_BIG (1 << 2) +// #define ED_SAV_SRM_SMALL (0 << 3) +// #define ED_SAV_SRM_BIG (1 << 3) +/* Enables the EEP save type */ +#define SAV_EEP_ON 1 +/* Enables the SRAM save type */ +#define SAV_SRM_ON 2 +/* Sets the EEP save type to 16K */ +#define SAV_EEP_BIG 4 +/* Sets the SRAM save type to 128K */ +#define SAV_SRM_BIG 8 + +#define SAV_RAM_BANK 128 +#define SAV_RAM_BANK_APPLY 32768 + + +// #define ED_CFG_SDRAM_OFF (0 << 0) +// #define ED_CFG_SDRAM_ON (1 << 0) +// #define ED_CFG_BYTESWAP_OFF (0 << 1) +// #define ED_CFG_BYTESWAP_ON (1 << 1) +typedef enum { + ED_CFG_SDRAM_ON = 0x0001, + ED_CFG_BYTESWAP_ON = 0x0002, + ED_CFG_WRITE_MODE_ON = 0x0004, + ED_CFG_WRITE_ADDR_MASK = 0x0008, + ED_CFG_RTC_ON = 0x0020, //32, + ED_CFG_GPIO_ON = 0x0060, //96, + ED_CFG_64DD_ON = 0x0100, //256, + ED_CFG_64DD_WRITE_ENABLED = 0x0200, //512, +} ed64_config_t; + + +#define FPGA_FW_DATA_SKIP_FW_INIT (1 << 8) +#define FPGA_FW_DATA_SKIP_TV_INIT (1 << 9) +#define FPGA_FW_DATA_TV_TYPE1 (1 << 10) +#define FPGA_FW_DATA_TV_TYPE2 (1 << 11) +#define FPGA_FW_DATA_SKIP_SD_INIT (1 << 12) +#define FPGA_FW_DATA_SD_TYPE (1 << 13) +#define FPGA_FW_DATA_HOT_START (1 << 14) + + +// static uint16_t spi_cfg; +uint8_t ed64_ll_sram_bank; +ed64_save_type_t ed64_ll_save_type; + +// #define REG_LAT 0x04 +// #define REG_PWD 0x04 + +// #define ROM_LAT 0x40 +// #define ROM_PWD 0x12 + +/* register functions (dependent on flashcart version) */ + +/* register functions for V2 & V2.5 (These carts do not support FLASHRAM)*/ +/* The end of SDRAM is used for SRAM or FlashRAM save types */ +// void ed64_ll_io_reg_v2(uint32_t address, uint32_t data) { + +// *(volatile uint32_t *) (ROM_ADDRESS); +// *(volatile uint32_t *) (ROM_ADDRESS + address) = data; +// } + +// /* register functions for V3 */ +// void ed64_ll_io_reg_v3(uint32_t address, uint16_t data) { + +// io_write(ED_FLASHRAM_ADDR_REG, address); +// io_write(ED_FLASHRAM_MSG_REG, data); +// } + +/* initialize functions (dependent on flashcart version) */ + +// /* Initilize V2 & 2.5 cart */ +// void ed64_ll_init_v2() { + +// uint8_t buff[512]; +// uint16_t cfg = io_read(ED_CFG_REG); + +// io_write(ED_CFG_REG, 0); +// ed64_ll_io_reg_v2(0xaa / 4 * 4, 0x00980098); +// ed64_ll_dma_read_rom(buff, 0, 1); +// ed64_ll_io_reg_v2(0xaa / 4 * 4, 0x00f000f0); +// ed64_ll_dma_read_rom(buff, 0, 1); +// io_write(ED_CFG_REG, cfg); + +// } + +// /* Initilize V3 cart */ +// void ed64_ll_init_v3() { + +// uint8_t buff[1024]; +// uint16_t cfg = ed64_ll_reg_read(ED_CFG_REG); + +// io_write(ED_CFG_REG, 0); +// io_write(ED_CFG_CNT_REG, 161); +// ed64_ll_io_reg_v3(0x55, 0x98); +// ed64_ll_dma_read_rom(buff, 0, 2); +// ed64_ll_io_reg_v3(0x55, 0xF0); +// ed64_ll_dma_read_rom(buff, 0, 2); +// ed64_ll_dma_read_rom(buff, 1024, 2); +// ed64_ll_dma_read_rom(buff, 1024 + 256 - 2, 2); +// io_write(ED_CFG_CNT_REG, 1); + +// io_write(ED_CFG_REG, cfg); +// } + +// /* Initialize cart */ +// int ed64_ll_init() { + +// uint16_t firmware_ver; +// uint16_t firmware_msg; +// uint8_t cold_start; + +// // TODO: take into account libCart! +// io_write(PI_BSD_DOM2_LAT_REG, REG_LAT); +// io_write(PI_BSD_DOM2_PWD_REG, REG_PWD); +// dma_wait(); // Make sure the libdragon Async io methods have been performed. + + +// ed64_ll_reg_write(REG_KEY, ED64_KEY_UNLOCK); +// ed64_ll_reg_write(REG_CFG, 0x0000); + + +// firmware_msg = ed64_ll_reg_read(REG_FPGA_FW_DATA); +// cold_start = (firmware_msg & FPGA_FW_DATA_HOT_START) == 0 ? 1 : 0; +// if (cold_start) { +// firmware_msg |= FPGA_FW_DATA_HOT_START; +// ed64_ll_reg_write(REG_FPGA_FW_DATA, firmware_msg); +// } + +// firmware_ver = ed64_ll_reg_read(REG_FPGA_FW_VER); +// if ((firmware_ver & 0xf000) >= 0x2000) { +// ed64_ll_init_v3(); +// } else { +// ed64_ll_init_v2(); +// } + + + +// //spi_cfg = SPI_CFG_SS | BI_SPI_SPD_LO; +// ed64_ll_reg_write(REG_CFG, ED_CFG_SDRAM_ON); +// //ed64_ll_reg_write(REG_SPI_CFG, spi_cfg); +// ed64_ll_save_type = SAVE_TYPE_NONE; + + +// return cold_start; +// } + +// void ed64_ll_reset_spx() { + +// uint16_t cfg = io_read(ED_CFG_REG); + +// io_write(ED_CFG_REG, 0x8000); +// wait_ms(100); +// io_write(ED_CFG_REG, cfg); +// wait_ms(100); +// } + + +/* Used for USB and SPI functions */ +uint8_t ed64_ll_dma_busy() { + + while ((io_read(ED_STATUS_REG) & STATE_DMA_BUSY) != 0); + return io_read(ED_STATUS_REG) & STATE_DMA_TOUT; +} + + +/* USB functions */ + +/* USB read is busy */ +uint8_t ed64_ll_usb_read_busy() { + + return io_read(ED_STATUS_REG) & STATE_USB_RXF; +} + +/* USB write is busy */ +uint8_t ed64_ll_usb_write_busy() { + + return io_read(ED_STATUS_REG) & STATE_USB_TXE; +} + +/* USB read */ +uint8_t ed64_ll_usb_read(uint32_t start_address, uint32_t slen) { + + start_address /= 4; + while (ed64_ll_usb_read_busy() != 0); + + io_write(ED_DMA_LEN_REG, slen - 1); + io_write(ED_DMA_ADDR_REG, start_address); + io_write(ED_DMA_CFG_REG, ED_DMA_FIFO_TO_RAM); + + if (ed64_ll_dma_busy() != 0)return ED_USB_ERR_FIFO_TIMEOUT; + + return 0; +} + +/* USB write */ +uint8_t ed64_ll_usb_write(uint32_t start_address, uint32_t slen) { + + start_address /= 4; + while (ed64_ll_usb_write_busy() != 0); + + io_write(ED_DMA_LEN_REG, slen - 1); + io_write(ED_DMA_ADDR_REG, start_address); + io_write(ED_DMA_CFG_REG, ED_DMA_RAM_TO_FIFO); + + if (ed64_ll_dma_busy() != 0)return ED_USB_ERR_FIFO_TIMEOUT; + + return 0; +} + + +ed64_save_type_t ed64_ll_get_save_type() { + + return ed64_ll_save_type; +} + +void ed64_ll_set_save_type(ed64_save_type_t type) { + + uint16_t save_cfg = 0; + uint8_t enable_eeprom = false; + uint8_t eeprom_size_16k = false; + uint8_t enable_sram = false; + uint8_t ram_size_large = false; + ed64_ll_save_type = type; // So we can retrive it later with `ed64_ll_get_save_type` + uint8_t ram_bank = ed64_ll_sram_bank; + + + switch (type) { + case SAVE_TYPE_EEPROM_16K: + enable_eeprom = true; + eeprom_size_16k = true; + break; + case SAVE_TYPE_EEPROM_4K: + enable_eeprom = true; + break; + case SAVE_TYPE_SRAM: + enable_sram = true; + break; + case SAVE_TYPE_SRAM_128K: + enable_sram = true; + ram_size_large = true; + break; + case SAVE_TYPE_FLASHRAM: + enable_sram = false; + ram_size_large = true; + break; + default: + enable_sram = false; + ram_size_large = false; + ram_bank = 1; + break; + } + + if (enable_eeprom)save_cfg |= SAV_EEP_ON; + if (enable_sram)save_cfg |= SAV_SRM_ON; + if (eeprom_size_16k)save_cfg |= SAV_EEP_BIG; + if (ram_size_large)save_cfg |= SAV_SRM_BIG; + if (ram_bank)save_cfg |= SAV_RAM_BANK; + save_cfg |= SAV_RAM_BANK_APPLY; + + //__cart_acs_get(); + io_write(ED_SAV_CFG_REG, save_cfg); + //__cart_acs_rel(); +} + +void ed64_ll_set_sram_bank(uint8_t bank) { + + ed64_ll_sram_bank = bank == 0 ? 0 : 1; + +} + +// /* reads metadata related to the manufacturing date and cart capabilities */ +// ed64_cart_metadata_t ed64_ll_read_cart_metadata(void *dest) { + +// ed64_cart_metadata_t cart_info; +// __cart_acs_get(); + +// uint16_t cfg = io_read(ED_CFG_REG); + +// cfg &= ~ED_CFG_SDRAM_ON; +// io_write(ED_CFG_REG, cfg); + +// ed64_ll_dma_read_rom(dest, 0, 1); + +// // cart_info.production_date = (buff[0x38] << 8) | buff[0x39]; +// // cart_info.production_time = (buff[0x3A] << 8) | buff[0x3B]; +// // cart_info.serial_number = (buff[0x3C] << 8) | buff[0x3D]; +// // cart_info.cic_6105_compatible = buff[0x40] == 0x03 ? true : false; // CIC_6105 : CIC_6102; + + +// cfg |= ED_CFG_SDRAM_ON; +// io_write(ED_CFG_REG, cfg); +// __cart_acs_rel(); +// return cart_info; +// } + +void ed64_ll_dma_read_rom(void *ram, uint32_t address_offset, uint32_t length) { + //__cart_acs_get(); + __cart_dma_rd(ram, ROM_ADDRESS + address_offset, length); + //__cart_acs_rel(); + +} + +void ed64_ll_dma_write_rom(void *ram, uint32_t address_offset, uint32_t length) { + //__cart_acs_get(); + __cart_dma_wr(ram, ROM_ADDRESS + address_offset, length); + //__cart_acs_rel(); + +} + +/* Read from SRAM over DMA */ +void ed64_ll_dma_read_sram(void *ram, uint32_t address_offset, uint32_t length) { + //__cart_acs_get(); + + // Read SRAM + __cart_dma_rd(ram, SRAM_ADDRESS + address_offset, length); + + //__cart_acs_rel(); +} + +/* Write to SRAM over DMA */ +void ed64_ll_dma_write_sram(void *ram, uint32_t address_offset, uint32_t length) { + //__cart_acs_get(); + + // Write SRAM + __cart_dma_wr(ram, SRAM_ADDRESS + address_offset, length); + + //__cart_acs_rel(); +} + +/** @brief Get the current FPGA version */ +uint16_t ed64_ll_get_fpga_version() { + //__cart_acs_get(); + return io_read(ED_VER_REG); + //__cart_acs_rel(); +} + +/** @brief Get the current CPLD version */ +uint16_t ed64_ll_get_cpld_version() { + //__cart_acs_get(); + uint16_t cpld_version; + uint16_t cfg = io_read(ED_CFG_REG); + + io_write(ED_CFG_REG, 0); + cpld_version = io_read(ED_CPLD_VER_REG); + io_write(ED_CFG_REG, cfg); + //__cart_acs_rel(); + return cpld_version; +} + +/** @brief Load the specified FPGA firmware */ +void ed64_ll_load_firmware(uint8_t *firmware) { + //__cart_acs_get(); + uint32_t i = 0; + uint16_t f_ctr = 0; + uint16_t cfg = io_read(ED_CFG_REG); + + cfg &= ~ED_CFG_SDRAM_ON; + io_write(ED_CFG_REG, cfg); + wait_ms(10); + io_write(ED_CFG_CNT_REG, 0); + wait_ms(10); + io_write(ED_CFG_CNT_REG, 1); + wait_ms(10); + + for (;;) { + io_write(ED_CFG_DAT_REG, *(uint16_t *) & firmware[i]); + while ((io_read(ED_CFG_CNT_REG) & 8) != 0); + + f_ctr = firmware[i++] == 0xff ? f_ctr + 1 : 0; + if (f_ctr >= 47) break; + f_ctr = firmware[i++] == 0xff ? f_ctr + 1 : 0; + if (f_ctr >= 47) break; + } + + while ((io_read(ED_CFG_CNT_REG) & 4) == 0) { + io_write(ED_CFG_DAT_REG, 0xffff); + while ((io_read(ED_CFG_CNT_REG) & 8) != 0); + } + + wait_ms(20); + //__cart_acs_rel(); +} + + +void ed64_ll_lock_regs() { + //__cart_acs_get(); + io_write(ED_KEY_REG, ED64_KEY_LOCK); + //__cart_acs_rel(); +} + +void ed64_ll_unlock_regs() { + //__cart_acs_get(); + io_write(ED_KEY_REG, ED64_KEY_UNLOCK); + //__cart_acs_rel(); +} + + +/* GPIO functions */ + +/** @brief Set GPIO mode RTC */ +void ed64_ll_gpio_mode_rtc() { + //__cart_acs_get(); + uint16_t cfg = io_read(ED_CFG_REG); + cfg &= ~ED_CFG_GPIO_ON; + cfg |= ED_CFG_RTC_ON; + io_write(ED_CFG_REG, cfg); + //__cart_acs_rel(); +} + +/** @brief Set GPIO mode ON */ +void ed64_ll_gpio_mode_io() { + //__cart_acs_get(); + uint16_t cfg = io_read(ED_CFG_REG); + cfg |= ED_CFG_GPIO_ON; + io_write(ED_CFG_REG, cfg); + //__cart_acs_rel(); +} + +/** @brief Set GPIO mode OFF */ +void ed64_ll_gpio_mode_off() { + //__cart_acs_get(); + uint16_t cfg = io_read(ED_CFG_REG); + cfg &= ~ED_CFG_GPIO_ON; + io_write(ED_CFG_REG, cfg); + //__cart_acs_rel(); +} + +/** @brief Set GPIO mode write */ +void ed64_ll_gpio_write(uint8_t data) { + //__cart_acs_get(); + io_write(ED_GPIO_REG, data); + //__cart_acs_rel(); +} + +/** @brief Set GPIO mode read */ +uint8_t ed64_ll_gpio_read() { + __cart_acs_get(); + uint8_t gpio_state = io_read(ED_GPIO_REG); + __cart_acs_rel(); + return gpio_state; +} + + +/* 64DD cart conversion save functions */ + +/* Set 64DD ON and Enabled?! */ +void ed64_ll_64dd_ram_oe() { + //__cart_acs_get(); + uint16_t cfg = io_read(ED_CFG_REG); + cfg &= ~ED_CFG_64DD_WRITE_ENABLED; + cfg |= ED_CFG_64DD_ON; + io_write(ED_CFG_REG, cfg); + //__cart_acs_rel(); +} + +/* Set 64DD Write Enable?? */ +void ed64_ll_64dd_ram_we() { + //__cart_acs_get(); + uint16_t cfg = io_read(ED_CFG_REG); + cfg |= ED_CFG_64DD_ON | ED_CFG_64DD_WRITE_ENABLED; + io_write(ED_CFG_REG, cfg); + //__cart_acs_rel(); +} + +/* Set 64DD Disabled?? */ +void ed64_ll_64dd_ram_off() { + //__cart_acs_get(); + uint16_t cfg = io_read(ED_CFG_REG); + cfg &= ~(ED_CFG_64DD_ON | ED_CFG_64DD_WRITE_ENABLED); + io_write(ED_CFG_REG, cfg); + //__cart_acs_rel(); +} + +/* 64DD Save RAM Clear */ +void ed64_ll_64dd_ram_clear() { + //__cart_acs_get(); + uint16_t cfg = io_read(ED_CFG_REG); + cfg |= ED_CFG_64DD_WRITE_ENABLED; + cfg &= ~ED_CFG_64DD_ON; + io_write(ED_CFG_REG, cfg); + wait_ms(100); + //__cart_acs_rel(); +} + +/** @brief Check if 64DD Cart conversions allowed on this cart?! */ +uint8_t ed64_ll_get_64dd_ram_supported() { + //__cart_acs_get(); + return (io_read(ED_STATUS_REG) >> 15) & 1; + //__cart_acs_rel(); +} diff --git a/src/flashcart/ed64/ed64_ll.h b/src/flashcart/ed64/ed64_ll.h new file mode 100644 index 00000000..7dc490bc --- /dev/null +++ b/src/flashcart/ed64/ed64_ll.h @@ -0,0 +1,105 @@ +/** + * @file ed64_ll.h + * @brief ED64 V-Series flashcart low level access + * @ingroup flashcart + */ + +#ifndef FLASHCART_ED64_LL_H__ +#define FLASHCART_ED64_LL_H__ + +#include +#include +#include + +/** + * @addtogroup ed64 + * @{ + */ + + +typedef enum { + SAVE_TYPE_NONE = 0, + SAVE_TYPE_SRAM = 1, + SAVE_TYPE_SRAM_128K = 2, + SAVE_TYPE_EEPROM_4K = 3, + SAVE_TYPE_EEPROM_16K = 4, + SAVE_TYPE_FLASHRAM = 5, + SAVE_TYPE_CPAK = 8, + SAVE_TYPE_DD64 = 16, +} ed64_save_type_t; + + +typedef struct { + uint16_t production_date; /* The Date that the ED64 was manufactured */ + uint16_t production_time; /* The Time that the ED64 was manufactured */ + uint16_t serial_number; /* The ED64 serial number (unique for each ED64) */ + bool cic_6105_compatible; /* This returns whether the the cart CIC is 6105 compatible + as many were produced before Ultra CIC existed! + Carts that are not compatible will be unable to run certain ROMs */ +} ed64_cart_metadata_t; + +/* ED64 save location base address */ +#define SRAM_ADDRESS (0xA8000000) +/* ED64 ROM location base address */ +#define ROM_ADDRESS (0xB0000000) + +#define ED_USB_ERR_FIFO_TIMEOUT 0x90 // FIXME: We can return a better error code?! +// #define ED_ERROR_MMC_TIMEOUT 0x91 + +// #define BOOT_UPD_ERR_WRONG_SIZE 0x95 +// #define BOOT_UPD_ERR_HDR 0x96 +// #define BOOT_UPD_ERR_CMP 0x97 +// #define BOOT_UPD_ERR_CIC_DTCT 0x98 + + +/* Initialization functions */ +// int ed64_ll_init(); +// void ed64_ll_init_v2(); +// void ed64_ll_init_v3(); + +/* Device information functions */ +uint16_t ed64_ll_get_fpga_version(); +uint16_t ed64_ll_get_cpld_version(); + +/* Firmware update functions */ +// void ed64_ll_load_firmware(uint8_t *firmware); + +// /* USB functions */ +// uint8_t ed64_ll_usb_read_busy(); +// uint8_t ed64_ll_usb_read(uint32_t start_address, uint32_t slen); +// uint8_t ed64_ll_usb_write(uint32_t start_address, uint32_t slen); + +/* Save functions */ +void ed64_ll_set_sram_bank(uint8_t bank); +ed64_save_type_t ed64_ll_get_save_type(); +void ed64_ll_set_save_type(ed64_save_type_t type); + +// /* reads metadata related to the production date and cart capabilities */ +// ed64_cart_metadata_t ed64_ll_read_cart_metadata(void *dest); + +void ed64_ll_dma_read_rom(void *ram, uint32_t address_offset, uint32_t length); +void ed64_ll_dma_write_rom(void *ram, uint32_t address_offset, uint32_t length); + +void ed64_ll_lock_regs(); +void ed64_ll_unlock_regs(); + +// void ed64_ll_reset_spx(); + +/* GPIO functions */ +void ed64_ll_gpio_mode_rtc(); +void ed64_ll_gpio_mode_io(); +void ed64_ll_gpio_mode_off(); +uint8_t ed64_ll_gpio_read(); + +// /* 64DD cart conversion save functions */ +void ed64_ll_64dd_ram_oe(); +void ed64_ll_64dd_ram_we(); +void ed64_ll_64dd_ram_off(); +void ed64_ll_64dd_ram_clear(); +uint8_t ed64_ll_get_64dd_ram_supported(); + +/** @} */ /* ed64 */ + +#endif + + diff --git a/src/flashcart/flashcart.c b/src/flashcart/flashcart.c index bd2e4163..56f6e828 100644 --- a/src/flashcart/flashcart.c +++ b/src/flashcart/flashcart.c @@ -11,6 +11,7 @@ #include "64drive/64drive.h" #include "sc64/sc64.h" +#include "ed64/ed64.h" #define SAVE_WRITEBACK_MAX_SECTORS (256) @@ -56,6 +57,7 @@ static flashcart_t *flashcart = &((flashcart_t) { .load_save = NULL, .set_save_type = NULL, .set_save_writeback = NULL, + .get_device_info = NULL, }); #ifdef NDEBUG @@ -96,7 +98,10 @@ flashcart_err_t flashcart_init (void) { break; case CART_EDX: // Series X EverDrive-64 - case CART_ED: // Original EverDrive-64 + // break; // FIXME: Commented out as required to fall through due to need of F/W 3.06 and UNFLoader. + // but, this is possibily causing the issues related to saves etc. + case CART_ED: // Original EverDrive-64 (V-Series & Clones) + flashcart = ed64_get_flashcart(); break; case CART_SC: // SummerCart64 diff --git a/src/flashcart/flashcart.h b/src/flashcart/flashcart.h index 001516bb..70c3bad2 100644 --- a/src/flashcart/flashcart.h +++ b/src/flashcart/flashcart.h @@ -76,6 +76,8 @@ typedef struct { flashcart_err_t (*set_save_type) (flashcart_save_type_t save_type); /** @brief The flashcart set save writeback function */ flashcart_err_t (*set_save_writeback) (uint32_t *sectors); + /** @brief The flashcart device information function */ + flashcart_err_t (*get_device_info) (void); } flashcart_t; diff --git a/src/flashcart/sc64/sc64.c b/src/flashcart/sc64/sc64.c index b7febf16..eaab7dba 100644 --- a/src/flashcart/sc64/sc64.c +++ b/src/flashcart/sc64/sc64.c @@ -581,6 +581,7 @@ static flashcart_t flashcart_sc64 = { .load_64dd_disk = sc64_load_64dd_disk, .set_save_type = sc64_set_save_type, .set_save_writeback = sc64_set_save_writeback, + .get_device_info = NULL, }; diff --git a/src/menu/views/browser.c b/src/menu/views/browser.c index 01cdb41b..92fbe75e 100644 --- a/src/menu/views/browser.c +++ b/src/menu/views/browser.c @@ -247,6 +247,10 @@ static void set_menu_next_mode (menu_t *menu, void *arg) { menu->next_mode = next_mode; } +static void show_flashcart_info (menu_t *menu) { + menu->next_mode = MENU_MODE_FLASHCART; +} + static component_context_menu_t settings_context_menu = { .list = { { .text = "Edit settings", .action = set_menu_next_mode, .arg = (void *) (MENU_MODE_SETTINGS_EDITOR) },