babyos (三)——利用BIOS INT 0x13读取软盘

版权声明:本文为博主原创文章,未经博主允许不得转载。 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





                                    
展开阅读全文

没有更多推荐了,返回首页