Bitte wie komme ich an die Call-Adresse des mit ASM erzeugten Maschinencodes?
ProcAddr liefert derzeit Fehlermeldung ...
Assemblercodes für das neue X4
-
-
-
Bitte wie komme ich an die Call-Adresse des mit ASM erzeugten Maschinencodes?
ProcAddr liefert derzeit Fehlermeldung ...Zur Zeit überhaupt nicht. ProcAddr funktioniert nur für XProfan-Prozeduren und weist diesen eine feste Adresse zu. Mit ASM-ENDASM werden die Funktionen genau so in die interne Liste externer Funktionen eingetragen, wie mit ImportFunc oder ImportDLL. Da die Funktionen ja über ihren Namen aufgerufen werden (auch in ASM-Funktionen mit FCALL), benötigt man (normalerweise) ihre Adressen nicht.
Ich habe aber geplant, bei ASM-ENDASM zwei Systemvariablen zu füllen, die Startadresse und Länge des erzeugten Codes bereitstellen, auch wenn ich nicht so recht weiß, was man damit Sinnvolles anfangen kann.
Gruß
Roland -
ad 39: Ich habe beobachtet, daß "Vorwärtsverkettete Indexlisten" im Grundzustand (also vor dem mitsortiert werden nach der neuen Ordnung) immer auf das NÄCHSTE Element der aktuellen Ordnung weisen sollten, nicht auf sich selbst. Auch dazu wäre es super, den Basiswert der Zählung bei 1 anzusetzen, da ja das nächste anzuspringende Element der Indexordnung dann das mit dem Index 1 ist...(Ich weiß aber nicht, ob Heinz das überhaupt im Auge hatte. Es wäre jedenfalls ein weiteres Argument für Element[0]="1" ).
ad 37: Die Call-Addy könnte sinnvoll sein, wenn mehrere ASM-ENDASM-Module zusammenarbeiten sollen. Auch Callback-Techniken benötigen das ja ... (wobei ich keine Ahnung von Callback habe). An selbstmodifizierenden Code denke ich natürlich nicht, - das wäre ja gegen alles, was den Informatik-Göttern heilig ist ...
GrussP.S: Mir gefällt sehr, daß man tatsächlich "in Line" codieren kann. Das klappt sonst in keinem der bekannten Assembler:
-
Mit Erlaubnis von RGH darf ich ein vorläufiges Merkblatt zum aktuellen Ausbaustand des RGH-XProfan-Inlineassemblers (RGH-"XASM") veröffentlichen. Na dann "Gut Netzausfall und Plattencrash!"
Gruss -
Besser langsam, aber dafür sorgfältig angehen...
Die untenstehend von RGH bekanntgegebenben Neuerungen wurden in das "vorläufige Merkblatt_XASM.pdf" eingearbeitet. Die alte Download-Addy dazu ist nicht mehr gültig.
Dafür gibt es ergänzend ein Merkblatt zu Bedingungen und Schleifen in Assembler.
Und hier ein Testcode, der sich selbst byteweise ausgibt und dann auch im Clipboard zu finden ist.
GrussCode
Alles anzeigenWindowTitle "Selbsterforschung" WindowStyle 24:Window 0,0-%maxx,%maxy:showmax set("AsmMode",2):font 2 ASM "Test2",1:data: mov ebx,par1:mov ecx,data:add ecx,ebx:xor eax,eax:mov al,[ecx]:exyt: ENDASM whileloop -3,-1 print right$("0"+hex$(test2(&Loop)),2); '55 89 E5 = Pgm-Start endwhile print whileloop 14,19 print right$("0"+hex$(test2(&Loop)),2); '89 EC 5D C2 (= Pgm-Ende)+ 04 00 (= 0004/4 = 1 Parameter) endwhile print clearclip whileloop -3,19 print right$("0"+hex$(test2(&Loop)),2); 'Programm putclip right$("0"+hex$(test2(&Loop)),2) endwhile print:print "\n ASM-PGM Hex-Bytecode im Clipboard" Waitinput 15000
P.S.: Vielleicht wäre eine separate Rubrik für solche und andere User-Codestücke sinnvoll.
Dann könnte dieser Thread allein für Neuerungen reserviert bleiben... -
Habe hier auch was, das 2 Strings zusammensetzen soll.
Das hatte ich aus dem Netz auch so übernommen.
Irgendwie mache ich da was falsch, was das Schreiben
an bestimmte Adressen angeht.
Hier kommt eine Fehlermeldung :Code
Alles anzeigenDeclare String s1, s2 s1 = "Hallo \z" s2 = "Welt\z" Cls ASM "api_strcat", 2 MOV ESI, Par1 MOV EDI, Par2 CALL strcat strcat: XOR ECX,ECX MOV EBP,ESI lp1: LODSB OR AL,AL JZ lp1d INC ECX JMP lp1 lp1d: MOV ESI,EDI MOV EBX,ECX lp2: LODSB OR AL,AL JZ lp2d MOV [EBP+EBX],AL INC EBX JMP lp2 XOR AL,AL MOV [EBP+EBX],AL lp2d: ENDASM Print api_strcat(s1, s2) WaitKey End
Da habe ich den Bogen noch nicht so raus.
PS : Zuerst mußte ich alle TABs entfernen. Die mag der Inline Assembler irgendwie nicht,
bzw. gibt mir schon in der ersten Zeile einen Fehler, wo er den Befehl nicht kennt. -
Erst mal kann das Ergebnis so nicht funktionieren. Die Assemblerroutine gibt einen Wert in EAX zurück, das ist ein 32-Bit-Wert, also eine Zahl, kein String. Prinzipiell könnte man zwar mit ausreichenden Kenntnissen Speicher reservieren und in EAX das Handle zurückgeben... das stellt Bedingungen an den nachfolgenden Programmteil. Also lieber nicht.
Sieh Assemblerroutinen so an, wie API-Aufrufe. Es kommt immer eine Zahl zurück. Strings kommen in einem Speicherbereich, den Du ausreichend dimensioniert als Parameter übergibst. Zudem kommt mir das Ganze irgendwie aus dem Zusammenhang gerissen vor, da taucht ein CALL in die eigene Routine auf, und es gibt kein passendes RET. CALL und RET gehören zusammen wie PUSH und POP, da immer Werte auf den Stack gelegt und wieder abgeholt werden.
Und spiele nicht mit dem Base-Pointer, den stellt Roland schon so ein, wie er gebraucht wird.Gruß Volkmar
-
Danke für die Erläuterungen.
Ich denke, ich schaue mir künftig lieber Rolands
Beispielcodes statt fremde Codes an.Das bringt mir mehr.
Ich hoffe, daß hier in die Rubrik noch einige
Beispiele kommen. -
So, habe nun auch eine Version fertig. StrCat könnte man etwa so machen
Code
Alles anzeigenDeclare String s1, s2 s1 = "Hall0 \z" s2 = "Welt\z" s1 = s1 + Space$(Len(s2)) ' String verlängern, um Ziel aufzunehmen Cls ASM "api_strcat", 2 MOV ESI, Par1 MOV EDI, Par2 CLD // directionflag setzen, man weiß ja nie, wie es gerade steht :-) lp1: LODSB OR AL,AL JNZ lp1 lp1d: DEC ESI XCHG ESI, EDI // Quelle und Ziel tauschen, da wir jetzt in Par1 schreiben wollen lp2: LODSB STOSB OR AL,AL JZ lp2d JMP lp2 XOR AL,AL MOVSB lp2d: ENDASM Print api_strcat(s1, s2) Print Trim$(s1)+ "!!" WaitKey End
Und in fremde Codes gucken ist doch gar nicht so verkehrt. Nur aus dem Zusammenhang gerissen ist da Einiges anzupassen. Im Original hat das sicher funktioniert. Aber dort mußte auch niemand auf den "Funktionsrahmen" und die Bedingungen in Profan Rücksicht nehmen. Ist also immer mit etwas Anpassung zu verwenden. Mit etwas Übung wird das schon. Ich muß jetzt auch viel nachdenken, es ist etliche Jahre her, als ich den letzten Assemblercode zusammengklopft habe
Die Zahl, die bei Print ausgegeben wird, sagt garnichts aus, das ist der aktuelle EAX-Inhalt. Und das Trim$() bei der Ergebnisanzeige kann hier auch entfallen, da die Stringlänge vorher berechnet wurde.Gruß Volkmar
PS: Code nochmal geändert, da wurde ein zusätzliches Leerzeichen eingebaut, weil das Schlußzeichen (Byte 0) als gültige Zeichenposition mitgezählt wurde, nach der dann der zweite String eingefügt wird
-
Und Speicher belegen und freigeben könnte man in etwa so machen.
Allerdings bekomme ich es nicht mal zum kompilieren.
Trotzdem... TEST, TEST, TEST
Code
Alles anzeigen'$I Mem_ASM.INC '$H C:\XProfan\Include\Windows.ph '$IFNDEF MEM_ASM '$DEFINE MEM_ASM Declare Handle mwmem_hDLL mwmem_hDLL = UseDLL("KERNEL32.DLL") ImportFunc( mwmem_hDLL, "GetLastError", "sys_GetLastError" ) // GetLastError( ) ImportFunc( mwmem_hDLL, "GlobalAlloc", "sys_GlobalAlloc" ) // GlobalAlloc( flags, Size ) ImportFunc( mwmem_hDLL, "GlobalFree", "sys_GlobalFree" ) // GlobalFree( hMem ) ImportFunc( mwmem_hDLL, "GlobalHandle", "sys_GlobalHandle" ) // GlobalHandle( pMem ) ImportFunc( mwmem_hDLL, "GlobalLock", "sys_GlobalLock" ) // GlobalLock( hMem ) ImportFunc( mwmem_hDLL, "GlobalReAlloc", "sys_GlobalReAlloc" ) // GlobalReAlloc( hMem, Size, flags ) ImportFunc( mwmem_hDLL, "GlobalSize", "sys_GlobalSize" ) // GlobalSize( hMem ) ImportFunc( mwmem_hDLL, "GlobalUnlock", "sys_GlobalUnlock" ) // GlobalUnlock( hMem ) ImportFunc( mwmem_hDLL, "RtlFillMemory", "sys_FillMemory" ) // FillMemory( pDest, Len, Char ) ImportFunc( mwmem_hDLL, "RtlMoveMemory", "sys_MoveMemory" ) // MoveMemory( pDest, pSrc, Len ) 'Überlappung erlaubt ImportFunc( mwmem_hDLL, "RtlZeroMemory", "sys_ZeroMemory" ) // ZeroMemory( pDest, Len ) // EAX, ECX, EDX müssen selbst gerettet werden (da der Windows-Standard diese zu Freiwild erklärt) /* Inhalt: Mem_New - pMem = Mem_New( Size ) - Speicher belegen/reservieren Mem_Free - bool = Mem_Free( pMem ) - Speicher freigeben Mem_Size - Size = Mem_Size( pMem ) - Größe des belegten Speichers ermitteln Mem_Clear - Size = Mem_Clear( pMem ) - Speicher mit Nullen überschreiben Mem_Fill - bool = Mem_Fill( pMem, Anz, Fill ) - Der angegebene belegte Speicherbereich wird mit Füllzeichen aufgefüllt. Mem_Move - bool = Mem_Move( Ziel, Quelle, Anz ) - Speicherinhalte kopieren Mem_Resize - pMem = Mem_Resize( pMem, Size ) - Speichergröße ändern/redimensionieren */ ' ******************** ' Mem_New() - Speicher belegen/reservieren ' pMem& = Mem_New( Size& ) ' Speicher belegen ' Reserviert "Size" Bytes Speicherplatz und liefert den Speicherzeiger als Rückgabewert. ASM "Mem_New", 1 PUSH ECX MOV ECX, 66 // GMEM_MOVEABLE = 2 | GMEM_ZEROINIT = 64 | GMEM_PTR = 66 MOV EAX, PAR1 // Size CMP EAX, EAX // Null ? JZ @@New_Done PUSH EAX // Size PUSH ECX // Flags FCALL "sys_GlobalAlloc" CMP EAX, EAX // Null ? JZ @@New_Done PUSH EAX // hMem FCALL "sys_GlobalLock" @@New_Done: POP ECX ENDASM ' ******************** ' Mem_Free() - Speicher freigeben ' bool% = Mem_Free( pMem& ) ' Speicher freigeben ' Gibt den Speicherbereich auf den der Speicherzeiger zeigt wieder frei. ASM "Mem_Free", 1 PUSH ECX MOV EAX, PAR1 // pMem CMP EAX, EAX JZ @@Free_Done PUSH EAX // pMem FCALL "sys_GlobalHandle" CMP EAX, EAX JZ @@Free_Error MOV ECX, EAX PUSH ECX // hMem FCALL "sys_GlobalUnlock" CMP EAX, EAX JZ @@Free_Error PUSH ECX // hMem FCALL "sys_GlobalFree" CMP EAX, EAX JNZ @@Free_Done @@Free_Error: FCALL "sys_GetLastError" @@Free_Done: POP ECX ENDASM ' ******************** ' Mem_Size() - Größe des belegten Speichers ermitteln ' Size& = Mem_Size( pMem& ) ' Ermittelt die Speichergröße auf die der Speicherzeiger zeigt. ' Liefert die Größe des Bereiches zurück. ASM "Mem_Size", 1 PUSH ESI PUSH EDI XOR EDI,EDI MOV ESI, PAR1 // pMem CMP ESI, ESI JZ @@Size_Done PUSH ESI // pMem FCALL "sys_GlobalHandle" // --> hMem MOV ESI, EAX // hMem CMP EAX, EAX JZ @@Size_Error PUSH ESI // hMem FCALL "sys_GlobalSize" // size MOV EDI, EAX // size JNZ @@Size_Done @@Size_Error: FCALL "sys_GetLastError" @@Size_Done: MOV EAX,EDI POP EDI POP ESI ENDASM ' ******************** ' Mem_Clear() - Speicher mit Nullen überschreiben ' Size& = Mem_Clear( pMem& ) ' Speicher mit Nullen beschreiben ' Der angegebene (gesamte) Speicherbereich wird mit binären Nullen überschrieben. ' Für eine Teilfüllung ist Mem_Fill() zu nutzen. ' benötigt Mem_Size(), "ZeroMemory" ' Ergebnis: Größe des überschriebenen Bereiches ASM "Mem_Clear", 1 PUSH ESI XOR EAX, EAX MOV ESI, PAR1 // pMem CMP ESI, ESI JZ @@Clear_Done PUSH ESI // pMem FCALL "Mem_Size" CMP EAX, EAX // size JZ @@Clear_Done PUSH EAX // size PUSH ESI // pMem FCALL "sys_ZeroMemory" @@Clear_Done: POP ESI ENDASM ' ******************** ' Mem_Fill() - Der angegebene belegte Speicherbereich wird mit Füllzeichen aufgefüllt. ' bool% = Mem_Fill( pMem&, Anzahl&, FüllChars$ ) ' belegten Speicher füllen ' Ist Anzahl = 0 dann wird Anzahl auf Mem_Size(pMem) gesetzt. ' Der angegebene Speicherbereich wird mit Füllzeichen aufgefüllt. ' - ohne diese Angabe wird mit Chr$(0) aufgefüllt ' - bei einem Zeichen wird sys_FillMemory() genutzt ' - bei mehreren Zeichen wird sys_MoveMemory() genutzt ASM "Mem_Fill", 3 PUSH ESI PUSH EDI PUSH ECX PUSH EBX PUSH EDX MOV ESI, PAR3 // FillChars MOV ECX, PAR2 // Anzahl MOV EDI, PAR1 // pMem MOV EBX, [ESI - 4] // Len(FillChars) PUSH EDI // pMem FCALL "Mem_Size" CMP ECX, EAX // Anzahl <= Size ? JBE @@Fill_Anz_Inside MOV ECX, EAX // neue Anzahl @@Fill_Anz_Inside: CMP ECX, ECX // Anzahl = 0 ? JZ @@Fill_Done CMP EBX, 1 // FillChars = Null or 1 ? (nein, dann Hauptstück) JA @@Fill_more // 1 Zeichen XOR EDX, EDX // Vorgabe ist Chr$(0) CMP ESI, ESI // FillChars = NullString ? JZ @@Fill_Start MOV DL, [ESI] // Nein, dann FillChar holen @@Fill_Start: // Bereich mit einem Zeichen füllen PUSH EDX // char PUSH ECX // Anzahl PUSH EDI // pMem FCALL "sys_FillMemory" JMP @@Fill_Done // -------------------- @@Fill_more: CMP EBX, ECX // LEN( FillChars ) >= Anzahl ? JAE @@Fill_MoveOne // Da wir den Füllstring mehrmals schreiben wird die Länge benötigt PUSH EBX // Len(FillChars) // ---------- @@Fill_Loop: CMP ECX, EBX // Anzahl < Len(FillChars) JAE @@Fill_Hop // Nein, weiter (sonst den Rest) POP EBX // Len(FillChars) MOV EBX,ECX // überschreibe mit Rest PUSH EBX // Rest @@Fill_Hop: PUSH EBX // Len(FillChars) (oder Rest) PUSH ESI // FillChars PUSH EDI // pMem FCALL "sys_MoveMemory" POP EBX // Len(FillChars) PUSH EBX // Len(FillChars) ADD EDI, EBX // pMem + Len(FillChars) SUB ECX, EBX // Anzahl - Len(FillChars) CMP ECX, ECX // Anzahl > 0 JA @@Fill_Loop // -------------------- // gesicherten Wert wieder entfernen POP EBX // Len(FillChars) JMP @@Fill_Done @@Fill_MoveOne: // Es sind mehr Füllzeichen als der Bereich groß ist. // Das läßt sich in einem Rutsch erledigen. PUSH ECX // Anzahl PUSH ESI // FillChars PUSH EDI // pMem FCALL "sys_MoveMemory" @@Fill_Done: POP EDX POP EBX POP ECX POP EDI POP ESI ENDASM ' ******************** ' Mem_Move() - Speicherinhalte kopieren ' bool = Mem_Move( Ziel&, Quelle&, Anzahl& ) ' Der angegebene Quellbereich wird in den Zielbereich kopiert. ' (Überlappungen von Quelle und Ziel sind hier erlaubt) ASM "Mem_Move", 3 PUSH ESI PUSH EDI PUSH ECX // XOR EAX, EAX // 0 = es wurde nichts kopiert MOV ECX, PAR3 // Anzahl MOV ESI, PAR2 // pQuelle MOV EDI, PAR1 // pZiel // CMP ESI, ESI // Anzahl = 0 ? JZ @@Move_Done CMP ESI, ESI // Quelle = Null JZ @@Move_Done CMP EDI, EDI // Ziel = Null JZ @@Move_Done PUSH ECX // Anzahl PUSH ESI // pQuelle PUSH EDI // pZiel FCALL "sys_MoveMemory" @@Move_Ok: INC EAX // 1 = Kopieren wurde durchgeführt @@Move_Done: POP ECX POP EDI POP ESI ENDASM ' ******************** ' Mem_Resize() - Speichergröße ändern/redimensionieren ' pMem& = Mem_Resize( pMem&, Size& ) ' Speicher redimensionieren ' Ändert die Speichergröße auf die der Speicherzeiger zeigt. ' Die Größe des angegebenen Speicherbereiches wird auf die neue Größe geändert (wenn ungleich). ' Bei einer Vergrößerung des Bereiches bleiben alle Daten erhalten, ' bei einer Verkleinerung nur der vordere Teil. (!!!ohne Gewähr!!!) ' Liefert den neuen Speicherzeiger zurück. ASM "Mem_Resize", 2 PUSH ECX PUSH ESI PUSH EDI MOV EDI, PAR2 // Size MOV ESI, PAR1 // pMem CMP ESI, ESI // Nullzeiger ? Dann Mem_New(Size) JZ @@Resize_New PUSH ESI // pMem FCALL "sys_GlobalHandle" // --> hMem CMP ESI, ESI JZ @@Resize_Error MOV ESI, EAX // hMem PUSH ESI // hMem FCALL "sys_GlobalSize" // OldSize CMP EAX, EAX // Null JZ @@Resize_Error CMP EAX, EDI // OldSize = Size ? dann keine Größenänderung JZ @@Resize_Done PUSH ESI // hMem FCALL "sys_GlobalUnlock" MOV EAX, 2 // GMEM_MOVEABLE = $2 PUSH EAX // flags PUSH EDI // size PUSH ESI // hMem FCALL "sys_GlobalReAlloc" MOV ESI, EAX // hMem CMP EAX, EAX // Null JZ @@Resize_Error PUSH ESI // hMem FCALL "sys_GlobalLock" MOV ESI, EAX // pMem CMP EAX, EAX // Null JZ @@Resize_Error JMP @@Resize_Done @@Resize_New: PUSH EDI // Size FCALL "Mem_New" MOV ESI, EAX // pMem JMP @@Resize_Done @@Resize_Error: FCALL "sys_GetLastError" // ohne Parameter @@Resize_Done: MOV EAX, ESI POP EDI POP ESI POP ECX ENDASM '$ENDIF Cls Declare long test1 Print "Neu" test1 = Mem_New( 200 ) Print "Adresse: "; test1 Print " Größe: "; Mem_Size(test1) Mem_Free(test1) WaitInput End
-
Bei FCALL wird der Funktionsname OHNE Anführungszeichen angegeben!
Gruß
Roland -
... und davon abgesehen: Ab der nächsten Alphaversion wird es "FCALL name" nicht mehr geben, dafür funktionieren dann direkt "CALL @name" und "CALL label".
Gruß
Roland -
Dann funktioniert das Testbeispiel auch schon.
Zuerst suchte ich nach Mem_ASM.lst, aber alle enthaltenen Routinen erscheinen separat.
Es sind aber nur New,Free,Size gründlich geprüft.Code
Alles anzeigen'$I Mem_ASM.INC '$H C:\XProfan\Include\Windows.ph Set("AsmMode",1) ' nur zum testen '$IFNDEF MEM_ASM '$DEFINE MEM_ASM Declare Long mem_v_Error Declare Handle mem_v_hDLL mem_v_hDLL = UseDLL("KERNEL32.DLL") ImportFunc( mem_v_hDLL, "GetLastError", "" ) // GetLastError( ) ImportFunc( mem_v_hDLL, "GlobalAlloc", "" ) // GlobalAlloc( flags, Size ) ImportFunc( mem_v_hDLL, "GlobalFree", "" ) // GlobalFree( hMem ) ImportFunc( mem_v_hDLL, "GlobalHandle", "" ) // GlobalHandle( pMem ) ImportFunc( mem_v_hDLL, "GlobalLock", "" ) // GlobalLock( hMem ) ImportFunc( mem_v_hDLL, "GlobalReAlloc", "" ) // GlobalReAlloc( hMem, Size, flags ) ImportFunc( mem_v_hDLL, "GlobalSize", "" ) // GlobalSize( hMem ) ImportFunc( mem_v_hDLL, "GlobalUnlock", "" ) // GlobalUnlock( hMem ) ImportFunc( mem_v_hDLL, "RtlFillMemory", "" ) // FillMemory( pDest, Len, Char ) ImportFunc( mem_v_hDLL, "RtlMoveMemory", "" ) // MoveMemory( pDest, pSrc, Len ) 'Überlappung erlaubt ImportFunc( mem_v_hDLL, "RtlZeroMemory", "" ) // ZeroMemory( pDest, Len ) // EAX, ECX, EDX müssen selbst gerettet werden (da der Windows-Standard diese zu Freiwild erklärt) /* Inhalt: Mem_New - pMem = Mem_New( Size ) - Speicher belegen/reservieren Mem_Free - bool = Mem_Free( pMem ) - Speicher freigeben Mem_Size - Size = Mem_Size( pMem ) - Größe des belegten Speichers ermitteln Mem_Clear - Size = Mem_Clear( pMem ) - Speicher mit Nullen überschreiben Mem_Fill - bool = Mem_Fill( pMem, Anz, Fill ) - Der angegebene belegte Speicherbereich wird mit Füllzeichen aufgefüllt. Mem_Move - bool = Mem_Move( Ziel, Quelle, Anz ) - Speicherinhalte kopieren Mem_Resize - pMem = Mem_Resize( pMem, Size ) - Speichergröße ändern/redimensionieren */ ' ******************** ' Mem_New() - Speicher belegen/reservieren ' pMem& = Mem_New( Size& ) ' Speicher belegen ' Reserviert "Size" Bytes Speicherplatz und liefert den Speicherzeiger als Rückgabewert. ASM "Mem_New", 1 PUSH ECX MOV ECX, 66 // GMEM_MOVEABLE = 2 | GMEM_ZEROINIT = 64 | GMEM_PTR = 66 MOV EAX, PAR1 // Size CMP EAX, 0 // Null ? JZ @@New_Done PUSH EAX // Size PUSH ECX // Flags CALL @GlobalAlloc CMP EAX, 0 // Null ? JZ @@New_Done PUSH EAX // hMem CALL @GlobalLock @@New_Done: POP ECX ENDASM ' ******************** ' Mem_Free() - Speicher freigeben ' bool% = Mem_Free( pMem& ) ' Speicher freigeben ' Gibt den Speicherbereich auf den der Speicherzeiger zeigt wieder frei. ASM "Mem_Free", 1 PUSH ECX MOV EAX, PAR1 // pMem CMP EAX, 0 JZ @@Free_Done PUSH EAX // pMem CALL @GlobalHandle CMP EAX, 0 JZ @@Free_Error MOV ECX, EAX PUSH ECX // hMem CALL @GlobalUnlock CMP EAX, 0 JZ @@Free_Error PUSH ECX // hMem CALL @GlobalFree CMP EAX, 0 JNZ @@Free_Done @@Free_Error: CALL @GetLastError @@Free_Done: POP ECX ENDASM ' ******************** ' Mem_Size() - Größe des belegten Speichers ermitteln ' Size& = Mem_Size( pMem& ) ' Ermittelt die Speichergröße auf die der Speicherzeiger zeigt. ' Liefert die Größe des Bereiches zurück. ASM "Mem_Size", 1 PUSH ESI PUSH EDI XOR EDI,EDI MOV ESI, PAR1 // pMem CMP ESI, 0 JZ @@Size_Done PUSH ESI // pMem CALL @GlobalHandle // --> hMem MOV ESI, EAX // hMem CMP EAX, 0 JZ @@Size_Error PUSH ESI // hMem CALL @GlobalSize // size MOV EDI, EAX // size JNZ @@Size_Done @@Size_Error: CALL @GetLastError @@Size_Done: MOV EAX,EDI POP EDI POP ESI ENDASM ' ******************** ' Mem_Clear() - Speicher mit Nullen überschreiben ' Size& = Mem_Clear( pMem& ) ' Speicher mit Nullen beschreiben ' Der angegebene (gesamte) Speicherbereich wird mit binären Nullen überschrieben. ' Für eine Teilfüllung ist Mem_Fill() zu nutzen. ' benötigt Mem_Size(), "ZeroMemory" ' Ergebnis: Größe des überschriebenen Bereiches ASM "Mem_Clear", 1 PUSH ESI XOR EAX, EAX MOV ESI, PAR1 // pMem CMP ESI, 0 JZ @@Clear_Done PUSH ESI // pMem CALL @Mem_Size CMP EAX, 0 // size JZ @@Clear_Done PUSH EAX // size PUSH ESI // pMem CALL @RtlZeroMemory @@Clear_Done: POP ESI ENDASM ' ******************** ' Mem_Fill() - Der angegebene belegte Speicherbereich wird mit Füllzeichen aufgefüllt. ' bool% = Mem_Fill( pMem&, Anzahl&, FüllChars$ ) ' belegten Speicher füllen ' Ist Anzahl = 0 dann wird Anzahl auf Mem_Size(pMem) gesetzt. ' Der angegebene Speicherbereich wird mit Füllzeichen aufgefüllt. ' - ohne diese Angabe wird mit Chr$(0) aufgefüllt ' - bei einem Zeichen wird sys_FillMemory() genutzt ' - bei mehreren Zeichen wird sys_MoveMemory() genutzt ASM "Mem_Fill", 3 PUSH ESI PUSH EDI PUSH ECX PUSH EBX PUSH EDX MOV ESI, PAR3 // FillChars MOV ECX, PAR2 // Anzahl MOV EDI, PAR1 // pMem MOV EBX, [ESI - 4] // Len(FillChars) PUSH EDI // pMem CALL @Mem_Size CMP ECX, EAX // Anzahl <= Size ? JNA @@Fill_Anz_Inside MOV ECX, EAX // neue Anzahl @@Fill_Anz_Inside: CMP ECX, 0 // Anzahl = 0 ? JZ @@Fill_Done CMP EBX, 1 // FillChars = Null or 1 ? (nein, dann Hauptstück) JA @@Fill_more // 1 Zeichen XOR EDX, EDX // Vorgabe ist Chr$(0) CMP ESI, 0 // FillChars = NullString ? JZ @@Fill_Start MOV DL, [ESI] // Nein, dann FillChar holen @@Fill_Start: // Bereich mit einem Zeichen füllen PUSH EDX // char PUSH ECX // Anzahl PUSH EDI // pMem CALL @RtlFillMemory JMP @@Fill_Done // -------------------- @@Fill_more: CMP EBX, ECX // LEN( FillChars ) >= Anzahl ? JGE @@Fill_MoveOne // Da wir den Füllstring mehrmals schreiben wird die Länge benötigt PUSH EBX // Len(FillChars) // ---------- @@Fill_Loop: CMP ECX, EBX // Anzahl < Len(FillChars) JGE @@Fill_Hop // Nein, weiter (sonst den Rest) POP EBX // Len(FillChars) MOV EBX,ECX // überschreibe mit Rest PUSH EBX // Rest @@Fill_Hop: PUSH EBX // Len(FillChars) (oder Rest) PUSH ESI // FillChars PUSH EDI // pMem CALL @RtlMoveMemory POP EBX // Len(FillChars) PUSH EBX // Len(FillChars) ADD EDI, EBX // pMem + Len(FillChars) SUB ECX, EBX // Anzahl - Len(FillChars) CMP ECX, 0 // Anzahl > 0 JA @@Fill_Loop // -------------------- // gesicherten Wert wieder entfernen POP EBX // Len(FillChars) JMP @@Fill_Done @@Fill_MoveOne: // Es sind mehr Füllzeichen als der Bereich groß ist. // Das läßt sich in einem Rutsch erledigen. PUSH ECX // Anzahl PUSH ESI // FillChars PUSH EDI // pMem CALL @RtlMoveMemory @@Fill_Done: POP EDX POP EBX POP ECX POP EDI POP ESI ENDASM ' ******************** ' Mem_Move() - Speicherinhalte kopieren ' bool = Mem_Move( Ziel&, Quelle&, Anzahl& ) ' Der angegebene Quellbereich wird in den Zielbereich kopiert. ' (Überlappungen von Quelle und Ziel sind hier erlaubt) ASM "Mem_Move", 3 PUSH ESI PUSH EDI PUSH ECX // XOR EAX, EAX // 0 = es wurde nichts kopiert MOV ECX, PAR3 // Anzahl MOV ESI, PAR2 // pQuelle MOV EDI, PAR1 // pZiel // CMP ECX, 0 // Anzahl = 0 ? JZ @@Move_Done CMP ESI, 0 // Quelle = Null JZ @@Move_Done CMP EDI, 0 // Ziel = Null JZ @@Move_Done PUSH ECX // Anzahl PUSH ESI // pQuelle PUSH EDI // pZiel CALL @RtlMoveMemory @@Move_Ok: INC EAX // 1 = Kopieren wurde durchgeführt @@Move_Done: POP ECX POP EDI POP ESI ENDASM ' ******************** ' Mem_Resize() - Speichergröße ändern/redimensionieren ' pMem& = Mem_Resize( pMem&, Size& ) ' Speicher redimensionieren ' Ändert die Speichergröße auf die der Speicherzeiger zeigt. ' Die Größe des angegebenen Speicherbereiches wird auf die neue Größe geändert (wenn ungleich). ' Bei einer Vergrößerung des Bereiches bleiben alle Daten erhalten, ' bei einer Verkleinerung nur der vordere Teil. (!!!ohne Gewähr!!!) ' Liefert den neuen Speicherzeiger zurück. ASM "Mem_Resize", 2 PUSH ECX PUSH ESI PUSH EDI MOV EDI, PAR2 // Size MOV ESI, PAR1 // pMem CMP ESI, 0 // Nullzeiger ? Dann Mem_New(Size) JZ @@Resize_New PUSH ESI // pMem CALL @GlobalHandle // --> hMem CMP ESI, 0 JZ @@Resize_Error MOV ESI, EAX // hMem PUSH ESI // hMem CALL @GlobalSize // OldSize CMP EAX, 0 // Null JZ @@Resize_Error CMP EAX, EDI // OldSize = Size ? dann keine Größenänderung JZ @@Resize_Done PUSH ESI // hMem CALL @GlobalUnlock MOV EAX, 2 // GMEM_MOVEABLE = $2 PUSH EAX // flags PUSH EDI // size PUSH ESI // hMem CALL @GlobalReAlloc MOV ESI, EAX // hMem CMP EAX, 0 // Null JZ @@Resize_Error PUSH ESI // hMem CALL @GlobalLock MOV ESI, EAX // pMem CMP EAX, 0 // Null JZ @@Resize_Error JMP @@Resize_Done @@Resize_New: PUSH EDI // Size CALL @Mem_New MOV ESI, EAX // pMem JMP @@Resize_Done @@Resize_Error: CALL @GetLastError // ohne Parameter @@Resize_Done: MOV EAX, ESI POP EDI POP ESI POP ECX ENDASM '$ENDIF Cls Declare long test1 Print "Neu" test1 = Mem_New( 256 ) Print "Adresse: "; test1 Print " Größe: "; Mem_Size(test1) Mem_Free(test1) WaitInput FreeDLL mem_v_hDLL End
-
Wie kann ich Fehlercodes mitlaufen lassen.
- globale Variable. Wie greife ich darauf zu?
- etwa immer den Zeiger auf die Fehlervariable überall mitschleppen?- als Objekt programmieren. Keine Ahnung wie in Asm.
-
Michael
Warum wird die PDF-Datei von meinem "MalwareBytes" geblockt ?An den Moderator:
Die Frage ist vielleicht auch für die Mitglieder und XProfan-Nutzer interessant ! -
Für Fehlerbehandlung in Assembler-Routinen ist der Programmierer selbst verantwortlich. So kann man z.B. definieren, dass ein bestimmter Rückgabewert für einen Fehler steht. So machen es ja auch die Funktionen der Windows-API zum großen Teil. Der Wert in EAX ist immer der Rückgabewert. Auch bei API-Funktionen steht der Rückgabewert nach dem AUfruf in EAX. Natürlich kann der Rückgabewert auch ein Zeiger auf einen String oder einen Datenbereich sein.
Globale Variablen als solche, die in verschiedenen Assemblerfunktionen gültig sind, gibt es naturgemäß nicht. Man kann sie aber relativ einfach simulieren, in dem man eine ASM-Funktion schreibt, die deren Wert zurückgibt. Diese kann dann von allen anderen ASM-Funktionen aufgerufen werden. (Windows handhabt das intern sehr ähnlich.) Du könntest also eine ASM-Funktion GetHWnd schreiben und schon könntest Du in allen weiteren Funktionen drauf zurückgreifen.
Asssembler ist letztlich eine Hilfe um Maschinencode einzugeben (bestimmte merkbare Wörter entsprechen Op-Codes), keine wirkliche Programmiersprache, also auch nicht per se Prozedural oder Objektorientiert. Man kann allerdings ASM-Funktionen in Objekten verwenden und sogar erzeugen: Wenn man ASM-Funktionen für ein Objekt benötigt, sollte man sie im Constructor mit ASM...ENDASM erzeugen und kann sie dann in allen anderen Methoden des Objektes (genau genommen sogar im ganzen Programm) nutzen. Du könntest aus Deinen Funktionen also ein Objekt basteln, indem du sie alle im Konstruktor erzeugst und dann in den einzelnen Methoden des Objekts aufrufst. PMem würde ich dann als private Eigenschaft (etwa pmen&) der Klasse definieren, so dass alle Methoden darauf zugreifen können, es aber nach außen nicht sichtbar ist, es sei denn Du baust die entsprechende Getter-Methode (etwa getPMem) ein.
Achtung: Derzeit fehlt noch die Überprüfung, ob es eine externe Funktion des Namens schon in der Funktionsliste gibt, bevor die neue hinzugefügt wird. Sie würde dann hinzugefügt werden und es wäre mehr oder weniger zufällig, welche der beiden beim Aufruf dann genutzt wird. Die Prüfung wird aber natürlich noch eingebaut!
Gruß
Roland -
Hier Michaels Memory-Routinen als Klasse. (Einige Kommentare mussten weichen wegen des 10k-Limits im Forum.)
Code: mem_asm.inc
Alles anzeigen// MEM_ASM-Klasse CLASS MEM_ASM = #hMem&, \ ' Handle (Adresse) des erzeugten Speichers Mem_Asm@, \ ' Konstruktor aMem = New(Mem_Asm, Size) - Speicher belegen/reservieren Free@, \ ' Erfolg = aMem.Free( hMem ) - Speicher freigeben Size@, \ ' Size = aMem.Size( hMem ) - Größe des belegten Speichers ermitteln Clear@, \ ' Size = aMem.Clear( hMem ) - Speicher mit Nullen überschreiben Fill@, \ ' Erfolg = aMem.Fill( hMem, Anz, Fill ) - Der angegebene belegte Speicherbereich wird mit Füllzeichen aufgefüllt. Move@, \ ' Erfolg = aMem.Move( Ziel, Quelle, Anz ) - Speicherinhalte kopieren Resize@, \ ' Erfolg = aMem.Resize( hMem, Size ) - Speichergröße ändern/redimensionieren getHandle@ ' hMem = aMem.GetHandle () - Handle ermitteln Declare handle MEM_ASM.hDLL ' Klassenvariable Proc MEM_ASM.Mem_Asm // Konstruktor /***********************/ Parameters long size MEM_ASM.hDLL = UseDLL("KERNEL32.DLL") ' Der Zähler für die DLL wird bei jedem Konstruktor erhöht und bei jedem Destruktor (Destroy) wieder erniedrigt If fAddr("Mem_New") = 0 // Der Code bis zum zugehörigen ENDIF wird nur beim allerersten Mal aufgerufen. Dann stehen die importierten Funktionen // und die mit ASM hinzugefügten Funktionen zur Verfügung ImportFunc( MEM_ASM.hDLL, "GetLastError", "" ) // GetLastError( ) ImportFunc( MEM_ASM.hDLL, "GlobalAlloc", "" ) // GlobalAlloc( flags, Size ) ImportFunc( MEM_ASM.hDLL, "GlobalFree", "" ) // GlobalFree( hMem ) ImportFunc( MEM_ASM.hDLL, "GlobalHandle", "" ) // GlobalHandle( pMem ) ImportFunc( MEM_ASM.hDLL, "GlobalLock", "" ) // GlobalLock( hMem ) ImportFunc( MEM_ASM.hDLL, "GlobalReAlloc", "" ) // GlobalReAlloc( hMem, Size, flags ) ImportFunc( MEM_ASM.hDLL, "GlobalSize", "" ) // GlobalSize( hMem ) ImportFunc( MEM_ASM.hDLL, "GlobalUnlock", "" ) // GlobalUnlock( hMem ) ImportFunc( MEM_ASM.hDLL, "RtlFillMemory", "" ) // FillMemory( pDest, Len, Char ) ImportFunc( MEM_ASM.hDLL, "RtlMoveMemory", "" ) // MoveMemory( pDest, pSrc, Len ) 'Überlappung erlaubt ImportFunc( MEM_ASM.hDLL, "RtlZeroMemory", "" ) // ZeroMemory( pDest, Len ) ASM "Mem_New", 1 PUSH ECX MOV ECX, 66 // GMEM_MOVEABLE = 2 | GMEM_ZEROINIT = 64 | GMEM_PTR = 66 MOV EAX, PAR1 // Size CMP EAX, 0 // Null ? JZ @@New_Done PUSH EAX // Size PUSH ECX // Flags CALL @GlobalAlloc CMP EAX, 0 // Null ? JZ @@New_Done PUSH EAX // hMem CALL @GlobalLock @@New_Done: POP ECX ENDASM ASM "Mem_Free", 1 PUSH ECX MOV EAX, PAR1 // pMem CMP EAX, 0 JZ @@Free_Done PUSH EAX // pMem CALL @GlobalHandle CMP EAX, 0 JZ @@Free_Error MOV ECX, EAX PUSH ECX // hMem CALL @GlobalUnlock CMP EAX, 0 JZ @@Free_Error PUSH ECX // hMem CALL @GlobalFree CMP EAX, 0 JNZ @@Free_Done @@Free_Error: CALL @GetLastError @@Free_Done: POP ECX ENDASM ASM "Mem_Size", 1 PUSH ESI PUSH EDI XOR EDI,EDI MOV ESI, PAR1 // pMem CMP ESI, 0 JZ @@Size_Done PUSH ESI // pMem CALL @GlobalHandle // --> hMem MOV ESI, EAX // hMem CMP EAX, 0 JZ @@Size_Error PUSH ESI // hMem CALL @GlobalSize // size MOV EDI, EAX // size JNZ @@Size_Done @@Size_Error: CALL @GetLastError @@Size_Done: MOV EAX,EDI POP EDI POP ESI ENDASM ASM "Mem_Clear", 1 PUSH ESI XOR EAX, EAX MOV ESI, PAR1 // pMem CMP ESI, 0 JZ @@Clear_Done PUSH ESI // pMem CALL @Mem_Size CMP EAX, 0 // size JZ @@Clear_Done PUSH EAX // size PUSH ESI // pMem CALL @RtlZeroMemory @@Clear_Done: POP ESI ENDASM ASM "Mem_Fill", 3 PUSH ESI PUSH EDI PUSH ECX PUSH EBX PUSH EDX MOV ESI, PAR3 // FillChars MOV ECX, PAR2 // Anzahl MOV EDI, PAR1 // pMem MOV EBX, [ESI - 4] // Len(FillChars) PUSH EDI // pMem CALL @Mem_Size CMP ECX, EAX // Anzahl <= Size ? JNA @@Fill_Anz_Inside MOV ECX, EAX // neue Anzahl @@Fill_Anz_Inside: CMP ECX, 0 // Anzahl = 0 ? JZ @@Fill_Done CMP EBX, 1 // FillChars = Null or 1 ? (nein, dann Hauptstück) JA @@Fill_more // 1 Zeichen XOR EDX, EDX // Vorgabe ist Chr$(0) CMP ESI, 0 // FillChars = NullString ? JZ @@Fill_Start MOV DL, [ESI] // Nein, dann FillChar holen @@Fill_Start: // Bereich mit einem Zeichen füllen PUSH EDX // char PUSH ECX // Anzahl PUSH EDI // pMem CALL @RtlFillMemory JMP @@Fill_Done // -------------------- @@Fill_more: CMP EBX, ECX // LEN( FillChars ) >= Anzahl ? JGE @@Fill_MoveOne // Da wir den Füllstring mehrmals schreiben wird die Länge benötigt PUSH EBX // Len(FillChars) // ---------- @@Fill_Loop: CMP ECX, EBX // Anzahl < Len(FillChars) JGE @@Fill_Hop // Nein, weiter (sonst den Rest) POP EBX // Len(FillChars) MOV EBX,ECX // überschreibe mit Rest PUSH EBX // Rest @@Fill_Hop: PUSH EBX // Len(FillChars) (oder Rest) PUSH ESI // FillChars PUSH EDI // pMem CALL @RtlMoveMemory POP EBX // Len(FillChars) PUSH EBX // Len(FillChars) ADD EDI, EBX // pMem + Len(FillChars) SUB ECX, EBX // Anzahl - Len(FillChars) CMP ECX, 0 // Anzahl > 0 JA @@Fill_Loop // -------------------- // gesicherten Wert wieder entfernen POP EBX // Len(FillChars) JMP @@Fill_Done @@Fill_MoveOne: // Es sind mehr Füllzeichen als der Bereich groß ist. // Das läßt sich in einem Rutsch erledigen. PUSH ECX // Anzahl PUSH ESI // FillChars PUSH EDI // pMem CALL @RtlMoveMemory @@Fill_Done: POP EDX POP EBX POP ECX POP EDI POP ESI ENDASM ASM "Mem_Move", 3 PUSH ESI PUSH EDI PUSH ECX // XOR EAX, EAX // 0 = es wurde nichts kopiert MOV ECX, PAR3 // Anzahl MOV ESI, PAR2 // pQuelle MOV EDI, PAR1 // pZiel // CMP ECX, 0 // Anzahl = 0 ? JZ @@Move_Done CMP ESI, 0 // Quelle = Null JZ @@Move_Done CMP EDI, 0 // Ziel = Null JZ @@Move_Done PUSH ECX // Anzahl PUSH ESI // pQuelle PUSH EDI // pZiel CALL @RtlMoveMemory @@Move_Ok: INC EAX // 1 = Kopieren wurde durchgeführt @@Move_Done: POP ECX POP EDI POP ESI ENDASM ASM "Mem_Resize", 2 PUSH ECX PUSH ESI PUSH EDI MOV EDI, PAR2 // Size MOV ESI, PAR1 // pMem CMP ESI, 0 // Nullzeiger ? Dann Mem_New(Size) JZ @@Resize_New PUSH ESI // pMem CALL @GlobalHandle // --> hMem CMP ESI, 0 JZ @@Resize_Error MOV ESI, EAX // hMem PUSH ESI // hMem CALL @GlobalSize // OldSize CMP EAX, 0 // Null JZ @@Resize_Error CMP EAX, EDI // OldSize = Size ? dann keine Größenänderung JZ @@Resize_Done PUSH ESI // hMem CALL @GlobalUnlock MOV EAX, 2 // GMEM_MOVEABLE = $2 PUSH EAX // flags PUSH EDI // size PUSH ESI // hMem CALL @GlobalReAlloc MOV ESI, EAX // hMem CMP EAX, 0 // Null JZ @@Resize_Error PUSH ESI // hMem CALL @GlobalLock MOV ESI, EAX // pMem CMP EAX, 0 // Null JZ @@Resize_Error JMP @@Resize_Done @@Resize_New: PUSH EDI // Size CALL @Mem_New MOV ESI, EAX // pMem JMP @@Resize_Done @@Resize_Error: CALL @GetLastError // ohne Parameter @@Resize_Done: MOV EAX, ESI POP EDI POP ESI POP ECX ENDASM EndIf .hMem& = Mem_New(size) var int Ergebnis = 0 if .hMem& > 0 Ergebnis = 1 EndIf EndProc Proc MEM_ASM.Free /***************/ Var int Ergebnis = Mem_Free(.hMem&) FreeDLL MEM_ASM.hDLL Return Ergebnis EndProc Proc MEM_ASM.Size /***************/ Return Mem_Size(.hMem&) EndProc Proc MEM_ASM.Clear /****************/ Return Mem_Clear(.hMem&) EndProc Proc MEM_ASM.Fill /***************/ Parameters long Anz, cFill Return Mem_Fill(.hMem&, Anz, cFill) EndProc Proc MEM_ASM.Move /***************/ Parameters long ziel, Quelle, Anz Return Mem_Move(Ziel, Quelle, Anz) EndProc Proc MEM_ASM.Resize /*****************/ parameters long size var int ergebnis = 0 If Mem_Resize(.hMem&, Size) ergebnis = 1 endif return ergebnis EndProc Proc MEM_ASM.getHandle /********************/ Return .hMem& EndProc
-
Und hier ein Beispielprogramm dass diese Klasse nutzt:
-
auch wenn die Bezeichnung nicht ganz stimmt... (New gibt einen Zeiger zurück, das Handle war für die Zwischenarbeiten)
Auf die Idee, das Ganze dann einfach in XProfan einzuwickeln bin ich nicht gekommen...
@Neuer Gast:
Erstens habe ich hier keine PDF hochgeladen und zweitens: Wer macht sich die Mühe etwas in einer PDF zu verstecken. Das ist doch von übergestern. -
Hier mal ein kleiner Hexdump. Die Ausgabe erfolgt in der Listbox. Der ASM-Code ist zwar noch verbesserungswürdig, aber immerhin das Schnellste an dem Progrämmchen
Code
Alles anzeigenDeclare Mem Quelle, Ziel Declare String Datei Declare Handle hFile Declare Int DateiLaenge, ZLaenge, Zeilen, ZielGross, Gelesen ASM "Hexe", 5 push ebx push ecx push edx push edi push esi mov esi, Par1 // Parameter 1: Quellbereich mov edi, Par2 // Parameter 2: Zielbereich mov ecx, Par3 // Parameter 3: QuellLänge mov edx, Par4 // Parameter 4: AusgabeLänge (Zeilenlänge üblicherweise 16 oder mitunter auch 8) mov eax, Par5 // Parameter 5: Anzeigeadresse der ersten Zeile mov [@@Adresse], eax mov [@@ZLang], edx // wenn als Zielbereich 0 gesetzt wird, wird nichts ausgeführt und anhand der QuellLänge // und AusgabeLänge wird die notwendige Mindestgröße für den Zielbereich berechnet und zurückgegeben or edi, edi jz @@MC jmp @@Begin // ein direkter Sprung JNZ @@SizeCalc ist zu lang // ebenso wie die Umkehrung JNZ @@Begin // deshalb dieser relative Sprung über einen JMP @@MC: jmp @@SizeCalc @@Zeile: dd 0 @@ZLang: dd 0 @@Adresse: dd 0 @@AsciiZeile: // gibt die Eingabe als Text aus und ersetzt besondere Zeichen durch einen Mittelpunkt mov esi, [@@Zeile] @@AsciiByte: lodsb cmp al, $5e jz @@NoChar cmp al, $60 jz @@NoChar cmp al, $a8 jz @@NoChar cmp al, $b4 jz @@NoChar cmp al, 31 jg @@AsciiOut @@NoChar: mov al, $b7 @@AsciiOut: stosb dec edx jnz @@AsciiByte ret @@Conv: // Konvertiert ein Nibble and al, $0f add al, $30 cmp al, $3a jl @@NibOk add al, 7 @@NibOk: ret @@ConvByte: // Konvertiert ein Byte push eax shr al, 4 call @@Conv stosb pop eax call @@Conv stosb ret @@LongConv: // Konvertiert einen Long mov al, [esi + 3] call @@ConvByte mov al, [esi + 2] call @@ConvByte mov al, [esi + 1] call @@ConvByte mov al, [esi] call @@ConvByte ret @@AdrOut: // Gibt die Adresse hexadezimal zu 8 Zeichen Länge aus push esi mov esi, @@Adresse @@NextAdr: call @@LongConv mov al, 32 stosb stosb mov eax, [@@Adresse] add eax, edx mov [@@Adresse], eax pop esi ret @@Begin: cld mov [@@Zeile], esi // notiert aktuellen Zeilenbeginn in der Quelle call @@AdrOut // Quelladresse ausgeben @@Zeichen: lodsb call @@ConvByte mov al, 32 stosb dec edx jnz @@InZeile // in aktueller Zeile verbleiben // oder Zeilenschaltung einfügen mov edx, [@@ZLang] call @@AsciiZeile // hier ASCII-Interpretation mov al, 13 stosb mov al, 10 stosb mov edx, [@@ZLang] mov [@@Zeile], esi // notiert neuen Zeilenbeginn in der Quelle call @@AdrOut // Quelladresse für neue Zeile ausgeben @@InZeile: dec ecx jnz @@Zeichen // Ausgabelänge noch nicht erreicht // Restlänge der letzten Zeile steht in EDX or edx, edx jz @@NoRest push edx mov al, 32 @@RestFill: stosb stosb stosb dec edx jnz @@RestFill pop edx mov eax, [@@ZLang] cmp eax, edx jz @@NoRest sub eax, edx mov edx, eax call @@AsciiZeile @@NoRest: mov al, 13 stosb mov al, 10 stosb xor eax, eax stosb jmp @@Raus @@SizeCalc: // Berechne Speichergröße für Ausgabe push edx mov eax, ecx mov ecx, edx xor edx, edx idiv ecx inc eax // Anzahl Zeilen mov ecx, eax // Anzahl Zeilen nach ECX pop eax // Zeilenlänge add eax, eax add eax, eax add eax, 12 mul ecx add eax, 4 @@Raus: pop esi pop edi pop edx pop ecx pop ebx EndASM CLS ZLaenge = 16 Datei = LoadFile$("Datei wählen", "C:\*.*") If Datei <> "" hFile = Assign(Datei) OpenRW hFile Dateilaenge = GetFileSize(hFile) DIM Quelle, DateiLaenge ZielGross = Hexe(Quelle, 0, DateiLaenge, ZLaenge, 0) ' Zielspeichergröße berechnen lassen und dimmen DIM Ziel, ZielGross Gelesen = BlockRead(hFile, Quelle, 0, DateiLaenge) Close hFile Hexe(Quelle, Ziel, DateiLaenge, ZLaenge, 0) ' Hexdump erzeugen Move("MemToList", Ziel, "\n") ListBox$("Hexdump", 2) Dispose Quelle, Ziel EndIf Print "Fertig" WaitInput End
HEXE wird mit 5 Parametern aufgerufen:
1. Bereichsvariable, in der die Quelldaten stehen
2. Bereichsvariable, die das Ergebnis aufnehmen soll
3. Anzahl Bytes im Quellbereich
4. Länge einer Ausgabezeile, üblicherweise 16 oder mitunter auch 8
5. Anzeigeadresse für die erste ZeileWird in 2 anstelle einer Bereichsvariablen der Wert 0 angegeben, wird die notwendige Mindestgröße für diesen Bereich berechnet und zurückgegeben. Damit kann dann die Bereichvariable gedimmt und anschließend die Funktion nochmals mit der Adresse des Bereiches aufgerufen werden.
Gruß Volkmar
-
Jetzt mitmachen!
Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!