mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-04 00:21:57 +00:00
Updated parse::line to support label functions.
Fixed single-argument "nop" not being parsed.
This commit is contained in:
parent
926e2669f7
commit
cd6f64cb34
@ -121,9 +121,7 @@ impl Encode 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> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
#[rustfmt::skip]
|
||||
let to_kind = match mnemonic {
|
||||
"addi" => |value: i64| value.try_into().map(Kind::Add ),
|
||||
@ -138,16 +136,16 @@ impl<'a> Parsable<'a> for Inst {
|
||||
|
||||
match *args {
|
||||
// Disallow `slti` and `sltiu` in short form
|
||||
[LineArg::Register(_), LineArg::Literal(_)] if ["slti", "sltiu"].contains(&mnemonic) => {
|
||||
[LineArg::Register(_), LineArg::Expr(_)] if ["slti", "sltiu"].contains(&mnemonic) => {
|
||||
Err(ParseError::InvalidArguments)
|
||||
},
|
||||
|
||||
// Else parse both `$dst, $lhs, value` and `$dst, value`.
|
||||
[LineArg::Register(lhs @ dst), LineArg::Literal(value)] |
|
||||
[LineArg::Register(dst), LineArg::Register(lhs), LineArg::Literal(value)] => Ok(Self {
|
||||
[LineArg::Register(lhs @ dst), LineArg::Expr(ref expr)] |
|
||||
[LineArg::Register(dst), LineArg::Register(lhs), LineArg::Expr(ref expr)] => Ok(Self {
|
||||
dst,
|
||||
lhs,
|
||||
kind: to_kind(value).map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
kind: to_kind(ctx.eval_expr(expr)?).map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
}),
|
||||
_ => Err(ParseError::InvalidArguments),
|
||||
}
|
||||
|
||||
@ -169,14 +169,15 @@ impl Encode 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> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
let inst = match mnemonic {
|
||||
"cop0" | "cop1" | "cop2" | "cop3" => {
|
||||
let n = mnemonic[3..].parse().expect("Unable to parse 0..=3");
|
||||
let imm = match *args {
|
||||
[LineArg::Literal(imm)] => imm.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
let imm = match args {
|
||||
[LineArg::Expr(imm)] => ctx
|
||||
.eval_expr(imm)?
|
||||
.try_into()
|
||||
.map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
};
|
||||
|
||||
@ -189,9 +190,12 @@ impl<'a> Parsable<'a> for Inst {
|
||||
"mtc3" | "ctc0" | "ctc1" | "ctc2" | "ctc3" => {
|
||||
let n = mnemonic[3..].parse().expect("Unable to parse 0..=3");
|
||||
let (reg, imm) = match *args {
|
||||
[LineArg::Register(dst), LineArg::Literal(src)] => {
|
||||
(dst, src.try_into().map_err(|_| ParseError::LiteralOutOfRange)?)
|
||||
},
|
||||
[LineArg::Register(dst), LineArg::Expr(ref imm)] => (
|
||||
dst,
|
||||
ctx.eval_expr(imm)?
|
||||
.try_into()
|
||||
.map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
),
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
};
|
||||
|
||||
@ -224,14 +228,11 @@ 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,
|
||||
offset.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
),
|
||||
[LineArg::Expr(ref dst), LineArg::Register(src)] => (ctx.eval_expr_as(dst)?, src, 0),
|
||||
[LineArg::Expr(ref dst), LineArg::RegisterOffset {
|
||||
register: src,
|
||||
ref offset,
|
||||
}] => (ctx.eval_expr_as(dst)?, src, ctx.eval_expr_as(offset)?),
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
};
|
||||
|
||||
|
||||
@ -124,21 +124,17 @@ impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
// Note: Literals are absolute, not relative
|
||||
|
||||
// Calculates the offset between a position and the current one
|
||||
// with a possible offset
|
||||
let offset_of = |pos: Pos, offset: i64| -> Result<i16, ParseError> {
|
||||
use std::ops::{Add, Div, Sub};
|
||||
pos.sub(ctx.cur_pos())
|
||||
.add(offset)
|
||||
// Calculates the offset between an argument's position and the current one
|
||||
let target_arg_to_offset = |arg| -> Result<i16, ParseError> {
|
||||
use std::ops::{Div, Sub};
|
||||
ctx.arg_pos(arg)?
|
||||
.sub(ctx.cur_pos())
|
||||
.div(4)
|
||||
.sub(1)
|
||||
.try_into()
|
||||
.map_err(|_| ParseError::RelativeJumpTooFar)
|
||||
};
|
||||
|
||||
// Calculates the offset of a literal/label/label offset argument
|
||||
let target_arg_to_offset = |arg| ctx.arg_pos_offset(arg).and_then(|(pos, offset)| offset_of(pos, offset));
|
||||
|
||||
let (arg, offset, kind) = match mnemonic {
|
||||
"b" => match args {
|
||||
[target] => (Register::Zr, target_arg_to_offset(target)?, Kind::Equal(Register::Zr)),
|
||||
|
||||
@ -8,7 +8,7 @@ use crate::inst::{
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
use int_conv::{Signed, Truncated, ZeroExtended};
|
||||
use std::{array, convert::TryInto};
|
||||
use std::array;
|
||||
|
||||
/// Instruction kind
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
@ -131,18 +131,15 @@ impl Encode 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> {
|
||||
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 (value, addr, offset) = match *args {
|
||||
[LineArg::Register(value), LineArg::Register(addr)] => (value, addr, 0),
|
||||
[LineArg::Register(value), LineArg::RegisterOffset { register: addr, offset }] => (
|
||||
value,
|
||||
addr,
|
||||
offset.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
),
|
||||
[LineArg::Register(value), LineArg::RegisterOffset {
|
||||
register: addr,
|
||||
ref offset,
|
||||
}] => (value, addr, ctx.eval_expr_as(offset)?),
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
};
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ use crate::inst::{
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
use int_conv::{Truncated, ZeroExtended};
|
||||
use std::{array, convert::TryInto};
|
||||
use std::array;
|
||||
|
||||
/// Load instructions
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
@ -47,17 +47,15 @@ impl Encode 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> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
if mnemonic != "lui" {
|
||||
return Err(ParseError::UnknownMnemonic);
|
||||
}
|
||||
|
||||
match *args {
|
||||
[LineArg::Register(dst), LineArg::Literal(value)] => Ok(Self {
|
||||
[LineArg::Register(dst), LineArg::Expr(ref expr)] => Ok(Self {
|
||||
dst,
|
||||
value: value.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
value: ctx.eval_expr_as(expr)?,
|
||||
}),
|
||||
_ => Err(ParseError::InvalidArguments),
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ use crate::inst::{
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
use int_conv::{Truncated, ZeroExtended};
|
||||
use std::{array, convert::TryInto};
|
||||
use std::array;
|
||||
|
||||
/// Shift immediate instruction kind
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
@ -106,9 +106,7 @@ impl TryEncode 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> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
let kind = match mnemonic {
|
||||
"sll" => Kind::LeftLogical,
|
||||
"srl" => Kind::RightLogical,
|
||||
@ -117,11 +115,11 @@ impl<'a> Parsable<'a> for Inst {
|
||||
};
|
||||
|
||||
match *args {
|
||||
[LineArg::Register(lhs @ dst), LineArg::Literal(rhs)] |
|
||||
[LineArg::Register(dst), LineArg::Register(lhs), LineArg::Literal(rhs)] => Ok(Self {
|
||||
[LineArg::Register(lhs @ dst), LineArg::Expr(ref rhs)] |
|
||||
[LineArg::Register(dst), LineArg::Register(lhs), LineArg::Expr(ref rhs)] => Ok(Self {
|
||||
dst,
|
||||
lhs,
|
||||
rhs: rhs.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
rhs: ctx.eval_expr_as(rhs)?,
|
||||
kind,
|
||||
}),
|
||||
_ => Err(ParseError::InvalidArguments),
|
||||
|
||||
@ -8,7 +8,7 @@ use crate::inst::{
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
use int_conv::{Signed, Truncated, ZeroExtended};
|
||||
use std::{array, convert::TryInto};
|
||||
use std::array;
|
||||
|
||||
/// Store instruction kind
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
@ -119,18 +119,15 @@ impl Encode 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> {
|
||||
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 (value, addr, offset) = match *args {
|
||||
[LineArg::Register(value), LineArg::Register(addr)] => (value, addr, 0),
|
||||
[LineArg::Register(value), LineArg::RegisterOffset { register: addr, offset }] => (
|
||||
value,
|
||||
addr,
|
||||
offset.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
),
|
||||
[LineArg::Register(value), LineArg::RegisterOffset {
|
||||
register: addr,
|
||||
ref offset,
|
||||
}] => (value, addr, ctx.eval_expr_as(offset)?),
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
};
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ use crate::inst::{
|
||||
parse::LineArg,
|
||||
DisplayCtx, InstDisplay, InstFmtArg, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
use std::{array, convert::TryInto};
|
||||
use std::array;
|
||||
|
||||
/// Sys instruction func
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
@ -87,17 +87,15 @@ impl TryEncode 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> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
let kind = match mnemonic {
|
||||
"sys" => Kind::Sys,
|
||||
"break" => Kind::Break,
|
||||
_ => return Err(ParseError::UnknownMnemonic),
|
||||
};
|
||||
|
||||
let comment = match *args {
|
||||
[LineArg::Literal(comment)] => comment.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
let comment = match args {
|
||||
[LineArg::Expr(comment)] => ctx.eval_expr_as(comment)?,
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
};
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ use ascii::{AsciiChar, AsciiStr};
|
||||
use dcb_util::NextFromBytes;
|
||||
use std::{
|
||||
array,
|
||||
convert::TryInto,
|
||||
io::{self, Write},
|
||||
};
|
||||
|
||||
@ -163,23 +162,21 @@ impl<'a> Directive<'a> {
|
||||
impl<'a> Parsable<'a> for Directive<'a> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
let inst = match mnemonic {
|
||||
"dw" => match *args {
|
||||
[LineArg::Literal(value)] => Self::Dw(value.try_into().map_err(|_| ParseError::LiteralOutOfRange)?),
|
||||
"dw" => match args {
|
||||
[LineArg::Expr(expr)] => Self::Dw(ctx.eval_expr_as(expr)?),
|
||||
[ref arg] => Self::Dw(ctx.arg_pos(arg)?.0),
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
},
|
||||
"dh" => match *args {
|
||||
[LineArg::Literal(value)] => Self::Dh(value.try_into().map_err(|_| ParseError::LiteralOutOfRange)?),
|
||||
"dh" => match args {
|
||||
[LineArg::Expr(expr)] => Self::Dh(ctx.eval_expr_as(expr)?),
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
},
|
||||
"db" => match *args {
|
||||
[LineArg::Literal(value)] => Self::Db(value.try_into().map_err(|_| ParseError::LiteralOutOfRange)?),
|
||||
"db" => match args {
|
||||
[LineArg::Expr(expr)] => Self::Db(ctx.eval_expr_as(expr)?),
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
},
|
||||
".ascii" => match *args {
|
||||
[LineArg::String(ref s)] => {
|
||||
Self::Ascii(AsciiStr::from_ascii(s).map_err(|_| ParseError::NonAsciiString)?)
|
||||
},
|
||||
".ascii" => match args {
|
||||
[LineArg::String(s)] => Self::Ascii(AsciiStr::from_ascii(s).map_err(|_| ParseError::NonAsciiString)?),
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
},
|
||||
_ => return Err(ParseError::UnknownMnemonic),
|
||||
|
||||
@ -6,11 +6,11 @@ pub mod line;
|
||||
|
||||
// Exports
|
||||
pub use error::ParseError;
|
||||
pub use line::{Line, LineArg};
|
||||
pub use line::{Line, LineArg, LineArgExpr, LineLabelFunc};
|
||||
|
||||
// Imports
|
||||
use crate::Pos;
|
||||
use std::convert::TryInto;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
/// Instruction parsing
|
||||
pub trait Parsable<'a>: Sized + 'a {
|
||||
@ -26,34 +26,60 @@ pub trait ParseCtx {
|
||||
/// Returns the position of a label
|
||||
fn label_pos(&self, label: &str) -> Option<Pos>;
|
||||
|
||||
/// Retrieves a position from an argument
|
||||
fn arg_pos(&self, arg: &LineArg) -> Result<Pos, ParseError> {
|
||||
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),
|
||||
/// Evaluates an expression
|
||||
fn eval_expr(&self, expr: &LineArgExpr) -> Result<i64, ParseError> {
|
||||
match *expr {
|
||||
LineArgExpr::Literal(num) => Ok(num),
|
||||
LineArgExpr::Label {
|
||||
ref label,
|
||||
offset,
|
||||
ref func,
|
||||
} => {
|
||||
// Get the label value
|
||||
let value: i64 = self.label_pos(label).ok_or(ParseError::UnknownLabel)?.0.into();
|
||||
|
||||
// Then add the offset
|
||||
let value = value
|
||||
.checked_add(offset.unwrap_or(0))
|
||||
.ok_or(ParseError::LiteralOutOfRange)?;
|
||||
|
||||
// And evaluate any function on it
|
||||
let value = func.as_ref().map_or(Ok(value), |func| self.eval_func(value, func))?;
|
||||
|
||||
Ok(value)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves a position and offset from an argument
|
||||
fn arg_pos_offset(&self, arg: &LineArg) -> Result<(Pos, i64), ParseError> {
|
||||
match *arg {
|
||||
LineArg::Literal(pos) => pos
|
||||
/// Evaluates an expression as `T`, returning `LiteralOutOfBounds` else
|
||||
fn eval_expr_as<T: TryFrom<i64>>(&self, expr: &LineArgExpr) -> Result<T, ParseError> {
|
||||
self.eval_expr(expr)?
|
||||
.try_into()
|
||||
.map_err(|_| ParseError::LiteralOutOfRange)
|
||||
}
|
||||
|
||||
/// Evaluates a function on a literal
|
||||
fn eval_func(&self, value: i64, func: &LineLabelFunc) -> Result<i64, ParseError> {
|
||||
// Converts a value into a position
|
||||
let to_pos = |value: i64| value.try_into().map(Pos).map_err(|_| ParseError::LiteralOutOfRange);
|
||||
|
||||
let value = match func {
|
||||
// For address, first get the value as a position to make sure it's within range
|
||||
LineLabelFunc::AddrLo => i64::from(to_pos(value)?.0 & 0xFFFF),
|
||||
LineLabelFunc::AddrHi => i64::from(to_pos(value)?.0 >> 16u32),
|
||||
};
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// Retrieves a position from an argument
|
||||
fn arg_pos(&self, arg: &LineArg) -> Result<Pos, ParseError> {
|
||||
match arg {
|
||||
LineArg::Expr(expr) => self
|
||||
.eval_expr(expr)?
|
||||
.try_into()
|
||||
.map(|pos| (Pos(pos), 0))
|
||||
.map(Pos)
|
||||
.map_err(|_| ParseError::LiteralOutOfRange),
|
||||
LineArg::Label(ref label) => self
|
||||
.label_pos(label)
|
||||
.map(|pos| (pos, 0))
|
||||
.ok_or(ParseError::UnknownLabel),
|
||||
LineArg::LabelOffset { ref label, offset } => self
|
||||
.label_pos(label)
|
||||
.map(|pos| (pos, offset))
|
||||
.ok_or(ParseError::UnknownLabel),
|
||||
_ => Err(ParseError::InvalidArguments),
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ pub mod error;
|
||||
use std::str::FromStr;
|
||||
|
||||
// Exports
|
||||
pub use error::{ParseLineError, ReadArgError, ReadLiteralError, ReadNameError};
|
||||
pub use error::{ParseLineError, ReadArgError, ReadFuncError, ReadLiteralError, ReadNameError};
|
||||
|
||||
// Imports
|
||||
use crate::inst::Register;
|
||||
@ -48,8 +48,8 @@ impl Line {
|
||||
continue;
|
||||
},
|
||||
|
||||
// If we got '#', we got a mnemonic with no arguments
|
||||
Some('#') => {
|
||||
// If we got '#' or eof, we got a mnemonic with no arguments
|
||||
Some('#') | None => {
|
||||
return Ok(Self {
|
||||
labels,
|
||||
inst: Some(LineInst {
|
||||
@ -59,12 +59,12 @@ impl Line {
|
||||
});
|
||||
},
|
||||
|
||||
// If we got a space or eof, we got a mnemonic
|
||||
Some(' ') | None => {
|
||||
// If we got a space or eof, we found the mnemonic.
|
||||
// On a space, break and read arguments
|
||||
Some(' ') => {
|
||||
line = rest.as_str().trim_start();
|
||||
break name.to_owned();
|
||||
},
|
||||
|
||||
_ => return Err(ParseLineError::InvalidNameSuffix),
|
||||
}
|
||||
};
|
||||
@ -72,14 +72,6 @@ impl Line {
|
||||
// Then read all arguments
|
||||
let mut args = vec![];
|
||||
loop {
|
||||
// If the line starts with a comment, there are no arguments
|
||||
if line.starts_with('#') {
|
||||
return Ok(Self {
|
||||
labels: vec![],
|
||||
inst: Some(LineInst { mnemonic, args }),
|
||||
});
|
||||
}
|
||||
|
||||
// Read an argument
|
||||
let (arg, rest) = self::read_arg(line)?;
|
||||
args.push(arg);
|
||||
@ -134,6 +126,10 @@ pub enum LineArg {
|
||||
/// `<reg>`
|
||||
Register(Register),
|
||||
|
||||
/// Mnemonic
|
||||
/// `^<mnemonic>`
|
||||
Mnemonic(String),
|
||||
|
||||
/// Register offset
|
||||
/// `<offset>(<reg>)`
|
||||
RegisterOffset {
|
||||
@ -141,30 +137,41 @@ pub enum LineArg {
|
||||
register: Register,
|
||||
|
||||
/// The offset
|
||||
offset: i64,
|
||||
offset: LineArgExpr,
|
||||
},
|
||||
|
||||
/// Expression
|
||||
Expr(LineArgExpr),
|
||||
}
|
||||
|
||||
/// Line argument expression
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum LineArgExpr {
|
||||
/// Literal
|
||||
/// `<literal>`
|
||||
Literal(i64),
|
||||
|
||||
/// Label
|
||||
/// `<name>`
|
||||
Label(String),
|
||||
|
||||
/// LabelOffset
|
||||
/// `<name>+<offset>`
|
||||
LabelOffset {
|
||||
/// `<name>(`+<offset>`)?(@<func>)?`
|
||||
Label {
|
||||
/// The label
|
||||
label: String,
|
||||
|
||||
/// The offset
|
||||
offset: i64,
|
||||
},
|
||||
offset: Option<i64>,
|
||||
|
||||
/// Mnemonic
|
||||
/// `^<mnemonic>`
|
||||
Mnemonic(String),
|
||||
/// The function
|
||||
func: Option<LineLabelFunc>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Line label functions
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum LineLabelFunc {
|
||||
/// Lower 16 bits of address
|
||||
AddrLo,
|
||||
|
||||
/// Higher 16 bits of address
|
||||
AddrHi,
|
||||
}
|
||||
|
||||
/// Reads a name
|
||||
@ -201,14 +208,16 @@ fn read_arg(s: &str) -> Result<(LineArg, &str), ReadArgError> {
|
||||
|
||||
// If we got '^', it's a mnemonic
|
||||
Some((_, '^')) => self::read_name(chars.as_str())
|
||||
.map(|(name, rest)| (LineArg::Label(name.to_owned()), rest))
|
||||
.map(|(name, rest)| (LineArg::Mnemonic(name.to_owned()), rest))
|
||||
.map_err(ReadArgError::ReadLabel),
|
||||
|
||||
// If it's numeric, 0..9 or '+' / '-', it's a literal
|
||||
Some((_, '0'..='9' | '+' | '-')) => {
|
||||
// Read the number
|
||||
let (num, rest) = self::read_literal(s).map_err(ReadArgError::ReadLiteral)?;
|
||||
// Else try to read an expression
|
||||
Some(_) => {
|
||||
// Read the expression
|
||||
let (expr, rest) = self::read_expr(s)?;
|
||||
|
||||
// Then check if we have a register
|
||||
let rest = rest.trim_start();
|
||||
match rest.strip_prefix('(') {
|
||||
// If the rest starts with '(', read it as a register offset
|
||||
Some(rest) => match rest.split_once(')') {
|
||||
@ -224,38 +233,58 @@ fn read_arg(s: &str) -> Result<(LineArg, &str), ReadArgError> {
|
||||
Ok((
|
||||
LineArg::RegisterOffset {
|
||||
register: reg,
|
||||
offset: num,
|
||||
offset: expr,
|
||||
},
|
||||
rest,
|
||||
))
|
||||
},
|
||||
None => Err(ReadArgError::MissingRegisterOffsetDelimiter),
|
||||
},
|
||||
None => Ok((LineArg::Literal(num), rest)),
|
||||
None => Ok((LineArg::Expr(expr), rest)),
|
||||
}
|
||||
},
|
||||
|
||||
None => Err(ReadArgError::Empty),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads an expression
|
||||
fn read_expr(s: &str) -> Result<(LineArgExpr, &str), ReadArgError> {
|
||||
let mut chars = s.char_indices();
|
||||
match chars.next() {
|
||||
// If it's numeric, 0..9 or '+' / '-', it's a simple literal
|
||||
Some((_, '0'..='9' | '+' | '-')) => self::read_literal(s)
|
||||
.map(|(num, rest)| (LineArgExpr::Literal(num), rest))
|
||||
.map_err(ReadArgError::ReadLiteral),
|
||||
|
||||
// If it starts with a label char, it's a label
|
||||
Some((_, c)) if self::is_valid_first_name_char(c) => {
|
||||
// Read the label
|
||||
let (label, rest) = self::read_name(s).map_err(ReadArgError::ReadLabel)?;
|
||||
|
||||
// If there's a '+' after, read an offset too
|
||||
match rest.strip_prefix('+') {
|
||||
Some(rest) => {
|
||||
// Read the offset
|
||||
let (offset, rest) = self::read_literal(rest).map_err(ReadArgError::ReadLabelOffset)?;
|
||||
let (offset, rest) = match rest.strip_prefix('+') {
|
||||
Some(rest) => self::read_literal(rest)
|
||||
.map(|(num, rest)| (Some(num), rest))
|
||||
.map_err(ReadArgError::ReadLabelOffset)?,
|
||||
None => (None, rest),
|
||||
};
|
||||
|
||||
Ok((
|
||||
LineArg::LabelOffset {
|
||||
label: label.to_owned(),
|
||||
offset,
|
||||
},
|
||||
rest,
|
||||
))
|
||||
},
|
||||
None => Ok((LineArg::Label(label.to_owned()), rest)),
|
||||
}
|
||||
// If there's a '@' after, read a function too
|
||||
let (func, rest) = match rest.strip_prefix('@') {
|
||||
Some(rest) => self::read_func(rest)
|
||||
.map(|(func, rest)| (Some(func), rest))
|
||||
.map_err(ReadArgError::ReadLabelFunc)?,
|
||||
None => (None, rest),
|
||||
};
|
||||
|
||||
let label = LineArgExpr::Label {
|
||||
label: label.to_owned(),
|
||||
offset,
|
||||
func,
|
||||
};
|
||||
|
||||
Ok((label, rest))
|
||||
},
|
||||
|
||||
// Else it's an invalid char
|
||||
@ -276,6 +305,13 @@ fn read_reg(s: &str) -> Result<(Register, &str), ReadArgError> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a func
|
||||
fn read_func(s: &str) -> Result<(LineLabelFunc, &str), ReadFuncError> {
|
||||
None.or_else(|| s.strip_prefix("addr_hi").map(|rest| (LineLabelFunc::AddrHi, rest)))
|
||||
.or_else(|| s.strip_prefix("addr_lo").map(|rest| (LineLabelFunc::AddrLo, rest)))
|
||||
.ok_or(ReadFuncError::Unknown)
|
||||
}
|
||||
|
||||
/// Reads a string
|
||||
fn read_string(s: &str) -> Result<(String, &str), ReadArgError> {
|
||||
let mut is_escaping = false;
|
||||
|
||||
@ -43,6 +43,14 @@ pub enum ReadLiteralError {
|
||||
Parse(#[from] std::num::ParseIntError),
|
||||
}
|
||||
|
||||
/// Func reading error
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ReadFuncError {
|
||||
/// Parse
|
||||
#[error("Unknown functions")]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/// Argument reading error
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ReadArgError {
|
||||
@ -70,6 +78,10 @@ pub enum ReadArgError {
|
||||
#[error("Unable to read label offset")]
|
||||
ReadLabelOffset(#[source] ReadLiteralError),
|
||||
|
||||
/// Read label func
|
||||
#[error("Unable to read label func")]
|
||||
ReadLabelFunc(#[source] ReadFuncError),
|
||||
|
||||
/// Expected register
|
||||
#[error("Expected register")]
|
||||
ExpectedRegister,
|
||||
|
||||
@ -157,9 +157,10 @@ 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 to_kind = match mnemonic {
|
||||
"li" => |_ctx: &Ctx, arg: &LineArg| match *arg {
|
||||
"li" => |ctx: &Ctx, arg: &LineArg| match arg {
|
||||
// Try `i16`, `u16` then `u32` for the literal
|
||||
LineArg::Literal(value) => {
|
||||
LineArg::Expr(expr) => {
|
||||
let value = ctx.eval_expr(expr)?;
|
||||
if let Ok(value) = value.try_into() {
|
||||
Ok(Kind::HalfWordSigned(value))
|
||||
} else if let Ok(value) = value.try_into() {
|
||||
|
||||
@ -5,10 +5,7 @@ use super::{Decodable, Encodable};
|
||||
use crate::inst::{
|
||||
basic, parse::LineArg, DisplayCtx, InstDisplay, InstFmtArg, InstSize, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
use std::{
|
||||
array,
|
||||
convert::{TryFrom, TryInto},
|
||||
};
|
||||
use std::{array, convert::TryFrom};
|
||||
|
||||
/// No-op
|
||||
///
|
||||
@ -50,15 +47,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> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
if mnemonic != "nop" {
|
||||
return Err(ParseError::UnknownMnemonic);
|
||||
}
|
||||
|
||||
let len = match *args {
|
||||
[LineArg::Literal(len)] => len.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
let len = match args {
|
||||
[] => 1,
|
||||
[LineArg::Expr(len)] => ctx.eval_expr_as(len)?,
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
};
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ use anyhow::Context;
|
||||
use dcb_bytes::Bytes;
|
||||
use dcb_exe::{
|
||||
inst::{
|
||||
parse::{Line, LineArg},
|
||||
parse::{Line, LineArg, LineArgExpr},
|
||||
Inst, InstSize, Label, LabelName, Parsable, ParseCtx,
|
||||
},
|
||||
Data, Pos,
|
||||
@ -107,7 +107,7 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
if let Some(mut inst) = line.inst {
|
||||
// Modify any local labels
|
||||
for arg in &mut inst.args {
|
||||
if let LineArg::Label(name) | LineArg::LabelOffset { label: name, .. } = arg {
|
||||
if let LineArg::Expr(LineArgExpr::Label { label: name, .. }) = arg {
|
||||
// If the label isn't local, continue
|
||||
if !name.starts_with('.') {
|
||||
continue;
|
||||
|
||||
@ -1,89 +1,89 @@
|
||||
? pos: 0x8001414c
|
||||
arg: 1
|
||||
: "something10@hi"
|
||||
: "something10@addr_hi"
|
||||
? pos: 0x80014154
|
||||
arg: 1
|
||||
: "something10@lo"
|
||||
: "something10@addr_lo"
|
||||
|
||||
? pos: 0x80013f1c
|
||||
arg: 1
|
||||
: "ZeroStart@hi"
|
||||
: "ZeroStart@addr_hi"
|
||||
? pos: 0x80013f20
|
||||
arg: 1
|
||||
: "ZeroStart@lo"
|
||||
: "ZeroStart@addr_lo"
|
||||
? pos: 0x80013f30
|
||||
arg: 1
|
||||
: "ZeroStart@lo"
|
||||
: "ZeroStart@addr_lo"
|
||||
|
||||
? pos: 0x80013f34
|
||||
arg: 1
|
||||
: "data_w686@hi"
|
||||
: "data_w686@addr_hi"
|
||||
? pos: 0x80013f38
|
||||
arg: 2
|
||||
: "data_w686@lo"
|
||||
: "data_w686@addr_lo"
|
||||
? pos: 0x80013f3c
|
||||
arg: 1
|
||||
: "data_w686@lo"
|
||||
: "data_w686@addr_lo"
|
||||
|
||||
? pos: 0x80013f60
|
||||
arg: 1
|
||||
: "main_loop_data1@hi"
|
||||
: "main_loop_data1@addr_hi"
|
||||
? pos: 0x80013f68
|
||||
arg: 1
|
||||
: "main_loop_data1@lo"
|
||||
: "main_loop_data1@addr_lo"
|
||||
|
||||
? pos: 0x80013f6c
|
||||
arg: 1
|
||||
: "ZeroStart@lo"
|
||||
: "ZeroStart@addr_lo"
|
||||
? pos: 0x80013f7c
|
||||
arg: 1
|
||||
: "ZeroStart@lo"
|
||||
: "ZeroStart@addr_lo"
|
||||
|
||||
? pos: 0x80013f78
|
||||
arg: 1
|
||||
: "data_w686@hi"
|
||||
: "data_w686@addr_hi"
|
||||
? pos: 0x80013f84
|
||||
arg: 1
|
||||
: "data_w686@lo"
|
||||
: "data_w686@addr_lo"
|
||||
|
||||
? pos: 0x80013f80
|
||||
arg: 1
|
||||
: "main_loop_data1@hi"
|
||||
: "main_loop_data1@addr_hi"
|
||||
? pos: 0x80013f88
|
||||
arg: 1
|
||||
: "main_loop_data1@lo"
|
||||
: "main_loop_data1@addr_lo"
|
||||
|
||||
? pos: 0x80013fd0
|
||||
arg: 1
|
||||
: "ZeroStart@hi"
|
||||
: "ZeroStart@addr_hi"
|
||||
? pos: 0x80013fd4
|
||||
arg: 1
|
||||
: "ZeroStart@lo"
|
||||
: "ZeroStart@addr_lo"
|
||||
|
||||
? pos: 0x80013ff4
|
||||
arg: 1
|
||||
: "something11_data6@hi"
|
||||
: "something11_data6@addr_hi"
|
||||
? pos: 0x8001400c
|
||||
arg: 1
|
||||
: "something11_data6@lo"
|
||||
: "something11_data6@addr_lo"
|
||||
|
||||
? pos: 0x80013ff8
|
||||
arg: 1
|
||||
: "something11_data1@hi"
|
||||
: "something11_data1@addr_hi"
|
||||
? pos: 0x80014008
|
||||
arg: 1
|
||||
: "something11_data1@lo"
|
||||
: "something11_data1@addr_lo"
|
||||
|
||||
? pos: 0x80013ffc
|
||||
arg: 1
|
||||
: "something11_data3@hi"
|
||||
: "something11_data3@addr_hi"
|
||||
? pos: 0x80014004
|
||||
arg: 1
|
||||
: "something11_data3@lo"
|
||||
: "something11_data3@addr_lo"
|
||||
|
||||
? pos: 0x80014010
|
||||
arg: 1
|
||||
: "something11_data5@hi"
|
||||
: "something11_data5@addr_hi"
|
||||
? pos: 0x8001401c
|
||||
arg: 1
|
||||
: "something11_data5@lo"
|
||||
: "something11_data5@addr_lo"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user