Last active
August 24, 2020 22:57
-
-
Save BreadFish64/739f518e567c5fcf4a7d963a7fd28fcd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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