您的位置:首页 > 其它

汇编语言:知识点拾遗

2016-12-18 17:33 190 查看

汇编语言:知识点拾遗

前言

读王爽的书已经有一段时间了,马上就要学习有关“中断”的知识了。在继续学习之前,我想把一些我感觉比较有意思的小细节,小技巧总结一下。文章不长,如果以后有新的收获,我会把这一系列继续写下去。

大小写字转换的技巧

一般来说,要想实现大小写字母的转换,有两种基本思路。

根据大小写字母在ascii表中的相对位置进行转换

根据字母在字母表中相对于首字母a的位置进行转换

第一种思路:

如果字母为大写字母,小写字母 = 大写字母 + 20H

如果字母为小写字母,大写字母 = 小写字母 - 20H

第二种思路:

如果字母为大写字母,小写字母 = ‘a’ + 大写字母 - ‘A’

如果字母为小写字母,大写字母 = ‘A’ + 小写字母 - ‘a’

王爽的书中有提供了令一种方法:

假设当前字母在寄存器AL中

如果AL为大写字母,则
and al,11011111B


如果AL为小写字母,则
or    al,00100000B


此时AL中即为所要转换的结果。

为什么可以这样做呢?我下面给出一张表格,供大家验证。

大写十六进制二进制小写十六进制二进制
A41H01000001a61H01100001
B42H01000010b62H01100010
C43H01000011c63H01100011
D44H01000100d64H01100100
E45H01000101e65H01100101
F46H01000110f66H01100110

JMP、CALL、RET指令的机制

以下用s表示标号

jmp short s
——依据位移修改IP进行段内短转移(8位位移)

jmp near ptr s
——依据位移修改IP进行段内近转移(16位位移)

jmp far ptr s
——段间远转移,使(CS) = s的段地址,(IP) = s的偏移地址

jmp 16位reg
——使(IP) = (reg)

jmp word ptr 内存单元地址
——使(IP) = (内存单元地址)

jmp  dword ptr 内存单元地址
——使(CS) = (内存单元地址+2),(IP) = (内存单元地址)

详细内容见原书。

我们可以用汇编语言(尽管不符合语法)去理解ret和call指令

ret
——相当于
pop IP


retf
——相对于先
pop IP
,然后
pop CS


call s
——相当于先
push IP
,然后
jmp near ptr s


call far ptr s
——相当于先
push CS
,再
push IP
,最后
jmp far ptr s


call 16位reg
——相当于
push IP
,然后
jmp reg(16位)


call word ptr 内存单元地址
——相当于
push IP
,然后
jmp word ptr 内存单元地址


call dword ptr 内存单元地址
——相当于先
push CS
,再
push IP
,最后
jmp dword ptr 内存单元地址


由于call与ret常常配合使用,所以之前我以为这两条指令必须成对出现(在设计子程序的时候)。汇编语言是相当自由的语言,当然没有这种约束,这些指令你想怎么用,你就怎么用,只不过它们的配合使用是一种“套路”罢了。

如果不熟悉call,ret的执行原理,在设计子程序的堆栈传参时,就会很难理解。

我在课堂上学习这两条指令的时候,老师完全没说堆栈的事,之后我发现有一个实验(输出一个集合的所有子集)是要用递归的技巧的,我当时就要骂人了,老师当时堆栈传参讲的不清不楚(说难听点,讲了和没讲是一样的),果断放弃这个选题,选了一个更简单的实验,但这样怎么体现出我认真的学习态度呢?(不要脸

后来学习了王爽的书,弄懂了这些原理,堆栈传参就很好理解了。

最后补充一点:

ret n
——相当于
pop IP
,然后
add  sp,n


C语言中局部变量也在堆栈中存放。

标志位DF与串处理指令

这部分老师在课堂上是不讲的(不知道其他学校是怎么样的)。其实这部分很简单。

方向标志位DF

DF = 1,每次操作后
inc si, inc di


DF = 2,每次操作后
dec si, dec di


cld
指令使DF=0,
std
指令DF =1

下面用汇编指令(不符合语法)来理解下面两条指令

movsb

功能:

mov         es:[di], byte ptr ds:[si]
;如果DF = 0
inc si
inc di
;如果DF = 1
dec si
dec di


movsw

功能:

mov         es:[di], word ptr ds:[si]
;如果DF = 0
add     si,2
add     di,2
;如果DF = 1
sub      si,2
sub      di,2


rep指令

rep指令常常和以上两条指令配合使用。举个例子:

rep movsb
相当于

s:      movsb
loop  s    ;根据CX决定循环次数


小结

串传送指令的注意点

传送的初始位置:ds : si

传送的目的位置:es : di

传送的长度: CX

传递的方向: 标志为DF

其他注意事项

由于篇幅原因(其实就是懒得再写下去了),还有一些注意点就不详细讨论了。

b0ed
比如:

- 除法指令的溢出问题

- 80X25彩色字符模式的显示缓冲区

总结

汇编语言是一门相当自由的语言,只要你有耐心,你可以用它完成好多事情。

这种感觉是在学习王爽的教材中体会得到。在此之前,我只是通过学校课堂大概了解了一下汇编语言,我甚至不知道数据段,代码段,堆栈段是可以通过自己修改段寄存器来“定义”的(就是蠢,没别的解释)。但这种自由也带来一种坏处,就是代码的可读性较差,核心代码往往被其他操作掩盖(比如中间值传递,传参,保护寄存器),因此看似很长的代码,实际实现的功能很简单(这让我怎么显摆),甚至昨天刚写的代码,第二天居然读不懂了(好吧,我只是因为懒,没写注释,但不管怎么说,汇编就是难读!)。

但不管怎么说汇编还是很有用的。下面就用王爽书中的实验11——编写子程序,结束这篇博文。(我怎么还没写注释!反正没有老师看,偷个懒啦)

assume      cs:codesg

datasg      segment
db 'Beginner`s All-purpose Symbolic Instruction Code.',0
datasg      ends

codesg      segment

main:       mov     ax,datasg
mov     ds,ax
mov     si,0
call    letterc

mov     ax,4c00h
int     21h
;-----------------------------------------------------------
;proc_name:     letterc
;function:      translate uppercase into lowercase in a string which ends with digit 0
;interface:     ds:si points to the first address of the string

letterc:            push    ax
push    si

letterc_s:      mov     al,[si]
cmp     al,'a'
jb      letterc_next
cmp     al,'z'
ja      letterc_next
and     al,11011111b
mov     [si],al
letterc_next:   inc     si
cmp     al,0
je      letterc_out
jmp     short letterc_s

letterc_out:    pop     si
pop     ax
ret
;----------------------------------------------------------
codesg      ends

end   main
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: