summaryrefslogtreecommitdiffstats
path: root/arith.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2015-01-29 19:07:10 -0800
committerKaz Kylheku <kaz@kylheku.com>2015-01-29 19:07:10 -0800
commita11dd30b417e1f48da573748fa4ab5fe09f0b212 (patch)
tree0f2a5852563b34861421aab59972698f0d4e6c40 /arith.c
parentd04250b7e3d809ded65b767f34baf398ca03eae1 (diff)
downloadtxr-a11dd30b417e1f48da573748fa4ab5fe09f0b212.tar.gz
txr-a11dd30b417e1f48da573748fa4ab5fe09f0b212.tar.bz2
txr-a11dd30b417e1f48da573748fa4ab5fe09f0b212.zip
* arith.c (make_ubignum): New static function.
(sign_extend): New function. * eval.c (eval_init): Register sign-extend intrinsic. * lib.h (sign_extend): Declared. * txr.1: Documented sign-extend. * tl.vim, txr.vim: Updated.
Diffstat (limited to 'arith.c')
-rw-r--r--arith.c34
1 files changed, 34 insertions, 0 deletions
diff --git a/arith.c b/arith.c
index a037e5df..8e4976db 100644
--- a/arith.c
+++ b/arith.c
@@ -58,6 +58,13 @@ val make_bignum(void)
return n;
}
+static val make_ubignum(void)
+{
+ val n = make_obj();
+ n->bn.type = BGNUM;
+ return n;
+}
+
val bignum(cnum cn)
{
val n = make_bignum();
@@ -1893,6 +1900,33 @@ bad3:
uw_throwf(error_s, lit("logtrunc: non-integral operand ~s"), a, nao);
}
+val sign_extend(val n, val nbits)
+{
+ val msb = minus(nbits, one);
+ val ntrunc = logtrunc(n, nbits);
+
+ if (bit(ntrunc, msb)) {
+ switch (type(ntrunc)) {
+ case NUM:
+ {
+ cnum cn = c_num(ntrunc);
+ cnum nb = c_num(nbits);
+ return num(cn | (INT_PTR_MAX << nb));
+ }
+ case BGNUM:
+ {
+ val out = make_ubignum();
+ mp_2comp(mp(ntrunc), mp(out), mp(ntrunc)->used);
+ mp_neg(mp(out), mp(out));
+ return normalize(out);
+ }
+ default:
+ internal_error("impossible case");
+ }
+ }
+ return ntrunc;
+}
+
val ash(val a, val bits)
{
cnum an, bn;