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

来源:互联网 发布:国二c语言题库 编辑:程序博客网 时间:2024/05/21 10:31

注:以下程序为原创,若发现任何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扇区00柱面,0磁头,2扇区1……0柱面,0磁头,18扇区170柱面,1磁头,1扇区18……0柱面,1磁头,18扇区351柱面,0磁头,1扇区36……1柱面,0磁头,18扇区531柱面,1磁头,1扇区54……1柱面,1磁头,18扇区712柱面,0磁头,1扇区72……

3.利用BIOS 中断读取软盘

-------------------------------------------------------------------INT 0x13,功能02-----------------------------------------------------------参数:AH02AL读取扇区数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:jmpmain#--------------------------------------------------------------# 清屏函数:#设置屏幕背景色,调色板的索引0指代的颜色为背景色clear_screen:# 清屏函数movb$0x06,%ah# 功能号0x06movb$0,%al# 上卷全部行,即清屏movb$0,%ch# 左上角行movb$0,%ch# 左上角列movb$24,%dh# 右下角行movb$79,%dl# 右下角列movb$0x07,%bh# 空白区域属性int$0x10ret#---------------------------------------------------------------# 直接写显存显示一些文字函数:#调用前需要设置DS:SI为源地址,DI为显示位置,#CX 为显示的字符个数, AL为颜色属性draw_some_text:# ES:DI is the dst address, DS:SI is the src addressmovw$VIDEO_SEG_TEXT,%bxmovw%bx,%escopy_a_char:movsbstosbloopcopy_a_charret#----------------------------------------------------------------# 读取软盘第二个扇区:#使用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$0x13jcre_read# 若进位位(CF)被置位,表示调用失败retmain:movw%cx,%axmovw%ax,%dsmovw%ax,%escallclear_screen# 清屏movw$0,%axmovw%ax,%dsleawmsg_str,%sixorw%di,%dimovwmsg_len,%cxmovb$TEXT_COLOR,%alcalldraw_some_text# 绘制字符串movw$BUFFER_SEG,%axmovw%ax,%es# ES:BX 为缓冲区地址xorw%bx,%bxcallread_one_sect# 下面调用绘制函数,在屏幕上显示读取的信息movw$BUFFER_SEG,%axmovw%ax,%ds# ds:si 为源地址xorw%si,%simovw$160,%di# 第一行已经打印了msg_str,从第二行开始显示movw$512,%cx# 显示512个字符movb$0x01,%alcalldraw_some_text1:jmp1bmsg_str:.asciz"The data of the second sect of the floppy (sect 1):"msg_len:.int. - msg_str - 1.org0x1fe,0x90.word0xaa55

实验结果:



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:jmpmain#--------------------------------------------------------------# 清屏函数:#设置屏幕背景色,调色板的索引0指代的颜色为背景色clear_screen:# 清屏函数movb$0x06,%ah# 功能号0x06movb$0,%al# 上卷全部行,即清屏movb$0,%ch# 左上角行movb$0,%ch# 左上角列movb$24,%dh# 右下角行movb$79,%dl# 右下角列movb$0x07,%bh# 空白区域属性int$0x10ret#---------------------------------------------------------------# 直接写显存显示一些文字函数:#调用前需要设置DS:SI为源地址,DI为在屏幕上的显示位置,#CX 为显示的字符个数, AL为颜色属性draw_some_text:# ES:DI is the dst address, DS:SI is the src addressmovw$VIDEO_SEG_TEXT,%bxmovw%bx,%escopy_a_char:movsbstosbloopcopy_a_charret#----------------------------------------------------------------# 读取软盘一个扇区:#使用BIOS INT 0x13中断,使用前需要设置ES:BX作为缓冲区#AX为相对扇区号read_one_sect:movb$36,%dldivb%dlmovb%al,%ch# 柱面号=N / 36, 假设x = N % 36movb%ah,%al# AL = N % 36movb$0,%ah# AX = N % 36movb$18,%dldivb%dlmovb%al,%dh# 磁头号DH = x / 18movb%ah,%clincb%cl# 扇区号CL = x % 18 + 1movb$0x00,%dl# 驱动器号DLmovb$0x02,%ah# 功能号movb$0x01,%al# 读取扇区数re_read:# 若调用失败则重新调用int$0x13jcre_read# 若进位位(CF)被置位,表示调用失败ret#-------------------------------------------------------------------# 该函数读取指定的若干扇区号#需要指定ES:BX作为缓冲区read_sects:movw$0x00,%si# 已经读取的扇区数leawsect_no,%di1:movw(%di),%ax# 获取相对扇区号addw$2,%dicallread_one_sectincw%siincw%bxcmpwnum_to_read,%sijne1bretmain:movw%cx,%axmovw%ax,%dsmovw%ax,%escallclear_screen# 清屏# 显示提示信息movw$0,%axmovw%ax,%dsleawmsg_str,%sixorw%di,%dimovwmsg_len,%cxmovb$TEXT_COLOR,%alcalldraw_some_text# 绘制字符串# 读取软盘movw$BUFFER_SEG,%axmovw%ax,%es# ES:BX 为缓冲区地址xorw%bx,%bxcallread_sects# 在屏幕上显示读取的信息#movw$BUFFER_SEG,%ax#movw%ax,%ds# ds:si 为源地址#movw$0,%si#movw$320,%di# 第一行已经打印了msg_str,从第二行开始显示#movw$512,%cx# 显示字符数#movb$0x01,%al#calldraw_some_text# 将缓冲区中前data_len个字节拷贝到data_savexorw%ax,%axmovw%ax,%dsmovwnum_to_read,%cxmovw$BUFFER_SEG,%axmovw%ax,%dsxorw%ax,%axmovw%ax,%esmovw$0,%simovw$data_save,%dicldrepmovsb# 下面调用绘制函数,在屏幕上显示读取的信息xorw%ax,%axmovw%ax,%ds# ds:si 为源地址leawdata_save,%simovw$160,%di# 第一行已经打印了msg_str,从第二行开始显示movwnum_to_read,%cx# 显示字符数movb$0x01,%alcalldraw_some_text1:jmp1bmsg_str:.asciz"The data read from floppy:"msg_len:.short. - msg_str - 1sect_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.short28,53,80,129,145,175.short293,333,364,372,379,411.short810,841,859,899,921,941num_to_read:.short18data_save:.asciz"XXXXXXXXXXXXXXXXXX".org0x1fe,0x90.word0xaa55