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



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

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

xlat [адрес_таблицы_перекодировки]

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

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

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

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

Листинг 2. Использование таблицы перекодировки<1>;---------Prg_7_3.asm----------------------<2>;Программа преобразования двузначного шестнадцатеричного числа<3>;в двоичное представление с использованием команды xlat.<4>;Вход: исходное шестнадцатеричное число; вводится с клавиатуры.<5>;Выход: результат преобразования в регистре al.<6>.data ;сегмент данных<7> message db ‘Введите две шестнадцатеричные цифры,$’<8> tabl db 48 dup (0),0,1,2,3,4,5,6,7,8,9, 8 dup (0),<9> db 0ah,0bh,0ch,odh,0eh,0fh,27 dup (0)<10> db 0ah,0bh,0ch,odh,0eh,0fh, 153 dup (0)<11> .stack 256 ;сегмент стека<12> .code<13> ;начало сегмента кода<14> proc main ;начало процедуры main<15> mov ax,@data ;физический адрес сегмента данных в регистр ax<16> mov ds,ax ;ax записываем в ds<17> lea bx,tabl ;загрузка адреса строки байт в регистр bx<18> mov ah,9<19> mov dx,offset message<20> int 21h ;вывести приглашение к вводу<21> xor ax,ax ;очистить регистр ax<22> mov ah,1h ;значение 1h в регистр ah<23> int 21h ;вводим первую цифру в al<24> xlat ;перекодировка первого введенного символа в al<25> mov dl,al<26> shl dl,4 ;сдвиг dl влево для освобождения места для младшей цифры<27> int 21h ;ввод второго символа в al<28> xlat ;перекодировка второго введенного символа в al<29> add al,dl ;складываем для получения результата<30> mov ax,4c00h ;пересылка 4c00h в регистр ax<31> int 21h ;завершение программы<32> endp main ;конец процедуры main<33> code ends ;конец сегмента кода<34> endmain ;конец программы с точкой входа main

Сама по себе программа проста; сложность вызывает обычно формирование таблицы перекодировки. Обсудим этот момент подробнее.

Прежде всего нужно определиться с значениями тех байтов, которые вы будете изменять. В нашем случае это символы шестнадцатеричных цифр. Сконструируем в сегменте данных таблицу, в которой на места байтов, соответствующих символам шестнадцатеричных цифр, помещаем их новые значения, то есть двоичные эквиваленты шестнадцатеричных цифр. Строки 8-10 листинга 2 демонстрируют, как это сделать. Байты этой таблицы, смещения которых не совпадают со значением кодов шестнадцатеричных цифр, нулевые. Таковыми являются первые 48 байт таблицы, промежуточные байты и часть в конце таблицы.

Желательно определить все 256 байт таблицы. Дело в том, что если мы ошибочно поместим в al код символа, отличный от символа шестнадцатеричной цифры, то после выполнения команды xlat получим непредсказуемый результат. В случае листинга 2 это будет ноль, что не совсем корректно, так как непонятно, что же в действительности было в al — код символа “0” или что-то другое.

Поэтому, наверное, есть смысл здесь поставить “защиту от дурака”, поместив в неиспользуемые байты таблицы какой-нибудь определенный символ. После каждого выполнения xlat нужно будет просто контролировать значение в al на предмет совпадения с этим символом, и если оно произошло, выдавать сообщение об ошибке.

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

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

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

Стек — это область памяти, специально выделяемая для временного хранения данных программы. Важность стека определяется тем, что для него в структуре программы предусмотрен отдельный сегмент. На тот случай, если программист забыл описать сегмент стека в своей программе, компоновщик 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

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


Дата добавления: 2019-09-13; просмотров: 314; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!