Skip to content

Commit dbeab28

Browse files
committed
Automatically derive decoding
1 parent e9fbbd7 commit dbeab28

File tree

5 files changed

+164
-68
lines changed

5 files changed

+164
-68
lines changed

lib/macros/src/lib.rs

Lines changed: 150 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ fn get_type_name(ty: &syn::Type) -> String {
3535
}
3636
}
3737

38+
fn variant_opcode_value(v: &syn::Variant) -> u8 {
39+
for attr in v.attrs.iter() {
40+
if attr.path().is_ident("opcode") {
41+
return attr.parse_args::<syn::LitInt>().unwrap().base10_parse().unwrap();
42+
}
43+
}
44+
45+
0
46+
}
47+
3848
fn impl_opcode_struct(ast: &ItemEnum) -> TokenStream {
3949
let field_names: Vec<_> = ast.variants.iter().map(|x| &x.ident).collect();
4050
let field_values = ast.variants.iter().map(|x| {
@@ -87,13 +97,94 @@ fn impl_opcode_struct(ast: &ItemEnum) -> TokenStream {
8797
| ((*r2 as u16) & 0xf) << 12
8898
},
8999

90-
["Register", "u8"] => quote! {
91-
// reg as u16 | 8 BIT INT
92-
// req as u16 | (((*reg as u8) >> 4) | (*amount))
93-
// Self::#name(reg, amount) => OpCode::#name as u16 | ((((*reg as u8) >> 4) | (amount)) as u16 >> 8)
94-
Self::#name(reg, amount) => ((((*reg as u8) << 4 | amount) as u16) << 8) | (OpCode::#name as u16)
95-
// ((*reg as u8) & 0xf) << 8
96-
// | ((*amount as u8) & 0xf) << 8
100+
_ => panic!("Invalid types {types:?}"),
101+
}
102+
} else {
103+
panic!("Unknown fields type for ident {name}");
104+
}
105+
})
106+
.collect();
107+
108+
let field_u16_decodings: Vec<_> = ast
109+
.variants
110+
.iter()
111+
.map(|x| {
112+
let value = variant_opcode_value(x);
113+
let name = &x.ident;
114+
115+
if let syn::Fields::Unit = &x.fields {
116+
return quote! {
117+
#value => Ok(Self::#name)
118+
};
119+
}
120+
121+
if let syn::Fields::Unnamed(fields) = &x.fields {
122+
let types: Vec<_> = fields
123+
.unnamed
124+
.iter()
125+
.map(|f| get_type_name(&f.ty))
126+
.collect();
127+
128+
let types: Vec<&str> = types.iter().map(AsRef::as_ref).collect();
129+
130+
match types[..] {
131+
["u8"] => quote! {
132+
#value => Ok(Self::#name(((ins & 0xff00) >> 8) as u8))
133+
},
134+
135+
["Register"] => quote! {
136+
#value => Ok(Self::#name(Register::from(((ins & 0xf00) >> 8) as u8)))
137+
},
138+
139+
["Register", "Register"] => quote! {
140+
#value => {
141+
let r1 = Register::from(((ins & 0xf00) >> 8) as u8);
142+
let r2 = Register::from(((ins & 0xf000) >> 12) as u8);
143+
144+
Ok(Self::#name(r1, r2))
145+
}
146+
},
147+
148+
_ => panic!("Invalid types {types:?}"),
149+
}
150+
} else {
151+
panic!("Unknown fields type for ident {name}");
152+
}
153+
})
154+
.collect();
155+
156+
let field_to_string: Vec<_> = ast
157+
.variants
158+
.iter()
159+
.map(|x| {
160+
let name = &x.ident;
161+
162+
if let syn::Fields::Unit = &x.fields {
163+
return quote! {
164+
Self::#name => write!(f, stringify!(#name))
165+
};
166+
}
167+
168+
if let syn::Fields::Unnamed(fields) = &x.fields {
169+
let types: Vec<_> = fields
170+
.unnamed
171+
.iter()
172+
.map(|f| get_type_name(&f.ty))
173+
.collect();
174+
175+
let types: Vec<&str> = types.iter().map(AsRef::as_ref).collect();
176+
177+
match types[..] {
178+
["u8"] => quote! {
179+
Self::#name(byte) => write!(f, "{} {}", stringify!(#name), byte)
180+
},
181+
182+
["Register"] => quote! {
183+
Self::#name(r) => write!(f, "{} {}", stringify!(#name), r)
184+
},
185+
186+
["Register", "Register"] => quote! {
187+
Self::#name(r1, r2) => write!(f, "{} {} {}", stringify!(#name), r1, r2)
97188
},
98189

99190
_ => panic!("Invalid types {types:?}"),
@@ -131,12 +222,32 @@ fn impl_opcode_struct(ast: &ItemEnum) -> TokenStream {
131222
fn try_from(value: u8) -> Result<Self, Self::Error> {
132223
match value {
133224
#(x if x == Self::#field_names as u8 => Ok(Self::#field_names),)*
134-
// x if x == Self::AddReg as u8 => Ok(Self::AddReg),
135225
_ => Err(format!("Unknown opcode 0x{value:X}")),
136226
}
137227
}
138228
}
139229

230+
impl TryFrom<u16> for Instruction {
231+
type Error = String;
232+
233+
fn try_from(ins: u16) -> Result<Self, Self::Error> {
234+
let op = (ins & 0xff) as u8;
235+
236+
match op {
237+
#(#field_u16_decodings,)*
238+
_ => panic!("Invalid types"),
239+
}
240+
}
241+
}
242+
243+
impl std::fmt::Display for Instruction {
244+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245+
match self {
246+
#(#field_to_string,)*
247+
}
248+
}
249+
}
250+
140251
impl Instruction {
141252
pub fn encode_u16(&self) -> u16 {
142253
match self {
@@ -176,3 +287,34 @@ pub fn derive_from_u8(input: proc_macro::TokenStream) -> proc_macro::TokenStream
176287

177288
proc_macro::TokenStream::from(expanded)
178289
}
290+
291+
/// Automatically implements the from display trait
292+
/// for ease of use
293+
#[proc_macro_derive(Display)]
294+
pub fn derive_display(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
295+
let input = parse_macro_input!(input as DeriveInput);
296+
let name = input.ident;
297+
298+
let variants = if let syn::Data::Enum(data) = input.data {
299+
data.variants
300+
} else {
301+
panic!("Display can only be derived for enums");
302+
};
303+
304+
let variant_names: Vec<_> = variants.iter().map(|v| &v.ident).collect();
305+
306+
let expanded = quote! {
307+
impl std::fmt::Display for #name {
308+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
309+
let string = match self {
310+
#(Self::#variant_names => stringify!(#variant_names),)*
311+
_ => panic!("Invalid value"),
312+
};
313+
314+
write!(f, "{string}")
315+
}
316+
}
317+
};
318+
319+
proc_macro::TokenStream::from(expanded)
320+
}

strawberry/src/memory.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::fmt::Write;
2+
13
type DynErr = Box<dyn std::error::Error>;
24

35
/// A trait implemented on all types of memory used
@@ -59,8 +61,8 @@ pub enum Error {
5961
impl std::fmt::Display for Error {
6062
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
6163
match *self {
62-
Error::OutOfBounds(v) => write!(f, "OutOfBounds error occurred @ 0x{v:X}"),
63-
Error::OtherError => write!(f, "Another error occurred"),
64+
Self::OutOfBounds(v) => write!(f, "OutOfBounds error occurred @ 0x{v:X}"),
65+
Self::OtherError => write!(f, "Another error occurred"),
6466
}
6567
}
6668
}
@@ -89,12 +91,13 @@ impl Linear {
8991

9092
impl Addressable for Linear {
9193
fn dump(&self) -> String {
92-
self.bytes
93-
.chunks_exact(2)
94-
.fold(String::new(), |mut acc, chunk| {
95-
acc.push_str(&format!("{:02x}{:02x} ", chunk[0], chunk[1]));
96-
acc
97-
})
94+
let mut result = String::with_capacity(self.bytes.len() * 4);
95+
96+
for chunk in self.bytes.chunks_exact(2) {
97+
write!(result, "{:02x}{:02x} ", chunk[0], chunk[1]).unwrap();
98+
}
99+
100+
result
98101
}
99102

100103
fn read(&self, addr: u16) -> Result<u8, DynErr> {

strawberry/src/op.rs

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -28,52 +28,3 @@ pub enum Instruction {
2828
#[opcode(0x6)]
2929
Signal(u8),
3030
}
31-
32-
fn parse_instruction_arg(ins: u16) -> u8 {
33-
((ins & 0xff00) >> 8) as u8
34-
}
35-
36-
impl TryFrom<u16> for Instruction {
37-
type Error = String;
38-
39-
fn try_from(ins: u16) -> Result<Self, Self::Error> {
40-
let op = (ins & 0xff) as u8;
41-
let opcode = OpCode::try_from(op)?;
42-
43-
match opcode {
44-
OpCode::Nop => Ok(Instruction::Nop),
45-
OpCode::Push => {
46-
let arg = parse_instruction_arg(ins);
47-
Ok(Instruction::Push(arg))
48-
}
49-
50-
OpCode::PopReg => {
51-
let reg = (ins & 0xf00) >> 8;
52-
let reg = Register::from(reg as u8);
53-
54-
Ok(Instruction::PopReg(reg))
55-
}
56-
57-
OpCode::PushReg => {
58-
let reg = (ins & 0xf00) >> 8;
59-
let reg = Register::from(reg as u8);
60-
61-
Ok(Instruction::PushReg(reg))
62-
}
63-
64-
OpCode::AddStack => Ok(Instruction::AddStack),
65-
66-
OpCode::AddReg => {
67-
let r1 = Register::from(((ins & 0xf00) >> 8) as u8);
68-
let r2 = Register::from(((ins & 0xf00) >> 12) as u8);
69-
70-
Ok(Instruction::AddReg(r1, r2))
71-
}
72-
73-
OpCode::Signal => {
74-
let arg = parse_instruction_arg(ins);
75-
Ok(Instruction::Signal(arg))
76-
}
77-
}
78-
}
79-
}

strawberry/src/register.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use strawberryvm_derive::FromU8;
1+
use strawberryvm_derive::{FromU8, Display};
22

33
/// Enum for registers, only really used
44
/// to coordinate the register slice.
5-
#[derive(Debug, Clone, Copy, FromU8)]
5+
#[derive(Debug, Clone, Copy, FromU8, Display)]
66
pub enum Register {
77
A = 0, // General purpose
88
B = 1, // General purpose

strawberry/src/vm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ impl Machine {
141141
let instruction = self.memory.read_u16(pc)?;
142142
let op = Instruction::try_from(instruction)?;
143143

144-
println!("{pc:0>4} │ Got instruction {op:?}");
144+
println!("{pc:0>4} │ Got instruction `{op}`");
145145

146146
match op {
147147
Instruction::Nop => Ok(()),

0 commit comments

Comments
 (0)