您的位置:首页 > 编程语言

实验3 编程、编译、连接、跟踪

2018-02-13 17:17 169 查看
实验3 编程、编译、连接、跟踪
(1)将下面的程序保存为t1.asm文件,将其生产可执行文件t1.exe。
assume cs:codesg
codesg segment
mov ax, 2000H
mov ss, ax
mov sp, 0
add sp, 10
pop ax
pop bx
push ax
push bx
pop ax
pop bx

mov ax, 4C00H
int 21H
codesg ends
end

(2)用debug跟踪t1.exe的执行过程,写出每一步骤执行后,相关寄存器中的内容和栈顶的内容。
(3)PSP的头两个字节是CD 20,用debug加载t1.exe,查看PSP的内容。

实验结果:【1】编译、连接t1.asm汇编源程序。1)在windows XP中,打开cmd窗口,编译t1.asm程序。如下图:E:\assembly>masm t1.asmMicrosoft (R) MASM Compatibility DriverCopyright (C) Microsoft Corp 1993.  All rights reserved.  Invoking: ML.EXE /I. /Zm /c /Ta t1.asm Microsoft (R) Macro Assembler Version 6.15.8803        Patched for you by promethee [ECL] in the year 2001 - enjoyCopyright (C) Microsoft Corp 1981-2000.  All rights reserved.  Assembling: t1.asm如果没有任何错误,编译器会生成一个t1.obj的文件。如果有语法等严重的错误,编译器会给出错误信息,你可以根据错误信息,修改源代码相关的行。连接这个obj文件,并生成t1.exe文件:如下图:E:\assembly>link t1.objMicrosoft (R) Segmented Executable Linker  Version 5.60.339 Dec  5 1994Copyright (C) Microsoft Corp 1984-1993.  All rights reserved. Run File [t1.exe]: t1.exeList File [nul.map]:Libraries [.lib]:[b]Definitions File [nul.def]:LINK : warning L4021: no stack segmentLINK : warning L4038: program has no starting address连接讲解:1)其中运行文件:我们输入我们将要生成的可执行程序(例如.exe)文件的名称。List File [nul.map]: 我们回车,没有列表文件;Libraries [.lib]: 我们回车,没有库文件Definitions File [nul.def]: 我们回车,没有交叉定义文件2)LINK : warning L4021: no stack segment       警告信息:没有栈段定义;  LINK : warning L4038: program has no starting address       警告信息:程序没有起始地址。       你可以根据连接程序的提示信息来修正你的源程序;一般的警告信息,我们都忽略了。关于编译器的详细介绍,请参考相应的编译器手册。【2】用debug跟踪t1.exe的执行过程在命令提示符下键入:debug t1.exe使用r命令查看寄存器信息。-rAX=0000  BX=0000  CX=0016  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000DS=0B55  ES=0B55  SS=0B65  CS=0B65  IP=0000   NV UP EI PL NZ NA PO NC0B65:0000 B80020        MOV     AX,20001)CX寄存器介绍:cx=0016; 含义:cx寄存器是通用寄存器,也是计数寄存器,cx还有一个功能是在程序最初始记录程序代码的字节数;我们使用d命令查看它-d cs:00B65:0000  B8 00 20 8E D0 BC 00 00-83 C4 0A 58 5B 50 53 58   .. ........X[PSX0B65:0010  5B B8 00 4C CD 21 A9 02-00 75 05 2E FF 06 48 91   [..L.!...u....H.红色标记的是执行的机器码。注意此时的0016是16进制的,也就是22个字节,你数数!你也可以使用U命令查看汇编指令和机器码,一样。-u cs:00B65:0000 B80020        MOV     AX,20000B65:0003 8ED0          MOV     SS,AX0B65:0005 BC0000        MOV     SP,00000B65:0008 83C40A        ADD     SP,+0A0B65:000B 58            POP     AX0B65:000C 5B            POP     BX0B65:000D 50            PUSH    AX0B65:000E 53            PUSH    BX0B65:000F 58            POP     AX0B65:0010 5B            POP     BX0B65:0011 B8004C        MOV     AX,4C000B65:0014 CD21          INT     21各寄存器状态值:CS=0B65  IP=0000      SS=0B652)执行代码  mov ax, 2000H  mov ss, ax  mov sp, 0代码含义:将2000H段开始的内存单元创建一个栈结构。栈顶指针ss:sp指向00H。这就意味着如果栈结构是空的,那么它的栈容量是64K。    &n@��怎么没有了那些t命令的中断例程保存的寄存器变量了?你在1FFF:0010H处,也就是它的低地址处看看有吗?执行后:AX=2000  BX=0000  CX=0016  DX=0000  SP=0000  BP=0000  SI=0000  DI=0000DS=0B55  ES=0B55  SS=2000  CS=0B65  IP=0008   NV UP EI PL NZ NA PO NC3)执行代码:       add sp, 10代码含义:明确说明sp指向10,也就是说我们创建的栈结构空间分配是10个字节;       使用d命令查看ss:0内存段,发现那些久违的寄存器变量数据就存储在这里,从2000:0a向低地址存储;我们不用管他们。执行后:AX=2000  BX=0000  CX=0016  DX=0000  SP=000A  BP=0000  SI=0000  DI=0000DS=0B55  ES=0B55  SS=2000  CS=0B65  IP=000B   NV UP EI PL NZ NA PE NC4)执行代码:  pop ax  pop bx代码含义:pop ax;将ss:sp指向的字单元赋值给ax=00;sp=sp+2=000A+2=000CH;                pop bx;将ss:sp指向的字单元赋值给bx=00;sp=sp+2=000C+2=000EH;       见下图中的红色标记;此时栈顶已经超界了。蓝色部分是给栈分配的内存空间。注意此时栈中有数据吗?有!就是蓝色部分,只不过是中断例程保护的寄存器变量的值。-d ss:02000:0000  00 20 00 00 0B 00 65 0B-68 05 00 00 00 00 00 00   . ....e.h.......执行后:AX=0000  BX=0000  CX=0016  DX=0000  SP=000E  BP=0000  SI=0000  DI=0000DS=0B55  ES=0B55  SS=2000  CS=0B65  IP=000D   NV UP EI PL NZ NA PE NC使用d查看栈段内存:-d ss:02000:0000  00 20 00 00 00 00 00 00-0D 00 65 0B 68 05 00 00   . ........e.h...我们发现sp=000EH,也就是栈底向高地址发展了,栈结构占用的内存空间也变大了。5)执行代码:  push ax  push bx代码含义:push ax;首先sp=sp-2=000EH-2=000CH;然后将ax值压栈,此时ax=00,                push bx;首先sp=sp-2=000CH-2=000AH;然后将bx值压栈,此时bx=00,执行后:AX=0000  BX=0000  CX=0016  DX=0000  SP=000A  BP=0000  SI=0000  DI=0000DS=0B55  ES=0B55  SS=2000  CS=0B65  IP=000F   NV UP EI PL NZ NA PE NC使用d查看栈段内存:-d ss:02000:0000  00 00 00 00 0F 00 65 0B-68 05 00 00 00 00 00 00   ......e.h.......内存中红色标记的部分就是压栈的2个字单元。6)执行代码:  pop ax  pop bx代码含义:pop ax;首先将ss:sp指向的内存字单元赋值给ax,也就是2000:000aH指向的字单元(00 00);然后sp=sp+2=000AH+2=000CH;                pop bx;首先将ss:sp指向的内存字单元赋值给bx,也就是2000:000cH指向的字单元(00 00);然后sp=sp+2=000CH+2=000EH;执行后:AX=0000  BX=0000  CX=0016  DX=0000  SP=000E  BP=0000  SI=0000  DI=0000DS=0B55  ES=0B55  SS=2000  CS=0B65  IP=0011   NV UP EI PL NZ NA PE NC使用d查看栈段内存:-d ss:02000:0000  00 00 00 00 00 00 00 00-11 00 65 0B 68 05 00 00   ..........e.h...此时sp指向了内存单元红色的地址。总结:       从CPU层面理解熟悉栈的结构,栈顶指针的变化;对push和pop两个指令执行后,CPU的执行步骤。       从编程角度要注意push和pop指令对栈中数据的顺序。       这个貌似sp有点变化,其他的寄存器死气沉沉的。呵呵我们使用栈时要小心,这个例子,栈结构的内存是没有使用的,它的栈顶随意的超界,没有事情,如果有数据的话,就破坏程序了。(3)PSP的头两个字节是CD 20,用debug加载t1.exe,查看PSP的内容。       首先要搞清楚,PSP段在什么地方?在代码段的前100H处。也就是上面的ds:0处。ds:100内存单元就是代码段的内容。你看看ds与cs值有什么联系?       PSP头的作用,请看书吧。       使用d命令查看-d ds:00B55:0000  CD 20 FF 9F 00 9A F0 FE-1D F0 4F 03 68 05 8A 03   . ........O.h...0B55:0010  68 05 17 03 68 05 57 05-01 01 01 00 02 FF FF FF   h...h.W.........0B55:0020  FF FF FF FF FF FF FF FF-FF FF FF FF 15 0B 4C 01   ..............L.0B55:0030  28 0A 14 00 18 00 55 0B-FF FF FF FF 00 00 00 00   (.....U.........0B55:0040  05 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................0B55:0050  CD 21 CB 00 00 00 00 00-00 00 00 00 00 20 20 20   .!...........0B55:0060  20 20 20 20 20 20 20 20-00 00 00 00 00 20 20 20           .....0B55:0070  20 20 20 20 20 20 20 20-00 00 00 00 00 00 00 00           ........
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: