从0开始进入汇编的世界之每天理解一篇汇编帖-第三节

urfyyyy   ·   发表于 2020-06-18 21:49:39   ·   技术文章

前言

上一节我们了解了通用寄存器,用几条汇编指令做了例子,了解了物理地址,以及段的概念,接下来我们根据”段”的内容,更加深入一些

上一节链接
从0开始进入汇编的世界之每天理解一篇汇编帖-第二节


一、段寄存器

我们前面讲到,8086CPU在访问内存时,需要由相关部件提供内存单元的段地址和偏移地址,送入地址加法器中合成物理地址。
段地址在CPU的段寄存器中存放,段寄存器就是提供段地址的。8086CPU有4个段寄存器:CS(代码段寄存器)、DS(数据段寄存器)、SS(堆栈段寄存器)、ES(附加段寄存器)
当CPU要访问内存时,就由这4个段寄存器来提供内存单元的段地址,现在我们主要来看一下CS(代码段寄存器)

1.1、CS和IP

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单元中存放的机器码对应汇编指令如下

  1. 地址:20000 - 20002,内容:B8 23 01,长度:3字节,对应汇编指令:mov ax,0123
  2. 地址:20003 - 20005,内容:BB 03 00,长度:3字节,对应汇编指令:mov bx,0003
  3. 地址:20006 - 20007,内容:89 D8,长度:2字节,对应汇编指令:mov ax,bx
  4. 地址: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机开机后执行的第一条指令。


1.2、修改CS和IP的指令

在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 段地址:偏移地址”的形式来完成,如:

  1. jmp 2AE3:3,执行后:CS = 2AE3IP = 0003CPU将从2AE33处读取指令(2AE30+0003
  2. jmp 3:0B16,执行后:CS = 0003IP = 0B16CPU将从B46处读取指令(00030+0B16

可以看出,”jmp 段地址:偏移地址”的指令功能为:用指令中给出的段地址修改CS,偏移地址修改IP。

若想仅修改IP的内容,可以使用”jmp 某一合法寄存器”的形式完成,如:

  1. jmp ax
  2. 指令执行前:ax=1000CS=2000IP=0003
  3. 指令执行后:ax=1000CS=2000IP=1000
  4. jmp bx
  5. 指令执行前:bx=0B16CS=2000IP=0003
  6. 指令执行后:bx=0B16CS=2000IP=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. 1mov ax,6622
  2. 2jmp 1000:3
  3. 3mov ax,0000
  4. 4mov bx,ax
  5. 5jmp bx
  6. 6mov ax,0123
  7. 7)转到第(3)步执行,继续往后执行4,5,6,3,4,5,6,3......成为死循环

1.3、代码段

在编程时,我们可以根据需要,将一组内存单元定义为一个段。
将长度为N(N<=64KB)的一组代码,存在一组地址连续的,起始地址(基础地址)为16的倍数的内存单元中,我们可以认为,这段内存是用来存放代码的,从而定义了一个代码段。因为偏移地址是16位,所以最大长度为2的16次方,为64KB。如:

  1. mov ax,0000 B8 00 00
  2. add ax,0123 05 23 01
  3. mov bx,ax 8B D8
  4. 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的值为多少?

  1. mov ax,bx
  2. sub ax,ax
  3. 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 文章通过

打赏我,让我更有动力~

0 Reply   |  Until 2020-6-18 | 801 View
LoginCan Publish Content
返回顶部 投诉反馈

© 2016 - 2024 掌控者 All Rights Reserved.