CnPack Forum


 
Subject: Delphi汇编学习(三篇)
不得闲
灌水科科长
Rank: 3Rank: 3



UID 40680
Digest Posts 3
Credits 118
Posts 33
点点分 118
Reading Access 10
Registered 2008-7-23
Status Offline
Post at 2008-7-23 12:51  Profile | Blog | P.M. 
Delphi汇编学习(三篇)

努力想学习汇编,所以先把以前博客上写的几篇关于BASM的文章贴过来。
大家共同看看,共同提高。
原帖位置:
http://suiyunonghen.blogspot.com ... E%E6%B1%87%E7%BC%96

在delphi中使用汇编异常的简单,只用使用关键字asm ....end来引导就行了。 得到一个字符在字符串中的位置有很多方法,最简单的就是使用delphi自己的Pos函数 另外一个方法就是使用 循环查找字符串数组的方法,二分法等,这几种是比较常规的方法,其中建议最好不使用Pos函数。 因为,虽然使用该函数写的代码是很简单,只用一句话 CharIndex := Pos(MyChar,str) 就能够完成,但是其实他的效率是最低的,因为delphi在使用它之前,先要开辟一个数组空间,然后将长度变成1 然后在把这个单字符拷贝到刚刚开辟的内存空间中去。然后才调用Pos函数进行查找,而查找完成之后,Delphi还 会 使用 Try...Finally...End机制来释放分配开辟的内存空间,而Try finally是很消耗资源的。所以不推荐使用。 顺序循环查找的方法也很简单,二分法稍微复杂一点。
下面是自己写的一个得到一个字符在字符串中的位置函数:(采用的是顺序查找,有兴趣可以在修改一下成为二分查找,其查找速度将会由数量级变成对数级)
止在讨论如何在Delphi中使用汇编!
function GetCharPos(ResChar: AnsiChar;Const s: string;Index: integer=0): integer;
asm
//Test指令执行的就是and与操作,唯一不同的是,不保存结果,所以Edx中的值不变
Test edx,edx; // 判断Edx是否为0,如果为0则指针为 nil
jz @Exit //如果为空, 则跳转退出
Mov Edi,Ecx; //保存Index的值,表示从什么地方开始查找
//使用一个增量计数Edi,可以在循环中使用[Edx+Edi]来得到S的每一个字符
mov ecx,[edx-4]; {得到S字符串的长度 Length(s)(因为在delphi的数据存储中String从1开始计数,那么0用来记录字符串的长度,所以我们移动到前一个地址上则是对应的字符串的长度了),Ecx用来存放字符个数便于使用Loop循环的计数器}
Test Ecx,Ecx; //看字符串 s是否为''没有一个字符,长度为0
Jz @Exit;
cmp ecx,edi //查找开始位置超过字符串长度,退出
Jb @Exit
//Mov edi,0;
@Compore:
cmp al,[edx+edi] //AL中存放字符
jz @Found //相等则说明找到
inc edi; //Edi计数器增加
Loop @Compore //循环比较
@Found:
inc Edi //由于Edi从0计数开始,所以加1
Mov Eax,edi //将查找结果传递给函数 返回结果保存在Eax中
ret
@Exit:
mov Eax,-1;
end;

[ 本帖最后由 不得闲 于 2008-7-23 12:56 编辑 ]
Top
不得闲
灌水科科长
Rank: 3Rank: 3



UID 40680
Digest Posts 3
Credits 118
Posts 33
点点分 118
Reading Access 10
Registered 2008-7-23
Status Offline
Post at 2008-7-23 12:53  Profile | Blog | P.M. 
delphi与汇编学习2
作者: 不得闲
时间:2007-11-21
联系:75492895@QQ.com
(此篇系属于原创,转载请保留字句)

进几天一直在研究在delphi中使用汇编的问题。上回说了一点。今天再把我刚刚弄出来的一点东西写上来。
EBP、ESP、BP和SP都称为指针寄存器,主要用于存放堆栈内存储单元的偏移量,用它们可实现多种存储器操作数的寻址方式。
现在我们来说一说EBP:
EBP是基址指针寄存器:一般用来确认堆栈帧的起始位置,也就是指向栈底。也就是说,一般一个函数入口的地址也就存放在EBP中(所以一般在进入函数的时候将ebp寄存器内容压栈,即保存其函数的上级调用函数的栈基地址,以便于以后返回调用)。
比如,我们要实现点一个Form1上的按扭Button1实现弹出对话框。我们一般是
procedure TForm1.Button1Click(Sender: TObject);
var ss: string;
begin
ss := '不得闲BASM测试';
showmessage(ss);
end;
那么换成汇编的写法如下:
procedure TForm1.Button1Click(Sender: TObject);
var ss: string;
begin
ss := '不得闲BASM测试';
asm
mov eax,[ebp-$04]
call showmessage
end;
end;
//说明:Ebp通过上面我们知道应该是用来指定Button1Click过程的入口地址,将其减4用来空一点空间出来用来存放局部变量 ,则此时[ebp-04]中存放的则是字符串 ss 的地址,所以此时 eax中存放的则是 'BASM测试' 的地址,然后 Call Showmessage,调用Showmessage函数,而Delphi默认的调用方式是寄存器方式,其1,2,3个参数分别对应着EAX,EDX,ECX, 而 EAX中已经存放了我们的局部变量地址。该函数则获得了字符串参数。

上面的是用在过程里面,直接使用赋值后的字符串的地址传递给Showmessage函数调用。下面我们使用另一个方法来使用局部变量:
通过查看CPU View我们可以知道,在上面的字符串赋值过程的汇编代码如下:
lea eax, [ebp-$04]{这是留出一个空位,用来给局部变量中转使用}
mov edx, $2342342{这里是一个随机的值,也就是字符串指针的值}
然后下面调用了一个系统函数LstrLAsg
Call LstrLAsg
该函数将EDX指向的地址内容复制到EAX指向的地址中去。所以由此可见,Delphi中的字符串赋值是通过拷贝实现的。
所以我们可以通过一个过程传递一个字符串地址值来得实现上面的方法,代码如下:
procedure Test(prAddress: integer);
asm
mov edx, eax //先将prAddress地址值存入Edx,便于调用LstrLAsg函数
lea eax, [ebp-$04] //将使用一个局部变量周转,(偏移地址)
Call System.@LstrLAsg //调用系统的LstrLAsg函数实现字串复制
mov eax, [ebp-$04] //将实际的局部变量地址传递给Eax让Showmessage调用
Call Showmessage //调用Showmessage函数
end;

然后我们在ButtonClick中调用如下:
procedure TForm1.Button1Click(Sender: TObject);
var ss: string;
begin
ss := '不得闲BASM测试';
Test(integer(ss));
end;
Top
不得闲
灌水科科长
Rank: 3Rank: 3



UID 40680
Digest Posts 3
Credits 118
Posts 33
点点分 118
Reading Access 10
Registered 2008-7-23
Status Offline
Post at 2008-7-23 12:53  Profile | Blog | P.M. 
delphi与汇编学习3
再来说一下关于参数的传递问题.
相信,看到这里了,基本的东西应该是能够搞清楚了。

在C,C++等语言中函数的参数的进栈顺序是从右向左进行,比如一个函数 int Func1(int x,y),所以其入栈方式就是:
Push Y
Push X
或者
mov (esp+4),Y //由于在调用函数之前会先把调用函数之后的指令地址地址入栈保存便于程序在执行完之后能够跳到正确的地方执行,入栈之后ESP减去4个字节所以,此时的栈顶指针应该增加4个字节
mov (esp), X//然后入栈参数X
然后是Call Func1,由此可见,需要使用存储器.
关于C中的我就介绍这样多吧,呵呵,也不晓得讲的对不对,都忘记完了.

那么在Delphi中参数的传递方式又如何呢?

在Delphi中,Delphi默认的参数传递方式是通过寄存器进行传递的.其中EAX,ECX,EDX三个寄存器用来传递参数.
参数1------> EAX
参数2------> EDX
参数3------> ECX

返回类型:
Char,Byte al 寄存器
SmallInt , Word ax 寄存器
Integer,LongWord,AnsiString,Pointer,class eax 寄存器
Real48 eax 寄存器中是栈内返回值的指针
Int64 edx:eax 寄存器对
Single,Double,Extended,Comp 栈首寄存器ST(0) //呵呵,这个东西还没有搞明白

下面我们来介绍几个简单的小例子呵呵,高手飘过)
1.得到最大数字,一个很简单的程序
function Max(x,y: integer): integer;
asm
//前面已经说了,EAX是第一个参数,EDX是第二个参数,则可知道
//EAX中存放的就是X的值,EDX中存放的则是Y的值,现在开始:
cmp EAX,EDX //比较
Jb @Beleve //小于
ret //否则直接返回
@Beleve:
mov EAX,EDX //EAX小于EDX,所以把EDX的值放到EAX中返回
end;

2:求和
function sum(x,y: integer): integer;
asm
add eax,edx
end;

3:得到第某个数字第index位上的数字是0还是1
//这里并没有使用到Eax和Edx作为参数进行值处理,因为不好控制其分配(反正我是一下没弄清楚,呵呵,如果有兴趣的可以使用Ctrl+Alt+C打开Cpu View的Cpu调试窗口看里面的汇编代码。)
function GetBit(index: Byte;Num: integer): byte;
var
msg: string;
begin
msg := '数字位数超界!';
asm
mov cl,index
mov eax,Num
cmp cl,32 //比较是否超过32
ja @ExitMsg //超过32跳到显示消息
@cmp1:
cmp cl,1 //是否小于1
jae @ValueRet//大于等于1跳到@ValueRet

@ExitMsg:
push eax //Eax压占保存,因为后面的Showmessage需要使用Eax传递的参数
PUSH Msg //将消息MSG压栈
Pop eax //弹出参数到EAX中,呵呵其实直接 Mov eax,Msg效果一样
call showmessage //调用函数显示消息
Pop Eax //恢复Eax中的值
mov al,0
jmp @Compolete //跳到完成
@ValueRet:
sub cl,1 //计算机中真正计位从0开始,所以减1
shr eax,cl //右移Index
And eax,1 //得到该位上的值
@Compolete:
mov @result,al
end;
end;
Top
lixupeng
普通灌水员
Rank: 2



UID 33687
Digest Posts 0
Credits 66
Posts 31
点点分 66
Reading Access 10
Registered 2007-12-11
Status Offline
Post at 2008-7-24 21:58  Profile | Blog | P.M. 
顶!!
Top
cqujsjcyj
新警察
Rank: 1



UID 40730
Digest Posts 0
Credits 2
Posts 1
点点分 2
Reading Access 10
Registered 2008-8-2
Status Offline
Post at 2008-8-2 14:13  Profile | Blog | P.M. 
好牛叉!

第一次发现汇编也可以这么好玩
Top
 




All times are GMT++8, the time now is 2024-4-23 15:36

    本论坛支付平台由支付宝提供
携手打造安全诚信的交易社区 Powered by Discuz! 5.0.0  © 2001-2006 Comsenz Inc.
Processed in 0.008958 second(s), 7 queries , Gzip enabled

Clear Cookies - Contact Us - CnPack Website - Archiver - WAP