А. Ю. Каргашина и А. С. Миркотан под редакцией > Ю. М. Баяковского

Вид материалаКнига

Содержание


Приложение A. ODT
Работа ODT
Run test
Глобальные имена
Globl start
Globl subrtn
Отладка и исправление программы
MEM имеет адрес 162
MEM имеет относительный адрес 162
Bne label
001046, что приводит к нужному результату и закрывает слово. Заметьте, что набор на терминале просто 46
Jsr pc,print
0,162/ выдаст на терминал 041101
Подобный материал:
1   ...   16   17   18   19   20   21   22   23   ...   27

Приложение A. ODT


ODT — это комплекс отладки в режиме on line (On line Debugging Technique), входящий в состав операционных систем DEC PDP-11. Его реализация, как и реализация любой другой системной программы, зависит от принятой версии операционной системы. Однако основные возможности ODT остаются в большой мере неизменными, несмотря на различия в формате команд.

Работа ODT


Как правило, программа ODT поставляется в виде перемещаемого двоичного файла ODT.OBJ. Он должен быть скомпонован с отлаживаемой программой. Допустим, что нам нужно отладить программу TEST.MAC посредством ODT. Сначала ее надо оттранслировать, а потом при помощи компоновщика скомпоновать перемещаемый модуль TEST.OBJ с ODT:

TEST,TEST=ODT,TEST

Эта команда указывает компоновщику на два входных файла. Он связывает их друг с другом и создает два выходных файла: TEST.SAV и TEST.MAP. Имейте в виду, что оба эти файла представляют собой снимок оперативной памяти программы TEST вместе с ODT. Поэтому, если нужно сохранить образ памяти о одной лишь вашей программой, то при компоновке вы можете просто задать различные имена для результирующих файлов. В зависимости от компоновщика может оказаться необходимым, чтобы в списке компонуемых файлов файл с ODT был расположен первым, как это и показано выше. Причина этого заключается в следующем: после того как системная команда

RUN TEST

загрузит файлы в память, она обязательно осуществит переход на начальный адрес программы отладки, а не на вход программы TEST. Когда это произойдет, отладчик напечатает краткое сообщение о номере своей версии и затем символ *, означающий, что он готов принимать указания.

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

Глобальные имена


Один из способов использования комплекса ODT заключается в прослеживании на различных стадиях вычислительного процесса изменений содержимого некоторых особенно важных ячеек. В команды отладчику должны при этом входить не имена ячеек, присвоенные пользователем, а значения их адресов. Так, мы не можем заглянуть в ячейку с меткой MEM, пока нам не известно, куда она была загружена. В файле .MAP не содержится подобной информации; по нему даже нельзя определить начальный адрес отлаживаемой программы, поскольку она теперь лишь часть программного комплекса, точка входа в который совпадает с адресом запуска отладчика.

Можно, однако, побудить компоновщик при создании файла .MAP указать начальный адрес нашей программы. Это достигается путем объявления имени глобальным (т.е. доступным вне программы) с помощью директивы

.GLOBL START

...

START: ...

Такая директива необходима также при независимой трансляции различных подпрограмм и затем последующего объединения их компоновщиком. Предположим, что нам нужно скомпоновать два файла MAIN.MAC и SUB.MAC после того, как они будут оттранслированы, и что файл SUB.MAC содержит подпрограмму SUBRTN. Если в первом файле стоит вызов подпрограммы, то они оба должны содержать описание .GLOBL SUBRTN.

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

Отладка и исправление программы


Перед тем как компоновать программу о ODT, нужно объявить ее адрес запуска глобальным. Кроме того, следует вооружиться листингом своей программы (без ODT).

После компоновки отлаживаемой программы с ODT выйдите из компоновщика и посмотрите в файл .MAP. Запомните значение начального адреса по карте загрузки. Потом запустите сформированную программу, чтобы в результате управление перешло к отладчику и он напечатал символ *.

Допустим, к примеру, что по листингу ячейка MEM имеет адрес 162. Его значение, конечно, берется относительно начала программы. Поэтому если в файле .MAP указано, что начальный адрес загрузки равен 6316, то при работе с ODT к ячейке MEM можно адресоваться по 6500 (=6316+162).

Так как утомительно каждый раз вручную выполнять подобные расчеты, то первой инструкцией отладчику необходимо задать базовый адрес, относительно которого рассматриваются все последующие адреса. В ODT имеется набор из восьми регистров смещения, пронумерованных от 0 до 7. Это позволяет устанавливать базовые адреса не менее чем восьми независимым программным модулям. Нам же нужно задать только одну константу (6316) для всей программы, поэтому в ответ на приглашение отладчика набираем 6316; R (без  в конце), что приводит к занесению числа 6316 в регистр 0. Отладчик ответит новым приглашением *. В дальнейшем при наборе на терминале любого адреса будем ставить перед ним 0, отмечая тем самым, что он берется относительно содержимого нулевого регистра. Чтобы передать требуемый адрес отладчику, всегда нужно указывать номер используемого вами регистра, как видно из приведенных ниже примеров.

Теперь можно передавать отладчику в точности те адреса, которые имеются в листинге трансляции, что очень удобно. Пусть по относительному адресу 72 в нашей программе стоит команда CLR R1. Если мы наберем 0,72/(здесь также не нужен ), ODT на той же строке ответит: 005001. Символ / указывает ODT на слово, адрес которого был только что набран, и требует, чтобы он выдал его содержимое. Важно понимать, что, прежде чем сделать это, отладчик выполнит две операции: он сместит указатель текущей ячейки или курсор в относительный адрес 72 и откроет слово по этому адресу.

Если ячейка была хоть раз открыта, ее содержимое можно изменить. Предположим, что команда в нашем примере, как оказалось, должна быть другой: CLR @R1. Тогда после вывода отладчиком кода 005001 нужно набрать новое значение, т.е. 005011, и на этот раз закончить строку возвратом каретки . Символ  сообщает отладчику о том, что нужно закрыть слово, записав в него новое содержимое. Если же изменять ничего не требуется, то просто нажмите . Поскольку нового содержимого задано не было, ODT закроет слово, оставив в нем все по-старому.

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

Если вы ошиблись во время набора очередной команды, то клавишей RUBOUT нельзя стереть неверный символ. Нажатие этой клавиши не противозаконно, но оно приведет к игнорированию отладчиком всей команды и появлению символа *. В результате придется ввести всю команду повторно.

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

После изменения содержимого ячейки (если оно было необходимо) ее можно закрыть не только возвратом каретки  но также вводом любого из символов: ↓(LINE FEED), , ←, @, > или <. На некоторых терминалах литере ← соответствует литера «подчеркивание».

При построчном просмотре программы наиболее целесообразным представляется использование клавиши LINE FEED, так как ее нажатие приводит не только к закрытию текущей ячейки, но и к сдвигу курсора на следующую, открытию ее и выводу на пульт ее содержимого. Пусть, например, после команды CLR R1, расположенной по адресу 72, следует команда MOV R3,MEM. Закрытие ячейки 72 при помощи ↓ вместо  приведет к появлению на терминале строки 0,000074/010007. Она указывает, что текущим адресом будет 74 с учетом содержимого нулевого регистра. Ячейка 74 теперь открыта, и по желанию можно либо изменить ее, либо просто закрыть.

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

Если метка MEM имеет относительный адрес 162, то закрытие ячейки 74 клавишей ↓ приведет к 0,000076/000062. По листингу программы легко удостовериться, что это правильная ссылка на MEM при относительной адресации. Расчет смещения выполняется в предположении, что счетчик команд указывает на следующую ячейку. Поэтому относительный адрес ячейки памяти, на которую ссылается команда, такой: (72+2)+62=162.

Закрытие ячейки клавишей ← приводит к индексированию содержимого текущего слова по отношению к адресу следующей ячейки: как мы только что видели, в результате получается адрес слова в относительной адресации. Отладчик открывает слово с таким адресом и выводит на терминал его содержимое. Так, в нашем случае в результате закрытия ячейки 76 клавишей ← будет напечатано 0,000162/000000 в предположении, что при трансляции в ячейку MEM был занесен нуль. Таким образом, клавиша ← позволяет проследить эффект от выполнения подобных команд.

Учтите, что не существует специальной команды, открывающей ячейку с адресом, который вычисляется с помощью индексирования по какому-либо иному регистру, отличному от PC. Команда ← сама по себе не использует значение счетчика команд, потому что он указывал бы на подпрограмму в комплексе ODT, отвечающую за выполнение этой команды. Зато в ней имеется специальный указатель, с помощью которого определяется, к какой ячейке во время исполнения будет произведено обращение при относительной адресации.

Если бы вместо рассмотренной выше команды у нас была бы MOV R3,@#MEM, то открытие ячейки 76 дало бы абсолютный адрес метки MEM: 0,000076/006500. Чтобы заглянуть на этот раз в ячейку MEM, нужно закрыть текущую ячейку нажатием клавиши @. В ответ отладчик выведет 0,000162/000000, интерпретируя содержимое закрываемого слова как адрес ячейки, которую нужно открыть. Поскольку мы еще не исполняли программу и ничего не изменяли в ней, ячейка MEM, как и следовало ожидать, содержит в точности то, что было занесено в нее директивой .WORD во время трансляции.

Если до применения команд ← или @ изменить содержимое закрываемого слова, то именно оно будет использовано для вычисления адреса следующей открываемой ячейки.

Для вызова командами JSR или JMP других подпрограмм можно применить эти же команды в зависимости от способа адресации к месту передачи управления. Поскольку открываемая ячейка есть начало соответствующей подпрограммы, представление последней в машинной памяти можно просмотреть клавишей ↓. Для проверки же команд перехода нужно использовать команду >. Допустим, из листинга программы следует, что по адресу 200 стоит команда BNE MEM. Если метка MEM имеет адрес 162, то отладчик выдал бы 0,000200/001370, так как условный переход должен произойти десятью словами раньше относительно ячейки, следующей за командой перехода. Если мы теперь командой > закроем открытую ячейку, то ODT выведет на терминал 0,000162/000000. Ячейка, в которую делается переход, вычисляется отладчиком по значению младшего байта закрываемого слова. Отладчик не проверяет, действительно ли в старшем байте закодирована команда перехода. Если перед обращением к команде > содержимое текущего слова было изменено, то ячейка, на которую произойдет переход, будет вычислена по новому значению.

В процессе отладки вы можете обнаружить, что ссылка на ячейку с относительным адресом приводит к неправильному адресу. Допустим, что команду в ячейке 200 нужно заменить на BNE LABEL, где LABEL имеет адрес 316. Сначала открываем ячейку 200 командой 0,200/, в результате чего ODT выведет на терминал 001370. Теперь командой ;O даем указание отладчику рассчитать смещение для перехода к ячейке 316. На терминале набираем 0,316;O. Отладчик ответит строкой 000114 046, выдавая значение смещения в байтах для команд с относительной адресацией и в словах для команд перехода. Если в последнем случае оно выходит за диапазон, допустимый в командах перехода, то напечатано не будет.

Нам нужно изменить команду перехода, поэтому набираем 001046, что приводит к нужному результату и закрывает слово. Заметьте, что набор на терминале просто 46 не сработает (хотя в то же время команда 1046 корректна).

Команды ←, @ и > нарушают последовательный просмотр программы. Однако в программе отладчика сохранится запись содержимого слова, которое было раскрыто последним перед командой, нарушающей обычный порядок. Команда < вернет указатель к соответствующему адресу и в дальнейшем будет эквивалентна →. Пусть в программе по относительному адресу 220 находится команда JSR PC,PRINT. Тогда ссылка по относительному адресу PRINT расположена в ячейке 222, и, чтобы просмотреть вызываемую подпрограмму, нужно закрыть указанную ячейку командой ←. Затем используем ↓ для последовательного просмотра ячеек подпрограммы. Когда мы просмотрим достаточно ячеек, закрываем текущее слово командой <. В результате открывается ячейка 224, т.е. следующая за последней перед переходом к подпрограмме PRINT ячейка, и будет выведено ее содержимое. Но повторное нажатие клавиши < не приведет к возвращению еще на один уровень назад, а будет равносильно команде ↓ .

Чтобы просмотреть содержимое регистров, нужно в команде / после символа $ набрать номер регистра. Так, после ввода команды $3/ отладчик распечатает содержимое регистра R3. Его можно заменить новым значением, которое вводится обычным способом перед закрытием регистра. При закрытии регистра клавишами ↓ или открывается соответственно следующий или предыдущий регистр. Можно также применять команды ← или @, но не > и <. Такое ограничение естественно, поскольку программа не может передавать управление регистру. Следовательно, он не может содержать команду перехода, и в то же время применение команд ←, @ или > никогда не приведет к его открытию.

Внутренний регистр отладчика $S содержит коды состояния процессора (в обычном порядке) в виде четырех младших битов слова состояния. Их можно посмотреть, набрав на терминале $3/. На другие разряды регистра $S обращать внимание не следует.

Комплекс ODT позволяет также выводить содержимое памяти в виде литер в коде ASCII или 50-ричном коде. Инструкция \ применяется для открытия байта, распечатки его содержимого в числовом виде и затем, если это возможно, в виде литер в коде ASCII. Так, если в программе меткой MEM помечена директива

MEM: .WORD "AB

то команда 0,162/ выдаст на терминал 041101, в то время как, набрав 0,162\, в результате получим 101=A. Теперь отладчик перешел в байтовый режим, и команды ↓ и будут обращаться к соответствующим байтам. Например, после нажатия клавиши ↓ отладчик выведет информацию:

0,000163 \102 =B

Команда / с четным адресом возвратит ODT в режим работы со словами. Применение же ее с нечетным адресом эквивалентно команде \.

Для проверки текста в 50-ричном коде используйте, как и обычно, клавишу /, чтобы выдать содержимое нужного вам слова в восьмеричном формате. После этого наберите литеру X (без ), в результате чего отладчик выдаст содержимое слова в виде трех литер 50-ричного кода. При закрытии ячейки применяйте только команды  или ↓, так как в некоторых версиях программы ODT иные закрывающие ячейку команды будут интерпретироваться (безуспешно) как новое содержимое (в 50-ричном коде) текущей ячейки.