377 lines
7.5 KiB
C++
377 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{},
|
|
registers{},
|
|
configuration(0),
|
|
mapped_base(0)
|
|
{}
|
|
|
|
void Emulator::run(const std::vector<uint16_t>& 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>&& 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;
|
|
}
|
|
|
|
}
|