21 Average
21 Average

Reputation: 27

How do you implement a 2D Array in NASM

I am trying to figure out how to print out both rows and columns of an array. The program asks how many rows and columns and depending on user input it would display "Enter a number for [0][0]" "Enter a number for [0][1]" and etc.

Here is what I have written so far:

%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

When I run the code the indexes [rows][cols] do not print correctly. Can someone guide me?

Upvotes: 0

Views: 682

Answers (1)

Brendan
Brendan

Reputation: 37214

Your problem has nothing to do with accessing array elements - your logic is correct (although imul eax,4 is a bad idea and should be replaced by a shl eax,2 or lea eax,[eax*4] or lea eax,[ebx+eax*4], because the offset in the array is not a signed value and it's faster to avoid this multiplication).

Instead; your problem is that C calling conventions are nasty. They pollute the code with lots of extra instructions to manipulate the stack that make it harder to read and debug the code; and optimizing it (e.g. using sub esp, ... to reserve space for the max. parameters you want to pass to any child function and mov [rsp+ ...], ... instead of push ... to set parameters before calling a child function) is painful; and the whole thing ends up being an error prone and slow mess (that is unnecessary for assembly unless you're calling functions compiled by a C compiler).

More specifically; for your GetElement, you're using ebp as stack frame but not setting up ebp as the stack frame, so when the function tries to get the parameters from the stack into registers the function doesn't get the parameters from the right location.

To actually comply with C calling conventions (CDECL), it would want to be more like:

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

Ironically, for your code the parameters are already in registers - the only caller is doing this:

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

..which means that (if you forget about C calling conventions) your GetElement could be like this (10 unnecessary instructions deleted):

;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

..and the calling code could be like this (4 unnecessary instructions deleted):

    call GetElement

Upvotes: 1

Related Questions