Assembleur - Ecrire un OS en Mode Réel - 16 bits

Affichage d'un message dans le Bootloader via le BIOS

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