260×260

科学搜查官yuchanns

理想的生活是纯粹地热爱技术
  • Shenzhen, China
  • 后端开发工程师
Posted 2 months ago

指令和指令集

说明

x86系列文章为书籍《x86汇编语言:从实模式到保护模式》的学习笔记,内容属于笔者学习总结性质。

笔者将之发表到博客上,一方面是作为笔记存档,另一方面希望对同在阅读本书的小伙伴起到理解帮助作用。

前置概念

  • 寄存器:处理器在操作过程中将数据临时存储的电路。4位、8位、16位、32位和64位寄存器,表示可以容纳的数据容量比特数(bit)。
  • 单位换算:16bit = 2byte = 1word
  • 高低位:从右到左为字节上的每一位编号。0是最低位;最左边是最高位。
  • 字:特指16个二进制位的长度,编号是0~15。0~7是低字节,8~15是高字节。
  • 双字:32位寄存器可以存放4个字节(byte),编号0~31。0~15是低字节,16~31是高字节。

内存储器

内存按字节来组织,单词访问的最小单位为1字节。

内存地址采用十六进制表示法,第一个字节地址为0000H,第二个字节地址为0001H……以此类推

假设一个内存的容量是65536字节,它的地址范围是多少?

分析:设当前字节在内存中为第n+1个,当n=0,也就是第一个字节时,地址为0000H;当n=1,也就是第二个字节时,地址为0001H,正对应10到16进制的转换。因此第65536个字节,也就是n=65535时,对应的地址就是FFFFH。所以它的地址范围是0000H~FFFFH。

内存可以按字节、字、双字和四字进行访问,称作访问的字长

指令和指令集

addr +---------------+ FFFF | | | | | | ≈ …… ≈ | | | | ----------------- 0040 | 10 | ----------------- 003F | 02 | ----------------- | | | | ≈ …… ≈ | | ----------------- 000C | F4 | <----------- 停机 ----------------- 000B | 00 | ----------------- 000A | 3F | ----------------- 0009 | A3 | ----------------- 0008 | D8 | ----------------- 0007 | 01 | ----------------- 0006 | 00 | <-----------+ ----------------- | 表示数据在内存地址003FH,非立即数 ------+ 0005 | 3F | <-----------+ | ----------------- | 从003FH地址取出一个字(1002H),放到RB寄存器中 0004 | 1E | <----------- 读取内存地址 | ----------------- | 0003 | 8B | <----------- 操作码8B,是传送指令,指出传送到寄存器RB --+ ----------------- 0002 | 00 | <-----------+ ----------------- | 立即数,非指令,表示数据为005DH ------+ 0001 | 5D | <-----------+ | ----------------- | 将005DH传入RA寄存器中 0000 | B8 | <----------- 操作码B8,是传送指令,指出传送到寄存器RA -----+ +---------------+
  • 指令:一般由操作码和操作数构成,指令长度不定。
  • 立即数:紧跟在操作码后面,可以立即从指令取得的操作数。
  • 大小端:以字的方式访问内存地址,一次可以读取到两个字节。根据处理器的规定,分为低端字节序和高端字节序。低端字节序规定高字节位于高地址,低字节位于低地址,而后者相反。
  • 复杂指令:给出字存储的内存地址,访问取出值后送入寄存器。(相当于高级语言里的指针)
  • 指令和数据分开存放:指令中混杂非指令数据会导致处理器不能正常工作。存放指令的区域叫代码区,存放数据的区域叫数据区。

8086处理器

8086处理器内部有8个16位通用寄存器,分别为AXBXCXDXSIDIBPSP

“通用”的含义

可以互相传送数据,进行算术逻辑运算;也可以和内存单元进行16位数据传送或算术逻辑运算。

前四个寄存器各自可以拆分成两个8位寄存器来使用,总共可以提供8个8位寄存器AHALBHBLCHCLDHDL

注:H是高位(High),L是低位(Low)。一个数据存在16位寄存器中,如果是小端(Little Endian)读取方式,那么二进制数据(从右数)编号0到7的前8位存在L寄存器,编号8到16后8位存在H寄存器。本文结尾会附上书籍检测点5.3第一题的解题思路。

分段机制

处理器在内存中按顺序取指令,只要每条指令正确无误,就能准确知道下一条指令的地址。所以完成某个工作的指令必须集中存放在某个位置,形成代码段。某个工作中程序操作的大量数据也应该集中存放起来,形成数据段

数据段和代码段的划分是逻辑上的。

程序运行时在内存中被加载的位置完全是随机的,所以指令的地址不能取绝对内存地址,否则无法重定位

内存分段后,每段内的存储单元的地址可以相对于所在的段开始处的距离,例如距离0、1、2、3、4、5,叫做偏移地址

  • 逻辑地址:使用段地址:偏移地址来表示内存单元的地址。
  • 代码段寄存器(Code Segment,CS):指向代码段起始地址。
  • 数据段寄存器(Data Segment,DS):指向数据段起始地址。
  • 数据暂存器:进行数据传送或算术逻辑运算时,结果返回到寄存器之前,使用的中转寄存器。
  • 指令预取队列:预先访问内存取出指令流进行排队等待解码和执行。
  • 指令指针寄存器(Instruction Pointer,IP):只和CS一起使用,指向段内偏移,和CS共同形成逻辑地址

8086的四个段寄存器

分别为CS、DS、ES(Extra Segment)和SS(Stack Segment)。ES也是数据段寄存器,用于指向另一个数据段。

扩大内存寻址容量

16位的段地址和16位的偏移地址相加,只能形成16位的物理地址,而8086提供了20位的物理地址访问能力:

将段寄存器的内容左移4位,形成20位的段地址,然后同16位的偏移地址相加,得到20位的物理地址。

分析:比如逻辑地址F000H:052DH,其段地址为F000H,转化为二进制位就是1111 0000 0000 0000,将其左移四位,形成1111 0000 0000 0000 0000,形成十六进制的F0000H。这相当于乘十六进制的10,或者十进制的16。加上偏移地址052DH形成了20位的物理地址F052DH。

字节对齐

8086处理器的段寄存器起始地址必须是16的倍数,才能表示成一个偏移地址为0000H的逻辑地址,称为按16字节对齐

段的最大长度

由于偏移地址是16位的,所以表示范围从0000H到FFFFH,也就是说最多表示65536个字节,即最大长度为64KB。

  • 1KB = 1024Byte
  • 1MB = 1024KB
  • 1GB = 1024MB

只要满足“物理地址位于其段的64KB范围内”的任意段都可以访问到该物理地址,也就是说一个物理地址对应着多个逻辑地址。

检测点5.3第一题

这一道题理应归属到下一篇文章《编写主引导扇区》中,但涉及到本文的大小端,故而放在此处。可在学习第五章后进行回顾。

INTEL x86 处理器访问内存时,按低端字节序进行。那么,以下程序片段执行后,寄存器AX中的内容是多少?

mov word [data], 0x2008 xor byte [data], 0x05 add word [data], 0x0101 mov ax, [data] data db 0, 0

0x2008,放在内存[data]地址中,从右到左数起,08H是低字节,20H是高字节;x86从低字节访问,所以先访问08H。进行8位(byte)xor运算时,是在08H05H之间运行,即0000 1000 ⊕ 0000 0101,结果为0000 11010DH,所以xor byte [data], 0x05的结果是内存[data]地址的内容变成0x200D;然后进行字相加,即200DH + 0101H,结果是210EH。所以AX寄存器中的内容是0x210E,其中AH的内容是21H,AL的内容是0EH

这一结果可以通过这段显存打印的代码得到验证,输出结果恰好是🎵!