1use core::cell::RefCell;
7use mailang_bytecode::Value;
8use std::collections::HashMap;
9use std::rc::Rc;
10
11pub type Pin = u8;
12pub type Port = u8;
13pub type I2cAddr = u8;
14
15pub trait Gpio {
16 fn set_mode_output(&mut self, pin: Pin);
17 fn set_mode_input(&mut self, pin: Pin);
18 fn write(&mut self, pin: Pin, high: bool) -> Result<(), String>;
19 fn read(&self, pin: Pin) -> Result<bool, String>;
20}
21
22pub trait Delay {
23 fn delay_ms(&mut self, ms: u32);
24}
25
26pub trait Adc {
27 fn read_raw(&mut self, channel: u8) -> Result<u16, String>;
28}
29
30pub trait Pwm {
31 fn set_duty(&mut self, pin: Pin, duty_16: u16) -> Result<(), String>;
32 fn set_freq(&mut self, pin: Pin, hz: u32) -> Result<(), String>;
33}
34
35pub trait Uart {
36 fn write_bytes(&mut self, port: Port, data: &[u8]) -> Result<(), String>;
37 fn read_bytes(&mut self, port: Port, max: usize) -> Result<Vec<u8>, String>;
38}
39
40pub trait I2c {
41 fn write_read(&mut self, addr: I2cAddr, w: &[u8], rlen: usize) -> Result<Vec<u8>, String>;
42}
43
44pub trait Spi {
45 fn transfer(&mut self, addr: I2cAddr, tx: &[u8], rx_len: usize) -> Result<Vec<u8>, String>;
46}
47
48#[derive(Debug, Clone)]
51pub struct SimulatedHal {
52 pins: Rc<RefCell<[bool; 32]>>,
53 outputs: Rc<RefCell<[bool; 32]>>,
54 ticks_ms: Rc<RefCell<u64>>,
55 adc: Rc<RefCell<[u16; 8]>>,
56 pwm_duty: Rc<RefCell<[u16; 32]>>,
57 pwm_freq: Rc<RefCell<[u32; 32]>>,
58 uart_rx: Rc<RefCell<HashMap<Port, Vec<u8>>>>,
60 i2c_writes: Rc<RefCell<HashMap<I2cAddr, Vec<u8>>>>,
62}
63
64impl Default for SimulatedHal {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70impl SimulatedHal {
71 pub fn new() -> Self {
72 Self {
73 pins: Rc::new(RefCell::new([false; 32])),
74 outputs: Rc::new(RefCell::new([false; 32])),
75 ticks_ms: Rc::new(RefCell::new(0)),
76 adc: Rc::new(RefCell::new([0; 8])),
77 pwm_duty: Rc::new(RefCell::new([0; 32])),
78 pwm_freq: Rc::new(RefCell::new([0; 32])),
79 uart_rx: Rc::new(RefCell::new(HashMap::new())),
80 i2c_writes: Rc::new(RefCell::new(HashMap::new())),
81 }
82 }
83
84 pub fn set_adc(&mut self, channel: u8, raw: u16) {
86 if (channel as usize) < 8 {
87 self.adc.borrow_mut()[channel as usize] = raw;
88 }
89 }
90
91 pub fn drive_pin(&mut self, pin: Pin, high: bool) {
93 if (pin as usize) < 32 {
94 self.pins.borrow_mut()[pin as usize] = high;
95 }
96 }
97
98 pub fn elapsed_ms(&self) -> u64 {
99 *self.ticks_ms.borrow()
100 }
101
102 pub fn pwm_duty(&self, pin: Pin) -> Result<u16, String> {
103 let i = Self::check_pin(pin)?;
104 Ok(self.pwm_duty.borrow()[i])
105 }
106
107 pub fn pwm_freq(&self, pin: Pin) -> Result<u32, String> {
108 let i = Self::check_pin(pin)?;
109 Ok(self.pwm_freq.borrow()[i])
110 }
111
112 pub fn i2c_last_write(&self, addr: I2cAddr) -> Option<Vec<u8>> {
113 self.i2c_writes.borrow().get(&addr).cloned()
114 }
115
116 fn check_pin(pin: Pin) -> Result<usize, String> {
117 if (pin as usize) < 32 {
118 Ok(pin as usize)
119 } else {
120 Err(format!("pin {} out of range (0..31)", pin))
121 }
122 }
123}
124
125impl Gpio for SimulatedHal {
126 fn set_mode_output(&mut self, _pin: Pin) {}
127 fn set_mode_input(&mut self, _pin: Pin) {}
128
129 fn write(&mut self, pin: Pin, high: bool) -> Result<(), String> {
130 let i = Self::check_pin(pin)?;
131 self.outputs.borrow_mut()[i] = high;
132 self.pins.borrow_mut()[i] = high;
133 Ok(())
134 }
135
136 fn read(&self, pin: Pin) -> Result<bool, String> {
137 let i = Self::check_pin(pin)?;
138 Ok(self.pins.borrow()[i])
139 }
140}
141
142impl Delay for SimulatedHal {
143 fn delay_ms(&mut self, ms: u32) {
144 *self.ticks_ms.borrow_mut() += ms as u64;
145 }
146}
147
148impl Adc for SimulatedHal {
149 fn read_raw(&mut self, channel: u8) -> Result<u16, String> {
150 if (channel as usize) < 8 {
151 Ok(self.adc.borrow()[channel as usize])
152 } else {
153 Err(format!("adc channel {} out of range (0..7)", channel))
154 }
155 }
156}
157
158impl Pwm for SimulatedHal {
159 fn set_duty(&mut self, pin: Pin, duty_16: u16) -> Result<(), String> {
160 let i = Self::check_pin(pin)?;
161 self.pwm_duty.borrow_mut()[i] = duty_16;
162 Ok(())
163 }
164
165 fn set_freq(&mut self, pin: Pin, hz: u32) -> Result<(), String> {
166 let i = Self::check_pin(pin)?;
167 self.pwm_freq.borrow_mut()[i] = hz;
168 Ok(())
169 }
170}
171
172impl Uart for SimulatedHal {
173 fn write_bytes(&mut self, port: Port, data: &[u8]) -> Result<(), String> {
174 self.uart_rx
175 .borrow_mut()
176 .entry(port)
177 .or_default()
178 .extend_from_slice(data);
179 Ok(())
180 }
181
182 fn read_bytes(&mut self, port: Port, max: usize) -> Result<Vec<u8>, String> {
183 let mut map = self.uart_rx.borrow_mut();
184 let buf = map.entry(port).or_default();
185 let n = max.min(buf.len());
186 Ok(buf.drain(..n).collect())
187 }
188}
189
190impl I2c for SimulatedHal {
191 fn write_read(&mut self, addr: I2cAddr, w: &[u8], rlen: usize) -> Result<Vec<u8>, String> {
192 self.i2c_writes.borrow_mut().insert(addr, w.to_vec());
193 Ok((0..rlen).map(|i| addr.wrapping_add(i as u8)).collect())
195 }
196}
197
198impl Spi for SimulatedHal {
199 fn transfer(&mut self, _addr: I2cAddr, tx: &[u8], rx_len: usize) -> Result<Vec<u8>, String> {
200 let mut rx = tx.to_vec();
202 rx.resize(rx_len, 0);
203 Ok(rx)
204 }
205}
206
207thread_local! {
208 static HAL: RefCell<SimulatedHal> = RefCell::new(SimulatedHal::new());
209}
210
211fn pin_arg(args: &[Value], idx: usize, who: &str) -> Result<Pin, String> {
212 match args.get(idx) {
213 Some(Value::Int(n)) if *n >= 0 && *n <= 255 => Ok(*n as u8),
214 _ => Err(format!("{}: pin must be int 0..255", who)),
215 }
216}
217
218fn parse_hex(s: &str) -> Result<Vec<u8>, String> {
219 let s = s
220 .strip_prefix("0x")
221 .or_else(|| s.strip_prefix("0X"))
222 .unwrap_or(s);
223 if !s.len().is_multiple_of(2) {
224 return Err("hex data must have even length".into());
225 }
226 (0..s.len())
227 .step_by(2)
228 .map(|i| {
229 u8::from_str_radix(&s[i..i + 2], 16)
230 .map_err(|_| format!("invalid hex at byte {}", i / 2))
231 })
232 .collect()
233}
234
235fn to_hex(data: &[u8]) -> String {
236 data.iter().map(|b| format!("{:02x}", b)).collect()
237}
238
239pub fn builtin_gpio_write(args: &[Value]) -> Result<Value, String> {
241 let pin = pin_arg(args, 0, "gpio_write(pin, high)")?;
242 let high = match args.get(1) {
243 Some(Value::Bool(b)) => *b,
244 Some(Value::Int(n)) => *n != 0,
245 _ => return Err("gpio_write(pin, high): high must be bool or int".into()),
246 };
247 HAL.with(|h| {
248 let mut h = h.borrow_mut();
249 h.set_mode_output(pin);
250 h.write(pin, high)
251 })?;
252 Ok(Value::Null)
253}
254
255pub fn builtin_gpio_read(args: &[Value]) -> Result<Value, String> {
257 let pin = pin_arg(args, 0, "gpio_read(pin)")?;
258 let v = HAL.with(|h| h.borrow().read(pin))?;
259 Ok(Value::Bool(v))
260}
261
262pub fn builtin_delay_ms(args: &[Value]) -> Result<Value, String> {
264 let ms = match args.first() {
265 Some(Value::Int(n)) if *n >= 0 => *n as u32,
266 _ => return Err("delay_ms(ms): ms must be non-negative int".into()),
267 };
268 HAL.with(|h| h.borrow_mut().delay_ms(ms));
269 Ok(Value::Null)
270}
271
272pub fn builtin_adc_read(args: &[Value]) -> Result<Value, String> {
274 let ch = match args.first() {
275 Some(Value::Int(n)) if *n >= 0 && *n <= 255 => *n as u8,
276 _ => return Err("adc_read(channel): channel must be int 0..255".into()),
277 };
278 let v = HAL.with(|h| h.borrow_mut().read_raw(ch))?;
279 Ok(Value::Int(v as i64))
280}
281
282pub fn builtin_pwm_write(args: &[Value]) -> Result<Value, String> {
284 let pin = pin_arg(args, 0, "pwm_write(pin, duty)")?;
285 let duty = match args.get(1) {
286 Some(Value::Int(n)) if *n >= 0 && *n <= 65535 => *n as u16,
287 _ => return Err("pwm_write(pin, duty): duty must be int 0..65535".into()),
288 };
289 HAL.with(|h| h.borrow_mut().set_duty(pin, duty))?;
290 Ok(Value::Null)
291}
292
293pub fn builtin_pwm_freq(args: &[Value]) -> Result<Value, String> {
295 let pin = pin_arg(args, 0, "pwm_freq(pin, hz)")?;
296 let hz = match args.get(1) {
297 Some(Value::Int(n)) if *n >= 0 && *n <= u32::MAX as i64 => *n as u32,
298 _ => return Err("pwm_freq(pin, hz): hz must be non-negative int".into()),
299 };
300 HAL.with(|h| h.borrow_mut().set_freq(pin, hz))?;
301 Ok(Value::Null)
302}
303
304pub fn builtin_uart_write(args: &[Value]) -> Result<Value, String> {
306 let port = match args.first() {
307 Some(Value::Int(n)) if *n >= 0 && *n <= 255 => *n as u8,
308 _ => return Err("uart_write(port, s): port must be int 0..255".into()),
309 };
310 let s = match args.get(1) {
311 Some(Value::Str(s)) => s.to_string(),
312 _ => return Err("uart_write(port, s): s must be string".into()),
313 };
314 HAL.with(|h| h.borrow_mut().write_bytes(port, s.as_bytes()))?;
315 Ok(Value::Null)
316}
317
318pub fn builtin_uart_read(args: &[Value]) -> Result<Value, String> {
320 let port = match args.first() {
321 Some(Value::Int(n)) if *n >= 0 && *n <= 255 => *n as u8,
322 _ => return Err("uart_read(port, n): port must be int 0..255".into()),
323 };
324 let n = match args.get(1) {
325 Some(Value::Int(n)) if *n >= 0 => *n as usize,
326 _ => return Err("uart_read(port, n): n must be non-negative int".into()),
327 };
328 let bytes = HAL.with(|h| h.borrow_mut().read_bytes(port, n))?;
329 Ok(Value::Str(
330 String::from_utf8_lossy(&bytes).into_owned().into(),
331 ))
332}
333
334pub fn builtin_i2c_xfer(args: &[Value]) -> Result<Value, String> {
336 let addr = match args.first() {
337 Some(Value::Int(n)) if *n >= 0 && *n <= 255 => *n as u8,
338 _ => return Err("i2c_xfer(addr, hexdata, rlen): addr must be int 0..255".into()),
339 };
340 let hex = match args.get(1) {
341 Some(Value::Str(s)) => parse_hex(s)?,
342 _ => return Err("i2c_xfer(addr, hexdata, rlen): hexdata must be string".into()),
343 };
344 let rlen = match args.get(2) {
345 Some(Value::Int(n)) if *n >= 0 => *n as usize,
346 _ => return Err("i2c_xfer(addr, hexdata, rlen): rlen must be non-negative int".into()),
347 };
348 let rx = HAL.with(|h| h.borrow_mut().write_read(addr, &hex, rlen))?;
349 Ok(Value::Str(to_hex(&rx).into()))
350}
351
352pub fn builtin_spi_xfer(args: &[Value]) -> Result<Value, String> {
354 let addr = match args.first() {
355 Some(Value::Int(n)) if *n >= 0 && *n <= 255 => *n as u8,
356 _ => return Err("spi_xfer(addr, hexdata, rxlen): addr must be int 0..255".into()),
357 };
358 let hex = match args.get(1) {
359 Some(Value::Str(s)) => parse_hex(s)?,
360 _ => return Err("spi_xfer(addr, hexdata, rxlen): hexdata must be string".into()),
361 };
362 let rxlen = match args.get(2) {
363 Some(Value::Int(n)) if *n >= 0 => *n as usize,
364 _ => return Err("spi_xfer(addr, hexdata, rxlen): rxlen must be non-negative int".into()),
365 };
366 let rx = HAL.with(|h| h.borrow_mut().transfer(addr, &hex, rxlen))?;
367 Ok(Value::Str(to_hex(&rx).into()))
368}
369
370pub fn sim_set_adc(channel: u8, raw: u16) {
372 HAL.with(|h| h.borrow_mut().set_adc(channel, raw));
373}
374
375pub fn sim_drive_pin(pin: Pin, high: bool) {
377 HAL.with(|h| h.borrow_mut().drive_pin(pin, high));
378}
379
380pub fn sim_elapsed_ms() -> u64 {
382 HAL.with(|h| h.borrow().elapsed_ms())
383}
384
385pub fn sim_pwm_duty(pin: Pin) -> Result<u16, String> {
387 HAL.with(|h| h.borrow().pwm_duty(pin))
388}
389
390pub fn sim_pwm_freq(pin: Pin) -> Result<u32, String> {
392 HAL.with(|h| h.borrow().pwm_freq(pin))
393}
394
395pub fn sim_i2c_last_write(addr: I2cAddr) -> Option<Vec<u8>> {
397 HAL.with(|h| h.borrow().i2c_last_write(addr))
398}
399
400#[cfg(test)]
401mod tests {
402 use super::*;
403
404 #[test]
405 fn gpio_roundtrip() {
406 builtin_gpio_write(&[Value::Int(3), Value::Bool(true)]).unwrap();
407 assert_eq!(
408 builtin_gpio_read(&[Value::Int(3)]).unwrap(),
409 Value::Bool(true)
410 );
411 }
412
413 #[test]
414 fn delay_advances_sim_clock() {
415 let before = sim_elapsed_ms();
416 builtin_delay_ms(&[Value::Int(50)]).unwrap();
417 assert_eq!(sim_elapsed_ms(), before + 50);
418 }
419
420 #[test]
421 fn adc_sim_inject() {
422 sim_set_adc(2, 1234);
423 assert_eq!(
424 builtin_adc_read(&[Value::Int(2)]).unwrap(),
425 Value::Int(1234)
426 );
427 }
428
429 #[test]
430 fn pwm_duty_and_freq() {
431 builtin_pwm_write(&[Value::Int(1), Value::Int(30000)]).unwrap();
432 builtin_pwm_freq(&[Value::Int(1), Value::Int(1000)]).unwrap();
433 assert_eq!(sim_pwm_duty(1).unwrap(), 30000);
434 assert_eq!(sim_pwm_freq(1).unwrap(), 1000);
435 }
436
437 #[test]
438 fn uart_loopback() {
439 builtin_uart_write(&[Value::Int(0), Value::Str("hi".into())]).unwrap();
440 let got = builtin_uart_read(&[Value::Int(0), Value::Int(16)]).unwrap();
441 assert_eq!(got, Value::Str("hi".into()));
442 }
443
444 #[test]
445 fn i2c_xfer_pattern_and_write_log() {
446 let rx = builtin_i2c_xfer(&[
447 Value::Int(0x50),
448 Value::Str("deadbeef".into()),
449 Value::Int(3),
450 ])
451 .unwrap();
452 assert_eq!(rx, Value::Str("505152".into()));
454 assert_eq!(sim_i2c_last_write(0x50), Some(vec![0xde, 0xad, 0xbe, 0xef]));
455 }
456
457 #[test]
458 fn spi_xfer_loopback_padded() {
459 let rx =
460 builtin_spi_xfer(&[Value::Int(0), Value::Str("a1b2".into()), Value::Int(4)]).unwrap();
461 assert_eq!(rx, Value::Str("a1b20000".into()));
462 }
463}