之前做的系统内核,放在虚拟软盘的第一扇区,由于一个扇区只有512字节,因此系统内核的大小无法超过512字节。但是,一个拥有完整功能的内核不可能只有512字节,因此要想越过512字节的限制,具体做法就是做一个内核加载器(大小小于512字节),放入第一扇区,加载器加载入内存后,再将内核从软盘加载到系统内存,最后跳转到内核的加载地址
假设把编译好的内核代码写入软盘的第一柱面,第2扇区,那么加载器的代码如下(boot.asm)
org 0x7c00;
LOAD_ADDR EQU 0X8000
entry:
mov ax, 0
mov ss, ax
mov ds, ax
mov es, ax
mov si, ax
readFloppy:
mov CH, 1 ;CH 用来存储柱面号
mov DH, 0 ;DH 用来存储磁头号
mov CL, 2 ;CL 用来存储扇区号
mov BX, LOAD_ADDR ;ES:BX 数据存储缓冲区
mov AH, 0x02 ;AH = 02 表示要做的是读盘操作
mov AL, 1 ;AL表示要练习读取几个扇区
mov DL, 0 ;驱动器编号,一般我们只有一个软盘驱动器,所以写死为0
INT 0x13 ;调用BIOS中断实现磁盘读取功能
JC fin
jmp LOAD_ADDR
fin:
HLT
jmp fin
readFloppy 这段代码的作用是从软盘的1柱面2扇区,将内核读取到系统内存的0x8000处,读取成功后,通过一个 jmp 跳转到内核的加载地址,将机器的控制权转交给内核。内核代码如下(kernel.asm)
org 0x8000
entry:
mov ax,0
mov ss,ax
mov ds,ax
mov es,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 "This is Hello World from kernel"
下载 nasm软件 对汇编语言进行编译。使用命令分别编译加载器和内核:
nasm boot.asm -o boot.bat nasm kernel.asm -o kernel.bat
编译好以后,通过java代码将编译好的内核写入虚拟软盘
Floppy.java
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
public class Floppy {
enum MAGNETIC_HEAD {
MAGNETIC_HEAD_0, MAGETIC_HEAD_1
};
public int SECTOR_SIZE = 512;
private int CYLINDER_COUNT = 80; // 80个柱面
private int SECTORS_COUNT = 18;
private MAGNETIC_HEAD magneticHead = MAGNETIC_HEAD.MAGNETIC_HEAD_0;
private int current_cylinder = 0;
private int current_sector = 0;
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++) {
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.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 {
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) {
e.printStackTrace();
}
}
}
OPeratingSystem.java
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 int MAX_SECTOR_NUM = 18;
private void writeFileToFloppy(String fileName, boolean bootable, int cylinder, int beginSec) {
File file = new File(fileName);
InputStream in = null;
try {
in = new FileInputStream(file);
byte[] buf = new byte[512];
if (bootable) {
buf[510] = 0x55;
buf[511] = (byte) 0xaa;
}
while (in.read(buf) > 0) {
// 将内核读入到磁盘第0面,第0柱面,第1个扇区
floppyDisk.writeFloppy(Floppy.MAGNETIC_HEAD.MAGNETIC_HEAD_0, cylinder, beginSec, buf);
beginSec++;
if (beginSec > MAX_SECTOR_NUM) {
beginSec = 1;
cylinder++;
}
}
} catch (IOException e) {
e.printStackTrace();
return;
}
}
public OperatingSystem(String s) {
writeFileToFloppy(s, true, 0, 1);
}
public void makeFllopy() {
writeFileToFloppy("kernel.bat", false, 1, 2);
floppyDisk.makeFloppy("system.img");
}
public static void main(String[] args) {
OperatingSystem op = new OperatingSystem("boot.bat");
op.makeFllopy();
}
}
运行的是 OperatingSystem.java 代码。将生成的虚拟软盘文件system.img加载到虚拟机中启动,就可以看到如下效果