use std::fmt;
use std::fmt::Display;
use self::prelude::*;
use crate::assembled::prelude::*;
use crate::instruction::prelude::*;
pub mod prelude {
pub use super::DisassembledRom;
pub use super::RomBlock;
pub use super::RomBlockContent::*;
}
#[test]
fn test_assemble() {
let disassembled = DisassembledRom::example();
let _assembled = disassembled.assemble();
}
#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct DisassembledRom {
blocks: Vec<RomBlock>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RomBlock {
pub content: RomBlockContent,
pub address: Option<u16>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum RomBlockContent {
Code(Vec<Instruction>),
Data(Vec<u8>),
}
impl DisassembledRom {
pub fn example() -> DisassembledRom {
DisassembledRom {
blocks: vec![RomBlock {
address: None,
content: Code(vec![INC(A), INC(A), INC(B), INC(C)]),
}],
}
}
pub fn assemble(&self) -> AssembledRom {
let mut bytes: Vec<RomByte> = vec![];
for block in self.blocks.iter() {
let current_length = bytes.len();
if let Some(address) = block.address {
let address_usize = usize::from(address);
if address_usize < bytes.len() {
panic!(
"target address {:X} is unsatisfiable, we are already at address {:X}",
address, current_length
)
}
for _padding_address in current_length..address_usize {
bytes.push(RomByte {
byte: 0x00,
role: RomByteRole::InstructionStart {
instruction: NOP,
known_jump_destination: false,
},
})
}
}
match &block.content {
Code(instructions) => {
for (i, instruction) in instructions.iter().enumerate() {
let is_first_instruction = i == 0;
for (j, byte) in instruction.to_bytes().iter().enumerate() {
let is_first_byte = j == 0;
bytes.push(RomByte {
byte: *byte,
role: if is_first_byte {
RomByteRole::InstructionStart {
instruction: *instruction,
known_jump_destination: is_first_instruction,
}
} else {
RomByteRole::InstructionRest
},
})
}
}
}
Data(block_bytes) => {
for byte in block_bytes {
bytes.push(RomByte {
byte: *byte,
role: RomByteRole::Unknown,
})
}
}
}
}
AssembledRom::from(bytes)
}
}
impl From<Vec<RomBlock>> for DisassembledRom {
fn from(blocks: Vec<RomBlock>) -> Self {
DisassembledRom { blocks }
}
}
impl From<Vec<RomBlockContent>> for DisassembledRom {
fn from(blocks_contents: Vec<RomBlockContent>) -> Self {
DisassembledRom {
blocks: blocks_contents
.into_iter()
.map(|content| RomBlock {
content,
address: None,
})
.collect(),
}
}
}
impl From<Vec<Instruction>> for DisassembledRom {
fn from(instructions: Vec<Instruction>) -> Self {
vec![Code(instructions)].into()
}
}
impl Display for DisassembledRom {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for block in self.blocks.iter() {
Display::fmt(&block, f)?;
write!(f, "\n")?;
}
Ok(())
}
}
impl Display for RomBlock {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(address) = self.address {
write!(f, "0x{:04X}:\n", address)?;
} else {
write!(f, "0x____:\n")?;
}
match self.content {
Data(ref bytes) => {
let mut n = 0;
for byte in bytes.iter() {
if n == 0 {
write!(f, " DATA 0x")?;
n += 6;
}
write!(f, "{:02X}", byte)?;
n += 2;
if n >= 61 {
write!(f, "\n")?;
n = 0;
}
}
write!(f, "\n")?;
}
Code(ref instructions) => {
for instruction in instructions.iter() {
write!(f, " {}\n", instruction)?;
}
}
}
Ok(())
}
}