DEBUG的基本使用

实验一 DEBUG的基本使用
1. 教学目的
 掌握DEBUG 的基本命令及其功能
 理解汇编指令、存储单元和寄存器等有关概念
 熟悉简单汇编指令汇编过程
2. 讲授新课
(1)什么是DEBUG
DEBUG是DOS、WINDOWS都提供的实模式(8086方式)程序的调试工具。使用它,可以查看CPU各种寄存器中的内容,内存的情况和在机器码跟踪程序的运行。
(2)用到的DEBUG功能
用DEBUG的R命令查看、改变CPU寄存器的内容:
用DEBUG的D命令查看内存中的内容:
用DEBUG的E命令改写内存中的内容:
用DEBUG的U命令将内存中的机器指令翻译成汇编指令:
用DEBUG的T命令执行一条机器指令:
用DEBUG的A命令以汇编指令的格式在内存中写入一条机器指令:
DEBUG命令比较多,共有20多个,但上述6个命令是和汇编语言学习密切相关的。在以后的实验中,我们还会用到一个P命令。
(3)进入DEBUG
DEBUG是在DOS方式下使用的程序。我们在进入DEBUG前,应先进入到DOS方式。
用以下方式可以进入DOS:
①重启计算机后,进入DOS方式,此时我们进入的是实模式的DOS。
②在WINDOWS中进入DOS方式,此时进入的是虚拟8086模式的DOS。
下面说明在Windows中进入Debug的一种方法。
选择【开始】菜单中的【运行】命令,打开【运行】对话框,在文本框中输入“command”后,单击【确定】按钮。进入DOS方式后,如果显示为窗口方式,可以按下Alt+Enter键将窗口变为全屏方式,然后运行Debug程序。

“-”是DEBUG的命令提示符,表明已进入DEBUG状态。在“-”提示符之后,可以键入DEBUG命令。
退出DEBUG时,键入如下命令从DEBUG状态返回DOS:
-Q
(4)用R命令查看、改变CPU寄存器的内容
已知道了AX、BX、CX、DX、SS、IP这6个寄存器,现在看一下它们之中的内容。
格式:R [<寄存器>]
功能:显示或修改寄存器内容。
说明:当R命令后面不指定寄存器时,显示所有寄存器的内容。
注意CS和IP的值,CS=0CA2,IP=0100,也就是说,0CA2:0100处的指令为CPU当前要读取、执行的指令。在所有的寄存器的下方,DEBUG还列出了CS:IP所指向的内存单元处的所存放的机器码,并将它翻译为汇编指令。可以看到,CS:IP所指向的内存单元为0CA2:0100此处存放的机器码为027548,对应的汇编指令为ADD DH,[DI+48],DEBUG输出的右下角还有一个信息:“DS:0048=0”,以后会进行说明。
若要修改一个寄存器中的值,比如AX中的值,输入“R AX”后按ENTER键,将出现“:”作为输出提示,在后面输入要写入的数据后按ENTER键,即完成了对AX中内容的修改。
用R命令查看,CS:IP指向0B39:0100,此处存放的机器码为40,对应的汇编指令是INC AX,接着,用R命令将CS修改为FF00,则CS:IP指向FF00:0200,此处存储的机器码为51,对应的汇编指令是PUSH CX。
(5)用DEBUG的R命令查看内存中的内容
用DEBUG的D命令,可以查看内存中的内容,D命令的格式较多。我们这里只介绍在以本次实验中用到的格式。
如果想知道内存10000H处的内容,
“D 段地址:偏移地址”
使用“D 段地址:偏移地址”的格式,DEBUG将列出从指定的内存单元开始的128个内存单元的内容,在使用D 1000:0后,DEBUG列出了1000:0~1000:7F中的内容。
使用D命令,DEBUG将输出3部分内容:
中间是部分从指定地址开始的128个内存单元的内容,用十六进制的格式输出,每行的输出从16的整数倍的地址开始,最多输出16个单元的内容。注意在每行的中间有一个“-”,它将每行的输出分为两部分,这样便于查看。
左边是每行的起始地址。
右边是每个内存单元中的数据对应的可以显示的ASCII码字符,它没有对应可显示的ASCII字符,DEBUG应用”.”来代替。
使用D 1000:9查看1000:9处的内容,DEBUG将怎样输出呢?
DEBUG从1000:9开始显示,一直到1000:88,一共是128个字节。第一行中的1000:0~1000:8单元中的内容不显示。
在一进入DEUBG后,用D命令直接查看,将列出DEBUG预设的地址的内容。
在使用“D 段地址:偏移地址”之后,接着使用D命令,可以列出后续的内容。、
也可以指定D命令的查看范围,此时采用“D 段地址:起始偏移地址 结尾偏移地址“的格式。比如要看1000:0~1000:9中的内容,可以用”D 1000:0 9“实现。
(6)用DEBUG的A命令以汇编指令的形式在内存中写入机器指令,直接以汇编指令的形式写入指令。
格式:A [<起始地址>]
功能:逐行汇编程序,主要用于小段程序的汇编。
说明:使用A命令汇编程序,不允许程序中出现标号和伪指令,但MS-DOS的DEBUG允许使用DB和DW这两条伪指令。如果未给出起始地址,汇编后的程序代码从指令指针寄存器IP的值指定的地址开始存放。
按两次回车键可退出汇编命令A的状态。
首先用A命令,以汇编语言向从1000:0开始的内存单元中写入了几条指令,然后用D命令查看A命令的执行结果,可以看到,在使用A命令写入指令时,我们输入的是汇编指令,DEBUG将这些汇编指令翻译为对应的机器指令,将它们的机器码写入内存。
3. 作业
(1) 使用Debug的A命令,将下面的程序段写入内存。
汇编指令: mov ax,4E20H
add ax,1416H
mov bx,2000H
add ax,bx
mov bx,ax
add ax,bx
mov ax,001AH
mov bx,0026H
add al,bl
add ah,bl
(2)使用Debug的D命令,写出上述指令所对应的机器码。
4. 小结
实验二 用DEBUG查看CPU和内存
1.教学目的
 熟练掌握DEBUG 的常用命令及其功能
 掌握汇编指令汇编、反汇编、执行等过程
 掌握几条汇编指令的功能
2. 讲授新课
(1)用DEBUG的E命令改写内存中的内容
要将内容1000:0~1000:9单元中的内容分别写为0、1、2、3、4、5、6、7、8、9,
可以用它“E 起始地址 数据 数据 数据。。。“的格式来进行。
先用D命令查看1000:0~1000:F单元的内容,再用E命令修改从1000:0开始的10个单元的内容,最后用D命令查看1000:0~1000:F中的内容的变化。
可以用E命令提问的方式来逐个地修改从某一地址开始的内存单元中的内容,以从1000:10单元开始为例,步骤如下:
①输入E 1000:10,按ENTER键。
②DEBUG显示起始地址1000:0010,和第一单元(即1000:0010单元)的原始内容:6D,然后光标停在“.“的后面提示输入想要写入数据,此时可以有两个选择:其一为输入数据(我们输入的是0),然后按空格键,则对当前内存单元进行改写。其二直接按空格键,则不对当前内存单元进行改写。
③当前单元处理完成后(不论是改写或没有改写,只要按了空格键,就表示处理完成),DEBUG将接着显示下一个内存单元的原始内容,并提示读者进行修改,读者可以用同样的方法处理。
可以用E命令向内存中写入字符,比如:用E命令从内存1000:0开始 写入:数值1、字符‘a’、数值3、字符‘c’
可以看出,DEBUG对E命令的执行结果是,向1000:0、1000:2、1000:4单元中写入数值1,2,3,向1000:1、1000:3、1000:5单元中写入字符”a”,”b”,”c”的ASCII码值:61H,62H,63H。
也可以用E命令向内存中写入字符串,比如:用E命令从内存1000:0开始写入:数值1、字符串”a+b”、数值2、字符串”c++”、数值3、字符串”IBM”。
(2)用E命令向内存中写入机器码,用U命令查看内存中机器码的含义,用T命令执行内存中的机器码。
如何向内存中写入机器码呢?我们知道,机器码也是数据,当然可以用E命令将机器码写入内存。
比如要从内存1000:0单元开始写入这样一段机器码:
机器码 对应的汇编指令
B8 01 00 MOV AX,0001
B9 02 00 MOV CX,0002
01C8 ADD AX,CX
如何查看我们写入的或内存中原有的机器码所对应的汇编指令呢?
可以用U命令进行。
比如可以用U命令将从1000:0开始的内存单元中的内容翻译为汇编指令,并显示出来。
首先用E命令向从10000:0开始的内存单元中写入了8个字节的机器码:然后用D命令查看内存1000:0~1000:1F数据(从数据的角度看一下我们写入的内容):最后用U命令查看从1000:0开始内存单元中的机器指令和它们所对应的汇编指令。
U命令的显示输出分为3部分:
第一条机器指令的地址、机器指令、所对应的汇编指令。
我们可以看到:
1000:0处存放的是我们写入的机器码B80100所组成的机器指令,对应的汇编指令是MOV AX,1
1000:3处存放的是我们写入的机器码B80C00所组成的机器指令、对应的汇编指令是ADD CX,2
1000:6处存放的是我们写入的机器码01C8所组成的机器指令、对应的汇编指令是ADD AX,CX
1000:8处存放的是内存中的机器码是034942所组成的机器指令、对应的汇编指令是ADD CX,[BX+DI+42]
由此,我们可以再次看到内存中的数据和代码没有任何区别,关键在于如何解释。
如何执行我们写入的机器指令?使用DEBUG的T命令可以执行一条或多条指令,简单使用T命令,可以执行CS:IP指向的指令。
首先用E命令向从1000:0开始的内存单元中写入了8个字节的机器码;
然后R命令查看CPU中寄存器的状,可以看到CS=0B39H,IP=0100H,指向内存0B39:0100;
若要用T从控制CPU执行我们写到1000:0的指令,必须先让CS:IP指向1000:0;
用R命令修改CS、IP中的内容,使CS:IP指向1000:0
完成上面的步骤后,就可以使用T命令来执行我们写入的指令了(此时,CS:IP指向我们的指令所在的内存单元)。
执行T命令后,CPU执行CS:IP指向的指令,则1000:0处的指令B8 01 00(MOV AX,0001)得到执行,指令执行后,DEBUG显示输出CPU中寄存器的状态。
注意,指令执行后,AX中的内容被改写为1,IP改变为IP+3(因为MOV AX,0001的指令长度为3个单元),CS:IP指向下一条指令。
接着可以继续使用T命令执行下面的指令。
用T命令继续执行后面的指令,注意每条指令执行后CPU相关寄存器内容的变化。
3. 作业
(1)使用Debug的E命令,将下面的程序段写入内存,逐条执行,观察每条指令执行后,CPU中相关寄存器中内容的变化。
机器码:b8 20 4e
05 16 14
bb 00 20
01 d8
89 c3
01 d8
b8 1a 00
bb 26 00
00 d8
00 dc
00 c7
b4 00
00 d8
04 9c
(2)将下面3条指令写入从2000:0开始的内存单元中,利用这3条指令计算2的8次方。
mov ax,1
add ax,ax
jmp 2000:0003
(3)查看内存中的内容。
PC机主板上的ROM中写有一个生产日期,在内存FFF00H~FFFFFH的某几个单元中,请找到这个生产日期并试图改变它。
(4)向内存从B8100H开始的单元中填写数据,请先填写不同的数据,观察产生的现象;再改变填写的地址,观察产生的现象。
如:-e B810:0000 01 01 02 02 03 03 04 04

DEBUG命令表
命令及其功能
A(Assemble)汇编源程序
U(Unassemble)对二进制指令代码进行反汇编
T(Trace)跟踪执行程序并显示寄存器内容
P(Proceed)跟踪执行一组相关的指令
D(Dump)显示存储区数据
E(Enter)修改存储区数据
F(Fill)将成组数据填入存储区
G(Go)运行程序
R(Register)显示和修改寄存器内容
N(Name)定义文件名
L(Load)装入文件或磁盘扇区
W(Write)写文件或写磁盘扇区
M(Move)传送存储区数据块
I(Input)读/显示端口
O(Output)输出数据到端口
H(Hexarithmetic)十六进制加减法运算
C(Compare)比较存储区数据
S(Search)检索字节或字符串
Q(Quit)退出DEBUG

实验三 用机器指令和汇编指令编程
1.教学目的
 掌握汇编指令的跟踪执行过程
 理解CPU提供的栈机制
 理解段寄存器的概念
2. 讲授新课
(1)关于D命令
段地址是放在段寄存器中的,在D命令后面直接给出段地址,是Debug提供的一种直观的操作方式。D命令是由Debug执行的,Debug在执行“d 1000:0”这样的命令时,也会先将段地址1000送入段寄存器中。
Debug是靠什么来执行D命令的?当然是一段程序。
谁来执行这段程序?当然是CPU。
CPU在访问内存单元的时候从哪里得到内存单元的段地址?从段寄存器中得到。
所以,Debug在其处理D命令的程序段中,必须有将段地址送入段寄存器的代码。
段寄存器有4个:CS、DS、SS、ES,将段地址送入哪个段寄存器呢?
首先不能是CS,因为CS:IP必须指向Debug处理D命令的代码,
也不能是SS,因为SS:SP要指向栈顶,
这样只剩下了DS和ES可以选择,放在哪里呢?
我们知道,访问内存的指令如“mov ax,[0]”等一般都默认段地址在ds中,所以Debug在执行
如:“d 段地址:偏移地址”这种D命令时,将段地址送入ds中比较方便。
D命令也提供了一种符合CPU机理的格式:
“d 段寄存器:偏移地址”,
以段寄存器中的数据为段地址SA,列出从SA:偏移地址开始的内存区间中的数据。以下是4个例子:
① -r ds
:1000
-d ds:0 ;查看从1000:0开始的内存区间中的内容
② -r ds
:1000
-d ds:10 18 ;查看1000:10~1000:18中的内容
③ -d cs:0 ;查看当前代码段中的指令代码
④ -d ss:0 ;查看当前栈段中的内容
(2)在E、A、U命令中使用段寄存器
在E、A、U这些可以带有内存单元地址的命令中,也可以同D命令一样,用段寄存器表示内存单元的段地址。以下是3个例子:
① -r ds
:1000
-e ds:0 11 22 33 44 55 66
;在从1000:0开始的内存区间中写入数据
② -u cs:0
;以汇编指令的形式,显示当前代码段中的代码,0代码的偏移地址
③ -r ds
:1000
-a ds:0
;以汇编指令的形式,向从1000:0开始的内存单元中写入指令
(3)下一条指令执行了吗?
在Debug中,用A命令写一段程序:
mov ax,2000
mov ss,ax
mov sp,10
;安排2000:0000~2000:000F 为栈空间,初始化栈顶。
mov ax,3123
push ax
mov ax,3366
push ax ;在栈中压入两个数据。
在用T命令单步执行mov ax,2000后,显示出当前CPU各个寄存器的状态和下一步要执行的指令:mov ss,ax;
在用T命令单步执行mov ss,ax后,显示出当前CPU各个寄存器的状态和下一步要执行的指令……,在这里我们发现了一个问题:mov ss,ax的下一条指令应该是mov sp,10,怎么变成了mov ax,3123?
在程序执行前,ax=0000,ss=0b39,sp=ffee
在用T命令单步执行mov ax,2000后,ax=2000;ss=0b39;sp=ffee
在用T命令单步执行mov ss,ax 后,ax=2000;ss=2000;sp=0010
注意,在用T命令单步执行mov ss,ax 前,ss=0b39,sp=ffee,而执行后 ss=2000,sp=0010。
ss变为2000是正常的,这正是mov ss,ax的执行结果。可是sp变为0010是怎么回事?在这期间,能够将sp设为0010的只有指令mov sp,10,看来,mov sp,10一定是得到了执行。
那么,mov sp,10是在什么时候被执行的呢?
当然是在mov ss,ax之后,因为它就是mov ss,ax的下一条指令。
显然,在用T命令执行mov ss,ax的时候,它的下一条指令mov sp,10也紧接着执行了。
整理一下我们分析的结果:在用T命令执行mov ss,ax的时候,它的下一条指令mov sp,10也紧接着执行了。
一般情况下,用T命令执行一条指令后,会停止继续执行,显示出当前CPU各个寄存器的状态和下一步要执行的指令,但T命令执行mov ss,ax的时候,没有做到这一点。
不单是mov ss,ax,对于如:mov ss,bx,mov ss,[0],pop ss等指令都会发生上面的情况,这些指令有哪些共性呢?它们都是修改栈段寄存器SS的指令。
为什么会这样呢?要想彻底说清楚这里面的来龙去脉,在这里还为时过早,因为这涉及到我们在以后的课程中要深入研究的内容:中断机制,它是我们后半部分课程中的一个主题。
现在我们只要知道这一点就可以了:Debug的T命令在执行修改寄存器SS的指令时,下一条指令也紧接着被执行。
3. 作业
(1)使用Debug,将上面的程序段写入内存,逐条执行,根据指令执行后的实际运行情况填空。
mov ax,ffff
mov ds,ax
mov ax,2200
mov ss,ax
mov sp,0100
mov ax,[0] ;ax=
add ax,[2] ;ax=
mov bx,[4] ;bx=
add bx,[6] ;bx=
push ax ;sp=
;修改的内存单元的地址是 内容为
push bx ;sp=
;修改的内存单元的地址是 内容为
pop ax ;sp= ;ax=
pop bx ;sp= ;bx=
push [4] ;sp=
;修改的内存单元的地址是 内容为
push [6] ;sp=
;修改的内存单元的地址是 内容为
实验四 汇编语言程序的调试与运行
1.教学目的
 掌握汇编源程序编辑的基本方法
 掌握汇编程序的汇编与连接
 掌握汇编程序的调试与运行
2. 讲授新课
(1)编辑源程序
可以用任意的文本编辑器来编辑源程序,只要最终将其存储为纯文本文件即可。
一般保存为*.asm文件。
(2)汇编源程序
上机过程中,我们采用微软的masm6.0汇编编译器,文件名为masm.exe。
假设我们的编译器在C:\masm5目录下,可以按照下面的过程来进行源程序的汇编,以C:\1.asm为例:
进入DOS方式,进入C:\masm5目录,运行masm.exe,首先显示一些版本信息,然后提示输入将要被编译的源程序文件名。
在输入源程序文件名时一定要指明路径,如果文件在当前路径下,只要输入文件名就可以,否则要输入全路径。
在此,建议输入C:\1.asm
输入源程序文件名后,程序继续提示我们输入要编译出的目标文件名,因为我们已经输入了源程序文件名为1.asm,则编译程序默认要输出的目标文件名为1.obj,所以我们可以不必另行指定文件名,直接按回车,编译程序将在当前目录下,生出1.obj文件。
我们也可以指定生成的目标文件所在的目录,如想在“C:\windows\desktop”下生成目标文件1.obj,则可以输入“C:\windows\desktop\1”
确定了目标文件名称后,编译程序提示输入列表文件名称和交叉引用文件名称,这些时编译器将源程序编译为目标文件过程中产生的中间结果,我们直接按回车键忽略。
对源程序编译结束,编译器输出的最后两行告诉我们源程序是否有警告错误和必须要改正的错误。
(3)对目标文件进行连接
对源程序进行编译得到目标文件后,需要对目标文件进行链接得到可执行文件。
我们使用微软的Overlay Linker3.6连接器,文件名为link.exe,假设连接器在C:\masm5目录下,我们按照下面的过程来进行程序的链接,以C:\masm5\1.obj为例:
进入DOS方式,进入C:\masm5目录,运行link.exe,首先显示一些版本信息,然后提示输入将要被连接的目标文件名。
此处我们输入”1”,按回车,程序继续提示我们输入要生成的可执行文件的名称,这是我们对一个程序进行连接要得到的最终结果。这里我们不必另行指定文件名,直接按回车,链接程序将在当前目录下,生成1.exe文件。
确定了可执行文件的名称后,连接程序提示输入映像文件的名称和库文件的连接,我们直接按回车键忽略即可。
最后对目标文件的连接结束,连接程序输出的最后一行告诉我们源程序是否有错误。如果尽出现一个警告错误:“没有栈段”,我们可以不理会。
注意:
将上述源程序可以一起汇编并连接,汇编命令如下:
H:> ML /c /Fl /Sg 1.asm
上述命令将产生目标文件1.obj和列表文件1.lst。其中选项/c要求只汇编不连接,选项/Fl要求产生列表文件,选项/Sg要求在列表文件中列出由汇编程序根据伪指令所产生的机器指令。
(4)可执行文件的执行与调试
将生成的可执行文件执行:C:\masm5>1.exe,看不到任何结果,因为我们的程序没有向显示器输出信息。
为了观察程序的运行过程,使用Debug的相关命令来单步执行程序,查看每一条指令的运行结果。
进入C:\masm5后,我们输入“Debug 1.exe”,按Enter键,Debug将程序从1.exe中加载到内存,进行相关的初始化后设置CS:IP指向程序的入口。
使用R命令查看各个寄存器的设置情况,使用U命令来查看我们写入的指令,使用T命令单步执行程序中的每一条指令,到达int 21,使用P命令执行,执行后,显示“Program terminated normally”,返回到Debug,表示程序正常结束。
注意:在DOS中运行程序时,是Command将程序加载到内存,所以程序运行结束后返回到command中,而在这里是debug将程序加载到内存,所以程序运行结束后返回到debug中。
3. 作业
(1)将下面的程序保存为t1.asm文件,生成可执行文件t1.exe。
assume cs:codesg
codesg segment
mov ax,2000h
mov ss,ax
mov sp,0
add sp,4
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的内容。
实验五 [bx]和loop指令的联合使用
1.教学目的
 熟练掌握汇编源程序结构的基本框架
 掌握[bx]访问内存的方法和loop循环指令
 熟练使用DEBUG调试跟踪循环指令的执行
2. 讲授新课
(1)编程,向内存0:200~0:23f依次传递数据0~63(3fh)。
这是个比较另类的做法,传统做法请参考本实验(2)
assume cs:code
code segment
mov bx,20h
mov ss,bx
mov sp,40h
mov bx,3f3eh
mov cx,32
s: push bx
sub bx,202h
loop s
mov ax,4c00h
int 21h
code ends
end
C:\DOCUME~1\ADMINI~1>debug sy5-1.exe
-d 0:200 23f
0000:0200 00 00 00 00 00 00 00 00
-00 00 00 00 00 00 00 00 …………….
0000:0210 00 00 00 00 00 00 00 00
-00 00 00 00 00 00 00 00 …………….
0000:0220 00 00 00 00 00 00 00 00
-00 00 00 00 00 00 00 00 …………….
0000:0230 00 00 00 00 00 00 00 00
-00 00 00 00 00 00 00 00 …………….
-u
0C79:0000 BB2000 MOV BX,0020
0C79:0003 8ED3 MOV SS,BX
0C79:0005 BC4000 MOV SP,0040
0C79:0008 BB3E3F MOV BX,3F3E
0C79:000B B92000 MOV CX,0020
0C79:000E 53 PUSH BX
0C79:000F 81EB0202 SUB BX,0202
0C79:0013 E2F9 LOOP 000E
0C79:0015 B8004C MOV AX,4C00
0C79:0018 CD21 INT 21
0C79:001A 8600 XCHG AL,[BX+SI]
0C79:001C FF508D CALL [BX+SI-73]
0C79:001F 46 INC SI
-g 0015
AX=0000 BX=FEFE CX=0000 DX=0000
SP=0000 BP=0000 SI=0000 DI=0000
DS=0C69 ES=0C69 SS=0020 CS=0C79
IP=0015 NV UP EI NG NZ AC PO CY
0C79:0015 B8004C MOV AX,4C00
-d 0:200 23f
0000:0200 00 01 02 03 04 05 06 07
-08 09 0A 0B 0C 0D 0E 0F …………….
0000:0210 10 11 12 13 14 15 16 17
-18 19 1A 1B 1C 1D 1E 1F …………….
0000:0220 20 21 22 23 24 25 26 27
-28 29 2A 2B 2C 2D 2E 2F !”#$%&'()*+,-./
0000:0230 30 31 32 33 34 35 36 37
-38 39 3A 3B 3C 3D 3E 3F 0123456789:;<=>?
-t
AX=4C00 BX=FEFE CX=0000 DX=0000
SP=0000 BP=0000 SI=0000 DI=0000
DS=0C69 ES=0C69 SS=0020 CS=0C79
IP=0018 NV UP EI NG NZ AC PO CY
0C79:0018 CD21 INT 21
-p
Program terminated normally
-q
(2)编程,向内存0:200~0:23f依次传递数据0~63(3fh),程序中只能使用9条指令,9条指中包括“mov ax,4c00h”和“int 21h”。
assume cs:code
code segment
mov ax,20h
mov ds,ax
mov bx,0
mov cx,40h ;或mov cx,64
s: mov [bx],bl
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end

C:\DOCUME~1\ADMINI~1>debug sy5-2.exe
-d 0:200 23f
0000:0200 00 00 00 00 00 00 00 00
-00 00 00 00 00 00 00 00 …………….
0000:0210 00 00 00 00 00 00 00 00
-00 00 00 00 00 00 00 00 …………….
0000:0220 00 00 00 00 00 00 00 00
-00 00 00 00 00 00 00 00 …………….
0000:0230 00 00 00 00 00 00 00 00
-00 00 00 00 00 00 00 00 …………….
-u
0C79:0000 B82000 MOV AX,0020
0C79:0003 8ED8 MOV DS,AX
0C79:0005 BB0000 MOV BX,0000
0C79:0008 B94000 MOV CX,0040
0C79:000B 881F MOV [BX],BL
0C79:000D 43 INC BX
0C79:000E E2FB LOOP 000B
0C79:0010 B8004C MOV AX,4C00
0C79:0013 CD21 INT 21
0C79:0015 CC INT 3
0C79:0016 FFFF ??? DI
0C79:0018 50 PUSH AX
0C79:0019 8D8600FF LEA AX,[BP+FF00]
0C79:001D 50 PUSH AX
0C79:001E 8D4680 LEA AX,[BP-80]
-g 0010
AX=0020 BX=0040 CX=0000 DX=0000
SP=0000 BP=0000 SI=0000 DI=0000
DS=0020 ES=0C69 SS=0C79 CS=0C79
IP=0010 NV UP EI PL NZ AC PO NC
0C79:0010 B8004C MOV AX,4C00
-d 0:200 23f
0000:0200 00 01 02 03 04 05 06 07
-08 09 0A 0B 0C 0D 0E 0F …………….
0000:0210 10 11 12 13 14 15 16 17
-18 19 1A 1B 1C 1D 1E 1F …………….
0000:0220 20 21 22 23 24 25 26 27
-28 29 2A 2B 2C 2D 2E 2F !”#$%&'()*+,-./
0000:0230 30 31 32 33 34 35 36 37
-38 39 3A 3B 3C 3D 3E 3F 0123456789:;<=>?
-t
AX=4C00 BX=0040 CX=0000 DX=0000
SP=0000 BP=0000 SI=0000 DI=0000
DS=0020 ES=0C69 SS=0C79 CS=0C79
IP=0013 NV UP EI PL NZ AC PO NC
0C79:0013 CD21 INT 21
-p
Program terminated normally
-q
3. 作业
(1)下面程序的功能是将“mov ax,4c00h”之前的指令复制到内存0:200处,补全程序。上机调试,跟踪运行结果。
assume cs:code
code segment
mov ax,
mov ds,ax
mov ax,0020h
mov es,ax
mov bx,0
mov cx,
s: mov al,[bx]
mov es:[bx],al
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end
(2)复制是什么?从哪里到哪里?
(3)复制的是什么?有多少个自节?你是如何知道要复制的字节的数量?
提示:
因为题目的要求是把代码段内的指令当作数据,复制到目的地址。所以要确定要复制的数据的位置和长度。
因为要复制的数据是代码段的一部分指令,那么要复制的数据的段地址就和代码段的段地址相同。
因为要复制的数据是代码段的一部分指令,所以要复制的数据的长度,就是要复制的指令的长度,可以先假设要复制8位[1h~0ffh]数据的字节数(如:10h),把程序补全,以便通过编译。将程序补充完整并将其编译、连接、运行,接着进行DEBUG,在DEBUG时我们可以算出该程序的长度。然后得到正确的值后重新将程序补充完整。
实验六 编写、调试具有多个段的程序
1.教学目的
 进一步熟练掌握汇编程序的结构
 掌握编写具有多个段的程序
2. 讲授新课
(1)将下面的程序编译连接,用Debug加载、跟踪,然后回答问题。
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
mov ax,data
mov ds,ax
push ds:[0]
push ds:[2]
pop ds:[2]
pop ds:[0]
mov ax,4c00h
int 21h
code ends
end start
① CPU执行程序,程序返回前,data段中的数据为多少? 解:不变
② CPU执行程序,程序返回前,cs=155ch、ss=155bh、ds=155ah。 (此题结果因环境而异,但相对差值不变)
③ 设程序加载后,code段的段地址为X,则data段的段地址为X-2,stack段的段地址为X-1。
(2)将下面的程序编译连接,用Debug加载、跟踪,然后回答问题。
assume cs:code,ds:data,ss:stack
data segment
dw 0123H,0456H
data ends
stack segment
dw 0,0
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,16
mov ax,data
mov ds,ax
push ds:[0]
push ds:[2]
pop ds:[2]
pop ds:[0]
mov ax,4c00h
int 21h
code ends
end start
① CPU执行程序,程序返回前,data段中的数据为多少? 解:不变
② CPU执行程序,程序返回前,cs=155ch、ss=155bh、ds=155ah。 (此题结果因环境而异,但相对差值不变)
③ 设程序加载后,code段的段地址为X,则data段的段地址为X-2,stack段的段地址为X-1。
④对于如下定义的段:
name segment

name ends
如果段中的数据占N个字节,则程序加载后,这段实际占有的空间为(N/16+1)*16。(N/16为取整数部分)
(3)将下面的程序编译连接,用Debug加载、跟踪,然后回答问题。
assume cs:code,ds:data,ss:stack
code segment
start: mov ax,stack
mov ss,ax
mov sp,16
mov ax,data
mov ds,ax
push ds:[0]
push ds:[2]
pop ds:[2]
pop ds:[0]
mov ax,4c00h
int 21h
code ends
data segment
dw 0123H,0456H
data ends
stack segment
dw 0,0
stack ends
end start
① CPU执行程序,程序返回前,data段中的数据为多少? 解:不变
② CPU执行程序,程序返回前,cs=155ah、ss=155eh、ds=155dh。 (此题结果因环境而异,但相对差值不变)
③ 设程序加载后,code段的段地址为X,则data段的段地址为X+3,stack段的段地址为X+4。
(4) 如果将(1)、(2)、(3)题中的最后一条伪指令“end start”改为“end”(也就是说不指明程序的入口),则那个程序仍然可以正确执行?请说明原因。
解:(1)、(2)不能正确执行(入口默认为data段的第一条指令),(3)能正确执行。如果不指明程序的入口,编译器自动默认整个代码的第一条指令为程序的入口。
3. 作业
(1)程序如下,编写code段中的代码,将a段和b段中的数据依次相加,将结果存到c段中。
assume cs:code
a segment
db 1,2,3,4,5,6,7,8
a ends
b segment
db 1,2,3,4,5,6,7,8
b ends
c segment
db 0,0,0,0,0,0,0,0
c ends
code segment
start:
?
code ends
end start
(2)程序如下,编写code段中的代码,用push指令将a段中的word数据,逆序存储到b段中。
assume cs code
a segment
dw 1,2,3,4,5,6,7,8
a ends
b segment
dw 0,0,0,0,0,0,0,0
b ends
code segment
start:
?
code ends
end start
提示:
对于(1),可以通过先把a段的数据加到c段,然后再把b段的数据加到c段来实现。
对于(2),把a段当作数据段,把b段当作栈段,然后通过入栈操作可以实现逆序。

实验七 寻址方式的灵活应用
1.教学目的
 熟练掌握使用多种不同的寻址方式
 掌握二重循环问题的处理
 掌握字符的大小写转换
 理解栈的应用
2. 讲授新课
(1)编程,将datasg段中每个单词的头一个字母改为大写字母。
assume cs:codesg,ds:datasg
datasg segment
db ‘1. file ’
db ‘2. edit ’
db ‘3. search ’
db ‘4. view ’
db ‘5. options ’
db ‘6. help ’
datasg ends
codesg segment
start:?
codesg ends
end start
需要进行6次循环,用一个变量R定位行,用常量3 定位列。处理的过程如下:
R=第一行的地址
mov cx,6
s: 改变R行,3列的字母为大写
R=下一行的地址
loop
用bx作变量,定位每行的起始地址,用3定位要修改的列,用[bx+idata]的方式来对目标单元进行寻址。
程序如下:
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,6
s:mov al,[bx+3]
and al,11011111b
mov [bx+3],al
add bx,16
loop s
(2)编程:将datasg段中每个单词改为大写字母。
assume cs:codesg,ds:datasg
datasg segment
db ‘ibm ’
db ‘dec ’
db ‘dos ’
db ‘vax ’
datasg ends
codesg segment
start:?
codesg ends
end start
可是如果循环中的程序比较复杂,这些寄存器也都被使用的话,那么该如何?
在这里讨论的问题是,程序中经常需要进行数据的暂存,我们怎样做将更为合理。
使用内存来暂存数据,这一点是确定了的,但是值得推敲的是,我们用怎样的结构来保存这些数据,而使得我们的程序更加清晰。
一般来说,在需要暂存数据的时候,我们都应该使用栈,回忆一下,栈空间在内存中,采用相关的指令,如:push、pop等,可对其进行特殊的操作。
程序如下:
assume cs:codesg,ds:datasg, ss:stacksg
datasg segment
db ‘ibm ’
db ‘dec ’
db ‘dos ’
db ‘vax ’
datasg ends
stacksg segment
dw 0,0,0,0,0,0,0,0
stacksg ends
codesg segment
start:mov ax,stacksg
mov ss,ax
mov sp,16
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,4
s0:push cx
mov si,0
mov cx,3
s: mov al,[bx+si]
and al,11011111b
mov [bx+si],al
inc si
loop s
add bx,16
pop cx
loop s0
mov ax,4c00h
int 21h
codesg ends
end start
3. 作业
(1)编程,将datasg段中每个单词的前4个字母改为大写字母。
assume cs:codesg,ss:stacksg,ds:datasg
stacksg segment
dw 0,0,0,0,0,0,0,0
stacksg ends
datasg segment
db ‘1.display ’
db ‘2.brows ’
db ‘3.replace ’
db ‘4.modify ’
datasg ends
codesg segment
start:?
codesg ends
end start
提示:
因为在datasg段中的4个字符串是连续存放的,可以看成是一个4行16列的二维数组,按照要求,需要修改每个单词的前四个字母,即二维数组的每一行的3-6列。
需要进行4*4次的二重循环,用变量R定义行,常量3定义每行要修改的起始列,变量C定位相对于起始列的要修改的列,外层循环按行来进行,内层按列来进行。首先用R定位第一行,循环修改R行的3+C列(0<=C<=3);然后再用R定位到下一行,再次循环修改R行的3+C列……,直至所有数据修改完毕。
(2)上级调试,用DEBUG跟踪其执行过程,并在过程中进一步理解所讲内容。

实验七 寻址方式的灵活应用
1.教学目的
 熟练掌握汇编程序的简化段定义格式
 掌握DOS的系统调用
 进一步掌握汇编程序的结构
2. 讲授新课
(1)编程,在屏幕上输出显示:“Hello,Everybody!”
完整段定义的源程序格式
;SampleB.ASM
stack segment stack
dw 512 dup(?)
stack ends
data segment
… ;在数据段定义数据
data ends
code segment ’code’
assume cs:code,ds:data,ss:stack
start: mov ax,data
mov ds,ax
… ;在代码段填入指令序列
mov ax,4c00h
int 21h
code ends
end start
程序如下:
data segment ;定义数据段
string db ’Hello, Everybody !’,0dh,0ah,’$’
data ends
code segment ’code’;定义代码段
assume cs:code,ds:data
start: mov ax,data ;建立DS段地址
mov ds,ax
mov dx,offset string
mov ah,9
int 21h
mov ax,4c00h
int 21h ;利用功能调用返回DOS
code ends ;代码段结束
end start
(2)简化段定义的源程序格式
;SampleA.ASM
.model small
.stack
.data
… ;在数据段定义数据
.code
.startup ;(注1)
… ;在代码段填入指令序列
.exit 0 ;(注2)
… ;子程序代码
end ;(注3)
程序如下:
.model small ;定义程序的存储模式
.data ;定义数据段
string db ’Hello, Everybody !’,0dh,0ah,’$’
.code ;定义代码段
.startup ;程序起始点,建立DS
mov dx,offset string ;指定字符串
mov ah,9
int 21h ;利用功能调用显示信息
.exit 0 ;程序结束点,返回DOS
end ;汇编结束
(3)DOS功能调用
21H号中断是DOS提供给用户的用于调用系统功能的中断,它有近百个功能供用户选择使用,主要包括设备管理、目录管理和文件管理三个方面的功能。
 在当前显示器光标位置显示一个问号
mov ah,02h ;设置功能号:ah←02h
mov dl,’?’ ;提供入口参数:dl←’?’
int 21h ;DOS功能调用:显示
 在当前显示器光标位置显示一串字符串
string db ‘Hello,Everybody !’,0dh,0ah,’$’
;在数据段定义要显示的字符串

mov ah,09h ;设置功能号:ah←09h
mov dx,offset string
;提供入口参数dx←字符串的偏移地址
int 21h ;DOS功能调用:显示
 在当前显示器光标位置输入一个字符
从键盘输入小写字符串,输出其大写字符串。
.model small
.code
.startup
again: mov ah,0ah
int 21h
cmp al,‘a’
jb again
cmp al,‘z’
ja again
sub al,20h
mov dl,al
mov ah,09h
int 21h
.exit 0
end
3. 作业
(1)编程,计算1 ~ 100数字之和,并将结果存入字变量SUM中 。
提示:
Sum=1+2+…+99+100=100+99+…+2+1
设置初值:Sum=0
CX=100
Sum=Sum+CX
CX=CX-1
直到CX=0退出
这是循环条件
实现过程:
mov sum, 0
mov cx, 100
add sum, cx
loop
(2)请将下面程序补充完整,其大写字母改为小写字母,字符串以0结尾。
.model small
.data
String db ‘HeLLO,woRLD!’,0
.code
.startup
mov bx,offset string
again: mov al,[bx] ;取一个字符
or al,al ;是否为结尾符0
jz done ;是,退出循环

mov [bx],al ;仍保存在原位置
next: inc bx
jmp again ;继续循环
done: .exit 0
end
提示:
大小写字母的ASCII码不同之处是:
大写字母 + 20H = 小写字母
循环体判断每个字符,如果是大写字母则转换为小写,否则不予处理。这是一个循环次数不定的循环程序结构,宜用转移指令决定是否循环结束,并应该先判断后循环。

您可能还喜欢...

想说点什么吗?

您将是第一位评论人!

提醒
avatar