第7章 更灵活的定位内存地址的方法
# 第7章 更灵活的定位内存地址的方法
# 7.1 and 和 or 指令
# 7.1.1 and 指令
and 指令:逻辑与指令,只有全为1,才为1
mov al,01100011B
and al, 00111011B
------------------------
结果 al = 00100011B
2
3
4
通过该指令可将操作对象的相应位设为0,其他位不变。
例如:
- 将 al 的第 6 位设为 0 的指令是:and al,10111111B
- 将 al 的第 7 位设为 0 的指令是:and al,01111111B
- 将 al 的第 0 位设为 0 的指令是:and al,11111110B
# 7.1.2 or 指令
or 指令:逻辑或指令,只用全为 0,才为 0
mov al,01100011B
or al,00111011B
---------------------
结果al=01111011B
2
3
4
通过该指令可将操作对象的相应位设为1,其他位不变。
例如:
- 将 al 的第 6 位设为 1 的指令是:or al,10111111B
- 将 al 的第 7 位设为 1 的指令是:or al,01111111B
- 将 al 的第 0 位设为 1 的指令是:or al,11111110B
# 7.2 以字符的形式给出数据
在汇编程序中,可用‘......’的方式指明数据是以字符的形式给出的,编辑器会将它们转化为相应的 ASCII 码。
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
2
3
4
5
6
7
8
9
10
11
12
db 'unIX'
相当于db 75H,6EH,49H,58H
,u
、n
、I
、X
的 ASCII 码分别为 75H、6EH、49H、58H;db 'foRK'
相当于db 66H,6FH,52H,4BH
,f
、o
、R
、K
的 ASCII 码分别为 66H、6FH、52H、4BH;mov al,'a'
相当于mov al,61H
,a
的 ASCII 码为 61H;mov bl,'b'
相当于mov al,62H
,b
的 ASCII 码分别为 62H;
# 7.3 大小写转换的问题
考虑如下问题:在 codesg 中填写代码,将 datasg 中的第一个字符串转化为大写,第二个字符串转化为小写。
assume cs:codesg,ds:datasg
datasg sement
db 'BaSiC'
db 'iNfOrMaTiOn'
datasg ends
codesg segment
start:
codesg ends
end start
2
3
4
5
6
7
8
9
思路:要改变一个字母的大小,实际就是改变它所对应的 ASCII 码。找到 ASCII 码规律,发现小写字母比大写字母的 ASCII 码值大 20H。所以,将 "a" 的 ASCII 码值减去 20H,就可以得到 "A",反之亦然。要注意的是,对于 'BaSiC' 字符串,只需对小写进行减 20H 处理,其他的大写字母不进行处理。对于 'iNfOrMaTiOn' 字符串则反之。
但是,程序还有一个前提,必须能够判断一个字母是大写还是小写。但是根据目前所学,还没有接触到可以实现这个功能的指令,所以继续观察上图,找 ASCII 码值的规律,可发现,大写字母和小写字母的其他位都一样,只是大写字母的 ASCII 码值第 5 位为 0,小写字母为 1。所以,我们只需将字母的 ASCII 码值第 5 位 置为0,则变成大写字母,置为1,则变成小写字母。
这个操作则刚好可以使用我们前面所学的 or and 指令。
assume cs:codesg,ds:datasg
datasg segment
db 'BaSiC'
db 'iNfOrMaTiOn'
datasg ends
codesg segment
start:mov ax,datasg
mov ds,ax ;设置 ds 指向 datasg 段
mov bx,0 ;设置(bx)=0,ds:bx指向 'BaSiC' 的第一个字母
mov cx,5 ;设置循环次数5,因为 'BaSiC' 有5个字母
s:mov al,[bx] ;将 ASCII 码从 ds:bx 所指向的单元中取出
and al,11011111B ;将 al 中的 ASCII 码的第5位置为0,变为大写字母
mov [bx],al ;将转变后的 ASCII 码协会原单元
inc bx ;(bx)加1,ds:bx 指向下一个字母
loop s
mov bx,5 ;设置(bx)=5,ds:bx 指向 iNfOrMaTiOn' 的第一个字母
mov cx,11 ;置循环次数11,因为 'iNfOrMaTiOn' 有11个字母
s0:mov al,[bx]
or al,00100000B ;将 al 中的 ASCII 码的第5位置为1,变为小写字母
mov [bx],al
inc bx
loop s0
mov ax,4c00h
int 21h
codesg ends
end start
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
# 7.4 [bx+idata]
[bx+idata]
表示一个内存单元,它的偏移地址为(bx) + idata
。
mov ax,[bx+200]
的含义:将一个内存单元的内容送入 ax,这个内存单元的长度为2个字节(字单元),存放一个字,偏移地址为 bx 中的数值加上200,段地址在 ds 中。
数字描述为:(ax) = ((bx)*16 + (bx) + 200)
也可写成如下格式(常用):
mov ax,[200+bx]
mov ax,200[bx]
mov ax,[bx].200
2
3
用 [bx+idata] 的方式重新实现大小写转换的问题,因为 datasg 段中的两个字符串一个起始地址为0,另一个起始地址为5。将这两个字符串视为两个数组,一个从0地址开始,一个从5开始存放。那么就可以用 [0+bx] 和 [5+bx] 或者 0[bx] 和 5[bx] 来表示。
改进程序如下:
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
2
3
4
5
6
7
8
9
10
11
12
或者如下:
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,5
s: mov al,0[bx] ;定位第一个字符串中的字符
and al,11011111b
mov 0[bx],al
mov al,5[bx] ;定位第二个字符串中的字符
or al,00100000b
mov [5+bx],al
inc bx
loop s
2
3
4
5
6
7
8
9
10
11
12
如果使用高级语言,比如C语言,大致如下:
char a[5] = "BaSiC"
char b[5] = "MinIX";
main()
{
int i;
i = 0;
do {
a[i] = a[i] & 0xDF;
b[i] = b[i] | 0x20;
i++;
} while(i < 5);
}
2
3
4
5
6
7
8
9
10
11
12
# 7.5 SI 和 DI
si 和 di 是与 bx 功能相近的寄存器,si 和di 不能够分成两个8为寄存器来使用,下面是3组指令实现了相同的功能
问题 7.2:用 si 和 di 实现将字符串 'welcome to masm!' 复制到它后面的数据区中
assume cs:codesg,ds:datasg datasg segment db 'welcome to masm!' db '................' datasg ends
1
2
3
4
5用 ds:si 指向要复制的源始字符串,ds:di 指向复制的目的空间
codesg segment start: mov ax,datasg mov si,0 mov di,16 s: mov ax,[si] mov [di],ax add si,2 add di,2 loop s mov ax,4c00h int 21h codesg ends end start
1
2
3
4
5
6
7
8
9
10
11
12
13在程序中,用 16 位寄存器进行内存单元之间的数据传送,一次复制2个字节,一共循环8次
用更少的代码实现:利用 [bx(si或di)+idata] 的方式
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
1
2
3
4
5
6
7
8
9
10
11
12
13
# 7.6 [bx+si] 和 [bx+di]
[bx+si] 表示一个内存单元,偏移地址为 (bx)+(si)即 bx 中的数值加上 si 中的数值。
mov ax,[bx+si] 的含义如下:将一个内存单元的内容送入 ax,这个内存单元的长度为2个字节(字单元),存放一个字,偏移地址为 bx 中的数值加上 si 中的数值,段地址在 ds 中。
数学化描述为:(ax) = ((ds)*16+(bx)+(si))
也可写成如下格式(常用):mov ax,[bx][si]
# 7.7 [bx+si+idata] 和 [bx+di+idata]
[bx+si+idata] 表示一个内存单元,偏移地址为 (bx)+(si)+idata 即 bx 中的数值加上 si 中的数值再加上idata。
mov ax,[bx+si+idata] 的含义如下:将一个内存单元的内容送入 ax,这个内存单元的长度为2个字节(字单元),存放一个字,偏移地址为 bx 中的数值加上 si 中的数值再加上idata,段地址在 ds 中。
数学化描述为:(ax) = ((ds)*16+(bx)+(si)+idata)
也可写成如下格式(常用):
mov ax,[bx+200+si]
mov ax,[200+bx+si]
mov ax,200[bx][si]
mov ax,[bx].200[si]
mov ax,[bx][si].200
# 7.8 不同的寻址方式的灵活应用
- [idata] 用一个常量表示地址,可用于直接定位一个内存单元;
- [bx] 用一个变量表示地址,可用于直接定位一个内存单元;
- [bx+idata] 用一个变量和常量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元;
- [bx+si] 用两个变量表示地址;
- [bx+si+idata]用两个变量和一个常量表示地址。
问题 7.6
6个字符串,每个长度均为16个字节,因为是连续存放的,可将这6个字符串看成一个6行16列的二维数组。
需要修改每一个单词的第一个字母,即二维数组的每一行的第4列(相对于行首的偏移地址为3)
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,6
s: mov al,[bx+3]
and al11011111b
mov [bx+3],al
add bx,16
loop s
2
3
4
5
6
7
8
9