《汇编语言》– 寄存器(内存访问)和第一个程序

我们继续学习汇编语言程序设计。

寄存器(内存访问)

 

①:我们提出字单元的概念:字单元,即存放一个字型数据(16位)的内存单元,由两个地址连续的内存单元组成。高地址内存单元中存放字型数据的高位字节,低地址内存单元中存放字型数据的低位字节。在以后的课程中,我们将起始地址为N的字单元简称为N地址字单元。

 

②:8086CPU中有一个DS寄存器,通常用来存放要访问数据的段地址。

比如我们要读取10000H单元的内容,可以用如下的程序段进行: 
mov bx,1000H mov ds,bx mov al,[0]
上面的三条指令将10000H(1000:0)中的数据读到al中。 
下面详细说明指令的含义:
mov al,[0]
前面我们使用mov指令,可完成两种传送:(1)将数据直接送入寄存器;(2)将一个寄存器中的内容送入另一个寄存器。 

指令执行时,8086CPU自动取ds中的数据为内存单元的段地址。

8086CPU不支持将数据直接送入段寄存器的操作,ds是一个段寄存器,所以mov ds,1000H这条指令是非法的。那么如何将1000H送入ds呢?只好用一个寄存器来进行中转,即先将1000H送入一个一般的寄存器,如bx,再将bx中的内容送入ds。

 

③:add和sub指令同mov一样,都有两个操作对象。它们也可以有以下几种形式:
add  寄存器,数据           比如:add ax,8 

add  寄存器,寄存器         比如:add ax,bx

add  寄存器,内存单元       比如:add ax,[0]

add  内存单元,寄存器       比如:add [0],ax

sub  寄存器,数据           比如:sub ax,9

sub  寄存器,寄存器         比如:sub ax,bx

sub  寄存器,内存单元       比如:sub ax,[0]

sub  内存单元,寄存器       比如:sub [0],ax

 

④:栈有两个基本的操作:入栈和出栈。入栈就是将一个新的元素放到栈顶,出栈就是从栈顶取出一个元素。栈顶的元素总是最后入栈,需要出栈时,又最先被从栈中取出。栈的这种操作规则被称为:LIFO(Last In First Out,后进先出)。

8086CPU提供入栈和出栈指令,最基本的两个是 PUSH(入栈) 和 POP(出栈)。比如:push ax 表示将寄存器ax中的数据送入栈中,pop ax表示从栈顶取出数据送入ax。8086CPU的入栈和出栈操作都是以字为单位进行的。

CPU如何知道栈顶的位置?显然,也应该有相应的寄存器来存放栈顶的地址,8086CPU中,有两个寄存器,段寄存器SS和寄存器SP,栈顶的段地址存放在SS中,偏移地址存放在SP中。任意时刻,SS:SP指向栈顶元素。push指令和pop指令执行时,CPU从SS和SP中得到栈顶的地址。

image

 

⑤: 8086CPU不保证我们对栈的操作不会超界。这也就是说,8086CPU只知道栈顶在何处(由SS:SP指示),而不知道读者安排的栈空间有多大。这点就好像,CPU只知道当前要执行的指令在何处(由CS:IP指示),而不知道读者要执行的指令有多少。

我们在编程的时候要自己操心栈顶超界的问题,要根据可能用到的最大栈空间,来安排栈的大小,防止入栈的数据太多而导致的超界;执行出栈操作的时候也要注意,以防栈空的时候继续出栈而导致的超界。

 

⑥:push和pop 指令的格式可以是如下形式:
push 寄存器;将一个寄存器中的数据入栈
pop 寄存器;出栈,用一个寄存器接收出栈的数据
当然也可以是如下形式:
push 段寄存器   ;将一个段寄存器中的数据入栈
pop 段寄存器    ;出栈,用一个段寄存器接收出栈的数据
push和pop也可以在内存单元和内存单元之间传送数据,我们可以:
push 内存单元  ;将一个内存字单元处的字入栈(注意:栈操作都是以字为单位。)

pop 内存单元  ;出栈,用一个内存字单元接收出栈的数据

我们要十分清楚的是,push和pop指令同mov指令不同,CPU执行mov指令只需一步操作,就是传送,而执行push、pop指令却需要两步操作。执行push时,CPU的两步操作是:先改变SP,后向SS:SP处传送。执行pop时,CPU的两步操作是:先读取SS:SP处的数据,后改变SP。

 

⑦:一个栈段最大可以设为多少?

这个问题显而易见,提出来只是为了提示我们将相关的知识融会起来。首先从栈操作指令所完成的功能的角度上来看,push、pop等指令在执行的时候只修改SP,所以栈顶的变化范围是0~FFFFH,从栈空时候的SP=0,一直压栈,直到栈满时SP=0;如果再次压栈,栈顶将环绕,覆盖了原来栈中的内容。所以一个栈段的容量最大为64 KB。(2的16次方)

 

⑧:对于数据段,将它的段地址放在DS中,用mov、add、sub等访问内存单元的指令时,CPU就将我们定义的数据段中的内容当作数据来访问;
对于代码段,将它的段地址放在CS中,将段中第一条指令的偏移地址放在IP中,这样CPU就将执行我们定义的代码段中的指令;
对于栈段,将它的段地址放在SS中,将栈顶单元的偏移地址放在SP中,这样CPU在需要进行栈操作的时候,比如执行push、pop指令等,就将我们定义的栈段当作栈空间来用。

一段内存,可以既是代码的存储空间,又是数据的存储空间,还可以是栈空间,也可以什么也不是。关键在于CPU中寄存器的设置,即:CS、IP,SS、SP,DS的指向。

 

⑨:Debug的T命令在执行修改寄存器SS的指令时,下一条指令也紧接着被执行。

 

第一个程序

 

①:segment和ends是一对成对使用的伪指令,这是在写可被编译器编译的汇编程序时,必须要用到的一对伪指令。segment和ends的功能是定义一个段,segment说明一个段开始,ends说明一个段结束。

段名 segment

段名 ends

 

end是一个汇编程序的结束标记,编译器在编译汇编程序的过程中,如果碰到了伪指令end,就结束对源程序的编译。

 

assume这条伪指令的含义为“假设”。也就是把定义的一个代码段和某个段寄存器关联起来。

 

mov ax,4c00H

int 21H

这两条指令所实现的功能就是程序返回。

 

②:编译汇编代码:masm 程序路径

       连接编译后的目标文件:link 文件路径

 

③:程序执行过程跟踪:

debug 文件名.exe  就可以将程序加载入内存,设置CS:IP指向程序的入口。cx存放的是程序的长度。

image到了int 21,我们要用P命令执行。

 

By:AloneMonkey

本文链接:http://www.alonemonkey.com/masm-chapter34.html