Krolyxon
Commit 6efefdd

Branches

Tags

Refs →

bench51cratesbench51-coresrcsimcpu.rs

528 lines · 16.7 KB · Rust

1//! Instruction execution.
2
3use super::{AC, CY, OV, Sim};
4use crate::sfr::{B, P2};
5
6/// Operand location for the register-indexed columns (opcode low nibble 5..F).
7#[derive(Clone, Copy)]
8enum Loc {
9 Direct(u8),
10 Iram(u8),
11}
12
13impl Sim {
14 fn fetch(&mut self) -> u8 {
15 let b = self.code[self.pc as usize];
16 self.pc = self.pc.wrapping_add(1);
17 b
18 }
19
20 fn fetch16(&mut self) -> u16 {
21 let h = self.fetch();
22 let l = self.fetch();
23 u16::from_be_bytes([h, l])
24 }
25
26 fn rel_jump(&mut self, off: u8) {
27 self.pc = self.pc.wrapping_add(off as i8 as u16);
28 }
29
30 /// Decodes the operand selected by the low nibble: 5 = direct,
31 /// 6/7 = @R0/@R1, 8..F = R0..R7.
32 fn loc(&mut self, op: u8) -> Loc {
33 match op & 0x0F {
34 0x05 => Loc::Direct(self.fetch()),
35 0x06 | 0x07 => Loc::Iram(self.reg(op & 1)),
36 n => Loc::Iram(self.reg_addr(n & 7)),
37 }
38 }
39
40 fn read_loc(&self, l: Loc, rmw: bool) -> u8 {
41 match l {
42 Loc::Direct(a) => self.read_direct(a, rmw),
43 Loc::Iram(a) => self.iram[a as usize],
44 }
45 }
46
47 fn write_loc(&mut self, l: Loc, v: u8) {
48 match l {
49 Loc::Direct(a) => self.write_direct(a, v),
50 Loc::Iram(a) => self.iram[a as usize] = v,
51 }
52 }
53
54 /// Source operand for the arithmetic/logic rows: 4 = #imm, otherwise `loc`.
55 fn src(&mut self, op: u8) -> u8 {
56 if op & 0x0F == 0x04 {
57 self.fetch()
58 } else {
59 let l = self.loc(op);
60 self.read_loc(l, false)
61 }
62 }
63
64 fn add(&mut self, v: u8, carry: bool) {
65 let a = self.acc();
66 let c = carry as u16;
67 let sum = a as u16 + v as u16 + c;
68 let r = sum as u8;
69 self.set_flag(CY, sum > 0xFF);
70 self.set_flag(AC, (a & 0x0F) as u16 + (v & 0x0F) as u16 + c > 0x0F);
71 self.set_flag(OV, (a ^ r) & (v ^ r) & 0x80 != 0);
72 self.set_acc(r);
73 }
74
75 fn subb(&mut self, v: u8) {
76 let a = self.acc();
77 let c = self.flag(CY) as i16;
78 let diff = a as i16 - v as i16 - c;
79 let r = diff as u8;
80 self.set_flag(CY, diff < 0);
81 self.set_flag(AC, ((a & 0x0F) as i16) < (v & 0x0F) as i16 + c);
82 self.set_flag(OV, (a ^ v) & (a ^ r) & 0x80 != 0);
83 self.set_acc(r);
84 }
85
86 fn cjne(&mut self, a: u8, b: u8) {
87 let off = self.fetch();
88 self.set_flag(CY, a < b);
89 if a != b {
90 self.rel_jump(off);
91 }
92 }
93
94 fn movx_addr_ri(&self, op: u8) -> u16 {
95 // The high address byte comes from the P2 latch.
96 u16::from_be_bytes([self.sfr(P2), self.reg(op & 1)])
97 }
98
99 pub(super) fn execute(&mut self, op: u8) {
100 match op {
101 0x00 => {}
102
103 // AJMP / ACALL addr11
104 _ if op & 0x1F == 0x01 || op & 0x1F == 0x11 => {
105 let lo = self.fetch();
106 let target = (self.pc & 0xF800) | ((op as u16 >> 5) << 8) | lo as u16;
107 if op & 0x10 != 0 {
108 self.push_pc();
109 }
110 self.pc = target;
111 }
112 0x02 => self.pc = self.fetch16(),
113 0x12 => {
114 let target = self.fetch16();
115 self.push_pc();
116 self.pc = target;
117 }
118 0x22 => self.pop_pc(),
119 0x32 => {
120 self.pop_pc();
121 self.in_service.pop();
122 self.irq_hold = true;
123 }
124 0x73 => self.pc = self.dptr().wrapping_add(self.acc() as u16),
125 0x80 => {
126 let off = self.fetch();
127 self.rel_jump(off);
128 }
129
130 // Rotates
131 0x03 => self.set_acc(self.acc().rotate_right(1)),
132 0x23 => self.set_acc(self.acc().rotate_left(1)),
133 0x13 => {
134 let a = self.acc();
135 self.set_acc((a >> 1) | ((self.flag(CY) as u8) << 7));
136 self.set_flag(CY, a & 1 != 0);
137 }
138 0x33 => {
139 let a = self.acc();
140 self.set_acc((a << 1) | self.flag(CY) as u8);
141 self.set_flag(CY, a & 0x80 != 0);
142 }
143
144 // INC / DEC
145 0x04 => self.set_acc(self.acc().wrapping_add(1)),
146 0x14 => self.set_acc(self.acc().wrapping_sub(1)),
147 0x05..=0x0F | 0x15..=0x1F => {
148 let l = self.loc(op);
149 let v = self.read_loc(l, true);
150 self.write_loc(
151 l,
152 if op < 0x10 {
153 v.wrapping_add(1)
154 } else {
155 v.wrapping_sub(1)
156 },
157 );
158 }
159 0xA3 => self.set_dptr(self.dptr().wrapping_add(1)),
160
161 // Conditional jumps on bits
162 0x10 | 0x20 | 0x30 => {
163 let bit = self.fetch();
164 let off = self.fetch();
165 let set = self.read_bit(bit, op == 0x10);
166 if op == 0x10 && set {
167 self.write_bit(bit, false);
168 }
169 if set == (op != 0x30) {
170 self.rel_jump(off);
171 }
172 }
173 0x40 | 0x50 | 0x60 | 0x70 => {
174 let off = self.fetch();
175 let take = match op {
176 0x40 => self.flag(CY),
177 0x50 => !self.flag(CY),
178 0x60 => self.acc() == 0,
179 _ => self.acc() != 0,
180 };
181 if take {
182 self.rel_jump(off);
183 }
184 }
185
186 // Arithmetic
187 0x24..=0x2F => {
188 let v = self.src(op);
189 self.add(v, false);
190 }
191 0x34..=0x3F => {
192 let v = self.src(op);
193 self.add(v, self.flag(CY));
194 }
195 0x94..=0x9F => {
196 let v = self.src(op);
197 self.subb(v);
198 }
199 0x84 => {
200 let (a, b) = (self.acc(), self.b());
201 self.set_flag(CY, false);
202 // Division by zero sets OV and leaves A and B unchanged.
203 match a.checked_div(b) {
204 Some(q) => {
205 self.set_acc(q);
206 self.set_sfr(B, a % b);
207 self.set_flag(OV, false);
208 }
209 None => self.set_flag(OV, true),
210 }
211 }
212 0xA4 => {
213 let r = self.acc() as u16 * self.b() as u16;
214 self.set_acc(r as u8);
215 self.set_sfr(B, (r >> 8) as u8);
216 self.set_flag(CY, false);
217 self.set_flag(OV, r > 0xFF);
218 }
219 0xD4 => {
220 let mut a = self.acc() as u16;
221 if a & 0x0F > 9 || self.flag(AC) {
222 a += 0x06;
223 }
224 if a > 0xFF {
225 self.set_flag(CY, true);
226 }
227 if (a >> 4) & 0x1F > 9 || self.flag(CY) {
228 a += 0x60;
229 }
230 if a > 0xFF {
231 self.set_flag(CY, true);
232 }
233 self.set_acc(a as u8);
234 }
235
236 // Logic: ORL/ANL/XRL direct,A / direct,#imm / A,src
237 0x42 | 0x43 | 0x52 | 0x53 | 0x62 | 0x63 => {
238 let addr = self.fetch();
239 let v = if op & 1 == 1 {
240 self.fetch()
241 } else {
242 self.acc()
243 };
244 let d = self.read_direct(addr, true);
245 self.write_direct(addr, logic(op, d, v));
246 }
247 0x44..=0x4F | 0x54..=0x5F | 0x64..=0x6F => {
248 let v = self.src(op);
249 self.set_acc(logic(op, self.acc(), v));
250 }
251 0xC4 => self.set_acc(self.acc().rotate_left(4)),
252 0xE4 => self.set_acc(0),
253 0xF4 => self.set_acc(!self.acc()),
254
255 // Boolean (carry) operations
256 0x72 | 0x82 | 0xA0 | 0xB0 => {
257 let bit = self.fetch();
258 let v = self.read_bit(bit, false) != (op >= 0xA0);
259 let c = self.flag(CY);
260 self.set_flag(CY, if op & 0x10 == 0 { c || v } else { c && v });
261 }
262 0x92 => {
263 let bit = self.fetch();
264 self.write_bit(bit, self.flag(CY));
265 }
266 0xA2 => {
267 let bit = self.fetch();
268 self.set_flag(CY, self.read_bit(bit, false));
269 }
270 0xB2 | 0xC2 | 0xD2 => {
271 let bit = self.fetch();
272 let v = match op {
273 0xB2 => !self.read_bit(bit, true),
274 0xC2 => false,
275 _ => true,
276 };
277 self.write_bit(bit, v);
278 }
279 0xB3 => self.set_flag(CY, !self.flag(CY)),
280 0xC3 => self.set_flag(CY, false),
281 0xD3 => self.set_flag(CY, true),
282
283 // Data transfer
284 0x74 => {
285 let v = self.fetch();
286 self.set_acc(v);
287 }
288 0x75 => {
289 let addr = self.fetch();
290 let v = self.fetch();
291 self.write_direct(addr, v);
292 }
293 0x76..=0x7F => {
294 let l = self.loc(op);
295 let v = self.fetch();
296 self.write_loc(l, v);
297 }
298 0x85 => {
299 let src = self.fetch();
300 let dst = self.fetch();
301 let v = self.read_direct(src, false);
302 self.write_direct(dst, v);
303 }
304 0x86..=0x8F => {
305 let l = self.loc(op);
306 let addr = self.fetch();
307 let v = self.read_loc(l, false);
308 self.write_direct(addr, v);
309 }
310 0xA6..=0xAF => {
311 let l = self.loc(op);
312 let addr = self.fetch();
313 let v = self.read_direct(addr, false);
314 self.write_loc(l, v);
315 }
316 0xE5..=0xEF => {
317 let v = self.src(op);
318 self.set_acc(v);
319 }
320 0xF5..=0xFF => {
321 let l = self.loc(op);
322 self.write_loc(l, self.acc());
323 }
324 0x90 => {
325 let v = self.fetch16();
326 self.set_dptr(v);
327 }
328 0x83 => self.set_acc(self.code[self.pc.wrapping_add(self.acc() as u16) as usize]),
329 0x93 => self.set_acc(self.code[self.dptr().wrapping_add(self.acc() as u16) as usize]),
330 0xE0 => self.set_acc(self.xram[self.dptr() as usize]),
331 0xF0 => self.xram[self.dptr() as usize] = self.acc(),
332 0xE2 | 0xE3 => self.set_acc(self.xram[self.movx_addr_ri(op) as usize]),
333 0xF2 | 0xF3 => self.xram[self.movx_addr_ri(op) as usize] = self.acc(),
334 0xC0 => {
335 let addr = self.fetch();
336 let v = self.read_direct(addr, false);
337 self.push(v);
338 }
339 0xD0 => {
340 let addr = self.fetch();
341 let v = self.pop();
342 self.write_direct(addr, v);
343 }
344 0xC5..=0xCF => {
345 let l = self.loc(op);
346 let v = self.read_loc(l, true);
347 self.write_loc(l, self.acc());
348 self.set_acc(v);
349 }
350 0xD6 | 0xD7 => {
351 let addr = self.reg(op & 1) as usize;
352 let (a, m) = (self.acc(), self.iram[addr]);
353 self.set_acc((a & 0xF0) | (m & 0x0F));
354 self.iram[addr] = (m & 0xF0) | (a & 0x0F);
355 }
356
357 // Compare and loop
358 0xB4 => {
359 let v = self.fetch();
360 self.cjne(self.acc(), v);
361 }
362 0xB5 => {
363 let addr = self.fetch();
364 let v = self.read_direct(addr, false);
365 self.cjne(self.acc(), v);
366 }
367 0xB6..=0xBF => {
368 let l = self.loc(op);
369 let a = self.read_loc(l, false);
370 let v = self.fetch();
371 self.cjne(a, v);
372 }
373 0xD5 | 0xD8..=0xDF => {
374 let l = self.loc(op);
375 let v = self.read_loc(l, true).wrapping_sub(1);
376 self.write_loc(l, v);
377 let off = self.fetch();
378 if v != 0 {
379 self.rel_jump(off);
380 }
381 }
382
383 // 0xA5 is reserved and rejected before `execute` is called.
384 _ => unreachable!("unhandled opcode {op:02X}"),
385 }
386 }
387}
388
389fn logic(op: u8, a: u8, b: u8) -> u8 {
390 match op & 0xF0 {
391 0x40 => a | b,
392 0x50 => a & b,
393 _ => a ^ b,
394 }
395}
396
397#[cfg(test)]
398mod tests {
399 use crate::asm::assemble;
400 use crate::sim::{AC, CY, OV, PARITY, Sim};
401 use std::collections::HashSet;
402
403 /// Assembles `src`, then steps until the PC reaches the `done` label.
404 fn run(src: &str) -> Sim {
405 let src = format!("{src}\ndone: SJMP done\n");
406 let out = assemble(&src);
407 assert!(out.ok(), "{:?}", out.diagnostics);
408 let done = out.symbols["DONE"].value as u16;
409 let mut s = Sim::new();
410 s.load_code(out.bytes());
411 for _ in 0..100_000 {
412 if s.pc == done {
413 return s;
414 }
415 s.step().unwrap();
416 }
417 panic!("program did not reach 'done'");
418 }
419
420 #[test]
421 fn add_flags() {
422 let s = run("MOV A,#0x7F\nADD A,#1");
423 assert_eq!(s.acc(), 0x80);
424 assert!(s.flag(OV) && s.flag(AC) && !s.flag(CY));
425 assert!(s.flag(PARITY));
426 let s = run("MOV A,#0xFF\nSETB C\nADDC A,#0");
427 assert_eq!(s.acc(), 0);
428 assert!(s.flag(CY) && s.flag(AC) && !s.flag(OV));
429 }
430
431 #[test]
432 fn subb_flags() {
433 let s = run("CLR C\nMOV A,#0x80\nSUBB A,#1");
434 assert_eq!(s.acc(), 0x7F);
435 assert!(s.flag(OV) && s.flag(AC) && !s.flag(CY));
436 let s = run("SETB C\nMOV A,#5\nSUBB A,#5");
437 assert_eq!(s.acc(), 0xFF);
438 assert!(s.flag(CY));
439 }
440
441 #[test]
442 fn decimal_adjust() {
443 let s = run("MOV A,#0x56\nADD A,#0x67\nDA A");
444 assert_eq!(s.acc(), 0x23);
445 assert!(s.flag(CY));
446 let s = run("MOV A,#0x19\nADD A,#0x28\nDA A");
447 assert_eq!(s.acc(), 0x47);
448 assert!(!s.flag(CY));
449 }
450
451 #[test]
452 fn mul_div() {
453 let s = run("MOV A,#80\nMOV B,#160\nMUL AB");
454 assert_eq!((s.acc(), s.b()), (0x00, 0x32));
455 assert!(s.flag(OV));
456 let s = run("MOV A,#251\nMOV B,#18\nDIV AB");
457 assert_eq!((s.acc(), s.b()), (13, 17));
458 let s = run("MOV B,#0\nDIV AB");
459 assert!(s.flag(OV));
460 }
461
462 #[test]
463 fn banks_and_indirect() {
464 let s = run("MOV R0,#1\nSETB RS0\nMOV R0,#2\nMOV R1,#40h\nMOV @R1,#99\nMOV A,40h");
465 assert_eq!(s.iram[0], 1);
466 assert_eq!(s.iram[8], 2);
467 assert_eq!(s.acc(), 99);
468 assert_eq!(s.bank(), 1);
469 }
470
471 #[test]
472 fn stack_and_calls() {
473 let s = run("MOV A,#5\nPUSH ACC\nMOV A,#0\nPOP B\nCALL sub\nSJMP done\nsub: INC A\nRET");
474 assert_eq!(s.b(), 5);
475 assert_eq!(s.acc(), 1);
476 assert_eq!(s.sp(), 7);
477 }
478
479 #[test]
480 fn loops_and_compares() {
481 let s = run("MOV R2,#10\nCLR A\nl: ADD A,#2\nDJNZ R2,l");
482 assert_eq!(s.acc(), 20);
483 let s = run("MOV A,#3\nCJNE A,#5,ne\nne: NOP");
484 assert!(s.flag(CY));
485 }
486
487 #[test]
488 fn bits() {
489 let s = run("SETB 20h.3\nMOV C,20h.3\nCPL ACC.0\nANL C,/ACC.0\nMOV 21h.0,C");
490 assert_eq!(s.iram[0x20], 0x08);
491 assert_eq!(s.acc(), 1);
492 assert_eq!(s.iram[0x21], 0);
493 let s = run("SETB 20h.1\nJBC 20h.1,x\nx: NOP");
494 assert_eq!(s.iram[0x20], 0);
495 }
496
497 #[test]
498 fn movc_movx_xchd() {
499 let s = run(
500 "MOV DPTR,#tbl\nMOV A,#2\nMOVC A,@A+DPTR\nSJMP go\ntbl: DB 10,20,30\ngo: MOV DPTR,#1234h\nMOVX @DPTR,A\nMOV R0,#30h\nMOV @R0,#0x5A\nMOV A,#0xC3\nXCHD A,@R0",
501 );
502 assert_eq!(s.xram[0x1234], 30);
503 assert_eq!(s.acc(), 0xCA);
504 assert_eq!(s.iram[0x30], 0x53);
505 }
506
507 #[test]
508 fn ports_read_pins_but_rmw_reads_latch() {
509 let mut s = run("MOV P1,#0FFh");
510 s.pins[1] = 0xF0;
511 assert_eq!(s.read_direct(0x90, false), 0xF0);
512 assert_eq!(s.read_direct(0x90, true), 0xFF);
513 }
514
515 #[test]
516 fn step_over_and_out() {
517 let out = assemble("CALL f\nNOP\nSJMP $\nf: MOV A,#1\nMOV A,#2\nRET");
518 let mut s = Sim::new();
519 s.load_code(out.bytes());
520 let none = HashSet::new();
521 s.step_over(1000, &none);
522 assert_eq!((s.pc, s.acc()), (3, 2));
523 s.reset();
524 s.step().unwrap(); // into f
525 s.step_out(1000, &none);
526 assert_eq!(s.pc, 3);
527 }
528}