mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-06 17:35:40 +00:00
Fixed jalr not supporting parsing single argument.
Fixed `[ls]wc{n}` not supporting non-offset register arguments.
Fixed `li` prefering unsigned values.
`pseudo::{load, store}` now use their unique mnemonics to avoid collision with basic instructions.
`ParseCtx:arg_pos` now supports label offsets.
Added `Inst::write` to write instructions.
Added `Pos + i64` impl.
Brought back and fixed `dcb-compiler`.
Fixed current function labels not having a leading `.`.
This commit is contained in:
parent
be3a9d6ec0
commit
443c6f6c38
@ -14,7 +14,7 @@ members = [
|
||||
"dcb-tools/dcb-uniso-bin",
|
||||
"dcb-tools/dcb-mkiso-bin",
|
||||
"dcb-tools/dcb-decompiler",
|
||||
#"dcb-tools/dcb-compiler",
|
||||
"dcb-tools/dcb-compiler",
|
||||
"dcb-tools/dcb-undrv",
|
||||
"dcb-tools/dcb-mkdrv",
|
||||
"dcb-tools/dcb-unpak",
|
||||
|
||||
@ -21,9 +21,13 @@ pub use reg::Register;
|
||||
pub use size::InstSize;
|
||||
|
||||
// Imports
|
||||
use self::{basic::Decode as _, parse::LineArg, pseudo::Decodable as _};
|
||||
use self::{
|
||||
basic::{Decode as _, TryEncode as _},
|
||||
parse::LineArg,
|
||||
pseudo::{Decodable as _, Encodable as _},
|
||||
};
|
||||
use crate::{DataTable, FuncTable, Pos};
|
||||
use std::{borrow::Borrow, ops::Deref};
|
||||
use std::{borrow::Borrow, io, ops::Deref};
|
||||
|
||||
/// An assembler instruction.
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
@ -94,6 +98,21 @@ impl<'a> Inst<'a> {
|
||||
// Else read it as a directive
|
||||
Directive::decode(pos, bytes).map(Self::Directive).ok_or(DecodeError::NoBytes)
|
||||
}
|
||||
|
||||
/// Writes an instruction
|
||||
pub fn write(&self, f: &mut impl io::Write) -> Result<(), WriteError> {
|
||||
match self {
|
||||
Inst::Basic(inst) => f.write_all(&inst.try_encode().map_err(WriteError::EncodeBasic)?.to_le_bytes())?,
|
||||
Inst::Pseudo(inst) => {
|
||||
for inst in inst.encode() {
|
||||
f.write_all(&inst.try_encode().map_err(WriteError::EncodeBasic)?.to_le_bytes())?;
|
||||
}
|
||||
},
|
||||
Inst::Directive(directive) => directive.write(f)?,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Parsable<'a> for Inst<'a> {
|
||||
@ -154,6 +173,18 @@ impl<'a> InstSize for Inst<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Write error
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum WriteError {
|
||||
/// Io
|
||||
#[error("Unable to write")]
|
||||
Write(#[from] io::Error),
|
||||
|
||||
/// Encode basic
|
||||
#[error("Unable to encode `basic` instruction")]
|
||||
EncodeBasic(#[source] basic::EncodeError),
|
||||
}
|
||||
|
||||
/// Label
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum Label {
|
||||
|
||||
@ -209,6 +209,7 @@ impl<'a> Parsable<'a> for Inst {
|
||||
"lwc0" | "lwc1" | "lwc2" | "lwc3" | "swc0" | "swc1" | "swc2" | "swc3" => {
|
||||
let n = mnemonic[3..].parse().expect("Unable to parse 0..=3");
|
||||
let (dst, src, offset) = match *args {
|
||||
[LineArg::Literal(dst), LineArg::Register(src)] => (dst.try_into().map_err(|_| ParseError::LiteralOutOfRange)?, src, 0),
|
||||
[LineArg::Literal(dst), LineArg::RegisterOffset { register: src, offset }] => (
|
||||
dst.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
src,
|
||||
|
||||
@ -82,6 +82,7 @@ impl<'a> Parsable<'a> for Inst {
|
||||
},
|
||||
|
||||
"jalr" => match *args {
|
||||
[LineArg::Register(target)] => (target, Kind::JumpLink(Register::Ra)),
|
||||
[LineArg::Register(target), LineArg::Register(reg)] => (target, Kind::JumpLink(reg)),
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
},
|
||||
|
||||
@ -31,6 +31,7 @@ pub trait ParseCtx {
|
||||
match *arg {
|
||||
LineArg::Literal(pos) => pos.try_into().map(Pos).map_err(|_| ParseError::LiteralOutOfRange),
|
||||
LineArg::Label(ref label) => self.label_pos(label).ok_or(ParseError::UnknownLabel),
|
||||
LineArg::LabelOffset { ref label, offset } => self.label_pos(label).map(|pos| pos + offset).ok_or(ParseError::UnknownLabel),
|
||||
_ => Err(ParseError::InvalidArguments),
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,7 +78,16 @@ impl Encodable for Inst {
|
||||
|
||||
impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
let kind = Kind::from_mnemonic(mnemonic).ok_or(ParseError::UnknownMnemonic)?;
|
||||
let kind = match mnemonic {
|
||||
"lbi" => Kind::Byte,
|
||||
"lhi" => Kind::HalfWord,
|
||||
"lwli" => Kind::WordLeft,
|
||||
"lwi" => Kind::Word,
|
||||
"lbui" => Kind::ByteUnsigned,
|
||||
"lhui" => Kind::HalfWordUnsigned,
|
||||
"lwri" => Kind::WordRight,
|
||||
_ => return Err(ParseError::UnknownMnemonic),
|
||||
};
|
||||
|
||||
let (value, target) = match *args {
|
||||
[LineArg::Register(value), ref arg] => (value, ctx.arg_pos(arg)?),
|
||||
@ -94,7 +103,15 @@ impl<'a> InstDisplay<'a> for Inst {
|
||||
type Mnemonic = &'static str;
|
||||
|
||||
fn mnemonic<Ctx: DisplayCtx>(&'a self, _ctx: &Ctx) -> Self::Mnemonic {
|
||||
self.kind.mnemonic()
|
||||
match self.kind {
|
||||
Kind::Byte => "lbi",
|
||||
Kind::HalfWord => "lhi",
|
||||
Kind::WordLeft => "lwli",
|
||||
Kind::Word => "lwi",
|
||||
Kind::ByteUnsigned => "lbui",
|
||||
Kind::HalfWordUnsigned => "lhui",
|
||||
Kind::WordRight => "lwri",
|
||||
}
|
||||
}
|
||||
|
||||
fn args<Ctx: DisplayCtx>(&'a self, _ctx: &Ctx) -> Self::Args {
|
||||
|
||||
@ -144,12 +144,12 @@ impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
let to_kind = match mnemonic {
|
||||
"li" => |_ctx: &Ctx, arg: &LineArg| match *arg {
|
||||
// Try `u16`, `i16` then `u32` for the literal
|
||||
// Try `i16`, `u16` then `u32` for the literal
|
||||
LineArg::Literal(value) => {
|
||||
if let Ok(value) = value.try_into() {
|
||||
Ok(Kind::HalfWordUnsigned(value))
|
||||
} else if let Ok(value) = value.try_into() {
|
||||
Ok(Kind::HalfWordSigned(value))
|
||||
} else if let Ok(value) = value.try_into() {
|
||||
Ok(Kind::HalfWordUnsigned(value))
|
||||
} else if let Ok(value) = value.try_into() {
|
||||
Ok(Kind::Word(value))
|
||||
} else {
|
||||
|
||||
@ -77,7 +77,14 @@ impl Encodable for Inst {
|
||||
|
||||
impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
let kind = Kind::from_mnemonic(mnemonic).ok_or(ParseError::UnknownMnemonic)?;
|
||||
let kind = match mnemonic {
|
||||
"sbi" => Kind::Byte,
|
||||
"shi" => Kind::HalfWord,
|
||||
"swli" => Kind::WordLeft,
|
||||
"swi" => Kind::Word,
|
||||
"swri" => Kind::WordRight,
|
||||
_ => return Err(ParseError::UnknownMnemonic),
|
||||
};
|
||||
|
||||
let (value, target) = match *args {
|
||||
[LineArg::Register(value), ref arg] => (value, ctx.arg_pos(arg)?),
|
||||
@ -93,7 +100,13 @@ impl<'a> InstDisplay<'a> for Inst {
|
||||
type Mnemonic = &'static str;
|
||||
|
||||
fn mnemonic<Ctx: DisplayCtx>(&'a self, _ctx: &Ctx) -> Self::Mnemonic {
|
||||
self.kind.mnemonic()
|
||||
match self.kind {
|
||||
Kind::Byte => "sbi",
|
||||
Kind::HalfWord => "shi",
|
||||
Kind::WordLeft => "swli",
|
||||
Kind::Word => "swi",
|
||||
Kind::WordRight => "swri",
|
||||
}
|
||||
}
|
||||
|
||||
fn args<Ctx: DisplayCtx>(&'a self, _ctx: &Ctx) -> Self::Args {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
//! Instruction position
|
||||
|
||||
// Imports
|
||||
use int_conv::{SignExtended, Signed};
|
||||
use int_conv::{SignExtended, Signed, Truncated};
|
||||
use std::{convert::TryFrom, ops};
|
||||
|
||||
/// An instruction position
|
||||
@ -66,6 +66,19 @@ impl ops::Add<i32> for Pos {
|
||||
}
|
||||
}
|
||||
|
||||
// `Pos + i64 = Pos`
|
||||
impl ops::Add<i64> for Pos {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: i64) -> Self::Output {
|
||||
Self(
|
||||
(self.0.as_signed().sign_extended::<i64>().wrapping_add(rhs))
|
||||
.truncated::<i32>()
|
||||
.as_unsigned(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// `Pos + usize = Pos`
|
||||
impl ops::Add<usize> for Pos {
|
||||
type Output = Self;
|
||||
|
||||
@ -19,7 +19,7 @@ use dcb_bytes::Bytes;
|
||||
use dcb_exe::{
|
||||
inst::{
|
||||
parse::{line::InstParser, LineArg},
|
||||
Inst, Label, LabelName,
|
||||
Inst, InstSize, Label, LabelName, Parsable, ParseCtx,
|
||||
},
|
||||
Data, Pos,
|
||||
};
|
||||
@ -109,7 +109,11 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
}
|
||||
}
|
||||
|
||||
let inst_size = Inst::size_from_parsed(&inst, cur_pos).map_err(|_| (n, anyhow::anyhow!("Unable to compile instruction")))?;
|
||||
// TODO: Better solution than assembling the instruction with a dummy context.
|
||||
let inst_size = Inst::parse(&inst.mnemonic, &inst.args, &DummyCtx { pos: cur_pos })
|
||||
.context("Unable to compile instruction")
|
||||
.map_err(|err| (n, err))?
|
||||
.size();
|
||||
|
||||
assert!(insts.insert(cur_pos, (n, inst)).is_none());
|
||||
|
||||
@ -139,8 +143,14 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
|
||||
// For each instruction, pack it and output it to the file
|
||||
for (&pos, (n, inst)) in &insts {
|
||||
// Create the context
|
||||
let ctx = Ctx {
|
||||
pos,
|
||||
labels_by_name: &labels_by_name,
|
||||
};
|
||||
|
||||
let inst =
|
||||
Inst::from_parsed(inst, pos, &labels_by_name).with_context(|| format!("Unable to compile instruction at {} in line {}", pos, n + 1))?;
|
||||
Inst::parse(&inst.mnemonic, &inst.args, &ctx).with_context(|| format!("Unable to compile instruction at {} in line {}", pos, n + 1))?;
|
||||
|
||||
inst.write(&mut output_file).context("Unable to write to file")?;
|
||||
}
|
||||
@ -150,7 +160,7 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
|
||||
// Go back and write the header
|
||||
let header = dcb_exe::Header {
|
||||
pc0: header.pc0,
|
||||
pc0: labels_by_name.get("start").context("No `start` label found")?.0,
|
||||
gp0: header.gp0,
|
||||
start_pos: header.start_pos,
|
||||
size,
|
||||
@ -170,13 +180,45 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Dummy context to get size
|
||||
struct DummyCtx {
|
||||
/// Current position
|
||||
pos: Pos,
|
||||
}
|
||||
|
||||
impl ParseCtx for DummyCtx {
|
||||
fn cur_pos(&self) -> Pos {
|
||||
self.pos
|
||||
}
|
||||
|
||||
fn label_pos(&self, _label: &str) -> Option<Pos> {
|
||||
Some(self.pos)
|
||||
}
|
||||
}
|
||||
|
||||
/// Context
|
||||
struct Ctx<'a> {
|
||||
/// Current position
|
||||
pos: Pos,
|
||||
|
||||
/// All labels by name
|
||||
labels_by_name: &'a HashMap<LabelName, Pos>,
|
||||
}
|
||||
|
||||
impl ParseCtx for Ctx<'_> {
|
||||
fn cur_pos(&self) -> Pos {
|
||||
self.pos
|
||||
}
|
||||
|
||||
fn label_pos(&self, label: &str) -> Option<Pos> {
|
||||
self.labels_by_name.get(label).copied()
|
||||
}
|
||||
}
|
||||
|
||||
/// Header
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct Header {
|
||||
/// Initial program counter
|
||||
pub pc0: u32,
|
||||
|
||||
struct Header {
|
||||
/// Initial global pointer
|
||||
pub gp0: u32,
|
||||
|
||||
|
||||
@ -259,7 +259,7 @@ enum LabelDisplay<'a> {
|
||||
impl<'a> fmt::Display for LabelDisplay<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
LabelDisplay::CurFuncLabel(label) => write!(f, "{label}"),
|
||||
LabelDisplay::CurFuncLabel(label) => write!(f, ".{label}"),
|
||||
LabelDisplay::OtherFuncLabel { func, label } => write!(f, "{func}.{label}"),
|
||||
LabelDisplay::OtherFunc { func } => write!(f, "{func}"),
|
||||
LabelDisplay::Data { name } => write!(f, "{name}"),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user