小甲鱼零基础汇编语言学习笔记第六章之包含多个段的程序

     在前面的几个章节中,我们的程序都是只有一个代码段,本章我们开始学习如何编写包含多个段的程序。
 
1、在代码段中使用数据
     首先考虑这样一个问题,计算以下8个数据的和,结果存放在ax寄存器中:
     0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
     在前面的课程中,我们都是累加某些内存单元中的数据,并不关心数据本身,可现在我们要累加就是已经给定了数值的数据。
     代码如下:
 1 assume cs:codesg
 2 codesg segment
 3      dw  0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
 4      mov bx,0
 5      mov ax,0
 6      mov cx,8
 7 s:  add ax,cs:[bx]
 8      add bx,2
 9      loop s
10      mov ax,4c00h
11      int 21h
12 codesg ends
13 end
View Code

 

     在该程序第一行中的"dw"的含义是定义字型数据,即define word。在这里,我们使用dw定义了8个字型数据(数据之间用逗号分隔),它们所占的内存空间大小为16个字节。
     将代码进行编译,连接后,使用debug进行调试,我们可以看到,在地址0B4FH:0000这个地址单元中,存放了我们定义的字型数据:
     
     在0B4FH:0010开始的地址单元中,存放了我们的指令:
     
     但是我们查看0B4F:0000这个地址单元时,它却并没有存放我们的指令(按理说应该是会存放我们的指令的,但是我们的指令却是存放在0B4FH:0010开始的地址单元中):
     
     出现这样的原因是,cpu读取的是指令所对应的机器码,从0B4F:0000开始的地质单元中,存放的是由数据0123h、0456h等我们预先定义好的字型数据转换成机器码所对应的指令,这样造成了cpu的误读。要解决这个问题,我们就应该给程序指定一个入口,而不是直接从定义字型数据的指令开始,所以程序应该改为如下所示:
     
 1 assume cs:codesg
 2 codesg segment
 3       dw  0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
 4 start:mov bx,0
 5         mov ax,0
 6         mov cx,8
 7  
 8 s:     add ax,cs:[bx]
 9         add bx,2
10         loop s
11  
12    mov ax,4c00h
13    int 21h
14 codesg ends
15 end start
View Code

 

     我们重新编译连接这个程序,然后使用debug调试后,使用r命令查看寄存器的情况,可以发现,cs:ip默认就指向了程序的第一条指令,这是因为我们在程序中定义了一个程序的入口,但是程序是根据最后的end来查找程序的入口的,这一点要特别提醒一下。debug调试如图所示:
     
 
2、在代码段中使用栈
     首先我们还是考虑一个问题,利用栈将程序定义的数据逆序存放,这个程序的代码应该如何来写?
     当我们看到逆序存放这样的字眼的时候,就应该首先想到栈,应为要实现逆序,就必定会用到栈“LIFO”这个特性(后进先出)。所以程序的代码如下所示:
 1 assume cs:codesg
 2 codesg segment
 3      dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
 4      dw 0,0,0,0,0,0,0,0 ;这里用dw定义8个字型数据,在程序加载后,将取得8个字的内存空间,存放这8个数据,我们在后面的程序中         ;就将这段空间当做栈来使用。
 5  
 6 start:mov ax,cs
 7         mov ss,ax
 8         mov sp,32 ;这里设置栈顶ss:sp指向cs:32
 9         mov bx,0
10         mov cx,8
11 s:     push cs:[bx]
12         add bx,2
13         loop s ;这里是将代码段0~16单元中的8个字型数据依次入栈
14  
15         mov bx,0
16         mov cx,8
17  s0:  pop cs:[bx]
18         add bx,2
19         loop s0 ;这里依次出栈8个字型数据到代码段0~16单元中
20  
21         mov ax,4c00h
22         int 21h
23  
24 codesg ends
25 end start
View Code

 

     在编译连接这段程序后,我们使用debug来查看程序的运行情况:
     
     我们是用t和d命令,来看看执行指令后,内存单元中的存储情况:
     
     
     此时使用d命令来查看内存的情况:
     

 

3、将数据、代码、栈放入不同的段中
     在前面的程序中,我们将数据、代码、栈放在了同一个段中,这样使程序显得很混乱。假如程序中的数据、代码、栈不大的话,这样做没问题,但是程序变大后,程序将变得很难看。在8086CPU中,如果数据、代码、栈的空间需求大于了64kb的话,这三者就不能存放在同一个段中,需要分开来存放。那么应该如何定义多个段呢?
     我们使用定义代码段一样的方法来定义多个段,在这些段里面定义需要的数据,或通过定义数据来取得栈空间。现在我们还是考虑同样的一个问题,将栈中的数据逆序存储到一个寄存器中,代码入下所示:
 1 assume cs:code,ds:data,ss:stack
 2  
 3 data segment
 4      dw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
 5 data ends
 6  
 7 stack segment
 8       dw 0,0,0,0,0,0,0,0
 9 stack ends
10  
11 code segment
12      start:mov ax,stack
13              mov ss,ax
14              mov sp,16 ;设置ss指向stack,设置ss:sp指向stack:16,cpu执行完这些指令后,就将stack段当做一个栈空间来使用。
15              mov ax,data
16              mov ds,ax ;想要ds:bx访问数据段,ds就要指向ax
17              mov bx,0
18              mov cx,8
19         s:  push [bx]
20              add bx,2
21              loop s
22  
23              mov bx,0
24              mov cx,8
25       s0:   pop [bx]
26               add bx,2
27               loop s0
28 code ends
29 end start
View Code
     在上面的代码中,我们定义了数据段data,栈段stack,代码段code,分别用ds,ss,cs指向这些段。这样做,看上去整个程序非常清晰明了。
     CPU如何处理我们定义的段中的内容,是当做指令来执行,当做数据访问,还是当做栈空间来使用,完全是靠程序中具体的汇编指令和汇编指令对CS:IP、SS:SP、DS等寄存器的设置来决定的。
posted @ 2017-08-02 23:41  GetMyCode  阅读(406)  评论(0编辑  收藏  举报