Skip to content

Instantly share code, notes, and snippets.

@BreadFish64
Last active August 24, 2020 22:57
Show Gist options
  • Save BreadFish64/739f518e567c5fcf4a7d963a7fd28fcd to your computer and use it in GitHub Desktop.
Save BreadFish64/739f518e567c5fcf4a7d963a7fd28fcd to your computer and use it in GitHub Desktop.
#pragma once
#include <functional>
#include "opcode.hpp"
namespace GBA::CPU::DataProcessing {
namespace Basic {
using DataProcessingOperation = void (*)(ProcessorState& state, u32 operand_one, u32 operand_two,
bool carry_in, u32& result) noexcept;
using DataProcessingSubOperation = u32 (*)(ProcessorState& state, u32 operand_one, u32 operand_two,
bool carry_in) noexcept;
// Splitting the operand mode handlers into a normal and template function gives a hint to the
// compiler that it can do a register jump to the operation from a common operand mode handler
// instead of inlining if the compiler deems it worthwhile.
[[gnu::hot, gnu::noinline]] static void Register(ProcessorState& state, Operands operands,
DataProcessingOperation operation) noexcept {
auto [operand_two, carry] = ShiftByImmediate(state, operands.data_processing_reg.shtype,
state.r[operands.data_processing_reg.rm],
operands.data_processing_reg.shamt);
operation(state, state.r[operands.data_processing_reg.rn], operand_two, carry,
state.r[operands.data_processing_shift_by_reg.rd]);
}
template <DataProcessingOperation operation>
[[gnu::hot]] static void Register(ProcessorState& state, Operands operands) noexcept {
Register(state, operands, operation);
}
[[gnu::hot, gnu::noinline]] static void Immediate(ProcessorState& state, Operands operands,
DataProcessingOperation operation) noexcept {
operation(state, state.r[operands.data_processing_shift_by_reg.rn],
operands.data_processing_imm.imm, operands.data_processing_imm.carry,
state.r[operands.data_processing_shift_by_reg.rd]);
}
template <DataProcessingOperation operation>
[[gnu::hot]] static void Immediate(ProcessorState& state, Operands operands) noexcept {
Immediate(state, operands, operation);
}
[[gnu::hot, gnu::noinline]] static void ShiftByRegister(
ProcessorState& state, Operands operands, DataProcessingOperation operation) noexcept {
auto [operand_two, carry] = ExecuteShifter(state, operands.data_processing_shift_by_reg.shtype,
state.r[operands.data_processing_shift_by_reg.rm],
state.r[operands.data_processing_shift_by_reg.rs]);
operation(state, state.r[operands.data_processing_shift_by_reg.rn], operand_two, carry,
state.r[operands.data_processing_shift_by_reg.rd]);
}
template <DataProcessingOperation operation>
[[gnu::hot]] static void ShiftByRegister(ProcessorState& state, Operands operands) noexcept {
ShiftByRegister(state, operands, operation);
}
template <DataProcessingOperation operation>
[[gnu::hot]] static void ReverseOperands(ProcessorState& state, u32 operand_one, u32 operand_two,
bool carry_in, u32& result) noexcept {
operation(state, operand_two, operand_one, carry_in, result);
}
template <DataProcessingOperation operation>
[[gnu::hot]] static void InvertOperandTwo(ProcessorState& state, u32 operand_one, u32 operand_two,
bool carry_in, u32& result) noexcept {
operation(state, operand_one, ~operand_two, carry_in, result);
}
template <DataProcessingSubOperation operation>
[[gnu::hot]] static void Store(ProcessorState& state, u32 operand_one, u32 operand_two,
bool carry_in, u32& result) noexcept {
result = operation(state, operand_one, operand_two, carry_in);
}
template <DataProcessingSubOperation operation>
[[gnu::hot]] static void Discard(ProcessorState& state, u32 operand_one, u32 operand_two,
bool carry_in, [[maybe_unused]] u32& result) noexcept {
operation(state, operand_one, operand_two, carry_in);
}
// The compiler isn't smart enough to set all the flags without multiple comparisons
template <bool subtract, bool carry>
[[gnu::hot]] u32 x86FlagAddition(ProcessorState& state, u32 operand_one, u32 operand_two) noexcept {
if constexpr (subtract) {
operand_two = -operand_two;
}
u8 n, z, c, v;
if constexpr (carry) {
c = state.cpsr.c;
asm(R"(
addb $0xFF, %3;
adc %5, %0;
sets %1;
setz %2;
setc %3;
seto %4;
)"
: "+r"(operand_one), "=r"(n), "=r"(z), "+r"(c), "=r"(v)
: "r"(operand_two)
: "cc");
} else {
asm(R"(
add %5, %0;
sets %1;
setz %2;
setc %3;
seto %4;
)"
: "+r"(operand_one), "=r"(n), "=r"(z), "=r"(c), "=r"(v)
: "r"(operand_two)
: "cc");
}
state.cpsr.condition_flags = (u8{n} << 3) | (u8{z} << 2) | (u8{c} << 1) | (u8{v} << 0);
return operand_one;
}
template <bool S, bool subtract, bool carry>
[[gnu::hot]] static u32 Addition(ProcessorState& state, u32 operand_one, u32 operand_two,
[[maybe_unused]] bool carry_in) noexcept {
u32 result;
if constexpr (S && __x86_64__) {
result = x86FlagAddition<subtract, carry>(state, operand_one, operand_two);
} else {
if constexpr (subtract) {
operand_two = -operand_two;
}
result = operand_one + operand_two;
if constexpr (carry) {
result += state.cpsr.c;
if constexpr (subtract) {
--result;
}
}
if constexpr (S) {
state.cpsr.n = static_cast<s32>(result) < 0;
state.cpsr.z = result == 0;
state.cpsr.c = result < operand_one;
// operands one and two have the same sign and the result sign differs
state.cpsr.v =
static_cast<s32>(~(operand_one ^ operand_two) & (operand_one ^ result)) < 0;
}
}
return result;
}
template <typename operation, bool S>
[[gnu::hot]] static u32 Bitwise(ProcessorState& state, u32 operand_one, u32 operand_two,
bool carry_in) noexcept {
u32 result = operation{}(operand_one, operand_two);
if constexpr (S) {
// Placing the z flag hints to use the flag from the actual operation
// instead of comparing.
state.cpsr.z = result == 0;
// The n flag is obtained by masking the result rather than retrieving a flag.
state.cpsr.n = static_cast<s32>(result) < 0;
state.cpsr.c = carry_in;
}
return result;
}
constexpr auto SelectOperandTwo = []([[maybe_unused]] u32 x, u32 y) noexcept { return y; };
} // namespace Basic
template <bool S>
constexpr Basic::DataProcessingOperation AND = Basic::Store<Basic::Bitwise<std::bit_and<u32>, S>>;
template <bool S>
constexpr Basic::DataProcessingOperation EOR = Basic::Store<Basic::Bitwise<std::bit_xor<u32>, S>>;
template <bool S>
constexpr Basic::DataProcessingOperation SUB = Basic::Store<Basic::Addition<S, true, false>>;
template <bool S>
constexpr Basic::DataProcessingOperation RSB = Basic::ReverseOperands<SUB<S>>;
template <bool S>
constexpr Basic::DataProcessingOperation ADD = Basic::Store<Basic::Addition<S, false, false>>;
template <bool S>
constexpr Basic::DataProcessingOperation ADC = Basic::Store<Basic::Addition<S, false, true>>;
template <bool S>
constexpr Basic::DataProcessingOperation SBC = Basic::Store<Basic::Addition<S, true, true>>;
template <bool S>
constexpr Basic::DataProcessingOperation RSC = Basic::ReverseOperands<SBC<S>>;
constexpr Basic::DataProcessingOperation TST =
Basic::Discard<Basic::Bitwise<std::bit_and<u32>, true>>;
constexpr Basic::DataProcessingOperation TEQ =
Basic::Discard<Basic::Bitwise<std::bit_xor<u32>, true>>;
constexpr Basic::DataProcessingOperation CMP = Basic::Discard<Basic::Addition<true, true, false>>;
constexpr Basic::DataProcessingOperation CMN = Basic::Discard<Basic::Addition<true, false, false>>;
template <bool S>
constexpr Basic::DataProcessingOperation ORR = Basic::Store<Basic::Bitwise<std::bit_or<u32>, S>>;
template <bool S>
constexpr Basic::DataProcessingOperation MOV =
Basic::Store<Basic::Bitwise<decltype(Basic::SelectOperandTwo), S>>;
template <bool S>
constexpr Basic::DataProcessingOperation BIC = Basic::InvertOperandTwo<AND<S>>;
template <bool S>
constexpr Basic::DataProcessingOperation MVN = Basic::InvertOperandTwo<MOV<S>>;
// clang-format off
#define OP_TABLE(mode) \
{Basic::mode<AND<false>>, Basic::mode<AND<true>>}, \
{Basic::mode<EOR<false>>, Basic::mode<EOR<true>>}, \
{Basic::mode<SUB<false>>, Basic::mode<SUB<true>>}, \
{Basic::mode<RSB<false>>, Basic::mode<RSB<true>>}, \
{Basic::mode<ADD<false>>, Basic::mode<ADD<true>>}, \
{Basic::mode<ADC<false>>, Basic::mode<ADC<true>>}, \
{Basic::mode<SBC<false>>, Basic::mode<SBC<true>>}, \
{Basic::mode<RSC<false>>, Basic::mode<RSC<true>>}, \
{nullptr, Basic::mode<TST>}, \
{nullptr, Basic::mode<TEQ>}, \
{nullptr, Basic::mode<CMP>}, \
{nullptr, Basic::mode<CMN>}, \
{Basic::mode<ORR<false>>, Basic::mode<ORR<true>>}, \
{Basic::mode<MOV<false>>, Basic::mode<MOV<true>>}, \
{Basic::mode<BIC<false>>, Basic::mode<BIC<true>>}, \
{Basic::mode<MVN<false>>, Basic::mode<MVN<true>>},
// clang-format on
std::array<std::array<Operation, 2>, 16> register_operations{{OP_TABLE(Register)}};
std::array<std::array<Operation, 2>, 16> immediate_operations{{OP_TABLE(Immediate)}};
std::array<std::array<Operation, 2>, 16> shift_by_register_operations{{OP_TABLE(ShiftByRegister)}};
} // namespace GBA::CPU::DataProcessing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment