本博文系列參考自<<汇编语言>>第三版,作者:王爽
在前面的介绍的程序中仅仅有一个代码段。那么假设我们须要将代码,数据分别存储在不同的内存空间应该怎么办呢?我们知道我们不可能随便使用不论什么一段内存空间,由于我们这段内存地址空间可能存储着很重要的内容。事实上,这仅仅是我们考虑的太多啦,一旦我们将程序加载内存后,操作系统为我们分配的用于程序执行的内存空间都是安全的,绝对不会与其它程序的内存空间相重叠的。
往往程序获取内存有两种方式:一种是在程序加载内存的时候操作系统已经分配好的内存空间,第二种是我们在程序执行的时候须要申请额外的内存空间进行相关的存储。(这里第二种方式为动态分配我们临时不讨论)
前面我们已经有过这种经验,我们在程序中定义一个段。然后当程序加载内存的时候,操作系统会自己主动为我们分配一个内存段用于存储。我们从还有一方面来说说这个问题。假设我们程序中不仅包含代码,还有数据,甚至还有后面学习到的栈空间。
我们假设把这些数据都保存到一个段,那么显然在程序设计和使用上难免逻辑不够清晰,往往我们都是在程序中定义不同的段来进而在内存空间中分别存储代码。数据和栈。
6.1在代码段中使用数据
这里我们先考虑这样一个问题,我们须要计算例如以下八个数的和:
0123h、0456h、0789h、0abch、0defh、0fedh、0cbah、0987h
这里我们要解决问题须要将上面这八个数据依次存储到某个寄存器中。然后再用某个寄存件保存累加结果进行累积。可是这样显然不够smart,假设能够用循环就最好了。那么问题来了,既然要用循环我们就须要把这8个数据依次存储到内存中的一段连续区域中。那么假设找到一段连续的内存空间呢?
我们能够在程序中定义一段这8个数据,然后当程序经过编译链接成可运行文件加载到内存后。这8个数据就存储在了连续的内存空间了,接下来我们就能够使用循环进行累积求和了。
程序代码例如以下:
程序主要解释。当中dw为定义字型数据 define word,这里一共定义了8个字型数据。一共16个字节。 bx用于循环的索引,每次循环内存向后移动两个字节,ax为用于存储累积结果,cx用于循环计数器,初始值为8。每循环一次cx=cx-1.程序非常easy,相信大家都能理解。这里特别说明。我们定义了一个代码段code。
由于dw存储在代码段的最開始位置,所以第一个字型数据的内存偏移定义为0.所以事实上际内存地址为cs:0 紧接着0456h在其后的两个字节为cs:2.
接下来我们将该程序保存为vpeot.asm同一时候编译链接为vpoet.exe(上图代码中mov ax,4c00h忘记加逗号了,囧),然后再用debug工具载入该程序,例如以下:
从上图中能够看到cs=14FB,能够知道程序是从CS:0000即14FB:0000開始运行的。
我们用u命令打印程序指令,发现的确是从14FB:0000開始运行的。然而前面的的一部分并不是程序真正的指令。我们能够大胆推測前面的内容为存储我们定义的dw数据的指令,如今我们使用d命令查看14FB:0000的内存内容。
能够看出从内存14FB:0000開始的16个字节为我们定义的8个字型数据。
那么假设要查看实际的汇编指令须要从14FB:0010開始查看:
\
这些对了吧。
程序被载入后,整个程序的前16个字节为我们定义的8个字型数据。后面的才为我们的汇编指令。
那么假设我们想运行我们的汇编指令。仅仅须要将寄存器IP的值设为0010就可以。‘
这里另一个问题就产生了,假设我们不是使用debug工具来改变IP而是直接将程序加载内存中。程序的入口地址并不是我们的汇编指令,怎么办呢?
我们能够为程序添加一个标号来标示程序的入口,比方:
这里,我们在第一句汇编指令前面加上了一个标号start同一时候在伪指令end后也加上了start 这里的start就标示了程序的入口,表明一旦程序载入内存后,CS:IP就会指向第一条指令mov bx,0 我们能够再次编译链接生成exe然后通过debug工具载入到内存中来证实这一点。
先看我们看到程序的入口已经指向了第一条指令,就不须要我们像之前那样更改IP的地址了。
6.2在代码段中使用栈
相同先看一个问题,有8个数据存储在内存的一段连续的空间中。
0123h、0456h、0789h、0abch、0defh、0fedh、0cbah、0987h
我们须要编程实现这8个数据的逆序存放。
这个问题的思路是,如果这8个数据存储在CS:0~CS:F这段内存空间中,我们能够利用栈存储空间的特点,将这8个字型数据依次压入栈的内存空间中,然后依次弹出到这CS:0~CS:F这段内存空间中。便能够显示数据的逆序存放。这样的思路须要我们在程序中定义一段内存空间作为栈空间使用。实现代码例如以下:
assume cs:code code segment dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h 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 mov bx,0 mov cx,8 s:push cs:[bx] add bx,2 loop s mov bx,0 mov cx,8 s0:pop cs:[bx] add bx,2 loop s0 mov ax,4c00h int 21h code ends end start
这里mov sp,30h须要注意,由于我们在程序的開始首先定义了8个字型用于存储须要逆序存储的数据,同一时候分配了16个字型的数据用于栈空间使用,所以一共同拥有24个字型,即48个字节的存储空间。所以SP指向栈顶的地址为:SS:IP=CS:30H。
接下来我们编译链接该程序,同一时候debug载入到内存中,例如以下所看到的:
前面的程序中我们都是将数据。代码,栈放到一个栈空间中。这种缺陷就是程序的逻辑不够清晰。另一个问题是假设代码数据以及栈空间的大小超过一个段的大小,显然他们就不能被放到一个段中了。所以在实际的程序设计中。我们往往将代码,数据以及栈分别定义到不同的段空间中。
我们还是用6.2的问题来进行描写叙述。这次我们将代码,数据。栈空间分配到不同的段中,程序例如以下:
assume cs:code,ds:data,ss:stack data segment dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h data ends stack segment dw 0,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 mov ax,data mov ds,ax mov bx,0 mov cx,8 s:push [bx] add bx,2 loop s mov bx,0 mov cx,8 s0:pop [bx] add bx,2 loop s0 mov ax,4c00h int 21h code ends end start
关于程序的说明:
1.定义多个段的方法与前面定义一个段的方法同样,不同的段有不同段名。
2.对段名的引用。
既然已经定义了不同的段,那么怎样通过段名来訪问呢。实际上段名代表了该段的段地址。比方如果data段中的数据0abch的地址为data:6。要将该地址的内存传入bx:
mov ax,data
mov ds,ax
mov bx,ds:[6]
3.代码段,数据段。栈段的由我们安排。我们定义了三个段名data,code,stack并不是是存储就代表data必须存储数据,code必须存储代码。我们能够把data用于存储代码,code用于存储数据都是能够的。
另外assume cs:code,ss,stack,ds:data并不是是这样关联后cs就指向了代码段,或者说ss指向了栈段。而这里仅仅是编译器让我们将code段与cs相关联。即把code段的段地址存入cs。CPU怎样处理我们定义段的内容,是当做指令运行还是数据訪问,这全然由汇编程序本身决定。也由cs:ip,ds,ss:sp的值来确定的。
OK。关于本章的内容就介绍到这里了。