
;       xmm0:  input data

;       move xmm0 to xmm1
;       shift left
;       move xmm0 to xmm2
;       shift right
;
;       use punpcldw to convert low bytes to words
;       use pslldq to shift left 2 bytes
;       use psrldq to shift right 2 bytes
;       use pmullw, paddw
;       use cvtpi2ps to convert dwords to float
;       use punpcklwd to convert low words to dwords
;       use punpckhwd to convert high words to dwords

;rdi, rsi, rdx, rcx, r8, r9
;xmm0-7

;caller cleans up stack
;stack aligned on 16 bytes so that local data for functions can be properly
;aligned for SSE, ...

;callee must preserve rbx, rbp, r12-r15

%macro  multipush 1-*
    %rep  %0
        push    %1
        %rotate 1
    %endrep
%endmacro

%macro  multipop 1-*
    %rep %0
        %rotate -1
        pop     %1
    %endrep
%endmacro

;       sobel ( input, output, rows, cols );
;       char input[rows][cols]
;       float output[rows][cols]
;       boundary of the output array will be unfilled
;
        segment .data
rows    equ   20000
cols    equ   20000
dsize   equ   rows*cols
        segment .bss
        
data:   resb dsize
result: resd dsize

        segment .text
        global  sobel, main
sobel:
.cols   equ     0
.rows   equ     8
.output equ     16
.input  equ     24
.bpir   equ     32
.bpor   equ     40
        multipush   rbx, rbx, r12, r13, r14, r15
        sub     rsp, 48
        cmp     rdx, 3
        jle     .noworktodo
        cmp     rcx, 3
        jle     .noworktodo
        mov     [rsp+.input], rdi
        mov     [rsp+.output], rsi
        mov     [rsp+.rows], rdx
        mov     [rsp+.cols], rcx
        mov     [rsp+.bpir], rcx
        imul     rcx, 4
        mov     [rsp+.bpor], rcx

        mov     rax, [rsp+.rows]; count of rows to process
        sub     rax, 2
        mov     r8, [rsp+.input]
        add     r8, rdx
        mov     r9, r8          ; address of row
        mov     r10, r8
        sub     r8, rdx         ; address of first byte of row-1
        add     r10, rdx        ; address of first byte of row+1
        pxor    xmm1, xmm1
        pxor    xmm2, xmm2
        pxor    xmm6, xmm6
        pxor    xmm7, xmm7
        pxor    xmm9, xmm9
        pxor    xmm10, xmm10
        pxor    xmm11, xmm11
        pxor    xmm12, xmm12
        pxor    xmm13, xmm13
        pxor    xmm14, xmm14
        pxor    xmm15, xmm15
.more_rows:
        mov     rbx, 1          ; first column to process
.more_cols:
        ;       8 low data              row-1
        ;       8 low data shifted left
        ;       8 low data shifted right
        ;       8 low data              row
        ;       8 low data shifted left
        ;       8 low data shifted right
        ;       8 low data              row+1
        ;       8 low data shifted left
        ;       8 low data shifted right
        
        movdqu  xmm0, [r8+rbx-1]        ; data for 1st row of 3
        movdqu  xmm1, xmm0
        movdqu  xmm2, xmm0
        psrldq  xmm1, 1
        psrldq  xmm2, 2
        movdqa  xmm3, xmm0
        movdqa  xmm4, xmm1
        movdqa  xmm5, xmm2
        punpcklbw   xmm3, xmm13
        punpcklbw   xmm4, xmm14
        punpcklbw   xmm5, xmm15         ; 8 values for 1st row
        psubw   xmm11, xmm3
        psubw   xmm9, xmm3
        paddw   xmm11, xmm5
        psubw   xmm9, xmm4
        psubw   xmm9, xmm4
        psubw    xmm9, xmm5      ; finished tally for 1st row, 1st 8
        punpckhbw  xmm0, xmm13
        punpckhbw  xmm1, xmm14
        punpckhbw  xmm2, xmm15
        psubw   xmm12, xmm0
        psubw   xmm10, xmm0
        paddw   xmm12, xmm2
        psubw   xmm10, xmm1
        psubw   xmm10, xmm1
        psubw   xmm10, xmm2     ; finished tally for 1st row, last 6

        movdqu  xmm0, [r9+rbx-1]        ; data for 1st row of 3
        ;movdqu  xmm1, xmm0
        movdqu  xmm2, xmm0
        ;psrldq  xmm1, 1
        psrldq  xmm2, 2
        movdqa  xmm3, xmm0
        ;movdqa  xmm4, xmm1
        movdqa  xmm5, xmm2
        punpcklbw   xmm3, xmm13
        ;punpcklbw   xmm4, xmm14
        punpcklbw   xmm5, xmm15         ; 8 values for 1st row
        psubw   xmm11, xmm3
        psubw   xmm11, xmm3
        paddw   xmm11, xmm5
        paddw   xmm11, xmm5
        punpckhbw  xmm0, xmm13
        ;punpckhbw  xmm1, xmm14
        punpckhbw  xmm2, xmm15
        psubw   xmm12, xmm0
        psubw   xmm12, xmm0
        paddw   xmm12, xmm2
        paddw   xmm12, xmm2     ; finished tally for 2nd row, last 6

        movdqu  xmm0, [r10+rbx-1]        ; data for 3rd row of 3
        movdqu  xmm1, xmm0
        movdqu  xmm2, xmm0
        psrldq  xmm1, 1
        psrldq  xmm2, 2
        movdqa  xmm3, xmm0
        movdqa  xmm4, xmm1
        movdqa  xmm5, xmm2
        punpcklbw   xmm3, xmm13
        punpcklbw   xmm4, xmm14
        punpcklbw   xmm5, xmm15         ; 8 values for 3rd row
        psubw   xmm11, xmm3
        paddw   xmm9, xmm3
        paddw   xmm11, xmm5
        paddw   xmm9, xmm4
        paddw   xmm9, xmm4
        paddw   xmm9, xmm5      ; finished tally for 3rd row, 1st 8
        punpckhbw  xmm0, xmm13
        punpckhbw  xmm1, xmm14
        punpckhbw  xmm2, xmm15
        psubw   xmm12, xmm0
        paddw   xmm10, xmm0
        paddw   xmm12, xmm2
        paddw   xmm10, xmm1
        paddw   xmm10, xmm1
        paddw   xmm10, xmm2     ; finished tally for 3rd row, last 6

        add     rbx, 14         ; process 14 Sobel values
        cmp     rbx, rdx
        jl      .more_cols
        
        add     r8, rdx
        add     r9, rdx
        add     r10, rdx
        dec     rax             ; 1 fewer row to process
        jp      .more_rows
.noworktodo:
        add     rsp, 48
        multipop    rbx, rbx, r12, r13, r14, r15
        ret

main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 32

        mov     rcx, dsize
        lea     rdi, [data]
        mov     al, 255
.more:
        inc     al
        stosb
        dec     rcx
        jnz     .more

        lea     rdi, [data]
        lea     rsi, [result]
        mov     rdx, rows
        mov     rcx, cols
        call    sobel

        leave
        ret
