Отладка приложений

       

Манипуляции со строками


Intel CPU обладают большими возможностями для манипуляций со строками, т. е. поддерживают обработку больших участков памяти в отдельной инструкции. Все рассмотренные здесь строчные инструкции имеют несколько мнемоник, которые можно найти в справочных руководствах Intel. Однако окно Disassembly в Visual C++ всегда дизассемблирует строчные инструкции только в те формы, которые показаны ниже. Все эти инструкции могут работать на областях памяти размером в байт, слово и двойное слово.

  •  MOVS    перемещают данные из строки в строку

Инструкция MOVS перемещает адрес памяти из регистра ESI в регистр EDI. Эта инструкция работает только на значениях, на которые указывают ESI и EDI. Инструкцию MOVS можно представить себе как реализацию С-функции memcpy. Окно Disassembly из Visual C++ всегда показывает размер операции со спецификатором размера, так сразу видно, сколько памяти было перемещено. После того как перемещение заканчивается, регистры ESI и EDI инкрементируются или декрементируются, в зависимости от флажка направления (DF) в регистре EFLAGS (отображаемого как поле UP в окне Registers Visual C++). Если поле UP равно 0, то регистры инкрементируются, а если равно 1, регистры декрементируются. Величина инкремента и декремента зависит от размера памяти, с которой работает операция: 1 — для байт, 2 — для слов и 4 — для двойных слов.

  •  SCAS   сканировать строку

Инструкция SCAS сравнивает значение по адресу памяти, указанному в регистре EDI, со значением в регистрах AL, АХ или ЕАХ (в зависимости от требуемого размера). Результаты сравнения устанавливают значения различных флажков в регистре EFLAGS. Установки флажков — те же, что показаны в табл. 6.4. Если вы сканируете строку в поисках NULL-терминатора (пустого указателя), то инструкцию SCAS можно использовать, чтобы дублировать возможности С-функции strien. Подобно инструкции MOVS, SCAS выполняет автоинкремент или автодекремент регистра EDI.

  •  STOS   сохранить строку


Инструкция STOS сохраняет значение регистров AL, АХ или ЕАХ (в зависимости от требуемого размера) по адресу, указанному регистром EDI.
Инструкция STOS похожа на С- функцию memset. Подобно инструкциям MOVS и SCAS, инструкция STOS автоинкрементирует или автодекрементирует регистр EDI.

  •  CMPS   сравнить строки
Инструкция CMPS сравнивает два строчных значения и устанавливает соответствующие флажки в EFLAGS. Тогда как SCAS выполняет сравнения символов в единственной строке, CMPS проходит символы в двух строках. Инструкция CMPS похожа на С-функцию memcmp. Подобно остальным строчным манипуляторам, инструкция CMPS сравнивает значения различных размеров, а также выполняет автоинкремент и автодекремент указателей обеих строк.

  •  ВЕР           повторять по счетчику в EСХ
  •  REPE         повторять, пока равно или счетчик ЕСХ не станет равен 0
  •  REPNE      повторять, пока не равно или счетчик ЕСХ не станет равен 0
Строчные инструкции, хотя и удобны, но не много стоят, когда манипулируют элементом только один раз. Префиксы повторения позволяют выполнять строчные инструкции заданное (в ЕСХ) количество раз или пока не будет выполнено указанное условие. При пошаговом проходе окна Disassembly командой Step Into выполнение такой инструкции повторяется необходимое число раз. Если же при этом используется команда Step Over, то такая инструкция не выполняется повторно. При отладке можно с помощью команды Step Into проверять строки в регистрах ESI или EDI. Другой прием: при поиске аварийного останова в строчной инструкции с префиксом повторения нужно взглянуть на регистр ЕСХ, чтобы увидеть, на какой итерации случился останов.

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

void MemCPY ( char * szSrc , char * szDest , int iLen ) 

{

_asm 

{

MOV ESI , szSrc // Установить исходную строку. 



MOV EDI , szDest // Установить целевую строку ng.

 MOV ЕСХ , iLen // Установить длину копирования.

// Копировать немедленно! 

REP MOVS BYTE PTR [EDI] , BYTE PTR [ESI]

 } 

}

int StrLEN (char * szSrc ) 

{

int iReturn ; _asm 

{

XOR EAX , EAX // Обнулить ЕАХ.

MOV EDI , szSrc // Поместить проверяемую строку в EDI.

 MOV ECX , 0FFFFFFFFh // Максимальное число проверяемых

// символов.

 REPNE SCAS BYTE PTR [EDI] // Сравнивать, пока ЕСХ не станет

// равным 0 или не будет найден конец 

// строки .

СМР ЕСХ ,0 // Если ЕСХ равен 0, то

 JE StrLEN_NoNull // в строке не был найден NULL. 

NOT ECX // Преобразовать ЕСХ в положительное число,

// поскольку он был просчитан.

DEC ЕСХ ' // Подсчет для Попадания на NULL. 

MOV EAX , ЕСХ // Возврат счета. 

JMP StrLen_Done .// Возврат. StrLENJNoNull:

MOV EAX , OFFFFFFFFh // Поскольку NULL не был найден,

И возвратить -1. 

StrLEN_Done: 

}

_asm MOV iReturn , EAX ;

 return ( iReturn ) ; 

}

void MemSET ( char * szDest , irit iVal , int iLen ) 

{  _asm



MOV EAX , iVal // EAX содержит полное значение.

 MOV EDI , szDest // Переместить строку в EDI.

 MOV ECX , iLen // Переместить счет в ЕСХ. 

REP STOS BYTE PTR [EDI] // Заполнить память. 



}

int MemCMP ( char * szMeml , char * szMem2 , int iLen )

 {

int iReturn ;

_asm

{

MOV ESI , szMeml // ESI содержит первый блок памяти.

 MOV EDI , szMem2 // EDI содержит второй блок памяти.

 MOV ECX , iLen // Максимальное число байт для сравнения

// Сравнить блоки памяти. 

REPE CMPS BYTE PTR. 

[ESI], BYTE PTR [EDI] 

JL MemCMP_LessThan // Если szSrc < szDest 

JG MemCMP_GreaterThan // Если szSrc > szDest

// Блоки памяти равны.

XOR EAX', EAX // Возвратить 0.

 JMP MemCMP_Done

MemCMP_Les sThan:

MOV EAX , 0FFFFFFFFh // Возвратить -1.

 JMP MemCMP_Done

 MemCMP_GreaterThan:

 MOV EAX , 1 // Возвратить 1.

JMP MemCMP_Done

 XemCMP_Done: 

}

_asm MOV iReturn , 

EAX return ( iReturn ) ;

}



Содержание раздела