Krolyxon
Commit c48ef97

Branches

Tags

Refs →

bench51srcisa.rs

347 lines · 9.7 KB · Rust

1//! The 8051 instruction set as a single opcode table, shared by the
2//! assembler (pattern -> opcode), the disassembler and the simulator
3//! (sizes and machine-cycle counts).
4
5use std::sync::OnceLock;
6
7/// Operand forms as they appear in an instruction encoding.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Operand {
10 A,
11 AB,
12 C,
13 Dptr,
14 Reg(u8),
15 AtReg(u8),
16 AtDptr,
17 AtAPlusDptr,
18 AtAPlusPc,
19 Imm8,
20 Imm16,
21 Direct,
22 Bit,
23 NotBit,
24 Addr11,
25 Addr16,
26 Rel,
27}
28
29impl Operand {
30 /// Bytes this operand occupies after the opcode byte.
31 pub fn len(self) -> u8 {
32 match self {
33 Operand::Imm8 | Operand::Direct | Operand::Bit | Operand::NotBit => 1,
34 Operand::Addr11 | Operand::Rel => 1,
35 Operand::Imm16 | Operand::Addr16 => 2,
36 _ => 0,
37 }
38 }
39}
40
41#[derive(Debug, Clone, Copy)]
42pub struct OpInfo {
43 pub opcode: u8,
44 pub mnemonic: &'static str,
45 pub operands: &'static [Operand],
46 pub size: u8,
47 pub cycles: u8,
48}
49
50use Operand::*;
51
52const RN: [[Operand; 1]; 8] = [
53 [Reg(0)],
54 [Reg(1)],
55 [Reg(2)],
56 [Reg(3)],
57 [Reg(4)],
58 [Reg(5)],
59 [Reg(6)],
60 [Reg(7)],
61];
62const A_RN: [[Operand; 2]; 8] = [
63 [A, Reg(0)],
64 [A, Reg(1)],
65 [A, Reg(2)],
66 [A, Reg(3)],
67 [A, Reg(4)],
68 [A, Reg(5)],
69 [A, Reg(6)],
70 [A, Reg(7)],
71];
72const RN_A: [[Operand; 2]; 8] = [
73 [Reg(0), A],
74 [Reg(1), A],
75 [Reg(2), A],
76 [Reg(3), A],
77 [Reg(4), A],
78 [Reg(5), A],
79 [Reg(6), A],
80 [Reg(7), A],
81];
82const RN_IMM: [[Operand; 2]; 8] = [
83 [Reg(0), Imm8],
84 [Reg(1), Imm8],
85 [Reg(2), Imm8],
86 [Reg(3), Imm8],
87 [Reg(4), Imm8],
88 [Reg(5), Imm8],
89 [Reg(6), Imm8],
90 [Reg(7), Imm8],
91];
92const DIR_RN: [[Operand; 2]; 8] = [
93 [Direct, Reg(0)],
94 [Direct, Reg(1)],
95 [Direct, Reg(2)],
96 [Direct, Reg(3)],
97 [Direct, Reg(4)],
98 [Direct, Reg(5)],
99 [Direct, Reg(6)],
100 [Direct, Reg(7)],
101];
102const RN_DIR: [[Operand; 2]; 8] = [
103 [Reg(0), Direct],
104 [Reg(1), Direct],
105 [Reg(2), Direct],
106 [Reg(3), Direct],
107 [Reg(4), Direct],
108 [Reg(5), Direct],
109 [Reg(6), Direct],
110 [Reg(7), Direct],
111];
112const RN_REL: [[Operand; 2]; 8] = [
113 [Reg(0), Rel],
114 [Reg(1), Rel],
115 [Reg(2), Rel],
116 [Reg(3), Rel],
117 [Reg(4), Rel],
118 [Reg(5), Rel],
119 [Reg(6), Rel],
120 [Reg(7), Rel],
121];
122const RN_IMM_REL: [[Operand; 3]; 8] = [
123 [Reg(0), Imm8, Rel],
124 [Reg(1), Imm8, Rel],
125 [Reg(2), Imm8, Rel],
126 [Reg(3), Imm8, Rel],
127 [Reg(4), Imm8, Rel],
128 [Reg(5), Imm8, Rel],
129 [Reg(6), Imm8, Rel],
130 [Reg(7), Imm8, Rel],
131];
132const RI: [[Operand; 1]; 2] = [[AtReg(0)], [AtReg(1)]];
133const A_RI: [[Operand; 2]; 2] = [[A, AtReg(0)], [A, AtReg(1)]];
134const RI_A: [[Operand; 2]; 2] = [[AtReg(0), A], [AtReg(1), A]];
135const RI_IMM: [[Operand; 2]; 2] = [[AtReg(0), Imm8], [AtReg(1), Imm8]];
136const DIR_RI: [[Operand; 2]; 2] = [[Direct, AtReg(0)], [Direct, AtReg(1)]];
137const RI_DIR: [[Operand; 2]; 2] = [[AtReg(0), Direct], [AtReg(1), Direct]];
138const RI_IMM_REL: [[Operand; 3]; 2] = [[AtReg(0), Imm8, Rel], [AtReg(1), Imm8, Rel]];
139
140/// Fixed (non register-indexed) opcodes.
141const FIXED: &[(u8, &str, &[Operand], u8)] = &[
142 (0x00, "NOP", &[], 1),
143 (0x02, "LJMP", &[Addr16], 2),
144 (0x03, "RR", &[A], 1),
145 (0x04, "INC", &[A], 1),
146 (0x05, "INC", &[Direct], 1),
147 (0x10, "JBC", &[Bit, Rel], 2),
148 (0x12, "LCALL", &[Addr16], 2),
149 (0x13, "RRC", &[A], 1),
150 (0x14, "DEC", &[A], 1),
151 (0x15, "DEC", &[Direct], 1),
152 (0x20, "JB", &[Bit, Rel], 2),
153 (0x22, "RET", &[], 2),
154 (0x23, "RL", &[A], 1),
155 (0x24, "ADD", &[A, Imm8], 1),
156 (0x25, "ADD", &[A, Direct], 1),
157 (0x30, "JNB", &[Bit, Rel], 2),
158 (0x32, "RETI", &[], 2),
159 (0x33, "RLC", &[A], 1),
160 (0x34, "ADDC", &[A, Imm8], 1),
161 (0x35, "ADDC", &[A, Direct], 1),
162 (0x40, "JC", &[Rel], 2),
163 (0x42, "ORL", &[Direct, A], 1),
164 (0x43, "ORL", &[Direct, Imm8], 2),
165 (0x44, "ORL", &[A, Imm8], 1),
166 (0x45, "ORL", &[A, Direct], 1),
167 (0x50, "JNC", &[Rel], 2),
168 (0x52, "ANL", &[Direct, A], 1),
169 (0x53, "ANL", &[Direct, Imm8], 2),
170 (0x54, "ANL", &[A, Imm8], 1),
171 (0x55, "ANL", &[A, Direct], 1),
172 (0x60, "JZ", &[Rel], 2),
173 (0x62, "XRL", &[Direct, A], 1),
174 (0x63, "XRL", &[Direct, Imm8], 2),
175 (0x64, "XRL", &[A, Imm8], 1),
176 (0x65, "XRL", &[A, Direct], 1),
177 (0x70, "JNZ", &[Rel], 2),
178 (0x72, "ORL", &[C, Bit], 2),
179 (0x73, "JMP", &[AtAPlusDptr], 2),
180 (0x74, "MOV", &[A, Imm8], 1),
181 (0x75, "MOV", &[Direct, Imm8], 2),
182 (0x80, "SJMP", &[Rel], 2),
183 (0x82, "ANL", &[C, Bit], 2),
184 (0x83, "MOVC", &[A, AtAPlusPc], 2),
185 (0x84, "DIV", &[AB], 4),
186 // Encoded as: opcode, src, dest (handled specially by the encoder).
187 (0x85, "MOV", &[Direct, Direct], 2),
188 (0x90, "MOV", &[Dptr, Imm16], 2),
189 (0x92, "MOV", &[Bit, C], 2),
190 (0x93, "MOVC", &[A, AtAPlusDptr], 2),
191 (0x94, "SUBB", &[A, Imm8], 1),
192 (0x95, "SUBB", &[A, Direct], 1),
193 (0xA0, "ORL", &[C, NotBit], 2),
194 (0xA2, "MOV", &[C, Bit], 1),
195 (0xA3, "INC", &[Dptr], 2),
196 (0xA4, "MUL", &[AB], 4),
197 (0xB0, "ANL", &[C, NotBit], 2),
198 (0xB2, "CPL", &[Bit], 1),
199 (0xB3, "CPL", &[C], 1),
200 (0xB4, "CJNE", &[A, Imm8, Rel], 2),
201 (0xB5, "CJNE", &[A, Direct, Rel], 2),
202 (0xC0, "PUSH", &[Direct], 2),
203 (0xC2, "CLR", &[Bit], 1),
204 (0xC3, "CLR", &[C], 1),
205 (0xC4, "SWAP", &[A], 1),
206 (0xC5, "XCH", &[A, Direct], 1),
207 (0xD0, "POP", &[Direct], 2),
208 (0xD2, "SETB", &[Bit], 1),
209 (0xD3, "SETB", &[C], 1),
210 (0xD4, "DA", &[A], 1),
211 (0xD5, "DJNZ", &[Direct, Rel], 2),
212 (0xE0, "MOVX", &[A, AtDptr], 2),
213 (0xE4, "CLR", &[A], 1),
214 (0xE5, "MOV", &[A, Direct], 1),
215 (0xF0, "MOVX", &[AtDptr, A], 2),
216 (0xF4, "CPL", &[A], 1),
217 (0xF5, "MOV", &[Direct, A], 1),
218];
219
220/// Register-indexed groups: (base opcode, mnemonic, cycles, @Ri forms, Rn forms).
221type Group = (
222 u8,
223 &'static str,
224 u8,
225 Option<&'static [[Operand; 2]; 2]>,
226 Option<&'static [[Operand; 2]; 8]>,
227);
228const GROUPS2: &[Group] = &[
229 (0x20, "ADD", 1, Some(&A_RI), Some(&A_RN)),
230 (0x30, "ADDC", 1, Some(&A_RI), Some(&A_RN)),
231 (0x40, "ORL", 1, Some(&A_RI), Some(&A_RN)),
232 (0x50, "ANL", 1, Some(&A_RI), Some(&A_RN)),
233 (0x60, "XRL", 1, Some(&A_RI), Some(&A_RN)),
234 (0x70, "MOV", 1, Some(&RI_IMM), Some(&RN_IMM)),
235 (0x80, "MOV", 2, Some(&DIR_RI), Some(&DIR_RN)),
236 (0x90, "SUBB", 1, Some(&A_RI), Some(&A_RN)),
237 (0xA0, "MOV", 2, Some(&RI_DIR), Some(&RN_DIR)),
238 (0xC0, "XCH", 1, Some(&A_RI), Some(&A_RN)),
239 (0xD0, "XCHD", 1, Some(&A_RI), None),
240 (0xD0, "DJNZ", 2, None, Some(&RN_REL)),
241 (0xE0, "MOV", 1, Some(&A_RI), Some(&A_RN)),
242 (0xF0, "MOV", 1, Some(&RI_A), Some(&RN_A)),
243];
244
245fn size_of(ops: &[Operand]) -> u8 {
246 1 + ops.iter().map(|o| o.len()).sum::<u8>()
247}
248
249fn build() -> Vec<Option<OpInfo>> {
250 let mut t: Vec<Option<OpInfo>> = vec![None; 256];
251 let mut put = |opcode: u8, mnemonic: &'static str, operands: &'static [Operand], cycles: u8| {
252 assert!(
253 t[opcode as usize].is_none(),
254 "duplicate opcode {opcode:02X}"
255 );
256 t[opcode as usize] = Some(OpInfo {
257 opcode,
258 mnemonic,
259 operands,
260 size: size_of(operands),
261 cycles,
262 });
263 };
264
265 for &(op, m, ops, cyc) in FIXED {
266 put(op, m, ops, cyc);
267 }
268 // AJMP / ACALL: addr11 high bits live in the opcode's top three bits.
269 for page in 0..8u8 {
270 put((page << 5) | 0x01, "AJMP", &[Addr11], 2);
271 put((page << 5) | 0x11, "ACALL", &[Addr11], 2);
272 }
273 for i in 0..2 {
274 put(0x06 + i as u8, "INC", &RI[i], 1);
275 put(0x16 + i as u8, "DEC", &RI[i], 1);
276 put(0xB6 + i as u8, "CJNE", &RI_IMM_REL[i], 2);
277 put(0xE2 + i as u8, "MOVX", &A_RI[i], 2);
278 put(0xF2 + i as u8, "MOVX", &RI_A[i], 2);
279 }
280 for n in 0..8 {
281 put(0x08 + n as u8, "INC", &RN[n], 1);
282 put(0x18 + n as u8, "DEC", &RN[n], 1);
283 put(0xB8 + n as u8, "CJNE", &RN_IMM_REL[n], 2);
284 }
285 for &(base, m, cyc, ri, rn) in GROUPS2 {
286 if let Some(ri) = ri {
287 for (i, ops) in ri.iter().enumerate() {
288 put(base + 6 + i as u8, m, ops, cyc);
289 }
290 }
291 if let Some(rn) = rn {
292 for (n, ops) in rn.iter().enumerate() {
293 put(base + 8 + n as u8, m, ops, cyc);
294 }
295 }
296 }
297 t
298}
299
300fn table() -> &'static [Option<OpInfo>] {
301 static TABLE: OnceLock<Vec<Option<OpInfo>>> = OnceLock::new();
302 TABLE.get_or_init(build)
303}
304
305/// Info for an opcode; `None` only for the reserved opcode 0xA5.
306pub fn info(opcode: u8) -> Option<&'static OpInfo> {
307 table()[opcode as usize].as_ref()
308}
309
310/// All defined opcodes with the given mnemonic (upper case).
311pub fn by_mnemonic(mnemonic: &str) -> impl Iterator<Item = &'static OpInfo> + '_ {
312 table()
313 .iter()
314 .flatten()
315 .filter(move |i| i.mnemonic == mnemonic)
316}
317
318pub fn is_mnemonic(s: &str) -> bool {
319 matches!(s, "JMP" | "CALL") || table().iter().flatten().any(|i| i.mnemonic == s)
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325
326 #[test]
327 fn only_a5_is_undefined() {
328 let undefined: Vec<_> = (0..=255u8).filter(|&op| info(op).is_none()).collect();
329 assert_eq!(undefined, vec![0xA5]);
330 }
331
332 #[test]
333 fn spot_check_sizes_and_cycles() {
334 let i = info(0x85).unwrap();
335 assert_eq!((i.mnemonic, i.size, i.cycles), ("MOV", 3, 2));
336 let i = info(0xB8).unwrap();
337 assert_eq!((i.mnemonic, i.size, i.cycles), ("CJNE", 3, 2));
338 let i = info(0xD6).unwrap();
339 assert_eq!((i.mnemonic, i.size), ("XCHD", 1));
340 let i = info(0xA4).unwrap();
341 assert_eq!((i.mnemonic, i.cycles), ("MUL", 4));
342 let i = info(0xF1).unwrap();
343 assert_eq!((i.mnemonic, i.size), ("ACALL", 2));
344 let i = info(0x90).unwrap();
345 assert_eq!((i.mnemonic, i.size), ("MOV", 3));
346 }
347}