2023-10-05 18:37:58 +00:00
|
|
|
|
//
|
2023-10-12 17:54:51 +00:00
|
|
|
|
//
|
2023-10-05 18:37:58 +00:00
|
|
|
|
// PerformImplementation.hpp
|
|
|
|
|
// Clock Signal
|
|
|
|
|
//
|
|
|
|
|
// Created by Thomas Harte on 05/10/2023.
|
|
|
|
|
// Copyright © 2023 Thomas Harte. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#ifndef PerformImplementation_h
|
|
|
|
|
#define PerformImplementation_h
|
|
|
|
|
|
2023-11-08 15:52:36 +00:00
|
|
|
|
#include "Arithmetic.hpp"
|
|
|
|
|
#include "BCD.hpp"
|
|
|
|
|
#include "FlowControl.hpp"
|
|
|
|
|
#include "InOut.hpp"
|
|
|
|
|
#include "LoadStore.hpp"
|
|
|
|
|
#include "Logical.hpp"
|
|
|
|
|
#include "Repetition.hpp"
|
2023-11-06 02:42:22 +00:00
|
|
|
|
#include "Resolver.hpp"
|
2023-11-08 15:52:36 +00:00
|
|
|
|
#include "ShiftRoll.hpp"
|
|
|
|
|
#include "Stack.hpp"
|
2023-10-05 19:49:07 +00:00
|
|
|
|
|
2023-11-08 15:52:36 +00:00
|
|
|
|
#include "../Interrupts.hpp"
|
|
|
|
|
#include "../AccessType.hpp"
|
2023-10-16 19:40:24 +00:00
|
|
|
|
|
2023-10-06 15:10:54 +00:00
|
|
|
|
//
|
2023-11-08 15:52:36 +00:00
|
|
|
|
// Comments throughout headers above come from the 1997 edition of the
|
|
|
|
|
// Intel Architecture Software Developer’s Manual; that year all such
|
|
|
|
|
// definitions still fitted within a single volume, Volume 2.
|
2023-10-06 15:10:54 +00:00
|
|
|
|
//
|
|
|
|
|
// Order Number 243191; e.g. https://www.ardent-tool.com/CPU/docs/Intel/IA/243191-002.pdf
|
|
|
|
|
//
|
|
|
|
|
|
2023-11-08 15:52:36 +00:00
|
|
|
|
namespace InstructionSet::x86 {
|
2023-10-08 17:34:28 +00:00
|
|
|
|
|
2023-10-05 18:37:58 +00:00
|
|
|
|
template <
|
|
|
|
|
DataSize data_size,
|
2023-10-25 20:00:01 +00:00
|
|
|
|
AddressSize address_size,
|
2023-10-05 20:49:02 +00:00
|
|
|
|
typename InstructionT,
|
2023-11-01 21:03:23 +00:00
|
|
|
|
typename ContextT
|
2023-10-05 18:37:58 +00:00
|
|
|
|
> void perform(
|
2023-10-05 20:49:02 +00:00
|
|
|
|
const InstructionT &instruction,
|
2023-11-01 21:03:23 +00:00
|
|
|
|
ContextT &context
|
2023-10-05 18:37:58 +00:00
|
|
|
|
) {
|
2023-10-05 20:49:02 +00:00
|
|
|
|
using IntT = typename DataSizeType<data_size>::type;
|
2023-10-25 20:00:01 +00:00
|
|
|
|
using AddressT = typename AddressSizeType<address_size>::type;
|
2023-10-05 20:49:02 +00:00
|
|
|
|
|
2023-10-31 19:06:19 +00:00
|
|
|
|
// Establish source() and destination() shorthands to fetch data if necessary.
|
|
|
|
|
//
|
|
|
|
|
// C++17, which this project targets at the time of writing, does not provide templatised lambdas.
|
|
|
|
|
// So the following division is in part a necessity.
|
|
|
|
|
//
|
|
|
|
|
// (though GCC offers C++20 syntax as an extension, and Clang seems to follow along, so maybe I'm overthinking)
|
2023-10-09 01:41:36 +00:00
|
|
|
|
IntT immediate;
|
2023-11-07 15:09:04 +00:00
|
|
|
|
const auto source_r = [&]() -> read_t<IntT> {
|
2023-11-06 21:04:31 +00:00
|
|
|
|
return resolve<IntT, AccessType::Read>(
|
2023-10-09 01:41:36 +00:00
|
|
|
|
instruction,
|
2023-10-25 18:43:58 +00:00
|
|
|
|
instruction.source().source(),
|
2023-10-09 01:41:36 +00:00
|
|
|
|
instruction.source(),
|
2023-11-01 21:03:23 +00:00
|
|
|
|
context,
|
2023-10-09 01:41:36 +00:00
|
|
|
|
nullptr,
|
|
|
|
|
&immediate);
|
|
|
|
|
};
|
2023-11-07 15:09:04 +00:00
|
|
|
|
const auto source_rmw = [&]() -> modify_t<IntT> {
|
2023-11-06 21:04:31 +00:00
|
|
|
|
return resolve<IntT, AccessType::ReadModifyWrite>(
|
2023-10-31 19:06:19 +00:00
|
|
|
|
instruction,
|
|
|
|
|
instruction.source().source(),
|
|
|
|
|
instruction.source(),
|
2023-11-01 21:03:23 +00:00
|
|
|
|
context,
|
2023-10-31 19:06:19 +00:00
|
|
|
|
nullptr,
|
|
|
|
|
&immediate);
|
|
|
|
|
};
|
2023-11-07 15:09:04 +00:00
|
|
|
|
const auto destination_r = [&]() -> read_t<IntT> {
|
2023-11-06 21:04:31 +00:00
|
|
|
|
return resolve<IntT, AccessType::Read>(
|
2023-10-29 20:19:10 +00:00
|
|
|
|
instruction,
|
|
|
|
|
instruction.destination().source(),
|
|
|
|
|
instruction.destination(),
|
2023-11-01 21:03:23 +00:00
|
|
|
|
context,
|
2023-10-29 20:19:10 +00:00
|
|
|
|
nullptr,
|
|
|
|
|
&immediate);
|
|
|
|
|
};
|
2023-11-07 15:09:04 +00:00
|
|
|
|
const auto destination_w = [&]() -> write_t<IntT> {
|
2023-11-06 21:04:31 +00:00
|
|
|
|
return resolve<IntT, AccessType::Write>(
|
2023-10-29 20:19:10 +00:00
|
|
|
|
instruction,
|
|
|
|
|
instruction.destination().source(),
|
|
|
|
|
instruction.destination(),
|
2023-11-01 21:03:23 +00:00
|
|
|
|
context,
|
2023-10-29 20:19:10 +00:00
|
|
|
|
nullptr,
|
|
|
|
|
&immediate);
|
|
|
|
|
};
|
2023-11-07 15:09:04 +00:00
|
|
|
|
const auto destination_rmw = [&]() -> modify_t<IntT> {
|
2023-11-06 21:04:31 +00:00
|
|
|
|
return resolve<IntT, AccessType::ReadModifyWrite>(
|
2023-10-09 01:41:36 +00:00
|
|
|
|
instruction,
|
2023-10-25 18:43:58 +00:00
|
|
|
|
instruction.destination().source(),
|
2023-10-09 01:41:36 +00:00
|
|
|
|
instruction.destination(),
|
2023-11-01 21:03:23 +00:00
|
|
|
|
context,
|
2023-10-09 01:41:36 +00:00
|
|
|
|
nullptr,
|
|
|
|
|
&immediate);
|
|
|
|
|
};
|
2023-10-05 20:49:02 +00:00
|
|
|
|
|
2023-10-11 19:08:04 +00:00
|
|
|
|
// Performs a displacement jump only if @c condition is true.
|
2023-10-11 18:36:42 +00:00
|
|
|
|
const auto jcc = [&](bool condition) {
|
|
|
|
|
Primitive::jump(
|
|
|
|
|
condition,
|
|
|
|
|
instruction.displacement(),
|
2023-11-01 21:03:23 +00:00
|
|
|
|
context);
|
2023-10-11 18:36:42 +00:00
|
|
|
|
};
|
|
|
|
|
|
2023-10-13 18:44:22 +00:00
|
|
|
|
const auto shift_count = [&]() -> uint8_t {
|
2023-11-01 21:03:23 +00:00
|
|
|
|
static constexpr uint8_t mask = (ContextT::model != Model::i8086) ? 0x1f : 0xff;
|
2023-10-25 18:43:58 +00:00
|
|
|
|
switch(instruction.source().source()) {
|
2023-10-13 18:44:22 +00:00
|
|
|
|
case Source::None: return 1;
|
2023-10-13 19:34:06 +00:00
|
|
|
|
case Source::Immediate: return uint8_t(instruction.operand()) & mask;
|
2023-11-01 21:03:23 +00:00
|
|
|
|
default: return context.registers.cl() & mask;
|
2023-10-13 18:44:22 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-10-11 19:08:04 +00:00
|
|
|
|
// Some instructions use a pair of registers as an extended accumulator — DX:AX or EDX:EAX.
|
|
|
|
|
// The two following return the high and low parts of that pair; they also work in Byte mode to return AH:AL,
|
|
|
|
|
// i.e. AX split into high and low parts.
|
|
|
|
|
const auto pair_high = [&]() -> IntT& {
|
2023-11-01 21:03:23 +00:00
|
|
|
|
if constexpr (data_size == DataSize::Byte) return context.registers.ah();
|
|
|
|
|
else if constexpr (data_size == DataSize::Word) return context.registers.dx();
|
|
|
|
|
else if constexpr (data_size == DataSize::DWord) return context.registers.edx();
|
2023-10-11 18:36:42 +00:00
|
|
|
|
};
|
2023-10-11 19:08:04 +00:00
|
|
|
|
const auto pair_low = [&]() -> IntT& {
|
2023-11-01 21:03:23 +00:00
|
|
|
|
if constexpr (data_size == DataSize::Byte) return context.registers.al();
|
|
|
|
|
else if constexpr (data_size == DataSize::Word) return context.registers.ax();
|
|
|
|
|
else if constexpr (data_size == DataSize::DWord) return context.registers.eax();
|
2023-10-11 18:36:42 +00:00
|
|
|
|
};
|
|
|
|
|
|
2023-10-20 21:00:32 +00:00
|
|
|
|
// For the string operations, evaluate to either SI and DI or ESI and EDI, depending on the address size.
|
2023-10-20 20:52:47 +00:00
|
|
|
|
const auto eSI = [&]() -> AddressT& {
|
|
|
|
|
if constexpr (std::is_same_v<AddressT, uint16_t>) {
|
2023-11-01 21:03:23 +00:00
|
|
|
|
return context.registers.si();
|
2023-10-20 20:52:47 +00:00
|
|
|
|
} else {
|
2023-11-01 21:03:23 +00:00
|
|
|
|
return context.registers.esi();
|
2023-10-20 20:52:47 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
const auto eDI = [&]() -> AddressT& {
|
|
|
|
|
if constexpr (std::is_same_v<AddressT, uint16_t>) {
|
2023-11-01 21:03:23 +00:00
|
|
|
|
return context.registers.di();
|
2023-10-20 20:52:47 +00:00
|
|
|
|
} else {
|
2023-11-01 21:03:23 +00:00
|
|
|
|
return context.registers.edi();
|
2023-10-20 20:52:47 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-10-20 21:00:32 +00:00
|
|
|
|
// For counts, provide either eCX or CX depending on address size.
|
|
|
|
|
const auto eCX = [&]() -> AddressT& {
|
|
|
|
|
if constexpr (std::is_same_v<AddressT, uint16_t>) {
|
2023-11-01 21:03:23 +00:00
|
|
|
|
return context.registers.cx();
|
2023-10-20 21:00:32 +00:00
|
|
|
|
} else {
|
2023-11-01 21:03:23 +00:00
|
|
|
|
return context.registers.ecx();
|
2023-10-20 21:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-10-22 02:52:50 +00:00
|
|
|
|
// Gets the port for an IN or OUT; these are always 16-bit.
|
|
|
|
|
const auto port = [&](Source source) -> uint16_t {
|
|
|
|
|
switch(source) {
|
2023-10-27 18:04:23 +00:00
|
|
|
|
case Source::DirectAddress: return instruction.offset();
|
2023-11-01 21:03:23 +00:00
|
|
|
|
default: return context.registers.dx();
|
2023-10-22 02:52:50 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-10-05 20:49:02 +00:00
|
|
|
|
// Guide to the below:
|
|
|
|
|
//
|
2023-11-08 16:23:21 +00:00
|
|
|
|
// * use hard-coded register names where appropriate, otherwise use the source_X() and destination_X() lambdas;
|
2023-10-05 20:49:02 +00:00
|
|
|
|
// * return directly if there is definitely no possible write back to RAM;
|
2023-11-08 16:23:21 +00:00
|
|
|
|
// * break if there's a chance of writeback.
|
2023-10-27 03:19:31 +00:00
|
|
|
|
switch(instruction.operation()) {
|
2023-10-09 15:46:59 +00:00
|
|
|
|
default:
|
|
|
|
|
assert(false);
|
2023-10-06 02:27:52 +00:00
|
|
|
|
|
2023-11-08 16:23:21 +00:00
|
|
|
|
case Operation::ESC:
|
|
|
|
|
case Operation::NOP: return;
|
|
|
|
|
|
2023-11-01 21:03:23 +00:00
|
|
|
|
case Operation::AAM: Primitive::aam(context.registers.axp(), instruction.operand(), context); return;
|
2023-11-09 03:30:39 +00:00
|
|
|
|
case Operation::AAD: Primitive::aad(context.registers.axp(), instruction.operand(), context); return;
|
|
|
|
|
case Operation::AAA: Primitive::aaas<true>(context.registers.axp(), context); return;
|
|
|
|
|
case Operation::AAS: Primitive::aaas<false>(context.registers.axp(), context); return;
|
2023-11-09 03:26:48 +00:00
|
|
|
|
case Operation::DAA: Primitive::daas<true>(context.registers.al(), context); return;
|
|
|
|
|
case Operation::DAS: Primitive::daas<false>(context.registers.al(), context); return;
|
2023-10-05 20:49:02 +00:00
|
|
|
|
|
2023-10-11 19:08:04 +00:00
|
|
|
|
case Operation::CBW: Primitive::cbw(pair_low()); return;
|
|
|
|
|
case Operation::CWD: Primitive::cwd(pair_high(), pair_low()); return;
|
2023-10-09 18:54:14 +00:00
|
|
|
|
|
2023-11-01 21:03:23 +00:00
|
|
|
|
case Operation::HLT: context.flow_controller.halt(); return;
|
|
|
|
|
case Operation::WAIT: context.flow_controller.wait(); return;
|
2023-10-09 20:21:04 +00:00
|
|
|
|
|
2023-11-08 16:23:21 +00:00
|
|
|
|
case Operation::ADC:
|
|
|
|
|
Primitive::add<true, IntT>(destination_rmw(), source_r(), context);
|
|
|
|
|
break;
|
|
|
|
|
case Operation::ADD:
|
|
|
|
|
Primitive::add<false, IntT>(destination_rmw(), source_r(), context);
|
|
|
|
|
break;
|
2023-11-07 14:58:42 +00:00
|
|
|
|
case Operation::SBB:
|
|
|
|
|
Primitive::sub<true, AccessType::ReadModifyWrite, IntT>(destination_rmw(), source_r(), context);
|
|
|
|
|
break;
|
|
|
|
|
case Operation::SUB:
|
|
|
|
|
Primitive::sub<false, AccessType::ReadModifyWrite, IntT>(destination_rmw(), source_r(), context);
|
|
|
|
|
break;
|
|
|
|
|
case Operation::CMP:
|
|
|
|
|
Primitive::sub<false, AccessType::Read, IntT>(destination_r(), source_r(), context);
|
|
|
|
|
return;
|
2023-11-08 16:23:21 +00:00
|
|
|
|
case Operation::TEST:
|
|
|
|
|
Primitive::test<IntT>(destination_r(), source_r(), context);
|
|
|
|
|
return;
|
2023-10-11 02:15:33 +00:00
|
|
|
|
|
2023-11-09 16:55:04 +00:00
|
|
|
|
case Operation::MUL: Primitive::mul<IntT>(pair_high(), pair_low(), source_r(), context); return;
|
|
|
|
|
case Operation::IMUL_1: Primitive::imul<IntT>(pair_high(), pair_low(), source_r(), context); return;
|
|
|
|
|
case Operation::DIV: Primitive::div<IntT>(pair_high(), pair_low(), source_r(), context); return;
|
|
|
|
|
case Operation::IDIV: Primitive::idiv<false, IntT>(pair_high(), pair_low(), source_r(), context); return;
|
|
|
|
|
case Operation::IDIV_REP: Primitive::idiv<true, IntT>(pair_high(), pair_low(), source_r(), context); return;
|
2023-10-09 20:21:04 +00:00
|
|
|
|
|
2023-11-07 14:58:42 +00:00
|
|
|
|
case Operation::INC: Primitive::inc<IntT>(destination_rmw(), context); break;
|
|
|
|
|
case Operation::DEC: Primitive::dec<IntT>(destination_rmw(), context); break;
|
2023-10-10 19:57:33 +00:00
|
|
|
|
|
2023-11-07 14:58:42 +00:00
|
|
|
|
case Operation::AND: Primitive::and_<IntT>(destination_rmw(), source_r(), context); break;
|
|
|
|
|
case Operation::OR: Primitive::or_<IntT>(destination_rmw(), source_r(), context); break;
|
|
|
|
|
case Operation::XOR: Primitive::xor_<IntT>(destination_rmw(), source_r(), context); break;
|
|
|
|
|
case Operation::NEG: Primitive::neg<IntT>(source_rmw(), context); break; // TODO: should be a destination.
|
|
|
|
|
case Operation::NOT: Primitive::not_<IntT>(source_rmw()); break; // TODO: should be a destination.
|
2023-10-09 15:46:59 +00:00
|
|
|
|
|
2023-11-07 14:58:42 +00:00
|
|
|
|
case Operation::CALLrel: Primitive::call_relative<AddressT>(instruction.displacement(), context); return;
|
|
|
|
|
case Operation::CALLabs: Primitive::call_absolute<IntT>(destination_r(), context); return;
|
|
|
|
|
case Operation::CALLfar: Primitive::call_far(instruction, context); return;
|
2023-11-01 21:03:23 +00:00
|
|
|
|
|
2023-11-07 14:58:42 +00:00
|
|
|
|
case Operation::JMPrel: jcc(true); return;
|
|
|
|
|
case Operation::JMPabs: Primitive::jump_absolute<IntT>(destination_r(), context); return;
|
|
|
|
|
case Operation::JMPfar: Primitive::jump_far(instruction, context); return;
|
2023-11-01 21:03:23 +00:00
|
|
|
|
|
2023-11-07 14:58:42 +00:00
|
|
|
|
case Operation::JCXZ: jcc(!eCX()); return;
|
|
|
|
|
case Operation::LOOP: Primitive::loop<AddressT>(eCX(), instruction.offset(), context); return;
|
|
|
|
|
case Operation::LOOPE: Primitive::loope<AddressT>(eCX(), instruction.offset(), context); return;
|
|
|
|
|
case Operation::LOOPNE: Primitive::loopne<AddressT>(eCX(), instruction.offset(), context); return;
|
2023-11-01 21:03:23 +00:00
|
|
|
|
|
|
|
|
|
case Operation::IRET: Primitive::iret(context); return;
|
|
|
|
|
case Operation::RETnear: Primitive::ret_near(instruction, context); return;
|
|
|
|
|
case Operation::RETfar: Primitive::ret_far(instruction, context); return;
|
|
|
|
|
|
|
|
|
|
case Operation::INT: interrupt(instruction.operand(), context); return;
|
|
|
|
|
case Operation::INTO: Primitive::into(context); return;
|
|
|
|
|
|
|
|
|
|
case Operation::SAHF: Primitive::sahf(context.registers.ah(), context); return;
|
|
|
|
|
case Operation::LAHF: Primitive::lahf(context.registers.ah(), context); return;
|
|
|
|
|
|
2023-11-08 16:23:21 +00:00
|
|
|
|
case Operation::LDS:
|
2023-11-11 03:58:59 +00:00
|
|
|
|
if constexpr (data_size == DataSize::Word) {
|
|
|
|
|
Primitive::ld<Source::DS>(instruction, destination_w(), context);
|
|
|
|
|
context.registers.did_update(Source::DS);
|
|
|
|
|
}
|
2023-11-08 16:23:21 +00:00
|
|
|
|
return;
|
|
|
|
|
case Operation::LES:
|
2023-11-11 03:58:59 +00:00
|
|
|
|
if constexpr (data_size == DataSize::Word) {
|
|
|
|
|
Primitive::ld<Source::ES>(instruction, destination_w(), context);
|
|
|
|
|
context.registers.did_update(Source::ES);
|
|
|
|
|
}
|
2023-11-08 16:23:21 +00:00
|
|
|
|
return;
|
2023-11-01 21:03:23 +00:00
|
|
|
|
|
2023-11-07 14:58:42 +00:00
|
|
|
|
case Operation::LEA: Primitive::lea<IntT>(instruction, destination_w(), context); return;
|
2023-11-11 03:11:52 +00:00
|
|
|
|
case Operation::MOV:
|
|
|
|
|
Primitive::mov<IntT>(destination_w(), source_r());
|
|
|
|
|
if constexpr (std::is_same_v<IntT, uint16_t>) {
|
2023-11-11 03:22:32 +00:00
|
|
|
|
context.registers.did_update(instruction.destination().source());
|
2023-11-11 03:11:52 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
2023-11-01 21:03:23 +00:00
|
|
|
|
|
2023-11-02 20:55:38 +00:00
|
|
|
|
case Operation::JO: jcc(context.flags.template condition<Condition::Overflow>()); return;
|
|
|
|
|
case Operation::JNO: jcc(!context.flags.template condition<Condition::Overflow>()); return;
|
|
|
|
|
case Operation::JB: jcc(context.flags.template condition<Condition::Below>()); return;
|
2023-11-07 14:58:42 +00:00
|
|
|
|
case Operation::JNB: jcc(!context.flags.template condition<Condition::Below>()); return;
|
2023-11-02 20:55:38 +00:00
|
|
|
|
case Operation::JZ: jcc(context.flags.template condition<Condition::Zero>()); return;
|
|
|
|
|
case Operation::JNZ: jcc(!context.flags.template condition<Condition::Zero>()); return;
|
|
|
|
|
case Operation::JBE: jcc(context.flags.template condition<Condition::BelowOrEqual>()); return;
|
|
|
|
|
case Operation::JNBE: jcc(!context.flags.template condition<Condition::BelowOrEqual>()); return;
|
|
|
|
|
case Operation::JS: jcc(context.flags.template condition<Condition::Sign>()); return;
|
|
|
|
|
case Operation::JNS: jcc(!context.flags.template condition<Condition::Sign>()); return;
|
2023-11-07 14:58:42 +00:00
|
|
|
|
case Operation::JP: jcc(!context.flags.template condition<Condition::ParityOdd>()); return;
|
2023-11-02 20:55:38 +00:00
|
|
|
|
case Operation::JNP: jcc(context.flags.template condition<Condition::ParityOdd>()); return;
|
|
|
|
|
case Operation::JL: jcc(context.flags.template condition<Condition::Less>()); return;
|
|
|
|
|
case Operation::JNL: jcc(!context.flags.template condition<Condition::Less>()); return;
|
|
|
|
|
case Operation::JLE: jcc(context.flags.template condition<Condition::LessOrEqual>()); return;
|
|
|
|
|
case Operation::JNLE: jcc(!context.flags.template condition<Condition::LessOrEqual>()); return;
|
2023-11-01 21:03:23 +00:00
|
|
|
|
|
2023-11-07 14:58:42 +00:00
|
|
|
|
case Operation::RCL: Primitive::rcl<IntT>(destination_rmw(), shift_count(), context); break;
|
|
|
|
|
case Operation::RCR: Primitive::rcr<IntT>(destination_rmw(), shift_count(), context); break;
|
|
|
|
|
case Operation::ROL: Primitive::rol<IntT>(destination_rmw(), shift_count(), context); break;
|
|
|
|
|
case Operation::ROR: Primitive::ror<IntT>(destination_rmw(), shift_count(), context); break;
|
|
|
|
|
case Operation::SAL: Primitive::sal<IntT>(destination_rmw(), shift_count(), context); break;
|
|
|
|
|
case Operation::SAR: Primitive::sar<IntT>(destination_rmw(), shift_count(), context); break;
|
|
|
|
|
case Operation::SHR: Primitive::shr<IntT>(destination_rmw(), shift_count(), context); break;
|
2023-11-01 21:03:23 +00:00
|
|
|
|
|
|
|
|
|
case Operation::CLC: Primitive::clc(context); return;
|
|
|
|
|
case Operation::CLD: Primitive::cld(context); return;
|
|
|
|
|
case Operation::CLI: Primitive::cli(context); return;
|
|
|
|
|
case Operation::STC: Primitive::stc(context); return;
|
|
|
|
|
case Operation::STD: Primitive::std(context); return;
|
|
|
|
|
case Operation::STI: Primitive::sti(context); return;
|
|
|
|
|
case Operation::CMC: Primitive::cmc(context); return;
|
2023-10-11 02:34:42 +00:00
|
|
|
|
|
2023-11-07 14:58:42 +00:00
|
|
|
|
case Operation::XCHG: Primitive::xchg<IntT>(destination_rmw(), source_rmw()); break;
|
2023-10-12 19:52:05 +00:00
|
|
|
|
|
2023-11-07 15:09:04 +00:00
|
|
|
|
case Operation::SALC: Primitive::salc(context.registers.al(), context); return;
|
2023-10-12 19:52:05 +00:00
|
|
|
|
case Operation::SETMO:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
if constexpr (ContextT::model == Model::i8086) {
|
2023-11-07 14:58:42 +00:00
|
|
|
|
Primitive::setmo<IntT>(destination_w(), context);
|
2023-10-29 20:19:10 +00:00
|
|
|
|
break;
|
2023-10-12 19:52:05 +00:00
|
|
|
|
} else {
|
2023-11-08 16:23:21 +00:00
|
|
|
|
// TODO: perform ENTER as of the 80186.
|
|
|
|
|
static_assert(int(Operation::SETMO) == int(Operation::ENTER));
|
2023-10-12 19:52:05 +00:00
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
case Operation::SETMOC:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
if constexpr (ContextT::model == Model::i8086) {
|
2023-11-01 02:04:26 +00:00
|
|
|
|
// Test CL out here to avoid taking a reference to memory if
|
|
|
|
|
// no write is going to occur.
|
2023-11-01 21:03:23 +00:00
|
|
|
|
if(context.registers.cl()) {
|
2023-11-07 14:58:42 +00:00
|
|
|
|
Primitive::setmo<IntT>(destination_w(), context);
|
2023-11-01 02:04:26 +00:00
|
|
|
|
}
|
2023-10-29 20:19:10 +00:00
|
|
|
|
break;
|
2023-10-12 19:52:05 +00:00
|
|
|
|
} else {
|
2023-11-08 16:23:21 +00:00
|
|
|
|
// TODO: perform BOUND as of the 80186.
|
|
|
|
|
static_assert(int(Operation::SETMOC) == int(Operation::BOUND));
|
2023-10-12 19:52:05 +00:00
|
|
|
|
}
|
|
|
|
|
return;
|
2023-10-13 01:12:03 +00:00
|
|
|
|
|
2023-11-08 15:52:36 +00:00
|
|
|
|
case Operation::OUT: Primitive::out<IntT>(port(instruction.destination().source()), pair_low(), context); return;
|
2023-11-08 16:23:21 +00:00
|
|
|
|
case Operation::IN: Primitive::in<IntT>(port(instruction.source().source()), pair_low(), context); return;
|
2023-10-22 02:37:25 +00:00
|
|
|
|
|
2023-11-01 21:03:23 +00:00
|
|
|
|
case Operation::XLAT: Primitive::xlat<AddressT>(instruction, context); return;
|
2023-10-18 19:59:39 +00:00
|
|
|
|
|
2023-11-11 03:11:52 +00:00
|
|
|
|
case Operation::POP:
|
|
|
|
|
destination_w() = Primitive::pop<IntT, false>(context);
|
|
|
|
|
if constexpr (std::is_same_v<IntT, uint16_t>) {
|
2023-11-11 03:22:32 +00:00
|
|
|
|
context.registers.did_update(instruction.destination().source());
|
2023-11-11 03:11:52 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
2023-11-07 14:58:42 +00:00
|
|
|
|
case Operation::PUSH:
|
|
|
|
|
Primitive::push<IntT, false>(source_rmw(), context); // PUSH SP modifies SP before pushing it;
|
|
|
|
|
// hence PUSH is sometimes read-modify-write.
|
|
|
|
|
break;
|
2023-11-08 15:52:36 +00:00
|
|
|
|
case Operation::POPF: Primitive::popf(context); return;
|
|
|
|
|
case Operation::PUSHF: Primitive::pushf(context); return;
|
2023-10-19 18:07:59 +00:00
|
|
|
|
|
|
|
|
|
case Operation::CMPS:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::cmps<IntT, AddressT, Repetition::None>(instruction, eCX(), eSI(), eDI(), context);
|
2023-11-08 15:52:36 +00:00
|
|
|
|
return;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
case Operation::CMPS_REPE:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::cmps<IntT, AddressT, Repetition::RepE>(instruction, eCX(), eSI(), eDI(), context);
|
2023-11-08 15:52:36 +00:00
|
|
|
|
return;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
case Operation::CMPS_REPNE:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::cmps<IntT, AddressT, Repetition::RepNE>(instruction, eCX(), eSI(), eDI(), context);
|
2023-11-08 15:52:36 +00:00
|
|
|
|
return;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
|
|
|
|
|
case Operation::SCAS:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::scas<IntT, AddressT, Repetition::None>(eCX(), eDI(), pair_low(), context);
|
2023-11-08 15:52:36 +00:00
|
|
|
|
return;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
case Operation::SCAS_REPE:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::scas<IntT, AddressT, Repetition::RepE>(eCX(), eDI(), pair_low(), context);
|
2023-11-08 15:52:36 +00:00
|
|
|
|
return;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
case Operation::SCAS_REPNE:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::scas<IntT, AddressT, Repetition::RepNE>(eCX(), eDI(), pair_low(), context);
|
2023-11-08 15:52:36 +00:00
|
|
|
|
return;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
|
2023-10-20 21:13:56 +00:00
|
|
|
|
case Operation::LODS:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::lods<IntT, AddressT, Repetition::None>(instruction, eCX(), eSI(), pair_low(), context);
|
2023-11-08 15:52:36 +00:00
|
|
|
|
return;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
case Operation::LODS_REP:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::lods<IntT, AddressT, Repetition::Rep>(instruction, eCX(), eSI(), pair_low(), context);
|
2023-11-08 15:52:36 +00:00
|
|
|
|
return;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
|
2023-10-21 01:46:47 +00:00
|
|
|
|
case Operation::MOVS:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::movs<IntT, AddressT, Repetition::None>(instruction, eCX(), eSI(), eDI(), context);
|
2023-10-27 03:08:07 +00:00
|
|
|
|
break;
|
|
|
|
|
case Operation::MOVS_REP:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::movs<IntT, AddressT, Repetition::Rep>(instruction, eCX(), eSI(), eDI(), context);
|
2023-10-21 01:46:47 +00:00
|
|
|
|
break;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
|
2023-10-21 01:46:47 +00:00
|
|
|
|
case Operation::STOS:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::stos<IntT, AddressT, Repetition::None>(eCX(), eDI(), pair_low(), context);
|
2023-10-21 01:46:47 +00:00
|
|
|
|
break;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
case Operation::STOS_REP:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::stos<IntT, AddressT, Repetition::Rep>(eCX(), eDI(), pair_low(), context);
|
2023-10-21 01:54:30 +00:00
|
|
|
|
break;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
|
2023-10-22 02:52:50 +00:00
|
|
|
|
case Operation::OUTS:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::outs<IntT, AddressT, Repetition::None>(instruction, eCX(), context.registers.dx(), eSI(), context);
|
2023-11-08 15:52:36 +00:00
|
|
|
|
return;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
case Operation::OUTS_REP:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::outs<IntT, AddressT, Repetition::Rep>(instruction, eCX(), context.registers.dx(), eSI(), context);
|
2023-11-08 15:52:36 +00:00
|
|
|
|
return;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
|
2023-10-22 02:52:50 +00:00
|
|
|
|
case Operation::INS:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::ins<IntT, AddressT, Repetition::None>(eCX(), context.registers.dx(), eDI(), context);
|
2023-10-27 03:08:07 +00:00
|
|
|
|
break;
|
|
|
|
|
case Operation::INS_REP:
|
2023-11-01 21:03:23 +00:00
|
|
|
|
Primitive::ins<IntT, AddressT, Repetition::Rep>(eCX(), context.registers.dx(), eDI(), context);
|
2023-10-22 02:52:50 +00:00
|
|
|
|
break;
|
2023-10-05 18:37:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 20:49:02 +00:00
|
|
|
|
// Write to memory if required to complete this operation.
|
2023-11-01 21:03:23 +00:00
|
|
|
|
//
|
2023-11-08 16:23:21 +00:00
|
|
|
|
// This is not currently handled via RAII because of the amount of context that would need to place onto the stack;
|
|
|
|
|
// instead code has been set up to make sure there is only at most one writeable target on loan for potential
|
|
|
|
|
// write back. I might flip-flop on this, especially if I can verify whether extra stack context is easily
|
|
|
|
|
// optimised out.
|
2023-11-01 21:03:23 +00:00
|
|
|
|
context.memory.template write_back<IntT>();
|
2023-10-05 18:37:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-08 16:23:21 +00:00
|
|
|
|
//
|
|
|
|
|
// Public function; just a trampoline into a version of perform templated on data and address size.
|
|
|
|
|
//
|
|
|
|
|
// Which, yes, means there's an outer switch leading to an inner switch, which could be reduced to one big switch.
|
|
|
|
|
// It'd be a substantial effort to find the most neat expression of that, I think, so it is not currently done.
|
|
|
|
|
//
|
2023-10-05 20:49:02 +00:00
|
|
|
|
template <
|
2023-10-05 18:37:58 +00:00
|
|
|
|
typename InstructionT,
|
2023-11-01 21:03:23 +00:00
|
|
|
|
typename ContextT
|
2023-10-05 18:37:58 +00:00
|
|
|
|
> void perform(
|
|
|
|
|
const InstructionT &instruction,
|
2023-11-01 21:03:23 +00:00
|
|
|
|
ContextT &context
|
2023-10-05 18:37:58 +00:00
|
|
|
|
) {
|
2023-10-25 20:00:01 +00:00
|
|
|
|
auto size = [](DataSize operation_size, AddressSize address_size) constexpr -> int {
|
|
|
|
|
return int(operation_size) + (int(address_size) << 2);
|
|
|
|
|
};
|
|
|
|
|
|
2023-10-25 20:15:08 +00:00
|
|
|
|
// Dispatch to a function specialised on data and address size.
|
2023-10-25 20:00:01 +00:00
|
|
|
|
switch(size(instruction.operation_size(), instruction.address_size())) {
|
|
|
|
|
// 16-bit combinations.
|
|
|
|
|
case size(DataSize::Byte, AddressSize::b16):
|
2023-11-01 21:03:23 +00:00
|
|
|
|
perform<DataSize::Byte, AddressSize::b16>(instruction, context);
|
2023-10-25 20:00:01 +00:00
|
|
|
|
return;
|
|
|
|
|
case size(DataSize::Word, AddressSize::b16):
|
2023-11-01 21:03:23 +00:00
|
|
|
|
perform<DataSize::Word, AddressSize::b16>(instruction, context);
|
2023-10-25 20:00:01 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// 32-bit combinations.
|
2023-10-25 20:15:08 +00:00
|
|
|
|
//
|
|
|
|
|
// The if constexprs below ensure that `perform` isn't compiled for incompatible data or address size and
|
|
|
|
|
// model combinations. So if a caller nominates a 16-bit model it can supply registers and memory objects
|
|
|
|
|
// that don't implement 32-bit registers or accesses.
|
2023-10-25 20:00:01 +00:00
|
|
|
|
case size(DataSize::Byte, AddressSize::b32):
|
2023-11-01 21:03:23 +00:00
|
|
|
|
if constexpr (is_32bit(ContextT::model)) {
|
|
|
|
|
perform<DataSize::Byte, AddressSize::b32>(instruction, context);
|
2023-10-25 20:00:01 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-10-05 20:49:02 +00:00
|
|
|
|
break;
|
2023-10-25 20:00:01 +00:00
|
|
|
|
case size(DataSize::Word, AddressSize::b32):
|
2023-11-01 21:03:23 +00:00
|
|
|
|
if constexpr (is_32bit(ContextT::model)) {
|
|
|
|
|
perform<DataSize::Word, AddressSize::b32>(instruction, context);
|
2023-10-25 20:00:01 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-10-05 20:49:02 +00:00
|
|
|
|
break;
|
2023-10-25 20:00:01 +00:00
|
|
|
|
case size(DataSize::DWord, AddressSize::b16):
|
2023-11-01 21:03:23 +00:00
|
|
|
|
if constexpr (is_32bit(ContextT::model)) {
|
|
|
|
|
perform<DataSize::DWord, AddressSize::b16>(instruction, context);
|
2023-10-25 20:00:01 +00:00
|
|
|
|
return;
|
2023-10-10 01:50:17 +00:00
|
|
|
|
}
|
2023-10-05 20:49:02 +00:00
|
|
|
|
break;
|
2023-10-25 20:00:01 +00:00
|
|
|
|
case size(DataSize::DWord, AddressSize::b32):
|
2023-11-01 21:03:23 +00:00
|
|
|
|
if constexpr (is_32bit(ContextT::model)) {
|
|
|
|
|
perform<DataSize::DWord, AddressSize::b32>(instruction, context);
|
2023-10-25 20:00:01 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default: break;
|
2023-10-05 18:37:58 +00:00
|
|
|
|
}
|
2023-10-25 20:00:01 +00:00
|
|
|
|
|
2023-10-25 20:15:08 +00:00
|
|
|
|
// This is reachable only if the data and address size combination in use isn't available
|
|
|
|
|
// on the processor model nominated.
|
2023-10-25 20:00:01 +00:00
|
|
|
|
assert(false);
|
2023-10-05 20:49:02 +00:00
|
|
|
|
}
|
2023-10-05 18:37:58 +00:00
|
|
|
|
|
2023-11-01 21:03:23 +00:00
|
|
|
|
template <
|
|
|
|
|
typename ContextT
|
|
|
|
|
> void interrupt(
|
|
|
|
|
int index,
|
|
|
|
|
ContextT &context
|
|
|
|
|
) {
|
|
|
|
|
const uint32_t address = static_cast<uint32_t>(index) << 2;
|
|
|
|
|
context.memory.preauthorise_read(address, sizeof(uint16_t) * 2);
|
|
|
|
|
context.memory.preauthorise_stack_write(sizeof(uint16_t) * 3);
|
|
|
|
|
|
2023-11-02 19:37:59 +00:00
|
|
|
|
const uint16_t ip = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(address);
|
|
|
|
|
const uint16_t cs = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(address + 2);
|
2023-11-01 21:03:23 +00:00
|
|
|
|
|
2023-11-02 20:55:38 +00:00
|
|
|
|
auto flags = context.flags.get();
|
2023-11-02 03:39:52 +00:00
|
|
|
|
Primitive::push<uint16_t, true>(flags, context);
|
2023-11-02 20:55:38 +00:00
|
|
|
|
context.flags.template set_from<Flag::Interrupt, Flag::Trap>(0);
|
2023-11-01 21:03:23 +00:00
|
|
|
|
|
|
|
|
|
// Push CS and IP.
|
|
|
|
|
Primitive::push<uint16_t, true>(context.registers.cs(), context);
|
|
|
|
|
Primitive::push<uint16_t, true>(context.registers.ip(), context);
|
|
|
|
|
|
|
|
|
|
// Set new destination.
|
|
|
|
|
context.flow_controller.jump(cs, ip);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 18:37:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* PerformImplementation_h */
|