[汇编]如何写一个简单的操作系统(未完)

我发现翻译是个累活,得尼玛组织语言,得写成人话,我还老纠结这东西我是不是该加几个词来给他组织成人话,又怕曲解了原文的意思!!啥也不说了,不足之处请砍砖!

原文地址:http://mikeos.berlios.de/write-your-own-os.html

How to write a simple operating system

如何写一个简单的操作系统

原文作者是 Mike Saunders和MikeOS的开发者们

陈冠羽译 

正文开始

这个文章介绍了如何使用汇编语言,写和构建你的第一个操作系统。它解释了你需要什么,比如PC开机流程和汇编语言基础以及如何更近一步的学习(take it further)。这个最终的操作系统是非常小而且只有很少的特性(其中内嵌一个bootloader)。但是它是你可以更深一步探索的起点。

        在你读完这个Guide之后,你可以通过探索一个更大的、使用x86汇编编写的,叫做MikeOS的项目去扩展你的能力。

要求

     首先,一些编程经验是必须的。如果你写过一些高级语言,比如PHP或者JAVA那就太好了。但是最好你还是要有一些低等语言的知识,比如C(C什么时候成低等语言了),尤其是关于内存和指针的知识。

 

     For this guide我们使用Linux。操作系统的开发当然是在Windows上,但是你也可以点几下鼠标,输入一些命令行就能简单的在Linux上火的全部的开发工具链(toolchain)。Linux还可以帮你轻松的制作软盘或光盘镜像文件,当然,你是不需要安装这些实体设备的。

 

     在今天,安装Linux已经是一件非常简单的事情了,如果你不想装多系统,你可以在VMware或者VirtualBox上面安装Ubuntu。当你进入Ubuntu以后,你只需要在终端输入下面的命令就可以获得全部的必要工具。

 

sudo apt-get install build-essential qemu nasm

 

     你安装的这个工具链(toolchain)(编译器等等),这些工具包括QEMU PC模拟器和一个NASM汇编器,他们可以编译你的汇编代码并且把它们变成可执行文件。

PC primer

     如果你正在写一个运行在x86平台上的操作系统(这是最好的选择,由于大量的可用文档),你必须理解一些计算机开机时的基础知识。非常幸运的是,你不用老想着那些非常复杂的问题,比如显卡驱动和网络协议,接下来你只需要关注一些必要的内容。

 

     当一台电脑通电后,它就开始执行BIOS(Basic Input/Output System),这些都是构建一些迷你操作系统所必须的。它会运行一些硬件的检测(比如内存)然后在屏幕上输入诊断信息和图形(比如DELL标志)。当这些完成之后,它开始从能到找的任何媒体中加载你的操作系统。许多PC会跳到你的硬盘的MBR(Master Boot Record)开始执行它的代码,在硬盘开始的512-byte中;有些会找并且执行你软驱或光驱中的可执行代码。

 

     这些都取决于你的boot order,它可以在BIOS中被自定义。BIOS会加载你选择的媒体介质中的前512 bytes到内存,然后开始执行。这个就是bootloader,这个小程序会加载你的操作系统主核心或者一个更大的boot程序(比如Linux系统上的GRUB/LILO)。在这512 byte的bootloader的结尾有两个特别的数字是用来告诉操作系统这是一个boot sector,我们一会会讲到。


此处略过一段回来补上(个人感觉这段没啥用,尽管上面也很多废话)...




AX, BX, CX, DX

多方面用途寄存器(rigister)用于存储一些你需要使用的数字。比如你可以使用AX去存储从键盘输入的字符。用CX去充当一个循环计数器。(注:这些16-bit16位)寄存器可以被分割成八位寄存器,比如AH/ALBH/BL等等)

SI, DI

源变址寄存器(Source Index register)和目标变址寄存器(Destination Date Index register),这些指针用来在内存中取回和存储数据。

SP

栈指针(一会解释)

IP

(有时是CP

指令/代码指针。它包含了在内存中即将被执行的指令的位置。当一个指令完成,它就会被增加和指向(movs on)下一个指令。你可以改变这个寄存器中的内容去指向你的代码。



     因此你可以使用这些寄存器去存储一些关于你要做的事情的数字(指令),比如变量,但是他们大多数情况会被修剪大小(but they're much more fixed in size and purpose。 当然还有其他情况,比如段寄存器。因为一些老电脑的限制,内存会被分成64K段。(译者注:老CPU指的是16位的,能存储的最大值是2的16次方,也就是65536 => 64K)这真是一个复杂的问题,但是有幸的是在一开始你不需要担心它,因为你的操作系统会小于1K(kilobyte)。在MikeOS中我们限制我们自己在一个64K的段中,因此我们不用在段寄存器问题上打转儿。

 

     栈(stack)是一个在你内存中用来储存临时信息的区域。它被叫做栈是因为每个数字都会被堆积到之前插入的数的顶部。设想一下桶装的品客薯片(Pringles tube,见下图(图1),译者加),如果你放进去一张扑克牌(playing card),然后是一个iPod Shuffle,最后是一个杯垫(beermat),当你要拿出来的时候就是一个相反的顺序(译者注:先进后出,后进先出)(杯垫,iPod Shuffle,扑克牌)。这跟你放入数字是到栈里是一样的,你push进5,7,15在栈中,当你执行pop的时候,先出的是15然后是7最后是5。在汇编语言中,你可以先把寄存器给push到栈中过一会再给他们pop出来,这对于你想要临时储存寄存器中的值是非常有用的,当你要把寄存器干别的事用的时候。

 

     计算机的内存可以被看作是一个线性的鸽笼式分类架(pigeon-holes)表,从0到你所安装的内存的上限(在现代计算机中一般都是数以百万byte)。At byte number 53,634,246 in your RAM, for instance, you may have your web browser code to view this document.但是我们人类一般都是以10进制(decimal)计数(10,100,1000等等 – 十进制),对于电脑来说2进制对于他们更方便一些。所以我们使用16进制(hexadecimal)去表示数字,看下面的这张图(图2):

 

图2


图1



此处又略去一段,之后补上

 

 

mov

把一个寄存器中的内容或者一个数拷贝到一个寄存器中。举个例子,mov ax, 30 的意思是把30放到ax寄存器中。使用方括号(square brackets)你可以把一个寄存器的位置拷贝到一个寄存器中。举个例子 mov ax, [bx],意思是把bx值所对应的内存地址位置所对应的值(见译者注1)拷贝到AX中。你同样也可以移动一个寄存器中的数到另外一个寄存器中去,比如mov bx, cx

 

译者注1

比如我们mov bx, 80

然后我们又mov, ax, [bx]

错误理解是,不是把80考到了AX

而是把在内存中位置为80的值拷贝到了AX中,还是有点抽象哈

看下图

79

80

100h

81

我们是把100h考到了AX中。

add/sub

(译者直接讲解:add的意思是加法,如果ax的值是50,那么执行add ax, 50之后,ax的结果就是ax = 100sub的意思就是减法)

cmp

拿一个数和寄存器中的数比较(Compares a register with a number)。cmp cx, 12的意思就是比较cx中的数和12是不是一样的。这个操错会更新一个CPU中的寄存器,它叫做FLAGS它会包含最后的操作结果。在这里,如果12大于CX,那么他会在FLAGS中产生一个阴性结果 negative result),我们可以在下面的操作中使用它。

译者注:可以查阅我的另外一篇文章:关于CMP后走向、转移的问题

Jmp/ jg / jl …

跳转到这个代码的其他部分。jmp标签的意思跳转到源代码中,我们已有的标签中。但是你可以让它有条件的去跳转,但这要基于我们上面所说到的CPIFLAGS标签的结果。举个例子,如果cmp指令判断出,如果前面的小于后面的,而且你也有jl标签,那么就会执行jl。同样的道理,如果前面的大于或等于后面的,那么就执行jgl

下面是译者例子

第一种情况:

mov ax, 50

cmp ax, 100

这样就会执行jl

第二种情况:

mov ax, 50

cmp ax, 20

这样就会执行jgl

第三种情况:

Mov ax, 50

Cmp ax, 50

这样也会执行jgl

译者注:可以查阅我的另外一篇文章:关于CMP后走向、转移的问题

int

中断(interrupt)程序然后跳转到内存中的一个指定位置(Interrupt the program and jump to a specified place in memory)。操作系统提供中断,就像高级语言提供了一个子程序一样。比如在MS-DOS中,21h中断提供了DOS服务(比如打开一个文件)。Typically, you put a value in the AX register, then call an interrupt and wait for a result (passed back in a register too).当你正在写一个擦写功能时,你可以调用BIOSint 10h, int 13h, int 14h或者int 16h比如去打印一个字符串或者去读软盘的某一个扇区等等。

 

让我们看一些指令片段:


mov bx, 1000h
       mov ax, [bx]
       cmp ax, 50
       jge label
       ...
label:
       mov ax, 10


在第一行指令中,我们把1000h这个数方到BX寄存器中。

第二行指令中,我们把BX寄存器在内存中的位置放到了AX中。

如果我们只是执行mav ax, bx,我们只是把1000h这个数拷贝到了AX中,但是我们使用方括号,就好比对电脑说,不要拷贝BX中的内容到AX中,我们要拷贝BX中值的位置所对应的那个值。(译者注,详细看MOV解释,实在不行,直接找我。)

第三行指令,我们使用cmp指令去比较AX中的数字和50(十进制的50,结尾如果没有h就是十进制)的大小。如果AX大于或者等于50那么jge指令就会被执行。如果不是,那么就会继续执行后面的…

 

最后一件事:你可以使用db(define byte)指令(译者注:伪指令)来插入一条数据(译者注:可以是字符串)。看下面指令

 

mylabel: db 'Message here', 0

 

译者注 start :db后面可以用,来分割N个值(N大于等于1),如上面所示,后面跟了一个十进制的0,那么意识就是ASCII表中的位置是0的对应。比如,如果我们想输出一条指令,而且后面要有换行,就比如Java中的System.out.println一样,那么,我们光是mylabel: db ‘message’这样是不够的,那肯定有朋友会想到是不是应该是mylabel: db ‘message\n’呢,这样也是不行的,必须是mylabel: db ‘message’, 0xa 也可以是 mylabel: db ‘message’, 10。

我先解释一下0xa也是十六进制的一种表现方式,同ah,所以写ah也是一样的。我们可以参照图2发现,十六进制的a就是十进制的10,所以我们写10也可以的。

 

关于ASCII的对照大家可以用度娘谷姐一下。

 

下面我举几个十进制的对照

 

8、9、10 和 13 可以分别转换为退格符、制表符、换行符和回车符

 

译者注 end


未完...


转载于:https://my.oschina.net/wchen/blog/124147

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值