]> mj.ucw.cz Git - minsk.git/blob - minsk.c
Minsk: Simplify jumps, first bits of printing
[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 print_ins(int x, int y)
206 {
207   word yy = rd(y);
208   int pos = x & 0177;
209   int r = (x >> 9) & 7;
210
211   if (x & 0400)
212     {
213       // r bit 0 = line feed
214       // r bit 1 = clear buffer
215       // r bit 2 = print
216       return;
217     }
218
219   switch (r)
220     {
221     case 0:                             // Decimal float
222     case 1:                             // Octal number
223     case 2:                             // Decimal fixed
224     case 3:                             // Decimal unsigned
225     case 4:                             // One Russian symbol
226     case 5:                             // Russian text
227     case 6:                             // One Latin symbol
228     case 7:                             // Latin text
229       noins();
230     }
231 }
232
233 static void run(void)
234 {
235   for (;;)
236     {
237       r2 = acc;
238       prev_ip = ip;
239       word w = mem[ip];
240       current_ins = w;
241
242       int op = (w >> 30) & 0177;        // Operation code
243       int ax = (w >> 28) & 3;           // Address extensions not supported
244       int ix = (w >> 24) & 15;          // Indexing
245       int x = (w >> 12) & 07777;        // Operands (original form)
246       int y = w & 07777;
247       int xi=x, yi=y;                   // (indexed form)
248       if (trace)
249         printf("@%04o  %c%02o %02o %04o %04o\n",
250           ip,
251           (w & SIGN_MASK) ? '-' : '+',
252           (int)((w >> 30) & 077),
253           (int)((w >> 24) & 077),
254           x,
255           y);
256       if (ix)
257         {
258           if (op != 0120)
259             {
260               word i = rd(ix);
261               xi = (xi + (int)((i >> 12) & 07777)) & 07777;
262               yi = (yi + (int)(i & 07777)) & 07777;
263               if (trace > 2)
264                 printf("\tIndexing -> %04o %04o\n", xi, yi);
265             }
266         }
267       ip = (ip+1) & 07777;
268
269       /* Arithmetic operations */
270
271       word a, b, c;
272       long long aa, bb, cc;
273       double ad, bd, cd;
274       int i;
275
276       auto void afetch(void);
277       void afetch(void)
278         {
279           if (op & 2)
280             a = r2;
281           else
282             a = rd(yi);
283           b = r1 = rd(xi);
284         }
285
286       auto void astore(word result);
287       void astore(word result)
288         {
289           acc = result;
290           if (op & 1)
291             wr(yi, acc);
292         }
293
294       if (ax)
295         op = -1;
296       switch (op)
297         {
298         case 000:               // NOP
299           break;
300         case 004 ... 007:       // XOR
301           afetch();
302           astore(a^b);
303           break;
304         case 010 ... 013:       // FIX addition
305           afetch();
306           cc = wtoll(a) + wtoll(b);
307           if (!inrange(cc))
308             over();
309           astore(wfromll(cc));
310           break;
311         case 014 ... 017:       // FP addition
312           nofpu();
313         case 020 ... 023:       // FIX subtraction
314           afetch();
315           cc = wtoll(a) - wtoll(b);
316           if (!inrange(cc))
317             over();
318           astore(wfromll(cc));
319           break;
320         case 024 ... 027:       // FP subtraction
321           nofpu();
322         case 030 ... 033:       // FIX multiplication
323           afetch();
324           // XXX: We ignore the rounding mode settings
325           cd = wtofrac(a) * wtofrac(b);
326           astore(wfromfrac(cd));
327           break;
328         case 034 ... 037:       // FP multiplication
329           nofpu();
330         case 040 ... 043:       // division
331           afetch();
332           ad = wtofrac(a);
333           bd = wtofrac(b);
334           if (!wabs(b))
335             stop("DIVISION BY ZERO");
336           cd = ad / bd;
337           if (!fracinrange(cd))
338             over();
339           astore(wfromfrac(cd));
340           break;
341         case 044 ... 047:       // FP division
342           nofpu();
343         case 050 ... 053:       // FIX subtraction of abs values
344           afetch();
345           cc = wabs(a) - wabs(b);
346           if (!inrange(cc))
347             over();
348           astore(wfromll(cc));
349           break;
350         case 054 ... 057:       // FP subtraction of abs values
351           nofpu();
352         case 060 ... 063:       // Shift logical
353           afetch();
354           i = wexp(b);
355           if (i <= -37 || i >= 37)
356             astore(0);
357           else if (i >= 0)
358             astore((a << i) & WORD_MASK);
359           else
360             astore(a >> (-i));
361           break;
362         case 064 ... 067:       // Shift arithmetical
363           afetch();
364           i = wexp(b);
365           aa = wabs(a);
366           if (i <= -36 || i >= 36)
367             cc = 0;
368           else if (i >= 0)
369             cc = (aa << i) & VAL_MASK;
370           else
371             cc = aa >> (-i);
372           astore((a & SIGN_MASK) | wfromll(cc));
373           break;
374         case 070 ... 073:       // And
375           afetch();
376           astore(a&b);
377           break;
378         case 074 ... 077:       // Or
379           afetch();
380           astore(a|b);
381           break;
382
383         case 0100:              // Halt
384           r1 = rd(x);
385           acc = rd(y);
386           stop("HALTED");
387         case 0103:              // I/O magtape
388           notimp();
389         case 0104:              // Disable rounding
390           notimp();
391         case 0105:              // Enable rounding
392           notimp();
393         case 0106:              // Interrupt control
394           notimp();
395         case 0107:              // Reverse tape
396           notimp();
397         case 0110:              // Move
398           wr(yi, r1 = acc = rd(xi));
399           break;
400         case 0111:              // Move negative
401           wr(yi, acc = (r1 = rd(xi)) ^ SIGN_MASK);
402           break;
403         case 0112:              // Move absolute value
404           wr(yi, acc = (r1 = rd(xi)) & VAL_MASK);
405           break;
406         case 0113:              // Read from keyboard
407           notimp();
408         case 0114:              // Copy sign
409           wr(yi, acc = rd(yi) ^ ((r1 = rd(xi)) & SIGN_MASK));
410           break;
411         case 0115:              // Read code from R1 (obscure)
412           notimp();
413         case 0116:              // Copy exponent
414           nofpu();
415         case 0117:              // I/O teletype
416           notimp();
417         case 0120:              // Loop
418           if (!ix)
419             noins();
420           a = r1 = rd(ix);
421           aa = (a >> 24) & 017777;
422           if (!aa)
423             break;
424           b = rd(y);            // (a mountain range near Prague)
425           acc = ((aa-1) << 24) |
426                 (((((a >> 12) & 07777) + (b >> 12) & 07777) & 07777) << 12) |
427                 (((a & 07777) + (b & 07777)) & 07777);
428           wr(ix, acc);
429           ip = x;
430           break;
431         case 0130:              // Jump
432           wr(y, r2);
433           ip = x;
434           break;
435         case 0131:              // Jump to subroutine
436           wr(y, acc = ((030ULL << 30) | ((ip & 07777ULL) << 12)));
437           ip = x;
438           break;
439         case 0132:              // Jump if positive
440           if (wsign(r2) >= 0)
441             ip = x;
442           else
443             ip = y;
444           break;
445         case 0133:              // Jump if overflow
446           // Since we always trap on overflow, this instruction always jumps to the 1st address
447           ip = x;
448           break;
449         case 0134:              // Jump if zero
450           if (flag_zero)
451             ip = y;
452           else
453             ip = x;
454           break;
455         case 0135:              // Jump if key pressed
456           // No keys are ever pressed, so always jump to 2nd
457           ip = y;
458           break;
459         case 0136:              // Interrupt masking
460           notimp();
461         case 0137:              // Used only when reading from tape
462           notimp();
463         case 0140 ... 0147:     // I/O
464           notimp();
465         case 0150 ... 0154:     // I/O
466           notimp();
467         case 0160 ... 0161:     // I/O
468           notimp();
469         case 0162:              // Printing
470           print_ins(x, y);
471           break;
472         case 0163:              // I/O
473           notimp();
474         case 0170:              // FIX multiplication, bottom part
475           afetch();
476           if (wtofrac(a) * wtofrac(b) >= .1/(1ULL << 32))
477             over();
478           acc = wfromll(((unsigned long long)wabs(a) * (unsigned long long)wabs(b)) & VAL_MASK);
479           // XXX: What should be the sign? The book does not define that.
480           break;
481         case 0171:              // Modulo
482           afetch();
483           aa = wabs(a);
484           bb = wabs(b);
485           if (!bb)
486             stop("DIVISION BY ZERO");
487           cc = aa % bb;
488           if (wsign(b) < 0)
489             cc = -cc;
490           acc = wfromll(cc);
491           break;
492         case 0172:              // Add exponents
493           nofpu();
494         case 0173:              // Sub exponents
495           nofpu();
496         case 0174:              // Addition in one's complement
497           a = r1 = rd(xi);
498           b = rd(yi);
499           c = a + b;
500           if (c > VAL_MASK)
501             c = c - VAL_MASK;
502           wr(yi, c);
503           // XXX: The effect on the accumulator is undocumented, but likely to be as follows:
504           acc = c;
505           break;
506         case 0175:              // Normalization
507           nofpu();
508         case 0176:              // Population count
509           a = r1 = rd(xi);
510           cc = 0;
511           for (int i=0; i<36; i++)
512             if (a & (1ULL << i))
513               cc++;
514           // XXX: Guessing that acc gets a copy of the result
515           acc = wfromll(cc);
516           wr(yi, acc);
517           break;
518         default:
519           noins();
520         }
521
522       flag_zero = !acc;
523       if (trace > 1)
524         printf("\tACC:%c%012llo R1:%c%012llo R2:%c%012llo Z:%d\n", WF(acc), WF(r1), WF(r2), flag_zero);
525     }
526 }
527
528 int main(void)
529 {
530   parse_in();
531   run();
532   return 0;
533 }