From 366ac05225d04a1a41cd04faadd51fbba5c82145 Mon Sep 17 00:00:00 2001 From: Dominic Grimm Date: Tue, 23 Aug 2022 16:20:38 +0200 Subject: [PATCH] Upgrades upgrades upgrades --- Cargo.lock | 55 +++-- Cargo.toml | 2 +- README.md | 33 ++- examples/forth.asm | 147 ++++++++++++++ examples/forth.bin | Bin 0 -> 32768 bytes examples/test.asm | 22 -- examples/test.lisp | 5 - hence/Cargo.toml | 3 +- hence/src/bin/main.rs | 13 +- hence/src/lib/arg.rs | 29 +-- hence/src/lib/assembler.rs | 342 ++++++++++++++++++++------------ hence/src/lib/emulator.rs | 54 ++--- hence/src/lib/lexer.rs | 24 +-- hence/src/lib/parser.rs | 19 +- hence/src/lib/parser/ast.rs | 4 +- hencelisp/.gitignore | 1 - hencelisp/Cargo.toml | 19 -- hencelisp/src/bin/main.rs | 59 ------ hencelisp/src/lib/compiler.rs | 61 ------ hencelisp/src/lib/lexer.rs | 99 --------- hencelisp/src/lib/lib.rs | 3 - hencelisp/src/lib/parser.rs | 65 ------ hencelisp/src/lib/parser/ast.rs | 30 --- 23 files changed, 495 insertions(+), 594 deletions(-) create mode 100644 examples/forth.asm create mode 100644 examples/forth.bin delete mode 100644 examples/test.asm delete mode 100644 examples/test.lisp delete mode 100644 hencelisp/.gitignore delete mode 100644 hencelisp/Cargo.toml delete mode 100644 hencelisp/src/bin/main.rs delete mode 100644 hencelisp/src/lib/compiler.rs delete mode 100644 hencelisp/src/lib/lexer.rs delete mode 100644 hencelisp/src/lib/lib.rs delete mode 100644 hencelisp/src/lib/parser.rs delete mode 100644 hencelisp/src/lib/parser/ast.rs diff --git a/Cargo.lock b/Cargo.lock index 7e14c59..3c3ac01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,9 +33,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "3.2.12" +version = "3.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8b79fe3946ceb4a0b1c080b4018992b8d27e9ff363644c1c9b6387c854614d" +checksum = "a3dbbb6653e7c55cc8595ad3e1f7be8f32aba4eb7ff7f0fd1163d4f3d137c0a9" dependencies = [ "atty", "bitflags", @@ -50,9 +50,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.7" +version = "3.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" +checksum = "9ba52acd3b0a5c33aeada5cdaa3267cdc7c594a98731d4268cdc1532f4264cb4" dependencies = [ "heck", "proc-macro-error", @@ -70,12 +70,32 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "console" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89eab4d20ce20cea182308bca13088fecea9c05f6776cf287205d41a0ed3c847" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "terminal_size", + "unicode-width", + "winapi", +] + [[package]] name = "either" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "getrandom" version = "0.2.7" @@ -104,6 +124,7 @@ name = "hence" version = "0.1.0" dependencies = [ "clap", + "console", "itertools", "num-parse", "radix_fmt", @@ -111,16 +132,6 @@ dependencies = [ "rhexdump", ] -[[package]] -name = "hencelisp" -version = "0.1.0" -dependencies = [ - "clap", - "hence", - "itertools", - "num-parse", -] - [[package]] name = "hermit-abi" version = "0.1.19" @@ -368,6 +379,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "textwrap" version = "0.15.0" @@ -380,6 +401,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 327d52a..392b71c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["hence", "hencelisp"] +members = ["hence"] diff --git a/README.md b/README.md index 31b3076..44bad58 100644 --- a/README.md +++ b/README.md @@ -10,20 +10,19 @@ # Opcodes -| Index | Name | Description | Arguments | -| ------ | ------ | ------------------------------------------ | --------- | -| `0x00` | `nop` | No operation | | -| `0x01` | `push` | Push to stack | | -| `0x02` | `pop` | Pops top of stack | | -| `0x03` | `ts` | Store value into `tmp` | | -| `0x04` | `tsr` | Store register's value into `tmp` | | -| `0x05` | `tss` | Stores top of stack into `tmp` | | -| `0x06` | `tlr` | Load `tmp`'s value into register | | -| `0x07` | `tls` | Push value of `tmp` to stack | | -| `0x08` | `ld` | Loads top of stack into register | | -| `0x09` | `st` | Loads registers's value onto top of stack | | -| `0x0a` | `dbg` | Debug | | -| `0x0b` | `alu` | Runs ALU | | -| `0x0c` | `at` | Runs ALU with `tmp`'s value as operatorion | | -| `0x0d` | `get` | Sets `tmp` to memory at address in `tmp` | | -| `0x0e` | `set` | Sets memory to value at specific address | | +| Index | Name | Description | Arguments | +| ------ | ------ | ------------------------------------------------------------------ | --------- | +| `0x00` | `nop` | No operation | | +| `0x01` | `push` | Push to stack | | +| `0x02` | `pop` | Pops top of stack | | +| `0x03` | `ts` | Store value into `tmp` | | +| `0x04` | `tsr` | Store register's value into `tmp` | | +| `0x05` | `tss` | Stores top of stack into `tmp` | | +| `0x06` | `tlr` | Load `tmp`'s value into register | | +| `0x07` | `tlrc` | Same as `tlr` but only executes if register `a`'s first bit is `1` | | +| `0x08` | `tls` | Push value of `tmp` to stack | | +| `0x09` | `ld` | Loads top of stack into register | | +| `0x0a` | `dbg` | Debug | | +| `0x0b` | `alu` | Runs ALU with `tmp`'s value as operator | | +| `0x0c` | `get` | Sets `tmp` to memory at address in `tmp` | | +| `0x0d` | `set` | Sets memory to value at specific address | | diff --git a/examples/forth.asm b/examples/forth.asm new file mode 100644 index 0000000..ee44d8a --- /dev/null +++ b/examples/forth.asm @@ -0,0 +1,147 @@ +@ hence core lib +core: + core_mem: + .define CORE_MEM_PRG, (0 * 1024) + .define CORE_MEM_ST, (32 * 1024) + .define CORE_MEM_MEM, (40 * 1024) + .define CORE_MEM_OUT, (56 * 1024) + .define CORE_MEM_CHR, (56 * 1024 + 1) + .define CORE_MEM_KEY, (56 * 1024 + 2) + + core_reg: + .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 + + core_alu: + .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 + +@ hence standard lib +STD: + .define STD_U8_MAX, 0xff + .define STD_U16_MAX, 0xffff + + .macro std_stop + ts 0xffff + tlr CORE_REG_PC + .macroend + +forth: + .define FORTH_MEM_INPUT_SIZE, 16 + .define FORTH_MEM_INPUT_DYN_SIZE, CORE_MEM_MEM + .define FORTH_MEM_INPUT_START, (FORTH_MEM_INPUT_DYN_SIZE + 1) + .define FORTH_MEM_INPUT_END, (FORTH_MEM_INPUT_START + FORTH_MEM_INPUT_SIZE) + +.define jump_main, (CORE_MEM_ST - 3 - 1) + +ts jump_main +tlr CORE_REG_PC + +end_input: + dbg + +main: + @ loop body + loop: + @ read key from stdin + ts CORE_MEM_KEY + get + + @ check if key is newline (0x0a) + tlr CORE_REG_D @ store in register D because register A is used later on + tlr CORE_REG_A @ store in register A as input for ALU + ts "\n" @ store newline in TMP + tlr CORE_REG_B @ store newline in register B as input for ALU + ts CORE_ALU_EQ @ ALU equal operation + alu @ run ALU + + @ go back to loop start if pressed key is newline + tlr CORE_REG_A @ store result of ALU operation in TMP + @ ts loop @ load memory address of loop start into TMP + ts 0 + tls + ts jump_switch + tls + ts end_input + tlrc CORE_REG_PC @ set register PC to loop start address if result is true + pop + + tlr CORE_REG_A + ts 0 + tlrc CORE_REG_C + + @ print out char + tsr CORE_REG_D @ get char + tlr CORE_REG_A @ load char into register A + ts CORE_MEM_CHR @ set TMP to char print memory address + set @ print char + + @ increment counter by one + tsr CORE_REG_C + tlr CORE_REG_A + ts 1 + tlr CORE_REG_B + ts CORE_ALU_ADD + alu + tlr CORE_REG_C + tlr CORE_REG_A + + ts FORTH_MEM_INPUT_SIZE + tlr CORE_REG_B + ts CORE_ALU_LT + alu + tlr CORE_REG_A + ts loop + tlrc CORE_REG_PC + + ts "\n" + tlr CORE_REG_A + ts CORE_MEM_CHR + set + + tsr CORE_REG_C + tls + dbg + pop + + ts 0 + tlr CORE_REG_C + ts loop + tlr CORE_REG_PC + +@ .std_stop + +@ set PC to maximum for u16 and therefore stops program execution +ts 0xffff @ load 0xffff into TMP +tlr CORE_REG_PC @ store value of TMP into register PC + +jump_switch: + dbg + +.org jump_main + ts main + tlr CORE_REG_PC diff --git a/examples/forth.bin b/examples/forth.bin new file mode 100644 index 0000000000000000000000000000000000000000..9f6cc04cac937bb4380c8a346780fc445108a158 GIT binary patch literal 32768 zcmeIuOA5k35Cy=FJ(-BQ@Wz6HD1Ppkg9tH9@D46&HqE17&!Y13nR4E}YV2$D8HXA* z literal 0 HcmV?d00001 diff --git a/examples/test.asm b/examples/test.asm deleted file mode 100644 index 8f9baf9..0000000 --- a/examples/test.asm +++ /dev/null @@ -1,22 +0,0 @@ -main: - push 40 - push (42 - 40) - - tss - tlr 0x5 - pop - tss - tlr 0x6 - pop - - alu 0x06 - tls - - alu 0x12 - tlr 0x5 - tls - - dbg - -ts 0xffff -tlr 0x0 diff --git a/examples/test.lisp b/examples/test.lisp deleted file mode 100644 index aaf6fa6..0000000 --- a/examples/test.lisp +++ /dev/null @@ -1,5 +0,0 @@ -;; main function -(defun main () - "Program entrypoint" - (let ((x (+ 40 2)))) - (debug x)) diff --git a/hence/Cargo.toml b/hence/Cargo.toml index 5f58bf7..7d74931 100644 --- a/hence/Cargo.toml +++ b/hence/Cargo.toml @@ -16,7 +16,8 @@ path = "src/bin/main.rs" [dependencies] itertools = "0.10.2" num-parse = "0.1.2" -clap = { version = "3.2.12", features = ["derive"] } +clap = { version = "3.2.16", features = ["derive"] } rhexdump = "0.1.1" radix_fmt = "1" rand = "0.8.5" +console = "0.15.1" diff --git a/hence/src/bin/main.rs b/hence/src/bin/main.rs index 9d6043c..2372c63 100644 --- a/hence/src/bin/main.rs +++ b/hence/src/bin/main.rs @@ -60,11 +60,14 @@ fn main() { let tokens = lexer::lex(assembly).unwrap(); let ast = parser::parse(tokens).unwrap(); - let mut data = assembler::Data::new(match Path::new(&src).parent().unwrap().to_str() { - Some(x) => x.to_string(), - _ => panic!("Could not get directory in which source code resides"), - }); - assembler::assemble(ast, &mut data).unwrap(); + let mut data = assembler::Data::new( + match Path::new(&src).parent().unwrap().to_str() { + Some(x) => x.to_string(), + _ => panic!("Could not get directory in which source code resides"), + }, + ast.body, + ); + assembler::assemble(&mut data).unwrap(); match bin { Some(x) => { diff --git a/hence/src/lib/arg.rs b/hence/src/lib/arg.rs index 1b55077..8d1be74 100644 --- a/hence/src/lib/arg.rs +++ b/hence/src/lib/arg.rs @@ -36,10 +36,12 @@ impl assembler::ByteResolvable for Arg { fn resolve_number(&self, data: &mut assembler::Data) -> Result { match self { Arg::String(x) => { - if x.len() == 1 { - Ok(x.as_bytes()[0] as u16) + let y = x.replace("\\n", "\n"); + + if y.len() == 1 { + Ok(y.as_bytes()[0] as u16) } else { - let bytes = x.as_bytes(); + let bytes = y.as_bytes(); Ok(((bytes[0] as u16) << 8) | bytes[1] as u16) } @@ -47,7 +49,7 @@ impl assembler::ByteResolvable for Arg { Arg::Number(x) => Ok(*x), Arg::Variable(x) => { let mut name = x.clone(); - let mut arg: Option = None; + let mut arg: Option; loop { arg = match data.contants.get(&name) { @@ -57,7 +59,7 @@ impl assembler::ByteResolvable for Arg { match arg { Some(a) => { - arg = Some(a.clone()); + // arg = Some(a.clone()); match a { arg::Arg::Variable(n) => { name = n; @@ -86,11 +88,11 @@ impl assembler::ByteResolvable for Arg { fn resolve_bytes(&self, data: &mut assembler::Data) -> Result, String> { match self { - Arg::String(x) => Ok(x.bytes().collect()), + Arg::String(x) => Ok(x.replace("\\n", "\n").bytes().collect()), Arg::Number(x) => Ok(vec![(*x >> 8) as u8, *x as u8]), Arg::Variable(x) => { let mut name = x.clone(); - let mut arg: Option = None; + let mut arg: Option; loop { dbg!(&name); @@ -102,7 +104,7 @@ impl assembler::ByteResolvable for Arg { match arg { Some(a) => { - arg = Some(a.clone()); + // arg = Some(a.clone()); match a { arg::Arg::Variable(n) => { name = n; @@ -148,22 +150,20 @@ impl assembler::ToCode for BinaryExpressionOperator { } } -pub fn parse_binary_operation( - token: &lexer::Token, -) -> Result { +pub fn parse_binary_operation(token: &lexer::Token) -> Result { match token { lexer::Token::Add => Ok(BinaryExpressionOperator::Add), lexer::Token::Sub => Ok(BinaryExpressionOperator::Sub), lexer::Token::Mul => Ok(BinaryExpressionOperator::Mul), lexer::Token::Div => Ok(BinaryExpressionOperator::Div), lexer::Token::Pow => Ok(BinaryExpressionOperator::Pow), - _ => Err("Invalid binary expression operator"), + _ => Err("Invalid binary expression operator".to_string()), } } -pub fn parse_binary_expression_arg(tokens: &mut Vec<&&lexer::Token>) -> Result { +pub fn parse_binary_expression_arg(tokens: &mut Vec<&&lexer::Token>) -> Result { if tokens.is_empty() { - return Err("Malformed binary expression"); + return Err("Malformed binary expression".to_string()); } let mut args: Vec<&&lexer::Token> = tokens.drain(..3).collect(); @@ -202,6 +202,7 @@ pub fn parse_args(tokens: Vec<&lexer::Token>) -> Result, &str> { } match token { + lexer::Token::Comment(_) => {} lexer::Token::StringLiteral(x) => { args.push(Arg::String(x.clone())); } diff --git a/hence/src/lib/assembler.rs b/hence/src/lib/assembler.rs index c96c1f5..675e816 100644 --- a/hence/src/lib/assembler.rs +++ b/hence/src/lib/assembler.rs @@ -15,163 +15,255 @@ pub trait ByteResolvable { fn resolve_bytes(&self, data: &mut T) -> Result, String>; } +#[derive(Debug)] +pub struct Macro { + pub args: Vec, + pub body: Vec, +} + +#[derive(Debug)] +pub enum State { + Default, + Macro { name: String, depth: usize }, +} + #[derive(Debug)] pub struct Data { pub dir: String, + pub body: Vec, pub program: [u8; 32 * 1024], pub offset: u16, pub contants: HashMap, + pub macros: HashMap, + pub state: State, } impl Data { - pub fn new(dir: String) -> Self { + pub fn new(dir: String, body: Vec) -> Self { Self { dir, + body, program: [0; 32 * 1024], offset: 0, contants: HashMap::new(), + macros: HashMap::new(), + state: State::Default, } } } -pub fn assemble(ast: parser::ast::AST, data: &mut Data) -> Result<(), String> { - for node in ast.body { +pub fn assemble(data: &mut Data) -> Result<(), String> { + for node in data.body.clone() { data.contants .insert("OFFSET".to_string(), arg::Arg::Number(data.offset)); - match node { - parser::ast::Node::Comment(_) => {} - parser::ast::Node::Label(x) => { - if data.contants.contains_key(&x) { - return Err(format!("Label already exists: '{x}'")); - } - - data.contants.insert(x, arg::Arg::Number(data.offset)); - } - parser::ast::Node::Call { name, arg } => { - let arg_num = match arg { - Some(x) => x.resolve_number(data).unwrap(), - _ => 0, - }; - - data.program[data.offset as usize] = match name.as_str() { - "nop" => 0x00, - "push" => 0x01, - "pop" => 0x02, - "ts" => 0x03, - "tsr" => 0x04, - "tss" => 0x05, - "tlr" => 0x06, - "tls" => 0x07, - "ld" => 0x08, - "st" => 0x09, - "dbg" => 0x0a, - "alu" => 0x0b, - "at" => 0x0c, - "get" => 0x0d, - "set" => 0x0e, - _ => return Err(format!("Unknown opcode: '{name}'")), - }; - if arg_num == 0 { - data.program[data.offset as usize] |= 0b10000000; - } - data.offset += 1; - - if arg_num != 0 { - data.program[data.offset as usize] = (arg_num >> 8) as u8; - data.offset += 1; - - data.program[data.offset as usize] = arg_num as u8; - data.offset += 1; - } - } - parser::ast::Node::MacroCall { name, args } => { - match name.as_str() { - "debug" => { - for arg in args { - let bytes = arg.resolve_bytes(data).unwrap(); - - println!("{}", arg.to_code().replace('\n', "\\n")); - println!(" => {}", arg.resolve_number(data).unwrap()); - println!( - " => [{}]", - bytes - .iter() - .map(|n| { - let num = radix(*n, 16).to_string(); - format!( - "0x{}{}", - "00".chars().take(2 - num.len()).collect::(), - num - ) - }) - .join(", ") - ); - println!( - " => \"{}\"", - String::from_utf8(bytes).unwrap().replace('\n', "\\n") - ); + match &data.state { + State::Default => { + match node { + parser::ast::Node::Comment(_) => {} + parser::ast::Node::Label(x) => { + if data.contants.contains_key(&x) { + return Err(format!("Label already exists: '{x}'")); } - println!("=========="); + + data.contants + .insert(x.to_string(), arg::Arg::Number(data.offset)); } - "define" => { - let name = match &args[0] { - arg::Arg::Variable(x) | arg::Arg::String(x) => x, - _ => { - return Err( - "First argument of define macro needs to be a literal-like" - .to_string(), - ) - } + parser::ast::Node::Call { name, arg } => { + let arg_num = match arg { + Some(x) => x.resolve_number(data).unwrap(), + _ => 0, }; - if data.contants.contains_key(name) { - return Err(format!("Constant already exists: '{name}'")); - } - data.contants.insert(name.to_string(), (&args[1]).clone()); - } - "define_override" => { - let name = match &args[0] { - arg::Arg::Variable(x) | arg::Arg::String(x) => x, - _ => { - return Err( - "First argument of define macro needs to be a literal-like" - .to_string(), - ) - } + data.program[data.offset as usize] = match name.as_str() { + "nop" => 0x00, + "push" => 0x01, + "pop" => 0x02, + "ts" => 0x03, + "tsr" => 0x04, + "tss" => 0x05, + "tlr" => 0x06, + "tlrc" => 0x07, + "tls" => 0x08, + "ld" => 0x09, + "dbg" => 0x0a, + "alu" => 0x0b, + "get" => 0x0c, + "set" => 0x0d, + _ => return Err(format!("Unknown opcode: '{name}'")), }; - - data.contants.insert(name.to_string(), (&args[1]).clone()); - } - "org" => { - data.offset = args[0].resolve_number(data).unwrap(); - } - "org_add" => { - data.offset += args[0].resolve_number(data).unwrap(); - } - "org_sub" => { - data.offset -= args[0].resolve_number(data).unwrap(); - } - "bytes" => { - for arg in args { - for n in arg.resolve_bytes(data).unwrap() { - data.program[data.offset as usize] = n; - data.offset += 1; - } + if arg_num == 0 { + data.program[data.offset as usize] |= 0b10000000; } - } - "bw" => { - let string_arg = args[0].resolve_bytes(data).unwrap(); - let string = String::from_utf8(string_arg).unwrap().replace("\\n", "\n"); + data.offset += 1; - for n in string.bytes() { - data.program[data.offset as usize] = n; + if arg_num != 0 { + data.program[data.offset as usize] = (arg_num >> 8) as u8; + data.offset += 1; + + data.program[data.offset as usize] = arg_num as u8; data.offset += 1; } } - _ => return Err(format!("Unknown macro: '{name}'")), - }; + parser::ast::Node::MacroCall { name, args } => { + match name.as_str() { + "debug" => { + for arg in args { + let bytes = arg.resolve_bytes(data).unwrap(); + + println!("{}", arg.to_code().replace('\n', "\\n")); + println!(" => {}", arg.resolve_number(data).unwrap()); + println!( + " => [{}]", + bytes + .iter() + .map(|n| { + let num = radix(*n, 16).to_string(); + format!( + "0x{}{}", + "00".chars() + .take(2 - num.len()) + .collect::(), + num + ) + }) + .join(", ") + ); + println!( + " => \"{}\"", + String::from_utf8(bytes).unwrap().replace('\n', "\\n") + ); + } + println!("=========="); + } + // "define" => { + // let name = match &args[0] { + // arg::Arg::Variable(x) | arg::Arg::String(x) => x, + // _ => return Err( + // "First argument of define macro needs to be a literal-like" + // .to_string(), + // ), + // }; + + // if data.contants.contains_key(name) { + // return Err(format!("Constant already exists: '{name}'")); + // } + // data.contants.insert(name.to_string(), (&args[1]).clone()); + // } + "define" => { + let name = match &args[0] { + arg::Arg::Variable(x) | arg::Arg::String(x) => x, + _ => return Err( + "First argument of define macro needs to be a literal-like" + .to_string(), + ), + }; + + data.contants.insert(name.to_string(), (&args[1]).clone()); + } + "macro" => { + let name = match &args[0] { + arg::Arg::Variable(x) | arg::Arg::String(x) => x, + _ => return Err( + "First argument of define macro needs to be a literal-like" + .to_string(), + ), + }; + let args = match (&args[1..]) + .into_iter() + .map(|a| match a { + arg::Arg::Variable(x) => Ok(x.clone()), + __ => { + return Err( + "Macro arguments need to be variables".to_string() + ) + } + }) + .collect::, _>>() + { + Ok(x) => x, + Err(x) => return Err(x), + }; + + data.macros.insert( + name.clone(), + Macro { + args, + body: Vec::new(), + }, + ); + data.state = State::Macro { + name: name.clone(), + depth: 1, + }; + } + "macroend" => return Err("Unexpected macro end".to_string()), + "org" => { + data.offset = args[0].resolve_number(data).unwrap(); + } + "org_add" => { + data.offset += args[0].resolve_number(data).unwrap(); + } + "org_sub" => { + data.offset -= args[0].resolve_number(data).unwrap(); + } + "bytes" => { + for arg in args { + for n in arg.resolve_bytes(data).unwrap() { + data.program[data.offset as usize] = n; + data.offset += 1; + } + } + } + "bw" => { + let string_arg = args[0].resolve_bytes(data).unwrap(); + let string = + String::from_utf8(string_arg).unwrap().replace("\\n", "\n"); + + for n in string.bytes() { + data.program[data.offset as usize] = n; + data.offset += 1; + } + } + _ => match data.macros.get(&name) { + Some(m) => { + dbg!(name, m); + } + None => return Err(format!("Unknown macro: '{name}'")), + }, + }; + } + } } + State::Macro { name, depth } => match &node { + parser::ast::Node::MacroCall { + name: node_name, + args: _, + } => match node_name.as_str() { + "macro" => { + data.state = State::Macro { + name: name.clone(), + depth: depth + 1, + }; + } + "macroend" => { + if *depth - 1 == 0 { + data.state = State::Default; + } else { + data.state = State::Macro { + name: name.clone(), + depth: depth - 1, + }; + } + } + _ => { + data.macros.get_mut(name).unwrap().body.push(node.clone()); + } + }, + _ => { + data.macros.get_mut(name).unwrap().body.push(node.clone()); + } + }, } if data.offset > (32 * 1024) { diff --git a/hence/src/lib/emulator.rs b/hence/src/lib/emulator.rs index 5c5769e..c1136f3 100644 --- a/hence/src/lib/emulator.rs +++ b/hence/src/lib/emulator.rs @@ -1,8 +1,7 @@ use itertools::Itertools; use radix_fmt::radix; use std::cmp::Ordering; -use std::io; -use std::io::Read; +use std::io::{self, Write}; #[derive(Debug)] pub struct Data { @@ -22,6 +21,8 @@ pub struct Data { stack: [u16; 8 * 1024], memory: [u16; 16 * 1024], + + term: console::Term, } impl Data { @@ -43,6 +44,8 @@ impl Data { stack: [0; 8 * 1024], memory: [0; 16 * 1024], + + term: console::Term::stdout(), }; } @@ -56,34 +59,24 @@ impl Data { self.stack[(address - (32 * 1024)) as usize] } else if address < (56 * 1024) { self.memory[(address - (40 * 1024)) as usize] + } else if address == (56 * 1024 + 2) { + self.term.read_char().unwrap() as u16 } else { 0 } } - pub fn set_memory(&mut self, address: u16, value: u16) -> u16 { + pub fn set_memory(&mut self, address: u16, value: u16) { if address >= (32 * 1024) && address < (40 * 1024) { self.stack[(address - (32 * 1024)) as usize] = value; - - value } else if address < (40 * 1024) { self.memory[(address - (40 * 1024)) as usize] = value; - - value - } else if address == (56 * 1024) { - print!("{}", value); - - value & 0xff } else if address == (56 * 1024) { print!("{value}"); - - 0 + io::stdout().flush().unwrap(); } else if address == (56 * 1024 + 1) { print!("{}", char::from(value as u8)); - - 0 - } else { - 0 + io::stdout().flush().unwrap(); } } @@ -203,9 +196,9 @@ impl Data { } 0x0a => { self.tmp = match self.reg_a.cmp(&self.reg_b) { + Ordering::Equal => 0, Ordering::Less => 1, Ordering::Greater => 2, - Ordering::Equal => 0, } } 0x0b => { @@ -248,8 +241,6 @@ pub fn bool_to_num(x: bool) -> u16 { } pub fn emulate(data: &mut Data) -> Result<(), String> { - let mut stdin = io::stdin().lock(); - 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); @@ -288,36 +279,35 @@ pub fn emulate(data: &mut Data) -> Result<(), String> { data.set_register(data.reg_arg as u8, data.tmp); } 0x07 => { + if data.reg_a & 1 == 1 { + data.set_register(data.reg_arg as u8, data.tmp); + } + } + 0x08 => { data.stack[data.reg_sp as usize] = data.tmp; data.reg_sp = data.reg_sp.wrapping_add(1); } - 0x08 => { + 0x09 => { data.reg_sp = data.reg_sp.wrapping_sub(1); data.set_register(data.reg_arg as u8, data.stack[data.reg_sp as usize]); data.stack[data.reg_sp as usize] = 0; } - 0x09 => { - data.stack[data.reg_sp as usize] = data.get_register(data.reg_arg as u8); - data.reg_sp = data.reg_sp.wrapping_add(1); - } 0x0a => { println!( "[DEBUG]: [{}]", data.stack.iter().take(data.reg_sp as usize).join(", ") ); - println!("Press enter to continue execution...",); - stdin.read_exact(&mut [0; 1]).unwrap(); + print!("Press enter to continue execution...",); + io::stdout().flush().unwrap(); + data.term.read_line().unwrap(); } 0x0b => { - data.alu(data.reg_arg as u8).unwrap(); - } - 0x0c => { data.alu(data.tmp as u8).unwrap(); } - 0x0d => { + 0x0c => { data.tmp = data.get_memory(data.tmp); } - 0x0e => { + 0x0d => { data.set_memory(data.tmp, data.reg_a); } _ => return Err(format!("Invalid opcode: 0x{}", radix(data.reg_opc, 16))), diff --git a/hence/src/lib/lexer.rs b/hence/src/lib/lexer.rs index 494a509..0fb81fc 100644 --- a/hence/src/lib/lexer.rs +++ b/hence/src/lib/lexer.rs @@ -56,17 +56,17 @@ pub fn lex(source: String) -> Result, String> { while let Some(&ch) = chars.peek() { match ch { - ';' => { - chars.next(); - chars.next_if(|c| *c == ';'); + // ';' => { + // chars.next(); + // chars.next_if(|c| *c == ';'); - tokens.push(Token::Comment( - chars.peeking_take_while(|c| *c != '\n').collect::(), - )); - } - '@' => { + // tokens.push(Token::Comment( + // chars.peeking_take_while(|c| *c != '\n').collect::(), + // )); + // } + ';' | '@' => { chars.next(); - chars.next_if(|c| *c == '@'); + chars.next_if(|c| *c == ';' || *c == '@'); tokens.push(Token::Comment( chars.peeking_take_while(|c| *c != '\n').collect::(), @@ -83,13 +83,13 @@ pub fn lex(source: String) -> Result, String> { tokens.push(Token::MacroLiteral(format!( ".{}", chars - .peeking_take_while(|c| c.is_alphabetic() || c.is_numeric()) + .peeking_take_while(|c| c.is_alphabetic() || c.is_numeric() || *c == '_') .collect::() ))); } ch if ch.is_alphabetic() => { let name: String = chars - .peeking_take_while(|c| c.is_alphabetic() || c.is_numeric()) + .peeking_take_while(|c| c.is_alphabetic() || c.is_numeric() || *c == '_') .collect(); tokens.push(Token::Literal(name)); @@ -97,7 +97,7 @@ pub fn lex(source: String) -> Result, String> { ch if ch.is_numeric() => { tokens.push(Token::Number( chars - .peeking_take_while(|c| c.is_alphanumeric()) + .peeking_take_while(|c| c.is_alphanumeric() || *c == '_') .collect::(), )); } diff --git a/hence/src/lib/parser.rs b/hence/src/lib/parser.rs index 06f02f5..86b2ee7 100644 --- a/hence/src/lib/parser.rs +++ b/hence/src/lib/parser.rs @@ -5,7 +5,7 @@ use crate::lexer; pub mod ast; -pub fn parse(tokens: Vec) -> Result { +pub fn parse(tokens: Vec) -> Result { let mut iter = tokens.iter().peekable(); let mut body: Vec = Vec::new(); @@ -37,15 +37,20 @@ pub fn parse(tokens: Vec) -> Result { { body.push(ast::Node::Label(x.clone())); } else { - let args = arg::parse_args( + let args = match arg::parse_args( iter.by_ref() .take_while(|t| !matches!(t, lexer::Token::Newline(_))) - .filter(|t| !matches!(t, lexer::Token::Whitespace(_))) + .filter(|t| { + !matches!(t, lexer::Token::Whitespace(_) | lexer::Token::Comment(_)) + }) .collect(), - ) - .unwrap(); + ) { + Ok(x) => x, + Err(x) => return Err(x.to_string()), + }; + if args.len() > 1 { - return Err("Opcode call only accepts one argument"); + return Err("Opcode call only accepts one argument".to_string()); } body.push(ast::Node::Call { @@ -60,7 +65,7 @@ pub fn parse(tokens: Vec) -> Result { lexer::Token::Whitespace(_) | lexer::Token::Newline(_) => { iter.next(); } - _ => return Err("Unexpected token"), + _ => return Err("Unexpected token".to_string()), } } diff --git a/hence/src/lib/parser/ast.rs b/hence/src/lib/parser/ast.rs index a92dfbb..414c50f 100644 --- a/hence/src/lib/parser/ast.rs +++ b/hence/src/lib/parser/ast.rs @@ -3,7 +3,7 @@ use itertools::Itertools; use crate::arg; use crate::assembler; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Node { Comment(String), Label(String), @@ -34,7 +34,7 @@ impl assembler::ToCode for Node { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct AST { pub body: Vec, } diff --git a/hencelisp/.gitignore b/hencelisp/.gitignore deleted file mode 100644 index ea8c4bf..0000000 --- a/hencelisp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/hencelisp/Cargo.toml b/hencelisp/Cargo.toml deleted file mode 100644 index abcf316..0000000 --- a/hencelisp/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "hencelisp" -version = "0.1.0" -edition = "2021" - -[lib] -name = "hencelisp" -path = "src/lib/lib.rs" - -[[bin]] -name = "hencelisp" -path = "src/bin/main.rs" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -hence = { path = "../hence" } -clap = { version = "3.2.12", features = ["derive"] } -itertools = "0.10.3" -num-parse = "0.1.2" diff --git a/hencelisp/src/bin/main.rs b/hencelisp/src/bin/main.rs deleted file mode 100644 index e2f6151..0000000 --- a/hencelisp/src/bin/main.rs +++ /dev/null @@ -1,59 +0,0 @@ -use clap::{Parser, Subcommand}; -use hencelisp::*; -use std::fs; - -#[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 source code to hence assembly")] - Compile { - #[clap(value_parser)] - src: String, - #[clap(value_parser)] - out: Option, - #[clap(long, action)] - dump: bool, - }, -} - -fn main() { - let args = Cli::parse(); - match args.commands { - Commands::Lex { src } => { - let source = fs::read_to_string(src).unwrap(); - let tokens = lexer::lex(source).unwrap(); - dbg!(tokens); - } - Commands::Parse { src } => { - let source = fs::read_to_string(src).unwrap(); - let tokens = lexer::lex(source).unwrap(); - let ast = parser::parse(tokens).unwrap(); - dbg!(ast); - } - Commands::Compile { src, out, dump } => { - let source = fs::read_to_string(src).unwrap(); - let tokens = lexer::lex(source).unwrap(); - let ast = parser::parse(tokens).unwrap(); - - let mut data = compiler::Data::new(); - compiler::compile(ast, &mut data).unwrap(); - dbg!(data); - } - } -} diff --git a/hencelisp/src/lib/compiler.rs b/hencelisp/src/lib/compiler.rs deleted file mode 100644 index 735d1f9..0000000 --- a/hencelisp/src/lib/compiler.rs +++ /dev/null @@ -1,61 +0,0 @@ -use hence; - -use crate::parser; - -#[derive(Debug)] - -pub struct Data { - pub body: Vec, -} - -impl Data { - pub fn new() -> Self { - Self { - body: Vec::new(), - } - } -} - -// pub fn create_call(list: Vec) -> Result { -// let mut raw_content = list.iter(); -// let content = raw_content.by_ref(); - -// let name = match content -// .skip_while(|n| matches!(n, parser::ast::Node::Comment(_))) -// .next() -// { -// Some(node) => match node { -// parser::ast::Node::String(x) -// | parser::ast::Node::Symbol(x) -// | parser::ast::Node::Literal(x) => x.clone(), -// _ => return Err("Invalid call name node literal"), -// }, -// _ => return Err("Cannot create call from empty list"), -// }; -// let args: Vec<_> = content.collect(); -// dbg!(args); - -// Ok(Call { -// name, -// args: Vec::new(), -// }) -// } - -pub fn compile(ast: parser::ast::AST, data: &mut Data) -> Result<(), &str> { - let mut iter = ast.body.into_iter().peekable(); - while let Some(node) = iter.next() { - match node { - parser::ast::Node::Comment(_) => {} - parser::ast::Node::String(_) => return Err("Unexpected string on top level"), - parser::ast::Node::Number(_) => return Err("Unexpected number on top level"), - parser::ast::Node::Symbol(_) => return Err("Unexpected symbol on top level"), - parser::ast::Node::Literal(_) => return Err("Unexpected literal on top level"), - - parser::ast::Node::List(x) => { - dbg!(&x); - } - } - } - - Ok(()) -} diff --git a/hencelisp/src/lib/lexer.rs b/hencelisp/src/lib/lexer.rs deleted file mode 100644 index f6a1ab7..0000000 --- a/hencelisp/src/lib/lexer.rs +++ /dev/null @@ -1,99 +0,0 @@ -use hence; -use itertools::Itertools; - -#[derive(Debug)] -pub enum Token { - Comment(String), - - Newline(String), - Whitespace(String), - - LParen, - RParen, - - String(String), - Number(String), - Symbol(String), - Literal(String), -} - -impl hence::assembler::ToCode for Token { - fn to_code(&self) -> String { - match self { - Token::Comment(x) => format!(";;{x}"), - Token::Newline(x) | Token::Whitespace(x) => x.clone(), - Token::LParen => "(".to_string(), - Token::RParen => ")".to_string(), - Token::String(x) => format!("\"{x}\""), - Token::Number(x) | Token::Symbol(x) | Token::Literal(x) => x.clone(), - } - } -} - -pub fn lex(source: String) -> Result, String> { - let mut chars = source.chars().peekable(); - let mut tokens: Vec = Vec::new(); - - while let Some(&ch) = chars.peek() { - match ch { - ';' => { - chars.next(); - chars.next_if(|c| *c == ';'); - - tokens.push(Token::Comment( - chars.peeking_take_while(|c| *c != '\n').collect(), - )); - } - '\n' => { - tokens.push(Token::Newline( - chars.peeking_take_while(|c| *c == '\n').collect(), - )); - } - _ if ch.is_whitespace() => { - tokens.push(Token::Whitespace( - chars - .peeking_take_while(|c| c.is_whitespace() && *c != '\n') - .collect(), - )); - } - '(' => { - tokens.push(Token::LParen); - chars.next(); - } - ')' => { - tokens.push(Token::RParen); - chars.next(); - } - '"' => { - chars.next(); - tokens.push(Token::String( - chars.by_ref().take_while(|c| *c != '"').collect(), - )); - } - _ if ch.is_numeric() => { - tokens.push(Token::Number( - chars - .peeking_take_while(|c| c.is_alphanumeric()) - .collect::(), - )); - } - ':' => { - chars.next(); - tokens.push(Token::Symbol( - chars - .peeking_take_while(|c| !c.is_whitespace() && *c != '(' && *c != ')') - .collect(), - )); - } - _ => { - tokens.push(Token::Literal( - chars - .peeking_take_while(|c| !c.is_whitespace() && *c != '(' && *c != ')') - .collect(), - )); - } - } - } - - Ok(tokens) -} diff --git a/hencelisp/src/lib/lib.rs b/hencelisp/src/lib/lib.rs deleted file mode 100644 index ac7a00d..0000000 --- a/hencelisp/src/lib/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod compiler; -pub mod lexer; -pub mod parser; diff --git a/hencelisp/src/lib/parser.rs b/hencelisp/src/lib/parser.rs deleted file mode 100644 index 5401eff..0000000 --- a/hencelisp/src/lib/parser.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::lexer; - -pub mod ast; - -pub fn parse(tokens: Vec) -> Result { - let mut iter = tokens.into_iter().peekable(); - let mut body: Vec = Vec::new(); - - while let Some(token) = iter.peek() { - match token { - lexer::Token::Comment(x) => { - body.push(ast::Node::Comment(x.trim().to_string())); - iter.next(); - } - lexer::Token::Newline(_) | lexer::Token::Whitespace(_) => { - iter.next(); - } - lexer::Token::LParen => { - iter.next(); - let mut depth: usize = 1; - body.push(ast::Node::List( - parse( - iter.by_ref() - .take_while(|t| match t { - lexer::Token::LParen => { - depth += 1; - true - } - lexer::Token::RParen => { - depth -= 1; - depth != 0 - } - _ => true, - }) - .collect(), - ) - .unwrap() - .body, - )); - } - lexer::Token::RParen => return Err("Unexpected right parenthesis"), - lexer::Token::String(x) => { - body.push(ast::Node::String(x.clone())); - iter.next(); - } - lexer::Token::Number(x) => { - body.push(ast::Node::Number(match num_parse::parse_int(x) { - Some(y) => y, - _ => return Err("Error parsing number"), - })); - iter.next(); - } - lexer::Token::Symbol(x) => { - body.push(ast::Node::Symbol(x.clone())); - iter.next(); - } - lexer::Token::Literal(x) => { - body.push(ast::Node::Literal(x.clone())); - iter.next(); - } - } - } - - Ok(ast::AST { body }) -} diff --git a/hencelisp/src/lib/parser/ast.rs b/hencelisp/src/lib/parser/ast.rs deleted file mode 100644 index 1de2d96..0000000 --- a/hencelisp/src/lib/parser/ast.rs +++ /dev/null @@ -1,30 +0,0 @@ -use hence; -use itertools::Itertools; - -#[derive(Debug)] -pub enum Node { - Comment(String), - List(Vec), - String(String), - Number(i32), - Symbol(String), - Literal(String), -} - -impl hence::assembler::ToCode for Node { - fn to_code(&self) -> String { - match self { - Node::Comment(x) => format!(";; {x}"), - Node::List(x) => format!("({})", x.iter().map(|n| n.to_code()).join(" ")), - Node::String(x) => format!("\"{x}\""), - Node::Number(x) => x.to_string(), - Node::Symbol(x) => format!(":{x}"), - Node::Literal(x) => x.clone(), - } - } -} - -#[derive(Debug)] -pub struct AST { - pub body: Vec, -}