/*
* TODO:
- * - time limit
* - error messages
* - debugging/play mode
- * - implement printing
- * - floating point?
* - we probably have to disable NOP
*/
+/*
+ * Things that are not implemented:
+ * - rounding modes
+ * - exact behavior of accumulator/R1/R2 (the manual lacks details)
+ * - exact behavior of negative zero
+ */
+
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
+#include <inttypes.h>
#include <assert.h>
+#include <math.h>
static int trace = 3;
+static int cpu_quota = -1;
+static int print_quota = -1;
// Minsk-2 has 37-bit words in sign-magnitude representation (bit 36 = sign)
typedef unsigned long long int word;
return wfromll((long long)(d * (double)(1ULL << 36)));
}
-static int inrange(long long x)
+static int int_in_range(long long x)
{
return (x >= -(long long)VAL_MASK && x <= (long long)VAL_MASK);
}
-static int fracinrange(double d)
+static int frac_in_range(double d)
{
return (d > -1. && d < 1.);
}
return (w & 0100 ? -exp : exp);
}
+static word wputexp(word w, int exp)
+{
+ return ((w & ~(word)0177) | ((exp < 0) ? 0100 | (-exp) : exp));
+}
+
+static int wmanti(word w)
+{
+ return ((w >> 8) & ((1 << 28) - 1));
+}
+
+static double wtofloat(word w)
+{
+ double x = wmanti(w);
+ return ldexp(x, wexp(w) - 28);
+}
+
+static int float_in_range(double x)
+{
+ x = fabs(x);
+ return (x <= ldexp((1 << 28) - 1, 63 - 28));
+}
+
+static word wfromfloat(double x, int normalized)
+{
+ word w = 0;
+ if (x < 0)
+ {
+ w |= SIGN_MASK;
+ x = -x;
+ }
+ int exp;
+ double m = frexp(x, &exp);
+ word mm = (word) ldexp(m, 28);
+ if (exp > 63)
+ assert(0);
+ else if (exp < -63)
+ {
+ if (normalized || exp < -91)
+ mm=0, exp=0;
+ else
+ {
+ mm >>= -exp - 63;
+ exp = -63;
+ }
+ }
+ w |= mm << 8;
+ if (exp < 0)
+ {
+ w |= 0100;
+ exp = -exp;
+ }
+ w |= exp;
+ return w;
+}
+
static word mem[4096];
static word rd(int addr)
*eol = 0;
char *c = line;
+ if (!c[0] || c[0] == ';')
+ continue;
if (c[0] == '@')
{
c++;
static word acc;
static word r1, r2, current_ins;
-static int flag_zero = 0;
static int ip = 00050; // Standard program start location
static int prev_ip;
stop("OVERFLOW");
}
-static void nofpu(void)
-{
- acc = current_ins;
- stop("NO FPU");
-}
-
static void notimp(void)
{
acc = current_ins;
stop("ILLEGAL INSTRUCTION");
}
+static uint16_t linebuf[128];
+
+static uint16_t russian_chars[64] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', // 0x
+ '8', '9', '+', '-', '/', ',', '.', ' ', // 1x
+ 0x2169, '^', '(', ')', 0x00d7, '=', ';', '[', // 2x
+ ']', '*', '`', '\'', 0x2260, '<', '>', ':', // 3x
+ 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, // 4x
+ 0x418, 0x419, 0x41a, 0x41b, 0x41c, 0x41d, 0x41e, 0x41f, // 5x
+ 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, // 6x
+ 0x428, 0x429, 0x42b, 0x42c, 0x42d, 0x42e, 0x42f, 0x2013 // 7x
+};
+
+static uint16_t latin_chars[64] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', // 0x
+ '8', '9', '+', '-', '/', ',', '.', ' ', // 1x
+ 0x2169, '^', '(', ')', 0x00d7, '=', ';', '[', // 2x
+ ']', '*', '`', '\'', 0x2260, '<', '>', ':', // 3x
+ 'A', 'B', 'W', 'G', 'D', 'E', 'V', 'Z', // 4x
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 5x
+ 'R', 'S', 'T', 'U', 'F', 'H', 'C', ' ', // 6x
+ ' ', ' ', 'Y', 'X', ' ', ' ', 'Q', 0x2013 // 7x
+};
+
+static void print_line(int r)
+{
+ /*
+ * Meaning of bits of r:
+ * 0 = perform line feed
+ * 1 = clear buffer
+ * 2 = actually print
+ */
+ if (r & 4)
+ {
+ if (print_quota > 0 && !--print_quota)
+ stop("OUT OF PAPER");
+ for (int i=0; i<128; i++)
+ {
+ int ch = linebuf[i];
+ if (!ch)
+ ch = ' ';
+ if (ch < 0x80)
+ putchar(ch);
+ else if (ch < 0x800)
+ {
+ putchar(0xc0 | (ch >> 6));
+ putchar(0x80 | (ch & 0x3f));
+ }
+ else
+ {
+ putchar(0xe0 | (ch >> 12));
+ putchar(0x80 | ((ch >> 6) & 0x3f));
+ putchar(0x80 | (ch & 0x3f));
+ }
+ }
+ }
+ if (r & 2)
+ memset(linebuf, 0, sizeof(linebuf));
+ if (r & 1)
+ putchar('\n');
+ else if (r & 4)
+ putchar('\r');
+ fflush(stdout);
+}
+
static void print_ins(int x, int y)
{
word yy = rd(y);
if (x & 0400)
{
- // r bit 0 = line feed
- // r bit 1 = clear buffer
- // r bit 2 = print
+ print_line(r);
return;
}
+ char *fmt;
+ int bit = 37;
+ int eat = 0;
switch (r)
{
case 0: // Decimal float
+ fmt = "+dddddddx+xbd";
+ break;
case 1: // Octal number
+ fmt = "+oooooooooooo";
+ break;
case 2: // Decimal fixed
+ fmt = "+ddddddddd";
+ break;
case 3: // Decimal unsigned
+ fmt = "x ddddddddd";
+ eat = 1;
+ break;
case 4: // One Russian symbol
+ bit = 6;
+ fmt = "r";
+ break;
case 5: // Russian text
+ fmt = "xrrrrrr";
+ break;
case 6: // One Latin symbol
- case 7: // Latin text
- noins();
+ bit = 6;
+ fmt = "l";
+ break;
+ default: // Latin text
+ fmt = "xllllll";
+ }
+
+ while (*fmt)
+ {
+ int ch;
+ switch (*fmt++)
+ {
+ case 'x':
+ bit--;
+ continue;
+ case ' ':
+ ch = ' ';
+ break;
+ case '+':
+ bit--;
+ ch = (yy & (1ULL << bit)) ? '-' : '+';
+ break;
+ case 'b':
+ bit--;
+ ch = '0' + ((yy >> bit) & 1);
+ break;
+ case 'o':
+ bit -= 3;
+ ch = '0' + ((yy >> bit) & 7);
+ break;
+ case 'd':
+ bit -= 4;
+ ch = '0' + ((yy >> bit) & 15);
+ if (ch > '0' + 9)
+ ch += 7;
+ break;
+ case 'r':
+ bit -= 6;
+ ch = russian_chars[(yy >> bit) & 077];
+ break;
+ case 'l':
+ bit -= 6;
+ ch = latin_chars[(yy >> bit) & 077];
+ break;
+ default:
+ assert(0);
+ }
+
+ if (eat && *fmt)
+ {
+ if (ch == '0' || ch == ' ')
+ ch = ' ';
+ else
+ eat = 0;
+ }
+ linebuf[pos] = ch;
+ pos = (pos+1) & 0177;
}
+ assert(!bit);
}
static void run(void)
}
ip = (ip+1) & 07777;
+ if (cpu_quota > 0 && !--cpu_quota)
+ stop("TIMED OUT");
+
/* Arithmetic operations */
word a, b, c;
long long aa, bb, cc;
- double ad, bd, cd;
+ double ad, bd;
int i;
auto void afetch(void);
wr(yi, acc);
}
+ auto void astore_int(long long x);
+ void astore_int(long long x)
+ {
+ if (!int_in_range(x))
+ over();
+ astore(wfromll(x));
+ }
+
+ auto void astore_frac(double f);
+ void astore_frac(double f)
+ {
+ if (!frac_in_range(f))
+ over();
+ astore(wfromfrac(f));
+ }
+
+ auto void astore_float(double f);
+ void astore_float(double f)
+ {
+ if (!float_in_range(f))
+ over();
+ astore(wfromfloat(f, 0));
+ }
+
if (ax)
op = -1;
switch (op)
break;
case 010 ... 013: // FIX addition
afetch();
- cc = wtoll(a) + wtoll(b);
- if (!inrange(cc))
- over();
- astore(wfromll(cc));
+ astore_int(wtoll(a) + wtoll(b));
break;
case 014 ... 017: // FP addition
- nofpu();
+ afetch();
+ astore_float(wtofloat(a) + wtofloat(b));
+ break;
case 020 ... 023: // FIX subtraction
afetch();
- cc = wtoll(a) - wtoll(b);
- if (!inrange(cc))
- over();
- astore(wfromll(cc));
+ astore_int(wtoll(a) - wtoll(b));
break;
case 024 ... 027: // FP subtraction
- nofpu();
+ afetch();
+ astore_float(wtofloat(a) - wtofloat(b));
+ break;
case 030 ... 033: // FIX multiplication
afetch();
- // XXX: We ignore the rounding mode settings
- cd = wtofrac(a) * wtofrac(b);
- astore(wfromfrac(cd));
+ astore_frac(wtofrac(a) * wtofrac(b));
break;
case 034 ... 037: // FP multiplication
- nofpu();
- case 040 ... 043: // division
+ afetch();
+ astore_float(wtofloat(a) * wtofloat(b));
+ break;
+ case 040 ... 043: // FIX division
afetch();
ad = wtofrac(a);
bd = wtofrac(b);
if (!wabs(b))
stop("DIVISION BY ZERO");
- cd = ad / bd;
- if (!fracinrange(cd))
- over();
- astore(wfromfrac(cd));
+ astore_frac(ad / bd);
break;
case 044 ... 047: // FP division
- nofpu();
+ afetch();
+ ad = wtofloat(a);
+ bd = wtofloat(b);
+ if (!bd)
+ stop("DIVISION BY ZERO");
+ astore_float(ad / bd);
+ break;
case 050 ... 053: // FIX subtraction of abs values
afetch();
- cc = wabs(a) - wabs(b);
- if (!inrange(cc))
- over();
- astore(wfromll(cc));
+ astore_int(wabs(a) - wabs(b));
break;
case 054 ... 057: // FP subtraction of abs values
- nofpu();
+ afetch();
+ astore_float(fabs(wtofloat(a)) - fabs(wtofloat(b)));
+ break;
case 060 ... 063: // Shift logical
afetch();
i = wexp(b);
case 0115: // Read code from R1 (obscure)
notimp();
case 0116: // Copy exponent
- nofpu();
+ wr(yi, acc = wputexp(rd(yi), wexp(r1 = rd(xi))));
+ break;
case 0117: // I/O teletype
notimp();
case 0120: // Loop
ip = x;
break;
case 0134: // Jump if zero
- if (flag_zero)
+ if (!wabs(r2))
ip = y;
else
ip = x;
acc = wfromll(cc);
break;
case 0172: // Add exponents
- nofpu();
+ a = r1 = rd(xi);
+ b = rd(yi);
+ i = wexp(a) + wexp(b);
+ if (i < -63 || i > 63)
+ over();
+ acc = wputexp(b, i);
+ wr(yi, acc);
+ break;
case 0173: // Sub exponents
- nofpu();
+ a = r1 = rd(xi);
+ b = rd(yi);
+ i = wexp(b) - wexp(a);
+ if (i < -63 || i > 63)
+ over();
+ acc = wputexp(b, i);
+ wr(yi, acc);
+ break;
case 0174: // Addition in one's complement
a = r1 = rd(xi);
b = rd(yi);
acc = c;
break;
case 0175: // Normalization
- nofpu();
+ a = r1 = rd(xi);
+ if (!wabs(a))
+ {
+ wr(yi, 0);
+ wr((yi+1) & 07777, 0);
+ acc = 0;
+ }
+ else
+ {
+ i = 0;
+ acc = a & SIGN_MASK;
+ a &= VAL_MASK;
+ while (!(a & (SIGN_MASK >> 1)))
+ {
+ a <<= 1;
+ i++;
+ }
+ acc |= a;
+ wr(yi, acc);
+ wr((yi+1) & 07777, i);
+ }
+ break;
case 0176: // Population count
a = r1 = rd(xi);
cc = 0;
noins();
}
- flag_zero = !acc;
if (trace > 1)
- printf("\tACC:%c%012llo R1:%c%012llo R2:%c%012llo Z:%d\n", WF(acc), WF(r1), WF(r2), flag_zero);
+ printf("\tACC:%c%012llo R1:%c%012llo R2:%c%012llo\n", WF(acc), WF(r1), WF(r2));
}
}