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