Lowlevel

Lowlevel => Lowlevel-Coding => Thema gestartet von: ChristianF am 13. August 2009, 08:40

Titel: Kleiner Assembler Thread
Beitrag von: ChristianF am 13. August 2009, 08:40
Moin Moin,
da ich für diese Frage keinen passenden existierenden Thread gefunden habe, der halbwegs aktuell ist, erstelle ich mal wieder einen neuen.  :evil: Deswegen werde ich auch weitere Fragen dann hier stellen :)
 
Und zwar beschäftige ich mich einfach mal so etwas mehr mit Assembler, was heißt, ich habe meinen GRUB Assembler-Part, damit der Kernel überhaupt geladen wird, übernommen und bin dabei einen kleinen Kernel in reinem Assembler zu schreiben.
 
Das erste Hindernis hat aber leider nicht lange auf sich warten lassen.  :cry:
So sieht das momentan aus (klein und noch übersichtlich):
[SECTION .text]
global kernel_main
kernel_main:
push dword string ; parameter 1 der String
call video_write_string

ret

[SECTION .data]
string db "Hello World, From AsmOS",10,0

Wie man sieht, schiebe ich den String "string" als Parameter auf den Stack und rufe die Funktion auf. Hier versuche ich dann wie folgt den übergebenen String auszugeben:
video_write_string:
push ebp
;push edi
mov ebp, esp

mov esi, [ebp+8]
mov edi, 0xB8000
.printloop:
lodsb

test al, al
jz .printdone

stosb

mov al, 0x0F
stosb

jmp .printloop
.printdone:

mov esp, ebp
;pop edi
pop ebp
ret

Allerdings funktioniert dies nicht richtig. Der Ablauf ist, dass der string und danach einige kryptische Zeichen ausgegeben werden und dann anscheinend ein triple fault ausgelöst wird, da bochs neugestartet wird. Was habe ich hier falsch gemacht?  :?
 
Gruß Christian
Titel: Re: Kleiner Assembler Thread
Beitrag von: Jidder am 13. August 2009, 11:00
[SECTION .text]
global kernel_main
kernel_main:
push dword string ; parameter 1 der String
call video_write_string

ret
Das ist ret ist falsch. Wo soll das denn hinspringen? Außerdem holst du den Zeiger auf den string nicht vom Stack. Deswegem springt das ret zum String.
Titel: Re: Kleiner Assembler Thread
Beitrag von: ChristianF am 13. August 2009, 11:17
Durch das ret soll er wieder in die Funktion zurückspringen, von wo sie aufgerufen wurde.
kernel_main wird von dem GRUB Assembler-Part aufgerufen:
extern kernel_main
kernel_startup:
push eax ; first Parameter - multiboot magic number
push ebx ; second Parameter - multiboot structure address
call kernel_main
;add esp, 8 ; remove previous pushed parameters
jmp $
Oder ist diese Art vorzugehen nicht sinnvoll? Sollte ich eventuell besser direkt dorthin (kernel_main) springen, oder einfach das ret weglassen?
 
Außerdem holst du den Zeiger auf den string nicht vom Stack. Deswegem springt das ret zum String...
Das war der Fehler, so was blödes. Was auf den Stack geschoben wird muss natürlich auch wieder runter...
 
Ich denke mal, dass man alle in der Funktion verwendeten Register erst sichern und am Schluss wiederherstellen sollte. Zumindest musste ich das in der Schule machen, als es um die Microcontoller-Programmierung ging.
Titel: Re: Kleiner Assembler Thread
Beitrag von: Jidder am 13. August 2009, 11:31
Oder ist diese Art vorzugehen nicht sinnvoll?
Doch das ist in Ordnung. Ich dachte das sollte vielleicht zu GRUB zurückspringen oder so. Oder dass du dir darüber überhaupt gar keine Gedanken gemacht hast. Aber so passt das schon.
 
Ich denke mal, dass man alle in der Funktion verwendeten Register erst sichern und am Schluss wiederherstellen sollte. Zumindest musste ich das in der Schule machen, als es um die Microcontoller-Programmierung ging.
Naja, ich denke mal in der Schule solltest du halt nach Schema F programmieren, damit du (oder dein Lehrer) nicht dauernd Fehler suchen musst, die wegen veränderter Register auftreten.

Aber ich finde Assemblerprogrammieren mit Aufrufkonventionen (dann noch die aus C -.-), Register sichern, Parameter auf dem Stack, etc langweilig ... da kannste gleich bei C bleiben. Da darfste dir auch ein paar Makros machen, und mit 8 globalen Variablen, die wie die Register benannt sind, arbeiten, wenn du willst, dass der Code aussieht wie Assembler. Ich glaube ich bau da mal ein Framework ;)
Titel: Re: Kleiner Assembler Thread
Beitrag von: ChristianF am 13. August 2009, 12:29
Oder ist diese Art vorzugehen nicht sinnvoll?
Doch das ist in Ordnung. Ich dachte das sollte vielleicht zu GRUB zurückspringen oder so. Oder dass du dir darüber überhaupt gar keine Gedanken gemacht hast. Aber so passt das schon.
 
Ich denke mal, dass man alle in der Funktion verwendeten Register erst sichern und am Schluss wiederherstellen sollte. Zumindest musste ich das in der Schule machen, als es um die Microcontoller-Programmierung ging.
Naja, ich denke mal in der Schule solltest du halt nach Schema F programmieren, damit du (oder dein Lehrer) nicht dauernd Fehler suchen musst, die wegen veränderter Register auftreten.

Aber ich finde Assemblerprogrammieren mit Aufrufkonventionen (dann noch die aus C -.-), Register sichern, Parameter auf dem Stack, etc langweilig ... da kannste gleich bei C bleiben. Da darfste dir auch ein paar Makros machen, und mit 8 globalen Variablen, die wie die Register benannt sind, arbeiten, wenn du willst, dass der Code aussieht wie Assembler. Ich glaube ich bau da mal ein Framework ;)
Ich bin ja noch am experimentieren.  :roll:
 
Wenn ich meine Funktion wie hier aufrufe mit "push word ..." muss ich ja am Ende esp immer um 4 (Rücksprungadresse) und die Anzahl der gepushten Parameter multipliziert mit 4 erhöhen, also hier dann das "add esp, 8". Oder habe ich da was falsch verstanden?
Titel: Re: Kleiner Assembler Thread
Beitrag von: Cjreek am 13. August 2009, 12:47
Hi,

Also wenn du im 32-Bit Modus bist und alle deine Parameter über den Stack übergibst, dann musst du am Ende deiner Funktion folgendes schreiben

ret ParamCount*4 ; <-- 4 Byte(32 Bit) pro Parameter
also als Bsp:

push 20
push 50
call addieren
jmp $

addieren:
     push ebp
     mov ebp, esp
     
     mov eax, [ebp+0x08]
     mov edx, [ebp+0x10] 

     add eax, edx

     pop ebp
     ret 8   ; = ret + add esp, 8

Lg
Cjreek
Titel: Re: Kleiner Assembler Thread
Beitrag von: ChristianF am 13. August 2009, 13:04
Gut zu wissen.  :-D
 
Und wieder eine weitere Frage...
Muss ich in jeder Assembler-Datei die Sektionen angeben?
Quasi so:
[SECTION .text]
global func
func:
    ; mach wass
    ret
[SECTION .data]
str db "Bla Fasel",0
Oder könnte man das auch weglassen?
Titel: Re: Kleiner Assembler Thread
Beitrag von: MNemo am 13. August 2009, 14:06
Wenn ich meine Funktion wie hier aufrufe mit "push dword ..." muss ich ja am Ende esp immer um 4 (Rücksprungadresse) und die Anzahl der gepushten Parameter multipliziert mit 4 erhöhen, also hier dann das "add esp, 8". Oder habe ich da was falsch verstanden?
Nein, die Rücksprungadresse wird schon vom ret vom Stack genommen.
Du musst dich nur um die Parameter kümmern die du vorher gepusht hast.

bei einem
ret xmusst du aufpassen, denn nach der C-Aufrufkonventionen kümmert sich der Aufrufer um den Stack. Du kannst das also nicht verwenden, wenn du die Funktion von C aus aufrufen willst.
Titel: Re: Kleiner Assembler Thread
Beitrag von: Jidder am 13. August 2009, 14:11
Edit: zu langsam -.-

Dann beantworte ich halt die nächste Frage:

Und wieder eine weitere Frage...
Muss ich in jeder Assembler-Datei die Sektionen angeben?
Quasi so:
[SECTION .text]
global func
func:
    ; mach wass
    ret
[SECTION .data]
str db "Bla Fasel",0
Oder könnte man das auch weglassen?
Nein, kannst du im Allgemeinen nicht weglassen. (Ausnahme wäre .text, weil das Standard ist.)
Titel: Re: Kleiner Assembler Thread
Beitrag von: ChristianF am 13. August 2009, 14:30
Gut gut.
Die sache mit den Aufruf-Konventionen muss ich mir mal genauer anschauen. Da ich allerdings nur in Assembler programmieren möchte, denke ich, dass ich das "ret x", also nicht die __cdecl-Konvention, nehmen kann.
Ich müsste allerdings erst nochmals schauen und genau abwägen, was ich letzten endes nutze.
Titel: Re: Kleiner Assembler Thread
Beitrag von: ChristianF am 14. August 2009, 11:34
So...
ich habe mal etwas weiter experimentiert und eine Funktion zustande gebracht, die den Bildschirm löscht. Diese funktioniert auch, da ich aber nicht so gut in Assembler bin, wollte ich einfach mal fragen, was überflüssig sein könnte, oder was ihr anders gemacht hättet.
 
Hier der Code:
video_clear_screen:
push ebp
mov ebp, esp

; Register sichern
push edi
push ebx
push eax

; Adresse aus Variable holen (0xB8000)
mov edi, [video_address]
; Prüfwert berechnen
mov eax, 25
mov edx, 80
mul edx
mov FUNCTION_LOCALVAR_1, eax

mov ebx, 0
.loop_cls:
; Leerzeichen ausgeben, mit schwarzem
; hintergrund und weißer schrift
mov al, 0x20 ; blank
stosb
mov al, 0x0f ; Black background, white text
stosb

; Schleifen-Variable um 1 erhöhen
inc ebx
; Wurde das Ende erreicht?
cmp ebx, FUNCTION_LOCALVAR_1
; Nein? -> Wiederholen :D
jne .loop_cls

; Register wiederherstellen
pop eax
pop ebx
pop edi

mov esp, ebp
pop ebp
ret

 
Bei "FUNCTION_LOCALVAR_1" handelt es sich um ein Makro, das wie folgt aussieht: "%define FUNCTION_LOCALVAR_1 [ebp-4]".
Wenn ich nun eine solche Variable auf dem Stack benutze, muss ich die dann auch vorher reservieren und später wieder freigeben, oder kann ich das machen, wie es mir beliebt?
Titel: Re: Kleiner Assembler Thread
Beitrag von: Jidder am 14. August 2009, 12:02
Hi,

du musst für lokale Variablen auf dem Stack den Speicher reservieren, sonst werden diese beim nächsten Interrupt oder Funktionsaufruf überschrieben.

Bei dir ist an [ebp - 4] übrigens der gesicherte Wert von edi. Ich weiß nicht, ob du den überschreiben willst.

Ich hätte einfach sowas gemacht:
; Leert den Bildschirm
; Diese Funktion verändert eax, ecx und edi. <-- sonstige Seiteneffekte
video_clear_screen:
    mov ecx, 80*25
    mov ax, 0x0f20
    mov edi, 0xb8000
    rep stosw
    ret

(ungetestet)
Titel: Re: Kleiner Assembler Thread
Beitrag von: ChristianF am 14. August 2009, 12:32
Der sollte natürlich nicht überschrieben werden...
 
Wenn ich deine Funktion richtig verstanden habe, ist rep zu vergleichen mit einer For-Schleife, die von 0 bis 80*25 hochzählt und jedes mal "stosw" ausführt.
Getestet wurde es eben von mir und es hat funktioniert...  :-)
Titel: Re: Kleiner Assembler Thread
Beitrag von: MNemo am 14. August 2009, 12:59
Wenn ich deine Funktion richtig verstanden habe, ist rep zu vergleichen mit einer For-Schleife, die von 0 bis 80*25 hochzählt und jedes mal "stosw" ausführt.
Genauer gesagt zählt sie (r/e)cx bis 0 runter.

wenn du dennoch mal Locale variablen brauchst reservierst du dir den Speicher mit
push ebp
mov ebp, esp
sup esp, <x-Bytes>
oder mit dem Äquivalent dazu 'enter <x-Bytes>, 0', was aber aus mir unbekannten Gründen so eigentlich nie verwendet wird, anders als
'leave', dem Äquivalent zu 'mov esp, ebp; pop ebp'.


@PorkChicken: Wenn schon ohne Calling Convention, dann fehlt in deiner Funktionsbeschreibung:
; setzt DF = 0 vorraus

PS: ChristianF, du setzt hoffentlich nicht wegen der "Geschwindigkeitsvorteile" auf asm.
Titel: Re: Kleiner Assembler Thread
Beitrag von: ChristianF am 14. August 2009, 13:15
Nein, ich möchte keine Geschwindigkeitsvorteile erzielen. :)
Ich möchte einfach nur etwas mehr Assembler programmieren, anstatt die ganze Zeit über C. :roll:
 
Wegen des "setzt DF = 0 vorraus", heißt das, ich muss vor jedem Aufruf der Funktion prüfen, ob df in FLAGS ungleich 0 ist und es dann auf 0 setzen? Das finde ich an und für sich nicht schön, es sei denn, das "Direction Flag" kann nur von Hand gesetzt werden...
Titel: Re: Kleiner Assembler Thread
Beitrag von: MNemo am 14. August 2009, 13:33
Wegen des "setzt DF = 0 vorraus", heißt das, ich muss vor jedem Aufruf der Funktion prüfen, ob df in FLAGS ungleich 0 ist und es dann auf 0 setzen? Das finde ich an und für sich nicht schön, es sei denn, das "Direction Flag" kann nur von Hand gesetzt werden...
Nein, nein… das Flag lässt sich nur von Hand ändern, entweder mit STD bzw. CLD oder über das FLAG-Register. Aber sobald du irgend wo ein STD verwendest wird dein Bildschirm plötzlich nicht mehr sauber.

Deshalb finde ich Konventionen, dir z.B. sagen das DF vor dem Start, und nach dem ende einer Funktion 0 sein muss, gar nicht schlecht.
Titel: Re: Kleiner Assembler Thread
Beitrag von: Jidder am 14. August 2009, 14:04
Normalerweise muss man auch nicht am Direction Flag rumfummeln. Auf vorwärts stellen und gut is ^^
Titel: Re: Kleiner Assembler Thread
Beitrag von: ChristianF am 14. August 2009, 14:55
Gut...
 
Wie das eben so ist, habe ich noch eine kleine Frage... :D
Folgendes Konstrukt funktioniert einwandfrei:
push byte 0x25
call video_write_char

Nun durchlaufe ich allerdings in der Funktion zum Ausgeben eines Strings diesen und lade ihn mit lodsb nach al. Folglich habe ich mir folgendes gedacht:
push byte al
call video_write_char
Allerdings funktioniert dies nicht, da mir der Fehler "error: invalid combination of opcode and operands" entgegengeworfen wird. Also habe ich ax und auch mal eax auf den Stack geschoben und dann die Funktion aufgerufen:
push ax ; oder auch 'push eax'
call video_write_char
Allerdings wird so immer nur der letzte Buchstabe ausgegeben. Was mache ich da Falsch? Wenn in "al" ein Leerzeichen ist, wäre es das gleiche, wie folgender Aufruf:
push byte 0x20
call video_write_char

Wieso funktioniert das nicht, bzw. nur teilweise, wenn ich ax oder eax auf den Stack schiebe?
Titel: Re: Kleiner Assembler Thread
Beitrag von: MNemo am 14. August 2009, 15:15
Mit den zwei Zeilen kann ich dir nicht sagen wo dein Fehler ist.

Funktioniert denn mit:
push byte 0x25
call video_write_char
Die Ausgabe mit eines '%', viel mehr sollte sich nicht tun?

Und das tut, dann auch noch mit push eax, (bei eax = 0x25); daran liegt der Fehler nicht.
Titel: Re: Kleiner Assembler Thread
Beitrag von: Cjreek am 14. August 2009, 15:26
Hi,

Ich habe das Gefühl dass du alle Zeichen auf die gleiche Stelle schreibst.

Zeig mal deine Funktion um Strings auszugeben. Ich glaube du schreibst alle Chars "übereinander" wodurch nur der letzte sichtbar ist.

Lg
Cjreek
Titel: Re: Kleiner Assembler Thread
Beitrag von: ChristianF am 14. August 2009, 15:59
Nun, so war es auch...
 
Ich habe das geändert, allerdings funktioniert es nicht. Auch habe ich noch eine Frage.
Wenn ich "mov eax, [GlobaleVariable]" schreibe, heißt das doch eigentlich hole den Inhalt von "GlobaleVariable" und schiebe ihn nach eax. Oder ist das falsch?
 
Hier der Code zum ausgeben eines Zeichens:
video_write_char:
push ebp
mov ebp, esp
; save later used registers
push eax
push edi

; ToDo: Calculate position for edi
mov esi, 0xb8000

mov eax, [video_cursor_y]
mov ebx, 80
mul ebx
add eax, [video_cursor_x]
mul esi
mov eax, esi

; Read parameter and print out character
mov al, FUNCTION_PARAMETER_1 ; <-- Character
mov ah, 0x0F ; <-- black background white fontcolor
stosw
; ToDo: Set new Cursor positions
mov eax, [video_cursor_x]
inc eax
mov [video_cursor_x], eax

; Move Cursor;
call video_move_cursor

; restore register
pop edi
pop eax

mov esp, ebp
pop ebp
ret 4
Am Ende der Datei sind folgende Daten reserviert worden:
[SECTION .data]
video_cursor_x dw 0
video_cursor_y dw 0
video_address dd 0xb8000

 
EDIT:
Noch eine kleine Frage nebenbei: Muss ich für die Shift-Operator wirklich lokale Variablen anlegen, oder kann man das auch anders lösen, da z.B. folgendes Konstrukt nicht funktioniert:
out dx, [ebx >> 8]
Titel: Re: Kleiner Assembler Thread
Beitrag von: MNemo am 14. August 2009, 16:17
Wenn ich "mov eax, [GlobaleVariable]" schreibe, heißt das doch eigentlich hole den Inhalt von "GlobaleVariable" und schiebe ihn nach eax.
Genau.
 
Zitat
video_write_char:
push ebp
mov ebp, esp
; save later used registers
push eax
push edi

; ToDo: Calculate position for edi
mov esi, 0xb8000

mov eax, [video_cursor_y]
mov ebx, 80
mul ebx
add eax, [video_cursor_x]
mul esi
mov eax, esi
Was machst du denn hier?
eax = (video_cursor_y * 80 + video_cursor_x) * 0xb8000;
eax = 0xb8000;

BTW verwendest du hier ebx, und esi, die du nicht sicherst. Wobei du das esi aber druch edi ersetzen solltest, dem stosw zu liebe  :wink:
Zitat
; Read parameter and print out character
mov al, FUNCTION_PARAMETER_1 ; <-- Character
mov ah, 0x0F ; <-- black background white fontcolor
stosw

; ToDo: Set new Cursor positions
mov eax, [video_cursor_x]
inc eax
mov [video_cursor_x], eax

; Move Cursor;
call video_move_cursor

; restore register
pop edi
pop eax

mov esp, ebp
pop ebp
ret 4
Am Ende der Datei sind folgende Daten reserviert worden:
[SECTION .data]
video_cursor_x dw 0
video_cursor_y dw 0
video_address dd 0xb8000
Titel: Re: Kleiner Assembler Thread
Beitrag von: ChristianF am 14. August 2009, 17:29
Eigentlich sollte das auch edi sein, anscheinend hatte ich da wegen irgendwas edi im Kopf.
Wie dem auch sei, ebx, edi und eax werden nun gesichert, wie es sein soll.
 
Was ich allerdings noch nicht verstehe ist, warum jeder 2. Buchstabe "verschluckt" wird. Anscheinend wird der 2. Buchstabe immer als Wert für Hintergrundfarbe und Schriftfarbe verwendet...
 
Was ich geändert habe ist die Berechnung von edi, die ja wie folgt ist:
eax = (video_cursor_y * 80 + video_cursor_x) + 0xb8000;
und esi wurde in edi umbenannt.
 
Der Aufruf aus write_string sieht wie folgt aus:
.printloop:
lodsb
test al, al
jz .printdone

; call function to print out current character
push eax
call video_write_char

jmp .printloop
.printdone:
Merkwürdig ist auch, dass bei einem "push ax" anstatt dem "push eax" ein triple fault ausgelöst wird. Ist das so normal?
Titel: Re: Kleiner Assembler Thread
Beitrag von: MNemo am 14. August 2009, 17:39
Was ich allerdings noch nicht verstehe ist, warum jeder 2. Buchstabe "verschluckt" wird. Anscheinend wird der 2. Buchstabe immer als Wert für Hintergrundfarbe und Schriftfarbe verwendet...
 
Was ich geändert habe ist die Berechnung von edi, die ja wie folgt ist:
eax = (video_cursor_y * 80 + video_cursor_x) + 0xb8000;
und esi wurde in edi umbenannt.
das hab ich ganz übersehen, natürlich brauchst du für jedes Zeichen 2 Bytes du musst also die Cursorposition mit 2 multiplizieren. das müsste so ungefähr gehen:
mov eax, [video_cursor_y]
mov ebx, 80
mul ebx
add eax, [video_cursor_x]
lea edi, [0xb8000 + eax*2]

Zitat
Merkwürdig ist auch, dass bei einem "push ax" anstatt dem "push eax" ein triple fault ausgelöst wird. Ist das so normal?
Ja. Weil ein 'push ax' pushed tatsächlich nur 2 byte auf den Stuck, und du räumst mit dem 'ret 4' 4 wieder ab, machst also irgendwas kaputt.
Titel: Re: Kleiner Assembler Thread
Beitrag von: ChristianF am 14. August 2009, 18:54
Mmmhhh...
Stimmt. C macht das ja automatisch...
 
Danke für die Hilfe. Und noch ein kleines, wahrscheinlich wie vorher auch ein schnell behebbares, Problem. Und zwar versuche ich den blinkenden Cursor an die entsprechende Stelle zu schieben, also hinter das letzte Zeichen. Das Problem ist, dass der Cursor einfach verschwindet... Mein Versuch sieht wie folgt aus:
mov eax, [video_cursor_y]
mov ebx, 80
mul ebx
add eax, [video_cursor_x]
mov ebx, eax

mov dx, 0x3D4
mov al, 0xE
out dx, al

mov dx, 0x3D5
mov al, bl
out dx, al

mov dx, 0x3D4
mov al, 0xF
out dx, al

mov dx, 0x3D5
mov al, bh
out dx, al
Natürlich werden wie bei jeder Funktion, die verwendeten Register gesichert. Das sind eax, ebx und edx...
Muss ich hier auch die Position, die ich berechne (y * 80 + x) mit 2 multiplizieren, oder kann man das hier weglassen?

EDIT:
Soeben hat sich das Problem aufgelöst. Der Code wurde nur leicht verändert:
; ....
mov dx, 0x3D5
mov ax, bx
shr ax, 8
out dx, ax
; ...
mov dx, 0x3D5
mov ax, bx
out dx, ax
; ...
Titel: Re: Kleiner Assembler Thread
Beitrag von: ChristianF am 17. August 2009, 12:37
So...
ich habe meine Funktion zur Ausgabe eines einzelnen Zeichens etwas verfeinert und bin dabei auf einen komischen Fehler gestoßen. In kernel_main wird der selbe Text 2 mal hintereinander und dann noch ein % ausgegeben. (Zumindest sollte das passieren...):
;....
; print out some text
push dword string ; parameter 1 - string to print out
call video_write_string ; call function

push dword string ; parameter 1 - string to print out
call video_write_string ; call function

mov al, 0x25
push eax
call video_write_char
;....
string db "Hello World, From AsmOS",0x0A,0x0D,0

Allerdings wird der String in "string" nur beim ersten Aufruf von video_write_string ausgegeben. Sobald ich die globale Variable video_cursor_y verändere, wird nichts mehr ausgegeben. Hier die aktuelle Version der Funktion "video_write_char", die von "video_write_string" verwendet wird:
video_write_char:
push ebp
mov ebp, esp
; save later used registers
push eax
push ebx
push edi

; Calculate position for edi
mov eax, dword [video_cursor_y]
mov ebx, 80
mul ebx
add eax, dword [video_cursor_x]
lea edi, [0xb8000 + eax * 2]

; Read parameter and print out character
mov eax, FUNCTION_PARAMETER_1 ; <-- Character
mov ah, 0x0F ; <-- black background white fontcolor

cmp al, 0x20 ; <-- normal characters
jae .normal
cmp al, 0x0D ; <-- Carriage return
je .cr
cmp al, 0x0A ; <-- line feed
je .nl
jmp .cont

.cr:
mov dword [video_cursor_x], 0
jmp .cont

.nl:
; sobald dieser abschnitt ausgeführt wird, wird kein Text, oder der Text aus irgendeinem Grund schwarz auf schwarz ausgegeben.
;Jedenfalls sieht man nichts mehr.
inc dword [video_cursor_y]
jmp .cont

.normal:
stosw
; Set new cursor positions
inc dword [video_cursor_x]
jmp .cont

.cont:

mov ebx, 80
mov eax, dword [video_cursor_x]
cmp eax, ebx
jae .nl

; Scroll screen if needed ...
call video_scroll
; ... and move cursor
call video_move_cursor

; restore register
pop edi
pop ebx
pop eax

mov esp, ebp
pop ebp
ret 4
Die übergabe geschieht immer mittels "push eax", deswegen auch das ret 4. Das komische dabei ist, dass alle 3 Ausgaben angezeigt werden, sobald ich die Behandlung des Zeilenumbruchs ausklammere.
Des Weiteren kann ich eine Überschreibung der Ausgaben ausschließen, da man sonst zumindest das '%'-Zeichen sehen müsste. Was habe ich in dem Abschnitt zur Behandlung des Zeilenumbruchs falsch gemacht? Greife ich auf die Variable an sich falsch zu?
Titel: Re: Kleiner Assembler Thread
Beitrag von: SHyx0rmZ am 17. August 2009, 19:32
Zuerst, nein, ich hab das Problem noch nciht gefunden, du willst aber warscheinlich trotzdem die Endlosschleife  bei .cont entfernen. Wenn eax > 80 springt die Funktion nämlich immer wieder zwischen .nl und .cont hin und her.
Titel: Re: Kleiner Assembler Thread
Beitrag von: ChristianF am 17. August 2009, 20:40
Ich habe zumindest diesen Fehler gefunden.
Es lag wohl daran, dass die Variablen für x und y "words" waren und ich diese beim Schreiben in "double words" konvertiert habe. Momentan kämpfe ich wieder mit der Funktion zum setzen des Cursors, da  der Cursor immer in der zweiten Zeile platziert wird. Ist nun auch behoben.
 
Danke für den Hinweis mit dem newline und den >80 Zeichen, das war mir bisher nicht aufgefallen und ist nun behoben.
 
Wenn ich eine "beliebig" lange Zahl ausgeben möchte, sagen wir mal 250, dann muss ich diese ja Zahl aufbrechen und dann bei jedem Zahlenteil die '0' hinzu addieren. Erst danach kann man den ganzen Krempel per String oder eben einzeln Zeichen für Zeichen ausgeben...