汇编指令有多少个6-汇编语⾔中段的使⽤+dw+start标号
在操作系统的环境中,合法地通过操作系统获得的空间都是安全的,因为操作系统不会让⼀个程序所⽤的空间和其他程序以及系统⾃⼰的空间相冲突。在操作系统允许的情况下,程序可以取得任意容量的空间。
程序取得所需空间的⽅法有两种,⼀是在加载程序的时候为程序分配,再就是程序在执⾏的过程中向系统申请。我们重点讨论第⼀种⽅式。
若要⼀个程序在被加载的时候取得所需的空间,则必须要在源程序中做出说明–我们通过在源程序中定义段来进⾏内存空间的获取。
⼀、在代码段中使⽤数据
考虑这样⼀个问题,编程计算以下8个数据的和,结果存在ax寄存器中:
0123h、0456h、0789h、0abch、0defh、0fedh、0cbah、0987h
⼀个最简单的⽅式就是将这些数据⼀个⼀个加到ax寄存器中,但是这样写的程序会⼗分繁琐,所以我们希望利⽤循环来处理。如果我们要使⽤循环,那么在累加前,我们要将这些数据存储在⼀组地址连续的
内存单元中,从程序规划的⾓度来讲,我们是不能⾃⼰随便决定使⽤哪段空间的,应该让系统来为我们进⾏分配。正确的做法是:我们在程序中,定义我们希望处理的数据,这些数据就会被编译、连接程序作为程序的⼀部分写到可执⾏⽂件中。当可执⾏⽂件中的程序被加载⼊内存时,这些数据也同时被加载⼊内存中。与此同时,我们要处理的数据也就⾃然⽽然地获得了存储空间。
下⾯就是⼀个具体实现的例⼦:
程序6.1
assume cs:code
code segment
dw 0123h,0456h,0789h,0abch,0dfh,0f edh,0cbah,0987h
mov bx,0
mov ax,0
mov cx,8
s: add ax,cs:[bx]
add bx,2
loop s
mov ax,4c00h
int21h
code ends
end
程序第⼀⾏的 “dw”的含义是定义字型数据 。dw即“define word”。在这⾥,使⽤dw定义了8个字型数据(数据之间以逗号分隔),它们所占的内存空间的⼤⼩为16个字节。
程序中的指令就要对这8个数据进⾏累加,可这8个数据在哪⾥呢?由于它们在代码段中,程序在运⾏的时候CS中存放代码段的段地址,所以可以从CS中得到它们的段地址。利⽤dw定义的数据处于代码段的最开始,所以偏移地址为0,这8个数据就在代码段的偏移0、2、4、6、8、A、C、E、处。程序运⾏时,它们的地址就是CS:0、CS:2、CS:4、CS:6、CS:8、CS:A、CS:C、CS:E
程序6.1编译、连接成可执⾏⽂件后,在系统中直接运⾏可能会出现问题,因为程序的⼊⼝处不是我们所希望执⾏的指令。我们可以在源程序中指明程序的⼊⼝所在,具体做法如下:
程序6.2
dw 0123h,0456h,0789h,0abch,0defh,0f edh,0cbah,0987h
start: mov bx,0
mov ax,0
mov cs,8
s: add ax,cs:[bx]
add bx,2
loop s
mov ax,4c00h
int21h
code ends
end start
注意在程序6.2中加⼊的新内容,在程序的第⼀条指令的前⾯加⼊了⼀个标号start,⽽这个标号在伪指令end的后⾯出现。这⾥,end除了通知编译器程序结束外,还可以通知编译器程序的⼊⼝在什么地⽅。在程序6.2中我们⽤end指令指明了程序的⼊⼝在标号start处,也就是说,"mov bx,0"是程序的第⼀条指令。
在单任务系统中,可执⾏⽂件中的程序执⾏过程如下:
1. 由其他的程序(Debug、command或其他程序)将可执⾏⽂件中的程序加载⼊内存
2. 设置CS:IP指向程序的第⼀条要执⾏的指令(即程序的⼊⼝),从⽽使程序得以运⾏
3. 程序运⾏结束后,返回到加载者。
可执⾏⽂件由描述信息和程序组成,程序来⾃于源程序中的汇编指令和定义的数据;描述信息则主要是编译、连接程序对源程序中相关伪指令进⾏处理所得到的信息。在程序6.2中,⽤伪指令end描述了程序的结束和程序的⼊⼝。在编译、连接后,由"end start"指明的程序⼊⼝,被转化为⼀个⼊⼝地址,
存储在可执⾏⽂件的描述信息中。当程序被加载⼊内存之后,加载者从程序的可执⾏⽂件的描述信息中读到程序的⼊⼝地址,设置CS:IP。这样CPU就从我们希望的地址处开始执⾏。
归根到底,我们若要CPU从某处开始执⾏程序,只要在源程序中⽤"end 标号“指明就可以了。
有了这种⽅法,就可以这样来安排程序的框架:
assume cs:code
code segment
...
...
数据
...
...
start:
...
...
代码
...
...
code ends
end start
⼆、在代码段中使⽤栈
下⾯的程序将程序中定义的数据逆序存放,程序的⼤致思路是将定义的数据存放在cs:0~cs:F单元中,共8个字单元。依次将这8个字单元中的数据⼊栈,然后再依次出栈到这8个字单元中,从⽽实现数据的逆序存放:
程序6.3
dw 0123h,0456h,089h,0abch,0defh,0f edh,0cbah,0987h ;这⾥⽤dw定义了16个字型数据,在程序加载后,将取得16个字的内存空间来存放这16个数据
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;在后⾯的程序中将这段空间当作栈来使⽤
start: mov ax,cs
mov ss,ax
mov sp,30h  ;将设置栈顶ss:sp指向cs:30
mov bx,0
mov cx 8
s: push cs:[bx]
add bx,2
loop s    ;以上将代码段0\~15单元中的8个字型数据依次⼊栈
mov bx,0
mov cx,8
s0: pop cs:[bx]
add bx,2
loop s0    ;以上依次出栈8个字型数据到代码段0~15单元中
mov ax 4c00h
int21h
codesg ends
end start    ;指明程序的⼊⼝在start处
注意程序6.3中的指令:
mov ax,cs
mov ss,ax
mov sp,30h
我们要将cs:10~cs:2F的内存空间当作栈来⽤,初始状态下栈为空,所以ss:sp要指向栈底,则设置ss:sp指向cs:30
在代码段中定义了16个字型数据,它们的数值都是0。这16个字型数据的值是多少,对程序来说没有意义。我们⽤dw定义16个数据,即在程序中写⼊了16个字型数据,⽽程序在加载后,将⽤32个字节的内存空间来存放它们。这段内存空间是我们所需要的,程序将它⽤作栈空间。可见,我们定义这些数据的最终⽬的是,通过它们取得⼀定容量的内存空间。所以我们在描述dw的作⽤时,可以说⽤它定义数据,也可以说⽤它开辟内存空间。
三、将数据、代码、栈放⼊不同的段
在前⾯我们将数据、栈和代码都放到了⼀个段⾥⾯,这样导致我们在编程的时候要注意何处是数据,何处是栈,何处是代码。这样做显然有两个问题:
1. 把它们放到⼀个段中使程序显得混乱
2. 如果数据、栈和代码需要的空间超过64KB,就不能放在⼀个段中(⼀个段的容量不能⼤于64KB,是8086模式的限制,并不是所有
的处理器都是这样)
所以,应该考虑⽤多个段来存放数据、代码和栈:我们⽤和定义代码段⼀样的⽅法来定义多个段,然后在这些段⾥⾯定义需要的数据,或通过定义数据来取得栈空间。具体的做法如下⾯的程序所⽰,这个程序实现了和程序6.3⼀样的功能,不同之处在于它将数据、栈和代码放到了不同的段中。
程序6.4
assume cs:code,ds:data,ss:stack
data segment
dw 0123h,0456h,0789h,0abch,0defh,0f edh,0cbah,0987h
data ends
stack segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,20h  ;设置栈顶ss:sp指向stack:20
mov ax,data
mov ds,ax  ;ds指向data段
mov bx,0;ds:bx指向data段中的第⼀个单元
mov cx,8
s: push [bx]
add bx,2
loop s    ;以上将data段中的0~15单元中的8个字型数据依次⼊栈
mov bx,0
mov cx,8
s0: pop [bx]
add bx,2
loop s0    ;以上依次出栈8个字型数据到data段的0~15单元中
mov ax,4c00h
int21h
code ends
end start
下⾯对程序6.4做出说明
3.1 定义多个段的⽅法
定义⼀个段的⽅法和前⾯所讲的定义代码段的⽅法没有区别,只是对于不同的段,要有不同的段名。
3.2 对段地址的引⽤
在程序中,段名就相当于⼀个标号,它代表了段地址。所以指令mov ax,data的含义就是将名称为"data"的段地址送⼊ax。⼀个段中的数据的段地址可由段名代表,偏移地址就要看它在段中的位置了。程序中"data"段中的数据"0abch"的地址就是:data:6。要将它送⼊bx中,就要⽤如下的代码:
mov ax,data
mov ds,ax
mov bx,ds:[6]
我们不能⽤下⾯的指令:
mov ds,data
mov bx,ds:[6]
其中指令mov ds,data是错误的,因为8086CPU不允许将⼀个数值直接送⼊段寄存器中。程序中对段
名的引⽤,如指令mov ds,data中
的”data“,将被编译器处理为⼀个表⽰段地址的数值。
3.3 “代码段”、“数据段”、“栈段”完全是我们的安排
在汇编源程序中,可以定义许多的段,⽐如在程序6.4中,定义了3个段,“code”、“data”和“stack“。我们可以分别安排它们存放代码、数据和栈。那么我们如何让CPU按照我们的这种安排来执⾏这个程序呢?下⾯来看看源程序中对这3个段所做的处理。
(1)我们在源程序中为这3个段起了具有含义的名称,⽤来放数据的段命名为”data“,⽤来放代码的段命名为”code“,⽤作栈空间的段命名为”stack“。
但是要注意,我们这样命名仅仅是为了使程序便于阅读。这些名称同“start”、“s”、“s0”等标号⼀样,仅在源程序中存在,CPU并不知道它们。
(2)我们在源程序中⽤伪指令assume cs:code,ds:data,ss:stack将cs、ds和ss分别和code、data、stack段相连。这样做了之后,CPU并不会将cs指向code,ds指向data,ss指向stack。
这些指令和assume⼀样,都是伪指令,由编译器执⾏,也是仅在源程序中存在的信息,CPU并不知道。
(我们不必深究assume的作⽤,只要知道需要⽤它将我们定义的具有⼀定⽤途的段和相关的寄存器联系起来就可以了)
(3)源程序中的汇编指令是CPU要执⾏的内容,我们在源程序的最后⽤end start说明了程序的⼊⼝,这个⼊⼝将被写⼊可执⾏⽂件的描述信息,可执⾏⽂件中的程序被加载⼊内存后,CPU的CS:IP被设置指向这个⼊⼝,从⽽开始执⾏程序中的第⼀条指令。标
号“start”在“code”段中,这样CPU就将code段中的内容当作指令来执⾏了。我们在code段中,使⽤指令:
mov ax,stack
mov ss,ax
mov sp,20h
设置ss指向stack,设置ss:sp指向stack:20,CPU执⾏完这些指令后,将把stack段当作栈空间来⽤。CPU若要访问data段中的数据,则可以⽤ds指向data段,⽤其他的寄存器(如bx)来存放data段中数据的偏移地址。
总之,CPU到底如何处理我们定义的段中的内容,是当作指令执⾏,当作数据访问,还是当作栈空间,完全是靠程序中具体的汇编指令,和汇编指令对CS:IP、SS:SP、DS等寄存器的设置来决定的。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。