我的作业是在 MASM 中更改数组内的值,交换对。
array[0] = array[1]
array[1] = array[0]
array[2] = array[3]
array[3] = array[2]
等等......
我已经放弃使用
var1
和 var2
变量,因为我似乎无法让它们工作。我有
.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
我的问题出在指令 9 上。它给了我一个错误
Assignment_03.exe 中 0x007C1032 处出现未处理的异常:0xC0000005:写入位置 0x00F88004 时发生访问冲突。
从调试器中,我的 EIP =
007C1032
但我的 EDI = 007C4000
我的 &array01
位于 0x007C4000
早期的跑步有
Exception thrown at 0x00171029 in Assignment_03.exe: 0xC0000005: Access violation writing location 0x002E8000.
我已经尝试过这个: https://termspar.wordpress.com/2019/10/12/x86- assembly-swap-array-elements/
mov [numList+ebx],dh
但我仍然遇到同样的错误。
我做错了什么?您能解释一下解决方案吗?需要明确的是,我不想要我的家庭作业的答案。我需要更改数组中的值的帮助。
mov dword ptr [array01], 700
可以更改第一个元素,但使用 EDI 建立索引不起作用。
使用
mov [edi], ebx
。
mov array01[edi], ebx
是将EDI中的指针添加到数组的地址上。如果 EDI 是一个小数字,即距数组开头的字节偏移量,那么这是正确的。但在本例中并非如此,您使用 EDI 作为指针本身,而 mov edi, OFFSET array01
在循环之外。
该地址计算类似于 C
int *ptr = array01;
array01[ (int)ptr ] = tmp2;
,但不按类型宽度进行缩放。在聊天讨论中,您使用调试器发现array01
的实际地址是0x007C4000
,但错误地址报告为写入0x00F88004
。这是更仔细地查看代码到底在做什么的线索。
0x007C4000 * 2 = 0xF88000
。我不知道多余的...4
是从哪里来的。第一个存储位于 sub edi, 4
之后,因此应该使用 EDI 恢复其原始值。 (内存保护使用 4K 页粒度,因此如果 0xF88004 出现故障,0xF88000 应该在第一次迭代中出现故障,就像调试器显示实际发生的情况一样。)
那么为什么它在存储上出错而不是在加载上出错?您对加载使用了正确的寻址模式,
[edi]
,只是取消引用指针,而不是向其添加另一个地址!
起初我认为该地址处有只读内存很奇怪,但事实并非如此,第一次访问是通过存储进行的。
顺便说一句,从
[edi]
和 [edi+4]
加载和存储会简单得多,因此您的循环只需要一个 add edi, 8
即可前进到下一对。 (目前,每次迭代您将前进 8 次并后退 4 次,净前进 1 个 4 字节元素。)
您必须调整循环计数器以避免超出范围;使用指向数组末尾的指针和
cmp /
jb L1against that, instead of calculating an iteration count for the slow
loopinstruction. You need to stop even if there's 0 or 1 elements left, like the bottom of a
do{... ; 可能会更容易ptr+=8;}while(ptr < endp-4);` where endp is a label at the end of the array.
顺便说一句,有趣的方法是使用 SIMD 加载 4x 32 位元素(2 对),对它们进行洗牌,然后将它们存储回来。由于本例中问题大小恰好是 2 对,因此我们可以放弃循环: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
您可能需要告诉 MASM 允许 SSE2 向量指令。如今,即使在 32 位代码中,C 编译器也默认使用它们。但显然使用 16 字节 shuffle 不会教你编写循环。
另一种有趣的方法是编写 64 位代码并执行诸如
ror qword ptr [rdi], 32
之类的操作,将 64 位旋转 32,以交换 64 位内存块的一半。