0

I'm trying to read from a reserved memory in NASM using the following instructions:

movzx eax, [mat+100*edi+esi]
mov [dmat+298*edi+3*esi], eax

But I get the following error

invalid effective address
invalid effective address: two index registers

The first error is solved when using a lower factor such as 50 instead of 100. The second error turns into invalid effective address when the factor turns into 1 instead of 298.

I've checked and NASM is supposed to be able to perform this with the used registers as index, so what's the problem here?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 2
    NASM is an assembler, not a compiler. If the machine can't do that addressing mode in a single instruction, NASM isn't going to invent the necessary instructions to compute part of the address in a register. The syntax is correct but the scale-factor values are not encodeable. (See the linked duplicate; one scaled-index with a 2-bit shift count.) – Peter Cordes Sep 12 '22 at 22:07
  • 1
    Recall how the addressing mode is `[base+index*scale+displacement]` where `base` and `index` are general purpose registers, `scale` is one of 1, 2, 4, or 8, and `displacement` is a signed 32 bit number. You can't do the thing you wrote there on x86. – fuz Sep 12 '22 at 22:15
  • @PeterCordes Why does the error gets solved when using a factor of 50 instead of 100? – user19980548 Sep 12 '22 at 22:18
  • It doesn't. Perhaps you did `[mat+50 + edi*3 + esi]` or something. You can *add* any arbitrary constant to a symbol address, but you certainly can't scale an index register by 50. – Peter Cordes Sep 12 '22 at 22:27
  • @PeterCordes Mmm ok, I sort of understand now. Is doing the multiplication beforehand a good way to solve this? Or are there better options? Also, I'm afraid that the result will not fit in EAX after doing the multiplication. – user19980548 Sep 12 '22 at 22:39
  • Look at how a C compiler does it, e.g. for a function that accesses an array of structs or a 2D array. (https://godbolt.org/, enable optimization). Addresses are 32-bit in 32-bit mode, so if the result of `imul eax, edi, 100` would overflow, your index is out of bounds. – Peter Cordes Sep 12 '22 at 22:47
  • "Is doing the multiplication beforehand a good way to solve this?" Yes, if you need to multiply by a number that is not encodable in the various addressing modes. There are only a few multipliers available within the addressing modes and they are all small powers of 2. – Erik Eidt Sep 13 '22 at 01:00
  • "Also, I'm afraid that the result will not fit in EAX after doing the multiplication." The indexing calculations to address memory that is *known to exist* will not overflow when properly done; this includes the necessary multiplications and additions for simple indexing of 1 or more dimensional arrays. – Erik Eidt Sep 13 '22 at 01:05
  • *Known to exist* would be global data (the program won't run if there's not enough memory for its global data including any arrays therein), or an allocated array where the allocation has been dynamically verified to have succeed (i.e. by the program checking `malloc` for a `null` return value and terminating itself, or similar). Of course we have to do the arithmetic in unsigned, using at least pointer-sized integer arithmetic (32-bits on x86 and 64-bits on 64 bit machines). – Erik Eidt Sep 13 '22 at 01:09
  • 1
    *Known to exist* also means you're not accessing the array out of bounds. So then in summary, you'll know (a) the whole array exists, and (b) the index position you're trying to access exists; under those conditions and using the proper arithmetic size & unsigned, you can rest assured the indexing calculation will not overflow. – Erik Eidt Sep 13 '22 at 01:10
  • Frequently, our algorithms will naturally ensure that we access only index positions that exist. See, for example, the loop termination conditions in sorting algorithms that stop loops from going past the end of an array. – Erik Eidt Sep 13 '22 at 01:16

0 Answers0