xenia-cpu: VMX128, FPSCR, decoder split, scheduler, decode/block caches
Split the monolithic interpreter into cohesive modules: dedicated decoder (decoder.rs) producing 8-byte DecodedInstr; opcode tables (opcode.rs); explicit traps (trap.rs); FPSCR helpers (fpscr.rs); overflow/carry helpers (overflow.rs); a 4 KiB-page-versioned decode cache and basic-block cache (block_cache.rs); and a full VMX/VMX128 implementation (vmx.rs) covering AltiVec + Xenon's 128-bit extensions. Add the parallel-execution substrate behind --parallel: a 7-party phaser (phaser.rs) for round-based barrier sync, ReservationTable (reservation.rs) for guest LL/SC, and the per-HW-thread scheduler core (scheduler.rs) that owns ThreadRefs, runqueues, and pending IRQs. Disassembler is now the single source of truth: disasm.rs gains the full base + extended + VMX128 mnemonic set, with golden JSON fixtures and a disasm_goldens test suite. Add a criterion-style interpreter bench. context.rs grows the per-thread state the new modules need (reservation slot, FPSCR, vector regs). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
95
crates/xenia-cpu/src/trap.rs
Normal file
95
crates/xenia-cpu/src/trap.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
//! TO-field evaluation for `tw`, `twi`, `td`, `tdi`.
|
||||
//!
|
||||
//! The TO field (5 bits) encodes which comparison outcomes trigger a trap:
|
||||
//!
|
||||
//! | bit | condition |
|
||||
//! |-----|-----------|
|
||||
//! | 0 | a < b (signed) |
|
||||
//! | 1 | a > b (signed) |
|
||||
//! | 2 | a == b |
|
||||
//! | 3 | a < b (unsigned) |
|
||||
//! | 4 | a > b (unsigned) |
|
||||
//!
|
||||
//! The bit numbering matches PowerISA ("MSB is bit 0"): TO[0] corresponds to
|
||||
//! the high bit of the 5-bit field, i.e. (to >> 4) & 1.
|
||||
//!
|
||||
//! `tw` / `twi` compare the low 32 bits of the operands (sign-extended back to
|
||||
//! 64 for the signed comparison); `td` / `tdi` compare the full 64 bits.
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum TrapWidth {
|
||||
Word, // tw, twi: 32-bit
|
||||
Doubleword, // td, tdi: 64-bit
|
||||
}
|
||||
|
||||
const TO_SLT: u32 = 1 << 4; // a < b signed
|
||||
const TO_SGT: u32 = 1 << 3; // a > b signed
|
||||
const TO_EQ: u32 = 1 << 2; // a == b
|
||||
const TO_ULT: u32 = 1 << 1; // a < b unsigned
|
||||
const TO_UGT: u32 = 1 << 0; // a > b unsigned
|
||||
|
||||
/// Returns true when the trap should fire.
|
||||
pub fn evaluate(to: u32, a: u64, b: u64, width: TrapWidth) -> bool {
|
||||
let (sa, sb, ua, ub): (i64, i64, u64, u64) = match width {
|
||||
TrapWidth::Word => (
|
||||
a as i32 as i64,
|
||||
b as i32 as i64,
|
||||
a as u32 as u64,
|
||||
b as u32 as u64,
|
||||
),
|
||||
TrapWidth::Doubleword => (a as i64, b as i64, a, b),
|
||||
};
|
||||
|
||||
if (to & TO_SLT) != 0 && sa < sb { return true; }
|
||||
if (to & TO_SGT) != 0 && sa > sb { return true; }
|
||||
if (to & TO_EQ) != 0 && ua == ub { return true; }
|
||||
if (to & TO_ULT) != 0 && ua < ub { return true; }
|
||||
if (to & TO_UGT) != 0 && ua > ub { return true; }
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn to_zero_never_traps() {
|
||||
assert!(!evaluate(0, 0, 0, TrapWidth::Doubleword));
|
||||
assert!(!evaluate(0, 5, 3, TrapWidth::Doubleword));
|
||||
assert!(!evaluate(0, !0, 0, TrapWidth::Doubleword));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_31_always_traps_when_any_condition_holds() {
|
||||
// 31 = 0b11111 = all conditions enabled
|
||||
assert!(evaluate(31, 1, 2, TrapWidth::Doubleword)); // slt+ult
|
||||
assert!(evaluate(31, 2, 1, TrapWidth::Doubleword)); // sgt+ugt
|
||||
assert!(evaluate(31, 7, 7, TrapWidth::Doubleword)); // eq
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_eq_only() {
|
||||
// TO[2] = 0b00100 = 4
|
||||
assert!(evaluate(4, 5, 5, TrapWidth::Doubleword));
|
||||
assert!(!evaluate(4, 5, 6, TrapWidth::Doubleword));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_signed_vs_unsigned_on_negative() {
|
||||
// a=-1 (as u64 = all-ones). TO[0]=slt enabled = 0b10000 = 16
|
||||
// Signed: -1 < 0 → true
|
||||
let neg1 = (-1i64) as u64;
|
||||
assert!(evaluate(16, neg1, 0, TrapWidth::Doubleword));
|
||||
// TO[3]=ult enabled = 0b00010 = 2 → unsigned: all-ones < 0 is false
|
||||
assert!(!evaluate(2, neg1, 0, TrapWidth::Doubleword));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn word_width_ignores_high_32_bits() {
|
||||
// a's low 32 = 1, high 32 = different; b = 1. With TO=eq, should trap.
|
||||
let a = 0xDEAD_BEEF_0000_0001u64;
|
||||
assert!(evaluate(4, a, 1, TrapWidth::Word));
|
||||
// In doubleword, different.
|
||||
assert!(!evaluate(4, a, 1, TrapWidth::Doubleword));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user