第7章 更灵活的定位内存地址的方法
前面,我们用[0]、[bx]的方法,在访问内存的指令中,定位内存单元的地址。本章我们主要通过具体的问题来讲解一些更灵活的定位内存地址的方法和相关的编程方法。我们的讲解将通过具体的问题来进行。
7.1 and和or指令
首先,介绍两条指令and和or,因为我们下面的例程中药用到它们。
(1)and指令:逻辑与指令,按位进行与运算。
例如指令:
mov al,01100011B
add al,00111011B
执行后:al=00100011B
通过该指令可将操作对象的相应位设为0,其他位不变。
例如:
将al的第6位设为0的指令是: and al,10111111B
将al的第7位设为0的指令是: and al,01111111B
将al的第0位设为0的指令是: and al,11111110B
(2) or指令:逻辑或指令,按位进行或运算。
例如指令:
mov al,01100011B
or al,00111011B
执行后:al=01111011B
通过该指令可将操作对象的相应位设为1,其他位不变。
例如:
将al的第6位设为1的指令是: or al,01000000B
将al的第7位设为1的指令是: or al,10000000B
将al的第0位设为1的指令是: or al,00000001B
7.2 关于ASCII码
我们可能已经学习过ASCII码的知识了,这里进行一下复习。
计算机中,所有的信息都是二进制,而人能理解的信息是已经具有约定意义的字符。比如说,人在有一定上下文的情况下看到“123”,就可以知道这是一个数值,它的大小为123;看到“BASIC”就知道这是在说BASIC这种编程语言;看到“desk”,就知道说的是桌子。而我们要把这些信息存储在计算机中,就要对其进行编码,将其转化为二进制信息进行存储。而计算机要将这些存储的信息再显示给我们看,就要再对其进行解码。只要编码和解码采用相同的规则,我们就可以将人能理解的信息存入到计算机,再从计算机中取出。
世界上有很多编码方案,有一种方案叫做ASCII编码,是在计算机系统中通常被采用的。简单地说,所谓编码方案,就是一套规则,它约定了用什么样的信息来表示现实对象。比如说,在ASCII编码中,用61H表示“a”,62H表示“b”。一种规则需要人们遵守才有意义。
一个文本编辑过程中,就包含着按照ASCII编码规则进行的编码和解码。在文本编辑过程中,我们按一下键盘的a键,就会在屏幕上看到“a”。这是怎样一个过程呢?我们按下键盘的a键,这个按钮的信息被送入计算机,计算机用ASCII码的规则对其进行编码,将其转化为61H存储在内存的指定空间中;文本编辑软件从内存中取出61H,将其送到显卡上的显存中;工作在文本模式下的显卡,用ASCII码的规则解释显存中的内容,61H被当作字符“a”,显卡驱动显示器,将字符“a”的图像画在屏幕上。我们可以看到,显卡在处理文本信息的时候,是按照ASCII码进行的。这也就是说,如果我们要想在显示器上看到“a”,就要给显卡提供“a”的ASCII码,61H。如何提供?当然是写入显存中。
以字符形式给出的数据
我们可以在汇编程序中,用'......'
的方式指明数据是以字符的形式给出的,编译器将把它们转化为相对应的ASCII码。如下面的程序。
程序7.1
assume cs:code,ds:data
data segment
db 'unIX'
da '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,85H”,“u”、“n”、“I”、“X”的ASCII码分别为75H、6EH、49H、85H;
“db 'foRK'”相当于“db 66H,6FH,52H,4BH”,“f”、“o”、“R”、“K”的ASCII码分别为66H、6FH、52H、4BH;
"mov al, 'a'”相当于“mov al,61H”,“a”的ASCII 码为61H;
“mov bl, 'b'”相当于“mov al,62H”,“b”的ASCII码为62H。
将程序7.1编译为可执行文件后,用Debug加载查看data段中的内容,如图7.1所示。
图7.1中,先用r命令分析一下data段的地址,因“ds=0B2D”,所以程序中0B3DH段开始,data段又是程序的第一段,它就在程序的起始处,所以它的段地址位0B3DH。
用d命令查看data段,Debug以十六进制数码和ASCII码字符的形式显示出其中的内容。从而,可以看出data段中的每个数据所对应的ASCII码。
7.4 大小写转换的问题
下面考虑这样一个问题,在codesg中填写代码,将datasg中的第一个字符串转化为大写,第二个字符串转换为小写。
assume cs:codesg,ds:datasg
datasg segment
db 'BaSic'
db 'iNfOrMaTiOn'
datasg ends
codesg segment
start:
codesg ends
end start
首先分析一下,我们知道同一个字母的大写字符和小写字符对应的ASCII码是不同的,比如“A”的ASCII码是41H,“a”的ASCII码是61H。要改变一个字母的大小写,实际上就是要改变它所对应的ASCII码。我们可以将所有的字母的大写字符和小写字符所对应的ASCII码列出,进行一下对比,从中找到规律。
大写 |
十六进制 |
二进制 |
小写 |
十六进制 |
二进制 |
A |
41 |
01000001 |
a |
61 |
01100001 |
B |
42 |
01000010 |
b |
62 |
01100010 |
C |
43 |
01000011 |
c |
63 |
01100011 |
D |
44 |
01000100 |
d |
64 |
01100100 |
E |
45 |
01000101 |
e |
65 |
01100101 |
F |
46 |
01000110 |
f |
66 |
01100110 |
通过对比,我们可以看出来,小写字母的ASCII码值比大写字母的ASCII码值大20H。这样,我们可以想到,如果将“a”的ASCII码值减去20H,就可以得到“A”;如果将“A”的ASCII码值加上20H就可以得到“a”。按照这样的方法,可以将datasg段中的第一个字符串“BaSic”中的小写字母变成大写,第二个字符串“iNfOrMaTiOn”中的大写字母变成小写。
要注意的是,对于字符串“BaSic”,应只对其中的小写字母所对应的ASCII码进行减20H的处理,将其转为大写,而对其中的大写字母不进行改变;对于字符串“iNfOrMaTiOn”,我们应只对其中的大写字母所对应的ASCII码进行加20H的处理,将其转换为小写,而对其中的小写字母不进行改变。这里就存在着一个前提条件,程序必须要能够判断一个字母是大写还是小写。以“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)>61H,则为小写字母的ASCII码,则:sub al,20H
mov [bx],al
inc bx
loop s
.
.
.
codesg ends
end start
判断将用到一些我们目前还没有学习到的指令。目前面临的问题是,用已学的指令来解决这个问题,则不能对字母的大小写进行任何判断。
但是,现在的问题却要求程序必须要能区别对待大写字母和小写字母。那么怎么办呢?
如果一个问题的解决方案,使我们陷入一种矛盾之中。那么,很可能是我们考虑问题的出发点有问题,或是说,我们期初运用的规律并不合适。
我们前面所运用的规律是,小写字母的ASCII码值,比大写字母的ASCII码值大20H。考虑问题的触发点是:大写字母+20H=小写字母,小写字母-20H=大写字母。这使我们最终落入了这样的一个矛盾之中:必须判断是大写字母还是小写字母,才能决定进行何种处理,而我们现在又没有可以使用的用于判断的指令。
我们应该重新观察,寻找新的规律。可以看出,就ASCII码的二进制形式来看,除第5为(位数从0开始计算)外,大写字母和小写字母的其他各位都一样。大写字母ASCII码的第5位为0,小写字母的第5位为1。这样,我们就有了新的方法,一个字母,不管它原来是大写还是小写,将它的第5位置0,它就必将变为大写字母;将它的第5位置1,它就必将变成小写字母。在这个方法中,我们不需要在处理前判断字母的大小写。比如:对于“BaSic”中的“B”,按赵秋,它已经是大写字母了,不应进行改变,将它的第5位设为0,它还是大写字母,因为它的第5位本来就是0。
用什么方法将一个数据中的某一位置0还是置1?当然是用我们刚刚学过的or和and指令。
完整的程序如下。
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,ds:bx指向'BaSic'的第一个字母
mov cx,5 ;设置循环次数5,因为'BaSic'有5个字母
s: mov al,[bx] ;将ASCII码ds:bx所指向的单元中取出
and al,11011111B ;将al中的ASCII码的第五位置0,变为大写字母
mov [bx],al ;将转变后的ASCII码写回原单元
inc bx ;(bx)加1,ds:bx指向下一个单元
loop s
mov bx,5 ;设置(bx)=5,ds:bx 指向 'iNfOrMaTiOn'的第一个字母
mov cx,11 ;设置循环次数11,一位 'iNfOrMaTiOn'有11个字母
s0: mov al,[bx]
or al,00100000B ;al中的ASCII码的第五位置1,变为小写字母
mov [bx],al
inc bx
loop s0
mov ax,4c00h
int 21h
codesg ends
end start