汇编07:定位内存地址的方法

定位内存地址的方法

and和or指令

and指令是按位与运算:

and al,00111011B

代表al中的值和数值00111011B进行按位与运算,然后将结果赋值给寄存器al。and指令可将操作对象的相应位设置为0,其他位不变,如将al的第6位设置为0:

and al,10111111B

or指令是按位或运算,同样的它能将操作对象的对应位设置为1,如将al的第6位设置为1:

or al,01000000B

以字符形式给出的数据

在汇编程序中,我们可以用单引号括起来的字符序列来表达多个数据,如:

db 'unIX'						相当于db 75H,6EH,49H,58H,db是define byte定义一字节数据。
mov al,'a'						相当于mov al,61H

其中的转换规则就是ASCII码,注意同一个字母大小写ascii码不一样。

案例:大小写转换

如果我们的程序有两个段,一个数据段datasg中定义了两个字符串,一个代码段codesg中定义了代码,需要把datasg中第一个字符串转换成大写,第二个字符串转换成小写。

首先我们可以找到大小写字母的ascii码关系,可以发现小写字母的ascii码比大写字母大20H,所以我们应该将第一个字符串中所有小写字母的ascii码都减20H,将第二个字符串中的所有大写字母的ascii码加20H,但是这个思路有一个问题:现在我们还没办法区分大小写字母。

我们发现同一个字母的编码第6位决定了是否是大小写,如果第6位是1就是小写,第6位是0就是小写,根据这个规律我们可以很简单的用and和or指令来转换大小写,同时又不会改动正确的位。

完整的程序如下:

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,也就是指向第一个字符串的起始位置
		mov cx,5						设置循环次数为5,因为第一个字符串有5个字母
	s:	mov al,[bx]						
		and al,11011111B
		mov [bx],al						取出字母,处理后放回去
		inc bx							bx加1,即将处理下一个字母
		loop s
			
		mov bx,5						设置bx为5,指向第二个字符串的起始位置
		mov cx,11						设置循环次数为11
	s0:	mov al,[bx]
		or al,00100000B
		mov [bx],al						取出字母,处理后放回去
		inc bx
		loop s0
		mov ax,4c00h
		int 21h
codesg ends
end start

[bx+idata]和idata[bx]

[bx]代表一个内存单元的偏移地址,[bx+idata]也表示一个内存单元的偏移地址,此时偏移地址为bx中的值加上数值idata。

下列指令:

mov ax,[bx+200]

代表将一个内存单元中的值放入寄存器ax中,这个内存地址的段地址在ds中,偏移地址为bx中的数值加200.

有了这种表示方式,我们可以表示一种类似数组的处理方式。

假如还是上个案例,我们要处理的字符串是等大的,长度都是5,此时我们就可以这样处理:

	mov ax,datasg
	mov ds,ax							让ds指向datasg段
	mov bx,0							设置bx指向字符串的起始位置
	mov cx,5							设置总的循环次数为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

这样,就让之前的问题得到了简化,前提是要处理的数据很规整,就像处理数组一样。idata[bx]和[bx+idata]是等价的,程序也可以这样写:

	mov ax,datasg
	mov ds,ax							让ds指向datasg段
	mov bx,0							设置bx指向字符串的起始位置
	mov cx,5							设置总的循环次数为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

si和di寄存器

si和di寄存器的作用和bx一样,[si]和[di]一样可以表示内存单元的偏移地址:

mov ax,[si]

代表将内存单元中的数据放入ax寄存器中,该内存单元的偏移地址是si中的数据,段地址是ds中的数据。

同样的,[si+idata]也有效。

假如我们要把字符串复制到它后面的数据区中,可以用si寄存器来完成:

assume cs:codesg,ds:datasg
datasg segment
	db 'welcome to masm!'
	db '.....................'
datasg ends
codesg segment
	start:	mov ax,datasg
		mov ds,ax					令ds指向datasg段
		mov si,0
		mov cx,8					设置起始偏移量和循环次数
	s:	mov ax,0[si]
		mov 16[si],ax					将字母取出,放入ax寄存器中,再从ax寄存器放入新地址
		add si,2
		loop s
			
		mov ax,4c00h
		int 21h
codesg ends
end start

我们可以把两个寄存器相加的值作为偏移地址,如:[bx+si]和[bx+di],同样也可以加上一个数值,即[bx+si+idata]和[bx+di+idata]。

不同寻址方式的对比应用

总结一下定位内存地址的几种方式:

1、[idata]用一个常量表示地址,可用于直接定位一个内存单元

2、[bx]用一个变量表示地址的偏移量,也就是间接定位一个内存单元

3、[bx+idata]用一个常量和变量间接表示地址,也可以表示为idata[bx],或[bx].idata

4、[bx+si]是两个变量表示地址

5、[bx+si+idata]是两个变量和一个常量表示地址,也可以表示为[bx].idata[si],一般用于处理类似结构体的数据,bx相当于结构体的地址,idata指明了数据项的地址,而用si进一步定位该数据项中的每一个字或字节。

由此可见,表示地址的方式越来越灵活,这些方式都可以用于不同的场合。

案例:用双层循环的大小写转换

要求将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

这个功能当然可以用一个循环来完成,但是灵活性很差,这里我们考虑用双层循环来解决,如果要用双层循环的话就面临一个问题,就是循环次数是默认存在cx寄存器中的,必须还得找一个位置存放另一个循环次数。

这里我们可以采用这样的方法:在每次开始内层循环的时候,将外层循环中的cx数值保存起来,在执行外层循环的loop指令前,再恢复外层循环的cx数值:

	mov ax,datasg
	mov ds,ax						让ds指向段datasg
	mov bx,0						
	mov cx,4						设置外层循环值是4
s0:	mov dx,cx						将外层循环次数保存在寄存器dx中
	mov si,0
	mov cx,3						内层循环开始前设置循环次数为3
	
s:	mov al,[bx+si]
	and al,11011111b
	mov [bx+si],al					        将字母取出,然后处理后放回原地址
	inc si
	loop s
	
	add bx,16
	mov cx,dx						将保存的cx值还给cx
	loop s0
	
	mov ax,4c00H
	int 21H

上述程序有一些局限性,主要是因为寄存器的数量是有限的,如果用寄存器作为保存关键数字的中转站,在较大的程序中寄存器就有用完的可能,所以我们可以尝试用一个内存单元来保存关键的数值,在数据段我们提前声明好一个空间然后使用:

assume cs:codesg,ds:datasg

datasg segment
	db 'ibm       '
	db 'dec       '
	db 'dos       '
	db 'vax       '
	dw 0								定义一个字来暂存cx寄存器中的数值
datasg ends

codesg segment
start:
		mov ax,datasg
		mov ds,ax						让ds指向段datasg
		mov bx,0						
		mov cx,4						设置外层循环值是4
	s0:	mov ds:[40H],cx					        将外层循环次数保存在内存单元中
		mov si,0
		mov cx,3						内层循环开始前设置循环次数为3
	
	s:	mov al,[bx+si]
		and al,11011111b
		mov [bx+si],al					        将字母取出,然后处理后放回原地址
		inc si
		loop s
	
		add bx,16
		mov cx,ds:[40H]					        将保存的cx值还给cx
		loop s0
		
		mov ax,4c00H
		int 21H
codesg ends

end start

我们也可以先声明一段空间用做栈,然后用push和pop指令来存入和取出数值。

原文地址:https://www.cnblogs.com/yinyunmoyi/p/12811503.html