自制操作系统--lelo OS (基于OSASK)day3-hello,world! (汇编进阶版)和MakeFile入门

这将是一个很有意义的博客,一个很长的博客。

我一直非常喜欢计算机编程,而且还经常把自己的ThinkPad搞得乱七八糟,重装过无数次系统,有时我还妄想自己要编一个操作系统,不过愿望马上就要实现了,几十天后,我们将编成一个独一无二的操作系统。

昨天我们学习了day2,并用汇编语言写了hello,world!,但是我们昨天写的程序根本看不出写的是什么意思。所以我今天,要把这个程序加工润色一下。

准备工作

主机配置:

ThinkPad E480
windows10 1909 专业工作站版
8G内存
Intel core i5

准备工具:

30天自制操作系统光盘
qemu(光盘自带)
开始!

资源下载

下载30天自制操作系统光盘

汇编程序加工润色

开始编写

重新创建leloOS2.nas,输入以下内容:

; hello-os
; TAB=4

; 以下は標準的なFAT12フォーマットフロッピーディスクのための記述

		DB		0xeb, 0x4e, 0x90
		DB		"HELLOIPL"		; ブートセクタの名前を自由に書いてよい(8バイト)
		DW		512				; 1セクタの大きさ(512にしなければいけない)
		DB		1				; クラスタの大きさ(1セクタにしなければいけない)
		DW		1				; FATがどこから始まるか(普通は1セクタ目からにする)
		DB		2				; FATの個数(2にしなければいけない)
		DW		224				; ルートディレクトリ領域の大きさ(普通は224エントリにする)
		DW		2880			; このドライブの大きさ(2880セクタにしなければいけない)
		DB		0xf0			; メディアのタイプ(0xf0にしなければいけない)
		DW		9				; FAT領域の長さ(9セクタにしなければいけない)
		DW		18				; 1トラックにいくつのセクタがあるか(18にしなければいけない)
		DW		2				; ヘッドの数(2にしなければいけない)
		DD		0				; パーティションを使ってないのでここは必ず0
		DD		2880			; このドライブ大きさをもう一度書く
		DB		0,0,0x29		; よくわからないけどこの値にしておくといいらしい
		DD		0xffffffff		; たぶんボリュームシリアル番号
		DB		"HELLO-OS   "	; ディスクの名前(11バイト)
		DB		"FAT12   "		; フォーマットの名前(8バイト)
		RESB	18				; とりあえず18バイトあけておく

; プログラム本体

		DB		0xb8, 0x00, 0x00, 0x8e, 0xd0, 0xbc, 0x00, 0x7c
		DB		0x8e, 0xd8, 0x8e, 0xc0, 0xbe, 0x74, 0x7c, 0x8a
		DB		0x04, 0x83, 0xc6, 0x01, 0x3c, 0x00, 0x74, 0x09
		DB		0xb4, 0x0e, 0xbb, 0x0f, 0x00, 0xcd, 0x10, 0xeb
		DB		0xee, 0xf4, 0xeb, 0xfd

; メッセージ部分

		DB		0x0a, 0x0a		; 改行を2つ
		DB		"hello, leloOS"
		DB		0x0a			; 改行
		DB		0

		RESB	0x1fe-$			; 0x001feまでを0x00で埋める命令

		DB		0x55, 0xaa

; 以下はブートセクタ以外の部分の記述

		DB		0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
		RESB	4600
		DB		0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
		RESB	1469432
保存退出。

虚拟机运行

打开cmd.bat,输入以下内容:

asm
run

出现这个画面说明运行成功!
在这里插入图片描述

命令学习


今天我们要学习一些命令。
首先是ORG指令。这个指令会告诉nask,在开始执行的时候,把这些机器语言指令装载到内 存中的哪个地址。如果没有它,有几个指令就不能被正确地翻译和执行。另外,有了这条指令的 话,美元符($)的含义也随之变化,它不再是指输出文件的第几个字节,而是代表将要读入的 内存地址。
ORG指令来源于英文“origin”,意思是“源头、起点”。它会告诉nask,程序要从指定的这 个地址开始,也就是要把程序装载到内存中的指定地址。这里指定的地址是0x7c00,至于指定它 的原因我们会在后文(本节末尾)详述。
下一个是JMP指令,它相当于C语言的goto语句,来源于英文的jump,意思是“跳转”。简 单吧!
再下面是“entry:”,这是标签的声明,用于指定JMP指令的跳转目的地等。这与C语言很像。 entiy这个词是“入口”的意思。
然后我们来看看MOV指令。MOV指令应该是最常用的指令了,即便在这段程序里,MOV指 令的使用次数也仅次于DB指令。这个指令的功能非常简单,即赋值。虽然简单,但笔者认为, 只要完全掌握了MOV指令,也就理解了汇编语言的一大半。所以,我们在这里详细地讲解一下 这个指令。
“MOV AX,0”,相当于“AX=0;M这样一个赋值语句。同样,“MOV SS»AX”就相当于“SS=AX;”。 或许有人会问:“这个AX和SS是什么东西? ”这个问题我们待会儿再回答。
MOV命令源自英文“move”,意思是“移动”。“赋值”与“移动”虽然有些相似,但毕竟还 是不同的。一般说来,如果我们把一个东西移走了,它原来所占用的位置就会空出来。但是,在 执行了 “MOVSS,AX”语句之后,AX并没有变“空”,还保留着原来的值不变。所以这实际上 是“赋值”,而不是“移动”。如果用“COPY”指令来打比方,理解起来就简单多了。至于为什
么成了MOV指令,笔者也搞不明白。
现在来说说AX和SS。CPU里有一种名为寄存器的存储电路,相当于机器语言中的变量。具 有代表性的寄存器有以下8个。各个寄存器本来都是有名字的,但现在知道这些名字的机会已经 不多了,所以在这里顺便介绍一下。
AX accumulator,累加寄存器
CX——ounter,计数寄存器
dx~ ta,数据寄存器
BX—base,基址寄存器
SP stack pointer,栈指针寄存器
BP base pointer,基址指针寄存器
SI source index,源变址寄存器
DI destination index,目的变址寄存器
这些寄存器全都是16位寄存器,因此可以存储16位的二进制数。虽然它们都有上面这种正式 名称,但在平常使用的时候,人们往往用简单的英文字母来代替,称它们为“AX寄存器”、“SI 寄存器”等。
其实寄存器的全名还是很能说明它本来的意义的。比如在这8个寄存器中,不管使用哪一个, 差不多都能进行同样的计算,但如果都用AX来进行各种运算的话,程序就可以写得很简洁。
“ADD CX,0x1234”编译成81 C1 34 12,是一个4字节的命令。
而“ADD AX,0x1234”编译成05 34 12,是一个3字节的命令。
从上面例子可以看出,这里所说的“程序可以写得简洁”是指“用机器语言写程序”的情况, 从汇编语言的源代码上是看不到这些区别的。如果我们不懂机器语言,就会有很多地方难以理解。
再说说别的寄存器,CX是为方便计数而设计的,BX则适合作为计算内存地址的基点。其他 的寄存器也各有优点。
关于AX、CX、DX、BX这几个寄存器名字的由来,虽然我们找不到缩写为X的单词,但这 个X表示扩展(extend)的意思。之所以说扩展是因为在这之前CPU的寄存器都是8位的,而现在 一下变成了 16位,扩展了一倍,所以发明者在原来寄存器的名字后面加了个X,意思是说“扩张 了一倍,了不起吧!”。大家可能注意到了这几个寄存器的排列顺序,它并不遵循名称的字母顺序。 没错,其实这是按照机器语言中寄存器的编号顺序排列的,可不是笔者随手瞎写的哦。
这8个寄存器全部合起来也才只有16个字节。换句话说,就算我们把这8个寄存器都用上,CPU 也只能存储区区16个字节。
另一方面,CPU中还有8个8位寄存器。
AL 累加寄存器低位(accumulatorlow)
CL 计数寄存器低位(counter low)
dl 据寄存器低位(data low)
BL 址寄存器低位(base low)
ah 累加寄存器高位(accumulatorhigh)
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)的设计人员的思维模式。
“喂,我家的电脑是32位的,可不是16位。这样就能以32位为单位来处理数据了吧?那32位 的寄存器在哪儿呀? ”大家可能会有这样的疑问,下面笔者就来回答这个问题。
EAX, ECX, EDX, EBX, ESP, BBP, ESI, EDI
这些就是32位寄存器。这次的程序虽然没有用到它们,但如果想用也是完全可以使用的。在 16位寄存器的名字前面加上一个E就是32位寄存器的名字了。这个字母E其实还是来源于 “Extend”(扩展)这个词。在当时主流为16位的时代里,能扩展到32位算是个飞跃了。虽说EAX 是个32位寄存器,但其实跟前面一样,它有一部分是与AX共用的,32位中的低16位就是AX,而 高16位既没有名字,也没有寄存器编号。也就是说,虽然我们可以把EAX作为2个16位寄存器来 用,但只有低16位用起来方便;如果我们要用高16位的话,就需要使用移位命令,把高16位移到 低16位后才能用。
这么说来,就是32位的CPU也只能存储区区32字节,存储能力还真是小得可怜。
有的读者用的电脑可能是64位的,但我们这次不使用64位模式,所以这里也就不再赘述了。 关于寄存器本来笔者就想介绍到这儿,但是突然想起来,还有一个段寄存器(segment register),所以在这里一并给大家介绍一下吧。这些段寄存器都是16位寄存器。
ES 附加段寄存器(extra segment)
CS 码段寄存器(code segment)
SS 栈段寄存器(stack segment)
DS 据段寄存器(data segment)
FS 没有名称(segment part 2)
GS 没有名称(segment part 3 )
关于段寄存器的具体内容,我们保留到明天再详细讲解。现在,我们暂时先在这些寄存器里 放上0就可以了。
好,到这里寄存器已经讲得差不多了。
那么接下来我们继续看程序,下一个看不懂的语句应该是“MOVSIjnsg”吧。MOV是赋值, 意思是SI=msg,而msg是下面将会出现的标号。“把标号赋值给寄存器?这到底是怎么回事? ” 为了理解这个谜团,我们先回到JMP指令。
前面我们已经看到了 “JMPentry”这个指令,其实把它写成“JMP0x7c50”也完全没有问题。 本来JMP指令的基本形式就是跳转到指定的内存地址,因此这个指令就是H1CPU去执行内存地址 0x7c50的程序。
之所以可以用“JMPentry”来代替“JMP0x7c50”,是因为entry就是0x7c50o在汇编语言中, 所有标号都仅仅是单纯的数字。每个标号对应的数字,是由汇编语言编译器根据ORG指令计算出 来的。编译器计算出的“标号的地方对应的内存地址”就是那个标号的值。
所以,如果我们在这个程序中写了 “MOVAX,entry”,那它就会把0x7c50代入到AX寄存器里, 我们代入到AX寄存器中的就是这个简单的数字。大家可不要以为写在“entry”下面的程序也都 被储存了,这是不可能的。
那么“MOV SI,msg”会怎么样呢?由于在这里msg的地址是0x7c74,所以这个指令就是把 0x7c74代入到SI寄存器中去。
下面我们来看“MOVAL,[Sir。如果这个命令是“MOVAL,SI”的话,不用多说大家也都能 明白它的意思,可这里用方括号把SI括了起来。如果在汇编语言中出现这个方括号,寄存器所代 表的意思就完全不一样了。
这个记号代表“内存”。如果大家自己组装过电脑,就知道所谓“内存”,指的是256MB或512MB 的那个零件。
内存
到现在为止,内存这个词我们已经使用了很多次了,可一直都还没有正式讲解过,那内存到 底是什么呢?简单地用一句话来概括,它就是一个超大规模的存储单元“住宅区”。用“住宅区” 来比喻内存再合适不过了,它能充分体现出存储单元紧密、整齐地排列在一起的样子。英语中 memory是“记忆”的意思,这里我们把它译成“内存”。
通过对寄存器的讲解,现在大家都知道了CPU的存储能力很差,如果我们想让CPU处理大量 信息,就必须给它另外准备一套用于存储的电路。因为即便是32位的CPU,把所有普通的寄存器 都加在一起,最多也只能存储32个字节的数据。就算把段寄存器也全部用上,也才只有44字节。 这么小的存储空间,就连启动电脑所必需的启动区数据都放不下。
现在大家已经知道了存储单元的必要性,那么我们下面就讲内存。内存并不在CPU的内部, 而是在CPU的外面。所以对于CPU来说,内存实际上是外部存储器。这点很重要,就是说CPU要 通过自己的一部分管脚(引线)向内存发送电信号,告诉内存说:“喂,把5678号地址的数据通 过我的管脚传过来(严格说来,CPU和内存之间还有称为芯片(chipset)的控制单元)!” CPU向 内存读写数据时,就是这样进行信息交换的。
CPU与内存之间的电信号交换,并不仅仅是为了存取数据。因为从根本上讲,程序本身也是 保存在内存里的。程序一般都大于44字节,不可能保存在寄存器中,所以规定程序必须放在内存 里。CPU在执行机器语言时,必须从内存一个命令一个命令地读取程序,顺序执行。
内存虽然如此重要,但它的位置却离CPU相当远。就算是只有10厘米左右的距离吧,可这与 CPU中的半导体相比已经非常遥远了。所以,当CPU向内存请求数据或者输出数据的时候,内存 需要花很长时间才能够完整无误地实现CPU的要求(CPU运行速度极快,所以即使在10厘米这么 短的距离内传送电信号,所花的时间都不容忽视)。所以,虽然内存比寄存器的存储能力大很多 个数量级,但使用内存时速度很慢。CPU访问内存的速度比访问寄存器慢很多倍,记住这一点, 我们才能开发出执行速度快的程序来。
寄存器 内存
能够计算出来 有很多空间
可以放很多东西
基础知识我们讲完了,下面再回到汇编语言。MOV指令的数据传送源和传送目的地不仅可 以是寄存器或常数,也可以是内存地址。这个时候,我们就使用方括号([])来表示内存地址。 另外,BYTE、WORD、DWORD等英文词也都是汇编语言的保留字,下面举个例子吧。
MOV BYTE [678],123
这个指令是要用内存的“678”号地址来保存“123”这个数值。虽然指令里有数字,看起来 像那么回事,但实际上内存和CPU一样,根本就没有什么数值的概念。所谓的“678”,不过就是 一大串开(ON)或者关(OFF)的电信号而已。当内存收到这一串信号时,电路
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值