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:
Filipe Rodrigues 2021-04-26 17:58:05 +01:00
parent be3a9d6ec0
commit 443c6f6c38
11 changed files with 139 additions and 20 deletions

View File

@ -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",

View File

@ -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 {

View File

@ -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,

View File

@ -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),
},

View File

@ -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),
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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;

View File

@ -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,

View File

@ -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}"),