MASM Assembly 中的数组(非常困惑的初学者)
Posted
技术标签:
【中文标题】MASM Assembly 中的数组(非常困惑的初学者)【英文标题】:Arrays in MASM Assembly (very confused beginner) 【发布时间】:2022-01-21 01:19:15 【问题描述】:我有一个非常基本的问题: 你如何在汇编中填充数组?在高级编程语言中,您可以使用 for 循环为每个索引设置一个值,但我不确定如何完成相同的组装。我知道这是错误的,但这就是我所拥有的:
ExitProcess PROTO
.data
warray WORD 1,2,3,4
darray DWORD ?
.code
main PROC
mov edi, OFFSET warray
mov esi, OFFSET darray
mov ecx, LENGTHOF warray
L1:
mov ax, [edi] ;i want to move a number from warray to ax
movzx esi,ax ;i want to move that number into darray...
add edi, TYPE warray ;this points to the next number?
loop L1
call ExitProcess
main ENDP
END
每次循环运行时,ax 都会被数组索引的值覆盖,对吧?相反,我如何使用 warray 中的数组元素填充 darray?任何帮助将不胜感激...我很困惑。
【问题讨论】:
【参考方案1】:填充数组的方法不止一种,而且您的代码几乎可以正常工作。一种方法是在间接地址中使用计数器,这样您就不必在每个循环中修改目标和源数组指针:
ExitProcess PROTO
.data
warray WORD 1,2,3,4
darray DWORD 4 dup (?) ; 4 elements
.code
main PROC
mov edi, OFFSET warray
mov esi, OFFSET darray
xor ecx, ecx ; clear counter
L1:
mov ax, [edi + ecx * 2] ; get number from warray
movzx [esi + ecx * 4], ax ; move number to darray
inc ecx ; increment counter
cmp ecx, LENGTHOF warray
jne L1
call ExitProcess
main ENDP
END
当然,可以修改此代码以向后填充数组,以节省几个字节,就像您在原始代码中可能打算做的那样。这是另一种具有更紧凑循环的方式:
ExitProcess PROTO
.data
warray WORD 1,2,3,4
darray DWORD 4 dup (?) ; 4 elements
.code
main PROC
mov edi, OFFSET warray
mov esi, OFFSET darray
mov ecx, LENGTHOF warray - 1 ; start from end of array
L1:
mov ax, [edi + ecx * 2] ; get number from warray
movzx [esi + ecx * 4], ax ; move number to darray
loop L1
; get and set element zero separately because loop terminates on ecx = 0:
mov ax, [edi]
movzx [esi], ax
call ExitProcess
main ENDP
END
您还应该注意,在使用相同类型的数组时,您可以使用重复前缀和 MOVSD 等指令非常有效地进行简单复制:
ExitProcess PROTO
.data
array1 DWORD 1,2,3,4
array2 DWORD 4 dup (?)
.code
main PROC
mov esi, OFFSET array1 ; source pointer in esi
mov edi, OFFSET array2 ; destination in edi
mov ecx, LENGTHOF array1 ; number of dwords to copy
cld ; clear direction flag so that pointers are increasing
rep movsd ; copy ecx dwords
call ExitProcess
main ENDP
END
【讨论】:
您需要movzx
加载和mov
存储,而不是相反。 movzx
只存在于注册目的地。【参考方案2】:
您可能不“应该知道”这一点,但无论如何,有一条指令和一条指令前缀(早在何时)就是为了做到这一点。
看看这里的微软页面:HERE (click on it)
在该页面上向下滚动,直到找到该短语...
"...这些指令是 x86 的 CISC 遗产的残余,在最近的处理器中实际上比写出的等效指令慢。..."
你所做的是……
将数组的大小放入Ecx
指向Edi
的起点
使用适当的字符串指令来填充它
语法(Masm/Tasm/etc.)可能看起来像这样......
Mov Ecx, The_Length_Of_The_Array ;Figure this out somehow
Lea Edi, The_Target_You_Want_To_Fill ;Define this somewhere
现在,如果您想从一个地方复制到另一个地方,请执行此操作...
Lea Esi, The_Source_You_Want_To_Copy ;Whatever, define it
Cld ;This is the direction flag, make it inc
Rep Movsb ;Movsb means move byte for byte
现在,如果您想在数组中的每个字节中填充相同的值,请执行以下操作...
Mov AL, The_Value_You_Want_To_Stuff ;Define this to your liking
Cld ;This is the direction flag, make it inc
Rep Stosb ;Stosb means store AL into each byte
同样,由于其他人会解释的原因,这些说明不再酷了,如果你使用它们,你会得到一些东西。
还有用于比较的字符串指令,“扫描”、“加载”等。它们曾经非常有用(现在仍然如此,但今天的“现代”帮派不会承认)特别是添加了Rep
前缀。
如果这有帮助,但您需要更多详细信息,请随时询问。
【讨论】:
它们并没有你想象的那么糟糕。它们过去实施得非常糟糕,但在较新的架构 (SNB+) 上,它们实际上非常快。它们还不能完全替代宏指令,但在某些情况下它们的性能优于手写汇编。 @drivingon9 完全同意你的看法。以正确的方式编写它们(我想这是一种荒谬的文字游戏),它们将完成其他指令很少能做到的事情。现在看来,字符串指令通常被标记为不酷。愚蠢的 ?确实 !但是,这就是今天的生活。 丢失了编辑窗口时间。 @drivingon9 加上您的评论,我编辑了我的答案。repne scasb
(memchr) 和 repe cmpsb
(memcmp) 仅对代码大小有用,而不是性能。快速字符串和 ERMSB 微码仅加速 rep stos
和 rep movs
,而不是条件代表搜索指令。它们非常慢,例如在现代 x86(例如 Skylake)上 repe cmpsb
每次比较 2 个周期,因此对于大型阵列(甚至 AVX2)来说,比良好的 SSE2 pcmpeqb
慢大约 32 倍。对于短数组也不是很好;他们有一些启动开销。 lodsd
或 lodsq
没有 rep 偶尔值得在 Haswell 及更高版本上使用(只有 2 微指令,与 mov-load + add 相同),但其他更差以上是关于MASM Assembly 中的数组(非常困惑的初学者)的主要内容,如果未能解决你的问题,请参考以下文章
在 Assembly x86 MASM 中连接字符串和数组的大小