操作系统开发日记-day2

汇编入门

DB指令:"data byte"的缩写,意思是往文件里直接写入一字节的指令

RESB指令:"reserve byte"的缩写,如果写成RESB 10意思是我们预约了10个字节。

在这里插入图片描述
在这里插入图片描述
DD、DW、DB的区别
在这里插入图片描述
;符号的作用是注释,相当于c++的//符号,DB指令的功能也变得更广,可以直接编写字符串。$符号是一个变量,可以告诉我们这一行现在的字符数。

在启动程序中,我们必须要保证第510字节(即第0x1fe)开始的地方是0x55 0xaa,使用$符号的话就可以直接计算出需要多少个字节来放0x00。

上述源代码中的一些专业的表示的含义如下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
ORG指令:这个指令告诉编译器,在开始执行的时候,把这些机器语言指令装载到内存中的哪个地址。有了这个指令的话,美元符$的含义也会有一些变化,它将代表将要读入的内存地址。

ORG指令来源于英文“origin”,意思是“源头、起点”。它会告诉编译器,程序要从指定的这个地址开始,也就是把程序装载到内存中的指定地址。这里指定地址是0x7c00, 对于很多人来讲0x7C00这个地址是很神秘的,不知道这是干什么的。但是对于了解过x86平台下BIOS启动过程的人,对这个地址再熟悉不过了。 BIOS就是将MBR读入0x7C00地址 ,然后进行后续的引导的。操作系统或是bootloader开发者必须假设 他们的汇编代码被加载并从0x7C00处开始执行。

entry指令:这个指令用于指定JMP指令的跳转目的地等。

MOV指令:这个指令的功能很简单,就是一个赋值的功能,虽然简单,但是MOV指令在不同的场景下有不同的意义,掌握它对于掌握汇编和操作系统开发至关重要。

在CPU中有一种名为寄存器的电路,相当于机器语言中的变量。具有代表性的寄存器有以下8个,简单在这里说明一下:

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

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

在这里插入图片描述
这8个寄存器全部合起来只有16个字节。换句话说,就算我们把这8个寄存器都用上,CPU也只能存储区区16个字节。

另一方面,CPU中还有8个8位寄存器。

  • AL:累加寄存器低位
  • CL:计数寄存器低位
  • DL:数据寄存器低位
  • BL:基址寄存器低位
  • AH:累加寄存器高位
  • CH:计数寄存器高位
  • DH:数据寄存器高位
  • BH:基址寄存器高位
    在这里插入图片描述
    需要注意的是BP、SP、DI这几个寄存器是没有高位和低位之分的,如果非要取这些寄存器的低位和高位的话,就必须先MOV,AX,SI,将SI的值赋值到AX中,然后再用AL、AH来取值。

在16位之上的就是32位的CPU和寄存器,对应的寄存器的名字是EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI

但是,对于32位寄存器,我们想要使用它的高16位可不像16位寄存器一样能够轻易的取它的低8位和高8位那样方便,我们必须将高32位的数据进行移位,将高16位的数据移到低16位上才可以取到对应的高16位数据。

关于寄存器,还有几个寄存器就是段寄存器,这些段寄存器都是16位的寄存器。

  • ES:附加段寄存器
  • CS:代码段寄存器
  • SS:栈段寄存器
  • DS:数据段寄存器
  • FS:没有名称
  • GS:没有名称

MOV SI,msg 这段代码的含义,就是将msg标签所对应的代码段的起始地址放入SI源变址寄存器中。

这里简单的说明一下CPU和内存的关系,如果说CPU是一个带有实权的掌权者,那么内存就是它的一个外部存储室,也就是说,CPU要通过自己的一部分管脚(引线)想内存发送电信号,告诉内存说,把对应的一个地址上的数据通过我的管脚(引线)传送过来。CPU向内存读写数据时,就是这样进行信息交换的。

内存离CPU相当远,就算是只有10cm左右的距离,可是这和CPU内部的寄存器与CPU自身的距离相比还是相当相当远的,因此,CPU访问内存的速度是要远远小于访问寄存器的速度的。

接下来我们来分析上面代码中出现的一个指令MOV AL,[SI],在我们之前的说明中,MOV指令的数据传送源和传送目的地不仅可以是寄存器或常数,也可以是内存地址。这个时候就可以用方括号[]来表示内存地址,上面的汇编代码的含义就是把SI寄存器中保存的一个内存地址里面保存的数据放到AL寄存器中。

虽然我们可以用寄存器来指定内存地址,但可作为此用途的寄存器非常有限,只有BX、BP、SI、DI这几个。剩下的AX、CX、DX、SP不能用来指定内存地址,这是因为CPU没有处理这种指令的电路,或者说没有表示这种处理的机器语言。没有对应的机器语言当然就不能进行这样的处理了。

MOV指令有一个规则,就是源数据和目的数据必须位数相同。也就是说,能向AL里代入的就只有BYTE,这样一来,就可以省略BYTE。

INT是软件中断指令。电脑中有个名为BIOS的程序,出厂时就组装在电脑主板的ROM单元里。电脑厂家在BIOS中预先写入了操作系统开发人员经常会用到的一些程序,非常方便。BIOS是英文basic input output system的缩写,直译过来就是基本输入输出程序。

最近的BIOS功能非常多,甚至包括了电脑的设定画面,不过它的本质正如其名,就是为操作系统开发人员准备的各种函数的集合,而INT指令就是用来调用这些函数的指令,INT的后面是个数字,相当于这些函数的一个编号,通过不同的编号可以调用不同的BIOS函数。上面的代码调用的是0x10号函数,它的功能是控制显卡。

那么就是按照BIOS的要求去实现寄存器中的内容就可以实现对应的功能,下面是显示一个字符对应的寄存器的要求
在这里插入图片描述
这里有一个HLT指令,这个指令的作用是让CPU处于一个休眠的状态,直到外部出现一个变化,比如移动鼠标,或者按下键盘,CPU才会苏醒过来,防止CPU做无谓的运转。

这样,对于上面的那个程序,我们就可以把msg里写的数据一个字符一个字符地显示出来,并且数据变成0之后,HLT指令就会让程序进入无限循环。一个hello world就这样显示出来了。

关于为什么ORG的地址要指向0x7c00的原因:
在这里插入图片描述
简单来说就是厂商在写BIOS的时候,需要占用内存的一部分地址,因此这些地址我们操作系统开发人员是不能够去用的,不然会发生很多冲突。同理,内存中还有一些地方是我们不能够使用的,因此就会有我们的地址必须从某个地方开始的规定。

程序源代码

; boot.s引导程序
; hello-os
; TAB=4
		ORG 0x7c00
		
;以下是FAT12的描述
		jmp entry
		db x90
		DB“HELLOIPL”			;可以自由地写引导扇区的名称(8字节)
		DW 512					;一个扇区的大小(必须是512)
		DB 1					;集群的大小(必须是一个扇区)
		DW 1					;FAT从哪里开始(通常是从第一扇区开始)
		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 0xffffffffff			;可能是音量序列号
		DB“HELLO-OS”			;磁盘名称(11字节)
		DB“FAT12”				;格式名称(8字节)
		RESB 18					;姑且留出18个字节

entry:
		MOV		AX,0			;寄存器初始化
		MOV		SS,AX			;SS:栈段寄存器
		mov     sp,0x7c00 		;栈指针赋为0x7c00,既引导程序初始地址,SP:栈指针寄存器
		MOV		DS,AX			;DS:数据段寄存器
		MOV		ES,AX			;ES:附加段寄存器
		
		MOV		SI,msg			;SI:source index,源变址寄存器

putloop:	
		MOV		AL,[SI]			;将第一个字符->AL
		ADD		SI,1			;SI+1
		cmp  	AL,0      		;比较0寻找最后一个字符,msg之后的byte是0
		JE		fin				;如果=0,则跳转到fin的地址位置
		mov  	AH,0x0e   		;显示字符(字符显示功能);指定文字
		MOV		BX,15			;指定颜色(黑色)
		INT		0x10			;调用video bios中断
		JMP  	putloop			;循环跳回
		
fin:
		HLT						;cpu休眠
		JMP		fin				;无限循环
		
msg:
		DB		0x0a, 0x0a		; 换行*2
		DB		"hello, world"
		DB		0x0a			; 换行
		DB		0

		RESB	0x7dfe-$		;0x00填充到0x7dfe的命令

		DB		0x55, 0xaa

猜你喜欢

转载自blog.csdn.net/weixin_45394002/article/details/113811442