1#![cfg_attr(not(test), no_std)]
3#![warn(missing_docs)]
4#![cfg_attr(not(any(test, feature = "native")), forbid(unsafe_code))]
5#![cfg_attr(all(nightly, feature = "tailcall"), feature(explicit_tail_calls))]
7#![cfg_attr(all(nightly, feature = "tailcall"), feature(rust_preserve_none_cc))]
8#![cfg_attr(all(nightly, feature = "tailcall"), expect(incomplete_features))]
9#[cfg(all(not(nightly), feature = "tailcall"))]
10compile_error!("the `tailcall` feature requires a nightly compiler");
11
12#[cfg(feature = "alloc")]
13extern crate alloc;
14
15#[cfg(feature = "native")]
16mod native;
17
18#[cfg(all(nightly, feature = "tailcall"))]
19mod tailcall;
20
21#[cfg(all(not(nightly), feature = "tailcall"))]
23mod tailcall {
24 use super::*;
25 pub fn entry<'a>(
26 _core: UxnCore<'a>,
27 _dev: &mut dyn Device,
28 _pc: u16,
29 ) -> (UxnCore<'a>, u16) {
30 unimplemented!()
31 }
32}
33
34const fn keep(flags: u8) -> bool {
35 (flags & (1 << 2)) != 0
36}
37const fn short(flags: u8) -> bool {
38 (flags & (1 << 0)) != 0
39}
40const fn ret(flags: u8) -> bool {
41 (flags & (1 << 1)) != 0
42}
43
44pub const DEV_SIZE: usize = 16;
46
47#[derive(Debug, Eq, PartialEq)]
49pub struct Stack<'a> {
50 data: &'a mut [u8; 256],
51
52 index: u8,
56}
57
58impl<'a> Stack<'a> {
59 fn new(data: &'a mut [u8; 256]) -> Self {
60 Self {
61 data,
62 index: u8::MAX,
63 }
64 }
65
66 fn reset(&mut self) {
67 self.data.fill(0u8);
68 self.index = u8::MAX;
69 }
70}
71
72pub trait Backend: Sized {
74 fn run<D: Device>(vm: &mut Uxn<Self>, dev: &mut D, pc: u16) -> u16;
76}
77
78pub mod backend {
83 use super::*;
84
85 pub struct Interpreter;
87 impl crate::Backend for Interpreter {
88 fn run<D: Device>(vm: &mut Uxn<Self>, dev: &mut D, mut pc: u16) -> u16 {
89 let core: &mut UxnCore = &mut *vm;
90 loop {
91 let op = core.next(&mut pc);
92 let Some(next) = core.op(op, dev, pc) else {
93 break pc;
94 };
95 pc = next;
96 }
97 }
98 }
99
100 #[cfg(feature = "native")]
101 pub struct Native;
103
104 #[cfg(feature = "native")]
105 impl crate::Backend for Native {
106 fn run<D: Device>(vm: &mut Uxn<Self>, dev: &mut D, pc: u16) -> u16 {
107 native::entry(vm, dev, pc)
108 }
109 }
110
111 #[cfg(feature = "tailcall")]
112 pub struct Tailcall;
114
115 #[cfg(feature = "tailcall")]
116 impl crate::Backend for Tailcall {
117 fn run<D: Device>(vm: &mut Uxn<Self>, dev: &mut D, pc: u16) -> u16 {
118 let core = vm.core.take().unwrap();
119 let (core, pc) = tailcall::entry(core, dev, pc);
120 vm.core = Some(core);
121 pc
122 }
123 }
124}
125
126struct StackView<'a, 'b, const FLAGS: u8> {
132 stack: &'a mut Stack<'b>,
133
134 offset: u8,
136}
137
138impl<'a, 'b, const FLAGS: u8> StackView<'a, 'b, FLAGS> {
139 #[inline(always)]
140 fn new(stack: &'a mut Stack<'b>) -> Self {
141 Self { stack, offset: 0 }
142 }
143
144 #[inline(always)]
152 fn pop(&mut self) -> Value {
153 if short(FLAGS) {
154 Value::Short(self.pop_short())
155 } else {
156 Value::Byte(self.pop_byte())
157 }
158 }
159
160 #[inline(always)]
161 fn pop_byte(&mut self) -> u8 {
162 if keep(FLAGS) {
163 let v = self.stack.peek_byte_at(self.offset);
164 self.offset = self.offset.wrapping_add(1);
165 v
166 } else {
167 self.stack.pop_byte()
168 }
169 }
170
171 #[inline(always)]
172 fn pop_short(&mut self) -> u16 {
173 if keep(FLAGS) {
174 let v = self.stack.peek_short_at(self.offset);
175 self.offset = self.offset.wrapping_add(2);
176 v
177 } else {
178 self.stack.pop_short()
179 }
180 }
181
182 #[inline(always)]
183 fn push(&mut self, v: Value) {
184 self.stack.push(v);
185 }
186
187 #[inline(always)]
188 fn reserve(&mut self, n: u8) {
189 self.stack.reserve(n);
190 }
191
192 #[inline(always)]
194 fn emplace(&mut self, v: Value) {
195 match v {
196 Value::Short(v) => {
197 self.stack.emplace_short(v);
198 }
199 Value::Byte(v) => {
200 self.stack.emplace_byte(v);
201 }
202 }
203 }
204
205 #[inline(always)]
206 fn push_byte(&mut self, v: u8) {
207 self.stack.push_byte(v);
208 }
209
210 #[inline(always)]
211 fn push_short(&mut self, v: u16) {
212 self.stack.push_short(v);
213 }
214}
215
216#[derive(Copy, Clone, Debug)]
217enum Value {
218 Short(u16),
219 Byte(u8),
220}
221
222impl Value {
223 #[inline(always)]
224 fn wrapping_add(&self, i: u8) -> Self {
225 match self {
226 Value::Short(v) => Value::Short(v.wrapping_add(u16::from(i))),
227 Value::Byte(v) => Value::Byte(v.wrapping_add(i)),
228 }
229 }
230 #[inline(always)]
231 fn shr(&self, i: u32) -> Self {
232 match self {
233 Value::Short(v) => Value::Short(v.checked_shr(i).unwrap_or(0)),
234 Value::Byte(v) => Value::Byte(v.checked_shr(i).unwrap_or(0)),
235 }
236 }
237 #[inline(always)]
238 fn shl(&self, i: u32) -> Self {
239 match self {
240 Value::Short(v) => Value::Short(v.checked_shl(i).unwrap_or(0)),
241 Value::Byte(v) => Value::Byte(v.checked_shl(i).unwrap_or(0)),
242 }
243 }
244}
245
246impl From<Value> for u16 {
247 fn from(v: Value) -> u16 {
248 match v {
249 Value::Short(v) => v,
250 Value::Byte(v) => u16::from(v),
251 }
252 }
253}
254
255impl Stack<'_> {
256 #[inline(always)]
257 fn pop_byte(&mut self) -> u8 {
258 let out = self.data[usize::from(self.index)];
259 self.index = self.index.wrapping_sub(1);
260 out
261 }
262
263 #[inline(always)]
264 fn pop_short(&mut self) -> u16 {
265 let lo = self.pop_byte();
266 let hi = self.pop_byte();
267 u16::from_le_bytes([lo, hi])
268 }
269
270 #[inline(always)]
271 fn push_byte(&mut self, v: u8) {
272 self.index = self.index.wrapping_add(1);
273 self.data[usize::from(self.index)] = v;
274 }
275
276 #[inline(always)]
277 fn emplace_byte(&mut self, v: u8) {
278 self.data[usize::from(self.index)] = v;
279 }
280
281 #[inline(always)]
282 fn emplace_short(&mut self, v: u16) {
283 let [lo, hi] = v.to_le_bytes();
284 self.data[usize::from(self.index.wrapping_sub(1))] = hi;
285 self.data[usize::from(self.index)] = lo;
286 }
287
288 #[inline(always)]
289 fn reserve(&mut self, n: u8) {
290 self.index = self.index.wrapping_add(n);
291 }
292
293 #[inline(always)]
294 fn push_short(&mut self, v: u16) {
295 let [lo, hi] = v.to_le_bytes();
296 self.push_byte(hi);
297 self.push_byte(lo);
298 }
299
300 #[inline(always)]
301 fn push(&mut self, v: Value) {
302 match v {
303 Value::Short(v) => self.push_short(v),
304 Value::Byte(v) => self.push_byte(v),
305 }
306 }
307
308 #[inline(always)]
310 pub fn peek_byte_at(&self, offset: u8) -> u8 {
311 self.data[usize::from(self.index.wrapping_sub(offset))]
312 }
313
314 #[inline(always)]
315 fn peek_short_at(&self, offset: u8) -> u16 {
316 let lo = self.peek_byte_at(offset);
317 let hi = self.peek_byte_at(offset.wrapping_add(1));
318 u16::from_le_bytes([lo, hi])
319 }
320
321 #[inline(always)]
323 pub fn len(&self) -> u8 {
324 self.index.wrapping_add(1)
325 }
326
327 #[inline(always)]
329 pub fn is_empty(&self) -> bool {
330 self.len() == 0
331 }
332
333 #[inline(always)]
335 pub fn set_len(&mut self, n: u8) {
336 self.index = n.wrapping_sub(1);
337 }
338}
339
340pub struct Uxn<'a, B> {
342 core: Option<UxnCore<'a>>,
343 _backend: core::marker::PhantomData<B>,
344}
345
346impl<'a, B: Backend> Uxn<'a, B> {
347 pub fn new(mem: &'a mut UxnMem) -> Self {
349 Self {
350 core: Some(UxnCore::new(mem)),
351 _backend: core::marker::PhantomData,
352 }
353 }
354
355 #[inline]
357 pub fn run<D: Device>(&mut self, dev: &mut D, pc: u16) -> u16 {
358 B::run(self, dev, pc)
359 }
360}
361
362impl<'a, B> core::ops::Deref for Uxn<'a, B> {
363 type Target = UxnCore<'a>;
364 fn deref(&self) -> &Self::Target {
365 self.core.as_ref().unwrap()
366 }
367}
368
369impl<'a, B> core::ops::DerefMut for Uxn<'a, B> {
370 fn deref_mut(&mut self) -> &mut Self::Target {
371 self.core.as_mut().unwrap()
372 }
373}
374
375pub struct UxnCore<'a> {
381 dev: &'a mut [u8; 256],
383 ram: &'a mut [u8; 65536],
385 stack: Stack<'a>,
387 ret: Stack<'a>,
389}
390
391macro_rules! op_cmp {
392 ($self:ident, $flags:ident, $f:expr) => {{
393 let mut s = $self.stack_view::<{ $flags }>();
394 #[allow(clippy::redundant_closure_call)]
395 let v = if short($flags) {
396 let b = s.pop_short();
397 let a = s.pop_short();
398 ($f)(a, b)
399 } else {
400 let b = s.pop_byte();
401 let a = s.pop_byte();
402 ($f)(a, b)
403 };
404 s.push_byte(u8::from(v));
405 }};
406}
407
408macro_rules! op_bin {
409 ($self:ident, $flags:ident, $f:expr) => {{
410 let mut s = $self.stack_view::<{ $flags }>();
411 #[allow(clippy::redundant_closure_call)]
412 if short($flags) {
413 let b = s.pop_short();
414 let a = s.pop_short();
415 let f: fn(u16, u16) -> u16 = $f;
416 s.push_short(f(a, b));
417 } else {
418 let b = s.pop_byte();
419 let a = s.pop_byte();
420 let f: fn(u8, u8) -> u8 = $f;
421 s.push_byte(f(a, b));
422 };
423 }};
424}
425
426#[derive(zerocopy::FromZeros)]
428pub struct UxnMem {
429 dev: [u8; 256],
430 stack: [u8; 256],
431 rstack: [u8; 256],
432 ram: [u8; 65536],
433}
434
435impl UxnMem {
436 #[expect(clippy::new_without_default)]
442 pub const fn new() -> Self {
443 Self {
444 dev: [0u8; 256],
445 stack: [0u8; 256],
446 rstack: [0u8; 256],
447 ram: [0u8; 65536],
448 }
449 }
450
451 #[cfg(feature = "alloc")]
453 pub fn boxed() -> alloc::boxed::Box<Self> {
454 <Self as zerocopy::FromZeros>::new_box_zeroed().unwrap()
455 }
456}
457
458impl<'a> UxnCore<'a> {
459 pub fn new(mem: &'a mut UxnMem) -> Self {
461 let UxnMem {
462 dev,
463 stack,
464 rstack,
465 ram,
466 } = mem;
467 ram.fill(0);
468 Self {
469 dev,
470 ram,
471 stack: Stack::new(stack),
472 ret: Stack::new(rstack),
473 }
474 }
475
476 #[inline]
478 fn next(&self, pc: &mut u16) -> u8 {
479 let out = self.ram[usize::from(*pc)];
480 *pc = pc.wrapping_add(1);
481 out
482 }
483
484 #[inline]
486 fn next2(&self, pc: &mut u16) -> u16 {
487 let hi = self.next(pc);
488 let lo = self.next(pc);
489 u16::from_le_bytes([lo, hi])
490 }
491
492 #[inline]
493 fn ram_write(&mut self, addr: u16, v: Value) {
494 match v {
495 Value::Short(v) => {
496 let [lo, hi] = v.to_le_bytes();
497 self.ram[usize::from(addr)] = hi;
498 self.ram[usize::from(addr.wrapping_add(1))] = lo;
499 }
500 Value::Byte(v) => {
501 self.ram[usize::from(addr)] = v;
502 }
503 }
504 }
505
506 #[inline(always)]
507 fn ram_read<const FLAGS: u8>(&self, addr: u16) -> Value {
508 if short(FLAGS) {
509 let hi = self.ram[usize::from(addr)];
510 let lo = self.ram[usize::from(addr.wrapping_add(1))];
511 Value::Short(u16::from_le_bytes([lo, hi]))
512 } else {
513 let v = self.ram[usize::from(addr)];
514 Value::Byte(v)
515 }
516 }
517
518 #[inline(always)]
519 fn stack_view<const FLAGS: u8>(&mut self) -> StackView<'_, 'a, FLAGS> {
520 let stack = if ret(FLAGS) {
521 &mut self.ret
522 } else {
523 &mut self.stack
524 };
525 StackView::new(stack)
526 }
527
528 #[inline(always)]
529 fn ret_stack_view<const FLAGS: u8>(&mut self) -> StackView<'_, 'a, FLAGS> {
530 let stack = if ret(FLAGS) {
531 &mut self.stack
532 } else {
533 &mut self.ret
534 };
535 StackView::new(stack)
536 }
537
538 #[inline(always)]
542 pub fn ram_read_word(&self, addr: u16) -> u16 {
543 let hi = self.ram[usize::from(addr)];
544 let lo = self.ram[usize::from(addr.wrapping_add(1))];
545 u16::from_le_bytes([lo, hi])
546 }
547
548 #[inline(always)]
550 pub fn write_dev_mem(&mut self, addr: u8, value: u8) {
551 self.dev[usize::from(addr)] = value;
552 }
553
554 #[inline(always)]
556 pub fn dev<D: Ports>(&self) -> &D {
557 self.dev_at(D::BASE)
558 }
559
560 #[inline(always)]
562 pub fn dev_at<D: Ports>(&self, pos: u8) -> &D {
563 Self::check_dev_size::<D>();
564 D::ref_from_bytes(&self.dev[usize::from(pos)..][..DEV_SIZE]).unwrap()
565 }
566
567 #[inline(always)]
569 pub fn dev_mut_at<D: Ports>(&mut self, pos: u8) -> &mut D {
570 Self::check_dev_size::<D>();
571 D::mut_from_bytes(&mut self.dev[usize::from(pos)..][..DEV_SIZE])
572 .unwrap()
573 }
574
575 #[inline(always)]
577 pub fn dev_mut<D: Ports>(&mut self) -> &mut D {
578 self.dev_mut_at(D::BASE)
579 }
580
581 #[inline(always)]
583 pub fn ram_read_byte(&self, addr: u16) -> u8 {
584 self.ram[usize::from(addr)]
585 }
586
587 #[inline(always)]
589 pub fn ram_write_byte(&mut self, addr: u16, v: u8) {
590 self.ram[usize::from(addr)] = v;
591 }
592
593 #[inline(always)]
595 pub fn stack(&self) -> &Stack<'a> {
596 &self.stack
597 }
598
599 #[inline(always)]
601 pub fn stack_mut(&mut self) -> &mut Stack<'a> {
602 &mut self.stack
603 }
604
605 #[inline(always)]
607 pub fn ret(&self) -> &Stack<'_> {
608 &self.ret
609 }
610
611 #[inline(always)]
613 pub fn ret_mut(&mut self) -> &mut Stack<'a> {
614 &mut self.ret
615 }
616
617 #[must_use]
622 pub fn reset<'b>(&mut self, rom: &'b [u8]) -> &'b [u8] {
623 self.dev.fill(0);
624 self.ram.fill(0);
625 self.stack.reset();
626 self.ret.reset();
627 let n = (self.ram.len() - 0x100).min(rom.len());
628 self.ram[0x100..][..n].copy_from_slice(&rom[..n]);
629 &rom[n..]
630 }
631
632 const fn check_dev_size<D: Ports>() {
634 struct AssertDevSize<D>(D);
635 impl<D> AssertDevSize<D> {
636 const ASSERT: () = if core::mem::size_of::<D>() != DEV_SIZE {
637 panic!("dev must be 16 bytes");
638 };
639 }
640 AssertDevSize::<D>::ASSERT
641 }
642
643 #[inline]
645 fn op<D: Device>(&mut self, op: u8, dev: &mut D, pc: u16) -> Option<u16> {
646 match op {
647 op::BRK => self.brk(pc),
648 op::INC => self.inc::<0b000>(pc),
649 op::POP => self.pop::<0b000>(pc),
650 op::NIP => self.nip::<0b000>(pc),
651 op::SWP => self.swp::<0b000>(pc),
652 op::ROT => self.rot::<0b000>(pc),
653 op::DUP => self.dup::<0b000>(pc),
654 op::OVR => self.ovr::<0b000>(pc),
655 op::EQU => self.equ::<0b000>(pc),
656 op::NEQ => self.neq::<0b000>(pc),
657 op::GTH => self.gth::<0b000>(pc),
658 op::LTH => self.lth::<0b000>(pc),
659 op::JMP => self.jmp::<0b000>(pc),
660 op::JCN => self.jcn::<0b000>(pc),
661 op::JSR => self.jsr::<0b000>(pc),
662 op::STH => self.sth::<0b000>(pc),
663 op::LDZ => self.ldz::<0b000>(pc),
664 op::STZ => self.stz::<0b000>(pc),
665 op::LDR => self.ldr::<0b000>(pc),
666 op::STR => self.str::<0b000>(pc),
667 op::LDA => self.lda::<0b000>(pc),
668 op::STA => self.sta::<0b000>(pc),
669 op::DEI => self.dei::<0b000>(pc, dev),
670 op::DEO => self.deo::<0b000>(pc, dev),
671 op::ADD => self.add::<0b000>(pc),
672 op::SUB => self.sub::<0b000>(pc),
673 op::MUL => self.mul::<0b000>(pc),
674 op::DIV => self.div::<0b000>(pc),
675 op::AND => self.and::<0b000>(pc),
676 op::ORA => self.ora::<0b000>(pc),
677 op::EOR => self.eor::<0b000>(pc),
678 op::SFT => self.sft::<0b000>(pc),
679 op::JCI => self.jci(pc),
680 op::INC2 => self.inc::<0b001>(pc),
681 op::POP2 => self.pop::<0b001>(pc),
682 op::NIP2 => self.nip::<0b001>(pc),
683 op::SWP2 => self.swp::<0b001>(pc),
684 op::ROT2 => self.rot::<0b001>(pc),
685 op::DUP2 => self.dup::<0b001>(pc),
686 op::OVR2 => self.ovr::<0b001>(pc),
687 op::EQU2 => self.equ::<0b001>(pc),
688 op::NEQ2 => self.neq::<0b001>(pc),
689 op::GTH2 => self.gth::<0b001>(pc),
690 op::LTH2 => self.lth::<0b001>(pc),
691 op::JMP2 => self.jmp::<0b001>(pc),
692 op::JCN2 => self.jcn::<0b001>(pc),
693 op::JSR2 => self.jsr::<0b001>(pc),
694 op::STH2 => self.sth::<0b001>(pc),
695 op::LDZ2 => self.ldz::<0b001>(pc),
696 op::STZ2 => self.stz::<0b001>(pc),
697 op::LDR2 => self.ldr::<0b001>(pc),
698 op::STR2 => self.str::<0b001>(pc),
699 op::LDA2 => self.lda::<0b001>(pc),
700 op::STA2 => self.sta::<0b001>(pc),
701 op::DEI2 => self.dei::<0b001>(pc, dev),
702 op::DEO2 => self.deo::<0b001>(pc, dev),
703 op::ADD2 => self.add::<0b001>(pc),
704 op::SUB2 => self.sub::<0b001>(pc),
705 op::MUL2 => self.mul::<0b001>(pc),
706 op::DIV2 => self.div::<0b001>(pc),
707 op::AND2 => self.and::<0b001>(pc),
708 op::ORA2 => self.ora::<0b001>(pc),
709 op::EOR2 => self.eor::<0b001>(pc),
710 op::SFT2 => self.sft::<0b001>(pc),
711 op::JMI => self.jmi(pc),
712 op::INCr => self.inc::<0b010>(pc),
713 op::POPr => self.pop::<0b010>(pc),
714 op::NIPr => self.nip::<0b010>(pc),
715 op::SWPr => self.swp::<0b010>(pc),
716 op::ROTr => self.rot::<0b010>(pc),
717 op::DUPr => self.dup::<0b010>(pc),
718 op::OVRr => self.ovr::<0b010>(pc),
719 op::EQUr => self.equ::<0b010>(pc),
720 op::NEQr => self.neq::<0b010>(pc),
721 op::GTHr => self.gth::<0b010>(pc),
722 op::LTHr => self.lth::<0b010>(pc),
723 op::JMPr => self.jmp::<0b010>(pc),
724 op::JCNr => self.jcn::<0b010>(pc),
725 op::JSRr => self.jsr::<0b010>(pc),
726 op::STHr => self.sth::<0b010>(pc),
727 op::LDZr => self.ldz::<0b010>(pc),
728 op::STZr => self.stz::<0b010>(pc),
729 op::LDRr => self.ldr::<0b010>(pc),
730 op::STRr => self.str::<0b010>(pc),
731 op::LDAr => self.lda::<0b010>(pc),
732 op::STAr => self.sta::<0b010>(pc),
733 op::DEIr => self.dei::<0b010>(pc, dev),
734 op::DEOr => self.deo::<0b010>(pc, dev),
735 op::ADDr => self.add::<0b010>(pc),
736 op::SUBr => self.sub::<0b010>(pc),
737 op::MULr => self.mul::<0b010>(pc),
738 op::DIVr => self.div::<0b010>(pc),
739 op::ANDr => self.and::<0b010>(pc),
740 op::ORAr => self.ora::<0b010>(pc),
741 op::EORr => self.eor::<0b010>(pc),
742 op::SFTr => self.sft::<0b010>(pc),
743 op::JSI => self.jsi(pc),
744 op::INC2r => self.inc::<0b011>(pc),
745 op::POP2r => self.pop::<0b011>(pc),
746 op::NIP2r => self.nip::<0b011>(pc),
747 op::SWP2r => self.swp::<0b011>(pc),
748 op::ROT2r => self.rot::<0b011>(pc),
749 op::DUP2r => self.dup::<0b011>(pc),
750 op::OVR2r => self.ovr::<0b011>(pc),
751 op::EQU2r => self.equ::<0b011>(pc),
752 op::NEQ2r => self.neq::<0b011>(pc),
753 op::GTH2r => self.gth::<0b011>(pc),
754 op::LTH2r => self.lth::<0b011>(pc),
755 op::JMP2r => self.jmp::<0b011>(pc),
756 op::JCN2r => self.jcn::<0b011>(pc),
757 op::JSR2r => self.jsr::<0b011>(pc),
758 op::STH2r => self.sth::<0b011>(pc),
759 op::LDZ2r => self.ldz::<0b011>(pc),
760 op::STZ2r => self.stz::<0b011>(pc),
761 op::LDR2r => self.ldr::<0b011>(pc),
762 op::STR2r => self.str::<0b011>(pc),
763 op::LDA2r => self.lda::<0b011>(pc),
764 op::STA2r => self.sta::<0b011>(pc),
765 op::DEI2r => self.dei::<0b011>(pc, dev),
766 op::DEO2r => self.deo::<0b011>(pc, dev),
767 op::ADD2r => self.add::<0b011>(pc),
768 op::SUB2r => self.sub::<0b011>(pc),
769 op::MUL2r => self.mul::<0b011>(pc),
770 op::DIV2r => self.div::<0b011>(pc),
771 op::AND2r => self.and::<0b011>(pc),
772 op::ORA2r => self.ora::<0b011>(pc),
773 op::EOR2r => self.eor::<0b011>(pc),
774 op::SFT2r => self.sft::<0b011>(pc),
775 op::LIT => self.lit::<0b100>(pc),
776 op::INCk => self.inc::<0b100>(pc),
777 op::POPk => self.pop::<0b100>(pc),
778 op::NIPk => self.nip::<0b100>(pc),
779 op::SWPk => self.swp::<0b100>(pc),
780 op::ROTk => self.rot::<0b100>(pc),
781 op::DUPk => self.dup::<0b100>(pc),
782 op::OVRk => self.ovr::<0b100>(pc),
783 op::EQUk => self.equ::<0b100>(pc),
784 op::NEQk => self.neq::<0b100>(pc),
785 op::GTHk => self.gth::<0b100>(pc),
786 op::LTHk => self.lth::<0b100>(pc),
787 op::JMPk => self.jmp::<0b100>(pc),
788 op::JCNk => self.jcn::<0b100>(pc),
789 op::JSRk => self.jsr::<0b100>(pc),
790 op::STHk => self.sth::<0b100>(pc),
791 op::LDZk => self.ldz::<0b100>(pc),
792 op::STZk => self.stz::<0b100>(pc),
793 op::LDRk => self.ldr::<0b100>(pc),
794 op::STRk => self.str::<0b100>(pc),
795 op::LDAk => self.lda::<0b100>(pc),
796 op::STAk => self.sta::<0b100>(pc),
797 op::DEIk => self.dei::<0b100>(pc, dev),
798 op::DEOk => self.deo::<0b100>(pc, dev),
799 op::ADDk => self.add::<0b100>(pc),
800 op::SUBk => self.sub::<0b100>(pc),
801 op::MULk => self.mul::<0b100>(pc),
802 op::DIVk => self.div::<0b100>(pc),
803 op::ANDk => self.and::<0b100>(pc),
804 op::ORAk => self.ora::<0b100>(pc),
805 op::EORk => self.eor::<0b100>(pc),
806 op::SFTk => self.sft::<0b100>(pc),
807 op::LIT2 => self.lit::<0b101>(pc),
808 op::INC2k => self.inc::<0b101>(pc),
809 op::POP2k => self.pop::<0b101>(pc),
810 op::NIP2k => self.nip::<0b101>(pc),
811 op::SWP2k => self.swp::<0b101>(pc),
812 op::ROT2k => self.rot::<0b101>(pc),
813 op::DUP2k => self.dup::<0b101>(pc),
814 op::OVR2k => self.ovr::<0b101>(pc),
815 op::EQU2k => self.equ::<0b101>(pc),
816 op::NEQ2k => self.neq::<0b101>(pc),
817 op::GTH2k => self.gth::<0b101>(pc),
818 op::LTH2k => self.lth::<0b101>(pc),
819 op::JMP2k => self.jmp::<0b101>(pc),
820 op::JCN2k => self.jcn::<0b101>(pc),
821 op::JSR2k => self.jsr::<0b101>(pc),
822 op::STH2k => self.sth::<0b101>(pc),
823 op::LDZ2k => self.ldz::<0b101>(pc),
824 op::STZ2k => self.stz::<0b101>(pc),
825 op::LDR2k => self.ldr::<0b101>(pc),
826 op::STR2k => self.str::<0b101>(pc),
827 op::LDA2k => self.lda::<0b101>(pc),
828 op::STA2k => self.sta::<0b101>(pc),
829 op::DEI2k => self.dei::<0b101>(pc, dev),
830 op::DEO2k => self.deo::<0b101>(pc, dev),
831 op::ADD2k => self.add::<0b101>(pc),
832 op::SUB2k => self.sub::<0b101>(pc),
833 op::MUL2k => self.mul::<0b101>(pc),
834 op::DIV2k => self.div::<0b101>(pc),
835 op::AND2k => self.and::<0b101>(pc),
836 op::ORA2k => self.ora::<0b101>(pc),
837 op::EOR2k => self.eor::<0b101>(pc),
838 op::SFT2k => self.sft::<0b101>(pc),
839 op::LITr => self.lit::<0b110>(pc),
840 op::INCkr => self.inc::<0b110>(pc),
841 op::POPkr => self.pop::<0b110>(pc),
842 op::NIPkr => self.nip::<0b110>(pc),
843 op::SWPkr => self.swp::<0b110>(pc),
844 op::ROTkr => self.rot::<0b110>(pc),
845 op::DUPkr => self.dup::<0b110>(pc),
846 op::OVRkr => self.ovr::<0b110>(pc),
847 op::EQUkr => self.equ::<0b110>(pc),
848 op::NEQkr => self.neq::<0b110>(pc),
849 op::GTHkr => self.gth::<0b110>(pc),
850 op::LTHkr => self.lth::<0b110>(pc),
851 op::JMPkr => self.jmp::<0b110>(pc),
852 op::JCNkr => self.jcn::<0b110>(pc),
853 op::JSRkr => self.jsr::<0b110>(pc),
854 op::STHkr => self.sth::<0b110>(pc),
855 op::LDZkr => self.ldz::<0b110>(pc),
856 op::STZkr => self.stz::<0b110>(pc),
857 op::LDRkr => self.ldr::<0b110>(pc),
858 op::STRkr => self.str::<0b110>(pc),
859 op::LDAkr => self.lda::<0b110>(pc),
860 op::STAkr => self.sta::<0b110>(pc),
861 op::DEIkr => self.dei::<0b110>(pc, dev),
862 op::DEOkr => self.deo::<0b110>(pc, dev),
863 op::ADDkr => self.add::<0b110>(pc),
864 op::SUBkr => self.sub::<0b110>(pc),
865 op::MULkr => self.mul::<0b110>(pc),
866 op::DIVkr => self.div::<0b110>(pc),
867 op::ANDkr => self.and::<0b110>(pc),
868 op::ORAkr => self.ora::<0b110>(pc),
869 op::EORkr => self.eor::<0b110>(pc),
870 op::SFTkr => self.sft::<0b110>(pc),
871 op::LIT2r => self.lit::<0b111>(pc),
872 op::INC2kr => self.inc::<0b111>(pc),
873 op::POP2kr => self.pop::<0b111>(pc),
874 op::NIP2kr => self.nip::<0b111>(pc),
875 op::SWP2kr => self.swp::<0b111>(pc),
876 op::ROT2kr => self.rot::<0b111>(pc),
877 op::DUP2kr => self.dup::<0b111>(pc),
878 op::OVR2kr => self.ovr::<0b111>(pc),
879 op::EQU2kr => self.equ::<0b111>(pc),
880 op::NEQ2kr => self.neq::<0b111>(pc),
881 op::GTH2kr => self.gth::<0b111>(pc),
882 op::LTH2kr => self.lth::<0b111>(pc),
883 op::JMP2kr => self.jmp::<0b111>(pc),
884 op::JCN2kr => self.jcn::<0b111>(pc),
885 op::JSR2kr => self.jsr::<0b111>(pc),
886 op::STH2kr => self.sth::<0b111>(pc),
887 op::LDZ2kr => self.ldz::<0b111>(pc),
888 op::STZ2kr => self.stz::<0b111>(pc),
889 op::LDR2kr => self.ldr::<0b111>(pc),
890 op::STR2kr => self.str::<0b111>(pc),
891 op::LDA2kr => self.lda::<0b111>(pc),
892 op::STA2kr => self.sta::<0b111>(pc),
893 op::DEI2kr => self.dei::<0b111>(pc, dev),
894 op::DEO2kr => self.deo::<0b111>(pc, dev),
895 op::ADD2kr => self.add::<0b111>(pc),
896 op::SUB2kr => self.sub::<0b111>(pc),
897 op::MUL2kr => self.mul::<0b111>(pc),
898 op::DIV2kr => self.div::<0b111>(pc),
899 op::AND2kr => self.and::<0b111>(pc),
900 op::ORA2kr => self.ora::<0b111>(pc),
901 op::EOR2kr => self.eor::<0b111>(pc),
902 op::SFT2kr => self.sft::<0b111>(pc),
903 }
904 }
905
906 #[inline(always)]
908 fn jump_offset(pc: u16, v: Value) -> u16 {
909 match v {
910 Value::Short(dst) => dst,
911 Value::Byte(offset) => {
912 let offset = i16::from(offset as i8);
913 pc.wrapping_add_signed(offset)
914 }
915 }
916 }
917
918 #[inline]
925 pub fn brk(&mut self, _: u16) -> Option<u16> {
926 None
927 }
928
929 #[inline]
939 pub fn jci(&mut self, mut pc: u16) -> Option<u16> {
940 let dt = self.next2(&mut pc);
941 if self.stack.pop_byte() != 0 {
942 pc = pc.wrapping_add(dt);
943 }
944 Some(pc)
945 }
946
947 #[inline]
952 pub fn jmi(&mut self, mut pc: u16) -> Option<u16> {
953 let dt = self.next2(&mut pc);
954 Some(pc.wrapping_add(dt))
955 }
956
957 #[inline]
967 pub fn jsi(&mut self, mut pc: u16) -> Option<u16> {
968 let dt = self.next2(&mut pc);
969 self.ret.push(Value::Short(pc));
970 Some(pc.wrapping_add(dt))
971 }
972
973 #[inline]
988 pub fn lit<const FLAGS: u8>(&mut self, mut pc: u16) -> Option<u16> {
989 let v = if short(FLAGS) {
990 Value::Short(self.next2(&mut pc))
991 } else {
992 Value::Byte(self.next(&mut pc))
993 };
994 self.stack_view::<FLAGS>().push(v);
995 Some(pc)
996 }
997
998 #[inline]
1012 pub fn inc<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1013 let mut s = self.stack_view::<FLAGS>();
1014 let v = s.pop();
1015 s.push(v.wrapping_add(1));
1016 Some(pc)
1017 }
1018
1019 #[inline]
1033 pub fn pop<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1034 self.stack_view::<FLAGS>().pop();
1035 Some(pc)
1036 }
1037
1038 #[inline]
1053 pub fn nip<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1054 let mut s = self.stack_view::<FLAGS>();
1055 let v = s.pop();
1056 let _ = s.pop();
1057 s.push(v);
1058 Some(pc)
1059 }
1060
1061 #[inline]
1076 pub fn swp<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1077 let mut s = self.stack_view::<FLAGS>();
1078 let b = s.pop();
1079 let a = s.pop();
1080 s.push(b);
1081 s.push(a);
1082 Some(pc)
1083 }
1084
1085 #[inline]
1101 pub fn rot<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1102 let mut s = self.stack_view::<FLAGS>();
1103 let c = s.pop();
1104 let b = s.pop();
1105 let a = s.pop();
1106 s.push(b);
1107 s.push(c);
1108 s.push(a);
1109 Some(pc)
1110 }
1111
1112 #[inline]
1126 pub fn dup<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1127 let mut s = self.stack_view::<FLAGS>();
1128 let v = s.pop();
1129 s.push(v);
1130 s.push(v);
1131 Some(pc)
1132 }
1133
1134 #[inline]
1149 pub fn ovr<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1150 let mut s = self.stack_view::<FLAGS>();
1151 let b = s.pop();
1152 let a = s.pop();
1153 s.push(a);
1154 s.push(b);
1155 s.push(a);
1156 Some(pc)
1157 }
1158
1159 #[inline]
1175 pub fn equ<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1176 op_cmp!(self, FLAGS, |a, b| a == b);
1177 Some(pc)
1178 }
1179
1180 #[inline]
1196 pub fn neq<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1197 op_cmp!(self, FLAGS, |a, b| a != b);
1198 Some(pc)
1199 }
1200
1201 #[inline]
1217 pub fn gth<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1218 op_cmp!(self, FLAGS, |a, b| a > b);
1219 Some(pc)
1220 }
1221
1222 #[inline]
1238 pub fn lth<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1239 op_cmp!(self, FLAGS, |a, b| a < b);
1240 Some(pc)
1241 }
1242
1243 #[inline]
1256 pub fn jmp<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1257 let mut s = self.stack_view::<FLAGS>();
1258 Some(Self::jump_offset(pc, s.pop()))
1259 }
1260
1261 #[inline]
1276 pub fn jcn<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1277 let mut s = self.stack_view::<FLAGS>();
1278 let dst = s.pop();
1279 let cond = s.pop_byte();
1280 Some(if cond != 0 {
1281 Self::jump_offset(pc, dst)
1282 } else {
1283 pc
1284 })
1285 }
1286
1287 #[inline]
1302 pub fn jsr<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1303 self.ret_stack_view::<FLAGS>().push(Value::Short(pc));
1304 let mut s = self.stack_view::<FLAGS>();
1305 Some(Self::jump_offset(pc, s.pop()))
1306 }
1307
1308 #[inline]
1323 pub fn sth<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1324 let v = self.stack_view::<FLAGS>().pop();
1325 self.ret_stack_view::<FLAGS>().push(v);
1326 Some(pc)
1327 }
1328
1329 #[inline]
1341 pub fn ldz<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1342 let addr = self.stack_view::<FLAGS>().pop_byte();
1343 let v = self.ram_read::<FLAGS>(u16::from(addr));
1344 self.stack_view::<FLAGS>().push(v);
1345 Some(pc)
1346 }
1347
1348 #[inline]
1359 pub fn stz<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1360 let mut s = self.stack_view::<FLAGS>();
1361 let addr = s.pop_byte();
1362 let v = s.pop();
1363 self.ram_write(u16::from(addr), v);
1364 Some(pc)
1365 }
1366
1367 #[inline]
1380 pub fn ldr<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1381 let offset = self.stack_view::<FLAGS>().pop_byte() as i8;
1382 let addr = pc.wrapping_add_signed(i16::from(offset));
1383 let v = self.ram_read::<FLAGS>(addr);
1384 self.stack_view::<FLAGS>().push(v);
1385 Some(pc)
1386 }
1387
1388 #[inline]
1401 pub fn str<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1402 let mut s = self.stack_view::<FLAGS>();
1403 let offset = s.pop_byte() as i8;
1404 let addr = pc.wrapping_add_signed(i16::from(offset));
1405 let v = s.pop();
1406 self.ram_write(addr, v);
1407 Some(pc)
1408 }
1409
1410 #[inline]
1422 pub fn lda<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1423 let addr = self.stack_view::<FLAGS>().pop_short();
1424 let v = self.ram_read::<FLAGS>(addr);
1425 self.stack_view::<FLAGS>().push(v);
1426 Some(pc)
1427 }
1428
1429 #[inline]
1441 pub fn sta<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1442 let mut s = self.stack_view::<FLAGS>();
1443 let addr = s.pop_short();
1444 let v = s.pop();
1445 self.ram_write(addr, v);
1446 Some(pc)
1447 }
1448
1449 #[inline]
1458 pub fn dei<const FLAGS: u8>(
1459 &mut self,
1460 pc: u16,
1461 dev: &mut dyn Device,
1462 ) -> Option<u16> {
1463 let mut s = self.stack_view::<FLAGS>();
1464 let i = s.pop_byte();
1465
1466 let v = if short(FLAGS) {
1472 s.reserve(2);
1473 dev.dei(self, i);
1474 let hi = self.dev[usize::from(i)];
1475 let j = i.wrapping_add(1);
1476 dev.dei(self, j);
1477 let lo = self.dev[usize::from(j)];
1478 Value::Short(u16::from_le_bytes([lo, hi]))
1479 } else {
1480 s.reserve(1);
1481 dev.dei(self, i);
1482 Value::Byte(self.dev[usize::from(i)])
1483 };
1484 self.stack_view::<FLAGS>().emplace(v);
1485 Some(pc)
1486 }
1487
1488 #[inline]
1497 pub fn deo<const FLAGS: u8>(
1498 &mut self,
1499 pc: u16,
1500 dev: &mut dyn Device,
1501 ) -> Option<u16> {
1502 let mut s = self.stack_view::<FLAGS>();
1503 let i = s.pop_byte();
1504 let mut run = true;
1505 match s.pop() {
1506 Value::Short(v) => {
1507 let [lo, hi] = v.to_le_bytes();
1508 let j = i.wrapping_add(1);
1509 self.dev[usize::from(i)] = hi;
1510 run &= dev.deo(self, i);
1511 self.dev[usize::from(j)] = lo;
1512 run &= dev.deo(self, j);
1513 }
1514 Value::Byte(v) => {
1515 self.dev[usize::from(i)] = v;
1516 run &= dev.deo(self, i);
1517 }
1518 }
1519 if run { Some(pc) } else { None }
1520 }
1521
1522 #[inline]
1535 pub fn add<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1536 op_bin!(self, FLAGS, |a, b| a.wrapping_add(b));
1537 Some(pc)
1538 }
1539
1540 #[inline]
1549 pub fn sub<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1550 op_bin!(self, FLAGS, |a, b| a.wrapping_sub(b));
1551 Some(pc)
1552 }
1553
1554 #[inline]
1563 pub fn mul<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1564 op_bin!(self, FLAGS, |a, b| a.wrapping_mul(b));
1565 Some(pc)
1566 }
1567
1568 #[inline]
1584 pub fn div<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1585 op_bin!(self, FLAGS, |a, b| a.checked_div(b).unwrap_or(0));
1586 Some(pc)
1587 }
1588
1589 #[inline]
1598 pub fn and<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1599 op_bin!(self, FLAGS, |a, b| a & b);
1600 Some(pc)
1601 }
1602
1603 #[inline]
1610 pub fn ora<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1611 op_bin!(self, FLAGS, |a, b| a | b);
1612 Some(pc)
1613 }
1614
1615 #[inline]
1624 pub fn eor<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1625 op_bin!(self, FLAGS, |a, b| a ^ b);
1626 Some(pc)
1627 }
1628
1629 #[inline]
1647 pub fn sft<const FLAGS: u8>(&mut self, pc: u16) -> Option<u16> {
1648 let mut s = self.stack_view::<FLAGS>();
1649 let shift = s.pop_byte();
1650 let shr = u32::from(shift & 0xF);
1651 let shl = u32::from(shift >> 4);
1652 let v = s.pop();
1653 s.push(v.shr(shr).shl(shl));
1654 Some(pc)
1655 }
1656}
1657
1658pub trait Device {
1660 fn dei(&mut self, vm: &mut UxnCore, target: u8);
1665
1666 #[must_use]
1674 fn deo(&mut self, vm: &mut UxnCore, target: u8) -> bool;
1675}
1676
1677impl<'a> Uxn<'a, backend::Interpreter> {
1678 #[inline]
1685 pub fn run_until<D: Device, F: Fn(&Self, &D, usize) -> bool>(
1686 &mut self,
1687 dev: &mut D,
1688 mut pc: u16,
1689 stop: F,
1690 ) -> Option<u16> {
1691 for i in 0.. {
1692 let op = self.next(&mut pc);
1693 let Some(next) = self.op(op, dev, pc) else {
1694 return Some(pc);
1695 };
1696 pc = next;
1697 if stop(self, dev, i) {
1698 return None;
1699 }
1700 }
1701 unreachable!()
1702 }
1703}
1704
1705pub trait Ports:
1707 zerocopy::IntoBytes
1708 + zerocopy::FromBytes
1709 + zerocopy::FromZeros
1710 + zerocopy::Immutable
1711 + zerocopy::KnownLayout
1712{
1713 const BASE: u8;
1715}
1716
1717pub struct EmptyDevice;
1719impl Device for EmptyDevice {
1720 fn dei(&mut self, _vm: &mut UxnCore, _target: u8) {
1721 }
1723 fn deo(&mut self, _vm: &mut UxnCore, _target: u8) -> bool {
1724 true
1726 }
1727}
1728
1729#[allow(non_upper_case_globals, missing_docs)]
1733pub mod op {
1734 pub const BRK: u8 = 0x0;
1735 pub const INC: u8 = 0x1;
1736 pub const POP: u8 = 0x2;
1737 pub const NIP: u8 = 0x3;
1738 pub const SWP: u8 = 0x4;
1739 pub const ROT: u8 = 0x5;
1740 pub const DUP: u8 = 0x6;
1741 pub const OVR: u8 = 0x7;
1742 pub const EQU: u8 = 0x8;
1743 pub const NEQ: u8 = 0x9;
1744 pub const GTH: u8 = 0xa;
1745 pub const LTH: u8 = 0xb;
1746 pub const JMP: u8 = 0xc;
1747 pub const JCN: u8 = 0xd;
1748 pub const JSR: u8 = 0xe;
1749 pub const STH: u8 = 0x0f;
1750 pub const LDZ: u8 = 0x10;
1751 pub const STZ: u8 = 0x11;
1752 pub const LDR: u8 = 0x12;
1753 pub const STR: u8 = 0x13;
1754 pub const LDA: u8 = 0x14;
1755 pub const STA: u8 = 0x15;
1756 pub const DEI: u8 = 0x16;
1757 pub const DEO: u8 = 0x17;
1758 pub const ADD: u8 = 0x18;
1759 pub const SUB: u8 = 0x19;
1760 pub const MUL: u8 = 0x1a;
1761 pub const DIV: u8 = 0x1b;
1762 pub const AND: u8 = 0x1c;
1763 pub const ORA: u8 = 0x1d;
1764 pub const EOR: u8 = 0x1e;
1765 pub const SFT: u8 = 0x1f;
1766 pub const JCI: u8 = 0x20;
1767 pub const INC2: u8 = 0x21;
1768 pub const POP2: u8 = 0x22;
1769 pub const NIP2: u8 = 0x23;
1770 pub const SWP2: u8 = 0x24;
1771 pub const ROT2: u8 = 0x25;
1772 pub const DUP2: u8 = 0x26;
1773 pub const OVR2: u8 = 0x27;
1774 pub const EQU2: u8 = 0x28;
1775 pub const NEQ2: u8 = 0x29;
1776 pub const GTH2: u8 = 0x2a;
1777 pub const LTH2: u8 = 0x2b;
1778 pub const JMP2: u8 = 0x2c;
1779 pub const JCN2: u8 = 0x2d;
1780 pub const JSR2: u8 = 0x2e;
1781 pub const STH2: u8 = 0x2f;
1782 pub const LDZ2: u8 = 0x30;
1783 pub const STZ2: u8 = 0x31;
1784 pub const LDR2: u8 = 0x32;
1785 pub const STR2: u8 = 0x33;
1786 pub const LDA2: u8 = 0x34;
1787 pub const STA2: u8 = 0x35;
1788 pub const DEI2: u8 = 0x36;
1789 pub const DEO2: u8 = 0x37;
1790 pub const ADD2: u8 = 0x38;
1791 pub const SUB2: u8 = 0x39;
1792 pub const MUL2: u8 = 0x3a;
1793 pub const DIV2: u8 = 0x3b;
1794 pub const AND2: u8 = 0x3c;
1795 pub const ORA2: u8 = 0x3d;
1796 pub const EOR2: u8 = 0x3e;
1797 pub const SFT2: u8 = 0x3f;
1798 pub const JMI: u8 = 0x40;
1799 pub const INCr: u8 = 0x41;
1800 pub const POPr: u8 = 0x42;
1801 pub const NIPr: u8 = 0x43;
1802 pub const SWPr: u8 = 0x44;
1803 pub const ROTr: u8 = 0x45;
1804 pub const DUPr: u8 = 0x46;
1805 pub const OVRr: u8 = 0x47;
1806 pub const EQUr: u8 = 0x48;
1807 pub const NEQr: u8 = 0x49;
1808 pub const GTHr: u8 = 0x4a;
1809 pub const LTHr: u8 = 0x4b;
1810 pub const JMPr: u8 = 0x4c;
1811 pub const JCNr: u8 = 0x4d;
1812 pub const JSRr: u8 = 0x4e;
1813 pub const STHr: u8 = 0x4f;
1814 pub const LDZr: u8 = 0x50;
1815 pub const STZr: u8 = 0x51;
1816 pub const LDRr: u8 = 0x52;
1817 pub const STRr: u8 = 0x53;
1818 pub const LDAr: u8 = 0x54;
1819 pub const STAr: u8 = 0x55;
1820 pub const DEIr: u8 = 0x56;
1821 pub const DEOr: u8 = 0x57;
1822 pub const ADDr: u8 = 0x58;
1823 pub const SUBr: u8 = 0x59;
1824 pub const MULr: u8 = 0x5a;
1825 pub const DIVr: u8 = 0x5b;
1826 pub const ANDr: u8 = 0x5c;
1827 pub const ORAr: u8 = 0x5d;
1828 pub const EORr: u8 = 0x5e;
1829 pub const SFTr: u8 = 0x5f;
1830 pub const JSI: u8 = 0x60;
1831 pub const INC2r: u8 = 0x61;
1832 pub const POP2r: u8 = 0x62;
1833 pub const NIP2r: u8 = 0x63;
1834 pub const SWP2r: u8 = 0x64;
1835 pub const ROT2r: u8 = 0x65;
1836 pub const DUP2r: u8 = 0x66;
1837 pub const OVR2r: u8 = 0x67;
1838 pub const EQU2r: u8 = 0x68;
1839 pub const NEQ2r: u8 = 0x69;
1840 pub const GTH2r: u8 = 0x6a;
1841 pub const LTH2r: u8 = 0x6b;
1842 pub const JMP2r: u8 = 0x6c;
1843 pub const JCN2r: u8 = 0x6d;
1844 pub const JSR2r: u8 = 0x6e;
1845 pub const STH2r: u8 = 0x6f;
1846 pub const LDZ2r: u8 = 0x70;
1847 pub const STZ2r: u8 = 0x71;
1848 pub const LDR2r: u8 = 0x72;
1849 pub const STR2r: u8 = 0x73;
1850 pub const LDA2r: u8 = 0x74;
1851 pub const STA2r: u8 = 0x75;
1852 pub const DEI2r: u8 = 0x76;
1853 pub const DEO2r: u8 = 0x77;
1854 pub const ADD2r: u8 = 0x78;
1855 pub const SUB2r: u8 = 0x79;
1856 pub const MUL2r: u8 = 0x7a;
1857 pub const DIV2r: u8 = 0x7b;
1858 pub const AND2r: u8 = 0x7c;
1859 pub const ORA2r: u8 = 0x7d;
1860 pub const EOR2r: u8 = 0x7e;
1861 pub const SFT2r: u8 = 0x7f;
1862 pub const LIT: u8 = 0x80;
1863 pub const INCk: u8 = 0x81;
1864 pub const POPk: u8 = 0x82;
1865 pub const NIPk: u8 = 0x83;
1866 pub const SWPk: u8 = 0x84;
1867 pub const ROTk: u8 = 0x85;
1868 pub const DUPk: u8 = 0x86;
1869 pub const OVRk: u8 = 0x87;
1870 pub const EQUk: u8 = 0x88;
1871 pub const NEQk: u8 = 0x89;
1872 pub const GTHk: u8 = 0x8a;
1873 pub const LTHk: u8 = 0x8b;
1874 pub const JMPk: u8 = 0x8c;
1875 pub const JCNk: u8 = 0x8d;
1876 pub const JSRk: u8 = 0x8e;
1877 pub const STHk: u8 = 0x8f;
1878 pub const LDZk: u8 = 0x90;
1879 pub const STZk: u8 = 0x91;
1880 pub const LDRk: u8 = 0x92;
1881 pub const STRk: u8 = 0x93;
1882 pub const LDAk: u8 = 0x94;
1883 pub const STAk: u8 = 0x95;
1884 pub const DEIk: u8 = 0x96;
1885 pub const DEOk: u8 = 0x97;
1886 pub const ADDk: u8 = 0x98;
1887 pub const SUBk: u8 = 0x99;
1888 pub const MULk: u8 = 0x9a;
1889 pub const DIVk: u8 = 0x9b;
1890 pub const ANDk: u8 = 0x9c;
1891 pub const ORAk: u8 = 0x9d;
1892 pub const EORk: u8 = 0x9e;
1893 pub const SFTk: u8 = 0x9f;
1894 pub const LIT2: u8 = 0xa0;
1895 pub const INC2k: u8 = 0xa1;
1896 pub const POP2k: u8 = 0xa2;
1897 pub const NIP2k: u8 = 0xa3;
1898 pub const SWP2k: u8 = 0xa4;
1899 pub const ROT2k: u8 = 0xa5;
1900 pub const DUP2k: u8 = 0xa6;
1901 pub const OVR2k: u8 = 0xa7;
1902 pub const EQU2k: u8 = 0xa8;
1903 pub const NEQ2k: u8 = 0xa9;
1904 pub const GTH2k: u8 = 0xaa;
1905 pub const LTH2k: u8 = 0xab;
1906 pub const JMP2k: u8 = 0xac;
1907 pub const JCN2k: u8 = 0xad;
1908 pub const JSR2k: u8 = 0xae;
1909 pub const STH2k: u8 = 0xaf;
1910 pub const LDZ2k: u8 = 0xb0;
1911 pub const STZ2k: u8 = 0xb1;
1912 pub const LDR2k: u8 = 0xb2;
1913 pub const STR2k: u8 = 0xb3;
1914 pub const LDA2k: u8 = 0xb4;
1915 pub const STA2k: u8 = 0xb5;
1916 pub const DEI2k: u8 = 0xb6;
1917 pub const DEO2k: u8 = 0xb7;
1918 pub const ADD2k: u8 = 0xb8;
1919 pub const SUB2k: u8 = 0xb9;
1920 pub const MUL2k: u8 = 0xba;
1921 pub const DIV2k: u8 = 0xbb;
1922 pub const AND2k: u8 = 0xbc;
1923 pub const ORA2k: u8 = 0xbd;
1924 pub const EOR2k: u8 = 0xbe;
1925 pub const SFT2k: u8 = 0xbf;
1926 pub const LITr: u8 = 0xc0;
1927 pub const INCkr: u8 = 0xc1;
1928 pub const POPkr: u8 = 0xc2;
1929 pub const NIPkr: u8 = 0xc3;
1930 pub const SWPkr: u8 = 0xc4;
1931 pub const ROTkr: u8 = 0xc5;
1932 pub const DUPkr: u8 = 0xc6;
1933 pub const OVRkr: u8 = 0xc7;
1934 pub const EQUkr: u8 = 0xc8;
1935 pub const NEQkr: u8 = 0xc9;
1936 pub const GTHkr: u8 = 0xca;
1937 pub const LTHkr: u8 = 0xcb;
1938 pub const JMPkr: u8 = 0xcc;
1939 pub const JCNkr: u8 = 0xcd;
1940 pub const JSRkr: u8 = 0xce;
1941 pub const STHkr: u8 = 0xcf;
1942 pub const LDZkr: u8 = 0xd0;
1943 pub const STZkr: u8 = 0xd1;
1944 pub const LDRkr: u8 = 0xd2;
1945 pub const STRkr: u8 = 0xd3;
1946 pub const LDAkr: u8 = 0xd4;
1947 pub const STAkr: u8 = 0xd5;
1948 pub const DEIkr: u8 = 0xd6;
1949 pub const DEOkr: u8 = 0xd7;
1950 pub const ADDkr: u8 = 0xd8;
1951 pub const SUBkr: u8 = 0xd9;
1952 pub const MULkr: u8 = 0xda;
1953 pub const DIVkr: u8 = 0xdb;
1954 pub const ANDkr: u8 = 0xdc;
1955 pub const ORAkr: u8 = 0xdd;
1956 pub const EORkr: u8 = 0xde;
1957 pub const SFTkr: u8 = 0xdf;
1958 pub const LIT2r: u8 = 0xe0;
1959 pub const INC2kr: u8 = 0xe1;
1960 pub const POP2kr: u8 = 0xe2;
1961 pub const NIP2kr: u8 = 0xe3;
1962 pub const SWP2kr: u8 = 0xe4;
1963 pub const ROT2kr: u8 = 0xe5;
1964 pub const DUP2kr: u8 = 0xe6;
1965 pub const OVR2kr: u8 = 0xe7;
1966 pub const EQU2kr: u8 = 0xe8;
1967 pub const NEQ2kr: u8 = 0xe9;
1968 pub const GTH2kr: u8 = 0xea;
1969 pub const LTH2kr: u8 = 0xeb;
1970 pub const JMP2kr: u8 = 0xec;
1971 pub const JCN2kr: u8 = 0xed;
1972 pub const JSR2kr: u8 = 0xee;
1973 pub const STH2kr: u8 = 0xef;
1974 pub const LDZ2kr: u8 = 0xf0;
1975 pub const STZ2kr: u8 = 0xf1;
1976 pub const LDR2kr: u8 = 0xf2;
1977 pub const STR2kr: u8 = 0xf3;
1978 pub const LDA2kr: u8 = 0xf4;
1979 pub const STA2kr: u8 = 0xf5;
1980 pub const DEI2kr: u8 = 0xf6;
1981 pub const DEO2kr: u8 = 0xf7;
1982 pub const ADD2kr: u8 = 0xf8;
1983 pub const SUB2kr: u8 = 0xf9;
1984 pub const MUL2kr: u8 = 0xfa;
1985 pub const DIV2kr: u8 = 0xfb;
1986 pub const AND2kr: u8 = 0xfc;
1987 pub const ORA2kr: u8 = 0xfd;
1988 pub const EOR2kr: u8 = 0xfe;
1989 pub const SFT2kr: u8 = 0xff;
1990
1991 pub const NAMES: [&str; 256] = [
1992 "BRK", "INC", "POP", "NIP", "SWP", "ROT", "DUP", "OVR", "EQU", "NEQ",
1993 "GTH", "LTH", "JMP", "JCN", "JSR", "STH", "LDZ", "STZ", "LDR", "STR",
1994 "LDA", "STA", "DEI", "DEO", "ADD", "SUB", "MUL", "DIV", "AND", "ORA",
1995 "EOR", "SFT", "JCI", "INC2", "POP2", "NIP2", "SWP2", "ROT2", "DUP2",
1996 "OVR2", "EQU2", "NEQ2", "GTH2", "LTH2", "JMP2", "JCN2", "JSR2", "STH2",
1997 "LDZ2", "STZ2", "LDR2", "STR2", "LDA2", "STA2", "DEI2", "DEO2", "ADD2",
1998 "SUB2", "MUL2", "DIV2", "AND2", "ORA2", "EOR2", "SFT2", "JMI", "INCr",
1999 "POPr", "NIPr", "SWPr", "ROTr", "DUPr", "OVRr", "EQUr", "NEQr", "GTHr",
2000 "LTHr", "JMPr", "JCNr", "JSRr", "STHr", "LDZr", "STZr", "LDRr", "STRr",
2001 "LDAr", "STAr", "DEIr", "DEOr", "ADDr", "SUBr", "MULr", "DIVr", "ANDr",
2002 "ORAr", "EORr", "SFTr", "JSI", "INC2r", "POP2r", "NIP2r", "SWP2r",
2003 "ROT2r", "DUP2r", "OVR2r", "EQU2r", "NEQ2r", "GTH2r", "LTH2r", "JMP2r",
2004 "JCN2r", "JSR2r", "STH2r", "LDZ2r", "STZ2r", "LDR2r", "STR2r", "LDA2r",
2005 "STA2r", "DEI2r", "DEO2r", "ADD2r", "SUB2r", "MUL2r", "DIV2r", "AND2r",
2006 "ORA2r", "EOR2r", "SFT2r", "LIT", "INCk", "POPk", "NIPk", "SWPk",
2007 "ROTk", "DUPk", "OVRk", "EQUk", "NEQk", "GTHk", "LTHk", "JMPk", "JCNk",
2008 "JSRk", "STHk", "LDZk", "STZk", "LDRk", "STRk", "LDAk", "STAk", "DEIk",
2009 "DEOk", "ADDk", "SUBk", "MULk", "DIVk", "ANDk", "ORAk", "EORk", "SFTk",
2010 "LIT2", "INC2k", "POP2k", "NIP2k", "SWP2k", "ROT2k", "DUP2k", "OVR2k",
2011 "EQU2k", "NEQ2k", "GTH2k", "LTH2k", "JMP2k", "JCN2k", "JSR2k", "STH2k",
2012 "LDZ2k", "STZ2k", "LDR2k", "STR2k", "LDA2k", "STA2k", "DEI2k", "DEO2k",
2013 "ADD2k", "SUB2k", "MUL2k", "DIV2k", "AND2k", "ORA2k", "EOR2k", "SFT2k",
2014 "LITr", "INCkr", "POPkr", "NIPkr", "SWPkr", "ROTkr", "DUPkr", "OVRkr",
2015 "EQUkr", "NEQkr", "GTHkr", "LTHkr", "JMPkr", "JCNkr", "JSRkr", "STHkr",
2016 "LDZkr", "STZkr", "LDRkr", "STRkr", "LDAkr", "STAkr", "DEIkr", "DEOkr",
2017 "ADDkr", "SUBkr", "MULkr", "DIVkr", "ANDkr", "ORAkr", "EORkr", "SFTkr",
2018 "LIT2r", "INC2kr", "POP2kr", "NIP2kr", "SWP2kr", "ROT2kr", "DUP2kr",
2019 "OVR2kr", "EQU2kr", "NEQ2kr", "GTH2kr", "LTH2kr", "JMP2kr", "JCN2kr",
2020 "JSR2kr", "STH2kr", "LDZ2kr", "STZ2kr", "LDR2kr", "STR2kr", "LDA2kr",
2021 "STA2kr", "DEI2kr", "DEO2kr", "ADD2kr", "SUB2kr", "MUL2kr", "DIV2kr",
2022 "AND2kr", "ORA2kr", "EOR2kr", "SFT2kr",
2023 ];
2024}
2025
2026#[rustfmt::skip]
2032#[doc(hidden)]
2033pub const FIB: &[u8] = &[
2034 op::LIT, 0, op::LIT, 1, op::JSR, op::BRK,
2038
2039 op::DUP, op::LIT, 1, op::GTH, op::JCI, 0,5, op::POP, op::LIT2, 0,1, op::JMP2r, op::LIT, 255, op::ADD, op::DUP, op::LIT, 255, op::ADD, op::LIT2, 1,6, op::JSR2, op::ROT, op::LIT2, 1,6, op::JSR2, op::ADD2, op::JMP2r,
2059];
2060
2061#[cfg(all(feature = "alloc", test))]
2062mod test {
2063 use super::*;
2064
2065 fn decode_op(s: &str) -> Result<u8, &str> {
2067 let (s, ret) =
2068 s.strip_suffix('r').map(|s| (s, true)).unwrap_or((s, false));
2069 let (s, keep) =
2070 s.strip_suffix('k').map(|s| (s, true)).unwrap_or((s, false));
2071 let (s, short) =
2072 s.strip_suffix('2').map(|s| (s, true)).unwrap_or((s, false));
2073 let mode = (u8::from(keep) << 7)
2074 | (u8::from(ret) << 6)
2075 | (u8::from(short) << 5);
2076 let out = match s {
2077 "BRK" => op::BRK,
2078 "JCI" => op::JCI,
2079 "JMI" => op::JMI,
2080 "JSI" => op::JSI,
2081 "LIT" => op::LIT | mode,
2082
2083 "INC" => op::INC | mode,
2084 "POP" => op::POP | mode,
2085 "NIP" => op::NIP | mode,
2086 "SWP" => op::SWP | mode,
2087 "ROT" => op::ROT | mode,
2088 "DUP" => op::DUP | mode,
2089 "OVR" => op::OVR | mode,
2090 "EQU" => op::EQU | mode,
2091 "NEQ" => op::NEQ | mode,
2092 "GTH" => op::GTH | mode,
2093 "LTH" => op::LTH | mode,
2094 "JMP" => op::JMP | mode,
2095 "JCN" => op::JCN | mode,
2096 "JSR" => op::JSR | mode,
2097 "STH" => op::STH | mode,
2098 "LDZ" => op::LDZ | mode,
2099 "STZ" => op::STZ | mode,
2100 "LDR" => op::LDR | mode,
2101 "STR" => op::STR | mode,
2102 "LDA" => op::LDA | mode,
2103 "STA" => op::STA | mode,
2104 "DEI" => op::DEI | mode,
2105 "DEO" => op::DEO | mode,
2106 "ADD" => op::ADD | mode,
2107 "SUB" => op::SUB | mode,
2108 "MUL" => op::MUL | mode,
2109 "DIV" => op::DIV | mode,
2110 "AND" => op::AND | mode,
2111 "ORA" => op::ORA | mode,
2112 "EOR" => op::EOR | mode,
2113 "SFT" => op::SFT | mode,
2114 _ => return Err(s),
2115 };
2116 Ok(out)
2117 }
2118
2119 fn parse_and_test(s: &str) {
2120 let mut mem = UxnMem::new();
2121 let mut vm = Uxn::<backend::Interpreter>::new(&mut mem);
2122 let mut iter = s.split_whitespace();
2123 let mut op = None;
2124 let mut dev = EmptyDevice;
2125 while let Some(i) = iter.next() {
2126 if let Some(s) = i.strip_prefix('#') {
2127 match s.len() {
2128 2 => {
2129 let v = u8::from_str_radix(s, 16).unwrap();
2130 vm.stack.push_byte(v);
2131 }
2132 4 => {
2133 let v = u16::from_str_radix(s, 16).unwrap();
2134 vm.stack.push_short(v);
2135 }
2136 _ => panic!("invalid length for literal: {i:?}"),
2137 }
2138 continue;
2139 } else if i == "(" {
2140 let mut expected: Vec<u8> = vec![];
2141 for s in iter {
2142 if s == ")" {
2143 break;
2144 } else {
2145 expected.push(u8::from_str_radix(s, 16).unwrap());
2146 }
2147 }
2148 vm.ram[0] = op.unwrap();
2149 vm.run(&mut dev, 0);
2150 let mut actual = vec![];
2151 while vm.stack.index != u8::MAX {
2152 actual.push(vm.stack.pop_byte());
2153 }
2154 actual.reverse();
2155 if actual != expected {
2156 panic!(
2157 "failed to execute {:?}: got {actual:2x?}, \
2158 expected {expected:2x?}",
2159 s.trim()
2160 );
2161 }
2162 break;
2163 } else {
2164 op = Some(decode_op(i).unwrap());
2165 }
2166 }
2167 }
2168
2169 #[test]
2170 fn opcodes() {
2171 const TEST_SUITE: &str = "
2172 #01 INC ( 02 )
2173 #0001 INC2 ( 00 02 )
2174 #0001 INC2k ( 00 01 00 02 )
2175 #1234 POP ( 12 )
2176 #1234 POP2 ( )
2177 #1234 POP2k ( 12 34 )
2178 #1234 NIP ( 34 )
2179 #1234 #5678 NIP2 ( 56 78 )
2180 #1234 #5678 NIP2k ( 12 34 56 78 56 78 )
2181 #1234 SWP ( 34 12 )
2182 #1234 SWPk ( 12 34 34 12 )
2183 #1234 #5678 SWP2 ( 56 78 12 34 )
2184 #1234 #5678 SWP2k ( 12 34 56 78 56 78 12 34 )
2185 #1234 #56 ROT ( 34 56 12 )
2186 #1234 #56 ROTk ( 12 34 56 34 56 12 )
2187 #1234 #5678 #9abc ROT2 ( 56 78 9a bc 12 34 )
2188 #1234 #5678 #9abc ROT2k ( 12 34 56 78 9a bc 56 78 9a bc 12 34 )
2189 #1234 DUP ( 12 34 34 )
2190 #12 DUPk ( 12 12 12 )
2191 #1234 DUP2 ( 12 34 12 34 )
2192 #1234 DUP2k ( 12 34 12 34 12 34 )
2193 #1234 OVR ( 12 34 12 )
2194 #1234 OVRk ( 12 34 12 34 12 )
2195 #1234 #5678 OVR2 ( 12 34 56 78 12 34 )
2196 #1234 #5678 OVR2k ( 12 34 56 78 12 34 56 78 12 34 )
2197 #1212 EQU ( 01 )
2198 #1234 EQUk ( 12 34 00 )
2199 #abcd #ef01 EQU2 ( 00 )
2200 #abcd #abcd EQU2k ( ab cd ab cd 01 )
2201 #1212 NEQ ( 00 )
2202 #1234 NEQk ( 12 34 01 )
2203 #abcd #ef01 NEQ2 ( 01 )
2204 #abcd #abcd NEQ2k ( ab cd ab cd 00 )
2205 #1234 GTH ( 00 )
2206 #3412 GTHk ( 34 12 01 )
2207 #3456 #1234 GTH2 ( 01 )
2208 #1234 #3456 GTH2k ( 12 34 34 56 00 )
2209 #0101 LTH ( 00 )
2210 #0100 LTHk ( 01 00 00 )
2211 #0001 #0000 LTH2 ( 00 )
2212 #0001 #0000 LTH2k ( 00 01 00 00 00 )
2213 #1a #2e ADD ( 48 )
2214 #02 #5d ADDk ( 02 5d 5f )
2215 #0001 #0002 ADD2 ( 00 03 )
2216 #10 #02 DIV ( 08 )
2217 #10 #03 DIVk ( 10 03 05 )
2218 #0010 #0000 DIV2 ( 00 00 )
2219 #0120 #0010 DIV2 ( 00 12 )
2220 #0120 #0010 DIV2k ( 01 20 00 10 00 12 )
2221 #34 #10 SFT ( 68 )
2222 #34 #01 SFT ( 1a )
2223 #34 #33 SFTk ( 34 33 30 )
2224 #1248 #34 SFT2k ( 12 48 34 09 20 )
2225 #1248 #34 SFT2 ( 09 20 )
2226 ";
2227 for line in TEST_SUITE.lines() {
2228 parse_and_test(line);
2229 }
2230
2231 #[allow(dead_code)]
2232 const HARD_TESTS: &str = "
2233 LIT 12 ( 12 )
2234 LIT2 abcd ( ab cd )
2235 ,&skip-rel JMP BRK &skip-rel #01 ( 01 )
2236 #abcd #01 ,&pass JCN SWP &pass POP ( ab )
2237 #abcd #00 ,&fail JCN SWP &fail POP ( cd )
2238 ,&routine JSR ( | PC* )
2239 ,&get JSR #01 BRK &get #02 JMP2r ( 02 01 )
2240 #12 STH ( | 12 )
2241 LITr 34 STHr ( 34 )
2242 |00 @cell $2 |0100 .cell LDZ ( 00 )
2243 |00 @cell $2 |0100 #abcd .cell STZ2 { ab cd }
2244 ,cell LDR2 BRK @cell abcd ( ab cd )
2245 #1234 ,cell STR2 BRK @cell $2 ( )
2246 ;cell LDA BRK @cell abcd ( ab )
2247 #abcd ;cell STA BRK @cell $1 ( ab )
2248 ";
2249 }
2250
2251 #[test]
2252 fn fib() {
2253 let mut mem = UxnMem::boxed();
2254 let mut vm = Uxn::<backend::Interpreter>::new(&mut mem);
2255
2256 let mut uxn_fib = |i| {
2257 let _ = vm.reset(FIB);
2258 vm.ram_write_byte(0x101, i);
2259 let mut dev = EmptyDevice;
2260 vm.run(&mut dev, 0x100);
2261 vm.stack_view::<0b001>().pop_short()
2262 };
2263
2264 let fib = |i| -> u32 {
2265 let (mut a, mut b) = (1, 1);
2266 for _ in 2..=i {
2267 (a, b) = (b, a + b)
2268 }
2269 b
2270 };
2271
2272 for i in 0..24 {
2273 assert_eq!(
2274 u16::try_from(fib(i)).unwrap(),
2275 uxn_fib(i),
2276 "failed to compute fib({i})"
2277 );
2278 }
2279 }
2280
2281 #[cfg(not(debug_assertions))]
2283 mod no_panic {
2284 macro_rules! init {
2301 ($vm:ident, $data:ident, $op:ident) => {
2302 struct NoPanic;
2303 unsafe extern "C" {
2304 #[link_name = concat!(stringify!($op), "_may_panic")]
2305 fn trigger() -> !;
2306 }
2307 impl ::core::ops::Drop for NoPanic {
2308 fn drop(&mut self) {
2309 unsafe {
2310 trigger();
2311 }
2312 }
2313 }
2314 let mut ram = UxnMem::boxed();
2315 let mut $vm = Uxn::<backend::Interpreter>::new(&mut ram);
2316 let _ = $vm.reset($data);
2317 for d in $data {
2318 $vm.stack.push(Value::Byte(*d));
2319 $vm.ret.push(Value::Byte(*d));
2320 }
2321 };
2322 }
2323 macro_rules! mode_fns {
2324 ($op:ident) => {
2325 #[test]
2326 fn $op() {
2327 use $op::no_panic;
2328 let data = std::hint::black_box(&[]);
2329 no_panic::<0b000>(data);
2330 no_panic::<0b001>(data);
2331 no_panic::<0b010>(data);
2332 no_panic::<0b011>(data);
2333 no_panic::<0b100>(data);
2334 no_panic::<0b101>(data);
2335 no_panic::<0b110>(data);
2336 no_panic::<0b111>(data);
2337 }
2338 };
2339 }
2340 macro_rules! no_panic {
2341 ($op:ident) => {
2342 mod $op {
2343 use super::*;
2344
2345 #[inline(never)]
2346 pub fn no_panic<const FLAGS: u8>(data: &[u8]) {
2347 let guard = NoPanic;
2348 init!(vm, data, $op);
2349 vm.$op::<FLAGS>(0x100);
2350 core::mem::forget(guard);
2351 }
2352 }
2353 mode_fns!($op);
2354 };
2355 }
2356 macro_rules! no_panic_modeless {
2357 ($op:ident) => {
2358 mod $op {
2359 use super::*;
2360
2361 #[inline(never)]
2362 pub fn no_panic(data: &[u8]) {
2363 let guard = NoPanic;
2364 init!(vm, data, $op);
2365 vm.$op(0x100);
2366 core::mem::forget(guard);
2367 }
2368 }
2369 #[test]
2370 fn $op() {
2371 let data = std::hint::black_box(&[]);
2372 $op::no_panic(data);
2373 }
2374 };
2375 }
2376 macro_rules! no_panic_dev {
2377 ($op:ident) => {
2378 mod $op {
2379 use super::*;
2380
2381 #[inline(never)]
2382 pub fn no_panic<const FLAGS: u8>(data: &[u8]) {
2383 let guard = NoPanic;
2384 init!(vm, data, $op);
2385 let mut dev = EmptyDevice;
2386 vm.$op::<FLAGS>(0x100, &mut dev);
2387 core::mem::forget(guard);
2388 }
2389 }
2390 mode_fns!($op);
2391 };
2392 }
2393
2394 use super::*;
2395
2396 no_panic_modeless!(brk);
2397 no_panic!(inc);
2398 no_panic!(pop);
2399 no_panic!(nip);
2400 no_panic!(swp);
2401 no_panic!(rot);
2402 no_panic!(dup);
2403 no_panic!(ovr);
2404 no_panic!(equ);
2405 no_panic!(neq);
2406 no_panic!(gth);
2407 no_panic!(lth);
2408 no_panic!(jmp);
2409 no_panic!(jcn);
2410 no_panic!(jsr);
2411 no_panic!(sth);
2412 no_panic!(ldz);
2413 no_panic!(stz);
2414 no_panic!(ldr);
2415 no_panic!(str);
2416 no_panic!(lda);
2417 no_panic!(sta);
2418 no_panic_dev!(dei);
2419 no_panic_dev!(deo);
2420 no_panic!(add);
2421 no_panic!(sub);
2422 no_panic!(mul);
2423 no_panic!(div);
2424 no_panic!(and);
2425 no_panic!(ora);
2426 no_panic!(eor);
2427 no_panic!(sft);
2428 no_panic_modeless!(jci);
2429 no_panic_modeless!(jmi);
2430 no_panic_modeless!(jsi);
2431 no_panic!(lit); }
2433}