自制操作系统(2)软盘读写

软盘读写

使用Java代码模拟出Floppy软盘:

package org.dreamtech;

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

public class Floppy {
	// 由于有两个盘面,于是有两个磁头
	enum MAGNETIC_HEAD {
		MAGNETIC_HEAD_0, MAGETIC_HEAD_1
	};

	// 每一个盘面有80个柱面
	private int CYLINDER_COUNT = 80;
	// 每一个柱面有18个扇区
	private int SECTORS_COUNT = 18;
	// 每一个扇区大小512字节
	public int SECTOR_SIZE = 512;
	// 默认设置第一个磁头
	private MAGNETIC_HEAD magneticHead = MAGNETIC_HEAD.MAGNETIC_HEAD_0;
	// 当前读取到的柱面
	private int currentCylinder = 0;
	// 当前读取到的扇区
	private int currentSector = 0;

	// 软盘可以定义成key为Integer,value为List<List<byte[]>>的HashMap
	// Integer是两个盘面的表示
	// byte[]表示一个扇区存储的内容
	// List<byte[]>表示一个柱面存储的内容
	// List<List<byte[]>>表示一个盘面所有的柱面存储的内容
	private HashMap<Integer, ArrayList<ArrayList<byte[]>>> floppy = new HashMap<Integer, ArrayList<ArrayList<byte[]>>>();

	public Floppy() {
		initFloppy();
	}

	private void initFloppy() {
		// 一个磁盘有两个盘面
		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++) {
			// 每个扇区先申请512字节的好内存
			byte[] sector = new byte[SECTOR_SIZE];
			cylinder.add(sector);
		}
		return cylinder;
	}

	// 设置读的盘面
	public void setMagneticHead(MAGNETIC_HEAD head) {
		magneticHead = head;
	}

	// 设置读的柱面
	public void setCylinder(int cylinder) {
		if (cylinder < 0) {
			this.currentCylinder = 0;
		} else if (cylinder >= 80) {
			this.currentCylinder = 79;
		} else {
			this.currentCylinder = cylinder;
		}
	}

	// 设置读的扇区
	public void setSector(int sector) {
		if (sector < 0) {
			this.currentSector = 0;
		} else if (sector > 18) {
			this.currentSector = 18 - 1;
		} else {
			this.currentSector = sector - 1;
		}
	}

	// 读磁盘的方法需要设置盘面柱面和扇区号
	public byte[] readFloppy(MAGNETIC_HEAD head, int cylinderNum, int sectorNum) {
		setMagneticHead(head);
		setCylinder(cylinderNum);
		setSector(sectorNum);
		ArrayList<ArrayList<byte[]>> disk = floppy.get(this.magneticHead.ordinal());
		ArrayList<byte[]> cylinder = disk.get(this.currentCylinder);
		byte[] sector = cylinder.get(this.currentSector);
		return sector;
	}

	// 写磁盘的方法需要设置盘面柱面和扇区号以及数据
	public void writeFloppy(MAGNETIC_HEAD head, int cylinderNum, int sectorNum, byte[] buf) {
		setMagneticHead(head);
		setCylinder(cylinderNum);
		setSector(sectorNum);
		ArrayList<ArrayList<byte[]>> disk = floppy.get(this.magneticHead.ordinal());
		ArrayList<byte[]> cylinder = disk.get(this.currentCylinder);
		byte[] buffer = cylinder.get(this.currentSector);
		System.arraycopy(buf, 0, buffer, 0, buf.length);
	}

	public void makeFloppy(String fileName) {
		DataOutputStream out = null;
		try {
			// 虚拟软盘是存粹的二进制文件,它的逻辑结构如下:
			// 前512*18 字节的内容对应盘面0,柱面0的所有扇区内容
			// 接着的512*18字节的内容对应盘面1,柱面0的所有扇区内容
			// 再接着的512*18字节的内容对应盘面0,柱面1所有扇区内容
			// 再接着512*18字节的内容对应盘面1,柱面1所有扇区内容
			// 以此类推
			// 简单理解就是柱面一正一反来写
			// 计算得出最终一个磁盘可以写1440KB的数据
			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) {
			e.printStackTrace();
		} finally {
			try {
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

将上节课的汇编代码编译后的二进制文件写入软盘:

package org.dreamtech;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

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];
			// Intel的CPU规定必须在第511-512字节写入0x55aa
			buf[510] = (byte) 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;
		} finally {
			try {
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

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

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

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

生成的system.img被成功加载并启动

使用Java实现软盘读写并不是正确的方案,下面采用汇编来实现:

org  0x7c00

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数据存储缓冲区,是INT 0x13中断需要的参数
                         ;BX寄存器里面保存的是msg的地址
    mov          AH, 0x02 ;AH=2表示要做的是读盘操作
    mov          AL,  1 ;AL表示要连续读取几个扇区
    mov          DL, 0 ;驱动器编号(一般我们只有一个软盘驱动器,所以写死为0)   
    INT          0x13 ;调用BIOS中断实现磁盘读取功能
                      ;效果是是将读取到的数据写入msg地址
    jc           error ;如果读盘出现错误,就执行error处的代码
                       ;如果BIOS调用出错,FLAGS寄存器的CF位会置1,同时把错误代码写入AH
                       ;如果BIOS调用没错,FLAGS寄存器的CF位会置0
                       ;所以这里类似if(FLAGS.CF==1){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 ;申请64字节的地址(从当前的地址开始空出64字节)

errmsg:
    DB "error"

进行编译:nasm -o boot.bat boot_readstring_from_sector.asm

修改Java代码并执行:

	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");
	}

加载到虚拟机,成功打印代码中的这段话

上一篇:Java实验报告四


下一篇:第六周&java实验报告四