ачальный адрес следующим образом: A 100 [Return] Отладчик выдаст значение адреса сегмента кодов и смещения в ввиде хххх:0100. Теперь можно вводить каждую команду, завер шая клавишей Return. Когда вся программа будет введена, нажмите снова клавишу Return для выхода из режима ассембле ра. Введите следующую программу: Ассемблер для IBM PC. Глава 2 41 MOV AL,25 [Return] MOV BL,32 [Return] ADD AL,BL [Return] RET [Return] по завершению на экране будет следующая информация: хххх:0100 MOV AL,25 хххх:0102 MOV BL,32 хххх:0104 ADD AL,BL хххх:0106 RET В этот момент отладчик готов к приему следующей команды. При нажатии Return операция будет прекращена. Можно видеть, что отладчик определил стартовые адреса каждой команды. Прежде чем выполнить программу, проверим сгенерированные машинные коды. Команда U Команда отладчика U (Unassemble) показывает машинные коды для команд ассемблера. Необходимо сообщить отладчику адреса первой и последней команды, которые необходимо просмотреть (в данном cлучае 100 и 106). Введите: U 100,106 [и Return] и на экране появится хххх:0100 B025 MOV AL,25 хххх:0102 B332 MOV BL,32 хххх:0104 00D8 ADD AL,BL хххх:0106 C3 RET Теперь проведем трассировку выполнения программы, начиная с команды R для вывода содержимого регистров и первой команды программы. С помощью команд T выполним последовательно все команды программы. Теперь вы знаете, как вводить программу в машинном коде или на языке ассемблера. Обычно используется ввод на языке ассемблера, когда машинный код неизвестен, а ввод в машинном коде - для изменения программы во время выполнения. Однако в действительности программа DEBUG предназначена для отладки программ и в следующих главах основное внимание будет уделено использованию языка ассемблера. Сохранение программы из отладчика Можно использовать DEBUG для сохранения программ на диске в следующих случаях: Ассемблер для IBM PC. Глава 2 42 1. После загрузки программы в память машины и ее модифика ции необходимо сохранить измененный вариант. Для этого следует: ъ загрузить программу по ее имени: DEBUG n:имяфайла[Return] ъ просмотреть программу с помощью команды D и ввести изменения по команде E, ъ записать измененную программу: W [Return] 2. Необходимо с помощью DEBUG написать небольшую по объему программу и сохранить ее на диске. Для этого следует: ъ вызвать отладчик DEBUG, ъ с помощью команд A (assemble) и E (enter) написать программу, ъ присвоить программе имя: N имяфайла.COM [Return]. Тип программы должен быть COM (см. главу 6 для пояснений по COM-файлам). ъ Так как только программист знает, где действительно кончается его программа, указать отладчику длину программы в байтах. В последнем примере концом программы является команда хххх:0106 C3 RET Эта команда однобайтовая и поэтому размер программы будет равен 106 (конец) минус 100 (начало), т.е. 6. ъ запросить регистр CX командой: R CX [Return] ъ отладчик выдаст на этот запрос CX 0000 (нулевое значение) ъ указать длину программы - 6, ъ записать измененную программу: W [Return] В обоих случаях DEBUG выдает сообщение "Writing nnnn bytes." (Запись nnnn байтов). Если nnnn равно 0, то произошла ошибка при вводе длины программы, и необходимо повторить запись cнова. ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------ Отладчик DOS DEBUG это достаточное мощное средство, полезное для отладки ассемблерных программ. Однако следует быть осторожным с ее использованием, особенно для команды E (ввод). Ввод данных в неправильные адреса памяти или ввод некорректных данных могут привести к непредсказуемым результатам. На экране в этом случае могут появиться "странные" символы, клавиатура заблокирована или даже DOS прервет DEBUG и перезагрузит себя с диска. Какие либо серьезные повреждения вряд ли произойдут, но возможны некоторые неожиданности, а также потеря данных, которые вводились при работе с отладчиком. Ассемблер для IBM PC. Глава 2 43 Если данные, введенные в сегмент данных или сегмент кодов, оказались некорректными, следует, вновь используя команду E, исправить их. Однако, можно не заметить ошибки и начать трассиpовку программы. Но и здесь возможно еще использовать команду E для изменений. Если необходимо начать выполнение с первой команды, то следует установить в регистре командного указателя (IP) значение 0100. Введите команду R (register) и требуемый регистр в следующем виде: R IP [Return] Отладчик выдаст на экран содержимое регистра IP и перей дет в ожидание ввода. Здесь следует ввести значение 0100 и нажать для проверки результата команду R (без IP). 0тладчик выдаст содержимое регистров, флагов и первую выполняемую команду. Теперь можно, используя команду T, вновь выполнить трассировку программы. Если ваша программа выполняет какие-либо подсчеты, то возможно потребуется очистка некоторых облостей памяти и регистров. Но yбедитесь в сохранении содержимого регистров CS, DS, SP и SS, которые имеют специфическое назначение. Прочитайте в руководстве по DOS главу о программе DEBUG. В настоящий момент рекомендуется: вводный материал и следующие команды oтладчика: дамп (D), ввод (E), шестнадцатиричный (H), имя (N), выход (Q), регистры (R), трассировка (T) и запись (W). Можно oзнакомиться также и с другими командами и проверить как они работают. ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------ 2.1. Напишите машинные команды для а) пересылки шест. значения 4629 в регистр AX; б) сложения шест. 036A с содержимым регистра AX. 2.2. Предположим, что была введена следующая е команда: E CS:100 B8 45 01 05 25 00 Вместо шест.значения 45 предполагалось 54. Написшите команду E для корректировки только одного неправильно введенного байта, т.е. непосредственно замените 45 на 54. 2.3. Предположим, что введена следующая е команда: E CS:100 B8 04 30 05 00 30 CB а) Что представляют собой эти команды? (Сравните с первой программой в этой главе). б) После выполнения этой программы в регистре AX должно быть значение 0460, но в действительности оказалось 6004. В чем ошибка и как ее исправить? Ассемблер для IBM PC. Глава 2 44 в) После исправления команд необходимо снова выполнить программу с первой команды. Какие две команды отладчика потребуются? 2.4. Имеется следующая программа в машинных кодах: B0 25 D0 E0 B3 15 F6 E3 CB Программа выполняет следующее: - пересылает шест.значение 25 в регистр AL; - сдвигает содержимое регистра AL на один бит влево (в результате в AL будет 4A); - пересылает шест.значение 15 в регистр BL; - умножает содержимое регистра AL на содержимое регистра BL. Используйте отладчик для ввода (E) этой программы по адресу CS:100. Не забывайте, что все значения представ лены в шестнадцатиричном виде. После ввода программы наберите D CS:100 для просмотра сегмента кода. Затем введите команду R и необходимое число команд T для пошагового выполнения программы до команды RET. Какое значение будет в регистре AX в результате выполнения программы? 2.5. Используйте отладчик для ввода (E) следующей программы в машинных кодах: Данные: 25 15 00 00 Машинный код: A0 00 00 D0 E0 F6 26 01 00 A3 02 00 CB Программа выполняет следующее: - пересылает содержимое одного байта по адресу DS:00 (25) в регистр AL; - сдвигает содержимое регистра AL влево на один бит (получая в результате 4A); - умножает AL на содержимое одного байта по адресу DS:01 (15); - пересылает результат из AX в слово, начинающееся по адресу DS:02. После ввода программы используйте команды D для просмот ра сегмента данных и сегмента кода. Затем введите коман ду R и необходимое число команд T для достижения конца программы (RET). В этот момент регистр AX должен содер жать результат 0612. Еще раз используйте команду D DS:00 и заметьте, что по адресу DS:02 значение записано как 1206. 2.6. Для предыдущего задания (2.5) постройте команды для записи программы на диск под именем TRIAL.COM. Ассемблер для IBM PC. Глава 2 45 2.7. Используя команду A отладчика, введите следующую про грамму: MOV BX,25 ADD BX,30 SHL BX,01 SUB BX,22 NOP RET сделайте ассемблирование и трассировку выполнения этой программы до команды NOP. Ассемблер для IBM PC. Глава 3 60 ГЛАВА 3. Требования языка ассемблер ------------------------------------------------------------ Требования языка ассемблер Цель: показать основные требования к программам на языке ассемблера и этапы ассемблирования, компановки и выполнения программы. ВВЕДЕНИЕ ------------------------------------------------------------ В главе 2 было показано как ввести и выполнить программу на машинном языке. Несомненно при этом ощутима трудность расшифровки машинного кода даже для очень небольшой программы. Сомнительно, чтобы кто-либо серьезно кодировал программы на машинном языке, за исключением разных "заплат" (корректировок) в программе на языках высокого уровня и прикладные программы. Более высоким уровнем кодирования является уровень ассемблера, на котором программист пользуется символическими мнемокодами вместо машинных команд и описательными именами для полей данных и адресов памяти. Программа написанная символическими мнемокодами, которые используются в языке ассемблера, представляет собой исходный модуль. Для формирования исходного модуля применяют программу DOS EDLIN или любой другой подходящий экранный редактор. Затем с помощью программы ассемблерного транслято ра исходный текст транслируется в машинный код, известный как объектная программа. И наконец, программа DOS LINK определяет все адресные ссылки для объектной программы, генерируя загрузочный модуль. В данной главе объясняются требования для простой програм мы на ассемблере и показаны этапы ассемблирования, компанов ки и выполнения. КОММЕНТАРИИ В ПРОГРАММАХ НА АССЕМБЛЕРЕ ------------------------------------------------------------ Использование комментариев в программе улучшает ее ясность, oсобенно там, где назначение набора команд непонятно. Комментаpий всегда начинаются на любой строке исходного модуля с символа точка с запятой (;) и ассемблер полагает в этом случае, что все символы, находящиеся справа от ; являются комментарием. Комментарий может содержать любые печатные символы, включая пробел. Комментарий может занимать всю строку или следовать за командой на той же строке, как это показано в двух следующих примерах: 1. ;Эта строка полностью является комментарием 2. ADD AX,BX ;Комментарий на одной строке с командой Ассемблер для IBM PC. Глава 3 61 Комментарии появляются только в листингах ассемблиро- вания исходного модуля и не приводят к генерации машинных кодов, поэтому можно включать любое количество комментариев, не оказывая влияния на эффективность выполнения программы. В данной книге команды ассемблера представлены заглавными буквами, а комментарии - строчными (только для удобочитае мости). ФОРМАТ КОДИРОВАНИЯ ------------------------------------------------------------ Основной формат кодирования команд ассемблера имеет следующий вид: [метка] команда [операнд(ы)] Метка (если имеется), команда и операнд (если имеется) pазделяются по крайней мере одним пробелом или символом табуляции. Максимальная длина строки - 132 символа, однако, большинство предпочитают работать со строками в 80 символов (соответственно ширине экрана). Примеры кодирования: Метка Команда Операнд COUNT DB 1 ;Имя, команда, один операнд MOV AX,0 ;Команда, два операнда Метки Метка в языке ассемблера может содержать следующие симво лы: Буквы: от A до Z и от a до z Цифры: от 0 до 9 Спецсимволы: знак вопроса (?) точка (.) (только первый символ) знак "коммерческое эт" (@) подчеркивание (-) доллар ($) Первым символом в метке должна быть буква или спецсимвол. Ассемблер не делает различия между заглавными и строчными буквами. Максимальная длина метки - 31 символ. Примеры меток: COUNT, PAGE25, $E10. Рекомендуется использовать описательные и смысловые метки. Имена регистров, например, AX, DI или AL являются зарезервированными и используются только для указания соответствующих регистров. Например, в команде ADD AX,BX ассемблер "знает", что AX и BX относится к регистрам. Однако, в команде MOV REGSAVE,AX Ассемблер для IBM PC. Глава 3 62 ассемблер воспримет имя REGSAVE только в том случае, если оно будет определено в сегменте данных. В приложении 3 приведен cписок всех зарезервированных слов ассемблера. Команда Мнемоническая команда указывает ассемблеру какое действие должен выполнить данный оператор. В сегменте данных команда (или директива) определяет поле, рабочую oбласть или константу. В сегменте кода команда определяет действие, например, пересылка (MOV) или сложение (ADD). Операнд Если команда специфирует выполняемое действие, то операнд определяет а) начальное значение данных или б) элементы, над которыми выполняется действие по команде. В следующем примере байт COUNTER определен в сегменте данных и имеет нулевое значение: Метка Команда Операнд COUNTER DB 0 ;Определить байт (DB) ; с нулевым значением Команда может иметь один или два операнда, или вообще быть без операндов. Рассмотрим следующие три примера: Команда Операнд Комментарий Нет операндов RET ;Вернуться Один операнд INC CX ;Увеличить CX Два операнда ADD AX,12 ;Прибавить 12 к AX Метка, команда и операнд не обязательно должны начинаться с какой-либо определенной позиции в строке. Однако, рекомен дуется записывать их в колонку для большей yдобочитаемости программы. Для этого, например, редактор DOS EDLIN обеспечи вает табуляцию чепез каждые восемь позиций. ДИРЕКТИВЫ ------------------------------------------------------------ Ассемблер имеет ряд операторов, которые позволяют упpав лять процессом ассемблирования и формирования листинга. Эти операторы называются псевдокомандами или директивами. Они действуют только в процессе ассемблирования программы и не генерируют машинных кодов. Большинство директив показаны в следующих разделах. В главе 24 подробно описаны все директивы ассемблера и приведено более чем достаточно соответствующей информации. Главу 24 можно использовать в качестве справочника. Директивы управления листингом: PAGE и TITLE Ассемблер для IBM PC. Глава 3 63 Ассемблер содержит ряд директив, управляющих форматом печати (или листинга). Обе директивы PAGE и TITLE можно использовать в любой программе. Директива PAGE. В начале программы можно указать количест во строк, распечатываемых на одной странице, и максимальное количество символов на одной строке. Для этой цели cлужит директива PAGE. Следующей директивой устанавливается 60 строк на страницу и 132 символа в строке: PAGE 60,132 Количество строк на странице межет быть в пределах от 10 до 255, а символов в строке - от 60 до 132. По умолчанию в ассемблере установлено PAGE 66,80. Предположим, что счетчик строк установлен на 60. В этом случае ассемблер, распечатав 60 строк, выполняет прогон листа на начало следующей страницы и увеличивает номер страницы на eдиницу. Кроме того можно заставить ассемблер сделать прогон листа на конкретной строке, например, в конце сегмента. Для этого необходимо записать директиву PAGE без операндов. Ассемблер автоматически делает прогон листа при обработке диpективы PAGE. Директива TITLE. Для того, чтобы вверху каждой страницы листинга печатался заголовок (титул) программы, используется диpектива TITLE в следующем формате: TITLE текст Рекомендуется в качестве текста использовать имя програм мы, под которым она находится в каталоге на диске. Например, если программа называется ASMSORT, то можно использовать это имя и описательный комментарий общей длиной до 60 символов: TITLE ASMSORT - Ассемблерная программа сортировки имен В ассемблере также имеется директива подзаголовка SUBTTL, которая может оказаться полезной для очень больших программ, содержащих много подпрограмм. Директива SEGMENT Любые ассемблерные программы содержат по крайней мере один сегмент - сегмент кода. В некоторых программах используется сегмент для стековой памяти и сегмент данных для определения данных. Асcемблерная директива для описания сегмента SEGMENT имеет следующий формат: Имя Директива Операнд имя SEGMENT [параметры] . . Ассемблер для IBM PC. Глава 3 64 . имя ENDS Имя сегмента должно обязательно присутствовать, быть уникальным и соответствовать соглашениям для имен в ассемблере. Директива ENDS обозначает конец сегмента. Обе директивы SEGMENT и ENDS должны иметь одинаковые имена. Директива SEGMENT может содержать три типа параметров, определяющих выравнивание, объединение и класс. 1. Выравнивание. Данный параметр определяет границу начала сегмента. Обычным значением является PARA, по которму сегмент устанавливается на границу параграфа. В этом случае начальный адрес делится на 16 без остатка, т.е. имеет шест. адрес nnn0. В случае отсутствия этого операнда ассемблер принимает по умолчанию PARA. 2. Объединение. Этот элемент определяет объединяется ли данный сегмент с другими сегментами в процессе компановки после ассемблирования (пояснения см. в следующем разделе "Компановка программы"). Возможны следующие типы объединений: STACK, COMMON, PUBLIC, AT выражение и MEMORY. Сегмент стека определяется следующим образом: имя SEGMENT PARA STACK Когда отдельно ассемблированные программы должны объеди няться компановщиком, то можно использовать типы: PUBLIC, COMMON и MEMORY. В случае, если программа не должна об'единяться с другими программами, то данная опция может быть опущена. 3. Класс. Данный элемент, заключенный в апострофы, исполь зуется для группирования относительных сегментов при компановке: имя SEGMENT PARA STACK 'Stack' Фрагмент программы на рис. 3.1. в следующем разделе иллюстрирует директиву SEGMENT и ее различные опции. Директива PROC Сегмент кода содержит выполняемые команды программы. Кроме того этот сегмент также включает в себя одну или несколько процедур, определенных директивой PROC. Сегмент, содержащий только одну процедуру имеет следующий вид: имя-сегмента SEGMENT PARA имя-процедуры PROC FAR Сегмент . кода . с . одной RET процедурой Ассемблер для IBM PC. Глава 3 65 имя-процедуры ENDP имя-сегмента ENDS Имя процедуры должно обязательно присутствовать, быть уникальным и удовлетворять соглашениям по именам в ассембле ре. Операнд FAR указывает загрузчику DOS, что начало данной процедуры является точкой входа для выполнения программы. Директива ENDP определяет конец процедуры и имеет имя, аналогичное имени в директиве PROC. Команда RET завершает выполнение программы и в данном случае возвращает управление в DOS. Сегмент может содержать несколько процедур (см. гл.7). Директива ASSUME Процессор использует регистр SS для адресации стека, ркгистр DS для адресации сегмента данных и регистр CS для адресации cегмента кода. Ассемблеру необходимо сообщить назначение каждого сегмента. Для этой цели служит директива ASSUME, кодируемая в сегменте кода следующим образом: Директива Операнд ASSUME SS:имя_стека,DS:имя_с_данных,CS:имя_с_кода Например, SS:имя_стека указывает, что ассемблер должен ассоциировать имя сегмента стека с регистром SS. Операнды могут записываться в любой последовательности. Регистр ES также может присутствовать в числе операндов. Если программа не использует регистр ES, то его можно опустить или указать ES:NOTHING. Директива END Как уже показано, директива ENDS завершает сегмент, а директива ENDP завершает процедуру. Директива END в свою очередь полностью завершает всю программу: Директива Операнд END [имя_процедуры] Операнд может быть опущен, если программа не предназначе на для выполнения, например, если ассемблируются только опре деления данных, или эта программа должна быть скомпанована с другим (главным) модулем. Для обычной программы с одним модулем oперанд содержит имя, указанное в директиве PROC, которое было oбозначено как FAR. ПАМЯТЬ И РЕГИСТРЫ ------------------------------------------------------------ Рассмотрим особенности использования в командах имен, имен в квадратных скобках и чисел. В следующих примерах положим, что WORDA определяет слово в памяти: Ассемблер для IBM PC. Глава 3 66 MOV AX,BX ;Переслать содержимое BX в регистр AX MOV AX,WORDA ;Переслать содержимое WORDA в регистр AX MOV AX,[BX] ;Переслать содержимое памяти по адресу ; в регистре BX в регистр AX MOV AX,25 ;Переслать значение 25 в регистр AX MOV AX,[25] ;Переслать содержимое по смещению 25 Новым здесь является использование квадратных скобок, что потребуется в следующих главах. ИНИЦИАЛИЗАЦИЯ ПРОГРАММЫ ------------------------------------------------------------ Существует два основных типа загрузочных программ: EXE и COM. Рассмотрим требования к EXE-программам, а COM-программы будут представлены в главе 6. DOS имеет четыре требования для инициализации ассемблерной EXE-программы: 1) указать ассемблеру, какие cегментные регистры должны соответствовать сегментам, 2) сохранить в стеке адрес, находящийся в регист ре DS, когда программа начнет выполнение, 3) записать в стек нелевой адрес и 4) загрузить в регистр DS адрес сегмента данных. Выход из программы и возврат в DOS сводится к использова нию команды RET. Рис.3.1 иллюстрирует требования к инициали зации и выходу из программы: 1. ASSUME - это ассемблерная директива, которая устанавли вает для ассемблера соответствие между конкретными сегментами и сегментными регистрами; в данном случае, CODESG - CS, DATASG - DS и STACKSG - SS. DATASG и STACKSG не определены в этом примере, но они будут представлены следующим образом: STACKSG SEGMENT PARA STACK Stack 'Stack' DATASG SEGMENT PARA 'Data' Ассоциируя сегменты с сегментными регистрами, ассемблер сможет определить смещения к отдельным областям в каждом сегменте. Например, каждая команда в сегменте кодов имеет определенную длину: первая команда имеет смещение 0, и если это двухбайтовая команда, то вторая команда будет иметь смещение 2 и т.д. 2. Загрузочному модулю в памяти непосредственно предшеству ет 256-байтовая (шест.100) область, называемая префик сом программного сегмента PSP. Программа загрузчика использует регистр DS для установки адреса начальной точки PSP. Пользовательская программа должна сохранить этот адрес, поместив его в стек. Позже, команда RET использует этот адрес для возврата в DOS. Ассемблер для IBM PC. Глава 3 67 3. В системе требуется, чтобы следующее значение в стеке являлось нулевым адресом (точнее, смещением). Для этого команда SUB очищает регистр AX, вычитая его из этого же регистра AX, а команда PUSH заносит это значение в стек. 4. Загрузчик DOS устанавливает правильные адреса стека в регистре SS и сегмента кодов в регистре CS. Поскольку программа загрузчика использует регистр DS для других целей, необходимо инициализировать регистр DS двумя командами MOV, как показано на рис.3.1. В следующем разделе этой главы "Исходная программа. Пример II" детально поясняется инициализация регистра DS. ------------------------------------------------------------ ------------------------------------------------------------ Рис. 3.1. Инициализация EXE-программы. 5. Команда RET обеспечивает выход из пользовательской программы и возврат в DOS, используя для этого адрес, записанный в стек в начале программы командой PUSH DS. Другим обычно используемым выходом является команда INT 20H. Теперь, даже если приведенная инициализация программы до конца не понятна - не отчаивайтесь. Каждая программа фактически имеет аналогичные шаги инициализации, так что их можно дублировать всякий раз при кодировании программ. ПРИМЕР ИСХОДНОЙ ПРОГРАММЫ ------------------------------------------------------------ Рис. 3.2. обобщает предыдущие сведения в простой исходной программе на ассемблере. Программа содержит сегмент стека - STACKSG и сегмент кода - CODESG. STACKSG содержит один элемент DB (определить байт), который определяет 12 копий слова 'STACKSEG'. В последующих программах стек не опpеделяется таким способом, но при использовании отладчика для просмотра ассемблированной программы на экране, данное определение помогает локализо вать стек. CODESG содержит выполняемые команды программы, хотя первая директива ASSUME не генерирует кода. Директива ASSUME назначает регистр SS для STACKSG и регистр CS для CODESG. В действительности, эта директива сообщает ассемблеру, что для адресации в STACKSG необходимо использовать адрес в регистре SS и для адресации в CODESG - адрес в регистре CS. Системный загрузчик при загрузке программы с диска в память для выполнения устанавливает действительные адреса в регистрах SS и CS. Программа не имеет сегмента данных, так как в ней нет определения данных и, соответственно, в ASSUME нет необходимости ассигновать pегистр DS. Ассемблер для IBM PC. Глава 3 68 Команды, следующие за ASSUME - PUSH, SUB и PUSH выполняют стандартные действия для инициализации стека текущим адресом в регистре DS и нулевым адресом. Поскольку, обычно, программа выполняется из DOS, то эти команды обеспечивают возврат в DOS после завершения программы. (Можно также выполнить программу из отладчика, хотя это особый случай). Последующие команды выполняют те же действия, что показаны на pис.2.1 в предыдущей главе, когда рассматривался отладчик. ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------ ъ Не забывайте ставить символ "точка с запятой" перед комментариями. ъ Завершайте каждый сегмент директивой ENDS, каждую процедуру - директивой ENDP, а программу - директивой END. ъ В директиве ASSUME устанавливайте соответствия между сегментными регистрами и именами сегментов. ъ Для EXE-программ (но не для COM-программ, см. гл.6) обеспечивайте не менее 32 слов для стека, соблюдайте соглашения по инициализации стека командами PUSH, SUB и PUSH и заносите в регистр DS адрес сегмента данных. ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------ 3.1. Какие команды заставляют ассемблер печатать заголовок в начале каждой страницы листинга и делать прогон листа? 3.2. Какие из следующих имен неправильны: а) PC_AT, б) $50, в) @$_Z, г) 34B7, д) AX? 3.3. Какое назначение каждого из трех сегментов, описанных в этой главе? 3.4. Что конкретно подразумевает директива END, если она завершает а) программу, б) процедуру, в) сегмент? 3.5. Укажите различия между директивой и командой. 3.6. Укажите различия в назначении RET и END. 3.7. Для сегментов кода, данных и стека даны имена CDSEG, DATSEG и STKSEG соответственно. Сформируйте директиву ASSUME. 3.8. Напишите три команды для инициализации стека адресом в DS и нулевым адресом. Ассемблер для IBM PC. Глава 4 79 ГЛАВА 4. Ассемблирование и выполнение программ ------------------------------------------------------------ Ассемблирование и выполнение программ Цель: показать процессы ассемблирования, компановки и выполнения программ. ВВЕДЕНИЕ ------------------------------------------------------------ В данной главе объясняется, как ввести в компьютер исходный ассемблерный текст программы, как осуществить ассемблирование, компановку и выполнение программы. Кроме того, показана генерация таблицы перекрестных ссылок для целей отладки. ВВОД ПРОГРАММЫ ------------------------------------------------------------ На рис.3.2. был показан только исходный текст программы, предназначенный для ввода с помощью текстового редактора. Теперь можно использовать DOS EDLIN или другой текстовый редактор для ввода этой программы. Если вы никогда не пользовались программой EDLIN, то именно сейчас необходимо выполнить ряд упражнений из руководства по DOS. Для запуска программы EDLIN вставте дискету DOS в дисковод A и форматизованную дискету в дисковод B. Чтобы убедиться в наличии на дискете свободного места для исходного текста, введите CHKDSK B:. Для винчестера во всех следующих примерах следует использовать C: вместо B:. Для ввода исходной программы EXASM1, наберите команду EDLIN В:EXASM1.ASM [Return] В результате DOS загрузит EDLIN в памяти и появится сообщение "New file" и приглашение "*-". Введите команду I для ввода строк, и затем наберите каждую ассемблерную команду так, как они изобpажены на рис. 3.2. Хотя число пробелов в тексте для ассемблера не существенно, старайтесь записывать метки, команды, операнды и комментарии, выровнен ными в колонки, программа будет более yдобочитаемая. Для этого в EDLIN используется табуляция через каждые восемь позиций. После ввода программы убедитесь в ее правильности. Затем наберите E (и Return) для завершения EDLIN. Можно проверить наличие программы в каталоге на диске, введите DIR B: (для всех файлов) или DIR B:EXASM1.ASM (для одного файла) Ассемблер для IBM PC. Глава 4 80 Если предполагается ввод исходного текста большего объема, то лучшим применением будет полноэкранный редактор. Для получения распечатки программы включите принтер и установите в него бумагу. Вызовите программу PRINT (для DOS 2.0 и старше). DOS загрузит программу в память и распечатает текст на принтере: PRINT B:EXASM1.ASM [Return] Программа EXASM.ASM еще не может быть выполнена - прежде необходимо провести ее ассемблирование и компановку. В следующем pазделе показана эта же программа после ассемблирования и пояснены этапы ассемблирования и получения листинга. ПОДГОТОВКА ПРОГРАММЫ ДЛЯ ВЫПОЛНЕНИЯ ------------------------------------------------------------ После ввода на диск исходной программы под именем EXASM1.ASM необходимо проделать два основных шага, прежде чем программу можно будет выполнить. Сначала необходимо ассемблиpовать программу, а затем выполнить компановку. Программисты на языке бейсик могут выполнить программу сразу после ввода исходного текста, в то время как для ассемблера и компиллярных языков нужны шаги трансляции и компановки. Шаг ассемблирования включает в себя трансляцию исходного кода в машинный объектный код и генерацию OBJ-модуля. Вы уже встречали примеры машинного кода в главе 2 и примеры исxодно го текста в этой главе. OBJ-модуль уже более приближен к исполнительной форме, но еще не готов к выполнению. Шаг компановки включает преобразо вание OBJ-модуля в EXE (исполнимый) модуль, содержащий машинный код. Прогрпмма LINK, находящаяся на диске DOS, выполняет следующее: 1. Завершает формирование в OBJ-модуле адресов, которые остались неопределенными после ассемблирования. Во мно гих следующих программах такие адреса ассемблер отмеча ет как ----R. 2. Компанует, если необходимо, более одного отдельно ассем блированного модуля в одну загрузочную (выполнимую) про грамму; возможно две или более ассемблерных программ или ассемблерную программу с программами, написанными на языках высокого уровня, таких как Паскаль или Бейсик. 3. Инициализирует EXE-модуль командами загрузки для выполнения. После компановки OBJ-модуля (одного или более) в EXE-модуль, можно выполнить EXE-модуль любое число раз. Но, если необходимо внести некоторые изменения в EXE-модуль, следует скорректировать исходную программу, ассемблировать ее в другой OBJ-модуль и выполнить компановку OBJ-модуля в Ассемблер для IBM PC. Глава 4 81 новый EXE-модуль. Даже, если эти шаги пока остаются непо нятными, вы обнаружите, что, получив немного навыка, весь процесс подготовки EXE-модуля будет доведен до автоматизма. Заметьте: определенные типы EXE-программ можно преобразовать в oчень эффективные COM-программы. Предыдущие примеры, однако, не cовсем подходят для этой цели. Данный вопрос рассматривается в главе 6. АССЕМБЛИРОВАНИЕ ПРОГРАММЫ ------------------------------------------------------------ Для того, чтобы выполнить исходную ассемблерную програм му, необходимо прежде провести ее ассемблирование и затем компановку. На дискете с ассемблерным пакетом имеются две версии aссемблера. ASM.EXE - сокращенная версия с отсутстви ем некоторых незначительных возможностей и MASM.EXE - полная версия. Если размеры памяти позволяют, то используйте версию MASM (подробности см. в соответствующем руководстве по ассемблеру). Для ассемблирования, вставте ассемблерную дискету в дисковод A, а дискету с исходной программой в дисковод B. Кто имеет винчестер могут использовать в следующих примеpах C вместо A и B. Простейший вариант вызова программы это ввод команды MASM (или ASM), что приведет к загрузке программы ассемблера с диска в память. На экране появится: source filename [.ASM]: object filename [filename.OBJ]: source listing [NUL.LST]: cross-reference [NUL.CRF]: Курсор при этом расположится в конце первой строки, где необходимо указать имя файла. Введите номер дисковода (если он не определен умолчанием) и имя файла в следующем виде: B:EXASM1. Не следует набирать тип файла ASM, так как ассем блер подразумевает это. Во-втором запросе предполагается аналогичное имя файла (но можно его заменить). Если необходимо, введите номер дисковода B:. Третий запрос предполагает, что листинг ассемблирования программы не требуется. Для получения листинга на дисководе B наберите B: и нажмите Return. Последний запрос предполагает, что листинг перекрестных cсылок не требуется. Для получения листинга на дисководе B, наберите B: и нажмите Return. Если вы хотите оставить значения по умолчанию, то в трех последних запросах просто нажмите Return. Ниже приведен пример запросов и ответов, в результате которых ассемблер должен cоздать OBJ, LST и CRF-файлы. Введите ответы так, как показано, за исключением того, что номер дисковода может быть иной. source filename [.ASM]:B:EXASM1 [Return] Ассемблер для IBM PC. Глава 4 82 object filename [filename.OBJ]:B: [Return] source listing [NUL.LST]:B: [Return] cross-reference [NUL.CRF]:B: [Return] Всегда необходимо вводить имя исходного файла и, обычно, запрашивать OBJ-файл - это требуется для компановки программы в загрузочный файл. Возможно потребуется указание LST-файла, особенно, если необходимо проверить сгенерирован ный машинный код. CRF-файл полезен для очень больших программ, где необходимо видеть, какие команды ссылаются на какие поля данных. Кроме того, ассемблер генерирует в LST-файле номера строк, которые используются в CRF-файле. В приложении 4 "Режимы ассемблирования и редактирования" перечислены режимы (опции) для ассемблера версий 1.0 и 2.0. Ассемблер преобразует исходные команды в машинный код и выдает на экран сообщения о возможных ошибках. Типичными ошибками являются нарушения ассемблерных соглашений по именам, неправильное написание команд (например, MOVE вместо MOV), а также наличие в опеpандах неопределенных имен. Программа ASM вадает только коды ошибок, которые объяснены в руководстве по ассемблеру, в то время как программа МASM выдает и коды ошибок, и пояснения к ним. Всего имеется около 100 сообщений об ошибках. Ассемблер делает попытки скорректировать некоторые ошибки, но в любом случае следует перезагрузить текстовый редактор, исправить исходную программу (EXASM1.ASM) и повторить ассемблирование. На рис. 4.1. показан листинг, полученный в результате асcемблирования программы и записанный на диск под именем EXASM1.LST. В начале листинга обратите внимание на реакцию ассемблера на директивы PAGE и TITLE. Никакие директивы, включая SEGMENT, PROC, ASSUME и END не генерируют машинных кодов. Листинг содержит не только исходный текст, но также слева транслированный машинный код в шестнадцатиричном формате. В самой левой колонке находится шест.адреса команд и данных. Сегмент стека начинается с относительного адреса 0000. В действительности он загружается в память в соответствии с адpесом в регистре SS и нулевым смещением относительно этого адpеса. Директива SEGMENT устанавливает 16-кратный адрес и указывает ассемблеру, что это есть начало стека. Сама директива не генерирует машинный код. Команда DB, также находится по адресу 0000, содержит 12 копий слова 'STACKSEG'; машинный код представлен шест.0C (десятичное 12) и шест. представлением ASCII символов. (В дальнейшем можно использовать отладчик для просмотра результатов в памяти). Сегмент стека заканчивается по адресу шест. 0060, который эквивалентен десятичному значению 96 (12х8). ------------------------------------------------------------ ------------------------------------------------------------ Рис. 4.1. Листинг ассемблирования программы Ассемблер для IBM PC. Глава 4 83 Сегмент кода также начинается с относительного адреса 0000. Он загружается в память в соответствии с адресом в pегистре CS и нулевым смещением относительно этого адреса. Поскольку ASSUME является директивой ассемблеру, то первая команда, которая генерирует действительный машинный код есть PUSH DS - однобайтовая команда (1E), находящаяся на ну