Chinaunix首页 | 论坛 | 博客
  • 博客访问: 338955
  • 博文数量: 40
  • 博客积分: 157
  • 博客等级: 入伍新兵
  • 技术积分: 536
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-15 19:44
文章分类

分类: LINUX

2013-08-11 22:20:11

转载自    Zachary.XiaoZhen - 梦想的天空

这篇博文是我从 Zachary.XiaoZhen 兄弟的博客转载过来的,他前面介绍了一些Intel CPU,我自做主张的拿掉了,直接捞干的上!

PS:这里说下,我主要看的是关于CPU寄存器的使用惯例方面的介绍,例如很多时候会有些默认的使用,例如SI寄存器的段地址默认是DS等。。。希望各位能有更多的收获!

好,关于 Intel CPU 的介绍就到这里了,下面就要开始回归中心,看 CPU 中的寄存器了,

首先,从学习的角度来说,从 8086/8088  CPU 下手是不错的选择,而我这里选择的也是 8086 CPU 而已,

说实在的,像 80386 CPU 我也还没有研究过,像奔腾这些,呵呵,扯更远了,

说到底也就只能拿 8086 出来晒晒而已,当然,从 8086 开始也是学习的最佳路径 。

       

说了这么久,到底寄存器是什么呢?其实很简单,寄存器就是个存储信息的单元或者说是器件又或者说是容器而已,

就比如内存也是一个存储介质或者说是存储单元而已,其实寄存器从理解上来说和内存差不多,

只不过寄存器(这里讨论的寄存器都是 CPU 中的寄存器,不包括外设上的寄存器)位于  CPU  内部,而内存位于 CPU 外部,

而且,寄存器比内存可是珍贵得多啊,就拿内存和硬盘来比,肯定是内存在使用上珍贵得多,是 PC 中的稀有资源,

而寄存器是 CPU 中的稀有资源,内存和寄存器相比就像硬盘和内存相比一样 。

而对于一个汇编程序员来说,CPU 中主要可以使用的也就是寄存器而已,汇编程序员可以使用指令来读写 CPU 中的寄存器,

从而可以实现对于 CPU 的控制,当然,不同的 CPU ,寄存器的个数和结构都是不一样的,

比如 8086 CPU 中,寄存器的个数也就 14 个而已,

并且 8086 CPU 中所有的寄存器的结构为 16 位,即一个寄存器中可以存放下 2B 2 个字节,

而到了 80386 CPU 中,寄存器的个数也比 8086 增多了,比如在 80386 中添加了系统地址寄存器等寄存器,

同时寄存器的结构也变了,比如在 80386 中绝大多数的寄存器为 32 位,而有些寄存器则是 16 位 。

8086  CPU 中寄存器总共为 14 个,且均为 16 位 。

即 AXBXCXDXSPBPSIDIIPFLAGCSDSSSES 14 个。

而这 14 个寄存器按照一定方式又分为了通用寄存器,控制寄存器和段寄存器。

通用寄存器:

AXBXCXDX 称作为数据寄存器:

AX (Accumulator):累加寄存器,也称之为累加器;

BX (Base):基地址寄存器;

CX (Count):计数器寄存器;

DX (Data):数据寄存器;

SP BP 又称作为指针寄存器:

SP (Stack Pointer):堆栈指针寄存器;

BP (Base Pointer):基指针寄存器;

SI DI 又称作为变址寄存器:

SI (Source Index):源变址寄存器;

DI (Destination Index):目的变址寄存器;

控制寄存器:

IP (Instruction Pointer):指令指针寄存器;

FLAG:标志寄存器;

段寄存器:

CS (Code Segment):代码段寄存器;

DS (Data Segment):数据段寄存器;

SS (Stack Segment):堆栈段寄存器;

ES (Extra Segment):附加段寄存器;

           

           

通用寄存器

从上面可以知道,在 8086 CPU 中,通用寄存器有 8 个,分别是 AXBXCXDXSPBPSIDI

至于为什么给它们取名做通用寄存器,那是因为,这些个寄存器每一个都有自己专门的用途,

比如 CX 作为计数寄存器,则是在使用 LOOP 指令循环时用来指定循环次数的寄存器,

如果它们每一个都只有一个专用的作用,那就它们只能称之为专用寄存器了,

正是因为这些个寄存器还可以用来传送数据和暂存数据,所以才称它们为通用寄存器 。

下面就按顺序来一一介绍这几个通用寄存器了:

数据寄存器(AXBXCXDX):

数据寄存器有 AXBXCXDX 四个组成,

由于在 8086 之前的 CPU 8 CPU,所以为了兼容以前的 8 位程序,

8086 CPU 中,每一个数据寄存器都可以当做两个单独的寄存器来使用,

由此,每一个 16 位寄存器就可以当做 2 个独立的 8 位寄存器来使用了 。

AX 寄存器可以分为两个独立的 8 位的 AH AL 寄存器;

BX 寄存器可以分为两个独立的 8 位的 BH BL 寄存器;

CX 寄存器可以分为两个独立的 8 位的 CH CL 寄存器;

DX 寄存器可以分为两个独立的 8 位的 DH DL 寄存器;

除了上面 4 个数据寄存器以外,其他寄存器均不可以分为两个独立的 8 位寄存器 ;

注意在上面标志中的“独立”二字,这两个字表明 AH AL 作为 8 位寄存器使用时,

可以看做它们是互不相关的,也就是看做两个完全没有联系的寄存器 X Y 即可,

比如指令   MOV   AH , 12H CPU 在执行时根本就不会知道 AL 中是什么鬼东西,因为它只认识  AH

下面给出一幅 16 位数据寄存器的结构图:

表示 16 位 寄存器 AX 可以表示成两个 8 位寄存器,

其中 AH 表示高位的 8 位寄存器,AL 表示低位的 8 位寄存器 。


AX 寄存器:

如上所说,AX 的另外一个名字叫做累加寄存器或者简称为累加器,其可以分为 2 个独立的 8 位寄存器 AH AL

在写汇编程序时,AX 寄存器可以说是使用率最高的寄存器(不过,总共才那么 14 个寄存器,哪一个不经常使用咯?),

既然 AX 是数据寄存器的话,那么理所当然,其可以用来存放普通的数据,由于其是 16 位寄存器,

自然也就可以存放 16 位数据,但是因为其又可以分为 2 个独立的 8 位寄存器 AH AL

所以,在 AH AL 中又可以独立的存放 2 8 位的数据,

可以有以下代码(即将 AX 当做普通的寄存器使用,即可以用来暂存数据):

MOV AX,1234H ;向寄存器 AX 传入数据 1234H MOV AH,56H ;向寄存器 AX 的高 8 位寄存器 AH 中传入数据 56H MOV AL,78H ;向寄存器 AX 的低 8 位寄存器 AL 中传入数据 78H 3 条语句的执行过程如下:
 

而既然 AX 又被称作为累加器,自然其还有一点点特殊的地方的:

AX 寄存器还具有的特殊用途是在使用 DIV MUL 指令时使用,

DIV 8086 CPU 中是除法指令,而在使用除法的时候有两种情况,即除数可以是 8 位或者是 16 位的,

而且除数可以存放在寄存器中或者是内存单元中,而至于被除数的话,自然,应该由 AX 来代替了,

除数是 8 时,被除数一定会是 16 的,并且默认是放在 AX 寄存器中,

而当除数是 16 时,被除数一定是 32 的,因为 AX 16 位寄存器,自然,放不下 32 位的被除数,

所以,在这里还需要使用另一个 16 位寄存器 DX

其中 DX 存放 32 位的被除数的高 16 ,而 AX 则存放 32 位的被除数的低 16

同时,AX 的作用还不仅仅是用来保存被除数的,当除法指令执行完成以后,

如果除数是 8 的,则在 AL 中会保存此次除法操作的商,而在 AH 中则会保存此次除法操作的余数

当然,如果除数是 16 的话,则 AX 中会保存本次除法操作的商,而DX 则保存本次除法操作的余数

上面介绍的是 AX 寄存器在除法操作中的应用,下面还需要介绍一下 AX 在乘法操作中的应用,

当使用 MUL 做乘法运算时,两个相乘的数要么都是 8 位,要么都是 16 位,

如果两个相乘的数都是 8 的话,则一个默认是放在 AL

而另一个 8 位的乘数则位于其他的寄存器或者说是内存字节单元中,

而如果两个相乘的数都是 16 位的话,则一个默认存放在 AX

另一个 16 位的则是位于 16 的寄存器中或者是某个内存字单元中。

同时,当 MUL 指令执行完毕后,如果是 8 位的乘法运算,则默认乘法运算的结果是保存在 AX

而如果是 16 位的乘法运算的话,则默认乘法运算的结果有 32

其中,高位默认保存在 DX ,而低位则默认保存在 AX

AX 寄存器在 DIV  指令中的使用:

MOV DX,0H ;设置 32 位被除数的高 16 位为 0H MOV AX,8H ;设置 32 位被除数的低 16 位为 8H MOV BX,2H ;设置 16 位除数为 2H DIV BX ;执行计算

4 条语句的执行过程如下:


AX 寄存器在 MUL  指令中的使用:

MOV AX,800H ;设置 16 位乘数为 800H MOV BX,100H ;设置 16 位乘数为 100H MOV DX,0H ;清空用来保存乘法结果的高 16   MUL BX ;执行计算 

4 条语句的执行过程如下:

BX 寄存器:

首先可以明确的是,BX 作为数据寄存器,表明其是可以暂存一般的数据的,

即在某种程度上,它和 AX 可以暂存一般性数据的功能是一样的,

其同样为了适应以前的 8 CPU ,而可以将 BX 当做两个独立的 8 位寄存器使用,即有 BH BL

除了暂存一般性数据的功能外,BX 作为通用寄存器的一种,BX 主要还是用于其专属功能 – 寻址(寻址物理内存地址)上,

BX 寄存器中存放的数据一般是用来作为偏移地址使用的,何为偏移地址呢?

既然是偏移地址的话,当然得有一个基地址了,而这个基地址其实就是段地址,这里就涉及到了段寄存器,

当然,在介绍 BX 寄存器的时候,我不会去介绍段寄存器,上面提到 BX 的主要功能是用在寻址上,

那么,其是如何寻址的呢?

对于寻址这个话题,我会在我的下一篇博文中作出详细的介绍,

而这里,我只点一下,在 8086 CPU 中,CPU 是根据 <段地址:偏移地址> 来进行寻址操作的,

BX 中存放的数据表示的是偏移地址的话,自然,便可以通过 <段地址:[BX]> 的方式来完成寻址操作了。

为了介绍 BX 在寻址当中的作用,下面我给出一副示意图:

上面的示意图表示:可以令 BX = 2,然后通过 DS : [BX] 来访问到内存中段地址为 DS,且偏移量为 2 的内存单元了。

上面介绍的这种寻址方式是 BX 在寻址中最最简单的应用了,而对于稍微复杂的寻址方式,

还可以依赖于 SIDIBP 等寄存器来一起完成,当然,这会是下一篇博文将要介绍的内容了。

BX 寄存器在寻址中的使用:

MOV BX,5H MOV AH,11H MOV AH,[BX] ;设置 AX 的值为偏移地址为 BX 中的值时所代表的内存单元

3 条语句的执行过程如下:

从上图可以看出,在偏移地址为 5 时的内存单元中的数据位 BBH

而从这幅图上面就可以看出,确实通过 [BX] 找到了偏移地址为 5 处的内存单元,并且将内存单元移入了 AH 中。


CX 寄存器:

CX 寄存器作为数据寄存器的一种呢,其同样具有和 AXBX 一样的特点,即可以暂存一般性的数据,

同时还可以将其当做两个独立的 8 位寄存器使用,即有 CH CL 两个 8 位寄存器,

当然,CX 也是有其专门的用途的,CX 中的 C 被翻译为 Counting 也就是计数器的功能,

在汇编指令中使用循环 LOOP 指令时,可以通过 CX 来指定需要循环的次数

CPU 在每一次执行 LOOP 指令的时候,都会做两件事:

一件就是令 CX = CX – 1,即令 CX 计数器自动减去 1

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

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

CX 寄存器在循环中的使用(输出 5 个白底蓝字的 A):

MOV AX,0B800H MOV DS,AX ;使用 80x25 彩色字符模式,内存地址 0xB8000 - 0xBFFFFF MOV BX,0 ;0xB8000 开始 MOV CX,5H ;循环 5  MOV DX,41H ;A 16 进制为 41H MOV AX,01110001B ;显示白底蓝字 s:  MOV [BX],DX ;显示 ASCII 字符  ADD BX,1  MOV [BX],AX ;设置字符显示属性  ADD BX,1 LOOP s

语句的执行过程如下:

              

DX 寄存器:

DX 寄存器作为数据寄存器的一种,同样具有和 AXBXCX 一样的特点,即可以暂存一般性的数据,

同时还可以将其当做两个独立的 8 位寄存器使用,极有 DH DL

同时,DX 作为一个通用寄存器的话,自然其还有其他的用途,而关于 DX 在其他方面的用途,

其实在前面介绍 AX 寄存器时便已经有所介绍了,

即当在使用 DIV 指令进行除法运算时,如果除数为 16 位时,被除数将会是 32 位,而被除数的高 16 位就是存放在 DX

而且执行完 DIV 指令后,本次除法运算所产生的余数将会保存在 DX 中,

同时,在执行 MUL 指令时,如果两个相乘的数都是 16 位的话,

那么相乘后产生的结果显然需要 32 位来保存,而这 32 位的结果的高 16 位就是存放在 DX 寄存器中 。

DX 寄存器在 DIV  指令中的使用(即 2293812 / 256 = 8960  余数为 52):

MOV DX,0023H ;32 位被除数的高 16  MOV AX,0034H ;32 位被除数的低 16  MOV BX,100H ;16 的除数 DIV BX 

语句的执行过程如下:


可以看到在语句结束以后,AX = 2300H  即十进制的 8960,而 DX = 34H即十进制的 52 和我们的结果是一致的。

DX 寄存器在 MUL  指令中的使用则各位可以参考在 AX MUL 运算的使用,这里就不贴出来了。

指针寄存器(BP,SP):

BP 寄存器:

8086  CPU 中的指针寄存器包括两个,即 SP 和 BP ,在这里呢,我先只对 BP 寄存器做介绍,

因为 SP 寄存器实质上必须和 SS 段寄存器一起使用,所以,我将会把 SP 寄存器留到后面和 SS 段寄存器一起作介绍。

BP (Base Pointer)也就是基指针寄存器,它和其他的几个用来进行寻址操作所使用的寄存器(还有 BX,SI,DI)没有太大的区别,

关于 SI 和 DI 寄存器的下面请见下文。

首先,BP 寄存器作为通用寄存器的一种,说明其是可以暂存数据的,而后,BP 又不是数据寄存器,

也就意味着其不能分割成 2 个独立的 8 位寄存器使用,

而后当以 […] 的方式访问内存单元而且在 […] 中使用了寄存器 BP 的话,

那么如果在指令中没有明确或者说是显示的给出段地址时,

段地址则使用默认的 SS 寄存器中的值(BX,SI,DI 会默认使用 DS 段寄存器),

比如 DS:[BP] 则在这里明确给出了段地址位于 DS 中,

所以,这里代表的内存单元即是段地址为 DS ,偏移量为 BP 寄存器中的值的内存单元,

而如果单单是使用 [BP] 的话,则代表的内存单元是段地址为 SS,偏移量为 BP 寄存器中的值的内存单元。

并且 BP 寄存器主要适用于给出堆栈中数据区的偏移,从而可以方便的实现直接存取堆栈中的数据,

至于堆栈的话,会在后面的博文中介绍。

在 8086 CPU 中,只有 4 个寄存器可以以  […]  的方式使用,这四个寄存器分别是 BX,SI,DI,BP。

下面的  Demo  是 BX 寄存器在寻址中的使用:

MOV BP,0
MOV AX,[BP] ;将 SS:[BP] 代表的内存单元移入 AX 中 MOV AX,CS:[BP] ;将 CS:[BP] 代表的内存单元移入 AX 中
语句的执行过程如下:

image

            

变址寄存器(SI,DI):

首先,变址寄存器和上面介绍的指针寄存器(也就是 BP 和 SP),它们的功能其实都是用于存放某个存储单元地址的偏移,

或者是用于某组存储单元开始地址的偏移,即作为存储器指针使用,当然,由于变址寄存器和指针寄存器都是属于通用寄存器,

所以它们也可以保存算术结果或者说是具有暂存数据的功能,但是因为它们不是数据寄存器,所以无法分割成 2 个独立的 8 位寄存器使用,

关于变址寄存器和指针寄存器的详细使用,笔者将会在下一篇博文中作出最详细的介绍,

SI (Source Index) 是源变址寄存器,DI (Destination Index) 即是目的变址寄存器,

8086 CPU 中的 SI 寄存器和 DI 寄存器其实和 BX 寄存器的功能是差不多的,

只不过 SI 寄存器和 DI 寄存器均不是数据寄存器,所以它们不能够拆分为 2 个独立的 8 位寄存器,

而这也就是 SI 寄存器和 DI 寄存器与BX 寄存器所不同的地方,

既然,SI,DI 两个寄存器的功能和 BX 差不多,自然,SI 和 DI 中也是可以暂存一般性数据的,

同时,通过使用 SI 和 DI 寄存器也是可以用来完成寻址操作的。

比如下面的代码就是可行的:

MOV SI,0 ;初始化偏移地址为 0 MOV AX,[SI] ;将段地址为 DS 偏移地址为 SI 的内存单元中的值移入 AX 中 MOV AX,DS:[SI] ;将段地址为 DS 偏移地址为 SI 的内存单元中的值移入 AX 中 MOV AX,SS:[SI] ;将段地址为 SS 偏移地址为 SI 的内存单元中的值移入 AX 中 MOV DI,0 ;初始化偏移地址为 0 MOV AX,[DI] ;将段地址为 DS 偏移地址为 DI 的内存单元中的值移入 AX 中 MOV AX,DS:[DI] ;将段地址为 DS 偏移地址为 DI 的内存单元中的值移入 AX 中 MOV AX,SS:[DI] ;将段地址为 SS 偏移地址为 DI 的内存单元中的值移入 AX 中


阅读(10471) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~