6
.github/workflows/rust.yml
vendored
6
.github/workflows/rust.yml
vendored
@@ -162,8 +162,12 @@ jobs:
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"name": "Run app",
|
||||
"run": "nix run . -- computer_enhance/perfaware/part1/listing_0055_challenge_rectangle computer_enhance/perfaware/part1/listing_0055_challenge_rectangle.asm"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Submodule computer_enhance updated: 99fdf556b0...784fcc0fe4
@@ -322,12 +322,16 @@ impl Computer {
|
||||
Register::Special(SpecialRegister::DestIndex) => self.registers.di = value,
|
||||
}
|
||||
let is_now = self.get_register(®ister_for_print);
|
||||
format!(
|
||||
"{}:{}->{}",
|
||||
register_for_print,
|
||||
Self::display_small(was),
|
||||
Self::display_small(is_now)
|
||||
)
|
||||
if was != is_now {
|
||||
format!(
|
||||
"{}:{}->{}",
|
||||
register_for_print,
|
||||
Self::display_small(was),
|
||||
Self::display_small(is_now)
|
||||
)
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_segment(&mut self, r: SegmentRegister, value: u16) -> String {
|
||||
@@ -352,18 +356,18 @@ impl Computer {
|
||||
}
|
||||
|
||||
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 {
|
||||
self.memory[index] = value;
|
||||
"todo".to_owned()
|
||||
"".to_owned()
|
||||
}
|
||||
|
||||
fn set_memory_word(&mut self, index: usize, value: u16) -> String {
|
||||
self.memory[index] = (value % 256) as u8;
|
||||
self.memory[index + 1] = (value / 256) as u8;
|
||||
"todo".to_owned()
|
||||
"".to_owned()
|
||||
}
|
||||
|
||||
fn resolve_eaddr(&self, a: &EffectiveAddress) -> usize {
|
||||
@@ -507,7 +511,7 @@ impl Computer {
|
||||
MoveInstruction::ImmediateToMemory(mov) => match mov {
|
||||
ImmediateToMemory::Byte(addr, value) => {
|
||||
let dest = self.resolve_eaddr(addr);
|
||||
self.set_memory_byte(dest, *value)
|
||||
self.set_memory_word(dest, *value as u16)
|
||||
}
|
||||
ImmediateToMemory::Word(addr, value) => {
|
||||
let dest = self.resolve_eaddr(addr);
|
||||
@@ -570,7 +574,8 @@ impl Computer {
|
||||
let ip_desc = match old_ip {
|
||||
None => "".to_owned(),
|
||||
Some(old_ip) => format!(
|
||||
" ip:{}->{}",
|
||||
"{}ip:{}->{}",
|
||||
if description.is_empty() { "" } else { " " },
|
||||
Self::display_small(old_ip),
|
||||
Self::display_small(self.program_counter)
|
||||
),
|
||||
@@ -723,7 +728,20 @@ impl Computer {
|
||||
(format!("{}", instr.dest), current_value, new_value, flags)
|
||||
}
|
||||
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) => {
|
||||
let current_value = self.get_register(register);
|
||||
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!(
|
||||
" {}:{}->{}",
|
||||
source,
|
||||
@@ -855,20 +873,26 @@ impl Computer {
|
||||
Jump::Loop => {
|
||||
let reg = Register::General(GeneralRegister::C, RegisterSubset::All);
|
||||
let prev = self.get_register(®);
|
||||
self.set_register(®, prev - 1);
|
||||
let new_value = if prev == 0 {
|
||||
self.set_register(®, u16::MAX);
|
||||
u16::MAX
|
||||
} else {
|
||||
self.set_register(®, prev - 1);
|
||||
prev - 1
|
||||
};
|
||||
reg_desc.push_str(&format!(
|
||||
" cx:{}->{}",
|
||||
" ; cx:{}->{}",
|
||||
Self::display_small(prev),
|
||||
Self::display_small(prev - 1)
|
||||
Self::display_small(new_value)
|
||||
));
|
||||
prev == 1
|
||||
prev != 1
|
||||
}
|
||||
Jump::Loopz => {
|
||||
let reg = Register::General(GeneralRegister::C, RegisterSubset::All);
|
||||
let prev = self.get_register(®);
|
||||
self.set_register(®, prev - 1);
|
||||
reg_desc.push_str(&format!(
|
||||
" cx:{}->{}",
|
||||
" ; cx:{}->{}",
|
||||
Self::display_small(prev),
|
||||
Self::display_small(prev - 1)
|
||||
));
|
||||
|
@@ -18,8 +18,8 @@ where
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
WithOffset::Basic(t) => f.write_fmt(format_args!("{}", t)),
|
||||
WithOffset::WithU8(t, offset) => f.write_fmt(format_args!("[{} + {}]", t, offset)),
|
||||
WithOffset::WithU16(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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,33 +45,59 @@ impl Display for EffectiveAddress {
|
||||
match self {
|
||||
EffectiveAddress::Sum(w) => match w {
|
||||
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) => {
|
||||
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) => {
|
||||
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)) => {
|
||||
f.write_fmt(format_args!("[{}i]", source_dest))
|
||||
}
|
||||
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)) => {
|
||||
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 {
|
||||
WithOffset::Basic(()) => f.write_str("bx"),
|
||||
WithOffset::WithU8((), offset) => f.write_fmt(format_args!("[bx + {}]", offset)),
|
||||
WithOffset::WithU16((), 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)),
|
||||
},
|
||||
EffectiveAddress::Direct(location) => f.write_fmt(format_args!("[{}]", location)),
|
||||
EffectiveAddress::BasePointer(offset) => f.write_fmt(format_args!("[bp + {}]", offset)),
|
||||
EffectiveAddress::Direct(location) => f.write_fmt(format_args!("[+{}]", location)),
|
||||
EffectiveAddress::BasePointer(offset) => {
|
||||
if *offset == 0 {
|
||||
f.write_str("[bp]")
|
||||
} else {
|
||||
f.write_fmt(format_args!("[bp+{}]", 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -391,9 +391,16 @@ impl Display for MoveInstruction {
|
||||
MoveInstruction::RegRegMove(mov) => {
|
||||
f.write_fmt(format_args!("mov {}, {}", mov.dest, mov.source))
|
||||
}
|
||||
MoveInstruction::RegMemMove(mov) => {
|
||||
f.write_fmt(format_args!("mov {}, {}", mov.dest, mov.source))
|
||||
}
|
||||
MoveInstruction::RegMemMove(mov) => f.write_fmt(format_args!(
|
||||
"mov {}{}, {}",
|
||||
if mov.source.is_wide() {
|
||||
"word "
|
||||
} else {
|
||||
"byte "
|
||||
},
|
||||
mov.dest,
|
||||
mov.source
|
||||
)),
|
||||
MoveInstruction::MemRegMove(mov) => {
|
||||
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))
|
||||
}
|
||||
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)) => {
|
||||
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!(
|
||||
"mov a{}, [{}]",
|
||||
|
@@ -64,10 +64,10 @@ mod test_computer {
|
||||
|
||||
trace.push("".to_owned());
|
||||
|
||||
assert_eq!(
|
||||
trace,
|
||||
clean_trace(expected_trace).lines().collect::<Vec<_>>()
|
||||
)
|
||||
let cleaned = clean_trace(expected_trace);
|
||||
let expected = cleaned.lines().collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(trace, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -145,7 +145,6 @@ mod test_computer {
|
||||
test_sim(input_bytecode, expected_trace, true)
|
||||
}
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn test_memory_mov() {
|
||||
let input_bytecode =
|
||||
@@ -194,5 +193,4 @@ mod test_computer {
|
||||
);
|
||||
test_sim(input_bytecode, expected_trace, true)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@@ -243,6 +243,26 @@ mod test_program {
|
||||
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]
|
||||
fn test_immediate_movs_parser() {
|
||||
let input_asm =
|
||||
|
@@ -1,6 +1,7 @@
|
||||
use std::{fs, path::Path};
|
||||
|
||||
use clap::Parser;
|
||||
use sim_8086::computer::Computer;
|
||||
use sim_8086::instruction::Instruction;
|
||||
use sim_8086::program::Program;
|
||||
|
||||
@@ -17,6 +18,8 @@ struct Args {
|
||||
compiled_path: std::path::PathBuf,
|
||||
#[arg(value_name = "ASM_PATH")]
|
||||
asm_path: std::path::PathBuf,
|
||||
#[arg()]
|
||||
verify_consistency: Option<bool>,
|
||||
}
|
||||
|
||||
fn program_equal_ignoring_labels<A, B>(
|
||||
@@ -54,12 +57,8 @@ where
|
||||
true
|
||||
}
|
||||
|
||||
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();
|
||||
let (remaining, compiled) = sim_8086::assembly::program(&asm).unwrap();
|
||||
fn verify_consistency(pre_compiled: &Vec<u8>, asm: &str) {
|
||||
let (remaining, compiled) = sim_8086::assembly::program(asm).unwrap();
|
||||
|
||||
if !remaining.is_empty() {
|
||||
println!(
|
||||
@@ -70,15 +69,25 @@ fn main() {
|
||||
}
|
||||
|
||||
let actual_bytecode = compiled.to_bytes();
|
||||
if expected_bytecode != actual_bytecode {
|
||||
if pre_compiled.len() != actual_bytecode.len() {
|
||||
println!(
|
||||
"Expected: {:?}\nActual: {:?}",
|
||||
expected_bytecode, actual_bytecode
|
||||
pre_compiled, actual_bytecode
|
||||
);
|
||||
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 {
|
||||
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."
|
||||
)
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user