用java和汇编开发一个Hello World系统内核

本文介绍了一种使用汇编语言打印字符串至屏幕并利用Java生成软盘文件的方法,通过具体代码示例详细解析了汇编指令的功能及nasm编译器的使用,同时展示了如何通过Java代码创建软盘映像。

1 汇编实现

1.1 汇编实现

汇编代码如下:

org  0x7c00;

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

上面的汇编代码主要是设置了一些初始化数据,然后调用bios中断,将某个缓冲区中的字符打印到屏幕上,然后进入一个死循环。代码段的详细解释如下:

/*
org 的意思是origin, 中文意思是“起始,起源,” org 后面的7c00 是物理内存地址,假设物理内存是一个byte类型
的大数组,例如byte[] memory, 如果你有2 G内容,换算成字节就是2097152, 也就相当于memory数组有2097152字
节,于是当虚拟机上电,然后new一块内存 byte[] memory = new byte[2097152]. org 0x7c00 的意思是将本汇编
编译后的二进制数据从memory[0x7c00]处写入memory.
*/
org  0x7c00;

/*
jmp entry 中的jmp 其实就是c语言中的语句goto, jmp entry 其实是让cpu跳转到entry 处,执行entry下面的代
码,如果entry是一个函数名字的话,jmp entry 相当于调用entry函数,类比于java就是函数调用:entry();
*/
jmp  entry

// 下面的代码一直到RESB 18这行代码是可以直接删除的,我这里之所以放在这里是为了记录一下汇编知识点
/*
jmp entry 对应的机器代码,长度是3字节,那么db 0x90 的意思就是 memory[0x7c00+3] = 0x90, 也就是说db 
0x90 实际上做的是赋值操作,db 0x90 表示将给定位置处的一个字节赋予数值0x90, 赋值的内存位置就在0x7c00+3处。
*/
db   0x90
/*
DB 和 db 是同一个意思, 那么DB “OSKERNEL” 意思是,strcpy(memory + 0x7c00 + 3 + 1, “OSKERNEL”); 也就
是把”OSKERNEL”这个字符串拷贝到内存0x7c00 + 3 + 1 处, 3是什么意思呢,3就是jmp entry 编译成二进制代码后
的数据长度, 1 就是db 0x90 所所赋值的那个字节的长度。DW 跟DB是一个意思, DB 是将数据赋值给一个字节,由于
一个字节只有8位,那么赋值给这个字节的数据大小不能超过256, 512大于256,所以需要两个字节才能存储512这个数
据,DD 0xFFFFFFFF 就是把0xFFFFFFFF存储到四个字节长的内存中, 语句RESB 18 表示把接下来的18个字节的内存全
部初始化为0,转换为java代码就类似于: 
byte[] block = new byte[18]; 
for (int i = 0; i < 18; i++) { 
block[i] = 0; 
}
*/
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:
/*
先做的是初始化一系列寄存器,寄存器其实相当于java程序中,我们定义的变量,ax 是一个2字节长的寄存器, mov 
ax, 0是把数值0放入到ax寄存器中,类比于java 就是: 
char ax = 0; 
char类型的数据在java中是两个字节长,跟寄存器ax的长度一样,类似的,语句: mov ss, ax 相当于java的 char 
ss = ax; 后面的语句意思类推。
*/
    mov  ax, 0
    mov  ss, ax
    mov  ds, ax
    mov  es, ax
/*
我们要注意看语句 mov si, msg. msg 相当于一段内存, 
msg: 
DB 0x0a, 0x0a 
db “hello, world” 
db 0x0a 
就类似于 C 语言中的char* msg = “\n\nhello,world\n”. 字符’\n’的ascii值就是0xa.

mov si, msg 就相当于把msg内存的起始地址放入到寄存器si里。如果用C语言做类比,那么就相当于: char* si = msg;
*/
    mov  si, msg

putloop:
/*
mov al, [si] 
[si]表示读取si存储的内存地址处的一个字节长度的信息, 
mov al, [si] ,把该字节的数据存储到寄存器al 中,ax是两个字节长度的寄存器,这样ax就可以分解成两部分,第一
部分就对应于al, 第二部分就对应于ah, 
也就是al, ah合起来就是ax, 对应于C语言就相当于 char ax[2], al 表示的是ax[0], ah 表示的就是 ax[1], mov al, [si], 转换成C语言就是 char al = *si;
*/
    mov  al, [si]
/* add si, 1 表示将寄存器si中的数值加1,也就相当于C语言的 si++;  */
    add  si, 1
/* cmp al, 0 表示将al寄存器中的数据跟0比较,看al中的值是否等于0  */
    cmp  al, 0
/*
je fin 中的 je 表示 jump if equal, 也就是如果al 的值确实等于0,那么就跳转到fin所表示的代码处去执行,转
换成C语言就是 : 
if (al == 0) { 
goto fin 
}
*/
    je   fin

/*
mov ah, 0xe 就是把0xe赋值给寄存器 ah, mov bx ,15 同理。接下来要调用一个中断,中断其实就是一个函数调用,
我们在写c语言或java程序时,往往需要调用一些系统库函数,例如printf, 或java的System.out.print. 中断就是
bios提供给汇编语言的库函数,这些库函数都放入到一个数组里,int 0x10 意思是在库函数数组中取出第0x10个库函
数,然后执行该库函数的代码。

我们知道,函数调用时需要传递参数,那么调用bios提供的函数时,怎么传递参数呢,做法是,把需要传递的参数放入到
指定的寄存器中,例如想要在屏幕上输出字符,那么bios提供的编号为0x10的库函数可以实现这个功能,同时按规定,
要把寄存器ah设置为0x0e, 把要输出的字符的ascii值放入到寄存器al, 同时要把寄存器设bh的值设置成0,字符的颜色
可以通过寄存器bl的值来设定。看起来相当麻烦,这是由于我们做的是非常底层的编程,所以麻烦也就不可避免。
*/
    mov  ah, 0x0e
    mov  bx, 15
    int  0x10
    jmp  putloop
/*
于是代码片段: 
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 
}

printf(“%c”, al); 
} while(true);
*/

/*
hlt 表示 halt, 也就是让cpu进入休眠状态,如果此时我们点击一下键盘,或动一下鼠标,那么cpu就被唤醒,然后执行hlt后面的语句: 
jmp fin 
也就是跳转到fin开始处去执行,也就是进入了死循环。
*/
fin:
    HLT
    jmp  fin

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

1.2 使用nasm编译器进行编译

nasm boot.asm -o boot.bat


2 利用java生成软盘文件

2.1 利用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 ArrayList<Integer> imgByteToWrite = new ArrayList<Integer>();

    private void readKernelFormatFile(String fileName)
    {
    	File file = new File(fileName);
    	FileInputStream in = null;
    	
    	try
    	{
    		in = new FileInputStream(file);
    		int val = 0;
    		
    		while ((val = in.read()) != -1)
    		{
    			imgByteToWrite.add(val);
    		}
    		
    		in.close();
    	}
    	catch (IOException e)
    	{
    		e.printStackTrace();
    		
    		return;
    	}
    	
    	// 当前代码之后至510字节全部写0
    	int len = 510;
    	int curlen = imgByteToWrite.size();
    	for (int i=curlen; i<len; i++)
    	{
    		imgByteToWrite.add(0);
    	}
    	
    	// 第511、512字节为磁盘主引导扇区的有效标志,必须为0x55、0xaa
    	imgByteToWrite.add(0x55);
    	imgByteToWrite.add(0xaa);
    	
    	//imgByteToWrite.add(0xf0);
    	//imgByteToWrite.add(0xff);
    	//imgByteToWrite.add(0xff);
    }

    public OperatingSystem(String fileName) {
        readKernelFormatFile(fileName);

        // 1.44MB大小的软盘
        int len = 0x168000;
        int curSize = imgByteToWrite.size();
        for (int i=curSize; i<len; i++) {
            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) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

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


参考资料:

  1. Linux操作系统, 构建自己的内核
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值