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