This commit is contained in:
ash lea 2023-09-14 21:21:39 -04:00
commit b60edfaef5
15 changed files with 474 additions and 0 deletions

14
.cargo/config.toml Normal file
View File

@ -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"

1
.envrc Normal file
View File

@ -0,0 +1 @@
use nix

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
target

62
Cargo.lock generated Normal file
View File

@ -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"

19
Cargo.toml Normal file
View File

@ -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

11
LICENSE.txt Normal file
View File

@ -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

1
README.md Normal file
View File

@ -0,0 +1 @@
lol

4
makerom.sh Executable file
View File

@ -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

100
mono_boot.ld Normal file
View File

@ -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/ : { *(*) }
}

3
rust-toolchain.toml Normal file
View File

@ -0,0 +1,3 @@
[toolchain]
channel = "nightly"
components = ["rust-src", "clippy", "rustfmt"]

9
shell.nix Normal file
View File

@ -0,0 +1,9 @@
{ pkgs ? import <nixpkgs> {} }:
with pkgs; stdenv.mkDerivation {
name = "env";
nativeBuildInputs = [
rustup
gcc-arm-embedded
];
}

14
src/dma.rs Normal file
View File

@ -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));
}

9
src/ezflash.rs Normal file
View File

@ -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);
}

41
src/main.rs Normal file
View File

@ -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);
}
}
}

185
src/sd.rs Normal file
View File

@ -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)
}
}