1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-08 15:29:09 +00:00

Merge pull request #1199 from TomHarte/8088Groupings

Split up the ungainly PerformImplementation.hpp.
This commit is contained in:
Thomas Harte 2023-11-08 19:02:59 -05:00 committed by GitHub
commit da7582d4b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1804 additions and 1525 deletions

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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

View 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 */

View 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 */

View 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 */

View File

@ -1129,6 +1129,15 @@
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>"; };
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>"; };
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>"; };
@ -2329,8 +2338,17 @@
42437B372ACF2798006DFED1 /* Implementation */ = {
isa = PBXGroup;
children = (
425739282AFBDF2700B7D1E4 /* Arithmetic.hpp */,
4257392D2AFBE27C00B7D1E4 /* BCD.hpp */,
4257392B2AFBE16000B7D1E4 /* FlowControl.hpp */,
425739302AFBE47700B7D1E4 /* InOut.hpp */,
4257392F2AFBE36B00B7D1E4 /* LoadStore.hpp */,
425739292AFBE03600B7D1E4 /* Logical.hpp */,
42437B382ACF2798006DFED1 /* PerformImplementation.hpp */,
4257392E2AFBE2BC00B7D1E4 /* Repetition.hpp */,
42AA41242AF8893F0016751C /* Resolver.hpp */,
4257392A2AFBE0AE00B7D1E4 /* ShiftRoll.hpp */,
4257392C2AFBE18900B7D1E4 /* Stack.hpp */,
);
path = Implementation;
sourceTree = "<group>";