Отправляет email-рассылки с помощью сервиса Sendsay

Assembler - Просто и Эффективно.

  Все выпуски  

Assembler - Просто и Эффективно.



Assembler

Глава #5 - команды процессора.

Процессоры 80x86 поддерживают несколько сотен команд. Большинство из них используется для ускорения каких-либо алгоритмов (например команды FPU для вычислений с плавающей точкой, MMX для обработки одной командой сразу нескольких чисел и т.д). Такие команды можно "эмулировать" при помощи нескольких команд, но это будет значительно медленей и, как правило, сложнее.
Новые версии процессоров поддерживают все команды предыдущих. Это называется обратной совместимостью. Также програмно совместимы процессоры фирм AMD, Intel, и других, используемых в IBM совместимых компьютерах.
Набор команд, используемый в программе задается директивами:

.386; .486; .586основные команды
.386p; .486p; .586pосновные+системные команды
.387; .487; .587сопроцессор (FPU)
.mmx или pmmxкоманды MMX
   -------------------------------

  Общие характеристики команд процессора:
  • Процессор может выполнять команды только из оперативной памяти. Адрес текущей команды: cs:eip.
  • Команды это данные, имеющие специальный формат, воспринимаемый процессором. Размер команды кратный байту.
  • Команда может иметь префиксы, влияющие на ее выполнение, например, префикс замены сегмента.
  • Не допустимо более одного операнда "память" в команде. При работе с несколькими ячейками используйте пормежуточные регистры или стэк.
  • Обычно, размер операндов должен совпадать.
  • Как правило, команды, имеющие два операнда, производят действие над первым операндом (приемником) при помощи второго (источника).
  • Обычно, арифметические и логические команды устанавливают флаги в соответствии с результатом.
  • Команда может вызвать исключение. Если исключение не предусмотрено, появится сообщение об ошибке программы.
  • Некоторые команды имеют несколько названий. Например nop и xchg eax,eax - одна и таже команда.
  • В большинстве случаев, командам работающим с операндом в слово, соответствует машинный код, аналогичный команде работающей с двойным словом. Перед командой компилятор ставит префикс замены разрядности, если она не совподает с текущей. Лучше не использовать команды, не совподающие с текущей разрядностью, без надобности. Т.е в программах под Windows желательно использовать байты или двойные слова в качестве операндов.
  • Многие команды имеют короткую форму. Если операнд (знаковое число) помещается в байт, команды обычно занимают меньше места. Например, команда mov eax,10 занимает 5 байт, а две команды push 10; pop eax только 3.
  • Название некоторых команд оканчивается символами "cc". Так обозначается группа команд, действие которых зависит от флагов. Замените символы "cc" в названии на значение из таблицы:

    "сс"УсловиеПояснение (при сравнении)
    oof=1переполнение
    b naecf=1ниже
    nb aecf=0не ниже
    e zzf=1ноль; равно
    ne nzzf=0не ноль; не равно
    be nacf=1 и zf=1не выше; ниже или равно
    nbe acf=0 и zf=0выше; не ниже или равно
    ssf=1минус; знаковый бит установлен
    nssf=0плюс; знаковый бит сброшен
    p pepf=1младший байт содержит четное количество бит
    np popf=0младший байт содержит нечетное количество бит
    l ngesf<>ofменьше; не больше или равно
    nl gesf=ofне меньше; больше или равно
    le ngzf=1 или sf<>ofменьше или равно; не больше
    nle gzf=0 и sf=ofне меньше или равно; больше
    Больше/меньше означает знаковое сравнение, ниже/выше - беззнаковое
       -------------------------------

  •   Ниже приведено описание наиболее часто используемых команд в прикладных программах под Windows. Второй столбец в таблице показывает количество операндов.

       Арифметические команды:
    add2Прибавить к первому операнду второй
    sub2Вычесть из первого операнда второй
    dec1Вычесть из операнда единицу
    inc1Прибавить к операнду единицу
    div1Беззнаковое деление. операнд - делитель
    Если размер операнда - байт, то делимое находится в ax, частное возвращается в al, остаток в ah.
    Если слово - делимое dx:ax, частное ax, остаток dx
    Если двойное слово - делимое edx:eax, частное eax, остаток edx
    idiv1деление со знаком. команда аналогична div. остаток всегда имеет знак делимого
    imul2Знаковое умножение. операнд1=операнд1*операнд2
    imul3операнд1=операнд2*операнд3
    mul1беззнаковое умножение. операнд - множитель.
    Если операнд размером в байт, то второй множитель в al, результат в ax
    Если слово - множитель в ax, результат в dx:ax
    Двойное слово - множитель в eax, результат в edx:eax
    neg1Изменить знак операнда
    Команды устанавливают значения флагов OF SF ZF AF PF CF в зависимости от результата. inc и dec не воздействуют на CF.
       -------------------------------

      Логические команды, работа с битами:
    Следующие команды используются для работы с битами, и также воздействуют на флаги:
    and or xor2Операнд1 = операнд1 and / or / xor операнд2
    bt btc btr bts2Установить флаг CF значением бита номер операнд2 из операнда1. c - изменить его значение на обратное, r - сбросить, s - установить
    not1изменить значение всех бит операнда на обратное
    shl shr2сдвиг битов операнда 1 влево / вправо на операнд2 (число или cl). CF устанавливается значением выдвигаемого бита
    Команда and устанавливает бит резултьтата в единицу, только если соответстующие биты обоих операндов равны 1. Используется для выделения бит.
    or - если хотя бы один бит установлен - используется для установки бит.
    xor - инверсия тех бит первого операнда, значения которых второго равно 1.
    Пример:
    and eax,100011bсброс всех бит регистра eax кроме нолевого, первого и пятого
    or eax,111bустановить младшие 3 бита регистра eax
    or ecx,ecxИспользуется для установки флагов. Занимает 2 байта вместо cmp ecx,0 - три
    xor dword ptr [AnyVal],0F0hинверсия бит 4,5,5,7 переменной AnyVal
    xor eax,eaxобнулить значение регистра eax. команда занимает 2 байта вместо mov eax,0 - пять
       -------------------------------

      Команды передачи управления.
    Эти команды используются для изменения хода выполнения программы, организации циклов, ветвлений. Команды имеют один операнд - адрес, на который передается управление (обычно задается меткой).
    callВызов процедуры. Перед передачей управления помещает в стэк адрес следующей команды.
    jccПереход в зависимости от состояния флагов. См первую таблицу.
    jcxz jecxzПереход если cx / ecx равен нулю.
    jmpБезусловный переход.
    loopУменьшить ecx на 1. если ecx не равен 0, передать управление по указанному адресу.

    Возврат из процедуры:
    ret0Возврат из процедуры. Извлечь из вершины стэка адрес, и перейти на него.
    ret1Действие аналогично ret, но прибавляет к esp указанное в операнде число.
       -------------------------------

      Прочие команды:
    cdq0 установить все биты регистра edx значением старшего бита регистра eax. Обычно используется перед командой div
    stc clc0Установить / сбросить флаг CF. Иногда используется для возврата результата выполнения процедуры
    cmp2Сравнить операнды. Установить флаги как при команде sub
    test2Установить флаги, как при команде and
    lea2вычислить выражение типа "Адресация Памяти", и поместить в первый операнд. Используется для простых вычислений
    bswap1поменять местами байты #0, 3 и #1,2 операнда
    mov2Поместить значение второго операнда в первый
    movsx movzx2поместить в первый операнд (word или dword) значение второго (word или byte). остальные биты приемника заполнить: знаковым битом источника / нулями.
    xchg2поменять значения операндов
    setcc1Если условие "cc" (см таблицу вверху) выполняется, операнд=1, иначе 0
    nop0команда, не выполняющая действий, занимает 1 байт
       -------------------------------

      Команды работы со стэком:
    pop1извлечь число из стэка в операнд
    popa0извлечь регистры общего назначения после команды pusha (кроме esp)
    popf0извлечь число из стэка в регистр eflags
    push1поместить операнд в стэк
    pusha0поместить регистры eax ecx edx ebx esp ebp esi edi в стэк. Регистр edi оказывается на вершине
    pushf0поместить eflags в стэк
       -------------------------------

      Строковые команды.
    Эти команды используютя для работы с массивами (байт, слов или двойных слов). Регистр esi используется как источник, edi - приемник. Направление работы команды зависит от флага DF. Если флаг установлен, команды работают в обратном направлении (значения регистров esi и/или edi уменьшаются на размер, определенный командой), если сброшен - в прямом (значения увеличиваются).
    Для установки флага DF используйте команду std, для сброса - cld. При использовании префиксов rep, команда повторяется заданное в ecx количество раз. Для cmpsb и scasb повторение может прерваться по результату сравнения.
    Команды не имеют операндов.
    Некоторые функции Windows "забывают" установить нужное им значение флага DF перед использованием строковых команд (например, WinExec). Если вызвать такую функцию с DF=1, произойдет ошибка программы. Поэтому после установки флага DF, нужно сбросить его перед вызовом функций Windows.

    lodsb lodsw lodsdЗагрузить в регистр al/ax/eax по адресу esi
    stosb stosw stosdЗаписать в память по адресу edi регистр al/ax/eax
    scasb scasw scasdсравнить регистр al/ax/eax с ячейкой по адресу edi
    cmpsb cmpsw cmpsdсравнить байт/слово/двойное слово по адресу esi, c ячейкой по адресу edi
    movsb movsw movsdкопировать байт/слово/двойное слово из ячейки памяти по адресу esi в edi

    Префиксы (используются только со строковыми командами)
    rep repe repzповторять,(пока равно для scasb,cmpsb) строковую команду. Количество повторов задается регистром ecx - при каждом выполнении команды уменьшается на 1
    repnz repneДействие аналогично rep, но пока не равно
    Примеры использования:
  • Копирование 100 слов из массива srcbuffer в destbuffer mov esi,offset srcbuffer mov edi,offset destbuffer mov ecx,100 cld rep movsw После выполнения: ecx=0, esi=srcbuffer+200,edi=destbuffer+200
  • Поиск байта со значением 0 в массиве anystring (исползуется для определения длинны ASCIIZ строки) mov edi,offset anystring xor ecx,ecx dec ecx mov al,0 cld repnz scasb not ecx dec ecx После выполнения: в ecx - размер строки, в edi - адрес байта, следующий за нолем.
  •    -------------------------------

      Пример использования команд процессора. Основная часть программы - процедура DisplayRegs, отображающая состояние регистров и флагов (может быть использована для отладки). Откомпилируйте и откройте программу отладчиком, как показано в предыдущих главах. Используйте клавишу F8 для выполнения команд call с заходом в процедуру и loop для повторения отладки цикла, F10 для остальных команд, F12 для выхода из процедуры. wd для отображения окошка с данными, db,dw,dd для отображения данных.
       -------------------------------
     .486
     .model flat,stdcall
     .data
    db 0
     .code
    start:
     sub esp,20h
     mov edi,esp
     mov eax,12345678h
     mov ecx,8
     cld
     rep stosd
     popa
     call DisplayRegs
    
     bswap eax
     xor ecx,ecx
     dec ecx
     inc cl
     shr ecx,4
     mov edx,eax
     and edx,ecx
     movzx ebx,ch
     movsx ebp,ch
     lea esi,[ebx*8+ebx+9]
     mov edi,ebp
     xor edi,ecx
     call DisplayRegs
    extrn ExitProcess:proc
     call ExitProcess,0
    
    ;Процедура для отображения содержимого регистров и флагов.
    ;Входных параметров нет; не изменяет содержимое регистров общего назначения и флагов.
    
    ;Данные процедуры. Данные, только для чтения, можно размещать в сегменте кода в местах,
    не получающих управления (например, после команд jmp, ret)
    RegTitle db 'Регистры и флаги:',0
    ;Массив состоящий из последних двух символов названий регистров
    RegNames db 'DISIBPSPBXDXCXAXFLIP'
    FlagsStr db 0Dh,0Ah,'Флаги: '
     FlagsStrSize equ $-offset FlagsStr
     ;FlagsStrSize равна текущему смещению минус смещение FlagsStr
      ;- размеру строки FlagsStr
    FlagTable db 'CxPxAxZSTIDO'
     NumOfFlags equ $-offset FlagTable
    
    OutBufferSize =200
    DisplayRegs:
    ;Начало процедуры. В стэке находится адрес возврата,
      ;помещенный коммандой call при вызове.
    ;используем его для отображения регистра EIP
     pushf  ;сохраним флаги
     pusha  ; и регистры общего назначения.
     add dword ptr [esp+3*4],8 ;команда pusha поместила в стэк значение esp
      ;до выполнения. Добавим 8 (команды call и pushf поместили в стэк 8 байт)
      ;чтобы отобразить значение esp до вызова процедуры.
     mov ebp,esp  ;сохраним значение esp в ebp для более удобного
      ;(и короткого) обращения к сохраненным в стэке значениям регистров.
      ;Обычно также поступают компиляторы языков высокого уровня и
      ;макросы, объявляющие процедуры.
     sub esp,OutBufferSize ;выделим OutBufferSize байт стэка для формирования строки
      ;Часто так выделяют память под небольшие локальные переменные.
     mov edi,esp
    ;содержимое памяти:
    ;edi=esp
    ; область паяти в OutBufferSize байт для формирования стрки
    ;ebp=esp+OutBufferSize=edi+OutBufferSize
    ; регистры общего назначения, помещенные командой pusha (8 регистров - 20h байт)
    ;ebp+20h
    ; регистр флагов (pushf)
    ;ebp+24h
    ; адрес возврата из процедуры (регистр eip)
     cld
     mov ecx,10
    AppendNextReg:
     push ecx
     mov al,'E'
    ;добавим к строке первый символ названия регистра
     stosb
     mov ax,word ptr [RegNames+ecx*2-2]
    ;второй и третий возмем из таблицы с именами
     stosw
     mov al,'='
     stosb
    
    ;получим значения регистра из стэка, переведем его в строку как шестнадцатиричное число
     mov edx,dword ptr [ebp+ecx*4-4]
     mov ecx,8
     add edi,ecx
     mov al,' '
     stosb
    ConvertNext4BitsToSym:
      mov eax,edx
      and al,0Fh
      add al,'0'
      cmp al,'9'
      jle NotSymbolNumber
      add al,'A'-'0'-10
    NotSymbolNumber:
      mov byte ptr [edi+ecx-1-9],al
      shr edx,4
     loop ConvertNext4BitsToSym
    
     pop ecx
     cmp cl,6
     jnz NotAppendNewString
     mov ax,0A0Dh
     stosw   ;добавим символы перевода строки,
      ;если отображено 5 регистров
    NotAppendNewString:
     loop AppendNextReg
    
     mov esi,offset FlagsStr
     mov ecx,offset FlagsStrSize
     rep movsb   ;добавим к строке со значениями регистров текст.
    
    ;выведем значения флагов (из стэка). Установленый флаг
      ;отображается заглавной буковй, сброшенный - прописной
     mov ecx,NumOfFlags
     mov edx,dword ptr [ebp+20h]   ;EFlags in stack
    DisplayNextFlag:
     lodsb
     cmp al,'x'   ;символом "x" в массиве с названиями флагов
      ;обозначены неотображаемые, не выводим.
     jz NotDisplayThisFlag
     mov ah,' '
     test dl,1   ;проверка состояния
     jnz FlagIs1
     or al,20h   ;переведем название флага из заглавного 
      ;в прописной (установим бит #5)
    FlagIs1:
     stosw
    NotDisplayThisFlag:
     shr edx,1   ;сдвинем вправо для отображения следующего флага
     loop DisplayNextFlag
    
     mov al,0   ;добавим ноль для обозначения конца строки
     stosb
    
     mov eax,esp
    extrn MessageBoxA:proc
     call MessageBoxA,0,eax,offset RegTitle,40h ;MB_ICONINFORMATION
     add esp,OutBufferSize  ;освободим выделенную память
     popa   ;восстановим регистры. Команда не восстанавливает 
      ;сохраненное значение esp из стэка
     popf    ;восстановим флаги
     ret    ;возврат из процедуры
    ;Конец процедуры DisplayRegs
    end start
    
       -------------------------------

     Содержание дальнейших выпусков зависит от вас. Будут разъясняться те темы, которые непонятны большинству читателей. Сайт рассылки: asm32.nm.ru. Присылайте свои вопросы и предложения по адресу: asm32@nm.ru .
       -------------------------------


    Автор рассылки Владимир Пронин. Любое коммерческое использование материалов рассылки без ведома и прямого согласия автора запрещено.
    Письма присланные автору рассылки могут быть опубликованы целиком или частично без предварительного уведомления. Если вы не хотите, чтобы ваше письмо было опубликовано укажите это в начале письма.


    В избранное