first!
This commit is contained in:
commit
b60edfaef5
|
@ -0,0 +1,14 @@
|
|||
[unstable]
|
||||
build-std = ["core"]
|
||||
build-std-features = ["compiler-builtins-weak-intrinsics"]
|
||||
|
||||
[build]
|
||||
target = "thumbv4t-none-eabi"
|
||||
|
||||
[target.thumbv4t-none-eabi]
|
||||
rustflags = ["-Clink-arg=-Tmono_boot.ld", "-Ctarget-cpu=arm7tdmi"]
|
||||
runner = "mgba-qt"
|
||||
|
||||
[target.armv4t-none-eabi]
|
||||
rustflags = ["-Clink-arg=-Tmono_boot.ld", "-Ctarget-cpu=arm7tdmi"]
|
||||
runner = "mgba-qt"
|
|
@ -0,0 +1 @@
|
|||
target
|
|
@ -0,0 +1,62 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitfrob"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a96c7c818dc8807bb1982dd2cba4c7de0ed6eba4ffb5fc24321d1b38676a120"
|
||||
|
||||
[[package]]
|
||||
name = "bracer"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6559b8c3065745016f5cc2d1095273fe8a175e953c976426947ad828d6ba6fda"
|
||||
|
||||
[[package]]
|
||||
name = "ezfode"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fatfs",
|
||||
"gba",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fatfs"
|
||||
version = "0.4.0"
|
||||
source = "git+https://github.com/rafalh/rust-fatfs#a3a834ef92d94dd227c316c0887654dab00ae085"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gba"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ceded54392ee7a8351fb75e839709185636e09fad53c5bf63b55fb97fff99ba4"
|
||||
dependencies = [
|
||||
"bitfrob",
|
||||
"bracer",
|
||||
"voladdress",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
|
||||
|
||||
[[package]]
|
||||
name = "voladdress"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fbf3fef7bc995d3f8350936bdd07966c7a1f96183d52e3f28c29d099d0b5ecc"
|
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "ezfode"
|
||||
version = "0.1.0"
|
||||
authors = ["ashkitten"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
fatfs = { git = "https://github.com/rafalh/rust-fatfs", default-features = false }
|
||||
gba = "0.11.2"
|
||||
log = "=0.4.19"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 3
|
||||
debug = true
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = "fat"
|
||||
debug = true
|
|
@ -0,0 +1,11 @@
|
|||
Tumbolia Public License
|
||||
|
||||
Copyright ash lea, 2023
|
||||
|
||||
Copying and distribution of this file, with or without modification, are
|
||||
permitted in any medium without royalty provided the copyright notice and this
|
||||
notice are preserved.
|
||||
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. opan saurce LOL
|
|
@ -0,0 +1,4 @@
|
|||
#! /usr/bin/env bash
|
||||
cargo build --release
|
||||
arm-none-eabi-objcopy -O binary target/thumbv4t-none-eabi/release/ezfode target/ezfode.gba
|
||||
~/.cargo/bin/gbafix -p -tezfode -cEZFO -mRS target/ezfode.gba
|
|
@ -0,0 +1,100 @@
|
|||
/* THIS LINKER SCRIPT FILE IS RELEASED TO THE PUBLIC DOMAIN (SPDX: CC0-1.0) */
|
||||
|
||||
ENTRY(__start)
|
||||
|
||||
MEMORY {
|
||||
ewram (w!x) : ORIGIN = 0x2000000, LENGTH = 256K
|
||||
iwram (w!x) : ORIGIN = 0x3000000, LENGTH = 32K
|
||||
rom (rx) : ORIGIN = 0x8000000, LENGTH = 32M
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
.text : {
|
||||
/* be sure that the ROM header is the very first */
|
||||
*(.text.gba_rom_header);
|
||||
*(.text .text.*);
|
||||
. = ALIGN(4);
|
||||
} >rom = 0x00
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*);
|
||||
. = ALIGN(4);
|
||||
} >rom = 0x00
|
||||
|
||||
. = ALIGN(4);
|
||||
__iwram_position_in_rom = .;
|
||||
.data : {
|
||||
__iwram_start = ABSOLUTE(.);
|
||||
|
||||
*(.data .data.*);
|
||||
*(.iwram .iwram.*);
|
||||
. = ALIGN(4);
|
||||
|
||||
__iwram_end = ABSOLUTE(.);
|
||||
} >iwram AT>rom = 0x00
|
||||
|
||||
. = ALIGN(4);
|
||||
__ewram_position_in_rom = __iwram_position_in_rom + (__iwram_end - __iwram_start);
|
||||
.ewram : {
|
||||
__ewram_start = ABSOLUTE(.);
|
||||
|
||||
*(.ewram .ewram.*);
|
||||
. = ALIGN(4);
|
||||
|
||||
__ewram_end = ABSOLUTE(.);
|
||||
} >ewram AT>rom = 0x00
|
||||
|
||||
. = ALIGN(4);
|
||||
__bss_position_in_rom = __ewram_position_in_rom + (__ewram_end - __ewram_start);
|
||||
.bss : {
|
||||
__bss_start = ABSOLUTE(.);
|
||||
|
||||
*(.bss .bss.*);
|
||||
. = ALIGN(4);
|
||||
|
||||
__bss_end = ABSOLUTE(.);
|
||||
} >iwram
|
||||
|
||||
__iwram_word_copy_count = (__iwram_end - __iwram_start) / 4;
|
||||
__ewram_word_copy_count = (__ewram_end - __ewram_start) / 4;
|
||||
__bss_word_clear_count = (__bss_end - __bss_start) / 4;
|
||||
|
||||
/* rust-lld demands we keep the `section header string table` */
|
||||
.shstrtab 0 : { *(.shstrtab) }
|
||||
|
||||
/* debugging sections */
|
||||
/* Stabs */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
|
||||
/* discard anything not already mentioned */
|
||||
/DISCARD/ : { *(*) }
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "nightly"
|
||||
components = ["rust-src", "clippy", "rustfmt"]
|
|
@ -0,0 +1,9 @@
|
|||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
with pkgs; stdenv.mkDerivation {
|
||||
name = "env";
|
||||
nativeBuildInputs = [
|
||||
rustup
|
||||
gcc-arm-embedded
|
||||
];
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
use core::ffi::c_void;
|
||||
|
||||
use gba::prelude::{DmaControl, DMA3_CONTROL, DMA3_COUNT, DMA3_DEST, DMA3_SRC};
|
||||
|
||||
/// this is supposed to mimic dmaCopy from libgba
|
||||
/// size is a u32 because it needs to represent a 17-bit count (of u8)
|
||||
/// DMA3_COUNT takes a count of u16
|
||||
#[inline(always)]
|
||||
pub unsafe fn dma_copy(src: *mut c_void, dst: *mut c_void, size: u32) {
|
||||
DMA3_SRC.write(src);
|
||||
DMA3_DEST.write(dst);
|
||||
DMA3_COUNT.write((size >> 1) as u16);
|
||||
DMA3_CONTROL.write(DmaControl::new().with_enabled(true));
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#[link_section = ".iwram"]
|
||||
pub unsafe fn set_rompage(page: u16) {
|
||||
(0x9fe0000 as *mut u16).write_volatile(0xd200);
|
||||
(0x8000000 as *mut u16).write_volatile(0x1500);
|
||||
(0x8020000 as *mut u16).write_volatile(0xd200);
|
||||
(0x8040000 as *mut u16).write_volatile(0x1500);
|
||||
(0x9880000 as *mut u16).write_volatile(page); //C4
|
||||
(0x9fc0000 as *mut u16).write_volatile(0x1500);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
#![feature(int_roundings)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use fatfs::{FileSystem, FsOptions};
|
||||
use gba::prelude::*;
|
||||
use sd::SDCard;
|
||||
|
||||
mod dma;
|
||||
mod ezflash;
|
||||
mod sd;
|
||||
|
||||
#[panic_handler]
|
||||
#[link_section = ".iwram"]
|
||||
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
|
||||
BACKDROP_COLOR.write(Color::RED);
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn main() -> ! {
|
||||
DISPCNT.write(DisplayControl::new().with_show_bg0(true));
|
||||
BACKDROP_COLOR.write(Color::YELLOW);
|
||||
|
||||
let sd = SDCard::new();
|
||||
let fs = FileSystem::new(sd, FsOptions::new()).unwrap();
|
||||
|
||||
BACKDROP_COLOR.write(Color::GREEN);
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub fn delay(count: u32) {
|
||||
let mut i = count;
|
||||
let i = &mut i as *mut u32;
|
||||
unsafe {
|
||||
while i.read_volatile() > 0 {
|
||||
i.write_volatile(i.read_volatile() - 1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
use core::ffi::c_void;
|
||||
|
||||
use fatfs::{IoBase, Read, Seek, SeekFrom, Write};
|
||||
|
||||
use crate::delay;
|
||||
use crate::dma::dma_copy;
|
||||
use crate::ezflash::set_rompage;
|
||||
|
||||
#[repr(u16)]
|
||||
pub enum SdControl {
|
||||
Disable = 0,
|
||||
Enable = 1,
|
||||
ReadState = 3,
|
||||
}
|
||||
|
||||
#[link_section = ".iwram"]
|
||||
pub unsafe fn set_sd_control(control: SdControl) {
|
||||
(0x9fe0000 as *mut u16).write_volatile(0xd200);
|
||||
(0x8000000 as *mut u16).write_volatile(0x1500);
|
||||
(0x8020000 as *mut u16).write_volatile(0xd200);
|
||||
(0x8040000 as *mut u16).write_volatile(0x1500);
|
||||
(0x9400000 as *mut u16).write_volatile(control as u16);
|
||||
(0x9fc0000 as *mut u16).write_volatile(0x1500);
|
||||
}
|
||||
|
||||
#[link_section = ".iwram"]
|
||||
pub unsafe fn sd_enable() {
|
||||
set_sd_control(SdControl::Enable);
|
||||
}
|
||||
|
||||
#[link_section = ".iwram"]
|
||||
pub unsafe fn sd_disable() {
|
||||
set_sd_control(SdControl::Disable);
|
||||
}
|
||||
|
||||
#[link_section = ".iwram"]
|
||||
pub unsafe fn sd_read_state() {
|
||||
set_sd_control(SdControl::ReadState);
|
||||
}
|
||||
|
||||
#[link_section = ".iwram"]
|
||||
pub unsafe fn sd_response() -> u16 {
|
||||
(0x9e00000 as *mut u16).read_volatile()
|
||||
}
|
||||
|
||||
#[link_section = ".iwram"]
|
||||
pub unsafe fn wait_sd_response() -> u32 {
|
||||
for _ in 0..0x100000 {
|
||||
if sd_response() != 0xeee1 {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// timeout!
|
||||
return 1;
|
||||
}
|
||||
|
||||
#[link_section = ".iwram"]
|
||||
pub unsafe fn read_sd_sectors(address: u32, count: u16, buffer: &mut [u8]) {
|
||||
sd_enable();
|
||||
|
||||
let mut blocks;
|
||||
let mut res;
|
||||
let mut times = 2;
|
||||
|
||||
for i in (0..count).step_by(4) {
|
||||
// read at most 4 blocks at a time
|
||||
blocks = if count - i > 4 { 4 } else { count - i };
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
(0x9fe0000 as *mut u16).write_volatile(0xd200);
|
||||
(0x8000000 as *mut u16).write_volatile(0x1500);
|
||||
(0x8020000 as *mut u16).write_volatile(0xd200);
|
||||
(0x8040000 as *mut u16).write_volatile(0x1500);
|
||||
(0x9600000 as *mut u16).write_volatile(((address + i as u32) & 0x0000FFFF) as u16);
|
||||
(0x9620000 as *mut u16)
|
||||
.write_volatile((((address + i as u32) & 0xFFFF0000) >> 16) as u16);
|
||||
(0x9640000 as *mut u16).write_volatile(blocks);
|
||||
(0x9fc0000 as *mut u16).write_volatile(0x1500);
|
||||
}
|
||||
|
||||
sd_read_state();
|
||||
res = wait_sd_response();
|
||||
sd_enable();
|
||||
|
||||
if res == 1 {
|
||||
times -= 1;
|
||||
if times > 0 {
|
||||
delay(5000);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let src = 0x9e00000 as *mut c_void;
|
||||
let dst = (buffer as *mut [u8] as *mut c_void).add(i as usize * 512);
|
||||
dma_copy(src, dst, blocks as u32 * 512);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sd_disable();
|
||||
}
|
||||
|
||||
pub struct SDCard {
|
||||
/// current stream position
|
||||
pos: u32,
|
||||
/// track the current page in the buffer
|
||||
page: Option<u16>,
|
||||
/// buffer containing the current page
|
||||
/// we can read at most four 512-byte blocks at a time
|
||||
buf: [u8; 2048],
|
||||
}
|
||||
|
||||
impl SDCard {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
pos: 0,
|
||||
page: None,
|
||||
buf: [0; 2048],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IoBase for SDCard {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl Read for SDCard {
|
||||
#[link_section = ".iwram"]
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()> {
|
||||
if !self.page.is_some_and(|p| p == (self.pos >> 11) as u16) {
|
||||
self.page = Some((self.pos >> 11) as u16);
|
||||
let page_start = self.pos ^ (u32::MAX >> 11);
|
||||
unsafe {
|
||||
set_rompage(0x8000); // OS mode
|
||||
read_sd_sectors(page_start, 4, &mut self.buf);
|
||||
set_rompage(0x200); // game mode
|
||||
}
|
||||
}
|
||||
|
||||
// offset inside page
|
||||
let offset = (self.pos & (u32::MAX >> 11)) as usize;
|
||||
// end of the read
|
||||
let end = self.buf.len().min(offset + buf.len());
|
||||
|
||||
let slice = &self.buf[offset..end];
|
||||
buf.copy_from_slice(slice);
|
||||
|
||||
let len = end - offset;
|
||||
self.pos += len as u32;
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for SDCard {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for SDCard {
|
||||
fn seek(&mut self, pos: SeekFrom) -> Result<u64, ()> {
|
||||
match pos {
|
||||
SeekFrom::Start(addr) => {
|
||||
self.pos = addr as u32;
|
||||
}
|
||||
SeekFrom::End(addr) => {
|
||||
self.pos = u32::MAX - addr as u32;
|
||||
}
|
||||
SeekFrom::Current(addr) => {
|
||||
self.pos = self.pos + addr as u32;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.pos as u64)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue