** Sorry for my English **
Code: Select all
;--------------------------------------------------------------------------------------------
; But : Ce fichier contient un secteur d'amorcage ( Disquette 1.44 )
; qui utilise un système de fichier FAT12
;
;
; Attention :
; -Ce secteur d'amorcage est écrit avec la syntaxe de NASM ( version 0.99.06 ).
; -L'osLoader doit être la première entrée dans le répertoire racine
;--------------------------------------------------------------------------------------------
;[=====================================================================================]
;
; Directive spécial pour NASM
;
;[=====================================================================================]
; Lorsque l'ordinateur démarre, leprocesseur est en mode réel 16 bits. Alors on indique
; à NASM que ce programme doit être assemblé en 16 bits.
[BITS 16]
; Le secteur d'amorcage est chargé en mémoire par le BIOS ou un Master Boot Record (MBR)
; standare à l'adresse physique 0x7C00. Alors, il faut indiquer à NASM que le programme
; ce déroule à cette adresse pour qu'il ajuste les offset des instructions et des
; données en conséquence. C'est à dire, qu'il va ajoute à l'offset de chaque instructions
; ou données la valeur 0x7C00.
[ORG 0x7C00]
start:
;[=====================================================================================]
;
; L'entête du système de fichier FAT12
; Cette entête est divisé en plusieurs sections
;
; 1. Le saut (JMP)
; 2. OEM ID
; 3. Le block de paramètre du BIOS ( BIOS parameter block ( BPB))
; 4. Le block de paramètre du BIOS étendu ( extended BIOS parameter block ( EBPB))
;
;[=====================================================================================]
;-----------------------------------------------------
; Section 1 : Le saut (JMP)
;-----------------------------------------------------
; Offset 0 - 2
;
; Saute par dessus l'entête du système de fichier FAT12
; pour se rendre directement au code exécutable sans
; provoquer d'erreur.
JMP bootStrapCode
;-----------------------------------------------------
; Section 2 : OEM ID
;-----------------------------------------------------
; Offset 3 - 10
; Le nom du système qui à formater le disque
EOM_Name DB 'B.O.S OS'
;-------------------------------------------------------------------------------------
; Section 3 : Le block de paramètre du BIOS ( BIOS parameter block ( BPB))
;-------------------------------------------------------------------------------------
; Offset 11 - 12
; Le nombre d'octets par secteur
;
; Les valeur autorisé sont 512, 1024, 2048 et 4096
; Pour un meilleur compatibilité avec de vieu
; système, utilisé 512
NB_OCTET_PAR_SECTEUR DW 512
; Offset 13
; Le nombre de secteur par clusteur
;
; Doit être un multiplie de 2, excepter 0
; Les valeurs supportées sont : 1, 2, 4, 8, 16, 32, 64, 128
;
; Pour le FAT12
;
; Drive size Secs/clusteur Clusteur size
; 360 KB 2 1 Ko
; 720 KB 2 1 Ko
; 1.2 MB 1 512 octets
; 1.44 MB 1 512 octets
; 2.88 MB 2 1 Ko
;
; ATTENTION, si BYTE_PER_SECRTOR * SECTOR_PER_CLUSTER (nombre d'octet par cluster) est
; plus grand que 32 KB, des erreurs de disque et de logiciel
; peuvent survenir.
NB_SECTEUR_PAR_CLUSTEUR DB 1
; Offset 14 - 15
; Le nombre de secteur réservé dans la section réservé
; à partir du premier secteur.
; (incluant le secteur d'amorcage)
;
; Pour FAT12 cette valeur doit toujours être 1.
;
NB_SECTEUR_RESERVE DW 1
; Offset 16
; Le nombre de FATs sur le disque.
;
; Cette valeur est toujours 2 peut
; importe le type de FAT pour garder une compatibilité.
; Cependant, les système Microsoft suporte d'autre valeur.
NB_FAT DB 2
; Offset 17 - 18
; Le nombre maximal de dossier/fichier qui peuvent
; être stocker à la racine du volume.
; Les entrées sont sur 32 octets
;
; Cette valeur multiplier par 32 doit être un multiple de BYTE_PER_SECRTOR
;
; Pour une plus grande compatibilité, une disquette (FAT12) utilise 224 entrées.
;
; Si un nom de dossier/fichier utilise plus de 32 bits, cette entrée
; utilisera plusieur entrée ce qui diminue le nombre maximal d'entrée disponible.
;
; ATTENTION : Sous Windows, une entrée est utilisé pour le nom du volume.
NB_ENTREE_REPERTOIR_RACINE DW 224
; Offset 19 - 20
; La taille de la disquette en secteur (tous les secteurs)
;
NB_TOTAL_SECTEUR16 DW 2880
; Offset 21
; Spécifie le type de volume utilisé.
;
; Les valeur autorisé sont :
; 0xF0, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
; Peut impote la valeur, elle doit se retrouver dans
; dans l'octet de poid faible de les FAT[0]
;
; F0 : 2,88 Mo 3,5 pouces, 2 face, 36 secteurs
; F0 : 1,44 Mo 3,5 pouces, 2 face, 18 secteurs
; F9 : 720 Ko 3,5 pouces, 2 face, 9 secteurs
; F9 : 1,2 Mo 5,25 pouces, 2 face, 15 secteurs
; FD : 360 Ko 5,25 pouces, 2 face, 9 secteurs
; FF : 320 Ko 5,25 pouces, 2 face, 8 secteurs
; FC : 180 Ko 5,25 pouces, 1 face, 9 secteurs
; FE : 160 Ko 5,25 pouces, 1 face, 8 secteurs
; F8 : ------- Disque dur
;
; ATTENTION : Un descripteur de média peut être associé
; à plusieurs média.
NO_MEDIA DB 0xF0
; Offset 22 - 23
; Le nombre de secteur occupé par chaque des FATs sur le volume
;
; Avec cette données, le nombre de FATs (NUMBER_OF_FATs) et le nombre
; de secteur réservé (RESERVED_SECTORS), on peut calculer où commence le
; répertoire racine du volume (SECTOR_PER_FAT * NUMBER_OF_FATs + RESERVED_SECTORS).
;
; Par la suite, on peut calculer où commence la zone de données du volume
; avec le nombre d'entrée du répertoire racine (ROOT_ENTRIES_TAB_TAB).
;
; ( Q * ( T - R - D + 2A ) )
; INT -------------------------------------
; ( Q * N + ( A * S ) ) + .5
;
; INT= nombre entier
; Q= 1,5 pour des FAT 12 bits et 2,0 pour des FATS 16 bits
; T=nombre total de secteurs du disque
; R=nombre de secteurs réservés
; D=nombre de secteurs du répertoire principal
; A=nombre de secteurs par cluster
; N=nombre de FAT
; S=nombre d'octets par secteur
NB_SECTEUR_PAR_FAT DW 9
; Offset 24 - 25
; Le nombre de secteur
;
; Fait partie de la géométrie du disque et
; est utilisé lors avec INT 0x13
NB_SECTEUR_PAR_CYLINDRE DW 18
; Offset 26 - 27
; Le nombre de tête
;
; Fait partie de la géométrie du disque et
; est utilisé lors avec INT 0x13
NB_TETE DW 2
; Offset 28 - 31
; Le nombre de secteur sur le disque physique
; qui se situe avant ce secteur de d'amorcage ( pour les partitions )
NB_SECTEUR_CACHE DD 0
; Offset 32 - 35
; Contient le nombre de secteur du disque/partition (tous les secteurs du disque) s'il
; est trop grand pour être stocker dans TOTAL_SECTOR16.
; Le champ TOTAL_SECTOR16 doit être à 0. Si ce n'est pas le cas, ce champ doit
; être à 0.
NB_TOTAL_SECTEUR32 DD 0
;--------------------------------------------------------------------------------------
; Section 4 : Le block de paramètre du BIOS étendu
; ( extended BIOS parameter block( EBPB))
;--------------------------------------------------------------------------------------
; Offset 36
; Ce nombre dépend du numéro de lecteur physique du BIOS.
;
; Les lecteurs de disquette sont numéroté à partir de 0x0
; et les disques dur commence à 0x80.
;
; Est normalement définit avant un apeller BIOS INT 13 pour spécifier
; quel périphérique il faut accèder.
;
; La valeur enregistré sur le disque est généralement 0x0 ou 0x80.
;
; Cette valeur est importante seulement si
; le volume est un périphérique d'amorcage.
PHYSICAL_DRIVE DB 0
; Offset 37
; Il s'agit d'un autre champ généralement utilisé lors des appels BIOS INT 13.
; À l'origine, sa valeur indiquait sur quel piste si trouvait l'enregistrement de
; démarrage mais, cette valeur n'est plus utilisé comme tel.
;
; Window NT active le premier bit de poid faible pour indiquer que autochk doit
; être exécuté sur le volume au prochain démarrage.
; Window NT active le deuxième bit de poid faible pour indiquer qu'un examen de
; la surface doit être exécuter sur le volume au prochain démarrage.
;
; Ce champ peut donc être à 0 au formatage.
RESERVED DB 0
; Offset 38
; Ce champ doit contenir 0x28 ou 0x29 pour être reconnu par window NT
; 0x29 indique que les trois prochian champs sont présent
SIGNATURE DB 0x29
; Offset 39 - 42
; Un nombre qui permet de différencier des autres volumes.
; Généralement la date combiner avec le temps
NO_SERIE DD 0
; Offset 43 - 53
; Ce champ contient le nom du volume
;
; Window ne l'utilise plus. Il place le nom du volume dans une entrée
; du répertoire racine.
;
; Linux lui, semble l'utiliser encore.
;
NOM_VOLUME DB 'B.O.S OS '
; Offset 54 - 61
; Ce champ doit contenir soit FAT12 ou FAT16, selon
; le système de fichier utilisé.
; Ce champ ne doit pas être utilisé pour déterminer
; le système de fichier utilisé.
TYPE_SYSTEME_FICHIER DB 'FAT12 '
;[======================================================================================]
; Définitions de quelques variables pour le préprocesseur de NASM
;[======================================================================================]
; Adresse où sera sauvegardé le
; numéro du phériphérique d'amorcage ( 1 Octets )
%define BOOT_DEVICE_ADR bootStrapCode
; Adresse où sera sauvegardé les 2 octets de
; poid faible de l'adresse LBA du premier
; secteur de la zone de données ( 2 Octets )
%define LBA_FISRT_DATA_SECTEUR_LOW BOOT_DEVICE_ADR + 1
; Adresse où sera sauvegardé les 2 octets de
; poid fort de l'adresse LBA du premier
; secteur de la zone de données ( 2 Octets )
%define LBA_FISRT_DATA_SECTEUR_HIGH LBA_FISRT_DATA_SECTEUR_LOW + 2
; Adresse de l'adresse LBA de la premiere FATs
%define FIRST_FATS_ADR_LOW ( LBA_FISRT_DATA_SECTEUR_HIGH + 2 )
%define FIRST_FATS_ADR_HIGH ( FIRST_FATS_ADR_LOW + 2 )
%define OS_LOADER_ADR_PTR FIRST_FATS_ADR_HIGH + 2
; L'adresse où sera chargé les secteur de la ROOT_DIR_TAB et de la FATs
%define FAT_BUFFER_ADR (magicWord + 2)
%define OS_LOADER_SEG 0x900
;[======================================================================================]
;
; Le bootStrap
;
; Ceci est la zone exécutable du secteur d'amorcage. Elle se divise en plusieurs étapes.
;
; 1. Inisialisation
;
;
;
;[======================================================================================]
bootStrapCode:
;[*************************************************************************]
; 1. Inisialisation et sauvegarde d'information
;[*************************************************************************]
CLD ; Désactive le flag de direction
; Quand ce flag est désactivé,
; les opération de chaîne de caractère
; incrémente (E)SI/(E)DI
; Inisialisation des segments et de la pile
CLI ; Désactive les interrupteurs
XOR AX, AX ; AX = 0
MOV DS, AX ; DS = 0
MOV ES, AX ; ES = 0
MOV SS, AX ; SS = 0
MOV SP, start ; Le pointeur de pile se décrémente durant l'exécution
STI ; Active les interrupteurs
; Sauvegarde le numéro de l'unité de stockage sur lequel l'ordinateur à été amorcé et
; que le BIOS ou le MBR à mis dans DL.
MOV BYTE [BOOT_DEVICE_ADR],DL
; Réinisialise le controleur de disque
;
; Entrées :
; AH = 00h
; DL = drive (Si le bit 7 est activé, les
; disquettes et les disque sont affectés)
;
; Sortie :
; AH = status (see #00234)
; CF clear if successful (returned AH=00h)
; CF set on error
INT 0x13 ; AX est déjas à 0 et DL contient déjà
; le numéro du disque à réinisialiser
JNC step1.5
MOV SI, szErrorDisk
CALL bootError
step1.5:
;*******************************************************************************
; Copy le nombre total de secteur dans situé dans le champ NB_TOTAL_SECTEUR16
; dans le champ NB_TOTAL_SECTEUR32 s'il n'est pas à 0
;*******************************************************************************
CMP WORD [NB_TOTAL_SECTEUR16], 0
JZ .noCopyTotalSector16
MOV CX, WORD [NB_TOTAL_SECTEUR16]
MOV WORD [NB_TOTAL_SECTEUR32], CX
.noCopyTotalSector16:
; Affichage du message de chargement
MOV SI, szLoadingSystem
CALL printSZ
calcFirstLBAFATs:
;[**********************************************************************************]
; 2. Calcule l'adresse de la première FATs
;
; Calcule :
; LBA = NB_SECTEUR_RESERVE + NB_SECTEUR_CACHE
;
;[**********************************************************************************]
MOV AX, WORD [NB_SECTEUR_RESERVE]
ADD AX, WORD [NB_SECTEUR_CACHE]
ADC DX, WORD [NB_SECTEUR_CACHE + 2]
; DX:AX -> l'adresse de la première FATs
MOV WORD [FIRST_FATS_ADR_LOW], AX
MOV WORD [FIRST_FATS_ADR_HIGH], DX
;[**********************************************************************************]
; 3. Calculer l'adresse LBA de la du répertoire racine
;
; Calcule :
; LBA = NB_SECTEUR_RESERVE + NB_SECTEUR_CACHE +
; (NB_SECTEUR_PAR_FAT * 2 )
;
;
;
;[**********************************************************************************]
MOV BX, WORD [NB_SECTEUR_PAR_FAT]
SHL BX, 1
ADD AX, BX
ADC DX, 00
; DX:AX -> l'adresse LBA de la du répertoire racine
PUSH DX
PUSH AX
;[**********************************************************************************]
; 4. Lecture du premier secteur du répertoire racine
;
; ** DX:AX conteint déjà l'adresse LBA du répertoire racine ( voir étape 2 et 3)
;
;[**********************************************************************************]
MOV BX, FAT_BUFFER_ADR
MOV CX, 0x0301
CALL readSector
;[**********************************************************************************]
; 5. Recherche l'osLoader dans la première entrée du répertoire racine
;
;
; ** Chaque entrée est sur 32 octets
;[**********************************************************************************]
MOV SI, FAT_BUFFER_ADR
MOV DI, szOsLoaderName
MOV CX, 11
REPZ CMPSB
JZ osLoaderFound
; L'osLoader est manquant
MOV SI, szFileMissing
CALL bootError
osLoaderFound:
MOV DI, WORD [SI + 15]
;[**********************************************************************************]
; 6. Calcule la taile du répertoire racine
;
; Calcule :
; tailleEnSecteur = (NB_ENTREE_REPERTOIR_RACINE * 32 +
; NB_OCTET_PAR_SECTEUR - 1) / NB_OCTET_PAR_SECTEUR
;
;[**********************************************************************************]
XOR DX, DX
MOV AX, WORD [NB_ENTREE_REPERTOIR_RACINE]
SHL AX, 5
MOV BX, WORD [NB_OCTET_PAR_SECTEUR]
ADD AX, BX
DEC AX
DIV BX
; DX:AX -> taile du répertoire racine
;[**********************************************************************************]
; 7. Calculer l'adresse LBS du premier secteur de la zone de donnée
; Calcule :
; LBA = tailleRootDirEnSecteur + LBARootDir
;
;
;[**********************************************************************************]
POP BX
POP DX
ADD AX, BX
ADC DX, 00
MOV WORD [LBA_FISRT_DATA_SECTEUR_LOW], AX
MOV WORD [LBA_FISRT_DATA_SECTEUR_HIGH], DX
MOV WORD [OS_LOADER_ADR_PTR], 0
XOR SI, SI
readFileSector:
;[**********************************************************************************]
; 8. Lire un cluster de l'osLoader
;
; DX:AX -> L'adresse LBA du début de la zone de données
; DI -> Le numéro du prochain cluster à lire ( index dans la FATs )
;
;[**********************************************************************************]
PUSH DI ; Le numéro du clusteur courrant ( index dans la FATs )
PUSH SI ; Le numéro du dernier secteur de la FATs chargé
XOR DX, DX
DEC DI
DEC DI
XCHG AX, DI
MUL BYTE [NB_SECTEUR_PAR_CLUSTEUR]
XCHG AX, DI
MOV AX, WORD [LBA_FISRT_DATA_SECTEUR_LOW]
MOV DX, WORD [LBA_FISRT_DATA_SECTEUR_HIGH]
ADD AX, DI
ADC DX, 00
MOV CH, 03
MOV BX, (OS_LOADER_SEG * 0x10)
ADD BX, WORD [OS_LOADER_ADR_PTR]
MOV CL, BYTE [NB_SECTEUR_PAR_CLUSTEUR]
CALL readSector
; Calcule le nombre d'octet lu
XOR DX, DX
MUL WORD [NB_OCTET_PAR_SECTEUR]
MOV WORD [OS_LOADER_ADR_PTR], AX
; Calcule le numéro de cluster maximal
XOR DX, DX
POP SI ; Le numéro du dernier secteur de la FATs chargé
MOV AX, SI
MUL WORD [NB_OCTET_PAR_SECTEUR]
MOV CX, 3
DIV CX
POP DI ; Le numéro du clusteur courrant ( index dans la FATs )
CMP DI, AX
JB .skipReadFATs
MOV AX, WORD [FIRST_FATS_ADR_LOW]
ADD AX, SI
MOV DX, WORD [FIRST_FATS_ADR_HIGH]
ADC DX, 00
MOV CX, 0x0303
MOV BX, FAT_BUFFER_ADR
CALL readSector
ADD SI, AX
.skipReadFATs:
; Calcule l'offset dans la FATs
MOV AX, DI
MOV BX, 3
MUL BX
SHR AX, 1
MOV BX, AX
MOV CX, WORD [FAT_BUFFER_ADR + BX]
TEST DI, 1
JZ .clusterPaire
SHR CX, 4
.clusterPaire:
AND CX, 0xFFF
MOV DI, CX
CMP DI, 0xFF8
JB readFileSector
;[**********************************************************************************]
; 9. Passe le contrôle à l'osLoader
;
;[**********************************************************************************]
MOV DL, BYTE [BOOT_DEVICE_ADR]
JMP OS_LOADER_SEG:0
;[=====================================================================================]
;
; Les routines
;
;[=====================================================================================]
;****************************************************************
;
; Routine printSZ
;
; BUT : Permet d'afficher à l'écran une chaîne de caractères
; qui ce termine par 0.
;
; Entrée :
; SI : L'adresse de la chaîne à afiche.
; ( Attention : Modifier à la sortie de la routine )
;
; Spécification :
; - Utilise la fonction 0xE de l'interrupteur 0x10 du BIOS
; - La chaîne est afficher en blanc sur un fond noir.
;
;****************************************************************
printSZ:
PUSH AX
PUSH DX
MOV AH, 0x0E ; Fonction de l'interrupteur
.print:
LODSB ; Met [DS:SI] dans AL et incrément SI
OR AL, AL ; Test si AL est égal à 0
JZ .fin
INT 0x10
JMP .print
.fin:
POP DX
POP AX
RET
;****************************************************************
;
; Routine bootError
;
; BUT :
;
; Entrée :
; SI : L'adresse de la chaîne à afiche.
; ( Attention : Modifier à la sortie de la routine )
;
;
;****************************************************************
bootError:
CALL printSZ
;MOV SI, szReset
;CALL printSZ
;XOR AH, AH
;INT 0x16
;MOV WORD [0x472], 1234
;JMP 0xFFFF0
JMP $
RET
;****************************************************************
;
; Routine readSector
;
; BUT : Permet de lire des secteurs d'un disque avec
; une adresse de type LBA
;
;
; Entrées :
; AX : Les 2 octets de poid faible de l'adresse LBA
; DX : Les 2 octets de poid fort de l'adresse LBA
; CL : Le nombre de secteur à lire
; CH : Le nombre de tentative de lecture
; ES:BX : L'adresse du buffer
;
; Sortie :
; AH : Le nombre de secteur lu
; CF activé si une erreur est survenue
;
;****************************************************************
readSector:
PUSH SI
PUSH DI
PUSH CX
;************************************************************************
; Convertie l'adresse LBA en CHS selon le format que le BIOS
; utilise pour les interrupteurs
;
; Formule
; Sector = (LBA/SectorsPerTrack) Remainder value + 1
; Head = (LBA/SectorsPerTrack)/NumHeads (Take Remainder value)
; Cylindre = (LBA/SectorsPerTrack)/NumHeads (Take quotient value)
;
; CH = Les 8 bits de poid faible du numéro de cylindre
; CL = Le numéro du secteur (bits 0-5)
; Les 2 bits de poid fort du numéro de cylindre (bits 6-7,
; disque dure seulement)
; DH = Le numéro de tête
;*************************************************************************
; Si DX ( retenue ) est plus grand ou égal au nombre de
; secteur par cylindre, la division (LBA/SectorsPerTrack)
; va provoqué une retenue alors on perdra de l'information.
CMP DX, WORD [NB_SECTEUR_PAR_CYLINDRE]
JAE .error
; Divise DX:AX par [NB_SECTEUR_PAR_CYLINDRE]
DIV WORD [NB_SECTEUR_PAR_CYLINDRE]
MOV CL, DL
INC CL ; Numéro du secteur
XOR DX, DX
DIV WORD [NB_TETE]
SHL AH, 6
OR CL, AH
MOV CH, AL
SHL DX, 8 ; Numéro de tête
;******************************************************************
; Lire des secteurs
;
; AH = 02h
; AL = Le nombre de secteur à lire
; CH = Les 8 bits de poid faible du numéro de cylindre
; CL = Le numéro du secteur (bits 0-5)
; Les 2 bits de poid fort du numéro de cylindre
; (bits 6-7, disque dure seulement)
; DH = Le numéro de tête
; DL = Le numéro du disque (bit 7 activé pour les disque dur)
; ES:BX -> Le buffer de lecture
;*******************************************************************
XOR SI, SI ; Compteur de tentative
POP AX ; AL -> Le nombre de secteur à lire
; AH -> Nombre maximum de tentative
PUSH AX
SHR AX, 8
MOV DI, AX
POP AX
.retry:
MOV AH, 2
MOV DL, [BOOT_DEVICE_ADR]
INT 0x13
JNC .noError
INC SI
CMP DI, SI
JNZ .retry
.error
CALL bootError
.noError
XOR AH, AH
POP DI
POP SI
RET
;[=====================================================================================]
;
; Les chaines de caractaire
;
;[=====================================================================================]
szLoadingSystem DB 10,13,"Chargement...",10,13,0
szErrorDisk DB "Erreur de disque !",0
szFileMissing DB "BOSLDR manquant !",0
szOsLoaderName DB "BOSLDR ",0
; Permet de s'assurer que le secteur d'amorcage soit bien de 512 octets
TIMES 510 - ( $ - $$ ) DB 0
magicWord: DW 0xAA55 ; 0x55 puis 0xAA