diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | bench/bench_window.cpp | 324 | ||||
-rw-r--r-- | bench/bench_window.hpp | 112 | ||||
-rw-r--r-- | bench/core_widget.cpp | 162 | ||||
-rw-r--r-- | bench/core_widget.hpp | 46 | ||||
-rw-r--r-- | bench/main.cpp | 31 | ||||
-rw-r--r-- | bench/main.hpp | 16 | ||||
-rw-r--r-- | include/lib94/lib94.hpp | 122 | ||||
-rw-r--r-- | lib94/core.cpp | 494 | ||||
-rw-r--r-- | lib94/executors.cpp | 573 | ||||
-rw-r--r-- | lib94/warrior.cpp | 775 | ||||
-rw-r--r-- | makefile | 23 | ||||
-rw-r--r-- | test/build.sh | 1 | ||||
-rw-r--r-- | test/dwarf.rc | 10 | ||||
-rw-r--r-- | test/imp.rc | 4 | ||||
-rw-r--r-- | test/test.cpp | 85 | ||||
-rw-r--r-- | warriors/big-nothing.red | 20 | ||||
-rw-r--r-- | warriors/dwarf.red | 6 | ||||
-rw-r--r-- | warriors/imp.red | 4 | ||||
-rw-r--r-- | warriors/splitter.red | 5 | ||||
-rw-r--r-- | warriors/trap.red | 28 |
21 files changed, 1806 insertions, 1038 deletions
@@ -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: |