Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции / GL16.doc
Скачиваний:
17
Добавлен:
20.05.2014
Размер:
103.94 Кб
Скачать

Xor ax, ax

rep stosw

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

16.5. Неожиданное применение строковых команд — определение типа процессора.

Бывают случаи, когда программа должна определить тип процессора, на котором она работает (ну, например, программы диагностики оборудования). Тип процессора можно определить только по косвенным признакам. Серия программ, различающих процессоры (от 8086 до Pentium) приведена в книге Правиков "Ключевые дискеты". Эти программы, как правило, анализируют новые флаги, появляющиеся в регистре флагов с увеличением номера процессора. ВPentiumвоявилась командаCPUID, с возможностями которой мы ознакомимся позже, и необходимость в специальных программах отпала.

Рассмотрим задачу различения процессоров 8086 и 8088 (хотя сейчас это имеет лишь исторический интерес). Система команд у процессоров одна и та же. Одинаков формат регистра Flags(на различиях в этом регистре основаны программы диагностики для старших моделей процессоров). Измерять быстродействие обращения к внешней шине (у 8086 она 16-разрядная, у 8088 — 8-разрядная) — дело безнадежное. Но можно воспользоваться косвенным признаком — длиной очереди команд. Очередь команд в шинном интерфейсе занимает у 8086 — 6 байтов, а у 8088 — 4 байта. Идея алгоритма: программа изменяет свой код одной командойrep stosb. При этом код команд, находящихся в очереди, изменить невозможно.

...

.DATA

lenDB?, '$' ; строка для вывода длины конвейера

Inc_bxDb43h; код командыincbx,

; получен с помощью debug

.CODE

start: mov ax, @data

movds,ax

Xorbx,bx; вbx- длина очереди

cld

mov di, OFFSET cs:_nop

movax,cs

moves,ax; НастроимESна кодовый сегмент

mov al, inc_bx

movcx,6 ; попытаемся изменить 6 байтов

cli; запрет внешних прерываний

repstosb

_nop: ; Здесь поместим 6 командnop

REPT6

nop

ENDM

sti; разрешить прерывания

sub6,bx; Получить длину очереди

addbx, '0' ; Сформировать код цифры

mov len, bl

message len

...

При запуске программы на 8088 на экран будет выведено число 4 — длина очереди, т.к. команды после метки _nopприняли вид

nop

nop

nop

nop

inc bx

inc bx

очередь

Команды, находящиеся в очереди, не претерпели изменений, а команды, оставшиеся в ОЗУ, — изменились. Если выполнить эту программу на 8086 (и выше), то будет выведена длина очереди 6 (хотя на 80386 длина очереди составляет 15 байт).

Любопытно, что если запустить эту программу под управлением отладчика, то программа выведет на экран длину очереди 0! Дело в том, что в очереди теперь не команды программы, а команды отладчика.

Это наводит на мысль, что можно создавать программы, защищенные от трассировки отладчиком.

Пример. Фроловы БСП т.1 кн. 3

.DATA

sDB"Программа работает под управлением отладчика!",CRLFT

...

cli

call Test

sti

...

Test PROC near

mov byte ptr next, 90h ; код команды nop

next: ret

message s

ret

TestENDP

16.6. Команды сравнения

Эти две команды осуществляют сравнение элементов цепочек, не меняя операндов. Удобство их применения — в автоматическом переходе к следующему элементу цепочки

Сравнить элементы строк

cmps src,dst

(cmpsb, cmpsw)

(SI)–(DI)

модифицировать SIиDI

(CoMPareStrings)

флаги сост. изменяются

Сканировать элемент строки

scas dst

(scasb, scasw)

ac–(DI)

модифицировать DI

(SCAnString)

флаги сост. изменяются

По результатам этих операций выставляются флаги, которые анализируются последующими командами. (Для процессора 386 добавляются команды cmpsdиscasd).

Пример. Заменить каждый элемент байтового массива его абсолютной величиной (в предположении, что в массиве нет элемента 80h).

.DATA

m DB 1,-2,3,2,1,-3

len_m = $ - m

.CODE

start:

mov ax, @data

mov ds, ax

mov es, ax

cld

mov al,0

mov cx, len_m

mov di, OFFSET m

p:scasb; из 0 вычитаем элемент массива

jln; если результат отрицательный

; то исходный элемент положительный

negbyteptr[di-1] ; меняем знак у отрицательного

; с учетом того, что указатель

; уже перемещен на следующий

; элемент

n:loopp

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

Повторять примитив пока равно (нуль)

repe/repz примитив

CXCX-1 пока (CX0 иZF= 1)

(REPetition if Equal/Zero)

Повторять примитив пока не равно (не нуль)

repne/repnz примитив

CXCX-1 пока (CX0 иZF= 0)

(REPetition if Not Equl or Zero)

Пример. Определение длины строки.

string DB "abc", 0

mov ax, @data

mov es, ax

cld

mov al,0

mov cx, -1

mov di, OFFSET string

repne scasb

not cx

deccx

Проследим выполнение этого фрагмента, начиная с команды repne scasb.

a

b

c

0

CX = FFFFh

CX = FFFEh

CX = FFFDh

CX = FFFCh

CX=FFFBh

DIв начале

DIв конце

На схеме показано начальное и конечное положение указателя DI(в конечном положенииDIсодержит адрес байта, следующего за нулевым). Показано также текущее значение счетчикаCX. Командаnot cxинвертирует битыCX. В результатеCX= 4. Командаdec cxкорректирует это значение. В результате вCXдлина строки (без учета терминатора строки — нулевого байта).

Пример. В двух строках одинаковой длины сосчитать количество несовпадающих элементов в позициях с одинаковыми номерами.

.MODEL small

.STACK 100h

.DATA

s1 DB "abcdesss"

len_s1 = $ - s1

s2 DB "abcdfsss"

.CODE

start:

mov ax,@data

mov ds,ax

mov es,ax

cld

mov cx, len_s1

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