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

Dos7book

.pdf
Скачиваний:
84
Добавлен:
09.02.2015
Размер:
5.1 Mб
Скачать

Глава 7: Ассемблерные команды отладчика Debug.exe

Код

Обозначение

Примечания

2E

CS:

 

3E

DS:

 

26

ES:

 

36

SS:

относительно регистра FS:

64

DB 64

65

DB 65

относительно регистра GS:

Примечание 1: посредством указания префикса нельзя изменить принимаемую по умолчанию адресацию относительно регистра ES:, в частности, в

строковых командах CMPSB, CMPSW, INSB, INSW, MOVSB, MOVSW, SCASB, SCASW, STOSB, STOSW.

7.02-02 LOCK – префикс захвата системной шины,

Префикс LOCK вводит в машинный код префиксный байт F0h, который

заставляет процессор выдавать сигнал занятости системной шины и удерживать его до завершения исполнения той машинной команды, перед которой префикс LOCK поставлен. Это бывает необходимо в многопроцессорных вычислительных

машинах для предотвращения нескоординированного доступа к совместно используемой памяти. При исполнении кода на обычных однопроцессорных компьютерах префикс LOCK не требуется.

Указывать префикс LOCK допускается только перед выполнением записи в память, в частности, посредством следующих команд: ADC, ADD, AND, DEC, INC, NEG, NOT, OR, SBB, SUB, XCHG, XOR. Но когда эти же команды выполняют операции считывания и регистровые операции, тогда указывать перед ними префикс LOCK не следует, потому что в таких случаях процессор будет реагировать на наличие префикса LOCK вызовом прерывания INT 06 (8.01-07).

Код

Обозначение

F0

LOCK

Примечание 1: процессоры фирмы Intel не допускают сочетаний префикса захвата системной шины с любым из префиксов повторения (7.02-03, 7.02-04) в одной команде.

Примечание 2: современные процессоры, имеющие более 8 управляющих регистров, допускают использование префикса F0h перед операциями обращения к управляющим регистрам (примечание 1 к 7.03-58), однако при этом интерпретируют его не как префикс захвата системной шины, а как префикс обращения к системным регистрам CR8 – CR15.

– 249 –

Глава 7: Ассемблерные команды отладчика Debug.exe

7.02-03 Префикс повторения REPNZ

Обозначение REPNZ расшифровывается как "REPeat while Not Zero", то есть повторять, пока нет нуля. Префикс REPNZ вызывает циклическое повторение исполнения команды, перед которой он поставлен, причем цикл повторения прекратится, как только будет выполнено хотя бы одно из следующих двух условий:

-исполняемая машинная команда установит флаг ZF в состояние ZR (6.05-15) в результате обнаружения равных операндов.

-число в регистре CX станет равно нулю в результате исчерпания предустановленного там количества повторений.

Префикс REPNE, расшифровываемый как "REPeat while Not Equal" (=

повторять, пока нет равенства) принимается отладчиком DEBUG.EXE как эквивалент REPNZ. Оба они вводят в машинный код один и тот же префиксный байт F2h, который заставляет процессор выполнять следующий цикл операций:

-во-первых, проверить условие CX = 0, если оно выполняется, то выйти из цикла повторения, а если не выполняется, то вычесть единицу из числа в регистре CX;

-во-вторых, установить флаг ZF в состояние NZ;

-в-третьих, выполнить машинную команду, перед которой префикс повторения поставлен;

-в-четвертых, проверить, не находится ли флаг ZF в состоянии ZR, если да, то выйти из цикла повторения, а если нет, то вернуться к началу цикла к проверке состояния регистра CX.

Пока оба проверяемых условия не выполняются, процессор продолжает исполнение цикла снова и снова. Как только любое из условий окажется выполненным, процессор выйдет из цикла повторения и перейдет к исполнению той операции, которая следует за исполнявшейся в цикле.

Префиксы повторения ставят перед строковыми командами, которые при

каждом исполнении автоматически увеличивают или уменьшают число в каком-либо индексном регистре, изменяя таким образом от цикла к циклу адрес операнда, к которому они обращаются. Строковые команды CMPSB, CMPSW, SCASB и SCASW влияют не только на индекс, но также на состояние флага ZF.

Поэтому указание префикса повторения перед этими командами позволяет осуществить поиск заданных байтов или слов. Когда префикс повторения поставлен перед строковыми командами, не влияющими на состояние флага ZF (INSB, INSW, MOVSB, MOVSW, OUTSB, OUTSW, STOSB, STOSW), тогда эти команды просто будут исполнены заданное число раз, которое следует заранее записать в регистр CX.

– 250 –

Глава 7: Ассемблерные команды отладчика Debug.exe

Код

Обозначения

F2

REPNE

F2

REPNZ

Примечание 1: если перед командой, кроме префикса повторения, указан еще какой-либо префикс, то процессоры древнее 80386 не всегда

возобновляют исполнение незавершенного цикла повторения после прерываний. Когда такого сочетания префиксов нельзя избежать, нужно либо перепроверять условия выхода из цикла, либо на время его исполнения запрещать прерывания (командой CLI, 7.03-12).

Примечание 2: префиксы повторения нельзя применять по отношению к любым не-строковым командам, потому что в таком случае современные

процессоры будут интерпретировать их как префиксы расширения состава команд, в частности, для ввода инструкций SSE.

Примечание 3: когда префиксы повторения используются в сочетании с префиксом смены разрядности операндов (7.02-06), тогда заданное число повторений считывается не из регистра CX, а из 32-разрядного регистра ECX. В таких случаях важно не забыть о записи должного значения в старшие разряды 31 – 16 регистра ECX.

7.02-04 Префикс повторения REPZ

Обозначение REPZ расшифровывается как "REPeat while Zero", то есть повторять, пока есть нуль. Префикс REPZ вызывает циклическое повторение исполнения команды, перед которой он поставлен, причем цикл повторения прекратится, как только будет выполнено хотя бы одно из следующих двух условий:

-исполняемая машинная команда установит флаг ZF в состояние NZ (6.05-15) в результате обнаружения отличающихся операндов.

-число в регистре CX станет равно нулю в результате исчерпания предустановленного там количества повторений.

Префиксы REP (REPeat = повторять), REPE (REPeat while Equal = повторять пока равно) и REPZ эквивалентны: все они вставляют один и тот же байт F3h в ассемблируемый машинный код. Встретив байт F3h, процессор выполняет ту же последовательность операций, как и для префикса REPNZ (7.02-03), за исключением того, что начальная установка флага ZF изменена на обратную (ZR) а проверяемое состояние флага ZF заменено на противоположное ( NZ ). Все другие особенности исполнения команд с префиксом REPNZ, изложенные в разделе 7.02-03 и в примечаниях к нему, в равной мере присущи исполнению команд с префиксами REP, REPE, REPZ.

– 251 –

Глава 7: Ассемблерные команды отладчика Debug.exe

Код

Обозначения

F3

REP

F3

REPE

F3

REPZ

Примечание 1: префикс REPZ часто применяют совместно с командами CMPSB или CMPSW для сопоставления двух последовательностей знаков слов, сигнатур или строк. По окончании таких циклов результат выражается оставляемым состоянием флага ZF: установленное состояние (ZR) свидетельствует о совпадении, сброшенное состояние (NZ) констатирует выявление различия.

7.02-05 Префиксы ожидания WAIT и FWAIT

Префиксы WAIT и FWAIT соответствуют одному и тому же префиксному байту 9Bh, который ставится перед командами, осуществляющими пересылку кодов между центральным процессором и асинхронно работающим сопроцессором. Байт 9Bh заставляет центральный процессор ждать поступления сигнала готовности сопроцессора на вход BUSY. Префиксный байт 9Bh должен предшествовать, в частности, каждой команде ESC (7.03-22), а также командам арифметического сопроцессора (7.04), если предполагается исполнять ассемблируемый машинный код на процессоре, не имеющем встроенного арифметического сопроцессора.

Современные центральные процессоры содержат встроенный арифметический сопроцессор с аппаратными средствами синхронизации, так что им прежняя роль префикса WAIT не нужна. Чтобы его игнорировать, нужно сбросить в нуль бит 01h "синхронизация сопроцессора" в управляющем регистре CR0 (A.11-4). По умолчанию бит 01h установлен, и тогда префикс WAIT может повлечь вызов прерывания INT 07, если одновременно установлен флаг переключения задач (бит 03h в регистре CR0). При переключении задач бывает нужно проверить и обработать зарегистрированные сопроцессором исключения. Эта миссия может быть возложена на обработчика прерывания INT 07, и ее исполнение будет обеспечиваться уместным применением префикса WAIT.

Код

Обозначения

9B

FWAIT

9B

WAIT

– 252 –

Глава 7: Ассемблерные команды отладчика Debug.exe

7.02-06 Префикс смены разрядности операнда.

При работе современных процессоров в реальном режиме они эмулируют действия с 16-разрядными операндами так, как их осуществлял устаревший процессор 8086. Однако фактически регистры общего назначения в современных процессорах 32-разрядные. Иногда требуется, не меняя реального режима работы, получить доступ к полному 32-разрядному операнду. Для этого в числе команд всех 32-разрядных процессоров платформы x86 имеется префикс 66h.

Поскольку отладчик DEBUG.EXE "не знает" префикса 66h, его следует вводить посредством инструкции DB (7.01-01),например:

DB 66

SHR AX,CL

В приведенном примере наличие префикса 66h изменит действие команды SHR (7.03-83) так, что она будет выполнять сдвиг во всем 32-разрядном регистре. Если, в частности, предварительно записанное в регистр CL число сдвигов было равно 10h, то содержимое старших разрядов 31 – 16 будет перемещено в младшие разряды 15 – 0 и станет доступно как обычный 16-разрядный операнд.

По отношению к операциям PUSH, PUSHF, POP и POPF действие префикса 66h затрагивает также стек, обеспечивая "заталкивание" или "выталкивание" четырех байтов сразу, причем старшие разряды "заталкиваются" в стек первыми. Выведение старших разрядов через стек можно осуществить, например, так:

DB 66

PUSH AX

POP BX

POP BX

В приведенном примере команда PUSH под действием префикса 66h копирует в стек весь 32-разрядный регистр EAX. Затем первая команда POP "выталкивает" из стека данные, считанные из младших разрядов 15 – 0 регистра EAX, но они не нужны, так как доступны из AX непосредственно. Вторая команда POP, "затирая" эти данные, выводит в регистр BX искомое содержимое старших разрядов 31 – 16 регистра EAX.

Префикс 66h заставляет команду CMP (7.03-14) сравнивать четырехбайтовые операнды, в том числе содержимое 32-разрядных регистров. Если префикс 66h

поставлен перед любой командой с операндом в следующих за ней байтах или в адресуемых ячейках памяти, то этот операнд должен быть четырехбайтовым (типа Dword). Отладчик DEBUG.EXE не позволяет вводить четырехбайтовые операнды в ассемблируемый код команд для центрального процессора. При необходимости дополнительные байты данных могут быть введены с помощью инструкции DB (7.01-01).

– 253 –

Глава 7: Ассемблерные команды отладчика Debug.exe

Примечание 1: программы с использованием префикса 66h нельзя запускать на компьютерах с 16-разрядными процессорами.

Примечание 2: не допускается указывать префикс 66h перед командами с однобайтовыми операндами, в том числе находящимися в любом из однобайтовых регистров (AH, AL, BH и т.д.), а также перед однобайтовыми строковыми командами (CMPSB, INSB, LODSB, MOVSB, OUTSB, SCASB, STOSB). Современные процессоры интерпретируют такие сочетания кодов как инструкции SSE.

Примечание 3: не допускается указание префикса 66h перед операциями обращения к операндам в сегментных регистрах, потому что в 32-разрядных процессорах сегментные регистры остались 16-разрядными. Однако это не касается операций, использующих содержимое сегментных регистров для доступа к ячейкам памяти.

Примечание 4: при отлаживании программ с помощью команд "Proceed" (6.05-14)

или "Trace" (6.05-17) отладчик DEBUG.EXE не показывает в качестве очередной исполняемой команды ту, которая следует за префиксом 66h. Тем не менее 32-разрядные процессоры

воспринимают префикс вместе со следующей за ним командой и исполняют их сразу, за один шаг.

Примечание 5: когда битом 6 в байте 06h дескриптора сегмента кода (примечание 5 к A.12-2) задан 32-разрядный размер операндов, тогда префикс 66h действует наоборот и указывает на 16-разрядный операнд. Здесь и далее действие префикса 66h рассматривается только при исходном 16-разрядном размере операндов, задаваемом по умолчанию "теневыми" регистрами процессора при его работе в реальном режиме.

7.02-07 Префикс смены разрядности адреса

При тестировании памяти на предмет сбоев и в ряде других задач необходимо получить доступ ко всему адресному пространству без тех ограничений, которые связаны с 32-разрядной адресацией в защищенном режиме. Чтобы в реальном режиме обеспечить доступ ко всему адресному пространству, надо установить соответствующий размер сегмента (пример в разделе 9.10-01) и разрешить 32-разрядную адресацию. Префикс 67h разрешает 32-разрядную адресацию одной следующей за ним команде.

Префикс 67h "неизвестен" отладчику DEBUG.EXE, и потому его приходится вводить как данные с помощью инструкции DB (7.01-01). Когда перед командой нужно ставить несколько префиксов, префикс 67h обычно занимает место после префикса смены сегмента (7.02-01), но перед префиксом 66h смены разрядности

– 254 –

Глава 7: Ассемблерные команды отладчика Debug.exe

операнда (7.02-06). Естественно, префикс 67h не имеет смысла перед командами, которые не оперируют адресами ячеек памяти.

Префикс 67h влияет на длину кода многих команд и, помимо того, изменяет интерпретацию всех адресных выражений, перечисленных в примечаниях 3 и 4 к таблице 7.00. Не подвержены этим влияниям только команды с неявной косвенной адресацией: CMPSB, CMPSW, LODSB, LODSW, MOVSB, MOVSW, SCASB, SCASW, STOSB, STOSW. Для ввода всех других команд с префиксом 67h ассемблерные "способности" отладчика DEBUG.EXE непригодны.

В приведенной ниже таблице показано, как 32-разрядные процессоры будут

интерпретировать одни и те же машинные коды адресных выражений при наличии префикса 67h и при его отсутствии. Для размещения в таблице отобраны только такие адресные выражения, взаимная замена которых не влияет на длину кода и на вид операции любых команд с косвенной адресацией. Пользуясь показанными здесь соответствиями, отладчика DEBUG.EXE несложно "обмануть", указав ему при ассемблировании то адресное выражение из левой колонки таблицы, которое будет интерпретировано процессором именно так, как Вам нужно.

Без префикса 67h

С префиксом 67h

[BP+DI]

[EBX]

[BX]

[EDI]

[BP+DI±7f]

[EBX±7f]

[BX±7f]

[EDI±7f]

[BP±7f]

[ESI±7f]

[DI±7f]

[EBP±7f]

Примечание 1: программы с использованием префикса 67h нельзя запускать на компьютерах с 16-разрядными процессорами.

Примечание 2: при отлаживании программ с помощью команд "Proceed" (6.05-14)

или "Trace" (6.05-17) отладчик DEBUG.EXE не показывает в качестве очередной исполняемой команды ту, которая следует за префиксом 67h. Тем не менее 32-разрядные процессоры

воспринимают префикс вместе со следующей за ним командой и исполняют их сразу, за один шаг.

Примечание 3: когда 32-разрядная адресация задана битом 6 в байте 06h дескриптора сегмента кода (примечание 5 к A.12-2), тогда префикс 67h действует наоборот и определяет 16-разрядную адресацию для следующей за ним команды. Здесь и далее действие префикса 67h рассматривается только при исходной 16-разрядной адресации, задаваемой по умолчанию "теневыми" регистрами процессора при его работе в реальном режиме.

– 255 –

Глава 7: Ассемблерные команды отладчика Debug.exe

7.02-08 Префиксы расширения состава команд.

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

Давно, еще в до-микропроцессорные времена, роль префикса расширения состава команд стал играть байт FFh. С него начинаются машинные коды модификаций многих разных команд, описываемых в разделе 7.03. Позже байты D8h – DFh были назначены на роль префиксов для команд сопроцессора, описываемых в разделе 7.04. Еще позже новые команды процессора Pentium были введены посредством префикса 0Fh; расшифровка кодов некоторых из этих команд дана в таблице 6.05-18.

В наше время свободных префиксных байтов просто не осталось. Для потоковых команд SSE современных процессоров была введена альтернативная интерпретация тех комбинаций командных кодов с префиксами 66h (7.02-06), F2h (7.02-03), F3h (7.02-04), которые прежде считались недействительными. А ради 64-разрядных процессоров приходится переводить в класс префиксов байты 40h – 4Fh, которые другими процессорами воспринимаются как команды DEC (7.03-20) и INC (7.03-27). Такие изменения уже нельзя игнорировать, даже если задачи этой книги ограничены знакомством с 16-разрядным программированием. Необходимые

меры по обеспечению совместимости машинных кодов некоторых команд с современными процессорами рассмотрены далее в статьях раздела 7.03.

7.03Команды, исполняемые центральным процессором

7.03-01 AAA – корректировка после суммирования

Команда AAA ("Adjust After Addition") преобразует в регистре AX двоичную сумму, полученную после суммирования двух неупакованных десятичных цифр, в правильное неупакованное десятичное слово, содержащее по одной десятичной цифре на байт (о сложении в упакованном десятичном формате - в 7.03-18).

Команда AAA проверяет, не произошло ли в регистре AX десятичное переполнение, выражающееся либо в установке флага AF в состояние AC, либо в том, что число в четырех младших битах регистра AL превышает 9. Если переполнение не случилось, то команда AAA просто сбрасывает флаг CF в

– 256 –

Глава 7: Ассемблерные команды отладчика Debug.exe

состояние NC. Если переполнение произошло, то выполняется суммирование AL = (AL+6), AH = (AH+1), а флаги AF и CF устанавливаются в состояния AC и CY соответственно. В любом случае старшие четыре бита в регистре AL сбрасываются в нуль. Состояния флагов OF, SF, ZF и PF не сохраняются, команда AAA оставляет их в неопределенном состоянии.

Код

Пример

37

AAA

7.03-02 AAD – подготовка перед делением

Команда AAD (Adjust AX before Dividing) преобразует в регистре AX

неупакованное десятичное слово, содержащее по одной десятичной цифре на байт, в форму двоичного числа с тем, чтобы его можно было бы подвергнуть операции двоичного деления (7.03-21).

Команда AAD вычисляет AL = AL+(10AH), и затем обнуляет регистр AH. Флаги SF, ZF и PF принимают состояния, соответствующие результату, оставляемому в регистре AL. Состояния флагов OF, AF и CF не сохраняются, команда AAD оставляет их в неопределенном состоянии.

Код

Пример

D5 0A

AAD

Примечание 1: коды "D5 (1-F)(0-9,B-F)" ошибочно дизассемблируются отладчиком

DEBUG.EXE как команда "AAD ff".

Примечание 2: числа в упакованном десятичном формате нельзя подготавливать к делению командой AAD, их нужно сначала распаковать.

7.03-03 AAM – корректировка после умножения

Команда AAM (Adjust After Multiplication) преобразует в регистре AX двоичное произведение, полученное в результате умножения двух десятичных неупакованных цифр, в правильное неупакованное десятичное слово, содержащее по одной десятичной цифре на байт. При этом предполагается, что перед умножением оба сомножителя имели байтовый формат, и четыре старших бита в обоих этих байтах содержали только нули.

Команда AAM делит двоичное произведение в регистре AX на 10, записывает частное в регистр AH, а остаток в регистр AL. Подобным образом команда AAM может преобразовывать двоичные числа до 63h в десятичный эквивалент. Флаги SF, ZF и PF принимают состояния, соответствующие получаемому результату.

– 257 –

Глава 7: Ассемблерные команды отладчика Debug.exe

Состояния флагов OF, AF и CF не сохраняются, команда AAM оставляет их в неопределенном состоянии.

Код

Пример

D4 0A

AAM

Примечание 1: коды "D4 (1-F)(0-9,B-F)" ошибочно дизассемблируются отладчиком

DEBUG.EXE как команда "AAM ff".

Примечание 2: десятичные числа в упакованном формате подвергать умножению нельзя, их нужно сначала распаковать.

7.03-04 AAS – корректировка после вычитания

Команда AAS (Adjust After Subtraction) преобразует в регистре AX двоичную разность, полученную после вычитания десятичных неупакованных цифр, в правильное десятичное неупакованное слово, содержащее по одной десятичной цифре на байт (о вычитании упакованных десятичных чисел в разделе 7.03-19).

Команда AAS проверяет, не произошло ли в регистре AX десятичное переполнение, выражающееся либо в установке флага AF в состояние AC, либо в том, что число в четырех младших битах регистра AL превышает 9. Если переполнение не случилось, то команда AAS просто сбрасывает флаг CF в состояние NC. Если переполнение произошло, то выполняется вычитание AL = (AL–6), AH = (AH–1), а флаги AF и CF устанавливаются в состояния AC и CY соответственно. В любом случае старшие четыре бита в регистре AL сбрасываются в нуль. Состояния флагов OF, SF, ZF и PF не сохраняются, команда AAS оставляет их в неопределенном состоянии.

 

 

Код

Пример

 

 

3F

AAS

7.03-05

ADC – сложение с переносом

 

Команда ADC (ADd with Carry) выполняет сложение двух указанных операндов, принимая во внимание перенос в младшем разряде. Перенос выражается состоянием флага переноса CF, который должен быть сохранен без изменения с момента исполнения предыдущей операции. Состояния флагов OF, SF, ZF, AF, PF и CF приводятся в соответствие с получаемой суммой, которая замещает собою первый операнд.

Команда ADC – двоичная операция, но имеются два исключения. Если первый операнд находится в регистре AX, то команда ADC может быть использована для

– 258 –

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]