moa/emulator/cpus/m68k/tests/decode_tests.rs
2023-03-25 21:27:02 -07:00

196 lines
10 KiB
Rust

use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, wrap_transmutable};
use moa_m68k::{M68k, M68kType};
use moa_m68k::instructions::{Instruction, Target, Size, Sign, XRegister, BaseRegister, IndexRegister, Direction, ShiftDirection};
use moa_m68k::assembler::M68kAssembler;
const INIT_STACK: Address = 0x00002000;
const INIT_ADDR: Address = 0x00000010;
struct TestCase {
cpu: M68kType,
data: &'static [u16],
ins: Option<Instruction>,
}
const DECODE_TESTS: &'static [TestCase] = &[
// MC68000
TestCase { cpu: M68kType::MC68000, data: &[0x4e71], ins: Some(Instruction::NOP) },
// TODO I think this one is illegal (which is causing problems for the assembler)
//TestCase { cpu: M68kType::MC68000, data: &[0x0008, 0x00FF], ins: Some(Instruction::OR(Target::Immediate(0xFF), Target::DirectAReg(0), Size::Byte)) },
TestCase { cpu: M68kType::MC68000, data: &[0x003C, 0x00FF], ins: Some(Instruction::ORtoCCR(0xFF)) },
TestCase { cpu: M68kType::MC68000, data: &[0x007C, 0x1234], ins: Some(Instruction::ORtoSR(0x1234)) },
TestCase { cpu: M68kType::MC68000, data: &[0x0263, 0x1234], ins: Some(Instruction::AND(Target::Immediate(0x1234), Target::IndirectARegDec(3), Size::Word)) },
TestCase { cpu: M68kType::MC68000, data: &[0x0240, 0x1234], ins: Some(Instruction::AND(Target::Immediate(0x1234), Target::DirectDReg(0), Size::Word)) },
TestCase { cpu: M68kType::MC68000, data: &[0x02A3, 0x1234, 0x5678], ins: Some(Instruction::AND(Target::Immediate(0x12345678), Target::IndirectARegDec(3), Size::Long)) },
TestCase { cpu: M68kType::MC68000, data: &[0x0280, 0x1234, 0x5678], ins: Some(Instruction::AND(Target::Immediate(0x12345678), Target::DirectDReg(0), Size::Long)) },
TestCase { cpu: M68kType::MC68000, data: &[0x023C, 0x1234], ins: Some(Instruction::ANDtoCCR(0x34)) },
TestCase { cpu: M68kType::MC68000, data: &[0x027C, 0xF8FF], ins: Some(Instruction::ANDtoSR(0xF8FF)) },
TestCase { cpu: M68kType::MC68000, data: &[0x4240], ins: Some(Instruction::CLR(Target::DirectDReg(0), Size::Word)) },
TestCase { cpu: M68kType::MC68000, data: &[0x4280], ins: Some(Instruction::CLR(Target::DirectDReg(0), Size::Long)) },
TestCase { cpu: M68kType::MC68000, data: &[0x4250], ins: Some(Instruction::CLR(Target::IndirectAReg(0), Size::Word)) },
TestCase { cpu: M68kType::MC68000, data: &[0x4290], ins: Some(Instruction::CLR(Target::IndirectAReg(0), Size::Long)) },
TestCase { cpu: M68kType::MC68000, data: &[0x0487, 0x1234, 0x5678], ins: Some(Instruction::SUB(Target::Immediate(0x12345678), Target::DirectDReg(7), Size::Long)) },
TestCase { cpu: M68kType::MC68000, data: &[0x063A, 0x1234, 0x0055], ins: Some(Instruction::ADD(Target::Immediate(0x34), Target::IndirectRegOffset(BaseRegister::PC, None, 0x55), Size::Byte)) },
TestCase { cpu: M68kType::MC68000, data: &[0x0A23, 0x1234], ins: Some(Instruction::EOR(Target::Immediate(0x34), Target::IndirectARegDec(3), Size::Byte)) },
TestCase { cpu: M68kType::MC68000, data: &[0x0A3C, 0x1234], ins: Some(Instruction::EORtoCCR(0x34)) },
TestCase { cpu: M68kType::MC68000, data: &[0x0A7C, 0xF8FF], ins: Some(Instruction::EORtoSR(0xF8FF)) },
TestCase { cpu: M68kType::MC68000, data: &[0x0C00, 0x0020], ins: Some(Instruction::CMP(Target::Immediate(0x20), Target::DirectDReg(0), Size::Byte)) },
TestCase { cpu: M68kType::MC68000, data: &[0x0C00, 0x0030], ins: Some(Instruction::CMP(Target::Immediate(0x30), Target::DirectDReg(0), Size::Byte)) },
TestCase { cpu: M68kType::MC68000, data: &[0x0C00, 0x0010], ins: Some(Instruction::CMP(Target::Immediate(0x10), Target::DirectDReg(0), Size::Byte)) },
TestCase { cpu: M68kType::MC68000, data: &[0x81FC, 0x0003], ins: Some(Instruction::DIVW(Target::Immediate(3), 0, Sign::Signed)) },
TestCase { cpu: M68kType::MC68000, data: &[0xC1FC, 0x0276], ins: Some(Instruction::MULW(Target::Immediate(0x276), 0, Sign::Signed)) },
TestCase { cpu: M68kType::MC68000, data: &[0xCDC5], ins: Some(Instruction::MULW(Target::DirectDReg(5), 6, Sign::Signed)) },
TestCase { cpu: M68kType::MC68000, data: &[0x0108, 0x1234], ins: Some(Instruction::MOVEP(0, 0, 0x1234, Size::Word, Direction::FromTarget)) },
TestCase { cpu: M68kType::MC68000, data: &[0x0148, 0x1234], ins: Some(Instruction::MOVEP(0, 0, 0x1234, Size::Long, Direction::FromTarget)) },
TestCase { cpu: M68kType::MC68000, data: &[0x0188, 0x1234], ins: Some(Instruction::MOVEP(0, 0, 0x1234, Size::Word, Direction::ToTarget)) },
TestCase { cpu: M68kType::MC68000, data: &[0x01C8, 0x1234], ins: Some(Instruction::MOVEP(0, 0, 0x1234, Size::Long, Direction::ToTarget)) },
TestCase { cpu: M68kType::MC68000, data: &[0xE300], ins: Some(Instruction::ASd(Target::Immediate(1), Target::DirectDReg(0), Size::Byte, ShiftDirection::Left)) },
TestCase { cpu: M68kType::MC68000, data: &[0xE200], ins: Some(Instruction::ASd(Target::Immediate(1), Target::DirectDReg(0), Size::Byte, ShiftDirection::Right)) },
TestCase { cpu: M68kType::MC68000, data: &[0xE318], ins: Some(Instruction::ROd(Target::Immediate(1), Target::DirectDReg(0), Size::Byte, ShiftDirection::Left)) },
TestCase { cpu: M68kType::MC68000, data: &[0xE218], ins: Some(Instruction::ROd(Target::Immediate(1), Target::DirectDReg(0), Size::Byte, ShiftDirection::Right)) },
TestCase { cpu: M68kType::MC68000, data: &[0xA000], ins: Some(Instruction::UnimplementedA(0xA000)) },
TestCase { cpu: M68kType::MC68000, data: &[0xFFFF], ins: Some(Instruction::UnimplementedF(0xFFFF)) },
// MC68030
TestCase { cpu: M68kType::MC68030, data: &[0x4C3C, 0x0800, 0x0000, 0x0097], ins: Some(Instruction::MULL(Target::Immediate(0x97), None, 0, Sign::Signed)) },
TestCase { cpu: M68kType::MC68030, data: &[0x21BC, 0x0010, 0x14C4, 0x09B0, 0x0010, 0xDF40], ins: Some(Instruction::MOVE(Target::Immediate(1053892), Target::IndirectRegOffset(BaseRegister::None, Some(IndexRegister { xreg: XRegister::DReg(0), scale: 0, size: Size::Long }), 0x10df40), Size::Long)) },
// Should Fail
];
fn init_decode_test(cputype: M68kType) -> (M68k, System) {
let mut system = System::default();
// Insert basic initialization
let data = vec![0; 0x00100000];
let mem = MemoryBlock::new(data);
system.add_addressable_device(0x00000000, wrap_transmutable(mem)).unwrap();
system.get_bus().write_beu32(0, INIT_STACK as u32).unwrap();
system.get_bus().write_beu32(4, INIT_ADDR as u32).unwrap();
// Initialize the CPU and make sure it's in the expected state
let port = if cputype <= M68kType::MC68010 {
BusPort::new(0, 24, 16, system.bus.clone())
} else {
BusPort::new(0, 24, 16, system.bus.clone())
};
let mut cpu = M68k::new(cputype, 10_000_000, port);
cpu.init().unwrap();
assert_eq!(cpu.state.pc, INIT_ADDR as u32);
assert_eq!(cpu.state.ssp, INIT_STACK as u32);
cpu.decoder.init(INIT_ADDR as u32);
assert_eq!(cpu.decoder.start, INIT_ADDR as u32);
assert_eq!(cpu.decoder.instruction, Instruction::NOP);
(cpu, system)
}
fn load_memory(system: &System, data: &[u16]) {
let mut addr = INIT_ADDR;
for word in data {
system.get_bus().write_beu16(addr, *word).unwrap();
addr += 2;
}
}
fn run_decode_test(case: &TestCase) {
let (mut cpu, system) = init_decode_test(case.cpu);
load_memory(&system, case.data);
match &case.ins {
Some(ins) => {
cpu.decode_next().unwrap();
assert_eq!(cpu.decoder.instruction, ins.clone());
},
None => {
let next = cpu.decode_next();
println!("{:?}", cpu.decoder.instruction);
assert!(next.is_err());
},
}
}
#[test]
pub fn run_decode_tests() {
for case in DECODE_TESTS {
println!("Testing for {:?}", case.ins);
run_decode_test(case);
}
}
#[test]
pub fn run_assembler_tests() {
let mut tests = 0;
let mut errors = 0;
for case in DECODE_TESTS {
if case.ins.is_some() {
tests += 1;
let assembly_text = format!("{}", case.ins.as_ref().unwrap());
print!("Testing assembling of {:?} ", assembly_text);
let mut assembler = M68kAssembler::new(M68kType::MC68000);
match assembler.assemble_words(&assembly_text) {
Ok(data) => {
if data == case.data {
print!("pass");
} else {
errors += 1;
print!("FAILED");
print!("\nleft: {:?}, right: {:?}", data, case.data);
}
println!();
},
Err(err) => {
println!("FAILED\n{:?}", err);
errors += 1;
},
}
}
}
if errors > 0 {
panic!("{} errors out of {} tests", errors, tests);
}
}
/*
#[test]
pub fn run_assembler_opcode_tests() {
let mut tests = 0;
let mut errors = 0;
use super::super::testcases::{TimingCase, TIMING_TESTS};
for case in TIMING_TESTS {
tests += 1;
let assembly_text = format!("{}", case.ins);
print!("Testing assembling of {:?} from {:?}", assembly_text, case.ins);
let mut assembler = M68kAssembler::new(M68kType::MC68000);
match assembler.assemble_words(&assembly_text) {
Ok(data) => {
if data[0] == case.data[0] {
print!("pass");
} else {
errors += 1;
print!("FAILED");
print!("\nleft: {:#06x}, right: {:#06x}", data[0], case.data[0]);
}
println!();
},
Err(err) => {
println!("FAILED\n{:?}", err);
errors += 1;
},
}
}
if errors > 0 {
panic!("{} errors out of {} tests", errors, tests);
}
}
*/