Skip to content

Commit 4e473c0

Browse files
committed
WIP
1 parent dd95be3 commit 4e473c0

File tree

9 files changed

+513
-226
lines changed

9 files changed

+513
-226
lines changed

macros/src/lib.rs

Lines changed: 206 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -2,172 +2,249 @@
22
extern crate proc_macro;
33
use core::panic;
44
use proc_macro::TokenStream;
5-
use std::cmp;
5+
use std::cmp::{self, min};
66
use std::collections::HashMap;
77
use syn::__private::quote::{format_ident, quote};
88
use syn::__private::ToTokens;
99
use syn::punctuated::Punctuated;
1010
use syn::spanned::Spanned;
11-
use syn::{parse_macro_input, Expr, Lit, Pat};
11+
use syn::{parse_macro_input, Expr, ExprField, Ident, Lit, Pat};
1212

1313
#[proc_macro]
1414
pub fn io_read(item: TokenStream) -> TokenStream {
1515
let input = parse_macro_input!(item with Punctuated::<Expr, syn::Token![,]>::parse_terminated);
16-
let name = input.first().unwrap().to_token_stream().to_string();
17-
let io_ports = match input.last().unwrap() {
18-
Expr::Array(array) => array,
19-
_ => panic!(),
20-
};
2116

22-
let mut min_addr = u32::MAX;
23-
let mut max_addr = 0;
24-
let mut exprs = HashMap::new();
17+
enum EntryFunc {
18+
Func(Ident),
19+
MemOffset(Expr),
20+
}
21+
22+
struct Entry {
23+
size: usize,
24+
offset: usize,
25+
func: EntryFunc,
26+
}
27+
28+
let mut entries = Vec::new();
2529
let mut funcs = Vec::new();
2630

27-
for elem in &io_ports.elems {
28-
let tuple = match elem {
31+
let mut last_offset = -1;
32+
for expr in &input {
33+
let tuple = match expr {
2934
Expr::Tuple(tuple) => tuple,
30-
_ => panic!(),
35+
_ => unreachable!(),
3136
};
3237

33-
let io_call = match &tuple.elems[0] {
34-
Expr::Call(io_call) => io_call,
35-
_ => panic!(),
38+
let (size, io_ident, offset) = match tuple.elems.first().unwrap() {
39+
Expr::Call(call) => {
40+
let ident = match call.func.as_ref() {
41+
Expr::Path(path) => &path.path.segments.first().unwrap().ident,
42+
_ => unreachable!(),
43+
};
44+
let size = match ident.to_string().as_str() {
45+
"io8" => 1usize,
46+
"io16" => 2,
47+
"io32" => 4,
48+
_ => unreachable!(),
49+
};
50+
let offset: usize = match call.args.first().unwrap() {
51+
Expr::Lit(lit) => match &lit.lit {
52+
Lit::Int(offset) => offset.base10_parse().unwrap(),
53+
_ => unreachable!(),
54+
},
55+
_ => unreachable!(),
56+
};
57+
(size, ident, offset)
58+
}
59+
_ => unreachable!(),
3660
};
3761

38-
let closure = match &tuple.elems[1] {
39-
Expr::Closure(closure) => closure,
40-
_ => panic!(),
62+
assert!(offset as i32 > last_offset);
63+
last_offset = offset as i32;
64+
65+
let func = match tuple.elems.last().unwrap() {
66+
Expr::Closure(closure) => {
67+
let func_arg = match closure.inputs.first().unwrap() {
68+
Pat::Ident(ident) => &ident.ident,
69+
_ => unreachable!(),
70+
};
71+
let func_body = closure.body.as_ref();
72+
let func_name = format_ident!("read_0x{offset:x}_u{}_", size * 8, span = io_ident.span());
73+
let func_ret_type = format_ident!("u{}", size * 8);
74+
funcs.push(quote! {
75+
unsafe extern "C" fn #func_name(#func_arg: &mut crate::core::emu::Emu) -> #func_ret_type {
76+
#func_body
77+
}
78+
});
79+
EntryFunc::Func(func_name)
80+
}
81+
Expr::Call(call) => {
82+
match call.func.as_ref() {
83+
Expr::Path(path) => {
84+
assert_eq!(path.path.segments.first().unwrap().ident.to_string(), "addr")
85+
}
86+
_ => unreachable!(),
87+
}
88+
EntryFunc::MemOffset(call.args.first().unwrap().clone())
89+
}
90+
_ => unreachable!(),
4191
};
4292

43-
let closure_arg = match closure.inputs.first().unwrap() {
44-
Pat::Ident(ident) => ident,
45-
_ => panic!(),
46-
};
93+
entries.push(Entry { size, offset, func });
94+
}
4795

48-
let io_call_path = match &io_call.func.as_ref() {
49-
Expr::Path(path) => path,
50-
_ => panic!(),
51-
};
96+
let min_addr = entries.first().unwrap().offset;
97+
let max_addr = entries.last().unwrap().offset + entries.last().unwrap().size;
98+
99+
let normalize_addr = if min_addr == 0 { "".to_string() } else { format!("subs r0, r0, {min_addr}") };
100+
let align = 5;
101+
let align_size = 1 << align;
102+
103+
let mut asm = format!(
104+
"
105+
.align {align}
106+
push {{{{r4-r7,lr}}}}
107+
{normalize_addr}
108+
add r3, pc, {}
109+
mov r4, r1
110+
mov r5, r1
111+
bic r3, r3, {}
112+
mov r6, r2
113+
movs r7, 0
114+
add r3, r3, r0, lsl #{align}
115+
mov pc, r3
116+
",
117+
align_size - 1,
118+
align_size - 1
119+
);
52120

53-
let io_call_lit = match &io_call.args[0] {
54-
Expr::Lit(lit) => match &lit.lit {
55-
Lit::Int(lit) => lit,
56-
_ => panic!(),
57-
},
58-
_ => panic!(),
59-
};
121+
let end_label = max_addr.to_string();
122+
let mut prev_offset_end = entries.first().unwrap().offset;
123+
for (i, entry) in entries.iter().enumerate() {
124+
if entry.offset > prev_offset_end {
125+
for i in prev_offset_end..entry.offset {
126+
let remaining_empty = min(entry.offset - i, 4);
127+
asm.push_str(&format!(
128+
".align {align}
129+
{i}:
130+
subs r4, r4, {remaining_empty}
131+
ble {end_label}f
132+
b {}f
133+
",
134+
entry.offset
135+
));
136+
}
137+
}
138+
prev_offset_end = entry.offset + entry.size;
60139

61-
let segment = io_call_path.path.segments.first().unwrap();
140+
let last_entry = i == entries.len() - 1;
62141

63-
let size = match segment.ident.to_string().as_str() {
64-
"io8" => 1u8,
65-
"io16" => 2u8,
66-
"io32" => 4u8,
67-
_ => panic!(),
68-
};
142+
for i in entry.offset..entry.offset + entry.size {
143+
let bytes_read = entry.offset + entry.size - i;
69144

70-
let addr_str = io_call_lit.to_string();
71-
let addr = u32::from_str_radix(addr_str.trim_start_matches("0x"), 16).unwrap();
72-
min_addr = cmp::min(min_addr, addr);
73-
max_addr = cmp::max(max_addr, addr + size as u32);
145+
let read_func = match &entry.func {
146+
EntryFunc::Func(_) => {
147+
format!(
148+
"mov r0, r6
149+
bl {{func_{}}}",
150+
entry.offset
151+
)
152+
}
153+
EntryFunc::MemOffset(_) => {
154+
let op = match entry.size {
155+
1 => "ldrb",
156+
2 => "ldrh",
157+
4 => "ldr",
158+
_ => unreachable!(),
159+
};
160+
format!(
161+
"ldr r1, ={{func_{}}}
162+
{op} r0, [r6, r1]",
163+
entry.offset
164+
)
165+
}
166+
};
74167

75-
let func_name = format_ident!("_read_{addr_str}");
76-
let func_arg = format_ident!("emu", span = closure_arg.span());
77-
let body = closure.body.as_ref();
78-
let func = quote!(
79-
#[allow(unreachable_code)]
80-
fn #func_name(#func_arg: &mut crate::core::emu::Emu) -> u32 {
81-
#body as u32
82-
}
83-
);
84-
exprs.insert(addr, (size, func_name));
85-
funcs.push(func);
168+
let terminate_cond = if last_entry {
169+
"".to_string()
170+
} else {
171+
format!(
172+
"subs r4, r4, {bytes_read}
173+
ble {end_label}f"
174+
)
175+
};
176+
let next_entry = if i == max_addr - 1 { "".to_string() } else { format!("b {}f", entry.offset + entry.size) };
177+
asm.push_str(&format!(
178+
".align {align}
179+
{i}:
180+
{read_func}
181+
subs r2, r5, r4
182+
lsls r2, r2, 3
183+
lsls r0, r0, r2
184+
orrs r7, r7, r0
185+
{terminate_cond}
186+
{next_entry}
187+
"
188+
));
189+
}
86190
}
87191

88-
funcs.push(quote!(
89-
fn _read_empty(_: &mut crate::core::emu::Emu) -> u32 {
90-
0
91-
}
192+
asm.push_str(&format!(
193+
"{end_label}:
194+
mov r0, r7
195+
pop {{{{r4-r7,pc}}}}
196+
"
92197
));
93198

94-
let mut lut_entries = Vec::new();
95-
96-
if min_addr > 0 {
97-
min_addr -= cmp::min(3, min_addr);
98-
}
99-
max_addr += 3;
199+
let mut asm_args = Vec::new();
100200

101-
let mut i = min_addr;
102-
while i < max_addr {
103-
if let Some((size, func_name)) = exprs.get(&i) {
104-
for j in 0..*size {
105-
let remaining = *size - j;
106-
let offset = j << 3;
107-
lut_entries.push(quote!(
108-
(Self::#func_name, #remaining, #offset),
109-
));
201+
for entry in entries {
202+
let offset = entry.offset;
203+
match entry.func {
204+
EntryFunc::Func(func_name) => {
205+
let func_arg = format_ident!("func_{offset}");
206+
asm_args.push(quote! {
207+
#func_arg = sym #func_name,
208+
});
110209
}
111-
i += *size as u32;
112-
} else {
113-
let mut j = i + 1;
114-
while j < max_addr {
115-
if exprs.contains_key(&j) {
116-
break;
117-
}
118-
j += 1;
210+
EntryFunc::MemOffset(field) => {
211+
let func_arg = format_ident!("func_{offset}");
212+
let value = match field {
213+
Expr::Field(field) => {
214+
quote! {
215+
std::mem::offset_of!(crate::core::emu::Emu, #field)
216+
}
217+
}
218+
Expr::Call(call) => {
219+
quote! {
220+
#call
221+
}
222+
}
223+
_ => unreachable!(),
224+
};
225+
asm_args.push(quote! {
226+
#func_arg = const #value,
227+
});
119228
}
120-
let size = j - i;
121-
for k in 0..size {
122-
let remaining = cmp::min(4, size - k) as u8;
123-
lut_entries.push(quote!(
124-
(Self::_read_empty, #remaining, 0),
125-
));
126-
}
127-
i = j;
128229
}
129230
}
130231

131-
let size = (max_addr - min_addr) as usize;
132-
assert_eq!(lut_entries.len(), size);
133-
134-
let lut_tokens = quote!(
135-
const _LUT: [(fn(&mut crate::core::emu::Emu) -> u32, u8, u8); #size] = [
136-
#(#lut_entries)*
137-
];
138-
);
139-
140-
let name = format_ident!("{name}");
141-
let tokens = quote!(
142-
pub struct #name;
143-
144-
impl #name {
145-
#(#funcs)*
146-
147-
#lut_tokens
232+
quote! {
233+
#[unsafe(naked)]
234+
pub unsafe extern "C" fn read(offset: u32, size: usize, emu: &mut crate::core::emu::Emu) -> u32 {
235+
std::arch::naked_asm!(
236+
#asm,
237+
#(#asm_args)*
238+
);
239+
}
148240

149-
const MIN_ADDR: u32 = #min_addr;
150-
const MAX_ADDR: u32 = #max_addr - 3;
241+
#(#funcs)*
151242

152-
pub fn is_in_range(addr: u32) -> bool {
153-
(Self::MIN_ADDR..Self::MAX_ADDR).contains(&addr)
154-
}
155-
156-
pub fn read(addr: u32, size: u8, emu: &mut crate::core::emu::Emu) -> u32 {
157-
let addr = addr - Self::MIN_ADDR;
158-
let mut ret = 0;
159-
let mut read = 0;
160-
while read < size {
161-
let (func, read_size, offset) = unsafe { Self::_LUT.get_unchecked(addr as usize + read as usize) };
162-
let value = func(emu) >> *offset;
163-
ret |= value << (read << 3);
164-
read += *read_size;
165-
}
166-
ret
167-
}
243+
pub fn is_in_range(offset: u32) -> bool {
244+
offset < (#max_addr as u32)
168245
}
169-
);
170-
tokens.into()
246+
}
247+
.into()
171248
}
172249

173250
#[proc_macro]
@@ -350,7 +427,7 @@ pub fn io_write(item: TokenStream) -> TokenStream {
350427
const MAX_ADDR: u32 = #max_addr - 3;
351428

352429
pub fn is_in_range(addr: u32) -> bool {
353-
(Self::MIN_ADDR..Self::MAX_ADDR).contains(&addr)
430+
addr < Self::MAX_ADDR
354431
}
355432

356433
pub fn write(value: u32, addr: u32, size: u8, emu: &mut crate::core::emu::Emu) {

0 commit comments

Comments
 (0)