参考链接
- 《30天自制操作系统》源码中文版 https://github.com/yourtion/30dayMakeOS
- How to create an OS from scratch https://github.com/cfenollosa/os-tutorial
- https://blog.csdn.net/tyler_download/article/category/6280228/3?
前言
做一个自己的操作系统一定是很一件很酷的事。今天仔细读了下上面三篇文章,都非常不错,不过比较遗憾的是除了第一节的hello world之外,后面的数据结构的定义,各种模式的转换都非常脑坑疼。为了能尽快做出一个成品来获得正反馈(zhuangb),我将中间一系列过程能省则省掉了,最终能最简单展示一个helloworld的窗口程序,来增强一下满足感。
普通流程
虽然我力图精简大部分的流程,但是操作系统的制作流程还是需要明白的。
软盘读写
首先BIOS只能读取软盘512个字节,但是操作系统内核明显会超过它,所以一般这512字节都会写一个内核加载器
,通过它来加载我们真正的内核。
内核加载器
内核加载器的作用是什么呢,它用来告诉BIOS,“来加载512字节之后的数据吧~”。同时我们还要注意我们编译出来的img的结构大小,前512字节是加载器的二进制代码,不足的用00来代替,后面是我们真正内核的代码。需要注意的是,内核加载器由于硬件(软盘)的结构限制,只能以512字节为单位加载内核,如果内核超出,需要及时修改加载器,告诉它“需要加载更多内核代码”。
保护模式32位寻址
计算机默认进入的实模式,但是它的功能有限,我们需要操作CPU来进入“保护模式”,上面的参考文章也会在一开始告诉你,如何进入保护模式。总之就是定义一堆看不懂的数据结构~
跨平台C语言的编译
进入到保护模式后,就可以访问到更大的内存空间了,接下来就是抛弃汇编,使用C语言编写内核。首先的问题是,C语言编写的程序如何让我们内核识别到?参见https://github.com/cfenollosa/os-tutorial/blob/master/11-kernel-crosscompiler/README.md,使用我们系统的gcc编译一个不带任何依赖的gcc即可(听起来有点好玩,就和go语言用go写的一样),然后在汇编种预留c语言的接口
[bits 32] [extern main] ; Define calling point. Must have same name as kernel.c 'main' function call main ; Calls the C function. The linker will know where it is placed in memory jmp $
之后用编译出来的gcc编译c语言代码,汇编编译汇编代码,将两个文件合起来即可。
绘制界面
如果要绘制界面,需要用汇编调用BIOS的中断,操作硬件打开显卡色彩功能(可以理解为用汇编操作BIOS的API)。
打开VGA显卡
mov al, 13h mov ah, 0x00 int 0x10
画像素点
mov ah,0ch mov al,10;颜色值 1~256 mov dx,1 ;x坐标 mov cx,1 ;y坐标 mov bh,0 int 0x10
颜色值
通过一个像素点,就可以画整个图形界面了(有点以前OpenGL写游戏的味道)
精简流程
为了最快自制一个带界面的操作系统,我精简了一个流程(虽然还是花了一天)。我们直接用汇编操作BIOS的显卡功能,什么加载器,什么GDT,统统不要。
直接上代码
org 0x7c00; jmp entry entry: mov ax, 0 mov ss, ax mov ds, ax mov es, ax mov si,font_X mov di,font_Y putloop: mov al, 13h mov ah, 0x00 int 0x10 mov cx,0 mov dx,0 x: cmp cx,320 je y ; mov dx, 2 call draw add cx,1 jmp x y: add dx,1 mov cx,0 cmp dx,200 je draw_menu jmp x draw_menu: mov cx,0 mov dx,183 x2: cmp cx,320 je y2 call draw_greem add cx,1 jmp x2 y2: add dx,1 mov cx,0 cmp dx,200 je draw_hello jmp x2 draw_hello: mov cx,0 mov dx,0 _loop_x: mov cl, [si] add si, 1 mov dl, [di] add di, 1 cmp cl, 0 je fin call draw_font ; mov ah, 0x0e ; mov bx, 15 ; int 0x10 jmp _loop_x ; _loop_y: jmp fin draw: pusha mov ah,0ch mov al,8 mov bh,0 int 0x10 popa ret font_X: DB 13,12,13,11,13,13,13,13,13,13,13,13,13,13,11,12,13,14,15,1,2,3,6,7,8,2,7,2,7,2,7,2,4,5,7,2,4,5,7,2,4,5,7,2,4,5,7,3,6,3,6,3,6,3,6,3,6,20,21,19,20,21,22,19,20,21,22,20,21,27,28,29,26,30,25,31,25,31,25,31,25,31,25,31,25,31,25,31,25,31,25,31,26,30,27,28,29,35,36,37,39,34,38,39,33,39,33,39,33,34,35,36,37,38,39,33,39,33,39,33,34,38,33,35,36,37,50,51,53,54,49,50,51,52,53,54,55,49,50,51,52,53,54,55,49,50,51,52,53,54,55,50,51,52,53,54,51,52,53,52 db 0 font_Y: DB 1,2,2,3,3,4,5,6,7,8,9,10,11,12,13,13,13,13,13,1,1,1,1,1,1,2,2,3,3,4,4,5,5,5,5,6,6,6,6,7,7,7,7,8,8,8,8,9,9,10,10,11,11,12,12,13,13,6,6,7,7,7,7,8,8,8,8,9,9,1,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,13,1,1,1,1,2,2,2,3,3,4,4,5,6,7,7,7,8,9,10,10,11,11,12,12,12,13,13,13,13,4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,7,7,7,7,7,7,7,8,8,8,8,8,9,9,9,10 db 0 draw_greem: ; pop dx ;坐标y ; pop cx ;坐标x pusha mov ah,0ch mov al,7 mov bh,0 int 0x10 popa ret draw_font: ; pop dx ;坐标y ; pop cx ;坐标x pusha mov ah,0ch mov al,10 mov bh,0 int 0x10 popa ret fin: HLT jmp fin times 510-($-$$) db 0 dw 0xaa55
通过nasm编译
nasm kernel.asm -f bin -o kernal.img
最终效果图
">
font_X,font_Y是什么呢,是文字的像素坐标,因为不会用汇编操作多维数组,只能自己用Python写个转换程序,将文字转换为坐标 - =
Python转换程序
def draw_font(chars, pianyi_x=0, pianyi_y=0): chars = chars.strip().splitlines() y = 0 + pianyi_y x = 0 + pianyi_x _x = [] _y = [] output = [] for line in chars: x = 0 + pianyi_x for i in line: if i == '*': output.append((x, y)) x += 1 y += 1 print(output) s = "" for point in output: s += str(point[0]) + "," print("x:DB " + s.strip(",")) s = "" for point in output: s += str(point[1]) + "," print("y:DB " + s.strip(",")) charsH = ''' ........ ........ ........ ........ .**.**.. *******. *******. *******. .*****.. ..***... ...*.... ........ ........ ........ ........ ........ ''' charW = ''' ''' draw_font(charsH, 1 + 16 + 16 + 8 + 8)
它将输出
x:DB 50,51,53,54,49,50,51,52,53,54,55,49,50,51,52,53,54,55,49,50,51,52,53,54,55,50,51,52,53,54,51,52,53,52 y:DB 4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,7,7,7,7,7,7,7,8,8,8,8,8,9,9,9,10
将这个数值放到那段代码中即可。最后,其实可以把界面画的更好看点,但是因为没有做内核加载器,我们的程序只能512字节的大小,我这段代码刚刚差不多。我明天可以试试做个内核加载器将界面完善一下~
发表评论