Реклама:

Основное назначение этого подраздела - показать механизм обработки повторяющихся строковых команд. В листингах В.5 и В.6 представлены простые программы для обработки символьных строк, strngcpy.s и reverspr.s, расположенные в папке examples.

Листинг В.5. Копирование строки (strngcpy.s)

.SECT .TEXT

stcstart: ! 1

PUSH mesgl ! 2

PUSH mesg2 ! 3

CALL strngcpy ! 4

ADD SP.4 ! 5

PUSH 0 ! 6

PUSH 1 ! 7

SYS ! 8

strngcpy: ! 9

PUSH CX ! 10

PUSH SI ! 11

PUSH DI ! 12

PUSH BP ! 13

MOV BP.SP ! 14

MOV AX.O ! 15

MOV DI.IO(BP) ! 16

MOV CX,- 1 ! 17

REPNZ SCASB ! 18

NEG CX ! 19

DEC CX ! 20

MOV SI.IO(BP) ! 21

MOV DI.12CBP) ! 22

PUSH DI ! 23

REP MOVSB ! 24

CALL stringpr ! 25

MOV SP.BP ! 26

POP BP ! 27

POP DI ! 28

POP SI ! 29

POP CX ! 30

RET ! 31

.SECT .DATA ! 32 mesgl: .ASCIZ "Have a look\n" ! 33

mesg2: .ASCIZ "qrstNn" ! 34 .SECT .BSS

Листинг В.6- Вывод символьных строк задом наперед (reverspr.s)

finclude "../syscalnr.h" ! 1

start: MOV Dl.str ! 2

PUSH AX ! 3

MOV BP.SP ! 4

PUSH _PUTCHAR ! 5

MOVB AL,'\n' ! 6

MOV СХ.-1 ! 7

REPNZ SCASB ! 8

NEG CX ! 9

STD ! 10

DEC CX ! 11

SUB DI.2 ! 12

MOV SI.DI ! 13

1: LODSB ! 14

MOV (BP).AX ! 15

SYS ! 16

LOOP lb ! 17

MOVB (BP),'\n' ! 18

SYS ! 19

PUSH 0 ! 20

PUSH JXIT ! 21

SYS ! 22

.SECT .DATA ! 23

str: .ASCIZ "reverse\n" ! 24

В листинге В.5 представлена программа копирования строки. Она вызывает подпрограмму stringpr, которую также можно найти в отдельном файле БЫпдрг.Б (листинга с этим файлом в нашем приложении нет). Чтобы ассемблировать программу, содержащую подпрограммы в отдельных файлах, в команде а$88 все исходные файлы следует указать, начиная с основной программы, которая определяет имена исполняемого и вспомогательных файлов. К примеру, чтобы ассемблировать программу, представленную в листинге В.5, нужно ввести команду аБ88 э^пдсру.Б э^пдрг.з

Программа из листинга В.6 выводит символьные строки с обратным порядком следования символов. Мы рассмотрим обе программы по очереди.

В листинге В.5 строки кода пронумерованы, начиная с первой метки. Основная программа (строки 2-8) начинается с вызова подпрограммы Б^пдсру с двумя аргументами: исходной строкой те$д2 и целевой строкой те$д1; подпрограмма копирует содержимое первой во вторую.

Теперь рассмотрим непосредственно подпрограмму Б^пдсру, которая начинается в строке 9 кода. Она рассчитана на то, что адреса целевого буфера и исходной строки вводятся в стек непосредственно перед ее вызовом. В строках 10-13 задействованные регистры сохраняются путем передачи их значений в стек; впоследствии, в строках 27-30, их предполагается восстановить. В строке 14 значения БР и ВР копируются стандартным методом. После этого в ВР можно загружать аргументы. В строке 26 стек очищается уже знакомым нам способом - значение ВР копируется в БР.

Центральным местом подпрограммы является команда REP M0VSB, расположенная в строке 24 кода. Команда M0VSB перемещает байт, на который указывает регистр SI, по адресу памяти, определяемому регистром DI. После этого содержимое обоих регистров увеличивается на единицу. Команда REP формирует цикл, в котором выполняется команда M0VSB; после перемещения каждого байта она уменьшает значение СХ на 1. Цикл завершается при достижении СХ нулевого значения.

Перед выполнением REP M0VSB необходимо подготовить регистры, что и делается в строках 15-22. Индекс источника, SI, копируется из аргумента в стек в строке 21; индекс приемника, DI, определяется в строке 22. Получить значение СХ несколько сложнее. Следует иметь в виду, что конец символьной строки обозначается нулевым байтом. Команда M0VSB не влияет на состояние нулевого флага, в отличие от команды SCASB (просмотр байтовой строки). Последняя сравнивает значение, на которое указывает DI, со значением AL, и выполняет приращение DI "на лету". Подобно M0VSB, она относится к числу повторяющихся команд. В строке 15 очищаются регистры АХ и A L, в строке 16 из стека выбирается указатель на DI, а в строке 17 регистр СХ инициализируется значением -1. В строке 18 находится команда REPNZ SCASB, выполняющая операцию сравнения в контексте цикла и в случае равенства устанавливающая нулевой флаг. При каждом проходе цикла выполняется отрицательное приращение СХ, а при установлении нулевого флага цикл останавливается - команда REPNZ проверяет и нулевой флаг, и СХ. Количество проходов цикла с командой M0VSB, таким образом, определяется как разность текущего значения СХ и предыдущего значения -1 (строки 19-20).

Использовать две повторяющиеся команды не слишком изящно, но такова цена проектного решения, основывающегося на принципе независимости кодов условий от команд перемещения. При выполнении циклов проводится приращение индексных регистров, и для этих целей флаг направления должен быть сброшен.

В строках 23 и 25 кода скопированная символьная строка выводится при помощи подпрограммы stringpr, имеющейся в папке examples. Она достаточно проста, поэтому обсуждать ее здесь мы не будем.

В программе вывода символьных строк в обратном порядке, показанной в листинге В.6, строка 1 кода содержит все стандартные номера системных вызовов. В строке 3 в стек помещается фиктивное значение, а в строке 4 указатель базы (BP) начинает указывать на текущую вершину стека. Эта программа выводит ASCII-символы по одному, а потому в стек вводится численное значение PUTCHAR. Обратите внимание: BP указывает на символ, который предполагается отобразить во время вызова SYS.

В строках 2, 6 и 7 регистры DI, AL и СХ подготавливаются к выполнению повторяющейся команды SCASB. Регистр счетчика и целевой индекс загружаются так же, как это происходит в программе копирования строки, за тем лишь исключением, что в регистр AL вместо нуля помещается символ новой строки. Таким образом, команда SCASB сравнивает значения символов строки str с \п, а не с нулем, и в случае соответствия устанавливает нулевой бит.

Команда REP SCASB выполняет приращение регистра DI, так что после совпадения целевой индекс указывает на символ нуля, следующий за символом новой строки. В строке 12 выполняется отрицательное приращение DI на 2; в результате этот регистр указывает на последнюю букву слова.

Если сканирование символьной строки идет в обратном порядке, а отображается она посимвольно, значит, наша задача решена; в таком случае флаг направления в строке 10 переустанавливается, и начинается обратная регулировка индексных регистров в строковых командах. Теперь команда L0DSB в строке кода 14 копирует символ в AL, а в строке 15 этот символ помещается в стек рядом с PUTCHAR, что позволяет команде SYS отобразить его.

Команды, находящиеся в строках 18 и 19, выводят новую строку, и программа традиционно закрывается вызовом EXIT.

В текущей версии программы, впрочем, содержится ошибка. Ее можно найти путем пошаговой трассировки.

Команда /str помещает строку str в поле данных трассера. Так как числовое значение адреса данных известно, мы можем выяснить, как меняются значения в индексных регистрах в отношении положения символьной строки.

Ошибка, впрочем, обнаруживается только после многократного нажатия клавиши возврата каретки. Команды трассера помогают сократить время локализации ошибки. Запустите программу трассировки и введите команду 13, которая переместит нас в середину цикла. Далее с помощью команды b установим контрольную точку в строке 15. Создав две новые строки, мы увидим, что в поле вывода отображается последняя буква "е". Запустив команду г, мы заставим программу трассировки работать вплоть до следующей контрольной точки или до завершения процесса. Таким образом, последовательно запуская команду г, можно видеть все буквы, пока мы не подберемся к решению проблемы. С этого момента программу трассировки можно будет перевести в пошаговый режим и проанализировать происходящее при выполнении важнейших команд.

Мы также можем установить дополнительную контрольную точку в той или иной строке кода, но при этом нужно учитывать включение файла syscalnr.h, приводящее к смещению номеров строк кода на 20. Следовательно, чтобы установить контрольную точку, скажем, в строке 16, нужно ввести команду 36Ь. Впрочем, от этого неуклюжего способа лучше отказаться в пользу установки глобальной метки start в строке 2 перед расположенной в ней командой - тогда контрольную точку в нужной строке кода можно будет установить командой

/start + 14b

К тому же такой способ установки контрольной точки позволяет не обращать внимания на размер включаемого файла.

Отладка программы вывода массива || Оглавление || Таблицы диспетчеризации