2012-02-17 169 views
3

我有以下功能在Delphi 2006中工作,但在Delphi XE2下,它在处理RET时给出访问冲突错误或特权指令错误。德尔福XE2程序集

function Q_TrimChar(const S: string; Ch: Char): string; 
asm 
     PUSH ESI 
     MOV  ESI,ECX 
     TEST EAX,EAX 
     JE  @@qt 
     MOV  ECX,[EAX-4] 
     TEST ECX,ECX 
     JE  @@qt 
     PUSH EBX 
     PUSH EDI 
     MOV  EBX,EAX 
     MOV  EDI,EDX 
     XOR  EDX,EDX 
     MOV  EAX,ESI 
     CALL [email protected] 
     MOV  EDX,EDI 
     MOV  ECX,[EBX-4] 
@@lp1: CMP  DL,BYTE PTR [EBX] 
     JNE  @@ex1 
     INC  EBX 
     DEC  ECX 
     JNE  @@lp1 
     MOV  EDX,[ESI] 
     JMP  @@wq 
@@ex1: DEC  ECX 
@@lp2: CMP  DL,BYTE PTR [EBX+ECX] 
     JNE  @@ex2 
     DEC  ECX 
     JMP  @@lp2 
@@ex2: MOV  EDI,[ESI] 
     LEA  EDX,[EDI+ECX+1] 
@@lp3: MOV  AL,BYTE PTR [EBX+ECX] 
     MOV  BYTE PTR [EDI+ECX],AL 
     DEC  ECX 
     JNS  @@lp3 
@@wq: MOV  EAX,[ESI] 
     MOV  BYTE PTR [EDX],0 
     SUB  EDX,EAX 
     MOV  [EAX-4],EDX 
     POP  EDI 
     POP  EBX 
     POP  ESI 
     RET 
@@qt: MOV  EAX,ESI 
     CALL [email protected] 
     POP  ESI 
end; 

我不知道程序集很好。问题是什么?

+1

顺便说一句,这个asm代码只是编码非常好。例如,如果s字符串只是Ch的一个字符,它将会生成一个AV,恕我直言。忘掉它,然后使用Mike提供的第二个pascal版本作为回答。 – 2012-02-17 08:22:11

+1

我投票结束,因为这是如此狭义的定义。尽管有人似乎已经明确地回答了这个问题,但令人印象深刻的是,这类问题对网站的价值就像零。 – 2012-02-18 00:12:56

回答

17

我完全同意David的建议,只是在Pascal中编写代码,并提出了答案。除非分析表明这是一个真正的瓶颈,否则真的不需要ASM。这里有两个版本。第一个更容易阅读,但第二个更高效:

function Q_TrimChar(const S: string; Ch: Char): string; 
begin 
    result := S; 
    while (result <> '') and (result[1] = Ch) do Delete(Result, 1, 1); 
    while (result <> '') and (result[Length(Result)] = Ch) do Delete(Result, Length(Result), 1); 
end; 

function Q_TrimChar(const S: string; Ch: Char): string; 
var 
    First, Last : integer; 
begin 
    First := 1; 
    Last := Length(S); 
    while (First < Last) and (S[First] = Ch) do inc(First); 
    while (Last >= First) and (S[Last] = Ch) do Dec(Last); 
    Result := copy(S, First, Last-First+1); 
end; 
+1

为了实现代码的实际工作,做得很好+1。第二个版本更好。当First = 1和Last = Length(S)时,明显的优化不是调用复制。 – 2012-02-17 07:45:41

+0

+1。良好的编译器生成的字符串操作例程很难打败,特别是在这种简单的情况下。我不认为原始汇编程序比这里提供的第二个pascal变种更快! – 2012-02-17 10:01:17

+0

@CosminPrund你说得对,第二个版本比原来的asm版本少bug,并且性能会相同 - 甚至可能更好,因为恕我直言,asm版本是错误的(例如'S = Ch')。对于一个真正优化的关闭算法的asm版本,参见'函数Trim(const S:RawUTF8):RawUTF8;'在asm和pascal中[this unit](http://synopse.info/fossil/finfo?name=SynCommons .pas) - 我已经使用John O'Harrow的汇编程序,在Delphi 2009/2010/XE/XE2中进行了修改。 – 2012-02-17 12:26:23

6

Delphi 2006使用单字节ANSI字符,所以stringAnsiString,CharAnsiChar。在Delphi 2009及更高版本中,使用了两个字节的Unicode字符。这个函数不可能适用于两个编译器。

即使使用AnsiString和AnsiChar的标准破解也不起作用。最有可能的是,这个函数对RTL实现的假设在现代Delphi中不再有效。

我会在Pascal中重写这个函数,让编译器完成这项工作。这不仅是解决当前问题的最快捷方式,它还将帮助您跨越64位编译的障碍,并且您可以选择解决这个问题。

+1

downvoter会照顾评论吗? – 2012-02-17 08:39:54

+2

提供的代码中的主要问题并不是string/AnsiString问题,而是整个asm块本身,由于各种寄存器布局*和* LStrFromPCharLen API的新签名,x64本身根本无法工作因为德尔福2009年。 – 2012-02-17 12:17:53

+1

@Arnaud我发现批评相当薄弱。 x64在旁边。 OP正在编译32位。该代码在32位XE2中不起作用。它在32位2010或XE中也不起作用。再加上我评论了x64位,所以我已经覆盖。至于主要问题,我根本不买。代码的任何单一问题意味着它将无法工作。所以代码有多个缺陷。如果一个答案不能确定每一个问题,那么它是否值得一次downvote?这个非常明显的结论是,需要Pascal转换。无需对破损的代码进行过度分析。 – 2012-02-17 13:04:30