mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-07 18:31:14 +00:00
Revamped most things.
Honstly not sure what's happened, but `dcb-exe` is mostly being redone from scratch.
This commit is contained in:
parent
c9644ae2fb
commit
48e29ec83c
@ -7,5 +7,5 @@ members = [
|
||||
"dcb-exe",
|
||||
"dcb-bytes",
|
||||
"dcb-bytes-derive",
|
||||
"dcb-tools"
|
||||
"dcb-tools",
|
||||
]
|
||||
|
||||
@ -16,13 +16,13 @@ log = "0.4"
|
||||
|
||||
# Util
|
||||
byteorder = "1.3"
|
||||
ascii = { version = "1.0", features = ["serde"] }
|
||||
arrayref = "0.3"
|
||||
int-conv = "0.1"
|
||||
ascii = { version = "1.0", features = ["serde"] }
|
||||
arrayref = "0.3"
|
||||
int-conv = "0.1"
|
||||
bitmatch = "0.1"
|
||||
either = "1.6"
|
||||
smallvec = "1.4"
|
||||
num_enum = "0.5"
|
||||
either = "1.6.1"
|
||||
|
||||
# Serde
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
@ -32,7 +32,6 @@ serde_yaml = "0.8"
|
||||
derive_more = "0.99"
|
||||
thiserror = "1.0"
|
||||
ref-cast = "1.0"
|
||||
dcb-bytes-derive = { path = "../dcb-bytes-derive" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
|
||||
@ -6,46 +6,46 @@
|
||||
// Modules
|
||||
pub mod data;
|
||||
pub mod error;
|
||||
//pub mod func;
|
||||
pub mod header;
|
||||
pub mod instruction;
|
||||
pub mod inst;
|
||||
pub mod pos;
|
||||
//pub mod func;
|
||||
|
||||
// Exports
|
||||
pub use data::{Data, DataTable, DataType};
|
||||
pub use error::DeserializeError;
|
||||
//pub use func::Func;
|
||||
pub use header::Header;
|
||||
pub use instruction::Instruction;
|
||||
pub use pos::Pos;
|
||||
//pub use func::Func;
|
||||
|
||||
// Imports
|
||||
use self::inst::Inst;
|
||||
use dcb_bytes::{ByteArray, Bytes};
|
||||
use dcb_io::GameFile;
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
io::{Read, Seek, Write},
|
||||
};
|
||||
use std::io::{Read, Seek, Write};
|
||||
|
||||
/// The game executable
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
//#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[allow(clippy::as_conversions)] // `SIZE` always fits
|
||||
pub struct Exe {
|
||||
/// The executable header
|
||||
pub header: Header,
|
||||
|
||||
/// All bytes within the exe
|
||||
pub bytes: Box<[u8; Self::SIZE as usize]>,
|
||||
|
||||
/// The data table.
|
||||
pub data_table: DataTable,
|
||||
//pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Exe {
|
||||
/// Code range
|
||||
///
|
||||
/// Everything outside of this range will be considered data.
|
||||
pub const CODE_RANGE: std::ops::Range<Pos> = Pos(0x80013e4c)..Pos(0x8006dd3c);
|
||||
/// Size of the executable
|
||||
const SIZE: u32 = 0x68000;
|
||||
/// Start address of the executable
|
||||
const START_ADDRESS: dcb_io::Data = dcb_io::Data::from_u64(0x58b9000);
|
||||
/// Start memory address of the executable
|
||||
const START_MEM_ADDRESS: Pos = Pos(0x80010000);
|
||||
}
|
||||
|
||||
impl Exe {
|
||||
@ -60,13 +60,31 @@ impl Exe {
|
||||
file.read_exact(&mut header_bytes).map_err(DeserializeError::ReadHeader)?;
|
||||
let header = Header::from_bytes(&header_bytes).map_err(DeserializeError::ParseHeader)?;
|
||||
|
||||
//
|
||||
//file.bytes();
|
||||
// Make sure the header size is the one we expect
|
||||
if header.size != Self::SIZE {
|
||||
return Err(DeserializeError::WrongDataSize { header: Box::new(header) });
|
||||
}
|
||||
|
||||
let mut data = vec![0u8; usize::try_from(header.size).expect("Header size didn't fit into a `usize`")];
|
||||
file.read_exact(data.as_mut()).map_err(DeserializeError::ReadData)?;
|
||||
// Read all of the bytes
|
||||
#[allow(clippy::as_conversions)] // `SIZE` always fits
|
||||
let mut bytes = Box::new([0u8; Self::SIZE as usize]);
|
||||
file.read_exact(bytes.as_mut()).map_err(DeserializeError::ReadData)?;
|
||||
|
||||
todo!();
|
||||
//Ok(Self { header, data })
|
||||
// Parse all instructions
|
||||
let insts = inst::ParseIter::new(&*bytes, Self::START_MEM_ADDRESS);
|
||||
|
||||
// Parse all data and code
|
||||
let known_data_table = DataTable::get_known().map_err(DeserializeError::KnownDataTable)?;
|
||||
let heuristics_data_table = DataTable::search_instructions(insts);
|
||||
|
||||
let data_table = known_data_table.merge_with(heuristics_data_table);
|
||||
|
||||
Ok(Self { header, bytes, data_table })
|
||||
}
|
||||
|
||||
/// Returns a parsing iterator for all instructions
|
||||
#[must_use]
|
||||
pub const fn parse_iter(&self) -> inst::ParseIter {
|
||||
inst::ParseIter::new(&*self.bytes, Self::START_MEM_ADDRESS)
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,15 +21,13 @@ use std::{borrow::Borrow, cmp::Ordering};
|
||||
/// A data location.
|
||||
///
|
||||
/// Two data locations are considered equal if they
|
||||
/// share the same position and kind.
|
||||
///
|
||||
/// Their relative
|
||||
/// order is also firstly dependent on their position
|
||||
/// and kind, although when 2 data locations share
|
||||
/// the same position, the larger one will appear first
|
||||
/// in the ordering. This is to implement 'specialized'
|
||||
/// data locations, where one may have a certain data location
|
||||
/// inside of another for a specific purpose.
|
||||
/// share the same position.
|
||||
///
|
||||
/// Their relative order first depends on their position.
|
||||
/// When their positions are equal, the larger one will
|
||||
/// appear first in the order.
|
||||
/// This is to implement `specialized` data locations, where
|
||||
/// a large data location can have multiple data locations inside.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Data {
|
||||
|
||||
@ -13,10 +13,14 @@ pub mod error;
|
||||
|
||||
// Exports
|
||||
pub use error::GetKnownError;
|
||||
use inst::directive::Directive;
|
||||
|
||||
// Imports
|
||||
use super::Data;
|
||||
use crate::exe::Pos;
|
||||
use super::{Data, DataType};
|
||||
use crate::exe::{
|
||||
inst::{self, basic, Inst},
|
||||
Pos,
|
||||
};
|
||||
use dcb_util::DiscardingSortedMergeIter;
|
||||
use std::{collections::BTreeSet, fs::File, iter::FromIterator};
|
||||
|
||||
@ -43,7 +47,7 @@ impl DataTable {
|
||||
/// discovered data locations, as the known functions are always kept, and the
|
||||
/// duplicate discovered ones are discarded.
|
||||
#[must_use]
|
||||
pub fn merge(self, other: Self) -> Self {
|
||||
pub fn merge_with(self, other: Self) -> Self {
|
||||
// Note: We don't return the iterator, as we want the user to
|
||||
// keep the guarantees supplied by this type.
|
||||
DiscardingSortedMergeIter::new(self.0.into_iter(), other.0.into_iter()).collect()
|
||||
@ -67,15 +71,17 @@ impl DataTable {
|
||||
serde_yaml::from_reader(file).map_err(GetKnownError::Parse)
|
||||
}
|
||||
|
||||
/*
|
||||
/// Searches all instructions for references to
|
||||
/// executable data using certain heuristics.
|
||||
#[must_use]
|
||||
pub fn search_instructions<'a>(instructions: impl Iterator<Item = (Pos, &'a Instruction)> + Clone) -> Self {
|
||||
pub fn search_instructions(insts: impl Iterator<Item = (Pos, Inst)> + Clone) -> Self {
|
||||
// Get all possible references to data
|
||||
let data_references: BTreeSet<Pos> = instructions
|
||||
let references: BTreeSet<Pos> = insts
|
||||
.clone()
|
||||
.filter_map(|(_, instruction)| match instruction {
|
||||
.filter_map(|(pos, inst)| match inst {
|
||||
Inst::Basic(basic::Inst::Load(basic::load::Inst { offset, .. }) | basic::Inst::Store(basic::store::Inst { offset, .. })) => {
|
||||
Some(pos + offset)
|
||||
},
|
||||
/*
|
||||
Instruction::Pseudo(
|
||||
PseudoInst::La { target: offset, .. } |
|
||||
@ -94,48 +100,47 @@ impl DataTable {
|
||||
PseudoInst::SwrImm { offset, .. },
|
||||
) |
|
||||
*/
|
||||
Instruction::Directive(Directive::Dw(offset)) => Some(Pos(*offset)),
|
||||
Inst::Directive(Directive::Dw(address)) => Some(Pos(address)),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Then filter the instructions for data locations.
|
||||
instructions
|
||||
insts
|
||||
// Filter all non-directives
|
||||
.filter_map(|(pos, instruction)| match instruction {
|
||||
Instruction::Directive(directive) if data_references.contains(&pos) => Some((pos, directive)),
|
||||
Inst::Directive(directive) if references.contains(&pos) => Some((pos, directive)),
|
||||
_ => None,
|
||||
})
|
||||
.zip(0..)
|
||||
.map(|((pos, directive), idx)| {
|
||||
match directive {
|
||||
Directive::Ascii(ascii) => Data {
|
||||
Directive::Ascii { len } => Data {
|
||||
name: format!("string_{idx}"),
|
||||
desc: String::new(),
|
||||
pos,
|
||||
kind: DataKind::AsciiStr { len: ascii.len().try_into().expect("String length didn't fit into a `u32`") },
|
||||
ty: DataType::Array { ty: Box::new(DataType::AsciiChar), len },
|
||||
},
|
||||
Directive::Dw(_) => Data {
|
||||
name: format!("data_w{idx}"),
|
||||
desc: String::new(),
|
||||
pos,
|
||||
kind: DataKind::Word,
|
||||
ty: DataType::Word,
|
||||
},
|
||||
Directive::Dh(_) => Data {
|
||||
name: format!("data_h{idx}"),
|
||||
desc: String::new(),
|
||||
pos,
|
||||
kind: DataKind::HalfWord,
|
||||
ty: DataType::HalfWord,
|
||||
},
|
||||
Directive::Db(_) => Data {
|
||||
name: format!("data_b{idx}"),
|
||||
desc: String::new(),
|
||||
pos,
|
||||
kind: DataKind::Byte,
|
||||
ty: DataType::Byte,
|
||||
},
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@ -10,8 +10,8 @@ use serde::de::Visitor;
|
||||
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum DataType {
|
||||
/// Ascii character
|
||||
#[display(fmt = "AsciiChar")]
|
||||
/// Ascii string
|
||||
#[display(fmt = "str")]
|
||||
AsciiChar,
|
||||
|
||||
/// Word
|
||||
@ -88,24 +88,27 @@ impl std::str::FromStr for DataType {
|
||||
type Err = FromStrError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
// Strip any whitespace.
|
||||
let s = s.trim();
|
||||
|
||||
// If it starts with '[', read it as an array
|
||||
if let Some(s) = s.strip_prefix('[') {
|
||||
// Find the first ';' from the end to split.
|
||||
let s = s.strip_suffix(']').ok_or(FromStrError::MissingArraySuffix)?;
|
||||
// Note: We find the first ';' from the end to split.
|
||||
let (ty, len) = s
|
||||
.char_indices()
|
||||
.rev()
|
||||
.find_map(|(pos, c)| c.eq(&';').then(|| s.split_at(pos)))
|
||||
.ok_or(FromStrError::MissingArraySep)?;
|
||||
// Ignore the leading ';'
|
||||
#[allow(clippy::indexing_slicing)] // This can't panic, as `pos < len.`
|
||||
|
||||
// Ignore the leading ';' on the second.
|
||||
#[allow(clippy::indexing_slicing)] // This can't panic, as `pos < len`.
|
||||
let len = &len[1..];
|
||||
|
||||
let ty = box Self::from_str(ty).map_err(|err| FromStrError::InvalidArrayTy(box err))?;
|
||||
let len = self::parse_u32(len).map_err(|err| FromStrError::InvalidArrayLen { len: len.to_string(), err })?;
|
||||
// Trim both strings
|
||||
let ty = ty.trim();
|
||||
let len = len.trim();
|
||||
|
||||
let ty = Self::from_str(ty).map_err(|err| FromStrError::InvalidArrayTy(Box::new(err)))?;
|
||||
let ty = Box::new(ty);
|
||||
let len = self::parse_u32(len).map_err(|err| FromStrError::InvalidArrayLen { len: len.to_owned(), err })?;
|
||||
|
||||
return Ok(Self::Array { ty, len });
|
||||
}
|
||||
@ -116,7 +119,7 @@ impl std::str::FromStr for DataType {
|
||||
"u8" => Ok(Self::Byte),
|
||||
"u16" => Ok(Self::HalfWord),
|
||||
"u32" => Ok(Self::Word),
|
||||
_ => Err(FromStrError::UnknownTy { ty: s.to_string() }),
|
||||
_ => Err(FromStrError::UnknownTy { ty: s.to_owned() }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
//! Errors
|
||||
|
||||
// Imports
|
||||
use super::header;
|
||||
use super::{data, header, Exe, Header};
|
||||
|
||||
/// Error type for [`Table::deserialize`]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@ -18,7 +18,18 @@ pub enum DeserializeError {
|
||||
#[error("Unable to parse header")]
|
||||
ParseHeader(#[source] header::FromBytesError),
|
||||
|
||||
/// Data had wrong size
|
||||
#[error("Wrong data size, expected {}, found {}", Exe::SIZE, header.size)]
|
||||
WrongDataSize {
|
||||
/// The read header
|
||||
header: Box<Header>,
|
||||
},
|
||||
|
||||
/// Unable to read data
|
||||
#[error("Unable to read data")]
|
||||
ReadData(#[source] std::io::Error),
|
||||
|
||||
/// Unable to get known data
|
||||
#[error("Unable to get known data table")]
|
||||
KnownDataTable(#[source] data::table::GetKnownError),
|
||||
}
|
||||
|
||||
@ -4,14 +4,13 @@
|
||||
pub mod error;
|
||||
|
||||
// Exports
|
||||
use std::fmt;
|
||||
|
||||
pub use error::{FromBytesError, ToBytesError};
|
||||
|
||||
// Import
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use dcb_bytes::Bytes;
|
||||
use dcb_util::{array_split, null_ascii_string::NullAsciiString, AsciiStrArr};
|
||||
use std::fmt;
|
||||
|
||||
/// The header of the executable.
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
|
||||
@ -66,12 +65,12 @@ impl fmt::Display for Header {
|
||||
..
|
||||
} = self;
|
||||
|
||||
write!(f, "PC: {pc0:#x}")?;
|
||||
write!(f, "GP: {gp0:#x}")?;
|
||||
write!(f, "Destination: {dest:#x} / size: {size:#x}")?;
|
||||
write!(f, "Memfill: {memfill_start:#X} / size: {memfill_size:#x}")?;
|
||||
write!(f, "SP: {initial_sp_base:#x} / offset: {initial_sp_offset:#x}")?;
|
||||
write!(f, "Marker: {marker:?}")
|
||||
writeln!(f, "PC: {pc0:#x}")?;
|
||||
writeln!(f, "GP: {gp0:#x}")?;
|
||||
writeln!(f, "Destination: {dest:#x} / size: {size:#x}")?;
|
||||
writeln!(f, "Memfill: {memfill_start:#X} / size: {memfill_size:#x}")?;
|
||||
writeln!(f, "SP: {initial_sp_base:#x} / offset: {initial_sp_offset:#x}")?;
|
||||
writeln!(f, "Marker: {marker:?}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
104
dcb-exe/src/exe/inst.rs
Normal file
104
dcb-exe/src/exe/inst.rs
Normal file
@ -0,0 +1,104 @@
|
||||
//! Psx cpu instructions
|
||||
|
||||
// Modules
|
||||
pub mod basic;
|
||||
pub mod directive;
|
||||
pub mod pseudo;
|
||||
pub mod raw;
|
||||
pub mod reg;
|
||||
|
||||
// Exports
|
||||
pub use directive::Directive;
|
||||
pub use raw::Raw;
|
||||
pub use reg::Register;
|
||||
|
||||
// Imports
|
||||
use crate::Pos;
|
||||
use dcb_util::NextFromBytes;
|
||||
|
||||
/// An assembler instruction
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum Inst {
|
||||
/// A basic instruction
|
||||
Basic(basic::Inst),
|
||||
|
||||
/// A pseudo instruction
|
||||
Pseudo(pseudo::PseudoInst),
|
||||
|
||||
/// A directive
|
||||
Directive(Directive),
|
||||
}
|
||||
|
||||
impl Inst {
|
||||
/// End of the code itself in the executable.
|
||||
pub const CODE_END: Pos = Pos(0x8006dd3c);
|
||||
/// Code range
|
||||
pub const CODE_RANGE: std::ops::Range<Pos> = Self::CODE_START..Self::CODE_END;
|
||||
/// Start of the code itself in the executable.
|
||||
pub const CODE_START: Pos = Pos(0x80013e4c);
|
||||
}
|
||||
|
||||
/// Parsing iterator, reads instructions from a `[u8]` slice
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct ParseIter<'a> {
|
||||
/// Remaining bytes
|
||||
bytes: &'a [u8],
|
||||
|
||||
/// Starting position of bytes
|
||||
cur_pos: Pos,
|
||||
}
|
||||
|
||||
impl<'a> ParseIter<'a> {
|
||||
/// Creates a new parsing iterator
|
||||
#[must_use]
|
||||
pub const fn new(bytes: &'a [u8], start_pos: Pos) -> Self {
|
||||
Self { bytes, cur_pos: start_pos }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ParseIter<'a> {
|
||||
type Item = (Pos, Inst);
|
||||
|
||||
#[allow(clippy::as_conversions, clippy::cast_possible_truncation)] // Byte lengths will always fit into a `u32`, as `self.bytes.len()` is always smaller than `u32`.
|
||||
#[allow(clippy::indexing_slicing)] // Our lengths will always be smaller than the bytes array they are used to index.
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// If we're outside of code range, decode a directive
|
||||
if !Inst::CODE_RANGE.contains(&self.cur_pos) {
|
||||
let (directive, len) = Directive::decode(self.cur_pos, self.bytes)?;
|
||||
self.bytes = &self.bytes[len..];
|
||||
let pos = self.cur_pos;
|
||||
self.cur_pos += len as u32;
|
||||
return Some((pos, Inst::Directive(directive)));
|
||||
}
|
||||
|
||||
// Else decode an instruction, falling back to a directive if unable to
|
||||
match self.bytes.next_u32().and_then(basic::Inst::decode) {
|
||||
// If we got one, update our bytes and check if it's a pseudo instruction
|
||||
Some(inst) => {
|
||||
self.bytes = &self.bytes[4..];
|
||||
let pos = self.cur_pos;
|
||||
self.cur_pos += 4;
|
||||
match pseudo::PseudoInst::decode(inst, self.bytes) {
|
||||
Some((inst, len)) => {
|
||||
self.bytes = &self.bytes[len..];
|
||||
self.cur_pos += len as u32;
|
||||
Some((pos, Inst::Pseudo(inst)))
|
||||
},
|
||||
None => Some((pos, Inst::Basic(inst))),
|
||||
}
|
||||
},
|
||||
|
||||
// If we don't have enough for a `u32` or we didn't manage to
|
||||
// parse an instruction, try to parse a directive
|
||||
None => match Directive::decode(self.cur_pos, self.bytes) {
|
||||
Some((directive, len)) => {
|
||||
self.bytes = &self.bytes[len..];
|
||||
let pos = self.cur_pos;
|
||||
self.cur_pos += len as u32;
|
||||
Some((pos, Inst::Directive(directive)))
|
||||
},
|
||||
None => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
123
dcb-exe/src/exe/inst/basic.rs
Normal file
123
dcb-exe/src/exe/inst/basic.rs
Normal file
@ -0,0 +1,123 @@
|
||||
//! Basic instructions
|
||||
//!
|
||||
//! This modules defines all the basic instructions from the psx.
|
||||
//! They are all 1 word (`u32`) long.
|
||||
|
||||
// Modules
|
||||
pub mod alu;
|
||||
pub mod cond;
|
||||
//pub mod iter;
|
||||
pub mod jmp;
|
||||
pub mod load;
|
||||
pub mod lui;
|
||||
pub mod mult;
|
||||
pub mod store;
|
||||
pub mod sys;
|
||||
|
||||
/// All basic instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum Inst {
|
||||
/// Store
|
||||
Store(store::Inst),
|
||||
|
||||
/// Load
|
||||
Load(load::Inst),
|
||||
|
||||
/// Condition
|
||||
Cond(cond::Inst),
|
||||
|
||||
/// Jump
|
||||
Jmp(jmp::Inst),
|
||||
|
||||
/// Alu
|
||||
Alu(alu::Inst),
|
||||
|
||||
/// Load upper immediate
|
||||
Lui(lui::Inst),
|
||||
|
||||
/// Syscall
|
||||
Sys(sys::Inst),
|
||||
}
|
||||
|
||||
impl Inst {
|
||||
/// Decodes an instruction
|
||||
#[must_use]
|
||||
#[bitmatch::bitmatch]
|
||||
#[allow(clippy::many_single_char_names)] // `bitmatch` can only output single character names.
|
||||
pub fn decode(raw: u32) -> Option<Self> {
|
||||
let inst = #[bitmatch]
|
||||
match raw {
|
||||
// Jump
|
||||
"00001p_iiiii_iiiii_iiiii_iiiii_iiiiii" => Self::Jmp(jmp::Inst::decode(jmp::imm::Raw { p, i })?),
|
||||
"000000_sssss_?????_ddddd_?????_00100f" => Self::Jmp(jmp::Inst::decode(jmp::reg::Raw { s, d, f })?),
|
||||
|
||||
"000ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::Cond(cond::Inst::decode(cond::Raw { p, s, t, i })?),
|
||||
"001111_?????_ttttt_iiiii_iiiii_iiiiii" => Self::Lui(lui::Inst::decode(lui::Raw { t, i })?),
|
||||
|
||||
// Alu
|
||||
"000000_sssss_ttttt_ddddd_?????_10ffff" => Self::Alu(alu::Inst::decode(alu::reg::Raw { s, t, d, f })?),
|
||||
"001ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::Alu(alu::Inst::decode(alu::imm::Raw { p, s, t, i })?),
|
||||
|
||||
// Syscall
|
||||
"000000_ccccc_ccccc_ccccc_ccccc_00110f" => Self::Sys(sys::Inst::decode(sys::Raw { c, f })?),
|
||||
|
||||
// Store / Load
|
||||
"100ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::Store(store::Inst::decode(store::Raw { p, s, t, i })?),
|
||||
"101ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::Load(load::Inst::decode(load::Raw { p, s, t, i })?),
|
||||
|
||||
// TODO: Remaining instructions, such as shift
|
||||
|
||||
/*
|
||||
"0100nn_1iiii_iiiii_iiiii_iiiii_iiiiii" => CopN { n: n.truncate(), imm: i},
|
||||
"0100nn_00000_ttttt_ddddd_?????_000000" => MfcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
|
||||
"0100nn_00010_ttttt_ddddd_?????_000000" => CfcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
|
||||
"0100nn_00100_ttttt_ddddd_?????_000000" => MtcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
|
||||
"0100nn_00110_ttttt_ddddd_?????_000000" => CtcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
|
||||
"0100nn_01000_00000_iiiii_iiiii_iiiiii" => BcNf { n: n.truncate(), target: i.truncate() },
|
||||
"0100nn_01000_00001_iiiii_iiiii_iiiiii" => BcNt { n: n.truncate(), target: i.truncate() },
|
||||
"1100nn_sssss_ttttt_iiiii_iiiii_iiiiii" => LwcN { n: n.truncate(), rs: reg(s)?, rt: reg(t)?, imm: i.truncate() },
|
||||
"1110nn_sssss_ttttt_iiiii_iiiii_iiiiii" => SwcN { n: n.truncate(), rs: reg(s)?, rt: reg(t)?, imm: i.truncate() },
|
||||
*/
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(inst)
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
#[bitmatch::bitmatch]
|
||||
pub fn encode(self) -> u32 {
|
||||
match self {
|
||||
Self::Jmp(inst) => match inst.encode() {
|
||||
jmp::Raw::Imm(jmp::imm::Raw { p, i }) => bitpack!("00001p_iiiii_iiiii_iiiii_iiiii_iiiiii"),
|
||||
jmp::Raw::Reg(jmp::reg::Raw { s, d, f }) => bitpack!("000000_sssss_?????_ddddd_?????_00100f"),
|
||||
},
|
||||
Self::Cond(inst) => {
|
||||
let cond::Raw { p, s, t, i } = inst.encode();
|
||||
bitpack!("000ppp_sssss_ttttt_iiiii_iiiii_iiiiii")
|
||||
},
|
||||
Self::Lui(inst) => {
|
||||
let lui::Raw { t, i } = inst.encode();
|
||||
bitpack!("001111_?????_ttttt_iiiii_iiiii_iiiiii")
|
||||
},
|
||||
Self::Alu(inst) => match inst.encode() {
|
||||
alu::Raw::Imm(alu::imm::Raw { p, s, t, i }) => bitpack!("001ppp_sssss_ttttt_iiiii_iiiii_iiiiii"),
|
||||
alu::Raw::Reg(alu::reg::Raw { s, t, d, f }) => bitpack!("000000_sssss_ttttt_ddddd_?????_10ffff"),
|
||||
},
|
||||
Self::Sys(inst) => {
|
||||
let sys::Raw { c, f } = inst.encode();
|
||||
bitpack!("000000_ccccc_ccccc_ccccc_ccccc_00110f")
|
||||
},
|
||||
Self::Store(inst) => {
|
||||
let store::Raw { p, s, t, i } = inst.encode();
|
||||
bitpack!("100ppp_sssss_ttttt_iiiii_iiiii_iiiiii")
|
||||
},
|
||||
Self::Load(inst) => {
|
||||
let load::Raw { p, s, t, i } = inst.encode();
|
||||
bitpack!("101ppp_sssss_ttttt_iiiii_iiiii_iiiiii")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
47
dcb-exe/src/exe/inst/basic/alu.rs
Normal file
47
dcb-exe/src/exe/inst/basic/alu.rs
Normal file
@ -0,0 +1,47 @@
|
||||
//! Alu instructions
|
||||
|
||||
// Modules
|
||||
pub mod imm;
|
||||
pub mod reg;
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::From)]
|
||||
pub enum Raw {
|
||||
/// Immediate
|
||||
Imm(imm::Raw),
|
||||
|
||||
/// Register
|
||||
Reg(reg::Raw),
|
||||
}
|
||||
|
||||
/// Alu register instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum Inst {
|
||||
/// Immediate
|
||||
Imm(imm::Inst),
|
||||
|
||||
/// Register
|
||||
Reg(reg::Inst),
|
||||
}
|
||||
|
||||
impl Inst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: impl Into<Raw>) -> Option<Self> {
|
||||
match raw.into() {
|
||||
Raw::Imm(raw) => Some(Self::Imm(imm::Inst::decode(raw)?)),
|
||||
Raw::Reg(raw) => Some(Self::Reg(reg::Inst::decode(raw)?)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> Raw {
|
||||
match self {
|
||||
Self::Imm(inst) => Raw::Imm(inst.encode()),
|
||||
Self::Reg(inst) => Raw::Reg(inst.encode()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,14 @@
|
||||
//! Alu immediate instructions
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::Register;
|
||||
use crate::exe::inst::Register;
|
||||
use dcb_util::SignedHex;
|
||||
use int_conv::{Signed, Truncated, ZeroExtended};
|
||||
use std::fmt;
|
||||
|
||||
/// Alu immediate instruction kind
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum AluImmInstKind {
|
||||
pub enum Kind {
|
||||
/// Add signed with overflow trap
|
||||
Add(i16),
|
||||
|
||||
@ -31,9 +31,10 @@ pub enum AluImmInstKind {
|
||||
Xor(u16),
|
||||
}
|
||||
|
||||
impl AluImmInstKind {
|
||||
impl Kind {
|
||||
/// Returns this kind's mnemonic
|
||||
pub fn mnemonic(self) -> &'static str {
|
||||
#[must_use]
|
||||
pub const fn mnemonic(self) -> &'static str {
|
||||
match self {
|
||||
Self::Add(_) => "addi",
|
||||
Self::AddUnsigned(_) => "addiu",
|
||||
@ -46,17 +47,18 @@ impl AluImmInstKind {
|
||||
}
|
||||
|
||||
/// Returns a displayable with the value of this kind
|
||||
#[must_use]
|
||||
pub fn value_fmt(self) -> impl fmt::Display {
|
||||
struct FmtValue(Self);
|
||||
/// Display wrapper
|
||||
struct FmtValue(Kind);
|
||||
|
||||
impl fmt::Display for FmtValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use AluImmInstKind::*;
|
||||
match self.0 {
|
||||
// Signed
|
||||
Add(rhs) | AddUnsigned(rhs) | SetLessThan(rhs) => write!(f, "{:#x}", SignedHex(rhs)),
|
||||
Kind::Add(rhs) | Kind::AddUnsigned(rhs) | Kind::SetLessThan(rhs) => write!(f, "{}", SignedHex(rhs)),
|
||||
// Unsigned
|
||||
SetLessThanUnsigned(rhs) | And(rhs) | Or(rhs) | Xor(rhs) => write!(f, "{rhs:#x}"),
|
||||
Kind::SetLessThanUnsigned(rhs) | Kind::And(rhs) | Kind::Or(rhs) | Kind::Xor(rhs) => write!(f, "{rhs}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,7 +69,7 @@ impl AluImmInstKind {
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct AluImmInstRaw {
|
||||
pub struct Raw {
|
||||
/// Opcode (lower 3 bits)
|
||||
pub p: u32,
|
||||
|
||||
@ -81,11 +83,11 @@ pub struct AluImmInstRaw {
|
||||
pub i: u32,
|
||||
}
|
||||
|
||||
/// Alu register instructions
|
||||
/// Alu immediate instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
#[display(fmt = "{} {dst}, {lhs}, {}", "kind.mnemonic()", "kind.value_fmt()")]
|
||||
pub struct AluImmInst {
|
||||
pub struct Inst {
|
||||
/// Destination register
|
||||
pub dst: Register,
|
||||
|
||||
@ -93,22 +95,22 @@ pub struct AluImmInst {
|
||||
pub lhs: Register,
|
||||
|
||||
/// Kind
|
||||
pub kind: AluImmInstKind,
|
||||
pub kind: Kind,
|
||||
}
|
||||
|
||||
impl AluImmInst {
|
||||
impl Inst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: AluImmInstRaw) -> Option<Self> {
|
||||
pub fn decode(raw: Raw) -> Option<Self> {
|
||||
#[rustfmt::skip]
|
||||
let kind = match raw.p {
|
||||
0x0 => AluImmInstKind::Add (raw.i.truncated::<u16>().as_signed()),
|
||||
0x1 => AluImmInstKind::AddUnsigned (raw.i.truncated::<u16>().as_signed()),
|
||||
0x2 => AluImmInstKind::SetLessThan (raw.i.truncated::<u16>().as_signed()),
|
||||
0x3 => AluImmInstKind::SetLessThanUnsigned(raw.i.truncated::<u16>()),
|
||||
0x4 => AluImmInstKind::And (raw.i.truncated::<u16>()),
|
||||
0x5 => AluImmInstKind::Or (raw.i.truncated::<u16>()),
|
||||
0x6 => AluImmInstKind::Xor (raw.i.truncated::<u16>()),
|
||||
0x0 => Kind::Add (raw.i.truncated::<u16>().as_signed()),
|
||||
0x1 => Kind::AddUnsigned (raw.i.truncated::<u16>().as_signed()),
|
||||
0x2 => Kind::SetLessThan (raw.i.truncated::<u16>().as_signed()),
|
||||
0x3 => Kind::SetLessThanUnsigned(raw.i.truncated::<u16>()),
|
||||
0x4 => Kind::And (raw.i.truncated::<u16>()),
|
||||
0x5 => Kind::Or (raw.i.truncated::<u16>()),
|
||||
0x6 => Kind::Xor (raw.i.truncated::<u16>()),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
@ -121,20 +123,20 @@ impl AluImmInst {
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> AluImmInstRaw {
|
||||
pub fn encode(self) -> Raw {
|
||||
#[rustfmt::skip]
|
||||
let (p, i) = match self.kind {
|
||||
AluImmInstKind::Add (rhs) => (0x0, rhs.zero_extended::<u32>()),
|
||||
AluImmInstKind::AddUnsigned (rhs) => (0x1, rhs.zero_extended::<u32>()),
|
||||
AluImmInstKind::SetLessThan (rhs) => (0x2, rhs.zero_extended::<u32>()),
|
||||
AluImmInstKind::SetLessThanUnsigned(rhs) => (0x3, rhs.zero_extended::<u32>()),
|
||||
AluImmInstKind::And (rhs) => (0x4, rhs.zero_extended::<u32>()),
|
||||
AluImmInstKind::Or (rhs) => (0x5, rhs.zero_extended::<u32>()),
|
||||
AluImmInstKind::Xor (rhs) => (0x6, rhs.zero_extended::<u32>()),
|
||||
Kind::Add (rhs) => (0x0, rhs.as_unsigned().zero_extended::<u32>()),
|
||||
Kind::AddUnsigned (rhs) => (0x1, rhs.as_unsigned().zero_extended::<u32>()),
|
||||
Kind::SetLessThan (rhs) => (0x2, rhs.as_unsigned().zero_extended::<u32>()),
|
||||
Kind::SetLessThanUnsigned(rhs) => (0x3, rhs.as_unsigned().zero_extended::<u32>()),
|
||||
Kind::And (rhs) => (0x4, rhs.as_unsigned().zero_extended::<u32>()),
|
||||
Kind::Or (rhs) => (0x5, rhs.as_unsigned().zero_extended::<u32>()),
|
||||
Kind::Xor (rhs) => (0x6, rhs.as_unsigned().zero_extended::<u32>()),
|
||||
};
|
||||
let s = self.lhs.idx();
|
||||
let t = self.dst.idx();
|
||||
|
||||
AluImmInstRaw { p, s, t, i }
|
||||
Raw { p, s, t, i }
|
||||
}
|
||||
}
|
||||
141
dcb-exe/src/exe/inst/basic/alu/reg.rs
Normal file
141
dcb-exe/src/exe/inst/basic/alu/reg.rs
Normal file
@ -0,0 +1,141 @@
|
||||
//! Alu register instructions
|
||||
|
||||
// Imports
|
||||
use crate::exe::inst::Register;
|
||||
|
||||
/// Alu register instruction kind
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum Kind {
|
||||
/// Add signed with overflow trap
|
||||
Add,
|
||||
|
||||
/// Add signed without overflow trap
|
||||
AddUnsigned,
|
||||
|
||||
/// Sub signed with overflow trap
|
||||
Sub,
|
||||
|
||||
/// Sub signed without overflow trap
|
||||
SubUnsigned,
|
||||
|
||||
/// Bit and
|
||||
And,
|
||||
|
||||
/// Bit or
|
||||
Or,
|
||||
|
||||
/// Bit xor
|
||||
Xor,
|
||||
|
||||
/// Bit nor
|
||||
Nor,
|
||||
|
||||
/// Set on less than signed
|
||||
SetLessThan,
|
||||
|
||||
/// Set on less than unsigned
|
||||
SetLessThanUnsigned,
|
||||
}
|
||||
|
||||
impl Kind {
|
||||
/// Returns this kind's mnemonic
|
||||
#[must_use]
|
||||
pub const fn mnemonic(self) -> &'static str {
|
||||
match self {
|
||||
Self::Add => "add",
|
||||
Self::AddUnsigned => "addu",
|
||||
Self::Sub => "sub",
|
||||
Self::SubUnsigned => "subu",
|
||||
Self::And => "and",
|
||||
Self::Or => "or",
|
||||
Self::Xor => "xor",
|
||||
Self::Nor => "nor",
|
||||
Self::SetLessThan => "slt",
|
||||
Self::SetLessThanUnsigned => "sltu",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct Raw {
|
||||
/// Rs
|
||||
pub s: u32,
|
||||
|
||||
/// Rt
|
||||
pub t: u32,
|
||||
|
||||
/// Rd
|
||||
pub d: u32,
|
||||
|
||||
/// Func (lower 4 bits)
|
||||
pub f: u32,
|
||||
}
|
||||
|
||||
/// Alu register instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
#[display(fmt = "{} {dst}, {lhs}, {rhs}", "kind.mnemonic()")]
|
||||
pub struct Inst {
|
||||
/// Destination register
|
||||
pub dst: Register,
|
||||
|
||||
/// Lhs argument
|
||||
pub lhs: Register,
|
||||
|
||||
/// Rhs argument
|
||||
pub rhs: Register,
|
||||
|
||||
/// Kind
|
||||
pub kind: Kind,
|
||||
}
|
||||
|
||||
impl Inst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: Raw) -> Option<Self> {
|
||||
let kind = match raw.f {
|
||||
0x0 => Kind::Add,
|
||||
0x1 => Kind::AddUnsigned,
|
||||
0x2 => Kind::Sub,
|
||||
0x3 => Kind::SubUnsigned,
|
||||
0x4 => Kind::And,
|
||||
0x5 => Kind::Or,
|
||||
0x6 => Kind::Xor,
|
||||
0x7 => Kind::Nor,
|
||||
0xa => Kind::SetLessThan,
|
||||
0xb => Kind::SetLessThanUnsigned,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(Self {
|
||||
dst: Register::new(raw.d)?,
|
||||
lhs: Register::new(raw.s)?,
|
||||
rhs: Register::new(raw.t)?,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub const fn encode(self) -> Raw {
|
||||
let f = match self.kind {
|
||||
Kind::Add => 0x0,
|
||||
Kind::AddUnsigned => 0x1,
|
||||
Kind::Sub => 0x2,
|
||||
Kind::SubUnsigned => 0x3,
|
||||
Kind::And => 0x4,
|
||||
Kind::Or => 0x5,
|
||||
Kind::Xor => 0x6,
|
||||
Kind::Nor => 0x7,
|
||||
Kind::SetLessThan => 0xa,
|
||||
Kind::SetLessThanUnsigned => 0xb,
|
||||
};
|
||||
|
||||
let d = self.dst.idx();
|
||||
let s = self.lhs.idx();
|
||||
let t = self.rhs.idx();
|
||||
|
||||
Raw { f, t, d, s }
|
||||
}
|
||||
}
|
||||
132
dcb-exe/src/exe/inst/basic/cond.rs
Normal file
132
dcb-exe/src/exe/inst/basic/cond.rs
Normal file
@ -0,0 +1,132 @@
|
||||
//! Condition branches
|
||||
|
||||
// Imports
|
||||
use crate::exe::inst::Register;
|
||||
use dcb_util::SignedHex;
|
||||
use int_conv::{Signed, Truncated, ZeroExtended};
|
||||
use std::fmt;
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct Raw {
|
||||
/// Opcode (lower 3 bits)
|
||||
pub p: u32,
|
||||
|
||||
/// Rs
|
||||
pub s: u32,
|
||||
|
||||
/// Rt
|
||||
pub t: u32,
|
||||
|
||||
/// Immediate
|
||||
pub i: u32,
|
||||
}
|
||||
|
||||
/// Condition kind
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum Kind {
|
||||
/// Equal
|
||||
Equal(Register),
|
||||
|
||||
/// Not equal
|
||||
NotEqual(Register),
|
||||
|
||||
/// Less than or zero
|
||||
LessOrEqualZero,
|
||||
|
||||
/// Greater than zero
|
||||
GreaterThanZero,
|
||||
|
||||
/// Less than zero
|
||||
LessThanZero,
|
||||
|
||||
/// Greater than or zero
|
||||
GreaterOrEqualZero,
|
||||
|
||||
/// Less than zero and link
|
||||
LessThanZeroLink,
|
||||
|
||||
/// Greater than or zero and link
|
||||
GreaterOrEqualZeroLink,
|
||||
}
|
||||
|
||||
/// Condition instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct Inst {
|
||||
/// Argument, `rs`
|
||||
pub arg: Register,
|
||||
|
||||
/// Offset
|
||||
pub offset: i16,
|
||||
|
||||
/// Kind
|
||||
pub kind: Kind,
|
||||
}
|
||||
|
||||
impl Inst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: Raw) -> Option<Self> {
|
||||
let kind = match raw.p {
|
||||
0x1 => match raw.t {
|
||||
0b00000 => Kind::LessThanZero,
|
||||
0b00001 => Kind::GreaterOrEqualZero,
|
||||
0b10000 => Kind::LessThanZeroLink,
|
||||
0b10001 => Kind::GreaterOrEqualZeroLink,
|
||||
_ => return None,
|
||||
},
|
||||
0x4 => Kind::Equal(Register::new(raw.t)?),
|
||||
0x5 => Kind::NotEqual(Register::new(raw.t)?),
|
||||
0x6 => Kind::LessOrEqualZero,
|
||||
0x7 => Kind::GreaterThanZero,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(Self {
|
||||
arg: Register::new(raw.s)?,
|
||||
offset: raw.i.truncated::<u16>().as_signed(),
|
||||
kind,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> Raw {
|
||||
#[rustfmt::skip]
|
||||
let (p, t) = match self.kind {
|
||||
Kind::Equal(reg) => (0x4, reg.idx()),
|
||||
Kind::NotEqual(reg) => (0x5, reg.idx()),
|
||||
Kind::LessOrEqualZero => (0x6, 0),
|
||||
Kind::GreaterThanZero => (0x7, 0),
|
||||
Kind::LessThanZero => (0x1, 0b00000),
|
||||
Kind::GreaterOrEqualZero => (0x1, 0b00001),
|
||||
Kind::LessThanZeroLink => (0x1, 0b10000),
|
||||
Kind::GreaterOrEqualZeroLink => (0x1, 0b10001),
|
||||
};
|
||||
|
||||
let s = self.arg.idx();
|
||||
let i = self.offset.as_unsigned().zero_extended();
|
||||
|
||||
Raw { p, s, t, i }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Fmt given `pc` / label
|
||||
|
||||
impl fmt::Display for Inst {
|
||||
#[rustfmt::skip]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Self { arg, offset, kind } = self;
|
||||
|
||||
match kind {
|
||||
Kind::Equal(reg) => write!(f, "beq {arg}, {reg}, {}", SignedHex(offset)),
|
||||
Kind::NotEqual(reg) => write!(f, "bne {arg}, {reg}, {}", SignedHex(offset)),
|
||||
Kind::LessOrEqualZero => write!(f, "blez {arg}, {}" , SignedHex(offset)),
|
||||
Kind::GreaterThanZero => write!(f, "bgtz {arg}, {}" , SignedHex(offset)),
|
||||
Kind::LessThanZero => write!(f, "bltz {arg}, {}" , SignedHex(offset)),
|
||||
Kind::GreaterOrEqualZero => write!(f, "bgez {arg}, {}" , SignedHex(offset)),
|
||||
Kind::LessThanZeroLink => write!(f, "bltzal {arg}, {}" , SignedHex(offset)),
|
||||
Kind::GreaterOrEqualZeroLink => write!(f, "bgezal {arg}, {}" , SignedHex(offset)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,12 +12,12 @@ pub struct InstIter<'a, I: Iterator<Item = u32> + Clone> {
|
||||
|
||||
impl<'a, I: Iterator<Item = u32> + Clone> InstIter<'a, I> {
|
||||
/// Creates a new instruction iterator
|
||||
pub fn new(iter: &mut I) -> Self {
|
||||
pub fn new(iter: &'a mut I) -> Self {
|
||||
Self { iter }
|
||||
}
|
||||
|
||||
/// Reborrows this iterator with a smaller lifetime
|
||||
pub fn reborrow<'b>(&mut self) -> InstIter<'b, I>
|
||||
pub fn reborrow<'b>(&'b mut self) -> InstIter<'b, I>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
@ -50,7 +50,7 @@ pub struct InstPeeker<'a, I: Iterator<Item = u32> + Clone> {
|
||||
|
||||
impl<'a, I: Iterator<Item = u32> + Clone> InstPeeker<'a, I> {
|
||||
/// Creates a new peeker
|
||||
pub(self) fn new(iter: &mut InstIter<'a, I>) -> Self {
|
||||
pub(self) fn new(iter: InstIter<'a, I>) -> Self {
|
||||
Self {
|
||||
iter,
|
||||
last_iter: None,
|
||||
47
dcb-exe/src/exe/inst/basic/jmp.rs
Normal file
47
dcb-exe/src/exe/inst/basic/jmp.rs
Normal file
@ -0,0 +1,47 @@
|
||||
//! Jmp register instructions
|
||||
|
||||
// Modules
|
||||
pub mod imm;
|
||||
pub mod reg;
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::From)]
|
||||
pub enum Raw {
|
||||
/// Immediate
|
||||
Imm(imm::Raw),
|
||||
|
||||
/// Register
|
||||
Reg(reg::Raw),
|
||||
}
|
||||
|
||||
/// Jmp register instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum Inst {
|
||||
/// Immediate
|
||||
Imm(imm::Inst),
|
||||
|
||||
/// Register
|
||||
Reg(reg::Inst),
|
||||
}
|
||||
|
||||
impl Inst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: impl Into<Raw>) -> Option<Self> {
|
||||
match raw.into() {
|
||||
Raw::Imm(raw) => Some(Self::Imm(imm::Inst::decode(raw)?)),
|
||||
Raw::Reg(raw) => Some(Self::Reg(reg::Inst::decode(raw)?)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub const fn encode(self) -> Raw {
|
||||
match self {
|
||||
Self::Imm(inst) => Raw::Imm(inst.encode()),
|
||||
Self::Reg(inst) => Raw::Reg(inst.encode()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
/// Jmp immediate instruction kind
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum JmpImmInstKind {
|
||||
pub enum Kind {
|
||||
/// Jump
|
||||
Jump,
|
||||
|
||||
@ -10,19 +10,20 @@ pub enum JmpImmInstKind {
|
||||
JumpLink,
|
||||
}
|
||||
|
||||
impl JmpImmInstKind {
|
||||
impl Kind {
|
||||
/// Returns this kind's mnemonic
|
||||
pub fn mnemonic(self) -> &'static str {
|
||||
#[must_use]
|
||||
pub const fn mnemonic(self) -> &'static str {
|
||||
match self {
|
||||
JmpImmInstKind::Jump => "j",
|
||||
JmpImmInstKind::JumpLink => "jal",
|
||||
Self::Jump => "j",
|
||||
Self::JumpLink => "jal",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct JmpImmInstRaw {
|
||||
pub struct Raw {
|
||||
/// Opcode (lower bit)
|
||||
pub p: u32,
|
||||
|
||||
@ -34,21 +35,21 @@ pub struct JmpImmInstRaw {
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
#[display(fmt = "{} {target}", "kind.mnemonic()")]
|
||||
pub struct JmpImmInst {
|
||||
pub struct Inst {
|
||||
/// Target
|
||||
pub target: u32,
|
||||
|
||||
/// Kind
|
||||
pub kind: JmpImmInstKind,
|
||||
pub kind: Kind,
|
||||
}
|
||||
|
||||
impl JmpImmInst {
|
||||
impl Inst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: JmpImmInstRaw) -> Option<Self> {
|
||||
pub const fn decode(raw: Raw) -> Option<Self> {
|
||||
let kind = match raw.p {
|
||||
0 => JmpImmInstKind::Jump,
|
||||
1 => JmpImmInstKind::JumpLink,
|
||||
0 => Kind::Jump,
|
||||
1 => Kind::JumpLink,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
@ -57,13 +58,13 @@ impl JmpImmInst {
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> JmpImmInstRaw {
|
||||
let (p, i) = match self.kind {
|
||||
JmpImmInstKind::Jump => 0,
|
||||
JmpImmInstKind::JumpLink => 1,
|
||||
pub const fn encode(self) -> Raw {
|
||||
let p = match self.kind {
|
||||
Kind::Jump => 0,
|
||||
Kind::JumpLink => 1,
|
||||
};
|
||||
let i = self.target;
|
||||
|
||||
JmpImmInstRaw { p, i }
|
||||
Raw { p, i }
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,12 @@
|
||||
//! Jump register instructions
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::Register;
|
||||
use crate::exe::inst::Register;
|
||||
use std::fmt;
|
||||
|
||||
/// Jmp register instruction kind
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum JmpRegInstKind {
|
||||
pub enum Kind {
|
||||
/// Jump
|
||||
Jump,
|
||||
|
||||
@ -14,46 +14,47 @@ pub enum JmpRegInstKind {
|
||||
JumpLink(Register),
|
||||
}
|
||||
|
||||
impl JmpRegInstKind {
|
||||
impl Kind {
|
||||
/// Returns this kind's mnemonic
|
||||
pub fn mnemonic(self) -> &'static str {
|
||||
#[must_use]
|
||||
pub const fn mnemonic(self) -> &'static str {
|
||||
match self {
|
||||
JmpRegInstKind::Jump => "j",
|
||||
JmpRegInstKind::JumpLink(_) => "jal",
|
||||
Self::Jump => "jr",
|
||||
Self::JumpLink(_) => "jalr",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct JmpRegInstRaw {
|
||||
pub struct Raw {
|
||||
/// Rs
|
||||
s: u32,
|
||||
pub s: u32,
|
||||
|
||||
/// Rd
|
||||
d: u32,
|
||||
pub d: u32,
|
||||
|
||||
/// Func (lower bit)
|
||||
f: u32,
|
||||
pub f: u32,
|
||||
}
|
||||
|
||||
/// Jmp register instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct JmpRegInst {
|
||||
pub struct Inst {
|
||||
/// Target
|
||||
pub target: Register,
|
||||
|
||||
/// Kind
|
||||
pub kind: JmpRegInstKind,
|
||||
pub kind: Kind,
|
||||
}
|
||||
|
||||
impl JmpRegInst {
|
||||
impl Inst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: JmpRegInstRaw) -> Option<Self> {
|
||||
pub fn decode(raw: Raw) -> Option<Self> {
|
||||
let kind = match raw.f {
|
||||
0 => JmpRegInstKind::Jump,
|
||||
1 => JmpRegInstKind::JumpLink(Register::new(raw.d)?),
|
||||
0 => Kind::Jump,
|
||||
1 => Kind::JumpLink(Register::new(raw.d)?),
|
||||
_ => return None,
|
||||
};
|
||||
let target = Register::new(raw.s)?;
|
||||
@ -63,25 +64,25 @@ impl JmpRegInst {
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> JmpRegInstRaw {
|
||||
pub const fn encode(self) -> Raw {
|
||||
let (f, d) = match self.kind {
|
||||
JmpRegInstKind::Jump => (0, 0),
|
||||
JmpRegInstKind::JumpLink(reg) => (1, reg.idx()),
|
||||
Kind::Jump => (0, 0),
|
||||
Kind::JumpLink(reg) => (1, reg.idx()),
|
||||
};
|
||||
let s = self.target.idx();
|
||||
|
||||
JmpRegInstRaw { s, d, f }
|
||||
Raw { s, d, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for JmpRegInst {
|
||||
impl fmt::Display for Inst {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mnemonic = self.kind.mnemonic();
|
||||
let target = self.target;
|
||||
|
||||
match self.kind {
|
||||
JmpRegInstKind::Jump => write!(f, "{mnemonic} {target}"),
|
||||
JmpRegInstKind::JumpLink(reg) => write!(f, "{mnemonic} {target}, {reg}"),
|
||||
Kind::Jump => write!(f, "{mnemonic} {target}"),
|
||||
Kind::JumpLink(reg) => write!(f, "{mnemonic} {target}, {reg}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
//! Load instructions
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::Register;
|
||||
use crate::exe::inst::Register;
|
||||
use dcb_util::SignedHex;
|
||||
use int_conv::{Signed, Truncated, ZeroExtended};
|
||||
use std::convert::TryFrom;
|
||||
@ -12,7 +12,7 @@ use std::convert::TryFrom;
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum LoadKind {
|
||||
pub enum Kind {
|
||||
/// Byte, `i8`
|
||||
Byte = 0x0,
|
||||
|
||||
@ -35,7 +35,7 @@ pub enum LoadKind {
|
||||
WordRight = 0x6,
|
||||
}
|
||||
|
||||
impl LoadKind {
|
||||
impl Kind {
|
||||
/// Returns the mnemonic for this load kind
|
||||
#[must_use]
|
||||
pub const fn mnemonic(self) -> &'static str {
|
||||
@ -53,7 +53,7 @@ impl LoadKind {
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct LoadRaw {
|
||||
pub struct Raw {
|
||||
/// Opcode (lower 3 bits)
|
||||
pub p: u32,
|
||||
|
||||
@ -70,8 +70,8 @@ pub struct LoadRaw {
|
||||
/// Load instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
#[display(fmt = "{} {dst}, {:#x}({src})", "kind.mnemonic()", "SignedHex(offset)")]
|
||||
pub struct LoadInst {
|
||||
#[display(fmt = "{} {dst}, {}({src})", "kind.mnemonic()", "SignedHex(offset)")]
|
||||
pub struct Inst {
|
||||
/// Source register, `rt`
|
||||
pub src: Register,
|
||||
|
||||
@ -82,14 +82,14 @@ pub struct LoadInst {
|
||||
pub offset: i16,
|
||||
|
||||
/// Kind
|
||||
pub kind: LoadKind,
|
||||
pub kind: Kind,
|
||||
}
|
||||
|
||||
impl LoadInst {
|
||||
impl Inst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: LoadRaw) -> Option<Self> {
|
||||
let op = LoadKind::try_from(raw.p.truncated::<u8>()).ok()?;
|
||||
pub fn decode(raw: Raw) -> Option<Self> {
|
||||
let op = Kind::try_from(raw.p.truncated::<u8>()).ok()?;
|
||||
|
||||
Some(Self {
|
||||
src: Register::new(raw.t)?,
|
||||
@ -101,12 +101,12 @@ impl LoadInst {
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> LoadRaw {
|
||||
pub fn encode(self) -> Raw {
|
||||
let t = self.src.idx();
|
||||
let s = self.dst.idx();
|
||||
let i = self.offset.as_unsigned().zero_extended::<u32>();
|
||||
let p = u8::from(self.kind).zero_extended::<u32>();
|
||||
|
||||
LoadRaw { p, s, t, i }
|
||||
Raw { p, s, t, i }
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,12 @@
|
||||
//! Lui instruction
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::Register;
|
||||
use crate::exe::inst::Register;
|
||||
use int_conv::{Truncated, ZeroExtended};
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct LuiRaw {
|
||||
pub struct Raw {
|
||||
/// Rt
|
||||
pub t: u32,
|
||||
|
||||
@ -18,7 +18,7 @@ pub struct LuiRaw {
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
#[display(fmt = "lui {dst}, {value:#x}")]
|
||||
pub struct LuiInst {
|
||||
pub struct Inst {
|
||||
/// Destination register, `rt`
|
||||
pub dst: Register,
|
||||
|
||||
@ -26,10 +26,10 @@ pub struct LuiInst {
|
||||
pub value: u16,
|
||||
}
|
||||
|
||||
impl LuiInst {
|
||||
impl Inst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: LuiRaw) -> Option<Self> {
|
||||
pub fn decode(raw: Raw) -> Option<Self> {
|
||||
Some(Self {
|
||||
dst: Register::new(raw.t)?,
|
||||
value: raw.i.truncated::<u16>(),
|
||||
@ -38,8 +38,8 @@ impl LuiInst {
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> LuiRaw {
|
||||
LuiRaw {
|
||||
pub fn encode(self) -> Raw {
|
||||
Raw {
|
||||
t: self.dst.idx(),
|
||||
i: self.value.zero_extended::<u32>(),
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
//! Multiplications
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::Register;
|
||||
use crate::exe::inst::Register;
|
||||
use std::fmt;
|
||||
|
||||
/// Operation func
|
||||
@ -90,8 +90,8 @@ pub enum MultInst {
|
||||
impl MultInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
#[rustfmt::skip]
|
||||
pub fn decode(raw: MultRaw) -> Option<Self> {
|
||||
#[rustfmt::skip]
|
||||
Some(match raw.f {
|
||||
0x10 => Self::MoveFrom { dst: Register::new(raw.d)?, src: MultReg::Hi },
|
||||
0x12 => Self::MoveFrom { dst: Register::new(raw.d)?, src: MultReg::Lo },
|
||||
@ -110,7 +110,7 @@ impl MultInst {
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> MultRaw {
|
||||
pub const fn encode(self) -> MultRaw {
|
||||
match self {
|
||||
Self::Mult { kind, mode, lhs, rhs } => MultRaw {
|
||||
s: lhs.idx(),
|
||||
@ -1,7 +1,7 @@
|
||||
//! Store instructions
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::Register;
|
||||
use crate::exe::inst::Register;
|
||||
use dcb_util::SignedHex;
|
||||
use int_conv::{Signed, Truncated, ZeroExtended};
|
||||
use std::convert::TryFrom;
|
||||
@ -12,7 +12,7 @@ use std::convert::TryFrom;
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum StoreKind {
|
||||
pub enum Kind {
|
||||
/// Byte, `u8`
|
||||
Byte = 0x0,
|
||||
|
||||
@ -29,7 +29,7 @@ pub enum StoreKind {
|
||||
WordRight = 0x6,
|
||||
}
|
||||
|
||||
impl StoreKind {
|
||||
impl Kind {
|
||||
/// Returns the mnemonic for this store kind
|
||||
#[must_use]
|
||||
pub const fn mnemonic(self) -> &'static str {
|
||||
@ -45,7 +45,7 @@ impl StoreKind {
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct StoreRaw {
|
||||
pub struct Raw {
|
||||
/// Opcode (lower 3 bits)
|
||||
pub p: u32,
|
||||
|
||||
@ -62,8 +62,8 @@ pub struct StoreRaw {
|
||||
/// Store instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
#[display(fmt = "{} {dst}, {:#x}({src})", "kind.mnemonic()", "SignedHex(offset)")]
|
||||
pub struct StoreInst {
|
||||
#[display(fmt = "{} {dst}, {}({src})", "kind.mnemonic()", "SignedHex(offset)")]
|
||||
pub struct Inst {
|
||||
/// Source register, `rt`
|
||||
pub src: Register,
|
||||
|
||||
@ -74,14 +74,14 @@ pub struct StoreInst {
|
||||
pub offset: i16,
|
||||
|
||||
/// Kind
|
||||
pub kind: StoreKind,
|
||||
pub kind: Kind,
|
||||
}
|
||||
|
||||
impl StoreInst {
|
||||
impl Inst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: StoreRaw) -> Option<Self> {
|
||||
let kind = StoreKind::try_from(raw.p.truncated::<u8>()).ok()?;
|
||||
pub fn decode(raw: Raw) -> Option<Self> {
|
||||
let kind = Kind::try_from(raw.p.truncated::<u8>()).ok()?;
|
||||
|
||||
Some(Self {
|
||||
src: Register::new(raw.t)?,
|
||||
@ -93,12 +93,12 @@ impl StoreInst {
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> StoreRaw {
|
||||
pub fn encode(self) -> Raw {
|
||||
let t = self.src.idx();
|
||||
let s = self.dst.idx();
|
||||
let i = self.offset.as_unsigned().zero_extended::<u32>();
|
||||
let p = u8::from(self.kind).zero_extended::<u32>();
|
||||
|
||||
StoreRaw { p, s, t, i }
|
||||
Raw { p, s, t, i }
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
/// Sys instruction func
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum SysInstKind {
|
||||
pub enum Kind {
|
||||
/// Syscall
|
||||
Sys,
|
||||
|
||||
@ -10,19 +10,20 @@ pub enum SysInstKind {
|
||||
Break,
|
||||
}
|
||||
|
||||
impl SysInstKind {
|
||||
impl Kind {
|
||||
/// Returns the mnemonic associated with this syscall kind
|
||||
pub fn mnemonic(self) -> &'static str {
|
||||
#[must_use]
|
||||
pub const fn mnemonic(self) -> &'static str {
|
||||
match self {
|
||||
SysInstKind::Sys => "sys",
|
||||
SysInstKind::Break => "break",
|
||||
Self::Sys => "sys",
|
||||
Self::Break => "break",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct SysInstRaw {
|
||||
pub struct Raw {
|
||||
/// Comment
|
||||
pub c: u32,
|
||||
|
||||
@ -34,21 +35,21 @@ pub struct SysInstRaw {
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
#[display(fmt = "{}, {comment:#x}", "kind.mnemonic()")]
|
||||
pub struct SysInst {
|
||||
pub struct Inst {
|
||||
/// Comment
|
||||
pub comment: u32,
|
||||
|
||||
/// Kind
|
||||
pub kind: SysInstKind,
|
||||
pub kind: Kind,
|
||||
}
|
||||
|
||||
impl SysInst {
|
||||
impl Inst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(SysInstRaw { c, f }: SysInstRaw) -> Option<Self> {
|
||||
pub const fn decode(Raw { c, f }: Raw) -> Option<Self> {
|
||||
let kind = match f {
|
||||
0 => SysInstKind::Sys,
|
||||
1 => SysInstKind::Break,
|
||||
0 => Kind::Sys,
|
||||
1 => Kind::Break,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
@ -58,13 +59,13 @@ impl SysInst {
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
#[allow(clippy::many_single_char_names)] // `Raw` has single character names
|
||||
pub fn encode(self) -> SysInstRaw {
|
||||
let c = self.comment.to_be_bytes();
|
||||
pub const fn encode(self) -> Raw {
|
||||
let c = self.comment;
|
||||
let f = match self.kind {
|
||||
SysInstKind::Sys => 0,
|
||||
SysInstKind::Break => 1,
|
||||
Kind::Sys => 0,
|
||||
Kind::Break => 1,
|
||||
};
|
||||
|
||||
SysInstRaw { c, f }
|
||||
Raw { c, f }
|
||||
}
|
||||
}
|
||||
168
dcb-exe/src/exe/inst/directive.rs
Normal file
168
dcb-exe/src/exe/inst/directive.rs
Normal file
@ -0,0 +1,168 @@
|
||||
//! Directives
|
||||
|
||||
// Imports
|
||||
//use super::{FromRawIter, Instruction, Raw};
|
||||
use super::Inst;
|
||||
use crate::exe::Pos;
|
||||
use ascii::AsciiChar;
|
||||
use dcb_util::NextFromBytes;
|
||||
use std::ops::{
|
||||
Bound::{self, Excluded, Included, Unbounded},
|
||||
RangeBounds,
|
||||
};
|
||||
|
||||
/// A directive
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum Directive {
|
||||
/// Write word
|
||||
Dw(u32),
|
||||
|
||||
/// Write half-word
|
||||
Dh(u16),
|
||||
|
||||
/// Write byte
|
||||
Db(u8),
|
||||
|
||||
/// Ascii string
|
||||
Ascii {
|
||||
/// String length
|
||||
len: u32,
|
||||
},
|
||||
}
|
||||
|
||||
/// A force decode range
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct ForceDecodeRange {
|
||||
/// Start bound
|
||||
start: Bound<Pos>,
|
||||
|
||||
/// End bound
|
||||
end: Bound<Pos>,
|
||||
|
||||
/// Decoding kind
|
||||
kind: ForceDecodeKind,
|
||||
}
|
||||
|
||||
impl RangeBounds<Pos> for ForceDecodeRange {
|
||||
fn start_bound(&self) -> Bound<&Pos> {
|
||||
match self.start {
|
||||
Included(ref start) => Included(start),
|
||||
Excluded(ref start) => Excluded(start),
|
||||
Unbounded => Unbounded,
|
||||
}
|
||||
}
|
||||
|
||||
fn end_bound(&self) -> Bound<&Pos> {
|
||||
match self.end {
|
||||
Included(ref end) => Included(end),
|
||||
Excluded(ref end) => Excluded(end),
|
||||
Unbounded => Unbounded,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Force decode range kind
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum ForceDecodeKind {
|
||||
/// Single Word
|
||||
Word,
|
||||
|
||||
/// Half word
|
||||
HalfWord,
|
||||
|
||||
/// Bytes
|
||||
Byte,
|
||||
}
|
||||
|
||||
impl Directive {
|
||||
/// Positions that should be force decoded using a specific variant.
|
||||
// TODO: Get this at run-time via a file.
|
||||
pub const FORCE_DECODE_RANGES: &'static [ForceDecodeRange] = &[
|
||||
ForceDecodeRange {
|
||||
start: Included(Pos(0x80010000)),
|
||||
end: Excluded(Pos(0x80010008)),
|
||||
kind: ForceDecodeKind::Word,
|
||||
},
|
||||
ForceDecodeRange {
|
||||
start: Included(Pos(0x8006fa20)),
|
||||
end: Excluded(Pos(0x8006fa24)),
|
||||
kind: ForceDecodeKind::HalfWord,
|
||||
},
|
||||
ForceDecodeRange {
|
||||
start: Included(Inst::CODE_END),
|
||||
end: Unbounded,
|
||||
kind: ForceDecodeKind::Word,
|
||||
},
|
||||
];
|
||||
|
||||
/// Returns the size of this directive
|
||||
#[must_use]
|
||||
pub const fn size(self) -> u32 {
|
||||
match self {
|
||||
Self::Dw(_) => 4,
|
||||
Self::Dh(_) => 2,
|
||||
Self::Db(_) => 1,
|
||||
// Round ascii strings' len up to the
|
||||
// nearest word (or one after if exactly 1 word).
|
||||
Self::Ascii { len } => len + 4 - (len % 4),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Directive {
|
||||
/// Decodes a directive
|
||||
#[must_use]
|
||||
pub fn decode(pos: Pos, bytes: &[u8]) -> Option<(Self, usize)> {
|
||||
// Check if we need to force decode it
|
||||
if let Some(ForceDecodeRange { kind, .. }) = Self::FORCE_DECODE_RANGES.iter().find(|range| range.contains(&pos)) {
|
||||
#[rustfmt::skip]
|
||||
return match kind {
|
||||
ForceDecodeKind::Word => bytes.next_u32().map(|value| (Self::Dw(value), 4)),
|
||||
ForceDecodeKind::HalfWord => bytes.next_u16().map(|value| (Self::Dh(value), 2)),
|
||||
ForceDecodeKind::Byte => bytes.next_u8 ().map(|value| (Self::Db(value), 1)),
|
||||
};
|
||||
}
|
||||
|
||||
// Else try to get a string
|
||||
if let Some((str_len, with_nulls_len)) = self::read_ascii_until_null(pos, bytes) {
|
||||
debug_assert!(with_nulls_len % 4 == 0, "Ascii string length wasn't multiple of 4");
|
||||
return Some((Self::Ascii { len: str_len }, with_nulls_len));
|
||||
}
|
||||
|
||||
// Else try to read a `u32`
|
||||
if let Some(value) = bytes.next_u32() {
|
||||
return Some((Self::Dw(value), 4));
|
||||
}
|
||||
|
||||
// Else read a single byte
|
||||
bytes.next_u8().map(|value| (Self::Db(value), 1))
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads an ascii string from a byte slice until null.
|
||||
///
|
||||
/// Will always read in multiples of a word (4 bytes), including the null.
|
||||
#[allow(clippy::as_conversions, clippy::cast_possible_truncation)] // Our length will always fit into a `u32`.
|
||||
fn read_ascii_until_null(pos: Pos, bytes: &[u8]) -> Option<(u32, usize)> {
|
||||
// Get the next null or invalid character
|
||||
let (idx, null) = bytes.iter().enumerate().find_map(|(idx, &byte)| match AsciiChar::from_ascii(byte) {
|
||||
Ok(AsciiChar::Null) => Some((idx, true)),
|
||||
Err(_) => Some((idx, false)),
|
||||
_ => None,
|
||||
})?;
|
||||
|
||||
// If it wasn't a null or the first character was a null, return None
|
||||
if !null || idx == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Else make sure until the end of the word it's all nulls
|
||||
let nulls_len = 4 - ((pos.0 as usize + idx) % 4);
|
||||
let nulls = bytes.get(idx..idx + nulls_len)?;
|
||||
if !nulls.iter().all(|&byte| byte == 0) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Else return both lengths
|
||||
Some((idx as u32, idx + nulls_len))
|
||||
}
|
||||
@ -5,29 +5,23 @@
|
||||
|
||||
// Modules
|
||||
pub mod alu_assign;
|
||||
pub mod jmp;
|
||||
pub mod load;
|
||||
pub mod load_imm;
|
||||
pub mod move_reg;
|
||||
pub mod nop;
|
||||
pub mod store;
|
||||
|
||||
// Exports
|
||||
pub use alu_assign::AluAssignInst;
|
||||
pub use jmp::JmpPseudoInst;
|
||||
pub use load::LoadPseudoInst;
|
||||
pub use load_imm::LoadImmInst;
|
||||
pub use move_reg::MoveRegPseudoInst;
|
||||
pub use nop::NopInst;
|
||||
pub use store::StorePseudoInst;
|
||||
//pub mod jmp;
|
||||
//pub mod load;
|
||||
//pub mod load_imm;
|
||||
//pub mod move_reg;
|
||||
//pub mod nop;
|
||||
//pub mod store;
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::basic::InstIter;
|
||||
use super::basic;
|
||||
|
||||
/// A pseudo instruction
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum PseudoInst {
|
||||
/// Alu self-assign
|
||||
AluAssign(alu_assign::Inst),
|
||||
/*
|
||||
/// Load
|
||||
Load(LoadPseudoInst),
|
||||
|
||||
@ -43,8 +37,7 @@ pub enum PseudoInst {
|
||||
/// No-op
|
||||
Nop(NopInst),
|
||||
|
||||
/// Alu self-assign
|
||||
AluAssign(AluAssignInst),
|
||||
*/
|
||||
/*
|
||||
/// Subtract immediate
|
||||
/// Alias for `addi $rt, $rs, -imm`
|
||||
@ -68,6 +61,16 @@ pub enum PseudoInst {
|
||||
*/
|
||||
}
|
||||
|
||||
impl PseudoInst {
|
||||
/// Attempts to parse a pseudo instruction from a start
|
||||
/// basic instruction and remaining bytes
|
||||
#[must_use]
|
||||
pub fn decode(inst: basic::Inst, bytes: &[u8]) -> Option<(Self, usize)> {
|
||||
alu_assign::Inst::decode(inst, bytes).map(|(inst, len)| (Self::AluAssign(inst), len))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl PseudoInst {
|
||||
pub fn decode(iter: InstIter<'_, impl Iterator<Item = u32> + Clone>) -> Option<Self> {
|
||||
LoadPseudoInst::decode(iter)
|
||||
@ -79,3 +82,4 @@ impl PseudoInst {
|
||||
.or_else(|| JmpPseudoInst::decode(iter))
|
||||
}
|
||||
}
|
||||
*/
|
||||
96
dcb-exe/src/exe/inst/pseudo/alu_assign.rs
Normal file
96
dcb-exe/src/exe/inst/pseudo/alu_assign.rs
Normal file
@ -0,0 +1,96 @@
|
||||
//! Alu self-assign instructions
|
||||
|
||||
// Imports
|
||||
use crate::exe::inst::{
|
||||
basic::{self, alu},
|
||||
Register,
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
/// Alu assign kind
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum Kind {
|
||||
/// Immediate
|
||||
Imm {
|
||||
/// Kind
|
||||
kind: alu::imm::Kind,
|
||||
},
|
||||
|
||||
/// Register
|
||||
Reg {
|
||||
/// Kind
|
||||
kind: alu::reg::Kind,
|
||||
|
||||
/// Argument
|
||||
rhs: Register,
|
||||
},
|
||||
}
|
||||
|
||||
impl Kind {
|
||||
/// Returns this kind's mnemonic
|
||||
#[must_use]
|
||||
pub const fn mnemonic(self) -> &'static str {
|
||||
match self {
|
||||
Self::Imm { kind } => kind.mnemonic(),
|
||||
Self::Reg { kind, .. } => kind.mnemonic(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a displayable with the value of this kind
|
||||
#[must_use]
|
||||
pub fn value_fmt(self) -> impl fmt::Display {
|
||||
/// Display wrapper
|
||||
struct FmtValue(Kind);
|
||||
|
||||
impl fmt::Display for FmtValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.0 {
|
||||
Kind::Imm { kind } => write!(f, "{}", kind.value_fmt()),
|
||||
Kind::Reg { rhs, .. } => write!(f, "{}", rhs),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FmtValue(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Alu self-assign instructions
|
||||
///
|
||||
/// Alias for
|
||||
/// ```mips
|
||||
/// [alu] $dst, $dst, $i
|
||||
/// ```
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
#[display(fmt = "{} {dst}, {}", "kind.mnemonic()", "kind.value_fmt()")]
|
||||
pub struct Inst {
|
||||
/// Destination and source register
|
||||
pub dst: Register,
|
||||
|
||||
/// Kind
|
||||
pub kind: Kind,
|
||||
}
|
||||
|
||||
impl Inst {
|
||||
/// Decodes this pseudo instruction
|
||||
#[must_use]
|
||||
pub fn decode(inst: basic::Inst, _bytes: &[u8]) -> Option<(Self, usize)> {
|
||||
let inst = match inst {
|
||||
basic::Inst::Alu(inst) => match inst {
|
||||
alu::Inst::Imm(alu::imm::Inst { dst, lhs, kind }) if dst == lhs => Some(Self {
|
||||
dst,
|
||||
kind: Kind::Imm { kind },
|
||||
}),
|
||||
alu::Inst::Reg(alu::reg::Inst { dst, lhs, rhs, kind }) if dst == lhs => Some(Self {
|
||||
dst,
|
||||
kind: Kind::Reg { kind, rhs },
|
||||
}),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
inst.map(|inst| (inst, 0))
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
//! Jump pseudo instructions
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::{
|
||||
use crate::exe::inst::{
|
||||
basic::{
|
||||
cond::CondKind,
|
||||
special::{jmp::JmpKind, JmpInst},
|
||||
@ -1,7 +1,7 @@
|
||||
//! Load instructions
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::{
|
||||
use crate::exe::inst::{
|
||||
basic::{load::LoadKind, InstIter},
|
||||
BasicInst, Register,
|
||||
};
|
||||
@ -1,7 +1,7 @@
|
||||
//! Load immediate
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::{
|
||||
use crate::exe::inst::{
|
||||
basic::{alu_imm::AluImmKind, AluImmInst, InstIter},
|
||||
BasicInst, Register,
|
||||
};
|
||||
@ -48,13 +48,13 @@ impl LoadImmKind {
|
||||
struct FmtValue(Self);
|
||||
|
||||
impl fmt::Display for FmtValue {
|
||||
#[rustfmt::skip]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
#[rustfmt::skip]
|
||||
match self.0 {
|
||||
LoadImmKind::Address(address) => write!(f, "{address:#x}"),
|
||||
LoadImmKind::Word(value) => write!(f, "{value:#x}"),
|
||||
LoadImmKind::HalfWordUnsigned(value) => write!(f, "{value:#x}"),
|
||||
LoadImmKind::HalfWordSigned(value) => write!(f, "{:#x}", SignedHex(value)),
|
||||
LoadImmKind::HalfWordSigned(value) => write!(f, "{}", SignedHex(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
//! Move register instruction
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::{
|
||||
use crate::exe::inst::{
|
||||
basic::{
|
||||
alu_imm::AluImmKind,
|
||||
special::{
|
||||
@ -1,7 +1,7 @@
|
||||
//! Nop
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::{
|
||||
use crate::exe::inst::{
|
||||
basic::{
|
||||
special::{
|
||||
shift::{reg::ShiftRegFunc, ShiftImmInst},
|
||||
@ -1,7 +1,7 @@
|
||||
//! Store instructions
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::{
|
||||
use crate::exe::inst::{
|
||||
basic::{store::StoreKind, InstIter},
|
||||
BasicInst, Register,
|
||||
};
|
||||
@ -13,6 +13,7 @@ pub struct Raw {
|
||||
pub pos: Pos,
|
||||
}
|
||||
|
||||
/*
|
||||
/// Raw instruction decoding
|
||||
///
|
||||
/// Implementors should be atomic about the consumed and
|
||||
@ -25,3 +26,4 @@ pub trait FromRawIter: Sized {
|
||||
/// Attempts to decode an instruction from an iterator of raw instructions
|
||||
fn decode<I: Iterator<Item = Raw> + Clone>(iter: &mut I) -> Self::Decoded;
|
||||
}
|
||||
*/
|
||||
@ -66,7 +66,7 @@ macro_rules! generate_register {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.trim() {
|
||||
match s {
|
||||
$(
|
||||
$fmt => Ok(Self::$variant),
|
||||
)*
|
||||
@ -95,7 +95,7 @@ impl Register {
|
||||
|
||||
/// Checks if this register is an argument register
|
||||
#[must_use]
|
||||
pub fn is_arg(self) -> bool {
|
||||
pub const fn is_arg(self) -> bool {
|
||||
self.arg_idx().is_some()
|
||||
}
|
||||
}
|
||||
@ -1,133 +0,0 @@
|
||||
//! Psx cpu instructions
|
||||
|
||||
// Modules
|
||||
pub mod basic;
|
||||
//pub mod directive;
|
||||
//pub mod pseudo;
|
||||
pub mod raw;
|
||||
pub mod reg;
|
||||
|
||||
// Exports
|
||||
pub use basic::BasicInst;
|
||||
//pub use directive::Directive;
|
||||
//pub use pseudo::PseudoInst;
|
||||
pub use raw::{FromRawIter, Raw};
|
||||
pub use reg::Register;
|
||||
|
||||
// Imports
|
||||
use crate::exe::Pos;
|
||||
|
||||
/// An assembler instruction
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub struct Instruction {
|
||||
/*
|
||||
/// A basic instruction
|
||||
Basic(BasicInst),
|
||||
|
||||
/// A pseudo instruction
|
||||
Pseudo(PseudoInst),
|
||||
|
||||
/// A directive
|
||||
Directive(Directive),
|
||||
*/}
|
||||
|
||||
impl Instruction {
|
||||
/// End of the code itself in the executable.
|
||||
pub const CODE_END: Pos = Pos(0x8006dd3c);
|
||||
/// Start of the code itself in the executable.
|
||||
pub const CODE_START: Pos = Pos(0x80013e4c);
|
||||
}
|
||||
|
||||
/// Iterator adaptor for converting [`RawInstruction`]s into [`Instruction`]s.
|
||||
pub struct Iter<I: Iterator<Item = Raw> + Clone> {
|
||||
/// Underlying iterator
|
||||
iter: I,
|
||||
|
||||
/// Remaining items from last iterator
|
||||
remaining: Option<Box<dyn Iterator<Item = (Pos, Instruction)>>>,
|
||||
}
|
||||
|
||||
impl<I: Iterator<Item = Raw> + Clone> Iter<I> {
|
||||
/*
|
||||
/// Helper function to try to decode without consuming the iterator
|
||||
fn try_decode<T: FromRawIter>(iter: &I) -> (I, T::Decoded) {
|
||||
let mut cloned_iter = iter.clone();
|
||||
let decoded = T::decode(&mut cloned_iter);
|
||||
(cloned_iter, decoded)
|
||||
}
|
||||
|
||||
/// Helper function to try to get instructions from `T`.
|
||||
fn try_next_from<T: FromRawIter + 'static>(&mut self, to_instruction: fn(T) -> Instruction) -> Option<(Pos, Instruction)> {
|
||||
// Try to decode it and get all instructions
|
||||
let (iter, instructions) = Self::try_decode::<T>(&self.iter);
|
||||
|
||||
// Map the instructions to be an iterator over `Instruction` and peekable
|
||||
let mut instructions = instructions
|
||||
.into_iter()
|
||||
.map(move |(pos, decoded)| (pos, to_instruction(decoded)))
|
||||
.peekable();
|
||||
|
||||
// Then check if we got any from the decode
|
||||
match instructions.next() {
|
||||
// If we did, set our iter, set any remaining instructions and return the instruction
|
||||
Some(instruction) => {
|
||||
self.iter = iter;
|
||||
// If there are any instructions left, set remaining, else just leave it
|
||||
if instructions.peek().is_some() {
|
||||
self.remaining = Some(Box::new(instructions));
|
||||
}
|
||||
Some(instruction)
|
||||
},
|
||||
|
||||
// Else we didn't get anything, don't update the iterator.
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/// Returns the current position of the iterator
|
||||
fn cur_pos(&self) -> Option<Pos> {
|
||||
self.iter.clone().next().map(|raw| raw.pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Iterator<Item = Raw> + Clone> Iterator for Iter<I> {
|
||||
type Item = (Pos, Instruction);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// If we have remaining instruction, supply them
|
||||
if let Some(remaining) = self.remaining.as_mut() {
|
||||
if let Some(instruction) = remaining.next() {
|
||||
return Some(instruction);
|
||||
} else {
|
||||
// Note: We set it to none in case `next` is expensive to check.
|
||||
self.remaining = None;
|
||||
}
|
||||
}
|
||||
|
||||
// Else get the current position
|
||||
let cur_pos = self.cur_pos()?;
|
||||
|
||||
// If we're before the code start, just read directives
|
||||
if cur_pos < Instruction::CODE_START || cur_pos >= Instruction::CODE_END {
|
||||
todo!();
|
||||
//return self.try_next_from(Instruction::Directive);
|
||||
}
|
||||
|
||||
// Else try to decode it as a pseudo, basic or directive, in that order.
|
||||
todo!()
|
||||
/*
|
||||
self.try_next_from(Instruction::Pseudo)
|
||||
.or_else(|| self.try_next_from(Instruction::Basic))
|
||||
.or_else(|| self.try_next_from(Instruction::Directive))
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
/// Adapts an iterator over raw words to an instruction iterator
|
||||
pub fn new_iter<I: Iterator<Item = Raw> + Clone>(iter: I) -> Iter<I> {
|
||||
Iter { iter, remaining: None }
|
||||
}
|
||||
}
|
||||
@ -1,107 +0,0 @@
|
||||
//! Basic instructions
|
||||
//!
|
||||
//! This modules defines all the basic instructions from the psx.
|
||||
//! They are all 1 word (`u32`) long.
|
||||
|
||||
// Modules
|
||||
pub mod alu;
|
||||
pub mod cond;
|
||||
pub mod iter;
|
||||
pub mod jmp;
|
||||
pub mod load;
|
||||
pub mod lui;
|
||||
pub mod mult;
|
||||
pub mod store;
|
||||
pub mod sys;
|
||||
|
||||
// Exports
|
||||
pub use alu::{AluInst, AluInstRaw};
|
||||
pub use cond::{CondInst, CondRaw};
|
||||
pub use iter::InstIter;
|
||||
pub use jmp::{JmpInst, JmpInstRaw};
|
||||
pub use load::{LoadInst, LoadRaw};
|
||||
pub use lui::{LuiInst, LuiRaw};
|
||||
//pub use special::{SpecialInst, SpecialRaw};
|
||||
pub use store::{StoreInst, StoreRaw};
|
||||
pub use sys::{SysInst, SysInstRaw};
|
||||
|
||||
/// All basic instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum BasicInst {
|
||||
/// Store
|
||||
Store(StoreInst),
|
||||
|
||||
/// Load
|
||||
Load(LoadInst),
|
||||
|
||||
/// Condition
|
||||
Cond(CondInst),
|
||||
|
||||
/// Jump
|
||||
Jmp(JmpInst),
|
||||
|
||||
/// Alu
|
||||
Alu(AluInst),
|
||||
|
||||
/// Load upper immediate
|
||||
Lui(LuiInst),
|
||||
|
||||
// Syscall
|
||||
Sys(SysInst),
|
||||
}
|
||||
|
||||
impl BasicInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
#[bitmatch::bitmatch]
|
||||
#[allow(clippy::many_single_char_names)] // `bitmatch` can only output single character names.
|
||||
pub fn decode(raw: u32) -> Option<Self> {
|
||||
use BasicInst::*;
|
||||
let inst = #[bitmatch]
|
||||
match raw {
|
||||
// Jump
|
||||
"00001p_iiiii_iiiii_iiiii_iiiii_iiiiii" => Jmp(JmpInst::decode(JmpInstRaw::Imm(jmp::JmpImmInstRaw { p, i }))),
|
||||
"000000_sssss_?????_ddddd_?????_00100f" => Jmp(JmpInst::decode(JmpInstRaw::Reg(jmp::JmpRegInstRaw { s, d, f }))),
|
||||
|
||||
"000ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Cond(CondInst::decode(CondRaw { p, s, t, i })?),
|
||||
"001111_?????_ttttt_iiiii_iiiii_iiiiii" => Lui(LuiInst::decode(LuiRaw { t, i })?),
|
||||
|
||||
// Alu
|
||||
"000000_sssss_ttttt_ddddd_?????_10ffff" => Alu(AluInst::decode(AluInstRaw::Imm(alu::AluRegInstRaw { s, t, d, f }))?),
|
||||
"001ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Alu(AluInst::decode(AluInstRaw::Reg(alu::AluImmInstRaw { p, s, t, i }))?),
|
||||
|
||||
// Syscall
|
||||
"000000_ccccc_ccccc_ccccc_ccccc_00110f" => Sys(SysInst::decode(SysInstRaw { c, f })?),
|
||||
|
||||
// Store / Load
|
||||
"100ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Store(StoreInst::decode(StoreRaw { p, s, t, i })?),
|
||||
"101ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Load(LoadInst::decode(LoadRaw { p, s, t, i })?),
|
||||
|
||||
/*
|
||||
"0100nn_1iiii_iiiii_iiiii_iiiii_iiiiii" => CopN { n: n.truncate(), imm: i},
|
||||
"0100nn_00000_ttttt_ddddd_?????_000000" => MfcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
|
||||
"0100nn_00010_ttttt_ddddd_?????_000000" => CfcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
|
||||
"0100nn_00100_ttttt_ddddd_?????_000000" => MtcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
|
||||
"0100nn_00110_ttttt_ddddd_?????_000000" => CtcN { n: n.truncate(), rt: reg(t)?, rd: reg(d)? },
|
||||
"0100nn_01000_00000_iiiii_iiiii_iiiiii" => BcNf { n: n.truncate(), target: i.truncate() },
|
||||
"0100nn_01000_00001_iiiii_iiiii_iiiiii" => BcNt { n: n.truncate(), target: i.truncate() },
|
||||
"1100nn_sssss_ttttt_iiiii_iiiii_iiiiii" => LwcN { n: n.truncate(), rs: reg(s)?, rt: reg(t)?, imm: i.truncate() },
|
||||
"1110nn_sssss_ttttt_iiiii_iiiii_iiiiii" => SwcN { n: n.truncate(), rs: reg(s)?, rt: reg(t)?, imm: i.truncate() },
|
||||
*/
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(inst)
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
#[bitmatch::bitmatch]
|
||||
pub fn encode(self) -> u32 {
|
||||
#[rustfmt::skip]
|
||||
match self {
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
//! Alu instructions
|
||||
|
||||
// Modules
|
||||
pub mod imm;
|
||||
pub mod reg;
|
||||
|
||||
// Exports
|
||||
pub use imm::{AluImmInst, AluImmInstKind, AluImmInstRaw};
|
||||
pub use reg::{AluRegInst, AluRegInstKind, AluRegInstRaw};
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum AluInstRaw {
|
||||
/// Immediate
|
||||
Imm(AluImmInstRaw),
|
||||
|
||||
/// Register
|
||||
Reg(AluRegInstRaw),
|
||||
}
|
||||
|
||||
/// Alu register instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum AluInst {
|
||||
/// Immediate
|
||||
Imm(AluImmInst),
|
||||
|
||||
/// Register
|
||||
Reg(AluRegInst),
|
||||
}
|
||||
|
||||
impl AluInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: AluInstRaw) -> Option<Self> {
|
||||
match raw {
|
||||
AluInstRaw::Imm(raw) => Self::Imm(AluImmInst::decode(raw)?),
|
||||
AluInstRaw::Reg(raw) => Self::Reg(AluRegInst::decode(raw)?),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> AluInstRaw {
|
||||
match self {
|
||||
AluInst::Imm(inst) => AluInstRaw::Imm(inst.encode()),
|
||||
AluInst::Reg(inst) => AluInstRaw::Reg(inst.encode()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,133 +0,0 @@
|
||||
//! Alu register instructions
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::Register;
|
||||
|
||||
/// Alu register instruction kind
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum AluRegInstKind {
|
||||
/// Add signed with overflow trap
|
||||
#[display(fmt = "add")]
|
||||
Add,
|
||||
|
||||
/// Add signed without overflow trap
|
||||
#[display(fmt = "addu")]
|
||||
AddUnsigned,
|
||||
|
||||
/// Sub signed with overflow trap
|
||||
#[display(fmt = "sub")]
|
||||
Sub,
|
||||
|
||||
/// Sub signed without overflow trap
|
||||
#[display(fmt = "subu")]
|
||||
SubUnsigned,
|
||||
|
||||
/// Bit and
|
||||
#[display(fmt = "and")]
|
||||
And,
|
||||
|
||||
/// Bit or
|
||||
#[display(fmt = "or")]
|
||||
Or,
|
||||
|
||||
/// Bit xor
|
||||
#[display(fmt = "xor")]
|
||||
Xor,
|
||||
|
||||
/// Bit nor
|
||||
#[display(fmt = "nor")]
|
||||
Nor,
|
||||
|
||||
/// Set on less than signed
|
||||
#[display(fmt = "slt")]
|
||||
SetLessThan,
|
||||
|
||||
/// Set on less than unsigned
|
||||
#[display(fmt = "sltu")]
|
||||
SetLessThanUnsigned,
|
||||
}
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct AluRegInstRaw {
|
||||
/// Rs
|
||||
s: u32,
|
||||
|
||||
/// Rt
|
||||
t: u32,
|
||||
|
||||
/// Rd
|
||||
d: u32,
|
||||
|
||||
/// Func (lower 4 bits)
|
||||
f: u32,
|
||||
}
|
||||
|
||||
/// Alu register instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
#[display(fmt = "{kind} {dst}, {lhs}, {rhs}")]
|
||||
pub struct AluRegInst {
|
||||
/// Destination register
|
||||
pub dst: Register,
|
||||
|
||||
/// Lhs argument
|
||||
pub lhs: Register,
|
||||
|
||||
/// Rhs argument
|
||||
pub rhs: Register,
|
||||
|
||||
/// Kind
|
||||
pub kind: AluRegInstKind,
|
||||
}
|
||||
|
||||
impl AluRegInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: AluRegInstRaw) -> Option<Self> {
|
||||
let kind = match raw.f {
|
||||
0x0 => AluRegInstKind::Add,
|
||||
0x1 => AluRegInstKind::AddUnsigned,
|
||||
0x2 => AluRegInstKind::Sub,
|
||||
0x3 => AluRegInstKind::SubUnsigned,
|
||||
0x4 => AluRegInstKind::And,
|
||||
0x5 => AluRegInstKind::Or,
|
||||
0x6 => AluRegInstKind::Xor,
|
||||
0x7 => AluRegInstKind::Nor,
|
||||
0xa => AluRegInstKind::SetLessThan,
|
||||
0xb => AluRegInstKind::SetLessThanUnsigned,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(Self {
|
||||
dst: Register::new(raw.d)?,
|
||||
lhs: Register::new(raw.s)?,
|
||||
rhs: Register::new(raw.t)?,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> AluRegInstRaw {
|
||||
let f = match self.kind {
|
||||
AluRegInstKind::Add => 0x0,
|
||||
AluRegInstKind::AddUnsigned => 0x1,
|
||||
AluRegInstKind::Sub => 0x2,
|
||||
AluRegInstKind::SubUnsigned => 0x3,
|
||||
AluRegInstKind::And => 0x4,
|
||||
AluRegInstKind::Or => 0x5,
|
||||
AluRegInstKind::Xor => 0x6,
|
||||
AluRegInstKind::Nor => 0x7,
|
||||
AluRegInstKind::SetLessThan => 0xa,
|
||||
AluRegInstKind::SetLessThanUnsigned => 0xb,
|
||||
};
|
||||
|
||||
let d = self.dst.idx();
|
||||
let s = self.lhs.idx();
|
||||
let t = self.rhs.idx();
|
||||
|
||||
AluRegInstRaw { f, t, d, s }
|
||||
}
|
||||
}
|
||||
@ -1,132 +0,0 @@
|
||||
//! Condition branches
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::Register;
|
||||
use dcb_util::SignedHex;
|
||||
use int_conv::{Signed, Truncated, ZeroExtended};
|
||||
use std::fmt;
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct CondRaw {
|
||||
/// Opcode (lower 3 bits)
|
||||
pub p: u32,
|
||||
|
||||
/// Rs
|
||||
pub s: u32,
|
||||
|
||||
/// Rt
|
||||
pub t: u32,
|
||||
|
||||
/// Immediate
|
||||
pub i: u32,
|
||||
}
|
||||
|
||||
/// Condition kind
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum CondKind {
|
||||
/// Equal
|
||||
Equal(Register),
|
||||
|
||||
/// Not equal
|
||||
NotEqual(Register),
|
||||
|
||||
/// Less than or zero
|
||||
LessOrEqualZero,
|
||||
|
||||
/// Greater than zero
|
||||
GreaterThanZero,
|
||||
|
||||
/// Less than zero
|
||||
LessThanZero,
|
||||
|
||||
/// Greater than or zero
|
||||
GreaterOrEqualZero,
|
||||
|
||||
/// Less than zero and link
|
||||
LessThanZeroLink,
|
||||
|
||||
/// Greater than or zero and link
|
||||
GreaterOrEqualZeroLink,
|
||||
}
|
||||
|
||||
/// Condition instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct CondInst {
|
||||
/// Argument, `rs`
|
||||
pub arg: Register,
|
||||
|
||||
/// Offset
|
||||
pub offset: i16,
|
||||
|
||||
/// Kind
|
||||
pub kind: CondKind,
|
||||
}
|
||||
|
||||
impl CondInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: CondRaw) -> Option<Self> {
|
||||
let kind = match raw.p {
|
||||
0x1 => match raw.t {
|
||||
0b00000 => CondKind::LessThanZero,
|
||||
0b00001 => CondKind::GreaterOrEqualZero,
|
||||
0b10000 => CondKind::LessThanZeroLink,
|
||||
0b10001 => CondKind::GreaterOrEqualZeroLink,
|
||||
_ => return None,
|
||||
},
|
||||
0x4 => CondKind::Equal(Register::new(raw.t)?),
|
||||
0x5 => CondKind::NotEqual(Register::new(raw.t)?),
|
||||
0x6 => CondKind::LessOrEqualZero,
|
||||
0x7 => CondKind::GreaterThanZero,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(Self {
|
||||
arg: Register::new(raw.s)?,
|
||||
offset: raw.i.truncated::<u16>().as_signed(),
|
||||
kind,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> CondRaw {
|
||||
#[rustfmt::skip]
|
||||
let (p, t) = match self.kind {
|
||||
CondKind::Equal(reg) => (0x4, reg.idx()),
|
||||
CondKind::NotEqual(reg) => (0x5, reg.idx()),
|
||||
CondKind::LessOrEqualZero => (0x6, 0),
|
||||
CondKind::GreaterThanZero => (0x7, 0),
|
||||
CondKind::LessThanZero => (0x1, 0b00000),
|
||||
CondKind::GreaterOrEqualZero => (0x1, 0b00001),
|
||||
CondKind::LessThanZeroLink => (0x1, 0b10000),
|
||||
CondKind::GreaterOrEqualZeroLink => (0x1, 0b10001),
|
||||
};
|
||||
|
||||
let s = self.arg.idx();
|
||||
let i = self.offset.as_unsigned().zero_extended();
|
||||
|
||||
CondRaw { p, s, t, i }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Fmt given `pc` / label
|
||||
|
||||
impl fmt::Display for CondInst {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Self { arg, offset, kind } = self;
|
||||
|
||||
#[rustfmt::skip]
|
||||
match kind {
|
||||
CondKind::Equal(reg) => write!(f, "beq {arg}, {reg}, {:#x}", SignedHex(offset)),
|
||||
CondKind::NotEqual(reg) => write!(f, "bne {arg}, {reg}, {:#x}", SignedHex(offset)),
|
||||
CondKind::LessOrEqualZero => write!(f, "blez {arg}, {:#x}" , SignedHex(offset)),
|
||||
CondKind::GreaterThanZero => write!(f, "bgtz {arg}, {:#x}" , SignedHex(offset)),
|
||||
CondKind::LessThanZero => write!(f, "bltz {arg}, {:#x}" , SignedHex(offset)),
|
||||
CondKind::GreaterOrEqualZero => write!(f, "bgez {arg}, {:#x}" , SignedHex(offset)),
|
||||
CondKind::LessThanZeroLink => write!(f, "bltzal {arg}, {:#x}" , SignedHex(offset)),
|
||||
CondKind::GreaterOrEqualZeroLink => write!(f, "bgezal {arg}, {:#x}" , SignedHex(offset)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
//! Jmp register instructions
|
||||
|
||||
// Modules
|
||||
pub mod imm;
|
||||
pub mod reg;
|
||||
|
||||
// Exports
|
||||
pub use imm::{JmpImmInst, JmpImmInstRaw};
|
||||
pub use reg::{JmpRegInst, JmpRegInstRaw};
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum JmpInstRaw {
|
||||
/// Immediate
|
||||
Imm(JmpImmInstRaw),
|
||||
|
||||
/// Register
|
||||
Reg(JmpRegInstRaw),
|
||||
}
|
||||
|
||||
/// Jmp register instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum JmpInst {
|
||||
/// Immediate
|
||||
Imm(JmpImmInst),
|
||||
|
||||
/// Register
|
||||
Reg(JmpRegInst),
|
||||
}
|
||||
|
||||
impl JmpInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: JmpInstRaw) -> Option<Self> {
|
||||
match raw {
|
||||
JmpInstRaw::Imm(raw) => Self::Imm(JmpImmInst::decode(raw)?),
|
||||
JmpInstRaw::Reg(raw) => Self::Reg(JmpRegInst::decode(raw)?),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> JmpInstRaw {
|
||||
match self {
|
||||
JmpInst::Imm(inst) => JmpInstRaw::Imm(inst.encode()),
|
||||
JmpInst::Reg(inst) => JmpInstRaw::Reg(inst.encode()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,78 +0,0 @@
|
||||
//! Jumps
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::Register;
|
||||
use std::fmt;
|
||||
|
||||
/// Jump kind
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum JmpKind {
|
||||
/// Simple
|
||||
Simple,
|
||||
|
||||
/// With link
|
||||
Link(Register),
|
||||
}
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct JmpRaw {
|
||||
/// Rs
|
||||
pub s: u32,
|
||||
|
||||
/// Rd
|
||||
pub d: u32,
|
||||
|
||||
/// Func
|
||||
pub f: u32,
|
||||
}
|
||||
|
||||
/// Jump instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct JmpInst {
|
||||
/// Target register, `rs`
|
||||
pub target: Register,
|
||||
|
||||
/// Jump kind, `rs`if `jalr`.
|
||||
pub kind: JmpKind,
|
||||
}
|
||||
|
||||
impl JmpInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: JmpRaw) -> Option<Self> {
|
||||
let kind = match raw.f {
|
||||
0x8 => JmpKind::Simple,
|
||||
0x9 => JmpKind::Link(Register::new(raw.d)?),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(Self {
|
||||
target: Register::new(raw.s)?,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> JmpRaw {
|
||||
let s = self.target.idx();
|
||||
let (d, f) = match self.kind {
|
||||
JmpKind::Simple => (0, 0x8),
|
||||
JmpKind::Link(d) => (d.idx(), 0x9),
|
||||
};
|
||||
|
||||
JmpRaw { s, d, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for JmpInst {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Self { target, kind } = self;
|
||||
|
||||
match kind {
|
||||
JmpKind::Simple => write!(f, "jr {target}"),
|
||||
JmpKind::Link(link) => write!(f, "jalr {link}, {target}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,84 +0,0 @@
|
||||
//! Shift instructions
|
||||
|
||||
// Modules
|
||||
pub mod imm;
|
||||
pub mod reg;
|
||||
|
||||
// Exports
|
||||
pub use imm::{ShiftImmInst, ShiftImmRaw};
|
||||
pub use reg::{ShiftRegInst, ShiftRegRaw};
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct ShiftRaw {
|
||||
/// Rs
|
||||
pub s: u32,
|
||||
|
||||
/// Rt
|
||||
pub t: u32,
|
||||
|
||||
/// Rd
|
||||
pub d: u32,
|
||||
|
||||
/// Immediate
|
||||
pub i: u32,
|
||||
|
||||
/// Func
|
||||
pub f: u32,
|
||||
}
|
||||
|
||||
impl From<ShiftRaw> for ShiftImmRaw {
|
||||
fn from(ShiftRaw { t, d, i, f, .. }: ShiftRaw) -> Self {
|
||||
Self { t, d, i, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ShiftImmRaw> for ShiftRaw {
|
||||
fn from(ShiftImmRaw { t, d, i, f }: ShiftImmRaw) -> Self {
|
||||
Self { t, d, s: 0, i, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ShiftRaw> for ShiftRegRaw {
|
||||
fn from(ShiftRaw { t, d, s, f, .. }: ShiftRaw) -> Self {
|
||||
Self { t, d, s, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ShiftRegRaw> for ShiftRaw {
|
||||
fn from(ShiftRegRaw { t, d, s, f }: ShiftRegRaw) -> Self {
|
||||
Self { t, d, s, i: 0, f }
|
||||
}
|
||||
}
|
||||
|
||||
/// Shift instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum ShiftInst {
|
||||
/// Register
|
||||
Reg(ShiftRegInst),
|
||||
|
||||
/// Immediate
|
||||
Imm(ShiftImmInst),
|
||||
}
|
||||
|
||||
impl ShiftInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: ShiftRaw) -> Option<Self> {
|
||||
Some(match raw.f {
|
||||
0x0..0x4 => Self::Imm(ShiftImmInst::decode(raw.into())?),
|
||||
0x4..0x8 => Self::Reg(ShiftRegInst::decode(raw.into())?),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> ShiftRaw {
|
||||
match self {
|
||||
Self::Reg(inst) => inst.encode().into(),
|
||||
Self::Imm(inst) => inst.encode().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,93 +0,0 @@
|
||||
//! Immediate shifts
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::Register;
|
||||
use int_conv::{Signed, Truncated, ZeroExtended};
|
||||
use std::{convert::TryFrom, fmt};
|
||||
|
||||
/// Shift immediate instruction func
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum ShiftImmFunc {
|
||||
/// Left logical
|
||||
LeftLogical = 0x0,
|
||||
|
||||
/// Right logical
|
||||
RightLogical = 0x2,
|
||||
|
||||
/// Right arithmetic
|
||||
RightArithmetic = 0x3,
|
||||
}
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct ShiftImmRaw {
|
||||
/// Rt
|
||||
pub t: u32,
|
||||
|
||||
/// Rd
|
||||
pub d: u32,
|
||||
|
||||
/// Immediate
|
||||
pub i: u32,
|
||||
|
||||
/// Func
|
||||
pub f: u32,
|
||||
}
|
||||
|
||||
/// Shift immediate instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct ShiftImmInst {
|
||||
/// Destination register, `rt`
|
||||
pub dst: Register,
|
||||
|
||||
/// Lhs argument, `rd`
|
||||
pub lhs: Register,
|
||||
|
||||
/// Rhs argument, immediate
|
||||
pub rhs: i16,
|
||||
|
||||
/// Function
|
||||
pub func: ShiftImmFunc,
|
||||
}
|
||||
|
||||
impl ShiftImmInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: ShiftImmRaw) -> Option<Self> {
|
||||
let func = ShiftImmFunc::try_from(raw.f.truncated::<u8>()).ok()?;
|
||||
|
||||
Some(Self {
|
||||
lhs: Register::new(raw.t)?,
|
||||
dst: Register::new(raw.d)?,
|
||||
rhs: raw.i.truncated::<u16>().as_signed(),
|
||||
func,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> ShiftImmRaw {
|
||||
let t = self.lhs.idx();
|
||||
let d = self.dst.idx();
|
||||
let i = self.rhs.as_unsigned().zero_extended::<u32>();
|
||||
let f = u8::from(self.func).zero_extended::<u32>();
|
||||
|
||||
ShiftImmRaw { f, t, d, i }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ShiftImmInst {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Self { lhs, dst, rhs, func } = self;
|
||||
|
||||
let mnemonic = match func {
|
||||
ShiftImmFunc::LeftLogical => "sll",
|
||||
ShiftImmFunc::RightLogical => "srl",
|
||||
ShiftImmFunc::RightArithmetic => "sra",
|
||||
};
|
||||
|
||||
write!(f, "{mnemonic} {dst}, {lhs}, {rhs:#x}")
|
||||
}
|
||||
}
|
||||
@ -1,93 +0,0 @@
|
||||
//! Register shifts
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::Register;
|
||||
use int_conv::{Truncated, ZeroExtended};
|
||||
use std::{convert::TryFrom, fmt};
|
||||
|
||||
/// Shift register instruction func
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum ShiftRegFunc {
|
||||
/// Left logical
|
||||
LeftLogical = 0x4,
|
||||
|
||||
/// Right logical
|
||||
RightLogical = 0x6,
|
||||
|
||||
/// Right arithmetic
|
||||
RightArithmetic = 0x7,
|
||||
}
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct ShiftRegRaw {
|
||||
/// Rs
|
||||
pub s: u32,
|
||||
|
||||
/// Rt
|
||||
pub t: u32,
|
||||
|
||||
/// Rd
|
||||
pub d: u32,
|
||||
|
||||
/// Func
|
||||
pub f: u32,
|
||||
}
|
||||
|
||||
/// Shift register instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct ShiftRegInst {
|
||||
/// Destination register, `rd`
|
||||
pub dst: Register,
|
||||
|
||||
/// Lhs argument, `rt`
|
||||
pub lhs: Register,
|
||||
|
||||
/// Rhs argument, `rs`
|
||||
pub rhs: Register,
|
||||
|
||||
/// Function
|
||||
pub func: ShiftRegFunc,
|
||||
}
|
||||
|
||||
impl ShiftRegInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: ShiftRegRaw) -> Option<Self> {
|
||||
let func = ShiftRegFunc::try_from(raw.f.truncated::<u8>()).ok()?;
|
||||
|
||||
Some(Self {
|
||||
dst: Register::new(raw.d)?,
|
||||
lhs: Register::new(raw.t)?,
|
||||
rhs: Register::new(raw.s)?,
|
||||
func,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> ShiftRegRaw {
|
||||
let d = self.dst.idx();
|
||||
let t = self.lhs.idx();
|
||||
let s = self.rhs.idx();
|
||||
let f = u8::from(self.func).zero_extended::<u32>();
|
||||
|
||||
ShiftRegRaw { f, t, d, s }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ShiftRegInst {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Self { dst, lhs, rhs, func } = self;
|
||||
|
||||
let mnemonic = match func {
|
||||
ShiftRegFunc::LeftLogical => "sllv",
|
||||
ShiftRegFunc::RightLogical => "srlv",
|
||||
ShiftRegFunc::RightArithmetic => "srav",
|
||||
};
|
||||
|
||||
write!(f, "{mnemonic} {dst}, {lhs}, {rhs}")
|
||||
}
|
||||
}
|
||||
@ -1,235 +0,0 @@
|
||||
//! Directives
|
||||
|
||||
// Imports
|
||||
use super::{FromRawIter, Instruction, Raw};
|
||||
use crate::exe::Pos;
|
||||
use ascii::{AsciiChar, AsciiStr, AsciiString};
|
||||
use int_conv::Split;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::ops::{
|
||||
Bound::{self, Excluded, Included, Unbounded},
|
||||
RangeBounds,
|
||||
};
|
||||
use AsciiChar::Null;
|
||||
|
||||
/// A directive
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum Directive {
|
||||
/// Write word
|
||||
#[display(fmt = "dw {_0:#x}")]
|
||||
Dw(u32),
|
||||
|
||||
/// Write half-word
|
||||
#[display(fmt = "dh {_0:#x}")]
|
||||
Dh(u16),
|
||||
|
||||
/// Write byte
|
||||
#[display(fmt = "db {_0:#x}")]
|
||||
Db(u8),
|
||||
|
||||
/// Ascii string
|
||||
#[display(fmt = ".ascii {_0:?}")]
|
||||
Ascii(AsciiString),
|
||||
}
|
||||
|
||||
/// A force decode range
|
||||
pub struct ForceDecodeRange {
|
||||
/// Start bound
|
||||
start: Bound<Pos>,
|
||||
|
||||
/// End bound
|
||||
end: Bound<Pos>,
|
||||
|
||||
/// Decoding kind
|
||||
kind: ForceDecodeKind,
|
||||
}
|
||||
|
||||
impl RangeBounds<Pos> for ForceDecodeRange {
|
||||
fn start_bound(&self) -> Bound<&Pos> {
|
||||
match self.start {
|
||||
Included(ref start) => Included(start),
|
||||
Excluded(ref start) => Excluded(start),
|
||||
Unbounded => Unbounded,
|
||||
}
|
||||
}
|
||||
|
||||
fn end_bound(&self) -> Bound<&Pos> {
|
||||
match self.end {
|
||||
Included(ref end) => Included(end),
|
||||
Excluded(ref end) => Excluded(end),
|
||||
Unbounded => Unbounded,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Force decode range kind
|
||||
pub enum ForceDecodeKind {
|
||||
/// Single Word
|
||||
W,
|
||||
|
||||
/// Two half-words
|
||||
HH,
|
||||
|
||||
/// Half-word followed by bytes
|
||||
HBB,
|
||||
|
||||
/// Bytes followed by half-word
|
||||
BBH,
|
||||
|
||||
/// Bytes
|
||||
BBBB,
|
||||
}
|
||||
|
||||
impl Directive {
|
||||
/// Positions that should be force decoded using a specific variant.
|
||||
pub const FORCE_DECODE_RANGES: &'static [ForceDecodeRange] = &[
|
||||
ForceDecodeRange {
|
||||
start: Included(Pos(0x80010000)),
|
||||
end: Excluded(Pos(0x80010008)),
|
||||
kind: ForceDecodeKind::W,
|
||||
},
|
||||
ForceDecodeRange {
|
||||
start: Included(Pos(0x8006fa20)),
|
||||
end: Excluded(Pos(0x8006fa24)),
|
||||
kind: ForceDecodeKind::HH,
|
||||
},
|
||||
ForceDecodeRange {
|
||||
start: Included(Instruction::CODE_END),
|
||||
end: Unbounded,
|
||||
kind: ForceDecodeKind::W,
|
||||
},
|
||||
];
|
||||
|
||||
/// Returns the size of this instruction
|
||||
#[must_use]
|
||||
pub fn size(&self) -> u32 {
|
||||
#[allow(clippy::as_conversions, clippy::cast_possible_truncation)] // Our length will always fit into a `u32`.
|
||||
match self {
|
||||
Self::Dw(_) => 4,
|
||||
Self::Dh(_) => 2,
|
||||
Self::Db(_) => 1,
|
||||
// Round ascii strings' len up to the
|
||||
// nearest word.
|
||||
Self::Ascii(ascii) => {
|
||||
let len = ascii.len() as u32;
|
||||
len + 4 - (len % 4)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Helper function to check if a string has null and if everything after the first
|
||||
/// null is also null (or if there were no nulls).
|
||||
fn check_nulls<S: AsRef<AsciiStr>>(s: S) -> (S, usize, bool) {
|
||||
let null_idx = s
|
||||
.as_ref()
|
||||
.as_slice()
|
||||
.iter()
|
||||
.position(|&ch| ch == Null)
|
||||
.unwrap_or_else(|| s.as_ref().len());
|
||||
#[allow(clippy::indexing_slicing)] // `null_idx <= len`
|
||||
let uniform_null = s.as_ref()[null_idx..].chars().all(|ch| ch == Null);
|
||||
(s, null_idx, uniform_null)
|
||||
}
|
||||
|
||||
impl FromRawIter for Directive {
|
||||
//type Decoded = Option<(Pos, Self)>;
|
||||
// Note: We return at most 4 directives.
|
||||
type Decoded = SmallVec<[(Pos, Self); 4]>;
|
||||
|
||||
fn decode<I: Iterator<Item = Raw> + Clone>(iter: &mut I) -> Self::Decoded {
|
||||
// Get the first raw
|
||||
let raw = match iter.next() {
|
||||
Some(raw) => raw,
|
||||
None => return smallvec![],
|
||||
};
|
||||
|
||||
// If we're past all the code, there are no more strings,
|
||||
// so just decode a `dw`.
|
||||
// Note: We're working in big endian when returning these.
|
||||
if let Some(ForceDecodeRange { kind, .. }) = Self::FORCE_DECODE_RANGES.iter().find(|range| range.contains(&raw.pos)) {
|
||||
return match kind {
|
||||
ForceDecodeKind::W => smallvec![(raw.pos, Self::Dw(raw.repr))],
|
||||
ForceDecodeKind::HH => {
|
||||
let (lo, hi) = raw.repr.lo_hi();
|
||||
smallvec![(raw.pos, Self::Dh(hi)), (raw.pos + 2, Self::Dh(lo))]
|
||||
},
|
||||
ForceDecodeKind::HBB => {
|
||||
let (lo, hi) = raw.repr.lo_hi();
|
||||
let (lo_lo, lo_hi) = lo.lo_hi();
|
||||
smallvec![(raw.pos, Self::Dh(hi)), (raw.pos + 2, Self::Db(lo_hi)), (raw.pos + 3, Self::Db(lo_lo))]
|
||||
},
|
||||
ForceDecodeKind::BBH => {
|
||||
let (lo, hi) = raw.repr.lo_hi();
|
||||
let (hi_lo, hi_hi) = hi.lo_hi();
|
||||
smallvec![(raw.pos, Self::Db(hi_hi)), (raw.pos + 1, Self::Db(hi_lo)), (raw.pos + 2, Self::Dh(lo))]
|
||||
},
|
||||
ForceDecodeKind::BBBB => {
|
||||
let (lo, hi) = raw.repr.lo_hi();
|
||||
let (lo_lo, lo_hi) = lo.lo_hi();
|
||||
let (hi_lo, hi_hi) = hi.lo_hi();
|
||||
smallvec![
|
||||
(raw.pos, Self::Db(hi_hi)),
|
||||
(raw.pos + 1, Self::Db(hi_lo)),
|
||||
(raw.pos + 2, Self::Db(lo_hi)),
|
||||
(raw.pos + 3, Self::Db(lo_lo))
|
||||
]
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Try to get an ascii string from the raw and check for nulls
|
||||
match AsciiString::from_ascii(raw.repr.to_ne_bytes()).map(check_nulls) {
|
||||
// If we got a string with at least 1 non-null, but
|
||||
// at least 1 null and uniformly null, return just it
|
||||
Ok((mut ascii_string, null_idx @ 1..=3, true)) => {
|
||||
ascii_string.truncate(null_idx);
|
||||
smallvec![(raw.pos, Self::Ascii(ascii_string))]
|
||||
},
|
||||
|
||||
// If we got a string without any nulls, keep
|
||||
// filling the string until we find one.
|
||||
Ok((mut ascii_string, 4, true)) => {
|
||||
let ascii_string = loop {
|
||||
let mut cur_iter = iter.clone();
|
||||
match cur_iter.next() {
|
||||
// If we don't have a next character, return the string as-is
|
||||
// Note: No need to update the iterator, it returned `None`.
|
||||
None => break ascii_string,
|
||||
|
||||
// Else try to get it as a string and check for nulls
|
||||
Some(next_raw) => match AsciiStr::from_ascii(&next_raw.repr.to_ne_bytes()).map(check_nulls) {
|
||||
// If we got it and it wasn't null, update the iterator, add it and continue
|
||||
Ok((new_ascii_str, 4, _)) => {
|
||||
*iter = cur_iter;
|
||||
ascii_string.push_str(new_ascii_str);
|
||||
},
|
||||
|
||||
// If we got it, but there was a uniform null, update the iterator,
|
||||
// add the non-null parts and return.
|
||||
#[allow(clippy::indexing_slicing)] // `null_idx < len`
|
||||
Ok((new_ascii_str, null_idx, true)) => {
|
||||
*iter = cur_iter;
|
||||
ascii_string.push_str(&new_ascii_str[..null_idx]);
|
||||
break ascii_string;
|
||||
},
|
||||
|
||||
// If we didn't get it or it was a non-uniform null, return the string we have so far
|
||||
// Note: We don't update the iterator, as we want to leave
|
||||
// the next value to `dw`.
|
||||
Err(_) | Ok((_, _, false)) => break ascii_string,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
smallvec![(raw.pos, Self::Ascii(ascii_string))]
|
||||
},
|
||||
|
||||
// Else if it was full null, non-uniformly null or non-ascii,
|
||||
// just return a normal word.
|
||||
_ => smallvec![(raw.pos, Self::Dw(raw.repr))],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,87 +0,0 @@
|
||||
//! Alu self-assign instructions
|
||||
|
||||
// Imports
|
||||
use crate::exe::instruction::{
|
||||
basic::{
|
||||
alu_imm::AluImmKind,
|
||||
special::{alu_reg::AluRegKind, AluRegInst},
|
||||
AluImmInst, InstIter, SpecialInst,
|
||||
},
|
||||
BasicInst, Register,
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
/// Alu assign kind
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum AluAssignKind {
|
||||
/// Immediate
|
||||
Imm(AluImmKind),
|
||||
|
||||
/// Register
|
||||
Reg(AluRegKind, Register),
|
||||
}
|
||||
|
||||
impl AluAssignKind {
|
||||
/// Returns this kind's mnemonic
|
||||
pub fn mnemonic(self) -> &'static str {
|
||||
match self {
|
||||
AluAssignKind::Imm(kind) => kind.mnemonic(),
|
||||
AluAssignKind::Reg(kind, _) => kind.mnemonic(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a displayable with the value of this kind
|
||||
pub fn value_fmt(self) -> impl fmt::Display {
|
||||
struct FmtValue(Self);
|
||||
|
||||
impl fmt::Display for FmtValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.0 {
|
||||
AluAssignKind::Imm(kind) => write!(f, "{}", kind.value_fmt()),
|
||||
AluAssignKind::Reg(kind, reg) => write!(f, "{}", reg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FmtValue(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Alu self-assign instructions
|
||||
///
|
||||
/// Alias for
|
||||
/// ```mips
|
||||
/// [alu] $dst, $dst, $i
|
||||
/// ```
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
#[display(fmt = "{} {dst}, {}", "kind.mnemonic()", "kind.value_fmt()")]
|
||||
pub struct AluAssignInst {
|
||||
/// Destination and source register
|
||||
pub dst: Register,
|
||||
|
||||
/// Kind
|
||||
pub kind: AluAssignKind,
|
||||
}
|
||||
|
||||
impl AluAssignInst {
|
||||
/// Decodes this pseudo instruction
|
||||
#[must_use]
|
||||
pub fn decode(iter: InstIter<'_, impl Iterator<Item = u32> + Clone>) -> Option<Self> {
|
||||
let peeker = iter.peeker();
|
||||
let inst = match peeker.next()?? {
|
||||
BasicInst::Special(SpecialInst::Alu(AluRegInst { dst, lhs, rhs, kind })) if dst == lhs => Self {
|
||||
dst,
|
||||
kind: AluAssignKind::Reg(kind, rhs),
|
||||
},
|
||||
BasicInst::AluImm(AluImmInst { dst, lhs, kind }) if dst == lhs => Self {
|
||||
dst,
|
||||
kind: AluAssignKind::Imm(kind),
|
||||
},
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
peeker.apply();
|
||||
Some(inst)
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,8 @@ use std::{convert::TryFrom, fmt, ops};
|
||||
/// An instruction position
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug)]
|
||||
#[derive(ref_cast::RefCast)]
|
||||
#[derive(derive_more::Display)]
|
||||
#[display(fmt = "{_0:#x?}")]
|
||||
#[repr(transparent)]
|
||||
pub struct Pos(pub u32);
|
||||
|
||||
@ -49,6 +51,23 @@ impl ops::Add<i32> for Pos {
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Add<i16> for Pos {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: i16) -> Self::Output {
|
||||
self + rhs.sign_extended::<i32>()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ops::AddAssign<T> for Pos
|
||||
where
|
||||
Pos: ops::Add<T, Output = Self>,
|
||||
{
|
||||
fn add_assign(&mut self, rhs: T) {
|
||||
*self = *self + rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::BitAnd<u32> for Pos {
|
||||
type Output = Self;
|
||||
|
||||
|
||||
@ -1,60 +1,7 @@
|
||||
//! `dcb` is a library for interacting with the game file of `Digimon Digital Card Battle`.
|
||||
//!
|
||||
//! # Modules
|
||||
//! `dcb` is split across 2 main modules, [`io`] and [`game`].
|
||||
//!
|
||||
//! ## Io
|
||||
//! The Io module is responsible for interacting with the game file. In the future it may be responsible
|
||||
//! for also interacting with the game extracted database, once work on that is complete.
|
||||
//!
|
||||
//! ## Game
|
||||
//! The game module is responsible for representing in-game structures such as cards, sprites, text, and
|
||||
//! others. The trait has various interfaces to be able to deserialize these structures from both the game
|
||||
//! file, database or even other sources, depending on the structure.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! The following is an example of how to use the `dcb` library.
|
||||
//! This example extracts the card table and prints it to screen
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! # use std::fs::File;
|
||||
//! let mut game_file = dcb::GameFile::from_reader( File::open("Digimon Digital Card Battle.bin")? )?;
|
||||
//! let card_table = dcb::CardTable::deserialize(&mut game_file)?;
|
||||
//! println!("Card table: {:?}", card_table);
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//! `dcb` executable debugging
|
||||
|
||||
// Features
|
||||
#![feature(
|
||||
seek_convenience,
|
||||
never_type,
|
||||
bool_to_option,
|
||||
decl_macro,
|
||||
stmt_expr_attributes,
|
||||
unwrap_infallible,
|
||||
external_doc,
|
||||
format_args_capture,
|
||||
const_fn,
|
||||
const_panic,
|
||||
min_const_generics,
|
||||
exclusive_range_pattern,
|
||||
unsafe_block_in_unsafe_fn,
|
||||
maybe_uninit_uninit_array,
|
||||
maybe_uninit_slice,
|
||||
array_map,
|
||||
const_mut_refs,
|
||||
core_intrinsics,
|
||||
const_assume,
|
||||
bindings_after_at,
|
||||
array_value_iter,
|
||||
or_patterns,
|
||||
once_cell,
|
||||
box_syntax,
|
||||
str_split_once
|
||||
)]
|
||||
#![feature(unsafe_block_in_unsafe_fn, format_args_capture, never_type, or_patterns)]
|
||||
// Lints
|
||||
#![warn(clippy::restriction, clippy::pedantic, clippy::nursery)]
|
||||
// Instead of `unwrap`, we must use `expect` and provide a reason
|
||||
@ -107,7 +54,9 @@
|
||||
// A `match Option / Result / Bool` can sometimes look cleaner than a `if let / else`
|
||||
#![allow(clippy::single_match_else, clippy::match_bool)]
|
||||
// We're usually fine with missing future variants
|
||||
#![allow(clippy::wildcard_enum_match_arm)]
|
||||
#![allow(clippy::wildcard_enum_match_arm, clippy::match_wildcard_for_single_variants)]
|
||||
// We only use globs in small scopes
|
||||
#![allow(clippy::enum_glob_use)]
|
||||
|
||||
// Modules
|
||||
pub mod exe;
|
||||
|
||||
@ -12,14 +12,15 @@ path = "src/extractor/main.rs"
|
||||
name = "patcher"
|
||||
path = "src/patcher/main.rs"
|
||||
|
||||
#[[bin]]
|
||||
#name = "decompiler"
|
||||
#path = "src/decompiler/main.rs"
|
||||
[[bin]]
|
||||
name = "decompiler"
|
||||
path = "src/decompiler/main.rs"
|
||||
|
||||
[dependencies]
|
||||
# Dcb
|
||||
dcb = { path = "../dcb" }
|
||||
dcb-io = { path = "../dcb-io" }
|
||||
dcb-exe = { path = "../dcb-exe" }
|
||||
|
||||
# Text
|
||||
ascii = "1.0"
|
||||
|
||||
@ -76,7 +76,7 @@ mod logger;
|
||||
|
||||
// Imports
|
||||
use anyhow::Context;
|
||||
use dcb::{exe::data::DataTable, GameFile};
|
||||
use dcb_io::GameFile;
|
||||
|
||||
#[allow(clippy::cognitive_complexity, clippy::too_many_lines)] // TODO: Refactor
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
@ -88,47 +88,18 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
|
||||
// Open the game file
|
||||
let input_file = std::fs::File::open(&game_file_path).context("Unable to open input file")?;
|
||||
let _game_file = GameFile::from_reader(input_file).context("Unable to parse input file as dcb")?;
|
||||
let mut game_file = GameFile::from_reader(input_file).context("Unable to parse input file as dcb")?;
|
||||
|
||||
// Read the executable
|
||||
log::debug!("Deserializing executable");
|
||||
//let exe = dcb::Exe::deserialize(&mut game_file).context("Unable to parse game executable")?;
|
||||
let exe = dcb_exe::Exe::deserialize(&mut game_file).context("Unable to parse game executable")?;
|
||||
|
||||
//log::info!("Header:\n{}\n", exe.header);
|
||||
println!("Header:\n{}", exe.header);
|
||||
|
||||
/*
|
||||
// Get all instructions
|
||||
log::debug!("Retrieving all instructions");
|
||||
let instructions: Vec<(Pos, Instruction)> = Instruction::new_iter(
|
||||
exe.data
|
||||
.array_chunks::<4>()
|
||||
.map(|bytes| LittleEndian::read_u32(bytes))
|
||||
.zip(0..)
|
||||
.map(|(word, offset)| Raw {
|
||||
repr: word,
|
||||
pos: Pos(exe.header.dest + 4 * offset),
|
||||
}),
|
||||
)
|
||||
.collect();
|
||||
*/
|
||||
println!("Data table:\n{:#?}", exe.data_table);
|
||||
|
||||
/*
|
||||
// Get all functions
|
||||
log::debug!("Retrieving all functions");
|
||||
let functions: FuncTable = FuncTable::get_known()
|
||||
.context("Unable to get known function table")?
|
||||
.merge(FuncTable::from_instructions(
|
||||
&instructions.iter().map(|(pos, instruction)| (*pos, instruction)),
|
||||
));
|
||||
*/
|
||||
|
||||
// Get all data
|
||||
log::debug!("Retrieving all locations");
|
||||
let data_pos: DataTable = DataTable::get_known().context("Unable to get known function table")?;
|
||||
|
||||
#[allow(clippy::use_debug)]
|
||||
{
|
||||
println!("{data_pos:#?}");
|
||||
for (pos, inst) in exe.parse_iter() {
|
||||
println!("{}: {:?}", pos, inst);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@ -13,12 +13,6 @@ macro_rules! array_split {
|
||||
|
||||
),* $(,)?
|
||||
) => {{
|
||||
#![allow(
|
||||
clippy::used_underscore_binding,
|
||||
clippy::ptr_offset_with_cast,
|
||||
clippy::indexing_slicing,
|
||||
)]
|
||||
|
||||
// Struct holding all fields
|
||||
struct Fields<'a, T> {
|
||||
$(
|
||||
@ -30,6 +24,11 @@ macro_rules! array_split {
|
||||
}
|
||||
|
||||
// Get everything from `array_refs`
|
||||
#[allow(
|
||||
clippy::used_underscore_binding,
|
||||
clippy::ptr_offset_with_cast,
|
||||
clippy::indexing_slicing,
|
||||
)]
|
||||
let (
|
||||
$(
|
||||
$name
|
||||
|
||||
@ -20,7 +20,6 @@ pub struct DiscardingSortedMergeIter<T: Ord, Li: Iterator<Item = T>, Ri: Iterato
|
||||
|
||||
impl<T: Ord, Li: Iterator<Item = T>, Ri: Iterator<Item = T>> DiscardingSortedMergeIter<T, Li, Ri> {
|
||||
/// Creates a new merging iterator
|
||||
#[allow(dead_code)] // TODO: Remove
|
||||
pub fn new(lhs: Li, rhs: Ri) -> Self {
|
||||
Self { lhs, rhs, last: None }
|
||||
}
|
||||
|
||||
@ -1,31 +1,4 @@
|
||||
//! `dcb` is a library for interacting with the game file of `Digimon Digital Card Battle`.
|
||||
//!
|
||||
//! # Modules
|
||||
//! `dcb` is split across 2 main modules, [`io`] and [`game`].
|
||||
//!
|
||||
//! ## Io
|
||||
//! The Io module is responsible for interacting with the game file. In the future it may be responsible
|
||||
//! for also interacting with the game extracted database, once work on that is complete.
|
||||
//!
|
||||
//! ## Game
|
||||
//! The game module is responsible for representing in-game structures such as cards, sprites, text, and
|
||||
//! others. The trait has various interfaces to be able to deserialize these structures from both the game
|
||||
//! file, database or even other sources, depending on the structure.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! The following is an example of how to use the `dcb` library.
|
||||
//! This example extracts the card table and prints it to screen
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! # use std::fs::File;
|
||||
//! let mut game_file = dcb::GameFile::from_reader( File::open("Digimon Digital Card Battle.bin")? )?;
|
||||
//! let card_table = dcb::CardTable::deserialize(&mut game_file)?;
|
||||
//! println!("Card table: {:?}", card_table);
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//! Dcb utilities
|
||||
|
||||
// Features
|
||||
#![feature(
|
||||
@ -53,7 +26,8 @@
|
||||
or_patterns,
|
||||
once_cell,
|
||||
box_syntax,
|
||||
str_split_once
|
||||
str_split_once,
|
||||
try_trait
|
||||
)]
|
||||
// Lints
|
||||
#![warn(clippy::restriction, clippy::pedantic, clippy::nursery)]
|
||||
@ -117,13 +91,17 @@ pub mod null_ascii_string;
|
||||
#[macro_use]
|
||||
pub mod impl_bytes;
|
||||
pub mod discarding_sorted_merge_iter;
|
||||
pub mod peekable_iter;
|
||||
pub mod signed_hex;
|
||||
pub mod next_from_bytes;
|
||||
|
||||
// Exports
|
||||
//pub use array_split::{array_split, array_split_mut};
|
||||
pub use ascii_str_arr::AsciiStrArr;
|
||||
pub use discarding_sorted_merge_iter::DiscardingSortedMergeIter;
|
||||
pub use peekable_iter::PeekableIter;
|
||||
pub use signed_hex::SignedHex;
|
||||
pub use next_from_bytes::NextFromBytes;
|
||||
|
||||
/// Returns the absolute different between `a` and `b`, `a - b` as a `i64`.
|
||||
///
|
||||
|
||||
36
dcb-util/src/next_from_bytes.rs
Normal file
36
dcb-util/src/next_from_bytes.rs
Normal file
@ -0,0 +1,36 @@
|
||||
//! Next type from bytes
|
||||
|
||||
/// Parses some types from bytes
|
||||
pub trait NextFromBytes {
|
||||
/// Parses the next `u8` from bytes
|
||||
fn next_u8(&self) -> Option<u8>;
|
||||
|
||||
/// Parses the next `u16` from bytes
|
||||
fn next_u16(&self) -> Option<u16>;
|
||||
|
||||
/// Parses the next `u32` from bytes
|
||||
fn next_u32(&self) -> Option<u32>;
|
||||
}
|
||||
|
||||
impl NextFromBytes for [u8] {
|
||||
fn next_u8(&self) -> Option<u8> {
|
||||
match *self {
|
||||
[a, ..] => Some(a),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn next_u16(&self) -> Option<u16> {
|
||||
match *self {
|
||||
[a, b, ..] => Some(u16::from_ne_bytes([a, b])),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn next_u32(&self) -> Option<u32> {
|
||||
match *self {
|
||||
[a, b, c, d, ..] => Some(u32::from_ne_bytes([a, b, c, d])),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
34
dcb-util/src/peekable_iter.rs
Normal file
34
dcb-util/src/peekable_iter.rs
Normal file
@ -0,0 +1,34 @@
|
||||
//! Peekable iterators
|
||||
|
||||
/// Iterators which are peekable
|
||||
pub trait PeekableIter: Iterator {
|
||||
/// Peeks the next element
|
||||
fn peek(&self) -> Option<Self::Item>;
|
||||
|
||||
/// Consumes the next element if `f` returns true
|
||||
fn next_if(&mut self, f: impl FnOnce(Self::Item) -> bool) -> bool;
|
||||
|
||||
/// Consumes the next element if `f` returns `Some`
|
||||
fn try_next<T: std::ops::Try>(&mut self, f: impl FnOnce(Self::Item) -> T) -> Option<Result<T::Ok, T::Error>>;
|
||||
}
|
||||
|
||||
impl<I: Iterator + Clone> PeekableIter for I {
|
||||
fn peek(&self) -> Option<Self::Item> {
|
||||
self.clone().next()
|
||||
}
|
||||
|
||||
fn next_if(&mut self, f: impl FnOnce(Self::Item) -> bool) -> bool {
|
||||
matches!(self.try_next(move |value| f(value).then_some(())), Some(Ok(())))
|
||||
}
|
||||
|
||||
fn try_next<T: std::ops::Try>(&mut self, f: impl FnOnce(Self::Item) -> T) -> Option<Result<T::Ok, T::Error>> {
|
||||
let mut iter = self.clone();
|
||||
match iter.next().map(f)?.into_result() {
|
||||
Ok(value) => {
|
||||
*self = iter;
|
||||
Some(Ok(value))
|
||||
},
|
||||
Err(err) => Some(Err(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@
|
||||
// Imports
|
||||
use int_conv::Extended;
|
||||
use ref_cast::RefCast;
|
||||
use std::fmt::{self, Formatter, LowerHex};
|
||||
use std::fmt;
|
||||
|
||||
/// A signed numeric type that uses signed hexadecimal formatting.
|
||||
#[derive(ref_cast::RefCast)]
|
||||
@ -13,20 +13,20 @@ pub struct SignedHex<T>(pub T);
|
||||
|
||||
// All references implement it for their underlying type.
|
||||
#[allow(clippy::use_self)] // We're using a generic version `SignedHex`, not `Self`
|
||||
impl<'a, T> LowerHex for SignedHex<&'a T>
|
||||
impl<'a, T> fmt::Display for SignedHex<&'a T>
|
||||
where
|
||||
SignedHex<T>: LowerHex,
|
||||
SignedHex<T>: fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
<SignedHex<T> as LowerHex>::fmt(SignedHex::<T>::ref_cast(self.0), f)
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<SignedHex<T> as fmt::Display>::fmt(SignedHex::<T>::ref_cast(self.0), f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Macro to help implement [`SignedHex`]
|
||||
macro impl_signed_hex($($T:ty => $TBigger:ty),* $(,)?) {
|
||||
$(
|
||||
impl LowerHex for SignedHex<$T> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
impl fmt::Display for SignedHex<$T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let sign = match (self.0 < 0, f.sign_plus()) {
|
||||
(true, _) => "-",
|
||||
(false, true) => "+",
|
||||
@ -40,7 +40,7 @@ macro impl_signed_hex($($T:ty => $TBigger:ty),* $(,)?) {
|
||||
|
||||
// TODO: Remove `+` from the formatter flags when we do
|
||||
// this to fully support the `+` flag.
|
||||
LowerHex::fmt(&self.0.extended::<$TBigger>().abs(), f)
|
||||
fmt::LowerHex::fmt(&self.0.extended::<$TBigger>().abs(), f)
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user