Овај чланак можда захтева чишћење и/или прерађивање како би се задовољили стандарди квалитета Википедије. Проблем: Недостају категорије или унутрашње везе, присутне су правописне и граматичке грешке, текст није разломљен на одељке и слично. |
x86 асемблерски језик је фамилија уназад-компатибилних асемблерских језика, који дају одређени ниво компатибилности све до Интел 8008. x86 асемблерски језици се користе за прављење објектног кода за процесоре из класе x86. Као и сви асемблерски језици, користи кратку мнемотехнику за репрезентовање основних инструкција које ЦПЈ може да може да разуме и прати. Компајлери понекад праве асемблерски код при преводу асемблерских језика у машински језик. Асемблерски језик се сматра програмским језиком, и он је машински специфичан и језик ниског нивоа. Овај језик се користи за мале уграђене системе у реалном времену, или језгра оперативних система.
Интел 8088 и 8086 су били први процесори који су имали x86 скуп инструкција. Ови 16-битни процесори су били еволуција претходне генерације 8-битних процесора од којих су наследили многе карактеристике и инструкције, који су проширени за 16-битну еру. 8088 и 8086 су користили 20-битну адресну магистралу и 16-битне унутрашње регистре, али 8086 је имао 16-битну магистралу за податке, а 8088 8-битни. x86 асемблерски језик подржава разне верзије које су пратиле ове, од Интелових 80188, 80186, 80286, 80386,80486, Pentium, Pentium Pro, и тако даље, као и АМД и Сајриксове 5x86 и K6 процесоре и НЕЦ В20. Термин x86 односи се на ЦПД који може да ради на оригиналном асемблерском језику.
Модерни x86 инструцкијски скуп је надскуп 8086 скупа инструкција и серија екстензија овом скупу инструкција које је почело са Интел 8080 процесором. Скоро потпуна бинарна компатибилност уназад постоји између Интел 8086 чипа и свих чипова до тренутне генерације x86 процесора. У пракси је нормално да се користе инструкције које ће радити на било чему после Интел 80386 или Пентијума, мада су у задњим годинама разни оперативни системи почели да захтевају модерније процесоре или бар да подржавају новије екстензије за инструкцијски скуп.
Свака x86 монтажна инструкција је представљена мнемотехником која комбинована са једним или више операнада, може да се претвори у један или више операционих кодова (опкода). НОП команда се преводи у 0x90, а ХЛТ инструкција се преводи у 0xF4.
x86 асемблерски језик има две главне бранше синтакси: Интел синтакса и АТиТ синтакса. Интел синтакса је домнантна у ДОС-у и Microsoft Windows свету, а АТиТ је доминантна у Јуникс свету, пошто је створена у АТиТ Беловим лабораторији. Овде је преглед главних разлика између Интел и АТиТ синтакси:
АТиТ | Интел | |
---|---|---|
редослед параметара | Извор пре одредишта
eax := 5 је mov $5, %eax |
Одредиште пре извора
eax := 5 је mov eax, 5 |
Parameter Size | На мнемотехнику се ставља суфикс са писмом које индијује величину операнада ("q" за qword, "l" за long (dword), "w" за word, и "b" за бајт)
addl $4, %esp |
Изведено из имена регистра који се користи (rax, eax, ax, al имплицирају q,l,w,b тим редом)
add esp, 4
|
Сигили тренутне вредности | Са префиксом "$", и регистри морају имати префикс "%" | Асемблер аутоматски детектује тип симбола, да ли су регистри, константе, или нешто друго. |
Ефективне адресе | Генерална синтакса DISP(BASE,INDEX,SCALE)
Пример: movl mem_location(%ebx,%ecx,4), %eax |
Користе промењиве, и морају да буду у угластим заградама; додатно, величинске речи као byte, word, или dword морају бити коришћени.
Пример: mov eax, dword [ebx + ecx*4 + mem_location]
|
Многи x86 асембелри користе Интел синтаксу, укључујући МАСМ, ТАСМ, НАСМ, ФАСМ, и ЈАСМ. ГАС је подржавао обе синтаксе под .интел_синтакс директивом.
x86 процесори имају колекцију регистара која је дозвољена за складиштење бинарних података. Заједно се подаци и адресни регистри зову генерални регистри. Сваки регистар има специјалну примену.
Заједно са генералним регистрима, ту су обично и:
ИП регистар показује на меморијски офсет следеће инструкције у кодном сегменту (показује на први бајт инструкције). Програмер не може приступити ИП регистру директно. x86 регистри се могу користити применом MOV инструкција. На пример:
mov ax, 1234h mov bx, ax
копира вредност 1234h (4660d) у регистар AX и онда копира вредност AX регистра у BX регистар (Интелова синтакса).
x86 архитектура у реалном и виртуелном 8086 режиму користи процес који се зове сегментација за адресирање меморије, а не равни меморијски модел који се користи у разним другим окружењима. Сегментација се састоји из компоновања меморијске адресе из два дела, сегмента и офсета. Сегмент показује на почетак групе од 64 kB адреса и офсет одређује колико је отакле удаљена жељена адреса. У сегментном адресирању, два регустра су потребна за комплетирање меморијске адресе: један да држи сегмент, а други да држи офсет. Да би се ово превело на равни меморијски модел, сегментна вредност је померена за 4 бита улево (еквавилентно множењу са 16) и онда додато на офсет да се направи цела адреса, што дозвољава ломљење 64к баријере кроз паметан избор адреса, иако то чини програмирање комплекснијим.
У реалном (заштићеном) режиму, ако DS садржи хексадецималну вредност 0xDEAD и DX садржи број 0xCAFE они би заједно показивали на адресу 0xDEAD * 0x10 + 0xCAFE = 0xEB5CE. Стога, ЦПЈ може адресирати до 1.048.576 бајтова (1 MB) у реалном режиму. Мешајући сегментну и офсет вредност налазимо 20-битну адресу.
Оригинални IBM ПЦ је забрањивао програме веће од 640 kB али проширена меморијска спецификација је коришћена за спровођење банке прекидачке шеме која се није користила када су каснији оперативни системи као што је Windows, користили веће адресне опсеге новијих процесора и спровели своје шеме виртуелне меморије.
Заштићени режим је започео са Интелом 80386. Неколико недостатака, као што је немогућност приступу БИОС-у и немогућност за враћањем на реални режим без ресетовања процесора су онемогућили широко коришћење. 80286 је такође био ограничен на адресну меморију у 16-битним сегментима, што значи да се може адресирати само 64 kB меморије одједном. За приступ проширеној функционалности 80286 процесор би поставио процесор у заштићени режим, што би омогућило 24-битно адресирање и 16 MB меморије.
У заштићеном режиму, сегментни селектор се може раставити на три дела: 13-битни индекс, индикатор табле који одређује да ли је улаз у GDT или LDT и двобитни Requested Privilege Level.
Када се говори о адреси са сегментом, и офсетирању нотације сегмента, офсет се користи, па се у горњем примеру равна адреса 0xEB5CE може написати као 0xDEAD:0xCAFE или као сегмент и офсет регистрованог пара; DS:DX.
Постоје специјалне комбинације сегментних регистара и генералних регистара које показују на битне адресе:
Интел 80386 има три оперативна режима: реални, заштићени и виртуелни режим. Заштићени режим, који је први пут био у 80286 је проширен за допуштање да 80386 адресира до 4 GB меморије, потпуно нови 8086 режим (VM86) је омогућио покретање једног или више реално-режимних програма у заштићеном окружењу које је емулирало реални режим, иако неку програми нису били компатибилни (типично због трикова у вези са адресирањем меморије или коришћењем неспецифираних опкодова).
Проширење заштићеног режима 32-битног равног меморијског модела 80386-ице је можда најбитнија промена у фамилији x86 процесора све док АМД није избацио x86-64 у 2003, пошто је помогло велико примање Windows-а 3.1 (који се ослањао на заштићени режим) пошто је Windows сада могао да покреће више апликација одједном, укључујући ДОС алпикцаије, коришћењем виртуелне меморије и простог мултитаскинга.
x86 процесори подржавају пет режима операција за x86 код, Реални режим, Заштићени режим, Дуги режим, Виртуелни 86 режим, и Режим за системски менаџмент, у ком су неке инструкције доступне а неке нису. 16-битни подскуп инструкција је доступан у реалном режиму (сви x86 процесори), 16-битни заштићени режим (80286 па надаље), V86 режим (80386 па надаље) и SMM (Интел i386SL, i486 и каснији). У 32-битном заштићеном режиму (Интел 80386 па надаље), 32-битне инструкције (укључукући каснија проширења) су такође доступни; у дугом режиму (АМД Оптерон па надаље), 64-битне инструкције, и више регистара су такође доступни. Инструкциони скуп је у сваком режима али мморијско адресирање и величина речи варирају, што захтева другачије програмерске стратегије.
Режими у којима се x86 код може извршити су:
Процесор улази у реални режим одмах након паљења, тако да оперативни систем или неки други програм морају експлицитно да га промене на други режим ако жели да ради у било чему осим у реалном режиму. Прекидачки режими су омогућени модификацијом одређених битова процесорских контролних регистра иако је нека припрема неопходна у највећем броју случајева, и неко чишћење после мењања.
Генерално, функције модерног x86 скупа инструкција су:
x86 архитектура има хадрверску подршку за извршавање стек механизма. Инструкције попут push, pop, call и ret се користе са стеком да алоцирају простор за локалне податке и да чувају и враћају call-return тачке. ret инструкцијаје веома корисна за имплементирање просторно ефикасних и брзих конвенција позивања где је позивани одговоран за враћање стек простора.
Када се поставља стек фрејм да чува локалне податке или рекурзивну процедуру постоји неколико избора. Инструкција на високом нивоу enter узима procedure-nesting-depth аргумент као и local size аргумент, и може да буде бржа од експлицитнијих манипулација регистрима. Да ли је бржа или не зависи од x86 имплементације, као и од конвенције позивања и кода потребном да ради на више процесора.
Пуни опсег адресних режима (укључујући тренутне и база + офсет) чак и за инструкције попут push и pop чини директно коришћење стека за цели број, покретни зарез и адресне податке једноставним, као што и АБИ спецификације и механизме истају једноставни у поређењу са неким РИСЦ архитектурама (захтевају експлицитније детаље позива стека).
x86 има стандардне математичке опције add, sub, mul, са idiv, логички оператори and, or, xor, neg аритметику за померање битова sal/sar, shl/shr, ротирање са и без ношења, rcl/rcr, rol/ror, комплемент БЦД аритметичких инструкција, aaa, aad, daa и друго.
x86 асемблерски језик садржи инструкције за јединицу за покретни зарез базиран на стеку. Она садржи сабирање, одузимање, негацију, множење, дељење, остатак, квадратни корнен и квадрирање. Операције такође садрже инструкције за конвертовање који могу да узимају или чувају вредност из меморије у свим следећим форматима: бинарно кодиране децимале, 32-битни цели бројеви, 64-битни цели бројеви, 64-битни бројевеи са покретним зарезом или 80-битни бројеви (који се након узимања конвертују у тренутно коришћен режим). x86 такође садржи функције попут синуса, косинуса, тангенса, аркус тангенса, рачунање са експонентима са базом 2 и логаритмовање са базама 2, 10 или е.
Стек регистар до стек регистар инструкције су обично fop st, st(*) или fop st(*), st, где је st еквевилентно st(0), и st(*) је један од 8 стек регистара (st(0), st(1), ..., st(7)). Као и цели бројеви, први операнд је и изворни операнд и дестинациони операнд. Додавање, одузимање, множење, дељење, чување и компензационе инструкције садрже инструкционе режиме који ће се попети на врх стека када су њихове инструкције извршене. Дакле на пример faddp st(1), st извршава рачунање st(1) = st(1) + st(0), онда уклања st(0) од врха стека, и тако мења оно што је био резултат у st(1)на врх стека у st(0).
Модерни x86 процесори садрже СИМД инстукције, које углавном одрађују исте операције паралелно на много вредносту кодираних у широком СИМД регустру. Разне инструкционе технологије подржавају различите операције на различитим регистарским скуповома, али гледано као целина (од MMX до SSE4.2) садрже генерална рачунања на целобројну или децималну аритметику (сабирање, одузимање, множење, shift, минимализација, максимизација, поређење, дељење или квадратни корен). Дакле на пример, paddw mm0, mm1 одрађује 4 паралелних 16-битних (индиковано w) целобројних додавања (индиковано padd) mm0 вредности на mm1 и чува резултате у mm0. Streaming SIMD Extensions или ССЕ такође садрже децимални режим у коме је само прва вредност регистра заправо модификована (тоје проширено у ССЕ2). Неке друге необичне инструкције су додате укључујући суму апсолутних разлика (коришћених за предвишање померања у компресији видеа, као што се ради у MPEG) и 16-битну акумулациону инструкцију (корисно за софтверско алфа блендовање и дигитално филтеровање). ССЕ (од ССЕ3) и 3DNow! екстензије садрже инструкције за сабирање и одузимањеза третирање упарених децималних вредности као што су комплексни бројеви.
Ови инстукцијски скупови такође садрже мноштво фискираних под-инструкција за мешање, убацивање и вађење вредности из тих регистара. Додатно ту су инструкције за померање података између целобројних регистара и XMM (коришћени у SSE)/FPU (коришћени у MMX) регистара.
x86 процесор такође садржи комплексне адресне режиме за адресирање меморије са тренутним офсетом, регистар, регистар са офсетом, скалирану верзију са офсетом, или без њега, и регистар са обционим офсетом и још један скалирани регистар. Тако на пример, један може да кодира mov eax, [Table + ebx + esi*4] као једну инструкцију која чита 32 бита података са адресе израчунате као (Table + ebx + esi * 4) офсет од ds селектора, и чува га у eax регистру. Генерално x86 могу да читају и корсите меморију погођену са величином било ког регистра на којем ради. (СИМД инструкције такође садрже инструкције за полу-увођење.) x86 инструкцијски скуп садржи читање стринга, чување, померање, скенирање и инструкције за поређење (lods, stos, movs, scas и cmps) које одрађују сваку операцију на специфирану величину (b за 8-битну, w за 16-битну и d за 32-битну дуплу реч) онда увећање и умањење (зависно од DF, direction flag) имплицитни адресни регистар (si за lods, di за stos за scas, и оба за movs и cmps). За читање, чување и операције скенирања, имплицитни регистар за циљ/извор/поређење је у al, ax или eax регистар (зависно од величине). Имплицитни сегменти регистри који се користе су ds за si и es за di. cx или ecx регистар се користи као бројач смањивања, и операција стаје кад бројач дође до нуле или (за скенирање или поређење) када је неједнакост детектована.
Стек је имплементиран са имплицитним декрементирањем (push) и инкрементирањем (pop) показивача на стек. У 16-битном режиму, овај имплицитни показивач стека је адресиран као SS:[SP], у 32-битном је SS:[ESP], а у 64-битном режиму је [RSP]. Показивач стека заправо показује на последњу вредност која је сачувана, под претпоставком да ће његова величина одговарати оперативном режиму процесора (16, 32, или 64 бита) да би одговарао нормалној ширини push/pop/call/ret инструкција. Такође су укључене enter и leave које чувају и бришу податке од врха стека док постављају стек фрејм показивач bp/ebp/rbp. Међутим, директно подешавање, или додавање или одузимање на sp/esp/rsp регистар је такође подржано, па су enter/leave инструкције генерално непотребне.
Овај код је почетак функције:
push ebp ; save calling function's stack frame (ebp) mov ebp, esp ; make a new stack frame on top of our caller's stack sub esp, 4 ; allocate 4 bytes of stack space for this function's local variables
и функционално је једнак:
enter 4, 0
Друге инструкције за манипулисање стеком укључују pushf/popf за чување и враћање (E)FLAGS регистра. pusha/popa инструкције ће чувати и враћати стање целог броја у и из стека.
Претпоставља се да су вредности за СИМД чување или враћање паковане на суседне позиције за СИМД регистар и да ће их поређати у секвенцијални низ. Неке SSE инструкције за чување и враћање захтевају 16-бајтно ређање да би функционисали. СИМД инстукциони скупови садрже и "prefetch" инструкције које одрађују узимање али не циљају ниједан регистар који се користи за узимање кеша.. SSE инструкцијски скуп такође садржи не-временске инстукције за чување које ће извршити чување право у меморију без алоцирања кеша ако одредиште није већ кеширано (иначе ће се понашати као регуларно чување).
Већина генеричких инструкција за целе и децималне бројеве (али не и СИМД) могу да користе један параметар као комплексну адресу као параметар другог извора. Целобројне инструкције такође могу прихватити један меморијски параметар као операнд одредишта.
Користећи interrupt 21h за излаз - други примери користе libc printf за писање на stdout.
.model small
.stack 100h
.data
msg db 'Hello world!$'
.code
start:
mov ah, 09h ; Прикажи поруку
lea dx, msg
int 21h
mov ax, 4C00h ; Прекини извршавање
int 21h
end start
; захтева /coff прекидач на верзији 6.15 и каснијим
.386
.model small,c
.stack 100h
.data
msg db "Hello World!",0
.code
includelib MSVCRT
extrn printf:near
extrn exit:near
public main
main proc
push offset msg
call printf
push 0
call exit
main endp
end main
;
; Овај програм ради у 32-битном заштићеном режиму.
; build: nasm -f elf -F stabs name.asm
; link: ld -o name name.o
;
; У 64-битном режиму се могу користити 64-битни регистри (rax уместо eax, rbx уместо ebx, итд.)
; Такође се мења "-f elf " за "-f elf64" у команди грађења.
;
section .data ; секција за иницијализоване податке
str: db 'Hello world!', 0Ah ; стринг поруке са карактером нове линије на крају (10 decimal)
str_len: equ $ - str ; израчунава дужину стринга у бајтовима одузимајући адресу ($ symbol)
; од str почетне адресе
section .text ; ово је кодна секција
global _start ; _start је улазна тачка и потребан му је глобалан обим да би био видљив
; linker - еквавилентан main() у C/C++
_start: ; процедура start
mov eax, 4 ; спецификовати sys_write код функције (од векторске табеле ОС-а)
mov ebx, 1 ; спецификовати опис фајла stdout -in Линукс, све се третира као фајл,
; чак и хардверски уређаји
mov ecx, str ; померити почетну _адресу_ стринг поруке до ecx регистра
mov edx, str_len ; померити дужину поруке (у бајтовима)
int 80h ; рећи језгру да изврши системски позив који је управо постављен
; у Линуксу се сервиси траже преко језгра
mov eax, 1 ; спецификовати sys_exit код функције (од векторске табеле ОС-а)
mov ebx, 0 ; спецификовати код враћања за ОС (0 = све је у реду)
int 80h ; рећи језгру да изврши системски позив
;
; Овај програм ради у 32-битном заштићеном режиму.
; gcc подразумевано веже стандардну C библиотеку
; build: nasm -f elf -F stabs name.asm
; link: gcc -o name name.o
;
; У 64-битном дугом режиму се могу користити 64-битни регистри (rax уместо eax, rbx уместо ebx, etc..)
; Такође променити "-f elf " за "-f elf64" у монтажној команди.
;
global main ;main мора бити дефинисан као да је компајлиран против C стандардне библиотеке
extern printf ;декларише коришћење ектерног симбола као што је printf декларисан у другом објектном модулу. Линкер касније решава ово.
segment .data ;секција за иницијализоване податке
string db 'Hello world!', 0Ah ;стринг поруке са новолинијским карактером на крају (10 decimal)
;стринг сада реферише на почетну адресу на којој је 'Hello, World' постављен.
segment .text
main:
push string ;гурнути адресу првог карактера стринга на стек. Ово ће бити аргумент за printf
call printf ;зове printf
add esp, 4 ;инкрементира показивач на стек за 4 бришући гурнути аргумент стринга
ret ;return
section .text
global _start, write
write:
mov al, 1 ; write syscall
syscall
ret
_start:
mov rax, 0x0a68732f6e69622f ; /bin/sh\n
push rax
xor rax, rax
mov rsi, rsp
mov rdi, 1
mov rdx, 8
call write
exit: ; just exit not a function
xor rax, rax
mov rax, 60
syscall
Заставе су коришћене за поређење у x86 архитектури. Када је поређење урађено између два податка, ЦПЈ поставља релевантну заставу или заставе. Пратећи ово, кондиционе jump инструкције се могу користити за проверу застава и гранање до кода који се треба извршити, на пример:
cmp eax, ebx
jne do_something
; ...
do_something:
; овде се нешто ради..
Заставе се такође корсите у x86 архитектури да се функције или режими упале и угасе. На пример, да се угасе сва маскабилна прекидања, може се користити следећа фунскција:
cli
Регистри флеша се такође могу директно адресирати. Нижих 8 битова заставиног регистра се може унети у ah
користећи lahf
инструкцију. Цео флеш регистар се може ставити и склонити са стека користећи инструкције pushf
, popf
, int
(укључујући into
) и iret
.
Инструкциони показивач се зове ip
у 16-битном режиму, eip
у 32-битном режиму, и rip
у 64-битном режиму. Регистар инструкционог показивача показује на меморијску адресу коју ће процесор следећи пут покушати да изврши; не може јој се директно приступити у 16-битном или 32-битном режиму, али секвенца као што следи се може написати да се постави адреса од next_line
у eax
:
call next_line
next_line:
pop eax
Ова секвенца инструкција генерише позиционо независтан код јер call
узима инструцкионом показивачу релативан тренутни операнд описујући офсет у бајтовима инструкције циљане инструкције од следеће инструкције, у овом случају 0.
Писање по инструкцијском показивачу је једноставно — jmp
инструкција поставља инструкциони показивач на циљану адресу, па на пример секвенца као што следи ће ставити садржај eax
у eip
:
jmp eax
У 64-битном режиму, инструкције могу референцирати податке релативне инструкционом показивачу, па има мање потребе за копирањем вредности инструкционог показивача на други регистар.