【问题标题】:procedure that swaps the bytes (low/high) of a Word variable交换 Word 变量的字节(低/高)的过程
【发布时间】:2011-07-05 06:31:32
【问题描述】:

我有这个程序可以交换 Word 变量的字节(低/高)(它的作用与 System.Swap 函数相同)。该过程在编译器优化关闭时有效,但在打开时无效。 有人可以帮我解决这个问题吗?

procedure SwapWord(VAR TwoBytes: word);   
asm
  Mov EBX, TwoBytes
  Mov AX, [EBX]
  XCHG AL,AH
  Mov [EBX], AX
end;

【问题讨论】:

  • 我的第一个猜测是它完美无缺,也许优化导致了你没想到的调试结果。你能给我们提供一个最小的复制案例吗?

标签: delphi assembly basm


【解决方案1】:

最快:

function ReverseWord(w: word): word;
asm
   {$IFDEF CPUX64}
   mov rax, rcx
   {$ENDIF}
   xchg   al, ah
end;

如果你也想反转 DWORD:

function ReverseDWord(dw: cardinal): cardinal;
asm
  {$IFDEF CPUX64}
  mov rax, rcx
  {$ENDIF}
  bswap eax
end;

【讨论】:

  • @Altar:“需要”是一个非常强烈的词,尤其是因为你总是可以做到procedure Proc(var w: word) begin w := Func(w); end;
  • 但这肯定不比编译器的Swap函数快吗?
  • 不是更快,但不是每个 Delphi 都实现了 Swap。
  • @gabr-ReverseDWord 在 Win64 上不再工作。知道如何解决吗?
  • @Altar - 我已经编辑了答案以包含 X64 支持。
【解决方案2】:

如果不保存/恢复它,您不能在 ASM 代码中使用 EBX 寄存器。您的代码的更正版本是

procedure SwapWord_Working(VAR TwoBytes: word);   
asm
  PUSH EBX     // save EBX
  Mov EBX, TwoBytes
  Mov AX, [EBX]
  XCHG AL,AH
  Mov [EBX], AX
  POP EBX     // restore EBX
end;

【讨论】:

  • 或者,您可以只使用不需要保留的 ECX 或 EDX。
  • 我看到你在几秒钟内击败了我。我将删除我的(几乎相同的)答案。当然还有 +1。
  • @Andreas:对不起。并为您的详细回答 +1。
【解决方案3】:

我有点惊讶,没有人提到 absolute“hack”,它已经存在了十多年,但并没有引起太多关注……无论如何,这是我的两分钱

function SwapWordBytes(const Value: Word): Word;
var
  // shares memory with Value parameter
  LMemValue: array[0..1] of Byte absolute Value;
  // shares memory with Result
  LMemResult: array[0..1] of Byte absolute Result;
begin
  LMemResult[0] := LMemValue[1];
  LMemResult[1] := LMemValue[0];
end;

【讨论】:

  • absolute 已经存在了十多年,而且不仅仅是 Delphi 本身,我记得在 Turbo Pascal 6 或 7 中使用过它!不确定版本,强硬,希望我的记忆不是在开玩笑。 :)
  • @jachguate 你是对的,absolute 关键字用于快速访问视频内存的方式很早以前,我记得几年前我的一个朋友和我谈过这个,但我只使用它在德尔福...
  • 我过去使用指针来访问视频内存......但是绝对非常方便使用不同的变量来访问相同的地址而无需类型转换,就像在你的例子中一样。 :)
【解决方案4】:

您是否考虑过使用编译器的Swap 函数?

procedure TForm1.FormCreate(Sender: TObject);
var
  a: word;
begin
  a := $1234;
  a := Swap(a);
  Caption := IntToHex(a, 4)
end;

如果没有,则不需要 ASM(并且 ASM 在 64 位 Delphi 中可能不可用)。你可以这样做

procedure MySwap(var a: word);
var
  tmp: byte;
begin
  tmp := PByte(@a)^;
  PByte(@a)^ := PByte(NativeUInt(@a) + sizeof(byte))^;
  PByte(NativeUInt(@a) + sizeof(byte))^ := tmp;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  a: word;
begin
  a := $123456;
  MySwap(a);
  Caption := IntToHex(a, 4)
end;

当然,这个主题有“一百万种”变化。

procedure MySwap(var a: word);
var
  tmp: byte;
type
  PWordRec = ^TWordRec;
  TWordRec = packed record
    byte1, byte2: byte;
  end;
begin
  with PWordRec(@a)^ do
  begin
    tmp := byte1;
    byte1 := byte2;
    byte2 := tmp;
  end;
end;

并且,非常简短,

procedure MySwap(var a: word);
begin
  a := word(a shl 8) + byte(a shr 8);
end;

procedure MySwap(var a: word);
begin
  a := lo(a) shl 8 + hi(a);
end;

【讨论】:

  • 你好安德烈亚斯。为什么说 ASM 在 Delphi 64 下不可用?
  • Embarcadero 的一名员工 Barry Kelly 曾写过这篇文章。首先,他写道“几乎可以肯定没有内置的汇编器,但可能支持链接到用其他东西汇编的目标文件。”但现在我看到他已经编辑了他的文字:stackoverflow.com/questions/4051603/…
  • FWIW Allen Bauer 发布了 x64 内联汇编器的屏幕截图。尚未确认,但似乎很有可能出现在产品中。
  • 非常感谢 Andreas 提供的链接!
【解决方案5】:

虽然 Serg 的回答肯定是正确的,正如 cmets 对 Serg 的回答所指出的那样,但它效率不高。最快的显然是 Gabr 的答案中提供的代码,但是由于您明确需要一个过程,而不是一个函数,因此以下将是 Serg 例程的首选版本:

procedure SwapWord_Working2 (VAR TwoBytes: word);
asm
  mov dx, [TwoBytes]  ;//[TwoBytes] = [eax] on x86 *[Note1]
  xchg dl, dh
  mov [TwoBytes], dx
end;

[Note1:] Serg 的函数版本很可能不适用于即将推出的 x64 Delphi 编译器。假设 Embarcadero 坚持他们使用 Win64 调用约定(@TwoBytes 将通过RCX 传递)的计划(Allen Bauer 在某处提到),则此答案中提供的版本仍应适用于 x64。

【讨论】:

    猜你喜欢
    • 2011-05-13
    • 1970-01-01
    • 1970-01-01
    • 2011-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-30
    • 1970-01-01
    相关资源
    最近更新 更多