
- •Основы алгоритмизации и программирования
- •29 Декабря 2011, протокол № 4
- •Введение
- •Этапы развития эвм
- •Поколения эвм
- •Машинно-ориентированные языки программирования
- •Архитектура эвм
- •Микропроцессоры intel
- •Набор регистров
- •Регистры общего назначения
- •Сегментные регистры
- •Регистры состояния и управления
- •Организация памяти
- •Сегментная организация памяти
- •Типы данных
- •Символы
- •Целые числа
- •Указатель на память
- •Цепочки
- •Вещественые числа
- •Двоично-десятичные числа (bcd)
- •Формат команд
- •Обработка прерываний
- •Int тип_прерывания
- •Синтаксис ассемблера
- •Алфавит ассемблера
- •Директивы сегментации
- •Упрощённые директивы сегментации
- •Директивы резервирования и инициализации данных
- •Операнды
- •Способы задания операндов Прямая адресация
- •Косвенная адресация
- •Косвенная базовая адресация
- •Косвенная базовая адресация со смещением
- •Косвенная индексная адресация со смещением
- •Косвенная базовая индексная адресация
- •Косвенная базовая индексная адресация со смещением
- •Операторы
- •Функциональная классификация машинных команд
- •Команды пересылки данных Команды общего назначения
- •Работа с адресами и указателями
- •Преобразование данных
- •Xlat [адрес_таблицы_перекодировки]
- •Ввод из порта и вывод в порт
- •Работа со стеком
- •Арифметические команды Форматы арифметических данных
- •Арифметические операции над целыми двоичными числами
- •Логические команды
- •Команды передачи управления
- •Команда безусловного перехода
- •Условные переходы
- •Организация циклов
- •Основы алгоритмизации и программирования
Арифметические команды Форматы арифметических данных
Целое двоичное число – это число, закодированное в двоичной системе счисления. Размерность – 8,16 или 32 бита. Знак двоичного числа определяется тем, как интерпретируется старший бит. Среди арифметических команд есть всего две команды, которые действительно учитывают этот разряд как знак – целочисленное умножение и деление imul и idiv. В остальных случаях ответственность за действия с числами со знаком ложится на программиста.
Описание двоичных чисел с фиксированной точкой в сегменте данных выполняется с помощью директив описания данных DB, DW и DD. Например:
. data
per 1 db 23
per 2 dw 9856
per 3 dd 98756432
per 4 dw 2985
Десятичные числа – специальный вид представления числовой информации, в основу которого положен принцип кодирования каждой десятичной цифры числа группой из четырех бит. При этом каждый байт числа содержит одну или две десятичные цифры в так называемом двоично-десятичном коде (BCD–Binary Coded Decimal). Используются два формата BCD-чисел:
в упакованном формате каждый байт содержит две десятичные цифры, например число 56704 хранится в трёх байтах: 0000 0101 0110 0111 0000 0100;
в неупакованном формате каждый байт содержит одну десятичную цифру в четырёх младших битах. Старшие четыре бита имеют нулевое значение. Тоже число 56704 в неупакованном виде будет хранится в 5 байтах: 0000 0101 0000 0110 0000 0111 0000 0000 0000 0100.
Для описания двоично-десятичных цифр в программе используются только две директивы – DB и DT. Например:
. data
per 1 db 2,3,4,6,8,2 ; неупакованное BCD число 286432
per 3 dt 9875645 ; упакованное BCD число 987545
Арифметические операции над целыми двоичными числами
Сложение двоичных чисел без знака
Микропроцессор выполняет сложение операндов по правилам сложения двоичных чисел. Для фиксирования выхода за разрядную сетку предназначен флаг переноса cf в бите 0 регистра флагов flags. Самый простой способ анализа флага cf – использовать команду jc (или jnc).
Имеется три команды двоичного сложения:
– INC операнд – операция инкремента, то есть увеличение значения операнда на 1;
– ADD операнд1,операнд2 – команда сложения с принципом действия: операнд1 = операнд1 + операнд2;
– ADC операнд1,операнд2 – команда сложения с учетом флага переноса cf с принципом действия: операнд1 = операнд1 + операнд2 + cf.
Рассмотрим фрагмент программы вычисления суммы чисел 254 и 17.
…
. data
a db 254
. code
…
xor ax, ax ;обнуление ax
add al,17 ;помещение в al одного из слагаемых
add al,a ;сложение переменной а с al
jnc m1 ;если нет переноса, то переход на m1
adc ah, 0 ;в ax(ah) – сумма с учетом переноса
m1: …
Сложение двоичных чисел со знаком
Кроме флага переноса cf и команды adc есть и другое средство – регистрация состояния старшего (знакового) разряда операнда, которое осуществляется с помощью флага переполнения оf в регистре flags.
Ситуация переполнения (установка флага оf в 1) происходит при переносе:
– из 14 разряда (для положительных чисел со знаком);
– из 15-го разряда (для отрицательных чисел).
И наоборот, переполнения не происходит, если есть перенос из обоих разрядов или перенос отсутствует в обоих разрядах. Переполнение регистрируется с помощью флага переполнения оf. Дополнительно к флагу оf при переносе из старшего разряда устанавливается и флаг переноса сf ). Так как микропроцессор не знает о существовании чисел со знаком и без знака, то вся ответственность за правильность действий с числами ложится на программиста. Проанализировать флаги сf и оf можно командами условного перехода jc/jnc и jo/jno соответственно.
Вычитание двоичных чисел без знака
Как и при анализе операции сложения, проанализируем процессы, происходящие при выполнении операции вычитания:
если уменьшаемое больше вычитаемого, то разность положительная, результат верен.
если уменьшаемое меньше вычитаемого, результат меньше 0, а это уже число со знаком. В этом случае результат необходимо откорректировать. После команды вычитания чисел без знака нужно анализировать флаг сf. Если сf=1, то произошел заем из старшего разряда, и результат получился в дополнительном коде.
К командам вычитания относятся следующие:
DEC операнд – операция декремента, то есть уменьшения операнда на 1;
SUB операнд1,операнд2 – команда вычитания с принципом действия: операнд1=операнд1–операнд2;
SBB операнд1,операнд2 – команда вычитания с принципом действия: операнд 1=операнд 1–операнд 2– сf.
Рассмотрим пример программной обработки ситуации при вычитании чисел без знака
xor ax,ax
mov al,5
sub al,10
jnc m1 ; нет переноса ?
neg al ; в al – модуль результата
m1: …
С указанными для этой команды вычитания исходными данными результат получается в дополнительном коде (отрицательный). Чтобы преобразовать результат к нормальному виду (получить его модуль), применяется команда neg, с помощью которой получается дополнение операнда. В нашем случае мы получили дополнение дополнения, или модуль отрицательного результата. Тот факт, что число отрицательное, отражен в состоянии флага cf.
Вычитание двоичных чисел со знаком
Для вычитания способом сложения чисел со знаком необходимо представить числа в дополнительном коде (и уменьшаемое, и вычитаемое). Результат тоже будет в дополнительном коде. Сложность здесь в том, что старший бит рассматривается как знаковый. Отследить эту ситуацию можно по флагу переполнения оf. Его установка в 1 говорит о том, что результат вышел за диапазон представления знаковых чисел (то есть изменился старший бит) для операнда данного размера, и программист должен предусмотреть действия по корректировке результата.
Умножение двоичных чисел без знака
Для умножения двоичных чисел без знака предназначена команда
MUL сомножитель1
Второй операнд – сомножитель2 задан неявно. Его местоположение фиксировано и зависит от размера сомножителей. Так как в общем случае результат умножения больше, чем любой из сомножителей, то его размер и местоположение должны быть определены тоже однозначно. Варианты размеров сомножителей и размещения второго операнда и результата приведены в таблице.
Таблица 8. – Расположение операндов и результата при умножении
Сомножитель1 |
Сомножитель2 |
Результат |
Байт |
al |
16 бит в ax: al-младшая часть результата; ah-старшая часть результата |
Слово |
ax |
32 бит в паре dx:ax: ax-младшая часть, dx-старшая часть |
Двойное слово |
eax |
64 бит в паре edx:eax: eax-младшая часть, edx-старшая часть |
Из таблицы видно, что произведение состоит из двух частей и в зависимости от размера операндов размещается в двух местах: на месте сомножитель2 (младшая часть) и в дополнительных регистрах ah, dx, edx (старшая часть). Для того, чтобы определить, что результат достаточно мал и уместился в одном регистре, или что он превысил размерность регистра, и старшая часть результата оказалась в другом регистре, используются флаги переноса cf и переполнения of.
Если старшая часть результата нулевая, то после операции умножения флаги cf и of равны 0. Если же эти флаги ненулевые, то это означает, что результат состоит из двух частей, и это обстоятельство следует учитывать при дальнейшей работе. Например умножение чисел 25 и 45:
.data
rez_l db 45
rez_h db 0
.code
…
xor ax, ax
mov al, 25 ;в al один из сомножителей
mul res_1 ;результат в al (младшая часть) и в ah (старшая часть)
jnc m1 ;если нет переполнения, то на m1
mov rez_h,ah ;старшая часть результата в rez_h
m1:
mov rez_1,al
…
Для определения размера результата командой условного перехода jnc анализируется состояние флага cf, и если оно не равно 1, то результат остается в рамках регистра al. Если же cf=1, то выполняется команда в строке 16, которая формирует в поле rez_h старшее слово результата.
Умножение двоичных чисел со знаком
Для умножения чисел со знаком предназначена команда
IMUL операнд_1[,операнд_2,операнд_3].
Эта команда выполняется так же, как и команда mul. Отличие только в формировании знака. Если результат мал и умещается в одном регистре (cf=of=0), то содержимое другого регистра (старшей части) является расширением знака – все его биты равны старшему биту (знаковому разряду) младшей части результата. В противном случае (cf=of=1) знаком результата является знаковый бит старшей части результата, а знаковый бит младшей части является значащим битом двоичного кода результата.
Деление двоичных чисел без знака
Для деления чисел без знака предназначена команда
DIV делитель.
Делитель может находиться в памяти или в регистре и иметь размер 8, 16 или 32 разряда. Местонахождение делимого фиксировано и так же, как в команде умножения, зависит от размера операндов. Результатом команды деления являются частное и остаток.
Таблица 9. – Расположение операндов и результата при делении
Делимое |
Делитель |
Частное |
Остаток |
Слово (16 бит) в регистре AX |
Байт в регистре или в ячейке памяти |
Байт в регистре AL |
Байт в регистре AH |
Двойное слово (32 бит), в DX – старшая часть, в AX – младшая часть |
Слово (16 бит) в регистре или ячейке памяти |
Слово (16 бит) в регистре AX |
Слово (16 бит) в регистре DX |
Учетверённое слово (64 бит), в EDX – старшая часть, в EAX – младшая часть |
Двойное слово (32 бит) в регистре или ячейка памяти |
Двойное слово (32 бит) в регистре EAX |
Двойное слово (32 бит) в регистре EDX |
После выполнения деления содержимое флагов неопределенно, но возможно возникновение прерывания с номером 0, называемого “деление на ноль”. Прерывание 0 при выполнении команды div может возникнуть из-за следующих причин:
делитель равен нулю;
частное не входит в отведенную для него разрядную сетку.
Рассмотрим деление значения в области del на значение в области delt :
.data
del_b label byte
del dw 29876
del_t db 45
.code
…
xor ax,ax
;следующие две команды можно заменить одной mov ax,del
mov ah,del_b ;старший байт делимого в ah
mov al,del_b+1 ;младший байт делимого в al
div delt ;в al – частное, в ah – остаток
…
Деление двоичных чисел со знаком
Для деления чисел со знаком предназначена команда
IDIV делитель.
Для этой команды справедливы все рассмотренные положения, касающиеся команд и чисел со знаком.
Команды преобразования типов
Эти команды используются, если операнды, участвующие в арифметических командах имеют разный размер. Вот некоторые из них:
CBW (Convert Byte to Word) – команда преобразования байта (в регистре AL) в слово (в регистре AX) путем распространения значения старшего бита AL на все биты регистра AH.
CWD (Convert Word to Double) – команда преобразования слова (в регистре AX) в двойное слово (в регистрах DX:AX) путем распространения значения старшего бита AX на все биты регистра DX.