Skip to main content

raven_uxn/
lib.rs

1//! Uxn virtual machine
2#![cfg_attr(not(test), no_std)]
3#![warn(missing_docs)]
4#![cfg_attr(not(any(test, feature = "native")), forbid(unsafe_code))]
5// Nightly features for tailcall implementation
6#![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// stub module to improve quality of error messages
22#[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
44/// Size of a device in port memory
45pub const DEV_SIZE: usize = 16;
46
47/// Simple circular stack, with room for 256 items
48#[derive(Debug, Eq, PartialEq)]
49pub struct Stack<'a> {
50    data: &'a mut [u8; 256],
51
52    /// The index points to the last occupied slot, and increases on `push`
53    ///
54    /// If the buffer is empty or full, it points to `u8::MAX`.
55    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
72/// Trait for a Uxn evaluator
73pub trait Backend: Sized {
74    /// Run the VM with the selected backend
75    fn run<D: Device>(vm: &mut Uxn<Self>, dev: &mut D, pc: u16) -> u16;
76}
77
78/// Available backends
79///
80/// Types in this module should be used as template parameters to the [`Uxn`]
81/// object.
82pub mod backend {
83    use super::*;
84
85    /// Bytecode interpreter
86    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    /// Hand-written threaded assembly
102    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    /// Tail-call interpreter
113    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
126/// Virtual stack, which is aware of `keep` and `short` modes
127///
128/// This type expects the user to perform all of their `pop()` calls first,
129/// followed by any `push(..)` calls.  `pop()` will either adjust the true index
130/// or a virtual index, depending on whether `keep` is set.
131struct StackView<'a, 'b, const FLAGS: u8> {
132    stack: &'a mut Stack<'b>,
133
134    /// Virtual index, used in `keep` mode
135    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    /// Pops a single value from the stack
145    ///
146    /// Returns a [`Value::Short`] if `self.short` is set, and a [`Value::Byte`]
147    /// otherwise.
148    ///
149    /// If `self.keep` is set, then only the view offset ([`StackView::offset`])
150    /// is changed; otherwise, the stack index ([`Stack::index`]) is changed.
151    #[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    /// Replaces the top item on the stack with the given value
193    #[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    /// Peeks at a byte from the data stack
309    #[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    /// Returns the number of items in the stack
322    #[inline(always)]
323    pub fn len(&self) -> u8 {
324        self.index.wrapping_add(1)
325    }
326
327    /// Checks whether the stack is empty
328    #[inline(always)]
329    pub fn is_empty(&self) -> bool {
330        self.len() == 0
331    }
332
333    /// Sets the number of items in the stack
334    #[inline(always)]
335    pub fn set_len(&mut self, n: u8) {
336        self.index = n.wrapping_sub(1);
337    }
338}
339
340/// The virtual machine itself
341pub 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    /// Build a new `Uxn` with zeroed memory
348    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    /// Runs the VM starting at the given address until it terminates
356    #[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
375/// Core implementation of the virtual machine
376///
377/// This is necessary because the tail-call interpreter must deconstruct the
378/// `UxnCore` into its component pieces, which requires ownership; the [`Uxn`]
379/// contains an `Option<UxnCore>` which we can steal then replace.
380pub struct UxnCore<'a> {
381    /// Device memory
382    dev: &'a mut [u8; 256],
383    /// 64 KiB of VM memory
384    ram: &'a mut [u8; 65536],
385    /// 256-byte data stack
386    stack: Stack<'a>,
387    /// 256-byte return stack
388    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/// Container with all of the memory needed to run a [`Uxn`] CPU
427#[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    /// Builds a new set of arrays, which are all zeroed
437    ///
438    /// This builds a large object on the stack, which is inadvisable; you
439    /// should instead place it in static memory or (if the `alloc` feature is
440    /// enabled) use [`UxnMem::boxed`] to build it on the heap instead.
441    #[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    /// Builds a new boxed [`UxnMem`] on the heap
452    #[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    /// Build a new `Uxn` with zeroed memory
460    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    /// Reads a byte from RAM at the program counter
477    #[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    /// Reads a word from RAM at the program counter
485    #[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    /// Reads a word from RAM
539    ///
540    /// If the address is at the top of RAM, the second byte will wrap to 0
541    #[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    /// Writes to the given address in device memory
549    #[inline(always)]
550    pub fn write_dev_mem(&mut self, addr: u8, value: u8) {
551        self.dev[usize::from(addr)] = value;
552    }
553
554    /// Converts raw ports memory into a [`Ports`] object
555    #[inline(always)]
556    pub fn dev<D: Ports>(&self) -> &D {
557        self.dev_at(D::BASE)
558    }
559
560    /// Returns a shared reference to a device located at `pos`
561    #[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    /// Returns an exclusive reference to a device located at `pos`
568    #[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    /// Returns a mutable reference to the given [`Ports`] object
576    #[inline(always)]
577    pub fn dev_mut<D: Ports>(&mut self) -> &mut D {
578        self.dev_mut_at(D::BASE)
579    }
580
581    /// Reads a byte from RAM
582    #[inline(always)]
583    pub fn ram_read_byte(&self, addr: u16) -> u8 {
584        self.ram[usize::from(addr)]
585    }
586
587    /// Writes a byte to RAM
588    #[inline(always)]
589    pub fn ram_write_byte(&mut self, addr: u16, v: u8) {
590        self.ram[usize::from(addr)] = v;
591    }
592
593    /// Shared borrow of the working stack
594    #[inline(always)]
595    pub fn stack(&self) -> &Stack<'a> {
596        &self.stack
597    }
598
599    /// Mutable borrow of the working stack
600    #[inline(always)]
601    pub fn stack_mut(&mut self) -> &mut Stack<'a> {
602        &mut self.stack
603    }
604
605    /// Shared borrow of the return stack
606    #[inline(always)]
607    pub fn ret(&self) -> &Stack<'_> {
608        &self.ret
609    }
610
611    /// Mutable borrow of the return stack
612    #[inline(always)]
613    pub fn ret_mut(&mut self) -> &mut Stack<'a> {
614        &mut self.ret
615    }
616
617    /// Resets system memory and loads the given ROM at address `0x100`
618    ///
619    /// Returns trailing ROM data (or an empty slice), which should be loaded
620    /// into extension memory.
621    #[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    /// Asserts that the given [`Ports`] object is of size [`DEV_SIZE`]
633    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    /// Executes a single operation
644    #[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    /// Computes a jump, either relative (signed) or absolute
907    #[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    /// Break
919    /// ```text
920    /// BRK --
921    /// ```
922    ///
923    /// Ends the evaluation of the current vector. This opcode has no modes.
924    #[inline]
925    pub fn brk(&mut self, _: u16) -> Option<u16> {
926        None
927    }
928
929    /// Jump Conditional Instant
930    ///
931    /// ```text
932    /// JCI cond8 --
933    /// ```
934    ///
935    /// Pops a byte from the working stack and if it is not zero, moves
936    /// the `PC` to a relative address at a distance equal to the next short in
937    /// memory, otherwise moves `PC+2`. This opcode has no modes.
938    #[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    /// Jump Instant
948    ///
949    /// JMI  -- Moves the PC to a relative address at a distance equal to the
950    /// next short in memory. This opcode has no modes.
951    #[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    /// Jump Stash Return Instant
958    ///
959    /// ```text
960    /// JSI  --
961    /// ```
962    ///
963    /// Pushes `PC+2` to the return-stack and moves the `PC` to a relative
964    /// address at a distance equal to the next short in memory. This opcode has
965    /// no modes.
966    #[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    /// Literal
974    ///
975    /// ```text
976    /// LIT -- a
977    /// ```
978    ///
979    /// Pushes the next bytes in memory, and moves the `PC+2`. The `LIT` opcode
980    /// always has the keep mode active. Notice how the `0x00` opcode, with the
981    /// keep bit toggled, is the location of the literal opcodes.
982    ///
983    /// ```text
984    /// LIT 12          ( 12 )
985    /// LIT2 abcd       ( ab cd )
986    /// ```
987    #[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    /// Increment
999    ///
1000    /// ```text
1001    /// INC a -- a+1
1002    /// ```
1003    ///
1004    /// Increments the value at the top of the stack, by 1.
1005    ///
1006    /// ```text
1007    /// #01 INC         ( 02 )
1008    /// #0001 INC2      ( 00 02 )
1009    /// #0001 INC2k     ( 00 01 00 02 )
1010    /// ```
1011    #[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    /// Pop
1020    ///
1021    /// ```text
1022    /// POP a --
1023    /// ```
1024    ///
1025    /// Removes the value at the top of the stack.
1026    ///
1027    /// ```text
1028    /// #1234 POP    ( 12 )
1029    /// #1234 POP2   ( )
1030    /// #1234 POP2k  ( 12 34 )
1031    /// ```
1032    #[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    /// Nip
1039    ///
1040    /// ```text
1041    /// NIP a b -- b
1042    /// ```
1043    ///
1044    /// Removes the second value from the stack. This is practical to convert a
1045    /// short into a byte.
1046    ///
1047    /// ```text
1048    /// #1234 NIP          ( 34 )
1049    /// #1234 #5678 NIP2   ( 56 78 )
1050    /// #1234 #5678 NIP2k  ( 12 34 56 78 56 78 )
1051    /// ```
1052    #[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    /// Swap
1062    ///
1063    /// ```text
1064    /// SWP a b -- b a
1065    /// ```
1066    ///
1067    /// Exchanges the first and second values at the top of the stack.
1068    ///
1069    /// ```text
1070    /// #1234 SWP          ( 34 12 )
1071    /// #1234 SWPk         ( 12 34 34 12 )
1072    /// #1234 #5678 SWP2   ( 56 78 12 34 )
1073    /// #1234 #5678 SWP2k  ( 12 34 56 78 56 78 12 34 )
1074    /// ```
1075    #[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    /// Rotate
1086    ///
1087    /// ```text
1088    /// ROT a b c -- b c a
1089    /// ```
1090    ///
1091    /// Rotates three values at the top of the stack, to the left, wrapping
1092    /// around.
1093    ///
1094    /// ```text
1095    /// #1234 #56 ROT            ( 34 56 12 )
1096    /// #1234 #56 ROTk           ( 12 34 56 34 56 12 )
1097    /// #1234 #5678 #9abc ROT2   ( 56 78 9a bc 12 34 )
1098    /// #1234 #5678 #9abc ROT2k  ( 12 34 56 78 9a bc 56 78 9a bc 12 34 )
1099    /// ```
1100    #[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    /// Duplicate
1113    ///
1114    /// ```text
1115    /// DUP a -- a a
1116    /// ```
1117    ///
1118    /// Duplicates the value at the top of the stack.
1119    ///
1120    /// ```text
1121    /// #1234 DUP   ( 12 34 34 )
1122    /// #12 DUPk    ( 12 12 12 )
1123    /// #1234 DUP2  ( 12 34 12 34 )
1124    /// ```
1125    #[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    /// Over
1135    ///
1136    /// ```text
1137    /// OVR a b -- a b a
1138    /// ```
1139    ///
1140    /// Duplicates the second value at the top of the stack.
1141    ///
1142    /// ```text
1143    /// #1234 OVR          ( 12 34 12 )
1144    /// #1234 OVRk         ( 12 34 12 34 12 )
1145    /// #1234 #5678 OVR2   ( 12 34 56 78 12 34 )
1146    /// #1234 #5678 OVR2k  ( 12 34 56 78 12 34 56 78 12 34 )
1147    /// ```
1148    #[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    /// Equal
1160    ///
1161    /// ```text
1162    /// EQU a b -- bool8
1163    /// ```
1164    ///
1165    /// Pushes `01` to the stack if the two values at the top of the stack are
1166    /// equal, `00` otherwise.
1167    ///
1168    /// ```text
1169    /// #1212 EQU          ( 01 )
1170    /// #1234 EQUk         ( 12 34 00 )
1171    /// #abcd #ef01 EQU2   ( 00 )
1172    /// #abcd #abcd EQU2k  ( ab cd ab cd 01 )
1173    /// ```
1174    #[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    /// Not Equal
1181    ///
1182    /// ```text
1183    /// NEQ a b -- bool8
1184    /// ```
1185    ///
1186    /// Pushes `01` to the stack if the two values at the top of the stack are
1187    /// not equal, `00` otherwise.
1188    ///
1189    /// ```text
1190    /// #1212 NEQ          ( 00 )
1191    /// #1234 NEQk         ( 12 34 01 )
1192    /// #abcd #ef01 NEQ2   ( 01 )
1193    /// #abcd #abcd NEQ2k  ( ab cd ab cd 00 )
1194    /// ```
1195    #[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    /// Greater Than
1202    ///
1203    /// ```text
1204    /// GTH a b -- bool8
1205    /// ```
1206    ///
1207    /// Pushes `01` to the stack if the second value at the top of the stack is
1208    /// greater than the value at the top of the stack, `00` otherwise.
1209    ///
1210    /// ```text
1211    /// #1234 GTH          ( 00 )
1212    /// #3412 GTHk         ( 34 12 01 )
1213    /// #3456 #1234 GTH2   ( 01 )
1214    /// #1234 #3456 GTH2k  ( 12 34 34 56 00 )
1215    /// ```
1216    #[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    /// Lesser Than
1223    ///
1224    /// ```text
1225    /// LTH a b -- bool8
1226    /// ```
1227    ///
1228    /// Pushes `01` to the stack if the second value at the top of the stack is
1229    /// lesser than the value at the top of the stack, `00` otherwise.
1230    ///
1231    /// ```text
1232    /// #0101 LTH          ( 00 )
1233    /// #0100 LTHk         ( 01 00 00 )
1234    /// #0001 #0000 LTH2   ( 00 )
1235    /// #0001 #0000 LTH2k  ( 00 01 00 00 00 )
1236    /// ```
1237    #[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    /// Jump
1244    ///
1245    /// ```text
1246    /// JMP addr --
1247    /// ```
1248    ///
1249    /// Moves the PC by a relative distance equal to the signed byte on the top
1250    /// of the stack, or to an absolute address in short mode.
1251    ///
1252    /// ```text
1253    /// ,&skip-rel JMP BRK &skip-rel #01  ( 01 )
1254    /// ```
1255    #[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    /// Jump Conditional
1262    ///
1263    /// ```text
1264    /// JCN cond8 addr --
1265    /// ```
1266    ///
1267    /// If the byte preceeding the address is not `00`, moves the `PC` by a
1268    /// signed value equal to the byte on the top of the stack, or to an
1269    /// absolute address in short mode.
1270    ///
1271    /// ```text
1272    /// #abcd #01 ,&pass JCN SWP &pass POP  ( ab )
1273    /// #abcd #00 ,&fail JCN SWP &fail POP  ( cd )
1274    /// ```
1275    #[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    /// Jump Stash Return
1288    ///
1289    /// ```text
1290    /// JSR addr -- | ret16
1291    /// ```
1292    ///
1293    /// Pushes the `PC` to the return-stack and moves the `PC` by a signed value
1294    /// equal to the byte on the top of the stack, or to an absolute address in
1295    /// short mode.
1296    ///
1297    /// ```text
1298    /// ,&routine JSR                     ( | PC* )
1299    /// ,&get JSR #01 BRK &get #02 JMP2r  ( 02 01 )
1300    /// ```
1301    #[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    /// Stash
1309    ///
1310    /// ```text
1311    /// STH a -- | a
1312    /// ```
1313    ///
1314    /// Moves the value at the top of the stack to the return stack. Note that
1315    /// with the `r`-mode, the stacks are exchanged and the value is moved from
1316    /// the return stack to the working stack.
1317    ///
1318    /// ```text
1319    /// #12 STH       ( | 12 )
1320    /// LITr 34 STHr  ( 34 )
1321    /// ```
1322    #[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    /// Load Zero-Page
1330    ///
1331    /// ```text
1332    /// LDZ addr8 -- value
1333    /// ```
1334    /// Pushes the value at an address within the first 256 bytes of memory, to
1335    /// the top of the stack.
1336    ///
1337    /// ```text
1338    /// |00 @cell $2 |0100 .cell LDZ ( 00 )
1339    /// ```
1340    #[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    /// Store Zero-Page
1349    ///
1350    /// ```text
1351    /// STZ val addr8 --
1352    /// ```
1353    /// Writes a value to an address within the first 256 bytes of memory.
1354    ///
1355    /// ```text
1356    /// |00 @cell $2 |0100 #abcd .cell STZ2  { ab cd }
1357    /// ```
1358    #[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    /// Load Relative
1368    ///
1369    /// ```text
1370    /// LDR addr8 -- value
1371    /// ```
1372    ///
1373    /// Pushes a value at a relative address in relation to the PC, within a
1374    /// range between -128 and +127 bytes, to the top of the stack.
1375    ///
1376    /// ```text
1377    /// ,cell LDR2 BRK @cell abcd  ( ab cd )
1378    /// ```
1379    #[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    /// Store Relative
1389    ///
1390    /// ```text
1391    /// STR val addr8 --
1392    /// ```
1393    ///
1394    /// Writes a value to a relative address in relation to the PC, within a
1395    /// range between -128 and +127 bytes.
1396    ///
1397    /// ```text
1398    /// #1234 ,cell STR2 BRK @cell $2  ( )
1399    /// ```
1400    #[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    /// Load Absolute
1411    ///
1412    /// ```text
1413    /// LDA addr16 -- value
1414    /// ```
1415    ///
1416    /// Pushes the value at a absolute address, to the top of the stack.
1417    ///
1418    /// ```text
1419    /// ;cell LDA BRK @cell abcd ( ab )
1420    /// ```
1421    #[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    /// Store Absolute
1430    ///
1431    /// ```text
1432    /// STA val addr16 --
1433    /// ```
1434    ///
1435    /// Writes a value to a absolute address.
1436    ///
1437    /// ```text
1438    /// #abcd ;cell STA BRK @cell $1 ( ab )
1439    /// ```
1440    #[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    /// Device Input
1450    ///
1451    /// ```text
1452    /// DEI device8 -- value
1453    /// ```
1454    ///
1455    /// Pushes a value from the device page, to the top of the stack. The target
1456    /// device might capture the reading to trigger an I/O event.
1457    #[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        // For compatibility with the C implementation, we'll
1467        // pre-emtively push a dummy value here, then use `emplace` to
1468        // replace it afterwards.  This is because the C implementation
1469        // `uxn.c` reserves stack space before calling `emu_deo/dei`,
1470        // which affects the behavior of `System.rst/wst`
1471        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    /// Device Output
1489    ///
1490    /// ```text
1491    /// DEO val device8 --
1492    /// ```
1493    ///
1494    /// Writes a value to the device page. The target device might capture the
1495    /// writing to trigger an I/O event.
1496    #[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    /// Add
1523    ///
1524    /// ```text
1525    /// ADD a b -- a+b
1526    /// ```
1527    /// Pushes the sum of the two values at the top of the stack.
1528    ///
1529    /// ```text
1530    /// #1a #2e ADD       ( 48 )
1531    /// #02 #5d ADDk      ( 02 5d 5f )
1532    /// #0001 #0002 ADD2  ( 00 03 )
1533    /// ```
1534    #[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    /// Subtract
1541    ///
1542    /// ```text
1543    /// SUB a b -- a-b
1544    /// ```
1545    ///
1546    /// Pushes the difference of the first value minus the second, to the top of
1547    /// the stack.
1548    #[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    /// Multiply
1555    ///
1556    /// ```text
1557    /// MUL a b -- a*b
1558    /// ```
1559    ///
1560    /// Pushes the product of the first and second values at the top of the
1561    /// stack.
1562    #[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    /// Divide
1569    ///
1570    /// ```text
1571    /// DIV a b -- a/b
1572    /// ```
1573    ///
1574    /// Pushes the quotient of the first value over the second, to the top of
1575    /// the stack. A division by zero pushes zero on the stack. The rounding
1576    /// direction is toward zero.
1577    ///
1578    /// ```text
1579    /// #10 #02 DIV       ( 08 )
1580    /// #10 #03 DIVk      ( 10 03 05 )
1581    /// #0010 #0000 DIV2  ( 00 00 )
1582    /// ```
1583    #[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    /// And
1590    ///
1591    /// ```text
1592    /// AND a b -- a&b
1593    /// ```
1594    ///
1595    /// Pushes the result of the bitwise operation `AND`, to the top of the
1596    /// stack.
1597    #[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    /// Or
1604    ///
1605    /// ```text
1606    /// ORA a b -- a|b
1607    /// ```
1608    /// Pushes the result of the bitwise operation `OR`, to the top of the stack.
1609    #[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    /// Exclusive Or
1616    ///
1617    /// ```text
1618    /// EOR a b -- a^b
1619    /// ```
1620    ///
1621    /// Pushes the result of the bitwise operation `XOR`, to the top of the
1622    /// stack.
1623    #[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    /// Shift
1630    ///
1631    /// ```text
1632    /// SFT a shift8 -- c
1633    /// ```
1634    ///
1635    /// Shifts the bits of the second value of the stack to the left or right,
1636    /// depending on the control value at the top of the stack. The high nibble of
1637    /// the control value indicates how many bits to shift left, and the low nibble
1638    /// how many bits to shift right. The rightward shift is done first.
1639    ///
1640    /// ```text
1641    /// #34 #10 SFT        ( 68 )
1642    /// #34 #01 SFT        ( 1a )
1643    /// #34 #33 SFTk       ( 34 33 30 )
1644    /// #1248 #34 SFT2k    ( 12 48 34 09 20 )
1645    /// ```
1646    #[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
1658/// Trait for a Uxn-compatible device
1659pub trait Device {
1660    /// Performs the `DEI` operation for the given target
1661    ///
1662    /// This function must write its output byte to `vm.dev[target]`; the CPU
1663    /// evaluation loop will then copy this value to the stack.
1664    fn dei(&mut self, vm: &mut UxnCore, target: u8);
1665
1666    /// Performs the `DEO` operation on the given target
1667    ///
1668    /// The input byte will be written to `vm.dev[target]` before this function
1669    /// is called, and can be read by the function.
1670    ///
1671    /// Returns `true` if the CPU should keep running, `false` if it should
1672    /// exit.
1673    #[must_use]
1674    fn deo(&mut self, vm: &mut UxnCore, target: u8) -> bool;
1675}
1676
1677impl<'a> Uxn<'a, backend::Interpreter> {
1678    /// Runs until the program terminates or we hit a stop condition
1679    ///
1680    /// Returns the new program counter if the program terminated, or `None` if
1681    /// the stop condition was reached.
1682    ///
1683    /// This function always uses the interpreter backend.
1684    #[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
1705/// Trait for a type which can be cast to a device ports `struct`
1706pub trait Ports:
1707    zerocopy::IntoBytes
1708    + zerocopy::FromBytes
1709    + zerocopy::FromZeros
1710    + zerocopy::Immutable
1711    + zerocopy::KnownLayout
1712{
1713    /// Base address of the port, of the form `0xA0`
1714    const BASE: u8;
1715}
1716
1717/// Device which does nothing
1718pub struct EmptyDevice;
1719impl Device for EmptyDevice {
1720    fn dei(&mut self, _vm: &mut UxnCore, _target: u8) {
1721        // nothing to do here
1722    }
1723    fn deo(&mut self, _vm: &mut UxnCore, _target: u8) -> bool {
1724        // nothing to do here, keep running
1725        true
1726    }
1727}
1728
1729////////////////////////////////////////////////////////////////////////////////
1730
1731/// Opcode names and constants
1732#[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/// Hand-crafted recursive Fibonacci bytecode, for tests and benchmarks
2027///
2028/// After using [`Uxn::reset`] to load this into to RAM (at address 0x100), set
2029/// the recursion depth by editing `ram[0x101]`.  This uses absolute jumps and
2030/// will not work at other addresses.
2031#[rustfmt::skip]
2032#[doc(hidden)]
2033pub const FIB: &[u8] = &[
2034    op::LIT, 0,     // load the argument (replace this)
2035    op::LIT, 1,     // jump amount
2036    op::JSR,        // jump into the function
2037    op::BRK,
2038
2039    // Function begins here with a single-byte argument ( n )
2040    op::DUP,                    // ( n n )
2041    op::LIT, 1,                 // ( n n #01 )
2042    op::GTH,                    // ( n n>1 )
2043    op::JCI, 0,5,               // ( n ) skip return if non-zero
2044    op::POP,                    // ( )
2045    op::LIT2, 0,1,              // ( #0001 )
2046    op::JMP2r,                  // return
2047
2048    // recursive section
2049    op::LIT, 255, op::ADD,      // ( n-1 )
2050    op::DUP,                    // ( n-1 n-1 )
2051    op::LIT, 255, op::ADD,      // ( n-1 n-2 )
2052    op::LIT2, 1,6, op::JSR2,    // recursive call
2053                                // ( n-1 fib(n-2) )
2054    op::ROT,                    // ( fib(n-2) n-1 )
2055    op::LIT2, 1,6, op::JSR2,    // recursive call
2056                                // ( fib(n-2) fib(n-1) )
2057    op::ADD2,                   // ( fib(n) )
2058    op::JMP2r,
2059];
2060
2061#[cfg(all(feature = "alloc", test))]
2062mod test {
2063    use super::*;
2064
2065    /// Simple parser for textual opcodes
2066    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    // The optimizer is not strong enough to eliminate panics in debug builds!
2282    #[cfg(not(debug_assertions))]
2283    mod no_panic {
2284        // Here's the big idea:
2285        //
2286        // We define a `struct NoPanic` which calls an `extern "C"` function
2287        // when dropped.  Importantly, that C function doesn't exist!  We
2288        // construct an instance of that `struct` as a guard, then call our
2289        // potentially-panicking operation.  After that call completes, we
2290        // `forget` the guard.
2291        //
2292        // If no panics are possible, then the guard is _always_ forgotten, the
2293        // C function is never called, and we don't try to link against it.
2294        //
2295        // However, if panics _are_ possible, then the unwinding has to drop the
2296        // guard, which tries to call that C function.  This fails at link time,
2297        // because the function doesn't exist.
2298        //
2299        // This is borrowed from [no-panic](https://github.com/dtolnay/no-panic)
2300        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); // doesn't use KEEP, but that's fine
2432    }
2433}