This commit is contained in:
Polprzewodnikowy 2021-11-30 00:47:35 +01:00
parent 3d1daf3fc6
commit f8ff05c79b
26 changed files with 681 additions and 217 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
**/.vscode
*.bak
*.zip
.DS_Store

View File

@ -21,6 +21,8 @@ BUILT_RELEASE=false
FORCE_CLEAN=false
SKIP_FPGA_REBUILD=false
DEBUG_ENABLED=false
USER_FLAGS+=" -D__SC64_VERSION=\"$__SC64_VERSION\""
build_cic () {
if [ "$BUILT_CIC" = true ]; then return; fi
@ -39,7 +41,7 @@ build_n64 () {
if [ "$FORCE_CLEAN" = true ]; then
make clean
fi
make all -j USER_FLAGS="-D__SC64_VERSION=\"$__SC64_VERSION\""
make all -j USER_FLAGS="$USER_FLAGS"
popd > /dev/null
BUILT_N64=true
@ -52,7 +54,7 @@ build_riscv () {
if [ "$FORCE_CLEAN" = true ]; then
make clean -j
fi
make all USER_FLAGS="-D__SC64_VERSION=\"$__SC64_VERSION\""
make all USER_FLAGS="$USER_FLAGS"
popd > /dev/null
BUILT_RISCV=true
@ -68,7 +70,11 @@ build_fpga () {
if [ "$SKIP_FPGA_REBUILD" = true ] && [ -f output_files/SummerCart64.sof ]; then
quartus_cpf -c SummerCart64.cof
else
if [ "$DEBUG_ENABLED" = true ]; then
quartus_sh --set VERILOG_MACRO="DEBUG" ./SummerCart64.qpf
fi
quartus_sh --flow compile ./SummerCart64.qpf
quartus_sh --set -remove VERILOG_MACRO="DEBUG" ./SummerCart64.qpf
fi
popd > /dev/null
@ -105,7 +111,7 @@ build_release () {
print_usage () {
echo "builder script for SummerCart64"
echo "usage: ./build.sh [cic] [n64] [riscv] [fpga] [update] [release] [-c] [-s] [--help]"
echo "usage: ./build.sh [cic] [n64] [riscv] [fpga] [update] [release] [-c] [-s] [-d] [--help]"
echo "parameters:"
echo " cic - assemble UltraCIC-III software"
echo " n64 - compile N64 bootloader software"
@ -117,6 +123,8 @@ print_usage () {
echo " - clean software compilation result directories before build"
echo " -s | --skip-fpga-rebuild"
echo " - do not recompile whole FPGA design if it's already done, just update software binaries"
echo " -d | --debug"
echo " - enable debug features"
echo " --help - print this guide"
}
@ -160,6 +168,9 @@ while test $# -gt 0; do
-s|--skip-fpga-rebuild)
SKIP_FPGA_REBUILD=true
;;
-d|--debug)
DEBUG_ENABLED=true
;;
--help)
print_usage
exit 0
@ -174,6 +185,7 @@ while test $# -gt 0; do
shift
done
if [ "$DEBUG_ENABLED" = true ]; then USER_FLAGS+=" -DDEBUG"; fi
if [ "$TRIGGER_CIC" = true ]; then build_cic; fi
if [ "$TRIGGER_N64" = true ]; then build_n64; fi
if [ "$TRIGGER_RISCV" = true ]; then build_riscv; fi

View File

@ -3,10 +3,10 @@
GIT_SHA=$(git rev-parse --short HEAD)
GIT_TAG=$(git describe --tags --exact-match 2> /dev/null)
if [ ! -z "$GIT_TAG" ]; then
__SC64_VERSION="git tag: $GIT_TAG"
if [ ! -z $GIT_TAG ]; then
__SC64_VERSION=$(printf "%.7q\ %.7q" $GIT_SHA $GIT_TAG)
else
__SC64_VERSION="git sha: $GIT_SHA"
__SC64_VERSION=$(printf "%.7q\ develop" $GIT_SHA)
fi
docker run \

View File

@ -33,7 +33,12 @@ package sc64;
parameter bit [31:0] SC64_VER = 32'h53437632;
parameter int CLOCK_FREQUENCY = 32'd100_000_000;
parameter bit [31:0] CPU_RESET_VECTOR = {4'(ID_CPU_FLASH), 28'h0035800};
parameter bit CPU_HAS_UART = 1'b0;
parameter int UART_BAUD_RATE = 32'd1_000_000;
`ifdef DEBUG
parameter bit CPU_HAS_UART = 1'b1;
`else
parameter bit CPU_HAS_UART = 1'b0;
`endif
endpackage

1
sw/n64/.gitignore vendored
View File

@ -1,2 +1 @@
/build
*.z64

View File

@ -6,13 +6,14 @@ OBJDUMP = $(TOOLCHAIN)objdump
SIZE = $(TOOLCHAIN)size
FLAGS = -march=vr4300 -mtune=vr4300 -falign-functions=32 $(USER_FLAGS)
CFLAGS = -Os -Wall -ffunction-sections -fdata-sections -ffreestanding -MMD -MP
CFLAGS = -Os -Wall -ffunction-sections -fdata-sections -ffreestanding -G 0 -MMD -MP
ASFLAGS = -Wa,-I$(N64_INST)/mips64-elf/lib
LDFLAGS = -lc -nostartfiles -Wl,--gc-sections
SRC_DIR = src
BUILD_DIR = build
SRC_FILES = startup.S main.c sc64.c boot.c crc32.c
SRC_FILES = startup.S main.c sys.c sc64.c syscalls.c crc32.c
SRCS = $(addprefix $(SRC_DIR)/, $(SRC_FILES))
OBJS = $(addprefix $(BUILD_DIR)/, $(notdir $(patsubst %,%.o,$(SRCS))))
@ -23,7 +24,7 @@ VPATH = $(SRC_DIR)
$(@info $(shell mkdir -p ./$(BUILD_DIR) &> /dev/null))
$(BUILD_DIR)/%.S.o: %.S
$(CC) -x assembler-with-cpp $(FLAGS) $(CFLAGS) -c $< -o $@
$(CC) -x assembler-with-cpp $(FLAGS) $(ASFLAGS) $(CFLAGS) -c $< -o $@
$(BUILD_DIR)/%.c.o: %.c
$(CC) $(FLAGS) $(CFLAGS) -c $< -o $@
@ -34,8 +35,8 @@ $(BUILD_DIR)/n64boot.elf: $(OBJS) N64.ld
$(BUILD_DIR)/n64boot.bin: $(BUILD_DIR)/n64boot.elf
@$(OBJCOPY) -O binary $< $@
@truncate --size=90k $@
@chksum64 $@ > /dev/null
@truncate --size=90k $@
$(BUILD_DIR)/n64boot.hex: $(BUILD_DIR)/n64boot.bin
@$(OBJCOPY) -I binary -O ihex $< $@

View File

@ -5,43 +5,43 @@ MEMORY {
ENTRY(entry_handler)
__stack_size = 64k;
SECTIONS {
.flash : {
KEEP(*(.text.bootcode));
KEEP(*(.text.rom_header));
KEEP(*(.text.ipl3));
__ipl3_font = LOADADDR(.flash) + 0xB70;
} > flash
.text : {
*(.text.entry_handler)
*(.text .text.* .gnu.linkonce.t.*)
*(.rdata .rodata .rodata.* .gnu.linkonce.r.*)
} > rdram AT > flash
.data : {
. = ALIGN(8);
_sdata = .;
*(.rodata .rodata.* .gnu.linkonce.r.*)
*(.data .data.* .gnu.linkonce.d.*)
. = ALIGN(8);
_ssdata = .;
_gp = . + 0x8000;
*(.sdata .sdata.* .gnu.linkonce.s.*)
*(.lit8)
*(.lit4)
. = ALIGN(4);
} > rdram AT > flash
.bss : {
. = ALIGN(8);
_sbss = .;
*(.sbss .sbss.* .gnu.linkonce.sb.*)
*(.scommon .scommon.*)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(8);
_ebss = .;
. = ALIGN(4);
} > rdram
_sheap = .;
. = ORIGIN(rdram) + LENGTH(rdram) - __stack_size;
_eheap = .;
. += __stack_size;
_sp = .;
/DISCARD/ : {
*(.MIPS.*)
}
_gp = MIN(_ssdata + 0x8000, MAX(_sdata + 0x8000, _ebss - 0x8000));
_sp = ORIGIN(rdram) + LENGTH(rdram);
}

Binary file not shown.

View File

@ -1,7 +1,7 @@
#include "boot.h"
#include "crc32.h"
#include "n64_regs.h"
// #include "sc64.h"
#include "sc64.h"
static const struct crc32_to_cic_seed {
@ -27,8 +27,14 @@ static cart_header_t global_cart_header __attribute__((aligned(16)));
cart_header_t *boot_load_cart_header(void) {
cart_header_t *cart_header_pointer = &global_cart_header;
platform_pi_dma_read(cart_header_pointer, CART_BASE, sizeof(cart_header_t));
platform_cache_invalidate(cart_header_pointer, sizeof(cart_header_t));
uint32_t *src = (uint32_t *)(CART_BASE);
uint32_t *dst = (uint32_t *) cart_header_pointer;
for (int i = 0; i < sizeof(cart_header_t); i += 4) {
*dst++ = pio_read(src++);
// while (PI->status & 0x03);
// ((uint32_t *)cart_header_pointer)[i] = (((uint32_t *)(CART_BASE))[i]);
}
return cart_header_pointer;
}
@ -74,7 +80,7 @@ tv_type_t boot_get_tv_type(cart_header_t *cart_header) {
}
}
void boot(cart_header_t *cart_header, uint16_t cic_seed, tv_type_t tv_type) {
__attribute__((noreturn)) void bootX(cart_header_t *cart_header, uint16_t cic_seed, tv_type_t tv_type) {
uint32_t is_x105_boot = (cic_seed == crc32_to_cic_seed[5].cic_seed);
uint32_t is_ddipl_boot = (
(cic_seed == crc32_to_cic_seed[7].cic_seed) ||
@ -140,11 +146,7 @@ void boot(cart_header_t *cart_header, uint16_t cic_seed, tv_type_t tv_type) {
gpr_regs[CPU_REG_S7] = BOOT_SEED_OS_VERSION(cic_seed);
gpr_regs[CPU_REG_SP] = CPU_ADDRESS_IN_REG(SP_MEM->imem[ARRAY_ITEMS(SP_MEM->imem) - 4]);
gpr_regs[CPU_REG_RA] = CPU_ADDRESS_IN_REG(SP_MEM->imem[(os_tv_type == TV_PAL) ? 341 : 340]);
// sc64_print_debug((uint32_t) gpr_regs[CPU_REG_T3], (uint32_t) gpr_regs[CPU_REG_S3], (uint32_t) gpr_regs[CPU_REG_S4]);
// sc64_print_debug((uint32_t) gpr_regs[CPU_REG_S5], (uint32_t) gpr_regs[CPU_REG_S6], (uint32_t) gpr_regs[CPU_REG_S7]);
// sc64_print_debug((uint32_t) gpr_regs[CPU_REG_SP], (uint32_t) gpr_regs[CPU_REG_RA], 0);
// return;
__asm__ (
".set noat \n\t"
".set noreorder \n\t"
@ -175,7 +177,7 @@ void boot(cart_header_t *cart_header, uint16_t cic_seed, tv_type_t tv_type) {
"nop \n\t"
"ctc1 $t0, $31 \n\t"
"nop \n\t"
"add $ra, $zero, %[gpr_regs] \n\t"
"move $ra, %[gpr_regs] \n\t"
"ld $at, 0x08($ra) \n\t"
"ld $v0, 0x10($ra) \n\t"
"ld $v1, 0x18($ra) \n\t"
@ -213,6 +215,4 @@ void boot(cart_header_t *cart_header, uint16_t cic_seed, tv_type_t tv_type) {
: [gpr_regs] "r" (gpr_regs)
: "t0", "ra"
);
while (1);
}

View File

@ -39,6 +39,12 @@ struct os_boot_config_s {
typedef struct os_boot_config_s os_boot_config_t;
typedef enum {
TV_PAL = 0,
TV_NTSC = 1,
TV_MPAL = 2,
} tv_type_t;
#define OS_BOOT_CONFIG_BASE (0xA0000300)
#define OS_BOOT_CONFIG ((os_boot_config_t *) OS_BOOT_CONFIG_BASE)
@ -57,7 +63,7 @@ typedef struct os_boot_config_s os_boot_config_t;
cart_header_t *boot_load_cart_header(void);
uint16_t boot_get_cic_seed(cart_header_t *cart_header);
tv_type_t boot_get_tv_type(cart_header_t *cart_header);
void boot(cart_header_t *cart_header, uint16_t cic_seed, tv_type_t tv_type);
void bootX(cart_header_t *cart_header, uint16_t cic_seed, tv_type_t tv_type);
#endif

View File

@ -1,7 +1,7 @@
#include "crc32.h"
static const uint32_t crc_table[256] = {
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,
@ -43,7 +43,7 @@ uint32_t crc32_calculate(void *buffer, size_t length) {
uint8_t *byte_pointer = (uint8_t *) buffer;
while (length--) {
crc32 = (crc32 >> 8) ^ crc_table[(crc32 & 0xFF) ^ (*byte_pointer++)];
crc32 = (crc32 >> 8) ^ crc32_table[(crc32 & 0xFF) ^ (*byte_pointer++)];
}
return ~crc32;

View File

@ -2,7 +2,7 @@
#define CRC32_H__
#include "platform.h"
#include "sys.h"
uint32_t crc32_calculate(void *buffer, size_t length);

View File

@ -1,19 +1,41 @@
#include "sc64.h"
#include "boot.h"
int main(void) {
OS_BOOT_CONFIG->tv_type = TV_NTSC;
void main(void) {
sc64_init();
while (!sc64_check_presence());
rom_header_t header;
sc64_wait_cpu_ready();
io32_t *src = (io32_t *) (ROM_HEADER_CART);
uint32_t *dst = (uint32_t *) (&header);
sc64_set_config(CFG_ID_SDRAM_SWITCH, true);
for (int i = 0; i < sizeof(rom_header_t); i += 4) {
*dst++ = pi_io_read(src++);
}
cart_header_t *cart_header = boot_load_cart_header();
uint16_t cic_seed = boot_get_cic_seed(cart_header);
tv_type_t tv_type = boot_get_tv_type(cart_header);
LOG_I("Booting ROM:\r\n");
LOG_I(" PI Config: 0x%08lX\r\n", header.pi_config);
LOG_I(" Clock rate: 0x%08lX\r\n", header.clock_rate);
LOG_I(" Load address: 0x%08lX\r\n", header.load_addr);
LOG_I(" SDK vesrion: %d.%d%c\r\n", header.sdk_version.major / 10, header.sdk_version.major % 10, header.sdk_version.minor);
LOG_I(" 1MB CRC: 0x%08lX, 0x%08lX\r\n", header.crc[0], header.crc[1]);
LOG_I(" Name: %.20s\r\n", header.name);
LOG_I(" ID: %.4s\r\n", header.id);
LOG_I(" Mask ROM version: %d\r\n", header.version);
LOG_I("\r\n");
boot(cart_header, cic_seed, tv_type);
info_t info;
sc64_get_info(&info);
LOG_I("SC64 settings:\r\n");
LOG_I(" DD enabled: %d\r\n", info.dd_enabled);
LOG_I(" Save type: %d\r\n", info.save_type);
LOG_I(" CIC seed: 0x%02X\r\n", info.cic_seed);
LOG_I(" TV type: %d\r\n", info.tv_type);
LOG_I(" Save offset: 0x%08lX\r\n", (uint32_t) info.save_offset);
LOG_I(" DD offset: 0x%08lX\r\n", (uint32_t) info.dd_offset);
LOG_I(" Boot mode: %d\r\n", info.boot_mode);
LOG_I(" Bootloader ver: %s\r\n", info.bootloader_version);
LOG_I("\r\n");
}

View File

@ -1,26 +0,0 @@
#ifndef PLATFORM_H__
#define PLATFORM_H__
#include <stddef.h>
#include <stdint.h>
#include <libdragon.h>
#define BOOTLOADER_VERSION_MAJOR (1)
#define BOOTLOADER_VERSION_MINOR (0)
#define __IO volatile
typedef uint32_t reg_t;
#define platform_cache_writeback(rdram, length) data_cache_hit_writeback_invalidate(rdram, length)
#define platform_cache_invalidate(rdram, length) data_cache_hit_invalidate(rdram, length)
#define platform_pi_io_write(pi, value) io_write((uint32_t) (pi), value)
#define platform_pi_io_read(pi) io_read((uint32_t) (pi))
#define platform_pi_dma_write(rdram, pi, length) dma_write(rdram, (uint32_t) (pi), length)
#define platform_pi_dma_read(rdram, pi, length) dma_read(rdram, (uint32_t) (pi), length)
#endif

View File

@ -1,48 +1,127 @@
#include <string.h>
#include "sc64.h"
typedef struct sc64_cart_registers {
__IO reg_t SR_CMD;
__IO reg_t DATA[2];
__IO reg_t VERSION;
} sc64_cfg_registers_t;
#define SC64_CFG_BASE (0x1FFF0000)
#define SC64_CFG ((__IO sc64_cfg_registers_t *) SC64_CFG_BASE)
bool sc64_check_presence(void) {
uint32_t version = platform_pi_io_read(&SC64_CFG->VERSION);
return (version == 0x53437632);
uint32_t version = pi_io_read(&SC64->VERSION);
return (version == SC64_VERSION_2);
}
void sc64_wait_cpu_ready(void) {
uint32_t sr;
do {
sr = platform_pi_io_read(&SC64_CFG->SR_CMD);
} while (!(sr & SC64_CFG_SCR_CPU_READY));
sr = pi_io_read(&SC64->SR_CMD);
} while (!(sr & SC64_SR_CPU_READY));
}
void sc64_wait_cpu_busy(void) {
bool sc64_wait_cpu_busy(void) {
uint32_t sr;
do {
sr = platform_pi_io_read(&SC64_CFG->SR_CMD);
} while (sr & SC64_CFG_SCR_CPU_BUSY);
sr = pi_io_read(&SC64->SR_CMD);
} while (sr & SC64_SR_CPU_BUSY);
return sr & SC64_SR_CMD_ERROR;
}
void sc64_perform_cmd(uint8_t cmd, uint32_t *args) {
for (int i = 0; i < 2; i++) {
platform_pi_io_write(&SC64_CFG->DATA[i], args[i]);
}
platform_pi_io_write(&SC64_CFG->SR_CMD, (uint32_t) cmd);
sc64_wait_cpu_busy();
for (int i = 0; i < 2; i++) {
args[i] = platform_pi_io_read(&SC64_CFG->DATA[i]);
bool sc64_perform_cmd(uint8_t cmd, uint32_t *args, uint32_t *result) {
pi_io_write(&SC64->DATA[0], args[0]);
pi_io_write(&SC64->DATA[1], args[1]);
pi_io_write(&SC64->SR_CMD, (uint32_t) cmd);
bool error = sc64_wait_cpu_busy();
if (result != NULL) {
result[0] = pi_io_read(&SC64->DATA[0]);
result[1] = pi_io_read(&SC64->DATA[1]);
}
return error;
}
void sc64_set_config(uint32_t type, uint32_t value) {
uint32_t args[2] = { type, value };
sc64_perform_cmd(SC64_CMD_CONFIG, args);
uint32_t sc64_get_config(cfg_id_t id) {
uint32_t args[2] = { id, 0 };
uint32_t result[2];
sc64_perform_cmd(SC64_CMD_QUERY, args, result);
return result[1];
}
void sc64_set_config(cfg_id_t id, uint32_t value) {
uint32_t args[2] = { id, value };
sc64_perform_cmd(SC64_CMD_CONFIG, args, NULL);
}
void sc64_get_info(info_t *info) {
io32_t *src = UNCACHED(SC64_BL_VERSION_BASE);
uint32_t *dst = (uint32_t *) info->bootloader_version;
sc64_set_config(CFG_ID_SDRAM_SWITCH, false);
for (int i = 0; i < sizeof(info->bootloader_version); i += sizeof(uint32_t)) {
*dst++ = pi_io_read(src++);
}
sc64_set_config(CFG_ID_SDRAM_SWITCH, true);
info->dd_enabled = (bool) sc64_get_config(CFG_ID_DD_ENABLE);
info->save_type = (save_type_t) sc64_get_config(CFG_ID_SAVE_TYPE);
info->cic_seed = (uint8_t) sc64_get_config(CFG_ID_CIC_SEED);
info->tv_type = (tv_type_t) sc64_get_config(CFG_ID_TV_TYPE);
info->save_offset = (io32_t *) sc64_get_config(CFG_ID_SAVE_OFFEST);
info->dd_offset = (io32_t *) sc64_get_config(CFG_ID_DD_OFFEST);
info->boot_mode = (boot_mode_t) sc64_get_config(CFG_ID_BOOT_MODE);
}
void sc64_wait_usb_tx_ready(void) {
uint32_t args[2] = { 0, 0 };
uint32_t result[2];
do {
sc64_perform_cmd(SC64_CMD_DEBUG_TX_READY, args, result);
} while (!result[0]);
}
void sc64_usb_tx_data(io32_t *address, uint32_t length) {
uint32_t args[2] = { (uint32_t) (address), length };
sc64_perform_cmd(SC64_CMD_DEBUG_TX_DATA, args, NULL);
}
void sc64_debug_write(uint8_t type, const void *data, uint32_t len) {
char *dma = "DMA@";
char *cmp = "CMPH";
io32_t *sdram = UNCACHED(SC64_DEBUG_WRITE_ADDRESS);
io32_t *src = (io32_t *) (data);
io32_t *dst = sdram;
uint32_t copy_length = ALIGN(len, 4);
uint32_t transfer_length = 4 + 4 + copy_length + 4;
bool writable = sc64_get_config(CFG_ID_SDRAM_WRITABLE);
sc64_wait_usb_tx_ready();
if (!writable) {
sc64_set_config(CFG_ID_SDRAM_WRITABLE, true);
}
pi_io_write(dst++, *((uint32_t *) (dma)));
pi_io_write(dst++, (type << 24) | len);
for (uint32_t i = 0; i < copy_length; i += 4) {
pi_io_write(dst++, *src++);
}
pi_io_write(dst++, *((uint32_t *) (cmp)));
if (!writable) {
sc64_set_config(CFG_ID_SDRAM_WRITABLE, false);
}
sc64_usb_tx_data(sdram, transfer_length);
}
void sc64_debug_print(const char *text) {
sc64_debug_write(SC64_DEBUG_TYPE_TEXT, text, strlen(text));
}
void sc64_init(void) {
while (!sc64_check_presence());
sc64_wait_cpu_ready();
sc64_set_config(CFG_ID_SDRAM_SWITCH, true);
}

View File

@ -2,16 +2,55 @@
#define SC64_H__
#include "platform.h"
#include <stdbool.h>
#include <stdio.h>
#include "sys.h"
#define SC64_CFG_SCR_CPU_READY (1 << 31)
#define SC64_CFG_SCR_CPU_BUSY (1 << 30)
#ifdef DEBUG
#include <assert.h>
#define LOG_I(args...) {iprintf("\033[32m" args);}
#define LOG_E(args...) {iprintf("\033[31m" args);}
#else
#define LOG_I(args...)
#define LOG_E(args...)
#define assert(expr)
#endif
#define SC64_CMD_CONFIG 'C'
#define SC64_CMD_QUERY 'Q'
enum cfg_id {
extern char *header_text_info;
#define SC64_BL_VERSION_BASE (PHYSICAL((io32_t *) (header_text_info)))
typedef struct {
io32_t SR_CMD;
io32_t DATA[2];
io32_t VERSION;
} sc64_regs_t;
#define SC64_BASE (0x1FFF0000)
#define SC64 ((sc64_regs_t *) SC64_BASE)
#define SC64_SR_CMD_ERROR (1 << 28)
#define SC64_SR_CPU_BUSY (1 << 30)
#define SC64_SR_CPU_READY (1 << 31)
#define SC64_CMD_CONFIG ('C')
#define SC64_CMD_QUERY ('Q')
#define SC64_CMD_DEBUG_TX_DATA ('D')
#define SC64_CMD_DEBUG_TX_READY ('S')
#define SC64_VERSION_2 (0x53437632)
#define SC64_DEBUG_WRITE_ADDRESS (0x13BD8000UL)
#define SC64_DEBUG_READ_ADDRESS (0x13BD0000UL)
#define SC64_DEBUG_MAX_SIZE (32 * 1024)
#define SC64_DEBUG_TYPE_TEXT (0x01)
typedef enum {
CFG_ID_SCR,
CFG_ID_SDRAM_SWITCH,
CFG_ID_SDRAM_WRITABLE,
@ -21,33 +60,61 @@ enum cfg_id {
CFG_ID_TV_TYPE,
CFG_ID_SAVE_OFFEST,
CFG_ID_DD_OFFEST,
CFG_ID_SKIP_BOOTLOADER,
};
CFG_ID_BOOT_MODE,
CFG_ID_FLASH_SIZE,
CFG_ID_FLASH_READ,
CFG_ID_FLASH_PROGRAM,
CFG_ID_RECONFIGURE,
} cfg_id_t;
typedef enum {
SAVE_TYPE_NONE = 0,
SAVE_TYPE_EEPROM_4K = 1,
SAVE_TYPE_EEPROM_16K = 2,
SAVE_TYPE_SRAM = 3,
SAVE_TYPE_FLASHRAM = 4,
SAVE_TYPE_SRAM_BANKED = 5,
SAVE_TYPE_FLASHRAM_PKST2 = 6,
} save_type_t;
typedef struct sc64_config {
union {
uint32_t ___raw_data[2];
struct {
uint8_t ___unused_1[2];
uint8_t save_type;
uint8_t ___unused_2: 1;
uint8_t dd_enable: 1;
uint8_t sdram_writable: 1;
uint8_t sdram_switch: 1;
uint8_t ___unused_3;
uint8_t tv_type;
uint16_t cic_type;
};
};
} sc64_config_t;
typedef enum {
TV_TYPE_PAL = 0,
TV_TYPE_NTSC = 1,
TV_TYPE_MPAL = 2,
TV_TYPE_UNKNOWN = 3,
} tv_type_t;
typedef enum {
BOOT_MODE_MENU = 0,
BOOT_MODE_ROM = 1,
BOOT_MODE_DD = 2,
BOOT_MODE_DIRECT = 3,
} boot_mode_t;
typedef struct {
bool dd_enabled;
save_type_t save_type;
uint8_t cic_seed;
tv_type_t tv_type;
io32_t *save_offset;
io32_t *dd_offset;
boot_mode_t boot_mode;
char bootloader_version[32];
} info_t;
bool sc64_check_presence(void);
void sc64_wait_cpu_ready(void);
void sc64_wait_cpu_busy(void);
void sc64_perform_cmd(uint8_t cmd, uint32_t *args);
void sc64_set_config(uint32_t type, uint32_t value);
bool sc64_wait_cpu_busy(void);
bool sc64_perform_cmd(uint8_t cmd, uint32_t *args, uint32_t *result);
uint32_t sc64_get_config(cfg_id_t id);
void sc64_set_config(cfg_id_t id, uint32_t value);
void sc64_get_info(info_t *info);
void sc64_wait_usb_tx_ready(void);
void sc64_usb_tx_data(io32_t *address, uint32_t length);
void sc64_debug_write(uint8_t type, const void *data, uint32_t len);
void sc64_debug_print(const char *text);
void sc64_init(void);
#endif

View File

@ -2,7 +2,8 @@
#define XSTR(s) STR(s)
#define VERSION XSTR(__SC64_VERSION)
.section .text.bootcode
.section .text.rom_header
header_pi_config:
.word 0x80371240
@ -12,60 +13,38 @@ header_clock_rate:
header_load_addr:
.word entry_handler
header_release_addr:
header_sdk_version:
.word 0x00000000
header_crc:
.fill 2, 4, 0
.org 0x18, 0x00
.org 0x20, 0x00
header_text_info:
.ascii "byMFinPL"
.ascii "SummerLoader64 "
.global header_text_info
.ascii "SummerLoader64 "
.ascii VERSION
.org 0x40, 0x00
.org 0x40, 0x20
.section .text.ipl3
ipl3:
.incbin "blob/ipl3.bin"
.incbin "header", 0x40
.section .text.entry_handler
.set noreorder
entry_handler:
.global entry_handler
li $a0, 0x08
sw $a0, 0xBFC007FC
la $gp, _gp
la $sp, _sp
init_bss:
la $a0, _sbss
la $a1, _ebss
bge $a0, $a1, 2f
nop
1:
sd $zero, 0($a0)
addi $a0, $a0, 8
blt $a0, $a1, 1b
nop
2:
init_bss_cache:
la $a0, _sbss
la $a1, _ebss
bge $a0, $a1, 2f
nop
1:
cache 0x15, 0($a0)
addi $a0, $a0, 16
blt $a0, $a1, 1b
nop
2:
run:
j main
nop
la $t0, init
jalr $t0
la $t0, main
jalr $t0
loop:
j loop

57
sw/n64/src/sys.c Normal file
View File

@ -0,0 +1,57 @@
#include "sys.h"
static uint32_t c0_get_count(void) {
uint32_t x;
asm volatile("mfc0 %0,$9" : "=r" (x));
return x;
}
void wait_ms(uint32_t ms) {
uint32_t start = c0_get_count();
while (c0_get_count() - start < (ms * ((CPU_FREQUENCY / 2) / 1000)));
}
uint32_t io_read(io32_t *address) {
asm volatile ("" : : : "memory");
uint32_t value = *UNCACHED(address);
asm volatile ("" : : : "memory");
return value;
}
void io_write(io32_t *address, uint32_t value) {
asm volatile ("" : : : "memory");
*UNCACHED(address) = value;
asm volatile ("" : : : "memory");
}
uint32_t pi_busy(void) {
return (io_read(&PI->SR) & (PI_SR_IO_BUSY | PI_SR_DMA_BUSY));
}
uint32_t pi_io_read(io32_t *address) {
return io_read(address);
}
void pi_io_write(io32_t *address, uint32_t value) {
io_write(address, value);
while (pi_busy());
}
uint32_t si_busy(void) {
return (io_read(&SI->SR) & (SI_SR_IO_BUSY | SI_SR_DMA_BUSY));
}
uint32_t si_io_read(io32_t *address) {
return io_read(address);
}
void si_io_write(io32_t *address, uint32_t value) {
io_write(address, value);
while (si_busy());
}
void init(void) {
uint32_t pifram = si_io_read((io32_t *) (&PIFRAM[0x3C]));
si_io_write((io32_t *) (&PIFRAM[0x3C]), pifram | 0x08);
}

123
sw/n64/src/sys.h Normal file
View File

@ -0,0 +1,123 @@
#ifndef SYS_H__
#define SYS_H__
#include <inttypes.h>
#include <stddef.h>
typedef volatile uint8_t io8_t;
typedef volatile uint32_t io32_t;
#define ALIGN(value, align) (((value) + ((typeof(value))(align) - 1)) & ~((typeof(value))(align) - 1))
#define PHYSICAL(address) ((io32_t *) (((io32_t) (address)) & 0x1FFFFFFFUL))
#define CACHED(address) ((io32_t *) ((((io32_t) (address)) & 0x1FFFFFFFUL) | 0x80000000UL))
#define UNCACHED(address) ((io32_t *) ((((io32_t) (address)) & 0x1FFFFFFFUL) | 0xA0000000UL))
#define CPU_FREQUENCY (93750000UL)
typedef struct {
uint32_t tv_type;
uint32_t rom_type;
uint32_t rom_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;
} boot_info_t;
#define BOOT_INFO_BASE (0x00000300UL)
#define BOOT_INFO ((boot_info_t *) BOOT_INFO_BASE)
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_CONTROLLER (1 << 0)
#define PI_SR_CLEAR_INTERRUPT (1 << 1)
typedef struct {
io32_t MADDR;
io32_t RDMA;
io32_t __reserved_1;
io32_t __reserved_2;
io32_t WDMA;
io32_t __reserved_3;
io32_t SR;
} si_regs_t;
#define SI_BASE (0x04800000UL)
#define SI ((si_regs_t *) SI_BASE)
#define SI_SR_DMA_BUSY (1 << 0)
#define SI_SR_IO_BUSY (1 << 1)
#define SI_SR_DMA_ERROR (1 << 3)
#define SI_SR_INTERRUPT (1 << 12)
#define SI_SR_CLEAR_INTERRUPT (0)
typedef struct {
uint32_t pi_config;
uint32_t clock_rate;
uint32_t load_addr;
struct {
uint16_t __reserved_1;
uint8_t major;
char minor;
} sdk_version;
uint32_t crc[2];
uint32_t __reserved_1[2];
char name[20];
uint8_t ___reserved_2[7];
char id[4];
uint8_t version;
uint32_t ipl3[1008];
} rom_header_t;
#define ROM_HEADER_DDIPL_BASE (0x06000000UL)
#define ROM_HEADER_DDIPL ((rom_header_t *) ROM_HEADER_DDIPL_BASE)
#define ROM_HEADER_CART_BASE (0x10000000UL)
#define ROM_HEADER_CART ((rom_header_t *) ROM_HEADER_CART_BASE)
#define PIFRAM_BASE (0x1FC007C0UL)
#define PIFRAM ((io8_t *) PIFRAM_BASE)
void wait_ms(uint32_t ms);
uint32_t io_read(io32_t *address);
void io_write(io32_t *address, uint32_t value);
uint32_t pi_busy(void);
uint32_t pi_io_read(io32_t *address);
void pi_io_write(io32_t *address, uint32_t value);
uint32_t si_busy(void);
uint32_t si_io_read(io32_t *address);
void si_io_write(io32_t *address, uint32_t value);
#endif

55
sw/n64/src/syscalls.c Normal file
View File

@ -0,0 +1,55 @@
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "sc64.h"
int _close_r(struct _reent *prt, int fd) {
return -1;
}
int _fstat_r(struct _reent *prt, int fd, struct stat *pstat) {
pstat->st_mode = S_IFCHR;
return 0;
}
int _isatty_r(struct _reent *prt, int fd) {
return 1;
}
off_t _lseek_r(struct _reent *prt, int fd, off_t pos, int whence) {
return 0;
}
ssize_t _read_r(struct _reent *prt, int fd, void *buf, size_t cnt) {
return 0;
}
caddr_t _sbrk_r(struct _reent *prt, ptrdiff_t incr) {
extern char _sheap;
extern char _eheap;
static char *curr_heap_end = &_sheap;
char *prev_heap_end;
prev_heap_end = curr_heap_end;
curr_heap_end += incr;
if (curr_heap_end > &_eheap) {
errno = ENOMEM;
return (caddr_t) -1;
}
return (caddr_t) prev_heap_end;
}
ssize_t _write_r(struct _reent *prt, int fd, const void *buf, size_t cnt) {
sc64_debug_write(SC64_DEBUG_TYPE_TEXT, buf, cnt);
return cnt;
}
void __assert_func(const char *file, int line, const char *func, const char *failedexpr) {
LOG_E("\r\nassertion \"%s\" failed: file \"%s\", line %d%s%s\r\n", failedexpr, file, line, func ? ", function: " : "", func ? func : "");
while (1);
}

View File

@ -27,9 +27,11 @@ class SC64:
__CFG_ID_TV_TYPE = 6
__CFG_ID_SAVE_OFFEST = 7
__CFG_ID_DD_OFFEST = 8
__CFG_ID_SKIP_BOOTLOADER = 9
__CFG_ID_FLASH_OPERATION = 10
__CFG_ID_RECONFIGURE = 11
__CFG_ID_BOOT_MODE = 9
__CFG_ID_FLASH_SIZE = 10
__CFG_ID_FLASH_READ = 11
__CFG_ID_FLASH_PROGRAM = 12
__CFG_ID_RECONFIGURE = 13
__SC64_VERSION_V2 = 0x53437632
@ -40,6 +42,8 @@ class SC64:
__MIN_ROM_LENGTH = 0x101000
__DDIPL_ROM_LENGTH = 0x400000
__DEBUG_ID_TEXT = 0x01
def __init__(self) -> None:
self.__serial = None
@ -89,6 +93,13 @@ class SC64:
self.__serial.write(self.__escape(data))
def __read_long(self, length: int) -> bytes:
data = bytearray()
while (len(data) < length):
data += self.__read(length - len(data))
return bytes(data)
def __write_dummy(self, length: int) -> None:
self.__write(bytes(length))
@ -148,8 +159,8 @@ class SC64:
raise SC64Exception(f"Unknown hardware version: {hex(version)}")
def __query_config(self, id: int, arg: int = 0) -> int:
self.__write_cmd("Q", id, arg)
def __query_config(self, id: int) -> int:
self.__write_cmd("Q", id, 0)
value = self.__read_int()
self.__read_cmd_status("Q")
return value
@ -211,7 +222,8 @@ class SC64:
def backup_firmware(self, file: str) -> None:
length = self.__query_config(self.__CFG_ID_FLASH_OPERATION, self.__UPDATE_OFFSET)
length = self.__query_config(self.__CFG_ID_FLASH_SIZE)
self.__change_config(self.__CFG_ID_FLASH_READ, self.__UPDATE_OFFSET)
self.__read_file_from_sdram(file, self.__UPDATE_OFFSET, length)
@ -219,14 +231,28 @@ class SC64:
self.__write_file_to_sdram(file, self.__UPDATE_OFFSET)
saved_timeout = self.__serial.timeout
self.__serial.timeout = 20.0
self.__change_config(self.__CFG_ID_FLASH_OPERATION, self.__UPDATE_OFFSET)
self.__change_config(self.__CFG_ID_FLASH_PROGRAM, self.__UPDATE_OFFSET)
self.__serial.timeout = saved_timeout
self.__reconfigure()
self.__find_sc64()
def set_skip_bootloader(self, enable: bool) -> None:
self.__change_config(self.__CFG_ID_SKIP_BOOTLOADER, 1 if enable else 0)
def set_boot_mode(self, mode: int) -> None:
if (mode >= 0 and mode <= 3):
self.__change_config(self.__CFG_ID_BOOT_MODE, mode)
else:
raise SC64Exception("Boot mode outside of supported values")
def get_boot_mode_label(self, mode: int) -> None:
if (mode < 0 or mode > 3):
return "Unknown"
return {
0: "Load menu from SD card",
1: "Load ROM from SDRAM through bootloader",
2: "Load DDIPL from SDRAM",
3: "Load ROM from SDRAM directly without bootloader"
}[mode]
def set_tv_type(self, type: int = None) -> None:
@ -321,6 +347,34 @@ class SC64:
self.__write_file_to_sdram(file, dd_ipl_offset, min_length=self.__DDIPL_ROM_LENGTH)
def debug_loop(self) -> None:
print("\r\nDebug server started\r\n")
self.__serial.timeout = 0.01
while (True):
start_indicator = self.__read_long(4)
if (start_indicator == b"DMA@"):
header = self.__read_long(4)
id = int(header[0])
length = int.from_bytes(header[1:4], byteorder="big")
align = self.__align(length, 4) - length
data = self.__read_long(length)
if (id == self.__DEBUG_ID_TEXT):
print(data.decode(encoding="ascii", errors="backslashreplace"), end="")
sys.stdout.flush()
else:
print(f"Got unknown id: {id}, length: {length}")
self.__read_long(align)
end_indicator = self.__read_long(4)
if (end_indicator != b"CMPH"):
print(f"Got unknown end indicator: {end_indicator.decode(encoding='ascii', errors='backslashreplace')}")
else:
print(f"Got unknown start indicator: {start_indicator.decode(encoding='ascii', errors='backslashreplace')}")
class SC64ProgressBar:
__LABEL_LENGTH = 30
@ -381,17 +435,18 @@ class SC64ProgressBar:
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="SummerCart64 one stop control center")
parser.add_argument("-b", default=False, action="store_true", required=False, help="skip internal bootloader")
parser.add_argument("-t", default="3", required=False, help="set TV type (0 - 2)")
parser.add_argument("-c", default="0xFFFF", required=False, help="set CIC seed")
parser.add_argument("-s", default="0", required=False, help="set save type (0 - 6)")
parser.add_argument("-b", metavar="boot_mode", default="1", required=False, help="set boot mode (0 - 3)")
parser.add_argument("-t", metavar="tv_type", default="3", required=False, help="set TV type (0 - 2)")
parser.add_argument("-c", metavar="cic_seed", default="0xFFFF", required=False, help="set CIC seed")
parser.add_argument("-s", metavar="save_type", default="0", required=False, help="set save type (0 - 6)")
parser.add_argument("-d", default=False, action="store_true", required=False, help="enable 64DD emulation")
parser.add_argument("-r", default=False, action="store_true", required=False, help="perform reading operation instead of writing")
parser.add_argument("-l", default="0x101000", required=False, help="specify ROM length to read")
parser.add_argument("-u", default=None, required=False, help="path to update file")
parser.add_argument("-e", default=None, required=False, help="path to save file")
parser.add_argument("-i", default=None, required=False, help="path to DDIPL file")
parser.add_argument("rom", default=None, help="path to ROM file", nargs="?")
parser.add_argument("-l", metavar="length", default="0x101000", required=False, help="specify ROM length to read")
parser.add_argument("-u", metavar="update_path", default=None, required=False, help="path to update file")
parser.add_argument("-e", metavar="save_path", default=None, required=False, help="path to save file")
parser.add_argument("-i", metavar="ddipl_path", default=None, required=False, help="path to DDIPL file")
parser.add_argument("-q", default=None, action="store_true", required=False, help="start debug server")
parser.add_argument("rom", metavar="rom_path", default=None, help="path to ROM file", nargs="?")
args = parser.parse_args()
@ -402,7 +457,7 @@ if __name__ == "__main__":
try:
sc64 = SC64()
skip_bootloader = args.b
boot_mode = int(args.b)
save_type = int(args.s)
tv_type = int(args.t)
cic_seed = int(args.c, 0)
@ -413,6 +468,7 @@ if __name__ == "__main__":
save_file = args.e
dd_ipl_file = args.i
rom_file = args.rom
debug_server = args.q
firmware_backup_file = "sc64firmware.bin.bak"
@ -430,12 +486,12 @@ if __name__ == "__main__":
os.remove(firmware_backup_file)
if (not is_read):
print(f"Setting boot mode to [{sc64.get_boot_mode_label(boot_mode)}]")
sc64.set_boot_mode(boot_mode)
print(f"Setting save type to [{sc64.get_save_type_label(save_type)}]")
sc64.set_save_type(save_type)
print(f"Setting skip internal bootloader to [{'True' if skip_bootloader else 'False'}]")
sc64.set_skip_bootloader(skip_bootloader)
print(f"Setting TV type to [{sc64.get_tv_type_label(tv_type)}]")
sc64.set_tv_type(tv_type)
@ -466,6 +522,13 @@ if __name__ == "__main__":
else:
sc64.upload_save(save_file)
if (debug_server):
sc64.debug_loop()
except SC64Exception as e:
print(f"Error: {e}")
parser.exit(1)
except KeyboardInterrupt:
pass
finally:
sys.stdout.write("\033[0m")

View File

@ -26,8 +26,10 @@ enum cfg_id {
CFG_ID_TV_TYPE,
CFG_ID_SAVE_OFFEST,
CFG_ID_DD_OFFEST,
CFG_ID_SKIP_BOOTLOADER,
CFG_ID_FLASH_OPERATION,
CFG_ID_BOOT_MODE,
CFG_ID_FLASH_SIZE,
CFG_ID_FLASH_READ,
CFG_ID_FLASH_PROGRAM,
CFG_ID_RECONFIGURE,
};
@ -41,11 +43,19 @@ enum save_type {
SAVE_TYPE_FLASHRAM_PKST2 = 6,
};
enum boot_mode {
BOOT_MODE_MENU = 0,
BOOT_MODE_ROM = 1,
BOOT_MODE_DD = 2,
BOOT_MODE_DIRECT = 3,
};
struct process {
enum save_type save_type;
uint16_t cic_seed;
uint8_t tv_type;
enum boot_mode boot_mode;
};
static struct process p;
@ -136,10 +146,14 @@ void cfg_update (uint32_t *args) {
case CFG_ID_DD_OFFEST:
CFG->DD_OFFSET = args[1];
break;
case CFG_ID_SKIP_BOOTLOADER:
change_scr_bits(CFG_SCR_SKIP_BOOTLOADER, args[1]);
case CFG_ID_BOOT_MODE:
p.boot_mode = args[1];
change_scr_bits(CFG_SCR_SKIP_BOOTLOADER, args[1] == BOOT_MODE_DIRECT);
break;
case CFG_ID_FLASH_OPERATION:
case CFG_ID_FLASH_READ:
flash_read(args[1]);
break;
case CFG_ID_FLASH_PROGRAM:
flash_program(args[1]);
break;
case CFG_ID_RECONFIGURE:
@ -182,11 +196,11 @@ void cfg_query (uint32_t *args) {
case CFG_ID_DD_OFFEST:
args[1] = CFG->DD_OFFSET;
break;
case CFG_ID_SKIP_BOOTLOADER:
args[1] = CFG->SCR & CFG_SCR_SKIP_BOOTLOADER;
case CFG_ID_BOOT_MODE:
args[1] = p.boot_mode;
break;
case CFG_ID_FLASH_OPERATION:
args[1] = flash_read(args[1]);
case CFG_ID_FLASH_SIZE:
args[1] = flash_size();
break;
case CFG_ID_RECONFIGURE:
args[1] = CFG->RECONFIGURE;
@ -203,6 +217,7 @@ void cfg_init (void) {
p.cic_seed = 0xFFFF;
p.tv_type = 0x03;
p.boot_mode = BOOT_MODE_MENU;
}

View File

@ -1,15 +1,17 @@
#include "flash.h"
uint32_t flash_read (uint32_t sdram_offset) {
uint32_t flash_size (void) {
return FLASH_SIZE;
}
void flash_read (uint32_t sdram_offset) {
io32_t *flash = (io32_t *) (FLASH_BASE);
io32_t *sdram = (io32_t *) (SDRAM_BASE + sdram_offset);
for (size_t i = 0; i < FLASH_SIZE; i += 4) {
*sdram++ = *flash++;
}
return FLASH_SIZE;
}
void flash_program (uint32_t sdram_offset) {

View File

@ -5,7 +5,8 @@
#include "sys.h"
uint32_t flash_read (uint32_t sdram_offset);
uint32_t flash_size(void);
void flash_read (uint32_t sdram_offset);
void flash_program (uint32_t sdram_offset);

View File

@ -2,6 +2,7 @@
#define XSTR(s) STR(s)
#define VERSION XSTR(__SC64_VERSION)
.section .text.reset_handler
reset_handler:
.global reset_handler
@ -13,16 +14,15 @@ reset_handler:
la sp, __stack_pointer$
j init_text
.org 0x18, 0x00
la ra, init_text
jalr zero, 0(ra)
.org 0x20, 0x00
header_text_info:
.ascii "byMFinPL"
.ascii "SummerGovernor64"
.global header_text_info
.ascii "SummerGovernor64 "
.ascii VERSION
.org 0x40, 0x20
.org 0x40, 0x00
init_text:
la a0, _sitext

View File

@ -189,4 +189,7 @@ typedef volatile struct joybus_regs {
#define JOYBUS_SCR_TX_LENGTH_BIT (16)
void reset_handler(void);
#endif