implement bit copy and swap with any size and offset using a special new syntax

pull/21/head
Ondřej Hruška 4 years ago
parent 2d4f6b4af1
commit e0e45bd223
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 35
      README.md
  2. 86
      crsn/src/builtin/defs.rs
  3. 32
      crsn/src/builtin/exec.rs
  4. 69
      crsn/src/builtin/parse.rs
  5. 7
      crsn/src/module/mod.rs
  6. 39
      examples/ldbits.csn

@ -314,17 +314,29 @@ Jumping to a label is always safer than a manual skip.
; Copy a value ; Copy a value
(ld Wr Rd) (ld Wr Rd)
; Copy a value N times. This is useful when used with stream handles or buffers. ; Copy lower XX bits (the rest is untouched)
(ldn Wr Rd Rd:count) (ldXX RW Rd)
; Copy 32 bits of a value ; Copy XX bits to bit YY and up
(ld32 Wr Rd) ; 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 16 bits of a value
(ld16 Wr Rd)
; Copy 8 bits of a value ; Copy a value N times. This is useful when used with stream handles or buffers.
(ld8 Wr Rd) (ldn Wr Rd 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.
@ -339,6 +351,11 @@ 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)
(xchXX RW RW)
(xchXX/YY RW RW)
(xchXX/YY/ZZ RW RW)
; Store status flags to a register ; Store status flags to a register
(stf Wr) (stf Wr)

@ -4,6 +4,10 @@ use crate::asm::data::{Rd, RdObj, Wr, RdWr};
use crate::asm::data::literal::{DebugMsg, Label, RoutineName}; use crate::asm::data::literal::{DebugMsg, Label, RoutineName};
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;
use crate::asm::error::CrsnError;
use crate::asm::patches::ErrWithPos;
#[derive(Debug)] #[derive(Debug)]
pub enum Barrier { pub enum Barrier {
@ -37,6 +41,78 @@ pub enum LdsValue {
Chars(String), Chars(String),
} }
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct BitSlice {
pub width: u32,
pub src_pos: u32,
pub dst_pos: u32
}
impl Display for BitSlice {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if self.is_full() {
return Ok(());
}
write!(f, "{}", self.width)?;
if self.src_pos != 0 || self.dst_pos != 0 {
write!(f, "/{}", self.dst_pos)?;
}
if self.src_pos != 0 {
write!(f, "/{}", self.src_pos)?;
}
Ok(())
}
}
impl Default for BitSlice {
fn default() -> Self {
Self {
width: 64,
src_pos: 0,
dst_pos: 0
}
}
}
impl BitSlice {
pub fn full() -> Self {
Self::default()
}
pub fn parse(src: &str, pos: &SourcePosition) -> Result<Option<Self>, CrsnError> {
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<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)
};
// Validation
if slice.src_pos + slice.width > 64 {
return Err(CrsnError::Parse("Invalid source bit slice".into(), pos.clone()));
}
if slice.dst_pos + slice.width > 64 {
return Err(CrsnError::Parse("Invalid destination bit slice".into(), pos.clone()));
}
Ok(Some(slice))
}
pub fn is_full(self) -> bool {
self.width == 64
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum BuiltinOp { pub enum BuiltinOp {
/// Do nothing (costs one cycle) /// Do nothing (costs one cycle)
@ -79,18 +155,14 @@ pub enum BuiltinOp {
Delete(RdObj), Delete(RdObj),
/// Move a value /// Move a value
Load { dst: Wr, src: Rd }, Load { dst: Wr, src: Rd },
/// Move lower 32 bits /// Move bits of a value
Load32 { dst: RdWr, src: Rd }, LoadBits { dst: RdWr, src: Rd, slice: BitSlice },
/// Move lower 16 bits
Load16 { dst: RdWr, src: Rd },
/// Move lower 8 bits
Load8 { dst: RdWr, src: Rd },
/// 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 }, Exchange { a: RdWr, b: RdWr, slice : BitSlice },
/// 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,27 +119,12 @@ impl OpTrait for BuiltinOp {
} }
state.update_status(last); state.update_status(last);
} }
BuiltinOp::Load32 { dst, src } => { BuiltinOp::LoadBits { dst, src, slice, } => {
state.clear_status(); state.clear_status();
let new = state.read(src)?; let new = state.read(src)?;
let old = state.read(dst)?; let old = state.read(dst)?;
let val = (old & !0xFFFFFFFF) | (new & 0xFFFFFFFF); let ones : u64 = (1 << slice.width) - 1;
state.update_status(val); let val = (old & !(ones << slice.dst_pos)) | (((new & (ones << slice.src_pos)) >> slice.src_pos) << slice.dst_pos);
state.write(dst, val)?;
}
BuiltinOp::Load16 { dst, src } => {
state.clear_status();
let new = state.read(src)?;
let old = state.read(dst)?;
let val = (old & !0xFFFF) | (new & 0xFFFF);
state.update_status(val);
state.write(dst, val)?;
}
BuiltinOp::Load8 { dst, src } => {
state.clear_status();
let new = state.read(src)?;
let old = state.read(dst)?;
let val = (old & !0xFF) | (new & 0xFF);
state.update_status(val); state.update_status(val);
state.write(dst, val)?; state.write(dst, val)?;
} }
@ -173,11 +158,20 @@ impl OpTrait for BuiltinOp {
} }
} }
} }
BuiltinOp::Exchange { a, b } => { BuiltinOp::Exchange { a, b, slice } => {
let aa = state.read(a)?; let aa = state.read(a)?;
let bb = state.read(b)?; let bb = state.read(b)?;
if slice.is_full() {
state.write(a, bb)?; state.write(a, bb)?;
state.write(b, aa)?; state.write(b, aa)?;
} else {
let ones: u64 = (1 << slice.width) - 1;
let aa2 = (aa & !(ones << slice.dst_pos)) | (((bb & (ones << slice.src_pos)) >> slice.src_pos) << slice.dst_pos);
let bb2 = (bb & !(ones << slice.src_pos)) | (((aa & (ones << slice.dst_pos)) >> slice.dst_pos) << slice.src_pos);
state.write(a, aa2)?;
state.write(b, bb2)?;
}
} }
BuiltinOp::StoreFlags { dst } => { BuiltinOp::StoreFlags { dst } => {
let packed = state.frame.status.store(); let packed = state.frame.status.store();

@ -8,7 +8,7 @@ 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}; use crate::builtin::defs::{Barrier, BuiltinOp, SleepUnit, LdsValue, BitSlice};
use crate::module::ParseRes; use crate::module::ParseRes;
use crate::utils::A; use crate::utils::A;
use crate::asm::data::{Rd, RdData, RdObj}; use crate::asm::data::{Rd, RdData, RdObj};
@ -190,27 +190,6 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok
} }
} }
"ld32" => {
BuiltinOp::Load32 {
dst: args.next_rdwr()?,
src: args.next_rd()?,
}
}
"ld16" => {
BuiltinOp::Load16 {
dst: args.next_rdwr()?,
src: args.next_rd()?,
}
}
"ld8" => {
BuiltinOp::Load8 {
dst: args.next_rdwr()?,
src: args.next_rd()?,
}
}
"ldn" => { "ldn" => {
BuiltinOp::LoadMultiple { BuiltinOp::LoadMultiple {
dst: args.next_wr()?, dst: args.next_wr()?,
@ -263,6 +242,7 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok
BuiltinOp::Exchange { BuiltinOp::Exchange {
a: args.next_rdwr()?, a: args.next_rdwr()?,
b: args.next_rdwr()?, b: args.next_rdwr()?,
slice: BitSlice::full(),
} }
} }
@ -296,6 +276,26 @@ 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(slice) = BitSlice::parse(s, op_pos)? {
return Ok(ParseRes::builtin(BuiltinOp::LoadBits {
dst: args.next_rdwr()?,
src: args.next_rd()?,
slice,
}));
}
}
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 other.starts_with(':') { if other.starts_with(':') {
BuiltinOp::Label(parse_label_str(other, &op_pos)?) BuiltinOp::Label(parse_label_str(other, &op_pos)?)
} else { } else {
@ -378,21 +378,21 @@ pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp {
} }
} }
BuiltinOp::Delete(obj) => sexp::list(&[A("del"), A(obj)]), BuiltinOp::Delete(obj) => sexp::list(&[A("del"), A(obj)]),
BuiltinOp::Load { dst, src } => sexp::list(&[A("ld"), A(dst), A(src)]), BuiltinOp::Load { dst, src, } => {
BuiltinOp::Load32 { dst, src } => sexp::list(&[A("ld32"), A(dst), A(src)]), sexp::list(&[A("ld"), A(dst), A(src)])
BuiltinOp::Load16 { dst, src } => sexp::list(&[A("ld16"), A(dst), A(src)]), },
BuiltinOp::Load8 { dst, src } => sexp::list(&[A("ld8"), A(dst), A(src)]), BuiltinOp::LoadBits { dst, src, slice, } => {
sexp::list(&[A(format!("ld{}", slice)), A(dst), A(src)])
},
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 } => sexp::list(&[A("xch"), A(a), A(b)]), BuiltinOp::Exchange { a, b, slice } => sexp::list(&[A(format!("xch{}", slice)), A(a), A(b)]),
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 } => {
match value { match value {
LdsValue::Values(vals) => { LdsValue::Values(vals) => {
let mut vals: Vec<_> = vals.iter().map(A).collect(); let vals: Vec<_> = vals.iter().map(A).collect();
vals.insert(0, A(dst)); sexp::list(&[A("lds"), A(dst), sexp::list(&vals)])
vals.insert(0, A("lds"));
sexp::list(&vals)
} }
LdsValue::Chars(str) => { LdsValue::Chars(str) => {
sexp::list(&[A("lds"), A(dst), atom_qs(str)]) sexp::list(&[A("lds"), A(dst), atom_qs(str)])
@ -469,15 +469,20 @@ mod test {
("(fault kur*a)", "(fault kur*a)"), ("(fault kur*a)", "(fault kur*a)"),
("(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/8/16 r0 r1)", "(xch32/8/16 r0 r1)"),
("(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/32/5 r0 r1)", "(ld32/32/5 r0 r1)"),
("(ld32/0/16 r0 r1)", "(ld32/0/16 r0 r1)"),
("(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)"),
("(lds @r0 \"ahoj jak se mas\")", "(lds @r0 \"ahoj jak se mas\")"), ("(lds @r0 \"ahoj jak se mas\")", "(lds @r0 \"ahoj jak se mas\")"),
("(lds @r0 (1 2 3 4 0x123 r6))", "(lds @r0 (1 2 3 4 0x123 r6))"), ("(lds @r0 (1 2 3 4 0x123 r6))", "(lds @r0 (1 2 3 4 291 r6))"),
("(stf r0)", "(stf r0)"), ("(stf r0)", "(stf r0)"),
("(ldf r0)", "(ldf r0)"), ("(ldf r0)", "(ldf r0)"),
("(far :label)", "(far :label)"), ("(far :label)", "(far :label)"),

@ -16,6 +16,7 @@ use crate::runtime::run_thread::ThreadInfo;
use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc; use std::sync::Arc;
use crate::asm::data::Wr; use crate::asm::data::Wr;
use crate::builtin::defs::BuiltinOp;
mod eval_res; mod eval_res;
@ -29,6 +30,12 @@ pub enum ParseRes<'a, T> {
Unknown(TokenParser<'a>), Unknown(TokenParser<'a>),
} }
impl ParseRes<'static, OpKind> {
pub fn builtin(op: BuiltinOp) -> ParseRes<'static, OpKind> {
Self::Parsed(OpKind::BuiltIn(op))
}
}
impl<'a> ParseRes<'a, OpKind> { impl<'a> ParseRes<'a, OpKind> {
/// Helper to construct an extension op /// Helper to construct an extension op
pub fn ext(op: impl OpTrait) -> Self { pub fn ext(op: impl OpTrait) -> Self {

@ -0,0 +1,39 @@
(
(ld r0 0x11223344_55667788)
(ld32 r0 0xaabbccdd)
(cmp r0 0x11223344_aabbccdd (ne? (fault "1")))
(ld r0 0x11223344_55667788)
(ld8 r0 0xaabbccdd)
(cmp r0 0x11223344_556677dd (ne? (fault "2")))
(ld r0 0x11223344_55667788)
(ld4 r0 0xaabbccdd)
(cmp r0 0x11223344_5566778d (ne? (fault "3")))
(ld r0 0x11223344_55667788)
(ld32/32 r0 0xaabbccdd)
(cmp r0 0xaabbccdd_55667788 (ne? (fault "4")))
(ld r0 0x11223344_55667788)
(ld32/16/32 r0 0xaabbccdd_00000000)
(cmp r0 0x1122aabb_ccdd7788 (ne? (fault "5")))
(ld r0 0x11223344_55667788)
(ld r1 0xaabbccdd_eeff9900)
(xch32 r0 r1)
(cmp r0 0x11223344_eeff9900 (ne? (fault "6")))
(cmp r1 0xaabbccdd_55667788 (ne? (fault "6")))
(ld r0 0x11223344_55667788)
(ld r1 0xaabbccdd_eeff9900)
(xch16/32 r0 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)
(cmp r0 0x1122eeff_55667788 (ne? (fault "7")))
(cmp r1 0xaabbccdd_33449900 (ne? (fault "7")))
)
Loading…
Cancel
Save