From 2dc9d118efb64de6ea54a5a9eb4474f8e5ef3145 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Thu, 29 Feb 2024 01:50:44 +0100 Subject: Initial commit --- .gitignore | 10 + Makefile | 32 ++ README.md | 111 +++++ build/.keep | 0 include/evolver.h | 17 + include/instset.h | 93 ++++ include/memory.h | 30 ++ include/process.h | 40 ++ include/salis.h | 22 + include/types.h | 31 ++ lib/.keep | 0 src/evolver.c | 125 ++++++ src/instset.c | 56 +++ src/memory.c | 227 ++++++++++ src/process.c | 1071 ++++++++++++++++++++++++++++++++++++++++++++++ src/salis.c | 98 +++++ tsalis/Makefile | 30 ++ tsalis/README.md | 76 ++++ tsalis/bin/.keep | 0 tsalis/build/.keep | 0 tsalis/include/handler.h | 6 + tsalis/include/printer.h | 35 ++ tsalis/include/tsalis.h | 11 + tsalis/src/handler.c | 317 ++++++++++++++ tsalis/src/printer.c | 749 ++++++++++++++++++++++++++++++++ tsalis/src/tsalis.c | 118 +++++ 26 files changed, 3305 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 build/.keep create mode 100644 include/evolver.h create mode 100644 include/instset.h create mode 100644 include/memory.h create mode 100644 include/process.h create mode 100644 include/salis.h create mode 100644 include/types.h create mode 100644 lib/.keep create mode 100644 src/evolver.c create mode 100644 src/instset.c create mode 100644 src/memory.c create mode 100644 src/process.c create mode 100644 src/salis.c create mode 100644 tsalis/Makefile create mode 100644 tsalis/README.md create mode 100644 tsalis/bin/.keep create mode 100644 tsalis/build/.keep create mode 100644 tsalis/include/handler.h create mode 100644 tsalis/include/printer.h create mode 100644 tsalis/include/tsalis.h create mode 100644 tsalis/src/handler.c create mode 100644 tsalis/src/printer.c create mode 100644 tsalis/src/tsalis.c 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 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 +#include +#include +#include +#include +#include + +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 + +#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 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 +#include +#include +#include +#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 +#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 +#include +#include +#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 +#include +#include +#include +#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 +#include +#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 diff --git a/tsalis/build/.keep b/tsalis/build/.keep new file mode 100644 index 0000000..e69de29 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 +#include +#include +#include +#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 +#include +#include +#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 +#include +#include +#include +#include +#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; +} -- cgit v1.2.1