summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Oliver <contact@pauloliver.dev>2026-07-31 04:04:37 +0200
committerPaul Oliver <contact@pauloliver.dev>2026-08-05 00:02:22 +0200
commit85a58f61ed0dd0b40a612d25ce966255c57efe3a (patch)
tree54862e397cfea8f5a32779a6674156912b46c48f
parent8a4563d6d0c9e81fdaa42f6f760cae2b49cad431 (diff)
Adds curses UIHEADmaster
-rw-r--r--arch/null/arch_inst.c15
-rw-r--r--arch/null/arch_inst.h21
-rw-r--r--arch/null/arch_spec.h2
-rw-r--r--core/arch_inst.h11
-rw-r--r--core/logger.c69
-rw-r--r--core/logger.h16
-rw-r--r--core/salis.c85
-rw-r--r--core/salis.h7
-rwxr-xr-xsalis.py29
-rw-r--r--ui/curses/opts.json5
-rw-r--r--ui/curses/ui.c1428
-rw-r--r--ui/daemon/links.json1
-rw-r--r--ui/daemon/opts.json5
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);
diff --git a/salis.py b/salis.py
index 0497ee4..e13af4c 100755
--- a/salis.py
+++ b/salis.py
@@ -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": []
+}