转载自
https://blog.csdn.net/guzhou_diaoke/article/details/8436037
注:以下程序为原创,若发现任何BUG,欢迎指正;若有问题,欢迎交流;权利归原作者所有,若转载,请注明出处;若能有益于一二访客,幸甚。
昨天学习了VGA显示的一些东西,今天准备学习一下读取软盘的知识。
1.babyos将使用的引导过程
1)系统上电或reset时,处理器执行一些初始化,CPU处于实模式2)处理器会执行一个位于已知位置处的代码,PC中这个位置位于BIOS,它保存在主板上的闪存中
3)控制权交给BIOS后,它寻找一个可引导的设备(软盘、硬盘等),BIOS读取引导扇区(512字节)到内存0x7c00处,并跳转到该地址执行
4)引导扇区中存放的指令可以使用BIOS中断,它将会读取软盘中内核部分到一个临时地址(如0x10000,不覆盖0x7c00处的boot代码即可)
5)将内核前512字节(load.s, 它主要负责将内核剩余部分拷贝到load.s后面)移动到0x0处,将GDT拷贝到0x80000处。为什么不一次全部将内核放到0x0处呢?因为内核可能较大,会覆盖掉0x7c00处的代码。
6)开启A20总线,置位CR0的bit 0,开启保护模式,加载GDT到GDTR,跳转到GDT第二项(第一项为空GDT),即load.s处执行
7)load.s将内核剩余部分移动到load.s后面,即0x200开始的地址处。然后执行初始化代码。
8)初始化代码,至此系统启动成功。
所以首当其冲的问题就是如何读软盘。
2.软盘的结构
3.5寸1.44M 软盘,如图floppy_struct.png 所示,有两个磁头,正反两面各一个;80个磁道(即80个圆圈);每个磁道有18个扇区;每个扇区为512字节。容量 = 512字节/扇区 * 2面 * 80磁道(柱面)/面 * 18扇区/磁道 = 1440 KB
磁头,即面:编号[0, 1]
80个磁道,即柱面(圆圈):编号[0, 79]
18个扇区:编号[1, 18]
相对扇区号[0, 2879]:
相对扇区号按照柱面排序,即从最外头的圆圈到最里头的圆圈。
0柱面正面(即磁头号为0)的1-18扇区为0-17号相对扇区,0柱面反面(即磁头号为2)的1-18扇区为18-35号相对扇区,然后是1柱面,2柱面,直到79柱面。如下:
-
0柱面,
0磁头,
1扇区
0
-
0柱面,
0磁头,
2扇区
1
-
……
-
0柱面,
0磁头,
18扇区
17
-
0柱面,
1磁头,
1扇区
18
-
……
-
0柱面,
1磁头,
18扇区
35
-
1柱面,
0磁头,
1扇区
36
-
……
-
1柱面,
0磁头,
18扇区
53
-
1柱面,
1磁头,
1扇区
54
-
……
-
1柱面,
1磁头,
18扇区
71
-
2柱面,
0磁头,
1扇区
72
-
……
3.利用BIOS 中断读取软盘
-
-------------------------------------------------------------------
-
INT
0x13,功能
02
-
-----------------------------------------------------------
-
参数:
-
AH
02
-
AL 读取扇区数
-
CH 柱面[
0,
79]
-
CL 扇区[
1,
18]
-
DH 磁头[
0,
1]
-
DL 驱动器(
0x0 ~
0x7f表示软盘,
0x80 ~
0xff表示硬盘)
-
ES:BX 缓冲区地址,即数据读到这里
-
返回值:
-
CF =
0表示操作成功,此时AH=
0,AL=传输的扇区数
-
CF =
1即carry位置位(可用JC表示跳转)表示操作失败,AH=状态代码
-
--------------------------------------------------------------------
4.相对扇区号的计算
1)知道柱面号,磁头号,扇区号计算相对扇区号由上面可知0号柱面包含了相对扇区号[0,35],1号柱面包含相对扇区号[36,71],依次类推。
设相对扇区号为N,则
柱面号CH = N / 36;
令x = N % 36;
则x范围为[0,35],其中[0,17] 为磁头号0, [18,35]为磁头号1.
则磁头号DH = x / 18;
零y = x % 18; y范围[0, 17]
则扇区号CL = y + 1。
2)知道相对扇区号,计算柱面号、磁头号、扇区号
N = 36*CH + 18*DH + CL;
由此式子,也可计算:
CH = N / 36
DH = (N % 36) / 18
CL = (N % 36) % 18 + 1
5.读取一个扇区
实验:将一些数据写入软盘的第二个扇区(第一个扇区是引导扇区),然后用BIOS 中断读取该扇区的数据,并显示在屏幕上。然后看读取的数据是否与写入的数据相同。注:第二个扇区相对扇区号为1.
-
写数据的C代码:
-
/*************************************************************************
-
> File: write_data.c
-
> Author: 孤舟钓客
-
> Mail: guzhoudiaoke@126.com
-
> Time: 2012年12月26日 星期三 01时20分26秒
-
************************************************************************/
-
-
#include <stdio.h>
-
#include <string.h>
-
-
int main()
-
{
-
FILE *fp;
-
fp = fopen(
"./data",
"wb");
-
-
int i;
-
char *str =
"baby os, guzhoudiaoke@126.com ";
-
int len =
strlen(str);
-
-
for (i =
0; i < len; i++)
-
fprintf(fp,
"%c", str[i]);
-
-
for (i =
512-len; i >
0; i--)
-
fprintf(fp,
"%c", i %
26 +
'A');
-
-
return
0;
-
}
-
-
汇编代码:
-
# This program draws color pixels at mode
0x13
-
#
2012
-12
-26
01:
31
-
# guzhoudiaoke@126.com
-
-
.include
"boot.inc"
-
-
.section .text
-
.global _start
-
.code16
-
-
_start:
-
jmp main
-
-
#--------------------------------------------------------------
-
# 清屏函数:
-
# 设置屏幕背景色,调色板的索引
0指代的颜色为背景色
-
clear_screen: # 清屏函数
-
movb $
0x06, %ah # 功能号
0x06
-
movb $
0, %al # 上卷全部行,即清屏
-
movb $
0, %ch # 左上角行
-
movb $
0, %ch # 左上角列
-
movb $
24, %dh # 右下角行
-
movb $
79, %dl # 右下角列
-
movb $
0x07, %bh # 空白区域属性
-
int $
0x10
-
ret
-
-
#---------------------------------------------------------------
-
# 直接写显存显示一些文字函数:
-
# 调用前需要设置DS:SI为源地址,DI为显示位置,
-
# CX 为显示的字符个数, AL为颜色属性
-
draw_some_text:
-
# ES:DI is the dst address, DS:SI is the src address
-
movw $VIDEO_SEG_TEXT, %bx
-
movw %bx, %es
-
-
copy_a_char:
-
movsb
-
stosb
-
loop copy_a_char
-
ret
-
-
#----------------------------------------------------------------
-
# 读取软盘第二个扇区:
-
# 使用BIOS INT
0x13中断,使用前需要设置ES:BX作为缓冲区
-
read_one_sect:
-
movb $
0x02, %ah # 功能号
-
movb $
0x01, %al # 读取扇区数
-
movb $
0x00, %ch # 柱面号
-
movb $
0x02, %cl # 扇区号
-
movb $
0x00, %dh # 磁头号
-
movb $
0x00, %dl # 驱动器号
-
-
re_read: # 若调用失败则重新调用
-
int $
0x13
-
jc re_read # 若进位位(CF)被置位,表示调用失败
-
-
ret
-
-
main:
-
movw %cx, %ax
-
movw %ax, %ds
-
movw %ax, %es
-
-
call clear_screen # 清屏
-
-
movw $
0, %ax
-
movw %ax, %ds
-
leaw msg_str, %si
-
xorw %di, %di
-
movw msg_len, %cx
-
movb $TEXT_COLOR,%al
-
call draw_some_text # 绘制字符串
-
-
movw $BUFFER_SEG,%ax
-
movw %ax, %es # ES:BX 为缓冲区地址
-
xorw %bx, %bx
-
call read_one_sect
-
-
# 下面调用绘制函数,在屏幕上显示读取的信息
-
movw $BUFFER_SEG,%ax
-
movw %ax, %ds
# ds:si 为源地址
-
xorw %si, %si
-
movw $
160, %di # 第一行已经打印了msg_str,从第二行开始显示
-
movw $
512, %cx # 显示
512个字符
-
movb $
0x01, %al
-
call draw_some_text
-
-
1:
-
jmp
1b
-
-
msg_str:
-
.asciz
"The data of the second sect of the floppy (sect 1):"
-
msg_len:
-
.
int . - msg_str -
1
-
-
.org
0x1fe,
0x90
-
.word
0xaa55
实验结果:
6.读取任意扇区(给定相对扇区号)
实验,写用C语言写入文件,该文件包含512个‘a’,512个1……512个‘z’, 循环50次,将该文件写入软盘(相对扇区号1~50*26),然后读取给定的相对扇区号的扇区,将读取的内容打印到屏幕上。并与写入的数据比较,验证读取的正确性。
-
C代码用于写文件:
-
/*************************************************************************
-
> File: write_data.c
-
> Author: 孤舟钓客
-
> Mail: guzhoudiaoke@126.com
-
> Time: 2012年12月26日 星期三 20时16分45秒
-
************************************************************************/
-
-
#include <stdio.h>
-
#include <string.h>
-
-
int main(int argc, char *argv[])
-
{
-
if (argc !=
2)
-
{
-
printf(
"usage: ./write_data file_name");
-
exit(
0);
-
}
-
-
FILE *fp;
-
fp = fopen(argv[
1],
"wb");
-
-
int i, j, k;
-
-
for (i =
0; i <
50; i++)
-
{
-
for (j =
'a'; j <=
'z'; j++)
-
{
-
for (k =
0; k <
512; k++)
-
{
-
fprintf(fp,
"%c", (
char)j);
-
}
-
}
-
}
-
-
return
0;
-
}
-
-
汇编代码:
-
# This program draws color pixels at mode
0x13
-
#
2012
-12
-26
20:
23:
42
-
# guzhoudiaoke@126.com
-
-
.include
"boot.inc"
-
-
.section .text
-
.global _start
-
.code16
-
-
_start:
-
jmp main
-
-
#--------------------------------------------------------------
-
# 清屏函数:
-
# 设置屏幕背景色,调色板的索引
0指代的颜色为背景色
-
clear_screen: # 清屏函数
-
movb $
0x06, %ah # 功能号
0x06
-
movb $
0, %al # 上卷全部行,即清屏
-
movb $
0, %ch # 左上角行
-
movb $
0, %ch # 左上角列
-
movb $
24, %dh # 右下角行
-
movb $
79, %dl # 右下角列
-
movb $
0x07, %bh # 空白区域属性
-
int $
0x10
-
-
ret
-
-
#---------------------------------------------------------------
-
# 直接写显存显示一些文字函数:
-
# 调用前需要设置DS:SI为源地址,DI为在屏幕上的显示位置,
-
# CX 为显示的字符个数, AL为颜色属性
-
draw_some_text:
-
# ES:DI is the dst address, DS:SI is the src address
-
movw $VIDEO_SEG_TEXT, %bx
-
movw %bx, %es
-
-
copy_a_char:
-
movsb
-
stosb
-
loop copy_a_char
-
-
ret
-
-
#----------------------------------------------------------------
-
# 读取软盘一个扇区:
-
# 使用BIOS INT
0x13中断,使用前需要设置ES:BX作为缓冲区
-
# AX为相对扇区号
-
read_one_sect:
-
movb $
36, %dl
-
divb %dl
-
movb %al, %ch # 柱面号=N /
36, 假设x = N %
36
-
-
movb %ah, %al # AL = N %
36
-
movb $
0, %ah # AX = N %
36
-
movb $
18, %dl
-
divb %dl
-
movb %al, %dh # 磁头号DH = x /
18
-
movb %ah, %cl
-
incb %cl # 扇区号CL = x %
18 +
1
-
-
movb $
0x00, %dl # 驱动器号DL
-
-
movb $
0x02, %ah # 功能号
-
movb $
0x01, %al # 读取扇区数
-
-
re_read: # 若调用失败则重新调用
-
int $
0x13
-
jc re_read # 若进位位(CF)被置位,表示调用失败
-
-
ret
-
-
#-------------------------------------------------------------------
-
# 该函数读取指定的若干扇区号
-
# 需要指定ES:BX作为缓冲区
-
read_sects:
-
movw $
0x00, %si # 已经读取的扇区数
-
leaw sect_no, %di
-
1:
-
movw (%di), %ax # 获取相对扇区号
-
addw $
2, %di
-
-
call read_one_sect
-
-
-
incw %si
-
incw %bx
-
cmpw num_to_read, %si
-
jne
1b
-
-
ret
-
-
main:
-
movw %cx, %ax
-
movw %ax, %ds
-
movw %ax, %es
-
-
call clear_screen # 清屏
-
-
# 显示提示信息
-
movw $
0, %ax
-
movw %ax, %ds
-
leaw msg_str, %si
-
xorw %di, %di
-
movw msg_len, %cx
-
movb $TEXT_COLOR,%al
-
call draw_some_text # 绘制字符串
-
-
# 读取软盘
-
movw $BUFFER_SEG, %ax
-
movw %ax, %es # ES:BX 为缓冲区地址
-
xorw %bx, %bx
-
call read_sects
-
-
# 在屏幕上显示读取的信息
-
# movw $BUFFER_SEG,%ax
-
# movw %ax, %ds # ds:si 为源地址
-
# movw $
0, %si
-
# movw $
320, %di # 第一行已经打印了msg_str,从第二行开始显示
-
# movw $
512, %cx # 显示字符数
-
# movb $
0x01, %al
-
# call draw_some_text
-
-
# 将缓冲区中前data_len个字节拷贝到data_save
-
xorw %ax, %ax
-
movw %ax, %ds
-
movw num_to_read,%cx
-
-
movw $BUFFER_SEG,%ax
-
movw %ax, %ds
-
xorw %ax, %ax
-
movw %ax, %es
-
movw $
0, %si
-
movw $data_save, %di
-
-
cld
-
rep movsb
-
-
# 下面调用绘制函数,在屏幕上显示读取的信息
-
xorw %ax, %ax
-
movw %ax, %ds # ds:si 为源地址
-
leaw data_save, %si
-
movw $
160, %di # 第一行已经打印了msg_str,从第二行开始显示
-
movw num_to_read,%cx # 显示字符数
-
movb $
0x01, %al
-
call draw_some_text
-
-
1:
-
jmp
1b
-
-
msg_str:
-
.asciz
"The data read from floppy:"
-
msg_len:
-
.
short . - msg_str -
1
-
-
sect_no:
-
# 下面的扇区数据为:
"babyosguzhoudiaoke"
-
# sect:
2+
26*
1,
1+
26*
2,
2+
26*
3,
25+
26*
4,
15+
26*
5,
19+
26*
6,
-
#
7+
26*
11,
21+
26*
12,
26+
26*
13,
8+
26*
14,
15+
26*
15,
21+
26*
16,
-
#
4+
26*
31,
9+
26*
32,
1+
26*
33,
15+
26*
34,
11+
26*
35,
5+
26*
36
-
.
short
28,
53,
80,
129,
145,
175
-
.
short
293,
333,
364,
372,
379,
411
-
.
short
810,
841,
859,
899,
921,
941
-
num_to_read:
-
.
short
18
-
-
data_save:
-
.asciz
"XXXXXXXXXXXXXXXXXX"
-
-
.org
0x1fe,
0x90
-
.word
0xaa55