1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-23 18:31:53 +00:00
CLK/InstructionSets/x86/Implementation/PerformImplementation.hpp

1935 lines
67 KiB
C++
Raw Normal View History

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-10-05 19:49:07 +00:00
#include "../../../Numeric/Carry.hpp"
#include "../../../Numeric/RegisterSizes.hpp"
#include "../Interrupts.hpp"
2023-10-05 19:49:07 +00:00
2023-10-11 02:35:25 +00:00
#include <utility>
2023-10-05 18:37:58 +00:00
namespace InstructionSet::x86 {
template <Model model, typename IntT, AccessType access, typename InstructionT, typename RegistersT, typename MemoryT>
2023-10-09 15:46:59 +00:00
IntT *resolve(
InstructionT &instruction,
Source source,
DataPointer pointer,
RegistersT &registers,
MemoryT &memory,
IntT *none = nullptr,
IntT *immediate = nullptr
);
template <Model model, Source source, typename IntT, AccessType access, typename InstructionT, typename RegistersT, typename MemoryT>
2023-10-09 15:46:59 +00:00
uint32_t address(
InstructionT &instruction,
DataPointer pointer,
RegistersT &registers,
MemoryT &memory
) {
// TODO: non-word indexes and bases.
2023-10-09 15:49:38 +00:00
if constexpr (source == Source::DirectAddress) {
return instruction.offset();
}
2023-10-09 15:46:59 +00:00
2023-10-09 15:49:38 +00:00
uint32_t address;
uint16_t zero = 0;
address = *resolve<model, uint16_t, access>(instruction, pointer.index(), pointer, registers, memory, &zero);
2023-10-09 15:49:38 +00:00
if constexpr (is_32bit(model)) {
address <<= pointer.scale();
}
address += instruction.offset();
2023-10-09 15:46:59 +00:00
2023-10-09 15:49:38 +00:00
if constexpr (source == Source::IndirectNoBase) {
return address;
2023-10-09 15:46:59 +00:00
}
return address + *resolve<model, uint16_t, access>(instruction, pointer.base(), pointer, registers, memory);
2023-10-09 15:46:59 +00:00
}
template <Model model, typename IntT, AccessType access, Source source, typename RegistersT>
2023-10-23 02:15:27 +00:00
IntT *register_(RegistersT &registers) {
2023-10-09 15:46:59 +00:00
switch(source) {
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) && std::is_same_v<IntT, uint32_t>) { return &registers.eax(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &registers.ax(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &registers.al(); }
else { return nullptr; }
case Source::eCX:
if constexpr (is_32bit(model) && std::is_same_v<IntT, uint32_t>) { return &registers.ecx(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &registers.cx(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &registers.cl(); }
else { return nullptr; }
case Source::eDX:
if constexpr (is_32bit(model) && std::is_same_v<IntT, uint32_t>) { return &registers.edx(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &registers.dx(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &registers.dl(); }
else if constexpr (std::is_same_v<IntT, uint32_t>) { return nullptr; }
case Source::eBX:
if constexpr (is_32bit(model) && std::is_same_v<IntT, uint32_t>) { return &registers.ebx(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &registers.bx(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &registers.bl(); }
else if constexpr (std::is_same_v<IntT, uint32_t>) { return nullptr; }
case Source::eSPorAH:
if constexpr (is_32bit(model) && std::is_same_v<IntT, uint32_t>) { return &registers.esp(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &registers.sp(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &registers.ah(); }
else { return nullptr; }
case Source::eBPorCH:
if constexpr (is_32bit(model) && std::is_same_v<IntT, uint32_t>) { return &registers.ebp(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &registers.bp(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &registers.ch(); }
else { return nullptr; }
case Source::eSIorDH:
if constexpr (is_32bit(model) && std::is_same_v<IntT, uint32_t>) { return &registers.esi(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &registers.si(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &registers.dh(); }
else { return nullptr; }
case Source::eDIorBH:
if constexpr (is_32bit(model) && std::is_same_v<IntT, uint32_t>) { return &registers.edi(); }
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &registers.di(); }
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &registers.bh(); }
else { return nullptr; }
2023-10-23 02:15:27 +00:00
default: return nullptr;
}
}
template <Model model, typename IntT, AccessType access, typename InstructionT, typename RegistersT, typename MemoryT>
2023-10-23 02:15:27 +00:00
uint32_t address(
InstructionT &instruction,
DataPointer pointer,
RegistersT &registers,
MemoryT &memory
) {
// TODO: at least on the 8086 this isn't how register 'addresses' are resolved; instead whatever was the last computed address
// remains in the address register and is returned. Find out what other x86s do and make a decision.
switch(pointer.source()) {
2023-10-23 02:15:27 +00:00
default: return 0;
case Source::eAX: return *register_<model, IntT, access, Source::eAX>(registers);
case Source::eCX: return *register_<model, IntT, access, Source::eCX>(registers);
case Source::eDX: return *register_<model, IntT, access, Source::eDX>(registers);
case Source::eBX: return *register_<model, IntT, access, Source::eBX>(registers);
case Source::eSPorAH: return *register_<model, IntT, access, Source::eSPorAH>(registers);
case Source::eBPorCH: return *register_<model, IntT, access, Source::eBPorCH>(registers);
case Source::eSIorDH: return *register_<model, IntT, access, Source::eSIorDH>(registers);
case Source::eDIorBH: return *register_<model, IntT, access, Source::eDIorBH>(registers);
case Source::Indirect: return address<model, Source::Indirect, IntT, access>(instruction, pointer, registers, memory);
case Source::IndirectNoBase: return address<model, Source::IndirectNoBase, IntT, access>(instruction, pointer, registers, memory);
case Source::DirectAddress: return address<model, Source::DirectAddress, IntT, access>(instruction, pointer, registers, memory);
2023-10-23 02:15:27 +00:00
}
}
template <Model model, typename IntT, AccessType access, typename InstructionT, typename RegistersT, typename MemoryT>
2023-10-23 02:15:27 +00:00
IntT *resolve(
InstructionT &instruction,
Source source,
DataPointer pointer,
RegistersT &registers,
MemoryT &memory,
IntT *none,
IntT *immediate
) {
// Rules:
//
// * if this is a memory access, set target_address and break;
// * otherwise return the appropriate value.
uint32_t target_address;
switch(source) {
case Source::eAX: return register_<model, IntT, access, Source::eAX>(registers);
case Source::eCX: return register_<model, IntT, access, Source::eCX>(registers);
case Source::eDX: return register_<model, IntT, access, Source::eDX>(registers);
case Source::eBX: return register_<model, IntT, access, Source::eBX>(registers);
case Source::eSPorAH: return register_<model, IntT, access, Source::eSPorAH>(registers);
case Source::eBPorCH: return register_<model, IntT, access, Source::eBPorCH>(registers);
case Source::eSIorDH: return register_<model, IntT, access, Source::eSIorDH>(registers);
case Source::eDIorBH: return register_<model, IntT, access, Source::eDIorBH>(registers);
2023-10-23 02:15:27 +00:00
// Segment registers are always 16-bit.
case Source::ES: if constexpr (std::is_same_v<IntT, uint16_t>) return &registers.es(); else return nullptr;
case Source::CS: if constexpr (std::is_same_v<IntT, uint16_t>) return &registers.cs(); else return nullptr;
case Source::SS: if constexpr (std::is_same_v<IntT, uint16_t>) return &registers.ss(); else return nullptr;
case Source::DS: if constexpr (std::is_same_v<IntT, uint16_t>) return &registers.ds(); else return nullptr;
2023-10-09 15:46:59 +00:00
// 16-bit models don't have FS and GS.
case Source::FS: if constexpr (is_32bit(model) && std::is_same_v<IntT, uint16_t>) return &registers.fs(); else return nullptr;
case Source::GS: if constexpr (is_32bit(model) && std::is_same_v<IntT, uint16_t>) return &registers.gs(); else return nullptr;
case Source::Immediate:
*immediate = instruction.operand();
return immediate;
case Source::None: return none;
case Source::Indirect:
target_address = address<model, Source::Indirect, IntT, access>(instruction, pointer, registers, memory);
2023-10-09 15:46:59 +00:00
break;
case Source::IndirectNoBase:
target_address = address<model, Source::IndirectNoBase, IntT, access>(instruction, pointer, registers, memory);
2023-10-09 15:46:59 +00:00
break;
case Source::DirectAddress:
target_address = address<model, Source::DirectAddress, IntT, access>(instruction, pointer, registers, memory);
2023-10-09 15:46:59 +00:00
break;
}
// If execution has reached here then a memory fetch is required.
// Do it and exit.
return &memory.template access<IntT, access>(instruction.data_segment(), target_address);
2023-10-09 15:46:59 +00:00
};
2023-10-05 18:37:58 +00:00
namespace Primitive {
2023-10-18 19:59:39 +00:00
// The below takes a reference in order properly to handle PUSH SP, which should place the value of SP after the
// push onto the stack.
template <typename IntT, bool Preauthorised, typename MemoryT, typename RegistersT>
2023-10-18 19:59:39 +00:00
void push(IntT &value, MemoryT &memory, RegistersT &registers) {
2023-10-16 19:40:24 +00:00
registers.sp_ -= sizeof(IntT);
2023-11-01 18:31:42 +00:00
memory.template access<IntT, Preauthorised ? AccessType::PreauthorisedWrite : AccessType::Write>(
2023-10-16 19:40:24 +00:00
InstructionSet::x86::Source::SS,
registers.sp_) = value;
memory.template write_back<IntT>();
}
template <typename IntT, bool Preauthorised, typename MemoryT, typename RegistersT>
2023-10-16 19:40:24 +00:00
IntT pop(MemoryT &memory, RegistersT &registers) {
2023-11-01 18:31:42 +00:00
const auto value = memory.template access<IntT, Preauthorised ? AccessType::PreauthorisedRead : AccessType::Read>(
2023-10-16 19:40:24 +00:00
InstructionSet::x86::Source::SS,
registers.sp_);
registers.sp_ += sizeof(IntT);
return value;
}
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 Developers 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
//
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.flag<Flag::AuxiliaryCarry>()) {
2023-10-05 18:37:58 +00:00
ax.halves.low += 6;
++ax.halves.high;
status.set_from<Flag::Carry, Flag::AuxiliaryCarry>(1);
2023-10-05 18:37:58 +00:00
} else {
status.set_from<Flag::Carry, Flag::AuxiliaryCarry>(0);
2023-10-05 18:37:58 +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.set_from<uint8_t, Flag::Zero, Flag::Sign, Flag::ParityOdd>(ax.halves.low);
2023-10-05 18:37:58 +00:00
}
template <typename FlowControllerT>
2023-10-11 20:01:09 +00:00
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.
*/
/*
If ... an immediate value of 0 is used, it will cause a #DE (divide error) exception.
*/
if(!imm) {
2023-10-10 14:34:18 +00:00
flow_controller.interrupt(Interrupt::DivideError);
return;
}
2023-10-05 18:52:24 +00:00
ax.halves.high = ax.halves.low / imm;
ax.halves.low = ax.halves.low % imm;
status.set_from<uint8_t, Flag::Zero, Flag::Sign, Flag::ParityOdd>(ax.halves.low);
2023-10-05 18:52:24 +00:00
}
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.flag<Flag::AuxiliaryCarry>()) {
2023-10-05 18:52:24 +00:00
ax.halves.low -= 6;
--ax.halves.high;
status.set_from<Flag::Carry, Flag::AuxiliaryCarry>(1);
2023-10-05 18:52:24 +00:00
} else {
status.set_from<Flag::Carry, Flag::AuxiliaryCarry>(0);
2023-10-05 18:52:24 +00:00
}
ax.halves.low &= 0x0f;
}
inline void daa(uint8_t &al, Status &status) {
/*
2023-10-09 18:42:32 +00:00
(as modified by https://www.felixcloutier.com/x86/daa ...)
old_AL AL;
old_CF CF;
CF 0;
IF (((AL AND 0FH) > 9) or AF = 1)
THEN
AL AL + 6;
2023-10-09 18:42:32 +00:00
CF old_CF OR CarryFromLastAddition; (* CF OR carry from AL AL + 6 *)
AF 1;
ELSE
AF 0;
FI;
2023-10-09 18:42:32 +00:00
IF ((old_AL > 99H) or old_CF = 1)
THEN
AL AL + 60H;
CF 1;
ELSE
CF 0;
FI;
*/
/*
The CF and AF flags are set if the adjustment of the value results in a
decimal carry in either digit of the result (see the Operation section above).
The SF, ZF, and PF flags are set according to the result. The OF flag is undefined.
*/
2023-10-09 18:42:32 +00:00
const uint8_t old_al = al;
const auto old_carry = status.flag<Flag::Carry>();
status.set_from<Flag::Carry>(0);
2023-10-09 18:42:32 +00:00
if((al & 0x0f) > 0x09 || status.flag<Flag::AuxiliaryCarry>()) {
status.set_from<Flag::Carry>(old_carry | (al > 0xf9));
al += 0x06;
status.set_from<Flag::AuxiliaryCarry>(1);
} else {
status.set_from<Flag::AuxiliaryCarry>(0);
}
2023-10-09 18:42:32 +00:00
if(old_al > 0x99 || old_carry) {
al += 0x60;
status.set_from<Flag::Carry>(1);
} else {
status.set_from<Flag::Carry>(0);
}
status.set_from<uint8_t, Flag::Zero, Flag::Sign, Flag::ParityOdd>(al);
}
inline void das(uint8_t &al, Status &status) {
/*
(as modified by https://www.felixcloutier.com/x86/daa ...)
old_AL AL;
old_CF CF;
CF 0;
IF (((AL AND 0FH) > 9) or AF = 1)
THEN
AL AL - 6;
CF old_CF OR CarryFromLastAddition; (* CF OR borrow from AL AL - 6 *)
AF 1;
ELSE
AF 0;
FI;
IF ((old_AL > 99H) or old_CF = 1)
THEN
AL AL - 60H;
CF 1;
ELSE
CF 0;
FI;
*/
/*
The CF and AF flags are set if the adjustment of the value results in a
decimal carry in either digit of the result (see the Operation section above).
The SF, ZF, and PF flags are set according to the result. The OF flag is undefined.
*/
const uint8_t old_al = al;
const auto old_carry = status.flag<Flag::Carry>();
status.set_from<Flag::Carry>(0);
if((al & 0x0f) > 0x09 || status.flag<Flag::AuxiliaryCarry>()) {
status.set_from<Flag::Carry>(old_carry | (al < 0x06));
al -= 0x06;
status.set_from<Flag::AuxiliaryCarry>(1);
} else {
status.set_from<Flag::AuxiliaryCarry>(0);
}
if(old_al > 0x99 || old_carry) {
al -= 0x60;
status.set_from<Flag::Carry>(1);
} else {
status.set_from<Flag::Carry>(0);
}
status.set_from<uint8_t, Flag::Zero, Flag::Sign, Flag::ParityOdd>(al);
}
2023-10-11 02:43:06 +00:00
template <bool with_carry, typename IntT>
2023-10-05 19:49:07 +00:00
void add(IntT &destination, IntT source, Status &status) {
/*
2023-10-11 02:43:06 +00:00
DEST DEST + SRC [+ CF];
2023-10-05 19:49:07 +00:00
*/
/*
The OF, SF, ZF, AF, CF, and PF flags are set according to the result.
*/
2023-10-11 02:43:06 +00:00
const IntT result = destination + source + (with_carry ? status.carry_bit<IntT>() : 0);
2023-10-05 19:49:07 +00:00
status.set_from<Flag::Carry>(
Numeric::carried_out<true, Numeric::bit_size<IntT>() - 1>(destination, source, result));
status.set_from<Flag::AuxiliaryCarry>(
Numeric::carried_in<4>(destination, source, result));
status.set_from<Flag::Overflow>(
Numeric::overflow<true, IntT>(destination, source, result));
2023-10-05 19:49:07 +00:00
status.set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(result);
2023-10-05 19:49:07 +00:00
destination = result;
}
2023-10-11 02:43:06 +00:00
template <bool with_borrow, bool write_back, typename IntT>
2023-10-09 20:21:04 +00:00
void sub(IntT &destination, IntT source, Status &status) {
/*
2023-10-11 02:43:06 +00:00
DEST DEST - (SRC [+ CF]);
2023-10-09 20:21:04 +00:00
*/
/*
The OF, SF, ZF, AF, CF, and PF flags are set according to the result.
*/
2023-10-11 02:43:06 +00:00
const IntT result = destination - source - (with_borrow ? status.carry_bit<IntT>() : 0);
2023-10-09 20:21:04 +00:00
status.set_from<Flag::Carry>(
Numeric::carried_out<false, Numeric::bit_size<IntT>() - 1>(destination, source, result));
status.set_from<Flag::AuxiliaryCarry>(
Numeric::carried_in<4>(destination, source, result));
status.set_from<Flag::Overflow>(
Numeric::overflow<false, IntT>(destination, source, result));
2023-10-09 20:21:04 +00:00
status.set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(result);
2023-10-11 02:15:33 +00:00
if constexpr (write_back) {
destination = result;
}
2023-10-09 20:21:04 +00:00
}
2023-10-11 02:28:10 +00:00
template <typename IntT>
void test(IntT &destination, IntT source, Status &status) {
/*
TEMP SRC1 AND SRC2;
SF MSB(TEMP);
IF TEMP = 0
THEN ZF 0;
ELSE ZF 1;
FI:
PF BitwiseXNOR(TEMP[0:7]);
CF 0;
OF 0;
*/
/*
The OF and CF flags are cleared to 0.
The SF, ZF, and PF flags are set according to the result (see the Operation section above).
The state of the AF flag is undefined.
*/
const IntT result = destination & source;
status.set_from<Flag::Carry, Flag::Overflow>(0);
status.set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(result);
2023-10-11 02:28:10 +00:00
}
2023-10-11 02:34:42 +00:00
template <typename IntT>
void xchg(IntT &destination, IntT &source) {
/*
TEMP DEST
DEST SRC
SRC TEMP
*/
std::swap(destination, source);
}
2023-10-10 01:50:17 +00:00
template <typename IntT>
void mul(IntT &destination_high, IntT &destination_low, IntT source, Status &status) {
/*
IF byte operation
THEN
AX AL * SRC
ELSE (* word or doubleword operation *)
IF OperandSize = 16 THEN
DX:AX AX * SRC
ELSE (* OperandSize = 32 *)
EDX:EAX EAX * SRC
FI;
*/
/*
The OF and CF flags are cleared to 0 if the upper half of the result is 0;
otherwise, they are set to 1. The SF, ZF, AF, and PF flags are undefined.
*/
destination_high = (destination_low * source) >> (8 * sizeof(IntT));
destination_low *= source;
status.set_from<Flag::Overflow, Flag::Carry>(destination_high);
2023-10-10 01:50:17 +00:00
}
template <typename IntT>
void imul(IntT &destination_high, IntT &destination_low, IntT source, Status &status) {
/*
(as modified by https://www.felixcloutier.com/x86/daa ...)
IF (OperandSize = 8)
THEN
AX AL SRC (* signed multiplication *)
IF (AX = SignExtend(AL))
THEN CF = 0; OF = 0;
ELSE CF = 1; OF = 1;
FI;
ELSE IF OperandSize = 16
THEN
DX:AX AX SRC (* signed multiplication *)
IF (DX:AX = SignExtend(AX))
THEN CF = 0; OF = 0;
ELSE CF = 1; OF = 1;
FI;
ELSE (* OperandSize = 32 *)
EDX:EAX EAX SRC (* signed multiplication *)
IF (EDX:EAX = SignExtend(EAX))
THEN CF = 0; OF = 0;
ELSE CF = 1; OF = 1;
FI;
FI;
*/
using sIntT = typename std::make_signed<IntT>::type;
destination_high = (sIntT(destination_low) * sIntT(source)) >> (8 * sizeof(IntT));
destination_low = IntT(sIntT(destination_low) * sIntT(source));
2023-10-11 15:06:20 +00:00
const auto sign_extension = (destination_low & Numeric::top_bit<IntT>()) ? IntT(~0) : 0;
status.set_from<Flag::Overflow, Flag::Carry>(destination_high != sign_extension);
}
2023-10-10 14:34:18 +00:00
template <typename IntT, typename FlowControllerT>
void div(IntT &destination_high, IntT &destination_low, IntT source, FlowControllerT &flow_controller) {
2023-10-10 19:57:33 +00:00
/*
IF SRC = 0
THEN #DE; (* divide error *)
FI;
IF OperandSize = 8 (* word/byte operation *)
THEN
temp AX / SRC;
IF temp > FFH
THEN #DE; (* divide error *) ;
ELSE
AL temp;
AH AX MOD SRC;
FI;
ELSE
IF OperandSize = 16 (* doubleword/word operation *)
THEN
temp DX:AX / SRC;
IF temp > FFFFH
THEN #DE; (* divide error *) ;
ELSE
AX temp;
DX DX:AX MOD SRC;
FI;
ELSE (* quadword/doubleword operation *)
temp EDX:EAX / SRC;
IF temp > FFFFFFFFH
THEN #DE; (* divide error *) ;
ELSE
EAX temp;
EDX EDX:EAX MOD SRC;
FI;
FI;
FI;
*/
/*
The CF, OF, SF, ZF, AF, and PF flags are undefined.
*/
2023-10-10 14:34:18 +00:00
if(!source) {
flow_controller.interrupt(Interrupt::DivideError);
return;
}
// TEMPORARY HACK. Will not work with DWords.
const uint32_t dividend = (destination_high << (8 * sizeof(IntT))) + destination_low;
const auto result = dividend / source;
if(IntT(result) != result) {
flow_controller.interrupt(Interrupt::DivideError);
return;
}
destination_low = IntT(result);
destination_high = dividend % source;
}
template <typename IntT, typename FlowControllerT>
void idiv(IntT &destination_high, IntT &destination_low, IntT source, FlowControllerT &flow_controller) {
2023-10-10 19:57:33 +00:00
/*
IF SRC = 0
THEN #DE; (* divide error *)
FI;
IF OperandSize = 8 (* word/byte operation *)
THEN
temp AX / SRC; (* signed division *)
IF (temp > 7FH) OR (temp < 80H) (* if a positive result is greater than 7FH or a negative result is less than 80H *)
THEN #DE; (* divide error *) ;
ELSE
AL temp;
AH AX MOD SRC;
FI;
ELSE
IF OperandSize = 16 (* doubleword/word operation *)
THEN
temp DX:AX / SRC; (* signed division *)
IF (temp > 7FFFH) OR (temp < 8000H) (* if a positive result is greater than 7FFFH or a negative result is less than 8000H *)
THEN #DE; (* divide error *) ;
ELSE
AX temp;
DX DX:AX MOD SRC;
FI;
ELSE (* quadword/doubleword operation *)
temp EDX:EAX / SRC; (* signed division *)
IF (temp > 7FFFFFFFH) OR (temp < 80000000H) (* if a positive result is greater than 7FFFFFFFH or a negative result is less than 80000000H *)
THEN #DE; (* divide error *) ;
ELSE
EAX temp;
EDX EDX:EAX MOD SRC;
FI;
FI;
FI;
*/
/*
The CF, OF, SF, ZF, AF, and PF flags are undefined.
*/
2023-10-10 14:34:18 +00:00
if(!source) {
flow_controller.interrupt(Interrupt::DivideError);
return;
}
// TEMPORARY HACK. Will not work with DWords.
using sIntT = typename std::make_signed<IntT>::type;
const int32_t dividend = (sIntT(destination_high) << (8 * sizeof(IntT))) + destination_low;
const auto result = dividend / sIntT(source);
if(sIntT(result) != result) {
flow_controller.interrupt(Interrupt::DivideError);
return;
}
destination_low = IntT(result);
destination_high = dividend % sIntT(source);
}
2023-10-10 19:57:33 +00:00
template <typename IntT>
void inc(IntT &destination, Status &status) {
/*
DEST DEST + 1;
*/
/*
The CF flag is not affected.
The OF, SF, ZF, AF, and PF flags are set according to the result.
*/
++destination;
status.set_from<Flag::Overflow>(destination == Numeric::top_bit<IntT>());
status.set_from<Flag::AuxiliaryCarry>(((destination - 1) ^ destination) & 0x10);
status.set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(destination);
2023-10-10 19:57:33 +00:00
}
2023-10-10 20:27:06 +00:00
template <typename IntT, typename RegistersT, typename FlowControllerT>
2023-10-11 20:01:09 +00:00
void jump(bool condition, IntT displacement, RegistersT &registers, FlowControllerT &flow_controller) {
2023-10-10 20:27:06 +00:00
/*
IF condition
THEN
EIP EIP + SignExtend(DEST);
IF OperandSize = 16
THEN
EIP EIP AND 0000FFFFH;
FI;
FI;
*/
// TODO: proper behaviour in 32-bit.
if(condition) {
flow_controller.jump(registers.ip() + displacement);
}
}
2023-10-18 18:04:21 +00:00
template <typename IntT, typename OffsetT, typename RegistersT, typename FlowControllerT>
void loop(IntT &counter, OffsetT displacement, RegistersT &registers, FlowControllerT &flow_controller) {
--counter;
if(counter) {
flow_controller.jump(registers.ip() + displacement);
}
}
template <typename IntT, typename OffsetT, typename RegistersT, typename FlowControllerT>
void loope(IntT &counter, OffsetT displacement, RegistersT &registers, Status &status, FlowControllerT &flow_controller) {
--counter;
if(counter && status.flag<Flag::Zero>()) {
flow_controller.jump(registers.ip() + displacement);
}
}
template <typename IntT, typename OffsetT, typename RegistersT, typename FlowControllerT>
void loopne(IntT &counter, OffsetT displacement, RegistersT &registers, Status &status, FlowControllerT &flow_controller) {
--counter;
if(counter && !status.flag<Flag::Zero>()) {
flow_controller.jump(registers.ip() + displacement);
}
}
2023-10-10 19:57:33 +00:00
template <typename IntT>
void dec(IntT &destination, Status &status) {
/*
DEST DEST - 1;
*/
/*
The CF flag is not affected.
The OF, SF, ZF, AF, and PF flags are set according to the result.
*/
status.set_from<Flag::Overflow>(destination == Numeric::top_bit<IntT>());
2023-10-10 19:57:33 +00:00
--destination;
status.set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(destination);
status.set_from<Flag::AuxiliaryCarry>(((destination + 1) ^ destination) & 0x10);
2023-10-10 19:57:33 +00:00
}
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.set_from<Flag::Overflow, Flag::Carry>(0);
status.set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(destination);
2023-10-09 02:18:40 +00:00
}
2023-10-10 21:12:06 +00:00
template <typename IntT>
void or_(IntT &destination, IntT source, Status &status) {
/*
DEST DEST OR 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.set_from<Flag::Overflow, Flag::Carry>(0);
status.set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(destination);
2023-10-10 21:12:06 +00:00
}
template <typename IntT>
void xor_(IntT &destination, IntT source, Status &status) {
/*
DEST DEST XOR 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.set_from<Flag::Overflow, Flag::Carry>(0);
status.set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(destination);
2023-10-10 21:12:06 +00:00
}
2023-10-11 02:09:10 +00:00
template <typename IntT>
void neg(IntT &destination, Status &status) {
/*
IF DEST = 0
THEN CF 0
ELSE CF 1;
FI;
DEST (DEST)
*/
/*
The CF flag cleared to 0 if the source operand is 0; otherwise it is set to 1.
The OF, SF, ZF, AF, and PF flags are set according to the result.
*/
status.set_from<Flag::AuxiliaryCarry>(Numeric::carried_in<4>(IntT(0), destination, IntT(-destination)));
2023-10-11 02:09:10 +00:00
destination = -destination;
status.set_from<Flag::Carry>(destination);
status.set_from<Flag::Overflow>(destination == Numeric::top_bit<IntT>());
status.set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(destination);
2023-10-11 02:09:10 +00:00
}
template <typename IntT>
void not_(IntT &destination) {
/*
DEST NOT DEST;
*/
/*
Flags affected: none.
*/
destination = ~destination;
}
2023-11-01 18:49:30 +00:00
template <typename IntT, typename RegistersT, typename MemoryT, typename FlowControllerT>
void call_relative(IntT offset, RegistersT &registers, MemoryT &memory, FlowControllerT &flow_controller) {
push<uint16_t, false>(registers.ip(), memory, registers);
flow_controller.jump(registers.ip() + offset);
2023-10-05 18:37:58 +00:00
}
2023-11-01 18:49:30 +00:00
template <typename IntT, typename RegistersT, typename MemoryT, typename FlowControllerT>
void call_absolute(IntT target, RegistersT &registers, MemoryT &memory, FlowControllerT &flow_controller) {
push<uint16_t, false>(registers.ip(), memory, registers);
flow_controller.jump(target);
2023-10-09 15:46:59 +00:00
}
2023-10-08 17:34:28 +00:00
2023-10-18 17:15:00 +00:00
template <typename IntT, typename FlowControllerT>
void jump_absolute(IntT target, FlowControllerT &flow_controller) {
flow_controller.jump(target);
}
2023-10-09 15:46:59 +00:00
template <Model model, typename InstructionT, typename FlowControllerT, typename RegistersT, typename MemoryT>
2023-10-09 15:59:38 +00:00
void call_far(InstructionT &instruction,
2023-10-09 15:46:59 +00:00
FlowControllerT &flow_controller,
RegistersT &registers,
2023-10-12 18:24:28 +00:00
MemoryT &memory
) {
2023-10-09 15:46:59 +00:00
// TODO: eliminate 16-bit assumption below.
2023-11-01 18:49:30 +00:00
const Source source_segment = instruction.data_segment();
memory.preauthorise_stack_write(sizeof(uint16_t) * 2);
uint16_t source_address;
2023-10-12 18:24:28 +00:00
const auto pointer = instruction.destination();
switch(pointer.source()) {
2023-10-09 15:46:59 +00:00
default:
2023-11-01 18:49:30 +00:00
case Source::Immediate:
push<uint16_t, true>(registers.cs(), memory, registers);
push<uint16_t, true>(registers.ip(), memory, registers);
flow_controller.jump(instruction.segment(), instruction.offset());
return;
2023-10-08 17:34:28 +00:00
2023-10-09 15:46:59 +00:00
case Source::Indirect:
2023-11-01 18:31:42 +00:00
source_address = address<model, Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, registers, memory);
2023-10-09 15:46:59 +00:00
break;
case Source::IndirectNoBase:
2023-11-01 18:31:42 +00:00
source_address = address<model, Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, registers, memory);
2023-10-09 15:46:59 +00:00
break;
2023-10-08 17:34:28 +00:00
case Source::DirectAddress:
2023-11-01 18:31:42 +00:00
source_address = address<model, Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, registers, memory);
2023-10-08 17:34:28 +00:00
break;
}
2023-11-01 18:49:30 +00:00
memory.preauthorise_read(source_segment, source_address, sizeof(uint16_t) * 2);
push<uint16_t, true>(registers.cs(), memory, registers);
push<uint16_t, true>(registers.ip(), memory, registers);
2023-10-09 15:46:59 +00:00
2023-11-01 18:49:30 +00:00
const uint16_t offset = memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
2023-10-09 15:46:59 +00:00
source_address += 2;
2023-11-01 18:49:30 +00:00
const uint16_t segment = memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
flow_controller.jump(segment, offset);
2023-10-09 15:46:59 +00:00
}
2023-10-18 17:15:00 +00:00
template <Model model, typename InstructionT, typename FlowControllerT, typename RegistersT, typename MemoryT>
void jump_far(InstructionT &instruction,
FlowControllerT &flow_controller,
RegistersT &registers,
MemoryT &memory
) {
// TODO: eliminate 16-bit assumption below.
uint16_t source_address = 0;
const auto pointer = instruction.destination();
switch(pointer.source()) {
2023-10-18 17:15:00 +00:00
default:
case Source::Immediate: flow_controller.jump(instruction.segment(), instruction.offset()); return;
2023-10-18 17:15:00 +00:00
case Source::Indirect:
source_address = address<model, Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, registers, memory);
2023-10-18 17:15:00 +00:00
break;
case Source::IndirectNoBase:
source_address = address<model, Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, registers, memory);
2023-10-18 17:15:00 +00:00
break;
case Source::DirectAddress:
source_address = address<model, Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, registers, memory);
2023-10-18 17:15:00 +00:00
break;
}
2023-10-27 16:54:42 +00:00
const Source source_segment = instruction.data_segment();
2023-10-18 17:15:00 +00:00
const uint16_t offset = memory.template access<uint16_t, AccessType::Read>(source_segment, source_address);
2023-10-18 17:15:00 +00:00
source_address += 2;
const uint16_t segment = memory.template access<uint16_t, AccessType::Read>(source_segment, source_address);
2023-10-18 17:15:00 +00:00
flow_controller.jump(segment, offset);
}
2023-10-16 19:40:24 +00:00
template <typename FlowControllerT, typename RegistersT, typename MemoryT>
void iret(RegistersT &registers, FlowControllerT &flow_controller, MemoryT &memory, Status &status) {
// TODO: all modes other than 16-bit real mode.
2023-11-01 18:49:30 +00:00
memory.preauthorise_stack_read(sizeof(uint16_t) * 3);
const auto ip = pop<uint16_t, true>(memory, registers);
const auto cs = pop<uint16_t, true>(memory, registers);
2023-11-01 01:55:22 +00:00
status.set(pop<uint16_t, true>(memory, registers));
2023-11-01 18:49:30 +00:00
flow_controller.jump(cs, ip);
2023-10-16 19:40:24 +00:00
}
template <typename InstructionT, typename FlowControllerT, typename RegistersT, typename MemoryT>
void ret_near(InstructionT instruction, RegistersT &registers, FlowControllerT &flow_controller, MemoryT &memory) {
2023-11-01 18:49:30 +00:00
const auto ip = pop<uint16_t, false>(memory, registers);
2023-10-16 19:40:24 +00:00
registers.sp() += instruction.operand();
2023-11-01 18:49:30 +00:00
flow_controller.jump(ip);
2023-10-16 19:40:24 +00:00
}
template <typename InstructionT, typename FlowControllerT, typename RegistersT, typename MemoryT>
void ret_far(InstructionT instruction, RegistersT &registers, FlowControllerT &flow_controller, MemoryT &memory) {
2023-11-01 18:49:30 +00:00
memory.preauthorise_stack_read(sizeof(uint16_t) * 2);
const auto ip = pop<uint16_t, true>(memory, registers);
const auto cs = pop<uint16_t, true>(memory, registers);
2023-10-16 19:40:24 +00:00
registers.sp() += instruction.operand();
2023-11-01 18:49:30 +00:00
flow_controller.jump(cs, ip);
2023-10-16 19:40:24 +00:00
}
2023-10-12 18:24:28 +00:00
template <Model model, Source selector, typename InstructionT, typename MemoryT, typename RegistersT>
void ld(
InstructionT &instruction,
uint16_t &destination,
MemoryT &memory,
RegistersT &registers
) {
const auto pointer = instruction.source();
auto source_address = address<model, uint16_t, AccessType::Read>(instruction, pointer, registers, memory);
2023-10-27 16:54:42 +00:00
const Source source_segment = instruction.data_segment();
2023-10-12 18:24:28 +00:00
destination = memory.template access<uint16_t, AccessType::Read>(source_segment, source_address);
2023-10-12 18:24:28 +00:00
source_address += 2;
switch(selector) {
case Source::DS: registers.ds() = memory.template access<uint16_t, AccessType::Read>(source_segment, source_address); break;
case Source::ES: registers.es() = memory.template access<uint16_t, AccessType::Read>(source_segment, source_address); break;
2023-10-12 18:24:28 +00:00
}
}
2023-10-12 18:31:25 +00:00
template <Model model, typename IntT, typename InstructionT, typename MemoryT, typename RegistersT>
void lea(
2023-10-13 01:12:03 +00:00
const InstructionT &instruction,
2023-10-12 18:31:25 +00:00
IntT &destination,
MemoryT &memory,
RegistersT &registers
) {
// TODO: address size.
2023-11-01 18:31:42 +00:00
destination = IntT(address<model, uint16_t, AccessType::Read>(instruction, instruction.source(), registers, memory));
2023-10-12 18:31:25 +00:00
}
2023-10-13 01:12:03 +00:00
template <typename AddressT, typename InstructionT, typename MemoryT, typename RegistersT>
void xlat(
const InstructionT &instruction,
MemoryT &memory,
RegistersT &registers
) {
AddressT address;
if constexpr (std::is_same_v<AddressT, uint16_t>) {
address = registers.bx() + registers.al();
}
registers.al() = memory.template access<uint8_t, AccessType::Read>(instruction.data_segment(), address);
2023-10-13 01:12:03 +00:00
}
2023-10-12 19:34:46 +00:00
template <typename IntT>
void mov(IntT &destination, IntT source) {
destination = source;
}
2023-10-11 20:01:09 +00:00
template <typename FlowControllerT>
void int_(uint8_t vector, FlowControllerT &flow_controller) {
flow_controller.interrupt(vector);
}
template <typename FlowControllerT>
void into(Status &status, FlowControllerT &flow_controller) {
if(status.flag<Flag::Overflow>()) {
flow_controller.interrupt(Interrupt::OnOverflow);
}
}
2023-10-12 17:54:51 +00:00
inline void sahf(uint8_t &ah, Status &status) {
/*
EFLAGS(SF:ZF:0:AF:0:PF:1:CF) AH;
*/
status.set_from<uint8_t, Flag::Sign>(ah);
status.set_from<Flag::Zero>(!(ah & 0x40));
status.set_from<Flag::AuxiliaryCarry>(ah & 0x10);
status.set_from<Flag::ParityOdd>(!(ah & 0x04));
status.set_from<Flag::Carry>(ah & 0x01);
}
inline void lahf(uint8_t &ah, Status &status) {
/*
AH EFLAGS(SF:ZF:0:AF:0:PF:1:CF);
*/
ah =
(status.flag<Flag::Sign>() ? 0x80 : 0x00) |
(status.flag<Flag::Zero>() ? 0x40 : 0x00) |
(status.flag<Flag::AuxiliaryCarry>() ? 0x10 : 0x00) |
(status.flag<Flag::ParityOdd>() ? 0x00 : 0x04) |
0x02 |
(status.flag<Flag::Carry>() ? 0x01 : 0x00);
}
2023-10-09 19:00:04 +00:00
template <typename IntT>
void cbw(IntT &ax) {
constexpr IntT test_bit = 1 << (sizeof(IntT) * 4 - 1);
constexpr IntT low_half = (1 << (sizeof(IntT) * 4)) - 1;
if(ax & test_bit) {
ax |= ~low_half;
} else {
ax &= low_half;
}
2023-10-09 15:59:38 +00:00
}
2023-10-09 18:54:14 +00:00
template <typename IntT>
2023-10-09 19:00:04 +00:00
void cwd(IntT &dx, IntT ax) {
2023-10-11 15:06:20 +00:00
dx = ax & Numeric::top_bit<IntT>() ? IntT(~0) : IntT(0);
2023-10-09 18:54:14 +00:00
}
// TODO: changes to the interrupt flag do quite a lot more in protected mode.
inline void clc(Status &status) { status.set_from<Flag::Carry>(0); }
inline void cld(Status &status) { status.set_from<Flag::Direction>(0); }
inline void cli(Status &status) { status.set_from<Flag::Interrupt>(0); }
inline void stc(Status &status) { status.set_from<Flag::Carry>(1); }
inline void std(Status &status) { status.set_from<Flag::Direction>(1); }
inline void sti(Status &status) { status.set_from<Flag::Interrupt>(1); }
inline void cmc(Status &status) { status.set_from<Flag::Carry>(!status.flag<Flag::Carry>()); }
2023-10-09 15:59:38 +00:00
2023-10-12 19:52:05 +00:00
inline void salc(uint8_t &al, const Status &status) {
al = status.flag<Flag::Carry>() ? 0xff : 0x00;
}
template <typename IntT>
void setmo(IntT &destination, Status &status) {
destination = ~0;
status.set_from<Flag::Carry, Flag::AuxiliaryCarry, Flag::Overflow>(0);
status.set_from<IntT, Flag::Sign, Flag::Zero, Flag::ParityOdd>(destination);
}
2023-10-14 01:44:48 +00:00
template <typename IntT>
2023-10-13 18:44:22 +00:00
inline void rcl(IntT &destination, uint8_t count, Status &status) {
/*
(* RCL and RCR instructions *)
SIZE OperandSize
CASE (determine count) OF
SIZE = 8: tempCOUNT (COUNT AND 1FH) MOD 9;
SIZE = 16: tempCOUNT (COUNT AND 1FH) MOD 17;
SIZE = 32: tempCOUNT COUNT AND 1FH;
ESAC;
*/
/*
(* RCL instruction operation *)
WHILE (tempCOUNT 0)
DO
tempCF MSB(DEST);
DEST (DEST * 2) + CF;
CF tempCF;
tempCOUNT tempCOUNT 1;
OD;
ELIHW;
IF COUNT = 1
THEN OF MSB(DEST) XOR CF;
ELSE OF is undefined;
FI;
*/
/*
The CF flag contains the value of the bit shifted into it.
The OF flag is affected only for single- bit rotates (see Description above);
it is undefined for multi-bit rotates. The SF, ZF, AF, and PF flags are not affected.
*/
2023-10-14 01:44:48 +00:00
const auto temp_count = count % (Numeric::bit_size<IntT>() + 1);
2023-10-13 18:44:22 +00:00
auto carry = status.carry_bit<IntT>();
2023-10-14 01:32:35 +00:00
switch(temp_count) {
case 0: break;
case Numeric::bit_size<IntT>(): {
const IntT temp_carry = destination & 1;
destination = (destination >> 1) | (carry << (Numeric::bit_size<IntT>() - 1));
carry = temp_carry;
} break;
default: {
const IntT temp_carry = destination & (Numeric::top_bit<IntT>() >> (temp_count - 1));
destination =
(destination << temp_count) |
(destination >> (Numeric::bit_size<IntT>() + 1 - temp_count)) |
(carry << (temp_count - 1));
carry = temp_carry ? 1 : 0;
} break;
2023-10-13 18:44:22 +00:00
}
2023-10-14 01:32:35 +00:00
2023-10-13 18:44:22 +00:00
status.set_from<Flag::Carry>(carry);
status.set_from<Flag::Overflow>(
((destination >> (Numeric::bit_size<IntT>() - 1)) & 1) ^ carry
);
}
2023-10-14 01:44:48 +00:00
template <typename IntT>
inline void rcr(IntT &destination, uint8_t count, Status &status) {
/*
(* RCR instruction operation *)
IF COUNT = 1
THEN OF MSB(DEST) XOR CF;
ELSE OF is undefined;
FI;
WHILE (tempCOUNT 0)
DO
tempCF LSB(SRC);
DEST (DEST / 2) + (CF * 2SIZE);
CF tempCF;
tempCOUNT tempCOUNT 1;
OD;
*/
auto carry = status.carry_bit<IntT>();
status.set_from<Flag::Overflow>(
((destination >> (Numeric::bit_size<IntT>() - 1)) & 1) ^ carry
);
const auto temp_count = count % (Numeric::bit_size<IntT>() + 1);
switch(temp_count) {
case 0: break;
case Numeric::bit_size<IntT>(): {
const IntT temp_carry = destination & Numeric::top_bit<IntT>();
destination = (destination << 1) | carry;
carry = temp_carry;
} break;
default: {
const IntT temp_carry = destination & (1 << (temp_count - 1));
destination =
(destination >> temp_count) |
(destination << (Numeric::bit_size<IntT>() + 1 - temp_count)) |
(carry << (Numeric::bit_size<IntT>() - temp_count));
carry = temp_carry;
} break;
}
status.set_from<Flag::Carry>(carry);
}
2023-10-14 02:03:54 +00:00
template <typename IntT>
inline void rol(IntT &destination, uint8_t count, Status &status) {
/*
(* ROL and ROR instructions *)
SIZE OperandSize
CASE (determine count) OF
SIZE = 8: tempCOUNT COUNT MOD 8;
SIZE = 16: tempCOUNT COUNT MOD 16;
SIZE = 32: tempCOUNT COUNT MOD 32;
ESAC;
*/
/*
(* ROL instruction operation *)
WHILE (tempCOUNT 0)
DO
tempCF MSB(DEST);
DEST (DEST * 2) + tempCF;
tempCOUNT tempCOUNT 1;
OD;
ELIHW;
IF COUNT = 1
THEN OF MSB(DEST) XOR CF;
ELSE OF is undefined;
FI;
*/
/*
The CF flag contains the value of the bit shifted into it.
The OF flag is affected only for single- bit rotates (see Description above);
it is undefined for multi-bit rotates. The SF, ZF, AF, and PF flags are not affected.
*/
const auto temp_count = count & (Numeric::bit_size<IntT>() - 1);
if(!count) {
// TODO: is this 8086-specific? i.e. do the other x86s also exit without affecting flags when temp_count = 0?
return;
}
if(temp_count) {
destination =
(destination << temp_count) |
(destination >> (Numeric::bit_size<IntT>() - temp_count));
}
status.set_from<Flag::Carry>(destination & 1);
status.set_from<Flag::Overflow>(
((destination >> (Numeric::bit_size<IntT>() - 1)) ^ destination) & 1
);
}
2023-10-14 01:44:48 +00:00
2023-10-14 02:03:54 +00:00
template <typename IntT>
inline void ror(IntT &destination, uint8_t count, Status &status) {
/*
(* ROL and ROR instructions *)
SIZE OperandSize
CASE (determine count) OF
SIZE = 8: tempCOUNT COUNT MOD 8;
SIZE = 16: tempCOUNT COUNT MOD 16;
SIZE = 32: tempCOUNT COUNT MOD 32;
ESAC;
*/
/*
(* ROR instruction operation *)
WHILE (tempCOUNT 0)
DO
tempCF LSB(DEST);
DEST (DEST / 2) + (tempCF * 2^SIZE);
tempCOUNT tempCOUNT 1;
OD;
ELIHW;
IF COUNT = 1
THEN OF MSB(DEST) XOR MSB - 1 (DEST);
ELSE OF is undefined;
FI;
*/
/*
The CF flag contains the value of the bit shifted into it.
The OF flag is affected only for single- bit rotates (see Description above);
it is undefined for multi-bit rotates. The SF, ZF, AF, and PF flags are not affected.
*/
const auto temp_count = count & (Numeric::bit_size<IntT>() - 1);
if(!count) {
// TODO: is this 8086-specific? i.e. do the other x86s also exit without affecting flags when temp_count = 0?
return;
}
if(temp_count) {
destination =
(destination >> temp_count) |
(destination << (Numeric::bit_size<IntT>() - temp_count));
}
status.set_from<Flag::Carry>(destination & Numeric::top_bit<IntT>());
status.set_from<Flag::Overflow>(
(destination ^ (destination << 1)) & Numeric::top_bit<IntT>()
);
}
2023-10-15 01:42:33 +00:00
/*
tempCOUNT (COUNT AND 1FH);
tempDEST DEST;
WHILE (tempCOUNT 0)
DO
IF instruction is SAL or SHL
THEN
CF MSB(DEST);
ELSE (* instruction is SAR or SHR *)
CF LSB(DEST);
FI;
IF instruction is SAL or SHL
THEN
DEST DEST 2;
ELSE
IF instruction is SAR
THEN
DEST DEST / 2 (*Signed divide, rounding toward negative infinity*);
ELSE (* instruction is SHR *)
DEST DEST / 2 ; (* Unsigned divide *);
FI;
FI;
tempCOUNT tempCOUNT 1;
OD;
(* Determine overflow for the various instructions *)
IF COUNT = 1
THEN
IF instruction is SAL or SHL
THEN
OF MSB(DEST) XOR CF;
ELSE
IF instruction is SAR
THEN
OF 0;
ELSE (* instruction is SHR *)
OF MSB(tempDEST);
FI;
FI;
ELSE
IF COUNT = 0
THEN
All flags remain unchanged;
ELSE (* COUNT neither 1 or 0 *)
OF undefined;
FI;
FI;
*/
/*
The CF flag contains the value of the last bit shifted out of the destination operand;
it is undefined for SHL and SHR instructions where the count is greater than or equal to
the size (in bits) of the destination operand. The OF flag is affected only for 1-bit shifts
(see Description above); otherwise, it is undefined.
The SF, ZF, and PF flags are set according to the result. If the count is 0, the flags are not affected.
For a non-zero count, the AF flag is undefined.
*/
template <typename IntT>
inline void sal(IntT &destination, uint8_t count, Status &status) {
switch(count) {
case 0: return;
case Numeric::bit_size<IntT>():
status.set_from<Flag::Carry, Flag::Overflow>(destination & 1);
destination = 0;
break;
default:
if(count > Numeric::bit_size<IntT>()) {
status.set_from<Flag::Carry, Flag::Overflow>(0);
destination = 0;
} else {
const auto mask = (Numeric::top_bit<IntT>() >> (count - 1));
status.set_from<Flag::Carry>(
destination & mask
);
status.set_from<Flag::Overflow>(
(destination ^ (destination << 1)) & mask
);
destination <<= count;
}
break;
}
status.set_from<IntT, Flag::Sign, Flag::Zero, Flag::ParityOdd>(destination);
}
template <typename IntT>
inline void sar(IntT &destination, uint8_t count, Status &status) {
if(!count) {
return;
}
const IntT sign = Numeric::top_bit<IntT>() & destination;
if(count >= Numeric::bit_size<IntT>()) {
destination = sign ? IntT(~0) : IntT(0);
status.set_from<Flag::Carry>(sign);
} else {
const IntT mask = 1 << (count - 1);
status.set_from<Flag::Carry>(destination & mask);
destination = (destination >> count) | (sign ? ~(IntT(~0) >> count) : 0);
}
status.set_from<Flag::Overflow>(0);
status.set_from<IntT, Flag::Sign, Flag::Zero, Flag::ParityOdd>(destination);
}
2023-10-16 16:34:11 +00:00
template <typename IntT>
inline void shr(IntT &destination, uint8_t count, Status &status) {
if(!count) {
return;
}
status.set_from<Flag::Overflow>(Numeric::top_bit<IntT>() & destination);
if(count == Numeric::bit_size<IntT>()) {
status.set_from<Flag::Carry>(Numeric::top_bit<IntT>() & destination);
destination = 0;
} else if(count > Numeric::bit_size<IntT>()) {
status.set_from<Flag::Carry>(0);
destination = 0;
} else {
const IntT mask = 1 << (count - 1);
status.set_from<Flag::Carry>(destination & mask);
destination >>= count;
}
status.set_from<IntT, Flag::Sign, Flag::Zero, Flag::ParityOdd>(destination);
}
2023-10-18 19:59:39 +00:00
template <typename MemoryT, typename RegistersT>
void popf(MemoryT &memory, RegistersT &registers, Status &status) {
2023-11-01 01:55:22 +00:00
status.set(pop<uint16_t, false>(memory, registers));
2023-10-18 19:59:39 +00:00
}
template <typename MemoryT, typename RegistersT>
void pushf(MemoryT &memory, RegistersT &registers, Status &status) {
uint16_t value = status.get();
2023-11-01 01:55:22 +00:00
push<uint16_t, false>(value, memory, registers);
2023-10-18 19:59:39 +00:00
}
template <typename AddressT, Repetition repetition>
bool repetition_over(const AddressT &eCX) {
return repetition != Repetition::None && !eCX;
2023-10-20 21:08:11 +00:00
}
template <typename AddressT, Repetition repetition, typename FlowControllerT>
void repeat([[maybe_unused]] Status &status, AddressT &eCX, FlowControllerT &flow_controller) {
2023-10-21 01:36:50 +00:00
if(
repetition == Repetition::None || // No repetition => stop.
!(--eCX) // [e]cx is zero after being decremented => stop.
2023-10-21 01:36:50 +00:00
) {
return;
}
if constexpr (repetition != Repetition::Rep) {
// If this is RepE or RepNE, also test the zero flag.
if((repetition == Repetition::RepNE) == status.flag<Flag::Zero>()) {
return;
}
}
2023-10-21 01:36:50 +00:00
flow_controller.repeat_last();
}
template <typename IntT, typename AddressT, Repetition repetition, typename InstructionT, typename MemoryT, typename FlowControllerT>
2023-10-20 21:08:11 +00:00
void cmps(const InstructionT &instruction, AddressT &eCX, AddressT &eSI, AddressT &eDI, MemoryT &memory, Status &status, FlowControllerT &flow_controller) {
if(repetition_over<AddressT, repetition>(eCX)) {
return;
}
IntT lhs = memory.template access<IntT, AccessType::Read>(instruction.data_segment(), eSI);
2023-11-01 01:58:32 +00:00
const IntT rhs = memory.template access<IntT, AccessType::Read>(Source::ES, eDI);
2023-10-20 21:03:23 +00:00
eSI += status.direction<AddressT>() * sizeof(IntT);
eDI += status.direction<AddressT>() * sizeof(IntT);
Primitive::sub<false, false>(lhs, rhs, status);
repeat<AddressT, repetition>(status, eCX, flow_controller);
}
template <typename IntT, typename AddressT, Repetition repetition, typename MemoryT, typename FlowControllerT>
void scas(AddressT &eCX, AddressT &eDI, IntT &eAX, MemoryT &memory, Status &status, FlowControllerT &flow_controller) {
if(repetition_over<AddressT, repetition>(eCX)) {
2023-10-21 01:54:30 +00:00
return;
}
const IntT rhs = memory.template access<IntT, AccessType::Read>(Source::ES, eDI);
2023-10-21 01:54:30 +00:00
eDI += status.direction<AddressT>() * sizeof(IntT);
Primitive::sub<false, false>(eAX, rhs, status);
repeat<AddressT, repetition>(status, eCX, flow_controller);
2023-10-21 01:54:30 +00:00
}
template <typename IntT, typename AddressT, Repetition repetition, typename InstructionT, typename MemoryT, typename FlowControllerT>
void lods(const InstructionT &instruction, AddressT &eCX, AddressT &eSI, IntT &eAX, MemoryT &memory, Status &status, FlowControllerT &flow_controller) {
if(repetition_over<AddressT, repetition>(eCX)) {
return;
}
eAX = memory.template access<IntT, AccessType::Read>(instruction.data_segment(), eSI);
eSI += status.direction<AddressT>() * sizeof(IntT);
repeat<AddressT, repetition>(status, eCX, flow_controller);
}
template <typename IntT, typename AddressT, Repetition repetition, typename InstructionT, typename MemoryT, typename FlowControllerT>
void movs(const InstructionT &instruction, AddressT &eCX, AddressT &eSI, AddressT &eDI, MemoryT &memory, Status &status, FlowControllerT &flow_controller) {
if(repetition_over<AddressT, repetition>(eCX)) {
return;
}
memory.template access<IntT, AccessType::Write>(Source::ES, eDI) = memory.template access<IntT, AccessType::Read>(instruction.data_segment(), eSI);
eSI += status.direction<AddressT>() * sizeof(IntT);
eDI += status.direction<AddressT>() * sizeof(IntT);
repeat<AddressT, repetition>(status, eCX, flow_controller);
}
template <typename IntT, typename AddressT, Repetition repetition, typename MemoryT, typename FlowControllerT>
void stos(AddressT &eCX, AddressT &eDI, IntT &eAX, MemoryT &memory, Status &status, FlowControllerT &flow_controller) {
if(repetition_over<AddressT, repetition>(eCX)) {
return;
}
memory.template access<IntT, AccessType::Write>(Source::ES, eDI) = eAX;
eDI += status.direction<AddressT>() * sizeof(IntT);
repeat<AddressT, repetition>(status, eCX, flow_controller);
}
template <typename IntT, typename AddressT, Repetition repetition, typename InstructionT, typename MemoryT, typename IOT, typename FlowControllerT>
2023-10-22 02:52:50 +00:00
void outs(const InstructionT &instruction, AddressT &eCX, uint16_t port, AddressT &eSI, MemoryT &memory, IOT &io, Status &status, FlowControllerT &flow_controller) {
if(repetition_over<AddressT, repetition>(eCX)) {
2023-10-22 02:52:50 +00:00
return;
}
io.template out<IntT>(port, memory.template access<IntT, AccessType::Read>(instruction.data_segment(), eSI));
2023-10-22 02:52:50 +00:00
eSI += status.direction<AddressT>() * sizeof(IntT);
repeat<AddressT, repetition>(status, eCX, flow_controller);
2023-10-22 02:52:50 +00:00
}
template <typename IntT, typename AddressT, Repetition repetition, typename MemoryT, typename IOT, typename FlowControllerT>
void ins(AddressT &eCX, uint16_t port, AddressT &eDI, MemoryT &memory, IOT &io, Status &status, FlowControllerT &flow_controller) {
if(repetition_over<AddressT, repetition>(eCX)) {
2023-10-22 02:52:50 +00:00
return;
}
memory.template access<IntT, AccessType::Write>(Source::ES, eDI) = io.template in<IntT>(port);
2023-10-22 02:52:50 +00:00
eDI += status.direction<AddressT>() * sizeof(IntT);
repeat<AddressT, repetition>(status, eCX, flow_controller);
2023-10-22 02:52:50 +00:00
}
2023-10-22 02:37:25 +00:00
template <typename IntT, typename IOT>
void out(uint16_t port, IntT value, IOT &io) {
io.template out<IntT>(port, value);
}
template <typename IntT, typename IOT>
void in(uint16_t port, IntT &value, IOT &io) {
value = io.template in<IntT>(port);
}
2023-10-09 15:46:59 +00:00
}
2023-10-08 17:34:28 +00:00
2023-10-05 18:37:58 +00:00
template <
Model model,
DataSize data_size,
2023-10-25 20:00:01 +00:00
AddressSize address_size,
typename InstructionT,
typename FlowControllerT,
typename RegistersT,
typename MemoryT,
typename IOT
2023-10-05 18:37:58 +00:00
> void perform(
const InstructionT &instruction,
2023-10-05 18:37:58 +00:00
Status &status,
2023-10-09 15:46:59 +00:00
FlowControllerT &flow_controller,
RegistersT &registers,
2023-10-09 15:46:59 +00:00
MemoryT &memory,
2023-10-22 02:37:25 +00:00
IOT &io
2023-10-05 18:37:58 +00:00
) {
using IntT = typename DataSizeType<data_size>::type;
2023-10-25 20:00:01 +00:00
using AddressT = typename AddressSizeType<address_size>::type;
// 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)
IntT immediate;
const auto source_r = [&]() -> IntT& {
2023-10-29 20:19:10 +00:00
return *resolve<model, IntT, AccessType::Read>(
instruction,
instruction.source().source(),
instruction.source(),
registers,
memory,
nullptr,
&immediate);
};
const auto source_rmw = [&]() -> IntT& {
return *resolve<model, IntT, AccessType::ReadModifyWrite>(
instruction,
instruction.source().source(),
instruction.source(),
registers,
memory,
nullptr,
&immediate);
};
2023-10-29 20:19:10 +00:00
const auto destination_r = [&]() -> IntT& {
return *resolve<model, IntT, AccessType::Read>(
instruction,
instruction.destination().source(),
instruction.destination(),
registers,
memory,
nullptr,
&immediate);
};
const auto destination_w = [&]() -> IntT& {
return *resolve<model, IntT, AccessType::Write>(
instruction,
instruction.destination().source(),
instruction.destination(),
registers,
memory,
nullptr,
&immediate);
};
const auto destination_rmw = [&]() -> IntT& {
return *resolve<model, IntT, AccessType::ReadModifyWrite>(
instruction,
instruction.destination().source(),
instruction.destination(),
registers,
memory,
nullptr,
&immediate);
};
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(),
registers,
flow_controller);
};
2023-10-13 18:44:22 +00:00
const auto shift_count = [&]() -> uint8_t {
2023-10-13 19:34:06 +00:00
static constexpr uint8_t mask = (model != Model::i8086) ? 0x1f : 0xff;
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;
default: return 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-10-11 18:36:42 +00:00
if constexpr (data_size == DataSize::Byte) return registers.ah();
else if constexpr (data_size == DataSize::Word) return registers.dx();
else if constexpr (data_size == DataSize::DWord) return registers.edx();
};
2023-10-11 19:08:04 +00:00
const auto pair_low = [&]() -> IntT& {
2023-10-11 18:36:42 +00:00
if constexpr (data_size == DataSize::Byte) return registers.al();
else if constexpr (data_size == DataSize::Word) return registers.ax();
else if constexpr (data_size == DataSize::DWord) return registers.eax();
};
// For the string operations, evaluate to either SI and DI or ESI and EDI, depending on the address size.
const auto eSI = [&]() -> AddressT& {
if constexpr (std::is_same_v<AddressT, uint16_t>) {
return registers.si();
} else {
return registers.esi();
}
};
const auto eDI = [&]() -> AddressT& {
if constexpr (std::is_same_v<AddressT, uint16_t>) {
return registers.di();
} else {
return registers.edi();
}
};
// For counts, provide either eCX or CX depending on address size.
const auto eCX = [&]() -> AddressT& {
if constexpr (std::is_same_v<AddressT, uint16_t>) {
return registers.cx();
} else {
return registers.ecx();
}
};
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-10-22 02:52:50 +00:00
default: return registers.dx();
}
};
// 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.
2023-10-27 03:19:31 +00:00
switch(instruction.operation()) {
2023-10-09 15:46:59 +00:00
default:
assert(false);
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;
case Operation::DAA: Primitive::daa(registers.al(), status); return;
case Operation::DAS: Primitive::das(registers.al(), status); return;
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-10-09 19:03:01 +00:00
case Operation::ESC:
case Operation::NOP: return;
2023-10-09 20:21:04 +00:00
case Operation::HLT: flow_controller.halt(); return;
case Operation::WAIT: flow_controller.wait(); return;
case Operation::ADC: Primitive::add<true>(destination_rmw(), source_r(), status); break;
case Operation::ADD: Primitive::add<false>(destination_rmw(), source_r(), status); break;
case Operation::SBB: Primitive::sub<true, true>(destination_rmw(), source_r(), status); break;
case Operation::SUB: Primitive::sub<false, true>(destination_rmw(), source_r(), status); break;
2023-11-01 02:42:39 +00:00
case Operation::CMP: Primitive::sub<false, false>(destination_r(), source_r(), status); return;
case Operation::TEST: Primitive::test(destination_r(), source_r(), status); return;
2023-10-11 02:15:33 +00:00
case Operation::MUL: Primitive::mul(pair_high(), pair_low(), source_r(), status); return;
case Operation::IMUL_1: Primitive::imul(pair_high(), pair_low(), source_r(), status); return;
case Operation::DIV: Primitive::div(pair_high(), pair_low(), source_r(), flow_controller); return;
case Operation::IDIV: Primitive::idiv(pair_high(), pair_low(), source_r(), flow_controller); return;
2023-10-09 20:21:04 +00:00
2023-10-29 20:19:10 +00:00
case Operation::INC: Primitive::inc(destination_rmw(), status); break;
case Operation::DEC: Primitive::dec(destination_rmw(), status); break;
2023-10-10 19:57:33 +00:00
case Operation::AND: Primitive::and_(destination_rmw(), source_r(), status); break;
case Operation::OR: Primitive::or_(destination_rmw(), source_r(), status); break;
case Operation::XOR: Primitive::xor_(destination_rmw(), source_r(), status); break;
case Operation::NEG: Primitive::neg(source_rmw(), status); break; // TODO: should be a destination.
case Operation::NOT: Primitive::not_(source_rmw()); break; // TODO: should be a destination.
2023-10-09 15:46:59 +00:00
case Operation::CALLrel:
2023-11-01 18:49:30 +00:00
Primitive::call_relative(instruction.displacement(), registers, memory, flow_controller);
2023-10-09 15:46:59 +00:00
return;
case Operation::CALLabs:
2023-11-01 18:49:30 +00:00
Primitive::call_absolute(destination_r(), registers, memory, flow_controller);
2023-10-09 15:46:59 +00:00
return;
case Operation::CALLfar:
Primitive::call_far<model>(instruction, flow_controller, registers, memory);
return;
2023-10-09 15:59:38 +00:00
2023-10-18 17:15:00 +00:00
case Operation::JMPrel: jcc(true); return;
2023-10-29 20:19:10 +00:00
case Operation::JMPabs: Primitive::jump_absolute(destination_r(), flow_controller); return;
2023-10-18 17:15:00 +00:00
case Operation::JMPfar: Primitive::jump_far<model>(instruction, flow_controller, registers, memory); return;
2023-10-18 18:04:21 +00:00
case Operation::JCXZ: jcc(!eCX()); return;
case Operation::LOOP: Primitive::loop(eCX(), instruction.offset(), registers, flow_controller); return;
case Operation::LOOPE: Primitive::loope(eCX(), instruction.offset(), registers, status, flow_controller); return;
case Operation::LOOPNE: Primitive::loopne(eCX(), instruction.offset(), registers, status, flow_controller); return;
2023-10-18 17:15:00 +00:00
case Operation::IRET: Primitive::iret(registers, flow_controller, memory, status); return;
2023-10-16 19:40:24 +00:00
case Operation::RETnear: Primitive::ret_near(instruction, registers, flow_controller, memory); return;
case Operation::RETfar: Primitive::ret_far(instruction, registers, flow_controller, memory); return;
2023-10-11 20:01:09 +00:00
case Operation::INT: Primitive::int_(instruction.operand(), flow_controller); return;
case Operation::INTO: Primitive::into(status, flow_controller); return;
2023-10-12 18:24:28 +00:00
case Operation::SAHF: Primitive::sahf(registers.ah(), status); return;
case Operation::LAHF: Primitive::lahf(registers.ah(), status); return;
2023-10-29 20:19:10 +00:00
case Operation::LDS: if constexpr (data_size == DataSize::Word) Primitive::ld<model, Source::DS>(instruction, destination_w(), memory, registers); return;
case Operation::LES: if constexpr (data_size == DataSize::Word) Primitive::ld<model, Source::ES>(instruction, destination_w(), memory, registers); return;
2023-10-12 17:54:51 +00:00
2023-10-29 20:19:10 +00:00
case Operation::LEA: Primitive::lea<model>(instruction, destination_w(), memory, registers); return;
case Operation::MOV: Primitive::mov(destination_w(), source_r()); break;
2023-10-12 18:31:25 +00:00
2023-10-11 18:36:42 +00:00
case Operation::JO: jcc(status.condition<Condition::Overflow>()); return;
case Operation::JNO: jcc(!status.condition<Condition::Overflow>()); return;
case Operation::JB: jcc(status.condition<Condition::Below>()); return;
case Operation::JNB: jcc(!status.condition<Condition::Below>()); return;
case Operation::JZ: jcc(status.condition<Condition::Zero>()); return;
case Operation::JNZ: jcc(!status.condition<Condition::Zero>()); return;
case Operation::JBE: jcc(status.condition<Condition::BelowOrEqual>()); return;
case Operation::JNBE: jcc(!status.condition<Condition::BelowOrEqual>()); return;
case Operation::JS: jcc(status.condition<Condition::Sign>()); return;
case Operation::JNS: jcc(!status.condition<Condition::Sign>()); return;
case Operation::JP: jcc(!status.condition<Condition::ParityOdd>()); return;
case Operation::JNP: jcc(status.condition<Condition::ParityOdd>()); return;
case Operation::JL: jcc(status.condition<Condition::Less>()); return;
case Operation::JNL: jcc(!status.condition<Condition::Less>()); return;
case Operation::JLE: jcc(status.condition<Condition::LessOrEqual>()); return;
case Operation::JNLE: jcc(!status.condition<Condition::LessOrEqual>()); return;
2023-10-10 20:27:06 +00:00
2023-10-29 20:19:10 +00:00
case Operation::RCL: Primitive::rcl(destination_rmw(), shift_count(), status); break;
case Operation::RCR: Primitive::rcr(destination_rmw(), shift_count(), status); break;
case Operation::ROL: Primitive::rol(destination_rmw(), shift_count(), status); break;
case Operation::ROR: Primitive::ror(destination_rmw(), shift_count(), status); break;
case Operation::SAL: Primitive::sal(destination_rmw(), shift_count(), status); break;
case Operation::SAR: Primitive::sar(destination_rmw(), shift_count(), status); break;
case Operation::SHR: Primitive::shr(destination_rmw(), shift_count(), status); break;
2023-10-13 18:44:22 +00:00
2023-10-09 15:59:38 +00:00
case Operation::CLC: Primitive::clc(status); return;
case Operation::CLD: Primitive::cld(status); return;
case Operation::CLI: Primitive::cli(status); return;
2023-10-10 02:16:37 +00:00
case Operation::STC: Primitive::stc(status); return;
case Operation::STD: Primitive::std(status); return;
case Operation::STI: Primitive::sti(status); return;
2023-10-09 15:59:38 +00:00
case Operation::CMC: Primitive::cmc(status); return;
2023-10-11 02:34:42 +00:00
case Operation::XCHG: Primitive::xchg(destination_rmw(), source_rmw()); break;
2023-10-12 19:52:05 +00:00
case Operation::SALC: Primitive::salc(registers.al(), status); return;
2023-10-12 19:52:05 +00:00
case Operation::SETMO:
if constexpr (model == Model::i8086) {
2023-10-29 20:19:10 +00:00
Primitive::setmo(destination_w(), status);
break;
2023-10-12 19:52:05 +00:00
} else {
// TODO.
}
return;
case Operation::SETMOC:
if constexpr (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.
if(registers.cl()) {
Primitive::setmo(destination_w(), status);
}
2023-10-29 20:19:10 +00:00
break;
2023-10-12 19:52:05 +00:00
} else {
// TODO.
}
return;
2023-10-13 01:12:03 +00:00
2023-10-22 02:52:50 +00:00
case Operation::OUT: Primitive::out(port(instruction.destination().source()), pair_low(), io); return;
case Operation::IN: Primitive::in(port(instruction.source().source()), pair_low(), io); return;
2023-10-22 02:37:25 +00:00
case Operation::XLAT: Primitive::xlat<AddressT>(instruction, memory, registers); return;
2023-10-18 19:59:39 +00:00
2023-11-01 01:55:22 +00:00
case Operation::POP: destination_w() = Primitive::pop<IntT, false>(memory, registers); break;
case Operation::PUSH: Primitive::push<IntT, false>(source_r(), memory, registers); break;
case Operation::POPF: Primitive::popf(memory, registers, status); break;
case Operation::PUSHF: Primitive::pushf(memory, registers, status); break;
case Operation::CMPS:
Primitive::cmps<IntT, AddressT, Repetition::None>(instruction, eCX(), eSI(), eDI(), memory, status, flow_controller);
break;
case Operation::CMPS_REPE:
Primitive::cmps<IntT, AddressT, Repetition::RepE>(instruction, eCX(), eSI(), eDI(), memory, status, flow_controller);
break;
case Operation::CMPS_REPNE:
Primitive::cmps<IntT, AddressT, Repetition::RepNE>(instruction, eCX(), eSI(), eDI(), memory, status, flow_controller);
break;
case Operation::SCAS:
Primitive::scas<IntT, AddressT, Repetition::None>(eCX(), eDI(), pair_low(), memory, status, flow_controller);
break;
case Operation::SCAS_REPE:
Primitive::scas<IntT, AddressT, Repetition::RepE>(eCX(), eDI(), pair_low(), memory, status, flow_controller);
break;
case Operation::SCAS_REPNE:
Primitive::scas<IntT, AddressT, Repetition::RepNE>(eCX(), eDI(), pair_low(), memory, status, flow_controller);
break;
case Operation::LODS:
Primitive::lods<IntT, AddressT, Repetition::None>(instruction, eCX(), eSI(), pair_low(), memory, status, flow_controller);
break;
case Operation::LODS_REP:
Primitive::lods<IntT, AddressT, Repetition::Rep>(instruction, eCX(), eSI(), pair_low(), memory, status, flow_controller);
break;
case Operation::MOVS:
Primitive::movs<IntT, AddressT, Repetition::None>(instruction, eCX(), eSI(), eDI(), memory, status, flow_controller);
break;
case Operation::MOVS_REP:
Primitive::movs<IntT, AddressT, Repetition::Rep>(instruction, eCX(), eSI(), eDI(), memory, status, flow_controller);
break;
case Operation::STOS:
Primitive::stos<IntT, AddressT, Repetition::None>(eCX(), eDI(), pair_low(), memory, status, flow_controller);
break;
case Operation::STOS_REP:
Primitive::stos<IntT, AddressT, Repetition::Rep>(eCX(), eDI(), pair_low(), memory, status, flow_controller);
2023-10-21 01:54:30 +00:00
break;
2023-10-22 02:52:50 +00:00
case Operation::OUTS:
Primitive::outs<IntT, AddressT, Repetition::None>(instruction, eCX(), registers.dx(), eSI(), memory, io, status, flow_controller);
break;
case Operation::OUTS_REP:
Primitive::outs<IntT, AddressT, Repetition::Rep>(instruction, eCX(), registers.dx(), eSI(), memory, io, status, flow_controller);
2023-10-22 02:52:50 +00:00
break;
2023-10-22 02:52:50 +00:00
case Operation::INS:
Primitive::ins<IntT, AddressT, Repetition::None>(eCX(), registers.dx(), eDI(), memory, io, status, flow_controller);
break;
case Operation::INS_REP:
Primitive::ins<IntT, AddressT, Repetition::Rep>(eCX(), registers.dx(), eDI(), memory, io, status, flow_controller);
2023-10-22 02:52:50 +00:00
break;
2023-10-05 18:37:58 +00:00
}
// Write to memory if required to complete this operation.
memory.template write_back<IntT>();
2023-10-05 18:37:58 +00:00
}
template <
2023-10-05 18:37:58 +00:00
Model model,
typename InstructionT,
typename FlowControllerT,
typename RegistersT,
typename MemoryT,
typename IOT
2023-10-05 18:37:58 +00:00
> void perform(
const InstructionT &instruction,
Status &status,
FlowControllerT &flow_controller,
RegistersT &registers,
MemoryT &memory,
IOT &io
) {
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):
perform<model, DataSize::Byte, AddressSize::b16>(instruction, status, flow_controller, registers, memory, io);
return;
case size(DataSize::Word, AddressSize::b16):
perform<model, DataSize::Word, AddressSize::b16>(instruction, status, flow_controller, registers, memory, io);
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):
if constexpr (is_32bit(model)) {
perform<model, DataSize::Byte, AddressSize::b32>(instruction, status, flow_controller, registers, memory, io);
return;
}
break;
2023-10-25 20:00:01 +00:00
case size(DataSize::Word, AddressSize::b32):
if constexpr (is_32bit(model)) {
perform<model, DataSize::Word, AddressSize::b32>(instruction, status, flow_controller, registers, memory, io);
return;
}
break;
2023-10-25 20:00:01 +00:00
case size(DataSize::DWord, AddressSize::b16):
2023-10-10 01:50:17 +00:00
if constexpr (is_32bit(model)) {
2023-10-25 20:00:01 +00:00
perform<model, DataSize::DWord, AddressSize::b16>(instruction, status, flow_controller, registers, memory, io);
return;
2023-10-10 01:50:17 +00:00
}
break;
2023-10-25 20:00:01 +00:00
case size(DataSize::DWord, AddressSize::b32):
if constexpr (is_32bit(model)) {
perform<model, DataSize::DWord, AddressSize::b32>(instruction, status, flow_controller, registers, memory, io);
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 18:37:58 +00:00
}
#endif /* PerformImplementation_h */