aboutsummaryrefslogtreecommitdiff
path: root/tsalis
diff options
context:
space:
mode:
authorPaul Oliver <contact@pauloliver.dev>2024-02-29 01:50:44 +0100
committerPaul Oliver <contact@pauloliver.dev>2024-02-29 01:50:44 +0100
commit2dc9d118efb64de6ea54a5a9eb4474f8e5ef3145 (patch)
tree74039957b10390da4875d676303a781bd0792e45 /tsalis
Initial commit
Diffstat (limited to 'tsalis')
-rw-r--r--tsalis/Makefile30
-rw-r--r--tsalis/README.md76
-rw-r--r--tsalis/bin/.keep0
-rw-r--r--tsalis/build/.keep0
-rw-r--r--tsalis/include/handler.h6
-rw-r--r--tsalis/include/printer.h35
-rw-r--r--tsalis/include/tsalis.h11
-rw-r--r--tsalis/src/handler.c317
-rw-r--r--tsalis/src/printer.c749
-rw-r--r--tsalis/src/tsalis.c118
10 files changed, 1342 insertions, 0 deletions
diff --git a/tsalis/Makefile b/tsalis/Makefile
new file mode 100644
index 0000000..c393350
--- /dev/null
+++ b/tsalis/Makefile
@@ -0,0 +1,30 @@
+CC := gcc
+BIN := bin/tsalis
+
+SOURCES := $(wildcard src/*.c)
+OBJECTS := $(patsubst src/%.c,build/%.o,$(SOURCES))
+DEPS := $(patsubst %.o,%.d,$(OBJECTS))
+
+LFLAGS := -L ../lib -lsalis -lncurses
+
+# uncomment for debug
+# OFLAGS := -ggdb
+
+# uncomment for release
+OFLAGS := -O3 -DNDEBUG
+
+CFLAGS := -Iinclude -I../include -c $(OFLAGS) -MMD -Wall -Wextra -std=c89 \
+ -pedantic-errors -Wmissing-prototypes -Wstrict-prototypes \
+ -Wold-style-definition
+
+all: $(OBJECTS)
+ $(CC) $(OBJECTS) $(LFLAGS) -o $(BIN)
+
+-include $(DEPS)
+
+$(OBJECTS): $(patsubst build/%.o,src/%.c,$@)
+ $(CC) $(CFLAGS) $(patsubst build/%.o,src/%.c,$@) -o $@
+
+clean:
+ -rm build/*
+ -rm $(BIN)
diff --git a/tsalis/README.md b/tsalis/README.md
new file mode 100644
index 0000000..214e3b9
--- /dev/null
+++ b/tsalis/README.md
@@ -0,0 +1,76 @@
+# TSALIS
+*TSALIS* is a text user interface (TUI) designed to communicate with *SALIS*.
+Its only dependencies are ncurses and the *SALIS* library itself. It should be
+portable enough and should run easily in any terminal environment.
+
+## Building instructions
+You'll need nothing but a C compiler (C89). You must build the program and link
+it with *SALIS* (e.g. *libsalis.a*) and ncurses. A sample makefile
+(Makefile) is provided for GNU Make. You can also just run the `make` command
+inside the salis directory and it will automate the building and linking of
+both the library and this application. Feel free to edit both makefiles as
+needed. If you run into any difficulties, please let me know!
+
+## List of commands
+### Command-line arguments
+You may run *TSALIS* from the terminal in any of these three ways (arguments
+are being represented by *XX*). Note that, upon exit, *SALIS* automatically
+generates a save (by default called *defsim*). This save file may be freely
+renamed (any name 10 characters or shorter) and reloaded as needed.
+
+|Arguments |Action |
+|:--------------|------------------------------------------------------------------------------:|
+|tsalis |If file *defsim* exists in directory, loads simulation from that file. |
+|tsalis |If file *defsim* does not exist, creates new simulation (memory size 2^16). |
+|tsalis n*XX* |Creates new simulation with memory size 2^*XX*. |
+|tsalis l*XX* |Loads simulation from file named *XX*. |
+
+### Keyboard commands
+|Key |Action |
+|:--------------|------------------------------------------------------:|
+|Left arrow |Previous page |
+|Right arrow |Next page |
+|wasd |Scroll (PROCESS and WORLD page) |
+|W |Scroll to top (PROCESS and WORLD page) |
+|A |Scroll to left (PROCESS page) |
+|zx |Zoom in/out (WORLD page) |
+|op |Select previous/next organism |
+|g |Toggle data/gene view (PROCESS page) |
+|c |Open console (pauses simulation) |
+|Space |Run/pause simulation |
+|jl |Select first/last organism |
+|k |Go to selected organism (PROCESS and WORLD page) |
+|Numbers (1-0) |Cycle simulation (1 = 1, 2 = 2, 3 = 4, 4 = 8, ...) |
+
+### Console commands
+The console opens up when 'c' is pressed. Commands, with their respective
+parameters separated by underscores, may be written in order to modify or
+control some aspects of the simulation. Parameters here are represented by
+*XX*.
+
+|Command |Param. 1 |Param. 2 |Action |
+|:-----------|:-----------|:-----------|---------------------------------------------------------------------:|
+|q |--- |--- |Save and quit simulation. |
+|i*XX*\_*XX* |address |instructions|Writes given instructions into address. |
+|c*XX*\_*XX* |address |file name |Compiles given file into address. |
+|n*XX*\_*XX* |address |size |Initializes organism of given size into address. |
+|k |--- |--- |Kills organism at bottom of queue (first organism). |
+|m*XX* |address |--- |Scroll/move (PROCESS and WORLD page) to given process/address. |
+|p*XX* |process id |--- |Select given process. |
+|s |--- |--- |Save simulation. |
+|r*XX* |name |--- |Rename simulation (will be automatically saved to this name on exit). |
+|a*XX* |interval |--- |Set simulation's auto-save interval. |
+
+### Legend
+In WORLD view, as well as in PROCESS view (when gene mode is selected), each
+cell is colored according to the following legend:
+
+|Background color |Meaning |
+|:----------------|---------------------------------------:|
+|BLUE |Non-allocated cell |
+|CYAN |Allocated cell |
+|WHITE |Start of memory block |
+|YELLOW |Main memory block of selected organism |
+|GREEN |Child memory block of selected organism |
+|MAGENTA |SP of selected organism |
+|RED |IP of selected organism |
diff --git a/tsalis/bin/.keep b/tsalis/bin/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tsalis/bin/.keep
diff --git a/tsalis/build/.keep b/tsalis/build/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tsalis/build/.keep
diff --git a/tsalis/include/handler.h b/tsalis/include/handler.h
new file mode 100644
index 0000000..9bb4b0e
--- /dev/null
+++ b/tsalis/include/handler.h
@@ -0,0 +1,6 @@
+#ifndef TSALIS_HANDLER_H
+#define TSALIS_HANDLER_H
+
+void tsh_handleEvent (int event);
+
+#endif
diff --git a/tsalis/include/printer.h b/tsalis/include/printer.h
new file mode 100644
index 0000000..5356594
--- /dev/null
+++ b/tsalis/include/printer.h
@@ -0,0 +1,35 @@
+#ifndef TSALIS_PRINTER_H
+#define TSALIS_PRINTER_H
+
+extern const int PROC_ELEMENT_COUNT;
+
+extern int g_currentPage;
+extern sword g_selectedProcess;
+extern sbool g_processShowGenes;
+extern sword g_processVertScroll;
+extern sword g_processDataScroll;
+extern sword g_processGeneScroll;
+extern sword g_worldPos;
+extern sword g_worldZoom;
+
+void tsp_init (void);
+void tsp_quit (void);
+void tsp_onResize (void);
+void tsp_prevPage (void);
+void tsp_nextPage (void);
+void tsp_scrollUp (void);
+void tsp_scrollDown (void);
+void tsp_scrollLeft (void);
+void tsp_scrollRight (void);
+void tsp_scrollToTop (void);
+void tsp_scrollToLeft (void);
+void tsp_zoomIn (void);
+void tsp_zoomOut (void);
+void tsp_prevOrganism (void);
+void tsp_nextOrganism (void);
+void tsp_gotoSelectedProc (void);
+void tsp_selectProcess (sword proc);
+void tsp_moveTo (sword loc);
+void tsp_printData (void);
+
+#endif
diff --git a/tsalis/include/tsalis.h b/tsalis/include/tsalis.h
new file mode 100644
index 0000000..d60b7c7
--- /dev/null
+++ b/tsalis/include/tsalis.h
@@ -0,0 +1,11 @@
+#ifndef TSALIS_H
+#define TSALIS_H
+
+#define NAME_MAX_SIZE 10
+
+extern sbool g_exit;
+extern sbool g_running;
+extern sword g_autoSaveInterval;
+extern char g_simName[];
+
+#endif
diff --git a/tsalis/src/handler.c b/tsalis/src/handler.c
new file mode 100644
index 0000000..21aa706
--- /dev/null
+++ b/tsalis/src/handler.c
@@ -0,0 +1,317 @@
+#include <stdlib.h>
+#include <string.h>
+#include <curses.h>
+#include <salis.h>
+#include "printer.h"
+#include "handler.h"
+#include "tsalis.h"
+
+#define CONSOLE_INPUT_LENGTH 64
+
+static sbyte
+symbolToInst(char charSymbol)
+{
+#define SINST(name, symbol) case symbol: return name;
+#define SILST(name, symbol) case symbol: return name;
+
+ switch (charSymbol) {
+ SINST_LIST
+ }
+
+#undef SINST
+#undef SILST
+ return (sbyte)(-1);
+}
+
+static void
+writeInstructions(const char *command)
+{
+ sword addr = atoi(command);
+ char *symbol = strchr(command, '_');
+
+ if (symbol) {
+ symbol++;
+
+ while (sm_isValidAt(addr) && *symbol) {
+ sbyte svalue = symbolToInst(*symbol);
+
+ if (si_isInst(svalue)) {
+ sm_setInstAt(addr, svalue);
+ } else {
+ sm_setInstAt(addr, 0);
+ }
+
+ addr++;
+ symbol++;
+ }
+ }
+}
+
+static void
+compileGenome(const char *command)
+{
+ sword addr = atoi(command);
+ char *fileName = strchr(command, '_');
+
+ if (fileName) {
+ FILE *file = fopen(fileName + 1, "r");
+
+ if (file) {
+ while (addr < sm_getSize()) {
+ int symbol = fgetc(file);
+ sbyte svalue = symbolToInst((char)symbol);
+
+ if (symbol == EOF) {
+ break;
+ }
+
+ if (si_isInst(svalue)) {
+ sm_setInstAt(addr, svalue);
+ } else {
+ sm_setInstAt(addr, 0);
+ }
+
+ addr++;
+ }
+ }
+ }
+}
+
+static void
+createOrganism(const char *command)
+{
+ sword addr = atoi(command);
+ char *sizep = strchr(command, '_');
+
+ if (sizep) {
+ sword size = atoi(sizep + 1);
+
+ if (sm_isValidAt(addr) && sm_isValidAt(addr + size - 1)) {
+ sword offset;
+
+ for (offset = 0; offset < size; offset++) {
+ if (sm_isAllocatedAt(addr + offset)) {
+ return;
+ }
+ }
+
+ sp_create(addr, size);
+ }
+ }
+}
+
+static void
+killOrganism(void)
+{
+ sp_kill();
+}
+
+static void
+moveTo(const char *command)
+{
+ sword loc = atoi(command);
+ tsp_moveTo(loc);
+}
+
+static void
+selectProc(const char *command)
+{
+ sword proc = atoi(command);
+ tsp_selectProcess(proc);
+}
+
+static void
+saveSim(void)
+{
+ s_save(g_simName);
+}
+
+static void
+renameSim(const char *name)
+{
+ if (strlen(name) <= NAME_MAX_SIZE) {
+ strncpy(g_simName, name, NAME_MAX_SIZE);
+ }
+}
+
+static void
+setAutoSave(const char *command)
+{
+ g_autoSaveInterval = atoi(command);
+}
+
+static void
+clearConsoleLine(void)
+{
+ move(LINES - 1, 0);
+ clrtoeol();
+}
+
+static void
+runConsole(void)
+{
+ char command[CONSOLE_INPUT_LENGTH] = {0};
+ clearConsoleLine();
+ echo();
+ mvprintw(LINES - 1, 1, "$ ");
+ curs_set(TRUE);
+ getnstr(command, CONSOLE_INPUT_LENGTH - 1);
+ curs_set(FALSE);
+ noecho();
+ clearConsoleLine();
+
+ switch (command[0]) {
+ case 'q':
+ g_exit = STRUE;
+ break;
+
+ case 'i':
+ writeInstructions(&command[1]);
+ break;
+
+ case 'c':
+ compileGenome(&command[1]);
+ break;
+
+ case 'n':
+ createOrganism(&command[1]);
+ break;
+
+ case 'k':
+ killOrganism();
+ break;
+
+ case 'm':
+ moveTo(&command[1]);
+ break;
+
+ case 'p':
+ selectProc(&command[1]);
+ break;
+
+ case 's':
+ saveSim();
+ break;
+
+ case 'r':
+ renameSim(&command[1]);
+ break;
+
+ case 'a':
+ setAutoSave(&command[1]);
+ break;
+ }
+}
+
+void
+tsh_handleEvent(int event)
+{
+ switch (event) {
+ case KEY_RESIZE:
+ tsp_onResize();
+ break;
+
+ case KEY_LEFT:
+ tsp_prevPage();
+ break;
+
+ case KEY_RIGHT:
+ tsp_nextPage();
+ break;
+
+ case 'w':
+ tsp_scrollDown();
+ break;
+
+ case 'a':
+ tsp_scrollLeft();
+ break;
+
+ case 's':
+ tsp_scrollUp();
+ break;
+
+ case 'd':
+ tsp_scrollRight();
+ break;
+
+ case 'W':
+ tsp_scrollToTop();
+ break;
+
+ case 'A':
+ tsp_scrollToLeft();
+ break;
+
+ case 'z':
+ tsp_zoomIn();
+ break;
+
+ case 'x':
+ tsp_zoomOut();
+ break;
+
+ case 'o':
+ tsp_prevOrganism();
+ break;
+
+ case 'p':
+ tsp_nextOrganism();
+ break;
+
+ case 'g':
+ g_processShowGenes = !g_processShowGenes;
+ break;
+
+ case 'c':
+ g_running = SFALSE;
+ nodelay(stdscr, SFALSE);
+ tsp_printData();
+ runConsole();
+ break;
+
+ case ' ':
+ g_running = !g_running;
+ nodelay(stdscr, g_running);
+ break;
+
+ case 'j':
+ if (sp_getCount()) {
+ g_selectedProcess = sp_getFirst();
+ }
+
+ break;
+
+ case 'l':
+ if (sp_getCount()) {
+ g_selectedProcess = sp_getLast();
+ }
+
+ break;
+
+ case 'k':
+ tsp_gotoSelectedProc();
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '0':
+ if (!g_running) {
+ int power = ((event - '0') ? (event - '0') : 10) - 1;
+ int cycles = 1 << power;
+
+ while (cycles--) {
+ s_cycle();
+ }
+ }
+
+ break;
+ }
+}
diff --git a/tsalis/src/printer.c b/tsalis/src/printer.c
new file mode 100644
index 0000000..a3f60b6
--- /dev/null
+++ b/tsalis/src/printer.c
@@ -0,0 +1,749 @@
+#include <stdarg.h>
+#include <curses.h>
+#include <salis.h>
+#include "printer.h"
+#include "tsalis.h"
+
+enum {
+ PAIR_NORMAL = 1,
+ PAIR_HEADER = 2,
+ PAIR_SELECTED_PROC = 3,
+ PAIR_FREE_CELL = 4,
+ PAIR_ALLOC_CELL = 5,
+ PAIR_MEM_BLOCK_START = 6,
+ PAIR_MEM_BLOCK_2 = 7,
+ PAIR_MEM_BLOCK_1 = 8,
+ PAIR_SELECTED_SP = 9,
+ PAIR_SELECTED_IP = 10
+};
+
+enum {
+ PAGE_MEMORY,
+ PAGE_EVOLVER,
+ PAGE_PROCESS,
+ PAGE_WORLD,
+ PAGE_COUNT
+};
+
+const char *g_instNames[] = {
+#define SINST(name, symbol) #name,
+#define SILST(name, symbol) #name
+ SINST_LIST
+#undef SINST
+#undef SILST
+};
+
+const char g_instSymbols[] = {
+#define SINST(name, symbol) symbol,
+#define SILST(name, symbol) symbol
+ SINST_LIST
+#undef SINST
+#undef SILST
+};
+
+const char *g_procElems[] = {
+ "mb1a",
+ "mb1s",
+ "mb2a",
+ "mb2s",
+ "ip",
+ "sp",
+ "reg[0]",
+ "reg[1]",
+ "reg[2]",
+ "reg[3]",
+ "stack[0]",
+ "stack[1]",
+ "stack[2]",
+ "stack[3]",
+ "stack[4]",
+ "stack[5]",
+ "stack[6]",
+ "stack[7]"
+};
+
+const int PROC_ELEMENT_COUNT = (sizeof(g_procElems) / sizeof(*g_procElems));
+const int DATA_WIDTH = 25;
+
+int g_currentPage;
+sword g_selectedProcess;
+sbool g_processShowGenes;
+sword g_processVertScroll;
+sword g_processDataScroll;
+sword g_processGeneScroll;
+sword g_worldPos;
+sword g_worldZoom;
+sword g_worldLineWidth;
+sword g_worldLineCoverage;
+sword g_worldArea;
+
+static void
+adjustWorld(void)
+{
+ g_worldLineWidth = COLS - DATA_WIDTH;
+ g_worldLineCoverage = g_worldLineWidth * g_worldZoom;
+ g_worldArea = LINES * g_worldLineCoverage;
+
+ while ((g_worldArea > (sm_getSize() * 2)) && (g_worldZoom > 1)) {
+ g_worldZoom /= 2;
+ }
+}
+
+void
+tsp_init(void)
+{
+ initscr();
+ cbreak();
+ noecho();
+ curs_set(0);
+ keypad(stdscr, TRUE);
+ start_color();
+ init_pair(PAIR_NORMAL, COLOR_WHITE, COLOR_BLACK);
+ init_pair(PAIR_HEADER, COLOR_CYAN, COLOR_BLACK);
+ init_pair(PAIR_SELECTED_PROC, COLOR_YELLOW, COLOR_BLACK);
+ init_pair(PAIR_FREE_CELL, COLOR_CYAN, COLOR_BLUE);
+ init_pair(PAIR_ALLOC_CELL, COLOR_BLUE, COLOR_CYAN);
+ init_pair(PAIR_MEM_BLOCK_START, COLOR_BLUE, COLOR_WHITE);
+ init_pair(PAIR_MEM_BLOCK_1, COLOR_BLACK, COLOR_YELLOW);
+ init_pair(PAIR_MEM_BLOCK_2, COLOR_BLACK, COLOR_GREEN);
+ init_pair(PAIR_SELECTED_SP, COLOR_BLACK, COLOR_MAGENTA);
+ init_pair(PAIR_SELECTED_IP, COLOR_BLACK, COLOR_RED);
+ g_worldZoom = 1;
+ adjustWorld();
+}
+
+void
+tsp_quit(void)
+{
+ endwin();
+}
+
+void
+tsp_onResize(void)
+{
+ clear();
+ adjustWorld();
+}
+
+void
+tsp_prevPage(void)
+{
+ clear();
+ g_currentPage += (PAGE_COUNT - 1);
+ g_currentPage %= PAGE_COUNT;
+}
+
+void
+tsp_nextPage(void)
+{
+ clear();
+ g_currentPage++;
+ g_currentPage %= PAGE_COUNT;
+}
+
+void
+tsp_scrollUp(void)
+{
+ switch (g_currentPage) {
+ case PAGE_PROCESS:
+ if (g_processVertScroll) {
+ g_processVertScroll--;
+ }
+
+ break;
+
+ case PAGE_WORLD:
+ if (g_worldPos >= g_worldLineCoverage) {
+ g_worldPos -= g_worldLineCoverage;
+ }
+
+ break;
+ }
+
+ refresh();
+}
+
+void
+tsp_scrollDown(void)
+{
+ switch (g_currentPage) {
+ case PAGE_PROCESS:
+ if (g_processVertScroll < (sp_getCap() - 1)) {
+ g_processVertScroll++;
+ }
+
+ break;
+
+ case PAGE_WORLD:
+ if ((g_worldPos + g_worldLineCoverage) < sm_getSize()) {
+ g_worldPos += g_worldLineCoverage;
+ }
+
+ break;
+ }
+
+ refresh();
+}
+
+void
+tsp_scrollLeft(void)
+{
+ switch (g_currentPage) {
+ case PAGE_PROCESS:
+ if (g_processShowGenes) {
+ if (g_processGeneScroll) {
+ g_processGeneScroll--;
+ }
+ } else {
+ if (g_processDataScroll) {
+ g_processDataScroll--;
+ }
+ }
+
+ break;
+
+ case PAGE_WORLD:
+ if (g_worldPos >= g_worldZoom) {
+ g_worldPos -= g_worldZoom;
+ } else {
+ g_worldPos = 0;
+ }
+
+ break;
+ }
+
+ refresh();
+}
+
+void
+tsp_scrollRight(void)
+{
+ switch (g_currentPage) {
+ case PAGE_PROCESS:
+ if (g_processShowGenes) {
+ g_processGeneScroll++;
+ } else {
+ if (g_processDataScroll < (sword)(PROC_ELEMENT_COUNT - 1)) {
+ g_processDataScroll++;
+ }
+ }
+
+ break;
+
+ case PAGE_WORLD:
+ if ((g_worldPos + g_worldZoom) < sm_getSize()) {
+ g_worldPos += g_worldZoom;
+ }
+
+ break;
+ }
+
+ refresh();
+}
+
+void
+tsp_scrollToTop(void)
+{
+ switch (g_currentPage) {
+ case PAGE_PROCESS:
+ g_processVertScroll = 0;
+ break;
+
+ case PAGE_WORLD:
+ g_worldPos = 0;
+ break;
+ }
+}
+
+void
+tsp_scrollToLeft(void)
+{
+ switch (g_currentPage) {
+ case PAGE_PROCESS:
+ if (g_processShowGenes) {
+ g_processGeneScroll = 0;
+ } else {
+ g_processDataScroll = 0;
+ }
+
+ break;
+ }
+}
+
+void
+tsp_zoomIn(void)
+{
+ if (g_currentPage == PAGE_WORLD) {
+ if (g_worldZoom > 1) {
+ g_worldZoom /= 2;
+ }
+
+ adjustWorld();
+ }
+}
+
+void
+tsp_zoomOut(void)
+{
+ if (g_currentPage == PAGE_WORLD) {
+ if (g_worldArea < sm_getSize()) {
+ g_worldZoom *= 2;
+ refresh();
+ }
+
+ adjustWorld();
+ }
+}
+
+void
+tsp_prevOrganism(void)
+{
+ switch (g_currentPage) {
+ case PAGE_PROCESS:
+ case PAGE_WORLD:
+ if (g_selectedProcess < sp_getCap()) {
+ g_selectedProcess += (sp_getCap() - 1);
+ g_selectedProcess %= sp_getCap();
+ }
+
+ break;
+ }
+
+ refresh();
+}
+
+void
+tsp_nextOrganism(void)
+{
+ switch (g_currentPage) {
+ case PAGE_PROCESS:
+ case PAGE_WORLD:
+ if (g_selectedProcess < sp_getCap()) {
+ g_selectedProcess++;
+ g_selectedProcess %= sp_getCap();
+ }
+
+ break;
+ }
+
+ refresh();
+}
+
+void
+tsp_gotoSelectedProc(void)
+{
+ switch (g_currentPage) {
+ case PAGE_PROCESS:
+ g_processVertScroll = g_selectedProcess;
+ break;
+
+ case PAGE_WORLD:
+ g_worldPos = sp_getProc(g_selectedProcess).mb1a;
+ break;
+ }
+}
+
+void
+tsp_selectProcess(sword proc)
+{
+ if (proc < sp_getCap()) {
+ g_selectedProcess = proc;
+ }
+}
+
+void
+tsp_moveTo(sword loc)
+{
+ switch (g_currentPage) {
+ case PAGE_PROCESS:
+ if (loc < sp_getCap()) {
+ g_processVertScroll = loc;
+ }
+
+ break;
+
+ case PAGE_WORLD:
+ if (loc < sm_getSize()) {
+ g_worldPos = loc;
+ }
+
+ break;
+ }
+}
+
+static void
+printWidget(int line, const char *format, ...)
+{
+ if (line < LINES) {
+ va_list args;
+ char dataLine[24];
+ va_start(args, format);
+ vsprintf(dataLine, format, args);
+ mvprintw(line, 1, "%.*s", COLS - 1, dataLine);
+ va_end(args);
+ }
+}
+
+static void
+printHeader(int line, const char *string)
+{
+ attron(COLOR_PAIR(PAIR_HEADER));
+ printWidget(line, string);
+ standend();
+}
+
+#define PHEADER(label) printHeader((*line)++, label)
+#define PWIDGET(label, data) printWidget((*line)++, "%-10s : %10u", label, data)
+#define PSIDGET(label, data) printWidget((*line)++, "%-10s : %10s", label, data)
+#define INCREMENT_LINE (*line)++
+
+static void
+printMemoryPage(int *line)
+{
+ PHEADER("MEMORY");
+ PWIDGET("order", sm_getOrder());
+ PWIDGET("size", sm_getSize());
+ PWIDGET("blocks", sm_getMemBlockCount());
+ PWIDGET("alloc", sm_getAllocated());
+ PWIDGET("cap", sm_getCap());
+}
+
+static void
+printEvolverPage(int *line)
+{
+ PHEADER("EVOLVER");
+ PWIDGET("lastAddr", se_getLastAddress());
+ PSIDGET("lastInst", g_instNames[se_getLastInst()] + 1);
+ PWIDGET("state0", se_getState(0));
+ PWIDGET("state1", se_getState(1));
+ PWIDGET("state2", se_getState(2));
+ PWIDGET("state3", se_getState(3));
+}
+
+static void
+printField(int y, int x, const char *field, sbool lalign)
+{
+ if ((y < LINES) && (x < COLS)) {
+ if (lalign) {
+ mvprintw(y, x, "%-.*s", COLS - x, field);
+ } else {
+ mvprintw(y, x, "%.*s", COLS - x, field);
+ }
+ }
+}
+
+static void
+printSingleProcessGenome(int line, sword pidx)
+{
+ char sidx[11];
+ SProc proc = sp_getProc(pidx);
+ sword gidx = g_processGeneScroll;
+ int xpos = 14;
+
+ if (pidx == g_selectedProcess) {
+ attron(COLOR_PAIR(PAIR_SELECTED_PROC));
+ } else if (!sp_isFree(pidx)) {
+ attron(COLOR_PAIR(PAIR_HEADER));
+ }
+
+ sprintf(sidx, "%-10u |", pidx);
+ printField(line, 1, sidx, STRUE);
+ move(line, xpos);
+
+ while ((gidx < proc.mb1s) && (xpos < COLS)) {
+ sword gaddr = proc.mb1a + gidx;
+
+ if (gaddr == proc.ip) {
+ attron(COLOR_PAIR(PAIR_SELECTED_IP));
+ } else if (gaddr == proc.sp) {
+ attron(COLOR_PAIR(PAIR_SELECTED_SP));
+ } else {
+ attron(COLOR_PAIR(PAIR_MEM_BLOCK_1));
+ }
+
+ addch(g_instSymbols[sm_getInstAt(gaddr)]);
+ gidx++;
+ xpos++;
+ }
+
+ if (proc.mb1s < g_processGeneScroll) {
+ gidx = g_processGeneScroll - proc.mb1s;
+ } else {
+ gidx = 0;
+ }
+
+ while ((gidx < proc.mb2s) && (xpos < COLS)) {
+ sword gaddr = proc.mb2a + gidx;
+
+ if (gaddr == proc.ip) {
+ attron(COLOR_PAIR(PAIR_SELECTED_IP));
+ } else if (gaddr == proc.sp) {
+ attron(COLOR_PAIR(PAIR_SELECTED_SP));
+ } else {
+ attron(COLOR_PAIR(PAIR_MEM_BLOCK_2));
+ }
+
+ addch(g_instSymbols[sm_getInstAt(gaddr)]);
+ gidx++;
+ xpos++;
+ }
+
+ standend();
+}
+
+static void
+printProcessGenes(int *line)
+{
+ sword pidx = g_processVertScroll;
+ attron(COLOR_PAIR(PAIR_HEADER));
+ printField(*line, 1, "pidx", STRUE);
+
+ standend();
+ INCREMENT_LINE;
+
+ while ((*line < LINES) && (pidx < sp_getCap())) {
+ printSingleProcessGenome(*line, pidx);
+ INCREMENT_LINE;
+ pidx++;
+ }
+
+ standend();
+}
+
+static void
+printSingleProcessData(int line, sword pidx)
+{
+ char sidx[11];
+ int eidx = g_processDataScroll;
+ int xpos = 12;
+ SProc proc = sp_getProc(pidx);
+ sword *data = (sword *)&proc;
+
+ if (pidx == g_selectedProcess) {
+ attron(COLOR_PAIR(PAIR_SELECTED_PROC));
+ } else if (!sp_isFree(pidx)) {
+ attron(COLOR_PAIR(PAIR_HEADER));
+ }
+
+ sprintf(sidx, "%u", pidx);
+ printField(line, 1, sidx, STRUE);
+
+ while (eidx < PROC_ELEMENT_COUNT) {
+ char element[13];
+ sprintf(element, "| %10u", data[eidx]);
+ printField(line, xpos, element, SFALSE);
+ eidx++;
+ xpos += 13;
+ }
+
+ standend();
+}
+
+static void
+printProcessData(int *line)
+{
+ sword pidx = g_processVertScroll;
+ int eidx = g_processDataScroll;
+ int xpos = 12;
+ attron(COLOR_PAIR(PAIR_HEADER));
+ printField(*line, 1, "pidx", STRUE);
+
+ while (eidx < PROC_ELEMENT_COUNT) {
+ char element[13];
+ sprintf(element, "| %10s", g_procElems[eidx]);
+ printField(*line, xpos, element, SFALSE);
+ eidx++;
+ xpos += 13;
+ }
+
+ standend();
+ INCREMENT_LINE;
+
+ while ((*line < LINES) && (pidx < sp_getCap())) {
+ printSingleProcessData(*line, pidx);
+ INCREMENT_LINE;
+ pidx++;
+ }
+
+ standend();
+}
+
+static void
+printProcessPage(int *line)
+{
+ int cline;
+ sbool fnull = (sp_getFirst() == (sword) - 1);
+ sbool lnull = (sp_getLast() == (sword) - 1);
+ PHEADER("PROCESS");
+ PWIDGET("count", sp_getCount());
+ PWIDGET("cap", sp_getCap());
+ fnull ? PSIDGET("first", "---") : PWIDGET("first", sp_getFirst());
+ lnull ? PSIDGET("last", "---") : PWIDGET("last", sp_getLast());
+ PWIDGET("selected", g_selectedProcess);
+ INCREMENT_LINE;
+
+ for (cline = *line; cline < LINES; cline++) {
+ move(cline, 0);
+ clrtoeol();
+ }
+
+ if (g_processShowGenes) {
+ printProcessGenes(line);
+ } else {
+ printProcessData(line);
+ }
+}
+
+static int
+getColorOf(sword addr)
+{
+ if (!sp_isFree(g_selectedProcess)) {
+ SProc proc = sp_getProc(g_selectedProcess);
+
+ if (addr == proc.ip) {
+ return PAIR_SELECTED_IP;
+ } else if (addr == proc.sp) {
+ return PAIR_SELECTED_SP;
+ } else if ((addr >= proc.mb1a) && (addr < (proc.mb1a + proc.mb1s))) {
+ return PAIR_MEM_BLOCK_1;
+ } else if ((addr >= proc.mb2a) && (addr < (proc.mb2a + proc.mb2s))) {
+ return PAIR_MEM_BLOCK_2;
+ }
+ }
+
+ if (sm_isMemBlockStartAt(addr)) {
+ return PAIR_MEM_BLOCK_START;
+ } else if (sm_isAllocatedAt(addr)) {
+ return PAIR_ALLOC_CELL;
+ } else {
+ return PAIR_FREE_CELL;
+ }
+}
+
+static void
+printWorld(void)
+{
+ sword y;
+
+ for (y = 0; y < (sword)LINES; y++) {
+ sword x;
+
+ for (x = 0; x < g_worldLineWidth; x++) {
+ sword addr = g_worldPos + (((y * g_worldLineWidth) + x) * g_worldZoom);
+ sbool atEnd = !sm_isValidAt(addr);
+ int xpos = DATA_WIDTH + x;
+
+ if (atEnd) {
+ mvaddch(y, xpos, ' ');
+ continue;
+ }
+
+ if (g_worldZoom == 1) {
+ char symbol = g_instSymbols[sm_getInstAt(addr)];
+ attron(COLOR_PAIR(getColorOf(addr)));
+ mvaddch(y, xpos, symbol);
+ } else {
+ sword offset;
+ char symbol;
+ sword instSum = 0;
+ int color = PAIR_FREE_CELL;
+
+ for (offset = 0; offset < g_worldZoom; offset++) {
+ int testColor;
+ sword offsetAddr = addr + offset;
+
+ if (!sm_isValidAt(offsetAddr)) {
+ break;
+ }
+
+ instSum += sm_getInstAt(offsetAddr);
+ testColor = getColorOf(offsetAddr);
+
+ if (testColor > color) {
+ color = testColor;
+ }
+ }
+
+ instSum /= g_worldZoom;
+
+ if (!instSum) {
+ symbol = ' ';
+ } else if (instSum < 32) {
+ symbol = '-';
+ } else {
+ symbol = '=';
+ }
+
+ attron(COLOR_PAIR(color));
+ mvaddch(y, xpos, symbol);
+ }
+
+ standend();
+ }
+ }
+}
+
+static void
+printWorldPage(int *line)
+{
+ int eidx;
+ SProc proc = sp_getProc(g_selectedProcess);
+ sword *data = (sword *)&proc;
+ PHEADER("WORLD");
+ PWIDGET("pos", g_worldPos);
+ PWIDGET("zoom", g_worldZoom);
+ PWIDGET("selected", g_selectedProcess);
+ INCREMENT_LINE;
+ PHEADER("SELECTED");
+
+ if (!sp_isFree(g_selectedProcess)) {
+ attron(COLOR_PAIR(PAIR_SELECTED_PROC));
+ }
+
+ for (eidx = 0; eidx < PROC_ELEMENT_COUNT; eidx++) {
+ PWIDGET(g_procElems[eidx], data[eidx]);
+ }
+
+ standend();
+ printWorld();
+}
+
+void
+tsp_printData(void)
+{
+ int linev = 1;
+ int *line = &linev;
+ PHEADER("SALIS");
+ PSIDGET("name", g_simName);
+ PSIDGET("state", g_running ? "running" : "paused");
+
+ if (g_autoSaveInterval) {
+ PWIDGET("autosave", g_autoSaveInterval);
+ } else {
+ PSIDGET("autosave", "---");
+ }
+
+ PWIDGET("cycle", s_getCycle());
+ PWIDGET("epoch", s_getEpoch());
+ INCREMENT_LINE;
+
+ switch (g_currentPage) {
+ case PAGE_MEMORY:
+ printMemoryPage(line);
+ break;
+
+ case PAGE_EVOLVER:
+ printEvolverPage(line);
+ break;
+
+ case PAGE_PROCESS:
+ printProcessPage(line);
+ break;
+
+ case PAGE_WORLD:
+ printWorldPage(line);
+ break;
+ }
+
+ refresh();
+}
diff --git a/tsalis/src/tsalis.c b/tsalis/src/tsalis.c
new file mode 100644
index 0000000..ab24c61
--- /dev/null
+++ b/tsalis/src/tsalis.c
@@ -0,0 +1,118 @@
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <curses.h>
+#include <salis.h>
+#include "printer.h"
+#include "handler.h"
+#include "tsalis.h"
+
+#define DEFAULT_ORDER 16
+
+sbool g_exit;
+sbool g_running;
+sword g_autoSaveInterval;
+char g_simName[NAME_MAX_SIZE + 1] = "def.sim";
+
+static void
+onDefault(void)
+{
+ FILE *testFile = fopen(g_simName, "r");
+
+ if (testFile) {
+ fclose(testFile);
+ s_load(g_simName);
+ } else {
+ s_init(DEFAULT_ORDER);
+ }
+}
+
+static void
+onLoad(const char *fileName)
+{
+ FILE *testFile;
+
+ if (strlen(fileName) > NAME_MAX_SIZE) {
+ fputs("ERROR: File name too long", stderr);
+ exit(1);
+ }
+
+ strncpy(g_simName, fileName, NAME_MAX_SIZE);
+ testFile = fopen(g_simName, "r");
+
+ if (testFile) {
+ fclose(testFile);
+ s_load(g_simName);
+ } else {
+ fputs("ERROR: File does not exist", stderr);
+ exit(1);
+ }
+}
+
+static void
+init(int argc, char **argv)
+{
+ if (argc == 1) {
+ onDefault();
+ } else if (argc == 2) {
+ char cmd = argv[1][0];
+ char *val = &argv[1][1];
+
+ if (cmd == 'n') {
+ s_init(atoi(val));
+ } else if (cmd == 'l') {
+ onLoad(val);
+ } else {
+ fputs("ERROR: Incorrect arguments", stderr);
+ exit(1);
+ }
+ } else {
+ fputs("ERROR: Incorrect argument count", stderr);
+ exit(1);
+ }
+
+ tsp_init();
+}
+
+static void
+exec(void)
+{
+ while (!g_exit) {
+ if (g_running) {
+ clock_t beg = clock();
+ clock_t end;
+ float delay;
+
+ do {
+ s_cycle();
+
+ if (g_autoSaveInterval && !(s_getCycle() % g_autoSaveInterval)) {
+ s_save(g_simName);
+ }
+
+ end = clock();
+ delay = (float)(end - beg) / CLOCKS_PER_SEC;
+ } while (delay < (1.0 / 60.0));
+ }
+
+ tsp_printData();
+ tsh_handleEvent(getch());
+ }
+}
+
+static void
+quit(void)
+{
+ tsp_quit();
+ s_save(g_simName);
+ s_quit();
+}
+
+int
+main(int argc, char **argv)
+{
+ init(argc, argv);
+ exec();
+ quit();
+ return 0;
+}