diff options
author | Paul Oliver <contact@pauloliver.dev> | 2024-02-29 01:50:44 +0100 |
---|---|---|
committer | Paul Oliver <contact@pauloliver.dev> | 2024-02-29 01:50:44 +0100 |
commit | 2dc9d118efb64de6ea54a5a9eb4474f8e5ef3145 (patch) | |
tree | 74039957b10390da4875d676303a781bd0792e45 |
Initial commit
-rw-r--r-- | .gitignore | 10 | ||||
-rw-r--r-- | Makefile | 32 | ||||
-rw-r--r-- | README.md | 111 | ||||
-rw-r--r-- | build/.keep | 0 | ||||
-rw-r--r-- | include/evolver.h | 17 | ||||
-rw-r--r-- | include/instset.h | 93 | ||||
-rw-r--r-- | include/memory.h | 30 | ||||
-rw-r--r-- | include/process.h | 40 | ||||
-rw-r--r-- | include/salis.h | 22 | ||||
-rw-r--r-- | include/types.h | 31 | ||||
-rw-r--r-- | lib/.keep | 0 | ||||
-rw-r--r-- | src/evolver.c | 125 | ||||
-rw-r--r-- | src/instset.c | 56 | ||||
-rw-r--r-- | src/memory.c | 227 | ||||
-rw-r--r-- | src/process.c | 1071 | ||||
-rw-r--r-- | src/salis.c | 98 | ||||
-rw-r--r-- | tsalis/Makefile | 30 | ||||
-rw-r--r-- | tsalis/README.md | 76 | ||||
-rw-r--r-- | tsalis/bin/.keep | 0 | ||||
-rw-r--r-- | tsalis/build/.keep | 0 | ||||
-rw-r--r-- | tsalis/include/handler.h | 6 | ||||
-rw-r--r-- | tsalis/include/printer.h | 35 | ||||
-rw-r--r-- | tsalis/include/tsalis.h | 11 | ||||
-rw-r--r-- | tsalis/src/handler.c | 317 | ||||
-rw-r--r-- | tsalis/src/printer.c | 749 | ||||
-rw-r--r-- | tsalis/src/tsalis.c | 118 |
26 files changed, 3305 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b7b88bd --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*
+!*.*
+!*/
+!Makefile
+!*/Makefile
+*.a
+*.d
+*.o
+*.sim
+*.exe
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e8f873a --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +AR := ar
+CC := gcc
+LIB := lib/libsalis.a
+
+SOURCES := $(wildcard src/*.c)
+OBJECTS := $(patsubst src/%.c,build/%.o,$(SOURCES))
+DEPS := $(patsubst %.o,%.d,$(OBJECTS))
+
+LFLAGS := rs
+
+# uncomment for debug
+# OFLAGS := -ggdb
+
+# uncomment for release
+OFLAGS := -O3 -DNDEBUG
+
+CFLAGS := -Iinclude -c $(OFLAGS) -MMD -Wall -Wextra -std=c89 -pedantic-errors \
+ -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition
+
+all: $(OBJECTS)
+ $(AR) $(LFLAGS) $(LIB) $(OBJECTS)
+ $(MAKE) -C tsalis
+
+-include $(DEPS)
+
+$(OBJECTS): $(patsubst build/%.o,src/%.c,$@)
+ $(CC) $(CFLAGS) $(patsubst build/%.o,src/%.c,$@) -o $@
+
+clean:
+ -rm build/*
+ -rm $(LIB)
+ $(MAKE) clean -C tsalis
diff --git a/README.md b/README.md new file mode 100644 index 0000000..6f90781 --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +# SALIS: A-Life Simulator
+
+## Overview
+*SALIS* is my newest artificial life project. Mainly a re-take on Tom Ray's
+TIERRA simulation, but with my very own set of tweaks. Having a grasp on TIERRA
+will make understanding this simulation a lot easier.
+
+- [video about TIERRA](https://www.youtube.com/watch?v=Wl5rRGVD0QI)
+- [read about TIERRA](http://life.ou.edu/pubs/doc/index.html#What)
+
+For those that already know TIERRA, the main differences between it and SALIS
+are:
+- the replacement of templates with key-lock instruction pairs
+- the addition of a SEEKER POINTER to all organisms
+
+The seeker pointer is an attempt to bring extra spatial and temporal coherence
+to the simulation. Allocation, reads and writes will take more time when done
+between addresses that are far away, as a consequence of the SP having to
+travel those distances at a speed of 1 byte per update. In other words, in
+SALIS information can't travel faster than 1 byte per update (SALIS' speed of
+light, if you will).
+
+To watch an introductory video about *SALIS*
+[go here.](https://www.youtube.com/watch?v=jCFmOCvy6po)
+
+### Details
+- *SALIS* is an API, so an UI must be written to communicate with it
+- *SALIS* is written in C
+- *SALIS* must be compiled as a library (e.g. *libsalis.a*)
+
+### Organisms consist of
+- One or two associated memory blocks
+- One instruction pointer
+- One seeker pointer
+- Four registers
+- A stack of configurable size (default is 8)
+
+### Queue
+- Newborn organisms are placed on top of the queue
+- Organisms are killed at the bottom of the queue
+
+### Instruction set
+*SALIS*' organisms read a simple language similar to ASM. This language
+consists of 64 instructions, each with an associated name and symbol.
+Whenever an organism performs an invalid instruction it's considered a *fault*.
+To preserve robustness, faulty instructions are simply ignored by the
+organisms and their IPs are incremented to the next address.
+
+#### Faults
+- IP or SP reaching ends of memory
+- Perform a search or attempt a jump without a following key
+- Writing to an allocated (but not owned) or invalid address
+- Reading (loading) from an invalid address
+- SP on address non-adjacent to child memory block while allocating
+- Swapping, freeing or splitting when not owning 2 memory blocks
+- Dividing by zero
+
+#### Instruction set table
+|Name |Sym. |Val. |Description |
+|:--------|:-----|:-----|---------------------------------------------------------------------:|
+|NOOP | |00 |No operation. Does nothing. |
+|NOP0-3 |0-3 |01-04 |No operation. Modifies registers to be used (r0x to r3x). |
+|JMPB-F\* |() |05-06 |Jump to lock matching following key. |
+|ADRB-F\* |[] |07-08 |Search for lock matching following key and store on r[0]. |
+|IFNZ |? |09 |If r[0] is not zero, execute following instruction. Skip otherwise. |
+|ALLB-F\* |{} |10-11 |Allocate block of size stored on r[0]. Store its address on r[0]. |
+|BSWP\* |% |12 |Swap parent and child memory blocks. |
+|BCLR\* |\| |13 |Free child memory block. |
+|SPLT\* |$ |14 |Split. Child memory block becomes new organism. |
+|ADDN |+ |15 |Add (r[0] = r[1] + r[2]). |
+|SUBN |- |16 |Subtract (r[0] = r[1] - r[2]). |
+|MULN |\* |17 |Multiply (r[0] = r[1] \* r[2]). |
+|DIVN\* |/ |18 |Divide (r[0] = r[1] / r[2]). Faults if divisor is zero. |
+|INCN |^ |19 |Increment (r[0]++). |
+|DECN |v |20 |Decrement (r[0]--). |
+|NOTN |! |21 |Not (r[0] = !r[0]). |
+|SHFL |< |22 |Shift left by 1 (r[0] << 1). |
+|SHFR |> |23 |Shift right by 1 (r[0] >> 1). |
+|ZERO |z |24 |Put zero (r[0] = 0). |
+|UNIT |u |25 |Put one (r[0] = 1). |
+|PSHN |# |26 |Push r[0] to stack. |
+|POPN |~ |27 |Pop from stack into r[0]. |
+|LOAD\* |. |28 |Load instruction at address pointed by r[0] into r[1]. |
+|WRTE\* |: |29 |Write instruction on r[1] to address pointed by r[0]. |
+|DUPL |" |30 |Duplicate value on r[0] into r[1]. |
+|SWAP |x |31 |Swap values on r[0] and r[1]. |
+|KEYA-P |a-p |32-47 |Keys. |
+|LOKA-P |A-P |48-63 |Locks. |
+
+Instructions that may fault are marked with an asterisk (\*) on the table above.
+
+Instructions that modify values on registers may be followed by
+up to 3 register modifiers (r[0], r[1] and r[2]). By default, all registers
+are set to r0x.
+
+|Sample |r[0] |r[1] |r[2] |Meaning |
+|:-------|:----|:----|:----|-----------------------------------------:|
+|+123 |r1x |r2x |r3x |r1x = r2x + r3x |
+|-11 |r1x |r1x |r0x |r1x = r1x - r0x |
+|\* |r0x |r0x |r0x |r0x = r0x \* r0x |
+|!2 |r2x |--- |--- |r2x = !r2x |
+|x01 |r0x |r1x |--- |swap r0x and r1x |
+|]a1 |r1x |--- |--- |r1x = address of closest following LOKA |
+|[b |r0x |--- |--- |r0x = address of closest previous LOKB |
+
+## Building instructions
+You'll need nothing but a C compiler (C89). A sample makefile (Makefile)
+is provided for GNU Make. Just run `make` inside this directory and the salis
+library should compile as well as the tsalis executable. Feel free to edit
+the makefile as needed. Code should compile easily on all platforms and on all
+C89 compliant compilers. If you run into any difficulties, please let me know!
diff --git a/build/.keep b/build/.keep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/build/.keep diff --git a/include/evolver.h b/include/evolver.h new file mode 100644 index 0000000..15a4f64 --- /dev/null +++ b/include/evolver.h @@ -0,0 +1,17 @@ +#ifndef SALIS_EVOLVER_H
+#define SALIS_EVOLVER_H
+
+void se_init (void);
+void se_quit (void);
+void se_load (FILE *file);
+void se_save (FILE *file);
+
+sbool se_isInit (void);
+sword se_getLastAddress (void);
+sbyte se_getLastInst (void);
+sword se_getState (sword eidx);
+void se_setState (sword eidx, sword state);
+
+void se_cycle (void);
+
+#endif
diff --git a/include/instset.h b/include/instset.h new file mode 100644 index 0000000..e819ff3 --- /dev/null +++ b/include/instset.h @@ -0,0 +1,93 @@ +#ifndef SALIS_INSTSET_H
+#define SALIS_INSTSET_H
+
+#define SINST_COUNT 64
+
+#define SINST_LIST \
+ SINST(SNOOP, ' ') \
+ SINST(SNOP0, '0') \
+ SINST(SNOP1, '1') \
+ SINST(SNOP2, '2') \
+ SINST(SNOP3, '3') \
+ \
+ SINST(SJMPB, '(') \
+ SINST(SJMPF, ')') \
+ SINST(SADRB, '[') \
+ SINST(SADRF, ']') \
+ SINST(SIFNZ, '?') \
+ \
+ SINST(SALLB, '{') \
+ SINST(SALLF, '}') \
+ SINST(SBSWP, '%') \
+ SINST(SBCLR, '|') \
+ SINST(SSPLT, '$') \
+ \
+ SINST(SADDN, '+') \
+ SINST(SSUBN, '-') \
+ SINST(SMULN, '*') \
+ SINST(SDIVN, '/') \
+ SINST(SINCN, '^') \
+ SINST(SDECN, 'v') \
+ SINST(SNOTN, '!') \
+ SINST(SSHFL, '<') \
+ SINST(SSHFR, '>') \
+ SINST(SZERO, 'z') \
+ SINST(SUNIT, 'u') \
+ \
+ SINST(SPSHN, '#') \
+ SINST(SPOPN, '~') \
+ \
+ SINST(SLOAD, '.') \
+ SINST(SWRTE, ':') \
+ SINST(SDUPL, '"') \
+ SINST(SSWAP, 'x') \
+ \
+ SINST(SKEYA, 'a') \
+ SINST(SKEYB, 'b') \
+ SINST(SKEYC, 'c') \
+ SINST(SKEYD, 'd') \
+ SINST(SKEYE, 'e') \
+ SINST(SKEYF, 'f') \
+ SINST(SKEYG, 'g') \
+ SINST(SKEYH, 'h') \
+ SINST(SKEYI, 'i') \
+ SINST(SKEYJ, 'j') \
+ SINST(SKEYK, 'k') \
+ SINST(SKEYL, 'l') \
+ SINST(SKEYM, 'm') \
+ SINST(SKEYN, 'n') \
+ SINST(SKEYO, 'o') \
+ SINST(SKEYP, 'p') \
+ \
+ SINST(SLOKA, 'A') \
+ SINST(SLOKB, 'B') \
+ SINST(SLOKC, 'C') \
+ SINST(SLOKD, 'D') \
+ SINST(SLOKE, 'E') \
+ SINST(SLOKF, 'F') \
+ SINST(SLOKG, 'G') \
+ SINST(SLOKH, 'H') \
+ SINST(SLOKI, 'I') \
+ SINST(SLOKJ, 'J') \
+ SINST(SLOKK, 'K') \
+ SINST(SLOKL, 'L') \
+ SINST(SLOKM, 'M') \
+ SINST(SLOKN, 'N') \
+ SINST(SLOKO, 'O') \
+ SILST(SLOKP, 'P')
+
+enum sinst {
+#define SINST(name, symb) name,
+#define SILST(name, symb) name
+ SINST_LIST
+#undef SINST
+#undef SILST
+};
+
+sbool si_isInst (sbyte inst);
+sbool si_isMod (sbyte inst);
+sbool si_isKey (sbyte inst);
+sbool si_isLock (sbyte inst);
+sbool si_keyLockMatch (sbyte key, sbyte lock);
+
+#endif
diff --git a/include/memory.h b/include/memory.h new file mode 100644 index 0000000..a36a2ff --- /dev/null +++ b/include/memory.h @@ -0,0 +1,30 @@ +#ifndef SALIS_MEMORY_H
+#define SALIS_MEMORY_H
+
+void sm_init (sword order);
+void sm_quit (void);
+void sm_load (FILE *file);
+void sm_save (FILE *file);
+
+sbool sm_isInit (void);
+sword sm_getOrder (void);
+sword sm_getSize (void);
+sword sm_getMemBlockCount (void);
+sword sm_getAllocated (void);
+sword sm_getCap (void);
+
+sbool sm_isOverCap (void);
+sbool sm_isValidAt (sword addr);
+sbool sm_isMemBlockStartAt (sword addr);
+sbool sm_isAllocatedAt (sword addr);
+
+void sm_setMemBlockStartAt (sword addr);
+void sm_unsetMemBlockStartAt (sword addr);
+void sm_allocateAt (sword addr);
+void sm_freeAt (sword addr);
+sbyte sm_getInstAt (sword addr);
+void sm_setInstAt (sword addr, sbyte inst);
+sbyte sm_getByteAt (sword addr);
+void sm_setByteAt (sword addr, sbyte byte);
+
+#endif
diff --git a/include/process.h b/include/process.h new file mode 100644 index 0000000..35e1fdf --- /dev/null +++ b/include/process.h @@ -0,0 +1,40 @@ +#ifndef SALIS_PROCESS_H
+#define SALIS_PROCESS_H
+
+#define SPROC_REG_COUNT 4
+#define SPROC_STACK_SIZE 8
+#define SPROC_ELEM_COUNT (6 + SPROC_REG_COUNT + SPROC_STACK_SIZE)
+
+typedef struct {
+ sword mb1a;
+ sword mb1s;
+ sword mb2a;
+ sword mb2s;
+
+ sword ip;
+ sword sp;
+
+ sword regs [SPROC_REG_COUNT];
+ sword stack [SPROC_STACK_SIZE];
+} SProc;
+
+void sp_init (void);
+void sp_quit (void);
+void sp_load (FILE *file);
+void sp_save (FILE *file);
+
+sbool sp_isInit (void);
+sword sp_getCount (void);
+sword sp_getCap (void);
+sword sp_getFirst (void);
+sword sp_getLast (void);
+
+sbool sp_isFree (sword pidx);
+SProc sp_getProc (sword pidx);
+void sp_setProc (sword pidx, SProc proc);
+
+void sp_create (sword addr, sword size);
+void sp_kill (void);
+void sp_cycle (void);
+
+#endif
diff --git a/include/salis.h b/include/salis.h new file mode 100644 index 0000000..a9e2d3d --- /dev/null +++ b/include/salis.h @@ -0,0 +1,22 @@ +#ifndef SALIS_H
+#define SALIS_H
+
+#include <stdio.h>
+#include <types.h>
+#include <instset.h>
+#include <memory.h>
+#include <process.h>
+#include <evolver.h>
+
+void s_init (sword order);
+void s_quit (void);
+void s_load (const char *fileName);
+void s_save (const char *fileName);
+
+sbool s_isInit (void);
+sword s_getCycle (void);
+sword s_getEpoch (void);
+
+void s_cycle (void);
+
+#endif
diff --git a/include/types.h b/include/types.h new file mode 100644 index 0000000..3c0c3ff --- /dev/null +++ b/include/types.h @@ -0,0 +1,31 @@ +#ifndef SALIS_TYPES_H
+#define SALIS_TYPES_H
+
+#include <limits.h>
+
+#define SWORD_MAX (0xffffffff)
+#define SBYTE_MAX (0xff)
+
+#if USHRT_MAX == SWORD_MAX
+ typedef unsigned short sword;
+#elif UINT_MAX == SWORD_MAX
+ typedef unsigned int sword;
+#elif ULONG_MAX == SWORD_MAX
+ typedef unsigned long sword;
+#elif
+ #error "Cannot define 32 bit unsigned int (sword)"
+#endif
+
+#if UCHAR_MAX == SBYTE_MAX
+ typedef unsigned char sbyte;
+#elif
+ #error "Cannot define 8 bit unsigned int (sbyte)"
+#endif
+
+typedef int sbool;
+
+#define SFALSE (0)
+#define STRUE (1)
+#define SNULL ((sword)-1)
+
+#endif
diff --git a/lib/.keep b/lib/.keep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/.keep diff --git a/src/evolver.c b/src/evolver.c new file mode 100644 index 0000000..ba043a3 --- /dev/null +++ b/src/evolver.c @@ -0,0 +1,125 @@ +#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "types.h"
+#include "instset.h"
+#include "memory.h"
+#include "evolver.h"
+
+sbool g_isInit;
+sword g_lastAddress;
+sbyte g_lastInst;
+sword g_state[4];
+
+void
+se_init(void)
+{
+ assert(!g_isInit);
+ srand((unsigned)time(NULL));
+ g_state[0] = rand();
+ g_state[1] = rand();
+ g_state[2] = rand();
+ g_state[3] = rand();
+ g_isInit = STRUE;
+}
+
+void
+se_quit(void)
+{
+ assert(g_isInit);
+ g_isInit = SFALSE;
+ g_lastAddress = 0;
+ g_lastInst = 0;
+ g_state[0] = 0;
+ g_state[1] = 0;
+ g_state[2] = 0;
+ g_state[3] = 0;
+}
+
+void
+se_load(FILE *file)
+{
+ assert(!g_isInit);
+ assert(file);
+ fread(&g_isInit, sizeof(sbool), 1, file);
+ fread(&g_lastAddress, sizeof(sword), 1, file);
+ fread(&g_lastInst, sizeof(sbyte), 1, file);
+ fread(g_state, sizeof(sword), 4, file);
+}
+
+void
+se_save(FILE *file)
+{
+ assert(g_isInit);
+ assert(file);
+ fwrite(&g_isInit, sizeof(sbool), 1, file);
+ fwrite(&g_lastAddress, sizeof(sword), 1, file);
+ fwrite(&g_lastInst, sizeof(sbyte), 1, file);
+ fwrite(g_state, sizeof(sword), 4, file);
+}
+
+sbool
+se_isInit(void)
+{
+ return g_isInit;
+}
+
+sword
+se_getLastAddress(void)
+{
+ return g_lastAddress;
+}
+
+sbyte
+se_getLastInst(void)
+{
+ assert(si_isInst(g_lastInst));
+ return g_lastInst;
+}
+
+sword
+se_getState(sword eidx)
+{
+ assert(eidx < 4);
+ return g_state[eidx];
+}
+
+void
+se_setState(sword eidx, sword state)
+{
+ assert(g_isInit);
+ assert(eidx < 4);
+ g_state[eidx] = state;
+}
+
+static sword
+generateRandomNumber(void)
+{
+ sword s;
+ sword t;
+ assert(g_isInit);
+ t = g_state[3];
+ t ^= t << 11;
+ t ^= t >> 8;
+ g_state[3] = g_state[2];
+ g_state[2] = g_state[1];
+ g_state[1] = g_state[0];
+ s = g_state[0];
+ t ^= s;
+ t ^= s >> 19;
+ g_state[0] = t;
+ return t;
+}
+
+void
+se_cycle(void)
+{
+ assert(g_isInit);
+ g_lastAddress = generateRandomNumber();
+ g_lastInst = (sbyte)(generateRandomNumber() % SINST_COUNT);
+
+ if (sm_isValidAt(g_lastAddress)) {
+ sm_setInstAt(g_lastAddress, (sbyte)g_lastInst);
+ }
+}
diff --git a/src/instset.c b/src/instset.c new file mode 100644 index 0000000..1333ffb --- /dev/null +++ b/src/instset.c @@ -0,0 +1,56 @@ +#include <assert.h>
+#include "types.h"
+#include "instset.h"
+
+sbool
+si_isInst(sbyte inst)
+{
+ return inst < SINST_COUNT;
+}
+
+static sbool
+isBetween(sbyte inst, sbyte lo, sbyte hi)
+{
+ assert(si_isInst(inst));
+ assert(lo < SINST_COUNT);
+ assert(hi < SINST_COUNT);
+
+ if (inst < lo) {
+ return SFALSE;
+ }
+
+ if (inst > hi) {
+ return SFALSE;
+ }
+
+ return STRUE;
+}
+
+sbool
+si_isMod(sbyte inst)
+{
+ assert(si_isInst(inst));
+ return isBetween(inst, SNOP0, SNOP3);
+}
+
+sbool
+si_isKey(sbyte inst)
+{
+ assert(si_isInst(inst));
+ return isBetween(inst, SKEYA, SKEYP);
+}
+
+sbool
+si_isLock(sbyte inst)
+{
+ assert(si_isInst(inst));
+ return isBetween(inst, SLOKA, SLOKP);
+}
+
+sbool
+si_keyLockMatch(sbyte key, sbyte lock)
+{
+ assert(si_isKey(key));
+ assert(si_isInst(lock));
+ return (key - SKEYA) == (lock - SLOKA);
+}
diff --git a/src/memory.c b/src/memory.c new file mode 100644 index 0000000..4befd4f --- /dev/null +++ b/src/memory.c @@ -0,0 +1,227 @@ +#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "types.h"
+#include "instset.h"
+#include "memory.h"
+
+#define MEM_BLOCK_START_FLAG (0x80)
+#define ALLOCATED_FLAG (0x40)
+#define INSTRUCTION_MASK (0x3f)
+
+static sbool g_isInit;
+static sword g_order;
+static sword g_size;
+static sword g_memBlockCount;
+static sword g_allocated;
+static sword g_cap;
+static sbyte *g_data;
+
+void
+sm_init(sword order)
+{
+ assert(!g_isInit);
+ assert(INSTRUCTION_MASK == SINST_COUNT - 1);
+ g_isInit = STRUE;
+ g_order = order;
+ g_size = 1 << g_order;
+ g_cap = g_size / 2;
+ g_data = calloc(g_size, sizeof(sbyte));
+ assert(g_data);
+}
+
+void
+sm_quit(void)
+{
+ assert(g_isInit);
+ free(g_data);
+ g_isInit = SFALSE;
+ g_order = 0;
+ g_size = 0;
+ g_memBlockCount = 0;
+ g_allocated = 0;
+ g_cap = 0;
+ g_data = NULL;
+}
+
+void
+sm_load(FILE *file)
+{
+ assert(!g_isInit);
+ assert(file);
+ fread(&g_isInit, sizeof(sbool), 1, file);
+ fread(&g_order, sizeof(sword), 1, file);
+ fread(&g_size, sizeof(sword), 1, file);
+ fread(&g_memBlockCount, sizeof(sword), 1, file);
+ fread(&g_allocated, sizeof(sword), 1, file);
+ fread(&g_cap, sizeof(sword), 1, file);
+ g_data = calloc(g_size, sizeof(sbyte));
+ assert(g_data);
+ fread(g_data, sizeof(sbyte), g_size, file);
+}
+
+void
+sm_save(FILE *file)
+{
+ assert(g_isInit);
+ assert(file);
+ fwrite(&g_isInit, sizeof(sbool), 1, file);
+ fwrite(&g_order, sizeof(sword), 1, file);
+ fwrite(&g_size, sizeof(sword), 1, file);
+ fwrite(&g_memBlockCount, sizeof(sword), 1, file);
+ fwrite(&g_allocated, sizeof(sword), 1, file);
+ fwrite(&g_cap, sizeof(sword), 1, file);
+ fwrite(g_data, sizeof(sbyte), g_size, file);
+}
+
+sbool
+sm_isInit(void)
+{
+ return g_isInit;
+}
+
+sword
+sm_getOrder(void)
+{
+ return g_order;
+}
+
+sword
+sm_getSize(void)
+{
+ return g_size;
+}
+
+sword
+sm_getMemBlockCount(void)
+{
+ return g_memBlockCount;
+}
+
+sword
+sm_getAllocated(void)
+{
+ return g_allocated;
+}
+
+sword
+sm_getCap(void)
+{
+ return g_cap;
+}
+
+sbool
+sm_isOverCap(void)
+{
+ assert(g_isInit);
+ return g_allocated > g_cap;
+}
+
+sbool
+sm_isValidAt(sword addr)
+{
+ assert(g_isInit);
+ return addr < g_size;
+}
+
+sbool
+sm_isMemBlockStartAt(sword addr)
+{
+ assert(g_isInit);
+ assert(sm_isValidAt(addr));
+ return !!(g_data[addr] & MEM_BLOCK_START_FLAG);
+}
+
+sbool
+sm_isAllocatedAt(sword addr)
+{
+ assert(g_isInit);
+ assert(sm_isValidAt(addr));
+ return !!(g_data[addr] & ALLOCATED_FLAG);
+}
+
+void
+sm_setMemBlockStartAt(sword addr)
+{
+ assert(g_isInit);
+ assert(sm_isValidAt(addr));
+ assert(!sm_isMemBlockStartAt(addr));
+ g_data[addr] ^= MEM_BLOCK_START_FLAG;
+ g_memBlockCount++;
+ assert(sm_isMemBlockStartAt(addr));
+ assert(g_memBlockCount);
+ assert(g_memBlockCount <= g_size);
+}
+
+void
+sm_unsetMemBlockStartAt(sword addr)
+{
+ assert(g_isInit);
+ assert(g_memBlockCount);
+ assert(sm_isValidAt(addr));
+ assert(sm_isMemBlockStartAt(addr));
+ g_data[addr] ^= MEM_BLOCK_START_FLAG;
+ g_memBlockCount--;
+ assert(!sm_isMemBlockStartAt(addr));
+ assert(g_memBlockCount <= g_size);
+}
+
+void
+sm_allocateAt(sword addr)
+{
+ assert(g_isInit);
+ assert(sm_isValidAt(addr));
+ assert(!sm_isAllocatedAt(addr));
+ g_data[addr] ^= ALLOCATED_FLAG;
+ g_allocated++;
+ assert(sm_isAllocatedAt(addr));
+ assert(g_allocated);
+ assert(g_allocated <= g_size);
+}
+
+void
+sm_freeAt(sword addr)
+{
+ assert(g_isInit);
+ assert(g_allocated);
+ assert(sm_isValidAt(addr));
+ assert(sm_isAllocatedAt(addr));
+ g_data[addr] ^= ALLOCATED_FLAG;
+ g_allocated--;
+ assert(!sm_isAllocatedAt(addr));
+ assert(g_allocated <= g_size);
+}
+
+sbyte
+sm_getInstAt(sword addr)
+{
+ assert(g_isInit);
+ assert(sm_isValidAt(addr));
+ return g_data[addr] & INSTRUCTION_MASK;
+}
+
+void
+sm_setInstAt(sword addr, sbyte inst)
+{
+ assert(g_isInit);
+ assert(sm_isValidAt(addr));
+ assert(si_isInst(inst));
+ g_data[addr] &= (MEM_BLOCK_START_FLAG | ALLOCATED_FLAG);
+ g_data[addr] |= inst;
+}
+
+sbyte
+sm_getByteAt(sword addr)
+{
+ assert(g_isInit);
+ assert(sm_isValidAt(addr));
+ return g_data[addr];
+}
+
+void
+sm_setByteAt(sword addr, sbyte byte)
+{
+ assert(g_isInit);
+ assert(sm_isValidAt(addr));
+ g_data[addr] = byte;
+}
diff --git a/src/process.c b/src/process.c new file mode 100644 index 0000000..e3d3c18 --- /dev/null +++ b/src/process.c @@ -0,0 +1,1071 @@ +#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "types.h"
+#include "instset.h"
+#include "memory.h"
+#include "process.h"
+
+static sbool g_isInit;
+static sword g_count;
+static sword g_cap;
+static sword g_first;
+static sword g_last;
+static SProc *g_procs;
+
+void
+sp_init(void)
+{
+ assert(!g_isInit);
+ g_isInit = STRUE;
+ g_cap = 1;
+ g_first = SNULL;
+ g_last = SNULL;
+ g_procs = calloc(g_cap, sizeof(SProc));
+ assert(g_procs);
+}
+
+void
+sp_quit(void)
+{
+ assert(g_isInit);
+ free(g_procs);
+ g_isInit = SFALSE;
+ g_count = 0;
+ g_cap = 0;
+ g_first = 0;
+ g_last = 0;
+ g_procs = NULL;
+}
+
+void
+sp_load(FILE *file)
+{
+ assert(!g_isInit);
+ assert(file);
+ fread(&g_isInit, sizeof(sbool), 1, file);
+ fread(&g_count, sizeof(sword), 1, file);
+ fread(&g_cap, sizeof(sword), 1, file);
+ fread(&g_first, sizeof(sword), 1, file);
+ fread(&g_last, sizeof(sword), 1, file);
+ g_procs = calloc(g_cap, sizeof(SProc));
+ assert(g_procs);
+ fread(g_procs, sizeof(SProc), g_cap, file);
+}
+
+void
+sp_save(FILE *file)
+{
+assert(g_isInit);
+assert(file);
+fwrite(&g_isInit, sizeof(sbool), 1, file);
+fwrite(&g_count, sizeof(sword), 1, file);
+fwrite(&g_cap, sizeof(sword), 1, file);
+fwrite(&g_first, sizeof(sword), 1, file);
+fwrite(&g_last, sizeof(sword), 1, file);
+fwrite(g_procs, sizeof(SProc), g_cap, file);
+}
+
+sbool
+sp_isInit(void)
+{
+ return g_isInit;
+}
+
+sword
+sp_getCount(void)
+{
+ return g_count;
+}
+
+sword
+sp_getCap(void)
+{
+ return g_cap;
+}
+
+sword
+sp_getFirst(void)
+{
+ return g_first;
+}
+
+sword
+sp_getLast(void)
+{
+ return g_last;
+}
+
+sbool
+sp_isFree(sword pidx)
+{
+ sbool isFree;
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ isFree = !(g_procs[pidx].mb1s);
+#ifndef NDEBUG
+
+ if (isFree) {
+ assert(!g_procs[pidx].mb1a);
+ assert(!g_procs[pidx].mb2a);
+ assert(!g_procs[pidx].mb2s);
+ }
+
+#endif
+ return isFree;
+}
+
+SProc
+sp_getProc(sword pidx)
+{
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ return g_procs[pidx];
+}
+
+void
+sp_setProc(sword pidx, SProc proc)
+{
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ g_procs[pidx] = proc;
+}
+
+static void
+reallocQueue(sword queueLock)
+{
+ sword newCap;
+ SProc *newQueue;
+ sword fidx;
+ sword bidx;
+ assert(g_isInit);
+ assert(g_count == g_cap);
+ assert(queueLock < g_cap);
+ newCap = g_cap * 2;
+ newQueue = calloc(newCap, sizeof(SProc));
+ assert(newQueue);
+ fidx = queueLock;
+ bidx = (queueLock - 1) % newCap;
+
+ /* copy all forward from lock */
+ while (STRUE) {
+ sword oldIdx = fidx % g_cap;
+ memcpy(&newQueue[fidx], &g_procs[oldIdx], sizeof(SProc));
+
+ if (oldIdx == g_last) {
+ g_last = fidx;
+ break;
+ } else {
+ fidx++;
+ }
+ }
+
+ if (queueLock != g_first) {
+ /* copy all backward from lock */
+ while (STRUE) {
+ sword oldIdx = bidx % g_cap;
+ memcpy(&newQueue[bidx], &g_procs[oldIdx], sizeof(SProc));
+
+ if (oldIdx == g_first) {
+ g_first = bidx;
+ break;
+ } else {
+ bidx--;
+ bidx %= newCap;
+ }
+ }
+ }
+
+ free(g_procs);
+ g_cap = newCap;
+ g_procs = newQueue;
+}
+
+static sword
+getNewProc(sword queueLock)
+{
+ assert(g_isInit);
+
+ if (g_count == g_cap) {
+ reallocQueue(queueLock);
+ }
+
+ g_count++;
+
+ if (g_count == 1) {
+ g_first = 0;
+ g_last = 0;
+ return 0;
+ } else {
+ g_last++;
+ g_last %= g_cap;
+ return g_last;
+ }
+}
+
+static void
+create(sword addr, sword size, sword queueLock, sbool allocate)
+{
+ sword pidx;
+ assert(g_isInit);
+ assert(sm_isValidAt(addr));
+ assert(sm_isValidAt(addr + size - 1));
+
+ if (allocate) {
+ sword offset;
+
+ for (offset = 0; offset < size; offset++) {
+ sword naddr = addr + offset;
+ assert(!sm_isAllocatedAt(naddr));
+ assert(!sm_isMemBlockStartAt(naddr));
+ sm_allocateAt(naddr);
+ }
+
+ sm_setMemBlockStartAt(addr);
+ }
+
+ pidx = getNewProc(queueLock);
+ g_procs[pidx].mb1a = addr;
+ g_procs[pidx].mb1s = size;
+ g_procs[pidx].ip = addr;
+ g_procs[pidx].sp = addr;
+}
+
+void
+sp_create(sword addr, sword size)
+{
+ assert(g_isInit);
+ assert(sm_isValidAt(addr));
+ assert(sm_isValidAt(addr + size - 1));
+ create(addr, size, 0, STRUE);
+}
+
+static void
+freeMemBlock(sword addr, sword size)
+{
+ sword offset;
+ assert(sm_isValidAt(addr));
+ assert(sm_isValidAt(addr + size - 1));
+ assert(size);
+ assert(sm_isMemBlockStartAt(addr));
+ sm_unsetMemBlockStartAt(addr);
+
+ for (offset = 0; offset < size; offset++) {
+ sword oaddr = addr + offset;
+ assert(sm_isValidAt(oaddr));
+ assert(sm_isAllocatedAt(oaddr));
+ assert(!sm_isMemBlockStartAt(oaddr));
+ sm_freeAt(oaddr);
+ }
+}
+
+static void
+freeMemOwnedBy(sword pidx)
+{
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ freeMemBlock(g_procs[pidx].mb1a, g_procs[pidx].mb1s);
+
+ if (g_procs[pidx].mb2s) {
+ assert(g_procs[pidx].mb1a != g_procs[pidx].mb2a);
+ freeMemBlock(g_procs[pidx].mb2a, g_procs[pidx].mb2s);
+ }
+}
+
+void
+sp_kill(void)
+{
+ assert(g_isInit);
+ assert(g_count);
+ assert(g_first != SNULL);
+ assert(g_last != SNULL);
+ assert(!sp_isFree(g_first));
+ freeMemOwnedBy(g_first);
+ memset(&g_procs[g_first], 0, sizeof(SProc));
+ g_count--;
+
+ if (g_first == g_last) {
+ g_first = SNULL;
+ g_last = SNULL;
+ return;
+ }
+
+ g_first++;
+ g_first %= g_cap;
+}
+
+static void
+incrementIP(sword pidx)
+{
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ g_procs[pidx].ip++;
+ g_procs[pidx].sp = g_procs[pidx].ip;
+}
+
+static sbool
+seek(sword pidx, sbool forward)
+{
+ sword nextAddr;
+ sbyte nextInst;
+ sbyte spInst;
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ nextAddr = g_procs[pidx].ip + 1;
+
+ if (!sm_isValidAt(nextAddr)) {
+ incrementIP(pidx);
+ return SFALSE;
+ }
+
+ nextInst = sm_getInstAt(nextAddr);
+
+ if (!si_isKey(nextInst)) {
+ incrementIP(pidx);
+ return SFALSE;
+ }
+
+ spInst = sm_getInstAt(g_procs[pidx].sp);
+
+ if (si_keyLockMatch(nextInst, spInst)) {
+ return STRUE;
+ }
+
+ if (forward) {
+ g_procs[pidx].sp++;
+ } else {
+ g_procs[pidx].sp--;
+ }
+
+ return SFALSE;
+}
+
+static void
+jump(sword pidx)
+{
+#ifndef NDEBUG
+ sbyte nextInst;
+ sbyte spInst;
+#endif
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+#ifndef NDEBUG
+ nextInst = sm_getInstAt(g_procs[pidx].ip + 1);
+ spInst = sm_getInstAt(g_procs[pidx].sp);
+ assert(si_isKey(nextInst));
+ assert(si_isLock(spInst));
+ assert(si_keyLockMatch(nextInst, spInst));
+#endif
+ g_procs[pidx].ip = g_procs[pidx].sp;
+}
+
+static sword *
+getRegAddr(sword pidx, sword modAddr)
+{
+ sbyte modInst;
+ sbyte modOffset;
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ assert(sm_isValidAt(modAddr));
+ modInst = sm_getInstAt(modAddr);
+ assert(si_isMod(modInst));
+ modOffset = modInst - SNOP0;
+ assert(modOffset < SPROC_REG_COUNT);
+ return &(g_procs[pidx].regs[modOffset]);
+}
+
+static void
+getRegAddrList(sword pidx, sword **regList, sword regCount, sbool offset)
+{
+ sword modAddr;
+ sbyte ridx;
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ assert(regList);
+ assert(regCount);
+ assert(regCount < 4);
+
+ if (offset) {
+ modAddr = g_procs[pidx].ip + 2;
+ } else {
+ modAddr = g_procs[pidx].ip + 1;
+ }
+
+ for (ridx = 0; ridx < regCount; ridx++) {
+ regList[ridx] = &(g_procs[pidx].regs[0]);
+ }
+
+ for (ridx = 0; ridx < regCount; ridx++) {
+ sword modNext = modAddr + ridx;
+
+ if (!sm_isValidAt(modNext)) {
+ break;
+ } else {
+ sword modInst = sm_getInstAt(modNext);
+
+ if (!si_isMod(modInst)) {
+ break;
+ } else {
+ regList[ridx] = getRegAddr(pidx, modNext);
+ }
+ }
+ }
+}
+
+static void
+addr(sword pidx)
+{
+#ifndef NDEBUG
+ sbyte nextInst;
+ sbyte spInst;
+#endif
+ sword *reg;
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+#ifndef NDEBUG
+ nextInst = sm_getInstAt(g_procs[pidx].ip + 1);
+ spInst = sm_getInstAt(g_procs[pidx].sp);
+ assert(si_isKey(nextInst));
+ assert(si_isLock(spInst));
+ assert(si_keyLockMatch(nextInst, spInst));
+#endif
+ getRegAddrList(pidx, ®, 1, STRUE);
+ *reg = g_procs[pidx].sp;
+ incrementIP(pidx);
+}
+
+static void
+ifnz(sword pidx)
+{
+ sword *reg;
+ sword jumpMod;
+ sword nextAddr;
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ getRegAddrList(pidx, ®, 1, SFALSE);
+ nextAddr = g_procs[pidx].ip + 1;
+
+ if (sm_isValidAt(nextAddr)) {
+ sbyte nextInst = sm_getInstAt(nextAddr);
+ sbool nextInstIsMod = si_isMod(nextInst);
+
+ if (nextInstIsMod) {
+ jumpMod = 1;
+ } else {
+ jumpMod = 0;
+ }
+ } else {
+ jumpMod = 0;
+ }
+
+ if (*reg) {
+ /* execute next instruction */
+ g_procs[pidx].ip += (jumpMod + 1);
+ } else {
+ /* skip next instruction */
+ g_procs[pidx].ip += (jumpMod + 2);
+ }
+
+ g_procs[pidx].sp = g_procs[pidx].ip;
+}
+
+static void
+freeMemBlock2Of(sword pidx)
+{
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ assert(g_procs[pidx].mb2s);
+ freeMemBlock(g_procs[pidx].mb2a, g_procs[pidx].mb2s);
+ g_procs[pidx].mb2a = 0;
+ g_procs[pidx].mb2s = 0;
+}
+
+static void
+alloc(sword pidx, sbool forward)
+{
+ sword *regs[2];
+ sword blockSize;
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ getRegAddrList(pidx, regs, 2, SFALSE);
+ blockSize = *regs[0];
+
+ /* check for possible errors */
+ /* ignore if requested block size is zero */
+ if (!blockSize) {
+ incrementIP(pidx);
+ return;
+ }
+
+ /* ignore if sp is not adjacent to already existing memory block */
+ if (g_procs[pidx].mb2s) {
+ sword correctAddr = g_procs[pidx].mb2a;
+
+ if (forward) {
+ correctAddr += g_procs[pidx].mb2s;
+ } else {
+ correctAddr--;
+ }
+
+ if (g_procs[pidx].sp != correctAddr) {
+ incrementIP(pidx);
+ return;
+ }
+ }
+
+ /* on successful allocation */
+ /* increment ip and save new block's address on register */
+ if (g_procs[pidx].mb2s == blockSize) {
+ incrementIP(pidx);
+ *regs[1] = g_procs[pidx].mb2a;
+ return;
+ }
+
+ /* handle block enlargement */
+ /* handle sp collision with allocated space */
+ if (sm_isAllocatedAt(g_procs[pidx].sp)) {
+ if (g_procs[pidx].mb2s) {
+ freeMemBlock2Of(pidx);
+ }
+
+ if (forward) {
+ g_procs[pidx].sp++;
+ } else {
+ g_procs[pidx].sp--;
+ }
+
+ return;
+ }
+
+ /* enlarge block when no collision occurs */
+ sm_allocateAt(g_procs[pidx].sp);
+
+ /* correct memory block start flag address */
+ if (!g_procs[pidx].mb2s) {
+ g_procs[pidx].mb2a = g_procs[pidx].sp;
+ sm_setMemBlockStartAt(g_procs[pidx].sp);
+ } else {
+ if (!forward) {
+ sm_unsetMemBlockStartAt(g_procs[pidx].mb2a);
+ g_procs[pidx].mb2a = g_procs[pidx].sp;
+ sm_setMemBlockStartAt(g_procs[pidx].mb2a);
+ }
+ }
+
+ g_procs[pidx].mb2s++;
+
+ /* move sp to next location */
+ if (forward) {
+ g_procs[pidx].sp++;
+ } else {
+ g_procs[pidx].sp--;
+ }
+}
+
+static void
+bswap(sword pidx)
+{
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+
+ if (g_procs[pidx].mb2s) {
+ sword addrTmp = g_procs[pidx].mb1a;
+ sword sizeTmp = g_procs[pidx].mb1s;
+ g_procs[pidx].mb1a = g_procs[pidx].mb2a;
+ g_procs[pidx].mb1s = g_procs[pidx].mb2s;
+ g_procs[pidx].mb2a = addrTmp;
+ g_procs[pidx].mb2s = sizeTmp;
+ }
+
+ incrementIP(pidx);
+}
+
+static void
+bclear(sword pidx)
+{
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+
+ if (g_procs[pidx].mb2s) {
+ freeMemBlock2Of(pidx);
+ }
+
+ incrementIP(pidx);
+}
+
+static void
+split(sword pidx)
+{
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+
+ if (g_procs[pidx].mb2s) {
+ create(g_procs[pidx].mb2a, g_procs[pidx].mb2s, pidx, SFALSE);
+ g_procs[pidx].mb2a = 0;
+ g_procs[pidx].mb2s = 0;
+ }
+
+ incrementIP(pidx);
+}
+
+static void
+r3op(sword pidx, sbyte inst)
+{
+ sword *regs[3];
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ getRegAddrList(pidx, regs, 3, SFALSE);
+
+ /* ignore when dividing by zero */
+ if ((inst == SDIVN) && (*regs[2] == 0)) {
+ incrementIP(pidx);
+ return;
+ }
+
+ switch (inst) {
+ case SADDN:
+ *regs[0] = *regs[1] + *regs[2];
+ break;
+
+ case SSUBN:
+ *regs[0] = *regs[1] - *regs[2];
+ break;
+
+ case SMULN:
+ *regs[0] = *regs[1] * *regs[2];
+ break;
+
+ case SDIVN:
+ assert(*regs[2]);
+ *regs[0] = *regs[1] / *regs[2];
+ break;
+
+ default:
+ assert(0);
+ }
+
+ incrementIP(pidx);
+}
+
+static void
+r1op(sword pidx, sbyte inst)
+{
+ sword *reg;
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ getRegAddrList(pidx, ®, 1, SFALSE);
+
+ switch (inst) {
+ case SINCN:
+ (*reg)++;
+ break;
+
+ case SDECN:
+ (*reg)--;
+ break;
+
+ case SNOTN:
+ *reg = !(*reg);
+ break;
+
+ case SSHFL:
+ *reg <<= 1;
+ break;
+
+ case SSHFR:
+ *reg >>= 1;
+ break;
+
+ case SZERO:
+ *reg = 0;
+ break;
+
+ case SUNIT:
+ *reg = 1;
+ break;
+
+ default:
+ assert(0);
+ }
+
+ incrementIP(pidx);
+}
+
+static void
+push(sword pidx)
+{
+ sword *reg;
+ sword sidx;
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ getRegAddrList(pidx, ®, 1, SFALSE);
+
+ for (sidx = SPROC_STACK_SIZE - 1; sidx; sidx--) {
+ g_procs[pidx].stack[sidx] = g_procs[pidx].stack[sidx - 1];
+ }
+
+ g_procs[pidx].stack[0] = *reg;
+ incrementIP(pidx);
+}
+
+static void
+pop(sword pidx)
+{
+ sword *reg;
+ sword sidx;
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ getRegAddrList(pidx, ®, 1, SFALSE);
+ *reg = g_procs[pidx].stack[0];
+
+ for (sidx = 1; sidx < SPROC_STACK_SIZE; sidx++) {
+ g_procs[pidx].stack[sidx - 1] = g_procs[pidx].stack[sidx];
+ }
+
+ g_procs[pidx].stack[SPROC_STACK_SIZE - 1] = 0;
+ incrementIP(pidx);
+}
+
+static void
+load(sword pidx)
+{
+ sword *regs[2];
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ getRegAddrList(pidx, regs, 2, SFALSE);
+
+ if (!sm_isValidAt(*regs[0])) {
+ incrementIP(pidx);
+ return;
+ }
+
+ if (g_procs[pidx].sp < *regs[0]) {
+ g_procs[pidx].sp++;
+ } else if (g_procs[pidx].sp > *regs[0]) {
+ g_procs[pidx].sp--;
+ } else {
+ *regs[1] = sm_getInstAt(*regs[0]);
+ incrementIP(pidx);
+ }
+}
+
+static sbool
+isWriteableBy(sword pidx, sword addr)
+{
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+
+ if (!sm_isValidAt(addr)) {
+ return SFALSE;
+ }
+
+ if (!sm_isAllocatedAt(addr)) {
+ return STRUE;
+ } else {
+ sword lo1 = g_procs[pidx].mb1a;
+ sword lo2 = g_procs[pidx].mb2a;
+ sword hi1 = lo1 + g_procs[pidx].mb1s;
+ sword hi2 = lo2 + g_procs[pidx].mb2s;
+ return (addr >= lo1 && addr < hi1) || (addr >= lo2 && addr < hi2);
+ }
+}
+
+static void
+write(sword pidx)
+{
+ sword *regs[2];
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ getRegAddrList(pidx, regs, 2, SFALSE);
+
+ if (!sm_isValidAt(*regs[0])) {
+ incrementIP(pidx);
+ return;
+ }
+
+ if (!si_isInst(*regs[1])) {
+ incrementIP(pidx);
+ return;
+ }
+
+ if (g_procs[pidx].sp < *regs[0]) {
+ g_procs[pidx].sp++;
+ } else if (g_procs[pidx].sp > *regs[0]) {
+ g_procs[pidx].sp--;
+ } else {
+ if (isWriteableBy(pidx, *regs[0])) {
+ sm_setInstAt(*regs[0], *regs[1]);
+ }
+
+ incrementIP(pidx);
+ }
+}
+
+static void
+r2op(sword pidx, sbyte inst)
+{
+ sword *regs[2];
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ getRegAddrList(pidx, regs, 2, SFALSE);
+
+ switch (inst) {
+ case SDUPL:
+ *regs[1] = *regs[0];
+ break;
+
+ case SSWAP: {
+ sword temp = *regs[0];
+ *regs[0] = *regs[1];
+ *regs[1] = temp;
+ break;
+ }
+
+ default:
+ assert(0);
+ }
+
+ incrementIP(pidx);
+}
+
+static void
+cycle(sword pidx)
+{
+ sbyte inst;
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+
+ if (!sm_isValidAt(g_procs[pidx].ip) || !sm_isValidAt(g_procs[pidx].sp)) {
+ incrementIP(pidx);
+ return;
+ }
+
+ inst = sm_getInstAt(g_procs[pidx].ip);
+
+ switch (inst) {
+ case SJMPB:
+ if (seek(pidx, SFALSE)) {
+ jump(pidx);
+ }
+
+ break;
+
+ case SJMPF:
+ if (seek(pidx, STRUE)) {
+ jump(pidx);
+ }
+
+ break;
+
+ case SADRB:
+ if (seek(pidx, SFALSE)) {
+ addr(pidx);
+ }
+
+ break;
+
+ case SADRF:
+ if (seek(pidx, STRUE)) {
+ addr(pidx);
+ }
+
+ break;
+
+ case SIFNZ:
+ ifnz(pidx);
+ break;
+
+ case SALLB:
+ alloc(pidx, SFALSE);
+ break;
+
+ case SALLF:
+ alloc(pidx, STRUE);
+ break;
+
+ case SBSWP:
+ bswap(pidx);
+ break;
+
+ case SBCLR:
+ bclear(pidx);
+ break;
+
+ case SSPLT:
+ split(pidx);
+ break;
+
+ case SADDN:
+ case SSUBN:
+ case SMULN:
+ case SDIVN:
+ r3op(pidx, inst);
+ break;
+
+ case SINCN:
+ case SDECN:
+ case SNOTN:
+ case SSHFL:
+ case SSHFR:
+ case SZERO:
+ case SUNIT:
+ r1op(pidx, inst);
+ break;
+
+ case SPSHN:
+ push(pidx);
+ break;
+
+ case SPOPN:
+ pop(pidx);
+ break;
+
+ case SLOAD:
+ load(pidx);
+ break;
+
+ case SWRTE:
+ write(pidx);
+ break;
+
+ case SDUPL:
+ case SSWAP:
+ r2op(pidx, inst);
+ break;
+
+ default:
+ incrementIP(pidx);
+ }
+}
+
+#ifndef NDEBUG
+
+static void
+isFreeValid(sword pidx)
+{
+ sword *element;
+ sword eidx;
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(sp_isFree(pidx));
+ element = (sword *)&g_procs[pidx];
+
+ for (eidx = 0; eidx < SPROC_ELEM_COUNT; eidx++) {
+ assert(!(*element));
+ element++;
+ }
+}
+
+static void
+isUsedValid(sword pidx)
+{
+ sword offset;
+ assert(g_isInit);
+ assert(pidx < g_cap);
+ assert(!sp_isFree(pidx));
+ assert(sm_isMemBlockStartAt(g_procs[pidx].mb1a));
+
+ if (g_procs[pidx].mb2s) {
+ assert(sm_isMemBlockStartAt(g_procs[pidx].mb2a));
+ }
+
+ for (offset = 0; offset < g_procs[pidx].mb1s; offset++) {
+ sword addr = g_procs[pidx].mb1a + offset;
+ assert(sm_isValidAt(addr));
+ assert(sm_isAllocatedAt(addr));
+ }
+
+ for (offset = 0; offset < g_procs[pidx].mb2s; offset++) {
+ sword addr = g_procs[pidx].mb2a + offset;
+ assert(sm_isValidAt(addr));
+ assert(sm_isAllocatedAt(addr));
+ }
+}
+
+static void
+isValid(sword pidx)
+{
+ assert(g_isInit);
+ assert(pidx < g_cap);
+
+ if (sp_isFree(pidx)) {
+ isFreeValid(pidx);
+ } else {
+ isUsedValid(pidx);
+ }
+}
+
+#endif
+
+void
+sp_cycle(void)
+{
+#ifndef NDEBUG
+ sword pidx;
+#endif
+ assert(g_isInit);
+#ifndef NDEBUG
+
+ /* check for validity before cycle */
+ for (pidx = 0; pidx < sp_getCap(); pidx++) {
+ isValid(pidx);
+ }
+
+#endif
+
+ if (sp_getCount()) {
+ sword qidx = sp_getLast();
+
+ /* cycle all procs */
+ while (STRUE) {
+ cycle(qidx);
+
+ if (qidx == sp_getFirst()) {
+ break;
+ } else {
+ qidx--;
+ qidx %= sp_getCap();
+ }
+ }
+
+ /* kill procs if memory is over capacity */
+ while (sm_isOverCap()) {
+ sp_kill();
+ }
+ } else {
+ sp_create(0, 1);
+ }
+
+#ifndef NDEBUG
+
+ /* check for validity after cycle */
+ for (pidx = 0; pidx < sp_getCap(); pidx++) {
+ isValid(pidx);
+ }
+
+#endif
+}
diff --git a/src/salis.c b/src/salis.c new file mode 100644 index 0000000..c57c60e --- /dev/null +++ b/src/salis.c @@ -0,0 +1,98 @@ +#include <assert.h>
+#include <stdlib.h>
+#include "salis.h"
+
+static sbool g_isInit;
+static sword g_cycle;
+static sword g_epoch;
+
+void
+s_init(sword order)
+{
+ assert(!g_isInit);
+ sm_init(order);
+ se_init();
+ sp_init();
+ g_isInit = STRUE;
+}
+
+void
+s_quit(void)
+{
+ assert(g_isInit);
+ sp_quit();
+ se_quit();
+ sm_quit();
+ g_isInit = SFALSE;
+ g_cycle = 0;
+ g_epoch = 0;
+}
+
+void
+s_load(const char *fileName)
+{
+ FILE *file;
+ assert(!g_isInit);
+ assert(fileName);
+ file = fopen(fileName, "rb");
+ assert(file);
+ fread(&g_isInit, sizeof(sbool), 1, file);
+ fread(&g_cycle, sizeof(sword), 1, file);
+ fread(&g_epoch, sizeof(sword), 1, file);
+ sm_load(file);
+ se_load(file);
+ sp_load(file);
+ fclose(file);
+}
+
+void
+s_save(const char *fileName)
+{
+ FILE *file;
+ assert(g_isInit);
+ assert(fileName);
+ file = fopen(fileName, "wb");
+ assert(file);
+ fwrite(&g_isInit, sizeof(sbool), 1, file);
+ fwrite(&g_cycle, sizeof(sword), 1, file);
+ fwrite(&g_epoch, sizeof(sword), 1, file);
+ sm_save(file);
+ se_save(file);
+ sp_save(file);
+ fclose(file);
+}
+
+sbool
+s_isInit(void)
+{
+ return g_isInit;
+}
+
+sword
+s_getCycle(void)
+{
+ return g_cycle;
+}
+
+sword
+s_getEpoch(void)
+{
+ return g_epoch;
+}
+
+void
+s_cycle(void)
+{
+ assert(g_isInit);
+ assert(sm_isInit());
+ assert(sp_isInit());
+ assert(se_isInit());
+ g_cycle++;
+
+ if (!g_cycle) {
+ g_epoch++;
+ }
+
+ se_cycle();
+ sp_cycle();
+}
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; +} |