1
Лабораторная работа по курсу "Организация ЭВМ и систем"
Выполнение и отладка программ на языке ассемблера
Цель работы: 1) познакомиться со структурой простой программы на языке ассемблера IBM PC; 2) освоить процесс разработки и отладки программ на языке ассемблера; 3) познакомиться с набором регистров общего назначения и сегментных регистров процессора Intel 80x86.
Лабораторная работа выполняется в MS DOS. Перед выполнением работы создайте на локальном диске вашего ПК, доступном для записи (D:\), временный каталог и скопируйте в него файлы tasm.exe, tlink.exe, td.exe и tdhelp.tdh из Methodic\ИПОВС\CompOrg\TASM. В конце работы сохраните файлы .ASM и .BAT на Вашем личном диске H:\.
1. Введение
Программа состоит из одного или нескольких сегментов. Каждый сегмент имеет имя. Обычно в программе задаются три сегмента - кода (команд), данных и стека. Общая структура программы приведена на рис. 1.
Имя 1 |
|
Сегмент стека |
|
|
Конец сегмента 1 |
|
|
|
|
|
|
|
|
|
Имя 2 |
|
|
|
|
|
Сегмент данных |
|
Логические |
|
Конец сегмента 2 |
|
|
||
|
|
|
сегменты |
|
Имя 3 |
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
Стартовая метка: |
|
Сегмент кода |
|
|
Конец сегмента 3 |
|
|
|
|
Конец программы |
|
|
|
|
|
|
|
|
|
|
Рис. 1. Общая структура программы |
|
||
На языке ассемблера шаблон программы выглядит следующим образом: |
SSEG SEGMENT STACK 'STACK'
.........................
SSEG ENDS DSEG SEGMENT
.........................
DSEG ENDS CSEG SEGMENT
ASSUME SS:SSEG,DS:DSEG,CS:CSEG ; связывание имен логических сегментов и ; имен сегментных регистров ЭВМ
START: <.....> ; первая выполняемая команда
.........................
CSEG ENDS
END START
Здесь SSEG, DSEG, CSEG - имена сегментов, назначаемые пользователем.
На рис.2 приведен пример простой программы.При вводе исходного текста программы можно использовать как прописные, так и строчные буквы. С помощью ключа \ML при запуске транслятора можно указать транслятору различать прописные и строчные буквы в именах. Первое предложение программы в операторе segment открывает сегмент кода. В конце предложения после точки с запятой располагается комментарий. Предложение языка ассемблера может состоять из четырех полей: имени, оператора, операндов и комментария. Из них обязательным является только оператор.
2
. Рис. 2. Пример простой программы на языке ассемблера
Оператор assume в предложении 2 сообщает ассемблеру, что сегментный регистр CS будет указывать на сегмент кода text, а сегментный регистр DS - на сегмент данных data. При загрузке программы в память в сегментные регистры заносятся начальные адреса закрепленных за ними сегментов. Обращения к ячейкам программы осуществляются путем указания сегмента, в котором находится ячейка, и номера байта внутри сегмента. Этот номер называется смещением. Транслятор должен знать заранее, через какие сегментные регистры будут адресоваться ячейки программы, и ему сообщается об этом с помощью оператора assume, который должен предшествовать первой команде программы. При этом в регистр CS адрес начала сегмента будет загружен автоматически, а регистр DS следует инициализировать вручную, поскольку DS будет содержать адрес префикса программы PSP - системной области длиной 256 байтов, создаваемой DOS и расположенной непосредственно перед программой. PSP содержит информацию, необходимую DOS для взаимодействия с программой.
Физический адрес в реальном режиме вычисляется как содержимое сегментного регистра, умноженное на 16 (сдвинутое влево на 4 двоичных разряда), плюс значение смещения.
Операторы segment и assume не транслируются в машинные коды, а используются ассемблером на этапе трансляции программы. Такие нетранслируемые операторы называются
директивами ассемблера в отличие от команд языка.
Предложение 3 с меткой begin - первая исполняемая команда программы, точка входа. Метка точки входа программы должна указываться в качестве операнда самого последнего оператора программы end (см. предложение 18). В предложениях 3 и 4 выполняется инициализация сегментного регистра DS. Сначала значение имени data (т.е. адрес сегмента data) загружается командой mov в регистр AX, а затем из регистра AX переносится в сегментный регистр DS. Такая двухступенчатая загрузка необходима, потому что не существует команд непосредственной за-
грузки адреса в сегментный регистр. В списке операндов на первом месте стоит приемник, на
втором - источник информации.
Предложения 5, 6 и 7 выводят на экран строку текста. Здесь это делается путем обращения к служебным программам операционной системы MS DOS. Для этого надо загрузить в регистр AH номер требуемой функции, в другие регистры - исходные данные для выполнения этой функции, после чего выполнить команду int 21h (от interrupt - прерывание), которая передаст управление DOS. Вывод на экран строки текста можно осуществить с помощью различных функций DOS. Функция 09h требует, чтобы в регистре DX содержался адрес выводимой строки. В предложении 6 адрес строки mesg загружается в регистр DX, а в предложении 7 осуществляется вызов DOS.
3
В предложениях 5 и 7 указанные в тексте программы числа сопровождаются знаком h - признаком шестнадцатеричных чисел.
После того, как DOS выполнит затребованные действия, т.е. выведет на экран текст "Hello!", выполнение программы продолжится. Чтобы завершить программу, надо вызвать функцию DOS 4Ch. При этом в регистре AL должен находиться код завершения нашей программы, который она передаст DOS. Если программа завершилась успешно, код завершения должен быть равен нулю, поэтому в предложении 9 записывается 0 в регистр AL и вызывается DOS командой int 21h. Выполняемая часть программы на этом закончилась .Директива ends закрывает сегмент кода, перед ней указывается имя закрываемого сегмента - text.
Вслед за сегментом кода в данной программе описывается сегмент данных. Директивой ассемблера db мы вводим в программу строку, заключенную в апострофы. Символ $ является для функции DOS 09h признаком конца выводимой строки. Далее в программе на рис. 2 описан сегмент стека. Директивой
db размер dup (заполнитель)
для него выделено 256 байтов, заполненных нулями.
Оператор segment, начинающий сегмент стека, имеет описатель stack 'STACK'. Указание этого обозначения приводит к тому, что при загрузке программы в память регистры процессора, используемые для работы со стеком, загружаются автоматически: сегментный регистр стека SS будет настроен на начало сегмента стека, а указатель стека SP - на его конец, т.к. стек заполняется данными от конца к началу. При отсутствии описателя stack пришлось бы выполнять инициализацию регистров, связанных со стеком, вручную.
2. Подготовка программы к выполнению
Процесс подготовки и отладки программы на языке ассемблера включает этапы подготовки файла с исходным текстом, его трансляции, компоновки (см. рис. 3), и отладки программы с помощью специальной программы-отладчика.
|
Программа |
Программа |
|
ассемблера |
компоновщика |
|
Файл TASM.EXE |
Файл TLINK.EXE |
Исходный текст |
Объектный |
Исполнимый |
программы |
модуль |
модуль |
Файл P.ASM |
Файл P.OBJ |
Файл P.EXE |
Рис. 3. Процесс подготовки программы к выполнению
Трансляция исходного текста программы состоит в преобразовании предложений исходного языка в коды машинных команд и выполняется с помощью программы-ассемблера (т.е. транслятора с языка ассемблера). Наиболее распространенные ассемблеры - пакеты TASM фирмы Borland и MASM фирмы Microsoft. В результате трансляции создается объектный файл с расширением
.OBJ.
Компоновка объектного файла выполняется с помощью программы-компоновщика (редактора связей). Компоновщик необходимо брать из одного пакета с ассемблеров. В пакете фирмы Borland компоновщик называется TLINK.EXE. В результате компоновки создается загрузочный, или выполнимый, файл с расширением .EXE.
Для отладки программы при использовании пакета Borland следует взять отладчик Turbo Debugger (TD.EXE), при использовании пакета Microsoft - отдадчик Codeview (CV.EXE). При вы-
полнении лабораторных работ будет использоваться пакет фирмы Borland.
Если файл с исходным текстом программы назван P.ASM, то строка вызова ассемблера может
иметь следующий вид: |
|
tasm p |
tasm/z/zi p,p,p |
или |
Список ключей ассемблера можно получить, введя команду tasm. Этот список также приве-
ден в файле Methodic\ИПОВС\CompOrg|Info|tasminfo.txt (кодировка DOS!).
4
Стоящие далее параметры определяют имена файлов: исходного (P.ASM), объектного (P.OBJ) и листинга (P.LST). Расширения имен файлов можно не указывать.
Строка вызова компоновщика может иметь следующий вид: tlink/v p,p,p или tlink p
Список ключей компоновщика можно получить, введя команду tlink Этот список также при-
веден в файле Methodic\ИПОВС\CompOrg|Info|tlnkinfo.txt (кодировка DOS!). Компоновщик создает загрузочный модуль, готовый к выполнению, в формате EXE. Запуск P.EXE осуществля-
ется командой |
или просто p |
p.exe |
Для получения в отладчике максимума информации следует транслировать и компоновать программу с использованием ключей, указанных выше. Правила работы с отладчиком и его основные команды описаны в разделе 3.
Для удобства отладки большого количества программ следует создать командный файл (см. рис. 4). Приведенный текст составлен в предположении, что командный файл находится в одной директории со всеми остальными файлами. Удобнее отлаживать программы под одним и тем же именем, а после отладки записывать на свой личный диск под уникальным именем, соответствующим номеру лабораторной работы.
echo off |
echo Translating error! |
tasm /z/zi/n p,p,p |
goto fin |
if errorlevel 1 goto err |
:end |
tlink /v p,p,p |
echo Session ended |
goto end |
:fin |
:err |
echo on |
Рис. 4. Командный файл для отладки программы
|
|
|
|
Номера предложений программы |
|
|
|||||
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
Смещения в каждом сегменте |
|
|||
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
Машинные коды команд |
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
Исходный текст программы |
||
|
|
|
|
|
|
|
|
|
|
|
|
1 |
0000 |
|
|
|
|
text |
segment |
||||
|
|
|
|
||||||||
2 |
0000 |
B8 |
0000s |
assume |
CS:text,DS:data |
||||||
3 |
begin: |
mov |
AX,data |
||||||||
4 |
0003 |
8E |
D8 |
|
mov |
DS,AX |
|||||
5 |
0005 |
B4 |
09 |
|
mov |
AH,09h |
|||||
6 |
0007 |
BA 0000r |
|
mov |
DX,offset mesg; |
||||||
7 |
000A |
CD 21 |
|
int |
21h |
||||||
8 |
000C |
B4 |
4C |
|
mov |
AH,4Ch |
|||||
9 |
000E |
B0 |
00 |
|
mov |
AL,0 |
|||||
10 |
0010 |
CD 21 |
text |
int |
21h |
||||||
11 |
0012 |
|
|
|
|
ends |
|
Размер в байтах сегмента кода
Сегмент кода
12 |
0000 |
|
data |
segment |
'Hello!$' |
||
13 |
0000 |
48 65 6C 6C 6F 21 24 |
mesg db |
||||
14 |
0007 |
|
data |
ends |
|
||
|
|
|
|
Размер в байтах сегмента данных |
|
||
|
|
|
|
|
|||
15 |
0000 |
0100*(00) |
stk |
segment stack 'STACK' |
|||
16 |
0000 |
stk |
db |
256 dup (0) |
|||
17 |
0100 |
|
ends |
|
|||
|
|
|
|
Размер в байтах сегмента кода |
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
18 |
|
|
|
|
end begin |
|
Сегмент |
данных |
Сегмент |
стека |
Рис. 5. Листинг трансляции программы
На рис. 5 приведен листинг трансляции (файл P.ASM). Команды программы имеют различную длину и располагаются в памяти вплотную друг к другу. Предложения программы с операторами segment, assume, end не транслируются в машинные коды. Они нужны лишь для пе-
5
редачи транслятору служебной информации. Транслятор не смог полностью сгенерировать код команды mov AX,data. В этой команде в регистр AX засылается адрес сегмента text, однако, этот адрес станет известен лишь в процессе загрузки выполнимого файла программы в память. Поэтому в листинге на месте этого адреса стоят нули, а строка помечена символом s.
3.Отладка программы в отладчике Turbo Debugger
Отладчик запускается командой |
td p |
Вокне отладчика видны два окна - Module с исходным текстом отлаживаемой программы,
иокно Watches для наблюдения за ходом изменения заданных переменных. Последнее окно не понадобится, его можно закрыть.
Для получения детальной информации о содержимом окон пользуйтесь подсистемой помощи, которая вызывается клавишей F1. Первое предложение программы помечается значком треугольника ►. Этот значок перемещается по мере выполнения строк программы.
Пошаговое выполнение программы осуществляется клавишей F7. Для повторного выполнения следует выбрать меню Run|Program reset. Для выполнения программы до заданного
места следует поместить курсор (символ ▬ ) на нужную строку и нажать клавишу F4. Просмотр элемента данных. Для того чтобы вывести на экран содержимое поля данных,
поместите курсор на имя элемента данных, например, поля mesg, и выберите из меню Data|Inspect. В появившемся окне ввода переменной можно скорректировать имя интересующего нас поля данных или ввести новое. Если имя правильное, достаточно нажать клавишу
Enter.
В кадр отладчика будет выведено окно с характеристиками и содержимым указанной переменной. Отладчик сообщает, что переменная mesg хранится в памяти по адресу 1D18:000, т.е. имеет сегментный адрес 1D18h и смещение 0000h, и описана как последовательность из 10 байт. Тут же приводятся значения всех байтов переменной, включая их начертание на экране, десятичное и 16-ричное представление.
Вызов локального меню. Локальное (всплывающее) меню существует у каждого окна отладчика. Для его вызова нажмите правую кнопку мыши или Alt+F10, предварительно сделав соответствующее окно (подокно) активным щелчком мыши или клавишей Tab.
Изменение элемента данных. В окне Inspecting можно изменить значение отображаемого поля данных. Для этого надо, сделав это окно активным и поместив курсор на отображение конкретного элемента нашего символьного массива, например, элемента с индексом 8, вызвать локальное меню, из которого следует выбрать пункт Change. В открывшемся окне можно ввести новое значение символа, например '>' или можно ввести его 16-ричный код ASCII 3E иди десятичный код с буквой d на конце 62d.
Просмотр результатов работы программы. Для того чтобы, находясь в отладчике, увидеть результат работы программы, надо ввести команду Alt+F5 или выбрать из меню Window|User screen. Возврат в окно отладчика осуществляется нажатием любой клавиши.
Любые изменения, вносимые в текст программы в окне отладчика, будут действовать только до окончания сеанса отладки.
Переход в окно процессора. Для отладки программы с наблюдением за всеми регистрами, стеком и флагами, следует перейти в окно CPU. Для этого выберите из меню View|CPU. Откроется "окно процессора" (см. рис. 6). Оно состоит из пяти внутренних окон: текста программы с машинными кодами, регистров процессора, флагов, стека и содержимого памяти. В окне
кода символом ромба ♦помечены строки Вашего исходного текста.
Щелкнув мышью по значку стрелки ↑ в правом верхнем углу окна процессора, можно увеличить это окно до размеров окна отладчика.
Изменение содержимого регистров. По ходу пошагового выполнения программы можно изменять содержимое регистров. Для этого надо перейти в окно регистров, поместить курсор на строку с интересующим нас регистром и вызвать локальное меню. В нем выбрать пункт Change (для изменения содержимого регистра), Increment (для увеличения на 1), Decrement (для уменьшения на 1), Zero (для обнуления).
6
текст программы с машинными кодами
регистры
процессора
содержимое памяти
стек
Рис. 6. Окно CPU
флаги
Переход к 32-битным регистрам. В локальном меню окна регистров выберите пункт
Registers 32-bit.
Просмотр содержимого (дампа) памяти. Для просмотра любых участков памяти перейдите в окне CPU в окно данных. В локальном меню этого окна выберите пункт Goto для перехода к интересующему адресу памяти. В появившемся окне введите адрес в виде смещения, при этом сегментным регистром по умолчанию будет DS. Окно дампа будет отображать последовательные байты. Для просмотра дампа в другом виде, например, в виде последовательности слов, выберите из локального меню окна данных пункт Display as.
Для просмотра дампа памяти в более удобном виде выберите из главного меню View|Dump и увеличьте размер окна.
Порядок выполнения лабораторной работы
1.Наберите текст программы, приведенной на рис. 2.
2.Наберите командный файл, приведенный на рис. 4. В отчете объясните назначение ключей ассемблера и компоновщика, использованных в командном файле.
3.Оттранслируйте, скомпонуйте и выполните программу.
4.Запустите программу в отладчике.
•Выполните программу по шагам и проследите за изменением состояния регистров.
•Перейдите в режим отображения 32-битных регистров и повторите предыдущий пункт. Почему изменяются 32-битные регистры при изменении 16-битных регистров?
•Выполните программу до первой команды int 21h, затем измените содержимое регистра DX, введя в него значение 2, после чего выполните очередную команду. Поясните в отчете, что теперь выведено на экран и почему?
5.Добавьте в начало сегмента данных программы следующие директивы резервирования и
инициализации данных:
var1 |
db |
0ffh |
var2 |
dw |
3a7fh |
var3 |
dd |
0f54d567ah |
addr |
dw |
var3 |
full_ad |
dd |
var3 |
Прокомментируйте каждую из этих директив. Оттранслируйте и выполните программу. Запишите содержимое сегмента данных в шестнадцатеричном виде, полученное в отладчике, сравните его с листингом. Что содержится в переменных addr и full_ad? Просмотрите сегмент данных в виде слов, двойных слов и поясните отличия в виде дампа памяти. Добавьте директиву резервирования байта перед var1. Как изменился вид дамп памяти при просмотре в виде слов, двойных слов и почему?
7
6. Добавьте две команды записи в стек, например, запишите в стек содержимое регистров AX и BX (команды PUSH AX и PUSH BX). Выполните программу по шагам, следя за всеми регистрами, связанными со стеком. Прокомментируйте в отчете изменение их содержимого. Закомментируйте в программе описание сегмента стека. Выполните программу и выясните, где теперь располагается стек?
7.Постройте чертеж расположения в памяти сегментов (рис. 1), указав адрес начала каждого сегмента, его размер, размер пустого пространства между сегментами, точку входа. Воспользуйтесь информацией из листинга (или карты компоновки *.map) и отладчика.
8.Повторите эксперимент 7, добавив в директивы SEGMENT атрибут выравнивания BYTE (DWORD, PARA). Как влияют эти атрибуты на вид Вашего чертежа из п. 7?
9.Повторите эксперимент 7, изменив порядок следования сегментов: стек, данные, код.
10.В тексте программы закомментируйте строку 4 и выполните программу. Что будет выведено на экран и почему?
11.* Модифицируйте программу, используя упрощенные директивы сегментации. Запишите в отчете модифицированный текст.
Требования
1.При подготовке к лабораторной работе (дома) занесите в отчет:
•текст программы (рис. 2) с комментариями;
•пояснения к использованным ключам при вызове ассемблера и компоновщика;
•текст командного файла (рис. 4) и назначение его команд;
•директивы резервирования памяти из эксперимента 5 с комментариями.
2.По результатам выполненной лабораторной работы занесите в отчет описание экспериментов со всеми требуемыми пояснениями.
3.Студент должен знать ответы на следующие вопросы:
Вопросы
1.В чем отличие между директивами ассемблера и командами языка?
2.Что такое сегменты и сегментные регистры, каково их назначение?
3.Из каких шагов состоит процесс подготовки программы?
4.Что такое ассемблер, компоновщик? Каково их назначение?
5.Как осуществляется в изученном примере вывод на экран и выход из программы?
6.Уметь читать листинг ассемблера и карту компоновки.
7.Уметь пользоваться средствами отладчика, описанными в работе.
8.Уметь определять физический адрес по адресу, заданному в форме сегмент:смещение.
Источники информации
1. Рудаков П.И., Финогенов К.Г. Язык ассемблера: уроки программирования. - М.: ДИАЛОГМИФИ, 2001. (Глава 1)
---------------------------------------------------------------------------------------------------
© Лабораторная работа подготовлена Л.В. Илюшечкиной
* Необязательное задание. Оценивается дополнительными пунктами при условии, что студент полностью выполнил остальные задания.