zoukankan      html  css  js  c++  java
  • 汇编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指令来存入和取出数值。

  • 相关阅读:
    JSP简单练习-数组应用实例
    Android中的动画具体解释系列【4】——Activity之间切换动画
    php学习之道:WSDL具体解释(三)
    破解电信光猫(个人真实经验)
    POj 1879 Tempus et mobilius Time and motion (模拟+群)
    使用mysql-mmm实现MySQL高可用集群
    德克萨斯扑克_百度百科
    姜饼屋_百度百科
    阿根廷探戈----中英文对照
    波尔卡舞_百度百科
  • 原文地址:https://www.cnblogs.com/yinyunmoyi/p/12811503.html
Copyright © 2011-2022 走看看