Skip to main content

k580_core/
decode.rs

1use crate::{DecodeError, InstructionTiming};
2
3#[derive(Clone, Debug, PartialEq, Eq)]
4pub struct InstructionInfo {
5    pub opcode: u8,
6    pub mnemonic: String,
7    pub size: u8,
8    pub timing: InstructionTiming,
9}
10
11const REGS: [&str; 8] = ["B", "C", "D", "E", "H", "L", "M", "A"];
12const RPS: [&str; 4] = ["B", "D", "H", "SP"];
13const CONDS: [&str; 8] = ["NZ", "Z", "NC", "C", "PO", "PE", "P", "M"];
14
15pub const fn is_undocumented_opcode(opcode: u8) -> bool {
16    matches!(
17        opcode,
18        0x08 | 0x10 | 0x18 | 0x20 | 0x28 | 0x30 | 0x38 | 0xCB | 0xD9 | 0xDD | 0xED | 0xFD
19    )
20}
21
22pub fn decode_opcode(opcode: u8) -> Result<InstructionInfo, DecodeError> {
23    if is_undocumented_opcode(opcode) {
24        return Err(DecodeError::UndocumentedOpcode(opcode));
25    }
26
27    if (0x40..=0x7F).contains(&opcode) {
28        return Ok(if opcode == 0x76 {
29            info(opcode, "HLT", 1, InstructionTiming::fixed(7))
30        } else {
31            let dst = ((opcode >> 3) & 7) as usize;
32            let src = (opcode & 7) as usize;
33            let cycles = if dst == 6 || src == 6 { 7 } else { 5 };
34            info(
35                opcode,
36                format!("MOV {},{}", REGS[dst], REGS[src]),
37                1,
38                InstructionTiming::fixed(cycles),
39            )
40        });
41    }
42
43    if (0x80..=0xBF).contains(&opcode) {
44        let src = (opcode & 7) as usize;
45        let cycles = if src == 6 { 7 } else { 4 };
46        let family = match (opcode >> 3) & 7 {
47            0 => "ADD",
48            1 => "ADC",
49            2 => "SUB",
50            3 => "SBB",
51            4 => "ANA",
52            5 => "XRA",
53            6 => "ORA",
54            _ => "CMP",
55        };
56        return Ok(info(
57            opcode,
58            format!("{} {}", family, REGS[src]),
59            1,
60            InstructionTiming::fixed(cycles),
61        ));
62    }
63
64    if opcode & 0xC7 == 0x04 {
65        let reg = ((opcode >> 3) & 7) as usize;
66        return Ok(info(
67            opcode,
68            format!("INR {}", REGS[reg]),
69            1,
70            InstructionTiming::fixed(if reg == 6 { 10 } else { 5 }),
71        ));
72    }
73    if opcode & 0xC7 == 0x05 {
74        let reg = ((opcode >> 3) & 7) as usize;
75        return Ok(info(
76            opcode,
77            format!("DCR {}", REGS[reg]),
78            1,
79            InstructionTiming::fixed(if reg == 6 { 10 } else { 5 }),
80        ));
81    }
82    if opcode & 0xC7 == 0x06 {
83        let reg = ((opcode >> 3) & 7) as usize;
84        return Ok(info(
85            opcode,
86            format!("MVI {},d8", REGS[reg]),
87            2,
88            InstructionTiming::fixed(if reg == 6 { 10 } else { 7 }),
89        ));
90    }
91    if opcode & 0xCF == 0x01 {
92        let rp = ((opcode >> 4) & 3) as usize;
93        return Ok(info(
94            opcode,
95            format!("LXI {},d16", RPS[rp]),
96            3,
97            InstructionTiming::fixed(10),
98        ));
99    }
100    if opcode & 0xCF == 0x03 {
101        let rp = ((opcode >> 4) & 3) as usize;
102        return Ok(info(
103            opcode,
104            format!("INX {}", RPS[rp]),
105            1,
106            InstructionTiming::fixed(5),
107        ));
108    }
109    if opcode & 0xCF == 0x09 {
110        let rp = ((opcode >> 4) & 3) as usize;
111        return Ok(info(
112            opcode,
113            format!("DAD {}", RPS[rp]),
114            1,
115            InstructionTiming::fixed(10),
116        ));
117    }
118    if opcode & 0xCF == 0x0B {
119        let rp = ((opcode >> 4) & 3) as usize;
120        return Ok(info(
121            opcode,
122            format!("DCX {}", RPS[rp]),
123            1,
124            InstructionTiming::fixed(5),
125        ));
126    }
127    if opcode & 0xC7 == 0xC0 {
128        let cond = ((opcode >> 3) & 7) as usize;
129        return Ok(info(
130            opcode,
131            format!("R{}", CONDS[cond]),
132            1,
133            InstructionTiming::conditional(11, 5),
134        ));
135    }
136    if opcode & 0xC7 == 0xC2 {
137        let cond = ((opcode >> 3) & 7) as usize;
138        return Ok(info(
139            opcode,
140            format!("J{} a16", CONDS[cond]),
141            3,
142            InstructionTiming::conditional(10, 10),
143        ));
144    }
145    if opcode & 0xC7 == 0xC4 {
146        let cond = ((opcode >> 3) & 7) as usize;
147        return Ok(info(
148            opcode,
149            format!("C{} a16", CONDS[cond]),
150            3,
151            InstructionTiming::conditional(17, 11),
152        ));
153    }
154    if opcode & 0xC7 == 0xC7 {
155        let n = (opcode >> 3) & 7;
156        return Ok(info(
157            opcode,
158            format!("RST {}", n),
159            1,
160            InstructionTiming::fixed(11),
161        ));
162    }
163    if opcode & 0xCF == 0xC1 {
164        return Ok(info(
165            opcode,
166            format!("POP {}", stack_pair(opcode)),
167            1,
168            InstructionTiming::fixed(10),
169        ));
170    }
171    if opcode & 0xCF == 0xC5 {
172        return Ok(info(
173            opcode,
174            format!("PUSH {}", stack_pair(opcode)),
175            1,
176            InstructionTiming::fixed(11),
177        ));
178    }
179
180    Ok(match opcode {
181        0x00 => info(opcode, "NOP", 1, InstructionTiming::fixed(4)),
182        0x02 => info(opcode, "STAX B", 1, InstructionTiming::fixed(7)),
183        0x07 => info(opcode, "RLC", 1, InstructionTiming::fixed(4)),
184        0x0A => info(opcode, "LDAX B", 1, InstructionTiming::fixed(7)),
185        0x0F => info(opcode, "RRC", 1, InstructionTiming::fixed(4)),
186        0x12 => info(opcode, "STAX D", 1, InstructionTiming::fixed(7)),
187        0x17 => info(opcode, "RAL", 1, InstructionTiming::fixed(4)),
188        0x1A => info(opcode, "LDAX D", 1, InstructionTiming::fixed(7)),
189        0x1F => info(opcode, "RAR", 1, InstructionTiming::fixed(4)),
190        0x22 => info(opcode, "SHLD a16", 3, InstructionTiming::fixed(16)),
191        0x27 => info(opcode, "DAA", 1, InstructionTiming::fixed(4)),
192        0x2A => info(opcode, "LHLD a16", 3, InstructionTiming::fixed(16)),
193        0x2F => info(opcode, "CMA", 1, InstructionTiming::fixed(4)),
194        0x32 => info(opcode, "STA a16", 3, InstructionTiming::fixed(13)),
195        0x37 => info(opcode, "STC", 1, InstructionTiming::fixed(4)),
196        0x3A => info(opcode, "LDA a16", 3, InstructionTiming::fixed(13)),
197        0x3F => info(opcode, "CMC", 1, InstructionTiming::fixed(4)),
198        0xC3 => info(opcode, "JMP a16", 3, InstructionTiming::fixed(10)),
199        0xC6 => info(opcode, "ADI d8", 2, InstructionTiming::fixed(7)),
200        0xC9 => info(opcode, "RET", 1, InstructionTiming::fixed(10)),
201        0xCD => info(opcode, "CALL a16", 3, InstructionTiming::fixed(17)),
202        0xCE => info(opcode, "ACI d8", 2, InstructionTiming::fixed(7)),
203        0xD3 => info(opcode, "OUT d8", 2, InstructionTiming::fixed(10)),
204        0xD6 => info(opcode, "SUI d8", 2, InstructionTiming::fixed(7)),
205        0xDB => info(opcode, "IN d8", 2, InstructionTiming::fixed(10)),
206        0xDE => info(opcode, "SBI d8", 2, InstructionTiming::fixed(7)),
207        0xE3 => info(opcode, "XTHL", 1, InstructionTiming::fixed(18)),
208        0xE6 => info(opcode, "ANI d8", 2, InstructionTiming::fixed(7)),
209        0xE9 => info(opcode, "PCHL", 1, InstructionTiming::fixed(5)),
210        0xEB => info(opcode, "XCHG", 1, InstructionTiming::fixed(5)),
211        0xEE => info(opcode, "XRI d8", 2, InstructionTiming::fixed(7)),
212        0xF3 => info(opcode, "DI", 1, InstructionTiming::fixed(4)),
213        0xF6 => info(opcode, "ORI d8", 2, InstructionTiming::fixed(7)),
214        0xF9 => info(opcode, "SPHL", 1, InstructionTiming::fixed(5)),
215        0xFB => info(opcode, "EI", 1, InstructionTiming::fixed(4)),
216        0xFE => info(opcode, "CPI d8", 2, InstructionTiming::fixed(7)),
217        _ => unreachable!("documented opcode must be covered: {opcode:#04X}"),
218    })
219}
220
221fn info(
222    opcode: u8,
223    mnemonic: impl Into<String>,
224    size: u8,
225    timing: InstructionTiming,
226) -> InstructionInfo {
227    InstructionInfo {
228        opcode,
229        mnemonic: mnemonic.into(),
230        size,
231        timing,
232    }
233}
234
235fn stack_pair(opcode: u8) -> &'static str {
236    match (opcode >> 4) & 3 {
237        0 => "B",
238        1 => "D",
239        2 => "H",
240        _ => "PSW",
241    }
242}