mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-25 15:16:53 +01:00
SD constrained i think? And PI bug fixes
This commit is contained in:
parent
982e230958
commit
a0fed3e8fa
@ -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)
|
||||
# ---------------------------
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 = {
|
||||
|
@ -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;
|
||||
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
|
||||
|
||||
localparam [31:0] CART_BASE = 32'h1E00_0000;
|
||||
localparam [31:0] CART_END = 32'h1E00_3FFF;
|
||||
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
|
||||
|
||||
localparam [31:0] EEPROM_BASE = 32'h1E00_4000;
|
||||
localparam [31:0] EEPROM_END = 32'h1E00_47FF;
|
||||
16'b000100??????????: begin // ROM
|
||||
o_bank <= `BANK_SDRAM;
|
||||
end
|
||||
|
||||
localparam [31:0] SD_BASE = 32'h1E00_8000;
|
||||
localparam [31:0] SD_END = 32'h1E00_83FF;
|
||||
16'b0001111000000000: begin // CART
|
||||
o_bank <= `BANK_CART;
|
||||
o_prefetch <= 1'b0;
|
||||
end
|
||||
|
||||
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};
|
||||
16'b0001111000000001: begin // EEPROM
|
||||
if (i_eeprom_enable) begin
|
||||
o_bank <= `BANK_EEPROM;
|
||||
end
|
||||
end
|
||||
|
||||
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;
|
||||
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 >= 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;
|
||||
end
|
||||
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
|
||||
|
||||
if ((i_address >= ROM_BASE) && (i_address <= ROM_END)) begin
|
||||
o_bank = `BANK_SDRAM;
|
||||
o_bank_prefetch = 1'b1;
|
||||
end
|
||||
`BANK_CART: begin
|
||||
if (i_n64_pi_ad[15:14] != 2'b00) begin
|
||||
o_bank <= `BANK_INVALID;
|
||||
end
|
||||
end
|
||||
|
||||
if ((i_address >= CART_BASE) && (i_address <= CART_END)) begin
|
||||
o_bank = `BANK_CART;
|
||||
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
|
||||
|
||||
if ((i_address >= EEPROM_BASE) && (i_address <= EEPROM_END) && i_eeprom_enable) begin
|
||||
o_bank = `BANK_EEPROM;
|
||||
o_bank_prefetch = 1'b1;
|
||||
end
|
||||
`BANK_SD: begin
|
||||
if (i_n64_pi_ad[15:10] != 6'b000000) begin
|
||||
o_bank <= `BANK_INVALID;
|
||||
end
|
||||
end
|
||||
|
||||
if ((i_address >= SD_BASE) && (i_address <= SD_END) && i_sd_enable) begin
|
||||
o_bank = `BANK_SD;
|
||||
default: begin end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -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,15 +188,16 @@ 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
|
||||
o_request <= 1'b0;
|
||||
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
|
||||
|
14
fw/rtl/top.v
14
fw/rtl/top.v
@ -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),
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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]);
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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,18 +31,21 @@ 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) {
|
||||
return RES_ERROR;
|
||||
switch (error) {
|
||||
case E_NO_INIT:
|
||||
return RES_NOTRDY;
|
||||
case E_PAR_ERROR:
|
||||
return RES_PARERR;
|
||||
default:
|
||||
return RES_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return RES_OK;
|
||||
@ -51,7 +54,26 @@ 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) {
|
||||
return RES_ERROR;
|
||||
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
|
||||
|
@ -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.
|
||||
|
@ -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,32 +60,71 @@ 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);
|
||||
audio_write_silence();
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -25,15 +25,23 @@ 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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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,22 +177,24 @@ 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_CRC_ERROR) {
|
||||
return E_CRC_ERROR;
|
||||
}
|
||||
if (!(reg & SC64_SD_DAT_BUSY)) {
|
||||
if (reg & SC64_SD_DAT_CRC_ERROR) {
|
||||
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);
|
||||
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;
|
||||
return E_FIFO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout == 0) {
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
if (sc64_sd_read_sectors_dma(sector, count, SC64_BANK_SDRAM, offset) != E_OK) {
|
||||
return RES_ERROR;
|
||||
error = sc64_sd_sectors_read_dma(sector, count, current_bank, current_offset + offset);
|
||||
|
||||
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:
|
||||
case FR_NOT_READY:
|
||||
error = SC64_SD_FS_NO_CARD;
|
||||
break;
|
||||
case FR_NO_FILESYSTEM:
|
||||
error = SC64_SD_FS_NO_FILESYSTEM;
|
||||
break;
|
||||
default:
|
||||
error = SC64_SD_FS_OTHER_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
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:
|
||||
return SC64_SD_FS_NO_CARD;
|
||||
case FR_NO_FILESYSTEM:
|
||||
return SC64_SD_FS_NO_FILESYSTEM;
|
||||
default:
|
||||
return SC64_SD_FS_OTHER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
fresult = fe_load(path, SC64_SDRAM_SIZE, sc64_sd_fs_load_rom_with_dma);
|
||||
if (fresult) {
|
||||
switch (fresult) {
|
||||
case FR_DISK_ERR:
|
||||
case FR_NOT_READY:
|
||||
error = SC64_SD_FS_READ_ERROR;
|
||||
break;
|
||||
case FR_NO_FILE:
|
||||
case FR_NO_PATH:
|
||||
error = SC64_SD_FS_NO_FILE;
|
||||
break;
|
||||
default:
|
||||
error = SC64_SD_FS_OTHER_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (0);
|
||||
fs_initialized = true;
|
||||
|
||||
f_unmount("");
|
||||
return SC64_SD_FS_OK;
|
||||
}
|
||||
|
||||
void sc64_sd_fs_deinit(void) {
|
||||
if (fs_initialized) {
|
||||
f_unmount("");
|
||||
}
|
||||
|
||||
sc64_sd_deinit();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
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:
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
while (!f_eof(&fil)) {
|
||||
char *line = f_gets(config_buffer, sizeof(config_buffer), &fil);
|
||||
|
||||
if (line == NULL) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user