summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--bench/bench_window.cpp324
-rw-r--r--bench/bench_window.hpp112
-rw-r--r--bench/core_widget.cpp162
-rw-r--r--bench/core_widget.hpp46
-rw-r--r--bench/main.cpp31
-rw-r--r--bench/main.hpp16
-rw-r--r--include/lib94/lib94.hpp122
-rw-r--r--lib94/core.cpp494
-rw-r--r--lib94/executors.cpp573
-rw-r--r--lib94/warrior.cpp775
-rw-r--r--makefile23
-rw-r--r--test/build.sh1
-rw-r--r--test/dwarf.rc10
-rw-r--r--test/imp.rc4
-rw-r--r--test/test.cpp85
-rw-r--r--warriors/big-nothing.red20
-rw-r--r--warriors/dwarf.red6
-rw-r--r--warriors/imp.red4
-rw-r--r--warriors/splitter.red5
-rw-r--r--warriors/trap.red28
21 files changed, 1806 insertions, 1038 deletions
diff --git a/.gitignore b/.gitignore
index 5e3dd03..e68f0aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
.vscode/
-test/test
+obj/
+bin/
diff --git a/bench/bench_window.cpp b/bench/bench_window.cpp
new file mode 100644
index 0000000..bcdc12d
--- /dev/null
+++ b/bench/bench_window.cpp
@@ -0,0 +1,324 @@
+#include <fstream>
+
+#include "bench_window.hpp"
+#include "main.hpp"
+
+bench_window::bench_window()
+ : runner_stop_now(false),
+ runner_stop_on_death(true),
+ runner_stop_on_win(true),
+ runner_update_ui(true),
+ control_box(Gtk::Orientation::VERTICAL),
+ new_round_button("new round"),
+ single_step_button("single step"),
+ start_button("start"),
+ stop_button("stop"),
+ draw_each_step_toggle("draw each step"),
+ pause_on_death_toggle("pause on death"),
+ pause_on_win_toggle("pause on win"),
+ add_warrior_button("add warrior"),
+ remove_warrior_button("remove warrior"),
+ runner({}),
+ runner_active(false) {
+
+ warrior_list_store = Gtk::TreeStore::create(warrior_list_columns);
+ instructions_store = Gtk::TreeStore::create(instructions_columns);
+
+ warrior_list_view.set_model(warrior_list_store);
+ instructions_view.set_model(instructions_store);
+
+ warrior_list_view.get_selection()->set_mode(Gtk::SelectionMode::SINGLE);
+ instructions_view.get_selection()->set_mode(Gtk::SelectionMode::NONE);
+
+ warrior_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &bench_window::update_buttons));
+
+ warrior_list_view.append_column("name", warrior_list_columns.warrior_name);
+ warrior_list_view.append_column("#p", warrior_list_columns.processes);
+ warrior_list_view.append_column("pc", warrior_list_columns.next_pc);
+
+ instructions_view.append_column("", instructions_columns.address);
+ instructions_view.append_column("", instructions_columns.instruction);
+
+ for (lib94::number_t i = 0; i < LIB94_CORE_SIZE; ++i) {
+ auto row = instructions_store->append();
+ (*row)[instructions_columns.address] = i;
+ }
+
+ update_ui();
+
+ draw_each_step_toggle.activate();
+ pause_on_death_toggle.activate();
+ pause_on_win_toggle.activate();
+
+ new_round_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_new_round));
+ single_step_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_single_step));
+ start_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_start));
+ stop_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_stop));
+ add_warrior_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_add_warrior));
+ remove_warrior_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_remove_warrior));
+
+ draw_each_step_toggle.signal_toggled().connect(sigc::mem_fun(*this, &bench_window::on_toggle_draw_each_step));
+ pause_on_death_toggle.signal_toggled().connect(sigc::mem_fun(*this, &bench_window::on_toggle_pause_on_death));
+ pause_on_win_toggle.signal_toggled().connect(sigc::mem_fun(*this, &bench_window::on_toggle_pause_on_win));
+
+ control_box.append(new_round_button);
+ control_box.append(single_step_button);
+ control_box.append(start_button);
+ control_box.append(stop_button);
+ control_box.append(draw_each_step_toggle);
+ control_box.append(pause_on_death_toggle);
+ control_box.append(pause_on_win_toggle);
+ control_box.append(add_warrior_button);
+ control_box.append(remove_warrior_button);
+
+ warrior_list_scroll.set_child(warrior_list_view);
+ warrior_list_scroll.set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::AUTOMATIC);
+ warrior_list_scroll.set_vexpand();
+
+ control_box.append(warrior_list_scroll);
+
+ control_box.append(core_rate_label);
+ control_box.append(core_render_label);
+
+ instructions_scroll.set_child(instructions_view);
+ instructions_scroll.set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::AUTOMATIC);
+
+ control_box.set_size_request(200, -1);
+ core.set_expand();
+ instructions_scroll.set_size_request(200, -1);
+
+ main_box.append(control_box);
+ main_box.append(core);
+ main_box.append(instructions_scroll);
+
+ set_child(main_box);
+ set_title("lib94 bench");
+
+ runner_update_ui_dispatcher.connect(sigc::mem_fun(*this, &bench_window::update_ui));
+ runner_stopping_dispatcher.connect(sigc::mem_fun(*this, &bench_window::on_runner_stopping));
+}
+
+void bench_window::add_modified_for_instruction_view(std::set<lib94::number_t> the_set) {
+ for (lib94::number_t n : the_set)
+ modified_addresses_for_instructions_view.insert(n);
+}
+
+const lib94::warrior *bench_window::do_step() {
+ auto time = std::chrono::system_clock::now();
+ last_core_step_distance = time - last_core_step_start;
+ last_core_step_start = time;
+
+ const lib94::warrior *result = lib94::single_step();
+
+ core.mut.lock();
+ core.age_all();
+ core.add_new_writes(lib94::get_written_addresses());
+ core.add_new_reads(lib94::get_read_addresses());
+ core.add_new_executions(lib94::get_executed_addresses());
+ core.mut.unlock();
+
+ add_modified_for_instruction_view(lib94::get_written_addresses());
+ add_modified_for_instruction_view(lib94::get_read_addresses());
+ add_modified_for_instruction_view(lib94::get_executed_addresses());
+
+ lib94::clear_address_sets();
+ return result;
+}
+
+void bench_window::runner_main() {
+ std::chrono::system_clock::time_point last_step = std::chrono::system_clock::now();
+
+ core_mutex.lock();
+ while (!runner_stop_now) {
+ bool death = do_step() != 0;
+
+ if (runner_stop_on_death && death)
+ break;
+ if (runner_stop_on_win && death && lib94::alive_warrior_count() == 1)
+ break;
+ if (runner_update_ui)
+ runner_update_ui_dispatcher.emit();
+
+ core_mutex.unlock();
+ std::this_thread::sleep_until(last_step + time_between_steps);
+ last_step = std::chrono::system_clock::now();
+ core_mutex.lock();
+ }
+
+ runner_stopping_dispatcher.emit();
+ core_mutex.unlock();
+}
+
+void bench_window::on_click_single_step() {
+ do_step();
+ update_ui();
+}
+
+void bench_window::on_click_new_round() {
+ lib94::clear_core({
+ .op = lib94::DAT,
+ .mod = lib94::F,
+ .amode = lib94::DIRECT,
+ .bmode = lib94::DIRECT,
+ .anumber = 0,
+ .bnumber = 0
+ });
+
+ lib94::init_round(warriors.data(), warriors.size());
+
+ core.mut.lock();
+ core.age_scale = std::pow(2.0 / 3.0, 1.0 / (float)warriors.size());
+ core.clear_all();
+ core.mut.unlock();
+
+ modified_addresses_for_instructions_view.clear();
+ for (lib94::number_t i = 0; i < LIB94_CORE_SIZE; ++i)
+ instructions_store->children()[i][instructions_columns.instruction]
+ = lib94::instruction_to_string(lib94::get_instruction(i));
+
+ update_ui();
+}
+
+void bench_window::on_click_start() {
+ runner_active = true;
+ update_ui();
+ runner_stop_now = false;
+ runner = std::thread(sigc::mem_fun(*this, &bench_window::runner_main));
+}
+
+void bench_window::on_click_stop() {
+ runner_stop_now = true;
+}
+
+void bench_window::on_toggle_draw_each_step() {
+ runner_update_ui = draw_each_step_toggle.get_active();
+}
+
+void bench_window::on_toggle_pause_on_death() {
+ runner_stop_on_death = pause_on_death_toggle.get_active();
+}
+
+void bench_window::on_toggle_pause_on_win() {
+ runner_stop_on_win = pause_on_win_toggle.get_active();
+}
+
+void bench_window::on_add_warrior_dialog_response(int response_id, Gtk::FileChooserDialog *dialog) {
+ if (response_id == Gtk::ResponseType::OK) {
+
+ Glib::RefPtr<Gio::File> file = dialog->get_file();
+ char *contents;
+ gsize length;
+ file->load_contents(contents, length);
+
+ auto w = lib94::compile_warrior(std::string(contents, length));
+
+ delete contents;
+
+ if (std::holds_alternative<lib94::warrior *>(w)) {
+ warriors.push_back(std::get<lib94::warrior *>(w));
+ on_click_new_round();
+ }
+
+ else {
+ Gtk::MessageDialog *md = new Gtk::MessageDialog(std::string("Failed to compile: ") + std::get<std::string>(w));
+ md->set_transient_for(*this);
+ md->set_modal();
+ md->signal_response().connect([md](int) {delete md;});
+ md->show();
+ }
+ }
+
+ delete dialog;
+}
+
+void bench_window::on_click_add_warrior() {
+ Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog("select a warrior");
+
+ dialog->set_transient_for(*this);
+ dialog->set_modal();
+ dialog->signal_response().connect(sigc::bind(sigc::mem_fun(*this, &bench_window::on_add_warrior_dialog_response), dialog));
+ dialog->add_button("add", Gtk::ResponseType::OK);
+ dialog->add_button("cancel", Gtk::ResponseType::CANCEL);
+
+ Glib::RefPtr<Gtk::FileFilter> text_filter = Gtk::FileFilter::create();
+ text_filter->add_mime_type("text/plain");
+ text_filter->set_name("text files");
+ dialog->add_filter(text_filter);
+
+ Glib::RefPtr<Gtk::FileFilter> any_filter = Gtk::FileFilter::create();
+ any_filter->add_pattern("*");
+ any_filter->set_name("all files");
+ dialog->add_filter(any_filter);
+
+ dialog->show();
+}
+
+void bench_window::on_click_remove_warrior() {
+ auto iter = warrior_list_view.get_selection()->get_selected();
+ if (iter) {
+
+ while (!(*iter)[warrior_list_columns.warrior])
+ iter = iter->parent();
+
+ const lib94::warrior *w = (*iter)[warrior_list_columns.warrior];
+
+ for (auto i = warriors.begin(); i != warriors.end(); ++i)
+ if (*i == w) {
+ warriors.erase(i);
+ break;
+ }
+
+ on_click_new_round();
+
+ delete w;
+
+ }
+}
+
+void bench_window::update_buttons() {
+ new_round_button.set_sensitive(!runner_active && warriors.size() > 0);
+ single_step_button.set_sensitive(!runner_active && lib94::alive_warrior_count() > 0);
+ start_button.set_sensitive(!runner_active && lib94::alive_warrior_count() > 0);
+ stop_button.set_sensitive(runner_active);
+ add_warrior_button.set_sensitive(!runner_active);
+ remove_warrior_button.set_sensitive(!runner_active && warrior_list_view.get_selection()->get_selected());
+}
+
+void bench_window::update_ui() {
+ if (runner_active)
+ core_mutex.lock();
+
+ update_buttons();
+
+ core_render_label.set_text("core render: " + ns_to_string(core.last_draw_time));
+ core_rate_label.set_text("core rate: " + hz_to_string(1000000000.0 / (double)last_core_step_distance.count()));
+
+ core.queue_draw();
+
+ warrior_list_store->clear();
+
+ for (const lib94::warrior *w : warriors) {
+ auto w_row = warrior_list_store->append();
+ (*w_row)[warrior_list_columns.warrior] = w;
+ (*w_row)[warrior_list_columns.warrior_name] = w->name;
+ auto procs = lib94::get_processes(w);
+ (*w_row)[warrior_list_columns.processes] = procs.size();
+ (*w_row)[warrior_list_columns.next_pc] = procs.size() ? std::to_string(procs[0]) : "";
+ }
+
+ //warrior_list_view.expand_all();
+
+ for (lib94::number_t i : modified_addresses_for_instructions_view)
+ instructions_store->children()[i][instructions_columns.instruction]
+ = lib94::instruction_to_string(lib94::get_instruction(i));
+ modified_addresses_for_instructions_view.clear();
+
+ if (runner_active)
+ core_mutex.unlock();
+}
+
+void bench_window::on_runner_stopping() {
+ runner.join();
+ runner_active = false;
+ update_ui();
+}
diff --git a/bench/bench_window.hpp b/bench/bench_window.hpp
new file mode 100644
index 0000000..86b2f1c
--- /dev/null
+++ b/bench/bench_window.hpp
@@ -0,0 +1,112 @@
+#ifndef LIB94_BENCH_BENCH_WINDOW_HPP
+#define LIB94_BENCH_BENCH_WINDOW_HPP
+
+#include <gtkmm.h>
+#include <thread>
+
+#include "core_widget.hpp"
+
+class bench_window : public Gtk::Window {
+public:
+ bench_window();
+
+ Glib::Dispatcher runner_update_ui_dispatcher;
+ Glib::Dispatcher runner_stopping_dispatcher;
+
+ bool runner_stop_now;
+ bool runner_stop_on_death;
+ bool runner_stop_on_win;
+ bool runner_update_ui;
+
+ core_widget core;
+
+private:
+ class warrior_list_columns_record : public Gtk::TreeModelColumnRecord {
+ public:
+ Gtk::TreeModelColumn<Glib::ustring> warrior_name;
+ Gtk::TreeModelColumn<size_t> processes;
+ Gtk::TreeModelColumn<Glib::ustring> next_pc;
+ Gtk::TreeModelColumn<const lib94::warrior *> warrior;
+
+ warrior_list_columns_record() {
+ add(warrior_name);
+ add(processes);
+ add(next_pc);
+ add(warrior);
+ }
+ };
+
+ class instructions_columns_record : public Gtk::TreeModelColumnRecord {
+ public:
+ Gtk::TreeModelColumn<lib94::number_t> address;
+ Gtk::TreeModelColumn<Glib::ustring> instruction;
+
+ instructions_columns_record() {
+ add(address);
+ add(instruction);
+ }
+ };
+
+ Gtk::ScrolledWindow warrior_list_scroll;
+ Gtk::ScrolledWindow instructions_scroll;
+
+ warrior_list_columns_record warrior_list_columns;
+ instructions_columns_record instructions_columns;
+
+ Glib::RefPtr<Gtk::TreeStore> warrior_list_store;
+ Glib::RefPtr<Gtk::TreeStore> instructions_store;
+
+ Gtk::TreeView warrior_list_view;
+ Gtk::TreeView instructions_view;
+
+ std::set<lib94::number_t> modified_addresses_for_instructions_view;
+
+ void add_modified_for_instruction_view(std::set<lib94::number_t> the_set);
+
+ Gtk::Box main_box;
+ Gtk::Box control_box;
+
+ Gtk::Button new_round_button;
+ Gtk::Button single_step_button;
+ Gtk::Button start_button;
+ Gtk::Button stop_button;
+
+ Gtk::CheckButton draw_each_step_toggle;
+ Gtk::CheckButton pause_on_death_toggle;
+ Gtk::CheckButton pause_on_win_toggle;
+
+ Gtk::Button add_warrior_button;
+ Gtk::Button remove_warrior_button;
+
+ std::chrono::system_clock::time_point last_core_step_start;
+ std::chrono::nanoseconds last_core_step_distance;
+ Gtk::Label core_rate_label;
+ Gtk::Label core_render_label;
+
+ const lib94::warrior *do_step();
+ void runner_main();
+
+ void on_click_new_round();
+ void on_click_single_step();
+ void on_click_start();
+ void on_click_stop();
+
+ void on_toggle_draw_each_step();
+ void on_toggle_pause_on_death();
+ void on_toggle_pause_on_win();
+
+ void on_click_add_warrior();
+ void on_click_remove_warrior();
+
+ std::thread runner;
+ bool runner_active;
+
+ void update_buttons();
+ void update_ui();
+
+ void on_runner_stopping();
+
+ void on_add_warrior_dialog_response(int response_id, Gtk::FileChooserDialog *dialog);
+};
+
+#endif
diff --git a/bench/core_widget.cpp b/bench/core_widget.cpp
new file mode 100644
index 0000000..b687a01
--- /dev/null
+++ b/bench/core_widget.cpp
@@ -0,0 +1,162 @@
+#include "core_widget.hpp"
+
+core_widget::core_widget() {
+ clear_all();
+}
+
+void core_widget::clear_all() {
+ for (int i = 0; i < LIB94_CORE_SIZE; ++i) {
+
+ if (write_values[i] != 0)
+ to_draw_write.insert(i);
+ write_values[i] = 0;
+
+ if (read_values[i] != 0)
+ to_draw_read.insert(i);
+ read_values[i] = 0;
+
+ if (execute_values[i] != 0)
+ to_draw_execute.insert(i);
+ execute_values[i] = 0;
+
+ }
+}
+
+void core_widget::age_all() {
+ for (int i = 0; i < LIB94_CORE_SIZE; ++i) {
+
+ if (write_values[i] != 0) {
+ to_draw_write.insert(i);
+ write_values[i] = (uint8_t)((float)write_values[i] * age_scale);
+ }
+
+ if (read_values[i] != 0) {
+ to_draw_read.insert(i);
+ read_values[i] = (uint8_t)((float)read_values[i] * age_scale);
+ }
+
+ if (execute_values[i] != 0) {
+ to_draw_execute.insert(i);
+ execute_values[i] = (uint8_t)((float)execute_values[i] * age_scale);
+ }
+
+ }
+}
+
+void core_widget::add_new_writes(const std::set<lib94::number_t> &writes) {
+ for (lib94::number_t n : writes) {
+ write_values[n] = 255;
+ to_draw_write.insert(n);
+ }
+}
+
+void core_widget::add_new_reads(const std::set<lib94::number_t> &reads) {
+ for (lib94::number_t n : reads) {
+ read_values[n] = 255;
+ to_draw_read.insert(n);
+ }
+}
+
+void core_widget::add_new_executions(const std::set<lib94::number_t> &executions) {
+ for (lib94::number_t n : executions) {
+ execute_values[n] = 255;
+ to_draw_execute.insert(n);
+ }
+}
+
+void core_widget::measure_vfunc(Gtk::Orientation, int for_size, int &minimum, int &natural, int &minimum_baseline, int &natural_baseline) const {
+ minimum = (LIB94_CORE_SIZE - 1) / for_size + 1;
+ natural = (LIB94_CORE_SIZE - 1) / for_size + 1;
+ minimum_baseline = -1;
+ natural_baseline = -1;
+}
+
+void core_widget::draw(int width, int height) {
+ mut.lock();
+
+ if (width == last_width && height == last_height) {
+
+ uint8_t *buffer = (uint8_t *)pixbuf->property_pixels().get_value();
+
+ for (lib94::number_t i : to_draw_write) {
+ int cy = i / cpr * scale + ypad;
+ int cx = i % cpr * scale + xpad;
+ for (int dy = 0; dy < scale; ++dy)
+ for (int dx = 0; dx < scale; ++dx)
+ buffer[(cy + dy) * pixbuf->property_rowstride() + (cx + dx) * 4] = write_values[i];
+ }
+
+ for (lib94::number_t i : to_draw_read) {
+ int cy = i / cpr * scale + ypad;
+ int cx = i % cpr * scale + xpad;
+ for (int dy = 0; dy < scale; ++dy)
+ for (int dx = 0; dx < scale; ++dx)
+ buffer[(cy + dy) * pixbuf->property_rowstride() + (cx + dx) * 4 + 2] = read_values[i];
+ }
+
+ for (lib94::number_t i : to_draw_execute) {
+ int cy = i / cpr * scale + ypad;
+ int cx = i % cpr * scale + xpad;
+ for (int dy = 0; dy < scale; ++dy)
+ for (int dx = 0; dx < scale; ++dx)
+ buffer[(cy + dy) * pixbuf->property_rowstride() + (cx + dx) * 4 + 1] = execute_values[i];
+ }
+
+ }
+
+ else {
+
+ pixbuf = Gdk::Pixbuf::create(Gdk::Colorspace::RGB, true, 8, width, height);
+ last_width = width;
+ last_height = height;
+
+ scale = 1;
+ while (true) {
+ ++scale;
+ if ((width / scale) * (height / scale) < LIB94_CORE_SIZE) {
+ --scale;
+ break;
+ }
+ }
+
+ cpr = width / scale;
+ xpad = (width % scale) / 2;
+ ypad = (height % scale) / 2;
+
+ uint8_t *buffer = (uint8_t *)pixbuf->property_pixels().get_value();
+
+ for (lib94::number_t i = 0; i < LIB94_CORE_SIZE; ++i) {
+ int cy = i / cpr * scale + ypad;
+ int cx = i % cpr * scale + xpad;
+ for (int dy = 0; dy < scale; ++dy)
+ for (int dx = 0; dx < scale; ++dx) {
+ buffer[(cy + dy) * pixbuf->property_rowstride() + (cx + dx) * 4] = write_values[i];
+ buffer[(cy + dy) * pixbuf->property_rowstride() + (cx + dx) * 4 + 2] = read_values[i];
+ buffer[(cy + dy) * pixbuf->property_rowstride() + (cx + dx) * 4 + 1] = execute_values[i];
+ buffer[(cy + dy) * pixbuf->property_rowstride() + (cx + dx) * 4 + 3] = 255;
+ }
+ }
+
+ }
+
+ to_draw_write.clear();
+ to_draw_read.clear();
+ to_draw_execute.clear();
+
+ mut.unlock();
+}
+
+void core_widget::snapshot_vfunc(const Glib::RefPtr<Gtk::Snapshot> &snapshot) {
+ auto start_time = std::chrono::system_clock::now();
+
+ Gtk::Allocation allocation = get_allocation();
+ Gdk::Rectangle allocation_rect(0, 0, allocation.get_width(), allocation.get_height());
+
+ draw(allocation.get_width(), allocation.get_height());
+
+ auto texture = Gdk::Texture::create_for_pixbuf(pixbuf);
+ snapshot->append_texture(texture, allocation_rect);
+
+ auto end_time = std::chrono::system_clock::now();
+ last_draw_time = end_time - start_time;
+}
diff --git a/bench/core_widget.hpp b/bench/core_widget.hpp
new file mode 100644
index 0000000..78797c9
--- /dev/null
+++ b/bench/core_widget.hpp
@@ -0,0 +1,46 @@
+#ifndef LIB94_BENCH_CORE_WIDGET_HPP
+#define LIB94_BENCH_CORE_WIDGET_HPP
+
+#include <lib94/lib94.hpp>
+#include <gtkmm.h>
+
+class core_widget : public Gtk::Widget {
+public:
+ core_widget();
+ void clear_all();
+ void age_all();
+ void add_new_writes(const std::set<lib94::number_t> &writes);
+ void add_new_reads(const std::set<lib94::number_t> &reads);
+ void add_new_executions(const std::set<lib94::number_t> &executions);
+
+ void measure_vfunc(Gtk::Orientation orientation, int for_size, int &minimum, int &natural, int &minimum_baseline, int &natural_baseline) const override;
+ void snapshot_vfunc(const Glib::RefPtr<Gtk::Snapshot> &snapshot);
+
+ float age_scale;
+
+ std::chrono::nanoseconds last_draw_time;
+
+ std::mutex mut;
+
+private:
+ uint8_t write_values[LIB94_CORE_SIZE];
+ uint8_t read_values[LIB94_CORE_SIZE];
+ uint8_t execute_values[LIB94_CORE_SIZE];
+
+ std::set<lib94::number_t> to_draw_write;
+ std::set<lib94::number_t> to_draw_read;
+ std::set<lib94::number_t> to_draw_execute;
+
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf;
+
+ int last_width;
+ int last_height;
+ int scale;
+ int cpr;
+ int xpad;
+ int ypad;
+
+ void draw(int width, int height);
+};
+
+#endif
diff --git a/bench/main.cpp b/bench/main.cpp
new file mode 100644
index 0000000..9663650
--- /dev/null
+++ b/bench/main.cpp
@@ -0,0 +1,31 @@
+#include "bench_window.hpp"
+#include "main.hpp"
+
+std::vector<lib94::warrior *> warriors;
+std::chrono::milliseconds time_between_steps(50);
+std::mutex core_mutex;
+
+int main(int argc, char **argv) {
+ auto app = Gtk::Application::create("net.benjidial.lib94.bench");
+ return app->make_window_and_run<bench_window>(argc, argv);
+}
+
+std::string ns_to_string(std::chrono::nanoseconds dur) {
+ if (dur.count() >= 10000000000)
+ return std::to_string((dur.count() + 500000000) / 1000000000) + "s";
+ if (dur.count() >= 10000000)
+ return std::to_string((dur.count() + 500000) / 1000000) + "ms";
+ if (dur.count() >= 10000)
+ return std::to_string((dur.count() + 500) / 1000) + "μs";
+ return std::to_string(dur.count()) + "ns";
+}
+
+std::string hz_to_string(double rate) {
+ if (rate >= 10000000.0)
+ return std::to_string((int)std::round(rate / 1000000.0)) + "MHz";
+ if (rate >= 10000.0)
+ return std::to_string((int)std::round(rate / 1000.0)) + "kHz";
+ if (rate >= 10.0)
+ return std::to_string((int)std::round(rate)) + "Hz";
+ return std::to_string((int)std::round(rate * 1000.0)) + "mHz";
+}
diff --git a/bench/main.hpp b/bench/main.hpp
new file mode 100644
index 0000000..ee0e1e3
--- /dev/null
+++ b/bench/main.hpp
@@ -0,0 +1,16 @@
+#ifndef LIB94_BENCH_MAIN_HPP
+#define LIB94_BENCH_MAIN_HPP
+
+#include <lib94/lib94.hpp>
+#include <chrono>
+#include <vector>
+#include <mutex>
+
+extern std::vector<lib94::warrior *> warriors;
+extern std::chrono::milliseconds time_between_steps;
+extern std::mutex core_mutex;
+
+std::string ns_to_string(std::chrono::nanoseconds dur);
+std::string hz_to_string(double rate);
+
+#endif
diff --git a/include/lib94/lib94.hpp b/include/lib94/lib94.hpp
index 1d061ec..3ca0c7b 100644
--- a/include/lib94/lib94.hpp
+++ b/include/lib94/lib94.hpp
@@ -1,5 +1,5 @@
-#ifndef LIB94_HPP
-#define LIB94_HPP
+#ifndef LIB94_LIB94_HPP
+#define LIB94_LIB94_HPP
#include <filesystem>
#include <optional>
@@ -7,82 +7,88 @@
#include <string>
#include <random>
#include <vector>
-#include <queue>
+#include <deque>
#include <set>
-#ifndef CORE_SIZE
-#define CORE_SIZE 8000
+#ifndef LIB94_CORE_SIZE
+#define LIB94_CORE_SIZE 8000
#endif
-typedef uint32_t number_t;
+namespace lib94 {
-enum opcode : uint8_t {
- DAT, MOV, ADD, SUB,
- MUL, DIV, MOD, JMP,
- JMZ, JMN, DJN, SEQ,
- SNE, SLT, SPL, NOP
-};
+ typedef int_least32_t number_t;
-enum modifier : uint8_t {
- A, B, AB, BA,
- F, X, I
-};
+ enum opcode : uint8_t {
+ DAT, MOV, ADD, SUB,
+ MUL, DIV, MOD, JMP,
+ JMZ, JMN, DJN, SEQ,
+ SNE, SLT, SPL, NOP
+ };
-enum mode : uint8_t {
- IMMEDIATE, DIRECT,
- A_INDIRECT, B_INDIRECT,
- A_DECREMENT, B_DECREMENT,
- A_INCREMENT, B_INCREMENT
-};
+ enum modifier : uint8_t {
+ A, B, AB, BA,
+ F, X, I
+ };
-struct instruction {
- opcode op;
- modifier mod;
- mode amode;
- mode bmode;
- number_t anumber;
- number_t bnumber;
-};
+ enum mode : uint8_t {
+ IMMEDIATE, DIRECT,
+ A_INDIRECT, B_INDIRECT,
+ A_DECREMENT, B_DECREMENT,
+ A_INCREMENT, B_INCREMENT
+ };
-struct warrior {
- std::string name;
- std::string author;
+ struct instruction {
+ opcode op;
+ modifier mod;
+ mode amode;
+ mode bmode;
+ number_t anumber;
+ number_t bnumber;
+ };
- number_t org;
+ struct warrior {
+ std::string name;
+ std::string author;
- std::vector<instruction> instructions;
-};
+ number_t org;
-void seed_prng(uint_fast64_t seed);
+ std::vector<instruction> instructions;
+ };
-std::variant<warrior *, std::string> compile_warrior(std::string source);
-bool save_warrior(const warrior &w, const std::filesystem::path &to);
-std::optional<warrior *> load_warrior(const std::filesystem::path &from);
+ void seed_prng(uint_fast64_t seed);
-void clear_core(const instruction &background);
-void clear_core_random();
+ std::string instruction_to_string(const instruction &instr);
-//warrior pointers need to remain valid for other
-//functions to return valid things during the round
-bool init_round(const std::vector<const warrior *> &warriors);
+ std::variant<warrior *, std::string> compile_warrior(std::string source);
+ bool save_warrior(const warrior &w, const std::filesystem::path &to);
+ std::optional<warrior *> load_warrior(const std::filesystem::path &from);
-size_t alive_warrior_count();
+ void clear_core(const instruction &background);
+ void clear_core_random();
-//asserts that there is a next warrior
-const warrior *get_next_warrior();
-const std::queue<number_t> &get_processes(const warrior *for_warrior);
-//asserts that there is a next process
-number_t get_next_process(const warrior *for_warrior);
+ //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);
-const std::set<number_t> &get_modified_addresses();
-void clear_modified_addresses();
+ size_t alive_warrior_count();
-const instruction &get_instruction(number_t address);
+ //asserts that there is a next warrior
+ const warrior *get_next_warrior();
+ 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);
-//asserts that there is a next warrior
-void single_step();
+ const std::set<number_t> &get_written_addresses();
+ const std::set<number_t> &get_read_addresses();
+ const std::set<number_t> &get_executed_addresses();
+ void clear_address_sets();
-const warrior *run_until_warrior_death();
-const warrior *run_until_winner();
+ const instruction &get_instruction(number_t address);
+
+ //assumes that there is a next warrior
+ //returns a warrior if it dies
+ const warrior *single_step();
+
+}
#endif
diff --git a/lib94/core.cpp b/lib94/core.cpp
index 4cbd098..d0e81e1 100644
--- a/lib94/core.cpp
+++ b/lib94/core.cpp
@@ -2,293 +2,303 @@
#include <functional>
#include <cassert>
#include <random>
-#include <queue>
+#include <deque>
#include <set>
-static std::mt19937_64 prng;
-instruction core[CORE_SIZE];
+namespace lib94 {
-void seed_prng(uint_fast64_t seed) {
- prng.seed(seed);
-}
+ static std::mt19937_64 prng;
+ instruction core[LIB94_CORE_SIZE];
-void clear_core(const instruction &background) {
- for (instruction &i : core)
- i = background;
-}
+ void seed_prng(uint_fast64_t seed) {
+ prng.seed(seed);
+ }
-void clear_core_random() {
- std::uniform_int_distribution<uint8_t> op(0, 15);
- std::uniform_int_distribution<uint8_t> mod(0, 6);
- std::uniform_int_distribution<uint8_t> modes(0, 7);
- std::uniform_int_distribution<number_t> number(0, CORE_SIZE - 1);
-
- for (instruction &i : core)
- i = {
- .op = (opcode)op(prng),
- .mod = (modifier)mod(prng),
- .amode = (mode)modes(prng),
- .bmode = (mode)modes(prng),
- .anumber = number(prng),
- .bnumber = number(prng)
- };
-}
+ void clear_core(const instruction &background) {
+ for (instruction &i : core)
+ i = background;
+ }
-static std::set<number_t> modified_addresses;
+ void clear_core_random() {
+ std::uniform_int_distribution<uint8_t> op(0, 15);
+ std::uniform_int_distribution<uint8_t> mod(0, 6);
+ std::uniform_int_distribution<uint8_t> modes(0, 7);
+ std::uniform_int_distribution<number_t> number(0, LIB94_CORE_SIZE - 1);
+
+ for (instruction &i : core)
+ i = {
+ .op = (opcode)op(prng),
+ .mod = (modifier)mod(prng),
+ .amode = (mode)modes(prng),
+ .bmode = (mode)modes(prng),
+ .anumber = number(prng),
+ .bnumber = number(prng)
+ };
+ }
-struct warrior_info {
- const warrior *the_warrior;
- std::queue<number_t> processes;
-};
+ static std::set<number_t> written_addresses;
+ static std::set<number_t> read_addresses;
+ static std::set<number_t> executed_addresses;
-static std::vector<warrior_info> warrior_infos;
-std::queue<warrior_info *> alive_warriors;
+ void add_written_instruction(const instruction *instr) {
+ written_addresses.insert(instr - core);
+ }
-bool init_round(const std::vector<const warrior *> &warriors) {
- modified_addresses.clear();
- warrior_infos.clear();
- alive_warriors = std::queue<warrior_info *>();
+ void add_read_instruction(const instruction *instr) {
+ read_addresses.insert(instr - core);
+ }
- std::uniform_int_distribution<number_t> number(0, CORE_SIZE - 1);
- std::vector<std::pair<number_t, number_t>> placements;
+ void add_executed_instruction(const instruction *instr) {
+ executed_addresses.insert(instr - core);
+ }
- for (const warrior *w : warriors) {
- unsigned tries = 0;
+ struct warrior_info {
+ const warrior *the_warrior;
+ std::deque<number_t> processes;
+ };
- new_place_at:
- if (tries > 1000)
- return false;
- ++tries;
+ static std::vector<warrior_info> warrior_infos;
+ std::deque<warrior_info *> alive_warriors;
- number_t place_at = number(prng);
+ bool init_round(const warrior *const *warriors, size_t count) {
+ clear_address_sets();
+ warrior_infos.clear();
+ alive_warriors = std::deque<warrior_info *>();
- for (std::pair<number_t, number_t> &other : placements)
- //there has to be a better way
- for (number_t i = 0; i < w->instructions.size(); ++i)
- if (((place_at + i) % CORE_SIZE >= other.first && (place_at + i) % CORE_SIZE < other.first + other.second) ||
- ((place_at + i) % CORE_SIZE + CORE_SIZE >= other.first && (place_at + i) % CORE_SIZE + CORE_SIZE < other.first + other.second))
- goto new_place_at;
+ std::uniform_int_distribution<number_t> number(0, LIB94_CORE_SIZE - 1);
+ std::vector<std::pair<number_t, number_t>> placements;
- placements.push_back(std::make_pair<>(place_at, w->instructions.size()));
+ for (size_t i = 0; i < count; ++i) {
+ const warrior *w = warriors[i];
+ unsigned tries = 0;
- for (number_t i = 0; i < w->instructions.size(); ++i)
- core[(place_at + i) % CORE_SIZE] = w->instructions[i];
+ new_place_at:
+ if (tries > 1000)
+ return false;
+ ++tries;
- warrior_infos.push_back({});
- warrior_info *wi = &warrior_infos.back();
- wi->the_warrior = w;
- wi->processes.push((place_at + w->org) % CORE_SIZE);
- }
+ number_t place_at = i == 0 ? 0 : number(prng);
- std::shuffle(warrior_infos.begin(), warrior_infos.end(), prng);
+ for (std::pair<number_t, number_t> &other : placements)
+ //there has to be a better way
+ for (number_t i = 0; i < (number_t)w->instructions.size(); ++i)
+ if (((place_at + i) % LIB94_CORE_SIZE >= other.first && (place_at + i) % LIB94_CORE_SIZE < other.first + other.second) ||
+ ((place_at + i) % LIB94_CORE_SIZE + LIB94_CORE_SIZE >= other.first && (place_at + i) % LIB94_CORE_SIZE + LIB94_CORE_SIZE < other.first + other.second))
+ goto new_place_at;
- for (warrior_info &wi : warrior_infos)
- alive_warriors.push(&wi);
+ placements.push_back(std::make_pair<>(place_at, w->instructions.size()));
- return true;
-}
+ for (number_t i = 0; i < (number_t)w->instructions.size(); ++i) {
+ core[(place_at + i) % LIB94_CORE_SIZE] = w->instructions[i];
+ add_written_instruction(core + (place_at + i) % LIB94_CORE_SIZE);
+ }
-size_t alive_warrior_count() {
- return alive_warriors.size();
-}
+ warrior_infos.push_back({});
+ warrior_info *wi = &warrior_infos.back();
+ wi->the_warrior = w;
+ wi->processes.push_back((place_at + w->org) % LIB94_CORE_SIZE);
+ }
-const warrior *get_next_warrior() {
- assert(alive_warriors.size() > 0);
- return alive_warriors.front()->the_warrior;
-}
+ std::shuffle(warrior_infos.begin(), warrior_infos.end(), prng);
-const std::queue<number_t> &get_processes(const warrior *for_warrior) {
- for (const warrior_info &wi : warrior_infos)
- if (wi.the_warrior == for_warrior)
- return wi.processes;
- assert(false);
-}
+ for (warrior_info &wi : warrior_infos)
+ alive_warriors.push_back(&wi);
-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);
-}
+ return true;
+ }
-const std::set<number_t> &get_modified_addresses() {
- return modified_addresses;
-}
+ size_t alive_warrior_count() {
+ return alive_warriors.size();
+ }
-void clear_modified_addresses() {
- modified_addresses.clear();
-}
+ const warrior *get_next_warrior() {
+ assert(alive_warriors.size() > 0);
+ return alive_warriors.front()->the_warrior;
+ }
-void add_modified_instruction(const instruction *instr) {
- modified_addresses.insert(instr - core);
-}
+ const std::deque<number_t> &get_processes(const warrior *for_warrior) {
+ for (const warrior_info &wi : warrior_infos)
+ if (wi.the_warrior == for_warrior)
+ return wi.processes;
+ assert(false);
+ }
-const instruction &get_instruction(number_t address) {
- return core[address];
-}
+ 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);
+ }
-static const warrior *single_step_return_dead();
+ const std::set<number_t> &get_written_addresses() {
+ return written_addresses;
+ }
-void single_step() {
- single_step_return_dead();
-}
+ const std::set<number_t> &get_read_addresses() {
+ return read_addresses;
+ }
-const warrior *run_until_warrior_death() {
- while (true) {
- const warrior *w = single_step_return_dead();
- if (w)
- return w;
+ const std::set<number_t> &get_executed_addresses() {
+ return executed_addresses;
}
-}
-const warrior *run_until_winner() {
- while (true) {
- const warrior *w = single_step_return_dead();
- if (w && alive_warriors.size() == 1)
- for (const warrior_info &wi : warrior_infos)
- if (wi.processes.size() > 0)
- return wi.the_warrior;
+ void clear_address_sets() {
+ written_addresses.clear();
+ read_addresses.clear();
+ executed_addresses.clear();
+ }
+
+ const instruction &get_instruction(number_t address) {
+ return core[address];
}
-}
-number_t program_counter;
-static instruction instruction_register;
-
-static number_t a_pointer, b_pointer;
-instruction a_instruction, b_instruction;
-
-instruction *a_instruction_writable;
-instruction *b_instruction_writable;
-
-static void evaluate_operand(mode m, number_t n, number_t &ptr, instruction &instr) {
- instruction *secondary;
-
- switch (m) {
-
- case IMMEDIATE:
- ptr = 0;
- instr = core[program_counter];
- break;
-
- case DIRECT:
- ptr = n;
- instr = core[(program_counter + n) % CORE_SIZE];
- break;
-
- case A_INDIRECT:
- secondary = core + (program_counter + n) % CORE_SIZE;
- ptr = (n + secondary->anumber) % CORE_SIZE;
- instr = core[(program_counter + ptr) % CORE_SIZE];
- break;
-
- case B_INDIRECT:
- secondary = core + (program_counter + n) % CORE_SIZE;
- ptr = (n + secondary->bnumber) % CORE_SIZE;
- instr = core[(program_counter + ptr) % CORE_SIZE];
- break;
-
- case A_DECREMENT:
- secondary = core + (program_counter + n) % CORE_SIZE;
- ptr = (n + --secondary->anumber) % CORE_SIZE;
- instr = core[(program_counter + ptr) % CORE_SIZE];
- add_modified_instruction(secondary);
- break;
-
- case B_DECREMENT:
- secondary = core + (program_counter + n) % CORE_SIZE;
- ptr = (n + --secondary->bnumber) % CORE_SIZE;
- instr = core[(program_counter + ptr) % CORE_SIZE];
- add_modified_instruction(secondary);
- break;
-
- case A_INCREMENT:
- secondary = core + (program_counter + n) % CORE_SIZE;
- ptr = (n + secondary->anumber) % CORE_SIZE;
- instr = core[(program_counter + ptr) % CORE_SIZE];
- ++secondary->anumber;
- add_modified_instruction(secondary);
- break;
-
- case B_INCREMENT:
- secondary = core + (program_counter + n) % CORE_SIZE;
- ptr = (n + secondary->bnumber) % CORE_SIZE;
- instr = core[(program_counter + ptr) % CORE_SIZE];
- ++secondary->bnumber;
- add_modified_instruction(secondary);
- break;
+ number_t program_counter;
+ static instruction instruction_register;
+
+ static number_t a_pointer, b_pointer;
+ instruction a_instruction, b_instruction;
+
+ instruction *a_instruction_writable;
+ instruction *b_instruction_writable;
+
+ static void evaluate_operand(mode m, number_t n, number_t &ptr, instruction &instr) {
+ instruction *secondary;
+
+ switch (m) {
+
+ case IMMEDIATE:
+ ptr = 0;
+ instr = core[program_counter];
+ break;
+
+ case DIRECT:
+ ptr = n;
+ instr = core[(program_counter + n) % LIB94_CORE_SIZE];
+ break;
+
+ case A_INDIRECT:
+ secondary = core + (program_counter + n) % LIB94_CORE_SIZE;
+ ptr = (n + secondary->anumber) % LIB94_CORE_SIZE;
+ instr = core[(program_counter + ptr) % LIB94_CORE_SIZE];
+ add_read_instruction(secondary);
+ break;
+
+ case B_INDIRECT:
+ secondary = core + (program_counter + n) % LIB94_CORE_SIZE;
+ ptr = (n + secondary->bnumber) % LIB94_CORE_SIZE;
+ instr = core[(program_counter + ptr) % LIB94_CORE_SIZE];
+ add_read_instruction(secondary);
+ break;
+
+ case A_DECREMENT:
+ secondary = core + (program_counter + n) % LIB94_CORE_SIZE;
+ ptr = (n + --secondary->anumber) % LIB94_CORE_SIZE;
+ instr = core[(program_counter + ptr) % LIB94_CORE_SIZE];
+ add_read_instruction(secondary);
+ add_written_instruction(secondary);
+ break;
+
+ case B_DECREMENT:
+ secondary = core + (program_counter + n) % LIB94_CORE_SIZE;
+ ptr = (n + --secondary->bnumber) % LIB94_CORE_SIZE;
+ instr = core[(program_counter + ptr) % LIB94_CORE_SIZE];
+ add_read_instruction(secondary);
+ add_written_instruction(secondary);
+ break;
+
+ case A_INCREMENT:
+ secondary = core + (program_counter + n) % LIB94_CORE_SIZE;
+ ptr = (n + secondary->anumber) % LIB94_CORE_SIZE;
+ instr = core[(program_counter + ptr) % LIB94_CORE_SIZE];
+ ++secondary->anumber;
+ add_read_instruction(secondary);
+ add_written_instruction(secondary);
+ break;
+
+ case B_INCREMENT:
+ secondary = core + (program_counter + n) % LIB94_CORE_SIZE;
+ ptr = (n + secondary->bnumber) % LIB94_CORE_SIZE;
+ instr = core[(program_counter + ptr) % LIB94_CORE_SIZE];
+ ++secondary->bnumber;
+ add_read_instruction(secondary);
+ add_written_instruction(secondary);
+ break;
+ }
}
-}
-template<opcode op, modifier mod>
-bool execute();
-
-#define EXECUTOR_DECL_LINE(mod) \
- extern template bool execute<DAT, mod>(); extern template bool execute<MOV, mod>(); \
- extern template bool execute<ADD, mod>(); extern template bool execute<SUB, mod>(); \
- extern template bool execute<MUL, mod>(); extern template bool execute<DIV, mod>(); \
- extern template bool execute<MOD, mod>(); extern template bool execute<JMP, mod>(); \
- extern template bool execute<JMZ, mod>(); extern template bool execute<JMN, mod>(); \
- extern template bool execute<DJN, mod>(); extern template bool execute<SEQ, mod>(); \
- extern template bool execute<SNE, mod>(); extern template bool execute<SLT, mod>(); \
- extern template bool execute<SPL, mod>(); extern template bool execute<NOP, mod>();
-
-EXECUTOR_DECL_LINE(A)
-EXECUTOR_DECL_LINE(B)
-EXECUTOR_DECL_LINE(AB)
-EXECUTOR_DECL_LINE(BA)
-EXECUTOR_DECL_LINE(F)
-EXECUTOR_DECL_LINE(X)
-EXECUTOR_DECL_LINE(I)
-
-#define EXECUTOR_REF_LINE(mod) \
- &execute<DAT, mod>, &execute<MOV, mod>, \
- &execute<ADD, mod>, &execute<SUB, mod>, \
- &execute<MUL, mod>, &execute<DIV, mod>, \
- &execute<MOD, mod>, &execute<JMP, mod>, \
- &execute<JMZ, mod>, &execute<JMN, mod>, \
- &execute<DJN, mod>, &execute<SEQ, mod>, \
- &execute<SNE, mod>, &execute<SLT, mod>, \
- &execute<SPL, mod>, &execute<NOP, mod>
-
-static const std::function<bool ()> executors[] = {
- EXECUTOR_REF_LINE(A), EXECUTOR_REF_LINE(B),
- EXECUTOR_REF_LINE(AB), EXECUTOR_REF_LINE(BA),
- EXECUTOR_REF_LINE(F), EXECUTOR_REF_LINE(X),
- EXECUTOR_REF_LINE(I)
-};
-
-static warrior_info *this_warrior;
-
-void enqueue_process(number_t pc) {
- this_warrior->processes.push(pc);
-}
+ template<opcode op, modifier mod>
+ bool execute();
+
+ #define EXECUTOR_DECL_LINE(mod) \
+ extern template bool execute<DAT, mod>(); extern template bool execute<MOV, mod>(); \
+ extern template bool execute<ADD, mod>(); extern template bool execute<SUB, mod>(); \
+ extern template bool execute<MUL, mod>(); extern template bool execute<DIV, mod>(); \
+ extern template bool execute<MOD, mod>(); extern template bool execute<JMP, mod>(); \
+ extern template bool execute<JMZ, mod>(); extern template bool execute<JMN, mod>(); \
+ extern template bool execute<DJN, mod>(); extern template bool execute<SEQ, mod>(); \
+ extern template bool execute<SNE, mod>(); extern template bool execute<SLT, mod>(); \
+ extern template bool execute<SPL, mod>(); extern template bool execute<NOP, mod>();
+
+ EXECUTOR_DECL_LINE(A)
+ EXECUTOR_DECL_LINE(B)
+ EXECUTOR_DECL_LINE(AB)
+ EXECUTOR_DECL_LINE(BA)
+ EXECUTOR_DECL_LINE(F)
+ EXECUTOR_DECL_LINE(X)
+ EXECUTOR_DECL_LINE(I)
+
+ #define EXECUTOR_REF_LINE(mod) \
+ &execute<DAT, mod>, &execute<MOV, mod>, \
+ &execute<ADD, mod>, &execute<SUB, mod>, \
+ &execute<MUL, mod>, &execute<DIV, mod>, \
+ &execute<MOD, mod>, &execute<JMP, mod>, \
+ &execute<JMZ, mod>, &execute<JMN, mod>, \
+ &execute<DJN, mod>, &execute<SEQ, mod>, \
+ &execute<SNE, mod>, &execute<SLT, mod>, \
+ &execute<SPL, mod>, &execute<NOP, mod>
+
+ static const std::function<bool ()> executors[] = {
+ EXECUTOR_REF_LINE(A), EXECUTOR_REF_LINE(B),
+ EXECUTOR_REF_LINE(AB), EXECUTOR_REF_LINE(BA),
+ EXECUTOR_REF_LINE(F), EXECUTOR_REF_LINE(X),
+ EXECUTOR_REF_LINE(I)
+ };
+
+ static warrior_info *this_warrior;
+
+ void enqueue_process(number_t pc) {
+ this_warrior->processes.push_back(pc);
+ }
-static const warrior *single_step_return_dead() {
- this_warrior = alive_warriors.front();
- alive_warriors.pop();
+ const warrior *single_step() {
+ this_warrior = alive_warriors.front();
+ alive_warriors.pop_front();
- program_counter = this_warrior->processes.front();
- this_warrior->processes.pop();
+ program_counter = this_warrior->processes.front();
+ this_warrior->processes.pop_front();
- instruction_register = core[program_counter];
+ executed_addresses.insert(program_counter);
+ instruction_register = core[program_counter];
- evaluate_operand(instruction_register.amode, instruction_register.anumber, a_pointer, a_instruction);
- evaluate_operand(instruction_register.bmode, instruction_register.bnumber, b_pointer, b_instruction);
+ evaluate_operand(instruction_register.amode, instruction_register.anumber, a_pointer, a_instruction);
+ evaluate_operand(instruction_register.bmode, instruction_register.bnumber, b_pointer, b_instruction);
- a_instruction_writable = core + (program_counter + a_pointer) % CORE_SIZE;
- b_instruction_writable = core + (program_counter + b_pointer) % CORE_SIZE;
+ a_instruction_writable = core + (program_counter + a_pointer) % LIB94_CORE_SIZE;
+ b_instruction_writable = core + (program_counter + b_pointer) % LIB94_CORE_SIZE;
- bool enqueue_process = executors[instruction_register.op + instruction_register.mod * 16]();
+ bool should_endeque = executors[instruction_register.op + instruction_register.mod * 16]();
- if (enqueue_process)
- this_warrior->processes.push((program_counter + 1) % CORE_SIZE);
+ if (should_endeque)
+ this_warrior->processes.push_back((program_counter + 1) % LIB94_CORE_SIZE);
- else if (this_warrior->processes.size() == 0)
- return this_warrior->the_warrior;
+ else if (this_warrior->processes.size() == 0)
+ return this_warrior->the_warrior;
+
+ alive_warriors.push_back(this_warrior);
+ return 0;
+ }
- alive_warriors.push(this_warrior);
- return 0;
}
diff --git a/lib94/executors.cpp b/lib94/executors.cpp
index 1c66243..c15eb7a 100644
--- a/lib94/executors.cpp
+++ b/lib94/executors.cpp
@@ -2,331 +2,364 @@
#include <functional>
#include <cassert>
-void enqueue_process(number_t pc);
+#include <cstdio>
-extern instruction core[CORE_SIZE];
-extern number_t program_counter;
-extern instruction a_instruction, b_instruction;
-extern instruction *a_instruction_writable;
-extern instruction *b_instruction_writable;
+namespace lib94 {
-void add_modified_instruction(const instruction *instr);
+ void enqueue_process(number_t pc);
-struct single_target {
- number_t *ptr;
+ extern instruction core[LIB94_CORE_SIZE];
+ extern number_t program_counter;
+ extern instruction a_instruction, b_instruction;
+ extern instruction *a_instruction_writable;
+ extern instruction *b_instruction_writable;
- inline bool assign(const single_target &left, const single_target &right,
- std::function<std::optional<number_t> (number_t, number_t)> f) {
+ void add_written_instruction(const instruction *instr);
+ void add_read_instruction(const instruction *instr);
+
+ struct single_target {
+ number_t *ptr;
+
+ inline bool assign(const single_target &left, const single_target &right,
+ std::function<std::optional<number_t> (number_t, number_t)> f) {
+
+ std::optional<number_t> result = f(*left.ptr, *right.ptr);
+ if (result.has_value()) {
+ *ptr = result.value();
+ return true;
+ }
+ return false;
- std::optional<number_t> result = f(*left.ptr, *right.ptr);
- if (result.has_value()) {
- *ptr = result.value();
- return true;
}
- return false;
- }
+ inline bool is_zero() {
+ return *ptr == 0;
+ }
- inline bool is_zero() {
- return *ptr == 0;
- }
+ inline bool dec_not_zero() {
+ return --*ptr != 0;
+ }
- inline bool dec_not_zero() {
- return --*ptr != 0;
- }
+ inline bool equal_to(const single_target &other) {
+ return *ptr == *other.ptr;
+ }
- inline bool equal_to(const single_target &other) {
- return *ptr == *other.ptr;
- }
+ inline bool less_than(const single_target &other) {
+ return *ptr < *other.ptr;
+ }
+ };
- inline bool less_than(const single_target &other) {
- return *ptr < *other.ptr;
- }
-};
+ struct pair_target {
+ number_t *ptr1;
+ number_t *ptr2;
-struct pair_target {
- number_t *ptr1;
- number_t *ptr2;
+ inline bool assign(const pair_target &left, const pair_target &right,
+ std::function<std::optional<number_t> (number_t, number_t)> f) {
+ bool to_return = true;
- inline bool assign(const pair_target &left, const pair_target &right,
- std::function<std::optional<number_t> (number_t, number_t)> f) {
- bool to_return = true;
+ std::optional<number_t> result1 = f(*left.ptr1, *right.ptr1);
+ if (result1.has_value())
+ *ptr1 = result1.value();
+ else
+ to_return = false;
- std::optional<number_t> result1 = f(*left.ptr1, *right.ptr1);
- if (result1.has_value())
- *ptr1 = result1.value();
- else
- to_return = false;
+ std::optional<number_t> result2 = f(*left.ptr2, *right.ptr2);
+ if (result2.has_value())
+ *ptr2 = result2.value();
+ else
+ to_return = false;
- std::optional<number_t> result2 = f(*left.ptr2, *right.ptr2);
- if (result2.has_value())
- *ptr2 = result2.value();
- else
- to_return = false;
+ return to_return;
- return to_return;
+ }
- }
+ inline bool is_zero() {
+ return *ptr1 == 0 && *ptr2 == 0;
+ }
- inline bool is_zero() {
- return *ptr1 == 0 && *ptr2 == 0;
- }
+ inline bool dec_not_zero() {
+ --*ptr1;
+ --*ptr2;
+ return *ptr1 != 0 || *ptr2 != 0;
+ }
- inline bool dec_not_zero() {
- --*ptr1;
- --*ptr2;
- return *ptr1 != 0 || *ptr2 != 0;
- }
+ inline bool equal_to(const pair_target &other) {
+ return *ptr1 == *other.ptr1 &&
+ *ptr2 == *other.ptr2;
+ }
- inline bool equal_to(const pair_target &other) {
- return *ptr1 == *other.ptr1 &&
- *ptr2 == *other.ptr2;
- }
+ inline bool less_than(const pair_target &other) {
+ return *ptr1 < *other.ptr1 &&
+ *ptr2 < *other.ptr2;
+ }
+ };
- inline bool less_than(const pair_target &other) {
- return *ptr1 < *other.ptr1 &&
- *ptr2 < *other.ptr2;
- }
-};
+ struct instruction_target {
+ instruction *instr;
-struct instruction_target {
- instruction *instr;
+ inline bool assign(const instruction_target &left, const instruction_target &right,
+ std::function<std::optional<number_t> (number_t, number_t)> f) {
+ bool to_return = true;
- inline bool assign(const instruction_target &left, const instruction_target &right,
- std::function<std::optional<number_t> (number_t, number_t)> f) {
- bool to_return = true;
+ std::optional<number_t> result1 = f(left.instr->anumber, right.instr->anumber);
+ if (result1.has_value())
+ instr->anumber = result1.value();
+ else
+ to_return = false;
- std::optional<number_t> result1 = f(left.instr->anumber, right.instr->anumber);
- if (result1.has_value())
- instr->anumber = result1.value();
- else
- to_return = false;
+ std::optional<number_t> result2 = f(left.instr->bnumber, right.instr->bnumber);
+ if (result2.has_value())
+ instr->bnumber = result2.value();
+ else
+ to_return = false;
- std::optional<number_t> result2 = f(left.instr->bnumber, right.instr->bnumber);
- if (result2.has_value())
- instr->bnumber = result2.value();
- else
- to_return = false;
+ return to_return;
- return to_return;
+ }
- }
+ inline bool is_zero() {
+ return instr->anumber == 0 && instr->bnumber == 0;
+ }
- inline bool is_zero() {
- return instr->anumber == 0 && instr->bnumber == 0;
- }
+ inline bool dec_not_zero() {
+ --instr->anumber;
+ --instr->bnumber;
+ return instr->anumber != 0 || instr->bnumber != 0;
+ }
- inline bool dec_not_zero() {
- --instr->anumber;
- --instr->bnumber;
- return instr->anumber != 0 || instr->bnumber != 0;
- }
+ inline bool equal_to(const instruction_target &other) {
+ return instr->op == other.instr->op &&
+ instr->mod == other.instr->mod &&
+ instr->amode == other.instr->amode &&
+ instr->anumber == other.instr->anumber &&
+ instr->bmode == other.instr->bmode &&
+ instr->bnumber == other.instr->bnumber;
+ }
- inline bool equal_to(const instruction_target &other) {
- return instr->op == other.instr->op &&
- instr->mod == other.instr->mod &&
- instr->amode == other.instr->amode &&
- instr->anumber == other.instr->anumber &&
- instr->bmode == other.instr->bmode &&
- instr->bnumber == other.instr->bnumber;
- }
+ inline bool less_than(const instruction_target &other) {
+ return instr->anumber < other.instr->anumber &&
+ instr->bnumber < other.instr->bnumber;
+ }
+ };
+
+ template<modifier mod>
+ struct target;
+
+ template<> struct target<A> : single_target {};
+ template<> struct target<B> : single_target {};
+ template<> struct target<AB> : single_target {};
+ template<> struct target<BA> : single_target {};
+ template<> struct target<F> : pair_target {};
+ template<> struct target<X> : pair_target {};
+ template<> struct target<I> : instruction_target {};
+
+ template<opcode op, modifier mod>
+ bool execute() {
+ target<mod> dest_out;
+ target<mod> dest_in;
+ target<mod> src_in;
+
+ if constexpr (mod == A) {
+ dest_out.ptr = &b_instruction_writable->anumber;
+ dest_in.ptr = &b_instruction.anumber;
+ src_in.ptr = &a_instruction.anumber;
+ }
- inline bool less_than(const instruction_target &other) {
- return instr->anumber < other.instr->anumber &&
- instr->bnumber < other.instr->bnumber;
- }
-};
-
-template<modifier mod>
-struct target;
-
-template<> struct target<A> : single_target {};
-template<> struct target<B> : single_target {};
-template<> struct target<AB> : single_target {};
-template<> struct target<BA> : single_target {};
-template<> struct target<F> : pair_target {};
-template<> struct target<X> : pair_target {};
-template<> struct target<I> : instruction_target {};
-
-template<opcode op, modifier mod>
-bool execute() {
- target<mod> dest_out;
- target<mod> dest_in;
- target<mod> src_in;
-
- if constexpr (mod == A) {
- dest_out.ptr = &b_instruction_writable->anumber;
- dest_in.ptr = &b_instruction.anumber;
- src_in.ptr = &a_instruction.anumber;
- }
+ else if constexpr (mod == B) {
+ dest_out.ptr = &b_instruction_writable->bnumber;
+ dest_in.ptr = &b_instruction.bnumber;
+ src_in.ptr = &a_instruction.bnumber;
+ }
- else if constexpr (mod == B) {
- dest_out.ptr = &b_instruction_writable->bnumber;
- dest_in.ptr = &b_instruction.bnumber;
- src_in.ptr = &a_instruction.bnumber;
- }
+ else if constexpr (mod == AB) {
+ dest_out.ptr = &b_instruction_writable->bnumber;
+ dest_in.ptr = &b_instruction.bnumber;
+ src_in.ptr = &a_instruction.anumber;
+ }
- else if constexpr (mod == AB) {
- dest_out.ptr = &b_instruction_writable->bnumber;
- dest_in.ptr = &b_instruction.bnumber;
- src_in.ptr = &a_instruction.anumber;
- }
+ else if constexpr (mod == BA) {
+ dest_out.ptr = &b_instruction_writable->anumber;
+ dest_in.ptr = &b_instruction.anumber;
+ src_in.ptr = &a_instruction.bnumber;
+ }
- else if constexpr (mod == BA) {
- dest_out.ptr = &b_instruction_writable->anumber;
- dest_in.ptr = &b_instruction.anumber;
- src_in.ptr = &a_instruction.bnumber;
- }
+ else if constexpr (mod == F) {
+ dest_out.ptr1 = &b_instruction_writable->anumber;
+ dest_out.ptr2 = &b_instruction_writable->bnumber;
+ dest_in.ptr1 = &b_instruction.anumber;
+ dest_in.ptr2 = &b_instruction.bnumber;
+ src_in.ptr1 = &a_instruction.anumber;
+ src_in.ptr2 = &a_instruction.bnumber;
+ }
- else if constexpr (mod == F) {
- dest_out.ptr1 = &b_instruction_writable->anumber;
- dest_out.ptr2 = &b_instruction_writable->bnumber;
- dest_in.ptr1 = &b_instruction.anumber;
- dest_in.ptr2 = &b_instruction.bnumber;
- src_in.ptr1 = &a_instruction.anumber;
- src_in.ptr2 = &a_instruction.bnumber;
- }
+ else if constexpr (mod == X) {
+ dest_out.ptr1 = &b_instruction_writable->anumber;
+ dest_out.ptr2 = &b_instruction_writable->bnumber;
+ dest_in.ptr1 = &b_instruction.anumber;
+ dest_in.ptr2 = &b_instruction.bnumber;
+ src_in.ptr1 = &a_instruction.bnumber;
+ src_in.ptr2 = &a_instruction.anumber;
+ }
- else if constexpr (mod == X) {
- dest_out.ptr1 = &b_instruction_writable->anumber;
- dest_out.ptr2 = &b_instruction_writable->bnumber;
- dest_in.ptr1 = &b_instruction.anumber;
- dest_in.ptr2 = &b_instruction.bnumber;
- src_in.ptr1 = &a_instruction.bnumber;
- src_in.ptr2 = &a_instruction.anumber;
- }
+ else if constexpr (mod == I) {
+ dest_out.instr = b_instruction_writable;
+ dest_in.instr = &b_instruction;
+ src_in.instr = &a_instruction;
+ }
- else if constexpr (mod == I) {
- dest_out.instr = b_instruction_writable;
- dest_in.instr = &b_instruction;
- src_in.instr = &a_instruction;
- }
+ else assert(false);
+
+ if constexpr (op == DAT)
+ return false;
+
+ else if constexpr (op == MOV) {
+ add_read_instruction(a_instruction_writable);
+ add_written_instruction(b_instruction_writable);
+ if constexpr (mod == I) {
+ dest_out.instr->op = src_in.instr->op;
+ dest_out.instr->mod = src_in.instr->mod;
+ dest_out.instr->amode = src_in.instr->amode;
+ dest_out.instr->bmode = src_in.instr->bmode;
+ }
+ return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional<number_t> {
+ (void)a;
+ return b;
+ });
+ }
- else assert(false);
+ else if constexpr (op == ADD) {
+ add_read_instruction(a_instruction_writable);
+ add_read_instruction(b_instruction_writable);
+ add_written_instruction(b_instruction_writable);
+ return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional<number_t> {
+ return (a + b) % LIB94_CORE_SIZE;
+ });
+ }
- if constexpr (op == DAT)
- return false;
+ else if constexpr (op == SUB) {
+ add_read_instruction(a_instruction_writable);
+ add_read_instruction(b_instruction_writable);
+ add_written_instruction(b_instruction_writable);
+ return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional<number_t> {
+ return (a + LIB94_CORE_SIZE - b) % LIB94_CORE_SIZE;
+ });
+ }
- else if constexpr (op == MOV) {
- add_modified_instruction(b_instruction_writable);
- if constexpr (mod == I) {
- dest_out.instr->op = src_in.instr->op;
- dest_out.instr->mod = src_in.instr->mod;
- dest_out.instr->amode = src_in.instr->amode;
- dest_out.instr->bmode = src_in.instr->bmode;
+ else if constexpr (op == MUL) {
+ add_read_instruction(a_instruction_writable);
+ add_read_instruction(b_instruction_writable);
+ add_written_instruction(b_instruction_writable);
+ return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional<number_t> {
+ return (a * b) % LIB94_CORE_SIZE;
+ });
}
- return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional<number_t> {
- return b;
- });
- }
- else if constexpr (op == ADD) {
- add_modified_instruction(b_instruction_writable);
- return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional<number_t> {
- return (a + b) % CORE_SIZE;
- });
- }
+ else if constexpr (op == DIV) {
+ add_read_instruction(a_instruction_writable);
+ add_read_instruction(b_instruction_writable);
+ add_written_instruction(b_instruction_writable);
+ return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional<number_t> {
+ if (b == 0)
+ return {};
+ return a / b;
+ });
+ }
- else if constexpr (op == SUB) {
- add_modified_instruction(b_instruction_writable);
- return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional<number_t> {
- return (a + CORE_SIZE - b) % CORE_SIZE;
- });
- }
+ else if constexpr (op == MOD) {
+ add_read_instruction(a_instruction_writable);
+ add_read_instruction(b_instruction_writable);
+ add_written_instruction(b_instruction_writable);
+ return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional<number_t> {
+ if (b == 0)
+ return {};
+ return a % b;
+ });
+ }
- else if constexpr (op == MUL) {
- add_modified_instruction(b_instruction_writable);
- return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional<number_t> {
- return (a * b) % CORE_SIZE;
- });
- }
+ else if constexpr (op == JMP) {
+ add_read_instruction(a_instruction_writable);
+ program_counter = (a_instruction_writable - core + LIB94_CORE_SIZE - 1) % LIB94_CORE_SIZE;
+ return true;
+ }
- else if constexpr (op == DIV) {
- add_modified_instruction(b_instruction_writable);
- return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional<number_t> {
- if (b == 0)
- return {};
- return a / b;
- });
- }
+ else if constexpr (op == JMZ) {
+ add_read_instruction(a_instruction_writable);
+ add_read_instruction(b_instruction_writable);
+ if (dest_in.is_zero())
+ program_counter = (a_instruction_writable - core + LIB94_CORE_SIZE - 1) % LIB94_CORE_SIZE;
+ return true;
+ }
- else if constexpr (op == MOD) {
- add_modified_instruction(b_instruction_writable);
- return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional<number_t> {
- if (b == 0)
- return {};
- return a % b;
- });
- }
+ else if constexpr (op == JMN) {
+ add_read_instruction(a_instruction_writable);
+ add_read_instruction(b_instruction_writable);
+ if (!dest_in.is_zero())
+ program_counter = (a_instruction_writable - core + LIB94_CORE_SIZE - 1) % LIB94_CORE_SIZE;
+ return true;
+ }
- else if constexpr (op == JMP) {
- program_counter = (a_instruction_writable - core + CORE_SIZE - 1) % CORE_SIZE;
- return true;
- }
+ else if constexpr (op == DJN) {
+ add_read_instruction(a_instruction_writable);
+ add_read_instruction(b_instruction_writable);
+ add_written_instruction(b_instruction_writable);
+ if (dest_in.dec_not_zero())
+ program_counter = (a_instruction_writable - core + LIB94_CORE_SIZE - 1) % LIB94_CORE_SIZE;
+ return true;
+ }
- else if constexpr (op == JMZ) {
- if (dest_in.is_zero())
- program_counter = (a_instruction_writable - core + CORE_SIZE - 1) % CORE_SIZE;
- return true;
- }
+ else if constexpr (op == SEQ) {
+ add_read_instruction(a_instruction_writable);
+ add_read_instruction(b_instruction_writable);
+ if (src_in.equal_to(dest_in))
+ program_counter = (program_counter + 1) % LIB94_CORE_SIZE;
+ return true;
+ }
- else if constexpr (op == JMN) {
- if (!dest_in.is_zero())
- program_counter = (a_instruction_writable - core + CORE_SIZE - 1) % CORE_SIZE;
- return true;
- }
+ else if constexpr (op == SNE) {
+ add_read_instruction(a_instruction_writable);
+ add_read_instruction(b_instruction_writable);
+ if (!src_in.equal_to(dest_in))
+ program_counter = (program_counter + 1) % LIB94_CORE_SIZE;
+ return true;
+ }
- else if constexpr (op == DJN) {
- add_modified_instruction(b_instruction_writable);
- if (dest_in.dec_not_zero())
- program_counter = (a_instruction_writable - core + CORE_SIZE - 1) % CORE_SIZE;
- return true;
- }
+ else if constexpr (op == SLT) {
+ add_read_instruction(a_instruction_writable);
+ add_read_instruction(b_instruction_writable);
+ if (src_in.less_than(dest_in))
+ program_counter = (program_counter + 1) % LIB94_CORE_SIZE;
+ return true;
+ }
- else if constexpr (op == SEQ) {
- if (src_in.equal_to(dest_in))
- program_counter = (program_counter + 1) % CORE_SIZE;
- return true;
- }
+ else if constexpr (op == SPL) {
+ add_read_instruction(a_instruction_writable);
+ enqueue_process((program_counter + 1) % LIB94_CORE_SIZE);
+ program_counter = (a_instruction_writable - core + LIB94_CORE_SIZE - 1) % LIB94_CORE_SIZE;
+ return true;
+ }
- else if constexpr (op == SNE) {
- if (!src_in.equal_to(dest_in))
- program_counter = (program_counter + 1) % CORE_SIZE;
- return true;
- }
+ else if constexpr (op == NOP)
+ return true;
- else if constexpr (op == SLT) {
- if (src_in.less_than(dest_in))
- program_counter = (program_counter + 1) % CORE_SIZE;
- return true;
+ else assert(false);
}
- else if constexpr (op == SPL) {
- enqueue_process((program_counter + 1) % CORE_SIZE);
- program_counter = (a_instruction_writable - core + CORE_SIZE - 1) % CORE_SIZE;
- return true;
- }
+ #define EXECUTOR_INST_LINE(mod) \
+ template bool execute<DAT, mod>(); template bool execute<MOV, mod>(); \
+ template bool execute<ADD, mod>(); template bool execute<SUB, mod>(); \
+ template bool execute<MUL, mod>(); template bool execute<DIV, mod>(); \
+ template bool execute<MOD, mod>(); template bool execute<JMP, mod>(); \
+ template bool execute<JMZ, mod>(); template bool execute<JMN, mod>(); \
+ template bool execute<DJN, mod>(); template bool execute<SEQ, mod>(); \
+ template bool execute<SNE, mod>(); template bool execute<SLT, mod>(); \
+ template bool execute<SPL, mod>(); template bool execute<NOP, mod>();
- else if constexpr (op == NOP)
- return true;
+ EXECUTOR_INST_LINE(A)
+ EXECUTOR_INST_LINE(B)
+ EXECUTOR_INST_LINE(AB)
+ EXECUTOR_INST_LINE(BA)
+ EXECUTOR_INST_LINE(F)
+ EXECUTOR_INST_LINE(X)
+ EXECUTOR_INST_LINE(I)
- else assert(false);
}
-
-#define EXECUTOR_INST_LINE(mod) \
- template bool execute<DAT, mod>(); template bool execute<MOV, mod>(); \
- template bool execute<ADD, mod>(); template bool execute<SUB, mod>(); \
- template bool execute<MUL, mod>(); template bool execute<DIV, mod>(); \
- template bool execute<MOD, mod>(); template bool execute<JMP, mod>(); \
- template bool execute<JMZ, mod>(); template bool execute<JMN, mod>(); \
- template bool execute<DJN, mod>(); template bool execute<SEQ, mod>(); \
- template bool execute<SNE, mod>(); template bool execute<SLT, mod>(); \
- template bool execute<SPL, mod>(); template bool execute<NOP, mod>();
-
-EXECUTOR_INST_LINE(A)
-EXECUTOR_INST_LINE(B)
-EXECUTOR_INST_LINE(AB)
-EXECUTOR_INST_LINE(BA)
-EXECUTOR_INST_LINE(F)
-EXECUTOR_INST_LINE(X)
-EXECUTOR_INST_LINE(I)
diff --git a/lib94/warrior.cpp b/lib94/warrior.cpp
index 0f100e6..274a31f 100644
--- a/lib94/warrior.cpp
+++ b/lib94/warrior.cpp
@@ -6,471 +6,512 @@
#include <memory>
#include <map>
-struct number_expr {
- virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &label_offsets, number_t this_offset) = 0;
-};
+namespace lib94 {
-struct label_number_expr : public number_expr {
- std::string label;
+ static const std::string opcode_strings[] = {
+ "dat", "mov", "add", "sub", "mul", "div", "mod", "jmp",
+ "jmz", "jmn", "djn", "seq", "sne", "slt", "spl", "nop"
+ };
- virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &label_offsets, number_t this_offset) {
- auto result = label_offsets.find(label);
- if (result == label_offsets.end())
- return {};
- return result->second - this_offset;
- }
-};
+ static const std::string modifier_strings[] = {
+ "a", "b", "ab", "ba", "f", "x", "i"
+ };
-struct number_number_expr : public number_expr {
- number_t value;
+ static const char mode_chars[] = "#$*@{<}>";
- virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &label_offsets, number_t this_offset) {
- return value;
+ std::string instruction_to_string(const instruction &instr) {
+ return
+ opcode_strings[instr.op] + '.' + modifier_strings[instr.mod] + ' ' +
+ mode_chars[instr.amode] + std::to_string(instr.anumber) + ", " +
+ mode_chars[instr.bmode] + std::to_string(instr.bnumber);
}
-};
-
-struct op_number_expr : public number_expr {
- std::unique_ptr<number_expr> left;
- std::unique_ptr<number_expr> right;
- std::function<std::optional<number_t> (number_t, number_t)> op;
-
- virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &label_offsets, number_t this_offset) {
- std::optional<number_t> left_result = left->to_number(label_offsets, this_offset);
- std::optional<number_t> right_result = right->to_number(label_offsets, this_offset);
- if (left_result.has_value() && right_result.has_value())
- return op(left_result.value(), right_result.value());
- return {};
+
+ number_t real_mod(number_t input) {
+ return (input % LIB94_CORE_SIZE + LIB94_CORE_SIZE) % LIB94_CORE_SIZE;
}
-};
-bool valid_label(std::string candidate) {
- if (candidate == "")
- return false;
+ struct number_expr {
+ virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &label_offsets, number_t this_offset) = 0;
+ };
- if (!isalpha(candidate[0]) && candidate[0] != '_')
- return false;
+ struct label_number_expr : public number_expr {
+ std::string label;
- for (char ch : candidate)
- if (!isalnum(ch) && ch != '_')
- return false;
+ virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &label_offsets, number_t this_offset) {
+ auto result = label_offsets.find(label);
+ if (result == label_offsets.end())
+ return {};
+ return real_mod(result->second - this_offset);
+ }
+ };
- return true;
-}
+ struct number_number_expr : public number_expr {
+ number_t value;
-size_t find_respecting_parentheses(std::string part, std::string candidates) {
- size_t layers = 0;
+ virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &label_offsets, number_t this_offset) {
+ (void)label_offsets;
+ (void)this_offset;
+ return real_mod(value);
+ }
+ };
- for (size_t i = 0; i < part.size(); ++i)
- if (layers == 0 && candidates.find(part[i]) != std::string::npos)
- return i;
- else if (part[i] == '(')
- ++layers;
- else if (part[i] == ')')
- --layers;
+ struct op_number_expr : public number_expr {
+ std::unique_ptr<number_expr> left;
+ std::unique_ptr<number_expr> right;
+ std::function<std::optional<number_t> (number_t, number_t)> op;
+
+ virtual std::optional<number_t> to_number(const std::map<std::string, number_t> &label_offsets, number_t this_offset) {
+ std::optional<number_t> left_result = left->to_number(label_offsets, this_offset);
+ std::optional<number_t> right_result = right->to_number(label_offsets, this_offset);
+ if (left_result.has_value() && right_result.has_value()) {
+ std::optional<number_t> op_result = op(left_result.value(), right_result.value());
+ if (op_result.has_value())
+ return real_mod(op_result.value());
+ }
+ return {};
+ }
+ };
- return std::string::npos;
-}
+ bool valid_label(std::string candidate) {
+ if (candidate == "")
+ return false;
-std::optional<std::unique_ptr<number_expr>> to_number_expr(std::string part);
+ if (!isalpha(candidate[0]) && candidate[0] != '_')
+ return false;
-std::optional<std::unique_ptr<number_expr>> make_op_expr(std::string part, size_t split) {
- std::string left = part.substr(0, split);
- std::string right = part.substr(split + 1);
+ for (char ch : candidate)
+ if (!isalnum(ch) && ch != '_')
+ return false;
- std::optional<std::unique_ptr<number_expr>> left_expr = to_number_expr(left);
- std::optional<std::unique_ptr<number_expr>> right_expr = to_number_expr(right);
- if (!left_expr.has_value() || !right_expr.has_value())
- return {};
+ return true;
+ }
- auto expr = std::make_unique<op_number_expr>();
- expr->left = std::move(left_expr.value());
- expr->right = std::move(right_expr.value());
+ size_t find_respecting_parentheses(std::string part, std::string candidates) {
+ size_t layers = 0;
- switch (part[split]) {
- case '+': expr->op = [](number_t a, number_t b) {return a + b;}; break;
- case '-': expr->op = [](number_t a, number_t b) {return a - b;}; break;
- case '*': expr->op = [](number_t a, number_t b) {return a * b;}; break;
- case '/': expr->op = [](number_t a, number_t b) {return a / b;}; break;
- case '%': expr->op = [](number_t a, number_t b) {return a % b;}; break;
- }
+ for (int i = part.size() - 1; i >= 0; --i)
+ if (layers == 0 && candidates.find(part[i]) != std::string::npos)
+ return i;
+ else if (part[i] == ')')
+ ++layers;
+ else if (part[i] == '(')
+ --layers;
- return expr;
-}
+ return std::string::npos;
+ }
-std::string trim_spaces(std::string str) {
- size_t start = str.find_first_not_of(' ');
- size_t end = str.find_last_not_of(' ') + 1;
- if (start == std::string::npos)
- return "";
- return str.substr(start, end - start);
-}
+ std::optional<std::unique_ptr<number_expr>> to_number_expr(std::string part);
-std::optional<std::unique_ptr<number_expr>> to_number_expr(std::string part) {
- part = trim_spaces(part);
- if (part == "")
- return {};
+ std::optional<std::unique_ptr<number_expr>> make_op_expr(std::string part, size_t split) {
+ std::string left = part.substr(0, split);
+ std::string right = part.substr(split + 1);
- size_t split = find_respecting_parentheses(part, "+-");
- if (split == std::string::npos)
- split = find_respecting_parentheses(part, "*/%");
- if (split != std::string::npos)
- return make_op_expr(part, split);
+ std::optional<std::unique_ptr<number_expr>> left_expr = to_number_expr(left);
+ std::optional<std::unique_ptr<number_expr>> right_expr = to_number_expr(right);
+ if (!left_expr.has_value() || !right_expr.has_value())
+ return {};
- if (part[0] == '(' && part[part.size() - 1] == ')')
- return to_number_expr(part.substr(1, part.size() - 2));
+ auto expr = std::make_unique<op_number_expr>();
+ expr->left = std::move(left_expr.value());
+ expr->right = std::move(right_expr.value());
+
+ switch (part[split]) {
+ case '+': expr->op = [](number_t a, number_t b) {return a + b;}; break;
+ case '-': expr->op = [](number_t a, number_t b) {return a - b;}; break;
+ case '*': expr->op = [](number_t a, number_t b) {return a * b;}; break;
+ case '/': expr->op = [](number_t a, number_t b) -> std::optional<number_t> {
+ if (b == 0)
+ return {};
+ return a / b;
+ }; break;
+ case '%': expr->op = [](number_t a, number_t b) -> std::optional<number_t> {
+ if (b == 0)
+ return {};
+ return a % b;
+ }; break;
+ }
- size_t count;
- number_t number = std::stoul(part, &count);
- if (count == part.size()) {
- std::unique_ptr<number_number_expr> expr = std::make_unique<number_number_expr>();
- expr->value = number;
return expr;
}
- if (valid_label(part)) {
- std::unique_ptr<label_number_expr> expr = std::make_unique<label_number_expr>();
- expr->label = part;
- return expr;
+ std::string trim_spaces(std::string str) {
+ size_t start = str.find_first_not_of(' ');
+ size_t end = str.find_last_not_of(' ') + 1;
+ if (start == std::string::npos)
+ return "";
+ return str.substr(start, end - start);
}
- return {};
-}
+ std::optional<std::unique_ptr<number_expr>> to_number_expr(std::string part) {
+ part = trim_spaces(part);
+ if (part == "")
+ return {};
-static const std::map<char, mode> mode_symbols = {
- {'#', IMMEDIATE}, {'$', DIRECT},
- {'*', A_INDIRECT}, {'@', B_INDIRECT},
- {'{', A_DECREMENT}, {'<', B_DECREMENT},
- {'}', A_INCREMENT}, {'>', B_INCREMENT}
-};
+ size_t split = find_respecting_parentheses(part, "+-");
+ if (split == std::string::npos)
+ split = find_respecting_parentheses(part, "*/%");
+ if (split != std::string::npos)
+ return make_op_expr(part, split);
+
+ if (part[0] == '(' && part[part.size() - 1] == ')')
+ return to_number_expr(part.substr(1, part.size() - 2));
+
+ try {
+ size_t count;
+ number_t number = std::stoul(part, &count);
+ if (count == part.size()) {
+ std::unique_ptr<number_number_expr> expr = std::make_unique<number_number_expr>();
+ expr->value = number;
+ return expr;
+ }
+ }
+ catch (const std::exception &e) {}
+
+ if (valid_label(part)) {
+ std::unique_ptr<label_number_expr> expr = std::make_unique<label_number_expr>();
+ expr->label = part;
+ return expr;
+ }
-std::optional<std::pair<mode, std::unique_ptr<number_expr>>> to_field(std::string part) {
- if (part == "")
return {};
+ }
+
+ static const std::map<char, mode> mode_symbols = {
+ {'#', IMMEDIATE}, {'$', DIRECT},
+ {'*', A_INDIRECT}, {'@', B_INDIRECT},
+ {'{', A_DECREMENT}, {'<', B_DECREMENT},
+ {'}', A_INCREMENT}, {'>', B_INCREMENT}
+ };
+
+ std::optional<std::pair<mode, std::unique_ptr<number_expr>>> to_field(std::string part) {
+ if (part == "")
+ return {};
+
+ mode m = DIRECT;
+
+ auto result = mode_symbols.find(part[0]);
+ if (result != mode_symbols.end()) {
+ m = result->second;
+ part = trim_spaces(part.substr(1));
+ }
- mode m = DIRECT;
+ std::optional<std::unique_ptr<number_expr>> expr = to_number_expr(part);
+ if (expr.has_value())
+ return std::make_pair<>(m, std::move(expr.value()));
- auto result = mode_symbols.find(part[0]);
- if (result != mode_symbols.end()) {
- m = result->second;
- part = trim_spaces(part.substr(1));
+ return {};
}
- std::optional<std::unique_ptr<number_expr>> expr = to_number_expr(part);
- if (expr.has_value())
- return std::make_pair<>(m, std::move(expr.value()));
+ static const std::map<std::string, opcode> opcode_names = {
+ {"dat", DAT}, {"mov", MOV}, {"add", ADD}, {"sub", SUB},
+ {"mul", MUL}, {"div", DIV}, {"mod", MOD}, {"jmp", JMP},
+ {"jmz", JMZ}, {"jmn", JMN}, {"djn", DJN}, {"seq", SEQ},
+ {"sne", SNE}, {"slt", SLT}, {"spl", SPL}, {"nop", NOP},
+ {"cmp", SEQ}, {"jnz", JMN}
+ };
- return {};
-}
+ struct future_instruction {
+ size_t source_line;
+ opcode op;
+ modifier mod;
+ mode amode;
+ mode bmode;
+ std::unique_ptr<number_expr> anumber;
+ std::unique_ptr<number_expr> bnumber;
+ };
-static const std::map<std::string, opcode> opcode_names = {
- {"dat", DAT}, {"mov", MOV}, {"add", ADD}, {"sub", SUB},
- {"mul", MUL}, {"div", DIV}, {"mod", MOD}, {"jmp", JMP},
- {"jmz", JMZ}, {"jmn", JMN}, {"djn", DJN}, {"seq", SEQ},
- {"sne", SNE}, {"slt", SLT}, {"spl", SPL}, {"nop", NOP},
- {"cmp", SEQ}
-};
-
-struct future_instruction {
- size_t source_line;
- opcode op;
- modifier mod;
- mode amode;
- mode bmode;
- std::unique_ptr<number_expr> anumber;
- std::unique_ptr<number_expr> bnumber;
-};
-
-std::variant<warrior *, std::string> compile_warrior(std::string source) {
- for (char &ch : source)
- if (ch == '\t' || ch == '\r')
- ch = ' ';
-
- std::unique_ptr<warrior> w = std::make_unique<warrior>();
-
- std::vector<future_instruction> instructions;
- std::map<std::string, number_t> label_offsets;
- std::optional<std::unique_ptr<number_expr>> org = {};
- number_t org_offset;
- size_t org_source_line;
-
- size_t on_line = 0;
-
- while (true) {
- if (source == "")
- break;
-
- ++on_line;
-
- size_t newline = source.find('\n');
- if (newline == std::string::npos)
- newline = source.size();
-
- std::string line = source.substr(0, newline);
- source = source.substr(newline + 1);
-
- size_t semicolon = line.find(';');
-
- if (semicolon != std::string::npos) {
- std::string comment = trim_spaces(line.substr(semicolon + 1));
- line = line.substr(0, semicolon);
-
- if (comment.starts_with("name "))
- w->name = trim_spaces(comment.substr(5));
- else if (comment.starts_with("author "))
- w->author = trim_spaces(comment.substr(7));
- }
+ std::variant<warrior *, std::string> compile_warrior(std::string source) {
+ for (char &ch : source)
+ if (ch == '\t' || ch == '\r')
+ ch = ' ';
+
+ std::unique_ptr<warrior> w = std::make_unique<warrior>();
- for (char &ch : line)
- ch = tolower(ch);
+ std::vector<future_instruction> instructions;
+ std::map<std::string, number_t> label_offsets;
+ std::optional<std::unique_ptr<number_expr>> org = {};
+ number_t org_offset = -1;
+ size_t org_source_line = -1;
+
+ size_t on_line = 0;
while (true) {
- size_t colon = line.find(':');
- if (colon == std::string::npos)
+ if (source == "")
break;
- std::string label = line.substr(0, colon);
- line = line.substr(colon + 1);
+ ++on_line;
- if (!valid_label(label))
- return std::string("bad label on line ") + std::to_string(on_line);
- if (label_offsets.contains(label))
- return std::string("duplicate label on line ") + std::to_string(on_line);
+ size_t newline = source.find('\n');
+ if (newline == std::string::npos)
+ newline = source.size();
- label_offsets[label] = instructions.size();
- }
+ std::string line = source.substr(0, newline);
+ source = source.substr(newline + 1);
+
+ size_t semicolon = line.find(';');
- line = trim_spaces(line);
- if (line == "")
- continue;
+ if (semicolon != std::string::npos) {
+ std::string comment = trim_spaces(line.substr(semicolon + 1));
+ line = line.substr(0, semicolon);
- future_instruction instr;
- instr.source_line = on_line;
+ if (comment.starts_with("name "))
+ w->name = trim_spaces(comment.substr(5));
+ else if (comment.starts_with("author "))
+ w->author = trim_spaces(comment.substr(7));
+ }
- std::string opcode_str = line.substr(0, 3);
- line = trim_spaces(line.substr(3));
+ for (char &ch : line)
+ ch = tolower(ch);
- if (opcode_str == "org") {
- if (org.has_value())
- return std::string("duplicate org on line ") + std::to_string(on_line);
+ while (true) {
+ size_t colon = line.find(':');
+ if (colon == std::string::npos)
+ break;
- org = to_number_expr(line);
+ std::string label = line.substr(0, colon);
+ line = line.substr(colon + 1);
- if (!org.has_value())
- return std::string("bad expression on line ") + std::to_string(on_line);
+ if (!valid_label(label))
+ return std::string("bad label on line ") + std::to_string(on_line);
+ if (label_offsets.contains(label))
+ return std::string("duplicate label on line ") + std::to_string(on_line);
- org_offset = instructions.size();
- org_source_line = on_line;
- continue;
- }
+ label_offsets[label] = instructions.size();
+ }
- auto opcode_it = opcode_names.find(opcode_str);
+ line = trim_spaces(line);
+ if (line == "")
+ continue;
- if (opcode_it == opcode_names.end())
- return std::string("unknown opcode on line ") + std::to_string(on_line);
+ future_instruction instr;
+ instr.source_line = on_line;
- instr.op = opcode_it->second;
+ std::string opcode_str = line.substr(0, 3);
+ line = trim_spaces(line.substr(3));
- bool got_mod = false;
+ if (opcode_str == "org") {
+ if (org.has_value())
+ return std::string("duplicate org on line ") + std::to_string(on_line);
- if (line != "" && line[0] == '.') {
- line = trim_spaces(line.substr(1));
+ org = to_number_expr(line);
- if (line == "")
- return std::string("dot with no modifier on line ") + std::to_string(on_line);
+ if (!org.has_value())
+ return std::string("bad expression on line ") + std::to_string(on_line);
- got_mod = true;
+ org_offset = instructions.size();
+ org_source_line = on_line;
+ continue;
+ }
- size_t mod_length = 2;
+ auto opcode_it = opcode_names.find(opcode_str);
- if (line.starts_with("ab"))
- instr.mod = AB;
- else if (line.starts_with("ba"))
- instr.mod = BA;
- else {
+ if (opcode_it == opcode_names.end())
+ return std::string("unknown opcode on line ") + std::to_string(on_line);
- mod_length = 1;
+ instr.op = opcode_it->second;
- if (line[0] == 'a')
- instr.mod = A;
- else if (line[0] == 'b')
- instr.mod = B;
- else if (line[0] == 'f')
- instr.mod = F;
- else if (line[0] == 'x')
- instr.mod = X;
- else if (line[0] == 'i')
- instr.mod = I;
+ bool got_mod = false;
- else
- return std::string("unknown opcode modifier on line ") + std::to_string(on_line);
+ if (line != "" && line[0] == '.') {
+ line = trim_spaces(line.substr(1));
- }
+ if (line == "")
+ return std::string("dot with no modifier on line ") + std::to_string(on_line);
- line = trim_spaces(line.substr(mod_length));
- }
+ got_mod = true;
- std::optional<std::pair<mode, std::unique_ptr<number_expr>>> afield, bfield;
+ size_t mod_length = 2;
- if (line == "") {
- afield = to_field("0");
- bfield = to_field("0");
- }
+ if (line.starts_with("ab"))
+ instr.mod = AB;
+ else if (line.starts_with("ba"))
+ instr.mod = BA;
+ else {
- else {
- size_t comma = line.find(",");
+ mod_length = 1;
- if (comma == std::string::npos) {
- afield = to_field(line);
+ if (line[0] == 'a')
+ instr.mod = A;
+ else if (line[0] == 'b')
+ instr.mod = B;
+ else if (line[0] == 'f')
+ instr.mod = F;
+ else if (line[0] == 'x')
+ instr.mod = X;
+ else if (line[0] == 'i')
+ instr.mod = I;
+
+ else
+ return std::string("unknown opcode modifier on line ") + std::to_string(on_line);
+
+ }
+
+ line = trim_spaces(line.substr(mod_length));
+ }
+
+ std::optional<std::pair<mode, std::unique_ptr<number_expr>>> afield, bfield;
+
+ if (line == "") {
+ afield = to_field("0");
bfield = to_field("0");
}
else {
- std::string apart = trim_spaces(line.substr(0, comma));
- std::string bpart = trim_spaces(line.substr(comma + 1));
- afield = to_field(apart);
- bfield = to_field(bpart);
+ size_t comma = line.find(",");
+
+ if (comma == std::string::npos) {
+ afield = to_field(line);
+ bfield = to_field("0");
+ }
+
+ else {
+ std::string apart = trim_spaces(line.substr(0, comma));
+ std::string bpart = trim_spaces(line.substr(comma + 1));
+ afield = to_field(apart);
+ bfield = to_field(bpart);
+ }
}
- }
-
- if (!afield.has_value() || !bfield.has_value())
- return std::string("bad expression on line ") + std::to_string(on_line);
- instr.amode = afield.value().first;
- instr.bmode = bfield.value().first;
- instr.anumber = std::move(afield.value().second);
- instr.bnumber = std::move(bfield.value().second);
+ if (!afield.has_value() || !bfield.has_value())
+ return std::string("bad expression on line ") + std::to_string(on_line);
- if (!got_mod)
- switch (instr.op) {
+ instr.amode = afield.value().first;
+ instr.bmode = bfield.value().first;
+ instr.anumber = std::move(afield.value().second);
+ instr.bnumber = std::move(bfield.value().second);
- case DAT:
- instr.mod = F;
- break;
+ if (!got_mod)
+ switch (instr.op) {
- case MOV:
- case SEQ:
- case SNE:
- if (instr.amode == IMMEDIATE)
- instr.mod = AB;
- else if (instr.bmode == IMMEDIATE)
+ case DAT:
+ instr.mod = F;
+ break;
+
+ case MOV:
+ case SEQ:
+ case SNE:
+ if (instr.amode == IMMEDIATE)
+ instr.mod = AB;
+ else if (instr.bmode == IMMEDIATE)
+ instr.mod = B;
+ else
+ instr.mod = I;
+ break;
+
+ case ADD:
+ case SUB:
+ case MUL:
+ case DIV:
+ case MOD:
+ if (instr.amode == IMMEDIATE)
+ instr.mod = AB;
+ else if (instr.bmode == IMMEDIATE)
+ instr.mod = B;
+ else
+ instr.mod = F;
+ break;
+
+ case SLT:
+ if (instr.amode == IMMEDIATE)
+ instr.mod = AB;
instr.mod = B;
- else
- instr.mod = I;
- break;
-
- case ADD:
- case SUB:
- case MUL:
- case DIV:
- case MOD:
- if (instr.amode == IMMEDIATE)
- instr.mod = AB;
- else if (instr.bmode == IMMEDIATE)
+ break;
+
+ case JMP:
+ case JMZ:
+ case JMN:
+ case DJN:
+ case SPL:
+ case NOP:
instr.mod = B;
- else
- instr.mod = F;
- break;
+ break;
- case SLT:
- if (instr.amode == IMMEDIATE)
- instr.mod = AB;
- instr.mod = B;
- break;
+ }
- case JMP:
- case JMZ:
- case JMN:
- case DJN:
- case SPL:
- case NOP:
- instr.mod = B;
- break;
+ instructions.push_back(std::move(instr));
+ }
- }
+ if (w->name == "")
+ return "no name";
+ if (w->author == "")
+ return "no author";
+
+ if (org.has_value()) {
+ std::optional<number_t> org_number = org.value()->to_number(label_offsets, org_offset);
+ if (org_number.has_value())
+ w->org = org_number.value() + org_offset;
+ else
+ return std::string("bad expression on line ") + std::to_string(org_source_line);
+ }
- instructions.push_back(std::move(instr));
- }
+ for (size_t i = 0; i < instructions.size(); ++i) {
+ const future_instruction &instr = instructions[i];
+ std::optional<number_t> anumber = instr.anumber->to_number(label_offsets, i);
+ std::optional<number_t> bnumber = instr.bnumber->to_number(label_offsets, i);
+ if (!anumber.has_value() || !bnumber.has_value())
+ return std::string("bad expression on line ") + std::to_string(instr.source_line);
+ w->instructions.push_back({
+ .op = instr.op, .mod = instr.mod, .amode = instr.amode, .bmode = instr.bmode,
+ .anumber = anumber.value(), .bnumber = bnumber.value()
+ });
+ }
- if (w->name == "")
- return "no name";
- if (w->author == "")
- return "no author";
-
- if (org.has_value()) {
- std::optional<number_t> org_number = org.value()->to_number(label_offsets, org_offset);
- if (org_number.has_value())
- w->org = org_number.value() + org_offset;
- else
- return std::string("bad expression on line ") + std::to_string(org_source_line);
+ return w.release();
}
- for (int i = 0; i < instructions.size(); ++i) {
- const future_instruction &instr = instructions[i];
- std::optional<number_t> anumber = instr.anumber->to_number(label_offsets, i);
- std::optional<number_t> bnumber = instr.bnumber->to_number(label_offsets, i);
- if (!anumber.has_value() || !bnumber.has_value())
- return std::string("bad expression on line ") + std::to_string(instr.source_line);
- w->instructions.push_back({
- .op = instr.op, .mod = instr.mod, .amode = instr.amode, .bmode = instr.bmode,
- .anumber = ((anumber.value() % CORE_SIZE) + CORE_SIZE) % CORE_SIZE,
- .bnumber = ((bnumber.value() % CORE_SIZE) + CORE_SIZE) % CORE_SIZE
- });
- }
+ struct wheader {
+ size_t name_size;
+ size_t author_size;
+ size_t instructions_size;
+ number_t org;
+ };
- return w.release();
-}
+ bool save_warrior(const warrior &w, const std::filesystem::path &to) {
+ FILE *f = fopen(to.c_str(), "wb");
+ if (!f)
+ return false;
-struct wheader {
- size_t name_size;
- size_t author_size;
- size_t instructions_size;
- number_t org;
-};
-
-bool save_warrior(const warrior &w, const std::filesystem::path &to) {
- FILE *f = fopen(to.c_str(), "wb");
- if (!f)
- return false;
-
- wheader wh = {
- .name_size = w.name.size(), .author_size = w.author.size(),
- .instructions_size = w.instructions.size(), .org = w.org
- };
+ wheader wh = {
+ .name_size = w.name.size(), .author_size = w.author.size(),
+ .instructions_size = w.instructions.size(), .org = w.org
+ };
- fwrite(&wh, sizeof(wheader), 1, f);
- fwrite(w.name.c_str(), w.name.size(), 1, f);
- fwrite(w.author.c_str(), w.author.size(), 1, f);
- fwrite(w.instructions.data(), sizeof(instruction) * w.instructions.size(), 1, f);
+ fwrite(&wh, sizeof(wheader), 1, f);
+ fwrite(w.name.c_str(), w.name.size(), 1, f);
+ fwrite(w.author.c_str(), w.author.size(), 1, f);
+ fwrite(w.instructions.data(), sizeof(instruction) * w.instructions.size(), 1, f);
- fclose(f);
- return true;
-}
+ fclose(f);
+ return true;
+ }
-std::optional<warrior *> load_warrior(const std::filesystem::path &from) {
- FILE *f = fopen(from.c_str(), "rb");
- if (!f)
- return {};
+ std::optional<warrior *> load_warrior(const std::filesystem::path &from) {
+ FILE *f = fopen(from.c_str(), "rb");
+ if (!f)
+ return {};
- std::unique_ptr<warrior> w = std::make_unique<warrior>();
+ std::unique_ptr<warrior> w = std::make_unique<warrior>();
- wheader wh;
- fread(&wh, sizeof(wheader), 1, f);
+ wheader wh;
+ fread(&wh, sizeof(wheader), 1, f);
- w->name.resize(wh.name_size);
- w->author.resize(wh.author_size);
- w->instructions.resize(wh.instructions_size);
- w->org = wh.org;
+ w->name.resize(wh.name_size);
+ w->author.resize(wh.author_size);
+ w->instructions.resize(wh.instructions_size);
+ w->org = wh.org;
- fread(w->name.data(), wh.name_size, 1, f);
- fread(w->author.data(), wh.author_size, 1, f);
- fread(w->instructions.data(), wh.instructions_size, 1, f);
+ fread(w->name.data(), wh.name_size, 1, f);
+ fread(w->author.data(), wh.author_size, 1, f);
+ fread(w->instructions.data(), wh.instructions_size, 1, f);
- fclose(f);
+ fclose(f);
- for (const instruction &i : w->instructions)
- if (i.op > NOP || i.mod > I || i.amode > B_INCREMENT || i.bmode > B_INCREMENT ||
- i.anumber >= CORE_SIZE || i.bnumber >= CORE_SIZE)
- return {};
+ for (const instruction &i : w->instructions)
+ if (i.op > NOP || i.mod > I || i.amode > B_INCREMENT || i.bmode > B_INCREMENT ||
+ i.anumber >= LIB94_CORE_SIZE || i.bnumber >= LIB94_CORE_SIZE)
+ return {};
+
+ return w.release();
+ }
- return w.release();
}
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..62e2684
--- /dev/null
+++ b/makefile
@@ -0,0 +1,23 @@
+CPP_ARGS = -std=c++20 -O2 -Wall -Wextra -Iinclude
+GTKMM_CPP_ARGS = $(shell pkg-config --cflags gtkmm-4.0)
+GTKMM_LD_ARGS = $(shell pkg-config --libs gtkmm-4.0)
+
+default: bin/bench
+
+clean:
+ rm -r obj bin
+
+bin/bench: obj/bench/main.o obj/bench/bench_window.o obj/bench/core_widget.o obj/lib94.o
+ @mkdir -p $(dir $@)
+ g++ ${CPP_ARGS} $^ ${GTKMM_LD_ARGS} -o $@
+
+obj/lib94.o: obj/lib94/core.o obj/lib94/executors.o obj/lib94/warrior.o
+ ld -r $^ -o $@
+
+obj/bench/%.o: bench/%.cpp
+ @mkdir -p $(dir $@)
+ g++ -c ${CPP_ARGS} ${GTKMM_CPP_ARGS} $^ -o $@
+
+obj/%.o: %.cpp
+ @mkdir -p $(dir $@)
+ g++ -c ${CPP_ARGS} $^ -o $@
diff --git a/test/build.sh b/test/build.sh
deleted file mode 100644
index 199813d..0000000
--- a/test/build.sh
+++ /dev/null
@@ -1 +0,0 @@
-g++ -O2 --std=c++20 -DCORE_SIZE=16 test.cpp ../lib94/*.cpp -I ../include -o test
diff --git a/test/dwarf.rc b/test/dwarf.rc
deleted file mode 100644
index ff64ddc..0000000
--- a/test/dwarf.rc
+++ /dev/null
@@ -1,10 +0,0 @@
-;author Standard
-;name Dwarf
-
-org 1
-
-dat 1, 1 ;not 0, 0 so i can see it change
-
-mov 0-1, 3
-add.ab #4, 0-1
-jmp 0-2
diff --git a/test/imp.rc b/test/imp.rc
deleted file mode 100644
index c6415fd..0000000
--- a/test/imp.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-;author Standard
-;name Imp
-
-mov 0, 1
diff --git a/test/test.cpp b/test/test.cpp
deleted file mode 100644
index b838a02..0000000
--- a/test/test.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-#include <lib94/lib94.hpp>
-#include <iostream>
-#include <cassert>
-#include <fstream>
-#include <chrono>
-#include <thread>
-
-const char *const opcodes[] = {
- "dat", "mov", "add", "sub",
- "mul", "div", "mod", "jmp",
- "jmz", "jmn", "djn", "seq",
- "sne", "slt", "spl", "nop"
-};
-
-const char *const modifiers[] = {
- "a ", "b ", "ab", "ba", "f ", "x ", "i "
-};
-
-const char *modes = "#$*@{<}>";
-
-int main(int argc, const char **argv) {
- assert(argc > 1);
-
- seed_prng(time(0));
-
- std::vector<const warrior *> ws;
-
- for (int i = 1; i < argc; ++i) {
- std::string source;
- std::ifstream f(argv[i]);
- std::getline(f, source, '\0');
- f.close();
-
- auto result = compile_warrior(source);
- if (std::holds_alternative<std::string>(result)) {
- std::cout << "Error in " << argv[i] << ": " << std::get<std::string>(result) << std::endl;
- return 0;
- }
-
- ws.push_back(std::get<warrior *>(result));
- }
-
- std::vector<number_t> wps;
- wps.resize(ws.size());
-
- clear_core({.op = DAT, .mod = F, .amode = DIRECT, .bmode = DIRECT, .anumber = 0, .bnumber = 0});
- init_round(ws);
-
- while (true) {
- for (int i = 0; i < ws.size(); ++i)
- if (get_processes(ws[i]).size() == 0)
- wps[i] = -1;
- else
- wps[i] = get_next_process(ws[i]);
-
- auto mod = get_modified_addresses();
-
- printf("\x1b[H\x1b[2J");
-
- for (int a = 0; a < CORE_SIZE; ++a) {
- const instruction &i = get_instruction(a);
-
- printf("%d: %s.%s %c%d, %c%d",
- a, opcodes[i.op], modifiers[i.mod],
- modes[i.amode], i.anumber,
- modes[i.bmode], i.bnumber);
-
- if (mod.contains(a))
- printf(" *");
-
- for (int w = 0; w < ws.size(); ++w)
- if (a == wps[w])
- printf(" < %s", ws[w]->name.c_str());
-
- putchar('\n');
- }
-
- std::this_thread::sleep_for(std::chrono::milliseconds(250));
-
- clear_modified_addresses();
- single_step();
- }
-
- return 0;
-}
diff --git a/warriors/big-nothing.red b/warriors/big-nothing.red
new file mode 100644
index 0000000..53a6bed
--- /dev/null
+++ b/warriors/big-nothing.red
@@ -0,0 +1,20 @@
+;author blah
+;name big-nothing
+
+start:
+nop
+nop
+nop
+nop
+nop
+nop
+nop
+nop
+nop
+nop
+nop
+nop
+nop
+nop
+nop
+jmp start
diff --git a/warriors/dwarf.red b/warriors/dwarf.red
new file mode 100644
index 0000000..cfa3b86
--- /dev/null
+++ b/warriors/dwarf.red
@@ -0,0 +1,6 @@
+;author standard
+;name dwarf
+
+mov 0-1, 3
+add.ab #4, 0-1
+jmp 0-2
diff --git a/warriors/imp.red b/warriors/imp.red
new file mode 100644
index 0000000..5ba74c4
--- /dev/null
+++ b/warriors/imp.red
@@ -0,0 +1,4 @@
+;author standard
+;name imp
+
+mov 0, 1
diff --git a/warriors/splitter.red b/warriors/splitter.red
new file mode 100644
index 0000000..17016c4
--- /dev/null
+++ b/warriors/splitter.red
@@ -0,0 +1,5 @@
+;author blah
+;name splitter
+
+spl 0
+jmp 0-1
diff --git a/warriors/trap.red b/warriors/trap.red
new file mode 100644
index 0000000..681ea6e
--- /dev/null
+++ b/warriors/trap.red
@@ -0,0 +1,28 @@
+;author benji
+;name trap
+
+org start
+
+trap:
+spl 1
+jmp trap
+
+start:
+mov.i bomb, bomb + 8
+add.ab #8, start
+sub.ab #8, bomb
+sne.ab #bomb - 8 - start, start
+jmp core_clear
+jmp start
+
+bomb:
+jmp trap - 8
+
+core_clear:
+mov background, background + 1
+add.ab #1, core_clear
+jnz.b core_clear, core_clear
+mov.ab #background + 1 - core_clear, core_clear
+jmp core_clear
+
+background: