1use std::fmt::{Debug, Display};
2use std::io::Write;
3use std::ops::Deref;
4use std::sync::{Arc, RwLock};
5
6use cpclib_common::itertools::Itertools;
7use cpclib_common::smallvec::SmallVec;
8use cpclib_tokens::ExprResult;
9use cpclib_tokens::symbols::{MemoryPhysicalAddress, PhysicalAddress};
10
11use crate::preamble::{LocatedToken, LocatedTokenInner, MayHaveSpan, SourceString};
12#[derive(PartialEq)]
16pub enum TokenKind {
17 Hidden,
18 Label(String),
19 Set(String),
20 MacroCall,
21 MacroDefine(String),
22 Displayable
23}
24
25impl TokenKind {
26 fn is_displayable(&self) -> bool {
27 self == &TokenKind::Displayable
28 }
29}
30pub struct ListingOutput {
31 writer: Box<dyn Write + Send + Sync>,
34 current_fname: Option<String>,
36 activated: bool,
37
38 current_line_bytes: SmallVec<[u8; 4]>,
40 current_source: Option<&'static str>,
42 current_line_group: Option<(u32, String)>, current_first_address: u32,
46 current_address_kind: AddressKind,
47 current_physical_address: PhysicalAddress,
48 crunched_section_counter: usize,
49 current_token_kind: TokenKind,
50 deferred_for_line: Vec<String>,
51 counter_update: Vec<String>
52}
53#[derive(PartialEq)]
54pub enum AddressKind {
55 Address,
56 CrunchedArea,
57 Mixed,
58 None
59}
60
61impl Display for AddressKind {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 write!(
64 f,
65 "{}",
66 match self {
67 AddressKind::Address => ' ',
68 AddressKind::CrunchedArea => 'C',
69 AddressKind::Mixed => 'M',
70 AddressKind::None => 'N'
71 }
72 )
73 }
74}
75
76impl Debug for ListingOutput {
77 fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 Ok(())
79 }
80}
81
82impl ListingOutput {
83 pub fn new<W: 'static + Write + Send + Sync>(writer: W) -> Self {
85 Self {
86 writer: Box::new(writer),
87 current_fname: None,
88 activated: false,
89 current_line_bytes: Default::default(),
90 current_line_group: None,
91 current_source: None,
92 current_first_address: 0,
93 current_address_kind: AddressKind::None,
94 crunched_section_counter: 0,
95 current_physical_address: MemoryPhysicalAddress::new(0, 0).into(),
96 current_token_kind: TokenKind::Hidden,
97 deferred_for_line: Default::default(),
98 counter_update: Vec::new()
99 }
100 }
101
102 fn bytes_per_line(&self) -> usize {
103 8
104 }
105
106 fn token_is_on_same_source(&self, token: &LocatedToken) -> bool {
108 match &self.current_source {
109 Some(current_source) => {
110 std::ptr::eq(token.context().source.as_ptr(), current_source.as_ptr())
111 },
112 None => false
113 }
114 }
115
116 fn token_is_on_same_line(&self, token: &LocatedToken) -> bool {
118 match &self.current_line_group {
119 Some((current_location, _current_line)) => {
120 self.token_is_on_same_source(token)
121 && *current_location == token.span().location_line()
122 },
123 None => false
124 }
125 }
126
127 fn extract_code(token: &LocatedToken) -> String {
128 match token {
129 LocatedToken {
130 inner: either::Left(LocatedTokenInner::Macro { .. } | LocatedTokenInner::Repeat(..)),
131 span,
132 ..
133 } => {
134 span.as_str().to_string()
136 },
137
138 _ => {
139 unsafe {
141 std::str::from_utf8_unchecked(token.span().get_line_beginning().as_bytes())
142 }
143 .to_owned()
144 }
145 }
146 }
147
148 fn add_token(
150 &mut self,
151 token: &LocatedToken,
152 bytes: &[u8],
153 address: u32,
154 address_kind: AddressKind,
155 physical_address: PhysicalAddress
156 ) {
157 if !self.activated {
158 return;
159 }
160
161 let fname_handling = self.manage_fname(token);
164
165 let specific_content = match &self.current_token_kind {
167 TokenKind::Hidden => None,
168 TokenKind::Label(l) => {
169 Some(format!(
170 "{:04X} {:05X} {l}",
171 self.current_first_address,
172 match self.current_physical_address {
173 PhysicalAddress::Memory(adr) => adr.offset_in_cpc(),
174 PhysicalAddress::Bank(adr) => adr.address() as _,
175 PhysicalAddress::Cpr(adr) => adr.address() as _
176 }
177 ))
178 },
179 TokenKind::Set(label) => {
180 Some(format!(
181 "{:04X} {} {label}",
182 self.current_first_address, "?????"
183 ))
184 },
185 TokenKind::MacroCall | TokenKind::Displayable => None,
186 TokenKind::MacroDefine(name) => Some(format!("MACRO {name}"))
187 };
188
189 if let Some(specific_content) = &specific_content {
191 self.deferred_for_line.push(specific_content.clone());
192 }
193
194 {
195 if true {
197 if !self.token_is_on_same_line(token) {
202 self.process_current_line(); }
204
205 self.current_source =
209 Some(unsafe { std::mem::transmute(token.context().complete_source()) });
210
211 self.current_line_group =
214 Some((token.span().location_line(), Self::extract_code(token)));
215 self.current_first_address = address;
216 self.current_physical_address = physical_address;
217 self.current_address_kind = AddressKind::None;
218 self.manage_fname(token);
219 }
220 else {
221 self.current_line_group =
223 Some((token.span().location_line(), Self::extract_code(token)));
224 }
225 }
226
227 self.current_line_bytes.extend_from_slice(bytes);
228 self.current_address_kind = if self.current_address_kind == AddressKind::None {
229 address_kind
230 }
231 else if self.current_address_kind != address_kind {
232 AddressKind::Mixed
233 }
234 else {
235 address_kind
236 };
237
238 if let Some(line) = fname_handling {
239 writeln!(self.writer, "{}", line).unwrap();
240 }
241
242 self.current_token_kind = match token.deref() {
243 LocatedTokenInner::Label(l) => TokenKind::Label(l.to_string()),
244 LocatedTokenInner::Equ { label, .. } | LocatedTokenInner::Assign { label, .. } => {
245 TokenKind::Set(label.to_string())
246 },
247 LocatedTokenInner::Macro { name, .. } => TokenKind::MacroDefine(name.to_string()),
248 LocatedTokenInner::MacroCall(..)
249 | LocatedTokenInner::Org { .. }
250 | LocatedTokenInner::Comment(..)
251 | LocatedTokenInner::Include(..)
252 | LocatedTokenInner::Repeat(..) => TokenKind::Displayable,
253 _ => TokenKind::Hidden
254 };
255 }
256
257 pub fn process_current_line(&mut self) {
258 let (line_number, line) = match &self.current_line_group {
260 Some((idx, line)) => (idx, line),
261 None => return
262 };
263
264 let mut line_representation = line.split("\n");
266 let data_representation = &self
267 .current_line_bytes
268 .iter()
269 .chunks(self.bytes_per_line())
270 .into_iter()
271 .map(|c| c.map(|b| format!("{:02X}", b)).join(" "))
272 .collect_vec();
273 let mut data_representation = data_representation.iter();
274
275 let delta = line_representation.clone().count();
278 for specific_content in self.deferred_for_line.iter() {
280 let lines = line.split("\n");
281 let lines_count = lines.clone().count(); for (line_delta, line) in lines.into_iter().enumerate() {
283 writeln!(
284 self.writer,
285 "{:37}{:4} {}",
286 if line_delta == 0 {
287 specific_content
288 }
289 else {
290 ""
291 },
292 line_number + delta as u32 + line_delta as u32 - lines_count as u32,
293 line
294 )
295 .unwrap();
296 }
297 }
298 self.deferred_for_line.clear();
299
300 let mut idx = 0;
302 loop {
303 let current_inner_line = line_representation.next();
304 let current_inner_data = data_representation.next();
305
306 if current_inner_data.is_none() && current_inner_line.is_none() {
307 break;
308 }
309
310 let loc_representation = if current_inner_line.is_none() {
311 " ".to_owned()
312 }
313 else {
314 format!("{:04X}", self.current_first_address)
315 };
316
317 let offset = match self.current_physical_address {
319 PhysicalAddress::Memory(adr) => adr.offset_in_cpc(),
320 PhysicalAddress::Bank(adr) => adr.address() as _,
321 PhysicalAddress::Cpr(adr) => adr.address() as _
322 };
323 let phys_addr_representation =
324 if current_inner_line.is_none() || offset == self.current_first_address {
325 " ".to_owned()
326 }
327 else {
328 format!("{:05X}{}", offset, self.current_address_kind)
329 };
330
331 let line_nb_representation = if current_inner_line.is_none() {
332 " ".to_owned()
333 }
334 else {
335 format!("{:4}", line_number + idx)
336 };
337
338 if !self.current_line_bytes.is_empty() || self.current_token_kind.is_displayable() {
340 writeln!(
341 self.writer,
342 "{loc_representation} {phys_addr_representation} {:bytes_width$} {line_nb_representation} {}",
343 current_inner_data.unwrap_or(&"".to_owned()),
344 current_inner_line.map(|line| line.trim_end()).unwrap_or(""),
345 bytes_width = self.bytes_per_line() * 3
346 )
347 .unwrap();
348 }
349
350 idx += 1;
351 }
352
353 if !self.current_line_bytes.is_empty() || self.current_token_kind.is_displayable() {
354 for counter in self.counter_update.iter() {
355 self.writer
356 .write(format!("{}\n", counter).as_bytes())
357 .unwrap();
358 }
359 self.counter_update.clear();
360 }
361
362 self.current_line_group = None;
364 self.current_source = None;
365 self.current_line_bytes.clear();
366 }
367
368 pub fn finish(&mut self) {
369 self.process_current_line();
370 if !self.deferred_for_line.is_empty() {
371 panic!()
372 }
373 }
374
375 pub fn manage_fname(&mut self, token: &LocatedToken) -> Option<String> {
377 let ctx = &token.span().state;
380 let fname = ctx
381 .filename()
382 .map(|p| p.as_os_str().to_str().unwrap_or("<NO FNAME>").to_string())
383 .or_else(|| ctx.context_name().map(|s| s.to_owned()));
384
385 match fname {
386 Some(fname) => {
387 let print = match self.current_fname.as_ref() {
388 Some(current_fname) => *current_fname != fname,
389 None => true
390 };
391
392 if print {
393 self.current_fname = Some(fname.clone());
394 Some(format!("Context: {}", fname))
395 }
396 else {
397 None
398 }
399 },
400 None => None
401 }
402 }
403
404 pub fn on(&mut self) {
405 self.activated = true;
406 }
407
408 pub fn off(&mut self) {
409 self.finish();
410 self.activated = false;
411 }
412
413 pub fn enter_crunched_section(&mut self) {
414 self.crunched_section_counter += 1;
415 }
416
417 pub fn leave_crunched_section(&mut self) {
418 self.crunched_section_counter -= 1;
419 }
420}
421
422#[derive(Clone)]
424pub struct ListingOutputTrigger {
425 pub(crate) token: Option<*const LocatedToken>,
428 pub(crate) bytes: Vec<u8>,
430 pub(crate) start: u32,
431 pub(crate) physical_address: PhysicalAddress,
432 pub(crate) builder: Arc<RwLock<ListingOutput>>
433}
434
435unsafe impl Sync for ListingOutputTrigger {}
436
437impl ListingOutputTrigger {
438 pub fn write_byte(&mut self, b: u8) {
439 self.bytes.push(b);
440 }
441
442 pub fn new_token(
443 &mut self,
444 new: *const LocatedToken,
445 code: u32,
446 kind: AddressKind,
447 physical_address: PhysicalAddress
448 ) {
449 if let Some(token) = &self.token {
451 self.builder.write().unwrap().add_token(
452 unsafe { &**token },
453 &self.bytes,
454 self.start,
455 kind,
456 self.physical_address
457 );
458 }
459
460 self.token.replace(new); self.bytes.clear();
464 self.start = code;
465 self.physical_address = physical_address;
466 }
467
468 pub fn replace_code_address(&mut self, address: &ExprResult) {
471 Self::result_to_address(address).map(|a| self.start = a);
472 }
473
474 fn result_to_address(address: &ExprResult) -> Option<u32> {
476 match address {
477 ExprResult::Float(_f) => None,
478 ExprResult::Value(v) => Some(*v as _),
479 ExprResult::Char(v) => Some(*v as _),
480 ExprResult::Bool(b) => Some(if *b { 1 } else { 0 }),
481 ExprResult::String(s) => Some(s.len() as _),
482 ExprResult::List(l) => Some(l.len() as _),
483 ExprResult::Matrix {
484 width,
485 height,
486 content: _
487 } => Some((*width * *height) as _)
488 }
489 }
490
491 pub fn replace_physical_address(&mut self, address: PhysicalAddress) {
492 self.physical_address = address;
493 }
494
495 pub fn finish(&mut self) {
496 if let Some(token) = &self.token {
497 self.builder.write().unwrap().add_token(
498 unsafe { &**token },
499 &self.bytes,
500 self.start,
501 AddressKind::Address,
502 self.physical_address
503 );
504 }
505 self.builder.write().unwrap().finish();
506 }
507
508 pub fn on(&mut self) {
509 self.builder.write().unwrap().on();
510 }
511
512 pub fn off(&mut self) {
513 self.builder.write().unwrap().off();
514 }
515
516 pub fn enter_crunched_section(&mut self) {
517 self.builder.write().unwrap().enter_crunched_section();
518 }
519
520 pub fn leave_crunched_section(&mut self) {
521 self.builder.write().unwrap().leave_crunched_section();
522 }
523
524 pub fn repeat_iteration(&mut self, counter: &str, value: Option<&ExprResult>) {
525 let line = if let Some(value) = value {
526 let value = Self::result_to_address(value);
527 if let Some(value) = value {
528 format!("{value:04X} ????? {counter}")
529 }
530 else {
531 format!("???? ????? {counter}")
532 }
533 }
534 else {
535 format!("???? ???? {counter}")
536 };
537
538 self.builder.write().unwrap().counter_update.push(line);
539 }
540}