From 68c9b336375aedff63e3dd76773258fb2dccd3b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 17 Oct 2020 23:58:24 +0200 Subject: [PATCH 1/2] basic float arith, wip float trig, abs --- crsn/src/asm/data/literal.rs | 2 +- crsn/src/asm/data/rd.rs | 21 ++- crsn/src/asm/parse/arg_parser.rs | 2 +- crsn/src/runtime/frame/status.rs | 7 + crsn/src/runtime/run_thread/state.rs | 7 + crsn_arith/src/defs.rs | 54 ++++-- crsn_arith/src/exec.rs | 185 +++++++++++++++++- crsn_arith/src/parse.rs | 269 ++++++++++++++++++++++++++- crsn_buf/src/parse.rs | 6 +- crsn_screen/src/parse.rs | 4 +- 10 files changed, 521 insertions(+), 36 deletions(-) diff --git a/crsn/src/asm/data/literal.rs b/crsn/src/asm/data/literal.rs index a202442..272484c 100644 --- a/crsn/src/asm/data/literal.rs +++ b/crsn/src/asm/data/literal.rs @@ -9,7 +9,7 @@ pub type Value = u64; #[inline(always)] pub const fn is_positive(val: Value) -> bool { - 0 == (val & 0x8000_0000_0000_0000) + 0 == (val & 0x8000_0000_0000_0000) && val != 0 } #[inline(always)] diff --git a/crsn/src/asm/data/rd.rs b/crsn/src/asm/data/rd.rs index 27a86ee..edbc875 100644 --- a/crsn/src/asm/data/rd.rs +++ b/crsn/src/asm/data/rd.rs @@ -14,11 +14,28 @@ impl Rd { pub const fn new(src: RdData) -> Self { Rd(src) } - pub const fn immediate(val: Value) -> Rd { + + pub const fn new_imm(val: Value) -> Rd { Rd(RdData::Immediate(val)) } - pub fn is_immediate_equal(self, other: Value) -> bool { + /// Construct new from float + pub fn new_float(val: f64) -> Rd { // TODO const when stabilized + Rd(RdData::Immediate(val.to_bits())) + } + + /// Convert immediate read to float + pub fn imm_to_float(self) -> Rd { + match self { + Rd(RdData::Immediate(v)) => { + let signed : i64 = unsafe { std::mem::transmute(v) }; + Rd::new_float(signed as f64) + } + other => other + } + } + + pub fn is_imm_equal(self, other: Value) -> bool { match self.0 { RdData::Immediate(val) => { val == other diff --git a/crsn/src/asm/parse/arg_parser.rs b/crsn/src/asm/parse/arg_parser.rs index 596be39..e1ba4a1 100644 --- a/crsn/src/asm/parse/arg_parser.rs +++ b/crsn/src/asm/parse/arg_parser.rs @@ -291,7 +291,7 @@ impl<'a> TokenParser<'a> { pub fn parse_wr_rd_rd_or_n(&mut self, n : Value) -> Result<(Wr, Rd, Rd), CrsnError> { if self.len() == 1 { let rw = self.next_rdwr()?; - Ok((rw.wr(), rw.rd(), Rd::immediate(n))) + Ok((rw.wr(), rw.rd(), Rd::new_imm(n))) } else if self.len() == 2 { let rw = self.next_rdwr()?; let rd = self.next_rd()?; diff --git a/crsn/src/runtime/frame/status.rs b/crsn/src/runtime/frame/status.rs index b14d8c3..0137169 100644 --- a/crsn/src/runtime/frame/status.rs +++ b/crsn/src/runtime/frame/status.rs @@ -78,6 +78,13 @@ impl StatusFlags { self.negative = is_negative(val); } + #[inline(always)] + pub fn update_float(&mut self, val: f64) { + self.zero = val == 0.0; + self.positive = val > 0.0; + self.negative = val < 0.0; + } + #[inline(always)] pub fn test(&self, cond: Cond) -> bool { match cond { diff --git a/crsn/src/runtime/run_thread/state.rs b/crsn/src/runtime/run_thread/state.rs index 3022e82..7e0f9a4 100644 --- a/crsn/src/runtime/run_thread/state.rs +++ b/crsn/src/runtime/run_thread/state.rs @@ -102,6 +102,13 @@ impl RunState { self.frame.status.update(val); } + /// Update status flags using a variable. + /// The update is additive - call `clear_status()` first if desired! + #[inline(always)] + pub fn update_status_float(&mut self, val: f64) { + self.frame.status.update_float(val); + } + /// Read object handle value pub fn read_obj(&mut self, rdo: impl Into) -> Result { rdo.into().read(self) diff --git a/crsn_arith/src/defs.rs b/crsn_arith/src/defs.rs index 3e96dbb..7bfba33 100644 --- a/crsn_arith/src/defs.rs +++ b/crsn_arith/src/defs.rs @@ -27,8 +27,10 @@ pub enum ArithOp { SignExtend { dst: Wr, src: Rd, mask: BitMask }, Add { dst: Wr, a: Rd, b: Rd }, + Abs { dst: Wr, a: Rd }, Sub { dst: Wr, a: Rd, b: Rd }, Mul { dst: Wr, a: Rd, b: Rd }, + Pow { dst: Wr, a: Rd, pow: Rd }, Div { dst: Wr, rem: Wr, a: Rd, div: Rd }, // "Mod" is functionally equivalent to "Div" with the result discarded, // but status flags are updated by the remainder @@ -46,18 +48,42 @@ pub enum ArithOp { Lsr { dst: Wr, a: Rd, n: Rd }, Asr { dst: Wr, a: Rd, n: Rd }, - // IntToFloat { dst: Wr, a: Rd }, - // FloatToInt { dst: Wr, a: Rd }, - // FloatTest { a: Rd }, - // FloatCompare { a: Rd, b: Rd }, - // FloatRangeTest { val: Rd, a: Rd, b: Rd }, - // FloatRng { dst: Wr, min: Rd, max: Rd }, - // FloatRound { dst: Wr, a: Rd, b: Rd }, - // FloatCeil { dst: Wr, a: Rd, b: Rd }, - // FloatFloor { dst: Wr, a: Rd, b: Rd }, - // FloatAdd { dst: Wr, a: Rd, b: Rd }, - // FloatSub { dst: Wr, a: Rd, b: Rd }, - // FloatMul { dst: Wr, a: Rd, b: Rd }, - // FloatDiv { dst: Wr, rem: Wr, a: Rd, div: Rd }, - // FloatMod { dst: Wr, a: Rd, div: Rd }, + IntToFloat { dst: Wr, a: Rd }, + FloatToInt { dst: Wr, a: Rd, mode: FloatToIntMode }, + FloatTest { a: Rd }, + FloatCompare { a: Rd, b: Rd }, + FloatRangeTest { val: Rd, a: Rd, b: Rd }, + FloatRng { dst: Wr, min: Rd, max: Rd }, + FloatAdd { dst: Wr, a: Rd, b: Rd }, + FloatSub { dst: Wr, a: Rd, b: Rd }, + FloatMul { dst: Wr, a: Rd, b: Rd }, + FloatPow { dst: Wr, a: Rd, pow: Rd }, + FloatRoot { dst: Wr, a: Rd, root: Rd }, + FloatHyp { dst: Wr, a: Rd, b: Rd }, + FloatDiv { dst: Wr, rem: Wr, a: Rd, div: Rd }, + FloatMod { dst: Wr, a: Rd, div: Rd }, + FloatAbs { dst: Wr, a: Rd }, + + FloatSin { dst: Wr, f: Rd }, + FloatAsin { dst: Wr, a: Rd }, + FloatCos { dst: Wr, f: Rd }, + FloatAcos { dst: Wr, a: Rd }, + FloatTan { dst: Wr, f: Rd }, + FloatAtan { dst: Wr, a: Rd }, + FloatAtan2 { dst: Wr, y: Rd, x: Rd }, + FloatCot { dst: Wr, f: Rd }, + FloatAcot { dst: Wr, a: Rd }, +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum FloatToIntMode { + Floor, + Ceil, + Round, +} + +impl Default for FloatToIntMode { + fn default() -> Self { + Self::Round + } } diff --git a/crsn_arith/src/exec.rs b/crsn_arith/src/exec.rs index c2f3966..cf4b1b8 100644 --- a/crsn_arith/src/exec.rs +++ b/crsn_arith/src/exec.rs @@ -10,14 +10,35 @@ use crsn::sexp; use crsn::sexp::Sexp; use crsn::utils::A; -use crate::defs::ArithOp; +use crate::defs::{ArithOp, FloatToIntMode}; use crsn::asm::instr::cond::Flag; use rand::Rng; +#[inline] +fn f2u(f: f64) -> u64 { + unsafe { std::mem::transmute(f) } +} + +#[inline] +fn u2f(f: u64) -> f64 { + unsafe { std::mem::transmute(f) } +} + +#[inline] +fn i2u(f: i64) -> u64 { + unsafe { std::mem::transmute(f) } +} + +#[inline] +fn u2i(f: u64) -> i64 { + unsafe { std::mem::transmute(f) } +} + impl OpTrait for ArithOp { fn execute(&self, _ti: &ThreadInfo, state: &mut RunState) -> Result { let eres = EvalRes::default(); match self { + // Integers ArithOp::Test { a } => { state.clear_status(); let res = state.read(a)?; @@ -142,6 +163,122 @@ impl OpTrait for ArithOp { state.write(dst, remainder)?; } } + + // Floating point + + ArithOp::FloatToInt { dst, a, mode } => { + state.clear_status(); + let val : f64 = u2f(state.read(a)?); + let res = i2u(match mode { + FloatToIntMode::Floor => val.floor(), + FloatToIntMode::Ceil => val.ceil(), + FloatToIntMode::Round => val.round(), + } as i64); + state.update_status(res); + state.write(dst, res)?; + } + ArithOp::IntToFloat { dst, a } => { + state.clear_status(); + let val : f64 = u2i(state.read(a)?) as f64; + state.update_status_float(val); + state.write(dst, f2u(val))?; + } + ArithOp::FloatTest { a } => { + state.clear_status(); + let res = u2f(state.read(a)?); + state.update_status_float(res); + } + ArithOp::FloatCompare { a, b } => { + state.clear_status(); + let x = u2f(state.read(a)?); + let y = u2f(state.read(b)?); + state.set_flag(Flag::Equal, x == y); + state.set_flag(Flag::Lower, x < y); + state.set_flag(Flag::Greater, x > y); + // Test flags are set when both arguments have the property + if x == y { + state.update_status_float(x); + } + } + ArithOp::FloatRangeTest { val, a, b } => { + state.clear_status(); + let val = u2f(state.read(val)?); + let a = u2f(state.read(a)?); + let b = u2f(state.read(b)?); + state.set_flag(Flag::Equal, val >= a && val <= b); + state.set_flag(Flag::Lower, val < a); + state.set_flag(Flag::Greater, val > b); + state.update_status_float(val); + } + ArithOp::FloatRng { dst, min, max } => { + state.clear_status(); + let min = u2f(state.read(min)?); + let max = u2f(state.read(max)?); + + let val = if min == max { + min + } else if min > max { + state.set_flag(Flag::Invalid, true); + min + } else { + rand::thread_rng() + .gen_range(min, max) + }; + state.write(dst, f2u(val))?; + state.update_status_float(val); + } + ArithOp::FloatAdd { dst, a, b } => { + state.clear_status(); + let x = u2f(state.read(a)?); + let y = u2f(state.read(b)?); + let res = x + y; + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatSub { dst, a, b } => { + state.clear_status(); + let x = u2f(state.read(a)?); + let y = u2f(state.read(b)?); + let res = x - y; + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatMul { dst, a, b } => { + state.clear_status(); + let x = u2f(state.read(a)?); + let y = u2f(state.read(b)?); + let res = x * y; + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatDiv { dst, rem, a, div } => { + state.clear_status(); + let x = u2f(state.read(a)?); + let d = u2f(state.read(div)?); + if d == 0.0 { + state.set_flag(Flag::Invalid, true); + } else { + let res = x / d; + let remainder = x % d; + state.update_status_float(res); + state.write(dst, f2u(res))?; + state.write(rem, f2u(remainder))?; + } + } + ArithOp::FloatMod { dst, a, div } => { + state.clear_status(); + let x = u2f(state.read(a)?); + let d = u2f(state.read(div)?); + if d == 0.0 { + state.set_flag(Flag::Invalid, true); + } else { + let rem = x % d; + state.update_status_float(rem); + state.write(dst, f2u(rem))?; + } + } + + // Bitwise ArithOp::And { dst, a, b } => { state.clear_status(); let x = state.read(a)?; @@ -282,6 +419,8 @@ impl OpTrait for ArithOp { state.update_status(res); state.write(dst, res)?; } + + _ => unimplemented!() // TODO implement float trig etc } Ok(eres) @@ -289,19 +428,19 @@ impl OpTrait for ArithOp { fn to_sexp(&self) -> Sexp { match self { - ArithOp::Add { dst, a, b } => to_sexp_2_or_3("add", dst, a, b), - ArithOp::Test { a } => sexp::list(&[A("test"), A(a)]), + ArithOp::Test { a } => sexp::list(&[A("tst"), A(a)]), ArithOp::Compare { a, b } => sexp::list(&[A("cmp"), A(a), A(b)]), ArithOp::RangeTest { val, a: start, b: end } => sexp::list(&[A("rcmp"), A(val), A(start), A(end)]), ArithOp::Rng { dst, min, max } => { - if min.is_immediate_equal(0) && max.is_immediate_equal(u64::MAX) { + if min.is_imm_equal(0) && max.is_imm_equal(u64::MAX) { sexp::list(&[A("rng"), A(dst)]) - } else if min.is_immediate_equal(0) { + } else if min.is_imm_equal(0) { sexp::list(&[A("rng"), A(dst), A(max)]) } else { sexp::list(&[A("rng"), A(dst), A(min), A(max)]) } } + ArithOp::Add { dst, a, b } => to_sexp_2_or_3("add", dst, a, b), ArithOp::Sub { dst, a, b } => to_sexp_2_or_3("sub", dst, a, b), ArithOp::Mul { dst, a, b } => to_sexp_2_or_3("mul", dst, a, b), ArithOp::Div { dst, rem, a, div } => { @@ -316,6 +455,40 @@ impl OpTrait for ArithOp { } } ArithOp::Mod { dst, a, div } => to_sexp_2_or_3("mod", dst, a, div), + + // TODO render as float + ArithOp::FloatTest { a } => sexp::list(&[A("tstf"), A(a)]), + ArithOp::FloatCompare { a, b } => sexp::list(&[A("cmpf"), A(a), A(b)]), + ArithOp::FloatRangeTest { val, a: start, b: end } => sexp::list(&[A("rcmpf"), A(val), A(start), A(end)]), + ArithOp::FloatRng { dst, min, max } => { + if min.is_imm_equal(0f64.to_bits()) && max.is_imm_equal(f64::MAX.to_bits()) { + sexp::list(&[A("rngf"), A(dst)]) + } else if min.is_imm_equal(0) { + sexp::list(&[A("rngf"), A(dst), A(max)]) + } else { + sexp::list(&[A("rngf"), A(dst), A(min), A(max)]) + } + } + ArithOp::FloatAdd { dst, a, b } => to_sexp_2_or_3("addf", dst, a, b), + ArithOp::FloatSub { dst, a, b } => to_sexp_2_or_3("subf", dst, a, b), + ArithOp::FloatMul { dst, a, b } => to_sexp_2_or_3("mulf", dst, a, b), + ArithOp::FloatDiv { dst, rem, a, div } => { + if rem.is_discard() { + to_sexp_2_or_3("divf", dst, a, div) + } else { + if &dst.as_rd() == a { + sexp::list(&[A("divrf"), A(dst), A(rem), A(div)]) + } else { + sexp::list(&[A("divrf"), A(dst), A(rem), A(a), A(div)]) + } + } + } + ArithOp::FloatMod { dst, a, div } => to_sexp_2_or_3("modf", dst, a, div), + ArithOp::IntToFloat { dst, a } => to_sexp_1_or_2("itf", dst, a), + ArithOp::FloatToInt { dst, a, mode: FloatToIntMode::Floor } => to_sexp_1_or_2("ftif", dst, a), + ArithOp::FloatToInt { dst, a, mode: FloatToIntMode::Round } => to_sexp_1_or_2("ftir", dst, a), + ArithOp::FloatToInt { dst, a, mode: FloatToIntMode::Ceil } => to_sexp_1_or_2("ftic", dst, a), + ArithOp::And { dst, a, b } => to_sexp_2_or_3("and", dst, a, b), ArithOp::Or { dst, a, b } => to_sexp_2_or_3("or", dst, a, b), ArithOp::Xor { dst, a, b } => to_sexp_2_or_3("xor", dst, a, b), @@ -333,6 +506,8 @@ impl OpTrait for ArithOp { 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), + + _ => unimplemented!() // FIXME } } } diff --git a/crsn_arith/src/parse.rs b/crsn_arith/src/parse.rs index 3e085f7..c689991 100644 --- a/crsn_arith/src/parse.rs +++ b/crsn_arith/src/parse.rs @@ -4,9 +4,7 @@ use crsn::asm::instr::op::OpKind; use crsn::asm::parse::arg_parser::TokenParser; use crsn::module::ParseRes; use crsn::sexp::SourcePosition; - -use crate::defs::ArithOp; - +use crate::defs::{ArithOp, FloatToIntMode}; pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result, CrsnError> { Ok(ParseRes::ext(match keyword { @@ -33,14 +31,14 @@ pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenP 1 => { ArithOp::Rng { dst: args.next_wr()?, - min: Rd::immediate(0), - max: Rd::immediate(u64::MAX), + min: Rd::new_imm(0), + max: Rd::new_imm(u64::MAX), } } 2 => { ArithOp::Rng { dst: args.next_wr()?, - min: Rd::immediate(0), + min: Rd::new_imm(0), max: args.next_rd()?, } } @@ -61,12 +59,12 @@ pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenP "inc" => { let (dst, a) = args.parse_wr_rd()?; - ArithOp::Add { dst, a, b: Rd::immediate(1) } + ArithOp::Add { dst, a, b: Rd::new_imm(1) } } "dec" => { let (dst, a) = args.parse_wr_rd()?; - ArithOp::Sub { dst, a, b: Rd::immediate(1) } + ArithOp::Sub { dst, a, b: Rd::new_imm(1) } } "add" => { @@ -74,6 +72,11 @@ pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenP ArithOp::Add { dst, a, b } } + "abs" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::Abs { dst, a } + } + "sub" => { let (dst, a, b) = args.parse_wr_rd_rd()?; ArithOp::Sub { dst, a, b } @@ -84,6 +87,11 @@ pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenP ArithOp::Mul { dst, a, b } } + "pow" => { + let (dst, a, pow) = args.parse_wr_rd_rd()?; + ArithOp::Pow { dst, a, pow } + } + "divr" => { match args.len() { 3 => { @@ -191,6 +199,251 @@ pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenP ArithOp::Rbit { dst, src } } + "cmpf" => { + let parsed = ArithOp::FloatCompare { + a: args.next_rd()?.imm_to_float(), + b: args.next_rd()?.imm_to_float(), + }; + args.ensure_empty("two input arguments")?; + parsed + } + + "frcmp" => { + let parsed = ArithOp::FloatRangeTest { + val: args.next_rd()?.imm_to_float(), + a: args.next_rd()?.imm_to_float(), + b: args.next_rd()?.imm_to_float(), + }; + args.ensure_empty("3 arguments (value, min, max)")?; + parsed + } + + "ftst" => { + let arg = args.next_rd()?; + args.ensure_empty("1 input argument")?; + ArithOp::FloatTest { a: arg.imm_to_float() } + } + + "frng" => { + let parsed = match args.len() { + 1 => { + ArithOp::FloatRng { + dst: args.next_wr()?, + min: Rd::new_float(0.0), + max: Rd::new_float(f64::MAX), + } + } + 2 => { + ArithOp::FloatRng { + dst: args.next_wr()?, + min: Rd::new_float(0.0), + max: args.next_rd()?.imm_to_float(), + } + } + 3 => { + ArithOp::FloatRng { + dst: args.next_wr()?, + min: args.next_rd()?.imm_to_float(), + max: args.next_rd()?.imm_to_float(), + } + } + _ => { + return Err(CrsnError::Parse("Rng requires 1, 2 or 3 arguments".into(), op_pos.clone())); + } + }; + args.ensure_empty("1 (dst) or 3 (dst, min, max) arguments")?; + parsed + } + + "fadd" => { + let (dst, a, b) = args.parse_wr_rd_rd()?; + ArithOp::FloatAdd { + dst, + a: a.imm_to_float(), + b: b.imm_to_float(), + } + } + + "fsub" => { + let (dst, a, b) = args.parse_wr_rd_rd()?; + ArithOp::FloatSub { + dst, + a: a.imm_to_float(), + b: b.imm_to_float(), + } + } + + "fmul" => { + let (dst, a, b) = args.parse_wr_rd_rd()?; + ArithOp::FloatMul { + dst, + a: a.imm_to_float(), + b: b.imm_to_float(), + } + } + + "fpow" => { + let (dst, a, pow) = args.parse_wr_rd_rd()?; + ArithOp::FloatPow { + dst, + a: a.imm_to_float(), + pow: pow.imm_to_float() + } + } + + "froot" => { + let (dst, a, root) = args.parse_wr_rd_rd()?; + ArithOp::FloatRoot { + dst, + a: a.imm_to_float(), + root: root.imm_to_float() + } + } + + "fhyp" => { + let (dst, a, b) = args.parse_wr_rd_rd()?; + ArithOp::FloatHyp { + dst, + a: a.imm_to_float(), + b: a.imm_to_float(), + } + } + + "fdiv" => { + let (dst, a, div) = args.parse_wr_rd_rd()?; + ArithOp::FloatDiv { + dst, + rem: Wr::discard(), + a: a.imm_to_float(), + div: div.imm_to_float(), + } + } + + "fdivr" => { + match args.len() { + 3 => { + let dst = args.next_rdwr()?; + let rem = args.next_wr()?; + let div = args.next_rd()?; + ArithOp::FloatDiv { + dst: dst.wr(), + rem, + a: dst.rd().imm_to_float(), + div: div.imm_to_float(), + } + } + 4 => { + ArithOp::FloatDiv { + dst: args.next_wr()?, + rem: args.next_wr()?, + a: args.next_rd()?.imm_to_float(), + div: args.next_rd()?.imm_to_float(), + } + } + _ => { + return Err(CrsnError::Parse("DivRF requires 3 or 4 arguments".into(), op_pos.clone())); + } + } + } + + "fmod" => { + let (dst, a, div) = args.parse_wr_rd_rd()?; + ArithOp::FloatMod { + dst, + a: a.imm_to_float(), + div: div.imm_to_float(), + } + } + + "itf" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::IntToFloat { + dst, + a: a.imm_to_float() + } + } + + "fti" | "ftir" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatToInt { + dst, + a: a.imm_to_float(), + mode: FloatToIntMode::Round + } + } + + "ftic" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatToInt { + dst, + a: a.imm_to_float(), + mode: FloatToIntMode::Ceil + } + } + + "ftif" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatToInt { + dst, + a: a.imm_to_float(), + mode: FloatToIntMode::Floor + } + } + + "fabs" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatAbs { dst, a: f.imm_to_float() } + } + + "fsin" => { + let (dst, f) = args.parse_wr_rd()?; + ArithOp::FloatSin { dst, f: f.imm_to_float() } + } + + "fasin" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatAsin { dst, a: a.imm_to_float() } + } + + "fcos" => { + let (dst, f) = args.parse_wr_rd()?; + ArithOp::FloatCos { dst, f: f.imm_to_float() } + } + + "facos" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatAcos { dst, a: a.imm_to_float() } + } + + "ftan" => { + let (dst, f) = args.parse_wr_rd()?; + ArithOp::FloatTan { dst, f: f.imm_to_float() } + } + + "fatan" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatAtan { dst, a: a.imm_to_float() } + } + + "fatan2" => { + let (dst, a, b) = args.parse_wr_rd_rd()?; + ArithOp::FloatAtan2 { + dst, + y: a.imm_to_float(), + x: b.imm_to_float(), + } + } + + "fcot" => { + let (dst, f) = args.parse_wr_rd()?; + ArithOp::FloatCot { dst, f: f.imm_to_float() } + } + + "facot" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatAcot { dst, a: a.imm_to_float() } + } + other => { if let Some((dst, src, mask)) = args.parse_masked_wr_rd(other, "clz")? { if !dst.disp_equals(src) && mask.dst_pos != 0 { diff --git a/crsn_buf/src/parse.rs b/crsn_buf/src/parse.rs index 10e04ac..209967b 100644 --- a/crsn_buf/src/parse.rs +++ b/crsn_buf/src/parse.rs @@ -21,7 +21,7 @@ pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenPar let value = match args.next() { None => { - BufValue::Zeros(Rd::immediate(0)) + BufValue::Zeros(Rd::new_imm(0)) } Some(tok @ Sexp::Atom(Atom::S(_), _)) | Some(tok @ Sexp::Atom(Atom::I(_), _)) | @@ -112,7 +112,7 @@ pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenPar "bfrpush" => { BufOps::Insert { obj: args.next_rdobj()?, - idx: Rd::immediate(0), + idx: Rd::new_imm(0), value: args.next_rd()?, } } @@ -121,7 +121,7 @@ pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenPar BufOps::Remove { dst: args.next_wr()?, obj: args.next_rdobj()?, - idx: Rd::immediate(0), + idx: Rd::new_imm(0), } } diff --git a/crsn_screen/src/parse.rs b/crsn_screen/src/parse.rs index 95c0d5b..9f6fb78 100644 --- a/crsn_screen/src/parse.rs +++ b/crsn_screen/src/parse.rs @@ -24,7 +24,7 @@ pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenPar color: if args.len() > 0 { args.next_rd()? } else { - Rd::immediate(0) // black + Rd::new_imm(0) // black }, } } @@ -60,7 +60,7 @@ pub(crate) fn parse<'a>(_pos: &SourcePosition, keyword: &str, mut args: TokenPar force: if args.have_more() { args.next_rd()? } else { - Rd::immediate(1) + Rd::new_imm(1) }, } } From 9723ac72be630aaef931931e4e7bc2a261c41ea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Tue, 20 Oct 2020 00:26:08 +0200 Subject: [PATCH 2/2] fixes, more trig, demo with floats --- Cargo.lock | 1 + crsn/src/asm/data/rd.rs | 19 +- crsn/src/runtime/frame/status.rs | 9 +- crsn_arith/Cargo.toml | 1 + crsn_arith/src/defs.rs | 19 +- crsn_arith/src/exec.rs | 295 ++++++++++++++++++++++++++++--- crsn_arith/src/lib.rs | 20 +++ crsn_arith/src/parse.rs | 73 ++++++-- examples/screen_trig.csn | 34 ++++ 9 files changed, 421 insertions(+), 50 deletions(-) create mode 100644 examples/screen_trig.csn diff --git a/Cargo.lock b/Cargo.lock index ba8c491..f03afcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,6 +188,7 @@ name = "crsn_arith" version = "0.1.0" dependencies = [ "crsn", + "log", "num-traits", "rand 0.7.3", ] diff --git a/crsn/src/asm/data/rd.rs b/crsn/src/asm/data/rd.rs index edbc875..5410d61 100644 --- a/crsn/src/asm/data/rd.rs +++ b/crsn/src/asm/data/rd.rs @@ -24,15 +24,18 @@ impl Rd { Rd(RdData::Immediate(val.to_bits())) } - /// Convert immediate read to float + /// Convert immediate integer to float pub fn imm_to_float(self) -> Rd { - match self { - Rd(RdData::Immediate(v)) => { - let signed : i64 = unsafe { std::mem::transmute(v) }; - Rd::new_float(signed as f64) - } - other => other - } + self + + // FIXME + // match self { + // Rd(RdData::Immediate(v)) => { + // let signed : i64 = unsafe { std::mem::transmute(v) }; + // Rd::new_float(signed as f64) + // } + // other => other + // } } pub fn is_imm_equal(self, other: Value) -> bool { diff --git a/crsn/src/runtime/frame/status.rs b/crsn/src/runtime/frame/status.rs index 0137169..dec3d5a 100644 --- a/crsn/src/runtime/frame/status.rs +++ b/crsn/src/runtime/frame/status.rs @@ -4,6 +4,7 @@ use std::fmt; use crate::asm::data::literal::{is_negative, is_positive, Value}; use crate::asm::instr::Cond; use crate::asm::instr::cond::Flag; +use num_traits::Zero; #[derive(Default, Clone, Debug)] pub struct StatusFlags { @@ -80,9 +81,11 @@ impl StatusFlags { #[inline(always)] pub fn update_float(&mut self, val: f64) { - self.zero = val == 0.0; - self.positive = val > 0.0; - self.negative = val < 0.0; + self.invalid = val.is_nan(); + self.overflow = val.is_infinite(); + self.zero = val.is_zero(); + self.positive = val.is_sign_positive(); + self.negative = val.is_sign_negative(); } #[inline(always)] diff --git a/crsn_arith/Cargo.toml b/crsn_arith/Cargo.toml index 7504dfa..2b0874c 100644 --- a/crsn_arith/Cargo.toml +++ b/crsn_arith/Cargo.toml @@ -10,3 +10,4 @@ edition = "2018" crsn = { path = "../crsn" } num-traits = "0.2.12" rand = "0.7.3" +log = "0.4.11" diff --git a/crsn_arith/src/defs.rs b/crsn_arith/src/defs.rs index 7bfba33..04c620f 100644 --- a/crsn_arith/src/defs.rs +++ b/crsn_arith/src/defs.rs @@ -28,6 +28,7 @@ pub enum ArithOp { Add { dst: Wr, a: Rd, b: Rd }, Abs { dst: Wr, a: Rd }, + Sgn { dst: Wr, a: Rd }, Sub { dst: Wr, a: Rd, b: Rd }, Mul { dst: Wr, a: Rd, b: Rd }, Pow { dst: Wr, a: Rd, pow: Rd }, @@ -63,16 +64,26 @@ pub enum ArithOp { FloatDiv { dst: Wr, rem: Wr, a: Rd, div: Rd }, FloatMod { dst: Wr, a: Rd, div: Rd }, FloatAbs { dst: Wr, a: Rd }, + FloatSgn { dst: Wr, a: Rd }, - FloatSin { dst: Wr, f: Rd }, + FloatSin { dst: Wr, a: Rd }, FloatAsin { dst: Wr, a: Rd }, - FloatCos { dst: Wr, f: Rd }, + FloatCos { dst: Wr, a: Rd }, FloatAcos { dst: Wr, a: Rd }, - FloatTan { dst: Wr, f: Rd }, + FloatTan { dst: Wr, a: Rd }, FloatAtan { dst: Wr, a: Rd }, FloatAtan2 { dst: Wr, y: Rd, x: Rd }, - FloatCot { dst: Wr, f: Rd }, + FloatCot { dst: Wr, a: Rd }, FloatAcot { dst: Wr, a: Rd }, + + FloatHypSin { dst: Wr, a: Rd }, + FloatHypAsin { dst: Wr, a: Rd }, + FloatHypCos { dst: Wr, a: Rd }, + FloatHypAcos { dst: Wr, a: Rd }, + FloatHypTan { dst: Wr, a: Rd }, + FloatHypAtan { dst: Wr, a: Rd }, + FloatHypCot { dst: Wr, a: Rd }, + FloatHypAcot { dst: Wr, a: Rd }, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] diff --git a/crsn_arith/src/exec.rs b/crsn_arith/src/exec.rs index cf4b1b8..1392656 100644 --- a/crsn_arith/src/exec.rs +++ b/crsn_arith/src/exec.rs @@ -1,6 +1,6 @@ use std::ops::Rem; -use num_traits::PrimInt; +use num_traits::{PrimInt}; use crsn::asm::data::{Rd, Wr}; use crsn::module::{EvalRes, OpTrait}; @@ -13,24 +13,25 @@ use crsn::utils::A; use crate::defs::{ArithOp, FloatToIntMode}; use crsn::asm::instr::cond::Flag; use rand::Rng; +use std::f64::consts::{PI, FRAC_PI_2}; #[inline] -fn f2u(f: f64) -> u64 { +pub(crate) fn f2u(f: f64) -> u64 { unsafe { std::mem::transmute(f) } } #[inline] -fn u2f(f: u64) -> f64 { +pub(crate) fn u2f(f: u64) -> f64 { unsafe { std::mem::transmute(f) } } #[inline] -fn i2u(f: i64) -> u64 { +pub(crate) fn i2u(f: i64) -> u64 { unsafe { std::mem::transmute(f) } } #[inline] -fn u2i(f: u64) -> i64 { +pub(crate) fn u2i(f: u64) -> i64 { unsafe { std::mem::transmute(f) } } @@ -174,14 +175,17 @@ impl OpTrait for ArithOp { FloatToIntMode::Ceil => val.ceil(), FloatToIntMode::Round => val.round(), } as i64); + trace!("fti {} -> {}", val, res); state.update_status(res); state.write(dst, res)?; } ArithOp::IntToFloat { dst, a } => { state.clear_status(); - let val : f64 = u2i(state.read(a)?) as f64; - state.update_status_float(val); - state.write(dst, f2u(val))?; + let i = u2i(state.read(a)?); + let res: f64 = i as f64; + trace!("itf {} -> {}", i, res); + state.update_status_float(res); + state.write(dst, f2u(res))?; } ArithOp::FloatTest { a } => { state.clear_status(); @@ -419,8 +423,227 @@ impl OpTrait for ArithOp { state.update_status(res); state.write(dst, res)?; } + ArithOp::Abs { dst, a } => { + state.clear_status(); + let x = u2i(state.read(a)?); + let res = i2u(x.abs()); + state.update_status(res); + state.write(dst, res)?; + } + ArithOp::Sgn { dst, a } => { + state.clear_status(); + let x = u2i(state.read(a)?); + let res = i2u(if x >= 0 { 0 } else { -1 }); + state.update_status(res); + state.write(dst, res)?; + } + ArithOp::Pow { dst, a, pow } => { + state.clear_status(); + let x = state.read(a)?; + let p = state.read(pow)?; + if p > u32::MAX as u64 { + state.set_flag(Flag::Invalid, true); + } else { + let res = x.pow(p as u32); + state.update_status(res); + state.write(dst, res)?; + } + } + ArithOp::FloatPow { dst, a, pow } => { + state.clear_status(); + let x = u2f(state.read(a)?); + let p = u2f(state.read(pow)?); + let res = x.powf(p); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatRoot { dst, a, root } => { + state.clear_status(); + let x = u2f(state.read(a)?); + let p = u2f(state.read(root)?); + if p == 0f64 { + state.set_flag(Flag::Invalid, true); + } else { + let res = x.powf(1f64 / p); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + } + ArithOp::FloatHyp { dst, a, b } => { + state.clear_status(); + let a = u2f(state.read(a)?); + let b = u2f(state.read(b)?); + let res = a.hypot(b); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatAbs { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + let res = a.abs(); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatSgn { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + let res = a.signum(); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + /* Simple trig */ + ArithOp::FloatSin { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + let res = a.sin(); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatAsin { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + let res = a.asin(); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatCos { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + let res = a.cos(); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatAcos { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + let res = a.acos(); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatTan { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + let res = a.tan(); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatAtan { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + let res = a.atan(); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatAtan2 { dst, y, x } => { + state.clear_status(); + let a = u2f(state.read(y)?); + let b = u2f(state.read(x)?); + let res = a.atan2(b); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatCot { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + let tan = a.tan(); + if tan == 0.0 { + state.set_flag(Flag::Invalid, true); + } else { + let res = 1f64 / tan; + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + } + ArithOp::FloatAcot { dst, a } => { + state.clear_status(); + let x = u2f(state.read(a)?); + + // TODO verify + let res = if x > 1.0 { + (1.0/x).atan() + } else if x < -1.0 { + PI + (1.0/x).atan() + } else { + FRAC_PI_2 - x.atan() + }; - _ => unimplemented!() // TODO implement float trig etc + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + /* Hyperbolic trig */ + ArithOp::FloatHypSin { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + let res = a.sinh(); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatHypAsin { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + let res = a.asinh(); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatHypCos { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + let res = a.cosh(); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatHypAcos { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + if a > 1.0 { + let res = a.acos(); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + } + ArithOp::FloatHypTan { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + let res = a.tanh(); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + ArithOp::FloatHypAtan { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + if a > -1.0 && a < 1.0 { + let res = a.atanh(); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } else { + state.set_flag(Flag::Invalid, true); + } + } + ArithOp::FloatHypCot { dst, a } => { + state.clear_status(); + let a = u2f(state.read(a)?); + let tan = a.tanh(); + if tan == 0.0 { + state.set_flag(Flag::Invalid, true); + } else { + let res = 1f64 / tan; + state.update_status_float(res); + state.write(dst, f2u(res))?; + } + } + ArithOp::FloatHypAcot { dst, a } => { + state.clear_status(); + let x = u2f(state.read(a)?); + + // TODO verify + if x > 1.0 || x < -1.0 { + let res = (1.0/x).atanh(); + state.update_status_float(res); + state.write(dst, f2u(res))?; + } else { + state.set_flag(Flag::Invalid, true); + }; + } } Ok(eres) @@ -457,33 +680,33 @@ impl OpTrait for ArithOp { ArithOp::Mod { dst, a, div } => to_sexp_2_or_3("mod", dst, a, div), // TODO render as float - ArithOp::FloatTest { a } => sexp::list(&[A("tstf"), A(a)]), - ArithOp::FloatCompare { a, b } => sexp::list(&[A("cmpf"), A(a), A(b)]), - ArithOp::FloatRangeTest { val, a: start, b: end } => sexp::list(&[A("rcmpf"), A(val), A(start), A(end)]), + ArithOp::FloatTest { a } => sexp::list(&[A("ftst"), A(a)]), + ArithOp::FloatCompare { a, b } => sexp::list(&[A("fcmp"), A(a), A(b)]), + ArithOp::FloatRangeTest { val, a: start, b: end } => sexp::list(&[A("frcmp"), A(val), A(start), A(end)]), ArithOp::FloatRng { dst, min, max } => { - if min.is_imm_equal(0f64.to_bits()) && max.is_imm_equal(f64::MAX.to_bits()) { - sexp::list(&[A("rngf"), A(dst)]) + if min.is_imm_equal((0f64).to_bits()) && max.is_imm_equal(f64::MAX.to_bits()) { + sexp::list(&[A("frng"), A(dst)]) } else if min.is_imm_equal(0) { - sexp::list(&[A("rngf"), A(dst), A(max)]) + sexp::list(&[A("frng"), A(dst), A(max)]) } else { - sexp::list(&[A("rngf"), A(dst), A(min), A(max)]) + sexp::list(&[A("frng"), A(dst), A(min), A(max)]) } } - ArithOp::FloatAdd { dst, a, b } => to_sexp_2_or_3("addf", dst, a, b), - ArithOp::FloatSub { dst, a, b } => to_sexp_2_or_3("subf", dst, a, b), - ArithOp::FloatMul { dst, a, b } => to_sexp_2_or_3("mulf", dst, a, b), + ArithOp::FloatAdd { dst, a, b } => to_sexp_2_or_3("fadd", dst, a, b), + ArithOp::FloatSub { dst, a, b } => to_sexp_2_or_3("fsub", dst, a, b), + ArithOp::FloatMul { dst, a, b } => to_sexp_2_or_3("fmul", dst, a, b), ArithOp::FloatDiv { dst, rem, a, div } => { if rem.is_discard() { - to_sexp_2_or_3("divf", dst, a, div) + to_sexp_2_or_3("fdiv", dst, a, div) } else { if &dst.as_rd() == a { - sexp::list(&[A("divrf"), A(dst), A(rem), A(div)]) + sexp::list(&[A("fdivr"), A(dst), A(rem), A(div)]) } else { - sexp::list(&[A("divrf"), A(dst), A(rem), A(a), A(div)]) + sexp::list(&[A("fdivr"), A(dst), A(rem), A(a), A(div)]) } } } - ArithOp::FloatMod { dst, a, div } => to_sexp_2_or_3("modf", dst, a, div), + ArithOp::FloatMod { dst, a, div } => to_sexp_2_or_3("fmod", dst, a, div), ArithOp::IntToFloat { dst, a } => to_sexp_1_or_2("itf", dst, a), ArithOp::FloatToInt { dst, a, mode: FloatToIntMode::Floor } => to_sexp_1_or_2("ftif", dst, a), ArithOp::FloatToInt { dst, a, mode: FloatToIntMode::Round } => to_sexp_1_or_2("ftir", dst, a), @@ -507,7 +730,31 @@ impl OpTrait for ArithOp { 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), - _ => unimplemented!() // FIXME + ArithOp::Abs { dst, a } => to_sexp_1_or_2("abs", dst, a), + ArithOp::Sgn { dst, a } => to_sexp_1_or_2("sgn", dst, a), + ArithOp::Pow { dst, a, pow } => to_sexp_2_or_3("pow", dst, a, pow), + ArithOp::FloatPow { dst, a, pow } => to_sexp_2_or_3("fpow", dst, a, pow), + ArithOp::FloatRoot { dst, a, root } => to_sexp_2_or_3("froot", dst, a, root), + ArithOp::FloatHyp { dst, a, b } => to_sexp_2_or_3("fhyp", dst, a, b), + ArithOp::FloatAbs { dst, a } => to_sexp_1_or_2("fabs", dst, a), + ArithOp::FloatSgn { dst, a } => to_sexp_1_or_2("fsgn", dst, a), + ArithOp::FloatSin { dst, a } => to_sexp_1_or_2("fsin", dst, a), + ArithOp::FloatAsin { dst, a } => to_sexp_1_or_2("fasin", dst, a), + ArithOp::FloatCos { dst, a } => to_sexp_1_or_2("fcos", dst, a), + ArithOp::FloatAcos { dst, a } => to_sexp_1_or_2("facos", dst, a), + ArithOp::FloatTan { dst, a } => to_sexp_1_or_2("ftan", dst, a), + ArithOp::FloatAtan { dst, a } => to_sexp_1_or_2("fatan", dst, a), + ArithOp::FloatAtan2 { dst, x, y } => to_sexp_2_or_3("fatan2", dst, x, y), + ArithOp::FloatCot { dst, a } => to_sexp_1_or_2("fcot", dst, a), + ArithOp::FloatAcot { dst, a } => to_sexp_1_or_2("facot", dst, a), + ArithOp::FloatHypSin { dst, a } => to_sexp_1_or_2("fsinh", dst, a), + ArithOp::FloatHypAsin { dst, a } => to_sexp_1_or_2("fasinh", dst, a), + ArithOp::FloatHypCos { dst, a } => to_sexp_1_or_2("fcosh", dst, a), + ArithOp::FloatHypAcos { dst, a } => to_sexp_1_or_2("facosh", dst, a), + ArithOp::FloatHypTan { dst, a } => to_sexp_1_or_2("ftanh", dst, a), + ArithOp::FloatHypAtan { dst, a } => to_sexp_1_or_2("fatanh", dst, a), + ArithOp::FloatHypCot { dst, a } => to_sexp_1_or_2("fcoth", dst, a), + ArithOp::FloatHypAcot { dst, a } => to_sexp_1_or_2("facoth", dst, a), } } } diff --git a/crsn_arith/src/lib.rs b/crsn_arith/src/lib.rs index 146fb98..045c482 100644 --- a/crsn_arith/src/lib.rs +++ b/crsn_arith/src/lib.rs @@ -1,8 +1,13 @@ +#[macro_use] +extern crate log; + use crsn::asm::error::CrsnError; use crsn::asm::instr::op::OpKind; use crsn::asm::parse::arg_parser::TokenParser; use crsn::module::{CrsnExtension, ParseRes}; use crsn::sexp::SourcePosition; +use crsn::asm::data::literal::Value; +use crate::exec::f2u; mod defs; mod parse; @@ -25,4 +30,19 @@ impl CrsnExtension for ArithOps { fn parse_op<'a>(&self, pos: &SourcePosition, keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { parse::parse(pos, keyword, args) } + + /// Get value of an extension-provided constant. + /// This constant may be an object handle, or a constant value used as argument in some other instruction. + fn get_constant_value<'a>(&self, name: &str) -> Option + { + use std::f64::consts; + match name { + "PI" => Some(f2u(consts::PI)), + "PI_2" => Some(f2u(consts::FRAC_PI_2)), + "TAU" => Some(f2u(consts::TAU)), + "E" => Some(f2u(consts::E)), + // TODO more?... + _ => None + } + } } diff --git a/crsn_arith/src/parse.rs b/crsn_arith/src/parse.rs index c689991..5a3f275 100644 --- a/crsn_arith/src/parse.rs +++ b/crsn_arith/src/parse.rs @@ -77,6 +77,11 @@ pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenP ArithOp::Abs { dst, a } } + "sgn" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::Sgn { dst, a } + } + "sub" => { let (dst, a, b) = args.parse_wr_rd_rd()?; ArithOp::Sub { dst, a, b } @@ -199,7 +204,7 @@ pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenP ArithOp::Rbit { dst, src } } - "cmpf" => { + "fcmp" => { let parsed = ArithOp::FloatCompare { a: args.next_rd()?.imm_to_float(), b: args.next_rd()?.imm_to_float(), @@ -305,7 +310,7 @@ pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenP ArithOp::FloatHyp { dst, a: a.imm_to_float(), - b: a.imm_to_float(), + b: b.imm_to_float(), } } @@ -392,12 +397,17 @@ pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenP "fabs" => { let (dst, a) = args.parse_wr_rd()?; - ArithOp::FloatAbs { dst, a: f.imm_to_float() } + ArithOp::FloatAbs { dst, a: a.imm_to_float() } + } + + "fsgn" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatSgn { dst, a: a.imm_to_float() } } "fsin" => { - let (dst, f) = args.parse_wr_rd()?; - ArithOp::FloatSin { dst, f: f.imm_to_float() } + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatSin { dst, a: a.imm_to_float() } } "fasin" => { @@ -406,8 +416,8 @@ pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenP } "fcos" => { - let (dst, f) = args.parse_wr_rd()?; - ArithOp::FloatCos { dst, f: f.imm_to_float() } + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatCos { dst, a: a.imm_to_float() } } "facos" => { @@ -416,8 +426,8 @@ pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenP } "ftan" => { - let (dst, f) = args.parse_wr_rd()?; - ArithOp::FloatTan { dst, f: f.imm_to_float() } + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatTan { dst, a: a.imm_to_float() } } "fatan" => { @@ -435,8 +445,8 @@ pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenP } "fcot" => { - let (dst, f) = args.parse_wr_rd()?; - ArithOp::FloatCot { dst, f: f.imm_to_float() } + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatCot { dst, a: a.imm_to_float() } } "facot" => { @@ -444,6 +454,47 @@ pub(crate) fn parse<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenP ArithOp::FloatAcot { dst, a: a.imm_to_float() } } + /* hyperbolic */ + "fsinh" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatHypSin { dst, a: a.imm_to_float() } + } + + "fasinh" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatHypAsin { dst, a: a.imm_to_float() } + } + + "fcosh" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatHypCos { dst, a: a.imm_to_float() } + } + + "facosh" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatHypAcos { dst, a: a.imm_to_float() } + } + + "ftanh" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatHypTan { dst, a: a.imm_to_float() } + } + + "fatanh" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatHypAtan { dst, a: a.imm_to_float() } + } + + "fcoth" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatHypCot { dst, a: a.imm_to_float() } + } + + "facoth" => { + let (dst, a) = args.parse_wr_rd()?; + ArithOp::FloatHypAcot { dst, a: a.imm_to_float() } + } + other => { if let Some((dst, src, mask)) = args.parse_masked_wr_rd(other, "clz")? { if !dst.disp_equals(src) && mask.dst_pos != 0 { diff --git a/examples/screen_trig.csn b/examples/screen_trig.csn new file mode 100644 index 0000000..2915fa8 --- /dev/null +++ b/examples/screen_trig.csn @@ -0,0 +1,34 @@ +( + ; mouse over the image to draw circles + (sc-init 800 600) + (sc-opt SCREEN_AUTO_BLIT 0) + + (:loop) + (sc-poll) + (sc-mouse r4 r5 (ov? (j :loop))) + (itf r4) + (itf r5) + + (ld r0 0.0) + (def RADIUS 50.0) + (:circle) + (fsin r1 r0) + (fcos r2 r0) + + (fmul r1 RADIUS) + (fmul r2 RADIUS) + + (fadd r1 r4) + (fadd r2 r5) + + (fti r1) + (fti r2) + (sc-px r1 r2 #66ff00) + (fadd r0 0.1) ; make lower for a smoother circle + (fcmp r0 TAU + (ge? + (sc-blit) + (j :loop))) + (j :circle) + (j :loop) +)