Skip to content

Commit 74089ae

Browse files
committed
Add an object for managing the TPA.
Also involves moving the filesystem code, and a new command called "loadf" which can load applications from Flash.
1 parent 8bd0638 commit 74089ae

File tree

13 files changed

+380
-163
lines changed

13 files changed

+380
-163
lines changed

neotron-flash-0002.ld

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ MEMORY
3232

3333
/* # Entry point = what the BIOS calls to start the OS */
3434
ENTRY(main);
35-
EXTERN(__RESET_VECTOR);
35+
36+
/*
37+
Where the Transient Program Area starts.
38+
*/
39+
_tpa_start = ORIGIN(RAM) + LENGTH(RAM);
3640

3741
/* # Sections */
3842
SECTIONS

neotron-flash-0802.ld

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ MEMORY
3232

3333
/* # Entry point = what the BIOS calls to start the OS */
3434
ENTRY(main);
35-
EXTERN(__RESET_VECTOR);
35+
36+
/*
37+
Where the Transient Program Area starts.
38+
*/
39+
_tpa_start = ORIGIN(RAM) + LENGTH(RAM);
3640

3741
/* # Sections */
3842
SECTIONS

neotron-flash-1002.ld

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,24 @@ MEMORY
2323
{
2424
/* The first 128 KiB is for the BIOS. We get the rest. */
2525
FLASH (rx) : ORIGIN = 0x10020000, LENGTH = 128K
26+
2627
/*
27-
* We get the bottom 4KB of RAM. Anything above that is for applications
28-
* (up to wherever the BIOS tells us we can use.)
28+
* The RAM reserved for the OS. Above this is the Transient Program Area.
29+
*
30+
* This is defined by the Neotron specification for a given platform. On this
31+
* Cortex-M based platform, it's the start of Cortex-M SRAM, plus 4 KiB, or
32+
* 0x2000_1000.
2933
*/
3034
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 4K
3135
}
3236

3337
/* # Entry point = what the BIOS calls to start the OS */
3438
ENTRY(main);
35-
EXTERN(__RESET_VECTOR);
39+
40+
/*
41+
Where the Transient Program Area starts.
42+
*/
43+
_tpa_start = ORIGIN(RAM) + LENGTH(RAM);
3644

3745
/* # Sections */
3846
SECTIONS

src/bin/flash0002.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
/// of our portion of Flash.
1414
#[link_section = ".entry_point"]
1515
#[used]
16-
pub static ENTRY_POINT_ADDR: extern "C" fn(*const neotron_common_bios::Api) -> ! = neotron_os::main;
16+
pub static ENTRY_POINT_ADDR: extern "C" fn(&neotron_common_bios::Api) -> ! = neotron_os::main;

src/bin/flash0802.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
/// of our portion of Flash.
1414
#[link_section = ".entry_point"]
1515
#[used]
16-
pub static ENTRY_POINT_ADDR: extern "C" fn(*const neotron_common_bios::Api) -> ! = neotron_os::main;
16+
pub static ENTRY_POINT_ADDR: extern "C" fn(&neotron_common_bios::Api) -> ! = neotron_os::main;

src/bin/flash1002.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
/// of our portion of Flash.
1414
#[link_section = ".entry_point"]
1515
#[used]
16-
pub static ENTRY_POINT_ADDR: extern "C" fn(*const neotron_common_bios::Api) -> ! = neotron_os::main;
16+
pub static ENTRY_POINT_ADDR: extern "C" fn(&neotron_common_bios::Api) -> ! = neotron_os::main;

src/commands/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub static COMMAND_ITEM: menu::Item<Ctx> = menu::Item {
2222

2323
/// Called when the "config" command is executed.
2424
fn command(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], ctx: &mut Ctx) {
25-
let command = args.get(0).cloned().unwrap_or("print");
25+
let command = args.first().cloned().unwrap_or("print");
2626
match command {
2727
"reset" => match config::Config::load() {
2828
Ok(new_config) => {

src/commands/fs.rs

Lines changed: 9 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
//! File Systems related commands for Neotron OS
22
3-
use chrono::{Datelike, Timelike};
43
use embedded_sdmmc::VolumeIdx;
54

6-
use crate::{bios, print, println, Ctx, API};
5+
use crate::{bios, print, println, Ctx};
76

87
pub static DIR_ITEM: menu::Item<Ctx> = menu::Item {
98
item_type: menu::ItemType::Callback {
@@ -14,7 +13,6 @@ pub static DIR_ITEM: menu::Item<Ctx> = menu::Item {
1413
help: Some("Dir the root directory on block device 0"),
1514
};
1615

17-
#[cfg(target_os = "none")]
1816
pub static LOAD_ITEM: menu::Item<Ctx> = menu::Item {
1917
item_type: menu::ItemType::Callback {
2018
function: load,
@@ -27,89 +25,12 @@ pub static LOAD_ITEM: menu::Item<Ctx> = menu::Item {
2725
help: Some("Load a file into the application area"),
2826
};
2927

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-
10728
/// Called when the "dir" command is executed.
10829
fn dir(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ctx: &mut Ctx) {
10930
fn work() -> Result<(), embedded_sdmmc::Error<bios::Error>> {
11031
println!("Listing files on Block Device 0, /");
111-
let bios_block = BiosBlock();
112-
let time = BiosTime();
32+
let bios_block = crate::fs::BiosBlock();
33+
let time = crate::fs::BiosTime();
11334
let mut mgr = embedded_sdmmc::VolumeManager::new(bios_block, time);
11435
// Open the first partition
11536
let volume = mgr.get_volume(VolumeIdx(0))?;
@@ -165,33 +86,12 @@ fn dir(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ctx: &
16586
}
16687

16788
/// 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) {
89+
fn load(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], ctx: &mut Ctx) {
90+
let Some(filename) = args.first() else {
91+
println!("Need a filename");
92+
return;
93+
};
94+
match ctx.tpa.load_program(filename) {
19595
Ok(_) => {}
19696
Err(e) => {
19797
println!("Error: {:?}", e);

src/commands/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ pub static OS_MENU: menu::Menu<Ctx> = menu::Menu {
2323
&fs::DIR_ITEM,
2424
&hardware::LSHW_ITEM,
2525
&ram::HEXDUMP_ITEM,
26-
#[cfg(target_os = "none")]
2726
&ram::RUN_ITEM,
28-
#[cfg(target_os = "none")]
27+
&ram::LOAD_ITEM,
2928
&fs::LOAD_ITEM,
3029
&screen::CLEAR_ITEM,
3130
&screen::BENCH_ITEM,

src/commands/ram.rs

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

23-
#[cfg(target_os = "none")]
2423
pub static RUN_ITEM: menu::Item<Ctx> = menu::Item {
2524
item_type: menu::ItemType::Callback {
2625
function: run,
@@ -30,13 +29,31 @@ pub static RUN_ITEM: menu::Item<Ctx> = menu::Item {
3029
help: Some("Jump to start of application area"),
3130
};
3231

32+
pub static LOAD_ITEM: menu::Item<Ctx> = menu::Item {
33+
item_type: menu::ItemType::Callback {
34+
function: loadf,
35+
parameters: &[
36+
menu::Parameter::Mandatory {
37+
parameter_name: "address",
38+
help: Some("The address to load from"),
39+
},
40+
menu::Parameter::Mandatory {
41+
parameter_name: "length",
42+
help: Some("The number of bytes to load"),
43+
},
44+
],
45+
},
46+
command: "loadf",
47+
help: Some("Copy a program from ROM/RAM into the application area"),
48+
};
49+
3350
fn parse_usize(input: &str) -> Result<usize, core::num::ParseIntError> {
3451
if let Some(digits) = input.strip_prefix("0x") {
3552
// Parse as hex
3653
usize::from_str_radix(digits, 16)
3754
} else {
3855
// Parse as decimal
39-
usize::from_str_radix(input, 10)
56+
input.parse::<usize>()
4057
}
4158
}
4259

@@ -47,7 +64,7 @@ fn parse_usize(input: &str) -> Result<usize, core::num::ParseIntError> {
4764
fn hexdump(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], _ctx: &mut Ctx) {
4865
const BYTES_PER_LINE: usize = 16;
4966

50-
let Some(address_str) = args.get(0) else {
67+
let Some(address_str) = args.first() else {
5168
println!("No address");
5269
return;
5370
};
@@ -81,43 +98,51 @@ fn hexdump(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], _ctx
8198
println!();
8299
}
83100

84-
#[allow(unused)]
85-
#[repr(C)]
86-
pub struct Api {
87-
pub print: extern "C" fn(data: *const u8, len: usize),
88-
}
89-
90-
#[allow(unused)]
91-
static CALLBACK_TABLE: Api = Api { print: print_fn };
92-
93-
#[allow(unused)]
94-
extern "C" fn print_fn(data: *const u8, len: usize) {
95-
let slice = unsafe { core::slice::from_raw_parts(data, len) };
96-
if let Ok(s) = core::str::from_utf8(slice) {
97-
print!("{}", s);
98-
} else {
99-
// Ignore App output - not UTF-8
101+
/// Called when the "run" command is executed.
102+
fn run(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], ctx: &mut Ctx) {
103+
match ctx.tpa.execute() {
104+
Ok(0) => {
105+
println!();
106+
}
107+
Ok(n) => {
108+
println!("\nError Code: {}", n);
109+
}
110+
Err(e) => {
111+
println!("\nFailed to execute: {:?}", e);
112+
}
100113
}
101114
}
102115

103-
/// Called when the "run" command is executed.
104-
#[cfg(target_os = "none")]
105-
fn run(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ctx: &mut Ctx) {
106-
use core::convert::TryInto;
107-
const APPLICATION_START_ADDR: usize = 0x2000_1000;
108-
const APPLICATION_LEN: usize = 4096;
109-
// Application space starts 4K into Cortex-M SRAM
110-
let application_ram: &'static mut [u8] = unsafe {
111-
core::slice::from_raw_parts_mut(APPLICATION_START_ADDR as *mut u8, APPLICATION_LEN)
116+
/// Called when the "loadf" command is executed.
117+
///
118+
/// If you ask for an address that generates a HardFault, the OS will crash. So
119+
/// don't.
120+
fn loadf(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, args: &[&str], ctx: &mut Ctx) {
121+
let Some(address_str) = args.first() else {
122+
println!("No address");
123+
return;
112124
};
113-
let start_word: [u8; 4] = (&application_ram[0..4]).try_into().unwrap();
114-
let start_ptr = usize::from_le_bytes(start_word) as *const ();
115-
let result = unsafe {
116-
let code: extern "C" fn(*const Api) -> u32 = ::core::mem::transmute(start_ptr);
117-
code(&CALLBACK_TABLE)
125+
let Ok(address) = parse_usize(address_str) else {
126+
println!("Bad address");
127+
return;
118128
};
119-
println!();
120-
if result != 0 {
121-
println!("Got error code {}", result);
129+
let Some(len_str) = args.get(1) else {
130+
println!("No length");
131+
return;
132+
};
133+
let Ok(len) = parse_usize(len_str) else {
134+
println!("Bad length");
135+
return;
136+
};
137+
138+
let src_slice = unsafe { core::slice::from_raw_parts(address as *const u8, len) };
139+
140+
match ctx.tpa.copy_program(src_slice) {
141+
Ok(_) => {
142+
println!("Ok");
143+
}
144+
Err(e) => {
145+
println!("Error: {:?}", e);
146+
}
122147
}
123148
}

0 commit comments

Comments
 (0)