当前位置: 首页 > news >正文

MyOS(三):软盘读写

      模拟软盘的读写,将上节汇编生成的boot.bin的二进制代码写入到虚拟软盘中,然后将虚拟软盘写成磁盘文件system.img中,并用虚拟机运行

     

       在前两节,我们将一段代码通过软盘加载到了系统内存中,并指示cpu执行加入到内存的代码。

       事实上,操作系统内核加载也是这么做的。

       现在我们windows操作系统,操作系统内核都会安装在硬盘上(在系统盘C盘上存储)。机器上电之后,会从硬盘上将操作系统内核读取到内存中,再执行。

       而我们现在做的这个项目是将操作系统内核写在软盘上,机器上电之后,我们会指示硬件从软盘中将我们的系统内核加载到内存中。

     在早期win95以前或者是DOS,要安装操作系统的话,需要将很多快软盘插入到机器中,让我们机器从软盘中读取内容才能去安装操作系统,后来硬盘普及了之后操作系统才安装在硬盘上的。我们现在就是在模拟操作操作系统的加载方式

       只不过我们加载的代码,最大只能512 byte, 一个操作系统内核,少说也要几百兆,由此,系统内核不可能直接从软盘读入系统内存。通常的做法是,被加载进内存的512 Byte程序,实际上是一个内核加载器,它运行起来后,通过读取磁盘,将存储在磁盘上的内核代码加载到指定的内存空间,然后再把cpu的控制权提交给加载进来的系统内核。

 

软盘结构

        软盘的物理结构如上图,一个盘面被划分成若干个圆圈,例如图中的灰色圆圈,我们称之为磁道,也可以称作柱面,一个磁道或柱面,又被分割成若干部分,每一部分,我们称之为一个扇区,一个扇区的大小正好是512k,从而,当我们把数据存储到软盘上时,数据会分解成若干个512Byte大小的块,然后写入到扇区里。

       要读取数据时,磁头会挪动到扇区所在的磁道或柱面,然后盘面转动,当要读取的扇区转到磁头正下方时,磁头通电,通过电磁效应将扇区的数据读取到内存中。

       从上图的左边图形可以了解,一个磁盘有两个盘面,每个盘面的组成跟右边图形一样,同时每个盘面对应一个磁头,所以当想从磁盘上读取数据时,需要确定数据在哪一个盘面,从而确定要用哪一个磁头来读取数据,然后确定哪一个磁道,最后再确定要读取的数据都存储在哪一个扇区。

      对于我们要开发的系统,我们要模拟的是3.5寸软盘,这种软盘的特点是,它有两个盘面,因此就对应两个磁头,每个盘面有80个磁道,也就是柱面,编号分别为0-79. 每个柱面都有18个扇区,编号分别为1-18. 所以一个盘面可以存储的数据量大小为:
512 * 18 * 80
一个软盘有两个盘面,因此一个软盘可以存储的数据为:
2 * 512 * 18 * 80 = 1474560 Byte = 1440 KB = 1.5M

接下来,我们用java来模拟一个3.5寸软盘,以及它的读写逻辑。
 

实现

 

Floppy.java 用于实现虚拟软盘

import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashMap;


public class Floppy {
    enum MAGNETIC_HEAD {
        //磁头编号0对应着上面的磁头(读上盘面),1对应着下面的磁头(读下盘面)
        MAGNETIC_HEAD_0,
        MAGETIC_HEAD_1
    };


    public int SECTOR_SIZE = 512;//1个扇区存储容量大小是512字节
    private int CYLINDER_COUNT = 80; //80个柱面
    private int SECTORS_COUNT = 18;//每个柱面都有18个扇区,也就是1个圆圈含有18个扇形
    private MAGNETIC_HEAD magneticHead = MAGNETIC_HEAD.MAGNETIC_HEAD_0;
    private int current_cylinder = 0;
    private int current_sector = 0;

    //HashMap来模拟整个盘片
    //byte[] 对应一个扇区
    //ArrayList对应一个磁道,或者叫一个柱面   一个ArrayList含有18个byte[]
    //外层的ArrayList含有80个里面的子ArrayList
    private HashMap<Integer,ArrayList<ArrayList<byte[]>> > floppy = new HashMap<Integer,ArrayList<ArrayList<byte[]>> >(); //一个磁盘两个面

    public Floppy() {
        //初始化
        initFloppy();
    }

    private void initFloppy() {
        //一个磁盘有两个盘面
        //初始化两个盘面,将2个盘面加入到floppy Hashmap中
        floppy.put(MAGNETIC_HEAD.MAGNETIC_HEAD_0.ordinal(), initFloppyDisk());
        floppy.put(MAGNETIC_HEAD.MAGETIC_HEAD_1.ordinal(), initFloppyDisk());
    }

    //初始化盘面
    private ArrayList<ArrayList<byte[]>> initFloppyDisk() {
        ArrayList<ArrayList<byte[]>> floppyDisk = new ArrayList<ArrayList<byte[]>>(); //磁盘的一个面
        //一个磁盘面有80个柱面
        for(int i = 0; i < CYLINDER_COUNT; i++) {
            floppyDisk.add(initCylinder());
        }

        return floppyDisk;
    }

    private ArrayList<byte[]> initCylinder() {
        //构造一个柱面,一个柱面有18个扇区
        ArrayList<byte[]> cylinder = new ArrayList<byte[]> ();
        for (int i = 0; i < SECTORS_COUNT; i++) {
            byte[] sector = new byte[SECTOR_SIZE];
            cylinder.add(sector);
        }

        return cylinder;
    }

    //读写的时候首先要确定磁头,即确定要读取的盘面是上面还是下面
    public void setMagneticHead(MAGNETIC_HEAD head) {
        magneticHead = head;
    }

    //设置磁道编号0-79
    public void setCylinder(int cylinder) {
        if (cylinder < 0) {
            this.current_cylinder = 0;
        }
        else if (cylinder >= 80) {
            this.current_cylinder = 79;
        }
        else {
            this.current_cylinder = cylinder;
        }
    }

    //确定要访问的扇区编号
    public void setSector(int sector) {
        //sector 编号从1到18
        if (sector < 0) {
            this.current_sector = 0;
        }
        else if (sector > 18) {
            this.current_sector = 18 - 1;
        }
        else {
            this.current_sector = sector - 1;
        }
    }

    //读扇区
    public byte[] readFloppy(MAGNETIC_HEAD head, int cylinder_num, int sector_num) {
        setMagneticHead(head);
        setCylinder(cylinder_num);
        setSector(sector_num);

        ArrayList<ArrayList<byte[]>> disk = floppy.get(this.magneticHead.ordinal());
        ArrayList<byte[]> cylinder = disk.get(this.current_cylinder);

        byte[] sector = cylinder.get(this.current_sector);

        return sector;
    }

    //写扇区
    public void writeFloppy(MAGNETIC_HEAD head, int cylinder_num, int sector_num, byte[] buf) {
        setMagneticHead(head);
        setCylinder(cylinder_num);
        setSector(sector_num);

        ArrayList<ArrayList<byte[]>> disk = floppy.get(this.magneticHead.ordinal());
        ArrayList<byte[]> cylinder = disk.get(this.current_cylinder);

        byte[] buffer = cylinder.get(this.current_sector);
        System.arraycopy(buf, 0, buffer, 0, buf.length);
    }

    public void makeFloppy(String fileName) {
        try {
            /*
                虚拟软盘是二进制文件
                前512*18字节的内容对应盘面0,柱面0的所有扇区内容
                接着的512*18字节对应盘面1,柱面0的所有扇区内容
                再接着的512*18字节对应盘面0,柱面1的所有扇区内容
                再接着的512*18字节对应盘面1,柱面1的所有扇区内容
                以此类推
             */
            DataOutputStream out = new DataOutputStream(new FileOutputStream(fileName));
            for (int cylinder = 0; cylinder < CYLINDER_COUNT; cylinder++) {
                for (int head = 0; head <= MAGNETIC_HEAD.MAGETIC_HEAD_1.ordinal(); head++) {
                    for (int sector = 1; sector <= SECTORS_COUNT; sector++) {
                        byte[] buf = readFloppy(MAGNETIC_HEAD.values()[head], cylinder, sector);

                        out.write(buf);
                    }

                }
            }

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

OperatingSystem.java将上节用汇编编译的操作系统内核写入到虚拟软盘中,然后将虚拟软盘写成磁盘文件

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;


public class OperatingSystem {

    private Floppy floppyDisk = new Floppy();

    private void writeFileToFloppy(String fileName) {
        File file = new File(fileName);
        InputStream in = null;

        try {
            in = new FileInputStream(file);
            byte[] buf = new byte[512];
            buf[510] = 0x55;
            buf[511] = (byte) 0xaa;
            if (in.read(buf) != -1) {
                //将内核读入到磁盘第0面,第0柱面,第1个扇区
                floppyDisk.writeFloppy(Floppy.MAGNETIC_HEAD.MAGNETIC_HEAD_0, 0, 1, buf);
            }
        } catch(IOException e) {
            e.printStackTrace();
            return;
        }
    }

    public OperatingSystem(String s) {
        writeFileToFloppy(s);
    }

    public void makeFllopy()   {
        floppyDisk.makeFloppy("system.img");
    }

    public static void main(String[] args) {
        OperatingSystem op = new OperatingSystem("boot.bin");
        op.makeFllopy();
    }
}

用virtualbox运行system.img

 

下面是怎样读取软盘

用汇编语言实现软盘的读写

Java代码先把要打印的字段写入到虚拟软盘的某个位置

然后我们用汇编写的操作系统内核从软盘的指定位置把要打印的内容读出来,显示到屏幕上

 

boot_readstring_from_floppy.asm

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        ;CH 用来存储柱面号
    mov  DH, 0        ;DH 用来存储磁头号
    mov  CL, 2        ;CL 用来存储扇区号

    mov  BX, msg       ;ES:BX 数据存储缓冲区
                       ;BX存储读取的512字节的内容

    mov  AH, 0x02      ;AH=02 表示要做的是读盘操作
    mov  AL, 1        ;AL 表示要连续读取几个扇区
    mov  DL, 0         ;驱动器编号,一般我们只有一个软盘驱动器,所以写死,为0
                       ;早年有一些机器可以同时插入多个软盘

    INT  0x13          ;调用BIOS中断实现磁盘读取功能

    jc   error         ;如果读盘出现错误,跳转到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   ;出现错误打印error
    jmp  putloop

msg:
    RESB   64
errmsg:
    DB "error"

用nasm将boot_readstring_from_floppy.asm编译成boot.bin

 

此时修改Java代码

以前

现在

 

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;


public class OperatingSystem {

    private Floppy floppyDisk = new Floppy();

    private void writeFileToFloppy(String fileName) {
        File file = new File(fileName);
        InputStream in = null;

        try {
            in = new FileInputStream(file);
            byte[] buf = new byte[512];
            buf[510] = 0x55;
            buf[511] = (byte) 0xaa;
            if (in.read(buf) != -1) {
                //将内核读入到磁盘第0面,第0柱面,第1个扇区
                floppyDisk.writeFloppy(Floppy.MAGNETIC_HEAD.MAGNETIC_HEAD_0, 0, 1, buf);
            }
        } catch(IOException e) {
            e.printStackTrace();
            return;
        }
    }

    public OperatingSystem(String s) {
        writeFileToFloppy(s);
    }

    public void makeFllopy()   {
        String s = "This is a text from cylinder 1 and sector 2";
        floppyDisk.writeFloppy(Floppy.MAGNETIC_HEAD.MAGNETIC_HEAD_0,1,2,s.getBytes());

        floppyDisk.makeFloppy("system.img");
    }

    public static void main(String[] args) {
        OperatingSystem op = new OperatingSystem("boot.bin");
        op.makeFllopy();
    }
}

 

这次我们要打印的语句,不再写死到内核里面了,而是通过代码写入到磁盘的第0盘面(磁头0),第一柱面的第二扇区
操作系统内核从这个位置把要打印的字符串的内容读取出来,最后打印到屏幕上


Java代码的作用

①将这句话写在软盘里

This is a text from cylinder 1 and sector 2

②将汇编语言写的操作系统内核写到软盘里

 

运行结果

 

参考:

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

相关文章:

  • Python函数式编程(二):map、 reduce、 filter、 sorted
  • OpenCV(一)——图片灰度转换(灰度图) 修改图片尺寸(cv2.resize) 读取视频
  • opencv人脸识别 (一):人脸检测 (Haar级联 静态图像中人脸检测 视频中的人脸检测 )
  • opencv人脸识别 (二):人脸识别 (基于 LBPH)
  • Caffe(一)——简介 优点 Caffe2 编译和安装
  • Pytorch之经典神经网络CNN(一) —— 全连接网络 / MLP (MNIST) (trainset和Dataloader batch training learning_rate)
  • 神经网络/深度学习 相关知识
  • pytorch搭建神经网络分类Fashion-MNIST数据集
  • Python函数式编程(三):匿名函数lambda
  • 编程范式—命令式编程与函数式编程
  • Scala语言(二)
  • Scala语言(三)
  • Python——virtualenv/virtualenvwrapper
  • Python多进程(multiprocessing)(mp)(一) —— 进程的创建 join()方法
  • Python多进程(multiprocessing)(mp)(二) —— 创建多进程 继承process类 进程池(Pool)
  • ES6指北【2】—— 箭头函数
  • 【技术性】Search知识
  • canvas 绘制双线技巧
  • CSS 专业技巧
  • CSS3 变换
  • Docker 笔记(2):Dockerfile
  • Javascript基础之Array数组API
  • JavaWeb(学习笔记二)
  • Spring-boot 启动时碰到的错误
  • vue总结
  • 百度地图API标注+时间轴组件
  • 基于OpenResty的Lua Web框架lor0.0.2预览版发布
  • 技术胖1-4季视频复习— (看视频笔记)
  • 蓝海存储开关机注意事项总结
  • 前嗅ForeSpider采集配置界面介绍
  • 使用阿里云发布分布式网站,开发时候应该注意什么?
  • 腾讯优测优分享 | 你是否体验过Android手机插入耳机后仍外放的尴尬?
  • 通过npm或yarn自动生成vue组件
  • 小程序 setData 学问多
  • 小李飞刀:SQL题目刷起来!
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • ionic入门之数据绑定显示-1
  • 组复制官方翻译九、Group Replication Technical Details
  • #QT(TCP网络编程-服务端)
  • $.ajax()
  • ( 用例图)定义了系统的功能需求,它是从系统的外部看系统功能,并不描述系统内部对功能的具体实现
  • (20050108)又读《平凡的世界》
  • (27)4.8 习题课
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (第二周)效能测试
  • (二)Eureka服务搭建,服务注册,服务发现
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (三)elasticsearch 源码之启动流程分析
  • (转)Oracle存储过程编写经验和优化措施
  • .gitignore文件—git忽略文件
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .NET Core 控制台程序读 appsettings.json 、注依赖、配日志、设 IOptions
  • .net redis定时_一场由fork引发的超时,让我们重新探讨了Redis的抖动问题
  • .net 按比例显示图片的缩略图