sign_extend() did NOT expect all upper bits cleared but clear it
[libfirm] / ir / tv / strcalc.c
index 5978bef..6e45a26 100644 (file)
  * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
  */
 
-#include "strcalc.h"
 
-#include <stdlib.h>
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#endif
+#ifdef HAVE_MALLOC_H
+# include <malloc.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h>  /* memset/memcmp */
+#endif
 #include <assert.h>   /* assertions */
-#include <string.h>   /* memset/memcmp */
 #include <stdio.h>    /* output for error messages */
 #include <limits.h>   /* definition of LONG_MIN, used in sc_get_val_from_long */
 
+#include "strcalc.h"
+#include "xmalloc.h"
+
 /*
  * local definitions and macros
  */
-#define CLEAR_BUFFER(b) assert(b); memset(b, SC_0, CALC_BUFFER_SIZE)
+#define CLEAR_BUFFER(b) assert(b); memset(b, SC_0, calc_buffer_size)
 #define _val(a) ((a)-SC_0)
 #define _digit(a) ((a)+SC_0)
 #define _bitisset(digit, pos) (and_table[_val(digit)][_val(shift_table[pos])] != SC_0)
 #define fail_char(a, b, c, d) _fail_char((a), (b), (c), (d), __FILE__,  __LINE__)
 
 /* shortcut output for debugging */
-#  define sc_print_hex(a) sc_print((a), 0, SC_HEX)
-#  define sc_print_dec(a) sc_print((a), 0, SC_DEC)
-#  define sc_print_oct(a) sc_print((a), 0, SC_OCT)
-#  define sc_print_bin(a) sc_print((a), 0, SC_BIN)
+#  define sc_print_hex(a) sc_print((a), 0, SC_HEX, 0)
+#  define sc_print_dec(a) sc_print((a), 0, SC_DEC, 1)
+#  define sc_print_oct(a) sc_print((a), 0, SC_OCT, 0)
+#  define sc_print_bin(a) sc_print((a), 0, SC_BIN, 0)
 
 #ifdef STRCALC_DEBUG_PRINTCOMP
 #  define DEBUGPRINTF_COMPUTATION(x) printf x
 /*
  * private variables
  */
-
 static char *calc_buffer = NULL;    /* buffer holding all results */
 static char *output_buffer = NULL;  /* buffer for output */
-static int BIT_PATTERN_SIZE;        /* maximum number of bits */
-static int CALC_BUFFER_SIZE;        /* size of internally stored values */
-static int MAX_VALUE_SIZE;          /* maximum size of values */
-
-static int carry_flag;              /* some computation set carry_flag: */
-                                    /* rightshift if bits were lost due to shifting */
-                                    /* division if there was a remainder */
-
+static int bit_pattern_size;        /* maximum number of bits */
+static int calc_buffer_size;        /* size of internally stored values */
+static int max_value_size;          /* maximum size of values */
+
+static int carry_flag;              /**< some computation set the carry_flag:
+                                         - right shift if bits were lost due to shifting
+                                         - division if there was a remainder
+                                         However, the meaning of carry is machine dependent
+                                         and often defined in other ways! */
+
+static const char sex_digit[4] = { SC_E, SC_C, SC_8, SC_0 };
+static const char zex_digit[4] = { SC_1, SC_3, SC_7, SC_F };
 static const char max_digit[4] = { SC_0, SC_1, SC_3, SC_7 };
 static const char min_digit[4] = { SC_F, SC_E, SC_C, SC_8 };
 
@@ -399,7 +418,7 @@ static char const shrs_table[16][4][2] = {
                        { {SC_F, SC_0}, {SC_7, SC_8}, {SC_3, SC_C}, {SC_1, SC_E} }
                                    };
 
-/* for converting to binary string */
+/** converting a digit to a binary string */
 static const char *binary_table[16] = {
   "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111",
   "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"
@@ -418,44 +437,62 @@ static void _fail_char(const char *str, size_t len, const char fchar, int pos,
   exit(-1);
 }
 
+/**
+ * implements the bitwise NOT operation
+ */
 static void _bitnot(const char *val, char *buffer)
 {
   int counter;
 
-  for (counter = 0; counter<CALC_BUFFER_SIZE; counter++)
+  for (counter = 0; counter<calc_buffer_size; counter++)
     buffer[counter] = not_table[_val(val[counter])];
 }
 
+/**
+ * implements the bitwise OR operation
+ */
 static void _bitor(const char *val1, const char *val2, char *buffer)
 {
   int counter;
 
-  for (counter = 0; counter<CALC_BUFFER_SIZE; counter++)
+  for (counter = 0; counter<calc_buffer_size; counter++)
     buffer[counter] = or_table[_val(val1[counter])][_val(val2[counter])];
 }
 
+/**
+ * implements the bitwise eXclusive OR operation
+ */
 static void _bitxor(const char *val1, const char *val2, char *buffer)
 {
   int counter;
 
-  for (counter = 0; counter<CALC_BUFFER_SIZE; counter++)
+  for (counter = 0; counter<calc_buffer_size; counter++)
     buffer[counter] = xor_table[_val(val1[counter])][_val(val2[counter])];
 }
 
+/**
+ * implements the bitwise AND operation
+ */
 static void _bitand(const char *val1, const char *val2, char *buffer)
 {
   int counter;
 
-  for (counter = 0; counter<CALC_BUFFER_SIZE; counter++)
+  for (counter = 0; counter<calc_buffer_size; counter++)
     buffer[counter] = and_table[_val(val1[counter])][_val(val2[counter])];
 }
 
+/**
+ * returns the sign bit.
+ *
+ * @todo This implementation is wrong, as it returns the highest bit of the buffer
+ *       NOT the highest bit depending on the real mode
+ */
 static int _sign(const char *val)
 {
-  return (val[CALC_BUFFER_SIZE-1] <= SC_7) ? (1) : (-1);
+  return (val[calc_buffer_size-1] <= SC_7) ? (1) : (-1);
 }
 
-/*
+/**
  * returns non-zero if bit at position pos is set
  */
 static int _bit(const char *val, int pos)
@@ -466,11 +503,14 @@ static int _bit(const char *val, int pos)
   return _bitisset(val[nibble], bit);
 }
 
-static void _inc(char *val, char *buffer)
+/**
+ * Implements a fast ADD + 1
+ */
+static void _inc(const char *val, char *buffer)
 {
   int counter = 0;
 
-  while (counter++ < CALC_BUFFER_SIZE)
+  while (counter++ < calc_buffer_size)
   {
     if (*val == SC_F)
     {
@@ -488,19 +528,28 @@ static void _inc(char *val, char *buffer)
    * happen only when a value changes sign. */
 }
 
+/**
+ * Implements a unary MINUS
+ */
 static void _negate(const char *val, char *buffer)
 {
   _bitnot(val, buffer);
   _inc(buffer, buffer);
 }
 
+/**
+ * Implements a binary ADD
+ *
+ * @todo The implementation of carry is wrong, as it is the
+ *       calc_buffer_size carry, not the mode depending
+ */
 static void _add(const char *val1, const char *val2, char *buffer)
 {
   int counter;
   const char *add1, *add2;
   char carry = SC_0;
 
-  for (counter = 0; counter < CALC_BUFFER_SIZE; counter++)
+  for (counter = 0; counter < calc_buffer_size; counter++)
   {
     add1 = add_table[_val(val1[counter])][_val(val2[counter])];
     add2 = add_table[_val(add1[0])][_val(carry)];
@@ -508,21 +557,40 @@ static void _add(const char *val1, const char *val2, char *buffer)
     buffer[counter] = add2[0];
     carry = add_table[_val(add1[1])][_val(add2[1])][0];
   }
+  carry_flag = carry != SC_0;
+}
+
+/**
+ * Implements a binary SUB
+ */
+static void _sub(const char *val1, const char *val2, char *buffer)
+{
+  char *temp_buffer = alloca(calc_buffer_size); /* intermediate buffer to hold -val2 */
+
+  _negate(val2, temp_buffer);
+  _add(val1, temp_buffer, buffer);
 }
 
+/**
+ * Implements a binary MUL
+ */
 static void _mul(const char *val1, const char *val2, char *buffer)
 {
-  char temp_buffer[CALC_BUFFER_SIZE]; /* result buffer */
-  char neg_val1[CALC_BUFFER_SIZE];    /* abs of val1 */
-  char neg_val2[CALC_BUFFER_SIZE];    /* abs of val2 */
+  char *temp_buffer; /* result buffer */
+  char *neg_val1;    /* abs of val1 */
+  char *neg_val2;    /* abs of val2 */
 
   const char *mul, *add1, *add2;      /* intermediate result containers */
   char carry = SC_0;                  /* container for carries */
   char sign = 0;                      /* marks result sign */
   int c_inner, c_outer;               /* loop counters */
 
+  temp_buffer = alloca(calc_buffer_size);
+  neg_val1 = alloca(calc_buffer_size);
+  neg_val2 = alloca(calc_buffer_size);
+
   /* init result buffer to zeroes */
-  memset(temp_buffer, SC_0, CALC_BUFFER_SIZE);
+  memset(temp_buffer, SC_0, calc_buffer_size);
 
   /* the multiplication works only for positive values, for negative values *
    * it is necessary to negate them and adjust the result accordingly       */
@@ -537,11 +605,11 @@ static void _mul(const char *val1, const char *val2, char *buffer)
     sign ^= 1;
   }
 
-  for (c_outer = 0; c_outer < MAX_VALUE_SIZE; c_outer++)
+  for (c_outer = 0; c_outer < max_value_size; c_outer++)
   {
     if (val2[c_outer] != SC_0)
     {
-      for (c_inner = 0; c_inner < MAX_VALUE_SIZE; c_inner++)
+      for (c_inner = 0; c_inner < max_value_size; c_inner++)
       {
         /* do the following calculation:                                    *
          * Add the current carry, the value at position c_outer+c_inner     *
@@ -571,8 +639,8 @@ static void _mul(const char *val1, const char *val2, char *buffer)
       }
 
       /* A carry may hang over */
-      /* c_outer is always smaller than MAX_VALUE_SIZE! */
-      temp_buffer[MAX_VALUE_SIZE + c_outer] = carry;
+      /* c_outer is always smaller than max_value_size! */
+      temp_buffer[max_value_size + c_outer] = carry;
       carry = SC_0;
     }
   }
@@ -580,42 +648,46 @@ static void _mul(const char *val1, const char *val2, char *buffer)
   if (sign)
     _negate(temp_buffer, buffer);
   else
-    memcpy(buffer, temp_buffer, CALC_BUFFER_SIZE);
-}
-
-static void _sub(const char *val1, const char *val2, char *buffer)
-{
-  char temp_buffer[CALC_BUFFER_SIZE];  /* intermediate buffer to hold -val2 */
-
-  _negate(val2, temp_buffer);
-  _add(val1, temp_buffer, buffer);
+    memcpy(buffer, temp_buffer, calc_buffer_size);
 }
 
+/**
+ * Shift the buffer to left and add a 4 bit digit
+ */
 static void _push(const char digit, char *buffer)
 {
   int counter;
 
-  for (counter = CALC_BUFFER_SIZE - 2; counter >= 0; counter--)
+  for (counter = calc_buffer_size - 2; counter >= 0; counter--)
   {
     buffer[counter+1] = buffer[counter];
   }
   buffer[0] = digit;
 }
 
-/* XXX: This is MOST slow */
-static void _divmod(const char *dividend, const char *divisor, char *quot, char *rem)
+/**
+ * Implements truncating integer division and remainder.
+ *
+ * Note: This is MOST slow
+ */
+static void _divmod(const char *rDividend, const char *divisor, char *quot, char *rem)
 {
+  const char *dividend = rDividend;
   const char *minus_divisor;
-  char neg_val1[CALC_BUFFER_SIZE];
-  char neg_val2[CALC_BUFFER_SIZE];
+  char *neg_val1;
+  char *neg_val2;
 
-  char sign = 0;     /* remember result sign */
+  char div_sign = 0;     /* remember division result sign */
+  char rem_sign = 0;     /* remember remainder esult sign */
 
   int c_dividend;      /* loop counters */
 
+  neg_val1 = alloca(calc_buffer_size);
+  neg_val2 = alloca(calc_buffer_size);
+
   /* clear result buffer */
-  memset(quot, SC_0, CALC_BUFFER_SIZE);
-  memset(rem, SC_0, CALC_BUFFER_SIZE);
+  memset(quot, SC_0, calc_buffer_size);
+  memset(rem, SC_0, calc_buffer_size);
 
   /* if the divisor is zero this won't work (quot is zero) */
   if (sc_comp(divisor, quot) == 0) assert(0 && "division by zero!");
@@ -624,24 +696,21 @@ static void _divmod(const char *dividend, const char *divisor, char *quot, char
   if (sc_comp(dividend, quot) == 0)
     return;
 
-  if (_sign(dividend) == -1)
-  {
+  if (_sign(dividend) == -1) {
     _negate(dividend, neg_val1);
-    sign ^= 1;
+    div_sign ^= 1;
+    rem_sign ^= 1;
     dividend = neg_val1;
   }
 
   _negate(divisor, neg_val2);
-  if (_sign(divisor) == -1)
-  {
-    sign ^= 1;
+  if (_sign(divisor) == -1) {
+    div_sign ^= 1;
     minus_divisor = divisor;
     divisor = neg_val2;
   }
   else
-  {
     minus_divisor = neg_val2;
-  }
 
   /* if divisor >= dividend division is easy
    * (remember these are absolute values) */
@@ -652,14 +721,14 @@ static void _divmod(const char *dividend, const char *divisor, char *quot, char
       return;
 
     case -1: /* dividend < divisor */
-      memcpy(rem, dividend, CALC_BUFFER_SIZE);
+      memcpy(rem, rDividend, calc_buffer_size);
       return;
 
     default: /* unluckily division is necessary :( */
       break;
   }
 
-  for (c_dividend = CALC_BUFFER_SIZE - 1; c_dividend >= 0; c_dividend--)
+  for (c_dividend = calc_buffer_size - 1; c_dividend >= 0; c_dividend--)
   {
     _push(dividend[c_dividend], rem);
     _push(SC_0, quot);
@@ -681,15 +750,22 @@ static void _divmod(const char *dividend, const char *divisor, char *quot, char
     }
   }
 
+  /* sets carry if remainder is non-zero ??? */
   carry_flag = !sc_is_zero(rem);
 
-  if (sign)
-  {
+  if (div_sign)
     _negate(quot, quot);
+
+  if (rem_sign)
     _negate(rem, rem);
-  }
 }
 
+/**
+ * Implements a Shift Left, which can either preserve the sign bit
+ * or not.
+ *
+ * @todo Assertions seems to be wrong
+ */
 static void _shl(const char *val1, char *buffer, long offset, int radius, unsigned is_signed)
 {
   const char *shl;
@@ -707,7 +783,7 @@ static void _shl(const char *val1, char *buffer, long offset, int radius, unsign
   /* if shifting far enough the result is zero */
   if (offset >= radius)
   {
-    memset(buffer, SC_0, CALC_BUFFER_SIZE);
+    memset(buffer, SC_0, calc_buffer_size);
     return;
   }
 
@@ -741,22 +817,28 @@ static void _shl(const char *val1, char *buffer, long offset, int radius, unsign
   {
     /* this sets the upper bits of the leftmost digit */
     buffer[offset] = or_table[_val(buffer[offset])][_val(min_digit[bitoffset])];
-    for (counter = offset+1; counter < CALC_BUFFER_SIZE; counter++)
+    for (counter = offset+1; counter < calc_buffer_size; counter++)
     {
       buffer[counter] = SC_F;
     }
   }
   else if (is_signed && !_bitisset(buffer[offset], bitoffset))
   {
-    /* this unsets the upper bits of the leftmost digit */
+    /* this clears the upper bits of the leftmost digit */
     buffer[offset] = and_table[_val(buffer[offset])][_val(max_digit[bitoffset])];
-    for (counter = offset+1; counter < CALC_BUFFER_SIZE; counter++)
+    for (counter = offset+1; counter < calc_buffer_size; counter++)
     {
       buffer[counter] = SC_0;
     }
   }
 }
 
+/**
+ * Implements a Shift Right, which can either preserve the sign bit
+ * or not.
+ *
+ * @todo Assertions seems to be wrong
+ */
 static void _shr(const char *val1, char *buffer, long offset, int radius, unsigned is_signed, int signed_shift)
 {
   const char *shrs;
@@ -769,7 +851,6 @@ static void _shr(const char *val1, char *buffer, long offset, int radius, unsign
   int bitoffset = 0;
 
   assert((offset >= 0) || (0 && "negative rightshift"));
-  assert(((_sign(val1) != -1) || is_signed) || (0 && "unsigned mode and negative value"));
   assert(((!_bitisset(val1[(radius-1)/4], (radius-1)%4)) || !is_signed || (_sign(val1) == -1)) || (0 && "value is positive, should be negative"));
   assert(((_bitisset(val1[(radius-1)/4], (radius-1)%4)) || !is_signed || (_sign(val1) == 1)) || (0 && "value is negative, should be positive"));
 
@@ -781,7 +862,7 @@ static void _shr(const char *val1, char *buffer, long offset, int radius, unsign
     if (!sc_is_zero(val1)) {
       carry_flag = 1;
     }
-    memset(buffer, sign, CALC_BUFFER_SIZE);
+    memset(buffer, sign, calc_buffer_size);
     return;
   }
 
@@ -816,7 +897,7 @@ static void _shr(const char *val1, char *buffer, long offset, int radius, unsign
 
   /* the last digit is special in regard of signed/unsigned shift */
   bitoffset = radius%4;
-  msd = (radius/4<CALC_BUFFER_SIZE)?(val1[radius/4]):(sign);  /* most significant digit */
+  msd = (radius/4<calc_buffer_size)?(val1[radius/4]):(sign);  /* most significant digit */
 
   /* remove sign bits if mode was signed and this is an unsigned shift */
   if (!signed_shift && is_signed) {
@@ -835,23 +916,27 @@ static void _shr(const char *val1, char *buffer, long offset, int radius, unsign
   if (counter > 0) buffer[counter - 1] = or_table[_val(buffer[counter-1])][_val(shrs[1])];
 
   /* fill with SC_F or SC_0 depending on sign */
-  for (counter++; counter < CALC_BUFFER_SIZE; counter++)
+  for (counter++; counter < calc_buffer_size; counter++)
   {
     buffer[counter] = sign;
   }
 }
 
-/* positive: low-order -> high order, negative other direction */
+/**
+ * Implements a Rotate Right.
+ * positive: low-order -> high order, negative other direction
+ */
 static void _rot(const char *val1, char *buffer, long offset, int radius, unsigned is_signed)
 {
-  char temp1[CALC_BUFFER_SIZE];
-  char temp2[CALC_BUFFER_SIZE];
+  char *temp1, *temp2;
+  temp1 = alloca(calc_buffer_size);
+  temp2 = alloca(calc_buffer_size);
 
   offset = offset % radius;
 
-  /* rotation by multiples of the typelength is identity */
+  /* rotation by multiples of the type length is identity */
   if (offset == 0) {
-    memmove(buffer, val1, CALC_BUFFER_SIZE);
+    memmove(buffer, val1, calc_buffer_size);
     return;
   }
 
@@ -869,20 +954,52 @@ const void *sc_get_buffer(void)
   return (void*)calc_buffer;
 }
 
-const int sc_get_buffer_length(void)
+int sc_get_buffer_length(void)
 {
-  return CALC_BUFFER_SIZE;
+  return calc_buffer_size;
+}
+
+/**
+ * Do sign extension if the mode is signed, otherwise to zero extension.
+ */
+void sign_extend(char *calc_buffer, ir_mode *mode) {
+  int bits    = get_mode_size_bits(mode) - 1;
+  int nibble  = bits >> 2;
+  int max     = max_digit[bits & 3];
+  int i;
+
+  if (mode_is_signed(mode)) {
+    if (calc_buffer[nibble] > max) {
+      /* sign bit is set, we need sign expansion */
+
+      for (i = nibble + 1; i < calc_buffer_size; ++i)
+        calc_buffer[i] = SC_F;
+      calc_buffer[nibble] = or_table[(int)calc_buffer[nibble]][(int)sex_digit[bits & 3]];
+    } else {
+      /* set all bits to zero */
+      for (i = nibble + 1; i < calc_buffer_size; ++i)
+        calc_buffer[i] = SC_0;
+      calc_buffer[nibble] = and_table[(int)calc_buffer[nibble]][(int)zex_digit[bits & 3]];
+    }
+  } else {
+    /* do zero extension */
+    for (i = nibble + 1; i < calc_buffer_size; ++i)
+      calc_buffer[i] = SC_0;
+    calc_buffer[nibble] = and_table[(int)calc_buffer[nibble]][(int)zex_digit[bits & 3]];
+  }
 }
 
-/* XXX doesn't check for overflows */
-void sc_val_from_str(const char *str, unsigned int len, void *buffer)
+/* FIXME doesn't check for overflows */
+void sc_val_from_str(const char *str, unsigned int len, void *buffer, ir_mode *mode)
 {
   const char *orig_str = str;
   unsigned int orig_len = len;
 
   char sign = 0;
-  char base[CALC_BUFFER_SIZE];
-  char val[CALC_BUFFER_SIZE];
+  char *base, *val;
+
+  base = alloca(calc_buffer_size);
+  val = alloca(calc_buffer_size);
 
   /* verify valid pointers (not null) */
   assert(str);
@@ -892,8 +1009,8 @@ void sc_val_from_str(const char *str, unsigned int len, void *buffer)
   if (buffer == NULL) buffer = calc_buffer;
 
   CLEAR_BUFFER(buffer);
-  memset(base, SC_0, CALC_BUFFER_SIZE);
-  memset(val, SC_0, CALC_BUFFER_SIZE);
+  CLEAR_BUFFER(base);
+  CLEAR_BUFFER(val);
 
   /* strip leading spaces */
   while ((len > 0) && (*str == ' ')) { len--; str++; }
@@ -1016,9 +1133,10 @@ void sc_val_from_str(const char *str, unsigned int len, void *buffer)
   } /* while (len > 0 ) */
 
   if (sign)
-  {
     _negate(calc_buffer, calc_buffer);
-  }
+
+  /* beware: even if hex numbers have no sign, we need sign extension here */
+  sign_extend(calc_buffer, mode);
 }
 
 void sc_val_from_long(long value, void *buffer)
@@ -1032,7 +1150,7 @@ void sc_val_from_long(long value, void *buffer)
   sign = (value < 0);
   is_minlong = value == LONG_MIN;
 
-  /* use absolute value, special treatment of MIN_LONG */
+  /* use absolute value, special treatment of MIN_LONG to avoid overflow */
   if (sign) {
     if (is_minlong)
       value = -(value+1);
@@ -1042,13 +1160,12 @@ void sc_val_from_long(long value, void *buffer)
 
   CLEAR_BUFFER(buffer);
 
-  while ((value != 0) && (pos < (char*)buffer + CALC_BUFFER_SIZE))
+  while ((value != 0) && (pos < (char*)buffer + calc_buffer_size))
   {
     *pos++ = _digit(value & 0xf);
     value >>= 4;
   }
 
-
   if (sign) {
     if (is_minlong)
       _inc(buffer, buffer);
@@ -1059,14 +1176,14 @@ void sc_val_from_long(long value, void *buffer)
 
 void sc_val_from_ulong(unsigned long value, void *buffer)
 {
-  char *pos;
+  unsigned char *pos;
 
   if (buffer == NULL) buffer = calc_buffer;
   pos = buffer;
 
-  while (pos < (char*)buffer + CALC_BUFFER_SIZE)
+  while (pos < (unsigned char *)buffer + calc_buffer_size)
   {
-    *pos++ = _digit(value & 0xf);
+    *pos++ = (unsigned char)_digit(value & 0xf);
     value >>= 4;
   }
 }
@@ -1076,7 +1193,7 @@ long sc_val_to_long(const void *val)
   int i;
   long l = 0;
 
-  for (i = CALC_BUFFER_SIZE - 1; i >= 0; i--)
+  for (i = calc_buffer_size - 1; i >= 0; i--)
   {
     l = (l << 4) + _val(((char *)val)[i]);
   }
@@ -1101,7 +1218,7 @@ void sc_min_from_bits(unsigned int num_bits, unsigned int sign, void *buffer)
 
   *pos++ = min_digit[bits%4];
 
-  for (i++; i <= CALC_BUFFER_SIZE - 1; i++)
+  for (i++; i <= calc_buffer_size - 1; i++)
     *pos++ = SC_F;
 }
 
@@ -1120,17 +1237,19 @@ void sc_max_from_bits(unsigned int num_bits, unsigned int sign, void *buffer)
 
   *pos++ = max_digit[bits%4];
 
-  for (i++; i <= CALC_BUFFER_SIZE - 1; i++)
+  for (i++; i <= calc_buffer_size - 1; i++)
     *pos++ = SC_0;
 }
 
 void sc_calc(const void* value1, const void* value2, unsigned op, void *buffer)
 {
-  char unused_res[CALC_BUFFER_SIZE]; /* temp buffer holding unused result of divmod */
+  char *unused_res;   /* temp buffer holding unused result of divmod */
 
   const char *val1 = (const char *)value1;
   const char *val2 = (const char *)value2;
 
+  unused_res = alloca(calc_buffer_size);
+
   CLEAR_BUFFER(calc_buffer);
   carry_flag = 0;
 
@@ -1186,7 +1305,7 @@ void sc_calc(const void* value1, const void* value2, unsigned op, void *buffer)
 
   if ((buffer != NULL) && (buffer != calc_buffer))
   {
-    memcpy(buffer, calc_buffer, CALC_BUFFER_SIZE);
+    memcpy(buffer, calc_buffer, calc_buffer_size);
   }
 }
 
@@ -1225,13 +1344,13 @@ void sc_bitcalc(const void* value1, const void* value2, int radius, int sign, un
 
   if ((buffer != NULL) && (buffer != calc_buffer))
   {
-    memmove(buffer, calc_buffer, CALC_BUFFER_SIZE);
+    memmove(buffer, calc_buffer, calc_buffer_size);
   }
 }
 
 int sc_comp(const void* value1, const void* value2)
 {
-  int counter = CALC_BUFFER_SIZE - 1;
+  int counter = calc_buffer_size - 1;
   const char *val1 = (const char *)value1;
   const char *val2 = (const char *)value2;
 
@@ -1258,9 +1377,9 @@ int sc_get_highest_set_bit(const void *value)
   const char *val = (const char*)value;
   int high, counter;
 
-  high = CALC_BUFFER_SIZE * 4 - 1;
+  high = calc_buffer_size * 4 - 1;
 
-  for (counter = CALC_BUFFER_SIZE-1; counter >= 0; counter--) {
+  for (counter = calc_buffer_size-1; counter >= 0; counter--) {
     if (val[counter] == SC_0) high -= 4;
     else {
       if (val[counter] > SC_7) return high;
@@ -1281,7 +1400,7 @@ int sc_get_lowest_set_bit(const void *value)
   sign = (_sign(val)==1)?(SC_0):(SC_F);
   low = 0;
 
-  for (counter = 0; counter < CALC_BUFFER_SIZE; counter++) {
+  for (counter = 0; counter < calc_buffer_size; counter++) {
     if (val[counter] == SC_0) low += 4;
     else {
       if (val[counter] < SC_2) return low;
@@ -1298,7 +1417,7 @@ int sc_is_zero(const void *value)
   const char* val = (const char *)value;
   int counter;
 
-  for (counter = 0; counter < CALC_BUFFER_SIZE; counter++) {
+  for (counter = 0; counter < calc_buffer_size; counter++) {
     if (val[counter] != SC_0) return 0;
   }
   return 1;
@@ -1316,8 +1435,8 @@ int sc_had_carry(void)
 
 unsigned char sc_sub_bits(const void *value, int len, unsigned byte_ofs)
 {
-  const char *val     = (const char *)value;
-  unsigned nibble_ofs = 2 * byte_ofs;
+  const char *val = (const char *)value;
+  int nibble_ofs  = 2 * byte_ofs;
   unsigned char res;
 
   /* the current scheme uses one byte to store a nibble */
@@ -1333,17 +1452,14 @@ unsigned char sc_sub_bits(const void *value, int len, unsigned byte_ofs)
 
 /*
  * convert to a string
- * XXX Doesn't check buffer bounds
+ * FIXME: Doesn't check buffer bounds
  */
-const char *sc_print(const void *value, unsigned bits, enum base_t base)
+const char *sc_print(const void *value, unsigned bits, enum base_t base, int signed_mode)
 {
   static const char big_digits[]   = "0123456789ABCDEF";
   static const char small_digits[] = "0123456789abcdef";
 
-  char base_val[CALC_BUFFER_SIZE];
-  char div1_res[CALC_BUFFER_SIZE];
-  char div2_res[CALC_BUFFER_SIZE];
-  char rem_res[CALC_BUFFER_SIZE];
+  char *base_val, *div1_res, *div2_res, *rem_res;
   int counter, nibbles, i, sign;
   char x;
 
@@ -1353,12 +1469,17 @@ const char *sc_print(const void *value, unsigned bits, enum base_t base)
   char *pos;
   const char *digits = small_digits;
 
-  pos = output_buffer + BIT_PATTERN_SIZE;
+  base_val = alloca(calc_buffer_size);
+  div1_res = alloca(calc_buffer_size);
+  div2_res = alloca(calc_buffer_size);
+  rem_res  = alloca(calc_buffer_size);
+
+  pos = output_buffer + bit_pattern_size;
   *(--pos) = '\0';
 
   /* special case */
   if (bits == 0) {
-    bits = BIT_PATTERN_SIZE;
+    bits = bit_pattern_size;
 #ifdef STRCALC_DEBUG_FULLPRINT
     bits <<= 1;
 #endif
@@ -1423,22 +1544,22 @@ const char *sc_print(const void *value, unsigned bits, enum base_t base)
 
   case SC_DEC:
   case SC_OCT:
-    memset(base_val, SC_0, CALC_BUFFER_SIZE);
+    memset(base_val, SC_0, calc_buffer_size);
     base_val[0] = base == SC_DEC ? SC_A : SC_8;
 
     p    = val;
     sign = 0;
-    if (base == SC_DEC) {
+    if (signed_mode && base == SC_DEC) {
       /* check for negative values */
       if (_bit(val, bits - 1)) {
-       _negate(val, div2_res);
-       sign = 1;
-       p = div2_res;
+        _negate(val, div2_res);
+        sign = 1;
+        p = div2_res;
       }
     }
 
-    /* transfer data into oscilating buffers */
-    memset(div1_res, SC_0, CALC_BUFFER_SIZE);
+    /* transfer data into oscillating buffers */
+    memset(div1_res, SC_0, calc_buffer_size);
     for (counter = 0; counter < nibbles; ++counter)
       div1_res[counter] = p[counter];
 
@@ -1459,7 +1580,7 @@ const char *sc_print(const void *value, unsigned bits, enum base_t base)
       *(--pos) = digits[_val(rem_res[0])];
 
       x = 0;
-      for (i = 0; i < sizeof(div1_res); ++i)
+      for (i = 0; i < calc_buffer_size; ++i)
        x |= _val(m[i]);
 
       if (x == 0)
@@ -1483,25 +1604,26 @@ void init_strcalc(int precision)
     if (precision <= 0) precision = SC_DEFAULT_PRECISION;
 
     /* round up to multiple of 4 */
-    if (precision & 0x3) precision += 4 - (precision&0x3);
+    precision = (precision + 3) & ~3;
 
-    BIT_PATTERN_SIZE = (precision);
-    CALC_BUFFER_SIZE = (precision / 2);
-    MAX_VALUE_SIZE   = (precision / 4);
+    bit_pattern_size = (precision);
+    calc_buffer_size = (precision / 2);
+    max_value_size   = (precision / 4);
 
-    calc_buffer = malloc(CALC_BUFFER_SIZE+1 * sizeof(char));
-    output_buffer = malloc(BIT_PATTERN_SIZE+1 * sizeof(char));
-
-    if (calc_buffer == NULL || output_buffer == NULL)
-    {
-      assert(0 && "malloc failed");
-      exit(-1);
-    }
+    calc_buffer   = xmalloc(calc_buffer_size+1 * sizeof(char));
+    output_buffer = xmalloc(bit_pattern_size+1 * sizeof(char));
 
-    DEBUGPRINTF(("init strcalc: \n\tPRECISION: %d\n\tCALC_BUFFER_SIZE = %d\n\tMAX_VALUE_SIZE = %d\n\tbuffer pointer: %p\n", precision, CALC_BUFFER_SIZE, MAX_VALUE_SIZE, calc_buffer));
+    DEBUGPRINTF(("init strcalc: \n\tPRECISION: %d\n\tCALC_BUFFER_SIZE = %d\n\tMAX_VALUE_SIZE = %d\n\tbuffer pointer: %p\n", precision, calc_buffer_size, max_value_size, calc_buffer));
   }
 }
+
+
+void finish_strcalc() {
+  free(calc_buffer);   calc_buffer   = NULL;
+  free(output_buffer); output_buffer = NULL;
+}
+
 int sc_get_precision(void)
 {
-  return BIT_PATTERN_SIZE;
+  return bit_pattern_size;
 }