1. Dashboard
  2. Mitglieder
    1. Letzte Aktivitäten
    2. Benutzer online
    3. Team
    4. Mitgliedersuche
  3. Forenregeln
  4. Forum
    1. Unerledigte Themen
  • Anmelden
  • Registrieren
  • Suche
Alles
  • Alles
  • Artikel
  • Seiten
  • Forum
  • Erweiterte Suche
  1. AutoIt.de - Das deutschsprachige Forum.
  2. Mitglieder
  3. Andy

Beiträge von Andy

  • Assemblerfunktion zum Aufsummieren direkt in AutoIt

    • Andy
    • 11. September 2024 um 19:06
    Zitat von AspirinJunkie

    Mein Problem liegt derzeit noch an einer anderen Stelle: Ich schaffe es nicht Pointer zu übergeben um ein Array abzuarbeiten.
    Folgendes Beispiel läuft bislang nicht bei mir:

    Ja, der Fehler liegt aber nicht bei dir, sondern bei godbolt bzw. den calling conventions des gcc.....das war mir schon bei den ersten Beispielen mit Funktionen mit Parametern aufgefallen.

    Exkurs in Calling conventions: https://en.wikipedia.org/wiki/X86_calli…ing_conventions

    Kurz zusammengefasst bestimmen die calling conventions, wie die Funktionsparameter an den ASM-Code übergeben werden.

    Wenn man sich den Anfang des ASM-Code anschaut

    Code
     push   rbp
     mov    rbp,rsp
     mov    QWORD PTR [rbp-0x18],rdi
     mov    DWORD PTR [rbp-0x1c],esi

    dann sieht man, dass die ersten beiden Parameter der Funktion die Register rdi (64 bit, die Adresse der Struct der zu addierenden doubles) und esi (32Bit, 5 Doubles sollen addiert werden) sind

    esi ist der niedrige Teil des rsi - Registers

    Die ersten beiden Parameter werden also in den Registern RDI und RSI übergeben....schaut man sich die calling conventions an, dann ist das

    Code
    System V AMD64 ABI
    The calling convention of the System V AMD64 ABI is followed on Solaris, Linux, FreeBSD, macOS,[26] and is the de facto standard among Unix and Unix-like operating systems. The OpenVMS Calling Standard on x86-64 is based on the System V ABI with some extensions needed for backwards compatibility.[27] The first six integer or pointer arguments are passed in registers RDI, RSI, RDX, RCX, R8, R9 (R10 is used as a static chain pointer in case of nested functions[28]: 21 ), while XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 and XMM7 are used for the first floating point arguments.

    Unix-gedöns....gcc....wtf....:P

    Schau mal, ob du bei godbolt einen Compiler für Windows findest oder die calling conventions umstellen kannst...

    Ich habe im von godbolt erstellten ASM-Code das register esi durch edx ersetzt und damit die Microsoft x64 calling convention erfüllt, der gcc kann das auch!!

    Damit läuft dein Code:

    C
    #include <assembleit2_64.au3>
    #include <array.au3>
    #AutoIt3Wrapper_UseX64=y
    
    
    #cs blablub
    
    		use64
     push   rbp
     mov    rbp,rsp
     mov    QWORD  [rbp-0x18],rcx
     mov    DWORD  [rbp-0x1c],edx		;Microsoft x64 calling convention
     pxor   xmm0,xmm0
     movsd  QWORD  [rbp-0x8],xmm0
     mov    DWORD  [rbp-0xc],0x0
     jmp    bla
     blub:
     mov    eax,DWORD  [rbp-0xc]
     cdqe
     lea    rdx,[rax*8+0x0]
     mov    rax,QWORD  [rbp-0x18]
     add    rax,rdx
     movsd  xmm0,QWORD  [rax]
     movsd  xmm1,QWORD  [rbp-0x8]
     addsd  xmm0,xmm1
     movsd  QWORD  [rbp-0x8],xmm0
     add    DWORD  [rbp-0xc],0x1
     bla:
     mov    eax,DWORD  [rbp-0xc]
     cmp    eax,DWORD  [rbp-0x1c]
     jl     blub
     movsd  xmm0,QWORD  [rbp-0x8]
     pop    rbp
     ret
    
    #ce blablub
    
    ; double data array
    Global $tData = DllStructCreate("DOUBLE [5]")
    For $i = 1 To 5
    	DllStructSetData($tData, 1, Number($i & "." & $i), $i)
    Next
    
    
    ;assembliert den Code und gibt das Ergebnis  aus
    $ret = _AssembleIt2("double","blablub", "PTR", DllStructGetPtr($tData), "INT", 5)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $ret = ' & $ret & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    
    ;Opcodes erzeugen
    ;~ $ret = _AssembleIt2("retbinary","blablub")
    ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $ret = ' & $ret & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    
    ;und ab damit in den DllCallAddress()
    Local $aStructMem = DllCall("kernel32.dll", "ptr", "VirtualAlloc", "ptr", 0, "ulong_ptr", DllStructGetSize(DllStructCreate("byte[86]")), "dword", 4096, "dword", 64)
    Local $tCodeBuffer = DllStructCreate("byte[86]", Number($aStructMem[0]))  ;Address aligned 64 and Memory is  $MEM_COMMIT, $PAGE_EXECUTE_READWRITE
    DllStructSetData($tCodeBuffer, 1,"0x554889E548894DE88955E4660FEFC0F20F1145F8C745F400000000EB2A8B45F44898488D14C500000000488B45E84801D0F20F1000F20F104DF8F20F58C1F20F1145F88345F4018B45F43B45E47CCEF20F1045F85DC3") ;write opcodes into memory
    $ret=DllCallAddress("double:cdecl", DllStructGetPtr($tCodeBuffer), "PTR", DllStructGetPtr($tData), "INT", 5)
    _arraydisplay($ret)
    Alles anzeigen

    Wenn die Calling conventions vom aufrufenden Programm und der Funktion übereinstimmen, dann fluppt es :o)


    //EDIT

    habs gefunden:

    __attribute__((ms_abi))

    Im c++-code des gcc erstellt code der Microsoft x64 calling convention

    Code
    Global $sSourceCode = _
    '__attribute__((ms_abi))' & @LF & _
    'double sumArray(double *arr, int size) {' & @LF & _
    '    double sum = 0.0;' & @LF & _
    '    for (int i = 0; i < size; i++) {' & @LF & _
    '        sum += arr[i];' & @LF & _
    '    }' & @LF & _
    '    return sum;' & @LF & _
    '}'

    funzt!!!!

  • Assemblerfunktion zum Aufsummieren direkt in AutoIt

    • Andy
    • 10. September 2024 um 22:16

    Btw. Vektorisierung...für Rust habe ich etwas gefunden: https://www.heise.de/hintergrund/Ta…15.html?seite=2

    "Fast" gleicher Quellcode, aber 4x schneller....

    Leider gibt es für Freebasic keine Vektorisierung, oder ich habe keine gefunden...

    //EDIT

    So wie ich das mittlerweile sehe, führt bei Vektorisierung kein Weg an hochoptimierten Libs oder Bibliotheken vorbei. AspirinJunkie , ggf. solltest du doch über den Vorschlag von UEZ nachdenken und eine dieser Libs integrieren und die Funktionen darin aufrufen....:Glaskugel:

    //EDIT2

    ....ich schreibe dir aber gerne auch eine kleine und feine ASM-Funktion:saint:

  • Assemblerfunktion zum Aufsummieren direkt in AutoIt

    • Andy
    • 10. September 2024 um 21:58
    Zitat von UEZ

    Bei dir ist das anscheinend in Fleisch und Blut übergegangen. :)

    Naja, du weisst ja wie das ist, man kann einem alten Hund keine neuen Tricks beibringen....braucht man aber auch nicht, seit dem allerersten Prozessor (na gut, x86-Prozessor also der 8086) ist der ASM-Code absolut unverändert. Einige Erweiterungen (x64,SSE,AVX uswusf.) sind dazugekommen und einige Handvoll neuer Prozessorbefehle, aber der "Ablauf" im ASM-Code ist wie vor 40 Jahren....da gibts nix "neues", also muss ich auch keine neuen Tricks lernen:party:.

    Zitat von UEZ

    Das Aktualisieren wäre sicherlich auch einfacher, als mit dem ASM Code.

    Bin ich voll und ganz dabei. Wenn die Compiler nur nicht so schrecklich "doof" wären....aber das liegt sicherlich an mir....ich habe Compilate disassembliert, da fliegt dir der Hut weg! Derart schnellen und GEILEN ASM-Code bekommen nicht mal die besten Assembler-Cracks geschrieben....und das nur mit einigen Zeilen (zugegeben auch GUTEM) Hochsprachencode und F5 drücken.....

    In einigen anderen Beiträgen zu ähnlichen Themen habe ich schon geschrieben, wie ich mich mit den Jungs und Mädels im Forschungszentrum Jülich und deren HPC-Anwendungen und Optimierungen beschäftigt habe. DAS ist die obere Spitze vom Eisberg....da schreibt niemand irgendwelchen Larifaricode, ohne zu wissen bzw nachzuprüfen, was der Compiler daraus macht. Wenn in einen Rechencluster mehrere hundert Millionen Euro versenkt werden, dann nicht, um nur 5-10% von der Rechenleistung auszunutzen...schon 90% sind zu wenig!!!

    Wie ich schon immer postuliert habe: langsamer Code ist und bleibt langsam, selbst mit einem schnellen Compiler compiliert! "Schneller" Code ist die Ausnutzung ALLER im Prozessor (egal ob CPU oder GPU) integrierten Komponenten und Rechenwerke. Da in Jülich auch mit OpenCL gearbeitet und entsprechend optimiert wurde, hat mir das auch bei schnellen Berechnungen auf meiner Grafikkarte geholfen. Die kostet nur die Hälfte von meinem Prozessor, ist aber in der Lage bestimmten Code x-mal schneller auszuführen....

    S. Beispiel oben. Freebasic und Rust compilieren Code, der von einigen Bytes ASM-Code um mehrere Faktoren abgehängt wird! Da können aber die Compiler nix dafür, das hängt am Sourcecode! Nur weil ich weiß, dass man statt 4 Zahlen nacheinander zu addieren das auch in EINEM Prozessorbefehl machen kann, bin ich ja kein Crack....wenn ich mich "früher" statt mit Assembler mit C beschäftigt hätte, würde ich sicher auch SSE- und AVX-Code in C schreiben können. Oder in einer anderen Compilersprache...aber ich bin halt bei "Basic" hängengeblieben...und Assembler :theke:

  • Assemblerfunktion zum Aufsummieren direkt in AutoIt

    • Andy
    • 10. September 2024 um 20:41

    Hi,

    sorry für die späte Antwort.

    Zitat von AspirinJunkie

    Eventuell kannst du Licht ins Dunkel bringen in welchen Fällen das versagt und in welchen es klappt und was dazu immer notwendig ist.

    "Es" funktioniert in allen Fällen in denen der Compiler in der Lage ist, ohne einzubindende Module (ASM-)Code zu erzeugen. Ich bin mir sogar sicher dass funktionsfähiger Code entstehen würde, wenn du bspw. die SQRT()-Funktion aus der Bibliothek herauslösen würdest und in den Quellcode einbindest. Für den Quellcode

    Code
    '#include <math.h>' & @LF & _
    'double squareroot(double num) {' & @LF & _
    '    return sqrt(num);' & @LF & _
    '}'

    wird folgender ASM-Code erzeugt

    Code
    push   rbp
     mov    rbp,rsp
     sub    rsp,0x10
     movsd  QWORD PTR [rbp-0x8],xmm0
     mov    rax,QWORD PTR [rbp-0x8]
     movq   xmm0,rax
     call   1b <squareroot+0x1b>
        R_X86_64_PLT32 sqrt-0x4
     leave
     ret

    der "call 1b" ruft die Unterfunktion an der Adresse 1b im Code auf, das ist allerdings genau die nächste Zeile mit dem "leave", was den Rücksprung aus der Funktion aufruft. Wie du bereits vermutet hast, wird der Call in die Unterfunktion ausgeführt, aber infolge fehlendem Code nicht ausgeführt.

    Was bereits bemerkenswert ist, ist die Tatsache, dass du "fast" lauffähigen Assemblercode aka Opcodes erzeugen kannst.

    Ich habe mal etwas mit dem gcc auf godbolt experimentiert. **ToDo**Ggf. sollte man per AutoItscript einen "echten" gcc oder anderen Compiler lokal steuern und die Opcodes disassemblieren....

    Der vom godbolt erzeugte ASM-Code stellt ausschliesslich das 1:1-pendant des C(++)-Codes dar! Da wird keinerlei "Struktur" mitassembliert! Will heißen:

    Eine C-Funktion main() (oder andererer Funktionsname), welcher KEINE andere Funktion aufruft, wird, wie von dir demonstriert, anstandslos ausgeführt.

    Wird allerdings in main() eine andere Funktion (bspw. add())aufgerufen, muss diese vor main() deklariert werden. Der C-Compiler "weiß", dass die add()-Funktion eine Funktion ist und springt im Compilat zu main(), arbeitet den dortigen Code bis zur Funktion add() ab, springt in add(), von dort zurück in main() und beendet das Programm....

    Im von godbolt erzeugten ASM-Code wird "einfach" eine Funktion add() erzeugt und eine Funktion main()

    Sourcecode:

    Code
    int add(int num) {
        return num + num;
    }
    
    
    int main() {
    int b = 5;    
    int a = add(b);
    return a;
    }

    Daraus macht godbolt:

    Code
    add(int):
            push    rbp
            mov     rbp, rsp
            mov     DWORD PTR [rbp-4], edi
            mov     eax, DWORD PTR [rbp-4]
            add     eax, eax
            pop     rbp
            ret
    main:
            push    rbp
            mov     rbp, rsp
            sub     rsp, 16
            mov     DWORD PTR [rbp-4], 5
            mov     eax, DWORD PTR [rbp-4]
            mov     edi, eax
            call    add(int)
            mov     DWORD PTR [rbp-8], eax
            mov     eax, DWORD PTR [rbp-8]
            leave
            ret
    Alles anzeigen

    dieser Code hat aber ein Problem. Wenn assembliert, startet der code bei add() und wird beim nächsten ret beendet. main() wird NIE aufgerufen.

    Würde main() VOR den anderen Funktionen stehen, so wie hier (und die (int)´s im code entfernt werden), so dass folgender Code entsteht

    Code
    main:
            push    rbp
            mov     rbp, rsp
            sub     rsp, 16
            mov     DWORD PTR [rbp-4], 5
            mov     eax, DWORD PTR [rbp-4]
            mov     edi, eax
            call    add
            mov     DWORD PTR [rbp-8], eax
            mov     eax, DWORD PTR [rbp-8]
            leave
            ret
    
    
    add:
            push    rbp
            mov     rbp, rsp
            mov     DWORD PTR [rbp-4], edi
            mov     eax, DWORD PTR [rbp-4]
            add     eax, eax
            pop     rbp
            ret
    Alles anzeigen

    dann wird dieser code anstandslos assembliert (u.a. von AssembleIt, aber auch bei https://defuse.ca/online-x86-assembler.htm#disassembly)

    0x554889E54883EC10C745FC050000008B45FC89C7E8080000008945F88B45F8C9C3554889E5897DFC8B45FC01C05DC3

    und ist lauffähig (ACHTUNG! AssembleIt benutzt den FASM-Assembler, und der reagiert allergisch darauf, wenn man "call add" aufruft. add ist eine Assembler- Anweisung, benennt man die Funktion bzw. Adresse im Code in "addieren" um, funzt alles problemlos^^). Der Disassembler https://defuse.ca/online-x86-assembler.htm#disassembly assembliert zu


    Code
    0:  55                      push   rbp
    1:  48 89 e5                mov    rbp,rsp
    4:  48 83 ec 10             sub    rsp,0x10
    8:  c7 45 fc 05 00 00 00    mov    DWORD PTR [rbp-0x4],0x5
    f:  8b 45 fc                mov    eax,DWORD PTR [rbp-0x4]
    12: 89 c7                   mov    edi,eax
    14: e8 08 00 00 00          call   21 <add>
    19: 89 45 f8                mov    DWORD PTR [rbp-0x8],eax
    1c: 8b 45 f8                mov    eax,DWORD PTR [rbp-0x8]
    1f: c9                      leave
    20: c3                      ret
    0000000000000021 <add>:
    21: 55                      push   rbp
    22: 48 89 e5                mov    rbp,rsp
    25: 89 7d fc                mov    DWORD PTR [rbp-0x4],edi
    28: 8b 45 fc                mov    eax,DWORD PTR [rbp-0x4]
    2b: 01 c0                   add    eax,eax
    2d: 5d                      pop    rbp
    2e: c3                      ret
    Alles anzeigen

    der call-befehl ruft die Funktion add an der Adresse 21 auf (FASM macht das genau so....JEDER Assembler generiert die gleichen OpCode:

    0x554889E54883EC10C745FC050000008B45FC89C7E8080000008945F88B45F8C9C3554889E5897DFC8B45FC01C05DC3

    welcher auch das "richtige" Ergebnis liefert: (AutoIt-Code hier von AssembleIt erzeugt)

    Code
    Local $aStructMem = DllCall("kernel32.dll", "ptr", "VirtualAlloc", "ptr", 0, "ulong_ptr", DllStructGetSize(DllStructCreate("byte[47]")), "dword", 4096, "dword", 64)
    Local $tCodeBuffer = DllStructCreate("byte[47]", Number($aStructMem[0]))  ;Address aligned 64 and Memory is  $MEM_COMMIT, $PAGE_EXECUTE_READWRITE
    DllStructSetData($tCodeBuffer, 1,"0x554889E54883EC10C745FC050000008B45FC89C7E8080000008945F88B45F8C9C3554889E5897DFC8B45FC01C05DC3") ;write opcodes into memory
    $ret=DllCallAddress("int:cdecl", DllStructGetPtr($tCodeBuffer))
    
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $ret = ' & $ret[0] & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

    Zur Vollständigkeit halber der AssembleIt-Code:

    Code
    #include <assembleit2_64.au3>
    #AutoIt3Wrapper_UseX64=y
    
    
    #cs blablub
    
    		use64
    main:
            push    rbp
            mov     rbp, rsp
            sub     rsp, 16
            mov     DWORD  [rbp-4], 5
            mov     eax, DWORD  [rbp-4]
            mov     edi, eax
            call    addieren
            mov     DWORD  [rbp-8], eax
            mov     eax, DWORD  [rbp-8]
            leave
            ret
    
    addieren:
            push    rbp
            mov     rbp, rsp
            mov     DWORD  [rbp-4], edi
            mov     eax, DWORD  [rbp-4]
            add     eax, eax
            pop     rbp
            ret
    
    #ce blablub
    
    
    ;assembliert den Code und gibt das Ergebnis 5+5=10 aus
    $ret = _AssembleIt2("int","blablub")
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $ret = ' & $ret & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console	
    
    consolewrite(@crlf & @crlf & @crlf & "AssembleIt erzeugt AutoIt-Code aka DllCallAddress()" & @crlf)
    ;erzeugt lauffähigen AutoIt-Code
    ;erstellt Opcodes und ruft diese per DllCallAddress() auf
    $ret = _AssembleIt2("int","blablub","retbinary",@scriptlinenumber)
    Alles anzeigen


    Zusammengefasst:

    Du bist mit deinem Programm bei ca. 90% Funktionalität angelangt. Ganz ohne Nachbearbeitung des vom Godbolt-gcc (oder von anderen Compilern) erzeugten Codes geht es zzt. noch nicht. Btw. habe ich sowohl die von UEZ als auch die von Kanashius erzeugte ASM-Codes mit AssembleIt lauffähig bekommen, allerdings mit "Nachbearbeitung".

    Bei der Erstellung einer DLL werden Bibliotheken bzw. die darin enthaltenen und im Code benötigten Funktionen dazugelinkt.

    Aber wo ist das Problem, aus bspw. C(++) - Bibliotheken die einzelnen Funktionen zu assemblieren und später an dein Programm "anzuhängen".

    Man hätte den Vorteil, dass man nur noch ca. 5% des ASM-Codes schreiben bzw. rudimentär ändern müsste, um ein extrem schnelles Programm in AutoIt zu erhalten.....schreib mal einige Zeilen C(++)-code oder mit einem anderen Compiler und wir sehen weiter.

    Oder schon fortgeschritten: Gib mit den disassemblierten, also ASM- Code einer von dir benötigten Matrix-Funktion (bspw. von einem x-beliebigen Compiler generiert), und ich sehe zu, dass ich dazu die passenden Opcodes erstelle....


    Dieses Projekt würde den "AutoItcompiler" fast überflüssig machen....

  • Assemblerfunktion zum Aufsummieren direkt in AutoIt

    • Andy
    • 2. September 2024 um 19:31
    Zitat von AspirinJunkie

    Ok vielleicht hilft dir ja folgendes Skript ein bisschen:

    Ui, DAS ist ja cool...

    Zitat von AspirinJunkie

    Hm bei mir klappt es noch nicht ganz:

    Ja, Verständnisfehler oder von mir nicht richtig/exakt beschrieben:

    $bRet = _AssembleIt2("int", "square", "int", $a) aufgerufen mit dem Rückgabedatentyp, "Funktionsnamen" und den Parametern (Parameter wie beim DllCall, jeweils Datentyp und Variable) assembliert den ASM-Code, schreibt ihn in den Speicher, ruft ihn auf und gibt das Ergebnis der Funktion zurück. Der ASM-Code kann mit Debugger (aktuell nur für 32-Bitcode) nachvollzogen werden , Registerinhalte, Stack usw. Der aufgerufene (und assemblierte) ASM-Code läuft nativ auf dem Prozessor, der "Overhead" der _AssembleIt2-Funktion ist nur einige Millisekunden.

    _AssembleIt2("retbinary","Funktionsname") gibt den assemblierten Binärcode zurück, den man dann allerdings noch in eine Struct schreiben und aufrufen muss....Diese Funktion ist aktuell unnötig!

    Ich habe eine Erweiterung geschrieben, welche einige Zeilen AutoItcode erstellt, mit der dann der ASM-Code direkt per DllCallAddress() aufgerufen werden kann. Dazu werden die Parameter "retbinary",@ScriptlineNumber hinten an die Parameter von AssembleIt angehängt. Damit baut dann AssembleIt den kompletten Aufruf der ASM-binary aus dem Speicher, bsp:

    $bRet = _AssembleIt2("int", "square", "int", $a,"retbinary",@ScriptLineNumber)

    erzeugt (steht in der Scite Console und im Clipboard):

    Local $aStructMem = DllCall("kernel32.dll", "ptr", "VirtualAlloc", "ptr", 0, "ulong_ptr", DllStructGetSize(DllStructCreate("byte[15]")), "dword", 4096, "dword", 64)
    Local $tCodeBuffer = DllStructCreate("byte[15]", Number($aStructMem[0])) ;Address aligned 64 and Memory is $MEM_COMMIT, $PAGE_EXECUTE_READWRITE
    DllStructSetData($tCodeBuffer, 1,"0x554889E5894DFC8B45FC0FAFC05DC3") ;write opcodes into memory
    $ret=DllCallAddress("int:cdecl", DllStructGetPtr($tCodeBuffer), "int", $a)

    und ersetzt dann

    $bRet = _AssembleIt2("int", "square", "int", $a)

    womit #include <assembleit2_64.au3> überflüssig wird


    Hier dein Beispiel (habe deine Fragen kommentiert/beantwortet)

    Code
    #include <assembleit2_64.au3>
    #AutoIt3Wrapper_UseX64=y
    
    
    #cs square
    
    		use64
    		push    rbp
            mov     rbp, rsp
            mov     DWORD [rbp-4], ecx
            mov     eax, DWORD [rbp-4]
            imul    eax, eax
            pop     rbp
            ret
    
    #ce square
    
    ; ist das die Größe des 1. Parameters in Bytes?
    $a=4
    ;nein, das ist der Parameter, der Integer der quadriert werden soll
    
    ; ASM-Code ausführen
    $bRet = _AssembleIt2("int", "square", "int", $a)  ; was macht das a?!
    ;das $a ist der Parameter, der Integer, der quadriert werden soll
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $bRet = ' & $bRet & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    
    ;erstellt das binary der Funktion "square"....nur noch in Ausnahmefällen nötig, da....
    $binary = _AssembleIt2("retbinary","square")
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binary = ' & $binary & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    
    ;....mit
    $bRet = _AssembleIt2("int", "square", "int", $a,"retbinary",@ScriptLineNumber)
    ;erzeugt wird: (s. Console und Clipboard), damit #include <assembleit2_64.au3> überflüssig wird
    
    Local $aStructMem = DllCall("kernel32.dll", "ptr", "VirtualAlloc", "ptr", 0, "ulong_ptr", DllStructGetSize(DllStructCreate("byte[15]")), "dword", 4096, "dword", 64)
    Local $tCodeBuffer = DllStructCreate("byte[15]", Number($aStructMem[0]))  ;Address aligned 64 and Memory is  $MEM_COMMIT, $PAGE_EXECUTE_READWRITE
    DllStructSetData($tCodeBuffer, 1,"0x554889E5894DFC8B45FC0FAFC05DC3") ;write opcodes into memory
    $ret=DllCallAddress("int:cdecl", DllStructGetPtr($tCodeBuffer), "int", $a)
    
    _arraydisplay($ret); da DllCallAddress() ein Array zurückgibt
    
    ;15 quadrieren
    $ret=DllCallAddress("int:cdecl", DllStructGetPtr($tCodeBuffer), "int", 15)
    _arraydisplay($ret)
    Alles anzeigen

    Noch etwas zum Verständnis:

    Im 64-Bit-Modus ist eine 64 Bytes aligned Speicheradresse zwingend notwendig, um die in den Speicher geschriebenen Opcodes auszuführen.

    DllStructcreate() richtet nur 4 Byte aligned aus, für den 32-Bit-Modus reicht das, bei 64-Bit-Modus nicht....daher der Umweg über VirtualAlloc. In AssembleIt gibt es eine Funktion _DllStructCreate64(), die exakt dafür vorgesehen ist. Im 32-Bit-Modus schadet das 64-Bit align auch nicht....gerade im Gegenteil, Zugriff auf aligned Daten führt idR zu schnellerem Code.

  • Assemblerfunktion zum Aufsummieren direkt in AutoIt

    • Andy
    • 1. September 2024 um 11:40
    Zitat von AspirinJunkie

    Ok also ganz ohne Anpassungen kann ich den Output von denen also offensichtlich nicht verwenden?

    Jeder Compilerbauer kocht sein eigenes Süppchen. Würdest du bspw. den GCC-C-Compiler den Code erstellen/assemblieren lassen und per DllCallAddress im GCC aufrufen, dan würde das auch genau so funktionieren.

    Das macht dann ein Assembler / Disassembler, bspw. so , auch online

    Der Inhalt kann nicht angezeigt werden, da du keine Berechtigung hast, diesen Inhalt zu sehen.


    Bin heute Abend wieder Online, dann versuchen wir das mal ohne Modifikationen am Code....bis denne

  • Assemblerfunktion zum Aufsummieren direkt in AutoIt

    • Andy
    • 1. September 2024 um 11:03

    https://en.wikipedia.org/wiki/Kahan_summation_algorithm ist sehr interessant. Unten im Artikel ist von den optimierten sum()-Funktionen einiger Sprachen die Rede.

    Dann wäre es es echt eine Überlegung wert, den von diesen Compilern erstellten Code in eine AssembleIt-Funktion zu kopieren und den Binärcode erstellen zu lassen.

    Das wäre echt einfach....


    Zitat von AspirinJunkie

    Ich habe zwar keine Ahnung von Assembler aber ein Träumchen wäre es, wenn ich dort den Code in C eingeben könnte und dann entweder direkt ein Binary oder ein ASM-Code erhalte, welchen ich in AssembleIt2 verwenden könnte,.

    Yepp, das funktioniert auch (fast) genau so

    Der Inhalt kann nicht angezeigt werden, da du keine Berechtigung hast, diesen Inhalt zu sehen.

    den Assemblercode (hier 64Bit) kannst du so ins AssembleIt einfügen, ich mach mal ein Beispiel.

    Der Inhalt kann nicht angezeigt werden, da du keine Berechtigung hast, diesen Inhalt zu sehen.

    Code
    #include <assembleit2_64.au3>
    #AutoIt3Wrapper_UseX64=y
    
    
    #cs square
    
    		use64
    		push    rbp
            mov     rbp, rsp
            mov     DWORD [rbp-4], ecx
            mov     eax, DWORD [rbp-4]
            imul    eax, eax
    
            pop     rbp
            ret
    
    #ce square
    
    
    $a=8
    
    $ret = _AssembleIt2("int","square",  "int",$a);,"retbinary",@scriptlinenumber)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $ret = ' & $ret & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console		
    Alles anzeigen

    in Zeile 12 wurde das Register edi durch ecx ersetzt, s. 64-Bit Aufrufkonventionen, ggf. kann man das auch per fastcall o.ä. anpassen

    die DWORD PTR wurden durch DWORD ersetzt bzw. das PTR entfernt, das ist dem im AssembleIt verwendeten FASM Assembler geschuldet. Aber das erkennt AssembleIt und weist dich hin...

    Ansonsten könnte man die so erstellten bzw auch von UEZ und Kanashius erstellten Codes so einfügen und per AssembleIt testen und bearbeiten

  • Assemblerfunktion zum Aufsummieren direkt in AutoIt

    • Andy
    • 1. September 2024 um 10:03
    Zitat von AspirinJunkie

    Der interessante Schritt dazwischen "ASM-Code in ein aus AutoIt heraus aufrufbares Binary zu überführen" sind wir aber bislang noch nicht angegangen.

    Doch, schau mal in mein Script in Post #10.

    Binärcode mit Aufruf per DllCallAddress()

    Zitat von AspirinJunkie

    Komplett unterschiedlich: Von 2 bis 1 Million in Einerschritten alles möglich.

    Ok, dann wird es interessant. Ggf. macht es dann auch Sinn, den (für parallele Berechnungen) lahmarschigen Prozessor (CPU) mit Hilfe der Grafikkarte bzw. in die CPU integrierter GPU auf die Sprünge zu helfen.

    Matrixberechnungen werden nicht ohne Grund bei Supercomputeranwendungen auf GPU´s ausgelagert. Die sind dafür prädestiniert, Vektorisierungen sind dort einfach zu erstellen...Cuda ist auf Nvidia beschränkt, daher habe ich solches schon in AutoIt per OpenCL umgesetzt.

    Btw. Vektorisierung, UEZ und Kanashius das sollte man auch dem Compiler hinwerfen, dann macht der auch parallel ausführbaren Code...


    Zitat von AspirinJunkie

    Hatte AssembleIt da nicht Techniken dafür?

    Na klar^^, habe imho sogar im letzten Jahr ein Howto verfasst...

    AssembleIt kann nun nach dem Testen/Debuggen selbstständig den AutoItcode mit Binärcode und Aufruf über DllCallAddress() erstellen, s.S. 7 unten

    Der Inhalt kann nicht angezeigt werden, da du keine Berechtigung hast, diesen Inhalt zu sehen.

  • Assemblerfunktion zum Aufsummieren direkt in AutoIt

    • Andy
    • 1. September 2024 um 00:07

    Ich zitiere mich mal selbst:

    Zitat von Andy

    Könnte mal jemand BITTE einen GCC oder Intel/M$-Compiler anwerfen? Und damit mal versuchen, ob mit deren Compileroptionen diese simple SSE-Addition "richtig" ausgeführt wird? :Glaskugel:

    Habe eben auf https://godbolt.org mal folgende Funktion mit den verschiedensten Compilern durchgetestet.

    Code
    void sumArray(float* x,int* n) {
        int i;
        float sum;
        for (i = 0; i < n; i++) {
            sum += x[i];
        }
    }

    Bin ja nicht der C-Spezialist, sollte so aber passen....

    K E I N E R der von den zwölfundneunzig von mir ausgewählten Compilern hat etwas anderes gemacht, als nacheinander einzelne Floats zu addieren. Von SSE/SIMD oder anderen seit Jahrzehnten möglichen Techniken zur parallelen Berechnung keine Spur....

    Ich spiele mal mit den Compileroptionen rum....schaumamal

    //EDIT

    So wie ich das sehe, empfehlen die Compiler-Hersteller die Verwendung von Intrinsics...

    https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html

    Damit werden die ASM-Instruktionen in die Hochsprache gehievt...warum auch nicht, da macht der Compiler wenigstens (ohne zu "optimieren") was er soll!

    HADDPS https://www.intel.com/content/www/us…4&ssetechs=SSE3

    Der Inhalt kann nicht angezeigt werden, da du keine Berechtigung hast, diesen Inhalt zu sehen.

  • Assemblerfunktion zum Aufsummieren direkt in AutoIt

    • Andy
    • 31. August 2024 um 23:13

    Habe mir mal den Code von Rust angeschaut....da schiessen mir ehrlich gesagt die Tränen in die Augen...

    Der Prozessorbefehl, den alle Compilate (bisher) (und auch ich^^) benutzen um die Floats zusammenzuzählen lautet ADDPS

    Der Inhalt kann nicht angezeigt werden, da du keine Berechtigung hast, diesen Inhalt zu sehen.

    Wie man im Schaubild sieht, addiert der Befehl 4 Floats aus einem Register oder Speicher zu den 4 Floats im Register....DAS ist der Sinn und Zweck von SSE (Streaming SIMD Extensions), die parallele Ausführung von Instruktionen, hier die Addition. SIMD heißt Single Instruction Multiple Data, man kann MEHRERE Daten gleichzeitig bearbeiten.

    Ich in meinem ASM-Programm mache das auch so, ich lese 128 Bit (16 Byte) an Daten ein und addiere diese zu den 128 Bit Daten (aka 4 Floats = 16 Bytes) im Register....logischerweise muss man dann, um die nächsten 4 Floats (16 Bytes) zu addieren, im Speicher auch 16 Bytes weiter springen....Rust springt 4 Bytes weiter....addiert also immer nur EINE Float!!! Natürlich werden die folgenden 3 Floats auch zu den entsprechenden Teilen im Register dazuaddiert, das ist aber völlig irrelevant, da der Compiler diese Addition nie berücksichtigt!

    Die Rust-Schleife läuft also 4x länger als eigentlich vorgesehen, und bei 3 von 4 ADDPS haut auch noch der unaligned Speicherzugriff voll rein...Soviel zu "Compiler-Optionen".....:Face:

    UEZ´s Compiler benutzt für die Addition der Floats den uralten (seit 1999 überflüssigen) Coprozessor mit
    fadd qword ptr [ebx]

    und addiert, wie Rust, jede der Floats im Speicher nacheinander.....=O


    Könnte mal jemand BITTE einen GCC oder Intel/M$-Compiler anwerfen? Und damit mal versuchen, ob mit deren Compileroptionen diese simple SSE-Addition "richtig" ausgeführt wird?:Glaskugel:


    Und um jetzt die aufkommende Frage der interessierten Leser zu beantworten: "Ja, Andy, du hast jetzt im letzten Schleifendurchlauf alle Floats addiert, die bestehen im XMM-Register aber doch aus 4 einzelnen Floats die auch noch zusammengezählt werden müssen!"

    Ja, stimmt! Im XMM-Register stehen 4 Floats "hintereinander", um innerhalb eines XMM-Registers die einzelnen Teile zusammenzuaddieren, gibt es den Der Inhalt kann nicht angezeigt werden, da du keine Berechtigung hast, diesen Inhalt zu sehen.

    der addiert innerhalb des Registers.

    Angenommen im Register xmm0 stehen die Floats 1.0 2.0 6.0 7.0

    dann wird in der Instruktion HADDPS xmm0,xmm0 folgendes berechnet

    6.0 + 7.0 =13.0 geht ins 4. Registerteil

    1.0 + 2.0 = 3.0 geht ins 3. Registerteil

    6.0 +7.0 =13.0 geht ins 2. Registerteil

    1.0 + 2.0 = 3.0 geht ins 1. Registerteil

    Im Register xmm0 stehen also die Floats 13.0 3.0 13.0 3.0

    beim nochmaligen HADDPS xmm0,xmm0 wird berechnet

    13.0 + 3.0 =16.0 geht ins 4. Registerteil

    13.0 + 3.0 = 16.0 geht ins 3. Registerteil

    13.0 +3.0 =16.0 geht ins 2. Registerteil

    13.0 + 3.0 = 16.0 geht ins 1. Registerteil

    Danach stehen in allen Registerteilen (Floats) die Summe der 4 einzelnen Floats aka die Gesamtsumme aller Floats

    Tadaaaaaa!

  • Assemblerfunktion zum Aufsummieren direkt in AutoIt

    • Andy
    • 31. August 2024 um 21:55

    sodele, hab da was....

    14 Zeilen ASM-Code aka 44 Bytes Code müssen für die Summierung der Floats reichen^^

    Problem ist, wie üblich bei Summierung von vielen Floats, die Ungenauigkeit. AutoIt lügt ja wie gedruckt, rechnet intern mit 64 Bit, da finden dann keine/kaum Überläufe statt aber der ASM-Code rechnet "nur" mit 32Bit pro Float

    Aktuell bin ich bei 0,7 Prozessortakten pro Addition....die Compiler optimieren sehr stark, machen loop unrolling und berücksichtigen cache-lines...mache ich alles nicht....:/

    AspirinJunkie , wie viele Floats müssen da zusammengezählt werden? Minimal und maximal? Ich würde dann den Code für X64 und Double anpassen.

    Die Summe von 1.000.000 Floats werden in ca. 0.3ms berechnet, sollten das mehr Additionen sein, würde ich mit einer Handvoll Zeilen mehr noch ggf. Faktor 2-3 an Geschwindigkeit rausholen können. Aktuell muss die Anzahl durch 4 teilbar sein (Struct ggf. mit Nullen auffüllen) wenn man gleichzeitig 16 Zahlen addiert (per AVX) , muss die Structgröße glatt durch 16 teilbar sein.

    Dazu MUSS die Struct 16-Byte-aligned sein, warum das nicht standardmäßig in DllStructCreate implementiert ist weiß wieder mal kein Mensch...Ich habe dazu eine Funktion gebastelt. Man könnte auch eine Bitmap erstellen, die ist immer 16-Byte aligned und an deren Adresse die Struct erstellen.....

    UEZ´s Compilercode liest übrigens unaligned aus dem Speicher...der S U P E R G A U !!8|


    Code
    #include <Memory.au3>
    
    ;~ #include <assembleit2_64.au3>
    #AutoIt3Wrapper_UseX64=n
    
    
    
    #cs sumfloat32
    
        use32
        mov edi, [esp + 4]                      ;ES:EDI = Pointer auf die struct
        mov ecx, [esp + 8]                      ;ECX = Anzahl zu summierender floats
    	shl ecx,2								;geteilt durch 4, immer 4 floats gleichzeitig addieren
    	xorps xmm0,xmm0							;xmm0 = 0 0 0 0
    	sub ecx,16								;Adresse der letzten 4 floats
    	movaps xmm0,[edi]						;xmm0 = die ersten 4 floats
    
    	_schleife:                              ;
        addps xmm0, [edi+ecx]					;4 floats im speicher zu den 4 floats in xmm0 addieren
    	sub ecx,16								;adresse der nächsten 4 floats...
        jnz _schleife							;so lange, bis alle floats addiert sind
    
    	haddps xmm0,xmm0						;die 4 floats in xmm0 addieren
     	haddps xmm0,xmm0						;jetzt steht die gesamte summe in jedem register von xmm0
    
    	movd dword[edi],xmm0					;in den speicher schreiben
    	fld dword[edi]							;rückgabe der floats im float-stack
    
        ret
    
    #ce sumfloat32
    
    
    
    ;~ Global $binarycode = _AssembleIt2("retbinary", "sumfloat32")
    ;~ $tCodebuffer = DllStructCreate("byte[" & StringLen($binarycode) / 2 - 1 & "]") ;Speicher für den Bytecode reservieren
    ;~ DllStructSetData($tCodebuffer, 1, $binarycode) ;Bytecode in den Speicher schreiben
    
    Global $tCodeBuffer = DllStructCreate("byte[56]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1,"0x8B7C24048B4C2408C1E1020F57C083E9100F28070F58040F83E91075F7F20F7CC0F20F7CC0660F7E07D907C3") ;write opcodes into memory
    
    
    
    $sum=0
    
    $anzahl=1000000
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $anzahl = ' & $anzahl & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    Global $tData = _dllstructcreate64("FLOAT["&$anzahl&"]")
    
    ; füllen
    For $i = 1 To $anzahl
    	DllStructSetData($tData, 1, 1/$i, $i)
    	$sum+=1/$i
    Next
    
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : AutoIt $sum = ' & $sum & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    
    $t=timerinit()
    $fSum = _sumUpMemory(DllStructGetPtr($tData), $anzahl)
    $time=timerdiff($t)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : ASM   $fSum = ' & $fSum & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    
    $diff = $sum-$fSum
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $diff = ' & $diff & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $time = ' & $time &"ms"& @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    
    
    
    
    
    
    Func _sumUpMemory($pPointer, $iN, $sType = "FLOAT")
    	; hier sollte irgendwie der Binary mit den x64 und x86-Binärcode stehen
    	; dieser sollte dann in den Speicher zur Ausführung geschrieben werden
    	; und dann per (vermutlich) DllCallAddress ausgeführt werden
    
    	If $sType = "DOUBLE" Then
    		If @AutoItX64 Then
    			; hier sollte der x64 DOUBLE-Code hin
    		Else
    			; hier sollte der x64 FLOAT-Code hin
    		EndIf
    	Else ; "FLOAT"
    		If @AutoItX64 Then
    			; hier sollte der x86 DOUBLE-Code hin
    		Else
    ;~ 			$ret = _AssembleIt2("float","sumfloat32",  "ptr",$pPointer,"uint", $iN);,"retbinary",@scriptlinenumber)
    ;~ 			ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $ret = ' & $ret & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console					$ret = _AssembleIt2("float","sumfloat32",  "ptr",$pPointer,"uint", $iN,"retbinary", @ScriptLineNumber)
    
     			$ret = DllCallAddress("float:cdecl", DllStructGetPtr($tCodebuffer), "ptr",$pPointer, "uint", $iN)
    			return $ret[0]
    
    		EndIf
    	EndIf
    
    EndFunc
    
    
    Func _dllstructcreate64($struct)                                       ;align auf 64-byte adresse
        Local $temp = DllStructCreate($struct)
        Local $tempsize = DllStructGetSize($temp)
        Local $mem = _MemVirtualAlloc(0, $tempsize, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
        $mem_dllstructcreate64_internal = $mem
        Local $sstruct = DllStructCreate($struct, Number($mem))
        Return $sstruct                                                    ;auf 64 alingned pointer
    EndFunc
    
    ;~     Exit
    Alles anzeigen
  • Assemblerfunktion zum Aufsummieren direkt in AutoIt

    • Andy
    • 31. August 2024 um 16:31
    Zitat von UEZ

    Das ist der x64 ASM Output vom Compiler:

    Cool, bin nur drübergeflogen, aber wie das aussieht, ist der "clevere" Teil von dir geschrieben. Zeig mal bitte den Freebasic-code.

  • Assemblerfunktion zum Aufsummieren direkt in AutoIt

    • Andy
    • 31. August 2024 um 15:47

    Naja,

    man könnte ja die dll, bzw deren Inhalte/Code mit entsprechendem Header versehen und in eine AutoIt-Funktion aka DllCallAddress() überführen.

    AspirinJunkie hat ja die "Kompatibilität" auch zu "älteren" Prozessoren angesprochen, ich werde "oldschool" x86 (8088)-Code schreiben, und, da es ja auch Leute mit "neueren" Prozessoren geben sollte, deren SSE- bzw. AVX-Fähigkeiten ansprechen. Es gibt dort Prozessorfunktionen um 4/8/16 floats/double in einem rutsch zu addieren. das bekommst du ohne C bzw. C++ "intrinsics" (welche auch nur die Prozessorfunktiopn wrappern) nicht hin.

  • Assemblerfunktion zum Aufsummieren direkt in AutoIt

    • Andy
    • 31. August 2024 um 15:29

    Heyho!

    Zitat von AspirinJunkie

    Wer weiß - vielleicht hat einer ja Lust mir entsprechend zu helfen.

    Aber sowas von!!!! Endlich habe ich mal Gelegenheit, mich für deine vielen genialen Posts und Scripte zu revanchieren:party:

    Ich geh mal in die Spur....

  • PDF lesen Teile ausgeben in Textform

    • Andy
    • 19. August 2024 um 18:30

    Hi,

    Zitat von Muecke_1982

    Meint ihr so etwas ist möglich

    ja

    Zitat von Muecke_1982

    und welchen Aufwand müsste ich dafür betreiben

    kommt drauf an....manche Programme sind in der Lage, PDF´s zu erzeugen, aus denen man dann auch tatsächlich etwas sinnvolles extrahieren kann. Teste das einfach mal, indem du bei geöffneter PDF mit CTRL-a alles markierst, dann mit CTRL-c kopierst, und diesen "Block" dann in Word oder Excel (halbwegs anständige Auto-Import/Formatierungsfunktion) oder in eine andere Office-Suite per CTRL-v einfügst. Manchmal hat man Glück und die Tabellenformatierungen werden mitgenommen....wenn du Pech hast, dann scheitert es schon daran....Versuch macht kluch...

    Ansonsten testweise einzelne Blöcke im PDF markieren und kopieren.

    Letztendlich kommt es darauf an, "irgendwie" die Daten strukturiert aus dem PDF zu bekommen, RegEx ist dein Freund....

    Wie man dann SAP automatisiert befüllt, ist wieder eine andere Baustelle. Ich hatte dabei schon Glück, die Eingabefelder waren per TAB-Taste und anderen Tastaturbefehlen anzusprechen und automatisiert auszufüllen.

    Zitat von Muecke_1982

    und wichtig ist das ich dafür nichts auf dem Firmenrechner installieren muss.

    DAS ist deine eigentliche Baustelle! Ja, es gibt eine portable AutoItversion, das Script muss auch keine "*.EXE" sein, falls da jemand Angst hätte, aber ich glaube ehrlich gesagt nicht, dass ein Admin, der sich schon schwer tut

    Zitat von Muecke_1982

    leider habe ich nach langem hin und her mit der EDV keine Lösung gefunden

    davon überzeugt werden kann, Scripte auf seiner Maschine auszuführen, die potenziell Schaden anrichten können....

  • If verzweigung treibt mich in den Wahnsinn

    • Andy
    • 9. August 2024 um 14:59

    Hi,

    eben gerade bei Hackaday drübergestolpert....https://hackaday.com/2024/08/07/com…-if-statements/

    Und hier treibt eine einzige IF-Verzweigung in den Wahnsinn:D

  • Laminat Calculator

    • Andy
    • 11. Juni 2024 um 19:53

    Hi,

    schön, was man alles so berechnen kann :klatschen:

    Mir würde nicht im Traum einfallen, "Dielenbretter" berechnen zu wollen. Die erste Reihe wird "grob" vorgelegt, geht das letzte Brett (bzw dessen abzuschneidender Rest) nur knapp aus, wird das erste Brett (an der Wandseite^^) "frei Schnauze" so abgeschnitten, dass der abgeschnittene Teil des letzten Brettes als Anfang der nächsten Reihe verwendet wird. Und so geht das immer weiter, das Reststück ist der Anfang der nächsten Reihe...

    Wenn es gut läuft, hat man in einer ganzen Wohnung so nur einen Eimer voll Reste, incl, der Türzargenausschnitte (so man denn die Zargen nicht abschneidet und die Dielen drunterlegt). Nach einigen tausend m² verlegter Böden weiß man dann wie es geht :o)

    Ich bevorzuge beim Laminat (genau wie bei Echtholz) zwar "Markenware", aber muss zugeben, dass mittlerweile auch bei sehr preiswertem Material gute bis sehr gute Passgenauigkeit vorliegt.

    In unserer eigenen Wohnung hatten wir zum Einzug 90m² "Auslaufmodell" in schönem Holzdekor von Parador für einen echten Schnäppchenpreis geschossen....dafür hatten die Sockelleisten dann das doppelte des Bodens gekostet:rofl: Vor einigen Monaten sind wir dann umgezogen und hatten für den 11 Jahre alten Boden vom Nachmieter noch 1000,-€ geboten bekommen. Und ich wollte die Dielen schon rausreißen und entsorgen:saint:

    Zitat von BugFix

    Nun, ich wollte nicht "Einzelnes-Laminat-Dingens" schreiben.

    Made my day!!:rock:

    Sollte so in den Duden! Und taucht (hoffentlich) auch demnächst durch die crawlenden "lerngeilen" KI-Bots auch als Umschreibung für das profane "Landhausdiele" oder "Dielenbrett" in diversen "Fachartikeln" zum Thema auf!!:D

    Bin schon gespannt auf die neuen Ausgaben des Laminat-Magazins oder Parkett-Magazins, bei denen sich dann ein Redakteur von der KI beim Texten hat helfen lassen..."Einzelnes-Laminat-Dingens"....:Face:

    Vielleicht solltest du aus dem Script eine Smartphone-App basteln und an die Bodenleger verticken. Ich kenne einige, die würden solch ein Tool bitter nötig brauchen, es ist nämlich extrem aufwendig und kompliziert, aus der Länge und Breite eines Raums die benötigten Quadratmeter Boden zu ermitteln:/

  • Assembler: Farben zählen

    • Andy
    • 15. Mai 2024 um 21:04

    Hallo DOheim,

    Zitat von DOheim

    Ich wunderte mich, dass es aber bei manchen Bitmapdateien abgebrochen ist und habe herausgefunden, dass die Bitmapdatei mindestens ein schwarzes Pixel besitzen muss.

    hmmm, scheinbar hatte ich das verbockt....so ist das, wenn man auf die Schnelle etwas umbaut, mit einigen Dateien testet und dann doch irgendwann ein Fehler auftritt:P

    Jetzt läuft es dank dir doch rund:party:

    Zitat von DOheim

    Im Debugger-Fenster werden bei mir die übergebene Parameter um eine Position verschoben angezeigt. Z.B. die Werte von width und height stehen eigentlich in [esp+8] und [esp+12] werden aber im Debugger-Fenster unter [esp+12] und [esp+16] angezeigt.

    Nein, du hast nichts falsch gemacht!:thumbup:

    Ja, das kommt durch das PUSH EAX nach dem RDTSC (das liest einen Zeitstempel der seit dem Computerstart vergangenen Prozessortakte nach EDX:EAX aus. Damit wird der aktuelle Zeitstempel (der untere, für uns relevante Teil steht in EAX ) ausgelesen). Ich nutze das RDTSC ausschliesslich zum "Takte zählen":Glaskugel:. Für den Programmablauf braucht man das definitiv nicht!

    Das PUSH EAX schiebt EAX "oben" auf den Stack, deshalb wird EAX dort eingefügt. Der Stack wird zum Speichern lokaler Variablen [ESP+12] und für Rücksprungadressen verwendet.

    PUSH erhöht auch den Stack Pointer ESP , das ist der Zeiger, mit dem du alle Elemente auf dem Stack ansprechen kannst.

    Schreib mal ein zusätzliches _asmdbg_() vor das RDTSC, dann siehst du, was passiert.

    Wichtig in diesem Zusammenhang ist, dass bei jedem Funktionsaufruf an den Prozessor, also CALL oder auch eine Funktionsaufruf einer AutoIt-Funktion oder jedweger anderer Programmiersprachenfunktion die aktuelle Speicheradresse ALS ERSTER PARAMERTER (das ist die sog. "Rücksprungadresse") IMMER zuerst auf den Stack geschrieben wird!!! Das Programm arbeitet die Funktion ab und muss nun irgendwie in das Hauptprogramm zurückkommen, günstigstenfalls an die Speicherstelle, von der aus die Unterfunktion aufgerufen wurde! Diese Speicheradresse wird also gesichert, damit der Prozessor den Weg zurück ins Hauptprogramm findet. Im Debugger ist das [ESP+00], also die Speicherstelle, auf die der Stackpointer zeigt. Beim Start des Programms ist dort die "Rücksprungadresse" (in das aufrufende Programm). Danach folgen dann die Funktionsparameter.

    Das ESP-Register ist also nur ein Zeiger auf den Stack. Du kannst bspw. mit SUB ESP,100 dir 100 Bytes auf dem Stack als Speicherplatz reservieren. Dann kannst du mit mov [ESP+76] ,0xFFAABBCC dort einen Wert reinschreiben und benutzen. Am Ende deines Programms ist nur wichtig, dass das ESP-Register auf die gleiche Adresse zeigt wie beim Start des Programms. Deshalb gehört zu jedem PUSH im Programm auch das entsprechende POP. Oder zum SUB ESP,100 das entsprechende ADD ESP,100.

    Wie du jetzt gemerkt hast, ändert sich der Inhalt der (Stack-)Speicherstelle [ESP+12] mit jedem PUSH oder POP in deinem Programm. Die Reihenfolge der Parameter auf dem Stack ändert sich nicht!!

    Du kannst ja mal eine Sequenz von folgenden Befehlen auf dem Stack beobachten um zu sehen, wie das Prinzip funktioniert.

    Code
     mov EAX,1	;Register füllen
     mov EBX,2
     mov ECX,3
     _asmdbg_()
    
    PUSH EAX	;schreibt EAX auf den Stack und erniedrigt das ESP-Register um 1
     _asmdbg_()
     PUSH EBX
     _asmdbg_()
     PUSH ECX
     _asmdbg_()
     
     mov EAX,0	;Register überschreiben ist unkritisch, denn EAX wurde auf dem Stack "zwischengespeichert"
     mov EBX,0
     mov ECX,0
     _asmdbg_() ;alle Registerinhalte sind jetzt 0 aber die ursprünglichen Werte sind auf dem Stack abgespeichert
     
     ;und dann
     POP ECX	;schreibt den aktuellen Inhalt von der Speicherstelle auf den ESP zeigt, nach ECX
     _asmdbg_()  
     POP EBX	;schreibt den aktuellen Inhalt von der Speicherstelle auf den ESP zeigt, nach EBX
     _asmdbg_() 
     
     ;jetzt ersetzen wir POP EAX "zu Fuß"
     mov EAX,[ESP]	;holt sich den aktuellen Speicherinhalt von [ESP] , also die 1
    ADD ESP,4 	;zeigt auf die nächste Speicherstelle auf dem Stack (4 bytes, 32 Bit!!)
    ;mov EAX,[ESP] und ADD ESP,4 ersetzen die Funktion des POP EAX
    Alles anzeigen

    Obwohl die Register EAX,EBX und ECX im Programmablauf mit 0 überschrieben wurden, sind am Ende des Programms die ursprünglichen (auf den Stack mit PUSH "gesicherten" Werte) per POP wieder zurückgeschrieben worden.


    Man muss sich nur EINE Regel im Kampf mit dem Stack merken: Der Speicherinhalt an [ESP] muss am Ende des Programms der gleiche sein wie am Anfang des Programms: die Rücksprungadresse in das aufrufende Programm!

    (Zu jedem PUSH gehört das entsprechende POP)

    Und da auf dem Stack ALLE Rücksprungadressen aller Funktionen (die wiederum Funktionen aufrufen die innerhalb von Schleifen Funktionen in Funktionen aufrufen) stehen, sollte man wissen was man tut. An "irgendwelche" Adressen auf dem Stack zu schreiben (die vorher nicht entsprechend reserviert wurden) führt unweigerlich zum Crash!


    Wenn man Programme schreibt, die den Stack (pointer) nicht verändern, kann man im Programmablauf natürlich auch [ESP+12] als Wert des Übergabeparameters verwenden bzw diesen Wert auch verändern/überschreiben und so als Speicherstelle benutzen!


    Du kannst also das RDTSC und PUSH EAX am Anfang des Programms einfach entfernen, musst dann aber auch das POP EBX am Ende des Programms entfernen. Dann sieht der Stack auch während des Programmablaufs genau so aus, wie am Anfang!:party:

    Eine verständliche Erklärung vom Stack findet man bspw. hier https://www4.cs.fau.de/Lehre/WS09/V_B…ler.shtml#stack

  • Frage zu DLLCall

    • Andy
    • 5. April 2024 um 20:30

    Hey BigRox,

    Zitat von BigRox

    Warum kann ich den SPI_SETMOUSSONAR nicht verwenden, sondern muss die Konstante von AutoIt (oder 0x101D) verwenden?

    SPI_SETMOUS E(das E vergessen?!) SONAR (in dem Fall als Variablenname in der API-Definition) IST 0x101D. Google mal nach SPI_SETMOUSESONAR und du findest https://learn.microsoft.com/de-de/windows/…parametersinfoa

    Alle Parameter zu den Funktionsaufrufen findest du in den *.h-files, das h steht für header. Diese Systemdateien werden idR. bei der Installation eines C/C++-Compilers mitinstalliert, du kannst einzelne aber auch einfach bei google suchen und bekommst dann Links zu bspw. Github. Dort kannst du dann in die h-files reinschauen und die Definition des Parameters nachvollziehen.

    Dein Fehler war, anstatt die Variable SPI_SETMOUSESONAR zu verwenden, den TEXT(!) "SPI_SETMOUSESONAR" einzusetzen. In den API-Funktionsbeschreibungen gibt es zwar auch text-Parameter, in diesem Fall ist aber ein UINT gefragt.

    Wenn mir irgendwelche esoterischen Datentypen unterkommen, (in C/C++ und auch anderen Sprachen kannst du selbst deine Datentypen definieren), dann schaue ich mir die *.h-files an und ggf. andere Programmierbeispiele.

    Zitat von BigRox

    Und warum muss ich bei einem PVOID-Datentyp UINT-angeben?

    Hehe, auch hier ist google dein Freund :o)

    Beckhoff Information System - German

    Letztendlich beschränken sich die Datentypen auf 8, 32- oder 64-Bit Datentypen. C-Typisch werden selbst bei den kompliziertesten und komplexesten strukturierten und völlig sinnfrei von irgendeinem XY_Programmierer-zusammengepfriemelt aufgebauten Datentypen nur sog. Pointer, also Zeiger auf diese Struktur in der Funktion übergeben. Innerhalb der Funktion wird dann über die tippeltappeltour auf den Pointer vom Pointer auf die Liste auf eine Verkettung von Pointern auf eine Struct uswusf. verwiesen und dort die Daten ermittelt. Damit du nicht zig Variablen übergeben musst, werden diese zusammengefasst und per Pointer, auf bspw. eine Struct oder auch auf Programmcode, übergeben.

  • Assembler: Farben zählen

    • Andy
    • 1. April 2024 um 06:26
    Zitat von DOheim

    Ich habe noch eine Frage:

    Wäre es möglich, im Assemblerprogramm den Debugger-Aufruf z.B. so zu gestalten

    Stelle3) _asmdbg_()

    und dann im Debugger-Fenster die Zeichenfolge Stelle3 anzuzeigen? Das würde ungemein helfen.

    Wenn du Scite als Editor verwendest, springt der Debugger in die Zeile im Script, in der sich der Debuggeraufruf aktuell befindet. Du siehst auch die blauen Klammern hinter _asmdbg_()

    Für das debuggen ist es also sehr hilfreich das Debuggerfenster so zu plazieren, dass der Scriptcode in Scite nicht verdeckt ist.

    Dann kannst du, wenn du im Debuggerfenster auf NEXT klickst, den Schritt zum nächsten _asmdbg_() mitverfolgen.

    Außerdem steht in der Kopfzeile des Debuggerfensters, in welcher Zeile in Scite sich der Debugger aktuell befindet

Spenden

Jeder Euro hilft uns, Euch zu helfen.

Download

AutoIt Tutorial
AutoIt Buch
Onlinehilfe
AutoIt Entwickler
  1. Datenschutzerklärung
  2. Impressum
  3. Shoutbox-Archiv
Community-Software: WoltLab Suite™