Challenge movs (#4)

This commit is contained in:
Patrick Stevens
2023-04-10 20:36:00 +01:00
committed by GitHub
parent 23aab8647c
commit c8a380562a
2 changed files with 330 additions and 114 deletions

View File

@@ -10,8 +10,9 @@ use nom::{
use crate::{
register::{ByteRegisterSubset, GeneralRegister, Register, RegisterSubset, SpecialRegister},
Base, EffectiveAddress, ImmediateToRegister, Instruction, MemRegMove, Program, RegMemMove,
RegRegMove, SourceDest, WithOffset,
AccumulatorToMemory, Base, EffectiveAddress, ImmediateToRegister, ImmediateToRegisterOrMemory,
Instruction, MemRegMove, MemoryToAccumulator, Program, RegMemMove, RegRegMove, SourceDest,
WithOffset,
};
fn comment(input: &str) -> IResult<&str, &str> {
@@ -152,14 +153,14 @@ fn absolute_u16(input: &str) -> IResult<&str, u16> {
fn negative_u8(input: &str) -> IResult<&str, u8> {
map_res(
preceded(char('-'), alt((digit1, preceded(tag("byte "), digit1)))),
preceded(ws(char('-')), alt((digit1, preceded(tag("byte "), digit1)))),
|x| str::parse::<u8>(x).map(|x| 255 - x + 1),
)(input)
}
fn negative_u16(input: &str) -> IResult<&str, u16> {
map_res(
preceded(char('-'), alt((digit1, preceded(tag("word "), digit1)))),
preceded(ws(char('-')), alt((digit1, preceded(tag("word "), digit1)))),
|x| str::parse::<u16>(x).map(|x| 65535 - x + 1),
)(input)
}
@@ -184,16 +185,15 @@ fn effective_address(input: &str) -> IResult<&str, EffectiveAddress> {
))))
},
),
// Sum, with positive offset
// Sum, with offset
map_res(
bracketed(tuple((
base,
ws(char('+')),
source_dest,
ws(char('+')),
literal_u8,
alt((preceded(ws(char('+')), absolute_u8), negative_u8)),
))),
|(base, _, source_dest, _, offset)| {
|(base, _, source_dest, offset)| {
Ok::<_, ()>(EffectiveAddress::Sum(WithOffset::WithU8(
(base, source_dest),
offset,
@@ -205,10 +205,9 @@ fn effective_address(input: &str) -> IResult<&str, EffectiveAddress> {
base,
ws(char('+')),
source_dest,
ws(char('+')),
literal_u16,
alt((preceded(ws(char('+')), absolute_u16), negative_u16)),
))),
|(base, _, source_dest, _, offset)| {
|(base, _, source_dest, offset)| {
Ok::<_, ()>(EffectiveAddress::Sum(WithOffset::WithU16(
(base, source_dest),
offset,
@@ -222,8 +221,11 @@ fn effective_address(input: &str) -> IResult<&str, EffectiveAddress> {
)))
}),
map_res(
bracketed(tuple((source_dest, ws(char('+')), absolute_u8))),
|(source_dest, _, offset)| {
bracketed(tuple((
source_dest,
alt((preceded(ws(char('+')), absolute_u8), negative_u8)),
))),
|(source_dest, offset)| {
Ok::<_, ()>(EffectiveAddress::SpecifiedIn(WithOffset::WithU8(
source_dest,
offset,
@@ -231,8 +233,11 @@ fn effective_address(input: &str) -> IResult<&str, EffectiveAddress> {
},
),
map_res(
bracketed(tuple((source_dest, ws(char('+')), absolute_u16))),
|(source_dest, _, offset)| {
bracketed(tuple((
source_dest,
alt((preceded(ws(char('+')), absolute_u16), negative_u16)),
))),
|(source_dest, offset)| {
Ok::<_, ()>(EffectiveAddress::SpecifiedIn(WithOffset::WithU16(
source_dest,
offset,
@@ -244,12 +249,18 @@ fn effective_address(input: &str) -> IResult<&str, EffectiveAddress> {
Ok::<_, ()>(EffectiveAddress::Bx(WithOffset::Basic(())))
}),
map_res(
bracketed(tuple((tag("bx"), ws(char('+')), absolute_u8))),
|(_, _, offset)| Ok::<_, ()>(EffectiveAddress::Bx(WithOffset::WithU8((), offset))),
bracketed(tuple((
tag("bx"),
alt((preceded(ws(char('+')), absolute_u8), negative_u8)),
))),
|(_, offset)| Ok::<_, ()>(EffectiveAddress::Bx(WithOffset::WithU8((), offset))),
),
map_res(
bracketed(tuple((tag("bx"), ws(char('+')), absolute_u16))),
|(_, _, offset)| Ok::<_, ()>(EffectiveAddress::Bx(WithOffset::WithU16((), offset))),
bracketed(tuple((
tag("bx"),
alt((preceded(ws(char('+')), absolute_u16), negative_u16)),
))),
|(_, offset)| Ok::<_, ()>(EffectiveAddress::Bx(WithOffset::WithU16((), offset))),
),
// Direct memory address
map_res(direct_offset, |offset| {
@@ -257,12 +268,18 @@ fn effective_address(input: &str) -> IResult<&str, EffectiveAddress> {
}),
// Offset from base pointer
map_res(
bracketed(tuple((tag("bp"), ws(char('+')), absolute_u8))),
|(_, _, offset)| Ok::<_, ()>(EffectiveAddress::BasePointer(offset)),
bracketed(tuple((
tag("bp"),
alt((preceded(ws(char('+')), absolute_u8), negative_u8)),
))),
|(_, offset)| Ok::<_, ()>(EffectiveAddress::BasePointer(offset)),
),
map_res(
bracketed(tuple((tag("bp"), ws(char('+')), absolute_u16))),
|(_, _, offset)| Ok::<_, ()>(EffectiveAddress::BasePointerWide(offset)),
bracketed(tuple((
tag("bp"),
alt((preceded(ws(char('+')), absolute_u16), negative_u16)),
))),
|(_, offset)| Ok::<_, ()>(EffectiveAddress::BasePointerWide(offset)),
),
// Specific support for [bp], which can't be represented as a simple instruction
map_res(bracketed(tag("bp")), |_| {
@@ -337,8 +354,66 @@ fn immediate_to_register_instruction(input: &str) -> IResult<&str, ImmediateToRe
)(input)
}
fn immediate_to_memory_instruction(input: &str) -> IResult<&str, ImmediateToRegisterOrMemory> {
map_res(
tuple((
terminated(preceded(tag("mov "), effective_address), argument_sep),
alt((
map_res(literal_u8, |x| Ok::<_, ()>(Ok::<_, u16>(x))),
map_res(literal_u16, |x| Ok::<_, ()>(Err::<u8, _>(x))),
)),
)),
|(addr, x)| {
Ok::<_, ()>(match x {
Ok(b) => ImmediateToRegisterOrMemory::Byte(addr, b),
Err(b) => ImmediateToRegisterOrMemory::Word(addr, b),
})
},
)(input)
}
fn memory_to_accumulator_instruction(input: &str) -> IResult<&str, MemoryToAccumulator> {
map_res(
preceded(
tag("mov a"),
tuple((
terminated(alt((char('h'), char('x'))), argument_sep),
bracketed(literal_u16),
)),
),
|(acc, address)| {
let is_wide = acc == 'x';
Ok::<_, ()>(MemoryToAccumulator { address, is_wide })
},
)(input)
}
fn accumulator_to_memory_instruction(input: &str) -> IResult<&str, AccumulatorToMemory> {
map_res(
preceded(
tag("mov "),
tuple((
terminated(bracketed(literal_u16), tuple((argument_sep, char('a')))),
alt((char('h'), char('x'))),
)),
),
|(address, acc)| {
let is_wide = acc == 'x';
Ok::<_, ()>(AccumulatorToMemory { address, is_wide })
},
)(input)
}
fn instruction(input: &str) -> IResult<&str, Instruction> {
alt((
// This must come before MemRegMove.
map_res(memory_to_accumulator_instruction, |v| {
Ok::<_, ()>(Instruction::MemoryToAccumulator(v))
}),
// This must come before RegMemMove.
map_res(accumulator_to_memory_instruction, |v| {
Ok::<_, ()>(Instruction::AccumulatorToMemory(v))
}),
map_res(reg_reg_move_instruction, |v| {
Ok::<_, ()>(Instruction::RegRegMove(v))
}),
@@ -351,6 +426,9 @@ fn instruction(input: &str) -> IResult<&str, Instruction> {
map_res(immediate_to_register_instruction, |v| {
Ok::<_, ()>(Instruction::ImmediateToRegister(v))
}),
map_res(immediate_to_memory_instruction, |v| {
Ok::<_, ()>(Instruction::ImmediateToRegisterOrMemory(v))
}),
))(input)
}

View File

@@ -90,7 +90,15 @@ impl Display for EffectiveAddress {
f.write_fmt(format_args!("[{} + {}i + {}]", base, source_dest, offset))
}
},
EffectiveAddress::SpecifiedIn(register) => f.write_fmt(format_args!("{}", register)),
EffectiveAddress::SpecifiedIn(WithOffset::Basic(source_dest)) => {
f.write_fmt(format_args!("[{}i]", source_dest))
}
EffectiveAddress::SpecifiedIn(WithOffset::WithU8(source_dest, offset)) => {
f.write_fmt(format_args!("[{}i + {}]", source_dest, offset))
}
EffectiveAddress::SpecifiedIn(WithOffset::WithU16(source_dest, offset)) => {
f.write_fmt(format_args!("[{}i + {}]", source_dest, offset))
}
EffectiveAddress::Bx(offset) => match offset {
WithOffset::Basic(()) => f.write_str("bx"),
WithOffset::WithU8((), offset) => f.write_fmt(format_args!("[bx + {}]", offset)),
@@ -123,6 +131,37 @@ pub enum ImmediateToRegister {
Wide(Register, u16),
}
impl Display for ImmediateToRegister {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ImmediateToRegister::Byte(dest, value) => {
f.write_fmt(format_args!("{}, {}", dest, value))
}
ImmediateToRegister::Wide(dest, value) => {
f.write_fmt(format_args!("{}, {}", dest, value))
}
}
}
}
#[derive(Eq, PartialEq, Debug)]
pub enum ImmediateToRegisterOrMemory {
Byte(EffectiveAddress, u8),
Word(EffectiveAddress, u16),
}
#[derive(Eq, PartialEq, Debug)]
pub struct MemoryToAccumulator {
address: u16,
is_wide: bool,
}
#[derive(Eq, PartialEq, Debug)]
pub struct AccumulatorToMemory {
address: u16,
is_wide: bool,
}
#[derive(Eq, PartialEq, Debug)]
pub enum Instruction {
/// Move a value from one register to another
@@ -133,6 +172,12 @@ pub enum Instruction {
MemRegMove(MemRegMove),
/// Load a literal value into a register
ImmediateToRegister(ImmediateToRegister),
/// Load a literal value into a register or into memory
ImmediateToRegisterOrMemory(ImmediateToRegisterOrMemory),
/// Load a value from memory into the accumulator
MemoryToAccumulator(MemoryToAccumulator),
/// Store a value into memory from the accumulator
AccumulatorToMemory(AccumulatorToMemory),
}
impl Display for Instruction {
@@ -147,12 +192,27 @@ impl Display for Instruction {
Instruction::MemRegMove(mov) => {
f.write_fmt(format_args!("mov {}, {}", mov.dest, mov.source))
}
Instruction::ImmediateToRegister(ImmediateToRegister::Byte(dest, value)) => {
f.write_fmt(format_args!("mov {}, {}", dest, value))
}
Instruction::ImmediateToRegister(ImmediateToRegister::Wide(dest, value)) => {
f.write_fmt(format_args!("mov {}, {}", dest, value))
Instruction::ImmediateToRegister(instruction) => {
f.write_fmt(format_args!("mov {}", instruction))
}
Instruction::ImmediateToRegisterOrMemory(ImmediateToRegisterOrMemory::Byte(
address,
value,
)) => f.write_fmt(format_args!("mov {}, {}", address, value)),
Instruction::ImmediateToRegisterOrMemory(ImmediateToRegisterOrMemory::Word(
address,
value,
)) => f.write_fmt(format_args!("mov {}, {}", address, value)),
Instruction::MemoryToAccumulator(instruction) => f.write_fmt(format_args!(
"mov a{}, [{}]",
if instruction.is_wide { 'x' } else { 'l' },
instruction.address
)),
Instruction::AccumulatorToMemory(instruction) => f.write_fmt(format_args!(
"mov [{}], a{}",
instruction.address,
if instruction.is_wide { 'x' } else { 'l' }
)),
}
}
}
@@ -406,6 +466,113 @@ impl Instruction {
}
result
}
Instruction::ImmediateToRegisterOrMemory(mov) => {
let mut result = Vec::<u8>::with_capacity(3);
let opcode = 0b11000110u8;
match mov {
ImmediateToRegisterOrMemory::Byte(address, data) => {
result.push(opcode);
Self::push_effective_address(address, 0, &mut result);
result.push(*data);
}
ImmediateToRegisterOrMemory::Word(address, data) => {
result.push(opcode + 1);
Self::push_effective_address(address, 0, &mut result);
result.push((data % 256) as u8);
result.push((data / 256) as u8);
}
}
result
}
Instruction::MemoryToAccumulator(mov) => {
let mut result = Vec::<u8>::with_capacity(3);
result.push(0b10100000u8 + if mov.is_wide { 1 } else { 0 });
result.push((mov.address % 256) as u8);
result.push((mov.address / 256) as u8);
result
}
Instruction::AccumulatorToMemory(mov) => {
let mut result = Vec::<u8>::with_capacity(3);
result.push(0b10100010u8 + if mov.is_wide { 1 } else { 0 });
result.push((mov.address % 256) as u8);
result.push((mov.address / 256) as u8);
result
}
}
}
fn mode_rm_to_eaddr<I>(mode: u8, rm: u8, bytes: &mut I) -> EffectiveAddress
where
I: Iterator<Item = u8>,
{
let source_dest = if rm % 2 == 0 {
SourceDest::Source
} else {
SourceDest::Dest
};
let base = if (rm / 2) % 2 == 0 {
Base::Bx
} else {
Base::Bp
};
let displacement_low = if rm == 6 || mode > 0 {
bytes.next().expect("required an 8-bit displacement")
} else {
0
};
let displacement_high = if (rm == 6 && mode == 0) || mode == 2 {
let high = bytes.next().expect("required a 16-bit displacement");
(high as u16) * 256 + (displacement_low as u16)
} else {
0
};
if rm < 4 {
match mode {
0 => EffectiveAddress::Sum(WithOffset::Basic((base, source_dest))),
1 => {
EffectiveAddress::Sum(WithOffset::WithU8((base, source_dest), displacement_low))
}
2 => EffectiveAddress::Sum(WithOffset::WithU16(
(base, source_dest),
displacement_high,
)),
_ => panic!("Maths is wrong, got bad mode: {}", mode),
}
} else if rm < 6 {
match mode {
0 => EffectiveAddress::SpecifiedIn(WithOffset::Basic(source_dest)),
1 => {
EffectiveAddress::SpecifiedIn(WithOffset::WithU8(source_dest, displacement_low))
}
2 => EffectiveAddress::SpecifiedIn(WithOffset::WithU16(
source_dest,
displacement_high,
)),
_ => panic!("Maths is wrong, got bad mode: {}", mode),
}
} else if rm == 6 {
match mode {
0 => EffectiveAddress::Direct(displacement_high),
1 => EffectiveAddress::BasePointer(displacement_low),
2 => EffectiveAddress::BasePointerWide(displacement_high),
_ => panic!("Maths is wrong, got bad mode: {}", mode),
}
} else {
assert_eq!(rm, 7);
match mode {
0 => EffectiveAddress::Bx(WithOffset::Basic(())),
1 => EffectiveAddress::Bx(WithOffset::WithU8((), displacement_low)),
2 => EffectiveAddress::Bx(WithOffset::WithU16((), displacement_high)),
_ => panic!("Maths is wrong, got bad mode: {}", mode),
}
}
}
@@ -439,73 +606,7 @@ impl Instruction {
};
Some(Instruction::RegRegMove(instruction))
} else {
let source_dest = if rm % 2 == 0 {
SourceDest::Source
} else {
SourceDest::Dest
};
let base = if (rm / 2) % 2 == 0 {
Base::Bx
} else {
Base::Bp
};
let displacement_low = if rm == 6 || mode > 0 {
bytes.next().expect("required an 8-bit displacement")
} else {
0
};
let displacement_high = if (rm == 6 && mode == 0) || mode == 2 {
let high = bytes.next().expect("required a 16-bit displacement");
(high as u16) * 256 + (displacement_low as u16)
} else {
0
};
let mem_location = if rm < 4 {
match mode {
0 => EffectiveAddress::Sum(WithOffset::Basic((base, source_dest))),
1 => EffectiveAddress::Sum(WithOffset::WithU8(
(base, source_dest),
displacement_low,
)),
2 => EffectiveAddress::Sum(WithOffset::WithU16(
(base, source_dest),
displacement_high,
)),
_ => panic!("Maths is wrong, got bad mode: {}", mode),
}
} else if rm < 6 {
match mode {
0 => EffectiveAddress::SpecifiedIn(WithOffset::Basic(source_dest)),
1 => EffectiveAddress::SpecifiedIn(WithOffset::WithU8(
source_dest,
displacement_low,
)),
2 => EffectiveAddress::SpecifiedIn(WithOffset::WithU16(
source_dest,
displacement_high,
)),
_ => panic!("Maths is wrong, got bad mode: {}", mode),
}
} else if rm == 6 {
match mode {
0 => EffectiveAddress::Direct(displacement_high),
1 => EffectiveAddress::BasePointer(displacement_low),
2 => EffectiveAddress::BasePointerWide(displacement_high),
_ => panic!("Maths is wrong, got bad mode: {}", mode),
}
} else {
assert!(rm == 7);
match mode {
0 => EffectiveAddress::Bx(WithOffset::Basic(())),
1 => EffectiveAddress::Bx(WithOffset::WithU8((), displacement_low)),
2 => {
EffectiveAddress::Bx(WithOffset::WithU16((), displacement_high))
}
_ => panic!("Maths is wrong, got bad mode: {}", mode),
}
};
let mem_location = Self::mode_rm_to_eaddr(mode, rm, bytes);
if d == 0 {
Some(Instruction::RegMemMove(RegMemMove {
source: reg,
@@ -540,6 +641,45 @@ impl Instruction {
reg, next_low,
)))
}
} else if (b & 0b11111110) == 0b10100000 {
// Memory to accumulator
let w = b % 2;
let addr_low = bytes.next().unwrap() as u16;
let addr_high = bytes.next().unwrap() as u16 * 256;
Some(Instruction::MemoryToAccumulator(MemoryToAccumulator {
address: addr_high + addr_low,
is_wide: w == 1,
}))
} else if (b & 0b11111110) == 0b10100010 {
// Accumulator to memory
let w = b % 2;
let addr_low = bytes.next().unwrap() as u16;
let addr_high = bytes.next().unwrap() as u16 * 256;
Some(Instruction::AccumulatorToMemory(AccumulatorToMemory {
address: addr_high + addr_low,
is_wide: w == 1,
}))
} else if (b & 0b11111110) == 0b11000110 {
// Immediate to register/memory
let w = b % 2;
let mod_reg_rm = bytes.next().unwrap();
let mode = (mod_reg_rm & 0b11000000) / 64;
let reg = (mod_reg_rm & 0b00111000) / 8;
let rm = mod_reg_rm & 0b00000111;
assert_eq!(reg, 0);
let dest = Self::mode_rm_to_eaddr(mode, rm, bytes);
let data_low = bytes.next().unwrap();
if w == 1 {
let data_high = bytes.next().unwrap() as u16 * 256;
Some(Instruction::ImmediateToRegisterOrMemory(
ImmediateToRegisterOrMemory::Word(dest, data_high + data_low as u16),
))
} else {
Some(Instruction::ImmediateToRegisterOrMemory(
ImmediateToRegisterOrMemory::Byte(dest, data_low),
))
}
} else {
panic!("Unrecognised instruction byte: {}", b)
}
@@ -805,25 +945,23 @@ mod test_program {
test_disassembler(asm, bytecode)
}
/*
#[test]
fn test_register_challenge_movs_parser() {
let input_asm =
include_str!("../computer_enhance/perfaware/part1/listing_0040_challenge_movs.asm");
let input_bytecode =
include_bytes!("../computer_enhance/perfaware/part1/listing_0040_challenge_movs");
test_parser(input_asm, input_bytecode)
}
#[test]
fn test_register_challenge_movs_parser() {
let input_asm =
include_str!("../computer_enhance/perfaware/part1/listing_0040_challenge_movs.asm");
let input_bytecode =
include_bytes!("../computer_enhance/perfaware/part1/listing_0040_challenge_movs");
test_parser(input_asm, input_bytecode)
}
#[test]
fn test_register_challenge_movs_disassembler() {
let bytecode =
include_bytes!("../computer_enhance/perfaware/part1/listing_0040_challenge_movs");
let asm =
include_str!("../computer_enhance/perfaware/part1/listing_0040_challenge_movs.asm");
test_disassembler(asm, bytecode)
}
*/
#[test]
fn test_register_challenge_movs_disassembler() {
let bytecode =
include_bytes!("../computer_enhance/perfaware/part1/listing_0040_challenge_movs");
let asm =
include_str!("../computer_enhance/perfaware/part1/listing_0040_challenge_movs.asm");
test_disassembler(asm, bytecode)
}
#[test]
fn mem_reg_move_to_bytes() {