;
;  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
;
;  Use of this source code is governed by a BSD-style license
;  that can be found in the LICENSE file in the root of the source
;  tree. An additional intellectual property rights grant can be found
;  in the file PATENTS.  All contributing project authors may
;  be found in the AUTHORS file in the root of the source tree.
;


%include "vpx_ports/x86_abi_support.asm"

;void vp8_short_fdct4x4_mmx(short *input, short *output, int pitch)
global sym(vp8_short_fdct4x4_mmx)
sym(vp8_short_fdct4x4_mmx):
    push        rbp
    mov         rbp,        rsp
    SHADOW_ARGS_TO_STACK 3
    GET_GOT     rbx
    push        rsi
    push        rdi
    ; end prolog

        mov         rsi,        arg(0)      ; input
        mov         rdi,        arg(1)      ; output

        movsxd      rax,        dword ptr arg(2) ;pitch

        lea         rcx,        [rsi + rax*2]
        ; read the input data
        movq        mm0,        [rsi]
        movq        mm1,        [rsi + rax]

        movq        mm2,        [rcx]
        movq        mm4,        [rcx + rax]

        ; transpose for the first stage
        movq        mm3,        mm0         ; 00 01 02 03
        movq        mm5,        mm2         ; 20 21 22 23

        punpcklwd   mm0,        mm1         ; 00 10 01 11
        punpckhwd   mm3,        mm1         ; 02 12 03 13

        punpcklwd   mm2,        mm4         ; 20 30 21 31
        punpckhwd   mm5,        mm4         ; 22 32 23 33

        movq        mm1,        mm0         ; 00 10 01 11
        punpckldq   mm0,        mm2         ; 00 10 20 30

        punpckhdq   mm1,        mm2         ; 01 11 21 31

        movq        mm2,        mm3         ; 02 12 03 13
        punpckldq   mm2,        mm5         ; 02 12 22 32

        punpckhdq   mm3,        mm5         ; 03 13 23 33

        ; mm0 0
        ; mm1 1
        ; mm2 2
        ; mm3 3

        ; first stage
        movq        mm5,        mm0
        movq        mm4,        mm1

        paddw       mm0,        mm3         ; a1 = 0 + 3
        paddw       mm1,        mm2         ; b1 = 1 + 2

        psubw       mm4,        mm2         ; c1 = 1 - 2
        psubw       mm5,        mm3         ; d1 = 0 - 3

        psllw       mm5,        3
        psllw       mm4,        3

        psllw       mm0,        3
        psllw       mm1,        3

        ; output 0 and 2
        movq        mm2,        mm0         ; a1

        paddw       mm0,        mm1         ; op[0] = a1 + b1
        psubw       mm2,        mm1         ; op[2] = a1 - b1

        ; output 1 and 3
        ; interleave c1, d1
        movq        mm1,        mm5         ; d1
        punpcklwd   mm1,        mm4         ; c1 d1
        punpckhwd   mm5,        mm4         ; c1 d1

        movq        mm3,        mm1
        movq        mm4,        mm5

        pmaddwd     mm1,        MMWORD PTR[GLOBAL (_5352_2217)]    ; c1*2217 + d1*5352
        pmaddwd     mm4,        MMWORD PTR[GLOBAL (_5352_2217)]    ; c1*2217 + d1*5352

        pmaddwd     mm3,        MMWORD PTR[GLOBAL(_2217_neg5352)]  ; d1*2217 - c1*5352
        pmaddwd     mm5,        MMWORD PTR[GLOBAL(_2217_neg5352)]  ; d1*2217 - c1*5352

        paddd       mm1,        MMWORD PTR[GLOBAL(_14500)]
        paddd       mm4,        MMWORD PTR[GLOBAL(_14500)]
        paddd       mm3,        MMWORD PTR[GLOBAL(_7500)]
        paddd       mm5,        MMWORD PTR[GLOBAL(_7500)]

        psrad       mm1,        12          ; (c1 * 2217 + d1 * 5352 +  14500)>>12
        psrad       mm4,        12          ; (c1 * 2217 + d1 * 5352 +  14500)>>12
        psrad       mm3,        12          ; (d1 * 2217 - c1 * 5352 +   7500)>>12
        psrad       mm5,        12          ; (d1 * 2217 - c1 * 5352 +   7500)>>12

        packssdw    mm1,        mm4         ; op[1]
        packssdw    mm3,        mm5         ; op[3]

        ; done with vertical
        ; transpose for the second stage
        movq        mm4,        mm0         ; 00 10 20 30
        movq        mm5,        mm2         ; 02 12 22 32

        punpcklwd   mm0,        mm1         ; 00 01 10 11
        punpckhwd   mm4,        mm1         ; 20 21 30 31

        punpcklwd   mm2,        mm3         ; 02 03 12 13
        punpckhwd   mm5,        mm3         ; 22 23 32 33

        movq        mm1,        mm0         ; 00 01 10 11
        punpckldq   mm0,        mm2         ; 00 01 02 03

        punpckhdq   mm1,        mm2         ; 01 22 12 13

        movq        mm2,        mm4         ; 20 31 30 31
        punpckldq   mm2,        mm5         ; 20 21 22 23

        punpckhdq   mm4,        mm5         ; 30 31 32 33

        ; mm0 0
        ; mm1 1
        ; mm2 2
        ; mm3 4

        movq        mm5,        mm0
        movq        mm3,        mm1

        paddw       mm0,        mm4         ; a1 = 0 + 3
        paddw       mm1,        mm2         ; b1 = 1 + 2

        psubw       mm3,        mm2         ; c1 = 1 - 2
        psubw       mm5,        mm4         ; d1 = 0 - 3

        pxor        mm6,        mm6         ; zero out for compare

        pcmpeqw     mm6,        mm5         ; d1 != 0

        pandn       mm6,        MMWORD PTR[GLOBAL(_cmp_mask)]   ; clear upper,
                                                                ; and keep bit 0 of lower

        ; output 0 and 2
        movq        mm2,        mm0         ; a1

        paddw       mm0,        mm1         ; a1 + b1
        psubw       mm2,        mm1         ; a1 - b1

        paddw       mm0,        MMWORD PTR[GLOBAL(_7w)]
        paddw       mm2,        MMWORD PTR[GLOBAL(_7w)]

        psraw       mm0,        4           ; op[0] = (a1 + b1 + 7)>>4
        psraw       mm2,        4           ; op[8] = (a1 - b1 + 7)>>4

        movq        MMWORD PTR[rdi + 0 ],  mm0
        movq        MMWORD PTR[rdi + 16],  mm2

        ; output 1 and 3
        ; interleave c1, d1
        movq        mm1,        mm5         ; d1
        punpcklwd   mm1,        mm3         ; c1 d1
        punpckhwd   mm5,        mm3         ; c1 d1

        movq        mm3,        mm1
        movq        mm4,        mm5

        pmaddwd     mm1,        MMWORD PTR[GLOBAL (_5352_2217)]    ; c1*2217 + d1*5352
        pmaddwd     mm4,        MMWORD PTR[GLOBAL (_5352_2217)]    ; c1*2217 + d1*5352

        pmaddwd     mm3,        MMWORD PTR[GLOBAL(_2217_neg5352)]  ; d1*2217 - c1*5352
        pmaddwd     mm5,        MMWORD PTR[GLOBAL(_2217_neg5352)]  ; d1*2217 - c1*5352

        paddd       mm1,        MMWORD PTR[GLOBAL(_12000)]
        paddd       mm4,        MMWORD PTR[GLOBAL(_12000)]
        paddd       mm3,        MMWORD PTR[GLOBAL(_51000)]
        paddd       mm5,        MMWORD PTR[GLOBAL(_51000)]

        psrad       mm1,        16          ; (c1 * 2217 + d1 * 5352 +  14500)>>16
        psrad       mm4,        16          ; (c1 * 2217 + d1 * 5352 +  14500)>>16
        psrad       mm3,        16          ; (d1 * 2217 - c1 * 5352 +   7500)>>16
        psrad       mm5,        16          ; (d1 * 2217 - c1 * 5352 +   7500)>>16

        packssdw    mm1,        mm4         ; op[4]
        packssdw    mm3,        mm5         ; op[12]

        paddw       mm1,        mm6         ; op[4] += (d1!=0)

        movq        MMWORD PTR[rdi + 8 ],  mm1
        movq        MMWORD PTR[rdi + 24],  mm3

     ; begin epilog
    pop         rdi
    pop         rsi
    RESTORE_GOT
    UNSHADOW_ARGS
    pop         rbp
    ret

SECTION_RODATA
align 8
_5352_2217:
    dw 5352
    dw 2217
    dw 5352
    dw 2217
align 8
_2217_neg5352:
    dw 2217
    dw -5352
    dw 2217
    dw -5352
align 8
_cmp_mask:
    times 4 dw 1
align 8
_7w:
    times 4 dw 7
align 8
_14500:
    times 2 dd 14500
align 8
_7500:
    times 2 dd 7500
align 8
_12000:
    times 2 dd 12000
align 8
_51000:
    times 2 dd 51000
