Files
dcb/dcb-exe/src/inst.rs

126 lines
4.0 KiB
Rust

//! Psx cpu instructions
//!
//! This module defines all instructions for the psx cpu, the
//! `MIPS R3051`, following [Nocash's specifications](https://problemkaputt.de/psx-spx.htm).
//!
//! The instructions are split across 3 main types,
//! - `[basic::Inst]`, which defines all 'basic' instructions, i.e. all instructions which are
//! a single word in size and that carry no simplifications (such as `addi $a0, $a0, 10` == `addi $a0, 10`).
//! - `[pseudo::Inst]`, instructions which are decoded from basic instructions and that represent either
//! a simplified version of an instruction, or multiple instructions (such as `la $a0, 0x80001000` == `lui $a0, 0x8000 / addiu $ao, 0x1000`).
//! - `[Directive]`, which represent data, rather than instructions, such as `dw` and `.ascii`.
//!
//! See each instruction's module for information on how they are decoded and their variants.
//!
//! Every instruction also uses the [`Register`] enum, which defines all registers in the cpu (except instruction specific
//! registers, such as the `lo` and `hi` registers).
//!
//! This module also contains the [`iter`] module, home to the [`ParseIter`] type, an iterator which parses
//! instructions from raw bytes and their position in memory.
// Modules
pub mod basic;
pub mod directive;
pub mod error;
pub mod fmt;
pub mod iter;
pub mod pseudo;
pub mod reg;
pub mod size;
pub mod target;
// Exports
pub use directive::Directive;
pub use error::DecodeError;
pub use fmt::{InstFmt, InstTargetFmt};
pub use iter::ParseIter;
pub use reg::Register;
pub use size::InstSize;
pub use target::InstTarget;
// Imports
use self::{basic::Decodable as _, pseudo::Decodable as _};
use crate::{DataTable, FuncTable, Pos};
/// An assembler instruction.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[derive(derive_more::TryInto)]
pub enum Inst<'a> {
/// A basic instruction
Basic(basic::Inst),
/// A pseudo instruction
Pseudo(pseudo::Inst),
/// A directive
Directive(Directive<'a>),
}
impl<'a> Inst<'a> {
/// Decodes an instruction from bytes and it's position.
pub fn decode(pos: Pos, bytes: &'a [u8], data_table: &'a DataTable, _func_table: &'a FuncTable) -> Result<Self, DecodeError<'a>> {
// If `bytes` is empty, return Err
if bytes.is_empty() {
return Err(DecodeError::NoBytes);
}
// If we're contained in some data, check it's type so we can read it
if let Some(data) = data_table.get_containing(pos) {
return Directive::decode_with_data(pos, bytes, data.ty(), data.start_pos())
.map(Self::Directive)
.map_err(|err| DecodeError::InvalidDataLocation { data, err });
}
// TODO: Check functions
// If we're not aligned to a word, decode a directive
if !pos.is_word_aligned() {
let directive = Directive::decode(pos, bytes).ok_or(DecodeError::NoBytes)?;
return Ok(Self::Directive(directive));
}
// Else make the instruction iterator
// Note: We fuse it to make sure that pseudo instructions don't try to skip
// invalid instructions.
let mut insts = bytes
.array_chunks::<4>()
.copied()
.map(u32::from_ne_bytes)
.map_while(|word| basic::Raw::from_u32(word).and_then(basic::Inst::decode))
.fuse();
// Try to decode a pseudo-instruction
if let Some(inst) = pseudo::Inst::decode(insts.clone()) {
return Ok(Self::Pseudo(inst));
}
// Else try to decode it as an basic instruction
if let Some(inst) = insts.next() {
return Ok(Self::Basic(inst));
}
// Else read it as a directive
Directive::decode(pos, bytes).map(Self::Directive).ok_or(DecodeError::NoBytes)
}
}
impl<'a> InstSize for Inst<'a> {
fn size(&self) -> usize {
match self {
Inst::Basic(inst) => inst.size(),
Inst::Pseudo(inst) => inst.size(),
Inst::Directive(directive) => directive.size(),
}
}
}
impl<'a> InstFmt for Inst<'a> {
fn fmt(&self, pos: Pos, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Basic(inst) => inst.fmt(pos, f),
Self::Pseudo(inst) => inst.fmt(pos, f),
Self::Directive(directive) => <Directive as InstFmt>::fmt(directive, pos, f),
}
}
}