x86 Assembly将int读取到struct数组中,然后打印

时间:2018-11-18 03:47:38

标签: assembly input x86 nasm increment

我应该读取10个整数,将它们存储为结构中的x和y点,然后将它们打印出来。 esi在我的输入循环中未正确递增。当我将值放入代码中的结构中时,当我使用输入循环时,它们可以正确打印:

What are the x and y values for the points?
1
2
3
4
5
6
7
8
9
0
The point coordinates are 
134520936
134520940
134520936
134520940
134520936
134520940
134520936
134520940
134520936
134520940

我需要精通gdb,但在我上课的过程中,讲师甚至都没有涉及它。

无论如何,似乎有两个内存地址,一个用于x,一个用于y,一次又一次地存储在struc数组中,然后打印。我尝试使用extern C printf而不是printDec函数,并且得到相同的输出。您能看到为什么地址而不是整数会进入我的点变量吗?您知道scanf是否修改了esi寄存器吗?

STRUC Point ;define a point structure
.x RESD 1   ;reserve 4 bytes for x coordinate
.y RESD 1   ;reserve 4 bytes for y coordinate
.size:
ENDSTRUC

section .data   ;data section

msgPt: db "What are the x and y values for the points?", 10, 0
msgPtL: equ $-msgPt

msg2: db"The point coordinates are ", 10  ,0
msgL2: equ $-msg2

formatPointScan: db "%d", 0

xVal: dd 0
yVal: dd 0

loopCount: dd 0

section .bss    ;bss section

PtArr: RESB Point.size*5    ;reserver place for 5 structures
;PtArr points to the start of array
ArrCount: EQU($ - PtArr)/Point.size ;should be 5


section .text

extern scanf

global main
main:
    push ebp
    mov ebp, esp

   mov ecx, msgPt
   mov edx, msgPtL
   call PString

    mov dword[loopCount], ArrCount  ;ecx has the number of array elements
    mov esi, PtArr  ;esi contains address of first struct in array

getPointsLoop:
    push xVal
    push formatPointScan
    call scanf
    mov dword[esi + Point.x], xVal  

    push yVal
    push formatPointScan
    call scanf
    mov dword[esi + Point.y], yVal
    add esi, Point.size
    dec dword[loopCount]
    cmp dword[loopCount], 0
jne getPointsLoop

    mov ecx, msg2
    mov edx, msgL2
    call PString
    mov dword[loopCount], ArrCount ;set ecx to num of array elements
    mov esi, PtArr  ;point esi to 1st element of array

printPointsLoop:
    mov eax, [esi + Point.x]    ;indirect access to x value
    call printDec
    call println

    mov eax, [esi + Point.y]    ;indirect access to y value
    call printDec
    call println

    add esi, Point.size
    dec dword[loopCount]
    cmp dword[loopCount], 0
jne printPointsLoop

    ;exit program and cleaning
    mov esp, ebp
    pop ebp
    ret

PString:; save register values of the called function
    pusha
    mov eax,4 ; use 'write' system call = 4
    mov ebx,1 ; file descriptor 1 = STDOUT
    int 80h ; call the kernel

    ; restore the old register values of the called function
    popa
    ret

println:
    ;will call PString func
    ;will change content of ecx and edx
    ;need to save registers used by the main program
    section .data
    nl db 10
    section .text
    pusha

    mov ecx, nl
    mov edx, 1
    call PString

    ;return original register values
    popa
    ret

printDec:
;saves all registers so they return unmodified
;build the function to handle dword size

    section .bss
    decstr resb 10 ; 10 32-bit digits
    ct1 resd 1 ;keep track of dec-string size

    section .text
    pusha; save registers

    mov dword[ct1],0 ;initially assume 0
    mov edi, decstr ; edi points to dec-string
    add edi, 9 ; moved to the last element of string
    xor edx, edx ; clear edx for 64-bit div
whileNotZero:
    mov ebx, 10 ; get ready to divide by 10
    div ebx ; divide by 10
    add edx, '0' ; convert to ascii
    mov byte[edi], dl ; put it in string
    dec edi ; move to next char in str
    inc dword[ct1] ; inc char counter
    xor edx, edx ; clear edx
    cmp eax, 0  ;is remainder 0?
    jne whileNotZero ;if no, keep on looping

    inc edi ; conversion finished, bring edi
    mov ecx, edi ; back to start of string. make ecx
    mov edx, [ct1] ; point to counterm edx gets # chars
    mov eax, 4 ; print to stdout
    mov ebx, 1
    int 0x80 ; call kernel

    popa ; restore registers
    ret

1 个答案:

答案 0 :(得分:1)

mov dword[esi + Point.x], xVal是地址的立即数。您正在用传递给scanf的静态存储位置的地址副本而不是存储的scanf值填充数组。在那里。

mov [mem], [mem]是非法的,因此您需要加载到临时寄存器中才能使用mov将其复制到其他地方。

或者更好的是,首先将scanf传递给正确的地址。 xVal甚至存在的唯一原因是作为scanf的临时空间,这完全没有必要。 (就像您可以使用寄存器的静态存储的其余大部分一样。)

section .rodata
fmt_2int: db "%d %d", 0

section .text

lea   eax, [esi + Point.y]
push  eax
push  esi         ; no LEA needed, Point.x = 0
push  fmt_2int
call  scanf        ; eax = scanf("%d %d", &arr[i].x, &arr[i].y);

add   esp, 12     ; pop args

或者您可以将args与mov存储在一起,并为它们在整个循环中分配堆栈空间,而不是压入和弹出。


请注意,esiedi,ebxebpesp一起保留在i386 System V调用约定中。您的main不会保留这些寄存器的调用者值。具有讽刺意味的是,您在从 pusha调用的函数中使用了超慢的popa / main,但只会破坏一些{{1 }}不需要保留它们。

正常的库函数会破坏EAX,ECX和EDX,因此使用该调用约定很有意义。 (不过,像您一样在寄存器中传递args很好;在堆栈上传递args的标准32位调用约定过时且效率低下。)

使用main进行的系统调用会保留所有寄存器(EAX除外,它会获取返回值)。

您不需要单独的println函数:您的int 0x80可以在数字末尾打印换行符。