diff --git a/.gitignore b/.gitignore index ea8c4bf..3b6ec09 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +examples/*.bin diff --git a/Cargo.lock b/Cargo.lock index 55313ec..5e3b7c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -231,10 +231,22 @@ dependencies = [ "rand", "rhexdump", "rust-embed", - "sdl2", "unescape", ] +[[package]] +name = "henceforth" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "hence", + "indexmap", + "itertools", + "lazy_static", + "parse_int", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -468,29 +480,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "sdl2" -version = "0.35.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7959277b623f1fb9e04aea73686c3ca52f01b2145f8ea16f4ff30d8b7623b1a" -dependencies = [ - "bitflags", - "lazy_static", - "libc", - "sdl2-sys", -] - -[[package]] -name = "sdl2-sys" -version = "0.35.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3586be2cf6c0a8099a79a12b4084357aa9b3e0b0d7980e3b67aaf7a9d55f9f0" -dependencies = [ - "cfg-if", - "libc", - "version-compare", -] - [[package]] name = "sha2" version = "0.10.6" @@ -568,12 +557,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" -[[package]] -name = "version-compare" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" - [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 392b71c..06c9a23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["hence"] +members = ["hence", "henceforth"] diff --git a/examples/henceforth.asm b/examples/henceforth.asm new file mode 100644 index 0000000..dcfc65d --- /dev/null +++ b/examples/henceforth.asm @@ -0,0 +1,56 @@ +.include "$lib/core.asm" +.include "$lib/std.asm" +.include "$lib/main.asm" + +.define HENCEFORTH_SOURCE_MAX_LEN, 128 +.define HENCEFORTH_SOURCE_START, CORE_MEM_MEM +.define HENCEFORTH_SOURCE_END, (HENCEFORTH_SOURCE_START + HENCEFORTH_SOURCE_MAX_LEN) +.define HENCEFORTH_SOURCE_LEN, (HENCEFORTH_SOURCE_END + 1) + +.jump_main + +.def_main + ts HENCEFORTH_SOURCE_START + tlr CORE_REG_B + + loop: + ; get key press + ts CORE_MEM_KEY + get + tlr CORE_REG_A + + ; echo + ts CORE_MEM_CHR + set + + ; store in source + tsr CORE_REG_B + set + + ; increment pointer + ts 1 + tlr CORE_REG_A + ts CORE_ALU_ADD + alu + tlr CORE_REG_B + + ; jump to loop start or not + ; backup + tsr CORE_REG_B + tlr CORE_REG_C + + ; check + ts HENCEFORTH_SOURCE_END + tlr CORE_REG_A + ts CORE_ALU_GT + alu + tlr CORE_REG_A + + ; restore + tsr CORE_REG_C + tlr CORE_REG_B + + ; jump + ts loop + tlrc CORE_REG_PC +.std_stop diff --git a/examples/test.asm b/examples/test.asm index d9d7407..bcabbaf 100644 --- a/examples/test.asm +++ b/examples/test.asm @@ -5,5 +5,4 @@ .jump_main main: .main main - .std_stop diff --git a/examples/test.bin b/examples/test.bin index cfb7a72..7ecb02b 100644 Binary files a/examples/test.bin and b/examples/test.bin differ diff --git a/examples/testforth.asm b/examples/testforth.asm new file mode 100644 index 0000000..bf4a2e2 --- /dev/null +++ b/examples/testforth.asm @@ -0,0 +1,68 @@ +.include "$lib/core.asm" +.include "$lib/std.asm" +.include "$lib/main.asm" +.define MEM_LOOP_I, CORE_MEM_MEM +.define MEM_LOOP_J, (MEM_LOOP_I + 1) +.define MEM_CALL_STACK_LEN, 16 +.define MEM_CALL_STACK_PTR, (MEM_LOOP_J + 1) +.define MEM_CALL_STACK_END, (MEM_CALL_STACK_PTR + MEM_CALL_STACK_LEN) +.define MEM_ALLOC_PTR, MEM_CALL_STACK_END +.macro stack_transfer_alu +.std_ld +tlr CORE_REG_B +.std_ld +tlr CORE_REG_A +.endmacro +.macro call_stack_jump, call_stack_jump_arg_0_label, call_stack_jump_arg_1_offset +.std_rset CORE_REG_C, call_stack_jump_arg_0_label +.std_rset CORE_REG_D, (call_stack_jump_arg_1_offset + 7) +ts call_stack_jump +tlr CORE_REG_PC +.endmacro +.macro return_call_stack_jump +.std_jump return_call_stack_jump +.endmacro +.std_rset CORE_REG_A, MEM_CALL_STACK_PTR +.std_set MEM_CALL_STACK_PTR +.std_rset CORE_REG_A, (MEM_ALLOC_PTR + 1) +.std_set MEM_ALLOC_PTR +.jump_main +call_stack_jump: +.std_get MEM_CALL_STACK_PTR +tlr CORE_REG_A +.std_rset CORE_REG_B, 1 +.std_alu CORE_ALU_ADD +tlr CORE_REG_A +tlr CORE_REG_B +.std_set MEM_CALL_STACK_PTR +tsr CORE_REG_D +tlr CORE_REG_A +tsr CORE_REG_B +set +tsr CORE_REG_C +tlr CORE_REG_PC +return_call_stack_jump: +.std_get MEM_CALL_STACK_PTR +tlr CORE_REG_A +tlr CORE_REG_C +.std_rset CORE_REG_B, 1 +.std_alu CORE_ALU_SUB +tlr CORE_REG_A +.std_set MEM_CALL_STACK_PTR +tsr CORE_REG_C +get +tlr CORE_REG_PC +words_0: +; word: "test" +push 40 +push 2 +.stack_transfer_alu +.std_alu CORE_ALU_ADD +tls +.return_call_stack_jump +main: +.main main +.call_stack_jump words_0, OFFSET +dbg +.call_stack_jump words_0, OFFSET +.std_stop diff --git a/examples/testforth.fth b/examples/testforth.fth new file mode 100644 index 0000000..8695958 --- /dev/null +++ b/examples/testforth.fth @@ -0,0 +1,3 @@ +: test 40 2 + ; + +test debug test diff --git a/hence/Cargo.toml b/hence/Cargo.toml index 271054c..63b2312 100644 --- a/hence/Cargo.toml +++ b/hence/Cargo.toml @@ -18,4 +18,3 @@ anyhow = { version = "1.0.62", features = ["backtrace"] } rust-embed = "6.4.0" unescape = "0.1.0" parse_int = "0.6.0" -sdl2 = "0.35" diff --git a/hence/lib/core.asm b/hence/lib/core.asm index 0c07a5f..db67b51 100644 --- a/hence/lib/core.asm +++ b/hence/lib/core.asm @@ -1,52 +1,52 @@ ; hence core lib - .define NULL, 0x0000 - .define VOID, NULL +.define NULL, 0 +.define VOID, NULL - .define FALSE, NULL - .define TRUE, 0x0001 +.define FALSE, NULL +.define TRUE, 1 - .define CORE_U8_MAX, 0x00ff - .define CORE_U16_MAX, 0xffff - - .define CORE_KB, 1024 +.define CORE_U8_MAX, 0x00ff +.define CORE_U16_MAX, 0xffff - .define CORE_MEM_PRG, (0 * CORE_KB) - .define CORE_MEM_PRG_END, (32 * CORE_KB) - .define CORE_MEM_ST, CORE_MEM_PRG_END - .define CORE_MEM_MEM, (40 * CORE_KB) - .define CORE_MEM_MEM_END, (48 * CORE_KB) - .define CORE_MEM_OUT, CORE_MEM_MEM_END - .define CORE_MEM_CHR, (CORE_MEM_MEM_END + 1) - .define CORE_MEM_KEY, (CORE_MEM_MEM_END + 2) +.define CORE_KB, 1024 - .define CORE_REG_PC, 0x0 - .define CORE_REG_OPC, 0x1 - .define CORE_REG_ARG, 0x2 - .define CORE_REG_S, 0x3 - .define CORE_REG_SP, 0x4 - .define CORE_REG_A, 0x5 - .define CORE_REG_B, 0x6 - .define CORE_REG_C, 0x7 - .define CORE_REG_D, 0x8 +.define CORE_MEM_PRG, (0 * CORE_KB) +.define CORE_MEM_PRG_END, (32 * CORE_KB) +.define CORE_MEM_ST, CORE_MEM_PRG_END +.define CORE_MEM_MEM, (40 * CORE_KB) +.define CORE_MEM_MEM_END, (48 * CORE_KB) +.define CORE_MEM_OUT, CORE_MEM_MEM_END +.define CORE_MEM_CHR, (CORE_MEM_MEM_END + 1) +.define CORE_MEM_KEY, (CORE_MEM_MEM_END + 2) - .define CORE_ALU_NOT, 0x00 - .define CORE_ALU_AND, 0x01 - .define CORE_ALU_OR, 0x02 - .define CORE_ALU_XOR, 0x03 - .define CORE_ALU_LSH, 0x04 - .define CORE_ALU_RSH, 0x05 - .define CORE_ALU_ADD, 0x06 - .define CORE_ALU_SUB, 0x07 - .define CORE_ALU_MUL, 0x08 - .define CORE_ALU_DIV, 0x09 - .define CORE_ALU_CMP, 0x0a - .define CORE_ALU_EQ, 0x0b - .define CORE_ALU_LT, 0x0c - .define CORE_ALU_GT, 0x0d - .define CORE_ALU_LEQ, 0x0e - .define CORE_ALU_GEQ, 0x0f - .define CORE_ALU_BOL, 0x10 - .define CORE_ALU_INV, 0x11 - .define CORE_ALU_RND, 0x12 - .define CORE_ALU_TME, 0x13 +.define CORE_REG_PC, 0x0 +.define CORE_REG_OPC, 0x1 +.define CORE_REG_ARG, 0x2 +.define CORE_REG_S, 0x3 +.define CORE_REG_SP, 0x4 +.define CORE_REG_A, 0x5 +.define CORE_REG_B, 0x6 +.define CORE_REG_C, 0x7 +.define CORE_REG_D, 0x8 + +.define CORE_ALU_NOT, 0x00 +.define CORE_ALU_AND, 0x01 +.define CORE_ALU_OR, 0x02 +.define CORE_ALU_XOR, 0x03 +.define CORE_ALU_LSH, 0x04 +.define CORE_ALU_RSH, 0x05 +.define CORE_ALU_ADD, 0x06 +.define CORE_ALU_SUB, 0x07 +.define CORE_ALU_MUL, 0x08 +.define CORE_ALU_DIV, 0x09 +.define CORE_ALU_CMP, 0x0a +.define CORE_ALU_EQ, 0x0b +.define CORE_ALU_LT, 0x0c +.define CORE_ALU_GT, 0x0d +.define CORE_ALU_LEQ, 0x0e +.define CORE_ALU_GEQ, 0x0f +.define CORE_ALU_BOL, 0x10 +.define CORE_ALU_INV, 0x11 +.define CORE_ALU_RND, 0x12 +.define CORE_ALU_TME, 0x13 diff --git a/hence/lib/main.asm b/hence/lib/main.asm index 2cf6443..e0cd4d4 100644 --- a/hence/lib/main.asm +++ b/hence/lib/main.asm @@ -15,3 +15,7 @@ tlr CORE_REG_PC .org main .endmacro + +.macro def_main + main: .main main +.endmacro diff --git a/hence/src/assembler.rs b/hence/src/assembler.rs index 7e23c33..4e800cf 100644 --- a/hence/src/assembler.rs +++ b/hence/src/assembler.rs @@ -23,7 +23,9 @@ pub trait ByteResolvable { fn resolve_string(&self, data: &mut T) -> Result; } -pub const PROGRAM_SIZE: u16 = 32 * 1024; +pub const KB: u16 = 1024; + +pub const PROGRAM_SIZE: u16 = 32 * KB; pub type Program = [u8; PROGRAM_SIZE as usize]; #[derive(RustEmbed)] diff --git a/hence/src/emulator.rs b/hence/src/emulator.rs index 5f873d0..7c6ed99 100644 --- a/hence/src/emulator.rs +++ b/hence/src/emulator.rs @@ -6,7 +6,7 @@ use std::io::{self, Write}; use std::time::Instant; #[derive(Debug)] -pub struct Data { +pub struct Emulator { program: [u8; 32 * 1024], pub tmp: u16, pub reg_pc: u16, @@ -24,7 +24,7 @@ pub struct Data { term: console::Term, } -impl Data { +impl Emulator { pub fn new(program: [u8; 32 * 1024]) -> Self { Self { program, @@ -135,7 +135,7 @@ impl Data { } } - fn alu(&mut self, operation: u8) -> Result<()> { + pub fn alu(&mut self, operation: u8) -> Result<()> { match operation { 0x00 => { self.tmp = !self.reg_a; @@ -224,76 +224,86 @@ impl Data { Ok(()) } -} -pub fn emulate(data: &mut Data) -> Result<()> { - while data.reg_pc != 0xffff { - data.reg_opc = data.get_memory(data.reg_pc)? as u8; - data.reg_pc = data.reg_pc.wrapping_add(1); + pub fn cycle(&mut self) -> Result<()> { + self.reg_opc = self.get_memory(self.reg_pc)? as u8; + self.reg_pc = self.reg_pc.wrapping_add(1); - if data.reg_opc >> 7 == 1 { - data.reg_opc &= 0b0111111; - data.reg_arg = 0; + if self.reg_opc >> 7 == 1 { + self.reg_opc &= 0b0111111; + self.reg_arg = 0; } else { - data.reg_arg = data.get_memory(data.reg_pc)? << 8; - data.reg_pc = data.reg_pc.wrapping_add(1); + self.reg_arg = self.get_memory(self.reg_pc)? << 8; + self.reg_pc = self.reg_pc.wrapping_add(1); - data.reg_arg |= data.get_memory(data.reg_pc)?; - data.reg_pc = data.reg_pc.wrapping_add(1); + self.reg_arg |= self.get_memory(self.reg_pc)?; + self.reg_pc = self.reg_pc.wrapping_add(1); } - match data.reg_opc { + match self.reg_opc { 0x00 => {} 0x01 => { - data.tmp = data.reg_arg; - data.memory[data.reg_sp as usize] = data.reg_arg; - data.reg_sp = data.reg_sp.wrapping_add(1); + self.tmp = self.reg_arg; + self.memory[self.reg_sp as usize] = self.reg_arg; + self.reg_sp = self.reg_sp.wrapping_add(1); } 0x02 => { - data.reg_sp = data.reg_sp.wrapping_sub(1); + self.reg_sp = self.reg_sp.wrapping_sub(1); } 0x03 => { - data.tmp = data.reg_arg; + self.tmp = self.reg_arg; } 0x04 => { - data.tmp = data.get_register(data.reg_arg as u8); + self.tmp = self.get_register(self.reg_arg as u8); } 0x05 => { - data.tmp = data.memory[data.reg_sp.wrapping_sub(1) as usize]; + self.tmp = self.memory[self.reg_sp.wrapping_sub(1) as usize]; } 0x06 => { - data.set_register(data.reg_arg as u8, data.tmp); + self.set_register(self.reg_arg as u8, self.tmp); } 0x07 => { - if data.reg_a & 1 == 1 { - data.set_register(data.reg_arg as u8, data.tmp); + if self.reg_a & 1 == 1 { + self.set_register(self.reg_arg as u8, self.tmp); } } 0x08 => { - data.memory[data.reg_sp as usize] = data.tmp; - data.reg_sp = data.reg_sp.wrapping_add(1); + self.memory[self.reg_sp as usize] = self.tmp; + self.reg_sp = self.reg_sp.wrapping_add(1); } 0x09 => { println!( "[DEBUG]: [{}]", - data.memory.iter().take(data.reg_sp as usize).join(", ") + self.memory.iter().take(self.reg_sp as usize).join(", ") ); print!("Press enter to continue execution...",); io::stdout().flush()?; - data.term.read_line()?; + self.term.read_line()?; } 0x0a => { - data.alu(data.tmp as u8)?; + self.alu(self.tmp as u8)?; } 0x0b => { - data.tmp = data.get_memory(data.tmp)?; + self.tmp = self.get_memory(self.tmp)?; } 0x0c => { - data.set_memory(data.tmp, data.reg_a)?; + self.set_memory(self.tmp, self.reg_a)?; } - _ => bail!("Invalid opcode: 0x{}", radix(data.reg_opc, 16)), + _ => bail!("Invalid opcode: 0x{}", radix(self.reg_opc, 16)), } + + Ok(()) } - Ok(()) + pub fn is_stopped(&self) -> bool { + self.reg_pc == 0xffff + } + + pub fn run(&mut self) -> Result<()> { + while !self.is_stopped() { + self.cycle()?; + } + + Ok(()) + } } diff --git a/hence/src/main.rs b/hence/src/main.rs index 580cc1c..2a8e557 100644 --- a/hence/src/main.rs +++ b/hence/src/main.rs @@ -95,9 +95,7 @@ fn main() -> Result<()> { } let program: [u8; 32 * 1024] = (&program_buf[0..(32 * 1024)]).try_into()?; - let mut data = emulator::Data::new(program); - - emulator::emulate(&mut data)?; + emulator::Emulator::new(program).run()?; Ok(()) } diff --git a/henceforth/.gitignore b/henceforth/.gitignore new file mode 100644 index 0000000..204b6bf --- /dev/null +++ b/henceforth/.gitignore @@ -0,0 +1,2 @@ +examples/*.asm +examples/*.bin diff --git a/henceforth/Cargo.toml b/henceforth/Cargo.toml new file mode 100644 index 0000000..0167fa5 --- /dev/null +++ b/henceforth/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "henceforth" +version = "0.1.0" +edition = "2021" +authors = ["Dominic Grimm "] +repository = "https://git.dergrimm.net/dergrimm/hence.git" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +hence = { path = "../hence" } +clap = { version = "3.2.16", features = ["derive"] } +anyhow = { version = "1.0.62", features = ["backtrace"] } +itertools = "0.10.2" +parse_int = "0.6.0" +indexmap = "1.9.1" +lazy_static = "1.4.0" diff --git a/henceforth/src/compiler.rs b/henceforth/src/compiler.rs new file mode 100644 index 0000000..5a72f0e --- /dev/null +++ b/henceforth/src/compiler.rs @@ -0,0 +1,259 @@ +use anyhow::{bail, Context, Result}; +use indexmap::IndexSet; +use itertools::Itertools; +use lazy_static::lazy_static; +use std::collections::HashMap; + +use crate::parser; + +mod instruction; + +pub use instruction::Instruction; + +pub const TEMPLATE_ASM: &str = include_str!("compiler/templates/default.asm"); + +lazy_static! { + #[derive(Debug)] + pub static ref TEMPLATE: hence::parser::ast::Body = hence::parser::parse( + hence::lexer::lex(TEMPLATE_ASM).unwrap() + ) + .unwrap() + .body; +} + +pub trait Compilable { + fn compile(&self, data: &T) -> Result; +} + +#[derive(Debug)] +pub struct Word { + pub id: usize, + pub instructions: Vec, + pub times_used: usize, +} + +#[derive(Debug, PartialEq, Eq, Hash)] +pub struct Condition { + pub if_instructions: Vec, + pub else_instructions: Vec, +} + +#[derive(Debug)] +pub enum CallableId { + Word(String), + Condition(usize), +} + +#[derive(Debug)] +pub struct Compiler { + pub strings: IndexSet, + pub words: HashMap, + pub conditions: Vec, +} + +impl Compiler { + pub fn default() -> Self { + Self { + // words: HashMap::new(), + // conditions: IndexSet::new(), + strings: IndexSet::new(), + words: HashMap::new(), + conditions: vec![], + } + } + + pub fn generate_instructions( + &mut self, + body: parser::ast::Body, + optimize: bool, + ) -> Result> { + let mut instructions: Vec = vec![]; + let mut iter = body.into_iter().peekable(); + while let Some(node) = iter.next() { + match node { + _ if optimize && iter.next_if_eq(&node).is_some() => { + let count = iter.by_ref().peeking_take_while(|n| *n == node).count() + 2; + instructions.push(Instruction::Multiple { + instruction: Box::new( + self.generate_instructions(vec![node], optimize)? + .into_iter() + .next() + .unwrap(), + ), + count, + }); + } + parser::ast::Node::Comment(_) => {} + parser::ast::Node::String { mode, string } => { + instructions.push(match mode.as_str() { + "." => { + let id = self.strings.insert_full(string).0; + Instruction::StringPrint(id) + } + "r" => { + let id = self.strings.insert_full(string).0; + Instruction::StringReference(id) + } + "asm" => Instruction::AsmQuote(string), + _ => bail!("Unknown string mode: {}", mode), + }); + } + parser::ast::Node::Number(x) => { + instructions.push(instruction::Instruction::Push(x)); + } + parser::ast::Node::WordDefinition { + name, + stack: _, + body, + } => { + if Instruction::from_word(&name).is_some() { + bail!("Word already exists as compiler instruction: {}", name); + } else if self.words.contains_key(&name) { + bail!("Word already exists as user word definition: {}", name); + } + + let instructions = self.generate_instructions(body, optimize)?; + self.words.insert( + name.to_string(), + Word { + id: self.words.len(), + instructions, + times_used: 0, + }, + ); + } + parser::ast::Node::Condition { if_body, else_body } => { + // let if_instructions = self.generate_instructions(if_body, optimize)?; + // let else_instructions = self.generate_instructions(else_body, optimize)?; + // let id = self.conditions.len(); + // let origin = self.callable_graph.add_node(CallableId::Condition(id)); + // self.conditions.push(Condition { + // if_instructions: if_instructions.clone(), + // else_instructions: else_instructions.clone(), + // callable_graph_node: origin, + // }); + // instructions.push(Instruction::Condition(id)); + // self.add_graph_edges(origin, if_instructions)?; + // self.add_graph_edges(origin, else_instructions)?; + // dbg!(&self); + } + parser::ast::Node::Word(x) => { + dbg!(&self.words, &x); + if let Some(ins) = Instruction::from_word(&x) { + instructions.push(ins); + } else if let Some(w) = self.words.get_mut(&x) { + w.times_used += 1; + instructions.push(Instruction::Call(x)); + } else { + bail!("Word does not exist: {}", x); + } + } + } + } + + Ok(instructions) + } + + pub fn embed(&self, body: hence::parser::ast::Body) -> Result { + let mut x = TEMPLATE.to_vec(); + + // strings + for (id, s) in self.strings.iter().enumerate() { + x.extend([ + hence::parser::ast::Node::Label(format!("data_strings_{}", id)), + hence::parser::ast::Node::MacroCall { + name: "bytes".to_string(), + args: vec![hence::arg::Arg::String(s.to_string())], + }, + hence::parser::ast::Node::Label(format!("data_strings_end_{}", id)), + ]); + } + + // conditions + for (id, c) in self.conditions.iter().enumerate() { + x.push(hence::parser::ast::Node::Label(format!( + "conditions_if_{}", + id + ))); + x.extend( + c.if_instructions + .iter() + .map(|ins| ins.compile(self)) + .collect::>>()? + .into_iter() + .flatten(), + ); + x.push(hence::parser::ast::Node::Label(format!( + "conditions_else_{}", + id + ))); + x.extend( + c.else_instructions + .iter() + .map(|ins| ins.compile(self)) + .collect::>>()? + .into_iter() + .flatten(), + ); + } + + // words + for (name, word) in &self + .words + .iter() + .filter(|(_, w)| w.times_used > 1) + .sorted_by(|a, b| Ord::cmp(&a.1.id, &b.1.id)) + .collect::>() + { + x.extend(vec![ + hence::parser::ast::Node::Label(format!("words_{}", word.id)), + hence::parser::ast::Node::Comment(format!("word: \"{}\"", name)), + ]); + x.extend( + word.instructions + .iter() + .map(|ins| ins.compile(self)) + .collect::>>() + .unwrap() + .into_iter() + .flatten(), + ); + x.push(hence::parser::ast::Node::MacroCall { + name: "return_call_stack_jump".to_string(), + args: vec![], + }); + } + + x.extend([ + hence::parser::ast::Node::Label("main".to_string()), + hence::parser::ast::Node::MacroCall { + name: "main".to_string(), + args: vec![hence::arg::Arg::Variable("main".to_string())], + }, + ]); + x.extend(body); + x.push(hence::parser::ast::Node::MacroCall { + name: "std_stop".to_string(), + args: vec![], + }); + + Ok(x) + } +} + +pub fn compile(ast: parser::ast::AST, optimize: bool) -> Result { + let mut data = Compiler::default(); + let instructions = data.generate_instructions(ast.body, optimize)?; + + Ok(hence::parser::ast::AST { + body: data.embed( + instructions + .iter() + .map(|ins| ins.compile(&data)) + .collect::>>()? + .into_iter() + .flatten() + .collect(), + )?, + }) +} diff --git a/henceforth/src/compiler/instruction.rs b/henceforth/src/compiler/instruction.rs new file mode 100644 index 0000000..779729b --- /dev/null +++ b/henceforth/src/compiler/instruction.rs @@ -0,0 +1,1533 @@ +use anyhow::{bail, Result}; + +use crate::compiler; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Instruction { + Nop, + Debug, + Quit, + Push(i32), + Drop, + Depth, + Pick, + Dup, + Swap, + Over, + Rot, + Nip, + I, + J, + Tuck, + Fetch, + FetchPrint, + Store, + PlusStore, + MinusStore, + Cells, + Allot, + Dot, + Emit, + Space, + Spaces, + Cr, + StringPrint(usize), + StringReference(usize), + Count, + Not, + And, + Nand, + Or, + Nor, + Xor, + Xnor, + Lsh, + Rsh, + Compare, + Eq, + Neq, + Lt, + Leq, + Gt, + Geq, + Min, + Max, + Boolean, + Invert, + Random, + Plus, + OnePlus, + TwoPlus, + Minus, + OneMinus, + TwoMinus, + Times, + Divide, + Mod, + AsmQuote(String), + + Multiple { + instruction: Box, + count: usize, + }, + + Call(String), + Condition(usize), +} + +impl compiler::Compilable for Instruction { + fn compile(&self, data: &compiler::Compiler) -> Result { + match self { + Instruction::Nop => Ok(vec![hence::parser::ast::Node::Call { + name: "nop".to_string(), + arg: None, + }]), + Instruction::Debug => Ok(vec![hence::parser::ast::Node::Call { + name: "dbg".to_string(), + arg: None, + }]), + Instruction::Quit => Ok(vec![hence::parser::ast::Node::MacroCall { + name: "std_stop".to_string(), + args: vec![], + }]), + Instruction::Push(x) => Ok(vec![hence::parser::ast::Node::Call { + name: "push".to_string(), + arg: Some(hence::arg::Arg::Number(*x as u16)), + }]), + Instruction::Drop => Ok(vec![hence::parser::ast::Node::Call { + name: "pop".to_string(), + arg: None, + }]), + Instruction::Depth => Ok(vec![ + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_SP".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Pick => Ok(vec![ + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_SP".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_SUB".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_rset".to_string(), + args: vec![ + hence::arg::Arg::Variable("CORE_REG_B".to_string()), + hence::arg::Arg::BinaryExpression { + left: Box::new(hence::arg::Arg::Variable( + "CORE_MEM_PRG_END".to_string(), + )), + right: Box::new(hence::arg::Arg::Number(2)), + op: hence::arg::BinaryExpressionOperator::Sub, + }, + ], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_ADD".to_string())], + }, + hence::parser::ast::Node::Call { + name: "get".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Dup => Ok(vec![ + hence::parser::ast::Node::Call { + name: "tss".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Swap => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Over => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tss".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Rot => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Nip => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "pop".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::I => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_get".to_string(), + args: vec![hence::arg::Arg::Variable("MEM_LOOP_I".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::J => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_get".to_string(), + args: vec![hence::arg::Arg::Variable("MEM_LOOP_J".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Tuck => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Fetch => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "get".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::FetchPrint => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "get".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_set".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_MEM_OUT".to_string())], + }, + ]), + Instruction::Store => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::Call { + name: "set".to_string(), + arg: None, + }, + ]), + Instruction::PlusStore => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "get".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_ADD".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "set".to_string(), + arg: None, + }, + ]), + Instruction::MinusStore => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "get".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_SUB".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "set".to_string(), + arg: None, + }, + ]), + Instruction::Cells => Ok(vec![]), + Instruction::Allot => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_get".to_string(), + args: vec![hence::arg::Arg::Variable("MEM_ALLOC_PTR".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_ADD".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_set".to_string(), + args: vec![hence::arg::Arg::Variable("MEM_ALLOC_PTR".to_string())], + }, + ]), + Instruction::Dot => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_set".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_MEM_OUT".to_string())], + }, + ]), + Instruction::Emit => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_set".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_MEM_CHR".to_string())], + }, + ]), + Instruction::Space => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_rset".to_string(), + args: vec![ + hence::arg::Arg::Variable("CORE_REG_A".to_string()), + hence::arg::Arg::Char(' '), + ], + }, + hence::parser::ast::Node::MacroCall { + name: "std_set".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_MEM_CHR".to_string())], + }, + ]), + Instruction::Spaces => Ok(vec![]), + Instruction::Cr => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_rset".to_string(), + args: vec![ + hence::arg::Arg::Variable("CORE_REG_A".to_string()), + hence::arg::Arg::Char('\n'), + ], + }, + hence::parser::ast::Node::MacroCall { + name: "std_set".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_MEM_CHR".to_string())], + }, + ]), + Instruction::StringPrint(x) => { + let loop_label = format!("loop_strings_{}", x); + let data_label = format!("data_strings_{}", x); + let data_end_label = format!("data_strings_end_{}", x); + + Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_rset".to_string(), + args: vec![ + hence::arg::Arg::Variable("CORE_REG_B".to_string()), + hence::arg::Arg::Variable(data_label), + ], + }, + hence::parser::ast::Node::Label(loop_label.clone()), + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::Call { + name: "get".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_set".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_MEM_CHR".to_string())], + }, + hence::parser::ast::Node::MacroCall { + name: "std_rset".to_string(), + args: vec![ + hence::arg::Arg::Variable("CORE_REG_A".to_string()), + hence::arg::Arg::Number(1), + ], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_ADD".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_rset".to_string(), + args: vec![ + hence::arg::Arg::Variable("CORE_REG_A".to_string()), + hence::arg::Arg::Variable(data_end_label), + ], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_GT".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_cond_jump".to_string(), + args: vec![hence::arg::Arg::Variable(loop_label)], + }, + ]) + } + Instruction::StringReference(x) => Ok(vec![ + hence::parser::ast::Node::Call { + name: "push".to_string(), + arg: Some(hence::arg::Arg::Variable(format!("data_strings_end_{}", x))), + }, + hence::parser::ast::Node::Call { + name: "push".to_string(), + arg: Some(hence::arg::Arg::Variable(format!("data_strings_{}", x))), + }, + ]), + Instruction::Count => Ok(vec![]), + Instruction::Not => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_NOT".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::And => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_AND".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Nand => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_AND".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_NOT".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Or => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_OR".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Nor => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_OR".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_NOT".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Xor => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_XOR".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Xnor => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_XOR".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_NOT".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Lsh => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_LSH".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Rsh => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_RSH".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Compare => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_CMP".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Eq => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_EQ".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Neq => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_EQ".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_INV".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Lt => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_LT".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Leq => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_LEQ".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Gt => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_GT".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Geq => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_GEQ".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Min => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_GT".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tlrc".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Max => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_LT".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tlrc".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Boolean => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_BOL".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Invert => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_INV".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Random => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_RND".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Plus => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_ADD".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::OnePlus => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_rset".to_string(), + args: vec![ + hence::arg::Arg::Variable("CORE_REG_B".to_string()), + hence::arg::Arg::Number(1), + ], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_ADD".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::TwoPlus => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_rset".to_string(), + args: vec![ + hence::arg::Arg::Variable("CORE_REG_B".to_string()), + hence::arg::Arg::Number(2), + ], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_ADD".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Minus => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_SUB".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::OneMinus => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_rset".to_string(), + args: vec![ + hence::arg::Arg::Variable("CORE_REG_B".to_string()), + hence::arg::Arg::Number(1), + ], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_SUB".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::TwoMinus => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_rset".to_string(), + args: vec![ + hence::arg::Arg::Variable("CORE_REG_B".to_string()), + hence::arg::Arg::Number(2), + ], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_SUB".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Times => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_MUL".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Divide => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "stack_transfer_alu".to_string(), + args: vec![], + }, + hence::parser::ast::Node::MacroCall { + name: "std_alu".to_string(), + args: vec![hence::arg::Arg::Variable("CORE_ALU_DIV".to_string())], + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + Instruction::Mod => Ok(vec![]), + Instruction::Call(x) => match data.words.get(x) { + Some(w) => { + if w.times_used > 1 { + Ok(vec![hence::parser::ast::Node::MacroCall { + name: "call_stack_jump".to_string(), + args: vec![ + hence::arg::Arg::Variable(format!("words_{}", w.id)), + hence::arg::Arg::Variable("OFFSET".to_string()), + ], + }]) + } else { + Ok(w.instructions + .iter() + .map(|ins| ins.compile(data)) + .collect::>>()? + .into_iter() + .flatten() + .collect()) + } + } + None => bail!("Unknown word: {}", x), + }, + // Instruction::Call(x) => bail!("Unknown word: {}", x), + Instruction::AsmQuote(x) => Ok(hence::parser::parse(hence::lexer::lex(x)?)?.body), + // Instruction::Condition(x) => Ok(vec![]), + Instruction::Condition(x) => { + dbg!(x); + + Ok(vec![]) + } + + Instruction::Multiple { instruction, count } => { + if *count == 0 { + Ok(vec![]) + } else if *count == 1 { + Ok(instruction.compile(data)?) + } else { + match **instruction { + Instruction::Nop => Ok(Instruction::Nop.compile(data)?), + Instruction::Quit => Ok(Instruction::Quit.compile(data)?), + Instruction::Push(x) => Ok([ + Instruction::Push(x).compile(data)?, + [hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }] + .into_iter() + .cycle() + .take(count - 1) + .collect(), + ] + .concat()), + Instruction::Depth => Ok([ + Instruction::Depth.compile(data)?, + Instruction::Multiple { + instruction: Box::new(Instruction::Dup), + count: count - 1, + } + .compile(data)?, + ] + .concat()), + Instruction::Dup => Ok([ + Instruction::Dup.compile(data)?, + [hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }] + .into_iter() + .cycle() + .take(count - 1) + .collect(), + ] + .concat()), + Instruction::Swap => { + if count % 2 == 0 { + Ok(vec![]) + } else { + Ok(Instruction::Swap.compile(data)?) + } + } + Instruction::Over => Ok([ + Instruction::Over.compile(data)?, + (0..count - 1) + .flat_map(|n| { + if n % 2 == 0 { + vec![ + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable( + "CORE_REG_A".to_string(), + )), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ] + } else { + vec![ + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable( + "CORE_REG_B".to_string(), + )), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ] + } + }) + .collect(), + ] + .concat()), + Instruction::Rot => match count % 3 { + 0 => Ok(vec![]), + 1 => Ok(Instruction::Rot.compile(data)?), + 2 => Ok(vec![ + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }, + hence::parser::ast::Node::Call { + name: "tlr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_A".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_B".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + hence::parser::ast::Node::Call { + name: "tsr".to_string(), + arg: Some(hence::arg::Arg::Variable("CORE_REG_C".to_string())), + }, + hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }, + ]), + x => bail!("Remainder of count % 3 cannot be higher than 3: {}", x), + }, + Instruction::Nip => Ok([ + vec![hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }], + [hence::parser::ast::Node::Call { + name: "pop".to_string(), + arg: None, + }] + .into_iter() + .cycle() + .take(*count) + .collect(), + vec![hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }], + ] + .concat()), + Instruction::I | Instruction::J => Ok([ + instruction.compile(data)?, + [hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }] + .into_iter() + .cycle() + .take(count - 1) + .collect(), + ] + .concat()), + Instruction::Fetch => Ok([ + vec![hence::parser::ast::Node::MacroCall { + name: "std_ld".to_string(), + args: vec![], + }], + [hence::parser::ast::Node::Call { + name: "get".to_string(), + arg: None, + }] + .into_iter() + .cycle() + .take(*count) + .collect(), + vec![hence::parser::ast::Node::Call { + name: "tls".to_string(), + arg: None, + }], + ] + .concat()), + Instruction::Space | Instruction::Cr => Ok([ + instruction.compile(data)?, + [hence::parser::ast::Node::Call { + name: "set".to_string(), + arg: None, + }] + .into_iter() + .cycle() + .take(count - 1) + .collect(), + ] + .concat()), + _ => { + let compiled = instruction.compile(data)?; + let len = compiled.len() * count; + Ok(compiled.into_iter().cycle().take(len).collect()) + } + } + } + } + } + } +} + +impl Instruction { + pub fn from_word(word: &str) -> Option { + match word.to_lowercase().as_str() { + "nop" => Some(Instruction::Nop), + "debug" => Some(Instruction::Debug), + "quit" => Some(Instruction::Quit), + "drop" => Some(Instruction::Drop), + "depth" => Some(Instruction::Depth), + "pick" => Some(Instruction::Pick), + "dup" => Some(Instruction::Dup), + "swap" => Some(Instruction::Swap), + "over" => Some(Instruction::Over), + "rot" => Some(Instruction::Rot), + "nip" => Some(Instruction::Nip), + "i" => Some(Instruction::I), + "j" => Some(Instruction::J), + "tuck" => Some(Instruction::Tuck), + "@" => Some(Instruction::Fetch), + "?" => Some(Instruction::FetchPrint), + "!" => Some(Instruction::Store), + "+!" => Some(Instruction::PlusStore), + "-!" => Some(Instruction::MinusStore), + "cells" => Some(Instruction::Cells), + "allot" => Some(Instruction::Allot), + "." => Some(Instruction::Dot), + "emit" => Some(Instruction::Emit), + "space" => Some(Instruction::Space), + "spaces" => Some(Instruction::Spaces), + "cr" => Some(Instruction::Cr), + "count" => Some(Instruction::Count), + "not" => Some(Instruction::Not), + "and" => Some(Instruction::And), + "nand" => Some(Instruction::Nand), + "or" => Some(Instruction::Or), + "nor" => Some(Instruction::Nor), + "xor" => Some(Instruction::Xor), + "xnor" => Some(Instruction::Xnor), + "lshift" => Some(Instruction::Lsh), + "rshift" => Some(Instruction::Rsh), + "compare" => Some(Instruction::Compare), + "=" => Some(Instruction::Eq), + "!=" => Some(Instruction::Neq), + "<" => Some(Instruction::Lt), + "<=" => Some(Instruction::Leq), + ">" => Some(Instruction::Gt), + ">=" => Some(Instruction::Geq), + "min" => Some(Instruction::Min), + "max" => Some(Instruction::Max), + "boolean" => Some(Instruction::Boolean), + "invert" => Some(Instruction::Invert), + "random" => Some(Instruction::Random), + "+" => Some(Instruction::Plus), + "1+" => Some(Instruction::OnePlus), + "2+" => Some(Instruction::TwoPlus), + "-" => Some(Instruction::Minus), + "1-" => Some(Instruction::OneMinus), + "2-" => Some(Instruction::TwoMinus), + "*" => Some(Instruction::Times), + "/" => Some(Instruction::Divide), + "mod" => Some(Instruction::Mod), + _ => None, + } + } +} diff --git a/henceforth/src/compiler/templates/default.asm b/henceforth/src/compiler/templates/default.asm new file mode 100644 index 0000000..847df67 --- /dev/null +++ b/henceforth/src/compiler/templates/default.asm @@ -0,0 +1,67 @@ +.include "$lib/core.asm" +.include "$lib/std.asm" +.include "$lib/main.asm" + +.define MEM_LOOP_I, CORE_MEM_MEM +.define MEM_LOOP_J, (MEM_LOOP_I + 1) + +.define MEM_CALL_STACK_LEN, 16 +.define MEM_CALL_STACK_PTR, (MEM_LOOP_J + 1) +.define MEM_CALL_STACK_END, (MEM_CALL_STACK_PTR + MEM_CALL_STACK_LEN) + +.define MEM_ALLOC_PTR, MEM_CALL_STACK_END + +.macro stack_transfer_alu + .std_ld + tlr CORE_REG_B + .std_ld + tlr CORE_REG_A +.endmacro + +.macro call_stack_jump, call_stack_jump_arg_0_label, call_stack_jump_arg_1_offset + .std_rset CORE_REG_C, call_stack_jump_arg_0_label + .std_rset CORE_REG_D, (call_stack_jump_arg_1_offset + 7) + ts call_stack_jump + tlr CORE_REG_PC +.endmacro + +.macro return_call_stack_jump + .std_jump return_call_stack_jump +.endmacro + +.std_rset CORE_REG_A, MEM_CALL_STACK_PTR +.std_set MEM_CALL_STACK_PTR + +.std_rset CORE_REG_A, (MEM_ALLOC_PTR + 1) +.std_set MEM_ALLOC_PTR + +.jump_main + +call_stack_jump: + .std_get MEM_CALL_STACK_PTR + tlr CORE_REG_A + .std_rset CORE_REG_B, 1 + .std_alu CORE_ALU_ADD + tlr CORE_REG_A + tlr CORE_REG_B + .std_set MEM_CALL_STACK_PTR + + tsr CORE_REG_D + tlr CORE_REG_A + tsr CORE_REG_B + set + tsr CORE_REG_C + tlr CORE_REG_PC + +return_call_stack_jump: + .std_get MEM_CALL_STACK_PTR + tlr CORE_REG_A + tlr CORE_REG_C + .std_rset CORE_REG_B, 1 + .std_alu CORE_ALU_SUB + tlr CORE_REG_A + .std_set MEM_CALL_STACK_PTR + + tsr CORE_REG_C + get + tlr CORE_REG_PC diff --git a/henceforth/src/lexer.rs b/henceforth/src/lexer.rs new file mode 100644 index 0000000..edc3e83 --- /dev/null +++ b/henceforth/src/lexer.rs @@ -0,0 +1,84 @@ +use anyhow::Result; +use hence::assembler::ToCode; +use itertools::Itertools; + +#[derive(Debug)] +pub enum Token { + Newline(usize), + Whitespace(usize), + + ParenComment(String), + BackslashComment(String), + DoubleDashComment(String), + + StringLiteral { mode: String, string: String }, + Number(String), + Word(String), +} + +impl ToCode for Token { + fn to_code(&self) -> String { + match self { + Token::Newline(x) => ["\n"].into_iter().cycle().take(*x).join(""), + Token::Whitespace(x) => [" "].into_iter().cycle().take(*x).join(""), + Token::ParenComment(x) => format!("( {})", x), + Token::BackslashComment(x) => format!("\\{}", x), + Token::DoubleDashComment(x) => format!("-- {}", x), + Token::StringLiteral { mode, string } => format!("{}\" {}\"", mode, string), + Token::Number(x) | Token::Word(x) => x.clone(), + } + } +} + +pub fn is_space(c: char) -> bool { + c.is_whitespace() || c == '\n' +} + +pub fn lex(source: &str) -> Result> { + let mut chars = source.chars().peekable(); + let mut tokens: Vec = vec![]; + + while let Some(c) = chars.peek() { + tokens.push(match c { + '\n' => Token::Newline(chars.peeking_take_while(|&c| c == '\n').count()), + _ if c.is_whitespace() => { + Token::Whitespace(chars.peeking_take_while(|&c| c.is_whitespace()).count()) + } + '\\' => Token::BackslashComment(chars.peeking_take_while(|&c| c != '\n').collect()), + _ if c.is_numeric() => { + Token::Number(chars.peeking_take_while(|&c| !is_space(c)).collect()) + } + _ => { + let x: String = chars.peeking_take_while(|&c| !is_space(c)).collect(); + let mut iter = x.chars(); + + match x.as_str() { + "(" => Token::ParenComment( + chars.by_ref().skip(1).take_while(|&c| c != ')').collect(), + ), + "--" => Token::DoubleDashComment( + chars.by_ref().take_while(|&c| c != '\n').collect(), + ), + _ if x.ends_with('"') => Token::StringLiteral { + mode: x.chars().take(x.len() - 1).collect(), + string: chars.by_ref().skip(1).take_while(|&c| c != '"').collect(), + }, + _ if iter.next() == Some('-') => { + if let Some(c) = iter.next() { + if c.is_numeric() { + Token::Number(x) + } else { + Token::Word(x) + } + } else { + Token::Word(x) + } + } + _ => Token::Word(x), + } + } + }); + } + + Ok(tokens) +} diff --git a/henceforth/src/lib.rs b/henceforth/src/lib.rs new file mode 100644 index 0000000..ac7a00d --- /dev/null +++ b/henceforth/src/lib.rs @@ -0,0 +1,3 @@ +pub mod compiler; +pub mod lexer; +pub mod parser; diff --git a/henceforth/src/main.rs b/henceforth/src/main.rs new file mode 100644 index 0000000..aad9f91 --- /dev/null +++ b/henceforth/src/main.rs @@ -0,0 +1,80 @@ +use anyhow::Result; +use clap::{Parser, Subcommand}; +use hence::assembler::ToCode; +use std::fs; + +use henceforth::*; + +#[derive(Debug, Parser)] +#[clap(author, version, about, long_about = None)] +struct Cli { + #[clap(subcommand)] + commands: Commands, +} + +#[derive(Debug, Subcommand)] +enum Commands { + #[clap(about = "Lexes source code and outputs tokens")] + Lex { + #[clap(value_parser)] + src: String, + }, + #[clap(about = "Parses source code and outputs AST")] + Parse { + #[clap(value_parser)] + src: String, + }, + #[clap(about = "Compiles assembly from source code")] + Compile { + #[clap(value_parser)] + src: String, + #[clap(value_parser)] + out: Option, + #[clap(short, long, action)] + optimize: Option, + #[clap(long, action)] + dump: bool, + }, +} + +fn main() -> Result<()> { + let args = Cli::parse(); + match args.commands { + Commands::Lex { src } => { + let source = fs::read_to_string(src)?; + let tokens = lexer::lex(&source)?; + println!("{:#?}", tokens); + + Ok(()) + } + Commands::Parse { src } => { + let source = fs::read_to_string(src)?; + let tokens = lexer::lex(&source)?; + let body = parser::parse(tokens)?; + println!("{:#?}", body); + + Ok(()) + } + Commands::Compile { + src, + out, + optimize, + dump, + } => { + let source = fs::read_to_string(&src)?; + let tokens = lexer::lex(&source)?; + let ast = parser::parse(tokens)?; + let ast = compiler::compile(ast, optimize.unwrap_or(true))?; + let assembly = format!("{}\n", ast.to_code()); + + if dump { + print!("{}", assembly); + } + if let Some(x) = out { + fs::write(x, &assembly)?; + } + + Ok(()) + } + } +} diff --git a/henceforth/src/parser.rs b/henceforth/src/parser.rs new file mode 100644 index 0000000..f1d6e35 --- /dev/null +++ b/henceforth/src/parser.rs @@ -0,0 +1,168 @@ +use anyhow::{bail, Result}; +use parse_int; + +use crate::lexer; + +pub mod ast; + +pub fn parse_stack_state(s: Option<&str>) -> Vec { + match s { + Some(x) if !x.trim().is_empty() => { + x.split_whitespace().map(|x| x.trim().to_string()).collect() + } + _ => vec![], + } +} + +pub fn parse_stack_result(s: &str) -> ast::StackResult { + let mut splitter = s.splitn(2, "--"); + + ast::StackResult { + before: parse_stack_state(splitter.next()), + after: parse_stack_state(splitter.next()), + } +} + +pub fn parse(tokens: Vec) -> Result { + let mut iter = tokens.into_iter().peekable(); + let mut body: ast::Body = vec![]; + + while let Some(token) = iter.next() { + match token { + lexer::Token::Newline(_) | lexer::Token::Whitespace(_) => {} + lexer::Token::ParenComment(x) + | lexer::Token::BackslashComment(x) + | lexer::Token::DoubleDashComment(x) => { + body.push(ast::Node::Comment(x.trim().to_string())); + } + lexer::Token::StringLiteral { mode, string } => { + body.push(ast::Node::String { mode, string }); + } + lexer::Token::Number(x) => body.push(ast::Node::Number(parse_int::parse(&x)?)), + lexer::Token::Word(x) => match x.as_str() { + ":" => { + let mut depth: usize = 1; + let mut content = iter + .by_ref() + .take_while(|t| match t { + lexer::Token::Word(x) => match x.as_str() { + ":" => { + depth += 1; + true + } + ";" => { + depth -= 1; + depth != 0 + } + _ => true, + }, + _ => true, + }) + .collect::>() + .into_iter() + .peekable(); + if depth != 0 { + bail!("Unbalanced word definitions"); + } + + let name = match content.find(|t| { + !matches!(t, lexer::Token::Newline(_) | lexer::Token::Whitespace(_)) + }) { + Some(t) => match t { + lexer::Token::Word(x) => x.clone(), + _ => bail!("Word definition name must be a word itself: {:?}", t), + }, + None => bail!("Word definition can not be empty"), + }; + let stack = loop { + if let Some(t) = content.peek() { + match t { + lexer::Token::Newline(_) | lexer::Token::Whitespace(_) => { + content.next(); + } + lexer::Token::ParenComment(x) + | lexer::Token::BackslashComment(x) + | lexer::Token::DoubleDashComment(x) => { + let y = &x.to_string(); + content.next(); + break Some(parse_stack_result(y)); + } + _ => break None, + } + } else { + break None; + } + }; + + body.push(ast::Node::WordDefinition { + name, + stack, + body: parse(content.collect())?.body, + }); + } + "if" => { + let mut depth: usize = 1; + let mut else_used = false; + let if_toks: Vec<_> = iter + .by_ref() + .take_while(|t| match t { + lexer::Token::Word(x) => match x.as_str() { + "if" => { + depth += 1; + true + } + "else" => { + if depth == 1 { + else_used = true; + false + } else { + true + } + } + "then" => { + depth -= 1; + depth != 0 + } + _ => true, + }, + _ => true, + }) + .collect(); + let else_toks: Vec<_> = if else_used { + iter.by_ref() + .take_while(|t| match t { + lexer::Token::Word(x) => match x.as_str() { + "if" => { + depth += 1; + true + } + "then" => { + depth -= 1; + depth != 0 + } + _ => true, + }, + _ => true, + }) + .collect() + } else { + vec![] + }; + if depth != 0 { + bail!("Unbalanced conditions"); + } + + body.push(ast::Node::Condition { + if_body: parse(if_toks)?.body, + else_body: parse(else_toks)?.body, + }); + } + _ => { + body.push(ast::Node::Word(x)); + } + }, + } + } + + Ok(ast::AST { body }) +} diff --git a/henceforth/src/parser/ast.rs b/henceforth/src/parser/ast.rs new file mode 100644 index 0000000..fdc5525 --- /dev/null +++ b/henceforth/src/parser/ast.rs @@ -0,0 +1,90 @@ +use hence::assembler::ToCode; +use itertools::Itertools; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct StackResult { + pub before: Vec, + pub after: Vec, +} + +impl ToCode for StackResult { + fn to_code(&self) -> String { + format!( + "{}--{}", + if self.before.is_empty() { + "".to_string() + } else { + format!("{} ", self.before.join(" ")) + }, + if self.after.is_empty() { + "".to_string() + } else { + format!("{} ", self.after.join(" ")) + } + ) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Node { + Comment(String), + String { + mode: String, + string: String, + }, + Number(i32), + WordDefinition { + name: String, + stack: Option, + body: Body, + }, + Condition { + if_body: Body, + else_body: Body, + }, + Word(String), +} + +impl ToCode for Node { + fn to_code(&self) -> String { + match self { + Node::Comment(x) => format!("\\ {}", x), + Node::String { mode, string } => format!("{}\" {}\"", mode, string), + Node::Number(x) => x.to_string(), + Node::WordDefinition { name, stack, body } => format!( + ": {}{} {} ;", + name, + match stack { + Some(x) => format!(" {}", x.to_code()), + None => "".to_string(), + }, + body.iter().map(|x| x.to_code()).join(" ") + ), + Node::Condition { if_body, else_body } => { + if else_body.is_empty() { + format!("if {} then", if_body.iter().map(|x| x.to_code()).join(" ")) + } else { + format!( + "if {} else {} then", + if_body.iter().map(|x| x.to_code()).join(" "), + else_body.iter().map(|x| x.to_code()).join(" ") + ) + } + } + Node::Word(x) => x.to_owned(), + } + } +} + +pub type Body = Vec; + +#[derive(Debug)] +pub struct AST { + pub body: Body, +} + +impl ToCode for AST { + fn to_code(&self) -> String { + self.body.iter().map(|x| x.to_code()).join(" ") + } +} diff --git a/presentation/.gitignore b/presentation/.gitignore new file mode 100644 index 0000000..849ddff --- /dev/null +++ b/presentation/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/presentation/Makefile b/presentation/Makefile new file mode 100644 index 0000000..42a43fa --- /dev/null +++ b/presentation/Makefile @@ -0,0 +1,30 @@ +# Author: Daniel Nicolas Gisolfi +# Date: 2020-8-11 + +source=$(shell find . -type f -name "*.md") +title=$(shell grep -m 1 title $(source) | cut -d ':' -f2 | xargs) +version=$(shell grep -m 1 version $(source) | cut -d ':' -f2 | xargs) +theme=$(shell find . -type f -name "*.css") +flags=--allow-local-files --theme $(theme) +devflags=--server --watch --allow-local-files +dist="./dist" + +.PHONY: pptx +pptx: + marp $(flags) "$(source)" -o "$(dist)/$(title)-$(version).pptx" + +.PHONY: pdf +pdf: + marp $(flags) "$(source)" -o "$(dist)/$(title)-$(version).pdf" + +.PHONY: html +html: + marp $(flags) "$(source)" -o "$(dist)/$(title)-$(version).html" + +.PHONY: png +png: + marp $(flags) --images png "$(source)" + +.PHONY: dev +dev: + marp $(devflags) . diff --git a/presentation/assets/assembler.png b/presentation/assets/assembler.png new file mode 100644 index 0000000..4316a1d Binary files /dev/null and b/presentation/assets/assembler.png differ diff --git a/presentation/assets/brian-kostiuk-S4jSvcHYcOs-unsplash.jpg b/presentation/assets/brian-kostiuk-S4jSvcHYcOs-unsplash.jpg new file mode 100644 index 0000000..be15751 Binary files /dev/null and b/presentation/assets/brian-kostiuk-S4jSvcHYcOs-unsplash.jpg differ diff --git a/presentation/assets/harrison-broadbent-VOz0gV9HC0I-unsplash.jpg b/presentation/assets/harrison-broadbent-VOz0gV9HC0I-unsplash.jpg new file mode 100644 index 0000000..9e89313 Binary files /dev/null and b/presentation/assets/harrison-broadbent-VOz0gV9HC0I-unsplash.jpg differ diff --git a/presentation/assets/was-ist-cpu-darstellung.png b/presentation/assets/was-ist-cpu-darstellung.png new file mode 100644 index 0000000..201e415 Binary files /dev/null and b/presentation/assets/was-ist-cpu-darstellung.png differ diff --git a/presentation/dracula.css b/presentation/dracula.css new file mode 100644 index 0000000..3391082 --- /dev/null +++ b/presentation/dracula.css @@ -0,0 +1,330 @@ +@charset "UTF-8"; +/*! + * Marp Dracula theme. + * @theme dracula + * @author Daniel Nicolas Gisolfi + * + * @auto-scaling true + * @size 4:3 960px 720px + * @size 16:9 1280px 720px + */ + +@import url("https://fonts.googleapis.com/css?family=Lato:400,900|IBM+Plex+Sans:400,700"); + +:root { + --dracula-background: #282a36; + --dracula-current-line: #44475a; + --dracula-foreground: #f8f8f2; + --dracula-comment: #6272a4; + --dracula-cyan: #8be9fd; + --dracula-green: #50fa7b; + --dracula-orange: #ffb86c; + --dracula-pink: #ff79c6; + --dracula-purple: #bd93f9; + --dracula-red: #ff5555; + --dracula-yellow: #f1fa8c; +} + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: var(--dracula-background); +} + +/* Dracula Foreground */ +.hljs, +.hljs-subst, +.hljs-typing, +.hljs-variable, +.hljs-template-variable { + color: var(--dracula-foreground); +} + +/* Dracula Comment */ +.hljs-comment, +.hljs-quote, +.hljs-deletion { + color: var(--dracula-comment); +} + +/* Dracula Cyan */ +.hljs-meta .hljs-doctag, +.hljs-built_in, +.hljs-selector-tag, +.hljs-section, +.hljs-link, +.hljs-class { + color: var(--dracula-cyan); +} + +/* Dracula Green */ +.hljs-title { + color: var(--dracula-green); +} + +/* Dracula Orange */ +.hljs-params { + color: var(--dracula-orange); +} + +/* Dracula Pink */ +.hljs-keyword { + color: var(--dracula-pink); +} + +/* Dracula Purple */ +.hljs-literal, +.hljs-number { + color: var(--dracula-purple); +} + +/* Dracula Red */ +.hljs-regexp { + color: var(--dracula-red); +} + +/* Dracula Yellow */ +.hljs-string, +.hljs-name, +.hljs-type, +.hljs-attr, +.hljs-symbol, +.hljs-bullet, +.hljs-addition, +.hljs-template-tag { + color: var(--dracula-yellow); +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-literal, +.hljs-title, +.hljs-section, +.hljs-doctag, +.hljs-type, +.hljs-name, +.hljs-strong { + font-weight: bold; +} + +.hljs-params, +.hljs-emphasis { + font-style: italic; +} + +svg[data-marp-fitting="svg"] { + max-height: 580px; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0.5em 0 0 0; + color: var(--dracula-pink); +} +h1 strong, +h2 strong, +h3 strong, +h4 strong, +h5 strong, +h6 strong { + font-weight: inherit; +} + +h1 { + font-size: 1.8em; +} + +h2 { + font-size: 1.5em; +} + +h3 { + font-size: 1.3em; +} + +h4 { + font-size: 1.1em; +} + +h5 { + font-size: 1em; +} + +h6 { + font-size: 0.9em; +} + +p, +blockquote { + margin: 1em 0 0 0; +} + +ul > li, +ol > li { + margin: 0.3em 0 0 0; + color: var(--dracula-cyan); +} +ul > li > p, +ol > li > p { + margin: 0.6em 0 0 0; +} + +code { + display: inline-block; + font-family: "IBM Plex Mono", monospace; + font-size: 0.8em; + letter-spacing: 0; + margin: -0.1em 0.15em; + padding: 0.1em 0.2em; + vertical-align: baseline; + color: var(--dracula-green); +} + +pre { + display: block; + margin: 1em 0 0 0; + min-height: 1em; + overflow: visible; +} +pre code { + box-sizing: border-box; + margin: 0; + min-width: 100%; + padding: 0.5em; + font-size: 0.7em; +} +pre code svg[data-marp-fitting="svg"] { + max-height: calc(580px - 1em); +} + +blockquote { + margin: 1em 0 0 0; + padding: 0 1em; + position: relative; + color: var(--dracula-orange); +} +blockquote::after, +blockquote::before { + content: "“"; + display: block; + font-family: "Times New Roman", serif; + font-weight: bold; + position: absolute; + color: var(--dracula-green); +} +blockquote::before { + top: 0; + left: 0; +} +blockquote::after { + right: 0; + bottom: 0; + transform: rotate(180deg); +} +blockquote > *:first-child { + margin-top: 0; +} + +mark { + background: transparent; +} + +table { + border-spacing: 0; + border-collapse: collapse; + margin: 1em 0 0 0; +} +table th, +table td { + padding: 0.2em 0.4em; + border-width: 1px; + border-style: solid; +} + +section { + font-size: 35px; + font-family: "IBM Plex Sans"; + line-height: 1.35; + letter-spacing: 1.25px; + padding: 70px; + color: var(--dracula-foreground); + background-color: var(--dracula-background); +} +section > *:first-child, +section > header:first-child + * { + margin-top: 0; +} +section a, +section mark { + color: var(--dracula-red); +} +section code { + background: var(--dracula-current-line); + color: var(--dracula-current-green); +} +section h1 strong, +section h2 strong, +section h3 strong, +section h4 strong, +section h5 strong, +section h6 strong { + color: var(--dracula-current-line); +} +section pre > code { + background: var(--dracula-current-line); +} +section header, +section footer, +section section::after, +section blockquote::before, +section blockquote::after { + color: var(--dracula-comment); +} +section table th, +section table td { + border-color: var(--dracula-current-line); +} +section table thead th { + background: var(--dracula-current-line); + color: var(--dracula-yellow); +} +section table tbody > tr:nth-child(even) td, +section table tbody > tr:nth-child(even) th { + background: var(--dracula-current-line); +} + +header, +footer, +section::after { + box-sizing: border-box; + font-size: 66%; + height: 70px; + line-height: 50px; + overflow: hidden; + padding: 10px 25px; + position: absolute; + color: var(--dracula-comment); +} + +header { + left: 0; + right: 0; + top: 0; +} + +footer { + left: 0; + right: 0; + bottom: 0; +} + +section::after { + right: 0; + bottom: 0; + font-size: 80%; +} diff --git a/presentation/presentation.md b/presentation/presentation.md new file mode 100644 index 0000000..409ae58 --- /dev/null +++ b/presentation/presentation.md @@ -0,0 +1,101 @@ +--- +title: Hence +version: 1.0.0 +theme: dracula +footer: Grimm +header: Hence +marp: true +size: 4K +--- + +# Hence + +Ein Ausflug in die Welt einer virtuellen CPU + +_Dominic Grimm_ + + + +![bg left](./assets/brian-kostiuk-S4jSvcHYcOs-unsplash.jpg) + +--- + +# Inhalt + +- Was ist eine CPU? +- Überblick Assembler +- Hence +- (Hence-)Forth + +=> _Workshop_ + +--- + +# Was ist eine CPU? + +- Herzstück eines Computers +- Kleiner Chip auf Hauptplatine +- Verarbeitung Arithmetik und Logik +- Zugriff auf Speicher und Peripherie + +![bg right](./assets/harrison-broadbent-VOz0gV9HC0I-unsplash.jpg) + +--- + +## Aufbau + +![bg right:35% fit](./assets/was-ist-cpu-darstellung.png) + +- _Steuerwerk, Rechenwerk, Cache, Register_ +- Cache: schneller Speicher für oft benötigte Daten +- CPU kommuniziert über Bus-Systeme +- Leistung abhängig von Kernzahl, Taktfrequenz, Cache und Architektur +- Neueste Entwicklungen: Multi-Core-Prozessoren und KI + +--- + +## Machine code + +- Niedrigste Ebene der Programmierung. +- Binäre Instruktionen, direkt von der CPU ausgeführt +- Jede CPU-Architektur hat ihre eigene Maschinensprache, die spezifisch für diese Architektur ist + +- Sehr effizient, da direkt von CPU ausgeführt +- Meist in höherer Programmiersprache geschrieben + +=> *Assembler* + +--- + +### Beispiel + +*hexadezimal kodiert (`hexdump`)* + +``` +00000000 03 7f fc 86 03 a0 00 06 00 06 03 c0 02 8b 06 00 |................| +00000010 05 03 c0 01 8c 04 00 06 8c 03 00 01 06 00 05 03 |................| +* +``` + +| Code | Opcode | Argument | +| ---------- | -------------- | ---------------- | +| `03 7f fc` | `0x03` (`ts`) | `0x7ffc` (32764) | +| `86` | `0x06` (`tlr`) | | +| `03 a0 00` | `0x03` (`ts`) | `0xa000` (40960) | + +--- + +# Überblick Assembler + +![width:30cm](./assets/assembler.png) + +--- + +# Hence + +- Virtuelle "CPU" (eher SBC) +-