内联asm(32)模拟移动(复制内存)命令

时间:2016-02-23 19:51:20

标签: delphi assembly x86 inline-assembly memcpy

我有两个动态大小的二维数组(猜测是正确的措辞)。我使用以下方法将第一个的内容复制到另一个:

-mutableCopy

有效。但是我无法在asm中重现这一点。我尝试了以下变化无济于事......有人可以启发我......

nonnull

还尝试了LEA(没想到它会工作,因为它应该获取指针地址而不是数组地址),没有工作,并尝试用:

    dest:=copy(src,0,4*x*y);
    // src,dest:array of array of longint; x,y:longint;
    // setlength(both arrays,x,y);  //x and y are max 15 bit positive!

提示?顺便说一句,这是delphi 6.错误当然是访问冲突。

2 个答案:

答案 0 :(得分:2)

这实际上是一个双重三重问题。

  1. 动态数组的结构是什么。

  2. asm中的哪些指令会复制数组。

  3. 我在CPU上抛出随机汇编程序,为什么它不起作用?

  4. 动态数组的结构
    请参阅:http://docwiki.embarcadero.com/RADStudio/Seattle/en/Internal_Data_Formats 引用:

      

    动态数组类型

         

    在32位平台上,动态数组变量占用4个字节的内存(64位上的8个字节),其中包含指向动态分配的数组的指针。当变量为空(未初始化)或保持零长度数组时,指针为零,并且没有动态内存与变量关联。对于非空数组,变量指向动态分配的内存块,除了32位(Win64上的64位)长度指示符和32位引用计数外,还包含该数组。下表显示了动态数组内存块的布局。

         

    动态数组内存布局(32位和64位)

    Offset 32-bit    -8       -4       0 
    Offset 64-bit   -12       -8       0
    contents      refcount  count    start of data   
    

    因此动态数组变量是指向上述结构中间的指针。

    如何在asm中访问

    假设数组包含TMyRec类型的记录

    您需要为外部数组中的每个内部数组运行此代码以执行深层复制。我将此作为练习留给读者。 (你可以在pascal中做另一部分)。

    type
      TDynArr: array of TMyRec;
    
    procedure SlowButBasicMove(const Source: TDynArr; var dest);
    asm
      //insert register pushes, see below.
      mov esi,Source          //esi = pointer to source data
      mov edi,Dest            //edi = pointer to dest
      sub esi,8               
      mov ebx,[esi]           //ebx = refcount (just in case)
      mov ecx,[esi+4]         //ecx = element count
      mov edx,SizeOf(TMyRec)  //anywhere from 1 to zillions
      mul ecx,edx             //==ecx=number of bytes in array.
      //// now we can start moving 
      xor ebx,ebx             //ebx =0
      add eax,8               //eax = @data
      @loop:
      mov eax,[esi+ebx]       //Get data from source
      mov [edi+ebx],esi       //copy it to dest
      add ebx,4               //4 bytes at a time
      cmp ebx,ecx             //is ebx> number of bytes?
      jle loop 
      //Done copying.
      //insert register pops, see below
    end;
    

    这是完成的复制,但是为了使系统不崩溃,您需要保存和恢复非易失性寄存器(除了EAX,ECX,EDX之外),请参阅:http://docwiki.embarcadero.com/RADStudio/Seattle/en/Program_Control

    push ebx
    push esi
    push edi
    --- insert code shown above
    //restore non-volatile registers
    pop edi
    pop esi
    pop ebx  //note the restoring must happen in the reverse order of the push.
    

    如果你对asm完全陌生,请参阅Jeff Dunteman的书assembly step by step

    如果出现以下情况,您将获得访问权限:

    • 你试图从错误的地址读取。
    • 你试着写错了地址。
    • 你读过数组的末尾。
    • 在使用GetMem或其他任何方法之前,您写入了您尚未声明的内存。
    • 如果你写过缓冲区的末尾。
    • 如果您不还原所有非易失性寄存器

    记住你直接处理CPU。德尔福不会以任何方式为您提供帮助。

    真正快速的代码将使用某种形式的SSE在展开的循环中每条指令移动16字节,有关优化汇编程序的示例,请参阅上面提到的fastcode。

    随机汇编程序
    在汇编程序中,您需要知道完全您要做什么,CPU的工作方式和内容。
    设置断点并运行代码。按 ctrl + alt + C 并看到CPU调试窗口。
    这将允许您查看Delphi生成的代码 您可以单步执行代码以查看CPU的功能 见:http://www.plantation-productions.com/Webster/index.html
    更多阅读。

答案 1 :(得分:0)

动态数组与静态数组不同,特别是涉及多维度时。

有关内部格式,请参阅this reference

关键是Array Of Array Of LongInt维度XY(按此顺序!)是指向X指针数组的指针,指向数组Y LongInts

从您的评论中看,您已经为Dest中的所有元素分配了空间,我假设您要进行深层复制。

这是一个示例程序,为了清楚起见,尽可能简化了装配。

Program Test;

Var Src, Dest: Array Of Array Of LongInt;
    X, Y, I, J: Integer;
Begin

   X := 4;
   Y := 2;

   setLength(Src, X, Y);
   setLength(Dest, X, Y);

   For I := 0 To X-1 Do
      For J := 0 To Y-1 Do
         Src[I,J] := I*Y + J;


   {$ASMMODE intel}
   Asm
       cld                         ;Be sure movsd increments the registers
       mov esi, DWORD PTR [Src]    ;Src pointer
       mov edi, DWORD PTR [Dest]   ;Dest pointer

       mov ecx, DWORD PTR [X]      ;Repeat for X times
                                   ;The number of elements in Src

      @_copy:
       push esi                    ;Save these for later
       push edi 
       push ecx 

       mov ecx, DWORD PTR [Y]      ;Repeat for Y times
                                   ;The number of element in a Src[i] array
       mov edi, DWORD PTR [edi]    ;Get the pointer to the Dest[i] array
       mov esi, DWORD PTR [esi]    ;Get the pointer to the Src[i] array
       rep movsd                   ;Copy sub array

       pop ecx                     ;Restore
       pop edi 
       pop esi 

       add esi, 04h                ;Go from Src[i] to Src[i+1]
       add edi, 04h                ;Go from Dest[i] to Dest[i+1]
      loop @_copy

   End;


   For I := 0 To X-1 Do
      Begin
         WriteLn();

         For J := 0 To Y-1 Do
            Begin
               Write(' ');
               Write(Dest[I,J]);
            End;
      End;
End.

注1 此源代码旨在使用 freepascal 进行编译。
欢迎捐赠 Spare Time(TM)以下载和安装 Delphi

注意2 此源代码仅用于插图,很明显,上面已经说明了,但不是每个人都知道它。
如果OP想要一种快速复制阵列的方法,他们应该这样说。

注3 我没有保存被破坏的寄存器,这是不良做法,我的不好;我忘记了,因为没有子程序,没有优化,没有理由让编译器在两个寄存器之间的寄存器中传递数据。
这留给读者练习。