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
;====================================================================
; 代码结束
;====================================================================