【问题标题】:Printing array as double word in assembly在汇编中将数组打印为双字
【发布时间】:2020-12-17 04:28:29
【问题描述】:
%include "asm_io.inc"
;
; initialized data is put in the .data segment
;
segment .data
array: dd 180,32,455,499,388,480,239,346,257,84
fmt: dd ",%d",0

; uninitialized data is put in the .bss segment
;
segment .bss
 resd 10
;
; code is put in the .text segment
;
segment .text
        extern  printf
        global  asm_main
asm_main:
        enter   0,0               ; setup routine
        pusha

; The following is just example of how to print an array

        push dword 10
        push dword array
        call print_array
        add  esp,8                ; clean up stack

; don't delete anything following this comment
        popa
        mov     eax, 0            ; return back to C
        leave                     
        ret



segment .data
ListFormat    db   ",%u", 0

segment .text
        global  print_array
print_array:
        enter   0,0
        push    esi
        push    ebx

        xor     esi, esi                  ; esi = 0
        mov     ecx, [ebp+12]             ; ecx = n
        mov     ebx, [ebp+8]
        xor     edx, edx
        mov     dl, [ebx + esi]     ; ebx = address of array
        mov     eax,edx             
        call    print_int
        dec     ecx
        inc     esi

print_loop:
        xor     edx,edx
        mov     dl,[ebx + esi]
        push    ecx                       ; printf might change ecx!
        push    edx                       ; push array value
        push    dword ListFormat
        call    printf
        add     esp, 8              ; remove parameters (leave ecx!)
        inc     esi
        pop     ecx
        loop    print_loop
        call    print_nl

        pop     ebx
        pop     esi
        leave
        ret

所以当我想打印出 180,32,455,499,388,480,239,346,257,84 时,这段代码会打印出 180,0,0,0,32,0,0,0,199,1。我认为这是因为这是为打印字节字而设计的。我正在尝试用双字打印,我猜 print_array 中的某些内容需要更改。我尝试了 mov dl, [ebx+esi*4] 但它​​仍然没有打印我想要打印的数组。还是需要更改其他内容才能打印双字数组?

【问题讨论】:

  • dl 是一个字节大小的寄存器。你想要mov edx, [ebx + esi*4](假设print_int 将使用edx 中的所有位,而不仅仅是dl)。

标签: arrays assembly x86


【解决方案1】:

您可以将 mov dl, [ebx+esi] 指令更改为 mov edx, [ebx+esi*4],但这只是乐趣的一半!

1 为什么不尝试创建一个循环来处理列表中不需要逗号前缀的第一个值的特殊情况?不再使用 print_int
2 也不要使用 LOOP 指令。很慢! cmp jb 对(可以进行宏熔断)要好得多。
3 并通过简单地通过 @ 寻址参数来替换 prolog enter 0,0 和 epilog leave 代码987654330@ 相对寻址很简单。
4 始终考虑特殊情况!如果数组恰好为空怎么办?

print_array:
        push    ebx
        push    esi
        mov     ebx, [esp+12]       ; Begin array
        mov     esi, [esp+16]       ; n
        test    esi, esi
        jz      done

        lea     esi, [ebx+esi*4]    ; End array
        mov     edx, ListFormat+1   ; "%u"
more:   mov     eax, [ebx]          ; Array dword value
        push    eax
        push    edx                 ; "%u" first time, ",%u" others
        call    printf
        add     esp, 8
        add     ebx, 4              ; To next dword in the array
        mov     edx, ListFormat     ; ",%u"
        cmp     ebx, esi            ; Current address < Last address ?
        jb      more                ; Yes
        call    print_nl

done:   pop     esi
        pop     ebx
        ret

在适当的条件下,将ESP 固定在此循环中是值得的。请参阅此答案下方的 Peter Cordes 的 cmets。
接下来是这段代码的一个版本,它将ESP 固定在循环内:

print_array:
        push    ebx
        push    esi
        mov     ebx, [esp+12]       ; Begin array
        mov     esi, [esp+16]       ; n
        test    esi, esi
        jz      done

        sub     esp, 8              ; Space for the printf args
        lea     esi, [ebx+esi*4]    ; End array
        mov     edx, ListFormat+1   ; "%u"
more:   mov     eax, [ebx]          ; Array dword value
        mov     [esp+4], eax
        mov     [esp], edx          ; "%u" first time, ",%u" others
        call    printf
        add     ebx, 4              ; To next dword in the array
        mov     edx, ListFormat     ; ",%u"
        cmp     ebx, esi            ; Current address < Last address ?
        jb      more                ; Yes
        call    print_nl
        add     esp, 8

done:   pop     esi
        pop     ebx
        ret

【讨论】:

  • 如果你想为了好玩而优化它,你可能会在循环中保持 ESP 固定,所以你用 mov [esp+4], eaxmov [esp], edx 存储 args。 add esp,8 在循环之后,但您需要在它之前有一个 sub esp,8。 (或者sub esp, 8+12,如果这是 Linux,则保持堆栈对齐 16;其他系统并没有通过要求 32 位代码中的 16 字节 ESP 对齐来破坏 ABI 兼容性。)但是,如果您想要高效但整体紧凑,这很好。 (并且适用于调用像 printf 这样的昂贵函数的代码)。用指针寄存器而不是剥离来处理不同格式的好主意。
  • @PeterCordes 感谢您纠正我的错字!我了解将ESP 固定在循环内的机制,但我不确定如何 这是一种改进。在您对this question 的回答中,尽管使用[ ESP+..] 有缺点,但您只将额外寄存器的可用性命名为优势。在上面的代码中没有寄存器短缺。那么这里到底有什么改进呢?
  • 改进之处在于循环内部少了一条指令,其他一切都一样。 (这些指令在现代 CPU 上都是单微指令,不像早期的 P6 中推送是多指令的。仍然是堆栈同步微指令,因为我们以任何一种方式直接访问 ESP;如果我们可以通过以下方式存储到堆栈中,也可以避免这种情况EBP。)这用较大的代码大小和静态指令计数换取较低的 dynamic 指令和 uop 计数。如果 printf 是一个便宜的函数(尽管使用了糟糕的堆栈参数约定)并且这个循环预计会运行 许多 次迭代,那么它可能是值得的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多