8086汇编学习之代码段数据段栈段与段地址寄存器
Posted Apollon_krj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了8086汇编学习之代码段数据段栈段与段地址寄存器相关的知识,希望对你有一定的参考价值。
同类学习笔记总结:
(一)、8086汇编学习之基础知识、通用寄存器、CS/IP寄存器与Debug的使用
(二)、8086汇编学习之DS寄存器、SS/SP寄存器
(三)、8086汇编学习之[BX],CX寄存器与loop指令,ES寄存器等
我们主要分析一下在单个段的程序与多个段的程序中,每个段寄存器的值是如何安排的,段的位置关系,内存大小等问题。
一、只有一个段的程序:
程序实例:
利用栈将程序中数据段中前8个word的数据按字型进行逆转。
assume cs:codeseg
codeseg segment
dw 0123H,0456H,0789H,0ABCH,0DEFH,0FEDH,0CBAH,0987H ;8word=16Byte
dw 0,0,0,0,0,0,0,0,0,0 ;栈设置的比实际要用的大10word=20Byte
start:
mov ax,cs
mov ss,ax
mov sp,36 ;给栈设置初始值
mov bx,0
mov cx,8 ;给CX循环计数寄存器设置初值
loop_push:
push cs:[bx]
add bx,2
loop loop_push
mov bx,0
mov cx,8
loop_pop:
pop cs:[bx]
add bx,2
loop loop_pop
mov ax,4C00H
int 21H
codeseg ends
end start
对于上面这段代码的分析:
由于这段程序只有一个段,该段为代码段,但是代码段前面有一部分数据,其中第一行为数据(dw命令开辟空间以word为单位,一个数据2Byte),第二行为为栈开辟的空间,之后的便是指令。由于指令寄存器CS:IP存放第一条指令的地址,CPU根据CS:IP的值开始解析并执行指令,但是默认情况下只有一个段的程序,其第一行指令/数据的地址就是CS:IP的值,在编译连接生成可执行程序时,就将程序的入口写入可执行文件描述信息中。但是这里第一行是数据而不是指令,将数据解析成指令其程序是不能正确运行的。而这时,start标识就标识了一个地址,end伪指令与start标识结合后,编译器根据该伪指令的信息就将satrt标识的地址写入可执行文件的描述信息中,根据可执行文件描述信息获取正确的指令其实地址存入CS:IP寄存器,那么CPU读取CS:IP时就能从正确的入口地址读取解析并执行指令了。
上图为例子程序的debug调试结果与对应的寄存器内存图,CS:IP初始值为start的标识地址(IP不为0,因为有数据段、栈段存在于代码段),这就将我们CS:IP=CS:0为程序入口地址的传统观念就打破,在指令前存在数据总是与指令存在于一个段显得格格不入。
我们可以看到当数据段、代码段、栈段存在于一个段时,会显得比较混乱,并且如果数据+栈+代码的量大于64K时就不能放在一个段中,所以我们将数据、代码、栈分别置于不同的段显得尤为迫切。
最终运行结果:
二、多个段的程序:
如何将code、data、stack置于不同段。其实很简单,我们再拿一个例子来说明,例子程序下:
目的:通过栈将0:0~0:F的值覆盖到给定数据段(程序中的数据段)的位置
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 ;36个字节的栈
stack ends
;代码段
code segment
start:
;设置数据段与栈段寄存器
mov ax,data
mov ds,ax ;设置数据段的段地址
mov ax,stack
mov ss,ax ;设置栈段的栈顶地址
mov sp,20H
mov bx,0
mov cx,8
mov ax,0
mov es,ax
push_pop_loop:
push es:[bx]
pop ds:[bx]
add bx,2
loop push_pop_loop
mov ax,4C00H
int 21H
code ends
end start
分析:段名相当于一个标号,它标识(标记)了该段的段地址,所以mov ax,stack、mov ax,data的含义就是:将栈段/或者数据段的段地址送入ax寄存器中,在mov ss,ax、mov ds,ax设置SS寄存器与DS寄存器,CPU就能识别内存中的一系列二进制哪些是数据、哪些是栈段、哪些是指令。
而在code段用end start标识CS:IP从start开始为程序的入口且是指令,这个入口在编译器下被编译器将伪指令end解析,然后将start的地址存放到文件的描述信息中去,可执行程序在被加载到内存中去时先设置CS:IP,那么CPU就从CS:IP即我们设置的start开始执行执行程序指令。对于这种情况,我们可以看到CS:0就是指令首地址,而不是只有一个段时的数据首地址,数据首地址在ds:0开始的地址,栈在ss:sp,这是,我们发现cs、ss、ds的特点意义显示了出来。
具体的寄存器的值与内存分布数据存储如下图所示:
测试结果:
(1)、关于一个段的大小问题:
一个段的数据的大小为N个字节,那么程序在加载的过程中为该段分配的内存地址大小为:((N-1)/16+1)*16。例如:
某code segment大小为16字节,那么它的装载占有空间为:16-1=15 ==> 15/16=0 ==> 0+1=1 ==> 1*16=16。
某stack segment大小为15字节,那么它的装载占有空间为:15-1=14 ==> 14/16=0 ==> 0+1=1 ==> 1*16=16。
某data segment大小为17字节,那么它的装载占有空间为:17-1=16 ==> 16/16=1 ==> 1+1=2 ==> 2*16=32。
小总结:实际大小为N,装载大小为M(M=n*16),则:M-16<=N<=M
所以说一个程序加载到内存以后,它所占用的总内存一定是16字节的倍数。
(2)关于end start:
如果修改最后一行的end start为end。因为start只是标识地址,而end start则是告诉编译器start是入口,如果修改最后一行的end start为end的话,那么编译器就不会理睬start,将默认的入口地址写入可执行文件描述信息。而默认的入口地址就是codeseg+dataseg+stackseg三段地址中最前面的地址(低地址)如果代码段(code)在我们编码时本来就在第一段,那么不加end start,编译器将首段地址(cs)写入可执行文件描述信息。但是如果stack/data段是第一段,那写入可执行文件描述信息的地址就是(ss)/(ds),将数据解析成指令,程序肯定是无法正常运行的,所以只有代码段在前面才能正确运行,因此为了不出现默认与实际编程段顺序安排偏离的情况,我们就要记得设置start与end start。
以上是关于8086汇编学习之代码段数据段栈段与段地址寄存器的主要内容,如果未能解决你的问题,请参考以下文章