实验2 汇编源程序编写与汇编、调试
实验2 汇编源程序编写与汇编、调试
一、实验目的
- 理解并掌握汇编源程序组成与结构
- 掌握汇编语言源程序编写→汇编→链接→调试的工具和方法
- 理解汇编源程序中地址表示、段寄存器的用法
- 理解和掌握寄存器间接寻址方式[bx]
- 通过汇编指令loop的使用理解编程语言中循环的本质
二、实验准备
- 学习/复习第5章使用[bx]和loop实现循环的编程应用示例(教材5.5节,5.8节)
- 复习第3章「栈」的知识
- 结合第4章课件,复习完整汇编源程序编写→汇编→连接→运行→调试的方法
- 复习8086汇编中内存单元地址的表示,以及段寄存器DS, SS, ES, CS的用途
三、实验结论
1. 实验任务1
使用任意一款文本编辑器,编写8086汇编源程序ex1.asm。
这里我直接采用记事本:

使用masm命令编译,link命令链接。

执行,发现屏幕右上方出现图案。

使用debug加载可执行文件ex1.exe后,使用d命令查看程序段前缀PSP所占的256个字节。程序载入后psp的段地址sa被存入ds,在ds被改变前查看ds:0即可查看psp。可以看到头两个字节为CD 20,操作正确。

结合可执行文件中寄存器CX的值,使用u命令对ex1.exe进行精确反汇编

使用g命令执行到程序退出执行之前(即源码文件中line16之前),观察结果。
这里我使用g 2d命令执行到mov ah,4c之前。注意这里程序执行完毕本应该出现图案但未出现,原因我将结合任务2的尝试结果在后面尝试解释

2. 实验任务2
使用任意一款文本编辑器,编写8086汇编源程序ex2.asm。使用masm、link对ex2.asm进行汇编、链接,得到可执行文件ex2.exe,运行并观察结果。
可以看到屏幕右上方出现图案。

使用debug工具对程序进行调试。结合可执行文件中寄存器CX的值,使用u命令对ex2.exe进行精确反汇编

灵活使用t命令、p命令、g命令,对ex2.exe进行调试。
使用g命令运行到e,完成各寄存器的设置,再使用t命令单步调试到loop命令前

先尝试使用t命令继续调试,可以看到程序跳转至S位置,接下来情况同上次循环,继续运行至loop命令

这次使用p命令,可以看到直接完成循环

尝试使用t命令进入INT 21,可以发现进入了中断子程序。而p命令可以直接完成中断子程序,结束调试。

重新调试,使用g命令运行至程序退出前,查看ds:0~8,此时发现该段空间不符合预期,同时图案也未出现,原因我将结合下面的其他尝试结果尝试解释

把ex2.asm中line9 mov cx, 4 改成mov cx, 8 ,保存后重新汇编、链接、运行并观察结果。
; ex2.asm
assume cs:code
code segment
mov ax, 0b810h
mov ds, ax
mov bx, 0
mov ax, 101H
mov cx, 8
s: mov [bx], ax
add bx, 2
add ax, 101H
loop s
mov ah, 4ch
int 21h
code ends
end
可以看到随着循环次数增加,图案也增加。

对该程序进行调试,直接使用g命令运行至程序退出前,避免屏幕滚动,查看ds:0~10,可以看到该段空间更改成功且有图案显示。

故意敲回车让屏幕滚动,此时图案消失

再次查看内存可以发现内容被改变。这里我们没有对内存进行任何操作,仅仅敲回车让屏幕滚动,而内存空间却发生了改变。

结合前面的实验情况跟,我做出如下推测:由于该段空间为显存空间,与显示的内容直接相关,且屏幕滚动后会覆盖之前的内容,上部分运行时由于已经到了屏幕底部,每进行一步操作都会刷新屏幕,因此更改都被屏幕滚动覆盖掉了,而第二次我特意使用较少的命令避免屏幕滚动,可以看到更改确实是生效的。也就是前面认为的“更改无效”实际上是生效的,只是生效后屏幕滚动,再次被修改。
当然,既然发生了修改,就应该会有短暂的图案出现,但是并没有,这里推测t命令调试时,是将所有指令完成,屏幕显示内容准备好后才进行输出,或者说之前的更改都在屏幕缓存中发生,即t命令->(改显存->生成调试信息->屏幕滚动)->显示,括号内都是在缓存中发生,这样就能解释为什么完全没有图案出现。
3. 实验任务3
综合使用loop,[bx],编写完整汇编程序,实现向内存b800:07b8开始的连续16个字单元重复填充字数据
0237H。
编写汇编代码如下:
; ex3.asm
assume cs:code
code segment
mov ax, 0b800h
mov ds, ax
mov bx, 07b8h
mov ax, 0237H
mov cx, 16
s: mov [bx], ax
add bx, 2
loop s
mov ah, 4ch
int 21h
code ends
end
结果如下:

把填充的字数据,从0237H 改成0239H,发现字符改变
; ex3.asm
assume cs:code
code segment
mov ax, 0b800h
mov ds, ax
mov bx, 07b8h
mov ax, 0239H
mov cx, 16
s: mov [bx], ax
add bx, 2
loop s
mov ah, 4ch
int 21h
code ends
end

把填充的字数据,从0237H 改成0437H,发现颜色改变
; ex3.asm
assume cs:code
code segment
mov ax, 0b800h
mov ds, ax
mov bx, 07b8h
mov ax, 0437H
mov cx, 16
s: mov [bx], ax
add bx, 2
loop s
mov ah, 4ch
int 21h
code ends
end

即高位字节决定颜色,低位字节决定图案,内存空间决定位置。同时可以发现显示字符是低位字节的低4位,可以推测低位字节的高4位决定图案类型,低4位决定该类型下对应的内容。
4. 实验任务4
编写完整汇编源程序,实现向内存0:200~0:23F依次传送数据0~63(3FH)。
需要注意,本题要求向内存0:200~0:23F依次传送数据0~63(3FH),也就是一个字存放一个数据,而不是字节,因此应该使用al或ah传递数据,并且bx每次加1,否则最终只能传到0~1F。
汇编代码如下:
; ex4.asm
assume cs:code
code segment
mov ax, 0
mov ds, ax
mov bx, 200h
mov al, 0 ;传送字节,采用al
mov cx, 40h
s: mov [bx], al
add bx, 1
add al, 1
loop s
mov ah, 4ch
int 21h
code ends
end
运行前内存情况

运行后内存情况,填充成功。

选做*:利用栈的特性,综合使用loop,push实现(限定仅使用8086中已学过指令实现),编写源程序
注意:栈只能对字进行操作,push al是将字节入栈,报错Illegal size for operand(使用操作数大小(字节数)出错)
尝试通过改sp实现字节入栈,但是会发生莫名其妙的错误,推测与t命令也需要使用栈有关,随意变更sp会产生问题
注意80806字的存放方式,ax需要设为3f3e,减的时候需要各减2才能保证正确循环
代码如下:
; ex4-1.asm
assume cs:code
code segment
mov ax, 0
mov ss, ax
mov sp, 240h ;栈底,第一次入栈位置为0:23e
mov ax, 3f3eh ;8086采用小端模式,低位3e存入低地址,高位3f存入高地址
mov cx, 20h
s:push ax
sub ax, 0202h ;一次存两个数,各减2才能正确循环
loop s
mov ah, 4ch
int 21h
code ends
end
运行前内存状态如下:

g命令调试,可以看到成功完成传送

5. 实验任务5
首先尝试填空,使用debug查看指令长度。

发现es:命令被单独存放,因此需要调整cx,同时注意每次只传送一个字节,即传送cs:0~cs:16。cx应填写17。
最终代码如下:
; ex5.asm
assume cs:code
code segment
mov ax,cs
mov ds, ax
mov ax, 0020h
mov es, ax
mov bx,0
mov cx,17h
s:mov al, [bx]
mov es: [bx],al
inc bx
loop s
mov ax, 4c00h
int 21h
code ends
end
运行前内存状态如下:

运行后使用u命令对0:200的17个字节进行反汇编,可以看到传送成功。

四、实验总结
- t命令调试时,是将所有指令完成,屏幕显示内容准备好后才进行输出,或者说之前的更改都在屏幕缓存中发生,即t命令->(改显存->生成调试信息->屏幕滚动)->显示,括号内都是在缓存中发生。
- 显存中的一个字高位字节决定颜色,低位字节决定图案,内存空间决定位置。同时可以发现显示字符是低位字节的低4位,可以推测低位字节的高4位决定图案类型,低4位决定该类型下对应的内容。
- 栈只能对字进行操作,如果push al,将字节入栈,会报错Illegal size for operand(使用操作数大小(字节数)出错)
- 如果是对字节进行操作,注意使用要使用al/ah,汇编程序是根据操作的寄存器长度决定操作数长度的,当然,如果两个操作数都不是寄存器,默认为字数据
- 尽量不要在程序中反复更改sp的值,因为调试及很多其他操作也会用到栈,很容易发生奇怪的错误
- 不同命令的字节长度是不同的,同时ex:[bx]在masm编译后会被拆分为两段:
;mov es:[bx],al
es:
mov [bx],al
- 在编辑器中写常数时,最后要加上h,并且如果该常数开头为字母,需要在开头加上0,否则无法正确识别。
- 使用debug命令时,需要跟上完整后缀,即.exe
- t命令与p命令的区别在于遇到遇loop循环或int这种子程序是会当做整体执行,不进入内部单步执行。



浙公网安备 33010602011771号