语雀原文链接
文章目录
1、执行过程
- 第一步:编写汇编源程序
- 第二步:对源程序进行编译连接。使用汇编语言编译程序对源程序文件中的源程序进行编译,产生目标文件;再用连接程序对目标文件进行连接,生成可在操作系统中直接运行的可执行文件。可执行文件包含两部分内容。
- 程序(从源程序中的汇编指令翻译过来的机器码)和数据(源程序中定义的数据)
- 相关的描述信息(比如,程序有多大、要占用多少内存空间等)
- 第三步:执行可执行文件中的程序。
- 操作系统依照可执行文件中的描述信息,将可执行文件中的机器码和数据加载入内存,并进行相关的初始化(比如设置CS:IP指向第一条要执行的指令),然后由CPU执行程序。
第一步:源程序
- 在汇编语言源程序中,包含两种指令,一种是汇编指令,一种是伪指令
- 汇编指令是有对应的机器码的指令,可以被编译为机器指令,最终为CPU所执行
- 伪指令没有对应的机器指令,最终不被CPU所执行。伪指令是由编译器来执行的指令,编译器根据伪指令来进行相关的编译工作。
- 新建一个first.asm文件,内容如下
assume cs:codemsg
codemsg segmentmov ax,0123Hmov bx,0456Hadd ax,bxadd ax,axmov ax,4c00Hint 21Hcodemsg ends
end
-
上述代码有3中伪指令
-
第一条伪指令:assume这个伪指令的含义为"假设"。假设某一段寄存器和程序中的某一个用segment…ends定义的段相关联。上述代码中,用assume cs:codesg将把代码段的codesg和CPU中的段寄存器cs联系起来
-
第二条伪指令:segment和ends是一对成对使用的伪指令,这是在写可被编记择器编译的汇编程序时,必须要用到的一对伪指令。segment和ends的功能是定义一个段,segment说明一个段开始,ends说明一个段结束。
-
第三条伪指令:end是一个汇编程序的结束标记,编译器在编译汇编程序的过程中,如果碰到了伪指令end,就结束对源程序的编译。所以,在我们写程序的时候,如果程序写完了,要在结尾处加上伪指令end。否则,编译器在编译程序时,无法去知道程序在何处结束。注意,不要搞混了end和ends,ends是和segment成对使用的,标记一个段的结束,ends的含义可理解为"end segment"。我们这里讲的end的作用是标记整个程序的结束。
-
基本格式如下
assume 段寄存器:xxx
xxx segment汇编指令......
xxx ends
end
第二步:编译连接
- 编译连接
第三步:执行
- 可执行文件怎么才能运行?
- 我们在DOS(一个单任务操作系统)的基础上,一个程序P2在可执行文件中,则必须有一个正在运行的程序P1,将P2从可执行文件中加载入内存后,将CPU的控制权交给P2,P2才能得以运行。P2开始运行后,P1暂停运行。而当P2运行完毕后,应该将CPU的控制权交还给使它得以运行的程序P1,此后,P1继续运行。
- 一个程序结束后,将CPU的控制权交还给使它得以运行的程序,我们称这个过程为:程序返回。在程序的末尾添加返回的程序段就可以实现程序返回
mov ax,4c00Hint 21H
2、DOSBox运行程序
第1步 进入EDIT.EXE
- 运行edit,IT/MASM文件中有几个EXE
- DEBUG.EXE:debug执行程序
- EDIT.EXE:编辑执行程序
- LINK.EXE:连接执行程序
- MASM.EXE:编译执行程序
Z:\>mount c d:\IT\MASM
Z:\>c:
Z:\>edit
第2步 编写源程序
- 编写保存源程序
- 保存到c:\first.asm
第3步 编译
- 在编译一个源程序之前首先要找到一个相应的编译器。这里采用微软的masm5.0汇编编译器,文件名为MASM.EXE。这里我们把它放在c:\MASM.EXE
C:\>masm
Microsoft (R) Macro Assembler Version 5.00
Copyright (C) Microsoft Corp 1981-1985, 1987. All rights reserved.Source filename [.ASM]:
- 运行masm后,首先显示出一些版本信息,然后提示输入将要被编译的源程序文件的名称。
- [.ASM]提示我们,默认的文件扩展名是asm,如果要编译的源程序文件名是"pl.asm",只要在这里输入"pl"即可。可如果源程序文件不是以asm为扩展名的话,就要输入它的全名。比如源程序文件名为"pl.txt",就要输入全名。在输入源程序文件名的时候一定要指明它所在的路径。如果文件就在当前路径下,只输入文件名就可以,可如果文件在其他的目录中,则要输入路径。比如,要编译的文件pl.txt在"c:\windows/desktop"下,则要输入"c:/windows/desktop\pbl.txt"
- 输入first,Enter回车3次即可
- Object filename:编译出的目标文件名称,默认和源程序同名,直接回车即可;如果想自定义目标文件路径和名称,例如输入“c:\xx\out”
- Source listing:编译程序提示输入列表文件的名称,这个文件是编译器将源程序编译为目标文件的过程中产生的中间结果。可以让编译器不生成这个文件,直接按Enter键即可。
- Cross-reference:编译程序提示输入交叉引用文件的名称,这个文件同列表文件一样是编译器将源程序编译为目标文件过程中产生的中间结果。可以让编译器不生成这个文件,直接按Enter键即可。
- 在编译的过程中,我们提供了一个输入,即源程序文件。最多可以得到3个输出:列表文件(.lst)、交叉引用文件(.crf)、目标文件(.obj)。这3个输出文件中,目标文件是我们最终要得到的结果,而另外两个只是中间结果,可以让编译器忽略对它们的生成。在汇编课程中,我们不讨论这两类文件。
C:\>masm
Microsoft (R) Macro Assembler Version 5.00
Copyright (C) Microsoft Corp 1981-1985, 1987. All rights reserved.Source filename [.ASM]: first
Object filename [first.OBJ]:
Source listing [NUL.LST]:
Cross-reference [NUL.CRF]:51798 + 464746 Bytes symbol space free0 Warning Errors0 Server Errors
C:\>masm
第4步 连接
- [.OBJ]:输入要连接的文件,此处输入first(无须后缀.OBJ)
- Run File:输入要生成的可执行文件的名称,默认同名.EXE,直接Enter使用默认值
- List File:连接程序提示输入映像文件的名称,这个文件是连接程序将目标文件连接为可执行文件过程中产生的中间结果,可以让连接程序不生成这个文件,直接按Enter键即可。
- Libraries:如果程序中调用了某一个库文件中的子程序,就需要在连接的时候,将这个库文件和目标文件连接到一起,生成可执行文件。如果这个程序没有调用任何子程序,这里忽略库文件名的输入,直接按Enter键即可。
- 最后输出一行警告:no stack segment,这里无需理会
C:\>link
Microsoft (R) Overly Linker Version 3.60
Copyright (C) Microsoft Corp 1981-1985, 1987. All rights reserved.Object Module [.OBJ]: first
Run File [FIRST.EXE]:
List File [NUL.MAP]:
Libraries [.LIB]:
LINK : warning L4021: no stack segment
- 连接有什么作用
-
- 当源程序很大时,可以将它分为多个源程序文件来编译,每个源程序编译成为目标文件后,再用连接程序将它们连接到一起,生成一个可执行文件;
-
- 程序中调用了某个库文件中的子程序,需要将这个库文件利该程序生成的目标文件连接到一起,生成一个可执行文件;
- 3.一个源程序编译后,得到了存有机器码的目标文件,目标文件中的有些内容还不能直接用来生成可执行文件,连接程序将这些内容处理为最终的可执行信息。所以,在只有一个源程序文件,而又不需要调用某个库中的子程序的情况下,也必须用连接程序对目标文件进行处理,生成可执行文件。
-
- 上述的编译和连接写法步骤比较多,可以简化以下,忽略一些过程
- masm c:\first;
- link first;
C:\>masm c:\first;
Microsoft (R) Macro Assembler Version 5.00
Copyright (C) Microsoft Corp 1981-1985, 1987. All rights reserved.51798 + 464746 Bytes symbol space free0 Warning Errors0 Server ErrorsC:\>link first;
Microsoft (R) Overly Linker Version 3.60
Copyright (C) Microsoft Corp 1981-1985, 1987. All rights reserved.LINK : warning L4021: no stack segment
第5步 执行
- 直接运行FIRST.EXE,输入first即可,这里也看不到任何结果
C:\>firstc:\>
完整过程
3、DEBUG跟踪执行过程
加载程序到内存
- 在DOS中运行一个程序的时候,是由command将程序从可执行文件中加载入内存并使其得以执行。但是,这样我们不能逐条扣指令地看到程序的执行过程,因为command的程序加载,设置CS:IP指向程序的入口的操作是连续完成的,而当CS:IP一指向程序的入口,command就放弃了CPU的控制权,CPU了立即开始运行程序,直至程序结束。
- DEBUG将程序FIRST.EXE加载入内存,进行相关的初始化后设置CS:IP指向程序的入口
c:\>debug first.exe
-r
AX=FFFF BX=0000 CX=000F DX=0000 SP=00FD BP=0000 SI=0000 DI=0000
DS=075A ES=075A SS=0769 CS=076A IP=0000 NV UP EI PL NZ NA PO NC
076A:0000 B82301 MOV AX,0123
-u
076A:0000 B82301 MOV AX,0123
076A:0003 BB5604 MOV BX,0456
076A:0006 03C3 ADD AX,BX
076A:0008 03C0 ADD AX,AX
076A:000A B8004C MOV AX,4C00
076A:000D CD21 INT 21
076A:000F 0000 ADD [BX+SI],AL
076A:0011 0000 ADD [BX+SI],AL
076A:0013 0000 ADD [BX+SI],AL
076A:0015 0000 ADD [BX+SI],AL
076A:0017 0000 ADD [BX+SI],AL
076A:0019 0000 ADD [BX+SI],AL
076A:001B 0000 ADD [BX+SI],AL
076A:001D 0000 ADD [BX+SI],AL
076A:001F 0000 ADD [BX+SI],AL
- 可以看到程序加载到内存从076A:0000~076A:000E,占据15个字节。CX=000F存储的就是程序的长度
- 程序为何存储在076A:0000开始,可以看下图(图中有一步重定位没有写,这里不做介绍)
- 程序加载后,DS=075A存放着程序所在内存区的段地址,这个内存区的偏移地址为0,所以这个内存区的地址为DS:0,也就是075A:0000
- 这个内存区的前256个字节存放的是PSP(DOS用来和程序进行通信),从256个字节向后的空间放的是程序,所以程序真真是从075A+10H = 076A:0000开始的,所以CS=076A IP=0000,指向程序的第一条指令
执行程序
c:\>debug first.exe
-r
AX=FFFF BX=0000 CX=000F DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=075A ES=075A SS=0769 CS=076A IP=0000 NV UP EI PL NZ NA PO NC
076A:0000 B82301 MOV AX,0123
-u
076A:0000 B82301 MOV AX,0123
076A:0003 BB5604 MOV BX,0456
076A:0006 03C3 ADD AX,BX
076A:0008 03C0 ADD AX,AX
076A:000A B8004C MOV AX,4C00
076A:000D CD21 INT 21
076A:000F 0000 ADD [BX+SI],AL
076A:0011 0000 ADD [BX+SI],AL
076A:0013 0000 ADD [BX+SI],AL
076A:0015 0000 ADD [BX+SI],AL
076A:0017 0000 ADD [BX+SI],AL
076A:0019 0000 ADD [BX+SI],AL
076A:001B 0000 ADD [BX+SI],AL
076A:001D 0000 ADD [BX+SI],AL
076A:001F 0000 ADD [BX+SI],AL// 执行MOV AX,0123,结果AX=0123 IP=0003指向下一条指令
-t
AX=0123 BX=0000 CX=000F DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=075A ES=075A SS=0769 CS=076A IP=0003 NV UP EI PL NZ NA PO NC
076A:0003 BB5604 MOV BX,0456// 执行MOV BX,0456,结果BX=0456 IP=0006指向下一条指令
-t
AX=0123 BX=0456 CX=000F DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=075A ES=075A SS=0769 CS=076A IP=0006 NV UP EI PL NZ NA PO NC
076A:0006 03C3 ADD AX,BX// 执行ADD AX,BX,结果AX=0579 IP=0008指向下一条指令
-t
AX=0579 BX=0456 CX=000F DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=075A ES=075A SS=0769 CS=076A IP=0008 NV UP EI PL NZ NA PO NC
076A:0008 03C0 ADD AX,AX// 执行ADD AX,AX,结果AX=0AF2 IP=0006指向下一条指令
-t
AX=0579 BX=0456 CX=000F DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=075A ES=075A SS=0769 CS=076A IP=000A NV UP EI PL NZ NA PO NC
076A:000A B8004C MOV AX,4C00// 执行MOV AX,4C00,结果AX=4C00 IP=0006指向下一条指令
-t
AX=4C00 BX=0456 CX=000F DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=075A ES=075A SS=0769 CS=076A IP=000D NV UP EI PL NZ NA PO NC
076A:000D CD21 INT 21// 最后执行int 21,这里需要输入p
-p
Program terminated normally
debug和源程序数字的默认进制
- 在debug中,默认所有数字都是 16 进制,加了 h 反而会报错;
- 在汇编源程序 .asm 中,不加 h 则默认为 10 进制,加 h 才表示 16 进制,加 b 为后缀为二进制;
- 使用 debug 跟踪可执行程序 .exe 的时候,debug会将不加 h 的 10 进制(或者加了b为后缀的二进制)变为对应的 16 进制。
4、DOS系统的shell
- 操作系统是由多个功能模块组成的庞大、复杂的软件系统。任何通用的操作系统,都要提供一个称为shell(外壳)的程序,用户(操作人员)使用这个程序来操作计算机系统进行工作。
- DOS中有一个程序command.com,这个程序在DOS中称为命令解释器,也就是DOS系统的shell。
- DOS启动时,先完成其他重要的初始化工作,然后运行command.com,运行后执行完其他的相关任务后,在屏幕上显示出由当前盘符和当前路径组成的提示符,比如:"c:"或"c:\windows"等,然后等待用户的输入。
- 用户可以输入所要执行的命令,比如,cd、dir、type等,这些些命令由command执行,command执行完这些命令后,再次显示由当前盘符和当前路径组成的提示符,等持用户的输入。
- 如果用户要执行一个程序,则输入该程序的可执行文件的名称,command首先根据文件名找到可执行文件,然后将这个可执行文件中的程序加载入内存,设置CS:IP指向程序的入口。此后,command暂停运行,CPU运行程序。程序运行结束后,返回到command中,command再次显示由当前盘符和当前路径组成的提示符,等待用户的输入。
- 在DOS中,command处理各种输入:命令或要执行的程序的文件名。我们就是通过command来进行工作的。