转载

用Java做一个最小的操作系统内核2

上一节,我用Java制作了一个虚拟软盘,当把虚拟软盘插入虚拟机,启动机器后,屏幕就打印出了Java程序中设定的语句,然后卡死。

在Java代码中,有一个二进制数组imgContent,它里面存储的实际上是一端二进制代码,当虚拟机设为从软盘启动后,这段代码会被BIOS读到内存中,然后指示CPU去执行imgContent所存储的二进制代码

汇编代码

下面我就将imgContent数组中的内容,用汇编语言实现,并且详细阐述其作用,首先看看汇编语言怎么写

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

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

首先看第一行代码 org 0x7c00org 的意思是“起始,起源”, org 后面的 7c00 是物理内存地址,假设物理内存是一个字节数组,例如 byte[] memory ,如果你有2M内容,就需要new2097152字节的内存, byte[] memory = new byte[2097152]org 0x7c00 的意思就是将汇编编译后的二进制数据从 memory[0x7c00] 处写入 memory 。至于为什么是 0x7c00 ,这就要问微软

jmp entry 中的 jmp 起始就是c语言中的 gotojmp entry 其实是让CPU跳转到 entry 处,执行 entry 下面的代码,如果 entry 是一个函数名, jmp entry 相当于调用 entry() 函数

db 0x90RESB 18 这段代码做的是一些初始化工作。 jmp entry 对应的机器码长度是3字节,那么 db 0x90 的作用就是 memory[0x7c00+3]=9x90 ,也就是说 db 9x90 实际上做的是赋值操作

DBdb 是相同的作用,所以 DB "OSKERNEL" 的意思是 strcpy(memory + 0x7c00 + 3 + 1,"OSKERNEL") ,也就是把"OSKERNEL"这个字符串拷贝到内存0x7c00 + 3 + 1处。3就是 jmp entry 所占的3字节长度,1就是 db ox90 所复制的那个字节的长度

DWDB 是相同的意思,只不过 DB 是将数据赋值给一个字节, DW 是将数据赋值给两个字节,从上面代码也能看出来 DW 512 ,因为512转换为二进制肯定超过了8位,所以不能用 DB

DD 0xFFFFFFFF 就是将 0xFFFFFFFF 存储到四个字节长的内存中

RESB 18 表示把接下来的18个字节的内容全部初始化为0,类似于下面的Java代码

byte[] block = new byte[18];
for(int i = 0;i < 18;i++)
    block[i] = 0;

接下来看 entry 代码,它的作用是初始化一系列寄存器,寄存器相当于Java程序中定义的变量, ax 是一个2字节长的寄存器, mov ax,0 就是把数值0放到 ax 寄存器中,类似Java的 char ax = 0 ,char类型的数据在Java中也是2字节长。

类似的, mov ss,ax 相当于Java中的 char ss = ax

其中比较重要的语句是 mov si,msgmsg 相当于一段内存

msg:
    DB    0x0a,  0x0a
    db    "hello, world"
    db    0x0a

上面这段代码类似于C语言中的 char* msg = "/n/nhello,world/n"/n 的ASCII值就是 0x0a 。那么 mov si,msg 就相当于把 msg 内存的真实地址放到寄存器 si 里,如果用C语言表示就是 char* si = msg

接着看后面的代码 mov al,[si][si] 表示读取 si 存储的内存地址中一个字节长度的信息,这行代码的含义就是把 [si] 内容数据存储到寄存器 al 中, ax 是两字节长的寄存器,这样 ax 就可以分解成两部分,第一部分是 al ,第二部分是 ah ,对应C语言就相当于 char ax[2]al 表示的是 ax[0]ah 表示的是 ax[1]mov ai,[si] 转换成C语言就是 char al = *si

add si,1 表示将寄存器 si 中的数值加1,也就相当于C语言中的 si++

cmp al,0 表示将寄存器 al 寄存器中的数据跟0比较,看 al 中的值是否等于0

je fin 中的 je 表示 jump if equal ,也就是如果 al 的值确实等于0,那么就跳转到 fin 所表示的代码处去执行,转换成C语言就是

if(al == 0)
    goto fin

mov ah,0xe 就是把 0xe 赋值给 ahmov bx,15 同理

接下来调用的是一个中断函数,我们在写C或Java程序时,往往需要调用一些系统库函数,例如 printf ,或 System.out.print ,中断函数就是BIOS提供给汇编语言的库函数,这些库函数都存放在一个数组里, int 0x10 的意思是在库函数数组中取出第 0x10 个库函数执行

有的函数调用时需要传递参数,如果想要调用BIOS提供的函数,在屏幕上输出字符,那么就要将传递的参数放入到指定的寄存器中。BIOS提供的编号为0x10的库函数可以实现这个功能,按照规定,要把寄存器 ah 的值设置为 0x0e ,把要输出字符的ASCII值放到寄存器 al ,同时要把寄存器 bh 的值设为0,字符的颜色可以通过寄存器 bl 的值来设定。

代码段

putloop:
    mov  al, [si]
    add  si, 1
    cmp  al, 0
    je   fin
    mov  ah, 0x0e
    mov  bx, 15
    int  0x10
    jmp  putloop

就相当于C语言中的

do {
    char al = *si;
    si++;
    if(al == 0)
        goto fin
    print("%c",al);
} while(true);

fin 处的代码就两条语句, htl 表示halt,也就是让CPU进入休眠状态,如果此时我们点击一下键盘或动一下鼠标,那么CPU就会被唤醒,然后执行 hlt 后面的语句 jmp fin ,又回到 fin 开始处去执行,进入无限循环

编译汇编代码

把上面的汇编代码存储成一个文件:boot.asm,然后利用汇编编译器nasm来编译, nasm boot.asm ,编译后会得到一个二进制文件,内容如下:

e94e 0090 4f53 4b45 524e 454c 0002 0101
0002 e000 400b f009 0012 0002 0000 0000
0040 0b00 0000 0029 ffff ffff 4d59 4649
5253 544f 5320 2046 4154 3132 2020 2000
0000 0000 0000 0000 0000 0000 0000 0000
00b8 0000 8ed0 8ed8 8ec0 be73 7c8a 0481
c601 003c 0074 09b4 0ebb 0f00 cd10 ebed
f4eb fd0a 0a68 656c 6c6f 2c20 776f 726c
640a 00

修改Java代码

把这个二进制文件改名为boot.bat,拷贝到上一个Java程序的工程目录下,然后把Java程序进行修改,将这段二进制数据都入到imgContent数组中,代码如下

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

public class OperatingSystem {
    private int[] imgContent = new int[] { 0xeb, 0x4e, 0x90, 0x48, 0x45, 0x4c, 0x4c, 0x4f, 0x49, 0x50, 0x4c, 0x00, 0x02,
            0x01, 0x01, 0x00, 0x02, 0xe0, 0x00, 0x40, 0x0b, 0xf0, 0x09, 0x00, 0x12, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x29, 0xff, 0xff, 0xff, 0xff, 0x48, 0x45, 0x4c, 0x4c, 0x4f, 0x2d,
            0x4f, 0x53, 0x20, 0x20, 0x20, 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x8e, 0xd0,
            0xbc, 0x00, 0x7c, 0x8e, 0xd8, 0x8e, 0xc0, 0xbe, 0x74, 0x7c, 0x8a, 0x04, 0x83, 0xc6, 0x01, 0x3c, 0x00, 0x74,
            0x09, 0xb4, 0x0e, 0xbb, 0x0f, 0x00, 0xcd, 0x10, 0xeb, 0xee, 0xf4, 0xeb, 0xfd };

    private ArrayList<Integer> imgByteToWrite = new ArrayList<Integer>();

    private void readKernelFromFile(String fileName) {
        File file = new File(fileName);
        InputStream in = null;
        try {
            in = new FileInputStream(file);
            int tempbyte;
            while ((tempbyte = in.read()) != -1) {
                imgByteToWrite.add(tempbyte);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        imgByteToWrite.add(0x55);
        imgByteToWrite.add(0xaa);
        imgByteToWrite.add(0xf0);
        imgByteToWrite.add(0xff);
        imgByteToWrite.add(0xff);
    }

    public OperatingSystem(String s) {
        readKernelFromFile("boot.bat");
        int len = 0x168000;
        int curSize = imgByteToWrite.size();
        for (int l = 0; l < len - curSize; l++) {
            imgByteToWrite.add(0);
        }
    }

    public void makeFllopy() {
        try {
            DataOutputStream out = new DataOutputStream(new FileOutputStream("system.img"));
            for (int i = 0; i < imgByteToWrite.size(); i++) {
                out.writeByte(imgByteToWrite.get(i).byteValue());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        OperatingSystem op = new OperatingSystem("hello, this is my first line of my operating system code");
        op.makeFllopy();
    }
}
原文  https://www.wmathor.com/index.php/archives/1121/
正文到此结束
Loading...