// File: lukasz-wiecaszek[P]h264iframedecoder/picture_cabac.cpp
// Function: ymn::h264::picture_cabac::decode_residual_block(int*, ymn::h264::ctx_block_cat_e, int, unsigned char const*, int)
void picture_cabac::decode_residual_block(dctcoeff* block,
                                          const enum ctx_block_cat_e ctxBlockCat,
                                          const int idx,
                                          const uint8_t* scantable,
                                          const int max_coeff)
{
    /* Table 9-43 � Mapping of scanning position to ctxIdxInc for ctxBlockCat == 5, 9, or 13 */
    static const int significant_coeff_flag_offset_8x8[2][63] =
    {
        { 0, 1, 2, 3, 4, 5, 5, 4, 4, 3, 3, 4, 4, 4, 5, 5,
          4, 4, 4, 4, 3, 3, 6, 7, 7, 7, 8, 9,10, 9, 8, 7,
          7, 6,11,12,13,11, 6, 7, 8, 9,14,10, 9, 8, 6,11,
         12,13,11, 6, 9,14,10, 9,11,12,13,11,14,10,12 },
        { 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 7, 7, 7, 8, 4, 5,
          6, 9,10,10, 8,11,12,11, 9, 9,10,10, 8,11,12,11,
          9, 9,10,10, 8,11,12,11, 9, 9,10,10, 8,13,13, 9,
          9,10,10, 8,13,13, 9, 9,10,10,14,14,14,14,14 }
    };

    /* Table 9-43 � Mapping of scanning position to ctxIdxInc for ctxBlockCat == 5, 9, or 13 */
    static const int last_significant_coeff_flag_offset_8x8[63] =
    {
        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
        3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
        5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8
    };

    int i;
    int index[64];
    int coeff_count = 0;
    const bool is_dc = (idx >= (16 * CC_MAX));

    if (max_coeff != 64) {
        for(i = 0; i < max_coeff - 1; ++i) {
            if (decode_significant_coeff_flag(ctxBlockCat, i)) {
                index[coeff_count++] = i;
                if (decode_last_significant_coeff_flag(ctxBlockCat, i))
                    i = max_coeff;
            }
        }
    }
    else {
        const bool is_field_mb = m_context_variables.mb_field_decoding_flag;
        for(i = 0; i < max_coeff - 1; ++i) {
            if (decode_significant_coeff_flag(ctxBlockCat, significant_coeff_flag_offset_8x8[is_field_mb][i])) {
                index[coeff_count++] = i;
                if (decode_last_significant_coeff_flag(ctxBlockCat, last_significant_coeff_flag_offset_8x8[i]))
                    i = max_coeff;
            }
        }
    }
    if (i == max_coeff - 1)
        index[coeff_count++] = i;

    if (is_dc) {
        mb_cache& nzc_cache = m_context_variables.non_zero_count_cache[idx - 16 * CC_MAX];
        nzc_cache[0] = coeff_count;
    }
    else {
        mb_cache& nzc_cache = m_context_variables.non_zero_count_cache[idx / 16];
        const int cache_idx = mb_cache_idx[idx % 16];
        if (max_coeff == 64)
            mb_cache_fill_rectangle_2x2(nzc_cache, cache_idx, coeff_count);
        else
            nzc_cache[cache_idx] = coeff_count;
    }

    /* If binIdx is equal to 0, ctxIdxInc is derived by
       ctxIdxInc = ((numDecodAbsLevelGt1 != 0) ? 0: Min(4, 1 + numDecodAbsLevelEq1)) */
    static const int coeff_abs_level_bin_idx_eq0[8] =
    {
        1, 2, 3, 4, 0, 0, 0, 0
    };

    /* Otherwise (binIdx is greater than 0), ctxIdxInc is derived by
       ctxIdxInc = 5 + Min(4 - ((ctxBlockCat == 3) ? 1 : 0), numDecodAbsLevelGt1) */
    static const int coeff_abs_level_bin_idx_gt0[2][8] =
    {
        /* ctxBlockCat == 3 */
        { 5, 5, 5, 5, 6, 7, 8, 8 },
        /* ctxBlockCat != 3 */
        { 5, 5, 5, 5, 6, 7, 8, 9 },
    };

    static const int coeff_abs_level_transition[2][8] =
    {
        /* update node ctx after decoding a coeff_abs_level == 1 */
        { 1, 2, 3, 3, 4, 5, 6, 7 },
        /* update node ctx after decoding a coeff_abs_level > 1 */
        { 4, 4, 4, 4, 5, 6, 7, 7 }
    };

    for (int node = 0; coeff_count > 0; ) {
        int ctxIdxInc;
        int coeff_abs_level;
        int pos = scantable[index[--coeff_count]];

        ctxIdxInc = coeff_abs_level_bin_idx_eq0[node];
        if (decode_coeff_abs_level_minus1(ctxBlockCat, ctxIdxInc) == 0) {
            coeff_abs_level = 1;
        }
        else {
            coeff_abs_level = 2;
            ctxIdxInc = coeff_abs_level_bin_idx_gt0[ctxBlockCat == 3 ? 0 : 1][node];
            while (coeff_abs_level < 15 && decode_coeff_abs_level_minus1(ctxBlockCat, ctxIdxInc))
                coeff_abs_level++;

#if 0
            if (coeff_abs_level >= 15) {
                int j = 0;
                while (m_cabac_decoder.decode_bypass())
                    j++;

                coeff_abs_level = 1;
                while (j--)
                    coeff_abs_level += coeff_abs_level + m_cabac_decoder.decode_bypass();

                coeff_abs_level += 14;
            }
#endif
        }

        block[pos] = (m_cabac_decoder.decode_bypass() == 0) ? coeff_abs_level : -coeff_abs_level;
        node = coeff_abs_level_transition[coeff_abs_level == 1 ? 0 : 1][node];
    }
}

// Assembly:
subq $0x178, %rsp            # imm = 0x178
movq %rdi, 0x170(%rsp)
movq %rsi, 0x168(%rsp)
movl %edx, 0x164(%rsp)
movl %ecx, 0x160(%rsp)
movq %r8, 0x158(%rsp)
movl %r9d, 0x154(%rsp)
movq 0x170(%rsp), %rax
movq %rax, 0x18(%rsp)
movl $0x0, 0x4c(%rsp)
cmpl $0x30, 0x160(%rsp)
setge %al
andb $0x1, %al
movb %al, 0x4b(%rsp)
cmpl $0x40, 0x154(%rsp)
je 0x2154a
movl $0x0, 0x150(%rsp)
movl 0x150(%rsp), %eax
movl 0x154(%rsp), %ecx
subl $0x1, %ecx
cmpl %ecx, %eax
jge 0x21545
movq 0x18(%rsp), %rdi
movl 0x164(%rsp), %esi
movl 0x150(%rsp), %edx
callq 0x21360
cmpl $0x0, %eax
je 0x2152d
movq 0x18(%rsp), %rdi
movl 0x150(%rsp), %ecx
movl 0x4c(%rsp), %eax
movl %eax, %edx
addl $0x1, %edx
movl %edx, 0x4c(%rsp)
cltq
movl %ecx, 0x50(%rsp,%rax,4)
movl 0x164(%rsp), %esi
movl 0x150(%rsp), %edx
callq 0x213b0
cmpl $0x0, %eax
je 0x2152b
movl 0x154(%rsp), %eax
movl %eax, 0x150(%rsp)
jmp 0x2152d
jmp 0x2152f
movl 0x150(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x150(%rsp)
jmp 0x214b4
jmp 0x21625
movq 0x18(%rsp), %rax
movb 0x19(%rax), %al
andb $0x1, %al
movb %al, 0x4a(%rsp)
movl $0x0, 0x150(%rsp)
movl 0x150(%rsp), %eax
movl 0x154(%rsp), %ecx
subl $0x1, %ecx
cmpl %ecx, %eax
jge 0x21623
movq 0x18(%rsp), %rdi
movl 0x164(%rsp), %esi
movb 0x4a(%rsp), %al
andb $0x1, %al
movzbl %al, %eax
movl %eax, %ecx
leaq 0x73a6(%rip), %rax      # 0x28940
imulq $0xfc, %rcx, %rcx
addq %rcx, %rax
movslq 0x150(%rsp), %rcx
movl (%rax,%rcx,4), %edx
callq 0x21360
cmpl $0x0, %eax
je 0x2160b
movq 0x18(%rsp), %rdi
movl 0x150(%rsp), %ecx
movl 0x4c(%rsp), %eax
movl %eax, %edx
addl $0x1, %edx
movl %edx, 0x4c(%rsp)
cltq
movl %ecx, 0x50(%rsp,%rax,4)
movl 0x164(%rsp), %esi
movslq 0x150(%rsp), %rcx
leaq 0x7552(%rip), %rax      # 0x28b40
movl (%rax,%rcx,4), %edx
callq 0x213b0
cmpl $0x0, %eax
je 0x21609
movl 0x154(%rsp), %eax
movl %eax, 0x150(%rsp)
jmp 0x2160b
jmp 0x2160d
movl 0x150(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x150(%rsp)
jmp 0x21563
jmp 0x21625
movl 0x150(%rsp), %eax
movl 0x154(%rsp), %ecx
subl $0x1, %ecx
cmpl %ecx, %eax
jne 0x21654
movl 0x150(%rsp), %ecx
movl 0x4c(%rsp), %eax
movl %eax, %edx
addl $0x1, %edx
movl %edx, 0x4c(%rsp)
cltq
movl %ecx, 0x50(%rsp,%rax,4)
testb $0x1, 0x4b(%rsp)
je 0x216a2
movq 0x18(%rsp), %rax
addq $0x18, %rax
addq $0x68, %rax
movl 0x160(%rsp), %ecx
subl $0x30, %ecx
movslq %ecx, %rcx
imulq $0x28, %rcx, %rcx
addq %rcx, %rax
movq %rax, 0x40(%rsp)
movl 0x4c(%rsp), %eax
movb %al, 0x17(%rsp)
movq 0x40(%rsp), %rdi
xorl %eax, %eax
movl %eax, %esi
callq 0x22380
movb 0x17(%rsp), %cl
movb %cl, (%rax)
jmp 0x2173a
movq 0x18(%rsp), %rax
addq $0x18, %rax
addq $0x68, %rax
movq %rax, 0x8(%rsp)
movl 0x160(%rsp), %eax
movl $0x10, %ecx
cltd
idivl %ecx
movl %eax, %ecx
movq 0x8(%rsp), %rax
movslq %ecx, %rcx
imulq $0x28, %rcx, %rcx
addq %rcx, %rax
movq %rax, 0x38(%rsp)
movl 0x160(%rsp), %eax
movl $0x10, %ecx
cltd
idivl %ecx
movslq %edx, %rcx
leaq 0x77ce(%rip), %rax      # 0x28ec0
movzbl (%rax,%rcx), %eax
movl %eax, 0x34(%rsp)
cmpl $0x40, 0x154(%rsp)
jne 0x2171b
movq 0x38(%rsp), %rdi
movl 0x34(%rsp), %esi
movl 0x4c(%rsp), %eax
movzbl %al, %edx
callq 0x22420
jmp 0x21738
movl 0x4c(%rsp), %eax
movb %al, 0x7(%rsp)
movq 0x38(%rsp), %rdi
movslq 0x34(%rsp), %rsi
callq 0x22380
movb 0x7(%rsp), %cl
movb %cl, (%rax)
jmp 0x2173a
movl $0x0, 0x30(%rsp)
cmpl $0x0, 0x4c(%rsp)
jle 0x21893
movq 0x18(%rsp), %rdi
movq 0x158(%rsp), %rax
movl 0x4c(%rsp), %ecx
addl $-0x1, %ecx
movl %ecx, 0x4c(%rsp)
movslq %ecx, %rcx
movslq 0x50(%rsp,%rcx,4), %rcx
movzbl (%rax,%rcx), %eax
movl %eax, 0x24(%rsp)
movslq 0x30(%rsp), %rcx
leaq 0x74bf(%rip), %rax      # 0x28c40
movl (%rax,%rcx,4), %eax
movl %eax, 0x2c(%rsp)
movl 0x164(%rsp), %esi
movl 0x2c(%rsp), %edx
callq 0x21400
cmpl $0x0, %eax
jne 0x217a7
movl $0x1, 0x28(%rsp)
jmp 0x21825
movl $0x2, 0x28(%rsp)
movl 0x164(%rsp), %edx
movl $0x1, %eax
xorl %ecx, %ecx
cmpl $0x3, %edx
cmovel %ecx, %eax
movslq %eax, %rcx
leaq 0x7493(%rip), %rax      # 0x28c60
shlq $0x5, %rcx
addq %rcx, %rax
movslq 0x30(%rsp), %rcx
movl (%rax,%rcx,4), %eax
movl %eax, 0x2c(%rsp)
xorl %eax, %eax
cmpl $0xf, 0x28(%rsp)
movb %al, 0x6(%rsp)
jge 0x2180c
movq 0x18(%rsp), %rdi
movl 0x164(%rsp), %esi
movl 0x2c(%rsp), %edx
callq 0x21400
cmpl $0x0, %eax
setne %al
movb %al, 0x6(%rsp)
movb 0x6(%rsp), %al
testb $0x1, %al
jne 0x21816
jmp 0x21823
movl 0x28(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x28(%rsp)
jmp 0x217e0
jmp 0x21825
movq 0x18(%rsp), %rdi
addq $0xdc0, %rdi            # imm = 0xDC0
callq 0x18ba0
cmpl $0x0, %eax
jne 0x21844
movl 0x28(%rsp), %eax
movl %eax, (%rsp)
jmp 0x2184d
xorl %eax, %eax
subl 0x28(%rsp), %eax
movl %eax, (%rsp)
movl (%rsp), %edx
movq 0x168(%rsp), %rax
movslq 0x24(%rsp), %rcx
movl %edx, (%rax,%rcx,4)
movl 0x28(%rsp), %edx
movl $0x1, %eax
xorl %ecx, %ecx
cmpl $0x1, %edx
cmovel %ecx, %eax
movslq %eax, %rcx
leaq 0x7425(%rip), %rax      # 0x28ca0
shlq $0x5, %rcx
addq %rcx, %rax
movslq 0x30(%rsp), %rcx
movl (%rax,%rcx,4), %eax
movl %eax, 0x30(%rsp)
jmp 0x21742
addq $0x178, %rsp            # imm = 0x178
retq
nopl (%rax,%rax)