Restructure into a library (#7)
This commit is contained in:
463
sim_8086/tests/test_program.rs
Normal file
463
sim_8086/tests/test_program.rs
Normal file
@@ -0,0 +1,463 @@
|
||||
#[cfg(test)]
|
||||
mod test_program {
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use sim_8086::{
|
||||
arithmetic_instruction::{
|
||||
ArithmeticInstruction, ArithmeticInstructionSelect, ArithmeticOperation,
|
||||
},
|
||||
assembly,
|
||||
instruction::Instruction,
|
||||
move_instruction::{ImmediateToRegister, MoveInstruction},
|
||||
program::Program,
|
||||
register::{GeneralRegister, Register, RegisterSubset},
|
||||
};
|
||||
|
||||
fn instruction_equal_ignoring_labels<A, B>(i1: &Instruction<A>, i2: &Instruction<B>) -> bool {
|
||||
match (i1, i2) {
|
||||
(Instruction::Move(i1), Instruction::Move(i2)) => i1 == i2,
|
||||
(Instruction::Move(_), _) => false,
|
||||
(Instruction::Arithmetic(i1), Instruction::Arithmetic(i2)) => i1 == i2,
|
||||
(Instruction::Arithmetic(_), _) => false,
|
||||
(Instruction::Jump(i1, _), Instruction::Jump(i2, _)) => i1 == i2,
|
||||
(Instruction::Jump(_, _), _) => false,
|
||||
(Instruction::Trivia(_), Instruction::Trivia(_)) => true,
|
||||
(Instruction::Trivia(_), _) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_programs_with_different_instruction_sequences_are_not_equal() {
|
||||
let program1: Program<_, u8> = Program {
|
||||
bits: 64,
|
||||
instructions: vec![],
|
||||
offset: PhantomData,
|
||||
};
|
||||
let program2 = Program {
|
||||
bits: 64,
|
||||
instructions: vec![Instruction::Move(MoveInstruction::ImmediateToRegister(
|
||||
ImmediateToRegister::Byte(
|
||||
Register::General(GeneralRegister::D, RegisterSubset::All),
|
||||
1,
|
||||
),
|
||||
))],
|
||||
offset: std::marker::PhantomData,
|
||||
};
|
||||
|
||||
assert_ne!(program1, program2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_programs_with_identical_instruction_sequences_are_equal() {
|
||||
let program1: Program<_, u8> = Program {
|
||||
bits: 64,
|
||||
instructions: vec![Instruction::Move(MoveInstruction::ImmediateToRegister(
|
||||
ImmediateToRegister::Byte(
|
||||
Register::General(GeneralRegister::D, RegisterSubset::All),
|
||||
1,
|
||||
),
|
||||
))],
|
||||
offset: PhantomData,
|
||||
};
|
||||
let program2 = Program {
|
||||
bits: 64,
|
||||
instructions: vec![Instruction::Move(MoveInstruction::ImmediateToRegister(
|
||||
ImmediateToRegister::Byte(
|
||||
Register::General(GeneralRegister::D, RegisterSubset::All),
|
||||
1,
|
||||
),
|
||||
))],
|
||||
offset: PhantomData,
|
||||
};
|
||||
|
||||
assert_eq!(program1, program2);
|
||||
}
|
||||
|
||||
fn test_parser_lax<T>(
|
||||
input_asm: &str,
|
||||
input_bytecode: T,
|
||||
permit_equivalences: HashMap<Instruction<&str>, Instruction<&str>>,
|
||||
) where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
let (remaining, parsed) = assembly::program(input_asm).unwrap();
|
||||
assert_eq!(remaining, "");
|
||||
assert_eq!(parsed.bits, 16);
|
||||
|
||||
let adjusted_program: Program<Vec<Instruction<_>>, _> = Program {
|
||||
bits: parsed.bits,
|
||||
instructions: parsed
|
||||
.instructions
|
||||
.into_iter()
|
||||
.map(|i| match permit_equivalences.get(&i) {
|
||||
Some(v) => v.clone(),
|
||||
None => i.clone(),
|
||||
})
|
||||
.collect(),
|
||||
offset: PhantomData,
|
||||
};
|
||||
|
||||
for (i, (actual, expected)) in adjusted_program
|
||||
.to_bytes()
|
||||
.iter()
|
||||
.zip(input_bytecode.as_ref().iter())
|
||||
.enumerate()
|
||||
{
|
||||
if actual != expected {
|
||||
panic!(
|
||||
"Failed assertion: expected {} (from Casey), got {}, at position {}\n{:?}",
|
||||
expected,
|
||||
actual,
|
||||
i,
|
||||
adjusted_program.to_bytes()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_parser<T>(input_asm: &str, input_bytecode: T)
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
test_parser_lax(input_asm, input_bytecode, HashMap::new())
|
||||
}
|
||||
|
||||
fn test_disassembler_lax<T>(
|
||||
input_asm: &str,
|
||||
input_bytecode: T,
|
||||
permit_equivalences: HashSet<(Vec<u8>, Vec<u8>)>,
|
||||
) where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
let disassembled = Program::of_bytes(input_bytecode.as_ref().iter().cloned());
|
||||
|
||||
let (remaining, pre_compiled) = assembly::program(&input_asm).unwrap();
|
||||
assert_eq!(remaining, "");
|
||||
|
||||
let disassembled = disassembled.instructions.iter().filter(|i| match i {
|
||||
Instruction::Trivia(_) => false,
|
||||
_ => true,
|
||||
});
|
||||
let mut compiled = pre_compiled.instructions.iter().filter(|i| match i {
|
||||
Instruction::Trivia(_) => false,
|
||||
_ => true,
|
||||
});
|
||||
|
||||
let mut is_different = false;
|
||||
|
||||
for dis in disassembled {
|
||||
if let Some(compiled) = compiled.next() {
|
||||
if !instruction_equal_ignoring_labels(dis, compiled) {
|
||||
let compiled_bytes = compiled.to_bytes();
|
||||
let dis_bytes = dis.to_bytes();
|
||||
if !permit_equivalences.contains(&(compiled_bytes.clone(), dis_bytes.clone()))
|
||||
&& !permit_equivalences
|
||||
.contains(&(dis_bytes.clone(), compiled_bytes.clone()))
|
||||
{
|
||||
println!(
|
||||
"Different instruction. From disassembly: {dis} ({:?}). From our compilation: {compiled} ({:?}).",
|
||||
compiled_bytes,
|
||||
dis_bytes
|
||||
);
|
||||
is_different = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!(
|
||||
"Extra instruction from disassembly: {dis} ({:?})",
|
||||
dis.to_bytes()
|
||||
);
|
||||
is_different = true;
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(compiled) = compiled.next() {
|
||||
println!(
|
||||
"Extra instruction from compilation: {compiled} ({:?})",
|
||||
compiled.to_bytes()
|
||||
);
|
||||
is_different = true;
|
||||
}
|
||||
|
||||
if is_different {
|
||||
panic!("Disassembling input bytecode produced a different program from compiling the input asm.")
|
||||
}
|
||||
}
|
||||
|
||||
fn test_disassembler<T>(input_asm: &str, input_bytecode: T)
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
test_disassembler_lax(input_asm, input_bytecode, HashSet::new())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_register_mov_parser() {
|
||||
let input_asm = include_str!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0037_single_register_mov.asm"
|
||||
);
|
||||
let input_bytecode = include_bytes!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0037_single_register_mov"
|
||||
);
|
||||
test_parser(input_asm, input_bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_register_mov_disassembler() {
|
||||
let bytecode = include_bytes!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0037_single_register_mov"
|
||||
);
|
||||
let asm = include_str!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0037_single_register_mov.asm"
|
||||
);
|
||||
test_disassembler(asm, bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_register_many_mov_parser() {
|
||||
let input_asm = include_str!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0038_many_register_mov.asm"
|
||||
);
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0038_many_register_mov");
|
||||
test_parser(input_asm, input_bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_register_many_mov_disassembler() {
|
||||
let bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0038_many_register_mov");
|
||||
let asm = include_str!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0038_many_register_mov.asm"
|
||||
);
|
||||
test_disassembler(asm, bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_more_mov_parser() {
|
||||
let input_asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0039_more_movs.asm");
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0039_more_movs");
|
||||
test_parser(input_asm, input_bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_more_mov_disassembler() {
|
||||
let bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0039_more_movs");
|
||||
let asm = include_str!("../../computer_enhance/perfaware/part1/listing_0039_more_movs.asm");
|
||||
test_disassembler(asm, bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_challenge_movs_parser() {
|
||||
let input_asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0040_challenge_movs.asm");
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0040_challenge_movs");
|
||||
test_parser(input_asm, input_bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_challenge_movs_disassembler() {
|
||||
let bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0040_challenge_movs");
|
||||
let asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0040_challenge_movs.asm");
|
||||
test_disassembler(asm, bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_sub_cmp_jnz_parser() {
|
||||
let input_asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0041_add_sub_cmp_jnz.asm");
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0041_add_sub_cmp_jnz");
|
||||
test_parser(input_asm, input_bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_sub_cmp_jnz_disassembler() {
|
||||
let bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0041_add_sub_cmp_jnz");
|
||||
let asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0041_add_sub_cmp_jnz.asm");
|
||||
test_disassembler(asm, bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_immediate_movs_parser() {
|
||||
let input_asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0043_immediate_movs.asm");
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0043_immediate_movs");
|
||||
test_parser(input_asm, input_bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_immediate_movs_disassembler() {
|
||||
let bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0043_immediate_movs");
|
||||
let asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0043_immediate_movs.asm");
|
||||
test_disassembler(asm, bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_movs_parser() {
|
||||
let input_asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0044_register_movs.asm");
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0044_register_movs");
|
||||
test_parser(input_asm, input_bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_movs_disassembler() {
|
||||
let bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0044_register_movs");
|
||||
let asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0044_register_movs.asm");
|
||||
test_disassembler(asm, bytecode)
|
||||
}
|
||||
|
||||
/*
|
||||
We have not yet implemented the segment registers, so this test can't pass.
|
||||
#[test]
|
||||
fn test_challenge_register_movs_parser() {
|
||||
let input_asm = include_str!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs.asm"
|
||||
);
|
||||
let input_bytecode = include_bytes!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs"
|
||||
);
|
||||
test_parser(input_asm, input_bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_challenge_register_movs_disassembler() {
|
||||
let bytecode = include_bytes!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs"
|
||||
);
|
||||
let asm = include_str!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs.asm"
|
||||
);
|
||||
test_disassembler(asm, bytecode)
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn test_add_sub_cmp_parser() {
|
||||
let input_asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0046_add_sub_cmp.asm");
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0046_add_sub_cmp");
|
||||
test_parser(input_asm, input_bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_sub_cmp_disassembler() {
|
||||
let bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0046_add_sub_cmp");
|
||||
let asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0046_add_sub_cmp.asm");
|
||||
test_disassembler(asm, bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_challenge_flags_parser() {
|
||||
let input_asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0047_challenge_flags.asm");
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0047_challenge_flags");
|
||||
test_parser(input_asm, input_bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_challenge_flags_disassembler() {
|
||||
let bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0047_challenge_flags");
|
||||
let asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0047_challenge_flags.asm");
|
||||
test_disassembler(asm, bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ip_register_parser() {
|
||||
let input_asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0048_ip_register.asm");
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0048_ip_register");
|
||||
test_parser(input_asm, input_bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ip_register_disassembler() {
|
||||
let bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0048_ip_register");
|
||||
let asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0048_ip_register.asm");
|
||||
test_disassembler(asm, bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conditional_jumps_parser() {
|
||||
let input_asm = include_str!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0049_conditional_jumps.asm"
|
||||
);
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0049_conditional_jumps");
|
||||
test_parser(input_asm, input_bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conditional_jumps_disassembler() {
|
||||
let bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0049_conditional_jumps");
|
||||
let asm = include_str!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0049_conditional_jumps.asm"
|
||||
);
|
||||
test_disassembler(asm, bytecode)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_challenge_jumps_parser() {
|
||||
let input_asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0050_challenge_jumps.asm");
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0050_challenge_jumps");
|
||||
let mut swaps = HashMap::new();
|
||||
swaps.insert(
|
||||
Instruction::Arithmetic(ArithmeticInstruction {
|
||||
op: ArithmeticOperation::Add,
|
||||
instruction: ArithmeticInstructionSelect::ImmediateToAccWord(1),
|
||||
}),
|
||||
Instruction::Arithmetic(ArithmeticInstruction {
|
||||
op: ArithmeticOperation::Add,
|
||||
instruction: ArithmeticInstructionSelect::ImmediateToRegisterWord(
|
||||
Register::General(GeneralRegister::A, RegisterSubset::All),
|
||||
1,
|
||||
true,
|
||||
),
|
||||
}),
|
||||
);
|
||||
test_parser_lax(input_asm, input_bytecode, swaps)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_challenge_jumps_disassembler() {
|
||||
let bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0050_challenge_jumps");
|
||||
let asm =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0050_challenge_jumps.asm");
|
||||
let mut allowed = HashSet::new();
|
||||
// We implemented `add ax, 1` using "immediate to accumulator";
|
||||
// in this example, Casey implemented it using "immediate to register".
|
||||
allowed.insert((vec![5, 1, 0], vec![131, 192, 1]));
|
||||
test_disassembler_lax(asm, bytecode, allowed)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user