yaxpeax_nd812/
lib.rs

1//! # `yaxpeax-nd812`, a decoder for the ND812 instruction set
2//!
3//! the ND812 instruction set is used in the Nuclear Data ND812 microcomputer, first introduced in
4//! 1970. the ND812 and associated additional hardware (such as the ND4410) were used for
5//! scientific applications and it seems relatively few programs for this equipment has survived to
6//! the present day.
7//!
8//! interesting for yaxpeax reasons, the ND812 is a 12-bit machine. `yaxpeax-nd812` decodes units
9//! of [`ND812Word`], consulting only the low 12 bits of the contained `u16`. `ND812Word` then
10//! requires an `impl Reader<u16, ND812Word>` to decode from; there is a default impl to read
11//! `ND812Word` from a regular `&[u8]`, but a more comprehensive `ND812` simulator would need to
12//! also reproduce the wrap-at-4k-boundary behavior from the real hardware.
13//!
14//! the actual packing of 12-bit words may also be of interest. i couldn't find any `ND812`
15//! programs online as binary, even as binary images to be loaded by simulators - i couldn't really
16//! find any `ND812` simulators available online either. so my best guess for a reasonable binary
17//! format is to do what people do with PDP-8 (also 12-bit) binary blobs, with 12-bit words in
18//! two-byte units of memory.
19//!
20//! lastly, thank goodness for `bitsavers.org`. i found many of the manuals and documents for the
21//! `ND812` in scattered places online, but bitsavers has them all in one place. the reference
22//! there helped me answer a few questions about missing documents, and led me to finding program
23//! `ND41-1085` in the `IM41-1085` manual for x-ray analysis. it turned out that the best test
24//! cases were reference programs from Nuclear Data themselves.
25//!
26//! reference materials:
27//! ```text
28//! shasum -a 256 IM*
29//! 3a4ccbdd898ff071636d14af908e2386d6204b77d39a546f416f40ad7ad890fa
30//!   IM41-0001_Software_Instruction_Manual_BASC-12_General_Assembler_Jan71.pdf
31//! 39dcb814862c385986aee6ff31b4f0a942e9ff8dcaac0046cbcba55f052d42e5
32//!   IM41-0002-04_Software_Instruction_Manual_ND812_Symbolic_Text_Editor_Nov72.pdf
33//! d5380bed1407566b491d00e654bc7967208fa71ef6daa7ec82e73805f671ff0a
34//!   IM41-0059-00_NUTRAN_User_and_Programmers_Guide_Nov72.pdf
35//! f508a4bb6a834352b1a391ac0dd851201dd6a6a5cfa6eec53aa4c6dbf86e088a
36//!   IM41-1062-00_Software_Instruction_Manual_ND4410_Low_High_Speed_Paper_Tape_IO_Overlay_Program_Apr72.pdf
37//! a1364c23ffadc4414c7b905cfce7cd4c0914a5b0d29b1726246a9d5d68d0aa7a
38//!   IM41-8001-01_Software_Instruction_Manual_ND812_Diagnostics_Feb72.pdf
39//! 62013481aab174473ae1cbaed35d02eb7f22a05acd6c56ae36d166502925cb25
40//!   IM41-8045-00_Software_Instruction_Manual_Hardware_Multipy_Divide_Test_Jun72.pdf
41//! 3cf00d268cab96eebda973b53b870fe761e83d2e61a733860094920b17d84b22
42//!   IM88-0481-02_Hardware_Instruction_Manual_ND812_Teletype_Auto_Loader_Interface_Sep72.pdf
43//! ```
44//!
45//! ## usage
46//!
47//! the fastest way to decode an nd812 instruction is through
48//! [`InstDecoder::decode_slice()`]:
49//! ```
50//! use yaxpeax_nd812::InstDecoder;
51//!
52//! let inst = InstDecoder::decode_u16(&[0o1122]).unwrap();
53//!
54//! assert_eq!("adr j", inst.to_string());
55//! ```
56//!
57//! opcodes and operands are available on the decoded instruction, as well as its length and
58//! operand count:
59//! ```
60//! use yaxpeax_nd812::{InstDecoder, Operand, Opcode};
61//!
62//! let inst = InstDecoder::decode_u16(&[0o1123]).unwrap();
63//!
64//! assert_eq!("sbr j", inst.to_string());
65//! assert_eq!(inst.operand_count(), 2);
66//! assert_eq!(inst.len(), 1);
67//! assert_eq!(inst.opcode(), Opcode::SubR);
68//! assert_eq!(inst.operand(0), Operand::R);
69//! assert_eq!(inst.operand(1), Operand::J);
70//! ```
71//!
72//! additionally, `yaxpeax-nd812` implements `yaxpeax-arch` traits for generic use, such as
73//! [`yaxpeax_arch::LengthedInstruction`]. [`yaxpeax_arch::Arch`] is implemented by
74//! the unit struct [`ND812`]. `yaxpeax-nd812` does not decode from a `U8Reader`, like many other
75//! decodes, but does decode from an `ND812Reader` that can be trivially constructed from a
76//! `U8Reader`.
77//!
78//! ## `#![no_std]`
79//!
80//! `yaxpeax-nd812` should support `no_std` usage, but this is entirely untested.
81
82#![no_std]
83
84mod display;
85
86use yaxpeax_arch::{AddressDiff, Arch, Decoder, LengthedInstruction, Reader, ReadError, U8Reader};
87
88/// a trivial struct for [`yaxpeax_arch::Arch`] to be implemented on. it's only interesting for the
89/// associated type parameters.
90#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone)]
91pub struct ND812;
92
93/// a 12-bit word, as used in the `nd812`.
94#[derive(Copy, Clone, Debug, Hash, Eq, Ord, PartialOrd, PartialEq)]
95#[repr(transparent)]
96pub struct ND812Word(u16);
97
98impl ND812Word {
99    /// get the value of this 12-bit word as a u16
100    pub fn value(&self) -> u16 {
101        self.0
102    }
103
104    /// create an `ND812Word` from a value `v`
105    ///
106    /// returns `None` if `v` is out of range for an `ND812Word` (is larger than `0o7777`, or in
107    /// base 16, `0x0fff`)
108    pub fn new(v: u16) -> Option<Self> {
109        if v > 0o7777 {
110            None
111        } else {
112            Some(ND812Word(v))
113        }
114    }
115}
116
117impl core::fmt::Display for ND812Word {
118    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
119        write!(f, "{}", self.0)
120    }
121}
122
123impl Arch for ND812 {
124    type Address = u16;
125    type Word = ND812Word;
126    type Instruction = Instruction;
127    type Decoder = InstDecoder;
128    type DecodeError = DecodeError;
129    type Operand = Operand;
130}
131
132#[derive(Copy, Clone, PartialEq, Eq, Debug)]
133pub enum DecodeError {
134    /// no input available but the instruction would require at least one more word to decode
135    ExhaustedInput,
136    /// the word(s) to decode this instruction do not map to a defined instruction
137    Undefined,
138}
139
140impl From<yaxpeax_arch::ReadError> for DecodeError {
141    fn from(_e: yaxpeax_arch::ReadError) -> Self {
142        DecodeError::ExhaustedInput
143    }
144}
145
146impl core::fmt::Display for DecodeError {
147    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
148        use yaxpeax_arch::DecodeError;
149        f.write_str(self.description())
150    }
151}
152
153impl yaxpeax_arch::DecodeError for DecodeError {
154    fn data_exhausted(&self) -> bool {
155        *self == DecodeError::ExhaustedInput
156    }
157    fn bad_opcode(&self) -> bool {
158        *self == DecodeError::Undefined
159    }
160    fn bad_operand(&self) -> bool {
161        *self == DecodeError::Undefined
162    }
163    fn description(&self) -> &'static str {
164        match self {
165            DecodeError::ExhaustedInput => "exhausted input",
166            DecodeError::Undefined => "undefined encoding",
167        }
168    }
169}
170
171/// a wrapper describing one of four (three optional) memory fields in an `nd812`. some `nd812`
172/// documentation refers to these as "stacks" of memory.
173#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
174pub struct MemoryField {
175    value: u8
176}
177
178impl MemoryField {
179    fn new(which: u8) -> Self {
180        assert!(which < 4);
181
182        MemoryField { value: which }
183    }
184
185    /// get the bits to select this `field` as encoded in an `nd812` instruction. default field is
186    /// `0`, with possible alternate field `1`, `2`, and `3`. the `nd812` does not support more
187    /// than four total field, so this function will never return a value of 4 or above.
188    pub fn num(&self) -> u8 {
189        self.value
190    }
191}
192
193/// an `nd812` instruction.
194///
195/// `nd812` instructions have an [`Opcode`] and up to three [`Operand`]s. they are one or two
196/// `ND812Word` long.
197#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
198pub struct Instruction {
199    /// the operation of this instruction.
200    opcode: Opcode,
201    operands: [Operand; 3],
202    /// the nd812 "field" this instruction references. if this is present, memory referenced will
203    /// be with respect to this 4096-word field of memory. a `field` of memory, to the `nd812`, is
204    /// a linear region of memory - this is also called a "stack" in some documentation. even so,
205    /// there is no "stack pointer" nor "growth" (upward or downward) notions.
206    ///
207    /// if there is a field selected, and the instruction is `jmp` or `jps`, this field will be
208    /// made default for instructions after this.
209    referenced_field: Option<MemoryField>,
210    length: u8,
211}
212
213impl Default for Instruction {
214    fn default() -> Instruction {
215        Instruction {
216            opcode: Opcode::STOP,
217            operands: [Operand::Nothing, Operand::Nothing, Operand::Nothing],
218            referenced_field: None,
219            length: 0,
220        }
221    }
222}
223
224impl Instruction {
225    fn reset_operands(&mut self) {
226        self.operands = [Operand::Nothing, Operand::Nothing, Operand::Nothing];
227    }
228
229    /// the length of this instruction, in terms of [`ND812Word`].
230    pub fn len(&self) -> u8 {
231        self.length
232    }
233
234    /// get the number of operands in this instruction.
235    ///
236    /// calls to `Instruction::operand` for indices between 0 and this value will return an operand
237    /// other than `Operand::Nothing`.
238    pub fn operand_count(&self) -> u8 {
239        if self.operands[0] == Operand::Nothing {
240            0
241        } else if self.operands[1] == Operand::Nothing {
242            1
243        } else {
244            2
245        }
246    }
247
248    /// get the `Operand` at the provided index.
249    ///
250    /// indices above `3` will always yield `Operand::Nothing`.
251    pub fn operand(&self, idx: u8) -> Operand {
252        self.operands.get(idx as usize).map(|x| *x).unwrap_or(Operand::Nothing)
253    }
254
255    /// get the `Opcode` of this instruction.
256    pub fn opcode(&self) -> Opcode {
257        self.opcode
258    }
259}
260
261impl LengthedInstruction for Instruction {
262    type Unit = AddressDiff<<ND812 as Arch>::Address>;
263    fn min_size() -> Self::Unit {
264        AddressDiff::from_const(1)
265    }
266    fn len(&self) -> Self::Unit {
267        AddressDiff::from_const(self.length as u16)
268    }
269}
270
271impl yaxpeax_arch::Instruction for Instruction {
272    fn well_defined(&self) -> bool { true }
273}
274
275/// an operand for an `nd812` instruction.
276#[derive(Copy, Clone, Hash, PartialEq, Eq)]
277pub enum Operand {
278    /// no operand in this position.
279    ///
280    /// reaching this as a user of `yaxpeax_nd812` is almost certainly a bug. `Instruction::operand`
281    /// will return `None` rather than `Operand::Nothing`.
282    Nothing,
283    /// the `J` register
284    J,
285    /// the `K` register
286    K,
287    /// the `JK` register
288    JK,
289    /// the `R` register
290    R,
291    /// the `S` register
292    S,
293    /// the `RS` register
294    RS,
295    /// the `OverflowBit` register
296    OverflowBit,
297    /// the `FlagBit` register
298    FlagBit,
299    /// memory access to `pc` plus the given signed offset, in the range `[-64, 64]` (inclusive).
300    /// additionally, the displacement may be indirect; `pc + offset` may be used as a pointer.
301    Displacement(bool, i8),
302    /// memory access to the absolute u12 address, in the range `[0, 4095]` (inclusive).
303    /// additionally, the displacement may be indirect; `offset` may be used as a pointer.
304    ///
305    /// in practice, instructions with an `Absolute` operand also may have an alternate field
306    /// selected, so correct interpretation of this operand will need to consult the referenced
307    /// field as well.
308    Absolute(bool, u16),
309    /// a literal value encoded in an instruction (modern instruction sets would call this an
310    /// `immediate`)
311    Literal(u8),
312}
313
314/// an `nd812` instruction's operation.
315#[allow(non_camel_case_types)]
316#[derive(Copy, Clone, Hash, PartialEq, Eq)]
317pub enum Opcode {
318    /// `Stop Execution`
319    STOP,
320    /// `One cycle delay`
321    IDLE,
322    /// `Cassette High-Speed Forward EOT (TWIO)`
323    CHSF,
324    /// `Cassette Space Forward to File Mark (TWIO)`
325    CSPF,
326    /// `Cassette Skip on File Mark (TWIO)`
327    CSFM,
328    /// `Cassette Skip if EOT (TWIO)`
329    CSET,
330    /// `Cassette High-Speed Reverse BOT (TWIO)`
331    CHSR,
332    /// `Cassette Skip No-Error (TWIO)`
333    CSNE,
334    /// `Cassette Skip if On-Line Tape Ready (TWIO)`
335    CSTR,
336    /// `Cassette Skip if BOT (TWIO)`
337    CSBT,
338    /// `Cassette Clear All Flags (TWIO)`
339    CCLF,
340    /// `Cassette Skip if Read Ready (TWIO)`
341    CSRR,
342    /// `Cassette Read to J (TWIO)`
343    CRDT,
344    /// `Cassette Write File Mark (TWIO)`
345    CWFM,
346    /// `Cassette Skip if Write Ready (TWIO)`
347    CSWR,
348    /// `Cassette Write Transfer (TWIO)`
349    CWRT,
350    /// `Two Word Skip if Memory Not Equal`
351    ///
352    /// `yaxpeax-nd812` records the register to operate against as an operand, `Operand::J` or
353    /// `Operand::K`.
354    TWSM,
355    TWDSZ,
356    TWISZ,
357    /// `Two Word Subtract`
358    ///
359    /// `yaxpeax-nd812` records the register to operate against as an operand, `Operand::J` or
360    /// `Operand::K`.
361    TWSB,
362    /// `Two Word Add`
363    ///
364    /// `yaxpeax-nd812` records the register to operate against as an operand, `Operand::J` or
365    /// `Operand::K`.
366    TWAD,
367    /// `Two Word Load`
368    ///
369    /// `yaxpeax-nd812` records the register to operate against as an operand, `Operand::J` or
370    /// `Operand::K`.
371    TWLD,
372    /// `Two Word Store`
373    ///
374    /// `yaxpeax-nd812` records the register to operate against as an operand, `Operand::J` or
375    /// `Operand::K`.
376    TWST,
377    /// `Two Word Unconditional Jump`
378    TWJMP,
379    /// `Two Word Jump Subroutine`
380    TWJPS,
381    /// `Multiply J by K`
382    MPY,
383    /// `Divide J and K by R`
384    DIV,
385    /// `Read Flag, Overflow from J`
386    RFOV,
387    /// `Disable All Interrupt Levels`
388    IOFF,
389    /// `Enable Level H Only`
390    IONH,
391    /// `Enable Interrupt Levels H & A`
392    IONA,
393    /// `Enable Interrupt Levels H & B`
394    IONB,
395    /// `Enable All Interrupt Levels`
396    IONN,
397    /// `Load J from Switches`
398    LJSW,
399    /// `Load J from Status Register`
400    LJST,
401    /// `And J, K, into <op0>`
402    ///
403    /// `yaxpeax-nd812` records the destination as an operand, `Operand::J`, `Operand::K`, or
404    /// `Operand::JK`
405    And,
406    /// `Load <op0> into <op1>`
407    ///
408    /// `yaxpeax-nd812` records the source and destination both as operands. operands will be one
409    /// of the following pairs:
410    /// * `R, J`
411    /// * `J, R`
412    /// * `S, K`
413    /// * `K, S`
414    /// * `K, J`
415    /// * `RS, JK`
416    /// * `JK, RS`
417    Load, // load op[0] from [1]
418    /// `Exchange <op0> and <op1>`
419    ///
420    /// `yaxpeax-nd812` records the source and destination both as operands. operands will be one
421    /// of the following pairs:
422    /// * `J, R`
423    /// * `K, S`
424    /// * `JK, RS`
425    Exchange, // exchange op[0], op[1]
426    /// `J + K to <op0>`
427    ///
428    /// `yaxpeax-nd812` records the destination as an operand, `Operand::J`, `Operand::K`, or
429    /// `Operand::JK`
430    AddJK, // `op[0] + op[1] to op[2]`
431    /// `J - K to <op0>`
432    ///
433    /// `yaxpeax-nd812` records the destination as an operand, `Operand::J` or `Operand::K`
434    SubJK, // `op[0] - op[1] to op[2]`
435    /// `<op0> + <op1> to <op1>`
436    ///
437    /// `yaxpeax-nd812` records the source and destination both as operands:
438    /// * `op0` can be `Operand::R` or `Operand::S`
439    /// * `op1` can be `Operand::J` or `Operand::K`
440    AddR, // `op[0] + op[1] to op[2]`
441    /// `<op0> - <op1> to <op1>`
442    ///
443    /// `yaxpeax-nd812` records the source and destination both as operands:
444    /// * `op0` can be `Operand::R` or `Operand::S`
445    /// * `op1` can be `Operand::J` or `Operand::K`
446    SubR, // `op[0] - op[1] to op[2]`
447    /// `-(J + K) to <op0>`
448    ///
449    /// `yaxpeax-nd812` records the destination as an operand, `Operand::J`, `Operand::K`, or
450    /// `Operand::JK`
451    NegAddJK, // `-(op[0] + op[1]) to op[2]`
452    /// `K - J to <op0>`
453    ///
454    /// `yaxpeax-nd812` records the destination as an operand, `Operand::J` or `Operand::K`
455    NegSubJK, // `(op[1] - op[0]) to op[2]`
456    /// `-(<op0> + <op1>) to <op1>`
457    ///
458    /// `yaxpeax-nd812` records the source and destination both as operands:
459    /// * `op0` can be `Operand::R` or `Operand::S`
460    /// * `op1` can be `Operand::J` or `Operand::K`
461    NegAddR, // `-(op[0] + op[1]) to op[2]`
462    /// `<op1> - <op0> to <op1>`
463    ///
464    /// `yaxpeax-nd812` records the source and destination both as operands:
465    /// * `op0` can be `Operand::R` or `Operand::S`
466    /// * `op1` can be `Operand::J` or `Operand::K`
467    NegSubR, // `(op[1] - op[0]) to op[2]`
468    /// `Shift <op0> left N`
469    ///
470    /// `yaxpeax-nd812` records the register as an operand, either `Operand::J`, `Operand::K`, or
471    /// `Operand::JK`.
472    Shift, // `j <<= N`, what is N?
473    /// `Rotate <op0> left N`
474    ///
475    /// `yaxpeax-nd812` records the register as an operand, either `Operand::J`, `Operand::K`, or
476    /// `Operand::JK`.
477    Rotate, // `j <<= N`, what is N?
478    /// `Skip if Flag Register One`
479    ///
480    /// `yaxpeax-nd812` records the register as an operand, either `Operand::FlagBit` or
481    /// `Operand::Overflow`, or J, K, JK,
482    SNZ,
483    /// `Skip if Flag Register Zero`
484    ///
485    /// `yaxpeax-nd812` records the register as an operand, either `Operand::FlagBit` or
486    /// `Operand::OverflowBit`, or J, K, JK,
487    SIZ,
488    ///
489    /// `yaxpeax-nd812` records the register as an operand, either `Operand::FlagBit` or
490    /// `Operand::OverflowBit`, or J, K, JK,
491    CLR,
492    ///
493    /// `yaxpeax-nd812` records the register as an operand, either `Operand::FlagBit` or
494    /// `Operand::OverflowBit`, or J, K, JK
495    CMP,
496    ///
497    /// `yaxpeax-nd812` records the register as an operand, either `Operand::FlagBit` or
498    /// `Operand::OverflowBit`, or J, K, JK
499    SET,
500    /// `Skip on Power Low`
501    SKPL,
502    /// `Powerfail System On`
503    PION,
504    /// `Powerfail System Off`
505    PIOF,
506    ///
507    /// `yaxpeax-nd812` records the register as an operand, either `Operand::FlagBit` or
508    /// `Operand::OverflowBit`, or J, K, JK
509    SIP,
510    ///
511    /// `yaxpeax-nd812` records the register as an operand, either `Operand::FlagBit` or
512    /// `Operand::OverflowBit`, or J, K, JK
513    INC,
514    ///
515    /// `yaxpeax-nd812` records the register as an operand, either `Operand::FlagBit` or
516    /// `Operand::OverflowBit`, or J, K, JK
517    SIN,
518    ///
519    /// `yaxpeax-nd812` records the register as an operand, either `Operand::FlagBit` or
520    /// `Operand::OverflowBit`, or J, K, JK
521    NEG,
522    /// `AND with J, Forward`
523    ANDF,
524    /// `AND J Literal`
525    ANDL,
526    /// `ADD J Literal`
527    ADDL,
528    /// `SUBTRACT J Literal`
529    SUBL,
530    /// `Skip if J not Equal Memory`
531    SMJ,
532    /// `Decrement Memory and Skip`
533    DSZ,
534    /// `Increment Memory and Skip`
535    ISZ,
536    /// `Subtract from J`
537    SBJ,
538    /// `Add to J`
539    ADJ,
540    /// `Load J`
541    LDJ,
542    /// `Store J`
543    STJ,
544    /// `Unconditional Jump`
545    JMP,
546    /// `Unconditional Skip`
547    SKIP,
548    /// `Jump Subroutine`
549    JPS,
550    /// `Execute Displaced Instruction`
551    XCT,
552    /// `TTY Keyboard-Reader Fetch`
553    TIF,
554    /// `TTY Keyboard Into J`
555    TIR,
556    /// `TIR and TIF combined`
557    TRF,
558    /// `TTY Skip if Keyboard Ready`
559    TIS,
560    /// `Clear printer/punch flag`
561    TOC,
562    /// `Clear printer/punch flag, load printer/punch buffer from J and print/punch`
563    TOP,
564    /// `TOP and TOC combined`
565    TCP,
566    /// `TTY Skip if Printer-Punch Reader`
567    TOS,
568    /// `HS Reader - Fetch`
569    HIF,
570    /// `HS Reader - CLR Flag, Read Buffer`
571    HIR,
572    /// `HIR and HIF combined`
573    HRF,
574    /// `Skip if HS reader flag = 1`
575    HIS,
576    /// `HS Punch - Punch On`
577    HOP,
578    /// `HS Punch - CLR Flag, Load Buffer`
579    HOL,
580    /// `HS Punch - Load and Punch`
581    HLP,
582    /// `HS Punch - Skip if punch ready`
583    HOS,
584    /// `Cassette - Unit 1 On-Line`
585    CSLCT1,
586    /// `Cassette - Unit 2 On-Line`
587    CSLCT2,
588    /// `Cassette - Unit 3 On-Line`
589    CSLCT3,
590    /// `Load JPS Reg from J, INT Reg from K`
591    LDREG,
592    /// `Load JPS Reg to J, INT Reg to K`
593    LDJK,
594    /// `Restore JPS and INT field bits`
595    RJIB,
596}
597
598/// the ND812 uses a modified character set to pack two characters into 12-bit words; each
599/// character is *6* bits.
600///
601/// TODO: not yet sure how this maps to ascii; `Appendix C` describes `A` as `ASCII CODE 301` -
602/// doesn't match as octal or.. anything else. the whole character set is reproduced here, for
603/// reference. this all DOES line up with ascii if ND812 assumes ascii has the high bit set?
604pub const ND812_CHARSET: &[u8] = &[
605    b'A', b'B', b'C', b'D', b'E', b'F',
606    b'G', b'H', b'I', b'J', b'K', b'L',
607    b'M', b'N', b'O', b'P', b'Q', b'R',
608    b'S', b'T', b'U', b'V', b'W', b'X',
609    b'Y', b'Z', b'0', b'1', b'2', b'3',
610    b'4', b'5', b'6', b'7', b'8', b'9',
611    b'$', b'*', b'+', b'!', b'-', b'.',
612    b'/', b';', b'=', b' ', b'\t', b'\n',
613    0x0c, b'\r', 0o377
614];
615
616/// an `nd812` instruction decoder.
617///
618/// there are no decode options for `nd812`, so this is a trivial struct that exists only for the
619/// [`yaxpeax_arch::Decoder`] trait impl.
620#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
621pub struct InstDecoder { }
622
623pub struct ND812Reader<T> {
624    underlying: T,
625    start: u16,
626    mark: u16,
627    offset: u16,
628}
629
630impl<'a> ND812Reader<&'a [u16]> {
631    pub fn of_u16(data: &'a [u16]) -> Self {
632        ND812Reader {
633            underlying: data,
634            start: 0,
635            mark: 0,
636            offset: 0,
637        }
638    }
639}
640
641impl<'a> Reader<u16, ND812Word> for ND812Reader<&'a [u16]> {
642    fn next(&mut self) -> Result<ND812Word, ReadError> {
643        if let Some(word) = self.underlying.get(self.offset as usize) {
644            if word & 0xf000 != 0 {
645                return Err(ReadError::IOError("invalid nd812 word in u16 data: bits in 12-15 are set"));
646            }
647
648            self.offset += 1;
649            Ok(ND812Word(word & 0o7777))
650        } else {
651            Err(ReadError::ExhaustedInput)
652        }
653    }
654
655    fn next_n(&mut self, buf: &mut [ND812Word]) -> Result<(), ReadError> {
656        if buf.len() > self.underlying.len() - self.offset as usize {
657            return Err(ReadError::ExhaustedInput);
658        }
659
660        // there's at least enough data, though some of it could be invalid...
661        // TODO: this will result in an error potentially consuming data without indicating how
662        // much data was consumed. not good!
663        for i in 0..buf.len() {
664            buf[i] = self.next()?;
665        }
666
667        Ok(())
668    }
669
670    fn mark(&mut self) {
671        self.mark = self.offset;
672    }
673
674    fn offset(&mut self) -> u16 {
675        self.offset - self.mark
676    }
677
678    fn total_offset(&mut self) -> u16 {
679        self.offset - self.start
680    }
681}
682
683impl<'a> ND812Reader<U8Reader<'a>> {
684    pub fn of_u8(data: &'a [u8]) -> Self {
685        ND812Reader {
686            underlying: U8Reader::new(data),
687            start: 0,
688            mark: 0,
689            offset: 0,
690        }
691    }
692}
693
694impl<'a> Reader<u16, ND812Word> for ND812Reader<U8Reader<'a>> {
695    fn next(&mut self) -> Result<ND812Word, ReadError> {
696        let high = Reader::<u16, u8>::next(&mut self.underlying)?;
697        let low = Reader::<u16, u8>::next(&mut self.underlying)?;
698        self.offset += 1;
699        Ok(ND812Word(u16::from_le_bytes([low, high])))
700    }
701
702    fn next_n(&mut self, buf: &mut [ND812Word]) -> Result<(), ReadError> {
703        // TODO: this will result in an error potentially consuming data without indicating how
704        // much data was consumed. not good!
705        for i in 0..buf.len() {
706            buf[i] = self.next()?;
707        }
708
709        Ok(())
710    }
711
712    fn mark(&mut self) {
713        Reader::<u16, u8>::mark(&mut self.underlying);
714        self.mark = self.offset;
715    }
716
717    fn offset(&mut self) -> u16 {
718        self.offset - self.mark
719    }
720
721    fn total_offset(&mut self) -> u16 {
722        self.offset - self.start
723    }
724}
725
726/*
727impl Reader<u16, ND812Word> for ND812Reader<U16Reader> {
728}
729*/
730
731impl InstDecoder {
732    /// decode a slice of bytes into an instruction (or error)
733    ///
734    /// this is just a higher-level interface to the [`InstDecoder`] impl of
735    /// [`yaxpeax_arch::Decoder`].
736    pub fn decode_slice(data: &[u8]) -> Result<Instruction, <ND812 as Arch>::DecodeError> {
737        InstDecoder::default()
738            .decode(&mut ND812Reader::of_u8(data))
739    }
740
741    /// decode a slice of `u16` into an instruction (or error)
742    ///
743    /// this is just a higher-level interface to the [`InstDecoder`] impl of
744    /// [`yaxpeax_arch::Decoder`].
745    pub fn decode_u16(data: &[u16]) -> Result<Instruction, <ND812 as Arch>::DecodeError> {
746        InstDecoder::default()
747            .decode(&mut ND812Reader::of_u16(data))
748    }
749}
750
751impl Default for InstDecoder {
752    fn default() -> Self {
753        InstDecoder { }
754    }
755}
756
757impl Decoder<ND812> for InstDecoder {
758    fn decode_into<T: Reader<<ND812 as Arch>::Address, <ND812 as Arch>::Word>>(&self, inst: &mut Instruction, words: &mut T) -> Result<(), <ND812 as Arch>::DecodeError> {
759        inst.length = 0;
760        inst.reset_operands();
761        words.mark();
762        let word = words.next()?;
763
764        let operation = word.0 >> 8;
765
766        if word.0 & 0o7700 == 0 {
767            inst.opcode = Opcode::STOP;
768            inst.length = words.offset() as u8;
769
770            return Ok(());
771        }
772
773        match operation {
774            0b0000 |
775            0b0001 => {
776                // two word instruction
777                let address = words.next()?;
778                let opc = (word.0 >> 5) & 0b0001111;
779                let field = word.0 & 0b11;
780                let change_field = (word.0 & 0b0100) != 0;
781                let kj = (word.0 & 0b1000) != 0;
782                let ind = (word.0 & 0b10000) != 0;
783
784                if word.0 < 0o0240 {
785                    // unallocated
786                    // cassette two-word i/o op
787                } else if word.0 == 0o0740 {
788                    // TWIO (two word i/o)
789                    let opc = address.0;
790
791                    const OPC: &[Option<Opcode>] = &[
792                        None, Some(Opcode::CHSF), Some(Opcode::CSPF), None, Some(Opcode::CSFM), None, None, None,
793                        Some(Opcode::CSET), None, None, None, None, None, None, None,
794                        None, Some(Opcode::CHSR), Some(Opcode::CSNE), None, Some(Opcode::CSTR), None, None, None,
795                        Some(Opcode::CSBT), None, None, None, None, None, None, None,
796                        None, Some(Opcode::CCLF), Some(Opcode::CSRR), None, Some(Opcode::CRDT), None, None, None,
797                        None, Some(Opcode::CWFM), Some(Opcode::CSWR), None, Some(Opcode::CWRT), None, None, None,
798                    ];
799
800                    if opc < 0o100 {
801                        return Err(DecodeError::Undefined);
802                    }
803
804                    let opc = opc - 0o0100;
805                    inst.opcode = *OPC.get(opc as usize).and_then(|x| x.as_ref()).ok_or(DecodeError::Undefined)?;
806                } else {
807                    let opc = opc - 5;
808                    // starts at `0240`
809                    const OPC: &[(Opcode, bool)] = &[
810                        (Opcode::TWSM, true),
811                        (Opcode::TWDSZ, false),
812                        (Opcode::TWISZ, false),
813                        (Opcode::TWSB, true),
814                        (Opcode::TWAD, true),
815                        (Opcode::TWLD, true),
816                        (Opcode::TWST, true),
817                        (Opcode::TWJMP, false),
818                        (Opcode::TWJPS, false),
819                        // nothing for 0700 - would be a two-word xct
820                    ];
821
822                    let (opcode, has_op) = *OPC.get(opc as usize).ok_or(DecodeError::Undefined)?;
823
824                    if change_field {
825                        inst.referenced_field = Some(MemoryField::new(field as u8));
826                    }
827
828                    inst.opcode = opcode;
829
830                    if has_op {
831                        inst.operands[0] = if kj {
832                            Operand::K
833                        } else {
834                            Operand::J
835                        };
836                        inst.operands[1] = Operand::Absolute(ind, address.0);
837                    } else {
838                        inst.operands[0] = Operand::Absolute(ind, address.0);
839                    }
840                }
841            },
842            0b0010 => {
843                // group 1 instructions
844                // ```
845                // op1 = 0010
846                //
847                // |      op1      | j | k | shift |  shift count  |
848                // |               |acc|acc|  rot  |               |
849                // | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11|
850                // ```
851
852                let shift = (word.0 & 0o0017) as u8;
853                let opc = ((word.0 >> 4) & 0o0003) as u8;
854                let kj = ((word.0 >> 6) & 0o0003) as u8;
855
856                let dest = match kj {
857                    0b00 => {
858                        // opcodes like `0o10xx`
859                        // in practice the only instructions here are `0b1000` to `0b1011`
860                        let low = (word.0 & 0o0077) as u8;
861                        const OPC: &[Opcode] = &[
862                            Opcode::MPY,
863                            Opcode::DIV,
864                            Opcode::RFOV,
865                            Opcode::IOFF,
866                            Opcode::IONH,
867                            Opcode::IONB,
868                            Opcode::IONA,
869                            Opcode::IONN,
870                            Opcode::LJSW,
871                            Opcode::LJST,
872                        ];
873                        inst.opcode = *OPC.get(low as usize).ok_or(DecodeError::Undefined)?;
874                        inst.length = words.offset() as u8;
875
876                        return Ok(());
877                    },
878                    0b01 => Operand::J,
879                    0b10 => Operand::K,
880                    _ => Operand::JK,
881                };
882
883                match opc {
884                    0b00 => {
885                        // `0b0010xx00xxxx`
886                        let opc = shift;
887                        if opc == 0o0000 {
888                            inst.opcode = Opcode::And;
889                            inst.operands[0] = dest;
890                        } else if opc == 0o0001 {
891                            // Load (R, J) or (S, K)
892                            inst.opcode = Opcode::Load;
893                            inst.operands[1] = dest;
894                            let source = match dest {
895                                Operand::J => Operand::R,
896                                Operand::K => Operand::S,
897                                _ /* JK */ => Operand::RS,
898                            };
899                            inst.operands[0] = source;
900                        } else if opc == 0o0002 {
901                            // Load (J, R) or (K, S)
902                            inst.opcode = Opcode::Load;
903                            inst.operands[0] = dest;
904                            let source = match dest {
905                                Operand::J => Operand::R,
906                                Operand::K => Operand::S,
907                                _ /* JK */ => Operand::RS,
908                            };
909                            inst.operands[1] = source;
910                        } else if opc == 0o0003 {
911                            // Exchange (J, R) or (K, S)
912                            inst.opcode = Opcode::Exchange;
913                            inst.operands[0] = dest;
914                            let source = match dest {
915                                Operand::J => Operand::R,
916                                Operand::K => Operand::S,
917                                _ /* JK */ => Operand::RS,
918                            };
919                            inst.operands[1] = source;
920                        } else if opc == 0o0004 {
921                            // if dest == K, load K from J, else invalid
922                            if dest == Operand::K {
923                                inst.opcode = Opcode::Load;
924                            } else {
925                                return Err(DecodeError::Undefined);
926                            }
927                        } else {
928                            return Err(DecodeError::Undefined);
929                        }
930                    }
931                    0b01 => {
932                        // `0b0010xx01xxxx`
933                        const OPC: &[Option<(Opcode, Option<Operand>)>] = &[
934                            Some((Opcode::AddJK, None)),
935                            Some((Opcode::SubJK, None)),
936                            Some((Opcode::AddR, Some(Operand::R))), // Add (R, J) -> J
937                            Some((Opcode::SubR, Some(Operand::R))), // Sub (R, J) -> J
938                            Some((Opcode::AddR, Some(Operand::S))), // Add (S, J) -> J
939                            Some((Opcode::SubR, Some(Operand::S))), // Sub (S, J) -> J
940                            None,
941                            None,
942                            Some((Opcode::NegAddJK, None)),
943                            Some((Opcode::NegSubJK, None)),
944                            Some((Opcode::NegAddR, Some(Operand::R))),
945                            Some((Opcode::NegSubR, Some(Operand::R))),
946                            Some((Opcode::NegAddR, Some(Operand::S))),
947                            Some((Opcode::NegSubR, Some(Operand::S))),
948                            None,
949                            None,
950                        ];
951
952                        let (opc, extra_operands) = *OPC.get(shift as usize).and_then(|x| x.as_ref()).ok_or(DecodeError::Undefined)?;
953                        inst.opcode = opc;
954                        if let Some(extra) = extra_operands {
955                            inst.operands[0] = extra;
956                            inst.operands[1] = dest;
957                        } else {
958                            inst.operands[0] = dest;
959                        }
960                    }
961                    0b10 => {
962                        inst.opcode = Opcode::Shift;
963                        inst.operands[0] = dest;
964                        inst.operands[1] = Operand::Literal(shift);
965                    }
966                    // 0b11
967                    _ => {
968                        inst.opcode = Opcode::Rotate;
969                        inst.operands[0] = dest;
970                        inst.operands[1] = Operand::Literal(shift);
971                    }
972                };
973            }
974            // octal codes 1400, 1500, 1600, 1700
975            0b0011 => {
976                // group 2 instructions
977                #[allow(non_upper_case_globals)]
978                const OPC_Flags: &[Option<(Opcode, Operand)>] = &[
979                    Some((Opcode::IDLE, Operand::Nothing)), Some((Opcode::SNZ, Operand::FlagBit)), None, None, None, Some((Opcode::SIZ, Operand::FlagBit)), None, None,
980                    Some((Opcode::CLR, Operand::FlagBit)), None, None, None, None, None, None, None,
981                    Some((Opcode::CMP, Operand::FlagBit)), None, None, None, None, None, None, None,
982                    Some((Opcode::SET, Operand::FlagBit)), None, None, None, None, None, None, None,
983                    Some((Opcode::SKPL, Operand::Nothing)), Some((Opcode::SNZ, Operand::OverflowBit)), Some((Opcode::SKIP, Operand::Nothing)), None, None, Some((Opcode::SIZ, Operand::OverflowBit)), None, None,
984                    Some((Opcode::CLR, Operand::OverflowBit)), None, None, None, None, None, None, None,
985                    Some((Opcode::CMP, Operand::OverflowBit)), None, None, None, None, None, None, None,
986                    Some((Opcode::SET, Operand::OverflowBit)), None, None, None, None, None, None, None,
987                ];
988
989                #[allow(non_upper_case_globals)]
990                const OPC_NotFlags: &[Option<Opcode>] = &[
991                    None, Some(Opcode::SNZ), Some(Opcode::SIP), None, Some(Opcode::INC), Some(Opcode::SIZ), Some(Opcode::SIN), None,
992                    Some(Opcode::CLR), None, None, None, None, None, None, None,
993                    Some(Opcode::CMP), None, None, None, Some(Opcode::NEG), None, None, None,
994                    Some(Opcode::SET), None, None, None, None, None, None, None,
995                ];
996
997                let jk = (word.0 & 0o0300) >> 6;
998                let opc = word.0 & 0o0077;
999
1000                if opc == 0o0000 {
1001                    // idle, pion, piof, or undefined
1002                    const OPS: &[Result<Opcode, DecodeError>] = &[
1003                        Ok(Opcode::IDLE),
1004                        Ok(Opcode::PION),
1005                        Ok(Opcode::PIOF),
1006                        Err(DecodeError::Undefined)
1007                    ];
1008                    inst.opcode = OPS[jk as usize]?;
1009                } else {
1010                    let (opcode, operand) = match jk {
1011                        0b00 => {
1012                            *OPC_Flags.get(opc as usize).and_then(|x| x.as_ref()).ok_or(DecodeError::Undefined)?
1013                        },
1014                        0b01 => {
1015                            (*OPC_NotFlags.get(opc as usize).and_then(|x| x.as_ref()).ok_or(DecodeError::Undefined)?, Operand::J)
1016                        }
1017                        0b10 => {
1018                            (*OPC_NotFlags.get(opc as usize).and_then(|x| x.as_ref()).ok_or(DecodeError::Undefined)?, Operand::K)
1019                        }
1020                        _ => {
1021                            (*OPC_NotFlags.get(opc as usize).and_then(|x| x.as_ref()).ok_or(DecodeError::Undefined)?, Operand::JK)
1022                        }
1023                    };
1024                    inst.opcode = opcode;
1025                    inst.operands[0] = operand;
1026                }
1027            }
1028            // octal code 2000
1029            0b0100 => {
1030                // literal instructions, `andf`, `andl`, ``addl`, subl`
1031                const OPC: &[Opcode] = &[
1032                    Opcode::ANDF,
1033                    Opcode::ANDL,
1034                    Opcode::ADDL,
1035                    Opcode::SUBL,
1036                ];
1037
1038                let opc = (word.0 >> 6) & 0b11;
1039                let literal = (word.0 & 0o0077) as u8;
1040
1041                let opc = OPC[opc as usize];
1042                inst.opcode = opc;
1043                inst.operands = [
1044                    Operand::Literal(literal),
1045                    Operand::Nothing,
1046                    Operand::Nothing,
1047                ];
1048            }
1049            // octal code 7400+
1050            0b1111 => {
1051                // `tif`, `tir`, `trf`, `tis`, ...
1052                let opc = word.0;
1053                if opc < 0o7500 {
1054                    const OPC: &[Option<Opcode>] = &[
1055                        None, Some(Opcode::TIF), Some(Opcode::TIR), Some(Opcode::TRF), Some(Opcode::TIS), None, None, None,
1056                        None, Some(Opcode::TOC), Some(Opcode::TOP), Some(Opcode::TCP), Some(Opcode::TOS), None, None, None,
1057                        None, Some(Opcode::HIS), Some(Opcode::HIR), Some(Opcode::HRF), Some(Opcode::HIS), None, None, None,
1058                        None, Some(Opcode::HOP), Some(Opcode::HOL), Some(Opcode::HLP), Some(Opcode::HOS), None, None, None,
1059                    ];
1060                    let idx = opc - 0o7400;
1061                    inst.opcode = *OPC.get(idx as usize).and_then(|x| x.as_ref()).ok_or(DecodeError::Undefined)?;
1062                } else if opc < 0o7600 {
1063                    return Err(DecodeError::Undefined);
1064                } else if opc < 0o7700 {
1065                    inst.opcode = if opc == 0o7601 {
1066                        Opcode::CSLCT1
1067                    } else if opc == 0o7602 {
1068                        Opcode::CSLCT2
1069                    } else if opc == 0o7604 {
1070                        Opcode::CSLCT3
1071                    } else {
1072                        return Err(DecodeError::Undefined);
1073                    };
1074                } else {
1075                    inst.opcode = if opc == 0o7720 {
1076                        Opcode::LDREG
1077                    } else if opc == 0o7721 {
1078                        Opcode::LDJK
1079                    } else if opc == 0o7722 {
1080                        Opcode::RJIB
1081                    } else {
1082                        return Err(DecodeError::Undefined);
1083                    };
1084                }
1085            }
1086            // remaining instructions are keyed entirely on the upper four bits:
1087            // `smj`, `dsz`, `isz`, `sbj`, `adj`, `ldj`, `stj`, `jmp`, `jps`, `xct`.
1088            // this set of instructions starts at 0o2400.
1089            other => {
1090                const OPC: &[Opcode] = &[
1091                    Opcode::SMJ,
1092                    Opcode::DSZ,
1093                    Opcode::ISZ,
1094                    Opcode::SBJ,
1095                    Opcode::ADJ,
1096                    Opcode::LDJ,
1097                    Opcode::STJ,
1098                    Opcode::JMP,
1099                    Opcode::JPS,
1100                    Opcode::XCT,
1101                ];
1102
1103                let offset = (word.0 & 0o0077) as i8;
1104                let negative = (word.0 >> 6) & 1;
1105                let indirect = (word.0 >> 7) & 1;
1106                let offset = if negative == 1 {
1107                    -offset
1108                } else {
1109                    offset
1110                };
1111
1112                let opc = OPC[(other - (0o2400 >> 8)) as usize];
1113                inst.opcode = opc;
1114                inst.operands = [
1115                    Operand::Displacement(indirect == 1, offset),
1116                    Operand::Nothing,
1117                    Operand::Nothing,
1118                ];
1119            }
1120        }
1121
1122        inst.length = words.offset() as u8;
1123        Ok(())
1124    }
1125}