xo65 0.1.0

Library for parsing cc65 object files (xo65 format)
Documentation
use crate::parse::{parse_bail, parse_uleb128_at, ParseAt, ParseResult};
use crate::xo65::Xo65Bytes;

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Expr {
    Null,
    Literal { value: i64 },
    Symbol { import_idx: u32 },
    Section { section_idx: u32 },
    Unary(Box<ExprUnary>),
    Binary(Box<ExprBinary>),
}

impl Expr {
    pub(crate) fn literal(value: i64) -> Self {
        Self::Literal { value }
    }

    pub(crate) fn symbol(import_idx: u32) -> Self {
        Self::Symbol { import_idx }
    }

    pub(crate) fn section(section_idx: u32) -> Self {
        Self::Section { section_idx }
    }

    pub(crate) fn unary(op: OpUnary, expr: Expr) -> Self {
        Self::Unary(Box::new(ExprUnary { op, expr }))
    }

    pub(crate) fn binary(op: OpBinary, lhs: Expr, rhs: Expr) -> Self {
        Self::Binary(Box::new(ExprBinary { op, lhs, rhs }))
    }
}

impl std::fmt::Display for Expr {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Null => write!(f, "<NULL>"),
            Self::Literal { value } => write!(f, "{value:#X}"),
            Self::Symbol { import_idx } => write!(f, "(Symbol {import_idx})"),
            Self::Section { section_idx } => write!(f, "(Section {section_idx})"),
            Self::Unary(unary) => unary.fmt(f),
            Self::Binary(binary) => binary.fmt(f),
        }
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ExprUnary {
    pub op: OpUnary,
    pub expr: Expr,
}

impl std::fmt::Display for ExprUnary {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "({} {})", self.op, self.expr)
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum OpUnary {
    Neg,
    BitNot,
    U16SwapBytes,
    Not,
    Bank,
    Byte0,
    Byte1,
    Byte2,
    Byte3,
    Word0,
    Word1,
    FarAddr,
    Dword,
    NearAddr,
}

impl OpUnary {
    /// 演算子を即値に適用した結果を返す。全ての演算はオーバーフローを許す (wrapping)。
    ///
    /// # Panics
    ///
    /// `self == Bank` の場合、panic する。
    pub fn apply(self, x: i64) -> i64 {
        match self {
            Self::Neg => x.wrapping_neg(),
            Self::BitNot => !x,
            Self::U16SwapBytes => (x as u16).swap_bytes().into(),
            Self::Not => (x == 0).into(),
            Self::Bank => unimplemented!("EXPR_BANK is not supported"),
            Self::Byte0 => x & 0xFF,
            Self::Byte1 => (x & 0xFF00) >> 8,
            Self::Byte2 => (x & 0xFF0000) >> 16,
            Self::Byte3 => (x & 0xFF000000) >> 24,
            Self::Word0 => x & 0xFFFF,
            Self::Word1 => (x & 0xFFFF0000) >> 16,
            Self::FarAddr => x & 0xFFFFFF,
            Self::Dword => x & 0xFFFFFFFF,
            Self::NearAddr => x & 0xFFFF,
        }
    }
}

impl std::fmt::Display for OpUnary {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        std::fmt::Debug::fmt(self, f)
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ExprBinary {
    pub op: OpBinary,
    pub lhs: Expr,
    pub rhs: Expr,
}

impl std::fmt::Display for ExprBinary {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "({} {} {})", self.op, self.lhs, self.rhs)
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum OpBinary {
    Add,
    Sub,
    Mul,
    Div,
    Rem,
    BitOr,
    BitXor,
    BitAnd,
    Shl,
    Shr,
    Eq,
    Ne,
    Lt,
    Gt,
    Le,
    Ge,
    And,
    Or,
    Xor,
    Max,
    Min,
}

impl OpBinary {
    /// 演算子を即値に適用した結果を返す。全ての演算はオーバーフローを許す (wrapping)。
    ///
    /// # Panics
    ///
    /// `(self == Div || self == Rem) && rhs == 0` の場合、panic する。
    pub fn apply(self, lhs: i64, rhs: i64) -> i64 {
        match self {
            Self::Add => lhs.wrapping_add(rhs),
            Self::Sub => lhs.wrapping_sub(rhs),
            Self::Mul => lhs.wrapping_mul(rhs),
            Self::Div => lhs.wrapping_div(rhs),
            Self::Rem => lhs.wrapping_rem(rhs),
            Self::BitOr => lhs | rhs,
            Self::BitXor => lhs ^ rhs,
            Self::BitAnd => lhs & rhs,
            Self::Shl => lhs.wrapping_shl(rhs as u32),
            Self::Shr => lhs.wrapping_shr(rhs as u32),
            Self::Eq => (lhs == rhs).into(),
            Self::Ne => (lhs != rhs).into(),
            Self::Lt => (lhs < rhs).into(),
            Self::Gt => (lhs > rhs).into(),
            Self::Le => (lhs <= rhs).into(),
            Self::Ge => (lhs >= rhs).into(),
            Self::And => ((lhs != 0) && (rhs != 0)).into(),
            Self::Or => ((lhs != 0) || (rhs != 0)).into(),
            Self::Xor => ((lhs != 0) ^ (rhs != 0)).into(),
            Self::Max => lhs.max(rhs),
            Self::Min => lhs.min(rhs),
        }
    }
}

impl std::fmt::Display for OpBinary {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        std::fmt::Debug::fmt(self, f)
    }
}

impl ParseAt<'_> for Expr {
    fn parse_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Self> {
        let ty = u8::parse_at(bytes, off)?;

        macro_rules! parse_literal {
            () => {{
                let value = i32::parse_at(bytes, off)?;
                Self::literal(i64::from(value))
            }};
        }
        macro_rules! parse_symbol {
            () => {{
                let import_idx = parse_uleb128_at(bytes, off)?;
                Self::symbol(import_idx)
            }};
        }
        macro_rules! parse_section {
            () => {{
                let section_idx = parse_uleb128_at(bytes, off)?;
                Self::section(section_idx)
            }};
        }
        macro_rules! parse_unary {
            ($op:expr) => {{
                let expr = Self::parse_at(bytes, off)?;
                let _rhs = Self::parse_at(bytes, off)?;
                // TODO: _rhs が Null でないとき警告する?
                Self::unary($op, expr)
            }};
        }
        macro_rules! parse_binary {
            ($op:expr) => {{
                let lhs = Self::parse_at(bytes, off)?;
                let rhs = Self::parse_at(bytes, off)?;
                Self::binary($op, lhs, rhs)
            }};
        }

        let expr = match ty {
            0x00 => Self::Null,
            0x01 => parse_binary!(OpBinary::Add),
            0x02 => parse_binary!(OpBinary::Sub),
            0x03 => parse_binary!(OpBinary::Mul),
            0x04 => parse_binary!(OpBinary::Div),
            0x05 => parse_binary!(OpBinary::Rem),
            0x06 => parse_binary!(OpBinary::BitOr),
            0x07 => parse_binary!(OpBinary::BitXor),
            0x08 => parse_binary!(OpBinary::BitAnd),
            0x09 => parse_binary!(OpBinary::Shl),
            0x0A => parse_binary!(OpBinary::Shr),
            0x0B => parse_binary!(OpBinary::Eq),
            0x0C => parse_binary!(OpBinary::Ne),
            0x0D => parse_binary!(OpBinary::Lt),
            0x0E => parse_binary!(OpBinary::Gt),
            0x0F => parse_binary!(OpBinary::Le),
            0x10 => parse_binary!(OpBinary::Ge),
            0x11 => parse_binary!(OpBinary::And),
            0x12 => parse_binary!(OpBinary::Or),
            0x13 => parse_binary!(OpBinary::Xor),
            0x14 => parse_binary!(OpBinary::Max),
            0x15 => parse_binary!(OpBinary::Min),
            0x41 => parse_unary!(OpUnary::Neg),
            0x42 => parse_unary!(OpUnary::BitNot),
            0x43 => parse_unary!(OpUnary::U16SwapBytes),
            0x44 => parse_unary!(OpUnary::Not),
            0x45 => parse_unary!(OpUnary::Bank),
            0x48 => parse_unary!(OpUnary::Byte0),
            0x49 => parse_unary!(OpUnary::Byte1),
            0x4A => parse_unary!(OpUnary::Byte2),
            0x4B => parse_unary!(OpUnary::Byte3),
            0x4C => parse_unary!(OpUnary::Word0),
            0x4D => parse_unary!(OpUnary::Word1),
            0x4E => parse_unary!(OpUnary::FarAddr),
            0x4F => parse_unary!(OpUnary::Dword),
            0x50 => parse_unary!(OpUnary::NearAddr),
            0x81 => parse_literal!(),
            0x82 => parse_symbol!(),
            0x83 => parse_section!(),
            _ => parse_bail!("unknown expr type: {ty:#X}"),
        };

        Ok(expr)
    }
}