1#![allow(clippy::wrong_self_convention)]
2
3use super::instruction;
4use crate::{instructions::control, primitives::Spec, Host, Interpreter};
5use std::boxed::Box;
6
7pub type Instruction<H> = fn(&mut Interpreter, &mut H);
9
10pub type InstructionTable<H> = [Instruction<H>; 256];
12
13pub type DynInstruction<'a, H> = dyn Fn(&mut Interpreter, &mut H) + 'a;
15
16pub type BoxedInstruction<'a, H> = Box<DynInstruction<'a, H>>;
18
19pub type BoxedInstructionTable<'a, H> = [BoxedInstruction<'a, H>; 256];
21
22pub enum InstructionTables<'a, H: ?Sized> {
26 Plain(InstructionTable<H>),
27 Boxed(BoxedInstructionTable<'a, H>),
28}
29
30impl<H: Host + ?Sized> InstructionTables<'_, H> {
31 #[inline]
33 pub const fn new_plain<SPEC: Spec>() -> Self {
34 Self::Plain(make_instruction_table::<H, SPEC>())
35 }
36}
37
38impl<'a, H: Host + ?Sized + 'a> InstructionTables<'a, H> {
39 #[inline]
41 pub fn insert(&mut self, opcode: u8, instruction: Instruction<H>) {
42 match self {
43 Self::Plain(table) => table[opcode as usize] = instruction,
44 Self::Boxed(table) => table[opcode as usize] = Box::new(instruction),
45 }
46 }
47
48 #[inline]
51 pub fn to_boxed(&mut self) -> &mut BoxedInstructionTable<'a, H> {
52 self.to_boxed_with(|i| Box::new(i))
53 }
54
55 #[inline]
58 pub fn to_boxed_with<F>(&mut self, f: F) -> &mut BoxedInstructionTable<'a, H>
59 where
60 F: FnMut(Instruction<H>) -> BoxedInstruction<'a, H>,
61 {
62 match self {
63 Self::Plain(_) => self.to_boxed_with_slow(f),
64 Self::Boxed(boxed) => boxed,
65 }
66 }
67
68 #[cold]
69 fn to_boxed_with_slow<F>(&mut self, f: F) -> &mut BoxedInstructionTable<'a, H>
70 where
71 F: FnMut(Instruction<H>) -> BoxedInstruction<'a, H>,
72 {
73 let Self::Plain(table) = self else {
74 unreachable!()
75 };
76 *self = Self::Boxed(make_boxed_instruction_table(table, f));
77 let Self::Boxed(boxed) = self else {
78 unreachable!()
79 };
80 boxed
81 }
82
83 #[inline]
85 pub fn get_boxed(&mut self, opcode: u8) -> &mut BoxedInstruction<'a, H> {
86 &mut self.to_boxed()[opcode as usize]
87 }
88
89 #[inline]
91 pub fn insert_boxed(&mut self, opcode: u8, instruction: BoxedInstruction<'a, H>) {
92 *self.get_boxed(opcode) = instruction;
93 }
94
95 #[inline]
98 pub fn replace_boxed(
99 &mut self,
100 opcode: u8,
101 instruction: BoxedInstruction<'a, H>,
102 ) -> BoxedInstruction<'a, H> {
103 core::mem::replace(self.get_boxed(opcode), instruction)
104 }
105
106 #[inline]
108 pub fn update_boxed<F>(&mut self, opcode: u8, f: F)
109 where
110 F: Fn(&DynInstruction<'a, H>, &mut Interpreter, &mut H) + 'a,
111 {
112 update_boxed_instruction(self.get_boxed(opcode), f)
113 }
114
115 #[inline]
117 pub fn update_all<F>(&mut self, f: F)
118 where
119 F: Fn(&DynInstruction<'a, H>, &mut Interpreter, &mut H) + Copy + 'a,
120 {
121 match self {
123 Self::Plain(_) => {
124 self.to_boxed_with(|prev| Box::new(move |i, h| f(&prev, i, h)));
125 }
126 Self::Boxed(boxed) => boxed
127 .iter_mut()
128 .for_each(|instruction| update_boxed_instruction(instruction, f)),
129 }
130 }
131}
132
133#[inline]
135pub const fn make_instruction_table<H: Host + ?Sized, SPEC: Spec>() -> InstructionTable<H> {
136 const {
137 let mut tables: InstructionTable<H> = [control::unknown; 256];
138 let mut i = 0;
139 while i < 256 {
140 tables[i] = instruction::<H, SPEC>(i as u8);
141 i += 1;
142 }
143 tables
144 }
145}
146
147#[inline]
149pub fn make_boxed_instruction_table<'a, H, FN>(
150 table: &InstructionTable<H>,
151 mut f: FN,
152) -> BoxedInstructionTable<'a, H>
153where
154 H: Host + ?Sized,
155 FN: FnMut(Instruction<H>) -> BoxedInstruction<'a, H>,
156{
157 core::array::from_fn(|i| f(table[i]))
158}
159
160#[inline]
162pub fn update_boxed_instruction<'a, H, F>(instruction: &mut BoxedInstruction<'a, H>, f: F)
163where
164 H: Host + ?Sized + 'a,
165 F: Fn(&DynInstruction<'a, H>, &mut Interpreter, &mut H) + 'a,
166{
167 let prev = core::mem::replace(instruction, Box::new(|_, _| {}));
169 *instruction = Box::new(move |i, h| f(&prev, i, h));
170}