mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-08 11:28:44 +00:00
Renamed SimpleInstruction to BasicInstruction.
Added `BasicInst` type to replace `BasicInstruction` eventually. Replaced `Formatter<'_>` with just `Formatter` where possible. Added a `Display` impl for `Header`. `dw` instructions now have their argument replaced instead of just having a comment.
This commit is contained in:
parent
e62561f228
commit
0e58b3df15
@ -82,9 +82,9 @@ use dcb::{
|
||||
data::DataTable,
|
||||
func::FuncTable,
|
||||
instruction::{
|
||||
Directive,
|
||||
BasicInstruction, Directive,
|
||||
PseudoInstruction::{self, Nop},
|
||||
Raw, SimpleInstruction,
|
||||
Raw,
|
||||
},
|
||||
Func, Instruction, Pos,
|
||||
},
|
||||
@ -107,6 +107,8 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
log::debug!("Deserializing executable");
|
||||
let exe = dcb::game::Exe::deserialize(&mut game_file).context("Unable to parse game executable")?;
|
||||
|
||||
log::info!("Header:\n{}\n", exe.header);
|
||||
|
||||
// Get all instructions
|
||||
log::debug!("Retrieving all instructions");
|
||||
let instructions: Vec<(Pos, Instruction)> = Instruction::new_iter(
|
||||
@ -182,8 +184,9 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
}
|
||||
|
||||
// Space out data if it had a name
|
||||
if let Some(data) = data_pos.get(cur_pos) {
|
||||
if let Some(data) = data_pos.get(cur_pos - 1) {
|
||||
if data.end_pos() == cur_pos && !data.name.is_empty() {
|
||||
println!("####################");
|
||||
println!();
|
||||
}
|
||||
}
|
||||
@ -207,6 +210,8 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
}
|
||||
if let Some(data) = data_pos.get(cur_pos) {
|
||||
if data.pos == cur_pos {
|
||||
println!();
|
||||
println!("####################");
|
||||
println!("{}:", data.name);
|
||||
for description in data.desc.lines() {
|
||||
println!("# {}", description);
|
||||
@ -215,19 +220,22 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
}
|
||||
|
||||
// Print the instruction and it's location.
|
||||
print!("{cur_pos:#010x}:\t");
|
||||
match cur_func {
|
||||
Some(cur_func) => print!("{:#05x}:\t", cur_pos - cur_func.start_pos),
|
||||
None => print!("{cur_pos:#010x}:\t"),
|
||||
}
|
||||
match instruction {
|
||||
Instruction::Simple(
|
||||
SimpleInstruction::J { target } |
|
||||
SimpleInstruction::Jal { target } |
|
||||
SimpleInstruction::Beq { target, .. } |
|
||||
SimpleInstruction::Bne { target, .. } |
|
||||
SimpleInstruction::Bltz { target, .. } |
|
||||
SimpleInstruction::Bgez { target, .. } |
|
||||
SimpleInstruction::Bgtz { target, .. } |
|
||||
SimpleInstruction::Blez { target, .. } |
|
||||
SimpleInstruction::Bltzal { target, .. } |
|
||||
SimpleInstruction::Bgezal { target, .. },
|
||||
Instruction::Basic(
|
||||
BasicInstruction::J { target } |
|
||||
BasicInstruction::Jal { target } |
|
||||
BasicInstruction::Beq { target, .. } |
|
||||
BasicInstruction::Bne { target, .. } |
|
||||
BasicInstruction::Bltz { target, .. } |
|
||||
BasicInstruction::Bgez { target, .. } |
|
||||
BasicInstruction::Bgtz { target, .. } |
|
||||
BasicInstruction::Blez { target, .. } |
|
||||
BasicInstruction::Bltzal { target, .. } |
|
||||
BasicInstruction::Bgezal { target, .. },
|
||||
) |
|
||||
Instruction::Pseudo(
|
||||
PseudoInstruction::B { target } | PseudoInstruction::Beqz { target, .. } | PseudoInstruction::Bnez { target, .. },
|
||||
@ -256,7 +264,8 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
PseudoInstruction::SwlImm { offset: target, .. } |
|
||||
PseudoInstruction::SwImm { offset: target, .. } |
|
||||
PseudoInstruction::SwrImm { offset: target, .. },
|
||||
) => match functions
|
||||
) |
|
||||
Instruction::Directive(Directive::Dw(target)) => match functions
|
||||
.get(Pos(*target))
|
||||
.map(|func| (func.start_pos, &func.name))
|
||||
.or_else(|| data_pos.get(Pos(*target)).map(|data| (data.pos, &data.name)))
|
||||
@ -277,22 +286,6 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
_ => print!("{instruction}"),
|
||||
}
|
||||
|
||||
// Comment any `dw` instructions that are function, data or string pointers
|
||||
if let Instruction::Directive(Directive::Dw(target)) = instruction {
|
||||
if let Some(func) = functions.get(Pos(*target)) {
|
||||
print!(" # {}", func.name);
|
||||
} else if let Some(data) = data_pos.get(Pos(*target)) {
|
||||
if data.pos == Pos(*target) {
|
||||
print!(" # {}", data.name);
|
||||
} else {
|
||||
let offset = Pos(*target) - data.pos;
|
||||
if offset > 0 {
|
||||
print!(" # {} + {offset:#x}", data.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append any comments in this line
|
||||
if let Some(cur_func) = cur_func {
|
||||
if let Some(comment) = cur_func.comments.get(&cur_pos) {
|
||||
|
||||
@ -20,6 +20,7 @@ int-conv = "0.1"
|
||||
bitmatch = "0.1"
|
||||
either = "1.6"
|
||||
smallvec = "1.4"
|
||||
num_enum = "0.5"
|
||||
|
||||
# Serde
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
@ -306,13 +306,13 @@ impl<const N: usize> Default for AsciiStrArr<N> {
|
||||
}
|
||||
|
||||
impl<const N: usize> fmt::Debug for AsciiStrArr<N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
AsciiStr::fmt(self.as_ascii(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> fmt::Display for AsciiStrArr<N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
AsciiStr::fmt(self.as_ascii(), f)
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,9 +11,6 @@
|
||||
//! All these strings must only contain ascii characters, thus on read and on write, if any
|
||||
//! strings contain non-ascii characters, an error will occur
|
||||
|
||||
// Lints
|
||||
//#![allow(clippy::missing_docs_in_private_items)] // A lot of our private items are simple digimon types, so they don't need documentation
|
||||
|
||||
// Modules
|
||||
pub mod card;
|
||||
pub mod deck;
|
||||
|
||||
@ -20,7 +20,7 @@ pub use iter::WithInstructionsIter;
|
||||
use super::Func;
|
||||
use crate::{
|
||||
game::exe::{
|
||||
instruction::{Directive, PseudoInstruction, Register, SimpleInstruction},
|
||||
instruction::{BasicInstruction, Directive, PseudoInstruction, Register},
|
||||
Instruction, Pos,
|
||||
},
|
||||
util::discarding_sorted_merge_iter::DiscardingSortedMergeIter,
|
||||
@ -84,15 +84,15 @@ impl FuncTable {
|
||||
#[allow(clippy::too_many_lines)] // TODO: Refactor?
|
||||
#[allow(clippy::enum_glob_use)] // It's only for this function
|
||||
pub fn from_instructions<'a>(instructions: &(impl Iterator<Item = (Pos, &'a Instruction)> + Clone)) -> Self {
|
||||
use Instruction::{Pseudo, Simple};
|
||||
use BasicInstruction::*;
|
||||
use Instruction::{Basic, Pseudo};
|
||||
use PseudoInstruction::*;
|
||||
use SimpleInstruction::*;
|
||||
|
||||
// Get all returns
|
||||
let returns: BTreeSet<Pos> = instructions
|
||||
.clone()
|
||||
.filter_map(|(pos, instruction)| match instruction {
|
||||
Simple(Jr { rs: Register::Ra }) => Some(pos),
|
||||
Basic(Jr { rs: Register::Ra }) => Some(pos),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
@ -101,7 +101,7 @@ impl FuncTable {
|
||||
let tailcalls: BTreeSet<Pos> = instructions
|
||||
.clone()
|
||||
.filter_map(|(pos, instruction)| match instruction {
|
||||
Simple(J { .. } | Jr { .. }) => Some(pos),
|
||||
Basic(J { .. } | Jr { .. }) => Some(pos),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
@ -110,7 +110,7 @@ impl FuncTable {
|
||||
let labels: BTreeSet<Pos> = instructions
|
||||
.clone()
|
||||
.filter_map(|(_, instruction)| match instruction {
|
||||
Simple(
|
||||
Basic(
|
||||
J { target } |
|
||||
Beq { target, .. } |
|
||||
Bne { target, .. } |
|
||||
@ -131,7 +131,7 @@ impl FuncTable {
|
||||
let function_entries: BTreeSet<Pos> = instructions
|
||||
.clone()
|
||||
.filter_map(|(_, instruction)| match instruction {
|
||||
Simple(Jal { target }) => Some(*target),
|
||||
Basic(Jal { target }) => Some(*target),
|
||||
Instruction::Directive(Directive::Dw(target)) => Some(Pos(*target)),
|
||||
_ => None,
|
||||
})
|
||||
@ -179,7 +179,7 @@ impl FuncTable {
|
||||
// TODO: Generalize this in `Instruction` as a method that
|
||||
// returns all registers used maybe.
|
||||
match instruction {
|
||||
Simple(Sb { rt, rs, .. } | Lb { rt, rs, .. } | Lbu { rt, rs, .. }) => {
|
||||
Basic(Sb { rt, rs, .. } | Lb { rt, rs, .. } | Lbu { rt, rs, .. }) => {
|
||||
if let Some(idx) = rt.arg_idx() {
|
||||
if arguments[idx].is_none() {
|
||||
arguments[idx] = Some("u8");
|
||||
@ -199,7 +199,7 @@ impl FuncTable {
|
||||
}
|
||||
},
|
||||
|
||||
Simple(Sh { rt, rs, .. } | Lh { rt, rs, .. } | Lhu { rt, rs, .. }) => {
|
||||
Basic(Sh { rt, rs, .. } | Lh { rt, rs, .. } | Lhu { rt, rs, .. }) => {
|
||||
if let Some(idx) = rt.arg_idx() {
|
||||
if arguments[idx].is_none() {
|
||||
arguments[idx] = Some("u16");
|
||||
@ -219,7 +219,7 @@ impl FuncTable {
|
||||
}
|
||||
},
|
||||
|
||||
Simple(
|
||||
Basic(
|
||||
Swl { rt, rs, .. } | Sw { rt, rs, .. } | Swr { rt, rs, .. } | Lwl { rt, rs, .. } | Lw { rt, rs, .. } | Lwr { rt, rs, .. },
|
||||
) => {
|
||||
if let Some(idx) = rt.arg_idx() {
|
||||
@ -244,7 +244,7 @@ impl FuncTable {
|
||||
}
|
||||
},
|
||||
|
||||
Simple(
|
||||
Basic(
|
||||
Addi { rt, rs, .. } |
|
||||
Addiu { rt, rs, .. } |
|
||||
Slti { rt, rs, .. } |
|
||||
@ -274,7 +274,7 @@ impl FuncTable {
|
||||
}
|
||||
},
|
||||
|
||||
Simple(
|
||||
Basic(
|
||||
Add { rd, rs, rt } |
|
||||
Addu { rd, rs, rt } |
|
||||
Sub { rd, rs, rt } |
|
||||
@ -306,7 +306,7 @@ impl FuncTable {
|
||||
}
|
||||
},
|
||||
|
||||
Simple(
|
||||
Basic(
|
||||
Sll { rd, rt, .. } |
|
||||
Srl { rd, rt, .. } |
|
||||
Sra { rd, rt, .. } |
|
||||
@ -327,7 +327,7 @@ impl FuncTable {
|
||||
}
|
||||
},
|
||||
|
||||
Simple(Jalr { rd, rs }) => {
|
||||
Basic(Jalr { rd, rs }) => {
|
||||
if let Some(idx) = rd.arg_idx() {
|
||||
if arguments[idx].is_none() {
|
||||
arguments[idx] = Some("u32");
|
||||
@ -340,7 +340,7 @@ impl FuncTable {
|
||||
}
|
||||
},
|
||||
|
||||
Simple(Lui { rt, .. }) => {
|
||||
Basic(Lui { rt, .. }) => {
|
||||
if let Some(idx) = rt.arg_idx() {
|
||||
if arguments[idx].is_none() {
|
||||
arguments[idx] = Some("u32");
|
||||
@ -348,7 +348,7 @@ impl FuncTable {
|
||||
}
|
||||
},
|
||||
|
||||
Simple(Mfhi { rd } | Mflo { rd }) => {
|
||||
Basic(Mfhi { rd } | Mflo { rd }) => {
|
||||
if let Some(idx) = rd.arg_idx() {
|
||||
if arguments[idx].is_none() {
|
||||
arguments[idx] = Some("u32");
|
||||
@ -356,7 +356,7 @@ impl FuncTable {
|
||||
}
|
||||
},
|
||||
|
||||
Simple(
|
||||
Basic(
|
||||
Bltz { rs, .. } |
|
||||
Bgez { rs, .. } |
|
||||
Bgtz { rs, .. } |
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
pub mod error;
|
||||
|
||||
// Exports
|
||||
use std::fmt;
|
||||
|
||||
pub use error::{FromBytesError, ToBytesError};
|
||||
|
||||
// Import
|
||||
@ -49,7 +51,34 @@ pub struct Header {
|
||||
pub initial_sp_offset: u32,
|
||||
|
||||
/// Executable region marker
|
||||
pub marker: AsciiStrArr<0x7b4>,
|
||||
pub marker: AsciiStrArr<0x7b3>,
|
||||
}
|
||||
|
||||
impl fmt::Display for Header {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Self {
|
||||
ref initial_pc,
|
||||
ref initial_gp,
|
||||
ref dest,
|
||||
ref size,
|
||||
ref memfill_start,
|
||||
ref memfill_size,
|
||||
ref initial_sp_base,
|
||||
ref initial_sp_offset,
|
||||
ref marker,
|
||||
..
|
||||
} = self;
|
||||
|
||||
write!(
|
||||
f,
|
||||
"PC: {initial_pc:#x}
|
||||
GP: {initial_gp:#x}
|
||||
Destination: {dest:#x} / size: {size:#x}
|
||||
Memfill: {memfill_start:#X} / size: {memfill_size:#x}
|
||||
SP: {initial_sp_base:#x} / offset: {initial_sp_offset:#x}
|
||||
Marker: {marker:?}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Header {
|
||||
@ -76,8 +105,8 @@ impl Bytes for Header {
|
||||
memfill_size : [0x4],
|
||||
initial_sp_base : [0x4],
|
||||
initial_sp_offset: [0x4],
|
||||
_zero2 : [0x13],
|
||||
marker : [0x7b5],
|
||||
_zero2 : [0x14],
|
||||
marker : [0x7b4],
|
||||
);
|
||||
|
||||
// If the magic is wrong, return Err
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
//! Psx cpu instructions
|
||||
|
||||
// Modules
|
||||
pub mod basic;
|
||||
pub mod directive;
|
||||
pub mod pseudo;
|
||||
pub mod raw;
|
||||
pub mod reg;
|
||||
pub mod simple;
|
||||
|
||||
// Exports
|
||||
pub use basic::BasicInstruction;
|
||||
pub use directive::Directive;
|
||||
pub use pseudo::PseudoInstruction;
|
||||
pub use raw::{FromRawIter, Raw};
|
||||
pub use reg::Register;
|
||||
pub use simple::SimpleInstruction;
|
||||
|
||||
// Imports
|
||||
use crate::game::exe::Pos;
|
||||
@ -21,8 +21,8 @@ use crate::game::exe::Pos;
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum Instruction {
|
||||
/// A simple instruction
|
||||
Simple(SimpleInstruction),
|
||||
/// A basic instruction
|
||||
Basic(BasicInstruction),
|
||||
|
||||
/// A pseudo instruction
|
||||
Pseudo(PseudoInstruction),
|
||||
@ -111,9 +111,9 @@ impl<I: Iterator<Item = Raw> + Clone> Iterator for Iter<I> {
|
||||
return self.try_next_from(Instruction::Directive);
|
||||
}
|
||||
|
||||
// Else try to decode it as a pseudo, simple or directive, in that order.
|
||||
// Else try to decode it as a pseudo, basic or directive, in that order.
|
||||
self.try_next_from(Instruction::Pseudo)
|
||||
.or_else(|| self.try_next_from(Instruction::Simple))
|
||||
.or_else(|| self.try_next_from(Instruction::Basic))
|
||||
.or_else(|| self.try_next_from(Instruction::Directive))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,19 +1,130 @@
|
||||
//! Raw instructions
|
||||
//! Basic instructions
|
||||
//!
|
||||
//! This modules defines all the basic instructions from the psx.
|
||||
//! They are all 1 word (`u32`) long.
|
||||
|
||||
// Lints
|
||||
// #[allow(clippy::similar_names)]
|
||||
// Modules
|
||||
pub mod alu_imm;
|
||||
pub mod cond;
|
||||
pub mod jmp;
|
||||
pub mod load;
|
||||
pub mod special;
|
||||
pub mod store;
|
||||
|
||||
// Exports
|
||||
pub use alu_imm::{AluImmInst, AluImmRaw};
|
||||
pub use cond::{CondInst, CondRaw};
|
||||
pub use jmp::{JmpInst, JmpRaw};
|
||||
pub use load::{LoadInst, LoadRaw};
|
||||
pub use special::{SpecialInst, SpecialRaw};
|
||||
pub use store::{StoreInst, StoreRaw};
|
||||
|
||||
// Imports
|
||||
use super::{FromRawIter, Raw, Register};
|
||||
use crate::{game::exe::Pos, util::SignedHex};
|
||||
use bitmatch::bitmatch;
|
||||
use int_conv::{SignExtended, Signed, Truncate, Truncated};
|
||||
use int_conv::{SignExtended, Signed, Truncate, Truncated, ZeroExtended};
|
||||
|
||||
/// All basic instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum BasicInst {
|
||||
/// Store
|
||||
Store(StoreInst),
|
||||
|
||||
/// Load
|
||||
Load(LoadInst),
|
||||
|
||||
/// Special
|
||||
Special(SpecialInst),
|
||||
|
||||
/// Condition
|
||||
Cond(CondInst),
|
||||
|
||||
/// Jump
|
||||
Jmp(JmpInst),
|
||||
|
||||
/// Alu immediate
|
||||
AluImm(AluImmInst),
|
||||
|
||||
/// Load upper immediate
|
||||
#[display(fmt = "lui {dest}, {value:#x}")]
|
||||
Lui {
|
||||
/// Destination
|
||||
dest: Register,
|
||||
|
||||
/// Value
|
||||
value: u16,
|
||||
},
|
||||
}
|
||||
|
||||
impl BasicInst {
|
||||
// TODO: MAybe extract the strings if the bitmatch macro allows for it.
|
||||
|
||||
/// 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> {
|
||||
Some(
|
||||
#[bitmatch]
|
||||
#[allow(unused_variables)] // TODO: Remove once `CopInst` is implemented.
|
||||
match raw {
|
||||
"000000_sssss_ttttt_ddddd_iiiii_ffffff" => Self::Special(SpecialInst::decode(SpecialRaw { s, t, d, i, f })?),
|
||||
"00001p_iiiii_iiiii_iiiii_iiiii_iiiiii" => Self::Jmp(JmpInst::decode(JmpRaw { p, i })),
|
||||
"000ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::Cond(CondInst::decode(CondRaw { p, s, t, i })?),
|
||||
"001111_?????_ttttt_iiiii_iiiii_iiiiii" => Self::Lui {
|
||||
dest: Register::new(t)?,
|
||||
value: i.truncated::<u16>(),
|
||||
},
|
||||
"001ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::AluImm(AluImmInst::decode(AluImmRaw { p, s, t, i })?),
|
||||
"100ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::Store(StoreInst::decode(StoreRaw { p, s, t, i })?),
|
||||
"101ppp_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::Load(LoadInst::decode(LoadRaw { p, s, t, i })?),
|
||||
|
||||
/*
|
||||
"0100nn_1iiii_iiiii_iiiii_iiiii_iiiiii" => Self::Cop(),
|
||||
"0100nn_00000_ttttt_ddddd_?????_000000" => Self::Cop(),
|
||||
"0100nn_00010_ttttt_ddddd_?????_000000" => Self::Cop(),
|
||||
"0100nn_00100_ttttt_ddddd_?????_000000" => Self::Cop(),
|
||||
"0100nn_00110_ttttt_ddddd_?????_000000" => Self::Cop(),
|
||||
"0100nn_01000_00000_iiiii_iiiii_iiiiii" => Self::Cop(),
|
||||
"0100nn_01000_00001_iiiii_iiiii_iiiiii" => Self::Cop(),
|
||||
"1100nn_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::Cop(),
|
||||
"1110nn_sssss_ttttt_iiiii_iiiii_iiiiii" => Self::Cop(),
|
||||
*/
|
||||
_ => return None,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
#[bitmatch]
|
||||
pub fn encode(self) -> u32 {
|
||||
#[rustfmt::skip]
|
||||
match self {
|
||||
Self::Special(inst) => { let SpecialRaw { s, t, d, i, f } = inst.encode(); bitpack!("000000_sssss_ttttt_ddddd_iiiii_ffffff") },
|
||||
Self::Jmp (inst) => { let JmpRaw { p, i } = inst.encode(); bitpack!("00001p_iiiii_iiiii_iiiii_iiiii_iiiiii") },
|
||||
Self::Cond (inst) => { let CondRaw { p, s, t, i } = inst.encode(); bitpack!("000ppp_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Self::AluImm (inst) => { let AluImmRaw { p, s, t, i } = inst.encode(); bitpack!("001ppp_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Self::Store (inst) => { let StoreRaw { p, s, t, i } = inst.encode(); bitpack!("100ppp_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Self::Load (inst) => { let LoadRaw { p, s, t, i } = inst.encode(); bitpack!("101ppp_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
|
||||
Self::Lui { dest, value } => {
|
||||
let t = dest.idx();
|
||||
let i = value.zero_extended::<u32>();
|
||||
bitpack!("001111_?????_ttttt_iiiii_iiiii_iiiiii")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// All simple instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
#[allow(clippy::missing_docs_in_private_items)] // They're mostly register and immediate names.
|
||||
pub enum SimpleInstruction {
|
||||
pub enum BasicInstruction {
|
||||
/// Store byte
|
||||
#[display(fmt = "sb {rt}, {:#x}({rs})", "SignedHex(offset)")]
|
||||
Sb { rt: Register, rs: Register, offset: i16 },
|
||||
@ -287,22 +398,27 @@ pub enum SimpleInstruction {
|
||||
Break { imm: u32 },
|
||||
}
|
||||
|
||||
impl SimpleInstruction {
|
||||
impl BasicInstruction {
|
||||
/// Decodes an instruction from it's raw representation
|
||||
#[bitmatch]
|
||||
#[allow(clippy::cognitive_complexity)] // It's just a big match, not much we can do about it.
|
||||
fn decode_repr(Raw { repr, pos }: Raw) -> Option<Self> {
|
||||
fn decode_repr(raws: &[u32], pos: Pos) -> Option<(Self, &[u32])> {
|
||||
#[allow(clippy::enum_glob_use)] // It's local to this function and REALLY reduces on the noise
|
||||
use SimpleInstruction::*;
|
||||
use BasicInstruction::*;
|
||||
|
||||
/// Alias for `Register::new`
|
||||
fn reg(idx: u32) -> Option<Register> {
|
||||
Register::new(idx.truncated())
|
||||
}
|
||||
|
||||
let (raw, raws) = match raws {
|
||||
[raw, raws @ ..] => (raw, raws),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
#[rustfmt::skip]
|
||||
let instruction = #[bitmatch]
|
||||
match repr {
|
||||
match raw {
|
||||
"000000_?????_ttttt_ddddd_iiiii_000000" => Sll { rd: reg(d)?, rt: reg(t)?, imm: i.truncated()},
|
||||
"000000_?????_ttttt_ddddd_iiiii_000010" => Srl { rd: reg(d)?, rt: reg(t)?, imm: i.truncated()},
|
||||
"000000_?????_ttttt_ddddd_iiiii_000011" => Sra { rd: reg(d)?, rt: reg(t)?, imm: i.truncated()},
|
||||
@ -349,8 +465,8 @@ impl SimpleInstruction {
|
||||
|
||||
"000100_sssss_ttttt_iiiii_iiiii_iiiiii" => Beq { rs: reg(s)?, rt: reg(t)?, target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
|
||||
"000101_sssss_ttttt_iiiii_iiiii_iiiiii" => Bne { rs: reg(s)?, rt: reg(t)?, target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
|
||||
"000110_sssss_?????_iiiii_iiiii_iiiiii" => Blez { rs: reg(s)? , target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
|
||||
"000111_sssss_?????_iiiii_iiiii_iiiiii" => Bgtz { rs: reg(s)? , target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
|
||||
"000110_sssss_?????_iiiii_iiiii_iiiiii" => Blez { rs: reg(s)? , target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
|
||||
"000111_sssss_?????_iiiii_iiiii_iiiiii" => Bgtz { rs: reg(s)? , target: pos + (i.truncated::<u16>().as_signed().sign_extended::<i32>() + 1) * 4 },
|
||||
|
||||
"001000_sssss_ttttt_iiiii_iiiii_iiiiii" => Addi { rt: reg(t)?, rs: reg(s)?, imm: i.truncated::<u16>().as_signed() },
|
||||
"001001_sssss_ttttt_iiiii_iiiii_iiiiii" => Addiu { rt: reg(t)?, rs: reg(s)?, imm: i.truncated::<u16>().as_signed() },
|
||||
@ -359,7 +475,7 @@ impl SimpleInstruction {
|
||||
"001100_sssss_ttttt_iiiii_iiiii_iiiiii" => Andi { rt: reg(t)?, rs: reg(s)?, imm: i.truncated() },
|
||||
"001101_sssss_ttttt_iiiii_iiiii_iiiiii" => Ori { rt: reg(t)?, rs: reg(s)?, imm: i.truncated() },
|
||||
"001110_sssss_ttttt_iiiii_iiiii_iiiiii" => Xori { rt: reg(t)?, rs: reg(s)?, imm: i.truncated() },
|
||||
"001111_?????_ttttt_iiiii_iiiii_iiiiii" => Lui { rt: reg(t)? , imm: i.truncated() },
|
||||
"001111_?????_ttttt_iiiii_iiiii_iiiiii" => Lui { rt: reg(t)? , imm: i.truncated() },
|
||||
|
||||
"0100nn_1iiii_iiiii_iiiii_iiiii_iiiiii" => CopN { n: n.truncate(), imm: i},
|
||||
|
||||
@ -390,16 +506,114 @@ impl SimpleInstruction {
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(instruction)
|
||||
Some((instruction, raws))
|
||||
}
|
||||
|
||||
/// Encodes an instruction.
|
||||
#[bitmatch]
|
||||
#[must_use]
|
||||
pub fn encode_repr(self, pos: Pos) -> Raw {
|
||||
#[allow(clippy::enum_glob_use)] // It's local to this function and REALLY reduces on the noise
|
||||
use BasicInstruction::*;
|
||||
|
||||
#[rustfmt::skip]
|
||||
let repr = match self {
|
||||
Sll { rd, rt, imm: i } => { let (d, t) = (rd.idx(), rt.idx()); bitpack!("000000_00000_ttttt_ddddd_iiiii_000000") },
|
||||
|
||||
Srl { rd, rt, imm: i } => { let (d, t) = (rd.idx(), rt.idx()); bitpack!("000000_00000_ttttt_ddddd_iiiii_000010") },
|
||||
Sra { rd, rt, imm: i } => { let (d, t) = (rd.idx(), rt.idx()); bitpack!("000000_00000_ttttt_ddddd_iiiii_000011") },
|
||||
|
||||
Sllv { rd, rt, rs } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_000100") },
|
||||
Srlv { rd, rt, rs } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_000110") },
|
||||
Srav { rd, rt, rs } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_000111") },
|
||||
|
||||
Jr { rs } => { let s = rs.idx() ; bitpack!("000000_sssss_00000_00000_00000_001000") },
|
||||
Jalr { rd, rs } => { let (d, s) = (rd.idx(), rs.idx()); bitpack!("000000_sssss_00000_ddddd_00000_001001") },
|
||||
|
||||
Syscall { imm: i } => { bitpack!("000000_iiiii_iiiii_iiiii_iiiii_001100") },
|
||||
Break { imm: i } => { bitpack!("000000_iiiii_iiiii_iiiii_iiiii_001101") },
|
||||
|
||||
Mfhi { rd } => { let d = rd.idx(); bitpack!("000000_00000_00000_ddddd_00000_010000") },
|
||||
Mthi { rs } => { let s = rs.idx(); bitpack!("000000_sssss_00000_00000_00000_010001") },
|
||||
Mflo { rd } => { let d = rd.idx(); bitpack!("000000_00000_00000_ddddd_00000_010010") },
|
||||
Mtlo { rs } => { let s = rs.idx(); bitpack!("000000_sssss_00000_00000_00000_010011") },
|
||||
|
||||
Mult { rs, rt } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_00000_00000_011000") },
|
||||
Multu { rs, rt } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_00000_00000_011001") },
|
||||
Div { rs, rt } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_00000_00000_011010") },
|
||||
Divu { rs, rt } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_00000_00000_011011") },
|
||||
|
||||
Add { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100000") },
|
||||
Addu { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100001") },
|
||||
Sub { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100010") },
|
||||
Subu { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100011") },
|
||||
And { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100100") },
|
||||
Or { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100101") },
|
||||
Xor { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100110") },
|
||||
Nor { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_100111") },
|
||||
|
||||
Slt { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_101010") },
|
||||
Sltu { rd, rs, rt } => { let (d, t, s) = (rd.idx(), rt.idx(), rs.idx()); bitpack!("000000_sssss_ttttt_ddddd_00000_101011") },
|
||||
|
||||
Bltz { rs, target } => { let s = rs.idx(); let i = (target - pos) / 4 - 1; bitpack!("000001_sssss_00000_iiiii_iiiii_iiiiii") },
|
||||
Bgez { rs, target } => { let s = rs.idx(); let i = (target - pos) / 4 - 1; bitpack!("000001_sssss_00000_iiiii_iiiii_iiiiii") },
|
||||
Bltzal { rs, target } => { let s = rs.idx(); let i = (target - pos) / 4 - 1; bitpack!("000001_sssss_00000_iiiii_iiiii_iiiiii") },
|
||||
Bgezal { rs, target } => { let s = rs.idx(); let i = (target - pos) / 4 - 1; bitpack!("000001_sssss_00000_iiiii_iiiii_iiiiii") },
|
||||
|
||||
J { target } => { let i = (target - (pos & 0x0fff_ffff)) / 4; bitpack!("000010_iiiii_iiiii_iiiii_iiiii_iiiiii") },
|
||||
Jal { target } => { let i = (target - (pos & 0x0fff_ffff)) / 4; bitpack!("000011_iiiii_iiiii_iiiii_iiiii_iiiiii") },
|
||||
|
||||
Beq { rs, rt, target } => { let (s, t) = (rs.idx(), rt.idx()); let i = (target - pos) / 4 - 1; bitpack!("000100_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Bne { rs, rt, target } => { let (s, t) = (rs.idx(), rt.idx()); let i = (target - pos) / 4 - 1; bitpack!("000101_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Blez { rs , target } => { let s = rs.idx() ; let i = (target - pos) / 4 - 1; bitpack!("000110_sssss_00000_iiiii_iiiii_iiiiii") },
|
||||
Bgtz { rs , target } => { let s = rs.idx() ; let i = (target - pos) / 4 - 1; bitpack!("000111_sssss_00000_iiiii_iiiii_iiiiii") },
|
||||
|
||||
Addi { rt, rs, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("001000_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Addiu { rt, rs, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("001001_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Slti { rt, rs, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("001010_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Sltiu { rt, rs, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("001011_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Andi { rt, rs, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("001100_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Ori { rt, rs, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("001101_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Xori { rt, rs, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("001110_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Lui { rt, imm: i } => { let t = rt.idx(); bitpack!("001111_00000_ttttt_iiiii_iiiii_iiiiii") },
|
||||
|
||||
CopN { n, imm: i } => { bitpack!("0100nn_1iiii_iiiii_iiiii_iiiii_iiiiii") },
|
||||
|
||||
MfcN { n, rt, rd } => { let (t, d) = (rt.idx(), rd.idx()); bitpack!("0100nn_00000_ttttt_ddddd_00000_000000") },
|
||||
CfcN { n, rt, rd } => { let (t, d) = (rt.idx(), rd.idx()); bitpack!("0100nn_00010_ttttt_ddddd_00000_000000") },
|
||||
MtcN { n, rt, rd } => { let (t, d) = (rt.idx(), rd.idx()); bitpack!("0100nn_00100_ttttt_ddddd_00000_000000") },
|
||||
CtcN { n, rt, rd } => { let (t, d) = (rt.idx(), rd.idx()); bitpack!("0100nn_00110_ttttt_ddddd_00000_000000") },
|
||||
BcNf { n, target: i } => { bitpack!("0100nn_01000_00000_iiiii_iiiii_iiiiii") },
|
||||
BcNt { n, target: i } => { bitpack!("0100nn_01000_00001_iiiii_iiiii_iiiiii") },
|
||||
|
||||
Lb { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("100000_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Lh { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("100001_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Lwl { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("100010_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Lw { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("100011_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Lbu { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("100100_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Lhu { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("100101_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Lwr { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("100110_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
|
||||
Sb { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("101000_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Sh { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("101001_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Swl { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("101010_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Sw { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("101011_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
Swr { rt, rs, offset: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("101110_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
|
||||
LwcN { n, rs, rt, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("1100nn_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
SwcN { n, rs, rt, imm: i } => { let (t, s) = (rt.idx(), rs.idx()); bitpack!("1110nn_sssss_ttttt_iiiii_iiiii_iiiiii") },
|
||||
};
|
||||
|
||||
Raw { repr, pos }
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawIter for SimpleInstruction {
|
||||
impl FromRawIter for BasicInstruction {
|
||||
type Decoded = Option<(Pos, Self)>;
|
||||
|
||||
fn decode<I: Iterator<Item = Raw> + Clone>(iter: &mut I) -> Self::Decoded {
|
||||
let raw = iter.next()?;
|
||||
let instruction = Self::decode_repr(raw)?;
|
||||
let (instruction, _) = Self::decode_repr(std::slice::from_ref(&raw.repr), raw.pos)?;
|
||||
Some((raw.pos, instruction))
|
||||
}
|
||||
}
|
||||
108
dcb/src/game/exe/instruction/basic/alu_imm.rs
Normal file
108
dcb/src/game/exe/instruction/basic/alu_imm.rs
Normal file
@ -0,0 +1,108 @@
|
||||
//! Alu immediate instructions
|
||||
|
||||
// Imports
|
||||
use crate::{game::exe::instruction::Register, util::SignedHex};
|
||||
use int_conv::{Signed, Truncated, ZeroExtended};
|
||||
use std::{convert::TryFrom, fmt};
|
||||
|
||||
/// Alu register opcode (lower 3 bits)
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum AluImmOp {
|
||||
/// Add
|
||||
Add = 0x0,
|
||||
|
||||
/// Add unsigned
|
||||
AddUnsigned = 0x1,
|
||||
|
||||
/// Set less than
|
||||
SetLessThan = 0x2,
|
||||
|
||||
/// Set less than unsigned
|
||||
SetLessThanUnsigned = 0x3,
|
||||
|
||||
/// And
|
||||
And = 0x4,
|
||||
|
||||
/// Or
|
||||
Or = 0x5,
|
||||
|
||||
/// Xor
|
||||
Xor = 0x6,
|
||||
}
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct AluImmRaw {
|
||||
/// Opcode (lower 3 bits)
|
||||
pub p: u32,
|
||||
|
||||
/// Rs
|
||||
pub s: u32,
|
||||
|
||||
/// Rt
|
||||
pub t: u32,
|
||||
|
||||
/// Immediate
|
||||
pub i: u32,
|
||||
}
|
||||
|
||||
/// Alu register instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct AluImmInst {
|
||||
/// Destination register, `rd`
|
||||
pub dest: Register,
|
||||
|
||||
/// Lhs argument, `rs`
|
||||
pub lhs: Register,
|
||||
|
||||
/// Rhs immediate argument
|
||||
pub rhs: u32,
|
||||
|
||||
/// Opcode
|
||||
pub op: AluImmOp,
|
||||
}
|
||||
|
||||
impl AluImmInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: AluImmRaw) -> Option<Self> {
|
||||
let op = AluImmOp::try_from(raw.p.truncated::<u8>()).ok()?;
|
||||
|
||||
Some(Self {
|
||||
dest: Register::new(raw.t)?,
|
||||
lhs: Register::new(raw.s)?,
|
||||
rhs: raw.i,
|
||||
op,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> AluImmRaw {
|
||||
let p = u8::from(self.op).zero_extended::<u32>();
|
||||
let s = self.lhs.idx();
|
||||
let t = self.dest.idx();
|
||||
let i = self.rhs;
|
||||
|
||||
AluImmRaw { p, s, t, i }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AluImmInst {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Self { op, dest, lhs, rhs } = self;
|
||||
|
||||
#[rustfmt::skip]
|
||||
match op {
|
||||
AluImmOp::Add => write!(f, "addi {dest}, {lhs}, {:#x}", SignedHex(rhs.as_signed())),
|
||||
AluImmOp::AddUnsigned => write!(f, "addiu {dest}, {lhs}, {:#x}", SignedHex(rhs.as_signed())),
|
||||
AluImmOp::SetLessThan => write!(f, "slti {dest}, {lhs}, {:#x}", SignedHex(rhs.as_signed())),
|
||||
AluImmOp::SetLessThanUnsigned => write!(f, "sltiu {dest}, {lhs}, {rhs:#x}"),
|
||||
AluImmOp::And => write!(f, "andi {dest}, {lhs}, {rhs:#x}"),
|
||||
AluImmOp::Or => write!(f, "ori {dest}, {lhs}, {rhs:#x}"),
|
||||
AluImmOp::Xor => write!(f, "xori {dest}, {lhs}, {rhs:#x}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
131
dcb/src/game/exe/instruction/basic/cond.rs
Normal file
131
dcb/src/game/exe/instruction/basic/cond.rs
Normal file
@ -0,0 +1,131 @@
|
||||
//! Condition branches
|
||||
|
||||
// Imports
|
||||
use crate::{game::exe::instruction::Register, 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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
74
dcb/src/game/exe/instruction/basic/jmp.rs
Normal file
74
dcb/src/game/exe/instruction/basic/jmp.rs
Normal file
@ -0,0 +1,74 @@
|
||||
//! Alu register instructions
|
||||
|
||||
// Imports
|
||||
use std::fmt;
|
||||
|
||||
/// Alu register func (bottom bit)
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum JmpKind {
|
||||
/// Jump
|
||||
Jump,
|
||||
|
||||
/// Jump and link
|
||||
Link,
|
||||
}
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct JmpRaw {
|
||||
/// Opcode (bottom bit)
|
||||
pub p: u32,
|
||||
|
||||
/// Immediate
|
||||
pub i: u32,
|
||||
}
|
||||
|
||||
/// Alu register instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct JmpInst {
|
||||
/// Target
|
||||
pub target: u32,
|
||||
|
||||
/// Kind
|
||||
pub kind: JmpKind,
|
||||
}
|
||||
|
||||
impl JmpInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: JmpRaw) -> Self {
|
||||
let kind = match raw.p {
|
||||
0 => JmpKind::Jump,
|
||||
1 => JmpKind::Link,
|
||||
_ => unreachable!("Received invalid bit in opcode."),
|
||||
};
|
||||
|
||||
Self { target: raw.i, kind }
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub const fn encode(self) -> JmpRaw {
|
||||
let p = match self.kind {
|
||||
JmpKind::Jump => 0,
|
||||
JmpKind::Link => 1,
|
||||
};
|
||||
|
||||
JmpRaw { p, i: self.target }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Format with `pc` / `label`.
|
||||
|
||||
impl fmt::Display for JmpInst {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Self { target, kind } = self;
|
||||
|
||||
let mnemonic = match kind {
|
||||
JmpKind::Jump => "j",
|
||||
JmpKind::Link => "jal",
|
||||
};
|
||||
|
||||
write!(f, "{mnemonic} {target}")
|
||||
}
|
||||
}
|
||||
109
dcb/src/game/exe/instruction/basic/load.rs
Normal file
109
dcb/src/game/exe/instruction/basic/load.rs
Normal file
@ -0,0 +1,109 @@
|
||||
//! Load instructions
|
||||
|
||||
// Imports
|
||||
use crate::game::exe::instruction::Register;
|
||||
use int_conv::{Signed, Truncated, ZeroExtended};
|
||||
use std::{convert::TryFrom, fmt};
|
||||
|
||||
/// Load instruction opcode (lower 3 bits)
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum LoadOpcode {
|
||||
/// Byte, `i8`
|
||||
Byte = 0x0,
|
||||
|
||||
/// Half-word, `i16`
|
||||
HalfWord = 0x1,
|
||||
|
||||
/// Word left-bits, `u32`
|
||||
WordLeft = 0x2,
|
||||
|
||||
/// Word, `u32`
|
||||
Word = 0x3,
|
||||
|
||||
/// Byte unsigned, `u8`
|
||||
ByteUnsigned = 0x4,
|
||||
|
||||
/// Half-word unsigned, `u16`
|
||||
HalfWordUnsigned = 0x5,
|
||||
|
||||
/// Word right-bits, `u32`
|
||||
WordRight = 0x6,
|
||||
}
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct LoadRaw {
|
||||
/// Opcode (lower 3 bits)
|
||||
pub p: u32,
|
||||
|
||||
/// Rs
|
||||
pub s: u32,
|
||||
|
||||
/// Rt
|
||||
pub t: u32,
|
||||
|
||||
/// Immediate
|
||||
pub i: u32,
|
||||
}
|
||||
|
||||
/// Load instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct LoadInst {
|
||||
/// Source register, `rt`
|
||||
pub source: Register,
|
||||
|
||||
/// Destination register, `rs`
|
||||
pub dest: Register,
|
||||
|
||||
/// Destination offset.
|
||||
pub offset: i16,
|
||||
|
||||
/// Opcode
|
||||
pub op: LoadOpcode,
|
||||
}
|
||||
|
||||
impl LoadInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: LoadRaw) -> Option<Self> {
|
||||
let op = LoadOpcode::try_from(raw.p.truncated::<u8>()).ok()?;
|
||||
|
||||
Some(Self {
|
||||
source: Register::new(raw.t)?,
|
||||
dest: Register::new(raw.s)?,
|
||||
offset: raw.i.truncated::<u16>().as_signed(),
|
||||
op,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> LoadRaw {
|
||||
let t = self.source.idx();
|
||||
let s = self.dest.idx();
|
||||
let i = self.offset.as_unsigned().zero_extended::<u32>();
|
||||
let p = u8::from(self.op).zero_extended::<u32>();
|
||||
|
||||
LoadRaw { p, s, t, i }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LoadInst {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Self { source, dest, offset, op } = self;
|
||||
|
||||
let mnemonic = match op {
|
||||
LoadOpcode::Byte => "lb",
|
||||
LoadOpcode::HalfWord => "lh",
|
||||
LoadOpcode::WordLeft => "lwl",
|
||||
LoadOpcode::Word => "lw",
|
||||
LoadOpcode::ByteUnsigned => "lbu",
|
||||
LoadOpcode::HalfWordUnsigned => "lhu",
|
||||
LoadOpcode::WordRight => "lwr",
|
||||
};
|
||||
|
||||
write!(f, "{mnemonic} {dest}, {offset}({source})")
|
||||
}
|
||||
}
|
||||
143
dcb/src/game/exe/instruction/basic/special.rs
Normal file
143
dcb/src/game/exe/instruction/basic/special.rs
Normal file
@ -0,0 +1,143 @@
|
||||
//! ALU instructions
|
||||
|
||||
// Modules
|
||||
pub mod alu_reg;
|
||||
pub mod jmp;
|
||||
pub mod mult;
|
||||
pub mod shift;
|
||||
pub mod sys;
|
||||
|
||||
// Exports
|
||||
pub use alu_reg::{AluRegInst, AluRegRaw};
|
||||
pub use jmp::{JmpInst, JmpRaw};
|
||||
pub use mult::{MultInst, MultRaw};
|
||||
pub use shift::{ShiftInst, ShiftRaw};
|
||||
pub use sys::{SysInst, SysRaw};
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct SpecialRaw {
|
||||
/// Rs
|
||||
pub s: u32,
|
||||
|
||||
/// Rt
|
||||
pub t: u32,
|
||||
|
||||
/// Rd
|
||||
pub d: u32,
|
||||
|
||||
/// Immediate
|
||||
pub i: u32,
|
||||
|
||||
/// Func
|
||||
pub f: u32,
|
||||
}
|
||||
|
||||
impl From<SpecialRaw> for ShiftRaw {
|
||||
fn from(SpecialRaw { t, d, s, i, f }: SpecialRaw) -> Self {
|
||||
Self { t, d, s, i, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ShiftRaw> for SpecialRaw {
|
||||
fn from(ShiftRaw { t, d, s, i, f }: ShiftRaw) -> Self {
|
||||
Self { t, d, s, i, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SpecialRaw> for JmpRaw {
|
||||
fn from(SpecialRaw { d, s, f, .. }: SpecialRaw) -> Self {
|
||||
Self { d, s, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JmpRaw> for SpecialRaw {
|
||||
fn from(JmpRaw { d, s, f }: JmpRaw) -> Self {
|
||||
Self { t: 0, d, s, i: 0, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SpecialRaw> for SysRaw {
|
||||
fn from(SpecialRaw { t, d, s, i, f }: SpecialRaw) -> Self {
|
||||
Self { t, d, s, i, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SysRaw> for SpecialRaw {
|
||||
fn from(SysRaw { t, d, s, i, f }: SysRaw) -> Self {
|
||||
Self { t, d, s, i, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SpecialRaw> for MultRaw {
|
||||
fn from(SpecialRaw { t, d, s, f, .. }: SpecialRaw) -> Self {
|
||||
Self { t, d, s, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultRaw> for SpecialRaw {
|
||||
fn from(MultRaw { t, d, s, f }: MultRaw) -> Self {
|
||||
Self { t, d, s, i: 0, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SpecialRaw> for AluRegRaw {
|
||||
fn from(SpecialRaw { t, d, s, f, .. }: SpecialRaw) -> Self {
|
||||
Self { t, d, s, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AluRegRaw> for SpecialRaw {
|
||||
fn from(AluRegRaw { t, d, s, f }: AluRegRaw) -> Self {
|
||||
Self { t, d, s, i: 0, f }
|
||||
}
|
||||
}
|
||||
|
||||
/// Special instructions for `opcode: 0`
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum SpecialInst {
|
||||
/// Shift instruction
|
||||
Shift(ShiftInst),
|
||||
|
||||
/// Jump
|
||||
Jmp(JmpInst),
|
||||
|
||||
/// Sys
|
||||
Sys(SysInst),
|
||||
|
||||
/// Mult
|
||||
Mult(MultInst),
|
||||
|
||||
/// Alu
|
||||
Alu(AluRegInst),
|
||||
}
|
||||
|
||||
impl SpecialInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: SpecialRaw) -> Option<Self> {
|
||||
Some(match raw.f {
|
||||
0x00..0x08 => Self::Shift(ShiftInst::decode(raw.into())?),
|
||||
0x08..0x0c => Self::Jmp(JmpInst::decode(raw.into())?),
|
||||
0x0c..0x10 => Self::Sys(SysInst::decode(raw.into())?),
|
||||
0x10..0x20 => Self::Mult(MultInst::decode(raw.into())?),
|
||||
0x20..0x30 => Self::Alu(AluRegInst::decode(raw.into())?),
|
||||
0x30..0x40 => return None,
|
||||
|
||||
_ => unreachable!("Func was larger than 6 bits."),
|
||||
})
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> SpecialRaw {
|
||||
match self {
|
||||
Self::Shift(inst) => inst.encode().into(),
|
||||
Self::Jmp(inst) => inst.encode().into(),
|
||||
Self::Sys(inst) => inst.encode().into(),
|
||||
Self::Mult(inst) => inst.encode().into(),
|
||||
Self::Alu(inst) => inst.encode().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
121
dcb/src/game/exe/instruction/basic/special/alu_reg.rs
Normal file
121
dcb/src/game/exe/instruction/basic/special/alu_reg.rs
Normal file
@ -0,0 +1,121 @@
|
||||
//! Alu register instructions
|
||||
|
||||
// Imports
|
||||
use crate::game::exe::instruction::Register;
|
||||
use int_conv::{Truncated, ZeroExtended};
|
||||
use std::{convert::TryFrom, fmt};
|
||||
|
||||
/// Alu register func
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum AluRegFunc {
|
||||
/// Add
|
||||
Add = 0x20,
|
||||
|
||||
/// Add unsigned
|
||||
AddUnsigned = 0x21,
|
||||
|
||||
/// Sub
|
||||
Sub = 0x22,
|
||||
|
||||
/// Sub unsigned
|
||||
SubUnsigned = 0x23,
|
||||
|
||||
/// And
|
||||
And = 0x24,
|
||||
|
||||
/// Or
|
||||
Or = 0x25,
|
||||
|
||||
/// Xor
|
||||
Xor = 0x26,
|
||||
|
||||
/// Nor
|
||||
Nor = 0x27,
|
||||
|
||||
/// Set less than
|
||||
SetLessThan = 0x2a,
|
||||
|
||||
/// Set less than unsigned
|
||||
SetLessThanUnsigned = 0x2b,
|
||||
}
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct AluRegRaw {
|
||||
/// Rs
|
||||
pub s: u32,
|
||||
|
||||
/// Rt
|
||||
pub t: u32,
|
||||
|
||||
/// Rd
|
||||
pub d: u32,
|
||||
|
||||
/// Func
|
||||
pub f: u32,
|
||||
}
|
||||
|
||||
/// Alu register instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct AluRegInst {
|
||||
/// Destination register, `rd`
|
||||
pub dest: Register,
|
||||
|
||||
/// Lhs argument, `rs`
|
||||
pub lhs: Register,
|
||||
|
||||
/// Rhs argument, `rt`
|
||||
pub rhs: Register,
|
||||
|
||||
/// Function
|
||||
pub func: AluRegFunc,
|
||||
}
|
||||
|
||||
impl AluRegInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: AluRegRaw) -> Option<Self> {
|
||||
let func = AluRegFunc::try_from(raw.f.truncated::<u8>()).ok()?;
|
||||
|
||||
Some(Self {
|
||||
dest: Register::new(raw.d)?,
|
||||
lhs: Register::new(raw.s)?,
|
||||
rhs: Register::new(raw.t)?,
|
||||
func,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> AluRegRaw {
|
||||
let d = self.dest.idx();
|
||||
let s = self.lhs.idx();
|
||||
let t = self.rhs.idx();
|
||||
let f = u8::from(self.func).zero_extended::<u32>();
|
||||
|
||||
AluRegRaw { f, t, d, s }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AluRegInst {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Self { dest, lhs, rhs, func } = self;
|
||||
|
||||
let mnemonic = match func {
|
||||
AluRegFunc::Add => "add",
|
||||
AluRegFunc::AddUnsigned => "addu",
|
||||
AluRegFunc::Sub => "sub",
|
||||
AluRegFunc::SubUnsigned => "subu",
|
||||
AluRegFunc::And => "and",
|
||||
AluRegFunc::Or => "or",
|
||||
AluRegFunc::Xor => "xor",
|
||||
AluRegFunc::Nor => "nor",
|
||||
AluRegFunc::SetLessThan => "slt",
|
||||
AluRegFunc::SetLessThanUnsigned => "sltu",
|
||||
};
|
||||
|
||||
write!(f, "{mnemonic} {dest}, {lhs}, {rhs}")
|
||||
}
|
||||
}
|
||||
78
dcb/src/game/exe/instruction/basic/special/jmp.rs
Normal file
78
dcb/src/game/exe/instruction/basic/special/jmp.rs
Normal file
@ -0,0 +1,78 @@
|
||||
//! Jumps
|
||||
|
||||
// Imports
|
||||
use crate::game::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}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
168
dcb/src/game/exe/instruction/basic/special/mult.rs
Normal file
168
dcb/src/game/exe/instruction/basic/special/mult.rs
Normal file
@ -0,0 +1,168 @@
|
||||
//! Multiplications
|
||||
|
||||
// Imports
|
||||
use crate::game::exe::instruction::Register;
|
||||
use std::fmt;
|
||||
|
||||
/// Operation func
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum MultKind {
|
||||
/// Multiplication
|
||||
Mult,
|
||||
|
||||
/// Division
|
||||
Div,
|
||||
}
|
||||
|
||||
/// Operation mode
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum MultMode {
|
||||
/// Signed
|
||||
Signed,
|
||||
|
||||
/// Unsigned
|
||||
Unsigned,
|
||||
}
|
||||
|
||||
/// Multiplication register
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum MultReg {
|
||||
/// Lo
|
||||
Lo,
|
||||
|
||||
/// Hi
|
||||
Hi,
|
||||
}
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct MultRaw {
|
||||
/// Rs
|
||||
pub s: u32,
|
||||
|
||||
/// Rt
|
||||
pub t: u32,
|
||||
|
||||
/// Rd
|
||||
pub d: u32,
|
||||
|
||||
/// Func
|
||||
pub f: u32,
|
||||
}
|
||||
|
||||
/// Multiplication instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum MultInst {
|
||||
/// Multiplication
|
||||
Mult {
|
||||
/// Kind
|
||||
kind: MultKind,
|
||||
|
||||
/// Mode
|
||||
mode: MultMode,
|
||||
|
||||
/// Lhs argument
|
||||
lhs: Register,
|
||||
|
||||
/// Rhs argument
|
||||
rhs: Register,
|
||||
},
|
||||
|
||||
/// Move from
|
||||
MoveFrom {
|
||||
/// Destination
|
||||
dest: Register,
|
||||
|
||||
/// Source
|
||||
source: MultReg,
|
||||
},
|
||||
|
||||
/// Move to
|
||||
MoveTo {
|
||||
/// Source
|
||||
source: Register,
|
||||
|
||||
/// Destination
|
||||
dest: MultReg,
|
||||
},
|
||||
}
|
||||
|
||||
impl MultInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: MultRaw) -> Option<Self> {
|
||||
#[rustfmt::skip]
|
||||
Some(match raw.f {
|
||||
0x10 => Self::MoveFrom { dest: Register::new(raw.d)?, source: MultReg::Hi },
|
||||
0x12 => Self::MoveFrom { dest: Register::new(raw.d)?, source: MultReg::Lo },
|
||||
|
||||
0x11 => Self::MoveTo { source: Register::new(raw.s)?, dest: MultReg::Hi },
|
||||
0x13 => Self::MoveTo { source: Register::new(raw.s)?, dest: MultReg::Lo },
|
||||
|
||||
0x18 => Self::Mult { kind: MultKind::Mult, mode: MultMode:: Signed, lhs: Register::new(raw.s)?, rhs: Register::new(raw.t)? },
|
||||
0x19 => Self::Mult { kind: MultKind::Mult, mode: MultMode::Unsigned, lhs: Register::new(raw.s)?, rhs: Register::new(raw.t)? },
|
||||
0x1a => Self::Mult { kind: MultKind::Div , mode: MultMode:: Signed, lhs: Register::new(raw.s)?, rhs: Register::new(raw.t)? },
|
||||
0x1b => Self::Mult { kind: MultKind::Div , mode: MultMode::Unsigned, lhs: Register::new(raw.s)?, rhs: Register::new(raw.t)? },
|
||||
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> MultRaw {
|
||||
match self {
|
||||
Self::Mult { kind, mode, lhs, rhs } => MultRaw {
|
||||
s: lhs.idx(),
|
||||
t: rhs.idx(),
|
||||
d: 0,
|
||||
f: match (kind, mode) {
|
||||
(MultKind::Mult, MultMode::Signed) => 0x18,
|
||||
(MultKind::Mult, MultMode::Unsigned) => 0x19,
|
||||
(MultKind::Div, MultMode::Signed) => 0x1a,
|
||||
(MultKind::Div, MultMode::Unsigned) => 0x1b,
|
||||
},
|
||||
},
|
||||
Self::MoveFrom { dest, source } => MultRaw {
|
||||
s: 0,
|
||||
t: 0,
|
||||
d: dest.idx(),
|
||||
f: match source {
|
||||
MultReg::Hi => 0x10,
|
||||
MultReg::Lo => 0x12,
|
||||
},
|
||||
},
|
||||
Self::MoveTo { source, dest } => MultRaw {
|
||||
s: source.idx(),
|
||||
t: 0,
|
||||
d: 0,
|
||||
f: match dest {
|
||||
MultReg::Hi => 0x11,
|
||||
MultReg::Lo => 0x13,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MultInst {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
#[rustfmt::skip]
|
||||
Self::Mult { kind, mode, lhs, rhs } => match (kind, mode) {
|
||||
(MultKind::Mult, MultMode:: Signed) => write!(f, "mult {lhs}, {rhs}"),
|
||||
(MultKind::Mult, MultMode::Unsigned) => write!(f, "multu {lhs}, {rhs}"),
|
||||
(MultKind::Div , MultMode:: Signed) => write!(f, "div {lhs}, {rhs}"),
|
||||
(MultKind::Div , MultMode::Unsigned) => write!(f, "diu {lhs}, {rhs}"),
|
||||
},
|
||||
Self::MoveFrom { dest, source } => match source {
|
||||
MultReg::Hi => write!(f, "mfhi {dest}"),
|
||||
MultReg::Lo => write!(f, "mflo {dest}"),
|
||||
},
|
||||
Self::MoveTo { source, dest } => match dest {
|
||||
MultReg::Hi => write!(f, "mthi {source}"),
|
||||
MultReg::Lo => write!(f, "mtlo {source}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
84
dcb/src/game/exe/instruction/basic/special/shift.rs
Normal file
84
dcb/src/game/exe/instruction/basic/special/shift.rs
Normal file
@ -0,0 +1,84 @@
|
||||
//! 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
93
dcb/src/game/exe/instruction/basic/special/shift/imm.rs
Normal file
93
dcb/src/game/exe/instruction/basic/special/shift/imm.rs
Normal file
@ -0,0 +1,93 @@
|
||||
//! Immediate shifts
|
||||
|
||||
// Imports
|
||||
use crate::game::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 {
|
||||
/// Source register, `rd`
|
||||
pub source: Register,
|
||||
|
||||
/// Destination register, `rt`
|
||||
pub dest: Register,
|
||||
|
||||
/// Immediate argument
|
||||
pub arg: 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 {
|
||||
source: Register::new(raw.t)?,
|
||||
dest: Register::new(raw.d)?,
|
||||
arg: raw.i.truncated::<u16>().as_signed(),
|
||||
func,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> ShiftImmRaw {
|
||||
let t = self.source.idx();
|
||||
let d = self.dest.idx();
|
||||
let i = self.arg.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 { source, dest, arg, func } = self;
|
||||
|
||||
let mnemonic = match func {
|
||||
ShiftImmFunc::LeftLogical => "sll",
|
||||
ShiftImmFunc::RightLogical => "srl",
|
||||
ShiftImmFunc::RightArithmetic => "sra",
|
||||
};
|
||||
|
||||
write!(f, "{mnemonic} {dest}, {source}, {arg}")
|
||||
}
|
||||
}
|
||||
93
dcb/src/game/exe/instruction/basic/special/shift/reg.rs
Normal file
93
dcb/src/game/exe/instruction/basic/special/shift/reg.rs
Normal file
@ -0,0 +1,93 @@
|
||||
//! Register shifts
|
||||
|
||||
// Imports
|
||||
use crate::game::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 dest: 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 {
|
||||
dest: 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.dest.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 { dest, lhs, rhs, func } = self;
|
||||
|
||||
let mnemonic = match func {
|
||||
ShiftRegFunc::LeftLogical => "sllv",
|
||||
ShiftRegFunc::RightLogical => "srlv",
|
||||
ShiftRegFunc::RightArithmetic => "srav",
|
||||
};
|
||||
|
||||
write!(f, "{mnemonic} {dest}, {lhs}, {rhs}")
|
||||
}
|
||||
}
|
||||
89
dcb/src/game/exe/instruction/basic/special/sys.rs
Normal file
89
dcb/src/game/exe/instruction/basic/special/sys.rs
Normal file
@ -0,0 +1,89 @@
|
||||
//! System calls
|
||||
|
||||
// Imports
|
||||
use int_conv::{Truncated, ZeroExtended};
|
||||
use std::{convert::TryFrom, fmt};
|
||||
|
||||
/// Sys instruction func
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum SysFunc {
|
||||
/// Sys
|
||||
Sys = 0xc,
|
||||
|
||||
/// Break
|
||||
Break = 0xd,
|
||||
}
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct SysRaw {
|
||||
/// Rs
|
||||
pub s: u32,
|
||||
|
||||
/// Rt
|
||||
pub t: u32,
|
||||
|
||||
/// Rd
|
||||
pub d: u32,
|
||||
|
||||
/// Immediate
|
||||
pub i: u32,
|
||||
|
||||
/// Func
|
||||
pub f: u32,
|
||||
}
|
||||
|
||||
/// Syscall instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct SysInst {
|
||||
/// Comment
|
||||
pub comment: u32,
|
||||
|
||||
/// Function
|
||||
pub func: SysFunc,
|
||||
}
|
||||
|
||||
impl SysInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(SysRaw { t, d, s, i, f }: SysRaw) -> Option<Self> {
|
||||
let s = s.truncated::<u8>();
|
||||
let t = t.truncated::<u8>();
|
||||
let d = d.truncated::<u8>();
|
||||
let i = i.truncated::<u8>();
|
||||
let comment = u32::from_be_bytes([s, t, d, i]);
|
||||
|
||||
let func = SysFunc::try_from(f.truncated::<u8>()).ok()?;
|
||||
|
||||
Some(Self { comment, func })
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
#[allow(clippy::many_single_char_names)] // `Raw` has single character names
|
||||
pub fn encode(self) -> SysRaw {
|
||||
let [s, t, d, i] = self.comment.to_be_bytes();
|
||||
let s = s.zero_extended::<u32>();
|
||||
let t = t.zero_extended::<u32>();
|
||||
let d = d.zero_extended::<u32>();
|
||||
let i = i.zero_extended::<u32>();
|
||||
let f = u8::from(self.func).zero_extended::<u32>();
|
||||
|
||||
SysRaw { s, t, d, i, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SysInst {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Self { func, comment } = self;
|
||||
|
||||
let mnemonic = match func {
|
||||
SysFunc::Sys => "sys",
|
||||
SysFunc::Break => "break",
|
||||
};
|
||||
|
||||
write!(f, "{mnemonic} {comment}")
|
||||
}
|
||||
}
|
||||
106
dcb/src/game/exe/instruction/basic/store.rs
Normal file
106
dcb/src/game/exe/instruction/basic/store.rs
Normal file
@ -0,0 +1,106 @@
|
||||
//! Store instructions
|
||||
|
||||
// Imports
|
||||
use crate::game::exe::instruction::Register;
|
||||
use int_conv::{Signed, Truncated, ZeroExtended};
|
||||
use std::{convert::TryFrom, fmt};
|
||||
|
||||
/// Store instruction opcode (lower 3 bits)
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum StoreOpcode {
|
||||
/// Byte, `u8`
|
||||
Byte = 0x0,
|
||||
|
||||
/// Half-word, `u16`
|
||||
HalfWord = 0x1,
|
||||
|
||||
/// Word left-bits, `u32`
|
||||
WordLeft = 0x2,
|
||||
|
||||
/// Word, `u32`
|
||||
Word = 0x3,
|
||||
|
||||
/// Word right-bits, `u32`
|
||||
WordRight = 0x6,
|
||||
}
|
||||
|
||||
/// Raw representation
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct StoreRaw {
|
||||
/// Opcode (lower 3 bits)
|
||||
pub p: u32,
|
||||
|
||||
/// Rs
|
||||
pub s: u32,
|
||||
|
||||
/// Rt
|
||||
pub t: u32,
|
||||
|
||||
/// Immediate
|
||||
pub i: u32,
|
||||
}
|
||||
|
||||
/// Store instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct StoreInst {
|
||||
/// Source register, `rt`
|
||||
pub source: Register,
|
||||
|
||||
/// Destination register, `rs`
|
||||
pub dest: Register,
|
||||
|
||||
/// Destination offset.
|
||||
pub offset: i16,
|
||||
|
||||
/// Opcode
|
||||
pub op: StoreOpcode,
|
||||
}
|
||||
|
||||
impl StoreInst {
|
||||
/// Decodes this instruction
|
||||
#[must_use]
|
||||
pub fn decode(raw: StoreRaw) -> Option<Self> {
|
||||
let kind = StoreOpcode::try_from(raw.p.truncated::<u8>()).ok()?;
|
||||
|
||||
Some(Self {
|
||||
source: Register::new(raw.t)?,
|
||||
dest: Register::new(raw.s)?,
|
||||
offset: raw.i.truncated::<u16>().as_signed(),
|
||||
op: kind,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encodes this instruction
|
||||
#[must_use]
|
||||
pub fn encode(self) -> StoreRaw {
|
||||
let t = self.source.idx();
|
||||
let s = self.dest.idx();
|
||||
let i = self.offset.as_unsigned().zero_extended::<u32>();
|
||||
let p = u8::from(self.op).zero_extended::<u32>();
|
||||
|
||||
StoreRaw { p, s, t, i }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for StoreInst {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Self {
|
||||
source,
|
||||
dest,
|
||||
offset,
|
||||
op: kind,
|
||||
} = self;
|
||||
|
||||
let mnemonic = match kind {
|
||||
StoreOpcode::Byte => "sb",
|
||||
StoreOpcode::HalfWord => "sh",
|
||||
StoreOpcode::Word => "sw",
|
||||
StoreOpcode::WordRight => "swr",
|
||||
StoreOpcode::WordLeft => "swl",
|
||||
};
|
||||
|
||||
write!(f, "{mnemonic} {dest}, {offset}({source})")
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
//! Pseudo instructions
|
||||
|
||||
// Imports
|
||||
use super::{FromRawIter, Raw, Register, SimpleInstruction};
|
||||
use super::{BasicInstruction, FromRawIter, Raw, Register};
|
||||
use crate::{game::exe::Pos, util::SignedHex};
|
||||
use int_conv::{Join, SignExtended, Signed, ZeroExtended};
|
||||
|
||||
@ -248,17 +248,17 @@ impl FromRawIter for PseudoInstruction {
|
||||
|
||||
#[allow(clippy::similar_names)] // With register names, this happens too much
|
||||
#[allow(clippy::too_many_lines, clippy::clippy::cognitive_complexity)] // We can't separate this into several functions, it's just 1 big match
|
||||
#[allow(clippy::enum_glob_use)] // This reduces the amount of typing for simple instructions and registers
|
||||
#[allow(clippy::enum_glob_use)] // This reduces the amount of typing for basic instructions and registers
|
||||
fn decode<I: Iterator<Item = Raw> + Clone>(iter: &mut I) -> Self::Decoded {
|
||||
use BasicInstruction::*;
|
||||
use Register::*;
|
||||
use SimpleInstruction::*;
|
||||
|
||||
// Get the first instruction
|
||||
let (pos, instruction) = SimpleInstruction::decode(iter)?;
|
||||
let (pos, instruction) = BasicInstruction::decode(iter)?;
|
||||
let pseudo = match instruction {
|
||||
Lui { imm: imm_hi, rt: prev_rt } => {
|
||||
let iter_before = iter.clone();
|
||||
match SimpleInstruction::decode(iter)?.1 {
|
||||
match BasicInstruction::decode(iter)?.1 {
|
||||
Addiu { imm: imm_lo, rt, rs } if rt == prev_rt && rs == prev_rt => Self::La {
|
||||
rx: prev_rt,
|
||||
target: self::hi_plus_lo(imm_lo, imm_hi),
|
||||
|
||||
@ -19,7 +19,7 @@ pub struct Raw {
|
||||
/// returned iterator, that is, consume the least possible
|
||||
/// input in order to produce an atomic part of themselves.
|
||||
pub trait FromRawIter: Sized {
|
||||
/// Returned iterator from [`decode`]
|
||||
/// Returned iterator from [`decode`].
|
||||
type Decoded: IntoIterator<Item = (Pos, Self)>;
|
||||
|
||||
/// Attempts to decode an instruction from an iterator of raw instructions
|
||||
|
||||
@ -31,7 +31,7 @@ macro_rules! generate_register {
|
||||
|
||||
/// Creates a new register index from a `u8`.
|
||||
#[must_use]
|
||||
pub const fn new(idx: u8) -> Option<Self> {
|
||||
pub const fn new(idx: u32) -> Option<Self> {
|
||||
match idx {
|
||||
$(
|
||||
$value => Some( Self::$variant ),
|
||||
@ -40,6 +40,16 @@ macro_rules! generate_register {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the index of this register
|
||||
#[must_use]
|
||||
pub const fn idx(self) -> u32 {
|
||||
match self {
|
||||
$(
|
||||
Self::$variant => $value,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Register> for usize {
|
||||
|
||||
@ -128,7 +128,7 @@ impl std::ops::Sub<Data> for Data {
|
||||
|
||||
// Display
|
||||
impl std::fmt::Display for Data {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{:#x}", u64::from(*self))
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,7 +165,7 @@ impl std::ops::Sub<Real> for Real {
|
||||
|
||||
// Display
|
||||
impl std::fmt::Display for Real {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{:#x}", self.as_u64())
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,5 +58,5 @@ impl_null_ascii_string!(
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
||||
30, 31, 32,
|
||||
1972
|
||||
1971
|
||||
);
|
||||
|
||||
@ -17,7 +17,7 @@ impl<'a, T> LowerHex for SignedHex<&'a T>
|
||||
where
|
||||
SignedHex<T>: LowerHex,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
<SignedHex<T> as LowerHex>::fmt(SignedHex::<T>::ref_cast(self.0), f)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user