汇编学习
2015年6月29日 22:40:52
一、基础
1、寄存器:CPU里面的存储数据的器件,一个CPU里面可以有多个寄存器,AX是其中一个寄存器的代号,BX也是。
1.7 CPU对存储器的读写
CPU要想进行数据的读写,必须和外部器件(标准的说法是芯片)进行三类信息的交互:
存储单元的地址(地址信息)
器件的选择,读或写命令(控制信息)
读或写的数据(数据信息)
1.8 地址总线
CPU是通过地址总线来指定存储单元的。
地址总线上能传送多少个不同的信息,CPU就可以对多少个存储单元进行寻址。
一个CPU有N根地址总线,则可以说这个CPU的地址总线的宽度为N。
这样的CPU最多可以寻找2的N次方个内存单元。
1.9 数据总线
CPU与内存或其它器件之间的数据传送是通过数据总线来进行的。
数据总线的宽度决定了CPU和外界的数据传送速度。
1.10 控制总线
CPU对外部器件的控制是通过控制总线来进行的。在这里控制总线是个总称,控制总线是一些不同控制线的集合。
有多少根控制总线,就意味着CPU提供了对外部器件的多少种控制。
所以,控制总线的宽度决定了CPU对外部器件的控制能力。
控制总线上发送的控制信息
小结
(1)汇编指令是机器指令的助记符,同机器指令一一对应。
(2)每一种CPU都有自己的汇编指令集。
(3)CPU可以直接使用的信息在存储器中存放。
(4)在存储器中指令和数据没有任何区别,都是二进制信息。
(8)每一个CPU芯片都有许多管脚,这些管脚和总线相连。也可以说,这些管脚引出总线。一个CPU可以引出三种总线的宽度标志了这个CPU的不同方面的性能:
地址总线的宽度决定了CPU的寻址能力;
数据总线的宽度决定了CPU与其它器件进行数据传送时的一次数据传送量;
控制总线宽度决定了CPU对系统中其它器件的控制能力。
二、寄存器
2.1
一个典型的CPU由运算器、控制器、寄存器等器件组成,这些器件靠内部总线相连。
区别:
内部总线实现CPU内部各个器件之间的联系。
外部总线实现CPU和主板上其它器件的联系。
8086CPU有14个寄存器 它们的名称为:
AX、BX、CX、DX、SI、DI、SP、BP、
IP、CS、SS、DS、ES、PSW。
AX、BX、CX、DX 通常用来存放一般性数据被称为通用寄存器。
8086上一代CPU中的寄存器都是8位的;
为保证兼容性,这四个寄存器都可以分为两个独立的8位寄存器使用。
AX可以分为AH和AL;
BX可以分为BH和BL;
CX可以分为CH和CL;
DX可以分为DH和DL。
2.2 字在寄存器中的存储
一个字可以存在一个16位寄存器中,这个字的高位字节和低位字节自然就存在这个寄存器的高8位寄存器和低8位寄存器中。
2.5 16位结构的CPU
概括的讲,16位结构描述了一个CPU具有以下几个方面特征:
1、运算器一次最多可以处理16位的数据。
2、寄存器的最大宽度为16位。
3、寄存器和运算器之间的通路是16位的。
8086有20位地址总线,可传送20位地址,寻址能力为1M。
8086内部为16位结构,它只能传送16位的地址,表现出的寻址能力却只有64K。
问题:那么,8086CPU如何用内部16位的数据转换成20位的地址呢?
8086CPU采用一种在内部用两个16位地址合成的方法来形成一个20位的物理地址~
8086CPU读写内存时,发生了这么一些事:
CPU中的相关部件提供两个16位的地址,一个称为段地址,另一个称为偏移地址;
段地址和偏移地址通过内部总线送入一个称为地址加法器的部件;
地址加法器将两个16位地址合并成一个20位的地址;
……
地址加法器合成物理地址的方法:物理地址=段地址×16+偏移地址
2.8 段的概念
错误认识:内存被划分成了一个一个的段,每一个段有一个段地址。
其实:内存并没有分段,段的划分来自于CPU,由于8086CPU用“(段地址×16)+偏移地址=物理地址”的方式给出内存单元的物理地址,使得我们可以用分段的方式来管理内存。
以后,在编程时可以根据需要,将若干地址连续的内存单元看作一个段,用段地址×16定位段的起始地址(基础地址),用偏移地址定位段中的内存单元。
(1)段地址×16 必然是 16的倍数,所以一个段的起始地址也一定是16的倍数;
(2)偏移地址为16位,16 位地址的寻址能力为 64K,所以一个段的长度最大为64K。
结论:CPU可以用不同的段地址和偏移地址形成同一个物理地址。
内存单元地址小结
在8086PC机中,存储单元的地址用两个元素来描述。即段地址和偏移地址。
“数据在21F60H内存单元中。”对于8086PC机的两种描述:
(a)数据存在内存2000:1F60单元中;
(b)数据存在内存的2000段中的1F60H单元中。
可根据需要,将地址连续、起始地址为16的倍数的一组内存单元定义为一个段。
2.9 段寄存器
段寄存器就是提供段地址的。
8086CPU有4个段寄存器:CS(代码段寄存器)、DS(数据段寄存器)、SS(堆栈段寄存器)、ES(附加段寄存器)
当8086CPU要访问内存时,由这4个段寄存器提供内存单元的段地址。
CS和IP是8086CPU中最关键的寄存器,它们指示了CPU当前要读取指令的地址。
CS为代码段寄存器;
IP为指令指针寄存器。
在任何时候,CPU将CS、IP中的内容当作指令的段地址和偏移地址,用它们合成指令的物理地址,到内存中读取指令码,执行。
如果说,内存中的一段信息曾被CPU执行过的话,那么,它所在的内存单元必然被CS:IP指向过。
2.11 修改CS、IP的指令
在CPU中,程序员能够用指令读写的部件只有寄存器,程序员可以通过改变寄存器中的内容实现对CPU的控制。
CPU从何处执行指令是由CS、IP中的内容决定的,程序员可以通过改变CS、IP中的内容来控制CPU执行目标指令。
我们如何改变CS、IP的值呢?
同时修改CS、IP的内容:
转移指令jmp 段地址:偏移地址
jmp 2AE3:3
jmp 3:0B16
功能:用指令中给出的段地址修改CS,偏移地址修改IP。
仅修改IP的内容:
jmp 某一合法寄存器
jmp ax (类似于 mov IP,ax)
jmp bx
功能:用寄存器中的值修改IP。
8086机中,任意时刻,CPU将CS:IP指向的内容当作指令执行。
3、8086CPU的工作过程:
(1)从CS:IP指向内存单元读取指令,读取的指令进入指令缓冲器;
(2)IP指向下一条指令;
(3)执行指令。(转到步骤(1),重复这个过程。)
4、8086CPU提供转移指令修改CS、IP的内容。
2.22 DEBUG
1、运行---cmd---debug进入
R命令查看、改变CPU寄存器的内容;
D命令查看内存中的内容;
E命令改写内存中的内容;
U命令将内存中的机器指令翻译成汇编指令;
T命令执行一条机器指令;
A命令以汇编指令的格式在内存中写入一条机器指令。
到了 int 21,我们要用P命令执行:
g命令调试时能跳到想去的地址,特别是调试循环代码时
进入循环时用p命令也可以退出循环
3.6 栈
栈有两个基本的操作:入栈和出栈。
入栈:将一个新的元素放到栈顶;
出栈:从栈顶取出一个元素。
PUSH(入栈)
POP (出栈)
push ax:将寄存器ax中的数据送入栈中;
pop ax :从栈顶取出数据送入ax。
8086CPU的入栈和出栈操作都是以字为单位进行的。
CPU如何指导当前要执行的指令所在的位置?
答:寄存器CS和IP中存放着当前指令的段地址和 偏移地址。
8086CPU中,有两个寄存器:
段寄存器SS 存放栈顶的段地址
寄存器SP 存放栈顶的偏移地址
任意时刻,SS:SP指向栈顶元素。
push ax
(1)SP=SP–2;
(2)将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶。
pop ax
(1)先把SS:SP地址的值给ax
(2)SP=SP+2
问题3.6:如果我们将10000H~1000FH 这段空间当作栈,初始状态栈是空的,此时,SS=1000H,SP=?
指向10010H,最高地址单元的下一个地址
我们将10000H~1000FH 这段空间当作栈段,SS=1000H,栈空间大小为16 字节 ,栈最底部的字单元地址为1000:000E。
任意时刻,SS:SP指向栈顶,当栈中只有一个元素的时候,
SS = 1000H,SP=000EH。
栈为空,就相当于栈中唯一的元素出栈,出栈后,SP=SP+2 ,SP 原来为 000EH,加 2 后SP=10H
所以,当栈为空的时候,SS=1000H,SP=10H。
3.8 栈顶超界的问题
SS和SP只记录了栈顶的地址,依靠SS和SP可以保证在入栈和出栈时找到栈顶。
可是,如何能够保证在入栈、出栈时,栈顶不会超出栈空间?
当栈满的时候再使用push指令入栈,
栈空的时候再使用pop指令出栈,
都将发生栈顶超界问题。
栈顶超界是危险的。因为我们既然将一段空间安排为栈 ,那么在栈空间之外的空间里很可能存放了具有其他用途的数据、代码等,这些数据、代码可能是我们自己的程序中的,也可能是别的程序中的。
(毕竟一个计算机系统并不是只有我们自己的程序在运行)
结论:
我们在编程的时候要自己操心栈顶超界的问题 ,要根据可能用到的最大栈空间,来安排栈的大小,防止入栈的数据太多而导致的超界;
执行出栈操作的时候也要注意,以防栈空的时候继续出栈而导致的超界。
3.9 push、pop指令
push和pop指令是可以在寄存器和内存之间传送数据的。
栈空间当然也是内存空间的一部分,它只是一段可以以一种特殊的方式进行访问的内存空间。
push和pop指令的格式(1)
push 寄存器:将一个寄存器中的数据入栈
pop寄存器:出栈,用一个寄存器接收出栈的数据
例如:push ax
pop bx
push和pop指令的格式(2)
push 段寄存器:将一个段寄存器中的数据入栈
pop段寄存器:出栈,用一个段寄存器接收出栈的数据
例如:push ds
pop es
push和pop指令的格式(3)
push内存单元:将一个内存单元处的字入栈(栈操作都是以字为单位)
pop 内存单元:出栈,用一个内存字单元接收出栈的数据
例如:push [0]
pop [2]
指令执行时 ,CPU 要知道内存单元的地址,可以在 push、pop 指令中给出内存单元的偏移地址,段地址在指令执行时,CPU从ds中取得
结论
push、pop 实质上就是一种内存传送指令,可以在寄存器和内存之间传送数据,与mov指令不同的是,push和pop指令访问的内存单元的地址不是在指令中给出的,而是由SS:SP指 出的。
同时,push和pop指令还要改变 SP 中的内容。
push、pop 等栈操作指令,修改的只是SP。也就是说,栈顶的变化范围最大为:0~FFFFH。
提供:SS、SP指示栈顶;改变SP后写内存的入栈指令;读内存后改变SP的出栈指令。
这就是8086CPU提供的栈操作机制。
栈的综述
(1)8086CPU提供了栈操作机制,方案如下:
在SS,SP中存放栈顶的段地址和偏移地址;
提供入栈和出栈指令,他们根据SS:SP指示的地址,按照栈的方式访问内存单元。
(2)push指令的执行步骤:
1)SP=SP-2;
2)向SS:SP指向的字单元中送入数据。
(3)pop指令的执行步骤:
1)从SS:SP指向的字单元中读取数据;
2)SP=SP-2。
(4)任意时刻,SS:SP指向栈顶元素。
(5)8086CPU只记录栈顶,栈空间的大小我们要自己管理。
(6)用栈来暂存以后需要恢复的寄存器的内容时 ,寄存器出栈的顺序要和 入栈的顺序相反。
(7)push、pop实质上是一种内存传送指令,注意它们的灵活应用。
栈是一种非常重要的机制,一定要深入理解,灵活掌握。
3.10 栈段
前面讲过,对于8086PC机,在编程时,我们可以根据需要 ,将一组内存单元定义为一个段。
我们可以将长度为 N(N ≤64K )的一组地址连续、起始地址为16的倍数的内存单元,当作栈来用,从而定义了一个栈段。
比如我们将10010H~1001FH 这段长度为 16 字节的内存空间当作栈来用,以栈的方式进行访问。
这段空间就可以成为栈段,段地址为1000H,大小为16字节。
段的综述
我们可以将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元。这完全是我们自己的安排。
我们可以用一个段存放数据,将它定义为“数据段”;
我们可以用一个段存放代码,将它定义为“代码段”;
我们可以用一个段当作栈,将它定义为“栈段”;
我们可以这样安排,但若要让CPU按照我们的安排来访问这些段,就要:
对于数据段,将它的段地址放在 DS中,用mov、add、sub等访问内存单元的指令时,CPU就将我们定义的数据段中的内容当作数据段来访问;
我们可以这样安排,但若要让CPU按照我们的安排来访问这些段,就要:
对于数据段,将它的段地址放在 DS中,用mov、add、sub等访问内存单元的指令时,CPU就将我们定义的数据段中的内容当作数据段来访问;
对于代码段,将它的段地址放在 CS中,将段中第一条指令的偏移地址放在IP中,这样CPU就将执行我们定义的代码段中的指令;
对于栈段,将它的段地址放在SS中,将栈顶单元的偏移地置放在 SP 中,这样CPU在需要进行栈操作的时候,比如执行 push、pop 指令等,就将我们定义的栈段当作栈空间来用。
可见,不管我们如何安排 ,CPU 将内存中的某段内存当作代码 ,是因为CS:IP指向了那里;CPU将某段内存当作栈 ,是因为 SS:IP 指向了那里。
我们一定要清楚 ,什么是我们的安排,以及如何让CPU按我们的安排行事。
要非常的清楚CPU的工作机理,才能在控制CPU来按照我们的安排运行的时候做到游刃有余。