foot/emulator/foot-emulator.cpp
shylie 9e2604855e
All checks were successful
Build / build (push) Successful in 4s
Add initial working version
2025-07-07 09:24:19 -04:00

376 lines
7.5 KiB
C++

#include "foot-emulator.h"
#include <stdexcept>
namespace foot
{
Operand::Operand(uint8_t bits) :
addressing_mode(static_cast<AddressingMode>((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{},
mapped_base(0)
{}
void Emulator::run(const std::vector<uint16_t>& program)
{
std::copy(program.begin(), program.end(), memory.begin());
while (true)
{
run_instruction();
for (auto& device : devices)
{
device->update();
}
}
}
void Emulator::map_device(std::unique_ptr<Device>&& 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;
}
}
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::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:
URSH();
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::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::URSH()
{
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);
d = uint32_t((int32_t(a) >> shift()) / int32_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 = uint32_t((int32_t(a) >> shift()) / int32_t(b));
}
bool Emulator::repeats()
{
return instruction & 0x10000000;
}
uint8_t Emulator::shift()
{
return configuration & 0x000F;
}
}