1#[inline(always)]
11pub fn sign_extend(value: u64, n: usize) -> u64 {
12 match n {
13 0 => 0,
14 1 => value as u8 as i8 as i64 as u64,
15 2 => value as u16 as i16 as i64 as u64,
16 3 => {
17 let v = value & 0xFF_FFFF;
18 if v & 0x80_0000 != 0 {
19 v | 0xFFFF_FFFF_FF00_0000
20 } else {
21 v
22 }
23 }
24 4 => value as u32 as i32 as i64 as u64,
25 _ => value, }
27}
28
29pub fn to_signed(value: u64) -> i64 {
31 value as i64
32}
33
34pub fn to_unsigned(value: i64) -> u64 {
36 value as u64
37}
38
39pub fn sign_extend_32(value: u64) -> u64 {
41 (value as u32) as i32 as i64 as u64
42}
43
44pub fn decode_le(bytes: &[u8]) -> u64 {
46 let mut value: u64 = 0;
47 for (i, &b) in bytes.iter().enumerate() {
48 value |= (b as u64) << (i * 8);
49 }
50 value
51}
52
53#[derive(Clone, Copy, Debug)]
55pub enum Args {
56 None,
58 Imm { imm: u64 },
60 RegExtImm { ra: usize, imm: u64 },
62 TwoImm { imm_x: u64, imm_y: u64 },
64 Offset { offset: u64 },
66 RegImm { ra: usize, imm: u64 },
68 RegTwoImm { ra: usize, imm_x: u64, imm_y: u64 },
70 RegImmOffset { ra: usize, imm: u64, offset: u64 },
72 TwoReg { rd: usize, ra: usize },
74 TwoRegImm { ra: usize, rb: usize, imm: u64 },
76 TwoRegOffset { ra: usize, rb: usize, offset: u64 },
78 TwoRegTwoImm {
80 ra: usize,
81 rb: usize,
82 imm_x: u64,
83 imm_y: u64,
84 },
85 ThreeReg { ra: usize, rb: usize, rd: usize },
87}
88
89#[inline(always)]
91fn zeta(code: &[u8], i: usize) -> u8 {
92 if i < code.len() { code[i] } else { 0 }
93}
94
95#[inline(always)]
97fn read_le_at(code: &[u8], offset: usize, n: usize) -> u64 {
98 if offset + n <= code.len() {
100 let s = &code[offset..offset + n];
101 match n {
102 0 => 0,
103 1 => s[0] as u64,
104 2 => u16::from_le_bytes([s[0], s[1]]) as u64,
105 3 => s[0] as u64 | (s[1] as u64) << 8 | (s[2] as u64) << 16,
106 4 => u32::from_le_bytes([s[0], s[1], s[2], s[3]]) as u64,
107 _ => {
108 let mut buf = [0u8; 8];
109 buf[..n].copy_from_slice(s);
110 u64::from_le_bytes(buf)
111 }
112 }
113 } else {
114 let mut val = 0u64;
116 for i in 0..n {
117 val |= (zeta(code, offset + i) as u64) << (i * 8);
118 }
119 val
120 }
121}
122
123#[inline(always)]
126pub fn read_signed_imm(code: &[u8], offset: usize, n: usize) -> u64 {
127 read_signed_at(code, offset, n)
128}
129
130#[inline(always)]
133pub fn read_le_imm(code: &[u8], offset: usize, n: usize) -> u64 {
134 read_le_at(code, offset, n)
135}
136
137#[inline(always)]
139fn read_signed_at(code: &[u8], offset: usize, n: usize) -> u64 {
140 sign_extend(read_le_at(code, offset, n), n)
141}
142
143pub fn decode_args(
148 code: &[u8],
149 pc: usize,
150 skip: usize,
151 category: crate::instruction::InstructionCategory,
152) -> Args {
153 use crate::instruction::InstructionCategory::*;
154 let l = skip; match category {
157 NoArgs => Args::None,
158
159 OneImm => {
161 let lx = l.min(4);
162 let imm = read_signed_at(code, pc + 1, lx);
163 Args::Imm { imm }
164 }
165
166 OneRegExtImm => {
168 let ra = (zeta(code, pc + 1) % 16).min(12) as usize;
169 let imm = read_le_at(code, pc + 2, 8);
170 Args::RegExtImm { ra, imm }
171 }
172
173 TwoImm => {
175 let lx = (zeta(code, pc + 1) as usize % 8).min(4);
176 let ly = if l > lx + 1 { (l - lx - 1).min(4) } else { 0 };
177 let imm_x = read_signed_at(code, pc + 2, lx);
178 let imm_y = read_signed_at(code, pc + 2 + lx, ly);
179 Args::TwoImm { imm_x, imm_y }
180 }
181
182 OneOffset => {
184 let lx = l.min(4);
185 let signed_offset = read_signed_at(code, pc + 1, lx) as i64;
186 let offset = (pc as i64).wrapping_add(signed_offset) as u64;
187 Args::Offset { offset }
188 }
189
190 OneRegOneImm => {
192 let ra = (zeta(code, pc + 1) % 16).min(12) as usize;
193 let lx = if l > 1 { (l - 1).min(4) } else { 0 };
194 let imm = read_signed_at(code, pc + 2, lx);
195 Args::RegImm { ra, imm }
196 }
197
198 OneRegTwoImm => {
200 let reg_byte = zeta(code, pc + 1);
201 let ra = (reg_byte % 16).min(12) as usize;
202 let lx = ((reg_byte as usize / 16) % 8).min(4);
203 let ly = if l > lx + 1 { (l - lx - 1).min(4) } else { 0 };
204 let imm_x = read_signed_at(code, pc + 2, lx);
205 let imm_y = read_signed_at(code, pc + 2 + lx, ly);
206 Args::RegTwoImm { ra, imm_x, imm_y }
207 }
208
209 OneRegImmOffset => {
211 let reg_byte = zeta(code, pc + 1);
212 let ra = (reg_byte % 16).min(12) as usize;
213 let lx = ((reg_byte as usize / 16) % 8).min(4);
214 let ly = if l > lx + 1 { (l - lx - 1).min(4) } else { 0 };
215 let imm = read_signed_at(code, pc + 2, lx);
216 let signed_offset = read_signed_at(code, pc + 2 + lx, ly) as i64;
217 let offset = (pc as i64).wrapping_add(signed_offset) as u64;
218 Args::RegImmOffset { ra, imm, offset }
219 }
220
221 TwoReg => {
223 let reg_byte = zeta(code, pc + 1);
224 let rd = (reg_byte % 16).min(12) as usize;
225 let ra = (reg_byte / 16).min(12) as usize;
226 Args::TwoReg { rd, ra }
227 }
228
229 TwoRegOneImm => {
231 let reg_byte = zeta(code, pc + 1);
232 let ra = (reg_byte % 16).min(12) as usize;
233 let rb = (reg_byte / 16).min(12) as usize;
234 let lx = if l > 1 { (l - 1).min(4) } else { 0 };
235 let imm = read_signed_at(code, pc + 2, lx);
236 Args::TwoRegImm { ra, rb, imm }
237 }
238
239 TwoRegOneOffset => {
241 let reg_byte = zeta(code, pc + 1);
242 let ra = (reg_byte % 16).min(12) as usize;
243 let rb = (reg_byte / 16).min(12) as usize;
244 let lx = if l > 1 { (l - 1).min(4) } else { 0 };
245 let signed_offset = read_signed_at(code, pc + 2, lx) as i64;
246 let offset = (pc as i64).wrapping_add(signed_offset) as u64;
247 Args::TwoRegOffset { ra, rb, offset }
248 }
249
250 TwoRegTwoImm => {
252 let reg_byte = zeta(code, pc + 1);
253 let ra = (reg_byte % 16).min(12) as usize;
254 let rb = (reg_byte / 16).min(12) as usize;
255 let lx = (zeta(code, pc + 2) as usize % 8).min(4);
256 let ly = if l > lx + 2 { (l - lx - 2).min(4) } else { 0 };
257 let imm_x = read_signed_at(code, pc + 3, lx);
258 let imm_y = read_signed_at(code, pc + 3 + lx, ly);
259 Args::TwoRegTwoImm {
260 ra,
261 rb,
262 imm_x,
263 imm_y,
264 }
265 }
266
267 ThreeReg => {
269 let reg_byte = zeta(code, pc + 1);
270 let ra = (reg_byte % 16).min(12) as usize;
271 let rb = (reg_byte / 16).min(12) as usize;
272 let rd = zeta(code, pc + 2).min(12) as usize;
273 Args::ThreeReg { ra, rb, rd }
274 }
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281
282 #[test]
283 fn test_sign_extend_positive() {
284 assert_eq!(sign_extend(0x7F, 1), 0x7F);
285 assert_eq!(sign_extend(0x7FFF, 2), 0x7FFF);
286 assert_eq!(sign_extend(0x7FFF_FFFF, 4), 0x7FFF_FFFF);
287 }
288
289 #[test]
290 fn test_sign_extend_negative() {
291 assert_eq!(sign_extend(0x80, 1), 0xFFFF_FFFF_FFFF_FF80);
292 assert_eq!(sign_extend(0x8000, 2), 0xFFFF_FFFF_FFFF_8000);
293 assert_eq!(sign_extend(0x8000_0000, 4), 0xFFFF_FFFF_8000_0000);
294 }
295
296 #[test]
297 fn test_sign_extend_3byte() {
298 assert_eq!(sign_extend(0x7F_FFFF, 3), 0x7F_FFFF);
299 assert_eq!(sign_extend(0x80_0000, 3), 0xFFFF_FFFF_FF80_0000);
300 }
301
302 #[test]
303 fn test_decode_le() {
304 assert_eq!(decode_le(&[0x01, 0x02, 0x03, 0x04]), 0x04030201);
305 assert_eq!(decode_le(&[0xFF]), 0xFF);
306 assert_eq!(decode_le(&[]), 0);
307 }
308}
309
310#[cfg(test)]
311mod proptests {
312 use super::*;
313 use proptest::prelude::*;
314
315 proptest! {
316 #[test]
318 fn sign_extend_idempotent(value in any::<u64>(), n in 0usize..=4) {
319 let once = sign_extend(value, n);
320 let twice = sign_extend(once, n);
321 prop_assert_eq!(once, twice);
322 }
323
324 #[test]
326 fn signed_unsigned_roundtrip(value in any::<u64>()) {
327 prop_assert_eq!(to_unsigned(to_signed(value)), value);
328 }
329
330 #[test]
332 fn decode_le_single_byte(b in any::<u8>()) {
333 prop_assert_eq!(decode_le(&[b]), b as u64);
334 }
335
336 #[test]
338 fn decode_le_deterministic(
339 bytes in proptest::collection::vec(any::<u8>(), 0..8),
340 ) {
341 prop_assert_eq!(decode_le(&bytes), decode_le(&bytes));
342 }
343
344 #[test]
346 fn sign_extend_zero_width_is_zero(value in any::<u64>()) {
347 prop_assert_eq!(sign_extend(value, 0), 0);
348 }
349
350 #[test]
352 fn sign_extend_32_matches_generic(value in any::<u64>()) {
353 prop_assert_eq!(sign_extend_32(value), sign_extend(value, 4));
354 }
355
356 #[test]
358 fn decode_args_registers_bounded(
359 code in proptest::collection::vec(any::<u8>(), 3..16),
360 skip in 0usize..8,
361 category_idx in 0u8..13,
362 ) {
363 use crate::instruction::InstructionCategory::*;
364 let category = match category_idx {
365 0 => NoArgs,
366 1 => OneImm,
367 2 => OneRegExtImm,
368 3 => TwoImm,
369 4 => OneOffset,
370 5 => OneRegOneImm,
371 6 => OneRegTwoImm,
372 7 => OneRegImmOffset,
373 8 => TwoReg,
374 9 => TwoRegOneImm,
375 10 => TwoRegOneOffset,
376 11 => TwoRegTwoImm,
377 12 => ThreeReg,
378 _ => unreachable!(),
379 };
380 let args = decode_args(&code, 0, skip, category);
381 match args {
382 Args::None | Args::Imm { .. } | Args::TwoImm { .. } | Args::Offset { .. } => {}
383 Args::RegExtImm { ra, .. }
384 | Args::RegImm { ra, .. }
385 | Args::RegTwoImm { ra, .. }
386 | Args::RegImmOffset { ra, .. } => {
387 prop_assert!(ra <= 12);
388 }
389 Args::TwoReg { rd, ra } => {
390 prop_assert!(rd <= 12);
391 prop_assert!(ra <= 12);
392 }
393 Args::TwoRegImm { ra, rb, .. }
394 | Args::TwoRegOffset { ra, rb, .. }
395 | Args::TwoRegTwoImm { ra, rb, .. } => {
396 prop_assert!(ra <= 12);
397 prop_assert!(rb <= 12);
398 }
399 Args::ThreeReg { ra, rb, rd } => {
400 prop_assert!(ra <= 12);
401 prop_assert!(rb <= 12);
402 prop_assert!(rd <= 12);
403 }
404 }
405 }
406
407 #[test]
409 fn decode_args_deterministic(
410 code in proptest::collection::vec(any::<u8>(), 3..16),
411 skip in 0usize..8,
412 ) {
413 use crate::instruction::InstructionCategory::*;
414 let args1 = decode_args(&code, 0, skip, TwoRegOneImm);
415 let args2 = decode_args(&code, 0, skip, TwoRegOneImm);
416 match (args1, args2) {
418 (Args::TwoRegImm { ra: a1, rb: b1, imm: i1 },
419 Args::TwoRegImm { ra: a2, rb: b2, imm: i2 }) => {
420 prop_assert_eq!(a1, a2);
421 prop_assert_eq!(b1, b2);
422 prop_assert_eq!(i1, i2);
423 }
424 _ => prop_assert!(false),
425 }
426 }
427 }
428}