summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock51
-rw-r--r--Cargo.toml9
-rw-r--r--opcode_proc/Cargo.toml12
-rw-r--r--opcode_proc/src/lib.rs212
-rw-r--r--src/interpreter.rs24
-rw-r--r--src/main.rs4
-rw-r--r--src/opcodes.rs28
7 files changed, 324 insertions, 16 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 11e08a5..71dd97a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,7 +1,54 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-version = 3
+version = 4
 
 [[package]]
-name = "optimistic-glulxe"
+name = "opcode_proc"
 version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tlulx"
+version = "0.1.0"
+dependencies = [
+ "opcode_proc",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
diff --git a/Cargo.toml b/Cargo.toml
index e8af3ea..c1f28d8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,13 @@
+[workspace]
+members = [
+  "./",
+  "opcode_proc"
+]
+
 [package]
-name = "optimistic-glulxe"
+name = "tlulx"
 version = "0.1.0"
 edition = "2021"
 
 [dependencies]
+opcode_proc = { path = "./opcode_proc" }
diff --git a/opcode_proc/Cargo.toml b/opcode_proc/Cargo.toml
new file mode 100644
index 0000000..9c17800
--- /dev/null
+++ b/opcode_proc/Cargo.toml
@@ -0,0 +1,12 @@
+[lib]
+proc-macro = true
+
+[package]
+name = "opcode_proc"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+syn = {version = "2.0.90", features = ["full"] }
+proc-macro2= "1.0.92"
+quote = "1.0"
diff --git a/opcode_proc/src/lib.rs b/opcode_proc/src/lib.rs
new file mode 100644
index 0000000..d777214
--- /dev/null
+++ b/opcode_proc/src/lib.rs
@@ -0,0 +1,212 @@
+extern crate proc_macro;
+extern crate syn;
+extern crate quote;
+extern crate proc_macro2;
+
+use proc_macro::TokenStream;
+use quote::quote;
+use proc_macro2::Span;
+use syn::{braced, bracketed, parenthesized, parse::{Parse, ParseStream}, parse_macro_input, punctuated::Punctuated, token::{self, Paren}, Block, Error, PatType, Ident, LitInt, Result, Token, Type, TypeTuple};
+
+#[proc_macro]
+pub fn generate_opcodes(item: TokenStream) -> TokenStream {
+    let ops_def = parse_macro_input!(item as OpcodeBlock);
+
+    let enum_name = ops_def.enum_name;
+    let field_names = ops_def.codes.iter().map(|field| &field.name).collect::<Vec<_>>();
+
+    let getcode_match_arms = ops_def.codes.iter().map(|field| {
+        let code = &field.byte_code;
+        let variant = &field.name;
+        quote! {
+            #code => Self::#variant,
+        }
+    }).collect::<Vec<_>>();
+
+    let getinput_match_arms = ops_def.codes.iter().map(|field| {
+        let variant = &field.name;
+        let num = &field.input_params.len();
+        quote! {
+            #enum_name::#variant => #num,
+        }
+    });
+
+    let getoutput_match_arms = ops_def.codes.iter().map(|field| {
+        let variant = &field.name;
+        let num = &field.output_params.len();
+        quote! {
+            #enum_name::#variant => #num,
+        }
+    });
+
+    let closure_match_arms = ops_def.codes.iter().map(|field| {
+        let variant = &field.name;
+        let closure_expr = &field.func_block;
+        let interpreter_arg = &field.interpreter_ident.clone().unwrap_or(Ident::new("_terp", Span::call_site()));
+        let input_args = &field.input_params;
+        let output_args = if field.output_params.len() == 1 {
+            let arg = field.output_params[0].clone();
+            quote! { #arg }
+        } else {
+            let args = &field.output_params;
+            quote! { (#(#args),*) }
+        };
+
+
+        let call_args = if field.input_params.len() > 0 {
+            let arg_expr = (0..field.input_params.len()).map(|index| quote! {input_params[#index]}).collect::<Vec<_>>();
+            quote! {interpreter, #(#arg_expr),*}
+        } else {
+            quote! {interpreter}
+        };
+
+        let return_vec = (0..field.output_params.len()).map(|index| quote! {out_tuple.#index});
+        let call = match field.output_params.len() {
+            0 => quote! {
+                closure(#call_args);
+                return Vec::new();
+            },
+            1 => quote! {
+                let result = closure(#call_args);
+                return vec![ result ];
+            },
+            _ => quote! {
+                let out_tuple = closure(#call_args);
+                return vec![ #(#return_vec),* ];
+            },
+        };
+
+        quote! {
+            #enum_name::#variant => {
+                let closure = |#interpreter_arg: &mut crate::Interpreter, #(#input_args),*| -> #output_args
+                    #closure_expr;
+
+                #call
+            }
+        }
+    });
+
+    let tokens = quote! {
+        pub enum #enum_name {
+            #(#field_names),*
+        }
+
+        impl #enum_name {
+            pub fn get_from_code(code: u32) -> Self {
+                match code {
+                    #(#getcode_match_arms)*
+                    _ => panic!("Unknown opcode 0x{code:03x}")
+                }
+            }
+
+            pub fn get_input_arg_count(opcode: Self) -> usize {
+                match opcode {
+                    #(#getinput_match_arms)*
+                }
+            }
+
+            pub fn get_output_arg_count(opcode: Self) -> usize {
+                match opcode {
+                    #(#getoutput_match_arms)*
+                }
+            }
+
+            pub fn call_opcode(interpreter: &mut crate::Interpreter, opcode: Self, input_params: Vec<u32>) -> Vec<u32> {
+                match opcode {
+                    #(#closure_match_arms)*
+                }
+            }
+        }
+    };
+
+    tokens.into()
+}
+
+impl Parse for OpcodeBlock {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let inner;
+        input.parse::<Token![pub]>()?;
+        Ok(OpcodeBlock {
+            enum_token: input.parse()?,
+            enum_name: input.parse()?,
+            brace_token: braced!(inner in input),
+            codes: inner.parse_terminated(SingleOpcodeDefinition::parse, Token![,])?,
+        })
+    }
+}
+
+impl Parse for SingleOpcodeDefinition {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let attr;
+        input.parse::<Token![#]>()?;
+        bracketed!(attr in input);
+        let code = attr.parse::<Ident>()?;
+        if code != "code" {
+            return Err(Error::new(code.span(), "expected `code` attribute"));
+        }
+        let code;
+        parenthesized!(code in attr);
+        let byte_code = code.parse::<LitInt>()?;
+
+        input.parse::<Token![fn]>()?;
+        let name = input.parse::<Ident>()?;
+
+        let params;
+        let mut interpreter_ident = None;
+        parenthesized!(params in input);
+        if params.peek(Token![&]) {
+            params.parse::<Token![&]>()?;
+            params.parse::<Token![mut]>()?;
+            interpreter_ident = Some(params.parse::<Ident>()?);
+
+            if params.peek(Token![,]) {
+                params.parse::<Token![,]>()?;
+            }
+        }
+
+        let input_params = params.parse_terminated(PatType::parse, Token![,])?
+            .into_iter().map(|param| param).collect();
+
+        let output_params;
+        if input.peek(Token![->]) {
+            input.parse::<Token![->]>()?;
+
+            if input.peek(Paren) {
+                let params;
+                parenthesized!(params in input);
+                output_params = params.parse_terminated(Type::parse, Token![,])?.into_iter().map(|s| s).collect::<Vec<_>>();
+            } else {
+                output_params = vec![input.parse::<Type>()?]
+            }
+        } else {
+            output_params = Vec::new();
+        }
+
+        let func_block = input.parse::<Block>()?;
+
+        Ok(SingleOpcodeDefinition {
+            byte_code,
+            name,
+            input_params,
+            interpreter_ident,
+            output_params,
+            func_block,
+        })
+    }
+}
+
+struct OpcodeBlock {
+    pub enum_token: token::Enum,
+    pub enum_name: Ident,
+    pub brace_token: token::Brace,
+    pub codes: Punctuated<SingleOpcodeDefinition, Token![,]>,
+}
+
+struct SingleOpcodeDefinition {
+    pub byte_code: LitInt,
+    pub name: Ident,
+    pub interpreter_ident: Option<Ident>,
+    pub input_params: Vec<PatType>,
+    pub output_params: Vec<Type>,
+    pub func_block: Block,
+}
diff --git a/src/interpreter.rs b/src/interpreter.rs
index ebddcf6..b93b05f 100644
--- a/src/interpreter.rs
+++ b/src/interpreter.rs
@@ -37,18 +37,18 @@ pub enum Output {
 }
 
 pub struct Interpreter {
-    memory: Vec<u8>,
-    stack: Vec<u8>,
-    mem_writeable: MemAddress,
-    mem_min: MemAddress,
-    heap: Vec<HeapChunk>,
-
-    reg_program: MemAddress,
-    reg_stack: StackAddress,
-    reg_frame: StackAddress,
-    reg_strdec: MemAddress,
-
-    output_mode: Output,
+    pub memory: Vec<u8>,
+    pub stack: Vec<u8>,
+    pub mem_writeable: MemAddress,
+    pub mem_min: MemAddress,
+    pub heap: Vec<HeapChunk>,
+
+    pub reg_program: MemAddress,
+    pub reg_stack: StackAddress,
+    pub reg_frame: StackAddress,
+    pub reg_strdec: MemAddress,
+
+    pub output_mode: Output,
 }
 
 impl Interpreter {
diff --git a/src/main.rs b/src/main.rs
index 7866a7a..4e97cd4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,8 +2,10 @@
 mod interpreter;
 mod glk;
 mod instructions;
-use interpreter::Interpreter;
+mod opcodes;
+use opcodes::OpCodes;
 
+use interpreter::Interpreter;
 use std::{env, fs, process::exit};
 
 const MAJOR_VERSION : u16 = 3;
diff --git a/src/opcodes.rs b/src/opcodes.rs
new file mode 100644
index 0000000..e8ef7fe
--- /dev/null
+++ b/src/opcodes.rs
@@ -0,0 +1,28 @@
+use opcode_proc::generate_opcodes;
+
+generate_opcodes!(
+    pub enum OpCodes {
+        #[code(0x00)]
+        fn Nop() {
+            
+        },
+
+        #[code(0x10)]
+        fn Add(a: u32, b: u32) -> u32 {
+            a + b
+        },
+
+        #[code(0x11)]
+        fn Sub(a: u32, b: u32) -> u32 {
+            a - b
+        },
+
+        #[code(0x23)]
+        fn Jnz(&mut interpreter, comparison: u32, offset: u32) {
+            if comparison != 0 {
+                interpreter.reg_program += offset;
+            }
+        }
+    }
+);
+