use crate::prog::{Program, PROGRAMS};
use crate::{uprintln, uprint};
use whyos::TaskHandle;
pub struct Env<'a> {
pub user_programs: &'a [Program],
pub last_task: &'a mut Option<TaskHandle>,
}
pub type CmdAction = fn(cmd: &Cmd, args: &str, env: &mut Env);
pub struct Cmd {
pub names: &'static [&'static str],
pub usage: &'static str,
pub desc: &'static str,
pub action: CmdAction,
}
impl Cmd {
pub fn parse(name: &str) -> Option<&'static Cmd> {
COMMANDS.iter().find(|cmd| cmd.names.contains(&name))
}
pub fn run(&self, args: &str, env: &mut Env) {
(self.action)(self, args, env)
}
}
static COMMANDS: &[Cmd] = &[
Cmd {
names: &["help", "h", "?"],
usage: "help|h|?",
desc: "Show this help message",
action: |_, _, _| {
uprintln!("Commands:");
for cmd in COMMANDS {
uprintln!(" {:<26} {}", cmd.usage, cmd.desc);
}
}
},
Cmd {
names: &["clear", "cls"],
usage: "clear|cls",
desc: "Clear the terminal screen",
action: |_, _, _| {
const CLEAR_SEQ: &str = "\x1b[2J\x1b[3J\x1b[H";
uprint!("{}", CLEAR_SEQ)
}
},
Cmd {
names: &["name", "n"],
usage: "name|n",
desc: "Print OS build name",
action: |_, _, _| {
uprintln!("{}", whyos::build_name());
}
},
Cmd {
names: &["reboot"],
usage: "reboot",
desc: "Reboot the system",
action: |_, _, _| {
uprintln!("Rebooting system...");
whyos::reboot();
}
},
Cmd {
names: &["uptime", "u"],
usage: "uptime",
desc: "Show system uptime in ticks",
action: |_, _, _| {
let ticks = whyos::uptime_ticks();
uprintln!("{} ticks", ticks);
}
},
Cmd {
names: &["ps"],
usage: "ps",
desc: "List all tasks",
action: |_, _, _| {
uprintln!(" ID | State | Stack(Peak/Total) | Name");
uprintln!("────+───────────+───────────────────+──────────────");
for handle in whyos::allocated() {
if let Ok(info) = handle.info() {
let name = info.name.unwrap_or("-");
let pct = (info.max_stack_usage * 100) / info.stack_size;
uprintln!(" {:>4} | {:<9} | {:>4} / {:<4} ({:>2}%) | {}",
info.handle.as_u32(),
info.state,
info.max_stack_usage,
info.stack_size,
pct,
name
);
}
}
}
},
Cmd {
names: &["info", "i"],
usage: "info|i <id>",
desc: "Show detailed task information",
action: |cmd, args, _| {
if let Some(handle) = parse_id(args) {
match handle.info() {
Ok(info) => {
let stack_top = info.stack_base + info.stack_size;
let current_usage = stack_top.saturating_sub(info.current_sp);
uprintln!(
"──────────────────────────────────────────\r\n\
Task ID: {}\r\n\
Name: {}\r\n\
State: {}\r\n\
Priority: {}\r\n\
------------------------------------------\r\n\
Stack Base: 0x{:08x}\r\n\
Stack Ptr: 0x{:08x}\r\n\
Stack Size: {} bytes\r\n\
Current Use: {} bytes\r\n\
Peak Usage: {} bytes ({}%)\r\n\
──────────────────────────────────────────",
info.handle,
info.name.unwrap_or("<unnamed>"),
info.state,
info.priority,
info.stack_base,
info.current_sp,
info.stack_size,
current_usage,
info.max_stack_usage,
(info.max_stack_usage * 100) / info.stack_size
);
}
Err(e) => uprintln!("Error: {}", e),
}
} else {
uprintln!("Usage: {}", cmd.usage)
}
}
},
Cmd {
names: &["suspend", "s"],
usage: "suspend|s <id>",
desc: "Suspend a task",
action: |cmd, args, _| {
if let Some(handle) = parse_id(args) {
match handle.suspend() {
Ok(_) => uprintln!("Task {} suspended", handle),
Err(_) => uprintln!("Error: Failed to suspend task {}", handle)
}
} else {
uprintln!("Usage: {}", cmd.usage);
}
}
},
Cmd {
names: &["resume", "r"],
usage: "resume|r <id>",
desc: "Resume a suspended task",
action: |cmd, args, _| {
if let Some(handle) = parse_id(args) {
match handle.resume() {
Ok(_) => uprintln!("Task {} resumed", handle),
Err(e) => uprintln!("Error: {}", e)
}
} else {
uprintln!("Usage: {}", cmd.usage);
}
}
},
Cmd {
names: &["kill", "k"],
usage: "kill|k <id>",
desc: "Kill a task (unsafe)",
action: |cmd, args, _| {
if let Some(handle) = parse_id(args) {
match handle.kill() {
Ok(_) => uprintln!("Task {} killed", handle),
Err(e) => uprintln!("Error: {}", e)
}
} else {
uprintln!("Usage: {}", cmd.usage);
}
}
},
Cmd {
names: &["freq"],
usage: "freq [hz]",
desc: "Get or set system tick frequency",
action: |cmd, args, _| {
let args = args.trim();
if args.is_empty() {
uprintln!("{} Hz", whyos::tick_freq());
} else if let Ok(freq) = args.parse::<u32>() {
if let Some(freq) = whyos::Freq::from_hz(freq) {
whyos::set_tick_freq(freq);
uprintln!("System frequency updated to {} Hz", freq.as_hz());
} else {
uprintln!("Error: Frequency cannot be 0");
}
} else {
uprintln!("Usage: {}", cmd.usage);
}
}
},
Cmd {
names: &["list"],
usage: "list",
desc: "List available programs",
action: |_, _, env| {
uprintln!(" Name | Prio | Stack | Default | Description");
uprintln!("────────────────+──────+───────+─────────+──────────────────────────");
let all_progs = PROGRAMS.iter().chain(env.user_programs.iter());
for prog in all_progs {
uprintln!(
" {:<14} | {:<4} | {:<5} | {:<7} | {}",
prog.name,
prog.priority,
prog.stack_size.as_bytes(),
prog.default_arg,
prog.desc
);
}
}
},
Cmd {
names: &["execute", "e"],
usage: "execute|e <name> [num_arg]",
desc: "Execute a program",
action: |cmd, args, env| {
let args = args.trim();
let (name, arg_str) = args.split_once(' ').unwrap_or((args, ""));
let arg_str = arg_str.trim();
if name.is_empty() {
uprintln!("Usage: {}", cmd.usage);
return;
}
let parsed_arg = if arg_str.is_empty() {
None
} else if let Ok(val) = arg_str.parse::<usize>() {
Some(val)
} else {
uprintln!("Usage: {}", cmd.usage);
return;
};
let found_prog = PROGRAMS.iter()
.chain(env.user_programs.iter())
.find(|prog| prog.name == name);
if let Some(prog) = found_prog {
let arg = parsed_arg.unwrap_or(prog.default_arg);
match whyos::TaskBuilder::with_value(prog.entry, arg)
.name(prog.name)
.priority(prog.priority)
.stack_size(prog.stack_size)
.spawn()
{
Ok(h) => {
uprintln!("Spawned task {} ({}) (arg: {})", h.as_u32(), name, arg);
*env.last_task = Some(h);
},
Err(e) => uprintln!("Couldn't execute '{}', error: {}", name, e),
}
} else {
uprintln!("Unknown program: '{}'. Type 'list' to see available.", name);
}
}
},
Cmd {
names: &["peek"],
usage: "peek <hex_addr>",
desc: "Read a 32-bit value from memory (might HardFault)",
action: |cmd, args, _| {
if let Some(addr) = parse_hex(args) {
if addr % 4 != 0 {
uprintln!("Error: Address must be 4-byte aligned!");
return;
}
let val = unsafe { core::ptr::read_volatile(addr as *const u32) };
uprintln!("Value at 0x{:08X} = 0x{:08X}", addr, val);
} else {
uprintln!("Usage: {}", cmd.usage);
}
}
},
Cmd {
names: &["poke"],
usage: "poke <hex_addr> <hex_val>",
desc: "Write a 32-bit value to memory (might HardFault)",
action: |cmd, args, _| {
let (addr_str, val_str) = args.trim().split_once(' ').unwrap_or(("", ""));
if let (Some(addr), Some(val)) = (parse_hex(addr_str), parse_hex(val_str)) {
if addr % 4 != 0 {
uprintln!("Error: Address must be 4-byte aligned!");
return;
}
unsafe { core::ptr::write_volatile(addr as *mut u32, val as u32) };
uprintln!("Written 0x{:08X} to 0x{:08X}", val as u32, addr);
} else {
uprintln!("Usage: {}", cmd.usage);
}
}
},
];
fn parse_id(args: &str) -> Option<TaskHandle> {
let args = args.trim();
if args.is_empty() { return None; }
args.parse::<u32>().ok().and_then(TaskHandle::from_u32)
}
fn parse_hex(args: &str) -> Option<usize> {
let args = args.trim().strip_prefix("0x").unwrap_or(args.trim());
if args.is_empty() { return None; }
usize::from_str_radix(args, 16).ok()
}