add more bugs to find later
This commit is contained in:
parent
b7acc63225
commit
f9192b1d32
|
@ -24,6 +24,12 @@ dependencies = [
|
|||
"num_enum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -98,7 +104,9 @@ dependencies = [
|
|||
"embedded-io",
|
||||
"gba",
|
||||
"gpt-parser",
|
||||
"itoa",
|
||||
"log",
|
||||
"ucs2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -122,6 +130,12 @@ dependencies = [
|
|||
"explicit-endian",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.19"
|
||||
|
@ -177,6 +191,15 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ucs2"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bad643914094137d475641b6bab89462505316ec2ce70907ad20102d28a79ab8"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
|
|
|
@ -5,12 +5,14 @@ authors = ["ashkitten"]
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
ape-fatfs = { version = "0.2.0", default-features = false }
|
||||
ape-fatfs = { version = "0.2.0", default-features = false, features = ["unicode", "lfn"] }
|
||||
ape-mbr = "0.1.1"
|
||||
embedded-io = "0.4.0"
|
||||
gba = "0.11.2"
|
||||
gpt-parser = { version = "0.0.9", features = ["no_std"] }
|
||||
itoa = "1.0.9"
|
||||
log = "=0.4.19"
|
||||
ucs2 = "0.3.2"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 3
|
||||
|
|
|
@ -25,7 +25,6 @@ SECTIONS {
|
|||
.data : {
|
||||
__iwram_start = ABSOLUTE(.);
|
||||
|
||||
*(.text .text.*);
|
||||
*(.data .data.*);
|
||||
*(.iwram .iwram.*);
|
||||
. = ALIGN(4);
|
||||
|
@ -38,6 +37,7 @@ SECTIONS {
|
|||
.ewram : {
|
||||
__ewram_start = ABSOLUTE(.);
|
||||
|
||||
*(.text .text.*);
|
||||
*(.ewram .ewram.*);
|
||||
. = ALIGN(4);
|
||||
|
||||
|
|
|
@ -6,3 +6,12 @@ pub unsafe fn set_rompage(page: u16) {
|
|||
(0x9880000 as *mut u16).write_volatile(page); //C4
|
||||
(0x9fc0000 as *mut u16).write_volatile(0x1500);
|
||||
}
|
||||
|
||||
pub unsafe fn set_led_control(status: 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);
|
||||
(0x96E0000 as *mut u16).write_volatile(status);
|
||||
(0x9fc0000 as *mut u16).write_volatile(0x1500);
|
||||
}
|
||||
|
|
17
src/fs.rs
17
src/fs.rs
|
@ -22,7 +22,8 @@ impl<E> From<ReadExactError<E>> for ErrorKind {
|
|||
}
|
||||
|
||||
/// BS: Block Size, PS: Page Size
|
||||
// optimally PS would be in terms of BS, but const generics don't allow that yet
|
||||
/// optimally PS would be in terms of BS, but const generics don't allow that yet
|
||||
/// BS and PS must be powers of two, and PS must be as large or larger than BS
|
||||
pub struct BufferedIo<const BS: usize, const PS: usize, IO: BlockIo<BS>> {
|
||||
io: IO,
|
||||
/// current stream position
|
||||
|
@ -39,6 +40,11 @@ impl<const BS: usize, const PS: usize, IO: BlockIo<BS>> BufferedIo<BS, PS, IO> {
|
|||
page: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn lba(&self) -> Lba {
|
||||
// floor to PS, but in terms of BS
|
||||
(self.pos / PS * PS / BS) as Lba
|
||||
}
|
||||
}
|
||||
|
||||
impl<const BS: usize, const PS: usize, IO: BlockIo<BS>> Io for BufferedIo<BS, PS, IO> {
|
||||
|
@ -50,9 +56,9 @@ impl<const BS: usize, const PS: usize, IO: BlockIo<BS>> Read for BufferedIo<BS,
|
|||
// ensure the page at self.pos is loaded
|
||||
let (lba, page) = self
|
||||
.page
|
||||
.filter(|(lba, _)| *lba as usize == self.pos / BS)
|
||||
.filter(|(lba, _)| *lba == self.lba())
|
||||
.unwrap_or_else(|| {
|
||||
let lba = (self.pos / BS) as Lba;
|
||||
let lba = self.lba();
|
||||
let mut buf = [0; PS];
|
||||
self.io.read_blocks(lba, &mut buf).unwrap();
|
||||
(lba, buf)
|
||||
|
@ -73,11 +79,12 @@ impl<const BS: usize, const PS: usize, IO: BlockIo<BS>> Read for BufferedIo<BS,
|
|||
|
||||
impl<const BS: usize, const PS: usize, IO: BlockIo<BS>> Write for BufferedIo<BS, PS, IO> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
todo!()
|
||||
self.pos += buf.len();
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
129
src/main.rs
129
src/main.rs
|
@ -3,10 +3,14 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::str::from_utf8_unchecked;
|
||||
|
||||
use ape_fatfs::fs::{FileSystem, FsOptions};
|
||||
use ape_mbr::{PartitionId, MBR};
|
||||
use ezflash::set_led_control;
|
||||
use fs::BufferedIo;
|
||||
use gba::prelude::*;
|
||||
use log::Log;
|
||||
use sd::SdCard;
|
||||
|
||||
mod dma;
|
||||
|
@ -15,21 +19,144 @@ mod fs;
|
|||
mod sd;
|
||||
|
||||
#[panic_handler]
|
||||
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
|
||||
fn panic_handler(info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
// red+green
|
||||
set_led_control(0b10100000);
|
||||
}
|
||||
BACKDROP_COLOR.write(Color::RED);
|
||||
|
||||
let mut itoa = itoa::Buffer::new();
|
||||
|
||||
let mut pos = 0;
|
||||
let mut bytes = [0u8; 512];
|
||||
let mut write = |str: &str| {
|
||||
// truncate silently
|
||||
let end = bytes.len().min(pos + str.len());
|
||||
let len = end - pos;
|
||||
bytes[pos..end].copy_from_slice(&str.as_bytes()[..len]);
|
||||
pos = end;
|
||||
};
|
||||
|
||||
write("panic at ");
|
||||
if let Some(location) = info.location() {
|
||||
write(location.file().rsplit_terminator('/').next().unwrap());
|
||||
write(" line ");
|
||||
write(itoa.format(location.line()));
|
||||
} else {
|
||||
write("unknown location");
|
||||
};
|
||||
|
||||
draw_text(unsafe { core::str::from_utf8_unchecked(&bytes) });
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
// static LOGGER: ScreenLogger = ScreenLogger::new();
|
||||
|
||||
// struct ScreenLogger {
|
||||
// buf: GbaCell<[u8; 1024]>,
|
||||
// len: GbaCell<u16>,
|
||||
// pos: GbaCell<u16>,
|
||||
// }
|
||||
|
||||
// impl ScreenLogger {
|
||||
// fn new() -> Self {
|
||||
// Self {
|
||||
// buf: GbaCell::new([0; 1024]),
|
||||
// len: GbaCell::new(0),
|
||||
// pos: GbaCell::new(0),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl Log for ScreenLogger {
|
||||
// fn enabled(&self, metadata: &log::Metadata) -> bool {
|
||||
// true
|
||||
// }
|
||||
|
||||
// fn log(&self, record: &log::Record) {}
|
||||
|
||||
// fn flush(&self) {
|
||||
// todo!()
|
||||
// }
|
||||
// }
|
||||
|
||||
extern "C" fn irq_handler(_: IrqBits) {}
|
||||
|
||||
fn draw_text(text: &str) {
|
||||
Cga8x8Thick.bitunpack_4bpp(CHARBLOCK0_4BPP.as_region(), 0);
|
||||
BG0CNT.write(BackgroundControl::new().with_screenblock(8));
|
||||
|
||||
let screenblock = TEXT_SCREENBLOCKS.get_frame(8).unwrap();
|
||||
for x in 0..32 {
|
||||
for y in 0..32 {
|
||||
screenblock
|
||||
.get(x, y)
|
||||
.unwrap()
|
||||
.write(TextEntry::new().with_tile(0));
|
||||
}
|
||||
}
|
||||
|
||||
for (y, line) in text.split_terminator("\n").take(32).enumerate() {
|
||||
let row = screenblock.get_row(y).unwrap();
|
||||
for (x, byte) in line.bytes().take(32).enumerate() {
|
||||
let text_entry = TextEntry::new().with_tile(byte as u16);
|
||||
row.get(x).unwrap().write(text_entry);
|
||||
}
|
||||
}
|
||||
|
||||
DISPCNT.write(DisplayControl::new().with_show_bg0(true));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn main() -> ! {
|
||||
RUST_IRQ_HANDLER.write(Some(irq_handler));
|
||||
DISPSTAT.write(DisplayStatus::new().with_irq_vblank(true));
|
||||
IE.write(IrqBits::VBLANK);
|
||||
IME.write(true);
|
||||
|
||||
DISPCNT.write(DisplayControl::new().with_show_bg0(true));
|
||||
BACKDROP_COLOR.write(Color::YELLOW);
|
||||
unsafe {
|
||||
// red+green + blue sd indicator
|
||||
set_led_control(0b10110001);
|
||||
}
|
||||
|
||||
draw_text("hello world!");
|
||||
|
||||
let mut mbr = MBR::new(BufferedIo::<512, 2048, _>::new(SdCard)).unwrap();
|
||||
let partition = mbr.get_partition(PartitionId::One).unwrap();
|
||||
let fs = FileSystem::new(partition, FsOptions::new()).unwrap();
|
||||
|
||||
BACKDROP_COLOR.write(Color::GREEN);
|
||||
unsafe {
|
||||
// green + blue sd indicator
|
||||
set_led_control(0b10010001);
|
||||
}
|
||||
|
||||
{
|
||||
let mut pos = 0;
|
||||
let mut bytes = [0u8; 512];
|
||||
for entry in fs.root_dir().iter() {
|
||||
let entry = entry.unwrap();
|
||||
if let Some(name) = entry.long_file_name_as_ucs2_units() {
|
||||
pos += ucs2::decode(name, &mut bytes[pos..]).unwrap()
|
||||
} else {
|
||||
let name = entry.short_file_name_as_bytes();
|
||||
bytes[pos..pos + name.len()].copy_from_slice(&name);
|
||||
pos += name.len();
|
||||
}
|
||||
if entry.is_dir() {
|
||||
bytes[pos..pos + 1].copy_from_slice("/".as_bytes());
|
||||
pos += 1;
|
||||
}
|
||||
bytes[pos..pos + 1].copy_from_slice("\n".as_bytes());
|
||||
pos += 1;
|
||||
}
|
||||
|
||||
draw_text(unsafe { from_utf8_unchecked(&bytes) });
|
||||
}
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
|
17
src/sd.rs
17
src/sd.rs
|
@ -1,6 +1,9 @@
|
|||
use core::ffi::c_void;
|
||||
use core::fmt;
|
||||
|
||||
use gba::prelude::BACKDROP_COLOR;
|
||||
use gba::video::Color;
|
||||
|
||||
use crate::delay;
|
||||
use crate::dma::dma_copy;
|
||||
use crate::ezflash::set_rompage;
|
||||
|
@ -12,6 +15,7 @@ pub enum SdControl {
|
|||
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);
|
||||
|
@ -21,22 +25,27 @@ pub unsafe fn set_sd_control(control: SdControl) {
|
|||
(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() -> Result<(), ()> {
|
||||
for _ in 0..0x100000 {
|
||||
if sd_response() != 0xeee1 {
|
||||
|
@ -77,6 +86,7 @@ impl SdCard {
|
|||
impl BlockIo<512> for SdCard {
|
||||
type Error = BlockIoError;
|
||||
|
||||
#[link_section = ".iwram"]
|
||||
fn read_blocks(&mut self, start_lba: Lba, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
unsafe {
|
||||
set_rompage(0x8000); // OS mode
|
||||
|
@ -94,6 +104,8 @@ impl BlockIo<512> for SdCard {
|
|||
|
||||
// try three times to read
|
||||
for _ in 0..2 {
|
||||
sd_enable();
|
||||
|
||||
(0x9fe0000 as *mut u16).write_volatile(0xd200);
|
||||
(0x8000000 as *mut u16).write_volatile(0x1500);
|
||||
(0x8020000 as *mut u16).write_volatile(0xd200);
|
||||
|
@ -105,6 +117,7 @@ impl BlockIo<512> for SdCard {
|
|||
|
||||
sd_read_state();
|
||||
if wait_sd_response().is_ok() {
|
||||
sd_enable();
|
||||
// successful read!
|
||||
let src = 0x9e00000 as *mut c_void;
|
||||
let dst = &mut buffer[i as usize * 512] as *mut u8 as *mut c_void;
|
||||
|
@ -114,13 +127,13 @@ impl BlockIo<512> for SdCard {
|
|||
continue 'chunks;
|
||||
} else {
|
||||
// read timed out, try again
|
||||
sd_enable();
|
||||
delay(5000);
|
||||
}
|
||||
}
|
||||
|
||||
// oh no! we couldn't read!
|
||||
panic!();
|
||||
BACKDROP_COLOR.write(Color::BLUE);
|
||||
loop {}
|
||||
}
|
||||
|
||||
sd_disable();
|
||||
|
|
Loading…
Reference in New Issue