====== Строковые операции в ассемблере ====== Строковые операции называют цепочечными или операциями над цепочками байтов. **Строка**-это цепочка байтов, для которой известен алгоритм определения ее длины.В языках программирования прописывают процедуру определения длины строки, а также вводят ограничение на то, какие байты могут содержаться в строке, а также в какой кодировке должны интерпретироваться строки- сколько байтов кодирует символ, и какая таблица будет использоваться для сопоставления байтов и символов.В языке Си под строкой понимается последовательность байтов, заканчивающаяся байтом со значением 0. Строковые операции работают с одним элементом строки: байтом, словом, двойным словом.Для того чтобы команда манипулировала последовательностью таких элементов, используются команды-префиксы: //REP//-повторять, пока содержимое //ECX// не обратится в 0 //REPE/REPZ//-повторять пока равно/нуль. Данный префикс заставляет выполнять строковую команду до тех пор, пока содержимое //ECX// не обратится в 0 //REPNE/REPZ//-повторять пока не равно/не нуль. Данный префикс заставляет выполнять строковую команду до тех пор, пока содержимое //ECX// не обратится в 0 Строковые команды считают, что строка -источник находится по адресу DS:ESI(для нас это просто ESI),а строка-приемникпо адреса ES:EDI(для нас просто EDI). Все строковые команды можно разделить на шесть групп: 1. **Команды пересылки** //MOVSB// - копирование строки байтов. //MOVSW// - копирование строки слов. //MOVSD// - копирование строки двойных слов Возможен также формат MOVS приемник, источник-в этом случае ассемблер по типу операндов сам определяет, какую из трех форм команд следует выбрать. Кобанда копирует элемент строки из адреса, определяемого ESI, в адрес, определяемый EDI. После выполнения такой команды содержимое ESI и EDI увеличивается(флаг DF=0) или уменьшается(флаг DF=1) на размерах элемента строки 2. **Команды сравнения** //CMPSB// - сравнение строк байтов. //CMPSW// - сравнение строк слов. //CMPSD// - сравнение строк двойных слов 3. **Команды поиска(сканирования)** //SCASB// - сканирование строки байтов. //SCASW// - сканирование строки слов //SCASD// - сканирование строки двойных слов 4. **Команды чтения из строки** //LODSB// - чтение байта из строки. //LODSW// - чтение слова из строки. //LODSD// - чтение двойного слова из строки. Возможен также формат LODS источник-в этом случае ассемблер по типу операндов сам определяет, какую из трех форм команд следует выбрать. Команда осуществляет копирование из памяти, которая адресуется ESI, элемента в AL,AX или EAX. После выполнения такой команды содержимое EDI увеличивается на размер элемента строки 5. **Команда записи в строку** //STOSB// - запись байта в строку. //STOSW// - запись слова в строку. //STOSD// - запись двойного слова в строку. Возможен также формат STOS приемник, источник-в этом случае ассемблер по типу операндов сам определяет, какую из трех форм команд следует выбрать. Команда осуществляет копирование в память, которая адресуется EDI, элемента из AL,AX или EAX. После выполнения такой команды содержимое EDI увеличивается на размер элемента строки 6. **Команды чтения/записи строки из порта** При использовании префиксов (//REP, REPZ/REPE, REPNZ/REPNE//)действие команд распространяется на цепочки байтов. **Использование строковых команд** // string.cpp: определяет точку входа для консольного приложения. // #include "stdafx.h" #include //необходим для работы DWORD #include //необходим для работы printf #include //необходим для работы _getch() /*Объявление функции*/ DWORD lens (char *);//функция определения длины строки void cats(char*, char*,char*);//функция слияния двух строк в третью /*Объявление переменных*/ char a[]="fdhfjliop"; char b[]="12345"; char c[]="4"; int _tmain(int argc, _TCHAR* argv[]) { __asm{ /*мы собираемся вызывать процедуру cats,которая своими параметрами имеет 3 указателя на строку типа char(в порядке a,b,c). Чтобы процедура cats могла получить параметры, передаемые по значению, мы должны поместить их в стек(в порядке c,b,a), а затем выровнять стек */ LEA EAX,c;//помещаем в регистр EAX адрес первого элемента строки c PUSH EAX;//помещаем содержимое EAX в стек LEA EAX,b;//помещаем в регистр EAX адрес первого элемента строки b PUSH EAX;//помещаем содержимое EAX в стек LEA EAX,a;//помещаем в регистр EAX адрес первого элемента строки a PUSH EAX;//помещаем содержимое EAX в стек CALL cats;//вызываем процедуру ADD ESP,12;// выравниваем стек }; printf("%s\n",c);//выводим суммарную строку _getch(); return 0; } //функция определения длины строки DWORD lens (char * s) { DWORD l=0; __asm { CLD;//задаем направление сканирования (сбрасываем флаг DF-флаг направления). //данный флаг учитывается в строковых операциях.Если флаг равен 1, то в строковых операциях //адрес автоматически уменьшается.Мы сбрасываем этот флаг,чтобы адрес автоматически увеличивался MOV EDI,s;// помещаем адрес начала строки в регистр EDI MOV ESI,EDI;//сохраняем адрес строки в регистре ESI MOV ECX, 0ffffffffh;//помещаем в регистр-счетчик цикла максимальное 32-битное целое число XOR AL,AL;//запускаем бесконечный цикл-операция XOR от двух одинаковых элементов возвращает 0 //таким образом,эта команда представляет с собой "пока 0 ..." REPNE SCASB;//сканируем строку байтов, пока не встретится 0 SUB EDI,ESI;//вычитаем из текущего адреса, который хранится в EDI, адрес начала строки(хранится в ESI ), //таким образом находим длину строки вместе с терминальным символом DEC EDI;//вычитаем из полученной длины строки 1- исключаем терминальный символ MOV l,EDI;//помещаем в переменную l на длину строки } return l;//возвращаем длину строки } //Функция слияния строк s1+s2->s3 void cats(char* s1, char* s2,char* s3) { __asm{ /*определяем длину строки s1*/ CLD; //задаем направление копирования MOV ESI,s1;// помещаем в регистр ESI указатель на начало строки s1 PUSH ESI;//помещаем указатель на s1 в стек CALL lens;//вызываем функцию определения длины строки ADD ESP,4;//выравниваем стек /*копируем содержимое строки s1 в строку s3*/ /*функция lens возвращает результат в регистр EAX*/ MOV ECX, EAX;//помещаем длину строки s1 в регистр ECX MOV ESI,s1;//помещаем в регистр ESI указатель на строку s1 MOV EDI,s3;//помещаем в регистр EDI указатель на строку s3 REP MOVSB;//повторяем, пока содержимое ECX не обратится в 0, побайтовое копирование строки , //адрес начала которой хранится в ESI, в строку адрес начала которой хранится в EDI //(копируем содержимое строки s1 в строку s3) /*копируем содержимое строки s2 в строку s3 начиная с позиции после s1*/ MOV ESI,s2;//помещаем в ESI указатель на первый элемент строки s2 PUSH ESI;//помещаем указатель на s2 в стек CALL lens;//вызываем функцию определения длины строки ADD ESP,4;//выравниваем стек /*функция lens возвращает результат в регистр EAX*/ MOV ECX,EAX;//помещаем длину строки s21 в регистр ECX REP MOVSB;//повторяем, пока содержимое ECX не обратится в 0, побайтовое копирование строки , //адрес начала которой хранится в ESI(адрес начала строки s2), в строку адрес начала которой хранится в EDI //(адрес конца строки s1 +1) //(копируем содержимое строки s2 в строку s3) MOV BYTE PTR [EDI],0;//фиксируем конец строки } } \ Назад: [[workroom:inb31-2013:comp:index:lab12]] {{tag>}}