I'm trying to declare an array of "quadwords" on the stack in my x64 assembly program. I know how to do this by declaring the array in the .data
segment, but I'd like to make the array local to main
if possible. I know that I could probably do this with malloc
and dynamic memory allocation, as well, but I'd first like to see if it's even possible to do this on the stack. My issue is that I've declared enough memory on the stack to store the array (along with some extra space just for good measure). I store initial values into the array elements, but I don't know how to 'iterate' through the indices. I'd like to sum all the values in this array, just for practice. I tried using movq
to retrieve the element, offset from the array's starting index, but I can't use negative indices in 'scaled index' mode.
...
subq $128, %rsp
movq $100, -8(%rbp) # arr[0] = 100
movq $79, -16(%rbp) # arr[1] = 79
movq $85, -24(%rbp) # arr[2] = 85
movq $62, -32(%rbp) # arr[3] = 62
movq $91, -40(%rbp) # arr[4] = 91
movq $0, -48(%rbp) # sum = 0
movq $5, %rcx # movq i = 5
loop:
cmp $1, %rcx
jz done
movq (%rbp, %rcx, 8), %rax # I believe this line may be wrong because the array starts at index -8(%rbp), right?
addq %rax, -48(%rbp)
subq $1, %rcx
jmp loop
...
In this answer, I show several ways to change your code to do what you want. The first one is a minimal change to your original code to get it to work; the final one is the way I would write it.
The first example only changes the starting and ending value of rcx. It leaves the array on the stack in the unusual top-down order, and iterates over the array from the end to the beginning.
...
subq $128, %rsp
movq $100, -8(%rbp) # arr[0] = 100
movq $79, -16(%rbp) # arr[1] = 79
movq $85, -24(%rbp) # arr[2] = 85
movq $62, -32(%rbp) # arr[3] = 62
movq $91, -40(%rbp) # arr[4] = 91
movq $0, -48(%rbp) # sum = 0
movq $-5, %rcx
loop:
cmp $0, %rcx
jz done
movq (%rbp, %rcx, 8), %rax
addq %rax, -48(%rbp)
addq $1, %rcx
jmp loop
The next example places the array in memory in the usual way, with index 0 at the lowest address, and iterates from index 0 to 4. Note the offset on the load instruction to cause index 0 to access rbp-40.
...
subq $128, %rsp
movq $100, -40(%rbp) # arr[0] = 100
movq $79, -32(%rbp) # arr[1] = 79
movq $85, -24(%rbp) # arr[2] = 85
movq $62, -16(%rbp) # arr[3] = 62
movq $91, -8(%rbp) # arr[4] = 91
movq $0, -48(%rbp) # sum = 0
movq $0, %rcx # i = 0
loop:
cmp $5, %rcx
jz done
movq -40(%rbp, %rcx, 8), %rax
addq %rax, -48(%rbp)
addq $1, %rcx
jmp loop
The final example changes a few other things to match the way I would write it:
...
subq $128, %rsp
movq $100, -40(%rbp) # arr[0] = 100
movq $79, -32(%rbp) # arr[1] = 79
movq $85, -24(%rbp) # arr[2] = 85
movq $62, -16(%rbp) # arr[3] = 62
movq $91, -8(%rbp) # arr[4] = 91
xor %eax, %eax # sum = 0
xor %ecx, %ecx # i = 0
loop:
addq -40(%rbp, %rcx, 8), %rax
add $1, %ecx
cmp $5, %ecx
jb loop
This version keeps the sum in a register instead of in memory. It makes use of the fact that writing to the lower half of a 64-bit register clears the upper half of the register. It uses the usual technique to load 0 into a register. And it puts the loop condition at the bottom of the loop instead of the top.