exec
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "asm"
|
||||
version = "0.1.0"
|
||||
authors = ["Ondřej Hruška <ondra@ondrovo.com>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
sexp = "1.1.4"
|
||||
thiserror = "1.0.20"
|
||||
anyhow = "1.0.32"
|
||||
@@ -0,0 +1,114 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub type DebugMsg = Cow<'static, str>;
|
||||
|
||||
/// Immediate value
|
||||
pub type Value = u64;
|
||||
|
||||
pub fn is_positive(val : Value) -> bool {
|
||||
0 == (val & 0x8000_0000_0000_0000)
|
||||
}
|
||||
|
||||
pub fn is_negative(val : Value) -> bool {
|
||||
0 != (val & 0x8000_0000_0000_0000)
|
||||
}
|
||||
|
||||
pub fn as_signed(val : Value) -> i64 {
|
||||
i64::from_ne_bytes(val.to_ne_bytes())
|
||||
}
|
||||
|
||||
/// Immediate address
|
||||
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct Addr(pub u64);
|
||||
|
||||
impl Addr {
|
||||
pub fn advance(&mut self, add: i64) {
|
||||
if add < 0 {
|
||||
self.0 = self.0.wrapping_sub(-add as u64);
|
||||
} else {
|
||||
self.0 = self.0.wrapping_add(add as u64);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Addr {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
if self.0 > 0x7fff_ffff_ffff_ffff {
|
||||
write!(f, "{}", i64::from_ne_bytes(self.0.to_ne_bytes()))
|
||||
} else {
|
||||
write!(f, "{:#010x}", self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Addr {
|
||||
fn from(n: u64) -> Self {
|
||||
Self(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Addr> for u64 {
|
||||
fn from(addr: Addr) -> Self {
|
||||
addr.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Label name
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum Label {
|
||||
Named(String),
|
||||
Numbered(u32),
|
||||
}
|
||||
|
||||
impl Label {
|
||||
/// Generate a unique numbered label from a counter
|
||||
pub fn unique(counter : &AtomicU32) -> Self {
|
||||
Label::Numbered(counter.fetch_add(1, std::sync::atomic::Ordering::Relaxed))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Label {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Label::Named(name) => write!(f, ":{}", name),
|
||||
Label::Numbered(num) => write!(f, ":#{}", num),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Label {
|
||||
fn from(n: &str) -> Self {
|
||||
Self::Named(n.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Label {
|
||||
fn from(n: String) -> Self {
|
||||
Self::Named(n)
|
||||
}
|
||||
}
|
||||
|
||||
/// Routine name
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct RoutineName(pub String);
|
||||
|
||||
impl Display for RoutineName {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for RoutineName {
|
||||
fn from(n: &str) -> Self {
|
||||
Self(n.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for RoutineName {
|
||||
fn from(n: String) -> Self {
|
||||
Self(n)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
//! Mask applied to a data source or destination
|
||||
|
||||
use crate::error::AsmError;
|
||||
|
||||
/// Bit mask to apply to a value
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct Mask {
|
||||
/// Length of the selected bit slice
|
||||
len: u8,
|
||||
/// Offset of the selected bit slice from bit zero
|
||||
offset: u8,
|
||||
}
|
||||
|
||||
impl Default for Mask {
|
||||
fn default() -> Self {
|
||||
Mask {
|
||||
len: 64,
|
||||
offset: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mask {
|
||||
pub const BYTE: Mask = Mask {
|
||||
len: 8,
|
||||
offset: 0,
|
||||
};
|
||||
pub const HALF_WORD: Mask = Mask {
|
||||
len: 16,
|
||||
offset: 0,
|
||||
};
|
||||
pub const WORD: Mask = Mask {
|
||||
len: 32,
|
||||
offset: 0,
|
||||
};
|
||||
pub const DOUBLE_WORD: Mask = Mask {
|
||||
len: 64,
|
||||
offset: 0,
|
||||
};
|
||||
pub const FULL: Mask = Self::DOUBLE_WORD;
|
||||
|
||||
pub fn new(len: u8, offset: u8) -> Result<Self, AsmError> {
|
||||
if len == 0 || offset >= 64 {
|
||||
// create the invalid mask to display it in the error
|
||||
return Err(AsmError::BadMask(Mask {
|
||||
len,
|
||||
offset
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
len: len.min(64 - offset),
|
||||
offset,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a binary mask representing the span
|
||||
pub fn as_bitmask(self) -> u64 {
|
||||
((1 << self.len) - 1) << self.offset
|
||||
}
|
||||
|
||||
pub fn is_default(self) -> bool {
|
||||
self == Self::default()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
use super::error::AsmError;
|
||||
|
||||
|
||||
pub mod literal;
|
||||
pub mod reg;
|
||||
pub mod mask;
|
||||
|
||||
pub use reg::Register;
|
||||
pub use mask::Mask;
|
||||
use literal::Addr;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::data::literal::{Value, is_negative, as_signed};
|
||||
use std::fmt::{Debug, Formatter, Display};
|
||||
use std::fmt;
|
||||
|
||||
/// Data source disposition
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum DataDisp {
|
||||
/// Constant value
|
||||
Immediate(Value),
|
||||
/// Constant memory address
|
||||
ImmediatePtr(Addr),
|
||||
/// Register
|
||||
Register(Register),
|
||||
/// Pointer into memory, stored in a numbered register
|
||||
RegisterPtr(Register),
|
||||
}
|
||||
|
||||
impl Display for DataDisp {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
DataDisp::Immediate(v) => {
|
||||
write!(f, "{}", as_signed(*v))
|
||||
}
|
||||
DataDisp::ImmediatePtr(a) => {
|
||||
write!(f, "@0x{:#18x}", a.0)
|
||||
}
|
||||
DataDisp::Register(r) => {
|
||||
write!(f, "{}", r)
|
||||
}
|
||||
DataDisp::RegisterPtr(r) => {
|
||||
write!(f, "@{}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SrcDisp> for DataDisp {
|
||||
fn from(s: SrcDisp) -> Self {
|
||||
match s {
|
||||
SrcDisp::Immediate(x) => DataDisp::Immediate(x),
|
||||
SrcDisp::ImmediatePtr(x) => DataDisp::ImmediatePtr(x),
|
||||
SrcDisp::Register(x) => DataDisp::Register(x),
|
||||
SrcDisp::RegisterPtr(x) => DataDisp::RegisterPtr(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DstDisp> for DataDisp {
|
||||
fn from(s: DstDisp) -> Self {
|
||||
match s {
|
||||
DstDisp::ImmediatePtr(x) => DataDisp::ImmediatePtr(x),
|
||||
DstDisp::Register(x) => DataDisp::Register(x),
|
||||
DstDisp::RegisterPtr(x) => DataDisp::RegisterPtr(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Data source disposition
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum SrcDisp {
|
||||
/// Constant value
|
||||
Immediate(Value),
|
||||
/// Constant memory address
|
||||
ImmediatePtr(Addr),
|
||||
/// Register
|
||||
Register(Register),
|
||||
/// Pointer into memory, stored in a numbered register
|
||||
RegisterPtr(Register),
|
||||
}
|
||||
|
||||
impl TryFrom<DataDisp> for SrcDisp {
|
||||
type Error = AsmError;
|
||||
|
||||
fn try_from(value: DataDisp) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
DataDisp::Immediate(x) => Ok(SrcDisp::Immediate(x)),
|
||||
DataDisp::ImmediatePtr(x) => Ok(SrcDisp::ImmediatePtr(x)),
|
||||
DataDisp::Register(x) => Ok(SrcDisp::Register(x)),
|
||||
DataDisp::RegisterPtr(x) => Ok(SrcDisp::RegisterPtr(x)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Data destination disposition
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum DstDisp {
|
||||
/// Constant memory address
|
||||
ImmediatePtr(Addr),
|
||||
/// Register
|
||||
Register(Register),
|
||||
/// Pointer into memory, stored in a numbered register
|
||||
RegisterPtr(Register),
|
||||
}
|
||||
|
||||
impl From<DstDisp> for SrcDisp {
|
||||
fn from(s: DstDisp) -> Self {
|
||||
match s {
|
||||
DstDisp::ImmediatePtr(x) => SrcDisp::ImmediatePtr(x),
|
||||
DstDisp::Register(x) => SrcDisp::Register(x),
|
||||
DstDisp::RegisterPtr(x) => SrcDisp::RegisterPtr(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<DataDisp> for DstDisp {
|
||||
type Error = AsmError;
|
||||
|
||||
fn try_from(value: DataDisp) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
DataDisp::Immediate(_x) => Err(AsmError::ValueAsOutput),
|
||||
DataDisp::ImmediatePtr(x) => Ok(DstDisp::ImmediatePtr(x)),
|
||||
DataDisp::Register(x) => Ok(DstDisp::Register(x)),
|
||||
DataDisp::RegisterPtr(x) => Ok(DstDisp::RegisterPtr(x)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Data source argument (read-only)
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub struct Rd(SrcDisp, Mask);
|
||||
|
||||
impl Rd {
|
||||
pub fn new(src: SrcDisp) -> Self {
|
||||
Rd(src, Mask::default())
|
||||
}
|
||||
pub fn d(self) -> SrcDisp {
|
||||
self.0
|
||||
}
|
||||
pub fn mask(self) -> Mask {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
/// Data destination argument (read-write)
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub struct Wr(DstDisp, Mask);
|
||||
|
||||
impl Wr {
|
||||
pub fn new(dst: DstDisp) -> Self {
|
||||
Wr(dst, Mask::default())
|
||||
}
|
||||
pub fn d(self) -> DstDisp {
|
||||
self.0
|
||||
}
|
||||
pub fn mask(self) -> Mask {
|
||||
self.1
|
||||
}
|
||||
pub fn as_rd(&self) -> Rd {
|
||||
Rd(self.0.into(), self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Rd {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Rd(")?;
|
||||
let disp : DataDisp = self.0.into();
|
||||
write!(f, "{}", disp);
|
||||
if !self.mask().is_default() {
|
||||
write!(f, ",{:?}", self.mask());
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Wr {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Wr(")?;
|
||||
let disp : DataDisp = self.0.into();
|
||||
write!(f, "{}", disp);
|
||||
if !self.mask().is_default() {
|
||||
write!(f, ",{:?}", self.mask());
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use crate::error::Error;
|
||||
|
||||
/// Register name
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Register {
|
||||
/// Argument register, read-only
|
||||
Arg(u8),
|
||||
/// Result register, read-only
|
||||
Res(u8),
|
||||
/// General purpose register
|
||||
Gen(u8),
|
||||
}
|
||||
|
||||
impl Display for Register {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Register::Arg(n) => write!(f, "arg{}", n),
|
||||
Register::Res(n) => write!(f, "res{}", n),
|
||||
Register::Gen(n) => write!(f, "r{}", n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_reg(name : &str) -> anyhow::Result<Register> {
|
||||
if let Some(rn) = name.strip_prefix("arg") {
|
||||
let val : u8 = rn.parse()?;
|
||||
Ok(Register::Arg(val))
|
||||
} else if let Some(rn) = name.strip_prefix("res") {
|
||||
let val : u8 = rn.parse()?;
|
||||
Ok(Register::Res(val))
|
||||
} else if let Some(rn) = name.strip_prefix("r") {
|
||||
let val : u8 = rn.parse()?;
|
||||
Ok(Register::Gen(val))
|
||||
} else {
|
||||
Err(Error::Parse(format!("Bad reg name: {}", name).into()))?
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
use crate::instr::{Cond};
|
||||
use crate::data::{Mask, Register};
|
||||
use thiserror::Error;
|
||||
use std::borrow::Cow;
|
||||
use crate::data::literal::Label;
|
||||
|
||||
|
||||
/// csn_asm unified error type
|
||||
#[derive(Error,Debug)]
|
||||
pub enum Error {
|
||||
#[error("S-expression syntax error: {0:?}")]
|
||||
PreParse(#[from] Box<sexp::Error>),
|
||||
#[error("Parse error: {0:?}")]
|
||||
Parse(Cow<'static, str>),
|
||||
#[error("Parse error in {1:?}: {0:?}")]
|
||||
ParseIn(Cow<'static, str>, sexp::Sexp),
|
||||
#[error("Assembler error: {0:?}")]
|
||||
Asm(AsmError),
|
||||
#[error("Architecture error: {0:?}")]
|
||||
Arch(ArchError),
|
||||
#[error(transparent)]
|
||||
Other(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
/// Error from the assembler stage (after parsing S-expressions and basic validation)
|
||||
#[derive(Error,Debug)]
|
||||
pub enum AsmError {
|
||||
#[error("Unknown instruction")]
|
||||
UnknownInstruction,
|
||||
#[error("Bad bit mask")]
|
||||
BadMask(Mask),
|
||||
#[error("Uneven operand size")]
|
||||
UnevenOperandSize(Mask, Mask),
|
||||
#[error("Value provided as output argument")]
|
||||
ValueAsOutput,
|
||||
#[error("Conditional branch already defined for \"{0}\"")]
|
||||
ConditionalAlreadyUsed(Cond),
|
||||
#[error("Label \"{0:?}\" not defined")]
|
||||
LabelNotDefined(Label),
|
||||
#[error("Bad register type: {0}")]
|
||||
BadRegisterType(Register),
|
||||
}
|
||||
|
||||
/// Architectural error - the code is syntactically OK, but cannot run
|
||||
#[derive(Error,Debug)]
|
||||
pub enum ArchError {
|
||||
#[error("Register {0} does not exist")]
|
||||
RegisterNotExist(Register),
|
||||
#[error("Register {0} is not writable")]
|
||||
RegisterNotWritable(Register),
|
||||
#[error("Register {0} is not readable")]
|
||||
RegisterNotReadable(Register),
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::ops::Not;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum Cond {
|
||||
Equal,
|
||||
NotEqual,
|
||||
Zero,
|
||||
NotZero,
|
||||
Lower,
|
||||
LowerOrEqual,
|
||||
Greater,
|
||||
GreaterOrEqual,
|
||||
Positive,
|
||||
NonPositive,
|
||||
Negative,
|
||||
NonNegative,
|
||||
Overflow,
|
||||
NotOverflow,
|
||||
Carry,
|
||||
NotCarry,
|
||||
}
|
||||
|
||||
impl Display for Cond {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match self {
|
||||
Cond::Equal => "eq",
|
||||
Cond::NotEqual => "ne",
|
||||
Cond::Zero => "z",
|
||||
Cond::NotZero => "nz",
|
||||
Cond::Lower => "lt",
|
||||
Cond::LowerOrEqual => "le",
|
||||
Cond::Greater => "gt",
|
||||
Cond::GreaterOrEqual => "ge",
|
||||
Cond::Positive => "pos",
|
||||
Cond::Negative => "neg",
|
||||
Cond::NonPositive => "npos",
|
||||
Cond::NonNegative => "nneg",
|
||||
Cond::Overflow => "ov",
|
||||
Cond::Carry => "c",
|
||||
Cond::NotCarry => "nc",
|
||||
Cond::NotOverflow => "nov"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for Cond {
|
||||
type Output = Cond;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
match self {
|
||||
Cond::Equal => Cond::NotEqual,
|
||||
Cond::Zero => Cond::NotZero,
|
||||
Cond::Overflow => Cond::NotOverflow,
|
||||
Cond::Carry => Cond::NotCarry,
|
||||
|
||||
Cond::Positive => Cond::NonPositive,
|
||||
Cond::Negative => Cond::NonNegative,
|
||||
Cond::NonPositive => Cond::Positive,
|
||||
Cond::NonNegative => Cond::Negative,
|
||||
|
||||
Cond::NotEqual => Cond::Equal,
|
||||
Cond::NotZero => Cond::Zero,
|
||||
Cond::NotOverflow => Cond::Overflow,
|
||||
Cond::NotCarry => Cond::Carry,
|
||||
|
||||
Cond::Lower => Cond::GreaterOrEqual,
|
||||
Cond::Greater => Cond::LowerOrEqual,
|
||||
Cond::LowerOrEqual => Cond::Greater,
|
||||
Cond::GreaterOrEqual => Cond::Lower,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use crate::instr::{HLOp, Instr, Cond, Routine, Op};
|
||||
use crate::error::{Error, AsmError};
|
||||
use std::collections::HashMap;
|
||||
use crate::data::literal::{Label, Value};
|
||||
use crate::data::{Rd, SrcDisp};
|
||||
|
||||
/// A trait for something that can turn into multiple instructions
|
||||
pub trait Flatten {
|
||||
fn flatten(self, label_num: &AtomicU32) -> Result<Vec<HLOp>, Error>;
|
||||
}
|
||||
|
||||
impl Flatten for Instr {
|
||||
fn flatten(self, label_num: &AtomicU32) -> Result<Vec<HLOp>, Error> {
|
||||
let mut ops = vec![self.op];
|
||||
|
||||
if let Some(branches) = self.branches {
|
||||
let labels = HashMap::<Cond, u32>::new();
|
||||
let branch_count = branches.len();
|
||||
let end_lbl = Label::unique(label_num);
|
||||
for (cnt, (cond, branch)) in branches.into_iter().enumerate() {
|
||||
if labels.contains_key(&cond) {
|
||||
return Err(Error::Asm(AsmError::ConditionalAlreadyUsed(cond)));
|
||||
}
|
||||
|
||||
let next_lbl = if cnt == branch_count - 1 {
|
||||
end_lbl.clone()
|
||||
} else {
|
||||
Label::unique(label_num)
|
||||
};
|
||||
ops.push(HLOp::JumpIf(!cond, next_lbl.clone()));
|
||||
|
||||
for branch_instr in branch {
|
||||
ops.extend(branch_instr.flatten(label_num)?);
|
||||
}
|
||||
|
||||
if cnt != branch_count - 1 {
|
||||
ops.push(HLOp::Jump(end_lbl.clone()));
|
||||
ops.push(HLOp::Label(next_lbl));
|
||||
}
|
||||
}
|
||||
ops.push(HLOp::Label(end_lbl));
|
||||
}
|
||||
|
||||
Ok(ops)
|
||||
}
|
||||
}
|
||||
|
||||
impl Flatten for Routine {
|
||||
fn flatten(self, label_num: &AtomicU32) -> Result<Vec<HLOp>, Error> {
|
||||
let mut ops = vec![
|
||||
Op::Routine(self.name.clone()).into(),
|
||||
];
|
||||
|
||||
for instr in self.body {
|
||||
ops.extend(instr.flatten(label_num)?);
|
||||
}
|
||||
|
||||
ops.push(Op::Barrier(Some(format!("Routine \"{}\" overrun", self.name).into())).into());
|
||||
Ok(ops)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert jumps to relative skips
|
||||
pub fn lower(ops: Vec<HLOp>) -> Result<Vec<Op>, Error> {
|
||||
let mut label_positions = HashMap::<Label, usize>::new();
|
||||
for (n, op) in ops.iter().enumerate() {
|
||||
if let HLOp::Label(name) = op {
|
||||
label_positions.insert(name.clone(), n - label_positions.len());
|
||||
}
|
||||
}
|
||||
|
||||
let mut cleaned = vec![];
|
||||
let mut skipped = 0;
|
||||
for (n, op) in ops.into_iter().enumerate() {
|
||||
match op {
|
||||
HLOp::Label(_) => {
|
||||
skipped += 1;
|
||||
}
|
||||
HLOp::Jump(target) => {
|
||||
if let Some(dest) = label_positions.get(&target) {
|
||||
let skip = *dest as isize - n as isize + skipped;
|
||||
cleaned.push(Op::Skip(Rd::new(SrcDisp::Immediate(skip as Value))));
|
||||
} else {
|
||||
return Err(Error::Asm(AsmError::LabelNotDefined(target)));
|
||||
}
|
||||
}
|
||||
HLOp::JumpIf(cond, target) => {
|
||||
if let Some(dest) = label_positions.get(&target) {
|
||||
let skip = *dest as isize - n as isize + skipped;
|
||||
cleaned.push(Op::SkipIf(cond, Rd::new(SrcDisp::Immediate(skip as Value))));
|
||||
} else {
|
||||
return Err(Error::Asm(AsmError::LabelNotDefined(target)));
|
||||
}
|
||||
}
|
||||
HLOp::L(op) => {
|
||||
cleaned.push(op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(cleaned)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
mod op;
|
||||
mod cond;
|
||||
mod flatten;
|
||||
|
||||
pub use flatten::Flatten;
|
||||
pub use flatten::lower;
|
||||
|
||||
pub use op::HLOp;
|
||||
pub use op::Op;
|
||||
pub use cond::Cond;
|
||||
use crate::data::literal::{RoutineName};
|
||||
|
||||
|
||||
|
||||
|
||||
/// A higher-level instruction
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Instr {
|
||||
pub op: HLOp,
|
||||
pub branches: Option<Vec<(Cond, Vec<Instr>)>>,
|
||||
}
|
||||
|
||||
/// A routine
|
||||
pub struct Routine {
|
||||
pub name: RoutineName,
|
||||
pub body: Vec<Instr>,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
use crate::data::{
|
||||
Wr, Rd,
|
||||
literal::Label,
|
||||
literal::RoutineName,
|
||||
literal::DebugMsg,
|
||||
};
|
||||
use crate::instr::{Cond};
|
||||
|
||||
/// A higher level simple opration
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum HLOp {
|
||||
/// Mark a jump target.
|
||||
Label(Label),
|
||||
/// Jump to a label
|
||||
Jump(Label),
|
||||
/// Jump to a label if a flag is set
|
||||
JumpIf(Cond, Label),
|
||||
/// Low level op
|
||||
L(Op),
|
||||
}
|
||||
|
||||
/// A low level instruction
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Op {
|
||||
/// Do nothing
|
||||
Nop,
|
||||
/// Mark a far jump target (can be jumped to from another routine).
|
||||
/// This label is preserved in optimized code.
|
||||
FarLabel(Label),
|
||||
/// Jump to a label that can be in another function
|
||||
FarJump(Label),
|
||||
/// Call a routine with arguments.
|
||||
/// The arguments are passed as argX. Return values are stored in resX registers.
|
||||
Call(RoutineName, Vec<Rd>),
|
||||
/// Exit the current routine with return values
|
||||
Ret(Vec<Rd>),
|
||||
/// Mark a routine entry point (call target).
|
||||
/// Kept in the low level instruction file for position-independent code
|
||||
Routine(RoutineName),
|
||||
/// Skip backward or forward
|
||||
Skip(Rd),
|
||||
/// Skip if a flag is set
|
||||
SkipIf(Cond, Rd),
|
||||
/// Deny jumps, skips and run across this address, producing a run-time fault with a message.
|
||||
Barrier(Option<DebugMsg>),
|
||||
/// Generate a run-time fault with a debugger message
|
||||
Fault(Option<DebugMsg>),
|
||||
|
||||
/* Arithmetic */
|
||||
|
||||
/// Copy a value
|
||||
Mov(Wr, Rd),
|
||||
/// Compare two values and set conditional flags.
|
||||
/// If the values are identical, evaluate if they are zero, positive, or negative (the "tst" op)
|
||||
Cmp(Rd, Rd),
|
||||
// Increment a value
|
||||
Inc(Wr),
|
||||
// Decrement a value
|
||||
Dec(Wr),
|
||||
|
||||
// TODO arithmetics, bit manipulation, byte operations
|
||||
}
|
||||
|
||||
/// Make "into" work
|
||||
impl From<Op> for HLOp {
|
||||
fn from(op: Op) -> Self {
|
||||
HLOp::L(op)
|
||||
}
|
||||
}
|
||||
+335
@@ -0,0 +1,335 @@
|
||||
pub mod data;
|
||||
pub mod error;
|
||||
pub mod instr;
|
||||
pub mod parse;
|
||||
pub mod patches;
|
||||
|
||||
use crate::instr::{Op, lower};
|
||||
|
||||
/// Parse a program from string and assemble a low level instruction sequence from it.
|
||||
pub fn assemble(source : &str) -> Result<Vec<Op>, error::Error> {
|
||||
let parsed = parse::parse(source)?;
|
||||
Ok(lower(parsed)?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::parse::{parse, parse_instructions};
|
||||
use crate::instr::{HLOp, Op, Flatten, Instr, lower};
|
||||
use crate::data::{Wr, DstDisp, Register, SrcDisp, Rd};
|
||||
use crate::data::literal::{Addr, Label};
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use crate::instr::Cond;
|
||||
|
||||
#[test]
|
||||
fn test_parse_empty() {
|
||||
let parsed = parse("
|
||||
()
|
||||
").unwrap();
|
||||
assert_eq!(Vec::<HLOp>::new(), parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_empty_routine() {
|
||||
let parsed = parse("
|
||||
(
|
||||
(hello)
|
||||
)
|
||||
").unwrap();
|
||||
assert_eq!(vec![
|
||||
HLOp::L(Op::Routine("hello".into())),
|
||||
HLOp::L(Op::Barrier(Some("Routine \"hello\" overrun".into())))
|
||||
], parsed);
|
||||
|
||||
let parsed = parse("
|
||||
(
|
||||
(hello)
|
||||
(world)
|
||||
)
|
||||
").unwrap();
|
||||
assert_eq!(vec![
|
||||
HLOp::L(Op::Routine("hello".into())),
|
||||
HLOp::L(Op::Barrier(Some("Routine \"hello\" overrun".into()))),
|
||||
HLOp::L(Op::Routine("world".into())),
|
||||
HLOp::L(Op::Barrier(Some("Routine \"world\" overrun".into())))
|
||||
], parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_data_formats() {
|
||||
let parsed = parse("
|
||||
(
|
||||
(move
|
||||
(mov r0 r1)
|
||||
(mov r15 7)
|
||||
(mov r15 0xabcd)
|
||||
(mov r7 0b11110000)
|
||||
(mov r7 arg1)
|
||||
(mov r255 arg255)
|
||||
(mov r7 res0)
|
||||
(mov r7 res255)
|
||||
(mov @r0 @r0) ; test in both Rd and Wr positions
|
||||
(mov @r0 @arg0)
|
||||
(mov @r0 @res0)
|
||||
(mov @123456 @0x123456)
|
||||
(mov @0b010101 @0b010101)
|
||||
)
|
||||
)
|
||||
").unwrap();
|
||||
assert_eq!(vec![
|
||||
HLOp::L(Op::Routine("move".into())),
|
||||
// (mov r0 r1)
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(1))),
|
||||
)),
|
||||
// (mov r15 7)
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(15))),
|
||||
Rd::new(SrcDisp::Immediate(7)),
|
||||
)),
|
||||
// (mov r15 0xabcd)
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(15))),
|
||||
Rd::new(SrcDisp::Immediate(0xabcd)),
|
||||
)),
|
||||
// (mov r7 0b11110000)
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(7))),
|
||||
Rd::new(SrcDisp::Immediate(0b11110000)),
|
||||
)),
|
||||
// (mov r7 arg1)
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(7))),
|
||||
Rd::new(SrcDisp::Register(Register::Arg(1))),
|
||||
)),
|
||||
// (mov r255 arg255)
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(255))),
|
||||
Rd::new(SrcDisp::Register(Register::Arg(255))),
|
||||
)),
|
||||
// (mov r7 res0)
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(7))),
|
||||
Rd::new(SrcDisp::Register(Register::Res(0))),
|
||||
)),
|
||||
// (mov r7 res255)
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(7))),
|
||||
Rd::new(SrcDisp::Register(Register::Res(255))),
|
||||
)),
|
||||
// (mov @r0 @r0)
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::RegisterPtr(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::RegisterPtr(Register::Gen(0))),
|
||||
)),
|
||||
// (mov @r0 @arg0)
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::RegisterPtr(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::RegisterPtr(Register::Arg(0))),
|
||||
)),
|
||||
// (mov @r0 @res0)
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::RegisterPtr(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::RegisterPtr(Register::Res(0))),
|
||||
)),
|
||||
// (mov @123456 @0x123456)
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::ImmediatePtr(Addr(123456))),
|
||||
Rd::new(SrcDisp::ImmediatePtr(Addr(0x123456))),
|
||||
)),
|
||||
// (mov @0b010101 @0b010101)
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::ImmediatePtr(Addr(0b010101))),
|
||||
Rd::new(SrcDisp::ImmediatePtr(Addr(0b010101))),
|
||||
)),
|
||||
HLOp::L(Op::Barrier(Some("Routine \"move\" overrun".into()))),
|
||||
], parsed);
|
||||
}
|
||||
|
||||
fn parse_single_instr(src : &str) -> anyhow::Result<Instr> {
|
||||
Ok(parse_instructions(vec![sexp::parse(src)?])?.remove(0))
|
||||
}
|
||||
|
||||
fn parse_single_op(src : &str) -> anyhow::Result<Vec<HLOp>> {
|
||||
let num = AtomicU32::new(0);
|
||||
Ok(parse_single_instr(src)?.flatten(&num)?)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_single() {
|
||||
let parsed = parse_single_op("(mov r0 r1)").unwrap();
|
||||
assert_eq!(vec![
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(1))),
|
||||
)),
|
||||
], parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_branches_instr() {
|
||||
let parsed = parse_single_instr("
|
||||
(cmp r0 r1 (eq? (mov r0 r0) (mov r1 r2)) (>? (mov r0 r0) (mov r1 r1)))
|
||||
").unwrap();
|
||||
assert_eq!(
|
||||
Instr {
|
||||
op: HLOp::L(Op::Cmp(
|
||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(1))),
|
||||
)),
|
||||
branches: Some(vec![
|
||||
(
|
||||
Cond::Equal,
|
||||
vec![
|
||||
Instr {
|
||||
op: HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||
)),
|
||||
branches: None
|
||||
},
|
||||
Instr {
|
||||
op: HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(1))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(2))),
|
||||
)),
|
||||
branches: None
|
||||
}
|
||||
]
|
||||
),
|
||||
(
|
||||
Cond::Greater,
|
||||
vec![
|
||||
Instr {
|
||||
op: HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||
)),
|
||||
branches: None
|
||||
},
|
||||
Instr {
|
||||
op: HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(1))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(1))),
|
||||
)),
|
||||
branches: None
|
||||
}
|
||||
]
|
||||
)
|
||||
])
|
||||
}
|
||||
, parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_branches_op() {
|
||||
let parsed = parse_single_op("
|
||||
(cmp r0 r1
|
||||
(eq?
|
||||
(mov r0 r0)
|
||||
(mov r1 r2))
|
||||
(>?
|
||||
(mov r0 r0)
|
||||
(mov r1 r1)))
|
||||
").unwrap();
|
||||
assert_eq!(
|
||||
vec![
|
||||
HLOp::L(Op::Cmp(
|
||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(1))),
|
||||
)),
|
||||
HLOp::JumpIf(Cond::NotEqual, Label::Numbered(1)),
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||
)),
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(1))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(2))),
|
||||
)),
|
||||
HLOp::Jump(Label::Numbered(0)),
|
||||
HLOp::Label(Label::Numbered(1)),
|
||||
HLOp::JumpIf(Cond::LowerOrEqual, Label::Numbered(0)),
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||
)),
|
||||
HLOp::L(Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(1))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(1))),
|
||||
)),
|
||||
HLOp::Label(Label::Numbered(0)),
|
||||
], parsed);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_jumps_to_skips() {
|
||||
let parsed = parse("(
|
||||
(foo
|
||||
(:foo)
|
||||
(:unused)
|
||||
(:whatever)
|
||||
(mov r0 r0)
|
||||
(j :foo)
|
||||
(j :foo)
|
||||
(mov r0 r0)
|
||||
(mov r0 r0)
|
||||
(j :whatever)
|
||||
(j.if eq :whatever)
|
||||
)
|
||||
)").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
vec![
|
||||
Op::Routine("foo".into()).into(),
|
||||
HLOp::Label(Label::Named("foo".to_string())),
|
||||
HLOp::Label(Label::Named("unused".to_string())),
|
||||
HLOp::Label(Label::Named("whatever".to_string())),
|
||||
Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||
).into(),
|
||||
HLOp::Jump(Label::Named("foo".to_string())),
|
||||
HLOp::Jump(Label::Named("foo".to_string())),
|
||||
Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||
).into(),
|
||||
Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||
).into(),
|
||||
HLOp::Jump(Label::Named("whatever".to_string())),
|
||||
HLOp::JumpIf(Cond::Equal, Label::Named("whatever".to_string())),
|
||||
Op::Barrier(Some("Routine \"foo\" overrun".into())).into(),
|
||||
], parsed);
|
||||
|
||||
// Labels are removed and jumps become skips
|
||||
|
||||
let cleaned = lower(parsed).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
vec![
|
||||
Op::Routine("foo".into()),
|
||||
Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||
),
|
||||
Op::Skip(Rd::new(SrcDisp::Immediate(-1))),
|
||||
Op::Skip(Rd::new(SrcDisp::Immediate(-2))),
|
||||
Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||
),
|
||||
Op::Mov(
|
||||
Wr::new(DstDisp::Register(Register::Gen(0))),
|
||||
Rd::new(SrcDisp::Register(Register::Gen(0))),
|
||||
),
|
||||
Op::Skip(Rd::new(SrcDisp::Immediate(-5))),
|
||||
Op::SkipIf(Cond::Equal, Rd::new(SrcDisp::Immediate(-6))),
|
||||
Op::Barrier(Some("Routine \"foo\" overrun".into())),
|
||||
], cleaned);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
use crate::instr::{Routine, HLOp, Flatten};
|
||||
use crate::error::Error;
|
||||
|
||||
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use crate::parse::sexp_expect::expect_list;
|
||||
|
||||
mod parse_cond;
|
||||
mod parse_instr;
|
||||
mod parse_data;
|
||||
mod parse_routines;
|
||||
mod sexp_expect;
|
||||
mod parse_op;
|
||||
|
||||
use parse_routines::parse_routines;
|
||||
pub use parse_instr::parse_instructions;
|
||||
|
||||
pub fn parse(source: &str) -> Result<Vec<HLOp>, Error> {
|
||||
let root = sexp::parse(source)?;
|
||||
|
||||
let subs: Vec<Routine> = parse_routines(expect_list(Some(root), true)?)?;
|
||||
|
||||
let mut combined = vec![];
|
||||
let label_num = AtomicU32::new(0);
|
||||
for sub in subs {
|
||||
combined.extend(sub.flatten(&label_num)?);
|
||||
}
|
||||
|
||||
Ok(combined)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
use crate::parse::sexp_expect::{expect_list, expect_string_atom};
|
||||
use sexp::Sexp;
|
||||
use crate::instr::{Cond, Instr};
|
||||
use crate::error::Error;
|
||||
use crate::parse::parse_instr::parse_instructions;
|
||||
use crate::patches::TryRemove;
|
||||
|
||||
pub fn parse_cond_branch(tok: Sexp) -> Result<(Cond, Vec<Instr>), Error> {
|
||||
let mut list = expect_list(Some(tok), false)?;
|
||||
let kw = expect_string_atom(list.try_remove(0))?;
|
||||
|
||||
if !kw.ends_with('?') {
|
||||
return Err(Error::Parse(format!("Condition must end with '?': {}", kw).into()));
|
||||
}
|
||||
|
||||
Ok((parse_cond(&kw)?, parse_instructions(list)?))
|
||||
}
|
||||
|
||||
pub fn parse_cond(text: &str) -> Result<Cond, Error> {
|
||||
Ok(match text.trim_end_matches('?') {
|
||||
"eq" | "=" | "==" => Cond::Equal,
|
||||
"ne" | "<>" | "!=" | "≠" => Cond::NotEqual,
|
||||
"z" | "0" => Cond::Zero,
|
||||
"nz" | "<>0" | "!0" => Cond::NotZero,
|
||||
"lt" | "<" => Cond::Lower,
|
||||
"le" | "<=" | "≤" => Cond::LowerOrEqual,
|
||||
"gt" | ">" => Cond::Greater,
|
||||
"ge" | ">=" | "≥" => Cond::GreaterOrEqual,
|
||||
"pos" | "+" | ">0" => Cond::Positive,
|
||||
"neg" | "-" | "<0" => Cond::Negative,
|
||||
"npos" | "!+" | "0-" | "<=0" | "≥0" => Cond::NonPositive,
|
||||
"nneg" | "!-" | "0+" | ">=0" | "≤0" => Cond::NonNegative,
|
||||
"ov" | "^" => Cond::Overflow,
|
||||
"c" => Cond::Carry,
|
||||
"nc" | "!c" => Cond::NotCarry,
|
||||
"nov" | "!ov" | "!^" => Cond::NotOverflow,
|
||||
_ => {
|
||||
return Err(Error::Parse(format!("Unknown cond: {}", text).into()));
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
use sexp::{Sexp, Atom};
|
||||
use crate::data::{DataDisp, Rd, Wr, DstDisp, SrcDisp, reg};
|
||||
use crate::error::Error;
|
||||
use crate::data::literal::{Value, Addr, Label};
|
||||
use std::convert::TryFrom;
|
||||
use crate::parse::sexp_expect::expect_string_atom;
|
||||
|
||||
/// Parse a label
|
||||
pub fn parse_label(name : Option<Sexp>) -> Result<Label, Error> {
|
||||
let name = expect_string_atom(name)?;
|
||||
Ok(Label::Named(name.trim_start_matches(':').into()))
|
||||
}
|
||||
|
||||
/// Parse data disposition (address/value, without the read/write restriction)
|
||||
pub fn parse_data_disp(tok: Option<Sexp>) -> Result<DataDisp, Error> {
|
||||
let tok = if let Some(tok) = tok {
|
||||
tok
|
||||
} else {
|
||||
return Err(Error::Parse("Expected data disposition token".into()));
|
||||
};
|
||||
|
||||
// TODO implement masks
|
||||
|
||||
match &tok {
|
||||
Sexp::Atom(Atom::I(val)) => {
|
||||
Ok(DataDisp::Immediate(unsafe { std::mem::transmute(*val) }))
|
||||
},
|
||||
Sexp::Atom(Atom::S(s)) => {
|
||||
if let Some(reference) = s.strip_prefix('@') {
|
||||
if reference.starts_with(|c : char| c.is_ascii_digit()) {
|
||||
let val : u64 = parse_u64(reference)?;
|
||||
Ok(DataDisp::ImmediatePtr(Addr(val)))
|
||||
} else {
|
||||
Ok(DataDisp::RegisterPtr(reg::parse_reg(reference)?))
|
||||
}
|
||||
} else if s.starts_with(|c : char| c.is_ascii_digit()) {
|
||||
Ok(DataDisp::Immediate(unsafe { std::mem::transmute(parse_i64(s)?) }))
|
||||
} else {
|
||||
Ok(DataDisp::Register(reg::parse_reg(s)?))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
Err(Error::Parse(format!("bad data disp: {:?}", tok).into()))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_u64(literal : &str) -> anyhow::Result<u64> {
|
||||
if let Some(hex) = literal.strip_prefix("0x") {
|
||||
Ok(u64::from_str_radix(hex, 16)?)
|
||||
} else if let Some(hex) = literal.strip_prefix("0b") {
|
||||
Ok(u64::from_str_radix(hex, 2)?)
|
||||
} else {
|
||||
Ok(u64::from_str_radix(literal, 10)?)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_i64(literal : &str) -> anyhow::Result<i64> {
|
||||
if let Some(_value) = literal.strip_prefix("-") {
|
||||
Ok(-1 * i64::try_from(parse_u64(literal)?)?)
|
||||
} else {
|
||||
Ok(i64::try_from(parse_u64(literal)?)?)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_rd(tok: Option<Sexp>) -> anyhow::Result<Rd> {
|
||||
Ok(Rd::new(SrcDisp::try_from(parse_data_disp(tok)?)?))
|
||||
}
|
||||
|
||||
pub fn parse_wr(tok: Option<Sexp>) -> anyhow::Result<Wr> {
|
||||
Ok(Wr::new(DstDisp::try_from(parse_data_disp(tok)?)?))
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
use sexp::Sexp;
|
||||
use crate::instr::{Instr};
|
||||
use crate::error::Error;
|
||||
use crate::parse::parse_cond::{parse_cond_branch};
|
||||
|
||||
|
||||
use crate::parse::sexp_expect::{expect_list, expect_string_atom};
|
||||
use crate::patches::SexpIsA;
|
||||
use super::parse_op::parse_op;
|
||||
|
||||
pub fn parse_instructions(instrs: Vec<Sexp>) -> Result<Vec<Instr>, Error> {
|
||||
let mut parsed = vec![];
|
||||
for expr in instrs {
|
||||
let tokens = expect_list(Some(expr), false)?;
|
||||
|
||||
let mut toki = tokens.into_iter();
|
||||
|
||||
let mut name = expect_string_atom(toki.next())?;
|
||||
|
||||
let far = if name == "far" {
|
||||
name = expect_string_atom(toki.next())?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let arg_tokens = toki.clone().take_while(|e| e.is_atom());
|
||||
let branch_tokens = toki
|
||||
.skip_while(|e| e.is_atom())
|
||||
.take_while(|e| e.is_list());
|
||||
|
||||
let branches = {
|
||||
let mut branches = vec![];
|
||||
for t in branch_tokens {
|
||||
branches.push(parse_cond_branch(t)?);
|
||||
}
|
||||
if branches.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(branches)
|
||||
}
|
||||
};
|
||||
|
||||
parsed.push(Instr {
|
||||
op: parse_op(name.as_str(), far, arg_tokens)?,
|
||||
branches
|
||||
});
|
||||
}
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
use crate::instr::{HLOp, Op};
|
||||
use sexp::Sexp;
|
||||
use crate::error::Error;
|
||||
use crate::data::literal::{RoutineName, Label};
|
||||
use crate::parse::sexp_expect::expect_string_atom;
|
||||
use crate::parse::parse_data::{parse_rd, parse_wr, parse_label};
|
||||
use crate::parse::parse_cond::parse_cond;
|
||||
|
||||
pub fn parse_op(keyword: &str, far : bool, mut arg_tokens: impl Iterator<Item=Sexp>) -> Result<HLOp, Error> {
|
||||
Ok(match keyword {
|
||||
"j" => {
|
||||
let dest = parse_label(arg_tokens.next())?;
|
||||
if far {
|
||||
HLOp::L(Op::FarJump(dest))
|
||||
} else {
|
||||
HLOp::Jump(dest)
|
||||
}
|
||||
}
|
||||
|
||||
"call" => {
|
||||
let dest = RoutineName(expect_string_atom(arg_tokens.next())?);
|
||||
|
||||
let mut args = vec![];
|
||||
for t in arg_tokens {
|
||||
args.push(parse_rd(Some(t))?);
|
||||
}
|
||||
HLOp::L(Op::Call(dest, args))
|
||||
}
|
||||
|
||||
"ret" => {
|
||||
let mut args = vec![];
|
||||
for t in arg_tokens {
|
||||
args.push(parse_rd(Some(t))?);
|
||||
}
|
||||
HLOp::L(Op::Ret(args))
|
||||
}
|
||||
|
||||
"routine" => {
|
||||
let dest = RoutineName(expect_string_atom(arg_tokens.next())?);
|
||||
HLOp::L(Op::Routine(dest))
|
||||
}
|
||||
|
||||
"s" => {
|
||||
HLOp::L(Op::Skip(parse_rd(arg_tokens.next())?))
|
||||
}
|
||||
|
||||
"sif" => {
|
||||
let cond = parse_cond(&expect_string_atom(arg_tokens.next())?)?;
|
||||
let offs = parse_rd(arg_tokens.next())?;
|
||||
HLOp::L(Op::SkipIf(cond, offs))
|
||||
}
|
||||
|
||||
"jif" => {
|
||||
let cond = parse_cond(&expect_string_atom(arg_tokens.next())?)?;
|
||||
let dest = parse_label(arg_tokens.next())?;
|
||||
HLOp::JumpIf(cond, dest)
|
||||
}
|
||||
|
||||
"barrier" => {
|
||||
HLOp::L(Op::Barrier(match arg_tokens.next() {
|
||||
None => None,
|
||||
Some(s) => Some(expect_string_atom(Some(s))?.into()),
|
||||
}))
|
||||
}
|
||||
|
||||
"fault" => {
|
||||
HLOp::L(Op::Fault(match arg_tokens.next() {
|
||||
None => None,
|
||||
Some(s) => Some(expect_string_atom(Some(s))?.into()),
|
||||
}))
|
||||
}
|
||||
|
||||
"ld" => {
|
||||
HLOp::L(Op::Mov(
|
||||
parse_wr(arg_tokens.next())?,
|
||||
parse_rd(arg_tokens.next())?
|
||||
))
|
||||
}
|
||||
|
||||
"cmp" => {
|
||||
HLOp::L(Op::Cmp(
|
||||
parse_rd(arg_tokens.next())?,
|
||||
parse_rd(arg_tokens.next())?
|
||||
))
|
||||
}
|
||||
|
||||
"tst" => {
|
||||
let arg = parse_rd(arg_tokens.next())?;
|
||||
HLOp::L(Op::Cmp(arg, arg))
|
||||
}
|
||||
|
||||
"inc" => {
|
||||
HLOp::L(Op::Inc(
|
||||
parse_wr(arg_tokens.next())?
|
||||
))
|
||||
}
|
||||
|
||||
"dec" => {
|
||||
HLOp::L(Op::Dec(
|
||||
parse_wr(arg_tokens.next())?
|
||||
))
|
||||
}
|
||||
|
||||
// TODO more instructions
|
||||
|
||||
other => {
|
||||
if let Some(label) = other.strip_prefix(':') {
|
||||
let label = Label::Named(label.to_string());
|
||||
if far {
|
||||
HLOp::L(Op::FarLabel(label))
|
||||
} else {
|
||||
HLOp::Label(label)
|
||||
}
|
||||
} else {
|
||||
return Err(Error::Parse(format!("Unknown instruction: {}", other).into()));
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use crate::parse::parse_instr::parse_instructions;
|
||||
use crate::instr::Routine;
|
||||
use crate::data::literal::RoutineName;
|
||||
use sexp::Sexp;
|
||||
use crate::error::Error;
|
||||
use crate::parse::sexp_expect::{expect_list, expect_string_atom};
|
||||
use crate::patches::TryRemove;
|
||||
|
||||
pub fn parse_routines(routines: Vec<Sexp>) -> Result<Vec<Routine>, Error> {
|
||||
let mut parsed = vec![];
|
||||
for rt in routines {
|
||||
let mut def = expect_list(Some(rt), false)?;
|
||||
let name = expect_string_atom(def.try_remove(0))?;
|
||||
let body = parse_instructions(def)?;
|
||||
parsed.push(Routine {
|
||||
name: RoutineName(name),
|
||||
body,
|
||||
})
|
||||
}
|
||||
Ok(parsed)
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
use sexp::{Sexp, Atom};
|
||||
use crate::error::Error;
|
||||
|
||||
pub fn expect_list(expr: Option<Sexp>, allow_empty: bool) -> Result<Vec<Sexp>, Error> {
|
||||
if let Some(expr) = expr {
|
||||
match &expr {
|
||||
Sexp::Atom(_) => {
|
||||
return Err(Error::ParseIn("Expected a list".into(), expr));
|
||||
}
|
||||
Sexp::List(list) => {
|
||||
if !allow_empty && list.is_empty() {
|
||||
return Err(Error::ParseIn("Routine: Empty list".into(), expr));
|
||||
}
|
||||
|
||||
if let Sexp::List(list) = expr {
|
||||
return Ok(list);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::Parse("Expected a list, got nothing".into()))
|
||||
}
|
||||
|
||||
pub fn expect_atom(expr: Option<Sexp>) -> Result<Atom, Error> {
|
||||
if let Some(expr) = expr {
|
||||
match &expr {
|
||||
Sexp::Atom(_atom) => {
|
||||
if let Sexp::Atom(a) = expr {
|
||||
return Ok(a);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
Sexp::List(_) => {
|
||||
return Err(Error::ParseIn("Expected atom got list".into(), expr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::Parse("Expected atom, got nothing".into()))
|
||||
}
|
||||
|
||||
pub fn expect_string_atom(expr: Option<Sexp>) -> Result<String, Error> {
|
||||
match expect_atom(expr) {
|
||||
Ok(Atom::S(s)) => Ok(s),
|
||||
Ok(atom) => Err(Error::ParseIn("Expected string atom".into(), Sexp::Atom(atom))),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn expect_int_atom(expr: Option<Sexp>) -> Result<i64, Error> {
|
||||
// match expect_atom(expr) {
|
||||
// Ok(Atom::I(v)) => Ok(v),
|
||||
// Ok(atom) => Err(Error::ParseIn("Expected int atom".into(), Sexp::Atom(atom))),
|
||||
// Err(e) => Err(e),
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,5 @@
|
||||
mod try_remove;
|
||||
mod sexp_is_a;
|
||||
|
||||
pub use try_remove::TryRemove;
|
||||
pub use sexp_is_a::SexpIsA;
|
||||
@@ -0,0 +1,23 @@
|
||||
use sexp::Sexp;
|
||||
|
||||
pub trait SexpIsA {
|
||||
fn is_atom(&self) -> bool;
|
||||
|
||||
fn is_list(&self) -> bool;
|
||||
}
|
||||
|
||||
impl SexpIsA for Sexp {
|
||||
fn is_atom(&self) -> bool {
|
||||
match self {
|
||||
Sexp::Atom(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_list(&self) -> bool {
|
||||
match self {
|
||||
Sexp::List(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
pub trait TryRemove {
|
||||
type Item;
|
||||
fn try_remove(&mut self, index: usize) -> Option<Self::Item>;
|
||||
}
|
||||
|
||||
impl<T> TryRemove for Vec<T> {
|
||||
type Item = T;
|
||||
|
||||
fn try_remove(&mut self, index: usize) -> Option<T> {
|
||||
if self.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.remove(index))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user