1use bytes::{BufMut, Bytes};
7use core::fmt::Display;
8use core::num::NonZeroU32;
9
10use crate::{
11 cast::Overflow,
12 decoding_table::DecodeNode,
13 error::AssemblerError,
14 instr_def::Instr,
15 resolver::{ResolvedAddr, Resolver},
16 strings::{MysteryString, Utf32String},
17};
18
19#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
21pub struct LabelRef<L>(pub L, pub i32);
22
23#[derive(Debug, Clone)]
25pub enum Item<L> {
26 Label(L),
28 Align(NonZeroU32),
34 DecodingTable(DecodeNode<L>),
36 FnHeader(CallingConvention, u32),
41 Instr(Instr<L>),
43 MysteryString(MysteryString),
45 CompressedString(Bytes),
48 Utf32String(Utf32String),
50 Blob(Bytes),
52 LabelRef(LabelRef<L>, u8),
54}
55
56#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
59pub enum ZeroItem<L> {
60 Label(L),
62 Space(u32),
64 Align(NonZeroU32),
70}
71
72#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
74pub enum CallingConvention {
75 ArgsOnStack,
77 ArgsInLocals,
79}
80
81impl<L> Item<L> {
82 pub fn map<F, M>(self, mut f: F) -> Item<M>
84 where
85 F: FnMut(L) -> M,
86 {
87 match self {
88 Item::Label(l) => Item::Label(f(l)),
89 Item::Align(a) => Item::Align(a),
90 Item::DecodingTable(t) => Item::DecodingTable(t.map(&mut f)),
91 Item::FnHeader(t, n) => Item::FnHeader(t, n),
92 Item::Instr(i) => Item::Instr(i.map(f)),
93 Item::MysteryString(s) => Item::MysteryString(s),
94 Item::Utf32String(s) => Item::Utf32String(s),
95 Item::CompressedString(s) => Item::CompressedString(s),
96 Item::Blob(b) => Item::Blob(b),
97 Item::LabelRef(l, shift) => Item::LabelRef(l.map(f), shift),
98 }
99 }
100}
101
102impl<L> Item<L>
103where
104 L: Clone,
105{
106 pub(crate) fn worst_len(&self) -> usize {
107 match self {
108 Item::Label(_) => 0,
109 Item::Align(_) => 0,
110 Item::DecodingTable(t) => 12 + t.len(),
111 Item::FnHeader(_, n) => {
112 let n_records: usize = n
113 .div_ceil(255)
114 .try_into()
115 .expect("u32 should fit in a usize");
116 2 * n_records + 3
117 }
118 Item::Instr(i) => i.worst_len(),
119 Item::MysteryString(s) => s.len() + 2,
120 Item::Utf32String(s) => s.byte_len() + 8,
121 Item::CompressedString(s) => 1 + s.len(),
122 Item::Blob(b) => b.len(),
123 Item::LabelRef(_, _) => 4,
124 }
125 }
126
127 pub(crate) fn align(&self) -> u32 {
128 match self {
129 Item::Align(a) => (*a).into(),
130 _ => 1,
131 }
132 }
133
134 pub(crate) fn resolved_len<R>(
135 &self,
136 position: u32,
137 resolver: &R,
138 ) -> Result<usize, AssemblerError<L>>
139 where
140 R: Resolver<Label = L>,
141 {
142 Ok(match self {
143 Item::Instr(i) => i.resolve(position, resolver)?.len(),
144 _ => self.worst_len(),
145 })
146 }
147
148 pub(crate) fn serialize<R, B>(
149 &self,
150 position: u32,
151 resolver: &R,
152 mut buf: B,
153 ) -> Result<(), AssemblerError<L>>
154 where
155 R: Resolver<Label = L>,
156 B: BufMut,
157 {
158 match self {
159 Item::Label(_) => {}
160 Item::Align(x) => {
161 let align: u32 = (*x).into();
162 let modulus = position % align;
163 let padding = if modulus == 0 { 0 } else { align - modulus };
164 buf.put_bytes(
165 0,
166 padding
167 .try_into()
168 .expect("u32 to usize conversion should succeed"),
169 );
170 }
171 Item::DecodingTable(table) => {
172 let resolved = table.resolve(resolver)?;
173 let count = u32::try_from(resolved.count_nodes()).overflow()?;
174 let length =
175 u32::try_from(resolved.len().checked_add(12).overflow()?).overflow()?;
176 let root = position.checked_add(12).overflow()?;
177 buf.put_u32(length);
178 buf.put_u32(count);
179 buf.put_u32(root);
180 resolved.serialize(0, &mut buf);
181 }
182 Item::FnHeader(cc, args) => {
183 match cc {
184 CallingConvention::ArgsOnStack => buf.put_u8(0xc0),
185 CallingConvention::ArgsInLocals => buf.put_u8(0xc1),
186 }
187
188 for _ in 0..(*args / 255) {
189 buf.put_u8(4);
190 buf.put_u8(255);
191 }
192
193 if *args % 255 != 0 {
194 buf.put_u8(4);
195 buf.put_u8(
196 u8::try_from(*args % 255).expect("a number modulo 255 should fit in a u8"),
197 );
198 }
199
200 buf.put_bytes(0, 2);
201 }
202 Item::Instr(instr) => {
203 let resolved = instr.resolve(position, resolver)?;
204 resolved.serialize(buf);
205 }
206 Item::MysteryString(s) => {
207 buf.put_u8(0xe0);
208 buf.put(s.to_bytes());
209 buf.put_u8(0);
210 }
211 Item::Utf32String(s) => {
212 buf.put_u32(0xe2000000);
213 buf.put(s.to_bytes());
214 buf.put_u32(0);
215 }
216 Item::CompressedString(bytes) => {
217 buf.put_u8(0xe1);
218 buf.put(bytes.clone());
219 }
220 Item::Blob(blob) => {
221 buf.put(blob.clone());
222 }
223 Item::LabelRef(l, shift) => {
224 let unshifted_addr = l.resolve_absolute(resolver)?;
225
226 if unshifted_addr.trailing_zeros() < (*shift).into() {
227 return Err(AssemblerError::InsufficientAlignment {
228 label: l.0.clone(),
229 offset: l.1,
230 shift: *shift,
231 });
232 }
233
234 buf.put_u32(unshifted_addr >> *shift);
235 }
236 }
237 Ok(())
238 }
239}
240
241impl<L> Display for Item<L>
242where
243 L: Display,
244{
245 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
246 match self {
247 Item::Label(label) => write!(f, ".label {label}")?,
248 Item::Align(a) => write!(f, ".align {a}")?,
249 Item::DecodingTable(_) => write!(f, ".decoding_table")?,
250 Item::FnHeader(CallingConvention::ArgsInLocals, args) => write!(f, ".fnlocal {args}")?,
251 Item::FnHeader(CallingConvention::ArgsOnStack, args) => write!(f, ".fnstack {args}")?,
252 Item::Instr(instr) => write!(f, "\t{instr}")?,
253 Item::MysteryString(s) => write!(f, ".string {:?}", s)?,
254 Item::CompressedString(c) => write!(f, ".compressed_string {c:x}")?,
255 Item::Utf32String(s) => write!(f, ".unistring {:?}", s)?,
256 Item::Blob(b) => write!(f, ".blob {b:x}")?,
257 Item::LabelRef(LabelRef(label, offset), shift) => {
258 write!(f, ".labelref ({label}")?;
259 if *offset != 0 {
260 write!(f, "{offset:+#x}")?;
261 }
262 if *shift != 0 {
263 write!(f, ">>{shift}")?;
264 }
265 write!(f, ")")?;
266 }
267 }
268 Ok(())
269 }
270}
271
272impl<L> ZeroItem<L> {
273 pub fn map<F, M>(self, mut f: F) -> ZeroItem<M>
275 where
276 F: FnMut(L) -> M,
277 {
278 match self {
279 ZeroItem::Label(l) => ZeroItem::Label(f(l)),
280 ZeroItem::Space(x) => ZeroItem::Space(x),
281 ZeroItem::Align(a) => ZeroItem::Align(a),
282 }
283 }
284
285 pub(crate) fn len(&self) -> u32 {
286 match self {
287 ZeroItem::Label(_) => 0,
288 ZeroItem::Space(x) => *x,
289 ZeroItem::Align(_) => 0,
290 }
291 }
292
293 pub(crate) fn align(&self) -> u32 {
294 match self {
295 ZeroItem::Label(_) => 1,
296 ZeroItem::Space(_) => 1,
297 ZeroItem::Align(a) => (*a).into(),
298 }
299 }
300}
301
302impl<L> Display for ZeroItem<L>
303where
304 L: Display,
305{
306 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
307 match self {
308 ZeroItem::Label(label) => write!(f, ".label {label}"),
309 ZeroItem::Space(x) => write!(f, ".space {x}"),
310 ZeroItem::Align(a) => write!(f, ".align {a}"),
311 }
312 }
313}
314
315impl<L> LabelRef<L> {
316 pub fn map<F, M>(self, mut f: F) -> LabelRef<M>
318 where
319 F: FnMut(L) -> M,
320 {
321 LabelRef(f(self.0), self.1)
322 }
323
324 pub(crate) fn resolve<R>(&self, resolver: &R) -> Result<ResolvedAddr, AssemblerError<L>>
325 where
326 R: Resolver<Label = L>,
327 {
328 Ok(match resolver.resolve(&self.0)? {
329 ResolvedAddr::Rom(addr) => {
330 ResolvedAddr::Rom(addr.checked_add_signed(self.1).overflow()?)
331 }
332 ResolvedAddr::Ram(addr) => {
333 ResolvedAddr::Ram(addr.checked_add_signed(self.1).overflow()?)
334 }
335 })
336 }
337
338 pub(crate) fn resolve_absolute<R>(&self, resolver: &R) -> Result<u32, AssemblerError<L>>
339 where
340 R: Resolver<Label = L>,
341 {
342 resolver
343 .resolve_absolute(&self.0)?
344 .checked_add_signed(self.1)
345 .overflow()
346 }
347}