赞
踩
系统从软盘启动,加载软盘第一个扇区作为引导扇区来加载操作系统,第一个扇区大小为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_jmpBOOT | 0 | 3 | 一个短跳转指令 | jmp Label_07c00H nop |
BS_OEMName | 3 | 8 | 厂商名 | 'QingFeng' |
BPB_BytesPerSec | 11 | 2 | 每扇区字节数(Bytes/Sector) | 0x200 |
BPB_SecPerClus | 13 | 1 | 每簇扇区数(Sector/Cluster) | 0x1 |
BPB_ResvdSecCnt | 14 | 2 | Boot记录占用多少扇区 | ox1 |
BPB_NumFATs | 16 | 1 | 共有多少FAT表 | 0x2 |
BPB_RootEntCnt | 17 | 2 | 根目录区文件最大数 | 0xE0 |
BPB_TotSec16 | 19 | 2 | 扇区总数 | 0xB40[2*80*18] |
BPB_Media | 21 | 1 | 介质描述符 | 0xF0 |
BPB_FATSz16 | 22 | 2 | 每个FAT表所占扇区数 | 0x9 |
BPB_SecPerTrk | 24 | 2 | 每磁道扇区数(Sector/track) | 0x12 |
BPB_NumHeads | 26 | 2 | 磁头数(面数) | 0x2 |
BPB_HiddSec | 28 | 4 | 隐藏扇区数 | 0 |
BPB_TotSec32 | 32 | 4 | 如果BPB_TotSec16=0,则由这里给出扇区数 | 0 |
BS_DrvNum | 36 | 1 | INT 13H的驱动器号 | 0 |
BS_Reserved1 | 37 | 1 | 保留,未使用 | 0 |
BS_BootSig | 38 | 1 | 扩展引导标记(29h) | 0x29 |
BS_VolID | 39 | 4 | 卷序列号 | 0 |
BS_VolLab | 43 | 11 | 卷标 | 'QingFeng' |
BS_FileSysType | 54 | 8 | 文件系统类型 | 'FAT12' |
引导代码及其他内容 | 62 | 448 | 引导代码及其他数据 | 引导代码(剩余空间用0填充) |
结束标志0x55AA | 510 | 2 | 第510字节为0x55,第511字节为0xAA | 0x55AA |
参考以上格式来理解一下hello world汇编代码
- org 0x7c00 ;CPU上电第一条指令位置
-
- jmp entry ;跳转到程序入口处
- db 0x90
- DB "OSKERNEL" ;厂商名
- DW 512 ;每扇区字节数(Bytes/Sector)
- DB 1 ;每簇扇区数(Sector/Cluster)
- DW 1 ;Boot记录占用多少扇区
- DB 2 ;共有多少FAT表
- DW 224 ;根目录区文件最大数
- DW 2880 ;扇区总数
- DB 0xf0 ;介质描述符
- DW 9 ;每个FAT表所占扇区数
- DW 18 ;每磁道扇区数(Sector/track)
- DW 2 ;磁头数(面数)
- DD 0 ;隐藏扇区数
- DD 2880 ;如果BPB_TotSec16=0,则由这里给出扇区数
- DB 0,0,0x29 ;INT 13H的驱动器号,保留,未使用,扩展引导标记(29h)
- DD 0xFFFFFFFF ;卷序列号
- DB "MYFIRSTOS " ;卷标
- DB "FAT12 " ;文件系统类型
- RESB 18 ;
-
- entry:
- mov ax, 0
- mov ss, ax
- mov ds, ax
- mov es, ax
- mov si, msg
-
- putloop:
- mov al, [si]
- add si, 1
- cmp al, 0
- je fin
- mov ah, 0x0e
- mov bx, 15
- int 0x10
- jmp putloop
-
- fin:
- HLT
- jmp fin
-
- msg:
- DB 0x0a, 0x0a
- db "hello, world"
- db 0x0a
- db 0
- TIMES 0x1FE-($-$$) DB 0x00
- DB 0x55, 0xAA ;结束标志
初始化寄存器,主要利用中断0x10来显示字符,具体使用说明如下:
- 以电传的方式写入字符串(AH=0x13)
- ------------------------------------------------------------------
- INT 0x10功能0x13
- --------------------------------------------------------------
- 描述:
- 以电传打字机的方式显示字符串
- 接受参数:
- AH 0x13
- AL 显示模式
- BH 视频页
- BL 属性值(如果AL=0x00或0x01)
- CX 字符串的长度
- DH,DL 屏幕上显示起始位置的行、列值
- ES:BP 字符串的段:偏移地址
- 返回值:
- 无
- 显示模式(AL):
- 0x00:字符串只包含字符码,显示之后不更新光标位置,属性值在BL中
- 0x01:字符串只包含字符码,显示之后更新光标位置,属性值在BL中
- 0x02:字符串包含字符码及属性值,显示之后不更新光标位置
- 0x03:字符串包含字符码及属性值,显示之后更新光标位置
- -------------------------------------------------------------------
利用C语言来实现软盘读写逻辑,首先明确一下C语言操作文件常用api fopen fclose fseek ftell
- 1. FILE *fopen( const char * filename, const char * mode );
- 2. int fclose( FILE *fp );
- 3. size_t fwrite ( void * ptr, size_t size, size_t count, FILE *fp );
- 从内存中的ptr指向的地址开始,将连续n*size字节的内容写入fp文件中。该函数的返回值是实际写入的数据块个数。
- 4. size_t fread ( void *ptr, size_t size, size_t count, FILE *fp );
- 从文件fp中,连续读取n*size字节的内容,并存入ptr指向的内存空间。该函数的返回值是实际读入的数据块个数。
- 5. int fseek( FILE *stream, long offset, int origin );
- stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置
- 6. long ftell(File *fp)
- 返回文件指针当前位置到文件开头的长度。
软盘读写实现类
floppy.h
- #include<stdio.h>
- #include<stdlib.h>
- #include<string.h>
-
- //根据文件名创建一个3.5寸虚拟软盘
- FILE* initFloppy(char * fileName);
-
- //从一个虚拟软盘fp中读取磁头head,柱面cylinder,扇区sector的数据到buf中
- void readFloppy(int cylinder, int head, int sector, FILE *fp, char *buf);
-
- //将buf中数据读取到虚拟软盘fp中读取磁头head,柱面cylinder,扇区sector
- void writeFloppy(int cylinder, int head, int sector, FILE *fp, char *buf);
floppy.c
- #include<stdio.h>
- #include<stdlib.h>
- #include<string.h>
-
- FILE* initFloppy(char *fileName){
- FILE *fp = fopen(fileName, "w");
- char buf[512];
- memset(buf, 0, 512);
- for(int cylinder=0; cylinder<80; cylinder++){
- for(int head=0; head<2; head++){
- for(int sector=1; sector<=18; sector++){
- fwrite(buf, 512, 1, fp);
- }
- }
- }
- return fp;
- }
-
- void readFloppy(int cylinder, int head, int sector, FILE *fp, char *buf){
- int index = 18*2*512*cylinder + 18*512*head + 512*(sector-1);
- int tmp = (int)ftell(fp);
- fseek(fp, index, SEEK_SET);
- fread(buf, 512, 1, fp);
- fseek(fp, tmp, SEEK_SET);
- }
-
- void writeFloppy(int cylinder, int head, int sector, FILE *fp, char *buf){
- int index = 18*2*512*cylinder + 18*512*head + 512*(sector-1);
- int tmp = (int)ftell(fp);
- fseek(fp, index, SEEK_SET);
- fwrite(buf, 512, 1, fp);
- fseek(fp, tmp, SEEK_SET);
- }
模拟2*18*80=2880个扇区,以扇区为单位来进行读写,可以将汇编语言得到的文件写到虚拟软盘上来制作一张虚拟软盘。
makeFloppy.c
- #include <stdio.h>
- #include <stdlib.h>
- #include "floppy.h"
-
- int main(int argc, char *argv[]){
- FILE* src = fopen("boot", "r");
- FILE* img = initFloppy("system.img");
- if(src == NULL){
- printf("The file not found");
- exit(0);
- }
- char buf[512];
- memset(buf, 0, 512);
- fread(buf, 512, 1, src);
- writeFloppy(0, 0, 1, img, buf);
- fclose(src);
- }
gcc编译以上文件
gcc makeFloppy.c floppy.c -o makeFloppy
执行可执行文件makeFloppy可以得到最终的虚拟软盘文件system.img,格式如下:
上述屏幕打印文件直接写在引导扇区,下面增强一下功能,将要显示的文件存放于非引导扇区,系统启动从其他扇区读取数据显示到屏幕中。系统读取软盘数据要使用13H中断,具体参数说明如下:
- 功能描述:读扇区
- 入口参数:AH=02H
- AL=扇区数
- CH=柱面
- CL=扇区
- DH=磁头
- DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘
- ES:BX=缓冲区的地址
- 出口参数:CF=0——操作成功,AH=00H,AL=传输的扇区数,否则,AH=状态代码,参见功能号01H中的说明
程序初始化在内存中申请64字节空间缓冲区,然后利用INT 13H中断将软盘中某个扇区数据读取到缓冲区,之前利用INT 10H中断将缓冲区数据显示到屏幕上。
汇编代码如下:
- org 0x7c00
-
- jmp entry
- db 0x90
- DB "OSKERNEL"
- DW 512
- DB 1
- DW 1
- DB 2
- DW 224
- DW 2880
- DB 0xf0
- DW 9
- DW 18
- DW 2
- DD 0
- DD 2880
- DB 0,0,0x29
- DD 0xFFFFFFFF
- DB "MYFIRSTOS "
- DB "FAT12 "
- RESB 18
-
- entry:
- mov ax, 0
- mov ss, ax
- mov ds, ax
- mov es, ax
- mov si, msg
-
- readFloppy:
- mov ch, 1 ;磁道号cylinder
- mov dh, 0 ;磁头号head
- mov cl, 2 ;扇区号sector
-
- mov bx, msg ;数据存储缓冲区
- mov ah, 0x02 ;读扇区
- mov al, 1 ;连续读取扇区数量
- mov dl, 0 ;驱动器编号
-
- INT 0x13 ;调用BIOS中断
-
- jc error
-
- putloop:
- mov al, [si]
- add si, 1
- cmp al, 0
- je fin
- mov ah, 0x0e
- mov bx, 15
- int 0x10
- jmp putloop
-
- fin:
- HLT
- jmp fin
-
- error:
- mov si, errmsg
- jmp putloop
-
- msg:
- RESB 64
-
- errmsg:
- DB "error"
-
- TIMES 0x1FE-($-$$) DB 0x00
-
- DB 0x55, 0xAA
主要增加了从磁头0 磁道1 扇区2位置处读取数据到缓冲区msg,调用BIOS中断来显示字符串。
往软盘磁头0 磁道1 扇区2位置处写入字符串
- #include <stdio.h>
- #include <stdlib.h>
- #include "floppy.h"
- #include <string.h>
-
- int main(int argc, char *argv[]){
-
- printf("src %s \n", argv[1]);
- FILE* src = fopen(argv[1], "r");
-
- printf("img %s \n", argv[2]);
- FILE* img = initFloppy(argv[2]);
-
- if(src == NULL){
- printf("The file not found");
- exit(0);
- }
-
- //写入引导扇区
- char buf[512];
- memset(buf, 0, 512);
- fread(buf, 512, 1, src);
- writeFloppy(0, 0, 1, img, buf);
-
- //head0 cylinder1 sector2 写入字符串
- memset(buf, 0, 512);
- char str[100] = {"Hello World cylinder1 sector2"};
- strncpy(buf, str, 100);
- writeFloppy(1, 0, 2, img, buf);
- fclose(src);
- }
为了便于执行将程序简单修改一下,搞了个简单脚本 编译汇编代码 编译c语言 制作软盘文件
- #!/bin/bash
- nasm boot.asm
- echo "nasm boot.asm"
- gcc floppy.c makeFloppy.c -o makeFloppy
- echo "gcc floppy.c makeFloppy.c -o makeFloppy"
- ./makeFloppy boot system.img
- echo "./makeFloppy boot system.img"
利用virtualBox加载软盘文件结果如下:
代码位置:https://github.com/ChenWenKaiVN/bb_os_core/tree/develop
中断号 | 功能说明 |
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 中断大全
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。