From ca118555214a176728b9aab87849391344306d6d Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Thu, 29 Feb 2024 02:29:13 +0100 Subject: Initial commit. --- include/common.h | 19 ++++++++ include/evolver.h | 38 ++++++++++++++++ include/getter.h | 20 ++++++++ include/instset.h | 71 +++++++++++++++++++++++++++++ include/memory.h | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/process.h | 97 +++++++++++++++++++++++++++++++++++++++ include/salis.h | 67 +++++++++++++++++++++++++++ include/types.h | 45 ++++++++++++++++++ 8 files changed, 491 insertions(+) create mode 100644 include/common.h create mode 100644 include/evolver.h create mode 100644 include/getter.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 (limited to 'include') 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 +#include +#include +#include +#include +#include + +/** 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 + +#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 -- cgit v1.2.1