Merge remote-tracking branch 'upstream/main' into ed64-basic

This commit is contained in:
Robin Jones 2023-10-10 20:03:54 +01:00
commit 7abb7e6e8b
29 changed files with 924 additions and 101 deletions

View File

@ -68,12 +68,13 @@ jobs:
name: SC64 name: SC64
path: ./output/sc64menu.n64 path: ./output/sc64menu.n64
- name: Delete rolling-release tag and release # - name: Delete rolling-release tag and release
uses: dev-drprasad/delete-tag-and-release@v1.0 # uses: dev-drprasad/delete-tag-and-release@v1.0
if: github.ref == 'refs/heads/main' # if: github.ref == 'refs/heads/main'
with: # with:
github_token: ${{ secrets.GITHUB_TOKEN }} # github_token: ${{ secrets.GITHUB_TOKEN }}
tag_name: rolling-release # tag_name: rolling-release
# continue-on-error: true
- name: Upload rolling release - name: Upload rolling release
uses: softprops/action-gh-release@v0.1.15 uses: softprops/action-gh-release@v0.1.15
@ -88,6 +89,7 @@ jobs:
./output/OS64.v64 ./output/OS64.v64
./output/OS64P.v64 ./output/OS64P.v64
./output/sc64menu.n64 ./output/sc64menu.n64
continue-on-error: true
generate-docs: generate-docs:
runs-on: ubuntu-latest runs-on: ubuntu-latest

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 \
@ -44,6 +46,7 @@ SRCS = \
menu/rom_database.c \ menu/rom_database.c \
menu/settings.c \ menu/settings.c \
menu/sound.c \ menu/sound.c \
menu/usb_comm.c \
menu/views/browser.c \ menu/views/browser.c \
menu/views/credits.c \ menu/views/credits.c \
menu/views/error.c \ menu/views/error.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 ### ED64
Download the `ED64.v64` ROM from the latest action run assets and place it in the `/ED64` folder. 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 #### ROM Boxart
To use boxart, you need to place png files of size 158x112 in the folder `/menu/boxart` on the SD card. 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`. Each file must be named according to the 2 letter ROM ID, or 3 letter ROM ID including media type.
A known set of PNG files can be downloaded [here](https://mega.nz/file/6cNGwSqI#8X5ukb65n3YMlGaUtSOGXkKo9HxVnnMOgqn94Epcr7w). 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 #### Emulator support
Emulators should be added to the `/emulators` directory on the SD card. 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` - **NES**: [neon64v2](https://github.com/hcs64/neon64v2) by *hcs64* - `neon64bu.rom`
- **SNES**: [sodium64](https://github.com/Hydr8gon/sodium64) by *Hydr8gon* - `sodium64.z64` - **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` - **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 # Developer documentation

View File

@ -21,8 +21,14 @@ echo !!! Now toggle power to the N64 !!!
echo: echo:
echo: echo:
if not "%1" == "/d" goto :exit if not "%1" == "/d" goto :not_d
%~dp0tools\sc64\sc64deployer debug --no-writeback %~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

View File

@ -4,28 +4,12 @@ set -e
REMOTE="--remote ${REMOTE:-host.docker.internal:9064}" 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 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 if [ "$1" = "-d" ]; then
sc64deployer $REMOTE debug --no-writeback sc64deployer $REMOTE debug --no-writeback
fi fi
if [ "$1" = "-du" ]; then
sc64deployer $REMOTE debug --no-writeback --init "send-file /sc64menu.n64 @output/sc64menu.n64@;reboot"
fi

View File

@ -12,7 +12,6 @@
extern uint32_t reboot_start __attribute__((section(".text"))); extern uint32_t reboot_start __attribute__((section(".text")));
extern size_t reboot_size __attribute__((section(".text"))); extern size_t reboot_size __attribute__((section(".text")));
extern int reboot_entry_offset __attribute__((section(".text")));
typedef struct { typedef struct {
@ -88,7 +87,23 @@ void boot (boot_params_t *params) {
while (!(cpu_io_read(&SP->SR) & SP_SR_HALT)); 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)); 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]))); 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 boot_device asm ("s3");
register uint32_t tv_type asm ("s4"); register uint32_t tv_type asm ("s4");
register uint32_t reset_type asm ("s5"); register uint32_t reset_type asm ("s5");
register uint32_t cic_seed asm ("s6"); register uint32_t cic_seed asm ("s6");
register uint32_t version asm ("s7"); 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); boot_device = (params->device_type & 0x01);
tv_type = (params->tv_type & 0x03); tv_type = (params->tv_type & 0x03);
reset_type = BOOT_RESET_TYPE_COLD; 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_NTSC) ? 1
: (params->tv_type == BOOT_TV_TYPE_MPAL) ? 4 : (params->tv_type == BOOT_TV_TYPE_MPAL) ? 4
: 0; : 0;
stack_pointer = (void *) UNCACHED(&SP_MEM->IMEM[1020]);
asm volatile ( asm volatile (
"move $sp, %[stack_pointer] \n" "la $t3, reboot \n"
"jr %[entry_point] \n" :: "jr $t3 \n" ::
[entry_point] "r" (entry_point),
[boot_device] "r" (boot_device), [boot_device] "r" (boot_device),
[tv_type] "r" (tv_type), [tv_type] "r" (tv_type),
[reset_type] "r" (reset_type), [reset_type] "r" (reset_type),
[cic_seed] "r" (cic_seed), [cic_seed] "r" (cic_seed),
[version] "r" (version), [version] "r" (version) :
[stack_pointer] "r" (stack_pointer) "t3"
); );
while (1); while (1);

View File

@ -37,6 +37,8 @@ typedef struct {
io32_t DMA_FULL; io32_t DMA_FULL;
io32_t DMA_BUSY; io32_t DMA_BUSY;
io32_t SEMAPHORE; io32_t SEMAPHORE;
io32_t __reserved[0xFFF8];
io32_t PC;
} sp_regs_t; } sp_regs_t;
#define SP_BASE (0x04040000UL) #define SP_BASE (0x04040000UL)

View File

@ -1,3 +1,7 @@
#define IPL3_ENTRY 0xA4000040
#define REBOOT_ADDRESS 0xA4001000
#define STACK_ADDRESS 0xA4001FF0
#define RI_ADDRESS 0xA4700000 #define RI_ADDRESS 0xA4700000
#define RI_MODE 0x00 #define RI_MODE 0x00
@ -13,8 +17,6 @@
#define RDRAM_RESET_DELAY 1024 #define RDRAM_RESET_DELAY 1024
#define RDRAM_STANDBY_DELAY 512 #define RDRAM_STANDBY_DELAY 512
#define IPL3_ENTRY 0xA4000040
.set noat .set noat
.section .text.reboot, "ax", %progbits .section .text.reboot, "ax", %progbits
.type reboot, %object .type reboot, %object
@ -36,8 +38,10 @@ ipl2:
.set reorder .set reorder
reboot_entry: reboot_entry:
.equ reboot_entry_offset, ((. - reboot_start) / 4) .set reboot, REBOOT_ADDRESS + (. - reboot_start)
.global reboot_entry_offset .global reboot
li $sp, STACK_ADDRESS
bnez $s5, reset_rdram_skip bnez $s5, reset_rdram_skip
@ -66,6 +70,12 @@ reset_rdram:
bnez $t2, 1b bnez $t2, 1b
reset_rdram_skip: reset_rdram_skip:
prepare_registers:
la $t0, ra_table
sll $t1, $s4, 2
add $t0, $t1
lw $ra, ($t0)
move $at, $zero move $at, $zero
move $v0, $zero move $v0, $zero
move $v1, $zero move $v1, $zero
@ -76,7 +86,6 @@ reset_rdram_skip:
move $t0, $zero move $t0, $zero
move $t1, $zero move $t1, $zero
move $t2, $zero move $t2, $zero
move $t3, $zero
move $t4, $zero move $t4, $zero
move $t5, $zero move $t5, $zero
move $t6, $zero move $t6, $zero
@ -90,13 +99,16 @@ reset_rdram_skip:
move $k1, $zero move $k1, $zero
move $gp, $zero move $gp, $zero
move $s8, $zero move $s8, $zero
move $ra, $zero
mtc0 $zero, $9
run_ipl3: run_ipl3:
li $t3, IPL3_ENTRY li $t3, IPL3_ENTRY
jr $t3 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 .global reboot_size

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,11 +8,13 @@
#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"
#include "ed64/ed64.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] = { 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(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 flashcart_error_t dummy_init (void) { static flashcart_error_t dummy_init (void) {
return FLASHCART_OK; return FLASHCART_OK;
} }
@ -56,7 +62,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, ed64_get_flashcart }, // Original 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) { if (flashcart->deinit) {
return flashcart->deinit(); return flashcart->deinit();
} }
return FLASHCART_OK; 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); 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 flashcart_load_save (char *save_path, flashcart_save_type_t save_type) {
flashcart_error_t error; flashcart_error_t error;
uint32_t sectors[WRITEBACK_MAX_SECTORS] __attribute__((aligned(8)));
if (save_type >= __FLASHCART_SAVE_TYPE_END) { if (save_type >= __FLASHCART_SAVE_TYPE_END) {
return FLASHCART_ERROR_ARGS; 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 (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; 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; return error;
} }
} }

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

@ -85,7 +85,7 @@ static flashcart_error_t sc64_init (void) {
uint32_t value; uint32_t value;
} default_config[] = { } default_config[] = {
{ CFG_ID_BOOTLOADER_SWITCH, false }, { 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_ROM_SHADOW_ENABLE, false },
{ CFG_ID_DD_MODE, DD_MODE_DISABLED }, { CFG_ID_DD_MODE, DD_MODE_DISABLED },
{ CFG_ID_ISV_ADDRESS, 0x00000000 }, { CFG_ID_ISV_ADDRESS, 0x00000000 },
@ -110,6 +110,8 @@ static flashcart_error_t sc64_init (void) {
} }
static flashcart_error_t sc64_deinit (void) { static flashcart_error_t sc64_deinit (void) {
sc64_ll_set_config(CFG_ID_ROM_WRITE_ENABLE, false);
sc64_ll_lock(); sc64_ll_lock();
return FLASHCART_OK; 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: 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

@ -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: case CART_LOAD_EMU_TYPE_SNES:
path_push(path, "sodium64.z64"); path_push(path, "sodium64.z64");
save_type = FLASHCART_SAVE_TYPE_SRAM; save_type = FLASHCART_SAVE_TYPE_SRAM;
emulated_rom_offset = 0x104000;
break; break;
case CART_LOAD_EMU_TYPE_GAMEBOY: case CART_LOAD_EMU_TYPE_GAMEBOY:
path_push(path, "gb.v64"); 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"); path_push(path, "gbc.v64");
save_type = FLASHCART_SAVE_TYPE_FLASHRAM; save_type = FLASHCART_SAVE_TYPE_FLASHRAM;
break; 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))) { 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) { switch (emu_type) {
case CART_LOAD_EMU_TYPE_SNES: 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; emulated_file_offset = ((file_get_size(path_get(path)) & 0x3FF) == 0x200) ? 0x200 : 0;
break; break;
default: default:

View File

@ -28,6 +28,7 @@ typedef enum {
CART_LOAD_EMU_TYPE_SNES, CART_LOAD_EMU_TYPE_SNES,
CART_LOAD_EMU_TYPE_GAMEBOY, CART_LOAD_EMU_TYPE_GAMEBOY,
CART_LOAD_EMU_TYPE_GAMEBOY_COLOR, CART_LOAD_EMU_TYPE_GAMEBOY_COLOR,
CART_LOAD_EMU_TYPE_SEGA_GENERIC_8BIT,
} cart_load_emu_type_t; } cart_load_emu_type_t;

View File

@ -59,7 +59,7 @@ typedef struct {
surface_t *image; surface_t *image;
} component_boxart_t; } 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_free (component_boxart_t *b);
void component_boxart_draw (component_boxart_t *b); void component_boxart_draw (component_boxart_t *b);

View File

@ -4,6 +4,7 @@
#include "../path.h" #include "../path.h"
#include "../png_decoder.h" #include "../png_decoder.h"
#include "constants.h" #include "constants.h"
#include "utils/fs.h"
#define BOXART_DIRECTORY "sd:/menu/boxart" #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)); component_boxart_t *b = calloc(1, sizeof(component_boxart_t));
if (b) { if (b) {
b->loading = true; 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); // 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)); 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) { if (png_decoder_start(path, BOXART_WIDTH, BOXART_HEIGHT, png_decoder_callback, b) != PNG_OK) {
free(b); free(b);

View File

@ -14,6 +14,7 @@
#include "png_decoder.h" #include "png_decoder.h"
#include "settings.h" #include "settings.h"
#include "sound.h" #include "sound.h"
#include "usb_comm.h"
#include "utils/fs.h" #include "utils/fs.h"
#include "views/views.h" #include "views/views.h"
@ -28,7 +29,6 @@
static menu_t *menu; static menu_t *menu;
static bool boot_pending;
static tv_type_t tv_type; static tv_type_t tv_type;
static volatile int frame_counter = 0; static volatile int frame_counter = 0;
@ -61,8 +61,6 @@ static void menu_init (boot_params_t *boot_params) {
fonts_init(); fonts_init();
sound_init_default(); sound_init_default();
boot_pending = false;
menu = calloc(1, sizeof(menu_t)); menu = calloc(1, sizeof(menu_t));
assert(menu != NULL); assert(menu != NULL);
@ -147,7 +145,7 @@ static struct views_s {
void menu_run (boot_params_t *boot_params) { void menu_run (boot_params_t *boot_params) {
menu_init(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; surface_t *display = (frame_counter >= FRAMERATE_DIVIDER) ? display_try_get() : NULL;
if (display != NULL) { if (display != NULL) {
@ -159,7 +157,12 @@ void menu_run (boot_params_t *boot_params) {
views[menu->mode].show(menu, display); views[menu->mode].show(menu, display);
} else { } else {
rdpq_attach_clear(display, NULL); 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) { while (menu->mode != menu->next_mode) {
@ -168,10 +171,6 @@ void menu_run (boot_params_t *boot_params) {
if (views[menu->mode].init) { if (views[menu->mode].init) {
views[menu->mode].init(menu); views[menu->mode].init(menu);
} }
if (menu->mode == MENU_MODE_BOOT) {
boot_pending = true;
}
} }
time(&menu->current_time); time(&menu->current_time);
@ -180,6 +179,8 @@ void menu_run (boot_params_t *boot_params) {
sound_poll(); sound_poll();
png_decoder_poll(); png_decoder_poll();
usb_comm_poll(menu);
} }
menu_deinit(menu); menu_deinit(menu);

163
src/menu/usb_comm.c Normal file
View File

@ -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 <fatfs/ff.h>
#include <string.h>
#include <usb.h>
#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");
}
}
}
}

21
src/menu/usb_comm.h Normal file
View File

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

View File

@ -10,7 +10,7 @@
static const char *rom_extensions[] = { "z64", "n64", "v64", "rom", NULL }; 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 *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 *image_extensions[] = { "png", NULL };
static const char *music_extensions[] = { "mp3", NULL }; static const char *music_extensions[] = { "mp3", NULL };

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"
); );

View File

@ -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_snes_rom_extensions[] = { "sfc", "smc", NULL };
static const char *emu_gameboy_rom_extensions[] = { "gb", NULL }; static const char *emu_gameboy_rom_extensions[] = { "gb", NULL };
static const char *emu_gameboy_color_rom_extensions[] = { "gbc", 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 bool load_pending;
static cart_load_emu_type_t emu_type; 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) { static void process (menu_t *menu) {
if (menu->actions.enter) { if (menu->actions.enter) {
@ -33,17 +51,22 @@ static void draw (menu_t *menu, surface_t *d) {
component_main_text_draw( component_main_text_draw(
ALIGN_CENTER, VALIGN_TOP, ALIGN_CENTER, VALIGN_TOP,
"Emulator information\n" "Load Emulated ROM\n"
"THE EMULATOR\n" );
"Rom Name\n"
component_main_text_draw(
ALIGN_LEFT, VALIGN_TOP,
"\n" "\n"
"%s", "\n"
"Emulated System: %s\n"
"Rom Name: %s",
format_emulator_name(emu_type),
menu->browser.entry->name menu->browser.entry->name
); );
component_actions_bar_text_draw( component_actions_bar_text_draw(
ALIGN_LEFT, VALIGN_TOP, ALIGN_LEFT, VALIGN_TOP,
"A: Load and run Emulator ROM\n" "A: Load and run Emulated ROM\n"
"B: Exit" "B: Exit"
); );
} }
@ -93,6 +116,8 @@ void view_load_emulator_init (menu_t *menu) {
emu_type = CART_LOAD_EMU_TYPE_GAMEBOY; emu_type = CART_LOAD_EMU_TYPE_GAMEBOY;
} else if (file_has_extensions(path_get(path), emu_gameboy_color_rom_extensions)) { } else if (file_has_extensions(path_get(path), emu_gameboy_color_rom_extensions)) {
emu_type = CART_LOAD_EMU_TYPE_GAMEBOY_COLOR; 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 { } else {
menu_show_error(menu, "Unsupported ROM"); menu_show_error(menu, "Unsupported ROM");
} }

View File

@ -250,7 +250,7 @@ void view_load_rom_init (menu_t *menu) {
rom_header = file_read_rom_header(path_get(path)); 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); path_free(path);
} }

View File

@ -13,7 +13,7 @@ char *strip_sd_prefix (char *path) {
char *found = strstr(path, prefix); char *found = strstr(path, prefix);
if (found) { if (found) {
return found + strlen(prefix); return found + strlen(prefix) - 1;
} }
return path; return path;
@ -106,13 +106,13 @@ bool file_fill (char *path, uint8_t value) {
return error; 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; FATFS *fs;
FIL fil; FIL fil;
bool error = false; bool error = false;
for (int i = 0; i < entries; i++) { if (!callback) {
sectors[i] = 0; return true;
} }
if (f_open(&fil, strip_sd_prefix(path), FA_READ) != FR_OK) { if (f_open(&fil, strip_sd_prefix(path), FA_READ) != FR_OK) {
@ -121,13 +121,11 @@ bool file_get_sectors (char *path, uint32_t *sectors, size_t entries) {
fs = fil.obj.fs; fs = fil.obj.fs;
uint32_t file_sector_entries = (ALIGN(f_size(&fil), FS_SECTOR_SIZE) / FS_SECTOR_SIZE); uint32_t sector_count = (ALIGN(f_size(&fil), FS_SECTOR_SIZE) / FS_SECTOR_SIZE);
file_sector_entries = (file_sector_entries > entries) ? entries : file_sector_entries;
uint32_t cluster_sector = 0; uint32_t cluster_sector = 0;
for (int file_sector = 0; file_sector < file_sector_entries; file_sector++) { for (int file_sector = 0; file_sector < sector_count; file_sector += fs->csize) {
if ((file_sector % fs->csize) == 0) {
if ((f_lseek(&fil, (file_sector * FS_SECTOR_SIZE) + (FS_SECTOR_SIZE / 2))) != FR_OK) { if ((f_lseek(&fil, (file_sector * FS_SECTOR_SIZE) + (FS_SECTOR_SIZE / 2))) != FR_OK) {
error = true; error = true;
break; break;
@ -138,8 +136,7 @@ bool file_get_sectors (char *path, uint32_t *sectors, size_t entries) {
break; break;
} }
cluster_sector = (fs->database + ((LBA_t) (fs->csize) * (cluster - 2))); cluster_sector = (fs->database + ((LBA_t) (fs->csize) * (cluster - 2)));
} callback(sector_count, file_sector, cluster_sector, fs->csize);
*sectors++ = (cluster_sector + (file_sector % fs->csize));
} }
if (f_close(&fil) != FR_OK) { if (f_close(&fil) != FR_OK) {

View File

@ -17,7 +17,7 @@ size_t file_get_size (char *path);
bool file_delete (char *path); bool file_delete (char *path);
bool file_allocate (char *path, size_t size); bool file_allocate (char *path, size_t size);
bool file_fill (char *path, uint8_t value); 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 file_has_extensions (char *path, const char *extensions[]);
bool directory_exists (char *path); bool directory_exists (char *path);