Implement mov simulation (#10)
This commit is contained in:
169
sim_8086/src/arithmetic_expression.rs
Normal file
169
sim_8086/src/arithmetic_expression.rs
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
@@ -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
|
||||
)))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -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(®ister_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(®ister_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
|
||||
}
|
||||
}
|
||||
|
@@ -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),
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -1,3 +1,4 @@
|
||||
mod arithmetic_expression;
|
||||
pub mod arithmetic_instruction;
|
||||
pub mod assembly;
|
||||
pub mod computer;
|
||||
|
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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),
|
||||
|
@@ -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)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user