1use core::cell::RefCell;
7use mailang_bytecode::Value;
8use std::rc::Rc;
9
10pub type Pin = u8;
11
12pub trait Gpio {
13 fn set_mode_output(&mut self, pin: Pin);
14 fn set_mode_input(&mut self, pin: Pin);
15 fn write(&mut self, pin: Pin, high: bool) -> Result<(), String>;
16 fn read(&self, pin: Pin) -> Result<bool, String>;
17}
18
19pub trait Delay {
20 fn delay_ms(&mut self, ms: u32);
21}
22
23pub trait Adc {
24 fn read_raw(&mut self, channel: u8) -> Result<u16, String>;
25}
26
27#[derive(Debug, Clone)]
29pub struct SimulatedHal {
30 pins: Rc<RefCell<[bool; 32]>>,
31 outputs: Rc<RefCell<[bool; 32]>>,
32 ticks_ms: Rc<RefCell<u64>>,
33 adc: Rc<RefCell<[u16; 8]>>,
34}
35
36impl Default for SimulatedHal {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl SimulatedHal {
43 pub fn new() -> Self {
44 Self {
45 pins: Rc::new(RefCell::new([false; 32])),
46 outputs: Rc::new(RefCell::new([false; 32])),
47 ticks_ms: Rc::new(RefCell::new(0)),
48 adc: Rc::new(RefCell::new([0; 8])),
49 }
50 }
51
52 pub fn set_adc(&mut self, channel: u8, raw: u16) {
54 if (channel as usize) < 8 {
55 self.adc.borrow_mut()[channel as usize] = raw;
56 }
57 }
58
59 pub fn drive_pin(&mut self, pin: Pin, high: bool) {
61 if (pin as usize) < 32 {
62 self.pins.borrow_mut()[pin as usize] = high;
63 }
64 }
65
66 pub fn elapsed_ms(&self) -> u64 {
67 *self.ticks_ms.borrow()
68 }
69
70 fn check_pin(pin: Pin) -> Result<usize, String> {
71 if (pin as usize) < 32 {
72 Ok(pin as usize)
73 } else {
74 Err(format!("pin {} out of range (0..31)", pin))
75 }
76 }
77}
78
79impl Gpio for SimulatedHal {
80 fn set_mode_output(&mut self, _pin: Pin) {}
81 fn set_mode_input(&mut self, _pin: Pin) {}
82
83 fn write(&mut self, pin: Pin, high: bool) -> Result<(), String> {
84 let i = Self::check_pin(pin)?;
85 self.outputs.borrow_mut()[i] = high;
86 self.pins.borrow_mut()[i] = high;
87 Ok(())
88 }
89
90 fn read(&self, pin: Pin) -> Result<bool, String> {
91 let i = Self::check_pin(pin)?;
92 Ok(self.pins.borrow()[i])
93 }
94}
95
96impl Delay for SimulatedHal {
97 fn delay_ms(&mut self, ms: u32) {
98 *self.ticks_ms.borrow_mut() += ms as u64;
99 }
100}
101
102impl Adc for SimulatedHal {
103 fn read_raw(&mut self, channel: u8) -> Result<u16, String> {
104 if (channel as usize) < 8 {
105 Ok(self.adc.borrow()[channel as usize])
106 } else {
107 Err(format!("adc channel {} out of range (0..7)", channel))
108 }
109 }
110}
111
112thread_local! {
113 static HAL: RefCell<SimulatedHal> = RefCell::new(SimulatedHal::new());
114}
115
116pub fn builtin_gpio_write(args: &[Value]) -> Result<Value, String> {
118 let pin = match args.first() {
119 Some(Value::Int(n)) if *n >= 0 && *n <= 255 => *n as u8,
120 _ => return Err("gpio_write(pin, high): pin must be int 0..255".into()),
121 };
122 let high = match args.get(1) {
123 Some(Value::Bool(b)) => *b,
124 Some(Value::Int(n)) => *n != 0,
125 _ => return Err("gpio_write(pin, high): high must be bool or int".into()),
126 };
127 HAL.with(|h| {
128 let mut h = h.borrow_mut();
129 h.set_mode_output(pin);
130 h.write(pin, high)
131 })?;
132 Ok(Value::Null)
133}
134
135pub fn builtin_gpio_read(args: &[Value]) -> Result<Value, String> {
137 let pin = match args.first() {
138 Some(Value::Int(n)) if *n >= 0 && *n <= 255 => *n as u8,
139 _ => return Err("gpio_read(pin): pin must be int 0..255".into()),
140 };
141 let v = HAL.with(|h| h.borrow().read(pin))?;
142 Ok(Value::Bool(v))
143}
144
145pub fn builtin_delay_ms(args: &[Value]) -> Result<Value, String> {
147 let ms = match args.first() {
148 Some(Value::Int(n)) if *n >= 0 => *n as u32,
149 _ => return Err("delay_ms(ms): ms must be non-negative int".into()),
150 };
151 HAL.with(|h| h.borrow_mut().delay_ms(ms));
152 Ok(Value::Null)
153}
154
155pub fn builtin_adc_read(args: &[Value]) -> Result<Value, String> {
157 let ch = match args.first() {
158 Some(Value::Int(n)) if *n >= 0 && *n <= 255 => *n as u8,
159 _ => return Err("adc_read(channel): channel must be int 0..255".into()),
160 };
161 let v = HAL.with(|h| h.borrow_mut().read_raw(ch))?;
162 Ok(Value::Int(v as i64))
163}
164
165pub fn sim_set_adc(channel: u8, raw: u16) {
167 HAL.with(|h| h.borrow_mut().set_adc(channel, raw));
168}
169
170pub fn sim_drive_pin(pin: Pin, high: bool) {
172 HAL.with(|h| h.borrow_mut().drive_pin(pin, high));
173}
174
175pub fn sim_elapsed_ms() -> u64 {
177 HAL.with(|h| h.borrow().elapsed_ms())
178}