mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-09 15:39:08 +00:00
Merge pull request #1464 from TomHarte/AheadOfTimeCRCTables
Grab bag: calculate CRC tables ahead of time; improve carry typing.
This commit is contained in:
commit
f46ac2d0ed
@ -152,8 +152,9 @@ struct Executor {
|
||||
if constexpr (flags.set_condition_codes()) {
|
||||
// "For a subtraction, including the comparison instruction CMP, C is set to 0 if
|
||||
// the subtraction produced a borrow (that is, an unsigned underflow), and to 1 otherwise."
|
||||
registers_.set_c(!Numeric::carried_out<false, 31>(lhs, rhs, conditions));
|
||||
registers_.set_v(Numeric::overflow<false>(lhs, rhs, conditions));
|
||||
using NumOp = Numeric::Operation;
|
||||
registers_.set_c(!Numeric::carried_out<NumOp::Subtract, 31>(lhs, rhs, conditions));
|
||||
registers_.set_v(Numeric::overflow<NumOp::Subtract>(lhs, rhs, conditions));
|
||||
}
|
||||
|
||||
if constexpr (!is_comparison(flags.operation())) {
|
||||
@ -185,8 +186,9 @@ struct Executor {
|
||||
}
|
||||
|
||||
if constexpr (flags.set_condition_codes()) {
|
||||
registers_.set_c(Numeric::carried_out<true, 31>(operand1, operand2, conditions));
|
||||
registers_.set_v(Numeric::overflow<true>(operand1, operand2, conditions));
|
||||
using NumOp = Numeric::Operation;
|
||||
registers_.set_c(Numeric::carried_out<NumOp::Add, 31>(operand1, operand2, conditions));
|
||||
registers_.set_v(Numeric::overflow<NumOp::Add>(operand1, operand2, conditions));
|
||||
}
|
||||
|
||||
if constexpr (!is_comparison(flags.operation())) {
|
||||
|
@ -28,10 +28,12 @@ namespace Primitive {
|
||||
/// Performs an add or subtract (as per @c is_add) between @c source and @c destination,
|
||||
/// updating @c status. @c is_extend indicates whether this is an extend operation (e.g. ADDX)
|
||||
/// or a plain one (e.g. ADD).
|
||||
template <bool is_add, bool is_extend, typename IntT>
|
||||
template <Numeric::Operation operation, bool is_extend, typename IntT>
|
||||
static void add_sub(const IntT source, IntT &destination, Status &status) {
|
||||
static_assert(!std::numeric_limits<IntT>::is_signed);
|
||||
|
||||
static_assert(operation == Numeric::Operation::Add || operation == Numeric::Operation::Subtract);
|
||||
constexpr bool is_add = operation == Numeric::Operation::Add;
|
||||
IntT result = is_add ?
|
||||
destination + source :
|
||||
destination - source;
|
||||
@ -59,7 +61,7 @@ static void add_sub(const IntT source, IntT &destination, Status &status) {
|
||||
status.zero_result = Status::FlagT(result);
|
||||
}
|
||||
status.set_negative(result);
|
||||
status.overflow_flag = Numeric::overflow<is_add>(destination, source, result);
|
||||
status.overflow_flag = Numeric::overflow<operation>(destination, source, result);
|
||||
destination = result;
|
||||
}
|
||||
|
||||
@ -121,7 +123,7 @@ void compare(const IntT source, const IntT destination, Status &status) {
|
||||
const IntT result = destination - source;
|
||||
status.carry_flag = result > destination;
|
||||
status.set_neg_zero(result);
|
||||
status.overflow_flag = Numeric::overflow<false>(destination, source, result);
|
||||
status.overflow_flag = Numeric::overflow<Numeric::Operation::Subtract>(destination, source, result);
|
||||
}
|
||||
|
||||
/// @returns the name of the bit to be used as a mask for BCLR, BCHG, BSET or BTST for
|
||||
@ -279,7 +281,7 @@ template <bool is_extend, typename IntT> void negative(IntT &source, Status &sta
|
||||
}
|
||||
status.extend_flag = status.carry_flag = result; // i.e. any value other than 0 will result in carry.
|
||||
status.set_negative(result);
|
||||
status.overflow_flag = Numeric::overflow<false>(IntT(0), source, result);
|
||||
status.overflow_flag = Numeric::overflow<Numeric::Operation::Subtract>(IntT(0), source, result);
|
||||
|
||||
source = result;
|
||||
}
|
||||
@ -519,6 +521,7 @@ template <
|
||||
Status &status,
|
||||
FlowController &flow_controller
|
||||
) {
|
||||
using NumOp = Numeric::Operation;
|
||||
switch((operation != Operation::Undefined) ? operation : instruction.operation) {
|
||||
/*
|
||||
ABCD adds the lowest bytes from the source and destination using BCD arithmetic,
|
||||
@ -551,20 +554,20 @@ template <
|
||||
|
||||
// ADD and ADDA add two quantities, the latter sign extending and without setting any flags;
|
||||
// ADDQ and SUBQ act as ADD and SUB, but taking the second argument from the instruction code.
|
||||
case Operation::ADDb: Primitive::add_sub<true, false>(src.b, dest.b, status); break;
|
||||
case Operation::SUBb: Primitive::add_sub<false, false>(src.b, dest.b, status); break;
|
||||
case Operation::ADDXb: Primitive::add_sub<true, true>(src.b, dest.b, status); break;
|
||||
case Operation::SUBXb: Primitive::add_sub<false, true>(src.b, dest.b, status); break;
|
||||
case Operation::ADDb: Primitive::add_sub<NumOp::Add, false>(src.b, dest.b, status); break;
|
||||
case Operation::SUBb: Primitive::add_sub<NumOp::Subtract, false>(src.b, dest.b, status); break;
|
||||
case Operation::ADDXb: Primitive::add_sub<NumOp::Add, true>(src.b, dest.b, status); break;
|
||||
case Operation::SUBXb: Primitive::add_sub<NumOp::Subtract, true>(src.b, dest.b, status); break;
|
||||
|
||||
case Operation::ADDw: Primitive::add_sub<true, false>(src.w, dest.w, status); break;
|
||||
case Operation::SUBw: Primitive::add_sub<false, false>(src.w, dest.w, status); break;
|
||||
case Operation::ADDXw: Primitive::add_sub<true, true>(src.w, dest.w, status); break;
|
||||
case Operation::SUBXw: Primitive::add_sub<false, true>(src.w, dest.w, status); break;
|
||||
case Operation::ADDw: Primitive::add_sub<NumOp::Add, false>(src.w, dest.w, status); break;
|
||||
case Operation::SUBw: Primitive::add_sub<NumOp::Subtract, false>(src.w, dest.w, status); break;
|
||||
case Operation::ADDXw: Primitive::add_sub<NumOp::Add, true>(src.w, dest.w, status); break;
|
||||
case Operation::SUBXw: Primitive::add_sub<NumOp::Subtract, true>(src.w, dest.w, status); break;
|
||||
|
||||
case Operation::ADDl: Primitive::add_sub<true, false>(src.l, dest.l, status); break;
|
||||
case Operation::SUBl: Primitive::add_sub<false, false>(src.l, dest.l, status); break;
|
||||
case Operation::ADDXl: Primitive::add_sub<true, true>(src.l, dest.l, status); break;
|
||||
case Operation::SUBXl: Primitive::add_sub<false, true>(src.l, dest.l, status); break;
|
||||
case Operation::ADDl: Primitive::add_sub<NumOp::Add, false>(src.l, dest.l, status); break;
|
||||
case Operation::SUBl: Primitive::add_sub<NumOp::Subtract, false>(src.l, dest.l, status); break;
|
||||
case Operation::ADDXl: Primitive::add_sub<NumOp::Add, true>(src.l, dest.l, status); break;
|
||||
case Operation::SUBXl: Primitive::add_sub<NumOp::Subtract, true>(src.l, dest.l, status); break;
|
||||
|
||||
case Operation::ADDAw: dest.l += u_extend16(src.w); break;
|
||||
case Operation::ADDAl: dest.l += src.l; break;
|
||||
|
@ -30,12 +30,13 @@ void add(
|
||||
*/
|
||||
const IntT result = destination + source + (with_carry ? context.flags.template carry_bit<IntT>() : 0);
|
||||
|
||||
using NumOp = Numeric::Operation;
|
||||
context.flags.template set_from<Flag::Carry>(
|
||||
Numeric::carried_out<true, Numeric::bit_size<IntT>() - 1>(destination, source, result));
|
||||
Numeric::carried_out<NumOp::Add, 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));
|
||||
Numeric::overflow<NumOp::Add, IntT>(destination, source, result));
|
||||
|
||||
context.flags.template set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(result);
|
||||
|
||||
@ -56,12 +57,13 @@ void sub(
|
||||
*/
|
||||
const IntT result = destination - source - (with_borrow ? context.flags.template carry_bit<IntT>() : 0);
|
||||
|
||||
using NumOp = Numeric::Operation;
|
||||
context.flags.template set_from<Flag::Carry>(
|
||||
Numeric::carried_out<false, Numeric::bit_size<IntT>() - 1>(destination, source, result));
|
||||
Numeric::carried_out<NumOp::Subtract, 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));
|
||||
Numeric::overflow<NumOp::Subtract, IntT>(destination, source, result));
|
||||
|
||||
context.flags.template set_from<IntT, Flag::Zero, Flag::Sign, Flag::ParityOdd>(result);
|
||||
|
||||
|
@ -8,6 +8,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Carry.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
@ -26,30 +29,41 @@ constexpr uint8_t reverse_byte(uint8_t byte) {
|
||||
}
|
||||
|
||||
/*! Provides a class capable of generating a CRC from source data. */
|
||||
template <typename IntType, IntType reset_value, IntType output_xor, bool reflect_input, bool reflect_output>
|
||||
template <
|
||||
typename IntType,
|
||||
IntType polynomial,
|
||||
IntType reset_value,
|
||||
IntType output_xor,
|
||||
bool reflect_input,
|
||||
bool reflect_output
|
||||
>
|
||||
class Generator {
|
||||
public:
|
||||
/*!
|
||||
Instantiates a CRC16 that will compute the CRC16 specified by the supplied
|
||||
@c polynomial and @c reset_value.
|
||||
*/
|
||||
constexpr Generator(IntType polynomial) noexcept: value_(reset_value) {
|
||||
const IntType top_bit = IntType(~(IntType(~0) >> 1));
|
||||
for(int c = 0; c < 256; c++) {
|
||||
IntType shift_value = IntType(c << multibyte_shift);
|
||||
for(int b = 0; b < 8; b++) {
|
||||
IntType exclusive_or = (shift_value&top_bit) ? polynomial : 0;
|
||||
shift_value = IntType(shift_value << 1) ^ exclusive_or;
|
||||
}
|
||||
xor_table[c] = shift_value;
|
||||
}
|
||||
}
|
||||
constexpr Generator() noexcept: value_(reset_value) {}
|
||||
|
||||
/// Resets the CRC to the reset value.
|
||||
void reset() { value_ = reset_value; }
|
||||
|
||||
/// Updates the CRC to include @c byte.
|
||||
void add(uint8_t byte) {
|
||||
static constexpr std::array<IntType, 256> xor_table = [] {
|
||||
std::array<IntType, 256> table{};
|
||||
constexpr IntType top_bit = Numeric::top_bit<IntType>();
|
||||
for(size_t c = 0; c < 256; c++) {
|
||||
IntType shift_value = IntType(c << multibyte_shift);
|
||||
for(int b = 0; b < 8; b++) {
|
||||
const IntType exclusive_or = (shift_value & top_bit) ? polynomial : 0;
|
||||
shift_value = IntType(shift_value << 1) ^ exclusive_or;
|
||||
}
|
||||
table[c] = shift_value;
|
||||
}
|
||||
return table;
|
||||
} ();
|
||||
|
||||
if constexpr (reflect_input) byte = reverse_byte(byte);
|
||||
value_ = IntType((value_ << 8) ^ xor_table[(value_ >> multibyte_shift) ^ byte]);
|
||||
}
|
||||
@ -100,7 +114,6 @@ public:
|
||||
|
||||
private:
|
||||
static constexpr int multibyte_shift = (sizeof(IntType) * 8) - 8;
|
||||
IntType xor_table[256];
|
||||
IntType value_;
|
||||
};
|
||||
|
||||
@ -108,15 +121,11 @@ private:
|
||||
Provides a generator of 16-bit CCITT CRCs, which amongst other uses are
|
||||
those used by the FM and MFM disk encodings.
|
||||
*/
|
||||
struct CCITT: public Generator<uint16_t, 0xffff, 0x0000, false, false> {
|
||||
CCITT(): Generator(0x1021) {}
|
||||
};
|
||||
using CCITT = Generator<uint16_t, 0x1021, 0xffff, 0x0000, false, false>;
|
||||
|
||||
/*!
|
||||
Provides a generator of "standard 32-bit" CRCs.
|
||||
*/
|
||||
struct CRC32: public Generator<uint32_t, 0xffffffff, 0xffffffff, true, true> {
|
||||
CRC32(): Generator(0x04c11db7) {}
|
||||
};
|
||||
using CRC32 = Generator<uint32_t, 0x04c11db7, 0xffffffff, 0xffffffff, true, true>;
|
||||
|
||||
}
|
||||
|
@ -12,11 +12,17 @@
|
||||
|
||||
namespace Numeric {
|
||||
|
||||
enum class Operation {
|
||||
Add,
|
||||
Subtract,
|
||||
};
|
||||
|
||||
/// @returns @c true if from @c bit there was:
|
||||
/// • carry after calculating @c lhs + @c rhs if @c is_add is true; or
|
||||
/// • borrow after calculating @c lhs - @c rhs if @c is_add is false;
|
||||
/// producing @c result.
|
||||
template <bool is_add, int bit, typename IntT> bool carried_out(IntT lhs, IntT rhs, IntT result) {
|
||||
template <Operation operation, int bit, typename IntT>
|
||||
constexpr bool carried_out(const IntT lhs, const IntT rhs, const IntT result) {
|
||||
// Additive:
|
||||
//
|
||||
// 0 and 0 => didn't.
|
||||
@ -28,11 +34,10 @@ template <bool is_add, int bit, typename IntT> bool carried_out(IntT lhs, IntT r
|
||||
// 1 and 0 => didn't
|
||||
// 1 and 1 or 0 and 0 => did if 1.
|
||||
// 0 and 1 => did.
|
||||
if constexpr (!is_add) {
|
||||
rhs = ~rhs;
|
||||
}
|
||||
const bool carry = IntT(1 << bit) & (lhs | rhs) & ((lhs & rhs) | ~result);
|
||||
if constexpr (!is_add) {
|
||||
static_assert(operation == Operation::Add || operation == Operation::Subtract);
|
||||
const auto adj_rhs = (operation == Operation::Subtract) ? ~rhs : rhs;
|
||||
const bool carry = IntT(1 << bit) & (lhs | adj_rhs) & ((lhs & adj_rhs) | ~result);
|
||||
if constexpr (operation == Operation::Subtract) {
|
||||
return !carry;
|
||||
} else {
|
||||
return carry;
|
||||
@ -43,21 +48,24 @@ template <bool is_add, int bit, typename IntT> bool carried_out(IntT lhs, IntT r
|
||||
/// • @c lhs + @c rhs; or
|
||||
/// • @c lhs - @c rhs;
|
||||
/// producing @c result.
|
||||
template <int bit, typename IntT> bool carried_in(IntT lhs, IntT rhs, IntT result) {
|
||||
template <int bit, typename IntT>
|
||||
constexpr bool carried_in(const IntT lhs, const IntT rhs, const IntT result) {
|
||||
// 0 and 0 or 1 and 1 => did if 1.
|
||||
// 0 and 1 or 1 and 0 => did if 0.
|
||||
return IntT(1 << bit) & (lhs ^ rhs ^ result);
|
||||
}
|
||||
|
||||
/// @returns An int of type @c IntT with only the most-significant bit set.
|
||||
template <typename IntT> constexpr IntT top_bit() {
|
||||
template <typename IntT>
|
||||
constexpr IntT top_bit() {
|
||||
static_assert(!std::numeric_limits<IntT>::is_signed);
|
||||
constexpr IntT max = std::numeric_limits<IntT>::max();
|
||||
return max - (max >> 1);
|
||||
}
|
||||
|
||||
/// @returns The number of bits in @c IntT.
|
||||
template <typename IntT> constexpr int bit_size() {
|
||||
template <typename IntT>
|
||||
constexpr int bit_size() {
|
||||
return sizeof(IntT) * 8;
|
||||
}
|
||||
|
||||
@ -65,12 +73,13 @@ template <typename IntT> constexpr int bit_size() {
|
||||
/// • @c lhs + @c rhs (if @c is_add is true); or
|
||||
/// • @c lhs - @c rhs (if @c is_add is false)
|
||||
/// and the result was @c result. All other bits will be clear.
|
||||
template <bool is_add, typename IntT>
|
||||
IntT overflow(IntT lhs, IntT rhs, IntT result) {
|
||||
template <Operation operation, typename IntT>
|
||||
constexpr IntT overflow(const IntT lhs, const IntT rhs, const IntT result) {
|
||||
const IntT output_changed = result ^ lhs;
|
||||
const IntT input_differed = lhs ^ rhs;
|
||||
|
||||
if constexpr (is_add) {
|
||||
static_assert(operation == Operation::Add || operation == Operation::Subtract);
|
||||
if constexpr (operation == Operation::Add) {
|
||||
return top_bit<IntT>() & output_changed & ~input_differed;
|
||||
} else {
|
||||
return top_bit<IntT>() & output_changed & input_differed;
|
||||
|
@ -368,7 +368,7 @@ void Processor<personality, T, uses_ready_line>::run_for(const Cycles cycles) {
|
||||
|
||||
// All flags are set based only on the decimal result.
|
||||
flags_.zero_result = result;
|
||||
flags_.carry = Numeric::carried_out<true, 7>(a_, operand_, result);
|
||||
flags_.carry = Numeric::carried_out<Numeric::Operation::Add, 7>(a_, operand_, result);
|
||||
flags_.negative_result = result;
|
||||
flags_.overflow = (( (result ^ a_) & (result ^ operand_) ) & 0x80) >> 1;
|
||||
|
||||
@ -418,7 +418,7 @@ void Processor<personality, T, uses_ready_line>::run_for(const Cycles cycles) {
|
||||
if(flags_.decimal && has_decimal_mode(personality)) {
|
||||
uint8_t result = a_ + operand_ + flags_.carry;
|
||||
flags_.zero_result = result;
|
||||
flags_.carry = Numeric::carried_out<true, 7>(a_, operand_, result);
|
||||
flags_.carry = Numeric::carried_out<Numeric::Operation::Add, 7>(a_, operand_, result);
|
||||
|
||||
// General ADC logic:
|
||||
//
|
||||
|
@ -14,7 +14,7 @@ namespace {
|
||||
constexpr int PLLClockRate = 1920000;
|
||||
}
|
||||
|
||||
Parser::Parser(): crc_(0x1021) {
|
||||
Parser::Parser() {
|
||||
shifter_.set_delegate(this);
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ private:
|
||||
void process_pulse(const Storage::Tape::Pulse &) override;
|
||||
|
||||
bool did_update_shifter(int new_value, int length);
|
||||
CRC::Generator<uint16_t, 0x0000, 0x0000, false, false> crc_;
|
||||
CRC::Generator<uint16_t, 0x1021, 0x0000, 0x0000, false, false> crc_;
|
||||
Shifter shifter_;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user