use super::instruction_set::{Instruction, InstructionType}; use auto_from::From; pub use logicsim::data_structures::BitIter; #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub enum PointerType { RAM, ROM, } #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct Pointer(pub u8, pub PointerType); #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct Label(pub usize); #[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct InstructionWithPtr { ty: InstructionType, ptr: Pointer, } #[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct InstructionWithLabel { ty: InstructionType, label: Label, } pub trait IntoInstructionWithPointer { fn with_ptr(self, ptr: Pointer) -> InstructionWithPtr; } impl IntoInstructionWithPointer for InstructionType { fn with_ptr(self, ptr: Pointer) -> InstructionWithPtr { InstructionWithPtr { ty: self, ptr } } } pub trait IntoInstructionWithLabel { fn with_label(self, label: Label) -> InstructionWithLabel; } impl IntoInstructionWithLabel for InstructionType { fn with_label(self, label: Label) -> InstructionWithLabel { InstructionWithLabel { ty: self, label } } } #[derive(From, Debug)] pub enum Directive { Instruction(Instruction), InstructionWithPtr(InstructionWithPtr), InstructionWithLabel(InstructionWithLabel), Data(u16), } impl From for Directive { fn from(ty: InstructionType) -> Self { ty.with_0().into() } } pub fn byte_iter_to_directives>(iter: I) -> Vec { let mut out = Vec::new(); for bytes in iter.collect::>().chunks(2) { let word = bytes[0] as u16 | ((bytes.get(1).copied().unwrap_or(0) as u16) << 8); out.push(Directive::Data(word)) } out } macro_rules! assemble_inner { ($vec:ident, $labels:ident, label $label:ident; $($rest:tt)*) => { let $label = Label($labels.len()); $labels.push(0); assemble_inner!($vec, $labels, $($rest)*); }; ($vec:ident, $labels:ident, data#$label:ident : $val:expr; $($rest:tt)*) => { $labels[$label.0] = ($vec.len() * 2) as u8; $vec.append(&mut byte_iter_to_directives($val)); assemble_inner!($vec, $labels, $($rest)*); }; ($vec:ident, $labels:ident, $label:ident : $val:expr; $($rest:tt)*) => { $labels[$label.0] = ($vec.len() * 2) as u8; $vec.push($val.into()); assemble_inner!($vec, $labels, $($rest)*); }; ($vec:ident, $labels:ident, $ptr:ident =ram= $val:expr; $($rest:tt)*) => { let $ptr = Pointer($val, PointerType::RAM); assemble_inner!($vec, $labels, $($rest)*); }; ($vec:ident, $labels:ident, $ptr:ident =rom= $val:expr; $($rest:tt)*) => { let $ptr = Pointer($val, PointerType::ROM); assemble_inner!($vec, $labels, $($rest)*); }; ($vec:ident, $labels:ident, $val:expr; $($rest:tt)*) => { $vec.push($val.into()); assemble_inner!($vec, $labels, $($rest)*); }; ($vec:ident, $labels:ident, ) => { }; } macro_rules! assemble { ($($all:tt)*) => { { let mut directives = Vec::::new(); #[allow(unused_mut)] let mut labels = Vec::::new(); assemble_inner!(directives, labels, $($all)*); assemble(directives, labels) } }; } pub fn assemble(directives: Vec, labels: Vec) -> Vec { let ram_mask = 1u8 << 7; let mut out = Vec::new(); for directive in directives { match directive { Directive::Instruction(instruction) => out.push(instruction.into()), Directive::InstructionWithPtr(InstructionWithPtr { ty, ptr }) => out.push( Instruction { ty, data: ptr.0 | if matches!(ptr.1, PointerType::RAM) { ram_mask } else { 0 }, } .into(), ), Directive::InstructionWithLabel(InstructionWithLabel { ty, label }) => out.push( Instruction { ty, data: labels[label.0], } .into(), ), Directive::Data(data) => out.push(data), } } assert!( out.len() * 2 <= 128, "Your program is too big! len:{}", out.len() * 2 ); out }