随记:
1 在汇编中可以定义函数,函数名使用标签定义, 函数体最后一条指令是 ret, 调用函数 是call function
2 如果代码中定义了函数,那么需要定义栈空间。用于保存关键寄存器的值,栈顶地址通过sp寄存器保存
3 汇编中用 equ 定义常量,如 Const equ 0x7c00 == #define Const 0x7c00 .
4 dx(db,dw,dd) 可以定义变量 他们与 equ的差别在于,equ定义不会占用内存空间,而dx定义占用对应的内存空间
db定义字节类型变量,一个字节数据占1个字节单元,读完一个,偏移量加1
dw定义字类型变量,一个字数据占2个字节单元,读完一个,偏移量加2
dd定义双字类型变量,一个双字数据占4个字节单元,读完一个,偏移量加4
5 and 按位与 运算
6 汇编中的16位除法操作(div)
一 在主引导程序中打印字符串 – 直接调用系统服务
二 主引导程序中读取指定扇区数据 – 直接调用系统服务
不过参数比较麻烦,这涉及到软盘的基本信息:
makefile
.PHONY : all clean rebuild
SRC := boot.asm
OUT := boot.bin
IMG := data.img
RM := rm -fr
#将主引导程序的二进制程序 烧写到 虚拟软盘的第0个扇区
all : $(OUT) $(IMG)
dd if=$(OUT) of=$(IMG) bs=512 count=1 conv=notrunc
@echo "Success!"
#创建虚拟软盘
$(IMG) :
bximage $@ -q -fd -size=1.44
#编译主引导程序代码 成二进制程序
$(OUT) : $(SRC)
nasm $^ -o $@
clean :
$(RM) $(IMG) $(OUT)
rebuild :
@$(MAKE) clean
@$(MAKE) all
bochsrc 配置
###############################################################
# Configuration file for Bochs
###############################################################
# how much memory the emulated machine will have
megs: 32
# filename of ROM images
romimage: file=/usr/local/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/share/vgabios/vgabios.bin
# what disk images will be used
floppya: 1_44=data.img, status=inserted
# choose the boot disk.
boot: a
# where do we send log messages?
# log: bochsout.txt
# disable the mouse
mouse: enabled=0
# enable key mapping, using US layout as default.
keyboard_mapping: enabled=1, map=/usr/local/share/bochs/keymaps/x11-pc-us.map
boot.asm
;主引导程序起始地址
org 0x7c00
;主引导程序的前三个字节 是跳转指令
;跳转指令(jmp指令占 一个字节) 跳到start标签(目标地址标签 是短跳转 占一个字节) nop(空指令占一个字节)
;这样写的原因是 下面的定义的 FAT12文件头并不是可执行的代码,所以需要跳过这一段信息 到 start 标签执行
jmp short start
nop
;栈空间起始地址
define:
BaseOfStack equ 0x7c00
; FAT12文件系统所需要的头
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
;指定 sp栈顶指针寄存器 值
mov sp, BaseOfStack
;指定参数 逻辑扇区号:34
mov ax, 34
;需要连续读取一个扇区
mov cx, 1
;读取到 buf地址
mov bx, Buf
call ReadSector
;调用打印函数前,在寄存器中指定函数参数:字符串地址+长度
;字符串地址
mov bp, Buf
;字符串长度
mov cx, 29
;调用打印函数
call Print
last:
hlt
jmp last
;打印函数,用寄存器传参数(目标字符串地址和长度)
; es:bp --> string address 目标字符串地址
; cx --> string length 长度
Print:
;打印参数
mov ax, 0x1301
mov bx, 0x0007
;调用中断(调用0x10号中断 打印)
int 0x10
ret
; no parameter
;重置软驱
ResetFloppy:
;备份 ax bx 寄存器值,将ax bx 寄存器进栈备份
push ax
push dx
;BIOS 中软盘数据读取 中断号0x13,参数(ah=0x00 软驱复位,dl=0 A盘)
;软驱复位
mov ah, 0x00
;驱动器号 0:A盘
mov dl, [BS_DrvNum]
; BIOS 中软盘数据读取 中断号0x13
int 0x13
;将ax bx 寄存器出栈(恢复 ax bx 寄存器值),顺序与进栈顺序相反
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
;bx(读取到的内存位置) cx(需要连续读取多少个扇区) 进栈备份
push bx
push cx
;每个磁道扇区数 18
mov bl, [BPB_SecPerTrk]
; 执行除法操作,ax寄存器值/18 = 商位于 AL 寄存器中 余数位于 AH 寄存器中
div bl
;将 ah寄存器值(余数) 放到 cl寄存器
mov cl, ah
;三元素 之 扇区号
add cl, 1
;将 al寄存器值(商) 放到 ch寄存器
mov ch, al
;三元素 之 柱面号 (sh 寄存器值 右移一位)
shr ch, 1
;三元素 之 磁头号 商按位与1 == 磁头号 放在 dh寄存器
mov dh, al
and dh, 1
;驱动器号
mov dl, [BS_DrvNum]
;将cx 寄存器值 出栈 并弹到 ax 寄存器中
pop ax
pop bx
;BIOS 中软盘数据读取 中断号0x13,参数:
; ah=0x02 从磁盘将数据读到ES:BX指向的内存
; dl= 扇区长度,ch= 柱面号, cl=起始扇区号, dh=磁头号,dl=驱动器号
mov ah, 0x02
;如果读取失败 则反复读取 直到读取成功
read:
int 0x13
;读完后 查看错误标志位有没有被设置,有的话 跳转到read标签处重读
jc read
;出栈恢复(顺序相反)
pop ax
pop dx
pop cx
pop bx
ret
MsgStr db "Hello, DTOS!"
;字符串长度: 用当前地址 - MsgStr 地址
MsgLen equ ($-MsgStr)
Buf:
times 510-($-$$) db 0x00
db 0x55, 0xaa