mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-04 00:21:57 +00:00
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:
parent
b84117ea48
commit
fdbd30e806
@ -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(_)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
217
dcb-exe/src/inst/pseudo/bios.rs
Normal file
217
dcb-exe/src/inst/pseudo/bios.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
);
|
||||
|
||||
@ -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!("+ ");
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user