В Turbo Debugger (TD.EXE) F8 «Шаг F8» будет полностью выполнять loop
, пока cx
не станет нулевым (вы даже можете создать бесконечный цикл, обновив cx
вернуться к некоторому значению, не позволяя ему достичь шага 1 -> 0).
Чтобы получить «одношаговый» из инструкции loop
, используйте трассировку F7 F7""- это приведет к тому, что cx
изменится с 6 на 5, а указатель кода будет следовать за переходом назад в начале цикла.
О некоторых других проблемах вашего кода:
MOV SP,STRING_LENGTH
DEC SP
MOV STRING_LENGTH,SP
sp
не является регистром общего назначения, не используйте его для таких расчетов.Всякий раз, когда какая-то инструкция использует стек неявно (push, pop, call, ret, ...
), значения записываются и читаются в области памяти, адресуемой парой регистров ss:sp
, поэтому, манипулируя значением sp
, вы изменяете текущий «стек».
Также в 16-битном реальном режиме x86 все прерывания (клавиатура, таймер, ...), когда они происходят, текущее состояние регистра флага и адрес кода сохраняется в стеке, прежде чем передать управление прерываниюкод обработчика, который обычно помещает дополнительные значения в стек, поэтому все, что находится в памяти по адресам ниже текущего ss:sp
, небезопасно в 16-битном реальном режиме x86, и содержимое памяти продолжает «случайным образом» изменяться там всеми прерываниями, являющимисявыполняется (сам TD.EXE использует часть этой стековой памяти после каждого шага).
Для арифметики используйте другие регистры, а не sp
.Как только вы узнаете достаточно о «стеке», вы поймете, какие манипуляции sp
распространены и почему (например, sub sp,40
в начале функции, которая требует дополнительного «локального» пространства памяти), и как восстановить стек обратно вожидаемое состояние.
Еще одна вещь об этом:
MOV SP,STRING_LENGTH
DEC SP
MOV STRING_LENGTH,SP
STRING_LENGTH
определяется как EQU
, что делает его постоянной времени компиляции и только временем компиляции.Это не «переменная» (выделение памяти), в отличие от таких вещей, как someLabel dw 1345
, из-за которых ассемблер испускает два байта со значениями 0100_0001B, 0000_0101B
(при чтении как 16-битное слово в порядке с прямым порядком байтов, это закодированное значение 1345)и первый байтовый адрес имеет символическое имя someLabel
, которое можно использовать в дальнейших инструкциях, например dec word ptr [someLabel]
, для уменьшения этого значения в памяти с 1345 до 1344 во время выполнения.
Но EQU
отличается, он присваивает символу STRING_LENGTH
окончательное значение, например 14.
Таким образом, ваш код можно прочитать как:
mov sp,14 ; makes almost sense, (practically destroys stack setup)
dec sp ; still valid
mov 14,sp ; doesn't make any sense, constant can't be destination for MOV