tis256-curses.c (13830B)
1#include <bits/time.h> 2#include <sys/select.h> 3#define NCURSES_WIDECHAR 1 4 5#include "tpu.h" 6#include "util.h" 7#include "asm.h" 8 9#include <ncurses.h> 10#include <sys/inotify.h> 11#include <time.h> 12#include <unistd.h> 13#include <locale.h> 14#include <errno.h> 15#include <stdarg.h> 16#include <string.h> 17#include <stdio.h> 18#include <stdbool.h> 19#include <stdlib.h> 20 21#define KEY_ESC 0x1b 22#define KEY_CTRL(c) ((c) & ~0x60) 23 24#define TPU_INPUT_ROWS 14 25#define TPU_INPUT_COLS 21 26#define TPU_INFO_W 7 27#define TPU_INFO_H 4 28#define TPU_W (1 + TPU_INPUT_COLS + TPU_INFO_W) 29#define TPU_H (1 + TPU_INPUT_ROWS + 1) 30 31enum input_mode { 32 MAIN, 33 TPU_NAV 34}; 35 36enum { 37 NONE, MIN, MAX, MID 38}; 39 40enum { 41 COLOR_WARN = 1 42}; 43 44static const char *mode_repr[] = { 45 "IDL", "RUN", "REA", "WRI" 46}; 47 48static int scrx = 0; 49static int scry = 0; 50static int scrw = 80; 51static int scrh = 40; 52 53static struct tis tis; 54 55static struct timespec show_reloaded = { 0 }; 56static bool showing_reloaded = false; 57 58static enum input_mode input_mode = MAIN; 59 60static struct tpu *tpu_sel = NULL; 61 62int (*cleanup)(void) = endwin; 63const char *progname = "tis256-curses"; 64 65static int64_t 66ms_since(struct timespec *ts) 67{ 68 struct timespec now; 69 70 if (clock_gettime(CLOCK_REALTIME, &now) < 0) 71 die("clock_gettime:"); 72 73 return (now.tv_sec - ts->tv_sec) * 1000 74 + (now.tv_nsec - ts->tv_nsec) / 1000000; 75} 76 77static enum tpu_port_dir 78key_to_dir(int key) 79{ 80 switch (key) { 81 case KEY_LEFT: 82 case 'A': 83 return DIR_LEFT; 84 case KEY_RIGHT: 85 case 'D': 86 return DIR_RIGHT; 87 case KEY_UP: 88 case 'W': 89 return DIR_UP; 90 case KEY_DOWN: 91 case 'S': 92 return DIR_DOWN; 93 default: 94 abort(); 95 } 96} 97 98static const cchar_t * 99dir_to_arrow(enum tpu_port_dir dir) 100{ 101 switch (dir) { 102 case DIR_UP: 103 return WACS_UARROW; 104 case DIR_DOWN: 105 return WACS_DARROW; 106 case DIR_LEFT: 107 return WACS_LARROW; 108 case DIR_RIGHT: 109 return WACS_RARROW; 110 } 111} 112 113static int 114tpu_pos_x(struct tpu *tpu) 115{ 116 return 2 + (int) tpu->x * (TPU_W + 4); 117} 118 119static int 120tpu_pos_y(struct tpu *tpu) 121{ 122 return 2 + (int) tpu->y * (TPU_H + 2); 123} 124 125static void 126tui_draw_box(int sx, int sy, int w, int h, attr_t attr, 127 const cchar_t *ul, const cchar_t *ur, 128 const cchar_t *ll, const cchar_t *lr) 129{ 130 int x, y; 131 132 if (sx + w < scrx || sx >= scrx + scrw) return; 133 if (sy + h < scry || sy >= scry + scrh) return; 134 135 sx -= scrx; 136 sy -= scry; 137 138 attron(attr); 139 mvadd_wch(sy, sx, ul); 140 mvadd_wch(sy, sx + w - 1, ur); 141 mvadd_wch(sy + h - 1, sx, ll); 142 mvadd_wch(sy + h - 1, sx + w - 1, lr); 143 for (x = sx + 1; x < sx + w - 1; x++) 144 mvadd_wch(sy, x, WACS_D_HLINE); 145 for (x = sx + 1; x < sx + w - 1; x++) 146 mvadd_wch(sy + h - 1, x, WACS_D_HLINE); 147 for (y = sy + 1; y < sy + h - 1; y++) 148 mvadd_wch(y, sx, WACS_D_VLINE); 149 for (y = sy + 1; y < sy + h - 1; y++) 150 mvadd_wch(y, sx + w - 1, WACS_D_VLINE); 151 attroff(attr); 152} 153 154static void 155__attribute__((format(printf, 4, 5))) 156tui_draw_text(int x, int y, attr_t attr, const char *fmt, ...) 157{ 158 char buf[512]; 159 va_list ap; 160 int i; 161 162 va_start(ap, fmt); 163 vsnprintf(buf, 512, fmt, ap); 164 va_end(ap); 165 166 attron(attr); 167 for (i = 0; i < strlen(buf) && x + i < scrx + scrw; i++) 168 mvaddch(y - scry, x + i - scrx, (chtype) buf[i]); 169 attroff(attr); 170} 171 172static void 173tui_draw_wch(int x, int y, attr_t attr, const cchar_t *c) 174{ 175 attron(attr); 176 mvadd_wch(y - scry, x - scrx, c); 177 attroff(attr); 178} 179 180static void 181tui_draw_tpu(struct tpu *tpu) 182{ 183 char linebuf[TPU_INPUT_COLS + 1]; 184 struct tpu_port *port; 185 int sx, sy, x, y, w, h; 186 int off, start, inst; 187 size_t len; 188 attr_t attr; 189 int idle; 190 191 attr = (tpu_sel == tpu && input_mode == TPU_NAV) ? A_BOLD : 0; 192 193 sx = tpu_pos_x(tpu); 194 sy = tpu_pos_y(tpu); 195 tui_draw_box(sx, sy, TPU_W, TPU_H, attr, 196 WACS_D_ULCORNER, WACS_D_URCORNER, 197 WACS_D_LLCORNER, WACS_D_LRCORNER); 198 199 if (tpu->inst_cnt > 0) { 200 start = MAX(0, (int) tpu->pc - 6); 201 for (off = 0, inst = start; inst < TPU_MAX_INST; inst++) { 202 if (tpu->labels[inst]) { 203 len = strlen(tpu->labels[inst]); 204 if (len > TPU_INPUT_COLS - 1) { 205 tui_draw_text(sx + 1, sy + 1 + off, A_DIM, 206 "%.*s..:", TPU_INPUT_COLS - 3, 207 tpu->labels[inst]); 208 } else { 209 tui_draw_text(sx + 1, sy + 1 + off, A_DIM, 210 "%s:", tpu->labels[inst]); 211 } 212 if (++off >= TPU_INPUT_ROWS) break; 213 } 214 if (inst < tpu->inst_cnt) { 215 asm_print_inst(linebuf, sizeof(linebuf), 216 &tpu->insts[inst]); 217 tui_draw_text(sx + 1, sy + 1 + off, 218 inst == tpu->pc ? A_STANDOUT : 0, "%-*.*s", 219 TPU_INPUT_COLS, TPU_INPUT_COLS, linebuf); 220 if (++off >= TPU_INPUT_ROWS) break; 221 } 222 } 223 } 224 225 x = sx + TPU_W - TPU_INFO_W; 226 y = sy; 227 w = TPU_INFO_W; 228 h = TPU_INFO_H; 229 tui_draw_box(x, y, w, h, attr, 230 WACS_D_TTEE, WACS_D_URCORNER, WACS_D_LTEE, WACS_D_RTEE); 231 tui_draw_text(x + 2, y + 1, A_BOLD, "ACC"); 232 tui_draw_text(x + 2, y + 2, 0, "%03i", tpu->acc); 233 234 tui_draw_box(x, (y += TPU_INFO_H - 1), w, h, attr, 235 WACS_D_LTEE, WACS_D_RTEE, WACS_D_LTEE, WACS_D_RTEE); 236 tui_draw_text(x + 2, y + 1, A_BOLD, "BAK"); 237 tui_draw_text(x + 2, y + 2, 0, "%03i", tpu->bak); 238 239 tui_draw_box(x, (y += TPU_INFO_H - 1), w, h, attr, 240 WACS_D_LTEE, WACS_D_RTEE, WACS_D_LTEE, WACS_D_RTEE); 241 tui_draw_text(x + 2, y + 1, A_BOLD, "LST"); 242 if (tpu->last < 0) { 243 tui_draw_text(x + 2, y + 2, 0, "N/A"); 244 } else { 245 tui_draw_wch(x + 2, y + 2, 0, 246 dir_to_arrow((enum tpu_port_dir) tpu->last)); 247 tui_draw_wch(x + 3, y + 2, 0, 248 dir_to_arrow((enum tpu_port_dir) tpu->last)); 249 tui_draw_wch(x + 4, y + 2, 0, 250 dir_to_arrow((enum tpu_port_dir) tpu->last)); 251 } 252 253 tui_draw_box(x, (y += TPU_INFO_H - 1), w, h, attr, 254 WACS_D_LTEE, WACS_D_RTEE, WACS_D_LTEE, WACS_D_RTEE); 255 tui_draw_text(x + 2, y + 1, A_BOLD, "MOD"); 256 tui_draw_text(x + 2, y + 2, 0, "%s", mode_repr[tpu->status]); 257 258 tui_draw_box(x, (y += TPU_INFO_H - 1), w, h, attr, 259 WACS_D_LTEE, WACS_D_RTEE, WACS_D_BTEE, WACS_D_LRCORNER); 260 tui_draw_text(x + 2, y + 1, A_BOLD, "IDL"); 261 if (tpu->steps > 0) 262 idle = (int) ((double) tpu->idle_steps * 100 / (double) tpu->steps); 263 else 264 idle = 100; 265 tui_draw_text(x + 2, y + 2, 0, "%03i", idle); 266 267 port = &tpu->ports[DIR_LEFT]; 268 if (!port->dst_port || !(port->dst_port->type & PORT_OUT)) 269 attron(COLOR_PAIR(COLOR_WARN)); 270 if (port->in >= 0) 271 tui_draw_text(sx - 3, sy + 6, A_BOLD, "%03i", port->in); 272 if (port->type & PORT_IN) 273 tui_draw_wch(sx - 1, sy + 7, 274 port->in >= 0 ? A_BOLD : 0, WACS_RARROW); 275 attroff(COLOR_PAIR(COLOR_WARN)); 276 if (!port->dst_port || !(port->dst_port->type & PORT_IN)) 277 attron(COLOR_PAIR(COLOR_WARN)); 278 if (port->type & PORT_OUT) 279 tui_draw_wch(sx - 1, sy + 8, 280 port->out_pending >= 0 ? A_BOLD : 0, WACS_LARROW); 281 if (port->out_pending >= 0) 282 tui_draw_text(sx - 3, sy + 10, A_BOLD, "%03i", port->out_pending); 283 attroff(COLOR_PAIR(COLOR_WARN)); 284 285 port = &tpu->ports[DIR_RIGHT]; 286 if (!port->dst_port || !(port->dst_port->type & PORT_IN)) 287 attron(COLOR_PAIR(COLOR_WARN)); 288 if (port->out_pending >= 0) 289 tui_draw_text(sx + TPU_W, sy + 5, A_BOLD, "%03i", port->out_pending); 290 if (port->type & PORT_OUT) 291 tui_draw_wch(sx + TPU_W, sy + 7, 292 port->out_pending >= 0 ? A_BOLD : 0, WACS_RARROW); 293 attroff(COLOR_PAIR(COLOR_WARN)); 294 if (!port->dst_port || !(port->dst_port->type & PORT_OUT)) 295 attron(COLOR_PAIR(COLOR_WARN)); 296 if (port->type & PORT_IN) 297 tui_draw_wch(sx + TPU_W, sy + 8, 298 port->in >= 0 ? A_BOLD : 0, WACS_LARROW); 299 if (port->in >= 0) 300 tui_draw_text(sx + TPU_W, sy + 9, A_BOLD, "%03i", port->in); 301 attroff(COLOR_PAIR(COLOR_WARN)); 302 303 port = &tpu->ports[DIR_UP]; 304 if (!port->dst_port || !(port->dst_port->type & PORT_IN)) 305 attron(COLOR_PAIR(COLOR_WARN)); 306 if (port->out_pending >= 0) 307 tui_draw_text(sx + 9, sy - 1, A_BOLD, "%03i", port->out_pending); 308 if (port->type & PORT_OUT) 309 tui_draw_wch(sx + 13, sy - 1, 310 port->out_pending >= 0 ? A_BOLD : 0, WACS_UARROW); 311 attroff(COLOR_PAIR(COLOR_WARN)); 312 if (!port->dst_port || !(port->dst_port->type & PORT_OUT)) 313 attron(COLOR_PAIR(COLOR_WARN)); 314 if (port->type & PORT_IN) 315 tui_draw_wch(sx + 15, sy - 1, 316 port->in >= 0 ? A_BOLD : 0, WACS_DARROW); 317 if (port->in >= 0) 318 tui_draw_text(sx + 17, sy - 1, A_BOLD, "%03i", port->in); 319 attroff(COLOR_PAIR(COLOR_WARN)); 320 321 port = &tpu->ports[DIR_DOWN]; 322 if (!port->dst_port || !(port->dst_port->type & PORT_OUT)) 323 attron(COLOR_PAIR(COLOR_WARN)); 324 if (port->in >= 0) 325 tui_draw_text(sx + 9, sy + TPU_H, A_BOLD, "%03i", port->in); 326 if (port->type & PORT_IN) 327 tui_draw_wch(sx + 13, sy + TPU_H, 328 port->in >= 0 ? A_BOLD : 0, WACS_UARROW); 329 attroff(COLOR_PAIR(COLOR_WARN)); 330 if (!port->dst_port || !(port->dst_port->type & PORT_IN)) 331 attron(COLOR_PAIR(COLOR_WARN)); 332 if (port->type & PORT_OUT) 333 tui_draw_wch(sx + 15, sy + TPU_H, 334 port->out_pending >= 0 ? A_BOLD : 0, WACS_DARROW); 335 if (port->out_pending >= 0) 336 tui_draw_text(sx + 17, sy + TPU_H, A_BOLD, "%03i", port->out_pending); 337 attroff(COLOR_PAIR(COLOR_WARN)); 338} 339 340static void 341tui_draw(void) 342{ 343 struct tpu *tpu; 344 size_t i; 345 int tx; 346 347 clear(); 348 for (tpu = tis.tpu_vec.tpus, i = 0; i < tis.tpu_vec.cnt; i++, tpu++) 349 tui_draw_tpu(tpu); 350 if (showing_reloaded) { 351 tx = scrx + scrw / 2 - 4; 352 tui_draw_text(scrx, scry, A_STANDOUT, "%*s", scrw, ""); 353 tui_draw_text(tx, scry, A_STANDOUT, "RELOADED"); 354 } 355 refresh(); 356} 357 358static void 359tui_resize(void) 360{ 361 scrw = getmaxx(stdscr); 362 scrh = getmaxy(stdscr); 363} 364 365static void 366tpu_seek_running(void) 367{ 368 struct tpu *tpu; 369 size_t i; 370 371 for (tpu = tis.tpu_vec.tpus, i = 0; i < tis.tpu_vec.cnt; i++, tpu++) { 372 if (tpu->status == STATUS_RUN) tpu_sel = tpu; 373 } 374} 375 376static void 377tui_seek(struct tpu *tpu, int dx, int dy) 378{ 379 int minx, miny, maxx, maxy; 380 int x, y; 381 size_t i; 382 383 if (tpu) { 384 minx = maxx = tpu_pos_x(tpu); 385 miny = maxy = tpu_pos_y(tpu); 386 } else { 387 minx = miny = maxx = maxy = -1; 388 for (tpu = tis.tpu_vec.tpus, i = 0; i < tis.tpu_vec.cnt; i++, tpu++) { 389 x = tpu_pos_x(tpu); 390 if (minx == -1 || x < minx) minx = x; 391 if (maxx == -1 || x > maxx) maxx = x; 392 y = tpu_pos_y(tpu); 393 if (miny == -1 || y < miny) miny = y; 394 if (maxy == -1 || y > maxy) maxy = y; 395 } 396 if (minx == -1 || miny == -1) return; 397 } 398 399 if (dx == MIN) scrx = minx - 2; 400 else if (dx == MAX) scrx = maxx + TPU_W + 2 - scrw; 401 else if (dx == MID) scrx = (minx + maxx + TPU_W - scrw) / 2; 402 403 if (dy == MIN) scry = miny - 2; 404 else if (dy == MAX) scry = maxy + TPU_H + 2 - scrh; 405 else if (dy == MID) scry = (miny + maxy + TPU_H - scrh) / 2; 406} 407 408static void 409handlekey(int key) 410{ 411 enum tpu_port_dir dir; 412 413 if (input_mode == MAIN) { 414 switch (key) { 415 case 'I': 416 input_mode = TPU_NAV; 417 break; 418 case KEY_UP: 419 case 'W': 420 scry -= 2; 421 break; 422 case KEY_DOWN: 423 case 'S': 424 scry += 2; 425 break; 426 case KEY_LEFT: 427 case 'A': 428 scrx -= 4; 429 break; 430 case KEY_RIGHT: 431 case 'D': 432 scrx += 4; 433 break; 434 } 435 } else { 436 switch (key) { 437 case KEY_ESC: 438 input_mode = MAIN; 439 break; 440 case KEY_UP: 441 case 'W': 442 case KEY_DOWN: 443 case 'A': 444 case KEY_LEFT: 445 case 'S': 446 case KEY_RIGHT: 447 case 'D': 448 dir = key_to_dir(key); 449 if (tpu_sel && tpu_sel->ports[dir].dst_tpu) 450 tpu_sel = tpu_sel->ports[dir].dst_tpu; 451 tui_seek(tpu_sel, MID, MID); 452 break; 453 case 'c': 454 if (!tpu_sel) return; 455 do { 456 tis_step(&tis); 457 } while (tpu_sel->status != STATUS_RUN && tis.alive); 458 break; 459 case 'p': 460 tpu_seek_running(); 461 break; 462 } 463 } 464} 465 466static void 467reset(int ifd, int argc, char **argv, bool watch) 468{ 469 FILE *tis_stdin, *tis_stdout; 470 471 tis_stdin = NULL; 472 if (argc >= 3) { 473 tis_stdin = fopen(argv[2], "r"); 474 if (!tis_stdin) die("fopen '%s':", argv[2]); 475 } 476 477 tis_stdout = NULL; 478 if (argc >= 4) { 479 tis_stdout = fopen(argv[3], "w+"); 480 if (!tis_stdout) die("fopen '%s':", argv[3]); 481 } 482 483 tis_load(&tis, argv[1], tis_stdin, tis_stdout); 484 485 if (watch) 486 if (inotify_add_watch(ifd, argv[1], IN_CLOSE_WRITE) < 0) 487 die("inotify_add_watch '%s':", argv[1]); 488 489 if (tis.stdin_port.attached) { 490 tpu_sel = tis.stdin_port.dst_tpu; 491 } else { 492 tpu_sel = tis.tpu_vec.cnt ? &tis.tpu_vec.tpus[0] : NULL; 493 } 494} 495 496int 497main(int argc, char **argv) 498{ 499 struct timeval timeout; 500 struct inotify_event event; 501 ssize_t len; 502 fd_set fds; 503 bool quit; 504 int key; 505 int ifd; 506 int rc; 507 508 if (argc < 2 || argc > 4) { 509 fprintf(stderr, "Usage: tis256-curses FILE [STDIN] [STDOUT]\n"); 510 exit(1); 511 } 512 513 setlocale(LC_ALL, ""); 514 515 initscr(); 516 raw(); 517 noecho(); 518 keypad(stdscr, TRUE); 519 start_color(); 520 curs_set(0); 521 tui_resize(); 522 ESCDELAY = 0; 523 524 init_pair(COLOR_WARN, COLOR_RED, COLOR_BLACK); 525 526 tis_init(&tis, NULL, NULL); 527 528 ifd = inotify_init1(IN_NONBLOCK); 529 530 reset(ifd, argc, argv, true); 531 532 tui_seek(NULL, MID, MID); 533 534 quit = false; 535 while (!quit) { 536 tui_draw(); 537 538 FD_ZERO(&fds); 539 FD_SET(ifd, &fds); 540 FD_SET(0, &fds); 541 timeout.tv_sec = 0; 542 timeout.tv_usec = 500000; 543 rc = select(ifd+1, &fds, NULL, NULL, 544 showing_reloaded ? &timeout : NULL); 545 if (rc < 0 && errno == EINTR) continue; 546 if (rc < 0) die("select:"); 547 548 if (FD_ISSET(ifd, &fds)) { 549 len = read(ifd, &event, sizeof(event)); 550 if (len < 0 && errno != EAGAIN) 551 die("inotify_read:"); 552 if (len >= 0) 553 reset(ifd, argc, argv, true); 554 if (clock_gettime(CLOCK_REALTIME, &show_reloaded) < 0) 555 die("clock_gettime:"); 556 showing_reloaded = true; 557 } 558 559 if (!showing_reloaded || ms_since(&show_reloaded) < 500) { 560 if (!FD_ISSET(ifd, &fds) && !FD_ISSET(0, &fds)) 561 continue; 562 } else { 563 showing_reloaded = false; 564 } 565 566 if (!FD_ISSET(0, &fds)) continue; 567 key = getch(); 568 switch (key) { 569 case KEY_RESIZE: 570 tui_resize(); 571 break; 572 case 'g': 573 tui_seek(tpu_sel, MID, MID); 574 break; 575 case 'h': 576 tui_seek(NULL, MID, MID); 577 break; 578 case 'i': 579 if (tis.stdin_port.dst_tpu) { 580 tpu_sel = tis.stdin_port.dst_tpu; 581 tui_seek(tis.stdin_port.dst_tpu, MID, MID); 582 } 583 break; 584 case 'o': 585 if (tis.stdout_port.dst_tpu) { 586 tpu_sel = tis.stdout_port.dst_tpu; 587 tui_seek(tis.stdout_port.dst_tpu, MID, MID); 588 } 589 break; 590 case 'r': 591 reset(ifd, argc, argv, false); 592 break; 593 case 's': 594 tis_step(&tis); 595 break; 596 case KEY_CTRL('c'): 597 quit = true; 598 break; 599 default: 600 handlekey(key); 601 break; 602 } 603 } 604 605 tis_deinit(&tis); 606 607 close(ifd); 608 609 endwin(); 610}