Assemblersprog | programmeringssprog

Et assemblagesprog er et programmeringssprog, der kan bruges til direkte at fortælle computeren, hvad den skal gøre. Et samlesprog er næsten nøjagtig som den maskinkode, som en computer kan forstå, bortset fra at det bruger ord i stedet for tal. En computer kan ikke rigtig forstå et assemblageprogram direkte. Den kan dog nemt ændre programmet til maskinkode ved at erstatte ordene i programmet med de tal, som de står for. Et program, der gør det, kaldes en assembler.

Programmer, der er skrevet i assemblagesprog, består normalt af instruktioner, som er små opgaver, som computeren udfører, når den kører programmet. De kaldes instruktioner, fordi programmøren bruger dem til at instruere computeren om, hvad den skal gøre. Den del af computeren, der følger instruktionerne, er processoren.

Assembleringssproget i en computer er et lavniveausprog, hvilket betyder, at det kun kan bruges til at udføre de enkle opgaver, som en computer kan forstå direkte. For at udføre mere komplekse opgaver skal man fortælle computeren hver enkelt af de simple opgaver, der indgår i den komplekse opgave. En computer forstår f.eks. ikke, hvordan man udskriver en sætning på skærmen. I stedet skal et program, der er skrevet i assembler, fortælle den, hvordan den skal udføre alle de små trin, der er nødvendige for at udskrive sætningen.

Et sådant assemblerprogram ville bestå af mange, mange instruktioner, som tilsammen gør noget, der virker meget simpelt og grundlæggende for et menneske. Dette gør det svært for mennesker at læse et assemblerprogram. I modsætning hertil kan et programmeringssprog på højt niveau have en enkelt instruktion som PRINT "Hello, world!", der fortæller computeren, at den skal udføre alle de små opgaver for dig.



   Zoom
 

Udvikling af samlesprog

Da dataloger først byggede programmerbare maskiner, programmerede de dem direkte i maskinkode, som er en række tal, der anviser computeren, hvad den skal gøre. Det var meget svært at skrive maskinsprog og tog lang tid, så til sidst blev der lavet assemblagesprog. Samlesprog er lettere for et menneske at læse og kan skrives hurtigere, men det er stadig meget sværere for et menneske at bruge end et programmeringssprog på højt niveau, som forsøger at efterligne menneskesprog.

Programmering i maskinkode

For at programmere i maskinkode skal programmøren vide, hvordan hver instruktion ser ud i binær (eller hexadecimal) kode. Selv om det er let for en computer at finde ud af, hvad maskinkode betyder, er det svært for en programmør. Hver instruktion kan have flere forskellige former, som alle bare ligner en masse tal for mennesker. Enhver fejl, som nogen begår, mens de skriver maskinkode, vil først blive bemærket, når computeren gør det forkerte. Det er svært at finde ud af fejlen, fordi de fleste mennesker ikke kan se, hvad maskinkode betyder ved at se på den. Et eksempel på, hvordan maskinkode ser ud:

05 2A 00

Denne hexadecimale maskinkode fortæller en x86-computerprocessor, at den skal tilføje 42 til akkumulatoren. Det er meget vanskeligt for en person at læse og forstå den, selv hvis vedkommende kender maskinkode.

Brug af samlesprog i stedet

I assemblersprog kan hver instruktion skrives som et kort ord, kaldet et mnemonisk ord, efterfulgt af andre ting, f.eks. tal eller andre korte ord. Mnemoteket bruges, så programmøren ikke behøver at huske de nøjagtige tal i maskinkode, der er nødvendige for at fortælle computeren, at den skal gøre noget. Eksempler på mnemoteknikker i assemblagesprog omfatter add, som tilføjer data, og mov, som flytter data fra et sted til et andet. Fordi "mnemoteknik" er et ualmindeligt ord, bruges udtrykket instruktionstype eller blot instruktion nogle gange i stedet, ofte på en forkert måde. Ordene og tallene efter det første ord giver flere oplysninger om, hvad der skal gøres. F.eks. kan tingene efter en addition være, hvilke to ting der skal lægges sammen, og tingene efter mov siger, hvad der skal flyttes, og hvor det skal placeres.

For eksempel kan maskinkoden i det foregående afsnit (05 2A 00) skrives i assembler som:

tilføje ax,42

Assembleringssprog giver også programmører mulighed for at skrive de data, som programmet bruger, på en nemmere måde. De fleste assembler-sprog har understøttelse af, at det er nemt at lave tal og tekst. I maskinkode skal hver enkelt taltype, f.eks. positive, negative eller decimale tal, manuelt konverteres til binære tal, og tekst skal defineres et bogstav ad gangen som tal.

Assembleringssprog er det, der kaldes en abstraktion af maskinkode. Når man bruger assembler, behøver programmører ikke at kende detaljerne i, hvad tal betyder for computeren, det finder assembleren ud af i stedet. Assembleringssprog giver faktisk stadig programmøren mulighed for at bruge alle processorens funktioner, som de kunne med maskinkode. I denne forstand har assemblersprog et meget godt og sjældent træk: det har samme evne til at udtrykke ting som det, det abstraherer (maskinkode), samtidig med at det er meget lettere at bruge. På grund af dette anvendes maskinkode næsten aldrig som programmeringssprog.

Adskillelse og fejlfinding

Når programmerne er færdige, er de allerede blevet omdannet til maskinkode, så processoren rent faktisk kan køre dem. Nogle gange, hvis der er en fejl i programmet, vil programmørerne imidlertid gerne kunne fortælle, hvad hver enkelt del af maskinkoden gør. Disassembler er programmer, der hjælper programmører med at gøre dette ved at omdanne programmets maskinkode tilbage til assemblagesprog, som er meget lettere at forstå. Disassemblere, som omdanner maskinkode til assembler, gør det modsatte af assemblere, som omdanner assembler til maskinkode.



 

Computerorganisation

For at forstå, hvordan computere er organiseret, hvordan de tilsyneladende fungerer på et meget lavt niveau, er det nødvendigt at have en forståelse for, hvordan et assemblerprogram fungerer. På det mest forsimplede niveau har computere tre hoveddele:

  1. hovedhukommelse eller RAM, som indeholder data og instruktioner,
  2. en processor, som behandler dataene ved at udføre instruktionerne, og
  3. input og output (undertiden forkortet til I/O), som gør det muligt for computeren at kommunikere med omverdenen og gemme data uden for hovedhukommelsen, så den kan hente dataene tilbage senere.

Hovedhukommelse

I de fleste computere er hukommelsen opdelt i bytes. Hver byte indeholder 8 bits. Hver byte i hukommelsen har også en adresse, som er et tal, der angiver, hvor byten befinder sig i hukommelsen. Den første byte i hukommelsen har adressen 0, den næste byte har adressen 1 og så videre. Opdelingen af hukommelsen i bytes gør den byteadresserbar, fordi hver byte får en unik adresse. Adresser i bytehukommelser kan ikke bruges til at henvise til en enkelt bit i en byte. En byte er den mindste del af hukommelsen, der kan adresseres.

Selv om en adresse henviser til en bestemt byte i hukommelsen, giver processorer mulighed for at bruge flere bytes hukommelse i træk. Den mest almindelige anvendelse af denne funktion er at bruge enten 2 eller 4 bytes i en række til at repræsentere et tal, normalt et heltal. Enkelte bytes bruges undertiden også til at repræsentere hele tal, men da de kun er 8 bit lange, kan de kun rumme 28 eller 256 forskellige mulige værdier. Ved at bruge 2 eller 4 bytes i en række kan antallet af forskellige mulige værdier øges til henholdsvis 216 , 65536 eller 232 , 4294967296.

Når et program bruger en byte eller et antal bytes i en række til at repræsentere noget som et bogstav, et tal eller noget andet, kaldes disse bytes for et objekt, fordi de alle er en del af den samme ting. Selv om objekter alle er gemt i identiske bytes i hukommelsen, behandles de som om de har en "type", der siger, hvordan bytesne skal forstås: enten som et heltal eller et tegn eller en anden type (f.eks. en ikke-taltværdi). Maskinkode kan også opfattes som en type, der fortolkes som instruktioner. Begrebet type er meget, meget vigtigt, fordi det definerer, hvilke ting der kan og ikke kan gøres med objektet, og hvordan objektets bytes skal fortolkes. Det er f.eks. ikke gyldigt at gemme et negativt tal i et positivt talobjekt, og det er ikke gyldigt at gemme en brøk i et heltal.

En adresse, der peger på (er adressen på) et objekt med flere byte, er adressen på den første byte i dette objekt - den byte, der har den laveste adresse. Som en sidebemærkning er det vigtigt at bemærke, at man ikke kan se, hvilken type et objekt er - eller endda dets størrelse - ud fra dets adresse. Faktisk kan man ikke engang se, hvilken type et objekt er, ved at se på det. Et assemblerprogram skal holde styr på, hvilke hukommelsesadresser der indeholder hvilke objekter, og hvor store disse objekter er. Et program, der gør dette, er type-sikkert, fordi det kun gør ting med objekter, som er sikre at gøre på deres type. Et program, der ikke gør det, vil sandsynligvis ikke fungere korrekt. Bemærk, at de fleste programmer faktisk ikke eksplicit gemmer, hvad typen af et objekt er, de tilgår blot objekter konsekvent - det samme objekt behandles altid som samme type.

Processoren

Processoren kører (udfører) instruktioner, som er gemt som maskinkode i hovedhukommelsen. Ud over at kunne få adgang til hukommelsen til lagring har de fleste processorer nogle få små, hurtige rum af fast størrelse til at opbevare objekter, der er under bearbejdning. Disse rum kaldes registre. Processorer udfører normalt tre typer instruktioner, selv om nogle instruktioner kan være en kombination af disse typer. Nedenfor er der nogle eksempler på hver type i x86-assemblersprog.

Instruktioner, der læser eller skriver hukommelse

Følgende x86-assemblerinstruktion læser (indlæser) et 2-byte objekt fra byteadressen 4096 (0x1000 i hexadecimalt format) til et 16-bit register kaldet "ax":

mov ax, [1000h]

I dette samlesprog betyder firkantede parenteser omkring et tal (eller et registernavn), at tallet skal bruges som en adresse til de data, der skal bruges. Brugen af en adresse til at pege på data kaldes indirektion. I det næste eksempel uden de firkantede parenteser får et andet register, bx, faktisk værdien 20 indlæst i det.

mov bx, 20

Da der ikke blev brugt nogen omledning, blev selve den faktiske værdi sat ind i registret.

Hvis operanderne (de ting, der kommer efter mnemoteket) vises i omvendt rækkefølge, vil en instruktion, der indlæser noget fra hukommelsen, i stedet skrive det til hukommelsen:

mov [1000h], bx

Her får hukommelsen på adressen 1000h værdien bx. Hvis dette eksempel udføres lige efter det foregående, vil de 2 bytes på 1000h og 1001h være et helt tal på 2 bytes med værdien 20.

Instruktioner, der udfører matematiske eller logiske operationer

Nogle instruktioner udfører ting som subtraktion eller logiske operationer som ikke:

Maskinkodeeksemplet tidligere i denne artikel ville være dette i assembler-sprog:

tilføj økse, 42

Her lægges 42 og ax sammen, og resultatet gemmes tilbage i ax. I x86-assembler er det også muligt at kombinere en hukommelsesadgang og en matematisk operation på denne måde:

add ax, [1000h]

Denne instruktion tilføjer værdien af det hele tal på 2 byte, der er gemt i 1000h, til ax og gemmer svaret i ax.

eller ax, bx

Denne instruktion beregner or af indholdet af registrene ax og bx og gemmer resultatet tilbage i ax.

Instruktioner, der bestemmer, hvad den næste instruktion skal være

Normalt udføres instruktioner i den rækkefølge, de vises i hukommelsen, hvilket er den rækkefølge, de er skrevet i assemblerkoden. Processoren udfører dem bare en efter en. Men for at processorer kan udføre komplicerede ting, skal de udføre forskellige instruktioner afhængigt af, hvilke data de har fået. Processorernes evne til at udføre forskellige instruktioner afhængigt af resultatet af noget kaldes branching. Instruktioner, der bestemmer, hvad den næste instruktion skal være, kaldes forgreningsinstruktioner.

I dette eksempel kan du antage, at en person ønsker at beregne den mængde maling, der skal bruges til at male et kvadrat med en bestemt sidelængde. På grund af stordriftsfordele vil farvehandleren imidlertid ikke sælge mindre end den mængde maling, der er nødvendig for at male et kvadrat på 100 x 100 kvadrat.

For at regne ud, hvor meget maling de skal bruge ud fra længden af den firkant, de vil male, finder de frem til dette sæt trin:

  • Træk 100 fra sidelængden
  • hvis svaret er mindre end nul, sættes sidelængden til 100
  • ganges sidelængden med sig selv

Denne algoritme kan udtrykkes i følgende kode, hvor ax er sidelængden.

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

Dette eksempel introducerer flere nye ting, men de to første instruktioner er velkendte. De kopierer værdien af ax til bx og trækker derefter 100 fra bx.

En af de nye ting i dette eksempel kaldes en label, et begreb, der findes i assembler-sprog generelt. Labels kan være hvad som helst, som programmøren ønsker (medmindre det er navnet på en instruktion, hvilket ville forvirre assembleren). I dette eksempel er etiketten "continue". Det fortolkes af assembleren som adressen på en instruktion. I dette tilfælde er det adressen på mult ax.

Et andet nyt koncept er flag. På x86-processorer sætter mange instruktioner "flag" i processoren, som kan bruges af den næste instruktion til at beslutte, hvad der skal ske. I dette tilfælde vil sub, hvis bx var mindre end 100, sætte et flag, der siger, at resultatet var mindre end nul.

Den næste instruktion er jge, som er en forkortelse for "jump if greater than or equal to" (spring hvis større end eller lig med). Det er en forgreningsinstruktion. Hvis flagene i processoren angiver, at resultatet var større end eller lig med nul, vil processoren i stedet for at gå til den næste instruktion springe til instruktionen ved continue-labelet, som er mul ax.

Dette eksempel fungerer fint, men det er ikke det, som de fleste programmører ville skrive. Subtraktionsinstruktionen satte flaget korrekt, men den ændrer også værdien, som den opererer på, hvilket krævede, at ax blev kopieret til bx. De fleste assembler-sprog tillader sammenligningsinstruktioner, som ikke ændrer nogen af de argumenter, de får overdraget, men som stadig sætter flagene korrekt, og x86-assembler er ingen undtagelse.

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

I stedet for at trække 100 fra ax, se, om tallet er mindre end nul, og tildele det tilbage til ax, forbliver ax uændret. Flagene sættes stadig på samme måde, og springet foretages stadig i de samme situationer.

Input og output

Ind- og uddata er en grundlæggende del af computerarbejde, men der er ikke én måde at gøre det på i assembler. Det skyldes, at den måde, hvorpå I/O fungerer, afhænger af computerens opsætning og det operativsystem, den kører, og ikke blot af, hvilken slags processor den har. I eksemplet Hello World-eksemplet i nedenstående afsnit anvendes MS-DOS-operativsystemopkald, og eksemplet efter det anvender BIOS-opkald.

Det er muligt at lave I/O i assemblagesprog. Assemblersprog kan faktisk generelt udtrykke alt, hvad en computer kan gøre. Men selv om der er instruktioner til at tilføje og forgrene i assembler, som altid vil gøre det samme, er der ingen instruktioner i assembler, som altid udfører I/O.

Det er vigtigt at bemærke, at den måde, hvorpå I/O fungerer, ikke er en del af noget assembler-sprog, fordi det ikke er en del af processorens funktion.



 

Samlingssprog og portabilitet

Selv om assemblagesprog ikke køres direkte af processoren - det gør maskinkode - har det stadig meget at gøre med den. Hver processorfamilie understøtter forskellige funktioner, instruktioner, regler for, hvad instruktionerne kan gøre, og regler for, hvilke kombinationer af instruktioner der er tilladt hvor. På grund af dette har forskellige typer processorer stadig brug for forskellige assembler-sprog.

Fordi hver version af assemblagesprog er knyttet til en processorfamilie, mangler det noget, der kaldes portabilitet. Noget, der er bærbart, kan let overføres fra en computertype til en anden. Mens andre typer programmeringssprog er overførbare, er assemblagesprog generelt ikke det.



 

Assembleringssprog og højniveausprog

Selv om assemblagesprog giver mulighed for en nem måde at bruge alle processorens funktioner på, bruges det af flere årsager ikke til moderne softwareprojekter:

  • Det kræver en stor indsats at udtrykke et simpelt program i assembler.
  • Selv om det ikke er lige så fejlbehæftet som maskinkode, er der stadig meget lidt beskyttelse mod fejl i assembler-sprog. Næsten alle assembler-sprog håndhæver ikke type-sikkerhed.
  • Assembleringssprog fremmer ikke god programmeringspraksis som f.eks. modularitet.
  • Selv om hver enkelt instruktion i assembler-sprog er let at forstå, er det svært at se, hvad programmøren, der skrev den, havde til hensigt at gøre. Faktisk er et programs assembler-sprog så svært at forstå, at virksomhederne ikke bekymrer sig om folk, der demonterer (får fat i assembler-sproget i) deres programmer.

Som følge af disse ulemper anvendes højniveausprog som Pascal, C og C++ i stedet for til de fleste projekter. De giver programmørerne mulighed for at udtrykke deres idéer mere direkte i stedet for at skulle bekymre sig om at fortælle processoren, hvad den skal gøre hvert eneste skridt på vejen. De kaldes højniveausprog, fordi de idéer, som programmøren kan udtrykke i samme mængde kode, er mere komplicerede.

Programmører, der skriver kode i kompilerede højniveausprog, bruger et program, der kaldes en compiler, til at omdanne deres kode til assemblagesprog. Compilere er meget sværere at skrive end assemblere. Desuden giver højniveausprog ikke altid programmører mulighed for at udnytte alle processorens funktioner. Det skyldes, at højniveausprog er designet til at understøtte alle processorfamilier. I modsætning til assembler-sprog, der kun understøtter én type processor, er højniveausprog overførbare.

Selv om compilere er mere komplicerede end assemblere, har årtiers arbejde med at lave og forske i compilere gjort dem meget gode. Nu er der ikke meget grund til at bruge assemblersprog længere til de fleste projekter, fordi compilere normalt kan finde ud af at udtrykke programmer i assemblersprog lige så godt eller bedre end programmører.



 

Eksempelprogrammer

Et Hello, world! program skrevet 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, der udskriver et tal på skærmen ved hjælp af BIOS-interrupts skrevet i NASM x86-assembler. Det er muligt at skrive modulær kode i assembler, men det kræver en ekstra indsats. Bemærk, at alt, der kommer efter et semikolon på en linje, er en kommentar og ignoreres af assembleren. Det er meget vigtigt at indsætte kommentarer i assemblerkode, fordi store assemblerprogrammer er så svære at forstå.

; void printn(int number, int base); printn: push bp mov bp, sp push ax push bx 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ængde af strengen mov dx, 0 ; nul dx div cx ; divider med base cmp dx, 10 ; er det ge 10? jge num add dx, '0' ; tilføj nul til dx jmp anum num: add dx, ('A'- 10) ; hex-værdi, tilføj 'A' til dx - 10. anum: push dx ; læg dx på stakken. cmp ax, 0 ; skal vi fortsætte? jne gloop mov bx, 7h ; for interrupt tloop: pop ax ; få dens værdi mov ah, 0eh ; for interrupt int 10h ; skriv tegn dec si ; kom af med tegnet jnz tloop pop si pop dx pop cx pop bx pop ax pop bp ret 4



 

Bøger

  • 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 til RISC Assembly Language Programmering. Addison Wesley, 1998. ISBN 0-201-39828-1
  • Jeff Duntemann: Assembleringssprog trin-for-trin. Wiley, 2000. ISBN 0-471-37523-3
  • Paul Carter: PC Assembly Language. Gratis e-bog, 2001.
     Websted
  • Robert Britton: MIPS Assembly Language Programmering. Prentice Hall, 2003. ISBN 0-13-14204444-5
  • Randall Hyde: The Art of Assembly Language. No Starch Press, 2003. ISBN 1-886411-97-2
    Udkast er tilgængelige online Archived 2011-01-28 at the
    Wayback Machine as PDF and HTML
  • Jonathan Bartlett: Programmering fra bunden. Bartlett Publishing, 2004. ISBN 0-975283838-4-7
    Tilgængelig online som PDF og som HTML
  • ASM Community Book "En online bog fuld af nyttige ASM-informationer, tutorials og kodeeksempler" af ASM Community

Software

  • MenuetOS - Operativsystem skrevet udelukkende i 64-bit assemblagesprog
  • SB-Assembler til de fleste 8-bit processorer/controllere
  • GNU lightning, et bibliotek, der genererer assemblerkode på køretid, hvilket er nyttigt for Just-In-Time-compilere
  • WinAsm Studio, The Assembly IDE - Gratis downloads, kildekode , en gratis Assembly IDE, en masse open source programmer til download og et populært forum Arkiveret 2008-08-05 på Wayback Machine
  • Netwide Assembler
  • GoAsm - en gratis komponent "Go" værktøjer: understøtter 32-bit & 64-bit Windows programmering


 

Spørgsmål og svar

Spørgsmål: Hvad er et samlesprog?


A: Et assemblagesprog er et programmeringssprog, der kan bruges til direkte at fortælle computeren, hvad den skal gøre. Det er næsten nøjagtigt som den maskinkode, som en computer kan forstå, bortset fra at det bruger ord i stedet for tal.

Spørgsmål: Hvordan forstår en computer et assemblageprogram?


Svar: En computer kan ikke rigtig forstå et assemblerprogram direkte, men den kan nemt ændre programmet til maskinkode ved at erstatte ordene i programmet med de tal, de står for. Denne proces sker ved hjælp af en assembler.

Spørgsmål: Hvad er instruktioner i et assemblerprogram?


Svar: Instruktioner i et assemblagesprog er små opgaver, som computeren udfører, når den kører programmet. De kaldes instruktioner, fordi de instruerer computeren om, hvad den skal gøre. Den del af computeren, der er ansvarlig for at følge disse instruktioner, kaldes processoren.

Spørgsmål: Hvilken type programmeringssprog er assembler?


A: Assembleringssprog er et programmeringssprog på lavt niveau, hvilket betyder, at det kun kan bruges til at udføre enkle opgaver, som en computer kan forstå direkte. For at kunne udføre mere komplekse opgaver skal man opdele hver opgave i dens enkelte komponenter og give instruktioner til hver komponent separat.

Spørgsmål: Hvordan adskiller dette sig fra højniveausprog?


A: Højniveausprog kan have enkelte kommandoer som f.eks. PRINT "Hello, world!", som fortæller computeren, at den skal udføre alle disse små opgaver automatisk uden at skulle specificere dem individuelt, som man skal gøre med et assemblerprogram. Dette gør højniveausprog lettere for mennesker at læse og forstå end assemblerprogrammer, der består af mange individuelle instruktioner.

Spørgsmål: Hvorfor kan det være svært for mennesker at læse et assemblerprogram?


A: Fordi der skal specificeres mange individuelle instruktioner for at kunne udføre en kompleks opgave som f.eks. at udskrive noget på skærmen eller udføre beregninger på datasæt - ting, der virker meget grundlæggende og enkle, når de udtrykkes i naturligt menneskesprog - så der kan være mange kodelinjer, der udgør én instruktion, hvilket gør det svært for mennesker, der ikke ved, hvordan computere fungerer internt på et så lavt niveau, at følge med og fortolke, hvad der foregår i dem.

AlegsaOnline.com - 2020 / 2023 - License CC3