pub struct Alu;
impl Alu {
pub fn calculate(instruction: u8, a: u8, b: u8, carry: bool) -> (u8, Flags) {
let (result, carry) = match instruction {
0b0000 => {
let (result, new_carry) = a.overflowing_add(b);
(result, carry || new_carry)
}
0b0001 => (a, false),
0b0010 => (!(a | b), false),
0b0011 => (0, false),
0b0100 => a.overflowing_add(b),
0b0101 => {
let tmp1 = a.overflowing_add(b);
let tmp2 = tmp1.0.overflowing_add(1);
(tmp2.0, !(tmp1.1 | tmp2.1))
}
0b0110 => {
let tmp1 = a.overflowing_add(b);
let tmp2 = tmp1.0.overflowing_add(if carry {1} else {0});
(tmp2.0, tmp1.1 | tmp2.1)
}
0b0111 => {
let tmp1 = a.overflowing_add(b);
let tmp2 = tmp1.0.overflowing_add(if carry {0} else {1});
(tmp2.0, !(tmp1.1 | tmp2.1))
}
0b1000 => (a >> 1, a & 0b00000001 != 0),
0b1001 => (a.rotate_right(1), a & 0b00000001 != 0),
0b1010 => (a >> 1 | (carry as u8) << 7, a & 0b00000001 != 0),
0b1011 => (a >> 1 | (a & 0b10000000), a & 0b00000001 != 0),
0b1100 => (b, false),
0b1101 => (b, true),
0b1110 => (b, carry),
0b1111 => (b, !carry),
_ => panic!("Invalid alu instruction {}", instruction),
};
let negative = result & 0b10000000 != 0;
let zero = result == 0;
return (result, Flags::new(carry, negative, zero));
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct Flags {
carry: bool,
negative: bool,
zero: bool,
}
impl Flags {
pub fn new(carry: bool, negative: bool, zero: bool) -> Flags {
Flags { carry: carry, negative: negative, zero: zero }
}
pub fn carry(&self) -> bool {
self.carry
}
pub fn negative(&self) -> bool {
self.negative
}
pub fn zero(&self) -> bool {
self.zero
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn logic() {
let a = 0b11010100;
let b = 0b00101101;
assert_eq!(Alu::calculate(0b0001, a, b, false), (a, Flags::new(false, true, false)));
assert_eq!(Alu::calculate(0b0011, a, b, false), (0, Flags::new(false, false, true)));
assert_eq!(Alu::calculate(0b0010, a, b, false), (0b00000010, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b0010, a, a, false), (0b00101011, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b0010, b, b, false), (0b11010010, Flags::new(false, true, false)));
}
#[test]
fn addition() {
assert_eq!(Alu::calculate(0b0100, 0, 0, false), ( 0, Flags::new(false, false, true)));
assert_eq!(Alu::calculate(0b0100, 0, 19, false), (19, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b0100, 47, 0, false), (47, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b0100, 47, 19, false), (66, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b0100, 47, 236, false), (27, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b0000, 47, 19, false), (66, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b0000, 47, 19, true), (66, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b0000, 47, 236, false), (27, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b0000, 47, 236, true), (27, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b0101, 0, 0, false), ( 1, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b0101, 0, 19, false), (20, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b0101, 47, 0, false), (48, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b0101, 47, 19, false), (67, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b0101, 47, 236, false), (28, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b0110, 47, 19, false), (66, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b0110, 47, 19, true), (67, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b0110, 47, 236, false), (27, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b0110, 47, 236, true), (28, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b0111, 47, 19, false), (67, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b0111, 47, 19, true), (66, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b0111, 47, 236, false), (28, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b0111, 47, 236, true), (27, Flags::new(false, false, false)));
}
#[test]
fn shifts() {
let a = 0b11010100;
let b = 0b00101101;
assert_eq!(Alu::calculate(0b0100, a, a, false), (0b10101000, Flags::new( true, true, false)));
assert_eq!(Alu::calculate(0b0100, b, b, false), (0b01011010, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b1000, a, 0, false), (0b01101010, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b1000, b, 0, false), (0b00010110, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b1011, a, 0, false), (0b11101010, Flags::new(false, true, false)));
assert_eq!(Alu::calculate(0b1011, b, 0, false), (0b00010110, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b1001, a, 0, false), (0b01101010, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b1001, b, 0, false), (0b10010110, Flags::new( true, true, false)));
assert_eq!(Alu::calculate(0b1010, a, 0, false), (0b01101010, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b1010, a, 0, true), (0b11101010, Flags::new(false, true, false)));
assert_eq!(Alu::calculate(0b1010, b, 0, false), (0b00010110, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b1010, b, 0, true), (0b10010110, Flags::new( true, true, false)));
}
#[test]
fn flags() {
let b = 0b00101101;
assert_eq!(Alu::calculate(0b1100, 0, b, false), (b, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b1100, 0, b, true), (b, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b1101, 0, b, false), (b, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b1101, 0, b, true), (b, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b1110, 0, b, false), (b, Flags::new(false, false, false)));
assert_eq!(Alu::calculate(0b1110, 0, b, true), (b, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b1111, 0, b, false), (b, Flags::new( true, false, false)));
assert_eq!(Alu::calculate(0b1111, 0, b, true), (b, Flags::new(false, false, false)));
}
#[test]
#[should_panic(expected = "Invalid alu instruction")]
fn invalid_instruction() {
Alu::calculate(0b10000, 0, 0, false);
}
}