aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md30
-rw-r--r--ancs/dummy/0123.asm6
-rw-r--r--ancs/salis-v1/1n.asm5
-rw-r--r--ancs/salis-v1/55b.asm74
-rw-r--r--arch/dummy/arch.j2.c130
-rw-r--r--arch/dummy/arch_vars.py21
-rw-r--r--arch/salis-v1/arch.j2.c890
-rw-r--r--arch/salis-v1/arch_vars.py98
-rw-r--r--bench.j2.c (renamed from src/bench.c)23
-rw-r--r--core.j2.c838
-rwxr-xr-xsalis406
-rwxr-xr-xsalis.py356
-rw-r--r--src/arch/dummy.c152
-rw-r--r--src/arch/salis-v1.c875
-rw-r--r--src/graphics.c238
-rw-r--r--src/salis.c811
-rw-r--r--ui/curses/ui.j2.c (renamed from src/ui/curses.c)564
-rw-r--r--ui/curses/ui_vars.py2
-rw-r--r--ui/daemon/ui.j2.c (renamed from src/ui/daemon.c)29
-rw-r--r--ui/daemon/ui_vars.py2
20 files changed, 2786 insertions, 2764 deletions
diff --git a/README.md b/README.md
index ba9b2d7..a4518aa 100644
--- a/README.md
+++ b/README.md
@@ -7,8 +7,8 @@
*SALIS* is a platform for conducting artificial life experiments. It enables
the development of Tierra-like virtual machines, with certain limitations. For
newcomers, I recommend exploring Tierra first. The following resources provide
-valuable context and insight into both the motivations and implementation of
-both Tierra and this project:
+valuable context and insight into the motivations and implementation of both
+Tierra and this project:
- [Video about Tierra](https://www.youtube.com/watch?v=Wl5rRGVD0QI)
- [Read about Tierra](https://tomray.me/pubs/doc/index.html#What)
@@ -23,28 +23,28 @@ with the original Tierra simulator, check out the following resources:
- [SALIS V1 introductory playlist](https://www.youtube.com/watch?v=jCFmOCvy6po&list=PLrEmYrpTcDJY2NdGL6B7wIRbKGp_6NkxY)
## Usage
-*SALIS* simulations are initialized using the provided `salis` shell script.
+*SALIS* simulations are initialized using the provided `salis.py` python script.
Use `salis new [...]` to start new simulations and `salis load [...]` to load
saved simulations. For a full list of available arguments for each command,
run `salis new --help` and `salis load --help`, respectively.
-The shell script compiles a temporary executable on the fly (compilation
+The python script compiles a temporary executable on the fly (compilation
typically takes less than a second) based on the specified arguments and
launches it immediately.
-Different architectures can be implemented as standalone C files in the
-`src/arch/` directory. When creating a new simulation, you can select a
-specific architecture using the `--arch` argument.
+Different architectures can be implemented as standalone C templates in the
+`arch/` directory. When creating a new simulation, you can select a specific
+architecture using the `--arch` argument.
-Similarly, different user interfaces are implemented as C files within the
-`src/ui/` directory. For example, the `curses.c` UI launches a terminal-based
+Similarly, different user interfaces are implemented as C templates within the
+`ui/` directory. For example, the `curses` UI launches a terminal-based
simulation visualizer, allowing easy exploration of *SALIS* memory cores and
-processes. In contrast, the `daemon.c` UI provides minimal output, making it
+processes. In contrast, the `daemon` UI provides minimal output, making it
ideal for running *SALIS* as a background service. Unlike the `--arch`
argument, you can choose a different `--ui` argument each time you load a
saved simulation.
-For example, the following command will launch a new *SALIS* simulation with 4
+As an example, the following command will launch a new *SALIS* simulation with 4
copies of the `55a` ancestor organisms pre-compiled in each memory core. It
will use the `salis-v1` architecture, run on 8 memory cores, with each core
having a size of 2^22 bytes. The PRNG seed is set to `123456789`:
@@ -52,9 +52,15 @@ having a size of 2^22 bytes. The PRNG seed is set to `123456789`:
user@host$ ./salis new -A55a -asalis-v1 -c8 -C4 -m22 -nworld-1 -s123456789 -o
```
-Upon exit, the simulation data will be automatically saved to
+Upon exit, the simulation state will be automatically saved to
`${HOME}/.salis/world-1/`. As long as the contents of this directory are not
removed, you can reload the saved simulation with the following command:
```console
user@host$ ./salis load -n world-1 -o
```
+
+## Requirements
+- C compiler - ideally GCC
+- Python3
+- Jinja2 - installed globally or within an active virtual environment
+
diff --git a/ancs/dummy/0123.asm b/ancs/dummy/0123.asm
new file mode 100644
index 0000000..b32d8bf
--- /dev/null
+++ b/ancs/dummy/0123.asm
@@ -0,0 +1,6 @@
+; Dummy ancestor for testing
+
+dummy 00
+dummy 01
+dummy 02
+dummy 03
diff --git a/ancs/salis-v1/1n.asm b/ancs/salis-v1/1n.asm
deleted file mode 100644
index b441922..0000000
--- a/ancs/salis-v1/1n.asm
+++ /dev/null
@@ -1,5 +0,0 @@
-; Project: Salis
-; Author: Paul Oliver
-; Email: contact@pauloliver.dev
-
-noop
diff --git a/ancs/salis-v1/55b.asm b/ancs/salis-v1/55b.asm
deleted file mode 100644
index 7c830e7..0000000
--- a/ancs/salis-v1/55b.asm
+++ /dev/null
@@ -1,74 +0,0 @@
-; Project: Salis
-; Author: Paul Oliver
-; Email: contact@pauloliver.dev
-
-; Based on the original 55.anc ancestor from salis-v1:
-; https://git.pauloliver.dev/salis-v1/tree/bin/genomes/55.anc
-; This organism replicates bidirectionally.
-
-; begin template
-lokb
-
-; measure gene
-adrb
-keyb
-adrf
-keyb
-nop1
-incn
-nop1
-subn
-nop1
-nop1
-
-; alloc gene
-lokc
-notn
-nop3
-pshn
-nop1
-pshn
-nop3
-ifnz
-nop3
-jmpf
-keyd
-allb
-nop1
-nop2
-jmpf
-keye
-lokd
-allf
-nop1
-nop2
-
-; copy gene
-loke
-load
-nop0
-nop3
-wrte
-nop2
-nop3
-incn
-incn
-nop2
-decn
-nop1
-ifnz
-nop1
-jmpb
-keye
-
-; split gene
-splt
-popn
-nop3
-popn
-nop1
-jmpb
-keyc
-
-; end template
-lokb
diff --git a/arch/dummy/arch.j2.c b/arch/dummy/arch.j2.c
new file mode 100644
index 0000000..d62d411
--- /dev/null
+++ b/arch/dummy/arch.j2.c
@@ -0,0 +1,130 @@
+// Author: Paul Oliver <contact@pauloliver.dev>
+// Project: salis-v3
+
+// Defines a minimal viable architecture for the Salis VM.
+// Useful for debugging and benchmarking. May be used as a template when
+// implementing a new architecture.
+
+{% if args.command in ["bench", "new"] and anc_bytes is defined %}
+void arch_anc_init(struct Core *core) {
+ assert(core);
+
+ {% if arch_vars.mvec_loop %}
+ uint64_t addr = {{ uint64_half }};
+ {% else %}
+ uint64_t addr = 0;
+ {% endif %}
+
+ for (uint64_t i = 0; i < {{ args.clones }}; ++i) {
+ uint64_t addr_clone = addr + (({{ mvec_size }} / {{ args.clones }})) * i;
+
+ struct Proc *panc = proc_fetch(core, i);
+
+ panc->mb0a = addr_clone;
+ panc->mb0s = {{ anc_bytes|length }};
+ panc->ip = addr_clone;
+ panc->sp = addr_clone;
+ }
+}
+{% endif %}
+
+uint64_t arch_proc_mb0_addr(const struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+ return proc_get(core, pix)->mb0a;
+}
+
+uint64_t arch_proc_mb0_size(const struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+ return proc_get(core, pix)->mb0s;
+}
+
+uint64_t arch_proc_mb1_addr(const struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+ return proc_get(core, pix)->mb1a;
+}
+
+uint64_t arch_proc_mb1_size(const struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+ return proc_get(core, pix)->mb1s;
+}
+
+uint64_t arch_proc_ip_addr(const struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+ return proc_get(core, pix)->ip;
+}
+
+uint64_t arch_proc_sp_addr(const struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+ return proc_get(core, pix)->sp;
+}
+
+uint64_t arch_proc_slice(const struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ (void)core;
+ (void)pix;
+
+ return 1;
+}
+
+void arch_on_proc_kill(struct Core *core) {
+ assert(core);
+ assert(core->pnum > 1);
+
+ (void)core;
+}
+
+void arch_proc_step(struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ (void)core;
+ (void)pix;
+
+ return;
+}
+
+{% if not args.optimized %}
+void arch_validate_proc(const struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ (void)core;
+ (void)pix;
+
+ assert(true);
+}
+{% endif %}
+
+wchar_t arch_symbol(uint8_t inst) {
+ switch (inst) {
+ {% for i in arch_vars.inst_set %}
+ case {{ loop.index0 }}: return L'{{ i[1] }}';
+ {% endfor %}
+ }
+}
+
+const char *arch_mnemonic(uint8_t inst) {
+ switch (inst) {
+ {% for i in arch_vars.inst_set %}
+ case {{ loop.index0 }}: return "{{ i[0]|join(' ') }}";
+ {% endfor %}
+ }
+}
+
+{% if data_push_path is defined %}
+void arch_push_data_header() {
+ assert(g_sim_data);
+}
+
+void arch_push_data_line() {
+ assert(g_sim_data);
+}
+{% endif %}
diff --git a/arch/dummy/arch_vars.py b/arch/dummy/arch_vars.py
new file mode 100644
index 0000000..3a18200
--- /dev/null
+++ b/arch/dummy/arch_vars.py
@@ -0,0 +1,21 @@
+core_fields = []
+mvec_loop = True
+
+proc_fields = [
+ ("uint64_t", "ip"),
+ ("uint64_t", "sp"),
+ ("uint64_t", "mb0a"),
+ ("uint64_t", "mb0s"),
+ ("uint64_t", "mb1a"),
+ ("uint64_t", "mb1s"),
+]
+
+inst_set = [
+ (["dummy", f"{i:02x}"], symbol)
+ for i, symbol in enumerate(
+ "⠀⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇⠈⠉⠊⠋⠌⠍⠎⠏⡈⡉⡊⡋⡌⡍⡎⡏⠐⠑⠒⠓⠔⠕⠖⠗⡐⡑⡒⡓⡔⡕⡖⡗⠘⠙⠚⠛⠜⠝⠞⠟⡘⡙⡚⡛⡜⡝⡞⡟"
+ "⠠⠡⠢⠣⠤⠥⠦⠧⡠⡡⡢⡣⡤⡥⡦⡧⠨⠩⠪⠫⠬⠭⠮⠯⡨⡩⡪⡫⡬⡭⡮⡯⠰⠱⠲⠳⠴⠵⠶⠷⡰⡱⡲⡳⡴⡵⡶⡷⠸⠹⠺⠻⠼⠽⠾⠿⡸⡹⡺⡻⡼⡽⡾⡿"
+ "⢀⢁⢂⢃⢄⢅⢆⢇⣀⣁⣂⣃⣄⣅⣆⣇⢈⢉⢊⢋⢌⢍⢎⢏⣈⣉⣊⣋⣌⣍⣎⣏⢐⢑⢒⢓⢔⢕⢖⢗⣐⣑⣒⣓⣔⣕⣖⣗⢘⢙⢚⢛⢜⢝⢞⢟⣘⣙⣚⣛⣜⣝⣞⣟"
+ "⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯⢰⢱⢲⢳⢴⢵⢶⢷⣰⣱⣲⣳⣴⣵⣶⣷⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿"
+ )
+]
diff --git a/arch/salis-v1/arch.j2.c b/arch/salis-v1/arch.j2.c
new file mode 100644
index 0000000..aaa5284
--- /dev/null
+++ b/arch/salis-v1/arch.j2.c
@@ -0,0 +1,890 @@
+// Author: Paul Oliver <contact@pauloliver.dev>
+// Project: Salis
+
+// Based on the original salis-v1 VM architecture:
+// https://git.pauloliver.dev/salis-v1/about/
+
+{% set inst_count = arch_vars.inst_set|length %}
+
+enum {
+ {% for i in arch_vars.inst_set %}
+ {{ i[0]|join(' ') }},
+ {% endfor %}
+};
+
+{% if args.command in ["bench", "new"] and anc_bytes is defined %}
+void arch_anc_init(struct Core *core) {
+ assert(core);
+
+ {% if arch_vars.mvec_loop %}
+ uint64_t addr = {{ uint64_half }};
+ {% else %}
+ uint64_t addr = 0;
+ {% endif %}
+
+ for (uint64_t i = 0; i < {{ args.clones }}; ++i) {
+ uint64_t addr_clone = addr + (({{ mvec_size }} / {{ args.clones }})) * i;
+
+ struct Proc *panc = proc_fetch(core, i);
+
+ panc->mb0a = addr_clone;
+ panc->mb0s = {{ anc_bytes|length }};
+ panc->ip = addr_clone;
+ panc->sp = addr_clone;
+ }
+}
+{% endif %}
+
+uint64_t arch_proc_mb0_addr(const struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+ return proc_get(core, pix)->mb0a;
+}
+
+uint64_t arch_proc_mb0_size(const struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+ return proc_get(core, pix)->mb0s;
+}
+
+uint64_t arch_proc_mb1_addr(const struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+ return proc_get(core, pix)->mb1a;
+}
+
+uint64_t arch_proc_mb1_size(const struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+ return proc_get(core, pix)->mb1s;
+}
+
+uint64_t arch_proc_ip_addr(const struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+ return proc_get(core, pix)->ip;
+}
+
+uint64_t arch_proc_sp_addr(const struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+ return proc_get(core, pix)->sp;
+}
+
+uint64_t arch_proc_slice(const struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ (void)core;
+ (void)pix;
+
+ return 1;
+}
+
+void _free_memory_block(struct Core *core, uint64_t addr, uint64_t size) {
+ assert(core);
+ assert(size);
+
+ for (uint64_t i = 0; i < size; ++i) {
+ mvec_free(core, addr + i);
+ }
+}
+
+void arch_on_proc_kill(struct Core *core) {
+ assert(core);
+ assert(core->pnum > 1);
+
+ struct Proc *pfst = proc_fetch(core, core->pfst);
+
+ _free_memory_block(core, pfst->mb0a, pfst->mb0s);
+
+ if (pfst->mb1s) {
+ _free_memory_block(core, pfst->mb1a, pfst->mb1s);
+ }
+
+ memcpy(pfst, &g_dead_proc, sizeof(struct Proc));
+}
+
+uint8_t _get_inst(const struct Core *core, uint64_t addr) {
+ assert(core);
+
+ return mvec_get_inst(core, addr) % {{ inst_count }};
+}
+
+void _increment_ip(struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ struct Proc *proc = proc_fetch(core, pix);
+
+ proc->ip++;
+ proc->sp = proc->ip;
+}
+
+bool _is_between(uint8_t inst, uint8_t lo, uint8_t hi) {
+ assert(inst < {{ inst_count }});
+ assert(lo < {{ inst_count }});
+ assert(hi < {{ inst_count }});
+ assert(lo < hi);
+
+ return (inst >= lo) && (inst <= hi);
+}
+
+bool _is_key(uint8_t inst) {
+ assert(inst < {{ inst_count }});
+
+ return _is_between(inst, keya, keyp);
+}
+
+bool _is_lock(uint8_t inst) {
+ assert(inst < {{ inst_count }});
+
+ return _is_between(inst, loka, lokp);
+}
+
+bool _is_rmod(uint8_t inst) {
+ assert(inst < {{ inst_count }});
+
+ return _is_between(inst, nop0, nop3);
+}
+
+bool _key_lock_match(uint8_t key, uint8_t lock) {
+ assert(key < {{ inst_count }});
+ assert(lock < {{ inst_count }});
+ assert(_is_key(key));
+
+ return (key - keya) == (lock - loka);
+}
+
+bool _seek(struct Core *core, uint64_t pix, bool fwrd) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ struct Proc *proc = proc_fetch(core, pix);
+ uint8_t next = _get_inst(core, proc->ip + 1);
+
+ if (!_is_key(next)) {
+ _increment_ip(core, pix);
+ return false;
+ }
+
+ uint8_t spin = _get_inst(core, proc->sp);
+
+ if (_key_lock_match(next, spin)) {
+ return true;
+ }
+
+ if (fwrd) {
+ proc->sp++;
+ } else {
+ proc->sp--;
+ }
+
+ return false;
+}
+
+void _jump(struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ struct Proc *proc = proc_fetch(core, pix);
+
+ {% if not args.optimized %}
+ uint8_t next = _get_inst(core, proc->ip + 1);
+ uint8_t spin = _get_inst(core, proc->sp);
+ assert(_is_key(next));
+ assert(_is_lock(spin));
+ assert(_key_lock_match(next, spin));
+ {% endif %}
+
+ proc->ip = proc->sp;
+}
+
+void _get_reg_addr_list(struct Core *core, uint64_t pix, uint64_t **rlist, int rcount, bool offset) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ assert(rlist);
+ assert(rcount);
+ assert(rcount < 4);
+
+ struct Proc *proc = proc_fetch(core, pix);
+ uint64_t madr = proc->ip + (offset ? 2 : 1);
+
+ for (int i = 0; i < rcount; ++i) {
+ rlist[i] = &proc->r0x;
+ }
+
+ for (int i = 0; i < rcount; ++i) {
+ uint64_t mnxt = madr + i;
+ uint8_t mins = _get_inst(core, mnxt);
+
+ if (!_is_rmod(mins)) {
+ break;
+ }
+
+ switch (mins) {
+ case nop0:
+ rlist[i] = &proc->r0x;
+ break;
+ case nop1:
+ rlist[i] = &proc->r1x;
+ break;
+ case nop2:
+ rlist[i] = &proc->r2x;
+ break;
+ case nop3:
+ rlist[i] = &proc->r3x;
+ break;
+ }
+ }
+}
+
+void _addr(struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ struct Proc *proc = proc_fetch(core, pix);
+ uint64_t *reg;
+
+ {% if not args.optimized %}
+ uint8_t next = _get_inst(core, proc->ip + 1);
+ uint8_t spin = _get_inst(core, proc->sp);
+ assert(_is_key(next));
+ assert(_is_lock(spin));
+ assert(_key_lock_match(next, spin));
+ {% endif %}
+
+ _get_reg_addr_list(core, pix, &reg, 1, true);
+ *reg = proc->sp;
+
+ _increment_ip(core, pix);
+}
+
+void _ifnz(struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ struct Proc *proc = proc_fetch(core, pix);
+ uint64_t *reg;
+
+ _get_reg_addr_list(core, pix, &reg, 1, false);
+
+ uint64_t jmod = _is_rmod(_get_inst(core, proc->ip + 1)) ? 1 : 0;
+ uint64_t rmod = *reg ? 1 : 2;
+
+ proc->ip += jmod + rmod;
+ proc->sp = proc->ip;
+}
+
+void _free_child_memory_of(struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ struct Proc *proc = proc_fetch(core, pix);
+
+ assert(proc->mb1s);
+
+ _free_memory_block(core, proc->mb1a, proc->mb1s);
+
+ proc->mb1a = 0;
+ proc->mb1s = 0;
+}
+
+// Organisms allocate new memory blocks by means of their seek pointer (sp),
+// which sweeps memory 1 byte per simulation step, extending the block as it goes.
+// In case allocated memory is found mid-way, current allocation is discarded.
+void _alloc(struct Core *core, uint64_t pix, bool fwrd) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ struct Proc *proc = proc_fetch(core, pix);
+ uint64_t *regs[2];
+
+ _get_reg_addr_list(core, pix, regs, 2, false);
+
+ uint64_t bsize = *regs[0];
+
+ // Do nothing if block-size is zero
+ if (!bsize) {
+ _increment_ip(core, pix);
+ return;
+ }
+
+ // Do nothing if seek pointer is not adjacent to allocated memory block
+ // This is an error condition
+ if (proc->mb1s) {
+ uint64_t exp_addr = proc->mb1a;
+
+ if (fwrd) {
+ exp_addr += proc->mb1s;
+ } else {
+ exp_addr--;
+ }
+
+ if (proc->sp != exp_addr) {
+ _increment_ip(core, pix);
+ return;
+ }
+ }
+
+ // Allocation was successful
+ // Store block address on register
+ if (proc->mb1s == bsize) {
+ _increment_ip(core, pix);
+ *regs[1] = proc->mb1a;
+ return;
+ }
+
+ // Seek pointer collided with another allocated block
+ // Discard and keep trying
+ if (mvec_is_alloc(core, proc->sp)) {
+ if (proc->mb1s) {
+ _free_child_memory_of(core, pix);
+ }
+
+ if (fwrd) {
+ proc->sp++;
+ } else {
+ proc->sp--;
+ }
+
+ return;
+ }
+
+ // Free (non-allocated) byte found
+ // Enlarge child block 1 byte
+ mvec_alloc(core, proc->sp);
+
+ if (!proc->mb1s || !fwrd) {
+ proc->mb1a = proc->sp;
+ }
+
+ proc->mb1s++;
+
+ // Move seek pointer
+ if (fwrd) {
+ proc->sp++;
+ } else {
+ proc->sp--;
+ }
+}
+
+void _bswap(struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ struct Proc *proc = proc_fetch(core, pix);
+
+ if (proc->mb1s) {
+ uint64_t tmpa = proc->mb0a;
+ uint64_t tmps = proc->mb0s;
+
+ proc->mb0a = proc->mb1a;
+ proc->mb0s = proc->mb1s;
+ proc->mb1a = tmpa;
+ proc->mb1s = tmps;
+ }
+
+ _increment_ip(core, pix);
+}
+
+void _bclear(struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ struct Proc *proc = proc_fetch(core, pix);
+
+ if (proc->mb1s) {
+ _free_child_memory_of(core, pix);
+ }
+
+ _increment_ip(core, pix);
+}
+
+void _split(struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ struct Proc *proc = proc_fetch(core, pix);
+
+ if (proc->mb1s) {
+ struct Proc child = {0};
+
+ child.ip = proc->mb1a;
+ child.sp = proc->mb1a;
+ child.mb0a = proc->mb1a;
+ child.mb0s = proc->mb1s;
+
+ proc->mb1a = 0;
+ proc->mb1s = 0;
+
+ // A new organism is born :)
+ proc_new(core, &child);
+ } else {
+ assert(!proc->mb1a);
+ }
+
+ _increment_ip(core, pix);
+}
+
+void _3rop(struct Core *core, uint64_t pix, uint8_t inst) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ uint64_t *regs[3];
+
+ _get_reg_addr_list(core, pix, regs, 3, false);
+
+ // Organisms can do arithmetic using any sequence of 3 registers
+ switch (inst) {
+ case addn:
+ *regs[0] = *regs[1] + *regs[2];
+ break;
+ case subn:
+ *regs[0] = *regs[1] - *regs[2];
+ break;
+ case muln:
+ *regs[0] = *regs[1] * *regs[2];
+ break;
+ case divn:
+ // Division by zero
+ // Do nothing
+ if (*regs[2]) {
+ *regs[0] = *regs[1] / *regs[2];
+ }
+
+ break;
+ default:
+ assert(false);
+ }
+
+ _increment_ip(core, pix);
+}
+
+void _1rop(struct Core *core, uint64_t pix, uint8_t inst) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ uint64_t *reg;
+
+ _get_reg_addr_list(core, pix, &reg, 1, false);
+
+ switch (inst) {
+ case incn:
+ (*reg)++;
+ break;
+ case decn:
+ (*reg)--;
+ break;
+ case notn:
+ *reg = !(*reg);
+ break;
+ case shfl:
+ *reg <<= 1;
+ break;
+ case shfr:
+ *reg >>= 1;
+ break;
+ case zero:
+ *reg = 0;
+ break;
+ case unit:
+ *reg = 1;
+ break;
+ default:
+ assert(false);
+ }
+
+ _increment_ip(core, pix);
+}
+
+void _push(struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ struct Proc *proc = proc_fetch(core, pix);
+ uint64_t *reg;
+
+ _get_reg_addr_list(core, pix, &reg, 1, false);
+
+ proc->s7 = proc->s6;
+ proc->s6 = proc->s5;
+ proc->s5 = proc->s4;
+ proc->s4 = proc->s3;
+ proc->s3 = proc->s2;
+ proc->s2 = proc->s1;
+ proc->s1 = proc->s0;
+ proc->s0 = *reg;
+
+ _increment_ip(core, pix);
+}
+
+void _pop(struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ struct Proc *proc = proc_fetch(core, pix);
+ uint64_t *reg;
+
+ _get_reg_addr_list(core, pix, &reg, 1, false);
+
+ *reg = proc->s0;
+ proc->s0 = proc->s1;
+ proc->s1 = proc->s2;
+ proc->s2 = proc->s3;
+ proc->s3 = proc->s4;
+ proc->s4 = proc->s5;
+ proc->s5 = proc->s6;
+ proc->s6 = proc->s7;
+ proc->s7 = 0;
+
+ _increment_ip(core, pix);
+}
+
+int _sp_dir(uint64_t src, uint64_t dst) {
+ if (src == dst) {
+ return 0;
+ } else if (src - dst <= dst - src) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+void _load(struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ struct Proc *proc = proc_fetch(core, pix);
+ uint64_t *regs[2];
+
+ _get_reg_addr_list(core, pix, regs, 2, false);
+
+ int sp_dir = _sp_dir(proc->sp, *regs[0]);
+
+ if (sp_dir == 1) {
+ proc->sp++;
+ } else if (sp_dir == -1) {
+ proc->sp--;
+ } else {
+ *regs[1] = mvec_get_inst(core, *regs[0]);
+ _increment_ip(core, pix);
+ }
+}
+
+bool _is_writeable_by(const struct Core *core, uint64_t addr, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ return !mvec_is_alloc(core, addr) || mvec_is_proc_owner(core, addr, pix);
+}
+
+void _write(struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ struct Proc *proc = proc_fetch(core, pix);
+ uint64_t *regs[2];
+
+ _get_reg_addr_list(core, pix, regs, 2, false);
+
+ int sp_dir = _sp_dir(proc->sp, *regs[0]);
+
+ if (sp_dir == 1) {
+ proc->sp++;
+ } else if (sp_dir == -1) {
+ proc->sp--;
+ } else {
+ if (_is_writeable_by(core, *regs[0], pix)) {
+ mvec_set_inst(core, *regs[0], *regs[1] % {{ inst_cap }});
+ }
+
+ _increment_ip(core, pix);
+ }
+}
+
+void _2rop(struct Core *core, uint64_t pix, uint8_t inst) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ uint64_t *regs[2];
+
+ _get_reg_addr_list(core, pix, regs, 2, false);
+
+ switch (inst) {
+ case dupl:
+ *regs[1] = *regs[0];
+ break;
+ case swap:
+ {
+ uint64_t tmp = *regs[0];
+ *regs[0] = *regs[1];
+ *regs[1] = tmp;
+ }
+
+ break;
+ default:
+ assert(false);
+ }
+
+ _increment_ip(core, pix);
+}
+
+void arch_proc_step(struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ struct Proc *proc = proc_fetch(core, pix);
+ uint8_t inst = _get_inst(core, proc->ip);
+
+ switch (inst) {
+ case jmpb:
+ if (_seek(core, pix, false)) {
+ _jump(core, pix);
+ }
+
+ break;
+ case jmpf:
+ if (_seek(core, pix, true)) {
+ _jump(core, pix);
+ }
+
+ break;
+ case adrb:
+ if (_seek(core, pix, false)) {
+ _addr(core, pix);
+ }
+
+ break;
+ case adrf:
+ if (_seek(core, pix, true)) {
+ _addr(core, pix);
+ }
+
+ break;
+ case ifnz:
+ _ifnz(core, pix);
+ break;
+ case allb:
+ _alloc(core, pix, false);
+ break;
+ case allf:
+ _alloc(core, pix, true);
+ break;
+ case bswp:
+ _bswap(core, pix);
+ break;
+ case bclr:
+ _bclear(core, pix);
+ break;
+ case splt:
+ _split(core, pix);
+ break;
+ case addn:
+ case subn:
+ case muln:
+ case divn:
+ _3rop(core, pix, inst);
+ break;
+ case incn:
+ case decn:
+ case notn:
+ case shfl:
+ case shfr:
+ case zero:
+ case unit:
+ _1rop(core, pix, inst);
+ break;
+ case pshn:
+ _push(core, pix);
+ break;
+ case popn:
+ _pop(core, pix);
+ break;
+ case load:
+ _load(core, pix);
+ break;
+ case wrte:
+ _write(core, pix);
+ break;
+ case dupl:
+ case swap:
+ _2rop(core, pix, inst);
+ break;
+ default:
+ _increment_ip(core, pix);
+ break;
+ }
+
+ return;
+}
+
+{% if not args.optimized %}
+void arch_validate_proc(const struct Core *core, uint64_t pix) {
+ assert(core);
+
+ const struct Proc *proc = proc_get(core, pix);
+
+ assert(proc->mb0s);
+
+ if (proc->mb1a) {
+ assert(proc->mb1s);
+ }
+
+ for (uint64_t i = 0; i < proc->mb0s; ++i) {
+ uint64_t addr = proc->mb0a + i;
+ assert(mvec_is_alloc(core, addr));
+ assert(mvec_is_proc_owner(core, addr, pix));
+ }
+
+ for (uint64_t i = 0; i < proc->mb1s; ++i) {
+ uint64_t addr = proc->mb1a + i;
+ assert(mvec_is_alloc(core, addr));
+ assert(mvec_is_proc_owner(core, addr, pix));
+ }
+}
+{% endif %}
+
+wchar_t arch_symbol(uint8_t inst) {
+ switch (inst % {{ inst_count }}) {
+ {% for i in arch_vars.inst_set %}
+ case {{ i[0]|join(' ') }}: return L'{{ i[1] }}';
+ {% endfor %}
+ }
+
+ assert(false);
+ return L'\0';
+}
+
+const char *arch_mnemonic(uint8_t inst) {
+ switch (inst % {{ inst_count }}) {
+ {% for i in arch_vars.inst_set %}
+ case {{ i[0]|join(' ') }}: return "{{ i[0]|join(' ') }}";
+ {% endfor %}
+ }
+
+ assert(false);
+ return NULL;
+}
+
+{% if data_push_path is defined %}
+void arch_push_data_header() {
+ assert(g_sim_data);
+
+ const char *sql = (
+ "create table data("
+ "step int not null, "
+ {% for i in range(args.cores) %}
+ "cycl_{{ i }} int not null, "
+ "mall_{{ i }} int not null, "
+ "pnum_{{ i }} int not null, "
+ "pfst_{{ i }} int not null, "
+ "plst_{{ i }} int not null, "
+ "avrg_mb0s_{{ i }} real not null, "
+ "avrg_mb1s_{{ i }} real not null, "
+ {% set outer_loop = loop %}
+ {% for j in arch_vars.inst_set %}
+ "inst_{{ j[0]|join(' ') }}_{{ i }} int not null{% if not outer_loop.last or not loop.last %},{% endif %} "
+ {% endfor %}
+ {% endfor %}
+ ");"
+ );
+
+ {% set sql_call %}sqlite3_exec(g_sim_data, sql, NULL, NULL, NULL){% endset %}
+
+ {% if not args.optimized %}
+ assert({{ sql_call }} == 0);
+ {% else %}
+ {{ sql_call }};
+ {% endif %}
+}
+
+void arch_push_data_line() {
+ assert(g_sim_data);
+
+ // Gather data on all cores
+ uint64_t inst_total[{{ args.cores }}][{{ inst_count }}] = { 0 };
+
+ double avrg_mb0s[{{ args.cores }}] = { 0 };
+ double avrg_mb1s[{{ args.cores }}] = { 0 };
+
+ for (int i = 0; i < {{ args.cores }}; ++i) {
+ const struct Core *core = &g_cores[i];
+
+ // Count number of instructions
+ for (uint64_t j = 0; j < {{ mvec_size }}; ++j) {
+ uint8_t inst = mvec_get_inst(core, j) % {{ inst_count }};
+ ++inst_total[i][inst];
+ }
+
+ // Avregare memory block sizes
+ for (uint64_t j = core->pfst; j <= core->plst; ++j) {
+ const struct Proc *proc = proc_get(core, j);
+
+ avrg_mb0s[i] += (double)proc->mb0s;
+ avrg_mb1s[i] += (double)proc->mb1s;
+ }
+
+ avrg_mb0s[i] /= core->pnum;
+ avrg_mb1s[i] /= core->pnum;
+ }
+
+ // Insert new row
+ char *sql = NULL;
+
+ asprintf(
+ &sql,
+ "insert into data ("
+ "step, "
+ {% for i in range(args.cores) %}
+ "cycl_{{ i }}, "
+ "mall_{{ i }}, "
+ "pnum_{{ i }}, "
+ "pfst_{{ i }}, "
+ "plst_{{ i }}, "
+ "avrg_mb0s_{{ i }}, "
+ "avrg_mb1s_{{ i }}, "
+ {% set outer_loop = loop %}
+ {% for j in arch_vars.inst_set %}
+ "inst_{{ j[0]|join(' ') }}_{{ i }}{% if not outer_loop.last or not loop.last %},{% endif %} "
+ {% endfor %}
+ {% endfor %}
+ ") values ("
+ "%ld, "
+ {% for i in range(args.cores) %}
+ "%ld, %ld, %ld, %ld, %ld, %f, %f, "
+ {% set outer_loop = loop %}
+ {% for _ in arch_vars.inst_set %}
+ "%ld{% if not outer_loop.last or not loop.last %},{% endif %} "
+ {% endfor %}
+ {% endfor %}
+ ");",
+ g_steps,
+ {% for i in range(args.cores) %}
+ g_cores[{{ i }}].cycl,
+ g_cores[{{ i }}].mall,
+ g_cores[{{ i }}].pnum,
+ g_cores[{{ i }}].pfst,
+ g_cores[{{ i }}].plst,
+ avrg_mb0s[{{ i }}],
+ avrg_mb1s[{{ i }}],
+ {% set outer_loop = loop %}
+ {% for j in arch_vars.inst_set %}
+ inst_total[{{ i }}][{{ j[0]|join(' ') }}]{% if not outer_loop.last or not loop.last %},{% endif %} // inst
+ {% endfor %}
+ {% endfor %}
+ );
+
+ {% set sql_call %}sqlite3_exec(g_sim_data, sql, NULL, NULL, NULL){% endset %}
+
+ {% if not args.optimized %}
+ assert({{ sql_call }} == 0);
+ {% else %}
+ {{ sql_call }};
+ {% endif %}
+
+ // Free query string returned by 'asprintf()'
+ free(sql);
+}
+{% endif %}
diff --git a/arch/salis-v1/arch_vars.py b/arch/salis-v1/arch_vars.py
new file mode 100644
index 0000000..07301c3
--- /dev/null
+++ b/arch/salis-v1/arch_vars.py
@@ -0,0 +1,98 @@
+core_fields = []
+mvec_loop = False
+
+proc_fields = [
+ ("uint64_t", "ip"),
+ ("uint64_t", "sp"),
+ ("uint64_t", "mb0a"),
+ ("uint64_t", "mb0s"),
+ ("uint64_t", "mb1a"),
+ ("uint64_t", "mb1s"),
+ ("uint64_t", "r0x"),
+ ("uint64_t", "r1x"),
+ ("uint64_t", "r2x"),
+ ("uint64_t", "r3x"),
+ ("uint64_t", "s0"),
+ ("uint64_t", "s1"),
+ ("uint64_t", "s2"),
+ ("uint64_t", "s3"),
+ ("uint64_t", "s4"),
+ ("uint64_t", "s5"),
+ ("uint64_t", "s6"),
+ ("uint64_t", "s7"),
+]
+
+# salis-v1 instruction set
+inst_set = [
+ (["noop"], " "),
+ (["nop0"], "0"),
+ (["nop1"], "1"),
+ (["nop2"], "2"),
+ (["nop3"], "3"),
+ # -------------
+ (["jmpb"], "("),
+ (["jmpf"], ")"),
+ (["adrb"], "["),
+ (["adrf"], "]"),
+ (["ifnz"], "?"),
+ # -------------
+ (["allb"], "{"),
+ (["allf"], "}"),
+ (["bswp"], "%"),
+ (["bclr"], "|"),
+ (["splt"], "$"),
+ # -------------
+ (["addn"], "+"),
+ (["subn"], "-"),
+ (["muln"], "*"),
+ (["divn"], "/"),
+ (["incn"], "^"),
+ (["decn"], "v"),
+ (["notn"], "!"),
+ (["shfl"], "<"),
+ (["shfr"], ">"),
+ (["zero"], "z"),
+ (["unit"], "u"),
+ # -------------
+ (["pshn"], "#"),
+ (["popn"], "~"),
+ # -------------
+ (["load"], "."),
+ (["wrte"], ":"),
+ (["dupl"], "="),
+ (["swap"], "x"),
+ # -------------
+ (["keya"], "a"),
+ (["keyb"], "b"),
+ (["keyc"], "c"),
+ (["keyd"], "d"),
+ (["keye"], "e"),
+ (["keyf"], "f"),
+ (["keyg"], "g"),
+ (["keyh"], "h"),
+ (["keyi"], "i"),
+ (["keyj"], "j"),
+ (["keyk"], "k"),
+ (["keyl"], "l"),
+ (["keym"], "m"),
+ (["keyn"], "n"),
+ (["keyo"], "o"),
+ (["keyp"], "p"),
+ # -------------
+ (["loka"], "A"),
+ (["lokb"], "B"),
+ (["lokc"], "C"),
+ (["lokd"], "D"),
+ (["loke"], "E"),
+ (["lokf"], "F"),
+ (["lokg"], "G"),
+ (["lokh"], "H"),
+ (["loki"], "I"),
+ (["lokj"], "J"),
+ (["lokk"], "K"),
+ (["lokl"], "L"),
+ (["lokm"], "M"),
+ (["lokn"], "N"),
+ (["loko"], "O"),
+ (["lokp"], "P"),
+]
diff --git a/src/bench.c b/bench.j2.c
index eb7e0e5..a776892 100644
--- a/src/bench.c
+++ b/bench.j2.c
@@ -1,27 +1,20 @@
+// Author: Paul Oliver <contact@pauloliver.dev>
// 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
+// Simple benchmark test helps measure simulation speed.
+// Steps the simulation N times and prints part of the simulator's state.
int main() {
printf("Salis Benchmark Test\n\n");
- salis_init("", SEED);
- salis_step(BENCH_STEPS);
+ salis_init();
+ salis_step({{ args.steps }});
- printf("seed => %#lx\n", SEED);
+ printf("seed => %#lx\n", {{ args.seed }});
printf("g_steps => %#lx\n", g_steps);
printf("g_syncs => %#lx\n", g_syncs);
- for (int i = 0; i < CORE_COUNT; ++i) {
+ for (int i = 0; i < {{ args.cores }}; ++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]);
@@ -34,7 +27,7 @@ int main() {
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 cycl => %#lx\n", i, g_cores[i].cycl);
printf("core %d ivpt => %#lx\n", i, g_cores[i].ivpt);
putchar('\n');
diff --git a/core.j2.c b/core.j2.c
new file mode 100644
index 0000000..ec158dc
--- /dev/null
+++ b/core.j2.c
@@ -0,0 +1,838 @@
+// Author: Paul Oliver <contact@pauloliver.dev>
+// Project: salis-v3
+
+// Core template of the salis simulator.
+// Different architectures and UIs can be attached in order to
+// create a streamlined source file.
+
+{% for include in includes|sort %}
+#include <{{ include }}>
+{% endfor %}
+
+// Each architecture defines its own process type
+struct Proc {
+ {% for type, val in arch_vars.proc_fields %}
+ {{ type }} {{ val }};
+ {% endfor %}
+};
+
+// Simulation core
+// Each core runs on a separate thread
+// Core synchronization and IPC occurs at set intervals
+struct Core {
+ uint64_t cycl;
+ uint64_t mall;
+ uint64_t muta[4];
+
+ uint64_t pnum;
+ uint64_t pcap;
+ uint64_t pfst;
+ uint64_t plst;
+ uint64_t pcur;
+ uint64_t psli;
+
+ thrd_t thrd;
+ uint64_t thrd_idx;
+
+ uint64_t ivpt;
+ uint64_t *ivav;
+ uint8_t *iviv;
+
+ // Architectures may provide custom fields
+ {% for type, val in arch_vars.core_fields %}
+ {{ type }} {{ val }};
+ {% endfor %}
+
+ struct Proc *pvec;
+ uint8_t mvec[{{ mvec_size }}];
+ uint8_t tgap[{{ thread_gap }}];
+};
+
+// Globals
+struct Core g_cores[{{ args.cores }}];
+uint64_t g_steps;
+uint64_t g_syncs;
+const struct Proc g_dead_proc;
+
+{% if args.command in ["load", "new"] %}
+char g_asav_pbuf[{{ auto_save_name_len }}];
+{% endif %}
+
+{% if data_push_path is defined %}
+sqlite3 *g_sim_data;
+{% endif %}
+
+// Forward declarations
+// Each architecture must define these
+{% if args.command in ["bench", "new"] and anc_bytes is defined %}
+void arch_anc_init(struct Core *core);
+{% endif %}
+
+uint64_t arch_proc_mb0_addr(const struct Core *core, uint64_t pix);
+uint64_t arch_proc_mb0_size(const struct Core *core, uint64_t pix);
+uint64_t arch_proc_mb1_addr(const struct Core *core, uint64_t pix);
+uint64_t arch_proc_mb1_size(const struct Core *core, uint64_t pix);
+uint64_t arch_proc_ip_addr(const struct Core *core, uint64_t pix);
+uint64_t arch_proc_sp_addr(const struct Core *core, uint64_t pix);
+uint64_t arch_proc_slice(const struct Core *core, uint64_t pix);
+void arch_on_proc_kill(struct Core *core);
+void arch_proc_step(struct Core *core, uint64_t pix);
+
+{% if not args.optimized %}
+void arch_validate_proc(const struct Core *core, uint64_t pix);
+{% endif %}
+
+wchar_t arch_symbol(uint8_t inst);
+const char *arch_mnemonic(uint8_t inst);
+
+{% if data_push_path is defined %}
+void arch_push_data_header();
+void arch_push_data_line();
+{% endif %}
+
+// ----------------------------------------------------------------------------
+// Memory vector functions
+// ----------------------------------------------------------------------------
+{% if arch_vars.mvec_loop %}
+uint64_t mvec_loop(uint64_t addr) {
+ return addr % {{ mvec_size }};
+}
+{% endif %}
+
+bool mvec_is_alloc(const struct Core *core, uint64_t addr) {
+ assert(core);
+
+ {% if arch_vars.mvec_loop %}
+ return core->mvec[mvec_loop(addr)] & {{ mall_flag }} ? true : false;
+ {% else %}
+ if (addr < {{ mvec_size }}) {
+ return core->mvec[addr] & {{ mall_flag }} ? true : false;
+ } else {
+ return true;
+ }
+ {% endif %}
+}
+
+void mvec_alloc(struct Core *core, uint64_t addr) {
+ assert(core);
+ assert(!mvec_is_alloc(core, addr));
+ {% if arch_vars.mvec_loop %}
+ core->mvec[mvec_loop(addr)] |= {{ mall_flag }};
+ {% else %}
+ assert(addr < {{ mvec_size }});
+ core->mvec[addr] |= {{ mall_flag }};
+ {% endif %}
+ core->mall++;
+}
+
+void mvec_free(struct Core *core, uint64_t addr) {
+ assert(core);
+ assert(mvec_is_alloc(core, addr));
+ {% if arch_vars.mvec_loop %}
+ core->mvec[mvec_loop(addr)] ^= {{ mall_flag }};
+ {% else %}
+ assert(addr < {{ mvec_size }});
+ core->mvec[addr] ^= {{ mall_flag }};
+ {% endif %}
+ core->mall--;
+}
+
+uint8_t mvec_get_byte(const struct Core *core, uint64_t addr) {
+ assert(core);
+ {% if arch_vars.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 arch_vars.mvec_loop %}
+ return core->mvec[mvec_loop(addr)] & {{ inst_mask }};
+ {% else %}
+ if (addr < {{ mvec_size }}) {
+ return core->mvec[addr] & {{ inst_mask }};
+ } else {
+ return 0;
+ }
+ {% endif %}
+}
+
+void mvec_set_inst(struct Core *core, uint64_t addr, uint8_t inst) {
+ assert(core);
+ assert(inst < {{ inst_cap}});
+ {% if arch_vars.mvec_loop %}
+ core->mvec[mvec_loop(addr)] &= {{ mall_flag }};
+ core->mvec[mvec_loop(addr)] |= inst;
+ {% else %}
+ assert(addr < {{ mvec_size }});
+ core->mvec[addr] &= {{ mall_flag }};
+ core->mvec[addr] |= inst;
+ {% endif %}
+}
+
+{% if args.muta_flip %}
+void mvec_flip_bit(struct Core *core, uint64_t addr, int bit) {
+ assert(core);
+ assert(bit < 8);
+ core->mvec[addr] ^= (1 << bit) & {{ inst_mask }};
+}
+{% endif %}
+
+bool mvec_proc_is_live(const struct Core *core, uint64_t pix) {
+ assert(core);
+
+ return pix >= core->pfst && pix <= core->plst;
+}
+
+bool mvec_is_proc_owner(const struct Core *core, uint64_t addr, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ uint64_t mb0a = arch_proc_mb0_addr(core, pix);
+ uint64_t mb0s = arch_proc_mb0_size(core, pix);
+
+ if (((addr - mb0a) % {{ mvec_size }}) < mb0s) {
+ return true;
+ }
+
+ uint64_t mb1a = arch_proc_mb1_addr(core, pix);
+ uint64_t mb1s = arch_proc_mb1_size(core, pix);
+
+ if (((addr - mb1a) % {{ mvec_size }}) < mb1s) {
+ return true;
+ }
+
+ return false;
+}
+
+uint64_t mvec_get_owner(const struct Core *core, uint64_t addr) {
+ assert(core);
+ assert(mvec_is_alloc(core, addr));
+
+ for (uint64_t pix = core->pfst; pix <= core->plst; ++pix) {
+ if (mvec_is_proc_owner(core, addr, pix)) {
+ return pix;
+ }
+ }
+
+ assert(false);
+ return -1;
+}
+
+// ----------------------------------------------------------------------------
+// Mutator functions
+// ----------------------------------------------------------------------------
+{% if args.command in ["bench", "new"] %}
+uint64_t muta_smix(uint64_t *seed) {
+ assert(seed);
+
+ uint64_t next = (*seed += 0x9e3779b97f4a7c15);
+ next = (next ^ (next >> 30)) * 0xbf58476d1ce4e5b9;
+ next = (next ^ (next >> 27)) * 0x94d049bb133111eb;
+
+ return next ^ (next >> 31);
+}
+{% endif %}
+
+uint64_t muta_ro64(uint64_t x, int k) {
+ return (x << k) | (x >> (64 - k));
+}
+
+uint64_t muta_next(struct Core *core) {
+ assert(core);
+
+ uint64_t r = muta_ro64(core->muta[1] * 5, 7) * 9;
+ uint64_t 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(struct Core *core) {
+ assert(core);
+
+ uint64_t a = muta_next(core) % {{ muta_range }};
+ uint64_t b = muta_next(core);
+
+ if (a < {{ mvec_size }}) {
+ {% if args.muta_flip %}
+ mvec_flip_bit(core, a, (int)(b % 8));
+ {% else %}
+ mvec_set_inst(core, a, b & {{ inst_mask }});
+ {% endif %}
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Process functions
+// ----------------------------------------------------------------------------
+void proc_new(struct Core *core, const struct Proc *proc) {
+ assert(core);
+ assert(proc);
+
+ if (core->pnum == core->pcap) {
+ // Reallocate dynamic array
+ uint64_t new_pcap = core->pcap * 2;
+ struct Proc *new_pvec = calloc(new_pcap, sizeof(struct Proc));
+
+ for (uint64_t pix = core->pfst; pix <= core->plst; ++pix) {
+ uint64_t iold = pix % core->pcap;
+ uint64_t inew = pix % new_pcap;
+ memcpy(&new_pvec[inew], &core->pvec[iold], sizeof(struct 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(struct Proc));
+}
+
+void proc_kill(struct Core *core) {
+ assert(core);
+ assert(core->pnum > 1);
+
+ arch_on_proc_kill(core);
+
+ core->pcur++;
+ core->pfst++;
+ core->pnum--;
+}
+
+const struct Proc *proc_get(const struct Core *core, uint64_t pix) {
+ assert(core);
+
+ if (mvec_proc_is_live(core, pix)) {
+ return &core->pvec[pix % core->pcap];
+ } else {
+ return &g_dead_proc;
+ }
+}
+
+struct Proc *proc_fetch(struct Core *core, uint64_t pix) {
+ assert(core);
+ assert(mvec_proc_is_live(core, pix));
+
+ return &core->pvec[pix % core->pcap];
+}
+
+// ----------------------------------------------------------------------------
+// Core functions
+// ----------------------------------------------------------------------------
+{% if args.command in ["load", "new"] %}
+void core_save(FILE *f, const struct Core *core) {
+ assert(f);
+ assert(core);
+
+ fwrite(&core->cycl, sizeof(uint64_t), 1, f);
+ fwrite(&core->mall, sizeof(uint64_t), 1, f);
+ fwrite( core->muta, sizeof(uint64_t), 4, f);
+ fwrite(&core->pnum, sizeof(uint64_t), 1, f);
+ fwrite(&core->pcap, sizeof(uint64_t), 1, f);
+ fwrite(&core->pfst, sizeof(uint64_t), 1, f);
+ fwrite(&core->plst, sizeof(uint64_t), 1, f);
+ fwrite(&core->pcur, sizeof(uint64_t), 1, f);
+ fwrite(&core->psli, sizeof(uint64_t), 1, f);
+ fwrite(&core->ivpt, sizeof(uint64_t), 1, f);
+
+ fwrite(core->iviv, sizeof(uint8_t), {{ sync_interval }}, f);
+ fwrite(core->ivav, sizeof(uint64_t), {{ sync_interval }}, f);
+ fwrite(core->pvec, sizeof(struct Proc), core->pcap, f);
+ fwrite(core->mvec, sizeof(uint8_t), {{ mvec_size }}, f);
+}
+{% endif %}
+
+{% if args.command in ["bench", "new"] %}
+{% if anc_bytes is defined %}
+void core_assemble_ancestor(struct Core *core) {
+ assert(core);
+
+ {% if arch_vars.mvec_loop %}
+ uint64_t addr = {{ uint64_half }};
+ {% else %}
+ uint64_t addr = 0;
+ {% endif %}
+
+ uint8_t anc_bytes[] = {
+ {{ anc_bytes|join(",") }}
+ };
+
+ for (uint64_t i = 0; i < sizeof(anc_bytes); ++i, ++addr) {
+ for (uint64_t j = 0; j < {{ args.clones }}; ++j) {
+ uint64_t addr_clone = addr + (({{ mvec_size }} / {{ args.clones }})) * j;
+
+ mvec_alloc(core, addr_clone);
+ mvec_set_inst(core, addr_clone, anc_bytes[i]);
+ }
+ }
+}
+{% endif %}
+
+void core_init(struct Core *core, uint64_t *seed) {
+ assert(core);
+ assert(seed);
+
+ 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 = {{ args.clones }};
+ core->pcap = {{ args.clones }};
+ core->plst = {{ args.clones }} - 1;
+ core->iviv = calloc({{ sync_interval }}, sizeof(uint8_t));
+ core->ivav = calloc({{ sync_interval }}, sizeof(uint64_t));
+ core->pvec = calloc(core->pcap, sizeof(struct Proc));
+
+ assert(core->iviv);
+ assert(core->ivav);
+ assert(core->pvec);
+
+ {% if anc_bytes is defined %}
+ core_assemble_ancestor(core);
+ arch_anc_init(core);
+ {% endif %}
+}
+{% endif %}
+
+{% if args.command in ["load"] %}
+void core_load(FILE *f, struct Core *core) {
+ assert(f);
+ assert(core);
+
+ fread(&core->cycl, sizeof(uint64_t), 1, f);
+ fread(&core->mall, sizeof(uint64_t), 1, f);
+ fread( core->muta, sizeof(uint64_t), 4, f);
+ fread(&core->pnum, sizeof(uint64_t), 1, f);
+ fread(&core->pcap, sizeof(uint64_t), 1, f);
+ fread(&core->pfst, sizeof(uint64_t), 1, f);
+ fread(&core->plst, sizeof(uint64_t), 1, f);
+ fread(&core->pcur, sizeof(uint64_t), 1, f);
+ fread(&core->psli, sizeof(uint64_t), 1, f);
+ fread(&core->ivpt, sizeof(uint64_t), 1, f);
+
+ core->iviv = calloc({{ sync_interval }}, sizeof(uint8_t));
+ core->ivav = calloc({{ sync_interval }}, sizeof(uint64_t));
+ core->pvec = calloc(core->pcap, sizeof(struct Proc));
+
+ assert(core->iviv);
+ assert(core->ivav);
+ assert(core->pvec);
+
+ fread(core->iviv, sizeof(uint8_t), {{ sync_interval }}, f);
+ fread(core->ivav, sizeof(uint64_t), {{ sync_interval }}, f);
+ fread(core->pvec, sizeof(struct Proc), core->pcap, f);
+ fread(core->mvec, sizeof(uint8_t), {{ mvec_size }}, f);
+}
+{% endif %}
+
+void core_pull_ipcm(struct Core *core) {
+ assert(core);
+ assert(core->ivpt < {{ sync_interval }});
+
+ uint8_t *iinst = &core->iviv[core->ivpt];
+ uint64_t *iaddr = &core->ivav[core->ivpt];
+
+ if ((*iinst & {{ ipc_flag }}) != 0) {
+ mvec_set_inst(core, *iaddr, *iinst & {{ inst_mask }});
+
+ *iinst = 0;
+ *iaddr = 0;
+ }
+
+ assert(*iinst == 0);
+ assert(*iaddr == 0);
+}
+
+void core_push_ipcm(struct Core *core, uint8_t inst, uint64_t addr) {
+ assert(core);
+ assert(core->ivpt < {{ sync_interval }});
+ assert((inst & {{ ipc_flag }}) == 0);
+
+ uint8_t *iinst = &core->iviv[core->ivpt];
+ uint64_t *iaddr = &core->ivav[core->ivpt];
+
+ assert(*iinst == 0);
+ assert(*iaddr == 0);
+
+ *iinst = inst | {{ ipc_flag }};
+ *iaddr = addr;
+}
+
+void core_step(struct 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->cycl++;
+
+ // TODO: Implement day-night cycle
+ while (core->mall > {{ mvec_size }} / 2 && core->pnum > 1) {
+ proc_kill(core);
+ }
+
+ muta_cosmic_ray(core);
+ core_step(core);
+}
+
+// ----------------------------------------------------------------------------
+// Main salis functions
+// ----------------------------------------------------------------------------
+{% if args.command in ["load", "new"] %}
+void salis_save(const char *path) {
+ {% if args.compress %}
+ size_t size = 0;
+ char *in = NULL;
+ FILE *f = open_memstream(&in, &size);
+ {% else %}
+ FILE *f = fopen(path, "wb");
+ {% endif %}
+
+ assert(f);
+
+ for (int i = 0; i < {{ args.cores }}; ++i) {
+ core_save(f, &g_cores[i]);
+ }
+
+ fwrite(&g_steps, sizeof(uint64_t), 1, f);
+ fwrite(&g_syncs, sizeof(uint64_t), 1, f);
+ fclose(f);
+
+ {% if args.compress %}
+ assert(size);
+
+ char *out = malloc(size);
+ assert(out);
+
+ z_stream strm = { 0 };
+ strm.zalloc = NULL,
+ strm.zfree = NULL,
+ strm.opaque = NULL,
+
+ deflateInit(&strm, Z_DEFAULT_COMPRESSION);
+
+ strm.avail_in = size;
+ strm.avail_out = size;
+ strm.next_in = (Bytef *)in;
+ strm.next_out = (Bytef *)out;
+
+ deflate(&strm, Z_FINISH);
+
+ FILE *fx = fopen(path, "wb");
+ assert(fx);
+
+ fwrite(&size, sizeof(size_t), 1, fx);
+ fwrite(out, sizeof(char), strm.total_out, fx);
+ fclose(fx);
+
+ deflateEnd(&strm);
+
+ free(in);
+ free(out);
+ {% endif %}
+}
+
+void salis_auto_save() {
+ {% if not args.optimized %}
+ 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 args.command in ["bench", "new"] %}
+void salis_init() {
+ uint64_t seed = {{ args.seed }};
+
+ for (int i = 0; i < {{ args.cores }}; ++i) {
+ core_init(&g_cores[i], &seed);
+ }
+
+ {% if args.command in ["new"] %}
+ salis_auto_save();
+ {% endif %}
+
+ {% if data_push_path is defined %}
+ sqlite3_open("{{ data_push_path }}", &g_sim_data);
+ assert(g_sim_data);
+
+ arch_push_data_header();
+ arch_push_data_line();
+ {% endif %}
+}
+{% endif %}
+
+{% if args.command in ["load"] %}
+void salis_load() {
+ {% if args.compress %}
+ FILE *fx = fopen("{{ sim_path }}", "rb");
+ assert(fx);
+
+ fseek(fx, 0, SEEK_END);
+ size_t x_size = ftell(fx) - sizeof(size_t);
+ char *in = malloc(x_size);
+ rewind(fx);
+ assert(x_size);
+ assert(in);
+
+ size_t size = 0;
+ fread(&size, sizeof(size_t), 1, fx);
+ fread(in, 1, x_size, fx);
+ fclose(fx);
+ assert(size);
+
+ char *out = malloc(size);
+ assert(out);
+
+ z_stream strm = { 0 };
+ strm.next_in = (Bytef *)in;
+ strm.avail_in = x_size;
+ strm.zalloc = NULL;
+ strm.zfree = NULL;
+ strm.opaque = NULL;
+
+ inflateInit(&strm);
+
+ strm.avail_out = size;
+ strm.next_out = (Bytef *)out;
+
+ {% if not args.optimized %}
+ assert(inflate(&strm, Z_FINISH));
+ {% else %}
+ inflate(&strm, Z_FINISH);
+ {% endif %}
+
+ inflateEnd(&strm);
+
+ FILE *f = fmemopen(out, size, "rb");
+ {% else %}
+ FILE *f = fopen("{{ sim_path }}", "rb");
+ {% endif %}
+
+ assert(f);
+
+ for (int i = 0; i < {{ args.cores }}; ++i) {
+ core_load(f, &g_cores[i]);
+ }
+
+ fread(&g_steps, sizeof(uint64_t), 1, f);
+ fread(&g_syncs, sizeof(uint64_t), 1, f);
+ fclose(f);
+
+ {% if args.compress %}
+ free(in);
+ free(out);
+ {% endif %}
+
+ {% if data_push_path is defined %}
+ sqlite3_open("{{ data_push_path }}", &g_sim_data);
+ assert(g_sim_data);
+ {% endif %}
+}
+{% endif %}
+
+int salis_thread(struct Core *core) {
+ assert(core);
+
+ for (uint64_t i = 0; i < core->thrd_idx; ++i) {
+ core_step(core);
+ }
+
+ return 0;
+}
+
+void salis_run_thread(uint64_t ns) {
+ for (int i = 0; i < {{ args.cores }}; ++i) {
+ g_cores[i].thrd_idx = ns;
+
+ thrd_create(
+ &g_cores[i].thrd,
+ (thrd_start_t)salis_thread,
+ &g_cores[i]
+ );
+ }
+
+ for (int i = 0; i < {{ args.cores }}; ++i) {
+ thrd_join(g_cores[i].thrd, NULL);
+ }
+
+ g_steps += ns;
+}
+
+void salis_sync() {
+ uint8_t *iviv0 = g_cores[0].iviv;
+ uint64_t *ivav0 = g_cores[0].ivav;
+
+ for (int i = 1; i < {{ args.cores }}; ++i) {
+ g_cores[i - 1].iviv = g_cores[i].iviv;
+ g_cores[i - 1].ivav = g_cores[i].ivav;
+ }
+
+ g_cores[{{ args.cores }} - 1].iviv = iviv0;
+ g_cores[{{ args.cores }} - 1].ivav = ivav0;
+
+ for (int i = 0; i < {{ args.cores }}; ++i) {
+ g_cores[i].ivpt = 0;
+ }
+
+ g_syncs++;
+}
+
+void salis_loop(uint64_t ns, uint64_t dt) {
+ assert(dt);
+
+ if (ns < dt) {
+ salis_run_thread(ns);
+ return;
+ }
+
+ salis_run_thread(dt);
+ salis_sync();
+
+ {% if args.command in ["load", "new"] %}
+ if (g_steps % {{ auto_save_interval }} == 0) {
+ salis_auto_save();
+ }
+ {% endif %}
+
+ {% if data_push_path is defined %}
+ if (g_steps % {{ data_push_interval }} == 0) {
+ arch_push_data_line();
+ }
+ {% endif %}
+
+ salis_loop(ns - dt, {{ sync_interval }});
+}
+
+{% if not args.optimized %}
+void salis_validate_core(const struct Core *core) {
+ assert(core->cycl <= g_steps);
+ 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);
+
+ uint64_t mall = 0;
+
+ for (uint64_t i = 0; i < {{ mvec_size }}; ++i) {
+ mall += mvec_is_alloc(core, i) ? 1 : 0;
+ }
+
+ assert(core->mall == mall);
+
+ for (uint64_t i = core->pfst; i <= core->plst; ++i) {
+ arch_validate_proc(core, i);
+ }
+
+ for (uint64_t i = 0; i < {{ sync_interval }}; ++i) {
+ uint8_t iinst = core->iviv[i];
+
+ if ((iinst & {{ ipc_flag }}) == 0) {
+ uint64_t 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 < {{ args.cores }}; ++i) {
+ salis_validate_core(&g_cores[i]);
+ }
+}
+{% endif %}
+
+void salis_step(uint64_t ns) {
+ assert(ns);
+ salis_loop(ns, {{ sync_interval }} - (g_steps % {{ sync_interval }}));
+
+ {% if not args.optimized %}
+ salis_validate();
+ {% endif %}
+}
+
+void salis_free() {
+ {% if data_push_path is defined %}
+ assert(g_sim_data);
+ sqlite3_close(g_sim_data);
+ {% endif %}
+
+ for (int i = 0; i < {{ args.cores }}; ++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;
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Architecture
+// ----------------------------------------------------------------------------
+{% include "arch/" ~ args.arch ~ "/arch.j2.c" %}
+
+// ----------------------------------------------------------------------------
+// UI
+// ----------------------------------------------------------------------------
+{% if args.command in ["load", "new"] %}
+ {% include "ui/" ~ args.ui ~ "/ui.j2.c" %}
+{% else %}
+ {% include "bench.j2.c" %}
+{% endif %}
diff --git a/salis b/salis
deleted file mode 100755
index ad5aded..0000000
--- a/salis
+++ /dev/null
@@ -1,406 +0,0 @@
-#!/bin/bash
-
-# Project: Salis
-# Author: Paul Oliver
-# Email: contact@pauloliver.dev
-
-# Salis simulator launcher script. Builds salis binary (according to passed
-# arguments) and launches it. Binary is purged on exit. This JIT compilation
-# scheme allows to quickly switch between available architectures and UIs.
-
-set -euo pipefail
-
-headline="Salis: Simple A-Life Simulator."
-help_msg="Shows help and exits"
-
-usage() {
-cat << EOF
-${headline}
-Usage: ${0} [-h|--help] COMMAND [args...]
-
-Options:
- -h, --help ${help_msg}
-
-Commands:
- bench Runs benchmark
- load Loads saved simulation
- new Creates a new simulation
-
-Use '-h' to list arguments for each command.
-Example: ${0} bench -h
-EOF
-}
-
-case ${1:-} in
-bench|load|new)
- ;;
--h|--help)
- usage
- exit 0
- ;;
-"")
- echo "${0}: please specify command -- 'bench|load|new'"
- exit 1
- ;;
-*)
- echo "${0}: invalid command -- '${1}'"
- exit 1
- ;;
-esac
-
-cmd=${1}
-
-shift
-
-falter() {
- find src/${1}/ -type f | sed 's|.*/||; s|\.c||' | paste -sd: -
-}
-
-arches=`falter arch`
-uis=`falter ui`
-
-anc_def_desc() {
- echo "Default ancestor file name without extension, "
- echo "to be compiled on all cores "
- echo "('ANC' points to file 'ancs/<ARCH>/<ANC>.asm')"
-}
-
-anc_spec_def() {
- echo "Core specific ancestor file names separated by commas, "
- echo "using same convention as with default ancestor (<ANC0>,<ANC1>,...). "
- echo "When provided will override default ancestor on specified cores."
-}
-
-options=(
- "A|anc-def|ANC|`anc_def_desc`|||bench:new"
- "a|arch|ARCH|VM architecture|${arches}|dummy|bench:new"
- "b|steps|N|Number of steps to run in benchmark||0x1000000|bench"
- "C|clones|N|Number of ancestor clones on each core||1|bench:new"
- "c|cores|N|Number of simulator cores||2|bench:new"
- "F|muta-flip||Cosmic rays flip bits instead of randomizing whole bytes||false|bench:new"
- "f|force||Overwrites existing simulation of given name||false|new"
- "H|half||Compiles ancestor at the middle of the memory buffer||false|bench:new"
- "h|help||${help_msg}|||bench:load:new"
- "M|muta-pow|POW|Mutator range exponent (range == 2^POW)||32|bench:new"
- "m|mvec-pow|POW|Memory vector size exponent (size == 2^POW)||20|bench:new"
- "n|name|NAME|Name of new or loaded simulation||def.sim|load:new"
- "o|optimized||Builds Salis binary with optimizations||false|bench:load:new"
- "p|pre-cmd|CMD|Shell command to wrap executable (e.g. gdb, valgrind, etc.)|||bench:load:new"
- "S|anc-spec|ANC0,ANC1,...|`anc_spec_def`|||bench:new"
- "s|seed|SEED|Seed value for new simulation||0|bench:new"
- "t|thread-gap|N|Memory gap between cores in bytes (could help reduce cache misses?)||0x100|bench:load:new"
- "u|ui|UI|User interface|${uis}|curses|load:new"
- "y|sync-pow|POW|Core sync interval exponent (interval == 2^POW)||20|bench:new"
- "x|compress||Compress save files (requires 'zlib')||true|new"
- "z|auto-save-pow|POW|Auto-save interval exponent (interval == 2^POW)||36|new"
-)
-
-field() {
- echo ${1} | cut -d'|' -f${2}
-}
-
-flist() {
- sopt=`field "${1}" 1`
- lopt=`field "${1}" 2`
- meta=`field "${1}" 3`
-
- echo -n "[-${sopt}|--${lopt}`[[ -n ${meta} ]] && echo " ${meta}"`] "
-}
-
-fhelp() {
- sopt=`field "${1}" 1`
- lopt=`field "${1}" 2`
- meta=`field "${1}" 3`
-
- printf "%s\r" " -${sopt}, --${lopt}`[[ -n ${meta} ]] && echo " ${meta}"`"
-
- help=`field "${1}" 4`
- choi=`field "${1}" 5`
- defv=`field "${1}" 6`
- copt=`[[ -n ${choi} ]] && echo " (choices: ${choi/:/, })"`
- dopt=`[[ -n ${defv} ]] && echo " (default: ${defv})"`
-
- echo -e "\t\t\t\t${help}${copt}${dopt}" | fmt -w120
-}
-
-fshort() {
- sopt=`field "${1}" 1`
- meta=`field "${1}" 3`
-
- echo -n "${sopt}`[[ -n ${meta} ]] && echo :`"
-}
-
-flong() {
- lopt=`field "${1}" 2`
- meta=`field "${1}" 3`
-
- echo -n "${lopt}`[[ -n ${meta} ]] && echo :`,"
-}
-
-fdefaults() {
- lopt=`field "${1}" 2`
-
- [[ ${lopt} == help ]] && return
-
- defv=`field "${1}" 6`
- nopt=opt_${lopt//-/_}
-
- eval ${nopt}=${defv}
-}
-
-fshow() {
- lopt=`field "${1}" 2`
-
- [[ ${lopt} == help ]] && return
-
- nopt=opt_${lopt//-/_}
-
- echo "${nopt}=${!nopt}"
-}
-
-fiter() {
- for ((i = 0; i < ${#options[@]}; i++)) ; do
- if [[ `field "${options[${i}]}" 7` =~ ${cmd} ]] ; then
- ${1} "${options[${i}]}" || true
- fi
- done
-}
-
-usage() {
-cat << EOF
-${headline}
-Usage: ${0} ${cmd} `fiter flist | fmt -t -w120`
-
-Options:
-`fiter fhelp`
-EOF
-}
-
-fiter fdefaults
-
-sopts=`fiter fshort`
-lopts=`fiter flong`
-popts=`getopt -n "${0} ${cmd}" -o ${sopts} -l ${lopts::-1} -- "${@}"`
-
-eval set -- ${popts}
-
-parse_next() {
- for ((i = 0; i < ${#options[@]}; i++)) ; do
- vopt="${options[${i}]}"
-
- sopt=`field "${vopt}" 1`
- lopt=`field "${vopt}" 2`
-
- [[ ${1} != -${sopt} ]] && [[ ${1} != --${lopt} ]] && continue
-
- meta=`field "${vopt}" 3`
- nopt=opt_${lopt//-/_}
-
- if [[ -z ${meta} ]] ; then
- defv=`field "${vopt}" 6`
- eval ${nopt}=`[[ ${defv} == true ]] && echo false || echo true`
- shift_next=1
- else
- eval ${nopt}=${2}
- shift_next=2
- fi
- done
-}
-
-while true ; do
- case ${1} in
- -h|--help)
- usage
- exit 0
- ;;
- --)
- shift
- break
- ;;
- *)
- parse_next ${@}
- shift ${shift_next}
- ;;
- esac
-done
-
-if [[ -n ${1:-} ]] ; then
- while [[ -n ${1:-} ]] ; do
- echo "${0} ${cmd}: unrecognized option -- '${1}'"
- shift
- done
-
- exit 1
-fi
-
-blue() {
- echo -e "\033[1;34m${1}\033[0m"
-}
-
-red() {
- echo -e "\033[1;31m${1}\033[0m"
-}
-
-blue "${headline}"
-blue "Called '${cmd}' command with the following options:"
-fiter fshow
-
-case ${cmd} in
-load|new)
- sim_dir=${HOME}/.salis/${opt_name}
- sim_path=${sim_dir}/${opt_name}
- sim_opts=${sim_dir}/opts
- ;;
-esac
-
-case ${cmd} in
-load)
- if [[ ! -d ${sim_dir} ]] ; then
- red "Error: no saved simulation was found named '${opt_name}'."
- exit 1
- fi
-
- blue "Sourcing configurations from '${sim_opts}':"
- cat ${sim_opts}
- source ${sim_opts}
- ;;
-esac
-
-blue "Generating a temporary Salis directory:"
-salis_tmp=/tmp/salis-tmp
-salis_exe=${salis_tmp}/salis-bin
-mkdir -pv ${salis_tmp}
-
-act_bench=1
-act_load=2
-act_new=3
-
-act_var="act_${cmd}"
-
-gcc_flags="-Wall -Wextra -Werror -std=gnu11 -pedantic"
-
-fquote() {
- echo "\\\"${1}\\\""
-}
-
-fpow() {
- printf '%#xul' $((1 << ${1}))
-}
-
-bcmd="gcc src/salis.c -o ${salis_exe} ${gcc_flags} -Isrc -pthread"
-bcmd="${bcmd} `[[ ${opt_optimized} == true ]] && echo "-O3 -DNDEBUG" || echo "-ggdb"`"
-bcmd="${bcmd} -DACTION=${!act_var}"
-bcmd="${bcmd} -DARCHITECTURE=`fquote ${opt_arch}`"
-bcmd="${bcmd} -DARCH_SOURCE=`fquote arch/${opt_arch}.c`"
-bcmd="${bcmd} -DCORE_COUNT=${opt_cores}"
-bcmd="${bcmd} -DMUTA_RANGE=`fpow ${opt_muta_pow}`"
-bcmd="${bcmd} -DMVEC_SIZE=`fpow ${opt_mvec_pow}`"
-bcmd="${bcmd} -DNCURSES_WIDECHAR=1"
-bcmd="${bcmd} -DSEED=${opt_seed}ul"
-bcmd="${bcmd} -DSYNC_INTERVAL=`fpow ${opt_sync_pow}`"
-bcmd="${bcmd} -DTGAP_SIZE=${opt_thread_gap}ul"
-
-case ${cmd} in
-bench)
- bcmd="${bcmd} -DBENCH_STEPS=${opt_steps}ul"
- bcmd="${bcmd} -DUI=`fquote bench.c`"
- ;;
-esac
-
-case ${cmd} in
-bench|new)
- anc_list=
-
- for cix in `seq 1 ${opt_cores}` ; do
- anc_spec=`echo ${opt_anc_spec}, | cut -s -d, -f${cix}`
- anc_spec=${anc_spec:-${opt_anc_def}}
-
- if [[ -n ${anc_spec} ]] ; then
- anc_src=ancs/${opt_arch}/${anc_spec}.asm
- anc_path=${salis_tmp}/${anc_spec}.asm
- sed -E '/(^$|^;)/d; s/ +/ /g' ${anc_src} > ${anc_path}
- else
- anc_path=_
- fi
-
- anc_list=${anc_list}${anc_path},
- done
-
- bcmd="${bcmd} -DANC_LIST=`fquote "${anc_list::-1}"`"
- bcmd="${bcmd} -DANC_HALF=`[[ ${opt_half} == true ]] && echo 1 || echo 0`"
- bcmd="${bcmd} -DANC_CLONES=${opt_clones}"
- ;;
-esac
-
-case ${cmd} in
-load|new)
- bcmd="${bcmd} -DAUTO_SAVE_INTERVAL=`fpow ${opt_auto_save_pow}`"
- bcmd="${bcmd} -DAUTO_SAVE_NAME_LEN=$((${#sim_path} + 20))"
- bcmd="${bcmd} -DMUTA_FLIP_BIT=`[[ ${opt_muta_flip} == true ]] && echo 1 || echo 0`"
- bcmd="${bcmd} -DSIM_NAME=`fquote ${opt_name}`"
- bcmd="${bcmd} -DSIM_PATH=`fquote ${sim_path}`"
-
- ui_file=ui/${opt_ui}.c
- ui_flags=`sed -n -e 's/^.*GCC_EXTRA_FLAGS //p' src/${ui_file}`
-
- shopt -s extglob
- bcmd="${bcmd} -DUI=`fquote ${ui_file}` ${ui_flags}"
- bcmd="${bcmd%%*( )}"
- shopt -u extglob
-
- if [[ ${opt_compress} == true ]] ; then
- bcmd="${bcmd} -DCOMPRESS -lz"
- fi
- ;;
-esac
-
-blue "Using build command:"
-echo "${bcmd}"
-eval "${bcmd}"
-
-case ${cmd} in
-new)
- if [[ -d ${sim_dir} ]] && [[ ${opt_force} == true ]] ; then
- red "Force flag used. Wiping old simulation at '${sim_dir}':"
- rm -rv ${sim_dir}
- fi
-
- if [[ -d ${sim_dir} ]] ; then
- red "Error: simulation directory found at '${sim_dir}'."
- red "Please, remove it or call 'load' instead."
- exit 1
- fi
-
- blue "Creating new simulation directory at '${sim_dir}':"
- mkdir -pv ${sim_dir}
- ;;
-esac
-
-rcmd="`[[ -z ${opt_pre_cmd} ]] || echo "${opt_pre_cmd} "`${salis_exe}"
-
-blue "Using run command:"
-echo "${rcmd}"
-
-blue "Running Salis..."
-eval "${rcmd}"
-
-case ${cmd} in
-new)
- blue "Saving new simulation configuration file at:"
- echo "${sim_opts}"
-
- for ((i = 0; i < ${#options[@]}; i++)) ; do
- oopt=`field "${options[${i}]}" 7`
-
- [[ ! ${oopt} =~ new ]] || [[ ${oopt} =~ load ]] && continue
-
- lopt=`field "${options[${i}]}" 2`
- nopt=opt_${lopt//-/_}
-
- echo "${nopt}=${!nopt}" >> ${sim_opts}
- done
- ;;
-esac
-
-blue "Removing temporary Salis directory and resources:"
-rm -rv ${salis_tmp}
diff --git a/salis.py b/salis.py
new file mode 100755
index 0000000..b7f7a18
--- /dev/null
+++ b/salis.py
@@ -0,0 +1,356 @@
+#!/usr/bin/env -S PYTHONDONTWRITEBYTECODE=1 python3
+
+# Author: Paul Oliver <contact@pauloliver.dev>
+# Project: salis-v3
+
+# Salis simulator launcher script
+# Emits a single C source file, builds it into a binary and launches it.
+# JIT compilation allows quick switching between all available executable configurations.
+
+import os
+import shutil
+import subprocess
+import sys
+
+from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser, RawTextHelpFormatter
+from jinja2 import Environment, FileSystemLoader, StrictUndefined
+from tempfile import TemporaryDirectory
+
+# ------------------------------------------------------------------------------
+# Parse CLI arguments
+# ------------------------------------------------------------------------------
+headline = "Salis: Simple A-Life Simulator"
+script = sys.argv[0]
+epilog = f"Use '-h' to list arguments for each command.\nExample: '{script} bench -h'"
+
+main_parser = ArgumentParser(
+ description = headline,
+ epilog = epilog,
+ formatter_class = RawTextHelpFormatter,
+ prog = script,
+)
+
+parsers = main_parser.add_subparsers(dest="command", required=True)
+fclass = ArgumentDefaultsHelpFormatter
+
+bench = parsers.add_parser("bench", formatter_class=fclass, help="run benchmark")
+load = parsers.add_parser("load", formatter_class=fclass, help="load saved simulation")
+new = parsers.add_parser("new", formatter_class=fclass, help="create new simulation")
+
+archs = os.listdir("./arch")
+uis = os.listdir("./ui")
+
+intt = lambda i: int(i, 0)
+
+option_keys = ["short", "long", "metavar", "description", "default", "type", "parsers"]
+
+# fmt: off
+option_conf = [
+ ["A", "anc", "ANC", "ancestor file name without extension, to be compiled on "
+ "all cores (ANC points to 'ancs/<ARCH>/<ANC>.asm')", None, str, [bench, new]],
+ ["a", "arch", archs, "VM architecture", "dummy", str, [bench, new]],
+ ["b", "steps", "N", "number of steps to run in benchmark", 0x1000000, intt, [bench]],
+ ["C", "clones", "N", "number of ancestor clones on each core", 1, intt, [bench, new]],
+ ["c", "cores", "N", "number of simulator cores", 2, intt, [bench, new]],
+ ["d", "data-push-pow", "POW", "data aggregation interval exponent (interval == 2^POW >= "
+ "thread sync interval); a value of 0 disables data "
+ "aggregation (requires 'sqlite')", 28, intt, [new]],
+ ["f", "force", None, "overwrite existing simulation of given name", False, bool, [new]],
+ ["F", "muta-flip", None, "cosmic rays flip bits instead of randomizing whole bytes", False, bool, [bench, new]],
+ ["M", "muta-pow", "POW", "mutator range exponent (range == 2^POW)", 32, intt, [bench, new]],
+ ["m", "mvec-pow", "POW", "memory vector size exponent (size == 2^POW)", 20, intt, [bench, new]],
+ ["n", "name", "NAME", "name of new or loaded simulation", "def.sim", str, [load, new]],
+ ["o", "optimized", None, "builds salis binary with optimizations", False, bool, [bench, load, new]],
+ ["p", "pre-cmd", "CMD", "shell command to wrap call to executable (e.g. gdb, "
+ "valgrind, etc.)", None, str, [bench, load, new]],
+ ["s", "seed", "SEED", "seed value for new simulation", 0, intt, [bench, new]],
+ ["S", "print-source", None, "print generated C source to stdout and exit", False, bool, [bench, load, new]],
+ ["T", "delete-temp-dir", None, "delete temporary directory on exit", True, bool, [bench, load, new]],
+ ["t", "thread-gap", "N", "memory gap between cores in bytes (may help reduce cache "
+ "misses?)", 0x100, intt, [bench, load, new]],
+ ["u", "ui", uis, "user interface", "curses", str, [load, new]],
+ ["x", "compress", None, "compress save files (requires 'zlib')", True, bool, [new]],
+ ["y", "sync-pow", "POW", "core sync interval exponent (interval == 2^POW)", 20, intt, [bench, new]],
+ ["z", "auto-save-pow", "POW", "auto-save interval exponent (interval == 2^POW)", 36, intt, [new]],
+]
+# fmt: on
+
+# Map arguments to subparsers that use them
+options = list(map(lambda option: dict(zip(option_keys, option)), option_conf))
+parser_map = ((parser, option) for option in options for parser in option["parsers"])
+
+for parser, option in parser_map:
+ arg_kwargs = {}
+
+ def push_same(key):
+ arg_kwargs[key] = option[key]
+
+ def push_diff(tgt_key, src_key):
+ arg_kwargs[tgt_key] = option[src_key]
+
+ def push_val(key, val):
+ arg_kwargs[key] = val
+
+ push_diff("help", "description")
+
+ # No metavar means this argument is a flag
+ if option["metavar"] is None:
+ push_val("action", "store_false" if option["default"] else "store_true")
+ else:
+ push_same("default")
+ push_same("type")
+
+ if type(option["metavar"]) is list:
+ push_diff("choices", "metavar")
+
+ if type(option["metavar"]) is str:
+ push_same("metavar")
+
+ parser.add_argument(
+ f"-{option["short"]}",
+ f"--{option["long"]}",
+ **arg_kwargs,
+ )
+
+args = main_parser.parse_args()
+
+
+def log(msg, val=""):
+ print(f"\033[1;34m{msg}\033[0m", val, flush=True)
+
+
+def warn(msg, val=""):
+ print(f"\033[1;31m{msg}\033[0m", val, flush=True)
+
+
+def error(msg, val=""):
+ warn(f"ERROR: {msg}", val)
+ sys.exit(1)
+
+
+# ------------------------------------------------------------------------------
+# Load configuration
+# ------------------------------------------------------------------------------
+log(headline)
+log(f"Called '{script}' with the following options:")
+
+for key, val in vars(args).items():
+ print(f"{key} = {repr(val)}")
+
+if args.command in ["load", "new"]:
+ sim_dir = f"{os.environ["HOME"]}/.salis/{args.name}"
+ sim_opts = f"{sim_dir}/opts.py"
+ sim_path = f"{sim_dir}/{args.name}"
+
+if args.command in ["load"]:
+ if not os.path.isdir(sim_dir):
+ error("No simulation found named:", args.name)
+
+ log(f"Sourcing configuration from '{sim_opts}':")
+ sys.path.append(sim_dir)
+ import opts as opts_module
+
+ # Copy all fields in configuration file into the 'args' object
+ opts = (opt for opt in dir(opts_module) if not opt.startswith("__"))
+
+ for opt in opts:
+ opt_attr = getattr(opts_module, opt)
+ print(f"{opt} = {repr(opt_attr)}")
+ setattr(args, opt, opt_attr)
+
+if args.command in ["new"]:
+ if args.data_push_pow != 0 and args.data_push_pow < args.sync_pow:
+ error("Data push power must be equal or greater than thread sync power")
+
+ if os.path.isdir(sim_dir) and args.force:
+ warn("Force flag used - wiping old simulation at:", sim_dir)
+ shutil.rmtree(sim_dir)
+
+ if os.path.isdir(sim_dir):
+ error("Simulation directory found at:", sim_dir)
+
+ log("Creating new simulation directory at:", sim_dir)
+ os.mkdir(sim_dir)
+
+ log("Creating configuration file at:", sim_opts)
+
+ opts = (
+ option["long"].replace("-", "_")
+ for option in options
+ if new in option["parsers"] and load not in option["parsers"]
+ )
+
+ with open(sim_opts, "w") as file:
+ for opt in opts:
+ file.write(f"{opt} = {repr(eval(f"args.{opt}"))}\n")
+
+# ------------------------------------------------------------------------------
+# Load architecture and UI variables
+# ------------------------------------------------------------------------------
+arch_path = f"arch/{args.arch}"
+log("Loading architecture specific variables from:", f"{arch_path}/arch_vars.py")
+sys.path.append(arch_path)
+import arch_vars
+
+if args.command in ["load", "new"]:
+ ui_path = f"ui/{args.ui}"
+ log("Loading UI specific variables from:", f"{ui_path}/ui_vars.py")
+ sys.path.append(ui_path)
+ import ui_vars
+
+# ------------------------------------------------------------------------------
+# Fill in template variables
+# ------------------------------------------------------------------------------
+ul_val = lambda val: f"{hex(val)}ul"
+ul_pow = lambda val: f"{hex(2 ** val)}ul"
+
+includes = [
+ "assert.h",
+ "stdbool.h",
+ "stddef.h",
+ "stdint.h",
+ "stdlib.h",
+ "string.h",
+ "threads.h",
+]
+
+inst_cap = "0x80"
+inst_mask = "0x7f"
+ipc_flag = "0x80"
+mall_flag = "0x80"
+muta_range = ul_pow(args.muta_pow)
+muta_seed = ul_val(args.seed)
+mvec_size = ul_pow(args.mvec_pow)
+sync_interval = ul_pow(args.sync_pow)
+thread_gap = ul_val(args.thread_gap)
+uint64_half = ul_val(0x8000000000000000)
+
+args.seed = ul_val(args.seed)
+
+if args.command in ["bench"]:
+ includes.append("stdio.h")
+ args.steps = ul_val(args.steps)
+
+if args.command in ["load", "new"]:
+ auto_save_interval = ul_pow(args.auto_save_pow)
+ auto_save_name_len = f"{len(sim_path) + 20}"
+
+ if args.data_push_pow != 0:
+ data_push_path = f"{sim_dir}/{args.name}.sqlite3"
+ data_push_interval = ul_pow(args.data_push_pow)
+ includes.append("sqlite3.h")
+ log("Data will be aggregated at:", data_push_path)
+ else:
+ warn("Data aggregation disabled")
+
+ if args.compress:
+ includes.append("zlib.h")
+ log("Save file compression enabled")
+ else:
+ warn("Save file compression disabled")
+
+ includes.extend(ui_vars.includes)
+
+# ------------------------------------------------------------------------------
+# Assemble ancestor organism into byte array
+# ------------------------------------------------------------------------------
+if args.command in ["bench", "new"] and args.anc is not None:
+ anc_path = f"ancs/{args.arch}/{args.anc}.asm"
+
+ if not os.path.isfile(anc_path):
+ error("Could not find ancestor file:", anc_path)
+
+ with open(anc_path, "r") as file:
+ lines = file.read().splitlines()
+
+ lines = filter(lambda line: not line.startswith(";"), lines)
+ lines = filter(lambda line: not line.isspace(), lines)
+ lines = filter(lambda line: line, lines)
+ lines = map(lambda line: line.split(), lines)
+
+ # A very simple assembler that compares lines in input ASM file against
+ # all entries in the instruction set table provided by each architecture.
+ # The resulting bytes equate to each instruction's index on the table.
+ anc_bytes = []
+
+ for line in lines:
+ found = False
+
+ for byte, tup in enumerate(arch_vars.inst_set):
+ if line == tup[0]:
+ anc_bytes.append(byte)
+ found = True
+ continue
+
+ if not found:
+ error("Unrecognized instruction in ancestor file:", line)
+
+ log(f"Compiled ancestor file '{anc_path}' into byte array:", anc_bytes)
+
+# ------------------------------------------------------------------------------
+# Emit C source
+# ------------------------------------------------------------------------------
+tempdir = TemporaryDirectory(prefix="salis_", delete=args.delete_temp_dir)
+log("Created a temporary salis directory at:", tempdir.name)
+
+salis_src = f"{tempdir.name}/salis.c"
+log("Emitting C source at:", salis_src)
+
+jinja_env = Environment(
+ loader = FileSystemLoader("."),
+ lstrip_blocks = True,
+ trim_blocks = True,
+ undefined = StrictUndefined,
+)
+
+source_str = jinja_env.get_template("core.j2.c").render(**locals())
+
+if args.print_source:
+ log("Printing C source and exiting...")
+ print(source_str)
+ exit(0)
+
+with open(salis_src, "w") as file:
+ file.write(source_str)
+
+# ------------------------------------------------------------------------------
+# Build executable
+# ------------------------------------------------------------------------------
+salis_bin = f"{tempdir.name}/salis_bin"
+log("Building salis binary at:", salis_bin)
+
+build_cmd = ["gcc", salis_src, "-o", salis_bin, "-Wall", "-Wextra", "-Werror", "-Wno-overlength-strings", "-pedantic", "-std=c11"]
+build_cmd.extend(["-O3", "-DNDEBUG"] if args.optimized else ["-ggdb"])
+
+if args.command in ["load", "new"]:
+ build_cmd.extend(ui_vars.flags)
+
+ # Enable POSIX extensions (open_memstream)
+ build_cmd.extend(["-lz", "-D_POSIX_C_SOURCE=200809L"] if args.compress else [])
+
+ # Enable GNU extensions (asprintf)
+ # This allows managing large SQL strings more easily
+ build_cmd.extend(["-lsqlite3", "-D_GNU_SOURCE"] if args.data_push_pow != 0 else [])
+
+log("Using build command:", " ".join(build_cmd))
+subprocess.run(build_cmd, check=True)
+
+# ------------------------------------------------------------------------------
+# Run salis binary
+# ------------------------------------------------------------------------------
+log("Running salis binary...")
+
+run_cmd = [args.pre_cmd] if args.pre_cmd else []
+run_cmd.append(salis_bin)
+
+log("Using run command:", " ".join(run_cmd))
+salis_sp = subprocess.Popen(run_cmd, stdout=sys.stdout)
+
+# Ctrl-C terminates the simulator gracefully.
+# When using signals (e.g. SIGTERM), they must be sent to the entire process group
+# to make sure both the simulator and the interpreter get shut down.
+try:
+ salis_sp.wait()
+except KeyboardInterrupt:
+ salis_sp.terminate()
+ salis_sp.wait()
diff --git a/src/arch/dummy.c b/src/arch/dummy.c
deleted file mode 100644
index 7a47900..0000000
--- a/src/arch/dummy.c
+++ /dev/null
@@ -1,152 +0,0 @@
-// 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, ip) \
- PROC_FIELD(u64, sp) \
- PROC_FIELD(u64, mb0a) \
- PROC_FIELD(u64, mb0s) \
- PROC_FIELD(u64, mb1a) \
- PROC_FIELD(u64, mb1s)
-
-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/arch/salis-v1.c b/src/arch/salis-v1.c
deleted file mode 100644
index 1085883..0000000
--- a/src/arch/salis-v1.c
+++ /dev/null
@@ -1,875 +0,0 @@
-// Project: Salis
-// Author: Paul Oliver
-// Email: contact@pauloliver.dev
-
-/*
- * This is based on the original VM architecture in salis-v1:
- * https://git.pauloliver.dev/salis-v1/about/
- */
-
-bool mvec_is_alloc(const Core *core, u64 pix);
-void mvec_alloc(Core *core, u64 addr);
-void mvec_free(Core *core, u64 addr);
-u8 mvec_get_inst(const Core *core, u64 addr);
-void mvec_set_inst(Core *core, u64 addr, u8 inst);
-bool mvec_is_proc_owner(const Core *core, u64 addr, u64 pix);
-void proc_new(Core *core, const Proc *proc);
-bool proc_is_live(const Core *core, u64 pix);
-const Proc *proc_get(const Core *core, u64 pix);
-Proc *proc_fetch(Core *core, u64 pix);
-
-#define INST_LIST \
- INST(noop, L' ') \
- INST(nop0, L'0') \
- INST(nop1, L'1') \
- INST(nop2, L'2') \
- INST(nop3, L'3') \
- \
- INST(jmpb, L'(') \
- INST(jmpf, L')') \
- INST(adrb, L'[') \
- INST(adrf, L']') \
- INST(ifnz, L'?') \
- \
- INST(allb, L'{') \
- INST(allf, L'}') \
- INST(bswp, L'%') \
- INST(bclr, L'|') \
- INST(splt, L'$') \
- \
- INST(addn, L'+') \
- INST(subn, L'-') \
- INST(muln, L'*') \
- INST(divn, L'/') \
- INST(incn, L'^') \
- INST(decn, L'v') \
- INST(notn, L'!') \
- INST(shfl, L'<') \
- INST(shfr, L'>') \
- INST(zero, L'z') \
- INST(unit, L'u') \
- \
- INST(pshn, L'#') \
- INST(popn, L'~') \
- \
- INST(load, L'.') \
- INST(wrte, L':') \
- INST(dupl, L'"') \
- INST(swap, L'x') \
- \
- INST(keya, L'a') \
- INST(keyb, L'b') \
- INST(keyc, L'c') \
- INST(keyd, L'd') \
- INST(keye, L'e') \
- INST(keyf, L'f') \
- INST(keyg, L'g') \
- INST(keyh, L'h') \
- INST(keyi, L'i') \
- INST(keyj, L'j') \
- INST(keyk, L'k') \
- INST(keyl, L'l') \
- INST(keym, L'm') \
- INST(keyn, L'n') \
- INST(keyo, L'o') \
- INST(keyp, L'p') \
- \
- INST(loka, L'A') \
- INST(lokb, L'B') \
- INST(lokc, L'C') \
- INST(lokd, L'D') \
- INST(loke, L'E') \
- INST(lokf, L'F') \
- INST(lokg, L'G') \
- INST(lokh, L'H') \
- INST(loki, L'I') \
- INST(lokj, L'J') \
- INST(lokk, L'K') \
- INST(lokl, L'L') \
- INST(lokm, L'M') \
- INST(lokn, L'N') \
- INST(loko, L'O') \
- INST(lokp, L'P')
-
-#define MNEMONIC_BUFF_SIZE (0x5)
-
-enum sinst {
-#define INST(name, symb) name,
- INST_LIST
-#undef INST
- INST_COUNT
-};
-
-#define PROC_FIELDS \
- PROC_FIELD(u64, ip) \
- PROC_FIELD(u64, sp) \
- PROC_FIELD(u64, mb0a) \
- PROC_FIELD(u64, mb0s) \
- PROC_FIELD(u64, mb1a) \
- PROC_FIELD(u64, mb1s) \
- PROC_FIELD(u64, r0x) \
- PROC_FIELD(u64, r1x) \
- PROC_FIELD(u64, r2x) \
- PROC_FIELD(u64, r3x) \
- PROC_FIELD(u64, s0) \
- PROC_FIELD(u64, s1) \
- PROC_FIELD(u64, s2) \
- PROC_FIELD(u64, s3) \
- PROC_FIELD(u64, s4) \
- PROC_FIELD(u64, s5) \
- PROC_FIELD(u64, s6) \
- PROC_FIELD(u64, s7)
-
-struct Proc {
-#define PROC_FIELD(type, name) type name;
- PROC_FIELDS
-#undef PROC_FIELD
-};
-
-u64 arch_proc_mb0_addr(const Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
- return proc_get(core, pix)->mb0a;
-}
-
-u64 arch_proc_mb0_size(const Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
- return proc_get(core, pix)->mb0s;
-}
-
-u64 arch_proc_mb1_addr(const Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
- return proc_get(core, pix)->mb1a;
-}
-
-u64 arch_proc_mb1_size(const Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
- return proc_get(core, pix)->mb1s;
-}
-
-u64 arch_proc_ip_addr(const Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
- return proc_get(core, pix)->ip;
-}
-
-u64 arch_proc_sp_addr(const Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
- return proc_get(core, pix)->sp;
-}
-
-u64 arch_proc_slice(const Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- (void)core;
- (void)pix;
-
- return 1;
-}
-
-void _free_memory_block(Core *core, u64 addr, u64 size) {
- assert(core);
- assert(size);
-
- for (u64 i = 0; i < size; ++i) {
- mvec_free(core, addr + i);
- }
-}
-
-void arch_on_proc_kill(Core *core) {
- assert(core);
- assert(core->pnum > 1);
-
- Proc *pfst = proc_fetch(core, core->pfst);
-
- _free_memory_block(core, pfst->mb0a, pfst->mb0s);
-
- if (pfst->mb1s) {
- _free_memory_block(core, pfst->mb1a, pfst->mb1s);
- }
-
- memcpy(pfst, &g_dead_proc, sizeof(Proc));
-}
-
-#if ACTION == ACT_BENCH || ACTION == ACT_NEW
-void arch_anc_init(Core *core, u64 size) {
- assert(core);
-
-#if ANC_HALF == 1
- u64 addr = U64_HALF;
-#else
- u64 addr = 0;
-#endif
-
- for (int i = 0; i < ANC_CLONES; ++i) {
- u64 addr_clone = addr + ((MVEC_SIZE / ANC_CLONES) * i);
-
- Proc *panc = proc_fetch(core, i);
-
- panc->mb0a = addr_clone;
- panc->mb0s = size;
- panc->ip = addr_clone;
- panc->sp = addr_clone;
- }
-}
-#endif
-
-u8 _get_inst(const Core *core, u64 addr) {
- assert(core);
-
- return mvec_get_inst(core, addr) % INST_COUNT;
-}
-
-void _increment_ip(Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- Proc *proc = proc_fetch(core, pix);
-
- proc->ip++;
- proc->sp = proc->ip;
-}
-
-bool _is_between(u8 inst, u8 lo, u8 hi) {
- assert(inst < INST_COUNT);
- assert(lo < INST_COUNT);
- assert(hi < INST_COUNT);
- assert(lo < hi);
-
- return (inst >= lo) && (inst <= hi);
-}
-
-bool _is_key(u8 inst) {
- assert(inst < INST_COUNT);
-
- return _is_between(inst, keya, keyp);
-}
-
-bool _is_lock(u8 inst) {
- assert(inst < INST_COUNT);
-
- return _is_between(inst, loka, lokp);
-}
-
-bool _is_rmod(u8 inst) {
- assert(inst < INST_COUNT);
-
- return _is_between(inst, nop0, nop3);
-}
-
-bool _key_lock_match(u8 key, u8 lock) {
- assert(key < INST_COUNT);
- assert(lock < INST_COUNT);
- assert(_is_key(key));
-
- return (key - keya) == (lock - loka);
-}
-
-bool _seek(Core *core, u64 pix, bool fwrd) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- Proc *proc = proc_fetch(core, pix);
- u8 next = _get_inst(core, proc->ip + 1);
-
- if (!_is_key(next)) {
- _increment_ip(core, pix);
- return false;
- }
-
- u8 spin = _get_inst(core, proc->sp);
-
- if (_key_lock_match(next, spin)) {
- return true;
- }
-
- if (fwrd) {
- proc->sp++;
- } else {
- proc->sp--;
- }
-
- return false;
-}
-
-void _jump(Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- Proc *proc = proc_fetch(core, pix);
-
-#ifndef NDEBUG
- u8 next = _get_inst(core, proc->ip + 1);
- u8 spin = _get_inst(core, proc->sp);
- assert(_is_key(next));
- assert(_is_lock(spin));
- assert(_key_lock_match(next, spin));
-#endif
-
- proc->ip = proc->sp;
-}
-
-void _get_reg_addr_list(Core *core, u64 pix, u64 **rlist, int rcount, bool offset) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- assert(rlist);
- assert(rcount);
- assert(rcount < 4);
-
- Proc *proc = proc_fetch(core, pix);
- u64 madr = proc->ip + (offset ? 2 : 1);
-
- for (int i = 0; i < rcount; ++i) {
- rlist[i] = &proc->r0x;
- }
-
- for (int i = 0; i < rcount; ++i) {
- u64 mnxt = madr + i;
- u8 mins = _get_inst(core, mnxt);
-
- if (!_is_rmod(mins)) {
- break;
- }
-
- switch (mins) {
- case nop0:
- rlist[i] = &proc->r0x;
- break;
- case nop1:
- rlist[i] = &proc->r1x;
- break;
- case nop2:
- rlist[i] = &proc->r2x;
- break;
- case nop3:
- rlist[i] = &proc->r3x;
- break;
- }
- }
-}
-
-void _addr(Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- Proc *proc = proc_fetch(core, pix);
- u64 *reg;
-
-#ifndef NDEBUG
- u8 next = _get_inst(core, proc->ip + 1);
- u8 spin = _get_inst(core, proc->sp);
- assert(_is_key(next));
- assert(_is_lock(spin));
- assert(_key_lock_match(next, spin));
-#endif
-
- _get_reg_addr_list(core, pix, &reg, 1, true);
- *reg = proc->sp;
-
- _increment_ip(core, pix);
-}
-
-void _ifnz(Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- Proc *proc = proc_fetch(core, pix);
- u64 *reg;
-
- _get_reg_addr_list(core, pix, &reg, 1, false);
-
- u64 jmod = _is_rmod(_get_inst(core, proc->ip + 1)) ? 1 : 0;
- u64 rmod = *reg ? 1 : 2;
-
- proc->ip += jmod + rmod;
- proc->sp = proc->ip;
-}
-
-void _free_child_memory_of(Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- Proc *proc = proc_fetch(core, pix);
-
- assert(proc->mb1s);
-
- _free_memory_block(core, proc->mb1a, proc->mb1s);
-
- proc->mb1a = 0;
- proc->mb1s = 0;
-}
-
-void _alloc(Core *core, u64 pix, bool fwrd) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- Proc *proc = proc_fetch(core, pix);
- u64 *regs[2];
-
- _get_reg_addr_list(core, pix, regs, 2, false);
-
- u64 bsize = *regs[0];
-
- // do nothing if block-size is zero
- if (!bsize) {
- _increment_ip(core, pix);
- return;
- }
-
- // do nothing if sp is not adjacent to allocated mem. block
- if (proc->mb1s) {
- u64 exp_addr = proc->mb1a;
-
- if (fwrd) {
- exp_addr += proc->mb1s;
- } else {
- exp_addr--;
- }
-
- if (proc->sp != exp_addr) {
- _increment_ip(core, pix);
- return;
- }
- }
-
- // allocation was successful, store block address on register
- if (proc->mb1s == bsize) {
- _increment_ip(core, pix);
- *regs[1] = proc->mb1a;
- return;
- }
-
- // sp collided with another allocated block, clear and try again
- if (mvec_is_alloc(core, proc->sp)) {
- if (proc->mb1s) {
- _free_child_memory_of(core, pix);
- }
-
- if (fwrd) {
- proc->sp++;
- } else {
- proc->sp--;
- }
-
- return;
- }
-
- // otherwise enlarge block
- mvec_alloc(core, proc->sp);
-
- // adjust child block address and size
- if (!proc->mb1s || !fwrd) {
- proc->mb1a = proc->sp;
- }
-
- proc->mb1s++;
-
- // move sp to new location
- if (fwrd) {
- proc->sp++;
- } else {
- proc->sp--;
- }
-}
-
-void _bswap(Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- Proc *proc = proc_fetch(core, pix);
-
- if (proc->mb1s) {
- u64 tmpa = proc->mb0a;
- u64 tmps = proc->mb0s;
-
- proc->mb0a = proc->mb1a;
- proc->mb0s = proc->mb1s;
- proc->mb1a = tmpa;
- proc->mb1s = tmps;
- }
-
- _increment_ip(core, pix);
-}
-
-void _bclear(Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- Proc *proc = proc_fetch(core, pix);
-
- if (proc->mb1s) {
- _free_child_memory_of(core, pix);
- }
-
- _increment_ip(core, pix);
-}
-
-void _split(Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- Proc *proc = proc_fetch(core, pix);
-
- if (proc->mb1s) {
- Proc child = {0};
-
- child.ip = proc->mb1a;
- child.sp = proc->mb1a;
- child.mb0a = proc->mb1a;
- child.mb0s = proc->mb1s;
-
- proc->mb1a = 0;
- proc->mb1s = 0;
-
- proc_new(core, &child);
- } else {
- assert(!proc->mb1a);
- }
-
- _increment_ip(core, pix);
-}
-
-void _3rop(Core *core, u64 pix, u8 inst) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- u64 *regs[3];
-
- _get_reg_addr_list(core, pix, regs, 3, false);
-
- switch (inst) {
- case addn:
- *regs[0] = *regs[1] + *regs[2];
- break;
- case subn:
- *regs[0] = *regs[1] - *regs[2];
- break;
- case muln:
- *regs[0] = *regs[1] * *regs[2];
- break;
- case divn:
- // do nothing on div. by zero
- if (*regs[2]) {
- *regs[0] = *regs[1] / *regs[2];
- }
-
- break;
- default:
- assert(false);
- }
-
- _increment_ip(core, pix);
-}
-
-void _1rop(Core *core, u64 pix, u8 inst) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- u64 *reg;
-
- _get_reg_addr_list(core, pix, &reg, 1, false);
-
- switch (inst) {
- case incn:
- (*reg)++;
- break;
- case decn:
- (*reg)--;
- break;
- case notn:
- *reg = !(*reg);
- break;
- case shfl:
- *reg <<= 1;
- break;
- case shfr:
- *reg >>= 1;
- break;
- case zero:
- *reg = 0;
- break;
- case unit:
- *reg = 1;
- break;
- default:
- assert(false);
- }
-
- _increment_ip(core, pix);
-}
-
-void _push(Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- Proc *proc = proc_fetch(core, pix);
- u64 *reg;
-
- _get_reg_addr_list(core, pix, &reg, 1, false);
-
- proc->s7 = proc->s6;
- proc->s6 = proc->s5;
- proc->s5 = proc->s4;
- proc->s4 = proc->s3;
- proc->s3 = proc->s2;
- proc->s2 = proc->s1;
- proc->s1 = proc->s0;
- proc->s0 = *reg;
-
- _increment_ip(core, pix);
-}
-
-void _pop(Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- Proc *proc = proc_fetch(core, pix);
- u64 *reg;
-
- _get_reg_addr_list(core, pix, &reg, 1, false);
-
- *reg = proc->s0;
- proc->s0 = proc->s1;
- proc->s1 = proc->s2;
- proc->s2 = proc->s3;
- proc->s3 = proc->s4;
- proc->s4 = proc->s5;
- proc->s5 = proc->s6;
- proc->s6 = proc->s7;
- proc->s7 = 0;
-
- _increment_ip(core, pix);
-}
-
-int _sp_dir(u64 src, u64 dst) {
- if (src == dst) {
- return 0;
- } else if (src - dst <= dst - src) {
- return -1;
- } else {
- return 1;
- }
-}
-
-void _load(Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- Proc *proc = proc_fetch(core, pix);
- u64 *regs[2];
-
- _get_reg_addr_list(core, pix, regs, 2, false);
-
- int sp_dir = _sp_dir(proc->sp, *regs[0]);
-
- if (sp_dir == 1) {
- proc->sp++;
- } else if (sp_dir == -1) {
- proc->sp--;
- } else {
- *regs[1] = mvec_get_inst(core, *regs[0]);
- _increment_ip(core, pix);
- }
-}
-
-bool _is_writeable_by(const Core *core, u64 addr, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- return !mvec_is_alloc(core, addr) || mvec_is_proc_owner(core, addr, pix);
-}
-
-void _write(Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- Proc *proc = proc_fetch(core, pix);
- u64 *regs[2];
-
- _get_reg_addr_list(core, pix, regs, 2, false);
-
- int sp_dir = _sp_dir(proc->sp, *regs[0]);
-
- if (sp_dir == 1) {
- proc->sp++;
- } else if (sp_dir == -1) {
- proc->sp--;
- } else {
- if (_is_writeable_by(core, *regs[0], pix)) {
- mvec_set_inst(core, *regs[0], *regs[1] % INST_CAPS);
- }
-
- _increment_ip(core, pix);
- }
-}
-
-void _2rop(Core *core, u64 pix, u8 inst) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- u64 *regs[2];
-
- _get_reg_addr_list(core, pix, regs, 2, false);
-
- switch (inst) {
- case dupl:
- *regs[1] = *regs[0];
- break;
- case swap:
- {
- u64 tmp = *regs[0];
- *regs[0] = *regs[1];
- *regs[1] = tmp;
- }
-
- break;
- default:
- assert(false);
- }
-
- _increment_ip(core, pix);
-}
-
-void arch_proc_step(Core *core, u64 pix) {
- assert(core);
- assert(proc_is_live(core, pix));
-
- Proc *proc = proc_fetch(core, pix);
- u8 inst = _get_inst(core, proc->ip);
-
- switch (inst) {
- case jmpb:
- if (_seek(core, pix, false)) {
- _jump(core, pix);
- }
-
- break;
- case jmpf:
- if (_seek(core, pix, true)) {
- _jump(core, pix);
- }
-
- break;
- case adrb:
- if (_seek(core, pix, false)) {
- _addr(core, pix);
- }
-
- break;
- case adrf:
- if (_seek(core, pix, true)) {
- _addr(core, pix);
- }
-
- break;
- case ifnz:
- _ifnz(core, pix);
- break;
- case allb:
- _alloc(core, pix, false);
- break;
- case allf:
- _alloc(core, pix, true);
- break;
- case bswp:
- _bswap(core, pix);
- break;
- case bclr:
- _bclear(core, pix);
- break;
- case splt:
- _split(core, pix);
- break;
- case addn:
- case subn:
- case muln:
- case divn:
- _3rop(core, pix, inst);
- break;
- case incn:
- case decn:
- case notn:
- case shfl:
- case shfr:
- case zero:
- case unit:
- _1rop(core, pix, inst);
- break;
- case pshn:
- _push(core, pix);
- break;
- case popn:
- _pop(core, pix);
- break;
- case load:
- _load(core, pix);
- break;
- case wrte:
- _write(core, pix);
- break;
- case dupl:
- case swap:
- _2rop(core, pix, inst);
- break;
- default:
- _increment_ip(core, pix);
- break;
- }
-
- return;
-}
-
-#ifndef NDEBUG
-void arch_validate_proc(const Core *core, u64 pix) {
- assert(core);
-
- const Proc *proc = proc_get(core, pix);
-
- assert(proc->mb0s);
-
- if (proc->mb1a) {
- assert(proc->mb1s);
- }
-
- for (u64 i = 0; i < proc->mb0s; ++i) {
- u64 addr = proc->mb0a + i;
- assert(mvec_is_alloc(core, addr));
- assert(mvec_is_proc_owner(core, addr, pix));
- }
-
- for (u64 i = 0; i < proc->mb1s; ++i) {
- u64 addr = proc->mb1a + i;
- assert(mvec_is_alloc(core, addr));
- assert(mvec_is_proc_owner(core, addr, pix));
- }
-}
-#endif
-
-wchar_t arch_symbol(u8 inst) {
- switch (inst % INST_COUNT) {
-#define INST(name, symb) case name: return symb;
- INST_LIST
-#undef INST
- }
-
- assert(false);
- return L'\0';
-}
-
-void arch_mnemonic(u8 inst, char *buff) {
- assert(buff);
-
- switch (inst % INST_COUNT) {
-#define INST(name, symb) case name: snprintf(buff, MNEMONIC_BUFF_SIZE, #name); return;
- INST_LIST
-#undef INST
- }
-
- assert(false);
-}
diff --git a/src/graphics.c b/src/graphics.c
deleted file mode 100644
index 780b879..0000000
--- a/src/graphics.c
+++ /dev/null
@@ -1,238 +0,0 @@
-// 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;
-
- g_gfx_inst[i] += mvec_get_byte(core, addr);
- g_gfx_mall[i] += mvec_is_alloc(core, addr) ? 1 : 0;
- }
- }
-}
-
-void gfx_clear_array(u64 *arry) {
- assert(arry);
- memset(arry, 0, g_gfx_vsiz * sizeof(u64));
-}
-
-#ifdef MVEC_LOOP
-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
-}
-#else
-void gfx_accumulate_pixel(u64 pos, u64 zoom, u64 pixa, u64 *arry) {
- assert(arry);
-
- u64 end = pos + (g_gfx_vsiz * zoom);
-
- if (pixa < pos || pixa >= end) {
- return;
- }
-
- u64 pixi = (pixa - pos) / zoom;
- assert(pixi < g_gfx_vsiz);
- arry[pixi]++;
-}
-#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
deleted file mode 100644
index 348385f..0000000
--- a/src/salis.c
+++ /dev/null
@@ -1,811 +0,0 @@
-// Project: Salis
-// Author: Paul Oliver
-// Email: contact@pauloliver.dev
-
-/*
- * Core of the Salis simulator. Can be built against different architectures
- * and UI modules.
- */
-
-#include <assert.h>
-#include <ctype.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <threads.h>
-
-#ifdef COMPRESS
-#include <zlib.h>
-#endif
-
-#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)
-
-#define U64_HALF (0x8000000000000000)
-
-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
-
-#ifdef MVEC_LOOP
-u64 mvec_loop(u64 addr) {
- return addr % MVEC_SIZE;
-}
-#endif
-
-bool mvec_is_alloc(const Core *core, u64 addr) {
- assert(core);
-#ifdef MVEC_LOOP
- return core->mvec[mvec_loop(addr)] & MALL_FLAG ? true : false;
-#else
- if (addr < MVEC_SIZE) {
- return core->mvec[addr] & MALL_FLAG ? true : false;
- } else {
- return true;
- }
-#endif
-}
-
-void mvec_alloc(Core *core, u64 addr) {
- assert(core);
- assert(!mvec_is_alloc(core, addr));
-#ifdef MVEC_LOOP
- core->mvec[mvec_loop(addr)] |= MALL_FLAG;
-#else
- assert(addr < MVEC_SIZE);
- core->mvec[addr] |= MALL_FLAG;
-#endif
- core->mall++;
-}
-
-void mvec_free(Core *core, u64 addr) {
- assert(core);
- assert(mvec_is_alloc(core, addr));
-#ifdef MVEC_LOOP
- core->mvec[mvec_loop(addr)] ^= MALL_FLAG;
-#else
- assert(addr < MVEC_SIZE);
- core->mvec[addr] ^= MALL_FLAG;
-#endif
- core->mall--;
-}
-
-u8 mvec_get_byte(const Core *core, u64 addr) {
- assert(core);
-#ifdef MVEC_LOOP
- return core->mvec[mvec_loop(addr)];
-#else
- if (addr < MVEC_SIZE) {
- return core->mvec[addr];
- } else {
- return 0;
- }
-#endif
-}
-
-u8 mvec_get_inst(const Core *core, u64 addr) {
- assert(core);
-#ifdef MVEC_LOOP
- return core->mvec[mvec_loop(addr)] & INST_MASK;
-#else
- if (addr < MVEC_SIZE) {
- return core->mvec[addr] & INST_MASK;
- } else {
- return 0;
- }
-#endif
-}
-
-void mvec_set_inst(Core *core, u64 addr, u8 inst) {
- assert(core);
- assert(inst < INST_CAPS);
-#ifdef MVEC_LOOP
- core->mvec[mvec_loop(addr)] &= MALL_FLAG;
- core->mvec[mvec_loop(addr)] |= inst;
-#else
- assert(addr < MVEC_SIZE);
- core->mvec[addr] &= MALL_FLAG;
- core->mvec[addr] |= inst;
-#endif
-}
-
-#if MUTA_FLIP_BIT == 1
-void mvec_flip_bit(Core *core, u64 addr, int bit) {
- assert(core);
- assert(bit < 8);
- core->mvec[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);
-
-#if ANC_HALF == 1
- u64 addr = U64_HALF;
-#else
- u64 addr = 0;
-#endif
-
- 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) {
- for (u64 j = 0; j < ANC_CLONES; ++j) {
- u64 addr_clone = addr + ((MVEC_SIZE / ANC_CLONES) * j);
-
- mvec_alloc(core, addr_clone);
- mvec_set_inst(core, addr_clone, 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 = ANC_CLONES;
- core->pcap = ANC_CLONES;
- core->plst = ANC_CLONES - 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);
-
-#if ANC_HALF == 1
- anc_size -= U64_HALF;
-#endif
-
- 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) {
-#ifdef COMPRESS
- size_t size = 0;
- char *in = NULL;
- FILE *f = open_memstream(&in, &size);
-#else
- FILE *f = fopen(path, "wb");
-#endif
-
- 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);
-
-#ifdef COMPRESS
- assert(size);
-
- char *out = malloc(size);
- assert(out);
-
- z_stream strm = { 0 };
- strm.zalloc = NULL,
- strm.zfree = NULL,
- strm.opaque = NULL,
-
- deflateInit(&strm, Z_DEFAULT_COMPRESSION);
-
- strm.avail_in = size;
- strm.avail_out = size;
- strm.next_in = (Bytef *)in;
- strm.next_out = (Bytef *)out;
-
- deflate(&strm, Z_FINISH);
-
- FILE *fx = fopen(path, "wb");
- assert(fx);
-
- fwrite(&size, sizeof(size_t), 1, fx);
- fwrite(out, sizeof(char), strm.total_out, fx);
- fclose(fx);
-
- deflateEnd(&strm);
-
- free(in);
- free(out);
-#endif
-}
-
-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() {
-#ifdef COMPRESS
- FILE *fx = fopen(SIM_PATH, "rb");
- assert(fx);
-
- fseek(fx, 0, SEEK_END);
- size_t x_size = ftell(fx) - sizeof(size_t);
- char *in = malloc(x_size);
- rewind(fx);
- assert(x_size);
- assert(in);
-
- size_t size = 0;
- fread(&size, sizeof(size_t), 1, fx);
- fread(in, 1, x_size, fx);
- fclose(fx);
- assert(size);
-
- char *out = malloc(size);
- assert(out);
-
- z_stream strm = { 0 };
- strm.next_in = (Bytef *)in;
- strm.avail_in = x_size;
- strm.zalloc = NULL;
- strm.zfree = NULL;
- strm.opaque = NULL;
-
- inflateInit(&strm);
-
- strm.avail_out = size;
- strm.next_out = (Bytef *)out;
-
-#ifdef NDEBUG
- inflate(&strm, Z_FINISH);
-#else
- assert(inflate(&strm, Z_FINISH));
-#endif
-
- inflateEnd(&strm);
-
- FILE *f = fmemopen(out, size, "rb");
-#else
- FILE *f = fopen(SIM_PATH, "rb");
-#endif
-
- 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);
-
-#ifdef COMPRESS
- free(in);
- free(out);
-#endif
-}
-#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/ui/curses/ui.j2.c
index d1f821d..4370774 100644
--- a/src/ui/curses.c
+++ b/ui/curses/ui.j2.c
@@ -1,30 +1,36 @@
+// Author: Paul Oliver <contact@pauloliver.dev>
// Project: Salis
-// Author: Paul Oliver
-// Email: contact@pauloliver.dev
-/*
- * Implements a TUI for the Salis simulator using the ncurses library.
- */
+// Implements a curses TUI for the Salis simulator.
+// World view renders the contents of the VM memory buffer into a 7 channel image.
+// It supports zooming in and out, condensing the state of several bytes of memory
+// into single pixels (terminal cells). When zoomed in, each pixel represents a
+// single byte in memory.
-// GCC_EXTRA_FLAGS -lncursesw
+{% set pane_width = 27 %}
+{% set proc_field_width = 21 %}
+{% set proc_page_lines = 12 %}
-#include <curses.h>
-#include <locale.h>
-#include <time.h>
+{% macro ctrl(x) %}('{{ x }}' & 0x1f){% endmacro %}
-#define CTRL(x) ((x) & 0x1f)
-#define PANE_WIDTH (27)
-#define PROC_FIELD_WIDTH (21)
-#define PROC_PAGE_LINES (12)
+{% if not args.optimized %}
+ {% set min_fps = 5 %}
+ {% set max_fps = 10 %}
+{% else %}
+ {% set min_fps = 30 %}
+ {% set max_fps = 60 %}
+{% endif %}
+// pages
enum {
PAGE_CORE,
PAGE_PROCESS,
PAGE_WORLD,
PAGE_IPC,
- PAGE_COUNT
+ PAGE_COUNT,
};
+// color pairs
enum {
PAIR_NOUSE,
PAIR_NORMAL,
@@ -40,28 +46,39 @@ enum {
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;
+// GFX globals
+uint64_t g_gfx_vsiz; // zoom level
+uint64_t *g_gfx_inst; // instruction channel
+uint64_t *g_gfx_mall; // allocated state channel
+uint64_t *g_gfx_mbst; // memory block start channel
+uint64_t *g_gfx_mb0s; // selected organism's memory block #1 channel
+uint64_t *g_gfx_mb1s; // selected organism's memory block #2 channel
+uint64_t *g_gfx_ipas; // selected organism's IP channel
+uint64_t *g_gfx_spas; // selected organism's SP channel
+
+// TUI globals
+bool g_exit;
+bool g_running;
+unsigned g_core;
+unsigned g_page;
+bool g_proc_genes;
+uint64_t g_proc_scroll;
+uint64_t g_proc_field_scroll;
+uint64_t g_proc_gene_scroll;
+uint64_t g_proc_selected;
+uint64_t g_wrld_pos;
+uint64_t g_wrld_zoom;
+bool g_wcursor_mode;
+int g_wcursor_x;
+int g_wcursor_y;
+uint64_t g_wcursor_pointed;
+uint64_t g_vlin;
+uint64_t g_vsiz;
+uint64_t g_vlin_rng;
+uint64_t g_vsiz_rng;
+uint64_t g_ivpt_scroll;
+char *g_line_buff;
+uint64_t g_step_block;
const wchar_t *g_zoomed_symbols = (
L"⠀⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇⠈⠉⠊⠋⠌⠍⠎⠏⡈⡉⡊⡋⡌⡍⡎⡏⠐⠑⠒⠓⠔⠕⠖⠗⡐⡑⡒⡓⡔⡕⡖⡗⠘⠙⠚⠛⠜⠝⠞⠟⡘⡙⡚⡛⡜⡝⡞⡟"
@@ -70,8 +87,231 @@ const wchar_t *g_zoomed_symbols = (
L"⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯⢰⢱⢲⢳⢴⢵⢶⢷⣰⣱⣲⣳⣴⣵⣶⣷⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿"
);
-#include "graphics.c"
+// ----------------------------------------------------------------------------
+// GFX functions
+// ----------------------------------------------------------------------------
+void gfx_init(uint64_t vsiz) {
+ assert(vsiz);
+
+ g_gfx_vsiz = vsiz;
+
+ g_gfx_inst = calloc(g_gfx_vsiz, sizeof(uint64_t));
+ g_gfx_mall = calloc(g_gfx_vsiz, sizeof(uint64_t));
+ g_gfx_mbst = calloc(g_gfx_vsiz, sizeof(uint64_t));
+ g_gfx_mb0s = calloc(g_gfx_vsiz, sizeof(uint64_t));
+ g_gfx_mb1s = calloc(g_gfx_vsiz, sizeof(uint64_t));
+ g_gfx_ipas = calloc(g_gfx_vsiz, sizeof(uint64_t));
+ g_gfx_spas = calloc(g_gfx_vsiz, sizeof(uint64_t));
+
+ assert(g_gfx_inst);
+ assert(g_gfx_mall);
+ assert(g_gfx_mbst);
+ assert(g_gfx_mb0s);
+ assert(g_gfx_mb1s);
+ assert(g_gfx_ipas);
+ assert(g_gfx_spas);
+}
+
+void gfx_free() {
+ if (g_gfx_vsiz == 0) {
+ return;
+ }
+
+ assert(g_gfx_inst);
+ assert(g_gfx_mall);
+ assert(g_gfx_mbst);
+ assert(g_gfx_mb0s);
+ assert(g_gfx_mb1s);
+ assert(g_gfx_ipas);
+ assert(g_gfx_spas);
+
+ g_gfx_vsiz = 0;
+
+ free(g_gfx_inst);
+ free(g_gfx_mall);
+ free(g_gfx_mbst);
+ free(g_gfx_mb0s);
+ free(g_gfx_mb1s);
+ free(g_gfx_ipas);
+ free(g_gfx_spas);
+
+ g_gfx_inst = NULL;
+ g_gfx_mall = NULL;
+ g_gfx_mbst = NULL;
+ g_gfx_mb0s = NULL;
+ g_gfx_mb1s = NULL;
+ g_gfx_ipas = NULL;
+ g_gfx_spas = NULL;
+}
+
+void gfx_resize(uint64_t vsiz) {
+ assert(vsiz);
+
+ gfx_free();
+ gfx_init(vsiz);
+}
+
+void gfx_render_inst(const struct Core *core, uint64_t pos, uint64_t zoom) {
+ assert(core);
+
+ for (uint64_t i = 0; i < g_gfx_vsiz; ++i) {
+ g_gfx_inst[i] = 0;
+ g_gfx_mall[i] = 0;
+
+ for (uint64_t j = 0; j < zoom; ++j) {
+ uint64_t addr = pos + (i * zoom) + j;
+
+ g_gfx_inst[i] += mvec_get_byte(core, addr);
+ g_gfx_mall[i] += mvec_is_alloc(core, addr) ? 1 : 0;
+ }
+ }
+}
+
+void gfx_clear_array(uint64_t *arry) {
+ assert(arry);
+ memset(arry, 0, g_gfx_vsiz * sizeof(uint64_t));
+}
+
+{% if arch_vars.mvec_loop %}
+void gfx_accumulate_pixel(uint64_t pos, uint64_t zoom, uint64_t pixa, uint64_t *arry) {
+ assert(arry);
+
+ uint64_t beg_mod = pos % {{ mvec_size }};
+ uint64_t end_mod = beg_mod + (g_gfx_vsiz * zoom);
+ uint64_t pix_mod = pixa % {{ mvec_size }};
+
+ {% if not args.optimized %}
+ uint64_t inc_cnt = 0;
+ {% endif %}
+
+ while (pix_mod < end_mod) {
+ if (pix_mod >= beg_mod && pix_mod < end_mod) {
+ uint64_t pixi = (pix_mod - beg_mod) / zoom;
+ assert(pixi < g_gfx_vsiz);
+ arry[pixi]++;
+
+ {% if not args.optimized %}
+ inc_cnt++;
+ {% endif %}
+ }
+
+ pix_mod += {{ mvec_size }};
+ }
+
+ {% if not args.optimized %}
+ if (zoom != 1) {
+ assert(inc_cnt <= 2);
+ }
+ {% endif %}
+}
+{% endif %}
+
+{% if not arch_vars.mvec_loop %}
+void gfx_accumulate_pixel(uint64_t pos, uint64_t zoom, uint64_t pixa, uint64_t *arry) {
+ assert(arry);
+
+ uint64_t end = pos + (g_gfx_vsiz * zoom);
+
+ if (pixa < pos || pixa >= end) {
+ return;
+ }
+
+ uint64_t pixi = (pixa - pos) / zoom;
+ assert(pixi < g_gfx_vsiz);
+ arry[pixi]++;
+}
+{% endif %}
+
+void gfx_render_mbst(const struct Core *core, uint64_t pos, uint64_t zoom) {
+ assert(core);
+
+ gfx_clear_array(g_gfx_mbst);
+
+ for (uint64_t pix = core->pfst; pix <= core->plst; ++pix) {
+ uint64_t mb0a = arch_proc_mb0_addr(core, pix);
+ uint64_t mb1a = arch_proc_mb1_addr(core, pix);
+
+ gfx_accumulate_pixel(pos, zoom, mb0a, g_gfx_mbst);
+ gfx_accumulate_pixel(pos, zoom, mb1a, g_gfx_mbst);
+ }
+}
+void gfx_render_mb0s(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t psel) {
+ assert(core);
+
+ gfx_clear_array(g_gfx_mb0s);
+
+ if (psel < core->pfst || psel > core->plst) {
+ return;
+ }
+
+ uint64_t mb0a = arch_proc_mb0_addr(core, psel);
+ uint64_t mb0s = arch_proc_mb0_size(core, psel);
+
+ for (uint64_t i = 0; i < mb0s; ++i) {
+ gfx_accumulate_pixel(pos, zoom, mb0a + i, g_gfx_mb0s);
+ }
+}
+
+void gfx_render_mb1s(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t psel) {
+ assert(core);
+
+ gfx_clear_array(g_gfx_mb1s);
+
+ if (psel < core->pfst || psel > core->plst) {
+ return;
+ }
+
+ uint64_t mb1a = arch_proc_mb1_addr(core, psel);
+ uint64_t mb1s = arch_proc_mb1_size(core, psel);
+
+ for (uint64_t i = 0; i < mb1s; ++i) {
+ gfx_accumulate_pixel(pos, zoom, mb1a + i, g_gfx_mb1s);
+ }
+}
+
+void gfx_render_ipas(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t psel) {
+ assert(core);
+
+ gfx_clear_array(g_gfx_ipas);
+
+ if (psel < core->pfst || psel > core->plst) {
+ return;
+ }
+
+ uint64_t ipa = arch_proc_ip_addr(core, psel);
+
+ gfx_accumulate_pixel(pos, zoom, ipa, g_gfx_ipas);
+}
+
+void gfx_render_spas(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t psel) {
+ assert(core);
+
+ gfx_clear_array(g_gfx_spas);
+
+ if (psel < core->pfst || psel > core->plst) {
+ return;
+ }
+
+ uint64_t spa = arch_proc_sp_addr(core, psel);
+
+ gfx_accumulate_pixel(pos, zoom, spa, g_gfx_spas);
+}
+
+void gfx_render(const struct Core *core, uint64_t pos, uint64_t zoom, uint64_t psel) {
+ assert(core);
+
+ gfx_render_inst(core, pos, zoom);
+ gfx_render_mbst(core, pos, zoom);
+ gfx_render_mb0s(core, pos, zoom, psel);
+ gfx_render_mb1s(core, pos, zoom, psel);
+ gfx_render_ipas(core, pos, zoom, psel);
+ gfx_render_spas(core, pos, zoom, psel);
+}
+
+// ----------------------------------------------------------------------------
+// TUI functions
+// ----------------------------------------------------------------------------
void ui_line_buff_free() {
if (g_line_buff) {
free(g_line_buff);
@@ -142,13 +382,14 @@ void ui_str_field(int l, const char *label, const char *value) {
ui_line(false, l, PAIR_NORMAL, A_NORMAL, "%s : %18s", label, value);
}
-void ui_ulx_field(int l, const char *label, u64 value) {
+void ui_ulx_field(int l, const char *label, uint64_t value) {
assert(label);
ui_line(false, l, PAIR_NORMAL, A_NORMAL, "%-4s : %#18lx", label, value);
}
void ui_print_core(int l) {
ui_line(false, ++l, PAIR_HEADER, A_BOLD, "CORE [%d]", g_core);
+ ui_ulx_field(++l, "cycl", g_cores[g_core].cycl);
ui_ulx_field(++l, "mall", g_cores[g_core].mall);
ui_ulx_field(++l, "mut0", g_cores[g_core].muta[0]);
ui_ulx_field(++l, "mut1", g_cores[g_core].muta[1]);
@@ -160,38 +401,42 @@ void ui_print_core(int l) {
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);
+
+ // Architecture specific fields
+ {% for type, val in arch_vars.core_fields %}
+ ui_ulx_field(++l, "{{ val }}", g_cores[g_core].{{ val }});
+ {% endfor %}
}
-int ui_proc_pair(u64 pix) {
+int ui_proc_pair(uint64_t pix) {
if (pix == g_proc_selected) {
return PAIR_SELECTED_PROC;
- } else if (proc_is_live(&g_cores[g_core], pix)) {
+ } else if (mvec_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";
+const char *ui_proc_state(uint64_t pix) {
+ return mvec_proc_is_live(&g_cores[g_core], pix) ? "live" : "dead";
}
void ui_print_process_genome_header(int l) {
ui_line(false, l++, PAIR_NORMAL, A_NORMAL, "%s : %18s : %s", "stat", "pix", "genome");
}
-void ui_print_process_gene(int l, int gcol, u64 gidx, u64 mba, u64 pix, int pair) {
- assert(gcol >= PANE_WIDTH + 2);
+void ui_print_process_gene(int l, int gcol, uint64_t gidx, uint64_t mba, uint64_t pix, int pair) {
+ assert(gcol >= {{ pane_width }} + 2);
assert(gcol < COLS);
- assert(proc_is_live(&g_cores[g_core], pix));
+ assert(mvec_proc_is_live(&g_cores[g_core], pix));
assert(pair == PAIR_SELECTED_MB1 || pair == PAIR_SELECTED_MB2);
- const Core *core = &g_cores[g_core];
+ const struct Core *core = &g_cores[g_core];
- u64 addr = mba + gidx;
- u8 byte = mvec_get_byte(core, addr);
+ uint64_t addr = mba + gidx;
+ uint8_t byte = mvec_get_byte(core, addr);
wchar_t gsym[2] = { arch_symbol(byte), L'\0' };
cchar_t cchar = { 0 };
@@ -210,29 +455,29 @@ void ui_print_process_gene(int l, int gcol, u64 gidx, u64 mba, u64 pix, int pair
mvadd_wch(l, gcol, &cchar);
}
-void ui_print_process_genes(int l, u64 pix) {
+void ui_print_process_genes(int l, uint64_t pix) {
ui_line(true, l, ui_proc_pair(pix), A_NORMAL, "%s : %#18lx :", ui_proc_state(pix), pix);
- if (!proc_is_live(&g_cores[g_core], pix)) {
+ if (!mvec_proc_is_live(&g_cores[g_core], pix)) {
return;
}
- const Core *core = &g_cores[g_core];
+ const struct 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);
+ int scol = {{ pane_width }} + 2;
+ int gcol = scol - g_proc_gene_scroll;
+ uint64_t mb0a = arch_proc_mb0_addr(core, pix);
+ uint64_t mb0s = arch_proc_mb0_size(core, pix);
+ uint64_t mb1a = arch_proc_mb1_addr(core, pix);
+ uint64_t mb1s = arch_proc_mb1_size(core, pix);
- for (u64 gidx = 0; gidx < mb0s && gcol < COLS; ++gidx, ++gcol) {
+ for (uint64_t gidx = 0; gidx < mb0s && gcol < COLS; ++gidx, ++gcol) {
if (gcol >= scol) {
ui_print_process_gene(l, gcol, gidx, mb0a, pix, PAIR_SELECTED_MB1);
}
}
- for (u64 gidx = 0; gidx < mb1s && gcol < COLS; ++gidx, ++gcol) {
+ for (uint64_t gidx = 0; gidx < mb1s && gcol < COLS; ++gidx, ++gcol) {
if (gcol >= scol) {
ui_print_process_gene(l, gcol, gidx, mb1a, pix, PAIR_SELECTED_MB2);
}
@@ -250,7 +495,7 @@ void ui_print_process_field_header_element(int l, int fidx, const char *name) {
}
int foff = fidx - g_proc_field_scroll;
- int fcol = foff * PROC_FIELD_WIDTH + PANE_WIDTH - 1;
+ int fcol = foff * {{ proc_field_width }} + {{ pane_width }} - 1;
ui_field(l, fcol, PAIR_NORMAL, A_NORMAL, " : %18s", name);
}
@@ -260,12 +505,12 @@ void ui_print_process_field_header(int l) {
int fidx = 0;
-#define PROC_FIELD(type, name) ui_print_process_field_header_element(l, fidx++, #name);
- PROC_FIELDS
-#undef PROC_FIELD
+ {% for _, val in arch_vars.proc_fields %}
+ ui_print_process_field_header_element(l, fidx++, "{{ val }}");
+ {% endfor %}
}
-void ui_print_process_field_element(int l, int fidx, int fclr, u64 field) {
+void ui_print_process_field_element(int l, int fidx, int fclr, uint64_t field) {
assert(fidx >= 0);
if (fidx < (int)g_proc_field_scroll) {
@@ -273,22 +518,22 @@ void ui_print_process_field_element(int l, int fidx, int fclr, u64 field) {
}
int foff = fidx - g_proc_field_scroll;
- int fcol = foff * PROC_FIELD_WIDTH + PANE_WIDTH - 1;
+ 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) {
+void ui_print_process_fields(int l, uint64_t pix) {
ui_line(true, l, ui_proc_pair(pix), A_NORMAL, "%s : %#18lx", ui_proc_state(pix), pix);
- const Proc *proc = proc_get(&g_cores[g_core], pix);
- int fidx = 0;
- int fclr = ui_proc_pair(pix);
+ const struct 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
+ {% for _, val in arch_vars.proc_fields %}
+ ui_print_process_field_element(l, fidx++, fclr, proc->{{ val }});
+ {% endfor %}
}
void ui_print_process(int l) {
@@ -304,7 +549,7 @@ void ui_print_process(int l) {
g_proc_gene_scroll
);
- u64 pix = g_proc_scroll;
+ uint64_t pix = g_proc_scroll;
if (g_proc_genes) {
ui_print_process_genome_header(l++);
@@ -329,8 +574,8 @@ void ui_world_resize() {
g_vlin_rng = 0;
g_vsiz_rng = 0;
- if (COLS > PANE_WIDTH) {
- g_vlin = COLS - PANE_WIDTH;
+ 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;
@@ -339,22 +584,30 @@ void ui_world_resize() {
}
}
-void ui_print_cell(u64 i, u64 r, u64 x, u64 y, u64 a) {
- wchar_t inst_nstr[2] = { L'\0', L'\0' };
- cchar_t cchar = { 0 };
- u64 inst_avrg = g_gfx_inst[i] / g_wrld_zoom;
+{% if arch_vars.mvec_loop %}
+void ui_print_cell(uint64_t i, uint64_t r, uint64_t x, uint64_t y) {
+{% else %}
+void ui_print_cell(uint64_t i, uint64_t r, uint64_t x, uint64_t y, uint64_t a) {
+{% endif %}
+ wchar_t inst_nstr[2] = { L'\0', L'\0' };
+ cchar_t cchar = { 0 };
+ uint64_t inst_avrg = g_gfx_inst[i] / g_wrld_zoom;
if (g_wrld_zoom == 1) {
- inst_nstr[0] = arch_symbol((u8)inst_avrg);
+ inst_nstr[0] = arch_symbol((uint8_t)inst_avrg);
} else {
- inst_nstr[0] = g_zoomed_symbols[(u8)inst_avrg];
+ inst_nstr[0] = g_zoomed_symbols[(uint8_t)inst_avrg];
}
int pair_cell;
- if (a >= MVEC_SIZE) {
+ {% if arch_vars.mvec_loop %}
+ if (g_wcursor_mode && r == (uint64_t)g_wcursor_x && y == (uint64_t)g_wcursor_y) {
+ {% else %}
+ if (a >= {{ mvec_size }} || (g_wcursor_mode && r == (uint64_t)g_wcursor_x && y == (uint64_t)g_wcursor_y)) {
+ {% endif %}
pair_cell = PAIR_NORMAL;
- } else if (g_wcursor_mode && r == (u64)g_wcursor_x && y == (u64)g_wcursor_y) {
+ } 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;
@@ -379,40 +632,26 @@ void ui_print_cell(u64 i, u64 r, u64 x, u64 y, u64 a) {
void ui_print_wcursor_bar() {
ui_clear_line(LINES - 1);
- const Core *core = &g_cores[g_core];
+ const struct Core *core = &g_cores[g_core];
- char cmnem[MNEMONIC_BUFF_SIZE];
- char cownr[PROC_FIELD_WIDTH];
+ 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);
+ uint64_t cpos = g_vlin * g_wcursor_y + g_wcursor_x;
+ uint64_t caddr = cpos * g_wrld_zoom + g_wrld_pos;
+ uint8_t cbyte = mvec_get_byte(core, caddr);
if (mvec_is_alloc(core, caddr)) {
g_wcursor_pointed = mvec_get_owner(core, caddr);
- snprintf(cownr, PROC_FIELD_WIDTH, "%#lx", g_wcursor_pointed);
+ snprintf(cownr, {{ proc_field_width }}, "%#lx", g_wcursor_pointed);
} else {
- g_wcursor_pointed = (u64)(-1);
- snprintf(cownr, PROC_FIELD_WIDTH, "-");
+ g_wcursor_pointed = (uint64_t)(-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"
- ),
+ "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,
@@ -420,7 +659,7 @@ void ui_print_wcursor_bar() {
g_gfx_inst[cpos] / g_wrld_zoom,
g_gfx_mall[cpos],
g_gfx_mbst[cpos],
- cmnem,
+ arch_mnemonic(cbyte),
cownr
);
}
@@ -440,11 +679,11 @@ void ui_print_world(int l) {
ui_line(false, l++, PAIR_HEADER, A_BOLD, "SELECTED");
- const Proc *psel = proc_get(&g_cores[g_core], g_proc_selected);
+ const struct 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
+ {% for _, val in arch_vars.proc_fields %}
+ ui_ulx_field(l++, "{{ val }}", psel->{{ val }});
+ {% endfor %}
if (!g_vlin) {
return;
@@ -460,13 +699,18 @@ void ui_print_world(int l) {
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;
- u64 a = g_wrld_pos + (i * g_wrld_zoom);
+ for (uint64_t i = 0; i < g_vsiz; ++i) {
+ uint64_t r = i % g_vlin;
+ uint64_t x = r + {{ pane_width }};
+ uint64_t y = i / g_vlin;
+
+ {% if arch_vars.mvec_loop %}
+ ui_print_cell(i, r, x, y);
+ {% else %}
+ uint64_t a = g_wrld_pos + (i * g_wrld_zoom);
ui_print_cell(i, r, x, y, a);
+ {% endif %}
}
if (g_wcursor_mode) {
@@ -474,19 +718,19 @@ void ui_print_world(int l) {
}
}
-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];
+void ui_print_ipc_field(int l, uint64_t i, int color) {
+ uint8_t iinst = g_cores[g_core].iviv[i];
+ uint64_t iaddr = g_cores[g_core].ivav[i];
- ui_field(l, PANE_WIDTH, color, A_NORMAL, "%#18x : %#18x : %#18x", i, iinst, iaddr);
+ 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");
+ 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) {
+ for (uint64_t i = 0; i < {{ sync_interval }}; ++i) {
if (i == g_cores[g_core].ivpt) {
if (l >= 1) {
ui_print_ipc_field(l++, i, PAIR_SELECTED_PROC);
@@ -495,9 +739,9 @@ void ui_print_ipc_data() {
continue;
}
- u8 iinst = g_cores[g_core].iviv[i];
+ uint8_t iinst = g_cores[g_core].iviv[i];
- if ((iinst & IPCM_FLAG) != 0) {
+ if ((iinst & {{ ipc_flag }}) != 0) {
if (l >= 1) {
ui_print_ipc_field(l++, i, PAIR_LIVE_PROC);
}
@@ -508,7 +752,7 @@ void ui_print_ipc_data() {
for (; l < LINES; ++l) {
if (l >= 1) {
- move(l, PANE_WIDTH);
+ move(l, {{ pane_width }});
clrtoeol();
}
}
@@ -517,7 +761,7 @@ void ui_print_ipc_data() {
void ui_print_ipc(int l) {
l++;
- const Core *core = &g_cores[g_core];
+ const struct Core *core = &g_cores[g_core];
ui_line(true, l++, PAIR_HEADER, A_BOLD, "IPC [%#lx]", g_ivpt_scroll);
ui_ulx_field(l++, "ivpt", core->ivpt);
@@ -530,14 +774,14 @@ void ui_print_ipc(int l) {
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_line(false, l++, PAIR_HEADER, A_BOLD, "SALIS [%d:%d]", g_core, {{ args.cores }});
+ ui_str_field(l++, "name", "{{ args.name }}");
+ ui_ulx_field(l++, "seed", {{ args.seed }});
+ ui_str_field(l++, "fbit", "{{ "yes" if args.muta_flip else "no" }}");
+ ui_ulx_field(l++, "asav", {{ auto_save_interval }});
+ ui_str_field(l++, "arch", "{{ args.arch }}");
+ ui_ulx_field(l++, "size", {{ mvec_size }});
+ ui_ulx_field(l++, "syni", {{ sync_interval }});
ui_ulx_field(l++, "step", g_steps);
ui_ulx_field(l++, "sync", g_syncs);
ui_ulx_field(l++, "step", g_step_block);
@@ -565,10 +809,10 @@ void ev_vscroll(int ev) {
case PAGE_PROCESS:
switch (ev) {
case 'W':
- g_proc_scroll += (LINES > PROC_PAGE_LINES) ? LINES - PROC_PAGE_LINES : 0;
+ 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;
+ g_proc_scroll -= (LINES > {{ proc_page_lines }}) ? LINES - {{ proc_page_lines }} : 0;
break;
case 'w':
g_proc_scroll += 1;
@@ -596,15 +840,15 @@ void ev_vscroll(int ev) {
g_wrld_pos += g_vlin_rng;
break;
case 's':
-#ifdef MVEC_LOOP
+ {% if arch_vars.mvec_loop %}
g_wrld_pos -= g_vlin_rng;
-#else
+ {% else %}
if (g_wrld_pos < g_vlin_rng) {
g_wrld_pos = 0;
} else {
g_wrld_pos -= g_vlin_rng;
}
-#endif
+ {% endif %}
break;
case 'q':
g_wrld_pos = 0;
@@ -621,7 +865,7 @@ void ev_vscroll(int ev) {
g_ivpt_scroll += LINES;
break;
case 'S':
- g_ivpt_scroll -= g_ivpt_scroll < (u64)LINES ? g_ivpt_scroll : (u64)LINES;
+ g_ivpt_scroll -= g_ivpt_scroll < (uint64_t)LINES ? g_ivpt_scroll : (uint64_t)LINES;
break;
case 'w':
g_ivpt_scroll += 1;
@@ -643,7 +887,7 @@ void ev_vscroll(int ev) {
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;
+ uint64_t *hs_var = g_proc_genes ? &g_proc_gene_scroll : &g_proc_field_scroll;
switch (ev) {
case 'A':
@@ -665,15 +909,15 @@ void ev_hscroll(int ev) {
case PAGE_WORLD:
switch (ev) {
case 'a':
-#ifdef MVEC_LOOP
+ {% if arch_vars.mvec_loop %}
g_wrld_pos -= g_wrld_zoom;
-#else
+ {% else %}
if (g_wrld_pos < g_wrld_zoom) {
g_wrld_pos = 0;
} else {
g_wrld_pos -= g_wrld_zoom;
}
-#endif
+ {% endif %}
break;
case 'd':
g_wrld_pos += g_wrld_zoom;
@@ -693,7 +937,7 @@ void ev_zoom(int ev) {
case PAGE_WORLD:
switch (ev) {
case 'x':
- g_wrld_zoom *= (g_vlin != 0 && g_vsiz_rng < MVEC_SIZE) ? 2 : 1;
+ g_wrld_zoom *= (g_vlin != 0 && g_vsiz_rng < {{ mvec_size }}) ? 2 : 1;
ui_world_resize();
break;
case 'z':
@@ -722,7 +966,7 @@ void ev_move_wcursor(int ev) {
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;
+ g_wcursor_x += ((uint64_t)g_wcursor_x < g_vlin - 1) ? 1 : 0;
break;
default:
break;
@@ -777,7 +1021,7 @@ void ev_handle() {
ev_move_wcursor(ev);
return;
case '\n':
- if (g_wcursor_pointed != (u64)(-1)) {
+ if (g_wcursor_pointed != (uint64_t)(-1)) {
g_proc_selected = g_wcursor_pointed;
}
@@ -788,16 +1032,16 @@ void ev_handle() {
}
switch (ev) {
- case CTRL('c'):
+ case {{ ctrl('c') }}:
g_exit = true;
break;
case KEY_SLEFT:
clear();
- g_core = (g_core - 1) % CORE_COUNT;
+ g_core = (g_core - 1) % {{ args.cores }};
break;
case KEY_SRIGHT:
clear();
- g_core = (g_core + 1) % CORE_COUNT;
+ g_core = (g_core + 1) % {{ args.cores }};
break;
case KEY_LEFT:
clear();
@@ -813,7 +1057,7 @@ void ev_handle() {
ui_world_resize();
if (g_vlin) {
- while (g_vsiz_rng >= MVEC_SIZE * 2 && g_wrld_zoom != 1) {
+ while (g_vsiz_rng >= {{ mvec_size }} * 2 && g_wrld_zoom != 1) {
g_wrld_zoom /= 2;
ui_world_resize();
}
@@ -880,7 +1124,7 @@ void ev_handle() {
case '9':
case '0':
if (!g_running) {
- u64 cycles = 1 << (((ev - '0') ? (ev - '0') : 10) - 1);
+ uint64_t cycles = 1 << (((ev - '0') ? (ev - '0') : 10) - 1);
salis_step(cycles);
}
@@ -914,11 +1158,11 @@ void init() {
init_pair(PAIR_SELECTED_IP, COLOR_BLACK, COLOR_RED );
init_pair(PAIR_SELECTED_SP, COLOR_BLACK, COLOR_MAGENTA);
-#if ACTION == ACT_NEW
+ {% if args.command == "new" %}
salis_init();
-#elif ACTION == ACT_LOAD
+ {% elif args.command == "load" %}
salis_load();
-#endif
+ {% endif %}
g_wrld_zoom = 1;
g_step_block = 1;
@@ -934,11 +1178,11 @@ void exec() {
salis_step(g_step_block - (g_steps % g_step_block));
clock_t end = clock();
- if ((end - beg) < (CLOCKS_PER_SEC / 30)) {
+ if ((end - beg) < (CLOCKS_PER_SEC / {{ min_fps }})) {
g_step_block <<= 1;
}
- if ((end - beg) >= (CLOCKS_PER_SEC / 60) && g_step_block != 1) {
+ if ((end - beg) >= (CLOCKS_PER_SEC / {{ max_fps }}) && g_step_block != 1) {
g_step_block >>= 1;
}
}
@@ -951,7 +1195,7 @@ void exec() {
void quit() {
gfx_free();
ui_line_buff_free();
- salis_save(SIM_PATH);
+ salis_save("{{ sim_path }}");
salis_free();
endwin();
}
diff --git a/ui/curses/ui_vars.py b/ui/curses/ui_vars.py
new file mode 100644
index 0000000..4dfdc10
--- /dev/null
+++ b/ui/curses/ui_vars.py
@@ -0,0 +1,2 @@
+flags = ["-lncurses", "-DNCURSES_WIDECHAR=1"]
+includes = ["curses.h", "locale.h", "time.h"]
diff --git a/src/ui/daemon.c b/ui/daemon/ui.j2.c
index f74713d..7d92f9d 100644
--- a/src/ui/daemon.c
+++ b/ui/daemon/ui.j2.c
@@ -1,23 +1,18 @@
+// Author: Paul Oliver <contact@pauloliver.dev>
// 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 <signal.h>
-#include <unistd.h>
+// Lightweight UI for the Salis simulator with minimal output.
+// Can be interrupted through OS signals.
+// Ideal for running Salis in the background.
volatile bool g_running;
-u64 g_step_block;
+uint64_t g_step_block;
void sig_handler(int signo) {
switch (signo) {
case SIGINT:
case SIGTERM:
- printf("signal received, stopping simulator...\n");
+ printf("Signal received, will stop simulator soon...\n");
g_running = false;
break;
}
@@ -36,15 +31,15 @@ void step_block() {
g_step_block >>= 1;
}
- printf("simulator running on step '%#lx'\n", g_steps);
+ printf("Simulator running on step '%#lx'\n", g_steps);
}
int main() {
-#if ACTION == ACT_NEW
+ {% if args.command == "new" %}
salis_init();
-#elif ACTION == ACT_LOAD
+ {% elif args.command == "load" %}
salis_load();
-#endif
+ {% endif %}
g_running = true;
g_step_block = 1;
@@ -56,8 +51,10 @@ int main() {
step_block();
}
- salis_save(SIM_PATH);
+ printf("Saving simulation...\n");
+ salis_save("{{ sim_path }}");
salis_free();
+ printf("Exiting salis...\n");
return 0;
}
diff --git a/ui/daemon/ui_vars.py b/ui/daemon/ui_vars.py
new file mode 100644
index 0000000..9d0fc33
--- /dev/null
+++ b/ui/daemon/ui_vars.py
@@ -0,0 +1,2 @@
+flags = []
+includes = ["signal.h", "stdio.h", "unistd.h"]