寄存器知识

一个典型的CPU由运算器、控制器、寄存器(CPU工作原理)等器件构成,这些器件靠内部总线相连。

  • 运算器进行信息处理(运算单元)

  • 寄存器进行信息存储(存储单元)

  • 控制器负责控制各种器件工作(控制单元)

8086CPU中一共有14 个寄存器,所有寄存器都是16位宽,存2个字节,因为是完全的16位微处理器。

AX,BX,CX,DX,SP,BP,SI,DI,IP,FLAG,CS,DS,SS,ES

image-20210910152047631

这 14 个寄存器有可能进行具体的划分,按照功能可以分为五种:

  • 通用寄存器
  • 段寄存器
  • 偏移寄存器
  • IP寄存器
  • 标志寄存器

通用寄存器

通用寄存器有4个:AX,BX,CX,DX,一般用来存放数据,也被称为数据寄存器。

它们可分为两个可独立使用的8位寄存器

如下图所示。

img

8086CPU可以一次性处理以下两种尺寸的数据。

  • 字节:记为byte,一个字节由8个bit组成,可以存在8位寄存器中。
  • 字:记为word,一个字由两个字节组成,可以存在一个16位寄存器中(16位CPU)

image-20210910153932483

8086采用小端模式:高地址存放高位字节,低地址存放低位字节。

一个8位寄存器所能存储的数据范围是:0~255 (2的8次方-1)

image-20210910154911376

image-20210910155427382

image-20210910155506131

AX寄存器(累加寄存器)

AX也叫做累加寄存器,主要用于输入/输出大规模的指令运算。

以下例子是我在Windows下编译的32位汇编,其中AX扩展成了EAX,我们可以看见乘法(累加)后的结果他其实就是保存到了EAX中,然后再将EAX赋值给整型变量c,所以乘法(imul)除法(div)运算都会用到AX寄存器,除法会将商保存到ax中,余数保存到dx中。

编译命令:cl -FAS .\1.cpp

image-20210910201432823

可以利用文章https://www.cnblogs.com/VxerLee/p/15264290.html中的方法将C语言转换成DOS汇编代码,然后对比源码和汇编代码发现乘法结果确实会放入到ax中。

乘法:

image-20210913191410502

除法(取余数):

image-20210914111835202

ax还常常被用作函数的返回值。

image-20210914114159719

BX寄存器(寻址寄存器)

bx叫做数据寄存器,用来暂存一般数据,不过用的最多的还是用来寻址,bx存放偏移地址,然后根据基地址+偏移进行物理内存地址的定位。

暂存一般数据:

1
2
3
mov bx,2
add bx,bx
mov ax,bx

用bx寻址:

image-20210914135730744

CX寄存器(计数寄存器)

cx也是叫做数据寄存器,也能暂存一般数据,不过cx还有个专门的用途,根据字面意思c - > “Count”,用来在Loop(循环)时候,用cx寄存器来进行计数指定循环的次数。

1
2
3
4
5
6
7
mov  cx,10  ;循环10次
xor ax,ax
xor bx,bx ;存放sum
s:
int ax ;
add bx,ax;/bx = 1+2+3+4+5+6+7+8+9+10
loop s

除此之外在Win32汇编中,ecx经常会作为类指针进行传入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <iostream>
using namespace std;

class Cat{
public:
int age;
char *name;

public:
void run();
};

void Cat::run()
{
cout << "I'm a Cat." << endl;
cout << "My Name is " << this->name << endl;
printf("age=%d\n",this->age);
printf("I'm Running.....\n");
}
int main()
{
Cat maomi;
maomi.age = 2;
maomi.name = "xiaohui";
maomi.run();
return 0;
}

进行反汇编后可以发现代码调用maomi.run()的时候会把this指针传入ecx寄存器。lea ecx,dword ptr ss:[x]

image-20210914145439088

image-20210914150007810

DX寄存器

dx寄存器和之前的寄存器一样,都是能暂存一般数据,在之前的汇编代码中可以发现编译器会将(通用寄存器)和si、di都拿来暂存一般的数据,除此之外之前还介绍过乘法和除法的值也会写入dx中。

dx用作乘法的时候,用来存储ax寄存器不够存储的高位数据

image-20210914152839479

dx用作除法的时候前面介绍过了,用来存储余数。

段寄存器

CPU中包含了四个段寄存器,用作程序指令,数据或栈的基础位置。ps:(不过在Windows中好像这些段寄存器没什么用,因为Windows用了平坦模式,每个段的地址都一样都是0,直接用偏移来定位)

段寄存器主要的功能如下:

  • CS(Code Segment) : 代码寄存器,程序代码的基础位置
  • DS(Data Segment): 数据寄存器,变量的基本位置
  • SS(Stack Segment): 栈寄存器,栈的基础位置
  • ES(Extra Segment): 其他寄存器,内存中变量的其他基本位置。
1
2
3
4
mov cs:[xxx],ax
mov ds:[xxx],ax
mov ss:[xxx],ax
mov es:[xxx],ax

偏移(索引)寄存器

偏移寄存器或者叫索引寄存器,主要是包含段地址的偏移量,用来进行内存地址的定位。

  • BP(Base Pointer):基础指针,它是栈寄存器上的偏移量,用来定位栈上变量
  • SP(Stack Pointer): 栈指针,它是栈寄存器上的偏移量,用来定位栈顶
  • SI(Source Index): 变址寄存器,用来拷贝源字符串
  • DI(Destination Index): 目标变址寄存器,用来复制到目标字符串

BP基础指针寄存器

[bp-xx]表示变量

image-20210914171745745

[bp+xx]表示函数参数

image-20210914182611731

堆栈数据中bp+6开始就是test函数的第一个参数直到bp+16为止。

image-20210914183856027

SP栈指针寄存器

这个其实主要是栈顶指针,无论何时sp指针都执行栈的顶部,[栈是一个从高地址向下生长的内存]。

push数据时候 sp指针情况(sp地址会减少)

image-20210914184634142

image-20210914184958415

pop数据时候 sp指针情况(sp地址会增加)

image-20210914185226125

SI变址寄存器

si寄存器是变址寄存器,可以用来存放寻址用的偏移,此外si还被用作隐含的源串地址,默认在DS段中。
[无论是si还是di这两个寄存器总感觉和字符串都有点关系,c++的string?]

1
2
3
4
5
6
7
char source[]="hello,world"; (假装=esi寄存器)
char dest[]={0}; (假装=edi寄存器)
;----------------------------------------------
mov ecx,strlen(source) ;字符串长度
rep movsb ;重复循环 并且传输字符串
;----------------------------------------------
[dest] = "hello,world"

image-20210915135422905

DI目标变址寄存器

di寄存器是目标变址寄存器,和si一样可以用来存放寻址用的偏移,此外di还被用作隐含的目的串地址,默认在ES段中。

先来看看STOS指令的介绍:

1
2
3
4
5
字符串存储指令 STOS 

格式: STOS OPRD

功能: 把AL(字节)或AX(字)中的数据存储到DI为目的串地址指针所寻址的存储器单元中去.指针DI将根据DF的值进行自动调整.

接下来我们来看看,在汇编语言中是如何实现高级语言中清空数组的操作,以下是一个初始化字符数组的高级代码。

1
char szMsg[500]={0};

翻译后的汇编代码。

image-20210915111540944

上面的汇编代码执行完后,可以发现szMsg数组的内容全部被清空了。

image-20210915111759044

image-20210915112024226

IP寄存器

IP(Instruction Pointer): 指令指针寄存器,它是从 Code Segment 代码寄存器处的偏移来存储执行的下一条指令。

标志寄存器

就剩下两种寄存器还没聊了,这两种寄存器是指令指针寄存器和标志寄存器:

  • FLAG : Flag 寄存器用于存储当前进程的状态,这些状态有
    • 位置 (Direction):用于数据块的传输方向,是向上传输还是向下传输
    • 中断标志位 (Interrupt) :1 - 允许;0 - 禁止
    • 陷入位 (Trap) :确定每条指令执行完成后,CPU 是否应该停止。1 - 开启,0 - 关闭
    • 进位 (Carry) : 设置最后一个无符号算术运算是否带有进位
    • 溢出 (Overflow) : 设置最后一个有符号运算是否溢出
    • 符号 (Sign) : 如果最后一次算术运算为负,则设置 1 =负,0 =正
    • 零位 (Zero) : 如果最后一次算术运算结果为零,1 = 零
    • 辅助进位 (Aux Carry) :用于第三位到第四位的进位
    • 奇偶校验 (Parity) : 用于奇偶校验

参考文献:
https://blog.csdn.net/qq_39654127/article/details/88698911 《王爽汇编笔记》
https://segmentfault.com/a/1190000037478310《十一假期淦了八天寄存器的相关知识》
https://docs.microsoft.com/zh-cn/cpp/build/reference/fa-fa-listing-file?view=msvc-160《/FA/Fa使用》
https://blog.csdn.net/bagboy_taobao_com/article/details/6203705 《了解寄存器:ESI EDI变址寄存器》
http://c.biancheng.net/view/3679.html《汇编语言字符串基本指令简介》
https://blog.csdn.net/weixin_43216249/article/details/110728729《汇编语言学习笔记--串操作篇(c++的string???)》