1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-29 12:50:28 +00:00
CLK/InstructionSets/x86/Implementation/ShiftRoll.hpp

363 lines
9.6 KiB
C++
Raw Normal View History

//
// 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);
}
}