Blog. Just Blog
Несколько полезных функций на Ассемблере
За время программирования на Ассемблере у меня накопилось несколько полезных решений. Выделять под каждое них отдельную статью не хочется, а держать под рукой пригодится. Поэтому все сложу сюда, по мере надобности буду пополнять.Начну с функции очень быстрого копирования одного участка памяти в другой. Фишка в том, что основная часть строки копируется по 4 байта DWORD'ами, а остаток дописывается побайтно. Это дает нехилый выигрыш в скорости.
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------
- ; Функция быстрого копирования участка памяти
- ;-----------------------------------------------------
- ; lpDst - указатель на приемник
- ; lpSrc - указатель на источник
- ; dSize - размер копируемого блока
- ;-----------------------------------------------------
- proc _memcopy lpDst:DWORD, lpSrc:DWORD, dSize:DWORD
- pusha
- ; Установить указатели на источник и приемник
- cld
- mov edi,[lpDst]
- mov esi,[lpSrc]
- mov ecx,[dSize]
- push ecx
- ; Разделить на 4 и получить длину в DWORD
- shr ecx,2
- ; Скопировать основную часть строки DWORD'ами
- rep movsd
- pop ecx
- ; Получить остаток от деления на 4
- and ecx,3
- ; Скопировать остаток строки байтами
- rep movsb
- popa
- ret
- endp
Еще один вариант копирования. Он более красивый по реализации, но менее эффективный по производительности за счет использования переходов и, соответственно, невозможности использования конвейера команд процессора.
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------
- ; Функция быстрого копирования участка памяти
- ;-----------------------------------------------------
- ; lpDst - указатель на приемник
- ; lpSrc - указатель на источник
- ; dSize - размер копируемого блока
- ;-----------------------------------------------------
- proc _memcopy lpDst:DWORD, lpSrc:DWORD, dSize:DWORD
- pusha
- ; Установить указатели на источник и приемник
- cld
- mov edi,[lpDst]
- mov esi,[lpSrc]
- mov ecx,[dSize]
- ; Если есть остаток от деления на 2
- shr ecx,1
- jnc @f
- ; Скопировать один байт
- movsb
- @@:
- ; Если есть остаток от деления на 4
- shr ecx,1
- jnc @f
- ; Скопировать слово
- movsw
- @@:
- ; Остаток строки скопировать двойными словами
- rep movsd
- popa
- ret
- endp
Аналог функции lstrlen - получение длины строки. На входе адрес строки в формате ASCIIZ, на выходе EAX - длина строки.
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------
- ; Функция получения длины строки
- ;-----------------------------------------------------
- ; lpStr - указатель на строку ASCIIZ
- ; На выходе: EAX - длина строки без учета завершающего
- ; нулевого байта
- ;-----------------------------------------------------
- proc _lstrlen lpStr:DWORD
- push edi ecx
- cld
- mov edi,[lpStr]
- xor ecx,ecx
- dec ecx
- xor eax,eax
- repne scasb
- not ecx
- dec ecx
- mov eax,ecx
- pop ecx edi
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------
- ; Функция получения длины строки (Fast)
- ;-----------------------------------------------------
- ; lpStr - указатель на строку ASCIIZ
- ; На выходе: EAX - длина строки без учета завершающего
- ; нулевого байта
- ;-----------------------------------------------------
- proc _lstrlen lpStr:DWORD
- mov eax, [lpStr]
- sub eax, 4
- @@:
- add eax, 4
- cmp byte [eax], 0
- je .szlen_lb1
- cmp byte [eax+1], 0
- je .szlen_lb2
- cmp byte [eax+2], 0
- je .szlen_lb3
- cmp byte [eax+3], 0
- jne @b
- sub eax, [lpStr]
- add eax, 3
- ret
- .szlen_lb3:
- sub eax, [lpStr]
- add eax, 2
- ret
- .szlen_lb2:
- sub eax, [lpStr]
- add eax, 1
- ret
- .szlen_lb1:
- sub eax, [lpStr]
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------
- ; Функция быстрого копирования строки
- ; используются функции _lstrlen, _memcopy
- ;-----------------------------------------------------
- ; lpDst - указатель на приемник
- ; lpSrc - указатель на строку ASCIIZ
- ;-----------------------------------------------------
- proc _lstrcpy lpDst:DWORD, lpSrc:DWORD
- pusha
- stdcall _lstrlen,[lpSrc]
- inc eax
- stdcall _memcopy,[lpDst],[lpSrc],eax
- popa
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------
- ; Функция быстрого слияния двух строк
- ; используются функции _lstrlen, _lstrcpy
- ;-----------------------------------------------------
- ; lpDst - указатель на исходную строку ASCIIZ
- ; lpSrc - указатель на добавляемую строку ASCIIZ
- ;-----------------------------------------------------
- proc _lstrcat lpDst:DWORD, lpSrc:DWORD
- pusha
- stdcall _lstrlen,[lpDst]
- add eax,[lpDst]
- stdcall _lstrcpy,eax,[lpSrc]
- popa
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------
- ; Функция быстрого сравнения двух строк
- ; используется функция _lstrlen
- ;-----------------------------------------------------
- ; lpStr1 - указатель на первую строку ASCIIZ
- ; lpStr2 - указатель на вторую строку ASCIIZ
- ; На выходе: EAX=0 - строки совпадают
- ; EAX=1 - строки различаются
- ;-----------------------------------------------------
- proc _lstrcmp lpStr1:DWORD, lpStr2:DWORD
- push ecx esi edi
- mov esi,[lpStr1]
- mov edi,[lpStr2]
- stdcall _lstrlen,esi
- mov ecx,eax
- stdcall _lstrlen,edi
- cmp ecx,eax
- ; Длина строк не совпадает
- jnz @f
- cld
- repe cmpsb
- @@:
- setnz al
- movsx eax,al
- pop edi esi ecx
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------
- ; Функция быстрого сравнения двух строк
- ;-----------------------------------------------------
- ; lpStr1 - указатель на первую строку ASCIIZ
- ; lpStr2 - указатель на вторую строку ASCIIZ
- ; На выходе: EAX=0 - строки совпадают
- ; EAX=1 - строки различаются
- ;-----------------------------------------------------
- proc _lstrcmp lpStr1:DWORD,lpStr2:DWORD
- push esi edi
- mov esi,[lpStr1] ; Указатели на строки
- mov edi,[lpStr2]
- xor eax,eax ; Предположим, что строки равны
- @@:
- lodsb ; Сравнить следующие символы
- mov ah,[edi]
- inc edi
- or al,al ; Первая строка закончилась?
- jz @f ; Да
- cmp al,ah ; Символы совпадают?
- je @b ; Да, проверить следующий символ
- @@:
- or ah,ah ; Вторая строка закончилась?
- je @f ; Да, строки равны
- xor eax,eax ; Строки не совпадают
- inc eax
- @@:
- pop edi esi
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------
- ; Функция быстрого регистронезависимого сравнения
- ; двух строк
- ;-----------------------------------------------------
- ; lpStr1 - указатель на первую строку ASCIIZ
- ; lpStr2 - указатель на вторую строку ASCIIZ
- ; На выходе: EAX=0 - строки совпадают
- ; EAX=1 - строки различаются
- ;-----------------------------------------------------
- proc _lstrcmpi lpStr1:DWORD,lpStr2:DWORD
- push esi edi
- mov esi,[lpStr1] ; Указатели на строки
- mov edi,[lpStr2]
- xor eax,eax ; Предположим, что строки равны
- .loc_compare:
- lodsb ; Сравнить следующие символы
- mov ah,[edi]
- inc edi
- cmp al,ah ; Символы совпадают?
- jne @f ; Нет
- or al,al ; Строки закончились?
- jz .loc_ret ; Да, строки совпадают
- jmp .loc_compare ; Проверить следующий символ
- @@:
- and eax,0DFDFh ; Оба символа в верхний регистр
- cmp al,'A'
- jb .loc_not_equal
- cmp al,'Z'
- ja .loc_not_equal
- cmp al,ah ; Разница была только в регистре?
- je .loc_compare ; Да, следующий символ
- .loc_not_equal:
- xor eax,eax ; Строки не совпадают
- inc eax
- .loc_ret:
- pop edi esi
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;-----------------------------------------------------
- ; Функция быстрого заполнения блока памяти значениями
- ;-----------------------------------------------------
- ; lpDst - указатель на приемник
- ; dVal - заполнитель (берется младший байт)
- ; dSize - размер заполняемого блока
- ;-----------------------------------------------------
- proc _fillmem lpDst:DWORD, dVal:DWORD, dSize:DWORD
- pusha
- cld
- mov edi,[lpDst]
- mov eax,[dVal]
- ; Оставить только младший байт
- movzx eax,al
- mov edx,01010101h
- ; Теперь в EAX будет DWORD-заполнитель
- mul edx
- mov ecx,[dSize]
- push ecx
- ; Разделить на 4 и получить длину в DWORD
- shr ecx,2
- ; Заполнить основную часть строки DWORD'ами
- rep stosd
- pop ecx
- ; Получить остаток от деления на 4
- and ecx,3
- ; Заполнить остаток строки байтами
- rep stosb
- popa
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ; my_path описан в данных как буфер размером MAX_PATH+1
- mov edi,my_path
- invoke GetModuleFileName,NULL,edi,MAX_PATH
- add edi,eax
- std
- mov ecx,eax
- mov al,'\'
- repne scasb
- mov byte [edi+2],0
- cld
- ; Теперь в my_path содержится каталог запуска с завершающим '\'
Code (Assembler) : Убрать нумерацию
- ; Вариант №1
- @@: neg eax
- js @b
- ; EAX = |EAX|
- ;------------------------------------
- ; Вариант №2
- mov edx, eax
- sar edx, 31
- xor eax, edx
- sub eax, edx
- ; EAX = |EAX|
Просмотров: 2130 | Комментариев: 18
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(26.03.2012 в 20:32):
Красивее - да, быстрее - сомневаюсь. Хотя для небольших проектов очень даже ничего. Добавил в статью, спасибо!
Alexey32
(26.03.2012 в 19:50):
По-моему, так лучше:
proc _memcopy lpDst:DWORD, lpSrc:DWORD, dSize:DWORD
pusha
; Установить указатели на источник и приемник
cld
mov edi,[lpDst]
mov esi,[lpSrc]
mov ecx,[dSize]
shr ecx,1 ;проверка на чётность и деление на 2
jnc @f
movsb ;по необходимости копируем байт
@@:
shr ecx,1 ;проверка на делимость на 4 и деление ещё на 2
jnc @f
movsw ;по необходимости копируем слово
@@:
rep movsd ;копируем оставшиеся двойные слова
popa
ret
endp
proc _memcopy lpDst:DWORD, lpSrc:DWORD, dSize:DWORD
pusha
; Установить указатели на источник и приемник
cld
mov edi,[lpDst]
mov esi,[lpSrc]
mov ecx,[dSize]
shr ecx,1 ;проверка на чётность и деление на 2
jnc @f
movsb ;по необходимости копируем байт
@@:
shr ecx,1 ;проверка на делимость на 4 и деление ещё на 2
jnc @f
movsw ;по необходимости копируем слово
@@:
rep movsd ;копируем оставшиеся двойные слова
popa
ret
endp
ManHunter
(22.08.2011 в 16:03):
Зачем городить все в одну кучу? Кому надо - добавят обработку исключений.
DJK
(22.08.2011 в 16:01):
ваши идеи хорошы, но при работе с буферами памяти, размер которых не удается достоверно определить в функции, следует использовать SEH иначе могут функции повести себя совсем не так как хотелось бы.
ManHunter
(10.07.2011 в 14:35):
Добавил еще один шустрый вариант сравнения строк _lstrcmp и регистронезависимое сравнение строк _lstrcmpi
ManHunter
(02.06.2011 в 00:42):
Надо будет позаимствовать оттуда счетчики времени. А так - отличная работа!
disciple27
(01.06.2011 в 00:04):
ManHunter, ещё раз здравствуйте!
Дело было вечером, делать было нечего... http://ifolder.ru/23887012
Дело было вечером, делать было нечего... http://ifolder.ru/23887012
disciple27
(30.05.2011 в 23:56):
ManHunter, всегда пожалуйста - правда это не моё решение, а всё из того же масма (а точнее masmlib которое, пардон за плагиат), которое я нагло оттуда позаимствовал, обожаю всё на свете портировать под FASM :)
ManHunter
(30.05.2011 в 16:28):
disciple27, добавил альтернативную функцию _lstrlen из твоего примера. Спасибо!
disciple27
(29.05.2011 в 23:58):
ManHunter огромное тебе спасибо за функции, особенно понравилась 'получение каталога' оригинально :)
В долгу не останусь, так как еще понравились MASM'овые счётчики по ссылкам, я их тоже немного позаимствовал ;) http://ifolder.ru/23841188
В долгу не останусь, так как еще понравились MASM'овые счётчики по ссылкам, я их тоже немного позаимствовал ;) http://ifolder.ru/23841188
ManHunter
(20.05.2011 в 20:14):
Фигасе там народ отжигает :) А я думал что это я извращенец :)
Voffka
(20.05.2011 в 19:42):
И еще тесты разных алгоритмов.
memcopy
http://www.masm32.com/board/in...topic=6445.0
http://www.masm32.com/board/in...topic=2471.0
strcat
http://www.masm32.com/board/in...topic=2555.0
strcmp
http://www.masm32.com/board/in...topic=2508.0
memcopy
http://www.masm32.com/board/in...topic=6445.0
http://www.masm32.com/board/in...topic=2471.0
strcat
http://www.masm32.com/board/in...topic=2555.0
strcmp
http://www.masm32.com/board/in...topic=2508.0
Voffka
(20.05.2011 в 19:22):
Тесты разных strLen
http://www.masm32.com/board/in...topic=1807.0
http://www.masm32.com/board/in...topic=1807.0
ManHunter
(20.05.2011 в 15:58):
Эх, не писал ты вирусов под MS-DOS :))))
zummenix
(20.05.2011 в 15:58):
Да, конечно esi. А в оптимизации я нуб :)
ManHunter
(20.05.2011 в 15:42):
А разве не mov ESI,[lpStr] ? И зачем xor eax,eax, если можно сравнивать с нулем al? Флаг направления тоже желательно устанавливать. Тогда уж надо что-то типа такого:
xor ecx,ecx
mov esi,[lpStr]
cld
@@:
inc ecx
lodsb
or al,al
jnz @b
dec ecx
Плюс команда перехода подавляет конвейер команд процессора, теряется производительность :)
xor ecx,ecx
mov esi,[lpStr]
cld
@@:
inc ecx
lodsb
or al,al
jnz @b
dec ecx
Плюс команда перехода подавляет конвейер команд процессора, теряется производительность :)
zummenix
(20.05.2011 в 15:41):
Для получения длины строки мне нравится такой вот способ :)
xor eax,eax
xor ecx,ecx
mov edi,[lpStr]
@@:
inc ecx
lodsb
test eax,eax
jne @R
dec ecx
xor eax,eax
xor ecx,ecx
mov edi,[lpStr]
@@:
inc ecx
lodsb
test eax,eax
jne @R
dec ecx
Всеволод
(20.05.2011 в 12:14):
Ассемблер — прикольная штука. Все хочу сесть, поучить :)
Добавить комментарий
Заполните форму для добавления комментария
