汇编二

Posted Luminary74

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了汇编二相关的知识,希望对你有一定的参考价值。

前言

内存中字存储、字单元的概念

CPU中用16位寄存器来存储一个字,高8位存放高位字节,低8位存放低位字节

单元即存放一个字型数据的内存单元,由两个地址连续的内存单元组成;高地址内存单元中存放字型数据的高位字节,低地址内存单元中存放字型数据的低位字节

将起始地址为N的字单元称为N地址字单元,比如:2地址字单元由2、3两个内存单元组成

DS寄存器

CPU要读写一个内存单元的时候,必须先给出这个内存单元的地址,在8086PC中,内存地址由段地址和偏移地址组成。8086CPU中有一个DS寄存器,通常用来存放要访问的数据的段地址。比如我们要读取10000H单元的内容,可用以下程序段:

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

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

使用mov指令,可完成两种传送:(1)将数据直接送入寄存器;(2)将一个寄存器中的内容送入另一个寄存器。
也可以使用mov指令将一个内存单元中的内容送入一个寄存器中。mov指令格式为: mov 寄存器名, 内存单元地址。
“[]”表示一个内存单元,“[0]”中的0表示内存单元的偏移地址,段地址默认放在ds中。

不禁疑惑,上面为什么要通过三行指令去把数据送给段寄存器而不是直接传送过去?
由于8086CPU硬件设计的问题,8086CPU不支持将数据直接送入段寄存器。如mov ds,1000H是非法的。所以我们得使用一个寄存器进行中转。

解释一下三条指令的意思:
(1).先将段地址1000H放入中转寄存器bx中,然后再放入ds
mov bx,1000H
mov ds,bx
(2).完成数据从1000:0单元到al的传送
mov al,[0]:“[]”表示一个内存单元,“[0]”中的0表示内存单元的偏移地址,段地址默认放在ds中。

mov add sub 指令

mov add sub的几种操作形式:

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

数据段

写几条指令,累加数据段123B0H–123BAH中的前3个字型数据
mov ax,123BH

mov ax,123BH
mov ds,ax
mov al,0
mov ax,[0]
mov ax,[2]
mov ax,[4]

一个字型数据占两个单元,所以偏移地址是0,2,4

CPU提供的栈机制

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

没有区别,都是二进制信息。cs:ip指向的内存为指令,ds段寄存器指向的为数据、

栈 :特殊的访问方式的存储结构,先进后出 LIFO(last in first out)

CPU都有栈的设计,提供相关的指令来以栈的方式读取内存空间,我们编程的时候可以将一段内存当作栈来使用 PUSH(入栈)POP(出栈)操作都是以字为单位进行。pop从栈顶取出数据

push ax
pop ax 
push [0] 
pop [0] 
push ds 
pop ds 
push cs 
pop cs


move ax,0123H   将0123H这一段地址给ax
push ax         ax入栈
move bx,2266H   将2266H这一段地址给bx
push bx        bx入栈

push ax 的执行,由以下两步完成:

    (1) sp = sp - 2 , SS:SP 指向当前栈顶前面的单元,以当前栈顶前面的单元为新的栈顶;

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

    由图可以看出, 8086CPU中,入栈时,栈顶从高地址向低地址方向增长。

    接下来,我们描述POP指令功能。 例如 pop ax :

    pop ax 的执行过程和push ax 刚好相反,由以下两步完成:

    (1) 将SS:SP指向的内存单元的数据送入ax中;

    (2) SP = SP + 2, SS:SP 指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。
//仅进行ax,bx操作时
AX = 0123H
PUSH AX
SS = 1000H
SP = 000DH

BX = 2266H
PUSH BX
SS = 1000H
SP = 000BH 
栈顶往上移SP = SP - 2

POP AX 
AX = 2266
sp = sp + 2
sp = 000DH

//push 先操作sp再放数据
//pop  先拿数据再操作sp

然后一个一个出栈,拿出来。
注意:这里有一个栈顶的概念,每次放入一个段地址,栈顶就会上升“两格”(两个字节),每次出栈栈顶就会下降“两格”。此时栈顶下降后,内存中的数据并未消失。但是倘若重新写入数据,就可以将其覆盖。

&:我猜,数据所谓的“删除后”还能被恢复大概是这个原理,你存入的数据并没有被真正的删除,除非你把你的数据覆盖掉。内存相当于一个仓库吧,你要想把它拿掉,就需要填满新的东西。(仅猜测,没仔细了解过,以后有时间去学习一下)

CPU如何知道10000H~1000FH这段空间被当作栈来使用?当我们push ax 时,要将寄存器中的内容放在当前栈顶单元的上方,成为新的栈顶元素;pop ax等指令执行时, 要从栈顶单元中取出数据,送入寄存器中。那么CPU是怎么知道栈顶元素的呢?

如同CPU知道当前执行的指令所在位置,用CS、IP 来存放当前指令的段地址和偏移地址。在8086CPU中,有两个寄存器,段寄存器SS和寄存器SP, SS用来存放栈顶的段地址SP存放偏移地址。任意时刻,SS:SP指向栈顶元素。当执行push和pop指令时,CPU从SS何SP中得到栈顶的地址。

栈顶超界的问题

8086CPU不保证我们对栈的操作不会超界。这就是说,8086CPU只知道栈顶在何处(由SS:SP指示),而不知道读者安排的栈空间有多大。这点就好像,CPU只知道当前要执行的指令在何处(由CS:IP指示),而不知道读者要执行的指令由多少。从这两点上我们可以看出8086CPU的工作原理,它只考虑当前的情况;当前的栈顶在何处、当前要执行的指令是哪一条。

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

有兴趣可以了解一下,现在对越界问题的防范,如windows的金丝雀机制

栈段

我们可以将一段内存定义为一个段。用一个段地址指示段,用偏移地址访问段内的单元。这完全是我们自己的安排。

对于数据段,将它的段地址放到DS中,CPU就将我们定义的数据段中的内容当作数据访问

对于代码段,将它的段地址放在CS中,将段中的第一条指令的偏移地址放在IP中,CPU就将执行我们定义的代码段中的指令

对于栈段,将它的段地址放在SS中,将栈顶单元的偏移地址放在SP中,这样CPU在需要执行栈操作的时候,比如执行push pop指令,就将我们定义的栈段当作栈空间来用

将10000H-1000FH当作栈,初始状态栈为空,将AX,BX,DS的数据入栈
mov ax,1000H
mov ss,ax
mov sp,0010H

push ax
push bx 
push ds
//若要原封不动拿出来,栈先进后出的特性
pop ds
pop bx
pop ax

&:感觉栈的特性有点像压手枪子弹的感觉,hhhh

第一个程序:源程序从写出来到执行过程

1文本编辑汇编代码

2使用汇编语言编译程序对源程序进行编译生成目标文件

3再用连接程序对目标文件进行连接,生成可在操作系统中直接运行的可执行文件

可执行文件包含2部分内容:

​ 1.程序(从源程序中的汇编指令翻译过来的机器码和数据(源程序中定义的数据))

​ 2相关的描述信息(比如这个程序有多大,要占用多少内存空间等)

assume cs:aicat 
aicat segment
	mov ax,0123H
	mov bx,0456H
	
	mov ax,4c00H
	int 21H
aicat ends

sutong segment

sutong ends
end	

参考博客:https://blog.csdn.net/wen623/article/details/5689613?spm=1001.2014.3001.5502

以上是关于汇编二的主要内容,如果未能解决你的问题,请参考以下文章

数据格式,訪问信息以及操作数指示符

汇编实验五

汇编实验5

深入理解计算机系统(3.3)------操作数指示符和数据传送指令

ruby 可选的Jekyll猴子补丁使用终端通知器来指示Jekyll构建完成。放入_plugins / ext.rb

汇编语言语句类型格式