Implement mov simulation (#10)

This commit is contained in:
Patrick Stevens
2023-04-14 08:58:31 +01:00
committed by GitHub
parent cad4920115
commit 22025bae39
10 changed files with 1250 additions and 238 deletions

View File

@@ -0,0 +1,169 @@
use std::ops::{Add, Mul, Sub};
#[derive(PartialEq, Eq)]
enum Op {
Add,
Times,
}
enum BinaryExpr<T> {
Const(T),
/// Indices into the arena.
Binary(Op, usize, usize),
}
pub(crate) struct ArithmeticExpression<T> {
/// In reverse Polish notation.
arena: Vec<BinaryExpr<T>>,
}
#[derive(Copy, Clone)]
pub(crate) enum Token<T> {
Literal(T),
Add,
Times,
}
pub(crate) trait HasMax {
const MAX: Self;
}
impl HasMax for u16 {
const MAX: u16 = u16::MAX;
}
impl HasMax for u8 {
const MAX: u8 = u8::MAX;
}
impl<T> ArithmeticExpression<T>
where
T: Copy,
{
fn eval_index(&self, index: usize) -> Result<T, ()>
where
T: Add<Output = T> + Sub<Output = T> + Mul<Output = T> + HasMax + PartialOrd,
u64: From<T>,
{
match &self.arena[index] {
BinaryExpr::Const(x) => Ok(*x),
BinaryExpr::Binary(op, left, right) => {
let left = self.eval_index(*left)?;
let right = self.eval_index(*right)?;
match op {
Op::Add => {
if left < T::MAX - right {
Ok(left + right)
} else {
Err(())
}
}
Op::Times => {
let x = u64::from(left);
let y = u64::from(right);
if x * y < u64::from(T::MAX) {
Ok(left * right)
} else {
Err(())
}
}
}
}
}
}
pub(crate) fn eval(&self) -> Result<T, ()>
where
T: Add<Output = T> + Mul<Output = T> + HasMax + PartialOrd + Sub<Output = T>,
u64: From<T>,
{
self.eval_index(self.arena.len() - 1)
}
pub(crate) fn of_tokens<Iter: AsRef<[Token<T>]>>(t: Iter) -> ArithmeticExpression<T> {
let mut arena = vec![];
let mut outstanding_ops = vec![];
let mut outstanding_exprs = vec![];
for token in t.as_ref() {
match token {
Token::Literal(l) => {
arena.push(BinaryExpr::Const(*l));
let to_add = match outstanding_ops.pop() {
None => arena.len() - 1,
Some(Op::Times) => match outstanding_exprs.pop() {
None => {
panic!("Bad token stream, had Times operation with no left operand")
}
Some(left_operand) => {
let right = arena.len() - 1;
arena.push(BinaryExpr::Binary(Op::Times, left_operand, right));
arena.len() - 1
}
},
Some(Op::Add) => {
outstanding_ops.push(Op::Add);
arena.len() - 1
}
};
outstanding_exprs.push(to_add);
}
Token::Add => outstanding_ops.push(Op::Add),
Token::Times => outstanding_ops.push(Op::Times),
}
}
while let Some(op) = outstanding_ops.pop() {
assert!(op == Op::Add);
let left = outstanding_exprs.pop().expect("needed a right operand");
let right = outstanding_exprs.pop().expect("needed a left operand");
arena.push(BinaryExpr::Binary(Op::Add, left, right));
outstanding_exprs.push(arena.len() - 1);
}
if !outstanding_ops.is_empty() {
panic!("Imbalanced expression");
}
if outstanding_exprs.len() > 1 {
panic!("Not enough operations");
}
if outstanding_exprs.is_empty() {
panic!("Empty expression?!");
}
ArithmeticExpression { arena }
}
}
#[cfg(test)]
mod test_arithmetic_expression {
use super::{ArithmeticExpression, Token};
#[test]
fn test_expr() {
let tokens = vec![
Token::Literal(10),
Token::Add,
Token::Literal(33),
Token::Times,
Token::Literal(12),
];
assert_eq!(
ArithmeticExpression::of_tokens(tokens).eval().unwrap(),
10u16 + (33 * 12)
);
let tokens = vec![
Token::Literal(10),
Token::Times,
Token::Literal(33),
Token::Add,
Token::Literal(12),
];
assert_eq!(
ArithmeticExpression::of_tokens(tokens).eval().unwrap(),
(10u16 * 33) + 12
);
}
}

View File

@@ -2,15 +2,17 @@ use nom::{
branch::alt,
bytes::complete::{is_not, tag},
character::complete::{
alphanumeric1, char, digit1, line_ending, multispace0, not_line_ending, one_of,
alphanumeric1, char, digit1, line_ending, multispace0, not_line_ending, one_of, space0,
},
combinator::map_res,
multi::many0,
combinator::{map_res, opt},
error::FromExternalError,
multi::{many0, many1},
sequence::{delimited, preceded, separated_pair, terminated, tuple},
IResult,
};
use crate::{
arithmetic_expression::{ArithmeticExpression, Token},
arithmetic_instruction::{
ArithmeticInstruction, ArithmeticInstructionSelect, ArithmeticOperation, MemRegArithmetic,
RegMemArithmetic, RegRegArithmetic,
@@ -19,13 +21,14 @@ use crate::{
instruction::Instruction,
jump_instruction::Jump,
move_instruction::{
AccumulatorToMemory, ImmediateToRegister, ImmediateToRegisterOrMemory, MemRegMove,
MemoryToAccumulator, MoveInstruction, RegMemMove, RegRegMove,
AccumulatorToMemory, ImmediateToMemory, ImmediateToRegister, MemRegMove,
MemoryToAccumulator, MemoryToSegment, MoveInstruction, RegMemMove, RegRegMove,
RegisterToSegment, SegmentToMemory, SegmentToRegister,
},
program::Program,
register::{
Base, ByteRegisterSubset, GeneralRegister, Register, RegisterSubset, SourceDest,
SpecialRegister,
Base, ByteRegisterSubset, GeneralRegister, Register, RegisterSubset, SegmentRegister,
SourceDest, SpecialRegister,
},
trivia_instruction::TriviaInstruction,
};
@@ -43,13 +46,42 @@ where
delimited(multispace0, inner, multispace0)
}
fn line_end(input: &str) -> IResult<&str, &str> {
alt((comment, line_ending))(input)
}
#[derive(Eq, PartialEq)]
enum OffsetTag {
Byte,
Word,
None,
}
fn bracketed<'a, F, O, E: nom::error::ParseError<&'a str>>(
inner: F,
) -> impl FnMut(&'a str) -> IResult<&'a str, O, E>
) -> impl FnMut(&'a str) -> IResult<&'a str, (OffsetTag, O), E>
where
F: FnMut(&'a str) -> IResult<&'a str, O, E>,
E: FromExternalError<&'a str, ()>,
{
preceded(char('['), terminated(inner, char(']')))
map_res(
tuple((
opt(ws(alt((tag("byte"), tag("word"))))),
preceded(char('['), terminated(inner, char(']'))),
)),
|(tag, inner)| {
Ok::<_, _>(match tag {
None => (OffsetTag::None, inner),
Some(tag) => {
if tag == "byte" {
(OffsetTag::Byte, inner)
} else {
(OffsetTag::Word, inner)
}
}
})
},
)
}
fn argument_sep(input: &str) -> IResult<&str, ()> {
@@ -57,7 +89,7 @@ fn argument_sep(input: &str) -> IResult<&str, ()> {
}
fn bits(input: &str) -> IResult<&str, u8> {
let p = preceded(tag("bits "), terminated(digit1, line_ending));
let p = preceded(tag("bits "), digit1);
map_res(p, str::parse)(input)
}
@@ -128,13 +160,14 @@ fn reg_reg_move_instruction(input: &str) -> IResult<&str, RegRegMove> {
map_res(
preceded(
tag("mov "),
tuple((register, argument_sep, register, line_ending)),
tuple((terminated(register, argument_sep), register)),
),
|(dest, _, source, _)| Ok::<_, ()>(RegRegMove { dest, source }),
|(dest, source)| Ok::<_, ()>(RegRegMove { dest, source }),
)(input)
}
fn direct_offset(input: &str) -> IResult<&str, u16> {
// TODO: anything bracketed should be able to know if it's a byte or a word
fn direct_offset(input: &str) -> IResult<&str, (OffsetTag, u16)> {
bracketed(map_res(digit1, str::parse))(input)
}
@@ -152,7 +185,7 @@ fn source_dest(input: &str) -> IResult<&str, SourceDest> {
))(input)
}
fn absolute_u8(input: &str) -> IResult<&str, u8> {
fn literal_absolute_u8(input: &str) -> IResult<&str, u8> {
alt((
map_res(preceded(tag("0x"), alphanumeric1), |s: &str| {
s.chars()
@@ -176,7 +209,7 @@ fn absolute_u8(input: &str) -> IResult<&str, u8> {
))(input)
}
fn absolute_u16(input: &str) -> IResult<&str, u16> {
fn literal_absolute_u16(input: &str) -> IResult<&str, u16> {
alt((
map_res(preceded(tag("0x"), alphanumeric1), |s: &str| {
s.chars()
@@ -200,6 +233,36 @@ fn absolute_u16(input: &str) -> IResult<&str, u16> {
))(input)
}
fn arithmetic_expression_u8(input: &str) -> IResult<&str, ArithmeticExpression<u8>> {
map_res(
many1(alt((
map_res(literal_absolute_u8, |x| Ok::<_, ()>(Token::Literal(x))),
map_res(ws(char('+')), |_| Ok::<_, ()>(Token::Add)),
map_res(ws(char('*')), |_| Ok::<_, ()>(Token::Times)),
))),
|stream| Ok::<_, ()>(ArithmeticExpression::of_tokens(stream)),
)(input)
}
fn arithmetic_expression_u16(input: &str) -> IResult<&str, ArithmeticExpression<u16>> {
map_res(
many1(alt((
map_res(literal_absolute_u16, |x| Ok::<_, ()>(Token::Literal(x))),
map_res(ws(char('+')), |_| Ok::<_, ()>(Token::Add)),
map_res(ws(char('*')), |_| Ok::<_, ()>(Token::Times)),
))),
|stream| Ok::<_, ()>(ArithmeticExpression::of_tokens(stream)),
)(input)
}
fn absolute_u8(input: &str) -> IResult<&str, u8> {
map_res(arithmetic_expression_u8, |expr| expr.eval())(input)
}
fn absolute_u16(input: &str) -> IResult<&str, u16> {
map_res(arithmetic_expression_u16, |expr| expr.eval())(input)
}
fn negative_u8(input: &str) -> IResult<&str, u8> {
map_res(
preceded(ws(char('-')), alt((digit1, preceded(tag("byte "), digit1)))),
@@ -214,24 +277,24 @@ fn negative_u16(input: &str) -> IResult<&str, u16> {
)(input)
}
fn literal_u8(input: &str) -> IResult<&str, u8> {
alt((absolute_u8, negative_u8))(input)
}
fn literal_u16(input: &str) -> IResult<&str, u16> {
alt((absolute_u16, negative_u16))(input)
}
fn effective_address(input: &str) -> IResult<&str, EffectiveAddress> {
fn literal_u8(input: &str) -> IResult<&str, u8> {
alt((absolute_u8, negative_u8))(input)
}
fn effective_address(input: &str) -> IResult<&str, (OffsetTag, EffectiveAddress)> {
alt((
// Sum, no offset
map_res(
bracketed(tuple((base, ws(char('+')), source_dest))),
|(base, _, source_dest)| {
Ok::<_, ()>(EffectiveAddress::Sum(WithOffset::Basic((
base,
source_dest,
))))
|(tag, (base, _, source_dest))| {
Ok::<_, ()>((
tag,
EffectiveAddress::Sum(WithOffset::Basic((base, source_dest))),
))
},
),
// Sum, with offset
@@ -242,11 +305,11 @@ fn effective_address(input: &str) -> IResult<&str, EffectiveAddress> {
source_dest,
alt((preceded(ws(char('+')), absolute_u8), negative_u8)),
))),
|(base, _, source_dest, offset)| {
Ok::<_, ()>(EffectiveAddress::Sum(WithOffset::WithU8(
(base, source_dest),
offset,
)))
|(tag, (base, _, source_dest, offset))| {
Ok::<_, ()>((
tag,
EffectiveAddress::Sum(WithOffset::WithU8((base, source_dest), offset)),
))
},
),
map_res(
@@ -256,29 +319,30 @@ fn effective_address(input: &str) -> IResult<&str, EffectiveAddress> {
source_dest,
alt((preceded(ws(char('+')), absolute_u16), negative_u16)),
))),
|(base, _, source_dest, offset)| {
Ok::<_, ()>(EffectiveAddress::Sum(WithOffset::WithU16(
(base, source_dest),
offset,
)))
|(tag, (base, _, source_dest, offset))| {
Ok::<_, ()>((
tag,
EffectiveAddress::Sum(WithOffset::WithU16((base, source_dest), offset)),
))
},
),
// Lookup
map_res(source_dest, |source_dest| {
Ok::<_, ()>(EffectiveAddress::SpecifiedIn(WithOffset::Basic(
source_dest,
)))
map_res(bracketed(source_dest), |(tag, source_dest)| {
Ok::<_, ()>((
tag,
EffectiveAddress::SpecifiedIn(WithOffset::Basic(source_dest)),
))
}),
map_res(
bracketed(tuple((
source_dest,
alt((preceded(ws(char('+')), absolute_u8), negative_u8)),
))),
|(source_dest, offset)| {
Ok::<_, ()>(EffectiveAddress::SpecifiedIn(WithOffset::WithU8(
source_dest,
offset,
)))
|(tag, (source_dest, offset))| {
Ok::<_, ()>((
tag,
EffectiveAddress::SpecifiedIn(WithOffset::WithU8(source_dest, offset)),
))
},
),
map_res(
@@ -286,71 +350,138 @@ fn effective_address(input: &str) -> IResult<&str, EffectiveAddress> {
source_dest,
alt((preceded(ws(char('+')), absolute_u16), negative_u16)),
))),
|(source_dest, offset)| {
Ok::<_, ()>(EffectiveAddress::SpecifiedIn(WithOffset::WithU16(
source_dest,
offset,
)))
|(tag, (source_dest, offset))| {
Ok::<_, ()>((
tag,
EffectiveAddress::SpecifiedIn(WithOffset::WithU16(source_dest, offset)),
))
},
),
// Offset from BX
map_res(bracketed(tag("bx")), |_| {
Ok::<_, ()>(EffectiveAddress::Bx(WithOffset::Basic(())))
map_res(bracketed(tag("bx")), |(tag, _)| {
Ok::<_, ()>((tag, EffectiveAddress::Bx(WithOffset::Basic(()))))
}),
map_res(
bracketed(tuple((
bracketed(preceded(
tag("bx"),
alt((preceded(ws(char('+')), absolute_u8), negative_u8)),
))),
|(_, offset)| Ok::<_, ()>(EffectiveAddress::Bx(WithOffset::WithU8((), offset))),
)),
|(tag, offset)| {
Ok::<_, ()>((tag, EffectiveAddress::Bx(WithOffset::WithU8((), offset))))
},
),
map_res(
bracketed(tuple((
bracketed(preceded(
tag("bx"),
alt((preceded(ws(char('+')), absolute_u16), negative_u16)),
))),
|(_, offset)| Ok::<_, ()>(EffectiveAddress::Bx(WithOffset::WithU16((), offset))),
)),
|(tag, offset)| {
Ok::<_, ()>((tag, EffectiveAddress::Bx(WithOffset::WithU16((), offset))))
},
),
// Direct memory address
map_res(direct_offset, |offset| {
Ok::<_, ()>(EffectiveAddress::Direct(offset))
map_res(direct_offset, |(tag, offset)| {
Ok::<_, ()>((tag, EffectiveAddress::Direct(offset)))
}),
// Offset from base pointer
map_res(
bracketed(tuple((
bracketed(preceded(
tag("bp"),
alt((preceded(ws(char('+')), absolute_u8), negative_u8)),
))),
|(_, offset)| Ok::<_, ()>(EffectiveAddress::BasePointer(offset)),
)),
|(tag, offset)| Ok::<_, ()>((tag, EffectiveAddress::BasePointer(offset))),
),
map_res(
bracketed(tuple((
bracketed(preceded(
tag("bp"),
alt((preceded(ws(char('+')), absolute_u16), negative_u16)),
))),
|(_, offset)| Ok::<_, ()>(EffectiveAddress::BasePointerWide(offset)),
)),
|(tag, offset)| Ok::<_, ()>((tag, EffectiveAddress::BasePointerWide(offset))),
),
// Specific support for [bp], which can't be represented as a simple instruction
map_res(bracketed(tag("bp")), |_| {
Ok::<_, ()>(EffectiveAddress::BasePointer(0))
map_res(bracketed(tag("bp")), |(tag, _)| {
Ok::<_, ()>((tag, EffectiveAddress::BasePointer(0)))
}),
))(input)
}
fn segment_register(input: &str) -> IResult<&str, SegmentRegister> {
map_res(
terminated(alt((char('s'), char('d'), char('e'), char('c'))), char('s')),
|c| match c {
's' => Ok::<_, ()>(SegmentRegister::Stack),
'd' => Ok(SegmentRegister::Data),
'e' => Ok(SegmentRegister::Extra),
'c' => Ok(SegmentRegister::Code),
_ => unreachable!(),
},
)(input)
}
fn reg_to_seg_move_instruction(input: &str) -> IResult<&str, RegisterToSegment> {
map_res(
preceded(
tag("mov "),
tuple((terminated(segment_register, ws(char(','))), register)),
),
|(dest, source)| Ok::<_, ()>(RegisterToSegment { dest, source }),
)(input)
}
fn mem_to_seg_move_instruction(input: &str) -> IResult<&str, MemoryToSegment> {
map_res(
preceded(
tag("mov "),
tuple((
terminated(segment_register, ws(char(','))),
effective_address,
)),
),
|(dest, (tag, source))| match tag {
OffsetTag::Byte => Err(()),
_ => Ok::<_, ()>(MemoryToSegment { dest, source }),
},
)(input)
}
fn seg_to_mem_move_instruction(input: &str) -> IResult<&str, SegmentToMemory> {
map_res(
preceded(
tag("mov "),
tuple((
terminated(effective_address, ws(char(','))),
segment_register,
)),
),
|((tag, dest), source)| match tag {
OffsetTag::Byte => Err(()),
_ => Ok::<_, ()>(SegmentToMemory { dest, source }),
},
)(input)
}
fn seg_to_reg_move_instruction(input: &str) -> IResult<&str, SegmentToRegister> {
map_res(
preceded(
tag("mov "),
tuple((terminated(register, ws(char(','))), segment_register)),
),
|(dest, source)| Ok::<_, ()>(SegmentToRegister { dest, source }),
)(input)
}
fn reg_mem_move_instruction(input: &str) -> IResult<&str, RegMemMove> {
map_res(
preceded(
tag("mov "),
tuple((
terminated(effective_address, argument_sep),
terminated(register, line_ending),
)),
tuple((terminated(effective_address, argument_sep), register)),
),
|(address, register)| {
Ok::<_, ()>(RegMemMove {
|((tag, address), register)| match (tag, register.is_wide()) {
(OffsetTag::Word, false) => Err(()),
_ => Ok::<_, ()>(RegMemMove {
dest: address,
source: register,
})
}),
},
)(input)
}
@@ -359,16 +490,14 @@ fn mem_reg_move_instruction(input: &str) -> IResult<&str, MemRegMove> {
map_res(
preceded(
tag("mov "),
tuple((
terminated(register, argument_sep),
terminated(effective_address, line_ending),
)),
tuple((terminated(register, argument_sep), effective_address)),
),
|(register, address)| {
Ok::<_, ()>(MemRegMove {
|(register, (tag, address))| match (tag, register.is_wide()) {
(OffsetTag::Word, false) => Err(()),
_ => Ok::<_, ()>(MemRegMove {
dest: register,
source: address,
})
}),
},
)(input)
}
@@ -392,18 +521,12 @@ fn immediate_to_register_instruction(input: &str) -> IResult<&str, ImmediateToRe
preceded(
tag("mov "),
alt((
terminated(
map_res(immediate_wide, |(register, x)| {
Ok::<_, ()>((register, Err(x)))
}),
line_ending,
),
terminated(
map_res(immediate_byte, |(register, x)| {
Ok::<_, ()>((register, Ok(x)))
}),
line_ending,
),
map_res(immediate_wide, |(register, x)| {
Ok::<_, ()>((register, Err(x)))
}),
map_res(immediate_byte, |(register, x)| {
Ok::<_, ()>((register, Ok(x)))
}),
)),
),
|(register, contents)| {
@@ -421,7 +544,7 @@ fn immediate_to_register_instruction(input: &str) -> IResult<&str, ImmediateToRe
)(input)
}
fn immediate_to_memory_instruction(input: &str) -> IResult<&str, ImmediateToRegisterOrMemory> {
fn immediate_to_memory_instruction(input: &str) -> IResult<&str, ImmediateToMemory> {
map_res(
tuple((
terminated(preceded(tag("mov "), effective_address), argument_sep),
@@ -430,11 +553,19 @@ fn immediate_to_memory_instruction(input: &str) -> IResult<&str, ImmediateToRegi
map_res(literal_u16, |x| Ok::<_, ()>(Err(x))),
)),
)),
|(addr, x)| {
Ok::<_, ()>(match x {
Ok(b) => ImmediateToRegisterOrMemory::Byte(addr, b),
Err(b) => ImmediateToRegisterOrMemory::Word(addr, b),
})
|((tag, addr), x)| match tag {
OffsetTag::None => Ok::<_, ()>(match x {
Ok(b) => ImmediateToMemory::Byte(addr, b),
Err(b) => ImmediateToMemory::Word(addr, b),
}),
OffsetTag::Byte => match x {
Ok(b) => Ok(ImmediateToMemory::Byte(addr, b)),
Err(b) => panic!("Can't fit literal {b} into byte memory"),
},
OffsetTag::Word => match x {
Ok(b) => Ok(ImmediateToMemory::Word(addr, b as u16)),
Err(b) => Ok(ImmediateToMemory::Word(addr, b)),
},
},
)(input)
}
@@ -448,9 +579,13 @@ fn memory_to_accumulator_instruction(input: &str) -> IResult<&str, MemoryToAccum
bracketed(literal_u16),
)),
),
|(acc, address)| {
|(acc, (tag, address))| {
let is_wide = acc == 'x';
Ok::<_, ()>(MemoryToAccumulator { address, is_wide })
if !is_wide && tag == OffsetTag::Word {
Err(())
} else {
Ok(MemoryToAccumulator { address, is_wide })
}
},
)(input)
}
@@ -464,9 +599,13 @@ fn accumulator_to_memory_instruction(input: &str) -> IResult<&str, AccumulatorTo
alt((char('h'), char('x'))),
)),
),
|(address, acc)| {
|((tag, address), acc)| {
let is_wide = acc == 'x';
Ok::<_, ()>(AccumulatorToMemory { address, is_wide })
if is_wide && tag == OffsetTag::Byte {
Err(())
} else {
Ok(AccumulatorToMemory { address, is_wide })
}
},
)(input)
}
@@ -522,18 +661,20 @@ fn arithmetic_select(input: &str) -> IResult<&str, ArithmeticInstructionSelect>
),
map_res(
tuple((terminated(register, argument_sep), effective_address)),
|(dest, source)| {
Ok::<_, ()>(ArithmeticInstructionSelect::MemoryToRegister(
|(dest, (tag, source))| match (tag, dest.is_wide()) {
(OffsetTag::Word, false) => Err(()),
_ => Ok(ArithmeticInstructionSelect::MemoryToRegister(
MemRegArithmetic { source, dest },
))
)),
},
),
map_res(
tuple((terminated(effective_address, argument_sep), register)),
|(dest, source)| {
Ok::<_, ()>(ArithmeticInstructionSelect::RegisterToMemory(
|((tag, dest), source)| match (tag, source.is_wide()) {
(OffsetTag::Byte, true) => Err(()),
_ => Ok(ArithmeticInstructionSelect::RegisterToMemory(
RegMemArithmetic { source, dest },
))
)),
},
),
map_res(
@@ -561,58 +702,24 @@ fn arithmetic_select(input: &str) -> IResult<&str, ArithmeticInstructionSelect>
},
),
map_res(
tuple((
terminated(preceded(tag("word "), effective_address), argument_sep),
literal_u8,
)),
|(addr, literal)| {
tuple((terminated(effective_address, argument_sep), literal_u8)),
|((tag, addr), literal)| {
Ok::<_, ()>(
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryByte(
addr, literal, true,
),
)
},
),
map_res(
tuple((
terminated(preceded(tag("word "), effective_address), argument_sep),
literal_u16,
)),
|(addr, literal)| {
Ok::<_, ()>(
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryWord(addr, literal),
)
},
),
map_res(
tuple((
terminated(preceded(tag("byte "), effective_address), argument_sep),
literal_u8,
)),
|(addr, literal)| {
Ok::<_, ()>(
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryByte(
addr, literal, false,
addr,
literal,
tag == OffsetTag::Word,
),
)
},
),
map_res(
tuple((terminated(effective_address, argument_sep), literal_u16)),
|(addr, literal)| {
Ok::<_, ()>(
|((tag, addr), literal)| match tag {
OffsetTag::Byte => Err(()),
_ => Ok::<_, ()>(
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryWord(addr, literal),
)
},
),
map_res(
tuple((terminated(effective_address, argument_sep), literal_u8)),
|(addr, literal)| {
Ok::<_, ()>(
ArithmeticInstructionSelect::ImmediateToRegisterOrMemoryByte(
addr, literal, false,
),
)
),
},
),
))(input)
@@ -620,16 +727,13 @@ fn arithmetic_select(input: &str) -> IResult<&str, ArithmeticInstructionSelect>
fn arithmetic_instruction(input: &str) -> IResult<&str, ArithmeticInstruction> {
map_res(
tuple((
terminated(arithmetic_op, char(' ')),
terminated(arithmetic_select, line_ending),
)),
tuple((terminated(arithmetic_op, char(' ')), arithmetic_select)),
|(op, instruction)| Ok::<_, ()>(ArithmeticInstruction { op, instruction }),
)(input)
}
fn label(input: &str) -> IResult<&str, &str> {
terminated(is_not(":\r\n \t"), terminated(char(':'), line_ending))(input)
terminated(is_not(":\r\n \t"), char(':'))(input)
}
fn label_terminator(input: &str) -> IResult<&str, &str> {
@@ -728,7 +832,19 @@ fn move_instruction(input: &str) -> IResult<&str, MoveInstruction> {
Ok::<_, ()>(MoveInstruction::ImmediateToRegister(v))
}),
map_res(immediate_to_memory_instruction, |v| {
Ok::<_, ()>(MoveInstruction::ImmediateToRegisterOrMemory(v))
Ok::<_, ()>(MoveInstruction::ImmediateToMemory(v))
}),
map_res(seg_to_mem_move_instruction, |v| {
Ok::<_, ()>(MoveInstruction::SegmentToMemory(v))
}),
map_res(seg_to_reg_move_instruction, |v| {
Ok::<_, ()>(MoveInstruction::SegmentToRegister(v))
}),
map_res(reg_to_seg_move_instruction, |v| {
Ok::<_, ()>(MoveInstruction::RegisterToSegment(v))
}),
map_res(mem_to_seg_move_instruction, |v| {
Ok::<_, ()>(MoveInstruction::MemoryToSegment(v))
}),
))(input)
}
@@ -746,20 +862,19 @@ fn instruction(input: &str) -> IResult<&str, Instruction<&str>> {
))(input)
}
fn trivia(input: &str) -> IResult<&str, &str> {
alt((comment, line_ending))(input)
}
pub fn program(input: &str) -> IResult<&str, Program<Vec<Instruction<&str>>, &str>> {
map_res(
preceded(
many0(trivia),
many0(line_end),
separated_pair(
bits,
many0(trivia),
many0(line_end),
many0(alt((
map_res(instruction, |i| Ok::<_, ()>(Some(i))),
map_res(trivia, |_| Ok::<_, ()>(None)),
map_res(
terminated(preceded(space0, instruction), preceded(space0, line_end)),
|i| Ok::<_, ()>(Some(i)),
),
map_res(preceded(space0, line_end), |_| Ok::<_, ()>(None)),
))),
),
),
@@ -772,3 +887,64 @@ pub fn program(input: &str) -> IResult<&str, Program<Vec<Instruction<&str>>, &st
},
)(input)
}
#[cfg(test)]
mod test_assembly {
use crate::{
arithmetic_instruction::{
ArithmeticInstruction, ArithmeticInstructionSelect, ArithmeticOperation,
},
assembly::instruction,
effective_address::{EffectiveAddress, WithOffset},
instruction::Instruction,
move_instruction::{ImmediateToMemory, MoveInstruction},
register::{GeneralRegister, Register, RegisterSubset},
};
#[test]
fn arithmetic_expression_parse() {
let (remaining, parsed) = instruction("add bx, 4*64").unwrap();
assert_eq!(remaining, "");
assert_eq!(
parsed,
Instruction::Arithmetic(ArithmeticInstruction {
op: ArithmeticOperation::Add,
instruction: ArithmeticInstructionSelect::ImmediateToRegisterWord(
Register::General(GeneralRegister::B, RegisterSubset::All),
4 * 64,
false
)
})
)
}
#[test]
fn arithmetic_expression_parse_2() {
let (remaining, parsed) = instruction("add cx, 166").unwrap();
assert_eq!(remaining, "");
assert_eq!(
parsed,
Instruction::Arithmetic(ArithmeticInstruction {
op: ArithmeticOperation::Add,
instruction: ArithmeticInstructionSelect::ImmediateToRegisterByte(
Register::General(GeneralRegister::C, RegisterSubset::All),
166,
true
)
})
)
}
#[test]
fn mov_parse() {
let (remaining, parsed) = instruction("mov byte [bx + 61*4 + 1], 255").unwrap();
assert_eq!(remaining, "");
assert_eq!(
parsed,
Instruction::Move(MoveInstruction::ImmediateToMemory(ImmediateToMemory::Byte(
EffectiveAddress::Bx(WithOffset::WithU8((), 61 * 4 + 1)),
255
)))
)
}
}

View File

@@ -1,7 +1,11 @@
use crate::{
effective_address::{EffectiveAddress, WithOffset},
instruction::Instruction,
move_instruction::{ImmediateToRegister, ImmediateToRegisterOrMemory, MoveInstruction},
register::{ByteRegisterSubset, GeneralRegister, Register, RegisterSubset, SpecialRegister},
move_instruction::{ImmediateToMemory, ImmediateToRegister, MoveInstruction},
register::{
Base, ByteRegisterSubset, GeneralRegister, Register, RegisterSubset, SegmentRegister,
SourceDest, SpecialRegister,
},
};
pub struct Registers {
@@ -13,6 +17,10 @@ pub struct Registers {
di: u16,
sp: u16,
bp: u16,
ss: u16,
cs: u16,
es: u16,
ds: u16,
}
pub struct Computer {
@@ -35,6 +43,10 @@ impl Computer {
di: 0,
sp: 0,
bp: 0,
ss: 0,
cs: 0,
es: 0,
ds: 0,
},
}
}
@@ -86,6 +98,15 @@ impl Computer {
}
}
fn get_segment(&self, r: SegmentRegister) -> u16 {
match r {
SegmentRegister::Code => self.registers.cs,
SegmentRegister::Data => self.registers.ds,
SegmentRegister::Stack => self.registers.ss,
SegmentRegister::Extra => self.registers.es,
}
}
fn display_big(u: u16) -> String {
if u == 0 {
"0x0".to_owned()
@@ -103,7 +124,11 @@ impl Computer {
}
fn set_register(&mut self, r: &Register, value: u16) -> String {
let was = self.get_register(r);
let register_for_print = match r {
Register::General(x, _) => Register::General(x.clone(), RegisterSubset::All),
_ => r.clone(),
};
let was = self.get_register(&register_for_print);
match r {
Register::General(GeneralRegister::A, RegisterSubset::All) => self.registers.a = value,
Register::General(GeneralRegister::B, RegisterSubset::All) => self.registers.b = value,
@@ -170,7 +195,25 @@ impl Computer {
Register::Special(SpecialRegister::SourceIndex) => self.registers.si = value,
Register::Special(SpecialRegister::DestIndex) => self.registers.di = value,
}
let is_now = self.get_register(r);
let is_now = self.get_register(&register_for_print);
// TODO: this needs to print out "al: 0x22 -> blah" instead of "ax: 0x2222 -> blah" if short
format!(
"{}:{}->{}",
register_for_print,
Self::display_small(was),
Self::display_small(is_now)
)
}
fn set_segment(&mut self, r: SegmentRegister, value: u16) -> String {
let was = self.get_segment(r);
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,
@@ -179,22 +222,225 @@ impl Computer {
)
}
fn get_memory_byte(&self, index: usize) -> u8 {
self.memory[index]
}
fn get_memory_word(&self, index: usize) -> u16 {
self.memory[index] as u16 * 256 + self.memory[index + 1] as u16
}
fn set_memory_byte(&mut self, index: usize, value: u8) -> String {
self.memory[index] = value;
"todo".to_owned()
}
fn set_memory_word(&mut self, index: usize, value: u16) -> String {
self.memory[index] = (value % 256) as u8;
self.memory[index + 1] = (value / 256) as u8;
"todo".to_owned()
}
fn resolve_eaddr(&self, a: &EffectiveAddress) -> usize {
match a {
EffectiveAddress::Sum(WithOffset::Basic((base, source_dest))) => {
let base_offset = match base {
Base::Bp => self.get_register(&Register::Special(SpecialRegister::BasePointer)),
Base::Bx => self
.get_register(&Register::General(GeneralRegister::B, RegisterSubset::All)),
};
let source_offset = match source_dest {
SourceDest::Source => {
self.get_register(&Register::Special(SpecialRegister::SourceIndex))
}
SourceDest::Dest => {
self.get_register(&Register::Special(SpecialRegister::DestIndex))
}
};
base_offset as usize + source_offset as usize
}
EffectiveAddress::Sum(WithOffset::WithU8((base, source_dest), addend)) => {
let base_offset = match base {
Base::Bp => self.get_register(&Register::Special(SpecialRegister::BasePointer)),
Base::Bx => self
.get_register(&Register::General(GeneralRegister::B, RegisterSubset::All)),
};
let source_offset = match source_dest {
SourceDest::Source => {
self.get_register(&Register::Special(SpecialRegister::SourceIndex))
}
SourceDest::Dest => {
self.get_register(&Register::Special(SpecialRegister::DestIndex))
}
};
base_offset as usize + source_offset as usize + *addend as usize
}
EffectiveAddress::Sum(WithOffset::WithU16((base, source_dest), addend)) => {
let base_offset = match base {
Base::Bp => self.get_register(&Register::Special(SpecialRegister::BasePointer)),
Base::Bx => self
.get_register(&Register::General(GeneralRegister::B, RegisterSubset::All)),
};
let source_offset = match source_dest {
SourceDest::Source => {
self.get_register(&Register::Special(SpecialRegister::SourceIndex))
}
SourceDest::Dest => {
self.get_register(&Register::Special(SpecialRegister::DestIndex))
}
};
base_offset as usize + source_offset as usize + *addend as usize
}
EffectiveAddress::SpecifiedIn(WithOffset::Basic(source_dest)) => {
let source_offset = match source_dest {
SourceDest::Source => {
self.get_register(&Register::Special(SpecialRegister::SourceIndex))
}
SourceDest::Dest => {
self.get_register(&Register::Special(SpecialRegister::DestIndex))
}
};
source_offset as usize
}
EffectiveAddress::SpecifiedIn(WithOffset::WithU8(source_dest, offset)) => {
let source_offset = match source_dest {
SourceDest::Source => {
self.get_register(&Register::Special(SpecialRegister::SourceIndex))
}
SourceDest::Dest => {
self.get_register(&Register::Special(SpecialRegister::DestIndex))
}
};
source_offset as usize + *offset as usize
}
EffectiveAddress::SpecifiedIn(WithOffset::WithU16(source_dest, offset)) => {
let source_offset = match source_dest {
SourceDest::Source => {
self.get_register(&Register::Special(SpecialRegister::SourceIndex))
}
SourceDest::Dest => {
self.get_register(&Register::Special(SpecialRegister::DestIndex))
}
};
source_offset as usize + *offset as usize
}
EffectiveAddress::Bx(WithOffset::Basic(())) => {
let bx =
self.get_register(&Register::General(GeneralRegister::B, RegisterSubset::All));
bx as usize
}
EffectiveAddress::Bx(WithOffset::WithU8((), offset)) => {
let bx =
self.get_register(&Register::General(GeneralRegister::B, RegisterSubset::All));
bx as usize + *offset as usize
}
EffectiveAddress::Bx(WithOffset::WithU16((), offset)) => {
let bx =
self.get_register(&Register::General(GeneralRegister::B, RegisterSubset::All));
bx as usize + *offset as usize
}
EffectiveAddress::Direct(addr) => *addr as usize,
EffectiveAddress::BasePointer(offset) => {
let bp = self.get_register(&Register::Special(SpecialRegister::BasePointer));
bp as usize + *offset as usize
}
EffectiveAddress::BasePointerWide(offset) => {
let bp = self.get_register(&Register::Special(SpecialRegister::BasePointer));
bp as usize + *offset as usize
}
}
}
fn step_mov(&mut self, instruction: &MoveInstruction) -> String {
let preamble = format!("{}", instruction);
let description = match &instruction {
MoveInstruction::RegRegMove(_) => todo!(),
MoveInstruction::RegMemMove(_) => todo!(),
MoveInstruction::MemRegMove(_) => todo!(),
MoveInstruction::RegRegMove(mov) => {
let value = self.get_register(&mov.source);
self.set_register(&mov.dest, value)
}
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)
}
}
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)
} else {
let value = self.get_memory_byte(self.resolve_eaddr(&mov.source));
self.set_register(&mov.dest, value as u16)
}
}
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::ImmediateToRegisterOrMemory(mov) => match mov {
ImmediateToRegisterOrMemory::Byte(_, _) => todo!(),
ImmediateToRegisterOrMemory::Word(_, _) => todo!(),
MoveInstruction::ImmediateToMemory(mov) => match mov {
ImmediateToMemory::Byte(addr, value) => {
let dest = self.resolve_eaddr(addr);
self.set_memory_byte(dest, *value)
}
ImmediateToMemory::Word(addr, value) => {
let dest = self.resolve_eaddr(addr);
self.set_memory_word(dest, *value)
}
},
MoveInstruction::MemoryToAccumulator(_) => todo!(),
MoveInstruction::AccumulatorToMemory(_) => todo!(),
MoveInstruction::MemoryToAccumulator(mov) => {
if mov.is_wide {
self.set_register(
&Register::General(GeneralRegister::A, RegisterSubset::All),
self.get_memory_word(mov.address as usize),
)
} else {
self.set_register(
&Register::General(
GeneralRegister::A,
RegisterSubset::Subset(ByteRegisterSubset::Low),
),
self.get_memory_byte(mov.address as usize) as u16,
)
}
}
MoveInstruction::AccumulatorToMemory(mov) => {
if mov.is_wide {
self.set_memory_word(
mov.address as usize,
self.get_register(&Register::General(
GeneralRegister::A,
RegisterSubset::All,
)),
)
} else {
self.set_memory_byte(
mov.address as usize,
self.get_register(&Register::General(
GeneralRegister::A,
RegisterSubset::Subset(ByteRegisterSubset::Low),
)) as u8,
)
}
}
MoveInstruction::SegmentToMemory(mov) => {
let v = self.get_segment(mov.source);
let dest = self.resolve_eaddr(&mov.dest);
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)
}
MoveInstruction::SegmentToRegister(mov) => {
let v = self.get_segment(mov.source);
self.set_register(&mov.dest, v)
}
MoveInstruction::RegisterToSegment(mov) => {
let value = self.get_register(&mov.source);
self.set_segment(mov.dest, value)
}
};
format!("{} ; {}", preamble, description)
}
@@ -241,6 +487,23 @@ impl Computer {
))
}
for r in [
SegmentRegister::Extra,
SegmentRegister::Code,
SegmentRegister::Stack,
SegmentRegister::Data,
] {
let value = self.get_segment(r);
if value != 0 {
result.push_str(&format!(
"{}: {} ({})\n",
r,
Self::display_big(value),
value
));
}
}
result
}
}

View File

@@ -24,11 +24,17 @@ where
#[derive(Eq, PartialEq, Debug, Hash, Clone)]
pub enum EffectiveAddress {
/// An offset from the contents of the (BX/BP) register plus the contents of the (SI/DI) register.
Sum(WithOffset<(Base, SourceDest)>),
/// An offset from the contents of the SI or DI register.
SpecifiedIn(WithOffset<SourceDest>),
/// An offset from the contents of the BX register.
Bx(WithOffset<()>),
/// A literal index into memory.
Direct(u16),
/// An offset from the contents of the BP register.
BasePointer(u8),
/// An offset from the contents of the BP register.
BasePointerWide(u16),
}

View File

@@ -8,10 +8,11 @@ use crate::{
effective_address::EffectiveAddress,
jump_instruction::Jump,
move_instruction::{
AccumulatorToMemory, ImmediateToRegister, ImmediateToRegisterOrMemory, MemRegMove,
MemoryToAccumulator, MoveInstruction, RegMemMove, RegRegMove,
AccumulatorToMemory, ImmediateToMemory, ImmediateToRegister, MemRegMove,
MemoryToAccumulator, MemoryToSegment, MoveInstruction, RegMemMove, RegRegMove,
RegisterToSegment, SegmentToMemory, SegmentToRegister,
},
register::Register,
register::{Register, SegmentRegister},
trivia_instruction::TriviaInstruction,
};
@@ -263,17 +264,55 @@ impl Instruction<i8> {
let data_low = bytes.next().unwrap();
if w == 1 {
let data_high = bytes.next().unwrap() as u16 * 256;
Some(Instruction::Move(
MoveInstruction::ImmediateToRegisterOrMemory(
ImmediateToRegisterOrMemory::Word(dest, data_high + data_low as u16),
),
))
Some(Instruction::Move(MoveInstruction::ImmediateToMemory(
ImmediateToMemory::Word(dest, data_high + data_low as u16),
)))
} else {
Some(Instruction::Move(
MoveInstruction::ImmediateToRegisterOrMemory(
ImmediateToRegisterOrMemory::Byte(dest, data_low),
),
))
Some(Instruction::Move(MoveInstruction::ImmediateToMemory(
ImmediateToMemory::Byte(dest, data_low),
)))
}
} else if b == 0b10001110 {
// Register/memory to segment
let mod_sr_rm = bytes.next().unwrap();
let mode = mod_sr_rm / 64;
let sr = SegmentRegister::of_byte((mod_sr_rm & 0b00011000) / 8);
let rm = mod_sr_rm % 8;
if mode == 3 {
let rm = Register::of_id(rm, true);
Some(Instruction::Move(MoveInstruction::RegisterToSegment(
RegisterToSegment {
dest: sr,
source: rm,
},
)))
} else {
let source = EffectiveAddress::of_mode_rm(mode, rm, bytes);
Some(Instruction::Move(MoveInstruction::MemoryToSegment(
MemoryToSegment { source, dest: sr },
)))
}
} else if b == 0b10001100 {
// Segment to register/memory
let mod_sr_rm = bytes.next().unwrap();
let mode = mod_sr_rm / 64;
let sr = SegmentRegister::of_byte((mod_sr_rm & 0b00011000) / 8);
let rm = mod_sr_rm % 8;
if mode == 3 {
let rm = Register::of_id(rm, true);
Some(Instruction::Move(MoveInstruction::SegmentToRegister(
SegmentToRegister {
source: sr,
dest: rm,
},
)))
} else {
let dest = EffectiveAddress::of_mode_rm(mode, rm, bytes);
Some(Instruction::Move(MoveInstruction::SegmentToMemory(
SegmentToMemory { dest, source: sr },
)))
}
} else if (b & 0b11000100) == 0b00000000u8 {
// Arithmetic instruction, reg/memory with register to either

View File

@@ -1,3 +1,4 @@
mod arithmetic_expression;
pub mod arithmetic_instruction;
pub mod assembly;
pub mod computer;

View File

@@ -2,7 +2,7 @@ use std::fmt::Display;
use crate::{
effective_address::EffectiveAddress,
register::{ByteRegisterSubset, Register, RegisterSubset},
register::{ByteRegisterSubset, Register, RegisterSubset, SegmentRegister},
};
#[derive(Eq, PartialEq, Debug, Hash, Clone)]
@@ -184,23 +184,23 @@ impl Display for ImmediateToRegister {
}
#[derive(Eq, PartialEq, Debug, Hash, Clone)]
pub enum ImmediateToRegisterOrMemory {
pub enum ImmediateToMemory {
Byte(EffectiveAddress, u8),
Word(EffectiveAddress, u16),
}
impl ImmediateToRegisterOrMemory {
impl ImmediateToMemory {
fn to_bytes(&self) -> Vec<u8> {
let mut result = Vec::<u8>::with_capacity(3);
let opcode = 0b11000110u8;
match self {
ImmediateToRegisterOrMemory::Byte(address, data) => {
ImmediateToMemory::Byte(address, data) => {
result.push(opcode);
address.push(0, &mut result);
result.push(*data);
}
ImmediateToRegisterOrMemory::Word(address, data) => {
ImmediateToMemory::Word(address, data) => {
result.push(opcode + 1);
address.push(0, &mut result);
result.push((data % 256) as u8);
@@ -246,6 +246,76 @@ impl AccumulatorToMemory {
}
}
#[derive(Eq, PartialEq, Debug, Hash, Clone)]
pub struct SegmentToMemory {
pub source: SegmentRegister,
pub dest: EffectiveAddress,
}
impl SegmentToMemory {
fn to_bytes(&self) -> Vec<u8> {
let mut result = Vec::with_capacity(4);
result.push(0b10001100);
EffectiveAddress::push(&self.dest, self.source as u8, &mut result);
result
}
}
#[derive(Eq, PartialEq, Debug, Hash, Clone)]
pub struct MemoryToSegment {
pub dest: SegmentRegister,
pub source: EffectiveAddress,
}
impl MemoryToSegment {
fn to_bytes(&self) -> Vec<u8> {
let mut result = Vec::with_capacity(4);
result.push(0b10001110);
EffectiveAddress::push(&self.source, self.dest as u8, &mut result);
result
}
}
#[derive(Eq, PartialEq, Debug, Hash, Clone)]
pub struct SegmentToRegister {
pub source: SegmentRegister,
pub dest: Register,
}
impl SegmentToRegister {
fn to_bytes(&self) -> Vec<u8> {
let mode = 0b11u8;
let sr = self.source as u8;
let (rm, is_wide) = self.dest.to_id();
if !is_wide {
panic!("Tried to move a segment register to a byte register");
}
vec![0b10001100, mode * 64 + sr * 8 + rm]
}
}
#[derive(Eq, PartialEq, Debug, Hash, Clone)]
pub struct RegisterToSegment {
pub dest: SegmentRegister,
pub source: Register,
}
impl RegisterToSegment {
fn to_bytes(&self) -> Vec<u8> {
let mode = 0b11u8;
let sr = self.dest as u8;
let (rm, is_wide) = self.source.to_id();
if !is_wide {
panic!("Tried to move a byte register to a segment register");
}
vec![0b10001110, mode * 64 + sr * 8 + rm]
}
}
#[derive(Eq, PartialEq, Debug, Hash, Clone)]
pub enum MoveInstruction {
/// Move a value from one register to another
@@ -257,11 +327,15 @@ pub enum MoveInstruction {
/// Load a literal value into a register
ImmediateToRegister(ImmediateToRegister),
/// Load a literal value into a register or into memory
ImmediateToRegisterOrMemory(ImmediateToRegisterOrMemory),
ImmediateToMemory(ImmediateToMemory),
/// Load a value from memory into the accumulator
MemoryToAccumulator(MemoryToAccumulator),
/// Store a value into memory from the accumulator
AccumulatorToMemory(AccumulatorToMemory),
SegmentToMemory(SegmentToMemory),
MemoryToSegment(MemoryToSegment),
SegmentToRegister(SegmentToRegister),
RegisterToSegment(RegisterToSegment),
}
impl Display for MoveInstruction {
@@ -279,14 +353,12 @@ impl Display for MoveInstruction {
MoveInstruction::ImmediateToRegister(instruction) => {
f.write_fmt(format_args!("mov {}", instruction))
}
MoveInstruction::ImmediateToRegisterOrMemory(ImmediateToRegisterOrMemory::Byte(
address,
value,
)) => f.write_fmt(format_args!("mov {}, {}", address, value)),
MoveInstruction::ImmediateToRegisterOrMemory(ImmediateToRegisterOrMemory::Word(
address,
value,
)) => f.write_fmt(format_args!("mov {}, {}", address, value)),
MoveInstruction::ImmediateToMemory(ImmediateToMemory::Byte(address, value)) => {
f.write_fmt(format_args!("mov {}, {}", address, value))
}
MoveInstruction::ImmediateToMemory(ImmediateToMemory::Word(address, value)) => {
f.write_fmt(format_args!("mov {}, {}", address, value))
}
MoveInstruction::MemoryToAccumulator(instruction) => f.write_fmt(format_args!(
"mov a{}, [{}]",
if instruction.is_wide { 'x' } else { 'l' },
@@ -297,6 +369,18 @@ impl Display for MoveInstruction {
instruction.address,
if instruction.is_wide { 'x' } else { 'l' }
)),
MoveInstruction::SegmentToMemory(mov) => {
f.write_fmt(format_args!("mov {}, {}", mov.dest, mov.source))
}
MoveInstruction::SegmentToRegister(mov) => {
f.write_fmt(format_args!("mov {}, {}", mov.dest, mov.source))
}
MoveInstruction::RegisterToSegment(mov) => {
f.write_fmt(format_args!("mov {}, {}", mov.dest, mov.source))
}
MoveInstruction::MemoryToSegment(mov) => {
f.write_fmt(format_args!("mov {}, {}", mov.dest, mov.source))
}
}
}
}
@@ -307,10 +391,14 @@ impl MoveInstruction {
MoveInstruction::RegMemMove(mov) => mov.to_bytes(),
MoveInstruction::MemRegMove(mov) => mov.to_bytes(),
MoveInstruction::ImmediateToRegister(mov) => mov.to_bytes(),
MoveInstruction::ImmediateToRegisterOrMemory(mov) => mov.to_bytes(),
MoveInstruction::ImmediateToMemory(mov) => mov.to_bytes(),
MoveInstruction::MemoryToAccumulator(mov) => mov.to_bytes(),
MoveInstruction::AccumulatorToMemory(mov) => mov.to_bytes(),
MoveInstruction::RegRegMove(mov) => mov.to_bytes(),
MoveInstruction::MemoryToSegment(mov) => mov.to_bytes(),
MoveInstruction::SegmentToMemory(mov) => mov.to_bytes(),
MoveInstruction::SegmentToRegister(mov) => mov.to_bytes(),
MoveInstruction::RegisterToSegment(mov) => mov.to_bytes(),
}
}
}

View File

@@ -104,6 +104,37 @@ impl Display for RegisterSubset {
}
}
#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy)]
pub enum SegmentRegister {
Code = 1,
Data = 3,
Stack = 2,
Extra = 0,
}
impl Display for SegmentRegister {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SegmentRegister::Code => f.write_str("cs"),
SegmentRegister::Data => f.write_str("ds"),
SegmentRegister::Stack => f.write_str("ss"),
SegmentRegister::Extra => f.write_str("es"),
}
}
}
impl SegmentRegister {
pub const fn of_byte(b: u8) -> SegmentRegister {
match b {
0 => SegmentRegister::Extra,
1 => SegmentRegister::Code,
2 => SegmentRegister::Stack,
3 => SegmentRegister::Data,
_ => concat_panic!("Bad byte for stack register: {}", b),
}
}
}
#[derive(Eq, PartialEq, Debug, Hash, Clone)]
pub enum Register {
General(GeneralRegister, RegisterSubset),

View File

@@ -21,19 +21,24 @@ mod test_computer {
let decoded = Program::of_bytes(input_bytecode.as_ref().iter().cloned());
let mut trace = "".to_owned();
let mut trace: Vec<String> = vec![];
for instruction in decoded.instructions {
trace.push_str(&computer.step(&instruction));
trace.push('\n');
trace.push(computer.step(&instruction));
}
trace.push_str("\nFinal registers:\n");
trace.push_str(&computer.dump_register_state());
trace.push("".to_owned());
trace.push("Final registers:".to_owned());
for line in computer.dump_register_state().lines() {
trace.push(line.to_string());
}
trace.push_str("\n");
trace.push("".to_owned());
assert_eq!(trace, clean_trace(expected_trace))
assert_eq!(
trace,
clean_trace(expected_trace).lines().collect::<Vec<_>>()
)
}
#[test]
@@ -44,4 +49,121 @@ mod test_computer {
include_str!("../../computer_enhance/perfaware/part1/listing_0043_immediate_movs.txt");
test_sim(input_bytecode, expected_trace)
}
#[test]
fn test_register_movs() {
let input_bytecode =
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)
}
#[test]
fn test_challenge_register_movs() {
let input_bytecode = include_bytes!(
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs"
);
let expected_trace = include_str!(
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs.txt"
);
test_sim(input_bytecode, expected_trace)
}
/*
#[test]
fn test_add_sub_cmp() {
let input_bytecode =
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)
}
#[test]
fn test_challenge_flags() {
let input_bytecode =
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)
}
#[test]
fn test_ip_register() {
let input_bytecode =
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)
}
#[test]
fn test_conditional_jumps() {
let input_bytecode =
include_bytes!("../../computer_enhance/perfaware/part1/listing_0049_conditional_jumps");
let expected_trace = include_str!(
"../../computer_enhance/perfaware/part1/listing_0049_conditional_jumps.txt"
);
test_sim(input_bytecode, expected_trace)
}
#[test]
fn test_challenge_jumps() {
let input_bytecode =
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)
}
#[test]
fn test_memory_mov() {
let input_bytecode =
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)
}
#[test]
fn test_memory_add_loop() {
let input_bytecode =
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)
}
#[test]
fn test_add_loop_challenge() {
let input_bytecode = include_bytes!(
"../../computer_enhance/perfaware/part1/listing_0053_add_loop_challenge"
);
let expected_trace = include_str!(
"../../computer_enhance/perfaware/part1/listing_0053_add_loop_challenge.txt"
);
test_sim(input_bytecode, expected_trace)
}
#[test]
fn test_draw_rectangle() {
let input_bytecode =
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)
}
#[test]
fn test_draw_rectangle_challenge() {
let input_bytecode = include_bytes!(
"../../computer_enhance/perfaware/part1/listing_0055_challenge_rectangle"
);
let expected_trace = include_str!(
"../../computer_enhance/perfaware/part1/listing_0055_challenge_rectangle.txt"
);
test_sim(input_bytecode, expected_trace)
}
*/
}

View File

@@ -10,7 +10,9 @@ mod test_program {
ArithmeticInstruction, ArithmeticInstructionSelect, ArithmeticOperation,
},
assembly,
effective_address::{EffectiveAddress, WithOffset},
instruction::Instruction,
move_instruction::{ImmediateToMemory, MoveInstruction},
program::Program,
register::{GeneralRegister, Register, RegisterSubset},
};
@@ -111,8 +113,8 @@ mod test_program {
{
println!(
"Different instruction. From disassembly: {dis} ({:?}). From our compilation: {compiled} ({:?}).",
compiled_bytes,
dis_bytes
dis_bytes,
compiled_bytes
);
is_different = true;
}
@@ -277,30 +279,27 @@ mod test_program {
test_disassembler(asm, bytecode)
}
/*
We have not yet implemented the segment registers, so this test can't pass.
#[test]
fn test_challenge_register_movs_parser() {
let input_asm = include_str!(
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs.asm"
);
let input_bytecode = include_bytes!(
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs"
);
test_parser(input_asm, input_bytecode)
}
#[test]
fn test_challenge_register_movs_parser() {
let input_asm = include_str!(
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs.asm"
);
let input_bytecode = include_bytes!(
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs"
);
test_parser(input_asm, input_bytecode)
}
#[test]
fn test_challenge_register_movs_disassembler() {
let bytecode = include_bytes!(
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs"
);
let asm = include_str!(
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs.asm"
);
test_disassembler(asm, bytecode)
}
*/
#[test]
fn test_challenge_register_movs_disassembler() {
let bytecode = include_bytes!(
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs"
);
let asm = include_str!(
"../../computer_enhance/perfaware/part1/listing_0045_challenge_register_movs.asm"
);
test_disassembler(asm, bytecode)
}
#[test]
fn test_add_sub_cmp_parser() {
@@ -412,4 +411,122 @@ mod test_program {
allowed.insert((vec![5, 1, 0], vec![131, 192, 1]));
test_disassembler_lax(asm, bytecode, allowed)
}
#[test]
fn test_memory_mov_parser() {
let input_asm =
include_str!("../../computer_enhance/perfaware/part1/listing_0051_memory_mov.asm");
let input_bytecode =
include_bytes!("../../computer_enhance/perfaware/part1/listing_0051_memory_mov");
test_parser(input_asm, input_bytecode)
}
#[test]
fn test_memory_mov_disassembler() {
let bytecode =
include_bytes!("../../computer_enhance/perfaware/part1/listing_0051_memory_mov");
let asm =
include_str!("../../computer_enhance/perfaware/part1/listing_0051_memory_mov.asm");
test_disassembler(asm, bytecode)
}
#[test]
fn test_memory_add_loop_parser() {
let input_asm =
include_str!("../../computer_enhance/perfaware/part1/listing_0052_memory_add_loop.asm");
let input_bytecode =
include_bytes!("../../computer_enhance/perfaware/part1/listing_0052_memory_add_loop");
test_parser(input_asm, input_bytecode)
}
#[test]
fn test_memory_add_loop_disassembler() {
let bytecode =
include_bytes!("../../computer_enhance/perfaware/part1/listing_0052_memory_add_loop");
let asm =
include_str!("../../computer_enhance/perfaware/part1/listing_0052_memory_add_loop.asm");
test_disassembler(asm, bytecode)
}
#[test]
fn test_add_loop_challenge_parser() {
let input_asm = include_str!(
"../../computer_enhance/perfaware/part1/listing_0053_add_loop_challenge.asm"
);
let input_bytecode = include_bytes!(
"../../computer_enhance/perfaware/part1/listing_0053_add_loop_challenge"
);
test_parser(input_asm, input_bytecode)
}
#[test]
fn test_add_loop_challenge_disassembler() {
let bytecode = include_bytes!(
"../../computer_enhance/perfaware/part1/listing_0053_add_loop_challenge"
);
let asm = include_str!(
"../../computer_enhance/perfaware/part1/listing_0053_add_loop_challenge.asm"
);
test_disassembler(asm, bytecode)
}
#[test]
fn test_draw_rectangle_parser() {
let input_asm =
include_str!("../../computer_enhance/perfaware/part1/listing_0054_draw_rectangle.asm");
let input_bytecode =
include_bytes!("../../computer_enhance/perfaware/part1/listing_0054_draw_rectangle");
test_parser(input_asm, input_bytecode)
}
#[test]
fn test_draw_rectangle_disassembler() {
let bytecode =
include_bytes!("../../computer_enhance/perfaware/part1/listing_0054_draw_rectangle");
let asm =
include_str!("../../computer_enhance/perfaware/part1/listing_0054_draw_rectangle.asm");
test_disassembler(asm, bytecode)
}
#[test]
fn test_challenge_rectangle_parser() {
let input_asm = include_str!(
"../../computer_enhance/perfaware/part1/listing_0055_challenge_rectangle.asm"
);
let input_bytecode = include_bytes!(
"../../computer_enhance/perfaware/part1/listing_0055_challenge_rectangle"
);
let mut swaps = HashMap::new();
swaps.insert(
Instruction::Move(MoveInstruction::ImmediateToMemory(ImmediateToMemory::Byte(
EffectiveAddress::Bx(WithOffset::WithU8((), 61 * 4 + 1)),
255,
))),
Instruction::Move(MoveInstruction::ImmediateToMemory(ImmediateToMemory::Byte(
EffectiveAddress::Bx(WithOffset::WithU16((), 61 * 4 + 1)),
255,
))),
);
test_parser_lax(input_asm, input_bytecode, swaps)
}
#[test]
fn test_challenge_rectangle_disassembler() {
let bytecode = include_bytes!(
"../../computer_enhance/perfaware/part1/listing_0055_challenge_rectangle"
);
let asm = include_str!(
"../../computer_enhance/perfaware/part1/listing_0055_challenge_rectangle.asm"
);
let mut allowed = HashSet::new();
// We implemented `mov [bx + 61*4 + 1], 255` using "immediate to memory, 8 bit displacement",
// taking only four bytes;
// in this example, Casey implemented it using "immediate to memory, 16 bit displacement",
// which takes five.
// The manual is explicit that this situation is allowed:
// If the displacement is only a single byte, the 8086 or 8088 automatically sign-extends
// this quantity to 16-bits before using the information in further address calculations.
allowed.insert((vec![198, 71, 245, 255], vec![198, 135, 245, 0, 255]));
test_disassembler_lax(asm, bytecode, allowed)
}
}