Fully up to date (#16)

* Run in main

* Pipeline

* Pacify Clippy
This commit is contained in:
Patrick Stevens
2023-04-16 16:33:52 +01:00
committed by GitHub
parent e905445786
commit 1512e794a6
8 changed files with 182 additions and 52 deletions

View File

@@ -162,8 +162,12 @@ jobs:
"with": { "extra-nix-config": "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" } "with": { "extra-nix-config": "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" }
}, },
{ {
"name": "Run app", "name": "Run app (verify consistency)",
"run": "nix run . -- computer_enhance/perfaware/part1/listing_0038_many_register_mov computer_enhance/perfaware/part1/listing_0038_many_register_mov.asm" "run": "nix run . -- computer_enhance/perfaware/part1/listing_0038_many_register_mov computer_enhance/perfaware/part1/listing_0038_many_register_mov.asm"
},
{
"name": "Run app",
"run": "nix run . -- computer_enhance/perfaware/part1/listing_0055_challenge_rectangle computer_enhance/perfaware/part1/listing_0055_challenge_rectangle.asm"
} }
] ]
} }

View File

@@ -322,12 +322,16 @@ impl Computer {
Register::Special(SpecialRegister::DestIndex) => self.registers.di = value, Register::Special(SpecialRegister::DestIndex) => self.registers.di = value,
} }
let is_now = self.get_register(&register_for_print); let is_now = self.get_register(&register_for_print);
format!( if was != is_now {
"{}:{}->{}", format!(
register_for_print, "{}:{}->{}",
Self::display_small(was), register_for_print,
Self::display_small(is_now) Self::display_small(was),
) Self::display_small(is_now)
)
} else {
"".to_owned()
}
} }
fn set_segment(&mut self, r: SegmentRegister, value: u16) -> String { fn set_segment(&mut self, r: SegmentRegister, value: u16) -> String {
@@ -352,18 +356,18 @@ impl Computer {
} }
fn get_memory_word(&self, index: usize) -> u16 { fn get_memory_word(&self, index: usize) -> u16 {
self.memory[index] as u16 * 256 + self.memory[index + 1] as u16 self.memory[index] as u16 + self.memory[index + 1] as u16 * 256
} }
fn set_memory_byte(&mut self, index: usize, value: u8) -> String { fn set_memory_byte(&mut self, index: usize, value: u8) -> String {
self.memory[index] = value; self.memory[index] = value;
"todo".to_owned() "".to_owned()
} }
fn set_memory_word(&mut self, index: usize, value: u16) -> String { fn set_memory_word(&mut self, index: usize, value: u16) -> String {
self.memory[index] = (value % 256) as u8; self.memory[index] = (value % 256) as u8;
self.memory[index + 1] = (value / 256) as u8; self.memory[index + 1] = (value / 256) as u8;
"todo".to_owned() "".to_owned()
} }
fn resolve_eaddr(&self, a: &EffectiveAddress) -> usize { fn resolve_eaddr(&self, a: &EffectiveAddress) -> usize {
@@ -507,7 +511,7 @@ impl Computer {
MoveInstruction::ImmediateToMemory(mov) => match mov { MoveInstruction::ImmediateToMemory(mov) => match mov {
ImmediateToMemory::Byte(addr, value) => { ImmediateToMemory::Byte(addr, value) => {
let dest = self.resolve_eaddr(addr); let dest = self.resolve_eaddr(addr);
self.set_memory_byte(dest, *value) self.set_memory_word(dest, *value as u16)
} }
ImmediateToMemory::Word(addr, value) => { ImmediateToMemory::Word(addr, value) => {
let dest = self.resolve_eaddr(addr); let dest = self.resolve_eaddr(addr);
@@ -570,7 +574,8 @@ impl Computer {
let ip_desc = match old_ip { let ip_desc = match old_ip {
None => "".to_owned(), None => "".to_owned(),
Some(old_ip) => format!( Some(old_ip) => format!(
" ip:{}->{}", "{}ip:{}->{}",
if description.is_empty() { "" } else { " " },
Self::display_small(old_ip), Self::display_small(old_ip),
Self::display_small(self.program_counter) Self::display_small(self.program_counter)
), ),
@@ -723,7 +728,20 @@ impl Computer {
(format!("{}", instr.dest), current_value, new_value, flags) (format!("{}", instr.dest), current_value, new_value, flags)
} }
ArithmeticInstructionSelect::RegisterToMemory(_) => todo!(), ArithmeticInstructionSelect::RegisterToMemory(_) => todo!(),
ArithmeticInstructionSelect::MemoryToRegister(_) => todo!(), ArithmeticInstructionSelect::MemoryToRegister(instr) => {
let current_value = self.get_register(&instr.dest);
let incoming_value = if instr.dest.is_wide() {
self.get_memory_word(self.resolve_eaddr(&instr.source))
} else {
self.get_memory_byte(self.resolve_eaddr(&instr.source)) as u16
};
let (new_value, flags) =
Self::apply(instruction.op, current_value, incoming_value, false);
if flags.should_write {
self.set_register(&instr.dest, new_value);
}
(format!("{}", instr.dest), current_value, new_value, flags)
}
ArithmeticInstructionSelect::ImmediateToRegisterByte(register, value, is_extended) => { ArithmeticInstructionSelect::ImmediateToRegisterByte(register, value, is_extended) => {
let current_value = self.get_register(register); let current_value = self.get_register(register);
let (new_value, flags) = if *is_extended { let (new_value, flags) = if *is_extended {
@@ -800,7 +818,7 @@ impl Computer {
) )
} }
}; };
let result_desc = if flags.should_write { let result_desc = if flags.should_write && old_value != new_value {
format!( format!(
" {}:{}->{}", " {}:{}->{}",
source, source,
@@ -855,20 +873,26 @@ impl Computer {
Jump::Loop => { Jump::Loop => {
let reg = Register::General(GeneralRegister::C, RegisterSubset::All); let reg = Register::General(GeneralRegister::C, RegisterSubset::All);
let prev = self.get_register(&reg); let prev = self.get_register(&reg);
self.set_register(&reg, prev - 1); let new_value = if prev == 0 {
self.set_register(&reg, u16::MAX);
u16::MAX
} else {
self.set_register(&reg, prev - 1);
prev - 1
};
reg_desc.push_str(&format!( reg_desc.push_str(&format!(
" cx:{}->{}", " ; cx:{}->{}",
Self::display_small(prev), Self::display_small(prev),
Self::display_small(prev - 1) Self::display_small(new_value)
)); ));
prev == 1 prev != 1
} }
Jump::Loopz => { Jump::Loopz => {
let reg = Register::General(GeneralRegister::C, RegisterSubset::All); let reg = Register::General(GeneralRegister::C, RegisterSubset::All);
let prev = self.get_register(&reg); let prev = self.get_register(&reg);
self.set_register(&reg, prev - 1); self.set_register(&reg, prev - 1);
reg_desc.push_str(&format!( reg_desc.push_str(&format!(
" cx:{}->{}", " ; cx:{}->{}",
Self::display_small(prev), Self::display_small(prev),
Self::display_small(prev - 1) Self::display_small(prev - 1)
)); ));

View File

@@ -18,8 +18,8 @@ where
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
WithOffset::Basic(t) => f.write_fmt(format_args!("{}", t)), WithOffset::Basic(t) => f.write_fmt(format_args!("{}", t)),
WithOffset::WithU8(t, offset) => f.write_fmt(format_args!("[{} + {}]", t, offset)), WithOffset::WithU8(t, offset) => f.write_fmt(format_args!("[{}+{}]", t, offset)),
WithOffset::WithU16(t, offset) => f.write_fmt(format_args!("[{} + {}]", t, offset)), WithOffset::WithU16(t, offset) => f.write_fmt(format_args!("[{}+{}]", t, offset)),
} }
} }
} }
@@ -45,33 +45,59 @@ impl Display for EffectiveAddress {
match self { match self {
EffectiveAddress::Sum(w) => match w { EffectiveAddress::Sum(w) => match w {
WithOffset::Basic((base, source_dest)) => { WithOffset::Basic((base, source_dest)) => {
f.write_fmt(format_args!("[{} + {}i]", base, source_dest)) f.write_fmt(format_args!("[{}+{}i]", base, source_dest))
} }
WithOffset::WithU8((base, source_dest), offset) => { WithOffset::WithU8((base, source_dest), offset) => {
f.write_fmt(format_args!("[{} + {}i + {}]", base, source_dest, offset)) if *offset == 0 {
f.write_fmt(format_args!("[{}+{}i]", base, source_dest))
} else {
f.write_fmt(format_args!("[{}+{}i+{}]", base, source_dest, offset))
}
} }
WithOffset::WithU16((base, source_dest), offset) => { WithOffset::WithU16((base, source_dest), offset) => {
f.write_fmt(format_args!("[{} + {}i + {}]", base, source_dest, offset)) if *offset == 0 {
f.write_fmt(format_args!("[{}+{}i]", base, source_dest))
} else {
f.write_fmt(format_args!("[{}+{}i+{}]", base, source_dest, offset))
}
} }
}, },
EffectiveAddress::SpecifiedIn(WithOffset::Basic(source_dest)) => { EffectiveAddress::SpecifiedIn(WithOffset::Basic(source_dest)) => {
f.write_fmt(format_args!("[{}i]", source_dest)) f.write_fmt(format_args!("[{}i]", source_dest))
} }
EffectiveAddress::SpecifiedIn(WithOffset::WithU8(source_dest, offset)) => { EffectiveAddress::SpecifiedIn(WithOffset::WithU8(source_dest, offset)) => {
f.write_fmt(format_args!("[{}i + {}]", source_dest, offset)) if *offset == 0 {
f.write_fmt(format_args!("[{}i]", source_dest))
} else {
f.write_fmt(format_args!("[{}i+{}]", source_dest, offset))
}
} }
EffectiveAddress::SpecifiedIn(WithOffset::WithU16(source_dest, offset)) => { EffectiveAddress::SpecifiedIn(WithOffset::WithU16(source_dest, offset)) => {
f.write_fmt(format_args!("[{}i + {}]", source_dest, offset)) if *offset == 0 {
f.write_fmt(format_args!("[{}i]", source_dest))
} else {
f.write_fmt(format_args!("[{}i+{}]", source_dest, offset))
}
} }
EffectiveAddress::Bx(offset) => match offset { EffectiveAddress::Bx(offset) => match offset {
WithOffset::Basic(()) => f.write_str("bx"), WithOffset::Basic(()) => f.write_str("bx"),
WithOffset::WithU8((), offset) => f.write_fmt(format_args!("[bx + {}]", offset)), WithOffset::WithU8((), offset) => f.write_fmt(format_args!("[bx+{}]", offset)),
WithOffset::WithU16((), offset) => f.write_fmt(format_args!("[bx + {}]", offset)), WithOffset::WithU16((), offset) => f.write_fmt(format_args!("[bx+{}]", offset)),
}, },
EffectiveAddress::Direct(location) => f.write_fmt(format_args!("[{}]", location)), EffectiveAddress::Direct(location) => f.write_fmt(format_args!("[+{}]", location)),
EffectiveAddress::BasePointer(offset) => f.write_fmt(format_args!("[bp + {}]", offset)), EffectiveAddress::BasePointer(offset) => {
if *offset == 0 {
f.write_str("[bp]")
} else {
f.write_fmt(format_args!("[bp+{}]", offset))
}
}
EffectiveAddress::BasePointerWide(offset) => { EffectiveAddress::BasePointerWide(offset) => {
f.write_fmt(format_args!("[bp + {}]", offset)) if *offset == 0 {
f.write_str("[bp]")
} else {
f.write_fmt(format_args!("[bp+{}]", offset))
}
} }
} }
} }

View File

@@ -391,9 +391,16 @@ impl Display for MoveInstruction {
MoveInstruction::RegRegMove(mov) => { MoveInstruction::RegRegMove(mov) => {
f.write_fmt(format_args!("mov {}, {}", mov.dest, mov.source)) f.write_fmt(format_args!("mov {}, {}", mov.dest, mov.source))
} }
MoveInstruction::RegMemMove(mov) => { MoveInstruction::RegMemMove(mov) => f.write_fmt(format_args!(
f.write_fmt(format_args!("mov {}, {}", mov.dest, mov.source)) "mov {}{}, {}",
} if mov.source.is_wide() {
"word "
} else {
"byte "
},
mov.dest,
mov.source
)),
MoveInstruction::MemRegMove(mov) => { MoveInstruction::MemRegMove(mov) => {
f.write_fmt(format_args!("mov {}, {}", mov.dest, mov.source)) f.write_fmt(format_args!("mov {}, {}", mov.dest, mov.source))
} }
@@ -401,10 +408,10 @@ impl Display for MoveInstruction {
f.write_fmt(format_args!("mov {}", instruction)) f.write_fmt(format_args!("mov {}", instruction))
} }
MoveInstruction::ImmediateToMemory(ImmediateToMemory::Byte(address, value)) => { MoveInstruction::ImmediateToMemory(ImmediateToMemory::Byte(address, value)) => {
f.write_fmt(format_args!("mov {}, {}", address, value)) f.write_fmt(format_args!("mov byte {}, {}", address, value))
} }
MoveInstruction::ImmediateToMemory(ImmediateToMemory::Word(address, value)) => { MoveInstruction::ImmediateToMemory(ImmediateToMemory::Word(address, value)) => {
f.write_fmt(format_args!("mov {}, {}", address, value)) f.write_fmt(format_args!("mov word {}, {}", address, value))
} }
MoveInstruction::MemoryToAccumulator(instruction) => f.write_fmt(format_args!( MoveInstruction::MemoryToAccumulator(instruction) => f.write_fmt(format_args!(
"mov a{}, [{}]", "mov a{}, [{}]",

View File

@@ -64,10 +64,10 @@ mod test_computer {
trace.push("".to_owned()); trace.push("".to_owned());
assert_eq!( let cleaned = clean_trace(expected_trace);
trace, let expected = cleaned.lines().collect::<Vec<_>>();
clean_trace(expected_trace).lines().collect::<Vec<_>>()
) assert_eq!(trace, expected)
} }
#[test] #[test]
@@ -145,7 +145,6 @@ mod test_computer {
test_sim(input_bytecode, expected_trace, true) test_sim(input_bytecode, expected_trace, true)
} }
/*
#[test] #[test]
fn test_memory_mov() { fn test_memory_mov() {
let input_bytecode = let input_bytecode =
@@ -194,5 +193,4 @@ mod test_computer {
); );
test_sim(input_bytecode, expected_trace, true) test_sim(input_bytecode, expected_trace, true)
} }
*/
} }

View File

@@ -243,6 +243,26 @@ mod test_program {
test_disassembler(asm, bytecode) test_disassembler(asm, bytecode)
} }
/*
#[test]
fn test_completionist_parser() {
let input_asm =
include_str!("../../computer_enhance/perfaware/part1/listing_0042_completionist_decode.asm");
let input_bytecode =
include_bytes!("../../computer_enhance/perfaware/part1/listing_0042_completionist_decode");
test_parser(input_asm, input_bytecode)
}
#[test]
fn test_completionist_disassembler() {
let bytecode =
include_bytes!("../../computer_enhance/perfaware/part1/listing_0042_completionist_decode");
let asm =
include_str!("../../computer_enhance/perfaware/part1/listing_0042_completionist_decode.asm");
test_disassembler(asm, bytecode)
}
*/
#[test] #[test]
fn test_immediate_movs_parser() { fn test_immediate_movs_parser() {
let input_asm = let input_asm =

View File

@@ -1,6 +1,7 @@
use std::{fs, path::Path}; use std::{fs, path::Path};
use clap::Parser; use clap::Parser;
use sim_8086::computer::Computer;
use sim_8086::instruction::Instruction; use sim_8086::instruction::Instruction;
use sim_8086::program::Program; use sim_8086::program::Program;
@@ -17,6 +18,8 @@ struct Args {
compiled_path: std::path::PathBuf, compiled_path: std::path::PathBuf,
#[arg(value_name = "ASM_PATH")] #[arg(value_name = "ASM_PATH")]
asm_path: std::path::PathBuf, asm_path: std::path::PathBuf,
#[arg()]
verify_consistency: Option<bool>,
} }
fn program_equal_ignoring_labels<A, B>( fn program_equal_ignoring_labels<A, B>(
@@ -54,12 +57,8 @@ where
true true
} }
fn main() { fn verify_consistency(pre_compiled: &Vec<u8>, asm: &str) {
let args = Args::parse(); let (remaining, compiled) = sim_8086::assembly::program(asm).unwrap();
let expected_bytecode = load_machine_code(args.compiled_path);
let asm = fs::read_to_string(args.asm_path).unwrap();
let (remaining, compiled) = sim_8086::assembly::program(&asm).unwrap();
if !remaining.is_empty() { if !remaining.is_empty() {
println!( println!(
@@ -70,15 +69,25 @@ fn main() {
} }
let actual_bytecode = compiled.to_bytes(); let actual_bytecode = compiled.to_bytes();
if expected_bytecode != actual_bytecode { if pre_compiled.len() != actual_bytecode.len() {
println!( println!(
"Expected: {:?}\nActual: {:?}", "Expected: {:?}\nActual: {:?}",
expected_bytecode, actual_bytecode pre_compiled, actual_bytecode
); );
std::process::exit(1) std::process::exit(1)
} }
let disassembled = Program::of_bytes(expected_bytecode.iter().cloned()); for (pre_compiled, actual) in pre_compiled.iter().zip(actual_bytecode.iter()) {
if *pre_compiled != *actual {
println!(
"Expected: {:?}\nActual: {:?}",
pre_compiled, actual_bytecode
);
std::process::exit(1)
}
}
let disassembled = Program::of_bytes(pre_compiled.iter().cloned());
if disassembled != compiled { if disassembled != compiled {
println!("Disassembled and compiled versions do not produce the same bytes. From disassembly:\n{}\nFrom assembling the input asm:\n{}", disassembled, compiled); println!("Disassembled and compiled versions do not produce the same bytes. From disassembly:\n{}\nFrom assembling the input asm:\n{}", disassembled, compiled);
@@ -94,3 +103,45 @@ fn main() {
"Our assembly was equal to the reference, and it disassembled back to the same structure." "Our assembly was equal to the reference, and it disassembled back to the same structure."
) )
} }
fn run(bytecode: Vec<u8>) -> Computer {
let program = Program::of_bytes(bytecode.iter().cloned());
let mut computer = Computer::new();
let mut counter = 0usize;
let mut instructions = vec![None; bytecode.len()];
for instruction in program.instructions.iter() {
instructions[counter] = Some(instruction);
counter += instruction.length() as usize;
}
let end_of_instructions = counter;
loop {
let counter = computer.get_program_counter() as usize;
if counter >= end_of_instructions {
break;
}
match instructions[counter] {
None => {
panic!("landed in middle of instruction")
}
Some(instruction) => {
computer.step(instruction, false);
}
}
}
computer
}
fn main() {
let args = Args::parse();
let expected_bytecode = load_machine_code(args.compiled_path);
let asm = fs::read_to_string(args.asm_path).unwrap();
if let Some(true) = args.verify_consistency {
verify_consistency(&expected_bytecode, &asm);
}
let _computer = run(expected_bytecode);
}