use core::fmt;
use alloc::sync::Arc;
use crate::{
addresses::{Rom, Vram},
config::{Compiler, GlobalConfig},
metadata::{
AutogeneratedPadInfo, LabelMetadata, LabelType, SegmentMetadata, SymbolMetadata, SymbolType,
},
section_type::SectionType,
};
pub(crate) enum WordComment {
No,
U32([u8; 4]),
U64([u8; 8]),
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct SymCommonDisplaySettings {
line_end: Option<Arc<str>>,
emit_asm_comment: bool,
asm_indentation: u8,
rom_comment_width: u8,
}
impl SymCommonDisplaySettings {
#[must_use]
pub fn new() -> Self {
Self {
line_end: None,
emit_asm_comment: true,
asm_indentation: 4,
rom_comment_width: 6,
}
}
#[must_use]
pub fn line_end(&self) -> &str {
if let Some(line_end) = &self.line_end {
line_end
} else {
"\n"
}
}
}
impl SymCommonDisplaySettings {
pub fn set_rom_comment_width(&mut self, rom_comment_width: u8) {
self.rom_comment_width = rom_comment_width;
}
}
impl SymCommonDisplaySettings {
pub fn display_sym_property_comments(
&self,
f: &mut fmt::Formatter<'_>,
metadata: &SymbolMetadata,
_owned_segment: &SegmentMetadata,
) -> fmt::Result {
if !self.emit_asm_comment {
return Ok(());
}
if false {
write!(
f,
"/* all_access_types: {:?} */{}",
metadata.all_access_types(),
self.line_end()
)?;
write!(
f,
"/* user_declared_type: {:?} */{}",
metadata.user_declared_type(),
self.line_end()
)?;
write!(
f,
"/* autodetected_type: {:?} */{}",
metadata.autodetected_type(),
self.line_end()
)?;
write!(
f,
"/* generated_by: {:?} */{}",
metadata.generated_by(),
self.line_end()
)?;
write!(
f,
"/* sym_referrers: {:?} */{}",
metadata.sym_referrers(),
self.line_end()
)?;
write!(
f,
"/* autogenerated_pad_info: {:?} */{}",
metadata.autogenerated_pad_info(),
self.line_end()
)?;
}
if false {
write!(f, "/* static variable */{}", self.line_end())?;
}
if let Some(info) = metadata.autogenerated_pad_info() {
write!(f, "/* Automatically generated and unreferenced pad ",)?;
if false {
match info {
AutogeneratedPadInfo::Unreferenced => {}
AutogeneratedPadInfo::CreatedBy(vram) => {
write!(f, "(generated by {vram}) ")?;
}
}
}
write!(f, "*/{}", self.line_end())?;
}
Ok(())
}
pub fn display_symbol_name(
&self,
f: &mut fmt::Formatter<'_>,
global_config: &GlobalConfig,
metadata: &SymbolMetadata,
in_middle: bool,
section_type: Option<SectionType>,
) -> fmt::Result {
let sym_name = metadata.display_name();
if let Some(macro_labels) = global_config.macro_labels() {
match metadata.sym_type() {
Some(SymbolType::Function) => {
if in_middle {
write!(f, "{}", macro_labels.alt_func())?;
} else {
write!(f, "{}", macro_labels.func())?;
}
}
Some(
SymbolType::Jumptable
| SymbolType::GccExceptTable
| SymbolType::Byte
| SymbolType::Short
| SymbolType::Word
| SymbolType::DWord
| SymbolType::Float32
| SymbolType::Float64
| SymbolType::CString
| SymbolType::VirtualTable
| SymbolType::UserCustom,
)
| None => match section_type {
Some(SectionType::Text) if in_middle => {
write!(f, "{}", macro_labels.alt_func())?
}
_ => write!(f, "{}", macro_labels.data())?,
},
}
write!(f, " {sym_name}")?;
match metadata.visibility().as_deref() {
None | Some("global") | Some("globl") => {}
Some(vis) => write!(f, ", {vis}")?,
}
} else {
let visibility = metadata.visibility();
let vis = match visibility.as_deref() {
None | Some("globl") => "globl",
Some(vis) => vis,
};
write!(f, ".{} {}{}", vis, sym_name, self.line_end())?;
match metadata.sym_type() {
Some(SymbolType::Function) => {
write!(f, ".type {}, @function{}", sym_name, self.line_end())?;
if in_middle {
write!(f, ".aent {}{}", sym_name, self.line_end())?;
} else {
write!(f, ".ent {}{}", sym_name, self.line_end())?;
}
}
Some(SymbolType::Jumptable | SymbolType::GccExceptTable) => {}
Some(
SymbolType::Byte
| SymbolType::Short
| SymbolType::Word
| SymbolType::DWord
| SymbolType::Float32
| SymbolType::Float64
| SymbolType::CString
| SymbolType::VirtualTable
| SymbolType::UserCustom,
)
| None => match section_type {
Some(SectionType::Text) if in_middle => {
write!(f, ".aent {}{}", sym_name, self.line_end())?
}
_ => write!(f, ".type {}, @object{}", sym_name, self.line_end())?,
},
}
write!(f, "{sym_name}:")?;
}
write!(f, "{}", self.line_end())
}
pub fn display_label(
&self,
f: &mut fmt::Formatter<'_>,
global_config: &GlobalConfig,
metadata: &LabelMetadata,
) -> fmt::Result {
let sym_name = metadata.display_name();
if let Some(macro_labels) = global_config.macro_labels() {
match metadata.label_type() {
LabelType::Jumptable => write!(f, "{}", macro_labels.jtbl_label())?,
LabelType::GccExceptTable => write!(f, "{}", macro_labels.ehtbl_label())?,
LabelType::AlternativeEntry => write!(f, "{}", macro_labels.alt_func())?,
LabelType::Branch => write!(f, "{}", macro_labels.data())?,
}
write!(f, " {sym_name}")?;
match metadata.visibility().as_deref() {
None | Some("global") | Some("globl") => {}
Some(vis) => write!(f, ", {vis}")?,
}
} else {
let visibility = metadata.visibility();
let vis = match visibility.as_deref() {
Some(vis) => Some(vis),
None => match metadata.label_type() {
LabelType::Jumptable
| LabelType::GccExceptTable
| LabelType::AlternativeEntry => Some("globl"),
LabelType::Branch => None,
},
};
if let Some(vis) = vis {
write!(f, ".{} {}{}", vis, sym_name, self.line_end())?;
}
write!(f, "{sym_name}:")?;
}
write!(f, "{}", self.line_end())
}
pub(crate) fn display_alignment_directive(
&self,
f: &mut fmt::Formatter<'_>,
metadata: &SymbolMetadata,
compiler: Compiler,
shift_value: Option<u8>,
) -> fmt::Result {
let shift_value = if let Some(shift_value) = shift_value {
shift_value
} else {
return Ok(());
};
let parent_metadata = if let Some(parent_metadata) = metadata.parent_metadata() {
parent_metadata
} else {
return Ok(());
};
let shifted_val = 1u32 << shift_value;
let sub_ram = if compiler.symbol_alignment_requires_aligned_section() {
if parent_metadata.vram().inner() % shifted_val != 0 {
return Ok(());
}
metadata.vram().inner()
} else {
(metadata.vram() - parent_metadata.vram()).inner() as u32
};
if sub_ram % shifted_val != 0 {
return Ok(());
}
write!(f, ".align {}{}", shift_value, self.line_end())
}
pub fn display_sym_prev_alignment(
&self,
f: &mut fmt::Formatter<'_>,
metadata: &SymbolMetadata,
) -> fmt::Result {
if let Some(compiler) = metadata.compiler() {
if let Some(sym_type) = metadata.sym_type() {
self.display_alignment_directive(
f,
metadata,
compiler,
compiler.prev_align_for_type(sym_type),
)?;
}
}
Ok(())
}
pub fn display_sym_end(
&self,
f: &mut fmt::Formatter<'_>,
global_config: &GlobalConfig,
metadata: &SymbolMetadata,
) -> fmt::Result {
let sym_name = metadata.display_name();
if let Some(macro_labels) = global_config.macro_labels() {
if let Some(sym_type) = metadata.sym_type() {
match sym_type {
SymbolType::Function => {
if let Some(func_end) = macro_labels.func_end() {
write!(f, "{} {}{}", func_end, sym_name, self.line_end())?;
}
}
SymbolType::Jumptable
| SymbolType::GccExceptTable
| SymbolType::Byte
| SymbolType::Short
| SymbolType::Word
| SymbolType::DWord
| SymbolType::Float32
| SymbolType::Float64
| SymbolType::CString
| SymbolType::VirtualTable
| SymbolType::UserCustom => {
if let Some(data_end) = macro_labels.data_end() {
write!(f, "{} {}{}", data_end, sym_name, self.line_end())?;
}
}
}
} else if let Some(data_end) = macro_labels.data_end() {
write!(f, "{} {}{}", data_end, sym_name, self.line_end())?;
}
} else if let Some(SymbolType::Function) = metadata.sym_type() {
write!(f, ".end {}{}", sym_name, self.line_end())?;
}
if global_config.emit_size_directive() {
write!(f, ".size {}, . - {}{}", sym_name, sym_name, self.line_end())?;
}
Ok(())
}
pub fn display_asm_indendation(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.asm_indentation > 0 {
write!(f, "{:width$}", " ", width = self.asm_indentation as usize)
} else {
Ok(())
}
}
pub fn display_asm_comment(
&self,
f: &mut fmt::Formatter<'_>,
rom: Option<Rom>,
vram: Vram,
word_comment: WordComment,
) -> fmt::Result {
self.display_asm_indendation(f)?;
if !self.emit_asm_comment {
return Ok(());
}
write!(f, "/* ")?;
if let Some(rom) = rom {
write!(
f,
"{:0width$X} ",
rom.inner(),
width = self.rom_comment_width as usize
)?;
}
write!(f, "{vram} ")?;
match word_comment {
WordComment::No => {}
WordComment::U32(arr) => Self::display_byte_array(f, arr)?,
WordComment::U64(arr) => Self::display_byte_array(f, arr)?,
}
write!(f, "*/ ")
}
fn display_byte_array<const LENGTH: usize>(
f: &mut fmt::Formatter<'_>,
arr: [u8; LENGTH],
) -> fmt::Result {
for x in arr {
write!(f, "{x:02X}")?;
}
write!(f, " ")?;
Ok(())
}
}