1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Implement RCL.

This commit is contained in:
Thomas Harte 2023-10-13 14:44:22 -04:00
parent bf832768e6
commit 1a0f848b21
2 changed files with 70 additions and 5 deletions

View File

@ -923,6 +923,54 @@ void setmoc(IntT &destination, uint8_t cl, Status &status) {
if(cl) setmo(destination, status);
}
template <Model model, typename IntT>
inline void rcl(IntT &destination, uint8_t count, Status &status) {
/*
(* RCL and RCR instructions *)
SIZE OperandSize
CASE (determine count) OF
SIZE = 8: tempCOUNT (COUNT AND 1FH) MOD 9;
SIZE = 16: tempCOUNT (COUNT AND 1FH) MOD 17;
SIZE = 32: tempCOUNT COUNT AND 1FH;
ESAC;
*/
if constexpr (model != Model::i8086) {
count &= 0x1f;
}
auto temp_count = count % (Numeric::bit_size<IntT>() + 1);
/*
(* 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.
*/
auto carry = status.carry_bit<IntT>();
while(temp_count--) {
const IntT temp_carry = (destination >> (Numeric::bit_size<IntT>() - 1)) & 1;
destination = (destination << 1) | carry;
carry = temp_carry;
}
status.set_from<Flag::Carry>(carry);
status.set_from<Flag::Overflow>(
((destination >> (Numeric::bit_size<IntT>() - 1)) & 1) ^ carry
);
}
}
template <
@ -976,6 +1024,14 @@ template <
flow_controller);
};
const auto shift_count = [&]() -> uint8_t {
switch(instruction.source().template source<false>()) {
case Source::None: return 1;
case Source::Immediate: return uint8_t(instruction.operand());
default: return registers.cl();
}
};
// Some instructions use a pair of registers as an extended accumulator — DX:AX or EDX:EAX.
// The two following return the high and low parts of that pair; they also work in Byte mode to return AH:AL,
// i.e. AX split into high and low parts.
@ -1075,6 +1131,8 @@ template <
case Operation::JLE: jcc(status.condition<Condition::LessOrEqual>()); return;
case Operation::JNLE: jcc(!status.condition<Condition::LessOrEqual>()); return;
case Operation::RCL: Primitive::rcl<model>(destination(), shift_count(), status); break;
case Operation::CLC: Primitive::clc(status); return;
case Operation::CLD: Primitive::cld(status); return;
case Operation::CLI: Primitive::cli(status); return;
@ -1126,6 +1184,9 @@ template <
// Dispatch to a function just like this that is specialised on data size.
// Fetching will occur in that specialised function, per the overlapping
// meaning of register names.
// TODO: incorporate and propagate address size.
switch(instruction.operation_size()) {
case DataSize::Byte:
perform<model, DataSize::Byte>(instruction, status, flow_controller, registers, memory, io);

View File

@ -415,10 +415,16 @@ struct FailedExecution {
// NOP
@"90.json.gz",
*/
// TODO: POP, POPF, PUSH, PUSHF
// TODO: RCL, RCR, ROL, ROR, SAL, SAR, SHR
// TODO: RCR, ROL, ROR, SAL, SAR, SHR
// RCL
@"D0.2.json.gz", @"D2.2.json.gz",
@"D1.2.json.gz", @"D3.2.json.gz",
/*
@"F8.json.gz", // CLC
@"FC.json.gz", // CLD
@"FA.json.gz", // CLI
@ -441,11 +447,9 @@ struct FailedExecution {
@"86.json.gz", @"87.json.gz",
@"91.json.gz", @"92.json.gz", @"93.json.gz", @"94.json.gz",
@"95.json.gz", @"96.json.gz", @"97.json.gz",
*/
@"D7.json.gz", // XLAT
/*
@"D6.json.gz", // SALC
@"D7.json.gz", // XLAT
@"D6.json.gz", // SALC
@"D0.6.json.gz", @"D1.6.json.gz", // SETMO
@"D2.6.json.gz", @"D3.6.json.gz", // SETMOC
*/