x86 程序集将整数读入结构数组然后打印
x86 Assembly read ints into struct array then print
我应该读取 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
mov dword[esi + Point.x], xVal
是地址的立即数。 您正在用传递给 scanf 的静态存储位置的地址副本填充数组,不是存储在那里的值 scanf。
mov [mem], [mem]
是非法的,所以你需要加载到一个临时寄存器以将其复制到其他地方 mov
.
或者更好的是,首先将正确的地址传递给 scanf。你的 xVal
存在的唯一原因是 scanf
的 scratch space,这是完全没有必要的。 (就像大多数其他静态存储一样,您可以只使用寄存器。)
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
或者您可以使用 mov
存储 args,并在整个循环中为它们分配堆栈 space,而不是推送和弹出。
请注意,esi
、edi,
和 ebx
在 i386 System V 调用约定中是保留调用的,还有 ebp
和 esp
。您的 main
不会保留其调用者在这些寄存器中的值。具有讽刺意味的是,您在调用 from main
的函数中使用了超慢的 pusha
/ popa
,但它只会破坏一些寄存器main
不需要它们来保存。
普通库函数会破坏 EAX、ECX 和 EDX,因此使用该调用约定是有意义的。 (不过,像您一样在寄存器中传递 args 很好;在堆栈上传递 args 的标准 32 位调用约定已经过时且效率低下。)
使用 int 0x80
进行的系统调用保留所有寄存器(EAX 除外,它获得 return 值)。
您不需要单独的 println 函数:您的 printDec
可以在数字末尾打印一个换行符。
我应该读取 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
mov dword[esi + Point.x], xVal
是地址的立即数。 您正在用传递给 scanf 的静态存储位置的地址副本填充数组,不是存储在那里的值 scanf。
mov [mem], [mem]
是非法的,所以你需要加载到一个临时寄存器以将其复制到其他地方 mov
.
或者更好的是,首先将正确的地址传递给 scanf。你的 xVal
存在的唯一原因是 scanf
的 scratch space,这是完全没有必要的。 (就像大多数其他静态存储一样,您可以只使用寄存器。)
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
或者您可以使用 mov
存储 args,并在整个循环中为它们分配堆栈 space,而不是推送和弹出。
请注意,esi
、edi,
和 ebx
在 i386 System V 调用约定中是保留调用的,还有 ebp
和 esp
。您的 main
不会保留其调用者在这些寄存器中的值。具有讽刺意味的是,您在调用 from main
的函数中使用了超慢的 pusha
/ popa
,但它只会破坏一些寄存器main
不需要它们来保存。
普通库函数会破坏 EAX、ECX 和 EDX,因此使用该调用约定是有意义的。 (不过,像您一样在寄存器中传递 args 很好;在堆栈上传递 args 的标准 32 位调用约定已经过时且效率低下。)
使用 int 0x80
进行的系统调用保留所有寄存器(EAX 除外,它获得 return 值)。
您不需要单独的 println 函数:您的 printDec
可以在数字末尾打印一个换行符。