学习自狄泰软件
1 CX DX寄存器是16位的 分别用 ch dh cl dl 代表高低八位数据
2 $ 表示当前指令行地址,$$表示当前汇编段起始地址
3 cmp si di
如果 si > di 走 ja
如果 si<=ji 走jna
如果 si < di 走 jb
如果 si >= di 走 jnb
4 mul 乘法,被乘数放在 AL 寄存器,乘数放到通用寄存器或内存,结果放在AX寄存器
5 and 按位与
6 shl 左移
7 shr 右移
8 拷贝操作注意 空间重叠
避免src数据被破坏
简单点说: FAT表每一个FAT表项 由1.5个字节组成:
如偶数FAT表偶数下标表项: FAT表 第i+1字节的 低四位,和 第i字节 组成。其中FAT表 第i+1字节的 低四位 作为组成表项的高四位
boot.asm
org 0x7c00
jmp short start
nop
define:
BaseOfStack equ 0x7c00
;加载 FAT表 到该地址之前的空间 addr < 0x9000
;将目标程序加载到 该内存
BaseOfLoader equ 0x9000
RootEntryOffset equ 19
RootEntryLength equ 14
;
EntryItemLength equ 32
;FAT表起始逻辑扇区号 1
FatEntryOffset equ 1
;FAT表 长度 9个扇区
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
;将根目录区 数据 读取到 Buf地址
mov bx, Buf
;读取软驱数据 中的 根目录区数据(起19扇区,共14个扇区) 到 Buf地址
call ReadSector
;起始地址 bx 根目录区数据已经拷贝到 buf
;需要查找的目标 字符串 "LOADER "
mov si, Target
;字符串长度
mov cx, TarLen
mov dx, 0
;在根目录区查找 字符串 == "LOADER ", 查到目标字符串在 bx 寄存器保存的地址所在的 根目录项
call FindEntry
;cmp dx, 0
;jz output
;内存拷贝
mov si, bx
;目标文件的目录项信息的入口地址
mov di, EntryItem
mov cx, EntryItemLength
;内存拷贝 ,将 bx保存的 根目录项数据 拷贝到 EntryItem,即拷贝完成后 EntryItem代表目标文件的目录项信息的入口地址
call MemCpy
;加载 FAT表 到指定内存地址
;计算目标空间地址
;FAT表 长度 9个扇区
mov ax, FatEntryLength
;每个扇区大小
mov cx, [BPB_BytsPerSec]
; 计算FAT表所占用内存字节数,乘法 被乘数放在 AL 寄存器,乘数放到通用寄存器或内存(这里是cx),结果放在 AX 寄存器
mul cx
;需要拷贝到的目标地址 0x9000 前的一块空间
mov bx, BaseOfLoader
;减去 FAT表占用的内存字节数,结果存放在bx, 即FAT表在内存中的起始位置,得到目标空间地址
sub bx, ax
;FAT表起始逻辑扇区号 1 逻辑扇区号
mov ax, FatEntryOffset
;FAT表 长度 9个扇区 需要连续读取多少个扇区
mov cx, FatEntryLength
;加载FAT表,将FAT表数据 加载到 bx 寄存器值指向的空间,即 0x9000 前的一块空间
call ReadSector
;获取目标文件 起始簇(该文件数据起始FAT表下标), 根目录区中 指定 文件目录项地址 + 0x1a == 目标文件开始的簇号
mov cx, [EntryItem + 0x1A]
;bx 寄存器 保存FAT表项在内存的起始位置
;查FAT表, 查询 cx寄存器中保存的值所对应的FAT表项是什么 对应的簇号
call FatVec
jmp last
output:
mov bp, MsgStr
mov cx, MsgLen
call Print
last:
;使程序停止运行,处理器进入暂停状态,不执行任何操作,使 CPU 进入这么一个状态:既不取指令,也不读写数据
hlt
jmp last
;读 FAT表
; cx --> index FAT表项下标 逻辑下标
; bx --> fat table address FAT表项在内存的起始位置,基地址
;
; return:
; dx --> fat[index] 返回FAT表项值
FatVec:
;根据FAT表项下标 计算对应的内存地址
; 判断下标奇偶 FAT逻辑下标*3/2 == 起始字节
mov ax, cx
mov cl, 2
;被除数放在AX寄存器,除数放在通用寄存器或内存中。商位于 AL 寄存器,余数位于AH寄存器
div cl
;保存商和余数
push ax
; 计算表项在内存的起始字节数
;al * 3
mov ah, 0
mov cx, 3
;乘法,被乘数放在 AL 寄存器,乘数放到通用寄存器或内存,结果放在AX寄存器
; al * 3
mul cx
mov cx, ax
pop ax
;余数为0 偶数 否则是奇数
cmp ah, 0
jz even
jmp odd
even: ; 偶数下标 FatVec[j] = ( (Fat[i+1] & 0x0F) << 8 ) | Fat[i];
mov dx, cx
;Fat[i+1]
add dx, 1
;Fat[i+1]地址 --> dx
add dx, bx
;借助bp寄存器,取该地址一个字节数据 到 dl 寄存器
mov bp, dx
mov dl, byte [bp]
;按位与 取 Fat[i+1] 低四位
and dl, 0x0F
;左移8位
shl dx, 8
;Fat[i]地址 放到 bp寄存器
add cx, bx
mov bp, cx
; 或运算 ( (Fat[i+1] & 0x0F) << 8 ) | Fat[i] 最终结果放在dl
or dl, byte [bp]
jmp return
odd: ; FatVec[j+1] = (Fat[i+2] << 4) | ( (Fat[i+1] >> 4) & 0x0F );
mov dx, cx
;Fat[i+2]
add dx, 2
;Fat[i+2]地址
add dx, bx
;借助bp寄存器
mov bp, dx
;取 bp 寄存器中的一个字节值到 dl寄存器
mov dl, byte [bp]
mov dh, 0
;dx 值左移4位 (Fat[i+2] << 4)
shl dx, 4
;Fat[i+1]
add cx, 1
;Fat[i+1]地址
add cx, bx
;借助bp寄存器
mov bp, cx
;取 bp 寄存器中的一个字节值到 cl寄存器
mov cl, byte [bp]
;右移4位 (Fat[i+1] >> 4)
shr cl, 4
;(Fat[i+1] >> 4) & 0x0F
and cl, 0x0F
mov ch, 0
; (Fat[i+2] << 4) |
or dx, cx
return:
ret
;内存拷贝函数
; ds:si --> source
; es:di --> destination
; cx --> length
MemCpy:
push si
push di
push cx
push ax
;比较 src des 地址 以判断是否需要调整拷贝方式
cmp si, di
;如果 si > di 则从前向后拷贝
ja btoe
;从后向前拷贝的准备工作
;指向原内存和 目标内存的尾部
add si, cx
add di, cx
;指向尾部最后一个有效字节
dec si
dec di
;si <= di 跳转到 etob标签 开始从后向前拷贝
jmp etob
;从头向尾进行拷贝
btoe:
;直到拷贝了 cx 长度
cmp cx, 0
jz done
mov al, [si]
;将 al 寄存器中的值 放到 di所指向的内存单元中
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
; 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
; 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:bp --> string address
; cx --> string length
Print:
;mov dx, 0 将目标字符串打印到 坐标 0,0 的地方 左上角
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 bx
push cx
push dx
push ax
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 ax
pop dx
pop cx
pop bx
ret
MsgStr db "No LOADER ..."
MsgLen equ ($-MsgStr)
Target db "LOADER "
TarLen equ ($-Target)
EntryItem times EntryItemLength db 0x00
Buf:
times 510-($-$$) db 0x00
db 0x55, 0xaa