1use alloc::borrow::{Borrow, Cow};
7use bytes::{Buf, BufMut, Bytes, BytesMut};
8use core::{fmt::Display, hash::Hash};
9
10#[cfg(not(feature = "std"))]
11use hashbrown::HashMap;
12#[cfg(feature = "std")]
13use std::collections::HashMap;
14
15use crate::{
16 cast::{checked_next_multiple_of, Overflow},
17 error::AssemblerError,
18 items::{Item, LabelRef, ZeroItem},
19 resolver::{ResolvedAddr, Resolver},
20};
21
22const HEADER_LENGTH: u32 = 0x24;
24const MAGIC_NUMBER: u32 = 0x476C756C;
26const GLULX_VERSION: u32 = 0x00030103;
28
29struct HashResolver<'a, L> {
31 hashmap: &'a HashMap<L, u32>,
32 ramstart: u32,
33}
34
35impl<L> Resolver for HashResolver<'_, L>
36where
37 L: Clone + Hash + Eq,
38{
39 type Label = L;
40
41 fn resolve(&self, label: &Self::Label) -> Result<ResolvedAddr, AssemblerError<Self::Label>> {
42 let addr = self.resolve_absolute(label)?;
43 if addr < self.ramstart {
44 Ok(ResolvedAddr::Rom(addr))
45 } else {
46 Ok(ResolvedAddr::Ram(addr - self.ramstart))
47 }
48 }
49
50 fn resolve_absolute(&self, label: &Self::Label) -> Result<u32, AssemblerError<Self::Label>> {
51 Ok(*self
52 .hashmap
53 .get(label)
54 .ok_or_else(|| AssemblerError::UndefinedLabel(label.clone()))?)
55 }
56}
57
58#[derive(Debug, Clone)]
60pub struct Assembly<'a, L>
61where
62 L: Clone,
63{
64 pub rom_items: Cow<'a, [Item<L>]>,
66 pub ram_items: Cow<'a, [Item<L>]>,
68 pub zero_items: Cow<'a, [ZeroItem<L>]>,
70 pub stack_size: u32,
72 pub start_func: LabelRef<L>,
74 pub decoding_table: Option<LabelRef<L>>,
76}
77
78impl<L> Assembly<'_, L>
79where
80 L: Clone + Eq + Hash,
81{
82 pub fn map<F, M>(self, mut f: F) -> Assembly<'static, M>
84 where
85 F: FnMut(L) -> M,
86 M: Clone,
87 {
88 let rom_items = self
89 .rom_items
90 .iter()
91 .cloned()
92 .map(|item| item.map(&mut f))
93 .collect();
94
95 let ram_items = self
96 .ram_items
97 .iter()
98 .cloned()
99 .map(|item| item.map(&mut f))
100 .collect();
101
102 let zero_items = self
103 .zero_items
104 .iter()
105 .cloned()
106 .map(|item| item.map(&mut f))
107 .collect();
108
109 let stack_size = self.stack_size;
110
111 let start_func = self.start_func.map(&mut f);
112 let decoding_table = self.decoding_table.map(|r| r.map(&mut f));
113
114 Assembly {
115 rom_items,
116 ram_items,
117 zero_items,
118 stack_size,
119 start_func,
120 decoding_table,
121 }
122 }
123
124 pub fn assemble(&self) -> Result<BytesMut, AssemblerError<L>> {
126 assemble(
127 self.rom_items.borrow(),
128 self.ram_items.borrow(),
129 self.zero_items.borrow(),
130 self.stack_size,
131 &self.start_func,
132 &self.decoding_table,
133 )
134 }
135
136 pub fn to_owning(&self) -> Assembly<'static, L> {
138 Assembly {
139 rom_items: Cow::Owned(self.rom_items.clone().into_owned()),
140 ram_items: Cow::Owned(self.ram_items.clone().into_owned()),
141 zero_items: Cow::Owned(self.zero_items.clone().into_owned()),
142 stack_size: self.stack_size,
143 start_func: self.start_func.clone(),
144 decoding_table: self.decoding_table.clone(),
145 }
146 }
147}
148
149impl<L> Display for Assembly<'_, L>
150where
151 L: Display + Clone,
152{
153 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
154 writeln!(f, ".stack_size {}", self.stack_size)?;
155 write!(f, ".start_func ({}", self.start_func.0)?;
156 if self.start_func.1 != 0 {
157 write!(f, "{:+#x}", self.start_func.1)?;
158 }
159 writeln!(f, ")")?;
160 if let Some(decoding_table) = &self.decoding_table {
161 write!(f, ".initial_decoding_table ({}", decoding_table.0)?;
162 if decoding_table.1 != 0 {
163 write!(f, "{:+#x}", decoding_table.1)?;
164 }
165 writeln!(f, ")")?;
166 }
167 for item in self.rom_items.iter() {
168 writeln!(f, "{item}")?;
169 }
170 writeln!(f, ".ram_items")?;
171 for item in self.ram_items.iter() {
172 writeln!(f, "{item}")?;
173 }
174 writeln!(f, ".zero_items")?;
175 for item in self.zero_items.iter() {
176 writeln!(f, "{item}")?;
177 }
178 Ok(())
179 }
180}
181
182fn assemble<L>(
210 rom_items: &[Item<L>],
211 ram_items: &[Item<L>],
212 zero_items: &[ZeroItem<L>],
213 stack_size: u32,
214 start_func: &LabelRef<L>,
215 decoding_table: &Option<LabelRef<L>>,
216) -> Result<BytesMut, AssemblerError<L>>
217where
218 L: Clone + Eq + Hash,
219{
220 let mut labeled: HashMap<L, u32> = HashMap::new();
221
222 let mut position = HEADER_LENGTH;
223
224 initialize_positions(rom_items, &mut labeled, &mut position)?;
226 position = checked_next_multiple_of(position, 256)?;
227 let mut ramstart = position;
228 initialize_positions(ram_items, &mut labeled, &mut position)?;
229 position = checked_next_multiple_of(position, 256)?;
230 initialize_zero_positions(zero_items, &mut labeled, &mut position)?;
231
232 loop {
234 position = HEADER_LENGTH;
235
236 let rom_improved = update_positions(rom_items, &mut labeled, &mut position, ramstart)?;
237 position = checked_next_multiple_of(position, 256)?;
238 ramstart = position;
239 let ram_improved = update_positions(ram_items, &mut labeled, &mut position, ramstart)?;
240 position = checked_next_multiple_of(position, 256)?;
241 let zero_improved = update_zero_positions(zero_items, &mut labeled, &mut position)?;
242
243 if !rom_improved && !ram_improved && !zero_improved {
244 break;
245 }
246 }
247
248 let mut body = BytesMut::new();
250 serialize_items(rom_items, &labeled, ramstart, &mut body)?;
251 assert_eq!(
252 ramstart
253 .checked_sub(HEADER_LENGTH)
254 .expect("ramstart should be >= HEADER_LENGTH"),
255 body.len().try_into().overflow()?
256 );
257 serialize_items(ram_items, &labeled, ramstart, &mut body)?;
258
259 let body = body.freeze();
260 let extstart = u32::try_from(body.len())
261 .overflow()?
262 .checked_add(HEADER_LENGTH)
263 .overflow()?;
264
265 let endmem = checked_next_multiple_of(verify_zero_items(zero_items, &labeled, extstart)?, 256)?;
266
267 let resolver = HashResolver {
268 hashmap: &labeled,
269 ramstart,
270 };
271
272 let resolved_decoding_table = if let Some(decoding_table) = decoding_table {
273 decoding_table.resolve_absolute(&resolver)?
274 } else {
275 0u32
276 };
277
278 let resolved_start_func: u32 = start_func.resolve_absolute(&resolver)?;
279
280 let sum = MAGIC_NUMBER
281 .wrapping_add(GLULX_VERSION)
282 .wrapping_add(ramstart)
283 .wrapping_add(extstart)
284 .wrapping_add(endmem)
285 .wrapping_add(stack_size)
286 .wrapping_add(resolved_start_func)
287 .wrapping_add(resolved_decoding_table)
288 .wrapping_add(checksum(body.clone()));
289
290 let mut output = BytesMut::with_capacity(
291 body.len()
292 .checked_add(
293 usize::try_from(HEADER_LENGTH).expect("u32 to usize conversion should succeed"),
294 )
295 .overflow()?,
296 );
297
298 output.put_u32(MAGIC_NUMBER);
299 output.put_u32(GLULX_VERSION);
300 output.put_u32(ramstart);
301 output.put_u32(extstart);
302 output.put_u32(endmem);
303 output.put_u32(stack_size);
304 output.put_u32(resolved_start_func);
305 output.put_u32(resolved_decoding_table);
306 output.put_u32(sum);
307 output.put(body);
308
309 Ok(output)
310}
311
312fn initialize_positions<L>(
314 items: &[Item<L>],
315 labeled: &mut HashMap<L, u32>,
316 position: &mut u32,
317) -> Result<(), AssemblerError<L>>
318where
319 L: Clone + Hash + Eq,
320{
321 for item in items {
322 let worst_len: u32 = item.worst_len().try_into().overflow()?;
323 let end_position = position.checked_add(worst_len).overflow()?;
324 if let Item::Label(label) = item {
325 if labeled.insert(label.clone(), *position).is_some() {
326 return Err(AssemblerError::DuplicateLabel(label.clone()));
327 }
328 }
329
330 *position = checked_next_multiple_of(end_position, item.align())?;
331 }
332
333 Ok(())
334}
335
336fn initialize_zero_positions<L>(
338 items: &[ZeroItem<L>],
339 labeled: &mut HashMap<L, u32>,
340 position: &mut u32,
341) -> Result<(), AssemblerError<L>>
342where
343 L: Clone + Hash + Eq,
344{
345 for item in items {
346 let end_position = position.checked_add(item.len()).overflow()?;
347 if let ZeroItem::Label(label) = item {
348 if labeled.insert(label.clone(), *position).is_some() {
349 return Err(AssemblerError::DuplicateLabel(label.clone()));
350 };
351 }
352
353 *position = checked_next_multiple_of(end_position, item.align())?;
354 }
355
356 Ok(())
357}
358
359fn update_positions<L>(
361 items: &[Item<L>],
362 labeled: &mut HashMap<L, u32>,
363 position: &mut u32,
364 ramstart: u32,
365) -> Result<bool, AssemblerError<L>>
366where
367 L: Clone + Hash + Eq,
368{
369 let mut improvement_found = false;
370 for item in items {
371 let resolver = HashResolver {
372 hashmap: labeled,
373 ramstart,
374 };
375
376 let resolved_len = item.resolved_len(*position, &resolver)?;
377 let end_position = position
378 .checked_add(u32::try_from(resolved_len).overflow()?)
379 .overflow()?;
380
381 if let Item::Label(label) = item {
382 let old_position = *labeled
383 .get(label)
384 .expect("previously-inserted label should still be in the HashMap");
385
386 if *position != old_position {
387 improvement_found = true;
388 labeled.insert(label.clone(), *position);
389 }
390 }
391
392 *position = checked_next_multiple_of(end_position, item.align())?;
393 }
394 Ok(improvement_found)
395}
396
397fn update_zero_positions<L>(
399 items: &[ZeroItem<L>],
400 labeled: &mut HashMap<L, u32>,
401 position: &mut u32,
402) -> Result<bool, AssemblerError<L>>
403where
404 L: Clone + Hash + Eq,
405{
406 let mut improvement_found = false;
407
408 for item in items {
409 let end_position = position.checked_add(item.len()).overflow()?;
410
411 if let ZeroItem::Label(label) = item {
412 let old_position = *labeled
413 .get(label)
414 .expect("previously-inserted label should still be in the HashMap");
415
416 if *position != old_position {
417 improvement_found = true;
418 labeled.insert(label.clone(), *position);
419 }
420 }
421
422 *position = checked_next_multiple_of(end_position, item.align())?;
423 }
424
425 Ok(improvement_found)
426}
427
428fn serialize_items<L>(
430 items: &[Item<L>],
431 labeled: &HashMap<L, u32>,
432 ramstart: u32,
433 buf: &mut BytesMut,
434) -> Result<(), AssemblerError<L>>
435where
436 L: Clone + Eq + Hash,
437{
438 for item in items {
439 let position = u32::try_from(buf.len())
440 .overflow()?
441 .checked_add(HEADER_LENGTH)
442 .overflow()?;
443
444 if let Item::Label(label) = item {
445 let expected_position = *labeled
446 .get(label)
447 .ok_or_else(|| AssemblerError::UndefinedLabel(label.clone()))?;
448 assert_eq!(
449 expected_position, position,
450 "label position should match previous calculation"
451 );
452 }
453
454 let resolver = HashResolver {
455 hashmap: labeled,
456 ramstart,
457 };
458
459 item.serialize(position, &resolver, &mut *buf)?;
460 }
461
462 let position = u32::try_from(buf.len())
463 .overflow()?
464 .checked_add(HEADER_LENGTH)
465 .overflow()?;
466
467 let page_offset = position % 256;
468 let padding = usize::try_from(if page_offset == 0 {
469 0
470 } else {
471 256 - page_offset
472 })
473 .expect("u32 to usize conversion should succeed");
474 buf.put_bytes(0, padding);
475
476 Ok(())
477}
478
479fn verify_zero_items<L>(
481 items: &[ZeroItem<L>],
482 labeled: &HashMap<L, u32>,
483 extstart: u32,
484) -> Result<u32, AssemblerError<L>>
485where
486 L: Clone + Eq + Hash,
487{
488 let mut position = extstart;
489
490 for item in items {
491 if let ZeroItem::Label(label) = item {
492 let expected_position = *labeled
493 .get(label)
494 .ok_or_else(|| AssemblerError::UndefinedLabel(label.clone()))?;
495 assert_eq!(
496 position, expected_position,
497 "label position should match previous calculation"
498 );
499 }
500
501 position = position.checked_add(item.len()).overflow()?;
502 position = checked_next_multiple_of(position, item.align())?;
503 }
504
505 Ok(position)
506}
507
508fn checksum(mut bytes: Bytes) -> u32 {
510 let mut sum: u32 = 0;
511 while bytes.has_remaining() {
512 sum = sum.wrapping_add(bytes.get_u32());
513 }
514 sum
515}