c09_mbr — MBR 加载器主流程框图
c09_mbr.asm · SECTION mbr · vstart=0x7C00 · 实模式
《x86汇编语言:从实模式到保护模式》· 李忠
第9章 · MBR加载器 (第1页 / 共2页)
主流程
重定位流程
子程序
循环体
判断分支
段间跳转
主流程
BIOS 跳转入口
CS=0x0000, IP=0x7C00 · MBR被加载到物理地址0x7C00
初始化堆栈
SS=0x0000, SP=0x0000 · 利用内存高端作临时栈
计算加载段地址
dx:ax ← [cs:phy_base] = 0x00010000
dx:ax ÷ 16 → ax=0x1000 · DS=ES=0x1000
读取第一个扇区
DI:SI = 0:100(LBA扇区号)
DS:BX = 0x1000:0x0000 → 写入物理地址0x10000
call read_hard_disk_0
dx == 0?
程序长度是512整数倍?
是
ax = ax − 1(已读1扇区)
否
ax == 0?
程序长度≤512字节?
是
否
循环读取剩余扇区(CX次)
DS+=0x20(下一512字节段), SI++
call read_hard_disk_0 → loop
call
direct: 单独回填入口点段地址
dx:ax ← header[0x08:0x06]
call calc_segment_base → [0x06] ← ax
call
段重定位表处理(realloc循环)
CX=[0x0a]=5项,BX=0x0c起
dx:ax←[BX+2:BX] → calc → [BX]←ax → BX+=4
loop realloc(共5次)
call ×5
jmp far [0x04]
CS←[0x06](code_1段值),IP←[0x04](start偏移0x00A2)
子程序
read_hard_disk_0
输入:DI:SI=32位LBA扇区号
DS:BX=目标内存地址
① 0x1F2←1(读1扇区)
② 0x1F3←SI低8,0x1F4←SI高8(LBA bit15~0)
③ 0x1F5←DI低8(LBA bit23~16)
④ 0x1F6←0xE0|DI高4位(LBA28主盘模式)
⑤ 0x1F7←0x20(读命令)
⑥ 等待状态(0x1F7 & 0x88)==0x08
⑦ 循环256次从0x1F0读字→[DS:BX],ret
calc_segment_base
输入:DX:AX = 32位文件内偏移
返回:AX = 16位段值
① dx:ax += phy_base(0x10000)
add ax,[cs:phy_base]
adc dx,[cs:phy_base+2]
② 右移4位得段值
shr ax,4
ror dx,4 / and dx,0xF000
or ax,dx → AX=段值,ret
header 内存布局(DS:0x00起)
[0x00~0x03]
program_length(程序总字节数)
[0x04~0x05]
start偏移(IP)= 0x00A2
[0x06~0x09]
code_1段地址 ← 单独回填
[0x0a~0x0b]
重定位表项数 = 5
[0x0c~0x0f]
code_1_segment ← 重定位表[0]
[0x10~0x13]
code_2_segment ← 重定位表[1]
[0x14~0x17]
data_1_segment ← 重定位表[2]
[0x18~0x1b]
data_2_segment ← 重定位表[3]
[0x1c~0x1f]
stack_segment ← 重定位表[4]
c09_mbr — 段重定位详细框图
direct: 单独回填入口点 · realloc: 循环处理重定位表 · calc_segment_base 算法
《x86汇编语言:从实模式到保护模式》· 李忠
第9章 · MBR加载器 (第2页 / 共2页)
重定位主流程
calc_segment_base 内部
循环体
判断分支
数值说明
重定位主流程
(pop ds 恢复DS后,直接进入 direct:)
direct: 单独处理入口点段地址
ax ← header[0x06](code_1文件偏移低16位)
dx ← header[0x08](code_1文件偏移高16位)
call calc_segment_base
输入 dx:ax=文件偏移,返回 ax=16位段值
call
回填入口点段值
header[0x06] ← ax(高2字节[0x08~0x09]废弃)
↓ 进入重定位表循环
初始化重定位循环
CX ← header[0x0a] = 5(重定位项数)
BX ← 0x0c(重定位表首项偏移)
realloc 循环体
① ax ← [DS:BX] 低16位文件偏移
② dx ← [DS:BX+2] 高16位文件偏移
③ call calc_segment_base → ax=段值
④ [DS:BX] ← ax 回填低2字节
⑤ BX += 4 指向下一项
⑥ loop realloc CX-- 不为0继续
call ×5
CX-- ≠ 0?
还有重定位项?
是(继续)
否(5项全部完成)
重定位完成 → jmp far [0x04]
CS:IP = code_1段值:0x00A2(start)
重定位表5项处理示意(BX递增)
BX
字段
编译时(文件偏移)
回填后
0x0c
code_1_segment
0x00000020
0x1002
0x10
code_2_segment
0x000000e0
0x100e
0x14
data_1_segment
0x000000f0
0x100f
0x18
data_2_segment
0x00000290
0x1029
0x1c
stack_segment
0x000002a0
0x102a
(偏移值为示意,实际取决于各段大小)
回填后高2字节[BX+2~BX+3]不再使用
段值 = (phy_base + 文件偏移) ÷ 16
= (0x10000 + offset) >> 4
calc_segment_base 内部流程
输入:DX:AX = 32位文件内偏移 返回:AX = 16位段值
push dx(保存DX,计算结束后恢复)
步骤①:加上 phy_base(0x10000)
add ax, [cs:phy_base] ax += 0x0000
adc dx, [cs:phy_base+2] dx += 0x0001(含进位)
→ dx:ax = 文件偏移 + 0x10000 = 物理地址
例:code_1偏移=0x0020
dx:ax = 0x0001:0x0020 → 物理地址 0x00010020
步骤②:右移4位得16位段值
shr ax,4
ax高4位补0,低4位丢弃
ror dx,4
dx循环右移4位,低4位→高4位
and dx,0xF000
只保留dx的最高4位
or ax,dx
合并dx高4位到ax,组成段值
→ AX = 完整16位段值
例:物理地址 0x00010020
shr ax(0x0020),4 → ax=0x0002
ror dx(0x0001),4 → dx=0x1000
and dx,0xF000 → dx=0x1000 or ax,dx → ax=0x1002
pop dx(恢复DX)· ret → AX=0x1002(段值)
右移4位位运算示意
物理地址 dx:ax = 0x0001 : 0x0020
二进制:0000 0000 0000 0001 : 0000 0000 0010 0000
shr ax,4:
ax=0x0020 → 0000 0000 0010 0000
ax=0x0002 → 0000 0000 0000 0010(低4位0000丢弃)
ror dx,4:
dx=0x0001 → 0000 0000 0000 0001
dx=0x1000 → 0001 0000 0000 0000(低4位0001→最高4位)
and dx,0xF000:
dx=0x1000(最高4位保留,其余清零)
or ax,dx:
ax=0x0002 → 0000 0000 0000 0010
dx=0x1000 → 0001 0000 0000 0000
ax=0x1002 → 0001 0000 0000 0010 ← 最终16位段值
物理地址验证:0x1002 × 16 = 0x10020 ✓
原理:(dx:ax) >> 4 等效于 物理地址 ÷ 16 = 段值
cs: 前缀:phy_base在代码段内,DS未知,必须用CS寻址