diff --git a/README.md b/README.md index 6c2ebb7..fc2dcc7 100644 --- a/README.md +++ b/README.md @@ -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. 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 @@ -314,29 +318,13 @@ Jumping to a label is always safer than a manual skip. ; Copy a value (ld Wr Rd) -; Copy lower XX bits (the rest is untouched) -(ldXX RW Rd) - -; Copy XX bits to bit YY and up -; 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 lower XX bits (the rest is untouched). +; Offsets can be specified to work with arbitrary bit slices +(ldXX RW:dst_offset Rd:src_offset) +(ldXX Wr Rd:dst_offset Rd:src_offset) ; 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. ; 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 (xch RW RW) -; Exchange bits in two registers (see ldXX/YY/ZZ for reference and diagrams) -(xchXX RW RW) -(xchXX/YY RW RW) -(xchXX/YY/ZZ RW RW) +; Exchange XX bits in two registers +; Offsets can be specified to work with arbitrary bit slices +(xchXX RW:offset RW:offset) ; Store status flags to a register (stf Wr) @@ -407,12 +394,12 @@ Many instructions have two forms: ; 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. -(rcmp Rd:val Rd:start Rd:end) +(rcmp Rd'val Rd'start Rd'end) ; Get a random number (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:min Rd:max) ; min to max, both are inclusive +(rng Wr Rd'max) ; 0 to max, max is inclusive +(rng Wr Rd'min Rd'max) ; min to max, both are inclusive ; Add A+B (add Wr Rd Rd) @@ -427,19 +414,19 @@ Many instructions have two forms: (mul RW Rd) ; Divide A/B -(div Wr Rd Rd:divider) -(div RW Rd:divider) +(div Wr Rd Rd'divider) +(div RW Rd'divider) ; Divide and get remainder ; Both DST and REM are output registers -(divr Wr:result Wr:remainder Rd Rd:divider) -(divr RW Wr:remainder Rd:divider) +(divr Wr'result Wr'remainder Rd Rd'divider) +(divr RW Wr'remainder Rd'divider) ; Get remainder A%B ; This is equivalent to (divr _ REM A B), ; except status flags are updated by the remainder value -(mod Wr Rd Rd:divider) -(mod RW Rd:divider) +(mod Wr Rd Rd'divider) +(mod RW Rd'divider) ; Swap the 32-bit halves of a value ; 0x01234567_89abcdef -> 0x89abcdef_01234567 @@ -468,21 +455,17 @@ Many instructions have two forms: (clz Wr Rd) (clz RW) ; Count leading zeros in the lower XX bits -(clzXX Wr Rd) -(clzXX RW) -; 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/YY Wr Rd) -(clzXX/YY RW) +; Offsets can be specified to work with arbitrary bit slices +(clzXX Wr Rd:src_offs) +(clzXX RW:src_offs) ; Count leading ones (clo Wr Rd) (clo RW) ; Count leading ones in the lower XX bits -(cloXX Wr Rd) -(cloXX RW) -; Count leading ones in the XX bits starting at YY -(cloXX/YY Wr Rd) -(cloXX/YY RW) +; Offsets can be specified to work with arbitrary bit slices +(cloXX Wr Rd:src_offs) +(cloXX RW:src_offs) ; Sign extend a XX-bit value to 64 bits, XX in range 1..63) (seXX Wr Rd) @@ -509,24 +492,24 @@ Many instructions have two forms: (ror RW Rd) ; Rotate left (wrap around) -(rol Wr Rd:value Rd:count) -(rol RW Rd:count) +(rol Wr Rd'value Rd'count) +(rol RW Rd'count) ; Logical shift right (fill with zeros) -(lsr Wr Rd Rd:count) -(lsr RW Rd:count) +(lsr Wr Rd Rd'count) +(lsr RW Rd'count) ; Logical shift left (fill with zeros) -(lsl Wr Rd Rd:count) -(lsl RW Rd:count) +(lsl Wr Rd Rd'count) +(lsl RW Rd'count) ; Arithmetic shift right (copy sign bit) -(asr Wr Rd Rd:count) -(asr RW Rd:count) +(asr Wr Rd Rd'count) +(asr RW Rd'count) ; Arithmetic shift left (this is identical to `lsl`, added for completeness) -(asl Wr Rd Rd:count) -(asl RW Rd:count) +(asl Wr Rd Rd'count) +(asl RW Rd'count) ; Delete an object by its handle. Objects are used by some extensions. (del @Rd) diff --git a/crsn/crsn-sexp/src/lib.rs b/crsn/crsn-sexp/src/lib.rs index 5a360ae..9eaf113 100644 --- a/crsn/crsn-sexp/src/lib.rs +++ b/crsn/crsn-sexp/src/lib.rs @@ -95,7 +95,7 @@ fn without_underscores(s: &str) -> Cow { } } -fn atom_of_string(s: String) -> Atom { +pub fn atom_of_string(s: String) -> Atom { if s.starts_with('#') { match u64::from_str_radix(&without_underscores(&s[1..]), 16) { Ok(u) => return Atom::U(u), diff --git a/crsn/src/asm/data/mod.rs b/crsn/src/asm/data/mod.rs index 4bda70e..0e2677e 100644 --- a/crsn/src/asm/data/mod.rs +++ b/crsn/src/asm/data/mod.rs @@ -78,6 +78,52 @@ impl From for DataDisp { } } +impl From for DataDisp { + fn from(s: Rd) -> Self { + s.0.into() + } +} + +impl From for DataDisp { + fn from(s: Wr) -> Self { + s.0.into() + } +} + +impl From 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 { + fn disp_equals>(self, other : T) -> bool; +} + +impl> DataDispEquals for T { + fn disp_equals>(self, other : X) -> bool { + self.into() == other.into() + } +} + /// Data source disposition #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum RdData { diff --git a/crsn/src/asm/instr/op.rs b/crsn/src/asm/instr/op.rs index 9a96bc2..61fdf92 100644 --- a/crsn/src/asm/instr/op.rs +++ b/crsn/src/asm/instr/op.rs @@ -18,9 +18,9 @@ pub enum OpKind { #[derive(Debug)] pub struct Op { + pub kind: OpKind, pub cond: Option, pub pos: SourcePosition, - pub kind: OpKind, } impl OpTrait for Op { diff --git a/crsn/src/asm/parse/arg_parser.rs b/crsn/src/asm/parse/arg_parser.rs index 0efeca4..fbb6192 100644 --- a/crsn/src/asm/parse/arg_parser.rs +++ b/crsn/src/asm/parse/arg_parser.rs @@ -2,10 +2,12 @@ use sexp::{Sexp, SourcePosition}; use crate::asm::data::{Rd, RdData, RdObj, Wr, RdWr}; 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::sexp_expect::expect_string_atom; use crate::asm::data::literal::Value; +use crate::builtin::defs::BitMask; +use crate::asm::patches::ErrWithPos; /// Utility for argument parsing #[derive(Debug)] @@ -21,7 +23,7 @@ impl<'a> IntoIterator for TokenParser<'a> { type IntoIter = std::vec::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. self.args.reverse(); 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 { self.len() > 0 } @@ -121,13 +133,180 @@ impl<'a> TokenParser<'a> { /// Get the next entry as write location pub fn next_rdwr(&mut self) -> Result { let next = self.next_or_err()?; - let pos = next.pos().clone(); - let wr = parse_wr(next, self.pcx)?; + parse_rdwr(next, self.pcx) + } - if !wr.is_readable() { - return Err(CrsnError::Parse("Argument is not readable!".into(), pos)); + /// Get the next entry as write location with optional :offset + 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, CrsnError> { + if let Some(s) = keyword.strip_prefix(prefix) { + let width = if s.is_empty() { + (std::mem::size_of::() 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, CrsnError> { + if let Some(s) = keyword.strip_prefix(prefix) { + let width = if s.is_empty() { + (std::mem::size_of::() 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, CrsnError> { + if let Some(s) = keyword.strip_prefix(prefix) { + let width = if s.is_empty() { + (std::mem::size_of::() 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())) + } } } diff --git a/crsn/src/asm/parse/parse_data.rs b/crsn/src/asm/parse/parse_data.rs index b522f39..27e5152 100644 --- a/crsn/src/asm/parse/parse_data.rs +++ b/crsn/src/asm/parse/parse_data.rs @@ -3,7 +3,7 @@ use std::convert::TryFrom; 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::error::CrsnError; use crate::asm::parse::ParserContext; @@ -71,9 +71,6 @@ pub fn parse_label_str(name: &str, pos: &SourcePosition) -> Result Result { // trace!("parse data: {:?}", tok); - - // TODO implement masks - match tok { Sexp::Atom(Atom::I(val), _pos) => { Ok(DataDisp::Immediate(unsafe { std::mem::transmute(val) })) @@ -94,69 +91,94 @@ pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result { - if s == "_" { - return Ok(DataDisp::Discard); - } + parse_data_disp_from_str(&s, &pos, pcx) + } + } +} - if let Some(reference) = s.strip_prefix('@') { - /* extension constants (pre-defined handles) */ - for p in pcx.parsers { - if let Some(val) = p.get_constant_value(reference) { - return Ok(DataDisp::ImmObject(val)); - } - } +/// Parse data disp from a string token (a &str is used so the string can be preprocessed) +pub fn parse_data_disp_from_str(s: &str, pos: &SourcePosition, pcx: &ParserContext) -> Result { + if s == "_" { + return Ok(DataDisp::Discard); + } - /* 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 let Some(reference) = s.strip_prefix('@') { + /* extension constants (pre-defined handles) */ + for p in pcx.parsers { + if let Some(val) = p.get_constant_value(reference) { + return Ok(DataDisp::ImmObject(val)); + } + } - 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() { - 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)); - } - } + let reg = reg::parse_reg(reference, pos)?; - /* program constants */ - let pstate = pcx.state.borrow(); - if let Some(val) = pstate.constants.get(&s) { - return Ok(DataDisp::Immediate(*val)); - } + 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::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 */ - 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)); - } + /* program constants */ + let pstate = pcx.state.borrow(); + if let Some(val) = pstate.constants.get(s) { + return Ok(DataDisp::Immediate(*val)); + } - /* register */ - let reg = reg::parse_reg(&s, &pos)?; + /* register aliases */ + 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(); - 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)) - } 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::Register(reg)) + /* It can also be a plain old number */ + + if s.starts_with('#') + || s.starts_with('-') + || s.starts_with('.') + || s.starts_with("0x") + || 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 { let pos = tok.pos().clone(); Ok(Wr::new(WrData::try_from(parse_data_disp(tok, pcx)?).err_pos(&pos)?)) } + +pub fn parse_rdwr(tok: Sexp, pcx: &ParserContext) -> Result { + 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)) + } +} diff --git a/crsn/src/builtin/defs.rs b/crsn/src/builtin/defs.rs index 3bf1b65..4681131 100644 --- a/crsn/src/builtin/defs.rs +++ b/crsn/src/builtin/defs.rs @@ -1,13 +1,13 @@ use sexp::SourcePosition; 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::OpKind; use std::fmt::{Display, Formatter}; use std::fmt; use crate::asm::error::CrsnError; -use crate::asm::patches::ErrWithPos; + #[derive(Debug)] pub enum Barrier { @@ -41,14 +41,20 @@ pub enum LdsValue { Chars(String), } +/// Instruction's bit mask #[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct BitSlice { +pub struct BitMask { + /// Operation bit width pub width: u32, + /// Destination (or first operand's) offset + pub dst_pos: u32, + /// Source (or second operand's) offset 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 { if self.is_full() { return Ok(()); @@ -65,94 +71,42 @@ impl Display for BitSlice { } } -impl Default for BitSlice { +impl Default for BitMask { fn default() -> Self { Self { width: 64, + dst_pos: 0, src_pos: 0, - dst_pos: 0 + src2_pos: 0, } } } -impl BitSlice { +impl BitMask { pub fn full() -> Self { Self::default() } - /// Parse LEN (no SRC and DST) - pub fn parse1(src: &str, pos: &SourcePosition) -> Result, CrsnError> { - match Self::parse(src, pos) { - 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, 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, CrsnError> { - if src.is_empty() { - return Ok(Some(BitSlice::full())); - } + pub fn validate(&self, pos: &SourcePosition) -> Result<(), CrsnError> { + #[allow(non_snake_case)] + let MAXWIDTH = (std::mem::size_of::() as u32)*8; - if !src.starts_with(|c: char| c.is_ascii_digit()) { - return Ok(None); - } - - // We have a opXX, opXX/YY, or opXX/YY/ZZ - let mut numbers : Vec = 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())); + if self.width == 0 || self.width > MAXWIDTH { + return Err(CrsnError::Parse(format!("Bit width must be 1-{}", MAXWIDTH).into(), pos.clone())); } // Validation - if slice.src_pos + slice.width > 64 { - return Err(CrsnError::Parse("Invalid source bit slice".into(), pos.clone())); + if self.src_pos + self.width > MAXWIDTH { + 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 { - return Err(CrsnError::Parse("Invalid destination bit slice".into(), pos.clone())); + if self.dst_pos + self.width > MAXWIDTH { + return Err(CrsnError::Parse("Invalid destination bit mask".into(), pos.clone())); } - Ok(Some(slice)) + Ok(()) } pub fn is_full(self) -> bool { @@ -203,13 +157,13 @@ pub enum BuiltinOp { /// Move a value Load { dst: Wr, src: Rd }, /// Move bits of a value - LoadBits { dst: RdWr, src: Rd, slice: BitSlice }, + LoadBits { dst: Wr, a: Rd, b: Rd, mask: BitMask }, /// Move N values LoadMultiple { dst: Wr, src: Rd, count: Rd }, /// Move values from a string or integer list LoadSequence { dst: Wr, value: LdsValue }, /// Swap two registers - Exchange { a: RdWr, b: RdWr, slice : BitSlice }, + Exchange { a: RdWr, b: RdWr, mask: BitMask }, /// Store runtime status to a register StoreFlags { dst: Wr }, /// Load runtime status from a register diff --git a/crsn/src/builtin/exec.rs b/crsn/src/builtin/exec.rs index bad3117..80eaab8 100644 --- a/crsn/src/builtin/exec.rs +++ b/crsn/src/builtin/exec.rs @@ -119,12 +119,12 @@ impl OpTrait for BuiltinOp { } state.update_status(last); } - BuiltinOp::LoadBits { dst, src, slice, } => { + BuiltinOp::LoadBits { dst, a, b, mask: slice, } => { state.clear_status(); - let new = state.read(src)?; - let old = state.read(dst)?; + let new = state.read(b)?; + let old = state.read(a)?; 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.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 bb = state.read(b)?; diff --git a/crsn/src/builtin/parse.rs b/crsn/src/builtin/parse.rs index 9342ec3..1e9fd18 100644 --- a/crsn/src/builtin/parse.rs +++ b/crsn/src/builtin/parse.rs @@ -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::sexp_expect::{expect_any_string_atom}; 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::utils::A; -use crate::asm::data::{Rd, RdData, RdObj}; +use crate::utils::{A, AM}; +use crate::asm::data::{Rd, RdData, RdObj, DataDispEquals}; pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { 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 } } - "xch" => { - BuiltinOp::Exchange { - a: args.next_rdwr()?, - b: args.next_rdwr()?, - slice: BitSlice::full(), - } - } + // "xch" => { + // BuiltinOp::Exchange { + // a: args.next_rdwr()?, + // b: args.next_rdwr()?, + // mask: BitMask::full(), + // } + // } "stf" => { BuiltinOp::StoreFlags { @@ -276,24 +276,17 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok } other => { - if let Some(s) = other.strip_prefix("ld") { - if let Some(slice) = BitSlice::parse(s, op_pos)? { - return Ok(ParseRes::builtin(BuiltinOp::LoadBits { - dst: args.next_rdwr()?, - src: args.next_rd()?, - slice, - })); + if let Some((dst, a, b, mask)) = args.parse_masked_wr_rd_rd(other, "ld")? { + if !dst.disp_equals(a) && mask.dst_pos != 0 { + return Err(CrsnError::Parse( + "Invalid offsets - permissible formats are (RW:dst_offs Rd:src_offs) and (Wr Rd:dst_offs, Rd:src_offs) ".into(), + op_pos.clone())); } + return Ok(ParseRes::builtin(BuiltinOp::LoadBits { dst, a, b, mask })); } - if let Some(s) = other.strip_prefix("xch") { - if let Some(slice) = BitSlice::parse(s, op_pos)? { - return Ok(ParseRes::builtin(BuiltinOp::Exchange { - a: args.next_rdwr()?, - b: args.next_rdwr()?, - slice, - })); - } + if let Some((a, b, mask)) = args.parse_masked_rdwr_rdwr(other, "xch")? { + return Ok(ParseRes::builtin(BuiltinOp::Exchange { a, b, mask })); } if other.starts_with(':') { @@ -381,11 +374,21 @@ pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp { BuiltinOp::Load { dst, src, } => { sexp::list(&[A("ld"), A(dst), A(src)]) }, - BuiltinOp::LoadBits { dst, src, slice, } => { - sexp::list(&[A(format!("ld{}", slice)), A(dst), A(src)]) + BuiltinOp::LoadBits { dst, a, b, mask, } => { + 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::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::LoadFlags { src } => sexp::list(&[A("ldf"), A(src)]), BuiltinOp::LoadSequence { dst, value } => { @@ -470,14 +473,15 @@ mod test { ("(fault \"do pr*ele\")", "(fault \"do pr*ele\")"), ("(xch r0 r1)", "(xch 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)"), ("(ld8 r0 r1)", "(ld8 r0 r1)"), ("(ld16 r0 r1)", "(ld16 r0 r1)"), ("(ld32 r0 r1)", "(ld32 r0 r1)"), - ("(ld32/32 r0 r1)", "(ld32/32 r0 r1)"), - ("(ld32/32/5 r0 r1)", "(ld32/32/5 r0 r1)"), - ("(ld32/0/16 r0 r1)", "(ld32/0/16 r0 r1)"), + ("(ld32 r0:32 r1)", "(ld32 r0:32 r1)"), + ("(ld32 r0:32 r1:5)", "(ld32 r0:32 r1:5)"), + ("(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 _ -32767)", "(ld _ -32767)"), ("(ldn _ @r0 7)", "(ldn _ @r0 7)"), @@ -512,7 +516,7 @@ mod test { .expect("flatten").remove(0); let exported = parsed.to_sexp().to_string(); - println!("Parsed: {}", exported); + println!("Parsed: {}, dbg {:?}", exported, parsed); assert_eq!(expected, exported); diff --git a/crsn/src/runtime/run_thread.rs b/crsn/src/runtime/run_thread.rs index f34a946..cb04b63 100644 --- a/crsn/src/runtime/run_thread.rs +++ b/crsn/src/runtime/run_thread.rs @@ -87,6 +87,7 @@ impl RunThread { } Err(e) => { error!("Fault: {:?}", e); + error!("Core dump: {:?}", self.state); break 'run; } } diff --git a/crsn/src/runtime/run_thread/state.rs b/crsn/src/runtime/run_thread/state.rs index 7e286f0..3022e82 100644 --- a/crsn/src/runtime/run_thread/state.rs +++ b/crsn/src/runtime/run_thread/state.rs @@ -10,6 +10,8 @@ use std::sync::Arc; use crate::runtime::run_thread::ThreadInfo; use nudge::{likely}; use crate::asm::instr::cond::Flag; +use std::fmt::{Debug, Formatter}; +use std::fmt; pub struct RunState { pub thread_info: Arc, @@ -23,6 +25,17 @@ pub struct RunState { 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)] pub struct ExtensionDataStore { store: HashMap> diff --git a/crsn/src/utils/mod.rs b/crsn/src/utils/mod.rs index 95765c8..ec95cf1 100644 --- a/crsn/src/utils/mod.rs +++ b/crsn/src/utils/mod.rs @@ -4,9 +4,10 @@ use std::str::FromStr; pub use option_ext::UncheckedOptionExt; use sexp::{Atom, Sexp}; + mod option_ext; -/// Convert a string token to Sexp +/// Convert a value to Sexp #[allow(non_snake_case)] pub fn A(s: impl Display) -> Sexp { let s = s.to_string(); @@ -21,9 +22,9 @@ pub fn A(s: impl Display) -> Sexp { return Sexp::Atom(Atom::I(x), Default::default()); } - let y: Result = FromStr::from_str(&s); - if let Ok(y) = y { - return Sexp::Atom(Atom::F(y), Default::default()); + let x: Result = FromStr::from_str(&s); + if let Ok(x) = x { + return Sexp::Atom(Atom::F(x), Default::default()); } 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()) } } +/// 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 = FromStr::from_str(&s); + if let Ok(x) = x { + return Sexp::Atom(Atom::S(format!("{}:{}", x, offs)), Default::default()); + } + + let x: Result = FromStr::from_str(&s); + if let Ok(x) = x { + return Sexp::Atom(Atom::S(format!("{}:{}", x, offs)), Default::default()); + } + + let x: Result = 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()) + } +} diff --git a/crsn_arith/src/defs.rs b/crsn_arith/src/defs.rs index e15ecce..3e96dbb 100644 --- a/crsn_arith/src/defs.rs +++ b/crsn_arith/src/defs.rs @@ -1,5 +1,5 @@ use crsn::asm::data::{Rd, Wr}; -use crsn::builtin::defs::BitSlice; +use crsn::builtin::defs::BitMask; /// A low level instruction #[derive(Clone, Debug, Eq, PartialEq)] @@ -20,11 +20,11 @@ pub enum ArithOp { /// Reverse bits Rbit { dst: Wr, src: Rd }, /// 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 - Clo { dst: Wr, src: Rd, slice: BitSlice }, + Clo { dst: Wr, src: Rd, mask: BitMask }, /// 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 }, Sub { dst: Wr, a: Rd, b: Rd }, diff --git a/crsn_arith/src/exec.rs b/crsn_arith/src/exec.rs index 967812a..c2f3966 100644 --- a/crsn_arith/src/exec.rs +++ b/crsn_arith/src/exec.rs @@ -250,19 +250,19 @@ impl OpTrait for ArithOp { let res = val.reverse_bits(); state.write(dst, res)?; } - ArithOp::Clz { dst, src, slice } => { + ArithOp::Clz { dst, src, mask: slice } => { 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() { - val |= ((1 << slice.width) - 1); + val |= (1 << slice.width) - 1; } let res = val.leading_zeros() as u64; state.update_status(res); state.write(dst, res)?; } - ArithOp::Clo { dst, src, slice } => { + ArithOp::Clo { dst, src, mask: slice } => { 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() { val &= ((1 << slice.width) - 1) << slice.src_pos; } @@ -270,7 +270,7 @@ impl OpTrait for ArithOp { state.update_status(res); state.write(dst, res)?; } - ArithOp::SignExtend { dst, src, slice } => { + ArithOp::SignExtend { dst, src, mask: slice } => { state.clear_status(); 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::Rev { dst, src } => to_sexp_1_or_2("rev", 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::Clo { dst, src, 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::Clz { dst, src, mask: slice } => to_sexp_1_or_2(&format!("clz{}", slice), dst, src), + ArithOp::Clo { dst, src, mask: slice } => to_sexp_1_or_2(&format!("clo{}", slice), dst, src), + ArithOp::SignExtend { dst, src, mask: slice } => to_sexp_1_or_2(&format!("se{}", slice), dst, src), } } } diff --git a/crsn_arith/src/parse.rs b/crsn_arith/src/parse.rs index 8501dfe..99d9d2a 100644 --- a/crsn_arith/src/parse.rs +++ b/crsn_arith/src/parse.rs @@ -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::instr::op::OpKind; use crsn::asm::parse::arg_parser::TokenParser; @@ -6,7 +6,7 @@ use crsn::module::ParseRes; use crsn::sexp::SourcePosition; use crate::defs::ArithOp; -use crsn::builtin::defs::BitSlice; + pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { Ok(ParseRes::ext(match keyword { @@ -574,58 +574,43 @@ pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenP } other => { - if let Some(s) = other.strip_prefix("clz") { - if let Some(slice) = BitSlice::parse2(s, op_pos)? { - return Ok(ParseRes::ext(match args.len() { - 2 => { - ArithOp::Clz { dst: args.next_wr()?, src: args.next_rd()?, slice } - } - 1 => { - let dst = args.next_rdwr()?; - ArithOp::Clz { dst: dst.wr(), src: dst.rd(), slice } - } - _ => { - return Err(CrsnError::Parse("Clz requires 1 or 2 arguments".into(), op_pos.clone())); - } - })); - } + if let Some((dst, src, mask)) = args.parse_masked_wr_rd(other, "clz")? { + if !dst.disp_equals(src) && mask.dst_pos != 0 { + return Err(CrsnError::Parse( + "Invalid offsets - permissible formats are (RW:src_offs) and (Wr Rd:src_offs) ".into(), + op_pos.clone())); + } + return Ok(ParseRes::ext(ArithOp::Clz { + dst, + src, + mask + })); } - if let Some(s) = other.strip_prefix("clo") { - if let Some(slice) = BitSlice::parse2(s, op_pos)? { - return Ok(ParseRes::ext(match args.len() { - 2 => { - ArithOp::Clo { dst: args.next_wr()?, src: args.next_rd()?, slice } - } - 1 => { - let dst = args.next_rdwr()?; - ArithOp::Clo { dst: dst.wr(), src: dst.rd(), slice } - } - _ => { - return Err(CrsnError::Parse("Clz requires 1 or 2 arguments".into(), op_pos.clone())); - } - })); - } + if let Some((dst, src, mask)) = args.parse_masked_wr_rd(other, "clo")? { + if !dst.disp_equals(src) && mask.dst_pos != 0 { + return Err(CrsnError::Parse( + "Invalid offsets - permissible formats are (RW:src_offs) and (Wr Rd:src_offs)".into(), + op_pos.clone())); + } + return Ok(ParseRes::ext(ArithOp::Clo { + dst, + src, + mask + })); } - if let Some(s) = other.strip_prefix("se") { - if let Some(slice) = BitSlice::parse1(s, op_pos)? { - if slice.is_full() { - return Err(CrsnError::Parse("Sign extend requires a bit size (< 64)".into(), op_pos.clone())); - } - return Ok(ParseRes::ext(match args.len() { - 2 => { - ArithOp::SignExtend { dst: args.next_wr()?, src: args.next_rd()?, slice } - } - 1 => { - 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())); - } - })); - } + if let Some((dst, src, mask)) = args.parse_masked_wr_rd(other, "se")? { + if mask.src_pos != 0 || mask.dst_pos != 0 { + return Err(CrsnError::Parse( + "Offsets do not make sense for SignExtend".into(), + op_pos.clone())); + } + return Ok(ParseRes::ext(ArithOp::SignExtend { + dst, + src, + mask + })); } return Ok(ParseRes::Unknown(args)); diff --git a/examples/bmp-parser.csn b/examples/bmp-parser.csn index 2d173fe..0f09bbe 100644 --- a/examples/bmp-parser.csn +++ b/examples/bmp-parser.csn @@ -104,16 +104,16 @@ (proc rd4 ; Read 4 bytes as little endian (ld8 r0 @cin_r) - (ld8/8 r0 @cin_r) - (ld8/16 r0 @cin_r) - (ld8/24 r0 @cin_r) + (ld8 r0:8 @cin_r) + (ld8 r0:16 @cin_r) + (ld8 r0:24 @cin_r) (ret r0) ) (proc rd2 ; Read 2 bytes as little endian (ld8 r0 @cin_r) - (ld8/8 r0 @cin_r) + (ld8 r0:8 @cin_r) (ret r0) ) ) diff --git a/examples/test_clz_clo_se.csn b/examples/test_clz_clo_se.csn index 8f336a3..6465b6d 100644 --- a/examples/test_clz_clo_se.csn +++ b/examples/test_clz_clo_se.csn @@ -26,10 +26,10 @@ (clz32 r0 0xFF) (cmp r0 24 (ne? (fault "9"))) - (clz32/16 r0 0x00ff_0000_0000) + (clz32 r0 0x00ff_0000_0000:16) (cmp r0 8 (ne? (fault "10"))) - (clz32/16 r0 0x0000_0000_0000) + (clz32 r0 0x0000_0000_0000:16) (cmp r0 32 (ne? (fault "11"))) (clo r0 0) diff --git a/examples/test_ldbits.csn b/examples/test_ldbits_xchbits.csn similarity index 75% rename from examples/test_ldbits.csn rename to examples/test_ldbits_xchbits.csn index 67e9425..e5ba239 100644 --- a/examples/test_ldbits.csn +++ b/examples/test_ldbits_xchbits.csn @@ -12,11 +12,15 @@ (cmp r0 0x11223344_5566778d (ne? (fault "3"))) (ld r0 0x11223344_55667788) - (ld32/32 r0 0xaabbccdd) + (ld32 r0:32 0xaabbccdd) (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) - (ld32/16/32 r0 0xaabbccdd_00000000) + (ld32 r0:16 0xaabbccdd_00000000:32) (cmp r0 0x1122aabb_ccdd7788 (ne? (fault "5"))) (ld r0 0x11223344_55667788) @@ -27,13 +31,13 @@ (ld r0 0x11223344_55667788) (ld r1 0xaabbccdd_eeff9900) - (xch16/32 r0 r1) + (xch16 r0:32 r1) (cmp r0 0x11229900_55667788 (ne? (fault "7"))) (cmp r1 0xaabbccdd_eeff3344 (ne? (fault "7"))) (ld r0 0x11223344_55667788) (ld r1 0xaabbccdd_eeff9900) - (xch16/32/16 r0 r1) + (xch16 r0:32 r1:16) (cmp r0 0x1122eeff_55667788 (ne? (fault "7"))) (cmp r1 0xaabbccdd_33449900 (ne? (fault "7"))) ) diff --git a/launcher/src/main.rs b/launcher/src/main.rs index ea0a278..c8e8fa0 100644 --- a/launcher/src/main.rs +++ b/launcher/src/main.rs @@ -34,6 +34,8 @@ struct Config { program_file: String, #[serde(skip)] assemble_only: bool, + #[serde(skip)] + assemble_debug: bool, #[serde(with = "serde_duration_millis")] cycle_time: Duration, } @@ -47,6 +49,7 @@ impl Default for Config { }, program_file: "".to_string(), assemble_only: false, + assemble_debug: false, cycle_time: Duration::default(), } } @@ -88,6 +91,12 @@ impl AppConfig for Config { .long("asm") .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( clap::Arg::with_name("cycle") .long("cycle") @@ -100,7 +109,8 @@ impl AppConfig for Config { fn configure(mut self, clap: &ArgMatches) -> anyhow::Result { 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") { let (t, mul) = if t.ends_with("us") { (&t[..(t.len()-2)], 1) @@ -142,7 +152,11 @@ fn main() -> anyhow::Result<()> { if config.assemble_only { 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(()); } else {