Команды пересылки ассемблер - IT Справочник
Llscompany.ru

IT Справочник
5 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Команды пересылки ассемблер

Команды пересылки ассемблер

Справочная система по языку Assembler

Команды пересылки данных


Команды пересылки данных общего назначения

Но есть и другой, более красивый способ выполнения данной операции — использование стека и команд push и pop:

Команды ввода-вывода в порт

Как видно из рис. 1, самым нижним уровнем является уровень BIOS, на котором работа с оборудованием ведется напрямую через порты. Тем самым реализуется концепция независимости от оборудования. При замене оборудования необходимо будет лишь подправить соответствующие функции BIOS, переориентировав их на новые адреса и логику работы портов.

Принципиально управлять устройствами напрямую через порты несложно. Сведения о номерах портов, их разрядности, формате управляющей информации приводятся в техническом описании устройства. Необходимо знать лишь конечную цель своих действий, алгоритм, в соответствии с которым работает конкретное устройство, и порядок программирования его портов. То есть, фактически, нужно знать, что и в какой последовательности нужно послать в порт (при записи в него) или считать из него (при чтении) и как следует трактовать эту информацию. Для этого достаточно всего двух команд, присутствующих в системе команд микропроцессора:
in аккумулятор,номер_порта — ввод в аккумулятор из порта с номером номер_порта;
out порт,аккумулятор — вывод содержимого аккумулятора в порт с номером номер_порта.

Команды работы с адресами и указателями памяти

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

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

Например, при организации работы с цепочкой символов удобно поместить ее начальный адрес в некоторый регистр и далее в цикле модифицировать это значение для последовательного доступа к элементам цепочки. В листинге 1 производится копирование строки байт str_1 в строку байт str_2.
В строках 12 и 13 в регистры si и di загружаются значения эффективных адресов переменных str_1 и str_2.
В строках 16 и 17 производится пересылка очередного байта из одной строки в другую. Указатели на позиции байтов в строках определяются содержимым регистров si и di. Для пересылки очередного байта необходимо увеличить на единицу регистры si и di, что и делается командами сложения inc (строки 18, 19). После этого программу необходимо зациклить до обработки всех символов строки.

Команды преобразования данных

Это очень интересная и полезная команда. Ее действие заключается в том, что она замещает значение в регистре al другим байтом из таблицы в памяти, расположенной по адресу, указанному операндом адрес_таблицы_перекодировки.
Слово “таблица” весьма условно — по сути это просто строка байт. Адрес байта в строке, которым будет производиться замещение содержимого регистра al, определяется суммой (bx) + (al), то есть содержимое al выполняет роль индекса в байтовом массиве.

При работе с командой xlat обратите внимание на следующий тонкий момент. Несмотря на то, что в команде указывается адрес строки байт, из которой должно быть извлечено новое значение, этот адрес должен быть предварительно загружен (например, с помощью команды lea) в регистр bx. Таким образом, операнд адрес_таблицы_перекодировки на самом деле не нужен (необязательность операнда показана заключением его в квадратные скобки). Что касается строки байт (таблицы перекодировки), то она представляет собой область памяти размером от 1 до 255 байт (диапазон числа без знака в 8-битном регистре).

В качестве иллюстрации работы данной команды мы рассмотрим программу, которая преобразует двузначное шестнадцатеричное число, вводимое с клавиатуры (то есть в символьном виде), в эквивалентное двоичное представление в регистре al. Ниже (листинг 2) приведен вариант этой программы с использованием команды xlat.

Команды работы со стеком

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

Для работы со стеком предназначены три регистра:

  • ss — сегментный регистр стека;
  • sp/esp — регистр указателя стека;
  • bp/ebp — регистр указателя базы кадра стека.

Размер стека зависит от режима работы микропроцессора и ограничивается 64 Кбайт (или 4 Гбайт в защищенном режиме).
В каждый момент времени доступен только один стек, адрес сегмента которого содержится в регистре ss. Этот стек называется текущим. Для того чтобы обратиться к другому стеку (“переключить стек”), необходимо загрузить в регистр ss другой адрес. Регистр ss автоматически используется процессором для выполнения всех команд, работающих со стеком.

Перечислим еще некоторые особенности работы со стеком:

  • запись и чтение данных в стеке осуществляется в соответствии с принципом LIFO (Last In First Out — “последним пришел, первым ушел”);
  • по мере записи данных в стек последний растет в сторону младших адресов. Эта особенность заложена в алгоритм команд работы со стеком;
  • при использовании регистров esp/sp и ebp/bp для адресации памяти ассемблер автоматически считает, что содержащиеся в нем значения представляют собой смещения относительно сегментного регистра ss.

В общем случае стек организован так, как показано на рис. 2. Рис. 2. Концептуальная схема организации стека

Для работы со стеком предназначены регистры ss, esp/sp и ebp/bp.
Эти регистры используются комплексно, и каждый из них имеет свое функциональное назначение.
Регистр esp/sp всегда указывает на вершину стека, то есть содержит смещение, по которому в стек был занесен последний элемент. Команды работы со стеком неявно изменяют этот регистр так, чтобы он указывал всегда на последний записанный в стек элемент. Если стек пуст, то значение esp равно адресу последнего байта сегмента, выделенного под стек.
При занесении элемента в стек процессор уменьшает значение регистра esp, а затем записывает элемент по адресу новой вершины.
При извлечении данных из стека процессор копирует элемент, расположенный по адресу вершины, а затем увеличивает значение регистра указателя стека esp.
Таким образом, получается, что стек растет вниз, в сторону уменьшения адресов.

Что делать, если нам необходимо получить доступ к элементам не на вершине, а внутри стека?
Для этого применяют регистр ebp. Регистр ebpрегистр указателя базы кадра стека.
Например, типичным приемом при входе в подпрограмму является передача нужных параметров путем записи их в стек. Если подпрограмма тоже активно работает со стеком, то доступ к этим параметрам становится проблематичным. Выход в том, чтобы после записи нужных данных в стек сохранить адрес вершины стека в указателе кадра (базы) стека — регистре ebp. Значение в ebp в дальнейшем можно использовать для доступа к переданным параметрам.

Начало стека расположено в старших адресах памяти. На рис. 2 этот адрес обозначен парой ss:ffff. Смещение ffff приведено здесь условно. Реально это значение определяется величиной, которую программист задает при описании сегмента стека в своей программе.
К примеру, для программы в листинге 2 началу стека будет соответствовать пара ss:0100h. Адресная пара ss:ffff — это максимальное для реального режима значение адреса начала стека, так как размер сегмента в нем ограничен величиной 64 Кбайт (0ffffh).

Для организации работы со стеком существуют специальные команды записи и чтения.
pushисточник — запись значения источник в вершину стека.
Интерес представляет алгоритм работы этой команды, который включает следующие действия (рис. 3):

  • (sp) = (sp) – 2; значение sp уменьшается на 2;
  • значение из источника записывается по адресу, указываемому парой ss:sp.

Рис. 3. Принцип работы команды push

popназначение — запись значения из вершины стека по месту, указанному операндом назначение. Значение при этом “снимается” с вершины стека.
Алгоритм работы команды pop обратен алгоритму команды push (рис. 4):

  • запись содержимого вершины стека по месту, указанному операндом назначение;
  • (sp) = (sp) + 2; увеличение значения sp.

Рис. 4. Принцип работы команды pop

pusha — команда групповой записи в стек.
По этой команде в стек последовательно записываются регистры ax, cx, dx, bx, sp, bp, si, di. Заметим, что записывается оригинальное содержимое sp, то есть то, которое было до выдачи команды pusha (рис. 5). Рис. 5. Принцип работы команды pusha

pushaw — почти синоним команды pusha. В чем разница? На уроке 5 мы обсуждали один из атрибутов сегмента — атрибут разрядности. Он может принимать значение use16 или use32.
Рассмотрим работу команд pusha и pushaw при каждом из этих атрибутов:

  • use16 — алгоритм работы pushaw аналогичен алгоритму pusha.
  • use32 — pushaw не изменяется (то есть она нечувствительна к разрядности сегмента и всегда работает с регистрами размером в слово — ax, cx, dx, bx, sp, bp, si, di). Команда pusha чувствительна к установленной разрядности сегмента и при указании 32-разрядного сегмента работает с соответствующими 32-разрядными регистрами, то есть eax, ecx, edx, ebx, esp, ebp, esi, edi.

pushad — выполняется аналогично команде pusha, но есть некоторые особенности, которые вы можете узнать из “Справочника команд”.

Следующие три команды выполняют действия, обратные вышеописанным командам:
popa;
popaw;
popad.

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

  • use16 — в стек записывается регистр flags размером 2 байта;
  • use32 — в стек записывается регистр eflags размером 4 байта.

pushfw — сохранение в стеке регистра флагов размером в слово. Всегда работает как pushf с атрибутом use16.
pushfd — сохранение в стеке регистра флагов flags или eflags в зависимости от атрибута разрядности сегмента (то есть то же, что и pushf).

Аналогично, следующие три команды выполняют действия, обратные рассмотренным выше операциям:
popf
popfw
popfd

И в заключение отметим основные виды операции, когда использование стека практически неизбежно:

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

[an error occurred while processing this directive]

Команды пересылки данных

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

•пересылки данных общего назначения

•ввода-вывода в порт

•работы с адресами и указателями

•работы со стеком

Команды пересылки данных общего назначения

К этой группе относятся следующие команды:

mov — это основная команда пересылки данных . Она реализует самые разнообразные варианты пересылки.

Отметим особенности применения этой команды:

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

К примеру, рассмотрим фрагмент программы для пересылки байта из ячейки fls в ячейку fld:

. нельзя загрузить в сегментный регистр значение непосредственно из памяти. Поэтому для выполнения такой загрузки нужно использовать промежуточный объект. Это может быть регистр общего назначения или стек. В начале сегмента кода две команды mov, выполняющие настройку сегментного регистра ds. При этом из-за невозможности загрузить впрямую в сегментный регистр значение адреса сегмента, содержащееся в предопределенной переменной @data, приходится использовать регистр общего назначения ax;

. нельзя переслать содержимое одного сегментного регистра в другой сегментный регистр. Это объясняется тем, что в системе команд нет соответствующего кода операции. Но необходимость в таком действии часто возникает. Выполнить такую пересылку можно, используя в качестве промежуточных все те же регистры общего назначения. Вот пример инициализации регистра es значением из регистра ds:

Но есть и другой, более красивый способ выполнения данной операции — использование стека и команд push и pop:

push ds ;поместить значение регистра ds в стек

pop es ;записать в es число из стека

. нельзя использовать сегментный регистр cs в качестве операнда назначения. Причина здесь простая. Дело в том, что в архитектуре микропроцессора пара cs:ip всегда содержит адрес команды, которая должна выполняться следующей. Изменение командой mov содержимого регистра cs фактически означало бы операцию перехода, а не пересылки, что недопустимо.

Схема команды: mov приемник,источник

Назначение: пересылка данных между регистрами или регистрами и памятью.

Алгоритм работы: копирование второго операнда в первый операнд.

Состояние флагов после выполнения команды: !! выполнение команды не влияет на флаги

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

•направление пересылки в команде mov всегда справа налево, то есть из второго операнда в первый;

•значение второго операнда не изменяется;

•оба операнда не могут быть из памяти (при необходимости можно использовать цепочечную команду movs );

•лишь один из операндов может быть сегментным регистром;

•желательно использовать в качестве одного из операндов регистр al / ax / eax , так как в этом случае TASM генерирует более быструю форму команды mov .

MOV (MOVe operand to/from system registers)

Пересылка операнда в системные регистры (или из них)

Схема команды: mov приемник,источник

Назначение: пересылка данных между регистрами или регистрами и памятью.

Алгоритм работы: копирование второго операнда в первый.

Состояние флагов после выполнения команды:

11 07 06 04 02 00

OF SF ZF AF PF CF

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

;переключение микропроцессора в защищенный

XCHG (eXCHanGe) Обмен

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

Схема команды: xchg операнд_1,операнд_2

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

Алгоритм работы: обмен содержимого операнд_1 и операнд_2.

Состояние флагов после выполнения команды: выполнение команды не влияет на флаги

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

!! операнды должны иметь один тип.

!! Не допускается (как и для всех команд ассемблера) обменивать между собой содержимое двух ячеек памяти.

xchg ax,bx ;обменять содержимое регистров ax и bx

xchg ax,word ptr [si] ;обменять содержимое регистра ax

Команды пересылки данных;

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

Команда пересылки MOV — основная команда пересылки данных, которая позволяет переслать содержимое источника (операнд 2) в приемник (операнд 1). Содержимое приемника безвозвратно теряется, содержимое источника не изменится

Формат команды MOV ,

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

В качестве источника можно также использовать один из регистров общего назначения (8-ми или 16-ти разрядные), переменную в памяти (байт или слово), а также константу размерностью в байт или слово (понятно, что константу нельзя использовать в качестве приемника).

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

— нельзя пересылать данные непосредственно между двумя адресами памяти;

— следить за тем, чтобы размерности пересылаемых операндов соответствовали друг другу, иначе может возникнуть ошибка;

— нельзя пересылать данные из одного сегментного регистра в другой;

— при помощи команды MOV нельзя загрузить сегментный регистр CS.

При выполнении команды MOV никакие флаги — не меняются.

Команда замены XCHG

Команда XCHG просто меняет местами между собой содержимое двух операндов (например, двух регистров, или регистра и ячейки памяти).

Формат команды XCHG ,

Команда XCHG заменяет три команды пересылки и не требует промежуточной ячейки памяти.

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

При выполнении команды XCHG никакие флаги не меняются

С помощью команды LEA можно вычислить и загрузить в один из регистров исполнительный адрес (смещение) операнда в памяти

Формат команды LEA , , где — один из регистров общего назначения, значение которого безвозвратно теряется. Второй операнд является некоторым адресом или адресным выражением. В качестве второго операнда можно использовать любой вид адресации.

При выполнении команды LEA никакие флаги микропроцессора, описывающие его состояние, не меняются.

Отличие от команды MOV в том, что по команде MOV загружается значение некоторого участка памяти, а при команде LEA — адрес начала этого участка.

Оператор указания типа — PTR

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

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

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

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

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

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

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

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

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

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

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

— это BYTE, WORD, а — может быть константой или адресом, например,

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

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

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

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

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

MOV AH,BYTE PTR X

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

К командам пересылки относятся также команды стека PUSH и POP

Стеком называют область памяти для временного хранения произвольных данных. Удобство стека заключается в том, что его область используется многократно. Размер стека не должен превышать 64 кбайт и начальный адрес должен быть кратен 16. Другими словами, эта область должна быть сег­ментом памяти; он называется сегментом стека, где начальный адрес хранится в регистре SS

В ПК принято стек заполнять снизу вверх: первый элемент записывается в стек в ячейку области с наибольшим адресом (дно стека), следующий элемент записывается над ним и т.д. При считывании из стека первым удаляется самый верхний элемент (признак LIFO — “последним пришёл — первым ушёл”).

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

Следовательно, в SP — находится смещение, т.е. адрес, отсчитанный от начала сегмента стека. Абсолютный адрес вершины стека задается парой регистров SS:SP

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

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

Основными стековыми командами являются команды записи слова в стек и считывания слова из стека.

Формат команды: PUSH

Команда PUSH записывает в стек свой операнд, которым может быть слово. Флаги команда не меняет.

Чтение из стека слова: POP

Команда POP считывает слово из вершины стека

Следующая пара стековых команд используется для записи в стек и чтения из стека регистра флагов:

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

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

При работе со стеком необходимо помнить: если мы что-то записали в стек, то обязаны всё это считать из стека (сколько было команд PUSH, столько должно быть команд POP).

wiki.vspu.ru

портал образовательных ресурсов

Содержание

Введение в Ассемблер. Работа с регистрами. Адресация и команды пересылки данных. Арифметические операции с целыми числами

Цели:

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

Адресация и выделение памяти

Для процессора вся память представляет собой последовательность однобайтовых ячеек, каждая из которых имеет свой адрес. Для того, чтобы оперировать большими числами, пары ячеек объединяют в слова, пары слов – в двойные слова, пары двойных слов – в учетверенные слова. Чаще всего в программах оперируют байтами, словами и двойными словами (в соответствии с одно-, двух- и четырехбайтовыми регистрами процессоров). Адресом слова и двойного слова является адрес их младшего байта.

Здесь используется доступ к переменной типа BYTE по указателю – структура BYTE PTR [EAX]. Немного позже мы увидим, как этот прием используется при написании программ.

Задания.

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

Доступ к переменной по указателю используется и в языках высокого уровня (очень часто – при создании динамических массивов).

Указатель – это переменная, которая содержит адрес другой переменной (говорят, что указатель указывает на переменную того типа, адрес которой он содержит). Существует одноместная (унарная, т.е. для одного операнда) операция взятия адреса переменной & (амперсанд, как в названии мультфильма Tom&Jerry). Если имеем объявление int a, то можно определить адрес этой переменной: &a. Если Pa – указатель, который будет указывать на переменную типа int, то можно записать: Pa=&a. Существует унарная операция * (она называется операцией разыменования), которая действует на переменную, содержащую адрес объекта, т.е. на указатель. При этом извлекается содержимое переменной, адрес которой находится в указателе. Если Pa=&a, то, воздействуя на обе части операцией * получим (по определению этой операции): *Pa=a. Исходя из этого, указатель объявляется так:

Это и есть правило объявления указателя: указатель на переменную какого-то типа – это такая переменная, при воздействии на которую операцией разыменования получаем значение переменной того же типа. На листинге 3 приведен пример использования указателя в языке Си.

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

Один из наиболее часто встречающихся случаев – использование указателей для динамического выделения памяти при создании массивов (листинг 5).

Задание. Выведите на экран адреса элементов массива, созданного в программе, показанной на листинге 5. Попробуйте создать динамический массив типа double, заполнить его, вывести на печать элементы массива и их адреса.

Арифметические операции над целыми числами

Сложение и вычитание целых чисел

Рассмотрим 3 основные команды сложения. Команда INC осуществляет инкремент, т.е. увеличение содержимого операнда на 1, например, INC EAX. Команда INC устанавливает флаги OF, SF, ZF, AF, PF в зависимости от результатов сложения. Команда ADD осуществляет сложение двух операндов. Результат пишется в первый операнд (приемник). Первый операнд может быть регистром или переменной. Второй операнд – регистром, переменной или числом. Невозможно, однако, осуществлять операцию сложения одновременно над двумя переменными. Команда действует на флаги CF, OF, SF, ZF, AF, PF. Её можно использовать для знаковых и для беззнаковых чисел. Команда ADC осуществляет сложение двух операндов подобно команде ADD и флага (бита) переноса. С её помощью можно осуществлять сложение чисел, размер которых превышает 32 бита или изначально длина операндов превышает 32 бита.

Умножение целых чисел

В отличие от сложения и вычитания умножение чувствительно к знаку числа, поэтому существует две команды умножения: MUL – для умножения беззнаковых чисел, IMUL – для умножения чисел со знаком. Единственным оператором команды MUL может быть регистр или переменная. Здесь важен размер этого операнда (источника).

Команда IMUL имеет 3 различных формата. Первый формат аналогичен команде MUL. Остановимся на двух других форматах.

operand1 должен быть регистр, operand2 может быть числом, регистром или переменной. В результате выполнения умножения (operand1 умножается на operand2, и результат помещается в operand1) может получиться число, не помещающееся в приемнике. В этом случае флаги CF и AF будут равны 1 (0 в противном случае).

В данном случае operand2 (регистр или переменная) умножается на operand3 (число) и результат заносится в operand1 (регистр). Если при умножении возникнет переполнение, т.е. результат не поместится в приемник, то будут установлены флаги CF и OF. Применение команд умножения приведено на листинге 8.

Листинг 8. Применение команд умножения

Деление целых чисел

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

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

Команды пересылки данных

Табл 2. Команды пересылки данных:

Команда

Описание

Действие

Циклы

Код операции

Флаги

ATtiny

ATmega

Move Between Registers

0010 11rd dddd rrrr

Copy Register Word

0000 0001 dddd rrrr

1110 KKKK dddd KKKK

1001 000d dddd 1100

Load Indirect and
Post-Inc.

1001 000d dddd 1101

Load Indirect and
Pre-Dec.

1001 000d dddd 1110

1000 000d dddd 1000

Load Indirect and
Post-Inc.

1001 000d dddd 1001

Load Indirect and
Pre-Dec.

1001 000d dddd 1010

Load Indirect with Displacement

10q0 qq0d dddd 1qqq

1000 000d dddd 0000

Load Indirect and
Post-Inc.

1001 000d dddd 0001

Load Indirect and
Pre-Dec.

1001 000d dddd 0010

Load Indirect with Displacement

10q0 qq0d dddd 0qqq

Load Direct from SRAM

1001 000d dddd 0000
kkkk kkkk kkkk kkkk

1001 001r rrrr 1100

Store Indirect and
Post-Inc.

1001 001r rrrr 1101

Store Indirect and
Pre-Dec.

1001 001r rrrr 1110

1000 001r rrrr 1000

Store Indirect and
Post-Inc.

1001 001r rrrr 1001

Store Indirect and
Pre-Dec.

1001 001r rrrr 1010

Store Indirect with Displacement

10q0 qq1r rrrr 1qqq

1000 001r rrrr 0000

Store Indirect and
Post-Inc.

1001 001r rrrr 0001

Store Indirect and
Pre-Dec.

1001 001r rrrr 0010

Store Indirect with Displacement

10q0 qq1r rrrr 0qqq

Store Direct to SRAM

1001 001r rrrr 0000
kkkk kkkk kkkk kkkk

Load Program Memory

1001 0101 1100 1000

Load Program Memory

1001 000d dddd 0100

Load Program Memory
and Post-Inc.

1001 000d dddd 0101

Extended Load
Program Memory

1001 0101 1101 1000

Extended Load
Program Memory

1001 000d dddd 0110

Extended Load Program Memory and Post-Inc.

1001 000d dddd 0111

Store Program Memory

1001 0101 1110 1000

1011 0PPd dddd PPPP

1011 1PPr rrrr PPPP

Push Register in Stack

1001 001r rrrr 1111

Pop Register from Stack

1001 000d dddd 1111

Группа команд пересылки данных сведена в табл.2. Под определением пересылки (перемещения) в данном случае понимается копирование содержимого источника без его изменения в приёмник. При этом ни одна из инструкций пересылок не влияет ни на какие флаги регистра состояния SREG. Множество возможностей адресации позволяют с одинаковым успехом обрабатывать как отдельные байты так и массивы данных. При этом часто существует несколько путей для реализации той или иной операции.

В пределах РОН пересылка производится с помощью команды mov Rd,Rr (Пересылка между регистрами). Кроме этого, имеется возможность перемещения двухбайтовых чисел (регистровых пар R1:R0, R3:R2,…, R29:R28, R31:R30) с помощью команды movw Rd,Rr (Пересылка между регистровыми парами), где на месте операндов Rd и Rr должны стоять младшие регистры в обозначениях регистровых парах приёмника и источника соответственно (например, movw R0, R30 для пересылки R1:R0 ← R31:R30). Обе эти команды выполняются за один машинный цикл:

Для загрузки константы в регистр служит команда с непосредственной адресацией ldi Rd,K (Загрузка константы в регистр). Она работает только со старшими РОНами (R16…R31).

Команды для работы с SRAM микроконтроллера используют, в основном, косвенную адресацию. Для этих целей применяются три 16-разрядных индексных регистра X,Y,Z, которые находятся в адресном пространстве РОН и по совместительству также являются регистровыми парами R27:R26, R29:R28, R31:R30.

Копировать байт из памяти ОЗУ в РОН можно любой из команд: ld Rd,X, ld Rd,Y, ld Rd,Z (Косвенное чтение из памяти данных). В этом случае в регистре приёмнике окажется содержимое ячейки памяти данных, адрес которой находится в одном из индексных регистров. Кроме этого, существует ещё две вариации данного действия. В одной из них после копирования производится инкрементирование регистров X,Y,Z, на что указывает знак “+” в командах: ld Rd,X+, ld Rd,Y+, ld Rd,Z+ (Косвенное чтение из памяти данных с постинкрементом). Во втором случае, перед пересылкой, содержимое индексного регистра сначала уменьшается на 1 (знак “-” в команде): ld Rd,-X, ld Rd,-Y, ld Rd,-Z (Косвенное чтение из памяти данных с преддекрементом).

Команды косвенного чтения с постинкрементом/преддекрементом очень эффективны при работе с массивами однотипных данных. Для обращения же к элементам структуры (набору данных разного типа) удобно использовать ld Rd,Y+q, ld Rd,Z+q (Косвенное относительное чтение из памяти данных), в которых в качестве указателей используется Y,Z со смещением q (адрес ячейки памяти определяет сумма (Y)+q или (Z)+q). Смещение q в командах -фиксированная величина, лежащая в пределах 0…63.

Пересылка данных из РОН в ОЗУ посредством косвенной адресации реализуется с помощью команд: st X,Rr, st Y,Rr, st Z,Rr (Косвенная запись в память данных). Содержимое регистра в них копируется в ячейку памяти, адрес которой определяется указателями X,Y и Z соответственно. Точно также существуют команды загрузки с постинкрементом st X+,Rr, st Y+,Rr, st Z+,Rr (Косвенная запись в память данных с постинкрементом) и с преддекрементом индексного регистра st -X,Rr, st -Y,Rr, st -Z, Rr (Косвенная запись в память данных с преддекрементом). Ещё две команды этой группы используют косвенную адресацию со смещением: st Y+q,Rr, st Z+q,Rr (Косвенная относительная запись в память данных). Здесь смещение q может находится в пределах 0…63.

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

Копирование данных из ОЗУ в РОН и обратно может быть произведено и с использованием прямой адресации по командам lds Rd,k (Прямое чтение из памяти данных) и sts k,Rr (Прямая запись в память данных) соответственно. В этом случае двухбайтовый адрес k ячейки памяти находится в коде операции, а сами команды занимают 2 слова (4 байта) памяти программ.

Все вышеуказанные команды пересылок работают в едином адресном пространстве где под РОН отведены адреса 0x00…0x1F, под РВВ от 0x20…0x5F, остальное под ячейки ОЗУ. Поэтому, например, копирование R16←R17 может быть произведено разными путями: mov R16,R17, lds R16,0x0011, ld R16,X (в регистре X адрес 0x0011) и т.д. Первый способ здесь, конечно, более предпочтительный (использует пересылку типа регистр-регистр и выполняется быстрее), но остальные команды позволяют получить доступ к произвольному элементу любой области памяти и поэтому очень универсальны.

Для обращения к РВВ служат команды: in Rd,P (Ввод из порта), out P,Rr (Вывод в порт). Первая считывает значение РВВ в один из РОНов, а вторая производит пересылку РОН в РВВ.

Поскольку напрямую модифицировать РВВ невозможно, приходится сначала копировать их содержимое в рабочие РОН, производить необходимые изменения, а потом заносить обратно:

Тоже самое относится и к данным, расположенным в ОЗУ:

В группе команд пересылки данных имеются две, специально разработанные для работы со стеком. Это push Rr (Сохранение в стеке) и pop Rd (Извлечение из стека). Не смотря на то, что основное назначение стека это сохранение адресов возврата при вызове подпрограмм, эти инструкции позволяют использовать его и для оперативного сохранения данных, находящихся в РОН. Командой push Rr содержимое регистра копируется в ячейку памяти (вершину стека), адрес которой содержится в указателе стека SP, после чего значение SP уменьшается на 1. По команде pop Rd указатель стека возвращается на предыдущий элемент (SP+1), а значение, байта записанное по этому адресу копируется в регистр.

Таким образом реализуется память магазинного типа, где вообще не надо заботиться о задании адресов сохраняемых и восстанавливаемых из в памяти ОЗУ данных. Единственное, про что всегда необходимо помнить, — это порядок доступа к элементам стека Last In First Out (Последний Вошел Первый Вышел). Очередность, в которой регистры восстанавливаются из стека, должна быть обратной по отношению к очередности сохранения регистров:

Имеется и другая возможность программно реализовать стек данных. Команды ld Rd,X+, ld Rd,Y+, ld Rd,Z+ по сути являются иной реализацией действия push Rr, а st -X,Rd, st -Y,Rd, st -Z, Rd подобны pop Rd. Разница состоит лишь в том, что в качестве индексного регистра выступает не SP, а один из регистров–указателей (X,Y,Z) и вершина стека перемещается в сторону увеличения адресов ОЗУ:

Учитывая то, что SP расположен в пространстве РВВ, а X,Y,Z в РОН, использование эмуляции стека может быть даже более предпочтительной. Регистры X,Y,Z могут быть быстрей модифицированы и кроме того отсутствует опасность повредить их содержимое при вызове подпрограмм и возникновении прерываний.

Подобно большинству микроконтроллеров, у AVR имеется возможность хранения таблиц констант в памяти программ. Для их чтения разработаны команды lpm (Загрузка памяти программ), lpm Rd,Z (Загрузка памяти программ), lpm Rd,Z+ (Загрузка памяти программ с постинкрементом). Все они переписывают содержимое байта из FLASH памяти программ (адрес байта находится в Z) в один из РОН. С помощью первой инструкции, не имеющей параметров, копирование производится в R0. Вторая и третья модификации команды используют в качестве приёмника любой РОН. Команда lpm Rd,Z+ при этом осуществляет ещё и инкрементирование индексного регистра Z после считывания. Типичный пример копирования строки из памяти программ в ОЗУ может выглядеть следующим образом:

Здесь последовательно осуществляется считывание строки «Hello World !» (13 байт в кодировке ASCII) расположенной в памяти программ начиная с адреса 0x1000 (задан директивой .org 0x1000). Для резервирования FLASH памяти используется директива ассемблера .db, после которой непосредственно следуют данные. Строка переписывается в ОЗУ по адресу buffer.

Есть два важных момента при использовании операций такого типа. В приведённом выше фрагменте программы в качестве указателя на строку в индексный регистр Z заносится удвоенный адрес метки 2*string и это не случайность. Дело в том, что метке string соответствует адрес слова программ 0x1000. Но слово программ у AVR имеет блину 2 байта, а в указатель Z необходимо занести именно адрес байта т.е. 2*0x1000 = 0x2000. По той же причине в памяти программ необходимо выделять только чётное (кратное двум) количество байтов и если это не так, то надо добавлять незначащий байт 0 в самом конце, как это и случилось в нашем примере (строка «Hello World !» содержит 13 байт). Попутно заметим, что строки символов ASCII, как правило, и хранят в таком формате (добавляют в конце 0). Это позволяет автоматически распознавать конец строки и считывать, не зная заранее её длины.

Использование 2-байтового регистра Z позволяет адресовать только 64 кб при считывании памяти программ. Этого явно недостаточно для тех моделей AVR, которые имеют 128 и 256 кб FLASH-памяти. Для того чтобы работать во всём диапазоне адресов к указателю Z в этом случае добавляется регистр RAMPZ из пространства РВВ, в котором используются 1 или 2 младших бита. Этот 3-байтовый индексный регистр RAMPZ:ZH:ZL используют инструкции elpm (Расширенная загрузка памяти программ), elpm Rd, Z (Расширенная загрузка памяти программ), elpm Rd,Z+ (Расширенная загрузка памяти программ с постинкрементом).

Последняя инструкция в группы команд пересылки spm (Запись памяти программ). Она реализует возможность самопрограммирования микроконтроллеров AVR. Конкретное действие, которое выполняет эта команда, зависит от установок в управляющем РВВ SPMCSR. Это может быть стирание страницы памяти программ, занесение данных для записи во временный буфер или копирование буфера в память программ, а также чтение ячеек идентификаторов и защиты. В любом случае для задания адреса области используется указатель Z, а данные, если они необходимы, передаются в регистровой паре R1:R0. Так как действие данной инструкции связано с модификацией программного кода, для ее корректного применения предпринят ряд мер предосторожности, о чём подробно будет сказано в разделе “Самопрограммирование микроконтроллеров AVR”.

Читать еще:  Защита от нсд
Ссылка на основную публикацию
ВсеИнструменты 220 Вольт
Adblock
detector