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!