aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorPaul Oliver <contact@pauloliver.dev>2024-02-29 02:29:13 +0100
committerPaul Oliver <contact@pauloliver.dev>2024-02-29 02:29:13 +0100
commitca118555214a176728b9aab87849391344306d6d (patch)
tree833cffdd4066a7114b1d79d6eeaa2e0152408fc8 /include
Initial commit.
Diffstat (limited to 'include')
-rw-r--r--include/common.h19
-rw-r--r--include/evolver.h38
-rw-r--r--include/getter.h20
-rw-r--r--include/instset.h71
-rw-r--r--include/memory.h134
-rw-r--r--include/process.h97
-rw-r--r--include/salis.h67
-rw-r--r--include/types.h45
8 files changed, 491 insertions, 0 deletions
diff --git a/include/common.h b/include/common.h
new file mode 100644
index 0000000..7386a34
--- /dev/null
+++ b/include/common.h
@@ -0,0 +1,19 @@
+/**
+* @file common.h
+* @author Paul Oliver
+*
+* This module controls the 'common pipe', which is the FIFO file through which
+* communication between different simulations can occur. By calling SEND,
+* processes may output local instructions through the pipe. These instructions
+* may then be read by processes running on a different simulation instance.
+*/
+
+#ifndef SALIS_COMMON_H
+#define SALIS_COMMON_H
+
+void _sal_comm_init(string pipe);
+void _sal_comm_quit(void);
+void _sal_comm_send(uint8 inst);
+uint8 _sal_comm_receive(void);
+
+#endif
diff --git a/include/evolver.h b/include/evolver.h
new file mode 100644
index 0000000..b2ead10
--- /dev/null
+++ b/include/evolver.h
@@ -0,0 +1,38 @@
+/**
+* @file evolver.h
+* @author Paul Oliver
+*
+* This module controls all random events in Salis. At its heart lies a
+* XOR-Shift pseudo-random number generator with 128 bits of state. It controls
+* cosmic rays and rises simulation entropy whenever organisms 'eat'
+* information.
+*/
+
+#ifndef SALIS_EVOLVER_H
+#define SALIS_EVOLVER_H
+
+void _sal_evo_init(void);
+void _sal_evo_quit(void);
+void _sal_evo_load_from(FILE *file);
+void _sal_evo_save_into(FILE *file);
+
+/** Get address where the last cosmic ray landed.
+* @return Last address changed by a cosmic ray
+*/
+SALIS_API uint32 sal_evo_get_last_changed_address(void);
+
+/** Get amount of random numbers generated during the last simulation cycle.
+* @return Number of calls to the random number generator during the last cycle
+*/
+SALIS_API uint32 sal_evo_get_calls_on_last_cycle(void);
+
+/** Access the internal state of the XOR-Shift random number generator.
+* @param state_index Index of one of the 32 bit state-blocks [0-4]
+* @return State of the 32 bit block
+*/
+SALIS_API uint32 sal_evo_get_state(uint8 state_index);
+
+void _sal_evo_randomize_at(uint32 address);
+void _sal_evo_cycle(void);
+
+#endif
diff --git a/include/getter.h b/include/getter.h
new file mode 100644
index 0000000..83f0664
--- /dev/null
+++ b/include/getter.h
@@ -0,0 +1,20 @@
+/**
+* @file getter.h
+* @author Paul Oliver
+*
+* We declare a helper macro for easy 'getting' of module state variables. Other
+* similar, more specific macros are defined inside the module sources. Don't
+* repeat yourself! :-)
+*/
+
+#ifndef SALIS_GETTER_H
+#define SALIS_GETTER_H
+
+#define UINT32_GETTER(mod, name) \
+uint32 sal_##mod##_get_##name(void) \
+{ \
+ assert(g_is_init); \
+ return g_##name; \
+}
+
+#endif
diff --git a/include/instset.h b/include/instset.h
new file mode 100644
index 0000000..ab7bab0
--- /dev/null
+++ b/include/instset.h
@@ -0,0 +1,71 @@
+/**
+* @file instset.h
+* @author Paul Oliver
+*
+* Here we declare the complete instruction set of the Salis virtual machine.
+* Additionally, some helper functions are declared for determining instruction
+* type and validity.
+*/
+
+#ifndef SALIS_INSTSET_H
+#define SALIS_INSTSET_H
+
+#define INST_COUNT 32
+
+/** Salis instruction set. The 'SALIS_INST' macro and inline doc-comments help
+* python parse this file. Don't edit these unless you know what you're doing!
+*/
+enum {
+ SALIS_INST NOP0, /**< . Template constructor */
+ SALIS_INST NOP1, /**< : Template constructor */
+ SALIS_INST MODA, /**< a Register modifier */
+ SALIS_INST MODB, /**< b Register modifier */
+ SALIS_INST MODC, /**< c Register modifier */
+ SALIS_INST MODD, /**< d Register modifier */
+ SALIS_INST JMPB, /**< ( Jump back to template complement */
+ SALIS_INST JMPF, /**< ) Jump forward to template complement */
+ SALIS_INST ADRB, /**< [ Search back for template complement */
+ SALIS_INST ADRF, /**< ] Search forward for template complement */
+ SALIS_INST MALB, /**< { Allocate backwards */
+ SALIS_INST MALF, /**< } Allocate forward */
+ SALIS_INST SWAP, /**< % Swap memory blocks */
+ SALIS_INST SPLT, /**< $ Split child memory block */
+ SALIS_INST INCN, /**< ^ Increment register */
+ SALIS_INST DECN, /**< v Decrement register */
+ SALIS_INST ZERO, /**< 0 Zero out register */
+ SALIS_INST UNIT, /**< 1 Place 1 on register */
+ SALIS_INST NOTN, /**< ! Negation operator */
+ SALIS_INST IFNZ, /**< ? Conditional operator */
+ SALIS_INST SUMN, /**< + Add two registers */
+ SALIS_INST SUBN, /**< - Subtract two registers */
+ SALIS_INST MULN, /**< * Multiply two registers */
+ SALIS_INST DIVN, /**< / Divide two registers */
+ SALIS_INST LOAD, /**< L Load instruction from memory */
+ SALIS_INST WRTE, /**< W Write instruction into memory */
+ SALIS_INST SEND, /**< S Send instruction to common pipe */
+ SALIS_INST RECV, /**< R Receive instruction from common pipe */
+ SALIS_INST PSHN, /**< # Push value to stack */
+ SALIS_INST POPN, /**< ~ Pop value from stack */
+ SALIS_INST EATB, /**< < Eat backwards */
+ SALIS_INST EATF /**< > Eat forward */
+};
+
+/** Determine if an unsigned integer contains a valid instruction.
+* @param byte Any unsigned integer up to 32 bits
+* @return Whether or nor integer contains a valid instruction
+*/
+SALIS_API boolean sal_is_inst(uint32 word);
+
+/** Determine if instruction is a template constructor [NOP0-NOP1].
+* @param inst Must contain a valid instruction
+* @return Whether or not instruction is a template constructor
+*/
+SALIS_API boolean sal_is_template(uint32 inst);
+
+/** Determine if instruction a register modifier [MOD0-MOD3].
+* @param inst Must contain a valid instruction
+* @return Whether or not instruction is a register modifier
+*/
+SALIS_API boolean sal_is_mod(uint32 inst);
+
+#endif
diff --git a/include/memory.h b/include/memory.h
new file mode 100644
index 0000000..e7b97ee
--- /dev/null
+++ b/include/memory.h
@@ -0,0 +1,134 @@
+/**
+* @file memory.h
+* @author Paul Oliver
+*
+* This module gives access to Salis memory. You can check the state of each
+* byte (instruction and flags) at any time and also perform manual memory
+* manipulations.
+*/
+
+#ifndef SALIS_MEMORY_H
+#define SALIS_MEMORY_H
+
+#define IP_FLAG 0x80
+#define BLOCK_START_FLAG 0x40
+#define ALLOCATED_FLAG 0x20
+#define INSTRUCTION_MASK 0x1f
+
+void _sal_mem_init(uint32 order);
+void _sal_mem_quit(void);
+void _sal_mem_load_from(FILE *file);
+void _sal_mem_save_into(FILE *file);
+
+/** Get memory order.
+* @return Order of memory (memory_size == 1 << order)
+*/
+SALIS_API uint32 sal_mem_get_order(void);
+
+/** Get memory size.
+* @return Size of memory (memory_size == 1 << order)
+*/
+SALIS_API uint32 sal_mem_get_size(void);
+
+/** Get amount of addresses with the IP flag set on them.
+* @return Amount of addresses with the IP flag set
+*/
+SALIS_API uint32 sal_mem_get_ip_count(void);
+
+/** Get amount of addresses with the memory-block-start flag set on them.
+* @return Amount of addresses with the memory-block-start flag set
+*/
+SALIS_API uint32 sal_mem_get_block_start_count(void);
+
+/** Get amount of addresses with the allocated flag set on them.
+* @return Amount of addresses with the allocated flag set
+*/
+SALIS_API uint32 sal_mem_get_allocated_count(void);
+
+/** Get memory capacity.
+* @return Memory capacity (capacity == size / 2)
+*/
+SALIS_API uint32 sal_mem_get_capacity(void);
+
+/** Get amount of addresses with a given instruction written on them.
+* @param inst Instruction whose amount we want to count
+* @return Amount of addresses with given instruction
+*/
+SALIS_API uint32 sal_mem_get_inst_count(uint8 inst);
+
+/** Determine if memory is above its capacity.
+* @return Memory is above capacity
+*/
+SALIS_API boolean sal_mem_is_over_capacity(void);
+
+/** Check validity of address.
+* @param address Address being queried
+* @return Validity of address (validity == address < size)
+*/
+SALIS_API boolean sal_mem_is_address_valid(uint32 address);
+
+/** Check if given address has the IP flag set.
+* @param address Address being queried
+* @return IP flag is set on this address
+*/
+SALIS_API boolean sal_mem_is_ip(uint32 address);
+
+/** Check if given address has the memory-block-start flag set.
+* @param address Address being queried
+* @return Memory-block-start flag is set on this address
+*/
+SALIS_API boolean sal_mem_is_block_start(uint32 address);
+
+/** Check if given address has the allocated flag set.
+* @param address Address being queried
+* @return Allocated flag is set on this address
+*/
+SALIS_API boolean sal_mem_is_allocated(uint32 address);
+
+void _sal_mem_set_ip(uint32 address);
+void _sal_mem_set_block_start(uint32 address);
+void _sal_mem_set_allocated(uint32 address);
+void _sal_mem_unset_ip(uint32 address);
+void _sal_mem_unset_block_start(uint32 address);
+void _sal_mem_unset_allocated(uint32 address);
+
+/** Get currently set flags at given address.
+* @param address Address being queried
+* @return Byte containing set flag bits
+*/
+SALIS_API uint8 sal_mem_get_flags(uint32 address);
+
+/** Get current instruction at address.
+* @param address Address being queried
+* @return Instruction currently written at address
+*/
+SALIS_API uint8 sal_mem_get_inst(uint32 address);
+
+/** Write instruction into address.
+* @param address Address being set
+* @param inst Instruction to write at given address
+*/
+SALIS_API void sal_mem_set_inst(uint32 address, uint8 inst);
+
+/** Get current byte at address.
+* @param address Address being queried
+* @return Byte currently written at address (includes bit flags & instruction)
+*/
+SALIS_API uint8 sal_mem_get_byte(uint32 address);
+
+/** Render a 1D image of a given block of memory. This is useful, as rendering
+* directly in python would be too slow. We use openmp for multi-threaded image
+* generation.
+*
+* @param origin Low bound of rendered image
+* @param cell_size Amount of bytes per rendered pixel (cell)
+* @param buff_size Amount of pixels (cells) to be generated
+* @param buffer Pre-allocated buffer to store the rendered pixels into
+*/
+SALIS_API void sal_mem_render_image(
+ uint32 origin, uint32 cell_size, uint32 buff_size, uint8_p buffer
+);
+
+void _sal_mem_cycle(void);
+
+#endif
diff --git a/include/process.h b/include/process.h
new file mode 100644
index 0000000..3723ad6
--- /dev/null
+++ b/include/process.h
@@ -0,0 +1,97 @@
+/**
+* @file process.h
+* @author Paul Oliver
+*
+* This module allows access to Salis processes, or procs. Procs are the actual
+* organisms in the simulation. They consist of a virtual CPU with 4 registers
+* and a stack of 8. The instruction pointer (IP) and seeker pointer (SP)
+* coordinate the execution of all instructions. Organisms get rewarded or
+* punished, depending on certain conditions.
+*/
+
+#ifndef SALIS_PROCESS_H
+#define SALIS_PROCESS_H
+
+/** The Process data-structure. The 'SALIS_PROC_ELEMENT' macro helps python
+* parse the struct, so don't change it!
+*/
+struct Process
+{
+ SALIS_PROC_ELEMENT uint32 mb1a;
+ SALIS_PROC_ELEMENT uint32 mb1s;
+ SALIS_PROC_ELEMENT uint32 mb2a;
+ SALIS_PROC_ELEMENT uint32 mb2s;
+ SALIS_PROC_ELEMENT uint32 reward;
+ SALIS_PROC_ELEMENT uint32 punish;
+ SALIS_PROC_ELEMENT uint32 ip;
+ SALIS_PROC_ELEMENT uint32 sp;
+ SALIS_PROC_ELEMENT uint32 rax;
+ SALIS_PROC_ELEMENT uint32 rbx;
+ SALIS_PROC_ELEMENT uint32 rcx;
+ SALIS_PROC_ELEMENT uint32 rdx;
+ SALIS_PROC_ELEMENT uint32 stack[8];
+};
+
+typedef struct Process Process;
+
+void _sal_proc_init(void);
+void _sal_proc_quit(void);
+void _sal_proc_load_from(FILE *file);
+void _sal_proc_save_into(FILE *file);
+
+/** Get process count.
+* @return Amount of running (living) processes
+*/
+SALIS_API uint32 sal_proc_get_count(void);
+
+/** Get reaper queue capacity.
+* @return Currently allocated size of reaper queue
+*/
+SALIS_API uint32 sal_proc_get_capacity(void);
+
+/** Get first process.
+* @return Process currently on top of reaper queue
+*/
+SALIS_API uint32 sal_proc_get_first(void);
+
+/** Get last process.
+* @return Process currently on bottom of reaper queue (closest to death)
+*/
+SALIS_API uint32 sal_proc_get_last(void);
+
+/** Get instructions executed on last cycle.
+* @return Amount of executed instructions during the last cycle
+*/
+SALIS_API uint32 sal_proc_get_instructions_executed(void);
+
+/** Check if process is currently free.
+* @param proc_id ID of process whose status we want to check
+* @return Status (either free or running) of the process with the given ID
+*/
+SALIS_API boolean sal_proc_is_free(uint32 proc_id);
+
+/** Get process.
+* @param proc_id ID of Process being queried
+* @return A copy of the process with the given ID
+*/
+SALIS_API Process sal_proc_get_proc(uint32 proc_id);
+
+/** Get process data.
+* @param proc_id ID of Process being queried
+* @param buffer Pre-allocated buffer to store data on [ > sizeof(Process)]
+*/
+SALIS_API void sal_proc_get_proc_data(uint32 proc_id, uint32_p buffer);
+
+/** Create new process.
+* @param address Address we want to allocate our process into
+* @param mb1_size Size of the memory block we want to allocate for our process
+*/
+SALIS_API void sal_proc_create(uint32 address, uint32 mb1_size);
+
+/** Kill process on bottom of reaper queue.
+*/
+SALIS_API void sal_proc_kill(void);
+
+void _sal_proc_cycle(void);
+
+#endif
diff --git a/include/salis.h b/include/salis.h
new file mode 100644
index 0000000..8b261b1
--- /dev/null
+++ b/include/salis.h
@@ -0,0 +1,67 @@
+/**
+* @file salis.h
+* @author Paul Oliver
+*
+* Main header file for the Salis library. Loading this header imports all API
+* modules and functions. It may be loaded from C or C++.
+*/
+
+#ifndef SALIS_H
+#define SALIS_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#include <types.h>
+#include <instset.h>
+#include <memory.h>
+#include <evolver.h>
+#include <common.h>
+#include <process.h>
+
+/** Initialize Salis simulation.
+* @param order Order of memory (memory_size == 1 << order)
+* @param pipe Desired path and file name of common pipe
+*/
+SALIS_API void sal_main_init(uint32 order, string pipe);
+
+/** Free resources and quit Salis.
+*/
+SALIS_API void sal_main_quit(void);
+
+/** Load existing Salis simulation from saved file.
+* @param file_name Path of the save file to be loaded
+* @param pipe Desired path and file name of common pipe
+*/
+SALIS_API void sal_main_load(string file_name, string pipe);
+
+/** Save Salis simulation to a file.
+* @param file_name Path of the save file to be created
+*/
+SALIS_API void sal_main_save(string file_name);
+
+/** Check if Salis simulation has been correctly initialized.
+* @return Salis has been correctly initialized
+*/
+SALIS_API boolean sal_main_is_init(void);
+
+/** Get current simulation cycle.
+* @return Current simulation cycle
+*/
+SALIS_API uint32 sal_main_get_cycle(void);
+
+/** Get current simulation epoch.
+* @return Current simulation epoch (1 epoch == 2^32 cycles)
+*/
+SALIS_API uint32 sal_main_get_epoch(void);
+
+/** Update simulation once. This will cycle all Salis modules and processes.
+*/
+SALIS_API void sal_main_cycle(void);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/include/types.h b/include/types.h
new file mode 100644
index 0000000..3ae418f
--- /dev/null
+++ b/include/types.h
@@ -0,0 +1,45 @@
+/**
+* @file types.h
+* @author Paul Oliver
+*
+* Declare main typedefs for the Salis library. Salis depends on fixed-width
+* unsigned types being available. We use the limits header to define these in
+* a C89 compliant way. Also, we typedef respective pointer types and a string
+* type to aid in header parsing.
+*/
+
+#ifndef SALIS_TYPES_H
+#define SALIS_TYPES_H
+
+#include <limits.h>
+
+#define UINT8_MAX 0xff
+#define UINT32_MAX 0xffffffff
+
+#if UCHAR_MAX == UINT8_MAX
+ typedef unsigned char uint8;
+ typedef unsigned char *uint8_p;
+#else
+ #error "Cannot define uint8/uint8_p types!"
+#endif
+
+#if ULONG_MAX == UINT32_MAX
+ typedef unsigned long int uint32;
+ typedef unsigned long int *uint32_p;
+#elif UINT_MAX == UINT32_MAX
+ typedef unsigned int uint32;
+ typedef unsigned int *uint32_p;
+#elif USHRT_MAX == UINT32_MAX
+ typedef unsigned short int uint32;
+ typedef unsigned short int *uint32_p;
+#else
+ #error "Cannot define uint32/uint32_p types!"
+#endif
+
+typedef int boolean;
+typedef const char *string;
+
+#define TRUE 1
+#define FALSE 0
+
+#endif