// Author: Paul Oliver // Project: Salis // Implements a curses TUI for the Salis simulator. // World view renders the contents of the VM memory buffer into a 7 channel image. // It supports zooming in and out, condensing the state of several bytes of memory // into single pixels (terminal cells). When zoomed in, each pixel represents a // single byte in memory. {% set pane_width = 27 %} {% set proc_field_width = 21 %} {% set proc_page_lines = 12 %} {% set log_line_size = 1024 %} {% set log_line_count = 1024 %} {% macro ctrl(x) %}('{{ x }}' & 0x1f){% endmacro %} {% if not args.optimized %} {% set min_fps = 5 %} {% set max_fps = 10 %} {% else %} {% set min_fps = 30 %} {% set max_fps = 60 %} {% endif %} // pages enum { PAGE_CORE, PAGE_PROCESS, PAGE_WORLD, PAGE_IPC, PAGE_LOG, PAGE_COUNT, }; // color pairs enum { PAIR_NOUSE, PAIR_NORMAL, PAIR_HEADER, PAIR_WARN, PAIR_LIVE_PROC, PAIR_SELECTED_PROC, PAIR_FREE_CELL, PAIR_ALLOC_CELL, PAIR_MEM_BLOCK_START, PAIR_SELECTED_MB1, PAIR_SELECTED_MB2, PAIR_SELECTED_IP, PAIR_SELECTED_SP, }; // GFX globals uint64_t g_gfx_vsiz; // zoom level uint64_t *g_gfx_inst; // instruction channel uint64_t *g_gfx_mall; // allocated state channel uint64_t *g_gfx_mbst; // memory block start channel uint64_t *g_gfx_mb0s; // selected organism's memory block #1 channel uint64_t *g_gfx_mb1s; // selected organism's memory block #2 channel uint64_t *g_gfx_ipas; // selected organism's IP channel uint64_t *g_gfx_spas; // selected organism's SP channel // TUI globals bool g_exit; bool g_running; unsigned g_core; unsigned g_page; bool g_proc_genes; uint64_t g_proc_scroll; uint64_t g_proc_field_scroll; uint64_t g_proc_gene_scroll; uint64_t g_proc_selected; uint64_t g_wrld_pos; uint64_t g_wrld_zoom; bool g_wcursor_mode; int g_wcursor_x; int g_wcursor_y; uint64_t g_wcursor_pointed; uint64_t g_log_cnt; unsigned g_log_ptr; unsigned g_log_scroll; bool g_log_warns[{{ log_line_count }}]; time_t g_log_times[{{ log_line_count }}]; char g_logs[{{ log_line_count }}][{{ log_line_size }}]; uint64_t g_vlin; uint64_t g_vsiz; uint64_t g_vlin_rng; uint64_t g_vsiz_rng; uint64_t g_ivpt_scroll; char *g_line_buff; uint64_t g_step_block; const wchar_t *g_zoomed_symbols = ( L"⠀⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇⠈⠉⠊⠋⠌⠍⠎⠏⡈⡉⡊⡋⡌⡍⡎⡏⠐⠑⠒⠓⠔⠕⠖⠗⡐⡑⡒⡓⡔⡕⡖⡗⠘⠙⠚⠛⠜⠝⠞⠟⡘⡙⡚⡛⡜⡝⡞⡟" L"⠠⠡⠢⠣⠤⠥⠦⠧⡠⡡⡢⡣⡤⡥⡦⡧⠨⠩⠪⠫⠬⠭⠮⠯⡨⡩⡪⡫⡬⡭⡮⡯⠰⠱⠲⠳⠴⠵⠶⠷⡰⡱⡲⡳⡴⡵⡶⡷⠸⠹⠺⠻⠼⠽⠾⠿⡸⡹⡺⡻⡼⡽⡾⡿" L"⢀⢁⢂⢃⢄⢅⢆⢇⣀⣁⣂⣃⣄⣅⣆⣇⢈⢉⢊⢋⢌⢍⢎⢏⣈⣉⣊⣋⣌⣍⣎⣏⢐⢑⢒⢓⢔⢕⢖⢗⣐⣑⣒⣓⣔⣕⣖⣗⢘⢙⢚⢛⢜⢝⢞⢟⣘⣙⣚⣛⣜⣝⣞⣟" L"⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯⢰⢱⢲⢳⢴⢵⢶⢷⣰⣱⣲⣳⣴⣵⣶⣷⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿" ); // ---------------------------------------------------------------------------- // GFX functions // ---------------------------------------------------------------------------- void gfx_init(uint64_t vsiz) { assert(vsiz); g_gfx_vsiz = vsiz; g_gfx_inst = calloc(g_gfx_vsiz, sizeof(uint64_t)); g_gfx_mall = calloc(g_gfx_vsiz, sizeof(uint64_t)); g_gfx_mbst = calloc(g_gfx_vsiz, sizeof(uint64_t)); g_gfx_mb0s = calloc(g_gfx_vsiz, sizeof(uint64_t)); g_gfx_mb1s = calloc(g_gfx_vsiz, sizeof(uint64_t)); g_gfx_ipas = calloc(g_gfx_vsiz, sizeof(uint64_t)); g_gfx_spas = calloc(g_gfx_vsiz, sizeof(uint64_t)); assert(g_gfx_inst); assert(g_gfx_mall); assert(g_gfx_mbst); assert(g_gfx_mb0s); assert(g_gfx_mb1s); assert(g_gfx_ipas); assert(g_gfx_spas); } void gfx_free() { if (g_gfx_vsiz == 0) { return; } assert(g_gfx_inst); assert(g_gfx_mall); assert(g_gfx_mbst); assert(g_gfx_mb0s); assert(g_gfx_mb1s); assert(g_gfx_ipas); assert(g_gfx_spas); g_gfx_vsiz = 0; free(g_gfx_inst); free(g_gfx_mall); free(g_gfx_mbst); free(g_gfx_mb0s); free(g_gfx_mb1s); free(g_gfx_ipas); free(g_gfx_spas); g_gfx_inst = NULL; g_gfx_mall = NULL; g_gfx_mbst = NULL; g_gfx_mb0s = NULL; g_gfx_mb1s = NULL; g_gfx_ipas = NULL; g_gfx_spas = NULL; } void gfx_resize(uint64_t vsiz) { assert(vsiz); gfx_free(); gfx_init(vsiz); } void gfx_render_inst(const struct Core *core, uint64_t pos, uint64_t zoom) { assert(core); for (uint64_t i = 0; i < g_gfx_vsiz; ++i) { g_gfx_inst[i] = 0; g_gfx_mall[i] = 0; for (uint64_t j = 0; j < zoom; ++j) { uint64_t addr = pos + (i * zoom) + j; g_gfx_inst[i] += mvec_get_byte(core, addr); g_gfx_mall[i] += mvec_is_alloc(core, addr) ? 1 : 0; } } } void gfx_clear_array(uint64_t *arry) { assert(arry); memset(arry, 0, g_gfx_vsiz * sizeof(uint64_t)); } {% if arch_vars.mvec_loop %} void gfx_accumulate_pixel(uint64_t pos, uint64_t zoom, uint64_t pixa, uint64_t *arry) { assert(arry); uint64_t beg_mod = pos % {{ mvec_size }}; uint64_t end_mod = beg_mod + (g_gfx_vsiz * zoom); uint64_t pix_mod = pixa % {{ mvec_size }}; {% if not args.optimized %} uint64_t inc_cnt = 0; {% endif %} while (pix_mod < end_mod) { if (pix_mod >= beg_mod && pix_mod < end_mod) { uint64_t pixi = (pix_mod - beg_mod) / zoom; assert(pixi < g_gfx_vsiz); arry[pixi]++; {% if not args.optimized %} inc_cnt++; {% endif %} } pix_mod += {{ mvec_size }}; } {% if not args.optimized %} if (zoom != 1) { assert(inc_cnt <= 2); } {% endif %} } {% endif %} {% if not arch_vars.mvec_loop %} void gfx_accumulate_pixel(uint64_t pos, uint64_t zoom, uint64_t pixa, uint64_t *arry) { assert(arry); uint64_t end = pos + (g_gfx_vsiz * zoom); if (pixa < pos || pixa >= end) { return; } uint64_t pixi = (pixa - pos) / zoom; assert(pixi < g_gfx_vsiz); arry[pixi]++; } {% endif %} void gfx_render_mbst(const struct Core *core, uint64_t pos, uint64_t zoom) { assert(core); gfx_clear_array(g_gfx_mbst); for (uint64_t pix = core->pfst; pix <= core->plst; ++pix) { uint64_t mb0a = arch_proc_mb0_addr(core, pix); uint64_t mb1a = arch_proc_mb1_addr(core, pix); gfx_accumulate_pixel(pos, zoom, mb0a, g_gfx_mbst); gfx_accumulate_pixel(pos, zoom, mb1a, g_gfx_mbst); } } void gfx_render_mb0s(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t psel) { assert(core); gfx_clear_array(g_gfx_mb0s); if (psel < core->pfst || psel > core->plst) { return; } uint64_t mb0a = arch_proc_mb0_addr(core, psel); uint64_t mb0s = arch_proc_mb0_size(core, psel); for (uint64_t i = 0; i < mb0s; ++i) { gfx_accumulate_pixel(pos, zoom, mb0a + i, g_gfx_mb0s); } } void gfx_render_mb1s(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t psel) { assert(core); gfx_clear_array(g_gfx_mb1s); if (psel < core->pfst || psel > core->plst) { return; } uint64_t mb1a = arch_proc_mb1_addr(core, psel); uint64_t mb1s = arch_proc_mb1_size(core, psel); for (uint64_t i = 0; i < mb1s; ++i) { gfx_accumulate_pixel(pos, zoom, mb1a + i, g_gfx_mb1s); } } void gfx_render_ipas(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t psel) { assert(core); gfx_clear_array(g_gfx_ipas); if (psel < core->pfst || psel > core->plst) { return; } uint64_t ipa = arch_proc_ip_addr(core, psel); gfx_accumulate_pixel(pos, zoom, ipa, g_gfx_ipas); } void gfx_render_spas(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t psel) { assert(core); gfx_clear_array(g_gfx_spas); if (psel < core->pfst || psel > core->plst) { return; } uint64_t spa = arch_proc_sp_addr(core, psel); gfx_accumulate_pixel(pos, zoom, spa, g_gfx_spas); } void gfx_render(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t psel) { assert(core); gfx_render_inst(core, pos, zoom); gfx_render_mbst(core, pos, zoom); gfx_render_mb0s(core, pos, zoom, psel); gfx_render_mb1s(core, pos, zoom, psel); gfx_render_ipas(core, pos, zoom, psel); gfx_render_spas(core, pos, zoom, psel); } // ---------------------------------------------------------------------------- // TUI generic functions // ---------------------------------------------------------------------------- void ui_line_buff_free() { if (g_line_buff) { free(g_line_buff); } g_line_buff = NULL; } void ui_line_buff_resize() { ui_line_buff_free(); g_line_buff = calloc(COLS + 1, sizeof(char)); } void ui_line(bool clear, int line, int color, int attr, const char *format, ...) { assert(line >= 0); assert(format); if (line >= LINES) { return; } if (clear) { move(line, 0); clrtoeol(); } va_list args; attron(COLOR_PAIR(color) | attr); va_start(args, format); vsnprintf(g_line_buff, COLS, format, args); mvprintw(line, 1, g_line_buff); va_end(args); attroff(COLOR_PAIR(color) | attr); } void ui_clear_line(int l) { ui_line(true, l, PAIR_NORMAL, A_NORMAL, ""); } void ui_field(int line, int col, int color, int attr, const char *format, ...) { assert(line >= 0); assert(col >= 0); assert(format); if (line >= LINES || col >= COLS) { return; } va_list args; attron(COLOR_PAIR(color) | attr); va_start(args, format); vsnprintf(g_line_buff, COLS - col, format, args); mvprintw(line, col, g_line_buff); va_end(args); attroff(COLOR_PAIR(color) | attr); } void ui_str_field(int l, const char *label, const char *value) { assert(label); assert(value); ui_line(false, l, PAIR_NORMAL, A_NORMAL, "%s : %18s", label, value); } void ui_ulx_field(int l, const char *label, uint64_t value) { assert(label); ui_line(false, l, PAIR_NORMAL, A_NORMAL, "%-4s : %#18lx", label, value); } // ---------------------------------------------------------------------------- // Core page functions // ---------------------------------------------------------------------------- void ui_print_core(int l) { ui_line(false, ++l, PAIR_HEADER, A_BOLD, "CORE [%d]", g_core); ui_ulx_field(++l, "cycl", g_cores[g_core].cycl); ui_ulx_field(++l, "mall", g_cores[g_core].mall); ui_ulx_field(++l, "mut0", g_cores[g_core].muta[0]); ui_ulx_field(++l, "mut1", g_cores[g_core].muta[1]); ui_ulx_field(++l, "mut2", g_cores[g_core].muta[2]); ui_ulx_field(++l, "mut3", g_cores[g_core].muta[3]); ui_ulx_field(++l, "pnum", g_cores[g_core].pnum); ui_ulx_field(++l, "pcap", g_cores[g_core].pcap); ui_ulx_field(++l, "pfst", g_cores[g_core].pfst); ui_ulx_field(++l, "plst", g_cores[g_core].plst); ui_ulx_field(++l, "pcur", g_cores[g_core].pcur); ui_ulx_field(++l, "psli", g_cores[g_core].psli); ui_ulx_field(++l, "ivpt", g_cores[g_core].ivpt); } // ---------------------------------------------------------------------------- // Process page functions // ---------------------------------------------------------------------------- int ui_proc_pair(uint64_t pix) { if (pix == g_proc_selected) { return PAIR_SELECTED_PROC; } else if (mvec_proc_is_live(&g_cores[g_core], pix)) { return PAIR_LIVE_PROC; } else { return PAIR_NORMAL; } } const char *ui_proc_state(uint64_t pix) { return mvec_proc_is_live(&g_cores[g_core], pix) ? "live" : "dead"; } void ui_print_process_genome_header(int l) { ui_line(false, l++, PAIR_NORMAL, A_NORMAL, "%s : %18s : %s", "stat", "pix", "genome"); } void ui_print_process_gene(int l, int gcol, uint64_t gidx, uint64_t mba, uint64_t pix, int pair) { assert(gcol >= {{ pane_width }} + 2); assert(gcol < COLS); assert(mvec_proc_is_live(&g_cores[g_core], pix)); assert(pair == PAIR_SELECTED_MB1 || pair == PAIR_SELECTED_MB2); const struct Core *core = &g_cores[g_core]; uint64_t addr = mba + gidx; uint8_t byte = mvec_get_byte(core, addr); wchar_t gsym[2] = { arch_symbol(byte), L'\0' }; cchar_t cchar = { 0 }; int pair_cell; if (arch_proc_ip_addr(core, pix) == addr) { pair_cell = PAIR_SELECTED_IP; } else if (arch_proc_sp_addr(core, pix) == addr) { pair_cell = PAIR_SELECTED_SP; } else { pair_cell = pair; } setcchar(&cchar, gsym, 0, pair_cell, NULL); mvadd_wch(l, gcol, &cchar); } void ui_print_process_genes(int l, uint64_t pix) { ui_line(true, l, ui_proc_pair(pix), A_NORMAL, "%s : %#18lx :", ui_proc_state(pix), pix); if (!mvec_proc_is_live(&g_cores[g_core], pix)) { return; } const struct Core *core = &g_cores[g_core]; int scol = {{ pane_width }} + 2; int gcol = scol - g_proc_gene_scroll; uint64_t mb0a = arch_proc_mb0_addr(core, pix); uint64_t mb0s = arch_proc_mb0_size(core, pix); uint64_t mb1a = arch_proc_mb1_addr(core, pix); uint64_t mb1s = arch_proc_mb1_size(core, pix); for (uint64_t gidx = 0; gidx < mb0s && gcol < COLS; ++gidx, ++gcol) { if (gcol >= scol) { ui_print_process_gene(l, gcol, gidx, mb0a, pix, PAIR_SELECTED_MB1); } } for (uint64_t gidx = 0; gidx < mb1s && gcol < COLS; ++gidx, ++gcol) { if (gcol >= scol) { ui_print_process_gene(l, gcol, gidx, mb1a, pix, PAIR_SELECTED_MB2); } } clrtoeol(); } void ui_print_process_field_header_element(int l, int fidx, const char *name) { assert(fidx >= 0); assert(name); if (fidx < (int)g_proc_field_scroll) { return; } int foff = fidx - g_proc_field_scroll; int fcol = foff * {{ proc_field_width }} + {{ pane_width }} - 1; ui_field(l, fcol, PAIR_NORMAL, A_NORMAL, " : %18s", name); } void ui_print_process_field_header(int l) { ui_line(true, l, PAIR_NORMAL, A_NORMAL, "%s : %18s", "stat", "pix"); int fidx = 0; {% for _, val in arch_vars.proc_fields %} ui_print_process_field_header_element(l, fidx++, "{{ val }}"); {% endfor %} } void ui_print_process_field_element(int l, int fidx, int fclr, uint64_t field) { assert(fidx >= 0); if (fidx < (int)g_proc_field_scroll) { return; } int foff = fidx - g_proc_field_scroll; int fcol = foff * {{ proc_field_width }} + {{ pane_width }} - 1; ui_field(l, fcol, fclr, A_NORMAL, " : %#18lx", field); } void ui_print_process_fields(int l, uint64_t pix) { ui_line(true, l, ui_proc_pair(pix), A_NORMAL, "%s : %#18lx", ui_proc_state(pix), pix); const struct Proc *proc = proc_get(&g_cores[g_core], pix); int fidx = 0; int fclr = ui_proc_pair(pix); {% for _, val in arch_vars.proc_fields %} ui_print_process_field_element(l, fidx++, fclr, proc->{{ val }}); {% endfor %} } void ui_print_process(int l) { l++; ui_line(true, l++, PAIR_HEADER, A_BOLD, "PROCESS [vs:%#lx | ps:%#lx | pf:%#lx | pl:%#lx | fs:%#lx | gs:%#lx]", g_proc_scroll, g_proc_selected, g_cores[g_core].pfst, g_cores[g_core].plst, g_proc_field_scroll, g_proc_gene_scroll ); uint64_t pix = g_proc_scroll; if (g_proc_genes) { ui_print_process_genome_header(l++); while (l < LINES) { ui_print_process_genes(l++, pix++); } } else { ui_print_process_field_header(l++); while (l < LINES) { ui_print_process_fields(l++, pix++); } } } // ---------------------------------------------------------------------------- // World page functions // ---------------------------------------------------------------------------- void ui_world_resize() { assert(g_wrld_zoom); g_vlin = 0; g_vsiz = 0; g_vlin_rng = 0; g_vsiz_rng = 0; if (COLS > {{ pane_width }}) { g_vlin = COLS - {{ pane_width }}; g_vsiz = LINES * g_vlin; g_vlin_rng = g_vlin * g_wrld_zoom; g_vsiz_rng = g_vsiz * g_wrld_zoom; gfx_resize(g_vsiz); } } {% if arch_vars.mvec_loop %} void ui_print_cell(uint64_t i, uint64_t r, uint64_t x, uint64_t y) { {% else %} void ui_print_cell(uint64_t i, uint64_t r, uint64_t x, uint64_t y, uint64_t a) { {% endif %} wchar_t inst_nstr[2] = { L'\0', L'\0' }; cchar_t cchar = { 0 }; uint64_t inst_avrg = g_gfx_inst[i] / g_wrld_zoom; if (g_wrld_zoom == 1) { inst_nstr[0] = arch_symbol((uint8_t)inst_avrg); } else { inst_nstr[0] = g_zoomed_symbols[(uint8_t)inst_avrg]; } int pair_cell; {% if arch_vars.mvec_loop %} if (g_wcursor_mode && r == (uint64_t)g_wcursor_x && y == (uint64_t)g_wcursor_y) { {% else %} if (a >= {{ mvec_size }} || (g_wcursor_mode && r == (uint64_t)g_wcursor_x && y == (uint64_t)g_wcursor_y)) { {% endif %} pair_cell = PAIR_NORMAL; } else if (g_wcursor_mode && r == (uint64_t)g_wcursor_x && y == (uint64_t)g_wcursor_y) { pair_cell = PAIR_NORMAL; } else if (g_gfx_ipas[i] != 0) { pair_cell = PAIR_SELECTED_IP; } else if (g_gfx_spas[i] != 0) { pair_cell = PAIR_SELECTED_SP; } else if (g_gfx_mb0s[i] != 0) { pair_cell = PAIR_SELECTED_MB1; } else if (g_gfx_mb1s[i] != 0) { pair_cell = PAIR_SELECTED_MB2; } else if (g_gfx_mbst[i] != 0) { pair_cell = PAIR_MEM_BLOCK_START; } else if (g_gfx_mall[i] != 0) { pair_cell = PAIR_ALLOC_CELL; } else { pair_cell = PAIR_FREE_CELL; } setcchar(&cchar, inst_nstr, 0, pair_cell, NULL); mvadd_wch(y, x, &cchar); } void ui_print_wcursor_bar() { ui_clear_line(LINES - 1); const struct Core *core = &g_cores[g_core]; char cownr[{{ proc_field_width }}]; uint64_t cpos = g_vlin * g_wcursor_y + g_wcursor_x; uint64_t caddr = cpos * g_wrld_zoom + g_wrld_pos; uint8_t cbyte = mvec_get_byte(core, caddr); if (mvec_is_alloc(core, caddr)) { g_wcursor_pointed = mvec_get_owner(core, caddr); snprintf(cownr, {{ proc_field_width }}, "%#lx", g_wcursor_pointed); } else { g_wcursor_pointed = (uint64_t)(-1); snprintf(cownr, {{ proc_field_width }}, "-"); } mvprintw( LINES - 1, 1, "cursor | x:%#x | y:%#x | addr:%#lx | isum:%#lx | iavr:%#lx | mall:%#lx | mbst:%#lx | mnem:<%s> | ownr:%s", g_wcursor_x, g_wcursor_y, caddr, g_gfx_inst[cpos], g_gfx_inst[cpos] / g_wrld_zoom, g_gfx_mall[cpos], g_gfx_mbst[cpos], arch_mnemonic(cbyte), cownr ); } void ui_print_world(int l) { l++; ui_line(false, l++, PAIR_HEADER, A_BOLD, "WORLD"); ui_ulx_field(l++, "wrlp", g_wrld_pos); ui_ulx_field(l++, "wrlz", g_wrld_zoom); ui_ulx_field(l++, "psel", g_proc_selected); ui_ulx_field(l++, "pabs", g_proc_selected % g_cores[g_core].pcap); ui_ulx_field(l++, "vrng", g_vsiz_rng); ui_str_field(l++, "curs", g_wcursor_mode ? "on" : "off"); l++; ui_line(false, l++, PAIR_HEADER, A_BOLD, "SELECTED"); const struct Proc *psel = proc_get(&g_cores[g_core], g_proc_selected); {% for _, val in arch_vars.proc_fields %} ui_ulx_field(l++, "{{ val }}", psel->{{ val }}); {% endfor %} if (!g_vlin) { return; } gfx_render(&g_cores[g_core], g_wrld_pos, g_wrld_zoom, g_proc_selected); if (g_wcursor_mode) { int xmax = g_vlin - 1; int ymax = LINES - 2; g_wcursor_x = (g_wcursor_x < xmax) ? g_wcursor_x : xmax; g_wcursor_y = (g_wcursor_y < ymax) ? g_wcursor_y : ymax; } for (uint64_t i = 0; i < g_vsiz; ++i) { uint64_t r = i % g_vlin; uint64_t x = r + {{ pane_width }}; uint64_t y = i / g_vlin; {% if arch_vars.mvec_loop %} ui_print_cell(i, r, x, y); {% else %} uint64_t a = g_wrld_pos + (i * g_wrld_zoom); ui_print_cell(i, r, x, y, a); {% endif %} } if (g_wcursor_mode) { ui_print_wcursor_bar(); } } // ---------------------------------------------------------------------------- // IPC page functions // ---------------------------------------------------------------------------- void ui_print_ipc_field(int l, uint64_t i, int color) { uint8_t iinst = g_cores[g_core].iviv[i]; uint64_t iaddr = g_cores[g_core].ivav[i]; ui_field(l, {{ pane_width }}, color, A_NORMAL, "%#18x : %#18x : %#18x", i, iinst, iaddr); } void ui_print_ipc_data() { ui_field(0, {{ pane_width }}, PAIR_NORMAL, A_NORMAL, "%18s : %18s : %18s", "ipci", "inst", "addr"); int l = 1 - g_ivpt_scroll; for (uint64_t i = 0; i < {{ sync_interval }}; ++i) { if (i == g_cores[g_core].ivpt) { if (l >= 1) { ui_print_ipc_field(l++, i, PAIR_SELECTED_PROC); } continue; } uint8_t iinst = g_cores[g_core].iviv[i]; if ((iinst & {{ ipc_flag }}) != 0) { if (l >= 1) { ui_print_ipc_field(l++, i, PAIR_LIVE_PROC); } continue; } } for (; l < LINES; ++l) { if (l >= 1) { move(l, {{ pane_width }}); clrtoeol(); } } } void ui_print_ipc(int l) { l++; const struct Core *core = &g_cores[g_core]; ui_line(true, l++, PAIR_HEADER, A_BOLD, "IPC [%#lx]", g_ivpt_scroll); ui_ulx_field(l++, "ivpt", core->ivpt); ui_ulx_field(l++, "ivpi", core->iviv[core->ivpt]); ui_ulx_field(l++, "ivpa", core->ivav[core->ivpt]); ui_print_ipc_data(); } // ---------------------------------------------------------------------------- // Log page functions // ---------------------------------------------------------------------------- void ui_info_impl(const char *format, ...) { g_log_warns[g_log_ptr] = false; g_log_times[g_log_ptr] = time(NULL); va_list args; va_start(args, format); vsnprintf(g_logs[g_log_ptr], {{ log_line_size }}, format, args); va_end(args); g_log_cnt++; g_log_ptr = (g_log_ptr + 1) % {{ log_line_count }}; } void ui_warn_impl(const char *format, ...) { g_log_warns[g_log_ptr] = true; g_log_times[g_log_ptr] = time(NULL); va_list args; va_start(args, format); vsnprintf(g_logs[g_log_ptr], {{ log_line_size }}, format, args); va_end(args); g_log_cnt++; g_log_ptr = (g_log_ptr + 1) % {{ log_line_count }}; } void ui_clear_log_line(int line) { assert(line >= 0 && line < LINES); move(line, {{ pane_width }}); clrtoeol(); } void ui_print_log_line(unsigned lptr, int line) { assert(lptr < {{ log_line_count }}); assert(line >= 0 && line < LINES); ui_clear_log_line(line); // Prints a log entry if (strlen(g_logs[lptr])) { struct tm tm = *localtime(&g_log_times[lptr]); // Timestamp ui_field( line, {{ pane_width }}, PAIR_NORMAL, A_NORMAL, ": %d-%02d-%02d %02d:%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec ); // Level ui_field( line, {{ pane_width }} + 22, g_log_warns[lptr] ? PAIR_WARN : PAIR_HEADER, A_NORMAL, g_log_warns[lptr] ? "WARN:" : "INFO:" ); // Message ui_field( line, {{ pane_width }} + 28, PAIR_NORMAL, A_NORMAL, g_logs[lptr] ); } } void ui_print_log(int l) { l++; ui_line(true, l++, PAIR_HEADER, A_BOLD, "LOG"); ui_ulx_field(l++, "lscr", g_log_scroll); ui_ulx_field(l++, "lcnt", g_log_cnt); ui_ulx_field(l++, "lptr", g_log_ptr); unsigned lptr = g_log_ptr; int line = LINES + g_log_scroll; while (line) { lptr = (lptr - 1 + {{ log_line_count }}) % {{ log_line_count }}; line--; if (line < LINES) { ui_print_log_line(lptr, line); } if (lptr == g_log_ptr) { break; } } while (line) { line--; ui_clear_log_line(line); } } // ---------------------------------------------------------------------------- // Main print function // ---------------------------------------------------------------------------- void ui_print() { int l = 1; ui_line(false, l++, PAIR_HEADER, A_BOLD, "SALIS [%d:%d]", g_core, {{ args.cores }}); ui_str_field(l++, "name", "{{ args.name }}"); ui_ulx_field(l++, "seed", {{ args.seed }}); ui_str_field(l++, "fbit", "{{ "yes" if args.muta_flip else "no" }}"); ui_ulx_field(l++, "asav", {{ auto_save_interval }}); ui_str_field(l++, "arch", "{{ args.arch }}"); ui_ulx_field(l++, "size", {{ mvec_size }}); ui_ulx_field(l++, "syni", {{ sync_interval }}); ui_ulx_field(l++, "step", g_steps); ui_ulx_field(l++, "sync", g_syncs); ui_ulx_field(l++, "step", g_step_block); switch (g_page) { case PAGE_CORE: ui_print_core(l); break; case PAGE_PROCESS: ui_print_process(l); break; case PAGE_WORLD: ui_print_world(l); break; case PAGE_IPC: ui_print_ipc(l); break; case PAGE_LOG: ui_print_log(l); break; default: break; } } // ---------------------------------------------------------------------------- // Control function // ---------------------------------------------------------------------------- void ev_vscroll(int ev) { switch (g_page) { case PAGE_PROCESS: switch (ev) { case 'W': g_proc_scroll += (LINES > {{ proc_page_lines }}) ? LINES - {{ proc_page_lines }} : 0; break; case 'S': g_proc_scroll -= (LINES > {{ proc_page_lines }}) ? LINES - {{ proc_page_lines }} : 0; break; case 'w': g_proc_scroll += 1; break; case 's': g_proc_scroll -= 1; break; case 'q': g_proc_scroll = 0; break; default: break; } break; case PAGE_WORLD: { switch (ev) { case 'W': g_wrld_pos += g_vsiz_rng; break; case 'S': g_wrld_pos -= g_vsiz_rng; break; case 'w': g_wrld_pos += g_vlin_rng; break; case 's': {% if arch_vars.mvec_loop %} g_wrld_pos -= g_vlin_rng; {% else %} if (g_wrld_pos < g_vlin_rng) { g_wrld_pos = 0; } else { g_wrld_pos -= g_vlin_rng; } {% endif %} break; case 'q': g_wrld_pos = 0; break; default: break; } break; } case PAGE_IPC: switch (ev) { case 'W': g_ivpt_scroll += LINES; break; case 'S': g_ivpt_scroll -= g_ivpt_scroll < (uint64_t)LINES ? g_ivpt_scroll : (uint64_t)LINES; break; case 'w': g_ivpt_scroll += 1; break; case 's': g_ivpt_scroll -= g_ivpt_scroll ? 1 : 0; break; case 'q': g_ivpt_scroll = 0; break; } break; case PAGE_LOG: switch (ev) { case 'W': g_log_scroll += LINES; g_log_scroll = g_log_scroll >= {{ log_line_count }} ? {{ log_line_count }} - 1 : g_log_scroll; break; case 'S': g_log_scroll -= g_log_scroll < (uint64_t)LINES ? g_log_scroll : (uint64_t)LINES; break; case 'w': g_log_scroll += 1; g_log_scroll = g_log_scroll >= {{ log_line_count }} ? {{ log_line_count }} - 1 : g_log_scroll; break; case 's': g_log_scroll -= g_log_scroll ? 1 : 0; break; case 'q': g_log_scroll = 0; break; } break; default: break; } } void ev_hscroll(int ev) { switch (g_page) { case PAGE_PROCESS: { uint64_t *hs_var = g_proc_genes ? &g_proc_gene_scroll : &g_proc_field_scroll; switch (ev) { case 'A': *hs_var = 0; break; case 'a': *hs_var -= *hs_var ? 1 : 0; break; case 'd': (*hs_var)++; break; default: break; } break; } case PAGE_WORLD: switch (ev) { case 'a': {% if arch_vars.mvec_loop %} g_wrld_pos -= g_wrld_zoom; {% else %} if (g_wrld_pos < g_wrld_zoom) { g_wrld_pos = 0; } else { g_wrld_pos -= g_wrld_zoom; } {% endif %} break; case 'd': g_wrld_pos += g_wrld_zoom; break; default: break; } break; default: break; } } void ev_zoom(int ev) { switch (g_page) { case PAGE_WORLD: switch (ev) { case 'x': g_wrld_zoom *= (g_vlin != 0 && g_vsiz_rng < {{ mvec_size }}) ? 2 : 1; ui_world_resize(); break; case 'z': g_wrld_zoom /= (g_wrld_zoom != 1) ? 2 : 1; ui_world_resize(); break; default: break; } break; default: break; } } void ev_move_wcursor(int ev) { switch (ev) { case KEY_UP: g_wcursor_y -= (g_wcursor_y != 0) ? 1 : 0; break; case KEY_DOWN: g_wcursor_y += (g_wcursor_y < LINES - 2) ? 1 : 0; break; case KEY_LEFT: g_wcursor_x -= (g_wcursor_x != 0) ? 1 : 0; break; case KEY_RIGHT: g_wcursor_x += ((uint64_t)g_wcursor_x < g_vlin - 1) ? 1 : 0; break; default: break; } } void ev_sel_proc(int ev) { if (g_page != PAGE_PROCESS && g_page != PAGE_WORLD) { return; } switch (ev) { case 'o': g_proc_selected -= 1; break; case 'p': g_proc_selected += 1; break; case 'f': g_proc_selected = g_cores[g_core].pfst; break; case 'l': g_proc_selected = g_cores[g_core].plst; break; default: break; } } void ev_goto_sel_proc() { switch (g_page) { case PAGE_PROCESS: g_proc_scroll = g_proc_selected; break; case PAGE_WORLD: g_wrld_pos = g_cores[g_core].pvec[g_proc_selected % g_cores[g_core].pcap].mb0a; break; default: break; } } void ev_handle() { int ev = getch(); if (g_page == PAGE_WORLD && g_wcursor_mode) { switch (ev) { case KEY_UP: case KEY_DOWN: case KEY_LEFT: case KEY_RIGHT: ev_move_wcursor(ev); return; case '\n': if (g_wcursor_pointed != (uint64_t)(-1)) { g_proc_selected = g_wcursor_pointed; } break; default: break; } } switch (ev) { case {{ ctrl('c') }}: g_exit = true; break; case KEY_SLEFT: clear(); g_core = (g_core + {{ args.cores }} - 1) % {{ args.cores }}; break; case KEY_SRIGHT: clear(); g_core = (g_core + 1) % {{ args.cores }}; break; case KEY_LEFT: clear(); g_page = (g_page + PAGE_COUNT - 1) % PAGE_COUNT; break; case KEY_RIGHT: clear(); g_page = (g_page + 1) % PAGE_COUNT; break; case KEY_RESIZE: clear(); ui_line_buff_resize(); ui_world_resize(); if (g_vlin) { while (g_vsiz_rng >= {{ mvec_size }} * 2 && g_wrld_zoom != 1) { g_wrld_zoom /= 2; ui_world_resize(); } } g_wcursor_mode = false; break; case 'W': case 'S': case 'w': case 's': case 'q': ev_vscroll(ev); break; case 'A': case 'a': case 'd': ev_hscroll(ev); break; case 'z': case 'x': ev_zoom(ev); break; case 'o': case 'p': case 'f': case 'l': ev_sel_proc(ev); break; case 'k': ev_goto_sel_proc(); break; case 'g': if (g_page == PAGE_PROCESS) { clear(); g_proc_genes = !g_proc_genes; } break; case 'c': if (g_page == PAGE_WORLD) { clear(); if (g_vlin == 0) { g_wcursor_mode = false; } else { g_wcursor_mode = !g_wcursor_mode; } } break; case ' ': g_running = !g_running; nodelay(stdscr, g_running); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': if (!g_running) { uint64_t cycles = 1 << (((ev - '0') ? (ev - '0') : 10) - 1); salis_step(cycles); } break; default: break; } } // ---------------------------------------------------------------------------- // Main functions // ---------------------------------------------------------------------------- void init() { setlocale(LC_ALL, ""); initscr(); raw(); noecho(); curs_set(0); keypad(stdscr, TRUE); start_color(); init_color(COLOR_BLACK, 0, 0, 0); init_pair(PAIR_NORMAL, COLOR_WHITE, COLOR_BLACK ); init_pair(PAIR_HEADER, COLOR_BLUE, COLOR_BLACK ); init_pair(PAIR_WARN, COLOR_RED, COLOR_BLACK ); init_pair(PAIR_LIVE_PROC, COLOR_BLUE, COLOR_BLACK ); init_pair(PAIR_SELECTED_PROC, COLOR_YELLOW, COLOR_BLACK ); init_pair(PAIR_FREE_CELL, COLOR_BLACK, COLOR_BLUE ); init_pair(PAIR_ALLOC_CELL, COLOR_BLACK, COLOR_CYAN ); init_pair(PAIR_MEM_BLOCK_START, COLOR_BLACK, COLOR_WHITE ); init_pair(PAIR_SELECTED_MB1, COLOR_BLACK, COLOR_YELLOW ); init_pair(PAIR_SELECTED_MB2, COLOR_BLACK, COLOR_GREEN ); init_pair(PAIR_SELECTED_IP, COLOR_BLACK, COLOR_RED ); init_pair(PAIR_SELECTED_SP, COLOR_BLACK, COLOR_MAGENTA); // Install loggers g_info = ui_info_impl; g_warn = ui_warn_impl; {% if args.command == "new" %} salis_init(); {% elif args.command == "load" %} salis_load(); {% endif %} g_wrld_zoom = 1; g_step_block = 1; ui_line_buff_resize(); ui_world_resize(); } void exec() { while (!g_exit) { if (g_running) { clock_t beg = clock(); salis_step(g_step_block - (g_steps % g_step_block)); clock_t end = clock(); if ((end - beg) < (CLOCKS_PER_SEC / {{ min_fps }})) { g_step_block <<= 1; } if ((end - beg) >= (CLOCKS_PER_SEC / {{ max_fps }}) && g_step_block != 1) { g_step_block >>= 1; } } ui_print(); ev_handle(); } } void quit() { gfx_free(); ui_line_buff_free(); salis_save("{{ sim_path }}"); salis_free(); endwin(); } int main() { init(); exec(); quit(); return 0; }