assembler. Учебник для вузов_Юров В.И_2003 -637с (862834), страница 48
Текст из файла (страница 48)
В языке ассемблера это делается с помощью меток. Метка —это символическое имя, обозначающее определенную ячейку памяти и предназначенное для использования в качестве операнда в командах передачи управления.Подобно переменной, транслятор ассемблера присваивает любой метке три атрибута:При обсуждении архитектуры микропроцессора мы говорили, что команды извлекаются из памятизаранее в так называемый конвейер, поэтому адрес подлежащей выборке команды из памяти и содержимое пары CS:E(IP) — не одно и то же. Эта пара регистров содержит адрес команды в программе, которая будет выполняться следующей, а не той команды, которая пойдет в конвейер.Команды передачи управления211имя сегмента кода, где эта метка описана;смещение — расстояние в байтах от начала сегмента кода, в котором описанаметка;тип, или атрибут расстояния, метки.Последний атрибут может принимать два значения:near — переход на метку возможен только в пределах сегмента кода, где эта метка описана, то есть для перехода на метку физически достаточно изменить толькосодержимое регистра EIP/IP;far — переход на метку возможен только в результате межсегментной передачиуправления, для осуществления которой требуется изменение содержимого какрегистра EIP/IP, так и регистра CS.Метку можно определить двумя способами:оператором : (двоеточие);директивой LABELСинтаксис первого способа показан на рис.
10.1.KDСимволическое имяЦ Команда ассемблера LJРис. 10.1. Синтаксис описания метки с помощью оператораС помощью оператора: можно определить метку только ближнего типа — near.Определенную таким образом метку можно использовать в качестве операнда в командах условных переходов JCC и безусловного перехода JMP, CALL Эти команды,естественно, должны быть в том же сегменте кода, в котором определена метка.Команда ассемблера может находиться как на одной строке с меткой, так и на следующей.Во втором способе определения меток в программе используется директиваLABEL (рис.
10.2).—| Символическое имя |—| label | 1 Тип метки]—Рис. 10.2. Синтаксис директивы LABELТип метки может иметь значение near или far. Обычно директиву LABEL используют для определения идентификатора заданного типа. Например, следующиеописания меток ближнего типа эквивалентны:ml:mov a x , p o l e _ lmllabel nearmov a x , p o l e _ lПонятно, что метка может быть только одного типа — либо near, либо far. Есливозникает необходимость использовать для одной и той же команды метки и дальнего, и ближнего типов, то в этом случае необходимо определить две метки, причем метку дальнего типа нужно описать, используя директиву LABEL, как показанов следующем фрагменте:212publicГлава 10.
Команды передачи управленияm_far;сделать метку m_far видимой для внешних программm_farlabel far определение метки дальнего типа m_farm_near:определение метки ближнего типа n_farmov ax,pole_lОпределив для команды mov ax,pole_l две метки, можно организовывать переход на эту команду как из данного сегмента команд, так и из других сегментовкоманд, в том числе принадлежащих другим модулям. Для того чтобы сделать видимым извне имя метки m_far, применяется директива PUBLIC. К более подробному описанию этой директивы мы еще вернемся в главе 15.Другой часто встречающийся случай применения директивы LABEL — это организация доступа к области памяти, содержащей данные разных типов, например:mas_bmas_wlabelbytedw 15 dup (?)в этом фрагменте оба идентификатора относятся к одной областипамяти и дают возможность работать с ней, используя разныеимена, либо как с байтовым массивом, либо как с массивом словmov mas_b+10,al ;запись из al в массив байтов (в 11-й байт)mov mas_w,ax;запись из ах в первое слово области mas_wВспомним еще одно очень важное понятие ассемблера, имеющее прямое отношение к меткам, — счетчик адреса команд.
Мы уже упоминали о нем в первых главах и говорили, что транслятор ассемблера обрабатывает исходную программупоследовательно — команду за командой. При этом ведется счетчик адреса команд,который для первой исполняемой команды равен нулю, а далее, по ходу обработкиочередной команды транслятором, увеличивается на длину этой команды. По сути,счетчик адреса команд — это смещение конкретной команды относительно началасегмента кода. Таким образом, каждая команда во время трансляции имеет адрес,равный значению счетчика адреса команд. Обратитесь к главе 6 и еще раз посмотрите на приведенный в нем листинг 6.2. Первая колонка в листинге — номер строки файла листинга.
Вторая колонка (или третья, если присутствует колонка с уровнем вложенности) — смещение команды относительно начала сегмента кода или,как мы сейчас определили, счетчик адреса. Значение, на которое он увеличиваетсяпо мере обработки ассемблером очередной строки исходной программы, равно значению длины машинной команды в этой строке. Исходя из этого, ясно, почемусчетчик адреса увеличивается только после тех строк исходной программы, которые генерируют некоторое машинное представление (в том числе после директиврезервирования и инициализации данных в сегменте данных).Транслятор ассемблера обеспечивает нам две возможности работы с этим счетчиком:В использование меток, атрибуту смещения которых транслятор присваиваетзначение счетчика адреса следующей команды;II применение для обозначения счетчика адреса команд при задании операндовкоманд специального символа $, во время трансляции заменяемого текущимчисленным значением счетчика адреса.Команды передачи управления213Классический пример:.dataвычисление длины строки в сегменте данныхStr_Mes db "Займись делом - учи ассемблер ..."Len_Msg=$-Str_MesПосле ассемблирования значение Len_Msg будет равно длине строки, так какзначение символа $ в месте его появления отличается от Str_Mes ровно на длинустроки.Другое применение, избавляющее программиста от необходимости «плодить»лишние метки в программе, — реализация близкого, буквально через следующуюили предыдущую команды, перехода.
Для примера рассмотрим фрагмент программы из листинга 6.1 (см. главу 6):;M1:;M2:cmp dl,9hjle $+5sub dl,7hmov cl,4hcmp al,9hjle $+4sub al,7h;сравнить (dl) с 9h;перейти на команду mov cl.,4h, если dl<9h или dl=9h;вычитание: (dl)=(dl)47h;пересылка 4h в регистр cl;сравнить (al) с 9h 28;перейти на команду add dl.al, если al<9h или al=9h;вычитание: (al)=(al)47hadd dl.alКак узнать правильные значения длины команд? Во-первых, по опыту, то.естьпросто догадаться.
Во-вторых, из файла листинга, что не всегда дает результат, таккак конечные машинные команды (колонка файла листинга с объектным кодом)в нем не всегда до конца сформированы. В-третьих, узнать длины команд можно,загрузив программу в отладчик и активировав окно CPU.
Последний способ самыйточный. Не будет ничего страшного, если для первого прогона исполняемого модуля программы в нем будут неверные значения для подобных относительных переходов. И еще одно замечание: при переходе вперед необходимо учитывать длину текущей команды перехода, при переходе назад этого делать не нужно.Кроме возможности получения значения счетчика адреса компилятор позволяет при необходимости установить счетчик адреса в нужное абсолютное значение. Это делается с помощью директивы ORG:ORG выражениеЗдесь выражение должно быть таким, чтобы ассемблер мог преобразовать егок абсолютному числу при первом проходе трансляции.1К примеру, эту директиву всегда используют при создании исполняемого файла с расширением .com.
В контексте нашего обсуждения поясним, в чем здесь суть.В главе 5 мы обсуждали сегментацию и деление программы на сегменты. Программав формате СОМ состоит из одного сегмента величиной не более 64 Кбайт. Сегментные регистры CS и DS содержат одно и то же значение физического адреса, а регистр SS указывает на конец этого единственного сегмента. Программа-загрузчикоперационной системы, считывая с диска исполняемые файлы с расширениями.ехе и .com, производит определенные действия. В частности, настраивает переме-214Глава 10.
Команды передачи управлениящаемые адреса программ на их конкретные физические значения. Кроме того,к началу каждой исполняемой программы в памяти добавляется специальная область величиной 256 байт (lOOh) — префикс программного сегмента (PSP). Он предназначен для хранения различной информации о загруженном исполняемом модуле. Для программ формата СОМ блок PSP находится в начале сегмента размеромв 64 Кбайт. В исходной программе, для исполняемого файла которой планируетсяформат СОМ, мы должны предусмотреть место для блока Р5Р,-что и делается директивой org lOOh.Чтобы закончить разговор о файлах этого типа, разберемся с тем, как получитьисполняемый модуль формата СОМ. Трансляция программы выполняется какобычно. Далее возможны два варианта действий.^ Во-первых, возможно использование утилиту tlink с ключом /t:t l i n k / t имя_объектного_файлаЭтот вариант подходит только в том случае, если вы правильно оформили исходный текст программы для формата СОМ.