;Win98MBR
.model tiny
.code
org 100h
Start:
; 0000:7C00~0000:7C1A:初始化各个段寄存器、堆栈指针,最后将主引导记录在内存中搬家,腾出其所占内
; 存空间以供装入分区引导记录。
XOR AX,AX ;AX寄存器清0
MOV SS,AX ;SS=0
MOV SP,7C00h ;装填栈指针——SS:SP=0000:7C00
STI ;开中断(装填栈指针时为避免硬件中断引起栈混乱应关中断)
PUSH AX
POP ES ;装填附加数据段寄存器ES=0
PUSH AX
POP DS ;装填数据段寄存器DS=0
CLD ;规定其后的串操作为正向串操作
MOV SI,7C1Bh ;源指针
MOV DI,061Bh ;目的指针
PUSH AX
PUSH DI ;看看0000:7C1A——构造一个跳转
MOV CX,01E5h
REPZ ;
MOVSB ;0000:7C1B起始的CX字节传送至0000:061B起始的区域
RETF ;跳转到0000:061B(这是一种技巧跳转)
;
; 为直观起见,下面的地址按实际运行时的地址给出。
; 0000:061B~0000:062B:对分区表进行初步检验,一旦检测到某分区表项状态字节大于等于80h,就通过(当
; 然,在此之前如果检测到某项分区表的状态字节小于80h,就转错误处理。当然,如果四个分区项的状态字节
; 都为零,主引导记录就会调用BIOS-ROM的INT 18h,显示"PRESS A KEY TO REBOOT"信息等待你的操作。
MOV SI,07BEh ;SI指向第一个分区表项,这时CX=0
MOV CL,04 ;分区表共四个表项
CheckBootFlag:
CMP [SI],CH ;CH=0
JL FoundBoot ;062D 大于等于80h转[注意JL指令:(SF xor OF)=1则转]
JNZ InvalidBootFlag ;063B ;不为0则[SI]一定小于80h,只能转错误处理了!
ADD SI,+10h ;为零则检查下一表项
LOOP CheckBootFlag ;0620 检查下一表项
INT 18h ;四表项的状态字节都为0,则系统只好调用INT 18h了!
; 0000:062D~0000:0639:检查剩余的分区表项——状态字节必须为零,否则显示错误信息“分区表无效”然
; 后当机!拜托,微软搞错没有,怎么用中文提示信息?真TM傻得可爱!
; 这里还有个小BUG,前面放行原则是只要状态字节大于等于80h,那么如果这个字节是诸如A0h、E5h之类数值
; 呢?嘿嘿,这个引导记录统统认为是有效的可引导分区了!
FoundBoot:
MOV DX,[SI] ;为读分区引导记录做准备:磁头号→DH,驱动器号→DL
MOV BP,SI ;SI→BP,保存可引导分区表项的指针
NextPartition:
ADD SI,+10h ;其余的分区表项还要检查检查的
DEC CX
JZ ValidBootFlag ;064D CX=0则检查顺利通过,转继续
CMP [SI],CH
JZ NextPartition ;0631 为零,是合法表项,再查下一表项
; 0000:063B~0000:064B:执行错误处理——报告错误信息后当机
InvalidBootFlag:
MOV SI,offset Invalid+1;0710h 错误信息字符串偏移+1→SI
MessageEnd:
DEC SI ;SI-1→SI
NextChar:
LODSB ;SI+1→SI
CMP AL,00 ;
JZ MessageEnd ;AL=0则表明一条错误信息显示完毕,系统陷入一个死循环
MOV BX,0007 ;字符方式显示
MOV AH,0Eh ;
INT 10h ;以写电传方式显示信息(只显示一个字符)
DeadCircle:
JMP NextChar ;显示下一个字符,直到遇到提示信息结束为止
;
; 0000:064D~0000:0662:判断可引导分区的分区类型,然后转相应处理程序。
ValidBootFlag:
MOV [BP+25h],AX ;BP=指向第一个可引导分区表项的指针,这时AX=0000h
;使用长度最短的指令将[BP+25]起始的两个单元清零
;这两个单元将被用来存放中间变量
XCHG SI,AX ;此时SI清零的最佳指令选择(仅1字节),将服务于0000:06B8
MOV AL,[BP+04] ;取分区类型(本例是“06”喽——FAT16主DOS分区)
MOV AH,06 ;为扩展INT 13h无法使用做好更改分区类型的准备
CMP AL,0Eh ;0Eh:需要用扩展INT 13h访问的FAT16主DOS分区
JZ ExtInt13 ;066B 0Eh类型的分区转066Bh
MOV AH,0Bh
CMP AL,0Ch ;0Ch 需要用扩展INT 13h访问的FAT32分区
JZ ExtInt13FAT32;0665 0Ch类型的分区转0665h先行预处理
CMP AL,AH ;0Bh:用传统INT 13h就可以访问的FAT32分区
JNZ OtherPartitionFlag ;068F 其他类型的分区转068Fh
;
; 0000:0664~0000:06A1:根据分区类型和分区表表项内容进行读取分区引导记录前的处理工作
INC AX ;0Bh类型的分区由此开始处理,此条指令用意是清ZF位
ExtInt13FAT32:
MOV BYTE PTR [BP+25],06 ;★★★0Ch类型的分区由此开始处理
;为什么取值06???
JNZ OtherPartitionFlag ;068F 请注意上面指令对ZF位的影响:0Bh类型分区转,0Ch则不转
; 0000:066B~0000:068C这段代码仅当分区类型是0Ch、0Eh才有获得执行的机会
ExtInt13:
MOV BX,55AAh ;★★★0Eh类型的分区由此开始处理
PUSH AX
MOV AH,41h ;扩展INT 13h功能,检测BIOS是否已经支持扩展INT13h
INT 13h ;入口参数:BX=55AAh,DL=驱动器号,AH=41h
POP AX ;执行完恢复AX为060Eh
JB NotSupportExtInt13 ;068C 不支持则转
CMP BX,0AA55h
JNZ NotSupportExtInt13 ;扩展INT13h不可用也转
TEST CL,01 ;测试扩展盘访问是否被支持
JZ NotSupportExtInt13 ;不支持还转
; 因为扩展INT13h方式读盘与标准INT13h方式读盘有很大差别,所以0000:0686处指令修改其后的代码以保证按
; 照扩展读方式读分区引导扇区时能正确跳转到相应的处理程序中。
MOV AH,AL ;分区类型→AH
MOV [BP+24h],DL ;保存驱动器号→[BP+24]
MOV WORD PTR [UseExtInt13OrNot],1EEBh ;修改0000:06A1处代码为"JMP 06C1"
NotSupportExtInt13:
MOV [BP+04],AH ;注意:如果扩展INT13h不能使用则A改分区类型为06,但如果
;扩展INT13h能使用,则仍保持原分区类型不变
OtherPartitionFlag:
MOV DI,000Ah ;★★★其它类型分区由此开始处理。此条指令初始化计数器
@@1:
MOV AX,0201h ;AH:读操作,AL:读取1个扇区的内容
MOV BX,SP ;SP=7C00→BX,指定分区引导记录装入内存的位置偏移
XOR CX,CX ;CX清零
CMP DI,+05 ;注意5
JG UseExtInt13OrNot;06A1 大于则转去读由分区表指定的分区引导扇区
MOV CX,[BP+25h] ;小于则证明所读分区表指定的引导扇区无合法的引导记录,
;改按???再读,毕竟多一种选择多一次机会嘛!;)
; 以下标有①②者请注意它们的地址都是一样的,就是说实际运行中只可能是二者之一,但为了分析之方便,我
; 把两者都列了出来以供对比,阅读时千万别看成是两条指令了啊!
UseExtInt13OrNot:
ADD CX,[BP+02]
;①0000:06A1 034E02 ADD CX,[BP+02] 获取分区引导扇区所在的柱面号和物理扇区号
;②0000:06A1 EB1E JMP 06C1 如果分区类型是0Ch、0Eh而且扩展读能使用则执行该指令
;
; 0000:06A4:将可引导分区的分区引导记录装入内存指定区域
; 入口参数:AH=功能号,02为读盘操作;AL=一次读取的扇区数
; ES:BX=读入内存的起始地址
; CH=10位柱面号的低8位;CL:高两位是10位柱面号的高两位,低6位是物理扇区号
; DH=磁头号;DL=驱动器号,最高位(即位7)为0是软盘,为1是硬盘
INT 13h ;读分区引导记录到0000:7C00起始的区域
;
Check:
JB ReadDbrFail;06D1 不成功转
MOV SI, offset Invalid+1eh ;错误信息字符串偏移→SI
CMP WORD PTR ds:[7DFEh],0AA55h ;分区引导记录合法吗?
JZ MbrOk ;070D 合法则转(这是主引导记录唯一的正常出口)
SUB DI,+05 ;不合法则为换读其他扇区做准备
JG @@1 ;只有一次换读扇区的机会!
;
; 0000:06B8~0000:06BF:错误预处理
ReadError:
TEST SI,SI ;测试SI值是否为0,其意义在于确定该显示哪条信息
JNZ NextChar ;不为0则转错误处理,显示“Missing operating system”
MOV SI,offset Invalid+0bh;071A 错误信息字符串偏移→SI
JMP DeadCircle ;转错误处理,显示“加载操作系统时出错”
;
; 0000:06C1~0000:06CF:整理扩展读所需入口参数,然后调用扩展读子程序
; 这段代码只有在以扩展读方式读取分区引导记录时才有机会获得执行
CBW ;转换字节AL为字AX,执行后,AX中是一次要读的扇区数
XCHG CX,AX ;AX→CX,CX→AX,执行后,CX中是一次要读的扇区数
PUSH DX
CWD ;将字AX转换为双字→DX,AX
ADD AX,[BP+08]
ADC DX,[BP+0Ah] ;执行后,DX:AX=LBA绝对物理扇区号
CALL ReadSectorWithExtInt13 ;调用扩展读子程序
POP DX
JMP Check
;
; 0000:06D1~0000:06D8分区引导记录装入失败时的处理
ReadDbrFail:
DEC DI ;计数器减1
JZ ReadError ;06B8 五次读盘均未成功则转错误处理(注意这时SI=0)
XOR AX,AX ;置功能号
INT 13h ;复位磁盘系统
JMP @@1 ;再读
;
;
;0000:06DA 00 00 80 49 12 00 ...I..
;
; 0000:06E0~0000:070C:使用扩展INT 13h功能读取分区引导记录的子程序
; 调用时,SP=7BFE。这段程序利用压栈寄存器方式构造了一个磁盘地址包,请注意体会。另外,0000:06FC处
; 的一条指令就释放了几乎全部由本段程序占用的栈空间,构思之巧妙,绝对需要我们学习!
; 所以,分析该段程序,一个重点应放在栈的变化上。
ReadSectorWithExtInt13:
PUSH SI ;保存SI——注意,这次压栈并不构造磁盘地址包
XOR SI,SI ;清零
PUSH SI
PUSH SI
PUSH DX
PUSH AX ;以上四条指令压栈的是扇区LBA号码*2
PUSH ES ;压栈内存目标缓冲区首址段址
PUSH BX ;压栈内存目标缓冲区首址偏移
PUSH CX ;压栈所读扇区数
MOV SI,0010h ;注意SI的高8位对应着磁盘地址包的保留字节,必须为0
PUSH SI ;压栈磁盘地址包包长,执行完本条指令一个包已经构造完毕
MOV SI,SP ;规定磁盘地址包偏移指针,这时SP=7BEA
PUSH AX ;保存AX
PUSH DX ;保存DX
MOV AX,4200h ;置扩展读功能号
MOV DL,[BP+24h] ;取驱动器号,参照0000:0683
; 入口参数:AH=功能号,02为读盘操作;DL=驱动器号
; DS:SI=16字节磁盘地址包——第0字节:包长度(固定为10h);第1字节:保留,必须为0;
; 第2、3字节:所读扇区数;第4~5字节:内存目标缓冲区首址偏移;
; 第6~7字节:内存目标缓冲区首址段址; 第8~15字节:扇区LBA号码
; 出口参数:成功则AH=0;错误则AH=错误代码
INT 13h ;执行扩展读操作
POP DX
POP AX
LEA SP,[SI+10h] ;7BEA+10h=7BFA→SP(注意是取偏移而不是取单元内容)
JB ExtReadError ;扩展读不成功转
tmp:
INC AX
JNZ AxNotFlow
INC DX ;AX加1溢出时(比如0FFFFh+1)DX才加1
AxNotFlow:
ADD BH,02 ;调整BX,使偏移量增加512字节(刚好一扇区)
LOOP tmp ;0701~0708一段代码暂未明白其真实意图!
CLC
ExtReadError:
POP SI
RET
; 0000:070D:中继跳转
MbrOk:
JMP SwitchtoDbr
;
; 070F~0745是错误信息!果然是中文Windows98生成的主引导记录,所以我要特别
; “感谢”微软这个傻B,真难为它竟然用中文表述前两个信息!可惜真需显示的时
; 候鬼才能看懂是什么呢!!!我K!——耍弄我们耶!?
; 070F~0718:“分区表无效”中文信息
; 071A~072B:“加载操作系统时出错”中文信息
; 072D~0744:“Missing operating system”英文信息
;0000:070F B7 .
;0000:0710 D6 C7 F8 B1 ED CE DE D0-A7 00 BC D3 D4 D8 B2 D9 ................
;0000:0720 D7 F7 CF B5 CD B3 CA B1-B3 F6 B4 ED 00 4D 69 73 .............Mis
;0000:0730 73 69 6E 67 20 6F 70 65-72 61 74 69 6E 67 20 73 sing operating s
;0000:0740 79 73 74 65 6D 00 00 00-00 00 00 00 00 00 00 00 system..........
;0000:0750 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
;0000:0760 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
;0000:0770 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
;0000:0780 00 00 00 ...
org offset Start+10fh
Invalid db "分区表无效",0
db "加载操作系统时出错",0
db "Missing operating system",0
; 0000:0783~0000:0789:控制权移交
org offset Start+183h
SwitchtoDbr:
MOV DI,SP ;
PUSH DS
PUSH DI ;构造一个跳转地址
MOV SI,BP ;
RETF ;交控制权给分区引导记录(0000:7C00)
org offset Start+1feh
dw 0AA55h
end Start