SD constrained i think? And PI bug fixes

This commit is contained in:
Polprzewodnikowy 2021-02-14 21:56:50 +01:00
parent 982e230958
commit a0fed3e8fa
23 changed files with 945 additions and 308 deletions

View File

@ -178,7 +178,7 @@ set_global_assignment -name TIMING_ANALYZER_DO_REPORT_TIMING ON
# Compiler Assignments
# ====================
set_global_assignment -name OPTIMIZATION_MODE "AGGRESSIVE PERFORMANCE"
set_global_assignment -name OPTIMIZATION_MODE BALANCED
# Analysis & Synthesis Assignments
# ================================
@ -266,6 +266,7 @@ set_global_assignment -name OUTPUT_IO_TIMING_FAR_END_VMEAS "HALF SIGNAL SWING" -
set_instance_assignment -name GLOBAL_SIGNAL GLOBAL_CLOCK -to i_clk
set_instance_assignment -name GLOBAL_SIGNAL GLOBAL_CLOCK -to "pll:sys_pll|altpll:altpll_component|pll_altpll:auto_generated|wire_pll1_clk[0]"
set_instance_assignment -name GLOBAL_SIGNAL GLOBAL_CLOCK -to "pll:sys_pll|altpll:altpll_component|pll_altpll:auto_generated|wire_pll1_clk[1]"
set_instance_assignment -name GLOBAL_SIGNAL GLOBAL_CLOCK -to "sd_interface:sd_interface_inst|sd_clk:sd_clk_inst|o_sd_clk|q"
# start DESIGN_PARTITION(Top)
# ---------------------------

View File

@ -5,9 +5,25 @@ derive_pll_clocks -create_base_clocks
set sys_clk {sys_pll|altpll_component|auto_generated|pll1|clk[0]}
set sdram_pll_clk {sys_pll|altpll_component|auto_generated|pll1|clk[1]}
create_generated_clock -name sdram_clk -source [get_pins $sdram_pll_clk] -master_clock $sdram_pll_clk [get_ports {o_sdram_clk}]
create_generated_clock -name flash_se_neg_reg -divide_by 2 \
create_generated_clock -name sdram_clk \
-source [get_pins $sdram_pll_clk] \
-master_clock $sdram_pll_clk \
[get_ports {o_sdram_clk}]
create_generated_clock -name sd_generated_clk \
-source [get_pins {sd_interface_inst|sd_clk_inst|o_sd_clk|clk}] \
-divide_by 2 \
-master_clock $sys_clk \
[get_pins {sd_interface_inst|sd_clk_inst|o_sd_clk|q}]
create_generated_clock -name sd_clk \
-source [get_pins {sd_interface_inst|sd_clk_inst|o_sd_clk|q}] \
-master_clock [get_clocks {sd_generated_clk}] \
[get_ports {o_sd_clk}]
create_generated_clock -name flash_se_neg_reg \
-source [get_pins -compatibility_mode {*altera_onchip_flash:*onchip_flash_0|altera_onchip_flash_avmm_data_controller:avmm_data_controller|flash_se_neg_reg|clk}] \
-divide_by 2 \
[get_pins -compatibility_mode {*altera_onchip_flash:*onchip_flash_0|altera_onchip_flash_avmm_data_controller:avmm_data_controller|flash_se_neg_reg|q}]
derive_clock_uncertainty
@ -35,8 +51,17 @@ set_false_path -from [get_ports {i_ftdi_so i_ftdi_cts}]
# SD card timings
set_false_path -to [get_ports {o_sd_clk io_sd_cmd io_sd_dat[*]}]
set_false_path -from [get_ports {io_sd_cmd io_sd_dat[*]}]
set_output_delay -clock [get_clocks {sd_clk}] -max 7.5 [get_ports {io_sd_cmd io_sd_dat[*]}]
set_output_delay -clock [get_clocks {sd_clk}] -min -3.5 [get_ports {io_sd_cmd io_sd_dat[*]}]
set_input_delay -clock [get_clocks {sd_clk}] -max 15.5 [get_ports {io_sd_cmd io_sd_dat[*]}]
set_input_delay -clock [get_clocks {sd_clk}] -min 4.0 [get_ports {io_sd_cmd io_sd_dat[*]}]
set_multicycle_path -setup -start 1 -from [get_clocks $sys_clk] -to [get_clocks {sd_clk}]
set_multicycle_path -hold -start 1 -from [get_clocks $sys_clk] -to [get_clocks {sd_clk}]
set_multicycle_path -setup -end 3 -from [get_clocks {sd_clk}] -to [get_clocks $sys_clk]
set_multicycle_path -hold -end 2 -from [get_clocks {sd_clk}] -to [get_clocks $sys_clk]
# N64, PI and SI timings

View File

@ -77,6 +77,7 @@ module cart_control (
// Registers
reg [15:0] r_bootloader;
reg r_skip_bootloader;
// Bus controller
@ -112,11 +113,13 @@ module cart_control (
o_debug_dma_address <= 24'hFC_0000;
o_debug_dma_length <= 20'd0;
r_bootloader <= 16'h0000;
r_skip_bootloader <= 1'b0;
end else begin
if (i_request && i_write && !o_busy) begin
case (i_address[3:0])
REG_SCR: begin
{
r_skip_bootloader,
o_flashram_enable,
o_sram_768k_mode,
o_sram_enable,
@ -127,7 +130,7 @@ module cart_control (
o_ddipl_enable,
o_rom_switch,
o_sdram_writable
} <= i_data[9:0];
} <= i_data[10:0];
end
REG_BOOT: begin
r_bootloader <= i_data[15:0];
@ -157,7 +160,7 @@ module cart_control (
if (!r_reset_ff2 || !r_nmi_ff2) begin
o_sdram_writable <= 1'b0;
o_rom_switch <= 1'b0;
o_rom_switch <= r_skip_bootloader;
o_n64_reset_btn <= 1'b1;
o_debug_fifo_flush <= 1'b1;
end
@ -172,9 +175,12 @@ module cart_control (
if (!i_reset && i_request && !i_write && !o_busy) begin
if (i_address < MEM_USB_FIFO_BASE) begin
o_data <= 32'h0000_0000;
case (i_address[3:0])
REG_SCR: begin
o_data[9:0] <= {
o_data[10:0] <= {
r_skip_bootloader,
o_flashram_enable,
o_sram_768k_mode,
o_sram_enable,

View File

@ -30,7 +30,7 @@ module memory_embedded_flash (
r_onchip_flash_request = 1'b0;
o_busy = 1'b0;
o_data = 32'h0000_0000;
if (w_onchip_flash_in_address_range) begin
if (w_onchip_flash_in_address_range && !r_dummy_ack) begin
r_onchip_flash_request = i_request;
o_busy = w_onchip_flash_busy;
o_data = {

View File

@ -1,80 +1,119 @@
`include "../constants.vh"
module n64_bank_decoder (
input [31:0] i_address,
output reg [25:0] o_translated_address,
input i_clk,
input i_address_high_op,
input i_address_low_op,
input [15:0] i_n64_pi_ad,
output reg [3:0] o_bank,
output reg o_bank_prefetch,
output o_sram_request,
output reg o_prefetch,
output reg o_ddipl_request,
output reg o_sram_request,
input i_ddipl_enable,
input i_sram_enable,
input i_sram_768k_mode,
input i_flashram_enable,
input i_sd_enable,
input i_eeprom_enable,
input [23:0] i_ddipl_address,
input [23:0] i_sram_address
input i_sd_enable
);
localparam [31:0] DDIPL_BASE = 32'h0600_0000;
localparam [31:0] DDIPL_END = 32'h063F_FFFF;
reg r_address_high_lsb;
localparam [31:0] SRAM_BASE = 32'h0800_0000;
localparam [31:0] SRAM_END = 32'h0800_7FFF;
localparam [31:0] SRAM_768K_END = 32'h0801_7FFF;
always @(posedge i_clk) begin
if (i_address_high_op) begin
o_bank <= `BANK_INVALID;
o_prefetch <= 1'b1;
o_ddipl_request <= 1'b0;
o_sram_request <= 1'b0;
localparam [31:0] ROM_BASE = 32'h1000_0000;
localparam [31:0] ROM_END = 32'h13FF_FFFF;
localparam [31:0] CART_BASE = 32'h1E00_0000;
localparam [31:0] CART_END = 32'h1E00_3FFF;
localparam [31:0] EEPROM_BASE = 32'h1E00_4000;
localparam [31:0] EEPROM_END = 32'h1E00_47FF;
localparam [31:0] SD_BASE = 32'h1E00_8000;
localparam [31:0] SD_END = 32'h1E00_83FF;
wire [25:0] w_ddipl_translated_address = i_address[25:0] + {i_ddipl_address, 2'd0};
wire [25:0] w_sram_translated_address = i_address[25:0] + {i_sram_address, 2'd0};
always @(*) begin
o_bank = `BANK_INVALID;
o_bank_prefetch = 1'b0;
o_translated_address = i_address[25:0];
o_sram_request = 1'b0;
if ((i_address >= DDIPL_BASE) && (i_address <= DDIPL_END) && i_ddipl_enable) begin
o_translated_address = w_ddipl_translated_address;
o_bank = `BANK_SDRAM;
o_bank_prefetch = 1'b1;
end
if ((i_address >= SRAM_BASE) && ((i_address <= SRAM_END) || ((i_sram_768k_mode && (i_address <= SRAM_768K_END))))) begin
if (i_sram_enable && !i_flashram_enable) begin
o_translated_address = w_sram_translated_address;
o_bank = `BANK_SDRAM;
o_bank_prefetch = 1'b1;
o_sram_request = 1'b1;
casez (i_n64_pi_ad)
16'b0000011000??????: begin // DDIPL
if (i_ddipl_enable) begin
o_bank <= `BANK_SDRAM;
o_ddipl_request <= 1'b1;
end
end
if ((i_address >= ROM_BASE) && (i_address <= ROM_END)) begin
o_bank = `BANK_SDRAM;
o_bank_prefetch = 1'b1;
16'b000010000000000?: begin // SRAM / FlashRAM
r_address_high_lsb <= i_n64_pi_ad[0];
if (i_flashram_enable && !i_n64_pi_ad[0]) begin
o_bank <= `BANK_FLASHRAM;
end else if (i_sram_enable) begin
o_bank <= `BANK_SDRAM;
o_sram_request <= 1'b1;
end
end
if ((i_address >= CART_BASE) && (i_address <= CART_END)) begin
o_bank = `BANK_CART;
16'b000100??????????: begin // ROM
o_bank <= `BANK_SDRAM;
end
if ((i_address >= EEPROM_BASE) && (i_address <= EEPROM_END) && i_eeprom_enable) begin
o_bank = `BANK_EEPROM;
o_bank_prefetch = 1'b1;
16'b0001111000000000: begin // CART
o_bank <= `BANK_CART;
o_prefetch <= 1'b0;
end
if ((i_address >= SD_BASE) && (i_address <= SD_END) && i_sd_enable) begin
o_bank = `BANK_SD;
16'b0001111000000001: begin // EEPROM
if (i_eeprom_enable) begin
o_bank <= `BANK_EEPROM;
end
end
16'b0001111000000010: begin // SD
if (i_sd_enable) begin
o_bank <= `BANK_SD;
o_prefetch <= 1'b0;
end
end
default: begin end
endcase
end
if (i_address_low_op) begin
case (o_bank)
`BANK_SDRAM: begin
if (o_sram_request) begin
if (i_sram_768k_mode) begin
if (r_address_high_lsb && i_n64_pi_ad[1]) begin
o_bank <= `BANK_INVALID;
end
end else begin
if (i_n64_pi_ad[1]) begin
o_bank <= `BANK_INVALID;
end
end
end
end
`BANK_CART: begin
if (i_n64_pi_ad[15:14] != 2'b00) begin
o_bank <= `BANK_INVALID;
end
end
`BANK_EEPROM: begin
if (i_n64_pi_ad[15:11] != 5'b00000) begin
o_bank <= `BANK_INVALID;
end
end
`BANK_FLASHRAM: begin
if (r_address_high_lsb) begin
o_bank <= `BANK_INVALID;
end
end
`BANK_SD: begin
if (i_n64_pi_ad[15:10] != 6'b000000) begin
o_bank <= `BANK_INVALID;
end
end
default: begin end
endcase
end
end

View File

@ -1,3 +1,5 @@
`include "../constants.vh"
module n64_pi (
input i_clk,
input i_reset,
@ -39,15 +41,15 @@ module n64_pi (
// Input synchronization
reg r_reset_ff1, r_reset_ff2;
reg r_alel_ff1, r_alel_ff2;
reg r_aleh_ff1, r_aleh_ff2;
reg r_alel_ff1, r_alel_ff2, r_alel_ff3;
reg r_aleh_ff1, r_aleh_ff2, r_aleh_ff3;
reg r_read_ff1, r_read_ff2;
reg r_write_ff1, r_write_ff2;
always @(posedge i_clk) begin
{r_reset_ff2, r_reset_ff1} <= {r_reset_ff1, i_n64_reset};
{r_alel_ff2, r_alel_ff1} <= {r_alel_ff1, i_n64_pi_alel};
{r_aleh_ff2, r_aleh_ff1} <= {r_aleh_ff1, i_n64_pi_aleh};
{r_alel_ff3, r_alel_ff2, r_alel_ff1} <= {r_alel_ff2, r_alel_ff1, i_n64_pi_alel};
{r_aleh_ff3, r_aleh_ff2, r_aleh_ff1} <= {r_aleh_ff2, r_aleh_ff1, i_n64_pi_aleh};
{r_read_ff2, r_read_ff1} <= {r_read_ff1, i_n64_pi_read};
{r_write_ff2, r_write_ff1} <= {r_write_ff1, i_n64_pi_write};
end
@ -55,7 +57,7 @@ module n64_pi (
// PI event signals generator
wire [1:0] w_pi_mode = {r_aleh_ff2, r_alel_ff2};
wire [1:0] w_pi_mode = {r_aleh_ff3, r_alel_ff3};
reg [1:0] r_last_pi_mode;
reg r_last_read;
reg r_last_write;
@ -72,42 +74,42 @@ module n64_pi (
localparam [1:0] PI_MODE_VALID = 2'b00;
wire w_address_high_op = r_reset_ff2 && (r_last_pi_mode != PI_MODE_HIGH) && (w_pi_mode == PI_MODE_HIGH);
wire w_address_low_op = r_reset_ff2 && (r_last_pi_mode != PI_MODE_LOW) && (w_pi_mode == PI_MODE_LOW);
wire w_address_valid_op = r_reset_ff2 && (r_last_pi_mode != PI_MODE_VALID) && (w_pi_mode == PI_MODE_VALID);
wire w_address_low_op = r_reset_ff2 && (r_last_pi_mode == PI_MODE_HIGH) && (w_pi_mode == PI_MODE_LOW);
wire w_read_op = r_reset_ff2 && (w_pi_mode == PI_MODE_VALID) && r_last_read && !r_read_ff2;
wire w_write_op = r_reset_ff2 && (w_pi_mode == PI_MODE_VALID) && r_last_write && !r_write_ff2;
// Bus address register
reg [31:0] r_pi_address;
reg r_address_valid_op;
always @(posedge i_clk) begin
if (w_address_high_op) r_pi_address[31:16] <= io_n64_pi_ad;
if (w_address_low_op) r_pi_address[15:0] <= {io_n64_pi_ad[15:1], 1'b0};
r_address_valid_op <= w_address_low_op;
end
// Bank decoder, address translator and prefetch signal
wire [25:0] w_translated_address;
wire w_bank_prefetch;
wire w_ddipl_request;
wire w_prefetch = !PREFETCH_DISABLE && w_bank_prefetch;
n64_bank_decoder n64_bank_decoder_inst (
.i_address(r_pi_address),
.o_translated_address(w_translated_address),
.i_clk(i_clk),
.i_address_high_op(w_address_high_op),
.i_address_low_op(w_address_low_op),
.i_n64_pi_ad(io_n64_pi_ad),
.o_bank(o_bank),
.o_bank_prefetch(w_bank_prefetch),
.o_prefetch(w_bank_prefetch),
.o_ddipl_request(w_ddipl_request),
.o_sram_request(o_sram_request),
.i_ddipl_enable(i_ddipl_enable),
.i_sram_enable(i_sram_enable),
.i_sram_768k_mode(i_sram_768k_mode),
.i_flashram_enable(i_flashram_enable),
.i_sd_enable(i_sd_enable),
.i_eeprom_enable(i_eeprom_enable),
.i_ddipl_address(i_ddipl_address),
.i_sram_address(i_sram_address)
.i_sd_enable(i_sd_enable)
);
@ -115,9 +117,16 @@ module n64_pi (
reg r_word_counter;
wire w_bus_read_op = w_read_op && !r_word_counter;
wire w_bus_write_op = w_write_op && r_word_counter;
always @(posedge i_clk) begin
if (w_address_valid_op) r_word_counter <= 1'b0;
if (w_read_op || w_write_op) r_word_counter <= ~r_word_counter;
if (r_address_valid_op) begin
r_word_counter <= 1'b0;
end
if (w_read_op || w_write_op) begin
r_word_counter <= ~r_word_counter;
end
end
@ -127,34 +136,35 @@ module n64_pi (
always @(*) begin
io_n64_pi_ad = 16'hZZZZ;
if (r_reset_ff2 && !r_read_ff2 && o_bank != 4'd0) begin
if (r_reset_ff2 && !r_read_ff2 && o_bank != `BANK_INVALID) begin
io_n64_pi_ad = r_word_counter ? r_pi_output_data[31:16] : r_pi_output_data[15:0];
end
end
// Bus event signals generator
wire w_bus_read_op = w_read_op && !r_word_counter;
wire w_bus_write_op = w_write_op && r_word_counter;
// Read buffer logic
reg [31:0] r_pi_read_buffer;
reg r_prefetch_read;
reg [31:0] r_pi_read_buffer;
always @(posedge i_clk) begin
if (w_address_valid_op) begin
if (r_address_valid_op) begin
r_prefetch_read <= w_prefetch;
end
if (i_ack) begin
if (w_prefetch) r_pi_read_buffer <= i_data;
else r_pi_output_data <= i_data;
if (r_prefetch_read) r_pi_output_data <= i_data;
r_prefetch_read <= 1'b0;
if (w_prefetch) begin
r_pi_read_buffer <= i_data;
end else begin
r_pi_output_data <= i_data;
end
if (r_prefetch_read) begin
r_pi_output_data <= i_data;
end
end
if (w_prefetch && w_bus_read_op) begin
r_pi_output_data <= r_pi_read_buffer;
end
if (w_prefetch && w_bus_read_op) r_pi_output_data <= r_pi_read_buffer;
end
@ -178,7 +188,8 @@ module n64_pi (
reg r_pending_request;
reg r_pending_request_write;
wire w_bus_request_op = (w_address_valid_op && w_prefetch) || w_bus_read_op || w_bus_write_op;
wire w_bus_request_op = (r_address_valid_op && w_prefetch) || w_bus_read_op || w_bus_write_op;
wire w_request_successful = o_request && !i_busy;
always @(posedge i_clk) begin
if (i_reset) begin
@ -186,7 +197,7 @@ module n64_pi (
o_write <= 1'b0;
r_pending_request <= 1'b0;
end else begin
if (o_request && !i_busy) begin
if (w_request_successful) begin
o_request <= 1'b0;
o_write <= 1'b0;
end
@ -208,23 +219,46 @@ module n64_pi (
end
// Address increment logic
// Address latch and increment logic
wire [9:0] w_pi_high_address = io_n64_pi_ad[9:0];
wire [15:0] w_pi_low_address = {io_n64_pi_ad[15:1], 1'b0};
wire [25:0] w_ddipl_translated_address = o_address + {i_ddipl_address, 2'b00};
wire [25:0] w_sram_translated_address = o_address + {i_sram_address, 2'b00};
reg r_first_transfer;
wire w_address_increment_op = (
(w_bus_read_op && (!r_first_transfer || w_prefetch)) ||
(w_bus_write_op && !r_first_transfer)
);
wire w_first_transfer_clear_op = w_bus_read_op || w_bus_write_op;
reg [14:0] r_address_low_buffer;
always @(posedge i_clk) begin
if (w_address_valid_op) begin
o_address <= w_translated_address;
r_first_transfer <= 1'b1;
if (w_address_high_op) begin
o_address[25:16] <= w_pi_high_address;
end
if (w_address_low_op) begin
o_address[15:0] <= w_pi_low_address;
end
if (r_address_valid_op) begin
r_first_transfer <= w_prefetch;
r_address_low_buffer <= o_address[15:1];
if (w_ddipl_request) begin
o_address <= w_ddipl_translated_address;
r_address_low_buffer <= w_ddipl_translated_address[15:1];
end
if (o_sram_request) begin
o_address <= w_sram_translated_address;
r_address_low_buffer <= w_sram_translated_address[15:1];
end
end
if (w_request_successful) begin
o_address[15:2] <= o_address[15:2] + 1'd1;
end
if (w_bus_write_op && r_first_transfer) begin
r_first_transfer <= 1'b0;
o_address[15:1] <= r_address_low_buffer;
end
if (w_first_transfer_clear_op) r_first_transfer <= 1'b0;
if (w_address_increment_op) o_address[15:2] <= o_address[15:2] + 1'b1;
end
endmodule

View File

@ -83,6 +83,18 @@ module top (
);
// SD clock output
wire w_sd_clk;
gpio_ddro sd_clk_ddro (
.outclock(w_sd_clk),
.outclocken(1'b1),
.din({1'b0, 1'b1}),
.pad_out(o_sd_clk)
);
// N64 PI
wire w_n64_request;
@ -385,7 +397,7 @@ module top (
.i_clk(w_sys_clk),
.i_reset(w_sys_reset),
.o_sd_clk(o_sd_clk),
.o_sd_clk(w_sd_clk),
.io_sd_cmd(io_sd_cmd),
.io_sd_dat(io_sd_dat),

View File

@ -34,6 +34,7 @@ ASFLAGS = $(COMMONFLAGS)
CFLAGS = $(COMMONFLAGS) -std=gnu99 -Os -Wall -I$(ROOTDIR)/mips64-elf/include $(INC_DIRS)
LINK_FLAGS = -L$(ROOTDIR)/mips64-elf/lib -ldragon -lc -lm -ldragonsys -Tn64.ld
N64_FLAGS = -l $(ROM_SIZE) -h $(HEADER_PATH)/$(HEADER_NAME) -o $(BUILD_DIR)/$(PROG_NAME).z64
N64_FLAGS_PADDED = -l 1028k -h $(HEADER_PATH)/$(HEADER_NAME) -o $(BUILD_DIR)/$(PROG_NAME)_padded.z64
all: make_output_dir $(BUILD_DIR)/$(PROG_NAME).z64
@ -43,7 +44,9 @@ $(BUILD_DIR)/$(PROG_NAME).z64: $(BUILD_DIR)/$(PROG_NAME).elf
$(OBJCOPY) $(BUILD_DIR)/$(PROG_NAME).elf $(BUILD_DIR)/$(PROG_NAME).bin -O binary
$(OBJDUMP) -S $(BUILD_DIR)/$(PROG_NAME).elf > $(BUILD_DIR)/$(PROG_NAME).lst
$(N64TOOL) $(N64_FLAGS) -t $(PROG_NAME) $(BUILD_DIR)/$(PROG_NAME).bin
$(N64TOOL) $(N64_FLAGS_PADDED) -t $(PROG_NAME) $(BUILD_DIR)/$(PROG_NAME).bin
$(CHKSUM64) $(BUILD_DIR)/$(PROG_NAME).z64
$(CHKSUM64) $(BUILD_DIR)/$(PROG_NAME)_padded.z64
$(OBJCOPY) $(BUILD_DIR)/$(PROG_NAME).z64 $(BUILD_DIR)/$(PROG_NAME).hex -I binary -O ihex
$(BUILD_DIR)/$(PROG_NAME).elf: $(OBJ_FILES)

View File

@ -23,7 +23,7 @@ static const struct crc32_to_cic_seed {
static cart_header_t global_cart_header __attribute__((aligned(16)));
cart_header_t *boot_load_cart_header(void) {
cart_header_t *boot_load_cart_header(bool ddipl) {
cart_header_t *cart_header_pointer = &global_cart_header;
platform_pi_dma_read(cart_header_pointer, CART_BASE, sizeof(cart_header_t));
@ -81,10 +81,12 @@ void boot(cart_header_t *cart_header, uint16_t cic_seed, tv_type_t tv_type, uint
(cic_seed == crc32_to_cic_seed[8].cic_seed) ||
(cic_seed == crc32_to_cic_seed[9].cic_seed)
);
tv_type_t os_tv_type = tv_type < 0 ? OS_BOOT_CONFIG->tv_type : tv_type;
tv_type_t os_tv_type = tv_type == -1 ? OS_BOOT_CONFIG->tv_type : tv_type;
volatile uint64_t gpr_regs[32];
MI->interrupt_mask = MI_INT_CLEAR_SP | MI_INT_CLEAR_SI | MI_INT_CLEAR_AI | MI_INT_CLEAR_VI | MI_INT_CLEAR_PI | MI_INT_CLEAR_DP;
while (!(SP->status & SP_STATUS_HALT));
SP->status = SP_STATUS_CLEAR_INTERRUPT | SP_STATUS_SET_HALT;
@ -117,7 +119,7 @@ void boot(cart_header_t *cart_header, uint16_t cic_seed, tv_type_t tv_type, uint
SP_MEM->imem[1] = 0x8DA807FC; // lw t0, 0x07FC(t5)
SP_MEM->imem[2] = 0x25AD07C0; // addiu t5, t5, 0x07C0
SP_MEM->imem[3] = 0x31080080; // andi t0, t0, 0x0080
SP_MEM->imem[4] = 0x5500FFFC; // bnel t0, zero, &SP_MEM->imem[0]
SP_MEM->imem[4] = 0x5500FFFC; // bnel t0, zero, &SP_MEM->imem[1]
SP_MEM->imem[5] = 0x3C0DBFC0; // lui t5, 0xBFC0
SP_MEM->imem[6] = 0x8DA80024; // lw t0, 0x0024(t5)
SP_MEM->imem[7] = 0x3C0BB000; // lui t3, 0xB000
@ -133,7 +135,7 @@ void boot(cart_header_t *cart_header, uint16_t cic_seed, tv_type_t tv_type, uint
gpr_regs[CPU_REG_T3] = CPU_ADDRESS_IN_REG(SP_MEM->dmem[16]);
gpr_regs[CPU_REG_S3] = is_ddipl_boot ? OS_BOOT_ROM_TYPE_DD : OS_BOOT_ROM_TYPE_GAME_PAK;
gpr_regs[CPU_REG_S4] = os_tv_type;
gpr_regs[CPU_REG_S5] = OS_BOOT_CONFIG->reset_type;
gpr_regs[CPU_REG_S5] = OS_BOOT_RESET_TYPE_COLD;
gpr_regs[CPU_REG_S6] = BOOT_SEED_IPL3(cic_seed);
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]);

View File

@ -46,11 +46,15 @@ typedef struct os_boot_config_s os_boot_config_t;
#define OS_BOOT_ROM_TYPE_GAME_PAK (0)
#define OS_BOOT_ROM_TYPE_DD (1)
#define OS_BOOT_RESET_TYPE_COLD (0)
#define OS_BOOT_RESET_TYPE_NMI (1)
#define OS_BOOT_RESET_TYPE_DISK (2)
#define BOOT_SEED_IPL3(x) (((x) & 0x000000FF) >> 0)
#define BOOT_SEED_OS_VERSION(x) (((x) & 0x00000100) >> 8)
cart_header_t *boot_load_cart_header(void);
cart_header_t *boot_load_cart_header(bool ddipl);
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, uint32_t ddipl_override);

View File

@ -72,6 +72,13 @@ typedef struct DP_CMD_regs_s {
volatile uint32_t tmem;
} DP_CMD_regs_t;
typedef struct MI_regs_s {
volatile uint32_t mode;
volatile uint32_t version;
volatile uint32_t interrupt;
volatile uint32_t interrupt_mask;
} MI_regs_t;
typedef struct VI_regs_s {
volatile uint32_t control;
volatile void *dram_addr;
@ -118,6 +125,7 @@ typedef struct PI_regs_s {
#define SP_MEM_BASE (0xA4000000)
#define SP_REGS_BASE (0xA4040000)
#define DP_CMD_REGS_BASE (0xA4100000)
#define MI_REGS_BASE (0xA4300000)
#define VI_REGS_BASE (0xA4400000)
#define AI_REGS_BASE (0xA4500000)
#define PI_REGS_BASE (0xA4600000)
@ -127,6 +135,7 @@ typedef struct PI_regs_s {
#define SP_MEM ((volatile SP_MEM_t *) SP_MEM_BASE)
#define SP ((volatile SP_regs_t *) SP_REGS_BASE)
#define DP_CMD ((volatile DP_CMD_regs_t *) DP_CMD_REGS_BASE)
#define MI ((volatile MI_regs_t *) MI_REGS_BASE)
#define VI ((volatile VI_regs_t *) VI_REGS_BASE)
#define AI ((volatile AI_regs_t *) AI_REGS_BASE)
#define PI ((volatile PI_regs_t *) PI_REGS_BASE)
@ -145,6 +154,13 @@ typedef struct PI_regs_s {
#define DP_CMD_STATUS_CLEAR_FREEZE (1 << 2)
#define DP_CMD_STATUS_CLEAR_FLUSH (1 << 4)
#define MI_INT_CLEAR_SP (1 << 0)
#define MI_INT_CLEAR_SI (1 << 2)
#define MI_INT_CLEAR_AI (1 << 4)
#define MI_INT_CLEAR_VI (1 << 6)
#define MI_INT_CLEAR_PI (1 << 8)
#define MI_INT_CLEAR_DP (1 << 10)
#define PI_STATUS_DMA_BUSY (1 << 0)
#define PI_STATUS_IO_BUSY (1 << 1)
#define PI_STATUS_RESET_CONTROLLER (1 << 0)

View File

@ -9,7 +9,7 @@ DSTATUS disk_status(BYTE pdrv) {
return STA_NOINIT;
}
return sc64_sd_get_status() ? 0 : STA_NOINIT;
return sc64_sd_status_get() ? 0 : STA_NOINIT;
}
DSTATUS disk_initialize(BYTE pdrv) {
@ -17,7 +17,7 @@ DSTATUS disk_initialize(BYTE pdrv) {
return STA_NOINIT;
}
if (!sc64_sd_get_status()) {
if (!sc64_sd_status_get()) {
if (sc64_sd_init()) {
return 0;
}
@ -31,19 +31,22 @@ DSTATUS disk_initialize(BYTE pdrv) {
DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
sc64_sd_err_t error;
if ((pdrv > 0) || (count == 0)) {
if (pdrv > 0) {
return RES_PARERR;
}
if (!sc64_sd_get_status()) {
return RES_NOTRDY;
}
error = sc64_sd_read_sectors(sector, count, buff);
error = sc64_sd_sectors_read(sector, count, buff);
if (error != E_OK) {
switch (error) {
case E_NO_INIT:
return RES_NOTRDY;
case E_PAR_ERROR:
return RES_PARERR;
default:
return RES_ERROR;
}
}
return RES_OK;
}
@ -51,8 +54,27 @@ DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
#if !FF_FS_READONLY
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count) {
sc64_sd_err_t error;
if (pdrv > 0) {
return RES_PARERR;
}
error = sc64_sd_sectors_write(sector, count, (uint8_t *) buff);
if (error != E_OK) {
switch (error) {
case E_NO_INIT:
return RES_NOTRDY;
case E_PAR_ERROR:
return RES_PARERR;
default:
return RES_ERROR;
}
}
return RES_OK;
}
#endif

View File

@ -25,7 +25,7 @@
/ 3: f_lseek() function is removed in addition to 2. */
#define FF_USE_STRFUNC 0
#define FF_USE_STRFUNC 2
/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf().
/
/ 0: Disable string functions.

View File

@ -15,6 +15,8 @@
#define NTOA(n) ('0' + ((n) % 10))
static bool loader_initialized = false;
static char version_string[] = "SC64 Bootloader ver. X.X";
static char error_number_string[] = "ERROR X";
static const char *error_strings[] = {
@ -31,12 +33,12 @@ static int x_offset = X_OFFSET;
static int y_offset = Y_OFFSET;
static display_context_t loader_get_display(void) {
static display_context_t loader_get_display(bool lock) {
display_context_t display;
do {
display = display_lock();
} while (!display);
} while (!display && lock);
x_offset = X_OFFSET;
y_offset = Y_OFFSET;
@ -58,9 +60,7 @@ static void loader_draw_version_and_logo(display_context_t display) {
graphics_draw_sprite(display, center_x, center_y, logo);
}
void loader_init(void) {
display_context_t display;
static void loader_init(void) {
init_interrupts();
audio_init(44100, 2);
@ -68,22 +68,63 @@ void loader_init(void) {
display_init(RESOLUTION_320x240, DEPTH_32_BPP, 2, GAMMA_NONE, ANTIALIAS_OFF);
display = loader_get_display();
loader_initialized = true;
}
void loader_cleanup(void) {
audio_close();
display_close();
disable_interrupts();
loader_initialized = false;
}
void loader_display_logo(void) {
if (!loader_initialized) {
loader_init();
}
display_context_t display;
display = loader_get_display(true);
graphics_fill_screen(display, 0);
loader_draw_version_and_logo(display);
display_show(display);
}
void loader_cleanup(void) {
audio_close();
display_close();
void loader_display_message(const char *message) {
if (!loader_initialized) {
loader_init();
}
void loader_display_error_and_halt(menu_load_error_t error, const char *path) {
display_context_t display;
display = loader_get_display();
display = loader_get_display(true);
graphics_fill_screen(display, 0);
loader_draw_version_and_logo(display);
graphics_draw_text(display, x_offset, y_offset, message);
display_show(display);
}
void loader_display_error_and_halt(menu_load_error_t error, const char *message) {
if (!loader_initialized) {
loader_init();
}
display_context_t display;
display = loader_get_display(true);
graphics_fill_screen(display, 0);
loader_draw_version_and_logo(display);
@ -94,11 +135,9 @@ void loader_display_error_and_halt(menu_load_error_t error, const char *path) {
int error_string_index = error >= E_MENU_END ? (E_MENU_END - 1) : error;
const char *error_string = error_strings[error_string_index];
graphics_draw_text(display, x_offset, y_offset, error_string);
y_offset += Y_PADDING;
y_offset += Y_PADDING * 2;
if (error == E_MENU_ERROR_NO_FILE) {
graphics_draw_text(display, x_offset, y_offset, path);
}
graphics_draw_text(display, x_offset, y_offset, message);
display_show(display);

View File

@ -5,9 +5,10 @@
#include "errors.h"
void loader_init(void);
void loader_cleanup(void);
void loader_display_error_and_halt(menu_load_error_t error, const char *path);
void loader_display_logo(void);
void loader_display_message(const char *message);
void loader_display_error_and_halt(menu_load_error_t error, const char *message);
#endif

View File

@ -4,11 +4,32 @@
#include "sc64/sc64_sd_fs.h"
static const char *MENU_FILE_PATH = "SC64/MENU.z64";
#define DEFAULT_MENU_FILE_PATH "SC64/MENU.z64\0";
static const char *CONFIG_FILE_PATH = "SC64/config.txt";
static menu_load_error_t convert_error(sc64_sd_fs_error_t sd_fs_error) {
switch (sd_fs_error) {
case SC64_SD_FS_NO_CARD:
return E_MENU_ERROR_NO_CARD;
case SC64_SD_FS_NO_FILESYSTEM:
return E_MENU_ERROR_NO_FILESYSTEM;
case SC64_SD_FS_NO_FILE:
return E_MENU_ERROR_NO_FILE;
case SC64_SD_FS_READ_ERROR:
return E_MENU_ERROR_READ_ERROR;
case SC64_SD_FS_OTHER_ERROR:
return E_MENU_ERROR_OTHER_ERROR;
default:
return E_MENU_OK;
}
}
int main(void) {
loader_init();
OS_BOOT_CONFIG->tv_type = TV_NTSC;
if (sc64_get_version() != SC64_CART_VERSION_A) {
loader_display_error_and_halt(E_MENU_ERROR_NOT_SC64, "");
@ -18,40 +39,99 @@ int main(void) {
uint32_t boot_mode = sc64_get_boot_mode();
uint32_t skip_menu = (boot_mode & SC64_CART_BOOT_SKIP_MENU);
uint32_t cic_seed_override = (boot_mode & SC64_CART_BOOT_CIC_SEED_OVERRIDE);
uint32_t tv_type_override = (boot_mode & SC64_CART_BOOT_TV_TYPE_OVERRIDE);
uint32_t ddipl_override = (boot_mode & SC64_CART_BOOT_DDIPL_OVERRIDE);
bool skip_menu = (boot_mode & SC64_CART_BOOT_SKIP_MENU);
bool cic_seed_override = (boot_mode & SC64_CART_BOOT_CIC_SEED_OVERRIDE);
bool tv_type_override = (boot_mode & SC64_CART_BOOT_TV_TYPE_OVERRIDE);
bool ddipl_override = (boot_mode & SC64_CART_BOOT_DDIPL_OVERRIDE);
bool rom_loaded = (boot_mode & SC64_CART_BOOT_ROM_LOADED);
tv_type_t tv_type = ((boot_mode & SC64_CART_BOOT_TV_TYPE_MASK) >> SC64_CART_BOOT_TV_TYPE_BIT);
uint16_t cic_seed = ((boot_mode & SC64_CART_BOOT_CIC_SEED_MASK) >> SC64_CART_BOOT_CIC_SEED_BIT);
if (!skip_menu) {
sc64_sd_fs_error_t sd_error = sc64_sd_fs_load_rom(MENU_FILE_PATH);
char rom_path[256] = DEFAULT_MENU_FILE_PATH;
char save_path[256] = "\0";
sc64_sd_fs_error_t sd_fs_error;
sc64_sd_fs_config_t config = {
.rom = rom_path,
.rom_reload = false,
.save = save_path,
.save_type = 0,
.save_writeback = false,
.cic_seed = 0xFFFF,
.tv_type = -1,
};
menu_load_error_t error = E_MENU_OK;
switch (sd_error) {
case SC64_SD_FS_NO_CARD:
error = E_MENU_ERROR_NO_CARD;
break;
case SC64_SD_FS_NO_FILESYSTEM:
error = E_MENU_ERROR_NO_FILESYSTEM;
break;
case SC64_SD_FS_NO_FILE:
error = E_MENU_ERROR_NO_FILE;
break;
case SC64_SD_FS_READ_ERROR:
error = E_MENU_ERROR_READ_ERROR;
break;
case SC64_SD_FS_OTHER_ERROR:
error = E_MENU_ERROR_OTHER_ERROR;
break;
default:
break;
sd_fs_error = sc64_sd_fs_init();
if (sd_fs_error != SC64_SD_FS_OK) {
loader_display_error_and_halt(convert_error(sd_fs_error), "sc64_sd_fs_init");
}
if (error != E_MENU_OK) {
loader_display_error_and_halt(error, MENU_FILE_PATH);
sd_fs_error = sc64_sd_fs_load_config(CONFIG_FILE_PATH, &config);
if ((sd_fs_error != SC64_SD_FS_OK) && (sd_fs_error != SC64_SD_FS_NO_FILE)) {
loader_display_error_and_halt(convert_error(sd_fs_error), "sc64_sd_fs_load_config");
}
if (config.cic_seed != 0xFFFF) {
cic_seed_override = true;
cic_seed = config.cic_seed;
}
if (config.tv_type != -1) {
tv_type_override = true;
tv_type = config.tv_type;
}
if (!rom_loaded || config.rom_reload) {
loader_display_logo();
}
if (config.save_type > 0) {
sc64_disable_eeprom();
sc64_disable_sram();
sc64_disable_flashram();
switch (config.save_type) {
case 1: sc64_enable_eeprom(false); break;
case 2: sc64_enable_eeprom(true); break;
case 3: sc64_enable_sram(false); break;
case 4: sc64_enable_sram(true); break;
case 5: sc64_enable_flashram(); break;
}
if (config.save_type == 3) {
sc64_set_sram_address(SC64_SDRAM_SIZE - (32 * 1024));
} else if (config.save_type == 4) {
sc64_set_sram_address(SC64_SDRAM_SIZE - (3 * 32 * 1024));
}
if (rom_loaded && (config.save[0] != '\0') && config.save_writeback) {
sd_fs_error = sc64_sd_fs_store_save(config.save);
if (sd_fs_error != SC64_SD_FS_OK) {
loader_display_error_and_halt(convert_error(sd_fs_error), "sc64_sd_fs_store_save");
}
}
}
if (!rom_loaded || config.rom_reload) {
sd_fs_error = sc64_sd_fs_load_rom(config.rom);
if (sd_fs_error != SC64_SD_FS_OK) {
loader_display_error_and_halt(convert_error(sd_fs_error), "sc64_sd_fs_load_rom");
}
sc64_set_boot_mode(boot_mode | SC64_CART_BOOT_ROM_LOADED);
}
if ((config.save_type > 0) && (config.save[0] != '\0') && !rom_loaded) {
sd_fs_error = sc64_sd_fs_load_save(config.save);
if (sd_fs_error != SC64_SD_FS_OK) {
loader_display_error_and_halt(convert_error(sd_fs_error), "sc64_sd_fs_load_save");
}
}
sc64_sd_fs_deinit();
if (!rom_loaded || config.rom_reload) {
loader_cleanup();
}
}
@ -61,7 +141,7 @@ int main(void) {
sc64_disable_ddipl();
}
cart_header_t *cart_header = boot_load_cart_header();
cart_header_t *cart_header = boot_load_cart_header(ddipl_override);
if (!cic_seed_override) {
cic_seed = boot_get_cic_seed(cart_header);
@ -71,7 +151,5 @@ int main(void) {
tv_type = boot_get_tv_type(cart_header);
}
loader_cleanup();
boot(cart_header, cic_seed, tv_type, ddipl_override);
}

View File

@ -25,6 +25,14 @@ void sc64_set_scr(uint32_t scr) {
platform_pi_io_write(&SC64_CART->SCR, scr);
}
void sc64_enable_skip_bootloader(void) {
sc64_enable_peripheral(SC64_CART_SCR_SKIP_BOOTLOADER);
}
void sc64_disable_skip_bootloader(void) {
sc64_disable_peripheral(SC64_CART_SCR_SKIP_BOOTLOADER);
}
void sc64_enable_flashram(void) {
sc64_enable_peripheral(SC64_CART_SCR_FLASHRAM_ENABLE);
}
@ -33,7 +41,7 @@ void sc64_disable_flashram(void) {
sc64_disable_peripheral(SC64_CART_SCR_FLASHRAM_ENABLE);
}
void sc64_enable_sram(uint8_t mode_768k) {
void sc64_enable_sram(bool mode_768k) {
sc64_enable_peripheral(SC64_CART_SCR_SRAM_ENABLE);
if (mode_768k) {
sc64_enable_peripheral(SC64_CART_SCR_SRAM_768K_MODE);
@ -62,7 +70,7 @@ void sc64_disable_eeprom_pi(void) {
sc64_disable_peripheral(SC64_CART_SCR_EEPROM_PI_ENABLE);
}
void sc64_enable_eeprom(uint8_t mode_16k) {
void sc64_enable_eeprom(bool mode_16k) {
sc64_enable_peripheral(SC64_CART_SCR_EEPROM_ENABLE);
if (mode_16k) {
sc64_enable_peripheral(SC64_CART_SCR_EEPROM_16K_MODE);

View File

@ -10,15 +10,17 @@
uint32_t sc64_get_scr(void);
void sc64_set_scr(uint32_t scr);
void sc64_enable_skip_bootloader(void);
void sc64_disable_skip_bootloader(void);
void sc64_enable_flashram(void);
void sc64_disable_flashram(void);
void sc64_enable_sram(uint8_t mode_768k);
void sc64_enable_sram(bool mode_768k);
void sc64_disable_sram(void);
void sc64_enable_sd(void);
void sc64_disable_sd(void);
void sc64_enable_eeprom_pi(void);
void sc64_disable_eeprom_pi(void);
void sc64_enable_eeprom(uint8_t mode_16k);
void sc64_enable_eeprom(bool mode_16k);
void sc64_disable_eeprom(void);
void sc64_enable_ddipl(void);
void sc64_disable_ddipl(void);

View File

@ -36,6 +36,7 @@ typedef struct sc64_cart_registers {
#define SC64_CART ((__IO sc64_cart_registers_t *) SC64_CART_BASE)
#define SC64_CART_SCR_SKIP_BOOTLOADER (1 << 10)
#define SC64_CART_SCR_FLASHRAM_ENABLE (1 << 9)
#define SC64_CART_SCR_SRAM_768K_MODE (1 << 8)
#define SC64_CART_SCR_SRAM_ENABLE (1 << 7)
@ -101,7 +102,7 @@ typedef struct sc64_eeprom_registers {
__IO reg_t MEM[512]; // EEPROM memory
} sc64_eeprom_registers_t;
#define SC64_EEPROM_BASE (0x1E004000)
#define SC64_EEPROM_BASE (0x1E010000)
#define SC64_EEPROM ((__IO sc64_eeprom_registers_t *) SC64_EEPROM_BASE)
@ -121,7 +122,7 @@ typedef struct sc64_sd_registers_s {
__IO reg_t FIFO[128]; // SD data path FIFO buffer
} sc64_sd_registers_t;
#define SC64_SD_BASE (0x1E008000)
#define SC64_SD_BASE (0x1E020000)
#define SC64_SD ((__IO sc64_sd_registers_t *) SC64_SD_BASE)
@ -146,10 +147,12 @@ typedef struct sc64_sd_registers_s {
#define SC64_SD_DAT_TX_FIFO_ITEMS_GET(dat) (((dat) >> 17) & 0x1FF)
#define SC64_SD_DAT_TX_FIFO_BYTES_GET(dat) (SC64_SD_DAT_TX_FIFO_ITEMS_GET(dat) * 4)
#define SC64_SD_DAT_TX_FIFO_FULL (1 << 16)
#define SC64_SD_DAT_TX_FIFO_EMPTY (1 << 15)
#define SC64_SD_DAT_TX_FIFO_UNDERRUN (1 << 14)
#define SC64_SD_DAT_RX_FIFO_ITEMS_GET(dat) (((dat) >> 5) & 0x1FF)
#define SC64_SD_DAT_RX_FIFO_BYTES_GET(dat) (SC64_SD_DAT_RX_FIFO_ITEMS_GET(dat) * 4)
#define SC64_SD_DAT_RX_FIFO_FULL (1 << 4)
#define SC64_SD_DAT_RX_FIFO_EMPTY (1 << 3)
#define SC64_SD_DAT_RX_FIFO_OVERRUN (1 << 2)
@ -164,6 +167,10 @@ typedef struct sc64_sd_registers_s {
#define SC64_SD_DAT_STOP (1 << 1)
#define SC64_SD_DAT_START (1 << 0)
#define SC64_SD_DAT_FIFO_SIZE_BYTES (1024)
#define SC64_SD_DAT_NUM_BLOCKS_MAX (256)
#define SC64_SD_DAT_BLOCK_SIZE_MAX (512)
#define SC64_SD_DMA_SCR_BUSY (1 << 0)
@ -181,5 +188,7 @@ typedef struct sc64_sd_registers_s {
#define SC64_SD_DMA_LEN(l) ((((l) / 4) - 1) & 0x7FFF)
#define SC64_SD_DMA_LEN_MAX (0x20000)
#endif

View File

@ -15,8 +15,6 @@
#define SD_BLOCK_SIZE (512)
#define MAX_NUM_BLOCKS (256)
typedef enum sc64_sd_clock_e {
CLOCK_STOP,
@ -44,6 +42,11 @@ typedef enum sc64_sd_dat_direction_e {
DAT_DIR_TX,
} sc64_sd_dat_direction_t;
typedef enum sc64_sd_dma_direction_e {
DMA_DIR_MEM,
DMA_DIR_CARD,
} sc64_sd_dma_direction_t;
static bool sd_card_initialized = false;
static bool sd_card_type_block = false;
@ -85,26 +88,24 @@ static void sc64_sd_set_dat_width(sc64_sd_dat_width_t dat_width) {
platform_pi_io_write(&SC64_SD->SCR, scr);
}
static void sc64_sd_hw_init(void) {
sc64_enable_sd();
static void sc64_sd_hw_reset(void) {
while (platform_pi_io_read(&SC64_SD->CMD) & SC64_SD_CMD_BUSY);
platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_STOP);
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_TX_FIFO_FLUSH | SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP);
platform_pi_io_write(&SC64_SD->SCR, 0);
}
static void sc64_sd_hw_init(void) {
sc64_enable_sd();
sc64_sd_hw_reset();
sc64_sd_set_clock(CLOCK_400_KHZ);
sc64_sd_set_dat_width(DAT_WIDTH_1BIT);
}
static void sc64_sd_hw_deinit(void) {
if (sc64_get_scr() & SC64_CART_SCR_SD_ENABLE) {
while (platform_pi_io_read(&SC64_SD->CMD) & SC64_SD_CMD_BUSY);
platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_STOP);
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_TX_FIFO_FLUSH | SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP);
platform_pi_io_write(&SC64_SD->SCR, 0);
sc64_sd_hw_reset();
}
sc64_disable_sd();
}
@ -176,14 +177,15 @@ static sc64_sd_err_t sc64_sd_dat_read(size_t block_size, void *buffer) {
int timeout;
uint32_t reg;
timeout = 100000;
timeout = 1000000;
do {
reg = platform_pi_io_read(&SC64_SD->DAT);
if (SC64_SD_DAT_RX_FIFO_ITEMS_GET(reg) >= block_size) {
if (SC64_SD_DAT_RX_FIFO_BYTES_GET(reg) >= block_size) {
break;
}
} while ((reg & SC64_SD_DAT_BUSY) && (--timeout));
if (!(reg & SC64_SD_DAT_BUSY)) {
if (reg & SC64_SD_DAT_CRC_ERROR) {
return E_CRC_ERROR;
}
@ -193,6 +195,7 @@ static sc64_sd_err_t sc64_sd_dat_read(size_t block_size, void *buffer) {
return E_FIFO_ERROR;
}
}
if (timeout == 0) {
sc64_sd_dat_abort();
@ -206,6 +209,64 @@ static sc64_sd_err_t sc64_sd_dat_read(size_t block_size, void *buffer) {
return E_OK;
}
static sc64_sd_err_t sc64_sd_dat_write(size_t block_size, void *buffer) {
int timeout;
uint32_t reg;
timeout = 1000000;
do {
reg = platform_pi_io_read(&SC64_SD->DAT);
if ((SC64_SD_DAT_FIFO_SIZE_BYTES - SC64_SD_DAT_TX_FIFO_BYTES_GET(reg)) >= block_size) {
break;
}
} while ((reg & SC64_SD_DAT_BUSY) && (--timeout));
if (!(reg & SC64_SD_DAT_BUSY)) {
if (reg & SC64_SD_DAT_CRC_ERROR) {
return E_CRC_ERROR;
}
if (reg & SC64_SD_DAT_TX_FIFO_UNDERRUN) {
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_TX_FIFO_FLUSH);
return E_FIFO_ERROR;
}
}
if (timeout == 0) {
sc64_sd_dat_abort();
return E_TIMEOUT;
}
platform_pi_dma_write(buffer, &SC64_SD->FIFO, block_size);
platform_cache_invalidate(buffer, block_size);
return E_OK;
}
static void sc64_sd_dma_prepare(size_t num_blocks, size_t block_size, sc64_sd_dma_direction_t direction, uint8_t bank, uint32_t address) {
platform_pi_io_write(&SC64_SD->DMA_ADDR, SC64_SD_DMA_BANK_ADDR(bank, address));
platform_pi_io_write(&SC64_SD->DMA_LEN, SC64_SD_DMA_LEN(num_blocks * block_size));
platform_pi_io_write(&SC64_SD->DMA_SCR, (direction == DMA_DIR_MEM ? SC64_SD_DMA_SCR_DIRECTION : 0) | SC64_SD_DMA_SCR_START);
}
static void sc64_sd_dma_abort(void) {
platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_STOP);
}
static sc64_sd_err_t sc64_sd_sectors_parameters_check(size_t count, uint8_t *buffer, bool check_buffer) {
if (!sd_card_initialized) {
return E_NO_INIT;
}
if ((count == 0) || (check_buffer && (buffer == NULL))) {
return E_PAR_ERROR;
}
return E_OK;
}
bool sc64_sd_init(void) {
sc64_sd_err_t error;
@ -316,23 +377,18 @@ void sc64_sd_deinit(void) {
sc64_sd_hw_deinit();
}
bool sc64_sd_get_status(void) {
bool sc64_sd_status_get(void) {
return sd_card_initialized;
}
sc64_sd_err_t sc64_sd_read_sectors(uint32_t starting_sector, size_t count, void *buffer) {
sc64_sd_err_t sc64_sd_sectors_read(uint32_t starting_sector, size_t count, uint8_t *buffer) {
sc64_sd_err_t error;
uint32_t response;
uint32_t current_sector;
uint32_t reg;
int timeout;
if (!sd_card_initialized) {
return E_NO_INIT;
}
if ((count == 0) || (buffer == NULL)) {
return E_PAR_ERROR;
error = sc64_sd_sectors_parameters_check(count, buffer, true);
if (error != E_OK) {
return error;
}
current_sector = starting_sector;
@ -341,39 +397,20 @@ sc64_sd_err_t sc64_sd_read_sectors(uint32_t starting_sector, size_t count, void
}
for (size_t i = 0; i < count; i++) {
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_NUM_BLOCKS(1) | SC64_SD_DAT_BLOCK_SIZE(SD_BLOCK_SIZE) | SC64_SD_DAT_START);
sc64_sd_dat_prepare(1, SD_BLOCK_SIZE, DAT_DIR_RX);
error = sc64_sd_cmd_send(17, current_sector, NO_FLAGS, &response);
if (error != E_OK) {
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP);
sc64_sd_dat_abort();
return error;
}
timeout = 100000;
do {
reg = platform_pi_io_read(&SC64_SD->DAT);
} while ((reg & SC64_SD_DAT_BUSY) && (--timeout));
if (reg & SC64_SD_DAT_CRC_ERROR) {
return E_CRC_ERROR;
error = sc64_sd_dat_read(SD_BLOCK_SIZE, buffer);
if (error != E_OK) {
return error;
}
if (reg & SC64_SD_DAT_RX_FIFO_OVERRUN) {
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH);
return E_FIFO_ERROR;
}
if (timeout == 0) {
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP);
return E_TIMEOUT;
}
platform_pi_dma_read(buffer, &SC64_SD->FIFO, SD_BLOCK_SIZE);
platform_cache_invalidate(buffer, SD_BLOCK_SIZE);
buffer += SD_BLOCK_SIZE;
current_sector += sd_card_type_block ? 1 : SD_BLOCK_SIZE;
}
@ -381,7 +418,44 @@ sc64_sd_err_t sc64_sd_read_sectors(uint32_t starting_sector, size_t count, void
return E_OK;
}
sc64_sd_err_t sc64_sd_read_sectors_dma(uint32_t starting_sector, size_t count, uint8_t bank, uint32_t address) {
sc64_sd_err_t sc64_sd_sectors_write(uint32_t starting_sector, size_t count, uint8_t *buffer) {
sc64_sd_err_t error;
uint32_t response;
uint32_t current_sector;
error = sc64_sd_sectors_parameters_check(count, buffer, true);
if (error != E_OK) {
return error;
}
current_sector = starting_sector;
if (!sd_card_type_block) {
current_sector *= SD_BLOCK_SIZE;
}
for (size_t i = 0; i < count; i++) {
sc64_sd_dat_prepare(1, SD_BLOCK_SIZE, DAT_DIR_TX);
error = sc64_sd_cmd_send(24, current_sector, NO_FLAGS, &response);
if (error != E_OK) {
sc64_sd_dat_abort();
return error;
}
error = sc64_sd_dat_write(SD_BLOCK_SIZE, buffer);
if (error != E_OK) {
return error;
}
buffer += SD_BLOCK_SIZE;
current_sector += sd_card_type_block ? 1 : SD_BLOCK_SIZE;
}
return E_OK;
}
sc64_sd_err_t sc64_sd_sectors_read_dma(uint32_t starting_sector, size_t count, uint8_t bank, uint32_t address) {
size_t sectors_left;
uint32_t current_sector;
uint32_t current_address;
@ -391,12 +465,9 @@ sc64_sd_err_t sc64_sd_read_sectors_dma(uint32_t starting_sector, size_t count, u
uint32_t response;
int timeout;
if (!sd_card_initialized) {
return E_NO_INIT;
}
if (count == 0) {
return E_PAR_ERROR;
error = sc64_sd_sectors_parameters_check(count, NULL, false);
if (error != E_OK) {
return error;
}
sectors_left = count;
@ -407,16 +478,15 @@ sc64_sd_err_t sc64_sd_read_sectors_dma(uint32_t starting_sector, size_t count, u
current_address = address;
do {
num_blocks = (sectors_left > MAX_NUM_BLOCKS) ? MAX_NUM_BLOCKS : sectors_left;
num_blocks = (sectors_left > SC64_SD_DAT_NUM_BLOCKS_MAX) ? SC64_SD_DAT_NUM_BLOCKS_MAX : sectors_left;
platform_pi_io_write(&SC64_SD->DMA_ADDR, SC64_SD_DMA_BANK_ADDR(bank, current_address));
platform_pi_io_write(&SC64_SD->DMA_LEN, SC64_SD_DMA_LEN(num_blocks * SD_BLOCK_SIZE));
platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_DIRECTION | SC64_SD_DMA_SCR_START);
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_NUM_BLOCKS(num_blocks) | SC64_SD_DAT_BLOCK_SIZE(SD_BLOCK_SIZE) | SC64_SD_DAT_START);
sc64_sd_dma_prepare(num_blocks, SD_BLOCK_SIZE, DMA_DIR_MEM, bank, current_address);
sc64_sd_dat_prepare(num_blocks, SD_BLOCK_SIZE, DAT_DIR_RX);
error = sc64_sd_cmd_send(18, current_sector, NO_FLAGS, &response);
if (error != E_OK) {
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP);
sc64_sd_dat_abort();
sc64_sd_dma_abort();
return error;
}
@ -428,23 +498,28 @@ sc64_sd_err_t sc64_sd_read_sectors_dma(uint32_t starting_sector, size_t count, u
error = sc64_sd_cmd_send(12, 0, NO_FLAGS, &response);
if (error != E_OK) {
sc64_sd_dat_abort();
sc64_sd_dma_abort();
return error;
}
if (reg & SC64_SD_DAT_CRC_ERROR) {
sc64_sd_dma_abort();
return E_CRC_ERROR;
}
if (reg & SC64_SD_DAT_RX_FIFO_OVERRUN) {
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH);
platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_STOP);
sc64_sd_dma_abort();
return E_FIFO_ERROR;
}
if (timeout == 0) {
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_RX_FIFO_FLUSH | SC64_SD_DAT_STOP);
platform_pi_io_write(&SC64_SD->DMA_SCR, SC64_SD_DMA_SCR_STOP);
sc64_sd_dat_abort();
sc64_sd_dma_abort();
return E_TIMEOUT;
}
@ -458,3 +533,80 @@ sc64_sd_err_t sc64_sd_read_sectors_dma(uint32_t starting_sector, size_t count, u
return E_OK;
}
sc64_sd_err_t sc64_sd_sectors_write_dma(uint32_t starting_sector, size_t count, uint8_t bank, uint32_t address) {
size_t sectors_left;
uint32_t current_sector;
uint32_t current_address;
uint32_t num_blocks;
uint32_t reg;
sc64_sd_err_t error;
uint32_t response;
int timeout;
error = sc64_sd_sectors_parameters_check(count, NULL, false);
if (error != E_OK) {
return error;
}
sectors_left = count;
current_sector = starting_sector;
if (!sd_card_type_block) {
current_sector *= SD_BLOCK_SIZE;
}
current_address = address;
do {
num_blocks = (sectors_left > SC64_SD_DAT_NUM_BLOCKS_MAX) ? SC64_SD_DAT_NUM_BLOCKS_MAX : sectors_left;
sc64_sd_dat_prepare(num_blocks, SD_BLOCK_SIZE, DAT_DIR_TX);
sc64_sd_dma_prepare(num_blocks, SD_BLOCK_SIZE, DMA_DIR_CARD, bank, current_address);
error = sc64_sd_cmd_send(25, current_sector, NO_FLAGS, &response);
if (error != E_OK) {
sc64_sd_dma_abort();
sc64_sd_dat_abort();
return error;
}
timeout = 1000000;
do {
reg = platform_pi_io_read(&SC64_SD->DAT);
} while ((reg & SC64_SD_DAT_BUSY) && (--timeout));
error = sc64_sd_cmd_send(12, 0, NO_FLAGS, &response);
if (error != E_OK) {
sc64_sd_dma_abort();
sc64_sd_dat_abort();
return error;
}
if (reg & SC64_SD_DAT_CRC_ERROR) {
sc64_sd_dma_abort();
return E_CRC_ERROR;
}
if (reg & SC64_SD_DAT_TX_FIFO_UNDERRUN) {
sc64_sd_dma_abort();
platform_pi_io_write(&SC64_SD->DAT, SC64_SD_DAT_TX_FIFO_FLUSH);
return E_FIFO_ERROR;
}
if (timeout == 0) {
sc64_sd_dma_abort();
sc64_sd_dat_abort();
return E_TIMEOUT;
}
sectors_left -= num_blocks;
current_sector += num_blocks * (sd_card_type_block ? 1 : SD_BLOCK_SIZE);
current_address += num_blocks * SD_BLOCK_SIZE;
} while (sectors_left > 0);
return E_OK;
}

View File

@ -18,9 +18,11 @@ typedef enum sc64_sd_err_e {
bool sc64_sd_init(void);
void sc64_sd_deinit(void);
bool sc64_sd_get_status(void);
sc64_sd_err_t sc64_sd_read_sectors(uint32_t starting_sector, size_t count, void *buffer);
sc64_sd_err_t sc64_sd_read_sectors_dma(uint32_t starting_sector, size_t count, uint8_t bank, uint32_t address);
bool sc64_sd_status_get(void);
sc64_sd_err_t sc64_sd_sectors_read(uint32_t starting_sector, size_t count, uint8_t *buffer);
sc64_sd_err_t sc64_sd_sectors_write(uint32_t starting_sector, size_t count, uint8_t *buffer);
sc64_sd_err_t sc64_sd_sectors_read_dma(uint32_t starting_sector, size_t count, uint8_t bank, uint32_t address);
sc64_sd_err_t sc64_sd_sectors_write_dma(uint32_t starting_sector, size_t count, uint8_t bank, uint32_t address);
#endif

View File

@ -5,69 +5,234 @@
#include "sc64_sd.h"
#include "sc64_sd_fs.h"
#include <string.h>
#include <stdlib.h>
static DRESULT sc64_sd_fs_load_rom_with_dma(BYTE pdrv, FSIZE_t offset, LBA_t sector, UINT count) {
if ((pdrv > 0) || (count == 0)) {
static uint8_t current_bank = SC64_BANK_INVALID;
static uint32_t current_offset = 0;
static uint8_t save_buffer[128 * 1024] __attribute__((aligned(16)));
static DRESULT sc64_sd_fs_load_with_dma(BYTE pdrv, FSIZE_t offset, LBA_t sector, UINT count) {
sc64_sd_err_t error;
if (pdrv > 0) {
return RES_PARERR;
}
if (!sc64_sd_get_status()) {
return RES_NOTRDY;
}
error = sc64_sd_sectors_read_dma(sector, count, current_bank, current_offset + offset);
if (sc64_sd_read_sectors_dma(sector, count, SC64_BANK_SDRAM, offset) != E_OK) {
if (error != E_OK) {
switch (error) {
case E_NO_INIT:
return RES_NOTRDY;
case E_PAR_ERROR:
return RES_PARERR;
default:
return RES_ERROR;
}
}
return RES_OK;
}
sc64_sd_fs_error_t sc64_sd_fs_load_rom(const char *path) {
static bool fs_initialized = false;
static FATFS fatfs;
sc64_sd_fs_error_t sc64_sd_fs_init(void) {
FRESULT fresult;
FATFS fatfs;
sc64_sd_fs_error_t error = SC64_SD_FS_OK;
do {
fresult = f_mount(&fatfs, "", 1);
if (fresult != FR_OK) {
switch (fresult) {
case FR_DISK_ERR:
return SC64_SD_FS_READ_ERROR;
case FR_NOT_READY:
error = SC64_SD_FS_NO_CARD;
break;
return SC64_SD_FS_NO_CARD;
case FR_NO_FILESYSTEM:
error = SC64_SD_FS_NO_FILESYSTEM;
break;
return SC64_SD_FS_NO_FILESYSTEM;
default:
error = SC64_SD_FS_OTHER_ERROR;
break;
return SC64_SD_FS_OTHER_ERROR;
}
break;
}
fresult = fe_load(path, SC64_SDRAM_SIZE, sc64_sd_fs_load_rom_with_dma);
if (fresult) {
fs_initialized = true;
return SC64_SD_FS_OK;
}
void sc64_sd_fs_deinit(void) {
if (fs_initialized) {
f_unmount("");
}
sc64_sd_deinit();
}
sc64_sd_fs_error_t sc64_sd_fs_load_config(const char *path, sc64_sd_fs_config_t *config) {
FRESULT fresult;
FIL fil;
char config_buffer[256];
fresult = f_open(&fil, path, FA_READ);
if (fresult != FR_OK) {
switch (fresult) {
case FR_DISK_ERR:
case FR_NOT_READY:
error = SC64_SD_FS_READ_ERROR;
break;
return SC64_SD_FS_READ_ERROR;
case FR_NO_FILE:
case FR_NO_PATH:
error = SC64_SD_FS_NO_FILE;
break;
return SC64_SD_FS_NO_FILE;
default:
error = SC64_SD_FS_OTHER_ERROR;
return SC64_SD_FS_OTHER_ERROR;
}
}
while (!f_eof(&fil)) {
char *line = f_gets(config_buffer, sizeof(config_buffer), &fil);
if (line == NULL) {
break;
}
break;
if (strncmp("rom=", line, 4) == 0) {
strncpy(config->rom, line + 4, sizeof(config_buffer) - 4);
} else if (strncmp("rom_reload=", line, 11) == 0) {
config->rom_reload = line[11] != '0';
} else if (strncmp("save=", line, 5) == 0) {
strncpy(config->save, line + 5, sizeof(config_buffer) - 5);
} else if (strncmp("save_type=", line, 10) == 0) {
config->save_type = (uint8_t) strtol(line + 10, NULL, 10);
} else if (strncmp("save_writeback=", line, 15) == 0) {
config->save_writeback = line[15] != '0';
} else if (strncmp("cic_seed=", line, 9) == 0) {
config->cic_seed = (uint16_t) strtoul(line + 9, NULL, 16);
} else if (strncmp("tv_type=", line, 8) == 0) {
config->tv_type = (tv_type_t) strtol(line + 8, NULL, 10);
}
} while (0);
f_unmount("");
sc64_sd_deinit();
return error;
}
fresult = f_close(&fil);
if (fresult != FR_OK) {
return SC64_SD_FS_OTHER_ERROR;
}
return SC64_SD_FS_OK;
}
sc64_sd_fs_error_t sc64_sd_fs_load_rom(const char *path) {
FRESULT fresult;
current_bank = SC64_BANK_SDRAM;
current_offset = 0;
fresult = fe_load(path, SC64_SDRAM_SIZE, sc64_sd_fs_load_with_dma);
if (fresult != FR_OK) {
switch (fresult) {
case FR_DISK_ERR:
case FR_NOT_READY:
return SC64_SD_FS_READ_ERROR;
case FR_NO_FILE:
case FR_NO_PATH:
return SC64_SD_FS_NO_FILE;
default:
return SC64_SD_FS_OTHER_ERROR;
}
}
return SC64_SD_FS_OK;
}
sc64_sd_fs_error_t sc64_sd_fs_load_save(const char *path) {
FRESULT fresult;
uint32_t scr;
size_t length;
scr = sc64_get_scr();
length = 0;
if (scr & SC64_CART_SCR_EEPROM_ENABLE) {
length = (scr & SC64_CART_SCR_EEPROM_16K_MODE) ? 2048 : 512;
current_bank = SC64_BANK_EEPROM;
current_offset = 0;
} else if (scr & SC64_CART_SCR_SRAM_ENABLE) {
length = ((scr & SC64_CART_SCR_SRAM_768K_MODE) ? 3 : 1) * (32 * 1024);
current_bank = SC64_BANK_SDRAM;
current_offset = sc64_get_sram_address();
} else if (scr & SC64_CART_SCR_FLASHRAM_ENABLE) { // TODO: FlashRAM
length = 0;
current_bank = SC64_BANK_FLASHRAM;
current_offset = 0;
}
if ((length == 0) || (path == NULL)) {
return SC64_SD_FS_OK;
}
fresult = fe_load(path, length, sc64_sd_fs_load_with_dma);
if (fresult != FR_OK) {
switch (fresult) {
case FR_DISK_ERR:
case FR_NOT_READY:
return SC64_SD_FS_READ_ERROR;
case FR_NO_FILE:
case FR_NO_PATH:
return SC64_SD_FS_NO_FILE;
default:
return SC64_SD_FS_OTHER_ERROR;
}
}
return SC64_SD_FS_OK;
}
sc64_sd_fs_error_t sc64_sd_fs_store_save(const char *path) {
FRESULT fresult;
FIL fil;
UINT written;
uint32_t scr;
size_t length;
scr = sc64_get_scr();
length = 0;
if (scr & SC64_CART_SCR_EEPROM_ENABLE) {
sc64_enable_eeprom_pi();
length = (scr & SC64_CART_SCR_EEPROM_16K_MODE) ? 2048 : 512;
platform_pi_dma_read(save_buffer, &SC64_EEPROM->MEM, length);
platform_cache_invalidate(save_buffer, length);
sc64_disable_eeprom_pi();
} else if (scr & SC64_CART_SCR_SRAM_ENABLE) {
length = ((scr & SC64_CART_SCR_SRAM_768K_MODE) ? 3 : 1) * (32 * 1024);
platform_pi_dma_read(save_buffer, sc64_get_sram_address(), length);
platform_cache_invalidate(save_buffer, length);
} else if (scr & SC64_CART_SCR_FLASHRAM_ENABLE) { // TODO: FlashRAM
length = 0;
// platform_pi_dma_read(save_buffer, 0, length);
// platform_cache_invalidate(save_buffer, length);
}
if ((length == 0) || (path == NULL)) {
return SC64_SD_FS_OK;
}
fresult = f_open(&fil, path, FA_CREATE_ALWAYS | FA_WRITE);
if (fresult != FR_OK) {
return SC64_SD_FS_WRITE_ERROR;
}
fresult = f_write(&fil, save_buffer, length, &written);
if (fresult != FR_OK) {
return SC64_SD_FS_WRITE_ERROR;
}
fresult = f_close(&fil);
if (fresult != FR_OK) {
return SC64_SD_FS_WRITE_ERROR;
}
return SC64_SD_FS_OK;
}

View File

@ -12,10 +12,27 @@ typedef enum sc64_sd_fs_error_e {
SC64_SD_FS_NO_FILE,
SC64_SD_FS_READ_ERROR,
SC64_SD_FS_OTHER_ERROR,
SC64_SD_FS_WRITE_ERROR,
} sc64_sd_fs_error_t;
typedef struct sc64_sd_fs_config_s {
char *rom;
char *save;
uint8_t save_type;
bool save_writeback;
bool rom_reload;
uint16_t cic_seed;
tv_type_t tv_type;
} sc64_sd_fs_config_t;
sc64_sd_fs_error_t sc64_sd_fs_init(void);
void sc64_sd_fs_deinit(void);
sc64_sd_fs_error_t sc64_sd_fs_load_config(const char *path, sc64_sd_fs_config_t *config);
sc64_sd_fs_error_t sc64_sd_fs_load_rom(const char *path);
sc64_sd_fs_error_t sc64_sd_fs_load_save(const char *path);
sc64_sd_fs_error_t sc64_sd_fs_store_save(const char *path);
#endif