skyline/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell/macro_interpreter.cpp

208 lines
7.6 KiB
C++

// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <soc/gmmu.h>
#include <soc/gm20b/engines/maxwell_3d.h>
namespace skyline::soc::gm20b::engine::maxwell3d {
void MacroInterpreter::Execute(size_t offset, const std::vector<u32> &args) {
// Reset the interpreter state
registers = {};
carryFlag = false;
methodAddress.raw = 0;
opcode = reinterpret_cast<Opcode *>(&maxwell3D.macroCode[offset]);
argument = args.data();
// The first argument is stored in register 1
registers[1] = *argument++;
while (Step());
}
FORCE_INLINE bool MacroInterpreter::Step(Opcode *delayedOpcode) {
switch (opcode->operation) {
case Opcode::Operation::AluRegister: {
u32 result{HandleAlu(opcode->aluOperation, registers[opcode->srcA], registers[opcode->srcB])};
HandleAssignment(opcode->assignmentOperation, opcode->dest, result);
break;
}
case Opcode::Operation::AddImmediate:
HandleAssignment(opcode->assignmentOperation, opcode->dest, registers[opcode->srcA] + opcode->immediate);
break;
case Opcode::Operation::BitfieldReplace: {
u32 src{registers[opcode->srcB]};
u32 dest{registers[opcode->srcA]};
// Extract the source region
src = (src >> opcode->bitfield.srcBit) & opcode->bitfield.GetMask();
// Mask out the bits that we will replace
dest &= ~(opcode->bitfield.GetMask() << opcode->bitfield.destBit);
// Replace the bitfield region in the destination with the region from the source
dest |= src << opcode->bitfield.destBit;
HandleAssignment(opcode->assignmentOperation, opcode->dest, dest);
break;
}
case Opcode::Operation::BitfieldExtractShiftLeftImmediate: {
u32 src{registers[opcode->srcB]};
u32 dest{registers[opcode->srcA]};
u32 result{((src >> dest) & opcode->bitfield.GetMask()) << opcode->bitfield.destBit};
HandleAssignment(opcode->assignmentOperation, opcode->dest, result);
break;
}
case Opcode::Operation::BitfieldExtractShiftLeftRegister: {
u32 src{registers[opcode->srcB]};
u32 dest{registers[opcode->srcA]};
u32 result{((src >> opcode->bitfield.srcBit) & opcode->bitfield.GetMask()) << dest};
HandleAssignment(opcode->assignmentOperation, opcode->dest, result);
break;
}
case Opcode::Operation::ReadImmediate: {
u32 result{maxwell3D.registers.raw[registers[opcode->srcA] + opcode->immediate]};
HandleAssignment(opcode->assignmentOperation, opcode->dest, result);
break;
}
case Opcode::Operation::Branch: {
if (delayedOpcode != nullptr)
throw exception("Cannot branch while inside a delay slot");
u32 value{registers[opcode->srcA]};
bool branch{(opcode->branchCondition == Opcode::BranchCondition::Zero) ? (value == 0) : (value != 0)};
if (branch) {
if (opcode->noDelay) {
opcode += opcode->immediate;
return true;
} else {
Opcode *targetOpcode{opcode + opcode->immediate};
// Step into delay slot
opcode++;
return Step(targetOpcode);
}
}
break;
}
default:
throw exception("Unknown MME opcode encountered: 0x{:X}", static_cast<u8>(opcode->operation));
}
if (opcode->exit && (delayedOpcode == nullptr)) {
// Exit has a delay slot
opcode++;
Step(opcode);
return false;
}
if (delayedOpcode != nullptr)
opcode = delayedOpcode;
else
opcode++;
return true;
}
FORCE_INLINE u32 MacroInterpreter::HandleAlu(Opcode::AluOperation operation, u32 srcA, u32 srcB) {
switch (operation) {
case Opcode::AluOperation::Add: {
u64 result{static_cast<u64>(srcA) + srcB};
carryFlag = result >> 32;
return static_cast<u32>(result);
}
case Opcode::AluOperation::AddWithCarry: {
u64 result{static_cast<u64>(srcA) + srcB + carryFlag};
carryFlag = result >> 32;
return static_cast<u32>(result);
}
case Opcode::AluOperation::Subtract: {
u64 result{static_cast<u64>(srcA) - srcB};
carryFlag = result & 0xFFFFFFFF;
return static_cast<u32>(result);
}
case Opcode::AluOperation::SubtractWithBorrow: {
u64 result{static_cast<u64>(srcA) - srcB - !carryFlag};
carryFlag = result & 0xFFFFFFFF;
return static_cast<u32>(result);
}
case Opcode::AluOperation::BitwiseXor:
return srcA ^ srcB;
case Opcode::AluOperation::BitwiseOr:
return srcA | srcB;
case Opcode::AluOperation::BitwiseAnd:
return srcA & srcB;
case Opcode::AluOperation::BitwiseAndNot:
return srcA & ~srcB;
case Opcode::AluOperation::BitwiseNand:
return ~(srcA & srcB);
}
}
FORCE_INLINE void MacroInterpreter::HandleAssignment(Opcode::AssignmentOperation operation, u8 reg, u32 result) {
switch (operation) {
case Opcode::AssignmentOperation::IgnoreAndFetch:
WriteRegister(reg, *argument++);
break;
case Opcode::AssignmentOperation::Move:
WriteRegister(reg, result);
break;
case Opcode::AssignmentOperation::MoveAndSetMethod:
WriteRegister(reg, result);
methodAddress.raw = result;
break;
case Opcode::AssignmentOperation::FetchAndSend:
WriteRegister(reg, *argument++);
Send(result);
break;
case Opcode::AssignmentOperation::MoveAndSend:
WriteRegister(reg, result);
Send(result);
break;
case Opcode::AssignmentOperation::FetchAndSetMethod:
WriteRegister(reg, *argument++);
methodAddress.raw = result;
break;
case Opcode::AssignmentOperation::MoveAndSetMethodThenFetchAndSend:
WriteRegister(reg, result);
methodAddress.raw = result;
Send(*argument++);
break;
case Opcode::AssignmentOperation::MoveAndSetMethodThenSendHigh:
WriteRegister(reg, result);
methodAddress.raw = result;
Send(methodAddress.increment);
break;
}
}
FORCE_INLINE void MacroInterpreter::Send(u32 pArgument) {
maxwell3D.CallMethod(MethodParams{methodAddress.address, pArgument, 0, true});
methodAddress.address += methodAddress.increment;
}
FORCE_INLINE void MacroInterpreter::WriteRegister(u8 reg, u32 value) {
// Register 0 should always be zero so block writes to it
if (reg == 0) [[unlikely]]
return;
registers[reg] = value;
}
}