
#AutoIt3Wrapper_UseX64=n

;n = Script assembliert den Code
;y = Script läuft im 64-Bit-Modus


#include <winapi.au3>
#include "assembleit2.au3"


#cs _nbodysse

    use64                                                   ;64 Bit-Modus

    sub rsp,4                                              ;platz auf dem stack 4 Byte
    STMXCSR dword[rsp]                                     ;Registerinhalt laden
    xor eax,eax
    or dword[rsp],00000000000000001000100101000000b        ;Bit 15 flush to zero  FTZ ,Bit 6 denormals are zero DAZ  = 0x8040
    ;    and dword[rsp],00000000000000000111011010111111b       ;FTZ und DAZ ausschalten !Bit 15 flush to zero  FTZ ,Bit 6 denormals are zero DAZ  = 0x8040
    LDMXCSR dword[rsp]                                     ;register speichern
    mov eax,dword[rsp]                                     ;wenn ret, rückgabe prüfen
    add rsp,4                                              ;stack cleanen


    push rdi                                                ;nichtflüchtige register retten
    push rsi
    push rbx
    push rbp


    ;timer initialisieren
    xor rsi,rsi
    xor rax,rax


    push rax
    push rdx
    push rcx
    cpuid
    pop rcx
    pop rdx
    pop rax
    rdtsc                                                  ;timer abfragen EDX:EAX -> RSI

    mov esi,edx                                             ;speichern
    db 0x48
    shl rsi,32
    db 0x48
    or rsi,rax


    mov r8d,ecx                                             ;ptr auf die parameter-struct
    mov ecx,[r8d+52]                                           ;TO
    mov r15d,[r8d+12]                                            ;anzahl partikel


    mov r9d,[r8d+16]                                        ;x_struct anfang
    mov r10d,[r8d+20]                                       ;y_struct
    mov r11d,[r8d+24]                                       ;z_struct

    ;~     prefetchnta [r9d+ebx*4]
    ;~     prefetchnta [r10d+ebx*4]


    mov r12d,[r8d+28]                                       ;vx_struct anfang
    mov r13d,[r8d+32]                                       ;vy_struct
    mov r14d,[r8d+36]                                       ;vz_struct

    mov edx,[r8d+40]                                          ;ausgleich
    movd xmm4,edx
    pshufd xmm4,xmm4,0

    mov edx,[r8d+44]                                          ;dt
    movd xmm3,edx
    pshufd xmm3,xmm3,0

    sub ecx,1
    align 16
    loop1:                                                     ;for i=1 to anzahl partikel

    ;partikel
    movd xmm11,[r9d+ecx*4]                                  ;anfang x_struct
    pshufd xmm11,xmm11,0                                        ;$x = DllStructGetData($x_struct, 1, $i) ;x-koordinate
    movd xmm12,[r10d+ecx*4]                                 ;anfang y_struct
    pshufd xmm12,xmm12,0                                        ;$y = DllStructGetData($y_struct, 1, $i) ;y-koordinate
    movd xmm13,[r11d+ecx*4]                                 ;anfang z_struct
    pshufd xmm13,xmm13,0                                        ;$z = DllStructGetData($z_struct, 1, $i) ;z-koordinate
    ;register sind mit 4 identischen floats gefüllt



    mov ebx,0                                              ;inner loop

    xorps xmm8,xmm8                                         ;Fx=0
    xorps xmm9,xmm9                                         ;Fy=0
    xorps xmm10,xmm10                                       ;Fz=0

    align 16
    loop2:                                                    ;for j=1 to anzahl partikel inner loop

    movaps xmm5,[r9d+ebx*4]                                        ;$xj = DllStructGetData($x_struct, 1, $j) ;x-koordinate
    movaps xmm6,[r10d+ebx*4]                                        ;$yj = DllStructGetData($y_struct, 1, $j) ;y-koordinate
    movaps xmm7,[r11d+ebx*4]                                        ;$zj = DllStructGetData($z_struct, 1, $j) ;z-koordinate

    subps xmm5,xmm11                                           ;dx=xj-x

    subps xmm6,xmm12                                           ;dy=yj-y
    subps xmm7,xmm13                                         ;dz=zj-z

    movdqa xmm0,xmm5                                        ;dx
    movdqa xmm1,xmm6                                        ;dy
    movdqa xmm2,xmm7                                        ;dz


    mulps xmm5,xmm5                                           ;dx^2
    mulps xmm6,xmm6                                           ;dy^2
    mulps xmm7,xmm7                                           ;dz^2

    addps xmm5,xmm6                                         ;quad=dx^2+dy^2
    addps xmm5,xmm7                                           ;quad=dx^2+dy^2+dz^2
    addps xmm5,xmm4                                           ;quad=dx^2+dy^2+dz^2+ausgleich


    rsqrtps xmm5,xmm5                                         ;1/sqrt(quad)
    movdqa xmm6,xmm5
    mulps xmm5,xmm5                                           ;1quad=1/(quad*sqrt(quad))   = (1/sqrt(quad))^3
    mulps xmm5,xmm6                                           ;1quad=1/(quad*sqrt(quad))

    mulps xmm0,xmm5                                           ;Fx+=dx*1quad
    mulps xmm1,xmm5                                           ;Fy+=dy*1quad
    mulps xmm2,xmm5                                           ;Fz+=dz*1quad

    addps xmm8,xmm0
    addps xmm9,xmm1
    addps xmm10,xmm2


    add ebx,4                                               ;adresse nächstes Particle
    cmp ebx,r15d                                               ;inner loop zuende? alle Partikel berechnet
    jne loop2


    mulps xmm8,xmm3                                         ;Fx=FX*dt
    mulps xmm9,xmm3                                         ;Fx=FX*dt
    mulps xmm10,xmm3                                        ;Fx=FX*dt


    ;die x-,y-, und z-geschwindigkeiten in die struct eintragen
    ;anfang vx_struct
    movd xmm5,dword[r12d+ecx*4]                              ;vx alt
    movd xmm6,dword[r13d+ecx*4]                              ;vy alt
    movd xmm7,dword[r14d+ecx*4]                              ;vz alt

    addps xmm8,xmm5
    addps xmm9,xmm6
    addps xmm10,xmm7

    haddps xmm8,xmm8                                        ;  vxi =  vxi +FX
    haddps xmm9,xmm9                                        ;  Fy = Fy+ vyi
    haddps xmm10,xmm10                                        ;  Fx = FX + vxi

    haddps xmm8,xmm8                                        ;  Fx = FX + vxi
    haddps xmm9,xmm9                                        ;  Fy = Fy + vyi
    haddps xmm10,xmm10                                        ;  Fx = FX + vxi

    movd dword[r12d+ecx*4],xmm8                              ;vxi =FX
    movd dword[r13d+ecx*4],xmm9                              ;vyi =F
    movd dword[r14d+ecx*4],xmm10                              ;vxi =F

    sub ecx,1
    cmp ecx,dword[r8d+48]                                      ;letztes Partikel erreicht?
    jge loop1


    ;alle x- y- und z-koordinaten in die struct schreiben

    mov ebx,[r8d+52]                                       ;TO
    sub ebx,4

    loop3:

    align 16
    movaps xmm5,[r9d+ebx*4]                                        ;$x = DllStructGetData($x_struct, 1, $j) ;x-koordinate
    movaps xmm6,[r10d+ebx*4]                                        ;$y = DllStructGetData($y_struct, 1, $j) ;y-koordinate
    movaps xmm7,[r11d+ebx*4]                                        ;$z = DllStructGetData($z_struct, 1, $j) ;z-koordinate

    movaps xmm0,[r12d+ebx*4]                                ;vx
    movaps xmm1,[r13d+ebx*4]                                ;vy
    movaps xmm2,[r14d+ebx*4]                                ;vz

    mulps xmm0,xmm3                                         ;vx*dt
    mulps xmm1,xmm3                                         ;vy*dt
    mulps xmm2,xmm3                                         ;vz*dt

    addps xmm5,xmm0                                         ;x=x+vx*dt
    addps xmm6,xmm1                                         ;x=x+vx*dt
    addps xmm7,xmm2                                         ;x=x+vx*dt

    ;~     movaps  [r9d+ebx*4],xmm5                                ;x speichern
    ;~     movaps  [r10d+ebx*4],xmm6
    ;~     movaps  [r11d+ebx*4],xmm7

    movntdq  [r9d+ebx*4],xmm5                                ;x speichern
    movntdq  [r10d+ebx*4],xmm6
    movntdq  [r11d+ebx*4],xmm7

    sub ebx,4
    cmp ebx,[r8d+48]
    jge loop3


    xor rax,rax

    push rax
    push rdx
    push rcx
    cpuid
    pop rcx
    pop rdx
    pop rax
    rdtsc                                                  ;timer abfragen

    mov edi,edx
    db 0x48
    shl rdi,32                                             ;EDX >highdword RDI
    db 0x48
    or rdi,rax                                             ;EAX >lovwords RDI
    db 0x48
    sub rdi,rsi
    db 0x48
    mov [r8d+56],rdi                                       ;ticks speichern, getexitcodethread gibt nur 32 bit zurück....
    ;anzahl takte

    pop rbp
    pop rbx
    pop rsi
    pop rdi

    ret


#ce





#cs _nbodyplot
    ;errechnet die Pixeladressen aus der x- und y-struct und setzt die entsprechende Farbe
    use64                                                   ;64 Bit-Modus

    push rdi                                                ;nichtflüchtige register retten
    push rsi
    push rbx
    push rbp

    xor eax,eax                                            ;löschen
    mov r8d,ecx                                             ;ptr auf die übergebene parameter-struct
    mov r14d,edx                                             ;zweiter parameter ptr auf die color-struct


    mov r13d,[r8d+12]                                        ;anzahl partikel(uint)
    mov r9d,[r8d+16]                                        ;x_struct anfang (uint)
    mov r10d,[r8d+20]                                       ;y_struct anfang (uint)
    mov r11d,[r8d]                                          ;offset Bitmap (uint)
    mov r12d,[r8d+4]                                        ;w = breite und hoehe Bitmap (float)

    mov eax,0xBF800000                                     ;float -1.0
    movd xmm3,eax
    pshufd xmm3,xmm3,0                                     ;-1.0,-1.0,-1.0,-1.0

    mov eax,0x3F800000                                     ;float 1.0
    movd xmm4,eax
    pshufd xmm4,xmm4,0                                     ;1.0,1.0,1.0,1.0

    movd xmm0,r12d                                         ;w
    cvttps2dq xmm0,xmm0                                     ;float konvertieren in integer  int(w)
    movd eax,xmm0                                          ;w
    mul eax                                                ;w*w
    shl eax,2                                              ;w*w*4  maximale adresse bitmap
    mov r15d,eax                                           ;uint

    mov rdi,0
    mov rdx,0

    align 16
    Loop1:                                                  ;alle koordinaten durchlaufen, immer 4 gleichzeitig bearbeiten
    sub r13d,4

    ;Speicherstelle pixel in Bitmap =  Int($y * $w) * $w + Int($x * $w)
    movaps xmm0,[r9d+r13d*4]                                 ;x-koordinaten

    movaps xmm5,xmm0                                       ;x-koordinaten
    pcmpgtd xmm5,xmm3                                      ;koordinate > -1.0? wenn ja 1, ansonsten 0

    movaps xmm6,xmm0                                       ;x-koordinaten
    pcmpgtd xmm6,xmm4                                      ;koordinate > 1.0? wenn ja 1, ansonsten 0
    andnps xmm6,xmm0                                       ;negieren , nur anzeigen wenn < 1.0
    movaps xmm0,xmm6                                       ;nur wenn koordinate < 1.0
    andps xmm0,xmm5                                        ;nur wenn koordinate > -1.0

    movaps xmm1,[r10d+r13d*4]                                ;y-koordinaten
    movd xmm2,r12d                                          ;breite w
    pshufd xmm2,xmm2,0                                      ;breite in alle 4 floats

    ;4 adressen  = int(x*w)+int(y*w)*w
    mulps xmm0,xmm2                                         ;x*w
    mulps xmm1,xmm2                                         ;y*w

    cvttps2dq xmm0,xmm0                                     ;float konvertieren in integer  int(x*w)
    cvttps2dq xmm1,xmm1                                     ;float konvertieren in integer  int(y*w)
    cvttps2dq xmm2,xmm2                                     ;float konvertieren in integer  int(w)

    movdqa xmm3,xmm1                                       ;int(y*w) sichern
    pmuludq xmm3,xmm2                                      ;erster teil von int(y*w)    0,y2*w,0,y4*w
    pshufd xmm1,xmm1, 245                                  ;dwords verschieben     y1,y1,y3,y3
    pmuludq xmm1,xmm2                                      ;zweiter teil vonint(y*w)   0,y1*w,0,y3*w
    pshufd xmm1,xmm1,145                                   ;y1+w,0,y3*w,0
    paddd xmm1,xmm3                                        ;y1*w,y2*w,y3*w,y4*w
    paddd xmm0,xmm1                                         ;4 adressen  = int(x*w)+int(y*w)*w
    pslld xmm0,2                                           ;adresse *4, da jedes pixel 4 byte hat

    ;farbe aus colorstruct an bitmapadresse schreiben
    mov rcx,4
    loop2:
    movd edi,xmm0                                           ;pixeladresse nach edi
    cmp edi,0                                              ;adresse kleiner null?
    jle ende                                                ;nicht mehr im Bild, kein pixel anzeigen
    cmp edi,r15d                                           ;adresse grösser als w*w*4?
    jge ende                                                ;nicht mehr im Bild, kein pixel anzeigen

    add edi,r11d                                            ;offset bitmap dazuaddieren
    mov ebx,r13d                                            ;offset in colorstruct
    add ebx,ecx                                             ;adresse anpassen
    sub ebx,1
    mov eax,[r14d+ebx*4]                                      ;color pixel
    stosd                                                  ;   mov dword[edi],eax                                          ;pixelfarbe an adresse
    ende:
    pshufd xmm0,xmm0,147                                    ;shift right 16, die pixeladressen nach edi
    loop loop2                                              ;ecx=ecx-1   if ecx>0 jmp loop2

    cmp r13d,0                                             ;letztes particle erreicht?
    jg Loop1


    pop rbp
    pop rbx
    pop rsi
    pop rdi

    ret

#ce


;das wars schon....


If Not @AutoItX64 Then                                     ;assemblieren, wenn kein 64-bitmodus
    ;Code assemblieren, nur im 32-Bit Modus möglich!
    ;in erste Scripteile: #AutoIt3Wrapper_UseX64=n

    $binarycode = _AssembleIt2("retbinary", "_nbodysse")   ;gibt nur den code zurück
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binarycode = ' & $binarycode & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

    FileDelete("nbodysse64.bin")
    FileWrite("nbodysse64.bin", $binarycode)


    $binarycode = _AssembleIt2("retbinary", "_nbodyplot")  ;gibt nur den code zurück
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binarycode = ' & $binarycode & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

    FileDelete("nbodyplot.bin")
    FileWrite("nbodyplot.bin", $binarycode)

    Exit (MsgBox(0, "n-Body", "Assembling is done, now you should change the first line to " & @CRLF & "#AutoIt3Wrapper_UseX64=y" & @CRLF & " and start the script again!"))


Else                                                       ;ausführen, wenn 64 bit
    ;Code ausführen im 64-Bit-Modus
    ;in erste Scriptzeile: #AutoIt3Wrapper_UseX64=y

    If Not (FileExists(@ScriptDir & "\nbodysse64.bin") And FileExists(@ScriptDir & "\nbodyplot.bin")) Then ;Dateien noch nicht assembliert
        Exit (MsgBox(0, "n-Body", "Now you should change the first line to " & @CRLF & "#AutoIt3Wrapper_UseX64=n" & @CRLF & " and start the script again!"))
    EndIf

    $binarycode = FileRead("nbodysse64.bin")               ;code in Speicher schreiben
    Global $tCodeBuffer = _dllstructcreate64("byte[" & StringLen($binarycode) * 2 + 2 & "]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1, $binarycode)         ;write opcodes into memory
    ;msgbox(0,0,dllstructgetptr($tCodeBuffer))
    $binarycode = FileRead("nbodyplot.bin")                ;code in Speicher schreiben
    Global $tCodeBuffer_plot = _dllstructcreate64("byte[" & StringLen($binarycode) * 2 + 2 & "]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer_plot, 1, $binarycode)    ;write opcodes into memory


    Global $w = 800                                        ;Fenstergröße
    Global $h = $w
    Global $ausgleich = 1E-10                              ;falls mehrere partikel dieselben parameter haben
    Global $dt = 0.000015                                  ;Zeit


    ;***********************************************************************
    Global $particles = 30000                              ;20000                              ;anzahl partikel
    Global $cleanflag = 1                                  ;0=weg zeigen, 1= nur pixel
    Global $number_Threads = 4
    Global $faktor = $particles / 30                       ;Anpassung Rotationsgeschwindigkeit
    ;***********************************************************************

    $particles -= Mod($particles, 4 * $number_Threads)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : Mod($particles, 4 * $number_Threads) = ' & Mod($particles, 4 * $number_Threads) & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $particles = ' & $particles & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

    $from = 0                                              ; bei mehreren Threads werden die Berechnungen auf die Threads verteilt
    $to = $particles

    If Mod($particles, 4 * $number_Threads) <> 0 Then Exit (MsgBox(0, "n-Body Simulation", "$particles must be a multiple of 4*$number_Threads!"))

    Dim $para_struct[$number_Threads]                      ;jeder thread bekommt seine eigene struct
    Dim $param[$number_Threads]                            ;pointer auf die parameter-struct

    Dim $Thread[$number_Threads]                           ;Threads
    Dim $Threadexit[$number_Threads]                       ;exit-code der Threads
    Global $minest_ticks = 1e22, $maxest_interacts, $maxest_clockticks, $clockticks, $bench, $bench_ticks
    Global $bench_Interacts, $clockticks, $time, $bench_time, $benchtime_fastest = 1e22, $cpuspeed, $cpuspeed_max

    ;CPU-Stuff for Benchmarking, BIG thx to UEZ
    Global $iCPUCores = _WinAPI_GetCPUCoreCount(), $sStructPPI, $hDLLPowrProf = DllOpen("PowrProf.dll"), $i
    For $i = 1 To $iCPUCores
        $sStructPPI &= "ulong Number                            ;ulong MaxMhz;ulong CurrentMhz;ulong MhzLimit;ulong MaxIdleState;ulong CurrentIdleState;"
    Next
    Global $tPROCESSOR_POWER_INFORMATION = DllStructCreate($sStructPPI)
    Global $aTotalCPUCurInfo, $aCPUsCurInfo, $aPercents
    Global $CPU_Name = RegRead("HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\0", "ProcessorNameString")
;~ If @error Then Exit ConsoleWrite("Error calling _CPUGetIndividualProcessorTimes():" & @error & ", @extended = " & @extended & @CRLF)
    Global $aTotalCPUPrevInfo = _CPUGetTotalProcessorTimes()
    If @error Then Exit ConsoleWrite("Error calling _CPUGetTotalProcessorTimes():" & @error & ", @extended = " & @extended & @CRLF)


    $gui = GUICreate("n-body", $w, $h, 0, 0)
    AutoItSetOption("GUIOnEventMode", 1)
    GUISetOnEvent(-3, "Ende")
    GUISetState()
    $DC_gui = _WinAPI_GetDC($gui)                          ;DeviceContext GUI holen
    Global $ptr1, $hbmp1
    $DC_bmp = _CreateNewBmp32($w, $h, $ptr1, $hbmp1)       ;leere Bitmap
;~     ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $ptr1 = ' & $ptr1 & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

    $bmp_struct = DllStructCreate("dword[" & $w * $h & "]", $ptr1)
    $leer_struct = DllStructCreate("byte[" & $w * $h * 4 & "]", $ptr1)

    $leerstring = ""                                       ;string mit w*w*4 chr(0) zum löschen der Bitmap ;o)
    Do
        $leerstring &= $leerstring & Chr(0)
    Until StringLen($leerstring) > $w * $h * 4
    $leerstring = StringLeft($leerstring, $w * $h * 4)


    $col_struct = DllStructCreate("int[" & $particles & "]") ;Farben
    ;struct of arrays ist wesentlich schneller als array of structs (später mit asm-SIMD oder c++)
    $x_struct = DllStructCreate("float [" & $particles & "]") ;Koordinaten
    $y_struct = DllStructCreate("float [" & $particles & "]") ;
    $z_struct = DllStructCreate("float [" & $particles & "]") ;
    $vx_struct = DllStructCreate("float [" & $particles & "]") ;Geschwindigkeiten
    $vy_struct = DllStructCreate("float [" & $particles & "]") ;
    $vz_struct = DllStructCreate("float [" & $particles & "]") ;


    $pi = 4 * ATan(1)                                      ;  zwei Kreisförmige Partikelwolken erstellen
    For $i = 1 To $particles
        $a = Random(-$pi, $pi)
        $b = Random(-$pi, $pi)                             ;    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $b = ' & $b & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
        If $a > $b Then                                    ;swap
            $c = $a
            $a = $b
            $b = $c
        EndIf
        ;    $z=random(-1,1)*$w/3*sin($t)
        If Mod($i, 2) Then                                 ;zwei Wolken
            $u = 0.3
            DllStructSetData($col_struct, 1, 0xFF000000 + Random(0, 0x00FFFF, 1), $i) ;zufällige Farben blau
        Else
            $u = 0.7
            DllStructSetData($col_struct, 1, 0xFF000000 + Random(0xFF00FF, 0xFFFF00, 1), $i) ;zufällige Farben rot
        EndIf
        $t = Random($a, $b)
        $x = $u + Random(-1, 1) * 1 / 3 * Sin($t)
        $y = $u + Random(-1, 1) * 1 / 3 * Cos($t)
        DllStructSetData($x_struct, 1, $x, $i)             ;koordinaten mit zufälligen floats füllen
        DllStructSetData($y_struct, 1, $y, $i)             ;
        DllStructSetData($z_struct, 1, Random(-0.2, .2), $i) ;

        $vx = ($y - $u) * $faktor
        $vy = -($x - $u) * $faktor
        DllStructSetData($vx_struct, 1, $vx, $i)
        DllStructSetData($vy_struct, 1, $vy, $i)
    Next


    For $i = 0 To $number_Threads - 1
        $para_struct[$i] = DllStructCreate("dword BITMAP;" & _
                "float W; float SIZE; dword PARTICLES;" & _
                "dword XSTRUCT; dword YSTRUCT; dword ZSTRUCT;" & _
                "dword vxSTRUCT ; dword vySTRUCT; dword vzSTRUCT; float AUSGLEICH;" & _
                "float DT; dword FROM; dword TO; uint64 TICKS")

        DllStructSetData($para_struct[$i], "BITMAP", DllStructGetPtr($bmp_struct)) ;eax
        DllStructSetData($para_struct[$i], "W", $w)        ;eax+4
        DllStructSetData($para_struct[$i], "SIZE", $w * $w * 4) ;eax+8
        DllStructSetData($para_struct[$i], "PARTICLES", $particles) ;eax+12
        DllStructSetData($para_struct[$i], "XSTRUCT", DllStructGetPtr($x_struct));eax+16
        DllStructSetData($para_struct[$i], "YSTRUCT", DllStructGetPtr($y_struct));eax+20
        DllStructSetData($para_struct[$i], "ZSTRUCT", DllStructGetPtr($z_struct));eax+24
        DllStructSetData($para_struct[$i], "vxSTRUCT", DllStructGetPtr($vx_struct));eax+28
        DllStructSetData($para_struct[$i], "vySTRUCT", DllStructGetPtr($vy_struct));eax+32
        DllStructSetData($para_struct[$i], "vzSTRUCT", DllStructGetPtr($vz_struct));eax+36
        DllStructSetData($para_struct[$i], "AUSGLEICH", 1E-3) ;eax+40
        DllStructSetData($para_struct[$i], "DT", $dt)      ;eax+44
        DllStructSetData($para_struct[$i], "FROM", $from)  ;eax+48
        DllStructSetData($para_struct[$i], "TO", $to)      ;eax+52
        DllStructSetData($para_struct[$i], "TICKS", 0)     ;eax+56  int64

        $param[$i] = DllStructGetPtr($para_struct[$i])
    Next


    $ptr_asm = DllStructGetPtr($tCodeBuffer)               ;pointer auf den assemblercode

    $sum_Threadnumber = 0
;~     $sum_Threadnumber += 0                                 ;summe aller Threadnummern
    For $i = 1 To $number_Threads
        $sum_Threadnumber += $i
    Next

    $bench_Interacts = 0
    $bench_ticks = 0
    $bench = 0


    ;http://msdn.microsoft.com/en-us/library/windows/desktop/ms686219(v=vs.85).aspx
    $ret = DllCall("kernel32.dll", "uint", "SetPriorityClass", "hwnd", _WinAPI_GetCurrentProcess(), "dword", 0x00000080)
    ;    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $ret = ' & $ret[0] & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console



    $overall_time = TimerInit()
    Global $steps = 0, $stepspersecond = 0, $stepspersecondmax = 0
    While 1                                                ;Hauptschleife, Threads erstellen und assemblercode zum Berechnen und Anzeigen aufrufen
        $steps += 1
        $t = TimerInit()
        For $i = 0 To $number_Threads - 1                  ;anzahl threads starten
            $teiler = Int($particles / $number_Threads)
            $from = $i * $teiler                           ;jeder thread bekommt nur einen teil der partikel aber berechnet
            $to = $from + $teiler                          ;die kräfte zu jedem vorhandenen Partikel
            If $i = $number_Threads - 1 Then $to = $particles
            DllStructSetData($para_struct[$i], "FROM", $from) ;eax+48
            DllStructSetData($para_struct[$i], "TO", $to)  ;eax+52
            $ret = DllCall("kernel32.dll", "hwnd", "CreateThread", "ptr", 0, "dword", 0, "int64", $ptr_asm, "int64", $param[$i], "long", 0, "int*", 0)
            $Thread[$i] = $ret[0]
        Next

        $flag = 0
        $sum_Threads = 0
        $loops = 0
        $test = 0
        Do                                                 ;threads are running
            $test += 1
            For $i = 0 To $number_Threads - 1
                $Return = DllCall("Kernel32.dll", "uint64", "GetExitCodeThread", "ptr", $Thread[$i], "dword*", 0) ;Rückgabe (EAX) aus den Threads
                If ($Return[2] <> 259) And $Threadexit[$i] = "" Then ;ist Thread bereits fertig?
                    $Threadexit[$i] = DllStructGetData($para_struct[$i], "TICKS");Anzahl Clockticks, da TerminateProcess und GetexitCodeThread  nur 32-Bit-Werte zurück gibt!
                    $sum_Threads += $i + 1                 ;summe aller Threads
                    If $sum_Threadnumber = $sum_Threads Then
                        $flag = 1
                        ExitLoop                           ;alle Threads fertig
                    EndIf
                EndIf
            Next
        Until $flag = 1                                    ;so lange, wie noch keine "alle fertig"- Rückgabe aus den Threads erfolgt

        $ticks = 0
        For $i = 0 To $number_Threads - 1                  ;alle threads terminieren
            $ret = DllCall("Kernel32.dll", "int64", "TerminateProcess", "dword", $Thread[$i], "uint", 999) ;Threads löschen
            $ticks += $Threadexit[$i]                      ;anzahl Clockticks, da TerminateProcess und GetexitCodeThread  nur 32-Bit-Werte zurück gibt!
            $Threadexit[$i] = ""                           ;feld löschen
        Next

        ;ab hier geht das benchmark-gedöns los
        $m = TimerDiff($t)                                 ;looptime
        $bench += 1                                        ;benchmark-zähler
        If $m < $benchtime_fastest Then $benchtime_fastest = $m ;schnellste looptime
        $bench_time += $m                                  ;alle looptimes zusammenzählen

        $Ticks_per_Interact = StringFormat("%.2f", $ticks / ($particles ^ 2 / 4))
        If Number($Ticks_per_Interact) < $minest_ticks Then $minest_ticks = Number($Ticks_per_Interact) ;wenigste clockticks

        $bench_ticks += $Ticks_per_Interact                ;alle ticks/interact
        $Interacts_per_Second = StringFormat("%.2e", $particles ^ 2 / ($m / 1000) * 4)
        If Number($Interacts_per_Second) > $maxest_interacts Then $maxest_interacts = Number($Interacts_per_Second)

        $bench_Interacts += $Interacts_per_Second

        DllCall($hDLLPowrProf, "ulong", "CallNtPowerInformation", _
                "int", 11, _                                    ;http://msdn.microsoft.com/en-us/library/windows/desktop/aa372675(v=vs.85).aspx
                "ptr", 0, _
                "ulong", 0, _
                "ptr", DllStructGetPtr($tPROCESSOR_POWER_INFORMATION), _
                "ulong", DllStructGetSize($tPROCESSOR_POWER_INFORMATION))
        $currentMHZ = DllStructGetData($tPROCESSOR_POWER_INFORMATION, "CurrentMhz")
        $cpuspeed += $currentMHZ

        If $currentMHZ > $cpuspeed_max Then $cpuspeed_max = $currentMHZ

        $stepspersecond += 1 / $m * 1000
        If $stepspersecondmax < 1 / $m * 1000 Then $stepspersecondmax = 1 / $m * 1000
        ;    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $stepspersecond = ' & $stepspersecond & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console


        WinSetTitle($gui, "", "n-body  #Particle= " & $particles & "   Interacts/sec= " & $Interacts_per_Second & "  Ticks/Interact= " & $Ticks_per_Interact & "   MaxSteps/sec=" & StringFormat("%.2f", $stepspersecondmax) & "   Threads= " & $number_Threads)
        _pic()                                             ;pixel anzeigen

    WEnd

EndIf

Func ende()
    _DeleteBitmap32($DC_bmp, $ptr1, $hbmp1)
    _bench($bench, $bench_ticks, $bench_Interacts, $cpuspeed, $minest_ticks, $maxest_interacts, $cpuspeed_max, TimerDiff($overall_time), $bench_time, $benchtime_fastest)
    ShellExecute(@ScriptDir & "\n-Body_bench.txt")
    Exit
EndFunc                                                    ;==>ende

Func _bench($anz, $tick, $inter, $clock, $mintick, $maxinter, $maxclock, $time, $benchtime, $benchtime_fastest)
    $tick = StringFormat("%.2f", $tick / $anz)             ;durchschnitte bilden
    $inter = StringFormat("%.2e", $inter / $anz)
    $clock = StringFormat("%.2f", $clock / $anz)
    $time = StringFormat("%.2f", $time / 1000)
    $benchtime = StringFormat("%.2f", $benchtime / $anz)
    $benchtime_fastest = StringFormat("%.2f", $benchtime_fastest)
    $maxinter = StringFormat("%.2e", $maxinter)
    $stepspersecond = StringFormat("%.2f", $stepspersecond / $steps)
    $stepspersecondmax = StringFormat("%.2f", $stepspersecondmax)
    $text = "Benchmark n-Body-Simulation" & @CRLF & @CRLF & _
            "Number Particles:" & @TAB & @TAB & $particles & @CRLF & _
            "Processor:" & @TAB & @TAB & $CPU_Name & @CRLF & _
            "Number Processors(Cores):" & @TAB & _WinAPI_GetCPUCoreCount() & @CRLF & _
            "Number Threads:" & @TAB & @TAB & $number_Threads & @CRLF & _
            "Overall Time:" & @TAB & @TAB & $time & " Seconds" & @CRLF & _
            "Number Loops benchmarked:" & @TAB & $anz & @CRLF & @CRLF & _
            "Average Interacts/sec:" & @TAB & $inter & @CRLF & _
            "Maximum Interacts/sec:" & @TAB & $maxinter & @CRLF & @CRLF & _
            "Average Steps/sec:" & @TAB & @TAB & $stepspersecond & @CRLF & _
            "Maximum Steps/sec:" & @TAB & $stepspersecondmax & @CRLF & @CRLF & _
            "Average Ticks/Interact:" & @TAB & $tick & "    CAUTION! Depends on real Clockticks!" & @CRLF & _
            "Minimum Ticks/Interact:" & @TAB & $mintick & "    CAUTION! Depends on real Clockticks!" & @CRLF & @CRLF & _
            "Average Clockticks[Mhz]:" & @TAB & Int($clock) & "    CAUTION! Clockticks may NOT match if over/underclocked! Please insert the real Clockticks!" & @CRLF & _
            "Maximum Clockticks[Mhz]:" & @TAB & Int($maxclock) & "    CAUTION! Clockticks may NOT match if over/underclocked! Please insert the real Clockticks!" & @CRLF & @CRLF & _
            "Average Looptime:" & @TAB & @TAB & $benchtime & " Milliseconds" & @CRLF & _
            "Fastest Looptime:" & @TAB & @TAB & $benchtime_fastest & " Milliseconds" & @CRLF & @CRLF
    ConsoleWrite($text)
    FileDelete(@ScriptDir & "\n-Body_bench.txt")
    FileWrite(@ScriptDir & "\n-Body_bench.txt", $text)

EndFunc                                                    ;==>_bench
Func _pic()                                                ;schreibt die koordinaten in die Bitmap
    If $cleanflag Then DllStructSetData($leer_struct, 1, $leerstring) ;hintergrund leeren
    $Return = DllCallAddress("uint", DllStructGetPtr($tCodeBuffer_plot), "int64", DllStructGetPtr($para_struct[0]), "int64", DllStructGetPtr($col_struct))
    _WinAPI_BitBlt($DC_gui, 0, 0, $w, $h, $DC_bmp, 0, 0, 0xCC0020);bitmap in DC blitten
EndFunc                                                    ;==>_pic


Func _dllstructcreate64($struct)                           ;align auf 16-byte adresse
    $temp = DllStructCreate($struct)
    $tempsize = DllStructGetSize($temp) + 64
    $ptr = DllStructGetPtr($struct)
    $a = Mod(Number($ptr), 64)
    $temp = 0
    $mem = DllCall("kernel32.dll", "ptr", "VirtualAlloc", "ptr", $ptr + $a, "ulong_ptr", $tempsize, "dword", 0x00003000, "dword", 0x00000040)
    $a = Mod(Number($mem[0]), 64)                          ;rest div 16 adresse = offset
    Return DllStructCreate($struct, (Number($mem[0]) - $a + 64)) ;auf 16 alingned pointer
EndFunc                                                    ;==>_dllstructcreate64



Func _dllstructcreate16($struct)                           ;align auf 16-byte adresse
    $temp = DllStructCreate($struct)
    $tempsize = DllStructGetSize($temp) + 16
    $ptr = DllStructGetPtr($struct)
    $a = Mod(Number($ptr), 16)
    $temp = 0
    $mem = DllCall("kernel32.dll", "ptr", "VirtualAlloc", "ptr", $ptr + $a, "ulong_ptr", $tempsize, "dword", 0x00003000, "dword", 0x00000040)
    $a = Mod(Number($mem[0]), 16)                          ;rest div 16 adresse = offset
    Return DllStructCreate($struct, (Number($mem[0]) - $a + 16)) ;auf 16 alingned pointer
EndFunc                                                    ;==>_dllstructcreate16

Func _CreateNewBmp32_test($iwidth, $iheight, ByRef $ptr, ByRef $hbmp) ;erstellt leere 32-bit-Bitmap; Rückgabe DC und ptr und handle auf die Bitmapdaten
    ;by Andy
    Local $aResult = DllCall("gdi32.dll", "handle", "CreateCompatibleDC", "handle", 0)
    If @error Then Return SetError(@error, @extended, 0)
    Local $hcdc = $aResult[0]                              ;Desktop-Kompatiblen DeviceContext erstellen lassen
    Local $tBMI = DllStructCreate($tagBITMAPINFO)          ;Struktur der Bitmapinfo erstellen und Daten eintragen
    DllStructSetData($tBMI, "Size", DllStructGetSize($tBMI) - 4) ;Structgröße abzüglich der Daten für die Palette
    DllStructSetData($tBMI, "Width", $iwidth)
    DllStructSetData($tBMI, "Height", -$iheight)           ;minus =standard = bottomup
    DllStructSetData($tBMI, "Planes", 1)
    DllStructSetData($tBMI, "BitCount", 32)                ;32 Bit = 4 Bytes => AABBGGRR
    Local $adib = DllCall('gdi32.dll', 'ptr', 'CreateDIBSection', 'hwnd', 0, 'ptr', DllStructGetPtr($tBMI), 'uint', 0, 'ptr*', 0, 'ptr', 0, 'uint', 0)
    $hbmp = $adib[0]                                       ;hbitmap handle auf die Bitmap, auch per GDI+ zu verwenden
    $ptr = $adib[4]                                        ;pointer auf den Anfang der Bitmapdaten, vom Assembler verwendet
    Local $aResult = DllCall("gdi32.dll", "handle", "SelectObject", "handle", $hcdc, "handle", $hbmp);objekt hbitmap in DC
    If @error Then Return SetError(@error, @extended, False)
    Return $hcdc                                           ;DC der Bitmap zurückgeben
EndFunc                                                    ;==>_CreateNewBmp32_test


Func _CreateNewBmp32($iwidth, $iheight, ByRef $ptr, ByRef $hbmp) ;erstellt leere 32-bit-Bitmap; Rückgabe $HDC und $ptr und handle auf die Bitmapdaten
    $hcdc = _WinAPI_CreateCompatibleDC(0)                  ;Desktop-Kompatiblen DeviceContext erstellen lassen
    $tBMI = DllStructCreate($tagBITMAPINFO)                ;Struktur der Bitmapinfo erstellen und Daten eintragen
    DllStructSetData($tBMI, "Size", DllStructGetSize($tBMI) - 4);Structgröße abzüglich der Daten für die Palette
    DllStructSetData($tBMI, "Width", $iwidth)
    DllStructSetData($tBMI, "Height", -$iheight)           ;minus =standard = bottomup
    DllStructSetData($tBMI, "Planes", 1)
    DllStructSetData($tBMI, "BitCount", 32)                ;32 Bit = 4 Bytes => AABBGGRR
    $adib = DllCall('gdi32.dll', 'ptr', 'CreateDIBSection', 'hwnd', 0, 'ptr', DllStructGetPtr($tBMI), 'uint', 0, 'ptr*', 0, 'ptr', 0, 'uint', 0)
    $hbmp = $adib[0]                                       ;hbitmap handle auf die Bitmap, auch per GDI+ zu verwenden
    $ptr = $adib[4]                                        ;pointer auf den Anfang der Bitmapdaten, vom Assembler verwendet
    _WinAPI_SelectObject($hcdc, $hbmp)                     ;objekt hbitmap in DC
    Return $hcdc                                           ;DC der Bitmap zurückgeben
EndFunc                                                    ;==>_CreateNewBmp32

Func _DeleteBitmap32($DC, $ptr, $hbmp)
    _WinAPI_DeleteDC($DC)
    _WinAPI_DeleteObject($hbmp)
    $ptr = 0
EndFunc                                                    ;==>_DeleteBitmap32


Func _WinAPI_GetCPUCoreCount()
    Local $tSYSTEM_INFO = DllStructCreate( _
            "ushort dwOemId;" & _
            "short wProcessorArchitecture;" & _
            "dword dwPageSize;" & _
            "ptr lpMinimumApplicationAddress;" & _
            "ptr lpMaximumApplicationAddress;" & _
            "long_ptr dwActiveProcessorMask;" & _
            "dword dwNumberOfProcessors;" & _
            "dword dwProcessorType;" & _
            "dword dwAllocationGranularity;" & _
            "short wProcessorLevel;" & _
            "short wProcessorRevision")
    Local $aRes = DllCall("Kernel32.dll", "none", "GetSystemInfo", "struct*", $tSYSTEM_INFO)
    If @error Or Not IsArray($aRes) Then Return -1
    Return DllStructGetData($tSYSTEM_INFO, "dwNumberOfProcessors")
EndFunc                                                    ;==>_WinAPI_GetCPUCoreCount


Func _CPU_speed()

    DllCall($hDLLPowrProf, "ulong", "CallNtPowerInformation", _
            "int", 11, _                                    ;http://msdn.microsoft.com/en-us/library/windows/desktop/aa372675(v=vs.85).aspx
            "ptr", 0, _
            "ulong", 0, _
            "ptr", DllStructGetPtr($tPROCESSOR_POWER_INFORMATION), _
            "ulong", DllStructGetSize($tPROCESSOR_POWER_INFORMATION))
    $iCPUSpeed_Current = DllStructGetData($tPROCESSOR_POWER_INFORMATION, "CurrentMhz")

    Return $iCPUSpeed_Current
EndFunc                                                    ;==>_CPU_speed



; Author: Ascend4nt
; ==============================================================================================
Func _CPUGetTotalProcessorTimes()
    Local $aRet, $stSystemTimes

    $stSystemTimes = DllStructCreate("int64 IdleTime;int64 KernelTime;int64 UserTime;")

    $aRet = DllCall("kernel32.dll", "bool", "GetSystemTimes", "ptr", DllStructGetPtr($stSystemTimes, 1), _
            "ptr", DllStructGetPtr($stSystemTimes, 2), "ptr", DllStructGetPtr($stSystemTimes, 3))

    If @error Then Return SetError(2, @error, "")
    If Not $aRet[0] Then Return SetError(3, 0, "")

    Dim $aRet[3] = [ _
            DllStructGetData($stSystemTimes, 1), _
            DllStructGetData($stSystemTimes, 2), _
            DllStructGetData($stSystemTimes, 3)]

    Return $aRet
EndFunc                                                    ;==>_CPUGetTotalProcessorTimes
















