Assembler - Fortsetzung

  • Fortsetzung von:
    Link
    ---------------------------------------------------------------------

    Ich habe die folgenden beiden Probleme

    1. Problem

    Wie kann ich _AssembleIt2 einen String zugänglich machen, welcher Nullbytes beeinhaltet?
    Mit folgendem Programm


    erfolgt dieser Abbruch:

    autoit.de/wcf/attachment/83503/

    Ersetze ich in assembleit2_64.au3 die Zeile

    If Eval("Type" & $n) <> "" Then $scriptstring &= ",'" & Eval("Type" & $n) & "'," & Eval("Param" & $n)

    durch die Zeile

    If Eval("Type" & $n) <> "" Then $scriptstring &= ",'" & Eval("Type" & $n) & "'," & "$Param" & $n

    erfolgt zwar kein Abbruch, jedoch werden dem Assemblerprogramm nur die ersten drei Zeichen (bis zum Nullbyte) zur Verfügung
    gestellt.
    Am besten wäre es meines Erachtens, wenn man die Adresse des Strings ermittel könnte und diese dann dem Assemblerprogramm zur
    Verfügung stellen könnte. Aber wie ermittle ich die Adresse eines Strings?


    2. Problem


    Wenn das Assemblerprogramm als Ergebnis ein Feld erarbeitet, wie kann ich das dem aufrufenden Autoit-Programm zur Verfügung stellen?


    Vielen Dank schon einmal für eine Antwort !

  • Am besten wäre es meines Erachtens, wenn man die Adresse des Strings ermittel könnte und diese dann dem Assemblerprogramm zur
    Verfügung stellen könnte. Aber wie ermittle ich die Adresse eines Strings?

    Die Adresse übergibst du, indem du an den Typ (int,str,byte usw.) ein * anhängst, also "str*" oder "int*".

    AutoIt behandelt Strings bei der Ausgabe/Anzeige genau wie C/C++ (logisch, deren API-Funktionen werden benutzt...).
    Daher sollte man, wenn man Strings mit enthaltenen Nullbytes benutzt, diesen an eine Struct übergeben.
    Die Frage ist, wo Strings mit enthaltenen Nullbytes herkommen, deren Vorkommen in der freien Wildbahn ist SEHR begrenzt! IdR. sind das sowieso BYTE Structs, daher sollte man diese auch so behandeln!

    Weiterhin musst du die "Endianess" (<-klick mich, ich bin ein Link) beachten, das bringt die meisten Assemblerjünger (und meist auch die Cracks) zur schieren Verzweiflung.
    Bsp.: Im Speicher stehen nebeneinander die Bytes ABCD.
    Liest man die BYTES nacheinander aus, dann erhält man als Lowbyte bspw eines Registers (also BL von EAX) erst A, dann B, dann C, dann D
    Liest man ein DWORD (egal welchen Typs) aus, dann erhält man im Register EAX die Bytereihenfolge DCBA....viel Spass^^


    Wenn das Assemblerprogramm als Ergebnis ein Feld erarbeitet, wie kann ich das dem aufrufenden Autoit-Programm zur Verfügung stellen?

    Am besten, indem du eine Struct erstellst und dann ausliest. An den Autoitcode gibst du den Pointer zurück!

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    3 Mal editiert, zuletzt von Andy (13. Januar 2016 um 20:43)

  • Hallo Andy,
    recht vielen Dank für Deine ausführlich Antwort.
    Das wird mir sehr weiterhelfen. Schön ist es, dass Du zu jedem Problem immer ein passendes und anschauliches Musterprogramm mitschickst. Und ohne den prima Debugger würde ich ohnehin immer völlig im Dunkeln stehen.
    Jetzt muss ich alles erst einmal verdauen.

    Die Frage ist, wo Strings mit enthaltenen Nullbytes herkommen, deren Vorkommen in der freien Wildbahn ist SEHR begrenzt! IdR. sind das sowieso BYTE Structs, daher sollte man diese auch so behandeln!

    Auf diese Weise will ich die eingelesene Bitmap-Datei dem Assemblerprogramm zugänglich machen. Und Bitmap-Dateien haben ja naturgemäß jede Menge Nullbytes.
    Bis bald Gruß Dieter

    PS: noch eine weitere Frage:
    Wie kann ich in meinem Assemblerprogramm einen zusammenhängenden Speicherbereich bestimmter Größe reservieren?

    Einmal editiert, zuletzt von DOheim (14. Januar 2016 um 17:28)

  • Hi,
    bei Grafiken/Bitmaps brauchst du dich idR um garnichts kümmern!
    Es gibt diverse Funktionen, um an den Pointer auf die Pixeldaten einer Bitmap zu kommen.
    Ich habe die Funktionen _CreateNewBmp32() und _CreateNewBmp32FromFile() erstellt, weil sie im Vergleich zu anderen Methoden sehr schnell und flexibel sind!

    Um Grafiken selbst zu erstellen muss man eine leere (32-Bit BGRA-)Bitmap erstellen, und dann die Pixel einfach an die richtige Adresse reinschreiben.
    $HDC = _CreateNewBmp32($breite, $hoehe, $ptr, $hbmp) findest du in den Beispielen. Die Funktion arbeitet mit globalen Variablen $ptr und $hbmp, den ptr=Pointer erhält man somit, mit dem hbmp kann man die GDI-Funktionen bedienen. Der RückgabeParameter $HDC ist das Handle zum Device-Context DC, diesen benötigt man, um die "schnellen" BitBlt-Funktionen verwenden zu können. Siehe Beispiele....

    Grafiken laden kann man mit _CreateNewBmp32FromFile(). Identisches Procedere.
    Um sog. Backbuffer für bewegte, flimmerfreie (!) Grafiken zu erstellen kann man diese Funktionen natürlich auch nutzen!

    Beispiel, um Bild in Graustufen zu verwandeln, der Kommentar für den ASM-Code ist in englisch, da ich dieses Script im engl. Forum gepostet hatte.
    Im Prinzip geht es darum, den R, G, undB-Anteil des Pixels zusammenzuzählen und das Ergebnis durch 3 zu teilen. Somit erhält man einen Durchschnitt, diesen auf R,G und B geschrieben den entsprechenden Grauton.
    Da ein DIV in Assembler so ziemlich die "teuerste" Möglichkeit ist, durch einen Wert zu teilen, habe ich die wesentlich schnellere Variante gewählt 8o
    Man schreibt ja keine Assemblerscripte, um dann mehrere hunderttausend Prozessortakte zu verschwenden :party:


    Btw muss man sich bei Bitmaps auch nicht ums alignen kümmern, Bitmaps werden glücklicherweise an 16-Bytegrenzen aligned^^

    Also ideal, um auch Hardcore SSE/SIMD-Programme zu schreiben /* malzueukalyptusrüberwinkt */


    Wie kann ich in meinem Assemblerprogramm einen zusammenhängenden Speicherbereich bestimmter Größe reservieren?

    Das macht man günstigstenfalls per dllstructcreate(). Da in dieser Funktion aber das alignen nicht zuverlässig funktioniert (hatte?), habe ich dort auch eine Funktion _dllstructcreate64() geschrieben, die braucht man ohnehin, um aligned Speicher in 64_Bitanwendungen zu reservieren.
    Diese Funktion ist in AssembleIt2_64 enthalten, einfach so benutzen wie dllstructcreate()

    Bei 32-Bitanwendungen ist idR kein alignment nötig, man kann also "normalerweise" dllstructcreate() verwenden.

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    3 Mal editiert, zuletzt von Andy (14. Januar 2016 um 22:22)

  • Recht vielen Dank!
    Du hast Dich ja schon sehr intensiv mit der Materie beschäftigt.
    Jetzt habe ich ja eine Menge "Handwerkszeug" zur Verfügung.
    Wie gesagt: Jetzt geht es erst einmal ans "Verdauen".
    Nochmals vielen Dank
    von Dieter

  • Wenn es um die Bearbeitung/Erstellung von Bitmaps geht, dann stelle dein Vorhaben/Idee hier im Forum vor, BEVOR du Stunden/Tage auf die Umsetzung verwendest. Es gibt hier einige Mitglieder, die bereits viel umgesetzt/programmiert haben. Mein eigener Fundus beschränkt sich auf etwa 100 ASM-Projekte, leider habe ich nicht mehr die Zeit mich intensiver mit der Materie zu beschäftigen...

  • Vielen Dank für Deinen Hinweis. Mein Bitmap-Programm habe ich ja schon lange fertig, und es klappt auch sehr gut. Aber es dauert eben so lange. Ich muss lediglich zwei Passagen durch ASS ersetzen. Seine Funktion hatte ich ja am 14. Dezember 2015 unter Assembler kurz vorgestellt.

    In meinem oben am Mittwoch, 17:02, eingestellten Programm habe ich entsprechend Deinem Hinweis "str*" statt "str" geschrieben. Aber _ASMDBG_() zeigt mir dann im Register ebx alles andere als meine String-Bytes an. Habe ich da etwas falsch verstanden?

    PS: Jetzt habe ich es kapiert: "str*" erzeugt die Adresse der Adresse des Strings.

    Einmal editiert, zuletzt von DOheim (16. Januar 2016 um 11:53)

  • Schau dir das mal an, hier hatte ich alle Pixelfarben einer Bitmap und ihre Anzahl per ASM ermittelt und diese direkt als String (RRGGBB,Anzahl) ausgegeben.
    Das funktioniert auch bei großen Bitmaps mit tausenden von verschiedenen Farben innerhalb von einigen hundert Millisekunden (jedenfalls auf meinem lahmen Rechner^^)

    In dieser Variante ohne AssembleIt aber incl. ASM-Sourcecode.

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    Einmal editiert, zuletzt von Andy (24. Juni 2018 um 11:00)

  • Hallo Andy,
    das werde ich mir zu Gemüte ziehen.
    Vielen Dank
    Dieter

    PS: Es ist ja der reine Wahnsinn. Innerhalb von 42 ms hat das Programm einen 2,5 MByte Screenshot ausgewertet.
    Da werde ich eine ganze Passage verwenden können. Eigentlich wollte ich ja mühselig das ASS-Programmieren lernen. Aber wenn es einem so leicht gemacht wird, siegt natürlich der innere Schweinehund.

    2 Mal editiert, zuletzt von DOheim (16. Januar 2016 um 16:54)

  • Das Prinzip ist einfach:
    Es wird eine Struct (UINT) mit allen möglichen Farben (256^3 0x000000 bis 0xFFFFFF) erstellt.
    In der ersten Schleife wird jede Pixelfarbe ausgelesen und deren Adresse in der o.g. Struct einfach um 1 erhöht.
    Letztendlich entsteht eine Liste mit mehr oder weniger "Nullen", da die meisten Farben garnicht verwendet werden.
    Also wird in der nächsten Schleife diese Liste durchlaufen, und jedes Mal, wenn ein DWORD ungleich Null gefunden wird, dann wird diese Adresse (die Farbe) in die entsprechende Hex-Darstellung umgeformt, ein Komma hinzugefügt, und anschließend die Anzahl (Integer) ebenfalls als ASCII-Ziffern in eine weitere Struct (Text) geschrieben. Linefeed dazu, fertig! Nächste Farbe...
    Ist dieser Text erstellt, aus der Struct auslesen, per stringreplace() das LF durch CRLF ersetzen und als "Abfall" erhält man die Anzahl der Farben.

    Die Verwendung von XMM-Registern rund um die Erstellung des Ziffernstrings ist dem Registerpressure geschuldet! Hätten sich die Jungs/Mädels bei der ersten Herstellung von 32-Bit-Prozessoren einige hundert (omg, was hätte das gekostet!!! ) Register gegönnt, dann würde es bis heute keinen 64-Bit-Modus geben...
    Und die Software wäre wesentlich schneller gewesen...

    Btw., bei der Mona-Lisa werden bei mir in 130ms die 145019 verschiedenen Farben gefunden und deren Häüfigkeit, incl. Texterstellung.

  • Du machst nichts falsch, jedenfalls nicht wissentlich^^

    Das "Problem" sind die Variablen in deinem Code. Die Adressen dieser Variablen werden BEIM ASSEMBLIEREN (!!!!!) erstellt. Dazu ermittelt der Assembler anhand der ORG-Anweisung die ZUR ZEIT gültige Adresse des Assemblerprogramms im Speicher und addiert einfach die Bytes des Codes bis zu deiner Variable dazu.
    Beim nächsten Start des Programms sind aber völlig andere Adressen aktuell und es folgt ein Crash! Solange AssembleIt eingesetzt wird, ist das unerheblich, denn AssembleIt ermittelt die Startadresse per ORG $PTR_SOURCE_ASMCODE bei jedem Programmstart neu.

    Um das Problem zu umgehen gibt es mehrere Möglichkeiten:
    - Alle Variablen, Übergabeparameter, Rückgabewerte und was man sonst noch in und aus einem ASM-Programm verwenden will, schreibt man in eine Struct.
    Von dieser Struct muss man nur noch die Startadresse als einzige Variable an DllCallAddress() übergeben.
    Diese Variante wird bei größeren Projekten mit vielen Variablen bevorzugt.

    - Man reserviert sich einige Bytes vom Stack (SUB ESP,12) und kann nun per [ESP+0], [ESP+4], [ESP+8], diese 3 DWORDS benutzen. Nicht vergessen, den Stack nachher wieder per ADD ESP+12 wieder instandzusetzen. PUSH/POP innerhalb des Programms ist weiterhin verwendbar.

    -Man ermittelt die Startadresse des ASM-Programms im Speicher (die Adresse, welche bei ORG verwendet werden würde) und schreibt alle "Hartkodierten" Adressen NACH DEM ASSEMBLIEREN, ABER VOR AUSFÜHREN DES PROGRAMMS neu. Das ist das, was ein Compiler/Assembler macht, um bspw. EXE-Dateien zu erzeugen, welche ja bei jedem Start an einer anderen Adresse im Speicher geladen werden.. Relokation / relocation ist das Stichwort. Wenn ich irgendwann dafür Bedarf habe, werde ich das in AssembleIt() integrieren.


    Weiterhin ist mir aufgefallen, dass du die Bitmap-Dateien einfach einliest, die 54 Byte Header abschneidest und diese "Bitmapdaten" dann als Pixel weiterverwendest. So habe ich das früher auch gemacht :)
    Das funktioniert dann sauber, wenn man Einfluss auf die Erstellung der Bitmapdateien hat! Wenn irgendwelche Software verwendet wird und diese dann bspw. in der Bitmap noch Paletten verwendet, ist man in den Ar*** gekniffen ;) Vorsicht auch mit "Standardsoftware". Die ist nur so lange "Standard" wie der Hersteller das postuliert.
    Sicherer ist die Verwendung von _CreateNewBmp32FromFile(), damit hat man alle relevanten Parameter/Variablen.
    Sind die von dir verwendeten Bitmaps alle so klein, bzw. haben die nur wenige Farben?

  • Recht vielen Dank Andy für Deine so ausführliche Antwort.
    Jetzt werde ich es in den Griff bekommen.
    Nein, das ist nur eine kleine Bitmap-Datei, mit der ich den Algorithmus getestet habe.
    Es wird bestimmt nicht meine letzte Frage gewesen sein.
    Nochmals herzlichen Dank!
    Gruß
    Dieter

  • Ich sehe an deinem Code, du bist ( wieder?! ) mit dem ASM-Virus infiziert...das freut mich :party:
    Vielleicht hast du ja noch andere Scripte / Programm(teil)e, in denen man etwas beschleunigen könnte...immer her damit :thumbup:

  • Außer "Zeile" waren ja alle Variablen in meinem Programm nicht notwendig. Ich habe sie eigentlich nur eingeführt, damit das Programm besser lesbar ist. Und statt "Zeile" habe ich einfach xmm1 verwendet. Übrigens habe ich über diese xmmi-Register nirgendwo etwas gefunden. Ich habe es mir einfach bei Dir abgeguckt. Es scheint so zu sein, dass sie nur mit anderen Registern korrespondieren können (z.B.: mov xmm1,eax), nicht aber mit Speichzellen (z.B.: mov xmm1,0 bzw. mov xmm1,[eax]).

    Vielleicht hast du ja noch andere Scripte / Programm(teil)e, in denen man etwas beschleunigen könnte

    Das Farben einsammeln war ja bisher erst der erste Teil. Nun kommt noch das Einfügen der veränderten Farben. Und das verbraucht bisher noch mehr Zeit. Aber wahrscheinlich muss ich mich jetzt erst einmal um den Umstieg auf Win10 kümmern. Im Sommer habe ich dafür dann keine Zeit mehr (Garten).

    Ich sehe an deinem Code, du bist ( wieder?! ) mit dem ASM-Virus infiziert...das freut mich

    Das habe ich aber nur dem hervorragenden Lehrgang, den ich bei Dir "besucht" habe zu verdanken. Schließlich war ich ja vom Embedding-Assembler völlig unbeleckt, als ich das Thema Assembler aufgesetzt hatte. Anhand dieser beiden Themen "Assembler" und "Assembler-Fortsetzung" kann jetzt jeder, der es möchte, in die Ass-Programmierung einsteigen.
    Nochmals vielen Dank dafür!

  • Die XMM-Register 0 bis 7 sind erweiterte Register (128-Bit) und wurden mit dem "erweiterten" SSE/ SIMD (Single Instruction, Multiple Data)-Befehlssatz eingeführt,
    Damit kann man bspw. das XMM-Register je nach Befehl als 128Bit,2x64Bit, 4x32Bit, 8x16Bit oder 16x8Bit ansprechen und mit nur einem Befehl alle diese "Blöcke" im Register gleichzeitig bearbeiten. So ist es bspw möglich, 4 Pixel (4x32Bit) gleichzeitig zu verarbeiten! Oder 16 Bytes gleichzeitig uswusf...
    Sehr sehr fein :thumbup: , vor allem, weil die meisten Befehle nur 1-2 Prozessortakte benötigen!

    Ich habe in den Tutorials ein "altes" AMD-Papier mit der Befehlsreferenz (incl. Zeichnungen/Bildern!!!) verlinkt, etwas besseres habe ich bisher zu diesem Thema nicht gefunden.

    Achtung! Um mit diesen XMM-Registern zu kommunizieren, kann man NUR die SSE-Befehle verwenden, MOVD xmm1,eax schreibt den Inhalt des eax-Registers in die unteren 32Bit von xmm1 (entsprechend zurück mit MOVD eax,xmm1).
    Ich benutze das um ein paar Takte Speicherzugriff zu sparen statt PUSH/POP :rolleyes:
    Speicherzugriffe versuche ich wo es nur geht zu vermeiden, Wenn man mal Takte gezählt hat, wird man in dieser Beziehung etwas "eigenartig" :Face: Bekloppt ist man heutzutage bei der Verwendung von ASM sowieso :rolleyes:
    Ausserdem kann man in den 8 XMM-Registern 32 der "normalen" 32-Bit-Register zwischenspeichern, Register kann man nie genug haben 8o

    Natürlich kann man auch die XMM-Registerinhalte komplett oder teilweise in den Speicher (oder Speicher ins Register) schreiben. Mit SSE/SIMD-Befehlen ist der Befehlssatz im Prozessor um ca. das fünffache angestiegen, incl. einiger sehr schneller Befehle (1 Takt) um bspw. Registerinhalte zu addieren und danach zu multiplizieren...


    Ich stehe allerdings auf dem Standpunkt, dass man mit zwei Handvoll x86-ASM-Befehle JEDES programmiertechnische Problem erschlagen kann. Nur wenn man das Programm dann noch um Faktor x beschleunigen will, muss man tiefer in die Trickkiste greifen!

  • Vielen Dank für Deine Erklärungen.

    Ich habe in den Tutorials ein "altes" AMD-Papier mit der Befehlsreferenz (incl. Zeichnungen/Bildern!!!) verlinkt, etwas besseres habe ich bisher zu diesem Thema nicht gefunden.

    Wo finde ich das Tutorial?

    Ich stehe allerdings auf dem Standpunkt, dass man mit zwei Handvoll x86-ASM-Befehle JEDES programmiertechnische Problem erschlagen kann.

    Nachdem ich in meinem Bitmap-Programm den entsprechenden Abschnitt ersetzt habe, werden statt 31 Sekunden nur noch 57 Millisekunden benötigt. Erfolg 1:500
    Gruß Dieter

  • Wo finde ich das Tutorial?

    Weniger ein Tutorial als ein Programmers Manual.

    Ist aber wie gesagt nur etwas für "Fortgeschrittene".
    Die Idee hinter AssembleIt ist ja, das gesamte Rumgezackere mit Fenstern, Speicher und Ein-Ausgabegedöns ,also alles, was in Assembler richtig Arbeit macht und fehleranfällig ist, von AutoIt machen zu lassen und in Assembler wirklich nur den benötigten "schnellen Inner Loop" in einer Handvoll Bytes umzusetzen.
    Egal um welches Problem es sich handelt, ist das mit einer Handvoll Befehlen zu erledigen. Kürzer/schneller geht aber IMMER! Das aber hinzubekommen "separates the boys from the men..." ;)


    Nachdem ich in meinem Bitmap-Programm den entsprechenden Abschnitt ersetzt habe, werden statt 31 Sekunden nur noch 57 Millisekunden benötigt. Erfolg 1:500

    Das muss so sein! :thumbup:

  • Hallo,

    ich Uppe den Thread mal.

    Wollte nochmals das Problem mit grossen Dateien ansprechen.

    bekomme bei meinen gewandelten *.bmp Bildern wie bei den *.tif Bildern von Autoit eine Fehlermeldeung:

    msgbox(0,"AutoIt","Error allocating Memory") ;mit Fehlerkreuz Icon in der Fehlermeldung

    und Speicherausnutzung von 2.191,9 MB