small build fix in debug mode, misc cleanup

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@1604 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
hrydgard
2008-12-20 11:20:47 +00:00
parent 4f4edc05a0
commit f0bb8f430a
9 changed files with 459 additions and 435 deletions

View File

@ -189,6 +189,7 @@ namespace CPUCompare
jo.fpAccurateFlags = true; jo.fpAccurateFlags = true;
jo.optimizeGatherPipe = true; jo.optimizeGatherPipe = true;
jo.fastInterrupts = false; jo.fastInterrupts = false;
jo.accurateSinglePrecision = true;
gpr.SetEmitter(this); gpr.SetEmitter(this);
fpr.SetEmitter(this); fpr.SetEmitter(this);
@ -328,6 +329,9 @@ namespace CPUCompare
if (emaddress == 0) if (emaddress == 0)
PanicAlert("ERROR : Trying to compile at 0. LR=%08x", LR); PanicAlert("ERROR : Trying to compile at 0. LR=%08x", LR);
// if (emaddress == 0x800aa278)
// DebugBreak();
int size; int size;
js.isLastInstruction = false; js.isLastInstruction = false;
js.blockStart = emaddress; js.blockStart = emaddress;
@ -433,6 +437,7 @@ namespace CPUCompare
CALL(thunks.ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0)); CALL(thunks.ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0));
} }
if (!ops[i].skip)
PPCTables::CompileInstruction(ops[i].inst); PPCTables::CompileInstruction(ops[i].inst);
gpr.SanityCheck(); gpr.SanityCheck();

View File

@ -19,6 +19,14 @@
// See comments in Jit.cpp. // See comments in Jit.cpp.
// ======================== // ========================
// Mystery: Capcom vs SNK 800aa278
// CR flags approach:
// * Store that "N+Z flag contains CR0" or "S+Z flag contains CR3".
// * All flag altering instructions flush this
// * A flush simply does a conditional write to the appropriate CRx.
// * If flag available, branch code can become absolutely trivial.
#ifndef _JIT_H #ifndef _JIT_H
#define _JIT_H #define _JIT_H
@ -128,6 +136,7 @@ private:
bool enableFastMem; bool enableFastMem;
bool optimizeGatherPipe; bool optimizeGatherPipe;
bool fastInterrupts; bool fastInterrupts;
bool accurateSinglePrecision;
}; };

View File

@ -70,7 +70,6 @@ using namespace Gen;
{ {
LOG(DYNA_REC, "JIT Statistics ======================="); LOG(DYNA_REC, "JIT Statistics =======================");
LOG(DYNA_REC, "Number of blocks currently: %i", numBlocks); LOG(DYNA_REC, "Number of blocks currently: %i", numBlocks);
LOG(DYNA_REC, "Code cache size: %i b", GetCodePtr() - codeCache);
LOG(DYNA_REC, "======================================"); LOG(DYNA_REC, "======================================");
} }

View File

@ -14,6 +14,7 @@
// Official SVN repository and contact information can be found at // Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/ // http://code.google.com/p/dolphin-emu/
#include "../PowerPC.h" #include "../PowerPC.h"
#include "../PPCTables.h" #include "../PPCTables.h"
#include "../PPCAnalyst.h" #include "../PPCAnalyst.h"
@ -22,14 +23,11 @@
#include "JitAsm.h" #include "JitAsm.h"
#include "JitRegCache.h" #include "JitRegCache.h"
using namespace Gen; using namespace Gen;
using namespace PowerPC; using namespace PowerPC;
void RegCache::Start(PPCAnalyst::BlockRegStats &stats)
{
void RegCache::Start(PPCAnalyst::BlockRegStats &stats)
{
for (int i = 0; i < NUMXREGS; i++) for (int i = 0; i < NUMXREGS; i++)
{ {
xregs[i].free = true; xregs[i].free = true;
@ -57,20 +55,20 @@ using namespace PowerPC;
}*/ }*/
//Find top regs - preload them (load bursts ain't bad) //Find top regs - preload them (load bursts ain't bad)
//But only preload IF written OR reads >= 3 //But only preload IF written OR reads >= 3
} }
// these are powerpc reg indices // these are powerpc reg indices
void RegCache::Lock(int p1, int p2, int p3, int p4) void RegCache::Lock(int p1, int p2, int p3, int p4)
{ {
locks[p1] = true; locks[p1] = true;
if (p2 != 0xFF) locks[p2] = true; if (p2 != 0xFF) locks[p2] = true;
if (p3 != 0xFF) locks[p3] = true; if (p3 != 0xFF) locks[p3] = true;
if (p4 != 0xFF) locks[p4] = true; if (p4 != 0xFF) locks[p4] = true;
} }
// these are x64 reg indices // these are x64 reg indices
void RegCache::LockX(int x1, int x2, int x3, int x4) void RegCache::LockX(int x1, int x2, int x3, int x4)
{ {
if (xlocks[x1]) { if (xlocks[x1]) {
PanicAlert("RegCache: x %i already locked!"); PanicAlert("RegCache: x %i already locked!");
} }
@ -78,27 +76,27 @@ using namespace PowerPC;
if (x2 != 0xFF) xlocks[x2] = true; if (x2 != 0xFF) xlocks[x2] = true;
if (x3 != 0xFF) xlocks[x3] = true; if (x3 != 0xFF) xlocks[x3] = true;
if (x4 != 0xFF) xlocks[x4] = true; if (x4 != 0xFF) xlocks[x4] = true;
} }
bool RegCache::IsFreeX(int xreg) const bool RegCache::IsFreeX(int xreg) const
{ {
return xregs[xreg].free && !xlocks[xreg]; return xregs[xreg].free && !xlocks[xreg];
} }
void RegCache::UnlockAll() void RegCache::UnlockAll()
{ {
for (int i = 0; i < 32; i++) for (int i = 0; i < 32; i++)
locks[i] = false; locks[i] = false;
} }
void RegCache::UnlockAllX() void RegCache::UnlockAllX()
{ {
for (int i = 0; i < NUMXREGS; i++) for (int i = 0; i < NUMXREGS; i++)
xlocks[i] = false; xlocks[i] = false;
} }
X64Reg RegCache::GetFreeXReg() X64Reg RegCache::GetFreeXReg()
{ {
int aCount; int aCount;
const int *aOrder = GetAllocationOrder(aCount); const int *aOrder = GetAllocationOrder(aCount);
for (int i = 0; i < aCount; i++) for (int i = 0; i < aCount; i++)
@ -127,36 +125,36 @@ using namespace PowerPC;
//Still no dice? Die! //Still no dice? Die!
_assert_msg_(DYNA_REC, 0, "Regcache ran out of regs"); _assert_msg_(DYNA_REC, 0, "Regcache ran out of regs");
return (X64Reg) -1; return (X64Reg) -1;
} }
void RegCache::SaveState() void RegCache::SaveState()
{ {
memcpy(saved_locks, locks, sizeof(locks)); memcpy(saved_locks, locks, sizeof(locks));
memcpy(saved_xlocks, xlocks, sizeof(xlocks)); memcpy(saved_xlocks, xlocks, sizeof(xlocks));
memcpy(saved_regs, regs, sizeof(regs)); memcpy(saved_regs, regs, sizeof(regs));
memcpy(saved_xregs, xregs, sizeof(xregs)); memcpy(saved_xregs, xregs, sizeof(xregs));
} }
void RegCache::LoadState() void RegCache::LoadState()
{ {
memcpy(xlocks, saved_xlocks, sizeof(xlocks)); memcpy(xlocks, saved_xlocks, sizeof(xlocks));
memcpy(locks, saved_locks, sizeof(locks)); memcpy(locks, saved_locks, sizeof(locks));
memcpy(regs, saved_regs, sizeof(regs)); memcpy(regs, saved_regs, sizeof(regs));
memcpy(xregs, saved_xregs, sizeof(xregs)); memcpy(xregs, saved_xregs, sizeof(xregs));
} }
void RegCache::FlushR(X64Reg reg) void RegCache::FlushR(X64Reg reg)
{ {
if (reg >= NUMXREGS) if (reg >= NUMXREGS)
PanicAlert("Flushing non existent reg"); PanicAlert("Flushing non existent reg");
if (!xregs[reg].free) if (!xregs[reg].free)
{ {
StoreFromX64(xregs[reg].ppcReg); StoreFromX64(xregs[reg].ppcReg);
} }
} }
void RegCache::SanityCheck() const void RegCache::SanityCheck() const
{ {
for (int i = 0; i < 32; i++) { for (int i = 0; i < 32; i++) {
if (regs[i].away) { if (regs[i].away) {
if (regs[i].location.IsSimpleReg()) { if (regs[i].location.IsSimpleReg()) {
@ -170,38 +168,38 @@ using namespace PowerPC;
} }
} }
} }
} }
void RegCache::DiscardRegContentsIfCached(int preg) void RegCache::DiscardRegContentsIfCached(int preg)
{ {
if (regs[preg].away && regs[preg].location.IsSimpleReg()) if (regs[preg].away && regs[preg].location.IsSimpleReg())
{ {
xregs[regs[preg].location.GetSimpleReg()].free = true; xregs[regs[preg].location.GetSimpleReg()].free = true;
xregs[regs[preg].location.GetSimpleReg()].dirty = false; xregs[regs[preg].location.GetSimpleReg()].dirty = false;
regs[preg].away = false; regs[preg].away = false;
} }
} }
void GPRRegCache::SetImmediate32(int preg, u32 immValue) void GPRRegCache::SetImmediate32(int preg, u32 immValue)
{ {
DiscardRegContentsIfCached(preg); DiscardRegContentsIfCached(preg);
regs[preg].away = true; regs[preg].away = true;
regs[preg].location = Imm32(immValue); regs[preg].location = Imm32(immValue);
} }
void GPRRegCache::Start(PPCAnalyst::BlockRegStats &stats) void GPRRegCache::Start(PPCAnalyst::BlockRegStats &stats)
{ {
RegCache::Start(stats); RegCache::Start(stats);
} }
void FPURegCache::Start(PPCAnalyst::BlockRegStats &stats) void FPURegCache::Start(PPCAnalyst::BlockRegStats &stats)
{ {
RegCache::Start(stats); RegCache::Start(stats);
} }
const int *GPRRegCache::GetAllocationOrder(int &count) const int *GPRRegCache::GetAllocationOrder(int &count)
{ {
static const int allocationOrder[] = static const int allocationOrder[] =
{ {
#ifdef _M_X64 #ifdef _M_X64
@ -216,10 +214,10 @@ using namespace PowerPC;
}; };
count = sizeof(allocationOrder) / sizeof(const int); count = sizeof(allocationOrder) / sizeof(const int);
return allocationOrder; return allocationOrder;
} }
const int *FPURegCache::GetAllocationOrder(int &count) const int *FPURegCache::GetAllocationOrder(int &count)
{ {
static const int allocationOrder[] = static const int allocationOrder[] =
{ {
#ifdef _M_X64 #ifdef _M_X64
@ -230,28 +228,28 @@ using namespace PowerPC;
}; };
count = sizeof(allocationOrder) / sizeof(int); count = sizeof(allocationOrder) / sizeof(int);
return allocationOrder; return allocationOrder;
} }
OpArg GPRRegCache::GetDefaultLocation(int reg) const OpArg GPRRegCache::GetDefaultLocation(int reg) const
{ {
return M(&ppcState.gpr[reg]); return M(&ppcState.gpr[reg]);
} }
OpArg FPURegCache::GetDefaultLocation(int reg) const OpArg FPURegCache::GetDefaultLocation(int reg) const
{ {
return M(&ppcState.ps[reg][0]); return M(&ppcState.ps[reg][0]);
} }
void RegCache::KillImmediate(int preg) void RegCache::KillImmediate(int preg)
{ {
if (regs[preg].away && regs[preg].location.IsImm()) if (regs[preg].away && regs[preg].location.IsImm())
{ {
LoadToX64(preg, true, true); LoadToX64(preg, true, true);
} }
} }
void GPRRegCache::LoadToX64(int i, bool doLoad, bool makeDirty) void GPRRegCache::LoadToX64(int i, bool doLoad, bool makeDirty)
{ {
if (!regs[i].away && regs[i].location.IsImm()) if (!regs[i].away && regs[i].location.IsImm())
PanicAlert("Bad immedaite"); PanicAlert("Bad immedaite");
@ -285,10 +283,10 @@ using namespace PowerPC;
if (xlocks[RX(i)]) { if (xlocks[RX(i)]) {
PanicAlert("Seriously WTF, this reg should have been flushed"); PanicAlert("Seriously WTF, this reg should have been flushed");
} }
} }
void GPRRegCache::StoreFromX64(int i) void GPRRegCache::StoreFromX64(int i)
{ {
if (regs[i].away) if (regs[i].away)
{ {
bool doStore; bool doStore;
@ -311,10 +309,10 @@ using namespace PowerPC;
regs[i].location = newLoc; regs[i].location = newLoc;
regs[i].away = false; regs[i].away = false;
} }
} }
void FPURegCache::LoadToX64(int i, bool doLoad, bool makeDirty) void FPURegCache::LoadToX64(int i, bool doLoad, bool makeDirty)
{ {
_assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - load - imm"); _assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - load - imm");
if (!regs[i].away) if (!regs[i].away)
{ {
@ -339,10 +337,10 @@ using namespace PowerPC;
// There are no immediates in the FPR reg file, so we already had this in a register. Make dirty as necessary. // There are no immediates in the FPR reg file, so we already had this in a register. Make dirty as necessary.
xregs[RX(i)].dirty |= makeDirty; xregs[RX(i)].dirty |= makeDirty;
} }
} }
void FPURegCache::StoreFromX64(int i) void FPURegCache::StoreFromX64(int i)
{ {
_assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - store - imm"); _assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - store - imm");
if (regs[i].away) if (regs[i].away)
{ {
@ -360,10 +358,10 @@ using namespace PowerPC;
{ {
// _assert_msg_(DYNA_REC,0,"already stored"); // _assert_msg_(DYNA_REC,0,"already stored");
} }
} }
void RegCache::Flush(FlushMode mode) void RegCache::Flush(FlushMode mode)
{ {
for (int i = 0; i < NUMXREGS; i++) { for (int i = 0; i < NUMXREGS; i++) {
if (xlocks[i]) if (xlocks[i])
PanicAlert("Somone forgot to unlock X64 reg %i.", i); PanicAlert("Somone forgot to unlock X64 reg %i.", i);
@ -392,4 +390,4 @@ using namespace PowerPC;
} }
} }
} }
} }

View File

@ -20,34 +20,34 @@
#include "x64Emitter.h" #include "x64Emitter.h"
using namespace Gen; using namespace Gen;
enum FlushMode enum FlushMode
{ {
FLUSH_ALL FLUSH_ALL
}; };
enum GrabMode enum GrabMode
{ {
M_READ = 1, M_READ = 1,
M_WRITE = 2, M_WRITE = 2,
M_READWRITE = 3, M_READWRITE = 3,
}; };
struct PPCCachedReg struct PPCCachedReg
{ {
OpArg location; OpArg location;
bool away; // value not in source register bool away; // value not in source register
}; };
struct X64CachedReg struct X64CachedReg
{ {
int ppcReg; int ppcReg;
bool dirty; bool dirty;
bool free; bool free;
}; };
typedef int XReg; typedef int XReg;
typedef int PReg; typedef int PReg;
#ifdef _M_X64 #ifdef _M_X64
#define NUMXREGS 16 #define NUMXREGS 16
@ -55,14 +55,14 @@
#define NUMXREGS 8 #define NUMXREGS 8
#endif #endif
class RegCache class RegCache
{ {
private: private:
bool locks[32]; bool locks[32];
bool saved_locks[32]; bool saved_locks[32];
bool saved_xlocks[NUMXREGS]; bool saved_xlocks[NUMXREGS];
protected: protected:
bool xlocks[NUMXREGS]; bool xlocks[NUMXREGS];
PPCCachedReg regs[32]; PPCCachedReg regs[32];
X64CachedReg xregs[NUMXREGS]; X64CachedReg xregs[NUMXREGS];
@ -75,7 +75,7 @@
XEmitter *emit; XEmitter *emit;
public: public:
virtual ~RegCache() {} virtual ~RegCache() {}
virtual void Start(PPCAnalyst::BlockRegStats &stats) = 0; virtual void Start(PPCAnalyst::BlockRegStats &stats) = 0;
@ -123,29 +123,28 @@
void SaveState(); void SaveState();
void LoadState(); void LoadState();
}; };
class GPRRegCache : public RegCache class GPRRegCache : public RegCache
{ {
public: public:
void Start(PPCAnalyst::BlockRegStats &stats); void Start(PPCAnalyst::BlockRegStats &stats);
void LoadToX64(int preg, bool doLoad = true, bool makeDirty = true); void LoadToX64(int preg, bool doLoad = true, bool makeDirty = true);
void StoreFromX64(int preg); void StoreFromX64(int preg);
OpArg GetDefaultLocation(int reg) const; OpArg GetDefaultLocation(int reg) const;
const int *GetAllocationOrder(int &count); const int *GetAllocationOrder(int &count);
void SetImmediate32(int preg, u32 immValue); void SetImmediate32(int preg, u32 immValue);
}; };
class FPURegCache : public RegCache class FPURegCache : public RegCache
{ {
public: public:
void Start(PPCAnalyst::BlockRegStats &stats); void Start(PPCAnalyst::BlockRegStats &stats);
void LoadToX64(int preg, bool doLoad = true, bool makeDirty = true); void LoadToX64(int preg, bool doLoad = true, bool makeDirty = true);
void StoreFromX64(int preg); void StoreFromX64(int preg);
const int *GetAllocationOrder(int &count); const int *GetAllocationOrder(int &count);
OpArg GetDefaultLocation(int reg) const; OpArg GetDefaultLocation(int reg) const;
}; };
#endif #endif

View File

@ -202,6 +202,7 @@
if (!merge_branch) { if (!merge_branch) {
// Keep the normal code separate for clarity. // Keep the normal code separate for clarity.
CMP(32, gpr.R(a), comparand); CMP(32, gpr.R(a), comparand);
FixupBranch pLesser = J_CC(less_than); FixupBranch pLesser = J_CC(less_than);
FixupBranch pGreater = J_CC(greater_than); FixupBranch pGreater = J_CC(greater_than);
MOV(8, M(&PowerPC::ppcState.cr_fast[crf]), Imm8(0x2)); // _x86Reg == 0 MOV(8, M(&PowerPC::ppcState.cr_fast[crf]), Imm8(0x2)); // _x86Reg == 0
@ -216,42 +217,45 @@
} else { } else {
int test_bit = 8 >> (js.next_inst.BI & 3); int test_bit = 8 >> (js.next_inst.BI & 3);
bool condition = (js.next_inst.BO & 8) ? false : true; bool condition = (js.next_inst.BO & 8) ? false : true;
u32 destination;
if (js.next_inst.AA)
destination = SignExt16(js.next_inst.BD << 2);
else
destination = js.next_compilerPC + SignExt16(js.next_inst.BD << 2);
CMP(32, gpr.R(a), comparand); CMP(32, gpr.R(a), comparand);
gpr.UnlockAll(); gpr.UnlockAll();
u32 destination1;
if (js.next_inst.AA)
destination1 = SignExt16(js.next_inst.BD << 2);
else
destination1 = js.next_compilerPC + SignExt16(js.next_inst.BD << 2);
u32 destination2 = js.next_compilerPC + 4;
// Test swapping (in the future, will be used to inline across branches the right way)
// if (rand() & 1)
// std::swap(destination1, destination2), condition = !condition;
gpr.Flush(FLUSH_ALL); gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL); fpr.Flush(FLUSH_ALL);
FixupBranch pLesser = J_CC(less_than); FixupBranch pLesser = J_CC(less_than);
FixupBranch pGreater = J_CC(greater_than); FixupBranch pGreater = J_CC(greater_than);
MOV(8, M(&PowerPC::ppcState.cr_fast[crf]), Imm8(0x2)); // _x86Reg == 0 MOV(8, M(&PowerPC::ppcState.cr_fast[crf]), Imm8(0x2)); // == 0
FixupBranch continue1 = J(); FixupBranch continue1 = J();
SetJumpTarget(pGreater); SetJumpTarget(pGreater);
MOV(8, M(&PowerPC::ppcState.cr_fast[crf]), Imm8(0x4)); // _x86Reg > 0 MOV(8, M(&PowerPC::ppcState.cr_fast[crf]), Imm8(0x4)); // > 0
FixupBranch continue2 = J(); FixupBranch continue2 = J();
SetJumpTarget(pLesser); SetJumpTarget(pLesser);
MOV(8, M(&PowerPC::ppcState.cr_fast[crf]), Imm8(0x8)); // _x86Reg < 0 MOV(8, M(&PowerPC::ppcState.cr_fast[crf]), Imm8(0x8)); // < 0
FixupBranch continue3; FixupBranch continue3;
if (!!(8 & test_bit) == condition) continue3 = J(); if (!!(8 & test_bit) == condition) continue3 = J();
//if (!!(8 & test_bit) != condition) SetJumpTarget(continue3);
if (!!(4 & test_bit) != condition) SetJumpTarget(continue2); if (!!(4 & test_bit) != condition) SetJumpTarget(continue2);
if (!!(2 & test_bit) != condition) SetJumpTarget(continue1); if (!!(2 & test_bit) != condition) SetJumpTarget(continue1);
WriteExit(destination, 0); WriteExit(destination1, 0);
if (!!(8 & test_bit) == condition) SetJumpTarget(continue3); if (!!(8 & test_bit) == condition) SetJumpTarget(continue3);
if (!!(4 & test_bit) == condition) SetJumpTarget(continue2); if (!!(4 & test_bit) == condition) SetJumpTarget(continue2);
if (!!(2 & test_bit) == condition) SetJumpTarget(continue1); if (!!(2 & test_bit) == condition) SetJumpTarget(continue1);
WriteExit(js.next_compilerPC + 4, 1); WriteExit(destination2, 1);
js.cancel = true; js.cancel = true;
} }

View File

@ -139,11 +139,18 @@ void Jit64::WriteFloatToConstRamAddress(const Gen::X64Reg& xmm_reg, u32 address)
void Jit64::ForceSinglePrecisionS(X64Reg xmm) { void Jit64::ForceSinglePrecisionS(X64Reg xmm) {
// Most games don't need these. Zelda requires it though - some platforms get stuck without them. // Most games don't need these. Zelda requires it though - some platforms get stuck without them.
if (jo.accurateSinglePrecision)
{
CVTSD2SS(xmm, R(xmm)); CVTSD2SS(xmm, R(xmm));
CVTSS2SD(xmm, R(xmm)); CVTSS2SD(xmm, R(xmm));
}
} }
void Jit64::ForceSinglePrecisionP(X64Reg xmm) { void Jit64::ForceSinglePrecisionP(X64Reg xmm) {
// Most games don't need these. Zelda requires it though - some platforms get stuck without them. // Most games don't need these. Zelda requires it though - some platforms get stuck without them.
if (jo.accurateSinglePrecision)
{
CVTPD2PS(xmm, R(xmm)); CVTPD2PS(xmm, R(xmm));
CVTPS2PD(xmm, R(xmm)); CVTPS2PD(xmm, R(xmm));
}
} }

View File

@ -313,6 +313,7 @@ bool Flatten(u32 address, int *realsize, BlockStats *st, BlockRegStats *gpa, Blo
code[i].inst = inst; code[i].inst = inst;
code[i].branchTo = -1; code[i].branchTo = -1;
code[i].branchToIndex = -1; code[i].branchToIndex = -1;
code[i].skip = false;
GekkoOPInfo *opinfo = GetOpInfo(inst); GekkoOPInfo *opinfo = GetOpInfo(inst);
if (opinfo) if (opinfo)
numCycles += opinfo->numCyclesMinusOne + 1; numCycles += opinfo->numCyclesMinusOne + 1;
@ -345,6 +346,7 @@ bool Flatten(u32 address, int *realsize, BlockStats *st, BlockRegStats *gpa, Blo
} }
else else
{ {
code[i].skip = true;
address = destination; address = destination;
} }
} }

View File

@ -49,6 +49,7 @@ struct CodeOp //16B
bool outputCR0; bool outputCR0;
bool outputCR1; bool outputCR1;
bool outputPS1; bool outputPS1;
bool skip; // followed BL-s for example
}; };
struct BlockStats struct BlockStats