; YASM x86-64 assembly language code for PAQ7/8 ver. 2, Jan 18, 2007
;
; (C) 2005-2007, Matt Mahoney, Matthew Fite.
; This is free software under GPL, http://www.gnu.org/licenses/gpl.txt
;
; This code was tested on an Athlon-64 under Ubuntu Linux 2.6.15.27.amd64-generic
; with paq8f and paq8jd.  It should work with any PAQ version since paq7,
; because all versions use the same paq7asm.asm code for 32 bit Windows/Linux
; versions.  To compile e.g. paq8jd in Linux:
;
;   yasm paq7asm-x86_64.asm -f elf -m amd64
;   g++ -O3 -s -fomit-frame-pointer -DUNIX paq8jd.cpp paq7asm-x86_64.o -o paq8jd
;
; This code has not been tested in Windows.  (You would need XP Professional
; 64 bit edition and a 64 bit compiler).

section .text

BITS 64

; Vector product a*b of n signed words, returning signed dword scaled
; down by 8 bits. n is rounded up to a multiple of 8.

    global dot_product ; (short* a, short* b, int n)
    align 16
dot_product:
    mov rcx, rdx        ; n
    mov rax, rdi        ; a
    mov rdx, rsi        ; b
    add rcx, 7          ; n rounding up
    and rcx, -8
    jz .done
    sub rax, 16
    sub rdx, 16
    pxor xmm0, xmm0     ; sum = 0
.loop:                  ; each loop sums 4 products
    movdqa xmm1, [rax+rcx*2] ; put parital sums of vector product in xmm1
    pmaddwd xmm1, [rdx+rcx*2]
    psrad xmm1, 8
    paddd xmm0, xmm1
    sub rcx, 8
    ja .loop
    movdqa xmm1, xmm0      ; add 4 parts of xmm0 and return in eax
    psrldq xmm1, 8
    paddd xmm0, xmm1
    movdqa xmm1, xmm0
    psrldq xmm1, 4
    paddd xmm0, xmm1
    movd rax, xmm0
.done
    ret

; Train n neural network weights w[n] on inputs t[n] and err.
; w[i] += (t[i]*err*2 >> 16)+1 >> 1 bounded to +- 32K.
; n is rounded up to a multiple of 8.

;1st arg rdi -> *t
;2nd arg rsi -> *w
;3rd arg rdx ->  n
;4th arg rcx ->  err (signed 16 bits)

    global train ; (short* t, short* w, int n, int err)
    BITS 64
    align 16
train:
    mov rax, rcx          ; err
    and rax, 0xffff       ; put 8 copies of err in xmm0
    movd xmm0, rax
    movd xmm1, rax
    pslldq xmm1, 2
    por xmm0, xmm1
    movdqa xmm1, xmm0
    pslldq xmm1, 4
    por xmm0, xmm1
    movdqa xmm1, xmm0
    pslldq xmm1, 8
    por xmm0, xmm1;
    pcmpeqb xmm1, xmm1    ; 8 copies of 1 in xmm1
    psrlw xmm1, 15
    mov rcx, rdx          ; n
    mov rax, rdi          ; t
    mov rdx, rsi          ; w
    add rcx, 7            ; n/8 rounding up
    and rcx, -8
    sub rax, 16
    sub rdx, 16
    jz .done
    align 16
.loop:                     ; each iteration adjusts 8 weights
    movdqa xmm2, [rdx+rcx*2] ; w[i]
    movdqa xmm3, [rax+rcx*2] ; t[i]
    paddsw xmm3, xmm3      ; t[i]*2
    pmulhw xmm3, xmm0      ; t[i]*err*2 >> 16
    paddsw xmm3, xmm1      ; (t[i]*err*2 >> 16)+1
    psraw xmm3, 1          ; (t[i]*err*2 >> 16)+1 >> 1
    paddsw xmm2, xmm3      ; w[i] + xmm3
    movdqa [rdx+rcx*2], xmm2
    sub rcx, 8
    ja .loop
.done:
    ret
