Introduktion till Assembly - Del 2: Instruktioner
Förord
I denna artikel tänker jag förklara lite om assembly instruktioner. Jag tänker ex. nämna några av de mest vanliga instruktionerna och vad de gör samt inkludera några exempel på hur assembly-kod ser ut och skrivs.
Instruktioner
Nedan har vi en lista med grundläggande assembly instruktioner, en kort beskrivning av namnet och vad de gör.• mov dest, src - kort för move ("flytta"), flyttar ett värde från minnesplatsen eller registret specifierat i src till dest.
• cmp dest, src - kort för compare ("jämför"), jämför två värden och sätter en eller flera CPU-flaggor beroende på resultatet.
• add dest, src - som namnet antyder; add ("addera"), adderar två värden och placerar resultatet i dest.
• sub dest, src - kort för substract ("substrahera"), substraherar värdet specifierat i src från dest.
• call dest - som namnet antyder; call ("anropa"), anropar funktionen vars adress är specifierad i dest.
• dec dest - kort för decrease ("minska"), förminskar minnesplatsen eller registret specifierat i dest med 1.
• inc dest - kort för increase ("öka"), ökar minnesplatsen eller registret specifierat i dest med 1.
• div src - kort för divide ("dividera"), utför en s k "unsigned" division av registret EAX med minnesplatsen eller registret specifierat i src.
• mul src - kort för multiply ("multiplicera"), utför en s k "unsigned" multiplicering av registret EAX med minnesplatsen eller registret specifierat i src.
Instruktionerna är ganska enkla att förstå, det gäller bara att bli van vid att om två parametrar är involverade i instruktionen så kommer dest alltid före i assemblers så som MASM och TASM (Turbo Assembler, Borlands egna assembler). Det finns andra assemblers som fungerar på det motsatta sättet (src före dest).
Här följer ett par exempel på hur man använder instruktionerna.
Du specifierar för övrigt kommentarer i assembly-källkod med ;
mov eax,10 ; eax=10
add eax,2 ; eax+=2
sub eax,5 ; eax-=5
mov ecx,10 ; ecx=10
mul ecx ; eax*=ecx
inc eax ; eax++
inc eax ; eax++
add eax,8 ; eax+=8
mov ecx,20 ; ecx=20
div ecx ; eax/=20
sub eax,4 ; eax-=4
inc eax ; eax++
mov eax,0 ; nollställ EAX
@loop:
inc eax
cmp eax,8 ; EAX==8?
jne @loop ; nej, så hoppa tillbaka till @loop och testa igen
; när programmet nått hit så innehåller registret EAX värdet 8. kodstycket ovan visar även hur en enkel loop fungerar
Det ovanstående exemplet kanske var lite förvirrande, så låt oss gå igenom den nya typen av instruktioner.
Alla instruktioner som börjar på j i assembly är instruktioner som på något vis hoppar (jumps) till en annan plats i programmet. Om du exempelvis skriver följande kod i C, så kommer kompileraren generera så kallade "conditional jump"-instruktioner. Alltså om ett "villkor" stämmer, ex. något är större än N, något är mindre än N, något är tomt osv, så kan du hoppa till ett kodstycke och fortsätta programflödet där. Det är så s k "if"-statements fungerar i grunden.
/* C-kod... */
if (min_funktion()==0x100) {
min_andra_funktion();
}
invoke min_funktion ; anropa min_funktion
cmp eax,100h ; kolla ifall returvärdet är 0x100 (returvärdet från en standard funktion placeras alltid i EAX)
jne @no_match ; om inte värdena inte matchar, så hoppar vi fram och fortsätter med något
invoke min_andra_funktion ; annars flyter programflödet på som vanligt och min_andra_funktion anropas
@no_match:
; programflödet når hit vare sig min_funktion returnerade 0x100 eller ej
Koden ovan anropar alltså, likt C-koden, på min_andra_funktion() BARA om min_funktion returnerar 0x100. Det må se krångligt ut, men bara man förstår principen med "villkorliga hopp" och CPU-flaggor så kommer enkronan snart falla.
Grundläggande instruktioner som ovillkorligt eller villkorligt bryter programflödet och hoppar i koden:
• ja dest - kort för jump [if] above ("hoppa om överskrider"), hoppar till dest om CF (Carry Flag) och ZF (Zero Flag) är nollställda.
• jae dest - kort för jump [if] above or equal ("hoppa om överskrider eller om lika"), hoppar till dest om CF (Carry Flag) är nollställd.
• jb dest - kort för jump [if] below ("hoppa om understiger"), hoppar till dest om CF (Carry Flag) är ställd (1).
• jbe dest - kort för jump [if] below or equal ("hoppa om understiger eller om lika"), hoppar till dest om CF (Carry Flag) och ZF (Zero Flag) är ställda (1).
• jc dest - kort för jump [if] carry ("hoppa om hålla"), hoppar till dest om CF (Carry Flag) är ställd (1).
• jecxz dest - kort för jump [if] ECX zero ("hoppa om ECX är noll"), hoppar till dest om ECX-registret är nollställt.
• je dest - kort för jump [if] equal ("hoppa om lika"), hoppar till dest om ZF (Zero Flag) är ställd (1).
• jg dest - kort för jump [if] greater (signed) ("hoppa om större"), hoppar till dest om ZF (Zero Flag) är nollställd och SF (Sign Flag) är lika med OF (Overflow Flag).
• jge dest - kort för jump [if] greater or equal (signed) ("hoppa om större eller om lika"), hoppar till dest om SF (Sign Flag) är lika med OF (Overflow Flag).
• jl dest - kort för jump [if] less (signed) ("hoppa om mindre"), hoppar till dest om SF (Sign Flag) inte är lika med OF (Overflow Flag).
• jle dest - kort för jump [if] less or equal (signed) ("hoppa om mindre eller om lika"), hoppar till dest om ZF (Zero Flag) är ställd (1) och SF (Sign Flag) inte är lika med OF (Overflow Flag).
• jmp dest - kort för jump ("hoppa"), hoppar ovillkorligt till dest
• jna dest - kort för jump [if] not above ("hoppa om inte överskrider"), hoppar till dest om CF (Carry Flag) är ställd (1) och ZF (Zero Flag) är ställd (1).
• jnc dest - kort för jump [if] not carry ("hoppa om inte hålla"), hoppar till dest om CF (Carry Flag) är nollställd.
• jne dest - kort jump [if] not equal ("hoppa om inte lika"), hoppar till dest om ZF (Zero Flag) är nollställd.
• jns dest - kort för jump [if] not signed (signed) ("hoppa om inte signed"), hoppar till dest om SF (Sign Flag) är nollställd.
• jnz dest - kort för jump [if] not zero ("hoppa om inte noll"), hoppar till dest om ZF (Zero Flag) är nollställd.
• js dest - kort för jump [if] signed (signed) ("hoppa om signed"), hoppar till dest om SF (Sign Flag) är ställd (1).
• jz dest - kort för jump [if] zero ("hoppa om noll"), hoppar till dest om ZF (Zero Flag) är ställd (1).
; placera följande i data-sektionen
srctxt db "Vi testar att hoppa lite med assembly",0
dsttxt db 40 dup(0) ; en buffert på 40 bytes, initierad med nollor
COUNT equ 24 ; vi vill kopiera 24 bytes från texten
; placera följande i kod-sektionen
; bevara tills vidare
push esi
push edi
mov esi,offset srctxt ; spara adressen för srctxt i ESI
mov edi,offset dsttxt ; spara adressen för dsttxt i EDI
mov eax,0 ; nollställ
mov ecx,0 ; nollställ
@loop:
mov al,byte ptr [esi+ecx*1] ; hämta 1 byte från srctxt (notera att vi använder 8-bitars registret AL)
mov byte ptr [edi+ecx*1],al ; spara 1 byte till dsttxt
inc ecx ; öka countern med 1
cmp ecx,COUNT ; om vi INTE har nått COUNT (24)...
jb @loop ; ...så hoppar vi tillbaka till @loop och kopierar nästa byte
; eftersom bufferten (dsttxt) redan är initierad med nollor behöver vi inte självmant
; lagra strängavslutaren (som är 0)
; när vi har kopierat COUNT (24) bytes, dvs ECX==COUNT..så
; återställ vi registerna - och dsttxt innehåller 24 bytes från srctxt
pop edi
pop esi
; addr är en s k "label" i MASM som hämtar minnesaddressen till den specifierade variabeln, och bör BARA användas när du använder invoke-makron...i övriga fall så ska du använda "offset"-labeln som demonstrerades i koden ovan
; alltså kommer "invoke"-makron nedan översättas till..
; push MB_OK
; push
; push
; push 0
; call MessageBoxA
invoke MessageBox,0,addr dsttxt,addr dsttxt,MB_OK ; visa den kopierade texten i ett meddelande..
I nästa artikel kommer vi gå igenom ett s k ”Hello World!”-program i assembly. På återseende!
Fredrik Boström
Allt är väldigt simpelt och lätt-förståeligt, har haft användning av jump-listan ett antal gånger. P.s. Ta en titt på jnz i listan, förmodar att det skall stå "jump [if] not zero".
Sebastian Andersson
Tack för komplimangen och för tipset angående jnz!;)