1use crate::lua51::opcodes::{OpCode, OpMode};
2
3const SIZE_OP: u32 = 6;
16const SIZE_A: u32 = 8;
17const SIZE_C: u32 = 9;
18const SIZE_B: u32 = 9;
19const SIZE_BX: u32 = SIZE_C + SIZE_B; const POS_OP: u32 = 0;
22const POS_A: u32 = POS_OP + SIZE_OP; const POS_C: u32 = POS_A + SIZE_A; const POS_B: u32 = POS_C + SIZE_C; const POS_BX: u32 = POS_C; const MAXARG_BX: u32 = (1 << SIZE_BX) - 1; const MAXARG_SBX: i32 = (MAXARG_BX >> 1) as i32; pub const BITRK: u32 = 1 << (SIZE_B - 1); #[inline]
35pub fn is_k(x: u32) -> bool {
36 x & BITRK != 0
37}
38
39#[inline]
41pub fn index_k(x: u32) -> u32 {
42 x & !BITRK
43}
44
45pub const LFIELDS_PER_FLUSH: u32 = 50;
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub struct Instruction {
51 pub raw: u32,
52 pub op: OpCode,
53 pub a: u32,
54 pub fields: InstructionFields,
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum InstructionFields {
60 ABC { b: u32, c: u32 },
61 ABx { bx: u32 },
62 AsBx { sbx: i32 },
63}
64
65#[inline]
66fn mask(n: u32) -> u32 {
67 (1u32 << n) - 1
68}
69
70impl Instruction {
71 pub fn decode(raw: u32) -> Option<Instruction> {
73 let opcode_val = (raw >> POS_OP) & mask(SIZE_OP);
74 let op = OpCode::from_u8(opcode_val as u8)?;
75 let a = (raw >> POS_A) & mask(SIZE_A);
76
77 let fields = match op.props().mode {
78 OpMode::ABC => {
79 let b = (raw >> POS_B) & mask(SIZE_B);
80 let c = (raw >> POS_C) & mask(SIZE_C);
81 InstructionFields::ABC { b, c }
82 }
83 OpMode::ABx => {
84 let bx = (raw >> POS_BX) & mask(SIZE_BX);
85 InstructionFields::ABx { bx }
86 }
87 OpMode::AsBx => {
88 let bx = (raw >> POS_BX) & mask(SIZE_BX);
89 let sbx = bx as i32 - MAXARG_SBX;
90 InstructionFields::AsBx { sbx }
91 }
92 };
93
94 Some(Instruction {
95 raw,
96 op,
97 a,
98 fields,
99 })
100 }
101
102 pub fn b(&self) -> u32 {
104 match self.fields {
105 InstructionFields::ABC { b, .. } => b,
106 _ => panic!("b() called on non-ABC instruction"),
107 }
108 }
109
110 pub fn c(&self) -> u32 {
112 match self.fields {
113 InstructionFields::ABC { c, .. } => c,
114 _ => panic!("c() called on non-ABC instruction"),
115 }
116 }
117
118 pub fn bx(&self) -> u32 {
120 match self.fields {
121 InstructionFields::ABx { bx } => bx,
122 _ => panic!("bx() called on non-ABx instruction"),
123 }
124 }
125
126 pub fn sbx(&self) -> i32 {
128 match self.fields {
129 InstructionFields::AsBx { sbx } => sbx,
130 _ => panic!("sbx() called on non-AsBx instruction"),
131 }
132 }
133}
134
135impl std::fmt::Display for Instruction {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 match self.fields {
138 InstructionFields::ABC { b, c } => {
139 write!(f, "{:<12} {} {} {}", self.op, self.a, b, c)
140 }
141 InstructionFields::ABx { bx } => {
142 write!(f, "{:<12} {} {}", self.op, self.a, bx)
143 }
144 InstructionFields::AsBx { sbx } => {
145 write!(f, "{:<12} {} {}", self.op, self.a, sbx)
146 }
147 }
148 }
149}
150
151pub fn encode_abc(op: OpCode, a: u32, b: u32, c: u32) -> u32 {
153 ((op as u32) << POS_OP) | (a << POS_A) | (b << POS_B) | (c << POS_C)
154}
155
156pub fn encode_abx(op: OpCode, a: u32, bx: u32) -> u32 {
157 ((op as u32) << POS_OP) | (a << POS_A) | (bx << POS_BX)
158}
159
160pub fn encode_asbx(op: OpCode, a: u32, sbx: i32) -> u32 {
161 let bx = (sbx + MAXARG_SBX) as u32;
162 ((op as u32) << POS_OP) | (a << POS_A) | (bx << POS_BX)
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn decode_move() {
171 let raw = encode_abc(OpCode::Move, 1, 2, 0);
173 let inst = Instruction::decode(raw).unwrap();
174 assert_eq!(inst.op, OpCode::Move);
175 assert_eq!(inst.a, 1);
176 assert_eq!(inst.b(), 2);
177 assert_eq!(inst.c(), 0);
178 }
179
180 #[test]
181 fn decode_loadk() {
182 let raw = encode_abx(OpCode::LoadK, 5, 100);
184 let inst = Instruction::decode(raw).unwrap();
185 assert_eq!(inst.op, OpCode::LoadK);
186 assert_eq!(inst.a, 5);
187 assert_eq!(inst.bx(), 100);
188 }
189
190 #[test]
191 fn decode_jmp() {
192 let raw = encode_asbx(OpCode::Jmp, 0, 10);
194 let inst = Instruction::decode(raw).unwrap();
195 assert_eq!(inst.op, OpCode::Jmp);
196 assert_eq!(inst.sbx(), 10);
197
198 let raw = encode_asbx(OpCode::Jmp, 0, -5);
200 let inst = Instruction::decode(raw).unwrap();
201 assert_eq!(inst.op, OpCode::Jmp);
202 assert_eq!(inst.sbx(), -5);
203 }
204
205 #[test]
206 fn decode_add() {
207 let raw = encode_abc(OpCode::Add, 0, BITRK, 1);
209 let inst = Instruction::decode(raw).unwrap();
210 assert_eq!(inst.op, OpCode::Add);
211 assert_eq!(inst.a, 0);
212 assert!(is_k(inst.b()));
213 assert_eq!(index_k(inst.b()), 0);
214 assert!(!is_k(inst.c()));
215 }
216
217 #[test]
218 fn roundtrip_all_formats() {
219 let raw = encode_abc(OpCode::Call, 3, 2, 1);
221 let inst = Instruction::decode(raw).unwrap();
222 assert_eq!(inst.raw, raw);
223 assert_eq!(inst.op, OpCode::Call);
224 assert_eq!(inst.a, 3);
225 assert_eq!(inst.b(), 2);
226 assert_eq!(inst.c(), 1);
227
228 let raw = encode_abx(OpCode::Closure, 0, 42);
230 let inst = Instruction::decode(raw).unwrap();
231 assert_eq!(inst.raw, raw);
232 assert_eq!(inst.bx(), 42);
233
234 let raw = encode_asbx(OpCode::ForLoop, 2, -100);
236 let inst = Instruction::decode(raw).unwrap();
237 assert_eq!(inst.raw, raw);
238 assert_eq!(inst.sbx(), -100);
239 }
240
241 #[test]
242 fn invalid_opcode() {
243 let raw = 38u32;
245 assert!(Instruction::decode(raw).is_none());
246 }
247
248 #[test]
249 fn display_format() {
250 let raw = encode_abc(OpCode::Move, 1, 2, 0);
251 let inst = Instruction::decode(raw).unwrap();
252 let s = format!("{}", inst);
253 assert!(s.contains("MOVE"));
254 assert!(s.contains("1"));
255 assert!(s.contains("2"));
256 }
257}