【汇编语言】更灵活的定位内存地址的方法(一)—— 字符操作:and与or指令、ASCII码及大小写转换
本文主要介绍了汇编语言中and和or指令的用法,以及如何利用规律将字母大小写进行转换。
文章目录
前言
📌
汇编语言是很多相关课程(如数据结构、操作系统、微机原理)的重要基础。但仅仅从课程的角度出发就太片面了,其实学习汇编语言可以深入理解计算机底层工作原理,提升代码效率,尤其在嵌入式系统和性能优化方面有重要作用。此外,它在逆向工程和安全领域不可或缺,帮助分析软件运行机制并增强漏洞修复能力。
本专栏的汇编语言学习章节主要是依据王爽老师的《汇编语言》来写的,和书中一样为了使学习的过程容易展开,我们采用以8086CPU为中央处理器的PC机来进行学习。
前面,我们用[0]、[bx]的方法,在访问内存的指令中,定位内存单元的地址。本章我们主要通过具体的问题来讲解一些更灵活的定位内存地址的方法和相关的编程方法。我们的讲解将通过具体的问题来进行。
1. and和or指令
首先,介绍两条指令 and和or,因为我们下面的例程中要用到它们。
1.1 and指令
and 指令:逻辑与指令,按位进行与运算。
例如指令:
mov al,01100011B
and al,00111011B
执行后:al=00100011B
通过该指令可将操作对象的相应位设为0,其他位不变。
例如:
-
将al的第6位设为0的指令是:
and al,10111111B
-
将al的第7位设为0的指令是:
and al,01111111B
-
将al的第0位设为0的指令是:
and al,11111110B
1.2 or指令
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
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。如何提供?当然是写入显存中。
3. 以字符形式给出的数据
我们可以在汇编程序中,用‘……’的方式指明数据是以字符的形式给出的,编译器将把它们转化为相对应的ASCII码。
3.1 示例代码
如下面的程序。
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 2lh
code ends
end start
3.2 分析代码
3.2.1 相关代码的含义
上面的源程序中:
-
“
db 'unIX'
” 相当于“db 75H,6EH,49H,58H
”, “u”、 “n”、 “I”、 “X”的ASCII码分别为75H、6EH、49H、58H。 -
“
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 al,'b'
”相当于“mov al,62H
”,”b”的ASCII码为62H。
3.2.2 查看代码段中的内容
将示例代码编译为可执行文件后,用Debug 加载査看 data 段中的内容,如下图所示。
上图中,先用r命令分析一下 data 段的地址,因“ds=0B2D”,所以程序从0B3DH段开始,data段又是程序中的第一个段,它就在程序的起始处,所以它的段地址为0B3DH。
用d命令查看data段,Debug 以十六进制数码和ASCII码字符的形式显示出其中的内容,从中,可以看出 data 段中的每个数据所对应的ASCII字符。
4. 大小写转换的问题
4.1 问题引入
下面考虑这样一个问题,在codesg中填写代码,将datasg 中的第一个字符串转化为大写,第二个字符串转化为小写。
4.2 问题代码
assume cs:codesg,ds:datasg
datasg segment
db 'BaSiC'
db 'iNfOrMaTiOn'
datasg ends
codesg segment
start:
codesg ends
end start
4.3 分析与解决问题
4.3.1 对比ASCII码值寻找规律
首先分析一下,我们知道同一个字母的大写字符和小写字符对应的ASCII码是不同的,比如“A”的ASCII码是41H,“a”的ASCI码是61H。要改变一个字母的大小的,写,实际上就是要改变它所对应的ASCII码。我们可以将所有的字母的大写字符和小写字符所对应的ASCI码列出来,进行一下对比,从中找到规律。
通过对比,我们可以看出来,小写字母的ASCII码值比大写字母的ASCII码值大20H。
这样,我们可以想到,如果将“a”的ASCII码值减去20H,就可以得到“A”;如果将“A”的 ASCII 码值加上20H 就可以得到“a”。
按照这样的方法,可以将 datasg 段中的第一个字符串“BaSiC”中的小写字母变成大写,第二个字符串“iNfOrMaTiOn”中的大写字母变成小写。
4.3.2 探讨程序流程
要注意的是,对于字符串“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
4.3.3 面临的问题
判断将用到一些我们目前还没有学习到的指令。现在面临的问题是,用已学的指令来解决这个问题,则不能对字母的大小写进行任何判断。
但是,现实的问题却要求程序必须要能区别对待大写字母和小写字。
那么怎么办呢?
4.3.4 重新思考问题,寻找解决方案
如果一个问题的解决方案,使我们陷入一种矛盾之中。那么,很可能是我们考虑问题的出发点有了问题,或是说,我们起初运用的规律并不合适。
我们前面所运用的规律是,小写字母的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指令。
4.3.5 正确的完整代码实现
完整的程序如下。
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码的第5位置为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码的第5位置为1,变为小写字母
mov [bx],al
inc bx
loop s0
mov ax,4c00h
int 2lh
codesg ends
end start
结语
今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。
也可以点点关注,避免以后找不到我哦!
Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)