AutoIt & ASM

  • Moin,

    Wie sagt Andy immer so schön: "Damals war alles besser... int21h, dann direkt in den Grafikspeicher ab 0B000h (PS: Diese Adresse ist für monochromatische Grafik :thumbup: ) reingeballert, und die Welt programmiert sich fast von alleine"
    (Der korrekte Wortlaut muss nicht mit Obigem übereinstimmen ;) )

    Was das Skript können soll:
    Es stellt eine Umgebung bereit (ein Fenster variabler Größe, einen Grafikpuffer, etwas RAM) in der man mit ASM ohne weiteres "direkt" (das ist die schönste Lüge die mir heute über die Lippen geht) ins Fenster zeichnen kann.

    Was es wirklich tut:
    Es stellt eine Umgebung bereit (ein Fenster variabler Größe, einen Grafikpuffer, etwas RAM) in der man mit ASM ohne weiteres so tun kann als würde man "direkt" ins Fenster zeichnen. :thumbup:

    Tatsächlich zeichnet man nur in eine Struct, die wiederum Teil eines WinAPI-GDI Puffers ist (kein GDI+), der wiederum mit einer voreingestellten Framerate ins Fenster verfrachtet wird (BitBlt). Dabei werden zwar nanosleep und Timer benutzt, zu 100% trifft man den vsync aber nicht, es ruckelt wenn es möchte. Ein sehr kleines, aber gut Kommentiertes Beispiel ist dabei. Wenn man selbst basteln will, so möge man NUR den ASM Code im Beispiel ändern, das restliche Skript stellt die Umgebung bereit. Wer Erweiterungen oder Sonstiges vornehmen will darf dies gerne tun :)

    Und wenns keiner braucht nehm ichs selbst :P

    lg
    M

  • Toller script Mars,
    €: stürzt leider tatsächlich ab wen der asm code länger ist
    leider wird der ASM code durch das sleep nur selten ausgeführt deshalb hab ich eine version mit Timern gemacht in der der code nicht so gebremst wird

    Spoiler anzeigen
    [autoit]

    #include 'EasyASM.au3'
    #include <WinAPI.au3>
    #include <Timers.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Opt('GUIOnEventMode', 1)
    _ASM_Startup()

    [/autoit] [autoit][/autoit] [autoit]

    ; Haupteigenschaften des Programms
    Global Const $sTitel = 'Test 01' ; Titel des Fensters
    Global Const $iW = Int(@DesktopWidth / 256) * 128 ; Breite des Fensters
    Global Const $iH = Int(@DesktopHeight / 2) ; Höhe des Fensters
    Global Const $iMemSize = 64 ; Reserviertes RAM in Bytes
    Global Const $nFrameRate = 60 ; Gewünschte Framerate (ob diese eingehalten wird hängt vom BitBlt, sowie vom ASM ab)

    [/autoit] [autoit][/autoit] [autoit]

    ; Aufbau der übergebenen Daten:
    ; int[5]
    ; [0] - Ptr für die Bitmapdaten
    ; [1] - Ptr für das reservierte RAM
    ; [2] - Anzahl reservierte Bytes (damit man im ASM weiß wie viel RAM man zur Verfügung hat)
    ; [3] - Breite der Anzeige | Die Anzahl Pixel errechnet man sich am
    ; [4] - Höhe der Anzeige | besten selbst, so bleibt $vData kleiner ;)
    Global $Memory = DllStructCreate('int[' & $iMemSize & ']')
    Global $Image = _ASM_CreateBitmap($iW, $iH)
    Global $hGUI = GUICreate($sTitel, $iW, $iH), $bExit = False
    Global $nAuslastung, $iFPS, $nFPS, $iFPS_Timer = TimerInit()
    Global $hDC = _WinAPI_GetDC($hGUI), $hDC_IMG = DllStructGetData($Image, 1, 1)
    Global $ntDLL = DllOpen('ntdll.dll'), $vNano = DllStructCreate('int64 time'), $pNano = DllStructGetPtr($vNano)
    Global $vData = DllStructCreate('int[5]'), $pData = DllStructGetPtr($vData)
    Global $iMyASM = _ASM_AddOP(_MyASM())
    DllStructSetData($vData, 1, DllStructGetData($Image, 1, 4), 1) ; Ptr auf die Bitmapdaten
    DllStructSetData($vData, 1, DllStructGetPtr($Memory), 2) ; Ptr auf das reservierte RAM
    DllStructSetData($vData, 1, $iMemSize, 3) ; Anzahl reservierte Bytes
    DllStructSetData($vData, 1, $iW, 4) ; Breite der Anzeige
    DllStructSetData($vData, 1, $iH, 5) ; Höhe der Anzeige
    GUISetOnEvent(-3, '_Exit', $hGUI)
    GUISetState(@SW_SHOW, $hGUI)

    [/autoit] [autoit][/autoit] [autoit]

    Global $i_timer1delay=10
    $timer = _Timer_SetTimer($hGUI, $i_timer1delay, "Paint");zeichnet alle x sekunden die GUI neu
    _Main()

    [/autoit] [autoit][/autoit] [autoit]

    ; ===============================================================================================================================
    ; Hier den eigenen Code einfügen. Das Restliche Skript unberührt lassen (außer man möchte es erweitern oder Fehler bekämpfen)
    ; ===============================================================================================================================
    Func _MyASM()
    _('use32')

    [/autoit] [autoit][/autoit] [autoit]

    ; Beim Call sind die Adressen um 4 Byte verschoben,
    ; später, beim Zugriff auf unsere Struct um Ram oder
    ; beim Zugriff auf die Bitmap ist das nicht mehr so !
    ; Um zur ersten Struct zu kommen müssen wir [esp+4] nehmen,
    ; hier liegt die ptr zu unserer DatenStruct

    [/autoit] [autoit][/autoit] [autoit]

    _('mov eax, [esp+4]') ; Ptr auf die Datenstruct

    [/autoit] [autoit][/autoit] [autoit]

    ; Wir wissen wie die Struct aussieht:
    ; ptrIMG|ptrMEM|anzahl|breite|höhe
    ; +0 | +4 | +8 | +12 | +16
    ; Hier gibts keine Verschiebung mehr, nie wieder :)

    [/autoit] [autoit][/autoit] [autoit]

    _('mov ebx, [eax+12]') ; Breite der Anzeige
    _('mov ecx, [eax+16]') ; Höhe der Anzeige
    _('imul ecx, ebx') ; Anzahl Pixel in ecx

    [/autoit] [autoit][/autoit] [autoit]

    ; Jetzt haben wir erstmal die Anzahl Pixel
    ; Fehlt nur noch eine Position an der wir
    ; wild herummalen können...

    [/autoit] [autoit][/autoit] [autoit]

    _('mov esi, [eax]') ; Ptr der Anzeige in ebx

    [/autoit] [autoit][/autoit] [autoit]

    ; man könnte jetzt jedes freie Register nutzen,
    ; wir nehmen einfach mal esi.
    ; Damit wir das Bild färben können brauchen
    ; wir noch eine Farbe die sich am besten
    ; laufend verändert. Dazu springen wir in
    ; unser bereitgestelltes RAM und benutzen die ersten 4 Byte. (0x FF FF FF FF = 4 Byte)

    [/autoit] [autoit][/autoit] [autoit]

    _('mov edx, [eax+4]') ; Ptr unseres Rams
    _('mov ebx, [edx]') ; Unsere ersten 4 Bytes !
    _('add ebx, 1024') ; Wir erhöhen die Farbe
    _('or ebx, 0xFF000000') ; Falls der Alphakanal flöten geht (overflow)
    _('mov [edx], ebx') ; neue Farbe speichern

    [/autoit] [autoit][/autoit] [autoit]

    ; Jetzt nur noch Alle Pixel ablaufen und die
    ; Farbe hineinmalen. (Dabei kann man sie natürlich
    ; weiterhin nach belieben manupulieren). Zum Glück
    ; haben wir ptrIMG sowie die Anzahl Pixel bereits in esi und ecx

    [/autoit] [autoit][/autoit] [autoit]

    _('_Label_01:') ; Sprungstelle mit kreativem Namen
    _('add ebx, 512') ; Weil es so schön war :)
    _('or ebx, 0xFF200080') ; bischl
    _('mov [esi], ebx') ; Die Farbe in die Bitmap schreiben

    [/autoit] [autoit][/autoit] [autoit]

    _('add esi, 4') ; 1PX = 4Byte weiterrücken
    _('dec ecx') ; Verbleibende Pixel = einer weniger als vorher (dec = decrement -> ecx -=1)
    _('ja _Label_01') ; Solange ecx > 0 springen wir zur Sprungstelle (ja = jump above)
    _('')
    _('')
    _('')
    _('ret') ; Braucht keine Sau :O

    [/autoit] [autoit][/autoit] [autoit]

    EndFunc ;==>_MyASM
    ; ===============================================================================================================================
    ; Hauptschleife inklusive Datenfreigabe
    ; ===============================================================================================================================
    Func _Main()
    While Not $bExit
    _ASM_Call($iMyASM, $pData); ASM code ausführen
    If TimerDiff($iFPS_Timer) > 1000 Then
    $iFPS_Timer = TimerInit()
    $nFPS = $iFPS
    $iFPS = 0
    WinSetTitle($hGUI, '', $sTitel & ' - (@' & $nFPS & 'FPS)')
    EndIf
    WEnd
    _Release()
    EndFunc ;==>_Main

    [/autoit] [autoit][/autoit] [autoit]

    Func Paint($hWnd, $Msg, $iIDTimer, $dwTime)
    _WinAPI_BitBlt($hDC, 0, 0, $iW, $iH, $hDC_IMG, 0, 0, 0xCC0020);blittet in die GUI
    $iFPS += 1;fps + 1 ;D
    _Timer_SetTimer($hGUI, $i_timer1delay, "Paint", $timer);timer widerverwenden
    EndFunc ;==>Paint
    ; ===============================================================================================================================
    ; Die eigentliche Funktion um alle benutzten Ressourcen freizugeben.
    ; ===============================================================================================================================
    Func _Release()
    _Timer_KillAllTimers($hGUI);timer killen
    _WinAPI_ReleaseDC($hGUI, $hDC)
    $_Memory = 0
    _ASM_DeleteBitmap($Image)
    _ASM_Shutdown()
    DllClose($ntDLL)
    EndFunc ;==>_Release

    [/autoit] [autoit][/autoit] [autoit]

    ; ===============================================================================================================================
    ; Da man mittem im ASM nicht unbedingt aus dem Skript gehen sollte,
    ; gibt es eine Variable die ein beenden der Hauptschleife verursacht.
    ; Der laufende ASM Code wird also nicht unterbrochen.
    ; ===============================================================================================================================
    Func _Exit()
    $bExit = True
    EndFunc ;==>_Exit

    [/autoit]


    €: unpassender ASM code für das Beispiel da es jetzt natürlich zu oft läuft

    2 Mal editiert, zuletzt von bollen (8. Mai 2014 um 17:04)

  • Ohje, das ist aus 2 Gründen ziemlich gewagt...
    1. Der ASM Code wird nicht mehr synchron zu den Frames gerendert. Vorher: Jeder Frame = 1 Durchlauf
    2. Zum Glück ist die Aufenthaltswahrscheinlichkeit im ASM zu sein recht gering. Wenn der Timer dazwischenfunkt kann es Abstürze geben.

    Wenn du den Code schneller haben willst, dann stell einfach die FPS in Zeile 13 auf 9999, dann arbeitet er so schnell er kann.

    Edit: Das klang jetzt viel negativer als gewollt :S, ich meine es nicht böse ;)

  • Hi,
    ich konnte leider das Script nicht starten.

    Code
    "C:\Program Files (x86)\AutoIt3\Include_Eigene\FASM.au3" (127) : ==> Subscript used on non-accessible variable.:
    Func FasmGetLastError()
    Func FasmGetLastError^ ERROR

    Vermutlich benutze ich nicht die aktuelle FASM.AU3 (warum wohl^^). Daher würde ich die von dir verwendete FASM.AU3 mit in die Zip packen.

    Was mir sofort aufgefallen ist, wieso benutzt du nicht AutoIt-Variablen für die Pointer usw. innerhalb des ASM-Codes zur Vereinfachung?

  • Interessant, die von mir benutzte FASM liegt doch in der zip mit drinnen ?
    Das einzige was ich bemerkt habe sind die <FASM.au3> statt "FASM.au3". Wenn man das ändert wird die Version im Ordner bevorzugt eingesetzt (glaube ich jedenfalls).

    Zu den Variablen: Da bin ich mir immernoch nicht soo sicher, funktionieren müsste es natürlich, sofern der ASM erst zusammenbebaut wird wenn die Structs schon fertig sind.

    bollen: Wenn der Code länger ist stürzt es ab ? Bitte um Beispiel...

    lg
    M

  • omfg....klappt natürlich einwandfrei!
    Mir fällt nur nicht ein, was man damit machen könnte...
    Es gibt ja schon Ansätze, bestehende Bilder zu laden und zu speichern, also Bild"be"arbeitung sollte ja kein Ding sein.
    Vielleicht ist es ja an der Zeit, einen "Compiler" zu basteln, der einfachen AutoIt-Code direkt in ASM übersetzt.
    Also eine Funktion, welche nativ in AutoIt eine Struct (Speicherbereich) beschreibt, so dass diese Funktion auch ohne Compiler lauffähig ist. Und diese Funktion dann durch den Compiler gejagt, würde selbst, wenn nur rudimentär und simpel nach ASM übersetzt, eine extreme Geschwindigkeitssteigerung bringen. Den Compiler schreibt man natürlich in AutoIt! 8o Wäre mal was für ein Community-Projekt...

    [autoit]

    ;Wo diese komplexe Funktion ihren Ursprung hat bleibt ein Mysterium...

    [/autoit]

    :rock: