数据段data segment 堆栈段stack segment 代码段code segment的 段地址 有什么关系?
发布网友
发布时间:2022-05-02 21:08
我来回答
共1个回答
热心网友
时间:2023-10-09 13:05
对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。我们可以将一组长度为N(N less or equal to 64KB)、地址连续、起始地址为16的倍数的内存单元作专门存储数据的内在空间,从而定义了一个数据段。比如用123B0H -- 123B9H这段内存空间来存放数据,我们就可以认为,123B0H -- 123B9H这段内存是一个数据段,它的段地址为123B0H,长度为10个字节。
如何访问数据段中的数据呢? 将一段内存当作数据段,是我们在编程时的一种安排,可以在具体操作的时候,用DS存放数据段的地址,再根据需要,用相关指令访问数据段中的具体单元。
比如,将123B0H -- 123B9H 的内存单元定义为数据段。现在要累加这个数据段中的前3个单元的数据,代码如下.
mov ax, 123BH
mov ds, ax ; 将123BH送入ds中,作为数据段的段地址
mov al, 0 ; 用al 存放累加结果
add al, [0] ; 将数据段第一个单元(偏移地址为0)中的数值加到al中
add al ,[1] ; 将数据段第二个单元(偏移地址为1)中的数值加到al中
add al, [2] ; 将数据段第三个单元(偏移地址为2)中的数值加到al中。
栈 : 栈是一种具有特殊访问方式的存储空间。它的特殊性在于,最后进入这个空间的数据,最先出去。
8086 CPU 提供相关的指令来以栈的方式访问内存空间,这意味着,在基于8086 编程的时候,可以将一段内存当作栈来使用。
8086 CPU 提供入栈和出栈指令,最基本的两个是 PUSH(入栈) 和 POP (出栈)。 比如 ,push ax 表示将寄存器ax 中的数据送入栈中,pop ax 表示从栈顶取出数据送入ax. 8086 CPU 的入栈和出栈操作都是以字为单位进行的。
下面举例说明,我们可以将 10000H -- 1000FH 这段内存当作栈来使用。
mov ax, 0123H
push ax
mov bx, 226H
push bx
mov cx, 1122H
push cx
pop ax
pop bx
pop cx
这里有两个问题。
其一, 我们将10000H - - 1000FH 这段内存当作栈来使用,CPU执行push 和 pop 指令时,将对这段空间按照栈的后进先出的规则进行访问。但是,一个重要的问题是,CPU如何知道 10000H -- 1000FH 这段空间被当作栈使用?
其二, push ax 等入栈指令执行时,要将寄存器中的内容放放当前栈顶单元的上方,成为新的栈顶元素;pop ax 等指令执行,要从栈顶单元取出数据,送入寄存器中。显然 push, pop 在执行的时候,必须知道哪个单元是栈顶单元,可是,如何知道呢?
8086 CPU 中,有两个寄存器,段寄存器 SS 和寄存器 SP,栈顶的段地址存在SS中,偏移地址存放在SP中,任意时刻,SS:SP指向栈顶元素。 push 指令和pop指令执行时,CPU从SS和SP中得到栈顶的址址。
现在, 我们可以完整的描述 push 和 pop 指令的功能了,例如 push ax.
push ax 的执行, 由以下两步完成。
1. SP = SP -2, SS:SP 指向当前栈顶前面的单元,以当前栈顶前面的单元为新的栈顶;
2. 将ax 中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶。
在masm中有一条重要的伪指令 assume ,下面给出 ASSUME 的具体用法
Setting the ASSUME Directive for Segment Registers
Many of the assebler instructions assume a default segment. For example,
JMP assumes the segment associated with the CS register, PUSH and POP assume
the segment associated with the SS register, and MOV instructions assumes the segments
associated with the DS register.
When the assebler needs to reference an address, it must know what segment contains the address. It finds this by using the default segment of group addresses assigned with the
ASSUME directive. The syntax is :
ASSUME segresister : seglocation [, segregister : seglocation ]
ASSUME dataregister; qualifiedtype [, dataregister : qualifiedtype]
ASSUME [regiser: ] NOTHING [, register : NOTHING]
ASSUME register : FLAT [, register : FLAT ]
The ASSUME directive can define a segment for each of the segment registers. the
segregister can be CS, DS, ES, or SS ( and FS and GS on the 80386/486).
段寻址伪指令 ASSUME
格式 : ASSUME [段寄存器]: [ 段名], [段寄存器]: [段名],...
功能 : 用于告知汇编程序,段寄存器CS、DS 、ES 和SS 的内容将被设定为那些段或组的段址。 所以 代码段,数据段,堆栈段的 地址是 可 以人为设置的。
在机器刚开机时,堆栈是还没有建立起来的,此时是不能用栈的。追问非常感谢你的回答,还有下面一个问题:
像这样一个程序
像这样一个程序,我们让cpu执行这个程序,它会先把程序加载到内存中,程序加载到内存,系统会给这个程序分配data 段 stack 段 code 段来存储这个程序,这里存储程序的时候,系统怎么分配data段和stack段 和code段的内存空间来存储不同的段?cs ds ss它们有 什么关系?系统根据什么规则来分配cs ds ss 的?
追答
你这里的系统是指os么? 要知道legacy bios 是全部用汇编语言写的,在它引导os之前,是不存在os这个概念的,所以并不是系统分配的,从你按下power button, 计算机的上电时序完全是人为控制的(可以用CPLD),当所有的电都 standby了,南桥发一个platform ret 信号给cpu,此时CPU去执行第一条指令,这个时候,所有的chipset,memory都还没有完成初始化,也就是说,是还不能用的,所以i386就人为的规定第一条指令放在哪里,这条指令是放在bios firmware下面的,下图是haswell-ep 处理器的基本架构。
CPU 并不care第一条指令入在哪里,它只管扔出这个地址,谁拥有这个地址,谁就做出响应,然后BIOS开始做事情,它要打一些microcode, 注意些时内存还不能用,BIOS是rom,很多指令是不能执行的,比如call,此时它会把BSP(现在都是多处理器结构,BSP初始化完成之后,去下指令初始化别的CPU,叫AP)的L2 cache作为临时ram.然后去初始化内存,load 各种option rom,最后去引导os,在这一段时间,计算机做的每一步都是人为控制的,所以并不存在系统来分配这个概念,你所说的段,仅仅是一段连续的内存(逻辑上的),然后你指定一个寄存器去放段的起始地址(给编译器看的),比如: 下面代码的mov ds,ax
assume cs:code
data segment
db 'Welcome to masm!', 0
data ends
code segment
start: mov dh, 8
mov dl, 3
mov cl, 2
mov ax, data
mov ds, ax ; 此时ds 指向了data段, 完成了ds 的赋值
mov bx, 0 ; ds: bx 指向data 段中的第一个单元.
学计算机 ,就要把它学透,从power on,到 application的执行。