Assembly(六)--寄存器总篇章

1.1 内存中的字存储

我们知道在8086CPU中,用16位寄存器来存放一个字,高8位(H)存放高字节,低8位(:L)存放低字节,在内存中存储时,由于内存单元是字节单元(一个单元存放一个字节),因此一个字要用两个连续的内存空间来存储(每个一个字节,共两个字节,16位,每个空间内存8位,1字节),字的低位字节存放在低地址,高位字节存放在高地址.
在这里插入图片描述
如图所示,不难看出 0,1 两个内存空间所组成的为 20000(4E20H) 其高位字节为4E 存放到1处,低位字节为20H存放到低处0
再来举个例子:18(0012H) 其高位为00 所以存放到3 处 低位为12 存放到2处
一个字单元就是存放一个完整的字所占的内存单元 ,这里的 01 23 都可以算作字单元
将起始地址为N的字单元简称为N地址字单元,如 一个字所占的内存空间为2,3 那么这个字单元就是2地址字单元,像图中的0012
为2地址字单元,4e20为0地址字单元

1.2 思考:

在这里插入图片描述
对于图中的表示,思考以下几个问题:

  1. 0地址单元中存放的字节数据是? 答:20H
  2. 0地址字单元中存放的字型数据是? 答:4E20H
  3. 2地址单元中存放的字节数据是? 答: 12H
  4. 2 地址字单元中存放的字型数据是? 答:0012H
  5. 1 地址单元中存放的字节数据是? 答:4EH
  6. 1 地址字单元中存放的字型数据是? 答:124EH(4686)
    所以这里的问题6就表明了 不一定是固定的两个 如 01 23 可以组成地址字单元 12 也可以

1.3 DS和[address]

我们知道 当cpu想要访问一个内存的单元时,必须要知道其物理地址,我们还知道,在8086CPU中的物理地址由段地址和偏移地址组成.DS寄存器就是存放要访问的段地址.
假设我们要读取10000H处的内容,我们可以用汇编语言来这样实现:

mov bx,10000H
mov ds,bx
mov al,[0]

这段指令的意思时将10000H中的第0块数据读入到AL中
mov指令的功能有:将数据送入寄存器,将一个寄存器的内容送入另一个寄存器.
这里还有一个功能:将内存中的数据送入一个寄存器,
这里的

mov al,[0]

就是用到了这一点
其中[xxx]表示一个内存单元,[0]表示一个内存单元的偏移地址,这个偏移是基于ds寄存器中的内容进行的,cpu在访问时,先去读取ds中的内容,后根据给定的偏移地址进行读取,因此在执行mov 指令将内存中的内容送入寄存器时,要先给出对应内存的地址,并将其送入ds寄存器中.
有人可能就想:为什么不能直接使用

mov ds,10000H

来将内存地址送入ds寄存器中呢?
很抱歉,并不可以,因为这是8086CPU的硬件设计问题,所以在送入ds寄存器中时,要从其他寄存器中传入值,再利用mov指令传送到ds寄存器中

1.3.1 思考

写出指令,将al中的数据送入到内存单元10000H处
答:

mov bx,10000H
mov ds.bx
mov [0],al

总结:mov指令的用法有:
在这里插入图片描述
继续来做问题:

问题1

给定内存空间如图所示,现执行以下指令,思考对应的寄存器变化:
在这里插入图片描述

mov ax,1000H
mov ds,ax
mov ax,[0]
mov bx,[2]
mov cx,[1]
add bx,[1]
add cx,[2]

首先毫无疑问的是 前两条指令是把ds的值变为10000H
随后开始mov ax 注意

mov ax,[0]

所处理的是将低位的值送入al中,高位的值送入ah中,从[0]开始计数,两个内存空间,一个内存空间占一个字节,一个字空间共占两个字节,因此[0]代表着1123
因此在这条指令执行后,ax变为:1123H

mov bx.[2]

执行后bx为6622

mov cx,[1]

执行后cx变为2211

add bx,[1]

bx现在的值为:6622
加上[1]处的2211 变为 8833
具体分析如王爽老师的表格所示
在这里插入图片描述

问题2

内存单元如图所示:
在这里插入图片描述
现有指令:

mov ax,1000H
mov ds,ax
mov ax,11316
mov [0].ax
mov bx,[0]
sub bx,[2]
mov [2],bx

这里的sub指令为相减的作用
具体来说:
sub a,b将a与b相减,结果存在a中.
前两条指令的作用是将10000H存入到ds寄存器中,很常规了
接下来看第三条指令,将11316送入ax中,但是很明显,11316并不是一个16进制数字,而是一个10进制数,这里需要将其转化为16进制数字;2C34H
因此这里的ax为2C34H
随后,将ax中的值送入到[0]处
因此10000H处变为34 10001处变为2C
在这里插入图片描述
然后继续:执行
mov bx,[0]
将2C34送入bx,bx变为2C34H
随后执行:
sub bx,[2]
也就是bx需要与1122做差 变为 1B12H
随后执行
mov [2],bx将bx中的内容存入内存[2]处,因此10003H变为1B 10002H变为12
在这里插入图片描述
我们接着来做个练习:
假设内存空间里:123B0H-123B9H里存放了一些数据,请使用汇编语言来进行前三个单元内的数据累加:

mov ax,123B0H
mov ds,ax
mov ax,0
add ax,[0]
add ax.[1]
add ax.[2]

来继续做一个练习吧
在这里插入图片描述
前两条指令毫无疑问的是把要访问的内存空间地址存入到ds寄存器里,方便下一步读取【x】
接着,将【0000】送入到ax 也就是第一个内存空间里的数据:
所以这里要访问的地址为:ds*16+0000=0100
因此要访问的AX内容是:2662
以此类推BX:E662
下面

mov ax bx 

ax变为:E662
后面的 mov ax 【0】 也没变
然后后面的 mov bx [0002]
将bx变成了:D6E6

add ax,bx

ax变为:FD48
然后

add ax. [0004]

这里的 0004 为 CCD6
所以ax变为:2C14

然后ax 变为0

mov al [0002]

将2号地址的高位送入 al中
因此 ax变为:00E6
后面的就不一一论述了
来学习下新内容:

1.4 栈

说起栈来,如果接受过正常的大学生的计算机课程的话,肯定都学过数据结构,数据结构中对于栈的描述是这样的:栈是一种先进后出的存储结构,这种操作规则被称为LIFO(Last In First Out)
这算是一种很重要的数据结构了
那么在经典的8086CPU中,栈显然也是存在的
可以将一段内存当作栈来使用,也提供了入栈、出栈指令。最基本的就是PUSH(入栈)和POP(出栈)了

push ax //将ax中的数据送入栈中
pop ax //将栈顶的数据取出送入ax中

来看一下王爽老师的图
在这里插入图片描述

注意,一个字是占两个字节,也就是两个方块的内容 高地址存放高8位,低地址存放低8位。
这里以 mov ax,0123H为例,push ax后 ax被送入了栈顶,栈顶就自然先是高地址后低地址了,所以栈里从底到顶就是01 23
这里有人可能就有疑问了:

  1. CPU是如何知道某一段空间被当作栈来使用的?
  2. 如何知道push 和pop指令中的操作对象,也即是栈顶、栈底底存在呢?

这里就引出来了两个寄存器
SS和SP寄存器,栈顶的段地址存放在SS中,偏移地址存放在SP中,任意时刻:SS:SP指向栈顶元素。push和pop指令执行时,CPU从SS和SP中得到栈顶的地址。
现在让我们来详细的探索一下push和pop的功能吧
以push ax为例:
3. sp=sp-2,在ax未入栈之前,sp的位置是ax位置的下面一个高地址处,为了方便下一步sp进行移动,移动到下一个ax即将到来的栈顶处
4. 随后将ax中的内容存入到SS:SP指向的内存单元处,SS:SP此时指向新栈顶
这里sp=sp-2是因为一个字占了两个内存空间,也就是了两个小块。
这里也可以看出,栈的走向是从高地址向低地址方向增长。
详情如图所示:
在这里插入图片描述
对于栈非空情况下的SP指针的情况,我想应该比较清楚了,那么对于栈空的情况下,或者栈刚刚被创建出来的情况下,sp指针是否也是跟SS指针一起呢?是否都在指着栈的最低处呢?
答案是否定的,事实上,sp指向的是栈的最高地址空间的下一个地址处,当执行了一次push指令后,sp指针会指向栈中的第一个元素,也就是栈顶
在这里插入图片描述
可以这样进行理解,如上图所示,内存地址中10000H-1000FH这段内存空间被开辟成了栈,假设这个时候只剩下了一个元素X
,那么在这个栈中,SS:SP指针都是指向了这个X元素,也就是10000EH处,(X是一个字,为两个字节,一个字节一个空格)
然后再执行pop指令之后,sp=sp+2 这里SP就从EH变成了10H ,这个时候的栈就是空的。所以栈空的时候,SP的地址就是栈的最高地址处的下一个地址的值。
因此顺理成章的找出POP指令的执行步骤:

  1. pop指令先将栈顶元素弹出到某个寄存器里
  2. 随后执行SP=SP+2的操作,随后SS:SP指向栈顶下单元的元素
    如图所示:
    在这里插入图片描述

1.5 栈顶超界问题

当我们连续的进行了push 或者连续进行了pop指令之后,会出现一种情况,在连续push之后,超过了栈的初始地址的情况
在这里插入图片描述
当连续pop之后,在栈为空的情况下仍执行pop指令
在这里插入图片描述
这种溢出的情况是十分危险的,很遗憾8086CPU并没有自动纠正的功能,因此在我们进行编程的时候要十分注意这种的情况。

1.6 PUSH、POP指令

push 寄存器
pop 寄存器
push 段寄存器
pop 段寄存器
push 内存单元
pop内存单元

For example

mov ax,1000H
mov ds,ax
push [1]
pop [1]

1.7

练习1:

将10000H-1000FH当作栈,将AX,BX,DS中的数据入栈

mov ax,10000H
mov ss,ax

mov sp,0010H

push ax
push bx
push ds

很简单吧,要注意的是 mov sp的时候 由于初始的栈是空的,这里的sp应该指向栈的最大地址的下一个地址处

练习2:

(1)将10000H-10000FH当作栈,初始状态为空
(2)AX=001AH BX=001BH
(3)将AX,BX入栈
(4)将AX,BX清零
(5)从栈中恢复AX,BX中的内容

mov ax,10000H
mov ss,ax
mov sp,0010H
mov ax,001AH
mov bx,001BH
push ax
push bx 
mov ax,0
mov ax,bx
pop bx
pop ax

很简单吧

练习3:

(1)将10000H-10000FH当作栈,初始状态为空
(2)AX=001AH BX=001BH
(3)利用栈,交换AX,BX中的元素

mov ax,10000H
mov ss,ax
mov sp,0010H

mov ax,001AH
mov bx,001BH

push ax
push bx
pop ax
pop bx

至此,我们学会了所有的段:数据段、代码段、栈段
CPU去访问这三个不同的段分别需要去看:ds、cs;ip、ss:sp

练习4

在这里插入图片描述
编写汇编,使10000H-10000FH中的8个字,逆序到20000H-2000FH中

mov ax,1000H
mov ds,ax
mov bx,2000FH
mov ss,bx
mov sp,0010H
push [0]
push [2]
push [4]
push [6]
push [8]
push [A]
push [C]
push [E]

还有一种写法:就是将1000H-1000FH作为栈,然后弹出 这样写:

mov ax,2000H
mov ds,ax
mov bx,1000FH
mov sp,0010H

pop[E]
pop[C]
pop[A]
pop[8]
pop[6]
pop[4]
pop[2]
pop[0]

当然也是比较简单的。
ok下班!

  • 13
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值