1use std::process::ExitCode;
25
26use clap::Parser;
27use tag_tools::{BaseArgs, init_logging, select_base};
28use tagcore::{MemAccess, Tag};
29
30#[derive(Parser)]
34#[command(name = "tag-peek")]
35struct Cli {
36 #[command(flatten)]
38 base: BaseArgs,
39
40 #[arg(long = "addr", value_name = "ADDR", num_args = 1..)]
42 addr_opt: Vec<String>,
43
44 #[arg(value_name = "ADDR")]
46 addr: Vec<String>,
47}
48
49fn parse_address(text: &str) -> Option<u32> {
54 let (radix, digits) = match text.strip_prefix("0x").or_else(|| text.strip_prefix("0X")) {
55 Some(hex) => (16, hex),
56 None if text.len() > 1 && text.starts_with('0') => (8, &text[1..]),
57 None => (10, text),
58 };
59 if digits.is_empty() || digits.starts_with(['+', '-']) {
60 return None;
61 }
62 u32::from_str_radix(digits, radix).ok()
63}
64
65fn main() -> ExitCode {
68 let cli = Cli::parse();
69 init_logging(&cli.base);
70
71 let tag = Tag::new();
72 let dev = select_base::<Cli>(&tag, &cli.base);
73 let addrs: Vec<&String> = cli.addr_opt.iter().chain(&cli.addr).collect();
74 if addrs.is_empty() {
75 eprintln!("usage: tag-peek <addr> [addr...]");
76 return ExitCode::FAILURE;
77 }
78
79 if !tag.attach(&dev) {
80 eprintln!("Attach failed");
81 return ExitCode::FAILURE;
82 }
83
84 let mut rc = ExitCode::SUCCESS;
85 for addr_str in addrs {
86 let Some(addr) = parse_address(addr_str) else {
87 eprintln!("not an address: {addr_str}");
88 rc = ExitCode::FAILURE;
89 continue;
90 };
91 let mut value = [0u8; 4];
92 let result = tag.read_memory(addr, &mut value);
93 if result != MemAccess::Ok {
94 eprintln!("read failed for {addr_str}: {}", result.name());
95 rc = ExitCode::FAILURE;
96 continue;
97 }
98 println!("0x{addr:08X}: 0x{:08X}", u32::from_le_bytes(value));
99 }
100 rc
101}
102
103#[cfg(test)]
104mod tests {
106 use super::parse_address;
107
108 #[test]
109 fn addresses_must_be_whole_32_bit_numbers() {
112 assert_eq!(parse_address("0xE000EDFC"), Some(0xE000_EDFC));
113 assert_eq!(parse_address("536870912"), Some(0x2000_0000));
114 assert_eq!(parse_address("010"), Some(8));
115 assert_eq!(parse_address("0"), Some(0));
116 assert_eq!(parse_address("12abc"), None);
117 assert_eq!(parse_address("zzz"), None);
118 assert_eq!(parse_address("-4"), None);
119 assert_eq!(parse_address("0x"), None);
120 assert_eq!(parse_address("0x100000000"), None);
121 assert_eq!(parse_address(""), None);
122 }
123}