#![allow(clippy::wildcard_enum_match_arm)]
use crate::encoded_field_mask::EncodedFieldMask;
use crate::isa::{IsaExtension, IsaVersion};
use crate::opcodes::{DecodingFlags, Opcode, OpcodeCategory};
use super::OpcodeValidityGate;
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct OpcodeDecoder {
pub(crate) opcode: Opcode,
pub(crate) opcode_category: OpcodeCategory,
pub(crate) mandatory_bits: EncodedFieldMask,
pub(crate) gated_behind: Option<OpcodeValidityGate>,
}
impl OpcodeDecoder {
#[must_use]
pub const fn decode(
word: u32,
flags: &DecodingFlags,
isa_version: IsaVersion,
isa_extension: Option<IsaExtension>,
) -> Self {
let mandatory_bits = EncodedFieldMask::empty();
match isa_extension {
None => {
Self::decode_isa_extension_none_normal(word, mandatory_bits, flags, isa_version)
}
#[cfg(feature = "RSP")]
Some(IsaExtension::RSP) => {
Self::decode_isa_extension_rsp_normal(word, mandatory_bits, flags, isa_version)
}
#[cfg(feature = "R3000GTE")]
Some(IsaExtension::R3000GTE) => {
Self::decode_isa_extension_r3000gte_normal(word, mandatory_bits, flags, isa_version)
}
#[cfg(feature = "R4000ALLEGREX")]
Some(IsaExtension::R4000ALLEGREX) => Self::decode_isa_extension_r4000allegrex_normal(
word,
mandatory_bits,
flags,
isa_version,
),
#[cfg(feature = "R5900EE")]
Some(IsaExtension::R5900EE) => {
Self::decode_isa_extension_r5900ee_normal(word, mandatory_bits, flags, isa_version)
}
#[cfg(not(any(
feature = "RSP",
feature = "R3000GTE",
feature = "R4000ALLEGREX",
feature = "R5900EE",
)))]
Some(_) => unreachable!(),
}
}
#[cfg(feature = "encoder")]
#[must_use]
pub(crate) const fn from_raw_parts(
opcode: Opcode,
opcode_category: OpcodeCategory,
mandatory_bits: EncodedFieldMask,
gated_behind: Option<OpcodeValidityGate>,
) -> Self {
Self {
opcode,
opcode_category,
mandatory_bits,
gated_behind,
}
}
}
impl OpcodeDecoder {
#[must_use]
pub const fn opcode(&self) -> Opcode {
self.opcode
}
#[must_use]
pub const fn opcode_category(&self) -> OpcodeCategory {
self.opcode_category
}
#[must_use]
pub const fn mandatory_bits(&self) -> EncodedFieldMask {
self.mandatory_bits
}
#[must_use]
pub const fn is_valid(&self, flags: &DecodingFlags) -> bool {
let _avoid_unused_warning = flags;
if !self.opcode.is_valid() {
return false;
}
match self.gated_behind {
#[cfg(feature = "RspViceMsp")]
Some(OpcodeValidityGate::RspViceMsp) => {
flags.contains(DecodingFlags::gated_rsp_vice_msp)
}
None => true,
#[cfg(not(any(feature = "RspViceMsp",)))]
Some(_) => unreachable!(),
}
}
}
impl OpcodeDecoder {
#[must_use]
pub const fn is_nop(word: u32) -> bool {
word == 0
}
}
impl OpcodeDecoder {
#[must_use]
pub(crate) const fn fixups_decode_isa_extension_none_normal(
word: u32,
mut opcode: Opcode,
flags: &DecodingFlags,
_isa_version: IsaVersion,
) -> Opcode {
match opcode {
Opcode::core_beq => {
if EncodedFieldMask::rt.get_shifted(word) == 0 {
if EncodedFieldMask::rs.get_shifted(word) == 0 {
if flags
.contains(DecodingFlags::enable_pseudos.union(DecodingFlags::pseudo_b))
{
opcode = Opcode::core_b;
}
} else if flags
.contains(DecodingFlags::enable_pseudos.union(DecodingFlags::pseudo_beqz))
{
opcode = Opcode::core_beqz;
}
}
}
Opcode::core_bne => {
if EncodedFieldMask::rt.get_shifted(word) == 0
&& flags
.contains(DecodingFlags::enable_pseudos.union(DecodingFlags::pseudo_bnez))
{
opcode = Opcode::core_bnez;
}
}
#[cfg(feature = "MIPS_II")]
Opcode::core_beql => {
if EncodedFieldMask::rt.get_shifted(word) == 0
&& flags
.contains(DecodingFlags::enable_pseudos.union(DecodingFlags::pseudo_beqzl))
{
opcode = Opcode::core_beqzl;
}
}
#[cfg(feature = "MIPS_II")]
Opcode::core_bnel => {
if EncodedFieldMask::rt.get_shifted(word) == 0
&& flags
.contains(DecodingFlags::enable_pseudos.union(DecodingFlags::pseudo_bnezl))
{
opcode = Opcode::core_bnezl;
}
}
_ => {}
}
opcode
}
#[must_use]
pub(crate) const fn fixups_decode_isa_extension_none_special(
word: u32,
mut opcode: Opcode,
flags: &DecodingFlags,
_isa_version: IsaVersion,
) -> Opcode {
if Self::is_nop(word) {
return Opcode::core_nop;
}
match opcode {
Opcode::core_nor => {
if EncodedFieldMask::rt.get_shifted(word) == 0
&& flags
.contains(DecodingFlags::enable_pseudos.union(DecodingFlags::pseudo_not))
{
opcode = Opcode::core_not;
}
}
Opcode::core_sub => {
if EncodedFieldMask::rs.get_shifted(word) == 0
&& flags
.contains(DecodingFlags::enable_pseudos.union(DecodingFlags::pseudo_neg))
{
opcode = Opcode::core_neg;
}
}
Opcode::core_subu => {
if EncodedFieldMask::rs.get_shifted(word) == 0
&& flags
.contains(DecodingFlags::enable_pseudos.union(DecodingFlags::pseudo_negu))
{
opcode = Opcode::core_negu;
}
}
_ => {}
}
opcode
}
#[must_use]
pub(crate) const fn fixups_decode_isa_extension_none_regimm(
word: u32,
opcode: Opcode,
flags: &DecodingFlags,
_isa_version: IsaVersion,
) -> Opcode {
match opcode {
Opcode::core_bgezal => {
if EncodedFieldMask::rs.get_shifted(word) == 0
&& flags
.contains(DecodingFlags::enable_pseudos.union(DecodingFlags::pseudo_bal))
{
Opcode::core_bal
} else {
opcode
}
}
_ => opcode,
}
}
}
#[cfg(feature = "RSP")]
impl OpcodeDecoder {}
#[cfg(feature = "R3000GTE")]
impl OpcodeDecoder {}
#[cfg(feature = "R4000ALLEGREX")]
impl OpcodeDecoder {}
#[cfg(feature = "R5900EE")]
impl OpcodeDecoder {
#[must_use]
pub(crate) const fn fixups_decode_isa_extension_r5900ee_special(
self,
word: u32,
_flags: &DecodingFlags,
_isa_version: IsaVersion,
) -> Self {
match self.opcode {
Opcode::core_sync | Opcode::r5900ee_sync_p => {
let mask = EncodedFieldMask::stype;
let mandatory_bits = self.mandatory_bits.union(mask.mask_value(word));
if (mask.get_shifted(word) & 0x10) == 0x10 {
Self {
opcode: Opcode::r5900ee_sync_p,
mandatory_bits,
..self
}
} else {
Self {
opcode: Opcode::core_sync,
mandatory_bits,
..self
}
}
}
_ => self,
}
}
}