04.第一个程序

第一个程序

前言

终于可以编写第一个程序,有了前面的这些基础,我们可以轻易的编写一个汇编程序。但为了透彻理解一个完整的程序,我们将经历一个漫长的过程

一个源程序从写出到执行的过程

  1. 编写汇编源程序 这一步的工作结果会生成一个存储汇编源程序的文本文件
  2. 对源程序进行编译连接 使用汇编语言编译程序对源程序中的文件进行编译,产生目标文件;再用连接程序对目标文件进行连接,生成可在操作系统中直接运行的可执行文件
    可执行文件包括两部分内容:
    • 程序(源程序翻译的机器码)和数据(源程序定义的数据)
    • 相关的描述信息(比如:程序有多大,要占用多大的内存空间)
      这一步的工作结果,产生了一个可以在操作系统上运行的可执行程序
  3. 执行可执行程序
    操作系统根据可执行程序中描述信息,将可执行程序中机器码和数据加载进入内存,并进行相应的初始化(将CS:IP指针指向第一条要执行的指令),然后由CPU执行程序

源程序

下面是一个简单的汇编程序,我们分析一下

  assume cs:codeseg

  codeseg segment
      mov ax, 0123H
      mov bx, 0456H
      add ax, bx
      add ax, ax

      mov ax, 4c00H
      int 21H
  codeseg ends

  end

伪指令

在汇编源程序中,包含两种指令,一种是伪指令,一种是汇编指令。汇编指令是有对应机器码的指令,可以被编译成机器指令,最终被 CPU 执行。而伪指令没有对应的机器指令,最终不被 CPU 执行。那么伪指令由谁来执行呢?伪指令是由编译器来执行的指令,编译器根据伪指令来进行相应的编译工作

那么,我们来分析一下上述程序有哪些指令是伪指令?

  1. XXX segment
    XXX ends
    segment 和 ends 是一对成对使用的伪指令。功能是定义一个段,segment 表示段开始,ends 表示段结束。一个段必须有一个名称来标识。一个汇编程序由多个段组成,这些段被用来存放代码,数据或当作栈空间来使用。我们前面提到的 数据段 代码段 栈段 都由这个伪指令来指定。一个汇编程序至少有一个段 代码段 存放代码。
  2. end
    end 是一个汇编程序的结束标志,编译器在编译汇编程序的过程中,如果碰到了伪指令 end ,就结束对源程序的编译。因此,我们在书写汇编程序时,必须加上结束标志 end,不然我们的编译器在编译程序时,无法知道程序在何时结束
  3. assume
    这段伪指令的含义是 “假设”。它假设某一个段寄存器与程序中某一个 segment ... ends 定义的段相关联。通过 assume 说明这种关联,在需要的情况下,编译程序将段寄存器与某一个具体的段相联系。例如:CS 和 代码段相关联,DS 和数据段相关联,SS 和栈段相关联

源程序中的“程序”

我们将源程序中的所有内容称之为源程序,将源程序中最终由计算机执行,处理的指令或数据,称为程序

标号

汇编源程序中,除了一些汇编指令和伪指令以外,还有一些标号,比如 “codeseg” 。一个标号指代了一个地址,比如codeseg 在 segment 的前面,作为一个段的名称,这个段最终被编译,连接程序处理成一个段的段地址

程序的结构

我们需要书写出能让编译器进行编译的源程序,这样的源程序应该具备起码的结构

  1. 我们首先定义一个代码段
  2. 在代码段中书写我们的汇编指令
  3. 添加结束标志 end
  4. 将 代码段 和 CS 寄存器关联起来
  5. 程序返回
    我们的程序最先以汇编指令的形式存在源程序中,经编译,连接转成机器码,存储在可执行文件中,那么,它怎么得到运行的呢
    下面,我们在 DOS(一个单任务操作系统)的基础上,简单的讨论一下这个问题
    1. 一个程序 P2 在可执行文件中,则必须要要有一个正在运行的程序 P1,将 P2 从可执行文件中加载进入内存后,将 CPU 的控制权交给 P2,P2 才能正常运行 。P2 正常运行,P1 暂停运行
    2. 而当 P2 运行完毕后,应该将 CPU 的控制权交还给使它得以运行的程序 P1,此后,P1 才能继续运行
    3. 一个程序结束运行后,将 CPU 的控制权交还给使他运行的程序,我们称这个过程叫做 程序返回

我们在汇编程序中是如何实现程序返回的?

mov ax, 4c00H
int 21H
通过以上几条指令实现的,目前我们不必理解 int 21H 的含义,只需知道这几条指令合在一起是程序返回

语法错误和逻辑错误

熟悉C语言的应该很清楚这两个的概念

编译源程序

  1. 下载 MASM 编译工具
  2. 书写一个汇编程序 04-1.asm
  3. 使用 DosBox 运行 masm.exe

上图通过 masm + 04-1.asm 会生成 04-1.obj 目标文件

上图通过 link 进行连接,首先选择目标文件名称,然后定义生成的 可执行文件名称,.lib 代表是否要链接库(相当于C语言里面的动静态库),其他的都是一些中间文件,我们在汇编这们课程中不予讨论

以简化的方式进行编译和连接

输入以下两条命令:

  • masm 04-1;
  • link 04-1;

04-1.exe 程序的执行

直接在dos窗口输入 04-1 即可

谁将可执行文件中的程序装在进入内存并让它执行

操作系统的外壳

操作系统是由多个功能模块组成的庞大,复杂的软件系统。任何一个通用的操作系统,都要提供一个称为 shell(外壳)的程序,用户使用这个程序来操作计算机进行工作

DOS 中有一个程序 command.com,这个程序在 DOS 中称之为命令解释器,也就是DOS中的shell

DOS启动后,先完成其他重要的初始化工作,然后运行 command.com,command.com 运行后,执行完其他的相关任务后,在屏幕上显示出由当前盘符和当前路径组成的提示符,比如 "C:" 等,然后等待用户的输入

用户可以输入“dir”等命令运行,用户执行玩这些命令后,再次显示由当前盘符和当前路径组成的提示符,如果用户要执行一个可执行程序,则输入该可执行程序的名称,command首先根据文件名找到可执行文件,然后将这个可执行文件中载入内存,设置 CS:IP 为程序的入口点。此后,command 暂停运行,CPU 运行程序。程序结束后,返回到command 中,在屏幕上显示出由当前盘符和当前路径组成的提示符,比如 "C:" 等,然后等待用户的输入

汇编程序从写出到执行的过程
编程 -> 1.asm -> 编译(masm) -> 1.obj -> 连接(link) -> 1.exe -> 加载(command) -> 内存中的程序 -> 运行

程序执行过程跟踪

DOS中.exe文件中程序的加载过程

  1. 找到一块起始地址为 SA:0(即骑士地址的偏移地址为0的容量足够大的空闲内存区)
  2. 在这段内存区的前256个字节处,创建一个称为程序前缀(PSP)的数据区,DOS 要利用 PSP 和被加载程序进行通信
  3. 从PSP后面,将程序装入,程序的起始地址 SA+10H:0
  4. 将该内存区的段地址放入ds中,初始化其他寄存器后,设置CS:IP指向程序的入口

调试程序 使用 debug + 可执行程序名称,以及前面提到的指令进行调试
一定要自己手动调试一下程序,然后查看寄存器的值

参考

<<汇编语言第四版>>-王爽

posted @ 2023-08-02 08:45  异世界穿越中!.!  阅读(17)  评论(0)    收藏  举报