WWW.DISS.SELUK.RU

БЕСПЛАТНАЯ ЭЛЕКТРОННАЯ БИБЛИОТЕКА
(Авторефераты, диссертации, методички, учебные программы, монографии)

 

Pages:     | 1 |   ...   | 2 | 3 || 5 |

«(Учебное пособие) Иркутск 2007 УДК 681.3.6 С50 Смоленцев М.Ю. Программирование на языке Ассемблера для микропроцессоров i80x86: Учебное пособие.— Иркутск: ИрИИТ, 2007.— 600с. Ил. Табл. Библиогр.: назв. Рекомендовано ...»

-- [ Страница 4 ] --

Команда ROL используется для циклического сдвига разрядов операнда влево. Отличие этого сдвига от RCL в том, что очередной сдвигаемый бит одновременно вдвигается в операнд справа и становится значением флага CF. Так же, как и для других сдвигов, значение второго операнда (счетчика сдвига) ограничено диапазоном 0...31. Это объясняется тем, что микропроцессор использует только пять младших разрядов операнда количество сдвигов. Аналогично другим командам сдвига сохраняется эффект, связанный с поведением флага OF, значение которого имеет смысл только в операциях сдвига на один разряд:

• если OF=1, то текущее значение флага CF и выдвигаемого слева бита операнда различны;

• если of=0, то текущее значение флага CF и выдвигаемого слева бита операнда совпадают.

Этот эффект, как Вы помните, обусловлен тем, что флаг OF устанавливается в единицу всякий раз при изменении знакового разряда операнда:

MOV EAX,0FFFF0000h ;поменять местами половинки (Циклический сдвиг операнда вправо= ROtate operand Right) Синтаксис команды: ROR, Семантика команды: операция циклического сдвига операнда вправо.

Алгоритм работы:

• сдвиг всех битов операнда вправо на один разряд, при этом младший бит операнда вдвигается в операнд слева и становится значением старшего бита операнда;

• одновременно этот младший бит операнда становится значением флага переноса CF;

• старое значение флага переноса CF вдвигается в операнд слева и становится значением старшего бита операнда;

указанные выше два действия повторяются количество раз, равное значению операнда количество сдвигов.

Применение:

Команда ROR используется для циклического сдвига разрядов операнда вправо. Отличие этого сдвига от RCR в том, что очередной сдвигаемый бит одновременно вдвигается в операнд слева и становится значением флага CF. Так же, как и для других сдвигов, значение второго операнда (счетчика сдвига) ограничено диапазоном 0...31. Это объясняется тем, что микропроцессор использует только пять младших разрядов операнда количество_разрядов. Аналогично другим командам сдвига сохраняется эффект, связанный с поведением флага OF, значение которого имеет смысл только в операциях сдвига на один разряд:

• если OF=1, то текущее значение флага CF и вдвигаемого слева бита операнда различны;

• если OF=0, то текущее значение флага CF и вдвигаемого слева бита операнда совпадают.

Этот эффект, как Вы помните, обусловлен тем, что флаг OF устанавливается в единицу всякий раз при изменении знакового разряда операнда.

;поместить четыре младших бита AX на место ROR AX,4 ;старших битов (Циклический сдвиг операнда влево через флаг переноса = “Rotate Синтаксис команды: RCL, Семантика команды: операция циклического сдвига операнда влево через флаг переноса CF.

Алгоритм работы:

• сдвиг всех битов операнда влево на один разряд, при этом старший бит операнда становится значением флага переноса CF;

• одновременно старое значение флага переноса CF вдвигается в операнд справа и становится значением младшего бита операнда;

• указанные выше два действия повторяются количество раз, равное значению операнда количество сдвигов команды RCL.

В случае команды RCL флаг OF представляет интерес, если сдвиг осуществляется на один разряд.

Применение:

Команда RCL используется для циклического сдвига разрядов операнда влево. Особенность этого сдвига в том, что он происходит с некоторой задержкой, так как очередной сдвигаемый бит оказывается на некоторое время вне операнда. В это время можно произвести его извлечение и (или) подмену. Другой важный момент заключается в том, что для счетчика сдвига микропроцессор использует только пять младших разрядов операнда количества разрядов. Таким образом, значение, большее 31, микропроцессором не допускается (аппаратно это ограничение реализуется тем, что игнорируются значения всех битов счетчика, кроме первых пяти). Обратите внимание на еще один интересный эффект, связанный с поведением флага OF. В операциях сдвига на один разряд по изменению этого флага можно судить о факте изменения знакового (старшего) разряда операнда:

• OF=1, если текущее значение флага CF и выдвигаемого бита операнда слева различны;

• OF=0, если текущее значение флага CF и выдвигаемого бита операнда слева совпадают.

;сдвиг операнда, занимающего два двойных слова на ;четыре разряда влево MOV EAX,CH_L ;младшая часть 64-битного операнда MOV EDX,CH_H ;старшая часть 64-битного операнда RCL EDX,1 ;CF в младший бит EDX, старший бит EDX (Циклический сдвиг операнда вправо через флаг переноса = “Rotate operand through Carry flag Right”) Синтаксис команды: RCR, Семантика команды: операция циклического сдвига операнда вправо через флаг переноса CF.

Алгоритм работы:

• сдвиг всех битов операнда вправо на один разряд, при этом младший бит операнда становится значением флага переноса CF;

• одновременно старое значение флага переноса CF вдвигается в операнд слева и становится значением старшего бита операнда;

• указанные выше два действия повторяются количество раз, равное значению операнда количество сдвигов команды RCR.



В случае команды RCR флаг OF представляет интерес, если сдвиг осуществляется на один разряд.

Применение:

Команда RCR используется для циклического сдвига разрядов операнда влево. Особенность этого сдвига в том, что он происходит с некоторой задержкой, так как очередной сдвигаемый бит оказывается на некоторое время вне операнда. В это время можно произвести его извлечение и (или) подмену. Другой важный момент заключается в том, что для счетчика сдвига микропроцессор использует только пять младших разрядов операнда количества разрядов. Таким образом, значение, большее 31, микропроцессором не допускается (аппаратно это ограничение реализуется тем, что игнорируются значения всех битов счетчика, кроме первых пяти). Обратите внимание на еще один интересный эффект, связанный с поведением флага OF. В операциях сдвига на один разряд по изменению этого флага можно судить о факте изменения знакового (старшего) разряда операнда:

• OF=1, если текущее значение флага CF и выдвигаемого бита операнда слева различны;

• OF=0, если текущее значение флага CF и выдвигаемого бита операнда слева совпадают:

;подсчет числа единичных битов в операнде CYCL: RCR OPERAND, JNC A ;переход,если очередной выдвинутый бит= Одним из наиболее полезных приложений микропроцессоров является применение их для моделирования реальных процессов и явлений окружающего нас мира. С помощью компьютера можно успешно моделировать такие процессы явления, как движение потока нефти или газа в трубопроводах, взлет и посадка самолетов, распределение потока работ в цифровой вычислительной системе, движение графического изображение в играх, и многое другое. Многие из этих моделей становятся тем ближе к своим реальным прототипам, чем больше содержат случайных и заранее неопределяемых элементов.

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

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

Если известны алгоритм и исходные величины, микропроцессор может генерировать последовательность случайных чисел. И хотя они не являются действительно случайными, многие их характеристики совпадают с характеристиками действительно случайных последовательностей. По этой причине генерируемые последовательности являются псевдослучайными.

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

Одним из широко используемых методов генерации псевдослучайных чисел является метод последовательного умножения. Его алгоритм состоит из следующих шагов:

1. Выбирается какое-нибудь значение X. Оно называется начальным значением и является первым числом генерируемой псевдослучайной последовательности.

2. Число X умножается на постоянный множитель N. В результате получается число P.

3. Число P делится на константу M, называемую модулем. Остаток от деления образует новое значение величины X и является следующим числом псевдослучайной последовательности.

4. шаги 2 и 3 повторяются.

Поскольку генерируемые псевдослучайные числа являются остатками от деления P/M, то значение модуля M определяется максимально возможную длину последовательности. Так, если M=216, то переменная X может принимать 216 или 65536 значений. Такой диапазон последовательности вполне достаточен для большинства применений. Если вычисления ограничиваются 16-битовыми числами, то для достижения максимально возможной независимости соседних членов последовательности величина множителя N не должна превосходить квадратного корня из модуля M.

Для того чтобы закон распределения чисел был более или менее постоянен на протяжении всей последовательности, последние три бита сомножителя X должны содержать 011 или 101. Начальное значение для процедуры можно взять из значения секунд и сотых долей секунд внутреннего таймера системы или ASCII или SCAN-код последнего введенного с клавиатуры символа (желательно проследить что бы в начальное значение не попал 0) ;читаем значение сотых долей секунд в таймере для ;начального значения последовательности случайных ;чисел. В регистр AH номер функции получения времени AGAIN: MOV AH,2Ch INT 21h ;получаем время MOV AX,DX ;берем секунды из DH и сотые доли секунды из DL

JE AGAIN

;генерация 200 псевдослучайных чисел

RANDOM: CALL PSEUDO_RANDOM

LOOP RANDOM

PSEUDO_RANDOM PROC NEAR

PUSH DX

Псевдослучайные последовательности, образованные при помощи Еще одним из достаточно простых способов образования псевдослучайной последовательности чисел является комбинирование операций циклического сдвига, операции «логического И» (AND) для выделения одиночных разрядов и «операции исключающего ИЛИ» (XOR).

Пусть командами RCR или RCL сдвигается число имеющее размер m бит. Результат операции XOR между n-ым и последним (m-ым) разрядом помещается во флаг переноса и оттуда попадает в 1-ый разряд. Такая схема проходит совокупность состояний, которая определяется комбинациями битов в регистре после каждого сдвига и эта последовательность повторится через каждые K чисел, то есть является циклической с периодом K.

Число возможных состояний m-разрядого регистра составляет K=2m, то есть равно числу двоичных комбинаций m бит. Однако состояние, когда в регистре содержаться все 0, является для данной схемы «тупиковым», поскольку операция XOR будет формировать только нули. Поэтому максимальная длина последовательности, которую можно сформировать с помощью данной схемы, равна 2m—1. Получить последовательности максимальной длины можно лишь в том случае, если m и n выбраны правильно и результирующая последовательность битов является псевдослучайной.

(Критерием для определения максимальной длины служит неприводимость полинома 1+xm+xn и его первичность на поле Галуа.) В качестве примера рассмотрим 4-разряный регистр сдвига с обратной связью. В таблице перечислины генерируемые псевдослучайные числа, начиная с 1111b (15) (с тем же успехом можно выбрать любое начальное число, за исключением 0000).

Регистры сдвига максимальной длины можно строить, используя и более двух точек для подключения обратной связи через операцию Исключающее ИЛИ. В этом случае по модулю 2 суммируются несколько битов.

При некоторых m для построения регистра максимальной длины требуется более двух точек подключения обратной связи. В таблице приводятся все значения m, вплоть до 33, при которых для построения регистра максимальной длины достаточно двух точек подключения обратной связи, то есть обратная связь, берется с n–ого и m–ого (последнего) разряда. Значения n и циклической длины изменяются числомпериодов. Иногда n может иметь более одного значения; в любом случае вместо n можно взять m—n.

Для 4-разрядного регистра можно было бы использовать точки подключения обратной связи при n=1 и m=4.

Длина регистра сдвига обычно выбирается кратной 8. В этом случае требуется более двух точек подключения обратной связи.

m точки подключения обратной связи Длина Свойства последовательностей максимальной длины для шифровальщиков и любителей взламывать чужие шифры 1. в одном полном цикле (K тактов) число «единиц» на одну превышает число «нулей». Дополнительная единица возникает благодаря исключению нулевого состояния. При значениях длины регистра, которые обычно используются на практике, дополнительная единица не может оказать какого-либо влияния: 17-разрядный регистр (16-разрядов + флаг переноса) вырабатывает за один период 65536 единиц и 65535 нулей.

2. в каждом цикле (K тактов) половину всех единиц составляют «одиночные» (5, 6, 7, 8, 11, 12, 13, 14), четвертую часть — двойные (то есть две следующие подряд) (3, 9, 10, 11), восьмую часть — тройные (2, 15) и т.д. То же самое относится и к последовательно идущим нулям. Это говорит о том, что вероятность появления начала и конца единичного состояния не зависит от результата последнего переброса и, следовательно, вероятность завершения цепочки последовательно возникших единиц или нулей для каждого переброса составляет одну вторую.

3. если последовательность полного цикла (K тактов) сравнить с последовательностью такой же длины, но сдвинутой на любое число n бит (где n не равно 0 и не кратно K), то число несовпадений будет превышать число совпадений на единицу. Автокорреляционная функция такой последовательности при нулевой задержке представляет собой дельта-функцию Кронекера, а во всех остальных точках равна 1/K.

1. Предположим, что регистр DX содержит 1011100110111001b, а регистр CX — 3. Определите содержимое регистра DX после следующих несвязанных команд: а) SHR DX,1, б) SHR DX,5, в) SHR DL,CL, г) SHL DX,CL, д) SHL DL,1, е) ROR DL,2, ж) ROR DX,4, з) SAL DH,1.

2. Напишите программу, в которой используя команды сдвига, пересылки и сложения, умножьте содержимое регистра AX на 100.

3. Напишите программу, в которой используя команды сдвига и циклического сдвига содержимое пары регистров DX:AX: а)умножается на 4; б) делится на 4; в) 48 бит в регистрах DX:AX:BX умножается на 2.

4. Разделите 16-разрядное число, помещенное на вершину стека, на два 8разрядных числа и снова разместите их в стеке.

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

Массивы предназначены для хранения данных одного типа и для удобного доступа к содержимому этих данных. Синтаксис описания массивов таков: D< тип> DUP(?) Например, вместо того чтобы описывать двадцать переменных величиною можно записать совсем просто: X DW 20 DUP(?) При этом в переменной X хранится не одно значение, а последовательность из 20 значений типа WORD. К каждому из них можно обращаться, указывая его номер (индекс), который может быть не только числовой константой, но и любым допустимым выражением, возвращающим значение типа WORD. Элементы массива располагаются в памяти строго последовательно, друг за другом, при компиляции все упоминания переменной X заменяются на физический адрес первого элемента этого массива в оперативной памяти, а все обращения к элементам массива заменяются на машинные команды сложения адреса X с индексом — смещением соответствующего элемента относительно начала этой последовательности. Такое смещение для первого элемента будет 0. Для второго смещение будет 1 и т.д.

Для многомерных массивов ситуация аналогична. Пусть имеется двумерный массив (матрица) A, в котором N строк и M столбцов и все элементы матрицы — двойные слова:

Зависимость адреса элемента матрицы, расположенного в линейной памяти компьютера от индексов элемента, выглядит так:

Адрес(A[i,j])=A+M*(type A)*i+(type A)*j где A — положение в памяти первого элемента матрицы, для двойных слов (type A) = 4, а M — ширина строки.

До сих пор рассматривались команды, в которых для операндов из памяти указывались их точные адреса (имена), например: MOV CX,A.

Если исполнительный адрес берется непосредственно из 8-, 16-или 32-разрядного поля смещения в коде конкретной команды такая адресация называется прямой.

Однако в общем случае в команде вместе с адресом может быть указан в квадратных скобках некоторый регистр, например: MOV CX,A[BX].

Тогда команда будет работать не с указанным в ней адресом A, а с так называемым исполнительным адресом Aисп, который вычисляется по следующей формуле: Aисп = (A + [BX]) mod 216, где [BX] обозначает содержимое регистра BX. Прежде чем выполнить команду, процессор прибавит к адресу A, указанному в команде, текущее содержимое регистра BX, получит некоторый новый адрес и именно из ячейки с этим новым адресом возьмет новый операнд. Если в результате сложения получилась слишком большая сумма, то от нее берутся только последние 16 битов (на это указывает операция mod в приведенной формуле).

Замена адреса из команды на исполнительный адрес называется модификацией адреса, а регистр, участвующий в модификации, называется регистром-модификатором. В качестве модификатора для микропроцессоров ниже i80386 можно было использовать только один из четырех регистров: BX, BP, SI или DI (для микропроцессоров i80386 и выше можно использовать почти любые 32-битные регистры общего назначения).

В команде ADD A[ESI],5 в роли модификатора выступает регистр ESI. Пусть в регистре ESI находится число 100. Тогда Aисп=A+ [ESI]=A+100. Значит, по данной команде к числу из ячейки с адресом A +100 будет прибавлено 5.

Если исполнительный адрес вычисляется как сумма некоторого заданного смещения и содержимого индексного регистра, то такой способ адресации называется индексной адресацией. Индексная адресация часто используется для для организации доступа к элементам массивов. Если исполнительный адрес вычисляется как сумма некоторого заданного смещения и содержимого базового регистра BX или BP, то такой способ адресации называется относительной адресацией.

Пусть имеется массив X DW 100 DUP (?) и в регистр AX требуется записать сумму его элементов. Для нахождения суммы пишем в AX 0, а затем в цикле выполним операцию AX=AX+X[i] при i от 0 до 99. Поскольку адрес элемента X[i] равен X+2*i, то команда, соответствующая этой операции, должна быть следующей ADD AX,X+2*i, но такая команда запрещена правилами и машинного языка, и языка Ассемблер: в любой команде все ее части, в том числе и адрес, должны быть фиксированными.

Наш адрес меняется вместе с изменением индекса i. По алгоритму наша команда должна работать с переменным адресом, а правила машинного языка допускают только фиксированный адрес. Для устранения подобных противоречий была введена модификация адресов. Разобьем переменный адрес X+2*i на постоянную слагаемую X и переменную 2*i.

Постоянную составляющую запишем в команду, а переменную в регистрмодификатор (например, в SI) и название этого регистра записываем в команду в качестве модификатора ADD AX,X [SI].

Фрагмент программы нахождения суммы элементов массива X:

MOV SI,0 ;начальные значения удвоенного индекса L: ADD AX,X[SI] ;AX=AX+X[i] Пусть имеется некоторая ячейка размером в слово, адрес которой не известен, но известно, что этот адрес находится в регистре BX, и необходимо записать в эту ячейку число 300. Если бы адрес X этой ячейки был бы известен, то использовалась бы команда MOV X,300.

Команды могут работать с исполнительными адресами, возьмем такой адрес и такой модификатор, чтобы в сумме они дали этот заранее не известный адрес — в данном случае это нулевой адрес и регистр BX.

Aисп=0+[BX]=0+X=X. Задача решается командой MOV[BX],300. Особенность используемого способа модификации в том, что адрес представлен в виде суммы двух слагаемых, одно из которых записано в команду, а другое в регистр-модификатор. Но если ранее оба слагаемых были ненулевые, то теперь одно из слагаемых, которое помещалось в команду, нулевое, и поэтому весь адрес упрятан в регистр. В команде указывается лишь место (регистр), где находится адрес. Способ задания адреса через промежуточное звено называется косвенной ссылкой или косвенной адресацией.

При косвенной ссылке обычно приходится уточнять размер ячейки, на которую она указывает. Если в ячейку, адрес которой находится в регистре EBX, надо записать 0, тогда использовать для этого команду MOV[EBX],0 нельзя, так как в ней непонятны размеры операндов: 0 может быть как байтом или словом, так и двойным словом, адрес из EBX также может быть адресом как байта или слова, так и двойного слова. А вот число 300 в регистре BX может быть только словом и неоднозначности не возникает. Поэтому с помощью оператора PTR надо указать, операнды какого размера имеются в виду:

MOV BYTE PTR [BX],0 ;пересылка байта MOV WORD PTR [BX],0 ;пересылка слова MOV DWORD PTR [EBX],0 ;пересылка двойного слова 13.4. Модификация по нескольким регистрам Идея модификации обобщается на случай нескольких регистров-модификаторов. Для этого в командах указывается вместе с адресом несколько регистров-модификаторов. Максимальное количество модификаторов — два, причем для микропроцессоров ниже i80386 один из них обязательно должен быть регистром BX или BP, а другой обязательно SI или DI.

Например: MOV AX,A[BX][SI] (для микропроцессоров i80386 и выше можно использовать почти любой 32-битный регистр общего назначения).

В данном случае исполнительный адрес вычисляется по формуле:

Модификация по двум регистрам обычно используется при работе с двумерными массивами. Например в матрицу A размером 10х20 требуется записать в регистр AL количество таких строк этой матрицы, в которых начальный элемент строки встречается в ней еще раз.

При расположении элементов матрицы в линейной модели памяти адрес элемента A[i, j] равен A+20*i+j. Для хранения величины 20*i отведем регистр BX. Для хранения величины j отведем регистр SI. A[BX] — начальный адрес i-ой строки, A[BX][SI] — адрес j-го элемента этой строки.

;Внешний цикл по строкам L: MOV AH,A[BX] ;Начальный элемент строки MOV DX,CX ;Сохранить счетчик внешнего цикла ;Внутренний цикл по столбцам CMP A[BX][SI],AH ;A[i,j]=AH?

LOOPNE L1;Цикл,пока A[i,j]AH, но не более 19 раз ;Конец внутреннего цикла L2: MOV CX,DX ;Восстановить счетчик внешнего цикла Если исполнительный адрес вычисляется как сумма некоторого заданного смещения, содержимого регистра базы и индексного регистра такой способадресации называется относительной индексной адресацией. В ходе выполнения программы могут меняться две компоненты адреса.

Пусть A обозначает адресное выражение, а E — любое выражение (адресное или константное), тогда в языке ассемблера допустимы следующие три основные формы записи адресов в командах, которые задают следующие исполнительные адреса:

Модификация по одному регистру: в качестве модификатора можно использовать только один из четырех регистров: BX, BP, SI или DI.

E[M1][M2]: Aисп=(E+[M1]+[M2])mod216 M1: BX или BP; M2: SI или DI Модификация по двум регистрам: один из них обязательно должен быть регистром BX или BP, а другой обязательно SI или DI.

Если E=0, то 0 можно опустить: 0[M]=M.

Модификация не меняет тип адреса. Если X — имя байтовой переменной, то TYPEX[SI] = BYTE. Если перед модификатором указано константное выражение (1[BX]), то тип такого адреса считается неопределенным.

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

Соглашение об использовании квадратных скобок при записи операторов команд:

1. Запись в квадратных скобках имени регистра-модификатора (BX/EBX, BP /EBP, SI/ESI или DI/EDI) эквивалентна выписыванию содержимого этого регистра (имя регистра без квадратных скобок обозначает сам регистр). Заключать в квадратные скобки имена регистров, не являющиеся регистрами-модификаторами, нельзя.

2. Любое выражение можно заключить в квадратные скобки, от этого смысл не изменится, но снятие квадратных скобок может изменить Выписывание рядом двух выражений в квадратных скобках означает сумму этих выражений. [X][Y]=[X]+[Y]=[X+Y] Используя эти соглашения вместе со свойством коммутативности сложения и отталкиваясь от трех основных способов записи адресов в командах, можно получить новые варианты записи адресов.

Эквивалентные формы записи одного и того же адреса:

A+1, [A+1], [A]+[1], [A][1], A[1], 1[A], [A]+1, … Во многих инструкциях процессора 80х86 за байтом (байтами) кода (глава «Машинная кодировка команд») операции следуют байты ModR/M и SIB. Они содержат следующую информацию; тип индексации или номер регистра, который должен использоваться в инструкции, используемый регистр или дальнейшая информация для выборки инструкции, а также информацию о базе, индексе и масштабе.

Байт ModR/M содержит следующие поля информации:

o Поле mod, которое занимает два самых старших бита байта, комбинируется с полем r/m для формирования 32 возможных значений: 8 регистров и 24 режимов адресации.

o Поле reg, которое занимает следующие три поля за полем mod, определяет номер регистра или другие три бита информации о коде операции. Значение поля reg определяется первыми байтом инструкции (кодом операции).

o Поле r/m, которое занимает три младших бита байта, может задавать в качестве адреса операнда регистр или может формировать часть кодировки режима адресации в сочетании с o Формы с базовой индексацией и масштабируемой 32-разрядной адресацией требуют наличия байта SIB. Присутствие байта SIB определяется специальной кодировкой байта Байт SIB включает в себя следующие поля:

o Поле ss, которое занимает 2 старших бита байта, определяет o Поле index, которое занимает следующие 3 бита за полем ss, задает для индексного регистра номер регистра.

o Поле base, которое занимает младшие три байта бита, задает Значения и соответствующие виды адресации байт ModR/M и SIB Виды 16-разрядной адресации с байтом ModR/M

r8 AL CL DL BL AH CH DH BH

r32 EAX ECX EDX EBX ESP EBP ESI EDI

Действующий ад- ModR/M Значения ModR/M в шестнадцатиричном - "dis8" обозначает 8-битовое смещение, следующее за байтом ModR/M, которое должно расширяться по знаку и добавляться к индексу;

- "dis16" обозначает 16-итовое смещение, следующее за байтом ModR/M, которое должно добавляться к индексу;

По умолчанию для действующего адреса, содержащего индексный регистр BP, действующим адресом является регистр SS. Для других действующих адресов сегментным регистром будет регистр DS.

Виды 32-разрядной адресации с байтом ModR/M

AL CL DL BL AH CH DH BH

r8(/r)

AX CX DX BX SP BP SI DI

r16(/r)

EAX ECX EDX EBX ESP EBP ESI EDI

r32(/r) REG= Действующий ModR/M Значения ModR/M в шестнадцатиричном виде адрес - [..][..] обозначает байт SIB, следующий за байтом ModR/M;

- "dis8" обозначает 8-битовое смещение, следующее за байтом SIB, которое должно расширяться по знаку и добавляться к индексу;

- "dis32" обозначает 32-битовое смещение, следующее за байтом ModR/M, которое должно добавляться к индексу.

Масштабирован- Индекс Значения ModR/M в шестнадцатиричном - [*] и Mod = 00 означает "dis32" без базы, в противном случае [ESP].

Это обеспечивает следующие режимы адресации:

dis[индекс] (MOD = 00) dis8[EBP][индекс] (MOD = 01) dis32[EPB][индекс] (MOD = 10) Контрольные вопросы и упражнения:

1. Напишите программу, которая упорядочивает в алфавитном порядке буквы, коды которых находятся в CHAR, CHAR+1, CHAR+2.

2. Пусть имеется массив DATA_LIST из 10 слов. Напишите программу, которая: а) помещает наибольшее и наименьшее из чисел DATA_LIST в регистры BX и DX; б) вычисляет сумму и произведение чисел DATA_LIST и запоминает результаты в SUM и PRODUCT соответственно.

это область памяти, работа с которой ведется по следующему принципу: элемент, записанный в стек последним, считывается из него первым. В EXE-программах размер стека не должен превышать 64 Кбайта и начальный адрес стека должен быть кратным 16. То есть эта область должна быть сегментом памяти, ее называют сегментом стека. На его начало должен указывать сегментный регистр SS (Stack Segment). В COM-программах под стек, по умолчанию, отводят конец кодового в COM-программах код занимает начало, а стек — конец сегмента. Но программист, по своему желанию, может отвести под стек любой участок памяти.

Процессор заполняет стек снизу вверх: первый элемент записывается в самый конец области стека (в ячейку с наибольшим адресом), следующий элемент записывается «над» ним и так далее. При чтении из стека первым всегда удаляется самый верхний элемент. Поэтому получается, что низ стека фиксирован (это последняя ячейка области стека), а вот вершина стека все время сдвигается. Для того чтобы знать текущее положение вершины стека, используют регистр SP/ESP (Stack Pointer, указатель стека). В нем храниться адрес той ячейки, в которой находится элемент записанный в стек последним. Абсолютный адрес вершины стека задается регистрами SS:SP. В нем храниться адрес той ячейки, в которой находится элемент записанный в стек последним.

Элементы стека могут иметь любой размер; это могут быть байты, слова двойные слова и т.п. Но команды считывания из стека и записи в стек, в зависимости от модели микропроцессора, работают только со словами или двойными словами. Поэтому считают, что элементы стека имеют размеры слова или двойного слово.

Чтобы в EXE-программе зарезервировать в памяти место под стек, описывают соответствующий программный сегмент. Если под стек выделяется k байтов, то этот сегмент описывается так:

.stack DB k DUP (?) Ends В COM-программах место под стек отводится автоматически в конце программного сегмента, но если Вы хотите расположить стек в другом месте:

DB k DUP (?) ;выделяете там, где необходимо k байтов под стек stackend dw 0 ;указываете “дно” стека mov sp,offset stackend ;позиционируете регистр SP на “дно” стека (Поместить операнд в стек = “PUSH OPERAND ONTO STACK”) Синтаксис команды: PUSH Семантика команды: размещение содержимого операнда в стеке.

Алгоритм работы:

• уменьшить значение указателя стека ESP/SP на 4/2 (в зависимости от значения атрибута размера адреса — use16 или use32), то есть ESP/SP сдвигается вверх и теперь указывает на свободную ячейку области стека;

• в свободную ячейку области стека (адресуемую парой SS:ESP/SP) записывается значение из операнда.

Применение:

Команда PUSH используется совместно с командой POP для записи значений в стек и извлечения их из стека. Размер записываемых значений — слово или двойное слово. Так же в стек можно записывать непосредственные значения. Заметьте, что в отличие от команды POP в стек можно включать значение сегментного регистра CS. Другой интересный момент связан с регистром ESP/SP. Команда PUSH ESP/SP записывает в стек значение ESP/SP по состоянию до выдачи этой команды. В микропроцессоре i по этой команде записывалось скорректированное значение SP. При записи в стек 8-битных значений для них все равно выделяется слово или двойное слово (в зависимости от use16 или use32).

(Извлечь операнд из стека =”POP OPERAND FROM THE STACK”) Синтаксис команды: POP Семантика команды: извлечение слова или двойного слова из стека.

Алгоритм работы команды зависит от установленного атрибута размера адреса — use16 или use32:

• загрузить в приемник содержимое вершины стека (адресуется парой SS:ESP/SP);

• увеличить содержимое ESP/SP на 4 (2 байта) для use32 (соответственно для use16).

Применение:

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

MY_PROC PROC NEAR

PUSH AX

PUSH BX

;тело процедуры, в которой изменяется содержимое ;регистров AX и BX POP BX POP AX ENDP Общее правило работы со стеком. Если мы что-либо записываем в стек, то мы же и обязаны все это считать из стека (правило: “поел и убрал за собой”). Иначе при работе со стеком “концов не найдешь”. Кстати, это наиболее часто встречающаяся ошибка, допускаемая программистами пишущими на языке ассемблера, приводящая к зависанию программ — несоответствие числа команд PUSH и POP. Всегда помните простое правило:

“На каждую попу должна быть своя пуша”.

Если вам необходимо написать подряд несколько PUSH или POP, следующих друг за другом — помните о следующей возможности транслятора — он может преобразовать строку PUSH AX BX CX в последовательность команд PUSH AX, PUSH BX, PUSH CX.

Иногда Вам приходится определять множество переменных, которые Вы используете, всего один или два раза, но под которые приходиться отводить память, или Вам не хватает фантазии для задания новых имен для новых переменных. Как выход из такой ситуации — определяете одну временную переменную, например по имени X, если значения которые в этой переменной содержится Вам необходимо в дальнейшем — отправляете это значение в стек командой PUSH X, используете переменную столько сколько Вам необходимо, а затем когда переменная Вам уже не нужна — востанавливаете ее значение из стека командой POP X.

(Поместить все регистры общего назначения в стек =

“PUSH ALL GENERAL REGISTERS ONTO STACK”)

Синтаксис команды: PUSHA Семантика команды: размещение в стеке регистров общего назначения в следующей последовательности: AX, CX, DX, BX, SP, BP, SI, DI.

Алгоритм работы:

• уменьшить значение указателя стека ESP/SP на 32/16 (в зависимости от значения атрибута размера адреса — use16 или use32);

• включить в стек последовательно значения регистров общего назначения AX, CX, DX, BX, SP, BP, SI, DI.

Содержимое DI при этом будет на вершине стека. В стек помещается содержимое SP по состоянию до выполнения команды.

Применение:

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

(Извлечь все регистры общего назначения из стека =”POP ALL GENERAL

REGISTERS FROM THE STACK”)

Синтаксис команды: POPA Семантика команды: извлечь из стека регистры общего назначения в следующем порядке DI, SI, BP, SP, BX, DX, CX, AX.

Алгоритм работы:

• извлечь из стека последовательно значения и загрузить ими регистры общего назначения DI, SI, BP, SP, BX, DX, CX, AX. Содержимое DI восстанавливается первым. Содержимое SP извлекается, но не восстанавливается;

• увеличить значение указателя стека ESP/SP на 16.

Применение:

Команда POPA по принципу работы является обратной команде PUSHA и используется для восстановления содержимого всех регистров общего назначения значениями из стека. Эту команду можно использовать в процедурах и программах обработки прерываний для восстановления регистров общего назначения прерванной программы.

MY_PROC PROC NEAR

PUSHA ;тело процедуры, в которой изменяется ;содержимое регистров общего назначения ••• POPA ENDP (Размещение всех регистров общего назначения в стеке =PUSH ALL GENERAL DOUBLE WORD REGISTERS ONTO STACK) Синтаксис команды: PUSHAD Семантика команды: размещение в стеке регистров общего назначения в следующей последовательности: EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI.

Алгоритм работы:

• уменьшить значение указателя стека ESP на 32;

• включить в стек последовательно значения регистров общего назначения EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI.

Содержимое EDI при этом будет на вершине стека. Содержимое ESP включается по состоянию на момент, предшествовавший выполнению данной команды.

Применение:

Команда PUSHAD используется совместно с командой POPAD для сохранения и восстановления всех регистров общего назначения. Эти команды используются аналогично командам POPA и PUSHA.

(Извлечение всех 32-разрядных регистров общего назначения из стека =

POP ALL GENERAL DOUBLE WORD REGISTERS FROM THE STACK)

Синтаксис команды: POPAD Семантика команды: извлечение из стека регистров общего назначения EDI, ESI, EBP, ESP, EBX, EDX, ECX, EAX.

Алгоритм работы:

извлечь из стека последовательно значения и загрузить ими 32-разрядные регистры общего назначения EDI, ESI, EBP, ESP, EBX, EDX, ECX, EAX. Содержимое EDI восстанавливается первым. Содержимое ESP извлекается, но не восстанавливается;

• увеличить значение указателя стека ESP на 32.

Применение:

Команда POPAD по принципу работы является обратной команде PUSHAD и используется для восстановления всех 32-разрядных регистров общего назначения. Эту команду можно использовать в процедурах и программах обработки прерываний для восстановления регистров общего назначения прерванной программы:

MY_PROC PROC NEAR

PUSHAD

;тело процедуры, в которой изменяется ;содержимое регистров общего назначения ••• POPAD ENDP 14.1.2. Команды записи и чтения в стек регистра флагов (Поместить слово из регистра флагов в стек =”PUSH FLAGS REGISTER Синтаксис команды: PUSHF Семантика команды: размещение в вершине стека (SS:SP) содержимого регистра флагов FLAGS.

Алгоритм работы:

• уменьшить значение указателя стека SP на 2;

• поместить в вершину стека содержимое регистра FLAGS.

Применение:

Команда PUSHF может использоваться для получения содержимого регистра флагов. Как известно, прямой доступ к регистру флагов невозможен, поэтому данная команда является одной из немногих команд, позволяющих получить доступ к регистру флагов как к содержимому обычного регистра. Обратное действие, то есть восстановление — возможно измененного слова — в регистр флагов, осуществляется командой POPF. Эта команда может использоваться в программах обработки прерываний и в других случаях, когда необходимо сохранить локальный контекст процесса вычисления. Довольно часто используется в антихакерских программах для отслеживания флага трассировки (TF 8 бит регистра EFLAGS). Если этот флаг взведен, значит некто изучает вашу программу, значит необходимо принимать меры:

;извлечь значение регистра FLAGS и изменить ;значение флага TF на обратное TEST AX,100000000b

JNZ EXIT

XOR AX,100000000b

PUSH AX

EXIT:

(Извлечь из стека слово в регистр флагов =”POP FLAGS REGISTER Синтаксис команды: POPF Семантика команды: извлечение из стека слова и восстановление его в регистр флагов FLAGS.

Алгоритм работы:

• извлечь из вершины стека слово и поместить его в регистр FLAGS;

• увеличить значение указателя стека ESP на 2.

Применение:

Команда POPF по принципу работы является обратной команде PUSHF и используется для восстановления из стека содержимого регистра флагов EFLAGS. Возможным вариантом использования этой команды являются программы обработки прерываний или другие случаи, в которых необходимо сохранять некоторый локальный контекст процесса вычисления. Изза того, что регистр EFLAGS/FLAGS непосредственно недоступен, команда POPF является одной из немногих возможностей влияния на его содержимое:

;установить значение регистра FLAGS в 03h MOV AX,3h

PUSH AX

POPF (Поместить двойное слово из расширенного регистра флагов в стек

=”PUSH EFLAGS DOUBLE WORD REGISTER ONTO STACK”)

Синтаксис команды: PUSHFD Семантика команды: размещение в стеке содержимого регистра флагов EFLAGS.

Алгоритм работы:

• уменьшить значение указателя стека ESP на 4;

• записать в вершину стека двойное слово, представляющее собой содержимое регистра EFLAGS.

Применение:

Команды PUSHFD и POPFD используются аналогично командам PUSHF и POPF. Команда PUSHFD применяется для получения содержимого регистра флагов. Как известно, прямой доступ к регистру флагов невозможен, поэтому данная команда является одной из немногих команд, позволяющих получить доступ к регистру флагов как к содержимому обычного регистра. Обратное действие, то есть восстановление — возможно измененного слова — в регистр флагов, осуществляется командой POPFD. Эта команда может использоваться в программах обработки прерываний или в других случаях, когда необходимо сохранить локальный контекст процесса вычисления:

;извлечь значение регистра EFLAGS и изменить ;значение флага CF на обратное

PUSHFD

POP EAX

XOR EAX,01h

PUSH EAX

POPFD (Извлечь двойное слово из стека в расширенный регистр флагов = ”POP

EFLAGS DOUBLE WORD REGISTER FROM THE STACK”)

Синтаксис команды: POPFD Семантика команды: извлечение из стека двойного слова и восстановление его в регистр флагов EFLAGS.

Алгоритм работы:

извлечь из вершины стека двойное слово и поместить его в регистр EFLAGS;

• увеличить значение указателя стека ESP на 4.

Применение:

Команда POPFD по принципу работы является обратной команде PUSHFD и используется для восстановления из стека содержимого регистра флагов EFLAGS. Необходимо отметить, что команда POPFD не влияет на состояние флагов VM и RF:

;установить значение регистра EFLAGS в 03h MOV EAX,3h

PUSH EAX

POPFD EAX ;установить новое значение EFLAGS Очевидное использование стека для временного хранения значений регистров. Если нам надо сохранить текущее значение какого-нибудь регистра и в то же время этот регистр нам нужен для иных целей — отправляем в стек значение из этого регистра, а через какое-то время восстанавливаем его значение из стека.

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

PUSH M POP M2 ;M2:=M Либо можно непосредственно писать в память /регистр /сегментный регистр через стек. Например, достаточно часто используемая конструкция — позиционирование регистра ES на область видеопамяти (0A000h):

Таблица 22.

B8 00 A0 MOV AX,0A000h То же самое, но с использованием стека занимает на 1 байт меньше 68 00 A0 PUSH 0A000h Команды PUSH и POP не осуществляют проверку на выход за пределы стека, то есть ошибка не будет зафиксирована, если мы читаем из пустого стека или пишем в стек, когда он уже полон. Такие проверки обязаны делать мы сами. Например, если SP/ESP = 0 — значит стек пуст. А если при размере стека в k байт SP/ESP = k, значит стек полон и туда нельзя писать.

Иногда приходится очищать стек, то есть удалять из него несколько последних элементов, никуда их не переписывая. Как это делается?

Вариант первый — лобовой: выбираем ненужный регистр и делаем в него команду POP столько раз, сколько нужно. Например, очистка стека от 3 слов с помощью регистра AX: POP AX Вариант второй — нам нужно удалить из стека N слов. После удаления N слов значение регистра SP/ESP должно увеличиться на 2*N. Поэтому принудительную очистку стека от N слов можно провести командой Вариант третий — запоминаем вначале то значение указателя стека SP/ESP, до которого затем нужно очистить стек, после чего пишем в стек все что угодно, а в конце восстанавливаем в SP/ESP это значение:

MOV AX,SP MOV SP,AX 14.2.5. Доступ к элементам стека. Регистр BP/EBP BP,SP WORD1 Команды PUSH и POP дают доступ только к Рисунок 20. Например, в стеке записано не менее трех слов, а нам необходимо отправить в регистр AX третий сверху элемент стека. Адрес третьего сверху слова стека равен адресу вершины стека +4.

Поэтому устанавливаем регистр BP/EBP (base pointer, указатель базы) на вершину стека и используем выражение [BP+4] для доступа к третьему Первое: почему мы напрямую не использовали конструкцию [SP+4]? Потому, что регистр SP не относится к числу регистров модификаторов.

Второе: в команде MOV AX,[BP+4] не указан никакой сегментный регистр, так как он выбран по умолчанию. Для регистра-модификатора BP по умолчанию префикс SS, поэтому команда воспринимается как MOV AX,SS:[BP+4]. Если вместо BP использовать модификатор BX, тогда по умолчанию берется сегментный регистр DS и нам бы явно пришлось указать нужный префикс MOV AX,SS:[BX+4].

Для доступа к N-му внутреннему слову стека необходимо установить BP на вершину стека и использовать конструкцию [BP+ N *2—2] Ранние программы представляли собой бесконечные колонки двоичных чисел. Много позже программисты изобрели мнемонику для обозначения двоичных чисел эквивалентными псевдословами. Так из машинного языка произошел язык Ассемблер. Следующим шагом были придуманы макросы, призванные упростить написание программ за счет наиболее часто встречающихся фрагментов кода. Макросы (макроопределения) представляют из себя символическое имя, за которым стоит некий логически цельный набор инструкций. В тех местах программ, где должны встретиться подобные фрагменты, подставляются имена этих фрагментов, которые при компиляции автоматически подменяются соответствующими последовательностями команд. Поскольку при использовании макросов происходит простое механическое замещение их имен соответствующими фрагментами кода, то получается, что при таком подходе наиболее популярные макроопределения (которые могут встречаться в крупных программах сотнями) многократно тиражируются в теле программы, приводя к непомерному распуханию исполняемого кода. К счастью, на этой стадии прогресс тоже не остановился. Для решения проблемы достаточно было привязать блоки кода к определенному месту в памяти и обеспечить механизм передачи им управления в нужный момент. Так, собственно, и появились функции, они же процедуры, они же подпрограммы.

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

При использовании подпрограмм основная программа становится более компактной. Кроме того использование подпрограмм облегчает поиск ошибок.

Где угодно. При этом не нужно забывать, что сама по себе подпрограмма не должна выполняться, а может выполняться только тогда, когда к ней обращаются. Поэтому ее размещают таким образом, чтобы на нее случайно не попало управление, например, сразу же за командами JMP, RET, IRET. Если имеется несколько подпрограмм, то их располагают рядом.

Обычно подпрограммы размещают либо в конце сегмента команд за командой INT 20h (или MOV AX,4C00h INT 21h или.exit0) (рисунок 21a), либо в самом начале этого сегмента — перед той командой, с которой должно начинаться выполнение программы (рисунок 21b). В больших программах подпрограммы нередко размещают в отдельном сегменте команд (рисунок 21c).

Описание подпрограммы в виде процедуры:

PROC RET ENDP Перед телом процедуры (ее командами) ставится директива PROC (procedure), за телом — команда RET (возврат из процедуры, return), после команды RET — директива ENDP (end of procedure). В обеих этих директивах указывается одно и то же имя — имя, которое мы дали подпрограмме. В директиве PROC после имени не ставится двоеточие, однако это имя считается меткой. Это имя указывается в команде перехода CALL, и тогда будет осуществлен переход на первую команду подпрограммы.

У директивы PROC есть параметр — это либо NEAR (близкий), либо FAR (дальний). Параметр может и отсутствовать, тогда считается, что он равен NEAR (в связи с этим параметр NEAR обычно не указывается). При параметре процедура называется близкой, при параметре FAR — дальней.

К близкой процедура можно обращаться только из того сегмента, где она описана, а к дальней — из любого сегмента.

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

Синтаксис команды: CALL Семантика команды:

• передача управления близкой или дальней процедуре с запоминанием в стеке адреса точки возврата;

• переключение задач.

Алгоритм работы (определяется типом операнда):

• метка ближняя — в стек заносится содержимое указателя команд EIP/IP и в этот же регистр загружается новое значение адреса, соответствующее метке;

• метка дальняя — в стек заносится содержимое указателя команд EIP/IP и CS. Затем в эти же регистры загружаются новые значения адресов, соответствующие дальней метке;

• R16, 32 или M16, 32 — определяют регистр или ячейку памяти, содержащие смещения в текущем сегменте команд, куда передается управление. При передаче управления в стек заносится содержимое указателя команд EIP/IP;

• указатель на память — определяет ячейку памяти, содержащую 4 или 6байтный указатель на вызываемую процедуру. Структура такого указателя 2+2 или 2+4 байта. Интерпретация такого указателя зависит от режима работы микропроцессора:

в реальном режиме — в зависимости от размера адреса (use16 или use32) первые два байта трактуются как сегментный адрес, вторые два/четыре байта как смещение целевой метки передачи управления. В стеке запоминается содержимое регистров CS и EIP/IP;

в защищенном режиме — интерпретация цели передачи управления зависит от значения байта AR дескриптора, определяемого селекторной частью указателя. Целью здесь являются дальний вызов процедуры без изменения уровня привилегий, дальний вызов процедуры с изменением уровня привилегий или переключение При переключении задачи значения флажков изменяются в соответствии с информацией о регистре EFLAGS в сегменте состояния TSS задачи, на которую производится переключение.

Применение:

Как видно из описания алгоритма, команда CALL позволяет организовать гибкую и многовариантную передачу управления на подпрограмму с сохранением адреса точки возврата.

call a a1: pop eax; Получение значение eip в eax push MB_OK push msg db ‘Hello, World!’, a1: push call _impMessageBoxA (Возврат ближний /дальний из процедуры = Семантика команды: возврат управления из процедуры вызывающей программе.

Алгоритм работы (работа команды зависит от типа процедуры):

• для процедур ближнего типа — восстановить из стека содержимое EIP/IP;

• для процедур дальнего типа — последовательно восстановить из стека содержимое EIP/IP и сегментного регистра CS.

• если команда RET имеет операнд, то увеличить содержимое ESP/SP на величину операнда число; при этом учитывается атрибут режима адресации — use16 или use32:

если use16, то SP=(SP+число), то есть указатель стека сдвигается на число байт, равное значению число;

если use32, то SP=(SP+2*число), то есть указатель стека сдвигается на число слов, равное значению число.

Применение: команду RET необходимо применять для возврата управления вызывающей программе из процедуры, управление которой было передано по команде CALL. На самом деле микропроцессор имеет три варианта команды возврата RET — это RET, ее синоним RETN (return near), а также команда RETF (return far). Они отличаются типами процедур, в которых используются. Команды RET и RETN служат для возврата из процедур ближнего типа. Команда RETF — команда возврата для процедур дальнего типа. Какая конкретно команда будет использоваться, определяется компилятором; программисту лучше использовать команду RET и доверить транслятору самому сгенерировать ее ближний или дальний вариант. Количество команд RET в процедуре должно соответствовать количеству точек выхода из нее.

Некоторые языки высокого уровня, к примеру Pascal, требуют, чтобы вызываемая процедура очищала стек от переданных ей параметров. Для этого команда RET содержит необязательный параметр число, который, в зависимости от установленного атрибута размера адреса, означает количество байт или слов, удаляемых из стека по окончании работы процедуры:

MY_PROC PROC ••• ENDP Команду RET можно также использовать для завершения программы.

Немного теории. Загруженным в память COM и EXE файлам предшествует префикс программного сегмента (Program Segment Prefix или PSP). Первые два байта этого префикса содержат команду INT 20h (возврат в DOS). При загрузке программы устанавливает в четырех сегментных регистрах (CS, DS, ES и SS) адрес первого байта PSP. Затем устанавливается указатель стека (SP) на конец сегмента объемом 64 Кбайт (FFFE), то есть на конец памяти, если сегмент недостаточно большой. В вершину стека заносится нулевое слово. В командный указатель (IP) помещается 100h (размер PSP). После этого управление передается по адресу регистровой пары CS:IP, то есть на адрес непосредственно после PSP. Этот адрес является началом выполняемой COM-программы и должен содержать исполняемую команду.

При выходе из программы команда RET заносит в регистр IP нулевое слово, которое было записано в вершину стека при инициализации. В этом случае в регистровой паре CS:IP получается адрес первого байта PSP, где находится команда INT 20h. При выполнении этой команды управление передается в резидентную часть COMMAND.COM (Если программа завершается по команде INT 20h вместо RET, то управление непосредственно передается в COMMAND.COM) Стандартное завершение программы занимает 6 байтов кода:

B4 4C MOV AH,4Ch ;функция завершения программы B0 00 MOV AL,0 ;код завершения (в данном случае 00h) B8 00 4C MOV AX,4C00h ;функция завершения программы команда INT 20h состоит из 2 байт — CD команда RET занимает всего 1 байт кода C3. Вы можете завершить программу командой RET, если у Вас завершены все операции со стеком, иначе Вам придется воспользоваться сочетанием команд PUSH 0 и RET (код 6A 00 C3 — это 3 байта, и тогда предпочтительнее двухбайтовая команда INT 20h). Из сочетания PUSH 0 и RET можно сделать “приятной сюрприз” для любителей копаться в чужих программах.

Нетрудно также подсчитать количественные данные об экономии команд и потере времени при использовании процедур. Предположим, что группа команд занимает k байтов и встречается в программе m раз. Если эта группа команд оформлена как процедура, то в подпрограмме понадобится дополнительная команда RET для выхода из процедуры (1 байт), и, кроме того, по одной команде с CALL адресом процедуры в каждом из m мест, где необходим вызов процедуры (для ближнего вызова процедуры байта). В сумме это дает байт 3m+k+1, а не mk, и поэтому экономия составляет: mk -3m-k-1=(m-1)(k-3)- Если m равно 1, то использование процедур не может дать какой-либо экономии; это совершенно очевидно. Если k равно 4, то чтобы получить выигрыш, m должно быть больше 5.

Количество потерянного времени равно общей длительности выполнения дополнительных команд CALL и RET (n тактов синхронизации), которые отсутствуют, если не используются процедуры; следовательно, если процедуры во время выполнения программы вызываются t раз, то требуется tn дополнительных тактов синхронизации Предположим, что Вы пишете программную оболочку наподобие «Far», «Волков» или «Norton Commander». Помните, как они устроены?

Если Вы нажали на клавишу F1 — вызывается помощь, на F2 — вызов меню, F3 — вызов программы просмотрщика, F10 — выход из программы.

На языке ассемблера это выглядит примерно так:

F0: MOV AH,0 ;функция 00h прерывания 16h — чтение ; ожиданием нажатия клавиши. При выходе из функции в ; регистре AL находится ASCII-код клавиши, в регистре ; AH скан-код нажатой клавиши CMP AH,3Bh ;Scan код клавиши F1 равен 3Bh.

JNE F2 ;Сравниваем,что находится в регистре CALL HelpProc ; AH. Если нажали на клавишу F ;вызываем процедуру вывода помощи на экран. После JMP F0 ;завершения процедуры возвращаемся к ;началу программы, где снова ожидаем нажатия клавиши.

;Если нажатая клавиша не F1, переходим на следующую ; метку F2 и т.д.

F2: CMP AH,3Ch ;проверяем Scan код клавиши F F3: CMP AH,3Dh ;проверяем Scan код клавиши F F10: CMP AH,44h ;проверяем Scan код клавиши F10.

; не F10, возвращаемся в начало программы Налицо в программе очень много повторяющихся почти одинаковых фрагментов. Нельзя ли написать как-нибудь иначе? Чем различаются эти фрагменты программы? Разными операндами в команде CMP, разными адресами переходов и разными вызываемыми процедурами. Уберем операнды команды CMP в какой-нибудь регистр и используем для разных адресов переходов косвенный переход, а для разных вызываемых процедур — косвенный вызов процедуры.

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

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

;таблица-диспетчер — в нее внесены сканкоды возможных ; нажатых клавиш и адреса соответствующих этим ; клавишам вызываемых процедур DW OFFSET ViewerProc ;адрес процедуры ;ViewerProc DW OFFSET ExitProc ;адрес процедуры ExitProc F0: MOV AH,0 ;ожидаем нажатия на клавишу AGAIN: CMP BYTE PTR [BX],0 ;это конец таблицы? Если CMP AH,[BX] ;Иначе, проверим сканкод. Если Основной вид команды CALL, когда в качестве операнда указывается имя процедуры. Но возможны все варианты операнда, используемого в команде безусловного перехода JMP. Примеры:

NA DW P FA DD Q P PROC P1: … P ENDP

Q PROC FAR

Q1: … Q ENDP CALL P1 ;близкий переход на P1 с возвратом CALL FAR PTR Q1 ;дальний переход на Q1 с возвратом CALL Q1 ;близкий переход на Q1 с возвратом LEA BX,Q CALL DWORD PTR [BX] ;дальний вызов процедуры Q При использовании этих вариантов команды CALL надо соблюдать осторожность.

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

Второе: как и в команде JMP, при использовании в команде CALL ссылок вперед или косвенных ссылок следует, если надо, уточнить тип этих ссылок (по умолчанию при ссылке вперед ассемблер транслирует близкий прямой вызов, а при косвенной ссылке — близкий косвенный вызов).

Если Вы вызываете процедуру FP, описанную как дальнюю, командой CALL FP, или Вы вызываете процедуру командой CALL FAR PTR FP транслятор генерирует последовательность команд PUSH CS и CALL 00: E81C00 CALL 0000001F CALL F1; ближний вызов F 04: E81900 CALL 00000020 вызов процедуры F 07: CALL 0000:FFFF db 9Ah,0FFh,0FFh,0,0; дальний вызов 9AFFFF0000 процедуры с указанием непосредственного адреса (только в кодах) 0C: FF13 CALL [BP+DI] CALL [BP+DI] 0E: FF1B CALL FAR db 0FFh,1Bh; дальний вызов [BP+DI] 0E: CALL [BX+9A00] CALL [BX+9A00h] FF97009A 12: CALL FAR db 0FFh,9Fh,0,9Ah; дальний вызов проFF9F009A [BX+9A00] цедуры с регистровой относительной 17: FF17 CALL [BX] 1A: FF1F CALL FAR [BX] ES:[BX] (только в кодах) Чтобы сгенерировать дальние вызовы процедур и сэкономить 1 байт на команде PUSH CS (код 0E) необходимо написать код вызова процедуры и добавить ко второму байту команды 8, например: код FF17 соответствует команде CALL [BX], а код FF17+0008=FF1F — команде CALL FAR [BX].

Попытка напрямую оттранслировать команду «CALL FAR PTR ES:

[BX]» вызывает ошибку транслятора «Argument to operation or instruction has illegal size». Попытка оттранслировать вот такую команду «CALL FAR ES:[BX]» вызывает следующую ошибку транслятора «Illegal immediate».

При трансляции же команды «CALL FAR [BX]» будет сгенерирован код FF57FE, почему то соответствующий команде «CALL [BX-2]», которая дальним вызовом не является, вероятнее всего это ошибка транслятора.

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

Чтобы обмана хакера можно:

1) использовать команду «CALL метка» вместо «JMP метка», правда помните, что в стеке останится адрес выхода 2) использовать сочетание двух команд «PUSH адрес возврата» и «JMP адрес процедуры» вместо команды «CALL процедура»

в микропроцессоре Intel 8080 имелись следующие команды, которых нет теперь уже в i80x86:

команды CC переход к подпрограмме по единичному значению CF= CNC переход к подпрограмме по нулевому значению CF= CZ переход к подпрограмме по нулевому значению ре- ZF= CNZ переход к подпрограмме по ненулевому значению ZF= CP переход к подпрограмме по положительному значе- SF= CM переход к подпрограмме по отрицательному значе- SF= CPE переход к подпрограмме по четному значению кода PF= CPO переход к подпрограмме по нечетному значению PF= RC выход из подпрограммы по единичному значению CF= RNC Выход из подпрограммы по нулевому значению CF= RZ выход из подпрограммы по нулевому значению ре- ZF= RNZ выход из подпрограммы по ненулевому значению ZF= RP выход из подпрограммы по положительному значе- SF= команды RM выход из подпрограммы по отрицательному значе- SF= RPE выход из подпрограммы по четному значению кода PF= RPO выход из подпрограммы по нечетному значению PF= используя сочетание двух команд «PUSH адрес возврата» и «Jcc адрес процедуры» вместо команды «CALL процедура» можно воскресить команды Информацию можно передать функции двумя способами: внешними переменными и с помощью механизма параметров. Хотя существуют исключения, передача параметров предпочтительнее. Такой подход ведет к улучшению модульности кода и уменьшению вероятность нежелательных побочных эффектов.

15.6.1. Передача параметров через регистры 15.6.1.1. Передача параметров по значению Иногда требуется передать процедуре фактические параметры. Простейший способ — передавать параметры через регистры: основная программа записывает фактические параметры в какие-то регистры, а процедура затем берет их оттуда и использует в своей работе. Результат процедура может так же записать в какой-нибудь регистр, а основная программа затем извлекает его оттуда. Через какие регистры будут переданы параметры и через какие регистры будет возвращен результат — это личное дело автора программы, хотя эта проблема уже обсуждалась в главе «Три закона модульного конструирования».

Пример: вычисляем d=min(a,b)+min(5,a-1). Вычисление min(x,y) опишем как процедуру, причем первый параметр x передадим через регистр AX, второй y — через BX, результат d вернем через регистр AX:

;процедура AX=min(AX, BX)

MIN PROC FAR

MIN ENDP

;основная программа MOV AX,a MOV BX,b CALL MIN ;AX=min(a,b) MOV AX, MOV BX,a CALL MIN ;AX=min(5,a-1) ADD d,AX ;d= min(a,b)+ min(5,a-1) 15.6.1.2. Передача параметров по ссылке Пусть имеется процедура D с параметром x и пусть процедура производит следующее вычисление x=x/16. В основной программе есть обращения к нашей процедуре D(A) и D(B), где A и B имена переменных, значениями которых являются неотрицательные числа. Процедуре надо знать адреса ячеек A и B, в которую она должна сделать запись, и этот адрес обязана ей сообщить основная программа. Передача параметров по ссылке означает передачу адреса ячейки, соответствующей фактическому параметру. Передача адреса происходит через регистр — основная программа записывает в регистр адрес фактической переменной, а процедура берет его оттуда. Обычно для этих целей используют регистры-модификаторы (BX, BP, SI или DI) так как процедуре придется модифицировать адрес по этому регистру. Пусть для процедуры D мы выбрали регистр BX, то есть к началу выполнения процедуры в регистре BX будет находиться адрес той ячейки (A или B), содержимое которой процедура обязана изменить. В подобном случае добраться до ячейки можно с помощью косвенной ссылки (конструкция [BX]):

;Процедура D(x) D PROC; BX = адрес x SHR WORD PTR [BX],4; x=x/ RET D ENDP ••• ;фрагмент основной программы LEA BX,A; BX = адрес A CALL D;D(A) ••• LEA BX,B; BX = адрес B CALL D;D(B) При работе вашей процедуре могут потребоваться любые регистры, но может ли процедура менять значения в регистрах? С большой вероятностью основной программе и процедуре могут потребоваться одни и те же регистры. Конечно, можно договориться, чтобы основная программа и процедура использовали разные регистры, однако на практике добиться этого достаточно сложно. Поэтому основную программу и процедуру не ограничивают в использовании регистров, но при этом требуют от процедуры, чтобы она сохраняла то значение в регистрах, которое туда записала основная программа. Достигается это тем, что в начале работы процедура помещает в стек значение тех регистров, которые ей понадобятся для работы, а перед выходом из процедуры из стека восстанавливаются прежние значения в регистрах (смотрите раздел «Модульное конструирование проедур»). Достаточно часто для этого используют команды PUSHA и POPA, сохраняя и восстанавливая из стека значения всех регистров, даже и неиспользуемых, «на всякий случай», так как в дальнейшем процедура может модифицироваться и начать работать с теми регистрами, которые в данный момент не используются. Сохранять значение регистра, через который процедура возвращает результат не надо, поскольку в изменении этого регистра и заключается цель работы процедуры.

15.6.1.4. Передача параметров сложных типов Пусть происходит передача параметра по ссылке — при этом параметром является данное сложного типа (массив, структура и т.п.). Даже если процедура не меняет это данное, ей все равно обычно передается не само данное (для него может просто не хватить регистров), а его начальный адрес. Зная этот адрес, процедура может добраться до любой части этого данного.

Пусть имеются массивы из чисел: X DB 100 DUP (?) и требуется записать в регистр DL сумму максимальных элементов этих массивов DL=max(X[i])+max(Y[j]). Поскольку дважды приходится находить максимальный элемент массива, то чтобы не повторяться, опишем это действие в виде процедуры MAX. Пусть начальный адрес массива передается через регистр BX, количество элементов — через регистр CX, а результат возвращается через регистр AL:

MAX PROC;AL=max(W[0…N-1]),где BX=начальный адрес W, ;CX=N PUSH BX CX ;сохраняем регистры MOV AL,0 ; начальное значение максимума AL= MAX1: CMP [BX],AL MOV AL,[BX] ;присваиваем AL значение AL=W[i]

MAX ENDP

вычисления ;DL=max(X[i])+max(Y[j]) MOV CX,100 ;CX=количество элементов массива X

CALL MAX

Передача параметров через регистры — вещь удобная и используется очень часто. Однако параметров может быть много и регистров попросту не хватит. В таком случае используется передача параметров через стек: основная программа записывает фактические параметры (их значения или адреса) в стек, а процедура их оттуда извлекает.

Пусть процедура P имеет k параметров P(a1,a2,…,ak). Перед обращением к процедуре основная программа записывает параметры в стек. Параметры записываются в стек слева направо: сначала первый параметр, затем второй и т.д. Команды основной программы, реализующие обращение к процедуре:

;обращение P(a1,a2,…,ak) SP Адрес возврата CALL P Рисунок 22. Состояние стека перед входом в процедуру Как процедуре добраться до параметров? Как добраться к элементам стека без считывания из стека? Воспользуемся регистром BP/EBP, засылаем в него адрес вершины стека (содержимое регистра SP/ESP), а затем используем выражение вида [BP+i] для доступа к параметрам процедуры.

Мы можем испортить содержимое регистра BP/EBP, а он может использоваться в основной программе. Поэтому сначала сохраним значение этого регистра в стеке, а затем пересылаем в регистр BP/EBP значение регистра SP/ESP:

RET Рисунок 23. Состояние стека после входа в процедуру После записи в стек старого значения BP (BPстарое) для доступа к параметрам процедуры надо использовать следующие выражения: [BP+4] — для доступа к последнему параметру, [BP+6] — для доступа к предпоследнему параметру и так далее. Например, считывание последнего параметра ak в регистр AX осуществляется командой MOV AX,[BP+4]. После того как процедура отработает, стек должен быть в том же состоянии, в каком он был до использования процедуры. Восстанавливаем командой POP BP старое значение BP и после этого возможны два варианта завершения процедуры:

1. После выполнения команды RET в стеке остаются использованные процедурой параметры и основная программа должна очистить стек. Но было бы лучше, если очистку стека будет делать сама процедура, так как обращений к процедуре может быть много и в основной программе команду ADD SP,2*k придется повторять многократно (рисунок 24a).

Существует правило: если что-то может сделать и основная программа и процедура, то лучше, если это будет делать процедура.

ADD SP,2*k ;принудительная очистка стека 2. Процедура сама очищает стек от параметров и передает управление по адресу возврата (рисунок 24b):

POP BP ;восстановить старое значение BP RET 2*k ;принудительная очистка стека По команде RET операнд из стека удаляется адрес возврата, затем стек очищается на указанное операндом число байтов и для перехода по адресу возврата выполняются следующие действия:

• Значение из стека в IP/EIP • [значение из стека в CS] • SP=SP+ операнд (действие по восстановлению значения CS из стека выполняется лишь при дальнем возврате).

Команда RET — это на самом деле команда RET 0, то есть возврат без очистки стека. Операнд команды RET указывает на сколько байтов, а не слов надо очищать стек. Для очистки стека от k параметров, каждый из которых имел размер слова, был указан операнд 2*k. В операнде не должен учитываться адрес возврата — команда RET считывает его до очистки стека.

Локальные данные процедуры — это величины, используемые только на время выполнения процедур. Обычно локальные данные размещают в регистрах, но если в процедуре много локальных данных, регистров может не хватить, а размещать локальные данные в памяти не рационально, так как после выполнения процедуры это место в памяти будет расходоваться напрасно. Поэтому для локальных данных выделяют место в стеке — при входе в процедуру в вершине стека захватывается нужное число байт для локальных данных, а перед выходом из процедуры это место освобождается. Для этого в стеке запоминается текущее значение регистра BP, а затем в регистр BP устанавливается значение SP, уменьшенное на число байт требуемых для локальных данных. Например, процедуре P требуется 3 байта (2 байта под локальную переменную A и 1 байт под локальную переменную B):

MOV BP,SP BP BP

Доступ к локальным данным осуществляется с помощью выражения вида [BP-k]: [BP-2] — адрес локальной переменной A, [BP-3] — адрес локальной переменной B. После завершения работы процедуры необходимо выполнить команды:

MOV SP,BP ;отказ от места для локальных переменных POP BP ;восстановление старого значения BP P ENDP Для большинства функций вход и выход из них программисты осуществляют одинаково. Вход в функцию называется прологом (prolog), а выход из функции — эпилогом (epilog). Во время пролога программа готовится к доступу к локальным переменным и параметрам функции. Этот доступ называется кадром стека (stack frame).

PUSH EBP ;сохранить значения регистра кадра стека MOV EBP,ESP ;записать в регистр кадра стека значение SUB ESP,N ;увеличить объем стека для хранения Была даже специально создана команда ENTER N, эквивалент последовательности команд PUSH EBP/MOV EBP,ESP/SUB ESP,N, но выполняется команда ENTER N медленнее, чем последовательность команд PUSH EBP/MOV EBP,ESP/SUB ESP,N MOV ESP,EBP ;восстановить значение стека POP EBP ;восстановить значение регистр кадра Команда LEAVE является эквивалентом последовательности команд MOV ESP,EBP/POP EBP, но выполняется команда LEAVE быстрее чем последовательность команд MOV ESP,EBP/POP EBP.

Иногда алгоритм определяется в собственных терминах; другими словами, алгоритм вкладывает сам в себя таким образом, что вычислительная процедура вызывает саму себя. Например, алгоритм Гонера вычисления полинома ((…(anx+an-1)x+an-2)x+…)x+a1)x+a0 включает в себя перемножение x и an, прибавление к результату an-1, умножение на x полученного результата, прибавление к новому результату an-2 и так далее. Следующий результат получается путем умножения предыдущего результата на x и прибавление очередного коэффициента. Однократное применение алгоритма состоит в умножении и сложении, а окончательный результат получается вложением простого алгоритма умножения-сложения в самого себя.

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

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

Рекурсивные алгоритмы допускают реализацию в виде процедуры, которая вызывает сама себя. Но при этом необходимо обеспечить, чтобы каждый последовательный вызов не разрушал параметров и результатов, полученных предыдущим вызовом и чтобы процедура не модифицировала сама себя. Это означает, что каждый вызов должен запоминать свой набор параметров, регистры и все промежуточные результаты в различных областях памяти. Чтобы гарантировать использование таких отдельных областей памяти, обычно применяется стек. При включении данных в стек их можно удобно считывать в обратном порядке по мере того, как процесс выходит из вложений. Запоминаемые при подобной организации процедуры данные называются кадром, и простой доступ к отдельным элементам в кадре реализуется через регистр BP. Обычно кадр хранит запомненное содержимое регистров, адреса параметров и рабочую (временную) область памяти. Правильное запоминание информации в стеке обеспечивает упорядоченный выход. Для предотвращения бесконечного вложения рекурсивный алгоритм должен включать в себя условный переход, который в конце концов прекращает вложение.

Ниже даны тривиальные примеры рекурсивных процедур вычисления числа Фибоначчи и вычисления факториала.

Функция, вычисляющая число Фибоначчи по правилу:

Аргумент функции передается через регистр AL, а значение возвращается через регистр BX:

; BX=F(n) — число Фибоначчи с номером n (AL=n)

FIBONACCI PROC

JA F ;нерекурсивная ветвь MOV BX,1 ;если n1 BX=F(n)= RET ;рекурсивная ветвь F1: PUSH AX ;сохранить AX (будем менять) CALL FIBONACCI;BX=F(n-2) (AX не изменится) ADD BX,AX ;BX=F(n)=F(n-2)+F(n -1) POP AX ;восстановить исходное значение AL RET

FIBONACCI ENDP

Процедура вычисления факториала:

Параметры [BP+4] и [BP+6] представляют собой отыскиваемый факториал и результат. Доступ к кадру организован через регистр BP:

MOV AX,OFFSET RESULT ;вычисляем адрес результата ;структура стекового кадра ;[BP] сохраненное старое значение BP ;[BP+2] сохраненный адрес возврата из процедуры ;[BP+4] место для N ;[BP+6] место для адреса результата

FACT PROC

PUSH BX AX ;запомнить используемые регистры

JZ DONE

CALL FACT

JMP SHORT R

R: MOV [BX],AX;возвращаем новое значение результата

POP AX BX BP

RET 4 ;удаляем ненужные локальные переменные

FACT ENDP

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

Поэтому рано еще отказываться от тех средств, которыми широко пользовались в «досопроцессорные» времена, например от разложения функции в ряд.

Пусть x — любое число, а f(x) — некоторая функция, зависящая от x, непрерывная. и имеющую все производные при x = a, тогда f(x) можно представить в вид суммы степннного ряда, получающегося из формулы Тейлора:

где a — известное число близкое к x; f(a) — известное значение этой функции.

Теперь, для примера, попробуем вычислить кубический корень по приближенному алгоритму разложения в ряд Тэйлора. Пусть x — число, из Вам понадобятся несколько заранее вычисленных кубических корней, например 23=8, 33=27, 43=64, 53=125, 63=216,… Попробуем извлечь кубический корень, например из 100, используя только сложение, деление и умножение.

Ближайшее к 100 число 125 с известным кубическим коренем 5.

сравнить с исходным числом 100. Если данная точность Вас почему-либо не устраивает, тогда придется использовать вместо a число 101,62963, а вместо — 4,6667 и повторить вычисления.

15.7. Модульное конструирование процедур Создание процедур в виде готовых модулей предполагает не «изобретение велосипеда» каждый раз при написании новой программы, а построение новых программ из уже готовых (отлаженных, проверенных и оптимизированных) модулей. Следующие рекомендации должны помочь Вам при оформлении процедур в виде готовых модулей. Если хотите, определите свои рекомендации, но в любом случае, все время придерживайтесь одного и того же. Ваша работа значительно упростится, если Вы будете последовательными.

1. Сохраняйте и восстанавливайте всегда все регистры (их значения), кроме тех случаев, когда процедура передает значение в регистре.

2. Будьте последовательны в применении для передачи информации одних и тех же регистров. Например:

• DL, DX, EDX — для передачи байта, слова или двойного слова.

• AL, AX, EAX — для возвращения байта, слова или двойного слова.

• BX:AX, EBX:EAX — для возвращения двойного или счетверенного • DS:DX, DS:EDX — передача и возвращение адресов • CX, ECX — счетчик повторения и другие счетчики CF — устанавливается при ошибке; код ошибки возвращается в одном из регистров, например в AL, AX или EAX 3. Определяйте все внешние взаимодействия процедуры в заголовке-комментарии:

• информация, необходимая на входе;

• возвращаемая информация (изменяемые регистры) ;

• вызываемые процедуры;

• используемые переменные (считывания, записи и т. д.).

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

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

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

Сохраняйте и восстанавливайте всегда все регистры (их значения), кроме тех случаев, когда процедура передает значение в регистре. У микропроцессора не так много регистров. Сохраняя значения регистров в начале процедуры, Вы освобождаете регистры для использования в самой процедуре. Но при этом необходимо быть весьма осторожным. Делайте это в начале процедуры с помощью команды PUSH, а командой POP восстанавливайте значения регистров в конце процедуры.

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

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

Будьте последовательными в использовании для передачи информации одних и тех же регистров. Работа станет легче, если Вы установите стандарты для обмена информацией между процедурами. Применяйте один регистр для передачи, а другой для получения информации. Вам также необходимо посылать адреса больших массивов информации, и для этого старайтесь использовать регистровую пару DS:DX (DS:EDX), это позволяет расположить данные в любом месте памяти.

Резервируйте регистр CX (ECX) для использования в качестве счетчика повторений. Старайтесь использовать регистр CX (ECX) всегда, когда Вам понадобится счетчик повторений или когда Вы захотите вернуть результат какого-либо подсчета, например числа символов, считанных с клавиатуры.

Наконец, устанавливайте флаг переноса (CF) всякий раз, когда происходит ошибка.

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

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

;Это пример полного заголовка. В этом месте обычно приводится полное ;описание того, что данная процедура делает. Например: Процедура ;записывает сообщение «Sector» в первую строку. DS:DX содержит ;адрес сообщения «Sector»

;calls (вызываемые процедуры): GOTO_XY, WRITE_STRING ;Reads (переменные считываемые из памяти): STATUS_LINE_NO ;Writes (переменные считываемые и записываемые в память): DUMMY Когда бы Вы не захотели впоследствии использовать любую из написанных процедур, Вам достаточно просто взглянуть на заголовок, чтобы узнать, как ее использовать. Не будет нужды вникать в тонкости работы процедуры, чтобы разобраться в том, что именно она делает.

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

Контрольные вопросы и упражнения:

1. Что произойдет при выполнении программы если в процедуре будет отсутствовать команда RET?

2. Какая разница между кодированием в директиве PROC операнда с типом FAR и с типом NEAR?

3. Каким образом программа может начать выполнение процедуры?

4. Для какой цели в поле операнда RET допускается употребление выражения?

5. Напишите набор процедур для выполнения беззнаковых двоичных арифметических операций над двоичными словами. Процедуры находят операнды включенными в стек непосредственно перед вызовом и возвращают результаты в регистрах DX:AX. В набор входят процедуры: а) ADDITION. Суммирует два операнда; б) SUBTRACT. Уменьшает два операнда. Уменьшаемое включается в стек первым; в) MULTIPLY.

Умножает два операнда и возвращает только младшие 32 бита;

г) DIVIDE. Делит два операнда и возвращает только частное. Делимое включается в стек первым.

6. Напишите рекурсивную процедуру для вычисления полинома по правилу Горнера: ((…(anx+an-1)x+an-2)x+…)x+a1)x+a0 коэффициенты находятся в смежных областях памяти и адреса всех параметров a0,…,an передаются через стек.

Одно из важных применений компьютеров связано с обработкой текстов, которое представляет собой манипуляции последовательностями байт, содержащих коды символов — символьными строками. При разработке редакторов текста или программы форматирования текста требуются программы пересылок и сравнения символьных строк, введения строк в другие строки или удаления строк из других строк. Часто необходимо отыскивать в строке заданную подстроку или заменять подстроку другой подстрокой. Редактор текста требуется в любой вычислительной системе широкого назначения, и его возможности, улучшающие операции системы над символьными строками, имеют большое значение. Рассмотрим те возможности микропроцессора i80x86, которые упрощают обработку строк.

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

Строковые команды требуются при переходе от одной системы кодирования к другой (от ASCII к EBCDIC или Unicode), строковые команды так же необходимы в системах шифрования и дешифрации.

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

ASCII — American Standard Code for Information Interchange — американский стандартный код для обмена информацией. Стандартная схема кодирования знаков, веденная в 1963 г. и широко используемая во многих машинах.

EBCDIC — Extended Binary Coded Decimal Interchange Code — расширенный двоично-десятичный код для обмена информацией. Восьмибитный код для кодирования знаков, в основном используемый в вычислительных машинах фирмы IBM.

Unicode — Стандарт Unicode был предложен некоммерческой организацией Unicode Consortium, образованной в 1991 г. Для представления каждого символа в этом стандарте используются два байта, что позволяет закодировать очень большое число символов из разных письменностей: в документах Unicode могут соседствовать русские, латинские, греческие буквы, китайские иероглифы и математические символы.

16.1.1. Команды сравнения строк CMPSB/CMPSW/CMPSD (Сравнение строк состоящих из байтов/слов/двойных слов =”CoMPare Strings Byte/Word/Double word operands”) В строковых командах под строкой понимается не только последовательность байт (символов), но и последовательность слов, а также двойных и четверных слов. Поэтому каждая строковая операция может быть представлена четырьмя командами: одни предназначены для обработки строк из байтов, другие — для обработки строк из слов, третьи — для двойных слов и четвертые — для учетверенных. В языке ассемблера мнемокоды этих команд различаются, тем, что в первом случае указывается буква B (byte), в другом — буква W (word), в третьем — D (double word), в четвертом — Q (quadruple word). Например, в командах сравнения строк (CMPS, compare strings) используются следующие мнемокоды:

Синтаксис команд:

В целом действия этих команд совпадают, поэтому обычно про них говорят как про одну команду CMPS, и только если надо, уточняют, какой вариант ее имелся в виду Семантика команды: сравнение двух последовательностей (цепочек) элементов в памяти.

Алгоритм работы:

• выполнить вычитание элементов ( -), адреса элементов предварительно должны быть загружены:

адрес операнда1 — в пару регистров DS:ESI/SI;

адрес операнда2 — в пару регистров ES:EDI/DI;

• в зависимости от состояния флага DF изменить значение регистров ESI/SI и EDI/DI:

если DF=0, то увеличить содержимое этих регистров на длину элемента последовательности;

если DF=1, то уменьшить содержимое этих регистров на длину элемента последовательности;

• в зависимости от результата вычитания установить флаги:

если очередные элементы цепочек не равны, то CF=1, ZF=0;

если очередные элементы цепочек или цепочки в целом равны, • при наличии префикса выполнить определяемые им действия (см.

команды REPE/REPNE).

Применение:

У команды два операнда, но их местоположение заранее известно, поэтому они явно не указываются. Команда без префиксов осуществляет простое сравнение двух элементов в памяти. Размеры сравниваемых элементов зависят от применяемой команды. Команда CMPS может работать с элементами размером в байт, слово, двойное слово. В качестве операндов в команде указываются идентификаторы последовательностей этих элементов в памяти. Реально эти идентификаторы используются лишь для получения типов элементов последовательностей, а их адреса должны быть предварительно загружены в указанные выше пары регистров. Транслятор, обработав команду CMPS и выяснив тип операндов, генерирует одну из машинных команд CMPSB, CMPSW или CMPSD. Машинного аналога для команды CMPS нет. Абсолютный адрес операнда1 из первой строки по умолчанию должен задаваться регистрами DS:SI/ESI, а абсолютный адрес операнда2 из второй строки должен задаваться регистрами ES:DI/EDI. Причем для операнда2 обязательно должен использоваться регистр ES, а для адресации операнда1 можно делать замену сегмента с использованием соответствующего префикса.



Pages:     | 1 |   ...   | 2 | 3 || 5 |


Похожие работы:

«Министерство образования и наук и, молодежи и спорта Украины Государственное высшее учебное заведение Донецкий национальный технический университет Выпуск посвящен 90–летию ДонНТУ и 40–летию кафедры ТТГР БУРЕНИЕ материалы XI Всеукраинской научно–технической конференции студентов 28–29 апреля 2011 года Донецк – 2011 XI Всеукраинская научно-техническая конференция студентов Бурение УДК 550.8.071(083); 622.24; 621.825.24; 622.248.6; 622.248; 65.015.11; 622.233:551.49; 622.242.243; 622.243;...»

«Список опубликованных работ Е.В. Пчелова Монографии, учебные пособия, справочники 1. Правители России от Юрия Долгорукого до наших дней. М., 1997. 240 с., ил. То же. 4-е изд., испр. и доп. М., 2000. 296 с., ил. (соавт.: В.Т. Чумаков). 2. Два века русской буквы Ё: История и словарь. М., 2000. 248 с., ил. (соавт.: В.Т. Чумаков). 3. Романовы: История династии. М., 2001. 494 с., ил. 4. Генеалогия древнерусских князей IX – начала XI в. / Отв. ред. д.и.н. О.М. Медушевская; РГГУ. М., 2001. 262 с. 5....»

«ВСЕСОЮЗНЫЙ НАУЧНЫЙ ЦЕНТР ПСИХИЧЕСКОГО ЗДОРОВЬЯ АМН СССР НИИ ПРОФИЛАКТИЧЕСКОЙ ПСИХИАТРИИ ГЛОССАРИЙ ПСИХОПАТОЛОГИЧЕСКИХ СИНДРОМОВ и состояний (Методическое пособие для унифицированной клинической оценки психопатологических состояний) Библиотека ЦНИИ СудС 1 ', : / - > : Т р И и V ко Г > Иы. Ov: М ) К/ Москва 1990 Р е д к о л л е г и я : В.С. Ястребов (ответственный редактор), A.К. Ануфриев (научный редактор), М.А.Лнсина (ответственный секретарь). г С о с т а в и т е л и : докт. мед. наук,...»

«БИБЛИОГРАФИЧЕСКИЙ УКАЗАТЕЛЬ КНИГ, ПОСТУПИВШИХ В БИБЛИОТЕКУ АМК в 2011 году БИБЛИОГРАФИЧЕСКИЙ УКАЗАТЕЛЬ КНИГ, ПОСТУПИВШИХ В БИБЛИОТЕКУ АМК (январь-сентябрь 2011г.) Акушерство 1. 618Г Б 75 Бодяжина В.И. Акушерство : Учебное пособие / В. И. Бодяжина, И. Б. Семенченко. - 8-е изд. Ростов-на-Дону : Феникс, 2009. - 477 с. : ил. - (Среднее профессиональное образование) Экземпляры: всего:7 - оф(1), кх(6) Аннотация: Допущено МО РФ в качестве учебного пособия для студентов образовательных учреждений...»

«ГБОУ СПО СК Ставропольский базовый медицинский колледж ЦМК Естественно-научных дисциплин ЦМК Узких специальных дисциплин Методические рекомендации по оформлению реферата Разработано преподавателями: Гребенкиной М. Е. Медушевской О. В. г. Ставрополь 2 Составители: преподаватели Гребенкина М. Б., Медушевская О. В. Данные методические рекомендации разработаны в помощь обучающимся образовательных учреждениях среднего профессионального образования в целях повышения роли технического творчества в...»

«РЕФЕРАТ Объем работы 86с., в том числе 7 рис., 33 табл., 39 исп.ист., 1 приложение. МАРКЕТИНГ, СТРАТЕГИЯ, НАПРАВЛЕНИЕ, РАЗВИТИЕ, СОЗДАНИЕ, ПРОИЗВОДСТВО, РЕКЛАМА, ПЕРСПЕКТИВА. Предметом исследования является маркетинговая стратегия предприятия на рынке. Цель дипломной работы – разработка маркетинговой стратегии для предприятия на основе проведенного анализа маркетинговой деятельности. В ходе выполнения работы был исследован процесс выработки и поддержания уже существующей маркетинговой стратегии...»

«ГБУЗ КО Кемеровская областная научная медицинская библиотека Научная библиотека ГОУ ВПО КемГМА Росздрава ГУК Кемеровская областная научная библиотека им. В.Д. Федорова Медицинская литература (текущий указатель литературы) Вып. 2 Кемерово - 2014 Текущий указатель новых поступлений Медицинская литература издается Кемеровской областной научной медицинской библиотекой совместно с научной библиотекой КемГМА, Кемеровской областной научной библиотекой им. В.Д. Федорова. Библиографический указатель...»

«ПРОГРАММА по ГЕОГРАФИИ Общие указания На экзамене по географии поступающий в высшее учебное заведение должен показать глубокие знания данного предмета, свободно ориентироваться по картам физическим, социально-экономическим и политико-административным; уметь дать характеристику элементов природной среды (рельефа, климата, вод, почв, растительности, животного мира) и показать взаимосвязи, существующие между ними; уметь дать оценку природным условиям и их влиянию на хозяйственную деятельность...»

«К.А ПАШКОВ, А.В. БЕЛОЛАПОТКОВА, Г.Н. ТРОЯНСКИЙ, УЧЕБНО-МЕТОДИЧЕСКОЕ ПОСОБИЕ К СЕМИНАРСКИМ ЗАНЯТИЯМ ПО ИСТОРИИ МЕДИЦИНЫ для студентов стоматологического факультета К.А ПАШКОВ, А.В. БЕЛОЛАПОТКОВА, Г.Н. ТРОЯНСКИЙ УЧЕБНО-МЕТОДИЧЕСКОЕ ПОСОБИЕ К СЕМИНАРСКИМ ЗАНЯТИЯМ ПО ИСТОРИИ МЕДИЦИНЫ для студентов стоматологического факультета Рекомендуется Учебно-методическим объединением по медицинскому и фармацевтическому образованию вузов России в качестве учебного пособия для студентов стоматологического...»

«Т.К. Миронова Право социального обесПечения Учебное пособие КНОРУС • МОСКВА • 2013 УДК 349.3(075.8) ББК 67.405я73 М64 Миронова Т.К. М64 Право социального обеспечения : учебное пособие / Т.К. Миронова. — М. : КНОРУС, 2013. — 312 с. ISBN 978-5-406-02868-1 Кратко отражены вопросы Общей части отрасли. Основное внимание уделе­ но институтам Особенной части — базовым положениям, которые определяют ключевые параметры отечественной системы социального обеспечения и глав­ ные подходы к регламентации...»

«УДК 30 ББК 74.26 К 30 РЕЦЕНЗЕНТЫ: Дергунова Нина Владимировна, доктор политических наук, заведующая кафедрой социологии и политологии УлГУ; Петухов Валерий Борисович, доктор исторических наук, доцент кафедры истории и культуры УлГТУ. НАУЧНЫЙ РЕДАКТОР Зарубина Валентина Викторовна, кандидат педагогических наук, про ректор по УМР УИПКПРО. Издание подготовлено при содействии Ульяновского института повыше ния квалификации и переподготовки работников образования. Качкина Т.Б., Качкин А.В. К 30...»

«Л. А. МИКЕШИНА ФИЛОСОФИЯ НАУКИ: ЭПИСТЕМОЛОГИЯ. МЕТОДОЛОГИЯ. КУЛЬТУРА Учебное пособие Издание 2-е, исправленное и дополненное Москва Издательский дом Международного университета в Москве 2006 St. Petersburg Center for the History of Ideas http://ideashistory.org.ru Аннотация Предметом учебного пособия являются научное познание, его реальные проблемы, принципы и методы научной деятельности, структура знания. Главные разделы пособия посвящены структуре и моделям развития науки в динамике культуры,...»

«Федеральное агентство по образованию Российской Федерации ГОУ ВПО Горно-Алтайский государственный университет А.П. Макошев ЭКОНОМИЧЕСКАЯ, СОЦИАЛЬНАЯ И ПОЛИТИЧЕСКАЯ ГЕОГРАФИЯ МИРА Учебно-методическое пособие Горно-Алтайск, РИО ГАГУ, 2007 Печатается по решению редакционно-издательского совета ГорноАлтайского государственного университета А.П. Макошев ЭКОНОМИЧЕСКАЯ, СОЦИАЛЬНАЯ И ПОЛИТИЧЕСКАЯ ГЕОГРАФИЯ МИРА: учебно-методическое пособие. – Горно-Алтайск: РИО ГАГУ, 2007. с. Это третье издание,...»

«ФЕДЕРАЛЬНОЕ АГЕНСТВО ПО ОБРАЗОВАНИЮ Уральский государственный лесотехнический университет Кафедра прикладной физики и биофизики В.И. Крюк С.В. Нескоромный Ю.В. Шалаумова И.О. Заплатина А.С. Попов Концепции современного естествознания Методические указания и контрольные задания для студентов заочного факультета специальностей 080000 – Экономика и управление, 100103 – Социально-культурный сервис и туризм, 220501 – Управление качеством по дисциплине – КОНЦЕПЦИИ СОВРЕМЕННОГО ЕСТЕСТВОЗНАНИЯ...»

«Федеральное агентство по образованию РОССИЙСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ НЕФТИ И ГАЗА имени И.М. Губкина _ Кафедра бурения нефтяных и газовых скважин В.И. БАЛАБА, И.А. ВЕДИЩЕВ ПРАКТИЧЕСКАЯ ПОДГОТОВКА СТУДЕНТОВ-БУРОВИКОВ Допущено Учебно-методическим объединением вузов Российской Федерации по нефтегазовому образованию в качестве учебного пособия для подготовки бакалавров по направлению 130500 Нефтегазовое дело и дипломированных специалистов по специальности 130504 Бурение нефтяных и газовых...»

«Департамент образования Тверской области Тверской государственный университет Тверской областной институт усовершенствования учителей Региональный центр обработки информации Методические рекомендации для экспертов Единого государственного экзамена по русскому языку ТВЕРЬ 2009 Составители: Сергеева Нина Михайловна – кандидат филологических наук, доцент кафедры русского языка Тверского государственного университета; Соловьева Татьяна Николаевна – кандидат педагогических наук, начальник отдела...»

«1 Примерная основная образовательная программа среднего профессионального образования по специальности 073002 Теория музыки Москва 2011 2 3 Материал настоящего издания подготовлен: Н.В.Солдатиковой, заместителем директора ФГОУ СПО Академический музыкальный колледж при Московской государственной консерватории им. П.И.Чайковского; Т.Е.Форшток, заместителем директора ГОУ СПО Нижегородский музыкальный колледж имени М.А.Балакирева; Л.А.Красноокой, заместителем директора ГОУ СПО Вологодский областной...»

«Санкт-Петербургский государственный университет В.Г.Горбацкий Лекции по истории астрономии Учебное пособие Издательство Санкт-Петербургского университета 2002 УДК ВВК Г 67 Р е ц е н з е н т ы : член-корреспондент РАН В.К. Абалакин (ГАО РАН) профессор В.В. Иванов (С.-Петерб. гос. ун-т) Печатается по постановлению Редакционно-издательского совета С.-Петербургского государственного университета УДК Го р б а ц к и й В. Г. Лекции по истории астрономии: Учеб. пособие. Г 67 СПб Изд. С.-Петерб. ун-та,...»

«Федеральное агентство по образованию Федеральное государственное образовательное учреждение высшего профессионального образования Чувашский государственный университет имени И.Н. Ульянова Батыревский филиал Гуманитарно-экономический факультет Кафедра экономических дисциплин Методические указания по выполнению контрольной работы по дисциплине: Комплексный экономический анализ для студентов заочной формы обучения Специальность 080109 Бухгалтерский учет, анализ и аудит Составитель: доцент Баданов...»

«С. Дикман, С. Дьячкова, В. Луховицкий, О. Погонина, Е. Русакова Разум против предрассудков: преодоление нетерпимости Элективный курс Методическое пособие для учителя 1 Авторский коллектив: С. Дикман (Что такое расизм?, Радикальные националистические организации в России) С. Дьячкова (Вводный раздел, Раздел 4, Работа над самостоятельными исследовательскими проектами, Маленькие игры и игровые разминки) Н. Клейменова и Л. Коровина (Антицыганские мифы) В. Луховицкий (Раздел 1, гл. 4, Раздел 2,...»






 
2014 www.av.disus.ru - «Бесплатная электронная библиотека - Авторефераты, Диссертации, Монографии, Программы»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.