30天自制操作系统
Day 1
Binary Editor
nask 汇编
Binary Editor
RESB指令是“reserve byte”的略写,如果想要从现在的地址开始空出10个字节来,就可以写成RESB 10,意思是我们预约了这10个字节。填0。
RESB 0x1fe-$了。这个美元符号的意思如果不讲,恐怕谁也搞不明白,它是一个变量,可以告诉我们这一行现在的字节数(如果严格来说,有时候它还会有别的意思,关于这一点我们明天再讲)。在这个程序里,我们已经在前面输出了132字节,所以这里的$就是132。
Day 2
ORG 0x7c00 ; origin 0x7c00
首先是ORG指令。这个指令会告诉nask,在开始执行的时候,把这些机器语言指令装载到内存中的哪个地址。
Register 16bit
AX——accumulator,累加寄存器
CX——counter,计数寄存器
DX——data,数据寄存器
BX——base,基址寄存器
SP——stack pointer,栈指针寄存器
BP——base pointer,基址指针寄存器
SI——source index,源变址寄存器
DI——destination index,目的变址寄存器
Register low 8 bit
另一方面,CPU中还有8个8位寄存器。
AL——累加寄存器低位(accumulator low)
CL——计数寄存器低位(counter low)
DL——数据寄存器低位(data low)
BL——基址寄存器低位(base low)
AH——累加寄存器高位(accumulator high)
CH——计数寄存器高位(counter high)
DH——数据寄存器高位(data high)
BH——基址寄存器高位(base high)
名字看起来有点像,其实这是有原因的:AX寄存器共有16位,其中0位到7位的低8位称为AL,而8位到15位的高8位称为AH。所以,如果以为“再加上这8个8位寄存器,CPU就又可以多保存8个字节了”就大错特错了,CPU还是那个CPU,依然只能存储区区16个字节。CPU的存储能力实在是太有限了
那BP、SP、SI、DI怎么没分为“L”和“H”呢?能这么想,就说明大家已经做到举一反三了,但可惜的是这几个寄存器不能分为“L”和“H”。如果无论如何都要分别取高位或低位数据的话,就必须先用“MOV,AX,SI”将SI的值赋到AX中去,然后再用AL、AH来取值。这貌似是英特尔(Intel)的设计人员的 思维模式。
register 32 bit
EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI
这些就是32位寄存器。虽说EAX是个32位寄存器,但其实跟前面一样,它有一部分是与AX共用的,32位中的低16位就是AX,而高16位既没有名字,也没有寄存器编号。也就是说,虽然我们可以把EAX作为2个16位寄存器来用,但只有低16位用起来方便;如果我们要用高16位的话,就需要使用移位命令,把高16位移到低16位后才能用。
segment Register 16bit
ES——附加段寄存器(extra segment)
CS——代码段寄存器(code segment)
SS——栈段寄存器(stack segment)
DS——数据段寄存器(data segment)
FS——没有名称(segment part 2)
GS——没有名称(segment part 3)
CPU & memory
下面我们来看“MOV AL,[SI]”。如果这个命令是“MOV AL,SI”的话,不用多说大家也都能明白它的意思,可这里用方括号把SI括了起来。如果在汇编语言中出现这个方括号,寄存器所代表的意思就完全不一样了
CPU与内存之间的电信号交换,并不仅仅是为了存取数据。因为从根本上讲,程序本身也是保存在内存里的。程序一般都大于44字节,不可能保存在寄存器中,所以规定程序必须放在内存里。CPU在执行机器语言时,必须从内存一个命令一个命令地读取程序,顺序执行
MOV指令的数据传送源和传送目的地不仅可以是寄存器或常数,也可以是内存地址。
MOV BYTE [678],123
这个指令是要用内存的“678”号地址来保存“123”这个数值。
至于内存地址的指定方法,我们不仅可以使用常数,还可以用寄存器。比如“BYTE [SI]”、“WORD [BX]”等等。如果SI中保存的是987的话,“BYTE [SI]”就会被解释成“BYTE [987]”,即指定地址为987的内存。
根据以上说明我们知道可以用下面这个指令将SI地址的1字节内容读入到AL。
MOV AL, BYTE [SI]
可是MOV指令有一个规则1,那就是源数据和目的数据必须位数相同。也就是说,能向AL里代入的就只有BYTE,这样一来就可以省略BYTE,即可以写成:
MOV AL, [SI]
JE
jump equre 如果相等就跳转
BIOS
电脑里有个名为BIOS的程序,出厂时就组装在电脑主板上的ROM2单元里。电脑厂家在BIOS中预先写入了操作系统开发人员经常会用到的一些程序,非常方便。BIOS是英文“basic input output system”的缩写,直译过来就是“基本输入输出系统(程序)”。
ORG 0x7c00
内存的0号地址,也就是最开始的部分,是BIOS程序用来实现各种不同功能的地方,如果我们随便使用的话,就会与BIOS发生冲突,结果不只 是BIOS会出错,而且我们的程序也肯定会问题百出。另外,在内存的0xf0000号地址附近,还存放着BIOS程序本身,那里我们也不能使用。
内存里还有其他不少地方也是不能用的,所以我们作为操作系统开发者,不得不注意这一点。
0x00007c00-0x00007dff :启动区内容的装载地址
程序中ORG指令的值就是这个数字。而且正是因为我们使用的是这个同样的数字,所以程序才能正常运行。
http://community.osdev.info/?(AT)memorymap
Day3 进入32位模式并导入C语言
JC
所谓JC,是“jump if carry”的缩写,意思是如果进位标志(carry flag)是1的话,就跳转。这里突然冒出来“进位标志”这么个新词,不过大家不用担心,很快就会明白的。
INT 0x13
至于“INT 0x13”这个指令,我们虽然知道这是要调用BIOS的0x13号函数,但还不明白它到底是干什么用的,那就来查一下吧。当然还是来看下面这个(AT)BIOS网页,
http://community.osdev.info/?(AT)BIOS
内存地址的 寄存器保存
一般说来,如果能用一个寄存器来表示内存地址的话,当然会很方便,但一个BX只能表示0~0xffff的值,也就是只有0~65535,最大才64K。大家的电脑起码也都有64M内存,或者更多,只用一个寄存器来表示内存地址的话,就只能用64K的内存,这太可惜了。
于是为了解决这个问题,就增加了一个叫EBX的寄存器,这样就能处理4G内存了。这是CPU能处理的最大内存量,没有任何问题。但EBX的导入是很久以后的事情,在设计BIOS的时代,CPU甚至还没有32位寄存器,所以当时只好设计了一个起辅助作用的段寄存器(segment register)。在指定内存地址的时候,可以使用这个段寄存器。
我们使用段寄存器时,以ES:BX这种方式来表示地址,写成“MOV AL,[ES:BX]”,它代表ES×16+BX的内存地址。我们可以把它理解成先用ES寄存器指定一个大致的地址,然后再用BX来指定其中一个具体地址。
32位系统0x8000启动区装载地址
0x8000~0x81ff这512字节是留给启动区的,要将启动区的内容读到那里,所以就这样吧。
0x7c00~0x7dff用于启动区,0x7e00以后直到0x9fbff为止的区域都没有特别的用途,操作系统 可以随便使用。