【问题标题】:How do you implement a 2D Array in NASM如何在 NASM 中实现二维数组
【发布时间】:2020-03-04 13:56:53
【问题描述】:

我想弄清楚如何打印出数组的行和列。该程序询问有多少行和列,并根据用户输入显示“为 [0][0] 输入一个数字”“为 [0][1] 输入一个数字”等。

这是我目前所写的:

%include "io.inc"

SECTION .data              ; Data section, initialized variables
num_rows db "How many rows?: ",0
num_col db "How many columns?: ",0
prompt db "Enter a number for [%d][%d]:",10,0

sum db "The sum is: ",10,0
number db "%d",10,0

rows times 4 dd "%d",0
col times 4 dd "%d",0

arrayLen dd 9 ; length of array 

;size equ rows*col

formatin db "%d", 0

section .bss
array resd 6; this is a test array for testing purposes

SECTION .text               ; Code section.

global CMAIN             ; the standard gcc entry point
extern    printf ,scanf 

CMAIN:                   ; the program label for the entry point

;-----Ask for the number of rows and display
push num_rows
call printf
add esp,4 ;remove the parameter

push rows ;address of rows
push formatin ;arguments are right to left
call scanf
add esp,8
;move the values into the registers
mov ebp,[rows]

push ebp
push number
call printf
add esp,8

;----Ask for the number of cols and display
push num_col
call printf
add esp,4

push col
push formatin
call scanf
add esp,8

;move the values into the registers
mov ebx, [col]

push ebx
push number
call printf
add esp,8

mov ebp,array

push ecx
push number
call printf
add esp,8 

mov ecx,0      
xor ebp,ebp

outerLoop:
  mov edx,ecx
  push ecx
  mov ecx,0                                                                                                                                                                                                                                                                                                                                                     
inner:
  push ecx

  ;output
  push ecx   
  push edx
  push prompt 
  call printf
  add esp,12

  ;Get addr
  push ecx
  push edx
  push esi
  ;call GetElement
  add esp,12

  ;input
 push eax
 push number
 call scanf
  add esp,8

  pop  ecx
  inc ecx
  cmp ecx,[col]
  jl inner
  pop ecx        

end_outer:
 inc ecx
 cmp ecx,[rows]
 jl outerLoop

push sum                                                                                                                                 
call printf
add esp,4                                                                                                                                                                                         

 xor ebp,ebp <- My professor told me never to use this
 ret

;GetElement: THIS WHOLE SUBPROGRAM IS COMMENTED     

;    mov ebx,[ebp+8] ;addr of array
;   mov ecx,[ebp+12] ;row
;  mov esi,[ebp+16] ;col

; mov eax,ecx
;  mul dword [col]
;  add eax,esi
;  imul eax,4
;  add eax,ebx
;  leave
;  ret

当我运行代码时,索引 [rows][cols] 无法正确打印。有人可以指导我吗?

【问题讨论】:

    标签: arrays assembly nasm sasm


    【解决方案1】:

    您的问题与访问数组元素无关 - 您的逻辑是正确的(尽管 imul eax,4 是个坏主意,应替换为 shl eax,2lea eax,[eax*4]lea eax,[ebx+eax*4],因为数组不是有符号值,避免这种乘法会更快)。

    相反;你的问题是 C 调用约定很讨厌。他们用大量额外的指令来污染代码来操纵堆栈,从而使代码更难阅读和调试;并对其进行优化(例如,使用sub esp, ... 为要传递给任何子函数的最大参数保留空间,并使用mov [rsp+ ...], ... 而不是push ... 在调用子函数之前设置参数)是痛苦的;整个事情最终成为一个容易出错且缓慢的混乱(除非您正在调用由 C 编译器编译的函数,否则这对于汇编来说是不必要的)。

    更具体地说;对于您的GetElement,您使用ebp 作为堆栈帧,但没有将ebp 设置为堆栈帧,因此当函数尝试将参数从堆栈中获取到寄存器时,函数不会获取参数从正确的位置。

    要真正遵守 C 调用约定 (CDECL),它应该更像:

    GetElement:
        push ebp
        mov ebp,esp            ;Set up ebp as stack frame
    
        push ebx               ;ebx is "callee preserved" (not "caller saved") and is modified, so it must be saved/restored
        push esi               ;esi is "callee preserved" (not "caller saved") and is modified, so it must be saved/restored
    
        mov ebx,[ebp+3*4+4]    ;addr of array
        mov ecx,[ebp+3*4+4+4]  ;row
        mov esi,[ebp+3*4+4+8]  ;col
    
        mov eax,ecx
        mul dword [col]
        add eax,esi
        lea eax,[ebx+eax*4]
    
        pop esi
        pop ebx
    
        leave
        ret
    

    具有讽刺意味的是,对于您的代码,参数已经在寄存器中 - 唯一的调用者正在这样做:

        push ecx         ;col
        push edx         ;row
        push esi         ;address of array
        call GetElement
        add esp,12
    

    ..这意味着(如果您忘记了 C 调用约定)您的 GetElement 可能是这样的(删除了 10 条不必要的指令):

    ;Inputs:
    ; ecx = column
    ; edx = row
    ; esi = address of array
    ;
    ;Outputs:
    ; eax = address of element in the array
    ;
    ;Trashed:
    ; edx
    
    GetElement:
        mov eax,edx
        mul dword [col]
        add eax,ecx
        lea eax,[esi+eax*4]
        ret
    

    ..调用代码可能是这样的(删除了4条不必要的指令):

        call GetElement
    

    【讨论】:

    • GetElement 如果从以下开始可能会更高效:mov eax, [col]; imul eax, edx。无需使用较慢的mul,它需要额外的一两个微指令将高半部分写入 EDX。此外,您不会丢弃任何寄存器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-09
    • 2011-03-17
    • 1970-01-01
    • 2013-11-13
    • 1970-01-01
    • 2013-04-12
    • 1970-01-01
    相关资源
    最近更新 更多