《汇编语言(第四版)》 . 王爽著 . 清华大学出版社 . 2019
实验 6 实践课程中的程序
实验任务(1)
将课程中所有讲解过的程序上机调试,用 Debug 跟踪其执行过程,并在过程中进一步理解所讲内容。
程序 1
在汇编程序中,用 ‘…’ 的方式指名数据是以字符的形式给出的,编译器将把它们转化为相对应的 ASCII 码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| assume cs:code,ds:data
data segment db 'unIX' db 'foRK' data ends
code segment
start: mov al,'a' mov bl,'b' mov ax,4c00h int 21h
code ends
end start
|
用 Debug 跟踪程序运行
ASCII ((American Standard Code for Information Interchange): 美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是最通用的信息交换标准,并等同于国际标准 ISO/IEC 646。ASCII 第一次以规范标准的类型发表是在 1967 年,最后一次更新则是在 1986 年,到目前为止共定义了 128 个字符
ASCII 码使用指定的 7 位或 8 位二进制数组合来表示 128 或 256 种可能的字符。标准 ASCII 码也叫基础 ASCII 码,使用 7 位二进制数(剩下的 1 位二进制为 0)来表示所有的大写和小写字母,数字 0 到 9、标点符号,以及在美式英语中使用的特殊控制字符
常用的 ASCII 码
0x30 ~ 0x39 : 字符 1 ~ 9
0x41 ~ 0x5A : 大写字母 A ~ Z
0x61 ~ 0x7A : 小写字母 a ~ z
程序 2
在 codesg 中填写代码,将 datasg 中的第一个字符串转化为大写,第二个字符串转化为小写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| assume cs:codesg,ds:datasg
datasg segment db 'BaSiC' db 'iNfOrMaTiOn' datasg ends
codesg segment
start: ?
codesg ends
end start
|
通过查 ASCII 码标准表,我们可以很轻易地看出来,小写字母的 ASCII 码值比大写字母的 ASCII 码值大 20H,那么如果我们能够判断当前字母是大写还是小写,就可以按这个规律进行大小写之间的改变
可惜的是,到这个实验为止,我们还没有接触到能够对字母的大小写进行判断的指令,那么我们必须找到新的规律
从 ASCII 码的二进制形式着手,我们发现,大写字母与其小写字母的二进制表示的唯一区别是第 5 位不相同,例如,小写字母 a 为 0110 0001
,大写字母 A 为 0100 0001
,那么如果有一个字母,将它的第 5 位置 1 即为小写,置 0 即为大写,这样就避免了判断
注意,将某一位置 0 或置 1 可以使用 and
与 or
指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| assume cs:codesg,ds:datasg
datasg segment db 'BaSiC' db 'iNfOrMaTiOn' datasg ends
codesg segment
start: mov ax,datasg mov ds,ax mov bx,0 mov cx,5
s: mov al,[bx] and al,11011111B mov [bx],al inc bx loop s
mov bx,5 mov cx,11
s0: mov al,[bx] or al,00100000B mov [bx],al inc bx loop s0
mov ax,4c00h int 21h
codesg ends
end start
|
用 Debug 跟踪程序运行
程序 3
用 [bx+idata]
来指明内存单元,这种寻址方式被称为寄存器相对寻址,它为高级语言实现数组提供了便利机制
用 Debug 查看内存,结果如下
2000:1000
BE 00 06 00 00 00...
写出下面的程序执行后,ax、bx、cx 中的内容
1 2 3 4 5 6
| mov ax, 2000H mov ds,ax mov bx,1000H mov ax,[bx] mov cx,[bx+1] mov cx,[bx+2]
|
AX 中为 00BEH
,BX 中为 1000H
,CX 中为 0606H
注意,数据寄存器的大小为一个字,传送进去的数据也应为一个字大小,并且注意是小端序
用 Debug 跟踪程序运行
程序 4
使用 [bx+idata]
的方式进行数组的处理,在 codesg 中填写代码,将 datasg 中的第一个字符串转化为大写,第二个字符串转化为小写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| assume cs:codesg,ds:datasg
datasg segment db 'BaSiC' db 'MinIX' datasg ends
codesg segment
start: ?
codesg ends
end start
|
为了简化程序,将两个循环合并成一个循环,可以用 [0+bx]
和 [5+bx]
的方式在同一个循环中定位这两个字符串中的字符,0 和 5 给定两个字符串的起始偏移地址,bx 给定从起始偏移地址开始的相对地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| assume cs:codesg,ds:datasg
datasg segment db 'BaSiC' db 'MinIX' datasg ends
codesg segment
start: mov ax,datasg mov ds,ax mov bx,0 mov cx,5 s: mov al,[bx] and al,11011111b mov [bx],al mov al,[5+bx] or al,00100000b mov [5+bx],al inc bx loop s
mov ax,4c00h int 21h
codesg ends
end start
|
用 Debug 跟踪程序运行
程序 5
si 和 di 是 8086CPU 中和 bx 功能相近的寄存器,但 si 和 di 不能够分成两个 8 位寄存器来使用
用 si 和 di 实现将字符串 ‘welcome to masm!’ 复制到它后面的数据区中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| assume cs:codesg,ds:datasg
datasg segment db 'welcome to masm!' db '................' datasg ends
codesg segment
start: mov ax,datasg mov ds,ax mov si,0 mov di,16
mov cx,8 s: mov ax,[si] mov [di],ax add si,2 add di,2 loop s mov ax,4c00h int 21h
codesg ends
end start
|
变址寄存器:SI(source index)、DI(destination index)
用 ds:si
指向要复制的源字符串,用 ds:di
指向复制的目的空间,用循环来完成复制,一次循环复制 2 个字节
用 Debug 跟踪程序运行
程序 6
用更少的代码,实现问题 7.2 中的程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| assume cs:codesg,ds:datasg
datasg segment db 'welcome to masm!' db '................' datasg ends
codesg segment
start: mov ax,datasg mov ds,ax mov si,0 mov cx,8
s: mov ax,0[si] mov 16[si],ax add si,2 loop s
mov ax,4c00h int 21h
codesg ends
end start
|
si 与 di 是存在一定的关系的,可以用 si+16 表示 di,这样可以省去一个寄存器
程序 7
用 Debug 查看内存,结果如下
2000:1000
BE 00 06 00 00 00...
写出下面的程序执行后,ax、bx、cx 中的内容
1 2 3 4 5 6 7 8 9 10
| mov ax,2000H mov ds,ax mov bx,1000H mov si,0 mov ax,[bx+si] inc si mov cx,[bx+si] inc si mov di,si add cx,[bx+di]
|
AX 中为 00BEH
,BX 中为 1000H
,CX 中为 0606H
可参考程序 3
用 Debug 跟踪程序运行
程序 8
[bx+si+idata]
与 [bx+di+idata]
的含义相似,表示一个偏移地址为 (bx)+(si)+idata 或 (bx)+(di)+idata 的内存单元,这种寻址方式被称为相对基址变址寻址
用 Debug 查看内存,结果如下
2000:1000
BE 00 06 00 6A 22...
写出下面的程序执行后,ax、bx、cx 中的内容
1 2 3 4 5 6 7 8 9 10
| mov ax,2000H mov ds,ax mov bx,1000H mov si,0 mov ax,[bx+2+si] inc si mov cx,[bx+2+si] inc si mov di,si add bx,[bx+2+di]
|
AX 中为 0006H
,BX 中为 226AH
,CX 中为 6A00H
用 Debug 跟踪程序运行
总结比较前面用到的寻址方式
[idata]
用一个常量来表示地址,可用于直接定位一个内存单元,称为直接寻址
[bx]
用一个变量来表示内存地址,可用于间接定位一个内存单元,称为寄存器间接寻址
[bx+idata]
用一个变量和常量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元,称为寄存器相对寻址
[bx+si]
用两个变量表示地址,称为基址变址寻址
[bx+si+idata]
用两个变量和一个常量表示地址,称为相对基址变址寻址
程序 9
编程,将 datasg 段中每个单词的头一个字母改为大写字母
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 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: 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
mov ax,4c00h int 21h
codesg ends
end start
|
用 bx 定位每行的起始地址,用 3 定位要修改的列,每行的起始地址相差 16,用 [bx+idata]
的方式来对目标单元进行寻址
用 Debug 跟踪程序运行
程序 10
编程,将 datasg 段中每个单词改为大写字母。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| assume cs:codesg,ds:datasg
datasg segment db 'ibm ' db 'dec ' db 'dos ' db 'vax ' datasg ends
codesg segment
start: ?
codesg ends
end start
|
用 bx 定位每行的起始地址,用 si 定位要修改的列,每行的起始地址相差 16,用 [bx+si]
的方式来对目标单元进行寻址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| assume cs:codesg,ds:datasg
datasg segment db 'ibm ' db 'dec ' db 'dos ' db 'vax ' datasg ends
codesg segment
start: mov ax,datasg mov ds,ax mov bx,0
mov cx,4
s0: 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 loop s0
mov ax,4c00h int 21h
codesg ends
end start
|
在上面的程序中,我们进行了二重循环,却只使用了一个循环计数器,造成在进行内层循环的时候,覆盖了外层循环的循环计数值,所以程序是有问题的
我们应该在每次开始内层循环的时候,将外层循环的 cx 中的数值保存起来,在执行外层循环的 loop 指令前,再恢复外层循环的 cx 数值
比如,用寄存器 dx 来临时保存 cx 中的数值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| assume cs:codesg,ds:datasg
datasg segment db 'ibm ' db 'dec ' db 'dos ' db 'vax ' datasg ends
codesg segment
start: mov ax,datasg mov ds,ax mov bx,0
mov cx,4
s0: mov dx,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 mov cx,dx loop s0
mov ax,4c00h int 21h
codesg ends
end start
|
用别的寄存器暂存 cx 中的值只是权宜之计,CPU 中寄存器的数量有限,且大都有各自的任务要做,而程序中经常会出现暂存数据的需求
1 2 3 4 5 6 7
| datasg segment db 'ibm ' db 'dec ' db 'dos ' db 'vax ' dw 0 datasg ends
|
可以考虑将暂存的数据放到内存单元中,需要的时候,从内存单元恢复,但如果需要同时保存多个数据的时候,就得记下数据放到了哪个内存单元中,这样会使得程序混乱
一般来说,在需要暂存数据的时候,我们都应该使用栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| 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
|
用 Debug 跟踪程序运行
实验任务(2)
编程,将 datasg 段中每个单词的前 4 个字母改为大写字母。
用二重循环 + 相对基址变址寻址解决
用 bx 定位每行的起始地址,用 si 定位要修改的列,用 [bx+3+si]
的方式来对目标单元进行寻址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| assume cs:codesg,ds:datasg,ss:stacksg
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: mov ax,stacksg mov ss,ax mov sp,16
mov ax,datasg mov ds,ax
mov bx,0 mov cx,4
s1: push cx mov si,0 mov cx,4
s2: mov al,[bx+3+si] and al,11011111b mov [bx+3+si],al inc si loop s2
add bx,16 pop cx loop s1
mov ax,4c00h int 21h
codesg ends
end start
|
用 Debug 跟踪程序运行
汇编语言实验合集
汇编语言实验合集
实验 1 查看 CPU 和内存,用机器指令和汇编指令编程
实验 2 用机器指令和汇编指令编程
实验 3 编程、编译、连接、跟踪
实验 4 [bx] 和 loop 的使用
实验 5 编写、调试具有多个段的程序
实验 6 实践课程中的程序
实验 7 寻址方式在结构化数据访问中的应用
实验 8 分析一个奇怪的程序
实验 9 根据材料编程
实验 10 编写子程序
课程设计 1
实验 11 编写子程序
实验 12 编写 0 号中断的处理程序
实验 13 编写、应用中断例程
实验 14 访问 CMOS RAM
实验 15 安装新的 int9 中断例程
实验 16 编写包含多个功能子程序的中断例程
实验 17 编写包含多个功能子程序的中断例程
课程设计 2