СБОРКА: проблема с вложенным циклом

Я работаю с вложенными циклами в сборке. Желаемый вывод должен быть "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

1 ответ

Решение

Ваш код ничего не делает с тем, что вы описываете... позвольте мне перешагнуть через одну инструкцию с вами, как происходит выполнение (это не исходный код, а последовательная запись инструкций, поскольку они будут выполняться ЦП, с некоторыми минимальными комментариями важных части):

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); стиль, который естественным образом соответствует ассемблерному способу кодирования (но он не проверяет достоверность счетчика на первой итерации, поэтому это хороший стиль только для случая, когда счетчик всегда является допустимым значением)

Как например:

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 бит из памяти, что достаточно в этом случае при работе с символами.

Другие вопросы по тегам