mirror of
https://github.com/Polprzewodnikowy/N64FlashcartMenu.git
synced 2024-11-25 03:56:54 +01:00
Initial version (no UI)
This commit is contained in:
commit
992fcf4923
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/.vscode
|
||||
/build
|
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
[submodule "libdragon"]
|
||||
path = libdragon
|
||||
url = https://github.com/DragonMinded/libdragon
|
||||
branch = trunk
|
5
.libdragon/config.json
Normal file
5
.libdragon/config.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"imageName": "ghcr.io/dragonminded/libdragon:latest",
|
||||
"vendorDirectory": "libdragon",
|
||||
"vendorStrategy": "submodule"
|
||||
}
|
35
Makefile
Normal file
35
Makefile
Normal file
@ -0,0 +1,35 @@
|
||||
EXE_NAME = N64FlashcartMenu
|
||||
|
||||
SOURCE_DIR = src
|
||||
BUILD_DIR = build
|
||||
|
||||
include $(N64_INST)/include/n64.mk
|
||||
|
||||
N64_CFLAGS += -iquote $(SOURCE_DIR)
|
||||
|
||||
SRCS = \
|
||||
boot/boot.c \
|
||||
boot/crc32.c \
|
||||
boot/ipl2.S \
|
||||
flashcart/flashcart.c \
|
||||
flashcart/sc64/sc64_internal.c \
|
||||
flashcart/sc64/sc64.c \
|
||||
menu/menu.c \
|
||||
utils/fs.c \
|
||||
main.c
|
||||
|
||||
OBJS = $(addprefix $(BUILD_DIR)/, $(addsuffix .o,$(basename $(SRCS))))
|
||||
|
||||
$(BUILD_DIR)/$(EXE_NAME).elf: $(OBJS)
|
||||
|
||||
$(EXE_NAME).z64: N64_ROM_TITLE=$(EXE_NAME)
|
||||
|
||||
all: $(EXE_NAME).z64
|
||||
$(shell mv $(EXE_NAME).z64 $(BUILD_DIR))
|
||||
.PHONY: all
|
||||
|
||||
clean:
|
||||
$(shell rm -rf ./$(BUILD_DIR))
|
||||
.PHONY: clean
|
||||
|
||||
-include $(wildcard $(BUILD_DIR)/*.d)
|
3
README.md
Normal file
3
README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# N64FlashcartMenu
|
||||
|
||||
Nothing to see here, come back later.
|
13
build.sh
Normal file
13
build.sh
Normal file
@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$1" = "-c" ]; then
|
||||
libdragon make clean
|
||||
fi
|
||||
|
||||
libdragon make -j all
|
||||
|
||||
pushd ./build > /dev/null
|
||||
../tools/finalize.py N64FlashcartMenu.z64
|
||||
popd > /dev/null
|
1
libdragon
Submodule
1
libdragon
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 2413aacd5c3119fe9c3ef9b8fa5e4e965a15cc01
|
178
src/boot/boot.c
Normal file
178
src/boot/boot.c
Normal file
@ -0,0 +1,178 @@
|
||||
#include <libdragon.h>
|
||||
|
||||
#include "boot.h"
|
||||
#include "boot_io.h"
|
||||
#include "crc32.h"
|
||||
|
||||
|
||||
extern uint32_t ipl2 __attribute__((section(".data")));
|
||||
|
||||
|
||||
typedef struct {
|
||||
const uint32_t crc32;
|
||||
const uint8_t seed;
|
||||
} ipl3_crc32_t;
|
||||
|
||||
static const ipl3_crc32_t ipl3_crc32[] = {
|
||||
{ .crc32 = 0x587BD543, .seed = 0xAC }, // 5101
|
||||
{ .crc32 = 0x6170A4A1, .seed = 0x3F }, // 6101
|
||||
{ .crc32 = 0x009E9EA3, .seed = 0x3F }, // 7102
|
||||
{ .crc32 = 0x90BB6CB5, .seed = 0x3F }, // x102
|
||||
{ .crc32 = 0x0B050EE0, .seed = 0x78 }, // x103
|
||||
{ .crc32 = 0x98BC2C86, .seed = 0x91 }, // x105
|
||||
{ .crc32 = 0xACC8580A, .seed = 0x85 }, // x106
|
||||
{ .crc32 = 0x0E018159, .seed = 0xDD }, // 5167
|
||||
{ .crc32 = 0x10C68B18, .seed = 0xDD }, // NDXJ0
|
||||
{ .crc32 = 0xBC605D0A, .seed = 0xDD }, // NDDJ0
|
||||
{ .crc32 = 0x502C4466, .seed = 0xDD }, // NDDJ1
|
||||
{ .crc32 = 0x0C965795, .seed = 0xDD }, // NDDJ2
|
||||
{ .crc32 = 0x8FEBA21E, .seed = 0xDE }, // NDDE0
|
||||
};
|
||||
|
||||
|
||||
static io32_t *boot_get_device_base (boot_params_t *params) {
|
||||
io32_t *device_base_address = ROM_CART;
|
||||
if (params->device_type == BOOT_DEVICE_TYPE_DD) {
|
||||
device_base_address = ROM_DDIPL;
|
||||
}
|
||||
return device_base_address;
|
||||
}
|
||||
|
||||
static bool boot_detect_tv_type (boot_params_t *params) {
|
||||
io32_t *base = boot_get_device_base(params);
|
||||
|
||||
char region = ((io_read((uint32_t) (&base[15])) >> 8) & 0xFF);
|
||||
|
||||
switch (region) {
|
||||
case 'P':
|
||||
case 'U':
|
||||
params->tv_type = BOOT_TV_TYPE_PAL;
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
case 'J':
|
||||
params->tv_type = BOOT_TV_TYPE_NTSC;
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
params->tv_type = BOOT_TV_TYPE_MPAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool boot_detect_cic_seed (boot_params_t *params) {
|
||||
io32_t *base = boot_get_device_base(params);
|
||||
|
||||
uint32_t ipl3[1008] __attribute__((aligned(8)));
|
||||
|
||||
data_cache_hit_writeback_invalidate(ipl3, sizeof(ipl3));
|
||||
dma_read_raw_async(ipl3, (uint32_t) (&base[16]), sizeof(ipl3));
|
||||
dma_wait();
|
||||
|
||||
uint32_t crc32 = crc32_calculate(ipl3, sizeof(ipl3));
|
||||
|
||||
for (int i = 0; i < sizeof(ipl3_crc32) / sizeof(ipl3_crc32_t); i++) {
|
||||
if (ipl3_crc32[i].crc32 == crc32) {
|
||||
params->cic_seed = ipl3_crc32[i].seed;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool boot_is_warm (void) {
|
||||
return (OS_INFO->reset_type == OS_INFO_RESET_TYPE_NMI);
|
||||
}
|
||||
|
||||
void boot (boot_params_t *params) {
|
||||
if (params->detect_tv_type) {
|
||||
if (!boot_detect_tv_type(params)) {
|
||||
params->tv_type = OS_INFO->tv_type;
|
||||
}
|
||||
}
|
||||
|
||||
if (params->detect_cic_seed) {
|
||||
if (!boot_detect_cic_seed(params)) {
|
||||
params->cic_seed = 0x3F;
|
||||
}
|
||||
}
|
||||
|
||||
OS_INFO->mem_size_6105 = OS_INFO->mem_size;
|
||||
|
||||
while (!(cpu_io_read(&SP->SR) & SP_SR_HALT));
|
||||
|
||||
cpu_io_write(&SP->SR, SP_SR_CLR_INTR | SP_SR_SET_HALT);
|
||||
|
||||
while (cpu_io_read(&SP->DMA_BUSY));
|
||||
|
||||
cpu_io_write(&PI->SR, PI_SR_CLR_INTR | PI_SR_RESET);
|
||||
cpu_io_write(&VI->V_INTR, 0x3FF);
|
||||
cpu_io_write(&VI->H_LIMITS, 0);
|
||||
cpu_io_write(&VI->CURR_LINE, 0);
|
||||
cpu_io_write(&AI->MADDR, 0);
|
||||
cpu_io_write(&AI->LEN, 0);
|
||||
|
||||
io32_t *base = boot_get_device_base(params);
|
||||
|
||||
uint32_t pi_config = io_read((uint32_t) (base));
|
||||
|
||||
cpu_io_write(&PI->DOM[0].LAT, pi_config & 0xFF);
|
||||
cpu_io_write(&PI->DOM[0].PWD, pi_config >> 8);
|
||||
cpu_io_write(&PI->DOM[0].PGS, pi_config >> 16);
|
||||
cpu_io_write(&PI->DOM[0].RLS, pi_config >> 20);
|
||||
|
||||
if (cpu_io_read(&DPC->SR) & DPC_SR_XBUS_DMEM_DMA) {
|
||||
while (cpu_io_read(&DPC->SR) & DPC_SR_PIPE_BUSY);
|
||||
}
|
||||
|
||||
uint32_t *ipl2_src = &ipl2;
|
||||
io32_t *ipl2_dst = SP_MEM->IMEM;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
cpu_io_write(&ipl2_dst[i], ipl2_src[i]);
|
||||
}
|
||||
|
||||
io32_t *ipl3_src = base;
|
||||
io32_t *ipl3_dst = SP_MEM->DMEM;
|
||||
|
||||
for (int i = 16; i < 1024; 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 tv_type asm ("s4");
|
||||
register uint32_t reset_type asm ("s5");
|
||||
register uint32_t cic_seed asm ("s6");
|
||||
register uint32_t version asm ("s7");
|
||||
void *stack_pointer;
|
||||
|
||||
entry_point = (void (*)(void)) UNCACHED(&SP_MEM->DMEM[16]);
|
||||
boot_device = (params->device_type & 0x01);
|
||||
tv_type = (params->tv_type & 0x03);
|
||||
reset_type = (params->reset_type & 0x01);
|
||||
cic_seed = (params->cic_seed & 0xFF);
|
||||
version = 1;
|
||||
stack_pointer = (void *) UNCACHED(&SP_MEM->IMEM[1020]);
|
||||
|
||||
asm volatile (
|
||||
"move $sp, %[stack_pointer] \n"
|
||||
"jr %[entry_point] \n" ::
|
||||
[entry_point] "r" (entry_point),
|
||||
[boot_device] "r" (boot_device),
|
||||
[tv_type] "r" (tv_type),
|
||||
[reset_type] "r" (reset_type),
|
||||
[cic_seed] "r" (cic_seed),
|
||||
[version] "r" (version),
|
||||
[stack_pointer] "r" (stack_pointer)
|
||||
);
|
||||
|
||||
while (1);
|
||||
}
|
40
src/boot/boot.h
Normal file
40
src/boot/boot.h
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef BOOT_H__
|
||||
#define BOOT_H__
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
typedef enum {
|
||||
BOOT_DEVICE_TYPE_ROM = 0,
|
||||
BOOT_DEVICE_TYPE_DD = 1,
|
||||
} boot_device_type_t;
|
||||
|
||||
typedef enum {
|
||||
BOOT_RESET_TYPE_COLD = 0,
|
||||
BOOT_RESET_TYPE_NMI = 1,
|
||||
} boot_reset_type_t;
|
||||
|
||||
typedef enum {
|
||||
BOOT_TV_TYPE_PAL = 0,
|
||||
BOOT_TV_TYPE_NTSC = 1,
|
||||
BOOT_TV_TYPE_MPAL = 2,
|
||||
} boot_tv_type_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
boot_device_type_t device_type;
|
||||
boot_reset_type_t reset_type;
|
||||
boot_tv_type_t tv_type;
|
||||
uint8_t cic_seed;
|
||||
bool detect_tv_type;
|
||||
bool detect_cic_seed;
|
||||
} boot_params_t;
|
||||
|
||||
|
||||
bool boot_is_warm (void);
|
||||
void boot (boot_params_t *params);
|
||||
|
||||
|
||||
#endif
|
234
src/boot/boot_io.h
Normal file
234
src/boot/boot_io.h
Normal file
@ -0,0 +1,234 @@
|
||||
#ifndef BOOT_IO_H__
|
||||
#define BOOT_IO_H__
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
typedef volatile uint8_t io8_t;
|
||||
typedef volatile uint32_t io32_t;
|
||||
|
||||
|
||||
#define UNCACHED(address) ((typeof(address)) (((io32_t) (address)) | (0xA0000000UL)))
|
||||
|
||||
|
||||
typedef struct {
|
||||
io32_t DMEM[1024];
|
||||
io32_t IMEM[1024];
|
||||
} sp_mem_t;
|
||||
|
||||
#define SP_MEM_BASE (0x04000000UL)
|
||||
#define SP_MEM ((sp_mem_t *) SP_MEM_BASE)
|
||||
|
||||
|
||||
typedef struct {
|
||||
io32_t PADDR;
|
||||
io32_t MADDR;
|
||||
io32_t RD_LEN;
|
||||
io32_t WR_LEN;
|
||||
io32_t SR;
|
||||
io32_t DMA_FULL;
|
||||
io32_t DMA_BUSY;
|
||||
io32_t SEMAPHORE;
|
||||
} sp_regs_t;
|
||||
|
||||
#define SP_BASE (0x04040000UL)
|
||||
#define SP ((sp_regs_t *) SP_BASE)
|
||||
|
||||
#define SP_SR_HALT (1 << 0)
|
||||
#define SP_SR_BROKE (1 << 1)
|
||||
#define SP_SR_DMA_BUSY (1 << 2)
|
||||
#define SP_SR_DMA_FULL (1 << 3)
|
||||
#define SP_SR_IO_FULL (1 << 4)
|
||||
#define SP_SR_SSTEP (1 << 5)
|
||||
#define SP_SR_INTR_BREAK (1 << 6)
|
||||
#define SP_SR_SIG0 (1 << 7)
|
||||
#define SP_SR_SIG1 (1 << 8)
|
||||
#define SP_SR_SIG2 (1 << 9)
|
||||
#define SP_SR_SIG3 (1 << 10)
|
||||
#define SP_SR_SIG4 (1 << 11)
|
||||
#define SP_SR_SIG5 (1 << 12)
|
||||
#define SP_SR_SIG6 (1 << 13)
|
||||
#define SP_SR_SIG7 (1 << 14)
|
||||
#define SP_SR_CLR_HALT (1 << 0)
|
||||
#define SP_SR_SET_HALT (1 << 1)
|
||||
#define SP_SR_CLR_BROKE (1 << 2)
|
||||
#define SP_SR_CLR_INTR (1 << 3)
|
||||
#define SP_SR_SET_INTR (1 << 4)
|
||||
#define SP_SR_CLR_SSTEP (1 << 5)
|
||||
#define SP_SR_SET_SSTEP (1 << 6)
|
||||
#define SP_SR_CLR_INTR_BREAK (1 << 7)
|
||||
#define SP_SR_SET_INTR_BREAK (1 << 8)
|
||||
#define SP_SR_CLR_SIG0 (1 << 9)
|
||||
#define SP_SR_SET_SIG0 (1 << 10)
|
||||
#define SP_SR_CLR_SIG1 (1 << 11)
|
||||
#define SP_SR_SET_SIG1 (1 << 12)
|
||||
#define SP_SR_CLR_SIG2 (1 << 13)
|
||||
#define SP_SR_SET_SIG2 (1 << 14)
|
||||
#define SP_SR_CLR_SIG3 (1 << 15)
|
||||
#define SP_SR_SET_SIG3 (1 << 16)
|
||||
#define SP_SR_CLR_SIG4 (1 << 17)
|
||||
#define SP_SR_SET_SIG4 (1 << 18)
|
||||
#define SP_SR_CLR_SIG5 (1 << 19)
|
||||
#define SP_SR_SET_SIG5 (1 << 20)
|
||||
#define SP_SR_CLR_SIG6 (1 << 21)
|
||||
#define SP_SR_SET_SIG6 (1 << 22)
|
||||
#define SP_SR_CLR_SIG7 (1 << 23)
|
||||
#define SP_SR_SET_SIG7 (1 << 24)
|
||||
|
||||
|
||||
typedef struct {
|
||||
io32_t START;
|
||||
io32_t END;
|
||||
io32_t CURRENT;
|
||||
io32_t SR;
|
||||
io32_t CLOCK;
|
||||
io32_t BUF_BUSY;
|
||||
io32_t PIPE_BUSY;
|
||||
io32_t TMEM;
|
||||
} dpc_regs_t;
|
||||
|
||||
#define DPC_BASE (0x04100000UL)
|
||||
#define DPC ((dpc_regs_t *) DPC_BASE)
|
||||
|
||||
#define DPC_SR_XBUS_DMEM_DMA (1 << 0)
|
||||
#define DPC_SR_FREEZE (1 << 1)
|
||||
#define DPC_SR_FLUSH (1 << 2)
|
||||
#define DPC_SR_START_GCLK (1 << 3)
|
||||
#define DPC_SR_TMEM_BUSY (1 << 4)
|
||||
#define DPC_SR_PIPE_BUSY (1 << 5)
|
||||
#define DPC_SR_CMD_BUSY (1 << 6)
|
||||
#define DPC_SR_CBUF_READY (1 << 7)
|
||||
#define DPC_SR_DMA_BUSY (1 << 8)
|
||||
#define DPC_SR_END_VALID (1 << 9)
|
||||
#define DPC_SR_START_VALID (1 << 10)
|
||||
#define DPC_SR_CLR_XBUS_DMEM_DMA (1 << 0)
|
||||
#define DPC_SR_SET_XBUS_DMEM_DMA (1 << 1)
|
||||
#define DPC_SR_CLR_FREEZE (1 << 2)
|
||||
#define DPC_SR_SET_FREEZE (1 << 3)
|
||||
#define DPC_SR_CLR_FLUSH (1 << 4)
|
||||
#define DPC_SR_SET_FLUSH (1 << 5)
|
||||
#define DPC_SR_CLR_TMEM_CTR (1 << 6)
|
||||
#define DPC_SR_CLR_PIPE_CTR (1 << 7)
|
||||
#define DPC_SR_CLR_CMD_CTR (1 << 8)
|
||||
#define DPC_SR_CLR_CLOCK_CTR (1 << 9)
|
||||
|
||||
|
||||
typedef struct {
|
||||
io32_t CR;
|
||||
io32_t MADDR;
|
||||
io32_t H_WIDTH;
|
||||
io32_t V_INTR;
|
||||
io32_t CURR_LINE;
|
||||
io32_t TIMING;
|
||||
io32_t V_SYNC;
|
||||
io32_t H_SYNC;
|
||||
io32_t H_SYNC_LEAP;
|
||||
io32_t H_LIMITS;
|
||||
io32_t V_LIMITS;
|
||||
io32_t COLOR_BURST;
|
||||
io32_t H_SCALE;
|
||||
io32_t V_SCALE;
|
||||
} vi_regs_t;
|
||||
|
||||
#define VI_BASE (0x04400000UL)
|
||||
#define VI ((vi_regs_t *) VI_BASE)
|
||||
|
||||
#define VI_CR_TYPE_16 (2 << 0)
|
||||
#define VI_CR_TYPE_32 (3 << 0)
|
||||
#define VI_CR_GAMMA_DITHER_ON (1 << 2)
|
||||
#define VI_CR_GAMMA_ON (1 << 3)
|
||||
#define VI_CR_DIVOT_ON (1 << 4)
|
||||
#define VI_CR_SERRATE_ON (1 << 6)
|
||||
#define VI_CR_ANTIALIAS_0 (1 << 8)
|
||||
#define VI_CR_ANTIALIAS_1 (1 << 9)
|
||||
#define VI_CR_PIXEL_ADVANCE_0 (1 << 12)
|
||||
#define VI_CR_PIXEL_ADVANCE_1 (1 << 13)
|
||||
#define VI_CR_PIXEL_ADVANCE_2 (1 << 14)
|
||||
#define VI_CR_PIXEL_ADVANCE_3 (1 << 15)
|
||||
#define VI_CR_DITHER_FILTER_ON (1 << 16)
|
||||
|
||||
|
||||
typedef struct {
|
||||
io32_t MADDR;
|
||||
io32_t LEN;
|
||||
io32_t CR;
|
||||
io32_t SR;
|
||||
io32_t DACRATE;
|
||||
io32_t BITRATE;
|
||||
} ai_regs_t;
|
||||
|
||||
#define AI_BASE (0x04500000UL)
|
||||
#define AI ((ai_regs_t *) AI_BASE)
|
||||
|
||||
#define AI_SR_DMA_BUSY (1 << 30)
|
||||
#define AI_SR_FIFO_FULL (1 << 31)
|
||||
#define AI_CR_DMA_ON (1 << 0)
|
||||
|
||||
|
||||
typedef struct {
|
||||
io32_t MADDR;
|
||||
io32_t PADDR;
|
||||
io32_t RDMA;
|
||||
io32_t WDMA;
|
||||
io32_t SR;
|
||||
struct {
|
||||
io32_t LAT;
|
||||
io32_t PWD;
|
||||
io32_t PGS;
|
||||
io32_t RLS;
|
||||
} DOM[2];
|
||||
} pi_regs_t;
|
||||
|
||||
#define PI_BASE (0x04600000UL)
|
||||
#define PI ((pi_regs_t *) PI_BASE)
|
||||
|
||||
#define PI_SR_DMA_BUSY (1 << 0)
|
||||
#define PI_SR_IO_BUSY (1 << 1)
|
||||
#define PI_SR_DMA_ERROR (1 << 2)
|
||||
#define PI_SR_RESET (1 << 0)
|
||||
#define PI_SR_CLR_INTR (1 << 1)
|
||||
|
||||
|
||||
#define ROM_DDIPL_BASE (0x06000000UL)
|
||||
#define ROM_DDIPL ((io32_t *) ROM_DDIPL_BASE)
|
||||
|
||||
|
||||
#define ROM_CART_BASE (0x10000000UL)
|
||||
#define ROM_CART ((io32_t *) ROM_CART_BASE)
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t tv_type;
|
||||
uint32_t device_type;
|
||||
uint32_t device_base;
|
||||
uint32_t reset_type;
|
||||
uint32_t cic_id;
|
||||
uint32_t version;
|
||||
uint32_t mem_size;
|
||||
uint8_t app_nmi_buffer[64];
|
||||
uint32_t __reserved_1[37];
|
||||
uint32_t mem_size_6105;
|
||||
} os_info_t;
|
||||
|
||||
#define OS_INFO_BASE (0x80000300UL)
|
||||
#define OS_INFO ((os_info_t *) OS_INFO_BASE)
|
||||
|
||||
#define OS_INFO_RESET_TYPE_COLD (0)
|
||||
#define OS_INFO_RESET_TYPE_NMI (1)
|
||||
|
||||
|
||||
static inline uint32_t cpu_io_read (io32_t *address) {
|
||||
io32_t *uncached = UNCACHED(address);
|
||||
uint32_t value = *uncached;
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline void cpu_io_write (io32_t *address, uint32_t value) {
|
||||
io32_t *uncached = UNCACHED(address);
|
||||
*uncached = value;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
50
src/boot/crc32.c
Normal file
50
src/boot/crc32.c
Normal file
@ -0,0 +1,50 @@
|
||||
#include "crc32.h"
|
||||
|
||||
|
||||
static const uint32_t crc32_table[256] = {
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
|
||||
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
|
||||
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
|
||||
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
|
||||
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
|
||||
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
|
||||
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
|
||||
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
|
||||
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
|
||||
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
|
||||
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
|
||||
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
|
||||
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
|
||||
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
|
||||
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
|
||||
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
|
||||
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
|
||||
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
|
||||
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
|
||||
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
|
||||
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
|
||||
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
|
||||
};
|
||||
|
||||
|
||||
uint32_t crc32_calculate (void *buffer, size_t length) {
|
||||
uint32_t crc32 = 0xFFFFFFFFUL;
|
||||
|
||||
uint8_t *byte_pointer = (uint8_t *) (buffer);
|
||||
|
||||
while (length--) {
|
||||
crc32 = (crc32 >> 8) ^ crc32_table[(crc32 & 0xFF) ^ (*byte_pointer++)];
|
||||
}
|
||||
|
||||
return ~(crc32);
|
||||
}
|
12
src/boot/crc32.h
Normal file
12
src/boot/crc32.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef CRC32_H__
|
||||
#define CRC32_H__
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
uint32_t crc32_calculate (void *buffer, size_t length);
|
||||
|
||||
|
||||
#endif
|
17
src/boot/ipl2.S
Normal file
17
src/boot/ipl2.S
Normal file
@ -0,0 +1,17 @@
|
||||
.set noat
|
||||
.set noreorder
|
||||
|
||||
|
||||
.section .text.ipl2, "ax", %progbits
|
||||
.type ipl2, %object
|
||||
ipl2:
|
||||
.global ipl2
|
||||
lui $t5, 0xBFC0
|
||||
1:
|
||||
lw $t0, 0x7FC($t5)
|
||||
addiu $t5, $t5, 0x7C0
|
||||
andi $t0, $t0, 0x80
|
||||
bnel $t0, $zero, 1b
|
||||
lui $t5, 0xBFC0
|
||||
lw $t0, 0x24($t5)
|
||||
lui $t3, 0xB000
|
90
src/flashcart/flashcart.c
Normal file
90
src/flashcart/flashcart.c
Normal file
@ -0,0 +1,90 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include <usb.h>
|
||||
|
||||
#include "utils/fs.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
#include "flashcart.h"
|
||||
#include "sc64/sc64.h"
|
||||
|
||||
|
||||
#define WRITEBACK_MAX_SECTORS (256)
|
||||
|
||||
|
||||
static const size_t SAVE_SIZE[__FLASHCART_SAVE_TYPE_END] = {
|
||||
0,
|
||||
512,
|
||||
KiB(2),
|
||||
KiB(32),
|
||||
KiB(96),
|
||||
KiB(128),
|
||||
KiB(128),
|
||||
};
|
||||
|
||||
static flashcart_t *flashcart;
|
||||
|
||||
|
||||
bool flashcart_init (void) {
|
||||
switch (usb_getcart()) {
|
||||
case CART_SC64:
|
||||
flashcart = sc64_get_flashcart();
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return flashcart->init();
|
||||
}
|
||||
|
||||
void flashcart_deinit (void) {
|
||||
flashcart->deinit();
|
||||
}
|
||||
|
||||
flashcart_error_t flashcart_load_rom (char *rom_path) {
|
||||
if ((rom_path == NULL) || (!file_exists(rom_path)) || (file_get_size(rom_path) < KiB(4))) {
|
||||
return FLASHCART_ERROR_ARGS;
|
||||
}
|
||||
return flashcart->load_rom(rom_path);
|
||||
}
|
||||
|
||||
flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type, bool save_writeback) {
|
||||
flashcart_error_t error;
|
||||
uint32_t sectors[WRITEBACK_MAX_SECTORS] __attribute__((aligned(8)));
|
||||
|
||||
if (save_type >= __FLASHCART_SAVE_TYPE_END) {
|
||||
return FLASHCART_ERROR_ARGS;
|
||||
}
|
||||
|
||||
flashcart->set_save_type(save_type);
|
||||
|
||||
if ((save_path == NULL) || (save_type == FLASHCART_SAVE_TYPE_NONE)) {
|
||||
return FLASHCART_OK;
|
||||
}
|
||||
|
||||
if (!file_exists(save_path)) {
|
||||
if (file_allocate(save_path, SAVE_SIZE[save_type])) {
|
||||
return FLASHCART_ERROR_LOAD;
|
||||
}
|
||||
}
|
||||
|
||||
if (file_get_size(save_path) != SAVE_SIZE[save_type]) {
|
||||
return FLASHCART_ERROR_LOAD;
|
||||
}
|
||||
|
||||
if ((error = flashcart->load_save(save_path)) != FLASHCART_OK) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (save_writeback) {
|
||||
if (file_get_sectors(save_path, sectors, WRITEBACK_MAX_SECTORS)) {
|
||||
return FLASHCART_ERROR_LOAD;
|
||||
}
|
||||
if ((error = flashcart->set_save_writeback(sectors)) != FLASHCART_OK) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return FLASHCART_OK;
|
||||
}
|
43
src/flashcart/flashcart.h
Normal file
43
src/flashcart/flashcart.h
Normal file
@ -0,0 +1,43 @@
|
||||
#ifndef FLASHCART_H__
|
||||
#define FLASHCART_H__
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
typedef enum {
|
||||
FLASHCART_OK,
|
||||
FLASHCART_ERROR_ARGS,
|
||||
FLASHCART_ERROR_LOAD,
|
||||
FLASHCART_ERROR_INT,
|
||||
} flashcart_error_t;
|
||||
|
||||
typedef enum {
|
||||
FLASHCART_SAVE_TYPE_NONE,
|
||||
FLASHCART_SAVE_TYPE_EEPROM_4K,
|
||||
FLASHCART_SAVE_TYPE_EEPROM_16K,
|
||||
FLASHCART_SAVE_TYPE_SRAM,
|
||||
FLASHCART_SAVE_TYPE_SRAM_BANKED,
|
||||
FLASHCART_SAVE_TYPE_SRAM_128K,
|
||||
FLASHCART_SAVE_TYPE_FLASHRAM,
|
||||
__FLASHCART_SAVE_TYPE_END
|
||||
} flashcart_save_type_t;
|
||||
|
||||
typedef struct {
|
||||
bool (*init) (void);
|
||||
void (*deinit) (void);
|
||||
flashcart_error_t (*load_rom) (char *rom_path);
|
||||
flashcart_error_t (*load_save) (char *save_path);
|
||||
flashcart_error_t (*set_save_type) (flashcart_save_type_t save_type);
|
||||
flashcart_error_t (*set_save_writeback) (uint32_t *sectors);
|
||||
} flashcart_t;
|
||||
|
||||
|
||||
bool flashcart_init (void);
|
||||
void flashcart_deinit (void);
|
||||
flashcart_error_t flashcart_load_rom (char *rom_path);
|
||||
flashcart_error_t flashcart_load_save (char *save_path, flashcart_save_type_t save_type, bool save_writeback);
|
||||
|
||||
|
||||
#endif
|
240
src/flashcart/sc64/sc64.c
Normal file
240
src/flashcart/sc64/sc64.c
Normal file
@ -0,0 +1,240 @@
|
||||
#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 "sc64_internal.h"
|
||||
#include "sc64.h"
|
||||
|
||||
|
||||
#define SRAM_FLASHRAM_ADDRESS (0x08000000)
|
||||
#define ROM_ADDRESS (0x10000000)
|
||||
#define EXTENDED_ADDRESS (0x14000000)
|
||||
#define SHADOW_ADDRESS (0x1FFC0000)
|
||||
#define EEPROM_ADDRESS (0x1FFE2000)
|
||||
|
||||
|
||||
static flashcart_error_t load_to_flash (FIL *fil, void *address, size_t size, UINT *br) {
|
||||
size_t erase_block_size;
|
||||
UINT bp;
|
||||
|
||||
*br = 0;
|
||||
|
||||
if (sc64_flash_get_erase_block_size(&erase_block_size) != SC64_OK) {
|
||||
return FLASHCART_ERROR_INT;
|
||||
}
|
||||
|
||||
while (size > 0) {
|
||||
size_t program_size = MIN(size, erase_block_size);
|
||||
if (sc64_flash_erase_block(address) != SC64_OK) {
|
||||
return FLASHCART_ERROR_INT;
|
||||
}
|
||||
if (f_read(fil, address, program_size, &bp) != FR_OK) {
|
||||
return FLASHCART_ERROR_LOAD;
|
||||
}
|
||||
if (sc64_flash_wait_busy() != SC64_OK) {
|
||||
return FLASHCART_ERROR_INT;
|
||||
}
|
||||
address += program_size;
|
||||
size -= program_size;
|
||||
*br += bp;
|
||||
}
|
||||
|
||||
return FLASHCART_OK;
|
||||
}
|
||||
|
||||
|
||||
static bool sc64_init (void) {
|
||||
sc64_unlock();
|
||||
return sc64_check_presence();
|
||||
}
|
||||
|
||||
static void sc64_deinit (void) {
|
||||
sc64_lock();
|
||||
}
|
||||
|
||||
static flashcart_error_t sc64_load_rom (char *rom_path) {
|
||||
FIL fil;
|
||||
UINT br;
|
||||
|
||||
if (f_open(&fil, rom_path, FA_READ) != FR_OK) {
|
||||
return FLASHCART_ERROR_LOAD;
|
||||
}
|
||||
|
||||
size_t rom_size = ALIGN(f_size(&fil), FS_SECTOR_SIZE);
|
||||
|
||||
if (rom_size > MiB(78)) {
|
||||
f_close(&fil);
|
||||
return FLASHCART_ERROR_LOAD;
|
||||
}
|
||||
|
||||
bool shadow_enabled = (rom_size > (MiB(64) - KiB(128)));
|
||||
bool extended_enabled = (rom_size > MiB(64));
|
||||
|
||||
size_t sdram_size = shadow_enabled ? (MiB(64) - KiB(128)) : rom_size;
|
||||
size_t shadow_size = shadow_enabled ? MIN(rom_size - sdram_size, KiB(128)) : 0;
|
||||
size_t extended_size = extended_enabled ? rom_size - MiB(64) : 0;
|
||||
|
||||
if (f_read(&fil, (void *) (ROM_ADDRESS), sdram_size, &br) != FR_OK) {
|
||||
f_close(&fil);
|
||||
return FLASHCART_ERROR_LOAD;
|
||||
}
|
||||
if (br != sdram_size) {
|
||||
f_close(&fil);
|
||||
return FLASHCART_ERROR_LOAD;
|
||||
}
|
||||
|
||||
if (sc64_set_config(CFG_ROM_SHADOW_ENABLE, shadow_enabled) != SC64_OK) {
|
||||
f_close(&fil);
|
||||
return FLASHCART_ERROR_INT;
|
||||
}
|
||||
|
||||
if (shadow_enabled) {
|
||||
flashcart_error_t error = load_to_flash(&fil, (void *) (SHADOW_ADDRESS), shadow_size, &br);
|
||||
if (error != FLASHCART_OK) {
|
||||
f_close(&fil);
|
||||
return error;
|
||||
}
|
||||
if (br != shadow_size) {
|
||||
f_close(&fil);
|
||||
return FLASHCART_ERROR_LOAD;
|
||||
}
|
||||
}
|
||||
|
||||
if (sc64_set_config(CFG_ROM_EXTENDED_ENABLE, extended_enabled) != SC64_OK) {
|
||||
f_close(&fil);
|
||||
return FLASHCART_ERROR_INT;
|
||||
}
|
||||
|
||||
if (extended_enabled) {
|
||||
flashcart_error_t error = load_to_flash(&fil, (void *) (EXTENDED_ADDRESS), extended_size, &br);
|
||||
if (error != FLASHCART_OK) {
|
||||
f_close(&fil);
|
||||
return error;
|
||||
}
|
||||
if (br != extended_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 sc64_load_save (char *save_path) {
|
||||
void *address = NULL;
|
||||
sc64_save_type_t type;
|
||||
|
||||
if (sc64_get_config(CFG_SAVE_TYPE, &type) != SC64_OK) {
|
||||
return FLASHCART_ERROR_INT;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SAVE_TYPE_EEPROM_4K:
|
||||
case SAVE_TYPE_EEPROM_16K:
|
||||
address = (void *) (EEPROM_ADDRESS);
|
||||
break;
|
||||
case SAVE_TYPE_SRAM:
|
||||
case SAVE_TYPE_FLASHRAM:
|
||||
case SAVE_TYPE_SRAM_BANKED:
|
||||
address = (void *) (SRAM_FLASHRAM_ADDRESS);
|
||||
break;
|
||||
case SAVE_TYPE_NONE:
|
||||
default:
|
||||
return FLASHCART_ERROR_ARGS;
|
||||
}
|
||||
|
||||
FIL fil;
|
||||
UINT br;
|
||||
|
||||
if (f_open(&fil, save_path, FA_READ) != FR_OK) {
|
||||
return FLASHCART_ERROR_LOAD;
|
||||
}
|
||||
|
||||
size_t save_size = ALIGN(f_size(&fil), FS_SECTOR_SIZE);
|
||||
|
||||
if (f_read(&fil, address, save_size, &br) != FR_OK) {
|
||||
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 sc64_set_save_type (flashcart_save_type_t save_type) {
|
||||
sc64_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:
|
||||
type = SAVE_TYPE_SRAM;
|
||||
break;
|
||||
case FLASHCART_SAVE_TYPE_FLASHRAM:
|
||||
type = SAVE_TYPE_FLASHRAM;
|
||||
break;
|
||||
default:
|
||||
return FLASHCART_ERROR_ARGS;
|
||||
}
|
||||
|
||||
if (sc64_set_config(CFG_SAVE_TYPE, type) != SC64_OK) {
|
||||
return FLASHCART_ERROR_INT;
|
||||
}
|
||||
|
||||
return FLASHCART_OK;
|
||||
}
|
||||
|
||||
static flashcart_error_t sc64_set_save_writeback (uint32_t *sectors) {
|
||||
sc64_write_data(sectors, SC64_BUFFERS->BUFFER, 1024);
|
||||
|
||||
if (sc64_writeback_enable(SC64_BUFFERS->BUFFER) != SC64_OK) {
|
||||
return FLASHCART_ERROR_INT;
|
||||
}
|
||||
|
||||
return FLASHCART_OK;
|
||||
}
|
||||
|
||||
|
||||
static flashcart_t flashcart_sc64 = {
|
||||
.init = sc64_init,
|
||||
.deinit = sc64_deinit,
|
||||
.load_rom = sc64_load_rom,
|
||||
.load_save = sc64_load_save,
|
||||
.set_save_type = sc64_set_save_type,
|
||||
.set_save_writeback = sc64_set_save_writeback,
|
||||
};
|
||||
|
||||
|
||||
flashcart_t *sc64_get_flashcart (void) {
|
||||
return &flashcart_sc64;
|
||||
}
|
11
src/flashcart/sc64/sc64.h
Normal file
11
src/flashcart/sc64/sc64.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef FLASHCART_SC64_H__
|
||||
#define FLASHCART_SC64_H__
|
||||
|
||||
|
||||
#include "../flashcart.h"
|
||||
|
||||
|
||||
flashcart_t *sc64_get_flashcart (void);
|
||||
|
||||
|
||||
#endif
|
129
src/flashcart/sc64/sc64_internal.c
Normal file
129
src/flashcart/sc64/sc64_internal.c
Normal file
@ -0,0 +1,129 @@
|
||||
#include <libdragon.h>
|
||||
|
||||
#include "sc64_internal.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t SR_CMD;
|
||||
uint32_t DATA[2];
|
||||
uint32_t VERSION;
|
||||
uint32_t KEY;
|
||||
} sc64_regs_t;
|
||||
|
||||
#define SC64_REGS_BASE (0x1FFF0000UL)
|
||||
#define SC64_REGS ((sc64_regs_t *) SC64_REGS_BASE)
|
||||
|
||||
#define SC64_SR_IRQ_PENDING (1 << 29)
|
||||
#define SC64_SR_CMD_ERROR (1 << 30)
|
||||
#define SC64_SR_CPU_BUSY (1 << 31)
|
||||
|
||||
#define SC64_VERSION_2 (0x53437632)
|
||||
|
||||
#define SC64_KEY_RESET (0x00000000UL)
|
||||
#define SC64_KEY_UNLOCK_1 (0x5F554E4CUL)
|
||||
#define SC64_KEY_UNLOCK_2 (0x4F434B5FUL)
|
||||
#define SC64_KEY_LOCK (0xFFFFFFFFUL)
|
||||
|
||||
|
||||
typedef enum {
|
||||
CMD_ID_CONFIG_GET = 'c',
|
||||
CMD_ID_CONFIG_SET = 'C',
|
||||
CMD_ID_WRITEBACK_SD_INFO = 'W',
|
||||
CMD_ID_FLASH_PROGRAM = 'K',
|
||||
CMD_ID_FLASH_WAIT_BUSY = 'p',
|
||||
CMD_ID_FLASH_ERASE_BLOCK = 'P',
|
||||
} sc64_cmd_id_t;
|
||||
|
||||
typedef struct {
|
||||
sc64_cmd_id_t id;
|
||||
uint32_t arg[2];
|
||||
uint32_t rsp[2];
|
||||
} sc64_cmd_t;
|
||||
|
||||
|
||||
static sc64_error_t sc64_execute_cmd (sc64_cmd_t *cmd) {
|
||||
io_write((uint32_t) (&SC64_REGS->DATA[0]), cmd->arg[0]);
|
||||
io_write((uint32_t) (&SC64_REGS->DATA[1]), cmd->arg[1]);
|
||||
|
||||
io_write((uint32_t) (&SC64_REGS->SR_CMD), (cmd->id & 0xFF));
|
||||
|
||||
uint32_t sr;
|
||||
do {
|
||||
sr = io_read((uint32_t) (&SC64_REGS->SR_CMD));
|
||||
} while (sr & SC64_SR_CPU_BUSY);
|
||||
|
||||
if (sr & SC64_SR_CMD_ERROR) {
|
||||
return (sc64_error_t) (io_read((uint32_t) (&SC64_REGS->DATA[0])));
|
||||
}
|
||||
|
||||
cmd->rsp[0] = io_read((uint32_t) (&SC64_REGS->DATA[0]));
|
||||
cmd->rsp[1] = io_read((uint32_t) (&SC64_REGS->DATA[1]));
|
||||
|
||||
return SC64_OK;
|
||||
}
|
||||
|
||||
|
||||
void sc64_unlock (void) {
|
||||
io_write((uint32_t) (&SC64_REGS->KEY), SC64_KEY_RESET);
|
||||
io_write((uint32_t) (&SC64_REGS->KEY), SC64_KEY_UNLOCK_1);
|
||||
io_write((uint32_t) (&SC64_REGS->KEY), SC64_KEY_UNLOCK_2);
|
||||
}
|
||||
|
||||
void sc64_lock (void) {
|
||||
io_write((uint32_t) (&SC64_REGS->KEY), SC64_KEY_LOCK);
|
||||
}
|
||||
|
||||
bool sc64_check_presence (void) {
|
||||
return (io_read((uint32_t) (&SC64_REGS->VERSION)) == SC64_VERSION_2);
|
||||
}
|
||||
|
||||
void sc64_read_data (void *src, void *dst, size_t length) {
|
||||
data_cache_hit_writeback_invalidate(dst, length);
|
||||
dma_read_raw_async(dst, (uint32_t) (src), length);
|
||||
dma_wait();
|
||||
}
|
||||
|
||||
void sc64_write_data (void *src, void *dst, size_t length) {
|
||||
data_cache_hit_writeback(src, length);
|
||||
dma_write_raw_async(src, (uint32_t) (dst), length);
|
||||
dma_wait();
|
||||
}
|
||||
|
||||
sc64_error_t sc64_get_config (sc64_cfg_t id, void *value) {
|
||||
sc64_cmd_t cmd = { .id = CMD_ID_CONFIG_GET, .arg = { id } };
|
||||
sc64_error_t error = sc64_execute_cmd(&cmd);
|
||||
*((uint32_t *) (value)) = cmd.rsp[1];
|
||||
return error;
|
||||
}
|
||||
|
||||
sc64_error_t sc64_set_config (sc64_cfg_t id, uint32_t value) {
|
||||
sc64_cmd_t cmd = { .id = CMD_ID_CONFIG_SET, .arg = { id, value } };
|
||||
return sc64_execute_cmd(&cmd);
|
||||
}
|
||||
|
||||
sc64_error_t sc64_writeback_enable (void *address) {
|
||||
sc64_cmd_t cmd = { .id = CMD_ID_WRITEBACK_SD_INFO, .arg = { (uint32_t) (address) } };
|
||||
return sc64_execute_cmd(&cmd);
|
||||
}
|
||||
|
||||
sc64_error_t sc64_flash_program (void *address, size_t length) {
|
||||
sc64_cmd_t cmd = { .id = CMD_ID_FLASH_PROGRAM, .arg = { (uint32_t) (address), length } };
|
||||
return sc64_execute_cmd(&cmd);
|
||||
}
|
||||
|
||||
sc64_error_t sc64_flash_wait_busy (void) {
|
||||
sc64_cmd_t cmd = { .id = CMD_ID_FLASH_WAIT_BUSY, .arg = { true } };
|
||||
return sc64_execute_cmd(&cmd);
|
||||
}
|
||||
|
||||
sc64_error_t sc64_flash_get_erase_block_size (size_t *erase_block_size) {
|
||||
sc64_cmd_t cmd = { .id = CMD_ID_FLASH_WAIT_BUSY, .arg = { false } };
|
||||
sc64_error_t error = sc64_execute_cmd(&cmd);
|
||||
*erase_block_size = (size_t) (cmd.rsp[0]);
|
||||
return error;
|
||||
}
|
||||
|
||||
sc64_error_t sc64_flash_erase_block (void *address) {
|
||||
sc64_cmd_t cmd = { .id = CMD_ID_FLASH_ERASE_BLOCK, .arg = { (uint32_t) (address) } };
|
||||
return sc64_execute_cmd(&cmd);
|
||||
}
|
72
src/flashcart/sc64/sc64_internal.h
Normal file
72
src/flashcart/sc64/sc64_internal.h
Normal file
@ -0,0 +1,72 @@
|
||||
#ifndef FLASHCART_SC64_INTERNAL_H__
|
||||
#define FLASHCART_SC64_INTERNAL_H__
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t BUFFER[8192];
|
||||
uint8_t EEPROM[2048];
|
||||
uint8_t DD_SECTOR[256];
|
||||
uint8_t FLASHRAM[128];
|
||||
} sc64_buffers_t;
|
||||
|
||||
#define SC64_BUFFERS_BASE (0x1FFE0000UL)
|
||||
#define SC64_BUFFERS ((sc64_buffers_t *) SC64_BUFFERS_BASE)
|
||||
|
||||
|
||||
typedef enum {
|
||||
SC64_OK,
|
||||
SC64_ERROR_BAD_ARGUMENT,
|
||||
SC64_ERROR_BAD_ADDRESS,
|
||||
SC64_ERROR_BAD_CONFIG_ID,
|
||||
SC64_ERROR_TIMEOUT,
|
||||
SC64_ERROR_SD_CARD,
|
||||
SC64_ERROR_UNKNOWN_CMD = -1
|
||||
} sc64_error_t;
|
||||
|
||||
typedef enum {
|
||||
CFG_BOOTLOADER_SWITCH,
|
||||
CFG_ROM_WRITE_ENABLE,
|
||||
CFG_ROM_SHADOW_ENABLE,
|
||||
CFG_DD_MODE,
|
||||
CFG_ISV_ENABLE,
|
||||
CFG_BOOT_MODE,
|
||||
CFG_SAVE_TYPE,
|
||||
CFG_CIC_SEED,
|
||||
CFG_TV_TYPE,
|
||||
CFG_DD_SD_ENABLE,
|
||||
CFG_DD_DRIVE_TYPE,
|
||||
CFG_DD_DISK_STATE,
|
||||
CFG_BUTTON_STATE,
|
||||
CFG_BUTTON_MODE,
|
||||
CFG_ROM_EXTENDED_ENABLE,
|
||||
} sc64_cfg_t;
|
||||
|
||||
typedef enum {
|
||||
SAVE_TYPE_NONE,
|
||||
SAVE_TYPE_EEPROM_4K,
|
||||
SAVE_TYPE_EEPROM_16K,
|
||||
SAVE_TYPE_SRAM,
|
||||
SAVE_TYPE_FLASHRAM,
|
||||
SAVE_TYPE_SRAM_BANKED,
|
||||
} sc64_save_type_t;
|
||||
|
||||
|
||||
void sc64_unlock (void);
|
||||
void sc64_lock (void);
|
||||
bool sc64_check_presence (void);
|
||||
void sc64_read_data (void *src, void *dst, size_t length);
|
||||
void sc64_write_data (void *src, void *dst, size_t length);
|
||||
sc64_error_t sc64_get_config (sc64_cfg_t cfg, void *value);
|
||||
sc64_error_t sc64_set_config (sc64_cfg_t cfg, uint32_t value);
|
||||
sc64_error_t sc64_writeback_enable (void *address);
|
||||
sc64_error_t sc64_flash_program (void *address, size_t length);
|
||||
sc64_error_t sc64_flash_wait_busy (void);
|
||||
sc64_error_t sc64_flash_get_erase_block_size (size_t *erase_block_size);
|
||||
sc64_error_t sc64_flash_erase_block (void *address);
|
||||
|
||||
|
||||
#endif
|
43
src/main.c
Normal file
43
src/main.c
Normal file
@ -0,0 +1,43 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <libdragon.h>
|
||||
#include <usb.h>
|
||||
|
||||
#include "boot/boot.h"
|
||||
#include "flashcart/flashcart.h"
|
||||
#include "menu/menu.h"
|
||||
|
||||
|
||||
static void init (void) {
|
||||
assertf(usb_initialize() != CART_NONE, "No flashcart was detected");
|
||||
assertf(debug_init_sdfs("sd:/", -1), "Couldn't initialize SD card");
|
||||
assertf(flashcart_init(), "Couldn't initialize flashcart");
|
||||
}
|
||||
|
||||
static void deinit (void) {
|
||||
flashcart_deinit();
|
||||
rdp_close();
|
||||
rspq_close();
|
||||
audio_close();
|
||||
display_close();
|
||||
disable_interrupts();
|
||||
}
|
||||
|
||||
|
||||
int main (void) {
|
||||
menu_t menu;
|
||||
|
||||
init();
|
||||
|
||||
if (boot_is_warm()) {
|
||||
menu_restore(&menu);
|
||||
}
|
||||
|
||||
menu_run(&menu);
|
||||
|
||||
deinit();
|
||||
|
||||
boot(&menu.boot_params);
|
||||
|
||||
return 1;
|
||||
}
|
82
src/menu/menu.c
Normal file
82
src/menu/menu.c
Normal file
@ -0,0 +1,82 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <fatfs/ff.h>
|
||||
#include <libdragon.h>
|
||||
|
||||
#include "flashcart/flashcart.h"
|
||||
|
||||
#include "menu.h"
|
||||
|
||||
|
||||
static char *config_path = "n64/config.txt";
|
||||
static char game_path[256];
|
||||
static char save_path[256];
|
||||
static flashcart_save_type_t save_type = FLASHCART_SAVE_TYPE_NONE;
|
||||
static bool save_writeback = false;
|
||||
|
||||
|
||||
static void get_string (char *buffer, size_t size, FIL *fil) {
|
||||
UINT br;
|
||||
char c;
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
assertf(f_read(fil, &c, 1, &br) == FR_OK, "Couldn't read string");
|
||||
if (br == 0 || c == '\r' || c == '\n') {
|
||||
*buffer = '\0';
|
||||
return;
|
||||
}
|
||||
*buffer++ = c;
|
||||
}
|
||||
}
|
||||
|
||||
static void load_config (void) {
|
||||
FIL fil;
|
||||
char buffer[266];
|
||||
|
||||
assertf(f_open(&fil, config_path, FA_READ) == FR_OK, "Couldn't open config file");
|
||||
|
||||
while (!f_eof(&fil)) {
|
||||
get_string(buffer, sizeof(buffer), &fil);
|
||||
if (strncmp("game_path=", buffer, 10) == 0) {
|
||||
strncpy(game_path, (buffer + 10), sizeof(game_path));
|
||||
} else if (strncmp("save_path=", buffer, 10) == 0) {
|
||||
strncpy(save_path, (buffer + 10), sizeof(save_path));
|
||||
save_writeback = true;
|
||||
} else if (strncmp("save_type=", buffer, 10) == 0) {
|
||||
save_type = (buffer[10] - '0');
|
||||
assertf(save_type < __FLASHCART_SAVE_TYPE_END, "Invalid save type in config file");
|
||||
}
|
||||
}
|
||||
|
||||
assertf(f_close(&fil) == FR_OK, "Couldn't close config file");
|
||||
}
|
||||
|
||||
|
||||
void menu_restore (menu_t *menu) {
|
||||
// TODO: restore last menu state from SD card
|
||||
}
|
||||
|
||||
void menu_run (menu_t *menu) {
|
||||
// TODO: implement nice user interface here
|
||||
|
||||
console_init();
|
||||
console_set_debug(true);
|
||||
|
||||
load_config();
|
||||
|
||||
printf("N64 Flashcart Menu\n\n");
|
||||
|
||||
printf("Loading ROM: %s\n", game_path);
|
||||
assertf(flashcart_load_rom(game_path) == FLASHCART_OK, "ROM load error");
|
||||
|
||||
printf("Loading save: %s, type: %d, writeback: %d\n", save_path, save_type, save_writeback);
|
||||
assertf(flashcart_load_save(save_path, save_type, save_writeback) == FLASHCART_OK, "Save load error");
|
||||
|
||||
// TODO: write menu state to SD card
|
||||
|
||||
menu->boot_params.device_type = BOOT_DEVICE_TYPE_ROM;
|
||||
menu->boot_params.reset_type = BOOT_RESET_TYPE_NMI;
|
||||
menu->boot_params.detect_tv_type = true;
|
||||
menu->boot_params.detect_cic_seed = true;
|
||||
}
|
17
src/menu/menu.h
Normal file
17
src/menu/menu.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef MENU_H__
|
||||
#define MENU_H__
|
||||
|
||||
|
||||
#include "boot/boot.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
boot_params_t boot_params;
|
||||
} menu_t;
|
||||
|
||||
|
||||
void menu_restore (menu_t *menu);
|
||||
void menu_run (menu_t *menu);
|
||||
|
||||
|
||||
#endif
|
90
src/utils/fs.c
Normal file
90
src/utils/fs.c
Normal file
@ -0,0 +1,90 @@
|
||||
#include <fatfs/ff.h>
|
||||
|
||||
#include "fs.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
bool file_exists (char *path) {
|
||||
FRESULT fr;
|
||||
FILINFO fno;
|
||||
|
||||
fr = f_stat(path, &fno);
|
||||
|
||||
return (fr == FR_OK);
|
||||
}
|
||||
|
||||
size_t file_get_size (char *path) {
|
||||
FILINFO fno;
|
||||
|
||||
if (f_stat(path, &fno) != FR_OK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (size_t) (fno.fsize);
|
||||
}
|
||||
|
||||
bool file_allocate (char *path, size_t size) {
|
||||
FIL fil;
|
||||
bool error = false;
|
||||
|
||||
if (f_open(&fil, path, FA_WRITE | FA_CREATE_NEW) != FR_OK) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (f_lseek(&fil, size) != FR_OK) {
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (f_tell(&fil) != size) {
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (f_close(&fil) != FR_OK) {
|
||||
error = true;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
bool file_get_sectors (char *path, uint32_t *sectors, size_t entries) {
|
||||
FATFS *fs;
|
||||
FIL fil;
|
||||
bool error = false;
|
||||
|
||||
for (int i = 0; i < entries; i++) {
|
||||
sectors[i] = 0;
|
||||
}
|
||||
|
||||
if (f_open(&fil, path, FA_READ) != FR_OK) {
|
||||
return true;
|
||||
}
|
||||
|
||||
fs = fil.obj.fs;
|
||||
|
||||
uint32_t file_sector_entries = (ALIGN(f_size(&fil), FS_SECTOR_SIZE) / FS_SECTOR_SIZE);
|
||||
file_sector_entries = (file_sector_entries > entries) ? entries : file_sector_entries;
|
||||
|
||||
uint32_t cluster_sector = 0;
|
||||
|
||||
for (int file_sector = 0; file_sector < file_sector_entries; file_sector++) {
|
||||
if ((file_sector % fs->csize) == 0) {
|
||||
if ((f_lseek(&fil, (file_sector * FS_SECTOR_SIZE) + (FS_SECTOR_SIZE / 2))) != FR_OK) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
uint32_t cluster = fil.clust;
|
||||
if (cluster >= fs->n_fatent) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
cluster_sector = (fs->database + ((LBA_t) (fs->csize) * (cluster - 2)));
|
||||
}
|
||||
*sectors++ = (cluster_sector + (file_sector % fs->csize));
|
||||
}
|
||||
|
||||
if (f_close(&fil) != FR_OK) {
|
||||
error = true;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
19
src/utils/fs.h
Normal file
19
src/utils/fs.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef UTILS_FS_H__
|
||||
#define UTILS_FS_H__
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#define FS_SECTOR_SIZE (512)
|
||||
|
||||
|
||||
bool file_exists (char *path);
|
||||
size_t file_get_size (char *path);
|
||||
bool file_allocate (char *path, size_t size);
|
||||
bool file_get_sectors (char *path, uint32_t *sectors, size_t entries);
|
||||
|
||||
|
||||
#endif
|
14
src/utils/utils.h
Normal file
14
src/utils/utils.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef UTILS_H__
|
||||
#define UTILS_H__
|
||||
|
||||
|
||||
#define ALIGN(x, a) (((x) + ((typeof(x))(a) - 1)) & ~((typeof(x))(a) - 1))
|
||||
|
||||
#define MAX(a,b) ({ typeof(a) _a = a; typeof(b) _b = b; _a > _b ? _a : _b; })
|
||||
#define MIN(a,b) ({ typeof(a) _a = a; typeof(b) _b = b; _a < _b ? _a : _b; })
|
||||
|
||||
#define KiB(x) ((x) * 1024)
|
||||
#define MiB(x) ((x) * 1024 * 1024)
|
||||
|
||||
|
||||
#endif
|
38
tools/finalize.py
Normal file
38
tools/finalize.py
Normal file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if (len(sys.argv) != 2):
|
||||
print(f'Usage: python {sys.argv[0]} input')
|
||||
sys.exit(1)
|
||||
|
||||
ALIGN = 512
|
||||
STRIP_BYTE = b'\x00'
|
||||
|
||||
input_file = sys.argv[1]
|
||||
sc64_output = 'sc64menu.n64'
|
||||
|
||||
try:
|
||||
bin_data = b''
|
||||
|
||||
with open(input_file, 'rb') as f:
|
||||
bin_data = f.read()
|
||||
|
||||
bin_data = bin_data.strip(STRIP_BYTE)
|
||||
modulo = len(bin_data) % ALIGN
|
||||
if (modulo > 0):
|
||||
bin_data += STRIP_BYTE * (ALIGN - modulo)
|
||||
|
||||
with open(sc64_output, 'wb') as f:
|
||||
f.write(bin_data)
|
||||
|
||||
except FileNotFoundError as e:
|
||||
print(f'Couldn\'t open file: {e}')
|
||||
sys.exit(2)
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
sys.exit(3)
|
Loading…
Reference in New Issue
Block a user