; BOOT.ASM : par Cyberchrist
; =========================
[ORG 0x7C00] ;le secteur d'amorçage est automatiquement chargé en 0000:7C00 au démarrage
[BITS 16] ;on se limite à des instructions du mode réel
jmp debut ;on saute la déclaration pour arriver directement au code
;Architecture Disquette 1.44M FAT12
;==================================
;0----------------------------DEBUT
; Secteur d'amorce
; (1 secteur)
;1---------------------------------
; FAT #1
; (9 secteurs)
;10--------------------------------
; FAT #2 (copie)
; (9 secteurs)
;19--------------------------------
; Root Directory
; (14 secteurs)
;33--------------------------------
; DATA
; (2847 secteurs)
;2880---------------------------FIN
; boot record : NE PAS MODIFIER afin d'avoir une FAT valide
; =========================
_OEM_NAME__________ db 'Chris ' ;8bytes
_bytes_per_sector__ dw 512
_sect_per_cluster__ db 1
_reserved_sectors__ dw 1 ;combien de secteurs avant le début de la 1ere FAT, secteur d'amorce compris
_number_of_FATs____ db 2
_number_of_entries_ dw 224
_nb_total_sectors__ dw 2880
_media_descriptor__ db 0xF0
_sectors_per_FAT___ dw 9
_sector_per_track__ dw 18
_number_of_heads___ dw 2
_nb_hidden_sectors_ dw 0
dw 0
_nb_part_sectors___ dw 0
dw 0
;optional:
_drive_number______ db 0
_reserved1_________ db 0
_signature_________ db 29h
_serial_number_____ dw 0
dw 0
_volume_label______ db 'OmniScience' ;11bytes
_filesystem_type___ db 'FAT12 ' ;8bytes
;Architecture de la Mémoire RAM
;==================================
;0----------------------------DEBUT
; ???
;0000:7C00-------------------------
; Secteur d'amorce
;0000:7E00-------------------------
; PILE
;0000:8000-------------------------
;0000:8010-------------------------
; Filebuffer
;0000:8100-------------------------
; RDbuffer
;1000:0000-------------------------
; OMLOAD.OS
;1000:FFFF----------------------FIN
; Synonymes
; ========================
fichier db 'OMLOAD OS ' ;le format du nom ici est en 8x3
lectamorce EQU 0 ;lecteur 0 = disquette
LOAD_ADDR EQU 0x1000 ;segment début du code chargé
RDbuffer dw 0x8100 ;buffer d'un secteur de la 'Root Directory' ( 512 octets ), 1 secteur = 16 entrées
filebuffer dw 0x8010 ;buffer structure d'un fichier ( 32 octets ) (une entrée)
; Déclaration des messages
; ========================
;bonjour db "Bonjour !",13,0 ;on termine par zéro pour indiquer la fin de chaîne
;afficher la valeur 13 = retour à la ligne
;db = un caractère est codé sur un octet
MSG_pix db '.',0
MSG_R db 'R',10,13,0
MSG_ok db 'Read',10,13,0
MSG_notfound db 'Err',10,13,0
; Début du code
; ===================
debut:
cli ;inhibe les interruptions
xor AX,AX ;ax:= 0000h
mov SS,AX ;segment de pile
mov SP,0x8000 ;pointeur de pile
;la queue de pile est à 0000:8000
sti ;réactive les interruptions
mov ds, ax ;segment de donnée
mov es, ax ;segment auxiliaire de donnée
mov si, _OEM_NAME__________
call P_print
load:
mov si, fichier ;affiche le nom du fichier
call P_print
mov ax, 19 ;premier secteur du 'root directory'
lo1:
push ax ;on sauvegarde ax (compteur de boucle principale)
mov bx, RDbuffer ;le buffer dans lequel on va lire les secteurs du RD
mov si, MSG_ok
call P_print
Call P_readsector ;lecture d'un secteur
mov cx, 16 ;16 entrées dans chaque buffer de 512 octets
mov dl, 1 ;1ere entrée du RD
lo2:
push cx ;on sauvegare cx (car il servira de compteur de boucle)
mov di, filebuffer ;buffer qui contiendra une entrée de la fat (32 octets) (chaque entrée contient le nom du fichier, les attributs, la taille ...etc)
mov bx, RDbuffer ;le buffer qui contient l'entrée à lire
mov si, MSG_notfound
call P_print
;==============CA MERDE : prochain affichage affiche n'importe quoi =========================
call P_buf2file ;on lit l'entrée
mov si, MSG_ok
call P_print
mov bx, filebuffer ;
call P_decompfile ;extraction des données de cette entrée (nom du fichier, debutfich, taille...)
mov si, filename ;filename est une chaine qui contient le nom du dernier ficher extrait de la fat
mov di, fichier ;le nom de notre kernel
mov cx, 11 ;8 char pour le nom 3 pour l'extention = 11
repe cmpsb ;comparaison des deux chaines
jz Found ;Le nom correspond ............... On a trouvé le kernel !!!
;sinon on n'a pas trouvé
next1:
inc dl ;entrée suivante de la fat
pop cx ;on recupère la valeur de notre compteur
loop lo2 ;puis on relance la recherche du kernel
pop ax ;on recupère la valeur de notre compteur principal
cmp ax, 32 ;toute la FAT a-t-elle été lue ?
jge NotFound ;oui ===> le kernel n'est pas là dedans :-(
inc ax ;il y a encore des entrées dans la fat ?
jmp lo1 ;oui , on passe à la suivante
Found: ;le kernel a été trouvé
pop cx ;d'abord, on libère la pile
pop ax ;
mov ax, 1 ;on va lire à partir de 1er secteur, càd la FAT #1
call P_sl2p ;secteur logique en physique
mov bx, RDbuffer ;destination de la copie démarre en ES:BX
mov ah, 02 ;Fonction n° 2 de l'INT 13h
mov al, 9 ;nombre de secteurs à copier
mov dl, lectamorce ;lecteur 0
int 13h ;on lance l'interruption!
;=========================on a toute la FAT #1 dans RDbuffer=======================
mov bx, 0 ;
push bx ;on empile 0
mov ax, LOAD_ADDR ;
mov es, ax ;segment auxiliaire LOAD_ADDR
lo: ;chargement du kernel
mov si, MSG_pix
Call P_print ;à chaque tour de boucle on affiche un "." de plus
mov ax, debutfich ;premier secteur qu'on commence à lire? (numéro logique)
pop bx ;récupère BX, on commence à BX = 0 et à chaque tour de boucle on ajoute 512
;add ax, 31 ;numéro logique du secteur
call P_readsector
add bx, 512 ;on se déplace dans le buffer
push bx ;sauvegarde BX
mov bx, RDbuffer ;buffer source dans lequel la fonction va lire
mov ax, debutfich ;numéro du cluster courant
call P_getnextcluster ;retourne le numéro du cluster suivant
mov [debutfich], ax ;qu'on sauvegarde
cmp ax, 0x0FF8 ;fin du fichier
jb lo ;si plus petit
flo:
mov si, MSG_ok ;le kernel est chargé
Call P_print
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KERNEL chargé en mémoire à LOAD_ADDR:0000
finish:
mov ax, word 0x1000 ;on sauvegarde l'adresse(segment) de chargement du kernel
jmp 0x1000:0 ;lancement du kernel...
NotFound: ;très mauvais, on ne va pas pouvoir lancer le kernel, on abandonne tout et reboot
mov si, MSG_notfound ;affichage d'un message d'erreur
call P_print
mov ah, 0 ;Fonction n° 0 de l'INT 16h : attente d'une touche
int 16h ;on lance l'interruption!
int 19h ;reboot
;*****************************************;
;* affichage d'une chaine ASCIIZ *;
;*****************************************;
;* ENTREE : *;
;* SI := adresse de la chaine à afficher *;
;*****************************************;
;* SORTIE : AUCUNE *;
;*****************************************;
P_print:
push ax ;on sauvegarde ax pour manipulation
push bx ;on sauvegarde bx pour manipulation
cld ;mise à zero du drapeau de direction
.next:
lodsb ;charge DS:SI dans AL
or al, al ;teste si AL:= 0
jz .end
mov ah, 0Eh ;fonction n° 0Eh de l'INT 10h : afficher un caractère à l'écran
mov bx, 0007h ;BH= couleur 07 BL= Page vidéo 00
int 10h
jmp .next ;tant que AL != 0 (caractère de fin de chaîne)
.end:
pop bx ;restoration bx
pop ax ;restoration ax
ret
;*********************************************;
;* Convertion du secteur logique en physique *;
;*********************************************;
;* ENTREE : *;
;* AX := numéro logique du secteur (0..2879) *;
;*********************************************;
;* SORTIE : *;
;* cl : numéro du secteur (1..18) *;
;* ch : numéro de la piste (0..79) *;
;* dh : numéro de la face (0 ou 1) *;
;*********************************************;
P_sl2p:
push ax
push bx
push ax
mov ax, _sector_per_track__
mov bx, _number_of_heads___
mul bx ;ax:= 18*2 = temp1
mov cx , ax ;cx = temp1
pop ax ;le paramètre
xor dx,dx ;préparation à la division
div cx
mov ch , al ;cylindre (ou piste)
;dx = temp2
mov ax, dx
xor dx, dx ;préparation à la division
mov bx, _sector_per_track__
div bx
mov cl, dl
inc cl ;secteur
mov dh , al ;tête
pop bx
pop ax
ret
;*********************************************;
;* lecture d'un secteur *;
;*********************************************;
;* ENTREE : *;
;* AX := numéro logique du secteur (0..2879) *;
;* BX := le buffer qui recevra les données *;
;*********************************************;
;* SORTIE : AUCUNE *;
;*********************************************;
P_readsector:
push ax
push dx
mov si, MSG_R
call P_print
call P_sl2p ;convertion secteur logique en physique
mov ah, 02h ;fonction de lecture
mov al, 01h ;d'un secteur
mov dl, 0
int 13h
pop dx
pop ax
ret
;**********************************************;
;* Extraction d'une entrée du répertoire *;
;* principal depuis un buffer *;
;**********************************************;
;* ENTREE : *;
;* BX : buffer source 512 octets *;
;* DL : numéro de l'entrée (1..16) *;
;* DI : buffer cible 32 octets *;
;* *;
;**********************************************;
;* SORTIE : AUCUNE *;
;**********************************************;
P_buf2file:
push ax
push bx
push cx
push si
mov al, 32 ;on calcule le rang de l'entrée qu'on veut
mul dl ;extraire
sub ax, 32 ;
add bx, ax ;on ajoute le rang au buffer
mov si, bx ;puis on effectue notre copie ;-)
mov cx, 15 ;chaque entrée contient 32 octets
.getfileb:
movsw ;copie 2 bytes à la fois (w=word)
loop .getfileb
.fin:
pop si
pop cx
pop bx
pop ax
ret
;**********************************************;
;* décomposition des entrées du rep principal *;
;**********************************************;
;* ENTREE : *;
;* BX : le buffer qui contient l'entrée à *;
;* décomposer (32 octets) *;
;* di : le buffer du nom de fichier *;
;* (destination) *;
;**********************************************;
;* SORTIE : *;
;* filename : nom du fichier *;
;* debutfich: contient le 1er cluster du *;
;* fichier *;
;**********************************************;
P_decompfile:
push ax
push bx
push cx
mov si, BX
mov di, filename
mov cx, 11
.getfilename:
lodsb
cmp cx, 11
jne .stock
cmp al, 31 ;si c'est pas > 31 c'est pas un ASCII readable
jg .stock
mov al, 0
stosb
jmp .nofile
.stock:
stosb
loop .getfilename
add bx, 26 ;voir la structure d'une entrée du "Root Directory"
mov si, bx
mov di, debutfich ;on lit le premier secteur du fichier
mov cx, 1 ;lecture de 2 octets
rep movsw ;2 octets pour le premier cluster
mov di, taillefich ;on lit la taille du fichier
mov cx, 2
rep movsw
.nofile:
pop cx
pop bx
pop ax
ret
;**********************************************;
;* lecture du n° du prochain cluster dans ax *;
;**********************************************;
;* ENTREE : *;
;* BX : buffer source 512 octets *;
;* AX : numéro du cluster courant [0..2879] *;
;* *;
;**********************************************;
;* SORTIE : *;
;* AX : numéro du cluster suivant *;
;**********************************************;
;* REMARQUE : pour connaître le secteur *;
;* correspondant à un cluster la formule est *;
;* numsecteur = 33 + numcluster - 2 *;
;* *;
;* > 33 est le numéro du 1er secteur dans une *;
;* FAT12 *;
;* > 2 est le nombre d'éléments de 12 bits *;
;* utilisés pour distinguer une FAT12. *;
;* la table FAT12 commence par : *;
;* F0F FFF *;
;**********************************************;
P_getnextcluster:
push bx ;on sauvegarde le buffer 2 fois
push bx ;car on doit le récupérer 2 fois
add ax, 1 ;
mov bx, 12 ;
mul bx ;ax := ax*12
mov bx, 8 ;
div bx ;ax := ax DIV bx
;dx := ax MOD bx
pop bx ;on récupère le buffer
add bx, ax ;on ajoute le nombre de déplacements nécessaires
cmp dx, 4 ;si le reste de ax/8 = 4
je .R4
.R0:
sub bx, 2 ;si le reste = 0 on ajuste bx
mov ax, word [bx] ;on lit le buffer à la position bx
sar ax, 4 ;puis on décale ax de 4 à droite
and ax, 0000111111111111b ;MASQUE des 4 premiers bits
jmp .endr ;car on a besoin de 12bits seulement
.R4:
sub bx, 1 ;si le reste = 4 on ajuste bx
mov ax, word [bx] ;on lit
and ax, 0000111111111111b ;puis on MASQUE
.endr:
pop bx ;on récupère notre buffer initial
ret
; On se décale à l'octet 510
; ============================
times 510 - ($-$$) DB 0 ;syntaxe NASM pour se déplacer à 510 en remplissant de 0...
; Signature du secteur d'amorçage
; ======================
dw 0AA55h
[SEGMENT .bss] ;déclaration des variables non-initialisées
filename resb 11 ;buffer de 11 bytes
debutfich resw 1 ;premier secteur du fichier à charger
taillefich resw 2 ;taille du fichier à charger