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

Assembler / P18

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

команда

команда

Эти строки могут повторяться многократно.

Цели и условия — списки имен файлов, разделенные пробелами. Команда — это любая команда DOS. Цель обязательно начинается с первой позиции, а команда — с отступом хотя бы на одну позицию.

файл a2v0.mak

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

main2v0.obj: main2v0.asm macr2v0.inc c:\tasm\bin\tasm /zi/m/la main2v0

sub2v0.obj: sub2v0.asm c:\tasm\bin\tasm /zi/m/la sub2v0

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

Перед ключами (опциями) команд надо обязательно вставлять пробел. Запуск файла на выполнение осуществляется по команде

c:\tasm\bin\make -fa2v0.mak (после ключа -f указывается имя make-

файла)

Make обрабатывает файл так. Поочередно просматриваются все зависимости. Для каждой зависимости сравниваются даты последнего изменения целей и условий. Если дата/время последнего изменения одного из файлов-условий больше, чем у любой из целей, или если файл-цель отсутствует, make вызывает все команды, указанные в зависимости.

Возможности этой утилиты намного больше, чем здесь показано. Рекомендуется make запускать так:

c:\tasm\bin\make -f2v0.mak > a2v0.txt

Тогда вы прочтете отчет о работе make и сообщения запускаемых программ в файле a2v0.txt.

В результате выполнения make-файла на диске помимо исходных файлов

(main2v0.asm, sub2v0.asm, macr2v0.asm, macro.inc) появляются файлы:

main2v0.obj, sub2v0.obj;

main2v0.lst, sub2v0.lst;

a2v0.exe;

a2v0.map.

3) Подготовка отчета.

После запуска Turbo Debugger перед вами два окна Module (1) и Watch (2). Они нам не понадобятся, но закрывать их не будем (хотя, если возникают проблемы с памятью, лишние окна рекомендуется закрывать). Перечислим действия пользователя в отладчике в предположении, что программа работает правильно.

1) Запишем адрес процедуры treat_array в окно протокола. Сейчас активно первое окно (текст главной программы). Выведем текст модуля с подпрограммами. Для этого нажмем F3 (Module) и возникшем меню выберем имя файла с подпрограммами. Подведем курсор в строку treat_array PROC и нажимаем Ctrl+I (Alt+F10/Inspect). Появляется окно, в

котором читаем адрес процедуры. Копируем содержимое окна в окно протокола. Alt+W,D (F10/Window/Dump Pane to Log). Замечание: если вы используете более позднюю версию

TD, то F10/Edit/Dump Pane to Log.

2)Создадим окно CPU. Alt+V,C,F5,Ctrl+M,Ctrl+M (эквивалент при использовании меню: F10/View/CPU, F10/Window/Zoom, Alt+F10/Mixed/Both).

3)Выполним команды загрузки DS (mov ax,@data / mov ds,ax): F7,F7.

4)В панели данных покажем массив arrb. Shift+Tab, Ctrl+G (Alt+F10/Goto). Набираем имя arrb. В панели данных отображается

ds:0000 D5 F5 D5 00 F5 00 …

5)Скопируем содержимое панели в окно протокола. Alt+W,D (F10/ Window/ Dump Pane to Log).

6)В окне данных покажем массив arrw. Alt+V,D (F10/View/Dump). Получаем 4-ое окно. Ctrl+G (Alt+F10/Goto..). Набираем имя arrw. Отобразим как слова. Ctrl+D,W

(Alt+F10/Display as…/Word) . В окне данных отображается ds:0002 00D5 00F5 …

Скопируем в окно протокола Alt+W,D.

7)Войдем в процедуру treat_elem и скопируем стек. Перейдем в окно CPU (Alt+3, т.е.

вокно 3; либо можно нажимать F6 пока не окажетесь в нужном окне). Перейдем в панель кода: Tab. Многократным нажатием на F7 попадаем в процедуру treat_elem (в ней выполним команды push ax, push bx). Переходим в панель стека: Tab,Tab,Tab. Скопируем в окно протокола: Alt+W,D. Вернемся в панель кода (необязательно): Tab,Tab.

8)Выполним программу и скопируем результат в окно протокола. Выполнить до конца: F9 (F10/Run/Run). Получим окно с сообщением: Terminated, exit code 0. Нажимаем

Enter. Переходим в панель данных: Shift+Tab. Скопируем в окно протокола: Alt+W,D. Перейдем в 4-ое окно: Alt+4 (F6,F6,F6). Скопируем в окно протокола: Alt+W,D.

9)Сохраним протокол в файле. Перейдем в окно протокола: Alt+V,L (F10/View/Log). Распахнем окно: F5 (F10/Window/Zoom). Откроем файл протокола: Ctrl+O (Alt+F10/Open log file …). Запрашивается имя файла (по умолчанию предлагается a2v0.log) —

подтверждаем. Закрываем файл: Ctrl+C (Alt+F10/Close log file).

10)Завершаем работу в TD. Alt+X (F10/File/Quit).

В текстовом редакторе правим файл a2v0.log, распечатываем его и снабжаем комментариями.

Turbo Debugger Log Inspecting treat_array @5571:006E

CPU 80286 ds:0000 D5 F5

Dump

ds:0002 00D5 00F5 CPU 80286

ss:0100

ss:00FE 0014 - адрес команды jc error (после call treat_array) ss:00FC 0079 - адрес команды mov [si],ax (после call treat_elem) ss:00FA F5D5 - содержимое AX

ss:00F8 0000 - содержимое BX

Terminated, exit code 0

CPU 80286 6356:0000 D5 75

Dump

6356:0002 80D5 00F5

Справа от исходных данных и результата можно вставить комментарий, поясняющий их (те же данные в двоичном представлении, с выделенными полями и т.д.). Что касается стека, желательно, прокручивая окно CPU, найти адреса команд, помещенные в стек.

Адрес программы обработки массива treat_array вы ищете в таблице имен листингов main2v0.lst, sub2v0.lst, а также в карте памяти a2v0.map. Адрес в отладчике мы уже

записали в окно Log. Наконец, размер кода и данных программы вы найдете в карте памяти a2v0.map. В отчет нужно включать строчки из этих файлов, вырезанные с помощью текстового редактора.

18.5. Несколько задач.

Задача. Поместить в регистр BX старшее слово регистра EAX 1-е решение

 

mov

cx,16 ; размер слова

m:

rol

eax,1

 

rcl

bx,1

 

loop

m

 

rol

eax,16 ; восстановить eax

2-е решение

ror eax, 16 mov bx, ax

ror eax, 16 ; восстановить eax

3-е решение

 

mov

ebx, eax

ror

ebx, 16

(недостаток: портится старшее слово EBX)

Позднее мы увидим еще одно решение этой задачи: с использованием команды двойного сдвига.

Задача: сосчитать количество единичных битов в eax xor bx,bx ; Счетчик единичных битов mov cx,32

n:rol eax,1 adc bx,0 loop n

Задача. Расположить содержимое AX в регистре BX в обратном порядке.

 

mov

cx, 16

n:

rol

ax,1

 

rcr

bx,1

 

loop

n

18.6. Битовые операции, появившиеся в 386-м процессоре

18.6.1. Команды сканирования битов bsf и bsr.

Сканирование бита вперед

bsf dst,src

в dst — индекс первого справа

 

 

единичного бита в src

Bit Scan Forward

 

ZF = 1, если все биты src нулевые

 

 

ZF = 0, если в src есть единичный бит

форматы операндов: bsf r16, r/m16, bsf r32, r/m32

 

 

 

Сканирование бита назад

bsr dst,src

в dst — индекс первого слева

 

 

единичного бита в src

Bit Scan Reverse

 

ZF = 1, если все биты src нулевые

 

 

ZF = 0, если в src есть единичный бит

форматы операндов: bsr r16, r/m16,

bsf r32, r/m32

Итак, "вперед" — в направлении увеличения нумерации битов, "назад" — в направлении уменьшения. Индекс — номер разряда (бита).

Пример. Выяснить индекс самого старшего установленного бита в регистре DX (пусть DX содержит число 5).

bsr

ax, dx ; ax = 2

Пример. Сдвинуть содержимое регистра EBX вправо так, чтобы младший бит EBX был

установлен;

если это

невозможно, перейти на метку null (Пусть EBX = 84h =

0…0 1000

0100b.)

 

bsf

 

ecx, ebx

; ecx = 2

jz

 

null

 

shr

ebx, cl

; ebx = 21h = 0…0 0010 0001b

18.6.2. Команды проверки битов bt, bts, btr, btc

Сначала разберем команду bt, остальные команды на нее похожи.

Проверка бита

 

bt src, index

сохраняет бит из src с номером index в CF

Bit Test

 

 

 

CF = 1, если бит src установлен,

 

 

 

 

CF = 0, если бит src сброшен

форматы операндов: bt r/m16, r16, bt r/m32, r32, bt r/m16, i8, bt r/m32, i8

Пример. Поместить второй бит регистра CX в флаг CF.

mov

cx, 5

;cx = 101b

bt

cx, 2

;CF = 1

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

AX.

 

xor

cx, cx

 

xor

bx, bx

n:

bt

ax, cx

 

adc

bx, 0

 

inc

cx

 

cmp

cx, 16

 

jnae

n

Для оставшихся трех команд не будем приводить подробного описания — оно вполне аналогично команде bt.

btc — проверка и инвертирование (bit test and complement) — бит помещается в CF, сам бит инвертируется.

btr — проверка и сброс (bit test and reset) — бит помещается в CF, сам бит сбрасывается. bts — проверка и установка (bit test and set) — бит помещается в CF, сам бит устанавливается.

Упражнение. Придумайте примеры для каждой из приведенных команд и проверьте их работу в TD.

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

Двойной сдвиг влево

shld dst,src, count

описание приведено ниже

Double precision Shift Left

SF, ZF, PF по результату;

в CF последний выдвинутый бит; OF = 1, если произошло изменение знака (имеет смысл при count = 1)

форматы операндов: shld r/m16, r16, i8/cl shld r/m32, r32, i8/cl

dst и src объединяются и происходит сдвиг влево на count бит объединенного содержимого. src остается без изменений.

 

CF

 

 

 

 

 

 

 

dst

 

 

 

 

 

src

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Аналогично работает команда двойного сдвига вправо shrd dst,src, count.

Пример. Поместить в регистр BX старшее слово регистра EAX shld ebx,eax,16

если нужно, чтобы старшее слово EBX при этом не изменялось, то shr ebx,16

shld ebx,eax,16

Пример. Пусть array — массив двойных слов. Осуществить циклический сдвиг всего массива влево на 12 бит

 

.MODEL small

 

.386

 

 

.STACK 100h

 

.DATA

 

array

DD 8 DUP(12345678h)

LEN_ARRAY = ($-array) SHR 2

 

.CODE

start: mov

ax, @data

 

mov

ds, ax

 

mov

edx, array

 

xor

esi, esi

 

mov

ecx, LEN_ARRAY - 1

next:

mov

eax, array+4[esi*4]

 

shld

array[esi*4], eax, 12

 

inc

esi

 

loop

next

 

shld

array[esi*4], edx, 12

 

mov

ax, 4C00h

 

int

21h

 

END

start

В отладчике мы видим, что теперь элементами массива являются числа 4568123h.

Команда двойного сдвига применяется для быстрого перемещения строки битов на новое место в памяти. (В англоязычной литературе используется сокращение "bit blt" (BIT Block Transfer — перемещение блока бит).

Пример. Вставить битовую подстроку длиной 16 бит в строку битов, начиная с 8-го бита (т.е. заменить биты 23:8 заданной строкой из 16 бит). Вставлямая подстрока выровнена к левому краю. [Юров, с изменениями]

MODEL small

 

 

.386

 

 

 

.STACK 100h

 

 

.DATA

 

 

 

bit_str DD

12345678h

;

строка-приемник

p_str DD

0ABCD0000h

;

вставляемая подстрока 0ABCDh

.CODE

 

 

start: mov

ax, @data

 

 

mov

ds, ax

 

 

mov

eax, p_str

: Поместить подстроку в EAX

;Правый край места вставки циклически переместить к краю

;строки bit_str (т.е. слева поместить правые биты)

ror bit_str,8 ; (bit_str = 78123456)

; Сдвинуть строку вправо на длину подстроки shr bit_str,16 ; (bit_str = 00007812)

; Поместить подстроку в строку-приемник (16 – размер подстроки) shld bit_str,eax,16 : (bit_str = 7812ABCD)

; Восстановить младшие 8 бит (вернуть правые биты на место) rol bit_str,8 : (bit_str=12ABCD78)

mov ax,4C00h int 21h

END start

Задача. Переписать эту программу с использованием битовых полей (константы 8 и 16 должны вычисляться на этапе ассемблирования, а bit_str и p_str должны определяться посредством записей).

Задача. С помощью команды двойного сдвига реализовать противоположную задачу: извлечь подстроку 23:8 и поместить ее в EAX, выровняв по правому краю.

18c. Битовые операции, появившиеся в 386-м процессоре

18c.1. Команды сканирования битов bsf и bsr.

Сканирование бита вперед

bsf dst,src

в dst — индекс первого справа

 

 

 

 

единичного бита в src

Bit Scan Forward

 

 

 

ZF = 1, если все биты src нулевые

 

 

 

 

ZF = 0, если в src есть единичный бит

форматы операндов: bsf

r16, r/m16,

bsf r32, r/m32

 

 

 

 

Сканирование бита назад

 

bsr dst,src

в dst — индекс первого слева

 

 

 

 

единичного бита в src

Bit Scan Reverse

 

 

 

ZF = 1, если все биты src нулевые

 

 

 

 

ZF = 0, если в src есть единичный бит

форматы операндов: bsr

r16, r/m16,

bsf r32, r/m32

Итак, "вперед" — в направлении увеличения нумерации битов, "назад" — в направлении уменьшения. Индекс — номер разряда (бита).

Пример. Выяснить индекс самого старшего установленного бита в регистре DX (пусть DX содержит число 5).

bsr

ax, dx ; ax = 2

Пример. Сдвинуть содержимое регистра EBX вправо так, чтобы младший бит EBX был

установлен;

если это

невозможно, перейти на метку null (Пусть EBX = 84h =

0…0 1000

0100b.)

 

bsf

 

ecx, ebx

; ecx = 2

jz

 

null

 

shr

ebx, cl

; ebx = 21h = 0…0 0010 0001b

18c.2. Команды проверки битов bt, bts, btr, btc

Сначала разберем команду bt, остальные команды на нее похожи.

Проверка бита

 

bt src, index

сохраняет бит из src с номером index в CF

Bit Test

 

 

 

CF = 1, если бит src установлен,

 

 

 

 

CF = 0, если бит src сброшен

форматы операндов: bt r/m16, r16, bt r/m32, r32, bt r/m16, i8, bt r/m32, i8

Пример. Поместить второй бит регистра CX в флаг CF.

mov

cx, 5

;cx = 101b

bt

cx, 2

;CF = 1

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

AX.

 

xor

cx, cx

 

xor

bx, bx

n:

bt

ax, cx

 

adc

bx, 0

 

inc

cx

 

cmp

cx, 16

 

jnae

n

Для оставшихся трех команд не будем приводить подробного описания — оно вполне аналогично команде bt.

btc — проверка и инвертирование (bit test and complement) — бит помещается в CF, сам бит инвертируется.

btr — проверка и сброс (bit test and reset) — бит помещается в CF, сам бит сбрасывается. bts — проверка и установка (bit test and set) — бит помещается в CF, сам бит устанавливается.

Упражнение. Придумайте примеры для каждой из приведенных команд и проверьте их работу в TD.

18c.3. Команды двойного сдвига

Двойной сдвиг влево

shld dst,src, count

описание приведено ниже

Double precision Shift Left

SF, ZF, PF по результату;

 

 

в CF последний выдвинутый бит;

 

 

OF = 1, если произошло

 

 

изменение знака (имеет смысл

 

 

при count = 1)

форматы операндов: shld r/m16, r16, i8/cl shld r/m32, r32, i8/cl

dst и src объединяются и происходит сдвиг влево на count бит объединенного содержимого. src остается без изменений.

 

CF

 

 

 

 

 

 

 

dst

 

 

 

 

 

src

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Аналогично работает команда двойного сдвига вправо shrd dst,src, count.

Пример. Поместить в регистр BX старшее слово регистра EAX shld ebx,eax,16

если нужно, чтобы старшее слово EBX при этом не изменялось, то shr ebx,16

shld ebx,eax,16

Пример. Пусть array — массив двойных слов. Осуществить циклический сдвиг всего массива влево на 12 бит

.MODEL small

.386

.STACK 100h

.DATA

array DD 8 DUP(12345678h) LEN_ARRAY = ($-array) SHR 2

.CODE

start: mov

ax, @data

mov

ds, ax

mov

edx, array

xor

esi, esi

mov

ecx, LEN_ARRAY - 1

next: mov

eax, array+4[esi*4]

shld

array[esi*4], eax, 12

inc

esi

loop

next

shld

array[esi*4], edx, 12

mov

ax, 4C00h

int

21h

END

start

В отладчике мы видим, что теперь элементами массива являются числа 4568123h.

Команда двойного сдвига применяется для быстрого перемещения строки битов на новое место в памяти. (В англоязычной литературе используется сокращение "bit blt" (BIT Block Transfer — перемещение блока бит).

Пример. Вставить битовую подстроку длиной 16 бит в строку битов, начиная с 8-го бита (т.е. заменить биты 23:8 заданной строкой из 16 бит). Вставлямая подстрока выровнена к левому краю. [Юров, с изменениями]

MODEL small

.386

.STACK 100h

.DATA

bit_str DD 12345678h ; строка-приемник

p_str DD

0ABCD0000h

; вставляемая подстрока 0ABCDh

.CODE

 

start: mov

ax, @data

 

mov

ds, ax

 

mov

eax, p_str

: Поместить подстроку в EAX

;Правый край места вставки циклически переместить к краю

;строки bit_str (т.е. слева поместить правые биты)

ror bit_str,8 ; (bit_str = 78123456)

; Сдвинуть строку вправо на длину подстроки shr bit_str,16 ; (bit_str = 00007812)

; Поместить подстроку в строку-приемник (16 – размер подстроки) shld bit_str,eax,16 : (bit_str = 7812ABCD)

; Восстановить младшие 8 бит (вернуть правые биты на место) rol bit_str,8 : (bit_str=12ABCD78)

mov ax,4C00h int 21h

END start

Задача. Переписать эту программу с использованием битовых полей (константы 8 и 16 должны вычисляться на этапе ассемблирования, а bit_str и p_str должны определяться посредством записей).

Задача. С помощью команды двойного сдвига реализовать противоположную задачу: извлечь подстроку 23:8 и поместить ее в EAX, выровняв по правому краю.

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