*/
#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 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
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);
}
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] == '@')
{
if (*c >= '0' && *c <= '7')
addr = 8*addr + *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("Номер слишком долгий");
+ parse_error("Номер слишком долгий", "Number too long");
wr(addr++, w);
addr &= 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");
}
}
+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)
{
- init_memory();
+}
- if (argc > 1)
+#endif
+
+static void init_memory(int set_password)
+{
+ 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<MEM_SIZE; i++)
+ mem[i] = 01000000000000ULL;
+
+ // Store the password
+ int pos = 02655;
+ mem[pos++] = 0574060565373;
+ mem[pos++] = 0371741405340;
+ mem[pos++] = 0534051524017;
}
+ else
+ for (int i=0; i<MEM_SIZE; i++)
+ mem[i] = 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' },
+ { "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\
+-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:desnp: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 '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();