自己动手写操作系统:4.模拟软盘的数据结构,加载指定软盘扇区中的数据

来源:互联网 发布:苹果电脑怎么使用软件 编辑:程序博客网 时间:2024/05/22 01:37

前面两节介绍了如何在系统加载的时候在屏幕上打印出Hello OS!

我们是使用汇编直接对cpu进行操作,我们的数据容量只有启动扇区的512字节,这样的空间显然不够以后的开发使用。

本节我们引入软盘的数据结构,对软盘的数据结构进行模拟,并加载软盘中指定扇区的内容。

本节按照一下结构组织:

1.介绍软盘的物理以及逻辑结构

2.根据软盘逻辑结构,使用C语言创建软盘映像文件,并把数据写入软盘扇区

3.介绍如何使用汇编读取软盘中扇区的数据

4.实际运行测试



一.介绍软盘的物理以及逻辑结构
以一块软盘为例,一块软盘有两个面,正面和反面,每个面被80个圆环分成80个磁道(1-80),每个磁道(或者称为柱面)由18个扇区(0-17)组成,每个扇区的大小为512字节。
所以一块软盘的大小为 2*80*18*512 = 1474560 字节 = 1440KB = 1.44M

物理地址                        逻辑扇区地址
0面0磁道  1扇区                       0
0面0磁道  2扇区                       1
           :                         :       
0面0磁道 18扇区                       17
1面0磁道  1扇区                       18
1面0磁道  2扇区                       19
           :                          :
1面0磁道18扇区                        35
           :                          :      
我们把逻辑扇区地址可以看成大小为1.44M的数组的下标,每个数组元素的大小为512字节。
所以我们希望能根据 面 磁道 扇区 找到和 逻辑扇区的对应的关系,这样把对 面、磁道、扇区转化为对数组元素的操作,
这样就大大简化了我们的实现难度。
相对扇区号 = {盘面(0~1)*每条磁道扇区数(18)} + {2*磁道(0~79)*每条磁道扇区数(18)} +  {扇区(1-18)-1};

对于我们制作写盘来说知道上面的公式就可以了,补充一点,知道了逻辑扇面号,怎么推断出 扇面 磁道 和 盘面 号呢?
而当知道相对扇区号,怎么推算出盘面、磁道、扇区呢?
具体步骤:
(1)从上面公式可以看出,相对扇区号对每条磁道扇区数(18)取余数 加1 就得到扇区;
(2)再根据(1)得到的商,如果盘面为1则商为奇数,否则为偶数,于是通过判断商的奇偶性,就可以得出盘;同时也可以算出磁道(减去1可得);

二、.根据软盘逻辑结构,使用C语言创建软盘映像文件,并把数据写入软盘扇区

下面是主程序 ReadWrite.c

#include "stdio.h"  #include <stdlib.h>  #include "floppy.h"    #include <stdlib.h>void TestResult(FILE * fp)  {      if(feof(fp))      {          //printf("Read OK\n");  //读取成功不许要打印消息      }      else if(ferror(fp))      {          printf("Read REEOR\n");      }        }  int main()  {      char Shan[512] = {0};      FILE *fpA = NULL;      FILE *fpB = NULL;      int  ret = 0;      int  DateNo = 2*80*18*512;  char * Floppy = NULL;char *meg = "This is message from floppy!" ;          fpA = fopen("A.boot","rb");//1.修改文件名为A.boot 2.b表示打开对象为二进制文件      fpB = fopen("system.img","w+");//目的文件      if(0 != fflush(NULL))      {          printf("flushall REEOR\n");          return 0;      }      if(NULL == fpA || NULL == fpB)      {          printf("open file error\n!");          return 0;      }                 ret = fread(Shan, 1, DateNo,fpA);//读取A文件数据      if(ret != DateNo)      {          TestResult(fpA);      }            Shan[512-1-1] = 0x55;// 写512最后两字字节      Shan[512-1]   = 0xaa;       Floppy = MakeFloppy();if(False == WriteFloppy(0, 0, 1, Floppy, Shan)){printf("WriteFloppy Error!\n");free(Floppy);return 0;}if(False == WriteFloppy(0, 1, 4, Floppy, meg)){printf("WriteFloppy Error!\n");free(Floppy);return 0;}    ret = fwrite(Floppy, 1, DateNo, fpB);//把缓冲文件写入到B文件中      if(ret != DateNo)      {          TestResult(fpB);      }            if(0 != fclose(fpB) && 0 != fclose(fpA) )//关闭所有打开的文件      {          printf("fcloseall REEOR\n");       }    free(Floppy);      return 0;  } 

下面是创建软盘印象文件的具体实现程序floppy.c :
#include <stdio.h> #include <stdlib.h>
#include <string.h>#include "floppy.h"   char * MakeFloppy(void){char * pFloppy = NULL;pFloppy = (char *)malloc(2*80*18*512);if(NULL == pFloppy){printf("malloc error!\n");}else{memset(pFloppy, 0, (2*80*18*512));}return pFloppy;}int CheckPara(char bSide, char bCidao, char bShanmian, char * pFloppy, char * pBuffe){if(NULL != pFloppy && NULL != pBuffe){if(0 == bSide || 1 == bSide){if(bCidao <= 79 && bCidao >= 0){if(bShanmian <= 18 && bShanmian >= 1){return True;}}}}return False;}static void WriteFloppyShanZero(char * pFloppy, char * pBuffe){int ShanNum = 512;int i = 0;for(i=0; i<ShanNum; i++){*pFloppy = *pBuffe;pFloppy++;pBuffe++;}}static void WriteFloppyShanNotZero(char * pFloppy, char * pBuffe){int ShanNum = 0;ShanNum = strlen(pBuffe);ShanNum = ShanNum/512;printf("we will write %d ShanQu\n", ShanNum+1);while(* pBuffe){*pFloppy = *pBuffe;pFloppy++;pBuffe++;}}int WriteFloppy(char bSide, char bCidao, char bShanmian, char * pFloppy, char * pBuffe){int LogicShanMian = 0;if(False == CheckPara(bSide, bCidao, bShanmian, pFloppy, pBuffe)){printf("Para error!\n");return False;}LogicShanMian = bSide * 18 + bCidao * 2 * 18 + (bShanmian - 1);pFloppy += LogicShanMian * 512;//把指针移动到第 LogicShanMian 个扇区开头if(0 == LogicShanMian){WriteFloppyShanZero(pFloppy, pBuffe);//0 扇区写满}else{WriteFloppyShanNotZero(pFloppy, pBuffe);}return True;}


三、介绍如何使用汇编读取软盘中扇区的数据

通过调用BIOS的 0x13 号中断,用于读取软盘数据,

在调用之前需要在相应的寄存器中设置该函数入参:

    mov          CH, 1        ;CH 用来存储磁道号
    mov          DH, 0        ;DH 用来存储磁头号
    mov          CL, 4        ;CL 用来存储扇区号


    mov          AH, 0x02   ; AH = 02 表示要做的是读盘操作
    mov          AL, 2         ; AL 表示要连续读取几个扇区
    mov          DL, 0         ;驱动器编号,一般我们只有一个软盘驱动器,所以写死为0

然后调用中断,具体的程序如下所示:

org  0x7c00;entry:    mov  ax, 0    mov  ss, ax    mov  ds, ax    mov  es, ax    mov  si, msgreadFloppy:    mov          CH, 1        ;CH 用来存储磁道号    mov          DH, 0        ;DH 用来存储磁头号    mov          CL, 4        ;CL 用来存储扇区号    mov          BX, msg       ; ES:BX 数据存储缓冲区    mov          AH, 0x02      ;  AH = 02 表示要做的是读盘操作    mov          AL, 2        ; AL 表示要练习读取几个扇区    mov          DL, 0         ;驱动器编号,一般我们只有一个软盘驱动器,所以写死                                  ;为0    INT          0x13          ;调用BIOS中断实现磁盘读取功能    jc           errorputloop:    mov  al, [si]    add  si, 1    cmp  al, 0    je   fin    mov  ah, 0x0e    mov  bx, 15    int  0x10    jmp  putloopfin:    HLT    jmp  finerror:    mov si, errmsg    jmp   putloopmsg:    RESB   64errmsg:    DB "error"


注:(1)实际上从软盘读取到缓冲区是读的软盘扇区的起始地址,经过测试:在创建的“缓冲区”只有64的时候能也够读到3个扇区的内容。 (2)软盘多扇区的连续读或者写是按照逻辑扇区的顺序进行读写的,因此当创建好软盘之后可以直接往软盘中写入大于一个扇区的内容,超过的部分会自动写入逻辑上连续物理上不连续的扇区中。
4.实际运行测试

我们使用下面的命令来编译执行上面的程序:

A.asm -o A.boot && gcc ReadWrite.c floppy.c -g -o ReadWrite && ./ReadWrite

执行效果如图所示,可以在屏幕上看到我们写入到软盘中的数据:This is message from floppy!




阅读全文
0 0