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