From 5cbd032fe4b619b665d9c48ee42d63b836620d24 Mon Sep 17 00:00:00 2001 From: Erin Date: Sun, 22 Oct 2023 15:08:45 +0200 Subject: [PATCH] Added assembler - its a bit cursed, prolly broken, but at least something --- Cargo.lock | 312 +++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- hbasm/Cargo.toml | 12 ++ hbasm/assets/exit.lua | 3 + hbasm/assets/label.lua | 8 ++ hbasm/src/arraylist.rs | 53 +++++++ hbasm/src/ins.rs | 194 +++++++++++++++++++++++++ hbasm/src/label.rs | 91 ++++++++++++ hbasm/src/linker.rs | 44 ++++++ hbasm/src/main.rs | 54 +++++++ hbbytecode/src/lib.rs | 1 + hbvm/src/vmrun.rs | 29 ++-- 12 files changed, 792 insertions(+), 11 deletions(-) create mode 100644 hbasm/Cargo.toml create mode 100644 hbasm/assets/exit.lua create mode 100644 hbasm/assets/label.lua create mode 100644 hbasm/src/arraylist.rs create mode 100644 hbasm/src/ins.rs create mode 100644 hbasm/src/label.rs create mode 100644 hbasm/src/linker.rs create mode 100644 hbasm/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 4faacd1..ab773b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "argh" version = "0.1.12" @@ -48,6 +57,12 @@ dependencies = [ "serde", ] +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "backtrace" version = "0.3.69" @@ -69,6 +84,16 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[package]] +name = "bstr" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "cc" version = "1.0.83" @@ -111,6 +136,22 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "errno" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "eyre" version = "0.6.8" @@ -127,6 +168,15 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +[[package]] +name = "hbasm" +version = "0.1.0" +dependencies = [ + "mlua", + "paste", + "with_builtin_macros", +] + [[package]] name = "hbbytecode" version = "0.1.0" @@ -149,12 +199,30 @@ dependencies = [ "nix", ] +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys", +] + [[package]] name = "indenter" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -167,6 +235,31 @@ version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +[[package]] +name = "linux-raw-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" + +[[package]] +name = "lua-src" +version = "546.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c26d4af78361e025a3d03a2b964cd1592aff7495f4d4f7947218c084c6fdca8" +dependencies = [ + "cc", +] + +[[package]] +name = "luajit-src" +version = "210.4.8+resty107baaf" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05167e8b2a2185758d83ed23541e5bd8bce37072e4204e0ef2c9b322bc87c4e" +dependencies = [ + "cc", + "which", +] + [[package]] name = "memchr" version = "2.6.4" @@ -182,6 +275,48 @@ dependencies = [ "adler", ] +[[package]] +name = "mlua" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c3a7a7ff4481ec91b951a733390211a8ace1caba57266ccb5f4d4966704e560" +dependencies = [ + "bstr", + "mlua-sys", + "mlua_derive", + "num-traits", + "once_cell", + "rustc-hash", +] + +[[package]] +name = "mlua-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ec8b54eddb76093069cce9eeffb4c7b3a1a0fe66962d7bd44c4867928149ca3" +dependencies = [ + "cc", + "cfg-if", + "lua-src", + "luajit-src", + "pkg-config", +] + +[[package]] +name = "mlua_derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f359220f24e6452dd82a3f50d7242d4aab822b5594798048e953d7a9e0314c6" +dependencies = [ + "itertools", + "once_cell", + "proc-macro-error", + "proc-macro2", + "quote", + "regex", + "syn 2.0.38", +] + [[package]] name = "nix" version = "0.27.1" @@ -193,6 +328,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.32.1" @@ -214,12 +358,48 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.69" @@ -238,12 +418,60 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "serde" version = "1.0.189" @@ -358,6 +586,90 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "with_builtin_macros" version = "0.0.3" diff --git a/Cargo.toml b/Cargo.toml index 8d81c80..91a11ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] resolver = "2" -members = ["hbbytecode", "hbvm", "hbxrt", "xtask"] +members = ["hbasm", "hbbytecode", "hbvm", "hbxrt", "xtask"] diff --git a/hbasm/Cargo.toml b/hbasm/Cargo.toml new file mode 100644 index 0000000..3fafd65 --- /dev/null +++ b/hbasm/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "hbasm" +version = "0.1.0" +edition = "2021" + +[dependencies] +paste = "1.0" +with_builtin_macros = "0.0.3" + +[dependencies.mlua] +version = "0.9" +features = ["luajit52", "macros", "vendored"] diff --git a/hbasm/assets/exit.lua b/hbasm/assets/exit.lua new file mode 100644 index 0000000..e563181 --- /dev/null +++ b/hbasm/assets/exit.lua @@ -0,0 +1,3 @@ +li8(r1, 60) +li8(r2, 69) +eca() \ No newline at end of file diff --git a/hbasm/assets/label.lua b/hbasm/assets/label.lua new file mode 100644 index 0000000..a88879f --- /dev/null +++ b/hbasm/assets/label.lua @@ -0,0 +1,8 @@ +label "label" -- set named label +local a = label {} -- unassigned label + +jmp16("label") +a:here() -- assign label +jmp16(a) + +addi8(r3, r4, 5) \ No newline at end of file diff --git a/hbasm/src/arraylist.rs b/hbasm/src/arraylist.rs new file mode 100644 index 0000000..439984c --- /dev/null +++ b/hbasm/src/arraylist.rs @@ -0,0 +1,53 @@ +use mlua::prelude::*; + +#[derive(Clone, Debug, Default, FromLua)] +pub struct ArrayList(pub Vec); +impl LuaUserData for ArrayList +where + T: for<'lua> FromLua<'lua> + for<'lua> IntoLua<'lua> + Clone + std::fmt::Debug, +{ + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_meta_method( + LuaMetaMethod::Index, + |lua, this, index: LuaInteger| match this.0.get( + (index as usize) + .checked_sub(1) + .ok_or_else(|| mlua::Error::runtime("Invalid index: 0"))?, + ) { + Some(i) => i.clone().into_lua(lua), + None => Ok(LuaValue::Nil), + }, + ); + + methods.add_meta_method_mut( + LuaMetaMethod::NewIndex, + |_, this, (index, val): (LuaInteger, T)| match this.0.get_mut( + (index as usize) + .checked_sub(1) + .ok_or_else(|| mlua::Error::runtime("Invalid index: 0"))?, + ) { + Some(x) => { + *x = val; + Ok(()) + } + None => Err(mlua::Error::runtime(format!( + "Index out of bounds: length = {}, index = {index}", + this.0.len() + ))), + }, + ); + + methods.add_meta_method(LuaMetaMethod::Len, |_, this, ()| Ok(this.0.len())); + methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| { + Ok(format!("{this:?}")) + }); + methods.add_method_mut("push", |_, this, val: T| { + this.0.push(val); + Ok(()) + }); + methods.add_method_mut("pop", |lua, this, ()| match this.0.pop() { + Some(val) => val.into_lua(lua), + None => Ok(LuaValue::Nil), + }); + } +} diff --git a/hbasm/src/ins.rs b/hbasm/src/ins.rs new file mode 100644 index 0000000..f20bd18 --- /dev/null +++ b/hbasm/src/ins.rs @@ -0,0 +1,194 @@ +use { + crate::arraylist::ArrayList, + mlua::{prelude::*, Result, Scope}, +}; + +mod opsty { + pub type R = i8; + pub type B = i8; + pub type H = i16; + pub type W = i32; + pub type D = i64; + pub type A = i64; + pub type O<'lua> = super::LuaValue<'lua>; + pub type P<'lua> = super::LuaValue<'lua>; +} + +macro_rules! gen_insert { + ($($plain:ident),* $(,)?) => { + macro_rules! insert { + $( + ($label:expr, $lua:expr, $symrt:expr, $code:expr, $plain) => { + $code.0.extend($label.to_le_bytes()); + }; + )* + + ($label:expr, $lua:expr, $symrt:expr, $code:expr, O) => { + insert_label_ref::<4>( + $label, + $lua, + &$symrt, + &mut $code.0, + )?; + }; + + ($label:expr, $lua:expr, $symrt:expr, $code:expr, P) => { + insert_label_ref::<2>( + $label, + $lua, + &$symrt, + &mut $code.0, + )?; + }; + } + }; +} + +gen_insert!(R, B, H, W, D, A); + +macro_rules! generic_ins { + { + ($lua:expr, $scope:expr); + $($name:ident($($param_i:ident : $param_ty:ident),*);)* + } => {{ + let lua = $lua; + let scope = $scope; + + let code = $lua.globals() + .get::<_, LuaTable>("_CODE")? + .get::<_, LuaAnyUserData>("text")?; + + let symrt = $lua.globals() + .get::<_, LuaTable>("_SYM_REPLS")?; + + lua.globals().set( + "_GENERIC", + lua.create_table_from([$(( + stringify!($name), + #[allow(unused)] + { + let code = code.clone(); + let symrt = symrt.clone(); + scope.create_function_mut(move |lua, (opcode, $($param_i),*): (u8, $(opsty::$param_ty),*)| { + let mut code = code.borrow_mut::>()?; + code.0.push(opcode); + $(insert!($param_i, lua, symrt, code, $param_ty);)* + Ok(()) + })? + } + )),*])? + )?; + }}; +} + +macro_rules! ins { + { + $lua:expr; + {$($opcode:expr, $mnemonic:ident, $ty:ident, $_doc:literal;)*} + } => {{ + use std::fmt::Write; + + let lua = $lua; + let mut code = String::new(); + + $({ + paste::paste! { + let name = match stringify!([<$mnemonic:lower>]) { + "and" => "and_", + "or" => "or_", + "not" => "not_", + name => name, + }; + } + + writeln!( + code, + "function {name}(...) _GENERIC.{ty}({opcode}, ...) end", + ty = stringify!($ty).to_lowercase(), + opcode = $opcode, + ).unwrap(); + + })* + + lua.load(code).exec()?; + }}; +} + +pub fn setup<'lua, 'scope>(lua: &'lua Lua, scope: &Scope<'lua, 'scope>) -> Result<()> +where + 'lua: 'scope, +{ + generic_ins! { + (lua, scope); + rr (o0: R, o1: R); + rrr (o0: R, o1: R, o2: R); + rrrr(o0: R, o1: R, o2: R, o3: R); + rrb (o0: R, o1: R, o2: B); + rrh (o0: R, o1: R, o2: H); + rrw (o0: R, o1: R, o2: W); + rrd (o0: R, o1: R, o2: D); + rb (o0: R, o1: B); + rh (o0: R, o1: H); + rw (o0: R, o1: W); + rd (o0: R, o1: D); + rrah(o0: R, o1: R, o2: A, o3: H); + rroh(o0: R, o1: R, o2: O, o3: H); + rrph(o0: R, o1: R, o2: P, o3: H); + rro (o0: R, o1: R, o2: O); + rrp (o0: R, o1: R, r2: P); + o (o0: O); + p (o0: P); + n (); + } + + with_builtin_macros::with_builtin! { + let $spec = include_from_root!("../hbbytecode/instructions.in") in { + ins!(lua; { $spec }); + } + } + + Ok(()) +} + +fn insert_label_ref( + label: LuaValue, + lua: &Lua, + symrt: &LuaTable, + code: &mut Vec, +) -> Result<()> { + match label { + LuaValue::Integer(offset) => { + if match SIZE { + 2 => i16::try_from(offset).map(|o| code.extend(o.to_le_bytes())), + 4 => i32::try_from(offset).map(|o| code.extend(o.to_le_bytes())), + s => { + return Err(mlua::Error::runtime(format!( + "Invalid offset size (expected 2 or 4 bytes, got {s})" + ))); + } + } + .is_err() + { + return Err(mlua::Error::runtime("Failed to cast offset")); + } + return Ok(()); + } + LuaValue::UserData(ud) => { + symrt.set( + code.len() + 1, + lua.create_table_from([("label", ud.get("id")?), ("size", SIZE)])?, + )?; + code.extend([0; SIZE]); + } + LuaValue::String(_) => { + symrt.set( + code.len() + 1, + lua.create_table_from([("label", label), ("size", SIZE.into_lua(lua)?)])?, + )?; + code.extend([0; SIZE]); + } + _ => return Err(mlua::Error::runtime("Invalid label type")), + } + + Ok(()) +} diff --git a/hbasm/src/label.rs b/hbasm/src/label.rs new file mode 100644 index 0000000..da0c1cd --- /dev/null +++ b/hbasm/src/label.rs @@ -0,0 +1,91 @@ +use { + crate::{arraylist::ArrayList, Labels}, + mlua::{Result, prelude::*}, +}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, FromLua)] +pub struct UnassignedLabel(pub usize); +impl LuaUserData for UnassignedLabel { + fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_get("id", |_, this| Ok(this.0)); + } + + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_method("here", |lua, this, ()| { + match lua + .globals() + .get::<_, LuaUserDataRefMut>("_LABELS")? + .0 + .get_mut( + this.0 + .checked_sub(1) + .ok_or_else(|| mlua::Error::runtime("Invalid label"))?, + ) { + Some(entry) => { + *entry = Some( + lua.globals() + .get::<_, LuaTable>("_CODE")? + .get::<_, LuaUserDataRef>>("text")? + .0 + .len(), + ); + + Ok(()) + } + None => Err(mlua::Error::runtime("Invalid label")), + } + }); + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, FromLua)] +pub struct Label(pub usize); +impl LuaUserData for Label { + fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_get("id", |_, this| Ok(this.0)); + } +} + +pub fn label<'lua>(lua: &'lua Lua, val: LuaValue<'lua>) -> Result> { + let globals = lua.globals(); + let mut labels = globals.get::<_, LuaUserDataRefMut>("_LABELS")?; + + let code_ix = globals + .get::<_, LuaTable>("_CODE")? + .get::<_, LuaUserDataRefMut>>("text")? + .0 + .len(); + + match val { + LuaValue::Table(_) => { + labels.0.push(None); + Ok(LuaValue::UserData( + lua.create_userdata(UnassignedLabel(labels.0.len()))?, + )) + } + LuaValue::String(str) => { + labels.0.push(Some(code_ix + 1)); + globals + .get::<_, LuaTable>("_SYM_TABLE")? + .set(str, labels.0.len())?; + + Ok(LuaValue::UserData( + lua.create_userdata(Label(labels.0.len()))?, + )) + } + LuaNil => { + labels.0.push(Some(code_ix + 1)); + Ok(LuaValue::UserData( + lua.create_userdata(Label(labels.0.len()))?, + )) + } + _ => Err(mlua::Error::BadArgument { + to: Some("label".into()), + pos: 1, + name: None, + cause: std::sync::Arc::new(mlua::Error::runtime( + "Unsupported type (nil and string are only supported)", + )), + }), + } +} diff --git a/hbasm/src/linker.rs b/hbasm/src/linker.rs new file mode 100644 index 0000000..566c2df --- /dev/null +++ b/hbasm/src/linker.rs @@ -0,0 +1,44 @@ +use mlua::prelude::*; + +pub fn link( + symrt: LuaTable, + symtab: LuaTable, + labels: &[Option], + code: &mut [u8], + out: &mut impl std::io::Write, +) -> mlua::Result<()> { + for item in symrt.pairs::() { + let (loc, val) = item?; + let size: usize = val.get("size")?; + let dest = labels + .get( + match val.get::<_, LuaValue>("label")? { + LuaValue::Integer(i) => i, + LuaValue::String(s) => symtab.get(s)?, + _ => { + return Err(mlua::Error::runtime( + "Invalid symbol type (int or string expected)", + )) + } + } as usize + - 1, + ) + .copied() + .flatten() + .ok_or_else(|| mlua::Error::runtime("Invalid label"))?; + + let loc = loc - 1; + let dest = dest - 1; + + let offset = dest.wrapping_sub(loc); + match size { + 4 => code[loc..loc + size].copy_from_slice(&(offset as u32).to_le_bytes()), + 2 => code[loc..loc + size].copy_from_slice(&(offset as u16).to_le_bytes()), + _ => return Err(mlua::Error::runtime("Invalid symbol")), + } + } + + dbg!(&code); + out.write_all(code)?; + Ok(()) +} diff --git a/hbasm/src/main.rs b/hbasm/src/main.rs new file mode 100644 index 0000000..a9a0f9a --- /dev/null +++ b/hbasm/src/main.rs @@ -0,0 +1,54 @@ +mod arraylist; +mod ins; +mod label; +mod linker; + +use {arraylist::ArrayList, mlua::{Result, prelude::*}, std::io::Read}; + +pub type Labels = ArrayList>; + +fn main() -> Result<()> { + let mut code = vec![]; + std::io::stdin().read_to_end(&mut code)?; + + let lua = Lua::new(); + lua.scope(|scope| { + // Global values + let globals = lua.globals(); + globals.set( + "_CODE", + lua.create_table_from([ + ("text", ArrayList::::default()), + ("data", ArrayList::::default()), + ])?, + )?; + + globals.set("_LABELS", Labels::default())?; + globals.set("_SYM_TABLE", lua.create_table()?)?; + globals.set("_SYM_REPLS", lua.create_table()?)?; + + // Functions + globals.set("label", lua.create_function(label::label)?)?; + ins::setup(&lua, scope)?; + + // Register symbols + for n in 0..255 { + globals.set(format!("r{n}"), n)?; + } + + lua.load(code).exec()?; + + linker::link( + globals.get("_SYM_REPLS")?, + globals.get("_SYM_TABLE")?, + &globals.get::<_, Labels>("_LABELS")?.0, + &mut globals + .get::<_, LuaTable>("_CODE")? + .get::<_, LuaUserDataRefMut>>("text")? + .0, + &mut std::io::stdout(), + ) + })?; + + Ok(()) +} diff --git a/hbbytecode/src/lib.rs b/hbbytecode/src/lib.rs index 40be3c8..4fe81ba 100644 --- a/hbbytecode/src/lib.rs +++ b/hbbytecode/src/lib.rs @@ -39,6 +39,7 @@ define_items! { OpsRW (OpR, OpW ), OpsRD (OpR, OpD ), OpsRRD (OpR, OpR, OpD ), + OpsRRA (OpR, OpR, OpA ), OpsRRAH (OpR, OpR, OpA, OpH), OpsRROH (OpR, OpR, OpO, OpH), OpsRRPH (OpR, OpR, OpP, OpH), diff --git a/hbvm/src/vmrun.rs b/hbvm/src/vmrun.rs index 8abc3b9..047e17c 100644 --- a/hbvm/src/vmrun.rs +++ b/hbvm/src/vmrun.rs @@ -2,7 +2,7 @@ //! //! Have fun -use hbbytecode::RoundingMode; +use hbbytecode::{OpsRRA, RoundingMode}; use crate::mem::addr::AddressOp; @@ -271,23 +271,29 @@ where usize::from(count), ); }), - JMP => handler!(self, |OpsO(off)| self.pc = self.pc.wrapping_add(off)), - JAL => handler!(self, |OpsRRO(save, reg, offset)| { + JMP => { + let OpsO(off) = self.decode(); + self.pc = self.pc.wrapping_add(off); + } + JAL => { // Jump and link. Save PC after this instruction to // specified register and jump to reg + relative offset. + let OpsRRO(save, reg, offset) = self.decode(); + self.write_reg(save, self.pc.get()); self.pc = self .pcrel(offset, 3) .wrapping_add(self.read_reg(reg).cast::()); - }), - JALA => handler!(self, |OpsRRW(save, reg, offset)| { + } + JALA => { // Jump and link. Save PC after this instruction to // specified register and jump to reg + let OpsRRA(save, reg, offset) = self.decode(); + self.write_reg(save, self.pc.get()); - self.pc = Address::new( - self.read_reg(reg).cast::().wrapping_add(offset.into()), - ); - }), + self.pc = + Address::new(self.read_reg(reg).cast::().wrapping_add(offset)); + } // Conditional jumps, jump only to immediates JEQ => self.cond_jmp::(Ordering::Equal), JNE => handler!(self, |OpsRRP(a0, a1, ja)| { @@ -385,7 +391,10 @@ where STR16 => handler!(self, |OpsRRPH(dst, base, off, count)| { self.store(dst, base, self.pcrel(off, 3).get(), count)?; }), - JMP16 => handler!(self, |OpsP(off)| self.pc = self.pcrel(off, 1)), + JMP16 => { + let OpsP(off) = self.decode(); + self.pc = self.pcrel(off, 1); + } op => return Err(VmRunError::InvalidOpcode(op)), } }