arrayspointersassemblyx86masm

Indexing an array in .data faults with an address a lot higher than the array


My homework to the change values inside an array in MASM, swapping pairs.

array[0] = array[1]
array[1] = array[0]
array[2] = array[3]
array[3] = array[2]

and so on....

I have abandoned using the var1 and var2 variables because I can't seem to get them to work. I have

.data
; add data here
array01 DWORD 100, 200, 300, 400, 500, 600
var1 DWORD ?
var2 DWORD ?

.code
main PROC
    mov edi, OFFSET array01         ; 1: EDI = address of array01
    mov ecx, LENGTHOF array01       ; 2: initialize loop counter
    mov eax, 0                      ; 3: sum =0

L1:                                 ; 4: Mark beginning of loop
    mov eax, [edi]                  ; 5: Get var1
    add edi, TYPE array01           ; 6: increment index
    mov ebx, [edi]                  ; 7: Get var2
    sub edi, TYPE array01           ; 8: decrement to get index to point to the right location

    mov array01[edi], ebx           ; 9: Change array01[i] to var1
    add edi, TYPE array01           ; 10: increment index
    mov array01[edi], eax           ; 11: Change array01[i] to var1
    loop L1                         ; 12: repeats until ecx is 0

    INVOKE ExitProcess, 0
main ENDP
End main

My problem is at instruction 9. It's giving me an error

Unhandled exception at 0x007C1032 in Assignment_03.exe: 0xC0000005: Access violation writing location 0x00F88004.

From the debugger, my EDI = 007C4000, and &array01 is at 0x007C4000, matching each other but not the data address reported for the fault. (My EIP = 007C1032, the code address of the faulting instruction.)

An earlier run had Exception thrown at 0x00171029 in Assignment_03.exe: 0xC0000005: Access violation writing location 0x002E8000.
I understand that it's telling me I'm accessing something I'm not allowed to but I don't know how to fix it.


I have tried mov [numList+ebx],dh from https://termspar.wordpress.com/2019/10/12/x86-assembly-swap-array-elements/ But I still get the same error.

What am I doing wrong? Can you please explain the solution. To be clear I don't want an answer to my homework. I want help in changing a value in an array.

mov dword ptr [array01], 700 works to change the first element, but indexing with EDI isn't working.


Solution

  • EDI is already a pointer: use mov [edi], ebx.

    mov array01[edi], ebx is adding the pointer in EDI to the address of of the array. That would be correct if EDI was a small number, a byte offset from the start of the array. But it's not in this case; you put a pointer into EDI with mov edi, OFFSET array01 before the loop.

    That address calculation is like C int *ptr = array01; array01[ (int)ptr ] = tmp2; but without scaling by the type width. In discussion in chat, you found the actual address of array01 was 0x007C4000 using the debugger, but the fault address was reported as writing to 0x00F88004. That was the clue to take a more careful look at exactly what the code is doing.

    0x007C4000 * 2 = 0xF88000, the fault address. I don't know where the extra ...4 is coming from. The first store is after the sub edi, 4 so should be with EDI back to its original value. (And memory protection uses 4K page granularity, so if 0xF88004 faulted, 0xF88000 should have faulted in the first iteration, like your debugger is showing you actually happened.)


    So why did it fault on the store but not the load? You used the correct addressing mode for the loads, [edi], just dereferencing the pointer, not adding another address to it!

    At first I was thinking it was odd there was read-only memory at that address, but no, the first access was with the stores.


    BTW, it would be a lot simpler to load and store from [edi] and [edi+4], so your loop only needs one add edi, 8 to advance to the next pair. (You're currently going forward 8 and back by 4 every iteration, for a net advance of one 4-byte element.)

    You'll have to adjust your loop counter to avoid going off the end; it might be easier to work with a pointer to the end of the array and cmp / jb L1against that, instead of calculating an iteration count for the slowloopinstruction. You need to stop even if there's 0 or 1 elements left, like the bottom of ado{... ; ptr+=8;}while(ptr < endp-4);` where endp is a label at the end of the array.


    BTW, the fun way is to use SIMD to load 4x 32-bit elements (2 pairs), shuffle them, and store them back. Since the problem size happens to be 2 pairs in this case, we can drop the loop :P

       movdqu xmm0, xmmword ptr [array01]   ; 16-byte load
       pshufd xmm0, xmm0, 0b10_11_00_01  ; 2-bit indices for 4x 4-byte elements, packed into an 8-bit immediate.
                  ; NASM accepts that syntax for binary literals, MASM probably doesn't
                  ; _mm_shuffle_epi32(vec, _MM_SHUFFLE(2,3, 0,1))
       movdqu xmmword ptr [array01], xmm0   ; 16-byte store
    

    You might need to tell MASM to allow SSE2 vector instructions. C compilers use those by default even in 32-bit code these days. But obviously using a 16-byte shuffle wouldn't teach you to write loops.

    The other fun way is to write 64-bit code and do stuff like ror qword ptr [rdi], 32 to do a 64-bit rotate by 32 to swap halves of a 64-bit chunk of memory.