Added j[al]b{a, b, c} pseudo instruction for calling bios functions.

Renamed `Inst::may_jump` to `Inst::expects_branch_delay` to reflect it's nature.
This commit is contained in:
Filipe Rodrigues 2021-05-08 15:00:32 +01:00
parent b84117ea48
commit fdbd30e806
5 changed files with 245 additions and 12 deletions

View File

@ -87,7 +87,7 @@ impl<'a> Inst<'a> {
let res: Result<(), ()> = 'validate: {
// If the previous instruction was a jump, and this instruction is larger
// than a simple instruction, don't consider it
if inst.size() > 4 && prev_inst.map_or(false, Self::may_jump) {
if inst.size() > 4 && prev_inst.map_or(false, Self::expects_branch_delay) {
break 'validate Err(());
}
@ -138,9 +138,9 @@ impl<'a> Inst<'a> {
}
impl<'a> Inst<'a> {
/// Returns if this instruction may jump
/// Returns if this instruction expects a branch delay after
#[must_use]
pub const fn may_jump(&self) -> bool {
pub const fn expects_branch_delay(&self) -> bool {
matches!(self, Self::Basic(basic::Inst::Cond(_) | basic::Inst::Jmp(_)))
}
}

View File

@ -5,6 +5,7 @@
//! via the [`Decodable`] trait.
// Modules
pub mod bios;
pub mod load;
pub mod load_imm;
pub mod move_reg;
@ -33,16 +34,21 @@ pub enum Inst {
/// Store
Store(store::Inst),
/// Bios
Bios(bios::Inst),
}
impl Decodable for Inst {
#[rustfmt::skip]
fn decode(insts: impl Iterator<Item = basic::Inst> + Clone) -> Option<Self> {
load_imm ::Inst::decode(insts.clone()).map(Self::LoadImm )
.or_else( || nop ::Inst::decode(insts.clone()).map(Self::Nop ))
.or_else( || load ::Inst::decode(insts.clone()).map(Self::Load ))
.or_else( || store ::Inst::decode(insts.clone()).map(Self::Store ))
.or_else(move || move_reg ::Inst::decode( insts ).map(Self::MoveReg ))
// Note: Order is important
None.or_else(|| bios ::Inst::decode(insts.clone()).map(Self::Bios ))
.or_else(|| load_imm ::Inst::decode(insts.clone()).map(Self::LoadImm))
.or_else(|| nop ::Inst::decode(insts.clone()).map(Self::Nop ))
.or_else(|| load ::Inst::decode(insts.clone()).map(Self::Load ))
.or_else(|| store ::Inst::decode(insts.clone()).map(Self::Store ))
.or_else(|| move_reg ::Inst::decode(insts.clone()).map(Self::MoveReg))
}
}
@ -57,6 +63,7 @@ impl Encodable for Inst {
Inst::MoveReg(inst) => inst.encode(),
Inst::Load(inst) => inst.encode(),
Inst::Store(inst) => inst.encode(),
Inst::Bios(inst) => inst.encode(),
}
}
}
@ -65,8 +72,10 @@ impl<'a> Parsable<'a> for Inst {
fn parse<Ctx: ?Sized + ParseCtx<'a>>(
mnemonic: &'a str, args: &'a [LineArg], ctx: &Ctx,
) -> Result<Self, ParseError> {
// Note: Order is important
#[rustfmt::skip]
let parsers: &[&dyn Fn() -> Result<Self, ParseError>] = &[
&|| bios ::Inst::parse(mnemonic, args, ctx).map(Self::Bios),
&|| load_imm::Inst::parse(mnemonic, args, ctx).map(Self::LoadImm),
&|| nop ::Inst::parse(mnemonic, args, ctx).map(Self::Nop),
&|| move_reg::Inst::parse(mnemonic, args, ctx).map(Self::MoveReg),
@ -101,6 +110,7 @@ impl<'a> InstDisplay<'a> for Inst {
Inst::MoveReg(inst) => inst.mnemonic(ctx),
Inst::Load (inst) => inst.mnemonic(ctx),
Inst::Store (inst) => inst.mnemonic(ctx),
Inst::Bios (inst) => inst.mnemonic(ctx),
}
}
@ -113,6 +123,7 @@ impl<'a> InstDisplay<'a> for Inst {
Inst::MoveReg(inst) => inst.args(ctx),
Inst::Load (inst) => inst.args(ctx),
Inst::Store (inst) => inst.args(ctx),
Inst::Bios (inst) => inst.args(ctx),
}
}
}
@ -125,6 +136,7 @@ impl InstSize for Inst {
Self::MoveReg(inst) => inst.size(),
Self::Load(inst) => inst.size(),
Self::Store(inst) => inst.size(),
Self::Bios(inst) => inst.size(),
}
}
}

View File

@ -0,0 +1,217 @@
//! Bios functions
// Imports
use super::{nop, Decodable, Encodable};
use crate::inst::{
basic, parse::LineArg, DisplayCtx, InstDisplay, InstFmtArg, InstSize, Parsable, ParseCtx, ParseError, Register,
};
use std::{array, convert::TryInto};
/// Bios function kind
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum FuncKind {
/// A function
A,
/// B function
B,
/// C function
C,
}
/// Kind
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Kind {
/// Jump
Jump,
/// Jump and link
JumpLink,
}
/// Bios call instruction
///
/// Alias for
/// ```mips
/// addiu $t2, $zr, <kind>
/// jr $t2
/// + addiu $t1, $zr, <num>
/// ```
/// or
/// ```mips
/// addiu $t1, $zr, <num>
/// addiu $t2, $zr, <kind>
/// jalr $t2
/// + nop
/// ```
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct Inst {
/// Kind
kind: Kind,
/// Function
func: FuncKind,
/// Function number
num: u8,
}
impl Decodable for Inst {
fn decode(mut insts: impl Iterator<Item = basic::Inst> + Clone) -> Option<Self> {
let (kind, func, num) = match (insts.next()?, insts.next()?, insts.next()?, insts.next()) {
(
basic::Inst::Alu(basic::alu::Inst::Imm(basic::alu::imm::Inst {
dst: Register::T2,
lhs: Register::Zr,
kind: basic::alu::imm::Kind::AddUnsigned(func),
})),
basic::Inst::Jmp(basic::jmp::Inst::Reg(basic::jmp::reg::Inst {
target: Register::T2,
kind: basic::jmp::reg::Kind::Jump,
})),
basic::Inst::Alu(basic::alu::Inst::Imm(basic::alu::imm::Inst {
dst: Register::T1,
lhs: Register::Zr,
kind: basic::alu::imm::Kind::AddUnsigned(num),
})),
_,
) => (Kind::Jump, func, num),
(
basic::Inst::Alu(basic::alu::Inst::Imm(basic::alu::imm::Inst {
dst: Register::T1,
lhs: Register::Zr,
kind: basic::alu::imm::Kind::AddUnsigned(num),
})),
basic::Inst::Alu(basic::alu::Inst::Imm(basic::alu::imm::Inst {
dst: Register::T2,
lhs: Register::Zr,
kind: basic::alu::imm::Kind::AddUnsigned(func),
})),
basic::Inst::Jmp(basic::jmp::Inst::Reg(basic::jmp::reg::Inst {
target: Register::T2,
kind: basic::jmp::reg::Kind::JumpLink(Register::Ra),
})),
Some(nop::Inst::INST),
) => (Kind::JumpLink, func, num),
_ => return None,
};
Some(Self {
kind,
func: match func {
0xa0 => FuncKind::A,
0xb0 => FuncKind::B,
0xc0 => FuncKind::C,
_ => return None,
},
num: num.try_into().ok()?,
})
}
}
impl Encodable for Inst {
type Iterator = impl Iterator<Item = basic::Inst>;
#[auto_enums::auto_enum(Iterator)]
fn encode(&self) -> Self::Iterator {
let func = match self.func {
FuncKind::A => 0xa0,
FuncKind::B => 0xb0,
FuncKind::C => 0xc0,
};
match self.kind {
Kind::Jump => array::IntoIter::new([
basic::Inst::Alu(basic::alu::Inst::Imm(basic::alu::imm::Inst {
dst: Register::T2,
lhs: Register::Zr,
kind: basic::alu::imm::Kind::AddUnsigned(func),
})),
basic::Inst::Jmp(basic::jmp::Inst::Reg(basic::jmp::reg::Inst {
target: Register::T2,
kind: basic::jmp::reg::Kind::Jump,
})),
basic::Inst::Alu(basic::alu::Inst::Imm(basic::alu::imm::Inst {
dst: Register::T1,
lhs: Register::Zr,
kind: basic::alu::imm::Kind::AddUnsigned(self.num.into()),
})),
]),
Kind::JumpLink => array::IntoIter::new([
basic::Inst::Alu(basic::alu::Inst::Imm(basic::alu::imm::Inst {
dst: Register::T1,
lhs: Register::Zr,
kind: basic::alu::imm::Kind::AddUnsigned(self.num.into()),
})),
basic::Inst::Alu(basic::alu::Inst::Imm(basic::alu::imm::Inst {
dst: Register::T2,
lhs: Register::Zr,
kind: basic::alu::imm::Kind::AddUnsigned(func),
})),
basic::Inst::Jmp(basic::jmp::Inst::Reg(basic::jmp::reg::Inst {
target: Register::T2,
kind: basic::jmp::reg::Kind::JumpLink(Register::Ra),
})),
nop::Inst::INST,
]),
}
}
}
impl<'a> Parsable<'a> for Inst {
fn parse<Ctx: ?Sized + ParseCtx<'a>>(
mnemonic: &'a str, args: &'a [LineArg], ctx: &Ctx,
) -> Result<Self, ParseError> {
let (kind, func) = match mnemonic {
"jba" => (Kind::Jump, FuncKind::A),
"jbb" => (Kind::Jump, FuncKind::B),
"jbc" => (Kind::Jump, FuncKind::C),
"jalba" => (Kind::JumpLink, FuncKind::A),
"jalbb" => (Kind::JumpLink, FuncKind::B),
"jalbc" => (Kind::JumpLink, FuncKind::C),
_ => return Err(ParseError::UnknownMnemonic),
};
let num = match args {
[LineArg::Expr(num)] => ctx.eval_expr_as(num)?,
_ => return Err(ParseError::InvalidArguments),
};
Ok(Self { kind, func, num })
}
}
impl<'a> InstDisplay<'a> for Inst {
type Args = array::IntoIter<InstFmtArg<'a>, 1>;
type Mnemonic = &'static str;
#[rustfmt::skip]
fn mnemonic<Ctx: DisplayCtx>(&'a self, _ctx: &Ctx) -> Self::Mnemonic {
match (self.kind, self.func) {
(Kind::Jump , FuncKind::A) => "jba",
(Kind::Jump , FuncKind::B) => "jbb",
(Kind::Jump , FuncKind::C) => "jbc",
(Kind::JumpLink, FuncKind::A) => "jalba",
(Kind::JumpLink, FuncKind::B) => "jalbb",
(Kind::JumpLink, FuncKind::C) => "jalbc",
}
}
fn args<Ctx: DisplayCtx>(&'a self, _ctx: &Ctx) -> Self::Args {
array::IntoIter::new([InstFmtArg::literal(self.num)])
}
}
impl InstSize for Inst {
fn size(&self) -> usize {
match self.kind {
Kind::Jump => 12,
Kind::JumpLink => 16,
}
}
}

View File

@ -166,12 +166,16 @@ fn main() -> Result<(), anyhow::Error> {
// Make sure this instruction has an branch delay marker is the previous instruction
// has a jump
anyhow::ensure!(
self::implies(branch_delay, || last_inst.as_ref().map_or(false, Inst::may_jump)),
self::implies(branch_delay, || last_inst
.as_ref()
.map_or(false, Inst::expects_branch_delay)),
"{}: Branch delay marker must be used only when the previous instruction may jump",
line_idx
);
anyhow::ensure!(
self::implies(!branch_delay, || !last_inst.as_ref().map_or(false, Inst::may_jump)),
self::implies(!branch_delay, || !last_inst
.as_ref()
.map_or(false, Inst::expects_branch_delay)),
"{}: Branch delay marker is required when the previous instruction may jump",
line_idx
);

View File

@ -158,7 +158,7 @@ fn main() -> Result<(), anyhow::Error> {
print!("\t");
// If we had a branch / jump instruction before this one, add a "+ "
if insts.get(&(pos - 4)).map_or(false, |inst| inst.may_jump()) {
if insts.get(&(pos - 4)).map_or(false, |inst| inst.expects_branch_delay()) {
print!("+ ");
}
@ -235,7 +235,7 @@ fn main() -> Result<(), anyhow::Error> {
}
// If we had a branch / jump instruction before this one, add a "+ "
if prev_inst.as_ref().map_or(false, Inst::may_jump) {
if prev_inst.as_ref().map_or(false, Inst::expects_branch_delay) {
print!("+ ");
}