МАШИННО-ОРИЕНТИРОВАННЫЙ ЯЗЫК ASSEMBLER
Основные понятия, факты
Назначение. Связь с языками высокого уровня. Директивы сегментации. Простые типы данных. Команды обмена данными. Арифметические и логические команды. Команды сдвига. Команды передачи управления. Работа со стеком. Макросредства. Процедуры.
Навыки и умения
Разработка программ на языке Assembler с использованием пакета TASM. Использование подпрограмм и макросредств. Отладка программ с помощью автономного отладчика Turbo Debugger.
ПОДРОБНЕЕ см.
1. Юров В., Хорошенко С. Assembler: учебный курс. – СПб: Питер Ком, 1999. – 665 с.
2. Пильщиков В.И. Программирование на языке ассемблера IBM PC. – М.: «ДИАЛОГ-МИФИ», 1999. –288 с.
3. Юров В. Assembler: специальный справочник – СПб: Питер, 2000. – 496 с.
4. Пустоваров В.И. Ассемблер: программирование и анализ корректности машинных программ: - К.: Издательская группа BHV, 2000. – 480 с.
«Интересно проследить, начиная со времени появления первых компьютеров и заканчивая сегодняшним днем, за трансформациями представлений о языке ассемблера у программистов.
Когда-то ассемблер был языком, без знания которого нельзя было заставить компьютер сделать что-либо полезное. Постепенно ситуация менялась. Появлялись более удобные средства общения с компьютером. Но, в отличие от других языков, ассемблер не умирал, более того он не мог сделать этого в принципе. Почему?
Если коротко, то язык ассемблера — это символическое представление машинного языка. Все процессы в машине на самом низком, аппаратном уровне приводятся в действие только командами (инструкциями) машинного языка. Отсюда понятно, что, несмотря на общее название, язык ассемблера для каждого типа компьютера свой. Это касается и внешнего вида программ, написанных на ассемблере, и идей, отражением которых этот язык является. …
так как язык ассемблера для компьютера “родной”, то и самая эффективная программа может быть написана только на нем (при условии, что ее пишет квалифицированный программист). Здесь есть одно маленькое “но”: это очень трудоемкий, требующий большого внимания и практического опыта процесс. Поэтому реально на ассемблере пишут в основном программы, которые должны обеспечить эффективную работу с аппаратной частью. Иногда на ассемблере пишутся критичные по времени выполнения или расходованию памяти участки программы.
Впоследствии они оформляются в виде подпрограмм и совмещаются с кодом на языке высокого уровня.» [1]
ТИПЫ ДАННЫХ АССЕМБЛЕРА
Любая программа предназначена для обработки некоторой информации, поэтому вопрос о том, как описать данные с использованием средств языка обычно встает одним из первых.
TASM предоставляет очень широкий набор средств описания и обработки данных, который вполне сравним с аналогичными средствами некоторых языков высокого уровня.
Команды пересылки данных общего назначения
К этой группе относятся следующие команды:
mov <операнд назначения>,<операнд-источник>
xchg <операнд1>,<операнд2>
mov - это основная команда пересылки данных. Она реализует самые разнообразные варианты пересылки.
Отметим особенности применения этой команды:
!!! командой mov нельзя осуществить пересылку из одной области памяти в другую. Если такая необходимость возникает, то нужно использовать в качестве промежуточного буфера любой доступный в данный момент регистр общего назначения.
К примеру, рассмотрим фрагмент программы для пересылки байта из ячейки fls в ячейку fld:
mov al,fls
mov fld,al
!!! нельзя загрузить в сегментный регистр значение непосредственно из памяти. Поэтому для выполнения такой загрузки нужно использовать промежуточный объект. Это может быть регистр общего назначения или стек.
!!! нельзя переслать содержимое одного сегментного регистра в другой сегментный регистр. Это объясняется тем, что в системе команд нет соответствующего кода операции. Но необходимость в таком действии часто возникает. Выполнить такую пересылку можно, используя в качестве промежуточных все те же регистры общего назначения. Вот пример инициализации регистра es значением из регистра ds:
mov ax,ds
mov es,ax
Но есть и другой, более красивый способ выполнения данной операции — использование стека и команд push и pop:
push ds ;поместить значение регистра ds в стек
pop es ;записать в es число из стека
!!! нельзя использовать сегментный регистр cs в качестве операнда назначения. Причина здесь простая. Дело в том, что в архитектуре микропроцессора пара cs:ip всегда содержит адрес команды, которая должна выполняться следующей. Изменение командой mov содержимого регистра cs фактически означало бы операцию перехода, а не пересылки, что недопустимо.
XCHG (eXCHanGe) Обмен
Для двунаправленной пересылки данных применяют команду xchg. Для этой операции можно, конечно, применить последовательность из нескольких команд mov, но из-за того, что операция обмена используется довольно часто, разработчики системы команд микропроцессора посчитали нужным ввести отдельную команду обмена xchg.
Схема команды: xchg операнд_1,операнд_2
Назначение: обмен двух значений между регистрами или между регистрами и памятью.
Алгоритм работы: обмен содержимого операнд_1 и операнд_2.
Применение:
Команду xchg можно использовать для выполнения операции обмена двух операндов с целью изменения порядка следования байт, слов, двойных слов или их временного сохранения в регистре или памяти. Альтернативой является использование для этой цели стека.
!! операнды должны иметь один тип.
!! Не допускается (как и для всех команд ассемблера) обменивать между собой содержимое двух ячеек памяти.
Пример 1
xchg ax,bx ;обменять содержимое регистров ax и bx
xchg ax,word ptr [si] ;обменять содержимое регистра ax
;и слова в памяти по адресу в [si]
Пример 2
;поменять порядок следования байт в слове
ch1 label byte
dw 0f85ch
...
mov al,ch1
xchg ch1+1,al
mov ch1,al
АРИФМЕТИЧЕСКИЕ КОМАНДЫ
Микропроцессор может выполнять целочисленные операции и операции с плавающей точкой. Для этого в его архитектуре есть два отдельных блока:
•устройство для выполнения целочисленных операций;
•устройство с плавающей точкой.
Каждое из этих устройств имеет свою систему команд. В принципе, целочисленное устройство может взять на себя многие функции устройства с плавающей точкой, но это потребует больших вычислительных затрат.
!!! Для большинства задач, использующих язык ассемблера, достаточно целочисленной арифметики.
Сложение беззнаковых чисел
Микропроцессор выполняет сложение операндов по правилам сложения двоичных чисел.
• add операнд1,операнд2 — команда сложения с принципом действия:
операнд1 = операнд1 + операнд2
· xadd назначение,источник — обмен местами и сложение.
Команда позволяет выполнить последовательно два действия:
•обменять значения назначение и источник;
•поместить на место операнда назначение сумму:
назначение = назначение + источник.
!!! В операции сложения должны участвовать операнды одного формата (b-b, w-w)
!!! Возможны сочетания регистр - регистр
память - регистр
регистр - память
регистр - непосредственный операнд
неп. Операнд - регистр
!!! Операция память - память выполняется через промежуточный регистр
Пример1 Вычисление суммы двух чисел
. . .
slag1 dw 250
slag2 dw 125
rez dw ?
. . .
mov ax,slag1
add ax,slag2
mov rez,ax
. . .
• inc операнд — операция инкремента, то есть увеличения значения операнда на 1;
операнд = операнд +1
!!! Операнд м.б. регистром или адресом памяти и !!! не м.б. непосредственным операндом
Особые ситуации, которые могут возникать при сложении:
1) значение результата превышает размерности поля операнда
для фиксирования ситуации выхода за разрядную сетку результата в микропроцессоре предусмотрено специальное средство: флаг переноса CF (curry flag). Он располагается в бите 0 регистра флагов eflags/flags. Именно установкой этого флага фиксируется факт переноса единицы из старшего разряда операнда. (CF=1)
Например, при сложении операндов размером в байт результат не должен превышать число 255. Если это происходит, то результат оказывается неверным.
Например, выполним сложение: 254 + 5 = 259 в двоичном виде. 11111110 + 0000101 = 1 00000011. Результат вышел за пределы восьми бит и правильное его значение укладывается в 9 бит, а в 8-битовом поле операнда осталось значение 3, что, конечно, неверно.
Т.е. если при сложении двух 8-битовых чисел результат занимает 9 битов, то значение старшего 9 бита запоминается во флажке CF.
!!! Программист должен предусматривать возможность такого исхода операции сложения Необходимо включать участки кода после операции сложения, в которых анализируется флаг cf.
Анализ этого флага можно провести различными способами.
Например, использовать команду условного перехода
jc <метка> ; Переход на метку если cf = 1
jnc <метка> ; Переход на метку если cf = 0
• adc операнд_1,операнд_2 — команда сложения с учетом флага переноса cf.
операнд_1 = операнд_1 + операнд_2 + значение_cf
это команда сложения, учитывающая перенос единицы из старшего разряда.
При сложении чисел со знаком может произойти особая ситуация
2) результат выходит из диапазона допустимых значений
Переполнение регистрируется с помощью флага переполнения of.
Дополнительно к флагу of при переносе из старшего разряда устанавливается в 1 и флаг переноса cf.
Проанализировать флаг of можно командами условного перехода jo \ jno .
учет особых ситуаций должен производиться самим программистом. !!!
Команды вычитания
К командам вычитания относятся следующие:
•sub операнд_1,операнд_2 — команда вычитания; ее принцип действия:
операнд_1 = операнд_1 – операнд_2
•sbb операнд_1,операнд_2 — команда вычитания с учетом заема (флага cf ):
(subtract with borrow -вычитание с заемом)
операнд_1 = операнд_1 – операнд_2 – значение_cf
флаг cf выполняет роль индикатора заема 1 из старшего разряда при вычитании чисел.
!!! Таким образом, после команды вычитания чисел без знака нужно анализировать состояние флага cf. Если он установлен в 1, то это говорит о том, что произошел заем из старшего разряда и результат получился в дополнительном коде.
•dec операнд — операция декремента, то есть уменьшения значения операнда на 1;
neg операнд — отрицание с дополнением до двух.
Команда выполняет инвертирование значения операнд.
операнд = 0 – операнд, то есть вычитает операнд из нуля.
Команду neg операнд можно применять:
•для смены знака;
•для выполнения вычитания из константы.
Резюме
1. Сложение и вычитание знаковых и беззнаковых чисел проводятся по одним и тем же алгоритмам. ПК не знает какие числа (знаковые или беззнаковые) он складывает и вычитает, поэтому фиксирует в флагах CF OF особенности операций. Какие числа обрабатываются знает программист. Если предполагается, что работа идет с беззнаковыми числами, необходимо производить анализ флага CF, а OF не надо. Если предполагается, что работа идет со знаковыми числами, необходимо производить анализ флага ОF, а СF не надо.
2. Команды INC DEC занимают только один байт и работают быстрее, чем команды ADD SUB, занимающие три байта.
3. Команды ADD SUB устанавливают флажок переноса, а INC DEC нет
4. Кроме флагов cf и of в регистре eflags есть еще несколько флагов, которые можно использовать с двоичными арифметическими командами:
•zf — флаг нуля, который устанавливается в 1, если результат операции равен 0, и в 1, если результат не равен 0;
•sf — флаг знака, значение которого после арифметических операций (и не только) совпадает со значением старшего бита результата, то есть с битом 7, 15 или 31. Таким образом, этот флаг можно использовать для операций над числами со знаком.
Если размеры операндов, участвующих в арифметических операциях, разные следует использовать
так называемые команды преобразования типа. Эти команды расширяют байты в слова, слова — в двойные слова и двойные слова — в учетверенные слова (64-разрядные значения). Команды преобразования типа особенно полезны при преобразовании целых со знаком, так как они автоматически заполняют старшие биты вновь формируемого операнда значениями знакового бита старого объекта. Эта операция приводит к целым значениям того же знака и той же величины, что и исходная, но уже в более длинном формате. Подобное преобразование называется операцией распространения знака.
Команды без операндов:
· cbw (Convert Byte to Word) — команда преобразования байта (в регистре al) в слово (в регистре ax) путем распространения значения старшего бита al на все биты регистра ah;
· cwd (Convert Word to Double) — команда преобразования слова (в регистре ax) в двойное слово (в регистрах dx:ax) путем распространения значения старшего бита ax на все биты регистра dx;
· cwde (Convert Word to Double) — команда преобразования слова (в регистре ax) в двойное слово (в регистре eax) путем распространения значения старшего бита ax на все биты старшей половины регистра eax;
· cdq (Convert Double Word to Quarter Word) — команда преобразования двойного слова (в регистре eax) в учетверенное слово (в регистрах edx:eax) путем распространения значения старшего бита eax на все биты регистра edx.
Умножение и деление
Умножение чисел без знака
Для умножения чисел без знака предназначена команда
mul сомножитель_1
Второй операнд — сомножитель_2 задан неявно. Его местоположение фиксировано и зависит от размера сомножителей. Так как в общем случае результат умножения больше, чем любой из его сомножителей, то его размер и местоположение должны быть тоже определены однозначно. Варианты размеров сомножителей и размещения второго операнда и результата приведены в табл.
Таблица. Расположение операндов и результата при умножении
сомножитель_1 сомножитель_2 Результат
Байт al 16 бит в ax: al — младшая часть результата;
ah — старшая часть результата
Слово ax 32 бит в паре dx:ax: ax — младшая часть результата;
dx — старшая часть результата
Двойное слово eax 64 бит в паре edx:eax: eax — младшая часть результата;
edx — старшая часть результата
Для того, чтобы узнать, что результат достаточно мал и уместился в одном регистре или что он превысил размерность регистра и старшая часть оказалась в другом регистре, привлекаются флаги переноса cf и переполнения of:
•если старшая часть результата нулевая, то после операции произведения флаги
cf = 0 и of = 0;
•если же эти флаги ненулевые, то это означает, что результат вышел за пределы младшей части произведения и состоит из двух частей, что и нужно учитывать при дальнейшей работе.
Умножение чисел со знаком
Для умножения чисел со знаком предназначена команда
imul операнд_1[,операнд_2,операнд_3]
Эта команда выполняется так же, как и команда mul. Отличительной особенностью команды imul является только формирование знака. Если результат мал и умещается в одном регистре (то есть если cf = of = 0), то содержимое другого регистра (старшей части) является расширением знака — все его биты равны старшему биту (знаковому разряду) младшей части результата.
В противном случае (если cf = of = 1) знаком результата является знаковый бит старшей части результата, а знаковый бит младшей части является значащим битом двоичного кода результата.
Деление чисел без знака
Для деления чисел без знака предназначена команда
div делитель
Делитель может находиться в памяти или в регистре и иметь размер 8, 16 или 32 бит. Местонахождение делимого фиксировано и так же, как в команде умножения, зависит от размера операндов. Результатом команды деления являются значения частного и остатка.
Варианты местоположения и размеров операндов операции деления показаны в таблице.
Таблица. Расположение операндов и результата при делении
Делимое Делитель Частное Остаток
16 бит Байт - регистр Байт
в регистре ax или ячейка памяти в регистре al Байт в регистре ah
32 бит 16 бит
dx — старшая часть регистр Слово 16 бит Слово 16 бит
ax — младшая часть или ячейка памяти регистре ax в регистре dx
64 бит Двойное слово Двойное слово Двойное слово
edx — старшая часть 32 бит 32 бит 32 бит
eax — младшая часть регистр или в регистре eax в регистре edx
ячейка памяти
После выполнения команды деления содержимое флагов неопределенно, но возможно возникновение прерывания с номером 0, называемого “деление на ноль” по одной из следующих причин:
•делитель равен нулю;
•частное не входит в отведенную под него разрядную сетку.
Деление чисел со знаком
Для деления чисел со знаком предназначена команда
idiv делитель
Для этой команды справедливы все рассмотренные положения, касающиеся команд и чисел со знаком.
Логические команды
Наряду со средствами арифметических вычислений, система команд микропроцессора имеет также средства логического преобразования данных. Под логическими понимаются такие преобразования данных, в основе которых лежат правила формальной логики.
Формальная логика работает на уровне утверждений истинно и ложно. Для микропроцессора это, как правило, означает 1 и 0 соответственно.
Для компьютера язык нулей и единиц является родным, но минимальной единицей данных, с которой работают машинные команды, является байт. Однако, на системном уровне часто необходимо иметь возможность работать на предельно низком уровне — на уровне бит.
К средствам логического преобразования данных относятся логические команды и логические операции.
Система команд микропроцессора содержит пять команд, поддерживающие логические операции. Эти команды выполняют логические операции над битами операндов. Размерность операндов, естественно, должна быть одинакова. Например, если размерность операндов равна слову (16 бит), то логическая операция выполняется сначала над нулевыми битами операндов и ее результат записывается на место бита 0 результата. Далее команда последовательно повторяет эти действия над всеми битами с первого до пятнадцатого.
Логические команды
and операнд_1,операнд_2 — операция логического умножения.
Команда выполняет поразрядно логическую операцию И (конъюнкцию) над битами операндов операнд_1 и операнд_2. Результат записывается на место операнд_1.
or операнд_1,операнд_2 — операция логического сложения.
Команда выполняет поразрядно логическую операцию ИЛИ (дизъюнкцию) над битами операндов операнд_1 и операнд_2. Результат записывается на место операнд_1.
xor операнд_1,операнд_2 — операция логического исключающего сложения.
Команда выполняет поразрядно логическую операцию исключающего ИЛИ над битами операндов операнд_1 и операнд_2. Результат записывается на место операнд_1.
test операнд_1,операнд_2 — операция “проверить” (способом логического умножения).
Команда выполняет поразрядно логическую операцию И над битами операндов операнд_1 и операнд_2. Состояние операндов остается прежним, изменяются только флаги zf, sf, и pf, что дает возможность анализировать состояние отдельных битов операнда без изменения их состояния.
not операнд — операция логического отрицания.
Команда выполняет поразрядное инвертирование (замену значения на обратное) каждого бита операнда. Результат записывается на место операнда.
С помощью логических команд возможно выделение отдельных битов в операнде с целью их установки, сброса, инвертирования или просто проверки на определенное значение.
Для организации подобной работы с битами операнд_2 обычно играет роль маски. С помощью установленных в 1 битов этой маски и определяются нужные для конкретной операции биты операнд_1.
Команды сдвига
Команды этой группы также обеспечивают манипуляции над отдельными битами операндов, но иным способом, чем логические команды, рассмотренные выше.
Все команды сдвига перемещают биты в поле операнда влево или вправо в зависимости от кода операции.
Все команды сдвига имеют одинаковую структуру:
коп операнд, счетчик_сдвигов
Количество сдвигаемых разрядов — счетчик_сдвигов — располагается на месте второго операнда и может задаваться двумя способами:
•статически, что предполагает задание фиксированного значения с помощью непосредственного операнда;
•динамически, что означает занесение значения счетчика сдвигов в регистр cl перед выполнением команды сдвига.
Все команды сдвига устанавливают флаг переноса cf.
По мере сдвига битов за пределы операнда они сначала попадают на флаг переноса, устанавливая его равным значению очередного бита, оказавшегося за пределами операнда. Куда этот бит попадет дальше, зависит от типа команды сдвига и алгоритма программы.
По принципу действия команды сдвига можно разделить на два типа:
•команды линейного сдвига;
•команды циклического сдвига.
Команды линейного сдвига
К командам этого типа относятся команды, осуществляющие сдвиг по следующему алгоритму:
•очередной “выдвигаемый” бит устанавливает флаг cf;
•бит, вводимый в операнд с другого конца, имеет значение 0;
•при сдвиге очередного бита он переходит во флаг cf, при этом значение предыдущего сдвинутого бита теряется!
Команды линейного сдвига делятся на два подтипа:
•команды логического линейного сдвига;
•команды арифметического линейного сдвига.
К командам логического линейного сдвига относятся следующие:
shl операнд,счетчик_сдвигов (Shift Logical Left) - логический сдвиг влево.
Содержимое операнда сдвигается влево на количество битов, определяемое значением счетчик_сдвигов. Справа (в позицию младшего бита) вписываются нули;
shr операнд,счетчик_сдвигов (Shift Logical Right) — логический сдвиг вправо.
Содержимое операнда сдвигается вправо на количество битов, определяемое значением счетчик_сдвигов. Слева (в позицию старшего, знакового бита) вписываются нули.
Команды арифметического линейного сдвига отличаются от команд логического сдвига тем, что они особым образом работают со знаковым разрядом операнда.
sal операнд,счетчик_сдвигов (Shift Arithmetic Left) — арифметический сдвиг влево.
Содержимое операнда сдвигается влево на количество битов, определяемое значением счетчик_сдвигов. Справа (в позицию младшего бита) вписываются нули. Команда sal не сохраняет знака, но устанавливает флаг cf в случае смены знака очередным выдвигаемым битом. В остальном команда sal полностью аналогична команде shl;
sar операнд,счетчик_сдвигов (Shift Arithmetic Right) — арифметический сдвиг вправо.
Содержимое операнда сдвигается вправо на количество битов, определяемое значением счетчик_сдвигов. Слева в операнд вписываются нули. Команда sar сохраняет знак, восстанавливая его после сдвига каждого очередного бита.
Команды циклического сдвига
К командам циклического сдвига относятся команды, сохраняющие значения сдвигаемых бит. Есть два типа команд циклического сдвига:
•команды простого циклического сдвига;
•команды циклического сдвига через флаг переноса cf.
К командам простого циклического сдвига относятся:
rol операнд,счетчик_сдвигов (Rotate Left) — циклический сдвиг влево.
Содержимое операнда сдвигается влево на количество бит, определяемое операндом счетчик_сдвигов. Сдвигаемые влево биты записываются в тот же операнд справа.
ror операнд,счетчик_сдвигов (Rotate Right) — циклический сдвиг вправо.
Содержимое операнда сдвигается вправо на количество бит, определяемое операндом счетчик_сдвигов. Сдвигаемые вправо биты записываются в тот же операнд слева.
Команды простого циклического сдвига в процессе своей работы осуществляют одно полезное действие, а именно: циклически сдвигаемый бит не только вдвигается в операнд с другого конца, но и одновременно его значение становиться значением флага cf.
Команды циклического сдвига через флаг переноса cf отличаются от команд простого циклического сдвига тем, что сдвигаемый бит не сразу попадает в операнд с другого его конца, а записывается сначала в флаг переноса cf. Лишь следующее исполнение данной команды сдвига (при условии, что она выполняется в цикле) приводит к помещению выдвинутого ранее бита с другого конца операнда.
К командам циклического сдвига через флаг переноса cf относятся следующие:
rcl операнд,счетчик_сдвигов (Rotate through Carry Left) — циклический сдвиг влево через перенос.
Содержимое операнда сдвигается влево на количество бит, определяемое операндом счетчик_сдвигов. Сдвигаемые биты поочередно становятся значением флага переноса cf.
rcr операнд,счетчик_сдвигов (Rotate through Carry Right) — циклический сдвиг вправо через перенос.
Содержимое операнда сдвигается вправо на количество бит, определяемое операндом счетчик_сдвигов. Сдвигаемые биты поочередно становятся значением флага переноса cf.
Команды передачи управления
Дата добавления: 2016-02-20; просмотров: 1583;