当前位置:   article > 正文

动手写操作系统3----软盘读写逻辑实现_软盘的读写

软盘的读写

        系统从软盘启动,加载软盘第一个扇区作为引导扇区来加载操作系统,第一个扇区大小为512byte,一般用来跳转到操作系统代码起始处,第一个扇区称为引导区,现在来研究一下软盘的物理结构,引导扇区的数据格式以及软盘的读写逻辑实现。

软盘物理结构

软盘的物理结构,以及软盘的数据读取方法

              

        软盘的物理结构如上图,一个盘面被划分成若干个圆圈,例如图中的灰色圆圈,我们称之为磁道,也可以称作柱面,一个磁道或柱面,又被分割成若干部分,每一部分,我们称之为一个扇区,一个扇区的大小正好是512Byte,从而,当我们把数据存储到软盘上时,数据会分解成若干个512Byte大小的块,然后写入到扇区里。要读取数据时,磁头会挪动到扇区所在的磁道或柱面,然后盘面转动,当要读取的扇区转到磁头正下方时,磁头通电,通过电磁效应将扇区的数据读取到内存中。一个磁盘有两个盘面,每个盘面的组成跟右边图形一样,同时每个盘面对应一个磁头,所以当想从磁盘上读取数据时,需要确定数据在哪一个盘面,从而确定要用哪一个磁头来读取数据,然后确定哪一个磁道,最后再确定要读取的数据都存储在哪一个扇区。

       3.5寸软盘,有两个盘面head,对应两个磁头,每个盘面有80个磁道cylinder,也就是柱面,编号分别为0-79. 每个柱面都有18个扇区sector,编号分别为1-18. 所以一个盘面可以存储的数据量大小为:
512 * 18 * 80
       一个软盘有两个盘面,因此一个软盘可以存储的数据为:
2 * 512 * 18 * 80 = 1474560 Byte = 1440 KB

引导扇区数据格式

       引导扇区是软盘的第0个扇区,数据结构叫BPB(BIOS Parameter Block)

其中BPB_开头的属于BPB,以BS_开头的只是BOOT Sector的一部分,不属于BPB。

名称开始字节长度内容参考值
BS_jmpBOOT03一个短跳转指令jmp Label_07c00H
nop
BS_OEMName38厂商名'QingFeng'
BPB_BytesPerSec112每扇区字节数(Bytes/Sector)0x200
BPB_SecPerClus131每簇扇区数(Sector/Cluster)0x1
BPB_ResvdSecCnt142Boot记录占用多少扇区ox1
BPB_NumFATs161共有多少FAT表0x2
BPB_RootEntCnt172根目录区文件最大数0xE0
BPB_TotSec16192扇区总数0xB40[2*80*18]
BPB_Media211介质描述符0xF0
BPB_FATSz16222每个FAT表所占扇区数0x9
BPB_SecPerTrk242每磁道扇区数(Sector/track)0x12
BPB_NumHeads262磁头数(面数)0x2
BPB_HiddSec284隐藏扇区数0
BPB_TotSec32324如果BPB_TotSec16=0,则由这里给出扇区数0
BS_DrvNum361INT 13H的驱动器号0
BS_Reserved1371保留,未使用0
BS_BootSig381扩展引导标记(29h)0x29
BS_VolID394卷序列号0
BS_VolLab4311卷标'QingFeng'
BS_FileSysType548文件系统类型'FAT12'
引导代码及其他内容62448引导代码及其他数据引导代码(剩余空间用0填充)
结束标志0x55AA5102第510字节为0x55,第511字节为0xAA0x55AA

参考以上格式来理解一下hello world汇编代码

  1. org 0x7c00 ;CPU上电第一条指令位置
  2. jmp entry ;跳转到程序入口处
  3. db 0x90
  4. DB "OSKERNEL" ;厂商名
  5. DW 512 ;每扇区字节数(Bytes/Sector)
  6. DB 1 ;每簇扇区数(Sector/Cluster)
  7. DW 1 ;Boot记录占用多少扇区
  8. DB 2 ;共有多少FAT表
  9. DW 224 ;根目录区文件最大数
  10. DW 2880 ;扇区总数
  11. DB 0xf0 ;介质描述符
  12. DW 9 ;每个FAT表所占扇区数
  13. DW 18 ;每磁道扇区数(Sector/track)
  14. DW 2 ;磁头数(面数)
  15. DD 0 ;隐藏扇区数
  16. DD 2880 ;如果BPB_TotSec16=0,则由这里给出扇区数
  17. DB 0,0,0x29 ;INT 13H的驱动器号,保留,未使用,扩展引导标记(29h)
  18. DD 0xFFFFFFFF ;卷序列号
  19. DB "MYFIRSTOS " ;卷标
  20. DB "FAT12 " ;文件系统类型
  21. RESB 18 ;
  22. entry:
  23. mov ax, 0
  24. mov ss, ax
  25. mov ds, ax
  26. mov es, ax
  27. mov si, msg
  28. putloop:
  29. mov al, [si]
  30. add si, 1
  31. cmp al, 0
  32. je fin
  33. mov ah, 0x0e
  34. mov bx, 15
  35. int 0x10
  36. jmp putloop
  37. fin:
  38. HLT
  39. jmp fin
  40. msg:
  41. DB 0x0a, 0x0a
  42. db "hello, world"
  43. db 0x0a
  44. db 0
  45. TIMES 0x1FE-($-$$) DB 0x00
  46. DB 0x55, 0xAA ;结束标志

初始化寄存器,主要利用中断0x10来显示字符,具体使用说明如下:

  1. 以电传的方式写入字符串(AH=0x13
  2. ------------------------------------------------------------------
  3. INT 0x10功能0x13
  4. --------------------------------------------------------------
  5. 描述:
  6. 以电传打字机的方式显示字符串
  7. 接受参数:
  8. AH 0x13
  9. AL 显示模式
  10. BH 视频页
  11. BL 属性值(如果AL=0x000x01
  12. CX 字符串的长度
  13. DH,DL 屏幕上显示起始位置的行、列值
  14. ES:BP 字符串的段:偏移地址
  15. 返回值:
  16. 显示模式(AL):
  17. 0x00:字符串只包含字符码,显示之后不更新光标位置,属性值在BL中
  18. 0x01:字符串只包含字符码,显示之后更新光标位置,属性值在BL中
  19. 0x02:字符串包含字符码及属性值,显示之后不更新光标位置
  20. 0x03:字符串包含字符码及属性值,显示之后更新光标位置
  21. -------------------------------------------------------------------

软盘读写逻辑C语言实现

利用C语言来实现软盘读写逻辑,首先明确一下C语言操作文件常用api    fopen   fclose    fseek    ftell

  1. 1. FILE *fopen( const char * filename, const char * mode );
  2. 2. int fclose( FILE *fp );
  3. 3. size_t fwrite ( void * ptr, size_t size, size_t count, FILE *fp );
  4. 从内存中的ptr指向的地址开始,将连续n*size字节的内容写入fp文件中。该函数的返回值是实际写入的数据块个数。
  5. 4. size_t fread ( void *ptr, size_t size, size_t count, FILE *fp );
  6.   从文件fp中,连续读取n*size字节的内容,并存入ptr指向的内存空间。该函数的返回值是实际读入的数据块个数。
  7. 5. int fseek( FILE *stream, long offset, int origin );
  8.   stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置
  9. 6. long ftell(File *fp)
  10. 返回文件指针当前位置到文件开头的长度。

软盘读写实现类

floppy.h

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4. //根据文件名创建一个3.5寸虚拟软盘
  5. FILE* initFloppy(char * fileName);
  6. //从一个虚拟软盘fp中读取磁头head,柱面cylinder,扇区sector的数据到buf中
  7. void readFloppy(int cylinder, int head, int sector, FILE *fp, char *buf);
  8. //将buf中数据读取到虚拟软盘fp中读取磁头head,柱面cylinder,扇区sector
  9. void writeFloppy(int cylinder, int head, int sector, FILE *fp, char *buf);

floppy.c

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4. FILE* initFloppy(char *fileName){
  5. FILE *fp = fopen(fileName, "w");
  6. char buf[512];
  7. memset(buf, 0, 512);
  8. for(int cylinder=0; cylinder<80; cylinder++){
  9. for(int head=0; head<2; head++){
  10. for(int sector=1; sector<=18; sector++){
  11. fwrite(buf, 512, 1, fp);
  12. }
  13. }
  14. }
  15. return fp;
  16. }
  17. void readFloppy(int cylinder, int head, int sector, FILE *fp, char *buf){
  18. int index = 18*2*512*cylinder + 18*512*head + 512*(sector-1);
  19. int tmp = (int)ftell(fp);
  20. fseek(fp, index, SEEK_SET);
  21. fread(buf, 512, 1, fp);
  22. fseek(fp, tmp, SEEK_SET);
  23. }
  24. void writeFloppy(int cylinder, int head, int sector, FILE *fp, char *buf){
  25. int index = 18*2*512*cylinder + 18*512*head + 512*(sector-1);
  26. int tmp = (int)ftell(fp);
  27. fseek(fp, index, SEEK_SET);
  28. fwrite(buf, 512, 1, fp);
  29. fseek(fp, tmp, SEEK_SET);
  30. }

模拟2*18*80=2880个扇区,以扇区为单位来进行读写,可以将汇编语言得到的文件写到虚拟软盘上来制作一张虚拟软盘。

makeFloppy.c

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "floppy.h"
  4. int main(int argc, char *argv[]){
  5. FILE* src = fopen("boot", "r");
  6. FILE* img = initFloppy("system.img");
  7. if(src == NULL){
  8. printf("The file not found");
  9. exit(0);
  10. }
  11. char buf[512];
  12. memset(buf, 0, 512);
  13. fread(buf, 512, 1, src);
  14. writeFloppy(0, 0, 1, img, buf);
  15. fclose(src);
  16. }

gcc编译以上文件

gcc makeFloppy.c floppy.c -o makeFloppy

执行可执行文件makeFloppy可以得到最终的虚拟软盘文件system.img,格式如下:

                                  

汇编语言读写软盘扇区

上述屏幕打印文件直接写在引导扇区,下面增强一下功能,将要显示的文件存放于非引导扇区,系统启动从其他扇区读取数据显示到屏幕中。系统读取软盘数据要使用13H中断,具体参数说明如下:

  1. 功能描述:读扇区
  2. 入口参数:AH=02H
  3. AL=扇区数
  4. CH=柱面
  5. CL=扇区
  6. DH=磁头
  7. DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘
  8. ES:BX=缓冲区的地址
  9. 出口参数:CF0——操作成功,AH=00H,AL=传输的扇区数,否则,AH=状态代码,参见功能号01H中的说明

       程序初始化在内存中申请64字节空间缓冲区,然后利用INT 13H中断将软盘中某个扇区数据读取到缓冲区,之前利用INT 10H中断将缓冲区数据显示到屏幕上。

汇编代码如下:

  1. org 0x7c00
  2. jmp entry
  3. db 0x90
  4. DB "OSKERNEL"
  5. DW 512
  6. DB 1
  7. DW 1
  8. DB 2
  9. DW 224
  10. DW 2880
  11. DB 0xf0
  12. DW 9
  13. DW 18
  14. DW 2
  15. DD 0
  16. DD 2880
  17. DB 0,0,0x29
  18. DD 0xFFFFFFFF
  19. DB "MYFIRSTOS "
  20. DB "FAT12 "
  21. RESB 18
  22. entry:
  23. mov ax, 0
  24. mov ss, ax
  25. mov ds, ax
  26. mov es, ax
  27. mov si, msg
  28. readFloppy:
  29. mov ch, 1 ;磁道号cylinder
  30. mov dh, 0 ;磁头号head
  31. mov cl, 2 ;扇区号sector
  32. mov bx, msg ;数据存储缓冲区
  33. mov ah, 0x02 ;读扇区
  34. mov al, 1 ;连续读取扇区数量
  35. mov dl, 0 ;驱动器编号
  36. INT 0x13 ;调用BIOS中断
  37. jc error
  38. putloop:
  39. mov al, [si]
  40. add si, 1
  41. cmp al, 0
  42. je fin
  43. mov ah, 0x0e
  44. mov bx, 15
  45. int 0x10
  46. jmp putloop
  47. fin:
  48. HLT
  49. jmp fin
  50. error:
  51. mov si, errmsg
  52. jmp putloop
  53. msg:
  54. RESB 64
  55. errmsg:
  56. DB "error"
  57. TIMES 0x1FE-($-$$) DB 0x00
  58. DB 0x55, 0xAA

主要增加了从磁头0 磁道1 扇区2位置处读取数据到缓冲区msg,调用BIOS中断来显示字符串。

往软盘磁头0 磁道1 扇区2位置处写入字符串

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "floppy.h"
  4. #include <string.h>
  5. int main(int argc, char *argv[]){
  6. printf("src %s \n", argv[1]);
  7. FILE* src = fopen(argv[1], "r");
  8. printf("img %s \n", argv[2]);
  9. FILE* img = initFloppy(argv[2]);
  10. if(src == NULL){
  11. printf("The file not found");
  12. exit(0);
  13. }
  14. //写入引导扇区
  15. char buf[512];
  16. memset(buf, 0, 512);
  17. fread(buf, 512, 1, src);
  18. writeFloppy(0, 0, 1, img, buf);
  19. //head0 cylinder1 sector2 写入字符串
  20. memset(buf, 0, 512);
  21. char str[100] = {"Hello World cylinder1 sector2"};
  22. strncpy(buf, str, 100);
  23. writeFloppy(1, 0, 2, img, buf);
  24. fclose(src);
  25. }

为了便于执行将程序简单修改一下,搞了个简单脚本  编译汇编代码  编译c语言  制作软盘文件

  1. #!/bin/bash
  2. nasm boot.asm
  3. echo "nasm boot.asm"
  4. gcc floppy.c makeFloppy.c -o makeFloppy
  5. echo "gcc floppy.c makeFloppy.c -o makeFloppy"
  6. ./makeFloppy boot system.img
  7. echo "./makeFloppy boot system.img"

利用virtualBox加载软盘文件结果如下:             

 

代码位置:https://github.com/ChenWenKaiVN/bb_os_core/tree/develop

BIOS中断总结

中断号功能说明
INT 10H

显示服务(Video Service——INT 10H)

INT 13H

直接磁盘服务(Direct Disk Service——INT 13H)

INT 14H

串行口服务(Serial Port Service——INT 14H)

INT 15H

杂项系统服务(Miscellaneous System Service——INT 15H)

INT 16H

键盘服务(Keyboard Service——INT 16H)

INT 17H

并行口服务(Parallel Port Service——INT 17H)

INT 1AH

时钟服务(Clock Service——INT 1AH)

直接系统服务(Direct System Service)

INT 00H —“0”作除数
INT 01H —单步中断
INT 02H —非屏蔽中断(NMI)
INT 03H —断点中断
INT 04H —算术溢出错误
INT 05H —打印屏幕和BOUND越界
INT 06H —非法指令错误
INT 07H —处理器扩展无效
INT 08H —时钟中断
INT 09H —键盘输入
INT 0BH —通信口(COM2:)
INT 0CH —通信口(COM1:)
INT 0EH —磁盘驱动器输入/输出
INT 11H —读取设备配置
INT 12H —读取常规内存大小(返回值AX为内存容量,以K为单位)
INT 18H —ROM BASIC
INT 19H —重启动系统
INT 1BH —CTRL+BREAK处理程序
INT 1CH —用户时钟服务
INT 1DH —指向显示器参数表指针
INT 1EH —指向磁盘驱动器参数表指针
INT 1FH —指向图形字符模式表指针

参考地址:

https://blog.csdn.net/Zllvincent/article/details/83515294

https://blog.csdn.net/tyler_download/article/details/51815483

https://blog.csdn.net/weixin_34342578/article/details/94067240

https://www.cnblogs.com/sea-stream/p/10850003.html

https://blog.csdn.net/m0_37329910/article/details/86081024

https://blog.csdn.net/BobYuan888/article/details/80153283   C语言文件读写函数总结

https://blog.csdn.net/weixin_37656939/article/details/79684611  BIOS 中断大全

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
  

闽ICP备14008679号