主引导扇区代码(MBR)

来源:互联网 发布:地暖暖气片空调知乎 编辑:程序博客网 时间:2024/05/22 10:34

;====================================================================
;
; FlyingDragon MBR ( Master Boot Record )
;
; Author : Jack
; History:
; 0.01 - 2005-08-13 19:34 采用传统的CHS磁盘调用
; 0.02 - 2005-08-23 19:44 优先采用扩展磁盘调用
;
; Build  : nasm -f bin MBR.ASM -oMBR.BIN 
;  
;====================================================================================
;
; MBR( Master Boot Record )主引导记录包含两部分的内容,前446字节为启动代码及数据,而
; 从446(0x1BE)开始则是分区表,分区表由四个分区项组成,每个分区项数据为16字节,记录了
; 启动时需要的分区参数。
;
; 在CPU上电之后,若由硬盘启动,则BIOS将硬盘的主引导记录(位于0柱面、0磁道、1扇区)读
; 入7C00处,然后将控制权交给主引导代码。主引导代码的任务包括:
; (1) 扫描分区表,找到一个激活(可引导)分区;
; (2) 找到激活分区的起始扇区;
; (3) 将激活分区的引导扇区装载到内存7C00处;
; (4) 将控制权交给引导扇区代码;
;  
; 如果主引导代码无法完成上述任务,它将显示以下错误信息之一:
; No active partition.
; Invalid partition table.
; Error loading operating system.
; Missing operating system.
;
;====================================================================================
; FAT16分区尺寸与LBA
;====================================================================================
; LBA   HeadsPerCylinder SectorsPerTrack  Maximum Size for Boot Partition
; Disabled 64     32     1GB
; Enabled  255     63     4GB
;
; 为了适应超过8G的硬盘,Windows2000忽略了Start CHS和End CHS,而使用StartLBA和TotalSector    
; 来确定分区在整个磁盘中的位置和大小。
;  
;====================================================================================
;     分区表项结构(16字节)
;====================================================================================
;
; typedef struct _PARTITION_ENTRY
; {
;  UCHAR BootIndicator;  // 能否启动标志
;  UCHAR StartHead;   // 该分区起始磁头号
;  UCHAR StartSector;  // 起始柱面号高2位:6位起始扇区号
;  UCHAR StartCylinder;  // 起始柱面号低8位
;  UCHAR PartitionType;  // 分区类型
;  UCHAR EndHead;   // 该分区终止磁头号
;  UCHAR EndSector;   // 终止柱面号高2位:6位终止扇区号
;  UCHAR EndCylinder;  // 终止柱面号低8位
;  ULONG StartLBA;   // 起始扇区号
;  ULONG TotalSector;  // 分区尺寸(总扇区数)
; }PARTITION_ENTRY,*PPARTITION_ENTRY;
;
;====================================================================================
;    主引导记录(MBR)结构
;====================================================================================
; typedef struct _MASTER_BOOT_RECORD
; {
;  UCHAR    BootCode[446];
;  PARTITION_ENTRY  Partition[4];
;  USHORT    Signature;
; }MASTER_BOOT_RECORD,*PMASTER_BOOT_RECORD;
;
;====================================================================================

BITS   16   ; 生成16位代码而不是32位代码
SECTION  .text   ; 代码段
ORG   0600H  ; 指定程序被装入内存的起始位置

;====================================================================
;
; 宏和常量定义
;
;====================================================================
?     EQU  0  ; NASM不支持DW ?这样的语法,可以使用这样的定义
        ; 模拟,以使代码的可读性更强
ACTIVE_FLAG  EQU  80H  ; 激活(可引导)分区标志
NOT_ACTIVE_FLAG EQU  00H  ; 不激活标志
MBR_MOVE_ADDR EQU  0600H ; MBR先移动自身到该位置然后再运行
BOOT_SIGNATURE EQU  0AA55H ; 启动标志
SEC_SIG_OFF  EQU  01FEH ; 启动扇区的标志位置

;====================================================================
; 分区表项结构偏移
;====================================================================
BootIndicator EQU  0  ; 能否启动标志
StartHead  EQU  1  ; 该分区起始磁头号
StartSector  EQU  2  ; 起始柱面号高2位:6位起始扇区号
StartCylinder EQU  3  ; 起始柱面号低8位
PartitionType EQU  4  ; 分区类型
EndHead   EQU  5  ; 该分区终止磁头号
EndSector  EQU  6  ; 终止柱面号高2位:6位终止扇区号
EndCylinder  EQU  7  ; 终止柱面号低8位
StartLBA  EQU  8  ; 起始扇区号
TotalSector  EQU  12  ; 分区尺寸(总扇区数)

;====================================================================
; 常用的分区类型
;====================================================================
PARTITION_TYPE_EMPTY  EQU  00H ; 空分区
PARTITION_TYPE_FAT12  EQU  01H ; FAT12 ( < 32680 sectors )
PARTITION_TYPE_FAT16  EQU  04H ; FAT16 ( 32680 - 65535 sectors )
PARTITION_TYPE_EXTENDED  EQU  05H ; DOS EXTENDED
PARTITION_TYPE_BIGDOS_FAT16 EQU  06H ; FAT16 ( 33MB - 4GB )
PARTITION_TYPE_NTFS   EQU  07H ; NTFS
PARTITION_TYPE_FAT32  EQU  0BH ; FAT32
PARTITION_TYPE_FAT32_LBA EQU  0CH ; FAT32 LBA
PARTITION_TYPE_FAT16_LBA EQU  0EH ; FAT16 LBA
PARTITION_TYPE_EXTENDED_LBA EQU  0FH ; LBA EXTENDED
PARTITION_TYPE_DYNAMIC_DISK EQU  42H ; Dynamic Disk Volume


;====================================================================
; 主引导记录的入口
;====================================================================
_ENTRY_POINT:
 
 ; 初始化相关寄存器及标志位
 CLI      ; 先关掉中断
 CLD      ; 方向为向前递增
 XOR  AX,AX   ; AX = 0
 MOV  DS,AX   ; 设置数据段寄存器 DS:SI
 MOV  ES,AX   ; 设置附加段寄存器 ES:DI
 MOV  SS,AX   ; 设置堆栈段寄存器
 MOV  BP,7C00H  ; 设置基址寄存器
 MOV  SP,BP   ; 设置堆栈栈顶
 
 ; 将MBR代码移动到0600H处
 MOV  SI,BP   ; SI = 7C00H
 MOV  DI,MBR_MOVE_ADDR; DI = 0600H
 MOV  CX,512   ; 待移动的字节数
 REP  MOVSB 
 JMP  0:.RealStart

; 真正开始   
.RealStart:

 ; 保存引导驱动器号
 MOV  BYTE [ DriveNumber ] , DL
 
 ;====================================================================  
 ; 检查是否支持磁盘中断INT 13H的扩展
 ;====================================================================
 MOV  AH,41H
 MOV  BX,055AAH
 INT  13H
 JC  .LookupActive   ; 如果失败,进位标志为1
 MOV  BYTE[DiskExtension],01H ; 设置支持磁盘扩展标志

.LookupActive:
 
 ; 查找激活分区
 MOV  BP,PartitionTable   ; 指向分区表
 MOV  BL,4      ; 最多4个分区

;检查激活分区 
.CheckNext: 

 CMP  BYTE [BP+BootIndicator],ACTIVE_FLAG  ; 检查该分区是否激活
 JZ  .FoundActive       ; 找到激活分区
 CMP  BYTE [BP+BootIndicator],NOT_ACTIVE_FLAG ; 检查该分区是否激活 
 JNZ  .InvalidTable       ; 无效值
 ADD  BP,10H         ; 指向下一个分区表项
 DEC  BL
 JZ  .NoActive        ; 没有找到激活分区
 JMP  .CheckNext

;找到了激活分区
.FoundActive:

 ; 保存分区表项
 MOV  AL,4
 SUB  AL,BL      ; AL = 4-BL = 第一个激活分区表项索引(0-3)
 MOV  BYTE [ActivePartition],AL ; 保存激活分区表项索引
 MOV  DI,BP      ; DI = 激活分区项
 MOV  DH,BYTE[BP+StartHead]  ; 该分区起始磁头号
 MOV  CL,BYTE[BP+StartSector]  ; 起始柱面号高2位:6位起始扇区号
 MOV  CH,BYTE[BP+StartCylinder] ; 起始柱面号低8位
 
 ; 保存起始扇区号
 MOV  AX,WORD[BP+StartLBA+2]
 MOV  WORD[DAP_SECTOR_LOW+2],AX 
 MOV  AX,WORD[BP+StartLBA]  
 MOV  WORD[DAP_SECTOR_LOW],AX 
 
; 检查确信只有一个激活分区 
.Recheck:     
 ADD  BP,10H      ; 指向下一个分区项
 DEC  BL
 JZ  .LoadBootSector    ; 装载该分区的引导扇区
 CMP  BYTE [BP+BootIndicator],NOT_ACTIVE_FLAG ; 检查该分区是否激活 
 JNZ  .InvalidTable    ; 无效值
 JMP  .Recheck

; 装载激活分区的引导扇区 
.LoadBootSector:
 
 ; 设置驱动器号
 MOV  DL,BYTE [DriveNumber]
 
 ; 检查是否支持扩展磁盘调用
 CMP  BYTE [DiskExtension],01H
 JNZ  .NoDiskExtension
 
 ; 使用扩展磁盘调用读取引导扇区
 ; 
 ; INT 13H 
 ;  AH = 42H
 ;  DL = Drive Number
 ;  DS:SI = 指向磁盘地址包的指针
 ;
 MOV  SI,DAP_PACKET_SIZE
 MOV  AH,42H
 INT  13H
 JC  .ErrorLoadOS
 JMP  .CheckBootSector

.NoDiskExtension: 
 ;====================================================================
 ; 
 ; INT 13H 
 ;  AH = 2        柱面号:0 - 1023
 ;  AL = 要读取的扇区数     磁头号:0 - 255   
 ;  CH = 柱面号低8位     扇区号:1 - 63 
 ;  CL = 柱面号高2位 : 6位扇区号  
 ;  DH = 磁头号
 ;  DL = 驱动器号
 ;  ES:BX = 缓冲区
 ;
 ;====================================================================

 ; 读取引导扇区 
 MOV  BX,7C00H
 MOV  AX,0201H
 INT  13H
 JC  .ErrorLoadOS
 
; 检查引导扇区是否合法 
.CheckBootSector: 
 
 ; 装载引导扇区成功,检查引导标志
 MOV  BX,7C00H
 CMP  WORD [BX+SEC_SIG_OFF], BOOT_SIGNATURE  ; 检查引导标志
 JNZ  .MissingOS     
 
 ; 准备跳转到激活扇区的引导扇区代码
 ; DL = 磁盘驱动器号
 ; DH = 激活分区号
 ; DI = 激活分区项
 MOV  DL,BYTE [DriveNumber]
 MOV  DH,BYTE [ActivePartition]
 JMP  0:7C00H

; 没有激活分区
.NoActive: 
 MOV  SI,MsgNoActive
 CALL ShowMessage
 JMP  .Hang
 
; 无效分区表
.InvalidTable:
 MOV  SI,MsgPartitionTable
 CALL ShowMessage
 JMP  .Hang
 
; 装载引导扇区失败
.ErrorLoadOS:
 MOV  SI,MsgLoadingOS
 CALL ShowMessage
 JMP  .Hang
 
; 引导扇区不合法
.MissingOS:
 MOV  SI,MsgMissingOS
 CALL ShowMessage

.Hang:
 JMP  .Hang

 
;====================================================================
;
; 显示一个字符串
; 输入:
;   DS:SI  = 字符串的起始地址(以NULL结束)
;   
;====================================================================
ShowMessage:
 LODSB    ; AL = DS:[SI] SI = SI+1
 OR  AL,AL    ; 检测是否遇到NULL字符串
 JZ  .ShowEnd
 MOV  AH,0EH
 MOV  BX,07H
 INT  10H
 JMP  ShowMessage

.ShowEnd:
 RET 
 
 
;====================================================================
; 调试例程
;====================================================================

%IFDEF DEBUG 
 
;====================================================================
;
; 显示一个字符
; 输入: AL = 待显示字符
;
;====================================================================
PrintChar:
 PUSH AX
 PUSH BX
 MOV  AH,0EH
 MOV  BX,7
 INT  10H
 POP  BX
 POP  AX
 RET
 

 
;====================================================================
;
; 显示16进制的值(将一个BYTE变为两个ASCII字符打印出来,用于调试)
; 输入: AL = 待显示的字节
;
;====================================================================
PrintByte:
 PUSH BX
 MOV  BH,AL
 
 ; 显示高4位
 SHR  AL,4
 AND  AL,0FH
 ADD  AL,30H
 CMP  AL,39H
 JLE  .PrintIt
 ADD  AL,07H
.PrintIt: 
 CALL PrintChar

 ; 显示低4位 
 MOV  AL,BH
 AND  AL,0FH
 ADD  AL,30H
 CMP  AL,39H
 JLE  .PrintItAgain
 ADD  AL,07H
.PrintItAgain 
 CALL PrintChar
 POP  BX
 RET

%ENDIF ; DEBUG

;====================================================================
; 数据区
;====================================================================
MsgNoActive     DB  "No active partition.",00H
MsgPartitionTable   DB "Invalid partition table.",00H   
MsgLoadingOS    DB "Error loading operating system.",00H
MsgMissingOS    DB "Missing operating system.",00H

;====================================================================
;临时数据
;====================================================================
DriveNumber     DB 00H ; 启动磁盘启动器号
ActivePartition    DB 00H ; 激活分区表索引(0-3)
DiskExtension    DB 00H ; 是否支持磁盘扩展调用

;====================================================================  
; 扩展磁盘服务所使用的地址包
;====================================================================
DAP_PACKET_SIZE    DB 10H  ; 包的大小为16字节
DAP_RESERVED1    DB 00H  ; 保留字节
DAP_READ_SECTORS   DB 01H  ; 要处理的扇区数(1 - 127 )
DAP_RESERVED2    DB 00H  ; 保留字节
DAP_BUFFER_OFF    DW 7C00H ; 缓冲区偏移
DAP_BUFFER_SEG    DW 0000H ; 缓冲区段地址
DAP_SECTOR_LOW    DD 0000H ; 起始扇区号的低32位
DAP_SECTOR_HIGH    DD 0000H ; 起始扇区号的高32位

;====================================================================
; 填充字节
Padding TIMES 440-($-$$) db  00H
;====================================================================

;====================================================================
; 标志字节
;====================================================================
UniqueMbrSignature   DD 4B43414AH
UnknownWord     DW 00H

;====================================================================
; 分区表(偏移为446)
;====================================================================
PartitionTable TIMES 64  DB 00H

;====================================================================
; 扇区最后的标记字节(NASM不支持重复ORG)
;====================================================================
BootSignature    dw 0AA55H


;====================================================================
; 代码结束
;====================================================================