feat(cpu): implement ARM7TDMI ARM and Thumb execution

Add ALU, transfers, banked status, exceptions, instruction timing, and CPU regression tests.
This commit is contained in:
0xrsydn 2026-10-05 12:55:35 +07:00
commit 535c69e09f
17 changed files with 4838 additions and 0 deletions

108
src/cpu/alu.rs Normal file
View file

@ -0,0 +1,108 @@
use super::Flags;
#[derive(Clone, Copy, Debug)]
pub(super) enum Shift {
Lsl,
Lsr,
Asr,
Ror,
}
impl Shift {
pub(super) fn decode(bits: u32) -> Self {
match bits & 3 {
0 => Self::Lsl,
1 => Self::Lsr,
2 => Self::Asr,
_ => Self::Ror,
}
}
}
/// ARM barrel shifter. Register shifts use only the low eight amount bits.
pub(super) fn shift(
value: u32,
kind: Shift,
amount: u32,
carry: bool,
by_register: bool,
) -> (u32, bool) {
let mut amount = if by_register { amount & 0xff } else { amount };
if amount == 0 {
if by_register || matches!(kind, Shift::Lsl) {
return (value, carry);
}
if matches!(kind, Shift::Ror) {
return ((u32::from(carry) << 31) | (value >> 1), value & 1 != 0); // RRX
}
amount = 32; // Immediate LSR/ASR #0 encodes a shift by 32.
}
match kind {
Shift::Lsl => match amount {
1..=31 => (value << amount, (value >> (32 - amount)) & 1 != 0),
32 => (0, value & 1 != 0),
_ => (0, false),
},
Shift::Lsr => match amount {
1..=31 => (value >> amount, (value >> (amount - 1)) & 1 != 0),
32 => (0, value >> 31 != 0),
_ => (0, false),
},
Shift::Asr => {
let sign = value >> 31 != 0;
if amount >= 32 {
(if sign { u32::MAX } else { 0 }, sign)
} else {
(
((value as i32) >> amount) as u32,
(value >> (amount - 1)) & 1 != 0,
)
}
}
Shift::Ror => {
let result = value.rotate_right(amount);
(result, result >> 31 != 0)
}
}
}
/// Addition is also used for subtraction: a - b = a + !b + 1.
fn add_with_carry(a: u32, b: u32, carry: bool) -> (u32, bool, bool) {
let wide = u64::from(a) + u64::from(b) + u64::from(carry);
let result = wide as u32;
let overflow = (!(a ^ b) & (a ^ result)) >> 31 != 0;
(result, wide > u64::from(u32::MAX), overflow)
}
pub(super) fn execute(
opcode: u32,
a: u32,
b: u32,
shifter_carry: bool,
previous: Flags,
) -> (u32, Flags) {
let (result, carry, overflow) = match opcode {
0x0 | 0x8 => (a & b, shifter_carry, previous.overflow),
0x1 | 0x9 => (a ^ b, shifter_carry, previous.overflow),
0x2 | 0xa => add_with_carry(a, !b, true),
0x3 => add_with_carry(b, !a, true),
0x4 | 0xb => add_with_carry(a, b, false),
0x5 => add_with_carry(a, b, previous.carry),
0x6 => add_with_carry(a, !b, previous.carry),
0x7 => add_with_carry(b, !a, previous.carry),
0xc => (a | b, shifter_carry, previous.overflow),
0xd => (b, shifter_carry, previous.overflow),
0xe => (a & !b, shifter_carry, previous.overflow),
0xf => (!b, shifter_carry, previous.overflow),
_ => unreachable!("opcode comes from a four-bit instruction field"),
};
(
result,
Flags {
negative: result >> 31 != 0,
zero: result == 0,
carry,
overflow,
},
)
}