Autor Thema: Bootfähiges Programm friert ein, wenn Enter gedrückt wird  (Gelesen 11530 mal)

misterunknown

  • Beiträge: 3
    • Profil anzeigen
Gespeichert
Moin,

um etwas Assembler zu lernen habe ich mir (mit einigen Anleitungen für ganz simple Bootloader) ein kleines bootfähiges Programm geschrieben, welches 3 Sachen macht:
  • einen Text ausgeben
  • in einer Schleife alle Tastatureingaben ausgeben
  • wenn ESC gedrückt wird noch einen Text ausgeben und beenden
Das funktioniert auch, bis auf dass die Enter-Taste nur einen Carriage Return ausgibt und keinen Zeilenumbruch macht. Das wollte ich ändern, indem ich prüfe, ob die Enter-Taste gedrückt wurde und wenn ja, erst der Zeilenumbruch ausgegeben wird (0x0A) und dann im Anschluss der Carriage Return (0x0D). Hier ein Ausschnitt:
Linebreak:
    mov ah,0x0E     ; Print-Funktion nutzen (0x0e)
    mov al,0x0A     ; Zeilenumbruch ausgeben
    int 0x10        ; Interrupt auslösen
    ret

Main:
    mov ah,00h      ; auf Tastendruck warten
    int 0x16        ; Interrupt auslösen
    cmp al,0x1B     ; ESC gedrückt?
    je  Ende        ;   => zum Ende springen
    push ax         ; ax sichern (al = 0x0D)
    cmp al,0x0D     ; Enter gedrückt?
    je  Linebreak   ;   => Zeilenumbruch (0x0A) ausgeben
    pop ax          ; ax wiederherstellen (damit sollte al wieder 0x0D sein)
    mov ah,0x0E     ; Print-Funktion einstellen
    int 0x10        ; Interrupt auslösen
    jmp Main        ; Main-Schleife

Das funktioniert nur so lange, bis man Enter drückt. Ist das der Fall wird nur der Zeilenumbruch ausgegeben (0x0A), und dann friert das Programm ein. Ich habe nach einigem hin- und herprobieren keine Idee mehr, was ich falsch mache. Ich habe auch keine Idee, wie ich das debuggen könnte, und hoffe jetzt, dass ihr mir helfen könnt.

Noch einige Informationen zu meinen Tools: Ich nutze zum Kompilieren nasm und zum Testen qemu:
$ nasm -f bin boot.asm -o boot.img
$ qemu-system-x86_64 -fda boot.img

Hier ist der vollständige Code:

;****************************************
; boot.asm - Einfacher Bootloader
;
; 2016 - marco@misterunknown.de
;****************************************

BITS 16             ; 16-Bit Real-Mode

org     0x7C00      ; Wir werden vom BIOS an diese Adresse geladen.


Start:  jmp Loader  ; Loader

msga    db  "Testausgabe",10,13,0
msgb    db  13,10,13,10,"Ende",13,10,0

;**************************************
; Funktionen
;**************************************

Print:
    lodsb           ; lädt das nächste Byte von SI nach AL
    or  al,al       ; ist AL null?
    jz  PrintDone   ; wenn ja, dann gehe zu PrintDone
    mov ah,0x0E     ; Print-Funktion nutzen (0x0e)
    int 0x10        ; Interrupt auslösen
    jmp Print       ; Rücksprungmarke aufrufen

PrintDone:
    ret

Linebreak:
    mov ah,0x0E     ; Print-Funktion nutzen (0x0e)
    mov al,0x0A     ; Zeilenumbruch ausgeben
    int 0x10        ; Interrupt auslösen
    ret

;**************************************
; Loader
;**************************************

Loader:
    xor ax,ax       ; schneller Weg um das Register zu nullen
    mov al,03h      ; in den Text-Modus wechseln (löscht gleichzeitig den Bildschirm)
    int 0x10        ; Interrupt auslösen

    mov si,msga     ; Nachricht in source-Register laden
    call Print      ; Print-Funktion aufrufen

;**************************************
; Hauptschleife
;**************************************

Main:
    mov ah,00h      ; auf Tastendruck warten
    int 0x16        ; Interrupt auslösen
    cmp al,0x1B     ; ESC gedrückt?
    je  Ende        ;   => zum Ende springen
    push ax         ; ax sichern (al = 0x0D)
    cmp al,0x0D     ; Enter gedrückt?
    je  Linebreak   ;   => Zeilenumbruch (0x0A) ausgeben
    pop ax          ; ax wiederherstellen (damit sollte al wieder 0x0D sein)
    mov ah,0x0E     ; Print-Funktion einstellen
    int 0x10        ; Interrupt auslösen
    jmp Main        ; Main-Schleife

Ende:
    mov si,msgb
    call Print
    cli             ; Interrupts leeren
    hlt             ; System anhalten

times 510 - ($ - $$) db 0   ; Das Programm muss 512 Bytes groß werden. Dazu wird mit Nullen aufgefüllt.
                            ; 510 kommt daher, weil hinten noch die 2 Byte lange Signatur folgt.
dw  0xAA55          ; Signatur des Bootloaders

; vim: syn=nasm
« Letzte Änderung: 15. March 2016, 21:54 von misterunknown »

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 15. March 2016, 16:08 »
Auf den ersten Blick: Wenn du mit jmp (bzw. je) nach LineBreak springst, dann kommst du mit ret nicht zurück. Was passiert ist, dass er dein gepushtes 0xd vom Stack nimmt und als Rücksprungadresse benutzt.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

misterunknown

  • Beiträge: 3
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 15. March 2016, 16:33 »
Auf den ersten Blick: Wenn du mit jmp (bzw. je) nach LineBreak springst, dann kommst du mit ret nicht zurück. Was passiert ist, dass er dein gepushtes 0xd vom Stack nimmt und als Rücksprungadresse benutzt.
Das ergibt natürlich Sinn -.- Offensichtlich ist "call" und "jmp" in meinem Kopf das gleiche gewesen.

Ich habe mir jetzt soetwas gebaut:
Linebreak:
    mov ah,0x0E     ; Print-Funktion nutzen (0x0e)
    mov al,0x0A     ; Zeilenumbruch ausgeben
    int 0x10        ; Interrupt auslösen
    mov al,0x0D     ; Zeilenvorschub ausgeben
    int 0x10        ; Interrupt auslösen
    jmp Main

Main:
    mov ah,00h      ; auf Tastendruck warten
    int 0x16        ; Interrupt auslösen
    cmp al,0x1B     ; ESC gedrückt?
    je  Ende        ;   => zum Ende springen
    cmp al,0x0D     ; Enter gedrückt?
    je  Linebreak   ;   => Zeilenumbruch (0x0A) ausgeben
    mov ah,0x0E     ; Print-Funktion einstellen
    int 0x10        ; Interrupt auslösen
    jmp Main        ; Main-Schleife
Und es funktioniert! Vielen Dank  8-)

Sollte dir oder jemand anderem beim anschauen meines Programms sonst noch auf etwas koschmisches/idiotisches aufgefallen sein bin ich für Tipps dankbar :)

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 15. March 2016, 19:49 »
Es ist Real-Mode-Assembler-Code. Reicht das nicht, um als komisch und idiotisch durchzugehen? :-D
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

misterunknown

  • Beiträge: 3
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 15. March 2016, 21:54 »
Es ist Real-Mode-Assembler-Code. Reicht das nicht, um als komisch und idiotisch durchzugehen? :-D
Vermutlich, aber für die ersten Schritte in einer virtuellen Maschine sollte das reichen^^ Mein Ziel ist es nicht ein Betriebssystem oder einen vollwertigen Bootloader zu schreiben, sondern mein grundsätzliches Verständnis für das Zusammenspiel von Software und Hardware auszubauen.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 16. March 2016, 03:15 »
Mein Ziel ist es nicht ein Betriebssystem oder einen vollwertigen Bootloader zu schreiben, sondern mein grundsätzliches Verständnis für das Zusammenspiel von Software und Hardware auszubauen.
Ich persönlich finde dafür Mikrocontroller eine wesentlich bessere Ausgangsbasis. Also zum Beispiel ein Arduino (ohne die Arduino-Tools, also klassisch mit make und gcc), ein STM32-Discovery, ein LPC-Launchpad oder sowas in der Art. Wenn du die paar Euros übrig hast, kriegst du das günstig im Internet. Da musst du dich zwar auch einarbeiten, aber du bekommst eine relativ einfache Architektur und vor allem eine Plattform, die aus einem Guss und an einer einzigen Stelle vollständig dokumentiert ist. Und du brauchst dich nicht mit beschränkten Kompatiblitätsmodi (Real Mode, A20) rumschlagen, sondern kannst den Kram ausreizen.

Im Gegensatz zum PC kann man solche Boards auch für Echtzeitanwendungen benutzen, dafür hast du weniger schicke Hardware (wenig Flash, wenig RAM, kein VGA-Display, etc). Aber für das Verständnis sind die Dinger prima.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 16. March 2016, 10:26 »
Vermutlich Geschmackssache und auch eine Frage davon, ob man wissen will wie x86 funktioniert oder einfach nur irgendeine Architektur. Ich mag klassisches OS-Dev auf dem PC.

Mein Ziel ist es nicht ein Betriebssystem oder einen vollwertigen Bootloader zu schreiben, sondern mein grundsätzliches Verständnis für das Zusammenspiel von Software und Hardware auszubauen.
Du musst dir halt bewusst sein, dass du noch gar direkt auf Hardware zugegriffen hast. Du bist im Moment dabei, was über das Zusammenspiel von DOS-Programmen und dem BIOS zu lernen. Das ist sicherlich historisch interessant, aber heute nicht mehr ganz so praxisrelevant. ;)

Man kann natürlich auch im Real Mode tatsächlich die Hardware ansteuern statt BIOS-Interrupts zu benutzen, aber in dem Fall ist der RM dann eher hinderlich als nützlich, weil er so viele Einschränkungen (vor allem im Hinblick auf die Speichergröße) mit sich bringt. Deswegen empfehlen wir eigentlich immer, GRUB (oder was anderes Multiboot-fähiges) als Bootloader zu benutzen und direkt im PM zu starten.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

 

Einloggen