#include "foot-emulator.h" #include namespace foot { Operand::Operand(uint8_t bits) : addressing_mode(static_cast((bits & 0b11100000) >> 5)), register_index(bits & 0b00011111) {} Device::Device(uint16_t size) : assigned_memory_base(nullptr), assigned_memory_size(size) {} uint16_t& Device::memory(uint16_t index) { if (index >= assigned_memory_size) { throw std::range_error("Index out of range"); } return assigned_memory_base[index]; } Emulator::Emulator() : memory{}, registers{}, configuration(0), mapped_base(0) {} void Emulator::run(const std::vector& program) { std::copy(program.begin(), program.end(), memory.begin()); keep_running = true; while (keep_running) { run_instruction(); for (auto& device : devices) { if (device->update()) { keep_running = false; } } } } void Emulator::map_device(std::unique_ptr&& device) { mapped_base -= device->mapped_memory_size(); device->assigned_memory_base = &memory[mapped_base]; devices.push_back(std::move(device)); } uint16_t& Emulator::memory_at(uint16_t address) { return memory[address]; } uint16_t& Emulator::decode_operand(Operand operand) { switch (operand.addressing_mode) { case Operand::AddressingMode::Immediate: dummy_value = operand.register_index; return dummy_value; case Operand::AddressingMode::Direct: return registers[operand.register_index]; case Operand::AddressingMode::IndirectAutoIncrement: return memory[registers[operand.register_index]++]; case Operand::AddressingMode::IndirectAutoDecrement: return memory[registers[operand.register_index]--]; case Operand::AddressingMode::Indirect: return memory[registers[operand.register_index]]; case Operand::AddressingMode::IndirectOffset1Word: return memory[registers[operand.register_index] + 1]; case Operand::AddressingMode::IndirectOffset2Word: return memory[registers[operand.register_index] + 2]; case Operand::AddressingMode::IndirectOffset3Word: return memory[registers[operand.register_index] + 3]; default: return dummy_value; } } uint16_t& Emulator::register_at(uint8_t index) { return registers[index]; } void Emulator::run_instruction() { read_instruction(); const bool rep = repeats(); const uint16_t loop_count = rep ? registers[LC] : 1; for (int iteration = 0; iteration < loop_count; iteration++) { if (rep) { registers[LC] = iteration; } switch ((instruction & 0x0F000000) >> 24) { case 0: CNST(); break; case 1: switch ((instruction & 0x0000FF00) >> 8) { case 0: CMPR(); break; case 1: BWNG(); break; case 2: ARNG(); break; case 3: LONG(); break; case 4: CONF(); break; } break; case 2: BWOR(); break; case 3: BAND(); break; case 4: BXOR(); break; case 5: break; case 6: SRSH(); break; case 7: ZLSH(); break; case 8: CLSH(); break; case 9: ADDI(); break; case 10: SUBT(); break; case 11: MULT(); break; case 14: DIVI(); break; case 15: MODU(); break; default: break; } } if (rep) { registers[LC] = loop_count; } } void Emulator::read_instruction() { uint16_t low = memory[registers[PC]++]; uint16_t high = memory[registers[PC]++]; instruction = (uint32_t(high) << 16) | uint32_t(low); } void Emulator::CNST() { decode_operand((instruction & 0x00FF0000) >> 16) = instruction & 0x0000FFFF; } void Emulator::CMPR() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t b = decode_operand((instruction & 0x00FF0000) >> 16); if (a < b) { status_register = Status::Less; } else if (a > b) { status_register = Status::Greater; } else { status_register = Status::Equal; } } void Emulator::BWNG() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); d = ~a; } void Emulator::ARNG() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); d = -a; } void Emulator::LONG() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); d = !a; } void Emulator::CONF() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); d = configuration; configuration = a; } void Emulator::BWOR() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8); uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); d = a | b; } void Emulator::BAND() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8); uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); d = a & b; } void Emulator::BXOR() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8); uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); d = a ^ b; } void Emulator::SRSH() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8); uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); d = int16_t(a) >> int16_t(b); } void Emulator::ZLSH() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8); uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); d = a << b; } void Emulator::CLSH() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8); uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); d = (a << b) | (a >> (16 - b)); } void Emulator::ADDI() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8); uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); d = a + b; } void Emulator::SUBT() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8); uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); d = a - b; } void Emulator::MULT() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8); uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); d = uint32_t(int32_t(a) * int32_t(b)) >> shift(); } void Emulator::DIVI() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8); uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); // convert from unsigned to signed, then widen d = uint32_t((int32_t(int16_t(a)) << shift()) / int32_t(int16_t(b))); } void Emulator::MODU() { uint16_t a = decode_operand(instruction & 0x000000FF); uint16_t b = decode_operand((instruction & 0x0000FF00) >> 8); uint16_t& d = decode_operand((instruction & 0x00FF0000) >> 16); d = int16_t(a) % int16_t(b); } bool Emulator::repeats() { return instruction & 0x10000000; } uint8_t Emulator::shift() { return configuration & 0x000F; } }