Spiele in den 512 Bytes des Boot-Sektors unterzubringen ist keine Neuheit, daher wollte ich auch mal etwas kleines probieren. Wie mit meinem letzten Projekt, bei dem ich hier um Rat gebeten habe, versuche ich etwas mehr über die Funktionsweise von Rechnern und x86-Assembly zu erfahren. Aktuell ist es erstmal mein Ziel, ein Rechteck mit Pfeiltasten über den Bildschirm zu bewegen.
Gesagt, getan. Das Programm läuft in QEMU super, Eingabe und Anzeige funktionieren wie gewollt. Jetzt das Problem: Es läuft nur auf QEMU (und Bochs). Sobald ich das Programm von einem USB-Stick auf meinem Rechner starte, macht es gar nichts mehr (Bildschirm bleibt leer, Cursor blinkt). Dabei ist mir ein möglicher Zusammenhang aufgefallen (bitte schlagt mich nicht, es folgen gefährliches Halbwissen und wahrscheinlich haufenweise falsche Annahmen): Es gibt Ausnahmen. Ich hatte viel Zeit das Programm an vielen Computern meiner Schule zu testen. Dabei ist mir aufgefallen, dass besonders Computer, die neuer waren, das Programm nicht richtig ausgeführt haben. Ich hatte die Überlegung, dass es möglicherweise am vom UEFI simulierten BIOS liegen könnte. Auf Rechnern ohne UEFI funktioniert es tatsächlich (zumindest gehe ich davon aus, dass die jeweiligen Rechner kein UEFI hatten, ich bin mir unsicher, wie genau ich das herausfinde). Soweit ich das beurteilen kann, würde ich außerdem eine Inkompatibilität aufgrund der Prozessorarchitektur ausschließen, da die meisten Computer auf x86/x64 laufen und sonst wahrscheinlich gar nichts passiert wäre (es gab Phasen, in denen Teile des Programmes auch auf den UEFI-Rechnern funktioniert haben, daher schließe ich das Problem mit den Architekturen aus).
Aktuell hat das Programm nette 118 Zeilen (NASM):
K_LEFT          equ 75
K_RIGHT         equ 77
K_UP            equ 72
K_DOWN          equ 80
MEM_VIDEO       equ 0xb800
SCREEN_WIDTH    equ 80
SCREEN_HEIGHT   equ 25
TICK_LENGTH     equ 4
org 0x7C00
bits 16
init:
    mov     ax, 0x0002      ; 16 farben, 80x25 Zeichen
    int     0x10
    ; -- Stack initialisieren
    xor     ax, ax
    mov     ds, ax
    mov     ss, ax
    mov     sp, 0x9c00
    mov     ax, MEM_VIDEO   ; zeiger auf bildschirmspeicher
    mov     es, ax          ; ES=MEM_VIDEO
    mov     al, 0x03
    int     0x10
    mov     ah, 1
    mov     ch, 0x26
    int     0x10
main:
    ; -- Bildschirm löschen und neu zeichnen
    call    clear_screen
    call    draw
    ; -- Eingabe und zurück zum Anfang springen
    call    input
    jmp     main
input:     ; steuerung
    xor     ah, ah
    int     0x16            ; tasteneingabe
    cmp     ah, K_RIGHT     ; Pfeiltaste nach Rechts
    je     .right_arrow
    cmp     ah, K_LEFT      ; Pfeiltaste nach Links
    je     .left_arrow
    cmp     ah, K_DOWN      ; Pfeiltaste nach Unten
    je     .down_arrow
    cmp     ah, K_UP        ; Pfeiltaste nach Oben
    je      .up_arrow
    jmp     .end
    .right_arrow:
        cmp     word [x_pos], word SCREEN_WIDTH-2
        jg      .end                    ; x_pos > SCREEN_WIDTH-2, keine veränderänderung
        add 	word [x_pos], 1
        jmp     .end
    .left_arrow:
        cmp     word [x_pos], word 1
        jl      .end                    ; x_pos < 1, keine veränderung
        sub     word [x_pos], 1
        jmp 	.end
    .down_arrow:
        cmp     word [y_pos], word SCREEN_HEIGHT-2
        jg      .end                    ; y_pos > SCREEN_HEIGHT-2, keine veränderung
        add     word [y_pos], 1
        jmp 	.end
    .up_arrow:
        cmp     word [y_pos], word 1
        jl      .end                    ; y_pos < 1, keine veränderung
        sub     word [y_pos], 1
        jmp 	.end
    .end:
        ret
clear_screen:
    ; -- Ganzen Bildschirm mit Schwarz füllen
    xor     di, di
    mov     cx, 0x07d0
    mov     ax, 0x0000
    rep     stosw
    ret
draw: ; Block an der Stelle aus [x_pos] und [y_pos] zeichnen
    ; -- Umwandeln der Koordinaten in Index für den Bildschirmspeicher, Ergebnis in dx
    mov     ax, word [y_pos]
    mov     bx, 160
    mul     bx
    mov     dx, ax
    mov     ax, word [x_pos]
    add     dx, ax
    add     dx, ax
    ; -- Datenindex setzen und zeichnen
    mov     di, dx
    mov     ax, 0xa020
    stosw
    ret
done:
    jmp     $           ; Endlosschleife am Ende (was eigentlich nie erreicht wird)
x_pos: times 10 dw 0
y_pos: times 10 dw 0
times 510-($-$$) db 0
dw 0xaa55           	; Signatur für's BIOS
Meine Frage dazu wäre dann wahrscheinlich klar: Warum läuft das Programm nicht? Gibt es Unterschiede am vom UEFI simulierten BIOS? Liegt es vielleicht an etwas völlig Anderem? Ich bin für jede Hilfe und Erklärung dankbar!