Ваш код ничего не делает с тем, что вы описываете ... позвольте мне перешагнуть через одну инструкцию с вами, как происходит выполнение (это не исходный код, а последовательная запись инструкций, поскольку они будут выполняться ЦП, с некоторыми минимальнымикомментарии важных частей):
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
Вы должны быть в состоянии проверить это в своем отладчике.Интересно, что заставило вас думать, что "Весь цикл будет выполнен" и "Ничего не появится в консоли" , так как явно выполняется только минимальная часть цикла, и там должно бытьпоявятся две новые строки в консоли.
Как исправить свой код ... сначала добавьте какой-нибудь выход в конец main, если вы создаете исполняемые файлы win32, то я думаю, что стандартный способ выходаПрограммы Irvin32 должны использовать ExitProcess
(проверьте рабочие примеры пакета Irvine32).Я думаю, что-то вроде invoke ExitProcess, 0
может работать, invoke
- это какой-то предопределенный макрос, который расширится до mov + call
инструкций, но у меня нет окон и / или Irvine32, чтобы проверить себя.
Тогдапримите решение, как вы хотите отслеживать петли.Например, поместите внешний счетчик в ecx
, а внутренний счетчик в ebx
, и поскольку вы знаете размер, если он фиксирован, 4x4, сделайте обратный отсчет в стиле do { ..; --counter; } while (counter);
, который естественным образом соответствует ассемблерному способу кодирования (но это не так).t проверять достоверность счетчика на первой итерации, так что это хороший стиль только для случая, когда счетчик всегда является допустимым значением).
Как, например:
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
игнорирует старшие 24 бита eax
и будет использовать только нижние 8 бит («al
»), и вы увеличиваете esi
только на один, так что в итоге это безвредная ошибка,но вы должны понимать, как это работает.mov al,[esi]
будет извлекать из памяти только 8 битов, чего достаточно при работе с символами.