|
2 | 2 | extern crate proc_macro; |
3 | 3 | use core::panic; |
4 | 4 | use proc_macro::TokenStream; |
5 | | -use std::cmp; |
| 5 | +use std::cmp::{self, min}; |
6 | 6 | use std::collections::HashMap; |
7 | 7 | use syn::__private::quote::{format_ident, quote}; |
8 | 8 | use syn::__private::ToTokens; |
9 | 9 | use syn::punctuated::Punctuated; |
10 | 10 | use syn::spanned::Spanned; |
11 | | -use syn::{parse_macro_input, Expr, Lit, Pat}; |
| 11 | +use syn::{parse_macro_input, Expr, ExprField, Ident, Lit, Pat}; |
12 | 12 |
|
13 | 13 | #[proc_macro] |
14 | 14 | pub fn io_read(item: TokenStream) -> TokenStream { |
15 | 15 | 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 | | - }; |
21 | 16 |
|
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(); |
25 | 29 | let mut funcs = Vec::new(); |
26 | 30 |
|
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 { |
29 | 34 | Expr::Tuple(tuple) => tuple, |
30 | | - _ => panic!(), |
| 35 | + _ => unreachable!(), |
31 | 36 | }; |
32 | 37 |
|
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!(), |
36 | 60 | }; |
37 | 61 |
|
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!(), |
41 | 91 | }; |
42 | 92 |
|
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 | + } |
47 | 95 |
|
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 | + ); |
52 | 120 |
|
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; |
60 | 139 |
|
61 | | - let segment = io_call_path.path.segments.first().unwrap(); |
| 140 | + let last_entry = i == entries.len() - 1; |
62 | 141 |
|
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; |
69 | 144 |
|
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 | + }; |
74 | 167 |
|
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 | + } |
86 | 190 | } |
87 | 191 |
|
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 | + " |
92 | 197 | )); |
93 | 198 |
|
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(); |
100 | 200 |
|
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 | + }); |
110 | 209 | } |
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 | + }); |
119 | 228 | } |
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; |
128 | 229 | } |
129 | 230 | } |
130 | 231 |
|
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 | + } |
148 | 240 |
|
149 | | - const MIN_ADDR: u32 = #min_addr; |
150 | | - const MAX_ADDR: u32 = #max_addr - 3; |
| 241 | + #(#funcs)* |
151 | 242 |
|
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) |
168 | 245 | } |
169 | | - ); |
170 | | - tokens.into() |
| 246 | + } |
| 247 | + .into() |
171 | 248 | } |
172 | 249 |
|
173 | 250 | #[proc_macro] |
@@ -350,7 +427,7 @@ pub fn io_write(item: TokenStream) -> TokenStream { |
350 | 427 | const MAX_ADDR: u32 = #max_addr - 3; |
351 | 428 |
|
352 | 429 | pub fn is_in_range(addr: u32) -> bool { |
353 | | - (Self::MIN_ADDR..Self::MAX_ADDR).contains(&addr) |
| 430 | + addr < Self::MAX_ADDR |
354 | 431 | } |
355 | 432 |
|
356 | 433 | pub fn write(value: u32, addr: u32, size: u8, emu: &mut crate::core::emu::Emu) { |
|
0 commit comments