Après l'élaboration d'un bootloader minimal, nous avons ce petit code au final dont l'éxécution se résumait à démarrer et s'arrêter dans une boucle infinie jmp halt.
org 0x7C00
use16
main:
hlt
halt:
jmp halt
times 510-($-$$) db 0
dw 0AA55h
Dans ce chapitre nous allons continuer ce bootloader afin qu'il affiche un message sur l'écran en utilisant des fonctionnalités du BIOS.
La première chose à faire est de paramêtrer notre programme de tel sorte que les registres du microprocesseur soient configurés et initilisés de façon cohérente car nous ignorons dans quel état sera notre programme au premier démarrage.
Pour ce faire nous allons essentiellement initialiser des registres du processeur à 0. pour placer une valeur dans un registrer on utilise l'instruction MOV. Et la première instruction que l'on va utiliser est MOV AX,0. Ce qui place la valeur 0 dans le registre AX. Ensuite nous allons copier la valeur d'AX (cad 0 maintenant) dans les autres registres du CPU.
...
main:
; Initialization of CPU registers.
mov ax,0 ; AX = 0
mov ds,ax ; DS value of AX = 0 | DS register essentially keep track of the start adress of the data segment.
mov es,ax ; ES value of AX = 0 | ES register set the start point of the extra segment.
mov ss,ax ; SS value of AX = 0 | SS register set the start adress of the stack.
hlt
halt:
jmp halt
...
La dernière chose à initialiser ensuite, est le pointeur de pile SP (Stack Pointer) que nous devons y placer l'adresse du départ de l'application 0x7C00.
...
mov ss,ax
mov sp,0x7c00 ; Set up stack to grow down from 0x0000:0x07c00 in region below the bootloader
hlt
...
Nous faisons cela car la pile augmente à la baisse. Cette manipulation démarre la pile juste après le code de l'application ce qui lui permet de continuer à grandir à partir de ce point. Ca nous donne ainsi plus d'espace pour augmenter la pile du processeur et permet de travailler de manière beaucoup plus efficace avec elle.
Maintenant nous allons créer la fonction (label) print juste à la suite de l'arrêt du programme halt:.
La fonction print servira à afficher un caractère sur l'écran.
...
halt:
jmp halt
print:
...
Cette fonction va utiliser certains registres; donc la première chose à faire est de sauvegarder l'état de ces registres. Nous allons travailler avec les registre SI AX et BX , nous allons les empiler sur la pile CPU afin de concerver leurs valeurs pendant l'éxécution de l'application. Pour ce faire nous utiliserons l'instruction PUSH.
...
halt:
jmp halt
print:
push si
push ax
push bx
...
Ensuite nous allons créer une boucle print_loop qui chargera les lettres d'une chaine de caractère donnée au préalable. Et affichera ces caractères sur l'écran un a un.
...
push ax
push bx
print_loop:
...
Donc pour ce faire nous allons créer une chaine de caractères ici après le label os_boot_message, dans notre code. Cette chaine nous allons la placer vers la fin de notre programme juste avant la signature.
...
print_loop:
os_boot_message: db 'KitsuneOS has booted!', 0x0D, 0x0A, 0
times 510-($-$$) db 0
dw 0AA55h
...
La chaine est suivie par les valeurs 0x0D et 0x0A. Ce sont les indications d'une nouvelle ligne (retour chariot). Aussi nous indiquons à la chaine de se terminer par 0 Ce sera un repère pour notre programme afin qu'il termine bien la lecture de la chaine de caractères.
Ensuite nous allons placer l'adresse mémoire de cette chaine de caractères dans le registre SI. Et appeller la fonction print. Nous intégrons ça vers la fin de la fonction principale du programme, main.
...
mov ss,ax
mov sp,0x7c00
mov si, os_boot_message
call print
hlt
...
Maintenant revenons à notre boucle print_loop; nous allons y placer quelques instructions afin que cette boucle charge et afficher sur l'écran le message donné caractère par caractère.
Pour ce faire, la première chose à faire est d'utiliser l'instruction LODSB. Cette instruction va charger un seul octet à partir de l'emplacement actuel spécifié dans SI
...
print_loop:
lodsb
os_boot_message: db 'KitsuneOS has booted!', 0x0D, 0x0A, 0
...
Donc LODSB charge un seul caractère et le place dans le registre AL du microprocesseur.
Ce qu'on va faire ensuite, c'est vérifier si AL contient la valeur 0. Ceci indiquerai une fin de chaine de caractères. Dans ce cas, on doit interrompre la fonction d'impression de la chaine sur l'écran. La manière la plus simple de faire ça est de passer par l'instruction OR AL, AL. Si l'indicateur donnée par cette instruction indique 0, ça veut dire que AL vaut 0.
...
print_loop:
lodsb
or al,al
os_boot_message: db 'KitsuneOS has booted!', 0x0D, 0x0A, 0
...
Ensuite nous créons une condition qui permettra de sortir de la boucle print_loop et de sauter vers la fin du programme si al est équivalent à 0 via l'instruction JZ.
...
print_loop:
lodsb
or al,al
jz done_print
done_print:
pop bx
pop ax
pop si
ret
os_boot_message: db 'KitsuneOS has booted!', 0x0D, 0x0A, 0
...
Dans la fonction done_print, nous remettons ici les registres dans l'état dans lequel ils étaient avant de lancer notre boucle print_loop. Et on place l'instruction RET afin de retourner vers la fonction main juste après l'instruction d'appelle de la fonction print.
Ce que nous allons faire ensuite est d'appeller une interruption du BIOS qui permettra d'afficher un caractère à l'écran après le JZ si le registre AL n'est pas égale à zéro.
...
print_loop:
lodsb
or al,al
jz done_print
mov ah,0x0E ; 0x0E is a value that represant printing a caractère to the screen.
mov bh,0 ; 0 represente here the page number as an argument.
int 0x10 ; The interrupt is a video interrupt.
jmp print_loop
done_print:
...
INT 0x10 est une l'interruption vidéo. Lors de l'éxécution de cette interruption, le BIOS va vérfier la valeur de AH afin de savoir quoi faire. Dans ce cas-ci, AH à une valeur de 0x0E, ce qui indiquera au BIOS d'imprimer un caractère (contenu dans AL) sur l'écran.
En dernier, on place l'instruction JMP(Jump) afin de sauter vers le début de notre boucle print_loop.
Au final voici le bootloader complet avec la fonction print incluse:
bootloader.asm
org 0x7C00
use16
main:
; Initialization of CPU registers.
mov ax,0 ; AX = 0
mov ds,ax ; DS value of AX = 0 | DS register essentially keep track of the start adress of the data segment.
mov es,ax ; ES value of AX = 0 | ES register set the start point of the extra segment.
mov ss,ax ; SS value of AX = 0 | SS register set the start adress of the stack.
mov ss,ax
mov sp,0x7c00 ; Set up stack to grow down from 0x0000:0x07c00 in region below the bootloader
mov si, os_boot_message
call print
hlt
halt:
jmp halt
print:
push si
push ax
push bx
print_loop:
lodsb
or al,al
jz done_print
mov ah,0x0E ; 0x0E is a value that represant printing a caractère to the screen.
mov bh,0 ; 0 represente here the page number as an argument.
int 0x10 ; The interrupt is a video interrupt.
jmp print_loop
done_print:
pop bx
pop ax
pop si
ret
os_boot_message: db 'KitsuneOS has booted!', 0x0D, 0x0A, 0
times 510-($-$$) db 0
dw 0AA55h
Retour vers la page d'accueil