]> mj.ucw.cz Git - minsk.git/blob - minsk.c
Minsk: emulate shifts, try to emulate R1/R2/ACC faithfully
[minsk.git] / minsk.c
1 /*
2  *      Minsk-2 Emulator
3  *
4  *      (c) 2010 Martin Mares <mj@ucw.cz>
5  */
6
7 /*
8  * TODO:
9  *      - time limit
10  *      - error messages
11  *      - debugging/play mode
12  *      - implement printing
13  *      - floating point?
14  *      - we probably have to disable NOP
15  */
16
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <assert.h>
21
22 // Minsk-2 has 37-bit words in sign-magnitude representation (bit 36 = sign)
23 typedef unsigned long long int word;
24
25 #define WORD_MASK 01777777777777ULL
26 #define SIGN_MASK 01000000000000ULL
27 #define  VAL_MASK 00777777777777ULL
28
29 static int wsign(word w)
30 {
31   return (w & SIGN_MASK) ? -1 : 1;
32 }
33
34 static word wabs(word w)
35 {
36   return w & VAL_MASK;
37 }
38
39 #define WF(w) (wsign(w) < 0 ? '-' : '+'), wabs(w)
40
41 static long long wtoll(word w)
42 {
43   if (wsign(w) < 0)
44     return -wabs(w);
45   else
46     return wabs(w);
47 }
48
49 static word wfromll(long long x)
50 {
51   word w = ((x < 0) ? -x : x) & VAL_MASK;
52   if (x < 0)
53     w |= SIGN_MASK;
54   return w;
55 }
56
57 static double wtofrac(word w)
58 {
59   return (double)wtoll(w) / (double)(1ULL << 36);
60 }
61
62 static word wfromfrac(double d)
63 {
64   return wfromll((long long)(d * (double)(1ULL << 36)));
65 }
66
67 static int inrange(long long x)
68 {
69   return (x >= -(long long)VAL_MASK && x <= (long long)VAL_MASK);
70 }
71
72 static int fracinrange(double d)
73 {
74   return (d > -1. && d < 1.);
75 }
76
77 static int wexp(word w)
78 {
79   int exp = w & 077;
80   return (w & 0100 ? -exp : exp);
81 }
82
83 static word mem[4096];
84
85 static word rd(int addr)
86 {
87   word val = addr ? mem[addr] : 0;
88   printf("\tRD %04o = %c%012llo\n", addr, WF(val));
89   return val;
90 }
91
92 static void wr(int addr, word val)
93 {
94   assert(!(val & ~(WORD_MASK)));
95   printf("\tWR %04o = %c%012llo\n", addr, WF(val));
96   mem[addr] = val;
97 }
98
99 static int lino;
100
101 static void parse_error(char *msg)
102 {
103   printf("Ошибка входа (стр. %d): %s\n", lino, msg);
104   exit(1);
105 }
106
107 static void parse_in(void)
108 {
109   char line[80];
110   int addr = 0;
111
112   while (fgets(line, sizeof(line), stdin))
113     {
114       lino++;
115       char *eol = strchr(line, '\n');
116       if (!eol)
117         parse_error("Строка слишком долгая");
118       *eol = 0;
119
120       char *c = line;
121       if (c[0] == '@')
122         {
123           c++;
124           addr = 0;
125           for (int i=0; i<4; i++)
126             {
127               while (*c == ' ')
128                 c++;
129               if (*c >= '0' && *c <= '7')
130                 addr = 8*addr + *c++ - '0';
131               else
132                 parse_error("Плохая цифва");
133             }
134           while (*c == ' ')
135             c++;
136           if (*c)
137             parse_error("Адрес слишком долгий");
138           continue;
139         }
140
141       word w = 0;
142       if (*c == '-')
143         w = 1;
144       else if (*c != '+')
145         parse_error("Плохой знак");
146       c++;
147       for (int i=0; i<12; i++)
148         {
149           while (*c == ' ')
150             c++;
151           if (*c >= '0' && *c <= '7')
152             w = 8*w + *c++ - '0';
153           else
154             parse_error("Плохая цифва");
155         }
156       while (*c == ' ')
157         c++;
158       if (*c)
159         parse_error("Номер слишком долгий");
160       wr(addr++, w);
161       addr &= 07777;
162     }
163 }
164
165 static word acc;
166 static word r1, r2, current_ins;
167 static int flag_zero = 0;
168 static int ip = 00050;                  // Standard program start location
169 static int prev_ip;
170
171 static void stop(char *reason)
172 {
173   printf("MACHINE STOPPED -- %s\n", reason);
174   printf("IP:%04o ACC:%c%012llo R1:%c%012llo R2:%c%012llo\n", prev_ip, WF(acc), WF(r1), WF(r2));
175   exit(0);
176 }
177
178 static void over(void)
179 {
180   stop("OVERFLOW");
181 }
182
183 static void nofpu(void)
184 {
185   acc = current_ins;
186   stop("NO FPU");
187 }
188
189 static void notimp(void)
190 {
191   acc = current_ins;
192   stop("NOT IMPLEMENTED");
193 }
194
195 static void noins(void)
196 {
197   acc = current_ins;
198   stop("ILLEGAL INSTRUCTION");
199 }
200
201 static void run(void)
202 {
203   for (;;)
204     {
205       r2 = acc;
206       prev_ip = ip;
207       word w = mem[ip];
208       current_ins = w;
209
210       int op = (w >> 30) & 0177;        // Operation code
211       int ax = (w >> 28) & 3;           // Address extensions not supported
212       int ix = (w >> 24) & 15;          // Indexing
213       int x = (w >> 12) & 07777;        // Operands (original form)
214       int y = w & 07777;
215       int xi=x, yi=y;                   // (indexed form)
216       printf("@%04o  %c%02o %02o %04o %04o\n",
217         ip,
218         (w & SIGN_MASK) ? '-' : '+',
219         (int)((w >> 30) & 077),
220         (int)((w >> 24) & 077),
221         x,
222         y);
223       if (ix)
224         {
225           if (op != 0120)
226             {
227               word i = rd(ix);
228               xi = (xi + (int)((i >> 12) & 07777)) & 07777;
229               yi = (yi + (int)(i & 07777)) & 07777;
230               printf("\tIndexing -> %04o %04o\n", xi, yi);
231             }
232         }
233       ip = (ip+1) & 07777;
234
235       /* Arithmetic operations */
236
237       word a, b, c;
238       long long aa, bb, cc;
239       double ad, bd, cd;
240       int i;
241
242       auto void afetch(void);
243       void afetch(void)
244         {
245           if (op & 2)
246             a = r2;
247           else
248             a = rd(yi);
249           b = r1 = rd(xi);
250         }
251
252       auto void astore(word result);
253       void astore(word result)
254         {
255           acc = result;
256           if (op & 1)
257             wr(yi, acc);
258         }
259
260       if (ax)
261         op = -1;
262       switch (op)
263         {
264         case 000:               // NOP
265           break;
266         case 004 ... 007:       // XOR
267           afetch();
268           astore(a^b);
269           break;
270         case 010 ... 013:       // FIX addition
271           afetch();
272           cc = wtoll(a) + wtoll(b);
273           if (!inrange(cc))
274             over();
275           astore(wfromll(cc));
276           break;
277         case 014 ... 017:       // FP addition
278           nofpu();
279         case 020 ... 023:       // FIX subtraction
280           afetch();
281           cc = wtoll(a) - wtoll(b);
282           if (!inrange(cc))
283             over();
284           astore(wfromll(cc));
285           break;
286         case 024 ... 027:       // FP subtraction
287           nofpu();
288         case 030 ... 033:       // FIX multiplication
289           afetch();
290           // XXX: We ignore the rounding mode settings
291           cd = wtofrac(a) * wtofrac(b);
292           astore(wfromfrac(cd));
293           break;
294         case 034 ... 037:       // FP multiplication
295           nofpu();
296         case 040 ... 043:       // division
297           afetch();
298           ad = wtofrac(a);
299           bd = wtofrac(b);
300           if (!wabs(b))
301             stop("DIVISION BY ZERO");
302           cd = ad / bd;
303           if (!fracinrange(cd))
304             over();
305           astore(wfromfrac(cd));
306           break;
307         case 044 ... 047:       // FP division
308           nofpu();
309         case 050 ... 053:       // FIX subtraction of abs values
310           afetch();
311           cc = wabs(a) - wabs(b);
312           if (!inrange(cc))
313             over();
314           astore(wfromll(cc));
315           break;
316         case 054 ... 057:       // FP subtraction of abs values
317           nofpu();
318         case 060 ... 063:       // Shift logical
319           afetch();
320           i = wexp(b);
321           if (i <= -37 || i >= 37)
322             astore(0);
323           else if (i >= 0)
324             astore((a << i) & WORD_MASK);
325           else
326             astore(a >> (-i));
327           break;
328         case 064 ... 067:       // Shift arithmetical
329           afetch();
330           i = wexp(b);
331           aa = wabs(a);
332           if (i <= -36 || i >= 36)
333             cc = 0;
334           else if (i >= 0)
335             cc = (aa << i) & VAL_MASK;
336           else
337             cc = aa >> (-i);
338           astore((a & SIGN_MASK) | wfromll(cc));
339           break;
340         case 070 ... 073:       // And
341           afetch();
342           astore(a&b);
343           break;
344         case 074 ... 077:       // Or
345           afetch();
346           astore(a|b);
347           break;
348
349         case 0100:              // Halt
350           r1 = rd(x);
351           acc = rd(y);
352           stop("HALTED");
353         case 0103:              // I/O magtape
354           notimp();
355         case 0104:              // Disable rounding
356           notimp();
357         case 0105:              // Enable rounding
358           notimp();
359         case 0106:              // Interrupt control
360           notimp();
361         case 0107:              // Reverse tape
362           notimp();
363         case 0110:              // Move
364           wr(yi, r1 = acc = rd(xi));
365           break;
366         case 0111:              // Move negative
367           wr(yi, acc = (r1 = rd(xi)) ^ SIGN_MASK);
368           break;
369         case 0112:              // Move absolute value
370           wr(yi, acc = (r1 = rd(xi)) & VAL_MASK);
371           break;
372         case 0113:              // Read from keyboard
373           notimp();
374         case 0114:              // Copy sign
375           wr(yi, acc = rd(yi) ^ ((r1 = rd(xi)) & SIGN_MASK));
376           break;
377         case 0115:              // Read code from R1 (obscure)
378           notimp();
379         case 0116:              // Copy exponent
380           nofpu();
381         case 0117:              // I/O teletype
382           notimp();
383         case 0120:              // Loop
384           if (!ix)
385             noins();
386           a = r1 = rd(ix);
387           aa = (a >> 24) & 017777;
388           if (!aa)
389             break;
390           b = rd(y);            // (a mountain range near Prague)
391           acc = ((aa-1) << 24) |
392                 (((((a >> 12) & 07777) + (b >> 12) & 07777) & 07777) << 12) |
393                 (((a & 07777) + (b & 07777)) & 07777);
394           wr(ix, acc);
395           ip = x & 07777;
396           break;
397         case 0130:              // Jump
398           wr(y, r2);
399           ip = x & 07777;
400           break;
401         case 0131:              // Jump to subroutine
402           wr(y, (030ULL << 30) | ((ip & 07777ULL) << 12));
403           ip = x & 07777;
404           break;
405         case 0132:              // Jump if positive
406           if (wsign(r2) >= 0)
407             ip = x & 07777;
408           else
409             ip = y & 07777;
410           break;
411         case 0133:              // Jump if overflow
412           // Since we always trap on overflow, this instruction always jumps to the 1st address
413           ip = x & 07777;
414           break;
415         case 0134:              // Jump if zero
416           if (flag_zero)
417             ip = y & 07777;
418           else
419             ip = x & 07777;
420           break;
421         case 0135:              // Jump if key pressed
422           // No keys are ever pressed, so always jump to 2nd
423           ip = y & 07777;
424           break;
425         case 0136:              // Interrupt masking
426           notimp();
427         case 0137:              // Used only when reading from tape
428           notimp();
429         case 0140 ... 0147:     // I/O
430           notimp();
431         case 0150 ... 0154:     // I/O
432           notimp();
433         case 0160 ... 0163:     // I/O
434           notimp();
435         case 0170:              // FIX multiplication, bottom part
436           afetch();
437           if (wtofrac(a) * wtofrac(b) >= .1/(1ULL << 32))
438             over();
439           acc = wfromll(((unsigned long long)wabs(a) * (unsigned long long)wabs(b)) & VAL_MASK);
440           // XXX: What should be the sign? The book does not define that.
441           break;
442         case 0171:              // Modulo
443           afetch();
444           aa = wabs(a);
445           bb = wabs(b);
446           if (!bb)
447             stop("DIVISION BY ZERO");
448           cc = aa % bb;
449           if (wsign(b) < 0)
450             cc = -cc;
451           acc = wfromll(cc);
452           break;
453         case 0172:              // Add exponents
454           nofpu();
455         case 0173:              // Sub exponents
456           nofpu();
457         case 0174:              // Addition in one's complement
458           a = r1 = rd(xi);
459           b = rd(yi);
460           c = a + b;
461           if (c > VAL_MASK)
462             c = c - VAL_MASK;
463           wr(yi, c);
464           // XXX: The effect on the accumulator is undocumented, but likely to be as follows:
465           acc = c;
466           break;
467         case 0175:              // Normalization
468           nofpu();
469         case 0176:              // Population count
470           a = r1 = rd(xi);
471           cc = 0;
472           for (int i=0; i<36; i++)
473             if (a & (1ULL << i))
474               cc++;
475           // XXX: Guessing that acc gets a copy of the result
476           acc = wfromll(cc);
477           wr(yi, acc);
478           break;
479         default:
480           noins();
481         }
482
483       flag_zero = !acc;
484       printf("\tACC:%c%012llo R1:%c%012llo R2:%c%012llo Z:%d\n", WF(acc), WF(r1), WF(r2), flag_zero);
485     }
486 }
487
488 int main(void)
489 {
490   parse_in();
491   run();
492   return 0;
493 }