use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum SpecialReg {
RA = 0, RB = 1,
RC = 2,
RD = 3,
RE = 4,
RF = 5,
RG = 6,
RH = 7,
RI = 8,
RJ = 9,
RK = 10,
RL = 11,
RM = 12,
RN = 13,
RO = 14,
RP = 15,
RQ = 16,
RR = 17, RS = 18,
RT = 19,
RU = 20,
RV = 21,
RW = 22,
RX = 23,
RY = 24,
RZ = 25,
RBB = 26,
RTT = 27,
RWW = 28,
RXX = 29,
RYY = 30,
RZZ = 31,
}
pub struct MMix {
general_regs: [u64; 256],
special_regs: [u64; 32],
memory: indexmap::IndexMap<u64, u8>,
pc: u64,
}
impl Default for MMix {
fn default() -> Self {
Self::new()
}
}
impl MMix {
pub fn new() -> Self {
Self {
general_regs: [0; 256],
special_regs: [0; 32],
memory: indexmap::IndexMap::new(),
pc: 0,
}
}
pub fn get_register(&self, reg: u8) -> u64 {
if reg == 255 {
0 } else {
self.general_regs[reg as usize]
}
}
pub fn set_register(&mut self, reg: u8, value: u64) {
if reg != 255 {
self.general_regs[reg as usize] = value;
}
}
pub fn get_special(&self, reg: SpecialReg) -> u64 {
self.special_regs[reg as usize]
}
pub fn set_special(&mut self, reg: SpecialReg, value: u64) {
self.special_regs[reg as usize] = value;
}
pub fn read_byte(&self, addr: u64) -> u8 {
*self.memory.get(&addr).unwrap_or(&0)
}
pub fn write_byte(&mut self, addr: u64, value: u8) {
if value == 0 {
self.memory.shift_remove(&addr); } else {
self.memory.insert(addr, value);
}
}
pub fn read_wyde(&self, addr: u64) -> u16 {
let b0 = self.read_byte(addr) as u16;
let b1 = self.read_byte(addr.wrapping_add(1)) as u16;
(b0 << 8) | b1
}
pub fn write_wyde(&mut self, addr: u64, value: u16) {
self.write_byte(addr, (value >> 8) as u8);
self.write_byte(addr.wrapping_add(1), value as u8);
}
pub fn read_tetra(&self, addr: u64) -> u32 {
let b0 = self.read_byte(addr) as u32;
let b1 = self.read_byte(addr.wrapping_add(1)) as u32;
let b2 = self.read_byte(addr.wrapping_add(2)) as u32;
let b3 = self.read_byte(addr.wrapping_add(3)) as u32;
(b0 << 24) | (b1 << 16) | (b2 << 8) | b3
}
pub fn write_tetra(&mut self, addr: u64, value: u32) {
self.write_byte(addr, (value >> 24) as u8);
self.write_byte(addr.wrapping_add(1), (value >> 16) as u8);
self.write_byte(addr.wrapping_add(2), (value >> 8) as u8);
self.write_byte(addr.wrapping_add(3), value as u8);
}
pub fn read_octa(&self, addr: u64) -> u64 {
let b0 = self.read_byte(addr) as u64;
let b1 = self.read_byte(addr.wrapping_add(1)) as u64;
let b2 = self.read_byte(addr.wrapping_add(2)) as u64;
let b3 = self.read_byte(addr.wrapping_add(3)) as u64;
let b4 = self.read_byte(addr.wrapping_add(4)) as u64;
let b5 = self.read_byte(addr.wrapping_add(5)) as u64;
let b6 = self.read_byte(addr.wrapping_add(6)) as u64;
let b7 = self.read_byte(addr.wrapping_add(7)) as u64;
(b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7
}
pub fn write_octa(&mut self, addr: u64, value: u64) {
self.write_byte(addr, (value >> 56) as u8);
self.write_byte(addr.wrapping_add(1), (value >> 48) as u8);
self.write_byte(addr.wrapping_add(2), (value >> 40) as u8);
self.write_byte(addr.wrapping_add(3), (value >> 32) as u8);
self.write_byte(addr.wrapping_add(4), (value >> 24) as u8);
self.write_byte(addr.wrapping_add(5), (value >> 16) as u8);
self.write_byte(addr.wrapping_add(6), (value >> 8) as u8);
self.write_byte(addr.wrapping_add(7), value as u8);
}
pub fn fetch_instruction(&self) -> (u8, u8, u8, u8) {
let instruction = self.read_tetra(self.pc);
let op = (instruction >> 24) as u8;
let x = (instruction >> 16) as u8;
let y = (instruction >> 8) as u8;
let z = instruction as u8;
(op, x, y, z)
}
pub fn get_pc(&self) -> u64 {
self.pc
}
pub fn set_pc(&mut self, pc: u64) {
self.pc = pc;
}
pub fn advance_pc(&mut self) {
self.pc = self.pc.wrapping_add(4);
}
pub fn execute_instruction(&mut self) -> bool {
let (op, x, y, z) = self.fetch_instruction();
match op {
0x80 => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let byte = self.read_byte(addr);
let value = (byte as i8) as i64 as u64; self.set_register(x, value);
self.advance_pc();
true
}
0x81 => {
let addr = self.get_register(y).wrapping_add(z as u64);
let byte = self.read_byte(addr);
let value = (byte as i8) as i64 as u64; self.set_register(x, value);
self.advance_pc();
true
}
0x82 => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let byte = self.read_byte(addr);
self.set_register(x, byte as u64);
self.advance_pc();
true
}
0x83 => {
let addr = self.get_register(y).wrapping_add(z as u64);
let byte = self.read_byte(addr);
self.set_register(x, byte as u64);
self.advance_pc();
true
}
0x84 => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let wyde = self.read_wyde(addr);
let value = (wyde as i16) as i64 as u64; self.set_register(x, value);
self.advance_pc();
true
}
0x85 => {
let addr = self.get_register(y).wrapping_add(z as u64);
let wyde = self.read_wyde(addr);
let value = (wyde as i16) as i64 as u64; self.set_register(x, value);
self.advance_pc();
true
}
0x86 => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let wyde = self.read_wyde(addr);
self.set_register(x, wyde as u64);
self.advance_pc();
true
}
0x87 => {
let addr = self.get_register(y).wrapping_add(z as u64);
let wyde = self.read_wyde(addr);
self.set_register(x, wyde as u64);
self.advance_pc();
true
}
0x88 => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let tetra = self.read_tetra(addr);
let value = (tetra as i32) as i64 as u64; self.set_register(x, value);
self.advance_pc();
true
}
0x89 => {
let addr = self.get_register(y).wrapping_add(z as u64);
let tetra = self.read_tetra(addr);
let value = (tetra as i32) as i64 as u64; self.set_register(x, value);
self.advance_pc();
true
}
0x8A => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let tetra = self.read_tetra(addr);
self.set_register(x, tetra as u64);
self.advance_pc();
true
}
0x8B => {
let addr = self.get_register(y).wrapping_add(z as u64);
let tetra = self.read_tetra(addr);
self.set_register(x, tetra as u64);
self.advance_pc();
true
}
0x8C => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let octa = self.read_octa(addr);
self.set_register(x, octa);
self.advance_pc();
true
}
0x8D => {
let addr = self.get_register(y).wrapping_add(z as u64);
let octa = self.read_octa(addr);
self.set_register(x, octa);
self.advance_pc();
true
}
0x8E => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let octa = self.read_octa(addr);
self.set_register(x, octa);
self.advance_pc();
true
}
0x8F => {
let addr = self.get_register(y).wrapping_add(z as u64);
let octa = self.read_octa(addr);
self.set_register(x, octa);
self.advance_pc();
true
}
0x90 => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let tetra = self.read_tetra(addr);
let value = (tetra as u64) << 32;
self.set_register(x, value);
self.advance_pc();
true
}
0x91 => {
let addr = self.get_register(y).wrapping_add(z as u64);
let tetra = self.read_tetra(addr);
let value = (tetra as u64) << 32;
self.set_register(x, value);
self.advance_pc();
true
}
0x22 => {
let y_val = self.get_register(y);
let z_val = self.get_register(z);
let result = y_val.wrapping_add(z_val);
self.set_register(x, result);
self.advance_pc();
true
}
0x23 => {
let y_val = self.get_register(y);
let result = y_val.wrapping_add(z as u64);
self.set_register(x, result);
self.advance_pc();
true
}
0xE0 => {
let y_val = self.get_register(y);
let z_val = self.get_register(z);
let sum = y_val.wrapping_add(z_val);
let current = self.get_register(x);
let result = current.wrapping_add(sum);
self.set_register(x, result);
self.advance_pc();
true
}
0xE1 => {
let yz = ((y as u64) << 8) | (z as u64);
let value = yz << 48;
let current = self.get_register(x);
self.set_register(x, current | value);
self.advance_pc();
true
}
0xE2 => {
let yz = ((y as u64) << 8) | (z as u64);
let value = yz << 32;
let current = self.get_register(x);
self.set_register(x, current | value);
self.advance_pc();
true
}
0xE3 => {
let yz = ((y as u64) << 8) | (z as u64);
let value = yz << 16;
let current = self.get_register(x);
self.set_register(x, current | value);
self.advance_pc();
true
}
0xE4 => {
let yz = ((y as u64) << 8) | (z as u64);
let current = self.get_register(x);
self.set_register(x, current | yz);
self.advance_pc();
true
}
0xA0 => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let value = self.get_register(x);
let signed_value = value as i64;
if !(-128..=127).contains(&signed_value) {
}
self.write_byte(addr, value as u8);
self.advance_pc();
true
}
0xA1 => {
let addr = self.get_register(y).wrapping_add(z as u64);
let value = self.get_register(x);
let signed_value = value as i64;
if !(-128..=127).contains(&signed_value) {
}
self.write_byte(addr, value as u8);
self.advance_pc();
true
}
0xA2 => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let value = self.get_register(x);
self.write_byte(addr, value as u8);
self.advance_pc();
true
}
0xA3 => {
let addr = self.get_register(y).wrapping_add(z as u64);
let value = self.get_register(x);
self.write_byte(addr, value as u8);
self.advance_pc();
true
}
0xA4 => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let value = self.get_register(x);
let signed_value = value as i64;
if !(-32768..=32767).contains(&signed_value) {
}
self.write_wyde(addr, value as u16);
self.advance_pc();
true
}
0xA5 => {
let addr = self.get_register(y).wrapping_add(z as u64);
let value = self.get_register(x);
let signed_value = value as i64;
if !(-32768..=32767).contains(&signed_value) {
}
self.write_wyde(addr, value as u16);
self.advance_pc();
true
}
0xA6 => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let value = self.get_register(x);
self.write_wyde(addr, value as u16);
self.advance_pc();
true
}
0xA7 => {
let addr = self.get_register(y).wrapping_add(z as u64);
let value = self.get_register(x);
self.write_wyde(addr, value as u16);
self.advance_pc();
true
}
0xA8 => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let value = self.get_register(x);
let signed_value = value as i64;
if !(-2_147_483_648..=2_147_483_647).contains(&signed_value) {
}
self.write_tetra(addr, value as u32);
self.advance_pc();
true
}
0xA9 => {
let addr = self.get_register(y).wrapping_add(z as u64);
let value = self.get_register(x);
let signed_value = value as i64;
if !(-2_147_483_648..=2_147_483_647).contains(&signed_value) {
}
self.write_tetra(addr, value as u32);
self.advance_pc();
true
}
0xAA => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let value = self.get_register(x);
self.write_tetra(addr, value as u32);
self.advance_pc();
true
}
0xAB => {
let addr = self.get_register(y).wrapping_add(z as u64);
let value = self.get_register(x);
self.write_tetra(addr, value as u32);
self.advance_pc();
true
}
0xAC => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let value = self.get_register(x);
self.write_octa(addr, value);
self.advance_pc();
true
}
0xAD => {
let addr = self.get_register(y).wrapping_add(z as u64);
let value = self.get_register(x);
self.write_octa(addr, value);
self.advance_pc();
true
}
0xAE => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let value = self.get_register(x);
self.write_octa(addr, value);
self.advance_pc();
true
}
0xAF => {
let addr = self.get_register(y).wrapping_add(z as u64);
let value = self.get_register(x);
self.write_octa(addr, value);
self.advance_pc();
true
}
0xB0 => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
self.write_octa(addr, x as u64);
self.advance_pc();
true
}
0xB1 => {
let addr = self.get_register(y).wrapping_add(z as u64);
self.write_octa(addr, x as u64);
self.advance_pc();
true
}
0xB2 => {
let addr = self.get_register(y).wrapping_add(self.get_register(z));
let value = self.get_register(x);
let high_tetra = (value >> 32) as u32;
self.write_tetra(addr, high_tetra);
self.advance_pc();
true
}
0xB3 => {
let addr = self.get_register(y).wrapping_add(z as u64);
let value = self.get_register(x);
let high_tetra = (value >> 32) as u32;
self.write_tetra(addr, high_tetra);
self.advance_pc();
true
}
0x20 => {
let a = self.get_register(y) as i64;
let b = self.get_register(z) as i64;
match a.checked_add(b) {
Some(result) => {
self.set_register(x, result as u64);
}
None => {
self.set_register(x, a.wrapping_add(b) as u64);
}
}
self.advance_pc();
true
}
0x21 => {
let a = self.get_register(y) as i64;
let b = z as i64;
match a.checked_add(b) {
Some(result) => {
self.set_register(x, result as u64);
}
None => {
self.set_register(x, a.wrapping_add(b) as u64);
}
}
self.advance_pc();
true
}
0x24 => {
let doubled = self.get_register(y).wrapping_mul(2);
let sum = doubled.wrapping_add(self.get_register(z));
self.set_register(x, sum);
self.advance_pc();
true
}
0x25 => {
let doubled = self.get_register(y).wrapping_mul(2);
let sum = doubled.wrapping_add(z as u64);
self.set_register(x, sum);
self.advance_pc();
true
}
0x26 => {
let quadrupled = self.get_register(y).wrapping_mul(4);
let sum = quadrupled.wrapping_add(self.get_register(z));
self.set_register(x, sum);
self.advance_pc();
true
}
0x27 => {
let quadrupled = self.get_register(y).wrapping_mul(4);
let sum = quadrupled.wrapping_add(z as u64);
self.set_register(x, sum);
self.advance_pc();
true
}
0x28 => {
let octupled = self.get_register(y).wrapping_mul(8);
let sum = octupled.wrapping_add(self.get_register(z));
self.set_register(x, sum);
self.advance_pc();
true
}
0x29 => {
let octupled = self.get_register(y).wrapping_mul(8);
let sum = octupled.wrapping_add(z as u64);
self.set_register(x, sum);
self.advance_pc();
true
}
0x2A => {
let multiplied = self.get_register(y).wrapping_mul(16);
let sum = multiplied.wrapping_add(self.get_register(z));
self.set_register(x, sum);
self.advance_pc();
true
}
0x2B => {
let multiplied = self.get_register(y).wrapping_mul(16);
let sum = multiplied.wrapping_add(z as u64);
self.set_register(x, sum);
self.advance_pc();
true
}
0x30 => {
let a = self.get_register(y) as i64;
let b = self.get_register(z) as i64;
match a.checked_sub(b) {
Some(result) => {
self.set_register(x, result as u64);
}
None => {
self.set_register(x, a.wrapping_sub(b) as u64);
}
}
self.advance_pc();
true
}
0x31 => {
let a = self.get_register(y) as i64;
let b = z as i64;
match a.checked_sub(b) {
Some(result) => {
self.set_register(x, result as u64);
}
None => {
self.set_register(x, a.wrapping_sub(b) as u64);
}
}
self.advance_pc();
true
}
0x32 => {
let diff = self.get_register(y).wrapping_sub(self.get_register(z));
self.set_register(x, diff);
self.advance_pc();
true
}
0x33 => {
let diff = self.get_register(y).wrapping_sub(z as u64);
self.set_register(x, diff);
self.advance_pc();
true
}
0x34 => {
let a = y as i64;
let b = self.get_register(z) as i64;
match a.checked_sub(b) {
Some(result) => {
self.set_register(x, result as u64);
}
None => {
self.set_register(x, a.wrapping_sub(b) as u64);
}
}
self.advance_pc();
true
}
0x35 => {
let a = y as i64;
let b = z as i64;
match a.checked_sub(b) {
Some(result) => {
self.set_register(x, result as u64);
}
None => {
self.set_register(x, a.wrapping_sub(b) as u64);
}
}
self.advance_pc();
true
}
0x36 => {
let a = y as u64;
let b = self.get_register(z);
self.set_register(x, a.wrapping_sub(b));
self.advance_pc();
true
}
0x37 => {
let a = y as u64;
let b = z as u64;
self.set_register(x, a.wrapping_sub(b));
self.advance_pc();
true
}
_ => {
eprintln!("Unknown opcode: {:#02x} at PC={:#018x}", op, self.pc);
false
}
}
}
pub fn run(&mut self) -> usize {
let mut count = 0;
while self.execute_instruction() {
count += 1;
if count >= 10000 {
eprintln!("Warning: Execution limit reached (10000 instructions)");
break;
}
}
count
}
}
impl fmt::Display for MMix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "MMIX Computer State:")?;
writeln!(f, " PC = {:#018x}", self.pc)?;
writeln!(f)?;
writeln!(f, "General Registers:")?;
let mut any_nonzero = false;
for (i, &value) in self.general_regs.iter().enumerate() {
if value != 0 && i != 255 {
writeln!(f, " ${:<3} = {:#018x} ({})", i, value, value)?;
any_nonzero = true;
}
}
if !any_nonzero {
writeln!(f, " (all zero)")?;
}
writeln!(f)?;
writeln!(f, "Special Registers:")?;
let special_names = [
"rA", "rB", "rC", "rD", "rE", "rF", "rG", "rH", "rI", "rJ", "rK", "rL", "rM", "rN",
"rO", "rP", "rQ", "rR", "rS", "rT", "rU", "rV", "rW", "rX", "rY", "rZ", "rBB", "rTT",
"rWW", "rXX", "rYY", "rZZ",
];
any_nonzero = false;
for (i, &value) in self.special_regs.iter().enumerate() {
if value != 0 {
writeln!(f, " {:<4} = {:#018x} ({})", special_names[i], value, value)?;
any_nonzero = true;
}
}
if !any_nonzero {
writeln!(f, " (all zero)")?;
}
writeln!(f)?;
writeln!(f, "Memory: {} bytes used", self.memory.len())?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mmix_new() {
let mmix = MMix::new();
assert_eq!(mmix.get_register(0), 0);
assert_eq!(mmix.get_register(255), 0);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
assert_eq!(mmix.get_pc(), 0);
}
#[test]
fn test_register_255_always_zero() {
let mut mmix = MMix::new();
mmix.set_register(255, 42);
assert_eq!(mmix.get_register(255), 0);
}
#[test]
fn test_general_registers() {
let mut mmix = MMix::new();
mmix.set_register(1, 0x123456789ABCDEF0);
assert_eq!(mmix.get_register(1), 0x123456789ABCDEF0);
assert_eq!(mmix.get_register(2), 0);
}
#[test]
fn test_special_registers() {
let mut mmix = MMix::new();
mmix.set_special(SpecialReg::RR, 42);
assert_eq!(mmix.get_special(SpecialReg::RR), 42);
assert_eq!(mmix.get_special(SpecialReg::RA), 0);
}
#[test]
fn test_memory_byte() {
let mut mmix = MMix::new();
mmix.write_byte(0x1000, 0x42);
assert_eq!(mmix.read_byte(0x1000), 0x42);
assert_eq!(mmix.read_byte(0x1001), 0);
}
#[test]
fn test_memory_wyde() {
let mut mmix = MMix::new();
mmix.write_wyde(0x1000, 0x1234);
assert_eq!(mmix.read_wyde(0x1000), 0x1234);
assert_eq!(mmix.read_byte(0x1000), 0x12);
assert_eq!(mmix.read_byte(0x1001), 0x34);
}
#[test]
fn test_memory_tetra() {
let mut mmix = MMix::new();
mmix.write_tetra(0x1000, 0x12345678);
assert_eq!(mmix.read_tetra(0x1000), 0x12345678);
}
#[test]
fn test_memory_octa() {
let mut mmix = MMix::new();
mmix.write_octa(0x1000, 0x123456789ABCDEF0);
assert_eq!(mmix.read_octa(0x1000), 0x123456789ABCDEF0);
}
#[test]
fn test_fetch_instruction() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x20010203);
let (op, x, y, z) = mmix.fetch_instruction();
assert_eq!(op, 0x20);
assert_eq!(x, 0x01);
assert_eq!(y, 0x02);
assert_eq!(z, 0x03);
}
#[test]
fn test_pc_operations() {
let mut mmix = MMix::new();
assert_eq!(mmix.get_pc(), 0);
mmix.set_pc(0x1000);
assert_eq!(mmix.get_pc(), 0x1000);
mmix.advance_pc();
assert_eq!(mmix.get_pc(), 0x1004);
}
#[test]
fn test_sparse_memory() {
let mut mmix = MMix::new();
mmix.write_byte(0x1000, 0x42);
mmix.write_byte(0x1000, 0); assert_eq!(mmix.memory.len(), 0);
}
#[test]
fn test_incl_instruction() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xE0010203);
mmix.set_register(1, 50);
mmix.set_register(2, 30);
mmix.set_register(3, 20);
let result = mmix.execute_instruction();
assert!(result); assert_eq!(mmix.get_register(1), 100); assert_eq!(mmix.get_pc(), 4); }
#[test]
fn test_incl_with_zero() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xE0020000);
mmix.set_register(2, 42);
mmix.execute_instruction();
assert_eq!(mmix.get_register(2), 42); }
#[test]
fn test_incl_overflow() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xE0030405);
mmix.set_register(3, u64::MAX - 5);
mmix.set_register(4, 3);
mmix.set_register(5, 10);
mmix.execute_instruction();
assert_eq!(mmix.get_register(3), 7); }
#[test]
fn test_incl_large_values() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xE0010203);
mmix.set_register(1, 100);
mmix.set_register(2, 1000000);
mmix.set_register(3, 2000000);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 3000100); }
#[test]
fn test_incl_register_255() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xE0FF0102);
mmix.set_register(1, 50);
mmix.set_register(2, 50);
mmix.execute_instruction();
assert_eq!(mmix.get_register(255), 0); }
#[test]
fn test_incl_using_255() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xE001FF02);
mmix.set_register(1, 100);
mmix.set_register(2, 50);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 150); }
#[test]
fn test_run_multiple_incl() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xE0010000); mmix.write_tetra(4, 0xE0010203); mmix.write_tetra(8, 0xE0010203); mmix.write_tetra(12, 0x00000000);
mmix.set_register(2, 10);
mmix.set_register(3, 5);
let count = mmix.run();
assert_eq!(count, 3);
assert_eq!(mmix.get_register(1), 30); assert_eq!(mmix.get_pc(), 12);
}
#[test]
fn test_unknown_opcode_halts() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x12345678);
let result = mmix.execute_instruction();
assert!(!result); assert_eq!(mmix.get_pc(), 0); }
#[test]
fn test_ldb_signed_positive() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x80010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_byte(150, 127);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 127);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldb_signed_negative() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x80010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_byte(150, 0xFF);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -1);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldb_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8101020A);
mmix.set_register(2, 100);
mmix.write_byte(110, 0x80);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -128);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldbu_unsigned() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x82010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_byte(150, 0xFF);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 255);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldbu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x83010214);
mmix.set_register(2, 100);
mmix.write_byte(120, 200);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 200);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldw_signed_positive() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x84010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_wyde(150, 32767);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 32767);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldw_signed_negative() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x84010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_wyde(150, 0xFFFF);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -1);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldw_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8501020A);
mmix.set_register(2, 100);
mmix.write_wyde(110, 0x8000);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -32768);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldwu_unsigned() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x86010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_wyde(150, 0xFFFF);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 65535);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldwu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8701021E);
mmix.set_register(2, 100);
mmix.write_wyde(130, 50000);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 50000);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldt_signed_positive() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x88010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_tetra(150, 2_147_483_647);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 2_147_483_647);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldt_signed_negative() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x88010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_tetra(150, 0xFFFFFFFF);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -1);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldt_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x89010214);
mmix.set_register(2, 100);
mmix.write_tetra(120, 0x80000000);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -2147483648);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldtu_unsigned() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8A010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_tetra(150, 0xFFFFFFFF);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 4294967295);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldtu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8B010228);
mmix.set_register(2, 100);
mmix.write_tetra(140, 3_000_000_000);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 3_000_000_000);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldo_load_octa() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8C010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_octa(150, 0x123456789ABCDEF0);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0x123456789ABCDEF0);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldo_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8D010210);
mmix.set_register(2, 100);
mmix.write_octa(116, 0xFEDCBA9876543210);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0xFEDCBA9876543210);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldou_same_as_ldo() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8E010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_octa(150, 0x123456789ABCDEF0);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0x123456789ABCDEF0);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldou_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x8F010208);
mmix.set_register(2, 1000);
mmix.write_octa(1008, 0xFFFFFFFFFFFFFFFF);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0xFFFFFFFFFFFFFFFF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldht_high_tetra() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x90010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.write_tetra(150, 0x12345678);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0x1234567800000000);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_ldht_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x9101020C);
mmix.set_register(2, 200);
mmix.write_tetra(212, 0xABCDEF00);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0xABCDEF0000000000);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_lda_load_address() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x22010203);
mmix.set_register(2, 0x1000);
mmix.set_register(3, 0x500);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0x1500);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_lda_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x23010240);
mmix.set_register(2, 0x2000);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0x2040);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_load_from_uninitialized_memory() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x83010064);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_load_address_wraparound() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x22010203);
mmix.set_register(2, u64::MAX - 100);
mmix.set_register(3, 200);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 99); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_all_set_instructions_have_tests() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xE1010001);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0x0001000000000000);
mmix.set_pc(4);
mmix.write_tetra(4, 0xE2020002);
mmix.execute_instruction();
assert_eq!(mmix.get_register(2), 0x0000000200000000);
mmix.set_pc(8);
mmix.write_tetra(8, 0xE3030003);
mmix.execute_instruction();
assert_eq!(mmix.get_register(3), 0x0000000000030000);
mmix.set_pc(12);
mmix.write_tetra(12, 0xE4040004);
mmix.execute_instruction();
assert_eq!(mmix.get_register(4), 0x0000000000000004);
}
#[test]
fn test_incl_has_multiple_tests() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xE0010203);
mmix.set_register(2, 10);
mmix.set_register(3, 20);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 30);
}
#[test]
fn test_stb_store_byte() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA0010203);
mmix.set_register(1, 0x42); mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_byte(150), 0x42);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stb_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA101020A);
mmix.set_register(1, 0x7F); mmix.set_register(2, 200);
mmix.execute_instruction();
assert_eq!(mmix.read_byte(210), 0x7F);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stbu_store_byte_unsigned() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA2010203);
mmix.set_register(1, 0xFF); mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_byte(150), 0xFF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stbu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA3010214);
mmix.set_register(1, 0xAB);
mmix.set_register(2, 1000);
mmix.execute_instruction();
assert_eq!(mmix.read_byte(1020), 0xAB);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stw_store_wyde() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA4010203);
mmix.set_register(1, 0x1234);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_wyde(150), 0x1234);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stw_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA501021E);
mmix.set_register(1, 0x7FFF); mmix.set_register(2, 2000);
mmix.execute_instruction();
assert_eq!(mmix.read_wyde(2030), 0x7FFF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stwu_store_wyde_unsigned() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA6010203);
mmix.set_register(1, 0xFFFF); mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_wyde(150), 0xFFFF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stwu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA7010228);
mmix.set_register(1, 0xABCD);
mmix.set_register(2, 5000);
mmix.execute_instruction();
assert_eq!(mmix.read_wyde(5040), 0xABCD);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stt_store_tetra() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA8010203);
mmix.set_register(1, 0x12345678);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_tetra(150), 0x12345678);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stt_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA9010232);
mmix.set_register(1, 0x7FFFFFFF); mmix.set_register(2, 10000);
mmix.execute_instruction();
assert_eq!(mmix.read_tetra(10050), 0x7FFFFFFF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_sttu_store_tetra_unsigned() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xAA010203);
mmix.set_register(1, 0xFFFFFFFF); mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_tetra(150), 0xFFFFFFFF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_sttu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xAB01023C);
mmix.set_register(1, 0xDEADBEEF);
mmix.set_register(2, 20000);
mmix.execute_instruction();
assert_eq!(mmix.read_tetra(20060), 0xDEADBEEF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_sto_store_octa() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xAC010203);
mmix.set_register(1, 0x123456789ABCDEF0);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_octa(150), 0x123456789ABCDEF0);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_sto_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xAD010246);
mmix.set_register(1, 0xFEDCBA9876543210);
mmix.set_register(2, 30000);
mmix.execute_instruction();
assert_eq!(mmix.read_octa(30070), 0xFEDCBA9876543210);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stou_same_as_sto() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xAE010203);
mmix.set_register(1, 0xFFFFFFFFFFFFFFFF);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_octa(150), 0xFFFFFFFFFFFFFFFF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stou_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xAF010250);
mmix.set_register(1, 0x0123456789ABCDEF);
mmix.set_register(2, 40000);
mmix.execute_instruction();
assert_eq!(mmix.read_octa(40080), 0x0123456789ABCDEF);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stco_store_constant() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xB02A0203); mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_octa(150), 42);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stco_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xB1FF025A); mmix.set_register(2, 50000);
mmix.execute_instruction();
assert_eq!(mmix.read_octa(50090), 255);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stht_store_high_tetra() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xB2010203);
mmix.set_register(1, 0xDEADBEEF12345678);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.read_tetra(150), 0xDEADBEEF); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_stht_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xB3010264);
mmix.set_register(1, 0xABCD123456789ABC);
mmix.set_register(2, 60000);
mmix.execute_instruction();
assert_eq!(mmix.read_tetra(60100), 0xABCD1234); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_store_and_load_roundtrip() {
let mut mmix = MMix::new();
let test_addr = 5000u64;
mmix.set_register(1, 0x123456789ABCDEF0);
mmix.set_register(2, test_addr);
mmix.write_tetra(0, 0xAD010200); mmix.execute_instruction();
mmix.set_pc(4);
mmix.write_tetra(4, 0x8D030200); mmix.execute_instruction();
assert_eq!(mmix.get_register(3), 0x123456789ABCDEF0);
}
#[test]
fn test_all_store_instructions_have_tests() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0xA0010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0xA2010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0xA4010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0xA6010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0xA8010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0xAA010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0xAC010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0xAE010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0xB0010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0xB2010203);
assert!(mmix.execute_instruction());
}
#[test]
fn test_add_positive_numbers() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x20010203);
mmix.set_register(2, 100);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 150);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_add_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x2101024B);
mmix.set_register(2, 25);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 100);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_add_negative_numbers() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x20010203);
mmix.set_register(2, (-50i64) as u64);
mmix.set_register(3, (-30i64) as u64);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -80);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_addu_wrapping() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x22010203);
mmix.set_register(2, u64::MAX);
mmix.set_register(3, 1);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 0); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_addu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x23010264);
mmix.set_register(2, 50);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 150);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_2addu_register() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x24010203);
mmix.set_register(2, 10);
mmix.set_register(3, 5);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 25); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_2addu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x25010207);
mmix.set_register(2, 12);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 31); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_4addu_register() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x26010203);
mmix.set_register(2, 10);
mmix.set_register(3, 5);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 45); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_4addu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x27010208);
mmix.set_register(2, 10);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 48); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_8addu_register() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x28010203);
mmix.set_register(2, 10);
mmix.set_register(3, 5);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 85); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_8addu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x2901020F);
mmix.set_register(2, 10);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 95); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_16addu_register() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x2A010203);
mmix.set_register(2, 10);
mmix.set_register(3, 5);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 165); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_16addu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x2B010214);
mmix.set_register(2, 10);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 180); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_sub_positive_result() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x30010203);
mmix.set_register(2, 100);
mmix.set_register(3, 30);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 70);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_sub_negative_result() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x30010203);
mmix.set_register(2, 30);
mmix.set_register(3, 100);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -70);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_sub_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x31010219);
mmix.set_register(2, 100);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 75);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_subu_wrapping() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x32010203);
mmix.set_register(2, 10);
mmix.set_register(3, 20);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), u64::MAX - 9); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_subu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x3301021E);
mmix.set_register(2, 100);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 70);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_neg_zero_minus_value() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x34010003);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -50);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_neg_immediate_both() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x35010A03);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 7);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_neg_one_minus_two() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x35010102);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1) as i64, -1);
assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_negu_register() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x36010003);
mmix.set_register(3, 50);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), u64::MAX - 49); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_negu_immediate() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x3701641E);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 70); assert_eq!(mmix.get_pc(), 4);
}
#[test]
fn test_multiply_add_for_array_indexing() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x28010203);
mmix.set_register(2, 5); mmix.set_register(3, 1000);
mmix.execute_instruction();
assert_eq!(mmix.get_register(1), 1040); }
#[test]
fn test_all_arithmetic_instructions_have_tests() {
let mut mmix = MMix::new();
mmix.write_tetra(0, 0x20010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0x22010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0x24010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0x26010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0x28010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0x2A010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0x30010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0x32010203);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0x34010003);
assert!(mmix.execute_instruction());
mmix.set_pc(0);
mmix.write_tetra(0, 0x36010003);
assert!(mmix.execute_instruction());
}
}