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-10-05 19:49:07 +00:00
|
|
|
|
#include "../../../Numeric/Carry.hpp"
|
2023-10-06 02:27:52 +00:00
|
|
|
|
#include "../../../Numeric/RegisterSizes.hpp"
|
2023-10-06 17:22:35 +00:00
|
|
|
|
#include "../Interrupts.hpp"
|
2023-10-05 19:49:07 +00:00
|
|
|
|
|
2023-10-05 18:37:58 +00:00
|
|
|
|
namespace InstructionSet::x86 {
|
|
|
|
|
|
|
|
|
|
namespace Primitive {
|
|
|
|
|
|
2023-10-05 19:49:07 +00:00
|
|
|
|
//
|
|
|
|
|
// BEGIN TEMPORARY COPY AND PASTE SECTION.
|
|
|
|
|
//
|
|
|
|
|
// The following are largely excised from the M68k PerformImplementation.hpp; if there proves to be no
|
|
|
|
|
// reason further to specialise them, there'll be a factoring out. In some cases I've tightened the documentation.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
/// @returns An int of type @c IntT with only the most-significant bit set.
|
|
|
|
|
template <typename IntT> constexpr IntT top_bit() {
|
|
|
|
|
static_assert(!std::numeric_limits<IntT>::is_signed);
|
|
|
|
|
constexpr IntT max = std::numeric_limits<IntT>::max();
|
|
|
|
|
return max - (max >> 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// @returns The number of bits in @c IntT.
|
|
|
|
|
template <typename IntT> constexpr int bit_size() {
|
|
|
|
|
return sizeof(IntT) * 8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// @returns An int with the top bit indicating whether overflow occurred during the calculation of
|
|
|
|
|
/// • @c lhs + @c rhs (if @c is_add is true); or
|
|
|
|
|
/// • @c lhs - @c rhs (if @c is_add is false)
|
|
|
|
|
/// and the result was @c result. All other bits will be clear.
|
|
|
|
|
template <bool is_add, typename IntT>
|
|
|
|
|
IntT overflow(IntT lhs, IntT rhs, IntT result) {
|
|
|
|
|
const IntT output_changed = result ^ rhs;
|
|
|
|
|
const IntT input_differed = lhs ^ rhs;
|
|
|
|
|
|
|
|
|
|
if constexpr (is_add) {
|
|
|
|
|
return top_bit<IntT>() & output_changed & ~input_differed;
|
|
|
|
|
} else {
|
|
|
|
|
return top_bit<IntT>() & output_changed & input_differed;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// END COPY AND PASTE SECTION.
|
|
|
|
|
//
|
|
|
|
|
|
2023-10-06 15:10:54 +00:00
|
|
|
|
//
|
|
|
|
|
// Comments below on intended functioning of each operation 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.
|
|
|
|
|
//
|
|
|
|
|
// Order Number 243191; e.g. https://www.ardent-tool.com/CPU/docs/Intel/IA/243191-002.pdf
|
|
|
|
|
//
|
|
|
|
|
|
2023-10-06 15:31:45 +00:00
|
|
|
|
inline void aaa(CPU::RegisterPair16 &ax, Status &status) { // P. 313
|
2023-10-05 18:37:58 +00:00
|
|
|
|
/*
|
|
|
|
|
IF ((AL AND 0FH) > 9) OR (AF = 1)
|
|
|
|
|
THEN
|
|
|
|
|
AL ← (AL + 6);
|
|
|
|
|
AH ← AH + 1;
|
|
|
|
|
AF ← 1;
|
|
|
|
|
CF ← 1;
|
|
|
|
|
ELSE
|
|
|
|
|
AF ← 0;
|
|
|
|
|
CF ← 0;
|
|
|
|
|
FI;
|
|
|
|
|
AL ← AL AND 0FH;
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
The AF and CF flags are set to 1 if the adjustment results in a decimal carry;
|
|
|
|
|
otherwise they are cleared to 0. The OF, SF, ZF, and PF flags are undefined.
|
|
|
|
|
*/
|
|
|
|
|
if((ax.halves.low & 0x0f) > 9 || status.auxiliary_carry) {
|
|
|
|
|
ax.halves.low += 6;
|
|
|
|
|
++ax.halves.high;
|
|
|
|
|
status.auxiliary_carry = status.carry = 1;
|
|
|
|
|
} else {
|
|
|
|
|
status.auxiliary_carry = status.carry = 0;
|
|
|
|
|
}
|
2023-10-06 02:27:52 +00:00
|
|
|
|
ax.halves.low &= 0x0f;
|
2023-10-05 18:37:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-06 15:11:29 +00:00
|
|
|
|
inline void aad(CPU::RegisterPair16 &ax, uint8_t imm, Status &status) {
|
2023-10-05 18:37:58 +00:00
|
|
|
|
/*
|
|
|
|
|
tempAL ← AL;
|
|
|
|
|
tempAH ← AH;
|
|
|
|
|
AL ← (tempAL + (tempAH * imm8)) AND FFH; (* imm8 is set to 0AH for the AAD mnemonic *)
|
|
|
|
|
AH ← 0
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
The SF, ZF, and PF flags are set according to the result;
|
|
|
|
|
the OF, AF, and CF flags are undefined.
|
|
|
|
|
*/
|
|
|
|
|
ax.halves.low = ax.halves.low + (ax.halves.high * imm);
|
|
|
|
|
ax.halves.high = 0;
|
|
|
|
|
status.sign = ax.halves.low & 0x80;
|
|
|
|
|
status.parity = status.zero = ax.halves.low;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-06 17:22:35 +00:00
|
|
|
|
template <typename FlowControllerT>
|
|
|
|
|
inline void aam(CPU::RegisterPair16 &ax, uint8_t imm, Status &status, FlowControllerT &flow_controller) {
|
2023-10-05 18:52:24 +00:00
|
|
|
|
/*
|
|
|
|
|
tempAL ← AL;
|
|
|
|
|
AH ← tempAL / imm8; (* imm8 is set to 0AH for the AAD mnemonic *)
|
|
|
|
|
AL ← tempAL MOD imm8;
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
The SF, ZF, and PF flags are set according to the result.
|
|
|
|
|
The OF, AF, and CF flags are undefined.
|
|
|
|
|
*/
|
2023-10-06 15:43:18 +00:00
|
|
|
|
/*
|
|
|
|
|
If ... an immediate value of 0 is used, it will cause a #DE (divide error) exception.
|
|
|
|
|
*/
|
2023-10-06 17:22:35 +00:00
|
|
|
|
if(!imm) {
|
|
|
|
|
flow_controller.interrupt(Interrupt::DivideByZero);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 18:52:24 +00:00
|
|
|
|
ax.halves.high = ax.halves.low / imm;
|
|
|
|
|
ax.halves.low = ax.halves.low % imm;
|
|
|
|
|
status.sign = ax.halves.low & 0x80;
|
|
|
|
|
status.parity = status.zero = ax.halves.low;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-06 15:11:29 +00:00
|
|
|
|
inline void aas(CPU::RegisterPair16 &ax, Status &status) {
|
2023-10-05 18:52:24 +00:00
|
|
|
|
/*
|
|
|
|
|
IF ((AL AND 0FH) > 9) OR (AF = 1)
|
|
|
|
|
THEN
|
|
|
|
|
AL ← AL – 6;
|
|
|
|
|
AH ← AH – 1;
|
|
|
|
|
AF ← 1;
|
|
|
|
|
CF ← 1;
|
|
|
|
|
ELSE
|
|
|
|
|
CF ← 0;
|
|
|
|
|
AF ← 0;
|
|
|
|
|
FI;
|
|
|
|
|
AL ← AL AND 0FH;
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
The AF and CF flags are set to 1 if there is a decimal borrow;
|
|
|
|
|
otherwise, they are cleared to 0. The OF, SF, ZF, and PF flags are undefined.
|
|
|
|
|
*/
|
|
|
|
|
if((ax.halves.low & 0x0f) > 9 || status.auxiliary_carry) {
|
|
|
|
|
ax.halves.low -= 6;
|
|
|
|
|
--ax.halves.high;
|
|
|
|
|
status.auxiliary_carry = status.carry = 1;
|
|
|
|
|
} else {
|
|
|
|
|
status.auxiliary_carry = status.carry = 0;
|
|
|
|
|
}
|
|
|
|
|
ax.halves.low &= 0x0f;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 19:49:07 +00:00
|
|
|
|
template <typename IntT>
|
|
|
|
|
void adc(IntT &destination, IntT source, Status &status) {
|
|
|
|
|
/*
|
|
|
|
|
DEST ← DEST + SRC + CF;
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
The OF, SF, ZF, AF, CF, and PF flags are set according to the result.
|
|
|
|
|
*/
|
|
|
|
|
const IntT result = destination + source + status.carry_bit<IntT>();
|
|
|
|
|
|
|
|
|
|
status.carry = Numeric::carried_out<bit_size<IntT>() - 1>(destination, source, result);
|
|
|
|
|
status.auxiliary_carry = Numeric::carried_in<4>(destination, source, result);
|
2023-10-08 17:39:46 +00:00
|
|
|
|
status.sign = result & top_bit<IntT>();
|
|
|
|
|
status.zero = status.parity = result;
|
2023-10-05 19:49:07 +00:00
|
|
|
|
status.overflow = overflow<true, IntT>(destination, source, result);
|
|
|
|
|
|
|
|
|
|
destination = result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename IntT>
|
|
|
|
|
void add(IntT &destination, IntT source, Status &status) {
|
|
|
|
|
/*
|
|
|
|
|
DEST ← DEST + SRC;
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
The OF, SF, ZF, AF, CF, and PF flags are set according to the result.
|
|
|
|
|
*/
|
|
|
|
|
const IntT result = destination + source;
|
|
|
|
|
|
|
|
|
|
status.carry = Numeric::carried_out<bit_size<IntT>() - 1>(destination, source, result);
|
|
|
|
|
status.auxiliary_carry = Numeric::carried_in<4>(destination, source, result);
|
2023-10-08 17:39:46 +00:00
|
|
|
|
status.sign = result & top_bit<IntT>();
|
|
|
|
|
status.zero = status.parity = result;
|
2023-10-05 19:49:07 +00:00
|
|
|
|
status.overflow = overflow<true, IntT>(destination, source, result);
|
|
|
|
|
|
|
|
|
|
destination = result;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-09 02:18:40 +00:00
|
|
|
|
template <typename IntT>
|
|
|
|
|
void and_(IntT &destination, IntT source, Status &status) {
|
|
|
|
|
/*
|
|
|
|
|
DEST ← DEST AND SRC;
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
The OF and CF flags are cleared; the SF, ZF, and PF flags are set according to the result.
|
|
|
|
|
The state of the AF flag is undefined.
|
|
|
|
|
*/
|
|
|
|
|
destination &= source;
|
|
|
|
|
|
|
|
|
|
status.overflow = 0;
|
|
|
|
|
status.carry = 0;
|
|
|
|
|
status.sign = destination & top_bit<IntT>();
|
|
|
|
|
status.zero = status.parity = destination;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 18:37:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-08 17:34:28 +00:00
|
|
|
|
template <Model model, DataSize data_size, typename InstructionT, typename RegistersT, typename MemoryT>
|
2023-10-09 01:41:36 +00:00
|
|
|
|
typename DataSizeType<data_size>::type *
|
|
|
|
|
resolve(
|
|
|
|
|
InstructionT &instruction,
|
|
|
|
|
Source source,
|
|
|
|
|
DataPointer pointer,
|
|
|
|
|
RegistersT ®isters,
|
|
|
|
|
MemoryT &memory,
|
|
|
|
|
typename DataSizeType<data_size>::type *none = nullptr,
|
|
|
|
|
typename DataSizeType<data_size>::type *immediate = nullptr
|
|
|
|
|
) {
|
2023-10-08 17:34:28 +00:00
|
|
|
|
// Rules:
|
|
|
|
|
//
|
|
|
|
|
// * if this is a memory access, set target_address and break;
|
|
|
|
|
// * otherwise return the appropriate value.
|
|
|
|
|
uint32_t address;
|
2023-10-08 17:47:43 +00:00
|
|
|
|
switch(source) {
|
2023-10-08 17:34:28 +00:00
|
|
|
|
case Source::eAX:
|
|
|
|
|
// Slightly contorted if chain here and below:
|
|
|
|
|
//
|
|
|
|
|
// (i) does the `constexpr` version of a `switch`; and
|
|
|
|
|
// (i) ensures .eax() etc aren't called on @c registers for 16-bit processors, so they need not implement 32-bit storage.
|
|
|
|
|
if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return ®isters.eax(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Word) { return ®isters.ax(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Byte) { return ®isters.al(); }
|
|
|
|
|
else { return nullptr; }
|
|
|
|
|
case Source::eCX:
|
|
|
|
|
if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return ®isters.ecx(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Word) { return ®isters.cx(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Byte) { return ®isters.cl(); }
|
|
|
|
|
else { return nullptr; }
|
|
|
|
|
case Source::eDX:
|
|
|
|
|
if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return ®isters.edx(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Word) { return ®isters.dx(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Byte) { return ®isters.dl(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::DWord) { return nullptr; }
|
|
|
|
|
case Source::eBX:
|
|
|
|
|
if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return ®isters.ebx(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Word) { return ®isters.bx(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Byte) { return ®isters.bl(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::DWord) { return nullptr; }
|
|
|
|
|
case Source::eSPorAH:
|
|
|
|
|
if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return ®isters.esp(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Word) { return ®isters.sp(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Byte) { return ®isters.ah(); }
|
|
|
|
|
else { return nullptr; }
|
|
|
|
|
case Source::eBPorCH:
|
|
|
|
|
if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return ®isters.ebp(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Word) { return ®isters.bp(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Byte) { return ®isters.ch(); }
|
|
|
|
|
else { return nullptr; }
|
|
|
|
|
case Source::eSIorDH:
|
|
|
|
|
if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return ®isters.esi(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Word) { return ®isters.si(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Byte) { return ®isters.dh(); }
|
|
|
|
|
else { return nullptr; }
|
|
|
|
|
case Source::eDIorBH:
|
|
|
|
|
if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return ®isters.edi(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Word) { return ®isters.di(); }
|
|
|
|
|
else if constexpr (data_size == DataSize::Byte) { return ®isters.bh(); }
|
|
|
|
|
else { return nullptr; }
|
|
|
|
|
|
|
|
|
|
case Source::ES: if constexpr (data_size == DataSize::Word) return ®isters.es(); else return nullptr;
|
|
|
|
|
case Source::CS: if constexpr (data_size == DataSize::Word) return ®isters.cs(); else return nullptr;
|
|
|
|
|
case Source::SS: if constexpr (data_size == DataSize::Word) return ®isters.ss(); else return nullptr;
|
|
|
|
|
case Source::DS: if constexpr (data_size == DataSize::Word) return ®isters.ds(); else return nullptr;
|
|
|
|
|
|
|
|
|
|
// 16-bit models don't have FS and GS.
|
|
|
|
|
case Source::FS: if constexpr (is_32bit(model) && data_size == DataSize::Word) return ®isters.fs(); else return nullptr;
|
|
|
|
|
case Source::GS: if constexpr (is_32bit(model) && data_size == DataSize::Word) return ®isters.gs(); else return nullptr;
|
|
|
|
|
|
2023-10-09 01:41:36 +00:00
|
|
|
|
case Source::Immediate:
|
|
|
|
|
*immediate = instruction.operand();
|
|
|
|
|
return immediate;
|
2023-10-08 17:34:28 +00:00
|
|
|
|
|
2023-10-08 17:50:36 +00:00
|
|
|
|
case Source::None: return none;
|
2023-10-08 17:34:28 +00:00
|
|
|
|
|
2023-10-08 17:50:36 +00:00
|
|
|
|
// TODO: non-word indexes and bases in the next two cases.
|
|
|
|
|
case Source::Indirect: {
|
|
|
|
|
uint16_t zero = 0;
|
|
|
|
|
address = *resolve<model, DataSize::Word>(instruction, pointer.index(), pointer, registers, memory, &zero);
|
2023-10-08 17:47:43 +00:00
|
|
|
|
if constexpr (is_32bit(model)) {
|
|
|
|
|
address <<= pointer.scale();
|
|
|
|
|
}
|
|
|
|
|
address += instruction.offset() + *resolve<model, DataSize::Word>(instruction, pointer.base(), pointer, registers, memory);
|
2023-10-08 17:50:36 +00:00
|
|
|
|
} break;
|
2023-10-08 17:34:28 +00:00
|
|
|
|
|
2023-10-08 17:50:36 +00:00
|
|
|
|
case Source::IndirectNoBase: {
|
|
|
|
|
uint16_t zero = 0;
|
|
|
|
|
address = *resolve<model, DataSize::Word>(instruction, pointer.index(), pointer, registers, memory, &zero);
|
2023-10-08 17:47:43 +00:00
|
|
|
|
if constexpr (is_32bit(model)) {
|
|
|
|
|
address <<= pointer.scale();
|
|
|
|
|
}
|
|
|
|
|
address += instruction.offset();
|
2023-10-08 17:50:36 +00:00
|
|
|
|
} break;
|
2023-10-08 17:34:28 +00:00
|
|
|
|
|
|
|
|
|
case Source::DirectAddress:
|
|
|
|
|
address = instruction.offset();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If execution has reached here then a memory fetch is required.
|
|
|
|
|
// Do it and exit.
|
2023-10-08 17:47:43 +00:00
|
|
|
|
const Source segment = pointer.segment(instruction.segment_override());
|
2023-10-08 17:34:28 +00:00
|
|
|
|
using IntT = typename DataSizeType<data_size>::type;
|
|
|
|
|
return &memory.template access<IntT>(segment, address);
|
|
|
|
|
};
|
|
|
|
|
|
2023-10-05 18:37:58 +00:00
|
|
|
|
template <
|
|
|
|
|
Model model,
|
|
|
|
|
DataSize data_size,
|
2023-10-05 20:49:02 +00:00
|
|
|
|
typename InstructionT,
|
|
|
|
|
typename FlowControllerT,
|
|
|
|
|
typename RegistersT,
|
|
|
|
|
typename MemoryT,
|
|
|
|
|
typename IOT
|
2023-10-05 18:37:58 +00:00
|
|
|
|
> void perform(
|
2023-10-05 20:49:02 +00:00
|
|
|
|
const InstructionT &instruction,
|
2023-10-05 18:37:58 +00:00
|
|
|
|
Status &status,
|
2023-10-05 20:49:02 +00:00
|
|
|
|
[[maybe_unused]] FlowControllerT &flow_controller,
|
|
|
|
|
RegistersT ®isters,
|
|
|
|
|
[[maybe_unused]] MemoryT &memory,
|
|
|
|
|
[[maybe_unused]] IOT &io
|
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-05 21:06:00 +00:00
|
|
|
|
using AddressT = typename AddressT<is_32bit(model)>::type;
|
2023-10-05 20:49:02 +00:00
|
|
|
|
|
2023-10-06 02:27:52 +00:00
|
|
|
|
// Establish source() and destination() shorthand to fetch data if necessary.
|
2023-10-09 01:41:36 +00:00
|
|
|
|
IntT immediate;
|
|
|
|
|
auto source = [&]() -> IntT& {
|
|
|
|
|
return *resolve<model, data_size>(
|
|
|
|
|
instruction,
|
|
|
|
|
instruction.source().template source<false>(),
|
|
|
|
|
instruction.source(),
|
|
|
|
|
registers,
|
|
|
|
|
memory,
|
|
|
|
|
nullptr,
|
|
|
|
|
&immediate);
|
|
|
|
|
};
|
|
|
|
|
auto destination = [&]() -> IntT& {
|
|
|
|
|
return *resolve<model, data_size>(
|
|
|
|
|
instruction,
|
|
|
|
|
instruction.destination().template source<false>(),
|
|
|
|
|
instruction.destination(),
|
|
|
|
|
registers,
|
|
|
|
|
memory,
|
|
|
|
|
nullptr,
|
|
|
|
|
&immediate);
|
|
|
|
|
};
|
2023-10-05 20:49:02 +00:00
|
|
|
|
|
|
|
|
|
// Guide to the below:
|
|
|
|
|
//
|
|
|
|
|
// * use hard-coded register names where appropriate;
|
|
|
|
|
// * return directly if there is definitely no possible write back to RAM;
|
|
|
|
|
// * otherwise use the source() and destination() lambdas, and break in order to allow a writeback if necessary.
|
|
|
|
|
switch(instruction.operation) {
|
2023-10-06 15:07:33 +00:00
|
|
|
|
default: return;
|
|
|
|
|
//assert(false);
|
2023-10-06 02:27:52 +00:00
|
|
|
|
|
2023-10-06 17:22:35 +00:00
|
|
|
|
case Operation::AAA: Primitive::aaa(registers.axp(), status); return;
|
|
|
|
|
case Operation::AAD: Primitive::aad(registers.axp(), instruction.operand(), status); return;
|
|
|
|
|
case Operation::AAM: Primitive::aam(registers.axp(), instruction.operand(), status, flow_controller); return;
|
|
|
|
|
case Operation::AAS: Primitive::aas(registers.axp(), status); return;
|
2023-10-05 20:49:02 +00:00
|
|
|
|
|
2023-10-06 17:22:35 +00:00
|
|
|
|
case Operation::ADC: Primitive::adc(destination(), source(), status); break;
|
|
|
|
|
case Operation::ADD: Primitive::add(destination(), source(), status); break;
|
2023-10-09 02:18:40 +00:00
|
|
|
|
|
|
|
|
|
case Operation::AND: Primitive::and_(destination(), source(), status); 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-10-09 02:11:05 +00:00
|
|
|
|
memory.template write_back<IntT>();
|
2023-10-05 18:37:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 20:49:02 +00:00
|
|
|
|
template <
|
2023-10-05 18:37:58 +00:00
|
|
|
|
Model model,
|
|
|
|
|
typename InstructionT,
|
|
|
|
|
typename FlowControllerT,
|
|
|
|
|
typename RegistersT,
|
|
|
|
|
typename MemoryT,
|
2023-10-05 20:49:02 +00:00
|
|
|
|
typename IOT
|
2023-10-05 18:37:58 +00:00
|
|
|
|
> void perform(
|
|
|
|
|
const InstructionT &instruction,
|
|
|
|
|
Status &status,
|
|
|
|
|
FlowControllerT &flow_controller,
|
|
|
|
|
RegistersT ®isters,
|
|
|
|
|
MemoryT &memory,
|
|
|
|
|
IOT &io
|
|
|
|
|
) {
|
2023-10-05 20:49:02 +00:00
|
|
|
|
// Dispatch to a function just like this that is specialised on data size.
|
|
|
|
|
// Fetching will occur in that specialised function, per the overlapping
|
|
|
|
|
// meaning of register names.
|
|
|
|
|
switch(instruction.operation_size()) {
|
|
|
|
|
case DataSize::Byte:
|
|
|
|
|
perform<model, DataSize::Byte>(instruction, status, flow_controller, registers, memory, io);
|
|
|
|
|
break;
|
|
|
|
|
case DataSize::Word:
|
|
|
|
|
perform<model, DataSize::Word>(instruction, status, flow_controller, registers, memory, io);
|
|
|
|
|
break;
|
|
|
|
|
case DataSize::DWord:
|
|
|
|
|
perform<model, DataSize::DWord>(instruction, status, flow_controller, registers, memory, io);
|
|
|
|
|
break;
|
|
|
|
|
case DataSize::None:
|
|
|
|
|
perform<model, DataSize::None>(instruction, status, flow_controller, registers, memory, io);
|
|
|
|
|
break;
|
2023-10-05 18:37:58 +00:00
|
|
|
|
}
|
2023-10-05 20:49:02 +00:00
|
|
|
|
}
|
2023-10-05 18:37:58 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* PerformImplementation_h */
|