更灵活的定位内存地址的方法
之前:[idata]、[bx]
7.1 and 和 or 指令。
(1)and 指令:逻辑与指令,按位进行 与 运算。
通过该指令可将操作对象的相应位设为0,其他位不变。(异位为0)
将al的第6位设为0的指令是:and al,10111111B
将al的第7位设为0的指令是:and al,01111111B
(2) or 指令 :逻辑或指令,按位进行 或 运算。
通过该指令可将操作对象的相应位设为1,其他位不变。(异位为1)
将al的第6位设为0的指令是:and al,01000000B
将al的第7位设为0的指令是:and al,10000000B
7.2关于ASCII码
世界上有很多编码方案,有一种方案叫做ASCII编码,是计算机系统中通常被采用的。(一套规则)
显示a:将a的ASCII的编码放入显存中,再通过ASCII码的规则将a画到显示器中。
7.3以字符形式给出的数据
我们可以再汇编程序中,用"....."的方式指明数据是以字符的形式给出的,编译器将把他们转化为相应的ASCII码。
如:
assume cs:code,ds:data
data segment
db 'nuIX'
db 'foRK'
data ends
code segment
start: mov al,'a'
mov bl,'b'
mov ax,4c00h
int 21h
code ends
end start
"db 'nuIX'" 相当于 "db 75H,6EH,49H,58H"。
"db 'foRK'" 相当于"db 66H、6FH、52H、4BH"。
"mov al,'a'"相当于"mov al,61H"。
"mov bl,'b'"相当于"mov al,62H"。
7.4大小写转换的问题。
如果一个问题的解决方案,使我们陷入一种矛盾之中。那么,很可能是我们考虑问题的出发点有了问题,或是说,我们起初运用的规律并不合适。
规律一:大写字母+20H=小写字母,小写字母-20H=大写字母。这使我们陷入这样的一个矛盾中:必须判断是大写字母还是小写字母,才能决定进行何种处理,而我们现在又没有可以使用的用于判断的指令。
规律二:就ASCII码的二进制形式来看,除第5位(从0计数)外,大写字母和小写字母的其他各位都一样。大写字母ASCII码的第5位为0,小写字母的第5位为1.
解决方案:一个字母,不管它原来是大写还是小写,将它的第5位置0,它就必将变为大写字母;将它的第5位置1,它就必将变为小写。
程序:
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
inc bx
loop s
mov bx,5
mov ax,11
s0:mov al,[bx]
or al,00100000B
mov [bx],al
inc bx
loop s0
mov ax,4c00h
int 21h
codeseg ends
end start.
7.5[bx+idata]
[bx+idata]表示一个内存单元,它的便宜地址为(bx)+idata(bx中的数值加上idata)
用Debug查看查看内存,结果如下:
2000:1000 BE 00 06 00 00 00 ......
写出下面的程序执行后后,ax、bx、cx中的内容。
mov ax,2000H
mov ds,ax
mov bx,1000H
mov ax,[bx]
mov cx,[bx+1]
mov cx,[bx+2]
我的解答:ax=00BE、bx=1000H、cx=0606
7.6 用[bx + idata]的方式进行数组的处理
在codesg中填写代码,将dataseg中定义的第一个字符串转化为大写,第二个字符串转化为小写。
assume cs:codeseg,da:detasg
datasg segment
db 'BaSic'
db 'MinInx'
datasg ends
codesg segment
start:
codesg ends
end start
改进的程序:
mov ax,da
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,00100000b
mov [5+bx],al
inc bx
loop s
也可以写成这样:
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,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
如果用C来写:
char a[5]="BaSic";
char b[5]="MinInx";
main()
{
int i;
i =0;
do
{
a[i] =a[i] &0xDF;
b[i] =b[i] | 0x20;
i++;
}
while(i<5);
}
比较相似之处:尤其是定位字符串中字符的方式。
C:a[i],b[i]'
汇编:0[bx],5[bx]
通过比较,我们可以发现,[bx+idata]的方式为高级语实现数组提供了便利机制。
7.7SI和DI
SI和DI是8086CPU中和bx功能相近的寄存器,SI和DI不能够分成两个8位寄存器来使用。
问题7.2:用SI和DI实现将字符串‘welcome to masm!’复制到它后面的数据区中。
assume c:codesg,ds:datasg
datasg segment
db 'welcome to masm!'
db '................'
datasg ends
答:
codesg segment
start: mov ax,datasg
mov ds,ax
mov si,0
mov di,16
mov cx,8
s: mov ax,[si]
mov [di],ax
add si,2
add di,2
loop s
mov ax,4c00h
int 21h
codesg ends
end start
更简洁:
分析:我们可以利用[bx(si或di)+idata]的方式,来使程序变得简洁。如下:
codesg segment
start: mov ax,datasg
mov ds,ax
mov si,0
mov cs,8
s: mov ax,0[si]
mov 16[si],ax
add si,2
loop s
mov ax,4c00h
int 21h
codesg ends
end start
7.8[bx+si]和[bx+di]
更灵活的指明偏移地址:[bx+si]和[bx+di]
[bx+si]和[bx+di]的含义相似,指令mov ax,[bx+si]
数学化得描述:(ax)=((ds)*16+(bx)+(si))
也可以写成如下格式:mov ax,[bx][si]
问题:
用Debug 查看内存,结果如下:
2000:1000 BE 00 06 00 00 00 .......
写出下面的程序,执行后、bx、cx中的内容。
mov ax,2000h
mov ds,ax
mov bx,1000h
mov si,0
mov ax,[bx+si]
inc si
mov cx,[bx+si]
inc si
mov di,si
add cx,[bx+di]
解答后:ax=00BEh bx=1000h cx=0606h
7.9[bx+si+idata]和[bx+di+idata]
他两含义相似。
指令mov ax,[bx+si+idata]
数学描述:(ax)=((ds)*16+(bx)+(si)+idata)
也可以写成如下格式:
mov ax,[bx+200+si]
mov ax,[200+bx+si]
mov ax,200[bx][si]
mov ax,[bx].200[si]
mov ax,[bx][si].200
问题7.5
用Debug查看内存,结果如下:
2000: 1000 BE 00 06 00 6A 22 .....
写出下面的程序执行后,ax,bx,cx中的内容。
源码: 解答:
mov ax,2000h ax=2000h
mov ds,ax ds=2000h
mov bx,1000h bx=1000h
mov si,0 si=0
mov ax,[bx+2+si] ax=0006h
inc si si=1
mov cx,[bx+2+si] cx=ds:1000+2+1=6A00h
inc si si=2
mov di,si di=2
mov bx,[bx+2+di] bx=ds:1000+2+2=226Ah
7.10不同的寻址方式的灵活应用
(1)[idata]用一个常量来表示地址,可用于直接定位一个内存单元;
(2)[bx]用一个变量来表示内存地址,可用于间接定位一个内存单元;
(3)[bx+idata]用一个变量和常量表示地址,可在一个起始地址的基础用变量间接定位一个内存单元;
(4)[bx+si]用两个变量表示地址;
(5)[bx+si+idata]用两个变量和一个常量表示地址。
问题7.6
编程,将datasg段中每个单词的头一个字母改为大写字母。
assume cs:codesg,ds:datasg
datasg segment
db '1.file '
db '2.edit '
db '3.search '
db '4.view '
db '5.options '
datasg ends
codesg segment
start:
codesg ends
end start
解答:
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,6
s: mov al,[bx+3] ;只是在寻址的时候加3.不是永久加3
and al,11011111b
mov [bx+3],al
add bx,16
loop s
问题7.7
编程,将datasg段中每个单词改为大写字母。
assume cs:codesg,ds:datasg
datasg segment
db: 'ibm '
db: 'dec '
db: 'dos '
db: 'vax '
datasg ends
codesg segment
start:
codesg ends
解答:
start:mov ax,datasg
mov bx,0
mov cx,4
S0: mov dx,cx ;将外层循环的cx值保存在dx中
mov di,0
mov cx,3 ;cx设置为内层循环的次数
s: mov al,[bx+di]
and al,11011111b
mov [bx+di],al
inc di
loop s
add bx,16
mov cx,dx
loop s0
mov ax,4c00h ;用dx中存放的外层循环的计数器恢复cx
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
mov bx,0
mov cx,4
s0: mov ds:[40H],cx ;将外层循环的cx值保存在datasg:40h单元中
mov si,0
mov cx,3
s: mov al,ds:[bx+si]
and al,11011111b
mov ds:[bx+si],al
inc si
loop s
add bx,16
mov cx,ds:[40H] ;用datasg:40h单元中的值恢复cx
loop s0 ;外层循环的loop指令将cx中的计数值减1.
mov ax,4c00h
int 21h
codesg ends
说明:上面的程序中,用内存单元来保存数据,可是上面的做法却有些麻烦,因为如果需要保存多个数据的时候,你必须要记住数据放到了那个单元中,这样程序容易混乱。
一般来说,在需要暂存数据的时候,我们都应该使用盏。
解答:(使用盏来暂存数据)
assume cs:codesg,ds:datasg,ss:stacksg
datasg segment
db: 'ibm '
db: 'dec '
db: 'dos '
db: 'vax '
datasg ends
stacksg segment
dw 0,0,0,0,0,0,0,0 :定义一个段,用来做栈段,容量为16个字节
stacksg segment
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,ds:[bx+si]
and al,11011111b
mov ds:[bx+si],al
inc si
loop s
add bx,16
pop cx ;从栈顶弹出cx的值,恢复cx
loop S0 ;外层循环的loop指令将cx 中的计数值减1
mov ax,4c00h
int 21h
codesg ends
end start
问题7.9
编程,将datasg段中每个单词的前4个字母改写为大写字母
assume cs:codesg,ss:stacksg,ds:datasg
stacksg segment
dw 0,0,0,0,0,0,0,0
stacksg ends
datasg segment
db '1. display123456'
db '2. brows12345678'
db '3. replace123456'
db '4. modify1234567'
datasg ends
codesg segment
start:
codesg ends
end 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
mov si,0
mov cx,4
s: mov al,ds:[bx+3+si]
and al,11011111b
mov ds:[bx+3+si],al
inc si
loop s
add bx,16
pop cx
loop S0
mov ax,4c00h
int 21h
本章总结:寻址方式[bx(或si、di)+idata]、[bx+si(或 di)]、[bx+si(或di)+idata]的意义和应用;
二重循环问题的处理。
盏的应用;
大小写转化的方法;
and、or 指令。
寻址方式的适当应用,使我们可以以更合理的结构来看待所要处理的数据。
而为所要处理的看似杂乱的数据设计一种清晰的数据结构式程序设计的一个关键的问题。