Count clocks (#19)
This commit is contained in:
@@ -32,17 +32,28 @@ impl Display for ArithmeticInstruction {
|
|||||||
ArithmeticInstructionSelect::RegisterToRegister(inst) => {
|
ArithmeticInstructionSelect::RegisterToRegister(inst) => {
|
||||||
f.write_fmt(format_args!("{}, {}", inst.dest, inst.source))
|
f.write_fmt(format_args!("{}, {}", inst.dest, inst.source))
|
||||||
}
|
}
|
||||||
ArithmeticInstructionSelect::RegisterToMemory(inst) => {
|
ArithmeticInstructionSelect::RegisterToMemory(inst) => f.write_fmt(format_args!(
|
||||||
f.write_fmt(format_args!("{}, {}", inst.dest, inst.source))
|
"{} {}, {}",
|
||||||
}
|
if inst.source.is_wide() {
|
||||||
|
"word"
|
||||||
|
} else {
|
||||||
|
"byte"
|
||||||
|
},
|
||||||
|
inst.dest,
|
||||||
|
inst.source
|
||||||
|
)),
|
||||||
ArithmeticInstructionSelect::MemoryToRegister(inst) => {
|
ArithmeticInstructionSelect::MemoryToRegister(inst) => {
|
||||||
f.write_fmt(format_args!("{}, {}", inst.dest, inst.source))
|
f.write_fmt(format_args!("{}, {}", inst.dest, inst.source))
|
||||||
}
|
}
|
||||||
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryByte(addr, data, _) => {
|
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryByte(addr, data, is_wide) => f
|
||||||
f.write_fmt(format_args!("{}, {}", addr, data))
|
.write_fmt(format_args!(
|
||||||
}
|
"{} {}, {}",
|
||||||
|
if *is_wide { "word" } else { "byte" },
|
||||||
|
addr,
|
||||||
|
data
|
||||||
|
)),
|
||||||
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryWord(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) => {
|
ArithmeticInstructionSelect::ImmediateToRegisterByte(addr, data, signed) => {
|
||||||
if *signed {
|
if *signed {
|
||||||
@@ -175,6 +186,89 @@ impl ArithmeticInstruction {
|
|||||||
ArithmeticInstructionSelect::ImmediateToAccWord(_) => 3,
|
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)]
|
#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash, Arbitrary)]
|
||||||
|
@@ -26,8 +26,8 @@ use crate::{
|
|||||||
instruction::Instruction,
|
instruction::Instruction,
|
||||||
jump_instruction::Jump,
|
jump_instruction::Jump,
|
||||||
move_instruction::{
|
move_instruction::{
|
||||||
AccumulatorToMemory, ImmediateToMemory, ImmediateToRegister, MemRegMove,
|
AccumulatorToMemory, ImmediateToMemory, ImmediateToRegister, MemToRegMove,
|
||||||
MemoryToAccumulator, MemoryToSegment, MoveInstruction, RegMemMove, RegRegMove,
|
MemoryToAccumulator, MemoryToSegment, MoveInstruction, RegRegMove, RegToMemMove,
|
||||||
RegisterToSegment, SegmentToMemory, SegmentToRegister,
|
RegisterToSegment, SegmentToMemory, SegmentToRegister,
|
||||||
},
|
},
|
||||||
program::Program,
|
program::Program,
|
||||||
@@ -475,7 +475,7 @@ fn seg_to_reg_move_instruction(input: &str) -> IResult<&str, SegmentToRegister>
|
|||||||
)(input)
|
)(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reg_mem_move_instruction(input: &str) -> IResult<&str, RegMemMove> {
|
fn reg_mem_move_instruction(input: &str) -> IResult<&str, RegToMemMove> {
|
||||||
map_res(
|
map_res(
|
||||||
preceded(
|
preceded(
|
||||||
ws(tag("mov ")),
|
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()) {
|
|((tag, address), register)| match (tag, register.is_wide()) {
|
||||||
(OffsetTag::Word, false) => Err(()),
|
(OffsetTag::Word, false) => Err(()),
|
||||||
_ => Ok::<_, ()>(RegMemMove {
|
_ => Ok::<_, ()>(RegToMemMove {
|
||||||
dest: address,
|
dest: address,
|
||||||
source: register,
|
source: register,
|
||||||
}),
|
}),
|
||||||
@@ -491,7 +491,7 @@ fn reg_mem_move_instruction(input: &str) -> IResult<&str, RegMemMove> {
|
|||||||
)(input)
|
)(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mem_reg_move_instruction(input: &str) -> IResult<&str, MemRegMove> {
|
fn mem_reg_move_instruction(input: &str) -> IResult<&str, MemToRegMove> {
|
||||||
map_res(
|
map_res(
|
||||||
preceded(
|
preceded(
|
||||||
ws(tag("mov ")),
|
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()) {
|
|(register, (tag, address))| match (tag, register.is_wide()) {
|
||||||
(OffsetTag::Word, false) => Err(()),
|
(OffsetTag::Word, false) => Err(()),
|
||||||
_ => Ok::<_, ()>(MemRegMove {
|
_ => Ok::<_, ()>(MemToRegMove {
|
||||||
dest: register,
|
dest: register,
|
||||||
source: address,
|
source: address,
|
||||||
}),
|
}),
|
||||||
|
@@ -163,4 +163,25 @@ impl BooleanInstruction {
|
|||||||
BooleanInstructionDestination::RegReg(_) => 2,
|
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 std::fmt::Display;
|
||||||
|
|
||||||
use crate::boolean_instruction::BooleanInstruction;
|
use crate::boolean_instruction::{
|
||||||
|
BooleanInstruction, BooleanInstructionDestination, BooleanInstructionType, ImmediateToAcc,
|
||||||
|
};
|
||||||
use crate::inc_instruction::IncInstruction;
|
use crate::inc_instruction::IncInstruction;
|
||||||
use crate::logic_instruction::LogicInstruction;
|
use crate::logic_instruction::{LogicInstruction, LogicInstructionType, LogicTarget};
|
||||||
use crate::{
|
use crate::{
|
||||||
arithmetic_instruction::{
|
arithmetic_instruction::{
|
||||||
ArithmeticInstruction, ArithmeticInstructionSelect, ArithmeticOperation,
|
ArithmeticInstruction, ArithmeticInstructionSelect, ArithmeticOperation,
|
||||||
@@ -249,11 +251,13 @@ pub struct Computer {
|
|||||||
registers: Registers,
|
registers: Registers,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
memory: [u8; 65536],
|
memory: [u8; 65536],
|
||||||
|
clocks_executed: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ResultFlags {
|
struct ResultFlags {
|
||||||
auxiliary_carry: bool,
|
auxiliary_carry: bool,
|
||||||
overflow: bool,
|
overflow: bool,
|
||||||
|
sign: bool,
|
||||||
should_write: bool,
|
should_write: bool,
|
||||||
carry: bool,
|
carry: bool,
|
||||||
}
|
}
|
||||||
@@ -279,6 +283,7 @@ impl Computer {
|
|||||||
},
|
},
|
||||||
flags: Flags { fields: 0 },
|
flags: Flags { fields: 0 },
|
||||||
program_counter: 0,
|
program_counter: 0,
|
||||||
|
clocks_executed: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,12 +351,7 @@ impl Computer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_register(&mut self, r: &Register, value: u16) -> String {
|
fn set_register(&mut self, r: &Register, value: u16) {
|
||||||
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);
|
|
||||||
match r {
|
match r {
|
||||||
Register::General(GeneralRegister::A, RegisterSubset::All) => self.registers.a = value,
|
Register::General(GeneralRegister::A, RegisterSubset::All) => self.registers.a = value,
|
||||||
Register::General(GeneralRegister::B, RegisterSubset::All) => self.registers.b = 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::SourceIndex) => self.registers.si = value,
|
||||||
Register::Special(SpecialRegister::DestIndex) => self.registers.di = 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 {
|
fn set_segment(&mut self, r: SegmentRegister, value: u16) {
|
||||||
let was = self.get_segment(r);
|
|
||||||
match r {
|
match r {
|
||||||
SegmentRegister::Code => self.registers.cs = value,
|
SegmentRegister::Code => self.registers.cs = value,
|
||||||
SegmentRegister::Data => self.registers.ds = value,
|
SegmentRegister::Data => self.registers.ds = value,
|
||||||
SegmentRegister::Stack => self.registers.ss = value,
|
SegmentRegister::Stack => self.registers.ss = value,
|
||||||
SegmentRegister::Extra => self.registers.es = 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 {
|
fn get_memory_byte(&self, index: usize) -> u8 {
|
||||||
self.memory[index]
|
self.memory[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_memory_word(&self, index: usize) -> u16 {
|
/// Returns true if the access took an extra four cycles.
|
||||||
self.memory[index] as u16 + self.memory[index + 1] as u16 * 256
|
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;
|
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] = (value % 256) as u8;
|
||||||
self.memory[index + 1] = (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 {
|
fn get_base_offset(&self, base: &Base) -> u16 {
|
||||||
@@ -536,38 +524,48 @@ impl Computer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step_mov(&mut self, instruction: &MoveInstruction) -> String {
|
/// Returns true if the access took four extra clocks.
|
||||||
let preamble = format!("{}", instruction);
|
fn step_mov(&mut self, instruction: &MoveInstruction) -> bool {
|
||||||
let description = match &instruction {
|
match &instruction {
|
||||||
MoveInstruction::RegRegMove(mov) => {
|
MoveInstruction::RegRegMove(mov) => {
|
||||||
let value = self.get_register(&mov.source);
|
let value = self.get_register(&mov.source);
|
||||||
self.set_register(&mov.dest, value)
|
self.set_register(&mov.dest, value);
|
||||||
|
false
|
||||||
}
|
}
|
||||||
MoveInstruction::RegMemMove(mov) => {
|
MoveInstruction::RegMemMove(mov) => {
|
||||||
let value = self.get_register(&mov.source);
|
let value = self.get_register(&mov.source);
|
||||||
if mov.source.is_wide() {
|
if mov.source.is_wide() {
|
||||||
self.set_memory_word(self.resolve_eaddr(&mov.dest), value)
|
self.set_memory_word(self.resolve_eaddr(&mov.dest), value)
|
||||||
} else {
|
} 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) => {
|
MoveInstruction::MemRegMove(mov) => {
|
||||||
if mov.dest.is_wide() {
|
if mov.dest.is_wide() {
|
||||||
let value = self.get_memory_word(self.resolve_eaddr(&mov.source));
|
let (value, slow) = self.get_memory_word(self.resolve_eaddr(&mov.source));
|
||||||
self.set_register(&mov.dest, value)
|
self.set_register(&mov.dest, value);
|
||||||
|
slow
|
||||||
} else {
|
} else {
|
||||||
let value = self.get_memory_byte(self.resolve_eaddr(&mov.source));
|
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 {
|
MoveInstruction::ImmediateToRegister(mov) => {
|
||||||
ImmediateToRegister::Byte(dest, value) => self.set_register(dest, *value as u16),
|
match mov {
|
||||||
|
ImmediateToRegister::Byte(dest, value) => {
|
||||||
|
self.set_register(dest, *value as u16)
|
||||||
|
}
|
||||||
ImmediateToRegister::Wide(dest, value) => self.set_register(dest, *value),
|
ImmediateToRegister::Wide(dest, value) => self.set_register(dest, *value),
|
||||||
},
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
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_word(dest, *value as u16)
|
self.set_memory_byte(dest, *value);
|
||||||
|
false
|
||||||
}
|
}
|
||||||
ImmediateToMemory::Word(addr, value) => {
|
ImmediateToMemory::Word(addr, value) => {
|
||||||
let dest = self.resolve_eaddr(addr);
|
let dest = self.resolve_eaddr(addr);
|
||||||
@@ -576,10 +574,12 @@ impl Computer {
|
|||||||
},
|
},
|
||||||
MoveInstruction::MemoryToAccumulator(mov) => {
|
MoveInstruction::MemoryToAccumulator(mov) => {
|
||||||
if mov.is_wide {
|
if mov.is_wide {
|
||||||
|
let (value, slow) = self.get_memory_word(mov.address as usize);
|
||||||
self.set_register(
|
self.set_register(
|
||||||
&Register::General(GeneralRegister::A, RegisterSubset::All),
|
&Register::General(GeneralRegister::A, RegisterSubset::All),
|
||||||
self.get_memory_word(mov.address as usize),
|
value,
|
||||||
)
|
);
|
||||||
|
slow
|
||||||
} else {
|
} else {
|
||||||
self.set_register(
|
self.set_register(
|
||||||
&Register::General(
|
&Register::General(
|
||||||
@@ -587,7 +587,8 @@ impl Computer {
|
|||||||
RegisterSubset::Subset(ByteRegisterSubset::Low),
|
RegisterSubset::Subset(ByteRegisterSubset::Low),
|
||||||
),
|
),
|
||||||
self.get_memory_byte(mov.address as usize) as u16,
|
self.get_memory_byte(mov.address as usize) as u16,
|
||||||
)
|
);
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MoveInstruction::AccumulatorToMemory(mov) => {
|
MoveInstruction::AccumulatorToMemory(mov) => {
|
||||||
@@ -606,7 +607,8 @@ impl Computer {
|
|||||||
GeneralRegister::A,
|
GeneralRegister::A,
|
||||||
RegisterSubset::Subset(ByteRegisterSubset::Low),
|
RegisterSubset::Subset(ByteRegisterSubset::Low),
|
||||||
)) as u8,
|
)) as u8,
|
||||||
)
|
);
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MoveInstruction::SegmentToMemory(mov) => {
|
MoveInstruction::SegmentToMemory(mov) => {
|
||||||
@@ -615,30 +617,138 @@ impl Computer {
|
|||||||
self.set_memory_word(dest, v)
|
self.set_memory_word(dest, v)
|
||||||
}
|
}
|
||||||
MoveInstruction::MemoryToSegment(mov) => {
|
MoveInstruction::MemoryToSegment(mov) => {
|
||||||
let value = self.get_memory_word(self.resolve_eaddr(&mov.source));
|
let (value, slow) = self.get_memory_word(self.resolve_eaddr(&mov.source));
|
||||||
self.set_segment(mov.dest, value)
|
self.set_segment(mov.dest, value);
|
||||||
|
slow
|
||||||
}
|
}
|
||||||
MoveInstruction::SegmentToRegister(mov) => {
|
MoveInstruction::SegmentToRegister(mov) => {
|
||||||
let v = self.get_segment(mov.source);
|
let v = self.get_segment(mov.source);
|
||||||
self.set_register(&mov.dest, v)
|
self.set_register(&mov.dest, v);
|
||||||
|
false
|
||||||
}
|
}
|
||||||
MoveInstruction::RegisterToSegment(mov) => {
|
MoveInstruction::RegisterToSegment(mov) => {
|
||||||
let value = self.get_register(&mov.source);
|
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
|
fn apply_u8(op: ArithmeticOperation, to_arg: u8, incoming_arg_raw: u8) -> (u8, ResultFlags) {
|
||||||
/// to be written to the destination (so `cmp` returns false), and true if there
|
let incoming_arg = if incoming_arg_raw >= 128 {
|
||||||
/// was a carry out of the low-order nibble.
|
u8::MAX - (255 - incoming_arg_raw)
|
||||||
fn apply(
|
} 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,
|
op: ArithmeticOperation,
|
||||||
to_arg: u16,
|
to_arg: u16,
|
||||||
incoming_arg_raw: u16,
|
incoming_arg_raw: u16,
|
||||||
@@ -654,14 +764,16 @@ impl Computer {
|
|||||||
match op {
|
match op {
|
||||||
ArithmeticOperation::Add => {
|
ArithmeticOperation::Add => {
|
||||||
let result = to_arg as u32 + incoming_arg as u32;
|
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 {
|
let result_flags = ResultFlags {
|
||||||
overflow: (to_arg >= 1 << 15 && incoming_arg >= 1 << 15 && result < 1 << 15)
|
overflow: (to_arg >= 1 << 15 && incoming_arg >= 1 << 15 && result < 1 << 15)
|
||||||
|| (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,
|
auxiliary_carry: (to_arg % 16) + (incoming_arg_raw % 16) >= 16,
|
||||||
should_write: true,
|
should_write: true,
|
||||||
carry: result > u16::MAX as u32,
|
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 => (
|
ArithmeticOperation::Or => (
|
||||||
to_arg | incoming_arg,
|
to_arg | incoming_arg,
|
||||||
@@ -670,6 +782,7 @@ impl Computer {
|
|||||||
should_write: true,
|
should_write: true,
|
||||||
carry: false,
|
carry: false,
|
||||||
auxiliary_carry: false,
|
auxiliary_carry: false,
|
||||||
|
sign: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ArithmeticOperation::AddWithCarry => todo!(),
|
ArithmeticOperation::AddWithCarry => todo!(),
|
||||||
@@ -681,6 +794,7 @@ impl Computer {
|
|||||||
should_write: true,
|
should_write: true,
|
||||||
auxiliary_carry: false,
|
auxiliary_carry: false,
|
||||||
carry: true,
|
carry: true,
|
||||||
|
sign: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ArithmeticOperation::Sub => {
|
ArithmeticOperation::Sub => {
|
||||||
@@ -694,6 +808,7 @@ impl Computer {
|
|||||||
overflow: (result < 1 << 15 && to_arg >= 1 << 15),
|
overflow: (result < 1 << 15 && to_arg >= 1 << 15),
|
||||||
auxiliary_carry,
|
auxiliary_carry,
|
||||||
carry: true,
|
carry: true,
|
||||||
|
sign: result > 1 << 15,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@@ -706,6 +821,7 @@ impl Computer {
|
|||||||
overflow: (result < 1 << 15 && to_arg >= 1 << 15),
|
overflow: (result < 1 << 15 && to_arg >= 1 << 15),
|
||||||
auxiliary_carry,
|
auxiliary_carry,
|
||||||
carry: false,
|
carry: false,
|
||||||
|
sign: result > 1 << 15,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -717,6 +833,7 @@ impl Computer {
|
|||||||
should_write: true,
|
should_write: true,
|
||||||
auxiliary_carry: false,
|
auxiliary_carry: false,
|
||||||
carry: false,
|
carry: false,
|
||||||
|
sign: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ArithmeticOperation::Cmp => {
|
ArithmeticOperation::Cmp => {
|
||||||
@@ -731,6 +848,7 @@ impl Computer {
|
|||||||
auxiliary_carry,
|
auxiliary_carry,
|
||||||
overflow: (result < 1 << 15 && to_arg >= 1 << 15),
|
overflow: (result < 1 << 15 && to_arg >= 1 << 15),
|
||||||
carry: true,
|
carry: true,
|
||||||
|
sign: result > 1 << 15,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@@ -742,6 +860,7 @@ impl Computer {
|
|||||||
auxiliary_carry,
|
auxiliary_carry,
|
||||||
overflow: (result < 1 << 15 && to_arg >= 1 << 15),
|
overflow: (result < 1 << 15 && to_arg >= 1 << 15),
|
||||||
carry: false,
|
carry: false,
|
||||||
|
sign: result > 1 << 15,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -762,37 +881,91 @@ impl Computer {
|
|||||||
parity % 2 == 1
|
parity % 2 == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step_arithmetic(&mut self, instruction: &ArithmeticInstruction) -> String {
|
/// Returns a count of the extra cycles caused by misaligned memory access.
|
||||||
let (source, old_value, new_value, flags) = match &instruction.instruction {
|
fn step_arithmetic(&mut self, instruction: &ArithmeticInstruction) -> u8 {
|
||||||
|
let mut slowness = 0;
|
||||||
|
|
||||||
|
let (new_value, flags) = match &instruction.instruction {
|
||||||
ArithmeticInstructionSelect::RegisterToRegister(instr) => {
|
ArithmeticInstructionSelect::RegisterToRegister(instr) => {
|
||||||
let current_value = self.get_register(&instr.dest);
|
let current_value = self.get_register(&instr.dest);
|
||||||
let incoming_value = self.get_register(&instr.source);
|
let incoming_value = self.get_register(&instr.source);
|
||||||
|
if instr.dest.is_wide() {
|
||||||
let (new_value, flags) =
|
let (new_value, flags) =
|
||||||
Self::apply(instruction.op, current_value, incoming_value, false);
|
Self::apply_u16(instruction.op, current_value, incoming_value, false);
|
||||||
if flags.should_write {
|
if flags.should_write {
|
||||||
self.set_register(&instr.dest, new_value);
|
self.set_register(&instr.dest, new_value);
|
||||||
}
|
}
|
||||||
(format!("{}", instr.dest), current_value, new_value, flags)
|
(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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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::RegisterToMemory(_) => todo!(),
|
|
||||||
ArithmeticInstructionSelect::MemoryToRegister(instr) => {
|
ArithmeticInstructionSelect::MemoryToRegister(instr) => {
|
||||||
let current_value = self.get_register(&instr.dest);
|
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))
|
self.get_memory_word(self.resolve_eaddr(&instr.source))
|
||||||
} else {
|
} 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 {
|
if flags.should_write {
|
||||||
self.set_register(&instr.dest, new_value);
|
self.set_register(&instr.dest, new_value);
|
||||||
}
|
}
|
||||||
(format!("{}", instr.dest), current_value, new_value, flags)
|
(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 {
|
||||||
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)
|
// Self::apply(instruction.op, current_value, *value as u16)
|
||||||
} else {
|
} else {
|
||||||
todo!()
|
todo!()
|
||||||
@@ -800,47 +973,68 @@ impl Computer {
|
|||||||
if flags.should_write {
|
if flags.should_write {
|
||||||
self.set_register(register, new_value);
|
self.set_register(register, new_value);
|
||||||
}
|
}
|
||||||
(format!("{}", register), current_value, new_value, flags)
|
(new_value, flags)
|
||||||
}
|
}
|
||||||
ArithmeticInstructionSelect::ImmediateToRegisterWord(register, value, is_extended) => {
|
ArithmeticInstructionSelect::ImmediateToRegisterWord(register, value, is_extended) => {
|
||||||
|
// TODO: why have we not used is_extended
|
||||||
let current_value = self.get_register(register);
|
let current_value = self.get_register(register);
|
||||||
let (new_value, flags) =
|
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 {
|
if flags.should_write {
|
||||||
self.set_register(register, new_value);
|
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::ImmediateToRegisterOrMemoryWord(_, _) => todo!(),
|
||||||
ArithmeticInstructionSelect::ImmediateToAccByte(value) => {
|
ArithmeticInstructionSelect::ImmediateToAccByte(value) => {
|
||||||
let reg = Register::General(
|
let reg = Register::General(
|
||||||
GeneralRegister::A,
|
GeneralRegister::A,
|
||||||
RegisterSubset::Subset(ByteRegisterSubset::Low),
|
RegisterSubset::Subset(ByteRegisterSubset::Low),
|
||||||
);
|
);
|
||||||
let current_value = self.get_register(®);
|
let current_value = self.get_register(®) as u8;
|
||||||
let (new_value, flags) =
|
let (new_value, flags) = Self::apply_u8(instruction.op, current_value, *value);
|
||||||
Self::apply(instruction.op, current_value, *value as u16, false);
|
|
||||||
if flags.should_write {
|
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) => {
|
ArithmeticInstructionSelect::ImmediateToAccWord(value) => {
|
||||||
let reg = Register::General(GeneralRegister::A, RegisterSubset::All);
|
let reg = Register::General(GeneralRegister::A, RegisterSubset::All);
|
||||||
let current_value = self.get_register(®);
|
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 {
|
if flags.should_write {
|
||||||
self.set_register(®, new_value);
|
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::Zero), new_value == 0);
|
||||||
self.set_flag(Flag::Status(StatusFlag::Overflow), flags.overflow);
|
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), flags.sign);
|
||||||
self.set_flag(Flag::Status(StatusFlag::Sign), new_value & 0x8000 > 0);
|
|
||||||
self.set_flag(
|
self.set_flag(
|
||||||
Flag::Status(StatusFlag::AuxiliaryCarry),
|
Flag::Status(StatusFlag::AuxiliaryCarry),
|
||||||
flags.auxiliary_carry,
|
flags.auxiliary_carry,
|
||||||
@@ -850,22 +1044,75 @@ impl Computer {
|
|||||||
Flag::Status(StatusFlag::Parity),
|
Flag::Status(StatusFlag::Parity),
|
||||||
!Self::is_odd_parity(new_value % 256),
|
!Self::is_odd_parity(new_value % 256),
|
||||||
);
|
);
|
||||||
let result_desc = if flags.should_write && old_value != new_value {
|
|
||||||
format!(
|
slowness
|
||||||
" {}:{}->{}",
|
|
||||||
source,
|
|
||||||
display_small(old_value),
|
|
||||||
display_small(new_value)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
"".to_owned()
|
|
||||||
};
|
|
||||||
format!("{instruction} ;{result_desc}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step_boolean(&mut self, _instruction: &BooleanInstruction) -> String {
|
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!()
|
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) {
|
fn step_inc(&mut self, instruction: &IncInstruction) {
|
||||||
let old_value = self.get_register(&instruction.target);
|
let old_value = self.get_register(&instruction.target);
|
||||||
@@ -885,18 +1132,101 @@ impl Computer {
|
|||||||
old_value - 1
|
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);
|
self.set_register(&instruction.target, new_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step_ret(&mut self) -> String {
|
#[allow(dead_code)]
|
||||||
todo!()
|
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) {
|
/// 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!()
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
fn step_jump(&mut self, jump: Jump, offset: i8) -> String {
|
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!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
let should_jump = match jump {
|
||||||
Jump::Je => self.flags.get(Flag::Status(StatusFlag::Zero)),
|
Jump::Je => self.flags.get(Flag::Status(StatusFlag::Zero)),
|
||||||
Jump::Jl => {
|
Jump::Jl => {
|
||||||
@@ -966,11 +1296,14 @@ impl Computer {
|
|||||||
self.program_counter = (self.program_counter as i32 + offset as i32) as u16;
|
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.
|
// In NASM, the dollar sign is an offset *without* including the bytes of the jump.
|
||||||
|
(
|
||||||
format!(
|
format!(
|
||||||
"{} ${}{}",
|
"{} ${}{}",
|
||||||
jump,
|
jump,
|
||||||
if offset > 0 { "+" } else { "" },
|
if offset > 0 { "+" } else { "" },
|
||||||
offset + 2,
|
offset + 2,
|
||||||
|
),
|
||||||
|
should_jump,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -978,8 +1311,13 @@ impl Computer {
|
|||||||
self.program_counter
|
self.program_counter
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a string representation of what happened.
|
/// 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) -> String {
|
pub fn step(
|
||||||
|
&mut self,
|
||||||
|
instruction: &Instruction<i8>,
|
||||||
|
display_ip: bool,
|
||||||
|
show_clock: bool,
|
||||||
|
) -> (String, bool) {
|
||||||
let advance = instruction.length();
|
let advance = instruction.length();
|
||||||
let old_ip = if display_ip {
|
let old_ip = if display_ip {
|
||||||
Some(self.program_counter)
|
Some(self.program_counter)
|
||||||
@@ -991,36 +1329,73 @@ impl Computer {
|
|||||||
|
|
||||||
self.program_counter += advance as u16;
|
self.program_counter += advance as u16;
|
||||||
|
|
||||||
let instruction_override = match instruction {
|
let (instruction_override, stop, slowness) = match instruction {
|
||||||
Instruction::Move(mov) => {
|
Instruction::Move(mov) => {
|
||||||
self.step_mov(mov);
|
let slow = self.step_mov(mov);
|
||||||
None
|
(None, false, if slow { 4 } else { 0 })
|
||||||
}
|
}
|
||||||
Instruction::Arithmetic(arith) => {
|
Instruction::Arithmetic(arith) => {
|
||||||
self.step_arithmetic(arith);
|
let slowness = self.step_arithmetic(arith);
|
||||||
None
|
(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) => {
|
Instruction::Boolean(boolean) => {
|
||||||
self.step_boolean(boolean);
|
self.step_boolean(boolean);
|
||||||
None
|
(None, false, 0)
|
||||||
}
|
}
|
||||||
Instruction::Inc(inc) => {
|
Instruction::Inc(inc) => {
|
||||||
self.step_inc(inc);
|
self.step_inc(inc);
|
||||||
None
|
(None, false, 0)
|
||||||
}
|
|
||||||
Instruction::Ret => {
|
|
||||||
self.step_ret();
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
Instruction::Ret => (None, true, 0),
|
||||||
Instruction::Logic(instruction) => {
|
Instruction::Logic(instruction) => {
|
||||||
self.step_logic(instruction);
|
self.step_logic(instruction);
|
||||||
None
|
(None, false, 0)
|
||||||
}
|
}
|
||||||
Instruction::Trivia(_) => None,
|
Instruction::Trivia(_) => (None, false, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut post = Vec::new();
|
if stop {
|
||||||
|
self.program_counter -= 1; // compensate for pre-incrementing
|
||||||
|
(
|
||||||
|
format!(
|
||||||
|
"STOPONRET: Return encountered at address {}.",
|
||||||
|
self.program_counter
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
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);
|
let acc_desc = self.registers.diff(&old_registers);
|
||||||
if !acc_desc.is_empty() {
|
if !acc_desc.is_empty() {
|
||||||
@@ -1046,13 +1421,14 @@ impl Computer {
|
|||||||
|
|
||||||
let instruction = match instruction_override {
|
let instruction = match instruction_override {
|
||||||
None => format!("{instruction}"),
|
None => format!("{instruction}"),
|
||||||
Some(i) => i,
|
Some((i, _)) => i,
|
||||||
};
|
};
|
||||||
|
|
||||||
if post.is_empty() {
|
if post.is_empty() {
|
||||||
instruction
|
(instruction, false)
|
||||||
} else {
|
} else {
|
||||||
format!("{instruction} ; {post}")
|
(format!("{instruction} ; {post}"), false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -80,7 +80,7 @@ impl Display for EffectiveAddress {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
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)),
|
||||||
},
|
},
|
||||||
@@ -306,4 +306,49 @@ impl EffectiveAddress {
|
|||||||
EffectiveAddress::BasePointerWide(_) => 3,
|
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 {
|
pub fn length(&self) -> u8 {
|
||||||
1
|
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,
|
effective_address::EffectiveAddress,
|
||||||
jump_instruction::Jump,
|
jump_instruction::Jump,
|
||||||
move_instruction::{
|
move_instruction::{
|
||||||
AccumulatorToMemory, ImmediateToMemory, ImmediateToRegister, MemRegMove,
|
AccumulatorToMemory, ImmediateToMemory, ImmediateToRegister, MemToRegMove,
|
||||||
MemoryToAccumulator, MemoryToSegment, MoveInstruction, RegMemMove, RegRegMove,
|
MemoryToAccumulator, MemoryToSegment, MoveInstruction, RegRegMove, RegToMemMove,
|
||||||
RegisterToSegment, SegmentToMemory, SegmentToRegister,
|
RegisterToSegment, SegmentToMemory, SegmentToRegister,
|
||||||
},
|
},
|
||||||
register::{Register, SegmentRegister},
|
register::{Register, SegmentRegister},
|
||||||
@@ -185,15 +185,19 @@ impl Instruction<i8> {
|
|||||||
} else {
|
} else {
|
||||||
let mem_location = EffectiveAddress::of_mode_rm(mode, rm, bytes);
|
let mem_location = EffectiveAddress::of_mode_rm(mode, rm, bytes);
|
||||||
if d == 0 {
|
if d == 0 {
|
||||||
Some(Instruction::Move(MoveInstruction::RegMemMove(RegMemMove {
|
Some(Instruction::Move(MoveInstruction::RegMemMove(
|
||||||
|
RegToMemMove {
|
||||||
source: reg,
|
source: reg,
|
||||||
dest: mem_location,
|
dest: mem_location,
|
||||||
})))
|
},
|
||||||
|
)))
|
||||||
} else {
|
} else {
|
||||||
Some(Instruction::Move(MoveInstruction::MemRegMove(MemRegMove {
|
Some(Instruction::Move(MoveInstruction::MemRegMove(
|
||||||
|
MemToRegMove {
|
||||||
dest: reg,
|
dest: reg,
|
||||||
source: mem_location,
|
source: mem_location,
|
||||||
})))
|
},
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -553,6 +557,22 @@ impl<A> Instruction<A> {
|
|||||||
Instruction::Ret => 1,
|
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)]
|
#[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 {
|
impl Display for LogicInstruction {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
f.write_fmt(format_args!(
|
f.write_fmt(format_args!(
|
||||||
"{} {},{}",
|
"{} {}, {}",
|
||||||
self.op,
|
self.op,
|
||||||
self.target,
|
self.target,
|
||||||
if self.amount_from_cl { "cl" } else { "1" }
|
if self.amount_from_cl { "cl" } else { "1" }
|
||||||
@@ -102,4 +102,29 @@ impl LogicInstruction {
|
|||||||
|
|
||||||
result
|
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)]
|
#[derive(Eq, PartialEq, Debug, Hash, Clone, Arbitrary)]
|
||||||
pub struct RegMemMove {
|
pub struct RegToMemMove {
|
||||||
pub source: Register,
|
pub source: Register,
|
||||||
pub dest: EffectiveAddress,
|
pub dest: EffectiveAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegMemMove {
|
impl RegToMemMove {
|
||||||
fn to_bytes(&self) -> Vec<u8> {
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
let mut result = Vec::<u8>::with_capacity(2);
|
let mut result = Vec::<u8>::with_capacity(2);
|
||||||
|
|
||||||
@@ -124,12 +124,12 @@ impl RegMemMove {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Debug, Hash, Clone, Arbitrary)]
|
#[derive(Eq, PartialEq, Debug, Hash, Clone, Arbitrary)]
|
||||||
pub struct MemRegMove {
|
pub struct MemToRegMove {
|
||||||
pub source: EffectiveAddress,
|
pub source: EffectiveAddress,
|
||||||
pub dest: Register,
|
pub dest: Register,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemRegMove {
|
impl MemToRegMove {
|
||||||
fn to_bytes(&self) -> Vec<u8> {
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
let mut result = Vec::with_capacity(2);
|
let mut result = Vec::with_capacity(2);
|
||||||
|
|
||||||
@@ -368,9 +368,9 @@ pub enum MoveInstruction {
|
|||||||
/// Move a value from one register to another
|
/// Move a value from one register to another
|
||||||
RegRegMove(RegRegMove),
|
RegRegMove(RegRegMove),
|
||||||
/// Store a value from a register into memory
|
/// Store a value from a register into memory
|
||||||
RegMemMove(RegMemMove),
|
RegMemMove(RegToMemMove),
|
||||||
/// Load a value from memory into a register
|
/// Load a value from memory into a register
|
||||||
MemRegMove(MemRegMove),
|
MemRegMove(MemToRegMove),
|
||||||
/// Load a literal value into a register
|
/// Load a literal value into a register
|
||||||
ImmediateToRegister(ImmediateToRegister),
|
ImmediateToRegister(ImmediateToRegister),
|
||||||
/// Load a literal value into a register or into memory
|
/// Load a literal value into a register or into memory
|
||||||
@@ -471,6 +471,41 @@ impl MoveInstruction {
|
|||||||
MoveInstruction::RegisterToSegment(mov) => mov.length(),
|
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)]
|
#[cfg(test)]
|
||||||
@@ -478,13 +513,13 @@ mod test_move_instruction {
|
|||||||
use crate::{
|
use crate::{
|
||||||
effective_address::EffectiveAddress,
|
effective_address::EffectiveAddress,
|
||||||
instruction::Instruction,
|
instruction::Instruction,
|
||||||
move_instruction::{MemRegMove, MoveInstruction},
|
move_instruction::{MemToRegMove, MoveInstruction},
|
||||||
register::{GeneralRegister, Register, RegisterSubset},
|
register::{GeneralRegister, Register, RegisterSubset},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mem_reg_move_to_bytes() {
|
fn mem_reg_move_to_bytes() {
|
||||||
let i = MoveInstruction::MemRegMove(MemRegMove {
|
let i = MoveInstruction::MemRegMove(MemToRegMove {
|
||||||
source: EffectiveAddress::BasePointer(0),
|
source: EffectiveAddress::BasePointer(0),
|
||||||
dest: Register::General(GeneralRegister::D, RegisterSubset::All),
|
dest: Register::General(GeneralRegister::D, RegisterSubset::All),
|
||||||
});
|
});
|
||||||
|
@@ -8,12 +8,22 @@ mod test_computer {
|
|||||||
.replace("\r\n", "\n")
|
.replace("\r\n", "\n")
|
||||||
.replace(" ", "")
|
.replace(" ", "")
|
||||||
.replace(" \n", "\n");
|
.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();
|
let first_newline = s.find('\n').unwrap();
|
||||||
s.drain(0..=first_newline);
|
s.drain(0..=first_newline);
|
||||||
|
|
||||||
|
// Chop the 8088 section on trace reports
|
||||||
|
if let Some(index) = s.find("\n******") {
|
||||||
|
s.drain(index..);
|
||||||
|
}
|
||||||
s
|
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
|
where
|
||||||
T: AsRef<[u8]>,
|
T: AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
@@ -41,12 +51,15 @@ mod test_computer {
|
|||||||
panic!("landed in middle of instruction")
|
panic!("landed in middle of instruction")
|
||||||
}
|
}
|
||||||
Some(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());
|
trace.push("Final registers:".to_owned());
|
||||||
for line in computer.dump_register_state().lines() {
|
for line in computer.dump_register_state().lines() {
|
||||||
trace.push(line.to_string());
|
trace.push(line.to_string());
|
||||||
@@ -62,10 +75,11 @@ mod test_computer {
|
|||||||
trace.push(format!(" flags: {}", flags));
|
trace.push(format!(" flags: {}", flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
trace.push("".to_owned());
|
|
||||||
|
|
||||||
let cleaned = clean_trace(expected_trace);
|
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());
|
assert_eq!(trace.len(), expected.len());
|
||||||
|
|
||||||
@@ -80,7 +94,7 @@ mod test_computer {
|
|||||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0043_immediate_movs");
|
include_bytes!("../../computer_enhance/perfaware/part1/listing_0043_immediate_movs");
|
||||||
let expected_trace =
|
let expected_trace =
|
||||||
include_str!("../../computer_enhance/perfaware/part1/listing_0043_immediate_movs.txt");
|
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]
|
#[test]
|
||||||
@@ -89,7 +103,7 @@ mod test_computer {
|
|||||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0044_register_movs");
|
include_bytes!("../../computer_enhance/perfaware/part1/listing_0044_register_movs");
|
||||||
let expected_trace =
|
let expected_trace =
|
||||||
include_str!("../../computer_enhance/perfaware/part1/listing_0044_register_movs.txt");
|
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]
|
#[test]
|
||||||
@@ -100,7 +114,7 @@ mod test_computer {
|
|||||||
let expected_trace = include_str!(
|
let expected_trace = include_str!(
|
||||||
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs.txt"
|
"../../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]
|
#[test]
|
||||||
@@ -109,7 +123,7 @@ mod test_computer {
|
|||||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0046_add_sub_cmp");
|
include_bytes!("../../computer_enhance/perfaware/part1/listing_0046_add_sub_cmp");
|
||||||
let expected_trace =
|
let expected_trace =
|
||||||
include_str!("../../computer_enhance/perfaware/part1/listing_0046_add_sub_cmp.txt");
|
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]
|
#[test]
|
||||||
@@ -118,7 +132,7 @@ mod test_computer {
|
|||||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0047_challenge_flags");
|
include_bytes!("../../computer_enhance/perfaware/part1/listing_0047_challenge_flags");
|
||||||
let expected_trace =
|
let expected_trace =
|
||||||
include_str!("../../computer_enhance/perfaware/part1/listing_0047_challenge_flags.txt");
|
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]
|
#[test]
|
||||||
@@ -127,7 +141,7 @@ mod test_computer {
|
|||||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0048_ip_register");
|
include_bytes!("../../computer_enhance/perfaware/part1/listing_0048_ip_register");
|
||||||
let expected_trace =
|
let expected_trace =
|
||||||
include_str!("../../computer_enhance/perfaware/part1/listing_0048_ip_register.txt");
|
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]
|
#[test]
|
||||||
@@ -137,7 +151,7 @@ mod test_computer {
|
|||||||
let expected_trace = include_str!(
|
let expected_trace = include_str!(
|
||||||
"../../computer_enhance/perfaware/part1/listing_0049_conditional_jumps.txt"
|
"../../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]
|
#[test]
|
||||||
@@ -146,7 +160,7 @@ mod test_computer {
|
|||||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0050_challenge_jumps");
|
include_bytes!("../../computer_enhance/perfaware/part1/listing_0050_challenge_jumps");
|
||||||
let expected_trace =
|
let expected_trace =
|
||||||
include_str!("../../computer_enhance/perfaware/part1/listing_0050_challenge_jumps.txt");
|
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]
|
#[test]
|
||||||
@@ -155,7 +169,7 @@ mod test_computer {
|
|||||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0051_memory_mov");
|
include_bytes!("../../computer_enhance/perfaware/part1/listing_0051_memory_mov");
|
||||||
let expected_trace =
|
let expected_trace =
|
||||||
include_str!("../../computer_enhance/perfaware/part1/listing_0051_memory_mov.txt");
|
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]
|
#[test]
|
||||||
@@ -164,7 +178,7 @@ mod test_computer {
|
|||||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0052_memory_add_loop");
|
include_bytes!("../../computer_enhance/perfaware/part1/listing_0052_memory_add_loop");
|
||||||
let expected_trace =
|
let expected_trace =
|
||||||
include_str!("../../computer_enhance/perfaware/part1/listing_0052_memory_add_loop.txt");
|
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]
|
#[test]
|
||||||
@@ -175,7 +189,7 @@ mod test_computer {
|
|||||||
let expected_trace = include_str!(
|
let expected_trace = include_str!(
|
||||||
"../../computer_enhance/perfaware/part1/listing_0053_add_loop_challenge.txt"
|
"../../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]
|
#[test]
|
||||||
@@ -184,7 +198,7 @@ mod test_computer {
|
|||||||
include_bytes!("../../computer_enhance/perfaware/part1/listing_0054_draw_rectangle");
|
include_bytes!("../../computer_enhance/perfaware/part1/listing_0054_draw_rectangle");
|
||||||
let expected_trace =
|
let expected_trace =
|
||||||
include_str!("../../computer_enhance/perfaware/part1/listing_0054_draw_rectangle.txt");
|
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]
|
#[test]
|
||||||
@@ -195,6 +209,80 @@ mod test_computer {
|
|||||||
let expected_trace = include_str!(
|
let expected_trace = include_str!(
|
||||||
"../../computer_enhance/perfaware/part1/listing_0055_challenge_rectangle.txt"
|
"../../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")
|
panic!("landed in middle of instruction")
|
||||||
}
|
}
|
||||||
Some(instruction) => {
|
Some(instruction) => {
|
||||||
computer.step(instruction, false);
|
computer.step(instruction, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user