summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/lib94/lib94.hpp62
-rw-r--r--lib94/core.cpp13
2 files changed, 54 insertions, 21 deletions
diff --git a/include/lib94/lib94.hpp b/include/lib94/lib94.hpp
index b8da8a8..86732de 100644
--- a/include/lib94/lib94.hpp
+++ b/include/lib94/lib94.hpp
@@ -14,6 +14,10 @@
namespace lib94 {
+ //this type is used to represent values inside the core.
+ //it has to be at least big enough to represent LIB94_CORE_SIZE squared,
+ //since it is used as an intermediate for all of the instructions,
+ //including in particular MUL.
typedef int_least32_t number_t;
enum opcode : uint8_t {
@@ -48,46 +52,86 @@ namespace lib94 {
std::string name;
std::string author;
+ //the org of the warrior, as an index into the instructions vector
number_t org;
+ //the instructions of the warrior at the start of a round
std::vector<instruction> instructions;
};
+ //this seeds the prng used to place warriors into the core at the start of a round
+ //if this is never called, 0 is used as a seed and the placements are deterministic.
void seed_prng(uint_fast64_t seed);
+ //converts an instruction to a string representation like
+ // dat.f $0, $0
std::string instruction_to_string(const instruction &instr);
+ //this exception is thrown by any error that occurs in the compile_warrior function,
+ //including a malformed instruction, a non-existent label being referenced, a division
+ //division by zero in a compile-time expression, a failed ;assert comment, etc.
struct compiler_exception : public std::exception {
unsigned source_line_number;
std::string message;
};
+ //takes a string with the full source code of a warrior. on success, returns a pointer
+ //to a warrior struct representing the compiled warrior (which you must delete when
+ //you are done with it). on failure, throws a compiler_exception as defined above.
warrior *compile_warrior(std::string source);
+ //fills the core with the supplied instruction; does not effect the address sets.
void clear_core(const instruction &background);
- void clear_core_random();
- //warrior pointers need to remain valid for other
- //functions to return valid things during the round
- bool init_round(const warrior *const *warriors, size_t count);
+ //fills the core with random instructions chosen from a uniform distribution;
+ //does not effect the address sets.
+ void clear_core_random();
+ //clears the address sets, places the supplied warriors into the core, and starts one
+ //process for each supplied warrior at its org. the count parameter specifies the
+ //number of warriors in the array. note that, if the supplied warriors cannot all be
+ //fit into the core, this will never return. the each of the warriors in the array
+ //must not be deleted for the duration of the round, but the array itself may be
+ //deleted after this function returns. note that this function does not clear the core
+ //before placing the warriors, so you may want to call clear_core or clear_core_random
+ //before calling this. note also that you probably want to call seed_prng, for example
+ //with the current time, before the first time you call this.
+ void init_round(const warrior *const *warriors, size_t count);
+
+ //returns the number of warriors who have at least one process
size_t alive_warrior_count();
- //asserts that there is a next warrior
+ //returns a pointer to the warrior whose turn it will be during the next call
+ //to single_step. makes an assertion that there is such a warrior, so check
+ //alive_warrior_count first if you are not sure.
const warrior *get_next_warrior();
+
+ //gets the program counter queue for the specified warrior. fails an assertion if this
+ //pointer was not one of the pointers in the array passed to init_round. it's okay if
+ //this warrior has no processes left; in that case this returns an empty deque.
const std::deque<number_t> &get_processes(const warrior *for_warrior);
- //asserts that there is a next process
- number_t get_next_process(const warrior *for_warrior);
+ //the addresses written to since the last call to init_round or clear_address_sets
const std::set<number_t> &get_written_addresses();
+
+ //the addresses read from since the last call to init_round or clear_address_sets
const std::set<number_t> &get_read_addresses();
+
+ //the addresses executed since the last call to init_round or clear_address_sets
const std::set<number_t> &get_executed_addresses();
+
void clear_address_sets();
+ //the instruction at the specified address of the core
const instruction &get_instruction(number_t address);
- //assumes that there is a next warrior
- //returns a warrior if it dies
+ //does one step of the simulation. assumes that there is at least one warrior with at
+ //least one alive process, so check alive_warrior_count first if you aren't sure.
+ //if the warrior whose turn it is dies during this step, the pointer to that warrior
+ //is returned. otherwise, a null pointer is returned. the update_address_sets template
+ //parameter controls whether to update the sets returned by get_written_addresses,
+ //get_read_addresses, and get_executed_addresses. this is provided so that you can
+ //improve the performance of the simulation if you do not need that information.
template <bool update_address_sets>
const warrior *single_step();
diff --git a/lib94/core.cpp b/lib94/core.cpp
index db1a911..1c09b3f 100644
--- a/lib94/core.cpp
+++ b/lib94/core.cpp
@@ -55,7 +55,7 @@ namespace lib94 {
static std::vector<warrior_info> warrior_infos;
std::deque<warrior_info *> alive_warriors;
- bool init_round(const warrior *const *warriors, size_t count) {
+ void init_round(const warrior *const *warriors, size_t count) {
clear_address_sets();
warrior_infos.clear();
alive_warriors = std::deque<warrior_info *>();
@@ -93,8 +93,6 @@ namespace lib94 {
for (warrior_info &wi : warrior_infos)
alive_warriors.push_back(&wi);
-
- return true;
}
size_t alive_warrior_count() {
@@ -113,15 +111,6 @@ namespace lib94 {
assert(false);
}
- number_t get_next_process(const warrior *for_warrior) {
- for (const warrior_info &wi : warrior_infos)
- if (wi.the_warrior == for_warrior) {
- assert(wi.processes.size() > 0);
- return wi.processes.front();
- }
- assert(false);
- }
-
const std::set<number_t> &get_written_addresses() {
return written_addresses;
}