目录
一、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]
七、不同寻址方式的灵活引用
前面用到了几种定位内存地址的方式,可以称为寻址方式
- [idata]用一个常量来表示地址,可以用于直接定位一个内存单元
- [bx]用一个变量来表示内存地址,用于间接定位一个内存单元
- [bx+idata]用一个变量和常量来表示地址,可在一个起始地址的基础上用变量间接定位内存单元
- [bx+si]用两个变量来表示地址
- [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