use crate::{DecodeOptions, DecodeResult, decode};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ParseMode {
Instruction,
Data,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ParseEndian {
Big,
Little,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum Data {
Byte(u8),
Word(u16),
Long(u32),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ParsedValue {
Instruction {
word: u16,
result: DecodeResult,
},
Data(Data),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct ParsedItem {
pub offset: usize,
pub address: u32,
pub size: u8,
pub value: ParsedValue,
}
#[derive(Clone, Debug)]
pub struct Parser<'a> {
bytes: &'a [u8],
options: DecodeOptions,
mode: ParseMode,
endian: ParseEndian,
base_address: u32,
offset: usize,
}
impl<'a> Parser<'a> {
pub const fn new(
bytes: &'a [u8],
mode: ParseMode,
endian: ParseEndian,
options: DecodeOptions,
) -> Self {
Self { bytes, options, mode, endian, base_address: 0, offset: 0 }
}
pub const fn mode(&self) -> ParseMode {
self.mode
}
pub fn set_mode(&mut self, mode: ParseMode) {
self.mode = mode;
}
pub const fn endian(&self) -> ParseEndian {
self.endian
}
pub fn set_endian(&mut self, endian: ParseEndian) {
self.endian = endian;
}
pub const fn offset(&self) -> usize {
self.offset
}
pub fn address(&self) -> u32 {
self.base_address.wrapping_add(self.offset as u32)
}
pub fn set_address(&mut self, address: u32) {
self.base_address = address.wrapping_sub(self.offset as u32);
}
pub fn goto_offset(&mut self, offset: usize) {
self.offset = offset.min(self.bytes.len());
}
pub fn jump(&mut self, delta: isize) {
self.offset = self.offset.saturating_add_signed(delta).min(self.bytes.len());
}
fn read_u16(&self) -> Option<u16> {
let bytes = self.bytes.get(self.offset..self.offset + 2)?;
Some(match self.endian {
ParseEndian::Big => u16::from_be_bytes([bytes[0], bytes[1]]),
ParseEndian::Little => u16::from_le_bytes([bytes[0], bytes[1]]),
})
}
fn read_u32(&self) -> Option<u32> {
let bytes = self.bytes.get(self.offset..self.offset + 4)?;
Some(match self.endian {
ParseEndian::Big => u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
ParseEndian::Little => u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
})
}
fn item(&mut self, size: u8, value: ParsedValue) -> ParsedItem {
let item = ParsedItem { offset: self.offset, address: self.address(), size, value };
self.offset += usize::from(size);
item
}
}
impl Iterator for Parser<'_> {
type Item = ParsedItem;
fn next(&mut self) -> Option<Self::Item> {
if self.offset >= self.bytes.len() {
return None;
}
match self.mode {
ParseMode::Instruction if self.offset + 2 <= self.bytes.len() => {
let word = self.read_u16()?;
let result = decode(word, &self.options);
Some(self.item(2, ParsedValue::Instruction { word, result }))
}
ParseMode::Instruction => {
let value = self.bytes[self.offset];
Some(self.item(1, ParsedValue::Data(Data::Byte(value))))
}
ParseMode::Data if self.address() & 3 == 0 && self.offset + 4 <= self.bytes.len() => {
let value = self.read_u32()?;
Some(self.item(4, ParsedValue::Data(Data::Long(value))))
}
ParseMode::Data if self.address() & 1 == 0 && self.offset + 2 <= self.bytes.len() => {
let value = self.read_u16()?;
Some(self.item(2, ParsedValue::Data(Data::Word(value))))
}
ParseMode::Data => {
let value = self.bytes[self.offset];
Some(self.item(1, ParsedValue::Data(Data::Byte(value))))
}
}
}
}