【汇编教程】更灵活的定位内存
前言
上期我们学习了用[bx]定位内存的方法,今天让我们进一步学习一些定位内存的方法。
and和or指令
这是两个位运算指令,and表示逻辑与,or表示逻辑或,格式如下:
and/or 寄存器名,参数值可以实现对寄存器内的数及参数值进行位运算,结果存在该寄存器中。
[bx+idata]
(idata泛指常数,由编程者决定)
可以表示值为(bx)+idata的偏移地址(即bx寄存器的值加上idata这一常数作为偏移地址)。
[bx+di/si]
di和si寄存器功能与bx相近,但是不能分割为两个八位寄存器。
[bx+di]、[bx+si]分别表示(bx)+(di)、(bx)+(si)作为偏移地址。
[bx+di/si+idata]
表示(bx)+(di/si)+idata)作为偏移地址。
简单应用一下
练个题~
编程,将datasg段中每个单词改成小写字母
assume cs:codesg,ds:datasg
datasg segment
db 'Far '
db 'You '
db 'HJC '
db 'BBS '
datasg ends
codesg segment
start:
codesg ends
end start首先,我们还没有学习判断语句,所以要改变大小写只能用别的方式。观察一下大小写字母的ASCLL码,注意到大小写字母只在第五位有区别,只要将大写字母的第五位改成1,即可实现大写转小写,由此,可以选择使用逻辑或(即前面的or指令)。
由此,我们可以使用两层循环,在每层中修改字符串前三字节的第五位为1。
还有一个问题是两层循环的实现,我们只有cx一个寄存器,所以应该用另外一个寄存器在内层循环前暂存cx的值,就用dx吧~
代码:
assume cs:codesg,ds:datasg
datasg segment
db 'Far '
db 'You '
db 'HJC '
db 'BBS '
datasg ends
codesg segment
start:mov ax,datasg
mov ds,ax
mov bx,0
mov cx,4
s0:mov dx,cx ;暂存cx
mov si,0
mov cx,3
s:mov al,[bx+si]
or al,00100000b
mov [bx+si],al
inc si
loop s ;内层循环
add bx,16
mov cx,dx ;恢复cx
loop s0 ;外层循环
mov ax,4c00H
int 21H
codesg ends
end start数据标号和assume指令
数据标号是一种特殊的标号。之前我们介绍的“标号:”格式的标号,只能表示一个内存地址。而数据标号则具有表示地址和数据的双重功能。其格式如下:
标号名 db/dw/dd 数据1,数据2,...可以看到,该标号在定义数据时使用。下面是其使用格式:
首先要在使用前用伪指令assume说明使用数据标号的段:
assume ds:data这样ds寄存器就与data段产生了关联。在调用data段中的数据时,如果使用数据标号替代,编译器就会将其识别为以ds为段地址、以数据标号的偏移地址为偏移地址的内存地址或该内存地址代表的数据(有点绕,自己断下句,下面看个例子)。下面假设data段里有如下声明:
data segment
a dw 0,0
b db 1,2,3,4
data ends则a、b即为data:[0]和data:[4]的数据标号。之后在代码中将ds的值设为data的段地址:
mov ax,data
mov ds,ax即可使用该标号。下面是在一些指令中使用数据标号的例子。当其代表内存单元时:
mov ax,a ;代表mov word ptr ax,ds:[0]
add b,al ;代表add ds:[4],al
inc b[2] ;代表inc ds:[4+2]
dec a[1] ;代表dec ds:[2]作为一个地址时,在数据标号前加上offset即可。
需要注意的悬,数据标号同时代表了一个内存单元的长度。也就是说,当其作为数据进行操作时,要注意位数问题。以下代码会导致编译错误:
mov al,a
sub ax,b原因是al、ax的位数和a、b的位数对不上。
这里需要知道,assume是伪指令,只用来提示编译器,用assume进行所谓“关联”也并不会将段地址存入指定的段寄存器里。这个需要自己注意~
结语
今天我们学习了更多定位内存地址的方式,有助于我们更方便的编程。我是faryou,下次见!