процедура, которая меняет местами байты (low/high) переменной Word

У меня есть эта процедура, которая меняет местами байты (low/high) переменной Word (она делает то же самое, что и System.Функция подкачки). Процедура работает, когда оптимизация компилятора выключена, но не когда она включена. Кто-нибудь может мне помочь?

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

5 ответов


быстрый:

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;

вы не можете использовать регистр EBX в коде ASM без его сохранения / восстановления. Исправленная версия кода

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;

Я немного удивлен, что никто не упомянул 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;

вы рассматривали возможность использования компилятора Swap?

procedure TForm1.FormCreate(Sender: TObject);
var
  a: word;
begin
  a := 34;
  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 := 3456;
  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;

хотя ответ Серга, безусловно, правильный, как указано в комментариях к ответу Серга, он не эффективен. Самым быстрым будет код, предоставленный в ответе 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) версия, представленная в этом ответе, должна по-прежнему работать на x64.