#include #include #include namespace lib94 { void enqueue_process(number_t pc); 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; void add_written_instruction(const instruction *instr); void add_read_instruction(const instruction *instr); #define maybe_add_written_instruction(instr) \ if constexpr (uas) add_written_instruction(instr) #define maybe_add_read_instruction(instr) \ if constexpr (uas) add_read_instruction(instr) struct single_target { number_t *ptr; inline bool assign(const single_target &left, const single_target &right, std::function (number_t, number_t)> f) { std::optional 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 dec_not_zero() { *ptr = (*ptr + LIB94_CORE_SIZE - 1) % LIB94_CORE_SIZE; return *ptr != 0; } inline bool equal_to(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; inline bool assign(const pair_target &left, const pair_target &right, std::function (number_t, number_t)> f) { bool to_return = true; std::optional result1 = f(*left.ptr1, *right.ptr1); if (result1.has_value()) *ptr1 = result1.value(); else to_return = false; std::optional result2 = f(*left.ptr2, *right.ptr2); if (result2.has_value()) *ptr2 = result2.value(); else to_return = false; return to_return; } inline bool is_zero() { return *ptr1 == 0 && *ptr2 == 0; } inline bool dec_not_zero() { *ptr1 = (*ptr1 + LIB94_CORE_SIZE - 1) % LIB94_CORE_SIZE; *ptr2 = (*ptr2 + LIB94_CORE_SIZE - 1) % LIB94_CORE_SIZE; return *ptr1 != 0 || *ptr2 != 0; } 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; } }; struct instruction_target { instruction *instr; inline bool assign(const instruction_target &left, const instruction_target &right, std::function (number_t, number_t)> f) { bool to_return = true; std::optional result1 = f(left.instr->anumber, right.instr->anumber); if (result1.has_value()) instr->anumber = result1.value(); else to_return = false; std::optional result2 = f(left.instr->bnumber, right.instr->bnumber); if (result2.has_value()) instr->bnumber = result2.value(); else to_return = false; return to_return; } inline bool is_zero() { return instr->anumber == 0 && instr->bnumber == 0; } inline bool dec_not_zero() { instr->anumber = (instr->anumber + LIB94_CORE_SIZE - 1) % LIB94_CORE_SIZE; instr->bnumber = (instr->bnumber + LIB94_CORE_SIZE - 1) % LIB94_CORE_SIZE; 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 less_than(const instruction_target &other) { return instr->anumber < other.instr->anumber && instr->bnumber < other.instr->bnumber; } }; template struct target; template<> struct target : single_target {}; template<> struct target : single_target {}; template<> struct target : single_target {}; template<> struct target : single_target {}; template<> struct target : pair_target {}; template<> struct target : pair_target {}; template<> struct target : instruction_target {}; template bool execute() { target dest_out; target dest_in; target 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 == 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 == 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 == 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) { maybe_add_read_instruction(a_instruction_writable); maybe_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 { (void)a; return b; }); } else if constexpr (op == ADD) { maybe_add_read_instruction(a_instruction_writable); maybe_add_read_instruction(b_instruction_writable); maybe_add_written_instruction(b_instruction_writable); return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional { return (a + b) % LIB94_CORE_SIZE; }); } else if constexpr (op == SUB) { maybe_add_read_instruction(a_instruction_writable); maybe_add_read_instruction(b_instruction_writable); maybe_add_written_instruction(b_instruction_writable); return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional { return (a + LIB94_CORE_SIZE - b) % LIB94_CORE_SIZE; }); } else if constexpr (op == MUL) { maybe_add_read_instruction(a_instruction_writable); maybe_add_read_instruction(b_instruction_writable); maybe_add_written_instruction(b_instruction_writable); return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional { return (a * b) % LIB94_CORE_SIZE; }); } else if constexpr (op == DIV) { maybe_add_read_instruction(a_instruction_writable); maybe_add_read_instruction(b_instruction_writable); maybe_add_written_instruction(b_instruction_writable); return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional { if (b == 0) return {}; return a / b; }); } else if constexpr (op == MOD) { maybe_add_read_instruction(a_instruction_writable); maybe_add_read_instruction(b_instruction_writable); maybe_add_written_instruction(b_instruction_writable); return dest_out.assign(dest_in, src_in, [](number_t a, number_t b) -> std::optional { if (b == 0) return {}; return a % b; }); } else if constexpr (op == JMP) { maybe_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 == JMZ) { maybe_add_read_instruction(a_instruction_writable); maybe_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 == JMN) { maybe_add_read_instruction(a_instruction_writable); maybe_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 == DJN) { maybe_add_read_instruction(a_instruction_writable); maybe_add_read_instruction(b_instruction_writable); maybe_add_written_instruction(b_instruction_writable); if (dest_out.dec_not_zero()) program_counter = (a_instruction_writable - core + LIB94_CORE_SIZE - 1) % LIB94_CORE_SIZE; return true; } else if constexpr (op == SEQ) { maybe_add_read_instruction(a_instruction_writable); maybe_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 == SNE) { maybe_add_read_instruction(a_instruction_writable); maybe_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 == SLT) { maybe_add_read_instruction(a_instruction_writable); maybe_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 == SPL) { maybe_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 == NOP) return true; else assert(false); } #define EXECUTOR_INST_LINE(mod, uas) \ template bool execute(); template bool execute(); \ template bool execute(); template bool execute(); \ template bool execute(); template bool execute(); \ template bool execute(); template bool execute(); \ template bool execute(); template bool execute(); \ template bool execute(); template bool execute(); \ template bool execute(); template bool execute(); \ template bool execute(); template bool execute(); #define EXECUTOR_INST(uas) \ EXECUTOR_INST_LINE(A, uas) \ EXECUTOR_INST_LINE(B, uas) \ EXECUTOR_INST_LINE(AB, uas) \ EXECUTOR_INST_LINE(BA, uas) \ EXECUTOR_INST_LINE(F, uas) \ EXECUTOR_INST_LINE(X, uas) \ EXECUTOR_INST_LINE(I, uas) EXECUTOR_INST(true) EXECUTOR_INST(false) }