版权声明:本文为博主原创文章,未经博主允许不得转载。
注:以下程序为原创,若发现任何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
- <pre></pre>
- <pre></pre>
-
-
- 原文地址 :http://blog.csdn.net/guzhou_diaoke/article/details/8436037