Migrate Maxwell3D::Registers to OffsetMember

Maxwell3D registers were primarily written with absolute offsets and ended up being fairly messy due to attempting to emulate this using struct relative positioning resulting in a lot of pointless padding members. This has now been improved by utilizing `OffsetMember` to directly use offsets resulting in much neater code.
This commit is contained in:
PixelyIon 2021-11-10 21:31:45 +05:30
parent 69761389ff
commit fbfad21f03
3 changed files with 227 additions and 239 deletions

View File

@ -29,6 +29,16 @@ namespace skyline::soc::gm20b::engine::maxwell3d::type {
MethodReplay = 3, //!< Replays older tracked writes for any new writes to registers, discarding the contents of the new write
};
struct SyncpointAction {
u16 id : 12;
u8 _pad0_ : 4;
bool flushCache : 1;
u8 _pad1_ : 3;
bool increment : 1;
u16 _pad2_ : 11;
};
static_assert(sizeof(SyncpointAction) == sizeof(u32));
constexpr static size_t RenderTargetCount{8}; //!< Maximum amount of render targets that can be bound at once on Maxwell 3D
/**
@ -360,6 +370,14 @@ namespace skyline::soc::gm20b::engine::maxwell3d::type {
};
static_assert(sizeof(Blend) == (sizeof(u32) * 8));
struct MultisampleControl {
bool alphaToCoverage : 1;
u8 _pad0_ : 3;
bool alphaToOne : 1;
u32 _pad1_ : 27;
};
static_assert(sizeof(MultisampleControl) == sizeof(u32));
enum class StencilOp : u32 {
Keep = 1,
Zero = 2,
@ -371,6 +389,18 @@ namespace skyline::soc::gm20b::engine::maxwell3d::type {
DecrementAndWrap = 8,
};
struct PointCoordReplace {
u8 _unk_ : 2;
enum class CoordOrigin : u8 {
LowerLeft = 0,
UpperLeft = 1,
};
CoordOrigin origin : 1;
u16 enable : 10;
u32 _pad_ : 19;
};
static_assert(sizeof(PointCoordReplace) == sizeof(u32));
enum class FrontFace : u32 {
Clockwise = 0x900,
CounterClockwise = 0x901,
@ -495,10 +525,5 @@ namespace skyline::soc::gm20b::engine::maxwell3d::type {
};
static_assert(sizeof(SemaphoreInfo) == sizeof(u32));
enum class CoordOrigin : u8 {
LowerLeft = 0,
UpperLeft = 1,
};
#pragma pack(pop)
}

View File

@ -16,57 +16,57 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
registers.rasterizerEnable = true;
for (auto &transform : registers.viewportTransforms) {
for (auto &transform : *registers.viewportTransforms) {
transform.swizzles.x = type::ViewportTransform::Swizzle::PositiveX;
transform.swizzles.y = type::ViewportTransform::Swizzle::PositiveY;
transform.swizzles.z = type::ViewportTransform::Swizzle::PositiveZ;
transform.swizzles.w = type::ViewportTransform::Swizzle::PositiveW;
}
for (auto &viewport : registers.viewports) {
for (auto &viewport : *registers.viewports) {
viewport.depthRangeFar = 1.0f;
viewport.depthRangeNear = 0.0f;
}
registers.polygonMode.front = type::PolygonMode::Fill;
registers.polygonMode.back = type::PolygonMode::Fill;
registers.polygonMode->front = type::PolygonMode::Fill;
registers.polygonMode->back = type::PolygonMode::Fill;
registers.stencilFront.failOp = registers.stencilFront.zFailOp = registers.stencilFront.zPassOp = type::StencilOp::Keep;
registers.stencilFront.compare.op = type::CompareOp::Always;
registers.stencilFront.compare.mask = 0xFFFFFFFF;
registers.stencilFront.writeMask = 0xFFFFFFFF;
registers.stencilFront->failOp = registers.stencilFront->zFailOp = registers.stencilFront->zPassOp = type::StencilOp::Keep;
registers.stencilFront->compare.op = type::CompareOp::Always;
registers.stencilFront->compare.mask = 0xFFFFFFFF;
registers.stencilFront->writeMask = 0xFFFFFFFF;
registers.stencilTwoSideEnable = true;
registers.stencilBack.failOp = registers.stencilBack.zFailOp = registers.stencilBack.zPassOp = type::StencilOp::Keep;
registers.stencilBack.compareOp = type::CompareOp::Always;
registers.stencilBackExtra.compareMask = 0xFFFFFFFF;
registers.stencilBackExtra.writeMask = 0xFFFFFFFF;
registers.stencilBack->failOp = registers.stencilBack->zFailOp = registers.stencilBack->zPassOp = type::StencilOp::Keep;
registers.stencilBack->compareOp = type::CompareOp::Always;
registers.stencilBackExtra->compareMask = 0xFFFFFFFF;
registers.stencilBackExtra->writeMask = 0xFFFFFFFF;
registers.rtSeparateFragData = true;
for (auto &attribute : registers.vertexAttributeState)
for (auto &attribute : *registers.vertexAttributeState)
attribute.fixed = true;
registers.depthTestFunc = type::CompareOp::Always;
registers.blend.colorOp = registers.blend.alphaOp = type::Blend::Op::Add;
registers.blend.colorSrcFactor = registers.blend.alphaSrcFactor = type::Blend::Factor::One;
registers.blend.colorDestFactor = registers.blend.alphaDestFactor = type::Blend::Factor::Zero;
registers.blendState->colorOp = registers.blendState->alphaOp = type::Blend::Op::Add;
registers.blendState->colorSrcFactor = registers.blendState->alphaSrcFactor = type::Blend::Factor::One;
registers.blendState->colorDestFactor = registers.blendState->alphaDestFactor = type::Blend::Factor::Zero;
registers.lineWidthSmooth = 1.0f;
registers.lineWidthAliased = 1.0f;
registers.pointSpriteEnable = true;
registers.pointSpriteSize = 1.0f;
registers.pointCoordReplace.enable = true;
registers.pointCoordReplace->enable = true;
registers.frontFace = type::FrontFace::CounterClockwise;
registers.cullFace = type::CullFace::Back;
for (auto &mask : registers.colorMask)
for (auto &mask : *registers.colorMask)
mask.r = mask.g = mask.b = mask.a = 1;
for (auto &blend : registers.independentBlend) {
for (auto &blend : *registers.independentBlend) {
blend.colorOp = blend.alphaOp = type::Blend::Op::Add;
blend.colorSrcFactor = blend.alphaSrcFactor = type::Blend::Factor::One;
blend.colorDestFactor = blend.alphaDestFactor = type::Blend::Factor::Zero;
@ -105,27 +105,31 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
return;
}
#define MAXWELL3D_OFFSET(field) U32_OFFSET(Registers, field)
#define MAXWELL3D_STRUCT_OFFSET(field, member) U32_OFFSET(Registers, field) + U32_OFFSET(typeof(Registers::field), member)
#define MAXWELL3D_ARRAY_OFFSET(field, index) U32_OFFSET(Registers, field) + ((sizeof(typeof(Registers::field[0])) / sizeof(u32)) * index)
#define MAXWELL3D_OFFSET(field) (sizeof(typeof(Registers::field)) - sizeof(typeof(*Registers::field))) / sizeof(u32)
#define MAXWELL3D_STRUCT_OFFSET(field, member) MAXWELL3D_OFFSET(field) + U32_OFFSET(typeof(*Registers::field), member)
#define MAXWELL3D_ARRAY_OFFSET(field, index) MAXWELL3D_OFFSET(field) + ((sizeof(typeof(Registers::field[0])) / sizeof(u32)) * index)
#define MAXWELL3D_ARRAY_STRUCT_OFFSET(field, index, member) MAXWELL3D_ARRAY_OFFSET(field, index) + U32_OFFSET(typeof(Registers::field[0]), member)
#define MAXWELL3D_ARRAY_STRUCT_STRUCT_OFFSET(field, index, member, submember) MAXWELL3D_ARRAY_STRUCT_OFFSET(field, index, member) + U32_OFFSET(typeof(Registers::field[0].member), submember)
#define MAXWELL3D_CASE(field, content) case MAXWELL3D_OFFSET(field): { \
auto field{util::BitCast<typeof(*registers.field)>(argument)}; \
content \
return; \
}
#define MAXWELL3D_CASE_BASE(fieldName, fieldAccessor, offset, content) case offset: { \
auto fieldName{util::BitCast<typeof(registers.fieldAccessor)>(argument)}; \
content \
return; \
}
#define MAXWELL3D_CASE(field, content) MAXWELL3D_CASE_BASE(field, field, MAXWELL3D_OFFSET(field), content)
#define MAXWELL3D_STRUCT_CASE(field, member, content) MAXWELL3D_CASE_BASE(member, field.member, MAXWELL3D_STRUCT_OFFSET(field, member), content)
#define MAXWELL3D_STRUCT_CASE(field, member, content) MAXWELL3D_CASE_BASE(member, field->member, MAXWELL3D_STRUCT_OFFSET(field, member), content)
#define MAXWELL3D_ARRAY_CASE(field, index, content) MAXWELL3D_CASE_BASE(field, field[index], MAXWELL3D_ARRAY_OFFSET(field, index), content)
#define MAXWELL3D_ARRAY_STRUCT_CASE(field, index, member, content) MAXWELL3D_CASE_BASE(member, field[index].member, MAXWELL3D_ARRAY_STRUCT_OFFSET(field, index, member), content)
#define MAXWELL3D_ARRAY_STRUCT_STRUCT_CASE(field, index, member, submember, content) MAXWELL3D_CASE_BASE(submember, field[index].member.submember, MAXWELL3D_ARRAY_STRUCT_STRUCT_OFFSET(field, index, member, submember), content)
if (method != MAXWELL3D_OFFSET(mme.shadowRamControl)) {
if (shadowRegisters.mme.shadowRamControl == type::MmeShadowRamControl::MethodTrack || shadowRegisters.mme.shadowRamControl == type::MmeShadowRamControl::MethodTrackWithFilter)
if (method != MAXWELL3D_STRUCT_OFFSET(mme, shadowRamControl)) {
if (shadowRegisters.mme->shadowRamControl == type::MmeShadowRamControl::MethodTrack || shadowRegisters.mme->shadowRamControl == type::MmeShadowRamControl::MethodTrackWithFilter)
shadowRegisters.raw[method] = argument;
else if (shadowRegisters.mme.shadowRamControl == type::MmeShadowRamControl::MethodReplay)
else if (shadowRegisters.mme->shadowRamControl == type::MmeShadowRamControl::MethodReplay)
argument = shadowRegisters.raw[method];
}
@ -135,7 +139,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
if (!redundant) {
switch (method) {
MAXWELL3D_STRUCT_CASE(mme, shadowRamControl, {
shadowRegisters.mme.shadowRamControl = shadowRamControl;
shadowRegisters.mme->shadowRamControl = shadowRamControl;
})
#define RENDER_TARGET_ARRAY(z, index, data) \
@ -220,43 +224,43 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
#undef SCISSOR_CALLBACKS
MAXWELL3D_CASE(renderTargetControl, {
context.UpdateRenderTargetControl(registers.renderTargetControl);
context.UpdateRenderTargetControl(renderTargetControl);
})
}
}
switch (method) {
MAXWELL3D_STRUCT_CASE(mme, instructionRamLoad, {
if (registers.mme.instructionRamPointer >= macroCode.size())
if (registers.mme->instructionRamPointer >= macroCode.size())
throw exception("Macro memory is full!");
macroCode[registers.mme.instructionRamPointer++] = instructionRamLoad;
macroCode[registers.mme->instructionRamPointer++] = instructionRamLoad;
// Wraparound writes
registers.mme.instructionRamPointer %= macroCode.size();
registers.mme->instructionRamPointer %= macroCode.size();
})
MAXWELL3D_STRUCT_CASE(mme, startAddressRamLoad, {
if (registers.mme.startAddressRamPointer >= macroPositions.size())
if (registers.mme->startAddressRamPointer >= macroPositions.size())
throw exception("Maximum amount of macros reached!");
macroPositions[registers.mme.startAddressRamPointer++] = startAddressRamLoad;
macroPositions[registers.mme->startAddressRamPointer++] = startAddressRamLoad;
})
MAXWELL3D_CASE(syncpointAction, {
state.logger->Debug("Increment syncpoint: {}", +syncpointAction.id);
state.logger->Debug("Increment syncpoint: {}", static_cast<u16>(syncpointAction.id));
channelCtx.executor.Execute();
state.soc->host1x.syncpoints.at(syncpointAction.id).Increment();
})
MAXWELL3D_CASE(clearBuffers, {
context.ClearBuffers(registers.clearBuffers);
context.ClearBuffers(clearBuffers);
})
MAXWELL3D_STRUCT_CASE(semaphore, info, {
switch (info.op) {
case type::SemaphoreInfo::Op::Release:
WriteSemaphoreResult(registers.semaphore.payload);
WriteSemaphoreResult(registers.semaphore->payload);
break;
case type::SemaphoreInfo::Op::Counter: {
@ -306,9 +310,9 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
u64 timestamp;
};
switch (registers.semaphore.info.structureSize) {
switch (registers.semaphore->info.structureSize) {
case type::SemaphoreInfo::StructureSize::OneWord:
channelCtx.asCtx->gmmu.Write<u32>(registers.semaphore.address.Pack(), static_cast<u32>(result));
channelCtx.asCtx->gmmu.Write<u32>(registers.semaphore->address.Pack(), static_cast<u32>(result));
break;
case type::SemaphoreInfo::StructureSize::FourWords: {
@ -319,7 +323,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
i64 nsTime{util::GetTimeNs()};
i64 timestamp{(nsTime / NsToTickDenominator) * NsToTickNumerator + ((nsTime % NsToTickDenominator) * NsToTickNumerator) / NsToTickDenominator};
channelCtx.asCtx->gmmu.Write<FourWordResult>(registers.semaphore.address.Pack(),
channelCtx.asCtx->gmmu.Write<FourWordResult>(registers.semaphore->address.Pack(),
FourWordResult{result, static_cast<u64>(timestamp)});
break;
}

View File

@ -46,103 +46,85 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
union Registers {
std::array<u32, RegisterCount> raw;
struct {
u32 _pad0_[0x40]; // 0x0
u32 noOperation; // 0x40
u32 _pad1_[0x3]; // 0x41
u32 waitForIdle; // 0x44
template<size_t Offset, typename Type>
using Register = util::OffsetMember<Offset, Type, u32>;
struct {
Register<0x40, u32> noOperation;
Register<0x44, u32> waitForIdle;
struct MME {
u32 instructionRamPointer; // 0x45
u32 instructionRamLoad; // 0x46
u32 startAddressRamPointer; // 0x47
u32 startAddressRamLoad; // 0x48
type::MmeShadowRamControl shadowRamControl; // 0x49
} mme;
};
Register<0x45, MME> mme;
u32 _pad2_[0x68]; // 0x4A
Register<0xB2, type::SyncpointAction> syncpointAction;
struct {
u16 id : 12;
u8 _pad0_ : 4;
bool flushCache : 1;
u8 _pad1_ : 3;
bool increment : 1;
u16 _pad2_ : 11;
} syncpointAction; // 0xB2
Register<0xDF, u32> rasterizerEnable;
Register<0x200, std::array<type::RenderTarget, type::RenderTargetCount>> renderTargets;
Register<0x280, std::array<type::ViewportTransform, type::ViewportCount>> viewportTransforms;
Register<0x300, std::array<type::Viewport, type::ViewportCount>> viewports;
u32 _pad3_[0x2C]; // 0xB3
u32 rasterizerEnable; // 0xDF
u32 _pad4_[0x120]; // 0xE0
std::array<type::RenderTarget, type::RenderTargetCount> renderTargets; // 0x200
std::array<type::ViewportTransform, type::ViewportCount> viewportTransforms; // 0x280
std::array<type::Viewport, type::ViewportCount> viewports; // 0x300
u32 _pad5_[0x20]; // 0x340
Register<0x360, std::array<u32, 4>> clearColorValue;
Register<0x364, u32> clearDepthValue;
std::array<u32, 4> clearColorValue; // 0x360
u32 clearDepthValue; // 0x364
u32 _pad5_1_[0x6]; // 0x365
struct {
struct PolygonMode {
type::PolygonMode front; // 0x36B
type::PolygonMode back; // 0x36C
} polygonMode;
};
Register<0x36B, PolygonMode> polygonMode;
u32 _pad6_[0x13]; // 0x36D
std::array<type::Scissor, type::ViewportCount> scissors; // 0x380
u32 _pad6_1_[0x15]; // 0x3C0
Register<0x380, std::array<type::Scissor, type::ViewportCount>> scissors;
struct {
struct StencilBackExtra {
u32 compareRef; // 0x3D5
u32 writeMask; // 0x3D6
u32 compareMask; // 0x3D7
} stencilBackExtra;
};
Register<0x3D5, StencilBackExtra> stencilBackExtra;
u32 tiledCacheEnable; // 0x3D8
struct {
Register<0x3D8, u32> tiledCacheEnable;
struct TiledCacheSize {
u16 width;
u16 height;
} tiledCacheSize; // 0x3D9
};
Register<0x3D9, TiledCacheSize> tiledCacheSize;
u32 _pad7_[0x11]; // 0x3DA
u32 rtSeparateFragData; // 0x3EB
u32 _pad8_[0x6C]; // 0x3EC
std::array<type::VertexAttribute, 0x20> vertexAttributeState; // 0x458
u32 _pad9_[0xF]; // 0x478
type::RenderTargetControl renderTargetControl; // 0x487
u32 _pad9_1_[0x3B]; // 0x488
type::CompareOp depthTestFunc; // 0x4C3
float alphaTestRef; // 0x4C4
type::CompareOp alphaTestFunc; // 0x4C5
u32 drawTFBStride; // 0x4C6
Register<0x3EB, u32> rtSeparateFragData;
Register<0x458, std::array<type::VertexAttribute, 0x20>> vertexAttributeState;
Register<0x487, type::RenderTargetControl> renderTargetControl;
Register<0x4C3, type::CompareOp> depthTestFunc;
Register<0x4C4, float> alphaTestRef;
Register<0x4C5, type::CompareOp> alphaTestFunc;
Register<0x4C6, u32> drawTFBStride;
struct {
struct BlendConstant {
float r; // 0x4C7
float g; // 0x4C8
float b; // 0x4C9
float a; // 0x4CA
} blendConstant;
};
Register<0x4C7, BlendConstant> blendConstant;
u32 _pad10_[0x4]; // 0x4CB
struct {
struct BlendState {
u32 seperateAlpha; // 0x4CF
type::Blend::Op colorOp; // 0x4D0
type::Blend::Factor colorSrcFactor; // 0x4D1
type::Blend::Factor colorDestFactor; // 0x4D2
type::Blend::Op alphaOp; // 0x4D3
type::Blend::Factor alphaSrcFactor; // 0x4D4
u32 _pad_; // 0x4D5
type::Blend::Factor alphaDestFactor; // 0x4D6
u32 enableCommon; // 0x4D7
std::array<u32, 8> enable; // 0x4D8 For each render target
} blend;
};
Register<0x4CF, BlendState> blendState;
u32 stencilEnable; // 0x4E0
struct {
Register<0x4E0, u32> stencilEnable;
struct StencilFront {
type::StencilOp failOp; // 0x4E1
type::StencilOp zFailOp; // 0x4E2
type::StencilOp zPassOp; // 0x4E3
@ -154,96 +136,73 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
} compare;
u32 writeMask; // 0x4E7
} stencilFront;
};
Register<0x4E1, StencilFront> stencilFront;
u32 _pad11_[0x4]; // 0x4E8
float lineWidthSmooth; // 0x4EC
float lineWidthAliased; // 0x4D
u32 _pad12_[0x1F]; // 0x4EE
u32 drawBaseVertex; // 0x50D
u32 drawBaseInstance; // 0x50E
u32 _pad13_[0x35]; // 0x50F
u32 clipDistanceEnable; // 0x544
u32 sampleCounterEnable; // 0x545
float pointSpriteSize; // 0x546
u32 zCullStatCountersEnable; // 0x547
u32 pointSpriteEnable; // 0x548
u32 _pad14_; // 0x549
u32 shaderExceptions; // 0x54A
u32 _pad15_[0x2]; // 0x54B
u32 multisampleEnable; // 0x54D
u32 depthTargetEnable; // 0x54E
Register<0x4EC, float> lineWidthSmooth;
Register<0x4D, float> lineWidthAliased;
struct {
bool alphaToCoverage : 1;
u8 _pad0_ : 3;
bool alphaToOne : 1;
u32 _pad1_ : 27;
} multisampleControl; // 0x54F
Register<0x50D, u32> drawBaseVertex;
Register<0x50E, u32> drawBaseInstance;
u32 _pad16_[0x7]; // 0x550
Register<0x544, u32> clipDistanceEnable;
Register<0x545, u32> sampleCounterEnable;
Register<0x546, float> pointSpriteSize;
Register<0x547, u32> zCullStatCountersEnable;
Register<0x548, u32> pointSpriteEnable;
Register<0x54A, u32> shaderExceptions;
Register<0x54D, u32> multisampleEnable;
Register<0x54E, u32> depthTargetEnable;
struct {
Register<0x54F, type::MultisampleControl> multisampleControl;
struct SamplerPool {
type::Address address; // 0x557
u32 maximumIndex; // 0x559
} texSamplerPool;
};
Register<0x557, SamplerPool> samplerPool;
u32 _pad17_; // 0x55A
u32 polygonOffsetFactor; // 0x55B
u32 lineSmoothEnable; // 0x55C
Register<0x55B, u32> polygonOffsetFactor;
Register<0x55C, u32> lineSmoothEnable;
struct {
struct TexturePool {
type::Address address; // 0x55D
u32 maximumIndex; // 0x55F
} texHeaderPool;
};
Register<0x55D, TexturePool> texturePool;
u32 _pad18_[0x5]; // 0x560
u32 stencilTwoSideEnable; // 0x565
Register<0x565, u32> stencilTwoSideEnable;
struct {
struct StencilBack {
type::StencilOp failOp; // 0x566
type::StencilOp zFailOp; // 0x567
type::StencilOp zPassOp; // 0x568
type::CompareOp compareOp; // 0x569
} stencilBack;
};
Register<0x566, StencilBack> stencilBack;
u32 _pad19_[0x17]; // 0x56A
Register<0x581, type::PointCoordReplace> pointCoordReplace;
struct {
u8 _unk_ : 2;
type::CoordOrigin origin : 1;
u16 enable : 10;
u32 _pad_ : 19;
} pointCoordReplace; // 0x581
Register<0x646, u32> cullFaceEnable;
Register<0x647, type::FrontFace> frontFace;
Register<0x648, type::CullFace> cullFace;
Register<0x649, u32> pixelCentreImage;
Register<0x64B, u32> viewportTransformEnable;
Register<0x674, type::ClearBuffers> clearBuffers;
Register<0x680, std::array<type::ColorWriteMask, type::RenderTargetCount>> colorMask;
u32 _pad20_[0xC4]; // 0x582
u32 cullFaceEnable; // 0x646
type::FrontFace frontFace; // 0x647
type::CullFace cullFace; // 0x648
u32 pixelCentreImage; // 0x649
u32 _pad21_; // 0x64A
u32 viewportTransformEnable; // 0x64B
u32 _pad22_[0x28]; // 0x64C
type::ClearBuffers clearBuffers; // 0x674
u32 _pad22_1_[0xB]; // 0x675
std::array<type::ColorWriteMask, type::RenderTargetCount> colorMask; // 0x680
u32 _pad23_[0x38]; // 0x688
struct {
struct Semaphore {
type::Address address; // 0x6C0
u32 payload; // 0x6C2
type::SemaphoreInfo info; // 0x6C3
} semaphore;
u32 _pad24_[0xBC]; // 0x6C4
std::array<type::Blend, type::RenderTargetCount> independentBlend; // 0x780
u32 _pad25_[0x100]; // 0x7C0
u32 firmwareCall[0x20]; // 0x8C0
};
Register<0x6C0, Semaphore> semaphore;
Register<0x780, std::array<type::Blend, type::RenderTargetCount>> independentBlend;
Register<0x8C0, u32[0x20]> firmwareCall;
};
static_assert(sizeof(Registers) == (RegisterCount * sizeof(u32)));
static_assert(U32_OFFSET(Registers, firmwareCall) == 0x8C0);
#pragma pack(pop)
Registers registers{};