上一节我们了解了通用寄存器,用几条汇编指令做了例子,了解了物理地址,以及段的概念,接下来我们根据”段”的内容,更加深入一些
上一节链接
从0开始进入汇编的世界之每天理解一篇汇编帖-第二节
我们前面讲到,8086CPU在访问内存时,需要由相关部件提供内存单元的段地址和偏移地址,送入地址加法器中合成物理地址。
段地址在CPU的段寄存器中存放,段寄存器就是提供段地址的。8086CPU有4个段寄存器:CS(代码段寄存器)、DS(数据段寄存器)、SS(堆栈段寄存器)、ES(附加段寄存器)
当CPU要访问内存时,就由这4个段寄存器来提供内存单元的段地址,现在我们主要来看一下CS(代码段寄存器)
CS个IP是8086CPU中最关键的2个寄存器,它们指示了CPU当前要读取指令的地址。
CS为代码段寄存器,IP为指令指针寄存器。
在任意时刻,设CS中的内容为M,IP中的内容为N,CPU将从内存 M x 16 + N 单元开始,读取一条指令并执行。
我们也可以说,任意时刻,CPU将CS:IP指向的内容当做指令执行
下图展示了CPU读取、执行指令的工作原理(图中的数字都为16进制)
从上图可见
(1)CPU当前状态:CS = 2000,IP = 0000;
(2)内存20000 - 20009单元中存放着可执行的机器码
(3)内存20000 - 20009单元中存放的机器码对应汇编指令如下
地址:20000 - 20002,内容:B8 23 01,长度:3字节,对应汇编指令:mov ax,0123
地址:20003 - 20005,内容:BB 03 00,长度:3字节,对应汇编指令:mov bx,0003
地址:20006 - 20007,内容:89 D8,长度:2字节,对应汇编指令:mov ax,bx
地址:20008 - 20009,内容:01 D8,长度:2字节,对应汇编指令:add ax,bx
将上图做解释,首先CS中的2000和IP中的0000进入地址加法器,地址加法器将段地址2000左移一位,把CS和IP合成为一个20位的物理地址,变为20000,所以物理地址为20000,并将其送入输入输出电路,接着由20为的地址总线输出,索引到物理地址为20000的内存地址,此时这是一条指令”mov ax,0123”,占用3个字节,然后通过数据总线将”B8 23 01”传回输入输出电路,由输入输出控制电路将其放入指令缓冲器中,最后执行,执行后,改变了ax寄存器中的值,变为0123。
接下来,读取一条指令后,IP的值自动增加,以使CPU可以读取下一条指令,由于我们上一次索引的地址是一个占用了3个字节的指令,所以IP此时自动加3(偏移3个地址),变为0003,此时,CS:IP指向内存单元2000:0003,再由地址加法器合成,变为20003,去索引下一条指令的地址。
简述以上的工作流程:
(1)从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器;
(2)IP = IP + 所读取指令的长度,从而指向下一条指令;
(3)执行指令。转到步骤(1),重复这个过程。
现在,我们更清楚了CS和IP的重要性,它们的内容提供了CPU要执行指令的地址。
在内存中,指令和数据没有任何区别,都是二进制信息,CPU在工作的时候把有的信息看作指令,有的信息看作数据。我们可以说,CPU将CS:IP指向的内存单元中的内容看作指令,因为,在任何时候,CPU将CS、IP中的内容当作指令的段地址和偏移地址,用它们合成指令的物理地址,到内存中读取指令码,执行。如果说,内存中的一段信息曾被CPU执行过的话,那么,它所在的内存单元必然被CPU指向过。
注:8086CPU加电启动或者复位后(即CPU刚开始工作时),CS和IP被设置为CS=FFFFH,IP=0000H,因其表示的物理地址为FFFF0,所以,FFFF0单元中的指令是8086PC机开机后执行的第一条指令。
在CPU中,程序员能够用指令读取的部件只有寄存器,程序员可以通过改变寄存器中的内容来实现对CPU的控制。因为CPU从何处开始执行指令是由CS和IP中的内容来决定的,所以我们可以通过修改CS、IP中的内容来控制CPU执行目标指令。
我们如何修改CS和IP这两个寄存器中的值呢?我们先来回忆一下,我们是如何修改通用寄存器ax,bx,cx,dx的,我们使用过mov ax,123来修改这些寄存器的值,其实,CPU大部分寄存器的值都可以用mov指令来改变,mov指令被称为”传送指令”。
但遗憾的是,我们并不能通过mov指令来修改CS和IP的值,原因很简单,因为CPU没有提供这样的功能,但CPU提供了另外的指令来修改CS和IP,能够修改CS、IP的内容的指令被统称为”转移指令”,我们来了解一个最简单的,“jmp”指令。(英文jump的缩写)
jmp指令的用法
若想同时修改CS、IP中的内容,可以使用”jmp 段地址:偏移地址”的形式来完成,如:
jmp 2AE3:3,执行后:CS = 2AE3,IP = 0003,CPU将从2AE33处读取指令(2AE30+0003)
jmp 3:0B16,执行后:CS = 0003,IP = 0B16,CPU将从B46处读取指令(00030+0B16)
可以看出,”jmp 段地址:偏移地址”的指令功能为:用指令中给出的段地址修改CS,偏移地址修改IP。
若想仅修改IP的内容,可以使用”jmp 某一合法寄存器”的形式完成,如:
jmp ax
指令执行前:ax=1000,CS=2000,IP=0003
指令执行后:ax=1000,CS=2000,IP=1000
jmp bx
指令执行前:bx=0B16,CS=2000,IP=0003
指令执行后:bx=0B16,CS=2000,IP=0B16
可以看出,”jmp 段地址:偏移地址”的指令功能为:用寄存器中的值修改IP。
接下来,我们做一道题来加深理解。
我们假设初始时,CS中的值为2000,IP中的值为0000。如下图,CPU执行指令的过程和结果是什么呢?
(1)因目前CS中的值为2000(段地址为2000),IP中的值为0000(偏移地址为0000),所以现在CS:IP指向的物理地址为20000(2000 x 10H +0000),CPU从20000处读取第一条指令
(2)第一条指令为mov ax,6622
,且长度为3个字节,读入指令后,IP自动增加3,变为0003,此时CS值不变,还是2000(因为我们并没有修改过CS),执行后ax寄存器的值变为6622,现在CS:IP指向20003这个物理地址,并读入其地址对应的指令,也就是jmp 1000:3
(3)读入指令后,IP自动增加5,IP变为0008,ax没有被修改,值不变,执行后,因为执行的指令是jmp 1000:3
,修改了CS和IP的值,所以IP被再次修改,变为0003,CS为1000,则CPU从地址10003处读取指令,读取了mov ax,0000
(4)读入指令后,IP自动增加3,变为0006,CS不变还是1000,执行指令后,覆盖了ax寄存器的值,ax变为0000,现在CS:IP指向10006,读取了mov ax,bx
(5)读入指令后,IP自动加2,变为0008,CS不变还是1000,执行指令后,将ax的值交给了bx,bx此时也为0000,现在CS:IP指向10008,读取了指令jmp bx
(6)读入指令后,IP自动加2,变为000A(0008+2),执行指令后,IP的值被再次修改,变为0000,CS一直没变,还是1000,所以现在CS:IP指向10000,读取了mov ax,0123
(7)读入指令后,IP自动加3,变为0003,执行指令后,ax值变为0123,现在CS:IP指向10003,读取了mov ax,0000
(8)……..死循环
经过本次分析,我们可以得知,指令执行的序列为:
(1)mov ax,6622
(2)jmp 1000:3
(3)mov ax,0000
(4)mov bx,ax
(5)jmp bx
(6)mov ax,0123
(7)转到第(3)步执行,继续往后执行4,5,6,3,4,5,6,3......成为死循环
在编程时,我们可以根据需要,将一组内存单元定义为一个段。
将长度为N(N<=64KB)的一组代码,存在一组地址连续的,起始地址(基础地址)为16的倍数的内存单元中,我们可以认为,这段内存是用来存放代码的,从而定义了一个代码段。因为偏移地址是16位,所以最大长度为2的16次方,为64KB。如:
mov ax,0000 (B8 00 00)
add ax,0123 (05 23 01)
mov bx,ax (8B D8)
jmp bx (FF E3)
我们将上面这些长度为10个字节的指令,存放在 123B0 - 123B9 的一组内存单元中,我们就可以认为 123B0 - 123B9 这段内存是用来存放代码的,是一个代码段,他的段地址为123B,长度为10个字节。
那么问题来了,我们如何使得代码段中的指令被执行呢?
将一段内存当做代码段,仅仅是我们在编程时的一种安排,CPU并不会由于这样的安排,就自动的,将我们定义的代码段中的指令当作指令来执行。CPU只认为被CS:IP指向的内存单元中的内容是指令。
所以,要让CPU执行我们的执行,就必须要讲CS:IP指向所定义的代码段中的第一条指令的地址首地址。如上面我们定义的这个代码段,如果要让这些指令被执行,可以设置CS的值为123B,IP的值为0000。
(1)段地址在CPU的段寄存器中存放,当CPU要访问内存时,由段寄存器提供内存单元的段地址。8086CPU有4个段寄存器,其中CS用来存放指令的段地址。
(2)CS存放指令的段地址,IP存放指令的偏移地址。任意时刻,CPU将CS:IP指向的内容当作指令执行。
(3)CPU的工作过程:
1.从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器
2.IP指向下一条指令。
3.执行指令。(转到步骤1,重复这个过程)
4.8086CPU提供转移指令来修改CS、IP的内容(jmp)
下面的3条指令执行后,CPU几次修改IP的值?都是什么时候?最后IP的值为多少?
mov ax,bx
sub ax,ax
jmp ax
CS初始值为x,IP的初始值为y,读取 x 乘以 16 + y 处的指令,也就是第一条
mov ax,bx //读取后,IP自动加2,这是CPU第一次修改IP,指向下一条指令
sub ax,ax //读取后,IP自动加2,这是CPU第二次修改IP,指向下一条指令
jmp ax //读取后,IP自动加2,这是CPU第三次修改IP,由于sub命令是相减,ax-ax=0,现在等于是jmp 0,修改了IP的值为0,这是CPU第四次修改IP。
结论,CPU修改了4次IP的值,最后IP的值为0。
用户名 | 金币 | 积分 | 时间 | 理由 |
---|---|---|---|---|
yyc | 0.80 | 0 | 2022-11-03 15:03:47 | 一个受益终生的帖子~~ |
Track-聂风 | 100.00 | 0 | 2021-03-12 16:04:52 | 文章通过 |
打赏我,让我更有动力~
© 2016 - 2024 掌控者 All Rights Reserved.