;*!
;* \copy
;*     Copyright (c)  2009-2013, Cisco Systems
;*     All rights reserved.
;*
;*     Redistribution and use in source and binary forms, with or without
;*     modification, are permitted provided that the following conditions
;*     are met:
;*
;*        * Redistributions of source code must retain the above copyright
;*          notice, this list of conditions and the following disclaimer.
;*
;*        * Redistributions in binary form must reproduce the above copyright
;*          notice, this list of conditions and the following disclaimer in
;*          the documentation and/or other materials provided with the
;*          distribution.
;*
;*     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
;*     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
;*     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
;*     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
;*     COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
;*     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
;*     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
;*     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
;*     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
;*     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
;*     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
;*     POSSIBILITY OF SUCH DAMAGE.
;*
;*************************************************************************/
%include "asm_inc.asm"

;***********************************************************************
; Local Data (Read Only)
;***********************************************************************
SECTION .rodata align=16

ALIGN 16
mv_x_inc_x4     dw  0x10, 0x10, 0x10, 0x10
mv_y_inc_x4     dw  0x04, 0x04, 0x04, 0x04
mx_x_offset_x4  dw  0x00, 0x04, 0x08, 0x0C

SECTION .text
%ifdef X86_32
;**********************************************************************************************************************
;void SumOf8x8BlockOfFrame_sse2(uint8_t* pRefPicture, const int32_t kiWidth, const int32_t kiHeight, const int32_t kiRefStride,
;                             uint16_t* pFeatureOfBlock, uint32_t pTimesOfFeatureValue[]);
;*********************************************************************************************************************
WELS_EXTERN SumOf8x8BlockOfFrame_sse2
%define     pushsize        16
%define     localsize       4
%define     ref             esp + pushsize + localsize + 4
%define     sum_ref         esp + pushsize + localsize + 20
%define     times_of_sum    esp + pushsize + localsize + 24
%define     width           esp + pushsize + localsize + 8
%define     height          esp + pushsize + localsize + 12
%define     linesize        esp + pushsize + localsize + 16
%define     tmp_width       esp + 0
    push    ebx
    push    ebp
    push    esi
    push    edi
    sub     esp,    localsize

    pxor    xmm0,   xmm0
    mov     esi,    [ref]
    mov     edi,    [sum_ref]
    mov     edx,    [times_of_sum]
    mov     ebx,    [linesize]
    mov     eax,    [width]
    lea     ecx,    [ebx+ebx*2] ; 3*linesize

    mov     [tmp_width],    eax
    lea     ebp,    [esi+ebx*4]
FIRST_ROW:
    movq    xmm1,   [esi]
    movq    xmm2,   [esi+ebx]
    movq    xmm3,   [esi+ebx*2]
    movq    xmm4,   [esi+ecx]

    shufps  xmm1,   xmm2,   01000100b
    shufps  xmm3,   xmm4,   01000100b
    psadbw  xmm1,   xmm0
    psadbw  xmm3,   xmm0
    paddd   xmm1,   xmm3

    movq    xmm2,   [ebp]
    movq    xmm3,   [ebp+ebx]
    movq    xmm4,   [ebp+ebx*2]
    movq    xmm5,   [ebp+ecx]

    shufps  xmm2,   xmm3,   01000100b
    shufps  xmm4,   xmm5,   01000100b
    psadbw  xmm2,   xmm0
    psadbw  xmm4,   xmm0
    paddd   xmm2,   xmm4

    paddd   xmm1,   xmm2
    pshufd  xmm2,   xmm1,   00001110b
    paddd   xmm1,   xmm2
    movd    eax,    xmm1
    mov     [edi],  ax
    inc     dword [edx+eax*4]

    inc     esi
    inc     ebp
    add     edi,    2

    dec     dword [tmp_width]
    jg      FIRST_ROW

    mov     esi,    [ref]
    mov     edi,    [sum_ref]
    mov     ebp,    [width]
    dec     dword [height]
HEIGHT_LOOP:
    mov     [tmp_width],    ebp
WIDTH_LOOP:
    movq    xmm1,   [esi+ebx*8]
    movq    xmm2,   [esi]
    psadbw  xmm1,   xmm0
    psadbw  xmm2,   xmm0
    psubd   xmm1,   xmm2
    movd    eax,    xmm1
    mov     cx,     [edi]
    add     eax,    ecx

    mov     [edi+ebp*2],    ax
    inc     dword [edx+eax*4]

    inc     esi
    add     edi,    2

    dec     dword [tmp_width]
    jg      WIDTH_LOOP

    add     esi,    ebx
    sub     esi,    ebp

    dec     dword [height]
    jg      HEIGHT_LOOP

    add     esp,    localsize
    pop     edi
    pop     esi
    pop     ebp
    pop     ebx
%undef      pushsize
%undef      localsize
%undef      ref
%undef      sum_ref
%undef      times_of_sum
%undef      width
%undef      height
%undef      linesize
%undef      tmp_width
    ret


%macro COUNT_SUM 3
%define xmm_reg %1
%define tmp_reg %2
    movd    tmp_reg,    xmm_reg
    inc     dword [edx+tmp_reg*4]
%if %3 == 1
    psrldq  xmm_reg,    4
%endif
%endmacro


;-----------------------------------------------------------------------------
; requires:  width % 8 == 0 && height > 1
;-----------------------------------------------------------------------------
;void SumOf8x8BlockOfFrame_sse4(uint8_t* pRefPicture, const int32_t kiWidth, const int32_t kiHeight, const int32_t kiRefStride,
;                             uint16_t* pFeatureOfBlock, uint32_t pTimesOfFeatureValue[]);
;-----------------------------------------------------------------------------
; read extra (16 - (width % 8) ) mod 16 bytes of every line
; write extra (16 - (width % 8)*2 ) mod 16 bytes in the end of sum_ref
WELS_EXTERN SumOf8x8BlockOfFrame_sse4
%define     pushsize        16
%define     localsize       4
%define     ref             esp + pushsize + localsize + 4
%define     sum_ref         esp + pushsize + localsize + 20
%define     times_of_sum    esp + pushsize + localsize + 24
%define     width           esp + pushsize + localsize + 8
%define     height          esp + pushsize + localsize + 12
%define     linesize        esp + pushsize + localsize + 16
%define     tmp_width       esp + 0
    push    ebx
    push    ebp
    push    esi
    push    edi
    sub     esp,    localsize

    pxor    xmm0,   xmm0
    mov     esi,    [ref]
    mov     edi,    [sum_ref]
    mov     edx,    [times_of_sum]
    mov     ebx,    [linesize]
    mov     eax,    [width]
    lea     ecx,    [ebx+ebx*2] ; 3*linesize

    mov     [tmp_width],    eax
    lea     ebp,    [esi+ebx*4]
FIRST_ROW_SSE4:
    movdqu  xmm1,   [esi]
    movdqu  xmm3,   [esi+ebx]
    movdqu  xmm5,   [esi+ebx*2]
    movdqu  xmm7,   [esi+ecx]

    movdqa  xmm2,   xmm1
    mpsadbw xmm1,   xmm0,   000b
    mpsadbw xmm2,   xmm0,   100b
    paddw   xmm1,   xmm2            ; 8 sums of line1

    movdqa  xmm4,   xmm3
    mpsadbw xmm3,   xmm0,   000b
    mpsadbw xmm4,   xmm0,   100b
    paddw   xmm3,   xmm4            ; 8 sums of line2

    movdqa  xmm2,   xmm5
    mpsadbw xmm5,   xmm0,   000b
    mpsadbw xmm2,   xmm0,   100b
    paddw   xmm5,   xmm2            ; 8 sums of line3

    movdqa  xmm4,   xmm7
    mpsadbw xmm7,   xmm0,   000b
    mpsadbw xmm4,   xmm0,   100b
    paddw   xmm7,   xmm4            ; 8 sums of line4

    paddw   xmm1,   xmm3
    paddw   xmm5,   xmm7
    paddw   xmm1,   xmm5            ; sum the upper 4 lines first

    movdqu  xmm2,   [ebp]
    movdqu  xmm3,   [ebp+ebx]
    movdqu  xmm4,   [ebp+ebx*2]
    movdqu  xmm5,   [ebp+ecx]

    movdqa  xmm6,   xmm2
    mpsadbw xmm2,   xmm0,   000b
    mpsadbw xmm6,   xmm0,   100b
    paddw   xmm2,   xmm6

    movdqa  xmm7,   xmm3
    mpsadbw xmm3,   xmm0,   000b
    mpsadbw xmm7,   xmm0,   100b
    paddw   xmm3,   xmm7

    movdqa  xmm6,   xmm4
    mpsadbw xmm4,   xmm0,   000b
    mpsadbw xmm6,   xmm0,   100b
    paddw   xmm4,   xmm6

    movdqa  xmm7,   xmm5
    mpsadbw xmm5,   xmm0,   000b
    mpsadbw xmm7,   xmm0,   100b
    paddw   xmm5,   xmm7

    paddw   xmm2,   xmm3
    paddw   xmm4,   xmm5
    paddw   xmm1,   xmm2
    paddw   xmm1,   xmm4            ; sum of lines 1- 8

    movdqu  [edi],  xmm1

    movdqa  xmm2,   xmm1
    punpcklwd   xmm1,   xmm0
    punpckhwd   xmm2,   xmm0

    COUNT_SUM   xmm1,   eax,    1
    COUNT_SUM   xmm1,   eax,    1
    COUNT_SUM   xmm1,   eax,    1
    COUNT_SUM   xmm1,   eax,    0
    COUNT_SUM   xmm2,   eax,    1
    COUNT_SUM   xmm2,   eax,    1
    COUNT_SUM   xmm2,   eax,    1
    COUNT_SUM   xmm2,   eax,    0

    lea     esi,    [esi+8]
    lea     ebp,    [ebp+8]
    lea     edi,    [edi+16]        ; element size is 2

    sub     dword [tmp_width], 8
    jg      near FIRST_ROW_SSE4

    mov     esi,    [ref]
    mov     edi,    [sum_ref]
    mov     ebp,    [width]
    dec     dword [height]
HEIGHT_LOOP_SSE4:
    mov     ecx,    ebp
WIDTH_LOOP_SSE4:
    movdqu  xmm1,   [esi+ebx*8]
    movdqu  xmm2,   [esi]
    movdqu  xmm7,   [edi]

    movdqa  xmm3,   xmm1
    mpsadbw xmm1,   xmm0,   000b
    mpsadbw xmm3,   xmm0,   100b
    paddw   xmm1,   xmm3

    movdqa  xmm4,   xmm2
    mpsadbw xmm2,   xmm0,   000b
    mpsadbw xmm4,   xmm0,   100b
    paddw   xmm2,   xmm4

    paddw   xmm7,   xmm1
    psubw   xmm7,   xmm2
    movdqu  [edi+ebp*2], xmm7

    movdqa  xmm6,   xmm7
    punpcklwd   xmm7,   xmm0
    punpckhwd   xmm6,   xmm0

    COUNT_SUM   xmm7,   eax,    1
    COUNT_SUM   xmm7,   eax,    1
    COUNT_SUM   xmm7,   eax,    1
    COUNT_SUM   xmm7,   eax,    0
    COUNT_SUM   xmm6,   eax,    1
    COUNT_SUM   xmm6,   eax,    1
    COUNT_SUM   xmm6,   eax,    1
    COUNT_SUM   xmm6,   eax,    0

    lea     esi,    [esi+8]
    lea     edi,    [edi+16]

    sub     ecx,    8
    jg      near WIDTH_LOOP_SSE4

    lea     esi,    [esi+ebx]
    sub     esi,    ebp

    dec     dword [height]
    jg      near HEIGHT_LOOP_SSE4

    add     esp,    localsize
    pop     edi
    pop     esi
    pop     ebp
    pop     ebx
%undef      pushsize
%undef      localsize
%undef      ref
%undef      sum_ref
%undef      times_of_sum
%undef      width
%undef      height
%undef      linesize
%undef      tmp_width
    ret


;****************************************************************************************************************************************************
;void SumOf16x16BlockOfFrame_sse2(uint8_t* pRefPicture, const int32_t kiWidth, const int32_t kiHeight, const int32_t kiRefStride,
;                             uint16_t* pFeatureOfBlock, uint32_t pTimesOfFeatureValue[]);
;****************************************************************************************************************************************************
WELS_EXTERN SumOf16x16BlockOfFrame_sse2
%define     pushsize        16
%define     localsize       4
%define     ref             esp + pushsize + localsize + 4
%define     sum_ref         esp + pushsize + localsize + 20
%define     times_of_sum    esp + pushsize + localsize + 24
%define     width           esp + pushsize + localsize + 8
%define     height          esp + pushsize + localsize + 12
%define     linesize        esp + pushsize + localsize + 16
%define     tmp_width       esp
    push    ebx
    push    ebp
    push    esi
    push    edi
    sub     esp,    localsize

    pxor    xmm0,   xmm0
    mov     esi,    [ref]
    mov     edi,    [sum_ref]
    mov     edx,    [times_of_sum]
    mov     ebx,    [linesize]
    mov     eax,    [width]

    lea     ecx,    [ebx+ebx*2]
    mov     [tmp_width],    eax
FIRST_ROW_X16H:
    movdqu  xmm1,   [esi]
    movdqu  xmm2,   [esi+ebx]
    movdqu  xmm3,   [esi+ebx*2]
    movdqu  xmm4,   [esi+ecx]

    psadbw  xmm1,   xmm0
    psadbw  xmm2,   xmm0
    psadbw  xmm3,   xmm0
    psadbw  xmm4,   xmm0
    paddw   xmm1,   xmm2
    paddw   xmm3,   xmm4
    paddw   xmm1,   xmm3

    lea     ebp,    [esi+ebx*4]
    movdqu  xmm2,   [ebp]
    movdqu  xmm3,   [ebp+ebx]
    movdqu  xmm4,   [ebp+ebx*2]
    movdqu  xmm5,   [ebp+ecx]

    psadbw  xmm2,   xmm0
    psadbw  xmm3,   xmm0
    psadbw  xmm4,   xmm0
    psadbw  xmm5,   xmm0
    paddw   xmm2,   xmm3
    paddw   xmm4,   xmm5
    paddw   xmm2,   xmm4

    paddw   xmm1,   xmm2

    lea     ebp,    [ebp+ebx*4]
    movdqu  xmm2,   [ebp]
    movdqu  xmm3,   [ebp+ebx]
    movdqu  xmm4,   [ebp+ebx*2]
    movdqu  xmm5,   [ebp+ecx]

    psadbw  xmm2,   xmm0
    psadbw  xmm3,   xmm0
    psadbw  xmm4,   xmm0
    psadbw  xmm5,   xmm0
    paddw   xmm2,   xmm3
    paddw   xmm4,   xmm5
    paddw   xmm2,   xmm4

    paddw   xmm1,   xmm2

    lea     ebp,    [ebp+ebx*4]
    movdqu  xmm2,   [ebp]
    movdqu  xmm3,   [ebp+ebx]
    movdqu  xmm4,   [ebp+ebx*2]
    movdqu  xmm5,   [ebp+ecx]

    psadbw  xmm2,   xmm0
    psadbw  xmm3,   xmm0
    psadbw  xmm4,   xmm0
    psadbw  xmm5,   xmm0
    paddw   xmm2,   xmm3
    paddw   xmm4,   xmm5
    paddw   xmm2,   xmm4

    paddw   xmm1,   xmm2
    movdqa  xmm2,   xmm1
    punpckhwd xmm2, xmm0
    paddw xmm1, xmm2
    movd    eax,    xmm1
    mov     [edi],  ax
    inc     dword [edx+eax*4]

    inc     esi
    lea     edi,    [edi+2]

    dec     dword [tmp_width]
    jg      near FIRST_ROW_X16H

    mov     esi,    [ref]
    mov     edi,    [sum_ref]
    mov     ebp,    [width]
    dec     dword [height]

    mov     ecx,    ebx
    sal     ecx,    4       ; succeeded 16th line
HEIGHT_LOOP_X16:
    mov     [tmp_width],    ebp
WIDTH_LOOP_X16:
    movdqu  xmm1,   [esi+ecx]
    movdqu  xmm2,   [esi]
    psadbw  xmm1,   xmm0
    psadbw  xmm2,   xmm0
    psubw   xmm1,   xmm2
    movdqa  xmm2,   xmm1
    punpckhwd xmm2, xmm0
    paddw   xmm1,   xmm2
    movd    eax,    xmm1
    add     ax, word [edi]
    mov     [edi+ebp*2],    ax
    inc     dword [edx+eax*4]

    inc     esi
    add     edi,    2

    dec     dword [tmp_width]
    jg      near WIDTH_LOOP_X16

    add     esi,    ebx
    sub     esi,    ebp

    dec     dword [height]
    jg      near HEIGHT_LOOP_X16

    add     esp,    localsize
    pop     edi
    pop     esi
    pop     ebp
    pop     ebx
%undef      pushsize
%undef      localsize
%undef      ref
%undef      sum_ref
%undef      times_of_sum
%undef      width
%undef      height
%undef      linesize
%undef      tmp_width
    ret

; requires:  width % 16 == 0 && height > 1
;-----------------------------------------------------------------------------------------------------------------------------
;void SumOf16x16BlockOfFrame_sse4(uint8_t* pRefPicture, const int32_t kiWidth, const int32_t kiHeight, const int32_t kiRefStride,
;                             uint16_t* pFeatureOfBlock, uint32_t pTimesOfFeatureValue[]);
;-----------------------------------------------------------------------------------------------------------------------------
; try 8 mv via offset
%macro SUM_LINE_X16_SSE41  5    ; ref, dst0, dst1, tmp0, tmp1
    movdqu  %2, [%1]
    movdqu  %3, [%1+8h]
    movdqa  %4, %2
    movdqa  %5, %3

    mpsadbw %2, xmm0,   0   ; 000 B
    mpsadbw %4, xmm0,   5   ; 101 B
    mpsadbw %3, xmm0,   2   ; 010 B
    mpsadbw %5, xmm0,   7   ; 111 B
    paddw   %2, %4
    paddw   %3, %5
    paddw   %2, %3  ; accumulate cost
%endmacro   ; end of SAD_16x16_LINE_SSE41

WELS_EXTERN SumOf16x16BlockOfFrame_sse4
%define     pushsize        16
%define     localsize       4
%define     ref             esp + pushsize + localsize + 4
%define     sum_ref         esp + pushsize + localsize + 20
%define     times_of_sum    esp + pushsize + localsize + 24
%define     width           esp + pushsize + localsize + 8
%define     height          esp + pushsize + localsize + 12
%define     linesize        esp + pushsize + localsize + 16
%define     tmp_width       esp
    push    ebx
    push    ebp
    push    esi
    push    edi
    sub     esp,    localsize

    pxor    xmm0,   xmm0
    mov     esi,    [ref]
    mov     edi,    [sum_ref]
    mov     edx,    [times_of_sum]
    mov     ebx,    [linesize]
    mov     eax,    [width]

    lea     ecx,    [ebx+ebx*2]
    mov     [tmp_width],    eax
FIRST_ROW_X16_SSE4:
    SUM_LINE_X16_SSE41  esi,        xmm1, xmm2, xmm3, xmm4
    SUM_LINE_X16_SSE41  esi+ebx,    xmm2, xmm3, xmm4, xmm5
    SUM_LINE_X16_SSE41  esi+ebx*2,  xmm3, xmm4, xmm5, xmm6
    SUM_LINE_X16_SSE41  esi+ecx,    xmm4, xmm5, xmm6, xmm7
    paddw   xmm1, xmm2
    paddw   xmm3, xmm4
    paddw   xmm1, xmm3

    lea     ebp,    [esi+ebx*4]
    SUM_LINE_X16_SSE41  ebp,        xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  ebp+ebx,    xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  ebp+ebx*2,  xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  ebp+ecx,    xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2

    lea     ebp,    [ebp+ebx*4]
    SUM_LINE_X16_SSE41  ebp,        xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  ebp+ebx,    xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  ebp+ebx*2,  xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  ebp+ecx,    xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2

    lea     ebp,    [ebp+ebx*4]
    SUM_LINE_X16_SSE41  ebp,        xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  ebp+ebx,    xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  ebp+ebx*2,  xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  ebp+ecx,    xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2

    movdqa  [edi],  xmm1
    movdqa  xmm2,   xmm1
    punpcklwd   xmm1,   xmm0
    punpckhwd   xmm2,   xmm0

    COUNT_SUM   xmm1,   eax,    1
    COUNT_SUM   xmm1,   eax,    1
    COUNT_SUM   xmm1,   eax,    1
    COUNT_SUM   xmm1,   eax,    0
    COUNT_SUM   xmm2,   eax,    1
    COUNT_SUM   xmm2,   eax,    1
    COUNT_SUM   xmm2,   eax,    1
    COUNT_SUM   xmm2,   eax,    0

    lea     esi,    [esi+8]
    lea     edi,    [edi+16]    ; element size is 2

    sub     dword [tmp_width], 8
    jg      near FIRST_ROW_X16_SSE4

    mov     esi,    [ref]
    mov     edi,    [sum_ref]
    mov     ebp,    [width]
    dec     dword [height]

    mov     ecx,    ebx
    sal     ecx,    4       ; succeeded 16th line

HEIGHT_LOOP_X16_SSE4:
    mov     [tmp_width],    ebp
WIDTH_LOOP_X16_SSE4:
    movdqa  xmm7,   [edi]
    SUM_LINE_X16_SSE41  esi+ecx, xmm1, xmm2, xmm3, xmm4
    SUM_LINE_X16_SSE41  esi, xmm2, xmm3, xmm4, xmm5

    paddw   xmm7,   xmm1
    psubw   xmm7,   xmm2
    movdqa  [edi+ebp*2], xmm7

    movdqa  xmm6,   xmm7
    punpcklwd   xmm7,   xmm0
    punpckhwd   xmm6,   xmm0

    COUNT_SUM   xmm7,   eax,    1
    COUNT_SUM   xmm7,   eax,    1
    COUNT_SUM   xmm7,   eax,    1
    COUNT_SUM   xmm7,   eax,    0
    COUNT_SUM   xmm6,   eax,    1
    COUNT_SUM   xmm6,   eax,    1
    COUNT_SUM   xmm6,   eax,    1
    COUNT_SUM   xmm6,   eax,    0

    lea     esi,    [esi+8]
    lea     edi,    [edi+16]

    sub     dword [tmp_width], 8
    jg      near WIDTH_LOOP_X16_SSE4

    add     esi,    ebx
    sub     esi,    ebp

    dec     dword [height]
    jg      near HEIGHT_LOOP_X16_SSE4

    add     esp,    localsize
    pop     edi
    pop     esi
    pop     ebp
    pop     ebx
%undef      pushsize
%undef      localsize
%undef      ref
%undef      sum_ref
%undef      times_of_sum
%undef      width
%undef      height
%undef      linesize
%undef      tmp_width
    ret


;-----------------------------------------------------------------------------------------------------------------------------
; void FillQpelLocationByFeatureValue_sse2(uint16_t* pFeatureOfBlock, const int32_t kiWidth, const int32_t kiHeight, uint16_t** pFeatureValuePointerList)
;-----------------------------------------------------------------------------------------------------------------------------
WELS_EXTERN FillQpelLocationByFeatureValue_sse2
    push    esi
    push    edi
    push    ebx
    push    ebp

    %define _ps         16              ; push size
    %define _ls         4               ; local size
    %define sum_ref     esp+_ps+_ls+4
    %define pos_list    esp+_ps+_ls+16
    %define width       esp+_ps+_ls+8
    %define height      esp+_ps+_ls+12
    %define i_height    esp
    sub     esp,    _ls

    mov     esi,    [sum_ref]
    mov     edi,    [pos_list]
    mov     ebp,    [width]
    mov     ebx,    [height]
    mov     [i_height], ebx

    movq    xmm7,   [mv_x_inc_x4]       ; x_qpel inc
    movq    xmm6,   [mv_y_inc_x4]       ; y_qpel inc
    movq    xmm5,   [mx_x_offset_x4]    ; x_qpel vector
    pxor    xmm4,   xmm4
    pxor    xmm3,   xmm3                ; y_qpel vector
HASH_HEIGHT_LOOP_SSE2:
    movdqa  xmm2,   xmm5    ; x_qpel vector
    mov     ecx,    ebp
HASH_WIDTH_LOOP_SSE2:
    movq    xmm0,   [esi]           ; load x8 sum
    punpcklwd   xmm0,   xmm4
    movdqa      xmm1,   xmm2
    punpcklwd   xmm1,   xmm3
%rep    3
    movd    edx,    xmm0
    lea     ebx,    [edi+edx*4]
    mov     eax,    [ebx]
    movd    [eax],  xmm1
    mov     edx,    [eax+4] ; explictly load eax+4 due cache miss from vtune observation
    lea     eax,    [eax+4]
    mov     [ebx],  eax
    psrldq  xmm1,   4
    psrldq  xmm0,   4
%endrep
    movd    edx,    xmm0
    lea     ebx,    [edi+edx*4]
    mov     eax,    [ebx]
    movd    [eax],  xmm1
    mov     edx,    [eax+4] ; explictly load eax+4 due cache miss from vtune observation
    lea     eax,    [eax+4]
    mov     [ebx],  eax

    paddw   xmm2,   xmm7
    lea     esi,    [esi+8]
    sub     ecx,    4
    jnz near HASH_WIDTH_LOOP_SSE2
    paddw   xmm3,   xmm6
    dec dword [i_height]
    jnz near HASH_HEIGHT_LOOP_SSE2

    add     esp,    _ls
    %undef  _ps
    %undef  _ls
    %undef  sum_ref
    %undef  pos_list
    %undef  width
    %undef  height
    %undef  i_height
    pop     ebp
    pop     ebx
    pop     edi
    pop     esi
    ret

;---------------------------------------------------------------------------------------------------------------------------------------------------
; void InitializeHashforFeature_sse2( uint32_t* pTimesOfFeatureValue, uint16_t* pBuf, const int32_t kiListSize,
;                        uint16_t** pLocationOfFeature, uint16_t** pFeatureValuePointerList )
;---------------------------------------------------------------------------------------------------------------------------------------------------
WELS_EXTERN InitializeHashforFeature_sse2
    push    ebx
    push    esi
    push    edi
    push    ebp
    %define _ps 16  ; push size
    mov     edi,    [esp+_ps+16]    ; pPositionOfSum
    mov     ebp,    [esp+_ps+20]    ; sum_idx_list
    mov     esi,    [esp+_ps+4]     ; pTimesOfSum
    mov     ebx,    [esp+_ps+8]     ; pBuf
    mov     edx,    [esp+_ps+12]    ; list_sz
    sar     edx,    2
    mov     ecx,    0
    pxor    xmm7,   xmm7
hash_assign_loop_x4_sse2:
    movdqa  xmm0,   [esi+ecx]
    pslld   xmm0,   2

    movdqa  xmm1,   xmm0
    pcmpeqd xmm1,   xmm7
    movmskps    eax,    xmm1
    cmp eax, 0x0f
    je  near hash_assign_with_copy_sse2

%assign x   0
%rep 4
    lea     eax,    [edi+ecx+x]
    mov     [eax],  ebx
    lea     eax,    [ebp+ecx+x]
    mov     [eax],  ebx
    movd    eax,    xmm0
    add     ebx,    eax
    psrldq  xmm0,   4
%assign x   x+4
%endrep
    jmp near assign_next_sse2

hash_assign_with_copy_sse2:
    movd    xmm1,   ebx
    pshufd  xmm2,   xmm1,   0
    movdqa  [edi+ecx], xmm2
    movdqa  [ebp+ecx], xmm2

assign_next_sse2:
    add     ecx,    16
    dec     edx
    jnz     near hash_assign_loop_x4_sse2

    mov     edx,    [esp+_ps+12]    ; list_sz
    and     edx,    3
    jz      near hash_assign_no_rem_sse2
hash_assign_loop_x4_rem_sse2:
    lea     eax,    [edi+ecx]
    mov     [eax],  ebx
    lea     eax,    [ebp+ecx]
    mov     [eax],  ebx
    mov     eax,    [esi+ecx]
    sal     eax,    2
    add     ebx,    eax
    add     ecx,    4
    dec     edx
    jnz     near hash_assign_loop_x4_rem_sse2

hash_assign_no_rem_sse2:
    %undef  _ps
    pop     ebp
    pop     edi
    pop     esi
    pop     ebx
    ret
%else

;**********************************************************************************************************************
;void SumOf8x8BlockOfFrame_sse2(uint8_t* pRefPicture, const int32_t kiWidth, const int32_t kiHeight, const int32_t kiRefStride,
;                             uint16_t* pFeatureOfBlock, uint32_t pTimesOfFeatureValue[]);
;*********************************************************************************************************************
WELS_EXTERN SumOf8x8BlockOfFrame_sse2
    %assign  push_num 0
    LOAD_6_PARA
    PUSH_XMM 6
    SIGN_EXTENSION  r1, r1d
    SIGN_EXTENSION  r2, r2d
    SIGN_EXTENSION  r3, r3d
    push r12
    push r13
    push r0
    push r2
    push r4

    pxor    xmm0,   xmm0
    lea     r6, [r3+r3*2]

    mov     r12,    r1              ;r12:tmp_width
    lea     r13,    [r0+r3*4]       ;rbp:r13
FIRST_ROW:
    movq    xmm1,   [r0]
    movq    xmm2,   [r0+r3]
    movq    xmm3,   [r0+r3*2]
    movq    xmm4,   [r0+r6]

    shufps  xmm1,   xmm2,   01000100b
    shufps  xmm3,   xmm4,   01000100b
    psadbw  xmm1,   xmm0
    psadbw  xmm3,   xmm0
    paddd   xmm1,   xmm3

    movq    xmm2,   [r13]
    movq    xmm3,   [r13+r3]
    movq    xmm4,   [r13+r3*2]
    movq    xmm5,   [r13+r6]

    shufps  xmm2,   xmm3,   01000100b
    shufps  xmm4,   xmm5,   01000100b
    psadbw  xmm2,   xmm0
    psadbw  xmm4,   xmm0
    paddd   xmm2,   xmm4

    paddd   xmm1,   xmm2
    pshufd  xmm2,   xmm1,   00001110b
    paddd   xmm1,   xmm2
    movd    r2d,    xmm1
    mov     [r4],   r2w
    inc     dword [r5+r2*4]

    inc     r0
    inc     r13
    add     r4, 2

    dec     r12
    jg      FIRST_ROW

    pop r4
    pop r2
    pop r0
    mov r13, r2
    dec r13
HEIGHT_LOOP:
    mov     r12,    r1
WIDTH_LOOP:
    movq    xmm1,   [r0+r3*8]
    movq    xmm2,   [r0]
    psadbw  xmm1,   xmm0
    psadbw  xmm2,   xmm0
    psubd   xmm1,   xmm2
    movd    r2d,    xmm1
    mov     r6w,    [r4]
    add     r2d,    r6d
    mov     [r4+r1*2],  r2w
    inc     dword [r5+r2*4]

    inc     r0
    add     r4, 2

    dec     r12
    jg      WIDTH_LOOP

    add     r0, r3
    sub     r0, r1


    dec     r13
    jg      HEIGHT_LOOP

    pop     r13
    pop     r12
    POP_XMM
    LOAD_6_PARA_POP
    ret


%macro COUNT_SUM 4
%define xmm_reg %1
%define tmp_dreg %2
%define tmp_qreg %3
    movd    tmp_dreg,   xmm_reg
    inc     dword [r5+tmp_qreg*4]
%if %4 == 1
    psrldq  xmm_reg,    4
%endif
%endmacro


;-----------------------------------------------------------------------------
; requires:  width % 8 == 0 && height > 1
;-----------------------------------------------------------------------------
;void SumOf8x8BlockOfFrame_sse4(uint8_t* pRefPicture, const int32_t kiWidth, const int32_t kiHeight, const int32_t kiRefStride,
;                             uint16_t* pFeatureOfBlock, uint32_t pTimesOfFeatureValue[]);
;-----------------------------------------------------------------------------
; read extra (16 - (width % 8) ) mod 16 bytes of every line
; write extra (16 - (width % 8)*2 ) mod 16 bytes in the end of sum_ref
WELS_EXTERN SumOf8x8BlockOfFrame_sse4
    %assign  push_num 0
    LOAD_6_PARA
    PUSH_XMM 8
    SIGN_EXTENSION  r1, r1d
    SIGN_EXTENSION  r2, r2d
    SIGN_EXTENSION  r3, r3d
    push r12
    push r13
    push r0
    push r2
    push r4

    pxor    xmm0,   xmm0
    lea     r6, [r3+r3*2]

    mov     r12,    r1              ;r12:tmp_width
    lea     r13,    [r0+r3*4]       ;rbp:r13
FIRST_ROW_SSE4:
    movdqu  xmm1,   [r0]
    movdqu  xmm3,   [r0+r3]
    movdqu  xmm5,   [r0+r3*2]
    movdqu  xmm7,   [r0+r6]

    movdqa  xmm2,   xmm1
    mpsadbw xmm1,   xmm0,   000b
    mpsadbw xmm2,   xmm0,   100b
    paddw   xmm1,   xmm2            ; 8 sums of line1

    movdqa  xmm4,   xmm3
    mpsadbw xmm3,   xmm0,   000b
    mpsadbw xmm4,   xmm0,   100b
    paddw   xmm3,   xmm4            ; 8 sums of line2

    movdqa  xmm2,   xmm5
    mpsadbw xmm5,   xmm0,   000b
    mpsadbw xmm2,   xmm0,   100b
    paddw   xmm5,   xmm2            ; 8 sums of line3

    movdqa  xmm4,   xmm7
    mpsadbw xmm7,   xmm0,   000b
    mpsadbw xmm4,   xmm0,   100b
    paddw   xmm7,   xmm4            ; 8 sums of line4

    paddw   xmm1,   xmm3
    paddw   xmm5,   xmm7
    paddw   xmm1,   xmm5            ; sum the upper 4 lines first

    movdqu  xmm2,   [r13]
    movdqu  xmm3,   [r13+r3]
    movdqu  xmm4,   [r13+r3*2]
    movdqu  xmm5,   [r13+r6]

    movdqa  xmm6,   xmm2
    mpsadbw xmm2,   xmm0,   000b
    mpsadbw xmm6,   xmm0,   100b
    paddw   xmm2,   xmm6

    movdqa  xmm7,   xmm3
    mpsadbw xmm3,   xmm0,   000b
    mpsadbw xmm7,   xmm0,   100b
    paddw   xmm3,   xmm7

    movdqa  xmm6,   xmm4
    mpsadbw xmm4,   xmm0,   000b
    mpsadbw xmm6,   xmm0,   100b
    paddw   xmm4,   xmm6

    movdqa  xmm7,   xmm5
    mpsadbw xmm5,   xmm0,   000b
    mpsadbw xmm7,   xmm0,   100b
    paddw   xmm5,   xmm7

    paddw   xmm2,   xmm3
    paddw   xmm4,   xmm5
    paddw   xmm1,   xmm2
    paddw   xmm1,   xmm4            ; sum of lines 1- 8

    movdqu  [r4],   xmm1

    movdqa  xmm2,   xmm1
    punpcklwd   xmm1,   xmm0
    punpckhwd   xmm2,   xmm0

    COUNT_SUM   xmm1,   r2d, r2, 1
    COUNT_SUM   xmm1,   r2d, r2, 1
    COUNT_SUM   xmm1,   r2d, r2, 1
    COUNT_SUM   xmm1,   r2d, r2, 0
    COUNT_SUM   xmm2,   r2d, r2 ,1
    COUNT_SUM   xmm2,   r2d, r2 ,1
    COUNT_SUM   xmm2,   r2d, r2 ,1
    COUNT_SUM   xmm2,   r2d, r2 ,0

    lea     r0,     [r0+8]
    lea     r13,    [r13+8]
    lea     r4,     [r4+16]     ; element size is 2

    sub     r12, 8
    jg      near FIRST_ROW_SSE4

    pop r4
    pop r2
    pop r0
    mov r13, r2
    dec r13
HEIGHT_LOOP_SSE4:
    mov     r12,    r1
WIDTH_LOOP_SSE4:
    movdqu  xmm1,   [r0+r3*8]
    movdqu  xmm2,   [r0]
    movdqu  xmm7,   [r4]

    movdqa  xmm3,   xmm1
    mpsadbw xmm1,   xmm0,   000b
    mpsadbw xmm3,   xmm0,   100b
    paddw   xmm1,   xmm3

    movdqa  xmm4,   xmm2
    mpsadbw xmm2,   xmm0,   000b
    mpsadbw xmm4,   xmm0,   100b
    paddw   xmm2,   xmm4

    paddw   xmm7,   xmm1
    psubw   xmm7,   xmm2
    movdqu  [r4+r1*2], xmm7

    movdqa  xmm6,   xmm7
    punpcklwd   xmm7,   xmm0
    punpckhwd   xmm6,   xmm0

    COUNT_SUM   xmm7,   r2d, r2, 1
    COUNT_SUM   xmm7,   r2d, r2, 1
    COUNT_SUM   xmm7,   r2d, r2, 1
    COUNT_SUM   xmm7,   r2d, r2, 0
    COUNT_SUM   xmm6,   r2d, r2, 1
    COUNT_SUM   xmm6,   r2d, r2, 1
    COUNT_SUM   xmm6,   r2d, r2, 1
    COUNT_SUM   xmm6,   r2d, r2, 0

    lea     r0, [r0+8]
    lea     r4, [r4+16]

    sub     r12,    8
    jg      near WIDTH_LOOP_SSE4

    lea     r0, [r0+r3]
    sub     r0, r1

    dec     r13
    jg      near HEIGHT_LOOP_SSE4

    pop     r13
    pop     r12
    POP_XMM
    LOAD_6_PARA_POP
    ret


;****************************************************************************************************************************************************
;void SumOf16x16BlockOfFrame_sse2(uint8_t* pRefPicture, const int32_t kiWidth, const int32_t kiHeight, const int32_t kiRefStride,
;                             uint16_t* pFeatureOfBlock, uint32_t pTimesOfFeatureValue[]);
;****************************************************************************************************************************************************
WELS_EXTERN SumOf16x16BlockOfFrame_sse2
    %assign  push_num 0
    LOAD_6_PARA
    PUSH_XMM 6
    SIGN_EXTENSION  r1, r1d
    SIGN_EXTENSION  r2, r2d
    SIGN_EXTENSION  r3, r3d
    push r12
    push r13
    push r0
    push r2
    push r4

    pxor    xmm0,   xmm0
    lea     r6, [r3+r3*2]

    mov     r12,    r1              ;r12:tmp_width
FIRST_ROW_X16H:
    movdqu  xmm1,   [r0]
    movdqu  xmm2,   [r0+r3]
    movdqu  xmm3,   [r0+r3*2]
    movdqu  xmm4,   [r0+r6]

    psadbw  xmm1,   xmm0
    psadbw  xmm2,   xmm0
    psadbw  xmm3,   xmm0
    psadbw  xmm4,   xmm0
    paddw   xmm1,   xmm2
    paddw   xmm3,   xmm4
    paddw   xmm1,   xmm3

    lea     r13,    [r0+r3*4]       ;ebp:r13
    movdqu  xmm2,   [r13]
    movdqu  xmm3,   [r13+r3]
    movdqu  xmm4,   [r13+r3*2]
    movdqu  xmm5,   [r13+r6]

    psadbw  xmm2,   xmm0
    psadbw  xmm3,   xmm0
    psadbw  xmm4,   xmm0
    psadbw  xmm5,   xmm0
    paddw   xmm2,   xmm3
    paddw   xmm4,   xmm5
    paddw   xmm2,   xmm4

    paddw   xmm1,   xmm2

    lea     r13,    [r13+r3*4]
    movdqu  xmm2,   [r13]
    movdqu  xmm3,   [r13+r3]
    movdqu  xmm4,   [r13+r3*2]
    movdqu  xmm5,   [r13+r6]

    psadbw  xmm2,   xmm0
    psadbw  xmm3,   xmm0
    psadbw  xmm4,   xmm0
    psadbw  xmm5,   xmm0
    paddw   xmm2,   xmm3
    paddw   xmm4,   xmm5
    paddw   xmm2,   xmm4

    paddw   xmm1,   xmm2

    lea     r13,    [r13+r3*4]
    movdqu  xmm2,   [r13]
    movdqu  xmm3,   [r13+r3]
    movdqu  xmm4,   [r13+r3*2]
    movdqu  xmm5,   [r13+r6]

    psadbw  xmm2,   xmm0
    psadbw  xmm3,   xmm0
    psadbw  xmm4,   xmm0
    psadbw  xmm5,   xmm0
    paddw   xmm2,   xmm3
    paddw   xmm4,   xmm5
    paddw   xmm2,   xmm4

    paddw   xmm1,   xmm2
    movdqa  xmm2,   xmm1
    punpckhwd xmm2, xmm0
    paddw xmm1, xmm2
    movd    r2d,    xmm1
    mov     [r4],   r2w
    inc     dword [r5+r2*4]

    inc     r0
    lea     r4, [r4+2]

    dec     r12
    jg      near FIRST_ROW_X16H

    pop r4
    pop r2
    pop r0
    mov r13, r2
    dec r13
    mov     r6, r3
    sal     r6, 4       ; succeeded 16th line
HEIGHT_LOOP_X16:
    mov     r12,    r1
WIDTH_LOOP_X16:
    movdqu  xmm1,   [r0+r6]
    movdqu  xmm2,   [r0]
    psadbw  xmm1,   xmm0
    psadbw  xmm2,   xmm0
    psubw   xmm1,   xmm2
    movdqa  xmm2,   xmm1
    punpckhwd xmm2, xmm0
    paddw   xmm1,   xmm2
    movd    r2d,    xmm1
    add     r2w,    word [r4]
    mov     [r4+r1*2],  r2w
    inc     dword [r5+r2*4]

    inc     r0
    add     r4, 2

    dec     r12
    jg      near WIDTH_LOOP_X16

    add     r0, r3
    sub     r0, r1

    dec     r13
    jg      near HEIGHT_LOOP_X16

    pop     r13
    pop     r12
    POP_XMM
    LOAD_6_PARA_POP
    ret

; requires:  width % 16 == 0 && height > 1
;-----------------------------------------------------------------------------------------------------------------------------
;void SumOf16x16BlockOfFrame_sse4(uint8_t* pRefPicture, const int32_t kiWidth, const int32_t kiHeight, const int32_t kiRefStride,
;                             uint16_t* pFeatureOfBlock, uint32_t pTimesOfFeatureValue[]);
;-----------------------------------------------------------------------------------------------------------------------------
; try 8 mv via offset
%macro SUM_LINE_X16_SSE41  5    ; ref, dst0, dst1, tmp0, tmp1
    movdqu  %2, [%1]
    movdqu  %3, [%1+8h]
    movdqa  %4, %2
    movdqa  %5, %3

    mpsadbw %2, xmm0,   0   ; 000 B
    mpsadbw %4, xmm0,   5   ; 101 B
    mpsadbw %3, xmm0,   2   ; 010 B
    mpsadbw %5, xmm0,   7   ; 111 B
    paddw   %2, %4
    paddw   %3, %5
    paddw   %2, %3  ; accumulate cost
%endmacro   ; end of SAD_16x16_LINE_SSE41

WELS_EXTERN SumOf16x16BlockOfFrame_sse4
    %assign  push_num 0
    LOAD_6_PARA
    PUSH_XMM 8
    SIGN_EXTENSION  r1, r1d
    SIGN_EXTENSION  r2, r2d
    SIGN_EXTENSION  r3, r3d
    push r12
    push r13
    push r0
    push r2
    push r4

    pxor    xmm0,   xmm0
    lea     r6, [r3+r3*2]

    mov     r12,    r1              ;r12:tmp_width
FIRST_ROW_X16_SSE4:
    SUM_LINE_X16_SSE41  r0,     xmm1, xmm2, xmm3, xmm4
    SUM_LINE_X16_SSE41  r0+r3,  xmm2, xmm3, xmm4, xmm5
    SUM_LINE_X16_SSE41  r0+r3*2,xmm3, xmm4, xmm5, xmm6
    SUM_LINE_X16_SSE41  r0+r6,  xmm4, xmm5, xmm6, xmm7
    paddw   xmm1, xmm2
    paddw   xmm3, xmm4
    paddw   xmm1, xmm3

    lea     r13,    [r0+r3*4]
    SUM_LINE_X16_SSE41  r13,        xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  r13+r3,     xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  r13+r3*2,   xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  r13+r6,     xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2

    lea     r13,    [r13+r3*4]
    SUM_LINE_X16_SSE41  r13,        xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  r13+r3,     xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  r13+r3*2,   xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  r13+r6,     xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2

    lea     r13,    [r13+r3*4]
    SUM_LINE_X16_SSE41  r13,        xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  r13+r3,     xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  r13+r3*2,   xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2
    SUM_LINE_X16_SSE41  r13+r6,     xmm2, xmm3, xmm4, xmm5
    paddw   xmm1, xmm2

    movdqa  [r4],   xmm1
    movdqa  xmm2,   xmm1
    punpcklwd   xmm1,   xmm0
    punpckhwd   xmm2,   xmm0

    COUNT_SUM   xmm1,   r2d, r2, 1
    COUNT_SUM   xmm1,   r2d, r2, 1
    COUNT_SUM   xmm1,   r2d, r2, 1
    COUNT_SUM   xmm1,   r2d, r2, 0
    COUNT_SUM   xmm2,   r2d, r2, 1
    COUNT_SUM   xmm2,   r2d, r2, 1
    COUNT_SUM   xmm2,   r2d, r2, 1
    COUNT_SUM   xmm2,   r2d, r2, 0

    lea     r0, [r0+8]
    lea     r4, [r4+16] ; element size is 2

    sub     r12, 8
    jg      near FIRST_ROW_X16_SSE4

    pop r4
    pop r2
    pop r0
    mov r13, r2
    dec r13
    mov     r6, r3
    sal     r6, 4       ; succeeded 16th line

HEIGHT_LOOP_X16_SSE4:
    mov     r12,    r1
WIDTH_LOOP_X16_SSE4:
    movdqa  xmm7,   [r4]
    SUM_LINE_X16_SSE41  r0+r6, xmm1, xmm2, xmm3, xmm4
    SUM_LINE_X16_SSE41  r0, xmm2, xmm3, xmm4, xmm5

    paddw   xmm7,   xmm1
    psubw   xmm7,   xmm2
    movdqa  [r4+r1*2], xmm7

    movdqa  xmm6,   xmm7
    punpcklwd   xmm7,   xmm0
    punpckhwd   xmm6,   xmm0

    COUNT_SUM   xmm7,   r2d, r2, 1
    COUNT_SUM   xmm7,   r2d, r2, 1
    COUNT_SUM   xmm7,   r2d, r2, 1
    COUNT_SUM   xmm7,   r2d, r2, 0
    COUNT_SUM   xmm6,   r2d, r2, 1
    COUNT_SUM   xmm6,   r2d, r2, 1
    COUNT_SUM   xmm6,   r2d, r2, 1
    COUNT_SUM   xmm6,   r2d, r2, 0

    lea     r0, [r0+8]
    lea     r4, [r4+16]

    sub     r12, 8
    jg      near WIDTH_LOOP_X16_SSE4

    add     r0, r3
    sub     r0, r1

    dec     r13
    jg      near HEIGHT_LOOP_X16_SSE4

    pop     r13
    pop     r12
    POP_XMM
    LOAD_6_PARA_POP
    ret

;-----------------------------------------------------------------------------------------------------------------------------
; void FillQpelLocationByFeatureValue_sse2(uint16_t* pFeatureOfBlock, const int32_t kiWidth, const int32_t kiHeight, uint16_t** pFeatureValuePointerList)
;-----------------------------------------------------------------------------------------------------------------------------
WELS_EXTERN FillQpelLocationByFeatureValue_sse2
    %assign  push_num 0
    LOAD_4_PARA
    PUSH_XMM 8
    SIGN_EXTENSION  r1, r1d
    SIGN_EXTENSION  r2, r2d
    push r12
    push r13
    mov     r12,    r2

    movq    xmm7,   [mv_x_inc_x4]       ; x_qpel inc
    movq    xmm6,   [mv_y_inc_x4]       ; y_qpel inc
    movq    xmm5,   [mx_x_offset_x4]    ; x_qpel vector
    pxor    xmm4,   xmm4
    pxor    xmm3,   xmm3                ; y_qpel vector
HASH_HEIGHT_LOOP_SSE2:
    movdqa  xmm2,   xmm5    ; x_qpel vector
    mov     r4, r1
HASH_WIDTH_LOOP_SSE2:
    movq    xmm0,   [r0]            ; load x8 sum
    punpcklwd   xmm0,   xmm4
    movdqa      xmm1,   xmm2
    punpcklwd   xmm1,   xmm3
%rep    3
    movd    r2d,    xmm0        ;edx:r3
    lea     r5,     [r3+r2*8]   ;ebx:r5
    mov     r6,     [r5]        ;eax:r6
    movd    [r6],   xmm1
    mov     r13,    [r6+4]  ; explictly load eax+4 due cache miss from vtune observation
    lea     r6,     [r6+4]
    mov     [r5],   r6
    psrldq  xmm1,   4
    psrldq  xmm0,   4
%endrep
    movd    r2d,    xmm0
    lea     r5,     [r3+r2*8]   ;ebx:r5
    mov     r6,     [r5]        ;eax:r6
    movd    [r6],   xmm1
    mov     r13,    [r6+4]  ; explictly load eax+4 due cache miss from vtune observation
    lea     r6,     [r6+4]
    mov     [r5],   r6

    paddw   xmm2,   xmm7
    lea     r0,     [r0+8]
    sub     r4,     4
    jnz near HASH_WIDTH_LOOP_SSE2
    paddw   xmm3,   xmm6
    dec r12
    jnz near HASH_HEIGHT_LOOP_SSE2

    pop     r13
    pop     r12
    POP_XMM
    ret

;---------------------------------------------------------------------------------------------------------------------------------------------------
; void InitializeHashforFeature_sse2( uint32_t* pTimesOfFeatureValue, uint16_t* pBuf, const int32_t kiListSize,
;                                 uint16_t** pLocationOfFeature, uint16_t** pFeatureValuePointerList);
;uint16_t** pPositionOfSum, uint16_t** sum_idx_list, uint32_t* pTimesOfSum, uint16_t* pBuf, const int32_t list_sz )
;---------------------------------------------------------------------------------------------------------------------------------------------------
WELS_EXTERN InitializeHashforFeature_sse2
    %assign  push_num 0
    LOAD_5_PARA
    SIGN_EXTENSION  r2, r2d
    push r12
    push r13
    mov     r12,    r2
    sar     r2,     2
    mov     r5,     0       ;r5:ecx
    xor     r6,     r6
    pxor    xmm3,   xmm3
hash_assign_loop_x4_sse2:
    movdqa  xmm0,   [r0+r5]
    pslld   xmm0,   2

    movdqa  xmm1,   xmm0
    pcmpeqd xmm1,   xmm3
    movmskps    r6, xmm1
    cmp     r6,     0x0f
    jz  near hash_assign_with_copy_sse2

%assign x   0
%rep 4
    lea     r13,    [r3+r5*2+x]
    mov     [r13],  r1
    lea     r13,    [r4+r5*2+x]
    mov     [r13],  r1
    movd    r6d,    xmm0
    add     r1,     r6
    psrldq  xmm0,   4
%assign x   x+8
%endrep
    jmp near assign_next_sse2

hash_assign_with_copy_sse2:
    movq    xmm1,   r1
    pshufd  xmm2,   xmm1,   01000100b
    movdqa  [r3+r5*2], xmm2
    movdqa  [r4+r5*2], xmm2
    movdqa  [r3+r5*2+16], xmm2
    movdqa  [r4+r5*2+16], xmm2

assign_next_sse2:
    add     r5, 16
    dec     r2
    jnz     near hash_assign_loop_x4_sse2

    and     r12,    3
    jz      near hash_assign_no_rem_sse2
hash_assign_loop_x4_rem_sse2:
    lea     r13,    [r3+r5*2]
    mov     [r13],  r1
    lea     r13,    [r4+r5*2]
    mov     [r13],  r1
    mov     r6d,    [r0+r5]
    sal     r6,     2
    add     r1,     r6
    add     r5,     4
    dec     r12
    jnz     near hash_assign_loop_x4_rem_sse2

hash_assign_no_rem_sse2:
    pop     r13
    pop     r12
    ret

%endif

;**********************************************************************************************************************************
;   int32_t SumOf8x8SingleBlock_sse2(uint8_t* ref0, int32_t linesize)
;**********************************************************************************************************************************
WELS_EXTERN SumOf8x8SingleBlock_sse2
    %assign  push_num 0
    LOAD_2_PARA
    SIGN_EXTENSION  r1, r1d

    pxor xmm0, xmm0
    movq xmm1, [r0]
    movhps xmm1, [r0+r1]
    lea r0, [r0+2*r1]
    movq xmm2, [r0]
    movhps xmm2, [r0+r1]
    lea r0, [r0+2*r1]
    movq xmm3, [r0]
    movhps xmm3, [r0+r1]
    lea r0, [r0+2*r1]
    movq xmm4, [r0]
    movhps xmm4, [r0+r1]

    psadbw xmm1, xmm0
    psadbw xmm2, xmm0
    psadbw xmm3, xmm0
    psadbw xmm4, xmm0
    paddw xmm1, xmm2
    paddw xmm3, xmm4
    paddw xmm1, xmm3

    movdqa xmm2, xmm1
    punpckhwd xmm2, xmm0
    paddw xmm1, xmm2

    movd retrd, xmm1
    ret

;**********************************************************************************************************************************
;   int32_t SumOf16x16SingleBlock_sse2(uint8_t* ref0, int32_t linesize)
;**********************************************************************************************************************************
WELS_EXTERN SumOf16x16SingleBlock_sse2
    %assign  push_num 0
    LOAD_2_PARA
    PUSH_XMM 6
    SIGN_EXTENSION  r1, r1d

    pxor xmm0, xmm0
    movdqa xmm1, [r0]
    movdqa xmm2, [r0+r1]
    lea r0, [r0+2*r1]
    movdqa xmm3, [r0]
    movdqa xmm4, [r0+r1]
    psadbw xmm1, xmm0
    psadbw xmm2, xmm0
    psadbw xmm3, xmm0
    psadbw xmm4, xmm0
    paddw xmm1, xmm2
    paddw xmm3, xmm4
    paddw xmm1, xmm3

    lea r0, [r0+2*r1]
    movdqa xmm2, [r0]
    movdqa xmm3, [r0+r1]
    lea r0, [r0+2*r1]
    movdqa xmm4, [r0]
    movdqa xmm5, [r0+r1]
    psadbw xmm2, xmm0
    psadbw xmm3, xmm0
    psadbw xmm4, xmm0
    psadbw xmm5, xmm0
    paddw xmm2, xmm3
    paddw xmm4, xmm5
    paddw xmm2, xmm4

    paddw xmm1, xmm2

    lea r0, [r0+2*r1]
    movdqa xmm2, [r0]
    movdqa xmm3, [r0+r1]
    lea r0, [r0+2*r1]
    movdqa xmm4, [r0]
    movdqa xmm5, [r0+r1]
    psadbw xmm2, xmm0
    psadbw xmm3, xmm0
    psadbw xmm4, xmm0
    psadbw xmm5, xmm0
    paddw xmm2, xmm3
    paddw xmm4, xmm5
    paddw xmm2, xmm4

    paddw xmm1, xmm2

    lea r0, [r0+2*r1]
    movdqa xmm2, [r0]
    movdqa xmm3, [r0+r1]
    lea r0, [r0+2*r1]
    movdqa xmm4, [r0]
    movdqa xmm5, [r0+r1]
    psadbw xmm2, xmm0
    psadbw xmm3, xmm0
    psadbw xmm4, xmm0
    psadbw xmm5, xmm0
    paddw xmm2, xmm3
    paddw xmm4, xmm5
    paddw xmm2, xmm4

    paddw xmm1, xmm2

    movdqa xmm2, xmm1
    punpckhwd xmm2, xmm0
    paddw xmm1, xmm2

    movd retrd, xmm1
    POP_XMM
    ret

;**********************************************************************************************************************************
;
;   uint32_t SampleSad16x16Hor8_sse41( uint8_t *src, int32_t stride_src, uint8_t *ref, int32_t stride_ref, uint16 base_cost[8], int32_t *index_min_cost )
;
;   \note:
;       src need align with 16 bytes, ref is optional
;   \return value:
;       return minimal SAD cost, according index carried by index_min_cost
;**********************************************************************************************************************************
; try 8 mv via offset
; xmm7 store sad costs
%macro SAD_16x16_LINE_SSE41  4  ; src, ref, stride_src, stride_ref
    movdqa      xmm0, [%1]
    movdqu      xmm1, [%2]
    movdqu      xmm2, [%2+8h]
    movdqa      xmm3, xmm1
    movdqa      xmm4, xmm2

    mpsadbw     xmm1, xmm0, 0   ; 000 B
    paddw       xmm7, xmm1      ; accumulate cost

    mpsadbw     xmm3, xmm0, 5   ; 101 B
    paddw       xmm7, xmm3      ; accumulate cost

    mpsadbw     xmm2, xmm0, 2   ; 010 B
    paddw       xmm7, xmm2      ; accumulate cost

    mpsadbw     xmm4, xmm0, 7   ; 111 B
    paddw       xmm7, xmm4      ; accumulate cost

    add         %1, %3
    add         %2, %4
%endmacro   ; end of SAD_16x16_LINE_SSE41
%macro SAD_16x16_LINE_SSE41E  4 ; src, ref, stride_src, stride_ref
    movdqa      xmm0, [%1]
    movdqu      xmm1, [%2]
    movdqu      xmm2, [%2+8h]
    movdqa      xmm3, xmm1
    movdqa      xmm4, xmm2

    mpsadbw     xmm1, xmm0, 0   ; 000 B
    paddw       xmm7, xmm1      ; accumulate cost

    mpsadbw     xmm3, xmm0, 5   ; 101 B
    paddw       xmm7, xmm3      ; accumulate cost

    mpsadbw     xmm2, xmm0, 2   ; 010 B
    paddw       xmm7, xmm2      ; accumulate cost

    mpsadbw     xmm4, xmm0, 7   ; 111 B
    paddw       xmm7, xmm4      ; accumulate cost
%endmacro   ; end of SAD_16x16_LINE_SSE41E

WELS_EXTERN SampleSad16x16Hor8_sse41
    ;push ebx
    ;push esi
    ;mov eax, [esp+12]  ;   src
    ;mov ecx, [esp+16]  ;   stride_src
    ;mov ebx, [esp+20]  ;   ref
    ;mov edx, [esp+24]  ;   stride_ref
    ;mov esi, [esp+28]  ;   base_cost
    %assign  push_num 0
    LOAD_6_PARA
    PUSH_XMM 8
    SIGN_EXTENSION  r1, r1d
    SIGN_EXTENSION  r3, r3d
    pxor    xmm7,   xmm7

    SAD_16x16_LINE_SSE41    r0, r2, r1, r3
    SAD_16x16_LINE_SSE41    r0, r2, r1, r3
    SAD_16x16_LINE_SSE41    r0, r2, r1, r3
    SAD_16x16_LINE_SSE41    r0, r2, r1, r3

    SAD_16x16_LINE_SSE41    r0, r2, r1, r3
    SAD_16x16_LINE_SSE41    r0, r2, r1, r3
    SAD_16x16_LINE_SSE41    r0, r2, r1, r3
    SAD_16x16_LINE_SSE41    r0, r2, r1, r3

    SAD_16x16_LINE_SSE41    r0, r2, r1, r3
    SAD_16x16_LINE_SSE41    r0, r2, r1, r3
    SAD_16x16_LINE_SSE41    r0, r2, r1, r3
    SAD_16x16_LINE_SSE41    r0, r2, r1, r3

    SAD_16x16_LINE_SSE41    r0, r2, r1, r3
    SAD_16x16_LINE_SSE41    r0, r2, r1, r3
    SAD_16x16_LINE_SSE41    r0, r2, r1, r3
    SAD_16x16_LINE_SSE41E   r0, r2, r1, r3

    pxor    xmm0,   xmm0
    movdqa  xmm6,   xmm7
    punpcklwd   xmm6,   xmm0
    punpckhwd   xmm7,   xmm0

    movdqa  xmm5,   [r4]
    movdqa  xmm4,   xmm5
    punpcklwd   xmm4,   xmm0
    punpckhwd   xmm5,   xmm0

    paddd   xmm4,   xmm6
    paddd   xmm5,   xmm7
    movdqa  xmm3,   xmm4
    pminud  xmm3,   xmm5
    pshufd  xmm2,   xmm3,   01001110B
    pminud  xmm2,   xmm3
    pshufd  xmm3,   xmm2,   10110001B
    pminud  xmm2,   xmm3
    movd    retrd,  xmm2
    pcmpeqd xmm4,   xmm2
    movmskps    r2d, xmm4
    bsf     r1d,    r2d
    jnz near WRITE_INDEX

    pcmpeqd xmm5,   xmm2
    movmskps    r2d, xmm5
    bsf     r1d,    r2d
    add     r1d,    4

WRITE_INDEX:
    mov     [r5],   r1d
    POP_XMM
    LOAD_6_PARA_POP
    ret

;**********************************************************************************************************************************
;
;   uint32_t SampleSad8x8Hor8_sse41( uint8_t *src, int32_t stride_src, uint8_t *ref, int32_t stride_ref, uint16_t base_cost[8], int32_t *index_min_cost )
;
;   \note:
;       src and ref is optional to align with 16 due inter 8x8
;   \return value:
;       return minimal SAD cost, according index carried by index_min_cost
;
;**********************************************************************************************************************************
; try 8 mv via offset
; xmm7 store sad costs
%macro SAD_8x8_LINE_SSE41  4    ; src, ref, stride_src, stride_ref
    movdqu      xmm0, [%1]
    movdqu      xmm1, [%2]
    movdqa      xmm2, xmm1

    mpsadbw     xmm1, xmm0, 0   ; 000 B
    paddw       xmm7, xmm1      ; accumulate cost

    mpsadbw     xmm2, xmm0, 5   ; 101 B
    paddw       xmm7, xmm2      ; accumulate cost

    add         %1, %3
    add         %2, %4
%endmacro   ; end of SAD_8x8_LINE_SSE41
%macro SAD_8x8_LINE_SSE41E  4   ; src, ref, stride_src, stride_ref
    movdqu      xmm0, [%1]
    movdqu      xmm1, [%2]
    movdqa      xmm2, xmm1

    mpsadbw     xmm1, xmm0, 0   ; 000 B
    paddw       xmm7, xmm1      ; accumulate cost

    mpsadbw     xmm2, xmm0, 5   ; 101 B
    paddw       xmm7, xmm2      ; accumulate cost
%endmacro   ; end of SAD_8x8_LINE_SSE41E

WELS_EXTERN SampleSad8x8Hor8_sse41
    %assign  push_num 0
    LOAD_6_PARA
    PUSH_XMM 8
    SIGN_EXTENSION  r1, r1d
    SIGN_EXTENSION  r3, r3d
    movdqa xmm7, [r4]   ;   load base cost list

    SAD_8x8_LINE_SSE41  r0, r2, r1, r3
    SAD_8x8_LINE_SSE41  r0, r2, r1, r3
    SAD_8x8_LINE_SSE41  r0, r2, r1, r3
    SAD_8x8_LINE_SSE41  r0, r2, r1, r3

    SAD_8x8_LINE_SSE41  r0, r2, r1, r3
    SAD_8x8_LINE_SSE41  r0, r2, r1, r3
    SAD_8x8_LINE_SSE41  r0, r2, r1, r3
    SAD_8x8_LINE_SSE41E r0, r2, r1, r3

    phminposuw  xmm0, xmm7  ; horizon search the minimal sad cost and its index
    movd    retrd, xmm0 ; for return: DEST[15:0] <- MIN, DEST[31:16] <- INDEX
    mov     r1d, retrd
    and     retrd, 0xFFFF
    sar     r1d, 16
    mov     [r5], r1d

    POP_XMM
    LOAD_6_PARA_POP
    ret
