[转载] 30天自制操作系统基本镜像的汇编代码(day2)

本文整理自书《30天自制操作系统》的原文,这位老哥写的书是在是太棒啦,我这里把他第一个镜像汇编源代码 copy 过来,并做一些最基本的注释,当年学的单片机和微机原理都忘光了hhhhh

本文也来回忆一下汇编基本功

0. 操作系统源代码

; hello-os
; TAB=4

		ORG		0x7c00			; 指明程序装载地址

; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy code

		JMP		entry
		DB		0x90
		DB		"HELLOIPL"		; 启动扇区名称(8字节)
		DW		512				; 每个扇区(sector)大小(必须512字节)
		DB		1				; 簇(cluster)大小(必须为1个扇区)
		DW		1				; FAT起始位置(一般为第一个扇区)
		DB		2				; FAT个数(必须为2)
		DW		224				; 根目录大小(一般为224项)
		DW		2880			; 该磁盘大小(必须为2880扇区1440*1024/512)
		DB		0xf0			; 磁盘类型(必须为0xf0)
		DW		9				; FAT的长度(必??9扇区)
		DW		18				; 一个磁道(track)有几个扇区(必须为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字节

; 程序主体

entry:
		MOV		AX,0			; 初始化寄存器
		MOV		SS,AX
		MOV		SP,0x7c00
		MOV		DS,AX
		MOV		ES,AX

		MOV		SI,msg
putloop:
		MOV		AL,[SI]
		ADD		SI,1			; 给SI加1
		CMP		AL,0
		JE		fin
		MOV		AH,0x0e			; 显示一个文字
		MOV		BX,15			; 指定字符颜色
		INT		0x10			; 调用显卡BIOS
		JMP		putloop
fin:
		HLT						; 让CPU停止,等待指令
		JMP		fin				; 无限循环

msg:
		DB		0x0a, 0x0a		; 换行两次
		DB		"hello, world"
		DB		0x0a			; 换行
		DB		0

		RESB	0x7dfe-$		; 填写0x00直到0x001fe

		DB		0x55, 0xaa

1. 部分寄存器

AX——accumulator,累加寄存器
CX——counter,计数寄存器
DX——data,数据寄存器
BX——base,基址寄存器
SP——stack pointer,栈指针寄存器
BP——base pointer,基址指针寄存器
SI——source index,源变址寄存器
DI——destination index,目的变址寄存器

这些寄存器全都是16位寄存器,因此可以存储16位的二进制数。

关于AXCXDXBX 这几个寄存器名字的由来,虽然我们找不到缩写为X的单词,但这个X表示扩展(extend)的意思。之所以说扩展是因为在这之前CPU的寄存器都是8位的,而现在一下变成了16位,扩展了一倍,所以发明者在原来寄存器的名字后面加了个X,意思是说“扩张了一倍,了不起吧!”。大家可能注意到了这几个寄存器的排列顺序,它并不遵循名称的字母顺序。没错,其实这是按照机器语言中寄存器的编号顺序排列的,可不是笔者随手瞎写的哦。

虽然我们可以用寄存器来指定内存地址,但可作此用途的寄存器非常有限,只有 BXBPSIDI这几个,剩下的AXCXDXSP不能用来指定内存地址。如果有意见的话,就写邮件找英特
尔的大叔们吧(笑)。
也就是说 MOV BX, [AX] 的内容不存在?

另一方面,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)

BPSPSIDI怎么没分为LH呢?能这么想,就说明大家已经做到举一反三了,但可惜的是这几个寄存器不能分为 LH 。如果无论如何都要分别取高位或低位数据的话,就必须先用MOV,AX,SISI 的值赋到 AX 中去,然后再用ALAH来取值。

32位的寄存器:

EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI

这些就是32位寄存器。这次的程序虽然没有用到它们,但如果想用也是完全可以使用的。在16位寄存器的名字前面加上一个E就是32位寄存器的名字了。这个字母E其实还是来源于 “Extend”(扩展)这个词。在当时主流为16位的时代里,能扩展到32位算是个飞跃了。虽说 EAX 是个32位寄存器,但其实跟前面一样,它有一部分是与 AX 共用的,32位中的低16位就是AX ,而高16位既没有名字,也没有寄存器编号。也就是说,虽然我们可以把 EAX 作为2个16位寄存器来用,但只有低16位用起来方便;如果我们要用高16位的话,就需要使用移位命令,把高16位移到低16位后才能用。

值得一提的是,还有一些16位段寄存器

ES——附加段寄存器(extra segment)
CS——代码段寄存器(code segment)
SS——栈段寄存器(stack segment)
DS——数据段寄存器(data segment)
FS——没有名称(segment part 2)
GS——没有名称(segment part 3)

2. 一些指令

MOV    AX, 0        # 将 AX 寄存器置0
MOV    AX, entry    # 将 entry 地址写入 AX 寄存器
MOV    AX, SS       # 将 SS 寄存器内容写入 AX 寄存器
MOV    AL, [SI]     # 将 SI 寄存器的内容 (也就是地址) 中的内容放入 AL 寄存器
MOV    BYTE [678], 123      # 用内存的 678 号地址来保存 123 这个数值
MOV    WORD [678],123

在这种情况下,内存地址中的678号和旁边的679号都会做出反应,一共是16位。这时,123被解释成一个16位的数值,也就是0000000001111011,低位的01111011保存在678号,高位的00000000保存在旁边的679号。

在这里插入图片描述

指令字节
DBdefine byte1
DWdefine word2
DDdefine double-word4

RESB 指令是 reserve byte 的略写,如果想要从现在的地址开始空出10个字节来,就可以写成RESB 10,意思是我们预约了这10个字节,而且 nask 不仅仅是把指定的地址空出来,它还会在空出来的地址上自动填入0x00

ORG 指令会告诉nask,在开始执行的时候,把这些机器语言指令装载到内存中的哪个地址。如果没有它,有几个指令就不能被正确地翻译和执行。


另外,有了这条指令的话,美元符($)的含义也随之变化,它不再是指输出文件的第几个字节,而是代表将要读入的内存地址。


ORG指令来源于英文“origin”,意思是“源头、起点”。它会告诉nask,程序要从指定的这个地址开始,也就是要把程序装载到内存中的指定地址。

CMP 源自英文中的 compare,意为“比较”。是比较指令。或许有人想,比较指令是干什么的呢?简单说来,它是 if 语句的一部分。譬如C语言会有这种语句:

if(a==3){ 处理; }

即对 a 和 3 进行比较,将其翻译成机器语言时,必须先写 CMP a,3,告诉CPU比较
的对象,然后下一步再写“如果二者相等,需要做什么”。


JE 是条件跳转指令中之一。所谓条件跳转指令,就是根据比较的结果决定跳转或不跳转。就 JE 指令而言,如果比较结果相等,则跳转到指定的地址;而如果比较结果不等,则不跳转,继续执行下一条指令。所以:

CMP AL, 0
JE fin

这两条指令,就相当于:

if (AL == 0) { goto fin; }

JE 指令源自于英文“jump if equal”,意思是如果相等就跳转。

INT 是软件中断指令。这个指令源自英文“interrupt”,是“中途打断”的意思。INT 是用来调用BIOS函数的指令。INT 的后面是个数字,使用不同的数字可以调用不同的函数。这次我们调用的是0x10(即16)号函数,它的功能是控制显卡。


显示一个字符的用法:

AH=0x0e;
AL=character code;
BH=0;
BL=color code;

HLT 是让CPU停止动作的指令,不过并不是彻底地停止(如果要彻底停止CPU的动作,只能切断电源),而是让CPU进入待机状态。只要外部发生变化,比如按下键盘,或是移动鼠标,CPU就会醒过来,继续执行程序。说到这,请大家再仔细看看这个程序,我们会发现其实不管有没有HLT指令,JMP fin都是无限循环,不写HLT指令也可以。所以很少有人一开始就向初学者介绍HLT指令,因为这样只会让话变得很长。


然而笔者讨厌让CPU毫无意义地空转。如果没有HLT指令, CPU就会不停地全力去执行JMP指令,这会使CPU的负荷达到100%,非常费电。这多浪费呀。我们仅仅加上一个HLT指令,就能让CPU基本处于睡眠状态,可以省很多电。什么都不干,还要耗费那么多电,这就是浪费。即便是初学者,最好也要一开始就养成待机时使用HLT指令的习惯。或者说,恰恰应该在初学阶段,就养成这样的好习惯。这样既节能环保,又节约电费,或许还能延长电脑的使用寿命呢。


HLT 指令源自英文“halt”,意思是“停止”。

3. 最终结果

用原作者的工具编译下,生成 img 文件

nask.exe   img.nas   helloimg.img

在这里插入图片描述

4. 其他说明

为什么会有这行代码

ORG		0x7c00

启动区内容的装载地址为 0x00007c00-0x00007dff 共 512 字节

看到这,大家可能会问:“为什么是0x7c00呢? 0x7000不是更简单、好记吗?”其实笔者也是这么想的,不过没办法,当初规定的就是0x7c00。做出这个规定的应该是IBM的大叔们,不过估计他们现在都成爷爷了。


一旦有了规定,人们就会以此为前提开发各种操作系统,因此以后就算有人说“现在地址变成0x7000-0x71ff了,请大家跟着改一下”,也只是空口号,不可能实现。因为硬要这么做的话,那现有的操作系统就必须全部加以改造才能在这台新电脑上运行,这样的电脑兼容性不好,根本就卖不出去。


今后也许大家还会提出很多疑问:“为什么是这样呢?”这些都是当年IBM和英特尔的大叔们规定的。如果非要深究的话,我们倒是也能找到一些当时时代背景下的原因,不过要把这些都说清楚的话,这本书恐怕还要再加厚一倍,所以关于这些问题我们就不过多解释了。

这是执行 nask.exe img.nas ipl.bin ipl.lstipl.lst 的内容

     1 00000000                                 ; hello-os
     2 00000000                                 ; TAB=4
     3 00000000                                 
     4                                          		ORG		0x7c00			; 指明程序装载地址
     5 00007C00                                 
     6 00007C00                                 ; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy code
     7 00007C00                                 
     8 00007C00 EB 4E                           		JMP		entry
     9 00007C02 90                              		DB		0x90
    10 00007C03 48 45 4C 4C 4F 49 50 4C         		DB		"HELLOIPL"		; 启动扇区名称(8字节)
    11 00007C0B 0200                            		DW		512				; 每个扇区(sector)大小(必须512字节)
    12 00007C0D 01                              		DB		1				; 簇(cluster)大小(必须为1个扇区)
    13 00007C0E 0001                            		DW		1				; FAT起始位置(一般为第一个扇区)
    14 00007C10 02                              		DB		2				; FAT个数(必须为2)
    15 00007C11 00E0                            		DW		224				; 根目录大小(一般为224项)
    16 00007C13 0B40                            		DW		2880			; 该磁盘大小(必须为2880扇区1440*1024/512)
    17 00007C15 F0                              		DB		0xf0			; 磁盘类型(必须为0xf0)
    18 00007C16 0009                            		DW		9				; FAT的长度(必??9扇区)
    19 00007C18 0012                            		DW		18				; 一个磁道(track)有几个扇区(必须为18)
    20 00007C1A 0002                            		DW		2				; 磁头数(必??2)
    21 00007C1C 00000000                        		DD		0				; 不使用分区,必须是0
    22 00007C20 00000B40                        		DD		2880			; 重写一次磁盘大小
    23 00007C24 00 00 29                        		DB		0,0,0x29		; 意义不明(固定)
    24 00007C27 FFFFFFFF                        		DD		0xffffffff		; (可能是)卷标号码
    25 00007C2B 48 45 4C 4C 4F 2D 4F 53 20 20   		DB		"HELLO-OS   "	; 磁盘的名称(必须为11字?,不足填空格)
       00007C35 20 
    26 00007C36 46 41 54 31 32 20 20 20         		DB		"FAT12   "		; 磁盘格式名称(必??8字?,不足填空格)
    27 00007C3E 00 00 00 00 00 00 00 00 00 00   		RESB	18				; 先空出18字节
       00007C48 00 00 00 00 00 00 00 00 
    28 00007C50                                 
    29 00007C50                                 ; 程序主体
    30 00007C50                                 
    31 00007C50                                 entry:
    32 00007C50 B8 0000                         		MOV		AX,0			; 初始化寄存器
    33 00007C53 8E D0                           		MOV		SS,AX
    34 00007C55 BC 7C00                         		MOV		SP,0x7c00
    35 00007C58 8E D8                           		MOV		DS,AX
    36 00007C5A 8E C0                           		MOV		ES,AX
    37 00007C5C                                 
    38 00007C5C BE 7C74                         		MOV		SI,msg
    39 00007C5F                                 putloop:
    40 00007C5F 8A 04                           		MOV		AL,[SI]
    41 00007C61 83 C6 01                        		ADD		SI,1			; 给SI加1
    42 00007C64 3C 00                           		CMP		AL,0
    43 00007C66 74 09                           		JE		fin
    44 00007C68 B4 0E                           		MOV		AH,0x0e			; 显示一个文字
    45 00007C6A BB 000F                         		MOV		BX,15			; 指定字符颜色
    46 00007C6D CD 10                           		INT		0x10			; 调用显卡BIOS
    47 00007C6F EB EE                           		JMP		putloop
    48 00007C71                                 fin:
    49 00007C71 F4                              		HLT						; 让CPU停止,等待指令
    50 00007C72 EB FD                           		JMP		fin				; 无限循环
    51 00007C74                                 
    52 00007C74                                 msg:
    53 00007C74 0A 0A                           		DB		0x0a, 0x0a		; 换行两次
    54 00007C76 68 65 6C 6C 6F 2C 20 77 6F 72   		DB		"hello, world"
       00007C80 6C 64 
    55 00007C82 0A                              		DB		0x0a			; 换行
    56 00007C83 00                              		DB		0
    57 00007C84                                 
    58 00007C84 00 00 00 00 00 00 00 00 00 00   		RESB	0x7dfe-$		; 填写0x00直到0x001fe
       00007C8E 00 00 00 00 00 00 00 00 00 00 
       00007C98 00 00 00 00 00 00 00 00 00 00 
       00007CA2 00 00 00 00 00 00 00 00 00 00 
       00007CAC 00 00 00 00 00 00 00 00 00 00 
       00007CB6 00 00 00 00 00 00 00 00 00 00 
       00007CC0 00 00 00 00 00 00 00 00 00 00 
       00007CCA 00 00 00 00 00 00 00 00 00 00 
       00007CD4 00 00 00 00 00 00 00 00 00 00 
       00007CDE 00 00 00 00 00 00 00 00 00 00 
       00007CE8 00 00 00 00 00 00 00 00 00 00 
       00007CF2 00 00 00 00 00 00 00 00 00 00 
       00007CFC 00 00 00 00 00 00 00 00 00 00 
       00007D06 00 00 00 00 00 00 00 00 00 00 
       00007D10 00 00 00 00 00 00 00 00 00 00 
       00007D1A 00 00 00 00 00 00 00 00 00 00 
       00007D24 00 00 00 00 00 00 00 00 00 00 
       00007D2E 00 00 00 00 00 00 00 00 00 00 
       00007D38 00 00 00 00 00 00 00 00 00 00 
       00007D42 00 00 00 00 00 00 00 00 00 00 
       00007D4C 00 00 00 00 00 00 00 00 00 00 
       00007D56 00 00 00 00 00 00 00 00 00 00 
       00007D60 00 00 00 00 00 00 00 00 00 00 
       00007D6A 00 00 00 00 00 00 00 00 00 00 
       00007D74 00 00 00 00 00 00 00 00 00 00 
       00007D7E 00 00 00 00 00 00 00 00 00 00 
       00007D88 00 00 00 00 00 00 00 00 00 00 
       00007D92 00 00 00 00 00 00 00 00 00 00 
       00007D9C 00 00 00 00 00 00 00 00 00 00 
       00007DA6 00 00 00 00 00 00 00 00 00 00 
       00007DB0 00 00 00 00 00 00 00 00 00 00 
       00007DBA 00 00 00 00 00 00 00 00 00 00 
       00007DC4 00 00 00 00 00 00 00 00 00 00 
       00007DCE 00 00 00 00 00 00 00 00 00 00 
       00007DD8 00 00 00 00 00 00 00 00 00 00 
       00007DE2 00 00 00 00 00 00 00 00 00 00 
       00007DEC 00 00 00 00 00 00 00 00 00 00 
       00007DF6 00 00 00 00 00 00 00 00 
    59 00007DFE                                 
    60 00007DFE 55 AA                           		DB		0x55, 0xaa

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值