汇编语言(三)--汇编指令与段的综述

本文最后更新于:2019年6月26日 晚上

概览:取指令过程、数据存取以及栈的概览,段的综述。

机器指令

机器指令也叫代码指令,是计算机能识别的一组二进制代码

形如

1011 0000 0000 0111 B

把数“7”送到AL中

汇编语言指令

汇编语言有三种指令形式,分别是汇编指令、伪指令、宏指令

并且汇编指令是从机器指令演化而来的,与机器密切相关。

汇编指令

汇编指令由操作码字段操作数字段构成。

格式: 操作码字段 操作数字段

操作数字段可以是0个,1个,2个,3个。

双操作数

MOV AX,BX

格式:操作码 目的操作数,源操作数

目的操作数也是操作后的结果

指令存取

CS : IP

这是8086CPU中最关键的寄存器,指出了CPU当前要读取指令的地址。

CS: 代码段寄存器,存放代码的段地址

IP: 指令指针寄存器,存放代码的偏移地址

8086取指过程

  1. 从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲区。
  2. IP=IP+所读取指令的长度,从而指向下一条指令。
  3. 执行指令,转到步骤1,重复此过程。

CPU区分指令与数据

内存中的指令与数据没有任何区别,都是二进制信息。CPU是将CS:IP指向的内存单元中的内容看做指令。

CPU将CS、IP中的内容当作指令的段地址和偏移地址,用它们合成指令的物理地址,到内存中读取指令码,执行。

修改CS、IP指令

在CPU中,程序员能够通过指令读写的部件只有寄存器,程序员通过改变寄存器中的内容实现对CPU的控制。

mov指令不能用于设置CS、IP的值,8086CPU没有提供这样的功能。

转移指令-jmp指令可以修改,用指令中给出的段地址修改CS,偏移地址修改IP。

格式:jmp 段地址:偏移地址

jmp 2AE3:3

仅修改IP指令

用寄存器中的值修改IP。

格式:jmp 某一合法寄存器

jmp ax

代码段

将一段内存当作代码段,仅仅是我们在编程时的一种安排,CPU 并不会由于这种安排,就自动地将我们定义得代码段中的指令当作指令来执行。

CPU 只认被 CS:IP 指向的内存单元中的内容为指令,所以要将CS:IP指向所定义的代码段中的第一条指令的首地址

数据存取

mov指令

mov bx,1000h
mov ds,bx
mov al,[0]
;将10000H(1000:0)中的数据读到al中。
  1. 将数据直接送入寄存器;
  2. 将一个寄存器中的内容送入另一个寄存器中。
  3. 将一个内存单元中的内容送入一个寄存器。

格式:mov 寄存器名,内存单元地址

“[…]”表示一个内存单元, “[…]”中的数字表示内存单元的偏移地址,其段地址是DS。

mov指令中的[]说明操作对象是一个内存单元,[]中的0说明这个内存单元的偏移地址是0,它的段地址默认放在ds中

将数据送入段寄存器

mov ds,1000H;非法!

8086CPU不支持将数据直接送入段寄存器的操作!(硬件的问题)

数据->一般寄存器->段寄存器

字的传送

引图

如图所示,内存里一个存储单元只能存放一个字节(8位),而寄存器是16位的。

引图

add、sub指令

引图

Last In First Out 后进先出

入栈:将一个新的元素放到栈顶;

出栈:从栈顶取出一个元素。

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

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

CPU如何判断栈

寄存器CS和IP中存放着当前指令的段地址和偏移地址。

SS: 段寄存器,存放栈顶的段地址
SP:堆栈指针寄存器,存放栈顶的偏移地址

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

push指令执行过程

  1. SP=SP-2
  2. 将ax的值放入SS:SP指向的内存单元

引图

栈为空时,SS:SP的状态

我们将 10000H~1000FH 这段空间当作栈段,SS=1000H,栈空间大小为16 字节 ,栈最底部的字单元地址1000:000E

任意时刻,SS:SP指向栈顶,当栈中只有一个元素的时候,SS = 1000H,SP=000EH。 

栈为空,就相当于栈中唯一的元素出栈,出栈后,SP=SP+2 ,SP 原来为 000EH,加 2 后SP=10H,所以,当栈为空的时候,SS=1000H,SP=0010H。

当栈为空时,栈中没有元素,也就不存在栈顶元素,所以SS:SP只能够指向栈的最底部单元下面的单元,即栈最底部的字单元的偏移地址+2.

pop指令执行过程

  1. 将SS:SP指向的内存单元的值放入ax
  2. SP=SP+2

引图

执行指令后原栈顶的那个数据依旧存在于那个内存单元,只是它不在属于栈了。当执行push之后。它将会被覆盖掉。

栈顶超界问题

当栈满的时候Push以及栈空的时候pop都会发生栈顶超界问题。

而且8086CPU不保证栈的操作不会超界,即8086CPU只知道栈顶的位置。

所以只能使用的时候进行注意了!

8086CPU的工作机理

只考虑当前的情况:
当前栈顶在何处;
当前要执行的指令是哪一条。

8086CPU只记录栈顶,栈空间的大小我们要自己管理。

栈段

我们自己将一段连续的内存单元当做栈段,仅仅是自己的安排,CPU不会自动的将我们自定义的栈段当做栈空间来使用。我们应当将SS:SP指向我们自定义的栈段。

栈为空时,SS:SP的状态

如果我们将10000H~1FFFFH这段空间当作栈段,初始状态是空的,栈空间大小为64KB,栈最底部的字单元地址为1000:FFFE。

SP原来为FFFEH,加2后SP=0,所以,当栈为空的时候,SS=1000H,SP=0。

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

从栈的操作指令来看,push、pop只能够修改SP的值,即只能修改偏移地址。

故栈顶的变化范围就是0-FFFFH,从栈空时候的SP=0,一直压栈,直到栈满时SP=0。

所以一个栈段的容量最大为64k,2的16次方。

段的综述

我们可以自由的将一段内存定义为一个段,随意的存放数据、代码或者当做栈来使用。但是若要是让CPU来按照我们的安排,就要把我们定义的段的段地址放入对应的CS、DS、SS,将偏移地址放入对应的地址寄存器,这样才可以使用。

不管我们怎么安排,CPU把某段内存当做代码段,就是因为我们让CS:IP指向了那里。

一段内存,可以既是代码的存储空间,又是数据的存储空间,还可以是栈空间,也可以什么也不是。

关键在于CPU中寄存器的设置,即:CS、IP、SS、SP、DS的指向。