mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 23:52:26 +00:00
Merge pull request #1199 from TomHarte/8088Groupings
Split up the ungainly PerformImplementation.hpp.
This commit is contained in:
commit
da7582d4b5
358
InstructionSets/x86/Implementation/Arithmetic.hpp
Normal file
358
InstructionSets/x86/Implementation/Arithmetic.hpp
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
//
|
||||||
|
// Arithmetic.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 08/11/2023.
|
||||||
|
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef Arithmetic_hpp
|
||||||
|
#define Arithmetic_hpp
|
||||||
|
|
||||||
|
#include "../AccessType.hpp"
|
||||||
|
#include "../Interrupts.hpp"
|
||||||
|
#include "../Perform.hpp"
|
||||||
|
|
||||||
|
#include "../../../Numeric/Carry.hpp"
|
||||||
|
|
||||||
|
namespace InstructionSet::x86::Primitive {
|
||||||
|
|
||||||
|
template <bool with_carry, typename IntT, typename ContextT>
|
||||||
|
void add(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
DEST ← DEST + SRC [+ CF];
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
The OF, SF, ZF, AF, CF, and PF flags are set according to the result.
|
||||||
|
*/
|
||||||
|
const IntT result = destination + source + (with_carry ? context.flags.template carry_bit<IntT>() : 0);
|
||||||
|
|
||||||
|
context.flags.template set_from<Flag::Carry>(
|
||||||
|
Numeric::carried_out<true, Numeric::bit_size<IntT>() - 1>(destination, source, result));
|
||||||
|
context.flags.template set_from<Flag::AuxiliaryCarry>(
|
||||||
|
Numeric::carried_in<4>(destination, source, result));
|
||||||
|
context.flags.template set_from<Flag::Overflow>(
|
||||||
|
Numeric::overflow<true, IntT>(destination, source, result));
|
||||||
|
|
||||||
|
context.flags.template set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(result);
|
||||||
|
|
||||||
|
destination = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool with_borrow, AccessType destination_type, typename IntT, typename ContextT>
|
||||||
|
void sub(
|
||||||
|
access_t<IntT, destination_type> destination,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
DEST ← DEST - (SRC [+ CF]);
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
The OF, SF, ZF, AF, CF, and PF flags are set according to the result.
|
||||||
|
*/
|
||||||
|
const IntT result = destination - source - (with_borrow ? context.flags.template carry_bit<IntT>() : 0);
|
||||||
|
|
||||||
|
context.flags.template set_from<Flag::Carry>(
|
||||||
|
Numeric::carried_out<false, Numeric::bit_size<IntT>() - 1>(destination, source, result));
|
||||||
|
context.flags.template set_from<Flag::AuxiliaryCarry>(
|
||||||
|
Numeric::carried_in<4>(destination, source, result));
|
||||||
|
context.flags.template set_from<Flag::Overflow>(
|
||||||
|
Numeric::overflow<false, IntT>(destination, source, result));
|
||||||
|
|
||||||
|
context.flags.template set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(result);
|
||||||
|
|
||||||
|
if constexpr (is_writeable(destination_type)) {
|
||||||
|
destination = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void test(
|
||||||
|
read_t<IntT> destination,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
|
||||||
|
context.flags.template set_from<Flag::Carry, Flag::Overflow>(0);
|
||||||
|
context.flags.template set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void mul(
|
||||||
|
modify_t<IntT> destination_high,
|
||||||
|
modify_t<IntT> destination_low,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
context.flags.template set_from<Flag::Overflow, Flag::Carry>(destination_high);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void imul(
|
||||||
|
modify_t<IntT> destination_high,
|
||||||
|
modify_t<IntT> destination_low,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
(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));
|
||||||
|
|
||||||
|
const auto sign_extension = (destination_low & Numeric::top_bit<IntT>()) ? IntT(~0) : 0;
|
||||||
|
context.flags.template set_from<Flag::Overflow, Flag::Carry>(destination_high != sign_extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void div(
|
||||||
|
modify_t<IntT> destination_high,
|
||||||
|
modify_t<IntT> destination_low,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
if(!source) {
|
||||||
|
interrupt(Interrupt::DivideError, context);
|
||||||
|
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) {
|
||||||
|
interrupt(Interrupt::DivideError, context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
destination_low = IntT(result);
|
||||||
|
destination_high = dividend % source;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void idiv(
|
||||||
|
modify_t<IntT> destination_high,
|
||||||
|
modify_t<IntT> destination_low,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
if(!source) {
|
||||||
|
interrupt(Interrupt::DivideError, context);
|
||||||
|
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) {
|
||||||
|
interrupt(Interrupt::DivideError, context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
destination_low = IntT(result);
|
||||||
|
destination_high = dividend % sIntT(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void inc(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
DEST ← DEST + 1;
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
The CF flag is not affected.
|
||||||
|
The OF, SF, ZF, AF, and PF flags are set according to the result.
|
||||||
|
*/
|
||||||
|
++destination;
|
||||||
|
|
||||||
|
context.flags.template set_from<Flag::Overflow>(destination == Numeric::top_bit<IntT>());
|
||||||
|
context.flags.template set_from<Flag::AuxiliaryCarry>(((destination - 1) ^ destination) & 0x10);
|
||||||
|
context.flags.template set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void dec(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
DEST ← DEST - 1;
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
The CF flag is not affected.
|
||||||
|
The OF, SF, ZF, AF, and PF flags are set according to the result.
|
||||||
|
*/
|
||||||
|
context.flags.template set_from<Flag::Overflow>(destination == Numeric::top_bit<IntT>());
|
||||||
|
|
||||||
|
--destination;
|
||||||
|
|
||||||
|
context.flags.template set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(destination);
|
||||||
|
context.flags.template set_from<Flag::AuxiliaryCarry>(((destination + 1) ^ destination) & 0x10);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void neg(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
context.flags.template set_from<Flag::AuxiliaryCarry>(Numeric::carried_in<4>(IntT(0), destination, IntT(-destination)));
|
||||||
|
|
||||||
|
destination = -destination;
|
||||||
|
|
||||||
|
context.flags.template set_from<Flag::Carry>(destination);
|
||||||
|
context.flags.template set_from<Flag::Overflow>(destination == Numeric::top_bit<IntT>());
|
||||||
|
context.flags.template set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* Arithmetic_hpp */
|
243
InstructionSets/x86/Implementation/BCD.hpp
Normal file
243
InstructionSets/x86/Implementation/BCD.hpp
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
//
|
||||||
|
// BCD.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 08/11/2023.
|
||||||
|
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BCD_h
|
||||||
|
#define BCD_h
|
||||||
|
|
||||||
|
#include "../AccessType.hpp"
|
||||||
|
|
||||||
|
#include "../../../Numeric/RegisterSizes.hpp"
|
||||||
|
|
||||||
|
namespace InstructionSet::x86::Primitive {
|
||||||
|
|
||||||
|
template <typename ContextT>
|
||||||
|
void aaa(
|
||||||
|
CPU::RegisterPair16 &ax,
|
||||||
|
ContextT &context
|
||||||
|
) { // P. 313
|
||||||
|
/*
|
||||||
|
IF ((AL AND 0FH) > 9) OR (AF = 1)
|
||||||
|
THEN
|
||||||
|
AL ← (AL + 6);
|
||||||
|
AH ← AH + 1;
|
||||||
|
AF ← 1;
|
||||||
|
CF ← 1;
|
||||||
|
ELSE
|
||||||
|
AF ← 0;
|
||||||
|
CF ← 0;
|
||||||
|
FI;
|
||||||
|
AL ← AL AND 0FH;
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
The AF and CF flags are set to 1 if the adjustment results in a decimal carry;
|
||||||
|
otherwise they are cleared to 0. The OF, SF, ZF, and PF flags are undefined.
|
||||||
|
*/
|
||||||
|
if((ax.halves.low & 0x0f) > 9 || context.flags.template flag<Flag::AuxiliaryCarry>()) {
|
||||||
|
ax.halves.low += 6;
|
||||||
|
++ax.halves.high;
|
||||||
|
context.flags.template set_from<Flag::Carry, Flag::AuxiliaryCarry>(1);
|
||||||
|
} else {
|
||||||
|
context.flags.template set_from<Flag::Carry, Flag::AuxiliaryCarry>(0);
|
||||||
|
}
|
||||||
|
ax.halves.low &= 0x0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ContextT>
|
||||||
|
void aad(
|
||||||
|
CPU::RegisterPair16 &ax,
|
||||||
|
uint8_t imm,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
context.flags.template set_from<uint8_t, Flag::Zero, Flag::Sign, Flag::ParityOdd>(ax.halves.low);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ContextT>
|
||||||
|
void aam(
|
||||||
|
CPU::RegisterPair16 &ax,
|
||||||
|
uint8_t imm,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
tempAL ← AL;
|
||||||
|
AH ← tempAL / imm8; (* imm8 is set to 0AH for the AAD mnemonic *)
|
||||||
|
AL ← tempAL MOD imm8;
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
The SF, ZF, and PF flags are set according to the result.
|
||||||
|
The OF, AF, and CF flags are undefined.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
If ... an immediate value of 0 is used, it will cause a #DE (divide error) exception.
|
||||||
|
*/
|
||||||
|
if(!imm) {
|
||||||
|
interrupt(Interrupt::DivideError, context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ax.halves.high = ax.halves.low / imm;
|
||||||
|
ax.halves.low = ax.halves.low % imm;
|
||||||
|
context.flags.template set_from<uint8_t, Flag::Zero, Flag::Sign, Flag::ParityOdd>(ax.halves.low);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ContextT>
|
||||||
|
void aas(
|
||||||
|
CPU::RegisterPair16 &ax,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
IF ((AL AND 0FH) > 9) OR (AF = 1)
|
||||||
|
THEN
|
||||||
|
AL ← AL – 6;
|
||||||
|
AH ← AH – 1;
|
||||||
|
AF ← 1;
|
||||||
|
CF ← 1;
|
||||||
|
ELSE
|
||||||
|
CF ← 0;
|
||||||
|
AF ← 0;
|
||||||
|
FI;
|
||||||
|
AL ← AL AND 0FH;
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
The AF and CF flags are set to 1 if there is a decimal borrow;
|
||||||
|
otherwise, they are cleared to 0. The OF, SF, ZF, and PF flags are undefined.
|
||||||
|
*/
|
||||||
|
if((ax.halves.low & 0x0f) > 9 || context.flags.template flag<Flag::AuxiliaryCarry>()) {
|
||||||
|
ax.halves.low -= 6;
|
||||||
|
--ax.halves.high;
|
||||||
|
context.flags.template set_from<Flag::Carry, Flag::AuxiliaryCarry>(1);
|
||||||
|
} else {
|
||||||
|
context.flags.template set_from<Flag::Carry, Flag::AuxiliaryCarry>(0);
|
||||||
|
}
|
||||||
|
ax.halves.low &= 0x0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ContextT>
|
||||||
|
void daa(
|
||||||
|
uint8_t &al,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
(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 carry from AL ← AL + 6 *)
|
||||||
|
AF ← 1;
|
||||||
|
ELSE
|
||||||
|
AF ← 0;
|
||||||
|
FI;
|
||||||
|
IF ((old_AL > 99H) or old_CF = 1)
|
||||||
|
THEN
|
||||||
|
AL ← AL + 60H;
|
||||||
|
CF ← 1;
|
||||||
|
ELSE
|
||||||
|
CF ← 0;
|
||||||
|
FI;
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
The CF and AF flags are set if the adjustment of the value results in a
|
||||||
|
decimal carry in either digit of the result (see the “Operation” section above).
|
||||||
|
The SF, ZF, and PF flags are set according to the result. The OF flag is undefined.
|
||||||
|
*/
|
||||||
|
const uint8_t old_al = al;
|
||||||
|
const auto old_carry = context.flags.template flag<Flag::Carry>();
|
||||||
|
context.flags.template set_from<Flag::Carry>(0);
|
||||||
|
|
||||||
|
if((al & 0x0f) > 0x09 || context.flags.template flag<Flag::AuxiliaryCarry>()) {
|
||||||
|
context.flags.template set_from<Flag::Carry>(old_carry | (al > 0xf9));
|
||||||
|
al += 0x06;
|
||||||
|
context.flags.template set_from<Flag::AuxiliaryCarry>(1);
|
||||||
|
} else {
|
||||||
|
context.flags.template set_from<Flag::AuxiliaryCarry>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(old_al > 0x99 || old_carry) {
|
||||||
|
al += 0x60;
|
||||||
|
context.flags.template set_from<Flag::Carry>(1);
|
||||||
|
} else {
|
||||||
|
context.flags.template set_from<Flag::Carry>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.flags.template set_from<uint8_t, Flag::Zero, Flag::Sign, Flag::ParityOdd>(al);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ContextT>
|
||||||
|
void das(
|
||||||
|
uint8_t &al,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
(as modified by https://www.felixcloutier.com/x86/daa ...)
|
||||||
|
|
||||||
|
old_AL ← AL;
|
||||||
|
old_CF ← CF;
|
||||||
|
CF ← 0;
|
||||||
|
|
||||||
|
IF (((AL AND 0FH) > 9) or AF = 1)
|
||||||
|
THEN
|
||||||
|
AL ← AL - 6;
|
||||||
|
CF ← old_CF OR CarryFromLastAddition; (* CF OR borrow from AL ← AL - 6 *)
|
||||||
|
AF ← 1;
|
||||||
|
ELSE
|
||||||
|
AF ← 0;
|
||||||
|
FI;
|
||||||
|
IF ((old_AL > 99H) or old_CF = 1)
|
||||||
|
THEN
|
||||||
|
AL ← AL - 60H;
|
||||||
|
CF ← 1;
|
||||||
|
ELSE
|
||||||
|
CF ← 0;
|
||||||
|
FI;
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
The CF and AF flags are set if the adjustment of the value results in a
|
||||||
|
decimal carry in either digit of the result (see the “Operation” section above).
|
||||||
|
The SF, ZF, and PF flags are set according to the result. The OF flag is undefined.
|
||||||
|
*/
|
||||||
|
const uint8_t old_al = al;
|
||||||
|
const auto old_carry = context.flags.template flag<Flag::Carry>();
|
||||||
|
context.flags.template set_from<Flag::Carry>(0);
|
||||||
|
|
||||||
|
if((al & 0x0f) > 0x09 || context.flags.template flag<Flag::AuxiliaryCarry>()) {
|
||||||
|
context.flags.template set_from<Flag::Carry>(old_carry | (al < 0x06));
|
||||||
|
al -= 0x06;
|
||||||
|
context.flags.template set_from<Flag::AuxiliaryCarry>(1);
|
||||||
|
} else {
|
||||||
|
context.flags.template set_from<Flag::AuxiliaryCarry>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(old_al > 0x99 || old_carry) {
|
||||||
|
al -= 0x60;
|
||||||
|
context.flags.template set_from<Flag::Carry>(1);
|
||||||
|
} else {
|
||||||
|
context.flags.template set_from<Flag::Carry>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.flags.template set_from<uint8_t, Flag::Zero, Flag::Sign, Flag::ParityOdd>(al);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* BCD_h */
|
222
InstructionSets/x86/Implementation/FlowControl.hpp
Normal file
222
InstructionSets/x86/Implementation/FlowControl.hpp
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
//
|
||||||
|
// FlowControl.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 08/11/2023.
|
||||||
|
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef FlowControl_hpp
|
||||||
|
#define FlowControl_hpp
|
||||||
|
|
||||||
|
#include "Resolver.hpp"
|
||||||
|
#include "Stack.hpp"
|
||||||
|
#include "../AccessType.hpp"
|
||||||
|
|
||||||
|
namespace InstructionSet::x86::Primitive {
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void jump(
|
||||||
|
bool condition,
|
||||||
|
IntT displacement,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
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) {
|
||||||
|
context.flow_controller.jump(context.registers.ip() + displacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename OffsetT, typename ContextT>
|
||||||
|
void loop(
|
||||||
|
modify_t<IntT> counter,
|
||||||
|
OffsetT displacement,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
--counter;
|
||||||
|
if(counter) {
|
||||||
|
context.flow_controller.jump(context.registers.ip() + displacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename OffsetT, typename ContextT>
|
||||||
|
void loope(
|
||||||
|
modify_t<IntT> counter,
|
||||||
|
OffsetT displacement,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
--counter;
|
||||||
|
if(counter && context.flags.template flag<Flag::Zero>()) {
|
||||||
|
context.flow_controller.jump(context.registers.ip() + displacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename OffsetT, typename ContextT>
|
||||||
|
void loopne(
|
||||||
|
modify_t<IntT> counter,
|
||||||
|
OffsetT displacement,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
--counter;
|
||||||
|
if(counter && !context.flags.template flag<Flag::Zero>()) {
|
||||||
|
context.flow_controller.jump(context.registers.ip() + displacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void call_relative(
|
||||||
|
IntT offset,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
push<uint16_t, false>(context.registers.ip(), context);
|
||||||
|
context.flow_controller.jump(context.registers.ip() + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void call_absolute(
|
||||||
|
read_t<IntT> target,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
push<uint16_t, false>(context.registers.ip(), context);
|
||||||
|
context.flow_controller.jump(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void jump_absolute(
|
||||||
|
read_t<IntT> target,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
context.flow_controller.jump(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InstructionT, typename ContextT>
|
||||||
|
void call_far(
|
||||||
|
InstructionT &instruction,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
// TODO: eliminate 16-bit assumption below.
|
||||||
|
const Source source_segment = instruction.data_segment();
|
||||||
|
context.memory.preauthorise_stack_write(sizeof(uint16_t) * 2);
|
||||||
|
|
||||||
|
uint16_t source_address;
|
||||||
|
const auto pointer = instruction.destination();
|
||||||
|
switch(pointer.source()) {
|
||||||
|
default:
|
||||||
|
case Source::Immediate:
|
||||||
|
push<uint16_t, true>(context.registers.cs(), context);
|
||||||
|
push<uint16_t, true>(context.registers.ip(), context);
|
||||||
|
context.flow_controller.jump(instruction.segment(), instruction.offset());
|
||||||
|
return;
|
||||||
|
|
||||||
|
case Source::Indirect:
|
||||||
|
source_address = address<Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, context);
|
||||||
|
break;
|
||||||
|
case Source::IndirectNoBase:
|
||||||
|
source_address = address<Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, context);
|
||||||
|
break;
|
||||||
|
case Source::DirectAddress:
|
||||||
|
source_address = address<Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.memory.preauthorise_read(source_segment, source_address, sizeof(uint16_t) * 2);
|
||||||
|
const uint16_t offset = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||||
|
source_address += 2;
|
||||||
|
const uint16_t segment = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||||
|
|
||||||
|
// At least on an 8086, the stack writes occur after the target address read.
|
||||||
|
push<uint16_t, true>(context.registers.cs(), context);
|
||||||
|
push<uint16_t, true>(context.registers.ip(), context);
|
||||||
|
|
||||||
|
context.flow_controller.jump(segment, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InstructionT, typename ContextT>
|
||||||
|
void jump_far(
|
||||||
|
InstructionT &instruction,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
// TODO: eliminate 16-bit assumption below.
|
||||||
|
uint16_t source_address = 0;
|
||||||
|
const auto pointer = instruction.destination();
|
||||||
|
switch(pointer.source()) {
|
||||||
|
default:
|
||||||
|
case Source::Immediate: context.flow_controller.jump(instruction.segment(), instruction.offset()); return;
|
||||||
|
|
||||||
|
case Source::Indirect:
|
||||||
|
source_address = address<Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, context);
|
||||||
|
break;
|
||||||
|
case Source::IndirectNoBase:
|
||||||
|
source_address = address<Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, context);
|
||||||
|
break;
|
||||||
|
case Source::DirectAddress:
|
||||||
|
source_address = address<Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Source source_segment = instruction.data_segment();
|
||||||
|
context.memory.preauthorise_read(source_segment, source_address, sizeof(uint16_t) * 2);
|
||||||
|
|
||||||
|
const uint16_t offset = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||||
|
source_address += 2;
|
||||||
|
const uint16_t segment = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||||
|
context.flow_controller.jump(segment, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ContextT>
|
||||||
|
void iret(
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
// TODO: all modes other than 16-bit real mode.
|
||||||
|
context.memory.preauthorise_stack_read(sizeof(uint16_t) * 3);
|
||||||
|
const auto ip = pop<uint16_t, true>(context);
|
||||||
|
const auto cs = pop<uint16_t, true>(context);
|
||||||
|
context.flags.set(pop<uint16_t, true>(context));
|
||||||
|
context.flow_controller.jump(cs, ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InstructionT, typename ContextT>
|
||||||
|
void ret_near(
|
||||||
|
InstructionT instruction,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
const auto ip = pop<uint16_t, false>(context);
|
||||||
|
context.registers.sp() += instruction.operand();
|
||||||
|
context.flow_controller.jump(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename InstructionT, typename ContextT>
|
||||||
|
void ret_far(
|
||||||
|
InstructionT instruction,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
context.memory.preauthorise_stack_read(sizeof(uint16_t) * 2);
|
||||||
|
const auto ip = pop<uint16_t, true>(context);
|
||||||
|
const auto cs = pop<uint16_t, true>(context);
|
||||||
|
context.registers.sp() += instruction.operand();
|
||||||
|
context.flow_controller.jump(cs, ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ContextT>
|
||||||
|
void into(
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
if(context.flags.template flag<Flag::Overflow>()) {
|
||||||
|
interrupt(Interrupt::OnOverflow, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* FlowControl_hpp */
|
36
InstructionSets/x86/Implementation/InOut.hpp
Normal file
36
InstructionSets/x86/Implementation/InOut.hpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// InOut.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 08/11/2023.
|
||||||
|
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef InOut_h
|
||||||
|
#define InOut_h
|
||||||
|
|
||||||
|
#include "../AccessType.hpp"
|
||||||
|
|
||||||
|
namespace InstructionSet::x86::Primitive {
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void out(
|
||||||
|
uint16_t port,
|
||||||
|
read_t<IntT> value,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
context.io.template out<IntT>(port, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void in(
|
||||||
|
uint16_t port,
|
||||||
|
write_t<IntT> value,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
value = context.io.template in<IntT>(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* InOut_h */
|
83
InstructionSets/x86/Implementation/LoadStore.hpp
Normal file
83
InstructionSets/x86/Implementation/LoadStore.hpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
//
|
||||||
|
// LoadStore.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 08/11/2023.
|
||||||
|
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LoadStore_h
|
||||||
|
#define LoadStore_h
|
||||||
|
|
||||||
|
#include "../AccessType.hpp"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace InstructionSet::x86::Primitive {
|
||||||
|
|
||||||
|
template <typename IntT>
|
||||||
|
void xchg(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
modify_t<IntT> source
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
TEMP ← DEST
|
||||||
|
DEST ← SRC
|
||||||
|
SRC ← TEMP
|
||||||
|
*/
|
||||||
|
std::swap(destination, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Source selector, typename InstructionT, typename ContextT>
|
||||||
|
void ld(
|
||||||
|
const InstructionT &instruction,
|
||||||
|
write_t<uint16_t> destination,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
const auto pointer = instruction.source();
|
||||||
|
auto source_address = address<uint16_t, AccessType::Read>(instruction, pointer, context);
|
||||||
|
const Source source_segment = instruction.data_segment();
|
||||||
|
|
||||||
|
context.memory.preauthorise_read(source_segment, source_address, 4);
|
||||||
|
destination = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||||
|
source_address += 2;
|
||||||
|
switch(selector) {
|
||||||
|
case Source::DS: context.registers.ds() = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address); break;
|
||||||
|
case Source::ES: context.registers.es() = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename InstructionT, typename ContextT>
|
||||||
|
void lea(
|
||||||
|
const InstructionT &instruction,
|
||||||
|
write_t<IntT> destination,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
// TODO: address size.
|
||||||
|
destination = IntT(address<uint16_t, AccessType::Read>(instruction, instruction.source(), context));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressT, typename InstructionT, typename ContextT>
|
||||||
|
void xlat(
|
||||||
|
const InstructionT &instruction,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
AddressT address;
|
||||||
|
if constexpr (std::is_same_v<AddressT, uint16_t>) {
|
||||||
|
address = context.registers.bx() + context.registers.al();
|
||||||
|
}
|
||||||
|
|
||||||
|
context.registers.al() = context.memory.template access<uint8_t, AccessType::Read>(instruction.data_segment(), address);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT>
|
||||||
|
void mov(
|
||||||
|
write_t<IntT> destination,
|
||||||
|
read_t<IntT> source
|
||||||
|
) {
|
||||||
|
destination = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* LoadStore_h */
|
146
InstructionSets/x86/Implementation/Logical.hpp
Normal file
146
InstructionSets/x86/Implementation/Logical.hpp
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
//
|
||||||
|
// Logical.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 08/11/2023.
|
||||||
|
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef Logical_h
|
||||||
|
#define Logical_h
|
||||||
|
|
||||||
|
#include "../AccessType.hpp"
|
||||||
|
|
||||||
|
namespace InstructionSet::x86::Primitive {
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void and_(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
|
||||||
|
context.flags.template set_from<Flag::Overflow, Flag::Carry>(0);
|
||||||
|
context.flags.template set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void or_(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
|
||||||
|
context.flags.template set_from<Flag::Overflow, Flag::Carry>(0);
|
||||||
|
context.flags.template set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void xor_(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
read_t<IntT> source,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
|
||||||
|
context.flags.template set_from<Flag::Overflow, Flag::Carry>(0);
|
||||||
|
context.flags.template set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT>
|
||||||
|
void not_(
|
||||||
|
modify_t<IntT> destination
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
DEST ← NOT DEST;
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
Flags affected: none.
|
||||||
|
*/
|
||||||
|
destination = ~destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT>
|
||||||
|
void cwd(
|
||||||
|
IntT &dx,
|
||||||
|
IntT ax
|
||||||
|
) {
|
||||||
|
dx = ax & Numeric::top_bit<IntT>() ? IntT(~0) : IntT(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: changes to the interrupt flag do quite a lot more in protected mode.
|
||||||
|
template <typename ContextT>
|
||||||
|
void clc(ContextT &context) { context.flags.template set_from<Flag::Carry>(0); }
|
||||||
|
template <typename ContextT>
|
||||||
|
void cld(ContextT &context) { context.flags.template set_from<Flag::Direction>(0); }
|
||||||
|
template <typename ContextT>
|
||||||
|
void cli(ContextT &context) { context.flags.template set_from<Flag::Interrupt>(0); }
|
||||||
|
template <typename ContextT>
|
||||||
|
void stc(ContextT &context) { context.flags.template set_from<Flag::Carry>(1); }
|
||||||
|
template <typename ContextT>
|
||||||
|
void std(ContextT &context) { context.flags.template set_from<Flag::Direction>(1); }
|
||||||
|
template <typename ContextT>
|
||||||
|
void sti(ContextT &context) { context.flags.template set_from<Flag::Interrupt>(1); }
|
||||||
|
template <typename ContextT>
|
||||||
|
void cmc(ContextT &context) {
|
||||||
|
context.flags.template set_from<Flag::Carry>(!context.flags.template flag<Flag::Carry>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ContextT>
|
||||||
|
void salc(
|
||||||
|
uint8_t &al,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
al = context.flags.template flag<Flag::Carry>() ? 0xff : 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void setmo(
|
||||||
|
write_t<IntT> destination,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
const auto result = destination = ~0;
|
||||||
|
context.flags.template set_from<Flag::Carry, Flag::AuxiliaryCarry, Flag::Overflow>(0);
|
||||||
|
context.flags.template set_from<IntT, Flag::Sign, Flag::Zero, Flag::ParityOdd>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* Logical_h */
|
File diff suppressed because it is too large
Load Diff
180
InstructionSets/x86/Implementation/Repetition.hpp
Normal file
180
InstructionSets/x86/Implementation/Repetition.hpp
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
//
|
||||||
|
// Repetition.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 08/11/2023.
|
||||||
|
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef Repetition_h
|
||||||
|
#define Repetition_h
|
||||||
|
|
||||||
|
#include "../AccessType.hpp"
|
||||||
|
|
||||||
|
namespace InstructionSet::x86::Primitive {
|
||||||
|
|
||||||
|
template <typename AddressT, Repetition repetition>
|
||||||
|
bool repetition_over(
|
||||||
|
const AddressT &eCX
|
||||||
|
) {
|
||||||
|
return repetition != Repetition::None && !eCX;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename AddressT, Repetition repetition, typename ContextT>
|
||||||
|
void repeat(
|
||||||
|
AddressT &eCX,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
if(
|
||||||
|
repetition == Repetition::None || // No repetition => stop.
|
||||||
|
!(--eCX) // [e]cx is zero after being decremented => stop.
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if constexpr (repetition != Repetition::Rep) {
|
||||||
|
// If this is RepE or RepNE, also test the zero flag.
|
||||||
|
if((repetition == Repetition::RepNE) == context.flags.template flag<Flag::Zero>()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.flow_controller.repeat_last();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename AddressT, Repetition repetition, typename InstructionT, typename ContextT>
|
||||||
|
void cmps(
|
||||||
|
const InstructionT &instruction,
|
||||||
|
AddressT &eCX,
|
||||||
|
AddressT &eSI,
|
||||||
|
AddressT &eDI,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
if(repetition_over<AddressT, repetition>(eCX)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntT lhs = context.memory.template access<IntT, AccessType::Read>(instruction.data_segment(), eSI);
|
||||||
|
const IntT rhs = context.memory.template access<IntT, AccessType::Read>(Source::ES, eDI);
|
||||||
|
eSI += context.flags.template direction<AddressT>() * sizeof(IntT);
|
||||||
|
eDI += context.flags.template direction<AddressT>() * sizeof(IntT);
|
||||||
|
|
||||||
|
Primitive::sub<false, AccessType::Read, IntT>(lhs, rhs, context);
|
||||||
|
|
||||||
|
repeat<AddressT, repetition>(eCX, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename AddressT, Repetition repetition, typename ContextT>
|
||||||
|
void scas(
|
||||||
|
AddressT &eCX,
|
||||||
|
AddressT &eDI,
|
||||||
|
IntT &eAX,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
if(repetition_over<AddressT, repetition>(eCX)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IntT rhs = context.memory.template access<IntT, AccessType::Read>(Source::ES, eDI);
|
||||||
|
eDI += context.flags.template direction<AddressT>() * sizeof(IntT);
|
||||||
|
|
||||||
|
Primitive::sub<false, AccessType::Read, IntT>(eAX, rhs, context);
|
||||||
|
|
||||||
|
repeat<AddressT, repetition>(eCX, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename AddressT, Repetition repetition, typename InstructionT, typename ContextT>
|
||||||
|
void lods(
|
||||||
|
const InstructionT &instruction,
|
||||||
|
AddressT &eCX,
|
||||||
|
AddressT &eSI,
|
||||||
|
IntT &eAX,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
if(repetition_over<AddressT, repetition>(eCX)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
eAX = context.memory.template access<IntT, AccessType::Read>(instruction.data_segment(), eSI);
|
||||||
|
eSI += context.flags.template direction<AddressT>() * sizeof(IntT);
|
||||||
|
|
||||||
|
repeat<AddressT, repetition>(eCX, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename AddressT, Repetition repetition, typename InstructionT, typename ContextT>
|
||||||
|
void movs(
|
||||||
|
const InstructionT &instruction,
|
||||||
|
AddressT &eCX,
|
||||||
|
AddressT &eSI,
|
||||||
|
AddressT &eDI,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
if(repetition_over<AddressT, repetition>(eCX)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.memory.template access<IntT, AccessType::Write>(Source::ES, eDI) =
|
||||||
|
context.memory.template access<IntT, AccessType::Read>(instruction.data_segment(), eSI);
|
||||||
|
|
||||||
|
eSI += context.flags.template direction<AddressT>() * sizeof(IntT);
|
||||||
|
eDI += context.flags.template direction<AddressT>() * sizeof(IntT);
|
||||||
|
|
||||||
|
repeat<AddressT, repetition>(eCX, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename AddressT, Repetition repetition, typename ContextT>
|
||||||
|
void stos(
|
||||||
|
AddressT &eCX,
|
||||||
|
AddressT &eDI,
|
||||||
|
IntT &eAX,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
if(repetition_over<AddressT, repetition>(eCX)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.memory.template access<IntT, AccessType::Write>(Source::ES, eDI) = eAX;
|
||||||
|
eDI += context.flags.template direction<AddressT>() * sizeof(IntT);
|
||||||
|
|
||||||
|
repeat<AddressT, repetition>(eCX, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename AddressT, Repetition repetition, typename InstructionT, typename ContextT>
|
||||||
|
void outs(
|
||||||
|
const InstructionT &instruction,
|
||||||
|
AddressT &eCX,
|
||||||
|
uint16_t port,
|
||||||
|
AddressT &eSI,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
if(repetition_over<AddressT, repetition>(eCX)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.io.template out<IntT>(
|
||||||
|
port,
|
||||||
|
context.memory.template access<IntT, AccessType::Read>(instruction.data_segment(), eSI)
|
||||||
|
);
|
||||||
|
eSI += context.flags.template direction<AddressT>() * sizeof(IntT);
|
||||||
|
|
||||||
|
repeat<AddressT, repetition>(eCX, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename AddressT, Repetition repetition, typename ContextT>
|
||||||
|
void ins(
|
||||||
|
AddressT &eCX,
|
||||||
|
uint16_t port,
|
||||||
|
AddressT &eDI,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
if(repetition_over<AddressT, repetition>(eCX)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.memory.template access<IntT, AccessType::Write>(Source::ES, eDI) = context.io.template in<IntT>(port);
|
||||||
|
eDI += context.flags.template direction<AddressT>() * sizeof(IntT);
|
||||||
|
|
||||||
|
repeat<AddressT, repetition>(eCX, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* Repetition_h */
|
361
InstructionSets/x86/Implementation/ShiftRoll.hpp
Normal file
361
InstructionSets/x86/Implementation/ShiftRoll.hpp
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
//
|
||||||
|
// ShiftRoll.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 08/11/2023.
|
||||||
|
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ShiftRoll_hpp
|
||||||
|
#define ShiftRoll_hpp
|
||||||
|
|
||||||
|
#include "../AccessType.hpp"
|
||||||
|
|
||||||
|
namespace InstructionSet::x86::Primitive {
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void rcl(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
uint8_t count,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
(* 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.
|
||||||
|
*/
|
||||||
|
const auto temp_count = count % (Numeric::bit_size<IntT>() + 1);
|
||||||
|
auto carry = context.flags.template carry_bit<IntT>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.flags.template set_from<Flag::Carry>(carry);
|
||||||
|
context.flags.template set_from<Flag::Overflow>(
|
||||||
|
((destination >> (Numeric::bit_size<IntT>() - 1)) & 1) ^ carry
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void rcr(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
uint8_t count,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
(* 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 = context.flags.template carry_bit<IntT>();
|
||||||
|
context.flags.template 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.flags.template set_from<Flag::Carry>(carry);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void rol(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
uint8_t count,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
(* 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
context.flags.template set_from<Flag::Carry>(destination & 1);
|
||||||
|
context.flags.template set_from<Flag::Overflow>(
|
||||||
|
((destination >> (Numeric::bit_size<IntT>() - 1)) ^ destination) & 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void ror(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
uint8_t count,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
(* 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
context.flags.template set_from<Flag::Carry>(destination & Numeric::top_bit<IntT>());
|
||||||
|
context.flags.template set_from<Flag::Overflow>(
|
||||||
|
(destination ^ (destination << 1)) & Numeric::top_bit<IntT>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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, typename ContextT>
|
||||||
|
void sal(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
uint8_t count,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
switch(count) {
|
||||||
|
case 0: return;
|
||||||
|
case Numeric::bit_size<IntT>():
|
||||||
|
context.flags.template set_from<Flag::Carry, Flag::Overflow>(destination & 1);
|
||||||
|
destination = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if(count > Numeric::bit_size<IntT>()) {
|
||||||
|
context.flags.template set_from<Flag::Carry, Flag::Overflow>(0);
|
||||||
|
destination = 0;
|
||||||
|
} else {
|
||||||
|
const auto mask = (Numeric::top_bit<IntT>() >> (count - 1));
|
||||||
|
context.flags.template set_from<Flag::Carry>(
|
||||||
|
destination & mask
|
||||||
|
);
|
||||||
|
context.flags.template set_from<Flag::Overflow>(
|
||||||
|
(destination ^ (destination << 1)) & mask
|
||||||
|
);
|
||||||
|
destination <<= count;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
context.flags.template set_from<IntT, Flag::Sign, Flag::Zero, Flag::ParityOdd>(destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void sar(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
uint8_t count,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
if(!count) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IntT sign = Numeric::top_bit<IntT>() & destination;
|
||||||
|
if(count >= Numeric::bit_size<IntT>()) {
|
||||||
|
destination = sign ? IntT(~0) : IntT(0);
|
||||||
|
context.flags.template set_from<Flag::Carry>(sign);
|
||||||
|
} else {
|
||||||
|
const IntT mask = 1 << (count - 1);
|
||||||
|
context.flags.template set_from<Flag::Carry>(destination & mask);
|
||||||
|
destination = (destination >> count) | (sign ? ~(IntT(~0) >> count) : 0);
|
||||||
|
}
|
||||||
|
context.flags.template set_from<Flag::Overflow>(0);
|
||||||
|
context.flags.template set_from<IntT, Flag::Sign, Flag::Zero, Flag::ParityOdd>(destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, typename ContextT>
|
||||||
|
void shr(
|
||||||
|
modify_t<IntT> destination,
|
||||||
|
uint8_t count,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
if(!count) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.flags.template set_from<Flag::Overflow>(Numeric::top_bit<IntT>() & destination);
|
||||||
|
if(count == Numeric::bit_size<IntT>()) {
|
||||||
|
context.flags.template set_from<Flag::Carry>(Numeric::top_bit<IntT>() & destination);
|
||||||
|
destination = 0;
|
||||||
|
} else if(count > Numeric::bit_size<IntT>()) {
|
||||||
|
context.flags.template set_from<Flag::Carry>(0);
|
||||||
|
destination = 0;
|
||||||
|
} else {
|
||||||
|
const IntT mask = 1 << (count - 1);
|
||||||
|
context.flags.template set_from<Flag::Carry>(destination & mask);
|
||||||
|
destination >>= count;
|
||||||
|
}
|
||||||
|
context.flags.template set_from<IntT, Flag::Sign, Flag::Zero, Flag::ParityOdd>(destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ShiftRoll_hpp */
|
94
InstructionSets/x86/Implementation/Stack.hpp
Normal file
94
InstructionSets/x86/Implementation/Stack.hpp
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
//
|
||||||
|
// Stack.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 08/11/2023.
|
||||||
|
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef Stack_hpp
|
||||||
|
#define Stack_hpp
|
||||||
|
|
||||||
|
#include "../AccessType.hpp"
|
||||||
|
|
||||||
|
namespace InstructionSet::x86::Primitive {
|
||||||
|
|
||||||
|
// The below takes a reference in order properly to handle PUSH SP,
|
||||||
|
// which should place the value of SP after the push onto the stack.
|
||||||
|
template <typename IntT, bool preauthorised, typename ContextT>
|
||||||
|
void push(
|
||||||
|
IntT &value,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
context.registers.sp_ -= sizeof(IntT);
|
||||||
|
if constexpr (preauthorised) {
|
||||||
|
context.memory.template preauthorised_write<IntT>(Source::SS, context.registers.sp_, value);
|
||||||
|
} else {
|
||||||
|
context.memory.template access<IntT, AccessType::Write>(
|
||||||
|
Source::SS,
|
||||||
|
context.registers.sp_) = value;
|
||||||
|
}
|
||||||
|
context.memory.template write_back<IntT>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename IntT, bool preauthorised, typename ContextT>
|
||||||
|
IntT pop(
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
const auto value = context.memory.template access<IntT, preauthorised ? AccessType::PreauthorisedRead : AccessType::Read>(
|
||||||
|
Source::SS,
|
||||||
|
context.registers.sp_);
|
||||||
|
context.registers.sp_ += sizeof(IntT);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ContextT>
|
||||||
|
void sahf(
|
||||||
|
uint8_t &ah,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
EFLAGS(SF:ZF:0:AF:0:PF:1:CF) ← AH;
|
||||||
|
*/
|
||||||
|
context.flags.template set_from<uint8_t, Flag::Sign>(ah);
|
||||||
|
context.flags.template set_from<Flag::Zero>(!(ah & 0x40));
|
||||||
|
context.flags.template set_from<Flag::AuxiliaryCarry>(ah & 0x10);
|
||||||
|
context.flags.template set_from<Flag::ParityOdd>(!(ah & 0x04));
|
||||||
|
context.flags.template set_from<Flag::Carry>(ah & 0x01);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ContextT>
|
||||||
|
void lahf(
|
||||||
|
uint8_t &ah,
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
AH ← EFLAGS(SF:ZF:0:AF:0:PF:1:CF);
|
||||||
|
*/
|
||||||
|
ah =
|
||||||
|
(context.flags.template flag<Flag::Sign>() ? 0x80 : 0x00) |
|
||||||
|
(context.flags.template flag<Flag::Zero>() ? 0x40 : 0x00) |
|
||||||
|
(context.flags.template flag<Flag::AuxiliaryCarry>() ? 0x10 : 0x00) |
|
||||||
|
(context.flags.template flag<Flag::ParityOdd>() ? 0x00 : 0x04) |
|
||||||
|
0x02 |
|
||||||
|
(context.flags.template flag<Flag::Carry>() ? 0x01 : 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ContextT>
|
||||||
|
void popf(
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
context.flags.set(pop<uint16_t, false>(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ContextT>
|
||||||
|
void pushf(
|
||||||
|
ContextT &context
|
||||||
|
) {
|
||||||
|
uint16_t value = context.flags.get();
|
||||||
|
push<uint16_t, false>(value, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* Stack_hpp */
|
@ -1129,6 +1129,15 @@
|
|||||||
42437B352ACF0AA2006DFED1 /* Perform.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Perform.hpp; sourceTree = "<group>"; };
|
42437B352ACF0AA2006DFED1 /* Perform.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Perform.hpp; sourceTree = "<group>"; };
|
||||||
42437B382ACF2798006DFED1 /* PerformImplementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PerformImplementation.hpp; sourceTree = "<group>"; };
|
42437B382ACF2798006DFED1 /* PerformImplementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PerformImplementation.hpp; sourceTree = "<group>"; };
|
||||||
42437B392AD07465006DFED1 /* Interrupts.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Interrupts.hpp; sourceTree = "<group>"; };
|
42437B392AD07465006DFED1 /* Interrupts.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Interrupts.hpp; sourceTree = "<group>"; };
|
||||||
|
425739282AFBDF2700B7D1E4 /* Arithmetic.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Arithmetic.hpp; sourceTree = "<group>"; };
|
||||||
|
425739292AFBE03600B7D1E4 /* Logical.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Logical.hpp; sourceTree = "<group>"; };
|
||||||
|
4257392A2AFBE0AE00B7D1E4 /* ShiftRoll.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ShiftRoll.hpp; sourceTree = "<group>"; };
|
||||||
|
4257392B2AFBE16000B7D1E4 /* FlowControl.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FlowControl.hpp; sourceTree = "<group>"; };
|
||||||
|
4257392C2AFBE18900B7D1E4 /* Stack.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Stack.hpp; sourceTree = "<group>"; };
|
||||||
|
4257392D2AFBE27C00B7D1E4 /* BCD.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = BCD.hpp; sourceTree = "<group>"; };
|
||||||
|
4257392E2AFBE2BC00B7D1E4 /* Repetition.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Repetition.hpp; sourceTree = "<group>"; };
|
||||||
|
4257392F2AFBE36B00B7D1E4 /* LoadStore.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LoadStore.hpp; sourceTree = "<group>"; };
|
||||||
|
425739302AFBE47700B7D1E4 /* InOut.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = InOut.hpp; sourceTree = "<group>"; };
|
||||||
4281572E2AA0334300E16AA1 /* Carry.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Carry.hpp; sourceTree = "<group>"; };
|
4281572E2AA0334300E16AA1 /* Carry.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Carry.hpp; sourceTree = "<group>"; };
|
||||||
428168372A16C25C008ECD27 /* LineLayout.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LineLayout.hpp; sourceTree = "<group>"; };
|
428168372A16C25C008ECD27 /* LineLayout.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LineLayout.hpp; sourceTree = "<group>"; };
|
||||||
428168392A37AFB4008ECD27 /* DispatcherTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DispatcherTests.mm; sourceTree = "<group>"; };
|
428168392A37AFB4008ECD27 /* DispatcherTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DispatcherTests.mm; sourceTree = "<group>"; };
|
||||||
@ -2329,8 +2338,17 @@
|
|||||||
42437B372ACF2798006DFED1 /* Implementation */ = {
|
42437B372ACF2798006DFED1 /* Implementation */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
425739282AFBDF2700B7D1E4 /* Arithmetic.hpp */,
|
||||||
|
4257392D2AFBE27C00B7D1E4 /* BCD.hpp */,
|
||||||
|
4257392B2AFBE16000B7D1E4 /* FlowControl.hpp */,
|
||||||
|
425739302AFBE47700B7D1E4 /* InOut.hpp */,
|
||||||
|
4257392F2AFBE36B00B7D1E4 /* LoadStore.hpp */,
|
||||||
|
425739292AFBE03600B7D1E4 /* Logical.hpp */,
|
||||||
42437B382ACF2798006DFED1 /* PerformImplementation.hpp */,
|
42437B382ACF2798006DFED1 /* PerformImplementation.hpp */,
|
||||||
|
4257392E2AFBE2BC00B7D1E4 /* Repetition.hpp */,
|
||||||
42AA41242AF8893F0016751C /* Resolver.hpp */,
|
42AA41242AF8893F0016751C /* Resolver.hpp */,
|
||||||
|
4257392A2AFBE0AE00B7D1E4 /* ShiftRoll.hpp */,
|
||||||
|
4257392C2AFBE18900B7D1E4 /* Stack.hpp */,
|
||||||
);
|
);
|
||||||
path = Implementation;
|
path = Implementation;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
Loading…
Reference in New Issue
Block a user