如何打印索引位置 x86 程序集
How to print index position x86 Assemby
我是 Assembly 的新手,正在尝试完成作业,我想知道如何打印数组的索引位置而不是索引处的值,我使用 ESI 作为指针。这里数组充满了 0 和 1,我只想打印 1 的索引。
示例数组 [1|1|0|0|1]
PRINT:
mov eax,[esi]
cmp eax,1
je Show
add esi,4
loop PRINT
Show:
call WriteDec
call Crlf
loop Show
输出应为 (1 2 5) 或 (0 1 4)。
谢谢
要么添加一个计数器:
mov ebx,1 ; index counter, first element = 1
PRINT:
mov eax,[esi]
cmp eax,1
jne noShow
mov eax,ebx
call WriteDec
call Crlf
noShow:
add esi,4
inc ebx ; increase counter
loop PRINT
或从 ESI 计算(假设 ESI = ARRAY + 4* Index)
PRINT:
mov eax,[esi]
cmp eax,1
jne noShow
mov eax, esi ; load position in index
sub eax, ARRAY ; remove starting position of the array
shr eax, 2 ; divide it by 4, to get index position
call WriteDec
call Crlf
noShow:
add esi,4
loop PRINT
(PS: 修正循环逻辑,问题中错误)
(PSS: 我无法测试代码 atm,我在工作 )
你必须从@MargaretBloom 的想法开始,数数,还有一些算法中的其他错误需要修复:
- 你有两个
loop
,问题是,当第一个loop
结束时,执行下一个代码块,再次打印eax
,开始死循环.
- 你的第二个
loop
跳转到 Show
但它应该跳转到 PRINT
.
- 您在
je Show
之后递增 esi
,因此当找到 1
的值时索引不会递增,应该递增 esi
之前.
让我们解决那些小问题,我将使用edi
作为您要显示的位置(您可以使用任何其他寄存器):
mov edi, 0 ;◄■■ THIS IS THE POSITION YOU WANT TO DISPLAY (1,2,3,...).
PRINT:
mov eax,[esi]
add esi,4 ;◄■■ INCREMENT INDEX HERE.
inc edi ;◄■■ INCREASE POSITION (1,2,3,...).
cmp eax,1
je Show
loop PRINT
jmp Finish ;◄■■ SKIP NEXT BLOCK WHEN FINISH.
Show:
mov eax, edi ;◄■■ DISPLAY POSITION.
call WriteDec
call Crlf
loop PRINT ;◄■■ JUMP TO PRINT, NOT TO SHOW.
Finish:
how to ... index position of an array ... ESI as pointer
首先数组的定义很重要,如果它是由连续元素组成的简单平面数组,那么要获得地址指向的特定元素的索引EDI
你可以从涉及的两个指针反向计算它(这需要原始的数组起始指针仍在 ESI
!):
mov eax,edi
sub eax,esi ; eax = (element_pointer - array_pointer)
; divide eax by <size_of_element>
; for example your OP code example suggest
; the array element size is DWORD = 4B
; then the division/4 can be done simply:
; shr eax,2
; for byte arrays there's no need to divide the address
; difference, eax already contains index, as BYTE size = 1B
; for other element sizes, which are *not* power-of-two
; (you can't divide the difference by simply shifting it right)
; it may be more efficient to address them through separate index
; or do the: imul/mul (1/el_size) || idiv/div el_size
; (when there's no way to avoid it)
; after division the eax contains 0, 1, ... index.
如果元素的大小是非平凡的(不是二次方),或者结构不是平凡的(链表所以两个指针的差异与元素的索引不相关),你可能想要单独计算索引。仍然要避免每次提取的索引 mul element_size
可能值得将两者混合,因此通过指针寻址,并分别计算(对提取无用)索引,这将仅用于需要索引的地方。
此外,在此变体中,您可以从 1
建立索引,但我会避免这种情况,除非它是非程序员的一些人工输出,因为大多数 ASM/C/C++ 程序员自然希望建立索引从 0
开始(由于指针数学在我的第一个示例中的工作原理)。
; stolen from Toommylee2k, then modified to focus on my explanation
xor ebx,ebx ; first index will be 1 (!)
; so I initialized ebx = 1 - 1 = 0, because
; I will increment it at beginning of loop
; for indexing from 0 the ebx should be initialized to -1
loop_start:
; update index first, so you can't miss it when branching later
lea ebx,[ebx+1] ; ebx = ebx+1 without flags modification
; since *here* "ebx" works as "index", contains "1" for first item
; do whatever you want with pointers, like "esi" in your sample code
; ...
; move to next element of array (avoids multiplication)
add esi,size_of_element
; although `imul` on modern CPU will perform quite close to `add`
; so when multiplication is unavoidable, go for it.
; the division on the other hand still costs very likely more
; than having separate register/variable for index counting
; loop till ecx is zero (in much faster way than "loop")
dec ecx
jnz loop_start
; "loop" is very slow due to historic reasons, to improve compatibility
最后的扩展,当元素大小为[1, 2, 4, 8]之一时,您可以使用扩展的x86寻址模式来使用“0, 1, ...”索引而不是纯指针:
mov ebx,7 ; index "7" = 8th element of array
lea esi,[array_of_words] ; array pointer
; addressing through index, supported directly by CPU for size 2
mov ax,[esi + ebx*2] ; complex x86 addressing allows this
; here ax = 8
...
.data
array_of_words:
dw 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
如果您在循环中经常使用索引,这可能是最佳解决方案。如果您很少需要元素的索引,那么纯指针通常更优。
我是 Assembly 的新手,正在尝试完成作业,我想知道如何打印数组的索引位置而不是索引处的值,我使用 ESI 作为指针。这里数组充满了 0 和 1,我只想打印 1 的索引。 示例数组 [1|1|0|0|1]
PRINT:
mov eax,[esi]
cmp eax,1
je Show
add esi,4
loop PRINT
Show:
call WriteDec
call Crlf
loop Show
输出应为 (1 2 5) 或 (0 1 4)。 谢谢
要么添加一个计数器:
mov ebx,1 ; index counter, first element = 1
PRINT:
mov eax,[esi]
cmp eax,1
jne noShow
mov eax,ebx
call WriteDec
call Crlf
noShow:
add esi,4
inc ebx ; increase counter
loop PRINT
或从 ESI 计算(假设 ESI = ARRAY + 4* Index)
PRINT:
mov eax,[esi]
cmp eax,1
jne noShow
mov eax, esi ; load position in index
sub eax, ARRAY ; remove starting position of the array
shr eax, 2 ; divide it by 4, to get index position
call WriteDec
call Crlf
noShow:
add esi,4
loop PRINT
(PS: 修正循环逻辑,问题中错误)
(PSS: 我无法测试代码 atm,我在工作 )
你必须从@MargaretBloom 的想法开始,数数,还有一些算法中的其他错误需要修复:
- 你有两个
loop
,问题是,当第一个loop
结束时,执行下一个代码块,再次打印eax
,开始死循环. - 你的第二个
loop
跳转到Show
但它应该跳转到PRINT
. - 您在
je Show
之后递增esi
,因此当找到1
的值时索引不会递增,应该递增esi
之前.
让我们解决那些小问题,我将使用edi
作为您要显示的位置(您可以使用任何其他寄存器):
mov edi, 0 ;◄■■ THIS IS THE POSITION YOU WANT TO DISPLAY (1,2,3,...).
PRINT:
mov eax,[esi]
add esi,4 ;◄■■ INCREMENT INDEX HERE.
inc edi ;◄■■ INCREASE POSITION (1,2,3,...).
cmp eax,1
je Show
loop PRINT
jmp Finish ;◄■■ SKIP NEXT BLOCK WHEN FINISH.
Show:
mov eax, edi ;◄■■ DISPLAY POSITION.
call WriteDec
call Crlf
loop PRINT ;◄■■ JUMP TO PRINT, NOT TO SHOW.
Finish:
how to ... index position of an array ... ESI as pointer
首先数组的定义很重要,如果它是由连续元素组成的简单平面数组,那么要获得地址指向的特定元素的索引EDI
你可以从涉及的两个指针反向计算它(这需要原始的数组起始指针仍在 ESI
!):
mov eax,edi
sub eax,esi ; eax = (element_pointer - array_pointer)
; divide eax by <size_of_element>
; for example your OP code example suggest
; the array element size is DWORD = 4B
; then the division/4 can be done simply:
; shr eax,2
; for byte arrays there's no need to divide the address
; difference, eax already contains index, as BYTE size = 1B
; for other element sizes, which are *not* power-of-two
; (you can't divide the difference by simply shifting it right)
; it may be more efficient to address them through separate index
; or do the: imul/mul (1/el_size) || idiv/div el_size
; (when there's no way to avoid it)
; after division the eax contains 0, 1, ... index.
如果元素的大小是非平凡的(不是二次方),或者结构不是平凡的(链表所以两个指针的差异与元素的索引不相关),你可能想要单独计算索引。仍然要避免每次提取的索引 mul element_size
可能值得将两者混合,因此通过指针寻址,并分别计算(对提取无用)索引,这将仅用于需要索引的地方。
此外,在此变体中,您可以从 1
建立索引,但我会避免这种情况,除非它是非程序员的一些人工输出,因为大多数 ASM/C/C++ 程序员自然希望建立索引从 0
开始(由于指针数学在我的第一个示例中的工作原理)。
; stolen from Toommylee2k, then modified to focus on my explanation
xor ebx,ebx ; first index will be 1 (!)
; so I initialized ebx = 1 - 1 = 0, because
; I will increment it at beginning of loop
; for indexing from 0 the ebx should be initialized to -1
loop_start:
; update index first, so you can't miss it when branching later
lea ebx,[ebx+1] ; ebx = ebx+1 without flags modification
; since *here* "ebx" works as "index", contains "1" for first item
; do whatever you want with pointers, like "esi" in your sample code
; ...
; move to next element of array (avoids multiplication)
add esi,size_of_element
; although `imul` on modern CPU will perform quite close to `add`
; so when multiplication is unavoidable, go for it.
; the division on the other hand still costs very likely more
; than having separate register/variable for index counting
; loop till ecx is zero (in much faster way than "loop")
dec ecx
jnz loop_start
; "loop" is very slow due to historic reasons, to improve compatibility
最后的扩展,当元素大小为[1, 2, 4, 8]之一时,您可以使用扩展的x86寻址模式来使用“0, 1, ...”索引而不是纯指针:
mov ebx,7 ; index "7" = 8th element of array
lea esi,[array_of_words] ; array pointer
; addressing through index, supported directly by CPU for size 2
mov ax,[esi + ebx*2] ; complex x86 addressing allows this
; here ax = 8
...
.data
array_of_words:
dw 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
如果您在循环中经常使用索引,这可能是最佳解决方案。如果您很少需要元素的索引,那么纯指针通常更优。