文章来自: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;
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>