]> mj.ucw.cz Git - minsk.git/blob - minsk.c
Minsk: Removed the secret tracing switch
[minsk.git] / minsk.c
1 /*
2  *      Minsk-2 Emulator
3  *
4  *      (c) 2010 Martin Mares <mj@ucw.cz>
5  */
6
7 /*
8  * Things that are not implemented:
9  *
10  *      - rounding modes
11  *      - exact behavior of accumulator/R1/R2 (the manual lacks details)
12  *      - exact behavior of negative zero
13  *      - I/O instructions for devices that are not emulated (paper tape
14  *        reader and puncher, card reader and puncher, magnetic tape unit)
15  */
16
17 #define _GNU_SOURCE
18 #define UNUSED __attribute__((unused))
19
20 #undef ENABLE_DAEMON_MODE
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <inttypes.h>
27 #include <assert.h>
28 #include <math.h>
29 #include <getopt.h>
30
31 static int trace;
32 static int cpu_quota = -1;
33 static int print_quota = -1;
34 static void (*error_hook)(char *msg);
35
36 // Minsk-2 has 37-bit words in sign-magnitude representation (bit 36 = sign)
37 typedef unsigned long long int word;
38
39 #define WORD_MASK 01777777777777ULL
40 #define SIGN_MASK 01000000000000ULL
41 #define  VAL_MASK 00777777777777ULL
42
43 static int wsign(word w)
44 {
45   return (w & SIGN_MASK) ? -1 : 1;
46 }
47
48 static word wabs(word w)
49 {
50   return w & VAL_MASK;
51 }
52
53 #define WF(w) (wsign(w) < 0 ? '-' : '+'), wabs(w)
54
55 static long long wtoll(word w)
56 {
57   if (wsign(w) < 0)
58     return -wabs(w);
59   else
60     return wabs(w);
61 }
62
63 static word wfromll(long long x)
64 {
65   word w = ((x < 0) ? -x : x) & VAL_MASK;
66   if (x < 0)
67     w |= SIGN_MASK;
68   return w;
69 }
70
71 static double wtofrac(word w)
72 {
73   return (double)wtoll(w) / (double)(1ULL << 36);
74 }
75
76 static word wfromfrac(double d)
77 {
78   return wfromll((long long)(d * (double)(1ULL << 36)));
79 }
80
81 static int int_in_range(long long x)
82 {
83   return (x >= -(long long)VAL_MASK && x <= (long long)VAL_MASK);
84 }
85
86 static int frac_in_range(double d)
87 {
88   return (d > -1. && d < 1.);
89 }
90
91 static int wexp(word w)
92 {
93   int exp = w & 077;
94   return (w & 0100 ? -exp : exp);
95 }
96
97 static word wputexp(word w, int exp)
98 {
99   return ((w & ~(word)0177) | ((exp < 0) ? 0100 | (-exp) : exp));
100 }
101
102 static int wmanti(word w)
103 {
104   return ((w >> 8) & ((1 << 28) - 1));
105 }
106
107 static double wtofloat(word w)
108 {
109   double x = wmanti(w);
110   return ldexp(x, wexp(w) - 28);
111 }
112
113 static int float_in_range(double x)
114 {
115   x = fabs(x);
116   return (x <= ldexp((1 << 28) - 1, 63 - 28));
117 }
118
119 static word wfromfloat(double x, int normalized)
120 {
121   word w = 0;
122   if (x < 0)
123     {
124       w |= SIGN_MASK;
125       x = -x;
126     }
127   int exp;
128   double m = frexp(x, &exp);
129   word mm = (word) ldexp(m, 28);
130   if (exp > 63)
131     assert(0);
132   else if (exp < -63)
133     {
134       if (normalized || exp < -91)
135         mm=0, exp=0;
136       else
137         {
138           mm >>= -exp - 63;
139           exp = -63;
140         }
141     }
142   w |= mm << 8;
143   if (exp < 0)
144     {
145       w |= 0100;
146       exp = -exp;
147     }
148   w |= exp;
149   return w;
150 }
151
152 static word mem[4096];
153
154 static word rd(int addr)
155 {
156   word val = addr ? mem[addr] : 0;
157   if (trace > 2)
158     printf("\tRD %04o = %c%012llo\n", addr, WF(val));
159   return val;
160 }
161
162 static void wr(int addr, word val)
163 {
164   assert(!(val & ~(WORD_MASK)));
165   if (trace > 2)
166     printf("\tWR %04o = %c%012llo\n", addr, WF(val));
167   mem[addr] = val;
168 }
169
170 static int lino;
171
172 static void parse_error(char *msg)
173 {
174   if (error_hook)
175     error_hook("Parse error");
176   printf("Ошибка входа (стр. %d): %s\n", lino, msg);
177   exit(0);
178 }
179
180 static void parse_in(void)
181 {
182   char line[80];
183   int addr = 0;
184
185   while (fgets(line, sizeof(line), stdin))
186     {
187       lino++;
188       char *eol = strchr(line, '\n');
189       if (!eol)
190         parse_error("Строка слишком долгая");
191       *eol = 0;
192       if (eol > line && eol[-1] == '\r')
193         *--eol = 0;
194
195       char *c = line;
196       if (!c[0] || c[0] == ';')
197         continue;
198
199       if (c[0] == '.')
200         return;
201
202       if (c[0] == '@')
203         {
204           c++;
205           addr = 0;
206           for (int i=0; i<4; i++)
207             {
208               while (*c == ' ')
209                 c++;
210               if (*c >= '0' && *c <= '7')
211                 addr = 8*addr + *c++ - '0';
212               else
213                 parse_error("Плохая цифра");
214             }
215           while (*c == ' ')
216             c++;
217           if (*c)
218             parse_error("Адрес слишком долгий");
219           continue;
220         }
221
222       word w = 0;
223       if (*c == '-')
224         w = 1;
225       else if (*c != '+')
226         parse_error("Плохой знак");
227       c++;
228       for (int i=0; i<12; i++)
229         {
230           while (*c == ' ')
231             c++;
232           if (*c >= '0' && *c <= '7')
233             w = 8*w + *c++ - '0';
234           else
235             parse_error("Плохая цифра");
236         }
237       while (*c == ' ')
238         c++;
239       if (*c)
240         parse_error("Номер слишком долгий");
241       wr(addr++, w);
242       addr &= 07777;
243     }
244 }
245
246 static word acc;
247 static word r1, r2, current_ins;
248 static int ip = 00050;                  // Standard program start location
249 static int prev_ip;
250
251 static void stop(char *reason, char *notice)
252 {
253   if (error_hook)
254     error_hook(notice);
255   printf("Машина остановлена -- %s\n", reason);
256   printf("СчАК:%04o См:%c%012llo Р1:%c%012llo Р2:%c%012llo\n", prev_ip, WF(acc), WF(r1), WF(r2));
257   exit(0);
258 }
259
260 static void over(void)
261 {
262   stop("Аварийный останов", "Overflow");
263 }
264
265 static void notimp(void)
266 {
267   acc = current_ins;
268   stop("Устройство разбитое", "Not implemented");
269 }
270
271 static void noins(void)
272 {
273   acc = current_ins;
274   stop("Эту команду не знаю", "Illegal instruction");
275 }
276
277 static uint16_t linebuf[128];
278
279 static uint16_t russian_chars[64] = {
280         '0',    '1',    '2',    '3',    '4',    '5',    '6',    '7',    // 0x
281         '8',    '9',    '+',    '-',    '/',    ',',    '.',    ' ',    // 1x
282         0x2169, '^',    '(',    ')',    0x00d7, '=',    ';',    '[',    // 2x
283         ']',    '*',    '`',    '\'',   0x2260, '<',    '>',    ':',    // 3x
284         0x410,  0x411,  0x412,  0x413,  0x414,  0x415,  0x416,  0x417,  // 4x
285         0x418,  0x419,  0x41a,  0x41b,  0x41c,  0x41d,  0x41e,  0x41f,  // 5x
286         0x420,  0x421,  0x422,  0x423,  0x424,  0x425,  0x426,  0x427,  // 6x
287         0x428,  0x429,  0x42b,  0x42c,  0x42d,  0x42e,  0x42f,  0x2013  // 7x
288 };
289
290 static uint16_t latin_chars[64] = {
291         '0',    '1',    '2',    '3',    '4',    '5',    '6',    '7',    // 0x
292         '8',    '9',    '+',    '-',    '/',    ',',    '.',    ' ',    // 1x
293         0x2169, '^',    '(',    ')',    0x00d7, '=',    ';',    '[',    // 2x
294         ']',    '*',    '`',    '\'',   0x2260, '<',    '>',    ':',    // 3x
295         'A',    'B',    'W',    'G',    'D',    'E',    'V',    'Z',    // 4x
296         'I',    'J',    'K',    'L',    'M',    'N',    'O',    'P',    // 5x
297         'R',    'S',    'T',    'U',    'F',    'H',    'C',    ' ',    // 6x
298         ' ',    ' ',    'Y',    'X',    ' ',    ' ',    'Q',    0x2013  // 7x
299 };
300
301 static void print_line(int r)
302 {
303   /*
304    *  Meaning of bits of r:
305    *    0 = perform line feed
306    *    1 = clear buffer
307    *    2 = actually print
308    */
309   if (r & 4)
310     {
311       if (print_quota > 0 && !--print_quota)
312         stop("Бумага дошла - нужно ехать в Сибирь про новую", "Out of paper");
313       for (int i=0; i<128; i++)
314         {
315           int ch = linebuf[i];
316           if (!ch)
317             ch = ' ';
318           if (ch < 0x80)
319             putchar(ch);
320           else if (ch < 0x800)
321             {
322               putchar(0xc0 | (ch >> 6));
323               putchar(0x80 | (ch & 0x3f));
324             }
325           else
326             {
327               putchar(0xe0 | (ch >> 12));
328               putchar(0x80 | ((ch >> 6) & 0x3f));
329               putchar(0x80 | (ch & 0x3f));
330             }
331         }
332     }
333   if (r & 2)
334     memset(linebuf, 0, sizeof(linebuf));
335   if (r & 1)
336     putchar('\n');
337   else if (r & 4)
338     putchar('\r');
339   fflush(stdout);
340 }
341
342 static void print_ins(int x, int y)
343 {
344   word yy = rd(y);
345   int pos = x & 0177;
346   int r = (x >> 9) & 7;
347
348   if (x & 0400)
349     {
350       print_line(r);
351       return;
352     }
353
354   char *fmt;
355   int bit = 37;
356   int eat = 0;
357   switch (r)
358     {
359     case 0:                             // Decimal float
360       fmt = "+dddddddx+xbd";
361       break;
362     case 1:                             // Octal number
363       fmt = "+oooooooooooo";
364       break;
365     case 2:                             // Decimal fixed
366       fmt = "+ddddddddd";
367       break;
368     case 3:                             // Decimal unsigned
369       fmt = "x ddddddddd";
370       eat = 1;
371       break;
372     case 4:                             // One Russian symbol
373       fmt = "xr";
374       break;
375     case 5:                             // Russian text
376       fmt = "xrrrrrr";
377       break;
378     case 6:                             // One Latin symbol
379       fmt = "xl";
380       break;
381     default:                            // Latin text
382       fmt = "xllllll";
383     }
384
385   while (*fmt)
386     {
387       int ch;
388       switch (*fmt++)
389         {
390         case 'x':
391           bit--;
392           continue;
393         case ' ':
394           ch = ' ';
395           break;
396         case '+':
397           bit--;
398           ch = (yy & (1ULL << bit)) ? '-' : '+';
399           break;
400         case 'b':
401           bit--;
402           ch = '0' + ((yy >> bit) & 1);
403           break;
404         case 'o':
405           bit -= 3;
406           ch = '0' + ((yy >> bit) & 7);
407           break;
408         case 'd':
409           bit -= 4;
410           ch = '0' + ((yy >> bit) & 15);
411           if (ch > '0' + 9)
412             ch += 7;
413           break;
414         case 'r':
415           bit -= 6;
416           ch = russian_chars[(yy >> bit) & 077];
417           break;
418         case 'l':
419           bit -= 6;
420           ch = latin_chars[(yy >> bit) & 077];
421           break;
422         default:
423           assert(0);
424         }
425
426       if (eat && *fmt)
427         {
428           if (ch == '0' || ch == ' ')
429             ch = ' ';
430           else
431             eat = 0;
432         }
433       linebuf[pos] = ch;
434       pos = (pos+1) & 0177;
435     }
436   assert(bit >= 0);
437 }
438
439 static void run(void)
440 {
441   for (;;)
442     {
443       r2 = acc;
444       prev_ip = ip;
445       word w = mem[ip];
446       current_ins = w;
447
448       int op = (w >> 30) & 0177;        // Operation code
449       int ax = (w >> 28) & 3;           // Address extensions not supported
450       int ix = (w >> 24) & 15;          // Indexing
451       int x = (w >> 12) & 07777;        // Operands (original form)
452       int y = w & 07777;
453       int xi=x, yi=y;                   // (indexed form)
454       if (trace)
455         printf("@%04o  %c%02o %02o %04o %04o\n",
456           ip,
457           (w & SIGN_MASK) ? '-' : '+',
458           (int)((w >> 30) & 077),
459           (int)((w >> 24) & 077),
460           x,
461           y);
462       if (ix)
463         {
464           if (op != 0120)
465             {
466               word i = rd(ix);
467               xi = (xi + (int)((i >> 12) & 07777)) & 07777;
468               yi = (yi + (int)(i & 07777)) & 07777;
469               if (trace > 2)
470                 printf("\tIndexing -> %04o %04o\n", xi, yi);
471             }
472         }
473       ip = (ip+1) & 07777;
474
475       if (cpu_quota > 0 && !--cpu_quota)
476         stop("Тайм-аут", "CPU quota exceeded");
477
478       /* Arithmetic operations */
479
480       word a, b, c;
481       long long aa, bb, cc;
482       double ad, bd;
483       int i;
484
485       auto void afetch(void);
486       void afetch(void)
487         {
488           if (op & 2)
489             a = r2;
490           else
491             a = rd(yi);
492           b = r1 = rd(xi);
493         }
494
495       auto void astore(word result);
496       void astore(word result)
497         {
498           acc = result;
499           if (op & 1)
500             wr(yi, acc);
501         }
502
503       auto void astore_int(long long x);
504       void astore_int(long long x)
505         {
506           if (!int_in_range(x))
507             over();
508           astore(wfromll(x));
509         }
510
511       auto void astore_frac(double f);
512       void astore_frac(double f)
513         {
514           if (!frac_in_range(f))
515             over();
516           astore(wfromfrac(f));
517         }
518
519       auto void astore_float(double f);
520       void astore_float(double f)
521         {
522           if (!float_in_range(f))
523             over();
524           astore(wfromfloat(f, 0));
525         }
526
527       if (ax)
528         op = -1;
529       switch (op)
530         {
531         case 000:               // NOP
532           break;
533         case 004 ... 007:       // XOR
534           afetch();
535           astore(a^b);
536           break;
537         case 010 ... 013:       // FIX addition
538           afetch();
539           astore_int(wtoll(a) + wtoll(b));
540           break;
541         case 014 ... 017:       // FP addition
542           afetch();
543           astore_float(wtofloat(a) + wtofloat(b));
544           break;
545         case 020 ... 023:       // FIX subtraction
546           afetch();
547           astore_int(wtoll(a) - wtoll(b));
548           break;
549         case 024 ... 027:       // FP subtraction
550           afetch();
551           astore_float(wtofloat(a) - wtofloat(b));
552           break;
553         case 030 ... 033:       // FIX multiplication
554           afetch();
555           astore_frac(wtofrac(a) * wtofrac(b));
556           break;
557         case 034 ... 037:       // FP multiplication
558           afetch();
559           astore_float(wtofloat(a) * wtofloat(b));
560           break;
561         case 040 ... 043:       // FIX division
562           afetch();
563           ad = wtofrac(a);
564           bd = wtofrac(b);
565           if (!wabs(b))
566             over();
567           astore_frac(ad / bd);
568           break;
569         case 044 ... 047:       // FP division
570           afetch();
571           ad = wtofloat(a);
572           bd = wtofloat(b);
573           if (!bd || wexp(b) < -63)
574             over();
575           astore_float(ad / bd);
576           break;
577         case 050 ... 053:       // FIX subtraction of abs values
578           afetch();
579           astore_int(wabs(a) - wabs(b));
580           break;
581         case 054 ... 057:       // FP subtraction of abs values
582           afetch();
583           astore_float(fabs(wtofloat(a)) - fabs(wtofloat(b)));
584           break;
585         case 060 ... 063:       // Shift logical
586           afetch();
587           i = wexp(b);
588           if (i <= -37 || i >= 37)
589             astore(0);
590           else if (i >= 0)
591             astore((a << i) & WORD_MASK);
592           else
593             astore(a >> (-i));
594           break;
595         case 064 ... 067:       // Shift arithmetical
596           afetch();
597           i = wexp(b);
598           aa = wabs(a);
599           if (i <= -36 || i >= 36)
600             cc = 0;
601           else if (i >= 0)
602             cc = (aa << i) & VAL_MASK;
603           else
604             cc = aa >> (-i);
605           astore((a & SIGN_MASK) | wfromll(cc));
606           break;
607         case 070 ... 073:       // And
608           afetch();
609           astore(a&b);
610           break;
611         case 074 ... 077:       // Or
612           afetch();
613           astore(a|b);
614           break;
615
616         case 0100:              // Halt
617           r1 = rd(x);
618           acc = rd(y);
619           stop("Останов машины", "Halted");
620         case 0103:              // I/O magtape
621           notimp();
622         case 0104:              // Disable rounding
623           notimp();
624         case 0105:              // Enable rounding
625           notimp();
626         case 0106:              // Interrupt control
627           notimp();
628         case 0107:              // Reverse tape
629           notimp();
630         case 0110:              // Move
631           wr(yi, r1 = acc = rd(xi));
632           break;
633         case 0111:              // Move negative
634           wr(yi, acc = (r1 = rd(xi)) ^ SIGN_MASK);
635           break;
636         case 0112:              // Move absolute value
637           wr(yi, acc = (r1 = rd(xi)) & VAL_MASK);
638           break;
639         case 0113:              // Read from keyboard
640           notimp();
641         case 0114:              // Copy sign
642           wr(yi, acc = rd(yi) ^ ((r1 = rd(xi)) & SIGN_MASK));
643           break;
644         case 0115:              // Read code from R1 (obscure)
645           notimp();
646         case 0116:              // Copy exponent
647           wr(yi, acc = wputexp(rd(yi), wexp(r1 = rd(xi))));
648           break;
649         case 0117:              // I/O teletype
650           notimp();
651         case 0120:              // Loop
652           if (!ix)
653             noins();
654           a = r1 = rd(ix);
655           aa = (a >> 24) & 017777;
656           if (!aa)
657             break;
658           b = rd(y);            // (a mountain range near Prague)
659           acc = ((aa-1) << 24) |
660                 (((((a >> 12) & 07777) + (b >> 12) & 07777) & 07777) << 12) |
661                 (((a & 07777) + (b & 07777)) & 07777);
662           wr(ix, acc);
663           ip = x;
664           break;
665         case 0130:              // Jump
666           wr(y, r2);
667           ip = x;
668           break;
669         case 0131:              // Jump to subroutine
670           wr(y, acc = ((030ULL << 30) | ((ip & 07777ULL) << 12)));
671           ip = x;
672           break;
673         case 0132:              // Jump if positive
674           if (wsign(r2) >= 0)
675             ip = x;
676           else
677             ip = y;
678           break;
679         case 0133:              // Jump if overflow
680           // Since we always trap on overflow, this instruction always jumps to the 1st address
681           ip = x;
682           break;
683         case 0134:              // Jump if zero
684           if (!wabs(r2))
685             ip = y;
686           else
687             ip = x;
688           break;
689         case 0135:              // Jump if key pressed
690           // No keys are ever pressed, so always jump to 2nd
691           ip = y;
692           break;
693         case 0136:              // Interrupt masking
694           notimp();
695         case 0137:              // Used only when reading from tape
696           notimp();
697         case 0140 ... 0147:     // I/O
698           notimp();
699         case 0150 ... 0154:     // I/O
700           notimp();
701         case 0160 ... 0161:     // I/O
702           notimp();
703         case 0162:              // Printing
704           print_ins(x, y);
705           break;
706         case 0163:              // I/O
707           notimp();
708         case 0170:              // FIX multiplication, bottom part
709           afetch();
710           if (wtofrac(a) * wtofrac(b) >= .1/(1ULL << 32))
711             over();
712           acc = wfromll(((unsigned long long)wabs(a) * (unsigned long long)wabs(b)) & VAL_MASK);
713           // XXX: What should be the sign? The book does not define that.
714           break;
715         case 0171:              // Modulo
716           afetch();
717           aa = wabs(a);
718           bb = wabs(b);
719           if (!bb)
720             over();
721           cc = aa % bb;
722           if (wsign(b) < 0)
723             cc = -cc;
724           acc = wfromll(cc);
725           break;
726         case 0172:              // Add exponents
727           a = r1 = rd(xi);
728           b = rd(yi);
729           i = wexp(a) + wexp(b);
730           if (i < -63 || i > 63)
731             over();
732           acc = wputexp(b, i);
733           wr(yi, acc);
734           break;
735         case 0173:              // Sub exponents
736           a = r1 = rd(xi);
737           b = rd(yi);
738           i = wexp(b) - wexp(a);
739           if (i < -63 || i > 63)
740             over();
741           acc = wputexp(b, i);
742           wr(yi, acc);
743           break;
744         case 0174:              // Addition in one's complement
745           a = r1 = rd(xi);
746           b = rd(yi);
747           c = a + b;
748           if (c > VAL_MASK)
749             c = c - VAL_MASK;
750           wr(yi, c);
751           // XXX: The effect on the accumulator is undocumented, but likely to be as follows:
752           acc = c;
753           break;
754         case 0175:              // Normalization
755           a = r1 = rd(xi);
756           if (!wabs(a))
757             {
758               wr(yi, 0);
759               wr((yi+1) & 07777, 0);
760               acc = 0;
761             }
762           else
763             {
764               i = 0;
765               acc = a & SIGN_MASK;
766               a &= VAL_MASK;
767               while (!(a & (SIGN_MASK >> 1)))
768                 {
769                   a <<= 1;
770                   i++;
771                 }
772               acc |= a;
773               wr(yi, acc);
774               wr((yi+1) & 07777, i);
775             }
776           break;
777         case 0176:              // Population count
778           a = r1 = rd(xi);
779           cc = 0;
780           for (int i=0; i<36; i++)
781             if (a & (1ULL << i))
782               cc++;
783           // XXX: Guessing that acc gets a copy of the result
784           acc = wfromll(cc);
785           wr(yi, acc);
786           break;
787         default:
788           noins();
789         }
790
791       if (trace > 1)
792         printf("\tACC:%c%012llo R1:%c%012llo R2:%c%012llo\n", WF(acc), WF(r1), WF(r2));
793     }
794 }
795
796 static void die(char *msg)
797 {
798   fprintf(stderr, "minsk: ");
799   fprintf(stderr, msg);
800   fputc('\n', stderr);
801   exit(1);
802 }
803
804 /*** Daemon interface ***/
805
806 #ifdef ENABLE_DAEMON_MODE
807
808 /*
809  * The daemon mode was a quick hack for the Po drate contest.
810  * Most parameters are hard-wired.
811  */
812
813 #include <unistd.h>
814 #include <errno.h>
815 #include <time.h>
816 #include <syslog.h>
817 #include <sys/signal.h>
818 #include <sys/wait.h>
819 #include <sys/poll.h>
820 #include <sys/socket.h>
821 #include <netinet/in.h>
822 #include <arpa/inet.h>
823
824 #if 0
825 #define DTRACE(msg, args...) fprintf(stderr, msg "\n", ##args)
826 #define DLOG(msg, args...) fprintf(stderr, msg "\n", ##args)
827 #else
828 #define DTRACE(msg, args...) do { } while(0)
829 #define DLOG(msg, args...) syslog(LOG_INFO, msg, ##args)
830 #endif
831
832 #define MAX_CONNECTIONS 50              // Per daemon
833 #define MAX_CONNS_PER_IP 1              // Per IP
834 #define MAX_TRACKERS 200                // IP address trackers
835 #define TBF_MAX 5                       // Max number of tokens in the bucket
836 #define TBF_REFILL_PER_SEC 0.2          // Bucket refill rate (buckets/sec)
837
838 #define PID_FILE "/var/run/pd-minsk.pid"
839 #define UID 124
840 #define GID 125
841
842 static char **spt_argv;
843 static char *spt_start, *spt_end;
844
845 static void setproctitle_init(int argc, char **argv)
846 {
847   int i, len;
848   char **env, **oldenv, *t;
849
850   spt_argv = argv;
851
852   /* Create a backup copy of environment */
853   oldenv = __environ;
854   len = 0;
855   for (i=0; oldenv[i]; i++)
856     len += strlen(oldenv[i]) + 1;
857   __environ = env = malloc(sizeof(char *)*(i+1));
858   t = malloc(len);
859   if (!__environ || !t)
860     die("malloc failed");
861   for (i=0; oldenv[i]; i++)
862     {
863       env[i] = t;
864       len = strlen(oldenv[i]) + 1;
865       memcpy(t, oldenv[i], len);
866       t += len;
867     }
868   env[i] = NULL;
869
870   /* Scan for consecutive free space */
871   spt_start = spt_end = argv[0];
872   for (i=0; i<argc; i++)
873     if (!i || spt_end+1 == argv[i])
874       spt_end = argv[i] + strlen(argv[i]);
875   for (i=0; oldenv[i]; i++)
876     if (spt_end+1 == oldenv[i])
877       spt_end = oldenv[i] + strlen(oldenv[i]);
878 }
879
880 static void
881 setproctitle(const char *msg, ...)
882 {
883   va_list args;
884   char buf[256];
885   int n;
886
887   va_start(args, msg);
888   if (spt_end > spt_start)
889     {
890       n = vsnprintf(buf, sizeof(buf), msg, args);
891       if (n >= (int) sizeof(buf) || n < 0)
892         sprintf(buf, "<too-long>");
893       n = spt_end - spt_start;
894       strncpy(spt_start, buf, n);
895       spt_start[n] = 0;
896       spt_argv[0] = spt_start;
897       spt_argv[1] = NULL;
898     }
899   va_end(args);
900 }
901
902 static void sigchld_handler(int sig UNUSED)
903 {
904 }
905
906 static void sigalrm_handler(int sig UNUSED)
907 {
908   const char err[] = "--- Timed out. Time machine disconnected. ---\n";
909   write(1, err, sizeof(err));
910   DLOG("Connection timed out");
911   exit(0);
912 }
913
914 static void child_error_hook(char *err)
915 {
916   DLOG("Stopped: %s", err);
917 }
918
919 static void child(int sk2)
920 {
921   dup2(sk2, 0);
922   dup2(sk2, 1);
923   close(sk2);
924
925   struct sigaction sact = {
926     .sa_handler = sigalrm_handler,
927   };
928   if (sigaction(SIGALRM, &sact, NULL) < 0)
929     die("sigaction: %m");
930
931   // Set up limits
932   alarm(60);
933   cpu_quota = 100000;
934   print_quota = 100;
935
936   const char welcome[] = "+++ Welcome to our computer museum. +++\n+++ Our time machine will connect you to one of our exhibits. +++\n\n";
937   write(1, welcome, sizeof(welcome));
938
939   error_hook = child_error_hook;
940   parse_in();
941   run();
942   fflush(stdout);
943   DTRACE("Finished");
944 }
945
946 struct conn {
947   pid_t pid;
948   struct in_addr addr;
949   struct tracker *tracker;
950 };
951
952 static struct conn connections[MAX_CONNECTIONS];
953
954 static struct conn *get_conn(struct in_addr *a)
955 {
956   for (int i=0; i<MAX_CONNECTIONS; i++)
957     {
958       struct conn *c = &connections[i];
959       if (!c->pid)
960         {
961           memcpy(&c->addr, a, sizeof(struct in_addr));
962           return c;
963         }
964     }
965   return NULL;
966 }
967
968 static struct conn *pid_to_conn(pid_t pid)
969 {
970   for (int i=0; i<MAX_CONNECTIONS; i++)
971     {
972       struct conn *c = &connections[i];
973       if (c->pid == pid)
974         return c;
975     }
976   return NULL;
977 }
978
979 static void put_conn(struct conn *c)
980 {
981   c->pid = 0;
982   c->tracker = NULL;
983 }
984
985 struct tracker {
986   struct in_addr addr;
987   int active_conns;
988   time_t last_access;
989   double tokens;
990 };
991
992 static struct tracker trackers[MAX_TRACKERS];
993
994 static int get_tracker(struct conn *c)
995 {
996   struct tracker *t;
997   time_t now = time(NULL);
998   int i;
999
1000   for (i=0; i<MAX_TRACKERS; i++)
1001     {
1002       t = &trackers[i];
1003       if (!memcmp(&t->addr, &c->addr, sizeof(struct in_addr)))
1004         break;
1005     }
1006   if (i < MAX_TRACKERS)
1007     {
1008       if (now > t->last_access)
1009         {
1010           t->tokens += (now - t->last_access) * (double) TBF_REFILL_PER_SEC;
1011           t->last_access = now;
1012           if (t->tokens > TBF_MAX)
1013             t->tokens = TBF_MAX;
1014         }
1015       DTRACE("TBF: Using tracker %d (%.3f tokens)", i, t->tokens);
1016     }
1017   else
1018     {
1019       int min_i = -1;
1020       for (int i=0; i<MAX_TRACKERS; i++)
1021         {
1022           t = &trackers[i];
1023           if (!t->active_conns && (min_i < 0 || t->last_access < trackers[min_i].last_access))
1024             min_i = i;
1025         }
1026       if (min_i < 0)
1027         {
1028           DLOG("TBF: Out of trackers!");
1029           return 0;
1030         }
1031       t = &trackers[min_i];
1032       if (t->last_access)
1033         DTRACE("TBF: Recycling tracker %d", min_i);
1034       else
1035         DTRACE("TBF: Creating tracker %d", min_i);
1036       memset(t, 0, sizeof(*t));
1037       t->addr = c->addr;
1038       t->last_access = now;
1039       t->tokens = TBF_MAX;
1040     }
1041
1042   if (t->active_conns >= MAX_CONNS_PER_IP)
1043     {
1044       DTRACE("TBF: Too many conns per IP");
1045       return 0;
1046     }
1047
1048   if (t->tokens >= 0.999)
1049     {
1050       t->tokens -= 1;
1051       t->active_conns++;
1052       c->tracker = t;
1053       DTRACE("TBF: Passed (%d conns)", t->active_conns);
1054       return 1;
1055     }
1056   else
1057     {
1058       DTRACE("TBF: Failed");
1059       t->tokens = 0;
1060       return 0;
1061     }
1062 }
1063
1064 static void put_tracker(struct conn *c)
1065 {
1066   struct tracker *t = c->tracker;
1067   if (!t)
1068     {
1069       DLOG("put_tracker: no tracker?");
1070       sleep(5);
1071       return;
1072     }
1073   if (t->active_conns <= 0)
1074     {
1075       DLOG("put_tracker: no counter?");
1076       sleep(5);
1077       return;
1078     }
1079   t->active_conns--;
1080   DTRACE("TBF: Put tracker (%d conns remain)", t->active_conns);
1081 }
1082
1083 static void run_as_daemon(int do_fork)
1084 {
1085   int sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
1086   if (sk < 0)
1087     die("socket: %m");
1088
1089   int one = 1;
1090   if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
1091     die("setsockopt: %m");
1092
1093   struct sockaddr_in sa = {
1094     .sin_family = AF_INET,
1095     .sin_port = ntohs(1969),
1096     .sin_addr.s_addr = INADDR_ANY,
1097   };
1098   if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0)
1099     die("bind: %m");
1100   if (listen(sk, 128) < 0)
1101     die("listen: %m");
1102   // if (fcntl(sk, F_SETFL, O_NONBLOCK) < 0)
1103   //  die("fcntl: %m");
1104
1105   if (do_fork)
1106     {
1107       pid_t pid = fork();
1108       if (pid < 0)
1109         die("fork: %m");
1110       if (pid)
1111         {
1112           FILE *f = fopen(PID_FILE, "w");
1113           if (f)
1114             {
1115               fprintf(f, "%d\n", pid);
1116               fclose(f);
1117             }
1118           exit(0);
1119         }
1120
1121       chdir("/");
1122       setresgid(GID, GID, GID);
1123       setresuid(UID, UID, UID);
1124       setsid();
1125     }
1126
1127   struct sigaction sact = {
1128     .sa_handler = sigchld_handler,
1129     .sa_flags = SA_RESTART,
1130   };
1131   if (sigaction(SIGCHLD, &sact, NULL) < 0)
1132     die("sigaction: %m");
1133
1134   DLOG("Daemon ready");
1135   setproctitle("minsk: Listening");
1136   openlog("minsk", LOG_PID, LOG_LOCAL7);
1137
1138   for (;;)
1139     {
1140       struct pollfd pfd[1] = {
1141         { .fd = sk, .events = POLLIN },
1142       };
1143
1144       int nfds = poll(pfd, 1, 60000);
1145       if (nfds < 0 && errno != EINTR)
1146         {
1147           DLOG("poll: %m");
1148           sleep(5);
1149           continue;
1150         }
1151
1152       int status;
1153       pid_t pid;
1154       while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
1155         {
1156           if (!WIFEXITED(status) || WEXITSTATUS(status))
1157             DLOG("Process %d exited with strange status %x", pid, status);
1158
1159           struct conn *conn = pid_to_conn(pid);
1160           if (conn)
1161             {
1162               DTRACE("Connection with PID %d exited", pid);
1163               put_tracker(conn);
1164               put_conn(conn);
1165             }
1166           else
1167             DTRACE("PID %d exited, matching no connection", pid);
1168         }
1169
1170       if (!(pfd[0].revents & POLLIN))
1171         continue;
1172
1173       socklen_t salen = sizeof(sa);
1174       int sk2 = accept(sk, (struct sockaddr *) &sa, &salen);
1175       if (sk2 < 0)
1176         {
1177           if (errno != EINTR)
1178             {
1179               DLOG("accept: %m");
1180               sleep(5);
1181             }
1182           continue;
1183         }
1184       DTRACE("Got connection: fd=%d", sk2);
1185
1186       struct conn *conn = get_conn(&sa.sin_addr);
1187       const char *reason = NULL;
1188       if (conn)
1189         {
1190           if (!get_tracker(conn))
1191             {
1192               DLOG("Connection from %s dropped: Throttling", inet_ntoa(sa.sin_addr));
1193               put_conn(conn);
1194               conn = NULL;
1195               reason = "--- Sorry, but you are sending too many requests. Please slow down. ---\n";
1196             }
1197         }
1198       else
1199         {
1200           DLOG("Connection from %s dropped: Too many connections", inet_ntoa(sa.sin_addr));
1201           reason = "--- Sorry, maximum number of connections exceeded. Please come later. ---\n";
1202         }
1203
1204       pid = fork();
1205       if (pid < 0)
1206         {
1207           DLOG("fork failed: %m");
1208           close(sk2);
1209           continue;
1210         }
1211       if (!pid)
1212         {
1213           close(sk);
1214           if (conn)
1215             {
1216               DLOG("Accepted connection from %s", inet_ntoa(sa.sin_addr));
1217               setproctitle("minsk: %s", inet_ntoa(sa.sin_addr));
1218               child(sk2);
1219             }
1220           else
1221             {
1222               DLOG("Sending error message to %s", inet_ntoa(sa.sin_addr));
1223               setproctitle("minsk: %s ERR", inet_ntoa(sa.sin_addr));
1224               write(sk2, reason, strlen(reason));
1225             }
1226           exit(0);
1227         }
1228
1229       DTRACE("Created process %d", pid);
1230       if (conn)
1231         conn->pid = pid;
1232       close(sk2);
1233     }
1234 }
1235
1236 #else
1237
1238 static void run_as_daemon(int do_fork UNUSED)
1239 {
1240   die("Daemon mode not supported in this version, need to recompile.");
1241 }
1242
1243 static void setproctitle_init(int argc UNUSED, char **argv UNUSED)
1244 {
1245 }
1246
1247 #endif
1248
1249 static void init_memory(void)
1250 {
1251   // For the contest, we fill the whole memory with -00 00 0000 0000 (HALT),
1252   // not +00 00 0000 0000 (NOP). Otherwise, an empty program would reveal
1253   // the location of the password :)
1254   for (int i=0; i<4096; i++)
1255     mem[i] = 01000000000000ULL;
1256
1257   // Store the password
1258   int pos = 02655;
1259   mem[pos++] = 0574060565373;
1260   mem[pos++] = 0371741405340;
1261   mem[pos++] = 0534051524017;
1262 }
1263
1264 static const struct option longopts[] = {
1265   { "cpu-quota",        1, NULL, 'q' },
1266   { "daemon",           0, NULL, 'd' },
1267   { "nofork",           0, NULL, 'n' },
1268   { "print-quota",      1, NULL, 'p' },
1269   { "trace",            1, NULL, 't' },
1270   { NULL,               0, NULL, 0   },
1271 };
1272
1273 static void usage(void)
1274 {
1275   fprintf(stderr, "\
1276 Options:\n\n\
1277 --daemon                Run as daemon and listen for network connections\n\
1278 --nofork                When run with --daemon, avoid forking\n\
1279 --trace=<level>         Enable tracing of program execution\n\
1280 --cpu-quota=<n>         Set CPU quota to <n> instructions\n\
1281 --print-quota=<n>       Set printer quota to <n> lines\n\
1282 ");
1283   exit(1);
1284 }
1285
1286 int main(int argc, char **argv)
1287 {
1288   int opt;
1289   int daemon_mode = 0;
1290   int do_fork = 1;
1291
1292   while ((opt = getopt_long(argc, argv, "", longopts, NULL)) >= 0)
1293     switch (opt)
1294       {
1295       case 'd':
1296         daemon_mode = 1;
1297         break;
1298       case 'n':
1299         do_fork = 0;
1300         break;
1301       case 'p':
1302         print_quota = atoi(optarg);
1303         break;
1304       case 'q':
1305         cpu_quota = atoi(optarg);
1306         break;
1307       case 't':
1308         trace = atoi(optarg);
1309         break;
1310       default:
1311         usage();
1312       }
1313   if (optind < argc)
1314     usage();
1315
1316   setproctitle_init(argc, argv);
1317   init_memory();
1318
1319   if (daemon_mode)
1320     run_as_daemon(do_fork);
1321
1322   parse_in();
1323   run();
1324   return 0;
1325 }