implement a more readable bit mask syntax, add (ldXX Wr Rd Rd) with separate dest and both sources

pull/21/head
Ondřej Hruška 4 years ago
parent 26616e20cb
commit 3999c51eb7
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 93
      README.md
  2. 2
      crsn/crsn-sexp/src/lib.rs
  3. 46
      crsn/src/asm/data/mod.rs
  4. 2
      crsn/src/asm/instr/op.rs
  5. 193
      crsn/src/asm/parse/arg_parser.rs
  6. 190
      crsn/src/asm/parse/parse_data.rs
  7. 106
      crsn/src/builtin/defs.rs
  8. 10
      crsn/src/builtin/exec.rs
  9. 70
      crsn/src/builtin/parse.rs
  10. 1
      crsn/src/runtime/run_thread.rs
  11. 13
      crsn/src/runtime/run_thread/state.rs
  12. 39
      crsn/src/utils/mod.rs
  13. 8
      crsn_arith/src/defs.rs
  14. 18
      crsn_arith/src/exec.rs
  15. 85
      crsn_arith/src/parse.rs
  16. 8
      examples/bmp-parser.csn
  17. 4
      examples/test_clz_clo_se.csn
  18. 12
      examples/test_ldbits_xchbits.csn
  19. 18
      launcher/src/main.rs

@ -188,7 +188,11 @@ Objects (`@reg`, `@sym`) can be read or written as if they were a register, but
Other objects may produce a runtime fault or set the INVALID flag. Other objects may produce a runtime fault or set the INVALID flag.
In the instruction lists below, I will use the symbols `Rd` for reads, `Wr` for writes, `RW` for read-writes, and `@Obj` for object handles, In the instruction lists below, I will use the symbols `Rd` for reads, `Wr` for writes, `RW` for read-writes, and `@Obj` for object handles,
with optional description after a colon, such as: `(add Wr:dst Rd:a Rd:b)`. with optional description after an apostrophe, such as: `(add Wr'dst Rd'a Rd'b)`.
Some instructions use bit offsets and widths. Width is directly attached to the opcode (e.g. `(ld16 …)`);
offsets are attached at the respective arguments after a colon: `(ld16 r0:8 r1:32)` -
load 16 bits, starting at offset 32 of `r1`, into `r0`, starting at offset 8. The rest if the register is not affected.
### Conditional branches ### Conditional branches
@ -314,29 +318,13 @@ Jumping to a label is always safer than a manual skip.
; Copy a value ; Copy a value
(ld Wr Rd) (ld Wr Rd)
; Copy lower XX bits (the rest is untouched) ; Copy lower XX bits (the rest is untouched).
(ldXX RW Rd) ; Offsets can be specified to work with arbitrary bit slices
(ldXX RW:dst_offset Rd:src_offset)
; Copy XX bits to bit YY and up (ldXX Wr Rd:dst_offset Rd:src_offset)
; Example: (ld32/32 r1 r0) ; copy lower 32 bits of r0 to upper 32 bits of r1
;
; Rd: ##############<-- X -->
; |<-Y->
; Wr: ########<-- xY -->######
(ldXX/YY RW Rd)
; Copy XX bits at ZZ to bit YY and up
; Example: (ld32/32/16 r1 r0) ; copy bits 16-47 (32 bits) of r0 to bits 32-63
;
; |<--Z--->
; Rd: #####<-- X -->#########
; |<-Y->
; Wr: ########<-- X -->######
(ldXX/YY/ZZ RW Rd)
; Copy a value N times. This is useful when used with stream handles or buffers. ; Copy a value N times. This is useful when used with stream handles or buffers.
(ldn Wr Rd Rd:count) (ldn Wr Rd'src Rd'count)
; Write a sequence of values, or all codepoints from a string, into the destination. ; Write a sequence of values, or all codepoints from a string, into the destination.
; This is most useful with object handles, such as a buffer or @cout. ; This is most useful with object handles, such as a buffer or @cout.
@ -351,10 +339,9 @@ Jumping to a label is always safer than a manual skip.
; Exchange two register's values ; Exchange two register's values
(xch RW RW) (xch RW RW)
; Exchange bits in two registers (see ldXX/YY/ZZ for reference and diagrams) ; Exchange XX bits in two registers
(xchXX RW RW) ; Offsets can be specified to work with arbitrary bit slices
(xchXX/YY RW RW) (xchXX RW:offset RW:offset)
(xchXX/YY/ZZ RW RW)
; Store status flags to a register ; Store status flags to a register
(stf Wr) (stf Wr)
@ -407,12 +394,12 @@ Many instructions have two forms:
; Check if a value is in a range (inclusive). ; Check if a value is in a range (inclusive).
; Sets the EQ, LT and GT flags. Also sets Z, POS and NEG based on the value. ; Sets the EQ, LT and GT flags. Also sets Z, POS and NEG based on the value.
(rcmp Rd:val Rd:start Rd:end) (rcmp Rd'val Rd'start Rd'end)
; Get a random number ; Get a random number
(rng Wr) ; the value will fill all 64 bits of the target (rng Wr) ; the value will fill all 64 bits of the target
(rng Wr Rd:max) ; 0 to max, max is inclusive (rng Wr Rd'max) ; 0 to max, max is inclusive
(rng Wr Rd:min Rd:max) ; min to max, both are inclusive (rng Wr Rd'min Rd'max) ; min to max, both are inclusive
; Add A+B ; Add A+B
(add Wr Rd Rd) (add Wr Rd Rd)
@ -427,19 +414,19 @@ Many instructions have two forms:
(mul RW Rd) (mul RW Rd)
; Divide A/B ; Divide A/B
(div Wr Rd Rd:divider) (div Wr Rd Rd'divider)
(div RW Rd:divider) (div RW Rd'divider)
; Divide and get remainder ; Divide and get remainder
; Both DST and REM are output registers ; Both DST and REM are output registers
(divr Wr:result Wr:remainder Rd Rd:divider) (divr Wr'result Wr'remainder Rd Rd'divider)
(divr RW Wr:remainder Rd:divider) (divr RW Wr'remainder Rd'divider)
; Get remainder A%B ; Get remainder A%B
; This is equivalent to (divr _ REM A B), ; This is equivalent to (divr _ REM A B),
; except status flags are updated by the remainder value ; except status flags are updated by the remainder value
(mod Wr Rd Rd:divider) (mod Wr Rd Rd'divider)
(mod RW Rd:divider) (mod RW Rd'divider)
; Swap the 32-bit halves of a value ; Swap the 32-bit halves of a value
; 0x01234567_89abcdef -> 0x89abcdef_01234567 ; 0x01234567_89abcdef -> 0x89abcdef_01234567
@ -468,21 +455,17 @@ Many instructions have two forms:
(clz Wr Rd) (clz Wr Rd)
(clz RW) (clz RW)
; Count leading zeros in the lower XX bits ; Count leading zeros in the lower XX bits
(clzXX Wr Rd) ; Offsets can be specified to work with arbitrary bit slices
(clzXX RW) (clzXX Wr Rd:src_offs)
; Count leading zeros in the XX bits starting at YY (e.g. 16/32 is the lower 16 bits of the higher 32 bits) (clzXX RW:src_offs)
(clzXX/YY Wr Rd)
(clzXX/YY RW)
; Count leading ones ; Count leading ones
(clo Wr Rd) (clo Wr Rd)
(clo RW) (clo RW)
; Count leading ones in the lower XX bits ; Count leading ones in the lower XX bits
(cloXX Wr Rd) ; Offsets can be specified to work with arbitrary bit slices
(cloXX RW) (cloXX Wr Rd:src_offs)
; Count leading ones in the XX bits starting at YY (cloXX RW:src_offs)
(cloXX/YY Wr Rd)
(cloXX/YY RW)
; Sign extend a XX-bit value to 64 bits, XX in range 1..63) ; Sign extend a XX-bit value to 64 bits, XX in range 1..63)
(seXX Wr Rd) (seXX Wr Rd)
@ -509,24 +492,24 @@ Many instructions have two forms:
(ror RW Rd) (ror RW Rd)
; Rotate left (wrap around) ; Rotate left (wrap around)
(rol Wr Rd:value Rd:count) (rol Wr Rd'value Rd'count)
(rol RW Rd:count) (rol RW Rd'count)
; Logical shift right (fill with zeros) ; Logical shift right (fill with zeros)
(lsr Wr Rd Rd:count) (lsr Wr Rd Rd'count)
(lsr RW Rd:count) (lsr RW Rd'count)
; Logical shift left (fill with zeros) ; Logical shift left (fill with zeros)
(lsl Wr Rd Rd:count) (lsl Wr Rd Rd'count)
(lsl RW Rd:count) (lsl RW Rd'count)
; Arithmetic shift right (copy sign bit) ; Arithmetic shift right (copy sign bit)
(asr Wr Rd Rd:count) (asr Wr Rd Rd'count)
(asr RW Rd:count) (asr RW Rd'count)
; Arithmetic shift left (this is identical to `lsl`, added for completeness) ; Arithmetic shift left (this is identical to `lsl`, added for completeness)
(asl Wr Rd Rd:count) (asl Wr Rd Rd'count)
(asl RW Rd:count) (asl RW Rd'count)
; Delete an object by its handle. Objects are used by some extensions. ; Delete an object by its handle. Objects are used by some extensions.
(del @Rd) (del @Rd)

@ -95,7 +95,7 @@ fn without_underscores(s: &str) -> Cow<str> {
} }
} }
fn atom_of_string(s: String) -> Atom { pub fn atom_of_string(s: String) -> Atom {
if s.starts_with('#') { if s.starts_with('#') {
match u64::from_str_radix(&without_underscores(&s[1..]), 16) { match u64::from_str_radix(&without_underscores(&s[1..]), 16) {
Ok(u) => return Atom::U(u), Ok(u) => return Atom::U(u),

@ -78,6 +78,52 @@ impl From<WrData> for DataDisp {
} }
} }
impl From<Rd> for DataDisp {
fn from(s: Rd) -> Self {
s.0.into()
}
}
impl From<Wr> for DataDisp {
fn from(s: Wr) -> Self {
s.0.into()
}
}
impl From<RdWr> for DataDisp {
fn from(s: RdWr) -> Self {
s.0.into()
}
}
impl From<&Rd> for DataDisp {
fn from(s: &Rd) -> Self {
s.0.into()
}
}
impl From<&Wr> for DataDisp {
fn from(s: &Wr) -> Self {
s.0.into()
}
}
impl From<&RdWr> for DataDisp {
fn from(s: &RdWr) -> Self {
s.0.into()
}
}
pub trait DataDispEquals : Into<DataDisp> {
fn disp_equals<T : Into<DataDisp>>(self, other : T) -> bool;
}
impl<T : Into<DataDisp>> DataDispEquals for T {
fn disp_equals<X : Into<DataDisp>>(self, other : X) -> bool {
self.into() == other.into()
}
}
/// Data source disposition /// Data source disposition
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum RdData { pub enum RdData {

@ -18,9 +18,9 @@ pub enum OpKind {
#[derive(Debug)] #[derive(Debug)]
pub struct Op { pub struct Op {
pub kind: OpKind,
pub cond: Option<Cond>, pub cond: Option<Cond>,
pub pos: SourcePosition, pub pos: SourcePosition,
pub kind: OpKind,
} }
impl OpTrait for Op { impl OpTrait for Op {

@ -2,10 +2,12 @@ use sexp::{Sexp, SourcePosition};
use crate::asm::data::{Rd, RdData, RdObj, Wr, RdWr}; use crate::asm::data::{Rd, RdData, RdObj, Wr, RdWr};
use crate::asm::error::CrsnError; use crate::asm::error::CrsnError;
use crate::asm::parse::parse_data::{parse_rd, parse_wr, parse_value}; use crate::asm::parse::parse_data::{parse_rd, parse_wr, parse_value, parse_rdwr_offset, parse_rdwr, parse_rd_offset, parse_wr_offset};
use crate::asm::parse::ParserContext; use crate::asm::parse::ParserContext;
use crate::asm::parse::sexp_expect::expect_string_atom; use crate::asm::parse::sexp_expect::expect_string_atom;
use crate::asm::data::literal::Value; use crate::asm::data::literal::Value;
use crate::builtin::defs::BitMask;
use crate::asm::patches::ErrWithPos;
/// Utility for argument parsing /// Utility for argument parsing
#[derive(Debug)] #[derive(Debug)]
@ -21,7 +23,7 @@ impl<'a> IntoIterator for TokenParser<'a> {
type IntoIter = std::vec::IntoIter<Sexp>; type IntoIter = std::vec::IntoIter<Sexp>;
fn into_iter(mut self) -> Self::IntoIter { fn into_iter(mut self) -> Self::IntoIter {
// The vec is reversed so we can call "pop", but here we will iterate it as slice so it // The vec is reversed so we can call "pop", but here we will iterate it as a slice, so it
// must be reversed to its original direction. // must be reversed to its original direction.
self.args.reverse(); self.args.reverse();
self.args.into_iter() self.args.into_iter()
@ -40,6 +42,16 @@ impl<'a> TokenParser<'a> {
} }
} }
/// Get error if not empty.
/// The argument is substituted into the phrase "Instruction needs ...!" - i.e. "one Wr argument and a list or string"
pub fn ensure_empty(&self, what_arguments : &str) -> Result<(), CrsnError> {
if self.have_more() {
Err(CrsnError::Parse(format!("Instruction needs {}!", what_arguments).into(), self.start_pos.clone()))
} else {
Ok(())
}
}
pub fn have_more(&self) -> bool { pub fn have_more(&self) -> bool {
self.len() > 0 self.len() > 0
} }
@ -121,13 +133,180 @@ impl<'a> TokenParser<'a> {
/// Get the next entry as write location /// Get the next entry as write location
pub fn next_rdwr(&mut self) -> Result<RdWr, CrsnError> { pub fn next_rdwr(&mut self) -> Result<RdWr, CrsnError> {
let next = self.next_or_err()?; let next = self.next_or_err()?;
let pos = next.pos().clone(); parse_rdwr(next, self.pcx)
let wr = parse_wr(next, self.pcx)?; }
if !wr.is_readable() { /// Get the next entry as write location with optional :offset
return Err(CrsnError::Parse("Argument is not readable!".into(), pos)); pub fn next_rdwr_offset(&mut self) -> Result<(RdWr, u32), CrsnError> {
let next = self.next_or_err()?;
parse_rdwr_offset(next, self.pcx)
}
pub fn next_rd_offset(&mut self) -> Result<(Rd, u32), CrsnError> {
let next = self.next_or_err()?;
parse_rd_offset(next, self.pcx)
}
pub fn next_wr_offset(&mut self) -> Result<(Wr, u32), CrsnError> {
let next = self.next_or_err()?;
parse_wr_offset(next, self.pcx)
}
/// Parse combining binary instruction operands (i.e. add) with bit masks
/// Accepts (Wr, Rd, Rd) and (RdWr, Rd)
pub fn parse_masked_wr_rd_rd(&mut self, keyword: &str, prefix: &str) -> Result<Option<(Wr, Rd, Rd, BitMask)>, CrsnError> {
if let Some(s) = keyword.strip_prefix(prefix) {
let width = if s.is_empty() {
(std::mem::size_of::<Value>() as u32) * 8
} else {
s.parse().err_pos(self.start_pos)?
};
if self.len() == 2 {
let (rw, dst_pos) = self.next_rdwr_offset()?;
let (rd, src_pos) = self.next_rd_offset()?;
let mask = BitMask {
width,
dst_pos,
src_pos: dst_pos,
src2_pos: src_pos,
};
mask.validate(self.start_pos)?;
Ok(Some((rw.wr(), rw.rd(), rd, mask)))
} else if self.len() == 3 {
let (wr, dst_pos) = self.next_wr_offset()?;
let (rd1, src_pos) = self.next_rd_offset()?;
let (rd2, src2_pos) = self.next_rd_offset()?;
let mask = BitMask {
width,
dst_pos,
src_pos,
src2_pos,
};
mask.validate(self.start_pos)?;
Ok(Some((wr, rd1, rd2, mask)))
} else {
Err(CrsnError::Parse("Instruction needs 2 (RW Rd) or 3 (Wr Rd Rd) arguments!".into(), self.start_pos.clone()))
}
} else {
Ok(None)
} }
}
/// Parse unary instruction operands (i.e. complement) with bit masks.
/// Accepts (Wr, Rd) and (RdWr)
pub fn parse_masked_wr_rd(&mut self, keyword: &str, prefix: &'static str) -> Result<Option<(Wr, Rd, BitMask)>, CrsnError> {
if let Some(s) = keyword.strip_prefix(prefix) {
let width = if s.is_empty() {
(std::mem::size_of::<Value>() as u32) * 8
} else {
s.parse().err_pos(self.start_pos)?
};
if self.len() == 1 {
let (rw, dst_pos) = self.next_rdwr_offset()?;
Ok(RdWr::new(wr.0)) let mask = BitMask {
width,
dst_pos,
src_pos: dst_pos,
src2_pos: 0,
};
mask.validate(self.start_pos)?;
Ok(Some((rw.wr(), rw.rd(), mask)))
} else if self.len() == 2 {
let (wr, dst_pos) = self.next_wr_offset()?;
let (rd, src_pos) = self.next_rd_offset()?;
let mask = BitMask {
width,
dst_pos,
src_pos,
src2_pos: 0,
};
mask.validate(self.start_pos)?;
Ok(Some((wr, rd, mask)))
} else {
Err(CrsnError::Parse("Instruction needs 1 (RW) or 2 (Wr Rd) arguments!".into(), self.start_pos.clone()))
}
} else {
Ok(None)
}
}
/// Parse a swap-type binary instruction operands (i.e. exchange) with bit masks
/// Accepts (RdWr, RdWr)
pub fn parse_masked_rdwr_rdwr(&mut self, keyword: &str, prefix: &str) -> Result<Option<(RdWr, RdWr, BitMask)>, CrsnError> {
if let Some(s) = keyword.strip_prefix(prefix) {
let width = if s.is_empty() {
(std::mem::size_of::<Value>() as u32) * 8
} else {
s.parse().err_pos(self.start_pos)?
};
if self.len() == 2 {
let (wr1, dst_pos) = self.next_rdwr_offset()?;
let (wr2, src_pos) = self.next_rdwr_offset()?;
let mask = BitMask {
width,
dst_pos,
src_pos,
src2_pos: 0,
};
mask.validate(self.start_pos)?;
Ok(Some((wr1, wr2, mask)))
} else {
Err(CrsnError::Parse("Instruction needs 2 (RW RW) arguments!".into(), self.start_pos.clone()))
}
} else {
Ok(None)
}
}
/// Parse combining binary instruction operands (i.e. add) without masks
pub fn parse_wr_rd_rd(&mut self) -> Result<(Wr, Rd, Rd), CrsnError> {
if self.len() == 2 {
let rw = self.next_rdwr()?;
let rd = self.next_rd()?;
Ok((rw.wr(), rw.rd(), rd))
} else if self.len() == 3 {
let wr = self.next_wr()?;
let rd1 = self.next_rd()?;
let rd2 = self.next_rd()?;
Ok((wr, rd1, rd2))
} else {
Err(CrsnError::Parse("Instruction needs 2 (RW Rd) or 3 (Wr Rd Rd) arguments!".into(), self.start_pos.clone()))
}
}
/// Parse unary instruction operands (i.e. complement) without masks
pub fn parse_wr_rd(&mut self) -> Result<(Wr, Rd), CrsnError> {
if self.len() == 1 {
let rw = self.next_rdwr()?;
Ok((rw.wr(), rw.rd()))
} else if self.len() == 2 {
let wr = self.next_wr()?;
let rd = self.next_rd()?;
Ok((wr, rd))
} else {
Err(CrsnError::Parse("Instruction needs 1 (RW) or 2 (Wr Rd) arguments!".into(), self.start_pos.clone()))
}
}
/// Parse a swap-type binary instruction operands (i.e. exchange) without masks
pub fn parse_rdwr_rdwr(&mut self, _keyword: &str, _prefix: &str, _pos: &SourcePosition) -> Result<(RdWr, RdWr), CrsnError> {
if self.len() == 2 {
let rw1 = self.next_rdwr()?;
let rw2 = self.next_rdwr()?;
Ok((rw1, rw2))
} else {
Err(CrsnError::Parse("Instruction needs 2 (RW RW) arguments!".into(), self.start_pos.clone()))
}
} }
} }

@ -3,7 +3,7 @@ use std::convert::TryFrom;
use sexp::{Atom, Sexp, SourcePosition}; use sexp::{Atom, Sexp, SourcePosition};
use crate::asm::data::{DataDisp, Rd, RdData, reg, Wr, WrData}; use crate::asm::data::{DataDisp, Rd, RdData, reg, Wr, WrData, RdWr};
use crate::asm::data::literal::{ConstantName, Label, RegisterAlias, Value}; use crate::asm::data::literal::{ConstantName, Label, RegisterAlias, Value};
use crate::asm::error::CrsnError; use crate::asm::error::CrsnError;
use crate::asm::parse::ParserContext; use crate::asm::parse::ParserContext;
@ -71,9 +71,6 @@ pub fn parse_label_str(name: &str, pos: &SourcePosition) -> Result<Label, CrsnEr
/// Parse data disposition (address/value, without the read/write restriction) /// Parse data disposition (address/value, without the read/write restriction)
pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result<DataDisp, CrsnError> { pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result<DataDisp, CrsnError> {
// trace!("parse data: {:?}", tok); // trace!("parse data: {:?}", tok);
// TODO implement masks
match tok { match tok {
Sexp::Atom(Atom::I(val), _pos) => { Sexp::Atom(Atom::I(val), _pos) => {
Ok(DataDisp::Immediate(unsafe { std::mem::transmute(val) })) Ok(DataDisp::Immediate(unsafe { std::mem::transmute(val) }))
@ -94,69 +91,94 @@ pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result<DataDisp, CrsnE
Err(CrsnError::Parse("List not expected here".into(), pos)) Err(CrsnError::Parse("List not expected here".into(), pos))
} }
Sexp::Atom(Atom::S(s), pos) => { Sexp::Atom(Atom::S(s), pos) => {
if s == "_" { parse_data_disp_from_str(&s, &pos, pcx)
return Ok(DataDisp::Discard); }
} }
}
if let Some(reference) = s.strip_prefix('@') { /// Parse data disp from a string token (a &str is used so the string can be preprocessed)
/* extension constants (pre-defined handles) */ pub fn parse_data_disp_from_str(s: &str, pos: &SourcePosition, pcx: &ParserContext) -> Result<DataDisp, CrsnError> {
for p in pcx.parsers { if s == "_" {
if let Some(val) = p.get_constant_value(reference) { return Ok(DataDisp::Discard);
return Ok(DataDisp::ImmObject(val)); }
}
}
/* register aliases */ if let Some(reference) = s.strip_prefix('@') {
let pstate = pcx.state.borrow(); /* extension constants (pre-defined handles) */
if let Some(reg) = pstate.reg_aliases.get(reference) { for p in pcx.parsers {
return Ok(DataDisp::RegObject(*reg)) if let Some(val) = p.get_constant_value(reference) {
} else if let Some(reg) = pstate.global_reg_aliases.get(reference) { return Ok(DataDisp::ImmObject(val));
return Ok(DataDisp::RegObject(*reg)) }
} }
let reg = reg::parse_reg(reference, &pos)?; /* register aliases */
let pstate = pcx.state.borrow();
if let Some(reg) = pstate.reg_aliases.get(reference) {
return Ok(DataDisp::RegObject(*reg))
} else if let Some(reg) = pstate.global_reg_aliases.get(reference) {
return Ok(DataDisp::RegObject(*reg))
}
if pstate.reg_aliases.values().find(|v| **v == reg).is_some() { let reg = reg::parse_reg(reference, pos)?;
Err(CrsnError::Parse(format!("Sym exists for register {}, direct access denied. Unsym it if needed.", reg).into(), pos))
} else if pstate.global_reg_aliases.values().find(|v| **v == reg).is_some() {
Err(CrsnError::Parse(format!("Global sym exists for register {}, direct access denied. Unsym it if needed.", reg).into(), pos))
} else {
Ok(DataDisp::RegObject(reg))
}
} else {
/* extension constants */
for p in pcx.parsers {
if let Some(val) = p.get_constant_value(&s) {
return Ok(DataDisp::Immediate(val));
}
}
/* program constants */ if pstate.reg_aliases.values().find(|v| **v == reg).is_some() {
let pstate = pcx.state.borrow(); Err(CrsnError::Parse(format!("Sym exists for register {}, direct access denied. Unsym it if needed.", reg).into(), pos.clone()))
if let Some(val) = pstate.constants.get(&s) { } else if pstate.global_reg_aliases.values().find(|v| **v == reg).is_some() {
return Ok(DataDisp::Immediate(*val)); Err(CrsnError::Parse(format!("Global sym exists for register {}, direct access denied. Unsym it if needed.", reg).into(), pos.clone()))
} } else {
Ok(DataDisp::RegObject(reg))
}
} else {
/* extension constants */
for p in pcx.parsers {
if let Some(val) = p.get_constant_value(&s) {
return Ok(DataDisp::Immediate(val));
}
}
/* register aliases */ /* program constants */
if let Some(val) = pstate.reg_aliases.get(&s) { let pstate = pcx.state.borrow();
return Ok(DataDisp::Register(*val)); if let Some(val) = pstate.constants.get(s) {
} else if let Some(val) = pstate.global_reg_aliases.get(&s) { return Ok(DataDisp::Immediate(*val));
return Ok(DataDisp::Register(*val)); }
}
/* register */ /* register aliases */
let reg = reg::parse_reg(&s, &pos)?; if let Some(val) = pstate.reg_aliases.get(s) {
return Ok(DataDisp::Register(*val));
} else if let Some(val) = pstate.global_reg_aliases.get(s) {
return Ok(DataDisp::Register(*val));
}
let pstate = pcx.state.borrow(); /* It can also be a plain old number */
if pstate.reg_aliases.values().find(|v| **v == reg).is_some() {
Err(CrsnError::Parse(format!("Sym exists for register {}, direct access denied. Unsym it if needed.", reg).into(), pos)) if s.starts_with('#')
} else if pstate.global_reg_aliases.values().find(|v| **v == reg).is_some() { || s.starts_with('-')
Err(CrsnError::Parse(format!("Global sym exists for register {}, direct access denied. Unsym it if needed.", reg).into(), pos)) || s.starts_with('.')
} else { || s.starts_with("0x")
Ok(DataDisp::Register(reg)) || s.starts_with("0b")
|| s.starts_with(|c : char| c.is_ascii_digit())
{
match sexp::atom_of_string(s.to_string()) {
Atom::C(v) => return Ok(DataDisp::Immediate(v as Value)),
Atom::I(v) => return Ok(DataDisp::Immediate(unsafe { std::mem::transmute::<_, u64>(v) } as Value)),
Atom::U(v) => return Ok(DataDisp::Immediate(v as Value)),
Atom::F(v) => return Ok(DataDisp::Immediate(unsafe { std::mem::transmute::<_, u64>(v) } as Value)),
Atom::S(_) | Atom::QS(_) => {
// this will probably fail validation down the line, but it is definitely not a number
} }
} }
} }
/* register */
let reg = reg::parse_reg(&s, &pos)?;
let pstate = pcx.state.borrow();
if pstate.reg_aliases.values().find(|v| **v == reg).is_some() {
Err(CrsnError::Parse(format!("Sym exists for register {}, direct access denied. Unsym it if needed.", reg).into(), pos.clone()))
} else if pstate.global_reg_aliases.values().find(|v| **v == reg).is_some() {
Err(CrsnError::Parse(format!("Global sym exists for register {}, direct access denied. Unsym it if needed.", reg).into(), pos.clone()))
} else {
Ok(DataDisp::Register(reg))
}
} }
} }
@ -225,3 +247,59 @@ pub fn parse_wr(tok: Sexp, pcx: &ParserContext) -> Result<Wr, CrsnError> {
let pos = tok.pos().clone(); let pos = tok.pos().clone();
Ok(Wr::new(WrData::try_from(parse_data_disp(tok, pcx)?).err_pos(&pos)?)) Ok(Wr::new(WrData::try_from(parse_data_disp(tok, pcx)?).err_pos(&pos)?))
} }
pub fn parse_rdwr(tok: Sexp, pcx: &ParserContext) -> Result<RdWr, CrsnError> {
let pos = tok.pos().clone();
let wr = parse_wr(tok, pcx)?;
if !wr.is_readable() {
return Err(CrsnError::Parse("Argument is not readable!".into(), pos));
}
Ok(RdWr::new(wr.0))
}
pub fn parse_wr_offset(tok: Sexp, pcx: &ParserContext) -> Result<(Wr, u32), CrsnError> {
match tok {
Sexp::Atom(Atom::S(s), pos) => {
let (s, offset) = parse_disp_offs(&s, &pos)?;
Ok((Wr::new(WrData::try_from(parse_data_disp_from_str(s, &pos, pcx)?).err_pos(&pos)?), offset))
}
_ => Ok((parse_wr(tok, pcx)?, 0))
}
}
pub fn parse_rd_offset(tok: Sexp, pcx: &ParserContext) -> Result<(Rd, u32), CrsnError> {
match tok {
Sexp::Atom(Atom::S(s), pos) => {
let (s, offset) = parse_disp_offs(&s, &pos)?;
Ok((Rd::new(RdData::try_from(parse_data_disp_from_str(s, &pos, pcx)?).err_pos(&pos)?), offset))
}
_ => Ok((parse_rd(tok, pcx)?, 0))
}
}
pub fn parse_rdwr_offset(tok: Sexp, pcx: &ParserContext) -> Result<(RdWr, u32), CrsnError> {
match tok {
Sexp::Atom(Atom::S(s), pos) => {
let (s, offset) = parse_disp_offs(&s, &pos)?;
let wr = WrData::try_from(parse_data_disp_from_str(s, &pos, pcx)?).err_pos(&pos)?;
if !wr.is_readable() {
return Err(CrsnError::Parse("Argument is not readable!".into(), pos));
}
Ok((RdWr::new(wr), offset))
}
_ => Ok((parse_rdwr(tok, pcx)?, 0))
}
}
fn parse_disp_offs<'a>(s : &'a str, pos: &SourcePosition) -> Result<(&'a str, u32), CrsnError> {
if s.contains(':') {
let mut split = s.split(':');
let disp = split.next().unwrap();
let num : u32 = split.next().unwrap().parse().err_pos(pos)?;
Ok((disp, num))
} else {
Ok((&s, 0))
}
}

@ -1,13 +1,13 @@
use sexp::SourcePosition; use sexp::SourcePosition;
use crate::asm::data::{Rd, RdObj, Wr, RdWr}; use crate::asm::data::{Rd, RdObj, Wr, RdWr};
use crate::asm::data::literal::{DebugMsg, Label, RoutineName}; use crate::asm::data::literal::{DebugMsg, Label, RoutineName, Value};
use crate::asm::instr::Op; use crate::asm::instr::Op;
use crate::asm::instr::op::OpKind; use crate::asm::instr::op::OpKind;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::fmt; use std::fmt;
use crate::asm::error::CrsnError; use crate::asm::error::CrsnError;
use crate::asm::patches::ErrWithPos;
#[derive(Debug)] #[derive(Debug)]
pub enum Barrier { pub enum Barrier {
@ -41,14 +41,20 @@ pub enum LdsValue {
Chars(String), Chars(String),
} }
/// Instruction's bit mask
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BitSlice { pub struct BitMask {
/// Operation bit width
pub width: u32, pub width: u32,
/// Destination (or first operand's) offset
pub dst_pos: u32,
/// Source (or second operand's) offset
pub src_pos: u32, pub src_pos: u32,
pub dst_pos: u32 /// Source 2 offset (if used)
pub src2_pos: u32,
} }
impl Display for BitSlice { impl Display for BitMask {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if self.is_full() { if self.is_full() {
return Ok(()); return Ok(());
@ -65,94 +71,42 @@ impl Display for BitSlice {
} }
} }
impl Default for BitSlice { impl Default for BitMask {
fn default() -> Self { fn default() -> Self {
Self { Self {
width: 64, width: 64,
dst_pos: 0,
src_pos: 0, src_pos: 0,
dst_pos: 0 src2_pos: 0,
} }
} }
} }
impl BitSlice { impl BitMask {
pub fn full() -> Self { pub fn full() -> Self {
Self::default() Self::default()
} }
/// Parse LEN (no SRC and DST) pub fn validate(&self, pos: &SourcePosition) -> Result<(), CrsnError> {
pub fn parse1(src: &str, pos: &SourcePosition) -> Result<Option<Self>, CrsnError> { #[allow(non_snake_case)]
match Self::parse(src, pos) { let MAXWIDTH = (std::mem::size_of::<Value>() as u32)*8;
Ok(Some(slice)) => {
if slice.src_pos != 0 || slice.dst_pos != 0 {
return Err(CrsnError::Parse("Excess bit slice modifiers".into(), pos.clone()));
}
Ok(Some(BitSlice {
width: slice.width,
src_pos: 0,
dst_pos: 0
}))
}
other => other
}
}
/// Parse LEN/SRC
pub fn parse2(src: &str, pos: &SourcePosition) -> Result<Option<Self>, CrsnError> {
match Self::parse(src, pos) {
Ok(Some(slice)) => {
if slice.src_pos != 0 {
return Err(CrsnError::Parse("Excess bit slice modifiers".into(), pos.clone()));
}
Ok(Some(BitSlice {
width: slice.width,
src_pos: slice.dst_pos,
dst_pos: 0
}))
}
other => other
}
}
/// Parse a bit slice LEN/DST/SRC
/// - String not starting with a digit is rejected as `Ok(None)`
/// - Empty string is parsed as a full slice
pub fn parse(src: &str, pos: &SourcePosition) -> Result<Option<Self>, CrsnError> {
if src.is_empty() {
return Ok(Some(BitSlice::full()));
}
if !src.starts_with(|c: char| c.is_ascii_digit()) { if self.width == 0 || self.width > MAXWIDTH {
return Ok(None); return Err(CrsnError::Parse(format!("Bit width must be 1-{}", MAXWIDTH).into(), pos.clone()));
}
// We have a opXX, opXX/YY, or opXX/YY/ZZ
let mut numbers : Vec<u32> = vec![];
for p in src.split('/') {
numbers.push(p.parse().err_pos(pos)?);
}
let slice = BitSlice {
width: numbers[0],
src_pos: numbers.get(2).copied().unwrap_or(0),
dst_pos: numbers.get(1).copied().unwrap_or(0)
};
if slice.width == 0 || slice.width > 64 {
return Err(CrsnError::Parse("Bit slice width must be 1-64".into(), pos.clone()));
} }
// Validation // Validation
if slice.src_pos + slice.width > 64 { if self.src_pos + self.width > MAXWIDTH {
return Err(CrsnError::Parse("Invalid source bit slice".into(), pos.clone())); return Err(CrsnError::Parse("Invalid source bit mask".into(), pos.clone()));
}
if self.src2_pos + self.width > MAXWIDTH {
return Err(CrsnError::Parse("Invalid source 2 bit mask".into(), pos.clone()));
} }
if slice.dst_pos + slice.width > 64 { if self.dst_pos + self.width > MAXWIDTH {
return Err(CrsnError::Parse("Invalid destination bit slice".into(), pos.clone())); return Err(CrsnError::Parse("Invalid destination bit mask".into(), pos.clone()));
} }
Ok(Some(slice)) Ok(())
} }
pub fn is_full(self) -> bool { pub fn is_full(self) -> bool {
@ -203,13 +157,13 @@ pub enum BuiltinOp {
/// Move a value /// Move a value
Load { dst: Wr, src: Rd }, Load { dst: Wr, src: Rd },
/// Move bits of a value /// Move bits of a value
LoadBits { dst: RdWr, src: Rd, slice: BitSlice }, LoadBits { dst: Wr, a: Rd, b: Rd, mask: BitMask },
/// Move N values /// Move N values
LoadMultiple { dst: Wr, src: Rd, count: Rd }, LoadMultiple { dst: Wr, src: Rd, count: Rd },
/// Move values from a string or integer list /// Move values from a string or integer list
LoadSequence { dst: Wr, value: LdsValue }, LoadSequence { dst: Wr, value: LdsValue },
/// Swap two registers /// Swap two registers
Exchange { a: RdWr, b: RdWr, slice : BitSlice }, Exchange { a: RdWr, b: RdWr, mask: BitMask },
/// Store runtime status to a register /// Store runtime status to a register
StoreFlags { dst: Wr }, StoreFlags { dst: Wr },
/// Load runtime status from a register /// Load runtime status from a register

@ -119,12 +119,12 @@ impl OpTrait for BuiltinOp {
} }
state.update_status(last); state.update_status(last);
} }
BuiltinOp::LoadBits { dst, src, slice, } => { BuiltinOp::LoadBits { dst, a, b, mask: slice, } => {
state.clear_status(); state.clear_status();
let new = state.read(src)?; let new = state.read(b)?;
let old = state.read(dst)?; let old = state.read(a)?;
let ones : u64 = (1 << slice.width) - 1; let ones : u64 = (1 << slice.width) - 1;
let val = (old & !(ones << slice.dst_pos)) | (((new & (ones << slice.src_pos)) >> slice.src_pos) << slice.dst_pos); let val = (old & !(ones << slice.src_pos)) | (((new & (ones << slice.src2_pos)) >> slice.src2_pos) << slice.src_pos);
state.update_status(val); state.update_status(val);
state.write(dst, val)?; state.write(dst, val)?;
} }
@ -158,7 +158,7 @@ impl OpTrait for BuiltinOp {
} }
} }
} }
BuiltinOp::Exchange { a, b, slice } => { BuiltinOp::Exchange { a, b, mask: slice } => {
let aa = state.read(a)?; let aa = state.read(a)?;
let bb = state.read(b)?; let bb = state.read(b)?;

@ -8,10 +8,10 @@ use crate::asm::parse::arg_parser::TokenParser;
use crate::asm::parse::parse_data::{parse_constant_name, parse_label, parse_label_str, parse_rd, parse_reg_alias, parse_value}; use crate::asm::parse::parse_data::{parse_constant_name, parse_label, parse_label_str, parse_rd, parse_reg_alias, parse_value};
use crate::asm::parse::sexp_expect::{expect_any_string_atom}; use crate::asm::parse::sexp_expect::{expect_any_string_atom};
use crate::asm::patches::ErrWithPos; use crate::asm::patches::ErrWithPos;
use crate::builtin::defs::{Barrier, BuiltinOp, SleepUnit, LdsValue, BitSlice}; use crate::builtin::defs::{Barrier, BuiltinOp, SleepUnit, LdsValue};
use crate::module::ParseRes; use crate::module::ParseRes;
use crate::utils::A; use crate::utils::{A, AM};
use crate::asm::data::{Rd, RdData, RdObj}; use crate::asm::data::{Rd, RdData, RdObj, DataDispEquals};
pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> { pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
let pcx = args.pcx; let pcx = args.pcx;
@ -238,13 +238,13 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok
BuiltinOp::LoadSequence { dst, value } BuiltinOp::LoadSequence { dst, value }
} }
"xch" => { // "xch" => {
BuiltinOp::Exchange { // BuiltinOp::Exchange {
a: args.next_rdwr()?, // a: args.next_rdwr()?,
b: args.next_rdwr()?, // b: args.next_rdwr()?,
slice: BitSlice::full(), // mask: BitMask::full(),
} // }
} // }
"stf" => { "stf" => {
BuiltinOp::StoreFlags { BuiltinOp::StoreFlags {
@ -276,24 +276,17 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok
} }
other => { other => {
if let Some(s) = other.strip_prefix("ld") { if let Some((dst, a, b, mask)) = args.parse_masked_wr_rd_rd(other, "ld")? {
if let Some(slice) = BitSlice::parse(s, op_pos)? { if !dst.disp_equals(a) && mask.dst_pos != 0 {
return Ok(ParseRes::builtin(BuiltinOp::LoadBits { return Err(CrsnError::Parse(
dst: args.next_rdwr()?, "Invalid offsets - permissible formats are (RW:dst_offs Rd:src_offs) and (Wr Rd:dst_offs, Rd:src_offs) ".into(),
src: args.next_rd()?, op_pos.clone()));
slice,
}));
} }
return Ok(ParseRes::builtin(BuiltinOp::LoadBits { dst, a, b, mask }));
} }
if let Some(s) = other.strip_prefix("xch") { if let Some((a, b, mask)) = args.parse_masked_rdwr_rdwr(other, "xch")? {
if let Some(slice) = BitSlice::parse(s, op_pos)? { return Ok(ParseRes::builtin(BuiltinOp::Exchange { a, b, mask }));
return Ok(ParseRes::builtin(BuiltinOp::Exchange {
a: args.next_rdwr()?,
b: args.next_rdwr()?,
slice,
}));
}
} }
if other.starts_with(':') { if other.starts_with(':') {
@ -381,11 +374,21 @@ pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp {
BuiltinOp::Load { dst, src, } => { BuiltinOp::Load { dst, src, } => {
sexp::list(&[A("ld"), A(dst), A(src)]) sexp::list(&[A("ld"), A(dst), A(src)])
}, },
BuiltinOp::LoadBits { dst, src, slice, } => { BuiltinOp::LoadBits { dst, a, b, mask, } => {
sexp::list(&[A(format!("ld{}", slice)), A(dst), A(src)]) if dst.disp_equals(a) {
sexp::list(&[A(format!("ld{}", mask.width)), AM(dst, mask.dst_pos), AM(b, mask.src2_pos)])
} else {
sexp::list(&[A(format!("ld{}", mask.width)), AM(dst, mask.dst_pos), AM(a, mask.src_pos), AM(b, mask.src2_pos)])
}
}, },
BuiltinOp::LoadMultiple { dst, src, count } => sexp::list(&[A("ldn"), A(dst), A(src), A(count)]), BuiltinOp::LoadMultiple { dst, src, count } => sexp::list(&[A("ldn"), A(dst), A(src), A(count)]),
BuiltinOp::Exchange { a, b, slice } => sexp::list(&[A(format!("xch{}", slice)), A(a), A(b)]), BuiltinOp::Exchange { a, b, mask } => {
if mask.is_full() {
sexp::list(&[A("xch"), A(a), A(b)])
} else {
sexp::list(&[A(format!("xch{}", mask.width)), AM(a, mask.dst_pos), AM(b, mask.src_pos)])
}
},
BuiltinOp::StoreFlags { dst } => sexp::list(&[A("stf"), A(dst)]), BuiltinOp::StoreFlags { dst } => sexp::list(&[A("stf"), A(dst)]),
BuiltinOp::LoadFlags { src } => sexp::list(&[A("ldf"), A(src)]), BuiltinOp::LoadFlags { src } => sexp::list(&[A("ldf"), A(src)]),
BuiltinOp::LoadSequence { dst, value } => { BuiltinOp::LoadSequence { dst, value } => {
@ -470,14 +473,15 @@ mod test {
("(fault \"do pr*ele\")", "(fault \"do pr*ele\")"), ("(fault \"do pr*ele\")", "(fault \"do pr*ele\")"),
("(xch r0 r1)", "(xch r0 r1)"), ("(xch r0 r1)", "(xch r0 r1)"),
("(xch32 r0 r1)", "(xch32 r0 r1)"), ("(xch32 r0 r1)", "(xch32 r0 r1)"),
("(xch32/8/16 r0 r1)", "(xch32/8/16 r0 r1)"), ("(xch32 r0:8 r1:16)", "(xch32 r0:8 r1:16)"),
("(ld r0 r0)", "(ld r0 r0)"), ("(ld r0 r0)", "(ld r0 r0)"),
("(ld8 r0 r1)", "(ld8 r0 r1)"), ("(ld8 r0 r1)", "(ld8 r0 r1)"),
("(ld16 r0 r1)", "(ld16 r0 r1)"), ("(ld16 r0 r1)", "(ld16 r0 r1)"),
("(ld32 r0 r1)", "(ld32 r0 r1)"), ("(ld32 r0 r1)", "(ld32 r0 r1)"),
("(ld32/32 r0 r1)", "(ld32/32 r0 r1)"), ("(ld32 r0:32 r1)", "(ld32 r0:32 r1)"),
("(ld32/32/5 r0 r1)", "(ld32/32/5 r0 r1)"), ("(ld32 r0:32 r1:5)", "(ld32 r0:32 r1:5)"),
("(ld32/0/16 r0 r1)", "(ld32/0/16 r0 r1)"), ("(ld32 r0 r1:16)", "(ld32 r0 r1:16)"),
("(ld32 r7 r0:3 r1:16)", "(ld32 r7 r0:3 r1:16)"),
("(ld r0 156)", "(ld r0 156)"), ("(ld r0 156)", "(ld r0 156)"),
("(ld _ -32767)", "(ld _ -32767)"), ("(ld _ -32767)", "(ld _ -32767)"),
("(ldn _ @r0 7)", "(ldn _ @r0 7)"), ("(ldn _ @r0 7)", "(ldn _ @r0 7)"),
@ -512,7 +516,7 @@ mod test {
.expect("flatten").remove(0); .expect("flatten").remove(0);
let exported = parsed.to_sexp().to_string(); let exported = parsed.to_sexp().to_string();
println!("Parsed: {}", exported); println!("Parsed: {}, dbg {:?}", exported, parsed);
assert_eq!(expected, exported); assert_eq!(expected, exported);

@ -87,6 +87,7 @@ impl RunThread {
} }
Err(e) => { Err(e) => {
error!("Fault: {:?}", e); error!("Fault: {:?}", e);
error!("Core dump: {:?}", self.state);
break 'run; break 'run;
} }
} }

@ -10,6 +10,8 @@ use std::sync::Arc;
use crate::runtime::run_thread::ThreadInfo; use crate::runtime::run_thread::ThreadInfo;
use nudge::{likely}; use nudge::{likely};
use crate::asm::instr::cond::Flag; use crate::asm::instr::cond::Flag;
use std::fmt::{Debug, Formatter};
use std::fmt;
pub struct RunState { pub struct RunState {
pub thread_info: Arc<ThreadInfo>, pub thread_info: Arc<ThreadInfo>,
@ -23,6 +25,17 @@ pub struct RunState {
pub ext_data: ExtensionDataStore, pub ext_data: ExtensionDataStore,
} }
impl Debug for RunState {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("RunState")
.field("frame", &self.frame)
.field("call_stack", &self.call_stack)
.field("global_regs", &self.global_regs)
//.field("ext_data", &self.ext_data)
.finish()
}
}
#[derive(Debug,Default)] #[derive(Debug,Default)]
pub struct ExtensionDataStore { pub struct ExtensionDataStore {
store: HashMap<TypeId, Box<dyn Any + Send>> store: HashMap<TypeId, Box<dyn Any + Send>>

@ -4,9 +4,10 @@ use std::str::FromStr;
pub use option_ext::UncheckedOptionExt; pub use option_ext::UncheckedOptionExt;
use sexp::{Atom, Sexp}; use sexp::{Atom, Sexp};
mod option_ext; mod option_ext;
/// Convert a string token to Sexp /// Convert a value to Sexp
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn A(s: impl Display) -> Sexp { pub fn A(s: impl Display) -> Sexp {
let s = s.to_string(); let s = s.to_string();
@ -21,9 +22,9 @@ pub fn A(s: impl Display) -> Sexp {
return Sexp::Atom(Atom::I(x), Default::default()); return Sexp::Atom(Atom::I(x), Default::default());
} }
let y: Result<f64, _> = FromStr::from_str(&s); let x: Result<f64, _> = FromStr::from_str(&s);
if let Ok(y) = y { if let Ok(x) = x {
return Sexp::Atom(Atom::F(y), Default::default()); return Sexp::Atom(Atom::F(x), Default::default());
} }
if s.contains(|c: char| " \t\"\\\n\t\r".contains(c)) { if s.contains(|c: char| " \t\"\\\n\t\r".contains(c)) {
@ -32,3 +33,33 @@ pub fn A(s: impl Display) -> Sexp {
Sexp::Atom(Atom::S(s), Default::default()) Sexp::Atom(Atom::S(s), Default::default())
} }
} }
/// Convert a value to Sexp, with a bit mask
#[allow(non_snake_case)]
pub fn AM(s: impl Display, offs : u32) -> Sexp {
if offs == 0 {
return A(s);
}
let s = s.to_string();
let x: Result<u64, _> = FromStr::from_str(&s);
if let Ok(x) = x {
return Sexp::Atom(Atom::S(format!("{}:{}", x, offs)), Default::default());
}
let x: Result<i64, _> = FromStr::from_str(&s);
if let Ok(x) = x {
return Sexp::Atom(Atom::S(format!("{}:{}", x, offs)), Default::default());
}
let x: Result<f64, _> = FromStr::from_str(&s);
if let Ok(x) = x {
return Sexp::Atom(Atom::S(format!("{}:{}", x, offs)), Default::default());
}
if s.contains(|c: char| " \t\"\\\n\t\r".contains(c)) {
panic!("Quoted string with a bit mask!");
} else {
Sexp::Atom(Atom::S(format!("{}:{}", s, offs)), Default::default())
}
}

@ -1,5 +1,5 @@
use crsn::asm::data::{Rd, Wr}; use crsn::asm::data::{Rd, Wr};
use crsn::builtin::defs::BitSlice; use crsn::builtin::defs::BitMask;
/// A low level instruction /// A low level instruction
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
@ -20,11 +20,11 @@ pub enum ArithOp {
/// Reverse bits /// Reverse bits
Rbit { dst: Wr, src: Rd }, Rbit { dst: Wr, src: Rd },
/// Count leading zeros in a slice /// Count leading zeros in a slice
Clz { dst: Wr, src: Rd, slice: BitSlice }, Clz { dst: Wr, src: Rd, mask: BitMask },
/// Count leading ones in a slice /// Count leading ones in a slice
Clo { dst: Wr, src: Rd, slice: BitSlice }, Clo { dst: Wr, src: Rd, mask: BitMask },
/// Sign extend a slice (zero aligned) /// Sign extend a slice (zero aligned)
SignExtend { dst: Wr, src: Rd, slice: BitSlice }, SignExtend { dst: Wr, src: Rd, mask: BitMask },
Add { dst: Wr, a: Rd, b: Rd }, Add { dst: Wr, a: Rd, b: Rd },
Sub { dst: Wr, a: Rd, b: Rd }, Sub { dst: Wr, a: Rd, b: Rd },

@ -250,19 +250,19 @@ impl OpTrait for ArithOp {
let res = val.reverse_bits(); let res = val.reverse_bits();
state.write(dst, res)?; state.write(dst, res)?;
} }
ArithOp::Clz { dst, src, slice } => { ArithOp::Clz { dst, src, mask: slice } => {
state.clear_status(); state.clear_status();
let mut val = ((state.read(src)? >> slice.src_pos) << (64 - slice.width)); let mut val = (state.read(src)? >> slice.src_pos) << (64 - slice.width);
if !slice.is_full() { if !slice.is_full() {
val |= ((1 << slice.width) - 1); val |= (1 << slice.width) - 1;
} }
let res = val.leading_zeros() as u64; let res = val.leading_zeros() as u64;
state.update_status(res); state.update_status(res);
state.write(dst, res)?; state.write(dst, res)?;
} }
ArithOp::Clo { dst, src, slice } => { ArithOp::Clo { dst, src, mask: slice } => {
state.clear_status(); state.clear_status();
let mut val = ((state.read(src)? >> slice.src_pos) << (64 - slice.width)); let mut val = (state.read(src)? >> slice.src_pos) << (64 - slice.width);
if !slice.is_full() { if !slice.is_full() {
val &= ((1 << slice.width) - 1) << slice.src_pos; val &= ((1 << slice.width) - 1) << slice.src_pos;
} }
@ -270,7 +270,7 @@ impl OpTrait for ArithOp {
state.update_status(res); state.update_status(res);
state.write(dst, res)?; state.write(dst, res)?;
} }
ArithOp::SignExtend { dst, src, slice } => { ArithOp::SignExtend { dst, src, mask: slice } => {
state.clear_status(); state.clear_status();
let val = state.read(src)?; let val = state.read(src)?;
@ -330,9 +330,9 @@ impl OpTrait for ArithOp {
ArithOp::Sw8 { dst, src } => to_sexp_1_or_2("sw8", dst, src), ArithOp::Sw8 { dst, src } => to_sexp_1_or_2("sw8", dst, src),
ArithOp::Rev { dst, src } => to_sexp_1_or_2("rev", dst, src), ArithOp::Rev { dst, src } => to_sexp_1_or_2("rev", dst, src),
ArithOp::Rbit { dst, src } => to_sexp_1_or_2("rbit", dst, src), ArithOp::Rbit { dst, src } => to_sexp_1_or_2("rbit", dst, src),
ArithOp::Clz { dst, src, slice } => to_sexp_1_or_2(&format!("clz{}", slice), dst, src), ArithOp::Clz { dst, src, mask: slice } => to_sexp_1_or_2(&format!("clz{}", slice), dst, src),
ArithOp::Clo { dst, src, slice } => to_sexp_1_or_2(&format!("clo{}", slice), dst, src), ArithOp::Clo { dst, src, mask: slice } => to_sexp_1_or_2(&format!("clo{}", slice), dst, src),
ArithOp::SignExtend { dst, src, slice } => to_sexp_1_or_2(&format!("se{}", slice), dst, src), ArithOp::SignExtend { dst, src, mask: slice } => to_sexp_1_or_2(&format!("se{}", slice), dst, src),
} }
} }
} }

@ -1,4 +1,4 @@
use crsn::asm::data::{Rd, Wr}; use crsn::asm::data::{Rd, Wr, DataDispEquals};
use crsn::asm::error::CrsnError; use crsn::asm::error::CrsnError;
use crsn::asm::instr::op::OpKind; use crsn::asm::instr::op::OpKind;
use crsn::asm::parse::arg_parser::TokenParser; use crsn::asm::parse::arg_parser::TokenParser;
@ -6,7 +6,7 @@ use crsn::module::ParseRes;
use crsn::sexp::SourcePosition; use crsn::sexp::SourcePosition;
use crate::defs::ArithOp; use crate::defs::ArithOp;
use crsn::builtin::defs::BitSlice;
pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> { pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
Ok(ParseRes::ext(match keyword { Ok(ParseRes::ext(match keyword {
@ -574,58 +574,43 @@ pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenP
} }
other => { other => {
if let Some(s) = other.strip_prefix("clz") { if let Some((dst, src, mask)) = args.parse_masked_wr_rd(other, "clz")? {
if let Some(slice) = BitSlice::parse2(s, op_pos)? { if !dst.disp_equals(src) && mask.dst_pos != 0 {
return Ok(ParseRes::ext(match args.len() { return Err(CrsnError::Parse(
2 => { "Invalid offsets - permissible formats are (RW:src_offs) and (Wr Rd:src_offs) ".into(),
ArithOp::Clz { dst: args.next_wr()?, src: args.next_rd()?, slice } op_pos.clone()));
} }
1 => { return Ok(ParseRes::ext(ArithOp::Clz {
let dst = args.next_rdwr()?; dst,
ArithOp::Clz { dst: dst.wr(), src: dst.rd(), slice } src,
} mask
_ => { }));
return Err(CrsnError::Parse("Clz requires 1 or 2 arguments".into(), op_pos.clone()));
}
}));
}
} }
if let Some(s) = other.strip_prefix("clo") { if let Some((dst, src, mask)) = args.parse_masked_wr_rd(other, "clo")? {
if let Some(slice) = BitSlice::parse2(s, op_pos)? { if !dst.disp_equals(src) && mask.dst_pos != 0 {
return Ok(ParseRes::ext(match args.len() { return Err(CrsnError::Parse(
2 => { "Invalid offsets - permissible formats are (RW:src_offs) and (Wr Rd:src_offs)".into(),
ArithOp::Clo { dst: args.next_wr()?, src: args.next_rd()?, slice } op_pos.clone()));
} }
1 => { return Ok(ParseRes::ext(ArithOp::Clo {
let dst = args.next_rdwr()?; dst,
ArithOp::Clo { dst: dst.wr(), src: dst.rd(), slice } src,
} mask
_ => { }));
return Err(CrsnError::Parse("Clz requires 1 or 2 arguments".into(), op_pos.clone()));
}
}));
}
} }
if let Some(s) = other.strip_prefix("se") { if let Some((dst, src, mask)) = args.parse_masked_wr_rd(other, "se")? {
if let Some(slice) = BitSlice::parse1(s, op_pos)? { if mask.src_pos != 0 || mask.dst_pos != 0 {
if slice.is_full() { return Err(CrsnError::Parse(
return Err(CrsnError::Parse("Sign extend requires a bit size (< 64)".into(), op_pos.clone())); "Offsets do not make sense for SignExtend".into(),
} op_pos.clone()));
return Ok(ParseRes::ext(match args.len() { }
2 => { return Ok(ParseRes::ext(ArithOp::SignExtend {
ArithOp::SignExtend { dst: args.next_wr()?, src: args.next_rd()?, slice } dst,
} src,
1 => { mask
let dst = args.next_rdwr()?; }));
ArithOp::SignExtend { dst: dst.wr(), src: dst.rd(), slice }
}
_ => {
return Err(CrsnError::Parse("Sign extend requires 1 or 2 arguments".into(), op_pos.clone()));
}
}));
}
} }
return Ok(ParseRes::Unknown(args)); return Ok(ParseRes::Unknown(args));

@ -104,16 +104,16 @@
(proc rd4 (proc rd4
; Read 4 bytes as little endian ; Read 4 bytes as little endian
(ld8 r0 @cin_r) (ld8 r0 @cin_r)
(ld8/8 r0 @cin_r) (ld8 r0:8 @cin_r)
(ld8/16 r0 @cin_r) (ld8 r0:16 @cin_r)
(ld8/24 r0 @cin_r) (ld8 r0:24 @cin_r)
(ret r0) (ret r0)
) )
(proc rd2 (proc rd2
; Read 2 bytes as little endian ; Read 2 bytes as little endian
(ld8 r0 @cin_r) (ld8 r0 @cin_r)
(ld8/8 r0 @cin_r) (ld8 r0:8 @cin_r)
(ret r0) (ret r0)
) )
) )

@ -26,10 +26,10 @@
(clz32 r0 0xFF) (clz32 r0 0xFF)
(cmp r0 24 (ne? (fault "9"))) (cmp r0 24 (ne? (fault "9")))
(clz32/16 r0 0x00ff_0000_0000) (clz32 r0 0x00ff_0000_0000:16)
(cmp r0 8 (ne? (fault "10"))) (cmp r0 8 (ne? (fault "10")))
(clz32/16 r0 0x0000_0000_0000) (clz32 r0 0x0000_0000_0000:16)
(cmp r0 32 (ne? (fault "11"))) (cmp r0 32 (ne? (fault "11")))
(clo r0 0) (clo r0 0)

@ -12,11 +12,15 @@
(cmp r0 0x11223344_5566778d (ne? (fault "3"))) (cmp r0 0x11223344_5566778d (ne? (fault "3")))
(ld r0 0x11223344_55667788) (ld r0 0x11223344_55667788)
(ld32/32 r0 0xaabbccdd) (ld32 r0:32 0xaabbccdd)
(cmp r0 0xaabbccdd_55667788 (ne? (fault "4"))) (cmp r0 0xaabbccdd_55667788 (ne? (fault "4")))
; ldX supports number literals, and can take 3 arguments to combine 2 and place the result in the 3rd
(ld24 r0 0xf0000000_0000abcd:32 0x77aabbcc)
(cmp r0 0xf0aabbcc_0000abcd (ne? (fault "4b")))
(ld r0 0x11223344_55667788) (ld r0 0x11223344_55667788)
(ld32/16/32 r0 0xaabbccdd_00000000) (ld32 r0:16 0xaabbccdd_00000000:32)
(cmp r0 0x1122aabb_ccdd7788 (ne? (fault "5"))) (cmp r0 0x1122aabb_ccdd7788 (ne? (fault "5")))
(ld r0 0x11223344_55667788) (ld r0 0x11223344_55667788)
@ -27,13 +31,13 @@
(ld r0 0x11223344_55667788) (ld r0 0x11223344_55667788)
(ld r1 0xaabbccdd_eeff9900) (ld r1 0xaabbccdd_eeff9900)
(xch16/32 r0 r1) (xch16 r0:32 r1)
(cmp r0 0x11229900_55667788 (ne? (fault "7"))) (cmp r0 0x11229900_55667788 (ne? (fault "7")))
(cmp r1 0xaabbccdd_eeff3344 (ne? (fault "7"))) (cmp r1 0xaabbccdd_eeff3344 (ne? (fault "7")))
(ld r0 0x11223344_55667788) (ld r0 0x11223344_55667788)
(ld r1 0xaabbccdd_eeff9900) (ld r1 0xaabbccdd_eeff9900)
(xch16/32/16 r0 r1) (xch16 r0:32 r1:16)
(cmp r0 0x1122eeff_55667788 (ne? (fault "7"))) (cmp r0 0x1122eeff_55667788 (ne? (fault "7")))
(cmp r1 0xaabbccdd_33449900 (ne? (fault "7"))) (cmp r1 0xaabbccdd_33449900 (ne? (fault "7")))
) )

@ -34,6 +34,8 @@ struct Config {
program_file: String, program_file: String,
#[serde(skip)] #[serde(skip)]
assemble_only: bool, assemble_only: bool,
#[serde(skip)]
assemble_debug: bool,
#[serde(with = "serde_duration_millis")] #[serde(with = "serde_duration_millis")]
cycle_time: Duration, cycle_time: Duration,
} }
@ -47,6 +49,7 @@ impl Default for Config {
}, },
program_file: "".to_string(), program_file: "".to_string(),
assemble_only: false, assemble_only: false,
assemble_debug: false,
cycle_time: Duration::default(), cycle_time: Duration::default(),
} }
} }
@ -88,6 +91,12 @@ impl AppConfig for Config {
.long("asm") .long("asm")
.help("Only assemble, do not run."), .help("Only assemble, do not run."),
) )
.arg(
clap::Arg::with_name("asm-debug")
.short("D")
.long("asm-debug")
.help("Only assemble, do not run. Print the result in debug format."),
)
.arg( .arg(
clap::Arg::with_name("cycle") clap::Arg::with_name("cycle")
.long("cycle") .long("cycle")
@ -100,7 +109,8 @@ impl AppConfig for Config {
fn configure(mut self, clap: &ArgMatches) -> anyhow::Result<Self> { fn configure(mut self, clap: &ArgMatches) -> anyhow::Result<Self> {
self.program_file = clap.value_of("input").unwrap().to_string(); self.program_file = clap.value_of("input").unwrap().to_string();
self.assemble_only = clap.is_present("asm-only"); self.assemble_debug = clap.is_present("asm-debug");
self.assemble_only = self.assemble_debug || clap.is_present("asm-only");
if let Some(t) = clap.value_of("cycle") { if let Some(t) = clap.value_of("cycle") {
let (t, mul) = if t.ends_with("us") { let (t, mul) = if t.ends_with("us") {
(&t[..(t.len()-2)], 1) (&t[..(t.len()-2)], 1)
@ -142,7 +152,11 @@ fn main() -> anyhow::Result<()> {
if config.assemble_only { if config.assemble_only {
for (n, op) in parsed.ops.iter().enumerate() { for (n, op) in parsed.ops.iter().enumerate() {
println!("{:04} : {}", n, op.to_sexp()); if config.assemble_debug {
println!("{:04} : {:?}", n, op);
} else {
println!("{:04} : {}", n, op.to_sexp());
}
} }
return Ok(()); return Ok(());
} else { } else {

Loading…
Cancel
Save