*/
#define _GNU_SOURCE
+#define UNUSED __attribute__((unused))
+#define NORETURN __attribute__((noreturn))
+
+#undef ENABLE_DAEMON_MODE
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <math.h>
+#include <getopt.h>
static int trace;
static int cpu_quota = -1;
static int print_quota = -1;
+static int english;
+static int memblocks = 1;
static void (*error_hook)(char *msg);
// Minsk-2 has 37-bit words in sign-magnitude representation (bit 36 = sign)
typedef unsigned long long int word;
+#define MEM_SIZE 4096
#define WORD_MASK 01777777777777ULL
#define SIGN_MASK 01000000000000ULL
#define VAL_MASK 00777777777777ULL
+typedef struct loc
+{
+ int block;
+ int address;
+} loc;
+
static int wsign(word w)
{
return (w & SIGN_MASK) ? -1 : 1;
}
#define WF(w) (wsign(w) < 0 ? '-' : '+'), wabs(w)
+#define LF(a) (a.block), (a.address)
static long long wtoll(word w)
{
return w;
}
-static word mem[4096];
+static word **mem;
-static word rd(int addr)
+static word rd(loc addr)
{
- word val = addr ? mem[addr] : 0;
+ word val = addr.address ? mem[addr.block][addr.address] : 0;
if (trace > 2)
- printf("\tRD %04o = %c%012llo\n", addr, WF(val));
+ printf("\tRD %d:%04o = %c%012llo\n", LF(addr), WF(val));
return val;
}
-static void wr(int addr, word val)
+static void wr(loc addr, word val)
{
assert(!(val & ~(WORD_MASK)));
if (trace > 2)
- printf("\tWR %04o = %c%012llo\n", addr, WF(val));
- mem[addr] = val;
+ printf("\tWR %d:%04o = %c%012llo\n", LF(addr), WF(val));
+ mem[addr.block][addr.address] = val;
}
static int lino;
-static void parse_error(char *msg)
+NORETURN static void parse_error(char *russian_msg, char *english_msg)
{
if (error_hook)
error_hook("Parse error");
- printf("Ошибка входа (стр. %d): %s\n", lino, msg);
+
+ if (english)
+ printf("Parse error (line %d): %s\n", lino, english_msg);
+ else
+ printf("Ошибка входа (стр. %d): %s\n", lino, russian_msg);
exit(0);
}
static void parse_in(void)
{
char line[80];
- int addr = 0;
+ loc addr = { 0, 0 };
while (fgets(line, sizeof(line), stdin))
{
lino++;
char *eol = strchr(line, '\n');
if (!eol)
- parse_error("Строка слишком долгая");
+ parse_error("Строка слишком долгая", "Line too long");
*eol = 0;
+ if (eol > line && eol[-1] == '\r')
+ *--eol = 0;
char *c = line;
if (!c[0] || c[0] == ';')
- {
- if (!strncmp(c, ";daji_zor_i_litva=", 18))
- {
- trace = atoi(c+18);
- if (error_hook)
- error_hook("Secret tracing switch flipped");
- }
- continue;
- }
+ continue;
+
+ if (c[0] == '.')
+ return;
if (c[0] == '@')
{
c++;
- addr = 0;
+ addr.address = 0;
for (int i=0; i<4; i++)
{
while (*c == ' ')
c++;
if (*c >= '0' && *c <= '7')
- addr = 8*addr + *c++ - '0';
+ addr.address = 8*addr.address + *c++ - '0';
else
- parse_error("Плохая цифра");
+ parse_error("Плохая цифра", "Invalid number");
}
while (*c == ' ')
c++;
if (*c)
- parse_error("Адрес слишком долгий");
+ parse_error("Адрес слишком долгий", "Address too long");
continue;
}
if (*c == '-')
w = 1;
else if (*c != '+')
- parse_error("Плохой знак");
+ parse_error("Плохой знак", "Invalid sign");
c++;
for (int i=0; i<12; i++)
{
if (*c >= '0' && *c <= '7')
w = 8*w + *c++ - '0';
else
- parse_error("Плохая цифра");
+ parse_error("Плохая цифра", "Invalid number");
}
while (*c == ' ')
c++;
if (*c)
- parse_error("Номер слишком долгий");
- wr(addr++, w);
- addr &= 07777;
+ parse_error("Номер слишком долгий", "Number too long");
+ wr(addr, w);
+ addr.address = (addr.address+1) & 07777;
}
}
static int ip = 00050; // Standard program start location
static int prev_ip;
-static void stop(char *reason, char *notice)
+NORETURN static void stop(char *russian_reason, char *english_reason)
{
if (error_hook)
- error_hook(notice);
- printf("Машина остановлена -- %s\n", reason);
- printf("СчАК:%04o См:%c%012llo Р1:%c%012llo Р2:%c%012llo\n", prev_ip, WF(acc), WF(r1), WF(r2));
+ error_hook(english_reason);
+
+ if (english)
+ {
+ printf("System stopped -- %s\n", english_reason);
+ printf("IP:%04o ACC:%c%012llo R1:%c%012llo R2:%c%012llo\n", prev_ip, WF(acc), WF(r1), WF(r2));
+ }
+ else
+ {
+ printf("Машина остановлена -- %s\n", russian_reason);
+ printf("СчАК:%04o См:%c%012llo Р1:%c%012llo Р2:%c%012llo\n", prev_ip, WF(acc), WF(r1), WF(r2));
+ }
exit(0);
}
-static void over(void)
+NORETURN static void over(void)
{
stop("Аварийный останов", "Overflow");
}
-static void notimp(void)
+NORETURN static void notimp(void)
{
acc = current_ins;
stop("Устройство разбитое", "Not implemented");
}
-static void noins(void)
+NORETURN static void noins(void)
{
acc = current_ins;
stop("Эту команду не знаю", "Illegal instruction");
fflush(stdout);
}
-static void print_ins(int x, int y)
+static void print_ins(int x, loc y)
{
word yy = rd(y);
int pos = x & 0177;
{
r2 = acc;
prev_ip = ip;
- word w = mem[ip];
+ word w = mem[0][ip];
current_ins = w;
int op = (w >> 30) & 0177; // Operation code
- int ax = (w >> 28) & 3; // Address extensions not supported
+ int ax = (w >> 28) & 3; // Address extensions supported in Minsk-22 mode
int ix = (w >> 24) & 15; // Indexing
- int x = (w >> 12) & 07777; // Operands (original form)
- int y = w & 07777;
- int xi=x, yi=y; // (indexed form)
+ loc x = { ax >> 1, (w >> 12) & 07777 }; // Operands (original form)
+ loc y = { ax & 1, w & 07777 };
+ loc xi=x, yi=y; // (indexed form)
if (trace)
- printf("@%04o %c%02o %02o %04o %04o\n",
+ printf("@%04o %c%02o %02o %d:%04o %d:%04o\n",
ip,
(w & SIGN_MASK) ? '-' : '+',
(int)((w >> 30) & 077),
(int)((w >> 24) & 077),
- x,
- y);
+ LF(x),
+ LF(y));
if (ix)
{
if (op != 0120)
{
- word i = rd(ix);
- xi = (xi + (int)((i >> 12) & 07777)) & 07777;
- yi = (yi + (int)(i & 07777)) & 07777;
+ loc iaddr = { 0, ix };
+ word i = rd(iaddr);
+ xi.address = (xi.address + (int)((i >> 12) & 07777)) & 07777;
+ yi.address = (yi.address + (int)(i & 07777)) & 07777;
if (trace > 2)
- printf("\tIndexing -> %04o %04o\n", xi, yi);
+ printf("\tIndexing -> %d:%04o %d:%04o\n", LF(xi), LF(yi));
}
}
ip = (ip+1) & 07777;
astore(wfromfloat(f, 0));
}
- if (ax)
+ if (ax && memblocks == 1) // Reject address extensions if we only have 1 memory block
op = -1;
switch (op)
{
case 0120: // Loop
if (!ix)
noins();
- a = r1 = rd(ix);
+ loc iaddr = { 0, ix };
+ a = r1 = rd(iaddr);
aa = (a >> 24) & 017777;
if (!aa)
break;
acc = ((aa-1) << 24) |
(((((a >> 12) & 07777) + (b >> 12) & 07777) & 07777) << 12) |
(((a & 07777) + (b & 07777)) & 07777);
- wr(ix, acc);
- ip = x;
+ wr(iaddr, acc);
+ ip = x.address;
break;
case 0130: // Jump
wr(y, r2);
- ip = x;
+ ip = x.address;
break;
case 0131: // Jump to subroutine
- wr(y, acc = ((030ULL << 30) | ((ip & 07777ULL) << 12)));
- ip = x;
+ wr(y, acc = ((0130ULL << 30) | ((ip & 07777ULL) << 12)));
+ ip = x.address;
break;
case 0132: // Jump if positive
if (wsign(r2) >= 0)
- ip = x;
+ ip = x.address;
else
- ip = y;
+ ip = y.address;
break;
case 0133: // Jump if overflow
// Since we always trap on overflow, this instruction always jumps to the 1st address
- ip = x;
+ ip = x.address;
break;
case 0134: // Jump if zero
if (!wabs(r2))
- ip = y;
+ ip = y.address;
else
- ip = x;
+ ip = x.address;
break;
case 0135: // Jump if key pressed
// No keys are ever pressed, so always jump to 2nd
- ip = y;
+ ip = y.address;
break;
case 0136: // Interrupt masking
notimp();
case 0160 ... 0161: // I/O
notimp();
case 0162: // Printing
- print_ins(x, y);
+ print_ins(x.address, y);
break;
case 0163: // I/O
notimp();
if (!wabs(a))
{
wr(yi, 0);
- wr((yi+1) & 07777, 0);
+ loc yinc = { yi.block, (yi.address+1) & 07777 };
+ wr(yinc, 0);
acc = 0;
}
else
}
acc |= a;
wr(yi, acc);
- wr((yi+1) & 07777, i);
+ loc yinc = { yi.block, (yi.address+1) & 07777 };
+ wr(yinc, i);
}
break;
case 0176: // Population count
}
}
+NORETURN static void die(char *msg)
+{
+ fprintf(stderr, "minsk: %s\n", msg);
+ exit(1);
+}
+
/*** Daemon interface ***/
+#ifdef ENABLE_DAEMON_MODE
+
+/*
+ * The daemon mode was a quick hack for the Po drate contest.
+ * Most parameters are hard-wired.
+ */
+
#include <unistd.h>
#include <errno.h>
#include <time.h>
#define UID 124
#define GID 125
-static void die(char *msg)
-{
- fprintf(stderr, "minsk: ");
- fprintf(stderr, msg);
- fputc('\n', stderr);
- exit(1);
-}
-
static char **spt_argv;
static char *spt_start, *spt_end;
va_end(args);
}
-static void sigchld_handler(int sig __attribute__((unused)))
+static void sigchld_handler(int sig UNUSED)
{
}
-static void sigalrm_handler(int sig __attribute__((unused)))
+static void sigalrm_handler(int sig UNUSED)
{
const char err[] = "--- Timed out. Time machine disconnected. ---\n";
write(1, err, sizeof(err));
}
}
-static void init_memory(void)
+#else
+
+static void run_as_daemon(int do_fork UNUSED)
{
- // For the contest, we fill the whole memory with -00 00 0000 0000 (HALT),
- // not +00 00 0000 0000 (NOP). Otherwise, an empty program would reveal
- // the location of the password :)
- for (int i=0; i<4096; i++)
- mem[i] = 01000000000000ULL;
-
- // Store the password
- int pos = 02655;
- mem[pos++] = 0574060565373;
- mem[pos++] = 0371741405340;
- mem[pos++] = 0534051524017;
+ die("Daemon mode not supported in this version, need to recompile.");
}
-int main(int argc, char **argv)
+static void setproctitle_init(int argc UNUSED, char **argv UNUSED)
+{
+}
+
+#endif
+
+static void init_memory(int set_password)
{
- init_memory();
+ mem = malloc(memblocks * sizeof(word *));
+ for (int i=0; i<memblocks; i++)
+ mem[i] = malloc(MEM_SIZE * sizeof(word));
- if (argc > 1)
+ if (set_password)
{
- setproctitle_init(argc, argv);
- if (!strcmp(argv[1], "--daemon"))
- run_as_daemon(1);
- else if (!strcmp(argv[1], "--net"))
- run_as_daemon(0);
- else if (!strncmp(argv[1], "--trace=", 8))
- trace = atoi(argv[1] + 8);
- else
- die("Usage: minsk [--daemon | --net]");
+ // For the contest, we fill the whole memory with -00 00 0000 0000 (HALT),
+ // not +00 00 0000 0000 (NOP). Otherwise, an empty program would reveal
+ // the location of the password :)
+ for (int i=0; i<memblocks; i++)
+ for (int j=0; j<MEM_SIZE; j++)
+ mem[i][j] = 01000000000000ULL;
+
+ // Store the password
+ int pos = 02655;
+ mem[0][pos++] = 0574060565373;
+ mem[0][pos++] = 0371741405340;
+ mem[0][pos++] = 0534051524017;
}
+ else
+ for (int i=0; i<memblocks; i++)
+ for (int j=0; j<MEM_SIZE; j++)
+ mem[i][j] = 00000000000000ULL;
+}
+
+static const struct option longopts[] = {
+ { "cpu-quota", required_argument, NULL, 'q' },
+ { "daemon", no_argument, NULL, 'd' },
+ { "nofork", no_argument, NULL, 'n' },
+ { "english", no_argument, NULL, 'e' },
+ { "set-password", no_argument, NULL, 's' },
+ { "upgrade", no_argument, NULL, 'u' },
+ { "print-quota", required_argument, NULL, 'p' },
+ { "trace", required_argument, NULL, 't' },
+ { NULL, 0, NULL, 0 },
+};
+
+static void usage(void)
+{
+ fprintf(stderr, "Options:\n\n");
+ #ifdef ENABLE_DAEMON_MODE
+ fprintf(stderr, "\
+-d, --daemon Run as daemon and listen for network connections\n\
+-n, --nofork When run with --daemon, avoid forking\n\
+");
+ #endif
+ fprintf(stderr, "\
+-e, --english Print messages in English\n\
+-s, --set-password Put hidden password in memory\n\
+-u, --upgrade Upgrade the Minsk-2 to the Minsk-22\n\
+-t, --trace=<level> Enable tracing of program execution\n\
+-q, --cpu-quota=<n> Set CPU quota to <n> instructions\n\
+-p, --print-quota=<n> Set printer quota to <n> lines\n\
+");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ int daemon_mode = 0;
+ int do_fork = 1;
+ int set_password = 0;
+
+ while ((opt = getopt_long(argc, argv, "q:desunp:t:", longopts, NULL)) >= 0)
+ switch (opt)
+ {
+ case 'd':
+ daemon_mode = 1;
+ break;
+ case 'n':
+ do_fork = 0;
+ break;
+ case 'e':
+ english = 1;
+ break;
+ case 's':
+ set_password = 1;
+ break;
+ case 'u':
+ memblocks = 2;
+ break;
+ case 'p':
+ print_quota = atoi(optarg);
+ break;
+ case 'q':
+ cpu_quota = atoi(optarg);
+ break;
+ case 't':
+ trace = atoi(optarg);
+ break;
+ default:
+ usage();
+ }
+ if (optind < argc)
+ usage();
+
+ setproctitle_init(argc, argv);
+ init_memory(set_password);
+
+ if (daemon_mode)
+ run_as_daemon(do_fork);
parse_in();
run();
+
return 0;
}