---- 整理自狄泰软件唐佐林老师课程
1. 突破限制的准备工作
- 辅助函数
- 字符串打印
- 软盘读取
- 内存比较
- 根目录区查找
1.1 字符串打印
问题:
- 主引导程序中如何进行字符串的打印?
1.1.1 BIOS中的字符串打印
指定打印参数(AX = 0x1301,BX = 0x0007) |
---|
指定打印字符串的内存地址(ES:BP = 串地址) |
指定字符串的长度(CX = 串长度) |
中断调用(int 0x10) |
- 字符串打印示例
; 指定字符串地址
mov ax, msg
mov bp, ax
mov ax, ds
mov es, ax
; 指定字符串长度
mov cx, 6
; 指定打印参数
mov ax, 0x1301
mov bx, 0x0007
; 中断调用
int 0x10
1.1.2 汇编小贴士
- 汇编中可以定义 函数(函数名使用标签定义)
call function
- 函数体的最后一条指令为 ret
- 如果代码中定义了函数,那么需要定义 栈空间
- 用于保存关键寄存器的值
- 栈顶地址通过 sp寄存器 保存
- 汇编中的 “常量定义”(equ)
- 用法:
CHANGLIANG equ 0x7c00 ; #define CHANGLIANG 0x7c00
- 与(db,dw,dd)的区别:
- dx 定义 会占用 相应的内存空间
- equ 定义 不会占用 任何内存空间
- 用法:
1.1.3 编程实验:定义打印函数print
- print
【参看链接】:06-07-08 - 突破512字节的限制 / 06 / 00print
只打印6个字符:
- print2
【参看链接】:06-07-08 - 突破512字节的限制 / 06 / 01print2
1.2 软盘读取
问题:
- 主引导程序中如何读取指定扇区处的数据?
1.2.1 软盘的构造
- 一个软盘有 2个盘面,每个盘面对应 1个磁头
- 每一个盘面 被划分为若干个圆圈,成为 若干个柱面(磁道)
- 每一个柱面 被划分为 若干个扇区,每个扇区512字节
- 3.5寸软盘 的数据特性
- 每一个盘面一共有80个柱面(编号0~79)
- 每一个柱面有18个扇区(编号1~18)
- 存储大小:
2 * 80 * 18 * 512Byte = 1440 KB
1张软盘 | 2个盘面 | 编号0~1 |
---|---|---|
1个盘面 | 80个柱面(磁道) | 编号0~79 |
1个柱面 | 18个扇区 | 编号1~18 |
1个扇区 | 512字节 | – |
1.2.2 软盘数据的读取
- 软盘数据以扇区(512字节)为单位进行读取
- 指定数据所在位置的磁头号(哪个盘面) 、柱面号、扇区号
- 计算公式:
逻辑扇区号(相对扇区号)/ 柱面扇区数
1.2.3 BIOS中的软盘数据读取(int 0x13)
注:
- 执行:锁定/解锁驱动器
- 输入:
DL = 驱动器号
AL = 00h 锁定驱动器
AL = 01h 驱动器解锁
AL = 02h 返回锁定/解锁状态
AL = 03h-FFh 保留- 返回:
CF = 0, AH = 0 成功
CF = 1, AH = 错误码- 这个调用用来锁定指定驱动器中的介质
1.2.4 软盘数据读取流程
==> 指定逻辑扇区号AX、指定读取扇区数CX
==> 指定内存位置(ES:BX)
==> 重置软驱状态
==> 根据逻辑扇区号计算:柱面号、磁头号、扇区号
==> int 0x13
==> ret
1.2.5 汇编小贴士
- 汇编中的16位除法操作 div
- 被除数放到AX寄存器
- 除数放到通用寄存器或内存单元(8位)
- 结果:商位于AL,余数位于AH
被除数 ➗ 除数 = 商 ...... 余数
AX ➗ 通用reg或内存单元 = AL ...... AH
1.2.6 编程实验:磁盘数据的读取
【参看链接】:06-07-08 - 突破512字节的限制 / 06 / 02read
1.3 内存比较
1.3.1 整体思路
FAT12文件格式 | 数据区 |
根目录区 | |
FAT2 | |
FAT1 | |
引导扇区 |
问题:
- 如何在根目录区查找目标文件?
- 通过根目录区中的 目录项的前11个字节(DIR_Name)进行判断
1.3.2 内存内容比较
指定源起始地址(DS:SI) |
---|
指定目标起始地址(ES:DI) |
判断在期望长度(CX)内每一个字节是否相等 |
1.3.3 汇编小贴士
- 汇编中的比较和跳转
- 比较:
cmp cx, 0 ; 比较cx的值是否等于0
- 跳转:
jz equal ; 如果比较结果为真,则跳转到equal标签处
- 比较:
1.3.4 编程实验:内存比较函数
【参看链接】:06-07-08 - 突破512字节的限制 / 07 / 00mem_cmp
1.4 根目录区查找
查找根目录区是否存在目标文件
1.4.1 汇编小贴士
- 访问栈空间中的栈顶数据
- 不能使用 sp 直接访问栈顶数据
- 这是错误的:
mov cx, [sp]
- 这是错误的:
- 通过其它 通用寄存器 间接访问栈顶数据
- 不能使用 sp 直接访问栈顶数据
push cx
mov bp, sp
mov cx, [bp]
1.4.2 编程实验:根目录区查找函数
【参看链接】:06-07-08 - 突破512字节的限制 / 07 / 01find_entry
- 直接通过makefile中生成的data.img中没有loader,所以找不到,结果如下:
为什么没有找到loader?反汇编调试确认:
ndisasm -o 0x7c00 boot.bin > boot.txt
- 根据前面章节《04-05 - 主引导程序的扩展》,在data.img中写入Loader.bin文件,则可以找到:
1.5 备份目标文件的目录信息(内存拷贝)
1.5.1 MemCpy实现要点:拷贝方向
问题:
- 从源内存地址处拷贝到目标内存地址处即可,但如果 地址出现重叠 ?如下情况:
1.5.2 汇编小贴士
如果si大于di,就跳转到btoe标签处执行。
1.5.3 编程实验:内存拷贝函数
- 无重叠
【参看链接】:06-07-08 - 突破512字节的限制 / 08 / 00mem_cpy
- 有重叠
si < di
,从头部开始拷贝
【参看链接】:06-07-08 - 突破512字节的限制 / 08 / 00mem_cpy2
- 有重叠
si > di
,从尾部开始拷贝
【参看链接】:06-07-08 - 突破512字节的限制 / 08 / 00mem_cpy3
1.6 FAT表项的读取
FAT表中的 每个表项占用 1.5字节,即:使用3个字节表示2个表项
1.6.1 FAT表项的“动态组装”
FatVec[j] 的动态组装:
1.6.2 汇编小贴士
- 汇编中的16位乘法操作( mul )
- 被乘数放到AL寄存器
- 乘数放到通用寄存器或内存单元(8位)
- 相乘的结果放到AX寄存器中
被乘数 × 乘数 = 积
AL × 通用reg或内存单元 = AX
1.6.3 编程实验:FAT项读取函数
【参看链接】:06-07-08 - 突破512字节的限制 / 08 / 01FatVec
1.7 小结
- 内存拷贝时需要考虑进行拷贝的方向
- 当 si > di 时,从前向后拷贝
- 当 si <= di 时,从后向前拷贝
- Fat表加载到内存中之后,需要 “ 动态组装 ” 表项
- Fat表中使用3个字节表示2个表项
- 起始字节 = 表项下标 / 2 * 3 ==> (这里先3个3个的算出起始字节,再判断除以2的奇偶,然后用公式算出Fat表项)