Delphi编写变长参数的cdecl函数

文章来自:http://hi.baidu.com/523399/blog/item/f36e2e9b25041fbdc9eaf4c6.html

学过C语言的人都知道,printf的参数是不固定的,这种参数形式叫做变长参数。带有变长参数的函数必须是cdecl调用约定,由函数的调用者来清除栈,参数入栈的顺序是从右到左。printf是根据格式化串中的占位标记来猜测栈中的参数的。以下就用Delphi模拟一个简单的类似printf的函数sprintf。

type
   VA_FN
= function(const par1, par2{, ...}: Pointer): Integer; cdecl varargs;

procedure
CvtInt;
{
IN:
     EAX:   The integer value to be converted to text
     ESI:   Ptr to the right-hand side of the output buffer:   LEA ESI, StrBuf[16]
     ECX:   Base for conversion: 0 for signed decimal, 10 or 16 for unsigned
     EDX:   Precision: zero padded minimum field width
   OUT:
     ESI:   Ptr to start of converted text (not start of buffer)
     ECX:   Length of converted text
}

asm
         OR       CL,CL
         JNZ      @CvtLoop
@C1:     OR       EAX,EAX
         JNS      @C2
         NEG      EAX
         CALL     @C2
         MOV      AL,
'-'
         INC      ECX
         DEC      ESI
         MOV      [ESI],AL
         RET
@C2:     MOV      ECX,
10

@CvtLoop:
         PUSH     EDX
         PUSH     ESI
@D1:     XOR      EDX,EDX
         DIV      ECX
         DEC      ESI
         ADD      DL,
'0'
         CMP      DL,
'0'+10
         JB       @D2
         ADD      DL,(
'A'-'0')-10
@D2:     MOV      [ESI],DL
         OR       EAX,EAX
         JNE      @D1
         POP      ECX
         POP      EDX
         SUB      ECX,ESI
         SUB      EDX,ECX
         JBE      @D5
         ADD      ECX,EDX
         MOV      AL,
'0'
         SUB      ESI,EDX
         JMP      @z
@zloop: MOV      [ESI
+ EDX],AL
@z:      DEC      EDX
         JNZ      @zloop
         MOV      [ESI],AL
@D5:
end
;

function sprintf(lpcszFormat:PAnsiChar;lpszBuf:PAnsiChar;BufLen:Integer{Arg1,Arg2}):Integer;cdecl
;
  
label
Scan,ExitProc,MeetInteger,Movbuf,ScanLoop,CopyLoop,label1,label2,label3,Check1;
asm

  
{
   编译器自动加的指令:
   push ebp
   mov ebp,esp
  
}
   add ebp,
20 {ebp指向第一个格式参数}
   push edx
   push esi
   push edi
   sub esp,
24   { 16字符的缓冲区+4字节保存当前格式化串的子串指针+4字节=24 }
   lea esi,[esp
+24] {esi保存字符缓冲区结尾地址}
   mov edx,[ebp
-8] { edx保存目标串当前位置 }
   mov ebx,[ebp
-12]  { ebx保存格式化串当前位置 }
Scan:
   mov eax,ebx
   cmp byte ptr [eax],
0
   jz ExitProc
   push edx
   mov dx,
'%'
   call StrScan
(* 查找占位符% *)
   pop edx
   test eax,eax
   jz ExitProc
   mov ecx,eax
   sub ecx,ebx
   call label1
   jmp label2
label1:
   push eax
   CopyLoop:
   mov al,[ebx]
   cmp al,
0  {遇到0字符终止}
   jz label3
   mov [edx],al
   inc ebx
   inc edx
   loop CopyLoop
label3:
   pop eax
   ret
label2:
   cmp byte ptr [eax
+$01],'d'
   jnz Check1
   add ebx,
2
   call MeetInteger
   jmp Scan
Check1:
   cmp byte ptr [eax
+$01],'%'
   jnz ExitProc
   add ebx,
2
   mov byte ptr [edx],
'%'
   inc edx
   jmp Scan
MeetInteger:
   push eax
   push esi
   push edx
   mov eax,[ebp]
   mov ecx,
0  (* 有符号数 *)
   mov edx,
0
   call CvtInt
   pop edx
   call MovBuf
   add ebp,
4  {让ebp指向下一个参数}
   pop esi
   pop eax
   ret
Movbuf:
   mov al,[esi]
   mov [edx],al
   inc esi
   inc edx
   loop MovBuf
   ret
ExitProc:
   mov ecx,$7FFFFFFF
   call label1
   mov [edx],
0
   mov Result,
0
   add esp,
24
   pop edi
   pop esi
   pop edx
  
{
   编译器自动加的指令:
   pop ebp
   ret
  
}
end;

<script type="text/javascript"> </script> <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值