0.简要说明:

我们完全可以使用bochs创建映像文件,如https://blog.csdn.net/jadeshu/article/details/89046838   ,那么为什么还去用C++去模拟文件呢,主要更深刻的理解和自己动手,比直接创建的文件映像更深刻,了解的内容也更多!!!

当然如果想省事的话,您也可以直接用bochs直接创建映射软盘和硬盘文件,然后用dd   ld 等命令把MBR Loader直接写入到该映射文件中对应的扇区中即可!!!

如下:

将mbr.bin文件写入虚拟磁盘boot.img,写入到第一个扇区中。

   dd if=mbr.bin of=boot.img bs=512 count=1 conv=notrunc   

   CHS方式扇区从1开始索引,LBA方式扇区从0开始索引
第一扇区 0~512字节
第二扇区 512~1024字节
第三扇区 1024~1536字节 将loader.bin文件写入虚拟磁盘boot.img,写入偏移2个扇区(即第三个扇区),count写入4块大小文件
即512*2 = 1024
   dd if=loader.bin of=boot.img bs=512 count=4  seek=2 conv=notrunc

建议初学者还是按照提供的内容进行学习!直接模拟硬盘!跟着一步一步做,不容易出错!

1.loader硬盘加载(文本模式)

后面会讲到,单独一节做个小实验,用图形模式去显示图形,我们还是先来看下下面的代码

图形模式小实验入口:https://blog.csdn.net/jadeshu/article/details/103092821

mbr.s

;主引导程序
;------------------------------------------------------------ SECTION MBR vstart=0x7c00
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
mov ax,0xb800
mov gs,ax ; 清屏
;利用0x06号功能,上卷全部行,则可清屏。
; -----------------------------------------------------------
;INT 0x10 功能号:0x06 功能描述:上卷窗口
;------------------------------------------------------
;输入:
;AH 功能号= 0x06
;AL = 上卷的行数(如果为0,表示全部)
;BH = 上卷行属性
;(CL,CH) = 窗口左上角的(X,Y)位置
;(DL,DH) = 窗口右下角的(X,Y)位置
;无返回值:
mov ax, 0600h
mov bx, 0700h
mov cx, 0 ; 左上角: (0, 0)
mov dx, 184fh ; 右下角: (80,25),
; 因为VGA文本模式中,一行只能容纳80个字符,共25行。
; 下标从0开始,所以0x18=24,0x4f=79
int 10h ; int 10h mov eax, 0x2 ; 起始扇区lba地址,从间隔第二个扇区开始
mov bx, 0x900 ; 写入的地址
mov cx, 1 ; 待读入的扇区数,读取1个扇区内容
call rd_disk_m_16 ; 以下读取程序的起始部分(一个扇区) jmp 0x900 ;-------------------------------------------------------------------------------
;功能:读取硬盘n个扇区
rd_disk_m_16:
;-------------------------------------------------------------------------------
; eax=LBA扇区号
; ebx=将数据写入的内存地址
; ecx=读入的扇区数
mov esi,eax ;备份eax
mov di,cx ;备份cx
;读写硬盘:
;第1步:设置要读取的扇区数
mov dx,0x1f2
mov al,cl
out dx,al ;读取的扇区数 mov eax,esi ;恢复ax ;第2步:将LBA地址存入0x1f3 ~ 0x1f6 ;LBA地址7~0位写入端口0x1f3
mov dx,0x1f3
out dx,al ;LBA地址15~8位写入端口0x1f4
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al ;LBA地址23~16位写入端口0x1f5
shr eax,cl
mov dx,0x1f5
out dx,al shr eax,cl
and al,0x0f ;lba第24~27位
or al,0xe0 ; 设置7~4位为1110,表示lba模式
mov dx,0x1f6
out dx,al ;第3步:向0x1f7端口写入读命令,0x20
mov dx,0x1f7
mov al,0x20
out dx,al ;第4步:检测硬盘状态
.not_ready:
;同一端口,写时表示写入命令字,读时表示读入硬盘状态
nop
in al,dx
and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙
cmp al,0x08
jnz .not_ready ;若未准备好,继续等。 ;第5步:从0x1f0端口读数据
mov ax, di
mov dx, 256
mul dx
mov cx, ax ; di为要读取的扇区数,一个扇区有512字节,每次读入一个字,
; 共需di*512/2次,所以di*256
mov dx, 0x1f0
.go_on_read:
in ax,dx
mov [bx],ax
add bx,2
loop .go_on_read
ret ; times 510-($-$$) db 0
; db 0x55,0xaa

其中代码大概意思是:

(1)首先初始化各个段寄存器

(2)清屏,设置为文本模式

(3)读取偏移第二个扇区(也就是后面C++写入文件偏移的2*512=1024位置)的512字节内容加载到0X900地址处,

(4)最后(jmp 0X900)让直接调转到0X900地址处执行

上面汇编代码应用到了LBA逻辑块知识,需要学习的进入https://blog.csdn.net/jadeshu/article/details/89072512

loader.s


section loader vstart=0x900 ; 输出背景色绿色,前景色红色,并且跳动的字符串"Jade OS"
mov byte [gs:0x00],'J'
mov byte [gs:0x01],0xA4 ; A表示绿色背景闪烁,4表示前景色为红色 mov byte [gs:0x02],'a'
mov byte [gs:0x03],0xA4 mov byte [gs:0x04],'d'
mov byte [gs:0x05],0xA4 mov byte [gs:0x06],'e'
mov byte [gs:0x07],0xA4 mov byte [gs:0x08],' '
mov byte [gs:0x09],0xA4 mov byte [gs:0x0a],'O'
mov byte [gs:0x0b],0xA4 mov byte [gs:0x0c],'S'
mov byte [gs:0x0d],0xA4 jmp $ ; 通过死循环使程序悬停在此

程序说明图:

(1)是将硬盘中的MBR加载到内存0X7C00开始的位置,由BIOS完成

(2)mbr.s中将loader加载到内存0X900开始的位置

执行流程 BIOS[CS:IP 0XFFF0:FFF0]--》CS:IP[0:0X7C00]----(MBR将loader加载到0X900)--->CS:IP[0:0X900]---(loader中)

其中代码大概意思是:

起始地址为0x900,上面mbr.s汇编代码指定了文本模式显示,并指定的gs为0XB800,而0XB8000~0XB8FFF地址是在实模式下用于文本模式显示适配器,可以看下面实模式下的内存布局图!

往这段内存处赋值,即可显示相关的字符!

最后进行汇编编译

nasm mbr.S -o mbr                  编译产生mbr文件

        nasm loader.S -o loader          编译产生loader文件

2.C++写入文件并创建

HardDisk.h

#ifndef __HARDDISK_H__
#define __HARDDISK_H__ #define SECTOR_SIZE 512 // 1个扇区占字节大小
#define CYLINDER_COUNT 121 // 柱面
#define SECTORS_COUNT 63 // 1个柱面的扇区数
#include <string> // 用数组存储
class CHardDisk
{
public:
CHardDisk();
~CHardDisk(); // 设置硬盘盘面、柱面、扇区
void setMagneticHead(unsigned int head) { this->head = head; }
void setCylinder(int cylinder) { this->current_cylinder = cylinder; }
void setSector(int sector) { this->current_sector = sector; } // 获取扇区数据
char* getDiskBuffer(unsigned int head, int cylinder_num, int sector_num);
// 将buf数据写入指定扇区
void setDiskBuffer(unsigned int head, int cylinder_num, int sector_num, char* buf);
// 制作映像文件
void makeVirtualDisk(const char* name = "system.img"); void writeFileToDisk(const char* fileName, bool bootable, int cylinder, int beginSec); private:
unsigned int head = 0; // 默认盘面
int current_cylinder = 0; // 当前磁道号
int current_sector = 0; // 当前扇区号
char* disk0[CYLINDER_COUNT][SECTORS_COUNT];
char* disk1[CYLINDER_COUNT][SECTORS_COUNT];
}; #endif

HardDisk.cpp

#include "HardDisk.h"

CHardDisk::CHardDisk()
{
char* buf = nullptr;
// 初始化硬盘,在分配和初始化内存中的数据
for (int i = 0; i < CYLINDER_COUNT; i++)
{
for (int j = 0; j < SECTORS_COUNT; j++)
{
buf = new char[SECTOR_SIZE]; // 效率低,初学者好理解
memset(buf, 0, SECTOR_SIZE); // **可以直接就申请一个大内存**
this->disk0[i][j] = buf;
buf = new char[SECTOR_SIZE];
memset(buf, 0, SECTOR_SIZE);
this->disk1[i][j] = buf;
}
}
} CHardDisk::~CHardDisk()
{
// 释放分配的内存
for (int i = 0; i < CYLINDER_COUNT; i++)
{
for (int j = 0; j < SECTORS_COUNT; j++)
{
delete[] this->disk0[i][j];
delete[] this->disk1[i][j];
}
}
} char* CHardDisk::getDiskBuffer(unsigned int head, int cylinder_num, int sector_num)
{
this->setMagneticHead(head);
this->setCylinder(cylinder_num);
this->setSector(sector_num); if (head == 0)
{
return this->disk0[cylinder_num][sector_num];
}
else if(head == 1)
{
return this->disk1[cylinder_num][sector_num];
} return nullptr;
} void CHardDisk::setDiskBuffer(unsigned int head, int cylinder_num, int sector_num, char* buf)
{
char* bufTmp = getDiskBuffer(head, cylinder_num, sector_num);
//memcpy_s(bufTmp, SECTOR_SIZE, buf, SECTOR_SIZE);
memcpy(bufTmp, buf, SECTOR_SIZE);
printf("已经写入到(磁头:%d - 柱面:%d - 扇区:%d)\n", head, cylinder_num, sector_num);
} void CHardDisk::makeVirtualDisk(const char* name)
{
printf("准备开始打包......\r\n");
FILE* file = nullptr;
fopen_s(&file, name, "wb");
for (int cylinder = 0; cylinder < CYLINDER_COUNT; cylinder++)
{
// 读完0面就读同一位置的1面数据
for (int head = 0; head <= 1; head++)
{
for (int sector = 0; sector < SECTORS_COUNT; sector++)
{
char* buf = getDiskBuffer(head, cylinder, sector);
// 将软件模拟的磁盘内容写入指定文件内
fwrite(buf, 1, SECTOR_SIZE, file);
}
}
} fclose(file);
printf("打包成功\r\n");
} void CHardDisk::writeFileToDisk(const char* fileName, bool bootable, int cylinder, int beginSec)
{
FILE* file = nullptr;
fopen_s(&file, fileName, "rb");
if (!file)
{
printf("读取文件不存在\r\n");
return;
}
char* buf = new char[512];
memset(buf, 0, 512);
if (bootable) {
buf[510] = (char)0x55;
buf[511] = (char)0xaa;
} //求得文件的大小
fseek(file, 0, SEEK_END);
int size = ftell(file);
rewind(file); if (size > SECTOR_SIZE)
{
int count_test = 0;
// 文件数据大于512字节,另作处理
while (!feof(file)) {
fread(buf, 1, SECTOR_SIZE, file);
setDiskBuffer(this->head, cylinder, beginSec, buf);
memset(buf, 0, SECTOR_SIZE);
beginSec++;
if (beginSec >= SECTORS_COUNT) {
beginSec = 0;
count_test++;
if (count_test % 2 == 0)
{
cylinder++;
}
this->head == 0 ? this->head = 1 : this->head = 0;
}
} }
else
{
fread(buf, 1, size, file);
setDiskBuffer(0, cylinder, beginSec, buf);
} fclose(file);
file = nullptr;
}

main.cpp

#include "HardDisk.h"

int main()
{
CHardDisk disk; printf("==========mbr=========\n");
disk.writeFileToDisk("mbr", true, 0, 0); //0-512 // 将loader二进制文件写入0柱面偏移2扇区
printf("==========loader=========\n");
disk.writeFileToDisk("loader", false, 0, 2); // 1024- disk.makeVirtualDisk("boot.img"); system("pause");
return 0;
}

编译结果如下:(我这里把loader文件做的大一点,是为了演示写入具体的过程!!如果仅编译上面的loader汇编代码不会有这么多文件写入),因机械磁盘效率问题,我们都是写入同一个柱面,这里有2个磁头,所以写完磁头0柱面0,然后直接写磁头1柱面0.。。随后磁头0柱面1,磁头1柱面1.。。随后磁头0柱面2 磁头1柱面2.。。。这样继续存储,读去也是,这样磁头不用倒来倒去,提高效率!

3.进入实验阶段

打开bochs模拟器,选择硬盘启动方式,更改配置文件,如https://blog.csdn.net/jadeshu/article/details/89046838 所示

在cmd中输入 bochs.exe -f bosh.src     (其中bosh.src是配置文件,选择硬件方式boot:disk )

然后界面如下所示

(4)打造简单OS-loader硬盘加载和C++写入文件的更多相关文章

  1. 为网格布局图片打造的超炫 CSS 加载动画

    今天,我想与大家分享一些专门为网格布局的图像制作的很酷的 CSS 加载动画效果.您可以把这些效果用在你的作品集,博客或任何你想要的网页中.设置很简单.我们使用了下面这些工具库来实现这个效果: Norm ...

  2. 【Web前沿技术】纯 CSS3 打造的10个精美加载进度条动画

    之前向大家介绍8款优秀的 jQuery 加载动画和进度条插件,今天这篇文章向大家推荐10个纯 CSS3 代码实现精美加载进度条动画效果的方案.加载动画和进度条在网站和 Web 应用中的使用非常流行,特 ...

  3. Cocos Creator 资源加载流程剖析【一】——cc.loader与加载管线

    这系列文章会对Cocos Creator的资源加载和管理进行深入的剖析.主要包含以下内容: cc.loader与加载管线 Download部分 Load部分 额外流程(MD5 Pipe) 从编辑器到运 ...

  4. 实现简单的 JS 模块加载器

    实现简单的 JS 模块加载器 1. 背景介绍 按需加载是前端性能优化的一个重要手段,按需加载的本质是从远程服务器加载一段JS代码(这里主要讨论JS,CSS或者其他资源大同小异),该JS代码就是一个模块 ...

  5. 一个简单的AMD模块加载器

    一个简单的AMD模块加载器 参考 https://github.com/JsAaron/NodeJs-Demo/tree/master/require PS Aaron大大的比我的完整 PS 这不是一 ...

  6. iview简单使用+按需加载组件的方法(全局和局部)

    1,简单使用 vue项目中使用iview非常简单: 首先安装依赖: $ npm install iview --save 会安装最新版本的依赖,安装完成后package.json会出现如下图配置 表示 ...

  7. 免插件,简单实现上拉加载loading

    上拉加载是前端经常遇到的问题,采用插件往往能够轻松解决,这里介绍一种免插件简单实现上拉加载的方法,参考一下,下面分享一下代码. html <body> <ul> <li& ...

  8. Tomcat7 自动加载类及检测文件变动原理

    在一般的web应用开发里通常会使用开发工具(如Eclipse.IntelJ)集成tomcat,这样可以将web工程项目直接发布到tomcat中,然后一键启动.经常遇到的一种情况是直接修改一个类的源文件 ...

  9. Tomcat 7 自动加载类及检测文件变动原理

    在一般的 web 应用开发里通常会使用开发工具(如 Eclipse.IntelJ )集成 tomcat ,这样可以将 web 工程项目直接发布到 tomcat 中,然后一键启动.经常遇到的一种情况是直 ...

随机推荐

  1. ASP.net Web API综合示例

    目录 概述 功能介绍 程序结构 服务器端介绍 客户端介绍 “契约” Web API设计规则 并行写入冲突与时间戳 身份验证详解 Web API验证规则 客户端MVVM简介 Web.Config 本DE ...

  2. iOS 动画基础-显式动画

    摘要 显式动画 属性动画 CABasicAnimation *animation = [CABasicAnimation animation];         [self updateHandsAn ...

  3. window.postMessage()实现(iframe嵌套页面)跨域消息传递

    window.postMessage()方法可以安全地实现Window对象之间的跨域通信.例如,在页面和嵌入其中的iframe之间. 不同页面上的脚本允许彼此访问,当且仅当它们源自的页面共享相同的协议 ...

  4. Java虚拟机(五):JVM 类加载机制

    一.JVM 类加载机制 JVM 类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程. 1. 加载: 加载是类加载过程中的第一个阶段,这个阶段会在内存中生成一个代表 ...

  5. LNMP环境搭建之编译安装指南(php-5.3.27.tar.gz)

    测试环境:CentOS release 6.5 (Final) 软件安装:nginx   mysql-5.5.32-linux2.6-x86_64.tar.gz   php-5.3.27.tar.gz ...

  6. 记一次SQL PLUS 不能登录的异常处理

    记一次SQL PLUS 不能登录的异常处理 现象 通过远程PLSQL Developer 访问数据发现卡死没响应. 通过Sqlplus 访问数据同样hang死在登录界面,且不能通过Ctrl+C取消 [ ...

  7. Resource接口

    [转]https://blog.csdn.net/hbtj_1216/article/details/85487787 参考:官方文档 1 简介 Java标准库中的java.net.URL类和标准处理 ...

  8. 2019-ACM-ICPC-南京区网络赛-D. Robots-DAG图上概率动态规划

    2019-ACM-ICPC-南京区网络赛-D. Robots-DAG图上概率动态规划 [Problem Description] ​ 有向无环图中,有个机器人从\(1\)号节点出发,每天等概率的走到下 ...

  9. linux修改MAC的方法

    Linux修改MAC地址方法 Linux modifies MAC address method 1 ifconfig wlan0 down 2 ifconfig wlan0 hw ether MAC ...

  10. linux网络编程之socket编程(七)

    今天继续学习socket编程,北京在持续几天的雾霾天之后久违的太阳终于出来了,心情也特别特别的好,于是乎,在这美好的夜晚,该干点啥事吧,那当然就是继续坚持我的程序学习喽,闲话不多说,进入正题: 通过这 ...