From 443c6f6c3813b57a96910839b10fafc76d23cb9f Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Mon, 26 Apr 2021 17:58:05 +0100 Subject: [PATCH] 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 `.`. --- Cargo.toml | 2 +- dcb-exe/src/inst.rs | 35 ++++++++++++++++- dcb-exe/src/inst/basic/co.rs | 1 + dcb-exe/src/inst/basic/jmp/reg.rs | 1 + dcb-exe/src/inst/parse.rs | 1 + dcb-exe/src/inst/pseudo/load.rs | 21 +++++++++- dcb-exe/src/inst/pseudo/load_imm.rs | 6 +-- dcb-exe/src/inst/pseudo/store.rs | 17 +++++++- dcb-exe/src/pos.rs | 15 ++++++- dcb-tools/dcb-compiler/src/main.rs | 58 ++++++++++++++++++++++++---- dcb-tools/dcb-decompiler/src/main.rs | 2 +- 11 files changed, 139 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 907657f..b143faf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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", diff --git a/dcb-exe/src/inst.rs b/dcb-exe/src/inst.rs index fa40b09..7578961 100644 --- a/dcb-exe/src/inst.rs +++ b/dcb-exe/src/inst.rs @@ -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 { diff --git a/dcb-exe/src/inst/basic/co.rs b/dcb-exe/src/inst/basic/co.rs index d88f2e1..dbfa655 100644 --- a/dcb-exe/src/inst/basic/co.rs +++ b/dcb-exe/src/inst/basic/co.rs @@ -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, diff --git a/dcb-exe/src/inst/basic/jmp/reg.rs b/dcb-exe/src/inst/basic/jmp/reg.rs index 95553e0..c8f6c11 100644 --- a/dcb-exe/src/inst/basic/jmp/reg.rs +++ b/dcb-exe/src/inst/basic/jmp/reg.rs @@ -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), }, diff --git a/dcb-exe/src/inst/parse.rs b/dcb-exe/src/inst/parse.rs index 67602ba..f9bb242 100644 --- a/dcb-exe/src/inst/parse.rs +++ b/dcb-exe/src/inst/parse.rs @@ -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), } } diff --git a/dcb-exe/src/inst/pseudo/load.rs b/dcb-exe/src/inst/pseudo/load.rs index 1089097..81d72c8 100644 --- a/dcb-exe/src/inst/pseudo/load.rs +++ b/dcb-exe/src/inst/pseudo/load.rs @@ -78,7 +78,16 @@ impl Encodable for Inst { impl<'a> Parsable<'a> for Inst { fn parse(mnemonic: &'a str, args: &'a [LineArg], ctx: &'a Ctx) -> Result { - 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(&'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(&'a self, _ctx: &Ctx) -> Self::Args { diff --git a/dcb-exe/src/inst/pseudo/load_imm.rs b/dcb-exe/src/inst/pseudo/load_imm.rs index 8062af1..7a29c2d 100644 --- a/dcb-exe/src/inst/pseudo/load_imm.rs +++ b/dcb-exe/src/inst/pseudo/load_imm.rs @@ -144,12 +144,12 @@ impl<'a> Parsable<'a> for Inst { fn parse(mnemonic: &'a str, args: &'a [LineArg], ctx: &'a Ctx) -> Result { 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 { diff --git a/dcb-exe/src/inst/pseudo/store.rs b/dcb-exe/src/inst/pseudo/store.rs index 25da371..b7a18d5 100644 --- a/dcb-exe/src/inst/pseudo/store.rs +++ b/dcb-exe/src/inst/pseudo/store.rs @@ -77,7 +77,14 @@ impl Encodable for Inst { impl<'a> Parsable<'a> for Inst { fn parse(mnemonic: &'a str, args: &'a [LineArg], ctx: &'a Ctx) -> Result { - 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(&'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(&'a self, _ctx: &Ctx) -> Self::Args { diff --git a/dcb-exe/src/pos.rs b/dcb-exe/src/pos.rs index 0be8459..6c95729 100644 --- a/dcb-exe/src/pos.rs +++ b/dcb-exe/src/pos.rs @@ -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 for Pos { } } +// `Pos + i64 = Pos` +impl ops::Add for Pos { + type Output = Self; + + fn add(self, rhs: i64) -> Self::Output { + Self( + (self.0.as_signed().sign_extended::().wrapping_add(rhs)) + .truncated::() + .as_unsigned(), + ) + } +} + // `Pos + usize = Pos` impl ops::Add for Pos { type Output = Self; diff --git a/dcb-tools/dcb-compiler/src/main.rs b/dcb-tools/dcb-compiler/src/main.rs index eb0b49d..5d92296 100644 --- a/dcb-tools/dcb-compiler/src/main.rs +++ b/dcb-tools/dcb-compiler/src/main.rs @@ -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 { + Some(self.pos) + } +} + +/// Context +struct Ctx<'a> { + /// Current position + pos: Pos, + + /// All labels by name + labels_by_name: &'a HashMap, +} + +impl ParseCtx for Ctx<'_> { + fn cur_pos(&self) -> Pos { + self.pos + } + + fn label_pos(&self, label: &str) -> Option { + 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, diff --git a/dcb-tools/dcb-decompiler/src/main.rs b/dcb-tools/dcb-decompiler/src/main.rs index 824e407..8646070 100644 --- a/dcb-tools/dcb-decompiler/src/main.rs +++ b/dcb-tools/dcb-decompiler/src/main.rs @@ -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}"),