Gor*_*ner 5 delphi assembly x86-64
我有一个 Delphi Firemonkey EXIF 实现,我在例程中使用它来加载图像文件。我试图确定图像是否已旋转,以便我可以在显示图像之前纠正图像的方向。该例程部分调用执行 BSWAP 的汇编代码来确定图像文件中标头信息的位置。这是代码的一部分:
type
TMarker = packed record
Marker : Word; //Section marker
Len : Word; //Length Section
Indefin : Array [0..4] of Char; //Indefiner - "Exif" 00, "JFIF" 00 and ets
Pad : Char; //0x00
end;
TIFDHeader = packed record
pad : Byte; //00h
ByteOrder : Word; //II (4D4D) or MM
i42 : Word; //2A00 (magic number from the 'Hitchhikers Guide'
Offset : Cardinal; //0th offset IFD
Count : Word; // number of IFD entries
end;
function SwapLong(Value: Cardinal): Cardinal;
asm bswap eax end;
procedure TExif.ReadFromFile(const FileName: string);
var
j: TMarker;
ifd: TIFDHeader;
off0: Cardinal; //Null Exif Offset
SOI: Word; //2 bytes SOI marker. FF D8 (Start Of Image)
f: File;
begin
if not FileExists(FileName) then exit;
Init;
System.FileMode:=0; //Read Only open
AssignFile(f,FileName);
reset(f,1);
BlockRead(f,SOI,2);
if SOI=$D8FF then begin //Is this Jpeg
BlockRead(f,j,9);
if j.Marker=$E0FF then begin //JFIF Marker Found
Seek(f,20); //Skip JFIF Header
BlockRead(f,j,9);
end;
//Search Exif start marker;
if j.Marker<>$E1FF then begin
i:=0;
repeat
BlockRead(f,SOI,2); //Read bytes.
inc(i);
until (EOF(f) or (i>1000) or (SOI=$E1FF));
//If we find maker
if SOI=$E1FF then begin
Seek(f,FilePos(f)-2); //return Back on 2 bytes
BlockRead(f,j,9); //read Exif header
end;
end;
if j.Marker=$E1FF then begin //If we found Exif Section. j.Indefin='Exif'.
FValid:=True;
off0:=FilePos(f)+1; //0'th offset Exif header
BlockRead(f,ifd,11); //Read IDF Header
FSwap := ifd.ByteOrder=$4D4D; // II or MM - if MM we have to swap
if FSwap then begin
ifd.Offset := SwapLong(ifd.Offset);
ifd.Count := Swap(ifd.Count);
end;
if ifd.Offset <> 8 then begin
Seek(f, FilePos(f)+abs(ifd.Offset)-8);
end;
Run Code Online (Sandbox Code Playgroud)
当应用程序是为 32 位 Windows 构建时,此方法工作正常,但在 64 位 Windows 下调用 SwapLong 时会失败。我对汇编语言一无所知,因此我正在寻找如何在构建 64 位版本的程序时处理相同的功能。请注意,在这两个版本中,传递给 SwapLong 函数的 idf.OffSet 值都是 134217728 ($08000000)。在 32 位版本中,SwapLong 返回值 8,但 64 位版本返回值 2694969615,因为输入看起来相同。
我需要 64 位版本才能工作,因为我希望使用相同的代码来定位 64 位 MAC OSX。任何帮助将不胜感激。
Che*_*yDT 10
存在此问题是因为内联程序集假定第一个参数以及返回值使用 register eax,根据 Delphi 的调用约定,这对于 32 位模式下的 Delphi 来说是正确的(尽管内联程序集文档指出不应该有可以对除ebp和以外的寄存器做出任何假设esp,即使在内联汇编语句内部(当它们被放置在函数顶部时)也始终如此)。
但是,64 位模式使用不同的调用约定,其中第一个参数是 in rcx,返回值是 using rax。因此,在这里您将获得随机的未初始化垃圾作为返回值,该值恰好位于该寄存器中(其字节已交换),因为它从未显式设置。
最好的、可移植的解决方案是在纯 Pascal 中实现字节交换,而无需内联汇编:
function SwapLong(Value: Cardinal): Cardinal;
begin
Result := Swap(Value shr 16) or (Cardinal(Swap(Value)) shl 16);
end;
Run Code Online (Sandbox Code Playgroud)
这使用了数十年历史的Swap函数,该函数交换值的低 2 个字节。它本身不再有多大用处,但可以使用两次(连同一些位移位和掩码)来缩短交换 32 位值的所有 4 个字节的代码。
另一种拥有更多源代码但可以生成更少复杂汇编代码的方法是使用Cardinal字节指针访问各个字节:
function SwapLong(Value: Cardinal): Cardinal; inline;
begin
PByte(@Result)^ := PByte(NativeUInt(@Value) + 3)^;
PByte(NativeUInt(@Result) + 1)^ := PByte(NativeUInt(@Value) + 2)^;
PByte(NativeUInt(@Result) + 2)^ := PByte(NativeUInt(@Value) + 1)^;
PByte(NativeUInt(@Result) + 3)^ := PByte(@Value)^;
end;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
408 次 |
| 最近记录: |