Как реализовать 2D-массив в NASM - PullRequest
0 голосов
/ 08 ноября 2019

Я пытаюсь выяснить, как распечатать строки и столбцы массива. Программа спрашивает, сколько строк и столбцов, и в зависимости от пользовательского ввода будет отображаться «Введите число для [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

Когда я запускаю код, индексы [строки] [столбцы] не печатаются правильно. Кто-нибудь может направить меня?

1 Ответ

1 голос
/ 08 ноября 2019

Ваша проблема не имеет ничего общего с доступом к элементам массива - ваша логика верна (хотя imul eax,4 - плохая идея и должна быть заменена на shl eax,2 или lea 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
...