%define BASE 0x100 %define KSIZE 1 ; nombre de secteurs de 512 octets a charger [BITS 16] [ORG 0x0] jmp start %include "UTIL.INC" start: mov [bootdrv],dl ; recuparation de l'unite de boot ; initialisation des segments en 0x07C0 mov ax,0x07C0 mov ds,ax mov es,ax mov ax,0x8000 ; stack en 0xFFFF mov ss,ax mov sp, 0xf000 ; affiche un msg mov si,msgDebut call afficher ; charger le noyau xor ax,ax int 0x13 push es mov ax,BASE mov es,ax mov bx,0 mov ah,2 mov al,KSIZE mov ch,0 mov cl,2 mov dh,0 mov dl,[bootdrv] int 0x13 pop es ; saut vers le kernel jmp dword BASE:0 msgDebut db "Chargement du kernel",13,10,0 bootdrv: db 0 ;; NOP jusqu'a 510 times 510-($-$$) db 144 dw 0xAA55 |
Le programme commence par un saut a l'adresse 'start'. Une directive
'include' ajoute au code du noyau le contenu du fichier 'UTIL.INC'. Ce fichier
contient le code de la fonction 'afficher' vu precedement :
jmp start
%include "UTIL.INC"
start:
Ensuite, le programme stocke dans une variable un nombre servant a
identifier le peripherique de boot (ici le lecteur de disquettes). Cette
variable sera reutilisee plus tard pour indiquer a partir de quel peripherique
doit etre charge le noyau.
mov [bootdrv],dl ; recuparation de l'unite de boot
Le noyau se situe au debut du second secteur de la disquette. Pour le
charger en memoire, on utilise l'interruption 0x13 du BIOS.
Cette fonction est expliquee en detail sur le site
"The Art of Assembly" . Elle permet de copier un ou plusieurs secteurs d'une disquette
en memoire. Dans le cas present, on recopie le deuxieme secteur de
la disquette, qui contient le noyau, a l'adresse 0x1000 en RAM.
La variable KSIZE defini le nombre de secteurs a charger pour que tout le
noyau soit bien recopie en memoire. Le premier noyau, qui est decrit
dans la partie suivante, est tres court. Sur ma machine, le binaire
correspondant fait 69 octets. On peut donc se contenter de mettre la
valeur de KSIZE a 1 pour copier un seul secteur.
; charger le noyau
xor ax,ax
int 0x13
push es
mov ax,BASE
mov es,ax
mov bx,0
mov ah,2
mov al,KSIZE
mov ch,0
mov cl,2
mov dh,0
mov dl,[bootdrv]
int 0x13
pop es
Ensuite, une instruction de saut execute le code du noyau. La macro 'BASE'
est definie de facon a ce que le pointeur d'instructions pointe sur l'adresse 0x1000 :
; saut vers le kernel
jmp dword BASE:0
[BITS 16] [ORG 0x0] jmp start %include "UTIL.INC" start: ; initialisation des segments en 0x100 mov ax,0x100 mov ds,ax mov es,ax mov ax,0x8000 ; stack en 0xFFFF mov ss,ax mov sp, 0xf000 ; affiche un msg mov si,msg00 call afficher end: jmp end msg00: db 'Kernel is speaking !',10,0 |
Ce programme initialise les registres de code et de donnees afin
qu'ils pointent sur la bonne zone memoire (0x1000). Ensuite, un message est
affiche pour attester de la reussite des operations. A la ligne
suivante, le noyau ne fait vraiment pas grand chose : il boucle indefiniment.
$ ls UTIL.INC bootsect.asm kernel.asm |
On compile les differents programmes :
$ nasm -f bin -o bootsect bootsect.asm $ nasm -f bin -o kernel kernel.asm $ ls -l total 14 -rw-r--r-- 1 am users 492 Jan 17 17:20 UTIL.INC -rw-r--r-- 1 am users 512 Jan 19 18:16 bootsect -rw-r--r-- 1 am users 715 Jan 17 17:22 bootsect.asm -rw-r--r-- 1 am users 297 Jan 17 17:50 kernel.asm -rw-r--r-- 1 am users 69 Jan 19 18:16 kernel |
On remarque que le binaire du secteur de boot fait bien 512 octets. Le
binaire obtenu pour le noyau fait seulement 69 octets.
La disquette que nous allons faire aura le noyau debutant au deuxieme
secteur. On realise une image de la disquette avec la commande
suivante :
$ cat bootsect kernel /dev/zero | dd of=floppy bs=512 count=2880 $ ls -l floppy -rw-r--r-- 1 am users 1474117 Jan 19 18:27 floppy |
Une fois la disquette realisee, on peut la tester avec bochs et on obtient l'ecran suivant :