1. 预备知识
- 目前为止还没有涉及到判断语句的使用,可通过大小写字符的二进制形式来进行大小写转换:
A 41 0100 0001 B 42 0100 0010 C 43 0100 0011 ...
a 61 0110 0001 b 62 0110 0010 c 63 0110 0011 ...
可观察到,对应大小写字符仅有第 5 位不同,所以可通过汇编指令按位与指令 and 和按位或指令 or 完成字符的大小写转换。如将字符转换为大写字符:and 1101 1111,将字符转换为小写字符:or 0010 0000。
- SI 和 DI 是 8086CPU 中和 BX 功能相近的寄存器,但 SI 和 DI 不能分为两个 8 位寄存器。
- 根据不同场景使用不同寻址方式:
- [idata] 用一个常量表示地址,可用于直接定位一个内存单元
- [bx] 用一个变量表示地址,可用于间接定位一个内存单元
- [bx+idata] 用一个常量和变量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元
- [bx+si] 用两个变量表示地址
- [bx+si+idata] 用两个变量和一个常量表示地址
- 前面提到,寄存器 CX 用以配置 loop 指令实现循环,如果涉及到多重循环,则内层循环的 CX 值会影响外层循环的值,解决办法是使用栈来暂存数据。
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
在定义数据段时,字符串后半部分使用空格填充至 16 字节,其在内存中的存放形式为:
可以看到,每个单词的第一个字母的相对位置都相同,即第 3 列。所以,可以使用一重循环完成,循环每次索引上二维数组的行,然后每次定位到行的第 3 列即可索引到相应字母,最后使用逻辑与运算将字符转换为大写字母。
mov ax,datasg
mov ds,ax ;使用段寄存器DS指向数据段datasg
mov bx,0
mov cx,6 ;循环次数
s: mov al,[bx+3] ;将相对于BX偏移3个位置的字符送入寄存器AL中
and al,11011111b ;通过逻辑与运算将字母转换为大写字母
mov [bx+3],al ;将转换后的字符重写回对应的内存单元
add bx,16 ;BX每次偏移16个位置
loop s
mov ax,4c00h
int 21h
使用指令 g 跳到循环执行前,此时寄存器 DS 为字符串存放内存的段地址:
以 076A:0000~076A:000F 为例,里面存放了第一个字符串的内容,21、2E、20、66、69、6C、65 分别为字符 1
、.
、
、f
、i
、l
、e
的 ASCII 码值,后面连续的 20 为填充的空格。待改变部分的偏移地址为 3,即 66、65、73、76、6F 和 68。程序执行结束后再查看这段内存单元:
(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
在定义数据段时,字符串后半部分使用空格填充至 16 字节,其在内存中的存放形式为:
和上一题只改变一个字母不同,本题要求同时改变三个字母,所以使用双重循环完成。同样地,外层循环用于索引每个字符串,内层循环遍历字符串中的每个字符。注意,上面提到多重循环需要注意循环控制量 CX 的存取,这里使用栈。
stacksg segment ;额外定义栈段用于存取寄存器CX的值
dw 0,0,0,0,0,0,0,0
stacksg ends
mov ax,stacksg
mov ss,ax
mov sp,16 ;定义空栈
mov ax,datasg
mov ds,ax
mov bx,0 ;使用段寄存器DS指向数据段datasg
mov cx,4 ;外层循环次数
s1: push cx ;使用栈保存CX的值
mov si,0 ;内层循环的偏移
mov cx,3 ;内层循环次数
s2: mov al,[bx+si] ;内层循环完成将字母转换为大写字母
and al,11011111b
mov [bx+si],al
inc si ;内层每次偏移1个字节
loop s2
add bx,16 ;外层循环每次偏移16个字节
pop cx ;恢复CX的值
loop s1
mov ax,4c00h
int 21h
(3)将 datasg 段中每个单词的前四个字母改为大写字母。
assume cs:codesg,ds:datasg
datasg segment
db '1. display '
db '2. brows '
db '3. replace '
db '4. modify '
datasg ends
codesg segment
start:
? ;待完成部分
codesg ends
end start
在定义数据段时,字符串后半部分使用空格填充至 16 字节,其在内存中的存放形式为:
本题和上一题思路类似,外层循环功能完全一致,内存循环也是实现功能将固定数量的字母转换为大写。在上一题中,每行待改变字母的起始偏移为 0,本题为 3;所以,上一题使用 [bx+si] 来定位起始字母,本题使用 [bx+si+3] 来定位起始字母。
stacksg segment ;额外定义栈段用于存取寄存器CX的值
dw 0,0,0,0,0,0,0,0
stacksg ends
mov ax,stacksg
mov ss,ax
mov sp,16 ;定义空栈
mov ax,datasg
mov ds,ax
mov bx,0 ;使用段寄存器DS指向数据段datasg
mov cx,4 ;外层循环次数
s1: push cx ;使用栈保存CX的值
mov si,0 ;内层循环的偏移
mov cx,4 ;内层循环次数
s2: mov al,[bx+si+3] ;内层循环完成将字母转换为大写字母
and al,11011111b
mov [bx+si+3],al
inc si ;内层每次偏移1个字节
loop s2
add ax,16 ;外层循环每次偏移16个字节
pop cx ;恢复CX的值
loop s1
mov ax,4c00h
int 21h
如果需要将前 5 个字母转换为大写,则 8 位寄存器 AL 的存储空间不足。