【第四章复习】突破512字节,实现读扇区函数
2010-02-07 21:21
197 查看
经历了保护模式的复习后,我们再来继续我们的操作系统。我们之前只做了一个极其简陋的引导程序,现在来扩展它。
引导扇区太小,只有512字节,很可能不够用,那么我们把只要的功能交给LOADER来做,引导扇区BOOT的工作就是把LOADER加载进内存,并把控制权交给LOADER。
那么LOADER怎么放呢?在这里不妨把软盘做成FAT12格式,这样对于操作会方便得多。FAT12是一种使用普遍的文件系统,关于文件系统已经在操作系统教科书上有详述,这里不多说了。
要想软盘被DOS或者LINUX识别,则在软盘的引导扇区中写入一些数据才行,如下列代码:
Code:
jmp short LABEL_START ; Start to boot.
nop ; 这个 nop 不可少
; 下面是 FAT12 磁盘的头
BS_OEMName db 'MarcusX ' ; OEM String, 必须 8 个字节
BPB_BytsPerSec dw 512 ; 每扇区字节数
BPB_SecPerClus db 1 ; 每簇多少扇区
BPB_RsvdSecCnt dw 1 ; Boot 记录占用多少扇区
BPB_NumFATs db 2 ; 共有多少 FAT 表
BPB_RootEntCnt dw 224 ; 根目录文件数最大值
BPB_TotSec16 dw 2880 ; 逻辑扇区总数
BPB_Media db 0xF0 ; 媒体描述符
BPB_FATSz16 dw 9 ; 每FAT扇区数
BPB_SecPerTrk dw 18 ; 每磁道扇区数
BPB_NumHeads dw 2 ; 磁头数(面数)
BPB_HiddSec dd 0 ; 隐藏扇区数
BPB_TotSec32 dd 0 ; wTotalSectorCount为0时这个值记录扇区数
BS_DrvNum db 0 ; 中断 13 的驱动器号
BS_Reserved1 db 0 ; 未使用
BS_BootSig db 29h ; 扩展引导标记 (29h)
BS_VolID dd 0 ; 卷序列号
BS_VolLab dd 'MarcusOs0.2' ; 卷标, 必须 11 个字节
BS_FileSysType db 'FAT12 ' ; 文件系统类型, 必须 8个字节
LABEL_START:
操作系统就是根据这些信息来进行管理的。这样,我们把编译好的LOADER.BIN文件放到软盘上,引导的时候需要找到这个文件并加载,那么找文件就必须要读软盘上的目录区和FAT,那么现在就来实现这个功能,我们用到BIOS的13h号中断:
入口参数:ah=02h,al=要读的扇区数,ch=柱面(磁道)号,cl=起始扇区号,dh=磁头号,dl=驱动器号(0表示A盘),es:bx缓冲区地址
对于1.44M的软盘来说,一共有2880个扇区,所以0到2879是扇区的逻辑号,但是调用这个中断要把逻辑扇区号转换成磁头号(0或1),柱面号(0到79)和相对柱面的扇区号(1到18),公式如下:
相对扇区号/每磁道扇区号(18)的商Q,余数R,其中:
柱面号=Q>>1,磁头号=Q&1,相对起始扇区号=R+1
有了公式,那就很容易的翻译成代码了:
Code:
Read_Sector:
;C function proto(REAL MODE,SHORT CALL):
;void Read_Sector(byte16 first_sector_index,byte16 read_sector_number);
;attention:
;before call this function must let es:bx point to the buffer
push bp
mov bp,sp
push ax
push bx
push cx
push dx
push bx ; 暂存缓冲区偏移
mov ax,[bp + 6] ; 取得要读取的扇区数
push ax ; 暂时保存之
mov ax,[bp + 4] ; 取得要读的逻辑扇区号
mov bl,[BPB_SecPerTrk]
div bl ; 除以每柱面扇区数
mov ch,al
shr ch,1 ; ch<-柱面号
mov dh,al
and dh,1 ; dh<-磁头号
mov cl,ah
inc cl ; cl<-相对扇区号
mov dl,0 ; 读A盘
pop ax ; al<-要读的扇区数
pop bx ; 恢复缓冲区偏移
.Go_On_Reading:
mov ah,2 ; ah<-功能号
int 13h
jc .Go_On_Reading ; 如果cf=1,继续读
pop dx
pop cx
pop bx
pop ax
pop bp
ret
为了验证这个函数的正确性,下面来测试一下,准备一个空白的软盘映像,在第1个扇区的第267个字节写入A的ASCII码,也就是逻辑地址30ah,如图所示:
![](http://blog.csdn.net/userfiles/未命名(144).jpg)
下面是完整代码,把第3个扇区读进1个512字节的缓冲,然后把A显示出来,另外还加了预处理宏,方便在DOS下调试或直接引导:
Code:
%define _BOOT_DEBUG_ ; 做 Boot Sector 时一定将此行注释掉!将此行打开后用 nasm Boot.asm -o Boot.com 做成一个.COM文件易于调试
%ifdef _BOOT_DEBUG_
org 0100h ; 调试状态, 做成 .COM 文件, 可调试
%else
org 07c00h ; Boot 状态, Bios 将把 Boot Sector 加载到 0:7C00 处并开始执行
%endif
jmp short LABEL_START ; Start to boot.
nop ; 这个 nop 不可少
; 下面是 FAT12 磁盘的头
BS_OEMName db 'MarcusX ' ; OEM String, 必须 8 个字节
BPB_BytsPerSec dw 512 ; 每扇区字节数
BPB_SecPerClus db 1 ; 每簇多少扇区
BPB_RsvdSecCnt dw 1 ; Boot 记录占用多少扇区
BPB_NumFATs db 2 ; 共有多少 FAT 表
BPB_RootEntCnt dw 224 ; 根目录文件数最大值
BPB_TotSec16 dw 2880 ; 逻辑扇区总数
BPB_Media db 0xF0 ; 媒体描述符
BPB_FATSz16 dw 9 ; 每FAT扇区数
BPB_SecPerTrk dw 18 ; 每磁道扇区数
BPB_NumHeads dw 2 ; 磁头数(面数)
BPB_HiddSec dd 0 ; 隐藏扇区数
BPB_TotSec32 dd 0 ; wTotalSectorCount为0时这个值记录扇区数
BS_DrvNum db 0 ; 中断 13 的驱动器号
BS_Reserved1 db 0 ; 未使用
BS_BootSig db 29h ; 扩展引导标记 (29h)
BS_VolID dd 0 ; 卷序列号
BS_VolLab dd 'MarcusOs0.2' ; 卷标, 必须 11 个字节
BS_FileSysType db 'FAT12 ' ; 文件系统类型, 必须 8个字节
LABEL_START:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,7c00h
mov bx,LABEL_BUFFER
push 1
push 1
call Read_Sector
add sp,4
mov ax,0b800h
mov gs,ax
mov ah,0ch
mov al,[LABEL_BUFFER + 266]
mov [gs:140],ax
mov ax,4c00h
int 21h
LABEL_BUFFER:
times 512 db 0
Read_Sector:
;C function proto(REAL MODE,SHORT CALL):
;void Read_Sector(byte16 first_sector_index,byte16 read_sector_number);
;attention:
;before call this function must let es:bx point to the buffer
push bp
mov bp,sp
push ax
push bx
push cx
push dx
push bx ; 暂存缓冲区偏移
mov ax,[bp + 6] ; 取得要读取的扇区数
push ax ; 暂时保存之
mov ax,[bp + 4] ; 取得要读的逻辑扇区号
mov bl,[BPB_SecPerTrk]
div bl ; 除以每柱面扇区数
mov ch,al
shr ch,1 ; ch<-柱面号
mov dh,al
and dh,1 ; dh<-磁头号
mov cl,ah
inc cl ; cl<-相对扇区号
mov dl,0 ; 读A盘
pop ax ; al<-要读的扇区数
pop bx ; 恢复缓冲区偏移
.Go_On_Reading:
mov ah,2 ; ah<-功能号
int 13h
jc .Go_On_Reading ; 如果cf=1,继续读
pop dx
pop cx
pop bx
pop ax
pop bp
ret
运行结果如图:
引导扇区太小,只有512字节,很可能不够用,那么我们把只要的功能交给LOADER来做,引导扇区BOOT的工作就是把LOADER加载进内存,并把控制权交给LOADER。
那么LOADER怎么放呢?在这里不妨把软盘做成FAT12格式,这样对于操作会方便得多。FAT12是一种使用普遍的文件系统,关于文件系统已经在操作系统教科书上有详述,这里不多说了。
要想软盘被DOS或者LINUX识别,则在软盘的引导扇区中写入一些数据才行,如下列代码:
Code:
jmp short LABEL_START ; Start to boot.
nop ; 这个 nop 不可少
; 下面是 FAT12 磁盘的头
BS_OEMName db 'MarcusX ' ; OEM String, 必须 8 个字节
BPB_BytsPerSec dw 512 ; 每扇区字节数
BPB_SecPerClus db 1 ; 每簇多少扇区
BPB_RsvdSecCnt dw 1 ; Boot 记录占用多少扇区
BPB_NumFATs db 2 ; 共有多少 FAT 表
BPB_RootEntCnt dw 224 ; 根目录文件数最大值
BPB_TotSec16 dw 2880 ; 逻辑扇区总数
BPB_Media db 0xF0 ; 媒体描述符
BPB_FATSz16 dw 9 ; 每FAT扇区数
BPB_SecPerTrk dw 18 ; 每磁道扇区数
BPB_NumHeads dw 2 ; 磁头数(面数)
BPB_HiddSec dd 0 ; 隐藏扇区数
BPB_TotSec32 dd 0 ; wTotalSectorCount为0时这个值记录扇区数
BS_DrvNum db 0 ; 中断 13 的驱动器号
BS_Reserved1 db 0 ; 未使用
BS_BootSig db 29h ; 扩展引导标记 (29h)
BS_VolID dd 0 ; 卷序列号
BS_VolLab dd 'MarcusOs0.2' ; 卷标, 必须 11 个字节
BS_FileSysType db 'FAT12 ' ; 文件系统类型, 必须 8个字节
LABEL_START:
操作系统就是根据这些信息来进行管理的。这样,我们把编译好的LOADER.BIN文件放到软盘上,引导的时候需要找到这个文件并加载,那么找文件就必须要读软盘上的目录区和FAT,那么现在就来实现这个功能,我们用到BIOS的13h号中断:
入口参数:ah=02h,al=要读的扇区数,ch=柱面(磁道)号,cl=起始扇区号,dh=磁头号,dl=驱动器号(0表示A盘),es:bx缓冲区地址
对于1.44M的软盘来说,一共有2880个扇区,所以0到2879是扇区的逻辑号,但是调用这个中断要把逻辑扇区号转换成磁头号(0或1),柱面号(0到79)和相对柱面的扇区号(1到18),公式如下:
相对扇区号/每磁道扇区号(18)的商Q,余数R,其中:
柱面号=Q>>1,磁头号=Q&1,相对起始扇区号=R+1
有了公式,那就很容易的翻译成代码了:
Code:
Read_Sector:
;C function proto(REAL MODE,SHORT CALL):
;void Read_Sector(byte16 first_sector_index,byte16 read_sector_number);
;attention:
;before call this function must let es:bx point to the buffer
push bp
mov bp,sp
push ax
push bx
push cx
push dx
push bx ; 暂存缓冲区偏移
mov ax,[bp + 6] ; 取得要读取的扇区数
push ax ; 暂时保存之
mov ax,[bp + 4] ; 取得要读的逻辑扇区号
mov bl,[BPB_SecPerTrk]
div bl ; 除以每柱面扇区数
mov ch,al
shr ch,1 ; ch<-柱面号
mov dh,al
and dh,1 ; dh<-磁头号
mov cl,ah
inc cl ; cl<-相对扇区号
mov dl,0 ; 读A盘
pop ax ; al<-要读的扇区数
pop bx ; 恢复缓冲区偏移
.Go_On_Reading:
mov ah,2 ; ah<-功能号
int 13h
jc .Go_On_Reading ; 如果cf=1,继续读
pop dx
pop cx
pop bx
pop ax
pop bp
ret
为了验证这个函数的正确性,下面来测试一下,准备一个空白的软盘映像,在第1个扇区的第267个字节写入A的ASCII码,也就是逻辑地址30ah,如图所示:
![](http://blog.csdn.net/userfiles/未命名(144).jpg)
下面是完整代码,把第3个扇区读进1个512字节的缓冲,然后把A显示出来,另外还加了预处理宏,方便在DOS下调试或直接引导:
Code:
%define _BOOT_DEBUG_ ; 做 Boot Sector 时一定将此行注释掉!将此行打开后用 nasm Boot.asm -o Boot.com 做成一个.COM文件易于调试
%ifdef _BOOT_DEBUG_
org 0100h ; 调试状态, 做成 .COM 文件, 可调试
%else
org 07c00h ; Boot 状态, Bios 将把 Boot Sector 加载到 0:7C00 处并开始执行
%endif
jmp short LABEL_START ; Start to boot.
nop ; 这个 nop 不可少
; 下面是 FAT12 磁盘的头
BS_OEMName db 'MarcusX ' ; OEM String, 必须 8 个字节
BPB_BytsPerSec dw 512 ; 每扇区字节数
BPB_SecPerClus db 1 ; 每簇多少扇区
BPB_RsvdSecCnt dw 1 ; Boot 记录占用多少扇区
BPB_NumFATs db 2 ; 共有多少 FAT 表
BPB_RootEntCnt dw 224 ; 根目录文件数最大值
BPB_TotSec16 dw 2880 ; 逻辑扇区总数
BPB_Media db 0xF0 ; 媒体描述符
BPB_FATSz16 dw 9 ; 每FAT扇区数
BPB_SecPerTrk dw 18 ; 每磁道扇区数
BPB_NumHeads dw 2 ; 磁头数(面数)
BPB_HiddSec dd 0 ; 隐藏扇区数
BPB_TotSec32 dd 0 ; wTotalSectorCount为0时这个值记录扇区数
BS_DrvNum db 0 ; 中断 13 的驱动器号
BS_Reserved1 db 0 ; 未使用
BS_BootSig db 29h ; 扩展引导标记 (29h)
BS_VolID dd 0 ; 卷序列号
BS_VolLab dd 'MarcusOs0.2' ; 卷标, 必须 11 个字节
BS_FileSysType db 'FAT12 ' ; 文件系统类型, 必须 8个字节
LABEL_START:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,7c00h
mov bx,LABEL_BUFFER
push 1
push 1
call Read_Sector
add sp,4
mov ax,0b800h
mov gs,ax
mov ah,0ch
mov al,[LABEL_BUFFER + 266]
mov [gs:140],ax
mov ax,4c00h
int 21h
LABEL_BUFFER:
times 512 db 0
Read_Sector:
;C function proto(REAL MODE,SHORT CALL):
;void Read_Sector(byte16 first_sector_index,byte16 read_sector_number);
;attention:
;before call this function must let es:bx point to the buffer
push bp
mov bp,sp
push ax
push bx
push cx
push dx
push bx ; 暂存缓冲区偏移
mov ax,[bp + 6] ; 取得要读取的扇区数
push ax ; 暂时保存之
mov ax,[bp + 4] ; 取得要读的逻辑扇区号
mov bl,[BPB_SecPerTrk]
div bl ; 除以每柱面扇区数
mov ch,al
shr ch,1 ; ch<-柱面号
mov dh,al
and dh,1 ; dh<-磁头号
mov cl,ah
inc cl ; cl<-相对扇区号
mov dl,0 ; 读A盘
pop ax ; al<-要读的扇区数
pop bx ; 恢复缓冲区偏移
.Go_On_Reading:
mov ah,2 ; ah<-功能号
int 13h
jc .Go_On_Reading ; 如果cf=1,继续读
pop dx
pop cx
pop bx
pop ax
pop bp
ret
运行结果如图:
![](http://blog.csdn.net/userfiles/未命名(145).jpg)
相关文章推荐
- 前端复习--很深入地理解对象(从实现bind函数入手)
- 算法竞赛入门经典(第二版)-刘汝佳-第四章 函数与递归 追踪电子表格中的单元格Uva512
- 1.6 给出一张图片,表示为NXN的居然,每个像素点为4字节。写一个函数实现将这张图片旋转90°。
- 请编写实现malloc()内存分配函数功能一样的代码。给出一个函数来复制两个字符串A和B。字符串A的后几个字节和字符串B的前几个字节重叠。
- [李景山php] 深入理解PHP内核[读书笔记]--第四章:函数的实现 --函数的定义
- [李景山php] 深入理解PHP内核[读书笔记]--第四章:函数的实现 --函数的参数-1
- [李景山php] 深入理解PHP内核[读书笔记]--第四章:函数的实现 --函数间的转换
- [李景山php] 深入理解PHP内核[读书笔记]--第四章:函数的实现 --函数的调用和执行
- 函数声明第四章利用函数实现指定的功能
- 大一C语言初学者的期末复习: 求3个数的极大值,用函数调用实现:int max(int x, int y)
- 实验 4.2.4 实现文件复制 1. 用文件流的 I/O 函数实现一个文件拷贝程序,将一个文件拷贝到另一个文件。 2. 分别用字节读,行读,任意大小读的方式。
- C语言接口与实现【第四章】 setjmp/longjmp非局部跳转函数分析
- 100430 非512字节扇区跳转WinHex脚本
- [李景山php] 深入理解PHP内核[读书笔记]--第四章:函数的实现 --函数的返回值
- 第四章 利用函数实现指定的功能
- 第四章 利用函数实现指定的功能
- 2.C语言实现函数void *memmove(void *dest, const void *src, size_t n)。memmove 函数的功能是拷贝src所指的内存内容前n个字节到dest所指
- [李景山php] 深入理解PHP内核[读书笔记]--第四章:函数的实现 --简介
- 深入理解PHP内核[读书笔记]--第四章:函数的实现 --匿名函数及闭包
- java开发操作系统内核:让内核突破512字节的限制