static bool
disassembleJump(State &state, shadir::CfInstruction *inst)
{
   if (inst->id == SQ_CF_INST_CALL && inst->callCount) {
      state.out << " CALL_COUNT(" << inst->callCount << ")";
   }

   disassembleCondition(state, inst);

   if (inst->popCount) {
      state.out << " POP_COUNT(" << inst->popCount << ")";
   }

   if (inst->id == SQ_CF_INST_CALL || inst->id == SQ_CF_INST_ELSE || inst->id == SQ_CF_INST_JUMP) {
      state.out << " ADDR(" << inst->addr << ")";
   }

   if (inst->validPixelMode) {
      state.out << " VALID_PIX";
   }

   if (!inst->barrier) {
      state.out << " NO_BARRIER";
   }

   state.out << '\n';
   return true;
}
static void
disassembleJump(fmt::MemoryWriter &out, const ControlFlowInst &inst)
{
   auto id = inst.word1.CF_INST();

   if (id == SQ_CF_INST_CALL && inst.word1.CALL_COUNT()) {
      out << " CALL_COUNT(" << inst.word1.CALL_COUNT() << ")";
   }

   disassembleCondition(out, inst);

   if (inst.word1.POP_COUNT()) {
      out << " POP_COUNT(" << inst.word1.POP_COUNT() << ")";
   }

   if (id == SQ_CF_INST_CALL || id == SQ_CF_INST_ELSE || id == SQ_CF_INST_JUMP) {
      out << " ADDR(" << inst.word0.ADDR << ")";
   }

   if (inst.word1.VALID_PIXEL_MODE()) {
      out << " VALID_PIX";
   }

   if (!inst.word1.BARRIER()) {
      out << " NO_BARRIER";
   }
}
void
disassembleCfVTX(fmt::MemoryWriter &out, const ControlFlowInst &inst)
{
   auto addr = inst.word0.ADDR;
   auto count = (inst.word1.COUNT() + 1) | (inst.word1.COUNT_3() << 3);

   out
      << " ADDR(" << addr << ")"
      << " CNT(" << count << ")";

   if (!inst.word1.BARRIER()) {
      out << " NO_BARRIER";
   }

   disassembleCondition(out, inst);
}
static void
disassembleLoop(fmt::MemoryWriter &out, const ControlFlowInst &inst)
{
   disassembleCondition(out, inst);

   switch (inst.word1.CF_INST()) {
   case SQ_CF_INST_LOOP_START:
   case SQ_CF_INST_LOOP_END:
      if (!inst.word1.COND()) {
         // If .COND is set - disassembleCondition will print CF_CONST
         out << " CF_CONST(" << inst.word1.CF_CONST() << ")";
      }
   }

   switch (inst.word1.CF_INST()) {
   case SQ_CF_INST_LOOP_START:
   case SQ_CF_INST_LOOP_START_DX10:
   case SQ_CF_INST_LOOP_START_NO_AL:
      out << " FAIL_JUMP_ADDR(" << inst.word0.ADDR << ")";
      break;
   case SQ_CF_INST_LOOP_CONTINUE:
   case SQ_CF_INST_LOOP_BREAK:
      out << " ADDR(" << inst.word0.ADDR << ")";
      break;
   case SQ_CF_INST_LOOP_END:
      out << " PASS_JUMP_ADDR(" << inst.word0.ADDR << ")";
      break;
   default:
      out << " UNKNOWN_LOOP_CF_INST";
   }

   if (inst.word1.POP_COUNT()) {
      out << " POP_COUNT(" << inst.word1.POP_COUNT() << ")";
   }

   if (inst.word1.VALID_PIXEL_MODE()) {
      out << " VALID_PIX";
   }

   if (!inst.word1.BARRIER()) {
      out << " NO_BARRIER";
   }
}
bool
disassembleVTX(State &state, shadir::CfInstruction *inst)
{
   state.out
      << " ADDR(" << inst->addr << ")"
      << " CNT(" << inst->clause.size() << ")";

   if (!inst->barrier) {
      state.out << " NO_BARRIER";
   }

   disassembleCondition(state, inst);

   state.out << '\n';
   increaseIndent(state);

   for (auto &child : inst->clause) {
      // TODO: Disassemble VTX clauses
   }

   decreaseIndent(state);
   return true;
}
void
disassembleCF(fmt::MemoryWriter &out, const ControlFlowInst &inst)
{
   auto id = inst.word1.CF_INST();
   auto name = getInstructionName(id);
   out << name;

   switch (id) {
   case SQ_CF_INST_TEX:
      disassembleCfTEX(out, inst);
      break;
   case SQ_CF_INST_VTX:
   case SQ_CF_INST_VTX_TC:
      disassembleCfVTX(out, inst);
      break;
   case SQ_CF_INST_LOOP_START:
   case SQ_CF_INST_LOOP_START_DX10:
   case SQ_CF_INST_LOOP_START_NO_AL:
   case SQ_CF_INST_LOOP_END:
   case SQ_CF_INST_LOOP_CONTINUE:
   case SQ_CF_INST_LOOP_BREAK:
      disassembleLoop(out, inst);
      break;
   case SQ_CF_INST_JUMP:
   case SQ_CF_INST_ELSE:
   case SQ_CF_INST_CALL:
   case SQ_CF_INST_CALL_FS:
   case SQ_CF_INST_RETURN:
   case SQ_CF_INST_POP_JUMP:
      disassembleJump(out, inst);
      break;
   case SQ_CF_INST_EMIT_VERTEX:
   case SQ_CF_INST_EMIT_CUT_VERTEX:
   case SQ_CF_INST_CUT_VERTEX:
      if (!inst.word1.BARRIER()) {
         out << " NO_BARRIER";
      }
      break;
   case SQ_CF_INST_PUSH:
   case SQ_CF_INST_PUSH_ELSE:
   case SQ_CF_INST_KILL:
      disassembleCondition(out, inst);
      // switch case pass through
   case SQ_CF_INST_POP:
   case SQ_CF_INST_POP_PUSH:
   case SQ_CF_INST_POP_PUSH_ELSE:
      if (inst.word1.POP_COUNT()) {
         out << " POP_COUNT(" << inst.word1.POP_COUNT() << ")";
      }

      if (inst.word1.VALID_PIXEL_MODE()) {
         out << " VALID_PIX";
      }
      break;
   case SQ_CF_INST_END_PROGRAM:
   case SQ_CF_INST_NOP:
      break;
   case SQ_CF_INST_WAIT_ACK:
   case SQ_CF_INST_TEX_ACK:
   case SQ_CF_INST_VTX_ACK:
   case SQ_CF_INST_VTX_TC_ACK:
   default:
      out << " UNK_FORMAT";
      break;
   }
}
bool
disassembleControlFlow(State &state, shadir::CfInstruction *inst)
{
   state.out.write("{}{:02} {}", state.indent, inst->cfPC, inst->name);

   switch (inst->id) {
   case SQ_CF_INST_TEX:
      return disassembleTEX(state, inst);
   case SQ_CF_INST_VTX:
   case SQ_CF_INST_VTX_TC:
      return disassembleVTX(state, inst);
   case SQ_CF_INST_LOOP_START:
   case SQ_CF_INST_LOOP_START_DX10:
   case SQ_CF_INST_LOOP_START_NO_AL:
   case SQ_CF_INST_LOOP_END:
   case SQ_CF_INST_LOOP_CONTINUE:
   case SQ_CF_INST_LOOP_BREAK:
      return disassembleLoop(state, inst);
   case SQ_CF_INST_JUMP:
   case SQ_CF_INST_ELSE:
   case SQ_CF_INST_CALL:
   case SQ_CF_INST_CALL_FS:
   case SQ_CF_INST_RETURN:
   case SQ_CF_INST_POP_JUMP:
      return disassembleJump(state, inst);
   case SQ_CF_INST_EMIT_VERTEX:
   case SQ_CF_INST_EMIT_CUT_VERTEX:
   case SQ_CF_INST_CUT_VERTEX:
      if (!inst->barrier) {
         state.out << " NO_BARRIER";
      }

      state.out << '\n';
      break;
   case SQ_CF_INST_PUSH:
   case SQ_CF_INST_PUSH_ELSE:
   case SQ_CF_INST_KILL:
      disassembleCondition(state, inst);
      // pass through
   case SQ_CF_INST_POP:
   case SQ_CF_INST_POP_PUSH:
   case SQ_CF_INST_POP_PUSH_ELSE:
      if (inst->popCount) {
         state.out << " POP_COUNT(" << inst->popCount << ")";
      }

      if (inst->validPixelMode) {
         state.out << " VALID_PIX";
      }

      state.out << '\n';
      break;
   case SQ_CF_INST_END_PROGRAM:
   case SQ_CF_INST_NOP:
      state.out << '\n';
      break;
   case SQ_CF_INST_WAIT_ACK:
   case SQ_CF_INST_TEX_ACK:
   case SQ_CF_INST_VTX_ACK:
   case SQ_CF_INST_VTX_TC_ACK:
   default:
      state.out << " UNK_FORMAT\n";
      break;
   }

   return false;
}