Assembler | programmeringsspråk

Ett assembleringsspråk är ett programmeringsspråk som kan användas för att direkt tala om för datorn vad den ska göra. Ett assemblerspråk är nästan exakt som den maskinkod som en dator kan förstå, förutom att det använder ord i stället för siffror. En dator kan egentligen inte förstå ett assemblerprogram direkt. Den kan dock lätt ändra programmet till maskinkod genom att ersätta programmets ord med de siffror som de står för. Ett program som gör detta kallas för en assembler.

Program som är skrivna i assembleringsspråk består vanligtvis av instruktioner, som är små uppgifter som datorn utför när den kör programmet. De kallas instruktioner eftersom programmeraren använder dem för att instruera datorn vad den ska göra. Den del av datorn som följer instruktionerna är processorn.

En dators assemblerspråk är ett lågnivåspråk, vilket innebär att det endast kan användas för att utföra enkla uppgifter som en dator kan förstå direkt. För att utföra mer komplexa uppgifter måste man tala om för datorn vilka enkla uppgifter som ingår i den komplexa uppgiften. En dator förstår till exempel inte hur man skriver ut en mening på skärmen. I stället måste ett program som är skrivet i assembler tala om för den hur den ska göra alla de små steg som ingår i utskriften av meningen.

Ett sådant assemblerprogram skulle bestå av många, många instruktioner som tillsammans gör något som verkar mycket enkelt och grundläggande för en människa. Detta gör det svårt för människor att läsa ett assemblerprogram. Däremot kan ett programmeringsspråk på hög nivå ha en enda instruktion, till exempel PRINT "Hello, world!", som säger åt datorn att utföra alla små uppgifter åt dig.



   Zoom
 

Utveckling av assemblerspråk

När datavetare först byggde programmerbara maskiner programmerade de dem direkt i maskinkod, vilket är en serie siffror som instruerar datorn vad den ska göra. Det var mycket svårt att skriva maskinspråk och det tog lång tid att skriva det, så till slut skapades assemblerspråk. Samlingsspråk är lättare för en människa att läsa och kan skrivas snabbare, men det är fortfarande mycket svårare för en människa att använda än ett högnivåprogrammeringsspråk som försöker efterlikna det mänskliga språket.

Programmering i maskinkod

För att programmera i maskinkod måste programmeraren veta hur varje instruktion ser ut i binär (eller hexadecimal). Även om det är lätt för en dator att snabbt räkna ut vad maskinkod betyder, är det svårt för en programmerare. Varje instruktion kan ha flera olika former, som alla bara ser ut som en massa siffror för människor. Varje misstag som någon gör när han eller hon skriver maskinkod kommer att märkas först när datorn gör fel. Det är svårt att ta reda på felet eftersom de flesta människor inte kan avgöra vad maskinkod betyder genom att titta på den. Ett exempel på hur maskinkod ser ut:

05 2A 00

Denna hexadecimala maskinkod talar om för en x86-datorprocessor att lägga till 42 till ackumulatorn. Det är mycket svårt för en person att läsa och förstå den, även om personen kan maskinkod.

Användning av assembleringsspråk i stället

I assemblerspråk kan varje instruktion skrivas som ett kort ord, en s.k. mnemonik, följt av andra saker, t.ex. siffror eller andra korta ord. Mnemoniken används så att programmeraren inte behöver komma ihåg de exakta siffrorna i maskinkoden som behövs för att beordra datorn att göra något. Exempel på mnemoniker i assembleringsspråk är add, som lägger till data, och mov, som flyttar data från ett ställe till ett annat. Eftersom "mnemoteknik" är ett ovanligt ord används ibland i stället frasen instruktionstyp eller bara instruktion, ofta felaktigt. Orden och siffrorna efter det första ordet ger mer information om vad man ska göra. Till exempel kan saker efter en addering vara vilka två saker som ska adderas och saker efter mov säger vad som ska flyttas och var det ska placeras.

Till exempel kan maskinkoden i föregående avsnitt (05 2A 00) skrivas i assembler som:

lägga till ax,42

Assemblyspråk gör det också möjligt för programmerare att skriva de faktiska data som programmet använder på enklare sätt. De flesta assemblerspråk har stöd för att enkelt skapa siffror och text. I maskinkod måste varje olika typ av tal, som positiva, negativa eller decimala, omvandlas manuellt till binära tal, och text måste definieras en bokstav i taget, som tal.

Samlingsspråk är en så kallad abstraktion av maskinkod. När man använder assembler behöver programmerare inte veta i detalj vad siffror betyder för datorn, utan det är assembler som tar reda på det i stället. Med assemblerspråk kan programmeraren faktiskt fortfarande använda alla processorns funktioner som de kunde med maskinkod. I denna mening har assembleringsspråk en mycket bra och sällsynt egenskap: det har samma förmåga att uttrycka saker som det som det abstraherar (maskinkod) samtidigt som det är mycket lättare att använda. På grund av detta används maskinkod nästan aldrig som programmeringsspråk.

Demontering och felsökning

När programmen är färdiga har de redan omvandlats till maskinkod så att processorn kan köra dem. Ibland, om programmet har en bugg (ett fel) i sig, vill programmerarna dock kunna tala om vad varje del av maskinkoden gör. Disassembler är program som hjälper programmerare att göra detta genom att omvandla maskinkoden i programmet tillbaka till assemblerspråk, som är mycket lättare att förstå. Disassemblers, som omvandlar maskinkod till assemblerspråk, gör motsatsen till assemblers, som omvandlar assemblerspråk till maskinkod.



 

Datororganisation

En förståelse för hur datorer är organiserade, hur de verkar fungera på en mycket låg nivå, behövs för att förstå hur ett program i assembleringsspråk fungerar. På den mest förenklade nivån har datorer tre huvuddelar:

  1. Huvudminne eller RAM-minne som innehåller data och instruktioner,
  2. en processor, som bearbetar uppgifterna genom att utföra instruktionerna, och
  3. In- och utdata (ibland förkortat I/O), som gör det möjligt för datorn att kommunicera med omvärlden och lagra data utanför huvudminnet så att den kan hämta tillbaka data senare.

Huvudminne

I de flesta datorer är minnet uppdelat i bytes. Varje byte innehåller 8 bitar. Varje byte i minnet har också en adress som är ett nummer som anger var byten befinner sig i minnet. Den första byten i minnet har adressen 0, nästa byte har adressen 1 och så vidare. Genom att dela upp minnet i bytes blir det byteadresser eftersom varje byte får en unik adress. Adresser till byte-minnen kan inte användas för att hänvisa till en enskild bit i en byte. En byte är den minsta delen av minnet som kan adresseras.

Även om en adress hänvisar till en viss byte i minnet kan processorer använda flera byte i minnet i rad. Den vanligaste användningen av denna funktion är att använda antingen 2 eller 4 bytes i en rad för att representera ett tal, vanligtvis ett heltal. Enstaka bytes används ibland också för att representera heltal, men eftersom de bara är 8 bitar långa kan de bara rymma 28 eller 256 olika möjliga värden. Genom att använda 2 eller 4 bytes i en rad ökar antalet olika möjliga värden till 216 , 65536 eller 232 , 4294967296.

När ett program använder en byte eller ett antal bytes i en rad för att representera något som en bokstav, ett nummer eller något annat kallas dessa bytes för ett objekt eftersom de alla är en del av samma sak. Även om alla objekt lagras i identiska minnesbytes behandlas de som om de har en "typ", som anger hur bytesna ska uppfattas: antingen som ett heltal eller ett tecken eller någon annan typ (t.ex. ett icke heltalsvärde). Maskinkod kan också betraktas som en typ som tolkas som instruktioner. Begreppet typ är mycket, mycket viktigt eftersom det definierar vilka saker som kan och inte kan göras med objektet och hur objektets bytes ska tolkas. Det är till exempel inte tillåtet att lagra ett negativt tal i ett positivt talobjekt och det är inte tillåtet att lagra ett bråk i ett heltal.

En adress som pekar på (är adressen till) ett objekt med flera byte är adressen till den första byten i objektet - den byte som har den lägsta adressen. En viktig sak att notera är att du inte kan avgöra vilken typ ett objekt har - eller ens dess storlek - genom dess adress. Faktum är att du inte ens kan avgöra vilken typ ett objekt är genom att titta på det. Ett assemblerprogram måste hålla reda på vilka minnesadresser som innehåller vilka objekt och hur stora dessa objekt är. Ett program som gör detta är typ-säkert eftersom det bara gör saker med objekt som är säkra att göra på deras typ. Ett program som inte gör det kommer förmodligen inte att fungera korrekt. Observera att de flesta program faktiskt inte uttryckligen lagrar vilken typ ett objekt har, de har bara konsekvent tillgång till objekt - samma objekt behandlas alltid som samma typ.

Processorn

Processorn kör (utför) instruktioner som lagras som maskinkod i huvudminnet. De flesta processorer har inte bara tillgång till minnet för lagring, utan har också några små, snabba utrymmen av fast storlek för att hålla objekt som för närvarande bearbetas. Dessa utrymmen kallas register. Processorer utför vanligtvis tre typer av instruktioner, även om vissa instruktioner kan vara en kombination av dessa typer. Nedan följer några exempel på varje typ i x86-assembleringsspråk.

Instruktioner som läser eller skriver i minnet

Följande instruktion i x86-assembleringsspråk läser (laddar) ett 2-byteobjekt från byteadressen 4096 (0x1000 i hexadecimaltal) till ett 16-bitarregister som kallas "ax":

mov ax, [1000h]

I det här monteringsspråket betyder hakparenteser runt ett nummer (eller ett registernamn) att numret ska användas som en adress till de data som ska användas. Användningen av en adress för att peka på data kallas indirektion. I nästa exempel, utan de fyrkantiga parenteserna, får ett annat register, bx, faktiskt värdet 20 inläst i det.

mov bx, 20

Eftersom ingen indirektion användes, sattes det faktiska värdet in i registret.

Om operanderna (de saker som kommer efter mnemotekniken) visas i omvänd ordning skriver en instruktion som laddar något från minnet det istället till minnet:

mov [1000h], bx

Här får minnet vid adress 1000h värdet bx. Om det här exemplet utförs direkt efter det föregående exemplet kommer de två bytena vid 1000h och 1001h att vara ett heltal på två byte med värdet 20.

Instruktioner som utför matematiska eller logiska operationer.

Vissa instruktioner gör saker som subtraktion eller logiska operationer som inte:

Exemplet med maskinkod tidigare i denna artikel skulle vara detsamma i assemblerspråk:

lägga till yxa, 42

Här adderas 42 och ax och resultatet lagras i ax. I x86-assemblering är det också möjligt att kombinera en minnesåtkomst och en matematisk operation på det här sättet:

lägga till ax, [1000h]

Den här instruktionen lägger till värdet av det 2-bytes heltal som är lagrat på 1000h till ax och lagrar svaret i ax.

eller ax, bx

Denna instruktion beräknar or av innehållet i registren ax och bx och lagrar resultatet i ax.

Instruktioner som bestämmer vad nästa instruktion ska vara.

Vanligtvis utförs instruktionerna i den ordning de visas i minnet, vilket är den ordning de är skrivna i assemblerkoden. Processorn utför dem bara en efter en. Men för att processorer ska kunna göra komplicerade saker måste de utföra olika instruktioner beroende på vilka data de har fått. Processorernas förmåga att exekvera olika instruktioner beroende på något resultat kallas för förgrening. Instruktioner som bestämmer vad nästa instruktion ska vara kallas för förgreningsinstruktioner.

I det här exemplet antar vi att någon vill beräkna hur mycket färg som behövs för att måla en kvadrat med en viss sidlängd. På grund av stordriftsfördelar kommer dock färgbutiken inte att sälja mindre än den mängd färg som behövs för att måla en kvadrat på 100 x 100.

För att räkna ut hur mycket färg de behöver utifrån längden på kvadraten de vill måla, tar de fram följande steg:

  • subtrahera 100 från sidlängden
  • Om svaret är mindre än noll, sätt sidlängden till 100.
  • multiplicera sidlängden med sig själv

Denna algoritm kan uttryckas i följande kod där ax är sidlängden.

mov bx, ax sub bx, 100 jge continue mov ax, 100 continue: mul ax

I det här exemplet introduceras flera nya saker, men de två första instruktionerna är bekanta. De kopierar värdet av ax till bx och subtraherar sedan 100 från bx.

En av de nya sakerna i det här exemplet kallas etikett, ett koncept som finns i assemblerspråk i allmänhet. Etiketter kan vara vad programmeraren vill (om det inte är namnet på en instruktion, vilket skulle förvirra assemblerprogrammet). I det här exemplet är etiketten "continue". Den tolkas av assemblerprogrammet som adressen till en instruktion. I det här fallet är det adressen till mult ax.

Ett annat nytt koncept är flaggor. I x86-processorer sätter många instruktioner "flaggor" i processorn som kan användas av nästa instruktion för att bestämma vad som ska göras. I det här fallet, om bx var mindre än 100, kommer sub att sätta en flagga som säger att resultatet var mindre än noll.

Nästa instruktion är jge, vilket är en förkortning för "jump if greater than or equal to" (hoppa om större än eller lika med). Det är en greninstruktion. Om flaggorna i processorn anger att resultatet var större än eller lika med noll, kommer processorn istället för att bara gå till nästa instruktion att hoppa till instruktionen vid continue label, vilket är mul ax.

Det här exemplet fungerar bra, men det är inte vad de flesta programmerare skulle skriva. Subtraktionsinstruktionen satte flaggan korrekt, men den ändrar också värdet den arbetar på, vilket krävde att ax kopierades till bx. De flesta assemblerspråk tillåter jämförelseinstruktioner som inte ändrar något av de argument de får, men som ändå ställer in flaggorna korrekt, och x86-assembler är inget undantag.

cmp ax, 100 jge continue mov ax, 100 continue: mul ax

Istället för att subtrahera 100 från ax, se om talet är mindre än noll och tilldela det tillbaka till ax, lämnas ax oförändrat. Flaggorna sätts fortfarande på samma sätt och hoppet görs fortfarande i samma situationer.

Inmatning och utmatning

Inmatning och utmatning är en grundläggande del av databehandling, men det finns inte ett enda sätt att göra det på i assemblerspråk. Detta beror på att sättet som I/O fungerar på beror på hur datorn är konfigurerad och vilket operativsystem den har, inte bara på vilken typ av processor den har. I exempelavsnittet nedan använder Hello World-exemplet MS-DOS-operativsystemanrop och exemplet efter det använder BIOS-anrop.

Det är möjligt att göra I/O i assemblerspråk. Assemblerspråk kan i allmänhet uttrycka allt som en dator kan göra. Även om det finns instruktioner för att lägga till och förgrena i assembler som alltid gör samma sak finns det dock inga instruktioner i assembler som alltid gör I/O.

Det är viktigt att notera att det sätt på vilket I/O fungerar inte är en del av något assemblerspråk eftersom det inte är en del av hur processorn fungerar.



 

Samlingsspråk och portabilitet

Även om assemblerspråket inte körs direkt av processorn - vilket maskinkoden gör - har det ändå mycket att göra med den. Varje processorfamilj stöder olika funktioner, instruktioner, regler för vad instruktionerna kan göra och regler för vilka kombinationer av instruktioner som är tillåtna var. På grund av detta behöver olika typer av processorer fortfarande olika monteringsspråk.

Eftersom varje version av assemblerspråket är knuten till en processorfamilj saknar det något som kallas portabilitet. Något som är portabelt kan lätt överföras från en typ av dator till en annan. Medan andra typer av programmeringsspråk är portabla, är assembleringsspråk i allmänhet inte det.



 

Samlingsspråk och högnivåspråk

Även om assembleringsspråk är ett enkelt sätt att använda processorns alla funktioner används det inte i moderna programvaruprojekt av flera skäl:

  • Det är mycket svårt att uttrycka ett enkelt program i assembler.
  • Även om det inte är lika felbenäget som maskinkod erbjuder assemblerspråk fortfarande ett mycket litet skydd mot fel. Nästan alla assemblerspråk tillämpar inte typsäkerhet.
  • Assembly-språk främjar inte goda programmeringsmetoder som modularitet.
  • Även om varje enskild instruktion i assemblerspråk är lätt att förstå är det svårt att avgöra vad programmeraren som skrev den hade för avsikt. Faktum är att ett programs assemblerspråk är så svårt att förstå att företagen inte oroar sig för att folk ska ta isär (ta reda på assemblerspråket i) deras program.

På grund av dessa nackdelar används högnivåspråk som Pascal, C och C++ i de flesta projekt. De gör det möjligt för programmerare att uttrycka sina idéer mer direkt i stället för att behöva oroa sig för att tala om för processorn vad den skall göra varje steg på vägen. De kallas för högnivåspråk eftersom de idéer som programmeraren kan uttrycka i samma mängd kod är mer komplicerade.

Programmerare som skriver kod i kompilerade högnivåspråk använder ett program som kallas kompilator för att omvandla koden till assemblerspråk. Kompilatorer är mycket svårare att skriva än assemblers. Dessutom gör högnivåspråken det inte alltid möjligt för programmerare att använda processorns alla funktioner. Detta beror på att högnivåspråken är utformade för att stödja alla processorfamiljer. Till skillnad från assemblerspråk, som endast stöder en typ av processor, är högnivåspråk portabla.

Även om kompilatorer är mer komplicerade än assemblers har årtionden av tillverkning och forskning gjort dem mycket bra. Nu finns det inte mycket anledning att använda assemblerspråk längre för de flesta projekt, eftersom kompilatorer vanligtvis kan räkna ut hur man uttrycker program i assemblerspråk lika bra eller bättre än programmerare.



 

Exempelprogram

Ett Hello, world! program skrivet i x86-assembler:

adosseg .model small .stack 100h .data hello_message db 'Hello, World! ',0dh,0ah,'$' .code main proc mov ax,@data mov ds,ax mov ah,9 mov dx,offset hello_message int 21h mov ax,4C00h int 21h main endp end main.

En funktion som skriver ut ett nummer på skärmen med hjälp av BIOS-interrupts, skriven i NASM x86-assembler. Det är möjligt att skriva modulär kod i assembler, men det kräver extra ansträngning. Observera att allt som kommer efter ett semikolon på en rad är en kommentar och ignoreras av assembleren. Att sätta kommentarer i assemblerkod är mycket viktigt eftersom stora assemblerprogram är så svåra att förstå.

; void printn(int number, int base); printn: push bp mov bp, sp push ax push bx push cx push dx push si mov si, 0 mov ax, [bp + 4] ; number mov cx, [bp + 6] ; base gloop: inc si ; längden på strängen mov dx, 0 ; noll dx div cx ; dividera med basen cmp dx, 10 ; är det ge 10? jge num add dx, '0' ; lägg till noll till dx jmp anum num: add dx, ('A'- 10) ; hexatekniskt värde, lägg till 'A' till dx - 10. anum: push dx ; lägg dx på stacken. cmp ax, 0 ; ska vi fortsätta? jne gloop mov bx, 7h ; för avbrott tloop: pop ax ; få dess värde mov ah, 0eh ; för avbrott int 10h ; skriv tecken dec si ; gör dig av med tecknet jnz tloop pop si pop dx pop cx pop bx pop ax pop bp ret 4



 

Böcker

  • Michael Singer, PDP-11. Assembler Language Programming and Machine Organization, John Wiley & Sons, NY: 1980.
  • Peter Norton, John Socha, Peter Norton's Assembly Language Book for the IBM PC, Brady Books, NY: 1986.
  • Dominic Sweetman: Se MIPS Run. Morgan Kaufmann Publishers, 1999. ISBN 1-55860-410-3
  • John Waldron: Introduktion till RISC Assembly Language Programming. Addison Wesley, 1998. ISBN 0-201-39828-1
  • Jeff Duntemann: Assembly Language steg för steg. Wiley, 2000. ISBN 0-471-37523-3
  • Paul Carter: PC Assembly Language. Gratis e-bok, 2001.
     Webbplats
  • Robert Britton: MIPS Assembly Language Programming. Prentice Hall, 2003. ISBN 0-13-142044-5
  • Randall Hyde: Konsten att använda samlingsnamnet. No Starch Press, 2003. ISBN 1-886411-97-2
    Utkast finns online Arkiverad 2011-01-28 vid
    Wayback Machine som PDF och HTML.
  • Jonathan Bartlett: Programmering från grunden. Bartlett Publishing, 2004. ISBN 0-9752838-4-7
    Tillgänglig online som PDF och som HTML
  • ASM Community Book "En online-bok full av användbar ASM-information, handledning och kodexempel" av ASM Community

Programvara

  • MenuetOS - Operativsystem skrivet helt och hållet i 64-bitars assemblerspråk
  • SB-Assembler för de flesta 8-bitars processorer/styrenheter
  • GNU lightning, ett bibliotek som genererar assembleringskod vid körning, vilket är användbart för Just-In-Time-kompilatorer.
  • WinAsm Studio, The Assembly IDE - Free Downloads, Source Code , ett gratis Assembly IDE, många program med öppen källkod att ladda ner och ett populärt forum Arkiverad 2008-08-05 vid Wayback Machine
  • Netwide Assembler
  • GoAsm - en gratis komponent "Go"-verktyg: stöd för 32-bitars och 64-bitars Windows-programmering


 

Frågor och svar

F: Vad är ett assembleringsspråk?


S: Ett assembleringsspråk är ett programmeringsspråk som kan användas för att direkt tala om för datorn vad den ska göra. Det är nästan exakt som den maskinkod som en dator kan förstå, förutom att det använder ord i stället för siffror.

F: Hur förstår en dator ett assemblerprogram?


S: En dator kan egentligen inte förstå ett assemblerprogram direkt, men den kan lätt ändra programmet till maskinkod genom att ersätta orden i programmet med de siffror som de står för. Denna process görs med hjälp av en assembler.

F: Vad är instruktioner i ett assembleringsspråk?


S: Instruktioner i ett assembleringsspråk är små uppgifter som datorn utför när den kör programmet. De kallas instruktioner eftersom de instruerar datorn vad den ska göra. Den del av datorn som ansvarar för att följa dessa instruktioner kallas processorn.

F: Vilken typ av programmeringsspråk är assemblering?


S: Assembleringsspråk är ett programmeringsspråk på låg nivå, vilket innebär att det endast kan användas för att utföra enkla uppgifter som en dator kan förstå direkt. För att utföra mer komplexa uppgifter måste man dela upp varje uppgift i dess enskilda komponenter och ge instruktioner för varje komponent separat.

F: Hur skiljer sig detta från högnivåspråk?


S: Högnivåspråk kan ha enskilda kommandon som PRINT "Hello, world!" som säger åt datorn att utföra alla dessa små uppgifter automatiskt utan att man behöver specificera dem individuellt som man skulle behöva göra med ett assemblerprogram. Detta gör högnivåspråk lättare för människor att läsa och förstå än assemblerprogram som består av många enskilda instruktioner.

F: Varför kan det vara svårt för människor att läsa ett monteringsprogram?


S: Eftersom många enskilda instruktioner måste specificeras för att en komplicerad uppgift, t.ex. att skriva ut något på skärmen eller utföra beräkningar på datamängder - saker som verkar mycket grundläggande och enkla när de uttrycks på naturligt mänskligt språk - så kan det finnas många rader kod som utgör en instruktion vilket gör det svårt för människor som inte vet hur datorer fungerar internt på en så låg nivå att följa med och tolka vad som händer i dem.

AlegsaOnline.com - 2020 / 2023 - License CC3