51 const char *slash = strrchr(argv0,
'/');
54 int dlen = (int)(slash - argv0);
55 snprintf(path,
sizeof(path),
"%.*s/console/curses/console.py", dlen, argv0);
57 snprintf(path,
sizeof(path),
"console/curses/console.py");
59 if (access(path, R_OK) != 0)
60 snprintf(path,
sizeof(path),
"console/curses/console.py");
61 if (access(path, R_OK) != 0) {
62 fprintf(stderr,
"error: --tui: cannot find console/curses/console.py\n");
72 execlp(
"python3",
"python3", path, (
char *)NULL);
73 perror(
"error: --tui: cannot exec python3");
82 "Usage: %s [OPTIONS] [deck.cap]\n"
84 " deck.cap A card deck. It is placed in the reader's hopper and\n"
85 " pulled in by the machine's own bootstrap: CLEAR, LOAD1,\n"
86 " LOAD, START. The IPL reads exactly ONE card (80 columns\n"
87 " nibble-packed to 40 bytes at 0x0000) and executes it;\n"
88 " that card's code pulls the rest of the deck.\n"
90 " A deck is the only way to get a program into the machine, here as on the\n"
91 " iron. Build one with `gasm -o prog.cap prog.s` or `gec -o prog.cap prog.c`.\n"
94 " --deck <path> Explicit alias for the positional .cap argument\n"
95 " --sat <id> Use a built-in Site Acceptance Test batch\n"
96 " --list-sat List the built-in SAT batches and exit\n"
97 " --trace <spec> Enable log types from spec string\n"
98 " --max-cycles <N> Maximum CPU cycles before forced exit (default: 100000,\n"
99 " or 500000 for --deck unless overridden)\n"
100 " --console Enable the console socket /tmp/gemu.console (no UI attached)\n"
101 " --tui Implies --console and starts the ncurses console client\n"
102 " --interactive, -i Run until killed; SIGUSR1/SIGUSR2 toggle SWITCH 1/2 at\n"
103 " runtime (prints the pid + step/halt progress)\n"
104 " --switch1 Start with SWITCH 1 (JS1) on\n"
105 " --switch2 Start with SWITCH 2 (JS2) on\n"
106 " (console/curses/console.py); runs until you quit the TUI\n"
107 " --help, -h Print this help and exit\n",
111int main(
int argc,
char *argv[])
115 long max_cycles = 100000;
116 int max_cycles_set = 0;
121 const char *deck_path = NULL;
122 const char *disk_path = NULL;
123 const char *tape_path = NULL;
124 const char *sat_batch = NULL;
130 for (
int i = 1; i < argc; i++) {
131 if (strcmp(argv[i],
"--help") == 0 || strcmp(argv[i],
"-h") == 0) {
134 }
else if (strcmp(argv[i],
"--console") == 0) {
136 }
else if (strcmp(argv[i],
"--tui") == 0) {
139 }
else if (strcmp(argv[i],
"--deck") == 0) {
141 fprintf(stderr,
"error: --deck requires an argument\n");
144 deck_path = argv[++i];
145 }
else if (strcmp(argv[i],
"--disk") == 0) {
147 fprintf(stderr,
"error: --disk requires an argument\n");
150 disk_path = argv[++i];
151 }
else if (strcmp(argv[i],
"--tape") == 0) {
153 fprintf(stderr,
"error: --tape requires an argument\n");
156 tape_path = argv[++i];
157 }
else if (strcmp(argv[i],
"--sat") == 0) {
159 fprintf(stderr,
"error: --sat requires an argument\n");
162 sat_batch = argv[++i];
163 }
else if (strcmp(argv[i],
"--list-sat") == 0) {
166 printf(
"%-20s %s\n", info->
id, info->
title);
167 printf(
" %s\n", info->
summary);
170 }
else if (strcmp(argv[i],
"--trace") == 0) {
172 fprintf(stderr,
"error: --trace requires an argument\n");
177 }
else if (strcmp(argv[i],
"--max-cycles") == 0) {
179 fprintf(stderr,
"error: --max-cycles requires an argument\n");
182 max_cycles = atol(argv[++i]);
184 if (max_cycles <= 0) {
185 fprintf(stderr,
"error: --max-cycles must be a positive integer\n");
188 }
else if (strcmp(argv[i],
"--interactive") == 0 || strcmp(argv[i],
"-i") == 0) {
190 }
else if (strcmp(argv[i],
"--switch1") == 0) {
192 }
else if (strcmp(argv[i],
"--switch2") == 0) {
194 }
else if (argv[i][0] ==
'-') {
195 fprintf(stderr,
"error: unknown option '%s'\n", argv[i]);
198 }
else if (!deck_path && !sat_batch) {
201 const char *p = argv[i];
202 size_t n = strlen(p);
203 if (n < 4 || strcmp(p + n - 4,
".cap") != 0) {
205 "error: '%s' is not a .cap card deck.\n"
206 " A program reaches the machine only on cards; build a deck with\n"
207 " `gasm -o prog.cap prog.s` or `gec -o prog.cap prog.c`.\n", p);
212 fprintf(stderr,
"error: unexpected argument '%s'\n", argv[i]);
218 if (deck_path && sat_batch) {
219 fprintf(stderr,
"error: give only one of a .cap deck, --deck, or --sat\n");
226 if ((deck_path || sat_batch) && !max_cycles_set)
246 static const char sat_cap_path[] =
"/tmp/gemu_sat_batch.cap";
249 fprintf(stderr,
"error: unknown SAT batch '%s' (use --list-sat)\n", sat_batch);
255 sat_cap_path, note,
sizeof(note)) != 0) {
256 fprintf(stderr,
"error: failed to compose SAT batch '%s'\n", sat_batch);
264 fprintf(stderr,
"error: failed to load SAT batch '%s'\n", sat_batch);
268 fprintf(stderr,
"SAT batch %s: %s\n", sat_batch, note);
269 }
else if (deck_path) {
274 fprintf(stderr,
"error: failed to load deck '%s'\n", deck_path);
289 fprintf(stderr,
"warning: failed to attach disk '%s'\n", disk_path);
291 fprintf(stderr,
"disk: attached '%s' on connector 3 unit 0\n", disk_path);
297 fprintf(stderr,
"warning: failed to attach tape '%s'\n", tape_path);
299 fprintf(stderr,
"tape: attached '%s' on connector 4 unit 0\n", tape_path);
302 int printer_enabled = 0;
318 kbd_fl = fcntl(0, F_GETFL, 0);
320 fcntl(0, F_SETFL, kbd_fl | O_NONBLOCK);
337 if (!printer_enabled) {
341 kbd_fl = fcntl(0, F_GETFL, 0);
343 fcntl(0, F_SETFL, kbd_fl | O_NONBLOCK);
345 long pid = (long)getpid();
346 printf(
"interactive: pid=%ld SWITCH1=%d SWITCH2=%d\n", pid,
ge.
JS1,
ge.
JS2);
347 printf(
" kill -USR1 %ld # toggle SWITCH 1 (JS1)\n", pid);
348 printf(
" kill -USR2 %ld # toggle SWITCH 2 (JS2)\n", pid);
349 printf(
" type to feed the operator keyboard; printer output appears as 'PRN> ...'\n");
351 uint8_t last_step =
ge.
mem[0x0010];
356 if (olen > printed) {
358 printf(
"PRN> %.*s", olen - printed, o + printed);
364 unsigned char kb[64];
365 ssize_t r = read(0, kb,
sizeof kb);
366 for (ssize_t k = 0; k < r; k++)
371 printf(
"[cyc %ld] SWITCH 1 -> %d PO=%04x step=0x%02x%s\n",
378 printf(
"[cyc %ld] SWITCH 2 -> %d PO=%04x step=0x%02x%s\n",
384 if (was_halted != 1) {
385 printf(
"[cyc %ld] HALT PO=%04x step=0x%02x\n",
398 uint8_t st =
ge.
mem[0x0010];
399 if (st != last_step) {
400 printf(
"[cyc %ld] step -> 0x%02x PO=%04x\n", cycles, st,
ge.
rPO);
405 }
else if (use_tui) {
421 while (waitpid(tui_pid, NULL, WNOHANG) == 0) {
431 kill(tui_pid, SIGTERM);
432 waitpid(tui_pid, NULL, 0);
435 if (printer_enabled) {
437 if (olen > printed) {
439 fwrite(o + printed, 1, (
size_t)(olen - printed), stdout);
443 unsigned char kb[64];
444 ssize_t r = read(0, kb,
sizeof kb);
445 for (ssize_t k = 0; k < r; k++)
455 printf(
"exit: halted=%d cycles=%ld max=%ld error=%d state=%02x PO=%04x\n",
int cardreader_register(struct ge *ge, const char *cap_path, enum transcode_mode mode)
int console_socket_register(struct ge *ge)
int disk_register(struct ge *ge, const char *image_path, uint8_t connector, uint8_t unit)
int ge_deinit(struct ge *ge)
Deinitialize the emulator.
void ge_load_1(struct ge *ge)
Emulate the press of the "load 1" button in the console.
void ge_clear(struct ge *ge)
Emulate the press of the "clear" button in the console.
int ge_run_cycle(struct ge *ge)
Run all GE "mastri" clock periods until next clock cycle.
void ge_init(struct ge *ge)
Initialize the emulator.
void ge_load(struct ge *ge)
Emulate the press of the "load" button in the console.
void ge_start(struct ge *ge)
Emulate the press of the "start" button in the console.
static uint8_t ge_halted(const struct ge *ge)
Is the CPU stopped?
int main(int argc, char *argv[])
static void on_sigusr2(int sig)
void ge_log_set_active_types_from_spec(const char *spec)
Set active log types from a comma-separated name specification.
static void print_usage(const char *argv0)
static void on_sigusr1(int sig)
static volatile sig_atomic_t g_toggle_js1
static pid_t spawn_tui(const char *argv0)
static volatile sig_atomic_t g_toggle_js2
int printer_output_len(struct ge *ge)
int printer_register(struct ge *ge)
const char * printer_output(struct ge *ge)
void printer_feed_key(struct ge *ge, uint8_t c)
const struct sat_batch_info * sat_batch_find(const char *id)
int sat_batch_count(void)
const struct sat_batch_info * sat_batch_info_at(int idx)
int sat_batch_prepare_deck(const char *root, const char *id, const char *out_path, char *note, size_t note_sz)
The entire state of the emulated system, including registers, memory, peripherals and timings.
uint8_t JS2
Console jump condition 2.
uint16_t rPO
Program addresser.
uint8_t rSO
Main sequencer.
uint8_t mem[MEM_SIZE]
The memory of the emulated system.
uint8_t JS1
Console jump condition 1.
int tape_register(struct ge *ge, const char *image_path, uint8_t connector, uint8_t unit)