процедура, которая меняет местами байты (низкий / высокий) переменной Word - PullRequest
7 голосов
/ 27 февраля 2011

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

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

Ответы [ 5 ]

8 голосов
/ 27 февраля 2011

Нельзя использовать регистр 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;
8 голосов
/ 27 февраля 2011

Самый быстрый:

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;
7 голосов
/ 28 февраля 2011

Я немного удивлен, что никто не упомянул "хак" absolute, который существует уже более десяти лет, но не привлекает слишком много внимания ... в любом случае, вот мои два цента

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;
5 голосов
/ 27 февраля 2011

Рассматривали ли вы использование функции Swap компилятора?

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

Если нет, то для этого вам не нужен ASM (и ASM, вероятно, не будет доступен в 64Бит Дельфи).Вы можете просто сделать

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;
1 голос
/ 20 июня 2011

Хотя ответ Сергея, безусловно, правильный, как указано в комментариях к ответу Сергея, он неэффективен.Очевидно, что самым быстрым будет код, приведенный в ответе Габра, но поскольку вы явно хотите процедуру, а не функцию, предпочтительной версией подпрограммы Серга будет следующая:

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

[Примечание 1:] версия Сергафункции, по всей вероятности, не будет работать для будущего x64 Delphi компилятора.Предполагая, что Embarcadero придерживается своего плана (упомянутого где-то Алленом Бауэром) использования соглашения о вызовах Win64 (где @TwoBytes будет передано через RCX), версия, предоставленная в этом ответе, должна все еще работать на x64.

...