零基础入门~汇编语言(第四版王爽)~第2章寄存器(CPU工作原理)

前言

一个典型的 CPU(此处讨论的不是某一具体的 CPU)由运算器、控制器、寄存器(CPU 工作原理)等器件构成,这些器件靠内部总线相连。前一章所说的总线,相对于 CPU 内部 来说是外部总线。内部总线实现CPU 内部各个器件之间的联系,外部总线实现CPU 和主 板上其他器件的联系。简单地说,在CPU 中:

● 运算器进行信息处理;
● 寄存器进行信息存储;
● 控制器控制各种器件进行工作;
● 内部总线连接各种器件,在它们之间进行数据的传送。

对于一个汇编程序员来说,CPU 中的主要部件是寄存器。寄存器是CPU 中程序员可以用指令读写的部件。程序员通过改变各种寄存器中的内容来实现对CPU的控制。
不同的CPU, 寄存器的个数、结构是不相同的。8086CPU 有14个寄存器,每个寄存器有一个名称。这些寄存器是:AX 、BX 、CX 、DX 、SI 、DI 、SP 、BP 、IP 、CS 、SS 、 DS 、ES 、PSW。

2.1 通用寄存器

8086CPU的所有寄存器都是16位的,可以存放两个字节。AX 、BX 、CX 、DX这 4 个寄存器通常用来存放一般性的数据,被称为通用寄存器。

以AX 为例,寄存器的逻辑结构如图2.1所示。
在这里插入图片描述
一个16位寄存器可以存储一个16位的数据,数据在寄存器中的存放情况如图2.2所示。 想一想,一个16位寄存器所能存储的数据的最大值为多少?
8086CPU 的上一代 CPU 中的寄存器都是8位的,为了保证兼容,使原来基于上代 CPU编写的程序稍加修改就可以运行在8086之上,8086CPU的 AX 、BX 、CX 、DX这 4 个寄存器都可分为两个可独立使用的8位寄存器来用:
● AX可分为AH 和 AL;
● BX可分为BH 和 BL;
● CX可分为CH 和 CL;
● DX可分为DH 和 DL;

数据:18
二进制表示:10010
在寄存器AX中的存储:
在这里插入图片描述
数据:20000
二进制表示:100111000100000
在寄存器 AX中的存储:
在这里插入图片描述
以 AX 为例,8086CPU 的16位寄存器分为两个8位寄存器的情况如图2.3所示。
在这里插入图片描述
AX 的低8位(0位~ 7位)构成了AL 寄存器,高8位(8位~15位)构成了AH 寄存器。 AH 和 AL 寄存器是可以独立使用的8位寄存器。图2.4展示了16位寄存器及它所分成的 两个8位寄存器的数据存储的情况。
想一想, 一个8位寄存器所能存储的数据的最大值为多少?
(2的8次方减一)
在这里插入图片描述

2.2 字在寄存器中的存储

出于对兼容性的考虑,8086CPU 可以一次性处理以下两种尺寸的数据。
● 字节:记为byte, 一 个字节由8个bit组成,可以存在8位寄存器中。
● 字:记为 word, 一个字由两个字节组成,这两个字节分别称为这个字的高位字 节和低位字节,如图2.5所示。
在这里插入图片描述
一个字可以存在一个16位寄存器中,这个字的高位字节和低位字节自然就存在这个 寄存器的高8位寄存器和低8位寄存器中。如图2.4所示, 一个字型数据20000,存在 AX 寄存器中,在AH 中存储了它的高8位,在AL中存储了它的低8位。 AH 和AL中的数据,既可以看成是一个字型数据的高8位和低8位,这个字型数据的大小是20000;又可以看成是两个独立的字节型数据,它们的大小分别是78和32。

                                        关于数制的讨论

任何数据,到了计算机中都是以二进制的形式存放的。为了描述不同的问题,又经常将它们用其他的进制来表示。比如图2.4中寄存器AX 中的数据是0100111000100000,这就是AX 中的信息本身,可以用不同的逻辑意义来看待它。可以将它看作一个数值,大小是20000。
当然,二进制数0100111000100000本身也可表示一个数值的大小,但人类习惯的是十进制,用十进制20000表示可以使我们直观地感受到这个数值的大小。十六进制数的一位相当于二进制数的四位,如0100111000100000可表示成:4(0100)、E(1110)、2(0010)、0(0000)四位十六进制数。 一个内存单元可存放8位数据,CPU 中的寄存器又可存放n个8位的数据。也就是说,计算机中的 数据大多是由1~N 个8位数据构成的。很多时候,需要直观地看出组成数据的各个字节数据的值,用十六进制来表示数据可以直观地看出这个数据是由哪些8位数据构成的。比如20000写成4E20就可以直观 地看出,这个数据是由4E 和20两个8位数据构成的,如果AX中存放4E20, 则 AH 里是4E,AL 里是20。这种表示方法便于许多问题的直观分析。在以后的课程中,我们多用十六进制来表示一个数据。
在以后的课程中,为了区分不同的进制,在十六进制表示的数据的后面加H, 在二进制表示的数据后面加B,十进制表示的数据后面什么也不加。如:可用3种不同的进制表示图2.4中AX 里的数据,十进制:20000,十六进制:4E20H,二进制:0100111000100000B。

2.3 几条汇编指令

通过汇编指令控制CPU进行工作,看一下表2.1中的几条指令。
在这里插入图片描述
注意,为了使具有高级语言基础的读者更好地理解指令的含义,有时会用文字描述和高级语言描述这两种方式来描述一条汇编指令的含义。在写一条汇编指令或一个寄存器的名称时不区分大小写。如: mov ax,18和 MOV AX,18的含义相同; bx 和 BX 的含义相同。
接下来看一下 CPU 执行表2.2中所列的程序段中的每条指令后,对寄存器中的数据 进行的改变。
在这里插入图片描述
在这里插入图片描述
问题2.1
指令执行后AX 中的数据为多少?思考后看分析。
分析:
程序段中的最后一条指令 add ax,bx, 在执行前ax 和 bx 中的数据都为8226H, 相加后所得的值为:1044CH, 但是 ax为16位寄存器,只能存放4位十六进制的数据,所以最高位的1不能在ax 中保存,ax 中的数据为:044CH。
表2.3中所列的一段程序的执行情况。
在这里插入图片描述
问题2.2

指令执行后AX 中的数据为多少?思考后看分析。
分析:
程序段中的最后一条指令 add al,93H, 在执行前,al 中的数据为C5H, 相加后所得的值为:158H, 但是al 为8位寄存器,只能存放两位十六进制的数据,所以最高位的1丢失 ,ax 中的数据为:0058H。(这里的丢失,指的是进位值不能在8位寄存器中保存,但 是CPU 并不真的丢弃这个进位值,关于这个问题,我们将在后面中讨论。)
注意,此时al 是作为一个独立的8位寄存器来使用的,和ah 没有关系,CPU 在执行这条指令时认为ah 和al 是两个不相关的寄存器。不要错误地认为,诸如 add al,93H的指令产生的进位会存储在ah 中,add al,93H 进行的是8位运算。
如果执行add ax,93H,低8位的进位会存储在ah 中 ,CPU 在执行这条指令时认为只 有一个16位寄存器 ax, 进行的是16位运算。指令 add ax,93H 执行后,ax 中的值为: 0158H。此时,使用的寄存器是16位寄存器ax,add ax,93H相当于将 ax 中的16位数据 00c5H和另一个16位数据0093H 相加,结果是16位的0158H。

在进行数据传送或运算时,要注意指令的两个操作对象的位数应当是一致的,例如:

在这里插入图片描述

检测点2 . 1
(1)写出每条汇编指令执行后相关寄存器中的值。
mov ax,62627 (AX=F4A3H)

mov ah,31H (AX=31A3H)

mov al,23H (AX=3123H )

add ax,ax ( AX=6246H)

mov bx,826CH (BX=826CH)

mov cx,ax (CX=6246H)

mov ax,bx (AX=826CH)

add ax,bx (AX=04D8H)

mov al,bh (AX=0482H)

mov ah,bl (AX=6C82H)

add ah,ah (AX=D882H)

add al,6 (AX=D888H)

add al,al (AX=D810H)

mov ax,cx (AX=6246H)

(2)只能使用目前学过的汇编指令,最多使用4条指令,编程计算2的4次方。

答案:
mov ax,2 AX=2

add ax,ax AX=4

add ax,ax AX=8

add ax,ax AX=16

2.4 物理地址

我们知道,CPU访问内存单元时,要给出内存单元的地址。所有的内存单元构成的存储空间是一个一维的线性空间,每一个内存单元在这个空间中都有唯一的地址,我们将 这个唯一的地址称为物理地址。
CPU 通过地址总线送入存储器的,必须是一个内存单元的物理地址。在 CPU 向地址总线上发出物理地址之前,必须要在内部先形成这个物理地址。不同的CPU可以有不同的 形成物理地址的方式。我们现在讨论8086CPU是如何在内部形成内存单元的物理地址的。

2.5 16位结构的CPU

我们说8086CPU 的上一代CPU(8080 、8085)等是8位机,而8086是16位机,也可 以说8086是16位结构的CPU。那么什么是16位结构的CPU 呢?
概括地讲,16位结构(16位机、字长为16位等常见说法,与16位结构的含义相同) 描述了一个CPU 具有下面几方面的结构特性。
● 运算器一次最多可以处理16位的数据;
● 寄存器的最大宽度为16位;
● 寄存器和运算器之间的通路为16位。
8086是16位结构的CPU, 这也就是说,在8086内部,能够一次性处理、传输、暂 时存储的信息的最大长度是16位的。内存单元的地址在送上地址总线之前,必须在 CPU 中处理、传输、暂时存放,对于16位CPU, 能一次性处理、传输、暂时存储16位的地址。

2.6 8086CPU给出物理地址的方法

8086CPU有20位地址总线,可以传送20位地址,达到1MB寻址能力。8086CPU 又是16位结构,在内部一次性处理、传输、暂时存储的地址为16位。从8086CPU的内部结构来看,如果将地址从内部简单地发出,那么它只能送出16位的地址,表现出的寻址 能力只有64KB。
8086CPU采用一种在内部用两个16位地址合成的方法来形成一个20位的物理地址。 8086CPU相关部件的逻辑结构如图2.6所示。
在这里插入图片描述
如图2.6所示,当8086CPU要读写内存时:
(1)CPU中的相关部件提供两个16位的地址,一个称为段地址,另一个称为偏移地址
(2)段地址和偏移地址通过内部总线送入一个称为地址加法器的部件;
(3)地址加法器将两个16位地址合成为一个20位的物理地址;
(4)地址加法器通过内部总线将20位物理地址送入输入输出控制电路;
(5)输入输出控制电路将20位物理地址送上地址总线;
(6)20位物理地址被地址总线传送到存储器。

地址加法器采用物理地址=段地址×16+偏移地址的方法用段地址和偏移地址合成物理地址。例如,8086CPU要访问地址为123C8H的内存单元,此时,地址加法器的工作过程如图2 . 7所示(图中数据皆为十六进制表示)。
在这里插入图片描述
由段地址×16引发的讨论
“段地址×16”有一个更为常用的说法是左移4位。计算机中的所有信息都是以二进制的形式存储 的,段地址当然也不例外。机器只能处理二进制信息,“左移4位”中的位,指的是二进制位。
我们看一个例子,一个数据为2H, 二进制形式为10B, 对其进行左移运算:
在这里插入图片描述
观察上面移位次数和各种形式数据的关系,我们可以发现:

(1)一个数据的二进制形式左移1位,相当于该数据乘以2;
(2)一个数据的二进制形式左移N 位,相当于该数据乘以2的N次方;
(3)地址加法器如何完成段地址×16的运算?就是将以二进制形式存放的段地址左移4位。

进一步思考,我们可看出:一个数据的十六进制形式左移1位,相当于乘以16;一个数据的十进制 形式左移1位,相当于乘以10;一个X 进制的数据左移1位,相当于乘以X。

2.7 “段地址×16+偏移地址=物理地址”的本质含义

注意,这里讨论的是 8086CPU 段地址和偏移地址的本质含义,而不是为了解决具体的问题而在本质含义之上引申出来的更高级的逻辑意义。不管以多少种不同的逻辑意义去 看待“段地址×16+偏移地址=物理地址”的寻址模式, 一定要清楚地知道它的本质含义, 这样才能更灵活地利用它来分析、解决问题。如果只拘泥于某一种引申出来的逻辑含义, 而模糊本质含义的话,将从意识上限制对这种寻址功能的灵活应用。
“段地址×16+偏移地址=物理地址”的本质含义是:CPU 在访问内存时,用一个基础地址(段地址×16)和一个相对于基础地址的偏移地址相加,给出内存单元的物理地址。
更一般地说,8086CPU 的这种寻址功能是“基础地址+偏移地址=物理地址”寻址模式的一种具体实现方案。8086CPU中,段地址×16可看作是基础地址。
下面,我们用两个与CPU无关的例子做进一步的比喻说明。 第一个比喻说明“基础地址+偏移地址=物理地址”的思想。
比如说,学校、体育馆、图书馆同在一条笔直的单行路上(参考图2.8),学校位于路 的起点(从路的起点到学校距离是0米)。
在这里插入图片描述
你要去图书馆,问我那里的地址,我可以用两种方式告诉你图书馆的地址:

(1)从学校走2826m 到图书馆。这2826m可以认为是图书馆的物理地址。
(2)从学校走2000m 到体育馆,从体育馆再走826m到图书馆。第一个距离2000m, 是相对于起点的基础地址,第二个距离826m 是相对于基础地址的偏移地址(以基础地址为起点的地址)。

第一种方式是直接给出物理地址2826m, 而第二种方式是用基础地址和偏移地址相加 来得到物理地址的。
第二个比喻进一步说明“段地址×16+偏移地址=物理地址”的思想。
我们为上面的例子加一些限制条件,比如,只能通过纸条来互相通信,你问我图书馆的地址我只能将它写在纸上告诉你。显然,我必须有一张可以容纳4位数据的纸条,才能写下2826这个数据。
在这里插入图片描述

可不巧的是,我没有能容纳4位数据的纸条,仅有两张可以容纳3位数据的纸条。这 样我只能以这种方式告诉你2826这个数据。
在这里插入图片描述
在第一张纸上写上200(段地址),在第二张纸上写上826(偏移地址)。假设我们事前对这种情况又有过相关的约定:你得到这两张纸后,做这样的运算:200(段地址)×10+826(偏移地址)=2826(物理地址)。
8086CPU 就是这样一个只能提供两张3位数据纸条的CPU。

2.8 段 的 概 念

我们注意到,“段地址”这个名称中包含着“段”的概念。这种说法可能对一些学习 者产生了误导,使人误以为内存被划分成了一个一个的段,每一个段有一个段地址。如果我们在一开始形成了这种认识,将影响以后对汇编语言的深入理解和灵活应用。其实,内存并没有分段,段的划分来自于 CPU, 由于8086CPU 用“基础地址(段地 址×16)+偏移地址=物理地址”的方式给出内存单元的物理地址,使得我们可以用分段的方 式来管理内存。如图2.9所示,我们可以认为:地址10000H~ 100FFH 的内存单元组成一 个段,该段的起始地址(基础地址)为10000H, 段地址为1000H, 大小为100H;我们也可 以认为地址10000H~ 1007FH、10080H~100FFH 的内存单元组成两个段,它们的起始地址 (基础地址)为:10000H和10080H, 段地址为:1000H和1008H, 大小都为80H。
在这里插入图片描述
以后,在编程时可以根据需要,将若干地址连续的内存单元看作一个段,用段地 址×16定位段的起始地址(基础地址),用偏移地址定位段中的内存单元。有两点需要注 意:段地址×16必然是16的倍数,所以一个段的起始地址也一定是16的倍数;偏移地址 为16位,16位地址的寻址能力为64KB, 所以一个段的长度最大为64KB。
在这里插入图片描述
在这里插入图片描述

检测点2.2

(1)给定段地址为0001H, 仅通过变化偏移地址寻址,CPU 的寻址范围为(0010H )到(1000FH)

解题思路:
物理地址=SA16+EA
EA的变化范围为0H~FFFFH
物理地址范围为(SA
16+0h)~(SA16+FFFFH)
现在SA=0001H,那么寻址范围为
(0001H
16+0H)~ (0001H*16+FFFFH) =0010h~1000FH

(2)有一数据存放在内存20000H 单元中,现给定段地址为 SA, 若想用偏移地址寻到此单元。则SA 应满足的条件是:最小为(1001H),最大为(2000H)。

解题思路:
20000H=SA*16+EA
SA=(20000H-EA)/16=2000H-EA/16
EA取最大值时,SA=2000H-FFFFH/16=1001H,SA为最小值
EA取最小值时,SA=2000H-0h/16=2000H,SA为最大值

提示,反过来思考一下,当段地址给定为多少,CPU无论怎么变化偏移地址都无法寻到20000H单元?

当段地址给定为 1001H 以下和2000H以上,CPU无论怎么变化偏移地址都无法寻到20000H单元。

2.9 段 寄 存 器

我们前面讲到,8086CPU在访问内存时要由相关部件提供内存单元的段地址和偏移地址,送入地址加法器合成物理地址。这里,需要看一下,是什么部件提供段地址。段地址在8086CPU 的段寄存器中存放。8086CPU有4个段寄存器:CS 、DS 、SS 、ES 。 当 8086CPU要访问内存时由这4个段寄存器提供内存单元的段地址。本章中只看一下CS。

2.10 CS 和IP

CS 和 IP 是8086CPU 中两个最关键的寄存器,它们指示了CPU当前要读取指令的地址。CS为代码段寄存器,IP为指令指针寄存器,从名称上我们可以看出它们和指令的关系。
在8086PC 机中,任意时刻,设CS 中的内容为M,IP中的内容为N,8086CPU将从内存M×16+N 单元开始,读取一条指令并执行。
也可以这样表述:8086机中,任意时刻,CPU将CS:IP 指向的内容当作指令执行。图2.10展示了8086CPU 读取、执行指令的工作原理(图中只包括了和所要说明的问题
密切相关的部件,图中数字都为十六进制)。
在这里插入图片描述
(1)8086CPU 当前状态:CS 中的内容为2000H,IP 中的内容为0000H;
(2)内存20000H~20009H单元存放着可执行的机器码;
(3)内存20000H~20009H单元中存放的机器码对应的汇编指令如下。
地址:20000H~ 20002H, 内容:B82301, 长度:3Byte, 对应汇编指令:mov ax,0123H 地址:20003H~20005H, 内容:BB 0300,长度:3Byte,对应汇编指令:mov bx,0003H 地址:20006H~20007H,内容:89 D8, 长度:2Byte, 对应汇编指令:mov ax,bx
地址:20008H~20009H,内容:01 D8, 长度:2Byte, 对应汇编指令:add ax,bx

下面的一组图(图2 . 11~图2 . 19),以图2 . 10描述的情况为初始状态,展示了8086CPU
读取、执行一条指令的过程。注意每幅图中发生的变化(下面对8086CPU 的描述,是在逻 辑结构、宏观过程的层面上进行的,目的是使读者对 CPU 工作原理有一个清晰、直观的 认识,为汇编语言的学习打下基础。其中隐蔽了CPU 的物理结构以及具体的工作细节)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
通过上面的过程展示,8086CPU 的工作过程可以简要描述如下。

(1) 从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器;
(2)IP=IP+ 所读取指令的长度,从而指向下一条指令;
(3)执行指令。转到步骤(1),重复这个过程;

在8086CPU 加电启动或复位后(即 CPU 刚开始工作时)CS 和 IP 被设置为 CS=FFFFH,IP=0000H, 即在8086PC机刚启动时,CPU 从内存FFFFOH 单元中读取指令 执行,FFFFOH单元中的指令是8086PC机开机后执行的第一条指令。
现在,我们更清楚了CS 和 IP 的重要性,它们的内容提供了CPU 要执行指令的地址。
我们在第1章中讲过,在内存中,指令和数据没有任何区别,都是二进制信息,CPU 在工作的时候把有的信息看作指令,有的信息看作数据。现在,如果提出一个问题:CPU 根据什么将内存中的信息看作指令?如何回答?我们可以说,CPU 将CS:IP 指向的内存单 元中的内容看作指令,因为,在任何时候,CPU 将 CS、IP 中的内容当作指令的段地址和 偏移地址,用它们合成指令的物理地址,到内存中读取指令码,执行。如果说,内存中的 一段信息曾被CPU 执行过的话,那么,它所在的内存单元必然被CS:IP指向过。

2.11 修改CS、IP的指令

在 CPU中,程序员能够用指令读写的部件只有寄存器,程序员可以通过改变寄存器中的内容实现对CPU的控制。CPU 从何处执行指令是由CS 、IP 中的内容决定的,程序员可以通过改变CS 、IP中的内容来控制CPU 执行目标指令。
我们如何改变CS 、IP 的值呢?显然,8086CPU 必须提供相应的指令。我们如何修改 AX中的值?可以用mov 指令,如mov ax,123将 ax 中的值设为123,显然,我们也可以 用同样的方法设置其他寄存器的值,如mov bx,123,mov cx,123,mov dx,123等。其实, 8086CPU大部分寄存器的值,都可以用mov 指令来改变,mov 指令被称为传送指令。
但是,mov 指令不能用于设置CS 、IP的值,原因很简单,因为8086CPU没有提供这样的功能。8086CPU 为 CS 、IP 提供了另外的指令来改变它们的值。能够改变 CS 、IP 的内容的指令被统称为转移指令(我们以后会深入研究)。我们现在介绍一个最简单的可以修改CS 、IP的指令:jmp 指令。
若想同时修改CS、IP 的内容,可用形如“jmp 段地址:偏移地址”的指令完成,如
jmp 2AE3:3,执行后:CS=2AE3H,IP=0003H,CPU 将从2AE33H处读取指令。
jmp 3:0B16, 执行后:CS=0003H,IP=0B16H,CPU 将从00B46H处读取指令。

“jmp 段地址:偏移地址”指令的功能为:用指令中给出的段地址修改 CS, 偏移地址 修改IP。
若想仅修改IP 的内容,可用形如 “jmp 某一合法寄存器”的指令完成,如

jmp ax
指令执行前:ax=1000H,CS=2000H,IP=0003H
指令执行后:ax=1000H,CS=2000H,IP=1000H
jmp bx
指令执行前: bx=0B16H,CS=2000H,IP=0003H
指令执行后: bx=0B16H,CS=2000H,IP=0B16H
“jmp 某一合法寄存器”指令的功能为:用寄存器中的值修改IP。 jmp ax,在含义上好似:
mov IP,ax。
注意,我们在适当的时候,会用已知的汇编指令的语法来描述新学的汇编指令的功 能。采用一种“用汇编解释汇编”的方法来使读者更好地理解汇编指令的功能,这样做有 助于读者进行知识的相互融会。要强调的是,我们是用“已知的汇编指令的语法”进行描 述,并不是用“已知的汇编指令”来描述,比如,我们用mov IP,ax来描述jmp ax,并不 是说真有mov IP,ax这样的指令,而是用mov 指令的语法来说明jmp 指令的功能。我们可 以用同样的方法描述 jmp 3:01B6 的功能:jmp 3:01B6 在含义上好似 mov CS,3 mov IP,01B6。

问题 2.3

内存中存放的机器码和对应的汇编指令情况如图2.27 所示,设 CPU 初始状态: CS=2000H,IP=0000H, 请写出指令执行序列。思考后看分析。
在这里插入图片描述
分析:
在这里插入图片描述
在这里插入图片描述

2.12 代码段

前面讲过,对于8086PC 机,在编程时,可以根据需要,将一组内存单元定义为一个 段。我们可以将长度为 N(N≤64KB)的一组代码,存在一组地址连续、起始地址为16的 倍数的内存单元中,我们可以认为,这段内存是用来存放代码的,从而定义了一个代码 段。比如,将:
在这里插入图片描述
这段长度为10个字节的指令,存放在123BOH~123B9H 的一组内存单元中,我们就 可以认为,123BOH~123B9H 这段内存是用来存放代码的,是一个代码段,它的段地址为 123BH, 长度为10个字节。
如何使得代码段中的指令被执行呢?将一段内存当作代码段,仅仅是我们在编程时的一种安排,CPU 并不会由于这种安排,就自动地将我们定义的代码段中的指令当作指令来执行。CPU 只认被 CS:IP 指向的内存单元中的内容为指令。所以,要让 CPU 执行我们放在代码段中的指令,必须要将 CS:IP 指向所定义的代码段中的第一条指令的首地址。对于上面的例子,我们将一段代码存放在123BOH~123B9H 内存单元中,将其定义为代码 段,如果要让这段代码得到执行,可设CS=123BH 、IP=0000H。
在这里插入图片描述

检测点2.3

下面的3条指令执行后,CPU 几次修改IP? 都是在什么时候?最后IP 中的值是多少?
mov ax,bx
sub ax,ax
jmp ax

答案思路:
一共修改四次
第一次:读取mov ax,bx之后
第二次:读取sub ax,ax之后
第三次:读取jmp ax之后
第四次:执行jmp ax修改IP 最后IP的值为0000H,因为最后ax中的值为0000H,所以IP中的值也为0000H

实验1 查看CPU 和内存,用机器指令和汇编指令编程

1 .预备知识:Debug 的使用
我们以后所有的实验中,都将用到Debug 程序,首先学习一下它的主要用法。
(1)、什么是Debug?
**Debug 是 DOS、Windows 都提供的实模式(8086方式)程序的调试工具。**使用它,可 以查看CPU 各种寄存器中的内容、内存的情况和在机器码级跟踪程序的运行。
(2)、我们用到的Debug 功能。
● 用Debug 的 R 命令查看、改变CPU 寄存器的内容;
● 用Debug 的 D 命令查看内存中的内容;
● 用Debug 的 E 命令改写内存中的内容;
● 用Debug 的 U 命令将内存中的机器指令翻译成汇编指令;
● 用Debug 的 T 命令执行一条机器指令;
● 用Debug 的 A 命令以汇编指令的格式在内存中写入一条机器指令。

Debug 的命令比较多,共有20多个,但这6个命令是和汇编学习密切相关的。在以后的实验中,我们还会用到一个P命令。
(3)进入 Debug。
Debug 是 在DOS 方式下使用的程序。我们在进入 Debug 前,应先进入到DOS 方式。 用以下方式可以进入DOS。
① 重新启动计算机,进入DOS方式,此时进入的是实模式的DOS。
② 在 Windows 中进入 DOS 方式,此时进入的是虚拟8086模式的DOS。
下面说明在 Windows 2000 中进入 Debug 的一种方法,在其它 Windows 系统中进入 的方法与此类似。
选择【开始】菜单中的【运行】命令,如图2.28 所示,打开【运行】对话框,如 图2.29所示,在文本框中输入“command” 后,单击【确定】按钮。
在这里插入图片描述
进入 DOS 方式后,如果显示为窗口方式,可以按下Alt+Enter 组合键将窗口变为全屏 方式。然后运行 Debug 程序,如图2.30所示。这个程序在不同的 Windows 系统中所在的 路径不尽相同,在 Windows 2000 中通常在 c:\winnt\system 下。由于系统指定了搜索路径,所以在任何一个路径中都可以运行。
在这里插入图片描述
( 4 ) 用R命令查看、改变CPU 寄存器的内容。
我们已经知道了 AX、BX、CX、DX、CS、IP 这6个寄存器,现在看一下它们之中 的内容,如图2.31 所示。其他寄存器如 SP、BP、SI、DI、DS、ES、SS、 标志寄存器等,我们先不予理会。
在这里插入图片描述
注意 CS 和 IP 的值 ,CS=0CA2,IP=0100, 也就是说,内存0CA2:0100 处的指令为 CPU当前要读取、执行的指令。在所有寄存器的下方,Debug 还列出了CS:IP 所指向的内 存单元处所存放的机器码,并将它翻译为汇编指令。可以看到,CS:IP 所指向的内存单元 为 0CA2:0100, 此处存放的机器码为027548,对应的汇编指令为ADD DH,[DI+48](这条 指令的含义我们还不知道,先不必深究)。
Debug 输出的右下角还有一个信息:“DS:0048=0”, 我们以后会进行说明,这里同
样不必深究。
还可以用R命令来改变寄存器中的内容,如图2.32所示。
在这里插入图片描述
若要修改一个寄存器中的值,比如 AX 中的值,可用 R 命令后加寄存器名来进行, 输入 “r ax” 后按 Enter 键,将出现“:”作为输入提示,在后面输入要写入的数据后按 Enter 键,即完成了对AX 中内容的修改。若想看一下修改的结果,可再用R 命令查看, 如图2.32所示。
在图2.33中,一进入Debug, 用 R 命令查看,CS:IP 指向0B39:0100, 此处存放的机器码为40,对应的汇编指令是INC AX;
在这里插入图片描述
接着,用R命令将 IP 修改为200,则CS:IP 指向0B39:0200, 此处存放的机器码为5B, 对应的汇编指令是POP BX;
接着,用 R 命令将 CS 修改为 ff00, 则 CS:IP 指 向 ff00:0200, 此处存放的机器码为 51,对应的汇编指令是PUSH CX。
( 5 ) 用Debug 的 D 命令查看内存中的内容。
用 Debug 的 D 命令,可以查看内存中的内容,D 命令的格式较多,这里只介绍在本 次实验中用到的格式。
如果我们想知道内存10000H 处的内容,可以用 “d 段地址:偏移地址”的格式来查 看,如图2.34所示。
在这里插入图片描述
要查看内存10000H 处的内容,首先将这个地址表示为段地址:偏移地址的格式,可以是1000:0,然后用“d 1000:0” 列出1000:0处的内容。
使用“d 段地址:偏移地址”的格式,Debug 将列出从指定内存单元开始的128个内 存单元的内容。图2.34中,在使用 d 1000:0后 ,Debug 列出了1000:0~1000:7F 中的内容。
使用D 命 令 ,Debug 将输出3部分内容(如图2.34所示)。
① 中间是从指定地址开始的128个内存单元的内容,用十六进制的格式输出,每行 的输出从16的整数倍的地址开始,最多输出16个单元的内容。从图中,我们可以知道, 内存1000:0单元中的内容是72H, 内存1000:1单元中的内容是64H, 内存1000:0~ 1000:F 中的内容都在第一行;内存1000:10中的内容是6DH,内存1000:11处的内容是61H,内 存1000:10~ 1000:1F 中的内容都在第二行。注意在每行的中间有一个“-”,它将每行的输 出分为两部分,这样便于查看。比如,要想从图中找出1000:6B 单元中内容,可以从 1000:60找到行,“-”前面是1000:60~ 1000:67的8个单元,后面是1000:68~1000:6F 的 8 个单元,这样我们就可以从1000:68单元向后数3个单元,找到1000:6B 单元,可以看 到,1000:6B 中的内容为67H。
② 左边是每行的起始地址。
③ 右边是每个内存单元中的数据对应的可显示的 ASCII 码字符。比如,内存单元 1000:0、1000:1、1000:2中存放的数据是72H 、64H 、73H, 它对应的 ASCII 字符分别是 “r” 、“d” 、“s”; 内存单元1000:36中的数据是0AH, 它没有对应可显示的 ASCII 字符,Debug 就用“.”来代替。
注意,我们看到的内存中的内容,在不同的计算机中是不一样的,也可能每次用 Debug 看到的内容都不相同,因为我们用Debug 看到的都是原来就在内存中的内容,这些 内容受随时都有可能变化的系统环境的影响。当然,我们也可以改变内存、寄存器中的 内容。
我们使用d 1000:9 查看1000:9处的内容,Debug 将怎样输出呢?如图2.35所示。
在这里插入图片描述
Debug 从1000:9开始显示, 一直到1000:88,一共是128个字节。第一行中的 1000:0~1000:8单元中的内容不显示。
在一进入 Debug 后,用D 命令直接查看,将列出 Debug 预设的地址处的内容,如 图2.36所示。
在这里插入图片描述
在使用“d 段地址:偏移地址”之后,接着使用D 命令,可列出后续的内容,如图2.37 所示。
也可以指定 D 命令的查看范围,此时采用 “d 段地址:起始偏移地址结尾偏移地址” 的格式。比如要看1000:0~1000:9中的内容,可以用 “d 1000:09” 实现,如图2.38所示。
在这里插入图片描述
如果我们就想查看内存单元10000H 中的内容,可以用图2.39 中的任何一种方法看 到,因为图中的所有“段地址:偏移地址”都表示了10000H 这一物理地址。
在这里插入图片描述
( 6 ) 用Debug 的 E 命令改写内存中的内容。
可以使用E 命令来改写内存中的内容,比如,要将内存1000:0~1000:9单元中的内容 分别写为0、1、2、3、4、5、6、7、8、9,可以用 “e 起始地址数据数据数 据 …… ”的格式来进行,如图2.40所示。
在这里插入图片描述
图2.40中,先用D 命令查看1000:0~ 1000:f单元的内容,再用E 命令修改从1000:0 开始的10个单元的内容,最后用D 命令查看1000:0~1000:f 中内容的变化。
也可以采用提问的方式来一个一个地改写内存中的内容,如图2.41所示。
在这里插入图片描述
如图2.41 中,可以用 E 命令以提问的方式来逐个地修改从某一地址开始的内存单元 中的内容,以从1000:10单元开始为例,步骤如下。
① 输入e 1000:10,按Enter键。
② Debug 显示起始地址1000:0010,和第一单元(即1000:0010单元)的原始内容: 6D, 然后光标停在“. ”的后面提示输入想要写入的数据,此时可以有两个选择:其一为 输入数据(我们输入的是0),然后按空格键,即用输入的数据改写当前的内存单元;其二 为不输入数据,直接按空格键,则不对当前内存单元进行改写。
③ 当前单元处理完成后(不论是改写或没有改写,只要按了空格键,就表示处理完 成 ) ,Debug 将接着显示下一个内存单元的原始内容,并提示进行修改,读者可以用同样 的方法处理。
④ 所有希望改写的内存单元改写完毕后,按Enter 键 ,E 命令操作结束。
可以用E 命令向内存中写入字符,比如,用E 命令从内存1000:0开始写入数值1、 字符 “a” 、 数值2、字符 “b” 、 数值3、字符“c”, 可采用图2.42中所示的方法进行。
在这里插入图片描述
从图2.42中可以看出,Debug 对 E 命令的执行结果是,向1000:0、1000:2、1000:4单 元中写入数值1、2、3,向1000:1、1000:3、1000:5单元中写入字符“a”、“b”、
“c” 的 ASCII 码值:61H 、62H 、63H。
也可以用E 命令向内存中写入字符串,比如,用E 命令从内存1000:0开始写入:数 值1、字符串“a+b”、 数值2、字符串 “c++” 、 字符3、字符串“IBM”, 如图2.43 所示。
( 7 ) 用E 命令向内存中写入机器码,用U 命令查看内存中机器码的含义,用T 命令 执行内存中的机器码。
在这里插入图片描述
如何向内存中写入机器码呢?我们知道,机器码也是数据,当然可以用E 命令将机器 码写入内存。比如我们要从内存1000:0单元开始写入这样一段机器码:
在这里插入图片描述
如何查看写入的或内存中原有的机器码所对应的汇编指令呢?可以使用 U 命令。比 如可以用U 命令将从1000:0开始的内存单元中的内容翻译为汇编指令,并显示出来,如 图2.45所示。
在这里插入图片描述
图2 .45中,首先用E 命令向从1000:0开始的内存单元中写入了8个字节的机器码;
然后用D 命令查看内存1000:0~1000:1f 中的数据(从数据的角度看一下写入的内容);最后 用 U 命令查看从1000:0开始的内存单元中的机器指令和它们所对应的汇编指令。
U 命令的显示输出分为3部分,每一条机器指令的地址、机器指令、机器指令所对应 的汇编指令。我们可以看到:
1000:0处存放的是写入的机器码 b80100 所组成的机器指令,对应的汇编指令是
mov ax,1;
1000:3处存放的是写入的机器码 b90200 所组成的机器指令;对应的汇编指令是
mov cx,2;
1000:6处存放的是写入的机器码01 c8 所组成的机器指令;对应的汇编指令是 add ax,CX;
1000:8处存放的是内存中的机器码034942所组成的机器指令;对应的汇编指令是 add cx,[bx+di+42]。
由此,我们可以再一次看到内存中的数据和代码没有任何区别,关键在于如何解释。
如何执行我们写入的机器指令呢?使用 Debug 的 T 命令可以执行一条或多条指令, 简单地使用T 命令,可以执行CS:IP 指向的指令,如图2.46所示。
在这里插入图片描述
图2.46中,首先用E 命令向从1000:0开始的内存单元中写入了8个字节的机器码; 然后用R 命令查看 CPU 中寄存器的状态,可以看到,CS=0b39H 、IP=0100H, 指向内存 0b39:0100; 若要用T 命令控制 CPU 执行我们写到1000:0的指令,必须先让 CS:IP 指 向 1000:0;接着用R 命令修改CS 、IP 中的内容,使CS:IP 指向1000:0。
完成上面的步骤后,就可以使用T 命令来执行我们写入的指令了(此时,CS:IP 指向我 们的指令所在的内存单元)。执行 T 命 令 后 ,CPU 执 行 CS:IP 指向的指令,则1000:0处
的指令 b80100(mov ax,0001)得到执行,指令执行后,Debug 显示输出CPU 中寄存器的状态。
注意,指令执行后,AX 中的内容被改写为1 ,IP 改变为IP+3(因为mov ax,0001的 指
令长度为3个字节),CS:IP 指向下一条指令。
接着图2.46,我们可以继续使用T 命令执行下面的指令,如图2.47所示。
在这里插入图片描述
在图2.47中,用T 命令继续执行后面的指令,注意每条指令执行后,CPU 相关寄存 器内容的变化。
( 8 ) 用Debug 的 A 命令以汇编指令的形式在内存中写入机器指令。
前面我们使用E 命令写入机器指令,这样做很不方便,最好能直接以汇编指令的形式 写入指令。为此,Debug 提供了A 命令。A 命令的使用方法如图2.48所示。
在这里插入图片描述
图2.48中,首先用A 命令,以汇编语言向从1000:0开始的内存单元中写入了几条指 令,然后用D 命令查看A 命令的执行结果。可以看到,在使用A 命令写入指令时,我们 输入的是汇编指令,Debug 将这些汇编指令翻译为对应的机器指令,将它们的机器码写入 内存。
使 用A 命令写入汇编指令时,在给出的起始地址后直接按Enter键表示操作结束。 如图2.49中,简单地用A 命令,从一个预设的地址开始输入指令。
在这里插入图片描述
在这里插入图片描述
2. 实验任务
(1)使用Debug, 将下面的程序段写入内存,逐条执行,观察每条指令执行后CPU 中 相关寄存器中内容的变化。
在这里插入图片描述
提示,可用 E 命令和 A 命令以两种方式将指令写入内存。注意用 T 命令执行时, CS:IP 的指向。
(2)将下面3条指令写入从2000:0开始的内存单元中,利用这3条指令计算2的8次方。
mov ax,1
add ax,ax
jmp 2000:0003
(3)查看内存中的内容。
PC 机主板上的 ROM 中写有一个生产日期,在内存 FFF00H~FFFFFH 的某几个单元 中,请找到这个生产日期并试图改变它。
提示,如果读者对实验的结果感到疑惑,请仔细阅读第1章中的1.15节。
(4)向内存从B8100H开始的单元中填写数据,如: -e B810:00000101020203030404
请读者先填写不同的数据,观察产生的现象;再改变填写的地址,观察产生的现象。 提示,如果读者对实验的结果感到疑惑,请仔细阅读第1章中的1.15节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值