1. 理解和掌握将数据、代码、栈放入不同逻辑段的程序的编写和调试
2. 理解具有多个段的汇编源程序对应的目标程序执行时,内存分配方式
3. 掌握大小写字符的转换方法、数字字符和数值之间的转换方法
4. 理解并掌握各种寻址方式的灵活应用
5. 掌握汇编指令loop, and, or,div, mul的用法
使用任意一款文本编辑器,编写8086汇编源程序task1.asm。
源代码如下:
assume cs:code, ds:data data segment db ‘Nuist‘ db 5 dup(2) data ends code segment start: mov ax, data mov ds, ax mov ax, 0b800H mov es, ax mov cx, 5 mov si, 0 mov di, 0f00h s: mov al, [si] and al, 0dfh mov es:[di], al mov al, [5+si] mov es:[di+1], al inc si add di, 2 loop s mov ah, 4ch int 21h code ends end start
阅读源程序,从理论上分析源代码的功能,尤其是line15-25,循环实现的功能是什么,逐行理解每条指
令的功能。
使用masm、link对task1.asm进行汇编、链接,得到可执行文件task1.exe,运行并观察结果。
使用debug工具对程序进行调试,执行到程序返回前,即line27之前,观察结果。
修改line4里5个字节单元的值,重新汇编、链接、运行,观察结果。
1、具体的汇编、链接过程可以参考《汇编语言程序设计 实验2 汇编源程序编写与汇编、调试》。运行ex1.exe文件,得到结果:
2、使用debug工具对程序进行调试
首先使用r命令查看寄存器中的值,再使用g命令执行到程序返回前,即line27之前,结果如下所示:
3、修改line4里5个字节单元的值:
db 5 dup(2) --> 改成: db 2,3,4,5,6
重新汇编、链接、运行,结果如下所示:
从这里已经可以看出data段line4的字节数据的用途是 决定ASCII码字符的颜色属性。
其中
mov cx, 5 //循环5次 mov si, 0 //将0送到si寄存器中 mov di, 0f00h//设置初始地址 s: mov al, [si]//将[si]的值送入al and al, 0dfh//转换字母为大写字母 mov es:[di], al//将结果保存至显示缓冲区 mov al, [5+si]//将第si+5个字节送入al mov es:[di+1], al//保存数据 inc si//si+1 add di, 2//di+2 loop s
已知数据段data中定义字节数据如下所示:
data segments
db 23, 50, 66, 71, 35
data ends
编写程序,在屏幕上以十进制整数形式打印输出这5个两位数。 程序如下所示:
assume cs:code,ds:data
data segment
db 23,50,66,71,35
data ends
code segment
start:
mov ax,data
mov ds,ax
mov di,0
mov cx,5
s1: mov ah,0
mov al,ds:[di]
mov bl,10
div bl
mov ds:[10+di],al
mov ds:[11+di],ah
mov ah,2
mov dl,ds:[10+di]
add dl,30h
int 21h
mov ah,2
mov dl,ds:[11+di]
add dl,30h
int 21h
mov ah,2
mov dl," "
int 21h
inc di
loop s1
mov ax,4c00h
int 21h
code ends
end start
教材「实验5 编写、调试具有多个段的程序」(1)
代码为:
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=076C,SS=0769,DS=075A 。(与自己系统有关)
③设程序加载后,CODE段的段地址为X,则DATA段的段地址为 X-2 ,STACK段的段地址为 X-1
「实验5 编写、调试具有多个段的程序」(2)
代码如下所示:
assume cs:code code segment mov ax, 20h mov ds, ax mov bx, 0 mov cx, 40h mov al, 0 s:mov [bx], al inc al inc bx loop s mov ax, 4c00h int 21h code ends end
运行结果如下所示:
2、选做部分
利用栈的特性,综合使用loop,push实现(限定仅使用8086中已学过指令实现),编写源程序
灵活使用debug的t命令、g命令、p命令调试。在程序退出前,用d命令查看0:200~0:23F,确认是否将0~3F传送至此段内存区域。
代码如下所示:
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段中的数据为多少?
执行程序后,data段有16个字节空间,前两个字数据不变,其余为00补全了。
②CPU执行程序,程序返回前,CS=076C,SS=076B,DS=076A 。
③设程序加载后,CODE段的段地址为X,则DATA段的段地址为 X-2 ,STACK段的段地址为 X-1
对于如下定义的段:
name segment
......
name ends
如果段中数据位N个字节,程序加载后,该段实际占据空间为:(N/16的取整数+1)*16个字节
如果N小于16,那么实际占用16个字节(理解这个小问题);如果N大于16,那么实际占用(N/16的取整数+1)*16个字节。其实都是这个公式。
「实验5 编写、调试具有多个段的程序」(3)
代码为:
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段中的数据为多少?
执行程序后,data段有16个字节空间,前两个字数据不变,其余为00补全了。
②CPU执行程序,程序返回前,CS=076A,SS=076E,DS=076D 。
③设程序加载后,CODE段的段地址为X,则DATA段的段地址为 X+3 ,STACK段的段地址为 X+4
「实验5 编写、调试具有多个段的程序」(4)
如果将(1)、(2)、(3)题中的最后一条伪指令“end start”改为“end”(也就是说不指明程序的入口),则那个程序仍然可以正确执行?请说明原因。
答案:如果不指名程序的(code段的)入口,并且使用end替换end start,都能正常运行。但只有(3)题中程序可以正确的执行(因为只有它是在内存中可执行代码在最前面)。
讲解:因为如果不指名入口,程序会从加载进内存的第一个单元起开始执行,前二个题中,定义的是数据,但CPU还是将数据当做指令代码执行了。只不过程序执行时逻辑上是错误了。但真的能执行的。
如果指明了程序的入口,CPU会直接从入口处开始执行真正的机器码,直到遇到中断指令返回。此种方式能够确保程序逻辑上的正确。因此有必要为程序来指明入口。
「实验5 编写、调试具有多个段的程序」(5)
源码如下:
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
cz segment
db 0,0,0,0,0,0,0,0
cz ends
code segment
start:
mov ax,a
mov ds,ax ;ds指向a段
mov ax,b
mov es,ax ;es指向b段
mov bx,0
mov cx,8 ;计算8次,故计数器为8
s:
mov dl, [bx] ;将ds:[bx]内存单元按字节送入dl,此循环用到ax
add dl, es:[bx] ;将ds:[bx]与es:[bx]内存单元值相加
push ds ;保护ds值,因为下面用到ds了
mov ax, cz ;我的编译器不认C这个段的标号,故改成了CZ
mov ds, ax ;将ds指向cz段
mov [bx], dl ;将dl(a和b相对应内存单元内容之和)写入cz中
pop ds ;将ds恢复
inc bx ;bx递增
loop s
mov ax,4c00h
int 21h
code ends
end start
结果如下:
「实验5 编写、调试具有多个段的程序」(6)
编写code段中代码,用push指令将a段中前8个字型数据逆序存储到b段中。
源码如下:
assume cs:code
a segment
dw 1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0eh,0fh,0ffh
a ends
b segment
dw 0,0,0,0,0,0,0,0
b ends
code segment
start:
mov ax,b
mov ss,ax
mov sp,16
mov ax,a
mov ds,ax
mov cx,8
mov bx,0
s0:push ds:[bx]
add bx,2
loop s0
mov cx,8
mov ax,4c00h
int 21h
code ends
end start
结果如下:
在完成任务345的时候突然有一点点搞不明白为什么是这样?看cx,程序加载时,我们发现cx=0044,含义:此程序所有机器码占用的空间是44H=68字节,data和stack由于定义的都是小于16个字节,一律按照16个字节分配空间,其余补00;剩余的36个字节就是code段真正的可执行的机器码。由于code段不足48个字节(3*16),故程序加载时也补0了
原文:https://www.cnblogs.com/amikeco/p/14010989.html