汇编基础教程段的定义应用详解

目录
  • 种类
    • 代码段
    • 数据段
    • DS和[address]
    • 栈段
      • 操作方式
      • 8086CPU提供入栈和出栈指令
      • 栈顶超界的问题
    • push、pop指令
      • PUSH(入栈)
      • POP(出栈)
    • 栈段定义
    • 思考
  • 访问
  • 段前缀
    • 场景1
    • 场景2

将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元

种类

代码段

定义

对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。

可以将长度为 N( N≤64KB )的一组代码,存在一组地址连续、起始地址为 16的倍数的内存单元中,这段内存是用来存放代码的,从而定义了一个代码段。

例如

这段长度为 10 字节的字节的指令,存在从123B0H~123B9H的一组内存单元中,我们就可以认为,123B0H~123B9H这段内存单元是用来存放代码的 ,是一个代码段 ,它的段地址为123BH,长度为10字节。

如何使得代码段中的指令被执行

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

数据段

定义

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

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

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

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

DS和[address]

我们要读取10000H单元的内容

CPU要读取一个内存单元的时候,必须先给出这个内存单元的地址

  • 在8086PC中,内存地址由段地址和偏移地址组成。
  • 8086CPU中有一个 DS寄存器,通常用来存放要访问的数据的段地址。

将10000H(1000:0)中的数据读到al中。

mov bx,1000H

mov ds,bx

mov al,[0]

mov 指令可以将一个内存单元中的内容送入一个寄存器。

mov指令的格式:

  • mov 寄存器名,内存单元地址
  • “[…]”表示一个内存单元, “[…]”中的0表示内存单元的偏移地址。

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

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

如何把1000H送入ds?

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

例如

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

代码如下:

栈段

栈空间当然也是内存空间一部分,它只是一段可以以一种特殊方式进行访问的内存空间。

操作方式

栈有两个基本的操作

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

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

栈顶的元素总是最后入栈,需要出栈时,又最先被从栈中取出

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

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

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

8086CPU提供入栈和出栈指令

SS:SP  

8086CPU中,有两个寄存器:

  • 段寄存器SS  存放栈顶的段地址
  • 寄存器SP  存放栈顶的偏移地址

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

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

栈顶超界的问题

种类

  • 当栈满的时候再使用push指令入栈,
  • 栈空的时候再使用pop指令出栈,

8086CPU不保证对栈的操作不会超界。

8086CPU 只知道栈顶在何处(由SS:SP指示),而不知道读者安排的栈空间有多大。

8086CPU的工作机理,只考虑当前的情况:

  • 当前栈顶在何处;
  • 当前要执行的指令是哪一条。

解决办法

  • 要根据可能用到的最大栈空间,来安排栈的大小,防止入栈的数据太多而导致的超界
  • 执行出栈操作的时候也要注意,以防栈空的时候继续出栈而导致的超界。

push、pop指令

定义

push和pop指令是可以在寄存器和内存之间传送数据的。

PUSH(入栈)

push ax:将寄存器ax中的数据送入栈中;

执行过程 push ax

(1)SP=SP–2;

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

图示

入栈时,栈顶从高地址向低地址方向增长

POP(出栈)

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

图示

注意:

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

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

格式

(1)

push 寄存器:将一个寄存器中的数据入栈

pop 寄存器:出栈,用一个寄存器接收出栈的数据

例如:push axpop bx

(2)

push 段寄存器:将一个段寄存器中的数据入栈 pop

段寄存器:出栈,用一个段寄存器接收出栈的数据

例如:push dspop es

(3)

push 内存单元:将一个内存单元处的字入栈(栈操作都是以字为单位)

pop 内存单元:出栈,用一个内存字单元接收出栈的数据

例如:push [0] pop [2]

指令执行时 ,CPU 要知道内存单元的地址,可以在 push、pop 指令中给出内存单元的偏移地址,段地址在指令执行时,CPU从ds中取得。

注意

与mov指令不同的是,push和pop指令访问的内存单元的地址不是在指令中给出的,而是由SS:SP指出的。

CPU执行mov指令只需一步操作,就是传送,而执行push、pop指令却需要两步操作

执行push时:先改变SP,后向SS:SP处传送。

执行pop时: 先读取SS:SP处的数据,后改变SP。

push、pop 等栈操作指令,修改的只是SP。也就是说,栈顶的变化范围最大为:0~FFFFH。

提供:SS、SP指示栈顶;改变SP后写内存的入栈指令;读内存后改变SP的出栈指令。

栈段定义

  • 可以根据需要 ,将一组内存单元定义为一个段
  • 比如我们将10010H~1001FH 这段长度为 16 字节的内存空间当作栈来用,以栈的方式进行访问。
  • 我们可以将长度为 N(N ≤64K )的一组地址连续、起始地址为16的倍数的内存单元,当作栈来用,从而定义了一个栈段
  • 这段空间就可以成为栈段,段地址为1000H,大小为16字节
  • 如何使的如push、pop 等栈操作指令访问我们定义的栈段
  • 将SS:SP指向我们定义的栈段。

思考

我们将10000H~1FFFFH这段空间当作栈段 ,SS=1000H ,栈空间大小为64KB ,栈最底部的字单元地址为1000:FFFE。

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

栈为空,就相当于栈中唯一的元素出栈,出栈后,SP=SP+2。

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

任意时刻,SS:SP指向栈顶元素,当栈为空的时候 ,栈中没有元素 ,也就不存在栈顶元素,所以SS:SP只能指向栈的最底部单元下面的单元 ,该单元的偏移地址为栈最底部的字单元的偏移地址+2 ,栈最底部字单元的地址为1000:FFFE,所以栈空时,SP=0000H。

访问

对于数据段,将它的段地址放在 DS中,用mov、add、sub等访问内存单元的指令时,CPU就将我们定义的数据段中的内容当作数据段来访问;

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

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

可见,不管我们如何安排 ,CPU 将内存中的某段内存当作代码 ,是因为CS:IP指向了那里;CPU将某段内存当作栈 ,是因为 SS:IP 指向了那里

比如我们将10000H~1001FH安排为代码段,并在里面存储如下代码:

设置CS=1000H,IP=0,这段代码将得到执行。

可以看到,在这段代码中,我们又将10000H~1001FH 安排为栈段和数据段。

10000H~1001FH这段内存,既是代码段,又是栈段和数据段。

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

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

段前缀

在访问内存单元的指令中,用于显式地指明内存单元的段地址的“ds:”、“cs:”、“ss:”或“es:”

应用

场景1

问题:将内存ffff:0~ffff:b段元中的数据拷贝到 0:200~0:20b单元中。

分析

(1) 0:200~0:20b单元等同于0020:0~0020:b单元,它们描述的是同一段内存空间

(2)拷贝的过程应用循环实现,简要描述如下:

  • 初始化:X=0 循
  • 环12次

将ffff:X单元中的数据送入0020:X(需要用一个寄存器中转)

X=X+1

(3)在循环中,源单元ffff:X和目标单元的0020:X的偏移地址X是变量。我们用bx来存放

(4)我们用将0:200~0:20b用0020:0~0020:b描述,就是为了使目标单元的偏移地址和源始单元的偏移地址从同一数值0开始。

问题

因源单元ffff:X和目标单元0020:X 相距大于64KB,在不同的64KB段里,程序中,每次循环要设置两次ds。

这样做是正确的,但是效率不高。

解决

我们可以使用两个段寄存器分别存放源单元ffff:X和目标单元0020:X的段地址,这样就可以省略循环中需要重复做12次的设置ds的程序段。

改进的程序中,使用 es 存放目标空间0020:0~0020:b的段地址,用ds存放源空间ffff:0~ffff:b的段地址。

在访问内存单元的指令“mov es:[bx],al”中 ,显式地用段前缀 “es:” 给出单元的段地址,这样就不必在循环中重复设置ds

场景2

Debug和汇编编译器Masm对指令的不同处理

Debug中

  • mov ax,[0]
  • 表示将ds:0处的数据送入al中。

在汇编源程序中,指令“mov ax,[0]”被编译器当作指令“mov ax,0”处理。

解决:利用段前缀显性的指出段地址

以上就是汇编基础教程段的定义应用详解的详细内容,更多关于汇编基础教程段的资料请关注我们其它相关文章!

(0)

相关推荐

  • 汇编语言超浓缩教程

    所以我个人看法学汇编,不一定要写程序,写程序确实不是汇编的强项,大家不妨玩玩DEBUG,有时CRACK出一个小软件比完成一个程序更有成就感(就像学电脑先玩游戏一样).某些高深的指令事实上只对有经验的汇编程序员有用,对我们而言,太过高深了.为了使学习汇编语言有个好的开始,你必须要先排除那些华丽复杂的命令,将注意力集中在最重要的几个指令上(CMP LOOP MOV JNZ--).但是想在啰里吧嗦的教科书中完成上述目标,谈何容易,所以本人整理了这篇超浓缩(用WINZIP.WINRAR-依次压迫,嘿嘿!

  • 汇编语言功能字符串大小写转换实现实例详解

    目录 问题1:将data段中的第一个字符串转换成大写,第二个字符串转换成小写 问题2:将data段中每个单词的头一个字母改成大写字母 问题3:将data段中每个单词改为大写字母 问题4:将data段中每个单词的前4个字母改为大写字母 问题5:设计一个子程序,将一个全是字母的字符串转化成大写 问题6:设计一个子程序,将一个全是字母,以0结尾的字符串,转化成大写 复杂一点的对结构化数据的版本 问题1:将data段中的第一个字符串转换成大写,第二个字符串转换成小写 分析: 如何转换成大小写 如果利用A

  • 汇编语言功能用循环累加实现乘法

    目录 问题1:编程计算2的2次方,结果存在ax中 分析:用2+2实现 问题2:编程实现2的12次方 分析:用loop实现 问题3:编程实现123*236,结果存在ax中 分析:用236相加123次的计算次数比较少,节约计算资源 问题4:计算ffff:0006单元中的数乘以3,结果存储在dx中 1.判断数据是否能够存储 2.判断数据相加是否能够位数相同 问题5:计算ffff:0~ffff:b单元中的数据的和,结果存储在dx中 1.运算的结果是否超出寄存器的范围 2.能否直接相加dx中的数据 问题6

  • 汇编语言显示功能实现教程详解

    目录 问题1 1 如何确定字符要显示的位置 确定3行字符在每一行的起始位置 确定3行字符在屏幕中的哪一行 2 如何确定字符要显示的颜色属性 问题2: 分析: 问题1 在屏幕中间分别显示绿底,绿底红色,白底蓝色的字符串"Welcome to masm!" 分析: 1 如何确定字符要显示的位置 在80*25的彩色字符模式显示缓冲区在内存地址的B8000H~BFFFFH共32KB的空间中 在该模式下,显示器可以显示25行,80列 所以在一行中就有80个字符,共占据160个字节,每个字符低位字

  • 汇编语言功能实现数据复制实例详解

    目录 问题1:将内存ffff:0~ffff:b单元中的数据复制到0:200~0:20b单元中 问题2:将"mov ax,4c00h"之前的指令复制到内存0:200处 问题3:将程序中定义的数据逆序存放 问题4:将字符串"welcome to masm"复制到它后面的数据区中 问题1:将内存ffff:0~ffff:b单元中的数据复制到0:200~0:20b单元中 分析 1.0:200~0:20b单元如何表示 0020:0~0020:b可以等同于以上单元,而且单元的偏移

  • 汇编语言入门教程阮一峰版

    汇编语言是一种最低级.最古老.不具有移植性的编程语言,它能够直接访问计算机硬件,所以执行效率极高,占用资源极少,一般用于嵌入式设备.驱动程序.实时应用.核心算法等. 汇编语言的缺点是开发周期特别长,实现一个简单的功能都非常麻烦,已经很少用来编写应用程序了. 学习编程其实就是学高级语言,即那些为人类设计的计算机语言. 但是,计算机不理解高级语言,必须通过编译器转成二进制代码,才能运行.学会高级语言,并不等于理解计算机实际的运行步骤. 计算机真正能够理解的是低级语言,它专门用来控制硬件.汇编语言就是

  • 汇编基础教程段的定义应用详解

    目录 段 种类 代码段 数据段 DS和[address] 栈段 栈 操作方式 8086CPU提供入栈和出栈指令 栈顶超界的问题 push.pop指令 PUSH(入栈) POP(出栈) 栈段定义 思考 访问 段前缀 场景1 场景2 段 将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元 种类 代码段 定义 对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段. 可以将长度为 N( N≤64KB )的一组代码,存在一组地址连续.起始地址为 16的倍数的内存单元中,

  • Golang基础教程之字符串string实例详解

    目录 1. string的定义 2.string不可变 3.使用string给另一个string赋值 4.string重新赋值 补充:字符串拼接 总结 1. string的定义 Golang中的string的定义在reflect包下的value.go中,定义如下: StringHeader 是字符串的运行时表示,其中包含了两个字段,分别是指向数据数组的指针和数组的长度. // StringHeader is the runtime representation of a string. // I

  • python基础教程之元组操作使用详解

    简介 tuple 1.元组是以圆括号"()"包围的数据集合,不同成员以","分隔.通过下标进行访问 2.不可变序列,可以看做不可变的列表,与列表不同:元组中数据一旦确立就不能改变(所以没有类似列表的增删改操作,只有基本序列操作) 3.支持任意类型,任意嵌套以及常见的序列操作 4.元组通常用在使语句或用户定义的函数能够安全地采用一组值的时候,即被使用的元组的值不会改变 声明及使用 复制代码 代码如下: t = ()  #空元组t =(1,)  #单个元素元组,注意逗号

  • python基础教程之数字处理(math)模块详解

    1.math简介 复制代码 代码如下: >>> import math>>>dir(math)          #这句可查看所有函数名列表>>>help(math)         #查看具体定义及函数0原型 2.常用函数 复制代码 代码如下: ceil(x) 取顶floor(x) 取底fabs(x) 取绝对值factorial (x) 阶乘hypot(x,y)  sqrt(x*x+y*y)pow(x,y) x的y次方sqrt(x) 开平方log(x

  • Kotlin基础教程之函数定义与变量声明

    Kotlin基础教程之函数定义与变量声明 可以看到,函数定义就是 <访问控制符> <函数名> <参数列表> <:返回类型(不写就是无返回类型)> { 函数体 } 单语句函数可以简写,比如add函数和add1函数效果是一样的 变量定义 var <标识符> : <类型> = <初始化值> 常量定义 val <标识符> : <类型> = <初始化值> 常量与变量都可以没有初始化值,但是在引用前

  • 计算机程序设计并行计算概念及定义全面详解

    目录 1 摘要 2 概述 2.1 什么是并行计算? 2.2 为什么要并行计算? 2.3 谁都在使用并行计算? 科学界和工程界: 工业界和商业界: 全球应用: 3 概念和术语 3.1 冯诺依曼体系结构 3.2 弗林的经典分类 3.3 一些常见的并行计算术语 3.4 并行程序的缺陷和代价 复杂性: 可移植性: 资源需求: 可扩展性: 4 并行计算机的内存架构 4.1 共享内存 统一内存存取(Uniform Memory Access): 非统一内存存取(Non-Uniform Memory Acce

  • Python NumPy教程之数组的基本操作详解

    目录 Numpy中的N维数组(ndarray) 数组创建 数组索引 基本操作 数据类型 Numpy中的N维数组(ndarray) Numpy 中的数组是一个元素表(通常是数字),所有元素类型相同,由正整数元组索引.在 Numpy 中,数组的维数称为数组的秩.给出数组沿每个维的大小的整数元组称为数组的形状.Numpy 中的数组类称为ndarray.Numpy 数组中的元素可以使用方括号访问,并且可以使用嵌套的 Python 列表进行初始化. 例子 : [[ 1, 2, 3], [ 4, 2, 5]

  • Java之JSP教程九大内置对象详解(上篇)

    目录 JSP九大内置对象一览表 九大内置对象 四大域对象 JSP request对象 示例 JSP response对象 示例 JSP out对象 示例 JSP九大内置对象一览表 为了简化页面的开发过程,JSP 提供了一些内置对象. JSP 内置对象又称为隐式对象,它们由容器实现和管理. 在 JSP 页面中,这些内置对象不需要预先声明,也不需要进行实例化,我们可以直接在脚本和表达式中使用. 注意:JSP 内置对象只能在脚本和表达式中使用,在声明中不能使用. 九大内置对象 JSP 中定义了 9 个

  • 正则表达式教程之匹配一组字符详解

    本文实例讲述了正则表达式教程之匹配一组字符的方法.分享给大家供大家参考,具体如下: 注:在所有例子中正则表达式匹配结果包含在源文本中的[和]之间,有的例子会使用Java来实现,如果是java本身正则表达式的用法,会在相应的地方说明.所有java例子都在JDK1.6.0_13下测试通过. 一.匹配多个字符中的某一个 在上一篇<正则表达式教程之匹配单个字符详解>中的一个匹配以na或sa开头的文本文件例子中,使用的正则表达式是.a.\.txt.如果还有一个文件是cal.txt,那么也将会被匹配到.如

  • MyBatis Properties及别名定义实例详解

    上一篇我们介绍了mybatis的增删改查入门实例,我们发现在 mybatis-configuration.xml 的配置文件中,对数据库的配置都是硬编码在这个xml文件中,如下图,那么我们如何改进这个写法呢? 1.我们将 数据库的配置语句写在 db.properties 文件中 jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssm jdbc.username=root jdbc.password=ro

随机推荐