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

лабораторные на Pasca (Кудрявцев)l

.pdf
Скачиваний:
32
Добавлен:
18.03.2015
Размер:
1.63 Mб
Скачать

 

Встроенный ассемблер

333

 

 

 

 

 

 

 

 

 

 

 

 

Команды управления циклами

 

 

 

 

 

 

 

LOOP

LOOP близкая метка

Повторить цикл

 

 

 

 

 

 

LOOPE/LOOPZ

LOOPE близкая метка

Повторять, пока равны

 

 

 

 

 

 

LOOPNE/LOOPNZ

LOOPNE близкая метка

Повторять, пока не равны

 

 

 

 

 

Команды безусловных переходов CALL, RET, JMP могут использо- вать дальнюю или ближнюю модель памяти, в то время как коман- ды условных переходов только малую (в пределах –128...+127 байтов). При дальней модели памяти (устанавливается опцией

Options/Compiler/Force far calls среды Турбо Паскаля или директивой

компилятора {F+}) осуществляется как внутрисегментная, так и межсегментная передача управления, при ближней только внут- рисегментная.

Инструкция CALL работает следующим образом. Вначале ад- рес следующей за CALL инструкции (адрес возврата) помещается в стек, затем в регистр IP (или в пару CS:IP) помещается адрес точки входа в процедуру, таким образом сразу за командой CALL будет исполняться уже первая команда процедуры. Оба адреса (точки входа и возврата) будут 16-битовыми смещениями для внутрисег- ментного вызова или 32-битовыми полными адресами для меж- сегментного. Все процедуры (функции) Паскаля, оттранслирован- ные в режиме {F+} или содержащие зарезервированное слово FAR в заголовке, должны вызываться как дальние. Для этого за инструк- цией CALL следует указать модель памяти:

Procedure MyProc; Far;

……

Asm

call FAR MyProc

{Вызов дальней процедуры}

end;

 

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

дальнем вызове в стек сначала заталкивается содержимое сегмента кода CS, а уже затем смещение возврата.

При выходе из дальней процедуры команда RET извлекает из стека оба 16-разрядных слова и помещает первое в IP, а второе в CS, а при выходе из ближней извлекает из стека только смещение и по- мещает его в IP.

334

Приложение

 

 

Команды условных переходов способны передавать управление на метку, расположенную в пределах ближайших плюсминус 128 байт от самой команды. Если нужно передать управление на метку, расположенную дальше в том же сегменте, или на метку в другом сегменте, сразу: а командой условной передачи располагают безус- ловную команду JMP или CAL, например:

cmp ах, 0

{Проверяем АХ}

jne @NotZero

{AX=0 ?}

jmp IsZero

{Да - переходим на дальнюю метку} .....

……

{Нет - продолжаем работу}

……

 

В таблице термин «выше/ниже» используется применительно к сравнению беззнаковых операндов, а «больше/меньше» – знаковых.

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

Условие

Для беззнаковых

Для чисел

чисел

со знаками

 

Приемник больше источника

JA

JG

Приемник и источник равны

JE

JE

Приемник меньше источника

JB

JL

 

 

 

Приемник не меньше источника

JAE

JGE

Приемник не больше источника

JBE

JLE

Приемник и источник не равны

JNE

JNE

Например:

cmp ах, 5

{AX>5 ?}

ja @Above5

{Да, больше переходим}

cmp bx, -3

{ВХ<= -3 ?}

jle@LessM3

{Да, меньше или равен}

Команды LOOP/LOOPE/LOOPNE служат для организации циклов. Все они используют содержимое регистра СХ как счетчик числа повторений. Команда LOOP уменьшает СХ на единицу и передает управление на метку начала цикла, если содержимое этого регистра

Встроенный ассемблер

335

 

 

отлично от нуля. Команды LOOPE/LOOPNE также уменьшают счет- чик СХ, но передают управление в начало цикла при совместном условии установки (или сброса) флага ZF и неравенства нулю счет- чика СХ.

Вот как, например, можно отыскать нулевой байт в массиве АОВ:

Var

АОВ: array [1..1000] of Byte;

……

Asm

mov сx, 1000

 

{Инициируем счетчик СХ}

lea bx, AOB

 

{Помещаем адрес АОВ в BX}

dec bx

 

{Готовим цикл}

 

{Здесь начало цикла проверки}

(@Test: inc bx

 

{Адрес очередного байта}

cmp BYTE PTR

[bx], 0

{Проверяем байт}

loopne @Test

 

{Замыкаем цикл}

jnz @NotZero

 

{Если не найден нулевой байт}

……

 

{Нашли нулевой байт}

end;

Строковые команды

Мнемоника

Формат

 

Комментарий

 

 

 

 

 

Пересылка строк

 

 

 

 

MOVSB

MOVSB

 

Пересылать байты

MOVSW

MOVSW

 

Пересылать слова

 

Сравнение строк

 

 

 

 

CMPSB

CMPSB

 

Сравнивать байты

CMPSW

CMPSW

 

Сравнивать слова

 

Сканирование

 

 

 

 

SCASB

SCASB

 

Искать байт

SCASW

SCASW

 

Искать слово

 

Загрузка и сохранение

 

 

 

 

LODSB

LODSB

 

Загружать байты

LODSH

LODSW

 

Загружать слова

STOSB

STOSB

 

Сохранять байты

STOSW

STOSW

 

Сохранять слова

Строковые команды рассчитаны на обработку строк. Заметим, что термин «строка» здесь отнюдь не эквивалентен аналогичному тер- мину Турбо Паскаля и означает произвольную цепочку байт или слов длиной до 64 Кбайт. Эти команды оперируют пятью примити-

336

Приложение

 

 

вами, каждый из которых обрабатывает лишь один байт или одно слово за раз. Перед примитивом обычно указывается префикс по- вторения REP/REPE/REPNE, заставляющий выполняться примитив до тех пор, пока не обнулится счетчик повторений СХ или не будет нарушено соответствующее условие.

При использовании строковых команд важно помнить два обстоя- тельства. Во-первых, эти команды всегда берут адрес строки- источника из пары DS:SI, а строки-приемника из пары ES:DI. Та- ким образом, перед исполнением строковой команды необходимо инициировать сегментные регистры нужным образом. Во-вторых, строковые команды используют индексную адресацию с автомати- ческим изменением смещения в SI/DI после однократного исполне- ния примитива. Содержимое этих регистров изменяется на 1 при обработке байтов и на 2 при обработке слов, причем наращивается, если флаг направления DF сброшен, и уменьшается, если он равен

1.

Вот как можно осуществить пересылку массива А в массив В:

Var

А, В: array [1..250] of Integer;

……

Asm

lea si, A

{Смещение А - в SI (источник)}

push ds

{Инициируем ES := DS}

pop es

lea di, B

{Смещение В - в DI (приемник)}

mov ex, 250

{Счетчик переноса}

Сld

{Направление переноса - наращивать}

rep movsw

{Переносим 500 байт}

end;

 

В программе на Турбо Паскале регистр DS всегда содержит сегмент данных, поэтому инициировать его необязательно. Что касается ре- гистра дополнительного сегмента ES, такого правила нет, и хотя в большинстве случаев он также ссылается на сегмент данных, реко- мендуется проводить его инициацию перед использованием стро- ковой команды (см. выше команды push ds, pop es).

Встроенный ассемблер

337

 

 

Команды прерываний

Мнемоника

Формат

Комментарий

 

 

 

INT

INT номер

Выполнить прерывание

 

 

 

INTO

INTO

Выполнить прерывание по переполнению

 

 

 

IRET

IRET

Вернуться из прерывания

 

 

 

Выполнение прерываний во многом напоминает косвенный вызов дальней процедуры. По команде INT (INTO) в стек помещается ре- гистр флагов, сегмент CS и указатель IP, а новые значения этих ре- гистров берутся из 4-байтного вектора прерывания, соответствую- щего номеру прерывания в команде INT, или из вектора 4 – для ко- манды INTO. Таким образом, единственным отличием от команды CALL является то, что в стек предварительно заносится регистр флагов. Следует, правда, оговориться: перед передачей управления программе обработки прерывания микропроцессор сбрасывает фла- ги трассировки TF и прерываний IF; сброс TF необходим для обес- печения нормальной работы отладчиков, использующих прерыва- ние по вектору 1 или 4, сброс IF блокирует вмешательство других процессов в ход обработки прерывания.

Команда INTO представляет собой условное прерывание и выпол- няется, если в этот момент взведен флаг переполнения OF. Команда IRET реализует правильный выход из программы обработки преры- вания: она считывает из стека 3 двухбайтные слова и помещает их в регистры IP, CS и регистр флагов.

Команды управления

Мнемоника

Формат

Комментарий

 

 

 

 

Управление флагами

 

 

 

STC

STC

Установить перенос

CLC

CLC

Очистить перенос

CMC

CMC

Инвертировать CF

STD

STD

Установить направление

CLD

CLD

Очистить направление

STI

STI

Разрешить прерывания

CLI

CLI

Запретить прерывания

 

Внешняя синхронизация

 

 

 

HLT

HLT

Остановить вычисления

WAIT

WAIT

Ждать активности на шине

ESC

ESC код, источник

Передать команду

338

Приложение

 

 

LOCK

LOCK

 

Захватить шину

 

Пустая команда

 

 

 

 

NOP

NOP

 

Нет операции

Команды внешней синхронизации работают следующим образом.

HALT переводит МП в состояние останова, из которого его можно вывести только при перезагрузке системы или при наступлении не- маскируемого прерывания.

WAIT заставляет МП выполнять холостой режим работы и каждые 5 тактов проверять уровень сигнала на входной шине: пока на этой шине нет сигнала активности, процессор выполняет WAIT, но как только шина активизируется, он продолжит исполнение програм- мы. Эта инструкция обычно используется для ожидания сигнала обслуживания (прерывания) высокоприоритетного устройства типа контроллера прямого доступа к памяти.

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

Команда LOCK фактически представляет собой однобайтовый пре- фикс, который можно использовать совместно с любой другой ко- мандой микропроцессора. По этой команде МП активизирует одно- именный сигнал на своей шине, что исключает возможность ис-

пользования этой шины любым другим внешним устройством (процессором).

СПЕЦИФИКА ВСТРОЕННОГО АССЕМБЛЕРА

Приведенное выше общее описание архитектуры МП 8086/8088 яв- ляется базовым для любого ассемблера, в том числе и для встроен- ного ассемблера Турбо Паскаля. Однако ассемблеры содержат мас- су дополнительных возможностей, облегчающих разработку гото- вых к работе программ. Эти возможности отражаются в директивах и макрокомандах ассемблера. Встроенный ассемблер не предназна- чен для написания законченных программ, поэтому в нем отсутст- вуют макрокоманды и директивы. Главной особенностью встроен-

Встроенный ассемблер

339

 

 

ного ассемблера является практически полное отсутствие в нем средств описания переменных и данных, т.к. эти объекты описыва- ются средствами Турбо Паскаля.

Оператор ASM

Зарезервированное слово ASM открывает доступ к средствам встро- енного ассемблера. Этот оператор может располагаться только внутри исполняемой части программы (подпрограммы). Область действия оператора ASM ограничивается ближайшим по тексту за- резервированным словом END. Таким образом, структура любого ассемблерного оператора такова:

Asm

<Одна или несколько команд встроенного ассемблера> end;

С точки зрения Турбо Паскаля пара asm... end считается оператор- ными скобками, ограничивающими единственный оператор Паска- ля, например:

if X>10 then Asm

……

end

else ……;

for k := 1 to 5 do

Asm

……

end;

Тело ассемблерного оператора asm... end может быть пустым или содержать несколько ассемблерных команд. Каждая ассемблерная

команда должна располагаться на отдельной строке или отделяться от следующей за ней команды символом «;». Ниже приводятся два разных способа написания одной и той же последовательности ас- семблерных команд:

Asm

mov ah, 0; int $16; mov ChCode, al; mov ScCode, ah end;

Asm

mov ah, 0 int $16

340

Приложение

 

 

mov ChCode, al mov ScCode, ah

end;

В конце строки, содержащей единственную ассемблерную коман- ду, или между двумя командами, располагающимися на одной строке, разрешается вставлять комментарий, который должен оформляться по обычным правилам Турбо Паскаля, т.е. ограничи- ваться символами «{», «}» или «(*», «*)». Таким образом, коммента- рии разрешены между ассемблерными командами, но не внутри них. Например, такой оператор будетправильным:

Asm

{Инициируем регистры} lea si, X; push ds;

pop es;

{ES := DS}

lea di,Y; mov ex,100

сld

{Перенос - вперед}

rep

{Выполняем Y := X} movsw

{Здесь нет ошибки - комментарий можно вставлять между префиксом и командой} end;

а такой неправильным:

Asm

{Готовим регистры} lea si, X; push ds; pop {ES:=DS} es;

{Ошибка! Комментарий разорвал мнемонику команды и ее операнд} lea di, Y; mov ex, 100 {и направление} cld

{Комментарий является разделителем команд, поэтому перед ним можно не ставить ";"}

rep movsw end;

Впределах ассемблерного оператора допускаются любые команды, но Турбо Паскаль требует выполнения следующего соглашения:

Вначале ассемблерного оператора регистр DS содержит сегмент кода, SS - сегмент стека, ВР текущий стек, SP указывает на вершину стека. Все эти регистры должны иметь точно такие же значения к моменту завершения работы ассемблерного оператора.

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

Встроенный ассемблер

341

 

 

тора. Исключением является случай ассемблерной функции, кото-

рая должна использовать некоторые регистры для возврата своего значения.

Синтаксис ассемблерных команд

Здесь и далее ассемблерными командами называются команды на языке встроенного ассемблера, вставляемые в тело ассемблерного

оператора asm... end.

Структура ассемблерной команды такова:

[Метка] [Префикс] [Код [Операнд [/Операнд]]]

В квадратных скобках указываются необязательные элементы структуры.

Метки

Любой команде ассемблерного оператора может предшествовать одна или несколько меток. В ассемблере используется два типа ме- ток: глобальные и локальные. Глобальные метки это обычные метки Турбо Паскаля. Они объявляются в разделе описаний после

зарезервированного слова Label. С помощью глобальной метки можно передать управление в тело ассемблерного оператора опера- тором GOTO. Например:

Label AltEnt;

Begin

{Передаем управление внутрь ассемблерного оператора}

Goto AltEnd;

 

……

 

Asm

 

……

{Сюда можно передать управление извне}

AltEnd:

……

 

end;

 

Локальные метки объявляются непосредственно в теле ассемблер- ного оператора. Эти метки обязаны начинаться символом «@». По- скольку этот символ нельзя использовать в именах Турбо Паскаля, он позволяет отличить локальную метку от глобальной. Локальная метка не известна нигде вне ассемблерного оператора, поэтому на нее нельзя передать управление оператором GOTO. По этой же

причине в разных ассемблерных операторах можно использовать одноименные локальные метки.

{$Н+}, 80286

342

Приложение

 

 

Префиксы

Встроенный ассемблер поддерживает следующие префиксы ко- манд:

LOCK

Захват шины

REP/REPE/REPME

Повтор строковой команды

REPZ/REPNZ

Синоним REPE/REPNE

SEGCS

Перекрытие CS

SEGSS

Перекрытие DS

SEGES

Перекрытие SS

SEGDS

Перекрытие ES

Префиксы LOCK/REP/REPE/REPNE описаны выше. Префиксы SEGxx определяют сегментный регистр, который должен использо- ваться вместо умалчиваемого, и распространяются только на сле- дующие за ними ассемблерные команды.

Если префикс указан без кода инструкции, он распространяет свое действие на следующую ассемблерную команду.

Код инструкции очень редко имеет более одного префикса и нико- гда более трех: допускается следующая последовательность

LOCK SEGxx REPxx

Заметим, что если при обработке строковой команды произошло аппаратное прерывание, МП 8086/8088 «забывает» префиксы LOCK и SEGxx, которые, возможно, определены в той же команде, так что использовать сложные префиксные конструкции не рекомендуется.

Коды инструкций

Встроенный ассемблер поддерживает мнемонику всех команд, пе- речисленных выше. Кроме того, в ассемблерных командах может использоваться мнемоника инструкций процессора 8087, а также команды процессоров 80286/80287. Заметим, что инструкции 8087 допустимы только при активном состоянии при

{$G+}, a 80287 в случае {$G+,N+}.