汇编(四):寄存器(内存访问)

前言

从访问内存的角度继续学习寄存器;
 

内存中字的存储

在0地址处开始存放20000(4E20H):


注意:0号单元是低地址单元,1号单元是高地址单元。

问题:
(1)0地址单元中存放的字节型数据是多少? 20H
(2)0地址字单元中存放的字型数据是多少? 4E20H
(3)2地址单元中存放的字节型数据是多少? 12H
(4)2地址字单元中存放的字型数据是多少? 0012H
(5)1地址字单元中存放的字型数据是多少? 124EH

结论:

任何两个地址连续的内存单元,N 号单元和 N+1号单元,可以将它们看成两个内存单元 ,也可以看成一个地址为 N 的字单元中的高位字节单元和低位字节单元。

DS 和 [address]

CPU 要读取一个内存单元的时候,必须先给出这个内存单元的地址;在8086PC 中,内存地址由段地址和偏移地址组成。

8086CPU 中有一个 DS 寄存器,通常用来存放要访问的数据的段地址。

扫描二维码关注公众号,回复: 13404464 查看本文章

例如:我们要读取10000H单元的内容可以用如下程序段进行:

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

上面三条指令将10000H(1000:0)中的数据读到al中。

执行指令时,8086CPU 自动取 DS 中的数据为内存单元的段地址。

如何用 mov 指令从10000H中读取数据?

10000H表示为1000:0(段地址:偏移地址),将段地址1000H放入 ds,用 mov al,[0] 完成传送(mov 指令中的[]说明操作对象是一个内存单元,[]中的0说明这个内存单元的偏移地址是0,它的段地址默认放在 ds 中);

如何把1000H送入 ds?

传送指令 mov ax,1mov ds,ax

mov ds,1000H 是不行的,8086CPU 不支持将数据直接送入段寄存器的操作,ds 是一个段寄存器(硬件设计的问题),所以 mov ds,1000H 是非法的。

数据 → 通用寄存器 → 段寄存器
 

问题:

写几条指令,将 al 中的数据送入内存单元10000H?

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

字的传送

因为 8086CPU 是16位结构,有16根数据线,所以,可以一次性传送16位的数据,也就是一次性传送一个字。

比如:
 
问题1:

存中的情况如下图,写出下面指令执行后寄存器ax,bx,cx中的值。

ax = 1123H
bx = 6622H
cx = 2211H
bx = 8833H
cx = 8833H

问题2:

内存中的情况如下图,写出下面指令执行后寄存器ax,bx,cx中的值。

ax  = 11316 = 2c34H
bx = 2c34H
bx  = 1b12H

mov,add,sub 指令

已学mov指令的几种形式:

mov 寄存器,数据
mov 寄存器,寄存器
mov 寄存器,内存单元
mov 内存单元,寄存器
mov 段寄存器,寄存器

根据已知指令进行推测:

mov 段寄存器,寄存器
→ mov 寄存器,段寄存器

验证:

mov 内存单元,寄存器
→ mov 内存单元,段寄存器
→ mov 段寄存器,内存单元
 

add 和 sub 指令同 mov 一样,都有两个操作对象。

 

数据段

前面讲过,对于 8086PC 机,我们可以根据需要将一组内存单元定义为一个段(可以是代码段、数据段等)。

我们可以将一组长度为N(N≤64K)、地址连续、起始地址为16的倍数的内存单元当作专门存储数据的内存空间,从而定义了一个数据段。

比如我们用123B0H~123B9H这段空间来存放数据:

  • 段地址:123BH
  • 长度:10字节

如何访问数据段中的数据呢?

将一段内存当作数据段,是我们在编程时的一种安排,我们可以在具体操作的时候 ,用 ds 存放数据段的段地址,再根据需要,用相关指令访问数据段中的具体单元。

示范

我们将123B0H~123BAH的内存单元定义为数据段,我们现在要累加这个数据段中的前3个单元中的数据,代码如下:

小结

(1)字在内存中存储时 ,要用两个地址连续的内存单元来存放,字的低位字节存放在低地址单元中,高位字节存放再高地址单元中。

(2)用 mov 指令要访问内存单元,可以在 mov 指令中只给出单元的偏移地址,此时,段地址默认在 DS 寄存器中。

(3)[address] 表示一个偏移地址为 address 的内存单元。

(4)在内存和寄存器之间传送字型数据时,高地址单元和高8位寄存器、低地址单元和低8位寄存器相对应。

(5)mov,add,sub 是具有两个操作对象的指令。jmp 是具有一个操作对象的指令。

(6)可以根据自己的推测,在 Debug 中实验指令的新格式。
 

→ 检测1 ←

(1) 在DEBUG中,用 d 0:0 1f 查看内存,结果如下:

0000:0000 70 80 F0 30 EF 60 30 E2-00 80 80 12 66 20 22 60 

0000:0010 62 26 E6 D6 CC 2E 3C 3B-AB BA 00 00 26 06 66 88 

下面的程序执行前,AX=0BX=0,写出每条汇编指令执行完后相关寄存器中的值:

mov ax,1

mov ds,ax

mov ax,[0000]  ax= 2662H 

mov bx,[0001]  bx= E626H 

mov ax,bx      ax= E626H 

mov ax,[0000]  ax= 2662H 

mov bx,[0002]  bx= D6E6H 

add ax,bx      ax= FD48H 

add ax,[0004]  ax= 2C14H 

mov ax,0       ax=   0   

mov al,[0002]  ax= 00e6H 

mov bx,0       bx=   0   

mov bl,[000c]  bx= 0026H 

add al,bl      ax= 000CH 

(2) 内存中的情况如图所示

各寄存器的初始值:cs=2000h,ip=0,ds=1000h,ax=0,bx=0;

① 写出CPU执行的指令序列(用汇编指令写出)。

② 写出CPU执行每条指令后,CS、IP和相关寄存器的数值。

③ 再次体会:数据和程序有区别吗?如何确定内存中的信息哪些是数据,哪些是程序?

CS=2000H,IP=0,DS=1000H
AX=0,BX=0

mov ax,6622H 
CS=2000H IP=3 DS=1000H AX=6622H BX=0000H

jmp 0ff0:0100 
CS=1000H IP=0 DS=1000H AX=6622H BX=0000H 

mov ax,2000H 
CS=1000H IP=3 DS=1000H AX=2000H BX=0000H

mov ds,ax 
CS=1000H IP=5 DS=2000H AX=2000H BX=0000H

mov ax,[0008] 
CS=1000H IP=8 DS=2000H AX=C389H BX=0000H

mov ax,[0002] 
CS=1000H IP=000B DS=2000H AX=EA66H BX=0000H

数据和程序在计算机中都是以二进制的形式存放的,在区别程序和数据时,关键是看段地址,如果段地址是 ds 段,说明该内存存放的是数据,如果段地址是 cs 段,说明该内存存放的是指令。

栈是一种具有特殊的访问方式的存储空间,它的特殊性就在于,最后进入这个空间的数据,最先出去。

栈有两个基本的操作:入栈和出栈。

  • 入栈:将一个新的元素放到栈顶;
  • 出栈:从栈顶取出一个元素;

栈的操作规则:LIFO(Last In First Out,后进先出)
 

CPU 提供的栈机制

现今的 CPU 中都有栈的设计。8086CPU 提供相关的指令来以栈的方式访问内存空间。这意味着,我们在基 于8086CPU 编程的时候,可以将一段内存当作栈来使用。

8086CPU 提供入栈和出栈指令(最基本的):

  • PUSH(入栈)
  • POP (出栈)

push ax:将寄存器 ax 中的数据送入栈中;
pop ax :从栈顶取出数据送入 ax。

8086CPU的入栈和出栈操作都是以字为单位进行的。

注意:字型数据用两个单元存放,高地址单元放高 8 位,低地址单元放低8 位。

问:CPU 如何知道一段内存空间被当作栈使用?执行 push 和 pop 的时候,如何知道哪个单元是栈顶单元?

结论:任意时刻,SS:SP 指向栈顶元素。
 

push,pop 指令

push ax

(1)SP=SP–2
(2)将 ax 中的内容送入 SS:SP 指向的内存单元处,SS:SP 此时指向新栈顶。

问:如果我们将10000H~1000FH 这段空间当作栈,初始状态栈是空的,此时,SS=1000H,SP=?

答:SP = 0010H

 

pop ax

(1)将 SS:SP 指向的内存单元处的数据送入 ax 中;
(2)SP = SP+2,SS:SP 指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。


注意:

  • 出栈后,SS:SP 指向新的栈顶 1000EH,pop 操作前的栈顶元素,1000CH 处的 2266H 依然存在 ,但是,它已不在栈中。

  • 当再次执行 push 等入栈指令后,SS:SP 移至 1000CH,并在里面写入新的数据,它将被覆盖。
     

例题

(1)将10000H~1000FH 这段空间当作栈,初始状态是空的;
(2)设置AX=001AH,BX=001BH;
(3)将AX、BX中的数据入栈;
(4)然后将AX、BX清零;
(5)从栈中恢复AX、BX原来的内容。


结论:

push,pop 实质上就是一种内存传送指令,可以在寄存器和内存之间传送数据,与 mov 指令不同的是,push 和 pop 指令访问的内存单元的地址不是在指令中给出的,而是由 SS:SP 指出的。

同时,push 和 pop 指令还要改变 SP 中的内容:

  • 执行push时:
    • 先改变 SP,后向 SS:SP 处传送。
  • 执行pop时:
    • 先读取 SS:SP 处的数据,后改变 SP。

栈顶的变化范围最大为:0~FFFFH。

栈顶超界的问题

SS 和 SP 只记录了栈顶的地址,依靠 SS 和 SP 可以保证在入栈和出栈时找到栈顶。

可是,如何能够保证在入栈、出栈时,栈顶不会超出栈空间?

当栈满的时候再使用 push 指令入栈,栈空的时候再使用 pop 指令出栈,都将发生栈顶超界问题。栈顶超界是危险的。

因为我们既然将一段空间安排为栈 ,那么在栈空间之外的空间里很可能存放了具有其他用途的数据、代码等,这些数据、代码可能是我们自己的程序中的,也可能是别的程序中的。(毕竟一个计算机系统并不是只有我们自己的程序在运行)

但是由于我们在入栈出栈时的不小心,而将这些数据、代码意外地改写,将会引发一连串的错误。如果是刻意的话,就会显得很糟糕了。

8086CPU 不保证对栈的操作不会超界。这就是说, 8086CPU 只知道栈顶在何处(由SS:SP指示),而不知道我们安排的栈空间有多大。这点就好像 ,CPU 只知道当前要执行的指令在何处(由 CS:IP 指示)而不知道我们要执行的指令有多少。

8086CPU的工作机理,只考虑当前的情况:当前栈顶在何处;当前要执行的指令是哪一条。

结论:

  • 我们在编程的时候要自己操心栈顶超界的问题 ,要根据可能用到的最大栈空间,来安排栈的大小,防止入栈的数据太多而导致的超界;
  • 执行出栈操作的时候也要注意,以防栈空的时候继续出栈而导致的超界。

栈的综述

(1)8086CPU 提供了栈操作机制,方案如下:

  • 在 SS,SP 中存放栈顶的段地址和偏移地址;
  • 提供入栈和出栈指令,他们根据 SS:SP 指示的地址,按照栈的方式访问内存单元。

(2)push 指令的执行步骤:

  • SP = SP - 2
  • 向 SS:SP 指向的字单元中送入数据。

(3)pop 指令的执行步骤:

  • 从 SS:SP 指向的字单元中读取数据;
  • SP=SP-2

(4)任意时刻,SS:SP 指向栈顶元素。
(5)8086CPU 只记录栈顶,栈空间的大小我们要自己管理。
(6)用栈来暂存以后需要恢复的寄存器的内容时 ,寄存器出栈的顺序要和 入栈的顺序相反。
(7)push,pop 实质上是一种内存传送指令,注意它们的灵活应用。

栈是一种非常重要的机制,一定要深入理解,灵活掌握。
 

栈段

我们可以将长度为 N(N ≤64K )的一组地址连续、起始地址为16的倍数的内存单元,当作栈来用,从而定义了一个栈段。

比如我们将10010H~1001FH 这段长度为 16 字节的内存空间当作栈来用,以栈的方式进行访问。

这段空间就可以成为栈段,段地址为1000H,大小为16字节。

将一段内存当作栈段,仅仅是我们在编程时的一种安排,CPU 并不会由于这种安排,就在执行 push、pop 等栈操作指令时就自动地将我们定义的栈段当作栈空间来访问。

如何使的如 push、pop 等栈操作指令访问我们定义的栈段呢?

将 SS:SP 指向我们定义的栈段。

一个栈段最大可以设为多少?

首先从栈操作指令所完成的功能的角度上来看,push、pop 等指令在执行的时候只修改 SP;所以栈顶的变化范围是0~FFFFH,从栈空时候的 SP=0,一直压栈,直到栈满时 SP=0;如果再次压栈,栈顶将环绕,覆盖了原来栈中的内容。

所以一个栈段的容量最大为64KB。

→ 检测2 ←


(1)补全下面的程序,使其可以将10000H-1000FH中的8个字,逆序拷贝到20000H-2000FH中。

mov ax,1000H 
mov ds,ax 
---------------------
mov ax,2000H 		|
mov ss,ax     		|
mov sp,10h    		|
---------------------
push [0] 
push [2] 
push [4] 
push [6] 
push [8] 
push [A] 
push [C] 
push [E] 

(2)补全下面的程序,使其可以将10000H-1000FH中的8个字,逆序拷贝到20000H-2000FH中。

mov ax,2000H 
mov ds,ax 
---------------------
mov ax,1000H 		|
mov ss,ax    		|
mov sp,0     		|
---------------------
pop [e] 
pop [c] 
pop [a] 
pop [8] 
pop [6] 
pop [4] 
pop [2] 
pop [0]  

猜你喜欢

转载自blog.csdn.net/weixin_46263782/article/details/120361343
今日推荐