Krolyxon
Commit 6ffb407

Branches

Tags

Refs →

bench51cratesbench51-coresrcsimperiph.rs

403 lines · 11.7 KB · Rust

1//! Timers 0/1, the UART and interrupt dispatch, advanced per machine cycle.
2
3use std::collections::VecDeque;
4
5use super::Sim;
6use crate::sfr::{IE, IP, PCON, SCON, TCON, TH0, TH1, TL0, TL1, TMOD};
7
8// TCON bits
9const TF1: u8 = 0x80;
10const TR1: u8 = 0x40;
11const TF0: u8 = 0x20;
12const TR0: u8 = 0x10;
13const IE1: u8 = 0x08;
14const IT1: u8 = 0x04;
15const IE0: u8 = 0x02;
16const IT0: u8 = 0x01;
17// SCON bits
18const SM2: u8 = 0x20;
19const REN: u8 = 0x10;
20const RB8: u8 = 0x04;
21const TI: u8 = 0x02;
22const RI: u8 = 0x01;
23// P3 pins
24const INT0_PIN: u8 = 0x04;
25const INT1_PIN: u8 = 0x08;
26const T0_PIN: u8 = 0x10;
27const T1_PIN: u8 = 0x20;
28
29#[derive(Debug, Clone, Copy)]
30struct Shift {
31 byte: u8,
32 /// Remaining time in the mode's unit (see `Sim::uart_units`).
33 remaining: u32,
34}
35
36#[derive(Debug, Default)]
37pub struct Uart {
38 /// Everything the 8051 has transmitted.
39 pub tx_log: Vec<u8>,
40 /// Bytes waiting to be received by the 8051.
41 pub rx_queue: VecDeque<u8>,
42 /// The receive half of SBUF.
43 pub sbuf_rx: u8,
44 tx: Option<Shift>,
45 rx: Option<Shift>,
46}
47
48impl Uart {
49 /// Clears the shift registers and the TX log; queued input is kept.
50 pub fn reset(&mut self) {
51 self.tx_log.clear();
52 self.sbuf_rx = 0;
53 self.tx = None;
54 self.rx = None;
55 }
56
57 pub fn transmitting(&self) -> bool {
58 self.tx.is_some()
59 }
60}
61
62/// Interrupt sources in hardware polling order: (flag test, IE/IP bit, vector).
63const SOURCES: [(u8, u16); 5] = [
64 (0x01, 0x03),
65 (0x02, 0x0B),
66 (0x04, 0x13),
67 (0x08, 0x1B),
68 (0x10, 0x23),
69];
70
71impl Sim {
72 pub(super) fn tick(&mut self, cycles: u32) {
73 for _ in 0..cycles {
74 self.cycles += 1;
75 let p3 = self.port_pins(3);
76 let fell = self.prev_p3 & !p3;
77 self.prev_p3 = p3;
78 self.external_interrupts(p3, fell);
79 let t1_overflows = self.timers(p3, fell);
80 self.uart_tick(t1_overflows);
81 }
82 }
83
84 fn external_interrupts(&mut self, p3: u8, fell: u8) {
85 let mut tcon = self.sfr(TCON);
86 for (pin, it, ie) in [(INT0_PIN, IT0, IE0), (INT1_PIN, IT1, IE1)] {
87 if tcon & it != 0 {
88 if fell & pin != 0 {
89 tcon |= ie;
90 }
91 } else if p3 & pin == 0 {
92 tcon |= ie;
93 } else {
94 tcon &= !ie;
95 }
96 }
97 self.set_sfr(TCON, tcon);
98 }
99
100 /// Advances both timers by one machine cycle. Returns the number of
101 /// timer 1 overflows (the UART baud clock in modes 1 and 3).
102 fn timers(&mut self, p3: u8, fell: u8) -> u32 {
103 let tmod = self.sfr(TMOD);
104 let mut tcon = self.sfr(TCON);
105 let t0_mode = tmod & 0x03;
106 let mut t1_overflows = 0;
107
108 for t in 0..2u8 {
109 let ctl = (tmod >> (4 * t)) & 0x0F;
110 let mode = ctl & 0x03;
111 let (tr, int_pin, count_pin) = if t == 0 {
112 (TR0, INT0_PIN, T0_PIN)
113 } else {
114 (TR1, INT1_PIN, T1_PIN)
115 };
116 let gate_ok = ctl & 0x08 == 0 || p3 & int_pin != 0;
117 let running = tcon & tr != 0 && gate_ok;
118 let event = if ctl & 0x04 != 0 {
119 fell & count_pin != 0
120 } else {
121 true
122 };
123
124 // Timer 1 is stopped in mode 3; timer 0 mode 3 also clocks TH0 from TR1.
125 if t == 1 && mode == 3 {
126 continue;
127 }
128 let (tl_addr, th_addr) = if t == 0 { (TL0, TH0) } else { (TL1, TH1) };
129 if t == 0 && mode == 3 && tcon & TR1 != 0 {
130 let th = self.sfr(TH0).wrapping_add(1);
131 self.set_sfr(TH0, th);
132 if th == 0 {
133 tcon |= TF1;
134 }
135 }
136 if !(running && event) {
137 continue;
138 }
139 let (tl, th) = (self.sfr(tl_addr), self.sfr(th_addr));
140 let (tl, th, overflow) = match mode {
141 0 => {
142 let low = (tl.wrapping_add(1)) & 0x1F;
143 let th2 = if low == 0 { th.wrapping_add(1) } else { th };
144 ((tl & 0xE0) | low, th2, low == 0 && th2 == 0)
145 }
146 1 => {
147 let v = u16::from_be_bytes([th, tl]).wrapping_add(1);
148 let [h, l] = v.to_be_bytes();
149 (l, h, v == 0)
150 }
151 2 => {
152 let l = tl.wrapping_add(1);
153 if l == 0 {
154 (th, th, true)
155 } else {
156 (l, th, false)
157 }
158 }
159 _ => {
160 // Timer 0 mode 3: TL0 is an 8-bit timer.
161 let l = tl.wrapping_add(1);
162 (l, th, l == 0)
163 }
164 };
165 self.set_sfr(tl_addr, tl);
166 self.set_sfr(th_addr, th);
167 if overflow {
168 if t == 1 {
169 t1_overflows += 1;
170 // With timer 0 in mode 3, TF1 belongs to TH0.
171 if t0_mode != 3 {
172 tcon |= TF1;
173 }
174 } else {
175 tcon |= TF0;
176 }
177 }
178 }
179 self.set_sfr(TCON, tcon);
180 t1_overflows
181 }
182
183 /// Frame length of the current serial mode, in the units that
184 /// `uart_tick` counts: machine cycles (mode 0), oscillator periods
185 /// (mode 2) or timer 1 overflows (modes 1 and 3).
186 fn uart_frame(&self) -> u32 {
187 let smod = self.sfr(PCON) & 0x80 != 0;
188 let bit = |base: u32| if smod { base / 2 } else { base };
189 match self.sfr(SCON) >> 6 {
190 0 => 8,
191 1 => 10 * bit(32),
192 2 => 11 * bit(64),
193 _ => 11 * bit(32),
194 }
195 }
196
197 pub(super) fn uart_transmit(&mut self, byte: u8) {
198 let remaining = self.uart_frame();
199 self.uart.tx = Some(Shift { byte, remaining });
200 }
201
202 fn uart_tick(&mut self, t1_overflows: u32) {
203 let mode = self.sfr(SCON) >> 6;
204 let units = match mode {
205 0 => 1,
206 2 => 12,
207 _ => t1_overflows,
208 };
209 if units == 0 {
210 return;
211 }
212 if let Some(tx) = &mut self.uart.tx {
213 tx.remaining = tx.remaining.saturating_sub(units);
214 if tx.remaining == 0 {
215 self.uart.tx_log.push(tx.byte);
216 self.uart.tx = None;
217 self.set_sfr(SCON, self.sfr(SCON) | TI);
218 }
219 }
220
221 let scon = self.sfr(SCON);
222 if self.uart.rx.is_none()
223 && scon & REN != 0
224 && scon & RI == 0
225 && let Some(byte) = self.uart.rx_queue.pop_front()
226 {
227 self.uart.rx = Some(Shift {
228 byte,
229 remaining: self.uart_frame(),
230 });
231 }
232 if let Some(rx) = &mut self.uart.rx {
233 rx.remaining = rx.remaining.saturating_sub(units);
234 if rx.remaining == 0 {
235 let byte = rx.byte;
236 self.uart.rx = None;
237 // Terminal input has no 9th data bit; in modes 1-3 RB8 gets
238 // the stop bit (1). With SM2 set in modes 2/3 that 9th bit
239 // would be 0, so the byte is discarded.
240 let mut scon = self.sfr(SCON);
241 if !(mode >= 2 && scon & SM2 != 0) {
242 self.uart.sbuf_rx = byte;
243 scon |= RI;
244 if mode == 1 {
245 scon |= RB8;
246 } else if mode >= 2 {
247 scon &= !RB8;
248 }
249 }
250 self.set_sfr(SCON, scon);
251 }
252 }
253 }
254
255 pub(super) fn service_interrupts(&mut self) {
256 if self.irq_hold {
257 self.irq_hold = false;
258 return;
259 }
260 let ie = self.sfr(IE);
261 if ie & 0x80 == 0 {
262 return;
263 }
264 let tcon = self.sfr(TCON);
265 let scon = self.sfr(SCON);
266 let pending = |mask: u8| match mask {
267 0x01 => tcon & IE0 != 0,
268 0x02 => tcon & TF0 != 0,
269 0x04 => tcon & IE1 != 0,
270 0x08 => tcon & TF1 != 0,
271 _ => scon & (RI | TI) != 0,
272 };
273 let ip = self.sfr(IP);
274 let current = self.in_service.last().copied();
275 for level in [1u8, 0] {
276 if current.is_some_and(|c| c >= level) {
277 continue;
278 }
279 let found = SOURCES.iter().find(|&&(mask, _)| {
280 ie & mask != 0 && (ip & mask != 0) == (level == 1) && pending(mask)
281 });
282 if let Some(&(mask, vector)) = found {
283 // Hardware clears timer flags and edge-triggered external flags.
284 let mut tcon = self.sfr(TCON);
285 match mask {
286 0x01 if tcon & IT0 != 0 => tcon &= !IE0,
287 0x02 => tcon &= !TF0,
288 0x04 if tcon & IT1 != 0 => tcon &= !IE1,
289 0x08 => tcon &= !TF1,
290 _ => {}
291 }
292 self.set_sfr(TCON, tcon);
293 self.idle = false;
294 self.set_sfr(PCON, self.sfr(PCON) & !0x01);
295 self.push_pc();
296 self.pc = vector;
297 self.in_service.push(level);
298 self.last_irq = Some(vector);
299 self.tick(2);
300 return;
301 }
302 }
303 }
304}
305
306#[cfg(test)]
307mod tests {
308 use crate::asm::assemble;
309 use crate::sim::Sim;
310
311 fn sim(src: &str) -> Sim {
312 let out = assemble(src);
313 assert!(out.ok(), "{:?}", out.diagnostics);
314 let mut s = Sim::new();
315 s.load_code(out.bytes());
316 s
317 }
318
319 #[test]
320 fn timer0_mode1_interrupt() {
321 let mut s = sim("
322 ORG 0
323 LJMP main
324 ORG 0Bh
325 INC R7
326 RETI
327main: MOV TMOD,#01h
328 MOV TH0,#0FFh
329 MOV TL0,#0F0h
330 SETB ET0
331 SETB EA
332 SETB TR0
333 SJMP $
334");
335 let mut saw_vector = false;
336 for _ in 0..200 {
337 s.step().unwrap();
338 saw_vector |= s.pc == 0x0B;
339 }
340 assert!(saw_vector);
341 assert!(s.reg(7) >= 1);
342 // TF0 is cleared by hardware on vectoring.
343 assert_eq!(s.sfr(crate::sfr::TCON) & 0x20, 0);
344 }
345
346 #[test]
347 fn timer_mode2_auto_reload() {
348 let mut s = sim("MOV TMOD,#02h\nMOV TH0,#0F0h\nMOV TL0,#0FEh\nSETB TR0\nSJMP $");
349 for _ in 0..8 {
350 s.step().unwrap();
351 }
352 assert!(s.sfr(crate::sfr::TCON) & 0x20 != 0, "TF0 set");
353 assert!(s.sfr(crate::sfr::TL0) >= 0xF0);
354 }
355
356 #[test]
357 fn uart_tx_and_rx() {
358 let mut s = sim("
359 MOV TMOD,#20h ; timer 1 mode 2
360 MOV TH1,#0FDh ; 9600 baud @ 11.0592 MHz
361 SETB TR1
362 MOV SCON,#50h ; mode 1, REN
363 MOV SBUF,#'H'
364w: JNB TI,w
365 CLR TI
366r: JNB RI,r
367 MOV A,SBUF
368 SJMP $
369");
370 s.uart_send(b"x");
371 for _ in 0..10_000 {
372 s.step().unwrap();
373 }
374 assert_eq!(s.uart.tx_log, b"H");
375 assert_eq!(s.acc(), b'x');
376 }
377
378 #[test]
379 fn external_interrupt_edge() {
380 let mut s = sim("
381 LJMP main
382 ORG 03h
383 INC R0
384 RETI
385main: SETB IT0
386 SETB EX0
387 SETB EA
388 SJMP $
389");
390 for _ in 0..10 {
391 s.step().unwrap();
392 }
393 s.pins[3] &= !0x04; // pull INT0 low
394 for _ in 0..10 {
395 s.step().unwrap();
396 }
397 s.pins[3] |= 0x04;
398 for _ in 0..10 {
399 s.step().unwrap();
400 }
401 assert_eq!(s.reg(0), 1, "one falling edge, one interrupt");
402 }
403}