Quraşdırma təlimatları və maşın təlimatları. Quraşdırma və maşın təlimatları Ümumi təyinatlı registrlər

Proyektor üçün assemblerdə (x86) proqram yazıram. Əsas odur ki, avtomatik sürüşmə keçid rejimi var. Bu, slayd şousunu 10 saniyəlik artımlarla gecikdirməyinizi tələb edir. Belə bir gecikmə etməyimə kömək etdilər. Aşağıda iki prosedur var (gecikmənin yaradılması və gecikmənin azaldılması)

MakeDelay Proc Near mov al,Timer ;gecikmə dəyəri 10 ilə 90 vahid diapazonunda shr al,4 mov ah,al xor al,al ror ax,2 mov word ptr Delay+1,ax mov byte ptr Delay,0 mov byte ptr Gecikmə + 3.0 ret MakeDelay Endp

mov ax,word ptr Gecikmə və ya balta,word ptr Gecikmə + 2 cmp ax,0 ; je nxtslide ;Bəli - slayd seçiminə keçin alt söz ptr Gecikmə,1 ;Xeyr - sbb sözünü azaltmaq üçün gedin ptr Gecikmə + 2,0 ;Gecikmələr jmp ext5 ;Alt proqramdan çıxın

Birinci prosedurda, ümumiyyətlə, belə bir gecikmə alqoritminə necə gəldikləri aydın deyil. Və gecikmənin azaldılması prosedurunda disjunksiyanı niyə etmək aydın deyil. Problem ondadır ki, gecikmənin həyata keçirilməsi əməliyyat sistemindən çox asılıdır. Və, məsələn, Virtual Windows xp-də dəyərin 60-a təyin edilməsi 65 saniyə, Windows 7-də isə 50 saniyə gecikmə ilə nəticələnir. Xahiş edirəm bu qarışıqlıqdan qurtulmağıma kömək edin.

Tapşırıq kodu: "Assembler x86"

mətn

Proqram siyahısı

ACP proc yaxınlığında düzgün dəyər ADC push balta ilə ;bu rutin push dx-də istifadə olunan registrləri yadda saxla ; mov al,01h ;"Start"-ı yandırın 03h,al ; Gözləyir: al,02h ;test al,01saatda "Rdy"nin yanmasını gözləyin; jz Gözləyirik; mov al,0 ; "Başla"nı sıfırla 03h,al ; al,01h-də ;giriş registrindəki qiyməti oxuyun mov ah,10d ; mov dl,0FFh ;giriş registrinə tətbiq oluna bilən maksimum ədədi dl-ə yükləyin mul ah ;giriş registrindən gələn ədədi ADC-nin yuxarı həddi (maksimum gərginlik) div dl ;nəticəni maksimuma bölün giriş registrindəki dəyər və 1..10 mov skor_ACP,al pop dx pop ax ret ACP endp diapazonundan nömrə alın

kimi hesab edilə bilər avtokod(aşağıya bax), konstruksiyalarla genişləndirilir. Əsasən platformadan asılıdır. Müxtəlif aparat platformaları üçün montaj dilləri uyğun gəlmir, baxmayaraq ki, onlar ümumiyyətlə oxşar ola bilər.

Rus dilində bunu sadəcə adlandırmaq olar " montajçı” (“assemblerdə proqram yazmaq” kimi ifadələr tipikdir), bu, ciddi şəkildə desək, doğru deyil, çünki montajçı ilə proqramı tərcümə etmək üçün köməkçi proqram montaj dili kompüter koduna daxil edin.

Ümumi tərif

Assembly dili, maşın kodunda yazılmış proqramları oxunaqlı formada təqdim etmək üçün istifadə olunan qeyddir. Assambleya dili proqramçıya əlifba mnemonik əməliyyat kodlarından istifadə etməyə, öz mülahizəsinə uyğun olaraq kompüter registrlərinə və yaddaşına simvolik adlar təyin etməyə, həmçinin onun üçün əlverişli (məsələn, indeks və ya dolayı) ünvanlama sxemlərini təyin etməyə imkan verir. Bundan əlavə, istifadə etməyə imkan verir müxtəlif sistemlər rəqəmsal sabitləri təmsil etmək üçün hesablama (məsələn, onluq və ya onaltılıq) və proqram sətirlərini simvolik adları olan etiketlərlə qeyd etməyə imkan verir ki, onlara proqramın digər hissələrindən (ünvanla deyil, adla) daxil olmaq mümkün olsun (məsələn, nəzarəti ötürmək).

Assembly dili proqramının icra edilə bilən maşın koduna tərcüməsi (ifadələrin hesablanması, makroların genişləndirilməsi, mnemonikanın müvafiq maşın kodları ilə və simvolik ünvanların mütləq və ya nisbi ünvanlarla əvəz edilməsi) həyata keçirilir. montajçı- assembler dilinə adını verən tərcüməçi proqramı.

Assembly dili təlimatları prosessor təlimatlarına birə bir uyğun gəlir. Əslində, onlar bir insan üçün daha əlverişli olan simvolik qeyd formasını təmsil edirlər - mnemokodlar- əmrlər və onların arqumentləri. Bu halda prosessor təlimatlarının bir neçə variantı bir montaj dili təlimatına uyğun gələ bilər.

Bundan əlavə, montaj dili yaddaş xanalarının ünvanları əvəzinə simvolik etiketlərdən istifadə etməyə imkan verir ki, onlar montaj zamanı assembler və ya əlaqələndirici tərəfindən hesablanmış mütləq və ya nisbi ünvanlarla, eləcə də sözdə direktivlər(prosessorun maşın göstərişlərinə çevrilməyən, lakin assemblerin özü tərəfindən yerinə yetirilən montajçı təlimatları).

Assembly direktivləri, xüsusən, məlumat bloklarını daxil etməyə, proqram fraqmentinin yığılmasını şərtlə təyin etməyə, etiket dəyərlərini təyin etməyə, parametrləri olan makrolardan istifadə etməyə imkan verir.

Prosessorların hər bir modelinin (və ya ailəsinin) özünəməxsus təlimatlar sistemi və müvafiq montaj dili var. Ən məşhur montaj dili sintaksisləri Intel sintaksisi və AT&T sintaksisidir.

Maşın dili kimi yüksək səviyyəli proqramlaşdırma dilini (Fort, Lisp, El-76) həyata keçirən kompüterlər var. Əslində belə kompüterlərdə montaj dilləri rolunu yerinə yetirirlər.

İmkanlar

Assembly dilinin istifadəsi proqramçıya yüksək səviyyəli dillərdə proqramlaşdırma zamanı adətən mövcud olmayan bir sıra funksiyaları təmin edir. Onların əksəriyyəti dilin hardware platformasına yaxınlığı ilə bağlıdır.

  • Aparat platformasının bütün xüsusiyyətlərindən tam istifadə etmək imkanı nəzəri olaraq verilmiş prosessor üçün mümkün olan ən sürətli və ən yığcam kodu yazmağa imkan verir. Bacarıqlı bir proqramçı, bir qayda olaraq, bir və ya bir neçə parametrdə yüksək səviyyəli dildən olan tərcüməçi ilə müqayisədə proqramı əhəmiyyətli dərəcədə optimallaşdıra və Pareto optimalına yaxın kod yarada bilər (bir qayda olaraq, proqramın sürəti kodu uzatmaqla əldə edilir və əksinə):
    • prosessor resurslarının daha rasional istifadəsi, məsələn, registrlərdə bütün ilkin məlumatların ən səmərəli yerləşdirilməsi sayəsində RAM-a lazımsız girişi aradan qaldırmaq mümkündür;
    • hesablamaların əl ilə optimallaşdırılması, o cümlədən aralıq nəticələrin daha səmərəli istifadəsi hesabına kodun miqdarını azaltmaq və proqramın sürətini artırmaq olar.
  • Avadanlıqlara, xüsusən də giriş-çıxış portlarına, xüsusi yaddaş ünvanlarına, prosessor registrlərinə birbaşa daxil olmaq imkanı (lakin bu qabiliyyət bir çox əməliyyat sistemlərində tətbiq proqramlarından periferik registrlərə yazmaq üçün birbaşa çıxışın olması ilə əhəmiyyətli dərəcədə məhdudlaşır. avadanlıq etibarlılıq sisteminin işləməsi üçün bloklanır).

Yaradarkən assemblerdən istifadənin demək olar ki, alternativi yoxdur:

  • periferik cihazların işini mərkəzi prosessorla əlaqələndirmək vacib olduqda, aparat drayverləri və əməliyyat sisteminin nüvəsi (ən azı ƏS nüvəsinin maşından asılı alt sistemləri);
  • məhdud ROM-da saxlanmalı və/və ya məhdud performansa malik cihazlarda (kompüterlərin və müxtəlif elektron cihazların proqram təminatı) işlədilməli olan proqramlar
  • platforma uyğunluğunu həyata keçirən yüksək səviyyəli dillərin, sistem kitabxanalarının və kodun tərtibçiləri və tərcüməçilərinin platformaya xas komponentləri.

Ayrı-ayrılıqda qeyd etmək olar ki, disassembler proqramının köməyi ilə tərtib edilmiş proqramı assembler dilində proqrama çevirmək mümkündür. Əksər hallarda, proqramın yüksək səviyyəli dildə mənbə kodu mövcud olmadıqda, proqramın alqoritmlərini tərsinə çevirməyin yeganə (çox vaxt aparan) yolu budur.

Məhdudiyyətlər

Ərizə

Tarixən maşın kodları proqramlaşdırma dillərinin birinci nəsli hesab edilirsə, o zaman assembler dilini ikinci nəsil proqramlaşdırma dilləri hesab etmək olar. Assembly dilinin çatışmazlıqları, onun üzərində iri proqram sistemlərinin işlənib hazırlanmasının mürəkkəbliyi üçüncü nəsil dillərin - yüksək səviyyəli proqramlaşdırma dillərinin (Fortran, Lisp, Cobol, Pascal, C və s. kimi) yaranmasına səbəb oldu. ). Hazırda əsasən informasiya texnologiyaları sənayesində istifadə olunan yüksək səviyyəli proqramlaşdırma dilləri və onların davamçılarıdır. Bununla belə, montaj dilləri səmərəlilik baxımından unikal üstünlükləri və müəyyən bir platformanın spesifik xüsusiyyətlərindən tam istifadə etmək qabiliyyətinə görə öz yerini saxlayır.

Proqramlar və ya onların fraqmentləri kritik olduğu hallarda assembler dilində yazılır:

  • performans (sürücülər, oyunlar);
  • istifadə olunan yaddaşın həcmi (yükləmə sektorları, quraşdırılmış (ing. gömülü) proqram təminatı, məhdud resursları olan mikrokontrollerlər və prosessorlar üçün proqramlar, viruslar, proqram təminatının mühafizəsi).

Assembly dili proqramlaşdırmadan istifadə edərək aşağıdakılar istehsal olunur:

  • C++ və ya Pascal kimi yüksək səviyyəli dillərdəki proqramlarda proqramların kritik sürət bölmələrinin optimallaşdırılması. Bu, xüsusilə sabit performansa malik oyun konsolları və daha az resurs tələb edən və daha sürətli olan multimedia kodekləri üçün doğrudur.
  • Əməliyyat sistemlərinin (ƏS) və ya onların komponentlərinin yaradılması. Hal-hazırda, əməliyyat sistemlərinin böyük əksəriyyəti daha yüksək səviyyəli dillərdə (əsasən C, UNIX-in ilk versiyalarından birini yazmaq üçün xüsusi olaraq yaradılmış yüksək səviyyəli dil) yazılmışdır. ƏS yükləyicisi, aparat abstraksiya təbəqəsi və ləpə kimi hardware-asılı kod parçaları çox vaxt montaj dilində yazılır. Əslində, Windows və ya Linux nüvələrində montaj kodu çox azdır, çünki müəlliflər daşıma qabiliyyətini və etibarlılığı təmin etməyə çalışırlar, lakin buna baxmayaraq, mövcuddur. MenuetOS və KolibriOS kimi bəzi həvəskar ƏS-lər tamamilə montaj dilində yazılmışdır. Eyni zamanda, MenuetOS və KolibriOS disketdə yerləşdirilir və qrafik çox pəncərəli interfeysi ehtiva edir.
  • Mikrokontrollerlərin (MC) və digər quraşdırılmış prosessorların proqramlaşdırılması. Professor Tanenbaumun fikrincə, MC-nin inkişafı müasir kompüterlərin tarixi inkişafını təkrarlayır. İndi (2013), montaj dili MK proqramlaşdırması üçün çox istifadə olunur (baxmayaraq ki, C kimi dillər də bu sahədə geniş istifadə olunur). MK-da siz ayrı-ayrı baytları və bitləri müxtəlif yaddaş hüceyrələri arasında köçürməlisiniz. MK proqramlaşdırması çox vacibdir, çünki Tanenbaumun fikrincə, müasir sivil bir insanın avtomobilində və mənzilində orta hesabla 50 mikrokontroller var.
  • Sürücülərin yaradılması. Sürücülər (və ya onların bəzi proqram modulları) montaj dilində proqram. Baxmayaraq ki, hazırda müasir prosessorların etibarlılığına və kifayət qədər performansına artan tələblər səbəbindən sürücülər də yüksək səviyyəli dillərdə yazmağa meyllidirlər (yüksək səviyyəli dildə etibarlı sürücü yazmaq daha asandır). cihazda və prosessorda proseslərin vaxtı) və yüksək səviyyəli dillərə malik kompilyatorların kifayət qədər təkmilləşdirilməsi (yaradılan kodda lazımsız məlumat ötürülməsinin olmaması), müasir sürücülərin böyük əksəriyyəti montaj dilində yazılmışdır. Sürücülər üçün etibarlılıq xüsusi rol oynayır, çünki Windows NT və UNIX-də (Linux daxil olmaqla) drayverlər sistemin nüvə rejimində işləyir. Sürücüdəki bir incə səhv bütün sistemi sıradan çıxara bilər.
  • Antivirusların və digər qoruyucu proqramların yaradılması.
  • Proqramlaşdırma dillərinin tərcüməçilərinin aşağı səviyyəli kitabxanaları üçün kodun yazılması.

Müxtəlif dillərdə proqramların əlaqələndirilməsi

Uzun müddətdir ki, yalnız proqram fraqmentləri tez-tez assembler dilində kodlaşdırıldığına görə, onlar digər proqramlaşdırma dillərində yazılmış proqram sisteminin qalan hissəsi ilə əlaqələndirilməlidir. Buna iki əsas yolla nail olunur:

  • Kompilyasiya mərhələsində - xüsusi dil direktivlərindən istifadə etməklə yüksək səviyyəli dildə proqramın mənbə koduna assembler fraqmentlərinin (ing. inline assembler) daxil edilməsi. Metod sadə verilənlərin çevrilməsi üçün əlverişlidir, lakin yüksək səviyyəli dil tərəfindən dəstəklənməyən çoxlu giriş və çıxışları olan alt proqramlar da daxil olmaqla, verilənlər və alt proqramlarla tam hüquqlu assembler kodu hazırlamaq mümkün deyil.
  • Ayrı-ayrılıqda tərtib edərkən keçid mərhələsində . Birləşdirilə bilən modulların qarşılıqlı əlaqədə olması üçün idxal edilmiş funksiyaların (bəzi modullarda müəyyən edilir və digərlərində istifadə olunur) müəyyən çağırış konvensiyalarını dəstəkləməsi kifayətdir. Ayrı-ayrı modullar montaj dili də daxil olmaqla istənilən dildə yazıla bilər.

Sintaksis

Assembly dili sintaksisi müəyyən bir prosessorun təlimat dəsti ilə müəyyən edilir.

Komanda dəsti

Tipik montaj dili əmrləri (əksər nümunələr x86 arxitekturasının Intel sintaksisi üçün verilmişdir):

  • Məlumat ötürmə əmrləri (mov və s.)
  • Arifmetik əmrlər (əlavə et, alt, imul və s.)
  • Məntiqi və bit əməliyyatları (və ya , və , xor , shr və s.)
  • Proqram axınına nəzarət əmrləri (jmp, loop, ret və s.)
  • Zəngin dayandırılması təlimatları (bəzən nəzarət təlimatları da adlandırılır): int
  • Portlara giriş/çıxış əmrləri (giriş, çıxış)
  • Mikrokontrollerlər və mikrokompüterlər həmçinin şərtlə yoxlama və keçidləri yerinə yetirən əmrlərlə xarakterizə olunur, məsələn:
  • cjne - bərabər deyilsə tullanmaq
  • djnz - azalma və nəticə sıfır deyilsə, atlayın
  • cfsneq - müqayisə edin və bərabər deyilsə, növbəti əmri keçin

Təlimatlar

Tipik Əmr Yazma Formatı

[etiket:] [ [prefiks] mnemokod [operand (, operand)] ] [ ;şərh]

harada mnemokod- təlimatın prosessora birbaşa mnemonikası. Ona prefikslər əlavə edilə bilər (təkrarlar, ünvan tiplərinin dəyişməsi və s.).

İstifadə olunan mnemonika adətən eyni arxitekturanın və ya arxitektura ailəsinin bütün prosessorları üçün eynidir (geniş tanınanlar arasında x86, ARM, SPARC, PowerPC, M68k prosessoru və nəzarətçi mnemonikaları var). Onlar prosessorun spesifikasiyasında təsvir edilmişdir. Mümkün istisnalar:

  • assembler platformalararası AT&T sintaksisindən istifadə edirsə (orijinal mnemonikalar AT&T sintaksisinə çevrilir);
  • əvvəlcə mnemonika yazmaq üçün iki standart varsa (təlimat sistemi başqa istehsalçının prosessorundan miras qalmışdır).

Məsələn, Zilog Z80 prosessoru Intel 8080 təlimat dəstini miras aldı, onu genişləndirdi və mnemonikaları (və qeydləri) özünəməxsus şəkildə dəyişdirdi. Motorola Fireball prosessorları Z80 təlimat dəstini miras aldı və onu bir qədər azaldıb. Eyni zamanda, Motorola rəsmi olaraq Intel mnemonikasına qayıdıb və hazırda Fireball montajçılarının yarısı Intel mnemonikaları ilə, yarısı isə Zilog mnemonikaları ilə işləyir.

direktivlər

Assembly dili proqramı ehtiva edə bilər direktivlər: birbaşa maşın təlimatlarına çevrilməyən, lakin kompilyatorun işinə nəzarət edən təlimatlar. Onların dəsti və sintaksisi əhəmiyyətli dərəcədə dəyişir və aparat platformasından deyil, istifadə olunan tərcüməçidən asılıdır (eyni memarlıq ailəsi daxilində dillərin dialektlərinin yaranmasına səbəb olur). Direktivlərin "centlmen dəsti" olaraq aşağıdakıları ayırd etmək olar:

  • məlumatların tərifi (sabitlər və dəyişənlər),
  • proqramın yaddaşda təşkilini və çıxış faylının parametrlərini idarə etmək,
  • kompilyator rejiminin qurulması,
  • bütün növ abstraksiyalar (yəni yüksək səviyyəli dillərin elementləri) - prosedurların və funksiyaların dizaynından (prosedur proqramlaşdırma paradiqmasının həyata keçirilməsini sadələşdirmək üçün) şərti strukturlara və dövrələrə (strukturlaşdırılmış proqramlaşdırma paradiqması üçün),

Proqram nümunəsi

Proqram nümunələri Salam, dünya! müxtəlif platformalar və müxtəlif dialektlər üçün:

SECTION .data msg: db " Hello , world " , 10 len: equ $-msg SECTION .text global _start _start: mov edx, len mov ecx, msg mov ebx, 1 ; stdout mov eax, 4; write(2) int 0x80 mov ebx , 0 mov eax , 1 ; exit (2) int 0x80

SECTION .data msg: db " Hello, world " , 10 len: equ $-msg SECTION .text global _start syscall: int 0x80 ret _start: push len push msg push 1 ; stdout mov eax, 4; write(2) zəng sistemi zəngi əlavə et esp , 3 * 4 təkan 0 mov eax , 1 ; exit(2) çağırış sistemi

386 .model flat , stdcall variantı casemap : none include \ masm32 \ include \ windows.inc include \ masm32 \ include \ kernel32.inc includelib \ masm32 \ lib \ kernel32.lib .data msg db " Salam , dünya " , 13 , 10 len equ $-msg .data ? yazılı dd ? .code start: push - 11 zəng GetStdHandle push 0 push OFFSET yazılı push len push OFFSET msg push eax zəng WriteFile push 0 zəng ÇıxışProsesi başa çatdırmaq

format PE konsolu giriş başlanğıcına " daxil \ win32a.inc " bölməsi " .data " verilənləri oxuna bilən yazıla bilən mesaj db " Salam , dünya ! " , 0 bölmə " .code " kodu oxuna bilən icra edilə bilən başlanğıc daxildir: ; FASM-də CINVOKE makrosu. ; CDECL funksiyalarına zəng etməyə imkan verir. cinvoke printf , cinvoke getch mesajı ; INVOKE STDCALL funksiyaları üçün oxşar makrodur. ExitProcess-i çağırın , 0 bölmə " .idata " verilənlərin oxuna bilən kitabxana nüvəsini , " kernel32.dll " , \ msvcrt , " msvcrt.dll " idxal nüvəsini , \ ExitProcess , " ExitProcess " idxal msvcrt , \ printf , " printf " , \ getch , "_getch"

;yasm-1.0.0-win32.exe -f win64 HelloWorld_Yasm.asm;setenv / Buraxılış /x64 /xp ;link HelloWorld_Yasm.obj Kernel32.lib User32.lib /entry:main /altsistem:windows /BÜYÜK ADRES:NO bit 64 qlobal əsas xarici MessageBoxA extern ExitProcess bölməsi .data mytit db " The 64-bit world of Windows & assembler... " , 0 mymsg db " Hello World !" , 0 bölmə .mətn əsas: mov r9d , 0 ; uType = MB_OK mov r8, mytit; LPCSTR lpCaption mov rdx, mymsg; LPCSTR lpText mov rcx, 0; hWnd = HWND_DESKTOP zəng MessageBoxA mov ecx , eax ; uExitCode = MessageBox(...) ExitProcess-ə zəng edin

Bölmə ".data" salam: .asciz "Salam Dünya!\n" .bölmə ".text" .align 4 .qlobal əsas əsas: yadda saxla %sp , - 96 , %sp ! yaddaş ayırmaq mov 4 , %g1 ! 4 = YAZ (sistem çağırışı) mov 1 , %o0 ! 1 = STDOUT set salam , %o1 mov 14 , %o2 ! simvol sayı 8 ! sistem zəngi! proqramdan çıx mov 1 , %g1 ! 1-i (çıxış () sistem zəngi ) %g1 mov 0, %o0-a köçürün! 0-ı (qaytarma ünvanı) %o0 ta 8-ə köçürün! sistem çağırışı

ORG 7 C00H USE16 JMP Kodu NOP DB "HELLOWRD" Sectsize DW 00200 H Clustsize DB 001 H Ressecs DW 00001 H Fatcnt DB 002 H Rootsiz DW 000 E0H Totsecs DW 000 E0H Totsecs DWH 000 E0H Totsecs DWH 0200c DWH Median DW000c DWH Median DW000c DWH 0200 Clustsize DW 00200 H Clustsize h HidnSec dw 00000 h kodu: cli mov ax , cs mov ds , ax mov ss , ax mov sp , 7 c00h sti mov ax , 0 b800h mov es , ax mov di , 200 mov ah , 2 mov bx:print _Str , al ,[ cs : bx ] mov [ es : di ], ax inc bx add di , 2 cmp bx , MessEnd jnz msg_print loo: jmp loo MessStr equ $ Mesaj db " Salam , Dünya ! " MessEnd equ $

Tarix və terminologiya

Bu dil növü öz adını bu dillərdən olan tərcüməçinin (tərtibçinin) adından almışdır - assembler (ingiliscə assembler - assembler). Adı, proqramın "avtomatik olaraq yığılması" və birbaşa kodlarda əl ilə əmr-əmr daxil edilməməsi ilə əlaqədardır. Eyni zamanda, terminlərin qarışıqlığı var: assembler çox vaxt təkcə tərcüməçi deyil, həm də müvafiq proqramlaşdırma dili (“assembler proqramı”) adlanır.

Bağışlayın, bizim xilaskarımız, montajçı haqqında danışmağa bir dəqiqəniz varmı? Sonuncu məqalədə biz ilk salam dünya tətbiqimizi asma-da yazdıq, onu tərtib etməyi və diskussiya etməyi öyrəndik, həmçinin Linux-da sistem zəngləri etməyi öyrəndik. Bu gün biz birbaşa assembler təlimatları, registrlər anlayışı, stek və bütün bunlarla tanış olacağıq. x86 (a.k.a i386) və x64 (a.k.a amd64) arxitekturaları üçün montajçılar çox oxşardır və buna görə də onları ayrı-ayrı məqalələrdə nəzərdən keçirməyin mənası yoxdur. Üstəlik, əgər varsa, x86-dan fərqləri qeyd edərək, x64-ə diqqət yetirməyə çalışacağam. Aşağıdakılar, məsələn, yığının yığından necə fərqləndiyini artıq bildiyinizi güman edir və belə şeyləri izah etməyə ehtiyac yoxdur.

Ümumi təyinatlı registrlər

Registr son dərəcə yüksək olan prosessorda kiçik (adətən 4 və ya 8 bayt) yaddaş parçasıdır yüksək sürət Giriş. Reyestrlər registrlərə bölünür xüsusi təyinatlı və ümumi registrlər. Biz indi ümumi təyinatlı registrlərlə maraqlanırıq. Adından da təxmin edə bildiyiniz kimi, proqram bu registrlərdən istədiyi kimi öz ehtiyacları üçün istifadə edə bilər.

X86-da səkkiz 32 bitlik ümumi təyinatlı registr mövcuddur - eax, ebx, ecx, edx, esp, ebp, esi və edi. Registrlərin əvvəlcədən təyin edilmiş növü yoxdur, yəni onlara işarəli və ya işarəsiz tam ədədlər, göstəricilər, mantiq, ASCII simvol kodları və s. kimi baxıla bilər. Baxmayaraq ki, nəzəri olaraq bu registrlərdən hər hansı bir şəkildə istifadə oluna bilər, praktikada hər bir registr adətən müəyyən şəkildə istifadə olunur. Beləliklə, esp yığının yuxarısını göstərir, ecx sayğac rolunu oynayır, eax isə əməliyyat və ya prosedurun nəticəsidir. 16 bitlik ax, bx, cx, dx, sp, bp, si və di registrləri var ki, bunlar müvafiq 32 bitlik registrlərin ən az əhəmiyyətli 16 bitidir. Həmçinin, müvafiq olaraq ax, bx, cx və dx-in yüksək və aşağı baytlarını təmsil edən ah, al, bh, bl, ch, cl, dh və dl 8 bitlik registrlər də mövcuddur.

Məsələni nəzərdən keçirək. Tutaq ki, aşağıdakı üç göstəriş yerinə yetirilir:

(gdb) x/3i $pc
=> 0x8048074: mov $0xaabbccdd,%eax
0x8048079: mov $0xee,%al
0x804807b: mov $0x1234,%ax

Eax-a 0 yazdıqdan sonra dəyərləri qeyd edin x AABCCCDD:

(gdb) p/x $eks
$1 = 0xaabbccdd
(gdb) p/x $ax
$2 = 0xccdd
(gdb) p/x $ah
$3 = 0xcc
(gdb) p/x $al
$4 = 0xdd

Qeydiyyatdan keçmək üçün 0 yazdıqdan sonra dəyərlər al x EE:

(gdb) p/x $eks
$5 = 0xaabbccee
(gdb) p/x $ax
$6 = 0xccee
(gdb) p/x $ah
$7 = 0xcc
(gdb) p/x $al
$8 = 0xee

Balta 0 yazdıqdan sonra dəyərləri qeyd edin x 1234:

(gdb) p/x $eks
$9 = 0xaabb1234
(gdb) p/x $ax
$10 = 0x1234
(gdb) p/x $ah
$11 = 0x12
(gdb) p/x $al
$12 = 0x34

Gördüyünüz kimi, mürəkkəb bir şey yoxdur.

Qeyd: GAS sintaksisi b (bayt), w (söz, 2 bayt), l (uzun söz, 4 bayt), q (dörd söz, 8 bayt) və bəzi digər şəkilçilərdən istifadə edərək operandların ölçülərini açıq şəkildə təyin etməyə imkan verir. Məsələn, əmr yerinə mov $0xEE , % al yaza bilersiniz movb $0xEE , %al , əvəzinə mov $0x1234 , % axmovw $0x1234 , %ax , və s. Müasir GAS-da bu şəkilçilər isteğe bağlıdır və mən şəxsən onlardan istifadə etmirəm. Ancaq onları başqasının kodunda görsəniz, narahat olmayın.

X64-də registr ölçüsü 64 bitə qədər artırıldı. Müvafiq registrlər rax, rbx və s. adlanır. Bundan əlavə, səkkiz əvəzinə on altı ümumi təyinatlı registr var. Əlavə registrlər r8, r9, ..., r15 adlanır. Aşağı 32, 16 və 8 bitləri təmsil edən müvafiq registrlər r8d, r8w, r8b adlanır və r9-r15 registrləri üçün analoji olaraq. Bundan əlavə, rsi, rdi, rbp və rsp registrlərinin aşağı 8 biti olan registrlər meydana çıxdı - müvafiq olaraq sil, dil, bpl və spl.

Ünvanlama haqqında

Artıq qeyd edildiyi kimi, registrlər yaddaşda olan məlumatların göstəriciləri kimi qəbul edilə bilər. Bu cür göstəricilərə istinad etmək üçün xüsusi sintaksis istifadə olunur:

mov(%rsp) , %rax

Bu giriş "rsp registrindəki ünvandan 8 baytı oxuyun və onları rax registrində saxlayın" deməkdir. Proqram işə salındıqda, rsp proqrama ötürülən arqumentlərin sayını (argc), həmin arqumentlərə göstəriciləri, həmçinin mühit dəyişənlərini və bəzi digər məlumatları saxlayan yığının yuxarı hissəsini göstərir. Beləliklə, yuxarıda göstərilən təlimatın yerinə yetirilməsi nəticəsində (təbii ki, ondan əvvəl başqa heç bir təlimat icra edilməmək şərti ilə) proqramın işə salındığı arqumentlərin sayı rax-a yazılacaqdır.

Bir əmrdə siz ünvanı və ona nisbətdə ofseti (həm müsbət, həm də mənfi) təyin edə bilərsiniz:

mov 8 (% rsp ) , % rax

Bu giriş "rsp götürün, ona 8 əlavə edin, nəticədə çıxan ünvanda 8 baytı oxuyun və onları rax-a qoyun" deməkdir. Beləliklə, rax proqramın birinci arqumentini təmsil edən sətirin ünvanını, yəni icra olunan faylın adını ehtiva edəcəkdir.

Massivlərlə işləyərkən müəyyən indeksdə olan elementə müraciət etmək rahat ola bilər. Müvafiq sintaksis:

# xchg təlimatı dəyərləri dəyişdirir
xchg 16 (% rsp , % rcx , 8 ) , % rax

Bu belə oxunur: "rcx*8 + rsp + 16 hesablayın və nəticədə 8 baytı (registr ölçüsü) və rax registrinin dəyərini dəyişdirin." Başqa sözlə, rsp və 16 hələ də ofset rolunu oynayır, rcx massivdə indeks rolunu oynayır, 8 isə massiv elementinin ölçüsüdür. Bu sintaksisi istifadə edərkən, icazə verilən yeganə element ölçüləri 1, 2, 4 və 8-dir. Əgər başqa ölçü tələb olunarsa, siz vurma, ikili sürüşmə və digər təlimatlardan istifadə edə bilərsiniz, biz bundan sonra nəzərdən keçirəcəyik.

Nəhayət, aşağıdakı kod da etibarlıdır:

Data
msj:
. ascii "Salam, dünya!\n"
. mətn

Qlobl_start
_başlamaq:
# rcx sıfırlayın
xor %rcx , %rcx
mov msg(,% rcx , 8 ) , % al
mov mesajı, %ah

O mənada ki, siz ofset və ya heç bir registr ilə registr təyin edə bilməzsiniz. Bu kodun icrası nəticəsində al və ah registrlərinə H hərfinin ASCII kodu və ya 0 yazılacaq. x 48.

Bu kontekstdə daha bir faydalı assembler təlimatını qeyd etmək istərdim:

# rax:= rcx*8 + rax + 123
lea 123 (% rax , % rcx , 8 ) , % rax

Lea təlimatı çox rahatdır, çünki o, eyni anda vurma və çoxlu əlavələr etməyə imkan verir.

əyləncəli faktlar! X64-də təlimat bayt kodu heç vaxt 64 bitlik ofsetlərdən istifadə etmir. X86-dan fərqli olaraq, təlimatlar çox vaxt mütləq ünvanlarla deyil, təlimatın özünə aid ünvanlarla işləyir ki, bu da sizə ən yaxın +/- 2 GB RAM-a daxil olmağa imkan verir. Müvafiq sintaksis:

movb msg(% rip) , % al

Gəlin "müntəzəm" və "nisbi" mov əməliyyat kodlarının (objdump -d ) uzunluqlarını müqayisə edək:

4000b0: 8a 0c 25 e8 00 60 00 mov 0x6000e8,%cl
4000b7: 8a 05 2b 00 20 00 mov 0x20002b(%rip),%al # 0x6000e8

Gördüyünüz kimi, "qohum" mov da bir bayt qısadır! Bu rip nə cür reyestrdir, biz bir az aşağıda öyrənəcəyik.

Reyestrə tam 64 bitlik dəyəri yazmaq üçün xüsusi təlimat verilir:

movabs $0x1122334455667788 , %rax

Başqa sözlə desək, x64 prosessorları təlimatları x86 prosessorları kimi az-az kodlayır və bu gün bir neçə giqabayt və ya daha az operativ yaddaşa malik sistemlərdə (mobil cihazlar, soyuducular, mikrodalğalı sobalar və s.) x86 prosessorlarından istifadə etmək o qədər də məntiqli deyil. ). X64 prosessorlarının daha çox mövcud registrlər sayəsində daha da səmərəli olacağı ehtimalı var daha böyük ölçü bu registrlər.

Arifmetik əməliyyatlar

Əsas arifmetik əməliyyatları nəzərdən keçirin:

# reyestr dəyərlərini işə salın
mov $123 , %rax
mov $456 , %rcx

# artım: rax = rax + 1 = 124
inc%rax

# azalma: rax = rax - 1 = 123
dec%rax

# əlavə: rax = rax + rcx = 579
% rcx , % rax əlavə edin

# çıxma: rax = rax - rcx = 123
sub % rcx , % rax

# işarəni dəyişdirin: rcx = - rcx = -456
mənfi %rcx

Burada və aşağıda operandlar təkcə registrlər deyil, həm də yaddaş sahələri və ya sabitlər ola bilər. Lakin hər iki operand yaddaş yeri ola bilməz. Bu qayda bütün x86/x64 assembler təlimatlarına, ən azı bu məqalədə müzakirə olunanlara aiddir.

Çoxalma nümunəsi:

hərəkət $100 , % al
hərəkət $3 , %cl
mul % cl

Bu misalda mul əmri al-ı cl-ə vurur və vurmanın nəticəsini al və ah registr cütlüyündə saxlayır. Beləliklə, ax 0 dəyərini alacaq x 12C və ya 300 onluq qeydlərdə. Ən pis halda, iki N bayt dəyərinin vurulmasının nəticəsini saxlamaq üçün 2*N bayta qədər vaxt lazım ola bilər. Operandın ölçüsündən asılı olaraq nəticə al:ah, ax:dx, eax:edx və ya rax:rdx formatında saxlanılır. Üstəlik, bu registrlərdən birincisi və təlimata ötürülən arqument həmişə çarpan kimi istifadə olunur.

İmzalı vurma imul təlimatından istifadə edərək tam olaraq eyni şəkildə həyata keçirilir. Bundan əlavə, iki və üç arqumentli imul variantları var:

mov $123 , %rax
mov $456 , %rcx

#rax=rax*rcx=56088
imul % rcx , % rax

#rcx=rax*10=560880
imul $10 , % rax , % rcx

div və idiv təlimatları mul və imulun əksini edir. Misal üçün:

hərəkət $0 , %rdx
mov $456 , %rax
mov $123 , %rcx

# rax = rdx:rax / rcx = 3
# rdx = rdx:rax % rcx = 87
div %rcx

Gördüyünüz kimi, bölmənin qalan hissəsi kimi, tam bölünmənin nəticəsi də alındı.

Bu, bütün arifmetik göstərişlər deyil. Məsələn, həmçinin adc (daşıma bayrağını nəzərə alaraq əlavə), sbb (borc nəzərə alınmaqla çıxma), eləcə də onlara uyğun olan müvafiq bayraqları (ctc, clc) təyin edən və təmizləyən təlimatlar var. bir çox başqaları. Ancaq onlar daha az yaygındır və buna görə də bu məqalə çərçivəsində nəzərdən keçirilmir.

Məntiq və bit əməliyyatları

Artıq qeyd edildiyi kimi, x86/x64 assembler-də xüsusi yazı yoxdur. Buna görə də, onun Boolean əməliyyatlarını yerinə yetirmək üçün ayrıca təlimatları və bit əməliyyatlarını yerinə yetirmək üçün ayrıca təlimatları olmadığına təəccüblənməyin. Bunun əvəzinə, bitlərlə işləyən bir təlimat dəsti var və nəticəni necə şərh etmək xüsusi proqramdan asılıdır.

Beləliklə, məsələn, ən sadə məntiqi ifadənin hesablanması belə görünür:

mov $0 , % rax # a = yanlış
mov $1 , % rbx # b = doğrudur
mov $0 , % rcx # c = yanlış

# rdx:= a || !(b && c)
mov % rcx , % rdx # rdx = c
və % rbx , % rdx # rdx &= b
%rdx#rdx=~rdx deyil
və ya % rax , % rdx # rdx |= a
və $1 , % rdx # rdx &= 1

Qeyd edək ki, burada biz 64 bitlik registrlərin hər birində ən az əhəmiyyətli bitdən istifadə etmişik. Beləliklə, son əmrlə sıfıra qaytardığımız yüksək bitlərdə zibil əmələ gəlir.

Digər faydalı təlimat xor (eksklüziv və ya). Boolean ifadələrdə xor nadir hallarda istifadə olunur, lakin çox vaxt registrləri sıfırlayır. Təlimat əməliyyat kodlarına baxsanız, bunun səbəbi aydın olur:

4000b3: 48 31 db xor %rbx,%rbx
4000b6: 48 ff c3 daxil %rbx
4000b9: 48 c7 c3 01 00 00 00 mov $0x1,%rbx

Gördüyünüz kimi, xor və inc təlimatları hər biri cəmi üç baytla kodlanır, eyni şeyi edən mov təlimatı isə yeddi bayt tutur. Əlbəttə ki, hər bir fərdi işi ayrıca müqayisə etmək daha yaxşıdır, lakin ümumi evristik qayda belədir - kod nə qədər qısa olsa, prosessorun ön yaddaşına nə qədər çox yerləşsə, bir o qədər tez işləyir.

Bu kontekstdə biz həmçinin bit sürüşməsi, bit testi (bit testi) və bit skanı (bit tarama) üçün təlimatları xatırlamalıyıq:

# reyestrə bir şey qoyun
movabs $0xc0de1c0ffee2beef , %rax

# 3 bit sola sürüşdürün
# rax = 0x0de1c0ffee2beef0
shl $4 , % rax

# 7 bit sağa sürüşdürün
#rax = 0x001bc381ffdc57dd
shr $7 , % rax

# 5 bit sağa fırladın
#rax=0xe800de1c0ffee2be
ror $5 , % rax

# 5 bit sola fırladın
#rax = 0x001bc381ffdc57dd
roll $5 , % rax

# ditto + set bit (bit testi və set)

bts $13 , % rax

# ditto + sıfırlama biti (bit testi və sıfırlama)
#rax=0x001bc381ffdc57dd, CF=1
btr $13 , % rax

# ditto + invert bit (bit testi və tamamlama)
#rax=0x001bc381ffdc77dd, CF=0
btc $13 , % rax

# ən az əhəmiyyətli sıfır olmayan baytı tapın (bit irəli skan)
#rcx=0, ZF=0
bsf %rax , %rcx

# ən əhəmiyyətli sıfırdan fərqli baytı tapın (bit skanının tərsinə)
#rdx=52, ZF=0
bsr % rax , % rdx

# əgər bütün bitlər sıfırdırsa, ZF = 1, rdx dəyəri qeyri-müəyyəndir
xor % rax , % rax
bsf %rax , %rdx

İmzalanmış bit sürüşmələri (sal, sar), daşıma bayrağı ilə tsiklik sürüşmələr (rcl, rcr) və ikiqat dəqiqlikli sürüşmələr (shld, shrd) var. Ancaq onlar tez-tez istifadə edilmir və ümumiyyətlə bütün təlimatları sadalamaqdan yorulacaqsınız. Ona görə də onların işini sizə ev tapşırığı kimi buraxıram.

Şərtlər və Döngələr

Bəzi bayraqlar yuxarıda bir neçə dəfə qeyd edilmişdir, məsələn, transfer bayrağı. Bayraqlar bayraqların/rflagların xüsusi registrinin bitləridir (müvafiq olaraq x86 və x64-də ad). Bu registrə mov, add və oxşar təlimatlarla birbaşa daxil olmaq mümkün deyil, lakin dolayı yolla müxtəlif təlimatlar tərəfindən dəyişdirilir və istifadə olunur. Məsələn, artıq qeyd olunan daşıma bayrağı (CF) bayraqların/rflagların bit sıfırında saxlanılır və məsələn, eyni bt təlimatında istifadə olunur. Digər tez-tez istifadə olunan bayraqlara sıfır bayraq (ZF, 6-cı bit), işarə bayrağı (SF, 7-ci bit), istiqamət bayrağı (DF, 10-cu bit) və daşqın bayrağı (OF, 11-ci bit) daxildir.

Bu gizli registrlərdən digəri cari təlimatın ünvanını saxlayan eip / rip adlandırılmalıdır. O, həmçinin birbaşa daxil ola bilməz, lakin info registrləri desəniz, bayraqlar/rflags ilə birlikdə GDB-də görünür və dolayı yolla dəyişdirilir. hər kəs təlimatlar. Əksər təlimatlar sadəcə olaraq bu təlimatın uzunluğuna görə eip / rip artırır, lakin bu qayda üçün istisnalar var. Məsələn, jmp təlimatı sadəcə olaraq verilmiş ünvana keçir:

# rax sıfırlayın
xor % rax , % rax
jmp növbəti
# bu təlimat atlanacaq
inc%rax
sonrakı:
inc%rax

Nəticədə, ilk inc təlimatı atılacağı üçün rax dəyəri birə bərabər olacaq. Qeyd edək ki, keçid ünvanı registrdə də yazıla bilər:

xor % rax , % rax
mov $növbəti, %rcx
jmp*%rcx
inc%rax
sonrakı:
inc%rax

Bununla belə, praktikada bu cür koddan ən yaxşı şəkildə qaçınmaq olar, çünki o, filial proqnozunu pozur və buna görə də daha az effektivdir.

Qeyd: GAS etiketlərə 1: , 2: və s. kimi ədədi adlar verməyə imkan verir və bu kimi təlimatlarla verilmiş nömrə ilə ən yaxın əvvəlki və ya növbəti etiketə keçin. jmp1bjmp 1f. Bu olduqca əlverişlidir, çünki etiketlər üçün mənalı adlar tapmaq bəzən çətin ola bilər. Detalları tapa bilərsiniz.

Şərti atlamalar adətən iki operandını müqayisə edən və müvafiq bayraqları təyin edən cmp təlimatı ilə həyata keçirilir, ardınca je, jg ​​və oxşar ailələrin göstərişi verilir:

cmp %rax , %rcx

je 1f bərabərdirsə # tullanmaq (bərabər)
jl 1f # işarəsi azdırsa (daha az) atla
jb 1f # (aşağıda) azdırsa, atla
jg 1f işarədən böyükdürsə # tullanmaq (böyük)
ja 1f İmzasız (yuxarıda) böyükdürsə # tullanmaq

Həmçinin jne (bərabər deyilsə tullanma), jle (imza az və ya bərabərdirsə tullanır), jna (imzasızsa tullanır) və bu kimi təlimatlar var. Adlandırma prinsipi, inşallah, göz qabağındadır. Je / jne əvəzinə tez-tez jz / jnz yazılır, çünki je / jne təlimatları sadəcə ZF dəyərini yoxlayır. Digər bayraqları yoxlayan təlimatlar da var - js, jo və jp, lakin praktikada onlar nadir hallarda istifadə olunur. Bütün bu təlimatlar bir yerə toplanmışdır, adətən jcc adlanır. Yəni konkret şərtlər yerinə “şərt”dən iki “c” hərfi yazılır. bütün jcc təlimatlarının və hansı bayraqların yoxlanılmasının yaxşı xülasə cədvəlini tapa bilərsiniz.

Cmp-dən əlavə, test ifadəsi də tez-tez istifadə olunur:

test %rax , %rax
jz 1f # tullanmaq əgər rax == 0
js 2f # tullanmaq əgər rax< 0
1 :
# bəzi kod
2 :
# başqa kod

əyləncəli faktlar! Maraqlıdır ki, cmp və test mahiyyətcə sub və və ilə eynidir, yalnız onlar operandlarını dəyişmir. Bu bilik əlavə cmp və ya test təlimatları olmadan eyni zamanda alt və ya və və şərti budaqları yerinə yetirmək üçün istifadə edilə bilər.

Şərti atlamalarla əlaqəli başqa bir təlimat aşağıdakılardır.

jrcxz 1f
# bəzi kod
1 :

jrcxz təlimatı yalnız rcx registrinin qiyməti sıfır olduqda sıçrayır.

cmovge %rcx , %rax

Cmovcc ailəsinin göstərişləri (şərti hərəkət) mov kimi işləyir, lakin yalnız göstərilən şərt yerinə yetirildikdə, jcc ilə analoji olaraq.

setnz % al

Setcc təlimatları, göstərilən şərt doğrudursa, yaddaşda bir baytlıq registr və ya baytı 1-ə, əks halda isə 0-a təyin edir.

cmpxchg % rcx , (% rdx )

Raxı verilmiş yaddaş parçası ilə müqayisə edin. Əgər bərabərdirsə, ZF-ni təyin edin və göstərilən registrin dəyərini bu rcx nümunəsində göstərilən ünvanda saxlayın. Əks halda ZF-ni təmizləyin və dəyəri yaddaşdan rax-a yükləyin. Həmçinin, hər iki operand registr ola bilər.

cmpxchg8b(%rsi)
cmpxchg16b(%rsi)

cmpxchg8b təlimatı əsasən x86-da tələb olunur. O, cmpxchg ilə eyni işləyir, yalnız 8 baytı eyni anda müqayisə edir və dəyişdirir. Müqayisə üçün edx:eax registrlərindən istifadə olunur, ecx:ebx registrləri isə bizim yazmaq istədiklərimizi saxlayır. cmpxchg16b təlimatı eyni prinsip əsasında x64-də eyni anda 16 baytı müqayisə edir və dəyişdirir.

Vacibdir! Qeyd edək ki, kilid prefiksi olmadan bütün bu müqayisə və dəyişdirmə təlimatları atom deyil.

mov $10 , %rcx
1 :
# bəzi kod
döngə 1b
# loopz 1b
# loopnz 1b

Döngü təlimatı rcx registrinin qiymətini bir azaldır və ondan sonra rcx != 0 olarsa, verilmiş etiketə keçir. loopz və loopnz təlimatları eyni şəkildə işləyir, yalnız şərtlər daha mürəkkəbdir - müvafiq olaraq (rcx != 0) && (ZF == 1) və (rcx != 0) && (ZF == 0).

Bu təlimatlarla if-then-else konstruksiyalarını və ya for/while döngələrini anlamaq üçün beyin lazım deyil, ona görə də davam edək.

"String" əməliyyatları

Aşağıdakı kod parçasını nəzərdən keçirin:

mov $str1, %rsi
mov $str2, % edi
cld
cmpsb

rsi və rdi registrləri iki sətirin ünvanları ilə doldurulur. cld əmri istiqamət bayrağını (DF) təmizləyir. Bunun əksini edən təlimat std adlanır. Sonra cmpsb təlimatı işə düşür. Baytları (%rsi) və (%rdi) müqayisə edir və müqayisənin nəticəsinə uyğun olaraq bayraqlar təyin edir. Sonra DF = 0 olarsa, rsi və rdi bir artır (müqayisə etdiyimiz baytların sayı), əks halda azalır. cmpsw, cmpsl və cmpsq oxşar təlimatlar sözləri müqayisə edir, uzun sözlər və müvafiq olaraq dördlü sözlər.

Cmps təlimatları maraqlıdır, çünki onlar rep prefiksi, repe (repz) və repne (repnz) ilə istifadə edilə bilər. Misal üçün:

mov $str1, %rsi
mov $str2, % edi
mov $len, %rcx
cld
cmpsb-ni təkrarlayın
jne bərabər deyil

Rep prefiksi təlimatı rcx registrində göstərilən sayda təkrarlayır. repz və repnz prefiksləri eyni şeyi edir, lakin yalnız təlimatın hər yerinə yetirilməsindən sonra ZF əlavə olaraq yoxlanılır. Əgər c repz halda ZF = 0, repnz halda isə ZF = 1 olarsa, dövrə dayandırılır. Beləliklə, yuxarıdakı kod eyni ölçülü iki bufer arasında bərabərliyi yoxlayır.

Oxşar təlimatlar movs məlumatları ünvanı rsi-də göstərilən buferdən ünvanı rdi-də göstərilən buferə köçürür (yadda saxlamaq asandır - rsi mənbə, rdi təyinat deməkdir). stos təlimatı rdi ünvanındakı buferi raxdakı baytlarla doldurur (yaxud xüsusi təlimatdan asılı olaraq eax, ya ax, ya da al). Lods təlimatları bunun əksini edir - rsi-də göstərilən ünvandakı baytları rax registrinə köçürün. Nəhayət, scas təlimatları rdi tərəfindən göstərilən buferdə rax registrində (və ya müvafiq daha kiçik registrlərdə) baytları axtarır. Cmps kimi, bu təlimatların hamısı rep, repz və repnz prefiksləri ilə işləyir.

Bu təlimatlara əsasən memcmp, memcpy, strcmp və oxşar prosedurlar asanlıqla həyata keçirilir. Maraqlıdır ki, məsələn, yaddaşı sıfırlamaq üçün Intel mühəndisləri müasir prosessorlarda istifadə etməyi məsləhət görürlər rep stosb, yəni baytı baytla sıfırlayın, deyək ki, dörd sözlə deyil.

Yığınla işləmə və prosedurlar

Bir yığın ilə hər şey çox sadədir. Təkan təlimatı öz arqumentini yığına itələyir və pop təlimatı yığından bir dəyər çıxarır. Məsələn, əgər müvəqqəti olaraq xchg təlimatını unudursunuzsa, onda siz iki registr dəyərini bu kimi dəyişə bilərsiniz:

itələyin %rax
mov % rcx , % rax
pop %rcx

Yığında rflags / flags qeydlərini itələyən və açan təlimatlar var:

pushf
# bayraqları dəyişdirən bir şey edin
popf
# bayraqlar bərpa edildi, jcc etmək vaxtıdır

Beləliklə, məsələn, CF bayrağının dəyərini əldə edə bilərsiniz:

pushf
pop %rax
və $1 , %rax

X86-da, yığındakı bütün registrlərin dəyərlərini saxlayan və bərpa edən pusha və popa təlimatları da var. x64-də bu təlimatlar artıq mövcud deyil. Görünür, daha çox registrlər olduğundan və registrlərin özləri indi daha uzun olduğundan - onların hamısını saxlamaq və bərpa etmək çox bahalaşıb.

Prosedurlar adətən zəng və ret təlimatlarından istifadə edərək "yaradılır". Zəng təlimatı ünvanı yığına itələyir növbəti təlimat və nəzarəti arqumentdə göstərilən ünvana ötürür. Ret təlimatı stekdən qayıdış ünvanını oxuyur və onun üzərində nəzarəti ötürür. Misal üçün:

someproc:
# tipik prosedur proloqu
# məsələn, yerli dəyişənlər üçün yığında 0x10 bayt ayırın
# rbp - çərçivə yığını üçün göstərici
itələyin %rbp
mov % rsp , % rbp
sub $0x10 , % rsp

# burada bir növ hesablama...
mov $1 , %rax

# tipik prosedur epiloqu
$0x10 əlavə edin, %rsp
pop %rbp

#çıxış proseduru
ret

başlamaq:
# jmp ilə olduğu kimi, keçid ünvanı registrdə ola bilər
someproc zəng edin
test %rax , %rax
jnz xətası

Qeyd: Bənzər bir proloq və epiloq təlimatlardan istifadə edərək yazıla bilər $0x10, $0 daxil edinburaxmaq. Ancaq bu günlərdə bu ifadələr nadir hallarda istifadə olunur, çünki iç-içə prosedurlar üçün əlavə dəstək sayəsində icrası daha yavaşdır.

Qaytarılan qiymət bir qayda olaraq rax registrinə verilir və ya ölçüsü kifayət qədər böyük deyilsə, ünvanı arqument kimi ötürülən struktura yazılır. Arqumentlərin ötürülməsi məsələsində. Bir çox çağırış konvensiyaları var. Bəzilərində bütün arqumentlər həmişə stekdən keçirilir (ayrıca sual hansı qaydadadır) və prosedurun özü arqumentlər yığınının təmizlənməsinə cavabdehdir, digərlərində arqumentlərin bəziləri registrlərdən, bəziləri isə stekdən keçir. , və zəng edən şəxs arqumentlər yığınını, üstəgəl ortada çoxlu seçimləri, yığındakı arqumentləri uyğunlaşdırmaq üçün ayrıca qaydalarla, OOP dilidirsə bunu ötürməkdən məsuldur və s. Ümumi halda, ixtiyari bir arxitektura, kompilyator və proqramlaşdırma dili üçün çağırış konvensiyası ümumiyyətlə hər hansı bir şey ola bilər.

I] ;
}
hash qaytarmaq;
}

Disassembler siyahısı (-O0 ilə tərtib edildikdə, şərhlər mənimdir):

# tipik prosedur proloqu
# register rsp dəyişmir, çünki prosedur heç birini çağırmır
# digər prosedurlar
400950: 55 təkan %rbp
400951: 48 89 e5 mov %rsp,%rbp

# yerli dəyişənlərin işə salınması:
# -0x08(%rbp) - const imzasız char *data (8 bayt)
# -0x10(%rbp) - const size_t data_len (8 bayt)
# -0x14(%rbp) - imzasız int hash (4 bayt)
# -0x18(%rbp) - int i (4 bayt)
400954: 48 89 7d f8 mov %rdi,-0x8(%rbp)
400958: 48 89 75 f0 mov %rsi,-0x10(%rbp)
40095c: c7 45 ec 4b 43 41 48 movl $0x4841434b,-0x14(%rbp)
400963: c7 45 e8 00 00 00 00 movl $0x0,-0x18(%rbp)

#rax:= i. data_len əldə olunarsa, dövrədən çıxın
40096a: 48 63 45 e8 movslq -0x18(%rbp),%rax
40096e: 48 3b 45 f0 cmp -0x10(%rbp),%rax
400972: 0f 83 28 00 00 00 jae 4009a0

# eax:= (hash<< 5) + hash
400978: 8b 45ec mov -0x14(%rbp),%eax
40097b: c1 e0 05 shl $0x5,%eks
40097e: 03 45 ec əlavə et -0x14(%rbp),%eax

# eax += data[i]
400981: 48 63 4d e8 movslq -0x18(%rbp),%rcx
400985: 48 8b 55 f8 mov -0x8(%rbp),%rdx
400989: 0f b6 34 0a movzbl(%rdx,%rcx,1),%esi
40098d: 01 f0 əlavə et %esi,%eks

# hash:= eks
40098f: 89 45 ec mov %eax,-0x14(%rbp)

# i++ və döngənin əvvəlinə keçin
400992: 8b 45 e8 mov -0x18(%rbp),%eax
400995: 83 c0 01 əlavə edin $0x1,%eks
400998: 89 45 e8 mov %eax,-0x18(%rbp)
40099b: e9 ca ff ff ff jmpq 40096a

# qaytarılan dəyər (hash) eax registrinə qoyulur
4009a0: 8b 45ec mov -0x14(%rbp),%eax

# tipik epiloq
4009a3: 5d pop %rbp
4009a4: c3 retq

Burada iki yeni təlimatla tanış olduq - movs və movz. Onlar tam olaraq mov kimi işləyirlər, yalnız bir operandı ikincinin ölçüsünə genişləndirirlər, müvafiq olaraq imzalanmış və imzasız. Məsələn, movzbl (%rdx,%rcx,1),%esi təlimatı (%rdx,%rcx,1) ünvanında bir baytı (b) oxuyur, əvvəldən sıfırları (z) yazmaqla onu uzun sözə (l) genişləndirir. ) və nəticəni esi registrinə qoyur.

Göründüyü kimi, rdi və rsi registrləri vasitəsilə prosedura iki arqument ötürüldü. Görünür, o, System V AMD64 ABI adlı konvensiyadan istifadə edir. Bunun *nix sistemlərində x64 üçün faktiki standart olduğu iddia edilir. Bu konvensiyanın təsvirini burada təkrar izah etmək üçün heç bir səbəb görmürəm, maraqlanan oxucular tam təsviri verilmiş linkdə oxuya bilərlər.

Nəticə

Söz yox ki, bir məqalə çərçivəsində bütün x86 / x64 assemblerini təsvir etmək mümkün deyil (üstəlik, mən özümün bunu düzgün bildiyimə əmin deyiləm. bütün). Ən azı, üzən nöqtə nömrələri üzərində əməliyyatlar, MMX, SSE və AVX təlimatları, eləcə də lidt, lgdt, bswap, rdtsc, cpuid, movbe, xlatb və ya prefetch kimi bütün növ ekzotik təlimatlar kimi mövzular geridə qaldı. səhnələr. Gələcək yazılarımda onları işıqlandırmağa çalışacam, amma heç nə vəd etmirəm. Onu da qeyd etmək lazımdır ki, əksər real proqramlar üçün objdump -d çıxışında siz çox nadir hallarda yuxarıda təsvir olunanlardan başqa heç nə görməyəcəksiniz.

Pərdə arxasında qalan digər maraqlı mövzu isə atom əməliyyatları, yaddaş maneələri, spinloklar və bu qədərdir. Məsələn, müqayisə və dəyişdirmə çox vaxt sadəcə olaraq lock prefiksi olan cmpxchg təlimatı kimi həyata keçirilir. Analoji olaraq, atom artımı, azalma və s. həyata keçirilir. Təəssüf ki, bütün bunlar ayrı bir məqalə üçün bir mövzu çəkir.

Əlavə məlumat mənbəyi kimi biz Müasir X86 Assembly Language Programming kitabını və əlbəttə ki, Intel-in təlimatlarını tövsiyə edə bilərik. wikibooks.org saytındakı x86 Assambleya kitabı da olduqca yaxşıdır.

Assembler təlimatları üçün onlayn istinadlardan aşağıdakılara diqqət yetirməlisiniz:

Assembly dilini bilirsinizmi və əgər varsa, bu bilikləri faydalı hesab edirsinizmi?

Bu məlumat ilk olaraq Əsas Cədvəl İzahatları səhifəsində yerləşdirilib. Amma sonra qərara alındı ​​ki, bu uzun ümumi arqumentlər ayrıca səhifəyə salınsın. Ancaq belə bir transferdən sonra bu mübahisələr bir az da böyüdü. İndi, bəlkə də, onlar yalnız "Müxtəlif qeydlər" bölməsi üçün uyğundur ...

Quraşdırma təlimatları və maşın təlimatları

Əvvəla, unutmamalıyıq ki, assembler dili təlimatları və maşın dili təlimatları iki fərqli şeydir. Baxmayaraq ki, bu iki anlayış bir-biri ilə sıx bağlıdır.

Assembler təlimatı bəzi mnemonik addır. X86 ailəsi prosessorları üçün bu ad ingilis dilində yazılmışdır. Məsələn, əlavə əmrinin adı var ƏLAVƏ EDİN, və çıxma əmrinin adı var SUB.

Əmr assembler dilində əmrin adını göstərir.

Maşın təlimatının əsasını sadəcə bir rəqəm olan əməliyyat kodu təşkil edir. X86 prosessorları üçün (lakin digər prosessorlar üçün də) onaltılıq nömrələrdən istifadə etmək adətdir. (Keçmişdə qeyd edirik ki, səkkizlik nömrələr sovet kompüterləri üçün qəbul edilmişdir, onlarla daha az qarışıqlıq var idi, çünki belə nömrələr yalnız rəqəmlərdən ibarətdir və hərflər yoxdur).

Sütundakı bu kitabçanın cədvəllərində Kod maşın təlimatının əməliyyat kodunu və sütunda göstərir Format maşın təlimatının formatını göstərir.

Verilən prosessor üçün müxtəlif maşın təlimatlarının sayının mümkün əməliyyat kodlarının sayına bərabər olduğunu düşünə bilərik. Formatla, verilmiş maşın təlimatının hansı komponentlərdən ibarət olduğunu öyrənə bilərsiniz. Fərqli maşın təlimatları fərqli formatlara malik ola bilər. Maşın təlimatının əməliyyat kodu onun formatını tamamilə müəyyən edir.

Çox vaxt bir assembler təlimatı maşın təlimatlarının bir neçə müxtəlif variantına malikdir. Üstəlik, bu maşın əmrlərinin formatları müxtəlif variantlar üçün fərqli ola bilər.

Məsələn, ADD assembler təlimatında müxtəlif əməliyyat kodları ilə maşın təlimatlarının on variantı var. Ancaq daha az fərqli format var, yalnız üç. Və bu üç formatın hər biri montaj dilində təlimat yazarkən müxtəlif növ operandlar tələb edir.

Burada qeyd etmək lazımdır ki, bütün bu on maşın təlimatı montaj dilində ADD adlanan eyni elementar əməliyyatı yerinə yetirir.

Və buna görə də belə çıxır ki, aşağıdakı kimi mübahisə etmək olar: prosessor müxtəlif montaj təlimatları olduğu qədər müxtəlif elementar əməliyyatları yerinə yetirə bilər. Bununla belə, bu sadə prinsip hələ də qeydlərə və qeydlərə ehtiyac duyur. Çünki assembler əmrlərinin bəzilərinin sinonimləri də var.

Prosessorun bütün təlimatlarının ümumi siyahısı müxtəlif təlimatlar sırasını seçməklə müxtəlif yollarla qurula bilər. Əsas iki yoldur.

Metod (1). Assembly dili əmrlərini əsas götürün və əmrləri əlifba sırası ilə düzün. Sonra bu kimi cədvəllər əldə edilə bilər. Bütün əmrlər əlifba sırası ilə (qısaca)

Metod (2). Maşın təlimatının əməliyyat kodunu əsas götürün və təlimatları əməliyyat kodlarının ardıcıllığına uyğunlaşdırın. Bu halda, ümumi siyahı iki yerə bölünsə, bir baytlıq əməliyyat kodu olan əmrlər və iki baytlıq əməliyyat kodu olan əmrlər üçün ayrıca siyahılar tərtib edilsə, daha yaxşı olardı. Birinci əməliyyat kodu baytı İkinci əməliyyat kodu baytı

Əlbəttə, üçüncü yol da var ki, adətən dərsliklərdə istifadə olunur. Bütün əmrləri mənalarına görə qruplara bölün və daha sadələrdən başlayaraq qruplara bölün.

Əsas əməliyyat kodu baytı

X86 komanda sistemində bir bayt (256 müxtəlif kombinasiya) bütün əmrləri kodlaşdırmaq üçün kifayət deyildi. Buna görə də, maşın təlimatındakı əməliyyat kodu ya bir bayt, ya da iki bayt tutur.

Əgər birinci bayt kodu ehtiva edirsə 0F, onda əməliyyat kodu iki baytdan ibarətdir.

Maşın təlimatında əməliyyat kodu bir baytdan ibarətdirsə, bu tək bayt əməliyyat kodunun əsas baytıdır. Və bu baytın məzmunu əməliyyatın nə olduğunu müəyyən edir.

Maşın təlimatında əməliyyat kodu iki baytdan ibarətdirsə, birinci deyil, ikinci bayt əməliyyat kodunda əsas və təyinedici bayt olacaqdır.

Maşın təlimatlarının kodlaşdırılmasını göstərən əl cədvəllərində əməliyyat kodunun əsas baytı adətən iki dəfə göstərilir, əvvəlcə "Kod" sütununda onaltılıq rəqəm kimi, sonra isə "Format" sütununda şərti səkkiz tire şəklində göstərilir. , əsas əməliyyat kodu baytında varsa, xüsusi bitlər qeyd olunur.

Təlimatın əsas səhifələri

x86 prosessor təlimatlarına istinad - əsas səhifə (burada bütün təlimat səhifələrinin xəritəsi var)

Assembler müxtəlif prosessorları, mikroprosessorları və mikro nəzarətçiləri proqramlaşdırmaq üçün istifadə olunan aşağı səviyyəli proqramlaşdırma dilidir. Bu test x86 prosessorları üçün assembler hesab edir.

Assembly dili proqramları xüsusi təlimatlar toplusundan ibarətdir. Daha sonra bu əmrlər tərcüməçinin köməyi ilə maşın koduna çevrilir və sonra mərkəzi prosessor tərəfindən yerinə yetirilir. Əmrlərin köməyi ilə siz arifmetik hesablamalar aparmaq, yaddaş və portlarla işləmək və s.

Bir qayda olaraq, assembler kodun kritik bölmələrini sürət üçün optimallaşdırmaq lazım olduqda, cihaz sürücülərində, viruslarda və digər zərərli proqramlarda, əməliyyat sistemlərində, kompilyatorlarda və s.

Assembler x86 testinin hədəf auditoriyası

Test montaj dili və x86 arxitekturası biliklərini yoxlayır. Test daha çox dil və arxitektura üzrə praktiki biliyə yönəlib və buna görə də sistem proqramçıları və tələbələr üçün bilikləri yoxlamaq üçün maraqlı olacaq, həmçinin kompüter arxitekturası və aşağı səviyyəli proqramlaşdırma haqqında bilikləri təkmilləşdirmək üçün bütün proqramçılar üçün faydalı olacaq.

X86 assembler-də test strukturu

Aşağıdakı mövzular özbaşına müəyyən edilə bilər:

  • Ümumi məsələlər
  • Prosessorun iş rejimləri (real, qorunan)
  • Prosessor təlimatları

X86 assembler testinin sonrakı inkişafı

Gələcəkdə əhatə olunmayan mövzular üzrə suallar əlavə etməyi planlaşdırırıq (FPU, cihazlar/portlarla işləmək). Həmçinin, inkişafda orta səviyyəli test var, tezliklə keçmək üçün hazır olacaq.