I can tell this is a super simple problem but I have yet to figure it out. Basically, I just want to be able to take one element an array and add and subtract some numbers from it using registers and then put the result into my result variable.
segment .data
a dw 4, 234, -212
b db 112, -78, 50
result dq 0
segment .text
global main
main:
mov rax, [a]
I know the solution has something to do with offsets and indexing, but I don't get how I am supposed to be able to get just one array element into a register.
What can I do?
If you want to treat your values as signed, you want
movsx
. Assuming NASM syntax:(MASM or GNU .intel_syntax would use
word ptr
instead ofword
, just addptr
to the size specifier for the memory operand.)The
1
can be a register like[a + rsi*2]
or[b + rsi]
so you can easily loop over your arrays. Referencing the contents of a memory location. (x86 addressing modes)I wrote
1*2
instead of just 2 to indicate that it's index 1 (the 2nd array element), scaled by the element size. The assembler will evaluate the constant expression and just use the same (RIP-relative) addressing mode it would for[a]
but with a different offset.If you need it to work in position-independent code (where you can't use a
[disp32 + register]
addressing mode with a 32-bit absolute address for the symbol),lea rdi, [a]
(RIP-relative LEA) first and do[rsi + rsi*2]
.If you wanted zero-extension, you'd use
movzx
If you knew the upper bits of your full result would always be zero, just use EAX (32-bit operand-size) except at the end. The advantages of using 32bit registers/instructions in x86-64
This code corresponds to C like
Speaking of which, you can look at compiler output on the Godbolt compiler explorer and see these instructions and addressing modes.
Note that
mov al, [b + 1]
would load a byte and merge it into the low byte of RAX.You normally don't want this;
movzx
is the normal way to load a byte in modern x86. Modern x86 CPUs decode x86 to RISC-like internal uops for register renaming + Out-of-Order execution.movzx
avoids any false dependency on the old value of the full register. It's analogous to ARMldrb
, MIPSlbu
, and so on.Merging into the low byte or word of RAX is a weird CISC thing that x86 can do but RISCs can't.
You can safely read 8-bit and 16-bit registers (and you need to for a word store) but generally avoid writing partial registers unless you have a good reason, and you understand the possible performance implications (Why doesn't GCC use partial registers?). e.g. you've xor-zeroed the full destination ahead of cmp +
setcc al
.