64drive support (#27)

<!--- Provide a general summary of your changes in the Title above -->

## Description
This PR implements 64drive support

## Motivation and Context
Goal of this PR is to support more devices
<!--- What does this sample do? What problem does it solve? -->
<!--- If it fixes/closes/resolves an open issue, please link to the
issue here -->

## How Has This Been Tested?
Not tested at all
<!-- (if applicable) -->
<!--- Please describe in detail how you tested your sample/changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->

## Screenshots
<!-- (if appropriate): -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [x] Improvement (non-breaking change that adds a new feature)
- [ ] Bug fix (fixes an issue)
- [ ] Breaking change (breaking change)
- [ ] Config and build (change in the configuration and build system,
has no impact on code or features)

## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
- [x] My code follows the code style of this project.
- [x] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.

<!--- It would be nice if you could sign off your contribution by
replacing the name with your GitHub user name and GitHub email contact.
-->
Signed-off-by: GITHUB_USER <GITHUB_USER_EMAIL>
This commit is contained in:
Mateusz Faderewski 2023-09-08 21:49:50 +02:00 committed by GitHub
parent b15be6467b
commit fc2f3aa42c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 570 additions and 5 deletions

View File

@ -16,6 +16,8 @@ SRCS = \
boot/boot.c \ boot/boot.c \
boot/crc32.c \ boot/crc32.c \
boot/reboot.S \ boot/reboot.S \
flashcart/64drive/64drive_ll.c \
flashcart/64drive/64drive.c \
flashcart/flashcart_utils.c \ flashcart/flashcart_utils.c \
flashcart/flashcart.c \ flashcart/flashcart.c \
flashcart/sc64/sc64_ll.c \ flashcart/sc64/sc64_ll.c \

View File

@ -23,10 +23,13 @@ An open source menu for N64 flashcarts.
## Getting started ## 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 ### SC64
Ensure the cart is running the latest [firmware](https://github.com/Polprzewodnikowy/SummerCart64/releases/latest). Ensure the cart has the latest [firmware](https://github.com/Polprzewodnikowy/SummerCart64/releases/latest) installed.
Download the `sc64menu.n64` ROM from the latest action run assets. Download the latest `sc64menu.n64` file from the releases page, then put it in the root directory of your SD card.
Add it to the root folder on your SD card.
### ED64 & ED64P ### ED64 & ED64P
Currently not supported, but there is an aim to do so. Currently not supported, but there is an aim to do so.

View File

@ -0,0 +1,271 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <fatfs/ff.h>
#include <libdragon.h>
#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;
}

View File

@ -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

View File

@ -0,0 +1,110 @@
#include <libdragon.h>
#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();
}

View File

@ -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 <stdbool.h>
#include <stdint.h>
/**
* @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

View File

@ -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.

View File

@ -8,6 +8,8 @@
#include "utils/utils.h" #include "utils/utils.h"
#include "flashcart.h" #include "flashcart.h"
#include "64drive/64drive.h"
#include "sc64/sc64.h" #include "sc64/sc64.h"
@ -22,6 +24,7 @@ static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = {
KiB(96), KiB(96),
KiB(128), KiB(128),
KiB(128), KiB(128),
KiB(128),
}; };
static uint32_t save_writeback_sectors[SAVE_WRITEBACK_MAX_SECTORS] __attribute__((aligned(8))); static uint32_t save_writeback_sectors[SAVE_WRITEBACK_MAX_SECTORS] __attribute__((aligned(8)));
@ -58,7 +61,7 @@ flashcart_error_t flashcart_init (void) {
int (* libcart_init) (void); int (* libcart_init) (void);
flashcart_t *(* get) (void); flashcart_t *(* get) (void);
} flashcarts[CART_MAX] = { } 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_SC, sc_init, sc64_get_flashcart }, // SC64
{ CART_EDX, edx_init, NULL }, // Series X EverDrive-64 { CART_EDX, edx_init, NULL }, // Series X EverDrive-64
{ CART_ED, ed_init, NULL }, // Original EverDrive-64 { CART_ED, ed_init, NULL }, // Original EverDrive-64

View File

@ -32,6 +32,7 @@ typedef enum {
FLASHCART_SAVE_TYPE_SRAM_BANKED, FLASHCART_SAVE_TYPE_SRAM_BANKED,
FLASHCART_SAVE_TYPE_SRAM_128K, FLASHCART_SAVE_TYPE_SRAM_128K,
FLASHCART_SAVE_TYPE_FLASHRAM, FLASHCART_SAVE_TYPE_FLASHRAM,
FLASHCART_SAVE_TYPE_FLASHRAM_PKST2,
__FLASHCART_SAVE_TYPE_END __FLASHCART_SAVE_TYPE_END
} flashcart_save_type_t; } flashcart_save_type_t;

View File

@ -311,6 +311,9 @@ static flashcart_error_t sc64_set_save_type (flashcart_save_type_t save_type) {
case FLASHCART_SAVE_TYPE_FLASHRAM: case FLASHCART_SAVE_TYPE_FLASHRAM:
type = SAVE_TYPE_FLASHRAM; type = SAVE_TYPE_FLASHRAM;
break; break;
case FLASHCART_SAVE_TYPE_FLASHRAM_PKST2:
type = SAVE_TYPE_FLASHRAM;
break;
default: default:
return FLASHCART_ERROR_ARGS; return FLASHCART_ERROR_ARGS;
} }

View File

@ -31,7 +31,7 @@ static void draw (menu_t *menu, surface_t *d) {
const char *firmware_message = ( const char *firmware_message = (
"Minimum supported versions:\n" "Minimum supported versions:\n"
"EverDrive-64: ?\n" "EverDrive-64: ?\n"
"64drive: ?\n" "64drive: 2.05\n"
"SC64: 2.16.0" "SC64: 2.16.0"
); );