#define NCURSES_WIDECHAR 1 #include "tpu.h" #include "util.h" #include "asm.h" #include #include #include #include #include #include #include #include #include #include #include #define KEY_ESC 0x1b #define KEY_CTRL(c) ((c) & ~0x60) #define TPU_INFO_W 7 #define TPU_INFO_H 4 #define TPU_W (1 + TPU_MAX_COLS + TPU_INFO_W) #define TPU_H (1 + TPU_MAX_ROWS + 1) #define TIMEOUT 50 enum input_mode { MAIN, TPU_NAV }; enum { NONE, MIN, MAX, MID }; enum { THEME_DEFAULT, THEME_DISABLED }; static const char *mode_repr[] = { "IDL", "RUN", "REA", "WRI" }; static int scrx = 0; static int scry = 0; static int scrw = 80; static int scrh = 40; static struct tis tis; static int show_reloaded = 0; static enum input_mode input_mode = MAIN; static struct tpu *tpu_sel = NULL; int (*cleanup)(void) = endwin; const char *progname = "tis100-curses"; static enum tpu_port_dir key_to_dir(int key) { switch (key) { case KEY_LEFT: return DIR_LEFT; case KEY_RIGHT: return DIR_RIGHT; case KEY_UP: return DIR_UP; case KEY_DOWN: return DIR_DOWN; default: abort(); } } static const cchar_t * dir_to_arrow(enum tpu_port_dir dir) { switch (dir) { case DIR_UP: return WACS_UARROW; case DIR_DOWN: return WACS_DARROW; case DIR_LEFT: return WACS_LARROW; case DIR_RIGHT: return WACS_RARROW; } } static int tpu_pos_x(struct tpu *tpu) { return 2 + (int) tpu->x * (TPU_W + 4); } static int tpu_pos_y(struct tpu *tpu) { return 2 + (int) tpu->y * (TPU_H + 2); } static void tui_draw_box(int sx, int sy, int w, int h, attr_t attr, const cchar_t *ul, const cchar_t *ur, const cchar_t *ll, const cchar_t *lr) { int x, y; if (sx + w < scrx || sx >= scrx + scrw) return; if (sy + h < scry || sy >= scry + scrh) return; sx -= scrx; sy -= scry; attron(attr); mvadd_wch(sy, sx, ul); mvadd_wch(sy, sx + w - 1, ur); mvadd_wch(sy + h - 1, sx, ll); mvadd_wch(sy + h - 1, sx + w - 1, lr); for (x = sx + 1; x < sx + w - 1; x++) mvadd_wch(sy, x, WACS_D_HLINE); for (x = sx + 1; x < sx + w - 1; x++) mvadd_wch(sy + h - 1, x, WACS_D_HLINE); for (y = sy + 1; y < sy + h - 1; y++) mvadd_wch(y, sx, WACS_D_VLINE); for (y = sy + 1; y < sy + h - 1; y++) mvadd_wch(y, sx + w - 1, WACS_D_VLINE); attroff(attr); } static void __attribute__((format(printf, 4, 5))) tui_draw_text(int x, int y, attr_t attr, const char *fmt, ...) { char buf[256]; va_list ap; int i; va_start(ap, fmt); vsnprintf(buf, 256, fmt, ap); va_end(ap); attron(attr); for (i = 0; i < strlen(buf) && x + i < scrx + scrw; i++) mvaddch(y - scry, x + i - scrx, (chtype) buf[i]); attroff(attr); } static void tui_draw_lit(int x, int y, attr_t attr, int lit) { char buf[6]; snprintf(buf, 5, "% 04i", lit); tui_draw_text(x, y, attr, "%.*s", 4, buf); } static void tui_draw_wch(int x, int y, attr_t attr, const cchar_t *c) { attron(attr); mvadd_wch(y - scry, x - scrx, c); attroff(attr); } static void tui_draw_tpu(struct tpu *tpu) { char rowbuf[TPU_MAX_COLS + 1]; struct tpu_port *port; int sx, sy, x, y, w, h; int offx, offy, start, inst; struct label *label; size_t len; attr_t attr; int idle; attr = (tpu_sel == tpu && input_mode == TPU_NAV) ? A_BOLD : 0; attr |= tpu->disabled ? COLOR_PAIR(THEME_DISABLED) : COLOR_PAIR(THEME_DEFAULT); sx = tpu_pos_x(tpu); sy = tpu_pos_y(tpu); tui_draw_box(sx, sy, TPU_W, TPU_H, attr, WACS_D_ULCORNER, WACS_D_URCORNER, WACS_D_LLCORNER, WACS_D_LRCORNER); if (tpu->disabled) { tui_draw_text(sx + 4, sy + 1 + 1, attr | A_REVERSE, " "); tui_draw_text(sx + 4, sy + 1 + 3, attr | A_BOLD, "COMMUNICATION"); tui_draw_text(sx + 4, sy + 1 + 4, attr | A_BOLD, " FAILURE "); tui_draw_text(sx + 4, sy + 1 + 6, attr | A_REVERSE, " "); } else if (tpu->inst_cnt > 0) { start = MAX(0, MIN(tpu->pc - 4, (int) tpu->inst_cnt - TPU_MAX_ROWS)); inst = start; for (offy = 0; offy < TPU_MAX_ROWS && inst < tpu->inst_cnt; ) { asm_print_inst(rowbuf, sizeof(rowbuf), &tpu->insts[inst], TPU_MAX_COLS - (size_t) offx); label = &tpu->label_map.labels[inst]; if (label->str) { len = strlen(label->str); tui_draw_text(sx + 1 + offx, sy + 1 + offy, A_DIM, "%s:", label->str); if (label->prefix) { offx += (int) strlen(label->str) + 1; if (offx + 1 + (int) strlen(rowbuf) <= TPU_MAX_COLS) offx += 1; } else { offy += 1; } } if ((size_t) offx + strlen(rowbuf) > TPU_MAX_COLS) asm_print_inst(rowbuf, sizeof(rowbuf), &tpu->insts[inst], TPU_MAX_COLS - (size_t) offx); tui_draw_text(sx + 1 + offx, sy + 1 + offy, inst == tpu->pc ? A_STANDOUT | (tpu->idle ? A_DIM : 0) : 0, "%-*.*s", TPU_MAX_COLS - offx, TPU_MAX_COLS, rowbuf); inst += 1; offy += 1; offx = 0; } } x = sx + TPU_W - TPU_INFO_W; y = sy; w = TPU_INFO_W; h = TPU_INFO_H; tui_draw_box(x, y, w, h, attr, WACS_D_TTEE, WACS_D_URCORNER, WACS_D_LTEE, WACS_D_RTEE); tui_draw_text(x + 2, y + 1, attr | A_BOLD, "ACC"); tui_draw_lit(x + 1, y + 2, attr, tpu->acc); tui_draw_box(x, (y += TPU_INFO_H - 1), w, h, attr, WACS_D_LTEE, WACS_D_RTEE, WACS_D_LTEE, WACS_D_RTEE); tui_draw_text(x + 2, y + 1, attr | A_BOLD, "BAK"); tui_draw_lit(x + 1, y + 2, attr, tpu->bak); tui_draw_box(x, (y += TPU_INFO_H - 1), w, h, attr, WACS_D_LTEE, WACS_D_RTEE, WACS_D_LTEE, WACS_D_RTEE); tui_draw_text(x + 2, y + 1, attr | A_BOLD, "LST"); if (tpu->last < 0) { tui_draw_text(x + 2, y + 2, attr, "N/A"); } else { tui_draw_wch(x + 2, y + 2, attr, dir_to_arrow((enum tpu_port_dir) tpu->last)); tui_draw_wch(x + 3, y + 2, attr, dir_to_arrow((enum tpu_port_dir) tpu->last)); tui_draw_wch(x + 4, y + 2, attr, dir_to_arrow((enum tpu_port_dir) tpu->last)); } tui_draw_box(x, (y += TPU_INFO_H - 1), w, h, attr, WACS_D_LTEE, WACS_D_RTEE, WACS_D_LTEE, WACS_D_RTEE); tui_draw_text(x + 2, y + 1, attr | A_BOLD, "MOD"); tui_draw_text(x + 2, y + 2, attr, "%s", mode_repr[tpu->mode]); tui_draw_box(x, (y += TPU_INFO_H - 1), w, h, attr, WACS_D_LTEE, WACS_D_RTEE, WACS_D_BTEE, WACS_D_RTEE); tui_draw_text(x + 2, y + 1, attr | A_BOLD, "IDL"); if (tpu->steps > 0) idle = (int) ((double) tpu->idle_steps * 100 / (double) tpu->steps); else idle = 0; tui_draw_text(x + 2, y + 2, attr, "%03i", idle); tui_draw_box(x, (y += TPU_INFO_H - 1), w, 2, attr, WACS_D_LTEE, WACS_D_RTEE, WACS_D_BTEE, WACS_D_LRCORNER); if (tpu->ports[DIR_LEFT].attached) { port = &tpu->ports[DIR_LEFT]; if (port->avail) tui_draw_lit(sx - 4, sy + 6, A_BOLD, port->in); if (port->type & PORT_IN) tui_draw_wch(sx - 1, sy + 7, port->avail ? A_BOLD : 0, WACS_RARROW); if (port->type & PORT_OUT) tui_draw_wch(sx - 1, sy + 8, port->writing ? A_BOLD : 0, WACS_LARROW); if (port->writing) tui_draw_lit(sx - 4, sy + 10, A_BOLD, port->out); } if (tpu->ports[DIR_RIGHT].attached) { port = &tpu->ports[DIR_RIGHT]; if (port->writing) tui_draw_lit(sx + TPU_W, sy + 5, A_BOLD, port->out); if (port->type & PORT_OUT) tui_draw_wch(sx + TPU_W, sy + 7, port->writing ? A_BOLD : 0, WACS_RARROW); if (port->type & PORT_IN) tui_draw_wch(sx + TPU_W, sy + 8, port->avail ? A_BOLD : 0, WACS_LARROW); if (port->avail) tui_draw_lit(sx + TPU_W, sy + 9, A_BOLD, port->in); } if (tpu->ports[DIR_UP].attached) { port = &tpu->ports[DIR_UP]; if (port->dst_port->io) tui_draw_text(sx + 10, sy - 1, A_BOLD, "IN.%c", toupper(port->dst_port->io->c)); if (port->writing) tui_draw_lit(sx + 8, sy - 1, A_BOLD, port->out); if (port->type & PORT_OUT) tui_draw_wch(sx + 13, sy - 1, port->writing ? A_BOLD : 0, WACS_UARROW); if (port->type & PORT_IN) tui_draw_wch(sx + 15, sy - 1, port->avail ? A_BOLD : 0, WACS_DARROW); if (port->avail) tui_draw_lit(sx + 16, sy - 1, A_BOLD, port->in); } if (tpu->ports[DIR_DOWN].attached) { port = &tpu->ports[DIR_DOWN]; if (port->dst_port->io) tui_draw_text(sx + 9, sy + TPU_H, A_BOLD, "OUT.%c", toupper(port->dst_port->io->c)); if (port->avail) tui_draw_lit(sx + 8, sy + TPU_H, A_BOLD, port->in); if (port->type & PORT_IN) tui_draw_wch(sx + 13, sy + TPU_H, port->avail ? A_BOLD : 0, WACS_UARROW); if (port->type & PORT_OUT) tui_draw_wch(sx + 15, sy + TPU_H, port->writing ? A_BOLD : 0, WACS_DARROW); if (port->writing) tui_draw_lit(sx + 16, sy + TPU_H, A_BOLD, port->out); } } static void tui_draw(void) { struct tpu_map_link *link; size_t i; int tx; clear(); for (i = 0; i < TPU_MAP_BUCKETS; i++) { for (link = tis.tpu_map.buckets[i]; link; link = link->next) tui_draw_tpu(link->tpu); } if (show_reloaded > 0) { tx = MAX(0, scrx + scrw / 2 - 4); tui_draw_text(scrx, scry, A_STANDOUT, "%-*s", scrw, ""); tui_draw_text(tx, scry, A_STANDOUT, "RELOADED"); show_reloaded--; } refresh(); } static void tui_resize(void) { scrw = getmaxx(stdscr); scrh = getmaxy(stdscr); } static void tui_seek(struct tpu *tpu, int dx, int dy) { struct tpu_map_link *link; int minx, miny, maxx, maxy; int x, y; size_t i; if (tpu) { minx = maxx = tpu_pos_x(tpu); miny = maxy = tpu_pos_y(tpu); } else { minx = miny = maxx = maxy = -1; for (i = 0; i < TPU_MAP_BUCKETS; i++) { link = tis.tpu_map.buckets[i]; for (; link; link = link->next) { if (tpu && tpu != link->tpu) continue; x = tpu_pos_x(link->tpu); if (minx == -1 || x < minx) minx = x; if (maxx == -1 || x > maxx) maxx = x; y = tpu_pos_y(link->tpu); if (miny == -1 || y < miny) miny = y; if (maxy == -1 || y > maxy) maxy = y; } } if (minx == -1 || miny == -1) return; } if (dx == MIN) scrx = minx - 2; else if (dx == MAX) scrx = maxx + TPU_W + 2 - scrw; else if (dx == MID) scrx = (minx + maxx + TPU_W - scrw) / 2; if (dy == MIN) scry = miny - 2; else if (dy == MAX) scry = maxy + TPU_H + 2 - scrh; else if (dy == MID) scry = (miny + maxy + TPU_H - scrh) / 2; } static void handlekey(int key) { enum tpu_port_dir dir; if (input_mode == MAIN) { switch (key) { case 'I': input_mode = TPU_NAV; break; case KEY_UP: scry -= 2; break; case KEY_DOWN: scry += 2; break; case KEY_LEFT: scrx -= 4; break; case KEY_RIGHT: scrx += 4; break; case 's': tis_step(&tis); break; } } else { switch (key) { case KEY_ESC: input_mode = MAIN; break; case KEY_UP: case KEY_DOWN: case KEY_LEFT: case KEY_RIGHT: dir = key_to_dir(key); if (tpu_sel && tpu_sel->ports[dir].dst_port->tpu) tpu_sel = tpu_sel->ports[dir].dst_port->tpu; tui_seek(tpu_sel, MID, MID); break; } } } static struct tpu * first_tpu(void) { size_t i; for (i = 0; i < TPU_MAP_BUCKETS; i++) { if (tis.tpu_map.buckets[i]) return tis.tpu_map.buckets[i]->tpu; } return NULL; } static void reset(int ifd, int argc, const char **argv, bool watch) { tis_deinit(&tis); tis_init(&tis); tis_load(&tis, argv); if (watch) { if (inotify_add_watch(ifd, tis.asm_filepath, IN_MODIFY) < 0) die("inotify_add_watch '%s':", argv[1]); } tpu_sel = first_tpu(); } int main(int argc, const char **argv) { struct inotify_event event; ssize_t len; bool quit; int key; int ifd; if (argc < 2) { fprintf(stderr, "Usage: tis100-curses FILE [IO..]\n"); exit(1); } setlocale(LC_ALL, ""); initscr(); raw(); noecho(); keypad(stdscr, TRUE); start_color(); init_pair(THEME_DEFAULT, COLOR_WHITE, COLOR_BLACK); init_pair(THEME_DISABLED, COLOR_RED, COLOR_BLACK); curs_set(0); tui_resize(); timeout(TIMEOUT); ESCDELAY = 0; tis_init(&tis); ifd = inotify_init1(IN_NONBLOCK); reset(ifd, argc, argv, true); quit = false; while (!quit) { len = read(ifd, &event, sizeof(event)); if (len < 0 && errno != EAGAIN) die("inotify_read:"); if (len >= 0) { reset(ifd, argc, argv, true); show_reloaded = 1000 / TIMEOUT; } tui_draw(); key = getch(); switch (key) { case KEY_RESIZE: tui_resize(); break; case 'g': tui_seek(tpu_sel, MID, MID); break; case 'h': tui_seek(NULL, MID, MID); break; case 'r': reset(ifd, argc, argv, false); break; case 'q': case KEY_CTRL('c'): quit = true; break; default: handlekey(key); break; } } tis_deinit(&tis); close(ifd); endwin(); }