Skip to content

Commit fefaff4

Browse files
authored
Merge pull request #36 from Neotron-Compute/lets-do-dir
Add a DIR command.
2 parents 31625ce + a7fe432 commit fefaff4

File tree

5 files changed

+212
-65
lines changed

5 files changed

+212
-65
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ bench = false
2525

2626
[lib]
2727
crate-type = ["rlib", "cdylib"]
28+
required-features = ["native-log"]
2829

2930
[profile.release]
3031
lto = true
@@ -44,3 +45,4 @@ postcard = "1.0"
4445
serde = { version = "1.0", default-features = false }
4546
menu = "0.3"
4647
chrono = { version = "0.4", default-features = false }
48+
embedded-sdmmc = { version = "0.5", default-features = false }

src/commands/block.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub static READ_ITEM: menu::Item<Ctx> = menu::Item {
2626
],
2727
},
2828
command: "readblk",
29-
help: Some("List all the Block Devices"),
29+
help: Some("Display one disk block, as hex"),
3030
};
3131

3232
/// Called when the "lsblk" command is executed.
@@ -96,15 +96,10 @@ fn read_block(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], _
9696
bios::Result::Ok(_) => {
9797
// Carry on
9898
let mut count = 0;
99-
for chunk in buffer.chunks(16) {
99+
for chunk in buffer.chunks(32) {
100100
print!("{:03x}: ", count);
101101
for b in chunk {
102-
print!("{:02x} ", *b);
103-
}
104-
print!(" ");
105-
for b in chunk {
106-
let c = char::from(*b);
107-
print!("{}", if c.is_ascii_graphic() { c } else { '.' });
102+
print!("{:02x}", *b);
108103
}
109104
count += chunk.len();
110105
println!();

src/commands/fs.rs

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
//! File Systems related commands for Neotron OS
2+
3+
use chrono::{Datelike, Timelike};
4+
use embedded_sdmmc::VolumeIdx;
5+
6+
use crate::{bios, print, println, Ctx, API};
7+
8+
pub static DIR_ITEM: menu::Item<Ctx> = menu::Item {
9+
item_type: menu::ItemType::Callback {
10+
function: dir,
11+
parameters: &[],
12+
},
13+
command: "dir",
14+
help: Some("Dir the root directory on block device 0"),
15+
};
16+
17+
#[cfg(target_os = "none")]
18+
pub static LOAD_ITEM: menu::Item<Ctx> = menu::Item {
19+
item_type: menu::ItemType::Callback {
20+
function: load,
21+
parameters: &[menu::Parameter::Mandatory {
22+
parameter_name: "file",
23+
help: Some("The file to load"),
24+
}],
25+
},
26+
command: "load",
27+
help: Some("Load a file into the application area"),
28+
};
29+
30+
struct BiosBlock();
31+
32+
impl embedded_sdmmc::BlockDevice for BiosBlock {
33+
type Error = bios::Error;
34+
35+
fn read(
36+
&self,
37+
blocks: &mut [embedded_sdmmc::Block],
38+
start_block_idx: embedded_sdmmc::BlockIdx,
39+
_reason: &str,
40+
) -> Result<(), Self::Error> {
41+
let api = API.get();
42+
let byte_slice = unsafe {
43+
core::slice::from_raw_parts_mut(
44+
blocks.as_mut_ptr() as *mut u8,
45+
blocks.len() * embedded_sdmmc::Block::LEN,
46+
)
47+
};
48+
match (api.block_read)(
49+
0,
50+
bios::block_dev::BlockIdx(u64::from(start_block_idx.0)),
51+
blocks.len() as u8,
52+
bios::ApiBuffer::new(byte_slice),
53+
) {
54+
bios::Result::Ok(_) => Ok(()),
55+
bios::Result::Err(e) => Err(e),
56+
}
57+
}
58+
59+
fn write(
60+
&self,
61+
blocks: &[embedded_sdmmc::Block],
62+
start_block_idx: embedded_sdmmc::BlockIdx,
63+
) -> Result<(), Self::Error> {
64+
let api = API.get();
65+
let byte_slice = unsafe {
66+
core::slice::from_raw_parts(
67+
blocks.as_ptr() as *const u8,
68+
blocks.len() * embedded_sdmmc::Block::LEN,
69+
)
70+
};
71+
match (api.block_write)(
72+
0,
73+
bios::block_dev::BlockIdx(u64::from(start_block_idx.0)),
74+
blocks.len() as u8,
75+
bios::ApiByteSlice::new(byte_slice),
76+
) {
77+
bios::Result::Ok(_) => Ok(()),
78+
bios::Result::Err(e) => Err(e),
79+
}
80+
}
81+
82+
fn num_blocks(&self) -> Result<embedded_sdmmc::BlockCount, Self::Error> {
83+
let api = API.get();
84+
match (api.block_dev_get_info)(0) {
85+
bios::Option::Some(info) => Ok(embedded_sdmmc::BlockCount(info.num_blocks as u32)),
86+
bios::Option::None => Err(bios::Error::InvalidDevice),
87+
}
88+
}
89+
}
90+
91+
struct BiosTime();
92+
93+
impl embedded_sdmmc::TimeSource for BiosTime {
94+
fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
95+
let time = API.get_time();
96+
embedded_sdmmc::Timestamp {
97+
year_since_1970: (time.year() - 1970) as u8,
98+
zero_indexed_month: time.month0() as u8,
99+
zero_indexed_day: time.day0() as u8,
100+
hours: time.hour() as u8,
101+
minutes: time.minute() as u8,
102+
seconds: time.second() as u8,
103+
}
104+
}
105+
}
106+
107+
/// Called when the "dir" command is executed.
108+
fn dir(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ctx: &mut Ctx) {
109+
fn work() -> Result<(), embedded_sdmmc::Error<bios::Error>> {
110+
println!("Listing files on Block Device 0, /");
111+
let bios_block = BiosBlock();
112+
let time = BiosTime();
113+
let mut mgr = embedded_sdmmc::VolumeManager::new(bios_block, time);
114+
// Open the first partition
115+
let volume = mgr.get_volume(VolumeIdx(0))?;
116+
let root_dir = mgr.open_root_dir(&volume)?;
117+
let mut total_bytes = 0u64;
118+
let mut num_files = 0;
119+
mgr.iterate_dir(&volume, &root_dir, |dir_entry| {
120+
let padding = 8 - dir_entry.name.base_name().len();
121+
for b in dir_entry.name.base_name() {
122+
let ch = *b as char;
123+
print!("{}", if ch.is_ascii_graphic() { ch } else { '?' });
124+
}
125+
for _ in 0..padding {
126+
print!(" ");
127+
}
128+
print!(" ");
129+
let padding = 3 - dir_entry.name.extension().len();
130+
for b in dir_entry.name.extension() {
131+
let ch = *b as char;
132+
print!("{}", if ch.is_ascii_graphic() { ch } else { '?' });
133+
}
134+
for _ in 0..padding {
135+
print!(" ");
136+
}
137+
if dir_entry.attributes.is_directory() {
138+
print!(" <DIR> ");
139+
} else {
140+
print!(" {:-13}", dir_entry.size,);
141+
}
142+
print!(
143+
" {:02}/{:02}/{:04}",
144+
dir_entry.mtime.zero_indexed_day + 1,
145+
dir_entry.mtime.zero_indexed_month + 1,
146+
u32::from(dir_entry.mtime.year_since_1970) + 1970
147+
);
148+
println!(
149+
" {:02}:{:02}",
150+
dir_entry.mtime.hours, dir_entry.mtime.minutes
151+
);
152+
total_bytes += dir_entry.size as u64;
153+
num_files += 1;
154+
})?;
155+
println!("{:-9} file(s) {:-13} bytes", num_files, total_bytes);
156+
Ok(())
157+
}
158+
159+
match work() {
160+
Ok(_) => {}
161+
Err(e) => {
162+
println!("Error: {:?}", e);
163+
}
164+
}
165+
}
166+
167+
/// Called when the "load" command is executed.
168+
#[cfg(target_os = "none")]
169+
fn load(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], _ctx: &mut Ctx) {
170+
fn work(args: &[&str]) -> Result<(), embedded_sdmmc::Error<bios::Error>> {
171+
println!("Loading /{} from Block Device 0", args[0]);
172+
let bios_block = BiosBlock();
173+
let time = BiosTime();
174+
let mut mgr = embedded_sdmmc::VolumeManager::new(bios_block, time);
175+
// Open the first partition
176+
let mut volume = mgr.get_volume(VolumeIdx(0))?;
177+
let root_dir = mgr.open_root_dir(&volume)?;
178+
let mut file = mgr.open_file_in_dir(
179+
&mut volume,
180+
&root_dir,
181+
args[0],
182+
embedded_sdmmc::Mode::ReadOnly,
183+
)?;
184+
let file_length = file.length();
185+
// Application space starts 4K into Cortex-M SRAM
186+
const APPLICATION_START_ADDR: usize = 0x2000_1000;
187+
let application_ram: &'static mut [u8] = unsafe {
188+
core::slice::from_raw_parts_mut(APPLICATION_START_ADDR as *mut u8, file_length as usize)
189+
};
190+
mgr.read(&mut volume, &mut file, application_ram)?;
191+
Ok(())
192+
}
193+
194+
match work(args) {
195+
Ok(_) => {}
196+
Err(e) => {
197+
println!("Error: {:?}", e);
198+
}
199+
}
200+
}

src/commands/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub use super::Ctx;
66

77
mod block;
88
mod config;
9+
mod fs;
910
mod hardware;
1011
mod input;
1112
mod ram;
@@ -19,11 +20,13 @@ pub static OS_MENU: menu::Menu<Ctx> = menu::Menu {
1920
&config::COMMAND_ITEM,
2021
&block::LSBLK_ITEM,
2122
&block::READ_ITEM,
23+
&fs::DIR_ITEM,
2224
&hardware::LSHW_ITEM,
2325
&ram::HEXDUMP_ITEM,
24-
&ram::LOAD_ITEM,
2526
#[cfg(target_os = "none")]
2627
&ram::RUN_ITEM,
28+
#[cfg(target_os = "none")]
29+
&fs::LOAD_ITEM,
2730
&screen::CLEAR_ITEM,
2831
&screen::BENCH_ITEM,
2932
&screen::FILL_ITEM,

src/commands/ram.rs

Lines changed: 3 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,6 @@ pub static HEXDUMP_ITEM: menu::Item<Ctx> = menu::Item {
2020
help: Some("Dump the contents of RAM as hex"),
2121
};
2222

23-
pub static LOAD_ITEM: menu::Item<Ctx> = menu::Item {
24-
item_type: menu::ItemType::Callback {
25-
function: load,
26-
parameters: &[
27-
menu::Parameter::Mandatory {
28-
parameter_name: "address",
29-
help: Some("Start address"),
30-
},
31-
menu::Parameter::Mandatory {
32-
parameter_name: "hex",
33-
help: Some("Bytes as hex string"),
34-
},
35-
],
36-
},
37-
command: "load",
38-
help: Some("Load hex bytes into RAM from stdin"),
39-
};
40-
4123
#[cfg(target_os = "none")]
4224
pub static RUN_ITEM: menu::Item<Ctx> = menu::Item {
4325
item_type: menu::ItemType::Callback {
@@ -99,52 +81,16 @@ fn hexdump(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], _ctx
9981
println!();
10082
}
10183

102-
/// Called when the "load" command is executed.
103-
fn load(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], _ctx: &mut Ctx) {
104-
let Some(address_str) = args.get(0) else {
105-
println!("No address");
106-
return;
107-
};
108-
let Ok(address) = parse_usize(address_str) else {
109-
println!("Bad address");
110-
return;
111-
};
112-
let Some(mut hex_str) = args.get(1).cloned() else {
113-
println!("No hex");
114-
return;
115-
};
116-
117-
let mut address = address as *mut u8;
118-
let mut count = 0;
119-
loop {
120-
let Some(hex_byte) = hex_str.get(0..2) else {
121-
println!("Bad hex from {:?}", hex_str);
122-
return;
123-
};
124-
hex_str = &hex_str[2..];
125-
let Ok(byte) = u8::from_str_radix(hex_byte, 16) else {
126-
println!("Bad hex {:?}", hex_byte);
127-
return;
128-
};
129-
130-
unsafe {
131-
address.write_volatile(byte);
132-
address = address.offset(1);
133-
}
134-
count += 1;
135-
136-
println!("Loaded {} bytes", count);
137-
}
138-
}
139-
14084
#[allow(unused)]
14185
#[repr(C)]
14286
pub struct Api {
14387
pub print: extern "C" fn(data: *const u8, len: usize),
14488
}
14589

90+
#[allow(unused)]
14691
static CALLBACK_TABLE: Api = Api { print: print_fn };
14792

93+
#[allow(unused)]
14894
extern "C" fn print_fn(data: *const u8, len: usize) {
14995
let slice = unsafe { core::slice::from_raw_parts(data, len) };
15096
if let Ok(s) = core::str::from_utf8(slice) {
@@ -170,6 +116,7 @@ fn run(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ctx: &
170116
let code: extern "C" fn(*const Api) -> u32 = ::core::mem::transmute(start_ptr);
171117
code(&CALLBACK_TABLE)
172118
};
119+
println!();
173120
if result != 0 {
174121
println!("Got error code {}", result);
175122
}

0 commit comments

Comments
 (0)