Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
flor_apparato-orientirovnnoe_prog.doc
Скачиваний:
89
Добавлен:
15.06.2014
Размер:
926.72 Кб
Скачать

6. Вспомогательные средства базовой архитектуры

6.1. Использование строковых команд пересылки

Изученными выше средствами не ограничиваются возможности современной архитектуры компьютера. За «бортом» нашего рассмотрения оказалось множество редко используемых команд, даже целые группы таких команд, а также специальные средства архитектуры, связанные с построением внутренней части операционной системы. Из еще не рассмотренных общеупотребительных средств наибольший интерес представляют строковые команды.

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

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

MOV ESI, oblast1

MOV EDI, oblast2

MOV ECX, число_байтов

povt: MOV AL, [ESI]

MOV [ESI], AL

INC ESI

INC EDI

LOOP povt

Листинг 6.1.1. Простейшее решение пересылки данных

где предполагается пересылка содержимого байтов из именованной области oblast1 в именованную область oblast2, а непосредственный операнд число_байтов задает число пересылаемых байтов.

Заметим, что для такой, простейшей в своей цели задачи, рассмотренное решение требует целых девяти команд, причем шесть из низ многократно выполняются внутри цикла. Более тщательный анализ показывает, что для пересылки одного очередного байта при этом требуется выполнение шести команд, образуемых десятью байтами командного кода. С учетом извлечения байтов командного кода процессором из памяти (хотя в современных компьютерах и из наиболее быстрой памяти внутреннего кэша) получается, что на пересылку одного байта данных процессор тратит время, обусловленное в том числе и извлечением (пересылкой из памяти в процессор) десяти байтов. Тем самым пересылается в пять раз больше байтов, чем кажется действительно необходимым. Для сравнения заметим, что в более ранней архитектуре компьютеров IBM360 (больших компьютеров) была команда

MVC куда(сколько), откуда

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

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

Особенностью строковых команд является то, что они как бы разбивают универсальное действие упомянутой выше команды MVC на подготовительные вспомогательные команды и некоторую специфическую команду, которая и осуществляет многократно желаемое действие. Подготовительные действия заключаются в занесении адресов области исходных данных и размещения результатов в специализированные регистры ESI, EDI (или в регистры SI, DI для 16-битного варианта), а также занесение числа пересылаемых байтов в регистр ECX (в регистр CX для 16-битного варианта). Именно эти три команды требовались и в простейшем техническом решении, приведенном в листинге 6.1.1, так что никакого "откровения" здесь не видно.

Кроме этого в подготовительных действиях указывалось направление будущей пересылки - от младших адресов к старшим или наоборот (целесообразность наличия в "запасе" второго варианта будет обсуждена позднее). Задание такого направления выполняется с помощью специального бита регистра EFLAGS, который называется флажком направления - DF (Direction Flag). Для изменения указанного флага служат две команды: CLD и STD, первая из них сбрасывает значение флага в нуль, а вторая устанавливает его в единицу. Значение флага DF, равное нулю, отвечает пересылке в направлении от младших адресов к старшим (в прямом направлении).

Собственно строковая команда пересылки обозначается на ассемблере мнемокодом MOVSx, где символ x задает конкретную модификацию команды. Во внутренней части цикла, организуемого с помощью строковой команды пересылки MOVSx, записывается всего лишь эта специальная команда и предшествующий ей специальный префикс повторения REP. Этот префикс при трансляции в машинный двоичный код преобразуется в специальный байт префикса. Поэтому программа, равносильная раннее приведенной в листинге 6.1.1, будет иметь вид, представленный в листинге 6.1.2.

MOV ESI, oblast1

MOV EDI, oblast2

MOV ECX, число_байтов

CLD

REP MOVSB

Листинг 6.1.2. Пересылка данных с помощью строковой команды

Еще раз отметим, что в этом решении первые четыре команды выполняются однократно, а многократному выполнению подлежит только одна командная строка (из двух байтов). Команды MOVSx имеют три модификации, задаваемые на ассемблере последним символом мнемокода, который обозначает размер единицы пересылаемого кода. Для последнего возможны значения B, W и D, что соответствует обозначениям BYTE, WORD и DWORD, задавая тем самым единицу пересылки в один, два и четыре байта.

Действия команды MOVSx заключаются в том, что элемент данных (размер которого задан метасимволом x) пересылается из места, заданного адресом в регистре ESI, в место, заданное адресом в регистре EDI. (Таким образом, как бы берется значение операнда [ESI] и пересылается по месту операнда [EDI]). Затем, в ходе выполнения той же команды, изменяются значения регистров ESI и EDI, оба они при DF=0 увеличиваются, а при DF=1 уменьшаются на число, определяемое модификатором x (на один для x=B, на два для x=W и на четыре для x=D).

Действие префикса повторения, управляющего повторением команды MOVSx, заключается в проверке текущего значения регистра ECX на нуль, в принятии решения о повторении или прекращении выполнения строковой команды и в уменьшении на единицу значения этого регистра, если принято решение о повторении строковой команды. Прекращается выполнение команды MOVSx только в том случае, когда значение регистра ECX равно нулю.

Получается, что если в составе командной строки использован префикс повторения REP и только что полученное значение регистра ECX не равно нулю, то действия команды MOVSx повторяются. Эти повторения происходят до тех пор, пока содержимое регистра ECX не станет равным нулю. (Для 16-битного варианта во всех указанных в описании регистрах используется только младшая половина.)

При использовании команды MOVSW в регистр ECX должно заноситься число пересылаемых слов (пар байтов), а при использовании команды MOVSD в регистр ECX следует заносить число пересылаемых двойных слов (четверок байтов). Единичное значение флажка направления предназначено для редкого, но возможного варианта пересылки частично перекрывающихся областей. Если исходная область данных перекрывается с результирующей областью данных, причем начало второй лежит далее начала первой, то первые пересылаемые байты непредусмотрено искажают дальнейшие исходные данные. Это показывает следующий пример, приведенный на рис. 6.1.3.

Рис. 6.1.3. Пересылка между частично перекрывающимися областями

В этом примере результирующая область данных oblst1 начинается на два байта дальше области данных oblst2, используемой в качестве исходных. Для этого случае пересылка первого байта a из области oblst2 на место первого байта области oblst1 изменит третий байт c исходной области (а пересылка второго байта b области oblst2 изменит четвертый байт исходной области). В результате (как ни странно!) результирующая область oblst1 будет заполнена чередующимися значениями байтов a и b, но никакими другими. Заметим, что этот "трюк" может быть использован именно для быстрого заполнения повторяющимися значениями, если соответствующим образом подобрать взаимное размещение частично пересекающихся областей.

Общее решения пересылки между областями данных с указанным расположением заключается в пересылке вначале самых последних байтов, затем предпоследних и т.д., что, как нетрудно видеть, обеспечивает правильное решение. Для такого решения вместо команды CLD необходимо использовать команду STD, а в регистры ESI и EDI занести адреса не начала соответствующих областей данных, а адреса их последних элементов. В самом общем случае требуется предварительно проверять взаимное расположение областей данных, между которыми пересылаются данные, и выбор в зависимости от этого одного из возможных вариантов направления (если области взаимно пересекаются и область назначения начинается в памяти раньше области исходных данных, то неверный результат дает пересылка, начиная со старших адресов).

Для 16-битной архитектуры существенным оказывается еще одна особенность строковых команд. Она обусловлена использованием (по умолчанию) различных сегментов памяти для неявных операндов строковых команд, основанных на регистрах SI и DI. (Для них используются специальные сегментные регистры DS и ES, соответственно, причем на программиста возлагается ответственность за правильное заполнение этих сегментных регистров.) К счастью для современных программистов, 32-битные операционные системы практически не дают возможности для прикладных программ изменять и устанавливать сегментные регистры, поэтому соответствующая проблема полностью исчезает.

Рассмотренная строковая команда MOVSx позволяет написать высокоэффективную процедуру для пересылки одной строки данных в область, заданную другой строкой. Эта процедура по своему смыслу равносильна системной функции strncpy системной библиотеки языка С.

Такая процедура может быть описана программой, приведенной в листинге 6.1.4

;procedure strncpy(target* edi, source* esi, maxlen ecx)

strncpy: push edi

push esi

push ecx

cld

rep movsb

pop ecx

pop esi

pop edi

ret

Листинг 6.1.4. Эффективная процедура strncpy копирования строк

Для этой процедуры предполагается, что перед вызовом процедуры адрес строки результата заносится в регистр ESI, адрес строки исходных данных заносится в регистр EDI, а число пересылаемых байтов задается в регистре ECX.

Для сравнения в листинге 6.1.5 приведена менее эффективная процедура пересылки строк, не использующая строковых команд.

astrncpy: push eax

push edi

push esi

push ecx

met2: mov al, [esi]

mov [edi], al

inc esi

inc edi

loop met2,ecx

pop ecx

pop esi

pop edi

pop eax

ret

Листинг 6.1.5. Неэффективная процедура astrncpy копирования строк

Кроме весьма общих команд пересылки MOVSx, в составе архитектуры Intel имеются еще менее значимые строковые команды STOSx и LODSx, где последний символ - модификатор x имеет уже рассмотренный смысл и конкретные обозначения, задавая размер элемента, с которым оперируют строковые команды.

Команды STOSB, STOSW и STOSD (в применении вместе с предшествующим им префиксом повторения REP) обеспечивают многократное «расписывание» содержимого, соответственно, регистра AL, AX или EAX по последовательности соседних ячеек памяти. Число заполняемых таким образом одно-, двух- или четырехбайтовых элементов задается предварительно в регистре ECX, а адрес области памяти, куда нужно записывать содержимое указанных регистров, должен быть предварительно занесен в регистр EDI. Теоретически может быть использована запись от старших адресов к младшим, хотя содержательных причин такого варианта не просматривается. Следует обратить внимание на обязательное задание для направления пересылки через флаг DF регистра флагов EFLAGS (практически с помощью команды CLD) - рассчитывать, что этот флаг сброшен по умолчанию совершенно не предусмотрительно и может вызвать неочевидную ошибку. Практически команды STOSx могут использоваться и используются для быстрого обнуления больших массивов оперативной памяти.

Еще менее значимые команды LODSx выполняют занесение в регистр AL, AX или EAX, соответствующий их модификатору, значения из элемента памяти по адресу, который задает регистр ESI. Причем тут же автоматически значение регистра ESI изменяется, указывая с его помощью на следующий элемент в направлении, которое определяется флагом DF. (Величина изменения на единицу, два или четыре определяется модификатором x команды.) Применять эту команду с префиксом повторения не имеет никакого смысла, практически она может служить только для замены пары команд

MOV AL, [ESI]

INC ESI

или аналогичных, где вместо регистра AL задаются регистры AX, EAX, а вместо команды инкремента (или декремента для DF=1) используется команда ADD ESI, 2 или ADD ESI, 4 (либо для DF=1 соответствующие команды вычитания). Такая замена несколько ускоряет требуемое суммарное действие и занимаем меньше места в машинной памяти, хотя экономия от этого и не значительна. (Заметим, что для 16-битного варианта все указанные выше регистры должны быть использованы без расширяющей их разрядности буквы E.)

Соседние файлы в предмете Системное программное обеспечение