4 * (c) 2010 Martin Mares <mj@ucw.cz>
9 * - debugging/play mode
10 * - we probably have to disable NOP
14 * Things that are not implemented:
17 * - exact behavior of accumulator/R1/R2 (the manual lacks details)
18 * - exact behavior of negative zero
19 * - I/O instructions for devices that are not emulated (paper tape
20 * reader and puncher, card reader and puncher, magnetic tape unit)
31 static int cpu_quota = -1;
32 static int print_quota = -1;
34 // Minsk-2 has 37-bit words in sign-magnitude representation (bit 36 = sign)
35 typedef unsigned long long int word;
37 #define WORD_MASK 01777777777777ULL
38 #define SIGN_MASK 01000000000000ULL
39 #define VAL_MASK 00777777777777ULL
41 static int wsign(word w)
43 return (w & SIGN_MASK) ? -1 : 1;
46 static word wabs(word w)
51 #define WF(w) (wsign(w) < 0 ? '-' : '+'), wabs(w)
53 static long long wtoll(word w)
61 static word wfromll(long long x)
63 word w = ((x < 0) ? -x : x) & VAL_MASK;
69 static double wtofrac(word w)
71 return (double)wtoll(w) / (double)(1ULL << 36);
74 static word wfromfrac(double d)
76 return wfromll((long long)(d * (double)(1ULL << 36)));
79 static int int_in_range(long long x)
81 return (x >= -(long long)VAL_MASK && x <= (long long)VAL_MASK);
84 static int frac_in_range(double d)
86 return (d > -1. && d < 1.);
89 static int wexp(word w)
92 return (w & 0100 ? -exp : exp);
95 static word wputexp(word w, int exp)
97 return ((w & ~(word)0177) | ((exp < 0) ? 0100 | (-exp) : exp));
100 static int wmanti(word w)
102 return ((w >> 8) & ((1 << 28) - 1));
105 static double wtofloat(word w)
107 double x = wmanti(w);
108 return ldexp(x, wexp(w) - 28);
111 static int float_in_range(double x)
114 return (x <= ldexp((1 << 28) - 1, 63 - 28));
117 static word wfromfloat(double x, int normalized)
126 double m = frexp(x, &exp);
127 word mm = (word) ldexp(m, 28);
132 if (normalized || exp < -91)
150 static word mem[4096];
152 static word rd(int addr)
154 word val = addr ? mem[addr] : 0;
156 printf("\tRD %04o = %c%012llo\n", addr, WF(val));
160 static void wr(int addr, word val)
162 assert(!(val & ~(WORD_MASK)));
164 printf("\tWR %04o = %c%012llo\n", addr, WF(val));
170 static void parse_error(char *msg)
172 printf("Ошибка входа (стр. %d): %s\n", lino, msg);
176 static void parse_in(void)
181 while (fgets(line, sizeof(line), stdin))
184 char *eol = strchr(line, '\n');
186 parse_error("Строка слишком долгая");
190 if (!c[0] || c[0] == ';')
196 for (int i=0; i<4; i++)
200 if (*c >= '0' && *c <= '7')
201 addr = 8*addr + *c++ - '0';
203 parse_error("Плохая цифва");
208 parse_error("Адрес слишком долгий");
216 parse_error("Плохой знак");
218 for (int i=0; i<12; i++)
222 if (*c >= '0' && *c <= '7')
223 w = 8*w + *c++ - '0';
225 parse_error("Плохая цифва");
230 parse_error("Номер слишком долгий");
237 static word r1, r2, current_ins;
238 static int ip = 00050; // Standard program start location
241 static void stop(char *reason)
243 printf("Машина остановлена -- %s\n", reason);
244 printf("СчАК:%04o См:%c%012llo Р1:%c%012llo Р2:%c%012llo\n", prev_ip, WF(acc), WF(r1), WF(r2));
248 static void over(void)
250 stop("Аварийный останов");
253 static void notimp(void)
256 stop("Устройство разбитое");
259 static void noins(void)
262 stop("Эту команду не знаю");
265 static uint16_t linebuf[128];
267 static uint16_t russian_chars[64] = {
268 '0', '1', '2', '3', '4', '5', '6', '7', // 0x
269 '8', '9', '+', '-', '/', ',', '.', ' ', // 1x
270 0x2169, '^', '(', ')', 0x00d7, '=', ';', '[', // 2x
271 ']', '*', '`', '\'', 0x2260, '<', '>', ':', // 3x
272 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, // 4x
273 0x418, 0x419, 0x41a, 0x41b, 0x41c, 0x41d, 0x41e, 0x41f, // 5x
274 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, // 6x
275 0x428, 0x429, 0x42b, 0x42c, 0x42d, 0x42e, 0x42f, 0x2013 // 7x
278 static uint16_t latin_chars[64] = {
279 '0', '1', '2', '3', '4', '5', '6', '7', // 0x
280 '8', '9', '+', '-', '/', ',', '.', ' ', // 1x
281 0x2169, '^', '(', ')', 0x00d7, '=', ';', '[', // 2x
282 ']', '*', '`', '\'', 0x2260, '<', '>', ':', // 3x
283 'A', 'B', 'W', 'G', 'D', 'E', 'V', 'Z', // 4x
284 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 5x
285 'R', 'S', 'T', 'U', 'F', 'H', 'C', ' ', // 6x
286 ' ', ' ', 'Y', 'X', ' ', ' ', 'Q', 0x2013 // 7x
289 static void print_line(int r)
292 * Meaning of bits of r:
293 * 0 = perform line feed
299 if (print_quota > 0 && !--print_quota)
300 stop("Бумага дошла - нужно ехать в Сивирь про новую");
301 for (int i=0; i<128; i++)
310 putchar(0xc0 | (ch >> 6));
311 putchar(0x80 | (ch & 0x3f));
315 putchar(0xe0 | (ch >> 12));
316 putchar(0x80 | ((ch >> 6) & 0x3f));
317 putchar(0x80 | (ch & 0x3f));
322 memset(linebuf, 0, sizeof(linebuf));
330 static void print_ins(int x, int y)
334 int r = (x >> 9) & 7;
347 case 0: // Decimal float
348 fmt = "+dddddddx+xbd";
350 case 1: // Octal number
351 fmt = "+oooooooooooo";
353 case 2: // Decimal fixed
356 case 3: // Decimal unsigned
360 case 4: // One Russian symbol
364 case 5: // Russian text
367 case 6: // One Latin symbol
371 default: // Latin text
388 ch = (yy & (1ULL << bit)) ? '-' : '+';
392 ch = '0' + ((yy >> bit) & 1);
396 ch = '0' + ((yy >> bit) & 7);
400 ch = '0' + ((yy >> bit) & 15);
406 ch = russian_chars[(yy >> bit) & 077];
410 ch = latin_chars[(yy >> bit) & 077];
418 if (ch == '0' || ch == ' ')
424 pos = (pos+1) & 0177;
429 static void run(void)
438 int op = (w >> 30) & 0177; // Operation code
439 int ax = (w >> 28) & 3; // Address extensions not supported
440 int ix = (w >> 24) & 15; // Indexing
441 int x = (w >> 12) & 07777; // Operands (original form)
443 int xi=x, yi=y; // (indexed form)
445 printf("@%04o %c%02o %02o %04o %04o\n",
447 (w & SIGN_MASK) ? '-' : '+',
448 (int)((w >> 30) & 077),
449 (int)((w >> 24) & 077),
457 xi = (xi + (int)((i >> 12) & 07777)) & 07777;
458 yi = (yi + (int)(i & 07777)) & 07777;
460 printf("\tIndexing -> %04o %04o\n", xi, yi);
465 if (cpu_quota > 0 && !--cpu_quota)
468 /* Arithmetic operations */
471 long long aa, bb, cc;
475 auto void afetch(void);
485 auto void astore(word result);
486 void astore(word result)
493 auto void astore_int(long long x);
494 void astore_int(long long x)
496 if (!int_in_range(x))
501 auto void astore_frac(double f);
502 void astore_frac(double f)
504 if (!frac_in_range(f))
506 astore(wfromfrac(f));
509 auto void astore_float(double f);
510 void astore_float(double f)
512 if (!float_in_range(f))
514 astore(wfromfloat(f, 0));
523 case 004 ... 007: // XOR
527 case 010 ... 013: // FIX addition
529 astore_int(wtoll(a) + wtoll(b));
531 case 014 ... 017: // FP addition
533 astore_float(wtofloat(a) + wtofloat(b));
535 case 020 ... 023: // FIX subtraction
537 astore_int(wtoll(a) - wtoll(b));
539 case 024 ... 027: // FP subtraction
541 astore_float(wtofloat(a) - wtofloat(b));
543 case 030 ... 033: // FIX multiplication
545 astore_frac(wtofrac(a) * wtofrac(b));
547 case 034 ... 037: // FP multiplication
549 astore_float(wtofloat(a) * wtofloat(b));
551 case 040 ... 043: // FIX division
557 astore_frac(ad / bd);
559 case 044 ... 047: // FP division
563 if (!bd || wexp(b) < -63)
565 astore_float(ad / bd);
567 case 050 ... 053: // FIX subtraction of abs values
569 astore_int(wabs(a) - wabs(b));
571 case 054 ... 057: // FP subtraction of abs values
573 astore_float(fabs(wtofloat(a)) - fabs(wtofloat(b)));
575 case 060 ... 063: // Shift logical
578 if (i <= -37 || i >= 37)
581 astore((a << i) & WORD_MASK);
585 case 064 ... 067: // Shift arithmetical
589 if (i <= -36 || i >= 36)
592 cc = (aa << i) & VAL_MASK;
595 astore((a & SIGN_MASK) | wfromll(cc));
597 case 070 ... 073: // And
601 case 074 ... 077: // Or
609 stop("Останов машины");
610 case 0103: // I/O magtape
612 case 0104: // Disable rounding
614 case 0105: // Enable rounding
616 case 0106: // Interrupt control
618 case 0107: // Reverse tape
621 wr(yi, r1 = acc = rd(xi));
623 case 0111: // Move negative
624 wr(yi, acc = (r1 = rd(xi)) ^ SIGN_MASK);
626 case 0112: // Move absolute value
627 wr(yi, acc = (r1 = rd(xi)) & VAL_MASK);
629 case 0113: // Read from keyboard
631 case 0114: // Copy sign
632 wr(yi, acc = rd(yi) ^ ((r1 = rd(xi)) & SIGN_MASK));
634 case 0115: // Read code from R1 (obscure)
636 case 0116: // Copy exponent
637 wr(yi, acc = wputexp(rd(yi), wexp(r1 = rd(xi))));
639 case 0117: // I/O teletype
645 aa = (a >> 24) & 017777;
648 b = rd(y); // (a mountain range near Prague)
649 acc = ((aa-1) << 24) |
650 (((((a >> 12) & 07777) + (b >> 12) & 07777) & 07777) << 12) |
651 (((a & 07777) + (b & 07777)) & 07777);
659 case 0131: // Jump to subroutine
660 wr(y, acc = ((030ULL << 30) | ((ip & 07777ULL) << 12)));
663 case 0132: // Jump if positive
669 case 0133: // Jump if overflow
670 // Since we always trap on overflow, this instruction always jumps to the 1st address
673 case 0134: // Jump if zero
679 case 0135: // Jump if key pressed
680 // No keys are ever pressed, so always jump to 2nd
683 case 0136: // Interrupt masking
685 case 0137: // Used only when reading from tape
687 case 0140 ... 0147: // I/O
689 case 0150 ... 0154: // I/O
691 case 0160 ... 0161: // I/O
693 case 0162: // Printing
698 case 0170: // FIX multiplication, bottom part
700 if (wtofrac(a) * wtofrac(b) >= .1/(1ULL << 32))
702 acc = wfromll(((unsigned long long)wabs(a) * (unsigned long long)wabs(b)) & VAL_MASK);
703 // XXX: What should be the sign? The book does not define that.
716 case 0172: // Add exponents
719 i = wexp(a) + wexp(b);
720 if (i < -63 || i > 63)
725 case 0173: // Sub exponents
728 i = wexp(b) - wexp(a);
729 if (i < -63 || i > 63)
734 case 0174: // Addition in one's complement
741 // XXX: The effect on the accumulator is undocumented, but likely to be as follows:
744 case 0175: // Normalization
749 wr((yi+1) & 07777, 0);
757 while (!(a & (SIGN_MASK >> 1)))
764 wr((yi+1) & 07777, i);
767 case 0176: // Population count
770 for (int i=0; i<36; i++)
773 // XXX: Guessing that acc gets a copy of the result
782 printf("\tACC:%c%012llo R1:%c%012llo R2:%c%012llo\n", WF(acc), WF(r1), WF(r2));