ASSEMBLY:嵌套循环问题
ASSEMBLY: issue with nested loop
我在汇编中使用嵌套循环。所需的输出应该是 "ABCD" (新行) "EFGH" (新行) "HIJK" (新行) "LMNO" 但目前我没有得到任何输出。整个循环将执行,但控制台中不会显示任何内容。
INCLUDE Irvine32.inc
.data
ALPHA byte "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z"
count byte 4 ; inner loop counter
.code
main proc
mov esi, OFFSET ALPHA
mov ecx, 4 ;outer loop counter
mov ebx, 0 ; inner counter
call part1
main endp
;-----------------------------------------------------------------------------
;part1 proc
;print 4x4 letter matric
;Receives EBX: arr address ECX: outer loop counter
;Returns EAX: full array
;-----------------------------------------------------------------------------
part1 PROC USES ebx ecx edx esi
L1:
cmp ecx, 4 ; test outer loop count
je next ; yes?
call Crlf ; print new line
inc ecx
L2: ;inner loop
cmp ebx, ecx ;
mov eax, [esi] ; mov next letter
call WriteChar ; write it
inc esi ; point to the next element
inc ebx
loop L2 ; loop
next:
call Crlf
ret
part1 ENDP
end main
你的代码没有做你描述的任何事情..让我和你一起跨过单条指令,执行是如何发生的(这不是源代码,而是指令的顺序写下,因为它们将由 CPU,对重要部分进行一些最少的评论):
mov esi, OFFSET ALPHA
mov ecx, 4
mov ebx, 0
call part1
cmp ecx, 4
je next ; jump is taken, because ecx == 4
call Crlf
... internal implementation of Crlf by Irvine32 ending with RET
ret ; part1 "ret" returning into main
; there is no more code in main, so the CPU continues
; with next memory interpreting it as machine code
; with a bit of luck (no padding between ENDP/PROC, or NOPs)
; the "part1" code follows after main, so it will get executed
cmp ecx, 4
je next ; jump is taken, because ecx == 4
call Crlf
... internal implementation of Crlf by Irvine32 ending with RET
ret ; part1 "ret" returning out of your code completely
; in some environments that may work as clean exit from app
; in others that ret will go to whatever address is at stack and crash
您应该能够在调试器中验证这一点。我想知道是什么让您认为 "The whole loop will execute" 和 "nothing will appear in the console",显然只有循环的最小部分正在执行,控制台中应该出现两条新行。
如何修复您的代码...首先在 main 的末尾添加某种出口,如果您正在构建 win32 可执行文件,那么我认为退出 Irvin32 程序的标准方法是使用 ExitProcess
(检查 Irvine32 包的工作示例)。我认为 invoke ExitProcess, 0
之类的东西可能会起作用,invoke
是某种预定义的宏,它将扩展为 mov + call
指令,但我没有 windows and/or Irvine32 来检查一下自己。
然后考虑一下您实际想要如何跟踪循环。例如将外部计数器放入 ecx
并将内部计数器放入 ebx
,并且因为您知道固定 4x4 时的大小,所以以 do { ..; --counter; } while (counter);
样式进行倒计时,这适合汇编编码方式自然地(但它不会在第一次迭代时检查计数器的有效性,所以这仅适用于计数器始终为有效值的情况)。
例如:
part1 PROC USES eax ebx ecx esi
mov ecx, 4
outerLoop:
mov ebx, 4 ; reset inner counter (for every outer loop)
innerLoop:
mov al, [esi] ; load next letter
call WriteChar
inc esi ; point to next element
dec ebx ; --innerCounter
jnz innerLoop
call Crlf ; print new line after 4 chars
dec ecx ; --outerCounter
jnz outerLoop
ret
part1 ENDP
现在这应该会在控制台中产生如下内容:
A,B,
C,D,
E,F,
G,H,
那是因为您确实将 ALPHA
定义为一个长字符串,包括逗号等...您可能确实想要
ALPHA byte 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P'
或等价物
ALPHA byte "ABCDEFGHIJKLMNOP"
它们都产生 16 个字节,值为 65、66、67,... ('A' == 65
)。
同样在您的原始代码中:
mov eax, [esi] ; mov next letter
将加载四个字节。 WriteChar
确实忽略了 eax
的高 24 位,并且将仅使用低 8 位(“al
”),并且您只将 esi
递增 1,所以最后这是一个无害的错误,但你应该了解它是如何工作的。 mov al,[esi]
只会从内存中获取 8 位,这在这种情况下就足够了,当处理字符时。
我在汇编中使用嵌套循环。所需的输出应该是 "ABCD" (新行) "EFGH" (新行) "HIJK" (新行) "LMNO" 但目前我没有得到任何输出。整个循环将执行,但控制台中不会显示任何内容。
INCLUDE Irvine32.inc
.data
ALPHA byte "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z"
count byte 4 ; inner loop counter
.code
main proc
mov esi, OFFSET ALPHA
mov ecx, 4 ;outer loop counter
mov ebx, 0 ; inner counter
call part1
main endp
;-----------------------------------------------------------------------------
;part1 proc
;print 4x4 letter matric
;Receives EBX: arr address ECX: outer loop counter
;Returns EAX: full array
;-----------------------------------------------------------------------------
part1 PROC USES ebx ecx edx esi
L1:
cmp ecx, 4 ; test outer loop count
je next ; yes?
call Crlf ; print new line
inc ecx
L2: ;inner loop
cmp ebx, ecx ;
mov eax, [esi] ; mov next letter
call WriteChar ; write it
inc esi ; point to the next element
inc ebx
loop L2 ; loop
next:
call Crlf
ret
part1 ENDP
end main
你的代码没有做你描述的任何事情..让我和你一起跨过单条指令,执行是如何发生的(这不是源代码,而是指令的顺序写下,因为它们将由 CPU,对重要部分进行一些最少的评论):
mov esi, OFFSET ALPHA
mov ecx, 4
mov ebx, 0
call part1
cmp ecx, 4
je next ; jump is taken, because ecx == 4
call Crlf
... internal implementation of Crlf by Irvine32 ending with RET
ret ; part1 "ret" returning into main
; there is no more code in main, so the CPU continues
; with next memory interpreting it as machine code
; with a bit of luck (no padding between ENDP/PROC, or NOPs)
; the "part1" code follows after main, so it will get executed
cmp ecx, 4
je next ; jump is taken, because ecx == 4
call Crlf
... internal implementation of Crlf by Irvine32 ending with RET
ret ; part1 "ret" returning out of your code completely
; in some environments that may work as clean exit from app
; in others that ret will go to whatever address is at stack and crash
您应该能够在调试器中验证这一点。我想知道是什么让您认为 "The whole loop will execute" 和 "nothing will appear in the console",显然只有循环的最小部分正在执行,控制台中应该出现两条新行。
如何修复您的代码...首先在 main 的末尾添加某种出口,如果您正在构建 win32 可执行文件,那么我认为退出 Irvin32 程序的标准方法是使用 ExitProcess
(检查 Irvine32 包的工作示例)。我认为 invoke ExitProcess, 0
之类的东西可能会起作用,invoke
是某种预定义的宏,它将扩展为 mov + call
指令,但我没有 windows and/or Irvine32 来检查一下自己。
然后考虑一下您实际想要如何跟踪循环。例如将外部计数器放入 ecx
并将内部计数器放入 ebx
,并且因为您知道固定 4x4 时的大小,所以以 do { ..; --counter; } while (counter);
样式进行倒计时,这适合汇编编码方式自然地(但它不会在第一次迭代时检查计数器的有效性,所以这仅适用于计数器始终为有效值的情况)。
例如:
part1 PROC USES eax ebx ecx esi
mov ecx, 4
outerLoop:
mov ebx, 4 ; reset inner counter (for every outer loop)
innerLoop:
mov al, [esi] ; load next letter
call WriteChar
inc esi ; point to next element
dec ebx ; --innerCounter
jnz innerLoop
call Crlf ; print new line after 4 chars
dec ecx ; --outerCounter
jnz outerLoop
ret
part1 ENDP
现在这应该会在控制台中产生如下内容:
A,B,
C,D,
E,F,
G,H,
那是因为您确实将 ALPHA
定义为一个长字符串,包括逗号等...您可能确实想要
ALPHA byte 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P'
或等价物
ALPHA byte "ABCDEFGHIJKLMNOP"
它们都产生 16 个字节,值为 65、66、67,... ('A' == 65
)。
同样在您的原始代码中:
mov eax, [esi] ; mov next letter
将加载四个字节。 WriteChar
确实忽略了 eax
的高 24 位,并且将仅使用低 8 位(“al
”),并且您只将 esi
递增 1,所以最后这是一个无害的错误,但你应该了解它是如何工作的。 mov al,[esi]
只会从内存中获取 8 位,这在这种情况下就足够了,当处理字符时。