王爽之《汇编语言》学习重点十

2024-03-05 02:48

本文主要是介绍王爽之《汇编语言》学习重点十,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第六章 包含多个段的程序

     前面我们说0:200~0:300这段内存空间是相对安全的,可这段空间容量只有256个字节。在操作系统环境中,合法地通过操作系统取得的空间都是安全的,因为操作系统部会让一个程序所用的空间和其他程序以及系统自己的空间相冲突。在操作系统允许的情况下,程序可以取得任意容量的空间。

     程序取得所需空间的方法有两种,一是在加载程序的时候为程序分配,再就是程序在执行的过程中向系统申请。加载程序的时候为程序分配空间,前面所体验,比如我们的程序在加载的时候,取得了代码段中的代码的存储空间。

     我们若要一个程序在被加载的时候取得所需的空间,则必须要在程序中做出说明。通过源程序中定义段来进行内存空间的获取大多数有用的程序,都要处理数据,使用栈空间,当然也都必须有指令,为了程序设计上的清晰和方便,我们一般也都定义不同的段来存放他们。

6.1 在代码段中使用数据

      例: 计算以下8个数据的和,结果存放在ax寄存器中:

      0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H

      通过之前的经验,我们可以讲它们一个一个地加到ax寄存器中,但是,我们希望用循环的方法来进行累加,所以在累加前,我们要将这些数据存储在一组地址连续的内存单元中。怎么样获得者连续的内存单元呢 ?

      从规范的角度讲,我们是不能自己随便决定那段空间可以使用的,应该让系统为我们分配。我们可以在程序中,定义我们希望处理的数据,这些数据就会被编译、连接程序作为程序的一部分写到可执行文件中。当可执行文件中的程序被加载如内存时,这些数据也同时被加载如内存中。与此同时,我们要处理的数据也就自然而然地获得了存储空间。如下程序:

程序6.1

      assume cs:code

          code segment

              dw   0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H

              mov bx,0

              mov ax,0

              mov cx, 8

          s: add ax,cs:[bx]

              add bx,2

              loop s

              mov ax, 4c00H

              int 21H

         code ends

     end

     程序中“dw”含义是定义字型数据,即define word。我们使用dw定义了8个字型数据(逗号隔开),它们所占内存为16个字节。

     由于这8个数据在代码段中,程序在运行的时候CS中存放代码段的段地址。而dw定义的数据处于代码段最开始,所以偏移地址为0;

     在4.8节,我们知道在单任务系统中,可执行文件中的程序执行过程如下:

     (1) 由其他程序(Debug、command 或其他程序)将可执行文件中的程序加载入内存

     (2) 设置CS:IP 指向程序的第一条要执行的指令(即程序的入口),从而使程序得以运行;

     (3) 程序运行结束后,返回到加载者。

     我们之前所有使用Debug对程序的调试,实际上都是由Debug加载了我们的可执行文件到内存中;之后由Debug设置CS:IP到程序的入口;待程序结束再返回到Debug环境中。

     但我们希望可执行文件自己可独立运行,无需其他程序将其加载入内存,并设置CS:IP的指向。所以我们在汇编源码中加入程序的入口标号: start

     程序变为,程序6.2: 

      assume cs:code

          code segment

             dw   0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H

             start:   mov bx,0

                         mov ax,0

                         mov cx, 8

                     s: add ax,cs:[bx]

                         add bx,2

                         loop s

             mov ax, 4c00H

             int 21H

            code ends

      end start

      标号start在伪指令end的后面出现。我们要再次探讨 end 的作用。end 除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方。在程序6.2中我们用end 伪指令指明了程序的入口在标号start 处,也就是说,“mov bx, 0”是程序的第一条指令。

      现在的问题是,根据什么设置CPU的CS:IP 指向程序的第一条要执行的指令?也就是说,如何知道那一条指令是程序的第一条要执行的指令?  这一点,是由可执行文件中的描述信息指明的。我们知道可执行文件由描述信息和程序组成,程序来自于源程序中的汇编指令和定义的数据;描述信息则主要是编译、连接程序对源程序中相关伪指令进行处理所得到的信息。在程序6.2中,用伪指令end 描述了程序的结束和程序的入口。在编译、连接后,由“end start ”指明的程序入口,被转化为一个入口地址,存储在可执行文件的描述信息中。在程序6.2中,这个入口地址的偏移地址部分为:10H。当程序被加载入内存之后,加载者从程序的可执行文件的描述信息中读到程序的入口地址,设置CS:IP。这些CPU就从我们希望的地址处开始执行。

      归根结底,我们若要CPU从何处开始执行程序,只要在源程序中用“end 标号”指明就可以了。

      由此得出程序的框架:

      assume cs:code

          code segment

                    数据……

             start:   

                    代码……

          code ends

     end start

 

 6.2 在代码段中使用栈

 例子: 利用栈,将程序中定义的数据逆序存放。

      assume cs:code

          code segment

              dw   0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H

              ??????????

          code ends

     end

 

 我的代码

  assume cs:code
    code segment
     dw   0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
      ;将数据复制到0:200处,将0:200~0:20f 这段内存当做栈,将数据压入栈中。
      mov ax,0
      mov ds,ax
      mov bx,0
   
   mov cx,8
     
      mov ss,ax             ;设置栈段寄存器
      mov sp, 020fH+1   ;

 

 s:  mov ax,cs:[bx]
      push  ax   
      add bx,2
      loop s
 
      ;弹出栈,进行逆序

      mov ax,0
      mov bx,0
   
   mov cx,8

   f: pop ax
      mov cs:[bx],ax
      add bx,2;
      loop f

 

      mov ax,4c00H
      int 21H
   code ends
end start

     注意设置栈的偏移地址处,此处加1,是因为在把数据压入栈之前,sp先执行减法操作即sp = sp - 2;地址SS:SP在任意时刻都指向栈顶元素,栈的操作都是以字(2个字节)为单位进行的,当栈中没有元素时,则SP则应该指向栈底(亦即栈顶)的下一个内存单元,所以加1。若不加1,则整个栈空间则不是计划的安全内存空间0:200~0:20f ,即最后栈顶元素地址将不是0:200。

     通过定义字型数据开辟内存空间:

     例子代码:

 assume cs:code
    code segment
     dw   0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
     dw   0,0,0,0, 0,0,0,0

start: mov ax,cs

          mov ss,ax

          mov sp,32

          mov bx,0

          mov cx,8

     s:  push cs:[bx]

          add bx,2

          loop s

         

          mov bx,0

          mov cx,8

     f:   pop cs:[bx]

          add bx,2

          loop f

      mov ax,4c00H
      int 21H
   code ends
end start

      由此得,我们定义了8个字型数据,它们的数据都是0。所以在描述dw作用时,可以说用它定义数据,也可以说用它开辟内存空间。

6.3 将数据、代码、栈放入不同的段

      在前面,我们在程序中用到了数据和栈,我们将数据、栈和代码都放到了一个段里面。我们在编程时要注意何处是数据,何处是栈,何处是代码。这样做显然有两个问题:

      (1) 把它们放到一个段中使程序显得混乱;

      (2) 若数据、栈和代码需要的空间超过64KB,就不能放在一个段中(一个段的容量不能大于64KB,是由于8086模式的限制,并不是所有的处理器都这样)。前面程序中处理的数据很少,用到得栈空间也小,代码长度也小,故放到一个段中没有问题。

      所以我们应该考虑用多个段来存放数据、代码和栈。

      我们用和定义代码段一样的方法来定义多个段,然后在这些段里面定义需要的数据,货通过定义数据来取得栈空间。如程序6.4和程序6.3实现了相同的功能:

      程序6.4:

      assume cs:code, ds:data, ss:stack

      data segment

            dw  0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H

      data ends

      stack segment

            dw  0,0,0,0, 0,0,0,0,

      stack ends
      code segment

      start:   mov ax, stack

                  mov ss, ax

                  mov sp, 16      ; 设置栈顶ss:sp指向stack:16

                  mov ax, data

                  mov ds, ax      ; ds 指向data段

                  mov bx, 0        ; ds:bx 指向data段的第一个单元

                  mov cx,  8

            s:   push  [bx]

                  add bx, 2

                  loop s              ; 以上将data段中的0~16单元中的8个字型数据依次入栈

                  mov bx, 0

                  mov cx, 8

          s0:   pop [bx]          ; 以上使8个字型数据依次出栈并送到data段的0~16单元中。

                  add bx, 2

                  loop s0

                  mov ax, 4c00H

                  int 21H

       code ends

       end start

   (2) 对段地址的引用

         我们通过段地址访问段中的数据,而段地址分为两部分,即段地址和偏移地址。在程序中,段名就相当于一个标号,它代表了段地址。所以“mov ax, data”的含义就是将名称为“data”的段的段地址送入ax。所以一个段中的数据的段地址可由段名代表,偏移地址就要看它在段中的位置了。程序中的“data”段中的数据“0abcH”的地址就是: data:6。我们用以下代码将它送入bx中:

      mov ax, data

      mov ds, ax

      mov bx, ds:[6]

不能将段地址data直接送入ds中,“mov ds, data”是错误的,因为8086CPU不允许将一个数值直接送入段寄存器中。程序中对段名的引用,如指令“mov ds, data” 中的“data”,将被编译器处理为一个表示段地址的数据。

   (3)“代码段”、“数据段”、“栈段”完全是我们的安排

      我们在源程序中用伪指令“assume cs:code, ds:data, ss:stack”,将cs、ds、ss分别和code、data、stack段相连。这样做了之后,CPU并不会讲cs指向code,ds指向data,ss指向stack。标号assume是伪指令,并不由CPU直接执行,而是编译器需要用它将我们源程序中定义的具有一定用途的段和相关的寄存器联系起来。

      若要CPU按照我们的安排行事,就要用机器指令控制它。我们在源程序的最后用“end start”说明了程序的入口,这个入口被写入可执行文件的描述信息,可执行文件中的程序被加载入内存后,CPU的CS:IP被设置指向这个入口,从而开始执行第一条指令。标号“start”在“code”段中,这样CPU就将code段中的内容当做指令来执行了。我们在code段中,使用指令:

                mov ax, stack

                mov ss, ax

                mov sp, 16

      设置ss指向stack,设置ss:sp指向stack:16,CPU执行这些指令后,将把stack段当做栈空间来用。CPU若要访问data段中的数据,则可用ds指向data段,用其他的寄存器来存放data段中数据的偏移地址。

     

 

这篇关于王爽之《汇编语言》学习重点十的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/775089

相关文章

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个