use std::collections::BTreeMap;
use std::sync::Arc;
#[allow(unused)]
use flexstr::{IntoSharedStr, SharedStr, ToSharedStr, shared_str};
use crate::assembler::sample_table::SampleTable;
use crate::directive::DirectiveParameterTable;
use crate::sema::instruction::MemoryAddress;
use crate::{AssemblyCode, AssemblyError, pretty_hex};
#[derive(Debug, Clone)]
pub struct Segments<Contained> {
pub segments: BTreeMap<MemoryAddress, Vec<Contained>>,
pub current_segment_start: Option<MemoryAddress>,
pub segment_stack: Vec<MemoryAddress>,
pub sample_table: SampleTable,
pub directive_parameters: Box<DirectiveParameterTable>,
}
#[allow(clippy::result_unit_err)]
impl<Contained> Segments<Contained> {
pub fn push_segment(&mut self) -> Result<(), ()> {
self.segment_stack.push(self.current_segment_start.ok_or(())?);
self.current_segment_start = None;
Ok(())
}
pub fn pop_segment(&mut self) -> Result<(), ()> {
if self.segment_stack.is_empty() {
return Err(());
}
self.current_segment_start = self.segment_stack.pop();
Ok(())
}
#[inline]
pub fn new_segment(&mut self, segment_start: MemoryAddress) -> &mut Self {
self.segments.insert(segment_start, Vec::new());
self.current_segment_start = Some(segment_start);
self
}
#[inline]
#[allow(clippy::missing_panics_doc)]
pub fn current_location(&self) -> Result<MemoryAddress, ()> {
Ok(MemoryAddress::try_from(self.segments[&self.current_segment_start.ok_or(())?].len()).map_err(|_| ())?
+ self.current_segment_start.unwrap())
}
#[inline]
#[allow(clippy::missing_panics_doc)]
pub fn current_offset(&self) -> Result<MemoryAddress, ()> {
MemoryAddress::try_from(self.segments[&self.current_segment_start.ok_or(())?].len()).map_err(|_| ())
}
#[inline]
#[allow(clippy::missing_panics_doc)]
pub fn current_segment_mut(&mut self) -> Result<&mut Vec<Contained>, ()> {
Ok(self.segments.get_mut(&self.current_segment_start.ok_or(())?).unwrap())
}
pub fn add_element(&mut self, element: Contained) -> Result<(), ()> {
self.current_segment_mut().map(|segment| segment.push(element))
}
pub fn try_map_segments<Output, Error>(
self,
map: impl Fn(MemoryAddress, Vec<Contained>) -> Result<Vec<Output>, Error>,
) -> Result<Segments<Output>, Error> {
Ok(Segments::<Output> {
current_segment_start: self.current_segment_start,
segment_stack: self.segment_stack,
sample_table: self.sample_table,
directive_parameters: self.directive_parameters,
segments: self
.segments
.into_iter()
.map(|(address, contents)| -> Result<(MemoryAddress, Vec<Output>), Error> {
Ok((address, map(address, contents)?))
})
.try_collect()?,
})
}
}
impl<Contained> Default for Segments<Contained> {
fn default() -> Self {
Self {
segments: BTreeMap::default(),
current_segment_start: None,
segment_stack: Vec::default(),
sample_table: SampleTable::default(),
directive_parameters: DirectiveParameterTable::default().into(),
}
}
}
impl Segments<u8> {
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
pub fn flatten(&self, source_code: &Arc<AssemblyCode>) -> Result<Vec<u8>, Box<AssemblyError>> {
let mut all_data = Vec::new();
for (starting_address, segment_data) in &self.segments {
if *starting_address < all_data.len() as MemoryAddress {
return Err(AssemblyError::SegmentMismatch {
src: Arc::new(AssemblyCode {
text: pretty_hex(&all_data, Some(*starting_address as usize)),
name: source_code.name.clone(),
include_path: Vec::new(),
}),
location: (*starting_address as usize * 3 + 1, 2).into(),
segment_start: *starting_address,
segment_end: all_data.len() as MemoryAddress,
}
.into());
}
all_data.resize(*starting_address as usize, 0);
all_data.extend_from_slice(segment_data);
}
Ok(all_data)
}
}