8086汇编学习之DS寄存器、SS/SP寄存器

相关博客:8086汇编基础知识、通用寄存器、CS/IP寄存器与Debug的使用

一、DS寄存器

一个8086CPU寄存器均是16位的,而数据类型有以下两种:
1Byte = 8bit (字节型数据)
1word = 2Byte = 16bit (字型数据)

16位寄存器存储一个字,而在内存中需要两个空间连续的字节存储一个字。(高位地址存放高位数据,低地址存放低位数据)。 任何两个地址连续的内存单元,N号单元与N+1号单元可以看做两个字节单元,也可以看做一个地址为N的字单元中的低字节单元(N)和高字节单元(N+1)。举例如图:
8086汇编学习之DS寄存器、SS/SP寄存器_第1张图片

那么CPU如何访问这些地址中的数据呢?我们知道CPU通过CS:IP寄存器获取下一条指令的地址。而要获取数据就需要通过DS寄存器,DS段地址寄存器:存放要访问数据的段地址。

-e 2000:0000 11 22 33 44    //向2000:0000设置初始值
-a
mov bx,2000 
mov ds,bx   //设置DS段地址寄存器的值为2000
mov al,[0]  
mov cx,[1]  

以上指令将2000:0000地址单元的字节型数据值读取到al(8位)寄存器中。将2000:0001地址单元的字型数据读取到cx(16位)寄存器中。[]表示内存单元,其中的0表示偏移地址,对哪个段偏移的呢?8086CPU会自动读取ds的数据最为内存单元的段地址。最终的寄存器值为:
BX=2000
DS=2000
AX=0011
CX=3322

测试如下:
8086汇编学习之DS寄存器、SS/SP寄存器_第2张图片

同样,DS和CS等段地址寄存器都是相通的,不支持将数据直接送进段地址寄存器中,要通过其它寄存器中转,这属于硬件设计问题。

当然,我们将[n]可以存向al、ax,也可以将al、ax存向[n](各种合理组合,都是可以的):

mov [0],al  //al的值存到ds:[0]的地址中
mov [2],ax  //ax字型数据,al存入ds:[2]和ah存入ds:[3]
add ax,[0]  //相加的结果保存到ax中
add [0],ax  //相加的结果保存到ds:[0]与ds:[1]的地址中
sub al,[0]  //相减的结果保存到al中
sub [0],ah  //相减的结果保存到ds:[0]的地址中
...
以下几条指令
mov ds,1000
add ds,ax 
sub ds,ax
...等等都是不合理的

在内存中,数据与指令是完全没有区别的,而在寄存器中,数据与指令是有区别的,因为不同寄存器保存的是不同类型数据的地址(CS保存指令数据段地址,DS保存操作数段地址…)而CPU负责读取CS:IP确定下一条指令存放的地址,读取DS得知将要处理的数据的段地址,根据指令中偏移地址找到具体数据。所以说,汇编程序通过CPU读取寄存器寻找指令与数据,进行一系列操作。

二、SS/SP栈段寄存器:

在C、C++、Java…一系列高级语言中,我们都使用过栈这种数据结构,在汇编这种古老的语言中,栈当然也是存在的。那么如何在汇编中使用寄存器描述一个栈空间?就需要用到SS与SP寄存器。对于8086汇编来说,一次入栈/出栈的字节数都是2字节即1字。

push指令 –> ①SP - 2 –> ②将字型数据存放到SS:SP地址中
pop指令 –> ①将SS:SP地址的字型数据取出来 –> ②SP + 2
上面这个步骤就像C中我们入栈前先移动栈顶指针,再入栈。出栈时先取出数据,再将栈顶指针向栈底移动(属于“满栈”(先移动Top再压栈))。

指令使用规则:
push 16bit寄存器/push “DS:[n]”:寄存器可以为 (ax\bx\cx\dx\ds\es\ss\sp…),8位寄存器不可以。(SP-2,从指定寄存器/“DS:[n]”地址中读取数据入栈)
pop 16bit寄存器/pop “DS:[n]”:(从栈中取出数据放到寄存器/“DS:[n]”地址中,SP+2)
(在Debug中“DS:”需要省略,只用一个[n],CPU自动会从DS中读取栈段地址。可以从寄存器或DS:[]地址中读取数据入栈但是不能“push FFFF”将值直接入栈,出栈亦然)

注意:栈底在高内存地址,任意时刻,SS:SP指向栈顶元素(那么SS:SP两个寄存器就类似于一个Top指针),所以说栈在哪里是由SS:SP决定的(给SS:SP初值即为栈底)。但是栈的大小是没有严格界限的,当SP-2<0时,又会从FFFF开始减,但是不保证其权限。可能越界错误,可能不越界错误(不会自动判断栈满/栈空)。而关于栈的越界问题需要程序员自己注意(这个比C更自由,但更不安全)。

eg:
/*通过Debug的-r修改SS:SP*/
-r SS
2000
-r SP 
10
/*或者:通过寄存器中转修改SS:SP*/
mov ax,2000 
mov ss,ax
mov sp,10   //IP寄存器不能直接用值修改,但是SP寄存器可以

//就是指定栈底地址为2000:10,此时SS:SP=2000:0010
mov ax,1234
mov bx,5678
push ax     //SP-2=000E,2000:E与2000:F中分别存放3412
push bx     //SP-2=000C,2000:C与2000:D中分别存放7856
pop cx  //出栈CX=5678,SP+2=000E   
pop dx  //出栈DX=1234,SP+2=0010

我们将入栈过程分析一下,如图所示:
8086汇编学习之DS寄存器、SS/SP寄存器_第3张图片
出栈则是其逆过程,先出值到寄存器中,再移动栈顶指针(修改SS:SP寄存器值)

明白了CS、DS、SS寄存器后我们就可以为数据段(text)、代码段(code)、栈段(stack)总结一下了:

数据段:存放数据的段(段地址在DS中)
代码段:存放代码的段(段地址在CS中)
栈段:存放临时性数据的并且由SS:SP维护的段(段地址在SS中)

三、DS、SS、SP寄存器混合使用:

(例题为王爽老师的《汇编语言》第三章检测点3.2)
将10000H~1001FH中的八个字,逆序复制到20000H~2001FH中,如何用栈做?
法一:
从ds:[]入栈,ds:[]是源(10000H~1000FH),栈是目标(20000H~2000FH)

//设置数据段地址
mov ax,1000H
mov ds,ax   

//设置栈段地址
mov ax,2000H
mov ss,ax
mov sp,10H  

//从数据段向栈段复制:是从指定数据段(10000H~1000FH)向指定栈段(20000H~2000FH)存放数据
push [0]
push [2]
push [4]
push [6]
push [8]
push [A]
push [C]
push [E]

法二:
出栈至ds:[],栈是源(20000H~2000FH),ds:[]是目标(10000H~1000FH)

//设置数据段地址
mov ax,2000H
mov ds,ax

//设置栈段地址
mov ax,1000H
mov ss,ax
mov sp,0H

//从栈段向数据段复制:是将指定栈中(10000H~1000FH)值放到指定的数据段中(20000H~2000FH)
pop [E]
pop [C]
pop [A]
pop [8]
pop [6]
pop [4]
pop [2]
pop [0]

混合图解:
8086汇编学习之DS寄存器、SS/SP寄存器_第4张图片

法一测试:
先给10000H~1000FH设定初始值:
8086汇编学习之DS寄存器、SS/SP寄存器_第5张图片

执行所有出栈指令后,可以看到10000H~1000FH的值被按字(不是安字节)逆序复制到了20000H~2000FH。
8086汇编学习之DS寄存器、SS/SP寄存器_第6张图片

你可能感兴趣的:(8086汇编学习之DS寄存器、SS/SP寄存器)