03_主引导程序加载FAT12文件内容
写在前面
本文主要是介绍如何编码实现在主引导扇区读取FAT12文件系统中的指定文件(hello)的内容,具体步骤如下:
- 标准流程图
- 知识点:磁盘数据寻址方式、用到的基础汇编指令
- 代码结构图
- 代码:带详细注释的汇编、Makefile编译文件、运行截图
- 思考:描述写此代码时的步骤与思考
知识点
一、磁盘数据寻址方式
存储设备支持CHS和LBA两种寻址方式,CHS多为老设备使用,需要磁头号、柱面号、扇区号确定位置。LBA是新设备常用的线性寻址方式。
-
CHS --> LBA
LBA = [(柱面编号 << 1) + (磁头编号 & 0x1)] * 18 + (扇区编号 - 1) -
LBA --> CHS
2.1 两个盘面,80个柱面/盘面,18个扇区/柱面,512字节/扇区
2.2 存储大小:2 x 80 x 18 x 512 = 1440 KB
2.3 逻辑扇区号转化为磁盘物理位置:磁头号、柱面号、扇区号
柱面扇区号:在上一篇文档中有介绍,是主引导扇区结构中的BPB_SecPerTrk参数FAT12文件系统
逻辑扇区号:如下图所示,找到内存地址0x4800就是想要输出的数据首地址,将其转化成扇区号(0x4800/0x200)
二、基础汇编指令
功能 | 参数 | 中断号 |
---|---|---|
软驱复位 | AH=0x00 DL=驱动器号 | int 0x13 |
从磁盘将数据 写入ES:BX指向的内存中 | AH=0x02 AL=长度(扇区) CH=柱面号 CL=起始扇区号 DH=磁头号 DL=驱动器号 ES:BX=目标地址 | int 0x13 |
打印字符串 | AX BX=打印样式 es:bp=字符串地址 cx=字符串长度 | int 0x10 |
加法 | 结果赋值给被加数寄存器 | add 被加数,加数 |
减法 | 结果赋值给被减数寄存器 | sub 被减数,减数 |
乘法 | eax=被乘数 eax=积 edx=结果溢出数据 | mul 乘数 |
除法 | ax=被除数 al=商 ah=余数 | div 除数 |
32位下除法 | edx:eax=被除数 eax=商 edx=余数 | div 除数 |
与运算 | 结果赋值给操作数1 | and 操作数1,操作数2 |
逻辑右移 | 结果赋值给操作数1 | shr 操作数1,操作数2 |
逻辑左移 | 结果赋值给操作数1 | shl 操作数1,操作数2 |
常量定义 | 不占内存宏定义,类似#define Const XXX | Const equ XXX |
自增 | inc 寄存器 | |
自减 | des 寄存器 | |
跳转 | jmp 目的地址 | |
判断跳转 | jz xxx | 满足cmp条件跳转 |
大于 | ja xxx | cmp si, di si > di即跳转 |
小于等于 | jna xxx | cmp si, di si <= di即跳转 |
小于 | jb xxx | cmp si, di si < di即跳转 |
大于等于 | jnb xxx | cmp si, di si >= di即跳转 |
三、反汇编调试
ndisasm -o 0x7c00 boot.bin > boot.txt
代码
org 0x7c00
jmp short start
nop
define:
BaseOfStack equ 0x7c00
BaseOfLoader equ 0x9000
RootEntryOffset equ 19
RootEntryLength equ 14
EntryItemLength equ 32
FatEntryOffset equ 1
FatEntryLength equ 9
header:
BS_OEMName db "D.T.Soft"
BPB_BytsPerSec dw 512
BPB_SecPerClus db 1
BPB_RsvdSecCnt dw 1
BPB_NumFATs db 2
BPB_RootEntCnt dw 224
BPB_TotSec16 dw 2880
BPB_Media db 0xF0
BPB_FATSz16 dw 9
BPB_SecPerTrk dw 18
BPB_NumHeads dw 2
BPB_HiddSec dd 0
BPB_TotSec32 dd 0
BS_DrvNum db 0
BS_Reserved1 db 0
BS_BootSig db 0x29
BS_VolID dd 0
BS_VolLab db "D.T.OS-0.01"
BS_FileSysType db "FAT12 "
start:
mov ax, cs
mov ss, ax
mov ds, ax
mov es, ax
mov sp, BaseOfStack
mov ax, RootEntryOffset
mov cx, RootEntryLength
mov bx, Buf
call ReadSector
mov si, Target
mov cx, TarLen
mov dx, 0
call FindEntry
cmp dx, 0
jz output
mov si, bx
mov di, EntryItem
mov cx, EntryItemLength
call MemCpy
mov ax, FatEntryLength
mov cx, [BPB_BytsPerSec]
mul cx
mov bx, BaseOfLoader
sub bx, ax
mov ax, FatEntryOffset
mov cx, FatEntryLength
call ReadSector
mov cx, [EntryItem + 0x1A]
call FatVec
jmp last
output:
mov bp, Target
mov cx, TarLen
call Print
last:
hlt
jmp last
; cx --> index
; bx --> fat table address
;
; return:
; dx --> fat[index]
FatVec:
mov ax, cx
mov cl, 2
div cl
push ax
mov ah, 0
mov cx, 3
mul cx
mov cx, ax
pop ax
cmp ah, 0
jz even
jmp odd
even: ; FatVec[j] = ( (Fat[i+1] & 0x0F) << 8 ) | Fat[i];
mov dx, cx
add dx, 1
add dx, bx
mov bp, dx
mov dl, byte [bp]
and dl, 0x0F
shl dx, 8
add cx, bx
mov bp, cx
or dl, byte [bp]
jmp return
odd: ; FatVec[j+1] = (Fat[i+2] << 4) | ( (Fat[i+1] >> 4) & 0x0F );
mov dx, cx
add dx, 2
add dx, bx
mov bp, dx
mov dl, byte [bp]
mov dh, 0
shl dx, 4
add cx, 1
add cx, bx
mov bp, cx
mov cl, byte [bp]
shr cl, 4
and cl, 0x0F
mov ch, 0
or dx, cx
return:
ret
; es:bp --> string address
; cx --> string len
Print:
mov dx, 0
mov ax, 0x1301
mov bx, 0x0007
int 0x10
ret
; no parameter
ResetFloppy:
push ax
push dx
mov ah, 0x00
mov dl, [BS_DrvNum]
int 0x13
pop dx
pop ax
ret
; ax --> logic sector number
; cx --> number of sector
; es:bx --> target address
ReadSector:
push ax
push cx
push dx
call ResetFloppy
push bx
push cx
mov bl, [BPB_SecPerTrk]
div bl
mov cl, ah
add cl, 1
mov ch, al
shr ch, 1
mov dh, al
and dh, 1
mov dl, [BS_DrvNum]
pop ax
pop bx
mov ah, 0x02
read:
int 0x13
jc read
pop dx
pop cx
pop ax
ret
; ds:si --> source
; es:di --> destination
; cx --> length
MemCpy:
push si
push di
push cx
push ax
cmp si, di
ja btoe
add si, cx
add di, cx
dec si
dec di
jmp etob
btoe:
cmp cx, 0
jz done
mov al, [si]
mov byte [di], al
inc si
inc di
dec cx
jmp btoe
etob:
cmp cx, 0
jz done
mov al, [si]
mov byte [di], al
dec si
dec di
dec cx
jmp etob
done:
pop ax
pop cx
pop di
pop si
ret
; ds:si --> source
; es:di --> destination
; cx --> length
; return (cx == 0) ? equal : noequal
MemCmp:
push si
push di
push ax
compare:
cmp cx, 0
jz equal
mov al, [si]
cmp al, byte [di]
jz goon
jmp noequal
goon:
inc si
inc di
dec cx
jmp compare
equal:
noequal:
pop ax
pop di
pop si
ret
; es:bx --> root entry offset address
; ds:si --> target string
; cx --> target length
;
; return:
; (dx != 0) ? exist : noexist
; exist --> bx is the target entry
FindEntry:
push di
push bp
push cx
mov dx, [BPB_RootEntCnt]
mov bp, sp
find:
cmp dx, 0
jz noexist
mov di, bx
mov cx, [bp]
call MemCmp
cmp cx, 0
jz exist
add bx, 32
dec dx
jmp find
exist:
noexist:
pop cx
pop bp
pop di
ret
MsgStr db "Hello, not exist"
MsgLen equ ($-MsgStr)
Target db "HELLO "
TarLen equ ($-Target)
EntryItem times EntryItemLength db 0x80
Buf:
times 510-($-$$) db 0x00
db 0x55, 0xaa
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?