1use std::fmt::Debug;
2
3use derive_more::UpperHex;
4use strum_macros::{Display, EnumDiscriminants, EnumIter};
5use thiserror::Error;
6use ux::{u12, u4};
7
8#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
10pub struct Register(pub u4);
11
12impl From<Register> for usize {
13 fn from(register: Register) -> Self {
14 usize::try_from(register.0).unwrap()
15 }
16}
17
18#[derive(Copy, Clone, Debug, PartialEq, Eq)]
20pub enum Byte {
21 Register(Register),
22 Immediate(u8),
23}
24
25#[derive(UpperHex)]
26#[upper_hex(fmt = "UpperHex")]
27enum Address {
28 Address(u12),
29 LongAddress(u16),
30}
31
32impl Default for Byte {
34 fn default() -> Self {
35 Self::Immediate(u8::default())
37 }
38}
39
40impl From<Byte> for u8 {
41 fn from(byte: Byte) -> Self {
42 match byte {
43 Byte::Register(Register(x)) => u8::from(x),
44 Byte::Immediate(x) => x,
45 }
46 }
47}
48
49#[non_exhaustive]
55#[derive(EnumIter, EnumDiscriminants, Copy, Clone, Debug, Display, PartialEq, Eq)]
56#[strum_discriminants(derive(Display))]
57pub enum Instruction {
58 Halt,
62 Exit(Option<u8>),
68 ScrollUp(u4),
74 ScrollDown(u4),
79 ScrollRight,
81 ScrollLeft,
83 Clear,
85 Return,
87 ToggleLoadStoreQuirk,
89 LoRes,
91 HiRes,
93 CallMachineCode(u12),
95 Jump(u12),
97 Call(u12),
99 SkipIfEqual(Register, Byte),
101 SkipIfNotEqual(Register, Byte),
102 Add(Register, Byte),
103 Set(Register, Byte),
104 Or(Register, Register),
105 And(Register, Register),
106 Xor(Register, Register),
107 Sub(Register, Register),
108 ShiftRight(Register, Register),
109 ShiftLeft(Register, Register),
110 SubReverse(Register, Register),
111 SetIndex(u12),
112 SetIndexLong(u16),
113 JumpRelative(u12),
114 Random(Register, u8),
115 Draw(Register, Register, u4),
117 SkipKey(Register),
118 SkipNotKey(Register),
119 LoadAudio,
120 LoadDelay(Register),
121 BlockKey(Register),
122 SelectPlane(u4),
123 SetPitch(Register),
124 SetDelay(Register),
125 SetSound(Register),
126 AddRegisterToIndex(Register),
127 FontCharacter(Register),
128 BigFontCharacter(Register),
129 Bcd(Register),
130 Store(Register),
131 Load(Register),
132 StoreRange(Register, Register),
133 LoadRange(Register, Register),
134 StoreFlags(Register),
135 LoadFlags(Register),
136}
137
138#[non_exhaustive]
139#[derive(EnumIter, Copy, Clone, Debug, Display, UpperHex, PartialEq, Eq)]
140#[upper_hex(fmt = "UpperHex")]
141pub enum Opcode {
142 Opcode(u16),
143 LongOpcode(u32),
144}
145
146impl From<Instruction> for Opcode {
147 fn from(instruction: Instruction) -> Opcode {
148 match instruction {
149 Instruction::Halt => Opcode::Opcode(0x0000),
150 Instruction::Exit(None) => Opcode::Opcode(0x00FD),
151 Instruction::Exit(Some(n)) => Opcode::Opcode(0x0010 | u16::from(n)),
152 Instruction::ScrollDown(n) => Opcode::Opcode(0x00C0 | u16::from(n)),
153 Instruction::ScrollUp(n) => Opcode::Opcode(0x00D0 | u16::from(n)),
154 Instruction::ToggleLoadStoreQuirk => Opcode::Opcode(0x00FA),
155 Instruction::ScrollRight => Opcode::Opcode(0x00FB),
156 Instruction::ScrollLeft => Opcode::Opcode(0x00FC),
157 Instruction::LoRes => Opcode::Opcode(0x00FE),
158 Instruction::HiRes => Opcode::Opcode(0x00FF),
159 Instruction::Clear => Opcode::Opcode(0x00E0),
160 Instruction::Return => Opcode::Opcode(0x00EE),
161 Instruction::CallMachineCode(nnn) => Opcode::Opcode(u16::from(nnn)),
162 Instruction::Jump(nnn) => Opcode::Opcode(0x1000 | u16::from(nnn)),
163 Instruction::Call(nnn) => Opcode::Opcode(0x2000 | u16::from(nnn)),
164 Instruction::SkipIfEqual(Register(x), Byte::Immediate(kk)) => {
165 Opcode::Opcode(0x3000 | (u16::from(x) << 8) | u16::from(kk))
166 }
167 Instruction::SkipIfEqual(Register(x), Byte::Register(Register(y))) => {
168 Opcode::Opcode(0x5000 | (u16::from(x) << 8) | (u16::from(y) << 4))
169 }
170 Instruction::SkipIfNotEqual(Register(x), Byte::Immediate(kk)) => {
171 Opcode::Opcode(0x4000 | (u16::from(x) << 8) | u16::from(kk))
172 }
173 Instruction::SkipIfNotEqual(Register(x), Byte::Register(Register(y))) => {
174 Opcode::Opcode(0x9000 | (u16::from(x) << 8) | (u16::from(y) << 4))
175 }
176 Instruction::Set(Register(x), Byte::Immediate(kk)) => {
177 Opcode::Opcode(0x6000 | (u16::from(x) << 8) | u16::from(kk))
178 }
179 Instruction::Add(Register(x), Byte::Immediate(kk)) => {
180 Opcode::Opcode(0x7000 | (u16::from(x) << 8) | u16::from(kk))
181 }
182 Instruction::Set(Register(x), Byte::Register(Register(y))) => {
183 Opcode::Opcode(0x8000 | (u16::from(x) << 8) | (u16::from(y) << 4))
184 }
185 Instruction::Add(Register(x), Byte::Register(Register(y))) => {
186 Opcode::Opcode(0x8004 | (u16::from(x) << 8) | (u16::from(y) << 4))
187 }
188 Instruction::Or(Register(x), Register(y)) => {
189 Opcode::Opcode(0x8001 | (u16::from(x) << 8) | (u16::from(y) << 4))
190 }
191 Instruction::And(Register(x), Register(y)) => {
192 Opcode::Opcode(0x8002 | (u16::from(x) << 8) | (u16::from(y) << 4))
193 }
194 Instruction::Xor(Register(x), Register(y)) => {
195 Opcode::Opcode(0x8003 | (u16::from(x) << 8) | (u16::from(y) << 4))
196 }
197 Instruction::Sub(Register(x), Register(y)) => {
198 Opcode::Opcode(0x8005 | (u16::from(x) << 8) | (u16::from(y) << 4))
199 }
200 Instruction::ShiftRight(Register(x), Register(y)) => {
201 Opcode::Opcode(0x8006 | (u16::from(x) << 8) | (u16::from(y) << 4))
202 }
203 Instruction::ShiftLeft(Register(x), Register(y)) => {
204 Opcode::Opcode(0x800E | (u16::from(x) << 8) | (u16::from(y) << 4))
205 }
206 Instruction::SubReverse(Register(x), Register(y)) => {
207 Opcode::Opcode(0x8007 | (u16::from(x) << 8) | (u16::from(y) << 4))
208 }
209 Instruction::SetIndex(nnn) => Opcode::Opcode(0xA000 | u16::from(nnn)),
210 Instruction::SetIndexLong(nnnn) => Opcode::LongOpcode(0xF000_0000 | u32::from(nnnn)),
211 Instruction::JumpRelative(nnn) => Opcode::Opcode(0xB000 | u16::from(nnn)),
212 Instruction::Random(Register(x), kk) => {
213 Opcode::Opcode(0xC000 | (u16::from(x) << 8) | u16::from(kk))
214 }
215 Instruction::Draw(Register(x), Register(y), n) => {
216 Opcode::Opcode(0xD000 | (u16::from(x) << 8) | (u16::from(y) << 4) | u16::from(n))
217 }
218 Instruction::SkipKey(Register(x)) => Opcode::Opcode(0xE09E | (u16::from(x) << 8)),
219 Instruction::SkipNotKey(Register(x)) => Opcode::Opcode(0xE0A1 | (u16::from(x) << 8)),
220 Instruction::LoadAudio => Opcode::Opcode(0xF002),
221 Instruction::LoadDelay(Register(x)) => Opcode::Opcode(0xF007 | (u16::from(x) << 8)),
222 Instruction::BlockKey(Register(x)) => Opcode::Opcode(0xF00A | (u16::from(x) << 8)),
223 Instruction::SelectPlane(n) => Opcode::Opcode(0xF001 | (u16::from(n) << 8)),
224 Instruction::SetPitch(Register(x)) => Opcode::Opcode(0xF03A | (u16::from(x) << 8)),
225 Instruction::SetDelay(Register(x)) => Opcode::Opcode(0xF015 | (u16::from(x) << 8)),
226 Instruction::SetSound(Register(x)) => Opcode::Opcode(0xF018 | (u16::from(x) << 8)),
227 Instruction::AddRegisterToIndex(Register(x)) => {
228 Opcode::Opcode(0xF01E | (u16::from(x) << 8))
229 }
230 Instruction::FontCharacter(Register(x)) => Opcode::Opcode(0xF029 | (u16::from(x) << 8)),
231 Instruction::BigFontCharacter(Register(x)) => {
232 Opcode::Opcode(0xF030 | (u16::from(x) << 8))
233 }
234 Instruction::Bcd(Register(x)) => Opcode::Opcode(0xF033 | (u16::from(x) << 8)),
235 Instruction::Store(Register(x)) => Opcode::Opcode(0xF055 | (u16::from(x) << 8)),
236 Instruction::Load(Register(x)) => Opcode::Opcode(0xF065 | (u16::from(x) << 8)),
237 Instruction::StoreFlags(Register(x)) => Opcode::Opcode(0xF075 | (u16::from(x) << 8)),
238 Instruction::LoadFlags(Register(x)) => Opcode::Opcode(0xF085 | (u16::from(x) << 8)),
239 Instruction::StoreRange(Register(x), Register(y)) => {
240 Opcode::Opcode(0x5002 | (u16::from(x) << 8) | (u16::from(y) << 4))
241 }
242 Instruction::LoadRange(Register(x), Register(y)) => {
243 Opcode::Opcode(0x5003 | (u16::from(x) << 8) | (u16::from(y) << 4))
244 }
245 }
246 }
247}
248
249#[derive(Error)]
250pub enum DecodeError {
251 #[error("unknown opcode: {0}")]
252 UnknownOpcodeError(Opcode),
253 #[error("incomplete opcode: {0}")]
254 IncompleteLongOpcodeError(InstructionDiscriminants, Box<dyn Fn(u16) -> Instruction>),
255}
256
257impl Debug for DecodeError {
258 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
259 match self {
260 Self::UnknownOpcodeError(arg0) => {
261 f.debug_tuple("UnknownOpcodeError").field(arg0).finish()
262 }
263 Self::IncompleteLongOpcodeError(arg0, arg1) => f
264 .debug_tuple("IncompleteLongOpcodeError")
265 .field(arg0)
266 .finish(),
267 }
268 }
269}
270
271impl TryFrom<Opcode> for Instruction {
272 type Error = DecodeError;
273
274 fn try_from(op: Opcode) -> Result<Self, Self::Error> {
275 match op {
276 Opcode::LongOpcode(opcode) => {
277 let prefix = u16::try_from(opcode >> 16).unwrap();
278 let suffix = u16::try_from(opcode & 0x0000_FFFF).unwrap();
279 if prefix == 0xF000 {
280 Ok(Instruction::SetIndexLong(suffix))
281 } else {
282 Err(DecodeError::UnknownOpcodeError(op))
283 }
285 }
286 Opcode::Opcode(opcode) => {
287 let x = u4::try_from((opcode & 0x0F00) >> 8).unwrap();
288 let y = u4::try_from((opcode & 0x00F0) >> 4).unwrap();
289 let nnn = u12::try_from(opcode & 0x0FFF).unwrap();
290 let kk = (opcode & 0x00FF) as u8;
291 let n = u4::try_from(opcode & 0x000F).unwrap();
292
293 let op1 = (opcode & 0xF000) >> 12;
294 let op2 = (opcode & 0x0F00) >> 8;
295 let op3 = (opcode & 0x00F0) >> 4;
296 let op4 = opcode & 0x000F;
297
298 Ok(
299 match (op1, op2, op3, op4) {
300 #![allow(clippy::match_same_arms)]
301 (0x0, 0x0, 0x0, 0x0) => Instruction::Halt,
302 (0x0, 0x0, 0x1, _n) => Instruction::Exit(Some(op4 as u8)),
303 (0x0, 0x0, 0xB, _n) => Instruction::ScrollUp(n),
304 (0x0, 0x0, 0xC, _n) => Instruction::ScrollDown(n),
305 (0x0, 0x0, 0xD, _n) => Instruction::ScrollUp(n),
306 (0x0, 0x0, 0xE, 0x0) => Instruction::Clear,
307 (0x0, 0x0, 0xE, 0xE) => Instruction::Return,
308 (0x0, 0x0, 0xF, 0xA) => Instruction::ToggleLoadStoreQuirk,
309 (0x0, 0x0, 0xF, 0xB) => Instruction::ScrollRight,
310 (0x0, 0x0, 0xF, 0xC) => Instruction::ScrollLeft,
311 (0x0, 0x0, 0xF, 0xD) => Instruction::Exit(None),
312 (0x0, 0x0, 0xF, 0xE) => Instruction::LoRes,
313 (0x0, 0x0, 0xF, 0xF) => Instruction::HiRes,
314 (0x0, _, _, _) => Instruction::CallMachineCode(nnn),
315 (0x1, _, _, _) => Instruction::Jump(nnn),
316 (0x2, _, _, _) => Instruction::Call(nnn),
317 (0x3, _x, _, _) => {
318 Instruction::SkipIfEqual(Register(x), Byte::Immediate(kk))
319 }
320 (0x4, _x, _, _) => {
321 Instruction::SkipIfNotEqual(Register(x), Byte::Immediate(kk))
322 }
323 (0x5, _x, _y, 0x0) => {
324 Instruction::SkipIfEqual(Register(x), Byte::Register(Register(y)))
325 }
326 (0x5, _x, _y, 0x2) => Instruction::StoreRange(Register(x), Register(y)),
327 (0x5, _x, _y, 0x3) => Instruction::LoadRange(Register(x), Register(y)),
328 (0x6, _x, _, _) => Instruction::Set(Register(x), Byte::Immediate(kk)),
329 (0x7, _x, _, _) => Instruction::Add(Register(x), Byte::Immediate(kk)),
330 (0x8, _x, _y, 0) => {
331 Instruction::Set(Register(x), Byte::Register(Register(y)))
332 }
333 (0x8, _x, _y, 1) => Instruction::Or(Register(x), Register(y)),
334 (0x8, _x, _y, 2) => Instruction::And(Register(x), Register(y)),
335 (0x8, _x, _y, 3) => Instruction::Xor(Register(x), Register(y)),
336 (0x8, _x, _y, 4) => {
337 Instruction::Add(Register(x), Byte::Register(Register(y)))
338 }
339 (0x8, _x, _y, 5) => Instruction::Sub(Register(x), Register(y)),
340 (0x8, _x, _y, 0x6) => Instruction::ShiftRight(Register(x), Register(y)),
341 (0x8, _x, _y, 0x7) => Instruction::SubReverse(Register(x), Register(y)),
342 (0x8, _x, _y, 0xE) => Instruction::ShiftLeft(Register(x), Register(y)),
343 (0x9, _x, _y, 0x0) => {
344 Instruction::SkipIfNotEqual(Register(x), Byte::Register(Register(y)))
345 }
346 (0xA, _, _, _) => Instruction::SetIndex(nnn),
347 (0xB, _, _, _) => Instruction::JumpRelative(nnn),
348 (0xC, _x, _, _) => Instruction::Random(Register(x), kk),
349 (0xD, _x, _y, _n) => Instruction::Draw(Register(x), Register(y), n),
350 (0xE, _x, 0x9, 0xE) => Instruction::SkipKey(Register(x)),
351 (0xE, _x, 0xA, 0x1) => Instruction::SkipNotKey(Register(x)),
352 (0xF, 0x0, 0x0, 0x0) => {
353 return Err(DecodeError::IncompleteLongOpcodeError(
354 InstructionDiscriminants::SetIndexLong,
355 Box::new(Instruction::SetIndexLong),
356 ));
357 }
359 (0xF, 0x0, 0x0, 0x2) => Instruction::LoadAudio,
360 (0xF, _x, 0x0, 0x7) => Instruction::LoadDelay(Register(x)),
361 (0xF, _x, 0x0, 0xA) => Instruction::BlockKey(Register(x)),
362 (0xF, _n, 0x0, 0x1) => Instruction::SelectPlane(x),
363 (0xF, _x, 0x1, 0x5) => Instruction::SetDelay(Register(x)),
364 (0xF, _x, 0x1, 0x8) => Instruction::SetSound(Register(x)),
365 (0xF, _x, 0x1, 0xE) => Instruction::AddRegisterToIndex(Register(x)),
366 (0xF, _x, 0x2, 0x9) => Instruction::FontCharacter(Register(x)),
367 (0xF, _x, 0x3, 0x0) => Instruction::BigFontCharacter(Register(x)),
368 (0xF, _x, 0x3, 0x3) => Instruction::Bcd(Register(x)),
369 (0xF, _x, 0x3, 0xA) => Instruction::SetPitch(Register(x)),
370 (0xF, _x, 0x5, 0x5) => Instruction::Store(Register(x)),
371 (0xF, _x, 0x6, 0x5) => Instruction::Load(Register(x)),
372 (0xF, _x, 0x7, 0x5) => Instruction::StoreFlags(Register(x)),
373 (0xF, _x, 0x8, 0x5) => Instruction::LoadFlags(Register(x)),
374 _ => return Err(DecodeError::UnknownOpcodeError(op)), },
376 )
377 }
378 }
379 }
380}