carbon-assembler 1.6.7

An assembler for the carbon CPU
use crate::instr::{CarbonASMProgram, CarbonConds, CarbonInstrVariants, CarbonOperand};

struct PageWriter {
    current_page: usize,
    current_page_ptr: usize,
    pages: Vec<Vec<PageOutput>>,
    comments: Vec<(String, usize)>,
}

impl PageWriter {
    pub fn new() -> PageWriter {
        PageWriter {
            current_page: 0,
            current_page_ptr: 0,
            pages: vec![vec![PageOutput::Lit(0); 32]; 32],
            comments: vec![],
        }
    }

    pub fn set_page(&mut self, page: usize) {
        self.current_page = page;
        self.current_page_ptr = 0;
    }

    pub fn write(&mut self, value: u8) {
        self.pages[self.current_page][self.current_page_ptr] = PageOutput::Lit(value);
        self.current_page_ptr += 1;
    }

    pub fn write_comment(&mut self, value: String) {
        self.comments
            .push((value, self.current_page * 32 + self.current_page_ptr));
    }

    pub fn get_pages(self) -> Vec<PageOutput> {
        let mut ret: Vec<PageOutput> = self.pages.into_iter().flatten().collect();
        for (pos, comment) in self.comments.into_iter().enumerate() {
            ret.insert(comment.1 + pos + 1, PageOutput::Comment(comment.0));
        }
        ret
    }
}

#[derive(Debug, Clone)]
pub enum PageOutput {
    Lit(u8),
    Comment(String),
}

pub fn assemble(ast: Vec<CarbonASMProgram>) -> Vec<PageOutput> {
    let mut pages = PageWriter::new();
    for node in ast {
        let mut word = 0;
        match node {
            CarbonASMProgram::Immediate(i) => {
                word = i;
            }
            CarbonASMProgram::Instruction(i) => {
                match i.opcode {
                    CarbonInstrVariants::Nop => word = 0,
                    CarbonInstrVariants::Hlt => word = 0b11111000,
                    CarbonInstrVariants::Add => word |= 0b00001000,
                    CarbonInstrVariants::Sub => word |= 0b00010000,
                    CarbonInstrVariants::Bsb => word |= 0b00011000,
                    CarbonInstrVariants::Or => word |= 0b00100000,
                    CarbonInstrVariants::Nor => word |= 0b00101000,
                    CarbonInstrVariants::And => word |= 0b00110000,
                    CarbonInstrVariants::Nand => word |= 0b00111000,
                    CarbonInstrVariants::Xor => word |= 0b01000000,
                    CarbonInstrVariants::Lia => word |= 0b01001000,
                    CarbonInstrVariants::Ldi => word |= 0b01010000,
                    CarbonInstrVariants::Adr => word |= 0b01011000,
                    CarbonInstrVariants::Rld => word |= 0b01100000,
                    CarbonInstrVariants::Rst => word |= 0b01101000,
                    CarbonInstrVariants::Mst => word |= 0b01110000,
                    CarbonInstrVariants::Mld => word |= 0b01111000,
                    CarbonInstrVariants::Ics => word |= 0b10000000,
                    CarbonInstrVariants::Jid => word |= 0b10001000,
                    CarbonInstrVariants::Brc => word |= 0b10010000,
                    CarbonInstrVariants::Dec => word |= 0b10011000,
                    CarbonInstrVariants::Cmp => word |= 0b10100000,
                    CarbonInstrVariants::Bsr => word |= 0b10101000,
                    CarbonInstrVariants::Bsl => word |= 0b10110000,
                    CarbonInstrVariants::Pst => word |= 0b10111000,
                    CarbonInstrVariants::Pld => word |= 0b11000000,
                    CarbonInstrVariants::Inc => word |= 0b11001000,
                }
                match i.operand {
                    Some(v) => {
                        for operand in v {
                            match operand {
                                CarbonOperand::Cond(c) => word |= write_cond(c),
                                CarbonOperand::Reg(r) => word |= r,
                                CarbonOperand::JmpAddr(a) => {
                                    pages.write(word);
                                    word = a.unwrap();
                                }
                                CarbonOperand::Label(_) => (),
                                _ => unreachable!()
                            }
                        }
                    }
                    None => (),
                }
            }
            CarbonASMProgram::Comment(c) => {
                pages.write_comment(c);
                continue;
            }
            CarbonASMProgram::Label(_) => unreachable!(),
            CarbonASMProgram::PageLabel(n) => {
                pages.set_page(n);
                continue;
            }
            CarbonASMProgram::LabelDeref(_) => unreachable!(),
        }
        pages.write(word);
    }
    pages.get_pages()
}

fn write_cond(cond: CarbonConds) -> u8 {
    cond as u8
}