fix(xexdb): clear the lint gate on the imported crates

rustfmt, then clippy -D warnings across the three new crates. Mechanical,
except three decisions that are stated rather than silently allowed:

  * lzx.rs gets file-scoped needless_range_loop/explicit_counter_loop allows.
    Index arithmetic IS the algorithm -- LZX is defined over symbol indices,
    Huffman slots and window positions, and a decompressor that is merely
    idiomatic is worth nothing if it is not bit-exact.
  * sylpheed-xexdb gets crate-scoped allows for needless_range_loop (nine
    sites index reg[r] where r is the PowerPC register number -- the index is
    the meaning), too_many_arguments and type_complexity. This code arrived
    whole from a retired repository; a refactor here would be an unreviewed
    edit dressed as a lint fix.
  * Everything else clippy asked for is FIXED, including all 14 doc-indent
    sites, the let-else, and a Prepared type alias in the binary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-09-13 20:25:44 +02:00
parent 62b0f79590
commit c9dd2cb705
37 changed files with 5308 additions and 2198 deletions

View File

@@ -12,13 +12,17 @@
use std::io::BufRead;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let path = std::env::args().nth(1).ok_or("usage: decode_table_check <table>")?;
let path = std::env::args()
.nth(1)
.ok_or("usage: decode_table_check <table>")?;
let f = std::io::BufReader::new(std::fs::File::open(path)?);
let (mut ok, mut bad, mut invalid) = (0u32, 0u32, 0u32);
for line in f.lines() {
let line = line?;
let mut it = line.split_whitespace();
let (Some(w), Some(name)) = (it.next(), it.next()) else { continue };
let (Some(w), Some(name)) = (it.next(), it.next()) else {
continue;
};
let word = u32::from_str_radix(w.trim_start_matches("0x"), 16)?;
let d = sylpheed_ppc::decoder::decode(word, 0x8200_0000);
let got = format!("{:?}", d.opcode);

View File

@@ -18,34 +18,68 @@ impl DecodedInstr {
// Common field extractors (PPC bit numbering)
/// Primary opcode (bits 0-5)
#[inline] pub fn op(&self) -> u32 { extract_bits(self.raw, 0, 5) }
#[inline]
pub fn op(&self) -> u32 {
extract_bits(self.raw, 0, 5)
}
/// rD/rS/rT (bits 6-10) - destination/source register
#[inline] pub fn rd(&self) -> usize { extract_bits(self.raw, 6, 10) as usize }
#[inline] pub fn rs(&self) -> usize { self.rd() }
#[inline] pub fn rt(&self) -> usize { self.rd() }
#[inline]
pub fn rd(&self) -> usize {
extract_bits(self.raw, 6, 10) as usize
}
#[inline]
pub fn rs(&self) -> usize {
self.rd()
}
#[inline]
pub fn rt(&self) -> usize {
self.rd()
}
/// rA (bits 11-15)
#[inline] pub fn ra(&self) -> usize { extract_bits(self.raw, 11, 15) as usize }
#[inline]
pub fn ra(&self) -> usize {
extract_bits(self.raw, 11, 15) as usize
}
/// rB (bits 16-20)
#[inline] pub fn rb(&self) -> usize { extract_bits(self.raw, 16, 20) as usize }
#[inline]
pub fn rb(&self) -> usize {
extract_bits(self.raw, 16, 20) as usize
}
/// rC (bits 21-25) - for 4-operand instructions
#[inline] pub fn rc(&self) -> usize { extract_bits(self.raw, 21, 25) as usize }
#[inline]
pub fn rc(&self) -> usize {
extract_bits(self.raw, 21, 25) as usize
}
/// SIMM/UIMM (bits 16-31) - signed/unsigned immediate
#[inline] pub fn simm16(&self) -> i16 { (self.raw & 0xFFFF) as i16 }
#[inline] pub fn uimm16(&self) -> u16 { (self.raw & 0xFFFF) as u16 }
#[inline]
pub fn simm16(&self) -> i16 {
(self.raw & 0xFFFF) as i16
}
#[inline]
pub fn uimm16(&self) -> u16 {
(self.raw & 0xFFFF) as u16
}
/// D-form displacement (signed, bits 16-31)
#[inline] pub fn d(&self) -> i32 { self.simm16() as i32 }
#[inline]
pub fn d(&self) -> i32 {
self.simm16() as i32
}
/// DS-form displacement (signed, bits 16-29, shifted left 2)
#[inline] pub fn ds(&self) -> i32 { (self.raw & 0xFFFC) as i16 as i32 }
#[inline]
pub fn ds(&self) -> i32 {
(self.raw & 0xFFFC) as i16 as i32
}
/// LI field for branch (bits 6-29, sign-extended, shifted left 2)
#[inline] pub fn li(&self) -> i32 {
#[inline]
pub fn li(&self) -> i32 {
let li = extract_bits(self.raw, 6, 29);
// Sign-extend from 24 bits, then shift left 2
let sign_extended = ((li as i32) << 8) >> 8;
@@ -53,87 +87,154 @@ impl DecodedInstr {
}
/// BD field for conditional branch (bits 16-29, sign-extended, shifted left 2)
#[inline] pub fn bd(&self) -> i32 {
#[inline]
pub fn bd(&self) -> i32 {
let bd = extract_bits(self.raw, 16, 29);
let sign_extended = ((bd as i32) << 18) >> 18;
sign_extended << 2
}
/// BO field (bits 6-10) - branch options
#[inline] pub fn bo(&self) -> u32 { extract_bits(self.raw, 6, 10) }
#[inline]
pub fn bo(&self) -> u32 {
extract_bits(self.raw, 6, 10)
}
/// BI field (bits 11-15) - branch condition
#[inline] pub fn bi(&self) -> u32 { extract_bits(self.raw, 11, 15) }
#[inline]
pub fn bi(&self) -> u32 {
extract_bits(self.raw, 11, 15)
}
/// AA bit (bit 30) - absolute address
#[inline] pub fn aa(&self) -> bool { (self.raw >> 1) & 1 != 0 }
#[inline]
pub fn aa(&self) -> bool {
(self.raw >> 1) & 1 != 0
}
/// LK bit (bit 31) - link (update LR)
#[inline] pub fn lk(&self) -> bool { self.raw & 1 != 0 }
#[inline]
pub fn lk(&self) -> bool {
self.raw & 1 != 0
}
/// Rc bit (bit 31) - record CR0
#[inline] pub fn rc_bit(&self) -> bool { self.raw & 1 != 0 }
#[inline]
pub fn rc_bit(&self) -> bool {
self.raw & 1 != 0
}
/// Rc for VC-form vector compare instructions — PPC bit 21 = host bit 10.
#[inline] pub fn vc_rc_bit(&self) -> bool { (self.raw >> 10) & 1 != 0 }
#[inline]
pub fn vc_rc_bit(&self) -> bool {
(self.raw >> 10) & 1 != 0
}
/// Rc for VX128_R-form vector compare instructions — PPC bit 27 = host bit 4.
/// VX128_R Rc bit — PPC bit 25 (host bit 6) per canary's FormatVX128_R
/// bitfield layout. PPCBUG-700.
#[inline] pub fn vx128r_rc_bit(&self) -> bool { (self.raw >> 6) & 1 != 0 }
#[inline]
pub fn vx128r_rc_bit(&self) -> bool {
(self.raw >> 6) & 1 != 0
}
/// IMM field for VX128_4-form instructions (vrlimi128) — 5-bit blend mask at PPC bits 11-15.
#[inline] pub fn vx128_4_imm(&self) -> u32 { extract_bits(self.raw, 11, 15) }
#[inline]
pub fn vx128_4_imm(&self) -> u32 {
extract_bits(self.raw, 11, 15)
}
/// z field for VX128_4-form instructions (vrlimi128) — 2-bit rotation index at PPC bits 24-25.
#[inline] pub fn vx128_4_z(&self) -> u32 { extract_bits(self.raw, 24, 25) }
#[inline]
pub fn vx128_4_z(&self) -> u32 {
extract_bits(self.raw, 24, 25)
}
/// OE bit (bit 21) - overflow enable
#[inline] pub fn oe(&self) -> bool { extract_bits(self.raw, 21, 21) != 0 }
#[inline]
pub fn oe(&self) -> bool {
extract_bits(self.raw, 21, 21) != 0
}
/// TO field (bits 6-10) for tw/twi/td/tdi trap instructions.
#[inline] pub fn to(&self) -> u32 { extract_bits(self.raw, 6, 10) }
#[inline]
pub fn to(&self) -> u32 {
extract_bits(self.raw, 6, 10)
}
/// MB, ME fields for rotate instructions
#[inline] pub fn mb(&self) -> u32 { extract_bits(self.raw, 21, 25) }
#[inline] pub fn me(&self) -> u32 { extract_bits(self.raw, 26, 30) }
#[inline]
pub fn mb(&self) -> u32 {
extract_bits(self.raw, 21, 25)
}
#[inline]
pub fn me(&self) -> u32 {
extract_bits(self.raw, 26, 30)
}
/// SH field (bits 16-20) for shift instructions
#[inline] pub fn sh(&self) -> u32 { extract_bits(self.raw, 16, 20) }
#[inline]
pub fn sh(&self) -> u32 {
extract_bits(self.raw, 16, 20)
}
/// SH field for 64-bit shifts (bits 16-20 + bit 30)
#[inline] pub fn sh64(&self) -> u32 {
#[inline]
pub fn sh64(&self) -> u32 {
(extract_bits(self.raw, 30, 30) << 5) | extract_bits(self.raw, 16, 20)
}
/// MB/ME field for MD-form and MDS-form instructions (6-bit field, split encoding).
/// MB[4:0] at PPC bits 21-25; MB[5] at PPC bit 26.
#[inline] pub fn mb_md(&self) -> u32 {
#[inline]
pub fn mb_md(&self) -> u32 {
extract_bits(self.raw, 21, 25) | (extract_bits(self.raw, 26, 26) << 5)
}
/// SPR field (bits 11-20, swapped halves)
#[inline] pub fn spr(&self) -> u32 {
#[inline]
pub fn spr(&self) -> u32 {
let spr_raw = extract_bits(self.raw, 11, 20);
((spr_raw & 0x1F) << 5) | ((spr_raw >> 5) & 0x1F)
}
/// CRM field (bits 12-19) for mtcrf
#[inline] pub fn crm(&self) -> u32 { extract_bits(self.raw, 12, 19) }
#[inline]
pub fn crm(&self) -> u32 {
extract_bits(self.raw, 12, 19)
}
/// crfD (bits 6-8) - condition register field destination
#[inline] pub fn crfd(&self) -> usize { extract_bits(self.raw, 6, 8) as usize }
#[inline]
pub fn crfd(&self) -> usize {
extract_bits(self.raw, 6, 8) as usize
}
/// crfS (bits 11-13)
#[inline] pub fn crfs(&self) -> usize { extract_bits(self.raw, 11, 13) as usize }
#[inline]
pub fn crfs(&self) -> usize {
extract_bits(self.raw, 11, 13) as usize
}
/// L bit (bit 10) - 64-bit compare
#[inline] pub fn l(&self) -> bool { extract_bits(self.raw, 10, 10) != 0 }
#[inline]
pub fn l(&self) -> bool {
extract_bits(self.raw, 10, 10) != 0
}
/// crbD (bits 6-10)
#[inline] pub fn crbd(&self) -> u32 { extract_bits(self.raw, 6, 10) }
#[inline]
pub fn crbd(&self) -> u32 {
extract_bits(self.raw, 6, 10)
}
/// crbA (bits 11-15)
#[inline] pub fn crba(&self) -> u32 { extract_bits(self.raw, 11, 15) }
#[inline]
pub fn crba(&self) -> u32 {
extract_bits(self.raw, 11, 15)
}
/// crbB (bits 16-20)
#[inline] pub fn crbb(&self) -> u32 { extract_bits(self.raw, 16, 20) }
#[inline]
pub fn crbb(&self) -> u32 {
extract_bits(self.raw, 16, 20)
}
// VMX128 field extractors — bit positions match canary's
// FormatVX128/VX128_2/VX128_4/VX128_5/VX128_R bitfield layout
@@ -141,7 +242,8 @@ impl DecodedInstr {
/// VA128 = VA128l(5) | VA128h(1) << 5 | VA128H(1) << 6.
/// Canonical 7-bit register selector: PPC 11-15 (low), PPC 26 (mid), PPC 21 (high).
#[inline] pub fn va128(&self) -> usize {
#[inline]
pub fn va128(&self) -> usize {
(extract_bits(self.raw, 11, 15)
| (extract_bits(self.raw, 26, 26) << 5)
| (extract_bits(self.raw, 21, 21) << 6)) as usize
@@ -149,35 +251,48 @@ impl DecodedInstr {
/// VB128 = VB128l(5) | VB128h(2) << 5. Canary's VB128h is a 2-bit
/// contiguous field at PPC 30-31 (host bits 0-1).
#[inline] pub fn vb128(&self) -> usize {
(extract_bits(self.raw, 16, 20)
| (extract_bits(self.raw, 30, 31) << 5)) as usize
#[inline]
pub fn vb128(&self) -> usize {
(extract_bits(self.raw, 16, 20) | (extract_bits(self.raw, 30, 31) << 5)) as usize
}
/// VD128 = VD128l(5) | VD128h(2) << 5. Canary's VD128h is a 2-bit
/// contiguous field at PPC 28-29 (host bits 2-3).
#[inline] pub fn vd128(&self) -> usize {
(extract_bits(self.raw, 6, 10)
| (extract_bits(self.raw, 28, 29) << 5)) as usize
#[inline]
pub fn vd128(&self) -> usize {
(extract_bits(self.raw, 6, 10) | (extract_bits(self.raw, 28, 29) << 5)) as usize
}
/// VS128 - same encoding as VD128
#[inline] pub fn vs128(&self) -> usize { self.vd128() }
#[inline]
pub fn vs128(&self) -> usize {
self.vd128()
}
/// VC register for VX128_2-form instructions (vperm128) — 3-bit at PPC bits 23-25.
#[inline] pub fn vc128_2(&self) -> usize { extract_bits(self.raw, 23, 25) as usize }
#[inline]
pub fn vc128_2(&self) -> usize {
extract_bits(self.raw, 23, 25) as usize
}
/// NB field (bits 16-20) for lswi/stswi
#[inline] pub fn nb(&self) -> u32 { extract_bits(self.raw, 16, 20) }
#[inline]
pub fn nb(&self) -> u32 {
extract_bits(self.raw, 16, 20)
}
/// PERM field for VX128_P-form instructions (vpermwi128) — 8-bit split encoding.
/// PERMl (5 bits) at PPC bits 11-15; PERMh (3 bits) at PPC bits 23-25.
#[inline] pub fn vx128_p_perm(&self) -> u32 {
#[inline]
pub fn vx128_p_perm(&self) -> u32 {
extract_bits(self.raw, 11, 15) | (extract_bits(self.raw, 23, 25) << 5)
}
/// SH field for VX128_5-form instructions (vsldoi128) — 4-bit shift at PPC bits 22-25.
#[inline] pub fn vx128_5_sh(&self) -> u32 { extract_bits(self.raw, 22, 25) }
#[inline]
pub fn vx128_5_sh(&self) -> u32 {
extract_bits(self.raw, 22, 25)
}
}
/// Extract the 5-bit `UIMM` (`VX128_3`) / `IMM` (`VX128_4`) field. Canary
@@ -1057,8 +1172,15 @@ mod tests {
/// vd_hi is 2 bits (PPC 28-29). Same shape for vb128 (vb_lo at PPC 16-20,
/// vb_hi 2 bits at PPC 30-31). va128 = va_lo | (va_h26<<5) | (va_h21<<6)
/// per canary's 7-bit VA selector.
fn vmx128_test_word(vd_lo: u32, vd_hi: u32, va_lo: u32, va_h26: u32, va_h21: u32,
vb_lo: u32, vb_hi: u32) -> u32 {
fn vmx128_test_word(
vd_lo: u32,
vd_hi: u32,
va_lo: u32,
va_h26: u32,
va_h21: u32,
vb_lo: u32,
vb_hi: u32,
) -> u32 {
// PPC bit i -> host bit (31-i).
(vd_lo << (31 - 10)) // VD128l: PPC 6-10 = host 21-25
| (vd_hi << (31 - 29)) // VD128h: PPC 28-29 = host 2-3 (LSB at host 2)
@@ -1066,15 +1188,19 @@ mod tests {
| (va_h26 << (31 - 26)) // VA128h: PPC 26 = host 5
| (va_h21 << (31 - 21)) // VA128H: PPC 21 = host 10
| (vb_lo << (31 - 20)) // VB128l: PPC 16-20 = host 11-15
| (vb_hi << (31 - 31)) // VB128h: PPC 30-31 = host 0-1 (LSB at host 0)
| vb_hi // VB128h: PPC 30-31 = host 0-1 (LSB at host 0)
}
#[test]
fn vmx128_vd128_low_5_bits_only() {
// vd_lo = 0..31, vd_hi = 0 → vd128 = vd_lo
for r in 0..32u32 {
let raw = (r as u32) << (31 - 10);
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let raw = r << (31 - 10);
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vd128(), r as usize, "vd_lo={r}");
}
}
@@ -1082,26 +1208,36 @@ mod tests {
#[test]
fn vmx128_vd128_high_low_bit_adds_32() {
// vd_lo = 0, VD128h = 0b01 (LSB only at host bit 2 = PPC 29) → vd128 = 32
let raw = (1u32 << (31 - 29));
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let raw = 1u32 << (31 - 29);
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vd128(), 32);
}
#[test]
fn vmx128_vd128_high_high_bit_adds_64() {
// vd_lo = 0, VD128h = 0b10 (MSB only at host bit 3 = PPC 28) → vd128 = 64
let raw = (1u32 << (31 - 28));
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let raw = 1u32 << (31 - 28);
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vd128(), 64);
}
#[test]
fn vmx128_vd128_full_127() {
// vd_lo = 31, VD128h = 0b11 → vd128 = 127
let raw = (31u32 << (31 - 10))
| (1u32 << (31 - 28))
| (1u32 << (31 - 29));
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let raw = (31u32 << (31 - 10)) | (1u32 << (31 - 28)) | (1u32 << (31 - 29));
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vd128(), 127);
}
@@ -1109,11 +1245,19 @@ mod tests {
fn vmx128_va128_canary_layout() {
// va_lo = 7 at PPC 11-15, VA128h = 1 at PPC 26 → va128 = 7 | 32 = 39
let raw = (7u32 << (31 - 15)) | (1u32 << (31 - 26));
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.va128(), 39);
// VA128H = 1 at PPC 21 → va128 += 64 = 103
let raw = raw | (1u32 << (31 - 21));
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.va128(), 7 | 32 | 64);
}
@@ -1122,10 +1266,18 @@ mod tests {
// vb_lo = 5 at PPC 16-20. VB128h = 0b01 (LSB at PPC 31 = host 0) → +32.
// VB128h = 0b11 → +96.
let raw = (5u32 << (31 - 20)) | (1u32 << (31 - 31));
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vb128(), 5 | 32);
let raw = raw | (1u32 << (31 - 30));
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vb128(), 5 | 32 | 64);
}
@@ -1135,9 +1287,12 @@ mod tests {
for r in [0u32, 31, 32, 64, 96, 127] {
let lo = r & 0x1F;
let hi = (r >> 5) & 0x3;
let raw = (lo << (31 - 10))
| (hi << (31 - 29));
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let raw = (lo << (31 - 10)) | (hi << (31 - 29));
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vd128(), r as usize, "vd128 mismatch for r={r}");
assert_eq!(d.vs128(), r as usize, "vs128 mismatch for r={r}");
assert_eq!(d.vd128(), d.vs128());
@@ -1150,7 +1305,11 @@ mod tests {
// Keep the helper validated against the real accessor.
// vd_lo=5, vd_hi=0b11 → vd128 = 5 | 96 = 101
let raw = vmx128_test_word(5, 3, 0, 0, 0, 0, 0);
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vd128(), 5 | 32 | 64);
}
@@ -1160,21 +1319,37 @@ mod tests {
// Host bit 9 = 1 (PPC bit 22), host bits 6-8 = 0.
// So raw bit 9 set = raw |= 1 << 9 = 0x200
let raw = 0x200u32; // host bit 9 set only
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vx128_5_sh(), 8, "SH=8: MSB at PPC bit 22");
// SH=1 (binary 0001): host bit 6 set = raw |= 1 << 6 = 0x40
let raw = 0x40u32;
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vx128_5_sh(), 1, "SH=1: LSB at PPC bit 25");
// SH=15 (binary 1111): host bits 6-9 all set = raw |= 0xF << 6 = 0x3C0
let raw = 0x3C0u32;
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vx128_5_sh(), 15, "SH=15: all 4 bits set");
// SH=0: raw=0
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw: 0, addr: 0 };
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw: 0,
addr: 0,
};
assert_eq!(d.vx128_5_sh(), 0, "SH=0");
}
@@ -1182,23 +1357,39 @@ mod tests {
fn vx128_4_accessors_correct_bit_positions() {
// z=3 (binary 11) at PPC bits 24-25 = host bits 6-7
let raw = 0b11u32 << 6;
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vx128_4_z(), 3, "z=3 from host bits 6-7");
// IMM=0x15 (binary 10101) at PPC bits 11-15 = host bits 16-20
let raw2 = 0x15u32 << 16;
let d2 = DecodedInstr { opcode: PpcOpcode::Invalid, raw: raw2, addr: 0 };
let d2 = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw: raw2,
addr: 0,
};
assert_eq!(d2.vx128_4_imm(), 0x15, "IMM=0x15 from host bits 16-20");
// Combined: z=1, IMM=0xA — fields must not bleed into each other
let raw3 = (0x1u32 << 6) | (0xAu32 << 16);
let d3 = DecodedInstr { opcode: PpcOpcode::Invalid, raw: raw3, addr: 0 };
let d3 = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw: raw3,
addr: 0,
};
assert_eq!(d3.vx128_4_z(), 1, "z=1 combined");
assert_eq!(d3.vx128_4_imm(), 0xA, "IMM=0xA combined");
// z=2, IMM=0xF — max 4-bit blend mask, exercises the full lower nibble
let raw4 = (0b10u32 << 6) | (0xFu32 << 16);
let d4 = DecodedInstr { opcode: PpcOpcode::Invalid, raw: raw4, addr: 0 };
let d4 = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw: raw4,
addr: 0,
};
assert_eq!(d4.vx128_4_z(), 2, "z=2 from binary 10");
assert_eq!(d4.vx128_4_imm(), 0xF, "IMM=0xF all-ones nibble");
}
@@ -1208,16 +1399,32 @@ mod tests {
// VC=5 (binary 101) at PPC bits 23-25 = host bits 6-8
// extract_bits(raw, 23, 25) = (raw >> (31-25)) & 0x7 = (raw >> 6) & 0x7
let raw = 5u32 << 6; // host bits 6-8 = 5
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vc128_2(), 5);
let d0 = DecodedInstr { opcode: PpcOpcode::Invalid, raw: 0, addr: 0 };
let d0 = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw: 0,
addr: 0,
};
assert_eq!(d0.vc128_2(), 0);
let d7 = DecodedInstr { opcode: PpcOpcode::Invalid, raw: 7u32 << 6, addr: 0 };
let d7 = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw: 7u32 << 6,
addr: 0,
};
assert_eq!(d7.vc128_2(), 7);
let d1 = DecodedInstr { opcode: PpcOpcode::Invalid, raw: 1u32 << 6, addr: 0 };
let d1 = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw: 1u32 << 6,
addr: 0,
};
assert_eq!(d1.vc128_2(), 1);
}
@@ -1225,21 +1432,37 @@ mod tests {
fn vx128_p_perm_assembles_correctly() {
// PERMl=0x1F (all 5 bits set) at host bits 16-20: raw = 0x1F << 16
let raw = 0x1Fu32 << 16;
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vx128_p_perm(), 0x1F, "PERMl only");
// PERMh=0x7 (all 3 bits set) at host bits 6-8: raw = 0x7 << 6 = 0x1C0
let raw = 0x7u32 << 6;
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vx128_p_perm(), 0x7 << 5, "PERMh only: bits 5-7");
// PERMl=0xA, PERMh=0x5: raw = (0xA << 16) | (0x5 << 6)
let raw = (0xAu32 << 16) | (0x5u32 << 6);
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw, addr: 0 };
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw,
addr: 0,
};
assert_eq!(d.vx128_p_perm(), 0xA | (0x5 << 5));
// PERMl and PERMh bits must not bleed into each other
let d = DecodedInstr { opcode: PpcOpcode::Invalid, raw: 0, addr: 0 };
let d = DecodedInstr {
opcode: PpcOpcode::Invalid,
raw: 0,
addr: 0,
};
assert_eq!(d.vx128_p_perm(), 0);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -5,131 +5,498 @@
#[allow(non_camel_case_types)]
pub enum PpcOpcode {
// ALU
addcx, addex, addi, addic, addicx, addis, addmex, addx, addzex,
andcx, andisx, andix, andx,
addcx,
addex,
addi,
addic,
addicx,
addis,
addmex,
addx,
addzex,
andcx,
andisx,
andix,
andx,
// Branch
bcctrx, bclrx, bcx, bx,
bcctrx,
bclrx,
bcx,
bx,
// Compare
cmp, cmpi, cmpl, cmpli,
cmp,
cmpi,
cmpl,
cmpli,
// Count leading zeros
cntlzdx, cntlzwx,
cntlzdx,
cntlzwx,
// Condition register
crand, crandc, creqv, crnand, crnor, cror, crorc, crxor,
crand,
crandc,
creqv,
crnand,
crnor,
cror,
crorc,
crxor,
// Data cache
dcbf, dcbi, dcbst, dcbt, dcbtst, dcbz, dcbz128,
dcbf,
dcbi,
dcbst,
dcbt,
dcbtst,
dcbz,
dcbz128,
// Division
divdux, divdx, divwux, divwx,
divdux,
divdx,
divwux,
divwx,
// Sync/barrier
eieio,
// Logical
eqvx, extsbx, extshx, extswx,
eqvx,
extsbx,
extshx,
extswx,
// FPU
fabsx, faddsx, faddx, fcfidx, fcmpo, fcmpu, fctidx, fctidzx, fctiwx, fctiwzx,
fdivsx, fdivx, fmaddsx, fmaddx, fmrx, fmsubsx, fmsubx, fmulsx, fmulx,
fnabsx, fnegx, fnmaddsx, fnmaddx, fnmsubsx, fnmsubx, fresx, frspx, frsqrtex,
fselx, fsqrtsx, fsqrtx, fsubsx, fsubx,
fabsx,
faddsx,
faddx,
fcfidx,
fcmpo,
fcmpu,
fctidx,
fctidzx,
fctiwx,
fctiwzx,
fdivsx,
fdivx,
fmaddsx,
fmaddx,
fmrx,
fmsubsx,
fmsubx,
fmulsx,
fmulx,
fnabsx,
fnegx,
fnmaddsx,
fnmaddx,
fnmsubsx,
fnmsubx,
fresx,
frspx,
frsqrtex,
fselx,
fsqrtsx,
fsqrtx,
fsubsx,
fsubx,
// Instruction cache
icbi, isync,
icbi,
isync,
// Load byte
lbz, lbzu, lbzux, lbzx,
lbz,
lbzu,
lbzux,
lbzx,
// Load doubleword
ld, ldarx, ldbrx, ldu, ldux, ldx,
ld,
ldarx,
ldbrx,
ldu,
ldux,
ldx,
// Load float
lfd, lfdu, lfdux, lfdx, lfs, lfsu, lfsux, lfsx,
lfd,
lfdu,
lfdux,
lfdx,
lfs,
lfsu,
lfsux,
lfsx,
// Load halfword
lha, lhau, lhaux, lhax, lhbrx, lhz, lhzu, lhzux, lhzx,
lha,
lhau,
lhaux,
lhax,
lhbrx,
lhz,
lhzu,
lhzux,
lhzx,
// Load multiple/string
lmw, lswi, lswx,
lmw,
lswi,
lswx,
// Load vector
lvebx, lvehx, lvewx, lvewx128, lvlx, lvlx128, lvlxl, lvlxl128,
lvrx, lvrx128, lvrxl, lvrxl128,
lvsl, lvsl128, lvsr, lvsr128,
lvx, lvx128, lvxl, lvxl128,
lvebx,
lvehx,
lvewx,
lvewx128,
lvlx,
lvlx128,
lvlxl,
lvlxl128,
lvrx,
lvrx128,
lvrxl,
lvrxl128,
lvsl,
lvsl128,
lvsr,
lvsr128,
lvx,
lvx128,
lvxl,
lvxl128,
// Load word
lwa, lwarx, lwaux, lwax, lwbrx, lwz, lwzu, lwzux, lwzx,
lwa,
lwarx,
lwaux,
lwax,
lwbrx,
lwz,
lwzu,
lwzux,
lwzx,
// Move CR
mcrf, mcrfs, mcrxr,
mcrf,
mcrfs,
mcrxr,
// Move from special
mfcr, mffsx, mfmsr, mfspr, mftb, mfvscr,
mfcr,
mffsx,
mfmsr,
mfspr,
mftb,
mfvscr,
// Move to special
mtcrf, mtfsb0x, mtfsb1x, mtfsfix, mtfsfx, mtmsr, mtmsrd, mtspr, mtvscr,
mtcrf,
mtfsb0x,
mtfsb1x,
mtfsfix,
mtfsfx,
mtmsr,
mtmsrd,
mtspr,
mtvscr,
// Multiply
mulhdux, mulhdx, mulhwux, mulhwx, mulldx, mulli, mullwx,
mulhdux,
mulhdx,
mulhwux,
mulhwx,
mulldx,
mulli,
mullwx,
// Logical
nandx, negx, norx, orcx, ori, oris, orx,
nandx,
negx,
norx,
orcx,
ori,
oris,
orx,
// Rotate
rldclx, rldcrx, rldiclx, rldicrx, rldicx, rldimix, rlwimix, rlwinmx, rlwnmx,
rldclx,
rldcrx,
rldiclx,
rldicrx,
rldicx,
rldimix,
rlwimix,
rlwinmx,
rlwnmx,
// System call
sc,
// Shift
sldx, slwx, sradix, sradx, srawix, srawx, srdx, srwx,
sldx,
slwx,
sradix,
sradx,
srawix,
srawx,
srdx,
srwx,
// Store byte
stb, stbu, stbux, stbx,
stb,
stbu,
stbux,
stbx,
// Store doubleword
std, stdbrx, stdcx, stdu, stdux, stdx,
std,
stdbrx,
stdcx,
stdu,
stdux,
stdx,
// Store float
stfd, stfdu, stfdux, stfdx, stfiwx, stfs, stfsu, stfsux, stfsx,
stfd,
stfdu,
stfdux,
stfdx,
stfiwx,
stfs,
stfsu,
stfsux,
stfsx,
// Store halfword
sth, sthbrx, sthu, sthux, sthx,
sth,
sthbrx,
sthu,
sthux,
sthx,
// Store multiple/string
stmw, stswi, stswx,
stmw,
stswi,
stswx,
// Store vector
stvebx, stvehx, stvewx, stvewx128, stvlx, stvlx128, stvlxl, stvlxl128,
stvrx, stvrx128, stvrxl, stvrxl128,
stvx, stvx128, stvxl, stvxl128,
stvebx,
stvehx,
stvewx,
stvewx128,
stvlx,
stvlx128,
stvlxl,
stvlxl128,
stvrx,
stvrx128,
stvrxl,
stvrxl128,
stvx,
stvx128,
stvxl,
stvxl128,
// Store word
stw, stwbrx, stwcx, stwu, stwux, stwx,
stw,
stwbrx,
stwcx,
stwu,
stwux,
stwx,
// Subtract
subfcx, subfex, subficx, subfmex, subfx, subfzex,
subfcx,
subfex,
subficx,
subfmex,
subfx,
subfzex,
// Sync
sync,
// Trap
td, tdi, tw, twi,
td,
tdi,
tw,
twi,
// VMX integer
vaddcuw, vaddfp, vaddfp128, vaddsbs, vaddshs, vaddsws,
vaddubm, vaddubs, vadduhm, vadduhs, vadduwm, vadduws,
vand, vand128, vandc, vandc128,
vavgsb, vavgsh, vavgsw, vavgub, vavguh, vavguw,
vcfpsxws128, vcfpuxws128, vcfsx, vcfux,
vcmpbfp, vcmpbfp128, vcmpeqfp, vcmpeqfp128,
vcmpequb, vcmpequh, vcmpequw, vcmpequw128,
vcmpgefp, vcmpgefp128, vcmpgtfp, vcmpgtfp128,
vcmpgtsb, vcmpgtsh, vcmpgtsw, vcmpgtub, vcmpgtuh, vcmpgtuw,
vcsxwfp128, vctsxs, vctuxs, vcuxwfp128,
vexptefp, vexptefp128, vlogefp, vlogefp128,
vmaddcfp128, vmaddfp, vmaddfp128,
vmaxfp, vmaxfp128, vmaxsb, vmaxsh, vmaxsw, vmaxub, vmaxuh, vmaxuw,
vmhaddshs, vmhraddshs,
vminfp, vminfp128, vminsb, vminsh, vminsw, vminub, vminuh, vminuw,
vaddcuw,
vaddfp,
vaddfp128,
vaddsbs,
vaddshs,
vaddsws,
vaddubm,
vaddubs,
vadduhm,
vadduhs,
vadduwm,
vadduws,
vand,
vand128,
vandc,
vandc128,
vavgsb,
vavgsh,
vavgsw,
vavgub,
vavguh,
vavguw,
vcfpsxws128,
vcfpuxws128,
vcfsx,
vcfux,
vcmpbfp,
vcmpbfp128,
vcmpeqfp,
vcmpeqfp128,
vcmpequb,
vcmpequh,
vcmpequw,
vcmpequw128,
vcmpgefp,
vcmpgefp128,
vcmpgtfp,
vcmpgtfp128,
vcmpgtsb,
vcmpgtsh,
vcmpgtsw,
vcmpgtub,
vcmpgtuh,
vcmpgtuw,
vcsxwfp128,
vctsxs,
vctuxs,
vcuxwfp128,
vexptefp,
vexptefp128,
vlogefp,
vlogefp128,
vmaddcfp128,
vmaddfp,
vmaddfp128,
vmaxfp,
vmaxfp128,
vmaxsb,
vmaxsh,
vmaxsw,
vmaxub,
vmaxuh,
vmaxuw,
vmhaddshs,
vmhraddshs,
vminfp,
vminfp128,
vminsb,
vminsh,
vminsw,
vminub,
vminuh,
vminuw,
vmladduhm,
vmrghb, vmrghh, vmrghw, vmrghw128, vmrglb, vmrglh, vmrglw, vmrglw128,
vmsum3fp128, vmsum4fp128,
vmsummbm, vmsumshm, vmsumshs, vmsumubm, vmsumuhm, vmsumuhs,
vmulesb, vmulesh, vmuleub, vmuleuh, vmulfp128,
vmulosb, vmulosh, vmuloub, vmulouh,
vnmsubfp, vnmsubfp128, vnor, vnor128,
vor, vor128,
vperm, vperm128, vpermwi128, vpkd3d128,
vpkpx, vpkshss, vpkshss128, vpkshus, vpkshus128,
vpkswss, vpkswss128, vpkswus, vpkswus128,
vpkuhum, vpkuhum128, vpkuhus, vpkuhus128,
vpkuwum, vpkuwum128, vpkuwus, vpkuwus128,
vrefp, vrefp128,
vrfim, vrfim128, vrfin, vrfin128, vrfip, vrfip128, vrfiz, vrfiz128,
vrlb, vrlh, vrlimi128, vrlw, vrlw128,
vrsqrtefp, vrsqrtefp128,
vsel, vsel128,
vsl, vslb, vsldoi, vsldoi128, vslh, vslo, vslo128, vslw, vslw128,
vspltb, vsplth, vspltisb, vspltish, vspltisw, vspltisw128, vspltw, vspltw128,
vsr, vsrab, vsrah, vsraw, vsraw128, vsrb, vsrh, vsro, vsro128, vsrw, vsrw128,
vsubcuw, vsubfp, vsubfp128, vsubsbs, vsubshs, vsubsws,
vsububm, vsububs, vsubuhm, vsubuhs, vsubuwm, vsubuws,
vsum2sws, vsum4sbs, vsum4shs, vsum4ubs, vsumsws,
vupkd3d128, vupkhpx, vupkhsb, vupkhsb128, vupkhsh,
vupklpx, vupklsb, vupklsb128, vupklsh,
vxor, vxor128,
vmrghb,
vmrghh,
vmrghw,
vmrghw128,
vmrglb,
vmrglh,
vmrglw,
vmrglw128,
vmsum3fp128,
vmsum4fp128,
vmsummbm,
vmsumshm,
vmsumshs,
vmsumubm,
vmsumuhm,
vmsumuhs,
vmulesb,
vmulesh,
vmuleub,
vmuleuh,
vmulfp128,
vmulosb,
vmulosh,
vmuloub,
vmulouh,
vnmsubfp,
vnmsubfp128,
vnor,
vnor128,
vor,
vor128,
vperm,
vperm128,
vpermwi128,
vpkd3d128,
vpkpx,
vpkshss,
vpkshss128,
vpkshus,
vpkshus128,
vpkswss,
vpkswss128,
vpkswus,
vpkswus128,
vpkuhum,
vpkuhum128,
vpkuhus,
vpkuhus128,
vpkuwum,
vpkuwum128,
vpkuwus,
vpkuwus128,
vrefp,
vrefp128,
vrfim,
vrfim128,
vrfin,
vrfin128,
vrfip,
vrfip128,
vrfiz,
vrfiz128,
vrlb,
vrlh,
vrlimi128,
vrlw,
vrlw128,
vrsqrtefp,
vrsqrtefp128,
vsel,
vsel128,
vsl,
vslb,
vsldoi,
vsldoi128,
vslh,
vslo,
vslo128,
vslw,
vslw128,
vspltb,
vsplth,
vspltisb,
vspltish,
vspltisw,
vspltisw128,
vspltw,
vspltw128,
vsr,
vsrab,
vsrah,
vsraw,
vsraw128,
vsrb,
vsrh,
vsro,
vsro128,
vsrw,
vsrw128,
vsubcuw,
vsubfp,
vsubfp128,
vsubsbs,
vsubshs,
vsubsws,
vsububm,
vsububs,
vsubuhm,
vsubuhs,
vsubuwm,
vsubuws,
vsum2sws,
vsum4sbs,
vsum4shs,
vsum4ubs,
vsumsws,
vupkd3d128,
vupkhpx,
vupkhsb,
vupkhsb128,
vupkhsh,
vupklpx,
vupklsb,
vupklsb128,
vupklsh,
vxor,
vxor128,
// XOR immediate
xori, xoris, xorx,
xori,
xoris,
xorx,
// Invalid
Invalid,
}
@@ -165,42 +532,102 @@ impl PpcOpcode {
pub fn terminates_block(&self) -> bool {
matches!(
self,
Self::bx | Self::bcx | Self::bclrx | Self::bcctrx
Self::bx
| Self::bcx
| Self::bclrx
| Self::bcctrx
| Self::sc
| Self::td | Self::tdi | Self::tw | Self::twi
| Self::td
| Self::tdi
| Self::tw
| Self::twi
| Self::Invalid
)
}
/// Returns true if this is a load instruction.
pub fn is_load(&self) -> bool {
matches!(self,
Self::lbz | Self::lbzu | Self::lbzux | Self::lbzx |
Self::lhz | Self::lhzu | Self::lhzux | Self::lhzx |
Self::lha | Self::lhau | Self::lhaux | Self::lhax |
Self::lwz | Self::lwzu | Self::lwzux | Self::lwzx |
Self::lwa | Self::lwax | Self::lwaux |
Self::ld | Self::ldu | Self::ldux | Self::ldx |
Self::lfs | Self::lfsu | Self::lfsux | Self::lfsx |
Self::lfd | Self::lfdu | Self::lfdux | Self::lfdx |
Self::lhbrx | Self::lwbrx | Self::ldbrx |
Self::lmw | Self::lswi | Self::lswx |
Self::lwarx | Self::ldarx
matches!(
self,
Self::lbz
| Self::lbzu
| Self::lbzux
| Self::lbzx
| Self::lhz
| Self::lhzu
| Self::lhzux
| Self::lhzx
| Self::lha
| Self::lhau
| Self::lhaux
| Self::lhax
| Self::lwz
| Self::lwzu
| Self::lwzux
| Self::lwzx
| Self::lwa
| Self::lwax
| Self::lwaux
| Self::ld
| Self::ldu
| Self::ldux
| Self::ldx
| Self::lfs
| Self::lfsu
| Self::lfsux
| Self::lfsx
| Self::lfd
| Self::lfdu
| Self::lfdux
| Self::lfdx
| Self::lhbrx
| Self::lwbrx
| Self::ldbrx
| Self::lmw
| Self::lswi
| Self::lswx
| Self::lwarx
| Self::ldarx
)
}
/// Returns true if this is a store instruction.
pub fn is_store(&self) -> bool {
matches!(self,
Self::stb | Self::stbu | Self::stbux | Self::stbx |
Self::sth | Self::sthu | Self::sthux | Self::sthx |
Self::stw | Self::stwu | Self::stwux | Self::stwx |
Self::std | Self::stdu | Self::stdux | Self::stdx |
Self::stfs | Self::stfsu | Self::stfsux | Self::stfsx |
Self::stfd | Self::stfdu | Self::stfdux | Self::stfdx |
Self::sthbrx | Self::stwbrx | Self::stdbrx |
Self::stmw | Self::stswi | Self::stswx |
Self::stwcx | Self::stdcx | Self::stfiwx
matches!(
self,
Self::stb
| Self::stbu
| Self::stbux
| Self::stbx
| Self::sth
| Self::sthu
| Self::sthux
| Self::sthx
| Self::stw
| Self::stwu
| Self::stwux
| Self::stwx
| Self::std
| Self::stdu
| Self::stdux
| Self::stdx
| Self::stfs
| Self::stfsu
| Self::stfsux
| Self::stfsx
| Self::stfd
| Self::stfdu
| Self::stfdux
| Self::stfdx
| Self::sthbrx
| Self::stwbrx
| Self::stdbrx
| Self::stmw
| Self::stswi
| Self::stswx
| Self::stwcx
| Self::stdcx
| Self::stfiwx
)
}
@@ -227,8 +654,13 @@ impl PpcOpcode {
pub fn is_sync_sensitive(&self) -> bool {
matches!(
self,
Self::lwarx | Self::ldarx | Self::stwcx | Self::stdcx
| Self::sync | Self::eieio | Self::isync
Self::lwarx
| Self::ldarx
| Self::stwcx
| Self::stdcx
| Self::sync
| Self::eieio
| Self::isync
)
}