突破512字节的限制(上)

前几节我们介绍了FAT12文件系统,制作了虚拟软盘文件a.img,并在Qt Creater中进行了文件内容的读取实验。那些读取都是使用外部的程序实现的,实际应用中,我们需要用主引导程序来实现文件的读写,主引导程序存在于主引导扇区MBR中,也就是说程序和文件是存在一张盘上的,而且这些主引导程序需要使用汇编语言实现。接下来,我们就来实现具有读取功能的主引导程序。

为了验证文件读取的正确性,我们需要在主引导程序中先实现一个字符串打印函数。BIOS已经将中断向量写到了内存的指定位置处,这其中就有能实现字符串打印的函数,我们需要做的就是配置一些参数,需要配置的参数和配置步骤如下:

1、指定打印参数(AX=0x1301 , BX = 0x0007)

2、指定字符串的内存地址(ES:BP  = 字符串地址)

3、指定字符串的长度(CX = 字符串长度)

4、中断调用(int 0x10)

示例如下:

需要用到的汇编语言的知识点如下: 

字符串打印的汇编程序如下所示 

org 0x7c00
jmp short start
nop

define:
    BaseOfStack equ 0x7c00

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, MsgStr
    mov bp, ax

    mov ax, ds
    mov es, ax

    mov cx, 6
    call Print

last:
    hlt
    jmp last

; es:bp --> string address
; cx    --> string length
Print:
    mov ax, 0x1301
    mov bx, 0x0007
    int 0x10
    ret

MsgStr db  "Hello, DTOS!"
MsgLen equ ($-MsgStr)
Buf:
    times 510-($-$$) db 0x00
    db 0x55, 0xaa

我们的虚拟软盘文件data.img已经格式化好了,现在要修改的只是第一个扇区即主引导扇区,所以我们要按照FAT12的格式来填充第一个扇区,因此,上面的程序中,第9到27行是FAT12的引导扇区的信息,它前面还有三个字节,其中前两个字节是跳转指令jmp short start,第三个字节为空指令nop。从start开始是我们的可执行程序,BaseOfStack是定义的栈底地址。

汇编程序我们需要编译成二进制可执行程序,然后写入a.img虚拟软盘的第一个扇区中,每次都执行这个过程很麻烦,我们将这个过程写makefile,如下所示:

.PHONY : all clean rebuild

SRC := boot.asm
OUT := boot.bin
IMG := data.img

RM := rm -fr

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
	

接下来,我们开始读取文件,假如我们知道了数据所在的扇区,那么怎么将它读出来呢?先来看一下软盘的构造,如下所示:

3.5寸软盘的特性如下: 

根据逻辑扇区号计算磁头号、柱面号、物理扇区号的方法如下: 

软盘复位和软驱数据读取的参数如下:

整体的读取流程如下: 

需要用到的汇编知识点如下: 

整体程序的汇编代码如下

org 0x7c00                                                      # 将本程序加载到0x7c00这段物理地址空间上去

define:
	BaseOfStack equ 0x7c00                # 设置对应的栈底指针

jmp short start                                    # 主引导程序的前三个字节需要实现主程序的跳转,这条指令占2个字节,nop占一个字节
nop

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                                      # 初始化各个段寄存器为0,sp寄存器为主引导程序的加载地址,向低地址增长
	mov ss, ax
	mov ds, ax
	mov es, ax
	mov sp, BaseOfStack

	mov ax, 34                                     # 参数:逻辑扇区的编号
	mov cx, 1                                       # 参数:读取扇区数
	mov bx, Buf                                    # 参数:指定读取到的内存地址

	call ReadSection                              # 调用读取函数

	mov bp, Buf                                    # 参数:指定需要打印字符串的起始地址
	mov cx, 27                                      # 参数:指定需要打印字符串的长度

	call Print                                         # 调用打印函数

    jmp last

last:
	hlt                                                    # 让cpu暂停执行   
	jmp last

;es:bp --> string address
;cx    --> string length
Print:
	mov ax, 0x1301                                # 这两行指定打印参数
	mov bx, 0x007
	int 0x10                                           # 打印所对应的BIOS中断号
	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
ReadSection:
	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                                           # cl的值为起始扇区号
	mov ch, al
	shr ch, 1                                           # 右移指令
	mov dh, al
	and dh, 1                                          # 按位与指令,dh的值为磁头号
	mov dl, [BS_DrvNum]                         # dl的值为驱动器号

	pop ax                                            # 将cx寄存器出栈,值赋给ax寄存器,ax寄存器的值为要读取的扇区长度
	pop bx
	
	mov ah, 0x02

read:
	int 0x13                                          # BIOS读取数据对应的中断号
	jc read                                            # 如果读取失败,则继续读取

	pop ax
	pop dx
	pop cx
	pop bx

	ret

MsgStr db  "Hello, DTOS!"                           # 定义字符串
MsgLen equ ($-MsgStr)                               # 定义字符串长度,$代表当前指令的起始地址,equ不占用存储空间

Buf:
    times 510-($-$$) db 0x00                         # $$表示程序的起始地址,从当前程序的起始地址开始,不足510字节的内存空间补0
    db 0x55, 0xaa                                         # 主引导程序的结束标志,最后两个字节为0x55aa

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值