diff options
| author | Paul Oliver <contact@pauloliver.dev> | 2026-07-31 04:04:37 +0200 |
|---|---|---|
| committer | Paul Oliver <contact@pauloliver.dev> | 2026-08-05 00:02:22 +0200 |
| commit | 85a58f61ed0dd0b40a612d25ce966255c57efe3a (patch) | |
| tree | 54862e397cfea8f5a32779a6674156912b46c48f | |
| parent | 8a4563d6d0c9e81fdaa42f6f760cae2b49cad431 (diff) | |
Adds curses UI
| -rw-r--r-- | arch/null/arch_inst.c | 15 | ||||
| -rw-r--r-- | arch/null/arch_inst.h | 21 | ||||
| -rw-r--r-- | arch/null/arch_spec.h | 2 | ||||
| -rw-r--r-- | core/arch_inst.h | 11 | ||||
| -rw-r--r-- | core/logger.c | 69 | ||||
| -rw-r--r-- | core/logger.h | 16 | ||||
| -rw-r--r-- | core/salis.c | 85 | ||||
| -rw-r--r-- | core/salis.h | 7 | ||||
| -rwxr-xr-x | salis.py | 29 | ||||
| -rw-r--r-- | ui/curses/opts.json | 5 | ||||
| -rw-r--r-- | ui/curses/ui.c | 1428 | ||||
| -rw-r--r-- | ui/daemon/links.json | 1 | ||||
| -rw-r--r-- | ui/daemon/opts.json | 5 |
13 files changed, 1580 insertions, 114 deletions
diff --git a/arch/null/arch_inst.c b/arch/null/arch_inst.c index 43f5d0c..3f30510 100644 --- a/arch/null/arch_inst.c +++ b/arch/null/arch_inst.c @@ -15,6 +15,7 @@ #include <stdbool.h> #include <stddef.h> #include <stdint.h> +#include <wchar.h> #include "arch_inst.h" @@ -34,9 +35,7 @@ // ---------------------------------------------------------------------------- // [section] globals // ---------------------------------------------------------------------------- -wchar_t *g_arch_inst_symbols = ( - L"⠀⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇⠈⠉⠊⠋⠌⠍⠎⠏⡈⡉⡊⡋⡌⡍⡎⡏⠐⠑⠒⠓⠔⠕⠖⠗⡐⡑⡒⡓⡔⡕⡖⡗⠘⠙⠚⠛⠜⠝⠞⠟⡘⡙⡚⡛⡜⡝⡞⡟" - L"⠠⠡⠢⠣⠤⠥⠦⠧⡠⡡⡢⡣⡤⡥⡦⡧⠨⠩⠪⠫⠬⠭⠮⠯⡨⡩⡪⡫⡬⡭⡮⡯⠰⠱⠲⠳⠴⠵⠶⠷⡰⡱⡲⡳⡴⡵⡶⡷⠸⠹⠺⠻⠼⠽⠾⠿⡸⡹⡺⡻⡼⡽⡾⡿" +static const wchar_t *g_arch_inst_symbols = ( L"⢀⢁⢂⢃⢄⢅⢆⢇⣀⣁⣂⣃⣄⣅⣆⣇⢈⢉⢊⢋⢌⢍⢎⢏⣈⣉⣊⣋⣌⣍⣎⣏⢐⢑⢒⢓⢔⢕⢖⢗⣐⣑⣒⣓⣔⣕⣖⣗⢘⢙⢚⢛⢜⢝⢞⢟⣘⣙⣚⣛⣜⣝⣞⣟" L"⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯⢰⢱⢲⢳⢴⢵⢶⢷⣰⣱⣲⣳⣴⣵⣶⣷⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿" ); @@ -44,17 +43,15 @@ wchar_t *g_arch_inst_symbols = ( // ---------------------------------------------------------------------------- // [section] definitions // ---------------------------------------------------------------------------- -int arch_inst_cap(void) { - return ARCH_INST_CAP; -} - wchar_t arch_inst_symbol(uint8_t inst) { - assert(inst < ARCH_INST_CAP); + assert(inst < ARCH_INST_COUNT); + assert(wcslen(g_arch_inst_symbols) == ARCH_INST_COUNT); return g_arch_inst_symbols[inst]; } const char *arch_inst_mnemonic(uint8_t inst) { - assert(inst < ARCH_INST_CAP); + assert(inst < ARCH_INST_COUNT); + assert(wcslen(g_arch_inst_symbols) == ARCH_INST_COUNT); switch (inst) { #define INST(code) case 0x##code: return "null " #code; diff --git a/arch/null/arch_inst.h b/arch/null/arch_inst.h deleted file mode 100644 index f9d85ff..0000000 --- a/arch/null/arch_inst.h +++ /dev/null @@ -1,21 +0,0 @@ -// file : arch/null/arch_inst.h -// project : Salis-VM -// author : Paul Oliver <contact@pauloliver.dev> - -#pragma once - -// index: -// [section] macros -// [section] definitions - -// ---------------------------------------------------------------------------- -// [section] macros -// ---------------------------------------------------------------------------- -#define ARCH_INST_CAP 0x80 - -// ---------------------------------------------------------------------------- -// [section] definitions -// ---------------------------------------------------------------------------- -int arch_inst_cap(void); -wchar_t arch_inst_symbol(uint8_t inst); -const char *arch_inst_mnemonic(uint8_t inst); diff --git a/arch/null/arch_spec.h b/arch/null/arch_spec.h index eeb6d44..ff1fb58 100644 --- a/arch/null/arch_spec.h +++ b/arch/null/arch_spec.h @@ -4,7 +4,7 @@ #pragma once -//#define ARCH_SPEC_MVEC_LOOP +#define ARCH_SPEC_MUTA_FLIP #define ARCH_SPEC_CORE_FIELDS #define ARCH_SPEC_PROC_FIELDS \ diff --git a/core/arch_inst.h b/core/arch_inst.h new file mode 100644 index 0000000..d06c9c8 --- /dev/null +++ b/core/arch_inst.h @@ -0,0 +1,11 @@ +// file : core/null/arch_inst.h +// project : Salis-VM +// author : Paul Oliver <contact@pauloliver.dev> + +#pragma once + +#define ARCH_INST_COUNT 0x80 +#define ARCH_INST_MASK 0x7f + +wchar_t arch_inst_symbol(uint8_t inst); +const char *arch_inst_mnemonic(uint8_t inst); diff --git a/core/logger.c b/core/logger.c index 47182ce..29f371e 100644 --- a/core/logger.c +++ b/core/logger.c @@ -5,6 +5,7 @@ // index: // [section] includes // [section] macros +// [section] extern globals // [section] definitions // ---------------------------------------------------------------------------- @@ -21,8 +22,6 @@ // ---------------------------------------------------------------------------- // [section] macros // ---------------------------------------------------------------------------- -#define LOG_BUFFER_SIZE 0x800 - #define COLOR_CLEAR "\033[0m" #define COLOR_TRACE "\033[1;34m" #define COLOR_INFO "\033[1;32m" @@ -33,13 +32,19 @@ #define LEVEL_ATTENTION "ATTENTION" // ---------------------------------------------------------------------------- +// [section] extern globals +// ---------------------------------------------------------------------------- +void (*log_trace)(const char *format, ...) = log_trace_to_stdout; +void (*log_info)(const char *format, ...) = log_info_to_stdout; +void (*log_attention)(const char *format, ...) = log_attention_to_stdout; + +// ---------------------------------------------------------------------------- // [section] definitions // ---------------------------------------------------------------------------- -static void log_default(const char *color_code, const char *level, const char *format, va_list args) { +static void log_to_buffer(char buffer[LOG_BUFFER_SIZE], const char *color_code, const char *level, const char *format, va_list args) { assert(level); assert(format); - char log_buffer[LOG_BUFFER_SIZE]; struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); int msec = (int)(ts.tv_nsec / 1000000l); @@ -47,9 +52,9 @@ static void log_default(const char *color_code, const char *level, const char *f pid_t pid = getpid(); int rem = snprintf( - log_buffer, + buffer, LOG_BUFFER_SIZE, - "\r%s%d-%02d-%02d %02d:%02d:%02d.%03d %07d [%s]%s ", + "%s%d-%02d-%02d %02d:%02d:%02d.%03d %07d [%s]%s ", color_code ? color_code : "", tm.tm_year + 1900, tm.tm_mon + 1, @@ -63,36 +68,66 @@ static void log_default(const char *color_code, const char *level, const char *f color_code ? COLOR_CLEAR : "" ); - vsnprintf(log_buffer + rem, LOG_BUFFER_SIZE - rem, format, args); - printf("%s\n", log_buffer); - fflush(stdout); + vsnprintf(buffer + rem, LOG_BUFFER_SIZE - rem, format, args); } -void log_trace(const char *format, ...) { +void log_trace_to_stdout(const char *format, ...) { assert(format); + (void)format; #if defined(LOG_TRACE) va_list args; va_start(args, format); - log_default(COLOR_TRACE, LEVEL_TRACE, format, args); + char buffer[LOG_BUFFER_SIZE]; + log_to_buffer(buffer, COLOR_TRACE, LEVEL_TRACE, format, args); + printf("\r%s\n", buffer); + fflush(stdout); va_end(args); -#else - (void)format; #endif } -void log_info(const char *format, ...) { +void log_info_to_stdout(const char *format, ...) { assert(format); va_list args; va_start(args, format); - log_default(COLOR_INFO, LEVEL_INFO, format, args); + char buffer[LOG_BUFFER_SIZE]; + log_to_buffer(buffer, COLOR_INFO, LEVEL_INFO, format, args); + printf("\r%s\n", buffer); + fflush(stdout); va_end(args); } -void log_attention(const char *format, ...) { +void log_attention_to_stdout(const char *format, ...) { assert(format); va_list args; va_start(args, format); - log_default(COLOR_ATTENTION, LEVEL_ATTENTION, format, args); + char buffer[LOG_BUFFER_SIZE]; + log_to_buffer(buffer, COLOR_ATTENTION, LEVEL_ATTENTION, format, args); + printf("\r%s\n", buffer); + fflush(stdout); va_end(args); } + +void vlog_trace_to_buffer(char buffer[LOG_BUFFER_SIZE], const char *format, va_list args) { + assert(format); + assert(args); + (void)buffer; + (void)format; + (void)args; + +#if defined(LOG_TRACE) + log_to_buffer(buffer, NULL, LEVEL_TRACE, format, args); +#endif +} + +void vlog_info_to_buffer(char buffer[LOG_BUFFER_SIZE], const char *format, va_list args) { + assert(format); + assert(args); + log_to_buffer(buffer, NULL, LEVEL_INFO, format, args); +} + +void vlog_attention_to_buffer(char buffer[LOG_BUFFER_SIZE], const char *format, va_list args) { + assert(format); + assert(args); + log_to_buffer(buffer, NULL, LEVEL_ATTENTION, format, args); +} diff --git a/core/logger.h b/core/logger.h index e0da38b..e31cba5 100644 --- a/core/logger.h +++ b/core/logger.h @@ -4,6 +4,16 @@ #pragma once -void log_trace(const char *format, ...); -void log_info(const char *format, ...); -void log_attention(const char *format, ...); +#define LOG_BUFFER_SIZE 0x800 + +extern void (*log_trace)(const char *format, ...); +extern void (*log_info)(const char *format, ...); +extern void (*log_attention)(const char *format, ...); + +void log_trace_to_stdout(const char *format, ...); +void log_info_to_stdout(const char *format, ...); +void log_attention_to_stdout(const char *format, ...); + +void vlog_trace_to_buffer(char buffer[LOG_BUFFER_SIZE], const char *format, va_list args); +void vlog_info_to_buffer(char buffer[LOG_BUFFER_SIZE], const char *format, va_list args); +void vlog_attention_to_buffer(char buffer[LOG_BUFFER_SIZE], const char *format, va_list args); diff --git a/core/salis.c b/core/salis.c index 6cc0725..99d26a2 100644 --- a/core/salis.c +++ b/core/salis.c @@ -37,10 +37,6 @@ // ---------------------------------------------------------------------------- // [section] macros // ---------------------------------------------------------------------------- -#define INST_MASK 0x7f -#define IPCM_FLAG 0x80 -#define MALL_FLAG 0x80 - #define STEP_GROUP_TIME_MIN_MS 0x400 #define STEP_GROUP_TIME_MAX_MS 0x800 @@ -50,13 +46,13 @@ struct Core g_cores[CORES]; uint64_t g_step; uint64_t g_sync; -const struct Proc g_null_proc; +static const struct Proc g_null_proc; -char g_asav_pbuf[AUTOSAVE_NAME_LEN]; -char g_evas_pbuf[EVA_SAVE_NAME_LEN]; +static char g_asav_pbuf[AUTOSAVE_NAME_LEN]; +static char g_evas_pbuf[EVA_SAVE_NAME_LEN]; -atomic_bool g_running; -thrd_t g_thrd; +static atomic_bool g_running; +static thrd_t g_thrd; // ---------------------------------------------------------------------------- // [section] memory vector functions @@ -71,10 +67,10 @@ bool mvec_is_alloc(const struct Core *core, uint64_t addr) { assert(core); #if defined(ARCH_SPEC_MVEC_LOOP) - return core->mvec[mvec_loop(addr)] & MALL_FLAG ? true : false; + return core->mvec[mvec_loop(addr)] & SALIS_MALL_FLAG ? true : false; #else if (addr < MVEC_SIZE) { - return core->mvec[addr] & MALL_FLAG ? true : false; + return core->mvec[addr] & SALIS_MALL_FLAG ? true : false; } else { return true; } @@ -92,7 +88,7 @@ void mvec_alloc(struct Core *core, uint64_t addr) { uint64_t loop_addr = addr; #endif - core->mvec[loop_addr] |= MALL_FLAG; + core->mvec[loop_addr] |= SALIS_MALL_FLAG; core->aeva.data[loop_addr]++; core->mall++; } @@ -109,33 +105,19 @@ void mvec_free(struct Core *core, uint64_t addr) { uint64_t loop_addr = addr; #endif - core->mvec[loop_addr] ^= MALL_FLAG; + core->mvec[loop_addr] ^= SALIS_MALL_FLAG; core->aeva.data[loop_addr]++; core->mall--; } -uint8_t mvec_get_byte(const struct Core *core, uint64_t addr) { - assert(core); - -#if defined(ARCH_SPEC_MVEC_LOOP) - return core->mvec[mvec_loop(addr)]; -#else - if (addr < MVEC_SIZE) { - return core->mvec[addr]; - } else { - return 0; - } -#endif -} - uint8_t mvec_get_inst(const struct Core *core, uint64_t addr) { assert(core); #if defined(ARCH_SPEC_MVEC_LOOP) - return core->mvec[mvec_loop(addr)] & INST_MASK; + return core->mvec[mvec_loop(addr)] & ARCH_INST_MASK; #else if (addr < MVEC_SIZE) { - return core->mvec[addr] & INST_MASK; + return core->mvec[addr] & ARCH_INST_MASK; } else { return 0; } @@ -144,14 +126,14 @@ uint8_t mvec_get_inst(const struct Core *core, uint64_t addr) { void mvec_set_inst(struct Core *core, uint64_t addr, uint8_t inst) { assert(core); - assert(inst < ARCH_INST_CAP); + assert(inst < ARCH_INST_COUNT); #if defined(ARCH_SPEC_MVEC_LOOP) - core->mvec[mvec_loop(addr)] &= MALL_FLAG; + core->mvec[mvec_loop(addr)] &= SALIS_MALL_FLAG; core->mvec[mvec_loop(addr)] |= inst; #else assert(addr < MVEC_SIZE); - core->mvec[addr] &= MALL_FLAG; + core->mvec[addr] &= SALIS_MALL_FLAG; core->mvec[addr] |= inst; #endif } @@ -160,7 +142,7 @@ void mvec_set_inst(struct Core *core, uint64_t addr, uint8_t inst) { void mvec_flip_bit(struct Core *core, uint64_t addr, int bit) { assert(core); assert(bit < 8); - core->mvec[addr] ^= (1 << bit) & INST_MASK; + core->mvec[addr] ^= (1 << bit) & ARCH_INST_MASK; } #endif @@ -250,7 +232,7 @@ void muta_cosmic_ray(struct Core *core) { #if defined(ARCH_SPEC_MUTA_FLIP) mvec_flip_bit(core, a, (int)(b % 8)); #else - mvec_set_inst(core, a, b & INST_MASK); + mvec_set_inst(core, a, b & ARCH_INST_MASK); #endif } } @@ -474,8 +456,8 @@ static void core_pull_ipcm(struct Core *core) { uint8_t *iinst = &core->iviv[core->ivpt]; uint64_t *iaddr = &core->ivav[core->ivpt]; - if ((*iinst & IPCM_FLAG) != 0) { - mvec_set_inst(core, *iaddr, *iinst & INST_MASK); + if ((*iinst & SALIS_IPCM_FLAG) != 0) { + mvec_set_inst(core, *iaddr, *iinst & ARCH_INST_MASK); *iinst = 0; *iaddr = 0; } @@ -487,12 +469,12 @@ static void core_pull_ipcm(struct Core *core) { void core_push_ipcm(struct Core *core, uint8_t inst, uint64_t addr) { assert(core); assert(core->ivpt < SYNC_INTERVAL); - assert((inst & IPCM_FLAG) == 0); + assert((inst & SALIS_IPCM_FLAG) == 0); uint8_t *iinst = &core->iviv[core->ivpt]; uint64_t *iaddr = &core->ivav[core->ivpt]; assert(*iinst == 0); assert(*iaddr == 0); - *iinst = inst | IPCM_FLAG; + *iinst = inst | SALIS_IPCM_FLAG; *iaddr = addr; } @@ -622,7 +604,7 @@ static void core_validate(struct Core *core) { for (uint64_t i = 0; i < SYNC_INTERVAL; i++) { uint8_t iinst = core->iviv[i]; - if ((iinst & IPCM_FLAG) == 0) { + if ((iinst & SALIS_IPCM_FLAG) == 0) { uint64_t iaddr = core->ivav[i]; assert(iinst == 0); assert(iaddr == 0); @@ -997,9 +979,11 @@ static int salis_thread(void *data) { assert(atomic_load_explicit(&g_running, memory_order_relaxed)); (void)data; +#if defined(SALIS_LOG_TIME) + uint64_t step_start = g_step; struct timespec time_start; clock_gettime(CLOCK_MONOTONIC_COARSE, &time_start); - uint64_t step_start = g_step; +#endif while (atomic_load_explicit(&g_running, memory_order_relaxed)) { log_trace("Launching core threads"); @@ -1037,18 +1021,24 @@ static int salis_thread(void *data) { salis_validate(); #endif +#if defined(SALIS_LOG_TIME) // Log simulation steps/s + // This will only trigger in situations where `salis_thread()` + // is let to run for more than 1 second. struct timespec time_end; clock_gettime(CLOCK_MONOTONIC_COARSE, &time_end); - int time_delta = time_end.tv_sec - time_start.tv_sec; + float time_delta = (float)(time_end.tv_sec - time_start.tv_sec); + time_delta += (float)(time_end.tv_nsec - time_start.tv_nsec) / 1000000000.f; - if (time_delta > 0) { + if (time_delta > 1.f) { uint64_t step_end = g_step; - uint64_t step_delta = step_end - step_start; + float step_delta = (float)(step_end - step_start); + float steps_per_sec = step_delta / time_delta; time_start = time_end; step_start = step_end; - log_info("Running simulation @%#018lx steps/s", step_delta); + log_info("Running simulation @%#018lx steps/s", (uint64_t)steps_per_sec); } +#endif } return 0; @@ -1067,6 +1057,12 @@ void salis_join(void) { thrd_join(g_thrd, NULL); } +void salis_wait(void) { + assert(!atomic_load_explicit(&g_running, memory_order_relaxed)); + log_trace("Waiting for main thread"); + thrd_join(g_thrd, NULL); +} + void salis_signal_stop(void) { assert(atomic_load_explicit(&g_running, memory_order_relaxed)); log_trace("Signaling main thread to stop"); @@ -1083,7 +1079,6 @@ void salis_step(uint64_t steps) { } g_step++; - salis_check_intervals(); } diff --git a/core/salis.h b/core/salis.h index e61752b..27adab1 100644 --- a/core/salis.h +++ b/core/salis.h @@ -16,8 +16,10 @@ // ---------------------------------------------------------------------------- // [section] macros // ---------------------------------------------------------------------------- -#define SALIS_UINT64_HALF 0x8000000000000000ul #define SALIS_EVENT_ARRAY_SIZE_COL_MARKER "_evasize_" +#define SALIS_IPCM_FLAG 0x80 +#define SALIS_MALL_FLAG 0x80 +#define SALIS_UINT64_HALF 0x8000000000000000ul // ---------------------------------------------------------------------------- // [section] structs @@ -83,6 +85,7 @@ struct Core { // ---------------------------------------------------------------------------- extern struct Core g_cores[CORES]; extern uint64_t g_step; +extern uint64_t g_sync; // ---------------------------------------------------------------------------- // [section] memory vector declarations @@ -95,7 +98,6 @@ bool mvec_is_alloc(const struct Core *core, uint64_t addr); void mvec_alloc(struct Core *core, uint64_t addr); void mvec_free(struct Core *core, uint64_t addr); -uint8_t mvec_get_byte(const struct Core *core, uint64_t addr); uint8_t mvec_get_inst(const struct Core *core, uint64_t addr); void mvec_set_inst(struct Core *core, uint64_t addr, uint8_t inst); @@ -142,6 +144,7 @@ void salis_load(void); void salis_start(void); void salis_join(void); +void salis_wait(void); void salis_signal_stop(void); void salis_step(uint64_t steps); void salis_free(void); @@ -14,7 +14,7 @@ # [section] entry-point; source options # [section] load instruction set # [section] assemble ancestor -# [section] defines +# [section] general defines # [section] build and launch # ------------------------------------------------------------------------------ @@ -175,9 +175,9 @@ logger_dll = ctypes.CDLL(logger_shared.binfile) # Pull in logging functions from C # This way there's just a single logging system to care about log = SimpleNamespace() -log.trace = lambda msg: logger_dll.log_trace(msg.encode()) -log.info = lambda msg: logger_dll.log_info(msg.encode()) -log.attention = lambda msg: logger_dll.log_attention(msg.encode()) +log.trace = lambda msg: logger_dll.log_trace_to_stdout(msg.encode()) +log.info = lambda msg: logger_dll.log_info_to_stdout(msg.encode()) +log.attention = lambda msg: logger_dll.log_attention_to_stdout(msg.encode()) logger_shared.log(log) # ------------------------------------------------------------------------------ @@ -263,14 +263,14 @@ if args.command == "load": # [section] load instruction set # ------------------------------------------------------------------------------ arch_inst_path = f"arch/{args.vm_arch}/arch_inst.c" -arch_build = Build(arch_inst_path, is_library=True) +arch_build = Build(arch_inst_path, is_library=True, flags=["-Icore"]) arch_build.build() arch_build.log(log) arch_dll = ctypes.CDLL(arch_build.binfile) -arch_dll.arch_inst_cap.restype = ctypes.c_int arch_dll.arch_inst_mnemonic.argtypes = [ctypes.c_uint8] arch_dll.arch_inst_mnemonic.restype = ctypes.c_char_p +inst_count = 0x80 # must match ARCH_INST_COUNT in core/arch_inst.h # ------------------------------------------------------------------------------ # [section] assemble ancestor @@ -286,17 +286,16 @@ with open(anc_path, "r") as f: lines = filter(lambda line: line and not line.startswith(";") and not line.isspace(), lines) lines = map(lambda line: tuple(line.split()), lines) -inst_cap = arch_dll.arch_inst_cap() -mnemo_map = {tuple(arch_dll.arch_inst_mnemonic(byte).decode().split()): byte for byte in range(inst_cap)} +mnemo_map = {tuple(arch_dll.arch_inst_mnemonic(byte).decode().split()): byte for byte in range(inst_count)} anc_bytes = [mnemo_map[line] for line in lines] anc_repr = f"{{{",".join(map(str, anc_bytes))}}}" log.info(f"Compiled ancestor file '{anc_path}' into byte array: {anc_repr}") # ------------------------------------------------------------------------------ -# [section] defines +# [section] general defines # ------------------------------------------------------------------------------ -defines = [ +general_defines = [ f"-DANC=\"{args.anc}\"", f"-DANC_BYTES={anc_repr}", f"-DANC_SIZE={len(anc_bytes)}", @@ -326,16 +325,16 @@ defines = [ # [section] build and launch # ------------------------------------------------------------------------------ ui_path = f"{args.ui_path}/{args.ui}" -ui_flags = [f"-Iarch/{args.vm_arch}", "-Icore", f"arch/{args.vm_arch}/arch.c", arch_inst_path, "core/compress.c", "core/logger.c", "core/salis.c", "core/sql.c"] -ui_defines = defines + logger_defines if not os.path.isdir(ui_path): raise RuntimeError(f"UI not found at '{ui_path}'") -with open(f"{ui_path}/links.json", "r") as f: - ui_links = json.load(f) +with open(f"{ui_path}/opts.json", "r") as f: + ui_opts = json.load(f) -ui_links += ["-lsqlite3", "-lz"] +ui_flags = ui_opts["flags"] + [f"-Iarch/{args.vm_arch}", "-Icore", f"arch/{args.vm_arch}/arch.c", arch_inst_path, "core/compress.c", "core/logger.c", "core/salis.c", "core/sql.c"] +ui_defines = ui_opts["defines"] + general_defines + logger_defines +ui_links = ui_opts["links"] + ["-lsqlite3", "-lz"] ui_build = Build(f"{ui_path}/ui.c", flags=ui_flags, defines=ui_defines, links=ui_links) ui_build.build() ui_build.log(log) diff --git a/ui/curses/opts.json b/ui/curses/opts.json new file mode 100644 index 0000000..b365aed --- /dev/null +++ b/ui/curses/opts.json @@ -0,0 +1,5 @@ +{ + "flags": [], + "defines": [], + "links": ["-lncurses"] +} diff --git a/ui/curses/ui.c b/ui/curses/ui.c new file mode 100644 index 0000000..6f49e24 --- /dev/null +++ b/ui/curses/ui.c @@ -0,0 +1,1428 @@ +// file : ui/curses/ui.c +// project : Salis-VM +// author : Paul Oliver <contact@pauloliver.dev> + +// index: +// [section] includes +// [section] macros/enums +// [section] gfx globals +// [section] tui globals +// [section] gfx functions +// [section] tui functions +// [section] process page functions +// [section] world page functions +// [section] ipc page functions +// [section] log page functions +// [section] main print functions +// [section] event functions +// [section] main functions + +// ---------------------------------------------------------------------------- +// [section] includes +// ---------------------------------------------------------------------------- +#include <assert.h> +#define NCURSES_WIDECHAR 1 +#include <curses.h> +#include <locale.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <threads.h> +#include <zlib.h> + +#include "arch.h" +#include "arch_inst.h" +#include "arch_spec.h" +#include "compress.h" +#include "logger.h" +#include "salis.h" + +// ---------------------------------------------------------------------------- +// [section] macros/enums +// ---------------------------------------------------------------------------- +#define LOG_LINE_COUNT 0x400 + +#define NCURSES_CTRL(x) (x & 0x1f) + +#define PANE_WIDTH 27 +#define PANE_WIDTH_AND_MARGIN (PANE_WIDTH + 2) +#define PROC_FIELD_WIDTH 21 +#define PROC_PAGE_LINES 12 + +#define FPS_MIN 10 +#define FPS_MAX 20 + +enum UIPage { + PAGE_CORE, + PAGE_PROCESS, + PAGE_WORLD, + PAGE_IPC, + PAGE_LOG, + PAGE_COUNT, +}; + +enum UIColorPair { + PAIR_NORMAL, + PAIR_HEADER, + PAIR_LIVE_PROC, + PAIR_SELECTED_PROC, + PAIR_FREE_CELL, + PAIR_ALLOC_CELL, + PAIR_MEM_BLOCK_START, + PAIR_POINTERS, + PAIR_SELECTED_MB1, + PAIR_SELECTED_MB2, + PAIR_SELECTED_IP, + PAIR_SELECTED_SP, + PAIR_LOG_TRACE, + PAIR_LOG_INFO, + PAIR_LOG_ATTENTION, +}; + +enum UILogLevel { + TRACE, + INFO, + ATTENTION, +}; + +// ---------------------------------------------------------------------------- +// [section] gfx globals +// ---------------------------------------------------------------------------- +static uint64_t g_gfx_vsiz; +static uint64_t *g_gfx_inst; +static uint64_t *g_gfx_mall; +static uint64_t *g_gfx_mbst; +static uint64_t *g_gfx_ptrs; +static uint64_t *g_gfx_mb0s; +static uint64_t *g_gfx_mb1s; +static uint64_t *g_gfx_ipas; +static uint64_t *g_gfx_spas; + +static const wchar_t *g_zoomed_symbols = ( + L"⢀⢁⢂⢃⢄⢅⢆⢇⣀⣁⣂⣃⣄⣅⣆⣇⢈⢉⢊⢋⢌⢍⢎⢏⣈⣉⣊⣋⣌⣍⣎⣏⢐⢑⢒⢓⢔⢕⢖⢗⣐⣑⣒⣓⣔⣕⣖⣗⢘⢙⢚⢛⢜⢝⢞⢟⣘⣙⣚⣛⣜⣝⣞⣟" + L"⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯⢰⢱⢲⢳⢴⢵⢶⢷⣰⣱⣲⣳⣴⣵⣶⣷⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿" +); + +// ---------------------------------------------------------------------------- +// [section] tui globals +// ---------------------------------------------------------------------------- +static bool g_exit; +static bool g_running; +static unsigned g_core; +static unsigned g_page; +static char *g_line_buff; +static bool g_proc_genes; +static uint64_t g_proc_scroll; +static uint64_t g_proc_field_scroll; +static uint64_t g_proc_gene_scroll; +static uint64_t g_proc_selected; +static uint64_t g_wrld_pos; +static uint64_t g_wrld_zoom; +static bool g_wcursor_mode; +static int g_wcursor_x; +static int g_wcursor_y; +static uint64_t g_wcursor_pointed; +static uint64_t g_log_cnt; +static unsigned g_log_ptr; +static unsigned g_log_scroll; +static mtx_t g_log_mutex; +static enum UILogLevel g_log_levels[LOG_LINE_COUNT]; +static char g_logs[LOG_LINE_COUNT][LOG_BUFFER_SIZE]; +static uint64_t g_vlin; +static uint64_t g_vsiz; +static uint64_t g_vlin_rng; +static uint64_t g_vsiz_rng; +static uint64_t g_ivpt_scroll; +static uint64_t g_step_block; +static float g_steps_per_sec; + +// ---------------------------------------------------------------------------- +// [section] gfx functions +// ---------------------------------------------------------------------------- +static 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_ptrs = 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_ptrs); + assert(g_gfx_mb0s); + assert(g_gfx_mb1s); + assert(g_gfx_ipas); + assert(g_gfx_spas); +} + +static void gfx_free(void) { + if (g_gfx_vsiz == 0) { + return; + } + + assert(g_gfx_inst); + assert(g_gfx_mall); + assert(g_gfx_mbst); + assert(g_gfx_ptrs); + 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_ptrs); + 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_ptrs = NULL; + g_gfx_mb0s = NULL; + g_gfx_mb1s = NULL; + g_gfx_ipas = NULL; + g_gfx_spas = NULL; +} + +static void gfx_resize(uint64_t vsiz) { + assert(vsiz); + gfx_free(); + gfx_init(vsiz); +} + +static 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_inst(core, addr); + g_gfx_mall[i] += mvec_is_alloc(core, addr) ? 1 : 0; + } + } +} + +static void gfx_clear_array(uint64_t *arry) { + assert(arry); + memset(arry, 0, g_gfx_vsiz * sizeof(uint64_t)); +} + +#if defined(ARCH_SPEC_MVEC_LOOP) +static 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 !defined(NDEBUG) + 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 !defined(NDEBUG) + inc_cnt++; +#endif + } + + pix_mod += MVEC_SIZE; + } + +#if !defined(NDEBUG) + if (zoom != 1) { + assert(inc_cnt <= 2); + } +#endif +} +#else +static 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 + +static 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_get_mb0_addr(core, pix); + uint64_t mb1a = arch_proc_get_mb1_addr(core, pix); + gfx_accumulate_pixel(pos, zoom, mb0a, g_gfx_mbst); + + if (arch_proc_get_mb1_size(core, pix)) { + gfx_accumulate_pixel(pos, zoom, mb1a, g_gfx_mbst); + } + } +} + +static void gfx_render_ptrs(const struct Core *core, uint64_t pos, uint64_t zoom) { + assert(core); + gfx_clear_array(g_gfx_ptrs); + + for (uint64_t pix = core->pfst; pix <= core->plst; pix++) { + uint64_t ipa = arch_proc_get_ip_addr(core, pix); + uint64_t spa = arch_proc_get_sp_addr(core, pix); + gfx_accumulate_pixel(pos, zoom, ipa, g_gfx_ptrs); + gfx_accumulate_pixel(pos, zoom, spa, g_gfx_ptrs); + } +} + +static 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_get_mb0_addr(core, psel); + uint64_t mb0s = arch_proc_get_mb0_size(core, psel); + + for (uint64_t i = 0; i < mb0s; i++) { + gfx_accumulate_pixel(pos, zoom, mb0a + i, g_gfx_mb0s); + } +} + +static 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_get_mb1_addr(core, psel); + uint64_t mb1s = arch_proc_get_mb1_size(core, psel); + + for (uint64_t i = 0; i < mb1s; i++) { + gfx_accumulate_pixel(pos, zoom, mb1a + i, g_gfx_mb1s); + } +} + +static 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; + } + + gfx_accumulate_pixel(pos, zoom, arch_proc_get_ip_addr(core, psel), g_gfx_ipas); +} + +static 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; + } + + gfx_accumulate_pixel(pos, zoom, arch_proc_get_sp_addr(core, psel), g_gfx_spas); +} + +static 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_ptrs(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); +} + +// ---------------------------------------------------------------------------- +// [section] tui functions +// ---------------------------------------------------------------------------- +static void tui_line_buff_free(void) { + if (g_line_buff) { + free(g_line_buff); + } + + g_line_buff = NULL; +} + +static void tui_line_buff_resize(void) { + tui_line_buff_free(); + g_line_buff = calloc(COLS + 1, sizeof(char)); +} + +static void tui_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; + va_start(args, format); + attron(COLOR_PAIR(color) | attr); + vsnprintf(g_line_buff, COLS, format, args); + mvprintw(line, 1, "%s", g_line_buff); + attroff(COLOR_PAIR(color) | attr); + va_end(args); +} + +static void tui_clear_line(int l) { + tui_line(true, l, PAIR_NORMAL, A_NORMAL, ""); +} + +static void tui_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; + va_start(args, format); + attron(COLOR_PAIR(color) | attr); + vsnprintf(g_line_buff, COLS - col, format, args); + mvprintw(line, col, "%s", g_line_buff); + attroff(COLOR_PAIR(color) | attr); + va_end(args); +} + +static void tui_str_field(int l, const char *label, const char *value) { + assert(label); + assert(strlen(label) <= 4); + assert(value); + tui_line(false, l, PAIR_NORMAL, A_NORMAL, "%-4s : %18s", label, value); +} + +static void tui_ulx_field(int l, const char *label, uint64_t value) { + assert(label); + assert(strlen(label) <= 4); + tui_line(false, l, PAIR_NORMAL, A_NORMAL, "%-4s : %#18lx", label, value); +} + +static void tui_print_core(int l) { + tui_line(false, ++l, PAIR_HEADER, A_BOLD, "CORE [%d]", g_core); + tui_ulx_field(++l, "cycl", g_cores[g_core].cycl); + tui_ulx_field(++l, "mall", g_cores[g_core].mall); + tui_ulx_field(++l, "mut0", g_cores[g_core].muta[0]); + tui_ulx_field(++l, "mut1", g_cores[g_core].muta[1]); + tui_ulx_field(++l, "mut2", g_cores[g_core].muta[2]); + tui_ulx_field(++l, "mut3", g_cores[g_core].muta[3]); + tui_ulx_field(++l, "pnum", g_cores[g_core].pnum); + tui_ulx_field(++l, "pcap", g_cores[g_core].pcap); + tui_ulx_field(++l, "pfst", g_cores[g_core].pfst); + tui_ulx_field(++l, "plst", g_cores[g_core].plst); + tui_ulx_field(++l, "pcur", g_cores[g_core].pcur); + tui_ulx_field(++l, "psli", g_cores[g_core].psli); + tui_ulx_field(++l, "ivpt", g_cores[g_core].ivpt); +} + +// ---------------------------------------------------------------------------- +// [section] process page functions +// ---------------------------------------------------------------------------- +static int tui_proc_pair(uint64_t pix) { + if (pix == g_proc_selected) { + return PAIR_SELECTED_PROC; + } + + if (mvec_proc_is_live(&g_cores[g_core], pix)) { + return PAIR_LIVE_PROC; + } + + return PAIR_NORMAL; +} + +static const char *tui_proc_state(uint64_t pix) { + return mvec_proc_is_live(&g_cores[g_core], pix) ? "live" : "dead"; +} + +static void tui_print_process_genome_header(int l) { + tui_line(false, l++, PAIR_NORMAL, A_NORMAL, "%s : %18s : %s", "stat", "pix", "genome"); +} + +static void tui_print_process_gene(int l, int gcol, uint64_t gidx, uint64_t mba, uint64_t pix, int pair) { + assert(gcol >= PANE_WIDTH_AND_MARGIN); + 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 inst = mvec_get_inst(core, addr); + wchar_t gsym[2] = { arch_inst_symbol(inst), L'\0' }; + cchar_t cchar = { 0 }; + int pair_cell; + + if (arch_proc_get_ip_addr(core, pix) == addr) { + pair_cell = PAIR_SELECTED_IP; + } else if (arch_proc_get_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); +} + +static void tui_print_process_genes(int l, uint64_t pix) { + tui_line(true, l, tui_proc_pair(pix), A_NORMAL, "%s : %#18lx :", tui_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_AND_MARGIN; + int gcol = scol - g_proc_gene_scroll; + uint64_t mb0a = arch_proc_get_mb0_addr(core, pix); + uint64_t mb0s = arch_proc_get_mb0_size(core, pix); + uint64_t mb1a = arch_proc_get_mb1_addr(core, pix); + uint64_t mb1s = arch_proc_get_mb1_size(core, pix); + + for (uint64_t gidx = 0; gidx < mb0s && gcol < COLS; ++gidx, ++gcol) { + if (gcol >= scol) { + tui_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) { + tui_print_process_gene(l, gcol, gidx, mb1a, pix, PAIR_SELECTED_MB2); + } + } + + clrtoeol(); +} + +static void tui_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; + tui_field(l, fcol, PAIR_NORMAL, A_NORMAL, " : %18s", name); +} + +static void tui_print_process_field_header(int l) { + tui_line(true, l, PAIR_NORMAL, A_NORMAL, "%s : %18s", "stat", "pix"); + int fidx = 0; + +#define ARCH_SPEC_PROC_FIELD(type, name) tui_print_process_field_header_element(l, fidx++, #name); + ARCH_SPEC_PROC_FIELDS +#undef ARCH_SPEC_PROC_FIELD +} + +static void tui_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; + tui_field(l, fcol, fclr, A_NORMAL, " : %#18lx", field); +} + +static void tui_print_process_fields(int l, uint64_t pix) { + tui_line(true, l, tui_proc_pair(pix), A_NORMAL, "%s : %#18lx", tui_proc_state(pix), pix); + const struct Proc *proc = proc_get(&g_cores[g_core], pix); + int fidx = 0; + int fclr = tui_proc_pair(pix); + +#define ARCH_SPEC_PROC_FIELD(type, name) tui_print_process_field_element(l, fidx++, fclr, proc->name); + ARCH_SPEC_PROC_FIELDS +#undef ARCH_SPEC_PROC_FIELD +} + +static void tui_print_process(int l) { + l++; + + tui_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) { + tui_print_process_genome_header(l++); + + while (l < LINES) { + tui_print_process_genes(l++, pix++); + } + + return; + } else { + tui_print_process_field_header(l++); + + while (l < LINES) { + tui_print_process_fields(l++, pix++); + } + } +} + +// ---------------------------------------------------------------------------- +// [section] world page functions +// ---------------------------------------------------------------------------- +static void tui_world_resize(void) { + 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 defined(ARCH_SPEC_MVEC_LOOP) +static void tui_print_cell(uint64_t i, uint64_t r, uint64_t x, uint64_t y) { +#else +static void tui_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' }; + uint64_t inst_avrg = g_gfx_inst[i] / g_wrld_zoom; + cchar_t cchar = { 0 }; + assert(inst_avrg < ARCH_INST_COUNT); + +#if !defined(ARCH_SPEC_MVEC_LOOP) + if (a >= MVEC_SIZE) { + inst_nstr[0] = L' '; + } else +#endif + if (g_wrld_zoom == 1) { + inst_nstr[0] = arch_inst_symbol((uint8_t)inst_avrg); + } else { + inst_nstr[0] = g_zoomed_symbols[(uint8_t)inst_avrg]; + } + + int pair_cell; + +#if defined(ARCH_SPEC_MVEC_LOOP) + if (g_wcursor_mode && r == (uint64_t)g_wcursor_x && y == (uint64_t)g_wcursor_y) { +#else + if ((g_wcursor_mode && r == (uint64_t)g_wcursor_x && y == (uint64_t)g_wcursor_y) || a >= MVEC_SIZE) { +#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_ptrs[i] != 0) { + pair_cell = PAIR_POINTERS; + } 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); +} + +static void tui_print_wcursor_bar(void) { + tui_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 cinst = mvec_get_inst(core, caddr); + + if (caddr < MVEC_SIZE && 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_inst_mnemonic(cinst), + cownr + ); +} + +static void tui_print_world(int l) { + l++; + tui_line(false, l++, PAIR_HEADER, A_BOLD, "WORLD"); + tui_ulx_field(l++, "wrlp", g_wrld_pos); + tui_ulx_field(l++, "wrlz", g_wrld_zoom); + tui_ulx_field(l++, "psel", g_proc_selected); + tui_ulx_field(l++, "pabs", g_proc_selected % g_cores[g_core].pcap); + tui_ulx_field(l++, "vrng", g_vsiz_rng); + tui_str_field(l++, "curs", g_wcursor_mode ? "on" : "off"); + + l++; + tui_line(false, l++, PAIR_HEADER, A_BOLD, "SELECTED"); + const struct Proc *psel = proc_get(&g_cores[g_core], g_proc_selected); + +#define ARCH_SPEC_PROC_FIELD(type, name) tui_ulx_field(l++, #name, psel->name); + ARCH_SPEC_PROC_FIELDS +#undef ARCH_SPEC_PROC_FIELD + + 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 defined(ARCH_SPEC_MVEC_LOOP) + tui_print_cell(i, r, x, y); +#else + uint64_t a = g_wrld_pos + (i * g_wrld_zoom); + tui_print_cell(i, r, x, y, a); +#endif + } + + if (g_wcursor_mode) { + tui_print_wcursor_bar(); + } +} + +// ---------------------------------------------------------------------------- +// [section] ipc page functions +// ---------------------------------------------------------------------------- +static void tui_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]; + tui_field(l, PANE_WIDTH, color, A_NORMAL, "%#18x : %#18x : %#18x", i, iinst, iaddr); +} + +static void tui_print_ipc_data(void) { + tui_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) { + tui_print_ipc_field(l++, i, PAIR_SELECTED_PROC); + } + + continue; + } + + uint8_t iinst = g_cores[g_core].iviv[i]; + + if ((iinst & SALIS_IPCM_FLAG) != 0) { + if (l >= 1) { + tui_print_ipc_field(l++, i, PAIR_LIVE_PROC); + } + + continue; + } + } + + for (; l < LINES; ++l) { + if (l >= 1) { + move(l, PANE_WIDTH); + clrtoeol(); + } + } +} + +static void tui_print_ipc(int l) { + l++; + const struct Core *core = &g_cores[g_core]; + tui_line(true, l++, PAIR_HEADER, A_BOLD, "IPC [%#lx]", g_ivpt_scroll); + tui_ulx_field(l++, "ivpt", core->ivpt); + tui_ulx_field(l++, "ivpi", core->iviv[core->ivpt]); + tui_ulx_field(l++, "ivpa", core->ivav[core->ivpt]); + tui_print_ipc_data(); +} + +// ---------------------------------------------------------------------------- +// [section] log page functions +// ---------------------------------------------------------------------------- +static void tui_trace_impl(const char *format, ...) { + assert(format); + (void)format; + +#if defined(LOG_TRACE) + va_list args; + va_start(args, format); + mtx_lock(&g_log_mutex); + vlog_trace_to_buffer(g_logs[g_log_ptr], format, args); + mtx_unlock(&g_log_mutex); + va_end(args); + g_log_levels[g_log_ptr] = TRACE; + g_log_cnt++; + g_log_ptr = (g_log_ptr + 1) % LOG_LINE_COUNT; +#endif +} + +static void tui_info_impl(const char *format, ...) { + assert(format); + va_list args; + va_start(args, format); + mtx_lock(&g_log_mutex); + vlog_info_to_buffer(g_logs[g_log_ptr], format, args); + mtx_unlock(&g_log_mutex); + va_end(args); + g_log_levels[g_log_ptr] = INFO; + g_log_cnt++; + g_log_ptr = (g_log_ptr + 1) % LOG_LINE_COUNT; +} + +static void tui_attention_impl(const char *format, ...) { + assert(format); + va_list args; + va_start(args, format); + mtx_lock(&g_log_mutex); + vlog_attention_to_buffer(g_logs[g_log_ptr], format, args); + mtx_unlock(&g_log_mutex); + va_end(args); + g_log_levels[g_log_ptr] = ATTENTION; + g_log_cnt++; + g_log_ptr = (g_log_ptr + 1) % LOG_LINE_COUNT; +} + +static void tui_clear_log_line(int line) { + assert(line >= 0 && line < LINES); + move(line, PANE_WIDTH); + clrtoeol(); +} + +static void tui_print_log_line(unsigned lptr, int line) { + assert(lptr < LOG_LINE_COUNT); + assert(line >= 0 && line < LINES); + tui_clear_log_line(line); + + if (strlen(g_logs[lptr])) { + int pair = 0; + + switch (g_log_levels[lptr]) { + case TRACE: + pair = PAIR_LOG_TRACE; + break; + case INFO: + pair = PAIR_LOG_INFO; + break; + case ATTENTION: + pair = PAIR_LOG_ATTENTION; + break; + default: + assert(false); + } + + tui_field(line, PANE_WIDTH_AND_MARGIN, pair, A_NORMAL, g_logs[lptr]); + } +} + +static void tui_print_log(int l) { + l++; + tui_line(true, l++, PAIR_HEADER, A_BOLD, "LOG"); + tui_ulx_field(l++, "lscr", g_log_scroll); + tui_ulx_field(l++, "lcnt", g_log_cnt); + tui_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) { + tui_print_log_line(lptr, line); + } + + if (lptr == g_log_ptr) { + break; + } + } + + while (line) { + line--; + tui_clear_log_line(line); + } +} + +// ---------------------------------------------------------------------------- +// [section] main print functions +// ---------------------------------------------------------------------------- +static void tui_print(void) { + int l = 1; + tui_line(false, l++, PAIR_HEADER, A_BOLD, "SALIS [%d:%d]", g_core, CORES); + tui_str_field(l++, "name", NAME); + tui_ulx_field(l++, "seed", SEED); + tui_str_field(l++, "anc", ANC); + tui_str_field(l++, "arch", ARCH); + tui_ulx_field(l++, "asav", AUTOSAVE_INTERVAL); +#if defined(ARCH_SPEC_MUTA_FLIP) + tui_str_field(l++, "mflp", "yes"); +#else + tui_str_field(l++, "mflp", "no"); +#endif + tui_ulx_field(l++, "mrng", MUTA_RANGE); + tui_ulx_field(l++, "size", MVEC_SIZE); +#if defined(ARCH_SPEC_MVEC_LOOP) + tui_str_field(l++, "loop", "true"); +#else + tui_str_field(l++, "loop", "false"); +#endif + tui_ulx_field(l++, "syni", SYNC_INTERVAL); +#if defined(DATA_PUSH_INTERVAL) + tui_ulx_field(l++, "dpsi", DATA_PUSH_INTERVAL); +#endif + tui_ulx_field(l++, "step", g_step); + tui_ulx_field(l++, "sync", g_sync); + tui_ulx_field(l++, "step", g_step_block); + tui_ulx_field(l++, "stps", (uint64_t)g_steps_per_sec); + + switch (g_page) { + case PAGE_CORE: + tui_print_core(l); + break; + case PAGE_PROCESS: + tui_print_process(l); + break; + case PAGE_WORLD: + tui_print_world(l); + break; + case PAGE_IPC: + tui_print_ipc(l); + break; + case PAGE_LOG: + tui_print_log(l); + break; + default: + break; + } +} + +// ---------------------------------------------------------------------------- +// [section] event functions +// ---------------------------------------------------------------------------- +static 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': +#if defined(ARCH_SPEC_MVEC_LOOP) + g_wrld_pos -= g_vsiz_rng; +#else + if (g_wrld_pos < g_vsiz_rng) { + g_wrld_pos = 0; + } else { + g_wrld_pos -= g_vsiz_rng; + } +#endif + break; + case 'w': + g_wrld_pos += g_vlin_rng; + break; + case 's': +#if defined(ARCH_SPEC_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; + } +} + +static 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 defined(ARCH_SPEC_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; + } +} + +static 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; + tui_world_resize(); + break; + case 'z': + g_wrld_zoom /= (g_wrld_zoom != 1) ? 2 : 1; + tui_world_resize(); + break; + default: + break; + } + + break; + default: + break; + } +} + +static 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; + } +} + +static 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; + } +} + +static void ev_goto_sel_proc(void) { + 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; + } +} + +static void ev_handle(void) { + 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 NCURSES_CTRL('c'): + g_exit = true; + break; + case KEY_SLEFT: + clear(); + g_core = (g_core + CORES - 1) % CORES; + break; + case KEY_SRIGHT: + clear(); + g_core = (g_core + 1) % 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(); + tui_line_buff_resize(); + tui_world_resize(); + + if (g_vlin) { + while (g_vsiz_rng >= MVEC_SIZE * 2 && g_wrld_zoom != 1) { + g_wrld_zoom /= 2; + tui_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; + g_steps_per_sec = 0.f; + 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; + } +} + +// ---------------------------------------------------------------------------- +// [section] main functions +// ---------------------------------------------------------------------------- +static void init(void) { + 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_LIVE_PROC, COLOR_BLUE, COLOR_BLACK); + init_pair(PAIR_SELECTED_PROC, COLOR_YELLOW, COLOR_BLACK); + init_pair(PAIR_FREE_CELL, COLOR_BLUE, COLOR_BLACK); + init_pair(PAIR_ALLOC_CELL, COLOR_BLACK, COLOR_BLUE); + init_pair(PAIR_MEM_BLOCK_START, COLOR_BLACK, COLOR_CYAN); + init_pair(PAIR_POINTERS, 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); + init_pair(PAIR_LOG_TRACE, COLOR_BLUE, COLOR_BLACK); + init_pair(PAIR_LOG_INFO, COLOR_GREEN, COLOR_BLACK); + init_pair(PAIR_LOG_ATTENTION, COLOR_YELLOW, COLOR_BLACK); + + log_trace = tui_trace_impl; + log_info = tui_info_impl; + log_attention = tui_attention_impl; + +#if defined(COMMAND_NEW) + salis_init(); +#elif defined(COMMAND_LOAD) + salis_load(); +#endif + + g_wrld_zoom = 1; + g_step_block = 1; + tui_line_buff_resize(); + tui_world_resize(); + mtx_init(&g_log_mutex, mtx_plain); +} + +static void exec(void) { + struct timespec ts = { + .tv_sec = 0, + .tv_nsec = (1000000000 / 60), + }; + + while (!g_exit) { + if (g_running) { + uint64_t step_start = g_step; + struct timespec time_start; + clock_gettime(CLOCK_MONOTONIC, &time_start); + + salis_start(); + nanosleep(&ts, NULL); + salis_signal_stop(); + salis_wait(); + + uint64_t step_end = g_step; + struct timespec time_end; + clock_gettime(CLOCK_MONOTONIC, &time_end); + + float step_delta = (float)(step_end - step_start); + float time_delta = (float)(time_end.tv_sec - time_start.tv_sec); + time_delta += (float)(time_end.tv_nsec - time_start.tv_nsec) / 1000000000.f; + g_steps_per_sec = step_delta / time_delta; + } + + tui_print(); + ev_handle(); + } +} + +static void quit(void) { + gfx_free(); + mtx_destroy(&g_log_mutex); + tui_line_buff_free(); + salis_free(); + endwin(); +} + +int main(void) { + init(); + exec(); + quit(); + return 0; +} diff --git a/ui/daemon/links.json b/ui/daemon/links.json deleted file mode 100644 index fe51488..0000000 --- a/ui/daemon/links.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/ui/daemon/opts.json b/ui/daemon/opts.json new file mode 100644 index 0000000..314852f --- /dev/null +++ b/ui/daemon/opts.json @@ -0,0 +1,5 @@ +{ + "flags": [], + "defines": ["-DSALIS_LOG_TIME"], + "links": [] +} |
