use crate::error::{AssemblerError, Result};
use std::io::Write;
pub struct Rom {
data: Vec<u8>,
position: u16,
size: usize,
source: Option<String>,
path: Option<String>,
length: usize,
}
impl Rom {
pub fn new() -> Self {
Self {
data: vec![0; 0x10000],
position: 0x0100, size: 0,
source: None,
path: None,
length: 0x0100,
}
}
pub fn set_source(&mut self, source: Option<String>) {
self.source = source;
}
pub fn get_source_line(&self, line: Option<usize>) -> String {
if let Some(src) = &self.source {
match line {
Some(line) if line > 0 => src.lines().nth(line - 1).map(|s| s.to_string()).unwrap_or_default(),
None => src.clone(),
_ => String::new(),
}
} else {
String::new()
}
}
pub fn source(&self) -> Option<&String> {
self.source.as_ref()
}
pub fn source_path(&self) -> Option<&String> {
self.path.as_ref()
}
pub fn set_path(&mut self, path: Option<String>) {
self.path = path;
}
pub fn write_byte(&mut self, byte: u8) -> Result<()> {
if self.position as usize >= 65536 {
return Err(AssemblerError::RomTooLarge {
size: self.position as usize + 1,
});
}
self.data[self.position as usize] = byte;
self.position += 1;
self.size = self.size.max(self.position as usize);
if byte != 0 {
self.length = self.length.max(self.position as usize);
}
Ok(())
}
pub fn write_short(&mut self, value: u16) -> Result<()> {
self.write_byte((value >> 8) as u8)?;
self.write_byte((value & 0xff) as u8)?;
Ok(())
}
pub fn write_short_at(&mut self, address: u16, value: u16) -> Result<()> {
self.write_byte_at(address, (value >> 8) as u8)?; self.write_byte_at(address + 1, (value & 0xff) as u8)?; Ok(())
}
pub fn pad_to(&mut self, address: u16) -> Result<()> {
if address as usize > 65536 {
return Err(AssemblerError::InvalidPadding { address });
}
self.position = address;
self.size = self.size.max(address as usize);
Ok(())
}
pub fn position(&self) -> u16 {
self.position
}
pub fn set_position(&mut self, position: u16) -> Result<()> {
if position as usize > 65536 {
return Err(AssemblerError::InvalidPadding { address: position });
}
self.position = position;
self.size = self.size.max(position as usize);
Ok(())
}
pub fn data(&self) -> &[u8] {
let start = 0x0100;
let end = self.length.clamp(start, self.data.len());
if end <= start || end > self.data.len() {
&[]
} else {
&self.data[start..end]
}
}
pub fn has_zero_page_data(&self) -> bool {
if self.data.len() < 0x0100 {
return false;
}
self.data[..0x0100].iter().any(|&b| b != 0)
}
pub fn len(&self) -> usize {
let start = 0x0100;
let end = self.length.clamp(start, self.data.len());
end.saturating_sub(start)
}
pub fn is_empty(&self) -> bool {
self.length <= 0x0100
}
pub fn save_to_file<P: AsRef<std::path::Path>>(&self, path: P) -> Result<()> {
let mut file = std::fs::File::create(path)?;
file.write_all(self.data())?;
Ok(())
}
pub fn write_byte_at(&mut self, position: u16, byte: u8) -> Result<()> {
self.data[position as usize] = byte;
if position as usize >= self.size {
self.size = position as usize + 1;
}
if byte != 0 {
self.length = self.length.max(position as usize + 1);
}
Ok(())
}
pub fn advance_position(&mut self, bytes: usize) {
self.position = self.position.saturating_add(bytes as u16);
self.size = self.size.max(self.position as usize);
}
}
impl Default for Rom {
fn default() -> Self {
Self::new()
}
}