Расширение из моих комментариев с примером.
Фрагмент, начинающийся с метки updateAndCount
, не должен заканчиваться ret
, он должен прыгать (или проваливаться) туда, где вы хотите, чтобы ваш l oop для продолжения. Однако это не единственная ошибка потока управления. После jnz yourCode
вы, вероятно, захотите безусловный переход к эпилогу функции (который начинается с popad
). Или переместите эпилог вокруг, чтобы следовать непосредственно за jnz yourCode
, а затем позвольте ему провалиться, если jnz
не прыгает.
Кроме того, вы не инициализировали переменную an
в функции, чтобы она Действуйте как переменная stati c в C, то есть если ваша функция вызывается повторно an
будет продолжать увеличиваться и не будет сбрасываться при последующем вызове вашей функции. Это может быть преднамеренным, но вы не указали, какое поведение предполагается. Кроме того, yourCode
является исключительно неописательным ярлыком. Я бы порекомендовал заменить его на .loop
- ведущая точка делает его локальной меткой для NASM . Имя должно описывать намерение этой цели перехода.
Вот пример, исправляющий поток управления, инициализирующий переменную an
и изменяющий метки на описательные и локальные метки. Я не буду ни оптимизировать программу, ни делать ее поточно-ориентированной (что не связано с глобальной переменной).
section .data ; we define (global) initialized variables in .data section
an: dd 0 ; an is a local variable of size double-word, we use it to count the string characters
section .text ; we write code in .text section
global do_Str ; 'global' directive causes the function do_Str(...) to appear in global scope
do_Str: ; do_Str function definition - functions are defined as labels
push ebp ; save Base Pointer (bp) original value
mov ebp, esp ; use Base Pointer to access stack contents (do_Str(...) activation frame)
pushad ; push all signficant registers onto stack (backup registers values)
mov dword [an], 0
;;; continues in the next code block
;;; all the code blocks in this answer combine to one function with one loop
При нулевой инициализации переменная обнуляется. (Я бы использовал and
с нулем лично, что немного короче, чем mov
с нулевым непосредственным числом. Но для ясности мы будем использовать mov
.)
mov ecx, dword [ebp+8] ; get function argument on stack
; now ecx register points to the input string
jmp .first
Этот безусловный переход исправляет другую ошибку logi c. Если самый первый байт в строке является байтом NUL, мы, скорее всего, должны соблюдать его, а не продолжать обрабатывать то, что следует после него.
.loop: ; use label to build a loop for treating the input string characters
cmp byte [ecx], ' '
jne .do_not_updateAndCount
Я инвертировал код условия перехода. Теперь он скачет, если мы не хотим запускать часть .updateAndCount
.
.updateAndCount:
mov byte [ecx], '_'
inc dword [an]
Я сохранил ваш ярлык (теперь локальный с лидирующей точкой), хотя на него нигде нет ссылок. Однако он может служить комментарием к тому, что должен делать этот блок.
Важно, что поток управления проваливается после окончания блока .updateAndCount
здесь. Эквивалентно, вы можете добавить безусловный jmp .next
в конце, чтобы прыгнуть (назад) в l oop. Если вы переместились вокруг блока .updateAndCount
(например, за эпилогом ret
), тогда потребуется jmp .next
.
.do_not_updateAndCount:
.next:
inc ecx ; increment ecx value; now ecx points to the next character of the string
.first:
cmp byte [ecx], 0 ; check if the next character (character = byte) is zero (i.e. null string termination)
jnz .loop ; if not, keep looping until meet null termination character
popad ; restore all previously used registers
mov eax, [an] ; return an (returned values are in eax)
mov esp, ebp ; free function activation frame
pop ebp ; restore Base Pointer previous value (to returnt to the activation frame of main(...))
ret ; returns from do_Str(...) function