【问题标题】:How to print index position x86 Assemby如何打印索引位置 x86 程序集
【发布时间】:2016-12-01 08:30:17
【问题描述】:

我是 Assembly 的新手,正在尝试完成作业,我想知道如何打印数组的索引位置而不是索引处的值,我使用 ESI 作为指针。这里数组填充了 0 和 1,我只想打印 1 的索引。 样本数组 [1|1|0|0|1]

PRINT:  
    mov eax,[esi]
    cmp eax,1
    je Show
    add esi,4
    loop PRINT
Show:   
    call WriteDec
    call Crlf
    loop Show

输出应为 (1 2 5) 或 (0 1 4)。 谢谢。

【问题讨论】:

  • 计数。就像您对 esi 所做的那样,但从 0 或 1 开始,然后逐步增加 1。
  • 你没有展示数组是如何定义的。在汇编中,数据的定义和结构往往比代码本身更重要。如果没有定义,很难判断您的代码是否按原样工作,是否可以修改以回答您,或者它是否已经包含一些错误(例如mov eax,[esi] 看起来非常可疑.. 但从add esi,4 看起来还不错) . minimal reproducible example 对 Assembly 非常重要。
  • 但是……再想一想,也许这个问题在没有代码的情况下更有意义,至于如果您询问方法/原理,我认为有效的作业,如何解决它,而不是解决方案代码。

标签: assembly x86 masm32 irvine32


【解决方案1】:

你必须从@MargaretBloom的想法开始,count,还需要修复算法中的一些其他错误:

  • 你有两个loop,问题是,当第一个loop结束时,下一个代码块执行,eax又被打印出来,无限循环开始了。
  • 您的第二个loop 跳转到Show,但它应该跳转到PRINT。
  • 你在je Show之后增加esi,所以当找到1的值时索引不会增加,esi应该增加之前。

让我们解决这些小问题,我将使用edi 作为您要显示的位置(您可以使用任何其他寄存器):

    mov edi, 0       ;◄■■ THIS IS THE POSITION YOU WANT TO DISPLAY (1,2,3,...).
PRINT:     
    mov eax,[esi]
    add esi,4        ;◄■■ INCREMENT INDEX HERE.
    inc edi          ;◄■■ INCREASE POSITION (1,2,3,...).
    cmp eax,1
    je Show
    loop PRINT
    jmp  Finish      ;◄■■ SKIP NEXT BLOCK WHEN FINISH.
Show:   
    mov  eax, edi    ;◄■■ DISPLAY POSITION.
    call WriteDec
    call Crlf
    loop PRINT       ;◄■■ JUMP TO PRINT, NOT TO SHOW.

Finish:

【讨论】:

    【解决方案2】:

    要么添加一个计数器:

    mov ebx,1           ; index counter, first element = 1
    PRINT:  
        mov eax,[esi]
        cmp eax,1
        jne noShow
    
        mov eax,ebx          
        call WriteDec
        call Crlf
    noShow:
        add esi,4
        inc ebx         ; increase counter
        loop PRINT
    

    或从 ESI 计算(假设 ESI = ARRAY + 4* 索引)

    PRINT:  
        mov eax,[esi]
        cmp eax,1
        jne noShow
    
        mov eax, esi       ; load position in index
        sub eax, ARRAY     ; remove starting position of the array
        shr eax, 2         ; divide it by 4, to get index position
        call WriteDec
        call Crlf
    
    noShow:
        add esi,4
        loop PRINT
    

    (PS:修正了循环逻辑,问题中写错了)

    (PSS:我无法测试代码atm,我在工作)

    【讨论】:

    • OP 的帖子中的代码已经错了……最后一个loop Show 没有意义。在您的第一个变体中,情况更糟,因为它不容易修复,因为您将仅在一个路径中更新 ebx。
    • shr eax, 4 中有错字。应该是 2
    【解决方案3】:

    如何...索引数组的位置... ESI 作为指针

    首先数组的定义很重要,如果它是由连续元素组成的简单平面数组,那么要获取地址EDI指向的特定元素的索引,您可以从涉及的两个指针反向计算它(这需要原始数组起始指针仍在ESI!):

    mov  eax,edi
    sub  eax,esi      ; eax = (element_pointer - array_pointer)
    ; divide eax by <size_of_element>
    
    ; for example your OP code example suggest
    ; the array element size is DWORD = 4B
    ; then the division/4 can be done simply:
    ; shr   eax,2
    
    ; for byte arrays there's no need to divide the address
    ; difference, eax already contains index, as BYTE size = 1B
    
    ; for other element sizes, which are *not* power-of-two
    ; (you can't divide the difference by simply shifting it right)
    ; it may be more efficient to address them through separate index
    ; or do the: imul/mul (1/el_size) || idiv/div el_size
    ; (when there's no way to avoid it)
    
    ; after division the eax contains 0, 1, ... index.
    

    如果元素的大小不是平凡的(不是二的幂),或者结构不是平凡的(链表,所以两个指针的差异与元素的索引无关),你可能想要计算索引分别地。仍然要避免每次获取索引的mul element_size 可能值得将两者混合,因此通过指针寻址,并分别计算(对获取无用)索引,这将仅用于您需要索引的事物。

    另外,在这个变体中,您可以从 1 索引,但我会避免这种情况,除非它是非程序员的一些人工输出,因为大多数 ASM/C/C++ 程序员自然希望索引从 0 开始(由于我的第一个示例中的指针数学如何工作)。

    ; stolen from Toommylee2k, then modified to focus on my explanation
    
        xor  ebx,ebx         ; first index will be 1 (!)
        ; so I initialized ebx = 1 - 1 = 0, because
        ; I will increment it at beginning of loop
    
        ; for indexing from 0 the ebx should be initialized to -1
    
    loop_start:
        ; update index first, so you can't miss it when branching later
        lea  ebx,[ebx+1] ; ebx = ebx+1 without flags modification
    
        ; since *here* "ebx" works as "index", contains "1" for first item
    
        ; do whatever you want with pointers, like "esi" in your sample code
        ; ...
    
        ; move to next element of array (avoids multiplication)
        add esi,size_of_element
        ; although `imul` on modern CPU will perform quite close to `add`
        ; so when multiplication is unavoidable, go for it.
    
        ; the division on the other hand still costs very likely more
        ; than having separate register/variable for index counting
    
        ; loop till ecx is zero (in much faster way than "loop")
        dec ecx
        jnz loop_start
        ; "loop" is very slow due to historic reasons, to improve compatibility
    

    最后的扩展,当元素大小是 [1, 2, 4, 8] 之一时,您可以使用扩展的 x86 寻址模式来处理“0, 1, ...”索引而不是纯指针:

    mov   ebx,7    ; index "7" = 8th element of array
    lea   esi,[array_of_words]  ; array pointer
    
    ; addressing through index, supported directly by CPU for size 2
    mov   ax,[esi + ebx*2]      ; complex x86 addressing allows this
    
    ; here ax = 8
    ...
    
    .data
    array_of_words:
        dw  1, 2, 3, 4, 5, 6, 7, 8, 9, 10
    

    如果您在循环中大量使用索引,这可能是最佳解决方案。如果您很少需要元素的索引,那么纯指针通常更理想。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-28
      • 2022-01-01
      • 2013-11-18
      • 1970-01-01
      • 2013-09-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多