Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Assembler / P18

.pdf
Скачиваний:
51
Добавлен:
02.06.2015
Размер:
595.3 Кб
Скачать
1 b 1, 0 b b.

18. Битовые операции

18.1. Булевские команды

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

ячейки.

 

 

Инвертировать

not opr

opr opr

"ИЛИ" (дизъюнкция)

or dst,src

dst dst src

"И" (конъюнкция)

and dst,src

dst dst src

"Исключающее ИЛИ"

xor dst,src

dst dst src

Логическое сравнение

test opr1,opr2

opr1 opr2

Команда not на флаги не действует, остальные команды сбрасывают CF и OF, а флаги SF, ZF, PF изменяют по обычным правилам. Рассмотрим серию примеров, иллюстрирующих работу этих команд.

Пример. Команда инвертирования битов. Операция НЕ применяется к каждому биту.

mov al, 10011101b

;

AL = 9Dh

not al

;

AL = 01100010b = 62h

Остальные команды двухоперандные. Обычно они используются по следующей схеме. В источник src помещается так называемая "маска" — непосредственный операнд. Маска указывает, как изменять биты приемника.

Пример. Установка и сброс битов в соответствии с маской.

1) В регистре DL установить 6-й, 3-й и 1-й биты. Применяем дизъюнкцию с маской, используя правила поглощения

маска

0

1

0

0

1

0

1

0

 

DL

b7

b6

b5

b4

b3

b2

b1

b0

 

DL маска

b7

1

b5

b4

1

b2

1

b0

Команда имеет вид or dl, 01001010b (или or dl, 4Ah).

 

 

 

 

2) В регистре CH сбросить 4-й и 3-й биты. Применяем конъюнкцию с маской, используя

правила поглощения 1 b b, 0 b 0.

 

 

 

 

 

 

 

 

маска

1

1

1

0

0

1

1

1

 

CH

b7

b6

b5

b4

b3

b2

b1

b0

 

CH маска

b7

b6

b5

0

0

b2

b1

b0

Команда and ch,11100111b (или and ch,0E7h).

 

 

 

 

 

 

3) Инвертировать 4-й и 3-й биты регистра BH. Применяем "исключающее ИЛИ",

используя правила 1 b b, 0 b b.

 

 

 

 

 

 

 

 

маска

0

0

0

1

1

0

0

0

 

 

CH

 

b7 b6 b5

b4

b3

b2 b1 b0

 

CH маска

 

b7 b6 b5

b4

b3

b2

b1

b0

Команда xor ch,11000b (или xor ch,18h).

 

 

 

 

 

 

Команда xor применяется для обнуления регистров:

код xor

ax,ax короче, чем

mov ax, 0 (проверьте!).

Пример [The Assembly Gems Pages. http://www.df.lth.se/~john_e/]. Вычисление абсолютной величины для содержимого AX без ветвлений.

cwd

xor ax,dx sub ax,dx

Самостоятельно разберите этот фрагмент.

Вместо команды

cmp ax,0 ; AX = 0 ?

предпочитают использовать более короткие команды and ax,ax или or ax,ax. Они оставляют содержимое AX неизменным, но устанавливают или сбрасывают флаг ZF.

Команду test используют, чтобы проверить, установлен ли определенный бит операнда, и при этом не "испортить" операнд.

Пример. Установлен ли 5-й бит DL? Если ДА, перейти на метку m. test dl,00100000b

jnz m

С помощью этой команды легко проверить делимость числа на степени двойки. Например, если два младших бита числа — нули, то оно делится на 4:

test cx, 11b jz yes

18.2. Команды сдвигов

Эти команды перемещают содержимое ячейки влево или вправо. Одним из операндов этих команд является количество сдвигов cnt. Оно либо равно 1, либо определяется содержимым регистра CL (при этом CL сохраняет свое содержимое после операции). Начиная с 286 процессора количество сдвигов, отличное от единицы, можно указывать как непосредственный операнд.

Естественно, возникает вопрос (для определенности говорим о сдвиге на одну позицию вправо): куда девается младший бит (точнее, его содержимое)? что записывается в старший бит? В командах сдвига эти вопросы решаются по-разному.

18.2.1. Логические и арифметические сдвиги.

Логический/арифметический

 

 

 

 

 

 

сдвиг влево

 

 

 

 

shl/sal opr,cnt

(Shift Logical/Arithmetic Left)

 

 

 

 

 

 

 

CF

 

opr

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Логический сдвиг вправо

 

 

 

 

 

shr opr,cnt

(Shift Logical Right)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

opr

 

 

 

CF

0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Арифметический сдвиг вправо

 

 

 

sar opr,cnt

(Shift Arithmetic Right)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

opr

 

 

 

CF

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Флаг CF устанавливается в соответствии с рисунками. Флаг OF имеет смысл , если cnt

= 1.

Сначала остановимся на формальном синтаксисе команд.

Неправильно

Правильно

1) sar ax,2 (в 286 и выше — правильно!)

1) mov cl,2

 

 

 

sar ax,cl

 

 

2) mov bl,3

2) shl ax,1

shr dx,bl (количество сдвигов только в CL)

 

Арифметические сдвиги предназначены для выполнения умножения и деления на степени двойки (табл.6.1).

 

 

Таблица 6.1

операнд

умножение

деление

знаковый

sal/shl

sar

беззнаковый

shr

 

Умножение на 2 — сдвиг влево и приписывание справа нуля. Докажите, что OF = CF

SF.

Деление на 2 — сдвиг вправо, в CF попадает остаток от деления. При арифметическом сдвиге вправо дублируется знаковый бит: поэтому отрицательное число остается отрицательным.

Пример. Пусть AL = –120 = 10001000. sar al,1 дает AL = –60 = 11000100.

Проверьте.

К сожалению, результат деления с помощью команды sar не всегда совпадает с результатом деления, полученным с помощью команды idiv. Сравните результат от деления –5 на 2, полученный с помощью и той и другой команды.

18.2.2. Циклические сдвиги.

Циклический сдвиг влево

rol opr,cnt

(ROtate Left)

 

CF opr

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Циклический сдвиг вправо

 

 

ror opr,cnt

(ROtate Right)

 

 

 

 

 

opr CF

Циклический сдвиг влево через перенос

rcl opr,cnt

(Rotate through Carry Left)

 

 

 

CF

opr

 

Циклический сдвиг вправо через перенос

rcr opr,cnt

(Rotate through Carry Right)

 

opr

CF

Пример. Двойное слово в DX:AX умножить на 4.

shl ax,1 ; Старший бит AX — в CF rcl dx,1 ; CF — в младший бит DX shl ax,1

rcl dx,1

Пример. Сосчитать количество единичных битов в AX. Результат поместить в BX.

xor bx,bx

; обнулить счетчик единичных битов

mov cx,16

; разрядность слова — в CX

n: rol ax,1

; циклический сдвиг влево, старший бит — в CF

adc bx,0

; добавить величину CF к счетчику

loop n

 

После выполнения этого фрагмента в AX будет восстановлено первоначальное содержимое.

Пример. Вращать содержимое AX влево, пока в старшем (15-ом) бите не появится '1', и перейти на метку n1. Если AX = 0, то перейти на метку n0.

Воспользуемся командой rcl (можно и rol) и тем, что OF = CF SF.

cmp ax,0

 

 

je

n0

; если AX = 0 — на n0

js

n1

;

если старший бит установлен — на n1

mov cx,16

;

разрядность AX — в CX

m:rcl ax,1

jo n1 ; если CF = 0 и SF = 1 — на n1 loop m

(метки n0 и n1 не показаны)

18.3. Средства Ассемблера для битовых операций

На этапе ассемблирования над числами возможны следующие логические операции:

NOT, AND, OR, XOR, SHL, SHR. Приведем примеры.

L = 101b

 

 

 

L = L SHL 2

 

; L = 10100b

mov al, NOT 3

 

;

AL = 0FCh

mov bh, 1100b

OR 1010b

;

BH = 1110b

Когда требуется плотная упаковка нескольких числовых значений, диапазон изменения которых невелик, их помещают в заранее определенные битовые поля ячейки памяти. Рассмотрим пример, взятый из [5].

Разместим в слове информацию о работнике (табл.6.2).

Таблица 6.2

Биты

Длина

Имя

Назначение

поля

поля

 

 

15

1

sex

пол (0 — мужчина, 1 — женщина)

14

1

married

семейное положение (0 — холост, 1 — женат)

13:10

4

children

количество детей (0 — 15)

9

1

xxx

зарезервировано

8:2

7

age

возраст (0 — 127)

1:0

2

school

уровень образования (0 — 3)

Пусть в программе имеется описание:

Johnson DW 0100100010001011b

Нужно получить количество детей Джонсона (чтобы предоставить ему налоговые льготы). Сначала дадим "лобовое" решение задачи.

mov ax, Johnson

; поместить данные в AX

and

ax,

0011110000000000b

; выделить поле children

shr

ax,

10

;

прижать поле к правому краю

 

 

 

;

(в AX — количество детей)

Стоит нам ошибиться хотя бы на один бит в маске и количестве сдвигов и мы получим неверный результат. Нельзя ли поручить этот скрупулезный подсчет битов Ассемблеру? Да, TASM предоставляет нам такую возможность. Приведем полный текст программы

.MODEL small

.286

.STACK 100h

.DATA

person RECORD sex:1=0,married:1,children:4,xxx:1, \ age:7,school:2=1

Johnson person <,1,2,,34,3>

.CODE

start:

mov ax,@data mov ds,ax

mov ax, Johnson

and ax, MASK children shr ax, children

mov ax,4c00h int 21h

END start

Прокомментируем текст программы. Директива .286 разрешает использовать инструкции 286-го процессора. Эта директива нужна, чтобы TASM сгенерировал код инструкции shr ax,10 (посмотрите в Turbo Debugger, что получится, если не указать эту директиву). Директива RECORD задает шаблон описания битовых полей. Этот шаблон носит имя person. В шаблоне указаны имя битового поля и его ширина, а не биты, которое занимает поле, — из-за этого пришлось включить в шаблон зарезервированное поле xxx. В двух полях (sex и age) после знака равенства дано значение "по умолчанию", которое может быть переопределено при описании с использованием шаблона. В шаблоне необязательно описывать все биты ячейки: можно было описать, например, только поля age и school. Само описание шаблона памяти не выделяет, зато описание выделяет в памяти слово в соответствии с шаблоном. Использован символ \ для продолжения директивы на следующей строке. В этом описании заданы значения всех полей, кроме xxx (значение которого безразлично), и sex (которое берется по умолчанию из шаблона).

Три строки в программе после загрузки DS полностью эквивалентны трем строкам, приведенным ранее. Операция (выполняемая на этапе ассемблирования) MASK поле_записи возвращает маску, необходимую для выделения поля записи. Значение поля_записи — число битов, на которые нужно осуществить сдвиг вправо, чтобы прижать поле к правому краю ячейки.

Если нужно прижать поле к левому краю слова, можно воспользоваться командой shl ax, 16 – WIDTH children – children.

Здесь в вычислениях участвует ширина поля WIDTH поле.

18.4. Задание D2. Пример программы, включающей битовые операции

18.4.1. Формулировка задания

Общее задание. Написать подпрограмму, обрабатывающую массив, и макрос для ее вызова. Подпрограмма обработки массива вызывает подпрограмму обработки элемента массива. В программе привести примеры вызова макроса. Программу, макрос и подпрограммы разместить в отдельных файлах.

В Turbo Debugger записать в файл протокола окна данных до и после выполнения программы. Записать туда же состояние стека в момент выполнения подпрограммы обработки элемента и интерпретировать его.

Отчет должен содержать:

1)распечатки файлов с программами и макросом,

2)распечатка окон с данными до и после выполнения программы,

3)окно стека с комментариями,

4)адрес подпрограммы обработки массива:

из листингов программ, из карты памяти, из отладчика.

5) размер кода и данных программы.

Задание. Дан массив слов (байтов) указанного размера. Над каждым элементом массива выполнить операцию: если битовое поле 6:4 совпадает с битовым полем 2:0 и с заданным образцом, то установить старший бит элемента, иначе — сбросить его.

18.4.2. Тексты программ

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

По условию задачи следует разместить программу и подпрограммы в разных файлах. Сначала напишем подпрограммы. Разместим их в файле sub2v0.asm. Сконструируем шаблон (рис.18.1) для слова и байта одновременно (можно, конечно, использовать и

несколько шаблонов).

 

 

 

 

 

 

 

 

 

 

 

 

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

b15

 

 

 

d2

 

 

 

b7

 

f6_4

 

d1

 

f2_0

 

Рис.18.1.

Фактически для нас представляют интерес только интерес только поля f6_4 и f2_0, из которых берутся исходные данные, и поля b15 и b7, которые (возможно) претерпевают изменения. Но приходится вводить названия и для неиспользуемых полей d1 и d2, в соответствии с синтаксисом описания RECORD.

sub2v0.asm

JUMPS

;

Оптимизация переходов

.286

;

Разрешены инструкции 286-го процессора

TypeByte EQU 1

TypeWord EQU 2

GLOBAL treat_array:PROC

.MODEL small

.CODE

;--------------------------------------------------------------------

;Подпрограмма обработки элемента массива

;Вход: AX(AL) — элемент массива

;

BP — образец

;

DX — тип элементов (1 - байты, 2 - слова)

; Выход: AX(AL) - измененный элемент массива

;--------------------------------------------------------------------

treat_elem PROC

template RECORD b15:1, d2:7, b7:1, f6_4:3, \ d1:1, f2_0:3

push ax bx

; Сохранить рабочие регистры

mov bx,ax

; Дублировать элемент в bx

and ax,MASK f6_4

; Выделить битовое поле 6:4

shr ax,f6_4

; Прижать его к правому краю

and bx,MASK f2_0

; Выделить битовое поле 2:0

cmp ax,bx

; Сравнить битовые поля

jne

clear

; Если не совпадают, сбросить старший бит

cmp

ax,bp

; Сравнить битовое поле с образцом

jne clear

; Если не совпадают, сбросить старший бит

; Установка старшего бита

 

 

pop bx ax

;

Восстановить регистры

cmp dx,TypeWord

; Если тип операнда слово –

je set_b15

;

на set_b15

or al, MASK b7

; Иначе — установить старший бит байта

jmp ret_elem

;

На выход

set_b15:

 

 

 

or ax, MASK b15

; Установить старший бит слова

jmp ret_elem

 

 

; Сброс старшего бита

 

 

clear: pop bx ax

 

 

cmp dx, TypeWord

 

je clear_b15

 

 

and al, NOT MASK b7

; Сбросить старший бит байта

jmp ret_elem

 

 

clear_b15:

 

 

 

and ax, NOT MASK b15

; Сбросить старший бит слова

ret_elem:

 

 

 

ret

 

 

 

treat_elem ENDP

 

 

;-------------------------------------------------------------------

;Подпрограмма обработки массива

;Вход: SI — адрес массива

;CX — количество элементов

;BP — образец

;DX — тип элементов (1 - байты, 2 - слова)

;Выход: CF = 1, если входной массив пуст

;CF = 0, если входной массив непуст

;Результат: измененный массив

;-------------------------------------------------------------------

treat_array PROC

 

jcxz error

;

Если массив нулевой длины — на error

m:

mov ax,[si]

;

Загрузить очередной элемент в AX (в AL)

 

call treat_elem

; Обработать элемент

 

mov [si],ax

;

Вернуть элемент в массив

add si,dx

; Переместить указатель на следующий элемент

loop m

 

clc

; Нормальное завершение

ret

 

error: stc; Сообщение об ошибке, если массив пустой ret

treat_array ENDP END

Обратим внимание на следующие моменты. 1) Мы сообщаем компилятору директивой

GLOBAL treat_array:PROC

что treat_array — это глобальное имя, т.е. оно будет использоваться программами, расположенными в других файлах. Спецификатор PROC означает, что это имя некоторой процедуры. Компилятор разместит это имя в таблице глобальных имен. Ей впоследствии воспользуется компоновщик.

2)Директива END не содержит метки стартового адреса, т.к. в файле нет главной программы — только подпрограммы.

3)Вместо того чтобы записывать команды

push ax push bx

мы записываем эти команды одной строкой. Такую возможность предоставляет Turbo

Assembler.

4)В начале каждой процедуры мы помещаем ее краткое описание и перечень входных

ивыходных параметров (если они есть). В нашем примере параметры передаются через регистры.

Файл с макросом для вызова подпрограммы treat_array. macr2v0.inc

TypeByte EQU 1

TypeWord EQU 2

;---------------------------------------------------------------------

;Макрос вызова подпрограммы обработки массива

;array — имя массива

;len_of_array — количество элементов массива

;pattern — образец

;error_label — метка для перехода по ошибке

;---------------------------------------------------------------------

treat MACRO array, len_of_array, pattern, error_label

TA = TYPE array

IF NOT (TA EQ TypeByte OR TA EQ TypeWord)

%OUT На входе должен быть массив слов или байтов! %OUT Массив array этому условию не удовлетворяет!

%OUT EXITM

ENDIF

IF pattern GT 111B

%OUT В образце pattern могут быть установлены %OUT только три младших бита.

%OUT EXITM

ENDIF

mov si, OFFSET array ;;

В SI — адрес массива

mov cx, len_of_array ;;

В CX — количество элементов

mov dx, TA

;;

В DX — тип элементов

mov bp, pattern

;;

В BP — образец

call treat_array

 

 

jc error_label

 

 

ENDM

 

 

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

Встроенная функция (времени ассемблирования) TYPE возвращает числовой код, соответствующий типу данных: 1 — для байтов, 2 — для слов, 4 — для двойных слов, остальные значения опустим.

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

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

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

Будем использовать образец, задаваемый как параметр в макросе, 101b. В нашем примере достаточно сконструировать массив всего из двух элементов: для одного

элемента условие выполняется, а для другого — нет.

 

 

 

Первый элемент (рис. 18.2):

 

 

 

 

 

 

 

7

6

5

4

3

 

2

1

0

 

1

1

0

1

0

 

1

0

1

 

 

 

 

 

 

 

=

 

 

 

 

 

установить

 

 

 

 

 

 

 

Рис. 18.2.

 

 

 

 

 

 

Второй элемент (рис.18.3):

 

 

 

 

 

 

 

 

 

7

 

6

5

4

3

2

1

0

 

 

1

 

1

 

1

1

0

1

0

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

сбросить

 

 

 

 

 

 

 

 

 

Рис. 18.3.

 

 

 

 

 

 

Итак, первый элемент 11010101 11010101, т.е. D5h

D5h, а второй элемент

11110101 01110101, т.е. F5h

75h. Аналогичный тест предложим для слов: 00D5h

80D5h, 00F5h 00F5h.

 

 

 

 

 

 

 

 

 

main2v0.asm

COMMENT &

0. Иванов И.И. Дан массив байтов (слов) указанного размера.

Над каждым элементом массива выполнить операцию: если битовое поле 6:4 совпадает с битовым полем 2:0 и с заданным образцом, то установить старший бит элемента, иначе — сбросить его.

&

JUMPS

;

Оптимизация переходов

.286

;

Разрешены инструкции 286-го процессора

INCLUDE

macro.inc

;

Включить общий файл макросов

INCLUDE macr2v0.inc ;

Включить специальный файл макросов

;

 

 

 

 

GLOBAL treat_array:PROC

.MODEL small

.STACK 100h

.DATA

arrb DB 0D5h,0F5h Len_of_arrb = $ - arrb arrw DW 00D5h,00F5h

Len_of_arrw = ($ - arrw) SHR 1 arrd DD 0FFFFFFFFh

Len_of_arrd = ($ - arrd) SHR 2 msg_err DB "Массив пуст!",CRLFT

.CODE

start:

mov ax,@data mov ds,ax

treat arrb, Len_of_arrb, 101B, error treat arrw, Len_of_arrw, 101B, error treat arrw, Len_of_arrw, 1101B, error treat arrd, Len_of_arrd, 10B, error exit

error: message msg_err exit 1

END start

Содержимое файла macro.inc описано ранее.

18.4.3. Выполнение задания на компьютере.

1) Ввод текстов программ. Можно воспользоваться файлами с излагаемым примером, как шаблоном.

2) Трансляция, компоновка, выполнение. Можно просто вручную набрать команды: c:\tasm\bin\tasm /zi/m/la main2v0

c:\tasm\bin\tasm/zi/m/la sub2v0 c:\tasm\bin\tlink/v/m main2v0 sub2v0, a2v0 c:\tasm\bin\td a2v0.exe

но эти команды наверняка придется набирать многократно, т.к. вряд ли у вас все получится с первого раза. Имеет смысл эти команды записать в командный файл. Но этот путь неэффективен вот по какой причине: предположим, мы уже отладили главную программу (она проще), зачем же каждый раз ее транслировать, хотя она не претерпевает изменений. А если при трансляции возникнет ошибка, то компоновку выполнять не нужно (вообще-то этой ситуации можно избежать, если в командный файл вставить проверку уровня ошибки ERRORLEVEL). Но лучшим решением является использование утилиты

MAKE.

Для того чтобы воспользоваться этой утилитой, представим наглядно в виде дерева (рис. 18.4) зависимость итогового файла a2v0.exe от исходных файлов:

a2v0.exe

 

 

 

 

 

 

 

 

 

main2v0.obj

 

sub2v0.obj

 

 

 

 

 

 

 

 

main2v0.asm

 

macr2v0.inc

 

sub2v0.asm

Рис. 18.4.

А теперь запишем это дерево в make-файле. Make-файл имеет следующую структуру:

цель: условие

Соседние файлы в папке Assembler