mirror of
https://github.com/TomHarte/CLK.git
synced 2024-09-03 15:29:51 +00:00
363 lines
9.6 KiB
C++
363 lines
9.6 KiB
C++
//
|
||
// ShiftRoll.hpp
|
||
// Clock Signal
|
||
//
|
||
// Created by Thomas Harte on 08/11/2023.
|
||
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||
//
|
||
|
||
#pragma once
|
||
|
||
#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 = IntT((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 = IntT(
|
||
(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 = IntT((destination << 1) | carry);
|
||
carry = temp_carry;
|
||
} break;
|
||
default: {
|
||
const IntT temp_carry = destination & (1 << (temp_count - 1));
|
||
destination = IntT(
|
||
(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 = IntT(
|
||
(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 = IntT(
|
||
(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>(IntT(
|
||
(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 auto mask = IntT(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 auto mask = IntT(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);
|
||
}
|
||
|
||
}
|