53 const char *slash = strrchr(argv0,
'/');
56 int dlen = (int)(slash - argv0);
57 snprintf(path,
sizeof(path),
"%.*s/console/curses/console.py", dlen, argv0);
59 snprintf(path,
sizeof(path),
"console/curses/console.py");
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");
74 execlp(
"python3",
"python3", path, (
char *)NULL);
75 perror(
"error: --tui: cannot exec python3");
84 "Usage: %s [OPTIONS] [prog.bin]\n"
86 " prog.bin Unified-format image (gasm output); loaded directly\n"
87 " at its origin and entered at its entry point.\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",
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;
125 const char *cap_path = NULL;
126 const char *image_path = NULL;
128 long load_org = 0x0000;
129 uint16_t poke_addr[32]; uint8_t poke_val[32];
int npoke = 0;
135 for (
int i = 1; i < argc; i++) {
136 if (strcmp(argv[i],
"--help") == 0 || strcmp(argv[i],
"-h") == 0) {
139 }
else if (strcmp(argv[i],
"--console") == 0) {
141 }
else if (strcmp(argv[i],
"--tui") == 0) {
144 }
else if (strcmp(argv[i],
"--deck") == 0) {
146 fprintf(stderr,
"error: --deck requires an argument\n");
149 deck_path = argv[++i];
150 }
else if (strcmp(argv[i],
"--disk") == 0) {
152 fprintf(stderr,
"error: --disk requires an argument\n");
155 disk_path = argv[++i];
156 }
else if (strcmp(argv[i],
"--tape") == 0) {
158 fprintf(stderr,
"error: --tape requires an argument\n");
161 tape_path = argv[++i];
162 }
else if (strcmp(argv[i],
"--sat") == 0) {
164 fprintf(stderr,
"error: --sat requires an argument\n");
167 sat_batch = argv[++i];
168 }
else if (strcmp(argv[i],
"--list-sat") == 0) {
171 printf(
"%-20s %s\n", info->
id, info->
title);
172 printf(
" %s\n", info->
summary);
175 }
else if (strcmp(argv[i],
"--trace") == 0) {
177 fprintf(stderr,
"error: --trace requires an argument\n");
182 }
else if (strcmp(argv[i],
"--max-cycles") == 0) {
184 fprintf(stderr,
"error: --max-cycles requires an argument\n");
187 max_cycles = atol(argv[++i]);
189 if (max_cycles <= 0) {
190 fprintf(stderr,
"error: --max-cycles must be a positive integer\n");
193 }
else if (strcmp(argv[i],
"--interactive") == 0 || strcmp(argv[i],
"-i") == 0) {
195 }
else if (strcmp(argv[i],
"--switch1") == 0) {
197 }
else if (strcmp(argv[i],
"--switch2") == 0) {
199 }
else if (strcmp(argv[i],
"--bin") == 0) {
202 fprintf(stderr,
"error: --bin requires an argument\n");
205 image_path = argv[++i];
206 }
else if (strcmp(argv[i],
"--raw") == 0) {
208 }
else if (strcmp(argv[i],
"--org") == 0) {
210 fprintf(stderr,
"error: --org requires an argument\n");
213 load_org = strtol(argv[++i], NULL, 0);
214 }
else if (strcmp(argv[i],
"--poke") == 0) {
216 fprintf(stderr,
"error: --poke requires ADDR=VAL\n");
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]);
224 poke_addr[npoke] = (uint16_t)strtoul(argv[i + 1], NULL, 0);
225 poke_val[npoke] = (uint8_t)strtoul(eq + 1, NULL, 0);
228 }
else if (argv[i][0] ==
'-') {
229 fprintf(stderr,
"error: unknown option '%s'\n", argv[i]);
232 }
else if (!image_path && !deck_path && !sat_batch) {
240 const char *p = argv[i];
241 size_t n = strlen(p);
242 if (n >= 4 && strcmp(p + n - 4,
".cap") == 0)
247 fprintf(stderr,
"error: unexpected argument '%s'\n", argv[i]);
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");
263 if ((deck_path || sat_batch) && !max_cycles_set)
281 int image_loaded = 0;
282 uint16_t image_entry = 0;
287 fprintf(stderr,
"error: unknown SAT batch '%s' (use --list-sat)\n", sat_batch);
294 unsigned lo = 0, hi = 0;
298 scat, &lo, &hi, &entry,
299 note,
sizeof(note)) != 0) {
300 fprintf(stderr,
"error: failed to prepare SAT batch '%s'\n", sat_batch);
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);
313 fprintf(stderr,
"SAT batch %s: %s\n", sat_batch, note);
315 static const char sat_cap_path[] =
"/tmp/gemu_sat_batch.cap";
317 sat_cap_path, note,
sizeof(note)) != 0) {
318 fprintf(stderr,
"error: failed to compose SAT batch '%s'\n", sat_batch);
326 fprintf(stderr,
"error: failed to load SAT batch '%s'\n", sat_batch);
330 fprintf(stderr,
"SAT batch %s: %s\n", sat_batch, note);
332 }
else if (deck_path) {
337 fprintf(stderr,
"error: failed to load deck '%s'\n", deck_path);
341 }
else if (cap_path) {
347 unsigned lo = 0, hi = 0;
348 memset(scat, 0,
sizeof scat);
352 fprintf(stderr,
"error: failed to scatter-load .cap '%s'\n", cap_path);
357 uint16_t len = (uint16_t)(hi - lo + 1);
359 fprintf(stderr,
"error: scattered image does not fit in memory\n");
365 image_entry = (uint16_t)lo;
366 ge_log(
LOG_DEBUG,
"scatter-loaded %d cards, span 0x%04x-0x%04x, entry 0x%04x\n",
368 }
else if (image_path) {
373 uint16_t origin, entry, len;
374 FILE *f = fopen(image_path,
"rb");
376 fprintf(stderr,
"error: cannot open image '%s'\n", image_path);
381 size_t n = fread(buf, 1,
sizeof buf, f);
382 origin = (uint16_t)load_org;
386 int rc =
binimage_read(f, &origin, &entry, buf,
sizeof buf, &len);
396 fprintf(stderr,
"error: image does not fit in memory\n");
407 (
unsigned)len, origin, entry);
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;
416 ge_log(
LOG_DEBUG,
"poke mem[0x%04x] = 0x%02x\n", poke_addr[p], poke_val[p]);
433 fprintf(stderr,
"warning: failed to attach disk '%s'\n", disk_path);
435 fprintf(stderr,
"disk: attached '%s' on connector 3 unit 0\n", disk_path);
441 fprintf(stderr,
"warning: failed to attach tape '%s'\n", tape_path);
443 fprintf(stderr,
"tape: attached '%s' on connector 4 unit 0\n", tape_path);
446 int printer_enabled = 0;
449 if (image_loaded && !use_tui) {
452 kbd_fl = fcntl(0, F_GETFL, 0);
454 fcntl(0, F_SETFL, kbd_fl | O_NONBLOCK);
471 if (!printer_enabled) {
475 kbd_fl = fcntl(0, F_GETFL, 0);
477 fcntl(0, F_SETFL, kbd_fl | O_NONBLOCK);
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");
485 uint8_t last_step =
ge.
mem[0x0010];
490 if (olen > printed) {
492 printf(
"PRN> %.*s", olen - printed, o + printed);
498 unsigned char kb[64];
499 ssize_t r = read(0, kb,
sizeof kb);
500 for (ssize_t k = 0; k < r; k++)
505 printf(
"[cyc %ld] SWITCH 1 -> %d PO=%04x step=0x%02x%s\n",
512 printf(
"[cyc %ld] SWITCH 2 -> %d PO=%04x step=0x%02x%s\n",
518 if (was_halted != 1) {
519 printf(
"[cyc %ld] HALT PO=%04x step=0x%02x\n",
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);
539 }
else if (use_tui) {
555 while (waitpid(tui_pid, NULL, WNOHANG) == 0) {
565 kill(tui_pid, SIGTERM);
566 waitpid(tui_pid, NULL, 0);
568 while (!
ge.
halted && cycles < max_cycles) {
569 if (printer_enabled) {
571 if (olen > printed) {
573 fwrite(o + printed, 1, (
size_t)(olen - printed), stdout);
577 unsigned char kb[64];
578 ssize_t r = read(0, kb,
sizeof kb);
579 for (ssize_t k = 0; k < r; k++)
589 printf(
"exit: halted=%d cycles=%ld max=%ld error=%d state=%02x PO=%04x\n",
const char * binimage_strerror(int code)
int binimage_read(FILE *fp, uint16_t *origin, uint16_t *entry, uint8_t *buf, size_t bufcap, uint16_t *len)
int cap_load_scattered(const char *path, int mode, unsigned char *image, unsigned *lo, unsigned *hi)
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_seed_segment_bases(struct ge *ge)
Seed the eight change/segment-base registers (mem[240+2N]) to identity bases N<<12.
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_enter(struct ge *ge, uint16_t entry)
Enter execution at entry: seed PO and drop into the alpha (fetch) phase, bypassing the peripheral LOA...
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.
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,...
void ge_start(struct ge *ge)
Emulate the press of the "start" button in the console.
void ge_log(ge_log_type type, const char *format,...)
Log message.
@ LOG_DEBUG
General detailed debug information.
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_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)
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 mem_written[MEM_SIZE]
1 once a location has been written; prevents false MEM CHECK on cleared memory
uint8_t rSO
Main sequencer.
uint8_t mem[MEM_SIZE]
The memory of the emulated system.
uint8_t mem_parity[MEM_SIZE]
Stored odd-parity bit (1 bit per location) written alongside mem[].
uint8_t JS1
Console jump condition 1.
enum sat_batch_launch launch
int tape_register(struct ge *ge, const char *image_path, uint8_t connector, uint8_t unit)