纯净、安全、绿色的下载网站

首页|软件分类|下载排行|最新软件|IT学院

当前位置:首页IT学院IT技术

图文详解汇编语言寄存器 图文详解通俗易懂的汇编语言寄存器

程序员cxuan   2021-11-23 我要评论
想了解图文详解通俗易懂的汇编语言寄存器的相关内容吗程序员cxuan在本文为您仔细讲解图文详解汇编语言寄存器的相关知识和一些Code实例欢迎阅读和指正我们先划重点:汇编语言寄存器,寄存器图文详解下面大家一起来学习吧

我把自己以往的文章汇总成为了 Github 欢迎各位大佬 star
https://github.com/crisxuan/bestJavaer

下面我们就来介绍一下关于寄存器的相关内容我们知道寄存器是 CPU 内部的构造它主要用于信息的存储除此之外CPU 内部还有运算器负责处理数据控制器控制其他组件外部总线连接 CPU 和各种部件进行数据传输内部总线负责 CPU 内部各种组件的数据处理

那么对于我们所了解的汇编语言来说我们的主要关注点就是 寄存器

为什么会出现寄存器?因为我们知道程序在内存中装载由 CPU 来运行CPU 的主要职责就是用来处理数据那么这个过程势必涉及到从存储器中读取和写入数据因为它涉及通过控制总线发送数据请求并进入存储器存储单元通过同一通道获取数据这个过程非常的繁琐并且会涉及到大量的内存占用而且有一些常用的内存页存在其实是没有必要的因此出现了寄存器存储在 CPU 内部

认识寄存器

寄存器的官方叫法有很多Wiki 上面的叫法是 Processing Register 也可以称为 CPU Register计算机中经常有一个东西多种叫法的情况反正你知道都说的是寄存器就可以了

认识寄存器之前我们首先先来看一下 CPU 内部的构造

assembly02 001

CPU 从逻辑上可以分为 3 个模块分别是控制单元、运算单元和存储单元这三部分由 CPU 内部总线连接起来

几乎所有的冯·诺伊曼型计算机的 CPU其工作都可以分为5个阶段:取指令、指令译码、执行指令、访存取数、结果写回

  • 取指令阶段是将内存中的指令读取到 CPU 中寄存器的过程程序寄存器用于存储下一条指令所在的地址
  • 指令译码阶段在取指令完成后立马进入指令译码阶段在指令译码阶段指令译码器按照预定的指令格式对取回的指令进行拆分和解释识别区分出不同的指令类别以及各种获取操作数的方法
  • 执行指令阶段译码完成后就需要执行这一条指令了此阶段的任务是完成指令所规定的各种操作具体实现指令的功能
  • 访问取数阶段根据指令的需要有可能需要从内存中提取数据此阶段的任务是:根据指令地址码得到操作数在主存中的地址并从主存中读取该操作数用于运算
  • 结果写回阶段作为最后一个阶段结果写回(Write BackWB)阶段把执行指令阶段的运行结果数据写回到 CPU 的内部寄存器中以便被后续的指令快速地存取

计算机架构中的寄存器

寄存器是一块速度非常快的计算机内存下面是现代计算机中具有存储功能的部件比对可以看到寄存器的速度是最快的同时也是造价最高昂的

assembly02 002

我们以 intel 8086 处理器为例来进行探讨8086 处理器是 x86 架构的前身在 8086 后面又衍生出来了 8088 

在 8086 CPU 中地址总线达到 20 根因此最大寻址能力是 2^20 次幂也就是 1MB 的寻址能力8088 也是如此

在 8086 架构中所有的内部寄存器、内部以及外部总线都是 16 位宽可以存储两个字节因为是完全的 16 位微处理器8086 处理器有 14 个寄存器每个寄存器都有一个特有的名称即

AXBXCXDXSPBPSIDIIPFLAGCSDSSSES

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

  • 通用寄存器
  • 控制寄存器
  • 段寄存器

下面我们分别介绍一下这几种寄存器

通用寄存器

通用寄存器主要有四种 即 AX、BX、CX、DX 同样的这四个寄存器也是 16 位的能存放两个字节 AX、BX、CX、DX 这四个寄存器一般用来存放数据也被称为 数据寄存器它们的结构如下

assembly02 003

8086 CPU 的上一代寄存器是 8080 它是一类 8 位的 CPU为了保证兼容性8086 在 8080 上做了很小的修改8086 中的通用寄存器 AX、BX、CX、DX 都可以独立使用两个 8 位寄存器来使用

在细节方面AX、BX、CX、DX 可以再向下进行划分

AX(Accumulator Register) : 累加寄存器它主要用于输入/输出和大规模的指令运算

BX(Base Register):基址寄存器用来存储基础访问地址

CX(Count Register):计数寄存器CX 寄存器在迭代的操作中会循环计数

DX(data Register):数据寄存器它也用于输入/输出操作它还与 AX 寄存器以及 DX 一起使用用于涉及大数值的乘法和除法运算

这四种寄存器可以分为上半部分和下半部分用作八个 8 位数据寄存器

  • AX 寄存器可以分为两个独立的 8 位的 AH 和 AL 寄存器
  • BX 寄存器可以分为两个独立的 8 位的 BH 和 BL 寄存器
  • CX 寄存器可以分为两个独立的 8 位的 CH 和 CL 寄存器
  • DX 寄存器可以分为两个独立的 8 位的 DH 和 DL 寄存器

除了上面 AX、BX、CX、DX 寄存器以外其他寄存器均不可以分为两个独立的 8 位寄存器

如下图所示

assembly02 004

合起来就是

assembly02 005

AX 的低位(0 - 7)位构成了 AL 寄存器高 8 位(8 - 15)位构成了 AH 寄存器AH 和 AL 寄存器是可以使用的 8 位寄存器其他同理

在认识了寄存器之后我们通过一个示例来看一下数据的具体存储方式

比如数据 19 它在 16 位存储器中所存储的表示如下

assembly02 006

寄存器的存储方式是先存储低位如果低位满足不了就存储高位如果低位能够满足高位用 0 补全在其他低位能满足的情况下其余位也用 0 补全

8086 CPU 可以一次存储两种类型的数据

字节(byte): 一个字节由 8 bit 组成这是一种恒定不变的存储方式

字(word):字是由指令集或处理器硬件作为单元处理的固定大小的数据对于 intel 来说一个字长就是两个字节字是计算机一个非常重要的特征针对不同的指令集架构来说计算机一次处理的数据也是不同的也就是说针对不同指令集的机器一次能处理不用的字长有字、双字(32位)、四字(64位)等

AX 寄存器

我们上面探讨过AX 的另外一个名字叫做累加寄存器或者简称为累加器其可以分为 2 个独立的 8 位寄存器 AH 和 AL在编写汇编程序中AX 寄存器可以说是使用频率最高的寄存器

下面是几段汇编代码

mov ax,20		/* 将 20 送入寄存器 AX*/
mov ah,80   /* 将 80 送入寄存器 AH*/
add ax,10	  /* 将寄存器 AX 中的数值加上 8 */

这里注意下:上面代码中出现的是 ax、ah 而注释中确是 AX、AH 其实含义是一样的不区分大小写

AX 相比于其他通用寄存器来说有一点比较特殊AX 具有一种特殊功能的使用那就是使用 DIV 和 MUL 指令式使用

DIV 是 8086 CPU 中的除法指令

MUL 是 8086 CPU 中的乘法指令

BX 寄存器

BX 被称为数据寄存器即表明其能够暂存一般数据同样为了适应以前的 8 位 CPU 而可以将 BX 当做两个独立的 8 位寄存器使用即有 BH 和 BLBX 除了具有暂存数据的功能外还用于 寻址即寻找物理内存地址BX 寄存器中存放的数据一般是用来作为偏移地址 使用的因为偏移地址当然是在基址地址上的偏移了偏移地址是在段寄存器中存储的关于段寄存器的介绍我们后面再说

CX 寄存器

CX 也是数据寄存器能够暂存一般性数据同样为了适应以前的 8 位 CPU 而可以将 CX 当做两个独立的 8 位寄存器使用即有 CH 和 CL除此之外CX 也是有其专门的用途的CX 中的 C 被翻译为 Counting 也就是计数器的功能当在汇编指令中使用循环 LOOP 指令时可以通过 CX 来指定需要循环的次数每次执行循环 LOOP 时候CPU 会做两件事

一件事是计数器自动减 1

还有一件就是判断 CX 中的值如果 CX 中的值为 0 则会跳出循环而继续执行循环下面的指令

当然如果 CX 中的值不为 0 则会继续执行循环中所指定的指令 

DX 寄存器

DX 也是数据寄存器能够暂存一般性数据同样为了适应以前的 8 位 CPU DX 的用途其实在前面介绍 AX 寄存器时便已经有所介绍了那就是支持 MUL 和 DIV 指令同时也支持数值溢出等

###段寄存器

CPU 包含四个段寄存器用作程序指令数据或栈的基础位置实际上对 IBM PC 上所有内存的引用都包含一个段寄存器作为基本位置

段寄存器主要包含

CS(Code Segment) : 代码寄存器程序代码的基础位置

DS(Data Segment): 数据寄存器变量的基本位置

SS(Stack Segment): 栈寄存器栈的基础位置

ES(Extra Segment): 其他寄存器内存中变量的其他基本位置

索引寄存器

索引寄存器主要包含段地址的偏移量索引寄存器主要分为

BP(Base Pointer):基础指针它是栈寄存器上的偏移量用来定位栈上变量

SP(Stack Pointer): 栈指针它是栈寄存器上的偏移量用来定位栈顶

SI(Source Index): 变址寄存器用来拷贝源字符串

DI(Destination Index): 目标变址寄存器用来复制到目标字符串

状态和控制寄存器

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

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) : 用于奇偶校验

物理地址

我们大家都知道 CPU 访问内存时需要知道访问内存的具体地址内存单元是内存的基本单位每一个内存单元在内存中都有唯一的地址这个地址即是 物理地址而 CPU 和内存之间的交互有三条总线即数据总线、控制总线和地址总线

assembly008

CPU 通过地址总线将物理地址送入存储器那么 CPU 是如何形成的物理地址呢?这将是我们接下来的讨论重点

现在我们先来讨论一下和 8086 CPU 有关的结构问题

cxuan 和你聊了这么久你应该知道 8086 CPU 是 16 位的 CPU 了那么什么是 16 位的 CPU 呢?

你可能大致听过这个回答16 位 CPU 指的是 CPU 一次能处理的数据是 16 位的能回答这个问题代表你的底层还不错但是不够全面其实16 位的 CPU 指的是

  • CPU 内部的运算器一次最多能处理 16 位的数据

运算器其实就是 ALU运算控制单元它是 CPU 内部的三大核心器件之一主要负责数据的运算

  • 寄存器的最大宽度为 16 位

这个寄存器的最大宽度值得就是通用寄存器能处理的二进制数的最大位数

  • 寄存器和运算器之间的通路为 16 位

这个指的是寄存器和运算器之间的总线一次能传输 16 位的数据

好了现在你应该知道为什么叫做 16 位 CPU 了吧

在你知道上面这个问题的答案之后我们下面就来聊一聊如何计算物理地址

8086 CPU 有 20 位地址总线每一条总线都可以传输一位的地址所以 8086 CPU 可以传送 20 位地址也就是说8086 CPU 可以达到 2^20 次幂的寻址能力也就是 1MB8086 CPU 又是 16 位的结构从 8086 CPU 的结构看它只能传输 16 位的地址也就是 2^16 次幂也就是 64 KB那么它如何达到 1MB 的寻址能力呢?

原来8086 CPU 的内部采用两个 16 位地址合成的方式来传输一个 20 位的物理地址如下图所示

assembly02 007

叙述一下上图描述的过程

CPU 中相关组件提供两个地址:段地址和偏移地址这两个地址都是 16 位的他们经由地址加法器变为 20 位的物理地址这个地址即是输入输出控制电路传递给内存的物理地址由此完成物理地址的转换

地址加法器采用 物理地址 = 段地址 * 16 + 偏移地址 的方法用段地址和偏移地址合成物理地址

下面是地址加法器的工作流程

assembly02 008

其实段地址 * 16 就是左移 4 位在上面的叙述中物理地址 = 段地址 * 16 + 偏移地址其实就是基础地址 + 偏移地址 = 物理地址 寻址模式的一种具体实现方案基础地址其实就等于段地址 * 16

你可能不太清楚 的概念下面我们就来探讨一下

什么是段

段这个概念经常出现在操作系统中比如在内存管理中操作系统会把不同的数据分成 来存储比如 代码段、数据段、bss 段、rodata 段 等

但是这些的划分并不是内存干的cxuan 告诉你是谁干的这其实是幕后 Boss CPU 搞的内存当作了声讨的对象

其实内存没有进行分段分段完全是由 CPU 搞的上面聊过的通过基础地址 + 偏移地址 = 物理地址的方式给出内存单元的物理地址使得我们可以分段管理 CPU

如图所示

assembly02 009

这是两个 16 KB 的程序分别被装载进内存的示意图可以看到这两个程序的段地址的大小都是 16380

这里需要注意一点 8086 CPU 段地址的计算方式是段地址 * 16所以16 位的寻址能力是 2^16 次方所以一个段的长度是 64 KB

段寄存器

cxuan 在上面只是简单为你介绍了一下段寄存器的概念介绍的有些浅而且介绍段寄存器不介绍段也有不知庐山真面目的感觉现在为你详细的介绍一下相信看完上面的段的概念之后段寄存器也是手到擒来

我们在合成物理地址的那张图提到了 相关部件 的概念这个相关部件其实就是段寄存器即 CS、DS、SS、ES 8086 的 CPU 在访问内存时由这四个寄存器提供内存单元的段地址

CS 寄存器

要聊 CS 寄存器那么 IP 寄存器是你绕不过去的曾经CS 和 IP 都是 8086 CPU 非常重要的寄存器它们指出了 CPU 当前需要读取指令的地址

CS 的全称是 Code Segment即代码寄存器而 IP 的全称是 Instruction Pointer 即指令指针现在知道这两个为什么一起出现了吧!

在 8086 CPU 中由 CS:IP 指向的内容当作指令执行如下图所示

assembly02 010

说明一下上图

在 CPU 内部由 CS、IP 提供段地址由加法器负责转换为物理地址输入输出控制电路负责输入/输出数据指令缓冲器负责缓冲指令指令执行器负责执行指令在内存中有一段连续存储的区域区域内部存储的是机器码、外面是地址和汇编指令

上面这幅图的段地址和偏移地址分别是 2000 和 0000当这两个地址进入地址加法器后会由地址加法器负责将这两个地址转换为物理地址

assembly02 011

然后地址加法器负责将指令输送到输入输出控制电路中

assembly02 012

输入输出控制电路将 20 位的地址总线送到内存中

assembly02 013

然后取出对应的数据也就是 B8、23、01图中的 B8、BB 都是操作数

assembly02 014

控制输入/输出电路会将 B8 23 01 送入指令缓存器中

assembly02 015

此时这个指令就已经具备执行条件此时 IP 也就是指令指针会自动增加我们上面说到 IP 其实就是从 Code Segment 也就是 CS 处偏移的地址也就是偏移地址它会知道下一个需要读取指令的地址如下图所示

assembly02 016

在这之后指令执行执行取出的 B8 23 01 这条指令

然后下面再把 2000 和 0003 送到地址加法器中再进行后续指令的读取后面的指令读取过程和我们上面探讨的如出一辙这里 cxuan 就不再赘述啦

通过对上面的描述我们能总结一下 8086 CPU 的工作过程

段寄存器提供段地址和偏移地址给地址加法器

由地址加法器计算出物理地址通过输入输出控制电路将物理地址送到内存中

提取物理地址对应的指令经由控制电路取回并送到指令缓存器中

IP 继续指向下一条指令的地址同时指令执行器执行指令缓冲器中的指令

什么是 Code Segment

Code Segment 即代码段它就是我们上面聊到就是 CS 寄存器中存储的基础地址也就是段地址段地址其本质上就是一组内存单元的地址例如上面的 mov ax,0123H 、mov bx, 0003H我们可以将长度为 N 的一组代码存放在一组连续地址、其实地址为 16 的倍数的内存单元中我们可以认为这段内存就是用来存放代码的

DS 寄存器

CPU 在读写一个内存单元的时候需要知道这个内存单元的地址在 8086 CPU 中有一个 DS 寄存器通常用来存放访问数据的段地址如果你想要读取一个 10000H 的数据你可能会需要下面这段代码

mov bx,10000H
mov ds,bx
mov a1,[0]

上面这三条指令就把 10000H 读取到了 a1 中

在上面汇编代码中mov 指令有两种传送方式

  • 一种是把数据直接送入寄存器
  • 一种是将一个寄存器的内容送入另一个寄存器

但是不仅仅如此mov 指令还具有下面这几种表达方式

描述 举例
mov 寄存器数据 比如:mov ax,8
mov 寄存器寄存器 比如:mov ax,bx
mov 寄存器内存单元 比如:mov ax,[0]
mov 内存单元寄存器 比如:mov[0], ax
mov 段寄存器寄存器 比如:mov ds,ax

栈我相信大部分小伙伴已经非常熟悉了栈是一种具有特殊的访问方式的存储空间它的特殊性就在于先进入栈的元素最后才出去也就是我们常说的 先入后出

它就像一个大的收纳箱你可以往里面放相同类型的东西比如书最先放进收纳箱的书在最下面最后放进收纳箱的书在最上面如果你想拿书的话 必须从最上面开始取否则是无法取出最下面的书籍的

栈的数据结构就是这样你把书籍压入收纳箱的操作叫做压入(push)你把书籍从收纳箱取出的操作叫做弹出(pop)它的模型图大概是这样

assembly02 017

入栈相当于是增加操作出栈相当于是删除操作只不过叫法不一样栈和内存不同它不需要指定元素的地址它的大概使用如下

// 压入数据
Push(123);
Push(456);
Push(789);
// 弹出数据
j = Pop();
k = Pop();
l = Pop();

在栈中LIFO 方式表示栈的数组中所保存的最后面的数据(Last In)会被最先读取出来(First Out)

assembly02 018

栈和 SS 寄存器

下面我们就通过一段汇编代码来描述一下栈的压入弹出的过程

8086 CPU 提供入栈和出栈指令最基本的两个是 PUSH(入栈)POP(出栈)比如 push ax 会把 ax 寄存器中的数据压入栈中pop ax 表示从栈顶取出数据送入 ax 寄存器中

这里注意一点:8086 CPU 中的入栈和出栈都是以字为单位进行的

我这里首先有一个初始的栈没有任何指令和数据

assembly02 019

然后我们向栈中 push 数据后栈中数据如下

assembly02 020

涉及的指令有

mov ax,2345H
push ax

注意数据会用两个单元存放高地址单元存放高 8 位地址低地址单元存放低 8 位

再向栈中 push 数据

assembly02 021

其中涉及的指令有

mov bx,0132H
push bx

现在栈中有两条数据现在我们执行出栈操作

assembly02 022

其中涉及的指令有

pop ax
/* ax = 0132H */

再继续取出数据

assembly02 023

涉及的指令有

pop bx
/* bx = */

完整的 push 和 pop 过程如下

assembly02 024

现在 cxuan 问你一个问题我们上面描述的是 10000H ~ 1000FH 这段空间来作为 push 和 pop 指令的存取单元但是你怎么知道这个栈单元就是 10000H ~ 1000FH 呢?也就是说你如何选择指定的栈单元进行存取?

事实上8086 CPU 有一组关于栈的寄存器 SSSPSS 是段寄存器它存储的是栈的基础位置也就是栈顶的位置而 SP 是栈指针它存储的是偏移地址在任意时刻SS:SP 都指向栈顶元素push 和 pop 指令执行时CPU 从 SS 和 SP 中得到栈顶的地址

现在我们可以完整的描述一下 push 和 pop 过程了下面 cxuan 就给你推导一下这个过程

assembly02 025

上面这个过程主要涉及到的关键变化如下

当使用 PUSH 指令向栈中压入 1 个字节单元时SP = SP - 1即栈顶元素会发生变化

而当使用 PUSH 指令向栈中压入 2 个字节的字单元时SP = SP – 2 即栈顶元素也要发生变化

当使用 POP 指令从栈中弹出 1 个字节单元时 SP = SP + 1即栈顶元素会发生变化

当使用 POP 指令从栈中弹出 2 个字节单元的字单元时 SP = SP + 2 即栈顶元素会发生变化

栈顶越界问题

现在我们知道8086 CPU 可以使用 SS 和 SP 指示栈顶的地址并且提供 PUSH 和 POP 指令实现入栈和出栈所以你现在知道了如何能够找到栈顶位置但是你如何能保证栈顶的位置不会越界呢?栈顶越界会产生什么影响呢?

比如如下是一个栈顶越界的示意图

assembly02 026

第一开始SS:SP 寄存器指向了栈顶然后向栈空间 push 一定数量的元素后SS:SP 位于栈空间顶部此时再向栈空间内部 push 元素就会出现栈顶越界问题

栈顶越界是危险的因为我们既然将一块区域空间安排为栈那么在栈空间外部也可能存放了其他指令和数据这些指令和数据有可能是其他程序的所以如此操作会让计算机懵逼

我们希望 8086 CPU 能自己解决问题毕竟 8086 CPU 已经是个成熟的 CPU 了要学会自己解决问题了

然鹅(故意的)这对于 8086 CPU 来说这可能是它一辈子的 夙愿 了真实情况是8086 CPU 不会保证栈顶越界问题也就是说 8086 CPU 只会告诉你栈顶在哪并不会知道栈空间有多大所以需要程序员自己手动去保证

另外我输出了 六本 PDF已免费提供下载如下所示


相关文章

猜您喜欢

  • react配置反向代理 JS跨域解决方案react配置反向代理

    想了解JS跨域解决方案react配置反向代理的相关内容吗tby_37在本文为您仔细讲解react配置反向代理的相关知识和一些Code实例欢迎阅读和指正我们先划重点:js跨域解决方案,react配置反向代理下面大家一起来学习吧..
  • Docker部署SpringBoot应用 Docker部署SpringBoot应用的实现步骤

    想了解Docker部署SpringBoot应用的实现步骤的相关内容吗花伤情犹在在本文为您仔细讲解Docker部署SpringBoot应用的相关知识和一些Code实例欢迎阅读和指正我们先划重点:Docker部署SpringBoot,DockerSpringBoot部署下面大家一起来学习吧..

网友评论

Copyright 2020 www.fresh-weather.com 【世纪下载站】 版权所有 软件发布

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 点此查看联系方式