《汇编语言》- 读书笔记 - 第4章-第一个程序
- 4.1 一个源程序从写出到执行的过程
- 4.2 源程序
- 程序 4.1
- 1. 伪指令
- 1.1 segment ends 声明段
- 1.2 end 结束标记
- 1.3 assume 关联
- 2. 源程序中的“程序”
- 3. 标号
- 4. 程序的结构
- 5. 程序返回
- 6. 语法错误和逻辑错误
- 4.3 编辑源程序
- 4.4 编译
- 4.5 连接
- 4.6 以简化的方式进行编译和连接
- 4.7 执行exe
- 4.8 谁将可执行文件中的程序装载进入内存并使它运行?
- 问题 4.1
- 问题 4.2
- 操作系统的shell
- 4.9 程序执行过程的跟踪
- 实验3 编程、编译、连接、跟踪
4.1 一个源程序从写出到执行的过程
- 第一步:编写汇编源程序。
- 第二步:对源程序进行编译连接。
可执行文件包含两部分内容- 程序(从源程序中的汇编指令翻译过来的机器码)和数据(源程序中定义的)
- 相关的描述信息(比如,程序有多大、要占用多少内存空间等)
- 第三步:执行可执行文件中的程序。
操作系统依照可执行文件中的描述信息,将可执行文件中的机器码和数据加载入内存,并进行相关的初始化(比如设置 CS:IP 指向第一条要执行的指令),然后由 CPU 执行程序。
4.2 源程序
程序 4.1
assume cs:codesg
codesg segment
mov ax, 0123H
mov bx, 0456H
add ax, bx
add ax, ax
mov ax,4c00H
int 21H
codesg ends
end
1. 伪指令
汇编指令:会被编译为机器指令,最终为 CPU 所执行。
而伪指令:没有对应的机器指令,它被用来辅助编译器进行相关的编译工作。
1.1 segment ends 声明段
段名 segment
; 代码...
段名 ends
segment
和 ends
是一对伪指令,功能是定义一个段,segment 说明一个段开始,ends 说明一个段结束。一个段必须有一个名称来标识。
一个汇编程序是由多个段
组成的,指令
、数据
、栈
,被划分到了不同的段中。
一个有意义的汇编程序中至少要有一个段,这个段用来存放代码。
1.2 end 结束标记
编译器在编译汇编程序的过程中,碰到了伪指end,就结束对源程序的编译。
1.3 assume 关联
用于将特定的段
与相关段寄存器
建立关联。
assume cs:codesg
将代码段的段 codesg 和段寄存器 cs 联系起来。
2. 源程序中的“程序”
程序最先以汇编指令的形式存在源程序中,经编译、连接后转变为机器码,存储在可执行文件中。这个过程如图 4.2 所示。
3. 标号
汇编源程序中,除了汇编指令
和伪指令
外,还有一些标号
,比如codesg
。它指代了一个地址。
比如 codesg
在 segment
的前面,作为一个段的名称,这个段的名称最终将被编译、连接程序处理为一个段的段地址。
4. 程序的结构
源程序是由一些段
构成的。
任务: 编程运算 2^3。源程序应该怎样来写呢?
assume cs:abc ; 将 abc 和 cs 建立关联,当作代码段。
abc segment ; 定义一个段,名称为 abc
mov ax,2 ; 在段中写入汇编指令 1
add ax,ax ; 在段中写入汇编指令 2
add ax,ax ; 在段中写入汇编指令 3
; 这里还缺程序返回指令,后面自己补上。
abc ends ; 段 abc 结束
end ; 指出程序在何处结束
5. 程序返回
程序原本只是躺在硬盘中的一坨二进制数据。
执行程序的流程:(这里讨论的是DOS系统)
- 程序A将程序B加载到内存。
- 程序A将CPU控制权交给程序B。
- 程序B执行。
- 程序B执行完毕,程序返回。交出CPU控制权。
- 程序A拿到CPU控制权,继续执行。
程序返回指令如下:(书上说先死记,后面会讲)
mov ax, 4c00H
int 21H
表4.1 与结束相关的概念
目的 | 相关指令 | 指令性质 | 指令执行者 |
---|---|---|---|
通知编译器一个段结束 | 段名 ends | 伪指令 | 编译时,由编译器执行 |
通知编译器程序结束 | end | 伪指令 | 编译时,由编译器执行 |
程序返回 | mov ax,4c00H int 21H | 汇编指令 | 执行时,由 CPU 执行 |
6. 语法错误和逻辑错误
语法错误:编译器能发现。
逻辑错误:跑起来看了效果才知道。
4.3 编辑源程序
任何你熟悉的文本编辑器都可以。
4.4 编译
- 将源文件保存得到
test.asm
- 编译:
masm test.asm
得到目标文件test.obj
。(也可以直接输入masm
回车,交互式设置编译参数 )
4.5 连接
连接的作用:
- 根据需要将源程序分为多个来编译,得到多个目标标文件后,再连接生成一个可执行文件;
- 程序中调用了某个库文件中的子程序,需要将这个库文件和该程序生成的目标文件连接到一起,生成一个可执行文件;
- 一个源程序编译后,得到了存有机器码的目标文件,目标文件中的有些内容还不能直接用来生成可执行文件,连接程序将这些内容处理为最终的可执行信息。所以,在只有一个源程序文件,而又不需要调用某个库中的子程序的情况下,也必须用连接程序对目标文件进行处理,生成可执行文件。
使用微软的 Overlay Linker3.60 连接器,文件名为 link.exe
4.6 以简化的方式进行编译和连接
末尾加分号 ;
表示都用默认参数:
masm test.asm;
link test.obj;
当然还有更简化的,我们直接用 ML.exe
4.7 执行exe
我们的 test.exe
只是对寄存器进行操作。直接执行是看不到效果的,debug调试一下,就能看到效果了。(作者说后面会介绍输出到屏幕的程序,不过要几章之后去了)
4.8 谁将可执行文件中的程序装载进入内存并使它运行?
我们在前面讲过,在 DOS 中,可执行文件中的程序 P1 若要运行,必须有一个正在运行的程序 P2,将 P1 从可执行文件中加载入内存,将 CPU 的控制权交给它,P1 才能得以运行;当 P1 运行完毕后,应该将 CPU 的控制权交还给使它得以运行的程序 P2。
按照上面的原理,再来看一下 4.7 节中 test.exe 的执行过程(思考相关的问题)。
- 在提示符
c:\
后面输入可执行文件的名字test
,按 Enter 键。这时,请思考问题 4.1。 - l.exe 中的程序运行。
- 运行结束,返回,再次显示提示符“c:masm’。请思考问题 4.2。
问题 4.1
此时,有一个正在运行的程序将 test.exe
中的程序加载入内存,这个正在运行的程序是什么? 它将程序加载入内存后,如何使程序得以运行?
- 在 DOS 中直接执行
test.exe
时,是正在运行的 shell 程序 command,将其加载入内存。 command
设置 CS:IP 指向test.exe
的第一条指令(即程序的入口),从而使test.exe
得以运行。
问题 4.2
程序运行结束后,返回到哪里?
- 程序运行结束后,返回到
command
。
操作系统的shell
操作系统是由多个功能模块组成的庞大、复杂的软件系统。任何通用的操作系统,都要提供一个称为 shell外壳)的程序,用户(操作人员)使用这个程序来操作计算机系统进行工作。
DOS 中有一个程序command.com
,这个程序在 DOS 中称为命令解释器
,也就是 DOS 系统的shell
。
DOS 启动时,先完成其他重要的初始化工作,然后运行command.com
,command.com
运行后,执行完其他的相关任务后,在屏幕上显示出由当前盘符和当前路径组成的提示符,比如:c:\
或“c:\windows`等,然后等待用户的输入。
用户可以输入所要执行的命令,比如,cd、dir、 type 等,这些命令由command
执行,command 执行完这些命令后,再次显示由当前盘符和当前路径组成的提示符,等待用户的输入。
如果用户要执行一个程序,则输入该程序的可执行文件的名称,command
首先根据文件名找到可执行文件,然后将这个可执行文件中的程序加载入内存,设置 CS:IP 指向程序的入口。此后,command
暂停运行,CPU运行程序。程序运行结束后,返回到command
中,command
再次显示由当前盘符和当前路径组成的提示符,等待用户的输入。
在 DOS 中,command
处理各种输入:命令
或要执行的程序
的文件名。我们就是通过command
来进行工作的。
4.9 程序执行过程的跟踪
在 DOS 系统中EXE 文件中的程序的加载过程
debug test.exe
CX:存放程序的长度。
DS:存放程序所在内存区的段地址。
CS:CS = DS + 10H。
PSP:此内存区的前256字节(100H),是DOS用来与程就进行通信的。
程序:PSP之后就是程序。物理地址:SA16 + 0 + 256 = SA * 16 + 1616 +0 = (SA+16) * 16 + 0
段地址:偏移地址表示为 SA+10H:0
- 单步执行用
T
- 执行到
int 21
用P
,正常执行结束会显示Program terminated normally
,然后回到debug
中。(因为是 Debug 加载程序进内存执行的)
实验3 编程、编译、连接、跟踪
《汇编语言》- 读书笔记 - 实验3 编程、编译、连接、跟踪