GE-115 Emulator
An Emulator of the General Electrics GE-115 computer
main.c
Go to the documentation of this file.
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <stdint.h>
5#include <unistd.h>
6#include <signal.h>
7#include <sys/types.h>
8#include <sys/wait.h>
9#include "ge.h"
10#include "console_socket.h"
11#include "cardreader.h"
12#include "printer.h"
13#include "disk.h"
14#include "tape.h"
15#include "cap.h"
16#include "transcode.h"
17#include "binimage.h"
18#include "log.h"
19#include "sat_batches.h"
20#include <fcntl.h>
21
22/*
23 * Forward declaration for ge_log_set_active_types_from_spec, which is being
24 * added concurrently in log.c/log.h by another agent. Once that lands the
25 * prototype in log.h this extern becomes redundant but harmless.
26 */
27extern void ge_log_set_active_types_from_spec(const char *spec);
28
29/*
30 * Launch the ncurses console client (console/curses/console.py) as a child
31 * process for --tui. The client connects to the /tmp/gemu.console socket that
32 * --console registers and draws the operator/diagnostic panel; the emulator
33 * keeps running in this (parent) process. Returns the child pid, or -1 if the
34 * client could not be found / launched.
35 *
36 * The script is looked for next to the ge executable first (so it works from
37 * any cwd), then relative to the current directory.
38 */
39/* Interactive console switches driven by signals: SIGUSR1 toggles SWITCH 1
40 * (JS1), SIGUSR2 toggles SWITCH 2 (JS2). The handler only sets a flag; the
41 * run loop applies it between cycles (so we never touch ge state from a
42 * handler). Lets a human (or an automated harness) flip the diagnostic
43 * switches mid-run: e.g. start the funktionalcpu test with SWITCH 2 on, then
44 * `kill -USR2 <pid>` to release it and watch where the deck goes. */
45static volatile sig_atomic_t g_toggle_js1 = 0;
46static volatile sig_atomic_t g_toggle_js2 = 0;
47static void on_sigusr1(int sig) { (void)sig; g_toggle_js1 = 1; }
48static void on_sigusr2(int sig) { (void)sig; g_toggle_js2 = 1; }
49
50static pid_t spawn_tui(const char *argv0)
51{
52 char path[4096];
53 const char *slash = strrchr(argv0, '/');
54
55 if (slash) {
56 int dlen = (int)(slash - argv0);
57 snprintf(path, sizeof(path), "%.*s/console/curses/console.py", dlen, argv0);
58 } else {
59 snprintf(path, sizeof(path), "console/curses/console.py");
60 }
61 if (access(path, R_OK) != 0)
62 snprintf(path, sizeof(path), "console/curses/console.py");
63 if (access(path, R_OK) != 0) {
64 fprintf(stderr, "error: --tui: cannot find console/curses/console.py\n");
65 return -1;
66 }
67
68 pid_t pid = fork();
69 if (pid < 0) {
70 perror("fork");
71 return -1;
72 }
73 if (pid == 0) {
74 execlp("python3", "python3", path, (char *)NULL);
75 perror("error: --tui: cannot exec python3");
76 _exit(127);
77 }
78 return pid;
79}
80
81static void print_usage(const char *argv0)
82{
83 fprintf(stderr,
84 "Usage: %s [OPTIONS] [prog.bin]\n"
85 "\n"
86 " prog.bin Unified-format image (gasm output); loaded directly\n"
87 " at its origin and entered at its entry point.\n"
88 "\n"
89 "Options:\n"
90 " --raw Treat the positional file as a headerless flat image\n"
91 " --org <0xNNNN> Load origin for --raw (default 0x0000; entry = origin)\n"
92 " --poke <A=V> Write byte V to address A after load (repeatable),\n"
93 " e.g. --poke 0x0E00=0x80 to set a diagnostic option\n"
94 " --deck <path> Path to a .cap card deck; loaded via the reader (connector 2)\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",
108 argv0);
109}
110
111int main(int argc, char *argv[])
112{
113 struct ge ge;
114 int ret = 0;
115 long max_cycles = 100000;
116 int max_cycles_set = 0;
117 long cycles = 0;
118 int use_console = 0;
119 int use_tui = 0;
120 int trace_set = 0;
121 const char *deck_path = NULL; /* --deck: cycle-faithful card-reader bootstrap */
122 const char *disk_path = NULL; /* --disk: DSS pack image on connector 3 unit 0 */
123 const char *tape_path = NULL; /* --tape: MTC reel image on connector 4 unit 0 */
124 const char *sat_batch = NULL; /* --sat: built-in SAT batch */
125 const char *cap_path = NULL; /* positional .cap: scatter-load (default) */
126 const char *image_path = NULL;
127 int raw = 0;
128 long load_org = 0x0000;
129 uint16_t poke_addr[32]; uint8_t poke_val[32]; int npoke = 0;
130 int interactive = 0; /* --interactive: run until killed, switches via signals */
131 int sw1_init = 0; /* --switch1: start with SWITCH 1 (JS1) on */
132 int sw2_init = 0; /* --switch2: start with SWITCH 2 (JS2) on */
133
134 /* --- argument parsing: --opt value style --- */
135 for (int i = 1; i < argc; i++) {
136 if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
137 print_usage(argv[0]);
138 return 0;
139 } else if (strcmp(argv[i], "--console") == 0) {
140 use_console = 1;
141 } else if (strcmp(argv[i], "--tui") == 0) {
142 use_tui = 1;
143 use_console = 1; /* --tui implies --console */
144 } else if (strcmp(argv[i], "--deck") == 0) {
145 if (i + 1 >= argc) {
146 fprintf(stderr, "error: --deck requires an argument\n");
147 return 1;
148 }
149 deck_path = argv[++i];
150 } else if (strcmp(argv[i], "--disk") == 0) {
151 if (i + 1 >= argc) {
152 fprintf(stderr, "error: --disk requires an argument\n");
153 return 1;
154 }
155 disk_path = argv[++i]; /* DSS pack image; connector 3, unit 0 */
156 } else if (strcmp(argv[i], "--tape") == 0) {
157 if (i + 1 >= argc) {
158 fprintf(stderr, "error: --tape requires an argument\n");
159 return 1;
160 }
161 tape_path = argv[++i]; /* MTC reel image; connector 4, unit 0 */
162 } else if (strcmp(argv[i], "--sat") == 0) {
163 if (i + 1 >= argc) {
164 fprintf(stderr, "error: --sat requires an argument\n");
165 return 1;
166 }
167 sat_batch = argv[++i];
168 } else if (strcmp(argv[i], "--list-sat") == 0) {
169 for (int j = 0; j < sat_batch_count(); j++) {
170 const struct sat_batch_info *info = sat_batch_info_at(j);
171 printf("%-20s %s\n", info->id, info->title);
172 printf(" %s\n", info->summary);
173 }
174 return 0;
175 } else if (strcmp(argv[i], "--trace") == 0) {
176 if (i + 1 >= argc) {
177 fprintf(stderr, "error: --trace requires an argument\n");
178 return 1;
179 }
181 trace_set = 1;
182 } else if (strcmp(argv[i], "--max-cycles") == 0) {
183 if (i + 1 >= argc) {
184 fprintf(stderr, "error: --max-cycles requires an argument\n");
185 return 1;
186 }
187 max_cycles = atol(argv[++i]);
188 max_cycles_set = 1;
189 if (max_cycles <= 0) {
190 fprintf(stderr, "error: --max-cycles must be a positive integer\n");
191 return 1;
192 }
193 } else if (strcmp(argv[i], "--interactive") == 0 || strcmp(argv[i], "-i") == 0) {
194 interactive = 1;
195 } else if (strcmp(argv[i], "--switch1") == 0) {
196 sw1_init = 1;
197 } else if (strcmp(argv[i], "--switch2") == 0) {
198 sw2_init = 1;
199 } else if (strcmp(argv[i], "--bin") == 0) {
200 /* Force direct binary (unified-format) load — debugging path. */
201 if (i + 1 >= argc) {
202 fprintf(stderr, "error: --bin requires an argument\n");
203 return 1;
204 }
205 image_path = argv[++i];
206 } else if (strcmp(argv[i], "--raw") == 0) {
207 raw = 1;
208 } else if (strcmp(argv[i], "--org") == 0) {
209 if (i + 1 >= argc) {
210 fprintf(stderr, "error: --org requires an argument\n");
211 return 1;
212 }
213 load_org = strtol(argv[++i], NULL, 0);
214 } else if (strcmp(argv[i], "--poke") == 0) {
215 if (i + 1 >= argc) {
216 fprintf(stderr, "error: --poke requires ADDR=VAL\n");
217 return 1;
218 }
219 char *eq = strchr(argv[i + 1], '=');
220 if (!eq || npoke >= 32) {
221 fprintf(stderr, "error: bad --poke '%s' (want 0xADDR=0xVAL)\n", argv[i + 1]);
222 return 1;
223 }
224 poke_addr[npoke] = (uint16_t)strtoul(argv[i + 1], NULL, 0);
225 poke_val[npoke] = (uint8_t)strtoul(eq + 1, NULL, 0);
226 npoke++;
227 i++;
228 } else if (argv[i][0] == '-') {
229 fprintf(stderr, "error: unknown option '%s'\n", argv[i]);
230 print_usage(argv[0]);
231 return 1;
232 } else if (!image_path && !deck_path && !sat_batch) {
233 /* Positional input. The DEFAULT is the authentic card-reader flow:
234 * a `.cap` deck is loaded by scattering each card's payload to its
235 * embedded load address (cap_load_scattered — the deck format), then
236 * entering at the lowest loaded address. Any other file (e.g. a
237 * `.bin`) is a direct unified-format binary load — the debugging path.
238 * `--deck` forces the cycle-faithful card-reader bootstrap; `--bin`
239 * forces direct binary load. */
240 const char *p = argv[i];
241 size_t n = strlen(p);
242 if (n >= 4 && strcmp(p + n - 4, ".cap") == 0)
243 cap_path = p;
244 else
245 image_path = p;
246 } else {
247 fprintf(stderr, "error: unexpected argument '%s'\n", argv[i]);
248 print_usage(argv[0]);
249 return 1;
250 }
251 }
252
253 if ((deck_path && image_path) || (deck_path && cap_path) || (deck_path && sat_batch) ||
254 (cap_path && image_path) || (cap_path && sat_batch) || (image_path && sat_batch)) {
255 fprintf(stderr, "error: give only one of a .cap deck, --deck, --sat, or --bin\n");
256 return 1;
257 }
258
259 /* The cycle-faithful card-reader bootstrap is substantially slower than a
260 * direct binary or scatter-loaded image. Give `--deck` a roomier default so
261 * a real deck does not time out during the load unless the user explicitly
262 * requested a tighter budget. */
263 if ((deck_path || sat_batch) && !max_cycles_set)
264 max_cycles = 500000;
265
266 ge_init(&ge);
267
268 if (use_console) {
270 if (ret != 0) {
271 ge_deinit(&ge);
272 return ret;
273 }
274 }
275
276 ge_clear(&ge);
277
278 /* Faithful load: drive the LOAD button so the bootstrap pulls the deck in
279 * through the card reader on connector 2 (channel 1), exactly as the
280 * bootstrap.c test does — no direct mem[] writes. */
281 int image_loaded = 0;
282 uint16_t image_entry = 0;
283 if (sat_batch) {
284 char note[256];
285 const struct sat_batch_info *info = sat_batch_find(sat_batch);
286 if (!info) {
287 fprintf(stderr, "error: unknown SAT batch '%s' (use --list-sat)\n", sat_batch);
288 ge_deinit(&ge);
289 return 1;
290 }
291
292 if (info->launch == SAT_BATCH_IMAGE) {
293 static uint8_t scat[MEM_SIZE];
294 unsigned lo = 0, hi = 0;
295 uint16_t entry = 0;
296
297 if (sat_batch_prepare_image("Site_Acceptance_Test", sat_batch,
298 scat, &lo, &hi, &entry,
299 note, sizeof(note)) != 0) {
300 fprintf(stderr, "error: failed to prepare SAT batch '%s'\n", sat_batch);
301 ge_deinit(&ge);
302 return 1;
303 }
304
305 if (ge_load_image(&ge, scat + lo, (uint16_t)(hi - lo + 1), (uint16_t)lo) != 0) {
306 fprintf(stderr, "error: SAT image '%s' does not fit in memory\n", sat_batch);
307 ge_deinit(&ge);
308 return 1;
309 }
311 image_loaded = 1;
312 image_entry = entry;
313 fprintf(stderr, "SAT batch %s: %s\n", sat_batch, note);
314 } else {
315 static const char sat_cap_path[] = "/tmp/gemu_sat_batch.cap";
316 if (sat_batch_prepare_deck("Site_Acceptance_Test", sat_batch,
317 sat_cap_path, note, sizeof(note)) != 0) {
318 fprintf(stderr, "error: failed to compose SAT batch '%s'\n", sat_batch);
319 ge_deinit(&ge);
320 return 1;
321 }
322 ge_load_1(&ge);
323 ge_load(&ge);
324 ret = cardreader_register(&ge, sat_cap_path, TC_NORMAL);
325 if (ret != 0) {
326 fprintf(stderr, "error: failed to load SAT batch '%s'\n", sat_batch);
327 ge_deinit(&ge);
328 return ret;
329 }
330 fprintf(stderr, "SAT batch %s: %s\n", sat_batch, note);
331 }
332 } else if (deck_path) {
333 ge_load_1(&ge); /* select connector 2 (LOAD1) */
334 ge_load(&ge); /* set AINI: state 80 -> c8 starts the load sequence */
335 ret = cardreader_register(&ge, deck_path, TC_NORMAL);
336 if (ret != 0) {
337 fprintf(stderr, "error: failed to load deck '%s'\n", deck_path);
338 ge_deinit(&ge);
339 return ret;
340 }
341 } else if (cap_path) {
342 /* Default .cap load: scatter each card's payload to its embedded load
343 * address (the deck format), then enter at the lowest loaded address.
344 * Honours the card addresses exactly like `gdis --image`; distinct from
345 * the cycle-faithful card-reader bootstrap (--deck). */
346 static uint8_t scat[MEM_SIZE];
347 unsigned lo = 0, hi = 0;
348 memset(scat, 0, sizeof scat);
349
350 int ncards = cap_load_scattered(cap_path, TC_COLBIN, scat, &lo, &hi);
351 if (ncards < 0) {
352 fprintf(stderr, "error: failed to scatter-load .cap '%s'\n", cap_path);
353 ge_deinit(&ge);
354 return 1;
355 }
356
357 uint16_t len = (uint16_t)(hi - lo + 1);
358 if (ge_load_image(&ge, scat + lo, len, (uint16_t)lo) != 0) {
359 fprintf(stderr, "error: scattered image does not fit in memory\n");
360 ge_deinit(&ge);
361 return 1;
362 }
364 image_loaded = 1;
365 image_entry = (uint16_t)lo;
366 ge_log(LOG_DEBUG, "scatter-loaded %d cards, span 0x%04x-0x%04x, entry 0x%04x\n",
367 ncards, lo, hi, lo);
368 } else if (image_path) {
369 /* Direct binary load: read the unified-format image (gasm output) and
370 * place it at its origin; entry comes from the header. With --raw the
371 * file is a headerless flat blob loaded at --org (entry = origin). */
372 static uint8_t buf[MEM_SIZE];
373 uint16_t origin, entry, len;
374 FILE *f = fopen(image_path, "rb");
375 if (!f) {
376 fprintf(stderr, "error: cannot open image '%s'\n", image_path);
377 ge_deinit(&ge);
378 return 1;
379 }
380 if (raw) {
381 size_t n = fread(buf, 1, sizeof buf, f);
382 origin = (uint16_t)load_org;
383 entry = origin;
384 len = (uint16_t)n;
385 } else {
386 int rc = binimage_read(f, &origin, &entry, buf, sizeof buf, &len);
387 if (rc != BINIMAGE_OK) {
388 fprintf(stderr, "error: %s: %s\n", image_path, binimage_strerror(rc));
389 fclose(f);
390 ge_deinit(&ge);
391 return 1;
392 }
393 }
394 fclose(f);
395 if (ge_load_image(&ge, buf, len, origin) != 0) {
396 fprintf(stderr, "error: image does not fit in memory\n");
397 ge_deinit(&ge);
398 return 1;
399 }
400 /* A contiguous image spanning 0x00F0-0x00FF overwrites the segment-base
401 * registers ge_clear set up; re-establish the identity bases so paged
402 * addressing works (programs may still reload bases at runtime). */
404 image_loaded = 1;
405 image_entry = entry;
406 ge_log(LOG_DEBUG, "loaded %u bytes at 0x%04x, entry 0x%04x\n",
407 (unsigned)len, origin, entry);
408 }
409
410 /* Apply --poke writes after the image + base seeding (e.g. the diagnostic
411 * console test-selection byte at 0x0E00) so they override loaded values. */
412 for (int p = 0; p < npoke; p++) {
413 ge.mem[poke_addr[p]] = poke_val[p];
414 ge.mem_parity[poke_addr[p]] = __builtin_parity(poke_val[p]) ? 0 : 1;
415 ge.mem_written[poke_addr[p]] = 1;
416 ge_log(LOG_DEBUG, "poke mem[0x%04x] = 0x%02x\n", poke_addr[p], poke_val[p]);
417 }
418
419 ge_start(&ge);
420
421 /* Direct binary load enters at the header's entry point without the
422 * peripheral LOAD bootstrap. */
423 if (image_loaded)
424 ge_enter(&ge, image_entry);
425
426 /* Console switch initial state (after ge_start, which clears them). */
427 ge.JS1 = sw1_init;
428 ge.JS2 = sw2_init;
429
430 /* Attach a DSS disk pack on connector 3 (standard GE-100), if requested. */
431 if (disk_path) {
432 if (disk_register(&ge, disk_path, 3, 0) != 0)
433 fprintf(stderr, "warning: failed to attach disk '%s'\n", disk_path);
434 else
435 fprintf(stderr, "disk: attached '%s' on connector 3 unit 0\n", disk_path);
436 }
437
438 /* Attach an MTC tape reel on connector 4 (standard GE-100), if requested. */
439 if (tape_path) {
440 if (tape_register(&ge, tape_path, 4, 0) != 0)
441 fprintf(stderr, "warning: failed to attach tape '%s'\n", tape_path);
442 else
443 fprintf(stderr, "tape: attached '%s' on connector 4 unit 0\n", tape_path);
444 }
445
446 int printer_enabled = 0;
447 int printed = 0;
448 int kbd_fl = -1;
449 if (image_loaded && !use_tui) {
451 printer_enabled = 1;
452 kbd_fl = fcntl(0, F_GETFL, 0);
453 if (kbd_fl != -1)
454 fcntl(0, F_SETFL, kbd_fl | O_NONBLOCK);
455 }
456
457 if (interactive) {
458 /* Signal-driven interactive run: flip the diagnostic switches with
459 * `kill -USR1/-USR2 <pid>` and watch the deck. Run until killed.
460 * Freeze PC on HLT (the GE-120 sequencer is frozen by ALTO when
461 * halted) so the stop address stays readable; signals are still
462 * serviced so you can record a switch change before restarting. */
463 signal(SIGUSR1, on_sigusr1);
464 signal(SIGUSR2, on_sigusr2);
465 if (!trace_set)
467 /* Integrated printer/typewriter on channel 2: completes print PERs (so
468 * the machine does not hang waiting for a device gemu does not drive at
469 * signal level) and captures output. Two-way: bytes typed on stdin are
470 * fed to the operator keyboard queue (non-blocking). */
471 if (!printer_enabled) {
473 printer_enabled = 1;
474 }
475 kbd_fl = fcntl(0, F_GETFL, 0);
476 if (kbd_fl != -1)
477 fcntl(0, F_SETFL, kbd_fl | O_NONBLOCK);
478 printed = 0; /* bytes of printer output already echoed to stdout */
479 long pid = (long)getpid();
480 printf("interactive: pid=%ld SWITCH1=%d SWITCH2=%d\n", pid, ge.JS1, ge.JS2);
481 printf(" kill -USR1 %ld # toggle SWITCH 1 (JS1)\n", pid);
482 printf(" kill -USR2 %ld # toggle SWITCH 2 (JS2)\n", pid);
483 printf(" type to feed the operator keyboard; printer output appears as 'PRN> ...'\n");
484 fflush(stdout);
485 uint8_t last_step = ge.mem[0x0010];
486 int was_halted = -1;
487 for (;;) {
488 /* Drain newly-printed characters to the terminal. */
489 int olen = printer_output_len(&ge);
490 if (olen > printed) {
491 const char *o = printer_output(&ge);
492 printf("PRN> %.*s", olen - printed, o + printed);
493 printed = olen;
494 fflush(stdout);
495 }
496 /* Feed any typed bytes to the operator keyboard queue. */
497 {
498 unsigned char kb[64];
499 ssize_t r = read(0, kb, sizeof kb);
500 for (ssize_t k = 0; k < r; k++)
501 printer_feed_key(&ge, kb[k]);
502 }
503 if (g_toggle_js1) {
504 g_toggle_js1 = 0; ge.JS1 = !ge.JS1;
505 printf("[cyc %ld] SWITCH 1 -> %d PO=%04x step=0x%02x%s\n",
506 cycles, ge.JS1, ge.rPO, ge.mem[0x0010],
507 ge.halted ? " (halted)" : "");
508 fflush(stdout);
509 }
510 if (g_toggle_js2) {
511 g_toggle_js2 = 0; ge.JS2 = !ge.JS2;
512 printf("[cyc %ld] SWITCH 2 -> %d PO=%04x step=0x%02x%s\n",
513 cycles, ge.JS2, ge.rPO, ge.mem[0x0010],
514 ge.halted ? " (halted)" : "");
515 fflush(stdout);
516 }
517 if (ge.halted) {
518 if (was_halted != 1) {
519 printf("[cyc %ld] HALT PO=%04x step=0x%02x\n",
520 cycles, ge.rPO, ge.mem[0x0010]);
521 fflush(stdout);
522 was_halted = 1;
523 }
524 usleep(5000); /* frozen; still responsive to signals */
525 continue;
526 }
527 was_halted = 0;
528 ret = ge_run_cycle(&ge);
529 cycles++;
530 if (ret != 0)
531 break;
532 uint8_t st = ge.mem[0x0010];
533 if (st != last_step) {
534 printf("[cyc %ld] step -> 0x%02x PO=%04x\n", cycles, st, ge.rPO);
535 fflush(stdout);
536 last_step = st;
537 }
538 }
539 } else if (use_tui) {
540 /* Interactive session: launch the ncurses client and run the emulator
541 * until the user quits the TUI. Ignore max-cycles, and keep cycling
542 * even after a HLT so the console socket stays serviced and the panel
543 * stays live (a halted GE-120 just spins on HLT;JU self). Throttle when
544 * halted so an idle session doesn't peg a core. */
545 /* The TUI owns the terminal; silence the (all-on by default) log so it
546 * doesn't scribble over the panel — unless the user explicitly asked
547 * for a --trace. */
548 if (!trace_set)
550 pid_t tui_pid = spawn_tui(argv[0]);
551 if (tui_pid < 0) {
552 ge_deinit(&ge);
553 return 1;
554 }
555 while (waitpid(tui_pid, NULL, WNOHANG) == 0) {
556 ret = ge_run_cycle(&ge);
557 cycles++;
558 if (ret != 0)
559 break;
560 if (ge.halted)
561 usleep(2000);
562 }
563 /* The TUI restores the terminal (curses.endwin) on quit; make sure the
564 * child is gone before we print and exit. */
565 kill(tui_pid, SIGTERM);
566 waitpid(tui_pid, NULL, 0);
567 } else {
568 while (!ge.halted && cycles < max_cycles) {
569 if (printer_enabled) {
570 int olen = printer_output_len(&ge);
571 if (olen > printed) {
572 const char *o = printer_output(&ge);
573 fwrite(o + printed, 1, (size_t)(olen - printed), stdout);
574 fflush(stdout);
575 printed = olen;
576 }
577 unsigned char kb[64];
578 ssize_t r = read(0, kb, sizeof kb);
579 for (ssize_t k = 0; k < r; k++)
580 printer_feed_key(&ge, kb[k]);
581 }
582 ret = ge_run_cycle(&ge);
583 cycles++;
584 if (ret != 0)
585 break;
586 }
587 }
588
589 printf("exit: halted=%d cycles=%ld max=%ld error=%d state=%02x PO=%04x\n",
590 ge.halted, cycles, max_cycles, ret, ge.rSO, ge.rPO);
591
592 ge_deinit(&ge);
593 return ret;
594}
const char * binimage_strerror(int code)
Definition binimage.c:79
int binimage_read(FILE *fp, uint16_t *origin, uint16_t *entry, uint8_t *buf, size_t bufcap, uint16_t *len)
Definition binimage.c:41
@ BINIMAGE_OK
Definition binimage.h:47
int cap_load_scattered(const char *path, int mode, unsigned char *image, unsigned *lo, unsigned *hi)
Definition cap.c:442
int cardreader_register(struct ge *ge, const char *cap_path, enum transcode_mode mode)
Definition cardreader.c:442
int console_socket_register(struct ge *ge)
int disk_register(struct ge *ge, const char *image_path, uint8_t connector, uint8_t unit)
Definition disk.c:105
int ge_deinit(struct ge *ge)
Deinitialize the emulator.
Definition ge.c:360
void ge_seed_segment_bases(struct ge *ge)
Seed the eight change/segment-base registers (mem[240+2N]) to identity bases N<<12.
Definition ge.c:83
void ge_load_1(struct ge *ge)
Emulate the press of the "load 1" button in the console.
Definition ge.c:172
void ge_clear(struct ge *ge)
Emulate the press of the "clear" button in the console.
Definition ge.c:27
int ge_run_cycle(struct ge *ge)
Run all GE "mastri" clock periods until next clock cycle.
Definition ge.c:349
void ge_enter(struct ge *ge, uint16_t entry)
Enter execution at entry: seed PO and drop into the alpha (fetch) phase, bypassing the peripheral LOA...
Definition ge.c:155
void ge_init(struct ge *ge)
Initialize the emulator.
Definition ge.c:14
void ge_load(struct ge *ge)
Emulate the press of the "load" button in the console.
Definition ge.c:162
int ge_load_image(struct ge *ge, const uint8_t *image, size_t size, uint16_t origin)
Load a flat image at origin (unified-format payload); origin-aware, not size-capped,...
Definition ge.c:131
void ge_start(struct ge *ge)
Emulate the press of the "start" button in the console.
Definition ge.c:194
#define MEM_SIZE
Definition ge.h:12
void ge_log(ge_log_type type, const char *format,...)
Log message.
Definition log.c:122
@ LOG_DEBUG
General detailed debug information.
Definition log.h:19
int main(int argc, char *argv[])
Definition main.c:111
static void on_sigusr2(int sig)
Definition main.c:48
void ge_log_set_active_types_from_spec(const char *spec)
Set active log types from a comma-separated name specification.
Definition log.c:47
static void print_usage(const char *argv0)
Definition main.c:81
static void on_sigusr1(int sig)
Definition main.c:47
static volatile sig_atomic_t g_toggle_js1
Definition main.c:45
static pid_t spawn_tui(const char *argv0)
Definition main.c:50
static volatile sig_atomic_t g_toggle_js2
Definition main.c:46
int printer_output_len(struct ge *ge)
Definition printer.c:393
int printer_register(struct ge *ge)
Definition printer.c:349
const char * printer_output(struct ge *ge)
Definition printer.c:398
void printer_feed_key(struct ge *ge, uint8_t c)
Definition printer.c:383
const struct sat_batch_info * sat_batch_find(const char *id)
int sat_batch_prepare_image(const char *root, const char *id, unsigned char *image, unsigned *lo, unsigned *hi, uint16_t *entry, char *note, size_t note_sz)
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)
@ SAT_BATCH_IMAGE
Definition sat_batches.h:10
The entire state of the emulated system, including registers, memory, peripherals and timings.
Definition ge.h:96
uint8_t JS2
Console jump condition 2.
Definition ge.h:367
uint16_t rPO
Program addresser.
Definition ge.h:113
uint8_t mem_written[MEM_SIZE]
1 once a location has been written; prevents false MEM CHECK on cleared memory
Definition ge.h:572
uint8_t rSO
Main sequencer.
Definition ge.h:220
uint8_t halted
Definition ge.h:99
uint8_t mem[MEM_SIZE]
The memory of the emulated system.
Definition ge.h:566
uint8_t mem_parity[MEM_SIZE]
Stored odd-parity bit (1 bit per location) written alongside mem[].
Definition ge.h:569
uint8_t JS1
Console jump condition 1.
Definition ge.h:366
const char * summary
Definition sat_batches.h:17
enum sat_batch_launch launch
Definition sat_batches.h:18
const char * title
Definition sat_batches.h:16
const char * id
Definition sat_batches.h:15
int tape_register(struct ge *ge, const char *image_path, uint8_t connector, uint8_t unit)
Definition tape.c:138
@ TC_NORMAL
Definition transcode.h:19
@ TC_COLBIN
Definition transcode.h:22