[汇编语言]更灵活的定位内存地址的方法


一、and和or指令

and指令逻辑与指令,按位进行与运算

例如指令:

mov al,01100011B
and al,00111011B

执行后:al = 00100011B

通过该指令可以将操作对象的相应位设置为0,其他位不变

例如:

将al的第6位设置为0的指令是:and al,10111111B

or指令逻辑或指令,可以进行或运算

例如指令:

mov al,01100011B
or al,00111011B

执行后:al = 01111011B

可以通过该指令将操作对象的相应位设置为1,其他位保持不变


二、以字符的形式给出数据

在汇编语言中,我们用’…'的方式来指明数据是以字符的形式给出的,编译器将他们转化为相对应的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

上面的源程序中db 'unIX'相对应是db 75H,6EH,49H,58H分别对应unIX的ASCII码

mov al,'a'相对应是mov al,61H,对应a的ASCII码

在这里插入图片描述
可以用d命令查看data段,debug下是以16进制数码和ASCII码字符的形式显示出其中的内容


三、大小写转化问题

A 十六进制41H
a 十六进制61H

下面考虑这样一个问题,在codesg下填写代码,将datasg中的第一个字符串转化为大写,第二个字符串转化为小写

assume cs:codesg,ds:datasg

datasg segment
	db 'BaSiC'
	db 'iNfOrMaTiOn'
datasg ends

codesg segment
start:
	

codesg ends

end start

大写字符’A’的ASCII码值+20H就可以得到’a’
小写字符’a’的ASCII码值-20H就可以得到’A’

因此我们先判断大小写,看是否需要进行转化,以BaSiC讨论,程序的流程是这样的:

assume cs:codesg,ds:datasg

datasg segment
	db 'BaSiC'
	db 'iNfOrMaTiOn'
datasg ends

codesg segment
start:
	mov ax,datasg
	mov ds,ax
	mov bx,0
	mov cx,5

s:	mov al,[bx] ;每个字符送入al
	;如果(al)> 61H,则为小写字母的ASCII码,则sub al,20H
	mov [bx],al ;转化完的字符再送回去
	inc bx
	loop s
	
	;...
	
codesg ends

end start

目前问题是我们还没有学习判断的指令,所以我们如何用已学的指令来解决这个问题?

从ASCII的二进制形式来看,除了第5位外(从0开始计算位数),大小写字母的其他各位都是一样的,大写字母ASCII的第五位为0,小写字母的第5位为1,这样子我们不管他们原先是大写还是小写,将它的第五位置为0,就变为大写字母,置为1,就变为小写字母。

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
	mov cx,5
	
s:	mov al,[bx] ;ds:bx送入al
	and al,11011111B ;第五位设置为大写
	mov [bx],al ;转变后的送入原单元
	inc bx
	loop s
	
	mov bx,5
	mov cx,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]

前面,我们用[bx]的方式来指明一个内存单元,还可以用一种更灵活的方式来指明内存单元:[bx+idata]表示一个内存单元,它的偏移地址为:(bx) = idata 也就是bx中的数值再加上idata

mov ax,[bx+200]的含义:将一个内存单元的内容送入ax,这个内存单元的长度为2个字节,存放一个字,偏移地址为bx中的数值加上200,段地址在ds中,数字化描述为:(ax) = ((ds)*16 + (bx) + 200)

也可以写为:mov ax,[200+bx]mov ax,200[bx]mov ax,bx.200

这样子我们就可以用更高级的结构来看待要处理的数据:

在codesg中填写代码,将datasg中定义的第一个字符串转化为大写,第二个字符串转化为小写

现在我们可以使用[bx+idata]的方式来简化上面的程序,datasg中有两个字符串,一个起始地址为0,另外一个起始地址为5,我们可以将这两个字符串看作是两个数组,一个从地址0开始存放,一个是从5开始存放,我们就可以使用[0+bx],[5+bx]的方式在同一循环中定位这两个字符串中的字符。

assume cs:codesg,ds:datasg

datasg segment
	db 'BaSiC'
	db 'iNfOrMaTiOn'
datasg ends

codesg segment
start:
	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,001000000b
	mov [b+bx],al
	inc bx
	loop s
	
codesg ends

end start

五、SI和DI

SI和DI是8086中和bx功能相近的寄存器,SI和DI不能分为两个8位寄存器来使用,下面三组指令实现了相同的功能:

(1)
mov bx,0
mov ax,[bx]
(2)
mov si,0
mov ax,[si]
(3)
mov di,0
mov ax,[di+123]

用SI和DI实现将字符串’welcome to masm!'复制到他后面的数据区中

assume cs:codesg,ds:datasg

datasg segment
	db 'welcome to masm!'
	db '................'
datasg ends

codesg segment
start:
	;...是从16字节开始存放的
	mov ax,datasg
	mov ds,ax
	
	mov si,0
	mov di,16 ;ds:di指向复制的空间
	
	mov cx,8
	
s:	mov ax,[si] ; ds:si指向的内容送入ax
	mov [di],ax ; ax送入ds:di
	add si,2
	add di,2
	
	loop s
	
	mov ax,4c00h
	int 21h
	
codesg ends

end start

用一个16位寄存器进行传送,一次复制2个字节,一共循环8次

我们还可以进行一些优化,使得程序更加简单:

assume cs:codesg,ds:datasg

datasg segment
	db 'welcome to masm!'
	db '................'
datasg ends

codesg segment
start:
	;...是从16字节开始存放的
	mov ax,datasg
	mov ds,ax
	
	mov si,0
	
	mov cx,8
	
s:	mov ax,[si+0] ; ds:si指向的内容送入ax
	mov [si+16],ax ; ax送入ds:di
	add si,2
	
	loop s
	
	mov ax,4c00h
	int 21h
	
codesg ends

end start

六、[bx+si]和[bx+di] 与 [bx+si+idata]和[bx+di+idata]

现在我们还可以用更灵活的方式来指明一个内存单元:[bx+si][bx+di]

[bx+si]表示一个内存单元,它的偏移地址为(bx)+(si),bx中的数值加上si中的数值

该指令也可以写为如下形式:mov ax,[bx][si]

[bx+si+idata]表示一个内存单元,它的偏移地址为(bx)+(si)+ idata,bx中的数值加上si中的数值再加上idata

该指令也可以写为如下形式:
mov ax,[bx+200+si]
mov ax,200[bx][si]
mov ax,[bx][si].200
mov ax,[bx].200[si]


七、不同寻址方式的灵活引用

前面用到了几种定位内存地址的方式,可以称为寻址方式

  1. [idata]用一个常量来表示地址,可以用于直接定位一个内存单元
  2. [bx]用一个变量来表示内存地址,用于间接定位一个内存单元
  3. [bx+idata]用一个变量和常量来表示地址,可在一个起始地址的基础上用变量间接定位内存单元
  4. [bx+si]用两个变量来表示地址
  5. [bx+si+idata]用两个变量和一个常量来表示地址

编程,将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:
	
	
	mov ax,4c00h
	int 21h
	
codesg ends

end start

我们可以看到,在datasg中定义了6个字符串,每个长度为16个字节(有空格)他们是连续存放的,可以把他们看为是6行16列的二维数组,按照要求,需要修改每一个单词的第一个字母,即二维数组的每一行的第3列,相当于首行偏移地址为2

我们只需要进行6次循环,用一个R变量定义行,用常量2定位列

	R = 第一行地址
	mov cx,6
s:	改变R行,3列的字母为大写
	R = 下一行的地址
	loop s
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:
	
	mov ax,datasg
	mov ds,ax
	mov bx,0
	
	mov cx,6
	
s:	mov al,[bx+3]
	and al,11011111b
	mov [bx+3],al
	add bx,16
	loop s
	
	
	mov ax,4c00h
	int 21h
	
codesg ends

end start

编程,将datasg段中每个单词改为大写字母

assume cs:codesg,ds:datasg

datasg segment
	db 'fie				'
	db 'edt				'
	db 'sch				'
	db 'vie				'

datasg ends

codesg segment
	start:
	
	
	mov ax,4c00h
	int 21h
	
codesg ends

end start

data中定义了4个字符串,每个长度为16个字节(为了使在Debug中方便查看,所以我们每个字符串后面加了空格符)可以把这4个字符串看成一个4行16列的二维数组

assume cs:codesg,ds:datasg

datasg segment
	db 'fie				'
	db 'edt				'
	db 'sch				'
	db 'vie				'

datasg ends

codesg segment
	start:
	
	
	mov ax,4c00h
	int 21h
	
codesg ends

end start
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:
	
	mov ax,datasg
	mov ds,ax
	mov bx,0 ;ds:ax
	
	mov cx,4	
	
s0:	mov dx,cx ;将外层循环cx保存在dx中
	mov si,0 ;第一列的地址
	mov cx,3 ;这里是内层循环次数
	
s:	mov al,[bx+si] ; 内层循环按列进行,
	and al,11011111b
	mov [bx+si],al
	
	inc si

	loop s
	
	
	add bx,16
	mov cx,dx
	loop s0
	
	
	mov ax,4c00h
	int 21h
	
codesg ends

end start

上面的程序我们用dx来暂时存放cx的值,但是如果在内层循环中,dx寄存器也被使用,怎么办?因为CPU寄存器数量是有限的,那怎么办?

这样子我们就不能考虑使用寄存器来存放,我们就使用内存来存放,这样子我们就需要去开辟一段内存空间

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:
	
	mov ax,datasg
	mov ds,ax
	mov bx,0 ;ds:ax
	
	mov cx,4	
	
s0:	mov ds:[40H],cx ;将外层循环的cx值保存在datasg:40单元中
	mov si,0 ;第一列的地址
	mov cx,3 ;这里是内层循环次数
	
s:	mov al,[bx+si] ; 内层循环按列进行,
	and al,11011111b
	mov [bx+si],al
	
	inc si

	loop s
	
	
	add bx,16
	mov cx,ds:[40H] ;用datasg:40H单元中的值来恢复cx
	loop s0
	
	
	mov ax,4c00h
	int 21h
	
codesg ends

end start

上面的程序中,我们使用了内存单元来保存数据,但是上面的作法有些麻烦,因为如果需要保存多份数据的时候,我们需要记住数据存放在哪一个单元中,这样子程序容易混乱。一般来说,我们需要暂存数据的时候,我们都应该使用栈,所以可以再改进我们的程序

assume cs:codesg,ds:datasg,ss:stacksg

datasg segment
	db 'fie		'
	db 'edt		'
	db 'sch		'
	db 'vie 	'
datasg ends

stacksg segment
	dw 0,0,0,0,0,0,0,0
stacksg ends

codesg segment
	start:
	mov ax,stacksg
	mov ss,ax
	mov sp,16
	mov ax,datasg
	mov ds,ax
	mov bx,0
	
	mov cx,4
	
s0:	push cx		;将外层循环cx压入栈
	mov si,0
	mov cx,3	;cx为内层循环
	
s:	mov al,[bx+si]
	and al,11011111b
	mov [bx+si],al
	
	inc si
	loop s
	
	and bx,16
	pop cx
	loop s0
	
	
	mov ax,4c00h
	int 21h
	
codesg ends

end start
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值