From 992fcf49236748fadeda8b0e53d18f45f1277b3b Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Tue, 14 Feb 2023 22:26:40 +0100 Subject: [PATCH] Initial version (no UI) --- .gitignore | 2 + .gitmodules | 4 + .libdragon/config.json | 5 + Makefile | 35 +++++ README.md | 3 + build.sh | 13 ++ libdragon | 1 + src/boot/boot.c | 178 +++++++++++++++++++++ src/boot/boot.h | 40 +++++ src/boot/boot_io.h | 234 ++++++++++++++++++++++++++++ src/boot/crc32.c | 50 ++++++ src/boot/crc32.h | 12 ++ src/boot/ipl2.S | 17 ++ src/flashcart/flashcart.c | 90 +++++++++++ src/flashcart/flashcart.h | 43 ++++++ src/flashcart/sc64/sc64.c | 240 +++++++++++++++++++++++++++++ src/flashcart/sc64/sc64.h | 11 ++ src/flashcart/sc64/sc64_internal.c | 129 ++++++++++++++++ src/flashcart/sc64/sc64_internal.h | 72 +++++++++ src/main.c | 43 ++++++ src/menu/menu.c | 82 ++++++++++ src/menu/menu.h | 17 ++ src/utils/fs.c | 90 +++++++++++ src/utils/fs.h | 19 +++ src/utils/utils.h | 14 ++ tools/finalize.py | 38 +++++ 26 files changed, 1482 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .libdragon/config.json create mode 100644 Makefile create mode 100644 README.md create mode 100644 build.sh create mode 160000 libdragon create mode 100644 src/boot/boot.c create mode 100644 src/boot/boot.h create mode 100644 src/boot/boot_io.h create mode 100644 src/boot/crc32.c create mode 100644 src/boot/crc32.h create mode 100644 src/boot/ipl2.S create mode 100644 src/flashcart/flashcart.c create mode 100644 src/flashcart/flashcart.h create mode 100644 src/flashcart/sc64/sc64.c create mode 100644 src/flashcart/sc64/sc64.h create mode 100644 src/flashcart/sc64/sc64_internal.c create mode 100644 src/flashcart/sc64/sc64_internal.h create mode 100644 src/main.c create mode 100644 src/menu/menu.c create mode 100644 src/menu/menu.h create mode 100644 src/utils/fs.c create mode 100644 src/utils/fs.h create mode 100644 src/utils/utils.h create mode 100644 tools/finalize.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..00a62912 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.vscode +/build diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..be699630 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "libdragon"] + path = libdragon + url = https://github.com/DragonMinded/libdragon + branch = trunk diff --git a/.libdragon/config.json b/.libdragon/config.json new file mode 100644 index 00000000..d66db2b9 --- /dev/null +++ b/.libdragon/config.json @@ -0,0 +1,5 @@ +{ + "imageName": "ghcr.io/dragonminded/libdragon:latest", + "vendorDirectory": "libdragon", + "vendorStrategy": "submodule" +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..1e7b6ee9 --- /dev/null +++ b/Makefile @@ -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) diff --git a/README.md b/README.md new file mode 100644 index 00000000..9bf37934 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# N64FlashcartMenu + +Nothing to see here, come back later. diff --git a/build.sh b/build.sh new file mode 100644 index 00000000..15fa4d86 --- /dev/null +++ b/build.sh @@ -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 diff --git a/libdragon b/libdragon new file mode 160000 index 00000000..2413aacd --- /dev/null +++ b/libdragon @@ -0,0 +1 @@ +Subproject commit 2413aacd5c3119fe9c3ef9b8fa5e4e965a15cc01 diff --git a/src/boot/boot.c b/src/boot/boot.c new file mode 100644 index 00000000..9814433c --- /dev/null +++ b/src/boot/boot.c @@ -0,0 +1,178 @@ +#include + +#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); +} diff --git a/src/boot/boot.h b/src/boot/boot.h new file mode 100644 index 00000000..ef18dc31 --- /dev/null +++ b/src/boot/boot.h @@ -0,0 +1,40 @@ +#ifndef BOOT_H__ +#define BOOT_H__ + + +#include +#include + + +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 diff --git a/src/boot/boot_io.h b/src/boot/boot_io.h new file mode 100644 index 00000000..7a532581 --- /dev/null +++ b/src/boot/boot_io.h @@ -0,0 +1,234 @@ +#ifndef BOOT_IO_H__ +#define BOOT_IO_H__ + + +#include +#include + + +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 diff --git a/src/boot/crc32.c b/src/boot/crc32.c new file mode 100644 index 00000000..5ae99580 --- /dev/null +++ b/src/boot/crc32.c @@ -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); +} diff --git a/src/boot/crc32.h b/src/boot/crc32.h new file mode 100644 index 00000000..b3294a88 --- /dev/null +++ b/src/boot/crc32.h @@ -0,0 +1,12 @@ +#ifndef CRC32_H__ +#define CRC32_H__ + + +#include +#include + + +uint32_t crc32_calculate (void *buffer, size_t length); + + +#endif diff --git a/src/boot/ipl2.S b/src/boot/ipl2.S new file mode 100644 index 00000000..a3e9277c --- /dev/null +++ b/src/boot/ipl2.S @@ -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 diff --git a/src/flashcart/flashcart.c b/src/flashcart/flashcart.c new file mode 100644 index 00000000..18ecedcc --- /dev/null +++ b/src/flashcart/flashcart.c @@ -0,0 +1,90 @@ +#include + +#include + +#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; +} diff --git a/src/flashcart/flashcart.h b/src/flashcart/flashcart.h new file mode 100644 index 00000000..1973924f --- /dev/null +++ b/src/flashcart/flashcart.h @@ -0,0 +1,43 @@ +#ifndef FLASHCART_H__ +#define FLASHCART_H__ + + +#include +#include + + +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 diff --git a/src/flashcart/sc64/sc64.c b/src/flashcart/sc64/sc64.c new file mode 100644 index 00000000..960eb91b --- /dev/null +++ b/src/flashcart/sc64/sc64.c @@ -0,0 +1,240 @@ +#include +#include +#include + +#include +#include + +#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; +} diff --git a/src/flashcart/sc64/sc64.h b/src/flashcart/sc64/sc64.h new file mode 100644 index 00000000..fff87bb2 --- /dev/null +++ b/src/flashcart/sc64/sc64.h @@ -0,0 +1,11 @@ +#ifndef FLASHCART_SC64_H__ +#define FLASHCART_SC64_H__ + + +#include "../flashcart.h" + + +flashcart_t *sc64_get_flashcart (void); + + +#endif diff --git a/src/flashcart/sc64/sc64_internal.c b/src/flashcart/sc64/sc64_internal.c new file mode 100644 index 00000000..bda9502c --- /dev/null +++ b/src/flashcart/sc64/sc64_internal.c @@ -0,0 +1,129 @@ +#include + +#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); +} diff --git a/src/flashcart/sc64/sc64_internal.h b/src/flashcart/sc64/sc64_internal.h new file mode 100644 index 00000000..c52d2d8c --- /dev/null +++ b/src/flashcart/sc64/sc64_internal.h @@ -0,0 +1,72 @@ +#ifndef FLASHCART_SC64_INTERNAL_H__ +#define FLASHCART_SC64_INTERNAL_H__ + + +#include +#include + + +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 diff --git a/src/main.c b/src/main.c new file mode 100644 index 00000000..39aab9f8 --- /dev/null +++ b/src/main.c @@ -0,0 +1,43 @@ +#include + +#include +#include + +#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; +} diff --git a/src/menu/menu.c b/src/menu/menu.c new file mode 100644 index 00000000..b005aa71 --- /dev/null +++ b/src/menu/menu.c @@ -0,0 +1,82 @@ +#include +#include + +#include +#include + +#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; +} diff --git a/src/menu/menu.h b/src/menu/menu.h new file mode 100644 index 00000000..e0b1e98c --- /dev/null +++ b/src/menu/menu.h @@ -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 diff --git a/src/utils/fs.c b/src/utils/fs.c new file mode 100644 index 00000000..a78ffb1d --- /dev/null +++ b/src/utils/fs.c @@ -0,0 +1,90 @@ +#include + +#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; +} diff --git a/src/utils/fs.h b/src/utils/fs.h new file mode 100644 index 00000000..a229dae7 --- /dev/null +++ b/src/utils/fs.h @@ -0,0 +1,19 @@ +#ifndef UTILS_FS_H__ +#define UTILS_FS_H__ + + +#include +#include +#include + + +#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 diff --git a/src/utils/utils.h b/src/utils/utils.h new file mode 100644 index 00000000..efef1366 --- /dev/null +++ b/src/utils/utils.h @@ -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 diff --git a/tools/finalize.py b/tools/finalize.py new file mode 100644 index 00000000..cdc02dd5 --- /dev/null +++ b/tools/finalize.py @@ -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)