aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md60
-rwxr-xr-xsalis79
-rw-r--r--sim.pngbin0 -> 311643 bytes
-rw-r--r--src/arch/dummy.c7
-rw-r--r--src/graphics.c21
-rw-r--r--src/salis.c147
-rw-r--r--src/ui/curses.c27
-rw-r--r--todo.adoc5
8 files changed, 289 insertions, 57 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ba9b2d7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,60 @@
+# SALIS: A-life Simulator
+
+![SALIS simulation](sim.png)
+*SALIS simulation running on the V1 architecture with the ncurses user interface*
+
+## Overview
+*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:
+
+- [Video about Tierra](https://www.youtube.com/watch?v=Wl5rRGVD0QI)
+- [Read about Tierra](https://tomray.me/pubs/doc/index.html#What)
+
+## SALIS V1 Reimplementation
+A fully functional clone of the V1 architecture for the SALIS virtual machine
+has been implemented using the tools available in this repository. For more
+information on the V1 architecture, including its similarities and differences
+with the original Tierra simulator, check out the following resources:
+
+- [SALIS V1 repository](https://git.pauloliver.dev/salis-v1/about/)
+- [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.
+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
+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.
+
+Similarly, different user interfaces are implemented as C files within the
+`src/ui/` directory. For example, the `curses.c` 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
+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
+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`:
+```console
+user@host$ ./salis new -A55a -asalis-v1 -c8 -C4 -m22 -nworld-1 -s123456789 -o
+```
+
+Upon exit, the simulation data 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
+```
diff --git a/salis b/salis
index a493861..ad5aded 100755
--- a/salis
+++ b/salis
@@ -11,7 +11,7 @@
set -euo pipefail
headline="Salis: Simple A-Life Simulator."
-help_msg="show help and exit"
+help_msg="Shows help and exits"
usage() {
cat << EOF
@@ -22,9 +22,9 @@ Options:
-h, --help ${help_msg}
Commands:
- bench run benchmark test
- load load saved simulation
- new create a new simulation
+ bench Runs benchmark
+ load Loads saved simulation
+ new Creates a new simulation
Use '-h' to list arguments for each command.
Example: ${0} bench -h
@@ -35,7 +35,7 @@ case ${1:-} in
bench|load|new)
;;
-h|--help)
- usage | less -CQS~
+ usage
exit 0
;;
"")
@@ -60,38 +60,39 @@ arches=`falter arch`
uis=`falter ui`
anc_def_desc() {
- echo "default ancestor file name without extension, "
+ 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"
+ 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||overwrite existing simulation of given name||false|new"
- "H|half||compile ancestor at the middle of the memory buffer||false|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||build Salis binary with optimizations||false|bench:load:new"
- "p|pre-cmd|CMD|shell command to wrap executable (e.g. gdb)|||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)||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"
- "z|auto-save-pow|POW|auto save interval exponent (interval == 2^POW)||36|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() {
@@ -111,7 +112,7 @@ fhelp() {
lopt=`field "${1}" 2`
meta=`field "${1}" 3`
- printf %-32s " -${sopt}, --${lopt}`[[ -n ${meta} ]] && echo " ${meta}"`"
+ printf "%s\r" " -${sopt}, --${lopt}`[[ -n ${meta} ]] && echo " ${meta}"`"
help=`field "${1}" 4`
choi=`field "${1}" 5`
@@ -119,7 +120,7 @@ fhelp() {
copt=`[[ -n ${choi} ]] && echo " (choices: ${choi/:/, })"`
dopt=`[[ -n ${defv} ]] && echo " (default: ${defv})"`
- echo ${help}${copt}${dopt}
+ echo -e "\t\t\t\t${help}${copt}${dopt}" | fmt -w120
}
fshort() {
@@ -168,7 +169,7 @@ fiter() {
usage() {
cat << EOF
${headline}
-Usage: ${0} ${cmd} `fiter flist`
+Usage: ${0} ${cmd} `fiter flist | fmt -t -w120`
Options:
`fiter fhelp`
@@ -196,7 +197,8 @@ parse_next() {
nopt=opt_${lopt//-/_}
if [[ -z ${meta} ]] ; then
- eval ${nopt}=true
+ defv=`field "${vopt}" 6`
+ eval ${nopt}=`[[ ${defv} == true ]] && echo false || echo true`
shift_next=1
else
eval ${nopt}=${2}
@@ -208,7 +210,7 @@ parse_next() {
while true ; do
case ${1} in
-h|--help)
- usage | less -CQS~
+ usage
exit 0
;;
--)
@@ -275,7 +277,7 @@ act_new=3
act_var="act_${cmd}"
-gcc_flags="-Wall -Wextra -Werror -std=c11 -pedantic"
+gcc_flags="-Wall -Wextra -Werror -std=gnu11 -pedantic"
fquote() {
echo "\\\"${1}\\\""
@@ -285,7 +287,7 @@ fpow() {
printf '%#xul' $((1 << ${1}))
}
-bcmd="gcc src/salis.c -o ${salis_exe} ${gcc_flags} -Isrc -lncursesw -pthread"
+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}`"
@@ -337,7 +339,18 @@ load|new)
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}`"
- bcmd="${bcmd} -DUI=`fquote ui/${opt_ui}.c`"
+
+ 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
diff --git a/sim.png b/sim.png
new file mode 100644
index 0000000..23eb84c
--- /dev/null
+++ b/sim.png
Binary files differ
diff --git a/src/arch/dummy.c b/src/arch/dummy.c
index b440be3..7a47900 100644
--- a/src/arch/dummy.c
+++ b/src/arch/dummy.c
@@ -11,7 +11,12 @@
bool proc_is_live(const Core *core, u64 pix);
#define PROC_FIELDS \
- PROC_FIELD(u64, dmmy)
+ 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;
diff --git a/src/graphics.c b/src/graphics.c
index 8114f30..780b879 100644
--- a/src/graphics.c
+++ b/src/graphics.c
@@ -89,10 +89,9 @@ void gfx_render_inst(const Core *core, u64 pos, u64 zoom) {
for (u64 j = 0; j < zoom; ++j) {
u64 addr = pos + (i * zoom) + j;
- u8 byte = mvec_get_byte(core, addr);
- g_gfx_inst[i] += byte;
- g_gfx_mall[i] += (byte & MALL_FLAG) ? 1 : 0;
+ g_gfx_inst[i] += mvec_get_byte(core, addr);
+ g_gfx_mall[i] += mvec_is_alloc(core, addr) ? 1 : 0;
}
}
}
@@ -102,6 +101,7 @@ void gfx_clear_array(u64 *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);
@@ -134,6 +134,21 @@ void gfx_accumulate_pixel(u64 pos, u64 zoom, u64 pixa, u64 *arry) {
}
#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);
diff --git a/src/salis.c b/src/salis.c
index 814dd0d..348385f 100644
--- a/src/salis.c
+++ b/src/salis.c
@@ -16,6 +16,10 @@
#include <string.h>
#include <threads.h>
+#ifdef COMPRESS
+#include <zlib.h>
+#endif
+
#define ACT_BENCH (1)
#define ACT_LOAD (2)
#define ACT_NEW (3)
@@ -73,51 +77,93 @@ const Proc g_dead_proc;
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[mvec_loop(addr)] ^= (1 << bit) & INST_MASK;
+ core->mvec[addr] ^= (1 << bit) & INST_MASK;
}
#endif
@@ -378,8 +424,6 @@ void core_load(FILE *f, Core *core) {
assert(f);
assert(core);
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-result"
fread(&core->mall, sizeof(u64), 1, f);
fread( core->muta, sizeof(u64), 4, f);
fread(&core->pnum, sizeof(u64), 1, f);
@@ -390,7 +434,6 @@ void core_load(FILE *f, Core *core) {
fread(&core->psli, sizeof(u64), 1, f);
fread(&core->ncyc, sizeof(u64), 1, f);
fread(&core->ivpt, sizeof(u64), 1, f);
-#pragma GCC diagnostic pop
core->iviv = calloc(SYNC_INTERVAL, sizeof(u8));
core->ivav = calloc(SYNC_INTERVAL, sizeof(u64));
@@ -400,13 +443,10 @@ void core_load(FILE *f, Core *core) {
assert(core->ivav);
assert(core->pvec);
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-result"
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);
-#pragma GCC diagnostic pop
}
#endif
@@ -476,7 +516,13 @@ void core_step(Core *core) {
#if ACTION == ACT_LOAD || ACTION == ACT_NEW
void salis_save(const char *path) {
- FILE *f = fopen(path, "wb");
+#ifdef COMPRESS
+ size_t size = 0;
+ char *in = NULL;
+ FILE *f = open_memstream(&in, &size);
+#else
+ FILE *f = fopen(path, "wb");
+#endif
assert(f);
@@ -487,6 +533,39 @@ void salis_save(const char *path) {
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() {
@@ -536,7 +615,50 @@ void salis_init() {
#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);
@@ -544,13 +666,14 @@ void salis_load() {
core_load(f, &g_cores[i]);
}
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-result"
fread(&g_steps, sizeof(u64), 1, f);
fread(&g_syncs, sizeof(u64), 1, f);
-#pragma GCC diagnostic pop
-
fclose(f);
+
+#ifdef COMPRESS
+ free(in);
+ free(out);
+#endif
}
#endif
diff --git a/src/ui/curses.c b/src/ui/curses.c
index 8f91d9f..d1f821d 100644
--- a/src/ui/curses.c
+++ b/src/ui/curses.c
@@ -6,6 +6,8 @@
* Implements a TUI for the Salis simulator using the ncurses library.
*/
+// GCC_EXTRA_FLAGS -lncursesw
+
#include <curses.h>
#include <locale.h>
#include <time.h>
@@ -337,7 +339,7 @@ void ui_world_resize() {
}
}
-void ui_print_cell(u64 i, u64 r, u64 x, u64 y) {
+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;
@@ -350,7 +352,9 @@ void ui_print_cell(u64 i, u64 r, u64 x, u64 y) {
int pair_cell;
- if (g_wcursor_mode && r == (u64)g_wcursor_x && y == (u64)g_wcursor_y) {
+ if (a >= MVEC_SIZE) {
+ pair_cell = PAIR_NORMAL;
+ } else if (g_wcursor_mode && r == (u64)g_wcursor_x && y == (u64)g_wcursor_y) {
pair_cell = PAIR_NORMAL;
} else if (g_gfx_ipas[i] != 0) {
pair_cell = PAIR_SELECTED_IP;
@@ -460,8 +464,9 @@ void ui_print_world(int l) {
u64 r = i % g_vlin;
u64 x = r + PANE_WIDTH;
u64 y = i / g_vlin;
+ u64 a = g_wrld_pos + (i * g_wrld_zoom);
- ui_print_cell(i, r, x, y);
+ ui_print_cell(i, r, x, y, a);
}
if (g_wcursor_mode) {
@@ -591,7 +596,15 @@ void ev_vscroll(int ev) {
g_wrld_pos += g_vlin_rng;
break;
case 's':
+#ifdef MVEC_LOOP
g_wrld_pos -= g_vlin_rng;
+#else
+ if (g_wrld_pos < g_vlin_rng) {
+ g_wrld_pos = 0;
+ } else {
+ g_wrld_pos -= g_vlin_rng;
+ }
+#endif
break;
case 'q':
g_wrld_pos = 0;
@@ -652,7 +665,15 @@ void ev_hscroll(int ev) {
case PAGE_WORLD:
switch (ev) {
case 'a':
+#ifdef MVEC_LOOP
g_wrld_pos -= g_wrld_zoom;
+#else
+ if (g_wrld_pos < g_wrld_zoom) {
+ g_wrld_pos = 0;
+ } else {
+ g_wrld_pos -= g_wrld_zoom;
+ }
+#endif
break;
case 'd':
g_wrld_pos += g_wrld_zoom;
diff --git a/todo.adoc b/todo.adoc
deleted file mode 100644
index 0011c6a..0000000
--- a/todo.adoc
+++ /dev/null
@@ -1,5 +0,0 @@
-= TODO
-Paul Oliver <contact@pauloliver.dev>
-
-== Architectures
-. salis-v3