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"
|
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-11 02:35:25 +00:00
|
|
|
|
#include <utility>
|
|
|
|
|
|
2023-10-05 18:37:58 +00:00
|
|
|
|
namespace InstructionSet::x86 {
|
|
|
|
|
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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 ®isters,
|
|
|
|
|
MemoryT &memory,
|
|
|
|
|
IntT *none = nullptr,
|
|
|
|
|
IntT *immediate = nullptr
|
|
|
|
|
);
|
|
|
|
|
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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 ®isters,
|
|
|
|
|
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;
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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
|
|
|
|
}
|
2023-10-29 18:33:39 +00:00
|
|
|
|
return address + *resolve<model, uint16_t, access>(instruction, pointer.base(), pointer, registers, memory);
|
2023-10-09 15:46:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-29 18:33:39 +00:00
|
|
|
|
template <Model model, typename IntT, AccessType access, Source source, typename RegistersT>
|
2023-10-23 02:15:27 +00:00
|
|
|
|
IntT *register_(RegistersT ®isters) {
|
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 ®isters.eax(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint16_t>) { return ®isters.ax(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint8_t>) { return ®isters.al(); }
|
|
|
|
|
else { return nullptr; }
|
|
|
|
|
case Source::eCX:
|
|
|
|
|
if constexpr (is_32bit(model) && std::is_same_v<IntT, uint32_t>) { return ®isters.ecx(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint16_t>) { return ®isters.cx(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint8_t>) { return ®isters.cl(); }
|
|
|
|
|
else { return nullptr; }
|
|
|
|
|
case Source::eDX:
|
|
|
|
|
if constexpr (is_32bit(model) && std::is_same_v<IntT, uint32_t>) { return ®isters.edx(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint16_t>) { return ®isters.dx(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint8_t>) { return ®isters.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 ®isters.ebx(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint16_t>) { return ®isters.bx(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint8_t>) { return ®isters.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 ®isters.esp(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint16_t>) { return ®isters.sp(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint8_t>) { return ®isters.ah(); }
|
|
|
|
|
else { return nullptr; }
|
|
|
|
|
case Source::eBPorCH:
|
|
|
|
|
if constexpr (is_32bit(model) && std::is_same_v<IntT, uint32_t>) { return ®isters.ebp(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint16_t>) { return ®isters.bp(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint8_t>) { return ®isters.ch(); }
|
|
|
|
|
else { return nullptr; }
|
|
|
|
|
case Source::eSIorDH:
|
|
|
|
|
if constexpr (is_32bit(model) && std::is_same_v<IntT, uint32_t>) { return ®isters.esi(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint16_t>) { return ®isters.si(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint8_t>) { return ®isters.dh(); }
|
|
|
|
|
else { return nullptr; }
|
|
|
|
|
case Source::eDIorBH:
|
|
|
|
|
if constexpr (is_32bit(model) && std::is_same_v<IntT, uint32_t>) { return ®isters.edi(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint16_t>) { return ®isters.di(); }
|
|
|
|
|
else if constexpr (std::is_same_v<IntT, uint8_t>) { return ®isters.bh(); }
|
|
|
|
|
else { return nullptr; }
|
|
|
|
|
|
2023-10-23 02:15:27 +00:00
|
|
|
|
default: return nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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 ®isters,
|
|
|
|
|
MemoryT &memory
|
|
|
|
|
) {
|
2023-10-29 18:33:39 +00:00
|
|
|
|
// 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.
|
2023-10-25 18:43:58 +00:00
|
|
|
|
switch(pointer.source()) {
|
2023-10-23 02:15:27 +00:00
|
|
|
|
default: return 0;
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-29 18:33:39 +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 ®isters,
|
|
|
|
|
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) {
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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 ®isters.es(); else return nullptr;
|
|
|
|
|
case Source::CS: if constexpr (std::is_same_v<IntT, uint16_t>) return ®isters.cs(); else return nullptr;
|
|
|
|
|
case Source::SS: if constexpr (std::is_same_v<IntT, uint16_t>) return ®isters.ss(); else return nullptr;
|
|
|
|
|
case Source::DS: if constexpr (std::is_same_v<IntT, uint16_t>) return ®isters.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 ®isters.fs(); else return nullptr;
|
|
|
|
|
case Source::GS: if constexpr (is_32bit(model) && std::is_same_v<IntT, uint16_t>) return ®isters.gs(); else return nullptr;
|
|
|
|
|
|
|
|
|
|
case Source::Immediate:
|
|
|
|
|
*immediate = instruction.operand();
|
|
|
|
|
return immediate;
|
|
|
|
|
|
|
|
|
|
case Source::None: return none;
|
|
|
|
|
|
|
|
|
|
case Source::Indirect:
|
2023-10-29 18:33:39 +00:00
|
|
|
|
target_address = address<model, Source::Indirect, IntT, access>(instruction, pointer, registers, memory);
|
2023-10-09 15:46:59 +00:00
|
|
|
|
break;
|
|
|
|
|
case Source::IndirectNoBase:
|
2023-10-29 18:33:39 +00:00
|
|
|
|
target_address = address<model, Source::IndirectNoBase, IntT, access>(instruction, pointer, registers, memory);
|
2023-10-09 15:46:59 +00:00
|
|
|
|
break;
|
|
|
|
|
case Source::DirectAddress:
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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.
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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.
|
2023-11-01 18:11:10 +00:00
|
|
|
|
template <typename IntT, bool Preauthorised, typename MemoryT, typename RegistersT>
|
2023-10-18 19:59:39 +00:00
|
|
|
|
void push(IntT &value, MemoryT &memory, RegistersT ®isters) {
|
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>();
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-01 18:11:10 +00:00
|
|
|
|
template <typename IntT, bool Preauthorised, typename MemoryT, typename RegistersT>
|
2023-10-16 19:40:24 +00:00
|
|
|
|
IntT pop(MemoryT &memory, RegistersT ®isters) {
|
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 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.
|
|
|
|
|
*/
|
2023-10-11 16:35:17 +00:00
|
|
|
|
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;
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Carry, Flag::AuxiliaryCarry>(1);
|
2023-10-05 18:37:58 +00:00
|
|
|
|
} else {
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Carry, Flag::AuxiliaryCarry>(0);
|
2023-10-05 18:37:58 +00:00
|
|
|
|
}
|
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;
|
2023-10-11 15:15:59 +00:00
|
|
|
|
status.set_from<uint8_t, Flag::Zero, Flag::Sign, Flag::ParityOdd>(ax.halves.low);
|
2023-10-05 18:37:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-06 17:22:35 +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.
|
|
|
|
|
*/
|
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) {
|
2023-10-10 14:34:18 +00:00
|
|
|
|
flow_controller.interrupt(Interrupt::DivideError);
|
2023-10-06 17:22:35 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 18:52:24 +00:00
|
|
|
|
ax.halves.high = ax.halves.low / imm;
|
|
|
|
|
ax.halves.low = ax.halves.low % imm;
|
2023-10-11 15:15:59 +00:00
|
|
|
|
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.
|
|
|
|
|
*/
|
2023-10-11 16:35:17 +00:00
|
|
|
|
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;
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Carry, Flag::AuxiliaryCarry>(1);
|
2023-10-05 18:52:24 +00:00
|
|
|
|
} else {
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Carry, Flag::AuxiliaryCarry>(0);
|
2023-10-05 18:52:24 +00:00
|
|
|
|
}
|
|
|
|
|
ax.halves.low &= 0x0f;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-09 18:27:02 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2023-10-09 18:27:02 +00:00
|
|
|
|
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 *)
|
2023-10-09 18:27:02 +00:00
|
|
|
|
AF ← 1;
|
|
|
|
|
ELSE
|
|
|
|
|
AF ← 0;
|
|
|
|
|
FI;
|
2023-10-09 18:42:32 +00:00
|
|
|
|
IF ((old_AL > 99H) or old_CF = 1)
|
2023-10-09 18:27:02 +00:00
|
|
|
|
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;
|
2023-10-11 16:35:17 +00:00
|
|
|
|
const auto old_carry = status.flag<Flag::Carry>();
|
|
|
|
|
status.set_from<Flag::Carry>(0);
|
2023-10-09 18:42:32 +00:00
|
|
|
|
|
2023-10-11 16:35:17 +00:00
|
|
|
|
if((al & 0x0f) > 0x09 || status.flag<Flag::AuxiliaryCarry>()) {
|
|
|
|
|
status.set_from<Flag::Carry>(old_carry | (al > 0xf9));
|
2023-10-09 18:27:02 +00:00
|
|
|
|
al += 0x06;
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::AuxiliaryCarry>(1);
|
2023-10-09 18:27:02 +00:00
|
|
|
|
} else {
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::AuxiliaryCarry>(0);
|
2023-10-09 18:27:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-09 18:42:32 +00:00
|
|
|
|
if(old_al > 0x99 || old_carry) {
|
2023-10-09 18:27:02 +00:00
|
|
|
|
al += 0x60;
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Carry>(1);
|
2023-10-09 18:27:02 +00:00
|
|
|
|
} else {
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Carry>(0);
|
2023-10-09 18:27:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-11 15:15:59 +00:00
|
|
|
|
status.set_from<uint8_t, Flag::Zero, Flag::Sign, Flag::ParityOdd>(al);
|
2023-10-09 18:27:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-09 18:47:39 +00:00
|
|
|
|
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;
|
2023-10-11 16:35:17 +00:00
|
|
|
|
const auto old_carry = status.flag<Flag::Carry>();
|
|
|
|
|
status.set_from<Flag::Carry>(0);
|
2023-10-09 18:47:39 +00:00
|
|
|
|
|
2023-10-11 16:35:17 +00:00
|
|
|
|
if((al & 0x0f) > 0x09 || status.flag<Flag::AuxiliaryCarry>()) {
|
|
|
|
|
status.set_from<Flag::Carry>(old_carry | (al < 0x06));
|
2023-10-09 18:47:39 +00:00
|
|
|
|
al -= 0x06;
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::AuxiliaryCarry>(1);
|
2023-10-09 18:47:39 +00:00
|
|
|
|
} else {
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::AuxiliaryCarry>(0);
|
2023-10-09 18:47:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(old_al > 0x99 || old_carry) {
|
|
|
|
|
al -= 0x60;
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Carry>(1);
|
2023-10-09 18:47:39 +00:00
|
|
|
|
} else {
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Carry>(0);
|
2023-10-09 18:47:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-11 15:15:59 +00:00
|
|
|
|
status.set_from<uint8_t, Flag::Zero, Flag::Sign, Flag::ParityOdd>(al);
|
2023-10-09 18:47:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
2023-10-11 16:35:17 +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
|
|
|
|
|
2023-10-11 15:15:59 +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
|
|
|
|
|
2023-10-11 16:35:17 +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
|
|
|
|
|
2023-10-11 15:15:59 +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;
|
|
|
|
|
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Carry, Flag::Overflow>(0);
|
2023-10-11 15:15:59 +00:00
|
|
|
|
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;
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Overflow, Flag::Carry>(destination_high);
|
2023-10-10 01:50:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-10 02:12:15 +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;
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Overflow, Flag::Carry>(destination_high != sign_extension);
|
2023-10-10 02:12:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Overflow>(destination == Numeric::top_bit<IntT>());
|
|
|
|
|
status.set_from<Flag::AuxiliaryCarry>(((destination - 1) ^ destination) & 0x10);
|
2023-10-11 15:15:59 +00:00
|
|
|
|
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 ®isters, 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 ®isters, 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 ®isters, 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 ®isters, 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.
|
|
|
|
|
*/
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Overflow>(destination == Numeric::top_bit<IntT>());
|
2023-10-10 19:57:33 +00:00
|
|
|
|
|
|
|
|
|
--destination;
|
|
|
|
|
|
2023-10-11 15:15:59 +00:00
|
|
|
|
status.set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(destination);
|
2023-10-11 16:35:17 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Overflow, Flag::Carry>(0);
|
2023-10-11 15:15:59 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Overflow, Flag::Carry>(0);
|
2023-10-11 15:15:59 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Overflow, Flag::Carry>(0);
|
2023-10-11 15:15:59 +00:00
|
|
|
|
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.
|
|
|
|
|
*/
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::AuxiliaryCarry>(Numeric::carried_in<4>(IntT(0), destination, IntT(-destination)));
|
2023-10-11 02:09:10 +00:00
|
|
|
|
|
|
|
|
|
destination = -destination;
|
|
|
|
|
|
2023-10-11 16:35:17 +00:00
|
|
|
|
status.set_from<Flag::Carry>(destination);
|
|
|
|
|
status.set_from<Flag::Overflow>(destination == Numeric::top_bit<IntT>());
|
2023-10-11 15:15:59 +00:00
|
|
|
|
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 ®isters, 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 ®isters, 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 ®isters,
|
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();
|
2023-10-25 18:43:58 +00:00
|
|
|
|
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 ®isters,
|
|
|
|
|
MemoryT &memory
|
|
|
|
|
) {
|
|
|
|
|
// TODO: eliminate 16-bit assumption below.
|
|
|
|
|
uint16_t source_address = 0;
|
|
|
|
|
const auto pointer = instruction.destination();
|
2023-10-25 18:43:58 +00:00
|
|
|
|
switch(pointer.source()) {
|
2023-10-18 17:15:00 +00:00
|
|
|
|
default:
|
2023-10-23 14:07:19 +00:00
|
|
|
|
case Source::Immediate: flow_controller.jump(instruction.segment(), instruction.offset()); return;
|
2023-10-18 17:15:00 +00:00
|
|
|
|
|
|
|
|
|
case Source::Indirect:
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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:
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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:
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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
|
|
|
|
|
2023-10-29 18:33:39 +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;
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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 ®isters, 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 ®isters, 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 ®isters, 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 ®isters
|
|
|
|
|
) {
|
|
|
|
|
const auto pointer = instruction.source();
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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
|
|
|
|
|
2023-10-29 18:33:39 +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) {
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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 ®isters
|
|
|
|
|
) {
|
|
|
|
|
// 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 ®isters
|
|
|
|
|
) {
|
|
|
|
|
AddressT address;
|
|
|
|
|
if constexpr (std::is_same_v<AddressT, uint16_t>) {
|
|
|
|
|
address = registers.bx() + registers.al();
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
2023-10-11 16:35:17 +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 ®isters, 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 ®isters, 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
|
|
|
|
}
|
|
|
|
|
|
2023-10-27 03:08:07 +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
|
|
|
|
}
|
|
|
|
|
|
2023-10-27 03:08:07 +00:00
|
|
|
|
template <typename AddressT, Repetition repetition, typename FlowControllerT>
|
2023-10-27 20:27:24 +00:00
|
|
|
|
void repeat([[maybe_unused]] Status &status, AddressT &eCX, FlowControllerT &flow_controller) {
|
2023-10-21 01:36:50 +00:00
|
|
|
|
if(
|
2023-10-27 03:08:07 +00:00
|
|
|
|
repetition == Repetition::None || // No repetition => stop.
|
|
|
|
|
!(--eCX) // [e]cx is zero after being decremented => stop.
|
2023-10-21 01:36:50 +00:00
|
|
|
|
) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-10-27 20:27:24 +00:00
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-27 03:08:07 +00:00
|
|
|
|
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) {
|
2023-10-27 03:08:07 +00:00
|
|
|
|
if(repetition_over<AddressT, repetition>(eCX)) {
|
2023-10-19 18:07:59 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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);
|
2023-10-20 20:52:47 +00:00
|
|
|
|
|
|
|
|
|
Primitive::sub<false, false>(lhs, rhs, status);
|
2023-10-19 18:07:59 +00:00
|
|
|
|
|
2023-10-27 20:27:24 +00:00
|
|
|
|
repeat<AddressT, repetition>(status, eCX, flow_controller);
|
2023-10-19 18:07:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-27 03:08:07 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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);
|
|
|
|
|
|
2023-10-27 20:27:24 +00:00
|
|
|
|
repeat<AddressT, repetition>(status, eCX, flow_controller);
|
2023-10-21 01:54:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-27 03:08:07 +00:00
|
|
|
|
template <typename IntT, typename AddressT, Repetition repetition, typename InstructionT, typename MemoryT, typename FlowControllerT>
|
2023-10-20 21:13:56 +00:00
|
|
|
|
void lods(const InstructionT &instruction, AddressT &eCX, AddressT &eSI, IntT &eAX, MemoryT &memory, Status &status, FlowControllerT &flow_controller) {
|
2023-10-27 03:08:07 +00:00
|
|
|
|
if(repetition_over<AddressT, repetition>(eCX)) {
|
2023-10-20 21:13:56 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-29 18:33:39 +00:00
|
|
|
|
eAX = memory.template access<IntT, AccessType::Read>(instruction.data_segment(), eSI);
|
2023-10-20 21:13:56 +00:00
|
|
|
|
eSI += status.direction<AddressT>() * sizeof(IntT);
|
|
|
|
|
|
2023-10-27 20:27:24 +00:00
|
|
|
|
repeat<AddressT, repetition>(status, eCX, flow_controller);
|
2023-10-20 21:13:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-27 03:08:07 +00:00
|
|
|
|
template <typename IntT, typename AddressT, Repetition repetition, typename InstructionT, typename MemoryT, typename FlowControllerT>
|
2023-10-21 01:46:47 +00:00
|
|
|
|
void movs(const InstructionT &instruction, AddressT &eCX, AddressT &eSI, AddressT &eDI, MemoryT &memory, Status &status, FlowControllerT &flow_controller) {
|
2023-10-27 03:08:07 +00:00
|
|
|
|
if(repetition_over<AddressT, repetition>(eCX)) {
|
2023-10-21 01:46:47 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-29 18:33:39 +00:00
|
|
|
|
memory.template access<IntT, AccessType::Write>(Source::ES, eDI) = memory.template access<IntT, AccessType::Read>(instruction.data_segment(), eSI);
|
2023-10-21 01:46:47 +00:00
|
|
|
|
|
|
|
|
|
eSI += status.direction<AddressT>() * sizeof(IntT);
|
|
|
|
|
eDI += status.direction<AddressT>() * sizeof(IntT);
|
|
|
|
|
|
2023-10-27 20:27:24 +00:00
|
|
|
|
repeat<AddressT, repetition>(status, eCX, flow_controller);
|
2023-10-21 01:46:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-27 03:08:07 +00:00
|
|
|
|
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)) {
|
2023-10-21 01:46:47 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-29 18:33:39 +00:00
|
|
|
|
memory.template access<IntT, AccessType::Write>(Source::ES, eDI) = eAX;
|
2023-10-21 01:46:47 +00:00
|
|
|
|
eDI += status.direction<AddressT>() * sizeof(IntT);
|
|
|
|
|
|
2023-10-27 20:27:24 +00:00
|
|
|
|
repeat<AddressT, repetition>(status, eCX, flow_controller);
|
2023-10-21 01:46:47 +00:00
|
|
|
|
}
|
2023-10-20 21:13:56 +00:00
|
|
|
|
|
2023-10-27 03:08:07 +00:00
|
|
|
|
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) {
|
2023-10-27 03:08:07 +00:00
|
|
|
|
if(repetition_over<AddressT, repetition>(eCX)) {
|
2023-10-22 02:52:50 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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);
|
|
|
|
|
|
2023-10-27 20:27:24 +00:00
|
|
|
|
repeat<AddressT, repetition>(status, eCX, flow_controller);
|
2023-10-22 02:52:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-27 03:08:07 +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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-29 18:33:39 +00:00
|
|
|
|
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);
|
|
|
|
|
|
2023-10-27 20:27:24 +00:00
|
|
|
|
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,
|
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-09 15:46:59 +00:00
|
|
|
|
FlowControllerT &flow_controller,
|
2023-10-05 20:49:02 +00:00
|
|
|
|
RegistersT ®isters,
|
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
|
|
|
|
) {
|
2023-10-05 20:49:02 +00:00
|
|
|
|
using IntT = typename DataSizeType<data_size>::type;
|
2023-10-25 20:00:01 +00:00
|
|
|
|
using AddressT = typename AddressSizeType<address_size>::type;
|
2023-10-05 20:49:02 +00:00
|
|
|
|
|
2023-10-31 19:06:19 +00:00
|
|
|
|
// Establish source() and destination() shorthands to fetch data if necessary.
|
|
|
|
|
//
|
|
|
|
|
// C++17, which this project targets at the time of writing, does not provide templatised lambdas.
|
|
|
|
|
// So the following division is in part a necessity.
|
|
|
|
|
//
|
|
|
|
|
// (though GCC offers C++20 syntax as an extension, and Clang seems to follow along, so maybe I'm overthinking)
|
2023-10-09 01:41:36 +00:00
|
|
|
|
IntT immediate;
|
2023-10-31 19:06:19 +00:00
|
|
|
|
const auto source_r = [&]() -> IntT& {
|
2023-10-29 20:19:10 +00:00
|
|
|
|
return *resolve<model, IntT, AccessType::Read>(
|
2023-10-09 01:41:36 +00:00
|
|
|
|
instruction,
|
2023-10-25 18:43:58 +00:00
|
|
|
|
instruction.source().source(),
|
2023-10-09 01:41:36 +00:00
|
|
|
|
instruction.source(),
|
|
|
|
|
registers,
|
|
|
|
|
memory,
|
|
|
|
|
nullptr,
|
|
|
|
|
&immediate);
|
|
|
|
|
};
|
2023-10-31 19:06:19 +00:00
|
|
|
|
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>(
|
2023-10-09 01:41:36 +00:00
|
|
|
|
instruction,
|
2023-10-25 18:43:58 +00:00
|
|
|
|
instruction.destination().source(),
|
2023-10-09 01:41:36 +00:00
|
|
|
|
instruction.destination(),
|
|
|
|
|
registers,
|
|
|
|
|
memory,
|
|
|
|
|
nullptr,
|
|
|
|
|
&immediate);
|
|
|
|
|
};
|
2023-10-05 20:49:02 +00:00
|
|
|
|
|
2023-10-11 19:08:04 +00:00
|
|
|
|
// Performs a displacement jump only if @c condition is true.
|
2023-10-11 18:36:42 +00:00
|
|
|
|
const auto jcc = [&](bool condition) {
|
|
|
|
|
Primitive::jump(
|
|
|
|
|
condition,
|
|
|
|
|
instruction.displacement(),
|
|
|
|
|
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;
|
2023-10-25 18:43:58 +00:00
|
|
|
|
switch(instruction.source().source()) {
|
2023-10-13 18:44:22 +00:00
|
|
|
|
case Source::None: return 1;
|
2023-10-13 19:34:06 +00:00
|
|
|
|
case Source::Immediate: return uint8_t(instruction.operand()) & mask;
|
|
|
|
|
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();
|
|
|
|
|
};
|
|
|
|
|
|
2023-10-20 21:00:32 +00:00
|
|
|
|
// For the string operations, evaluate to either SI and DI or ESI and EDI, depending on the address size.
|
2023-10-20 20:52:47 +00:00
|
|
|
|
const auto eSI = [&]() -> AddressT& {
|
|
|
|
|
if constexpr (std::is_same_v<AddressT, uint16_t>) {
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-10-20 21:00:32 +00:00
|
|
|
|
// For counts, provide either eCX or CX depending on address size.
|
|
|
|
|
const auto eCX = [&]() -> AddressT& {
|
|
|
|
|
if constexpr (std::is_same_v<AddressT, uint16_t>) {
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
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.
|
2023-10-27 03:19:31 +00:00
|
|
|
|
switch(instruction.operation()) {
|
2023-10-09 15:46:59 +00:00
|
|
|
|
default:
|
|
|
|
|
assert(false);
|
2023-10-06 02:27:52 +00:00
|
|
|
|
|
2023-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-09 18:27:02 +00:00
|
|
|
|
case Operation::DAA: Primitive::daa(registers.al(), status); return;
|
2023-10-09 18:47:39 +00:00
|
|
|
|
case Operation::DAS: Primitive::das(registers.al(), status); return;
|
2023-10-05 20:49:02 +00:00
|
|
|
|
|
2023-10-11 19:08:04 +00:00
|
|
|
|
case Operation::CBW: Primitive::cbw(pair_low()); return;
|
|
|
|
|
case Operation::CWD: Primitive::cwd(pair_high(), pair_low()); return;
|
2023-10-09 18:54:14 +00:00
|
|
|
|
|
2023-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;
|
|
|
|
|
|
2023-10-31 19:06:19 +00:00
|
|
|
|
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;
|
2023-10-31 19:06:19 +00:00
|
|
|
|
case Operation::TEST: Primitive::test(destination_r(), source_r(), status); return;
|
2023-10-11 02:15:33 +00:00
|
|
|
|
|
2023-10-31 19:06:19 +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
|
|
|
|
|
2023-10-31 19:06:19 +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
|
|
|
|
|
2023-10-20 21:00:32 +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;
|
2023-10-31 19:06:19 +00:00
|
|
|
|
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
|
|
|
|
|
2023-10-31 19:06:19 +00:00
|
|
|
|
case Operation::XCHG: Primitive::xchg(destination_rmw(), source_rmw()); break;
|
2023-10-12 19:52:05 +00:00
|
|
|
|
|
2023-10-31 19:06:19 +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
|
|
|
|
|
2023-10-20 21:00:32 +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;
|
2023-10-19 18:07:59 +00:00
|
|
|
|
|
|
|
|
|
case Operation::CMPS:
|
2023-10-27 03:08:07 +00:00
|
|
|
|
Primitive::cmps<IntT, AddressT, Repetition::None>(instruction, eCX(), eSI(), eDI(), memory, status, flow_controller);
|
2023-10-19 18:07:59 +00:00
|
|
|
|
break;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2023-10-20 21:13:56 +00:00
|
|
|
|
case Operation::LODS:
|
2023-10-27 03:08:07 +00:00
|
|
|
|
Primitive::lods<IntT, AddressT, Repetition::None>(instruction, eCX(), eSI(), pair_low(), memory, status, flow_controller);
|
|
|
|
|
break;
|
|
|
|
|
case Operation::LODS_REP:
|
2023-10-27 20:27:24 +00:00
|
|
|
|
Primitive::lods<IntT, AddressT, Repetition::Rep>(instruction, eCX(), eSI(), pair_low(), memory, status, flow_controller);
|
2023-10-20 21:13:56 +00:00
|
|
|
|
break;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
|
2023-10-21 01:46:47 +00:00
|
|
|
|
case Operation::MOVS:
|
2023-10-27 03:08:07 +00:00
|
|
|
|
Primitive::movs<IntT, AddressT, Repetition::None>(instruction, eCX(), eSI(), eDI(), memory, status, flow_controller);
|
|
|
|
|
break;
|
|
|
|
|
case Operation::MOVS_REP:
|
2023-10-27 20:27:24 +00:00
|
|
|
|
Primitive::movs<IntT, AddressT, Repetition::Rep>(instruction, eCX(), eSI(), eDI(), memory, status, flow_controller);
|
2023-10-21 01:46:47 +00:00
|
|
|
|
break;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
|
2023-10-21 01:46:47 +00:00
|
|
|
|
case Operation::STOS:
|
2023-10-27 03:08:07 +00:00
|
|
|
|
Primitive::stos<IntT, AddressT, Repetition::None>(eCX(), eDI(), pair_low(), memory, status, flow_controller);
|
2023-10-21 01:46:47 +00:00
|
|
|
|
break;
|
2023-10-27 03:08:07 +00:00
|
|
|
|
case Operation::STOS_REP:
|
2023-10-27 20:27:24 +00:00
|
|
|
|
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-27 03:08:07 +00:00
|
|
|
|
|
2023-10-22 02:52:50 +00:00
|
|
|
|
case Operation::OUTS:
|
2023-10-27 03:08:07 +00:00
|
|
|
|
Primitive::outs<IntT, AddressT, Repetition::None>(instruction, eCX(), registers.dx(), eSI(), memory, io, status, flow_controller);
|
|
|
|
|
break;
|
|
|
|
|
case Operation::OUTS_REP:
|
2023-10-27 20:27:24 +00:00
|
|
|
|
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-27 03:08:07 +00:00
|
|
|
|
|
2023-10-22 02:52:50 +00:00
|
|
|
|
case Operation::INS:
|
2023-10-27 03:08:07 +00:00
|
|
|
|
Primitive::ins<IntT, AddressT, Repetition::None>(eCX(), registers.dx(), eDI(), memory, io, status, flow_controller);
|
|
|
|
|
break;
|
|
|
|
|
case Operation::INS_REP:
|
2023-10-27 20:27:24 +00:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
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-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;
|
|
|
|
|
}
|
2023-10-05 20:49:02 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
2023-10-05 20:49:02 +00:00
|
|
|
|
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
|
|
|
|
}
|
2023-10-05 20:49:02 +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 20:49:02 +00:00
|
|
|
|
}
|
2023-10-05 18:37:58 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* PerformImplementation_h */
|