diff options
-rw-r--r-- | include/lib94/lib94.hpp | 62 | ||||
-rw-r--r-- | lib94/core.cpp | 13 |
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; } |