如何在 NASM 中实现二维数组
How do you implement a 2D Array in NASM
我想知道如何打印出数组的行和列。该程序询问有多少行和列,并根据用户输入显示 "Enter a number for [0][0]" "Enter a number for [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
当我 运行 索引 [行] [列] 的代码无法正确打印。有人可以指导我吗?
您的问题与访问数组元素无关 - 您的逻辑是正确的(尽管 imul eax,4
是个坏主意,应该用 shl eax,2
或 lea eax,[eax*4]
或 lea eax,[ebx+eax*4]
,因为数组中的偏移量不是有符号值,避免这种乘法会更快)。
相反;你的问题是 C 调用约定很讨厌。他们用大量额外的指令污染代码来操纵堆栈,使代码更难阅读和调试;并优化它(例如,使用 sub esp, ...
为要传递给任何子函数的最大参数保留 space,并使用 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
我想知道如何打印出数组的行和列。该程序询问有多少行和列,并根据用户输入显示 "Enter a number for [0][0]" "Enter a number for [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
当我 运行 索引 [行] [列] 的代码无法正确打印。有人可以指导我吗?
您的问题与访问数组元素无关 - 您的逻辑是正确的(尽管 imul eax,4
是个坏主意,应该用 shl eax,2
或 lea eax,[eax*4]
或 lea eax,[ebx+eax*4]
,因为数组中的偏移量不是有符号值,避免这种乘法会更快)。
相反;你的问题是 C 调用约定很讨厌。他们用大量额外的指令污染代码来操纵堆栈,使代码更难阅读和调试;并优化它(例如,使用 sub esp, ...
为要传递给任何子函数的最大参数保留 space,并使用 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