From 5daf52d92c472ebf2a675cb2d27ca3e3fbdf0034 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Thu, 29 Feb 2024 02:39:32 +0100 Subject: Initial --- src/arch/dummy.c | 147 +++++++++ src/bench.c | 49 +++ src/graphics.c | 223 +++++++++++++ src/salis.c | 661 ++++++++++++++++++++++++++++++++++++++ src/ui/curses.c | 944 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ui/daemon.c | 63 ++++ 6 files changed, 2087 insertions(+) create mode 100644 src/arch/dummy.c create mode 100644 src/bench.c create mode 100644 src/graphics.c create mode 100644 src/salis.c create mode 100644 src/ui/curses.c create mode 100644 src/ui/daemon.c (limited to 'src') diff --git a/src/arch/dummy.c b/src/arch/dummy.c new file mode 100644 index 0000000..b440be3 --- /dev/null +++ b/src/arch/dummy.c @@ -0,0 +1,147 @@ +// Project: Salis +// Author: Paul Oliver +// Email: contact@pauloliver.dev + +/* + * Defines a minimal viable architecture for the Salis VM. Useful for + * debugging and benchmarking. Also, this file can be used as a template when + * implementing a real architecture. + */ + +bool proc_is_live(const Core *core, u64 pix); + +#define PROC_FIELDS \ + PROC_FIELD(u64, dmmy) + +struct Proc { +#define PROC_FIELD(type, name) type name; + PROC_FIELDS +#undef PROC_FIELD +}; + +#define MNEMONIC_BUFF_SIZE (0x10) + +const wchar_t *g_arch_byte_symbols = ( + L"⠀⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇⠈⠉⠊⠋⠌⠍⠎⠏⡈⡉⡊⡋⡌⡍⡎⡏⠐⠑⠒⠓⠔⠕⠖⠗⡐⡑⡒⡓⡔⡕⡖⡗⠘⠙⠚⠛⠜⠝⠞⠟⡘⡙⡚⡛⡜⡝⡞⡟" + L"⠠⠡⠢⠣⠤⠥⠦⠧⡠⡡⡢⡣⡤⡥⡦⡧⠨⠩⠪⠫⠬⠭⠮⠯⡨⡩⡪⡫⡬⡭⡮⡯⠰⠱⠲⠳⠴⠵⠶⠷⡰⡱⡲⡳⡴⡵⡶⡷⠸⠹⠺⠻⠼⠽⠾⠿⡸⡹⡺⡻⡼⡽⡾⡿" + L"⢀⢁⢂⢃⢄⢅⢆⢇⣀⣁⣂⣃⣄⣅⣆⣇⢈⢉⢊⢋⢌⢍⢎⢏⣈⣉⣊⣋⣌⣍⣎⣏⢐⢑⢒⢓⢔⢕⢖⢗⣐⣑⣒⣓⣔⣕⣖⣗⢘⢙⢚⢛⢜⢝⢞⢟⣘⣙⣚⣛⣜⣝⣞⣟" + L"⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯⢰⢱⢲⢳⢴⢵⢶⢷⣰⣱⣲⣳⣴⣵⣶⣷⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿" +); + +u64 arch_proc_mb0_addr(const Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + (void)core; + (void)pix; + + return 0; +} + +u64 arch_proc_mb0_size(const Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + (void)core; + (void)pix; + + return 0; +} + +u64 arch_proc_mb1_addr(const Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + (void)core; + (void)pix; + + return 0; +} + +u64 arch_proc_mb1_size(const Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + (void)core; + (void)pix; + + return 0; +} + +u64 arch_proc_ip_addr(const Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + (void)core; + (void)pix; + + return 0; +} + +u64 arch_proc_sp_addr(const Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + (void)core; + (void)pix; + + return 0; +} + +u64 arch_proc_slice(const Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + (void)core; + (void)pix; + + return 1; +} + +void arch_on_proc_kill(Core *core) { + assert(core); + assert(core->pnum > 1); + + (void)core; +} + +#if ACTION == ACT_BENCH || ACTION == ACT_NEW +void arch_anc_init(Core *core, u64 size) { + assert(core); + + (void)core; + (void)size; +} +#endif + +void arch_proc_step(Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + (void)core; + (void)pix; + + return; +} + +#ifndef NDEBUG +void arch_validate_proc(const Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + (void)core; + (void)pix; + + assert(true); +} +#endif + +wchar_t arch_symbol(u8 inst) { + return g_arch_byte_symbols[inst]; +} + +void arch_mnemonic(u8 inst, char *buff) { + assert(buff); + + snprintf(buff, MNEMONIC_BUFF_SIZE, "dummy %#x", inst); +} diff --git a/src/bench.c b/src/bench.c new file mode 100644 index 0000000..eb7e0e5 --- /dev/null +++ b/src/bench.c @@ -0,0 +1,49 @@ +// Project: Salis +// Author: Paul Oliver +// Email: contact@pauloliver.dev + +/* + * Simple benchmark test helps measure simulation speed by stepping the + * simulator N times and printing results. + */ + +#if ACTION != ACT_BENCH +#error Using bench UI with unsupported action +#endif + +int main() { + printf("Salis Benchmark Test\n\n"); + + salis_init("", SEED); + salis_step(BENCH_STEPS); + + printf("seed => %#lx\n", SEED); + printf("g_steps => %#lx\n", g_steps); + printf("g_syncs => %#lx\n", g_syncs); + + for (int i = 0; i < CORE_COUNT; ++i) { + putchar('\n'); + printf("core %d mall => %#lx\n", i, g_cores[i].mall); + printf("core %d mut0 => %#lx\n", i, g_cores[i].muta[0]); + printf("core %d mut1 => %#lx\n", i, g_cores[i].muta[1]); + printf("core %d mut2 => %#lx\n", i, g_cores[i].muta[2]); + printf("core %d mut3 => %#lx\n", i, g_cores[i].muta[3]); + printf("core %d pnum => %#lx\n", i, g_cores[i].pnum); + printf("core %d pcap => %#lx\n", i, g_cores[i].pcap); + printf("core %d pfst => %#lx\n", i, g_cores[i].pfst); + printf("core %d plst => %#lx\n", i, g_cores[i].plst); + printf("core %d pcur => %#lx\n", i, g_cores[i].pcur); + printf("core %d psli => %#lx\n", i, g_cores[i].psli); + printf("core %d ncyc => %#lx\n", i, g_cores[i].ncyc); + printf("core %d ivpt => %#lx\n", i, g_cores[i].ivpt); + putchar('\n'); + + for (int j = 0; j < 32; ++j) { + printf("%02x ", g_cores[i].mvec[j]); + } + + putchar('\n'); + } + + salis_free(); +} diff --git a/src/graphics.c b/src/graphics.c new file mode 100644 index 0000000..8114f30 --- /dev/null +++ b/src/graphics.c @@ -0,0 +1,223 @@ +// Project: Salis +// Author: Paul Oliver +// Email: contact@pauloliver.dev + +/* + * This module renders the contents of the VM memory buffer into a 7 channel + * image. It supports zooming in and out, condensing the state of several + * bytes of memory into single pixels, when zoomed out. When zoomed in, each + * pixel represents a single byte in memory. + */ + +u64 g_gfx_vsiz; // zoom level + +u64 *g_gfx_inst; // instruction channel +u64 *g_gfx_mall; // allocated state channel +u64 *g_gfx_mbst; // memory block start channel +u64 *g_gfx_mb0s; // selected organism's memory block #1 channel +u64 *g_gfx_mb1s; // selected organism's memory block #2 channel +u64 *g_gfx_ipas; // selected organism's IP channel +u64 *g_gfx_spas; // selected organism's SP channel + +void gfx_init(u64 vsiz) { + assert(vsiz); + + g_gfx_vsiz = vsiz; + + g_gfx_inst = calloc(g_gfx_vsiz, sizeof(u64)); + g_gfx_mall = calloc(g_gfx_vsiz, sizeof(u64)); + g_gfx_mbst = calloc(g_gfx_vsiz, sizeof(u64)); + g_gfx_mb0s = calloc(g_gfx_vsiz, sizeof(u64)); + g_gfx_mb1s = calloc(g_gfx_vsiz, sizeof(u64)); + g_gfx_ipas = calloc(g_gfx_vsiz, sizeof(u64)); + g_gfx_spas = calloc(g_gfx_vsiz, sizeof(u64)); + + assert(g_gfx_inst); + assert(g_gfx_mall); + assert(g_gfx_mbst); + assert(g_gfx_mb0s); + assert(g_gfx_mb1s); + assert(g_gfx_ipas); + assert(g_gfx_spas); +} + +void gfx_free() { + if (g_gfx_vsiz == 0) { + return; + } + + assert(g_gfx_inst); + assert(g_gfx_mall); + assert(g_gfx_mbst); + assert(g_gfx_mb0s); + assert(g_gfx_mb1s); + assert(g_gfx_ipas); + assert(g_gfx_spas); + + g_gfx_vsiz = 0; + + free(g_gfx_inst); + free(g_gfx_mall); + free(g_gfx_mbst); + free(g_gfx_mb0s); + free(g_gfx_mb1s); + free(g_gfx_ipas); + free(g_gfx_spas); + + g_gfx_inst = NULL; + g_gfx_mall = NULL; + g_gfx_mbst = NULL; + g_gfx_mb0s = NULL; + g_gfx_mb1s = NULL; + g_gfx_ipas = NULL; + g_gfx_spas = NULL; +} + +void gfx_resize(u64 vsiz) { + assert(vsiz); + + gfx_free(); + gfx_init(vsiz); +} + +void gfx_render_inst(const Core *core, u64 pos, u64 zoom) { + assert(core); + + for (u64 i = 0; i < g_gfx_vsiz; ++i) { + g_gfx_inst[i] = 0; + g_gfx_mall[i] = 0; + + for (u64 j = 0; j < zoom; ++j) { + u64 addr = pos + (i * zoom) + j; + u8 byte = mvec_get_byte(core, addr); + + g_gfx_inst[i] += byte; + g_gfx_mall[i] += (byte & MALL_FLAG) ? 1 : 0; + } + } +} + +void gfx_clear_array(u64 *arry) { + assert(arry); + memset(arry, 0, g_gfx_vsiz * sizeof(u64)); +} + +void gfx_accumulate_pixel(u64 pos, u64 zoom, u64 pixa, u64 *arry) { + assert(arry); + + u64 beg_mod = pos % MVEC_SIZE; + u64 end_mod = beg_mod + (g_gfx_vsiz * zoom); + u64 pix_mod = pixa % MVEC_SIZE; + +#ifndef NDEBUG + u64 inc_cnt = 0; +#endif + + while (pix_mod < end_mod) { + if (pix_mod >= beg_mod && pix_mod < end_mod) { + u64 pixi = (pix_mod - beg_mod) / zoom; + assert(pixi < g_gfx_vsiz); + arry[pixi]++; + +#ifndef NDEBUG + inc_cnt++; +#endif + } + + pix_mod += MVEC_SIZE; + } + + +#ifndef NDEBUG + if (zoom != 1) { + assert(inc_cnt <= 2); + } +#endif +} + +void gfx_render_mbst(const Core *core, u64 pos, u64 zoom) { + assert(core); + + gfx_clear_array(g_gfx_mbst); + + for (u64 pix = core->pfst; pix <= core->plst; ++pix) { + u64 mb0a = arch_proc_mb0_addr(core, pix); + u64 mb1a = arch_proc_mb1_addr(core, pix); + + gfx_accumulate_pixel(pos, zoom, mb0a, g_gfx_mbst); + gfx_accumulate_pixel(pos, zoom, mb1a, g_gfx_mbst); + } +} + +void gfx_render_mb0s(const Core *core, u64 pos, u64 zoom, u64 psel) { + assert(core); + + gfx_clear_array(g_gfx_mb0s); + + if (psel < core->pfst || psel > core->plst) { + return; + } + + u64 mb0a = arch_proc_mb0_addr(core, psel); + u64 mb0s = arch_proc_mb0_size(core, psel); + + for (u64 i = 0; i < mb0s; ++i) { + gfx_accumulate_pixel(pos, zoom, mb0a + i, g_gfx_mb0s); + } +} + +void gfx_render_mb1s(const Core *core, u64 pos, u64 zoom, u64 psel) { + assert(core); + + gfx_clear_array(g_gfx_mb1s); + + if (psel < core->pfst || psel > core->plst) { + return; + } + + u64 mb1a = arch_proc_mb1_addr(core, psel); + u64 mb1s = arch_proc_mb1_size(core, psel); + + for (u64 i = 0; i < mb1s; ++i) { + gfx_accumulate_pixel(pos, zoom, mb1a + i, g_gfx_mb1s); + } +} + +void gfx_render_ipas(const Core *core, u64 pos, u64 zoom, u64 psel) { + assert(core); + + gfx_clear_array(g_gfx_ipas); + + if (psel < core->pfst || psel > core->plst) { + return; + } + + u64 ipa = arch_proc_ip_addr(core, psel); + + gfx_accumulate_pixel(pos, zoom, ipa, g_gfx_ipas); +} + +void gfx_render_spas(const Core *core, u64 pos, u64 zoom, u64 psel) { + assert(core); + + gfx_clear_array(g_gfx_spas); + + if (psel < core->pfst || psel > core->plst) { + return; + } + + u64 spa = arch_proc_sp_addr(core, psel); + + gfx_accumulate_pixel(pos, zoom, spa, g_gfx_spas); +} + +void gfx_render(const Core *core, u64 pos, u64 zoom, u64 psel) { + assert(core); + + gfx_render_inst(core, pos, zoom); + gfx_render_mbst(core, pos, zoom); + gfx_render_mb0s(core, pos, zoom, psel); + gfx_render_mb1s(core, pos, zoom, psel); + gfx_render_ipas(core, pos, zoom, psel); + gfx_render_spas(core, pos, zoom, psel); +} diff --git a/src/salis.c b/src/salis.c new file mode 100644 index 0000000..c3de4f4 --- /dev/null +++ b/src/salis.c @@ -0,0 +1,661 @@ +// Project: Salis +// Author: Paul Oliver +// Email: contact@pauloliver.dev + +/* + * Core of the Salis simulator. Can be built against different architectures + * and UI modules. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ACT_BENCH (1) +#define ACT_LOAD (2) +#define ACT_NEW (3) + +#define ASM_LINE_LEN (0x100) + +#define MALL_FLAG (0x80) +#define IPCM_FLAG (0x80) +#define INST_CAPS (0x80) +#define INST_MASK (0x7f) + +typedef struct Core Core; +typedef struct Ipcm Ipcm; +typedef struct Proc Proc; +typedef thrd_t Thread; +typedef uint64_t u64; +typedef uint8_t u8; + +struct Core { + u64 mall; + u64 muta[4]; + u64 pnum; + u64 pcap; + u64 pfst; + u64 plst; + u64 pcur; + u64 psli; + u64 ncyc; + + Thread thread; + u64 tix; + + u64 ivpt; + u8 *iviv; + u64 *ivav; + + Proc *pvec; + u8 mvec[MVEC_SIZE]; + u8 tgap[TGAP_SIZE]; +}; + +Core g_cores[CORE_COUNT]; +u64 g_steps; +u64 g_syncs; +#if ACTION == ACT_LOAD || ACTION == ACT_NEW +char g_asav_pbuf[AUTO_SAVE_NAME_LEN]; +#endif +const Proc g_dead_proc; + +#include ARCH_SOURCE + +#if ACTION == ACT_BENCH || ACTION == ACT_NEW +char g_mnemo_table[0x100][MNEMONIC_BUFF_SIZE]; +#endif + +u64 mvec_loop(u64 addr) { + return addr % MVEC_SIZE; +} + +bool mvec_is_alloc(const Core *core, u64 addr) { + assert(core); + return core->mvec[mvec_loop(addr)] & MALL_FLAG ? true : false; +} + +void mvec_alloc(Core *core, u64 addr) { + assert(core); + assert(!mvec_is_alloc(core, addr)); + core->mvec[mvec_loop(addr)] |= MALL_FLAG; + core->mall++; +} + +void mvec_free(Core *core, u64 addr) { + assert(core); + assert(mvec_is_alloc(core, addr)); + core->mvec[mvec_loop(addr)] ^= MALL_FLAG; + core->mall--; +} + +u8 mvec_get_byte(const Core *core, u64 addr) { + assert(core); + return core->mvec[mvec_loop(addr)]; +} + +u8 mvec_get_inst(const Core *core, u64 addr) { + assert(core); + return core->mvec[mvec_loop(addr)] & INST_MASK; +} + +void mvec_set_inst(Core *core, u64 addr, u8 inst) { + assert(core); + assert(inst < INST_CAPS); + core->mvec[mvec_loop(addr)] &= MALL_FLAG; + core->mvec[mvec_loop(addr)] |= inst; +} + +#if MUTA_FLIP_BIT == 1 +void mvec_flip_bit(Core *core, u64 addr, int bit) { + assert(core); + assert(bit < 8); + core->mvec[mvec_loop(addr)] ^= (1 << bit) & INST_MASK; +} +#endif + +bool mvec_is_proc_owner(const Core *core, u64 addr, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + u64 mb0a = arch_proc_mb0_addr(core, pix); + u64 mb0s = arch_proc_mb0_size(core, pix); + + if (((addr - mb0a) % MVEC_SIZE) < mb0s) { + return true; + } + + u64 mb1a = arch_proc_mb1_addr(core, pix); + u64 mb1s = arch_proc_mb1_size(core, pix); + + if (((addr - mb1a) % MVEC_SIZE) < mb1s) { + return true; + } + + return false; +} + +u64 mvec_get_owner(const Core *core, u64 addr) { + assert(core); + assert(mvec_is_alloc(core, addr)); + + for (u64 pix = core->pfst; pix <= core->plst; ++pix) { + if (mvec_is_proc_owner(core, addr, pix)) { + return pix; + } + } + + assert(false); + return -1; +} + +#if ACTION == ACT_BENCH || ACTION == ACT_NEW +u64 muta_smix(u64 *seed) { + assert(seed); + + u64 next = (*seed += 0x9e3779b97f4a7c15); + next = (next ^ (next >> 30)) * 0xbf58476d1ce4e5b9; + next = (next ^ (next >> 27)) * 0x94d049bb133111eb; + + return next ^ (next >> 31); +} +#endif + +u64 muta_ro64(u64 x, int k) { + return (x << k) | (x >> (64 - k)); +} + +u64 muta_next(Core *core) { + assert(core); + + u64 r = muta_ro64(core->muta[1] * 5, 7) * 9; + u64 t = core->muta[1] << 17; + + core->muta[2] ^= core->muta[0]; + core->muta[3] ^= core->muta[1]; + core->muta[1] ^= core->muta[2]; + core->muta[0] ^= core->muta[3]; + + core->muta[2] ^= t; + core->muta[3] = muta_ro64(core->muta[3], 45); + + return r; +} + +void muta_cosmic_ray(Core *core) { + assert(core); + + u64 a = muta_next(core) % MUTA_RANGE; + u64 b = muta_next(core); + + if (a < MVEC_SIZE) { +#if MUTA_FLIP_BIT == 1 + mvec_flip_bit(core, a, (int)(b % 8)); +#else + mvec_set_inst(core, a, b & INST_MASK); +#endif + } +} + +void proc_new(Core *core, const Proc *proc) { + assert(core); + assert(proc); + + if (core->pnum == core->pcap) { + u64 new_pcap = core->pcap * 2; + Proc *new_pvec = calloc(new_pcap, sizeof(Proc)); + + for (u64 pix = core->pfst; pix <= core->plst; ++pix) { + u64 iold = pix % core->pcap; + u64 inew = pix % new_pcap; + memcpy(&new_pvec[inew], &core->pvec[iold], sizeof(Proc)); + } + + free(core->pvec); + core->pcap = new_pcap; + core->pvec = new_pvec; + } + + core->pnum++; + core->plst++; + memcpy(&core->pvec[core->plst % core->pcap], proc, sizeof(Proc)); +} + +void proc_kill(Core *core) { + assert(core); + assert(core->pnum > 1); + + arch_on_proc_kill(core); + + core->pcur++; + core->pfst++; + core->pnum--; +} + +bool proc_is_live(const Core *core, u64 pix) { + assert(core); + + return pix >= core->pfst && pix <= core->plst; +} + +const Proc *proc_get(const Core *core, u64 pix) { + assert(core); + + if (proc_is_live(core, pix)) { + return &core->pvec[pix % core->pcap]; + } else { + return &g_dead_proc; + } +} + +Proc *proc_fetch(Core *core, u64 pix) { + assert(core); + assert(proc_is_live(core, pix)); + + return &core->pvec[pix % core->pcap]; +} + +#if ACTION == ACT_LOAD || ACTION == ACT_NEW +void core_save(FILE *f, const Core *core) { + assert(f); + assert(core); + + fwrite(&core->mall, sizeof(u64), 1, f); + fwrite( core->muta, sizeof(u64), 4, f); + fwrite(&core->pnum, sizeof(u64), 1, f); + fwrite(&core->pcap, sizeof(u64), 1, f); + fwrite(&core->pfst, sizeof(u64), 1, f); + fwrite(&core->plst, sizeof(u64), 1, f); + fwrite(&core->pcur, sizeof(u64), 1, f); + fwrite(&core->psli, sizeof(u64), 1, f); + fwrite(&core->ncyc, sizeof(u64), 1, f); + fwrite(&core->ivpt, sizeof(u64), 1, f); + + fwrite(core->iviv, sizeof(u8), SYNC_INTERVAL, f); + fwrite(core->ivav, sizeof(u64), SYNC_INTERVAL, f); + fwrite(core->pvec, sizeof(Proc), core->pcap, f); + fwrite(core->mvec, sizeof(u8), MVEC_SIZE, f); +} +#endif + +#if ACTION == ACT_BENCH || ACTION == ACT_NEW +u64 core_assemble_ancestor(int cix, const char *anc) { + assert(cix >= 0 && cix < CORE_COUNT); + assert(anc); + + if (anc[0] == '_') { + return 0; + } + + FILE *f = fopen(anc, "r"); + + assert(f); + + u64 addr = 0; + char line[ASM_LINE_LEN] = {0}; + Core *core = &g_cores[cix]; + + for (; fgets(line, ASM_LINE_LEN, f); ++addr) { +#ifndef NDEBUG + bool line_ok = false; +#endif + + line[strcspn(line, "\r\n")] = '\0'; + + for (int i = 0; i < 0x100; ++i) { + if (strcmp(line, g_mnemo_table[i]) == 0) { + mvec_alloc(core, addr); + mvec_set_inst(core, addr, i); +#ifndef NDEBUG + line_ok = true; +#endif + break; + } + } + + assert(line_ok); + } + + fclose(f); + + return addr; +} + +void core_init(int cix, u64 *seed, const char *anc) { + assert(cix >= 0 && cix < CORE_COUNT); + assert(seed); + assert(anc); + + Core *core = &g_cores[cix]; + + if (*seed) { + core->muta[0] = muta_smix(seed); + core->muta[1] = muta_smix(seed); + core->muta[2] = muta_smix(seed); + core->muta[3] = muta_smix(seed); + } + + core->pnum = 1; + core->pcap = 1; + core->iviv = calloc(SYNC_INTERVAL, sizeof(u8)); + core->ivav = calloc(SYNC_INTERVAL, sizeof(u64)); + core->pvec = calloc(core->pcap, sizeof(Proc)); + + assert(core->iviv); + assert(core->ivav); + assert(core->pvec); + + u64 anc_size = core_assemble_ancestor(cix, anc); + + arch_anc_init(core, anc_size); +} +#endif + +#if ACTION == ACT_LOAD +void core_load(FILE *f, Core *core) { + assert(f); + assert(core); + + fread(&core->mall, sizeof(u64), 1, f); + fread( core->muta, sizeof(u64), 4, f); + fread(&core->pnum, sizeof(u64), 1, f); + fread(&core->pcap, sizeof(u64), 1, f); + fread(&core->pfst, sizeof(u64), 1, f); + fread(&core->plst, sizeof(u64), 1, f); + fread(&core->pcur, sizeof(u64), 1, f); + fread(&core->psli, sizeof(u64), 1, f); + fread(&core->ncyc, sizeof(u64), 1, f); + fread(&core->ivpt, sizeof(u64), 1, f); + + core->iviv = calloc(SYNC_INTERVAL, sizeof(u8)); + core->ivav = calloc(SYNC_INTERVAL, sizeof(u64)); + core->pvec = calloc(core->pcap, sizeof(Proc)); + + assert(core->iviv); + assert(core->ivav); + assert(core->pvec); + + fread(core->iviv, sizeof(u8), SYNC_INTERVAL, f); + fread(core->ivav, sizeof(u64), SYNC_INTERVAL, f); + fread(core->pvec, sizeof(Proc), core->pcap, f); + fread(core->mvec, sizeof(u8), MVEC_SIZE, f); +} +#endif + +void core_pull_ipcm(Core *core) { + assert(core); + assert(core->ivpt < SYNC_INTERVAL); + + u8 *iinst = &core->iviv[core->ivpt]; + u64 *iaddr = &core->ivav[core->ivpt]; + + if ((*iinst & IPCM_FLAG) != 0) { + mvec_set_inst(core, *iaddr, *iinst & INST_MASK); + + *iinst = 0; + *iaddr = 0; + } + + assert(*iinst == 0); + assert(*iaddr == 0); +} + +void core_push_ipcm(Core *core, u8 inst, u64 addr) { + assert(core); + assert(core->ivpt < SYNC_INTERVAL); + assert((inst & IPCM_FLAG) == 0); + + u8 *iinst = &core->iviv[core->ivpt]; + u64 *iaddr = &core->ivav[core->ivpt]; + + assert(*iinst == 0); + assert(*iaddr == 0); + + *iinst = inst | IPCM_FLAG; + *iaddr = addr; +} + +void core_step(Core *core) { + assert(core); + + if (core->psli != 0) { + core_pull_ipcm(core); + arch_proc_step(core, core->pcur); + + core->psli--; + core->ivpt++; + + return; + } + + if (core->pcur != core->plst) { + core->psli = arch_proc_slice(core, ++core->pcur); + core_step(core); + return; + } + + core->pcur = core->pfst; + core->psli = arch_proc_slice(core, core->pcur); + core->ncyc++; + + while (core->mall > MVEC_SIZE / 2 && core->pnum > 1) { + proc_kill(core); + } + + muta_cosmic_ray(core); + core_step(core); +} + +#if ACTION == ACT_LOAD || ACTION == ACT_NEW +void salis_save(const char *path) { + FILE *f = fopen(path, "wb"); + + assert(f); + + for (int i = 0; i < CORE_COUNT; ++i) { + core_save(f, &g_cores[i]); + } + + fwrite(&g_steps, sizeof(u64), 1, f); + fwrite(&g_syncs, sizeof(u64), 1, f); + fclose(f); +} + +void salis_auto_save() { + if (g_steps % AUTO_SAVE_INTERVAL != 0) { + return; + } + +#ifndef NDEBUG + int rem = snprintf( +#else + snprintf( +#endif + g_asav_pbuf, + AUTO_SAVE_NAME_LEN, + "%s-%#018lx", + SIM_PATH, + g_steps + ); + + assert(rem >= 0); + assert(rem < AUTO_SAVE_NAME_LEN); + + salis_save(g_asav_pbuf); +} +#endif + +#if ACTION == ACT_BENCH || ACTION == ACT_NEW +void salis_init() { + for (int i = 0; i < 0x100; ++i) { + arch_mnemonic(i, g_mnemo_table[i]); + } + + u64 seed = SEED; + char anc_list[] = ANC_LIST; + + assert(anc_list); + + for (int i = 0; i < CORE_COUNT; ++i) { + core_init(i, &seed, strtok(i ? NULL : anc_list, ",")); + } + +#if ACTION == ACT_NEW + salis_auto_save(); +#endif +} +#endif + +#if ACTION == ACT_LOAD +void salis_load() { + FILE *f = fopen(SIM_PATH, "rb"); + + assert(f); + + for (int i = 0; i < CORE_COUNT; ++i) { + core_load(f, &g_cores[i]); + } + + fread(&g_steps, sizeof(u64), 1, f); + fread(&g_syncs, sizeof(u64), 1, f); + fclose(f); +} +#endif + +int salis_thread(Core *core) { + assert(core); + + for (u64 i = 0; i < core->tix; ++i) { + core_step(core); + } + + return 0; +} + +void salis_run_thread(u64 ns) { + for (int i = 0; i < CORE_COUNT; ++i) { + g_cores[i].tix = ns; + + thrd_create( + &g_cores[i].thread, + (thrd_start_t)salis_thread, + &g_cores[i] + ); + } + + for (int i = 0; i < CORE_COUNT; ++i) { + thrd_join(g_cores[i].thread, NULL); + } + + g_steps += ns; +} + +void salis_sync() { + u8 *iviv0 = g_cores[0].iviv; + u64 *ivav0 = g_cores[0].ivav; + + for (int i = 1; i < CORE_COUNT; ++i) { + g_cores[i - 1].iviv = g_cores[i].iviv; + g_cores[i - 1].ivav = g_cores[i].ivav; + } + + g_cores[CORE_COUNT - 1].iviv = iviv0; + g_cores[CORE_COUNT - 1].ivav = ivav0; + + for (int i = 0; i < CORE_COUNT; ++i) { + g_cores[i].ivpt = 0; + } + + g_syncs++; +} + +void salis_loop(u64 ns, u64 dt) { + assert(dt); + + if (ns < dt) { + salis_run_thread(ns); + return; + } + + salis_run_thread(dt); + salis_sync(); +#if ACTION == ACT_LOAD || ACTION == ACT_NEW + salis_auto_save(); +#endif + salis_loop(ns - dt, SYNC_INTERVAL); +} + +#ifndef NDEBUG +void salis_validate_core(const Core *core) { + assert(core->plst >= core->pfst); + assert(core->pnum == core->plst + 1 - core->pfst); + assert(core->pnum <= core->pcap); + assert(core->pcur >= core->pfst && core->pcur <= core->plst); + assert(core->ncyc <= g_steps); + + u64 mall = 0; + + for (u64 i = 0; i < MVEC_SIZE; ++i) { + mall += mvec_is_alloc(core, i) ? 1 : 0; + } + + assert(core->mall == mall); + + for (u64 i = core->pfst; i <= core->plst; ++i) { + arch_validate_proc(core, i); + } + + for (u64 i = 0; i < SYNC_INTERVAL; ++i) { + u8 iinst = core->iviv[i]; + + if ((iinst & IPCM_FLAG) == 0) { + u64 iaddr = core->ivav[i]; + + assert(iinst == 0); + assert(iaddr == 0); + } + } + + assert(core->ivpt == g_steps % SYNC_INTERVAL); +} + +void salis_validate() { + assert(g_steps / SYNC_INTERVAL == g_syncs); + + for (int i = 0; i < CORE_COUNT; ++i) { + salis_validate_core(&g_cores[i]); + } +} +#endif + +void salis_step(u64 ns) { + assert(ns); + salis_loop(ns, SYNC_INTERVAL - (g_steps % SYNC_INTERVAL)); + +#ifndef NDEBUG + salis_validate(); +#endif +} + +void salis_free() { + for (int i = 0; i < CORE_COUNT; ++i) { + assert(g_cores[i].pvec); + assert(g_cores[i].iviv); + assert(g_cores[i].ivav); + + free(g_cores[i].pvec); + free(g_cores[i].iviv); + free(g_cores[i].ivav); + + g_cores[i].pvec = NULL; + g_cores[i].iviv = NULL; + g_cores[i].ivav = NULL; + } +} + +#include UI diff --git a/src/ui/curses.c b/src/ui/curses.c new file mode 100644 index 0000000..8f91d9f --- /dev/null +++ b/src/ui/curses.c @@ -0,0 +1,944 @@ +// Project: Salis +// Author: Paul Oliver +// Email: contact@pauloliver.dev + +/* + * Implements a TUI for the Salis simulator using the ncurses library. + */ + +#include +#include +#include + +#define CTRL(x) ((x) & 0x1f) +#define PANE_WIDTH (27) +#define PROC_FIELD_WIDTH (21) +#define PROC_PAGE_LINES (12) + +enum { + PAGE_CORE, + PAGE_PROCESS, + PAGE_WORLD, + PAGE_IPC, + PAGE_COUNT +}; + +enum { + PAIR_NOUSE, + PAIR_NORMAL, + PAIR_HEADER, + PAIR_LIVE_PROC, + PAIR_SELECTED_PROC, + PAIR_FREE_CELL, + PAIR_ALLOC_CELL, + PAIR_MEM_BLOCK_START, + PAIR_SELECTED_MB1, + PAIR_SELECTED_MB2, + PAIR_SELECTED_IP, + PAIR_SELECTED_SP, +}; + +bool g_exit; +bool g_running; +unsigned g_core; +unsigned g_page; +bool g_proc_genes; +u64 g_proc_scroll; +u64 g_proc_field_scroll; +u64 g_proc_gene_scroll; +u64 g_proc_selected; +u64 g_wrld_pos; +u64 g_wrld_zoom; +bool g_wcursor_mode; +int g_wcursor_x; +int g_wcursor_y; +u64 g_wcursor_pointed; +u64 g_vlin; +u64 g_vsiz; +u64 g_vlin_rng; +u64 g_vsiz_rng; +u64 g_ivpt_scroll; +char *g_line_buff; +u64 g_step_block; + +const wchar_t *g_zoomed_symbols = ( + L"⠀⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇⠈⠉⠊⠋⠌⠍⠎⠏⡈⡉⡊⡋⡌⡍⡎⡏⠐⠑⠒⠓⠔⠕⠖⠗⡐⡑⡒⡓⡔⡕⡖⡗⠘⠙⠚⠛⠜⠝⠞⠟⡘⡙⡚⡛⡜⡝⡞⡟" + L"⠠⠡⠢⠣⠤⠥⠦⠧⡠⡡⡢⡣⡤⡥⡦⡧⠨⠩⠪⠫⠬⠭⠮⠯⡨⡩⡪⡫⡬⡭⡮⡯⠰⠱⠲⠳⠴⠵⠶⠷⡰⡱⡲⡳⡴⡵⡶⡷⠸⠹⠺⠻⠼⠽⠾⠿⡸⡹⡺⡻⡼⡽⡾⡿" + L"⢀⢁⢂⢃⢄⢅⢆⢇⣀⣁⣂⣃⣄⣅⣆⣇⢈⢉⢊⢋⢌⢍⢎⢏⣈⣉⣊⣋⣌⣍⣎⣏⢐⢑⢒⢓⢔⢕⢖⢗⣐⣑⣒⣓⣔⣕⣖⣗⢘⢙⢚⢛⢜⢝⢞⢟⣘⣙⣚⣛⣜⣝⣞⣟" + L"⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯⢰⢱⢲⢳⢴⢵⢶⢷⣰⣱⣲⣳⣴⣵⣶⣷⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿" +); + +#include "graphics.c" + +void ui_line_buff_free() { + if (g_line_buff) { + free(g_line_buff); + } + + g_line_buff = NULL; +} + +void ui_line_buff_resize() { + ui_line_buff_free(); + + g_line_buff = calloc(COLS + 1, sizeof(char)); +} + +void ui_line(bool clear, int line, int color, int attr, const char *format, ...) { + assert(line >= 0); + assert(format); + + if (line >= LINES) { + return; + } + + if (clear) { + move(line, 0); + clrtoeol(); + } + + va_list args; + + attron(COLOR_PAIR(color) | attr); + va_start(args, format); + + vsnprintf(g_line_buff, COLS, format, args); + mvprintw(line, 1, g_line_buff); + + va_end(args); + attroff(COLOR_PAIR(color) | attr); +} + +void ui_clear_line(int l) { + ui_line(true, l, PAIR_NORMAL, A_NORMAL, ""); +} + +void ui_field(int line, int col, int color, int attr, const char *format, ...) { + assert(line >= 0); + assert(col >= 0); + assert(format); + + if (line >= LINES || col >= COLS) { + return; + } + + va_list args; + + attron(COLOR_PAIR(color) | attr); + va_start(args, format); + + vsnprintf(g_line_buff, COLS - col, format, args); + mvprintw(line, col, g_line_buff); + + va_end(args); + attroff(COLOR_PAIR(color) | attr); +} + +void ui_str_field(int l, const char *label, const char *value) { + assert(label); + assert(value); + ui_line(false, l, PAIR_NORMAL, A_NORMAL, "%s : %18s", label, value); +} + +void ui_ulx_field(int l, const char *label, u64 value) { + assert(label); + ui_line(false, l, PAIR_NORMAL, A_NORMAL, "%-4s : %#18lx", label, value); +} + +void ui_print_core(int l) { + ui_line(false, ++l, PAIR_HEADER, A_BOLD, "CORE [%d]", g_core); + ui_ulx_field(++l, "mall", g_cores[g_core].mall); + ui_ulx_field(++l, "mut0", g_cores[g_core].muta[0]); + ui_ulx_field(++l, "mut1", g_cores[g_core].muta[1]); + ui_ulx_field(++l, "mut2", g_cores[g_core].muta[2]); + ui_ulx_field(++l, "mut3", g_cores[g_core].muta[3]); + ui_ulx_field(++l, "pnum", g_cores[g_core].pnum); + ui_ulx_field(++l, "pcap", g_cores[g_core].pcap); + ui_ulx_field(++l, "pfst", g_cores[g_core].pfst); + ui_ulx_field(++l, "plst", g_cores[g_core].plst); + ui_ulx_field(++l, "pcur", g_cores[g_core].pcur); + ui_ulx_field(++l, "psli", g_cores[g_core].psli); + ui_ulx_field(++l, "ncyc", g_cores[g_core].ncyc); + ui_ulx_field(++l, "ivpt", g_cores[g_core].ivpt); +} + +int ui_proc_pair(u64 pix) { + if (pix == g_proc_selected) { + return PAIR_SELECTED_PROC; + } else if (proc_is_live(&g_cores[g_core], pix)) { + return PAIR_LIVE_PROC; + } else { + return PAIR_NORMAL; + } +} + +const char *ui_proc_state(u64 pix) { + return proc_is_live(&g_cores[g_core], pix) ? "live" : "dead"; +} + +void ui_print_process_genome_header(int l) { + ui_line(false, l++, PAIR_NORMAL, A_NORMAL, "%s : %18s : %s", "stat", "pix", "genome"); +} + +void ui_print_process_gene(int l, int gcol, u64 gidx, u64 mba, u64 pix, int pair) { + assert(gcol >= PANE_WIDTH + 2); + assert(gcol < COLS); + assert(proc_is_live(&g_cores[g_core], pix)); + assert(pair == PAIR_SELECTED_MB1 || pair == PAIR_SELECTED_MB2); + + const Core *core = &g_cores[g_core]; + + u64 addr = mba + gidx; + u8 byte = mvec_get_byte(core, addr); + + wchar_t gsym[2] = { arch_symbol(byte), L'\0' }; + cchar_t cchar = { 0 }; + + int pair_cell; + + if (arch_proc_ip_addr(core, pix) == addr) { + pair_cell = PAIR_SELECTED_IP; + } else if (arch_proc_sp_addr(core, pix) == addr) { + pair_cell = PAIR_SELECTED_SP; + } else { + pair_cell = pair; + } + + setcchar(&cchar, gsym, 0, pair_cell, NULL); + mvadd_wch(l, gcol, &cchar); +} + +void ui_print_process_genes(int l, u64 pix) { + ui_line(true, l, ui_proc_pair(pix), A_NORMAL, "%s : %#18lx :", ui_proc_state(pix), pix); + + if (!proc_is_live(&g_cores[g_core], pix)) { + return; + } + + const Core *core = &g_cores[g_core]; + + int scol = PANE_WIDTH + 2; + int gcol = scol - g_proc_gene_scroll; + u64 mb0a = arch_proc_mb0_addr(core, pix); + u64 mb0s = arch_proc_mb0_size(core, pix); + u64 mb1a = arch_proc_mb1_addr(core, pix); + u64 mb1s = arch_proc_mb1_size(core, pix); + + for (u64 gidx = 0; gidx < mb0s && gcol < COLS; ++gidx, ++gcol) { + if (gcol >= scol) { + ui_print_process_gene(l, gcol, gidx, mb0a, pix, PAIR_SELECTED_MB1); + } + } + + for (u64 gidx = 0; gidx < mb1s && gcol < COLS; ++gidx, ++gcol) { + if (gcol >= scol) { + ui_print_process_gene(l, gcol, gidx, mb1a, pix, PAIR_SELECTED_MB2); + } + } + + clrtoeol(); +} + +void ui_print_process_field_header_element(int l, int fidx, const char *name) { + assert(fidx >= 0); + assert(name); + + if (fidx < (int)g_proc_field_scroll) { + return; + } + + int foff = fidx - g_proc_field_scroll; + int fcol = foff * PROC_FIELD_WIDTH + PANE_WIDTH - 1; + + ui_field(l, fcol, PAIR_NORMAL, A_NORMAL, " : %18s", name); +} + +void ui_print_process_field_header(int l) { + ui_line(true, l, PAIR_NORMAL, A_NORMAL, "%s : %18s", "stat", "pix"); + + int fidx = 0; + +#define PROC_FIELD(type, name) ui_print_process_field_header_element(l, fidx++, #name); + PROC_FIELDS +#undef PROC_FIELD +} + +void ui_print_process_field_element(int l, int fidx, int fclr, u64 field) { + assert(fidx >= 0); + + if (fidx < (int)g_proc_field_scroll) { + return; + } + + int foff = fidx - g_proc_field_scroll; + int fcol = foff * PROC_FIELD_WIDTH + PANE_WIDTH - 1; + + ui_field(l, fcol, fclr, A_NORMAL, " : %#18lx", field); +} + + +void ui_print_process_fields(int l, u64 pix) { + ui_line(true, l, ui_proc_pair(pix), A_NORMAL, "%s : %#18lx", ui_proc_state(pix), pix); + + const Proc *proc = proc_get(&g_cores[g_core], pix); + int fidx = 0; + int fclr = ui_proc_pair(pix); + +#define PROC_FIELD(type, name) ui_print_process_field_element(l, fidx++, fclr, proc->name); + PROC_FIELDS +#undef PROC_FIELD +} + +void ui_print_process(int l) { + l++; + + ui_line(true, l++, PAIR_HEADER, A_BOLD, + "PROCESS [vs:%#lx | ps:%#lx | pf:%#lx | pl:%#lx | fs:%#lx | gs:%#lx]", + g_proc_scroll, + g_proc_selected, + g_cores[g_core].pfst, + g_cores[g_core].plst, + g_proc_field_scroll, + g_proc_gene_scroll + ); + + u64 pix = g_proc_scroll; + + if (g_proc_genes) { + ui_print_process_genome_header(l++); + + while (l < LINES) { + ui_print_process_genes(l++, pix++); + } + } else { + ui_print_process_field_header(l++); + + while (l < LINES) { + ui_print_process_fields(l++, pix++); + } + } +} + +void ui_world_resize() { + assert(g_wrld_zoom); + + g_vlin = 0; + g_vsiz = 0; + g_vlin_rng = 0; + g_vsiz_rng = 0; + + if (COLS > PANE_WIDTH) { + g_vlin = COLS - PANE_WIDTH; + g_vsiz = LINES * g_vlin; + g_vlin_rng = g_vlin * g_wrld_zoom; + g_vsiz_rng = g_vsiz * g_wrld_zoom; + + gfx_resize(g_vsiz); + } +} + +void ui_print_cell(u64 i, u64 r, u64 x, u64 y) { + wchar_t inst_nstr[2] = { L'\0', L'\0' }; + cchar_t cchar = { 0 }; + u64 inst_avrg = g_gfx_inst[i] / g_wrld_zoom; + + if (g_wrld_zoom == 1) { + inst_nstr[0] = arch_symbol((u8)inst_avrg); + } else { + inst_nstr[0] = g_zoomed_symbols[(u8)inst_avrg]; + } + + int pair_cell; + + if (g_wcursor_mode && r == (u64)g_wcursor_x && y == (u64)g_wcursor_y) { + pair_cell = PAIR_NORMAL; + } else if (g_gfx_ipas[i] != 0) { + pair_cell = PAIR_SELECTED_IP; + } else if (g_gfx_spas[i] != 0) { + pair_cell = PAIR_SELECTED_SP; + } else if (g_gfx_mb0s[i] != 0) { + pair_cell = PAIR_SELECTED_MB1; + } else if (g_gfx_mb1s[i] != 0) { + pair_cell = PAIR_SELECTED_MB2; + } else if (g_gfx_mbst[i] != 0) { + pair_cell = PAIR_MEM_BLOCK_START; + } else if (g_gfx_mall[i] != 0) { + pair_cell = PAIR_ALLOC_CELL; + } else { + pair_cell = PAIR_FREE_CELL; + } + + setcchar(&cchar, inst_nstr, 0, pair_cell, NULL); + mvadd_wch(y, x, &cchar); +} + +void ui_print_wcursor_bar() { + ui_clear_line(LINES - 1); + + const Core *core = &g_cores[g_core]; + + char cmnem[MNEMONIC_BUFF_SIZE]; + char cownr[PROC_FIELD_WIDTH]; + + u64 cpos = g_vlin * g_wcursor_y + g_wcursor_x; + u64 caddr = cpos * g_wrld_zoom + g_wrld_pos; + u8 cbyte = mvec_get_byte(core, caddr); + + if (mvec_is_alloc(core, caddr)) { + g_wcursor_pointed = mvec_get_owner(core, caddr); + snprintf(cownr, PROC_FIELD_WIDTH, "%#lx", g_wcursor_pointed); + } else { + g_wcursor_pointed = (u64)(-1); + snprintf(cownr, PROC_FIELD_WIDTH, "-"); + } + + arch_mnemonic(cbyte, cmnem); + + 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], + cmnem, + cownr + ); +} + +void ui_print_world(int l) { + l++; + + ui_line(false, l++, PAIR_HEADER, A_BOLD, "WORLD"); + ui_ulx_field(l++, "wrlp", g_wrld_pos); + ui_ulx_field(l++, "wrlz", g_wrld_zoom); + ui_ulx_field(l++, "psel", g_proc_selected); + ui_ulx_field(l++, "pabs", g_proc_selected % g_cores[g_core].pcap); + ui_ulx_field(l++, "vrng", g_vsiz_rng); + ui_str_field(l++, "curs", g_wcursor_mode ? "on" : "off"); + + l++; + + ui_line(false, l++, PAIR_HEADER, A_BOLD, "SELECTED"); + + const Proc *psel = proc_get(&g_cores[g_core], g_proc_selected); + +#define PROC_FIELD(type, name) ui_ulx_field(l++, #name, psel->name); + PROC_FIELDS +#undef 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 (u64 i = 0; i < g_vsiz; ++i) { + u64 r = i % g_vlin; + u64 x = r + PANE_WIDTH; + u64 y = i / g_vlin; + + ui_print_cell(i, r, x, y); + } + + if (g_wcursor_mode) { + ui_print_wcursor_bar(); + } +} + +void ui_print_ipc_field(int l, u64 i, int color) { + u8 iinst = g_cores[g_core].iviv[i]; + u64 iaddr = g_cores[g_core].ivav[i]; + + ui_field(l, PANE_WIDTH, color, A_NORMAL, "%#18x : %#18x : %#18x", i, iinst, iaddr); +} + +void ui_print_ipc_data() { + ui_field(0, PANE_WIDTH, PAIR_NORMAL, A_NORMAL, "%18s : %18s : %18s", "ipci", "inst", "addr"); + + int l = 1 - g_ivpt_scroll; + + for (u64 i = 0; i < SYNC_INTERVAL; ++i) { + if (i == g_cores[g_core].ivpt) { + if (l >= 1) { + ui_print_ipc_field(l++, i, PAIR_SELECTED_PROC); + } + + continue; + } + + u8 iinst = g_cores[g_core].iviv[i]; + + if ((iinst & IPCM_FLAG) != 0) { + if (l >= 1) { + ui_print_ipc_field(l++, i, PAIR_LIVE_PROC); + } + + continue; + } + } + + for (; l < LINES; ++l) { + if (l >= 1) { + move(l, PANE_WIDTH); + clrtoeol(); + } + } +} + +void ui_print_ipc(int l) { + l++; + + const Core *core = &g_cores[g_core]; + + ui_line(true, l++, PAIR_HEADER, A_BOLD, "IPC [%#lx]", g_ivpt_scroll); + ui_ulx_field(l++, "ivpt", core->ivpt); + ui_ulx_field(l++, "ivpi", core->iviv[core->ivpt]); + ui_ulx_field(l++, "ivpa", core->ivav[core->ivpt]); + + ui_print_ipc_data(); +} + +void ui_print() { + int l = 1; + + ui_line(false, l++, PAIR_HEADER, A_BOLD, "SALIS [%d:%d]", g_core, CORE_COUNT); + ui_str_field(l++, "name", SIM_NAME); + ui_ulx_field(l++, "seed", SEED); + ui_str_field(l++, "fbit", MUTA_FLIP_BIT ? "yes" : "no"); + ui_ulx_field(l++, "asav", AUTO_SAVE_INTERVAL); + ui_str_field(l++, "arch", ARCHITECTURE); + ui_ulx_field(l++, "size", MVEC_SIZE); + ui_ulx_field(l++, "syni", SYNC_INTERVAL); + ui_ulx_field(l++, "step", g_steps); + ui_ulx_field(l++, "sync", g_syncs); + ui_ulx_field(l++, "step", g_step_block); + + switch (g_page) { + case PAGE_CORE: + ui_print_core(l); + break; + case PAGE_PROCESS: + ui_print_process(l); + break; + case PAGE_WORLD: + ui_print_world(l); + break; + case PAGE_IPC: + ui_print_ipc(l); + break; + default: + break; + } +} + +void ev_vscroll(int ev) { + switch (g_page) { + case PAGE_PROCESS: + switch (ev) { + case 'W': + g_proc_scroll += (LINES > PROC_PAGE_LINES) ? LINES - PROC_PAGE_LINES : 0; + break; + case 'S': + g_proc_scroll -= (LINES > PROC_PAGE_LINES) ? LINES - PROC_PAGE_LINES : 0; + break; + case 'w': + g_proc_scroll += 1; + break; + case 's': + g_proc_scroll -= 1; + break; + case 'q': + g_proc_scroll = 0; + break; + default: + break; + } + + break; + case PAGE_WORLD: { + switch (ev) { + case 'W': + g_wrld_pos += g_vsiz_rng; + break; + case 'S': + g_wrld_pos -= g_vsiz_rng; + break; + case 'w': + g_wrld_pos += g_vlin_rng; + break; + case 's': + g_wrld_pos -= g_vlin_rng; + 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 < (u64)LINES ? g_ivpt_scroll : (u64)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; + default: + break; + } +} + +void ev_hscroll(int ev) { + switch (g_page) { + case PAGE_PROCESS: { + u64 *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': + g_wrld_pos -= g_wrld_zoom; + break; + case 'd': + g_wrld_pos += g_wrld_zoom; + break; + default: + break; + } + + break; + default: + break; + } +} + +void ev_zoom(int ev) { + switch (g_page) { + case PAGE_WORLD: + switch (ev) { + case 'x': + g_wrld_zoom *= (g_vlin != 0 && g_vsiz_rng < MVEC_SIZE) ? 2 : 1; + ui_world_resize(); + break; + case 'z': + g_wrld_zoom /= (g_wrld_zoom != 1) ? 2 : 1; + ui_world_resize(); + break; + default: + break; + } + + break; + default: + break; + } +} + +void ev_move_wcursor(int ev) { + switch (ev) { + case KEY_UP: + g_wcursor_y -= (g_wcursor_y != 0) ? 1 : 0; + break; + case KEY_DOWN: + g_wcursor_y += (g_wcursor_y < LINES - 2) ? 1 : 0; + break; + case KEY_LEFT: + g_wcursor_x -= (g_wcursor_x != 0) ? 1 : 0; + break; + case KEY_RIGHT: + g_wcursor_x += ((u64)g_wcursor_x < g_vlin - 1) ? 1 : 0; + break; + default: + break; + } +} + +void ev_sel_proc(int ev) { + if (g_page != PAGE_PROCESS && g_page != PAGE_WORLD) { + return; + } + + switch (ev) { + case 'o': + g_proc_selected -= 1; + break; + case 'p': + g_proc_selected += 1; + break; + case 'f': + g_proc_selected = g_cores[g_core].pfst; + break; + case 'l': + g_proc_selected = g_cores[g_core].plst; + break; + default: + break; + } +} + +void ev_goto_sel_proc() { + switch (g_page) { + case PAGE_PROCESS: + g_proc_scroll = g_proc_selected; + break; + case PAGE_WORLD: + g_wrld_pos = g_cores[g_core].pvec[g_proc_selected % g_cores[g_core].pcap].mb0a; + break; + default: + break; + } +} + +void ev_handle() { + int ev = getch(); + + if (g_page == PAGE_WORLD && g_wcursor_mode) { + switch (ev) { + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + ev_move_wcursor(ev); + return; + case '\n': + if (g_wcursor_pointed != (u64)(-1)) { + g_proc_selected = g_wcursor_pointed; + } + + break; + default: + break; + } + } + + switch (ev) { + case CTRL('c'): + g_exit = true; + break; + case KEY_SLEFT: + clear(); + g_core = (g_core - 1) % CORE_COUNT; + break; + case KEY_SRIGHT: + clear(); + g_core = (g_core + 1) % CORE_COUNT; + break; + case KEY_LEFT: + clear(); + g_page = (g_page - 1) % PAGE_COUNT; + break; + case KEY_RIGHT: + clear(); + g_page = (g_page + 1) % PAGE_COUNT; + break; + case KEY_RESIZE: + clear(); + ui_line_buff_resize(); + ui_world_resize(); + + if (g_vlin) { + while (g_vsiz_rng >= MVEC_SIZE * 2 && g_wrld_zoom != 1) { + g_wrld_zoom /= 2; + ui_world_resize(); + } + } + + g_wcursor_mode = false; + break; + case 'W': + case 'S': + case 'w': + case 's': + case 'q': + ev_vscroll(ev); + break; + case 'A': + case 'a': + case 'd': + ev_hscroll(ev); + break; + case 'z': + case 'x': + ev_zoom(ev); + break; + case 'o': + case 'p': + case 'f': + case 'l': + ev_sel_proc(ev); + break; + case 'k': + ev_goto_sel_proc(); + break; + case 'g': + if (g_page == PAGE_PROCESS) { + clear(); + g_proc_genes = !g_proc_genes; + } + + break; + case 'c': + if (g_page == PAGE_WORLD) { + clear(); + + if (g_vlin == 0) { + g_wcursor_mode = false; + } else { + g_wcursor_mode = !g_wcursor_mode; + } + } + + break; + case ' ': + g_running = !g_running; + nodelay(stdscr, g_running); + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '0': + if (!g_running) { + u64 cycles = 1 << (((ev - '0') ? (ev - '0') : 10) - 1); + salis_step(cycles); + } + + break; + default: + break; + } +} + +void init() { + setlocale(LC_ALL, ""); + + initscr(); + raw(); + noecho(); + curs_set(0); + keypad(stdscr, TRUE); + + start_color(); + init_color(COLOR_BLACK, 0, 0, 0); + + init_pair(PAIR_NORMAL, COLOR_WHITE, COLOR_BLACK ); + init_pair(PAIR_HEADER, COLOR_BLUE, COLOR_BLACK ); + init_pair(PAIR_LIVE_PROC, COLOR_BLUE, COLOR_BLACK ); + init_pair(PAIR_SELECTED_PROC, COLOR_YELLOW, COLOR_BLACK ); + init_pair(PAIR_FREE_CELL, COLOR_BLACK, COLOR_BLUE ); + init_pair(PAIR_ALLOC_CELL, COLOR_BLACK, COLOR_CYAN ); + init_pair(PAIR_MEM_BLOCK_START, COLOR_BLACK, COLOR_WHITE ); + init_pair(PAIR_SELECTED_MB1, COLOR_BLACK, COLOR_YELLOW ); + init_pair(PAIR_SELECTED_MB2, COLOR_BLACK, COLOR_GREEN ); + init_pair(PAIR_SELECTED_IP, COLOR_BLACK, COLOR_RED ); + init_pair(PAIR_SELECTED_SP, COLOR_BLACK, COLOR_MAGENTA); + +#if ACTION == ACT_NEW + salis_init(); +#elif ACTION == ACT_LOAD + salis_load(); +#endif + + g_wrld_zoom = 1; + g_step_block = 1; + + ui_line_buff_resize(); + ui_world_resize(); +} + +void exec() { + while (!g_exit) { + if (g_running) { + clock_t beg = clock(); + salis_step(g_step_block - (g_steps % g_step_block)); + clock_t end = clock(); + + if ((end - beg) < (CLOCKS_PER_SEC / 30)) { + g_step_block <<= 1; + } + + if ((end - beg) >= (CLOCKS_PER_SEC / 60) && g_step_block != 1) { + g_step_block >>= 1; + } + } + + ui_print(); + ev_handle(); + } +} + +void quit() { + gfx_free(); + ui_line_buff_free(); + salis_save(SIM_PATH); + salis_free(); + endwin(); +} + +int main() { + init(); + exec(); + quit(); + + return 0; +} diff --git a/src/ui/daemon.c b/src/ui/daemon.c new file mode 100644 index 0000000..f74713d --- /dev/null +++ b/src/ui/daemon.c @@ -0,0 +1,63 @@ +// Project: Salis +// Author: Paul Oliver +// Email: contact@pauloliver.dev + +/* + * Implements a minimal UI for the Salis simulator with minimal output and + * interruptible via OS signals. Ideal for running Salis in the background. + */ + +#include +#include + +volatile bool g_running; +u64 g_step_block; + +void sig_handler(int signo) { + switch (signo) { + case SIGINT: + case SIGTERM: + printf("signal received, stopping simulator...\n"); + g_running = false; + break; + } +} + +void step_block() { + clock_t beg = clock(); + salis_step(g_step_block - (g_steps % g_step_block)); + clock_t end = clock(); + + if ((end - beg) < (CLOCKS_PER_SEC * 4)) { + g_step_block <<= 1; + } + + if ((end - beg) >= (CLOCKS_PER_SEC * 2) && g_step_block != 1) { + g_step_block >>= 1; + } + + printf("simulator running on step '%#lx'\n", g_steps); +} + +int main() { +#if ACTION == ACT_NEW + salis_init(); +#elif ACTION == ACT_LOAD + salis_load(); +#endif + + g_running = true; + g_step_block = 1; + + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + + while (g_running) { + step_block(); + } + + salis_save(SIM_PATH); + salis_free(); + + return 0; +} -- cgit v1.2.1