一个典型的CPU由运算器、控制器、寄存器(CPU工作原理)等器件构成。内部总线实现 CPU 内部各个器件之间的联系,外部总线实现CPU和主板其他器件的联系。
在CPU中(下列重要内容)
对于汇编程序员来说,CPU中的主要部件是寄存器。寄存器是CPU中与程序员可以用指令读写的部件。
不同的CPU,寄存器的个数、结构是不相同的。
8086CPU的所有寄存器都是16位的。
通用寄存器:AX、BX、CX、DX.
一个16位寄存器的逻辑结构:
8086CPU的上一代CPU中的寄存器都是8位的。
为了保证兼容,8086的16bit寄存器可分为两个独立8bit寄存器
一个8bit寄存器所能存储的数据的最大值为255。
一个字可以存在一个16位寄存器中,这个字的高位字节和低位字节自然就存在这个寄存器的高8位寄存器和低8位寄存器中。一个word数据20000的例子,如图:
其中,信息本身是二进制数据,而不是十进制数据,别搞混。
再强调一遍,信息本身就是二进制数据。
汇编指令 | 控制CPU完成的操作 | 用高级语言的语法描述 |
---|---|---|
mov a,b | 将b的数据送入a中 | a = b |
add a,b | 将a+b的数据送入a中 | a = a+b |
a代表寄存器,b代表数据或者寄存器。
问题2.1:指令执行后AX中的数据是多少?
mov ax,4E20H ;AX=4E20H
add ax,1406H ;AX=6226H
mov bx,2000H ;BX=2000H
add ax,bx ;AX=8226H
mov bx,ax ;BX=8226H
add ax,bx ;AX=044CH
指令执行后AX中的数据为 004CH。
问题2.2:指令执行后AX中的数据是多少?
MOV AX,001AH ;AX=001AH
MOV BX,0026H ;BX=0026H
ADD AL,BL ;AX=0040H
ADD AH,BL ;AX=2640H
ADD BH,AL ;BX=4026H
MOV AH,0 ;AX=0040H
MOV AL,85H ;AX=00C5H
ADD AL,93H ;AX=0158H
指令执行后AX中的数据为 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=D806H
ADD AL,AL ;AX=D80CH
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
所有的内存单元构成的存储空间是一个一维的线性空间,每一个内存单元在这个空间中都有一个唯一的地址,称为物理地址。
CPU通过地址总线送入存储器的,必须是一个内存单元的物理地址。
8080、8085是8位机,8086是16位机。
16位结构的CPU具有的结构性质
8086是16位结构的CPU,也就是说,能够一次性处理、传输、暂时存储的信息的最大长度是16位的。
8086CPU有20位地址总线,可以传送20位地址,达到1MB的寻址能力。8086CPU是16位结构,在内部一次性处理、传输、暂时存储的地址为16位。从表面上看CPU只能寻址64KB。
8086CPU采用一种内部用两个16位地址合成的方法来形成一个20位的物理地址。
地址加法器采用物理地址 = 段地址x16+偏移地址
地址加法器工作过程(图中数据皆用十六进制表示):
由段地址x16引发的讨论
“段地址x16”有一个更为常用的说法是左移4位。
一个例子:
左移位数 | 二进制 | 十六进制 | 十进制 |
---|---|---|---|
0 | 10B | 2H | 2 |
1 | 100B | 4H | 4 |
2 | 1000B | 8H | 8 |
3 | 10000B | 10H | 16 |
4 | 100000B | 20H | 32 |
观察上面移位数和各种进制数据的关系,我们可以发现:
不难得出,一个数据的十六进制形式左移1位,相当于乘以16。
本质含义是:CPU在访问内存时,用一个基础地址(段地址x16)和偏移地址相加,给出内存单元的物理地址。(可以理解为从基础地址出发+偏移量 = 你要去的目的地)
实际上,内存并没有分段,段的划分来自于CPU。
我们可以在逻辑上将内存“分段”,如图
编程时,可以根据需要,将若干个地址连续的内存单元看做一个段(注意:段地址一定是16的倍数,一个段的长度最大为64KB)。
(1)观察下面的地址,你有什么发现?
物理地址 | 段地址 | 偏移地址 |
---|---|---|
21F60H | 2000H | 1F60H |
21F60H | 2100H | 0F60H |
21F60H | 21F0H | 0060H |
21F60H | 21F6H | 0000H |
21F60H | 1F00H | 2F60H |
结论:CPU可以用不同的段地址(SA)和偏移地址(EA)形成同一个物理地址。
(2)如果给定一个段地址,仅通过变化偏移地址来寻址最多可定位多少个内存单元?
结论:如果给定一个段地址,仅通过变化偏移地址来寻址最多可定位64KB个内存单元。
数据在 21F60H 内存单元中,CPU 表示 形式为 2000:1F60 单元中。
检测点 2.2
(1)给定段地址为0001H,仅通过变化偏移地址寻址,CPU的寻址范围为 0001:0000 到 0001:FFFF 。
(2)有一数据存放在内存 20000H 单元中,现给定段地址为SA,若想用偏移地址寻到此单元。则SA应满足的条件是:最小为 1001H ,最大为 2000H 。
8086CPU有4个段寄存器:CS、DS、SS、ES。
CS为段寄存器,IP为指令指针寄存器。
一个例子(展示CPU执行指令原理,比较长)
从下面一系列的图展示过程。
下面,首先初始化
将CS、IP的内容送入地址加法器
地址加法器将物理地址送入输入输出控制电路
输入输出控制电路将物理地址送到地址总线
内存中存放的机器指令被送入CPU
输入输出控制电路将指令送入指令缓冲器
IP中的值自动增加,以使CPU可以读取下一条指令
执行控制器执行指令
指令被执行后,AX内容发生了变化
同上过程,读取下一条指令
再读取下一条!
又读取一条!
到此为止,4条指令执行历程结束!
通过上面的例子,8086CPU的工作过程可以简要描述如下。
8086CPU加电启动或复位后,CS和IP被设置为CS=FFFFH,IP=0000H(重要,也就是说FFFF0H是8086PC机开机后执行的第一条指令)。
CPU从何处执行指令?
显然,由CS、IP中的内容决定的。
我们如何改变CS、IP的值呢?
8086CPU提供相应的指令。
我们在初步了解汇编指令的时候使用过 mov 指令,那我们能够使用mov指令来修改CS、IP的内容吗?
答案是不可以的,因为8086CPU的mov指令没有提供这样的功能。
我们将用最简单的可以修改CS、IP的指令: jmp 指令。
使用方式形如: jmp 段地址 : 偏移地址。这个指令的功能是修改CS和IP的值。
还有另一种修改方式: jmp 某一合法寄存器。这个指令的功能是修改IP的值。
对于8086PC机,在编程时,可以将一组内存单元定义为一个段。我们可以将长度为 N(N<=64KB) 的一组代码,存在一组地址连续、起始地址为16的倍数的内存单元中。我们可以认为,这段内存是用来存放代码的,从而定义了代码段。
例如:
MOV AX,0000H ;(B8 00 00)
ADD AX,0123H ;(05 23 01)
MOV BX,AX ;(8B D8)
JMP BX ;(FF E3)
这段长度为10个字节的指令,存放在123B0H~123B9H的一组内存单元中,我们就可以认为,123B0H~123B9H这段内存时用来存放代码的,是一个代码段,它的段地址为123BH,长度为10个字节。
这段代码如何被执行呢?
显然,只要将CS = 123BH,IP = 0000H即可。
检测点 2.3
下面的3条指令执行后,CPU几次修改IP?都是在什么时候?最后IP中的值是多少?
MOV AX,BX ;AX=BX
SUB AX,AX ;AX=0
JMP AX ;IP=0
答案:一次修改 IP,在 JMP AX 的时候,最后 IP 为0000H。
(1)什么是Debug?
Debug是 DOS、Windows 都提供的实模式(8086 方式)程序的调试工具。使用它,可以查看CPU各种寄存器中的内容、内存的情况和在机器码级跟踪程序的运行。
(2)我们用到的Debug功能。
Debug的命令比较多,共有20多个,但这6个命令是和汇编学习密切相关的。在以后还有用到一个P命令。
(3)进入Debug。
Debug是在DOS方式下使用的程序。我们在进入Debug前,应先进入到DOS方式。
用以下方式可以进入DOS。
在这里,我用的是 实验楼 的环境。这样降低了学习成本。
(4)用 R命令 查看、改变CPU寄存器的内容。
如下,成功显示了寄存器的内容
如下,成功修改了AX寄存器的内容
我们注意到 DS:0000 = 0 以后的章节会介绍。还看到了最下面一行出现的 TEST …的汇编指令。
可以用 R命令 修改CS和IP的内容。
(5)用Debug的 D命令 查看内存中的内容。
用Debug的D命令,可以查看内存中的内容,D命令的格式比较多。
可以用:D 段地址 : 偏移地址,如下。
使用D命令,Debug将输出3部分内容,如下。
(6)用 Debug 的 E命令 改写内存中的内容。
E 10 ;修改当前数据段10H号单元内容
E ES:100 ;修改附加段100H号单元内容
D ES:100 ;查看一下100H单元的内容是否修改了
(7)用E命令向内存中写入机器码,用U命令查看内存中机器码的含义,用T命令执行内容从中的机器码。
E命令其他用法
编写一个如下代码的汇编程序
MOV AX,0001 ;B80100
MOV CX,0002 ;B90200
ADD AX,CX ;01C8
编写代码如下:
修改CS、IP寄存器
使用T命令执行汇编指令
观察以上所有图片寄存器的变化(眼睛都花了)。
(8)用Debug的 A命令 以汇编指令形式在内存中写入机器指令。
如图。
从最后一行可以看出,内存1000H段的内容已经被修改成功。
最后,在一张命令表格吧。
命令 | 说明 |
---|---|
R命令 | 查看、修改CPU |
D命令 | 查看内存中的内容 |
E命令 | 修改内存中的内容 |
U命令 | 将内存中的内容解释为机器指令和对应的汇编指令 |
T命令 | 执行CS:IP指向的内存单元处的指令 |
A命令 | 以汇编指令的形式向内存中写入指令 |
(1)使用 Debug,将下面的程序段写入内存,逐条执行,观察每条指令执行后,CPU中相关寄存器中内容的变化。
汇编指令 机器码
MOV AX,4E20H ;B8 20 4E
ADD AX,1416H ;05 16 14
MOV BX,2000H ;BB 00 20
ADD AX,BX ;01 D8
MOV BX,AX ;89 C3
ADD AX,BX ;01 D8
MOV AX,001AH ;B8 1A 00
MOV BX,0026H ;BB 26 00
ADD AL,BL ;00 D8
ADD AH,BL ;00 DC
ADD BH,AL ;00 C7
MOV AH,0 ;B4 00
ADD AL,BL ;00 D8
ADD AL,9CH ;04 9C
Debug下用A命令输入汇编指令:
再用D命令查看是否输入汇编指令到内存:
在使用R命令查看和修改CS、IP的值,修改成代码段的开始位置:
最后,T命令执行,自行实验观察步骤。
整个过程只要观察AX和BX就好,最后AX=0002,BX=4026。
(2)将下面3条指令写入从 2000:0 开始的内存单元中,利用这3条指令计算2的8次方。
mov ax,1
add ax,ax
jmp 2000:0003
分析,我们知道 mov ax,1 占了3个内存单元。所以我们代码段的开始位置为2000H。
如下(要算2的8次方,add ax,ax,这个跟算法的快速幂一样,我们只要add ax,ax四次就行,因为ax初始化为1):
(3)查看内存中的内容。
PC机主板上的ROM中写有一个生产日期,在内存 FFF00H~FFFFFH 的某几个单元中,请找到这个生产日期,在内 FFF00H~FFFFFH 的某几个单元中,请找出这个生产日期并试图改变它。
显然,用D FFF0 : 0000 L 100就可以得出答案,如下。
(4)向内存从B8100H开始的单元中填写数据,如:
-e B8100:0000 01 01 02 02 03 03 04 04
发现:数据没有被改变,为什么呢?
原因:我们阅读第一章的时候,我们知道我们将所有内存当做一个逻辑连续的内存。而B810H地址段是ROM(只读)的内存段。