arraysassemblynasmsasm

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?


Solution

  • 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