From c8a380562a9fef785bfa7370eaef73003b5045f1 Mon Sep 17 00:00:00 2001 From: Patrick Stevens Date: Mon, 10 Apr 2023 20:36:00 +0100 Subject: [PATCH] Challenge movs (#4) --- src/assembly.rs | 124 +++++++++++++++---- src/main.rs | 320 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 330 insertions(+), 114 deletions(-) diff --git a/src/assembly.rs b/src/assembly.rs index d7adf57..05c6443 100644 --- a/src/assembly.rs +++ b/src/assembly.rs @@ -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::(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::(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::(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) } diff --git a/src/main.rs b/src/main.rs index e38f7a4..763f303 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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::::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::::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::::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(mode: u8, rm: u8, bytes: &mut I) -> EffectiveAddress + where + I: Iterator, + { + 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() {