Jeg begynner å introdusere instruksjoner i denne delen. De vil bli introdusert etter hvert som vi trenger dem, og jeg vil ikke gi alle detaljene i instruksjonen. For å se detaljene, må du lese ARM-manualene,ARM Architecture Reference Manual ARMv7-A og ARMv7-R utgave[1]for 32-bit ogReferansehåndbok for arkitektur ARMv8, for ARMv8-A arkitekturprofil[2]for 64-bit. Når jeg skal beskrive en instruksjon, vil jeg i hovedsak bruke samme notasjon som i manualene for å gjøre det lettere for deg å lære å lese manualene. Notasjonen i ARMs manualer har en tendens til å variere litt mellom manualene. En forskjell er at jeg vil bruke '%
' for å legge til kommentarene mine. Jeg utelater også noen av de detaljerte alternativene for mange av instruksjonene fordi de ikke gjelder for programmeringen som brukes i denne boken.
ARM gir faktisk et andre instruksjonssett kalt "Thumb." Det gir mulighet for enten 16-biters eller 32-biters instruksjoner. Det kan brukes til å forbedre effektiviteten. Å bruke Thumb er utenfor omfanget av denne boken, men du vil se det nevnt i beskrivelsen av noen instruksjoner i ARM-manualene.
Underseksjon 9.2.1 Noen notasjon
Syntaksen som ARM bruker for sitt assemblerspråk kallesUnified Assembler Language(UAL). Vår montør,som
, gjenkjenner UAL-syntaksen hvis du bruker assembler-direktivene jeg brukte iOppføring 9.1.3for å identifisere ARM-modellen. Dessverre er versjonen avgcc
for øyeblikket (august 2016) som kjører på Raspbian bruker pre-UAL syntaks. Forskjellene er små. For eksempel, det kompilatorgenererte assemblyspråket (Oppføring 9.1.2) bruker en '#
’ tegn for å prefiksere hver bokstavelig verdi:
str fp, [sp, #-4]!
Men UAL-syntaksen spesifiserer at#
' tegnet er valgfritt, så iOppføring 9.1.3Jeg skrev:
str fp, [sp, -4]! @ lagre innringerrammepekeren
Jeg vil ikke bruke '#
’ karakter for umiddelbare verdier i eksemplene mine i denne boken. Jegsterkt oppfordrerdu kan bruke UAL-syntaksen når du skriver dine egne assembly-språkprogrammer. Dette blir veldig viktig når vi kommer til flyttallinstruksjonene iAvsnitt 16.7. Det er der du vil se de største forskjellene mellom UAL- og pre-UAL-syntaksene.
Den generelle notasjonen som brukes til å forklare instruksjoner er:
Store bokstaver representerer tegnene du må bruke, selv om du kan skrive dem med små bokstaver.
Små bokstaver representerer steder der du må oppgi den aktuelle teksten.
Vinkelparenteser, <...>, er steder der du må oppgi riktig verdi (uten «<» og «>».
Krøllete klammeparenteser, {...} representerer valgfrie elementer (uten '{' og '}').
den '
#
’ prefiks for konstante verdier vises, men det er valgfritt når du bruker.syntax unified
direktiv.
Underseksjon 9.2.2 Tilstandskoder
Som nevnt iAvsnitt 8.2, de fleste AARCH32 ARM-instruksjoner har et alternativ som lar deg spesifisere at det bare vil bli utført hvis det finnes en spesifikk innstilling av tilstandsflaggene. Disse innstillingene uttrykkes ved å legge til en mnemonikkTilstandskodetil instruksjonen mnemonic. Disse kodene vises iTabell 9.2.1. «kond
”-kolonnen viser maskinkodedelen av instruksjonen, som vil bli beskrevet iAvsnitt 11.3.3.
kond | Mnemonic | Heltall | Flyte | Tilstand Flagg |
\(\binær{0000}\) | EQ | Lik | Lik | Z == 1 |
\(\binær{0001}\) | NEI | Ikke lik | Ikke lik | Z == 0 |
\(\binær{0010}\) | CS | Bæresett | Større enn, lik eller uordnet | C == 1 |
\(\binær{0011}\) | CC | Bær klart | Mindre enn | C == 0 |
\(\binær{0100}\) | MI | Negativ | Mindre enn | N == 1 |
\(\binær{0101}\) | PL | Positiv, eller null | Større enn, lik eller uordnet | N == 0 |
\(\binær{0110}\) | VS | Flyte | Uordnet | V == 1 |
\(\binær{0111}\) | VC | Ingen overløp | Ikke uordnet | V == 0 |
\(\binær{1000}\) | HI | Usignert høyere | Større enn, eller uordnet | C == 1 OG Z == 0 |
\(\binær{1001}\) | LS | Usignert lavere eller samme | Mindre enn eller lik | C == 0 ELLER Z == 1 |
\(\binær{1010}\) | GE | Signert større enn eller lik | Større enn eller lik | N == V |
\(\binær{1011}\) | LT | Signert mindre enn | Mindre enn, eller uordnet | N != V |
\(\binær{1100}\) | GT | Signert større enn | Større enn | Z == 0 OG N == V |
\(\binær{1101}\) | DE | Signert mindre enn eller lik | Mindre enn, lik eller uordnet | Z == 1 ELLER N != V |
\(\binær{1110}\) | ingen (AL ) | Alltid | Alltid | Noen |
Underseksjon 9.2.3 Skiftalternativer
Mange ARM-instruksjoner inkluderer et alternativ for å skifte en av dataverdiene under operasjonen som instruksjonen utfører.Tabell 9.2.2viser mnemonkodene som brukes og deres respektive effekt.
Konstant | Variabel | Effekt |
LSL # | LSL | Logisk skift venstre \(n\) biter. \(1 \le n \le 31\text{.}\) |
LSR # | LSR | Logisk skift høyre \(n\) biter. \(1 \le n \le 32\text{.}\) |
ASR# | ASR | Aritmetisk skift høyre \(n\) biter. \(1 \le n \le 32\text{.}\) |
ROR # | ROR | Roter høyre \(n\) biter. \(1 \le n \le 31\text{.}\) |
RRX | Roter en bit til høyre, med forleng. Bits [\(31:1\)] forskyves én bit til høyre, og bæreflagget blir forskjøvet til bit [\(31\)]. |
hvor:
- Logisk skifte
Nuller skrives inn i de ledige bitposisjonene.
- Aritmetisk skift til høyre
Verdien i høyordensbiten, \(\binær{0}\) eller \(\binær{1}\tekst{,}\) kopieres til hver av de ledige bitposisjonene, og bevarer dermed tegnet til verdien blir forskjøvet.
- Roter til høyre
Ettersom bitene av lav orden renner fra høyre side (laverekkelige posisjoner), roteres de rundt for å flyte inn i høyordensposisjonene når verdien roteres.
De fleste instruksjoner som tillater skiftalternativet lar deg spesifisere skiftbeløpet, \(n\tekst{,}\) enten som en konstant eller som en variabel i et register. DeRRX
operasjonen er alltid en bit.
Som et eksempel på hvordan skiftende syntaks brukes, de tre instruksjonene:
mov r0, 12mov r1, 60add r2, r0, r1, lsl 2
ville lagre \(252\) ir2
. De to første instruksjonene lagrer \(12\) ir0
og \(60\) inr1
. Delsl #2
i den tredje instruksjonen skifter verdien innr1
to bitposisjoner til venstre, multipliser det med \(4\tekst{.}\) Dette resultatet, \(240\text{,}\) legges til verdien ir0
, og resultatet lagres ir2
. Som du vil se i beskrivelsen avLegg til
nedenfor, verdiene ir0
ogr1
forbli uendret.
Hvis algoritmen din krever at mengden av skiftet er under programkontroll, vil du først lagre skiftbeløpet i et register. For eksempel:
mov r0, 12mov r1, 60mov r3, 2add r2, r0, r1, lsl r3
ville gi samme resultatr2
som ovenfor. Men programmet ditt kan endre verdien ir3
for å oppnå et annet skiftbeløp.
Underseksjon 9.2.4 Første instruksjoner
Selv om programmet iOppføring 9.1.1gjør ingenting, det krever seks instruksjoner.
MOV
Kopierer (flytter) en verdi inn i et register.
MOV{S}{
} , # % immediateMOV{S}{ } , % register Hvis '
S
' er tilstede, oppdateres tilstandsflaggene i henhold til verdien som flyttes. Hvis fraværende, endres ikke tilstandsflaggene.
er tilstandskoden,Tabell 9.2.1.
spesifiserer destinasjonsregisteret, og
er kilderegisteret.\(-257 \le const \le +256\text{,}\) eller \(const = +256, +260, +264, \ldots, +65280\text{,}\) eller \(const = - 261, -265, \ldots, -65281\text{.}\) Denne odde sekvensen av verdier vil bli forklart iAvsnitt 11.3.3.
I "umiddelbar" form, verdien av
er lagret i
. I "register"-skjemaet, verdien i
er kopiert til
. Det er også en skiftversjon, som vil bli forklart iAvsnitt 14.3.
Faktisk bruker ikke montørenmov
instruksjon for negative verdier i umiddelbar form. Hvis du bruker en negativ konstant, trekker assembleren \(1\) fra den absolutte verdien, bruker resultatet som konstanten og erstattermvn
instruksjon. Hvis du bruker en negativ konstant for den umiddelbare formen avmvn
instruksjon, utfører assembleren den samme beregningen og erstattermov
instruksjon.
Så følgende to instruksjoner:
mov r3, 1mov r4, -2
gjør det samme som:
mov r3, 1mvn r4, 1
Og følgende to instruksjoner:
mvn r5, 1mvn r6, -2
er de samme som:
mov r5, -2mov r6, 1
Resultatet er at du ganske enkelt kan bruke både positive og negative konstanter medmov
instruksjon, som jegoppfordrer deg sterkt til å gjøre, og montøren vil gjøre det riktige. Men hvis du leser kompilator-generert assembly-språk, eller du brukerdemontere
kommando inngdb
, kan du semvn
instruksjonen brukes, så jeg vil beskrive den her.
MVN
Kopierer (flytter) komplementet (bitvis IKKE) av en verdi inn i et register.
MVN{S}{
} , # % immediateMVN{S}{ } , {, } % registerMVN(S}{ } , , % registerforskyvet register Hvis '
S
' er tilstede, oppdateres tilstandsflaggene i henhold til verdien som flyttes. Hvis fraværende, endres ikke tilstandsflaggene.
er tilstandskoden,Tabell 9.2.1.
spesifiserer destinasjonsregisteret, og
er kilderegisteret.
inneholder skiftbeløpet i skjemaet «registerflyttet register».\(-257 \le const \le +256\text{,}\) eller \(const = +256, +260, +264, \ldots, +65280\text{,}\) eller \(const = - 261, -265, \ldots, -65281\text{.}\) Denne odde sekvensen av verdier vil bli forklart iAvsnitt 11.3.3
og
er forklart iAvsnitt 9.2.3
I "umiddelbar" form, en kopi av bitvise IKKE av
er lagret i
. I "register" og "register-forskyvde register"-formene, er bitvis IKKE av verdien i
er kopiert til
. Hvis et skifte er spesifisert, vil verdien i
forskyves med den angitte mengden før bitvis NOT beregnes, og resultatet lagres i
. Verdiene i
og
er uendret.LEGG TIL
Legger til to heltall.
ADD{S}{
} { ,} , # % immediateADD{S}{ } { ,} , {, } % registerADD{S}{ } { ,} , , % registerforskyvet register Hvis '
S
' er tilstede, oppdateres tilstandsflaggene i henhold til verdien som flyttes. Hvis fraværende, endres ikke tilstandsflaggene.
er tilstandskoden,Tabell 9.2.1.
spesifiserer destinasjonsregisteret, og
og
er kilderegistrene.
inneholder skiftbeløpet i skjemaet «registerflyttet register».\(-257 \le const \le +256\text{,}\) eller \(const = +256, +260, +264, \ldots, +65280\text{,}\) eller \(const = - 261, -265, \ldots, -65281\text{.}\) Denne odde sekvensen av verdier vil bli forklart iAvsnitt 11.3.3
og
er forklart iAvsnitt 9.2.3
I "umiddelbar" form,
legges til verdien i
. I skjemaene "register" og "registerforskyvet register" vises verdien i
legges til verdien i
. Hvis et skifte er spesifisert, vil verdien i
forskyves med spesifisert mengde før tilsetningen utføres. Hvis
er tilstede resultatet lagres der og
er uendret. Hvis ikke, lagres resultatet i
. Verdiene i
og
er uendret.UNDER
Trekker fra to heltall.
SUB{S}{
} { ,} , # % immediateSUB{S}{ } { ,} , {, } % registerSUB{S}{ } { ,} , , % registerforskyvet register Hvis '
S
' er tilstede, oppdateres tilstandsflaggene i henhold til resultatene av subtraksjonen. Hvis fraværende, endres ikke tilstandsflaggene.
er tilstandskoden,Tabell 9.2.1.
spesifiserer destinasjonsregisteret, og
og
er kilderegistrene.
inneholder skiftbeløpet i skjemaet «registerflyttet register».\(-257 \le const \le +256\text{,}\) eller \(const = +256, +260, +264, \ldots, +65280\text{,}\) eller \(const = - 261, -265, \ldots, -65281\text{.}\) Denne odde sekvensen av verdier vil bli forklart iAvsnitt 11.3.3.
og
er forklart iAvsnitt 9.2.3
I den "umiddelbare" formen
trekkes fra verdien i
. Hvis
er fraværende, lagres forskjellen i
. Hvis
er tilstede, lagres forskjellen der og
forblir uendret.I skjemaene "register" og "registerforskyvet register" vises verdien i
trekkes fra verdien i
. Hvis
er fraværende, lagres forskjellen i
. HvisRd
er tilstede, lagres forskjellen der og
forblir uendret. Hvis et skifte er spesifisert, vil verdien i
forskyves med det angitte beløpet før det trekkes fra verdien i
, og resultatet lagres i
.BX
Forgreninger til et annet sted i programmet. Adressen til stedet er i et register. Tillater også bytte mellom ARM- og Thumb-instruksjonssett.
BX{
}
er tilstandskoden,Tabell 9.2.1.
er måladresseregisteret.
Verdien i
Rm
register flyttes tilpc
, og får dermed programkjøring til å forgrene seg til det stedet. Bit \(0\) spesifiserer instruksjonssettet: \(\binær{0}\) for ARM, \(\binær{1}\) for Thumb. Verdien iRm
endres ikke.LDR
Laster et ord fra minnet inn i et register.
LDR
er tilstandskoden,Tabell 9.2.1.er destinasjonsregisteret, og
er basisregisteret.
er en merket minneadresse.
er et fortegnet heltall i området \(-2048 \ldots +2047\text{.}\)
Minneadressen for å laste ordet fra bestemmes på følgende måte (nærmere forklart iAvsnitt 11.1):
Etikettskjemaet bruker adressen som tilsvarer
.I Offset-skjemaet, det signerte heltall,
, legges til verdien i basisregisteret,
, er verdien på denne adressen lastet inn, men basisregisteret endres ikke.
I det forhåndsindekserte skjemaet legges det signerte heltall til verdien i basisregisteret,
, oppdateres basisregisteret til den nye adressen, og deretter lastes verdien på denne nye adressen inn.
I etterindeksert skjema, verdien i basisregisteret,
, brukes som en adresse, og verdien på den adressen lastes inn. Deretter legges det signerte heltall til verdien i basisregisteret.
STR
Lagrer et ord fra et register i minnet.
STR
er tilstandskoden,Tabell 9.2.1.er kilderegisteret, og
er basisregisteret.
er en merket minneadresse.
er et fortegnet heltall i området \(-2048 \ldots +2047\text{.}\)
Minneadressen for å lagre ordet på bestemmes på følgende måte (nærmere forklart iAvsnitt 11.1):
Etikettskjemaet bruker adressen som tilsvarer
.I Offset-skjemaet, det signerte heltall,
, legges til verdien i basisregisteret,
, verdien ier lagret på denne adressen, men baseregisteret endres ikke..
I det forhåndsindekserte skjemaet legges det signerte heltall til verdien i basisregisteret,
, oppdateres basisregisteret til den nye adressen, og deretter verdien inner lagret på denne adressen.
I etterindeksert skjema, verdien i basisregisteret,
, brukes som adresse, og verdien ier lagret på den adressen. Deretter legges det signerte heltall til verdien i basisregisteret.
Underseksjon 9.2.5 Kodegjennomgang
Vi kan nå gå gjennom koden innOppføring 9.1.3og forklar formålet med hver instruksjon. De to linjene:
hoved: str fp, [sp, -4]! @ lagre innringerrammepekeren
er bare én instruksjon. Plassering av etiketten på sin egen (ellers blank) linje knytter etiketten til neste instruksjon. Dette gjør det lettere å bruke lengre etiketter, noe som ofte forbedrer kodens lesbarhet.
Fra beskrivelsen avstr
, bestemmer denne instruksjonen først en minneadresse ved å trekke \(4\) fra adressen isp
registrere og oppdateresp
registrere deg på denne nye adressen. Den lagrer deretter adressen ifp
registrere deg i minnet på denne nye adressen.
den '!
’ karakter etter[sp, -4]
konstruksjon forårsaker verdien isp
register som skal endres med den numeriske verdien (\(-4\)). Så verdien isp
er \(4\) mindre etter at denne instruksjonen er utført. Årsakene til dette vil bli forklart iAvsnitt 10.2–10.3.
Programmer som kjøres i C-runtime-miljøet, gjør omfattende bruk av stabelen. Hver funksjon i programmet har sitt eget område av stabelen, kjent som enStabelramme. Funksjonen holder styr på hvor rammen er ved å opprettholde minneadressen ifp
registrere. Du vil lære alt om stabler og stablerammer iAvsnitt 10.2, men foreløpig er det viktige å vite at denne instruksjonen lagrer den anropende funksjonens rammepeker og etablerer sin egen rammepeker.
Du vil lære iAvsnitt 10.3at hver funksjon skal ha et område på stabelen til eget bruk. Funksjonen trenger et referansepunkt til det området, som er en adresse som er lagret iRammepekerregistrere. Den forrige instruksjonen lagret anropsfunksjonens rammepeker på stabelen, og denne instruksjonen,
legg til fp, sp, 0 @ etablere vår rammepeker
etablerer en rammepeker for denne funksjonen. Ja,mov fp,sp
ville gi nøyaktig de samme resultatene, men de fleste meningsfulle funksjoner vil lagre andre elementer på stabelen, og rammepekeren må stilles inn tilsvarende. Igjen vil du se hvordan dette fungererAvsnitt 10.3.
Denne funksjonen returnerer \(0\) til den kallende funksjonen (som er i operativsystemet), og den bruker et register,r3
, som en lokal variabel for å holde denne verdien:
mov r3, 0 @ return 0;
Operativsystemet krever at en returverdi er i registeretr0
, så programmet flytter det dit:
mov r0, r3 @ returverdier går i r0
Som du vil lære iAvsnitt 10.2, er det viktig å rydde opp i all bruk av stabelen før du går tilbake til ringefunksjonen. Husk at programmet opprettet en rammepeker i forhold til stabelpekeren da funksjonen først startet. Den må flytte stabelpekeren tilbake til der den var da funksjonen begynte:
sub sp, fp, 0 @ restore stack pointer
Den neste oppryddingsoperasjonen er å gjenopprette rammepekeren til den som brukes av den anropende funksjonen:
ldr fp, [sp], 4 @ gjenopprett innringerens rammepeker
Se nøye på instruksjonen i begynnelsen av funksjonen som lagret den anropende funksjonens rammepeker:
str fp, [sp, -4]! @ lagre innringerrammepekeren
Når du sammenligner den med den som gjenoppretter den, kan du se at du lagrer denforhåndsnedsettelserstabelpekeren før du lagrer den, og gjenoppretting av rammepekeren gjøres ved å hente adressen og deretteretterøkningstabelpekeren. Jeg tenker på disse to operasjonene som litt som parenteser som omgir et begrep i en algebraisk ligning.
Nå som stabelen er ryddet opp, går funksjonen tilbake til kallefunksjonen. Instruksjonen som kalte denne plasserte returadressen i lenkeregisteret,lr
. Så alt denne funksjonen trenger å gjøre er å forgrene seg til adressen ilr
registrere:
bx lr @ tilbake til den som ringer
Du vil lære hvordan dette fungererKapittel 13.