关于计算机:十一假期淦了八天寄存器的相关知识

4次阅读

共计 8546 个字符,预计需要花费 22 分钟才能阅读完成。

我把本人以往的文章汇总成为了 Github,欢送各位大佬 star
https://github.com/crisxuan/b…

上面咱们就来介绍一下对于寄存器的相干内容。咱们晓得,寄存器 是 CPU 外部的结构,它次要用于信息的存储。除此之外,CPU 外部还有 运算器 ,负责解决数据; 控制器 管制其余组件;内部总线 连贯 CPU 和各种部件,进行数据传输;外部总线 负责 CPU 外部各种组件的数据处理。

那么对于咱们所理解的汇编语言来说,咱们的次要关注点就是 寄存器

为什么会呈现寄存器?因为咱们晓得,程序在内存中装载,由 CPU 来运行,CPU 的主要职责就是用来解决数据。那么这个过程势必波及到从存储器中读取和写入数据,因为它波及通过管制总线发送数据申请并进入存储器存储单元,通过同一通道获取数据,这个过程十分的繁琐并且会波及到大量的内存占用,而且有一些罕用的内存页存在,其实是没有必要的,因而呈现了寄存器,存储在 CPU 外部。

意识寄存器

寄存器的官网叫法有很多,Wiki 下面的叫法是 Processing Register,也能够称为 CPU Register,计算机中常常有一个货色多种叫法的状况,反正你晓得都说的是寄存器就能够了。

意识寄存器之前,咱们首先先来看一下 CPU 外部的结构。

CPU 从逻辑上能够分为 3 个模块,别离是管制单元、运算单元和存储单元,这三局部由 CPU 外部总线连接起来。

简直所有的冯·诺伊曼型计算机的 CPU,其工作都能够分为 5 个阶段:取指令、指令译码、执行指令、访存取数、后果写回

  • 取指令 阶段是将内存中的指令读取到 CPU 中寄存器的过程,程序寄存器用于存储下一条指令所在的地址
  • 指令译码 阶段,在取指令实现后,立马进入指令译码阶段,在指令译码阶段,指令译码器依照预约的指令格局,对取回的指令进行拆分和解释,辨认辨别出不同的指令类别以及各种获取操作数的办法。
  • 执行指令 阶段,译码实现后,就须要执行这一条指令了,此阶段的工作是实现指令所规定的各种操作,具体实现指令的性能。
  • 拜访取数 阶段,依据指令的须要,有可能须要从内存中提取数据,此阶段的工作是:依据指令地址码,失去操作数在主存中的地址,并从主存中读取该操作数用于运算。
  • 后果写回 阶段,作为最初一个阶段,后果写回(Write Back,WB)阶段把执行指令阶段的运行后果数据写回到 CPU 的外部寄存器中,以便被后续的指令疾速地存取;

计算机架构中的寄存器

寄存器是一块速度十分快的计算机内存,上面是古代计算机中具备存储性能的部件比对,能够看到,寄存器的速度是最快的,同时也是造价最昂扬的。

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

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

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

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

这 14 个寄存器有可能进行具体的划分,依照性能能够分为三种

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

上面咱们别离介绍一下这几种寄存器

通用寄存器

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

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 位寄存器

如下图所示。

合起来就是

AX 的低位(0 – 7)位形成了 AL 寄存器,高 8 位(8 – 15)位形成了 AH 寄存器。AH 和 AL 寄存器是能够应用的 8 位寄存器,其余同理。

在意识了寄存器之后,咱们通过一个示例来看一下数据的具体存储形式。

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

寄存器的存储形式是先存储低位,如果低位满足不了就存储高位,如果低位可能满足,高位用 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 和 BL。BX 除了具备暂存数据的性能外,还用于 寻址 ,即寻找物理内存地址。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 和内存之间的交互有三条总线,即数据总线、管制总线和地址总线。

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 次幂的寻址能力,也就是 1MB。8086 CPU 又是 16 位的构造,从 8086 CPU 的构造看,它只能传输 16 位的地址,也就是 2^16 次幂也就是 64 KB,那么它如何达到 1MB 的寻址能力呢?

原来,8086 CPU 的外部采纳两个 16 位地址合成的形式来传输一个 20 位的物理地址,如下图所示

叙述一下上图形容的过程

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

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

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

其实段地址 16,就是左移 4 位。在下面的叙述中,物理地址 = 段地址 16 + 偏移地址,其实就是 根底地址 + 偏移地址 = 物理地址 寻址模式的一种具体实现计划。根底地址其实就等于段地址 * 16。

你可能不太分明 的概念,上面咱们就来探讨一下。

什么是段

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

然而这些的划分并不是内存干的,cxuan 通知你是谁干的,这其实是幕后 Boss CPU 搞的,内存当作了声讨的对象。

其实,内存没有进行分段,分段齐全是由 CPU 搞的,下面聊过的通过根底地址 + 偏移地址 = 物理地址的形式给出内存单元的物理地址,使得咱们能够分段治理 CPU。

如图所示

这是两个 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 指向的内容当作指令执行。如下图所示

阐明一下上图

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

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

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

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

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

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

此时这个指令就曾经具备执行条件,此时 IP 也就是指令指针会主动减少。咱们下面说到 IP 其实就是从 Code Segment 也就是 CS 处偏移的地址,也就是偏移地址。它会晓得下一个须要读取指令的地址,如下图所示

在这之后,指令执行执行取出的 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),它的模型图大略是这样

入栈相当于是减少操作,出栈相当于是删除操作,只不过叫法不一样。栈和内存不同,它不须要指定元素的地址。它的大略应用如下

// 压入数据
Push(123);
Push(456);
Push(789);

// 弹出数据
j = Pop();
k = Pop();
l = Pop();

在栈中,LIFO 形式示意栈的数组中所保留的最初面的数据(Last In)会被最先读取进去(First Out)。

栈和 SS 寄存器

上面咱们就通过一段汇编代码来形容一下栈的压入弹出的过程

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

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

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

而后咱们向栈中 push 数据后,栈中数据如下

波及的指令有

mov ax,2345H
push ax

留神,数据会用两个单元寄存,高地址单元寄存高 8 位地址,低地址单元寄存低 8 位。

再向栈中 push 数据

其中波及的指令有

mov bx,0132H
push bx

当初栈中有两条数据,当初咱们执行出栈操作

其中波及的指令有

pop ax
/* ax = 0132H */

再持续取出数据

波及的指令有

pop bx
/* bx = */

残缺的 push 和 pop 过程如下

当初 cxuan 问你一个问题,咱们下面形容的是 10000H ~ 1000FH 这段空间来作为 push 和 pop 指令的存取单元。然而,你怎么晓得这个栈单元就是 10000H ~ 1000FH 呢?也就是说,你如何抉择指定的栈单元进行存取?

事实上,8086 CPU 有一组对于栈的寄存器 SSSP。SS 是段寄存器,它存储的是栈的根底地位,也就是栈顶的地位,而 SP 是栈指针,它存储的是偏移地址。在任意时刻,SS:SP 都指向栈顶元素。push 和 pop 指令执行时,CPU 从 SS 和 SP 中失去栈顶的地址。

当初,咱们能够残缺的形容一下 push 和 pop 过程了,上面 cxuan 就给你推导一下这个过程。

下面这个过程次要波及到的要害变动如下。

当应用 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 指令实现入栈和出栈,所以,你当初晓得了如何可能找到栈顶地位,然而你如何能保障栈顶的地位不会越界呢?栈顶越界会产生什么影响呢?

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

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

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

咱们心愿 8086 CPU 能本人解决问题,毕竟 8086 CPU 曾经是个成熟的 CPU 了,要学会本人解决问题了。

然鹅(成心的),这对于 8086 CPU 来说,这可能是它一辈子的 夙愿 了,真实情况是,8086 CPU 不会保障栈顶越界问题,也就是说 8086 CPU 只会通知你栈顶在哪,并不会晓得栈空间有多大,所以须要程序员本人手动去保障。。。

另外,我输入了 六本 PDF,已收费提供下载,如下所示

链接: pan.baidu.com/s/1mYAeS9hI… 明码: p9rs

正文完
 0