Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Представление команд. Модификация адресов.




    1.3.1 Структура команд. Исполнительные адреса

Машинные команды ПК занимают от 1 до 6 байтов.

Код операции (КОП) занимает один или два первых байта команды. В ПК столь много различных операций, что для них не хватает 256 различ­ных КОПов, которые можно представить в одном байте. Поэтому некоторые операции объединяются в группу и им дается один и тот же КОП, во вто­ром же байте этот КОП уточняется. Кроме того, во втором байте указыва­ются типы и способ адресации операндов. Остальные байты команды указы­вают на операнды.

Команды могут иметь от 0 до 3 операндов, у большинства команд ­один или два операнда. Размер операндов - байт или слово (редко ­двойное слово). Операнд может быть указан в самой команде (это т.н. непосредственный операнд), либо может находиться в одном из регистров ПК и тогда в команде указывается этот регистр, либо может находиться в ячейке памяти и тогда в команде тем или иным способом указывается ад­рес этой ячейки. Некоторые команды требуют, чтобы операнд находился в фиксированном месте (например, в регистре AX), тогда операнд явно не указывается в команде. Результат выполнения команды помещается в ре­гистр или ячейку памяти, из которого (которой), как правило, берется первый операнд. Например, большинство команд с двумя операндами реали­зуют действие

   op1:= op1 _ op2

где op1 - регистр или ячейка, а op2 - непосредственный операнд, ре­гистр или ячейка.

Адрес операнда разрешено модифицировать по одному или двум регист­рам. В первом случае в качестве регистра-модификатора разрешено ис­пользовать регистр BX, BP, SI или DI (и никакой иной). Во втором слу­чае один из модификаторов обязан быть регистром BX или BP, а другой -

регистром SI или DI; одновременная модификация по BX и BP или SI и DI недопустима. Регистры BX и BP обычно используются для хранения базы (начального адреса) некоторого участка памяти (скажем, массива) и по­тому называются базовыми регистрами, а регистры SI и DI часто содержат индексы элементов массива и потому называются индексными регистрами. Однако такое распределение ролей необязательно, и, например, в SI мо­жет находиться база массива, а в BX - индекс элемента массива.

В MASM адреса в командах записываются в виде одной из следующих конструкции:

      A, A[M] или A[M1][M2],

где A - адрес, M - регистр BX, BP, SI или DI, M1 - регистр BX или BP, а M2 - регистр SI или DI. Во второрм и третьем варианте A может отсут­ствовать, в этом случае считается, что A=0.

При выполнении команды процессор прежде всего вычисляет т.н. ис­полнительный (эффективный) адрес - как сумму адреса, заданного в ко­манде, и текущих значений указанных регистров-модификаторов, причем все эти величины рассматриваются как неотрицательные и суммирование

ведется по модулю 2^16 ([r] означает содержимое регистра r):

  A: Aисп = A

  A[M]: Aисп = A+[M] (mod 2^16)

  A[M1][M2]: Aисп = A+[M1]+[M2] (mod 2^16)

Полученный таким образом 16-разрядный адрес определяет т.н. смеще­ние - адрес, отсчитанный от начала некоторого сегмента (области) памя­ти. Перед обращением к памяти процессор еще добавляет к смещению на­чальный адрес этого сегмента (он хранится в некотором сегментном реги­стре), в результате чего получается окончательный 20-разрядный ад­рес, по которому и происходит реальное обращение к памяти (см. 1.4).

    1.3.2 Форматы команд

В ПК форматы машинных команд достаточно разнообразны. Для примера приведем лишь основные форматы команд с двумя операндами.

1) Формат "регистр-регистр" (2байта):

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

     | КОП |d|w| | 11 |reg1|reg2|

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

      7 2 1 0 7 6 5 3 2 0

Команды этого формата описывают обычно действие reg1:=reg1_reg2 или

reg2:=reg2_reg1. Поле КОП первого байта указывает на операцию (_), ко­торую надо выполнить. Бит w определяет размер операндов, а бит d ука­зывает, в какой из регистров записывается результат:

  w = 1 - слова d = 1 - reg1:=reg1_reg2

    = 0 - байты   = 0 - reg2:=reg2_reg1

Во втором байте два левых бита фиксированы (для данного формата), а трехбитовые поля reg1 и reg2 указывают на регистры, участвующие в опе­рации, согласно следующей таблице:

reg w=1 w=0    reg w=1 w=0 -----------------  ----------------­

000 AX AL    100 SP AH

    001 CX CL    101 BP CH

    010 DX DL      110 SI DH

    011 BX BL    111 DI BH

2) Формат "регистр-память" (2-4 байта):

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

  | КОП |d|w| |mod|reg|mem| |адрес (0-2 байта)|

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

Эти команды описывают операции reg:=reg_mem или mem:=mem_reg. Бит w первого байта определяет размер операндов (см. выше), а бит d указыва­ет, куда записывается результат: в регистр (d=1) или в ячейку памяти (d=0). Трехбитовое поле reg второго байта указывает операнд-регистр (см. выше), двухбитовое поле mod определяет, сколько байтов в команде занимает операнд-адрес (00 - 0 байтов, 01 - 1 байт, 10 - 2 байта), а трехбитовое поле mem указывает способ модификации этого адреса. В сле­дующей таблице указаны правила вычисления исполнительного адреса в за­висимости от значений полей mod и mem (a8 - адрес размером в байт, a16

- адрес размером в слово):

mem \ mod | 00 01  10

------------------------------------------------------­000 | [BX]+[SI] [BX]+[SI]+a8 [BX]+[SI]+a16

001  | [BX]+[DI] [BX]+[DI]+a8 [BX]+[DI]+a16

010  | [BP]+[SI] [BP]+[SI]+a8 [BP]+[SI]+a16

011  | [BP]+[DI] [BP]+[DI]+a8 [BP]+[DI]+a16

100  | [SI]  [SI]+a8  [SI]+a16

101  | [DI]  [DI]+a8  [DI]+a16

110  | a16   [BP]+a8  [BP]+a16

111  | [BX]  [BX]+a8  [BX]+a16

Замечания. Если в команде не задан адрес, то он считается нулевым. Если адрес задан в виде байта (a8), то он автоматически расширяется со знаком до слова (a16). Случай mod=00 и mem=110 указывает на отсутствие регистров-модификаторов, при этом адрес должет иметь размер слова (ад­ресное выражение [BP] ассемблер транслирует в mod=01 и mem=110 при a8=0). Случай mod=11 соответствует формату "регистр-регистр".

3) Формат "регистр-непосредственный операнд" (3-4 байта): ----------- ------------- --------------------------

| КОП |s|w| |11|КОП"|reg| |непосред.операнд (1-2 б)|

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

Команды этого формата описывают операции reg:=reg_immed (immed - не­посредственный операнд). Бит w указывает на размер операндов, а поле reg - на регистр-операнд (см. выше). Поле КОП в первом байте определя­ет лишь класс операции (например, класс сложения), уточняет же опера­цию поле КОП" из второго байта. Непосредственный операнд может зани­мать 1 или 2 байта - в зависимости от значения бита w, при этом опе­ранд-слово записывается в команде в "перевернутом" виде. Ради экономии памяти в ПК предусмотрен случай, когда в операции над словами непос­редственный операнд может быть задан байтом (на этот случай указывает 1 в бите s при w=1), и тогда перед выполнением операции байт автомати­чески расширяется (со знаком) до слова.

4) Формат "память-непосредственный операнд" (3-6 байтов):

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

| КОП |s|w| |mod|КОП"|mem| |адрес (0-2б)| |непоср.оп (1-2б)|

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

Команды этого формата описывают операции типа mem:=mem_immed. Смысл всех полей - тот же, что и в предыдущих форматах.

Помимо рассмотренных в ПК используются и другие форматы команды с двумя операндами; так, предусмотрен специальный формат для команд, один из операндов которых фиксирован (обычно это регистр AX). Имеют свои форматы и команды с другим числом операндов.

    1.3.3 Запись команд в MASM

Из сказанного ясно, что одна и та же операция в зависимости от ти­пов операдов записывается в виде различных машинных команд: например, в ПК имеется 28 команд пересылки байтов и слов. В то же время в MASM

все эти "родственные" команды записываются единообразно: например, все команды пересылки имеют одну и ту же символьную форму записи:

    MOV op1,op2 (op1:=op2)

Анализируя типы операндов, ассемблер сам выбирает подходящую машинную команду.

В общем случае команды записываются в MASM следующим образом:

    метка: мнемокод операнды;комментарий

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

Мнемонические названия операций полностью перечислены в главе 2. Операнды, если есть, перечисляются через запятую. Основные правила

записи операндов следующие.

Регистры указываются своими именами, например:

    MOV AX,SI;оба операнда - регистры

Непосредственные операнды задаются константными выражениями (их значениями являются константы-числа), например:

    MOV BH,5;5 - непосредственный операнд

MOV DI,SIZE X;SIZE X (число байтов, занимаемых перемен­;ной X) - непосредственный операнд

Адреса описываются адресными выражениями (например, именами пере­менных), которые могут быть модифицированы по одному или двум регист­рам; например, в следующих командах первые операнды задают адреса:

    MOV X,AH

    MOV X[BX][DI],5

    MOV [BX],CL

При записи команд в символьной форме необходимо внимательно сле­дить за правильным указанием типа (размера) операндов, чтобы не было ошибок. Тип обычно определяется по внешнему виду одного из них, напри­мер:

    MOV AH,5;пересылка байта, т.к. AH - байтовый регистр

    MOV AX,5;пересылка слова, т.к. AX - 16-битовый регистр

;(операнд 5 может быть байтом и словом, по нему;нельзя определить размер пересылаемой величины)

MOV [BX],300;пересылка слова, т.к. число 300 не может быть;байтом

Если по внешнему виду можно однозначно определить тип обоих опе­рандов, тогда эти типы должны совпадать, иначе ассемблер зафиксирует ошибку. Примеры:

    MOV DS,AX;оба операнда имеют размер слова

    MOV CX,BH;ошибка: регистры CX и BH имеют разные размеры

MOV DL,300;ошибка: DL - байтовый регистр, а число 300 не;может быть байтом

Возможны ситуации, когда по внешнему виду операндов нельзя опреде­лить тип ни одного из них, как, например, в команде

    MOV [BX],5

Здесь число 5 может быть и байтом, и словом, а адрес из регистра BX может указывать и на байт памяти, и на слово. В подобных ситуациях ас­семблер фиксирует ошибку. Чтобы избежать ее, надо уточнить тип одного из операндов с помощью оператора с названием PTR:

    MOV BYTE PTR [BX],5;пересылка байта

    MOV WORD PTR [BX],5;пересылка слова

(Операторы - это разновидность выражений языка MASM, аналогичные функ­циям.)

Оператор PTR необходим и в том случае, когда надо изменить тип, предписанный имени при его описании. Если, например, X описано как имя переменной размером в слово:

     X DW 999

и если надо записать в байтовый регистр AH значение только первого байта этого слова, тогда воспользоваться командой

     MOV AH,X

нельзя, т.к. ее операнды имеют разный размер. Эту команду следует за­писать несколько иначе:

     MOV AH,BYTE PTR X

Здесь конструкция BYTE PTR X означает адрес X, но уже рассматриваемый не как адрес слова, а как адрес байта. (Напомним, что с одного и того же адреса может начинаться байт, слово и двойное слово; оператор PTR

уточняет, ячейку какого размера мы имеем в виду.)

И еще одно замечание. Если в символьной команде, оперирующей со словами, указан непосредственный операнд размером в байт, как, напри­мер, в команде

      MOV AX,80h

то возникает некоторая неоднозначность: что будет записано в регистр AX - число 0080h (+128) или 0FF80h (-128)? В подобных ситуациях ассем­блер формирует машинную команду, где операнд-байт расширен до слова, причем расширение происходит со знаком, если операнд был записан как отрицательное число, и без знака в остальных случаях. Например:

    MOV AX,-128; => MOV AX,0FF80h (A:=-128)

MOV AX,128; => MOV AX,0080h (A:=+128) MOV AX,80h; => MOV AX,0080h (A:=+128)

 

СЕГМЕНТИРОВНИЕ

    1.4.1 Сегменты памяти. Сегментные регистры.

Первые модели ПК имели оперативную память объемом 2^16 байтов (64Кб) и потому использовали 16-битовые адреса. В последующих моделях память была увеличена до 2^20 байтов (1Мб=1000Кб), для чего уже необ­ходимы 20-битовые адреса. Однако в этих ПК ради сохранения преемствен­ности были сохранены 16-битовые адреса: именно такие адреса хранятся в регистрах и указываются в командах, именно такие адреса получаются в результате модмфикации по базовым и индексным регистрам. Как же удает­ся 16-битовыми адресами ссылаться на 1Мб памяти?

Эта проблема решается с помощью сегментирования адресов (неявного базирования адресов). В ПК вводится понятие "сегмент памяти". Так на­зывается любой участок памяти размером до 64Кб и с начальным адресом, кратным 16. Абсолютный (20-битовый) адрес A любой ячейки памяти можно

представить как сумму 20-битового начального адреса (базы) B сегмента, которому принадлежит ячейка, и 16-битового смещения D - адреса этой ячейки, отсчитанного от начала сегмента: A=B+D. (Неоднозначность выбо­ра сегмента не играет существенной роли, главное - чтобы сумма B и D давала нужный адрес.) Адрес B заносится в некоторый регистр S, а в ко­манде, где должен быть указан адрес A, вместо него записывается пара из регистра S и смещения D (в MASM такая пара, называемая адресной па­рой или указателем, записывается как S:D). Процессор же устроен так, что при выполнении команды он прежде всего по паре S:D вычисляет абсо­лютный адрес A как сумму содержимого регистра S и смещения D и только затем обращается к памяти по этому адресу A. Вот так, заменяя в коман­дах абсолютные адреса на адресные пары, и удается адресовать всю па­мять 16-битовыми адресами (смещениями).

В качестве регистра S разрешается использовать не любой регистр, а только один из 4 регистров, называемых сегментными: CS, DS, SS и ES. В связи с этим одновременно можно работать с 4 сегментами памяти: начало одного из них загружается в регистр CS и все ссылки на ячейки этого сегмента указываются в виде пар CS:D, начало другого заносится в DS и все ссылки на его ячейки задаются в виде пар DS:D и т.д. Если одновре­менно надо работать с большим числом сегментов, тогда нужно своевре­менно спасать содержимое сегментных регистров и записывать в них на­чальные адреса пятого, шестого и т.д. сегментов.

Отметим, что используемые сегменты могут быть расположены в памяти произвольным образом: они могут не пересекаться, а могут пересекаться и даже совпадать. Какие сегменты памяти использовать, в каких сегмент­ных регистрах хранить их начальные адреса - все это личное дело автора машинной программы.

Как и все регистры ПК, сегментные регистры имеют размер слова. По­этому возникает вопрос: как удается разместить в них 20-битовые на­чальные адреса сегментов памяти? Ответ такой. Поскольку все эти адреса кратны 16 (см. выше), то в них младшие 4 бита (последняя 16-ричная цифра) всегда нулевые, а потому эти биты можно не хранить явно, а лишь подразумевать. Именно так и делается: в сегментном регистре всегда хранятся только первые 16 битов (первые четыре 16-ричные цифры) на­чального адреса сегмента (эта величина называется номером сегмента или просто сегментом). При вычислении же абсолютного адреса A по паре S:D процессор сначала приписывает справа к содержимому регистра S четыре нулевых бита (другими словами, умножает на 16) и лишь затем прибавляет смещение D, причем суммирование ведется по модулю 2^20:

     Aабс = 16*[S]+D (mod 2^20)

Если, например, в регистре CS хранится величина 1234h, тогда адресная пара 1234h:507h определяет абсолютный адрес, равный 16*1234h+507h = 12340h+507h = 12847h.

    1.4.2 Сегментные регистры по умолчанию

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

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

Что это за договоренность?

Считается, что регистр CS всегда указывает на начало области памя­ти, в которой размещены команды программы (эта область называется сег­ментом команд или сегментом кодов), и потому при ссылках на ячейки этой области регистр CS можно не указывать явно, он подразумевается по умолчанию. (Отметим попутно, что абсолютный адрес очередной команды, подлежащей выполнению, всегда задается парой CS:IP: в счетчике команд IP всегда находится смещение этой команды относительно адреса из реги­стра CS.) Аналогично предполагается, что регистр DS указывает на сег­мент данных (область памяти с константами, переменными и другими вели­чинами программы), и потому во всех ссылках на этот сегмент регистр DS можно явно не указывать, т.к. он подразумевается по умолчанию. Регистр SS, считается, указывает на стек - область памяти, доступ к которой осуществляется по принципу "последним записан - первым считан" (см. 1.7), и потому все ссылки на стек, в которых явно не указан сегментный регистр, по умолчанию сегментируются по регистру SS. Регистр ES счита­ется свободным, он не привязан ни к какому сегменту памяти и его можно использовать по своему усмотрению; чаще всего он применяется для дос­тупа к данным, которые не поместились или сознательно не были размеще­ны в сегменте данных.

С учетом такого распределения ролей сегментных регистров машинные программы обычно строятся так: все команды программы размещаются в од­ном сегменте памяти, начало которого  заносится в регистр CS, а все данные размещаются в другом сегменте, начало которого заносится в ре­гистр DS; если нужен стек, то под него отводится третий сегмент памя­ти, начало которого записывается в регистр SS. После этого практически во всех командах можно указывать не полные адресные пары, а лишь сме­щения, т.к. сегментные регистры в этих парах будут восстанавливаться автоматически.

Здесь, правда, возникает такой вопрос: как по смещению определить, на какой сегмент памяти оно указывает? Точный ответ приведен ниже (см. 1.4.3), а в общих чертах он такой: ссылки на сегмент команд могут быть только в командах перехода, а ссылки практически во всех других коман­дах (кроме строковых и стековых) - это ссылки на сегмент данных. Нап­ример, в команде пересылки

    MOV AX,X

имя X воспринимается как ссылка на данное, а потому автоматически вос­станавливается до адресной пары DS:X. В команде же безусловного пере­хода по адресу, находящемуся в регистре BX,

    JMP BX

абсолютный адрес перехода определяется парой CS:[BX].

Итак, если в ссылке на какую-то ячейку памяти не указан явно сег­ментный регистр, то этот регистр берется по умолчанию. Явно же сегмен­тные регистры надо указывать, только если по каким-то причинам регистр по умолчанию не подходит. Если, например, в команде пересылки нам надо сослаться на стек (скажем, надо записать в регистр AH байт стека, по­меченный именем X), тогда нас уже не будет устраивать договоренность о том, что по умолчанию операнд команды MOV сегментируется по регистру DS, и потому мы обязаны явно указать иной регистр - в нашем случае ре­гистр SS, т.к. именно он указывает на стек:

    MOV AH,SS:X

Однако такие случаи встречаются редко и потому в командах, как прави­ло, указываются только смещения.

Отметим, что в MASM сегментный регистр записывается в самой коман­де непосредственно перед смещением (именем переменной, меткой и т.п.), однако на уровне машинного языка ситуация несколько иная. Имеется 4 специальные однобайтовые команды, называемые префиксами замены сегмен­та (обозначаемые как CS:, DS:, SS: и ES:). Они ставятся перед коман­дой, операнд-адрес которой должен быть просегментирован по регистру, отличному от регистра, подразумеваемому по умолчанию. Например, приве­денная выше символическая команда пересылки - это на самом деле две машинные команды:

       SS:

       MOV AH,X

1.4.3 Сегментирование, базирование и индексирование адресов Поскольку сегментирование адресов - это разновидность модификации

адресов, то в ПК адрес, указываемый в команде, в общем случае модифи­цируется по трех регистрам - сегментному, базовому и индексному. В це­лом, модификация адреса производится в два этапа. Сначала учитываются только базовый и индексный регистры (если они, конечно, указаны в ко­манде), причем вычисление здесь происходит в области 16-битовых адре­сов; полученный в результате 16-битовый адрес называется исполнитель­ным (эффективным) адресом. Если в команде не предусмотрено обращение к памяти (например, она загружает адрес в регистр), то на этом модифика­ция адреса заканчивается и используется именно исполнительный адрес (он загружается в регистр). Если же нужен доступ к памяти, тогда на втором этапе исполнительный адрес рассматривается как смещение и к не­му прибавляется (умноженное на 16) содержимое сегментного регистра, указанного явно или взятого по умолчанию, в результате чего получается абсолютный (физический) 20-битовый адрес, по которому реально и проис­ходит обращение к памяти.

Отметим, что сегментный регистр учитывается только в "последний" момент, непосредственно перед обращением к памяти, а до этого работа ведется только с 16-битовыми адресами. Если учесть к тому же, что сег­ментные регистры, как правило, не указываются в командах, то можно в общем-то считать, что ПК работает с 16-битовыми адресами.

Как уже сказано, если в ссылке на ячейку памяти не указан сегмент­ный регистр, то он определяется по умолчанию. Это делается по следую­щим правилам.

1) В командах перехода адрес перехода сегментируется по регистру CS и только по нему, т.к. абсолютный адрес команды, которая должна быть выполнена следующей, всегда определяется парой CS:IP (попытка из­менить в таких командах сегментный регистр будет безуспешной).

Отметим, что сегментиорвание по регистру CS касается именно адреса

перехода, а не адреса той ячейки, где он может находиться. Например, в команде безусловного перехода по адресу, находящемуся в ячейке X:

    JMP X

имя X сегментируется по регистру DS, а вот адрес перехода, взятый из ячейки X, уже сегментируется по регистру CS.

2) Адреса во всех других командах, кроме строковых (STOS, MOVS, SCAS и CMPS), по умолчанию сегментируются:

- по регистру DS, если среди указанных регистров-модификаторов нет регистра BP;

- по регистру SS, если один из модификаторов - регистр BP.

Таким образом, адреса вида A, A[BX], A[SI], A[DI], A[BX][SI] и A[BX][DI] сегментируются по регистру DS, а адреса A[BP], A[BP][SI] и A[BP][DI] - по регистру SS, т.е. адреса трех последних видов использу­ются для доступа к ячейкам стека.

3) В строковых командах STOS, MOVS, SCAS и CMPS, имеющих два опе­ранда-адреса, на которые указывают индексные регистры SI и DI, один из операндов (на который указывает SI) сегментируется по регистру DS, а другой (на него указывает DI) - по регистру ES.

    1.4.4 Программные сегменты. Директива ASSUME

Рассмотрим, как сегментирование проявляется в программах на MASM.

Для того чтобы указать, что некоторая группа предложений программы на MASM образуют единый сегмент памяти, они оформляются как программ­ный сегмент: перед ними ставится директива SEGMENT, после них - дирек­тива ENDS, причем в начале обеих этих директив должно быть указано од­но и то же имя, играющее роль имени сегмента. Программа же в целом представляет собой последовательность таких программных сегментов, в конце которой указывается директива конца программы END, например:

DT1 SEGMENT;программный сегмент с именем DT1 A DB 0

B DW? DT1 ENDS

;

DT2 SEGMENT;программный сегмент DT2

C DB 'hello'

DT2 ENDS

;

CODE SEGMENT;программный сегмент CODE

   ASSUME CS:CODE, DS:DT1, ES:DT2

BEG: MOV AX,DT2

   MOV DS,AX

   MOV BH,C

  ...

CODE ENDS

   END BEG;конец текста программы

Предложения программного сегмента ассемблер размещает в одном сег­менте памяти (в совокупности они не должны занимать более 64Кб) начи­ная с ближайшего свободного адреса, кратного 16. Номер (первые 16 би­тов начального адреса) этого сегмента становится значением имени сег­мента. В MASM это имя относится к константным выражениям, а не адрес-

ным, в связи с чем в команде

  MOV AX,DT2

второй операнд является непосредственным, поэтому в регистр AX будет записано начало (номер) сегмента DT2, а не содержимое начальной ячейки этого сегмента.

Имена же переменных (A, B, C) и метки (BEG) относятся к адресным выражениям, и им ставится в соответствие адрес их ячейки относительно "своего" сегмента: имени A соответствует адрес 0, имени B - адрес 1, имени C - адрес 0, а метке BEG - адрес 0.

Все ссылки на предложения одного программного сегмента ассемблер сегментирует по умолчанию по одному и тому же сегментному регистру. По какому именно - устанавливается специальной директивой ASSUME. В нашем примере эта директива определяет, что все ссылки на сегмент CODE долж­ны, если явно не указан сегментный регистр, сегментироваться по регис­тру CS, все ссылки на DT1 - по регистру DS, а все ссылки на DT2 - по регистру ES.

Встретив в тексте программы ссылку на какое-либо имя (например, на имя C в команде MOV AX,C), ассемблер определяет, в каком программном сегменте оно описано (у нас - в DT2), затем по информации из директивы ASSUME узнает, какой сегментный регистр поставлен в соответствие этому сегменту (у нас - это ES), и далее образует адресную пару иэ данного регистра и смещения имени (у нас - ES:0),  которую и записывает в фор­мируемую машинную команду. При этом ассемблер учитывает используемое в ПК соглашение о сегментных регистрах по умолчанию: если в адресной па­ре, построенной им самим или явно заданной в программе, сегментный ре­гистр совпадает с регистром по умолчанию, то в машинную команду зано­сится лишь смещение. Если, скажем, в нашем примере встретится команда MOV CX,B, тогда по имени В ассемблер построит пару DS:1, но раз опе­ранд-адрес команды MOV по умолчанию сегментируется по регистру DS, то записывать этот регистр в машинную команду излишне и ассемблер записы­вает в нее только смещение 1.

Таким образом, директива ASSUME избавляет программистов от необхо­димости выписывать полные адресные пары не только тогда, когда исполь­зуются сегментные регистры по умолчанию (как в случае с именем B), но тогда, когда в машинной команде нужно было бы явно указать сегментный регистр (как в случае с именем C). В MASM сегментный регистр в ссылке на имя требуется указывать лишь тогда, когда имя должно по каким-либо причинам сегментироваться по регистру, отличному от того, что постав­лен в соответствие всему сегменту, в котором это имя описано.

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

 

    1.4.5 Начальная загрузка сегментных регистров

Директива ASSUME сообщает ассмеблеру о том, по каким регистрам он должен сегментировать имена из каких сегментов, и "обещает", что в этих регистрах будут находиться начальные адреса этих сегментов. Одна­ко загрузку этих адресов в регистры сама директива не осуществляет. Сделать такую загрузку - обязанность самой программы, с загрузки сег­ментных регистров и должно начинаться выполнение программы. Делается это так.

Поскольку в ПК нет команды пересылки непосредственного операнда в сегментный регистр (а имя, т.е. начало, сегмента - это непосредствен­ный операнд), то такую загрузку приходится делать через какой-то дру­гой, несегментный, регистр (например, AX):

    MOV AX,DT1;AX:=начало сегмента DT1

    MOV DS,AX;DS:=AX

Аналогично загружается и регистр ES.

Загружать регистр CS в начале программы не надо: он, как и счетчик команд IP, загружается операционной системой перед тем, как начинается выполнение программы (иначе нельзя было бы начать ее выполнение). Что же касается регистра SS, используемого для работы со стеком, то он мо­жет быть загружен так же, как и регистры DS и ES, однако в MASM преду­смотрена возможность загрузки этого регистра еще до выполнения прог­раммы (см. 1.7).

 

    1.4.6 Ссылки вперед

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

В подобной ситуации ассемблер действует следующим образом: если в команде встретилась ссылка вперед, то он делает некоторое предположе­ние относительно этого имени и уже на основе этого предположения фор­мирует машинную команду. Если затем (когда встретится описание имени) окажется, что данное предположение было неверным, тогда ассемблер пы­тается исправить сформированнную им ранее машинную команду. Однако это не всегда удается: если правильная машинная команда должна занимать больше места, чем машинная команда, построенная на основе предположе­ния (например, перед командой надо на самом деле вставить префикс за­мены сегмента), тогда ассемблер фиксирует ошибку (как правило, это ошибка номер 6: Phase error between passes.)

Какие же предположения делает ассемблер, встречая ссылку вперед? Во всех командах, кроме команд перехода (о них см. 1.5), ассемблер предполагает, что имя будет описано в сегменте данных и потому сегмен­тируется по регистру DS. Это следует учитывать при составлении прог­раммы: если в команде встречается ссылка вперед на имя, которое описа­но в сегменте, на начало которого указывает сегментный регистр, отлич­ный от DS, то перед таким именем автор программы должен написать соот­вествующмй префикс. Пример:

  code segment

       assume cs:code

     x dw?

beg: mov ax,x;здесь вместо cs:x можно записать просто x mov cs:y,ax;здесь обязательно надо записать cs:y

      ...

y dw?

code ends

ПЕРЕХОДЫ

 

В систему команд ПК входит обычный для ЭВМ набор команд перехода: безусловные и условные переходы, переходы с возвратами и др. Однако в ПК эти команды имеют некоторые особенности, которые здесь и рассматри­ваются.

Абсолютный адрес команды, которая должна быть выполнена следующей, определяется парой CS:IP, поэтому выполнение перехода означает измене­ние этих регистров, обоих или только одного (IP). Если изменяется только счетчик команд IP, то такой переход называется внутрисегментным или близким (управление остается в том же сегменте команд), а если ме­няются оба регистра CS и IP, то это межсегментный или дальний переход (начинают выполняться команды из другого сегмента команд). По способу изменения счетчика команд переходы делятся на абсолютные и относитель­ные. Если в команде перехода указан адрес (смещение) той команды, ко­торой надо передать управление, то это абсолютный переход. Однако в команде может быть указана величина (сдвиг), которую надо добавить к текущему значению регистра IP, чтобы получился адрес перехода, и тогда это будет относительный переход; при этом сдвиг может быть положитель­ным и отрицательным, так что возможен переход вперед и назад. По вели­чине сдвига относительные переходы делятся на короткие (сдвиг задается байтом) и длинные (сдвиг - слово). Абсолютные же переходы делятся на прямые и косвенные: при прямом переходе адрес перехода задается в са­мой команде, а при косвенном - в команде указывается регистр или ячей­ка памяти, в котором (которой) находится адрес перехода.

1.5.1 Безусловные переходы.

В MASM все команды безусловного перехода обозначаются одинаково:

        JMP op

но в зависимости от типа операнда, ассемблер формирует разные машинные команды.

1) Внутрисегментный относительный короткий переход.

        JMP i8   (IP:=IP+i8)

Здесь i8 обозначает непосредственный операнд размеров в байт, который интерпретируется как знаковое целое от -128 до 127. Команда прибавляет это число к текущему значению регистра IP, получая в нем адрес (смеще­ние) той команды, которая должна быть выполнена следующей. Регистр CS при этом не меняется.

Необходимо учитывать следующую особенность регистра IP. Выполнение любой команды начинается с того, что в IP заносится адрес следующей за ней команды, и только затем выполняется собственно команда. Для коман­ды относительного перехода это означает, что операнд i8 прибавляется не к адресу этой команды, а к адресу команды, следующей за ней, поэто­му, к примеру, команда JMP 0 - это переход на следующую команду про­граммы.

При написании машинной программы сдвиги для относительных перехо­дов приходится вычислять вручную, однако MASM избавляет от этого не­приятного занятия: в MASM в командах относительного перехода всегда указывается метка той команды, на которую надо передать управление, и ассемблер сам вычисляет сдвиг, который он и записывает в машинную ко­манду. Отсюда следует, что в MASM команда перехода по метке восприни­мается не как абсолютный переход, а как относительный.

По короткому переходу можно передать управление только на ближай­шие команды программы - отстоящие от команды, следующей за командой перехода, до 128 байтов назад или до 127 байтов вперед. Для перехода на более дальние команды используется

2) Внутрисегментный относительный длинный переход.

        JMP i16  (IP:=IP+i16)

Здесь i16 обозначает непосредственный операнд размером в слово, кото­рый рассматривается как знаковое целое от -32768 до 32767. Этот пере­ход аналогичен короткому переходу.

Отметим, что, встретив команду перехода с меткой, которой была по­мечена одна из предыдущих (по тексту) команд программы, ассемблер вы­числяет разность между адресом этой метки и адресом команды перехода и по этому сдвигу определяет, какую машинную команду относительного пе­рехода - короткую или длинную - надо сформировать. Но если метка еще

не встречалась в тексте программы, т.е. делается переход вперед, тогда ассемблер, не зная еще адреса метки, не может определить, какую именно машинную команду относительного перехода формировать, поэтому он  на всякий случай выбирает команду длинного перехода. Однако эта машинная команда занимает 3 байта, тогда как команда короткого перехода - 2 байта, и если автор программы на MASM стремится к экономии памяти и знает заранее, что переход вперед будет на близкую метку, то он должен сообщить об этом ассемблеру, чтобы тот сформировал команду короткого перехода. Такое указание делается с помощью оператора SHORT:

        JMP SHORT L

Для переходов назад оператор SHORT не нужен: уже зная адрес метки, ас­семблер сам определит вид команды относительного перехода.

3) Внутрисегментный абсолютный косвенный переход.

        JMP r16   (IP:=[r])

  или JMP m16   (IP:=[m16])

Здесь r16 обозначает любой 16-битовый регистр общего назначения, а m16 - адрес слова памяти. В этом регистре (слове памяти) должен находиться адрес, по которому и будет произведен переход. Например, по команде JMP BX осушествляется переход по адресу, находящемуся в регистре BX.

4) Межсегментный абсолютный прямой переход.

    JMP seg:ofs (CS:=seg, IP:=ofs)

Здесь seg - начало (первые 16 битов начального адреса) некоторого сег­мента памяти, а ofs - смещение в этом сегменте. Пара seg:ofs определя­ет абсолютный адрес, по которому делается переход. В MASM эта пара всегда задается конструкцией FAR PTR <метка>, которая "говорит", что надо сделать переход по указанной метке, причем эта метка - "дальняя", из другого сегмента. Отметим, что ассемблер сам определяет, какой это сегмент, и сам подставляет в машинную команду его начало, т.е. seg.

5) Межсегментный абсолютный косвенный пе

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...