From ff232562cad02bb951ca8a42d290247fb179ac42 Mon Sep 17 00:00:00 2001 From: Kestrel Date: Fri, 17 Nov 2023 22:08:43 -0800 Subject: [PATCH] Move microcode into build script --- Cargo.lock | 7 + Cargo.toml | 4 + build.rs | 386 +++++++++++++++++++++++++++++++++++++++++++++- microcode.py | 362 ------------------------------------------- src/ctrl_high.rom | Bin 1024 -> 0 bytes src/ctrl_low.rom | Bin 1024 -> 0 bytes src/ctrl_mid.rom | Bin 1024 -> 0 bytes src/emulator.rs | 6 +- src/microcode.asm | 143 +++++++++++++++++ 9 files changed, 541 insertions(+), 367 deletions(-) delete mode 100644 microcode.py delete mode 100644 src/ctrl_high.rom delete mode 100644 src/ctrl_low.rom delete mode 100644 src/ctrl_mid.rom create mode 100644 src/microcode.asm diff --git a/Cargo.lock b/Cargo.lock index 3c32e10..39f0b4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,6 +68,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "async-attributes" version = "1.1.2" @@ -436,6 +442,7 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" name = "fateful" version = "0.1.0" dependencies = [ + "anyhow", "async-std", "bitflags 2.4.1", "clap", diff --git a/Cargo.toml b/Cargo.toml index f1ad16e..bf3075d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,3 +27,7 @@ thiserror = "1" [build-dependencies] shadow-rs = "0.24" +logos = "0.13" +bitflags = "2.4" +anyhow = "1" +thiserror = "1" diff --git a/build.rs b/build.rs index 4a0dfc4..73a65e5 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,385 @@ -fn main() -> shadow_rs::SdResult<()> { - shadow_rs::new() +// Didn't make actual good error reporting since I'm the only one who'll be debugging this. + +use bitflags::bitflags; +use logos::{Lexer, Logos}; +use std::ops::Range; +use std::path::Path; +use std::str::FromStr; +use std::{env, fs}; +use std::collections::HashMap; +use thiserror::Error; + +#[derive(Logos, Debug, PartialEq)] +#[logos(skip r";[^\n]*")] +#[logos(skip r"[ \r\t\f]+")] +enum Token { + #[regex(r"[._a-zA-Z][_a-zA-Z0-9]*:?", Token::ident)] + Ident(String), + #[token("|")] + Pipe, + #[token("\n")] + Newline, +} + +impl Token { + fn ident(lex: &mut Lexer) -> String { + lex.slice().to_owned() + } +} + +#[derive(Debug, Error)] +enum Error { + #[error(transparent)] + Shadow(#[from] shadow_rs::ShadowError), + #[error("unable to locate output directory")] + OutDir, + #[error(transparent)] + Fs(#[from] std::io::Error), + #[error("Unknown instruction: {0}")] + Instruction(String), + #[error("Unknown section: {0}")] + Section(String), + #[error("Unknown flag: {0}")] + UnknownFlag(String), + #[error("Expected `|`, found {0}")] + UnexpectedFlag(String), + #[error("Expected newline after")] + Newline, + #[error("Expected flag, found `|`")] + Pipe, + #[error("Top level cycles not allowed")] + Top, + #[error("Unknown token encountered at index: {0:?}")] + Lex(Range), +} + +fn main() -> Result<(), Error> { + shadow_rs::new()?; + + println!("cargo:rerun-if-changed=src/microcode.asm"); + println!("cargo:rerun-if-changed=build.rs"); + + let file = fs::read_to_string("src/microcode.asm")?; + let lex = Token::lexer(&file); + let stream = Stream::parse(lex)?; + let microcode = stream.stitch(); + let (ctrl_low, (ctrl_mid, ctrl_high)): (Vec, (Vec, Vec)) = microcode + .into_iter() + .map(|cw| { + ( + (cw.bits() & 0xFF) as u8, + ( + ((cw.bits() >> 8) & 0xFF) as u8, + ((cw.bits() >> 16) & 0xFF) as u8, + ), + ) + }) + .unzip(); + + let out_env = env::var_os("OUT_DIR").ok_or(Error::OutDir)?; + let out_dir = Path::new(&out_env); + fs::write(out_dir.join("ctrl_low.rom"), &ctrl_low)?; + fs::write(out_dir.join("ctrl_mid.rom"), &ctrl_mid)?; + fs::write(out_dir.join("ctrl_high.rom"), &ctrl_high)?; + + Ok(()) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum Instruction { + Add = 0x0, + Sub = 0x1, + Adc = 0x2, + Sbc = 0x3, + Nand = 0x4, + Or = 0x5, + Cmp = 0x6, + Mv = 0x7, + Ld = 0x8, + St = 0x9, + Lda = 0xA, + Push = 0xB, + Pop = 0xC, + Jnz = 0xD, + In = 0xE, + Out = 0xF, +} + +#[derive(Debug, Clone)] +struct Sequence { + start: Vec, + reg: Vec, + imm: Vec, + end: Vec, +} + +impl Sequence { + fn empty() -> Self { + Sequence { + start: Vec::new(), + reg: Vec::new(), + imm: Vec::new(), + end: Vec::new(), + } + } +} + +#[derive(Debug, Clone)] +struct Stream { + instructions: HashMap, +} + +bitflags! { + /// Representation of the CPU Control Word + /// + /// Find more in-depth explanations of flags in `Arch.md`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + struct ControlWord: u32 { + /// ALU opcode low + const AOL = 1 << 0; + /// ALU opcode middle + const AOM = 1 << 1; + /// ALU opcode high + const AOH = 1 << 2; + /// ALU active + const AA = 1 << 3; + /// ALU load primary + const ALP = 1 << 4; + /// ALU load secondary + const ALS = 1 << 5; + /// Register bank bus in + const RI = 1 << 6; + /// Register bank bus out + const RO = 1 << 7; + /// Instruction builtin register address + const RBA = 1 << 8; + /// Instruction primary register address + const RPA = 1 << 9; + /// Stack pointer increment + const SPI = 1 << 10; + /// Stack pointer decrement + const SPD = 1 << 11; + /// Clock reset + const CR = 1 << 12; + /// Program counter increment + const PCI = 1 << 13; + /// Set program counter + const JNZ = 1 << 14; + /// Load instruction + const LI = 1 << 15; + /// Program out + const PO = 1 << 16; + /// Swap Temp Register + const ST = 1 << 17; + /// Transfer HIGH/LOW + const THL = 1 << 18; + /// Load Address + const LA = 1 << 19; + /// Store Address + const SA = 1 << 20; + /// ADDRESS low in + const AL = 1 << 21; + /// ADDRESS high in + const AH = 1 << 22; + /// Load Stack Pointer + const LSP = 1 << 23; + } +} + +impl FromStr for ControlWord { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s.to_ascii_lowercase().as_str() { + "aol" => Ok(ControlWord::AOL), + "aom" => Ok(ControlWord::AOM), + "aoh" => Ok(ControlWord::AOH), + "aa" => Ok(ControlWord::AA), + "alp" => Ok(ControlWord::ALP), + "als" => Ok(ControlWord::ALS), + "ri" => Ok(ControlWord::RI), + "ro" => Ok(ControlWord::RO), + "rba" => Ok(ControlWord::RBA), + "rpa" => Ok(ControlWord::RPA), + "spi" => Ok(ControlWord::SPI), + "spd" => Ok(ControlWord::SPD), + "cr" => Ok(ControlWord::CR), + "pci" => Ok(ControlWord::PCI), + "jnz" => Ok(ControlWord::JNZ), + "li" => Ok(ControlWord::LI), + "po" => Ok(ControlWord::PO), + "st" => Ok(ControlWord::ST), + "thl" => Ok(ControlWord::THL), + "la" => Ok(ControlWord::LA), + "sa" => Ok(ControlWord::SA), + "al" => Ok(ControlWord::AL), + "ah" => Ok(ControlWord::AH), + "lsp" => Ok(ControlWord::LSP), + _ => Err(Error::UnknownFlag(s.to_owned())), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Section { + Start, + Reg, + Imm, + End, +} + +impl Stream { + fn parse(lex: Lexer) -> Result { + let mut instructions = HashMap::new(); + let mut current_instr = None; + let mut newline = false; + let mut pipe = false; + let mut current_sequence = Sequence::empty(); + let mut current_section = Section::Start; + let mut current_cw = None; + + + + for (token, span) in lex.spanned() { + println!("{token:?}"); + match token { + Ok(tok) => match tok { + Token::Ident(i) => { + if i.starts_with('.') && i.ends_with(':') { + if current_instr.is_some() && newline { + current_section = match i.as_str() { + ".start:" => Section::Start, + ".reg:" => Section::Reg, + ".imm:" => Section::Imm, + ".both:" | ".end:" => Section::End, + _ => return Err(Error::Section(i)), + }; + } else { + if current_instr.is_some() { + return Err(Error::Newline); + } else { + return Err(Error::Top); + } + } + } else if i.ends_with(':') { + if let Some(instr) = current_instr { + instructions.insert(instr, current_sequence); + current_sequence = Sequence::empty(); + } + + current_instr = Some(match i.as_str() { + "add:" => Instruction::Add, + "sub:" => Instruction::Sub, + "adc:" => Instruction::Adc, + "sbc:" => Instruction::Sbc, + "nand:" => Instruction::Nand, + "or:" => Instruction::Or, + "cmp:" => Instruction::Cmp, + "mv:" => Instruction::Mv, + "ld:" => Instruction::Ld, + "st:" => Instruction::St, + "lda:" => Instruction::Lda, + "push:" => Instruction::Push, + "pop:" => Instruction::Pop, + "jnz:" => Instruction::Jnz, + "in:" => Instruction::In, + "out:" => Instruction::Out, + _ => return Err(Error::Instruction(i)), + }); + newline = false; + current_section = Section::Start; + } else { + match current_cw { + Some(ref mut cw) => if pipe { + *cw |= ControlWord::from_str(&i)?; + pipe = false; + } else { + return Err(Error::UnexpectedFlag(i)); + }, + None => current_cw = Some(ControlWord::from_str(&i)?), + } + } + }, + Token::Newline => { + if current_instr.is_some() { + if newline && !pipe { + if let Some(cw) = current_cw { + match current_section { + Section::Start => current_sequence.start.push(cw), + Section::Reg => current_sequence.reg.push(cw), + Section::Imm => current_sequence.imm.push(cw), + Section::End => current_sequence.end.push(cw), + } + current_cw = None; + } + } else { + newline = true; + } + } + }, + Token::Pipe => if current_cw.is_some() { + pipe = true + } else { + return Err(Error::Pipe); + }, + }, + Err(_) => return Err(Error::Lex(span)), + } + } + + if let Some(instr) = current_instr { + if let Some(cw) = current_cw { + match current_section { + Section::Start => current_sequence.start.push(cw), + Section::Reg => current_sequence.reg.push(cw), + Section::Imm => current_sequence.imm.push(cw), + Section::End => current_sequence.end.push(cw), + } + } + + instructions.insert(instr, current_sequence); + } + + println!("{:?}", instructions); + + Ok(Stream {instructions}) + } + + fn stitch(self) -> [ControlWord; 1 << 8] { + let mut ctrl = [ControlWord::empty(); 1 << 8]; + + for (instr, seq) in self.instructions { + let base = (instr as u8) << 4; + let mut reg = base; + let mut imm = base & 0b1000; + + for cw in seq.start { + ctrl[reg as usize] = cw; + reg += 1; + + ctrl[imm as usize] = cw; + imm += 1; + } + + for cw in seq.reg { + ctrl[reg as usize] = cw; + reg += 1; + } + + for cw in seq.imm { + ctrl[imm as usize] = cw; + reg += 1; + } + + for cw in seq.end { + ctrl[reg as usize] = cw; + reg += 1; + + ctrl[imm as usize] = cw; + imm += 1; + } + } + + ctrl + } } diff --git a/microcode.py b/microcode.py deleted file mode 100644 index fc88558..0000000 --- a/microcode.py +++ /dev/null @@ -1,362 +0,0 @@ -from enum import IntFlag - -# TODO: Figure out how to work `IN` and `OUT` into 24 bits -class ControlWord(IntFlag): - """ - Representation of the CPU control word. - - Find more in-depth explanations of flags in `Arch.md`. - """ - - EMPTY = 0 - """Empty set""" - AOL = 1 << 0 - """ALU opcode low""" - AOM = 1 << 1 - """ALU opcode middle""" - AOH = 1 << 2 - """ALU opcode high""" - AA = 1 << 3 - """ALU active""" - ALP = 1 << 4 - """ALU load primary""" - ALS = 1 << 5 - """ALU load secondary""" - RI = 1 << 6 - """Register Bank in""" - RO = 1 << 7 - """Register Bank out""" - RBA = 1 << 8 - """Instruction builtin register address""" - RPA = 1 << 9 - """Instruction primary register address""" - SPI = 1 << 10 - """Stack Pointer increment""" - SPD = 1 << 11 - """Stack Pointer decrement""" - CR = 1 << 12 - """Clock reset""" - PCI = 1 << 13 - """Program Counter increment""" - JNZ = 1 << 14 - """Set Program Counter""" - LI = 1 << 15 - """Load instruction""" - PO = 1 << 16 - """Program out""" - ST = 1 << 17 - """Swap Temp Register""" - THL = 1 << 18 - """Transfer HIGH/LOW""" - LA = 1 << 19 - """Load ADDRESS""" - SA = 1 << 20 - """Store ADDRESS""" - AL = 1 << 21 - """ADDRESS low in""" - AH = 1 << 22 - """ADDRESS high in""" - LSP = 1 << 23 - """Load Stack Pointer""" - -BLANK: list[ControlWord] = [ControlWord.EMPTY] * (1 << 6) - -def main(): - content = generate_content() - - wb("src/ctrl_low.rom", bytes(map(lambda x: int(x) & 0xFF, content))) - wb("src/ctrl_mid.rom", bytes(map(lambda x: (int(x) >> 8) & 0xFF, content))) - wb("src/ctrl_high.rom", bytes(map(lambda x: (int(x) >> 16) & 0xFF, content))) - -def wb(file: str, content: bytes): - with open(file, 'wb') as f: - f.write(bytes(content)) - -def generate_content() -> list[ControlWord]: - content = [] - - # Yeah I know this is terrible sorry - content += generate_add() - content += generate_sub() - content += generate_adc() - content += generate_sbc() - content += generate_nand() - content += generate_or() - content += generate_cmp() - content += generate_mv() - content += generate_ld() - content += generate_st() - content += generate_lda() - content += generate_push() - content += generate_pop() - content += generate_jnz() - content += generate_in() - content += generate_out() - - print(len(content)) - - return content - -def generate_add() -> list[ControlWord]: - add = BLANK - - add[0b00000] = ControlWord.LI - add[0b00001] = ControlWord.RBA | ControlWord.RO | ControlWord.ALP - add[0b00010] = ControlWord.PCI - add[0b00011] = ControlWord.RPA | ControlWord.ALS - add[0b00100] = ControlWord.AA | ControlWord.RBA | ControlWord.RI - add[0b00101] = ControlWord.CR | ControlWord.PCI - - add[0b10000] = ControlWord.LI - add[0b10001] = ControlWord.RBA | ControlWord.RO | ControlWord.ALP - add[0b10010] = ControlWord.PCI - add[0b10011] = ControlWord.PO | ControlWord.ALS - add[0b10100] = ControlWord.AA | ControlWord.RBA | ControlWord.RI - add[0b10101] = ControlWord.CR | ControlWord.PCI - - return add - -def generate_sub() -> list[ControlWord]: - sub = BLANK - - sub[0b00000] = ControlWord.LI - sub[0b00001] = ControlWord.RBA | ControlWord.RO | ControlWord.ALP - sub[0b00010] = ControlWord.PCI - sub[0b00011] = ControlWord.RPA | ControlWord.ALS - sub[0b00100] = ControlWord.AA | ControlWord.AOL | ControlWord.RBA | ControlWord.RI - sub[0b00101] = ControlWord.CR | ControlWord.PCI - - sub[0b10000] = ControlWord.LI - sub[0b10001] = ControlWord.RBA | ControlWord.RO | ControlWord.ALP - sub[0b10010] = ControlWord.PCI - sub[0b10011] = ControlWord.PO | ControlWord.ALS - sub[0b10100] = ControlWord.AA | ControlWord.AOL | ControlWord.RBA | ControlWord.RI - sub[0b10101] = ControlWord.CR | ControlWord.PCI - - return sub - -def generate_adc() -> list[ControlWord]: - adc = BLANK - - adc[0b00000] = ControlWord.LI - adc[0b00001] = ControlWord.RBA | ControlWord.RO | ControlWord.ALP - adc[0b00010] = ControlWord.PCI - adc[0b00011] = ControlWord.RPA | ControlWord.ALS - adc[0b00100] = ControlWord.AA | ControlWord.AOM | ControlWord.RBA | ControlWord.RI - adc[0b00101] = ControlWord.CR | ControlWord.PCI - - adc[0b10000] = ControlWord.LI - adc[0b10001] = ControlWord.RBA | ControlWord.RO | ControlWord.ALP - adc[0b10010] = ControlWord.PCI - adc[0b10011] = ControlWord.PO | ControlWord.ALS - adc[0b10100] = ControlWord.AA | ControlWord.AOM | ControlWord.RBA | ControlWord.RI - adc[0b10101] = ControlWord.CR | ControlWord.PCI - - return adc - -def generate_sbc() -> list[ControlWord]: - sbc = BLANK - - sbc[0b00000] = ControlWord.LI - sbc[0b00001] = ControlWord.RBA | ControlWord.RO | ControlWord.ALP - sbc[0b00010] = ControlWord.PCI - sbc[0b00011] = ControlWord.RPA | ControlWord.ALS - sbc[0b00100] = ControlWord.AA | ControlWord.AOL | ControlWord.AOM | ControlWord.RBA | ControlWord.RI - sbc[0b00101] = ControlWord.CR | ControlWord.PCI - - sbc[0b10000] = ControlWord.LI - sbc[0b10001] = ControlWord.RBA | ControlWord.RO | ControlWord.ALP - sbc[0b10010] = ControlWord.PCI - sbc[0b10011] = ControlWord.PO | ControlWord.ALS - sbc[0b10100] = ControlWord.AA | ControlWord.AOL | ControlWord.AOM | ControlWord.RBA | ControlWord.RI - sbc[0b10101] = ControlWord.CR | ControlWord.PCI - - return sbc - -def generate_nand() -> list[ControlWord]: - nand = BLANK - - nand[0b00000] = ControlWord.LI - nand[0b00001] = ControlWord.RBA | ControlWord.RO | ControlWord.ALP - nand[0b00010] = ControlWord.PCI - nand[0b00011] = ControlWord.RPA | ControlWord.ALS - nand[0b00100] = ControlWord.AA | ControlWord.AOH | ControlWord.RBA | ControlWord.RI - nand[0b00101] = ControlWord.CR | ControlWord.PCI - - nand[0b10000] = ControlWord.LI - nand[0b10001] = ControlWord.RBA | ControlWord.RO | ControlWord.ALP - nand[0b10010] = ControlWord.PCI - nand[0b10011] = ControlWord.PO | ControlWord.ALS - nand[0b10100] = ControlWord.AA | ControlWord.AOH | ControlWord.RBA | ControlWord.RI - nand[0b10101] = ControlWord.CR | ControlWord.PCI - - return nand - -def generate_or() -> list[ControlWord]: - ctrl_or = BLANK - - ctrl_or[0b00000] = ControlWord.LI - ctrl_or[0b00001] = ControlWord.RBA | ControlWord.RO | ControlWord.ALP - ctrl_or[0b00010] = ControlWord.PCI - ctrl_or[0b00011] = ControlWord.RPA | ControlWord.ALS - ctrl_or[0b00100] = ControlWord.AA | ControlWord.AOH | ControlWord.AOL | ControlWord.RBA | ControlWord.RI - ctrl_or[0b00101] = ControlWord.CR | ControlWord.PCI - - ctrl_or[0b10000] = ControlWord.LI - ctrl_or[0b10001] = ControlWord.RBA | ControlWord.RO | ControlWord.ALP - ctrl_or[0b10010] = ControlWord.PCI - ctrl_or[0b10011] = ControlWord.PO | ControlWord.ALS - ctrl_or[0b10100] = ControlWord.AA | ControlWord.AOH | ControlWord.AOL | ControlWord.RBA | ControlWord.RI - ctrl_or[0b10101] = ControlWord.CR | ControlWord.PCI - - return ctrl_or - -def generate_cmp() -> list[ControlWord]: - cmp = BLANK - - cmp[0b00000] = ControlWord.LI - cmp[0b00001] = ControlWord.RBA | ControlWord.RO | ControlWord.ALP - cmp[0b00010] = ControlWord.PCI - cmp[0b00011] = ControlWord.RPA | ControlWord.ALS - cmp[0b00100] = ControlWord.AA | ControlWord.AOH | ControlWord.AOM - cmp[0b00101] = ControlWord.CR | ControlWord.PCI - - cmp[0b10000] = ControlWord.LI - cmp[0b10001] = ControlWord.RBA | ControlWord.RO | ControlWord.ALP - cmp[0b10010] = ControlWord.PCI - cmp[0b10011] = ControlWord.PO | ControlWord.ALS - cmp[0b10100] = ControlWord.AA | ControlWord.AOH | ControlWord.AOM - cmp[0b10101] = ControlWord.CR | ControlWord.PCI - - return cmp - -def generate_mv() -> list[ControlWord]: - mv = BLANK - - mv[0b00000] = ControlWord.LI - mv[0b00001] = ControlWord.RBA | ControlWord.RO | ControlWord.ST - mv[0b00010] = ControlWord.PCI - mv[0b00011] = ControlWord.RPA | ControlWord.RI | ControlWord.ST - mv[0b00100] = ControlWord.CR | ControlWord.PCI - - mv[0b10000] = ControlWord.LI - mv[0b10001] = ControlWord.PCI - mv[0b10010] = ControlWord.RBA | ControlWord.RI | ControlWord.PO - mv[0b10011] = ControlWord.CR | ControlWord.PCI - - return mv - -def generate_ld() -> list[ControlWord]: - ld = BLANK - - ld[0b00000] = ControlWord.LI - ld[0b00001] = ControlWord.THL | ControlWord.RO - ld[0b00010] = ControlWord.LA | ControlWord.RBA | ControlWord.RI - ld[0b00011] = ControlWord.CR | ControlWord.PCI - - ld[0b10000] = ControlWord.LI - ld[0b10001] = ControlWord.PCI - ld[0b10010] = ControlWord.AH | ControlWord.PO - ld[0b10011] = ControlWord.PCI - ld[0b10100] = ControlWord.AL | ControlWord.PO - ld[0b10101] = ControlWord.LA | ControlWord.RBA | ControlWord.RI - ld[0b10110] = ControlWord.CR | ControlWord.PCI - - return ld - -def generate_st() -> list[ControlWord]: - st = BLANK - - st[0b00000] = ControlWord.LI - st[0b00001] = ControlWord.THL | ControlWord.RO - st[0b00010] = ControlWord.SA | ControlWord.RBA | ControlWord.RO - st[0b00011] = ControlWord.CR | ControlWord.PCI - - st[0b10000] = ControlWord.LI - st[0b10001] = ControlWord.PCI - st[0b10010] = ControlWord.AH | ControlWord.PO - st[0b10011] = ControlWord.PCI - st[0b10100] = ControlWord.AL | ControlWord.PO - st[0b10101] = ControlWord.SA | ControlWord.RBA | ControlWord.RO - st[0b10110] = ControlWord.CR | ControlWord.PCI - - return st - -def generate_lda() -> list[ControlWord]: - lda = BLANK - - # LDA doesn't have a register version, so we just skip to immediate - lda[0b10000] = ControlWord.LI - lda[0b10001] = ControlWord.PCI - lda[0b10010] = ControlWord.PO | ControlWord.AH - lda[0b10011] = ControlWord.PCI - lda[0b10100] = ControlWord.PO | ControlWord.AL - lda[0b10101] = ControlWord.THL | ControlWord.RI - lda[0b10110] = ControlWord.CR | ControlWord.PCI - - return lda - -def generate_push() -> list[ControlWord]: - push = BLANK - - push[0b00000] = ControlWord.LI - push[0b00001] = ControlWord.LSP - push[0b00010] = ControlWord.RBA | ControlWord.RO | ControlWord.SA - push[0b00011] = ControlWord.SPI - push[0b00100] = ControlWord.CR | ControlWord.PCI - - push[0b10000] = ControlWord.LI - push[0b10001] = ControlWord.LSP | ControlWord.PCI - push[0b10010] = ControlWord.PO | ControlWord.SA - push[0b00011] = ControlWord.SPI - push[0b00100] = ControlWord.CR | ControlWord.PCI - - return push - -def generate_pop() -> list[ControlWord]: - pop = BLANK - - pop[0b00000] = ControlWord.LI - pop[0b00001] = ControlWord.SPD - pop[0b00010] = ControlWord.LSP - pop[0b00011] = ControlWord.RBA | ControlWord.RO | ControlWord.SA - pop[0b00100] = ControlWord.CR | ControlWord.PCI - - # POP doesn't have an immediate argument, so we can just skip this - - return pop - -def generate_jnz() -> list[ControlWord]: - jnz = BLANK - - jnz[0b00000] = ControlWord.LI - jnz[0b00001] = ControlWord.RBA | ControlWord.RO | ControlWord.ALP - jnz[0b00010] = ControlWord.AOL | ControlWord.AOM | ControlWord.AOH - jnz[0b00011] = ControlWord.JNZ - jnz[0b00100] = ControlWord.CR - - jnz[0b10000] = ControlWord.LI - jnz[0b10001] = ControlWord.PCI - jnz[0b10010] = ControlWord.PO | ControlWord.ALP - jnz[0b10011] = ControlWord.AOL | ControlWord.AOM | ControlWord.AOH - jnz[0b10100] = ControlWord.JNZ - jnz[0b10101] = ControlWord.CR - - return jnz - -def generate_in() -> list[ControlWord]: - ctrl_in = BLANK - - # TODO: Finish `IN` - - return ctrl_in - -def generate_out() -> list[ControlWord]: - out = BLANK - - # TODO: Finish `OUT` - - return out - -if __name__ == "__main__": - main() diff --git a/src/ctrl_high.rom b/src/ctrl_high.rom deleted file mode 100644 index e618f674751cd1c31bfd6d7094bacf22fa6e7f64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1024 zcmeH@F%Ez*3&w$we~qRKGMw5K o=c2o136XV(xy3hwAs&i#g* zJ35tfza!_4PUYMm$ho6aIrk@W?&ws`{fL}9I+eq`$8Oz!y{s+hye$N3SiXpg1*)Tm dsC$213lWuhMIR)J^FrRiHISPakf-|d{S7fO6_Wq} diff --git a/src/ctrl_mid.rom b/src/ctrl_mid.rom deleted file mode 100644 index a2f22cf024eb6df66df12384c647df5be9bbfab5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1024 zcmZo*RA6E>V1R=LMg<0BK3oC^1E_vfKZW7PfTkark0aRO!l3XsK-RASj6EbiTp9;2?miA5{QhG*(u_0P6<; D$Qd1@ diff --git a/src/emulator.rs b/src/emulator.rs index 20e57b5..44dc8d9 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -17,9 +17,9 @@ use clio::Input; use modular_bitfield::{bitfield, BitfieldSpecifier}; use thiserror::Error; -const CTRL_LOW: &[u8; 1 << 10] = include_bytes!("ctrl_low.rom"); -const CTRL_MID: &[u8; 1 << 10] = include_bytes!("ctrl_mid.rom"); -const CTRL_HIGH: &[u8; 1 << 10] = include_bytes!("ctrl_high.rom"); +const CTRL_LOW: &[u8; 1 << 8] = include_bytes!(concat!(env!("OUT_DIR"), "/ctrl_low.rom")); +const CTRL_MID: &[u8; 1 << 8] = include_bytes!(concat!(env!("OUT_DIR"), "/ctrl_mid.rom")); +const CTRL_HIGH: &[u8; 1 << 8] = include_bytes!(concat!(env!("OUT_DIR"), "/ctrl_high.rom")); #[derive(Error, Debug)] pub enum EmulatorError { diff --git a/src/microcode.asm b/src/microcode.asm new file mode 100644 index 0000000..1ab90f3 --- /dev/null +++ b/src/microcode.asm @@ -0,0 +1,143 @@ +add: + li + rba | ro | alp | pci +.reg: + rpa | als +.imm: + po | als +.both: + aa | rba | ri | cr | pci + +sub: + li + rba | ro | alp | pci +.reg: + rpa | als +.imm: + po | als +.both: + aa | aol | rba | ri | cr | pci + +adc: + li + rba | ro | alp | pci +.reg: + rpa | als +.imm: + po | als +.both: + aa | aom | rba | ri | cr | pci + +sbc: + li + rba | ro | alp | pci +.reg: + rpa | als +.imm: + po | als +.both: + aa | aol | aom | rba | ri | cr | pci + +nand: + li + rba | ro | alp | pci +.reg: + rpa | als +.imm: + po | als +.both: + aa | aoh | rba | ri | cr | pci + +or: + li + rba | ro | alp | pci +.reg: + rpa | als +.imm: + po | als +.both: + aa | aoh | aol | rba | ri | cr | pci + +cmp: + li + rba | ro | alp | pci +.reg: + rpa | als +.imm: + po | als +.both: + aa | aoh | aom | cr | pci + +mv: +.reg: + li + rba | ro | st | pci + rpa | ri | st | cr | pci +.imm: + li | pci + rba | ri | po | cr | pci + +ld: +.reg: + li + thl | ro + la | rba | ri | cr | pci +.imm: + li | pci + ah | po | pci + al | po + la | rba | ri | cr | pci + +st: +.reg: + li + thl | ro + sa | rba | ro | cr | pci +.imm: + li | pci + ah | po | pci + al | po + sa | rba | ro | cr | pci + +lda: +.imm: + li | pci + po | ah | pci + po | al + thl | ri | cr | pci + +push: + li +.reg: + lsp + rba | ro | sa +.imm: + lsp | pci + po | sa +.both: + spi | cr | pci + +pop: +.reg: + li + spd + lsp + rba | ro | sa | cr | pci + +jnz: +.reg: + li + rba | ro | alp + aol | aom | aoh + jnz | cr +.imm: + li | pci + po | alp + aol | aom | aoh + jnz | cr + +in: + ; TODO: figure out `in` and `out` + +out: + ; TODO: figure out `in` and `out`