浅谈汇编中的ds,cs与ip,ss与sp寄存器
ds,cs,ss都是汇编中的段寄存器⽽ip状态与控制寄存器,sp为索引寄存器,cs与ip搭配使⽤,ss与sp搭配使⽤;虽然ds,cs,ss都是段寄存器但是他们的作⽤却⼤不相同,(基于8086CPU)
(1)ds (Data Segment) 数据段寄存器
当我们想读取⼀个指定的内存单元上的数据时,我们可以通过把数据所在的内存段地址放⼊到ds寄存器中去,然后读取或者写⼊数据时就可以通过该ds寄存器内的段地址偏移得到我们的数据,或者偏移后写⼊数据
例如,我们现在想读取1000:0这个地址上的数据并且往1000:1这个地址写⼊⼀个数据时:
mov ax,1000//把1000这个段地址写⼊到ax这个通⽤寄存器中
mov ds,ax  //把ax中的段地址1000送⼊ds数据寄存器
mov bx,[0]//把1000这个段地址偏移0位即1000:0地址上的内容读取到bx寄存器去
mov cx,2020//把2020这个数据送⼊到cx寄存器
mov [1],cx  //最后把cx寄存器中的内容送⼊1000段地址偏移1位即1000:0001地址去
注:不能直接把数据送⼊到ds寄存器,因此需要先把数据送⼊⼀个通⽤寄存器再送⼊数据段寄存器
(2)cs(代码段寄存器)与ip(指令指针寄存器)
当我们想要声明⼀段内存单元为代码段让CPU去执⾏我们代码段⾥的指令或者代码时,因为指令和代码都是存放在内存单元中的;因此我们需要告诉CPU⼀个地址,让它去执⾏该地址的内存单元中的指令或代码;
其中:
cs(Code Segment)是⽤于存放我们指令所在的段地址
ip (Instruction Pointer)寄存器是⽤于存放指令所在的地址的偏移地址
注:因为8086CPU是16位的,但是物理地址是20位的,它内存的寄存器只能表现16位的地址,因此使⽤了ip寄存器来存放偏移地址
在我们想要执⾏1000:0123地址上的⼀个指令时,则先修改cs与ip的值:
通过r来修改寄存器的内容
r cs  回车输⼊段地址1000
r ip  回车输⼊偏移地址
(3)ss(栈寄存器)与sp(栈指针寄存器)
我们知道栈是后进先出(last in frist out)的且始终有⼀个栈顶指针指向栈顶的,当栈内⽆元素时,栈顶指针指向的是栈底;我们来讲⼀下汇编中的栈是怎样的:
CPU中设置了两个两个寄存器⽤于存放栈顶的段地址与偏移地址,分别为:
ss (Stack Segment):⽤于存放栈顶的段地址
sp(Stack Pointer):⽤于栈顶的偏移地址
其中⼊栈的步骤分为两步:
⼊栈汇编指令:push 寄存器名 ----->把寄存器中的值⼊栈
(1)sp = sp - 2
(2)然后把⼊栈的寄存器内的数据放⼊栈的内存单元中
出栈的顺序也是分为两步:
出栈汇编指令:pop 寄存器名 —>把栈顶元素出栈保存到寄存器中
(1)sp = sp + 2
(2)把该内存单元中的数据送到出栈的寄存器中
注:⼊栈与出栈都是按⼀个寄存器即⼀个字(两个字节)来⼊栈或出栈的(8086是16位的结构)
注:
(1)为什么是sp偏移地址加2减2呢
因为push 与 pop 汇编指令执⾏是占两个字节的
(2)当栈为空时,当栈为空时,栈顶指针指向栈底内存单元地址+2(因为⼊栈时会先减2)
例如:1000:0 - 1000:000f为栈内存,空栈时,栈的偏移地址sp为多少?
答案:因为此时空栈,栈底的字内存单元地址为000e,加2即是0010
还有⼀种情况,栈的内存单元地址是1000:0 - 1000:ffff;空栈时,偏移地址sp为多少?
答案:此时空栈的栈底字内存单元为为fffe,加2即时空栈时的栈顶地址;此时加后为10000,只能存放16位,因此剩下0000;此时满栈与空栈的偏移地址均为0
(地址从上到下是上⾯低位到下⾯⾼位的)
在指定的内存输⼊指令测试⼊栈与出栈:
//把bx、cx⼊栈到1000:0 - 1000:f 这段内存中去
mov ax,1000
mov ss,ax
mov sp,0
//把数据送⼊bx、cx寄存器
mov bx,1234
mov cx,4567
//⼊栈
push bx
push cx
//出栈,后进先出;cx会先出
pop bx //会把cx放到bx中
pop cx
当我们想指定⼀段内存单元为栈时,因为8086CPU中没有预防我们栈溢出(超出栈内存继续⼊栈或出栈)问题;因此我们使⽤时需要⼩⼼使⽤,不要访问到其他内存单元去;⽽且尽量不要把ds,cs,ss同时使⽤⼀⽚内存
汇编指令有多少个
其实这个数据段,代码段和栈都是我们⼈为给他们指定的,我们可以同时把⼀段连续的内存分别指定
为数据段、代码段和栈(但尽量不要这样做);在CPU中它看到的只是⼀个完整的逻辑单元