- •Программы и программное обеспечение
- •5) Программирование на ассемблере.
- •10) Элементарные конструкции языка ассемблера:
- •11) Элементарные конструкции языка ассемблера:
- •16) Описание данных в ассемблер.
- •17) Команды пересылки данных общего назначения в ассемблере.
- •18) Команды загрузки адресных значений.
- •19) Команды ввода-вывода
- •21) Сложение двоичных чисел со знаком
- •22) Умножение двоичных чисел без знака
- •23) Деление двоичных чисел без знака
- •27) Логические команды языка ассемблер. Синтаксис. Примеры.
- •33) Кросс-система
- •34) Средства и виды отладки в ассемблер и кросс-системах
- •35) Отладка с помощью программы – интерпретатор
- •4. Модель времени
- •5. Модель системы прерываний
- •6. Модель системы ввода/вывода
18) Команды загрузки адресных значений.
К этой подгруппе относятся команды LEA, LDS и LES.
Команда загрузки исполнительного адреса LEA имеет формат:
LEA регистр16, память
и загружает в указанный 16-битный регистр адрес (смещение offset) указанной ячейки памяти, адресуемой любым способом.
Пример 3.49:
Пусть имеется сегмент данных:
Data SEGMENT
DB 10 DUP(?)
Tabl DB 20h, 21h, 22h, 23h, 24h, 25h
Data ENDS
Тогда при обращениях к таблице Tabl, получим:
LEA BX, Tabl ;Идентично MOV BX, OFFSET Tabl
MOV AL, [BX] ;(AL)=20h
LEA BX, Tabl[4] ;Идентично MOV BX, OFFSET Tabl[4]
MOV AL, [BX] ;(AL)=24h
MOV SI, 3
LEA BX, Tabl[SI] ;Корректно
MOV AL, [BX] ;(AL)=23h
MOV BX, OFFSET Tabl[SI]; Необнаруживаемая ошибка MOV AL, [BX] ;Ошибка – (AL)=20h
Команды загрузки полных адресных значений LDS и LES имеют формат:
LDS регистр16, память32
LES регистр16, память32
и осуществляют загрузку двойного слова из ячейки памяти, адресуемой любым способом, в соответствующий сегментный регистр и указанный 16-битный регистр. При этом младшее слово, расположенное по адресу "память32", загружается в 16-битный регистр, а старшее слово, считываемое по адресу "память32+2", командой LDS загружается в регистр DS, а командой LES - в регистр ES. В соответствии с этим младшее слово области "память32" должно содержать смещение offset, а старшее слово - сегментную компоненту segment выбираемой переменной. Так как команды LDS и LES загружают полный логический адрес, то программа может сразу обращаться к объекту, на который указывает этот адрес.
Команды LDS и LES наиболее часто используются для инициализации регистров при обработке строк.
Пример 3.50:
Скопировать элемент одной строки в две других строки:
String1 DB 80 DUP(?) ;Описание строк
String2 DB 80 DUP(?)
String3 DB 80 DUP(?)
BuffSrc DD String1 ;Инициализация адресного буфера
;источника на String1
BuffDst DD String2 ;Инициализация адресного буфера
;приемника на String2
;. . . . . . . . . . . . . . . . . . . . . . . . .
LDS SI,BuffSrc ;Инициализация DS:SI на строку-источник
LES DI,BuffDst ;Инициализация ES:DI на строку-приемник
MOV AL,[SI] ;Копирование элемента
MOV ES:[DI],AL ;String1 в String2
;. . . . . . . . . . . . . . . . . . . . . . . . .
MOV AX,OFFSET String3 ;Переопределение адресного указателя
MOV WORD PTR BuffDst,AX ;приемника на String3
MOV AX,SEG String3
MOV WORD PTR BuffDst+2,AX
;. . . . . . . . . . . . . . . . . . . . . . . . .
LDS SI,BuffSrc ;Инициализация DS:SI на строку- источник
LES DI,BuffDst ;Инициализация ES:DI на строку- приемник
MOV AL,[SI] ;Копирование элемента
MOV ES:[DI],AL ;String1 в String3.
19) Команды ввода-вывода
Команда ввода IN и команда вывода OUT допускают работу как с байтами, так и со словами. Команда IN загружает данные из заданного порта в аккумулятор, а команда OUT выполняет передачу из аккумулятора в порт. Для портов ввода-вывода в диапазоне 00-FF можно использовать прямую укороченную адресацию, а остальные порты в диапазоне 100-FFFF можно адресовать только косвенно через регистр DX.
Формат команд:
IN ac,port OUT port,ac (прямая укороченная адресация)
IN ac,DX OUT DX,ac (косвенная адресация)
20) СЛОЖЕНИЕ ДВОИЧНЫХ ЧИСЕЛ БЕЗ ЗНАКА.
Процессор выполняет сложение операндов по правилам сложения двоичных чисел. Проблем не возникает до тех пор, пока значение результата не превышает размерности поля операнда. Например, при сложении операндов размером в байт результат не должен превышать 255. Если это происходит, то результат оказывается неверен. Рассмотрим, почему. К примеру, выполним сложение: 254 +5 = 259 в двоичном виде:
11111110 + 0000101 - 1 00000011.
Результат вышел за пределы восьми битов, и правильное его значение укладывается в 9 битов, а в 8-разрядном поле операнда осталось значение 3, что, конечно, неверно. В процессоре подобный исход сложения прогнозируется, и предусмотрены специальные средства для фиксации подобных ситуаций и их обработки. Так, для фиксации ситуации выхода за разрядную сетку результата, как в данном случае, предназначен флаг переноса CF. Он располагается в бите 0 регистра флагов EFLAGS/FLAGS. Именно установкой этого флага фиксируется факт переноса единицы из старшего разряда операнда. Естественно, что программист должен предусматривать возможность такого исхода операции сложения и средства для корректировки. Это предполагает включение фрагментов кода после операции сложения, в которых анализируется состояние флага CF. Этот анализ можно провести различными способами. Самый простой и доступный — использовать команду условного перехода JC. Эта команда в качестве операнда имеет имя метки в текущем сегменте кода. Переход на метку осуществляется в случае, если в результате работы предыдущей команды флаг CF установился в 1.
В системе команд процессора имеются три команды двоичного сложения:
- команда инкремента, то есть увеличения значения операнда на 1:
inc операнд
- команда сложения (операнд_1 = операнд_1 + операнд_2):
add операнд_1,операнд_2
- команда сложения с учетом флага переноса CF
(операнд_1 =• операнд_1 + операнд_2 + значение_СР):
adc операнд_1,операнд_2
Рассмотрим пример вычисления суммы чисел (листинг 8.3).
Листинг 8.3. Вычисление суммы чисел
<1> ;prg_8_3.asm
<2> masm
<3> model small
<4> stack 256
<5> .data
<6> a db 254
<7> .code ;сегмент кода
<8> main:
<9> mov ax,@data
<10> mov ds.ax
<11>…
<12>xor ax,ax
<13>add al,17
<14>add al, a
<15>jnc ml ;если нет переноса, то перейти на ml
<16>adc ah,0 ;в ах сумма с учетом переноса
<17> ml: ;...
<18> exit:
<19> mov ax,4c00h;стандартный выход
<20> int 21h
<21> end main ;конец программы
В строках 13-14 создана ситуация, когда результат сложения выходит за границы операнда. Эта возможность учитывается строкой 15, где команда JNC (хотя можно было обойтись и без нее) проверяет состояние флага CF. Если он установлен в 1, то результат операции получился большим по размеру, чем операнд, и для его корректировки необходимо выполнить некоторые действия. В данном случае мы просто полагаем, что границы операнда расширяются до размера АХ, для чего учитываем перенос в старший разряд командой ADC (строка 16). Напомню, что исследовать работу команд сложения без учета знака вы можете в отладчике. Для этого введите в текстовом редакторе текст листинга 8.3, получите исполняемый модуль, запустите отладчик и откройте в нем окна командами View > Dump и View > Registers. Далее, в пошаговом режиме отладки можно более наглядно проследить за всеми процессами, происходящими в процессоре во время работы программы.
ВЫЧИТАНИЕ ДВОИЧНЫХ ЧИСЕЛ БЕЗ ЗНАКА
Аналогично анализу операции сложения, порассуждаем над сутью процессов, происходящих при выполнении вычитания.
Если уменьшаемое больше вычитаемого, то проблем нет, разность положительна, результат верен.
Если уменьшаемое меньше вычитаемого, возникает проблема: результат меньше 0, а это уже число со знаком. В этом случае результат необходимо завернуть. Что это означает? При обычном вычитании (в столбик) делают заем 1 из старшего разряда. Процессор поступает аналогично, то есть занимает 1 из разряда, следующего за старшим в разрядной сетке операнда.
Поясним на примере.
Первый вариант вычитания чисел:
05 = 0000000000000101
-10 = 0000000000001010.
Для того чтобы произвести вычитание, произведем воображаемый заем из старшего разряда:
100000000 00000101
-
0000000000001010
=
11111111 11111011.
Тем самым, по сути, выполняется действие (65 536 + 5) - 10 = 65 531, 0 здесь как бы эквивалентен числу 65 536. Результат, конечно, неверен, но процессор считает, что все нормально, тем не менее, факт заема единицы он фиксирует, устанавливая флаг переноса CF. Посмотрите еще раз внимательно на результат операции вычитания. Это же число -5 в дополнительном коде! Проведем эксперимент: представим разность в виде суммы 5 + (-10).
Второй вариант вычитания чисел:
5 =0000000000000101
+
(-10) =11111111 11110110
=
11111111 11111011.
То есть мы получили тот же результат, что и в предыдущем примере. Таким образом, после команды вычитания чисел без знака нужно анализировать состояние флага CF. Если он установлен в 1, это говорит о том, что произошел заем из старшего разряда и результат получился в дополнительном коде.
Команда декремента выполняет уменьшения значения операнда на 1:
dec операнд
Команда вычитания (операнд_1 = операнд_1 - операнд_2):
sub операнд_1,операнд_2
Команда вычитания с учетом заема, то есть флага CF (операнд_1 = операнд_1 -
операнд_2 - значение_СР):
sbb операнд_1,операнд_2
