Assembler Hilfe-Thread

  • Ich öffne FASMW, kopiere den Text

    in den Editor, dann drücke ich CTRL+F9 fürs Compile, gebe ggf Dateinamen "TestEXE.exe" an, drücke den OK-Button des fensters mit den (nicht vorhandenen ^^) Fehlermeldungen weg.
    Dann öffne ich eine CMD im Pfad der erstellten exe, gebe am Prompt testexe ein, enter und bekomme als Ausgabe die 2 Zeilen.

  • Wie muss denn die Erste Zeile aussehen, damit ich in 32Bit bzw 64Bit compilieren kann ?

    hab mal in die erste Zeile "use32" geschrieben. Der Compiler hat auch iwas gemacht, aber es kann von meinem System nicht ausgeführt werden...
    Immer wieder die Meldung, dass ein 16-Bit Programm vorliegt... (hab ein x64 System...)

    Spoiler anzeigen

    mfg
    Mars(i)

  • @Marsi, es würde helfen wenn du genau beschreiben würdest, was du eigentlich machen willst, mit welchem Programm(en) du arbeitest und wie genau die Fehlermeldungen lauten.
    Natürlich kannst du auf einem 64 Bitsystem auch 32Bitsoftware laufen lassen, allerdings funktionieren DOS-Programme (wie die Beispiele mit dem INT21h) nur in einer DOS-Box!

    @Shadow
    Befehlsreferenz und Interrupts für 8088/8086

  • Du hast grade gesagt, warum es nicht klappt...

    Ich kenne mich mit Assembler nicht aus. Deshalb wusste ich nicht, dass die Int21 Funktion nicht unter Windows läuft (außer in der Dos Box)
    Was man auf welchen Systemen Laufen lassen kann weiß ich schon. Aber seid Win7 x64 gibts halt leider keine 16Bit unterstützung mehr...

    Ich möchte mich mit Assembler etwas vertraut machen, um so Rechenintensive Sachen bei Zukünftigen Programmen Auszulagern, oder evtl (wenn ich mal ganz besonderst gut drauf bin^^) komplette kleinere Programme mit Assembler zu basteln.

    Mich Reizt hierbei natürlich die Enorme Geschwindigkeit die die Programme mit sich bringen, weil da natürlich keine andere Programmiersprache mithalten kann. Autoit ist ja teils über Tausendfach langsamer und C#/C++ liegt auch in fast allen Fällen um mehr als das 10-100 Fache hinten.

    Mein System ist ein Win7 x64 PC, und alternativ habe ich noch einen WinXP x86 PC (der sollte allerdings nur als Große Datenverwaltung sein. Win7 ist mir nicht so ganz sympatisch wenn es um Datenverwaltung geht.)

    Ich habe mir die fasmw16916.zip runtergeladen. (die Windoof Version) und damit habe ich Das Testprogramm mit den 2 Zeilen Text als Ausgabe geladen und versucht es zu starten.

    Wenn ich es erstmal hinbekomme überhaupt irgendwas mit Assembler (32Bit) zum laufen zu bringen lässt mir das wahrscheinlich in der Freizeit kaum noch ruhe daran irgendwas zu basteln^^
    Aber so wies aussieht klappts noch nicht. (Ich bräuchte also mal en Tipp, wie ich ein Windows fähiges (in der normalen cmd ausführbar) assemblerprogramm schreiben kann was mir z.B. eine Zeile Text ausgibt.
    Um dafür selbst nachforschungen anzustellen bin ich jetzt zu müde. --> ab ins Bett :P

    mfg
    Mars(i)

  • @Marsi
    Im Examples-Order des FASM-Paketes sind einige Beispiele, mE. auch für den 64-Bit-Mode, ansonsten bei FASM auf der Homepage.
    Sowohl 32- als auch 64-Bitprogramme greifen über die Include-Dateien auf fast die gesamte Windows-API zu. Am Präfix der includierten Datei erkennt man den Anwendungsbereich.
    Weil, wie gesagt, die gesamte Windows-API zur Verfügung steht, muss man keine grossen Klimmzüge machen, um z.B. Fenster zu erzeugen. Schau dir einfach mal die Beispiele an, für Windows lassen sich Programme wesentlich einfacher als für DOS erstellen.

    Zitat

    Autoit ist ja teils über Tausendfach langsamer und C#/C++ liegt auch in fast allen Fällen um mehr als das 10-100 Fache hinten.

    Wobei man fairerweise sagen muss, das die gängigen C++Compiler schon sehr nah am Optimum arbeiten! Aber es gilt wie überall auf der Welt, man kann aus Sch*** kein Gold machen! Das heisst im Klartext, wer im Vorfeld weiss, was der Compiler aus dem Code macht und entsprechendes Know-How hat, der kann auch mit C++ extrem schnellen Maschinencode erzeugen. Wer da Interesse zeigt, dem kann ich nur die sehr guten AMD-Dokumente, u.a. dieses empfehlen.

    Das gilt übrigens und gerade auch für den Assembler! Ich bin sicher, dass die allermeisten C++Compiler aus gutem bis mittelmäßigem C++Code schnelleren Maschinencode fabrizieren als mittelmässige Assemblerprogrammierer!

  • Ich versuch grad die Aviwriter UDF als Grundlage für Assembleraufnahme zu benutzen :D

    Also meine Fragen:

    Wie erstellt man Structs?
    Wie kriegt man jede Millisekunde heraus?
    Wird ConsoleRead mit mit einer DOS-Funktion gemacht?
    -invoke StdIn,buffer,length <- StdIn gibt es ebenfalls nicht

    9 Mal editiert, zuletzt von TheShadowAE (11. September 2010 um 13:04)

  • Hi,
    habe mal etwas probiert, bitte um Rückmeldung!
    Was haltet ihr von AssembleIt?
    Einfach mal die Beispiele anschauen und laufen lassen, ist das ausbaufähig, hat noch jemand weitere Ideen/Vorschläge?

  • Hallo ihr lieben,

    Ich habe folgendes Problem:
    Ich möchte eine ARGB Farbe in ihre Einzelteile zerlegen. Ich hatte mit Andy bereits gesprochen, und er erklärte es folgendermaßen:

    Das eax ist ja 32 Bit groß. Die unteren 16 Bit sind das ax (eax = extended ax), welches wiederum in Low- und Highbit (al & ah) aufgeteilt werden kann. Das eax bestehlt also aus 16 oberen Bits, die man nicht direkt ansprechen kann, und weitere 16 Bit (das ax).

    Mal angenommen, man hat eine ARGB Farbe im EAX und möchte den R Farbanteil. Beispielsweise 0xFFABCDEF (R = AB). Muss man also erst das den Inhalt des EAX um 16 Bit nach rechts schieben, damit Transparenz und R Farbanteil im "ansprechbaren" Bereich - dem ax - liegen.

    Danach muss noch das Lowbyte (al) in ein anderes Register geschoben werden, zum Beispiel ins EDX.
    Und genau das klappt nicht.

    Ich habe schon sämtliche Variationen von mov byte[edx], al ; mov edx, byte[al], mov edx, [al] ; mov edx, al probiert aber komme auf keinen Grünen Zweig. Kann mir jemand helfen?

    Spoiler anzeigen
    [autoit]

    #include <FASM.au3>
    #include <Array.au3>

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

    $hFasm = FasmInit()
    FasmReset($hFasm)
    FasmAdd($hFasm, "use32")
    FasmAdd($hFasm, "mov eax, [esp+4]") ; AARRGGBB Farbe ins EAX
    FasmAdd($hFasm, "shr eax, 16") ; 16 Bit nach rechts (FFABCDEF -> 0000FFAB)

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

    ; Jetzt will ich das Lowbit vom EAX (Was "AB", also den R-Farbanteil enthält) ins edx schieben
    FasmAdd($hFasm, "mov edx, al")
    ; edx sollte jetzt AB enthalten

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

    FasmAdd($hFasm, "mov eax, edx")
    FasmAdd($hFasm, "ret")
    _makebytecode()

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

    Func _makebytecode()
    $bBin = FasmGetBinary($hFasm)
    If @extended Then
    Local $Error = FasmGetLastError()
    MsgBox(0, "FASM-ERROR", "Error Code:" & $Error[0] & @CRLF & "Error Message:" & $Error[1] & @CRLF & "Error Line:" & $Error[2] & @CRLF)
    EndIf
    $bytecode = String($bBin)
    ConsoleWrite($bytecode & @CRLF)
    $tCodebuffer = DllStructCreate("byte[" & StringLen($bytecode) / 2 - 1 & "]") ;Speicher für den Bytecode reservieren
    DllStructSetData($tCodebuffer, 1, $bytecode) ;Bytecode in den Speicher schreiben
    $Ret = DllCall("user32.dll", "int", "CallWindowProcW", "ptr", DllStructGetPtr($tCodebuffer), "int", 0xFFABCDEF, "int", 0, "int", 0, "int", 0);
    If $Ret[0] = 0 Or @error Then Exit -1
    _ArrayDisplay($Ret)
    ;~ MsgBox(0, "", Hex($Ret[0],2))
    MsgBox(0, "", $Ret[0] & @LF & Hex($Ret[0]))
    Exit
    EndFunc ;==>_makebytecode

    [/autoit]

    Danke im Vorraus für die Hilfe.

  • Hi,
    zunächst der logische Teil:
    EAX ist, wie du schon weisst, 4 Byte gross.
    Gesamtgröße Register = 32 Bit = 4 Byte
    Die oberen 16 Bit haben keine gesonderte Bezeichnung
    Die unteren 16 Bit sind das AX-Register!
    Dieses unterteilt sich weiter in das AH (H für High)-Register( oberen 8 Bit) und das AL(L für Low)-Register(untere 8 Bit)
    Genauso verhält es sich mit EBX,ECX,EDX dort kann man die untersten Bytes auch mit ihrem Namen CX, DL oder BH ansprechen.

    Wenn du nun den Farbcode AARRGGBB in Einzelteile aufdröseln willst, dann schreib doch erst mal AutoItcode dafür (im Hinterkopf behalten, dass dieser Code so einfach wie möglich sein soll)

    Spoiler anzeigen
    [autoit]

    $EAX=0x01020304 ;AARRGGBB
    msgbox(0,"EAX=",hex($EAX))

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

    ;**************in Assembler nicht nötig ,da ja AX, AL und AH alles Teile von EAX sind
    ;untere 16 Bit sind AX
    $AX=dec(hex($EAX,4)) ;unterste 2 Byte
    msgbox(0,"AX=",hex($AX,4))

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

    ;AX aufteilen in AL und AH
    $AH=dec(stringleft(hex($AX,4),2)) ;
    msgbox(0,"AH=",hex($AH,2))

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

    $AL=dec(stringright(hex($AX,4),2))
    msgbox(0,"AL=",hex($AL,2))
    ;bis hierhin muss man im Assembler nichts machen, da ja AX, AL und AH alles Teile von EAX sind
    ;*********************************************************************************

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

    ;soweit sogut, in AH steht nun der Wert von GG, und in AL der Wert von BB
    ;wenn du jetzt ein Shift machst, werden diese Werte natürlich überschrieben!
    ;also erst EAX kopieren

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

    $EBX=$EAX

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

    ;und nun die 16 bit nach rechts shiften, um AARR nach BH und BL zu schieben
    $EBX=bitshift($EBX,16)

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

    ;in BH steht nun der Wert von AA und in BL der Wert von AL

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

    ;**************in Assembler nicht nötig ,da ja BX, BL und BH alles Teile von EBX sind
    ;untere 16 Bit sind BX
    $BX=dec(hex($EBX,4)) ;unterste 2 Byte
    msgbox(0,"nach shift ist BX=",hex($BX,4))

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

    ;BX aufteilen in BL und BH
    $BH=dec(stringleft(hex($BX,4),2)) ;
    msgbox(0,"BH=",hex($BH,2))

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

    $BL=dec(stringright(hex($BX,4),2))
    msgbox(0,"BL=",hex($BL,2))
    ;bis hierhin muss man im Assembler nichts machen, da ja BX, BL und BH alles Teile von EBX sind
    ;*********************************************************************************

    [/autoit]


    Im Prinzip sind das nur 3 Zeilen

    Code
    mov eax,0x01020304     ;AH=03 und AL=04
    mov ebx,eax
    shr ebx,16    ;BH=01 und BL=02

    um jetzt teile der Register per MOV zu verschieben, solltest du die größen der Einzelteile beachten!
    ECX=32 Bit
    BH=8 Bit
    8<>32 , daher Fehler! Man kann kein BYTE in ein DWORD "moven"
    aber MOV CH,BH geht!
    Dabei beachten, dass der Rest des ECX-Registers seine Werte beibehält! Es werden nur die Bits 15-8 ersetzt!
    Wenn du die 32Bit der "großen" Register nicht brauchst, kannst du auch nur mit den "kleinen", also AX, BH oder CL arbeiten.


    Aber aufpassen bei der Rückgabe an Autoit, denn da wird das komplette EAX-Register zurückgegeben! Wenn du nur AL änderst, musst du nach der Rückgabe an AutoIt das AL aus dem AX herauslösen, (oder im Assembler vorher EAX=0 setzen und dann erst das AL-Register schreiben)
    Und ich würde dir empfehlen, den AssembleIt zu verwenden, der fängt nämlich solche Fehler (unterschiedliche Registergrößen) schon ab^^

    [autoit]

    #include <AssembleIt.au3>

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

    func _test()
    _("use32") ;sollte immer eingesetzt werden!
    _("mov ecx,dword[esp+4]") ;1.Parameter in Register einlesen ;in CH=03 und in CL=04
    _("mov eax,ecx") ;kopieren, testweise mal ecx in cx ändern, der AssembleIt fängt den fehler ab
    _("shr eax,16") ;AH=01 AL=02
    _("ret 4")
    _("")
    endfunc

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

    $ret=_AssembleIt("ptr","_test","int",0x01020304) ;ptr als Rückgabe, um die hexzahlen schön zu sehen
    MsgBox(262144,'Ergebnis _test() ', $ret)

    [/autoit]
  • So schon wieder Fragen diesmal mit Code (wenn ich langsam nerve mit so vielen Fragen kannst mir sagen, dann frag ich woanders, mine letzten Fragen sind ja auch noch unbeantwortet:D)

    Ich wollte gerade eax nach invoke GetDC,0 in eine HDC-Speicherstelle schreiben, aber es wird ein Fehler (Invalid Operand.) ausgegeben bei folgendem:
    mov sourcedc,eax
    ...
    sourcedc HDC ?
    In den Programmcodes im Internet ist es doch überall so und es scheint zu klappen


    EDIT:
    Hab den Fehler HDC ist nicht im Include, aber wie kriege ich es dann rein?

    Einmal editiert, zuletzt von TheShadowAE (11. September 2010 um 11:53)

  • Zitat

    wenn ich langsam nerve mit so vielen Fragen kannst mir sagen, dann frag ich woanders, meine letzten Fragen sind ja auch noch unbeantwortet

    ja, das lag einfach daran, daß ich sie überlesen hatte...

    Zitat

    Ich wollte gerade eax nach invoke GetDC,0 in eine HDC-Speicherstelle schreiben

    HandleDeviceContext ist eine "Gerätebeschreibung", wieso willst du dort reinschreiben?

    Zitat

    In den Programmcodes im Internet ist es doch überall so und es scheint zu klappen

    zeig doch mal ein Stück von diesem funktionierenden Code und dann deinen Brocken, der nicht klappt...
    Ich kann zwar ahnen was du vorhast, aber sicher bin ich mir nicht^^

    Aus einem der FASM-Beispiele (opengl.asm)

    Zitat

    .wmcreate:
    invoke GetDC,[hwnd] ;ermittelt den DC des Fensters (hwnd ist die Adresse, an der das Fensterhandle steht! )
    mov [hdc],eax ; schreibt diesen in eine Speicherstelle namens hdc, also gewissermassen ein variablenname

    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 (12. September 2010 um 09:13)

  • Mein Code bis jetzt ist dieser, Parameter sind auch noch falsch ich schreibe 4 bytes stand eine Zahl ins CmdLine Array

    Spoiler anzeigen
    [autoit]

    include 'win32ax.inc' ; you can simply switch between win32ax, win32wx, win64ax and win64wx here

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

    .code
    start:
    ;Parameter
    invoke GetCommandLine;CommandLine
    mov esi,eax;String zum splitten zum Source,esi
    mov edi,tmpstr;hier rein kommt Pfad+Dateiname dieser Exe

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

    mov ecx,0;Parameterzähler
    mov edx,0;'"' Zähler(Strings dürfen nicht auseinander gerupft werden)
    chschleife:;CmdLineSchleife
    cmp byte[esi]," ";Prüfen ob nächster Parameter
    je do1;Parameter
    jmp do2;noch kein Parameter
    weiter:;nach Funktionen
    cmp ecx,4;Prüfen ob alle Parameter ausgelsen wurden
    jne chschleife;Wenn nicht Schleife wiederholen
    jmp nachcmdline;sonst hinter die ganzen Funktionen springen und starten

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

    do2: ;Pfad+Datei auslesen
    movsb;ein Byte verschieben
    jmp weiter;zurück

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

    do1: ;Parameter auslesen
    cmp ecx,0;Prüfen ob es der erste Parameter ist
    je makepar1;Wenn ja String schreiben
    jmp makepar2;Sonst DWORD-Parameter schreiben
    weiter2:;Zurück von den Funktionen
    inc ecx;Parameterzähler erhöhen
    jmp weiter;zurück

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

    makepar1: ;Parameter 1(datei), string
    mov edi,datei;Pointer zu String
    makepar11:;innere Schleife zum String schreiben
    movsb;Ein Byte verschieben
    cmp byte[esi]," ";Prüfen ob es das Ende des Parameters war
    jne makepar11;Schleife wiederholen
    jmp weiter2;zurück

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

    makepar2: ;Parameter 2-4, DWORD
    mov edi,[cmdline+ecx*4];Adresse des Arrays+Index ########### Pointer/Wert?
    movsd;ganzen DWORD schreiben
    jmp weiter2;zurück

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

    nachcmdline:;Nach dem Parameterzeugs:
    ;Aus dem Array die CommandLineWerte endlich auslesen (Variable datei wurde schon beschrieben)
    mov eax,[cmdline+4]
    mov [screenw],eax
    mov eax,[cmdline+8]
    mov [screenh],eax
    mov eax,[cmdline+12]
    mov [vidfps],eax

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

    ;Vorbereiten
    invoke GetDC,0;Desktop-DC
    mov [sourcedc],eax;in sourcedc schreiben und speichern
    invoke CreateCompatibleDC,[sourcedc];für BitBlt brauch man eine Compatible DC
    mov [destdc],eax;auch speichern
    invoke CreateCompatibleBitmap,[sourcedc],[screenw],[screenh];CompatibleBitmap, hier kommen die Screenshots rein
    mov [hbmp],eax;auch speichern
    invoke SelectObject,[destdc],[hbmp];Compatible-DC mit -Bitmap verknüpfen
    mov [flag],SRCCOPY;Flag für BitBlt erstellen
    or [flag],CAPTUREBLT ;SRCCOPY und CAPTUREBLT
    mov eax,[sltime];1000 nach eax (1s)
    mov ebx,[vidfps];FPS nach ebx
    mov edx,0;Für div
    div ebx;um auszurechnen wie lange in der Schleife gewartet werden muss

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

    ;Schleife
    schleife:
    ;Prüfen ob das Bild gleich das vorherige ist?
    ;invoke BitBlt,destdc,0,0,[screenw],[screenh],sourcedc,0,0,SRCINVERT
    ;
    ;Bild kopieren
    invoke BitBlt,[destdc],0,0,[screenw],[screenh],[sourcedc],0,0,[flag]
    cmp eax,0
    je fehl
    nfnow:
    ;und speichern (Video)

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

    ;Warten, um FPS zu regulieren
    ;
    invoke Sleep,[sltime]
    ;Schleife wiederholen, wenn nicht Ende in StdIn steht
    invoke StdIn,buffer,1
    cmp [buffer],"E" ;Exit
    je ende
    jmp schleife

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

    fehl:;bei einem Fehler
    invoke GetLastError;letzten Error -> eax
    mov [fehler],eax ;BitBlt meldet Fehler
    jmp nfnow;zurück

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

    ende: ;bereinigen und beenden
    invoke ReleaseDC,0,[sourcedc]
    invoke DeleteDC,[destdc]
    invoke DeleteObject,[hbmp]
    invoke ExitProcess,[fehler]
    .end start

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

    section '.data' data readable writeable ;die ganzen Variablen
    datei db ?
    screenw dd 0
    screenh dd 0
    vidfps dd 0
    fehler dd 0
    flag dd 0
    buffer db ?
    sltime dd 1000
    tmpstr db ?
    cmdline dd 1 dup(3)

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

    CAPTUREBLT dd H40000000h

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

    HDC typedef ptr
    HBITMAP typedef ptr

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

    sourcedc HDC ?
    destdc HDC ?
    hbmp HBITMAP ?

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

    icon main_icon,icon_data,'vid.ico'

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

    ;section '.idata' import data readable writeable ;Noch mehr Dll-Funktionen laden

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

    ; library avi,'Avifil32.dll',\
    ; import avi,\
    ;AVIFileInit,'AVIFileInit',\
    ;AVIFileExit,'AVIFileExit',\
    ;AVIFileOpen,'AVIFileOpenW',\
    ;AVIFileCreateStream,'AVIFileCreateStream',\
    ;AVIStreamSetFormat,'AVIStreamSetFormat',\
    ;AVIStreamWrite,'AVIStreamWrite'

    [/autoit]
  • Wenn du MASM-Funktionen verwendest, solltest du auch die MASM32Lib einbinden, z.B für StdIn
    Ansonsten sieht das doch gut aus :thumbup:
    Zum Thema StdIn schau doch mal hier

    ansonsten sind ptr, hdc usw alles dwords, also ein
    variable dd ?
    statt
    variable ptr ?
    funktioniert auch.

    /edit/ wobei das nichts mehr mit AutoIt zu tun hat, und vom Level her schon weit über dem liegt, was hier sonst diskutiert wird :thumbup:

  • schau mal bei den FASM-jungs im Forum, sicher gibts ein Macro für StdIn, ansonsten hilft vielleicht GetStdHandle, hab den Code mal soweit umgebaut, dass FASMW compiliert, die Zeile mit der Icon-Datei hab ich rausgelassen...

    Spoiler anzeigen