汇编语言入门(二)
汇编语言入门(一)
文章目录
- 寄存器【内存访问】
- 内存中字的存储
- DS和【address】
- 字的传送
- mov、add、sub指令
- 数据段
- 栈
- 栈顶越界的问题
- PUSH和POP指令
- 栈的综述
- 栈段【栈的综述】
寄存器【内存访问】
内存中字的存储
4E20
是两个字节构成一个字。
上图中0地址单元中存放的字节型数据是
20
。
0地址字节单元中存放的字型数据是4E20
。
2地址字单元中存放的字节型数据是12
。
2地址单元中存放的字型数据是0012
。
1地址单元中存放的字型数据是124EH
。
结论:任何两个地址连续的内存单元,N
号单元和N+1
号单元,可以将它们看成两个内存单元,也可以看成一个地址为N的字单元中的高位字节单元和低位字节单元。
DS和【address】
- CPU要读取一个内存单元的时候,必须先要给出这个内存单元的地址。
- 在
8086CPU
中,内存地址是由段地址和偏移地址组成。 8086CPU
中有一个DS
寄存器,通常用来存放要访问的数据的段地址。8086CPU
有4个段寄存器:CS
【代码地址】、DS
【数据地址】、SS
【堆栈地址】、ES
【前面不够放这个寄存器】。- 下面三条指令将
10000H【1000:0】
中的数据读到AL
中。
mov bx,1000H
mov ds,bx
mov al,[0] //偏移地址为0的内存单元送入寄存器
- 已知的
mov
指令可完成两种传送功能:(1)将数据直接送入寄存器;(2)将一个寄存器中的内容送入另一个寄存器中;(3)将一个内存单元中的内容送入一个寄存器。 - 从哪个内存单元到哪个寄存器?
mov指令的格式:mov 寄存器名,内存单元地址
- 执行指令时,
8086CPU
自动取DS
中的数据为内存单元的段地址。 CS:IP
读取到的东西会被CPU
认为是指令,而DS
读取到的东西会直接被CPU
认为是数据。CS
、DS
都是段寄存器,一个程序往往被分为好几个段,DS
保存的是数据段的基地址,而CS
保存的是代码段的基地址,IP
中保存的是要执行的下一条指令的地址。- 如何用mov指令从10000H中读取数据?
10000H
表示从1000:0
【段地址:偏移地址】- 将段地址
1000H
放入DS
- 用
mov al,[0]
完成传送【mov
指令中的[]
说明操作对象是一个内存单元,[]
中的0
说明这个内存单元的偏移地址是0
,它的段地址默认放在DS
中。】
- 8086CPU不支持将数据直接送入段寄存器的操作,如
mov ax,1
可以,但是mov ds,1000H
不可以。 - 数据 -> 通用寄存器 -> 段寄存器
- 如何将数据从寄存器送入内存单元?将
al
中的数据如何送入到内存单元10000H
中?
mov bx,1000H
mov ds,bx //前两句是确认数据的段地址
mov [0],al //这里的al是题目的要求
字的传送
- 因为
8086CPU
是16
位结构,有16
根数据线,所以可以一次性传输16
位的数据,也就是一个字。
mov bx,1000H
mov ds,bx //前两句是确认数据的段地址
mov ax,[0] //1000:0处的字型数据放入ax中
mov [0],cx //cx中的16位数据送到1000:0中
mov、add、sub指令
- mov指令的几种形式
mov 寄存器,数据
mov 寄存器,寄存器
mov 寄存器,内存单元
mov 内存单元,寄存器
mov 段寄存器,寄存器
mov 寄存器,段寄存器
add
和sub
指令同mov
一样,都有两个操作对象。
3. sub:左边=左边-右边
数据段
- 我们可以将一组长度为N【N<=64K】,地址连续、起始地址为16的倍数的内存单元当作专门存储数据的内存空间,从而定义了一个数据段。
- 比如,我们用
123B0H~123B9H
这段空间来存放数据,段地址:123BH
,长度:10
字节。 - 如何访问数据段中的数据呢? 将一段内存中当作数据段,是我们在编程时的一种安排,我们可以在具体操作的时候,用
DS
存放数据段的段地址,再根据需要,用相关指令访问数据段中的具体单元。
栈
- 栈是一种具有特殊的访问方式的存储空间。它的特殊性在于,最后进入这个空间的数据,最先出去。【
LIFO
】 - 栈的基本操作:入栈、出栈。
- 现今的
CPU
中都有栈的设计。 8086CPU
提供相关的指令以栈的方式访问内存空间,这意味着,我们在基于8086CPU
编程的时候,可以将一段内存当作栈来使用。- 入栈:
PUSH
,出栈:POP
。
push ax //将寄存器ax中的数据送入栈中
pop ax //从栈顶取出数据送入ax
8086CPU
的入栈和出栈操作都是以字为单位进行的。- 寄存器
CS
和IP
中存放着当前指令的段地址和偏移地址。 8086CPU
中,段寄存器SS
:存放栈顶的段地址,寄存器SP
:存放栈顶的偏移地址。- 任意时刻,
SS:SP
指向栈顶元素。当栈为空的时候,栈中没有元素,也就不存在栈顶元素。 - 所以,
SS:SP
只能指向栈的最底部单元下面的单元,该单元的偏移地址为栈最底部的字单元的偏移地址+2。
栈顶越界的问题
- 8086CPU在工作过程中只考虑当前的情况:当前栈在何处?当前要执行的指令是哪一条。
- 如何保证入栈、出栈时栈顶不会超出栈空间? 我们在编程的时候要自己操心栈顶超界的问题,要根据可能用到的最大栈空间来安排栈的大小,防止入栈的数据太多而导致的超界。执行出栈操作的时候也要注意。
PUSH和POP指令
- push和pop指令是可以在寄存器和内存之间传送数据的。
push
寄存器:将一个寄存器中的数据入栈
pop
寄存器:用一个寄存器接受出栈的数据。
push
段寄存器:将一个段寄存器中的数据入栈
pop
段寄存器:用一个段寄存器接受出栈的数据
push
内存单元:将一个内存单元处的字入栈
pop
内存单元:出栈,用一个内存字单元接收出栈的数据
push[0]
指令执行时,CPU
要知道内存单元的地址,可以在push
、pop
指令中给出内存单元的偏移地址,段地址在指令执行时,CPU从DS
中取得。
mov ax,1000H
mov ss,ax
mov sp,0010H
mov ax,001AH
mov bx,001BH
push ax
push bx
sub ax,ax // mov ax,0
sub bx,bx // mov bx,0
pop bx
pop bx
mov ax,1000H
mov ss,ax
mov sp,001H
mov ax,002AH
mov bx,002BH
push ax
push bx
pop ax //用寄存器ax接受出来的bx的数据,即可实现交换
pop bx
另一种写法:
mov ax,1000H
mov ss,ax
mov sp,2
mov ax,2266H
push ax //会执行 sp = sp -2
栈的综述
- 栈空间当然也是内存空间的一部分,它只是一段可以以一种特殊的方式进行访问的内存空间。
- push指令的执行步骤:
- sp = sp - 2
- 向SS:SP指向的字单元中送入数据
- pop指令执行步骤:
- 从SS:SP指向的字单元中读取数据;
- sp = sp + 2
8086CPU
只记录栈顶,栈空间的大小我们要自己管理。- 可以用栈来暂存以后需要恢复的寄存器的内容。
栈段【栈的综述】
- 对于
8086PC
机,在编程时,我们可以根据需要,将一组内存单元定义为一个段。 - 我们可以将长度为
N
【N<= 64K
】的一组地址连续,起始地址为16
的倍数的内存单元,当作栈来用,从而定义了一个栈段。 - 将一段内存当作栈段,仅仅是在编程时的一种安排,CPU不会由于这种安排,就在执行
push
、pop
等栈操作指令时就自动将我们定义的栈段当作栈空间来访问。CPU
只能看见栈顶。 - 栈段的最大可以设为:2的16次方,
64KB
。 - 从栈操作指令所完成的功能的角度来看,
push
、pop
指令在执行的时候只修改SP
。 - 我们可以将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的大暖,这完全是我们自己的安排。
- 对于代码段,我们将它的段地址放在CS中,将段中的第一条指令的偏移地址放在IP中,这样CPU就将执行我们定义的代码段中的指令。
- 对于栈段,我们将它的段地址放在
SS
中,将栈顶单元的偏移地址放在SP
中,这样CPU
在需要进行栈操作的时候,比如push
、pop
指令等,就将我们定义的栈段当作栈空间来用。 CPU
将内存中的某段内存当作代码,是因为CS:IP
指向那里;CPU
将某段内存当作栈,是因为SS:IP
指向了那里。
10.一段内存,可以既是代码的存储空间,又是数据的存储空间,还可以是栈空间,也可以什么都不是,关键在于CPU中寄存器的设置。