|
В тази статия ще изложа някои кратки трикове, които могат да се реализират само на асемблер и правят програмата по-къса и/или по-бърза.
Предполагам, че за разбирането на тези трикове се изисква базисни знания за инструкциите на x86, както и познания за бройни системи - специално двоична и шестнайсетична, както и за основните бинарни логически операции. По-начинаещите също могат да прочетат статията и дори и да не разберат всичко, могат да питат, а и някои от примерите могат да им помогнат да си изградят "мислене на асемблер". 1. Аритметика: 1.1 Изчисляване на абсолютна стойност. Най-късото решение известно до сега е: CODE
Дължина 4 байта. Кода работи така: а) Ако eax съдържа отрицателно число на входа, след инверсията резултата е по-голям от 0, прехода не се изпълнява и на изхода получаваме положителна стойност. б) Ако eax съдържа положително число, след инверсията, резултата е по-малък от нула, прехода се изпълнява и попадаме в ситуация а) когато имаме отрицателно число на входа. Този трик е по-известен във варианта: CODE
Този вариант има обаче един недостатък, че има скрит бъг, който води до безкраен цикъл. Наистина, ако на входа се подаде числото $80000000, което няма положителен еквивалент, се получава безкраен цикъл и програмата ще заспи. При варианта с jl проблема се решава, защото при него се използва и флага за препълване (инструкцията е за работа с числа със знак) който при "neg $80000000" се вдига, индицирайки грешен знак и прехода не се изпълнява. Горният вариант е най-късото възможно решение, но дали е най-бързото. При новите процесори - не, заради прехода. Въобще при последните модели процесори, условните преходи трябва да се избягват, ако искаме да получим най-високата скорост на изпълнение. Ето един вариант на abs, без използването на преходи: CODE
Как работи този код: Знаем, че: -x = (not x) + 1 = (not x) - (-1); (1) От друга страна знаем, че: not x = x xor (-1); (2) Инструкцията cdq (convert dword to qword) запълва edx със знаковият бит на eax - тоест ако eax e положително, edx = 0, а ако eax е отрицателно, edx=-1. Сега, ако на входа на кода се подаде положително число, edx = 0 и операциите върху eax са: xor eax, 0 sub eax, 0 Което очевидно няма да промени стойността му. Ако eax е отрицателно, ще имаме: xor eax, -1 ( not eax от (2) ) sub eax, -1 ( събирането с 1 от (1) ) Което ще ни даде точно neg eax. Този код ще се изпълни по-бързо на компютри Pentium и по-нови. Недостатъците му са два: 1. задължително трябва да се използват eax/edx (ax/dx) 2. Губи се съдържанието на edx. 3. инструкцията cdq не е от най бързите в новите процесори. Решението на проблеми 1 и 3 е замяната на cdq със еквивалента и: CODE
Така получаваме окончателният код: CODE
Този код с дължина 9 байта е най-бърз от показаните, ако се направи правилно сдвояване на кода, за използване на двата конвеера на новите процесори. Тоест между инструкциите трябва да се вмъкнат други инструкции от кода, които не са свързани с тези и могат да се изпълняват паралелно. Допълнително предимство е, че могат да се използват произволни два регистъра.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||









