Count clocks (#19)
This commit is contained in:
@@ -32,17 +32,28 @@ impl Display for ArithmeticInstruction {
|
||||
ArithmeticInstructionSelect::RegisterToRegister(inst) => {
|
||||
f.write_fmt(format_args!("{}, {}", inst.dest, inst.source))
|
||||
}
|
||||
ArithmeticInstructionSelect::RegisterToMemory(inst) => {
|
||||
f.write_fmt(format_args!("{}, {}", inst.dest, inst.source))
|
||||
}
|
||||
ArithmeticInstructionSelect::RegisterToMemory(inst) => f.write_fmt(format_args!(
|
||||
"{} {}, {}",
|
||||
if inst.source.is_wide() {
|
||||
"word"
|
||||
} else {
|
||||
"byte"
|
||||
},
|
||||
inst.dest,
|
||||
inst.source
|
||||
)),
|
||||
ArithmeticInstructionSelect::MemoryToRegister(inst) => {
|
||||
f.write_fmt(format_args!("{}, {}", inst.dest, inst.source))
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryByte(addr, data, _) => {
|
||||
f.write_fmt(format_args!("{}, {}", addr, data))
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryByte(addr, data, is_wide) => f
|
||||
.write_fmt(format_args!(
|
||||
"{} {}, {}",
|
||||
if *is_wide { "word" } else { "byte" },
|
||||
addr,
|
||||
data
|
||||
)),
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryWord(addr, data) => {
|
||||
f.write_fmt(format_args!("{}, {}", addr, data))
|
||||
f.write_fmt(format_args!("word {}, {}", addr, data))
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterByte(addr, data, signed) => {
|
||||
if *signed {
|
||||
@@ -175,6 +186,89 @@ impl ArithmeticInstruction {
|
||||
ArithmeticInstructionSelect::ImmediateToAccWord(_) => 3,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clock_count(&self) -> (u32, String) {
|
||||
match self.op {
|
||||
ArithmeticOperation::Sub
|
||||
| ArithmeticOperation::AddWithCarry
|
||||
| ArithmeticOperation::SubWithBorrow
|
||||
| ArithmeticOperation::Add => match &self.instruction {
|
||||
ArithmeticInstructionSelect::RegisterToRegister(_) => (3, "".to_owned()),
|
||||
ArithmeticInstructionSelect::RegisterToMemory(instr) => {
|
||||
let (count, result) = instr.dest.clock_count();
|
||||
(count + 16, format!("16 {result}"))
|
||||
}
|
||||
ArithmeticInstructionSelect::MemoryToRegister(instr) => {
|
||||
let (count, result) = instr.source.clock_count();
|
||||
(count + 9, format!("9 {result}"))
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterByte(_, _, _) => (4, "".to_owned()),
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterWord(_, _, _) => (4, "".to_owned()),
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryByte(dest, _, _) => {
|
||||
let (count, result) = dest.clock_count();
|
||||
(count + 17, format!("17 {result}"))
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryWord(dest, _) => {
|
||||
let (count, result) = dest.clock_count();
|
||||
(count + 17, format!("17 {result}"))
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToAccByte(_) => (4, "".to_owned()),
|
||||
ArithmeticInstructionSelect::ImmediateToAccWord(_) => (4, "".to_owned()),
|
||||
},
|
||||
ArithmeticOperation::Cmp => match &self.instruction {
|
||||
ArithmeticInstructionSelect::RegisterToRegister(_) => (3, "".to_owned()),
|
||||
ArithmeticInstructionSelect::RegisterToMemory(instr) => {
|
||||
let (count, result) = instr.dest.clock_count();
|
||||
(count + 9, format!("9 {result}"))
|
||||
}
|
||||
ArithmeticInstructionSelect::MemoryToRegister(instr) => {
|
||||
let (count, result) = instr.source.clock_count();
|
||||
(count + 9, format!("9 {result}"))
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterByte(_, _, _) => (4, "".to_owned()),
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterWord(_, _, _) => (4, "".to_owned()),
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryByte(dest, _, _) => {
|
||||
let (count, result) = dest.clock_count();
|
||||
(count + 10, format!("10 {result}"))
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryWord(dest, _) => {
|
||||
let (count, result) = dest.clock_count();
|
||||
(count + 10, format!("10 {result}"))
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToAccByte(_) => (4, "".to_owned()),
|
||||
ArithmeticInstructionSelect::ImmediateToAccWord(_) => (4, "".to_owned()),
|
||||
},
|
||||
ArithmeticOperation::Xor | ArithmeticOperation::And | ArithmeticOperation::Or => {
|
||||
match &self.instruction {
|
||||
ArithmeticInstructionSelect::RegisterToRegister(_) => (3, "".to_owned()),
|
||||
ArithmeticInstructionSelect::RegisterToMemory(instr) => {
|
||||
let (count, result) = instr.dest.clock_count();
|
||||
(count + 16, format!("16 {result}"))
|
||||
}
|
||||
ArithmeticInstructionSelect::MemoryToRegister(instr) => {
|
||||
let (count, result) = instr.source.clock_count();
|
||||
(count + 9, format!("9 {result}"))
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterByte(_, _, _) => {
|
||||
(4, "".to_owned())
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterWord(_, _, _) => {
|
||||
(4, "".to_owned())
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryByte(addr, _, _) => {
|
||||
let (count, result) = addr.clock_count();
|
||||
(count + 17, format!("17, {result}"))
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryWord(addr, _) => {
|
||||
let (count, result) = addr.clock_count();
|
||||
(count + 17, format!("17, {result}"))
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToAccByte(_) => (4, "".to_owned()),
|
||||
ArithmeticInstructionSelect::ImmediateToAccWord(_) => (4, "".to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash, Arbitrary)]
|
||||
|
@@ -26,8 +26,8 @@ use crate::{
|
||||
instruction::Instruction,
|
||||
jump_instruction::Jump,
|
||||
move_instruction::{
|
||||
AccumulatorToMemory, ImmediateToMemory, ImmediateToRegister, MemRegMove,
|
||||
MemoryToAccumulator, MemoryToSegment, MoveInstruction, RegMemMove, RegRegMove,
|
||||
AccumulatorToMemory, ImmediateToMemory, ImmediateToRegister, MemToRegMove,
|
||||
MemoryToAccumulator, MemoryToSegment, MoveInstruction, RegRegMove, RegToMemMove,
|
||||
RegisterToSegment, SegmentToMemory, SegmentToRegister,
|
||||
},
|
||||
program::Program,
|
||||
@@ -475,7 +475,7 @@ fn seg_to_reg_move_instruction(input: &str) -> IResult<&str, SegmentToRegister>
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn reg_mem_move_instruction(input: &str) -> IResult<&str, RegMemMove> {
|
||||
fn reg_mem_move_instruction(input: &str) -> IResult<&str, RegToMemMove> {
|
||||
map_res(
|
||||
preceded(
|
||||
ws(tag("mov ")),
|
||||
@@ -483,7 +483,7 @@ fn reg_mem_move_instruction(input: &str) -> IResult<&str, RegMemMove> {
|
||||
),
|
||||
|((tag, address), register)| match (tag, register.is_wide()) {
|
||||
(OffsetTag::Word, false) => Err(()),
|
||||
_ => Ok::<_, ()>(RegMemMove {
|
||||
_ => Ok::<_, ()>(RegToMemMove {
|
||||
dest: address,
|
||||
source: register,
|
||||
}),
|
||||
@@ -491,7 +491,7 @@ fn reg_mem_move_instruction(input: &str) -> IResult<&str, RegMemMove> {
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn mem_reg_move_instruction(input: &str) -> IResult<&str, MemRegMove> {
|
||||
fn mem_reg_move_instruction(input: &str) -> IResult<&str, MemToRegMove> {
|
||||
map_res(
|
||||
preceded(
|
||||
ws(tag("mov ")),
|
||||
@@ -499,7 +499,7 @@ fn mem_reg_move_instruction(input: &str) -> IResult<&str, MemRegMove> {
|
||||
),
|
||||
|(register, (tag, address))| match (tag, register.is_wide()) {
|
||||
(OffsetTag::Word, false) => Err(()),
|
||||
_ => Ok::<_, ()>(MemRegMove {
|
||||
_ => Ok::<_, ()>(MemToRegMove {
|
||||
dest: register,
|
||||
source: address,
|
||||
}),
|
||||
|
@@ -163,4 +163,25 @@ impl BooleanInstruction {
|
||||
BooleanInstructionDestination::RegReg(_) => 2,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clock_count(&self) -> (u32, String) {
|
||||
match self.selection {
|
||||
BooleanInstructionType::Test => match self.dest {
|
||||
BooleanInstructionDestination::ImmediateToAcc(_) => (4, "".to_owned()),
|
||||
BooleanInstructionDestination::RegReg(_) => (3, "".to_owned()),
|
||||
},
|
||||
BooleanInstructionType::And => match self.dest {
|
||||
BooleanInstructionDestination::ImmediateToAcc(_) => (4, "".to_owned()),
|
||||
BooleanInstructionDestination::RegReg(_) => (3, "".to_owned()),
|
||||
},
|
||||
BooleanInstructionType::Or => match self.dest {
|
||||
BooleanInstructionDestination::ImmediateToAcc(_) => (4, "".to_owned()),
|
||||
BooleanInstructionDestination::RegReg(_) => (3, "".to_owned()),
|
||||
},
|
||||
BooleanInstructionType::Xor => match self.dest {
|
||||
BooleanInstructionDestination::ImmediateToAcc(_) => (4, "".to_owned()),
|
||||
BooleanInstructionDestination::RegReg(_) => (3, "".to_owned()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,10 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::boolean_instruction::BooleanInstruction;
|
||||
use crate::boolean_instruction::{
|
||||
BooleanInstruction, BooleanInstructionDestination, BooleanInstructionType, ImmediateToAcc,
|
||||
};
|
||||
use crate::inc_instruction::IncInstruction;
|
||||
use crate::logic_instruction::LogicInstruction;
|
||||
use crate::logic_instruction::{LogicInstruction, LogicInstructionType, LogicTarget};
|
||||
use crate::{
|
||||
arithmetic_instruction::{
|
||||
ArithmeticInstruction, ArithmeticInstructionSelect, ArithmeticOperation,
|
||||
@@ -249,11 +251,13 @@ pub struct Computer {
|
||||
registers: Registers,
|
||||
#[allow(dead_code)]
|
||||
memory: [u8; 65536],
|
||||
clocks_executed: u32,
|
||||
}
|
||||
|
||||
struct ResultFlags {
|
||||
auxiliary_carry: bool,
|
||||
overflow: bool,
|
||||
sign: bool,
|
||||
should_write: bool,
|
||||
carry: bool,
|
||||
}
|
||||
@@ -279,6 +283,7 @@ impl Computer {
|
||||
},
|
||||
flags: Flags { fields: 0 },
|
||||
program_counter: 0,
|
||||
clocks_executed: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,12 +351,7 @@ impl Computer {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_register(&mut self, r: &Register, value: u16) -> String {
|
||||
let register_for_print = match r {
|
||||
Register::General(x, _) => Register::General(x.clone(), RegisterSubset::All),
|
||||
_ => r.clone(),
|
||||
};
|
||||
let was = self.get_register(®ister_for_print);
|
||||
fn set_register(&mut self, r: &Register, value: u16) {
|
||||
match r {
|
||||
Register::General(GeneralRegister::A, RegisterSubset::All) => self.registers.a = value,
|
||||
Register::General(GeneralRegister::B, RegisterSubset::All) => self.registers.b = value,
|
||||
@@ -418,48 +418,36 @@ impl Computer {
|
||||
Register::Special(SpecialRegister::SourceIndex) => self.registers.si = value,
|
||||
Register::Special(SpecialRegister::DestIndex) => self.registers.di = value,
|
||||
}
|
||||
let is_now = self.get_register(®ister_for_print);
|
||||
if was != is_now {
|
||||
format!(
|
||||
"{}:{}->{}",
|
||||
register_for_print,
|
||||
display_small(was),
|
||||
display_small(is_now)
|
||||
)
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_segment(&mut self, r: SegmentRegister, value: u16) -> String {
|
||||
let was = self.get_segment(r);
|
||||
fn set_segment(&mut self, r: SegmentRegister, value: u16) {
|
||||
match r {
|
||||
SegmentRegister::Code => self.registers.cs = value,
|
||||
SegmentRegister::Data => self.registers.ds = value,
|
||||
SegmentRegister::Stack => self.registers.ss = value,
|
||||
SegmentRegister::Extra => self.registers.es = value,
|
||||
}
|
||||
let is_now = self.get_segment(r);
|
||||
format!("{}:{}->{}", r, display_small(was), display_small(is_now))
|
||||
}
|
||||
|
||||
fn get_memory_byte(&self, index: usize) -> u8 {
|
||||
self.memory[index]
|
||||
}
|
||||
|
||||
fn get_memory_word(&self, index: usize) -> u16 {
|
||||
self.memory[index] as u16 + self.memory[index + 1] as u16 * 256
|
||||
/// Returns true if the access took an extra four cycles.
|
||||
fn get_memory_word(&self, index: usize) -> (u16, bool) {
|
||||
let value = self.memory[index] as u16 + self.memory[index + 1] as u16 * 256;
|
||||
(value, index % 2 == 1)
|
||||
}
|
||||
|
||||
fn set_memory_byte(&mut self, index: usize, value: u8) -> String {
|
||||
fn set_memory_byte(&mut self, index: usize, value: u8) {
|
||||
self.memory[index] = value;
|
||||
"".to_owned()
|
||||
}
|
||||
|
||||
fn set_memory_word(&mut self, index: usize, value: u16) -> String {
|
||||
/// Returns true if the access took an extra four cycles.
|
||||
fn set_memory_word(&mut self, index: usize, value: u16) -> bool {
|
||||
self.memory[index] = (value % 256) as u8;
|
||||
self.memory[index + 1] = (value / 256) as u8;
|
||||
"".to_owned()
|
||||
index % 2 == 1
|
||||
}
|
||||
|
||||
fn get_base_offset(&self, base: &Base) -> u16 {
|
||||
@@ -536,38 +524,48 @@ impl Computer {
|
||||
}
|
||||
}
|
||||
|
||||
fn step_mov(&mut self, instruction: &MoveInstruction) -> String {
|
||||
let preamble = format!("{}", instruction);
|
||||
let description = match &instruction {
|
||||
/// Returns true if the access took four extra clocks.
|
||||
fn step_mov(&mut self, instruction: &MoveInstruction) -> bool {
|
||||
match &instruction {
|
||||
MoveInstruction::RegRegMove(mov) => {
|
||||
let value = self.get_register(&mov.source);
|
||||
self.set_register(&mov.dest, value)
|
||||
self.set_register(&mov.dest, value);
|
||||
false
|
||||
}
|
||||
MoveInstruction::RegMemMove(mov) => {
|
||||
let value = self.get_register(&mov.source);
|
||||
if mov.source.is_wide() {
|
||||
self.set_memory_word(self.resolve_eaddr(&mov.dest), value)
|
||||
} else {
|
||||
self.set_memory_byte(self.resolve_eaddr(&mov.dest), value as u8)
|
||||
self.set_memory_byte(self.resolve_eaddr(&mov.dest), value as u8);
|
||||
false
|
||||
}
|
||||
}
|
||||
MoveInstruction::MemRegMove(mov) => {
|
||||
if mov.dest.is_wide() {
|
||||
let value = self.get_memory_word(self.resolve_eaddr(&mov.source));
|
||||
self.set_register(&mov.dest, value)
|
||||
let (value, slow) = self.get_memory_word(self.resolve_eaddr(&mov.source));
|
||||
self.set_register(&mov.dest, value);
|
||||
slow
|
||||
} else {
|
||||
let value = self.get_memory_byte(self.resolve_eaddr(&mov.source));
|
||||
self.set_register(&mov.dest, value as u16)
|
||||
self.set_register(&mov.dest, value as u16);
|
||||
false
|
||||
}
|
||||
}
|
||||
MoveInstruction::ImmediateToRegister(mov) => match mov {
|
||||
ImmediateToRegister::Byte(dest, value) => self.set_register(dest, *value as u16),
|
||||
ImmediateToRegister::Wide(dest, value) => self.set_register(dest, *value),
|
||||
},
|
||||
MoveInstruction::ImmediateToRegister(mov) => {
|
||||
match mov {
|
||||
ImmediateToRegister::Byte(dest, value) => {
|
||||
self.set_register(dest, *value as u16)
|
||||
}
|
||||
ImmediateToRegister::Wide(dest, value) => self.set_register(dest, *value),
|
||||
}
|
||||
false
|
||||
}
|
||||
MoveInstruction::ImmediateToMemory(mov) => match mov {
|
||||
ImmediateToMemory::Byte(addr, value) => {
|
||||
let dest = self.resolve_eaddr(addr);
|
||||
self.set_memory_word(dest, *value as u16)
|
||||
self.set_memory_byte(dest, *value);
|
||||
false
|
||||
}
|
||||
ImmediateToMemory::Word(addr, value) => {
|
||||
let dest = self.resolve_eaddr(addr);
|
||||
@@ -576,10 +574,12 @@ impl Computer {
|
||||
},
|
||||
MoveInstruction::MemoryToAccumulator(mov) => {
|
||||
if mov.is_wide {
|
||||
let (value, slow) = self.get_memory_word(mov.address as usize);
|
||||
self.set_register(
|
||||
&Register::General(GeneralRegister::A, RegisterSubset::All),
|
||||
self.get_memory_word(mov.address as usize),
|
||||
)
|
||||
value,
|
||||
);
|
||||
slow
|
||||
} else {
|
||||
self.set_register(
|
||||
&Register::General(
|
||||
@@ -587,7 +587,8 @@ impl Computer {
|
||||
RegisterSubset::Subset(ByteRegisterSubset::Low),
|
||||
),
|
||||
self.get_memory_byte(mov.address as usize) as u16,
|
||||
)
|
||||
);
|
||||
false
|
||||
}
|
||||
}
|
||||
MoveInstruction::AccumulatorToMemory(mov) => {
|
||||
@@ -606,7 +607,8 @@ impl Computer {
|
||||
GeneralRegister::A,
|
||||
RegisterSubset::Subset(ByteRegisterSubset::Low),
|
||||
)) as u8,
|
||||
)
|
||||
);
|
||||
false
|
||||
}
|
||||
}
|
||||
MoveInstruction::SegmentToMemory(mov) => {
|
||||
@@ -615,30 +617,138 @@ impl Computer {
|
||||
self.set_memory_word(dest, v)
|
||||
}
|
||||
MoveInstruction::MemoryToSegment(mov) => {
|
||||
let value = self.get_memory_word(self.resolve_eaddr(&mov.source));
|
||||
self.set_segment(mov.dest, value)
|
||||
let (value, slow) = self.get_memory_word(self.resolve_eaddr(&mov.source));
|
||||
self.set_segment(mov.dest, value);
|
||||
slow
|
||||
}
|
||||
MoveInstruction::SegmentToRegister(mov) => {
|
||||
let v = self.get_segment(mov.source);
|
||||
self.set_register(&mov.dest, v)
|
||||
self.set_register(&mov.dest, v);
|
||||
false
|
||||
}
|
||||
MoveInstruction::RegisterToSegment(mov) => {
|
||||
let value = self.get_register(&mov.source);
|
||||
self.set_segment(mov.dest, value)
|
||||
self.set_segment(mov.dest, value);
|
||||
false
|
||||
}
|
||||
};
|
||||
format!(
|
||||
"{} ;{}{}",
|
||||
preamble,
|
||||
if description.is_empty() { "" } else { " " },
|
||||
description
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the operation overflowed, and true if the value is supposed
|
||||
/// to be written to the destination (so `cmp` returns false), and true if there
|
||||
/// was a carry out of the low-order nibble.
|
||||
fn apply(
|
||||
fn apply_u8(op: ArithmeticOperation, to_arg: u8, incoming_arg_raw: u8) -> (u8, ResultFlags) {
|
||||
let incoming_arg = if incoming_arg_raw >= 128 {
|
||||
u8::MAX - (255 - incoming_arg_raw)
|
||||
} else {
|
||||
incoming_arg_raw
|
||||
};
|
||||
|
||||
match op {
|
||||
ArithmeticOperation::Add => {
|
||||
let result = to_arg as u16 + incoming_arg as u16;
|
||||
let to_ret = (result % (u8::MAX as u16 + 1)) as u8;
|
||||
let result_flags = ResultFlags {
|
||||
overflow: (to_arg >= 1 << 7 && incoming_arg >= 1 << 7 && result < 1 << 7)
|
||||
|| (to_arg < 1 << 7 && incoming_arg < 1 << 7 && result > 1 << 7),
|
||||
auxiliary_carry: (to_arg % 16) + (incoming_arg_raw % 16) >= 16,
|
||||
should_write: true,
|
||||
carry: result > u8::MAX as u16,
|
||||
sign: to_ret > 1 << 7,
|
||||
};
|
||||
(to_ret, result_flags)
|
||||
}
|
||||
ArithmeticOperation::Or => (
|
||||
to_arg | incoming_arg,
|
||||
ResultFlags {
|
||||
overflow: false,
|
||||
should_write: true,
|
||||
carry: false,
|
||||
auxiliary_carry: false,
|
||||
sign: false,
|
||||
},
|
||||
),
|
||||
ArithmeticOperation::AddWithCarry => todo!(),
|
||||
ArithmeticOperation::SubWithBorrow => todo!(),
|
||||
ArithmeticOperation::And => (
|
||||
to_arg & incoming_arg,
|
||||
ResultFlags {
|
||||
overflow: false,
|
||||
should_write: true,
|
||||
auxiliary_carry: false,
|
||||
carry: true,
|
||||
sign: false,
|
||||
},
|
||||
),
|
||||
ArithmeticOperation::Sub => {
|
||||
let auxiliary_carry = to_arg % 16 < incoming_arg_raw % 16;
|
||||
if to_arg < incoming_arg {
|
||||
let result = u8::MAX - (incoming_arg - to_arg) + 1;
|
||||
(
|
||||
result,
|
||||
ResultFlags {
|
||||
should_write: true,
|
||||
overflow: (result < 1 << 7 && to_arg >= 1 << 7),
|
||||
auxiliary_carry,
|
||||
carry: true,
|
||||
sign: result > 1 << 7,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
// We might be wrapping around 0, so check for overflow separately.
|
||||
let result = to_arg - incoming_arg;
|
||||
(
|
||||
result,
|
||||
ResultFlags {
|
||||
should_write: true,
|
||||
overflow: (result < 1 << 7 && to_arg >= 1 << 7),
|
||||
auxiliary_carry,
|
||||
carry: false,
|
||||
sign: result > 1 << 7,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
ArithmeticOperation::Xor => (
|
||||
to_arg ^ incoming_arg,
|
||||
ResultFlags {
|
||||
overflow: false,
|
||||
should_write: true,
|
||||
auxiliary_carry: false,
|
||||
carry: false,
|
||||
sign: false,
|
||||
},
|
||||
),
|
||||
ArithmeticOperation::Cmp => {
|
||||
// CPAS can all be set by Cmp
|
||||
let auxiliary_carry = to_arg % 16 < incoming_arg_raw % 16;
|
||||
if to_arg < incoming_arg {
|
||||
let result = u8::MAX - (incoming_arg - to_arg) + 1;
|
||||
(
|
||||
result,
|
||||
ResultFlags {
|
||||
should_write: false,
|
||||
auxiliary_carry,
|
||||
overflow: (result < 1 << 7 && 7 >= 1 << 7),
|
||||
carry: true,
|
||||
sign: result > 1 << 7,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
let result = to_arg - incoming_arg;
|
||||
(
|
||||
result,
|
||||
ResultFlags {
|
||||
should_write: false,
|
||||
auxiliary_carry,
|
||||
overflow: (result < 1 << 7 && to_arg >= 1 << 7),
|
||||
carry: false,
|
||||
sign: result > 1 << 7,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_u16(
|
||||
op: ArithmeticOperation,
|
||||
to_arg: u16,
|
||||
incoming_arg_raw: u16,
|
||||
@@ -654,14 +764,16 @@ impl Computer {
|
||||
match op {
|
||||
ArithmeticOperation::Add => {
|
||||
let result = to_arg as u32 + incoming_arg as u32;
|
||||
let to_ret = (result % (u16::MAX as u32 + 1)) as u16;
|
||||
let result_flags = ResultFlags {
|
||||
overflow: (to_arg >= 1 << 15 && incoming_arg >= 1 << 15 && result < 1 << 15)
|
||||
|| (to_arg < 1 << 15 && incoming_arg < 1 << 15 && result > 1 << 15),
|
||||
auxiliary_carry: (to_arg % 16) + (incoming_arg_raw % 16) >= 16,
|
||||
should_write: true,
|
||||
carry: result > u16::MAX as u32,
|
||||
sign: to_ret > 1 << 15,
|
||||
};
|
||||
((result % (u16::MAX as u32 + 1)) as u16, result_flags)
|
||||
(to_ret, result_flags)
|
||||
}
|
||||
ArithmeticOperation::Or => (
|
||||
to_arg | incoming_arg,
|
||||
@@ -670,6 +782,7 @@ impl Computer {
|
||||
should_write: true,
|
||||
carry: false,
|
||||
auxiliary_carry: false,
|
||||
sign: false,
|
||||
},
|
||||
),
|
||||
ArithmeticOperation::AddWithCarry => todo!(),
|
||||
@@ -681,6 +794,7 @@ impl Computer {
|
||||
should_write: true,
|
||||
auxiliary_carry: false,
|
||||
carry: true,
|
||||
sign: false,
|
||||
},
|
||||
),
|
||||
ArithmeticOperation::Sub => {
|
||||
@@ -694,6 +808,7 @@ impl Computer {
|
||||
overflow: (result < 1 << 15 && to_arg >= 1 << 15),
|
||||
auxiliary_carry,
|
||||
carry: true,
|
||||
sign: result > 1 << 15,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
@@ -706,6 +821,7 @@ impl Computer {
|
||||
overflow: (result < 1 << 15 && to_arg >= 1 << 15),
|
||||
auxiliary_carry,
|
||||
carry: false,
|
||||
sign: result > 1 << 15,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -717,6 +833,7 @@ impl Computer {
|
||||
should_write: true,
|
||||
auxiliary_carry: false,
|
||||
carry: false,
|
||||
sign: false,
|
||||
},
|
||||
),
|
||||
ArithmeticOperation::Cmp => {
|
||||
@@ -731,6 +848,7 @@ impl Computer {
|
||||
auxiliary_carry,
|
||||
overflow: (result < 1 << 15 && to_arg >= 1 << 15),
|
||||
carry: true,
|
||||
sign: result > 1 << 15,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
@@ -742,6 +860,7 @@ impl Computer {
|
||||
auxiliary_carry,
|
||||
overflow: (result < 1 << 15 && to_arg >= 1 << 15),
|
||||
carry: false,
|
||||
sign: result > 1 << 15,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -762,37 +881,91 @@ impl Computer {
|
||||
parity % 2 == 1
|
||||
}
|
||||
|
||||
fn step_arithmetic(&mut self, instruction: &ArithmeticInstruction) -> String {
|
||||
let (source, old_value, new_value, flags) = match &instruction.instruction {
|
||||
/// Returns a count of the extra cycles caused by misaligned memory access.
|
||||
fn step_arithmetic(&mut self, instruction: &ArithmeticInstruction) -> u8 {
|
||||
let mut slowness = 0;
|
||||
|
||||
let (new_value, flags) = match &instruction.instruction {
|
||||
ArithmeticInstructionSelect::RegisterToRegister(instr) => {
|
||||
let current_value = self.get_register(&instr.dest);
|
||||
let incoming_value = self.get_register(&instr.source);
|
||||
let (new_value, flags) =
|
||||
Self::apply(instruction.op, current_value, incoming_value, false);
|
||||
if flags.should_write {
|
||||
self.set_register(&instr.dest, new_value);
|
||||
if instr.dest.is_wide() {
|
||||
let (new_value, flags) =
|
||||
Self::apply_u16(instruction.op, current_value, incoming_value, false);
|
||||
if flags.should_write {
|
||||
self.set_register(&instr.dest, new_value);
|
||||
}
|
||||
(new_value, flags)
|
||||
} else {
|
||||
let (new_value, flags) =
|
||||
Self::apply_u8(instruction.op, current_value as u8, incoming_value as u8);
|
||||
if flags.should_write {
|
||||
self.set_register(&instr.dest, new_value as u16);
|
||||
}
|
||||
(new_value as u16, flags)
|
||||
}
|
||||
(format!("{}", instr.dest), current_value, new_value, flags)
|
||||
}
|
||||
ArithmeticInstructionSelect::RegisterToMemory(_) => todo!(),
|
||||
ArithmeticInstructionSelect::RegisterToMemory(instr) => {
|
||||
let dest = self.resolve_eaddr(&instr.dest);
|
||||
let (current_value, slow) = if instr.source.is_wide() {
|
||||
self.get_memory_word(dest)
|
||||
} else {
|
||||
(self.get_memory_byte(dest) as u16, false)
|
||||
};
|
||||
if slow {
|
||||
slowness += 4;
|
||||
}
|
||||
let incoming = self.get_register(&instr.source);
|
||||
|
||||
let (new_value, flags) = if instr.source.is_wide() {
|
||||
Self::apply_u16(instruction.op, current_value, incoming, false)
|
||||
} else {
|
||||
let (result, flags) =
|
||||
Self::apply_u8(instruction.op, current_value as u8, incoming as u8);
|
||||
(result as u16, flags)
|
||||
};
|
||||
|
||||
if flags.should_write {
|
||||
if instr.source.is_wide() {
|
||||
if self.set_memory_word(dest, new_value) {
|
||||
slowness += 4;
|
||||
}
|
||||
} else {
|
||||
self.set_memory_byte(dest, new_value as u8);
|
||||
}
|
||||
}
|
||||
|
||||
(new_value, flags)
|
||||
}
|
||||
ArithmeticInstructionSelect::MemoryToRegister(instr) => {
|
||||
let current_value = self.get_register(&instr.dest);
|
||||
let incoming_value = if instr.dest.is_wide() {
|
||||
let (incoming_value, slow) = 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
|
||||
(
|
||||
self.get_memory_byte(self.resolve_eaddr(&instr.source)) as u16,
|
||||
false,
|
||||
)
|
||||
};
|
||||
if slow {
|
||||
slowness += 4;
|
||||
}
|
||||
let (new_value, flags) = if instr.dest.is_wide() {
|
||||
Self::apply_u16(instruction.op, current_value, incoming_value, false)
|
||||
} else {
|
||||
let (result, flags) =
|
||||
Self::apply_u8(instruction.op, current_value as u8, incoming_value as u8);
|
||||
(result as u16, flags)
|
||||
};
|
||||
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)
|
||||
(new_value, flags)
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterByte(register, value, is_extended) => {
|
||||
let current_value = self.get_register(register);
|
||||
let (new_value, flags) = if *is_extended {
|
||||
Self::apply(instruction.op, current_value, *value as u16, *is_extended)
|
||||
Self::apply_u16(instruction.op, current_value, *value as u16, *is_extended)
|
||||
// Self::apply(instruction.op, current_value, *value as u16)
|
||||
} else {
|
||||
todo!()
|
||||
@@ -800,47 +973,68 @@ impl Computer {
|
||||
if flags.should_write {
|
||||
self.set_register(register, new_value);
|
||||
}
|
||||
(format!("{}", register), current_value, new_value, flags)
|
||||
(new_value, flags)
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterWord(register, value, is_extended) => {
|
||||
// TODO: why have we not used is_extended
|
||||
let current_value = self.get_register(register);
|
||||
let (new_value, flags) =
|
||||
Self::apply(instruction.op, current_value, *value, *is_extended);
|
||||
Self::apply_u16(instruction.op, current_value, *value, *is_extended);
|
||||
if flags.should_write {
|
||||
self.set_register(register, new_value);
|
||||
}
|
||||
(format!("{}", register), current_value, new_value, flags)
|
||||
(new_value, flags)
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryByte(
|
||||
addr,
|
||||
value,
|
||||
is_extended,
|
||||
) => {
|
||||
let location = self.resolve_eaddr(addr);
|
||||
let current_value = self.get_memory_byte(location);
|
||||
if *is_extended {
|
||||
let (new_value, flags) =
|
||||
Self::apply_u16(instruction.op, current_value as u16, *value as u16, false);
|
||||
if flags.should_write {
|
||||
self.set_memory_byte(location, new_value as u8);
|
||||
}
|
||||
(new_value, flags)
|
||||
} else {
|
||||
let (new_value, flags) = Self::apply_u8(instruction.op, current_value, *value);
|
||||
if flags.should_write {
|
||||
self.set_memory_byte(location, new_value);
|
||||
}
|
||||
(new_value as u16, flags)
|
||||
}
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryByte(_, _, _) => todo!(),
|
||||
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryWord(_, _) => todo!(),
|
||||
ArithmeticInstructionSelect::ImmediateToAccByte(value) => {
|
||||
let reg = Register::General(
|
||||
GeneralRegister::A,
|
||||
RegisterSubset::Subset(ByteRegisterSubset::Low),
|
||||
);
|
||||
let current_value = self.get_register(®);
|
||||
let (new_value, flags) =
|
||||
Self::apply(instruction.op, current_value, *value as u16, false);
|
||||
let current_value = self.get_register(®) as u8;
|
||||
let (new_value, flags) = Self::apply_u8(instruction.op, current_value, *value);
|
||||
if flags.should_write {
|
||||
self.set_register(®, new_value);
|
||||
self.set_register(®, new_value as u16);
|
||||
}
|
||||
(format!("{}", reg), current_value, new_value, flags)
|
||||
(new_value as u16, flags)
|
||||
}
|
||||
ArithmeticInstructionSelect::ImmediateToAccWord(value) => {
|
||||
let reg = Register::General(GeneralRegister::A, RegisterSubset::All);
|
||||
let current_value = self.get_register(®);
|
||||
let (new_value, flags) = Self::apply(instruction.op, current_value, *value, false);
|
||||
let (new_value, flags) =
|
||||
Self::apply_u16(instruction.op, current_value, *value, false);
|
||||
if flags.should_write {
|
||||
self.set_register(®, new_value);
|
||||
}
|
||||
(format!("{}", reg), current_value, new_value, flags)
|
||||
(new_value, flags)
|
||||
}
|
||||
};
|
||||
|
||||
self.set_flag(Flag::Status(StatusFlag::Zero), new_value == 0);
|
||||
self.set_flag(Flag::Status(StatusFlag::Overflow), flags.overflow);
|
||||
// TODO: what if this was a byte instruction instead
|
||||
self.set_flag(Flag::Status(StatusFlag::Sign), new_value & 0x8000 > 0);
|
||||
self.set_flag(Flag::Status(StatusFlag::Sign), flags.sign);
|
||||
self.set_flag(
|
||||
Flag::Status(StatusFlag::AuxiliaryCarry),
|
||||
flags.auxiliary_carry,
|
||||
@@ -850,21 +1044,74 @@ impl Computer {
|
||||
Flag::Status(StatusFlag::Parity),
|
||||
!Self::is_odd_parity(new_value % 256),
|
||||
);
|
||||
let result_desc = if flags.should_write && old_value != new_value {
|
||||
format!(
|
||||
" {}:{}->{}",
|
||||
source,
|
||||
display_small(old_value),
|
||||
display_small(new_value)
|
||||
)
|
||||
} else {
|
||||
"".to_owned()
|
||||
};
|
||||
format!("{instruction} ;{result_desc}")
|
||||
|
||||
slowness
|
||||
}
|
||||
|
||||
fn step_boolean(&mut self, _instruction: &BooleanInstruction) -> String {
|
||||
todo!()
|
||||
fn compute_boolean(
|
||||
&mut self,
|
||||
instruction: &BooleanInstructionType,
|
||||
op1: u16,
|
||||
op2: u16,
|
||||
) -> Option<u16> {
|
||||
self.set_flag(Flag::Status(StatusFlag::Overflow), false);
|
||||
self.set_flag(Flag::Status(StatusFlag::Carry), false);
|
||||
let new_value = match instruction {
|
||||
BooleanInstructionType::Test | BooleanInstructionType::And => op1 & op2,
|
||||
BooleanInstructionType::Or => op1 | op2,
|
||||
BooleanInstructionType::Xor => op1 ^ op2,
|
||||
};
|
||||
|
||||
self.set_flags_u16(new_value);
|
||||
|
||||
match instruction {
|
||||
BooleanInstructionType::Test => None,
|
||||
_ => Some(new_value),
|
||||
}
|
||||
}
|
||||
|
||||
fn step_boolean(&mut self, instruction: &BooleanInstruction) {
|
||||
match &instruction.dest {
|
||||
BooleanInstructionDestination::RegReg(reg) => {
|
||||
let incoming = self.get_register(®.source);
|
||||
let current = self.get_register(®.dest);
|
||||
if let Some(new) = self.compute_boolean(&instruction.selection, incoming, current) {
|
||||
self.set_register(®.dest, new)
|
||||
}
|
||||
}
|
||||
BooleanInstructionDestination::ImmediateToAcc(imm) => match imm {
|
||||
ImmediateToAcc::Wide(incoming) => {
|
||||
let register = Register::General(GeneralRegister::A, RegisterSubset::All);
|
||||
let current = self.get_register(®ister);
|
||||
if let Some(new) =
|
||||
self.compute_boolean(&instruction.selection, *incoming, current)
|
||||
{
|
||||
self.set_register(®ister, new);
|
||||
}
|
||||
}
|
||||
ImmediateToAcc::Narrow(_) => {
|
||||
todo!()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn set_flags_u16(&mut self, v: u16) {
|
||||
self.set_flag(
|
||||
Flag::Status(StatusFlag::Parity),
|
||||
!Self::is_odd_parity(v % 256),
|
||||
);
|
||||
self.set_flag(Flag::Status(StatusFlag::Sign), v > 1 << 15);
|
||||
self.set_flag(Flag::Status(StatusFlag::Zero), v == 0);
|
||||
}
|
||||
|
||||
fn set_flags_u8(&mut self, v: u8) {
|
||||
self.set_flag(
|
||||
Flag::Status(StatusFlag::Parity),
|
||||
!Self::is_odd_parity(v as u16),
|
||||
);
|
||||
self.set_flag(Flag::Status(StatusFlag::Sign), v > 1 << 7);
|
||||
self.set_flag(Flag::Status(StatusFlag::Zero), v == 0);
|
||||
}
|
||||
|
||||
fn step_inc(&mut self, instruction: &IncInstruction) {
|
||||
@@ -885,18 +1132,101 @@ impl Computer {
|
||||
old_value - 1
|
||||
};
|
||||
|
||||
self.set_flags_u16(new_value);
|
||||
if instruction.is_inc {
|
||||
self.set_flag(Flag::Status(StatusFlag::AuxiliaryCarry), old_value == 15);
|
||||
} else {
|
||||
self.set_flag(Flag::Status(StatusFlag::AuxiliaryCarry), old_value == 16);
|
||||
}
|
||||
|
||||
self.set_register(&instruction.target, new_value);
|
||||
}
|
||||
|
||||
fn step_ret(&mut self) -> String {
|
||||
todo!()
|
||||
#[allow(dead_code)]
|
||||
fn step_ret(&mut self) {
|
||||
let sp = Register::Special(SpecialRegister::StackPointer);
|
||||
let sp_prev = self.get_register(&sp);
|
||||
let new_sp = if sp_prev > u16::MAX - 2 {
|
||||
sp_prev - (u16::MAX - 2)
|
||||
} else {
|
||||
sp_prev + 2
|
||||
};
|
||||
self.set_register(&sp, new_sp);
|
||||
let (new_counter, _) = self.get_memory_word(sp_prev as usize);
|
||||
self.program_counter = new_counter;
|
||||
}
|
||||
|
||||
fn step_logic(&mut self, _instruction: &LogicInstruction) {
|
||||
todo!()
|
||||
/// Returns the number of extra clock cycles incurred by misaligned memory access.
|
||||
fn step_logic(&mut self, instruction: &LogicInstruction) -> u8 {
|
||||
if instruction.amount_from_cl {
|
||||
todo!()
|
||||
}
|
||||
match &instruction.op {
|
||||
LogicInstructionType::Shr => match &instruction.target {
|
||||
LogicTarget::Register(reg) => {
|
||||
if instruction.is_wide {
|
||||
let current_value = self.get_register(reg);
|
||||
let new_value = current_value >> 1;
|
||||
self.set_register(reg, new_value);
|
||||
if (current_value > 1 << 15) == (new_value > 1 << 15) {
|
||||
self.set_flag(Flag::Status(StatusFlag::Overflow), false)
|
||||
} else {
|
||||
self.set_flag(Flag::Status(StatusFlag::Overflow), true)
|
||||
}
|
||||
self.set_flags_u16(new_value);
|
||||
self.set_flag(Flag::Status(StatusFlag::Carry), current_value % 2 == 1);
|
||||
} else {
|
||||
let current_value = self.get_register(reg) as u8;
|
||||
let new_value = current_value >> 1;
|
||||
self.set_register(reg, new_value as u16);
|
||||
if (current_value > 1 << 7) == (new_value > 1 << 7) {
|
||||
self.set_flag(Flag::Status(StatusFlag::Overflow), false)
|
||||
} else {
|
||||
self.set_flag(Flag::Status(StatusFlag::Overflow), true)
|
||||
}
|
||||
self.set_flags_u8(new_value);
|
||||
self.set_flag(Flag::Status(StatusFlag::Carry), current_value % 2 == 1);
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
LogicTarget::Address(addr) => {
|
||||
if instruction.is_wide {
|
||||
let addr = self.resolve_eaddr(addr);
|
||||
let (current_value, slow) = self.get_memory_word(addr);
|
||||
let new_value = current_value >> 1;
|
||||
let slow_2 = self.set_memory_word(addr, new_value);
|
||||
if (current_value > 1 << 15) == (new_value > 1 << 15) {
|
||||
self.set_flag(Flag::Status(StatusFlag::Overflow), false)
|
||||
} else {
|
||||
self.set_flag(Flag::Status(StatusFlag::Overflow), true)
|
||||
}
|
||||
self.set_flags_u16(new_value);
|
||||
self.set_flag(Flag::Status(StatusFlag::Carry), current_value % 2 == 1);
|
||||
(if slow { 4 } else { 0 }) + if slow_2 { 4 } else { 0 }
|
||||
} else {
|
||||
let addr = self.resolve_eaddr(addr);
|
||||
let current_value = self.get_memory_byte(addr);
|
||||
let new_value = current_value >> 1;
|
||||
self.set_memory_byte(addr, new_value);
|
||||
if (current_value > 1 << 7) == (new_value > 1 << 7) {
|
||||
self.set_flag(Flag::Status(StatusFlag::Overflow), false)
|
||||
} else {
|
||||
self.set_flag(Flag::Status(StatusFlag::Overflow), true)
|
||||
}
|
||||
self.set_flags_u8(new_value);
|
||||
self.set_flag(Flag::Status(StatusFlag::Carry), current_value % 2 == 1);
|
||||
0
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn step_jump(&mut self, jump: Jump, offset: i8) -> String {
|
||||
/// Returns a string description of the jump, and a bool indicating
|
||||
/// whether the jump actually happened.
|
||||
fn step_jump(&mut self, jump: Jump, offset: i8) -> (String, bool) {
|
||||
let should_jump = match jump {
|
||||
Jump::Je => self.flags.get(Flag::Status(StatusFlag::Zero)),
|
||||
Jump::Jl => {
|
||||
@@ -966,11 +1296,14 @@ impl Computer {
|
||||
self.program_counter = (self.program_counter as i32 + offset as i32) as u16;
|
||||
}
|
||||
// In NASM, the dollar sign is an offset *without* including the bytes of the jump.
|
||||
format!(
|
||||
"{} ${}{}",
|
||||
jump,
|
||||
if offset > 0 { "+" } else { "" },
|
||||
offset + 2,
|
||||
(
|
||||
format!(
|
||||
"{} ${}{}",
|
||||
jump,
|
||||
if offset > 0 { "+" } else { "" },
|
||||
offset + 2,
|
||||
),
|
||||
should_jump,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -978,8 +1311,13 @@ impl Computer {
|
||||
self.program_counter
|
||||
}
|
||||
|
||||
/// Returns a string representation of what happened.
|
||||
pub fn step(&mut self, instruction: &Instruction<i8>, display_ip: bool) -> String {
|
||||
/// Returns a string representation of what happened, and true if we are meant to stop.
|
||||
pub fn step(
|
||||
&mut self,
|
||||
instruction: &Instruction<i8>,
|
||||
display_ip: bool,
|
||||
show_clock: bool,
|
||||
) -> (String, bool) {
|
||||
let advance = instruction.length();
|
||||
let old_ip = if display_ip {
|
||||
Some(self.program_counter)
|
||||
@@ -991,68 +1329,106 @@ impl Computer {
|
||||
|
||||
self.program_counter += advance as u16;
|
||||
|
||||
let instruction_override = match instruction {
|
||||
let (instruction_override, stop, slowness) = match instruction {
|
||||
Instruction::Move(mov) => {
|
||||
self.step_mov(mov);
|
||||
None
|
||||
let slow = self.step_mov(mov);
|
||||
(None, false, if slow { 4 } else { 0 })
|
||||
}
|
||||
Instruction::Arithmetic(arith) => {
|
||||
self.step_arithmetic(arith);
|
||||
None
|
||||
let slowness = self.step_arithmetic(arith);
|
||||
(None, false, slowness)
|
||||
}
|
||||
Instruction::Jump(jump, offset) => {
|
||||
let overridden = self.step_jump(*jump, *offset);
|
||||
(Some(overridden), false, 0)
|
||||
}
|
||||
Instruction::Jump(jump, offset) => Some(self.step_jump(*jump, *offset)),
|
||||
Instruction::Boolean(boolean) => {
|
||||
self.step_boolean(boolean);
|
||||
None
|
||||
(None, false, 0)
|
||||
}
|
||||
Instruction::Inc(inc) => {
|
||||
self.step_inc(inc);
|
||||
None
|
||||
}
|
||||
Instruction::Ret => {
|
||||
self.step_ret();
|
||||
None
|
||||
(None, false, 0)
|
||||
}
|
||||
Instruction::Ret => (None, true, 0),
|
||||
Instruction::Logic(instruction) => {
|
||||
self.step_logic(instruction);
|
||||
None
|
||||
(None, false, 0)
|
||||
}
|
||||
Instruction::Trivia(_) => None,
|
||||
Instruction::Trivia(_) => (None, false, 0),
|
||||
};
|
||||
|
||||
let mut post = Vec::new();
|
||||
|
||||
let acc_desc = self.registers.diff(&old_registers);
|
||||
if !acc_desc.is_empty() {
|
||||
post.push(acc_desc);
|
||||
}
|
||||
|
||||
match old_ip {
|
||||
None => {}
|
||||
Some(old_ip) => {
|
||||
post.push(format!(
|
||||
"ip:{}->{}",
|
||||
display_small(old_ip),
|
||||
display_small(self.program_counter)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if old_flags != self.flags {
|
||||
post.push(format!("flags:{}->{}", old_flags, self.flags));
|
||||
}
|
||||
|
||||
let post = post.join(" ");
|
||||
|
||||
let instruction = match instruction_override {
|
||||
None => format!("{instruction}"),
|
||||
Some(i) => i,
|
||||
};
|
||||
|
||||
if post.is_empty() {
|
||||
instruction
|
||||
if stop {
|
||||
self.program_counter -= 1; // compensate for pre-incrementing
|
||||
(
|
||||
format!(
|
||||
"STOPONRET: Return encountered at address {}.",
|
||||
self.program_counter
|
||||
),
|
||||
true,
|
||||
)
|
||||
} else {
|
||||
format!("{instruction} ; {post}")
|
||||
let mut post: Vec<String> = Vec::new();
|
||||
|
||||
let (clock_count, clock_description) = match &instruction_override {
|
||||
None => instruction.clock_count(None),
|
||||
Some((_, jumped)) => instruction.clock_count(Some(!*jumped)),
|
||||
};
|
||||
let new_clock = self.clocks_executed + clock_count + slowness as u32;
|
||||
if show_clock {
|
||||
post.push("Clocks:".to_owned());
|
||||
post.push(format!(
|
||||
"+{} = {}",
|
||||
new_clock - self.clocks_executed,
|
||||
new_clock
|
||||
));
|
||||
if !clock_description.is_empty() {
|
||||
post.push(format!(
|
||||
"({}{})",
|
||||
clock_description,
|
||||
if slowness > 0 {
|
||||
format!(" + {slowness}p")
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
))
|
||||
}
|
||||
post.push("|".to_owned());
|
||||
}
|
||||
self.clocks_executed = new_clock;
|
||||
|
||||
let acc_desc = self.registers.diff(&old_registers);
|
||||
if !acc_desc.is_empty() {
|
||||
post.push(acc_desc);
|
||||
}
|
||||
|
||||
match old_ip {
|
||||
None => {}
|
||||
Some(old_ip) => {
|
||||
post.push(format!(
|
||||
"ip:{}->{}",
|
||||
display_small(old_ip),
|
||||
display_small(self.program_counter)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if old_flags != self.flags {
|
||||
post.push(format!("flags:{}->{}", old_flags, self.flags));
|
||||
}
|
||||
|
||||
let post = post.join(" ");
|
||||
|
||||
let instruction = match instruction_override {
|
||||
None => format!("{instruction}"),
|
||||
Some((i, _)) => i,
|
||||
};
|
||||
|
||||
if post.is_empty() {
|
||||
(instruction, false)
|
||||
} else {
|
||||
(format!("{instruction} ; {post}"), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -80,7 +80,7 @@ impl Display for EffectiveAddress {
|
||||
}
|
||||
}
|
||||
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::WithU16((), offset) => f.write_fmt(format_args!("[bx+{}]", offset)),
|
||||
},
|
||||
@@ -306,4 +306,49 @@ impl EffectiveAddress {
|
||||
EffectiveAddress::BasePointerWide(_) => 3,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn clock_count(&self) -> (u32, String) {
|
||||
let count = match self {
|
||||
EffectiveAddress::Sum(WithOffset::Basic((base, source_dest))) => {
|
||||
match (base, source_dest) {
|
||||
(Base::Bx, SourceDest::Source) | (Base::Bp, SourceDest::Dest) => 7,
|
||||
_ => 8,
|
||||
}
|
||||
}
|
||||
EffectiveAddress::Sum(WithOffset::WithU8((base, source_dest), _)) => {
|
||||
match (base, source_dest) {
|
||||
(Base::Bx, SourceDest::Source) | (Base::Bp, SourceDest::Dest) => 11,
|
||||
_ => 12,
|
||||
}
|
||||
}
|
||||
EffectiveAddress::Sum(WithOffset::WithU16((base, source_dest), _)) => {
|
||||
match (base, source_dest) {
|
||||
(Base::Bx, SourceDest::Source) | (Base::Bp, SourceDest::Dest) => 11,
|
||||
_ => 12,
|
||||
}
|
||||
}
|
||||
EffectiveAddress::SpecifiedIn(WithOffset::Basic(_)) => 5,
|
||||
EffectiveAddress::SpecifiedIn(WithOffset::WithU8(_, _)) => 9,
|
||||
EffectiveAddress::SpecifiedIn(WithOffset::WithU16(_, _)) => 9,
|
||||
EffectiveAddress::Bx(WithOffset::Basic(())) => 5,
|
||||
EffectiveAddress::Bx(WithOffset::WithU8((), _)) => 9,
|
||||
EffectiveAddress::Bx(WithOffset::WithU16((), _)) => 9,
|
||||
EffectiveAddress::Direct(_) => 6,
|
||||
EffectiveAddress::BasePointer(i) => {
|
||||
if *i == 0 {
|
||||
5
|
||||
} else {
|
||||
9
|
||||
}
|
||||
}
|
||||
EffectiveAddress::BasePointerWide(i) => {
|
||||
if *i == 0 {
|
||||
5
|
||||
} else {
|
||||
9
|
||||
}
|
||||
}
|
||||
};
|
||||
(count, format!("+ {count}ea"))
|
||||
}
|
||||
}
|
||||
|
@@ -27,4 +27,12 @@ impl IncInstruction {
|
||||
pub fn length(&self) -> u8 {
|
||||
1
|
||||
}
|
||||
|
||||
pub fn clock_count(&self) -> (u32, String) {
|
||||
if self.target.is_wide() {
|
||||
(2, "".to_owned())
|
||||
} else {
|
||||
(3, "".to_owned())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,8 +15,8 @@ use crate::{
|
||||
effective_address::EffectiveAddress,
|
||||
jump_instruction::Jump,
|
||||
move_instruction::{
|
||||
AccumulatorToMemory, ImmediateToMemory, ImmediateToRegister, MemRegMove,
|
||||
MemoryToAccumulator, MemoryToSegment, MoveInstruction, RegMemMove, RegRegMove,
|
||||
AccumulatorToMemory, ImmediateToMemory, ImmediateToRegister, MemToRegMove,
|
||||
MemoryToAccumulator, MemoryToSegment, MoveInstruction, RegRegMove, RegToMemMove,
|
||||
RegisterToSegment, SegmentToMemory, SegmentToRegister,
|
||||
},
|
||||
register::{Register, SegmentRegister},
|
||||
@@ -185,15 +185,19 @@ impl Instruction<i8> {
|
||||
} else {
|
||||
let mem_location = EffectiveAddress::of_mode_rm(mode, rm, bytes);
|
||||
if d == 0 {
|
||||
Some(Instruction::Move(MoveInstruction::RegMemMove(RegMemMove {
|
||||
source: reg,
|
||||
dest: mem_location,
|
||||
})))
|
||||
Some(Instruction::Move(MoveInstruction::RegMemMove(
|
||||
RegToMemMove {
|
||||
source: reg,
|
||||
dest: mem_location,
|
||||
},
|
||||
)))
|
||||
} else {
|
||||
Some(Instruction::Move(MoveInstruction::MemRegMove(MemRegMove {
|
||||
dest: reg,
|
||||
source: mem_location,
|
||||
})))
|
||||
Some(Instruction::Move(MoveInstruction::MemRegMove(
|
||||
MemToRegMove {
|
||||
dest: reg,
|
||||
source: mem_location,
|
||||
},
|
||||
)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -553,6 +557,22 @@ impl<A> Instruction<A> {
|
||||
Instruction::Ret => 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a description (possibly empty) of how this computation was performed.
|
||||
pub fn clock_count(&self, is_jump_success: Option<bool>) -> (u32, String) {
|
||||
match self {
|
||||
Instruction::Move(instr) => instr.clock_count(),
|
||||
Instruction::Arithmetic(instr) => instr.clock_count(),
|
||||
Instruction::Jump(instr, _) => {
|
||||
(instr.clock_count(is_jump_success.unwrap()), "".to_owned())
|
||||
}
|
||||
Instruction::Boolean(instr) => instr.clock_count(),
|
||||
Instruction::Logic(instr) => instr.clock_count(),
|
||||
Instruction::Inc(instr) => instr.clock_count(),
|
||||
Instruction::Ret => (8, "".to_owned()),
|
||||
Instruction::Trivia(_) => (0, "".to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@@ -52,3 +52,53 @@ impl Display for Jump {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Jump {
|
||||
pub fn clock_count(&self, is_success: bool) -> u32 {
|
||||
match self {
|
||||
Jump::Jle
|
||||
| Jump::Jp
|
||||
| Jump::Jo
|
||||
| Jump::Js
|
||||
| Jump::Jne
|
||||
| Jump::Jl
|
||||
| Jump::Jnl
|
||||
| Jump::Jnle
|
||||
| Jump::Jnb
|
||||
| Jump::Jb
|
||||
| Jump::Jbe
|
||||
| Jump::Je
|
||||
| Jump::Jnp
|
||||
| Jump::Jno
|
||||
| Jump::Jns
|
||||
| Jump::Jnbe => {
|
||||
if is_success {
|
||||
4
|
||||
} else {
|
||||
16
|
||||
}
|
||||
}
|
||||
Jump::Loop => {
|
||||
if is_success {
|
||||
5
|
||||
} else {
|
||||
17
|
||||
}
|
||||
}
|
||||
Jump::Loopnz => {
|
||||
if is_success {
|
||||
5
|
||||
} else {
|
||||
19
|
||||
}
|
||||
}
|
||||
Jump::Loopz | Jump::Jcxz => {
|
||||
if is_success {
|
||||
6
|
||||
} else {
|
||||
18
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -56,7 +56,7 @@ pub struct LogicInstruction {
|
||||
impl Display for LogicInstruction {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!(
|
||||
"{} {},{}",
|
||||
"{} {}, {}",
|
||||
self.op,
|
||||
self.target,
|
||||
if self.amount_from_cl { "cl" } else { "1" }
|
||||
@@ -102,4 +102,29 @@ impl LogicInstruction {
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn clock_count(&self) -> (u32, String) {
|
||||
match (&self.op, &self.target) {
|
||||
(LogicInstructionType::Not, LogicTarget::Register(_)) => (3, "".to_owned()),
|
||||
(LogicInstructionType::Not, LogicTarget::Address(addr)) => {
|
||||
let (count, result) = addr.clock_count();
|
||||
(count + 16, format!("16 {result}"))
|
||||
}
|
||||
(_, LogicTarget::Register(_)) => {
|
||||
if self.amount_from_cl {
|
||||
todo!()
|
||||
} else {
|
||||
(2, "".to_owned())
|
||||
}
|
||||
}
|
||||
(_, LogicTarget::Address(addr)) => {
|
||||
if self.amount_from_cl {
|
||||
todo!()
|
||||
} else {
|
||||
let (count, result) = addr.clock_count();
|
||||
(count + 15, format!("15 {result}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -99,12 +99,12 @@ impl RegRegMove {
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Hash, Clone, Arbitrary)]
|
||||
pub struct RegMemMove {
|
||||
pub struct RegToMemMove {
|
||||
pub source: Register,
|
||||
pub dest: EffectiveAddress,
|
||||
}
|
||||
|
||||
impl RegMemMove {
|
||||
impl RegToMemMove {
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut result = Vec::<u8>::with_capacity(2);
|
||||
|
||||
@@ -124,12 +124,12 @@ impl RegMemMove {
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Hash, Clone, Arbitrary)]
|
||||
pub struct MemRegMove {
|
||||
pub struct MemToRegMove {
|
||||
pub source: EffectiveAddress,
|
||||
pub dest: Register,
|
||||
}
|
||||
|
||||
impl MemRegMove {
|
||||
impl MemToRegMove {
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut result = Vec::with_capacity(2);
|
||||
|
||||
@@ -368,9 +368,9 @@ pub enum MoveInstruction {
|
||||
/// Move a value from one register to another
|
||||
RegRegMove(RegRegMove),
|
||||
/// Store a value from a register into memory
|
||||
RegMemMove(RegMemMove),
|
||||
RegMemMove(RegToMemMove),
|
||||
/// Load a value from memory into a register
|
||||
MemRegMove(MemRegMove),
|
||||
MemRegMove(MemToRegMove),
|
||||
/// Load a literal value into a register
|
||||
ImmediateToRegister(ImmediateToRegister),
|
||||
/// Load a literal value into a register or into memory
|
||||
@@ -471,6 +471,41 @@ impl MoveInstruction {
|
||||
MoveInstruction::RegisterToSegment(mov) => mov.length(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clock_count(&self) -> (u32, String) {
|
||||
match self {
|
||||
MoveInstruction::RegRegMove(_) => (2, "".to_owned()),
|
||||
MoveInstruction::RegMemMove(instr) => {
|
||||
let (count, result) = instr.dest.clock_count();
|
||||
(count + 9, format!("9 {result}"))
|
||||
}
|
||||
MoveInstruction::MemRegMove(instr) => {
|
||||
let (count, result) = instr.source.clock_count();
|
||||
(count + 8, format!("8 {result}"))
|
||||
}
|
||||
MoveInstruction::ImmediateToRegister(_) => (4, "".to_owned()),
|
||||
MoveInstruction::ImmediateToMemory(instr) => {
|
||||
let dest = match instr {
|
||||
ImmediateToMemory::Byte(addr, _) => addr,
|
||||
ImmediateToMemory::Word(addr, _) => addr,
|
||||
};
|
||||
let (count, result) = dest.clock_count();
|
||||
(10 + count, format!("10 {result}"))
|
||||
}
|
||||
MoveInstruction::MemoryToAccumulator(_) => (10, "".to_owned()),
|
||||
MoveInstruction::AccumulatorToMemory(_) => (10, "".to_owned()),
|
||||
MoveInstruction::SegmentToMemory(instr) => {
|
||||
let (count, result) = instr.dest.clock_count();
|
||||
(9 + count, format!("9 {result}"))
|
||||
}
|
||||
MoveInstruction::MemoryToSegment(instr) => {
|
||||
let (count, result) = instr.source.clock_count();
|
||||
(9 + count, format!("9, {result}"))
|
||||
}
|
||||
MoveInstruction::SegmentToRegister(_) => (2, "".to_owned()),
|
||||
MoveInstruction::RegisterToSegment(_) => (2, "".to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -478,13 +513,13 @@ mod test_move_instruction {
|
||||
use crate::{
|
||||
effective_address::EffectiveAddress,
|
||||
instruction::Instruction,
|
||||
move_instruction::{MemRegMove, MoveInstruction},
|
||||
move_instruction::{MemToRegMove, MoveInstruction},
|
||||
register::{GeneralRegister, Register, RegisterSubset},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn mem_reg_move_to_bytes() {
|
||||
let i = MoveInstruction::MemRegMove(MemRegMove {
|
||||
let i = MoveInstruction::MemRegMove(MemToRegMove {
|
||||
source: EffectiveAddress::BasePointer(0),
|
||||
dest: Register::General(GeneralRegister::D, RegisterSubset::All),
|
||||
});
|
||||
|
@@ -8,12 +8,22 @@ mod test_computer {
|
||||
.replace("\r\n", "\n")
|
||||
.replace(" ", "")
|
||||
.replace(" \n", "\n");
|
||||
|
||||
// Chop the initial header on trace reports
|
||||
if let Some(index) = s.find(" ---") {
|
||||
s.drain(0..=index);
|
||||
}
|
||||
let first_newline = s.find('\n').unwrap();
|
||||
s.drain(0..=first_newline);
|
||||
|
||||
// Chop the 8088 section on trace reports
|
||||
if let Some(index) = s.find("\n******") {
|
||||
s.drain(index..);
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
fn test_sim<T>(input_bytecode: T, expected_trace: &str, display_ip: bool)
|
||||
fn test_sim<T>(input_bytecode: T, expected_trace: &str, display_ip: bool, show_clock: bool)
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
@@ -41,12 +51,15 @@ mod test_computer {
|
||||
panic!("landed in middle of instruction")
|
||||
}
|
||||
Some(instruction) => {
|
||||
trace.push(computer.step(instruction, display_ip));
|
||||
let (trace_line, stop) = computer.step(instruction, display_ip, show_clock);
|
||||
trace.push(trace_line);
|
||||
if stop {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trace.push("".to_owned());
|
||||
trace.push("Final registers:".to_owned());
|
||||
for line in computer.dump_register_state().lines() {
|
||||
trace.push(line.to_string());
|
||||
@@ -62,10 +75,11 @@ mod test_computer {
|
||||
trace.push(format!(" flags: {}", flags));
|
||||
}
|
||||
|
||||
trace.push("".to_owned());
|
||||
|
||||
let cleaned = clean_trace(expected_trace);
|
||||
let expected = cleaned.lines().collect::<Vec<_>>();
|
||||
let expected = cleaned
|
||||
.lines()
|
||||
.filter(|x| !x.is_empty())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(trace.len(), expected.len());
|
||||
|
||||
@@ -80,7 +94,7 @@ mod test_computer {
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0043_immediate_movs");
|
||||
let expected_trace =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0043_immediate_movs.txt");
|
||||
test_sim(input_bytecode, expected_trace, false)
|
||||
test_sim(input_bytecode, expected_trace, false, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -89,7 +103,7 @@ mod test_computer {
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0044_register_movs");
|
||||
let expected_trace =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0044_register_movs.txt");
|
||||
test_sim(input_bytecode, expected_trace, false)
|
||||
test_sim(input_bytecode, expected_trace, false, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -100,7 +114,7 @@ mod test_computer {
|
||||
let expected_trace = include_str!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs.txt"
|
||||
);
|
||||
test_sim(input_bytecode, expected_trace, false)
|
||||
test_sim(input_bytecode, expected_trace, false, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -109,7 +123,7 @@ mod test_computer {
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0046_add_sub_cmp");
|
||||
let expected_trace =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0046_add_sub_cmp.txt");
|
||||
test_sim(input_bytecode, expected_trace, false)
|
||||
test_sim(input_bytecode, expected_trace, false, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -118,7 +132,7 @@ mod test_computer {
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0047_challenge_flags");
|
||||
let expected_trace =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0047_challenge_flags.txt");
|
||||
test_sim(input_bytecode, expected_trace, false)
|
||||
test_sim(input_bytecode, expected_trace, false, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -127,7 +141,7 @@ mod test_computer {
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0048_ip_register");
|
||||
let expected_trace =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0048_ip_register.txt");
|
||||
test_sim(input_bytecode, expected_trace, true)
|
||||
test_sim(input_bytecode, expected_trace, true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -137,7 +151,7 @@ mod test_computer {
|
||||
let expected_trace = include_str!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0049_conditional_jumps.txt"
|
||||
);
|
||||
test_sim(input_bytecode, expected_trace, true)
|
||||
test_sim(input_bytecode, expected_trace, true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -146,7 +160,7 @@ mod test_computer {
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0050_challenge_jumps");
|
||||
let expected_trace =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0050_challenge_jumps.txt");
|
||||
test_sim(input_bytecode, expected_trace, true)
|
||||
test_sim(input_bytecode, expected_trace, true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -155,7 +169,7 @@ mod test_computer {
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0051_memory_mov");
|
||||
let expected_trace =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0051_memory_mov.txt");
|
||||
test_sim(input_bytecode, expected_trace, true)
|
||||
test_sim(input_bytecode, expected_trace, true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -164,7 +178,7 @@ mod test_computer {
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0052_memory_add_loop");
|
||||
let expected_trace =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0052_memory_add_loop.txt");
|
||||
test_sim(input_bytecode, expected_trace, true)
|
||||
test_sim(input_bytecode, expected_trace, true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -175,7 +189,7 @@ mod test_computer {
|
||||
let expected_trace = include_str!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0053_add_loop_challenge.txt"
|
||||
);
|
||||
test_sim(input_bytecode, expected_trace, true)
|
||||
test_sim(input_bytecode, expected_trace, true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -184,7 +198,7 @@ mod test_computer {
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0054_draw_rectangle");
|
||||
let expected_trace =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0054_draw_rectangle.txt");
|
||||
test_sim(input_bytecode, expected_trace, true)
|
||||
test_sim(input_bytecode, expected_trace, true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -195,6 +209,80 @@ mod test_computer {
|
||||
let expected_trace = include_str!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0055_challenge_rectangle.txt"
|
||||
);
|
||||
test_sim(input_bytecode, expected_trace, true)
|
||||
test_sim(input_bytecode, expected_trace, true, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_estimating_cycles() {
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0056_estimating_cycles");
|
||||
let expected_trace = include_str!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0056_estimating_cycles.txt"
|
||||
);
|
||||
test_sim(input_bytecode, expected_trace, true, true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_challenge_cycles() {
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0057_challenge_cycles");
|
||||
let expected_trace = include_str!(
|
||||
"../../computer_enhance/perfaware/part1/listing_0057_challenge_cycles.txt"
|
||||
);
|
||||
test_sim(input_bytecode, expected_trace, true, true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_scalar() {
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0059_SingleScalar");
|
||||
let expected_trace =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0059_SingleScalar.txt");
|
||||
test_sim(input_bytecode, expected_trace, true, true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unroll2_scalar() {
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0060_Unroll2Scalar");
|
||||
let expected_trace =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0060_Unroll2Scalar.txt");
|
||||
test_sim(input_bytecode, expected_trace, true, true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dual_scalar() {
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0061_DualScalar");
|
||||
let expected_trace =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0061_DualScalar.txt");
|
||||
test_sim(input_bytecode, expected_trace, true, true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quad_scalar() {
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0062_QuadScalar");
|
||||
let expected_trace =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0062_QuadScalar.txt");
|
||||
test_sim(input_bytecode, expected_trace, true, true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quad_scalar_ptr() {
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0063_QuadScalarPtr");
|
||||
let expected_trace =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0063_QuadScalarPtr.txt");
|
||||
test_sim(input_bytecode, expected_trace, true, true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tree_scalar_ptr() {
|
||||
let input_bytecode =
|
||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0064_TreeScalarPtr");
|
||||
let expected_trace =
|
||||
include_str!("../../computer_enhance/perfaware/part1/listing_0064_TreeScalarPtr.txt");
|
||||
test_sim(input_bytecode, expected_trace, true, true)
|
||||
}
|
||||
}
|
||||
|
@@ -126,7 +126,7 @@ fn run(bytecode: Vec<u8>) -> Computer {
|
||||
panic!("landed in middle of instruction")
|
||||
}
|
||||
Some(instruction) => {
|
||||
computer.step(instruction, false);
|
||||
computer.step(instruction, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user