#define NCURSES_WIDECHAR 1 #include "tpu.h" #include "util.h" #include "asm.h" #include #include #include #include #include #include #include #include #include #define KEY_ESC 0x1b #define KEY_CTRL(c) ((c) & ~0x60) #define TPUT_INPUT_ROWS 14 #define TPUT_INPUT_COLS 20 #define TPUT_INFO_W 6 #define TPUT_INFO_H 4 #define TPUT_CNT 6 #define TPUT_W (TPUT_INPUT_COLS + 2 + 6) #define TPUT_H (TPUT_INPUT_ROWS + 2) enum input_mode { MAIN, TPU_NAV }; enum { NONE, MIN, MAX, MID }; enum { COLOR_HEADING, COLOR_VAL }; static int scrx = 0; static int scry = 0; static int scrw = 80; static int scrh = 40; static struct tis tis; static FILE *tis_stdin = NULL; static FILE *tis_stdout = NULL; static enum input_mode input_mode = MAIN; static struct tpu *tpu_sel = NULL; void (*cleanup)(void) = (void *) endwin; const char *progname = "tis-curses"; static const char * tpu_mode_str(struct tpu *tpu) { switch (tpu->status) { case STATUS_READ: return "=R="; case STATUS_WRITE: return "=W="; case STATUS_IDLE: return "=I="; case STATUS_RUN: return "=X="; } } 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 char * tpu_last_str(struct tpu *tpu) { if (tpu->last < 0) return "N/A"; return dir_reprs[tpu->last]; } static int tpu_pos_x(struct tpu *tpu) { return 2 + (int) tpu->x * (TPUT_W + 4); } static int tpu_pos_y(struct tpu *tpu) { return 2 * (int) tpu->y * (TPUT_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_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_set_attr(int x, int y, attr_t attr) { (void) mvchgat(y - scry, x - scrx, 1, attr, 0, NULL); } static void tui_draw_tpu(struct tpu *tpu) { char linebuf[TPUT_INPUT_COLS + 1]; struct tpu_port *port; int sx, sy, x, y, w, h; int i, idle, min; attr_t attr;; attr = (tpu_sel == tpu && input_mode == TPU_NAV) ? A_BOLD : 0; sx = tpu_pos_x(tpu); sy = tpu_pos_y(tpu); tui_draw_box(sx, sy, TPUT_W, TPUT_H, attr, WACS_D_ULCORNER, WACS_D_URCORNER, WACS_D_LLCORNER, WACS_D_LRCORNER); min = MIN(tpu->pc, MAX(0, (int) tpu->inst_cnt - TPUT_INPUT_ROWS)); for (i = 0; i < MIN((int) tpu->inst_cnt - min, TPUT_INPUT_ROWS); i++) { asm_print_inst(linebuf, sizeof(linebuf), &tpu->insts[min + i]); tui_draw_text(sx + 2, sy + 1 + i, i == tpu->pc ? A_STANDOUT : 0, "%-*s", TPUT_INPUT_COLS, linebuf); } x = sx + TPUT_W - TPUT_INFO_W; y = sy; w = TPUT_INFO_W; h = TPUT_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, A_BOLD, "ACC"); tui_draw_text(x + 2, y + 2, 0, "%03i", tpu->acc); tui_draw_box(x, (y += TPUT_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, A_BOLD, "BAK"); tui_draw_text(x + 2, y + 2, 0, "%03i", tpu->bak); tui_draw_box(x, (y += TPUT_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, A_BOLD, "LST"); tui_draw_text(x + 2, y + 2, 0, "%s", tpu_last_str(tpu)); tui_draw_box(x, (y += TPUT_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, A_BOLD, "MOD"); tui_draw_text(x + 2, y + 2, 0, "%s", tpu_mode_str(tpu)); tui_draw_box(x, (y += TPUT_INFO_H - 1), w, h, attr, WACS_D_LTEE, WACS_D_RTEE, WACS_D_BTEE, WACS_D_LRCORNER); tui_draw_text(x + 2, y + 1, A_BOLD, "IDL"); if (tpu->steps > 0) idle = (int) ((double) tpu->idle_steps * 100 / (double) tpu->steps); else idle = 100; tui_draw_text(x + 2, y + 2, 0, "%03i", idle); if (tpu->ports[DIR_LEFT].attached) { port = &tpu->ports[DIR_LEFT]; if (port->in >= 0) tui_draw_text(sx - 3, sy + 6, A_BOLD, "%03i", port->in); if (port->type & PORT_IN) tui_draw_wch(sx - 1, sy + 7, port->in >= 0 ? A_BOLD : 0, WACS_RARROW); if (port->type & PORT_OUT) tui_draw_wch(sx - 1, sy + 8, port->out >= 0 ? A_BOLD : 0, WACS_LARROW); if (port->out >= 0) tui_draw_text(sx - 3, sy + 10, A_BOLD, "%03i", port->out); } if (tpu->ports[DIR_RIGHT].attached) { port = &tpu->ports[DIR_RIGHT]; if (port->out >= 0) tui_draw_text(sx + TPUT_W + 1, sy + 5, A_BOLD, "%03i", port->out); if (port->type & PORT_OUT) tui_draw_wch(sx + TPUT_W + 1, sy + 7, port->out >= 0 ? A_BOLD : 0, WACS_RARROW); if (port->type & PORT_IN) tui_draw_wch(sx + TPUT_W + 1, sy + 8, port->in >= 0 ? A_BOLD : 0, WACS_LARROW); if (port->in >= 0) tui_draw_text(sx + TPUT_W + 1, sy + 9, A_BOLD, "%03i", port->in); } if (tpu->ports[DIR_UP].attached) { port = &tpu->ports[DIR_UP]; if (port->out >= 0) tui_draw_text(sx + 9, sy - 1, A_BOLD, "%03i", port->out); if (port->type & PORT_OUT) tui_draw_wch(sx + 13, sy - 1, port->out >= 0 ? A_BOLD : 0, WACS_UARROW); if (port->type & PORT_IN) tui_draw_wch(sx + 15, sy - 1, port->in >= 0 ? A_BOLD : 0, WACS_DARROW); if (port->in >= 0) tui_draw_text(sx + 17, sy - 1, A_BOLD, "%03i", port->in); } if (tpu->ports[DIR_DOWN].attached) { port = &tpu->ports[DIR_DOWN]; if (port->out >= 0) tui_draw_text(sx + 9, sy + TPUT_H, A_BOLD, "%03i", port->out); if (port->type & PORT_IN) tui_draw_wch(sx + 13, sy + TPUT_H, port->in >= 0 ? A_BOLD : 0, WACS_UARROW); if (port->type & PORT_OUT) tui_draw_wch(sx + 15, sy + TPUT_H, port->out >= 0 ? A_BOLD : 0, WACS_DARROW); if (port->in >= 0) tui_draw_text(sx + 17, sy + TPUT_H, A_BOLD, "%03i", port->in); } } static void tui_draw(void) { struct tpu_map_link *link; size_t i; 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); } 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 (dx == MIN) scrx = minx - 2; else if (dx == MAX) scrx = maxx + TPUT_W + 2 - scrw; else if (dx == MID) scrx = (minx + maxx + TPUT_W - scrw) / 2; if (dy == MIN) scry = miny - 2; else if (dy == MAX) scry = maxy + TPUT_H + 2 - scrh; else if (dy == MID) scry = (miny + maxy + TPUT_H - scrh) / 2; } static void handlekey(int key) { enum tpu_port_dir dir; int c; 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': if (tis.stdin_port.out < 0) { c = getc(tis_stdin); if (c >= 0) tis.stdin_port.out = c; } if (tis.stdout_port.in >= 0) { putc(tis.stdout_port.in, tis_stdout); tis.stdout_port.in = -1; } 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->ports[dir].dst_tpu) tpu_sel = tpu_sel->ports[dir].dst_tpu; tui_seek(tpu_sel, MID, MID); break; } } } int main(int argc, char **argv) { bool quit; int key; if (argc != 4) { fprintf(stderr, "Usage: tis-curses FILE STDIN STDOUT\n"); exit(1); } setlocale(LC_ALL, ""); initscr(); raw(); noecho(); keypad(stdscr, TRUE); start_color(); curs_set(0); tui_resize(); ESCDELAY = 0; tis_init(&tis); tis_load(&tis, argv[1]); tis_stdin = fopen(argv[2], "r"); if (!tis_stdin) die("fopen '%s':", argv[2]); tis_stdout = fopen(argv[3], "w+"); if (!tis_stdout) die("fopen '%s':", argv[3]); tpu_sel = tis.stdin_port.dst_tpu; tui_seek(NULL, MID, MID); quit = false; while (!quit) { 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 'i': tui_seek(tis.stdin_port.dst_tpu, MID, MID); break; case 'o': tui_seek(tis.stdout_port.dst_tpu, MID, MID); break; case KEY_CTRL('c'): quit = true; break; default: handlekey(key); break; } } tis_deinit(&tis); fclose(tis_stdin); fclose(tis_stdout); endwin(); }