Встроенный ассемблер

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by DooD, 7 Jun 2011.

  1. DooD

    DooD Elder - Старейшина

    Joined:
    30 Sep 2010
    Messages:
    1,168
    Likes Received:
    450
    Reputations:
    288
    Введение​

    Здравствуйте уважаемые Античатовцы! В этой статье я буду рассказывать вам о встроенном ассемблере в паскале и Delphi.У Вас на сей момент уже должны быть средние знания языков программирования высокого уровня.

    Знаю что можно сказать что на дворе 21 век кому нужен ассемблер , да к тому же еще и встроенный.Но, хочу сказать что ни один более-менее серьезный проект не обойдется без ассемблерных вставок, тем более в Delphi всегда найдется что ускорить:),а может Вам когда нибудь эта статья поможет при решении какой либо задачи.
    В статье также буду приводить примеры, куда ж без них)


    Зачем это нужно?

    Зачем вам нужно использовать встроенный ассемблер? В некоторых случаях использование встроенного ассемблера даст ЗНАЧИТЕЛЬНЫЙ выигрыш в скорости, особенно при работе со строками, сравнении, работе с нестандартными устройствами ввода-вывода etc.Также он позволит выполнить те функции,которые на паскале или делфи выполнить просто невозможно.К примеру даже хиленькие игровые движки вроде id tech1 для doom были написаны на 40-50% с ассемблерными вставками,не говоря о современных движках.Или системные утилиты:сложно создать утилиту без использования ассемблера для получения физических данных компьютера или работе с его устройствами.Примеров можно привести много…

    Ну-с начнем.
    Для начала немного теории.
    ТЕОРИЯ
    Встроенный ассемблер позволит вам непосредственно в программах на паскале или делфи записывать код ассемблера.
    Операторы для ассемблерных вставок :

    asm
    … (ассемблерный код)
    end;
    (тоесть как begin-end)

    Думаю большинство кто слышал про ассемблер, знают что программирование на ассемблере непосредственно связано с регистрами.
    Регистр - это определенный участок памяти внутри самого процессора, от 8-ми до 32-х бит длиной, который используется для промежуточного хранения информации, обрабатываемой процессором. Некоторые регистры содержат только определенную информацию.

    Регистрами общего назначения являются следующие регистры:
    EAX, EBX, ECX, EDX. Во встроенном ассемблере их можно использовать как глобальные переменные.
    EAX, EBX, ECX, EDX делятся на AX, BX, CX, DX, и деляется еще на AH , AL, BH,BL,CH,CL,DH,DL.
    Так 32-битные регистры (EAX,EBX,ECX,EDX)- по размеру составляют 4 байта или DWORD- двойное слово. Соответственно ax ,bx, cx ,dx-16 битные и составляют 2 байта или слово (word)
    Так, AH , AL, BH,BL,CH,CL,DH,DL по одному байту.Буква L и H означают младший или старший регистр.
    Эти регистры используются для операций с данными, такими, как сравнение, математические операции или запись данных в память.
    Регистр СХ чаще всего используется как счетчик в циклах.

    Существуют также и другие регистры, а именно:
    Регистры сегментов
    Регистры оффсета
    Регистры управления
    Регистры дебаггера
    Контрольные регистры
    Но это нам особо не потребуется.

    Встроенный ассемблер поддерживает три дирек-
    тивы ассемблера: DB (определить байт), DW (определить слово) и DD
    (определить двойное слово). Каждая из них генерирует данные, со-
    ответствующие разделенным запятым операндам, которые следуют за
    директивой.
    Приведем некоторые примеры директив DB, DW и DD:

    asm
    DB 00FH { 1 байт }
    DB 0,99 { 2 байта }
    DB 'A' { Ord('A) }
    DB 'Пример',0DH,OAH { строка, за которой
    следуют возврат каретки и перевод строки }


    В Турбо Ассемблере, когда перед идентификатором указывается
    DB, DW или DD, это приводит к генерации в том месте, где указана
    директива, переменной размером в байт, слово или двойное слово.

    Команды:
    Приведу команды которые чаще используются во встроенном ассемблере:

    mov-Переслать значение.
    push-Положить(сохранить) в стек
    pop-Извлечь из стека
    xchg-Обменять значения
    add-Сложить
    inc-Увеличить на единицу
    sub-Вычесть
    dec-Уменьшить на единицу
    mul-Умножить
    imul-Умножить со знаком
    div-Поделить
    idiv-Поделить со знаком
    cmp-сравнить

    Прыжки:

    Прыжки используются вместе с командами сравнения
    прыжки бывают условные и безусловные:

    безусловный прыжок:
    jmp -метка

    метки в паскале должны начинаться с символа @ и заканчиваться :
    Условные прыжки (+пояснение):


    JA Jump if above (X > Y)
    JAE Jump if above or equal (X >= Y)
    JB Jump if below (X < Y)
    JBE Jump if below or equal (X < Y)
    JC Jump if carry (cf=1) CF=1
    JCXZ Jump if CX=0 регистр CX=0
    JE (то же, что и JZ) Jump if equal (X = Y)
    JG Jump if greater (signed) (X > Y)
    JGE Jump if greater or equal (signed) (X >= Y)
    JL Jump if less (signed) (X < Y)
    JLE Jump if less or equal (signed) (X <= Y)
    JNA Jump if not above (X <= Y)
    JNAE Jump if not above or equal (X < Y)
    JNB Jump if not below (X >= Y)
    JNBE Jump if not below or equal (X > Y)
    JNC Jump if not carry (cf=0)
    JNE Jump if not equal (X != Y)
    JNG Jump if not greater (signed) (X <= Y)
    JNGE Jump if not greater or equal (signed) (X < Y) SF!=OF
    JNL Jump if not less (signed) (X >= Y)
    JNLE Jump if not less or equal (signed) (X > Y)
    JNO Jump if not overflow (signed) (of=0) OF=0
    JNP Jump if no parity (pf=0) PF=0
    JNS Jump if not signed (signed) (sf=0) SF=0
    JNZ Jump if not zero (X != Y)
    JO Jump if overflow (signed) (of=1) OF=1
    JP Jump if parity (pf=1) PF=1
    JPE Jump if parity even ( PF=1
    JPO Jump if parity odd PF=0
    JS Jump if signed (signed) SF=1
    JZ Jump if zero (X = Y)

    Инструкции условных прыжков используют один или несколько флагов состояния для проверки условия.

    Итак теория закончена можно приступить к практической части.

    Первое и несложное что можно написать: модуль для простейших арифметических операций: +-*/ с целыми числами
    Code:
    unit unit2;
    interface
     
    
    function Sum(A,B:integer):integer;  {экспортируемые функции}
    function Sub(a,b:integer):integer;
    function Mul(a,b:integer):integer;
    function dev(a,b:integer):integer;
    
    implementation
     
    function Sum(A,B:integer):integer; {функция сложения}
    begin
    asm
    mov eax,a ; {в eax-a}
    add b,eax {прибавляем b+a}
    end;
    writeln('summa chisel=',b); {выводим сумму}
    end;
    
    function Sub(a,b:integer):integer;
    begin
    asm
    mov eax,b {в eax-b}
    sub a,eax {вычитаем из а-eax}
    end;
    writeln('raznost chisel=',a); {выводим разность}
    end;
    
    function Mul(a,b:integer):integer;
    begin
    asm
    mov eax,a {в eax-a}
    mov ecx,b {в ecx-b}
    mul ecx {умножаем a на b результат в eax}
    mov a,eax {результат в а}
    end;
    writeln('proizvedennie chisel=',a); {выводим произведение}
    end;
    
    function dev(a,b:integer):integer;
    begin
    asm
    xor edx,edx {остаток в edx}
    mov eax, a {в eax-a}
    mov ecx, b {в ecx-b}
    div ecx {a/b, результат в eax}
    mov a,eax 
    end;
    writeln('chastnoe chisel=',a); {выводим частное}
    end;
    
    end.
     
    вот и все,довольно простой модуль с использованием встроенного ассемблера.

    использование модуля( Delphi)
    Code:
    program Project2;
    
    {$APPTYPE CONSOLE}
    
    uses
     unit2;
    var a,b:integer;
    begin
    writeln('vvedite a i b');
    readln(a);
    readln(b);
    mul(a,b);
    readln;
    end.
     
    Вот уже Ваш первый модуль на встроенном асме.

    mov, xchg нельзя использовать для пересылки из памяти в память:
    то есть нельзя делать так:
    mov a,b

    Правильно так:
    mov eax,a
    mov b,eax

    Что касается программирования под винду.В Delphi
    так же можно вызывать api функции используя ассемблерные вставки.Приведу пример из жизни:
    Update module из моей assembler editor работает через idhttp и использует progressbar чтобы показать скорость закачки. При запуске закачки приложение подвисает,а без использования application.proceemessages вообще замерзает.Но вдруг вы резко передумали и хотите выйти написав код для кнопки выохда так:
    Code:
    procedure TForm1.Button2Click(Sender: TObject);
    begin
    exit;
    end;
     
    то знайте что ничего не произойдет.

    но вот простейший вызов ускорит все в десятки раз
    Code:
    procedure TForm1.Button2Click(Sender: TObject);
    begin
    asm
    push 0
    call exitprocess
    end;
    end;
     
    Да во встроенном дельфийском асме используется синтаксис турбо ассемблера,то есть для вызова функции,параметры кладутся в стек как бы вверх дном, а затем следует сам вызов функции.Ето одни пример замены тормознутой дельфийской функции,а заменять приходится довольно часто,если программа имеет большой функционал.


    Теперь рассмотрим простые операции на паскале
    К примеру инкремент переменной:
    код на паскале
    Code:
      
    uses crt;
    var a,b:integer;
    begin
    a:=0;
    b:=999;
    begin
    repeat
    inc(a);
    until a=b;
    end;
    writeln('a=',a);
    readln;
    end;
    
    со вставками:
    Code:
    uses crt;
    var a,b:integer;
    begin
    a:=0;
    b:=999;
    asm {оператор}
    @one: {метка для цикла}
    inc a {a:=a+1}
    mov ax,b {b в ax}
    cmp a,ax {если a=b}
    je @end {прыжок}
    loop @one {цикл пока a не станет = b}
    @end:
    xor ax,ax
    end;
    writeln(a);
    readln;
    end.
     
    Рассмотрим к примеру организацию цикла for to do

    mov eax, 2 {for ax:=2 to 9 do}
    @For:

    inc ax
    cmp ax, 9
    jg @For


    то-бишь циклы организуются легко.

    Теперь пример программы на использование push/pop xchg
    Code:
     
    uses crt;
    var a,b:integer;
    begin
    a:=10;
    b:=20;
    
    writeln(a, b);
    readln;
    asm
    mov ax,a {в ax-a}
    push ax{сохраняем ax в стеке}
    mov bx,b
    mov ax,bx {ax:=bx,ax=20}
    pop ax {ax=10}
    xchg ax,bx {ax=20}
    mov a,ax
    end;
    writeln(a);
    readln;
    
    
    end.
     
    Побалуемся и с графикой немного,
    вывод линии используя асм вставки с прерываниями биоса

    Code:
     
    uses crt;
    var a,b:integer;
    begin
    asm
    mov ah,0h {устанавливаем графический режим}
    mov al,10h
    int 10h
    mov cx,400
    @line: {вывод линии}
    push cx
    mov ah,0ch {выводим точку}
    mov al,2 {зеленую}
    mov bh,0
    mov dx,100 {коор.}
    int 10h{прерывание биоса}
    
      pop cx
    loop @line {loop-им вывод в единую линию}
    end;
    readkey;
    
    end.
     
    работа с графикой,аппаратурой,циклами и
    сравнениями-несомненно лучше во встроенном асме.

    Когда то давно писал рахитский движок раза в 3 хуже вульфейнштейновского,был у меня там модуль с записью в порты клавиатуры,к сожалению утерял.


    ЗАКЛЮЧЕНИЕ​

    Многие программисты сегодня ассоциируют ассемблер как сложный, низкоуровневый язык программирования. Они считают его быстрым, но большинство их них думает, что его сложно или невозможно изучить. В действительности, положение не настолько сложно. Вполне возможно научиться писать хороший код, не будучи гением.
    Большинство программистов считают, что собственный ассемблерный код по определению быстрее, чем скомпилированный компилятором Паскаля. Конечно, так не всегда. Плохо написанные процедуры на Ассемблере могут оказаться по качеству хуже и могут вызвать странные ошибки и проблемы в ваших приложениях.
    В тот момент, когда вы придете к выводу, что ассемблер это нужный вам путь, вы не должны хвататься за него, как за соломинку. Проверьте внимательно свою программу на предмет определения слабых мест. Профилировщик может немного в этом помочь, но лучше обратите особое внимание на структуру и алгоритм. Часто, вы сможете получить лучшие результаты, за счет оптимизации алгоритмов, скорее, чем с помощью ассемблера. С другой стороны, в некоторых особых случаях, таких как манипуляции с битами, ассемблер даст лучший и более простой результат.
    Если вы решите, что ассемблер действительно нужен, то потратьте немного времени на планирование вашего кода и алгоритмов. Только после того, как вы будете четко представлять, что вы хотите сделать и как, вы можете приступать к реализации вашей идеи. Если вы не будете об этом думать, то получите кашу из кода, закрученные операторы и трудно управляемую программу.(с) http://www.programmersclub.ru/
    Спасибо за внимание. Я особо не имею журналистских наклонностей, так что не пинайте ежели что:)
     
    #1 DooD, 7 Jun 2011
    Last edited: 7 Jun 2011
    6 people like this.
  2. fox_malder

    fox_malder Active Member

    Joined:
    28 Nov 2008
    Messages:
    162
    Likes Received:
    131
    Reputations:
    73
    хорошая но много буковок , пока что не могу полностью прочитать но начало хорошее
    молодец так держать:)
     
  3. Osstudio

    Osstudio Banned

    Joined:
    17 Apr 2011
    Messages:
    638
    Likes Received:
    160
    Reputations:
    81
    Статья очень даже хорошая, но много плюшек... :)
     
  4. DooD

    DooD Elder - Старейшина

    Joined:
    30 Sep 2010
    Messages:
    1,168
    Likes Received:
    450
    Reputations:
    288
    Спасиб, ночью писал, буду за компом,грамматику поправлю:)
     
  5. Osstudio

    Osstudio Banned

    Joined:
    17 Apr 2011
    Messages:
    638
    Likes Received:
    160
    Reputations:
    81
    Грамматика даже очень хорошая, но много лишних слов просто... :)
     
  6. alexey-m

    alexey-m Elder - Старейшина

    Joined:
    15 Jul 2009
    Messages:
    518
    Likes Received:
    100
    Reputations:
    37
    DooD, начало неплохое, дополни её о соглашениях передачи параметров, о вариациях возврата результата функций в зависимости от типов данных, какие регистры нужно сохранять, какие не обязательно, что в принципе очень не плохо описано в этой статье
     
  7. DooD

    DooD Elder - Старейшина

    Joined:
    30 Sep 2010
    Messages:
    1,168
    Likes Received:
    450
    Reputations:
    288
    посмотрю обязательно,спасибо.
     
    1 person likes this.
  8. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,296
    Reputations:
    327
    я много с чем не соглашусь, например с мнением многих программистов (имхо говнокодеров) которые считают что асм это ацкий пиздец и сложность, однако это всегда будет неотъемлемой частью любого аспекта кодинга, поймите правильно, нах асм во флеше или яваскрипте, оно ясно, но платформенное программирование, тем паче программирование железяк, это уж совсем другая стезя. вот спросите тех кодеров, они хоть раз вдумывались в такую элементарщину как strpos? уверен что больше половины знают только то что она возвращает смещение подстроки, если таковая есть. также и асм, это как кирпичи из которых строится дом. за статью бонус конечно, молодец что написал.
     
    1 person likes this.
  9. DooD

    DooD Elder - Старейшина

    Joined:
    30 Sep 2010
    Messages:
    1,168
    Likes Received:
    450
    Reputations:
    288
    sn0w ,согласен
     
  10. Gar|k

    Gar|k Moderator

    Joined:
    20 Mar 2009
    Messages:
    1,166
    Likes Received:
    266
    Reputations:
    82
    запятые (сам не люблю, но пришлось недавно).
     
    _________________________
  11. Sams

    Sams Member

    Joined:
    18 Apr 2009
    Messages:
    247
    Likes Received:
    70
    Reputations:
    17
    1. Еще перед началом примера первой функции нужно было сразу сказать, что отсутствие ret является обязательным условием.

    2.
    WTF? :confused:

    А вообще в Делфи настолько дебильный АСМ, что плакать охота. Я когда писал какую-то прогу с красивым GUI, мне понадобилась АСМ вставка. Я столько промучился с ней, что переписал полностью всю прогу на MASM32 включая GUI. It's terrible. Borland are sick.

    P.S. Я так и не понял, в чем собственно смысл статьи. В половине книг АСМ под Win32 описывается встроенный АСМ как на Делфи, так и на Сцы++. К тому же есть небольшая документация, страниц эдак на 80, где сугубо описывается работа АСМ в Делфи. :eek:
     
    #11 Sams, 8 Jun 2011
    Last edited: 8 Jun 2011
  12. rpo3a

    rpo3a New Member

    Joined:
    24 Apr 2011
    Messages:
    33
    Likes Received:
    1
    Reputations:
    0
    Спасибо за хорошую статью....По больше бы таких людей)))
     
  13. TikTik

    TikTik Member

    Joined:
    20 Feb 2009
    Messages:
    241
    Likes Received:
    12
    Reputations:
    2
    Assembler вообще считаю самым легким, он как бы это выразиться он очень одностороний, тоесть нет миллион вариантов решения одной и той же задачи, самое правильное решение будет практический единственным.
     
  14. yuran666666

    yuran666666 Member

    Joined:
    18 Jan 2009
    Messages:
    84
    Likes Received:
    19
    Reputations:
    11
    Зомба, возвращайся уже и напеши есчо этих технических стотей - все тебя ждут.