Skip to content

Commit e333abd

Browse files
Add load, hexdump and run.
1 parent 824e569 commit e333abd

File tree

2 files changed

+181
-0
lines changed

2 files changed

+181
-0
lines changed

src/commands/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,18 @@ pub use super::Ctx;
77
mod config;
88
mod hardware;
99
mod input;
10+
mod ram;
1011
mod screen;
1112

1213
pub static OS_MENU: menu::Menu<Ctx> = menu::Menu {
1314
label: "root",
1415
items: &[
1516
&config::COMMAND_ITEM,
1617
&hardware::LSHW_ITEM,
18+
&ram::HEXDUMP_ITEM,
19+
&ram::LOAD_ITEM,
20+
#[cfg(target_os = "none")]
21+
&ram::RUN_ITEM,
1722
&screen::CLEAR_ITEM,
1823
&screen::FILL_ITEM,
1924
&input::KBTEST_ITEM,

src/commands/ram.rs

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
//! Raw RAM read/write related commands for Neotron OS
2+
3+
use crate::{print, println, Ctx};
4+
5+
pub static HEXDUMP_ITEM: menu::Item<Ctx> = menu::Item {
6+
item_type: menu::ItemType::Callback {
7+
function: hexdump,
8+
parameters: &[
9+
menu::Parameter::Mandatory {
10+
parameter_name: "address",
11+
help: Some("Start address"),
12+
},
13+
menu::Parameter::Optional {
14+
parameter_name: "length",
15+
help: Some("Number of bytes"),
16+
},
17+
],
18+
},
19+
command: "hexdump",
20+
help: Some("Dump the contents of RAM as hex"),
21+
};
22+
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+
41+
#[cfg(target_os = "none")]
42+
pub static RUN_ITEM: menu::Item<Ctx> = menu::Item {
43+
item_type: menu::ItemType::Callback {
44+
function: run,
45+
parameters: &[],
46+
},
47+
command: "run",
48+
help: Some("Jump to start of application area"),
49+
};
50+
51+
fn parse_usize(input: &str) -> Result<usize, core::num::ParseIntError> {
52+
if let Some(digits) = input.strip_prefix("0x") {
53+
// Parse as hex
54+
usize::from_str_radix(digits, 16)
55+
} else {
56+
// Parse as decimal
57+
usize::from_str_radix(input, 10)
58+
}
59+
}
60+
61+
/// Called when the "hexdump" command is executed.
62+
///
63+
/// If you ask for an address that generates a HardFault, the OS will crash. So
64+
/// don't.
65+
fn hexdump(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], _ctx: &mut Ctx) {
66+
const BYTES_PER_LINE: usize = 16;
67+
68+
let Some(address_str) = args.get(0) else {
69+
println!("No address");
70+
return;
71+
};
72+
let Ok(address) = parse_usize(address_str) else {
73+
println!("Bad address");
74+
return;
75+
};
76+
let len_str = args.get(1).unwrap_or(&"16");
77+
let Ok(len) = parse_usize(len_str) else {
78+
println!("Bad length");
79+
return;
80+
};
81+
82+
let mut ptr = address as *const u8;
83+
84+
let mut this_line = 0;
85+
print!("{:08x}: ", address);
86+
for count in 0..len {
87+
if this_line == BYTES_PER_LINE {
88+
println!();
89+
print!("{:08x}: ", address + count);
90+
this_line = 1;
91+
} else {
92+
this_line += 1;
93+
}
94+
95+
let b = unsafe { ptr.read_volatile() };
96+
print!("{:02x} ", b);
97+
ptr = unsafe { ptr.offset(1) };
98+
}
99+
println!();
100+
}
101+
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+
140+
#[allow(unused)]
141+
#[repr(C)]
142+
pub struct Api {
143+
pub print: extern "C" fn(data: *const u8, len: usize),
144+
}
145+
146+
static CALLBACK_TABLE: Api = Api { print: print_fn };
147+
148+
extern "C" fn print_fn(data: *const u8, len: usize) {
149+
let slice = unsafe { core::slice::from_raw_parts(data, len) };
150+
if let Ok(s) = core::str::from_utf8(slice) {
151+
print!("{}", s);
152+
} else {
153+
// Ignore App output - not UTF-8
154+
}
155+
}
156+
157+
/// Called when the "run" command is executed.
158+
#[cfg(target_os = "none")]
159+
fn run(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ctx: &mut Ctx) {
160+
use core::convert::TryInto;
161+
const APPLICATION_START_ADDR: usize = 0x2000_1000;
162+
const APPLICATION_LEN: usize = 4096;
163+
// Application space starts 4K into Cortex-M SRAM
164+
let application_ram: &'static mut [u8] = unsafe {
165+
core::slice::from_raw_parts_mut(APPLICATION_START_ADDR as *mut u8, APPLICATION_LEN)
166+
};
167+
let start_word: [u8; 4] = (&application_ram[0..4]).try_into().unwrap();
168+
let start_ptr = usize::from_le_bytes(start_word) as *const ();
169+
let result = unsafe {
170+
let code: extern "C" fn(*const Api) -> u32 = ::core::mem::transmute(start_ptr);
171+
code(&CALLBACK_TABLE)
172+
};
173+
if result != 0 {
174+
println!("Got error code {}", result);
175+
}
176+
}

0 commit comments

Comments
 (0)