Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Labs / 2_Debug / Lab2

.pdf
Скачиваний:
20
Добавлен:
16.04.2013
Размер:
419.53 Кб
Скачать

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)

---------------------------------------------------------------------------------------------------

© Лабораторная работа подготовлена Л.В. Илюшечкиной

* Необязательное задание. Оценивается дополнительными пунктами при условии, что студент полностью выполнил остальные задания.