汇编语言(王爽第三版)实验6 实践课程中的程序
实验6 实践课程中的程序
(1)将课程中所有讲解过的程序上机测试,用debug跟踪其执行过程,并在过程中理解所讲内容。
问题7.6 将data段中每个单词的头一个字母改为大写字母。
assume cs:code
data segment
db '1. file '
db '2. edit '
db '3. search '
db '4. view '
db '5. options '
db '6. help '
data ends
code segment
程序分析:
1)数据段定义了6个字符串结构,长度一致都是16字节,并且字符都是连续的,并且头一个字母在字符串中的位置都是第4个字符,在上面的寻址方式中,我们发现[bx+idata]方式比较适合这种情况,idata代表每个字符串起始地址,bx代表基本偏移地址,bx的偏移量也是有规律的16。
2)在debug中,这6个字符串显示的正好是6行16列的字符,是一个我们在C语言中讲到的二维数组[6][16],其实它在内存中是线性单列排列的。
3)遇到此种情况我们使用[bx+idata]的方式进行内存的寻址比较科学。
完整的代码如下:
assume cs:code
data segment
db '1. file '
db '2. edit '
db '3. search '
db '4. view '
db '5. options '
db '6. help '
data ends
code segment
start:
mov ax, data
mov ds, ax
mov bx, 0 ;将ds:bx指向data第一个单元
mov cx, 6 ;需要修改6次
s: mov al, [bx+3] ;使用[bx+idata]寻址方式,并送入al(单字节)
and al, 11011111b ;转换为大写字母
mov [bx+3], al ;回写内存
add bx, 16 ;6个字符串长度一致,都是16字节,增量为16
loop s
mov ax, 4c00H
int 21H
code ends
end start
结果分析:
(1)在循环中,如遇到loop指令,首先我们要认识到,(cx)=(cx)-1;直到cx=0退出循环。
问题7.7将data段中每个单词的字母改为大写字母。
assume cs:code
data segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
data ends
程序分析:
(1)数据段定义了4个字符串,长度依然是16字节,并且是连续存储的,这次是要求将每个字符串的小写字母都给修改了,这样[bx+idata]就不合适了,我们采用[bx+si]方式寻址。bx依然代表基本偏移地址,si代表了每个字符串每个字母的偏移地址。
(2)一个循环肯定满足不了要求了,这里需要二重循环,外层循环递增bx的值;内存循环对每个字符串的字符进行变换操作。
(3)对于loop循环来说,它判断的是cx计数器,对于二重循环,一个cx值显然满足不了要求。书中代码中的cx设置就有问题了。
问题7.8 解决cx计数器重复设置的问题:
程序分析:我们可以想法将外层循环计数器保存起来,待某一个内层循环执行完毕后,在恢复。这样可以解决了cx计数器冲突的问题。
改进的代码:
assume cs:code
data segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
data ends
code segment
start:
mov ax, data
mov ds, ax
mov bx, 0 ;bx作为基本偏址变量
mov cx, 4 ;需要外循环4次,修改4个字符串
s0: mov dx, cx ;将外层循环计数器cx保存到dx中
mov si, 0 ;si作为每个字符串的偏址
mov cx, 3 ;内存循环s1,需要循环3次
s1: mov al, [bx+si] ;使用[bx+si]寻址方式,并送入al(单字节)
and al, 11011111b ;转换为大写字母
mov [bx+si], al ;回写内存
inc si
loop s1
add bx, 16 ;6个字符串长度一致,都是16字节,增量为16
mov cx, dx ;将外层循环计数器cx从dx中恢复
loop s0
mov ax, 4c00H
int 21H
code ends
end start
结果分析:
(1)我们通过将cx寄存器变量保存的方式,来解决多重循环cx变量冲突的问题。
(2)此例中我们解决方式是将cx保存到dx中,在8086CPU中,寄存器本来就比较紧张,段寄存器肯定不能用,ip是程序的指针、dx一般用于结果的输出,占用后又出现新的问题,sp是默认的ss栈段的指针;显然,将临时的数据保存在一个寄存器中的方式是不合适的。
(3)既然不能讲暂存的数据(它可能是寄存器变量的值,也可能是内存中单元的值)保存到一个寄存器中,那么使用内存作为暂存空间是可行的。我们将代码继续改进。
改进后的代码:
assume cs:code
data segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
dw 0 ;定义一个字,用于暂存cx值
data ends
code segment
start:
mov ax, data
mov ds, ax
mov bx, 0 ;bx作为基本偏址变量
mov cx, 4 ;需要外循环4次,修改4个字符串
s0: mov ds:[40H], cx ;将外层循环计数器cx保存到data内存中
mov si, 0 ;si作为每个字符串的偏址
mov cx, 3 ;内存循环s1,需要循环3次
s1: mov al, [bx+si] ;使用[bx+si]寻址方式,并送入al(单字节)
and al, 11011111b ;转换为大写字母
mov [bx+si], al ;回写内存
inc si
loop s1
add bx, 16 ;6个字符串长度一致,都是16字节,增量为16
mov cx, ds:[40H] ;将外层循环计数器cx从data内存中恢复
loop s0
mov ax, 4c00H
int 21H
code ends
end start
结果分析:
(1)在data段中定义了一个字单元空间,用于暂存cx的值。用直接寻址方式就行了。它在data段中偏移地址是40H,那么ds:[40H]==data:[40H]就代表了内存的那个字。
(2)这种情况,如果需要暂存的数据多的情况下,比较混乱,你还得记住内存单元的地址。
(3)一般来说,在需要暂存数据的时候,我们都应该使用栈。
继续改进代码,使用人工创建的栈空间来暂存cx值。
assume cs:code
data segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
data ends
stack segment
db 16 dup (0) ;人工定义一个栈段,空间16个字节,初始化为0
stack ends
code segment
start:
mov ax, data
mov ds, ax
mov bx, 0 ;bx作为基本偏址变量
mov cx, 4 ;需要外循环4次,修改4个字符串
mov ax, stack
mov ss, ax ;人工创建一个栈结构
mov sp, 16 ;将ss:sp指向栈顶
s0: push cx ;将外层循环计数器cx保存到stack栈中
mov si, 0 ;si作为每个字符串的偏址
mov cx, 3 ;内存循环s1,需要循环3次
s1: mov al, [bx+si] ;使用[bx+si]寻址方式,并送入al(单字节)
and al, 11011111b ;转换为大写字母
mov [bx+si], al ;回写内存
inc si
loop s1
add bx, 16 ;6个字符串长度一致,都是16字节,增量为16
pop cx ;将外层循环计数器cx从stack栈中恢复
loop s0
mov ax, 4c00H
int 21H
code ends
end start
结果分析:
(1)以后我们遇到越来越多的将暂存的数据存储在栈空间中去。下面讲到的子程序就大量使用栈。
(2)此例中我们使用的是人工创建的栈结构,还是就是系统自动创建的栈结构,那个我们不需要在内存中定义栈空间所需的单元。
(2)编程完成7.9问题中的程序
问题 7.9编程将data段中每个单词的前4个字母改为大写字母。
assume cs:code
data segment
db '1. display '
db '2. brows '
db '3. replace '
db '4. modify '
data ends
stack segment
db 16 dup (0) ;人工定义一个栈段,空间16个字节,初始化为0
stack ends
程序分析:
(1)4个字符串,每个字符串长度都相同,都是16个字节,并且是连续的,此时我们可以把这种结构看成是一个二维数组。每个单词字符长度都大于4.
(2)需要改变的是每个字符串的第4个字符开始的4个字符,那么在每一行,我们表示内存单元可以使用[si+idata]的方式寻址,idata代表每个行(每个字符串)开始地址3.si作为每行的偏移地址,偏移量从0~3;由于是个二维数组结构,bx代表每行的偏移量,偏移量从0~3,最终我们采用[bx+si+idata]寻址的方式来定位每行的字符串。
代码如下:
assume cs:code
data segment
db '1. display '
db '2. brows '
db '3. replace '
db '4. modify '
data ends
stack segment
db 16 dup (0) ;人工定义一个栈段,空间16个字节,初始化为0
stack ends
code segment
start:
mov ax, data
mov ds, ax
mov bx, 0 ;bx作为基本偏址变量
mov ax, stack
mov ss, ax ;人工创建一个栈结构
mov sp, 16 ;将ss:sp指向栈顶
mov cx, 4 ;需要外循环4次,修改4个字符串
s0: push cx ;将外层循环计数器cx保存到stack栈中
mov si, 0 ;si作为每个字符串的偏址
mov cx, 4 ;内存循环s1,需要循环4次
s1: mov al, [bx+si+3] ;使用[bx+si+3]寻址方式,并送入al(单字节)
and al, 11011111b ;转换为大写字母
mov [bx+si+3], al ;回写内存
inc si
loop s1
add bx, 16 ;4个字符串长度一致,都是16字节,增量为16
pop cx ;将外层循环计数器cx从stack栈中恢复
loop s0
mov ax, 4c00H
int 21H
code ends
end start
程序运行结果:
-d ds:0
0B65:0000 31 2E 20 44 49 53 50 6C-61 79 20 20 20 20 20 20 1. DISPlay
0B65:0010 32 2E 20 42 52 4F 57 73-20 20 20 20 20 20 20 20 2. BROWs
0B65:0020 33 2E 20 52 45 50 4C 61-63 65 20 20 20 20 20 20 3. REPLace
0B65:0030 34 2E 20 4D 4F 44 49 66-79 20 20 20 20 20 20 20 4. MODIfy