Assembler commands intel 8086 referensbok. Assembly språkprogrammering för nybörjare och mer

De första mikroprocessorerna var 4-bitars enheter. Det betyder att de bara kunde bearbeta fyra informationsbitar åt gången. För att bearbeta mer än fyra bitar var de tvungna att utföra flera sekventiella operationer. Detta bromsade förstås arbetet.

Den första industriella 8-bitars mikroprocessorn (bearbetar 8 bitar information samtidigt) var 8008 mikroprocessorn, släppt av Intel 1972. Den anses vara den bästa 8-bitars mikroprocessorn i den första generationen. I sin arkitektur liknar denna mikroprocessor en miniräknare; den har en ackumulator, sex allmänna register, en stackpekare (ett speciellt arbetscelladressregister), åtta adressregister och speciella instruktioner för datainmatning och -utmatning. 1973 släppte Intel en andra generationens version av 8008-mikroprocessorn, kallad 8080.

Jämfört med 8008-mikroprocessorn kunde 8080-mikroprocessorn adressera mer minne, hade utökade I/O-möjligheter, hade ytterligare instruktioner och var snabbare. Även om idéerna med 8008-mikroprocessorarkitekturen till stor del överfördes av Intel till 8080-mikroprocessorn, förbättrades dess interna organisation så mycket att 8080-mikroprocessorn blev de facto-standarden för andra generationens mikroprocessorer; och när det kommer till mikroprocessorer är det första som kommer att tänka på för många människor 8080-mikroprocessorn.

Framsteg inom teknik gjorde det möjligt för Intel att släppa en förbättrad version av 8080-mikroprocessorn, kallad 8085, 1976. Den skilde sig från 8080-mikroprocessorn i designen av höljet, hade en återställning till det ursprungliga tillståndet (för att initiera mikroprocessorn), vektor avbrott (för service av kringutrustning), en seriell ingångsport - utgång (för att ansluta skrivare och andra kringutrustningar). Dessutom krävde den bara en +5V strömförsörjning (8080-mikroprocessorn krävde två strömförsörjningar).

När 8085-mikroprocessorn släpptes mötte Intel allvarlig konkurrens på 8-bitars mikroprocessormarknaden. Z80-mikroprocessorn från Zilog Corporation, en förbättring av 8080-mikroprocessorn, började njuta av framgång, liksom 6800-mikroprocessorerna från Motorola och 6502 från MOS Technology (nu Commodore), som skilde sig avsevärt från 8080 i sin arkitektur. Och snarare än att fortsätta kämpa på den trånga 8-bitars mikroprocessormarknaden tog Intel ett kvantkliv framåt och släppte 16-bitars 8086-mikroprocessorn 1978, som kunde bearbeta data 10 gånger snabbare än 8080-mikroprocessorn.

8086-mikroprocessorn är mjukvarukompatibel med 8080-mikroprocessorn på assemblerspråksnivå. Detta innebär att med några mindre modifieringar kan program skrivna för mikroprocessorn 8080 återöversättas och exekveras på mikroprocessorn 8086. Denna kompatibilitet säkerställs genom att 8080 mikroprocessorns register och instruktionsuppsättning är delmängder av 8086 mikroprocessorns register och instruktioner Detta gjorde det möjligt för Intel att dra nytta av 8080-mikroprocessorns omfattande erfarenhet och få en fördel i mer komplexa applikationer.

Eftersom många designers fortfarande föredrog att använda billigare 8-bitars hjälp- och kringutrustning i sina 16-bitars system, släppte Intel en version av 8086-mikroprocessorn som hade samma enhet inuti, men en 8-bitars databuss på utsidan. Denna version (8088 mikroprocessor) är identisk med 8086, men tar längre tid att överföra 16-bitars data, vilket åstadkoms med två på varandra följande 8-bitars överföringar. Men i applikationer som primärt bearbetar 8-bitars värden presterar 8088 inom 10 % av 8086.

Du kan alltså anta att din IBM-persondator har en 16-bitars mikroprocessor. (Och därför kan du använda den många litteraturen om 8086-mikroprocessorn.) Låt oss avsluta denna introduktion och gå vidare till att diskutera funktionerna hos 8088-mikroprocessorn.

För att kunna skriva assemblerprogram behöver vi veta vilka processorregister som finns och hur de kan användas. Alla x86-processorer (även flerkärniga, stora och komplexa) är avlägsna ättlingar till den antika Intel 8086 och är kompatibla med dess arkitektur. Det betyder att program skrivna i 8086 assemblerspråk kommer att fungera på alla moderna x86-processorer.

Alla interna register i Intel 8086-processorn är 16-bitars:

Totalt innehåller processorn 12 programvarutillgängliga register, samt ett flaggregister (FLAGS) och en instruktionspekare (IP).

Register för allmänna ändamål (RON) AX, BX, CX och DX används för att lagra data och utföra olika aritmetiska och logiska operationer. Dessutom är vart och ett av dessa register uppdelat i 2 delar om 8-bitar, som kan arbetas med som 8-bitars register (AH, AL, BH, BL, CH, CL, DH, DL). De nedre delarna av registren har bokstaven L i sina namn (från ordet Låg), och de äldre är H (från ordet Hög). Vissa instruktioner använder implicit ett visst register, till exempel kan CX fungera som en loopräknare.

Indexregisterär avsedda för att lagra index när du arbetar med arrayer. SI ( Källindex) innehåller källindexet och DI ( Destinationsindex) är destinationsindexet, även om de också kan användas som register för allmänna ändamål.

Pekarregister BP och SP används för att arbeta med stacken. BP ( Baspekare) låter dig arbeta med variabler på stacken. Den kan också användas för andra ändamål. SP ( Stackpekare) pekar på toppen av stapeln. Det används av kommandon som fungerar med stacken. (Jag kommer att prata om stacken i detalj i en separat del av utbildningen)

Segmentregister CS ( Kodsegment), DS ( Datasegment), SS ( Stack segment) och ES ( Förbättrat segment) är utformade för att tillhandahålla segmentadressering. Koden finns i kodsegmentet, data finns i datasegmentet, stacken finns i stacksegmentet och det finns ytterligare ett datasegment. Den verkliga fysiska adressen erhålls genom att skifta innehållet i segmentregistret 4 bitar åt vänster och lägga till en offset till den (den relativa adressen inom segmentet). Segmentadressering diskuteras mer i detalj i del 31.

Ett COM-program finns alltid i ett enda segment, som samtidigt är ett kod-, data- och stacksegment. När du kör ett COM-program kommer segmentregistren att innehålla samma värden.

Kommandoindex IP ( Instruktionspekare) innehåller adressen till kommandot (i kodsegmentet). Du kan inte direkt ändra dess innehåll, men processorn gör det själv. När du kör vanliga kommandon ökas IP-värdet med storleken på det körda kommandot. Det finns också instruktioner för kontrollöverföring som ändrar IP-värdet för att göra övergångar inom programmet.

Flaggregister FLAGS innehåller separata bitar: kontrollflaggor och resultatflaggor. Kontrollflaggor ändrar processorns driftläge:

  • D ( Riktning) - riktningsflagga. Styr riktningen för behandling av datalinjer: DF=0 - från låga till höga adresser, DF=1 - från höga till låga adresser (för speciella strängkommandon).
  • jag ( Avbryta) - avbryt flagga. Om värdet på denna bit är 1, är avbrott aktiverade, annars är de inaktiverade.
  • T ( Fälla) - spårningsflagga. Används av felsökaren för att köra programmet steg för steg.

Resultatattribut ställs in efter att aritmetiska och logiska kommandon har utförts:

  • S ( Skylt) - tecken på resultatet, lika med teckenbiten för operationsresultatet. Om lika med 1 är resultatet negativt.
  • Z ( Noll) - flagga för noll resultat. ZF=1 om resultatet är noll.
  • P( Paritet) - ett tecken på jämnhet av resultatet.
  • C ( Bära) - bär flagga. CF=1, om det under addition/subtraktion finns ett bär/lån från den högsta siffran. Under skift lagras värdet på den bit som flyttas ut.
  • A ( Extra) - ytterligare bärflagga. Används i operationer med packade binära decimaltal.
  • O( Svämma över) - spillflagga. CF=1 om resultatet ligger utanför det acceptabla värdeintervallet.

Oroa dig inte om något verkar oklart. Från den ytterligare förklaringen kommer det att bli tydligt vad som är vad och hur man använder allt detta :)


Emu8086 är ett betalprogram. Däremot kan du använda den som provversion i 30 dagar gratis.

Så du har laddat ner och installerat Emu8086-programmet på din dator. Vi startar den och skapar en ny fil via menyn FIL – NY – COM-MALL (Arkiv – Ny – COM-filmall). I källkodsredigeraren efter detta kommer vi att se följande:

Ris. 1.1. Skapa en ny fil i Emu8086.

Det bör noteras här att program skapade med hjälp av assemblers för datorer som kör Windows är av två typer: COM och EXE. Vi kommer att titta på skillnaderna mellan dessa filer senare, men för nu räcker det för dig att veta att vi för första gången kommer att skapa körbara filer med COM-tillägget, eftersom de är enklare.

Efter att ha skapat en fil i Emu8086 med metoden som beskrivs ovan, i källkodsredigeraren kommer du att se raden "lägg till din kod hör" - "lägg till din kod här" (Fig. 1.1). Vi tar bort den här raden och infogar följande text i dess ställe:

MOV AH, 02h MOV DL, 41h INT 21h INT 20h Således kommer hela programmets text att se ut så här: ORG 100h MOV AH, 02h MOV DL, 41h INT 21h INT 20h RET Dessutom finns det också kommentarer överst (i fig. 1.1 – detta är grön text). En kommentar på församlingens språk börjar med en symbol; (semikolon) och fortsätter till slutet av raden. Om du inte vet vad kommentarer är och varför de behövs, se boken How to Become a Programmer. Som jag redan sa, här kommer vi inte att förklara grunderna i programmering, eftersom boken du läser nu är avsedd för personer som är bekanta med grunderna i programmering.

Observera också att fallet med tecken i assemblerspråk inte spelar någon roll. Du kan skriva RET, ret eller Ret - det blir samma kommando.

Du kan spara den här filen någonstans på disken. Men du behöver inte spara den. För att köra programmet, tryck på EMULER-knappen (med den gröna triangeln) eller F5-tangenten. Två fönster öppnas: emulatorfönstret och källkodsfönstret (Fig. 1.2).

Ris. 1.2. Emu8086 emulatorfönster.

Emulatorfönstret visar register och innehåller programkontrollknappar. Källfönstret visar källkoden för ditt program och markerar raden som körs för närvarande. Allt detta är väldigt bekvämt för att studera och felsöka program. Men vi behöver inte det här än.

I emulatorfönstret kan du köra ditt program för exekvering i sin helhet (KÖR-knapp) eller i steg-för-steg-läge (SINGLE STEP-knapp). Steg-för-steg-läget är bekvämt för felsökning. Nåväl, nu kommer vi att starta programmet för exekvering med RUN-knappen. Efter detta (om du inte har gjort några fel i programtexten) kommer du att se ett meddelande om programmets slut (Fig. 1.3). Här informeras du om att programmet har överfört kontrollen till operativsystemet, det vill säga att programmet har slutförts framgångsrikt. Klicka på OK i det här fönstret och du kommer äntligen att se resultatet av ditt första program i assemblerspråk (Fig. 1.4).

Ris. 1.3. Meddelande om att programmet är klart.

Ris. 1.4. Ditt första program är klart.

Som vi redan har sagt visar vårt första program den engelska bokstaven "A" på skärmen. Resultatet motsvarade våra förväntningar - bokstaven "A" visades på skärmen.

Det är värt att notera här att Emu8086 är en EMULATOR, det vill säga den emulerar driften av en dator med en processor 8086. Därför, i exemplet som beskrivs ovan, exekveras programmet inte av operativsystemet, utan av emulatorn. Emu8086 kan också skapa riktiga program som kan köras oberoende på en dator. Men en beskrivning av arbetet med Emu8086 ingår inte i våra planer. Läs hjälpen och experimentera - allt kommer att lösa sig för dig.

I vårt fall spelar det ingen roll hur programmet körs - av en emulator eller ett operativsystem. Det viktigaste är att förstå frågan om att skapa program på assemblerspråk. Låt oss därför analysera vårt enkla program i detalj.

#make_COM#– 1:a raden. Detta kommando är specifikt för Emu8086. Den används för att bestämma vilken typ av fil som skapas. I vårt fall är detta en fil med tillägget .COM.

ORG 100h– 2:a raden. Detta kommando ställer in programräknaren till 100h eftersom när en COM-fil läses in i minnet allokerar DOS de första 256 byten (decimal 256 är lika med hex 100) till PSP-datablocket. Programkoden finns först efter detta block. Alla program som kompileras till filer av COM-typ måste börja med detta direktiv.

MOV AH, 02h– 3:e raden. MOV-instruktionen (eller kommandot) placerar värdet på den andra operanden i den första operanden. Det vill säga att värdet 02h placeras i AN-registret. Varför görs detta? 02h är en DOS-funktion som visar en symbol på skärmen. Vi skriver ett program för DOS, så vi använder kommandona för detta operativsystem (OS). Och vi skriver denna funktion (eller snarare dess nummer) i AH-registret, eftersom interrupt 21h använder detta register.

MOV DL, 41h– 4:e raden. Teckenkoden "A" matas in i DL-registret. ASCII-koden för tecknet "A" är 41h.

INT 21h– 5:e raden. Detta är samma avbrott 21h - ett kommando som anropar DOS-systemfunktionen specificerad i AH-registret (i vårt exempel är detta funktion 02h). INT 21h-kommandot är det huvudsakliga sättet för interaktion mellan program och operativsystemet.

INT 20h– 6:e raden. Detta är ett avbrott som talar om för operativsystemet att avsluta programmet och överföra kontrollen till konsolapplikationen. Detta innebär att när du använder INT 20h i vårt exempel kommer kontrollen att överföras till Emu8086-programmet. Och om programmet redan har kompilerats och lanserats från operativsystemet, kommer INT 20h-kommandot att returnera oss till OS (till exempel till DOS). I princip, i fallet med Emu8086, kan detta kommando hoppas över, eftersom samma funktion utförs av kommandot RET, som infogas i källtexten automatiskt när en ny fil skapas med hjälp av en mall (som vi gjorde tidigare). Men jag bestämde mig för att använda INT 20h även här för kompatibilitet med andra montörer.

För den som inte förstår allt från dessa förklaringar rekommenderar jag att läsa boken Hur man blir programmerare, samt följande kapitel.

ASSEMBLERINGSSPRÅKMIKROPROCESSOR 8088

Monteringsspråk är maskinspråk skrivet i symbolisk form. Medan adresser och instruktioner på maskinspråk är siffror, tillåter monteringsspråk att minnesadresser tilldelas unika bokstavsnamn. Eftersom ställdonen för 8086- och 8088-mikroprocessorerna är identiska, visar sig 8088-assemblyspråket också vara identiskt med 8086-assemblyspråket och är en del av assemblerspråket för 80186- och 80286-mikroprocessorerna, som är förbättrade modifieringar av 8086-processorn.

Ett program skrivet på 8088 assemblerspråk kan översättas (översättas) till maskinspråk med hjälp av ett program som heter assemblerare. Den assembler som beskrivs i den här boken är en Microsoft makro assembler som används i IBMs persondator med PC-DOS skivoperativsystem.

Det finns andra versioner av assemblers som är acceptabla för 8086/8088-familjen av mikroprocessorer, till exempel IASM-86 assembler som utvecklats av Intel Corporation, som (inkluderar standardmnemoniska instruktioner, inklusive aritmetiska instruktioner för flyttal i 8087-processorn. Även om skillnaderna mellan dessa assemblers är tillgängliga, är deras strukturer och språkens kommandoformat i stort sett kompatibla.

Varför är assemblerspråk nödvändigt?

Det är väldigt obekvämt, tråkigt och ibland svårt att skriva program direkt på maskinspråk. I det här fallet måste programmeraren utföra mycket noggranna och rutinmässiga operationer: övervaka distributionen av celler i mikrodatorns minne för att spela in kommandon eller programdata, så att dessa adresser sedan kan specificeras eller bestämmas i programmet; kom ihåg faktiska maskininstruktioner, representerade i binär eller hexadecimal kod, och tillhörande mikroadresseringsmetoder


processor; all data i ett program måste omvandlas till binär kod så att den kan ingå i ett maskinspråksprogram. Uppenbarligen, som ett resultat av sådant arbete, kan många misstag göras.

Assembly language är ett sätt att symboliskt skriva program på maskinspråk. Till exempel kan maskininstruktionskoden ZS02 (hexadecimal) skrivas i symbolisk form i assemblerspråk som CMP AL,02 (denna instruktion jämför innehållet i AL-registret med det hexadecimala numret 02). Således är rutinomvandlingar av maskinkoder och programdata listade ovan möjliga. överföra så mycket som möjligt till assemblerprogrammet. Därför är huvudsyftet med assembler att översätta program skrivna på assemblerspråk till maskinspråk (maskinkoder).

Samlaren håller reda på använda minnesplatser så att instruktioner i programmet refererar till dessa adresser symboliskt, med namn och etiketter som ersätter faktiska värden. Till exempel kan maskinkod i hexadecimal notation C606001201 skrivas i assemblerspråk som MOV BYTE-COUNT,01, där BYTE-COUNT är det symboliska namnet på minnesadressen 1200H.

Assembleren tillåter symbolisk representation av konstanter och programdata och utför alla nödvändiga omvandlingar av dem till motsvarande binära koder, beräknar det korrekta antalet byte eller ord som behövs för att lagras, och sammanställer en tabell med namn eller etiketter och deras motsvarande adresser.

I assemblerspråk kan en programmerare skriva en procedur (subrutin) som en oberoende och helt separat modul. Assembleren genererar den information som är nödvändig så att när en procedurobjektmodul laddas i mikroprocessorminnet, kan den kombineras med andra moduler som finns i separata programbibliotek.

Att ändra program skrivna i assemblerspråk är mycket lättare än att ändra program skrivna direkt på maskinspråk. Till exempel kan infogning av kommandon ändras




placera adresserna för operander eller programdata i minnet efter insättningspunkten. Därför, om programmet är skrivet i maskinkod, måste programmeraren ändra adresserna för alla operander och programdata som finns efter det inmatade kommandot. Detta är en mycket tråkig och tidskrävande procedur, för att inte tala om att den är benägen att göra fel. Om infogningen utförs i ett program skrivet i assemblerspråk, utförs omnumreringen av alla operandadresser i minnet automatiskt i enlighet med den nya strukturen och placeringen av data i det modifierade programmet.

Assembleren kan producera en källprogramlista (text) som innehåller de ursprungliga monteringsspråksinstruktionerna, tillhörande maskinspråk och datakoder, diagnostiska resultat och/eller felmeddelanden som genereras under översättningsprocessen. Därför kan många programvarufel upptäckas i förväg vid översättningsstadiet. En programlista innehåller symboler, namn eller etiketter som är symboliska representationer av konstanter eller programdata. Listan i sig är ett bekvämt verktyg för att analysera och korrigera fel som kan uppstå under programfelsökning.

Programformat

Varje enskild så kallad assemblerspråkpåstående översätts vanligtvis till en enda maskininstruktion. På de flesta assemblerspråk har varje sats fyra unikt definierade fält som beskriver olika attribut för instruktionen som översätts. Dessa fält är etikettfältet, operationsfältet, operanderfältet och kommentarsfältet. Varje fält kan vara av variabel längd och måste separeras från sina grannar med ett eller flera mellanslag.

Det första fältet används för att ange namnet eller symbolen för minnesplatsen som innehåller kommandot, konstanten eller data. Adressen till denna cell finns i

Den inbyggda assemblern (nedan helt enkelt assembler) gör det möjligt att programmera på nivån för individuella maskininstruktioner. Detta är huvudskillnaden mellan assembler och Pascal, och alla dess fördelar och nackdelar är koncentrerade i denna skillnad. Fördelen är att programmeraren vid programmering i assemblerspråk vanligtvis väljer en sekvens av maskininstruktioner för att implementera de önskade beräkningarna med maximal hastighet med minimal minnesförbrukning, medan även en så mycket avancerad kompilator som Turbo Pascal-kompilatorn oundvikligen introducerar kod har viss redundans, vilket minskar beräkningshastigheten och ökar minneskostnaderna. Å andra sidan är programmering på nivå med maskininstruktioner en extremt besvärlig uppgift och kan inte jämföras i hastigheten för programutveckling med programmering i Pascal - detta är den största nackdelen med assembler.

För att använda assemblerverktyg måste du ha en klar förståelse för detaljerna i Intel 80x86-mikroprocessorarkitekturen. Denna familj inkluderar mikroprocessorer:

8086 - 16-bitars mikroprocessor som används i IBM PC/XT;

8088 är en analog av 8086, skiljer sig från den endast i interaktion med minnet: 8086 kan byta både byte och 16-bitars ord med minne, medan 8088 bara kan byta byte;

80286 - en förbättrad version av 8086 som används i IBM AT PC; kan fungera i två lägen: i verkligt läge, som helt emulerar driften av MP 8086, och i skyddat läge, där den kan adressera minne upp till 16 MB (i verkligt läge - upp till 1 MB);

80386 - 32-bitars variant av 80286; kan adressera upp till 4 GB;

80486 - kombination 80386/80387, dvs. har ett internt delsystem för implementering av flyttalsoperationer;

80586 (Pentium) - har ett antal förbättringar som ger den en prestandaökning på 2...3 gånger jämfört med 80486, inklusive förmågan att bearbeta 64-bitars tal.

Mikroprocessorer i denna familj ökar sina möjligheter i den listade ordningen, men är strikt kompatibla från yngre modeller till äldre: allt som 8086/8088 kan göra implementeras av Pentium, men inte vice versa. Arkitekturen (intern struktur, adresseringsmetoder och kommandosystem) för MP 8086/8088 diskuteras nedan.



12.1.1. Register

MP 8086/8088 har 14 register. Funktionellt är de indelade i grupper:

· allmänna register (AX, BX, CX, DX); är avsedda för att lagra operander och utföra grundläggande instruktioner; vilken som helst av dem kan användas som en uppsättning av två oberoende 8-bitars register: den höga byten i registret (AN, VN, CH, DH) och den låga byten (AL, BL, CL, DL); till exempel består AX av AN och AL;

· segmentregister (CS, DS, SS, ES); används för att indikera ett segment vid adressering av minne;

· pekarregister (SP, BP, IP); används för att indikera en offset vid adressering av minne;

· indexregister (SI, DI); används för indexadressering;

· flaggregister; används för att lagra indikationer på processortillståndet.

Inom samma funktionsgrupp används register på olika sätt. Detaljerna för att använda register beskrivs nedan.

Registrera AX. Det är huvudadderaren. Används i alla aritmetiska operationer (lägg till, multiplicera, etc.). Endast med hjälp av AX och dess AHIAL-halvregister är det möjligt att utbyta data med I/O-portar.

VX register. Används som adderare i aritmetiska operationer, och även som basregister för indexadressering.

Registrera CX. Används huvudsakligen som räknare när du utför repetition och skiftoperationer. Kan även delta i räkneoperationer.

DX-register. Används som dataregister i I/O-operationer, och även som adderare vid bearbetning av långa heltal (32-bitars).

CS register. Innehåller numret på minnessegmentet (kodsegmentet) där den aktuella maskininstruktionen finns. För att erhålla den fullständiga adressen för nästa kommando, flyttas dess innehåll till vänster med 4 bitar och läggs till i IP-pekarregistret. Innehållet i CS ändras automatiskt i långdistans (intersegment) hopp- och proceduranropskommandon.

IP-register. Bestämmer förskjutningen i förhållande till början av CS-kodsegmentet för nästa körbara maskininstruktion. Innehållet i IP:n ändras automatiskt under instruktionsexekveringen för att säkerställa att instruktioner hämtas från minnet i rätt ordning.

DS register. Innehåller numret på minnessegmentet (datasegmentet) där datan (konstanter och variabler) finns. Alla globala variabler och typkonstanter i ett Turbo Pascal-program är alltid placerade i ett enda segment som adresseras av detta register.

SS-register. Innehåller stacksegmentets nummer. Stacken är en del av autoadresserbart minne utformat för att tillfälligt lagra operander. Med hjälp av stacken organiserar TurboPascal utbytet av data mellan programmet och procedurerna, dessutom placerar den alla lokala variabler (det vill säga variabler som deklareras inuti proceduren) i den. Stackminne används enligt sist-in-först-ut-regeln: den senast pushade operanden på stacken kommer att vara den första som tas bort från den.

SP-register. Pekar på toppen av stapeln, d.v.s. tillsammans med registret 55, adresserar minnescellen där operanden kommer att placeras eller varifrån den kommer att hämtas. Innehållet i detta register minskas automatiskt efter att nästa operand har tryckts på stapeln och ökas efter att operanden har skjutits upp från stapeln.

VR-register. Den så kallade baspekaren. Gör det lättare att skapa och använda en lokal stack (det vill säga en stack för användning inom en procedur).

ES register. Det valfria segmentregistret ES används för datautbyte mellan segment och vissa strängoperationer.

SI-registret. Bestämmer adressen till informationskällan vid indexering av dataadressering (till exempel vid bearbetning av matriser). Används vanligtvis tillsammans med DS-registret.

DI-register. Parat med £5-registret bestämmer det informationsmottagaren för datautbyte mellan segment.

Flaggregister. De enskilda siffrorna (bitarna) i detta register har följande syfte.

Bär flagga CF. Innehåller 1 om en enhet bars under addition eller en enhet lånades under subtraktion. Används även i cykliska och jämförelseoperationer.

PF paritetsflagga. Innehåller 1 om operationen resulterar i ett tal med ett jämnt antal signifikanta siffror, d.v.s. kompletterar resultatet till ett udda tal - används i utbytesoperationer för att kontrollera data.

Extern bärflagga AF. Styr överföringen från den 3:e databiten. Användbar för operationer på packade decimaltal.

ZF nollflagga. Lika med 1 om resultatet av operationen är noll, och lika med 0 annars.

SF tecken flagga. Lika med 1 om resultatet av operationen är ett negativt tal (med en i den mest signifikanta siffran).

TF spår flagga. Lika med 1 om programmet exekveras i steg, med kontroll överförd efter varje utfört kommando via avbrott med vektor 1.

IF-avbrottsflagga. Innehåller 1 om mikroprocessorn är aktiverad för att hantera avbrott.

Riktningsflagga DF. Styr riktningen för dataöverföring: om den innehåller 0, efter varje indexoperation ökas innehållet i indexregistren med 1, annars minskas de med 1.

Overflow flagga OF. Ställ in på ett om operationen resulterar i ett nummer som ligger utanför mikroprocessorbitrutnätet.

12.1.2. Adressering

I MP 8086/8088-arkitekturen specificeras adressen till vilken byte som helst av två 16-bitars ord - ett segment och en offset. När man bildar den 20-bitars fulla adress som behövs för att adressera inom 1 MB, skiftas segmentet till vänster med 4 bitar (multiplicerat med 16) och läggs till med en offset. Eftersom 16-bitars offsetkapacitet är 65536 värden, kan upp till 64 KB adresseras inom ett enda segment.

MP-arkitekturen tillåter användning av sju olika adresseringsmetoder.

Registrera

Hämtar en operand från ett register eller placerar den i ett register. Exempel:

mov akh, bх (Borttagen från BX och placerad i AX)

lägg till cx,ax (Innehållet i AX läggs till i CX)

tryck ex (Push innehållet i CX på stapeln)

Direkt

Operanden (8- eller 16-bitars konstant) finns direkt i instruktionens brödtext. Exempel:

mov yxa, 100 (Ladda in värdet 100 i AX)

lägg till yxa,5 (Lägg till 5 till innehållet i AX)

mov cx,$FFFF (Sätt in värdet 65535 i CX)

Hetero

Operandoffset specificeras i programkroppen och läggs till DS-registret; Till exempel:

X:Word; I: Byte;

mov ah, X (Vi skickar värdet på variabeln X-registret AX)

lägg till ah,B (Vi lägger till värdet av variabel B till innehållet i register AN)

mov X,ax (Vi överför innehållet i AX-registret till minnesområdet för X-variabeln)

Indirekt register

Operaandens exekutiva adress (mer exakt, dess offset) finns i ett av registren ВХ, BP, SI eller DI. För att indikera indirekt adressering måste detta register omges av hakparenteser, till exempel:

mov ax, (Innehållet i 16-bitarsordet lagrat i minnet på adressen DS:BX skickas till AXE-registret);

Vart och ett av BX...DI-registren fungerar som standard med sitt eget segmentregister:

DS:BX, SS:BP, DS:SI, ES:DI

Explicit indikering av segmentregistret är tillåtet om det skiljer sig från standardregistret, till exempel:

Adressering via databas

Basregistret BX (eller BP) innehåller basen (adressen till början av något minnesfragment), i förhållande till vilken assemblern beräknar offset, till exempel:

mov ax,[bx]+10 (Ladda in den 10:e byten i AX från början av minnesbasen på adressen DS-.BX);

Indexadressering

Ett av indexregistren SI eller DI indikerar elementets position i förhållande till början av något minnesområde. Låt till exempel AOB vara namnet på en array av värden av typen Byte. Sedan kan du använda följande fragment:

mov si,15 (Placera konstant 15 i SI)

mov ah, AOB (Vi skickar den 16:e byten från början av arrayen till AN)

mov AOB, ah (Vi skickar det vi fick till det allra första elementet i arrayen)

Adressering via databas med indexering

En variant av indexadressering för fallet när det indexerade minnesområdet specificeras av dess bas. Till exempel:

Denna typ av adressering är användbar vid bearbetning av tvådimensionella arrayer. Om till exempel AOB är en array på 10x10 byte av formuläret

AOB: array av byte;

sedan för att komma åt AOB-elementet kan du använda följande fragment

mov bx,20 (Rad bas 2)

movsi, 2 (3:e elementnummer)

mov yxa,AOB (Elementåtkomst)

12.1.3. Kommandosystem

Tabellerna nedan ger ett minnesmärke för alla giltiga instruktioner för 8086/8088 MP. För enkel användning är alla kommandon indelade i 6 funktionella grupper - dataöverföringar, aritmetik, bit, sträng, kontrollöverföringar, avbrott. Inom varje grupp kombineras team till undergrupper baserat på gemensamma tilläggsegenskaper.

En detaljerad analys av alla MP 8086/8088-kommandon skulle ta för mycket utrymme, så endast de mest populära kommandona beaktas i förklaringarna efter tabellerna. Du hittar en omfattande beskrivning av alla kommandon i,.

Dataöverföringskommandon

Mnemonics Formatera Förklaring
Kommandon för allmänna ändamål
MOV MOV diskbänk, källa Framåtvärde
SKJUTA PÅ PUSH-källa Lägg på högen
POP POP-mottagare Pop från stapeln
XCHG XCHG diskbänk, källa Byt värden
XLAT XLAT bord Ladda en byte från tabellen till AL
I/O-kommandon
I IN batteri, port Läs från hamn
UT OUT-port, batteri Skriv till hamn
Kommandon för vidarebefordran av adress
LEA LEA-register 16, minne 16 Ladda chefsadress
LDS LDS-register 16, minne 32 Ladda hela adressen i DS:register16
LES LES register 16, minne 32 Ladda hela adressen till ES:register16
Flagga kommandon för vidarebefordran
LAHF LAHF Ladda upp flaggor till NA
SAHF SAHF Sätt flaggor från NA
PUSHF PUSHF Placera flaggor på högen
POPF POPF Popflaggor från stack

Ett av de mest använda kommandona, MOV, låter dig säkert överföra en byte eller ord från register till register, från minne till register eller från register till minne. Typen av överförd data (byte eller ord) bestäms av registret som är involverat i överföringen. Följande är exempel på hur du använder kommandot:

mov ah, Tabell (Vidarebefordra ett ord från minnet till AX)

mov Tabell, ah (Vidarebefordra en byte från AN till minnet)

mov ds,ax (Vidarebefordra till datasegment)

mov es:,ax (Vidarebefordra ett ord till minnet: grundläggande adressering med segmentersättning)

mov ch,-17 (vidarebefordra konstant för registrering)

mov Tabell,$FF (Skicka konstant till minnet)

MOV kan inte användas för att skicka:

från minne till minne, till exempel, istället för

borde användas

· en konstant eller en variabel i DS, till exempel, kan inte vara

· ett segmentregister till ett annat, till exempel är det omöjligt

· till CS-registret; värdet på detta register (kodsegment) ändras automatiskt vid exekvering av fjärrstyrda CALL- och JMP-kommandon; dessutom laddas den från stacken när RETF-instruktionen (exit far procedure) exekveras.

Stackinstruktionerna PUSH och POP används i stor utsträckning för att tillfälligt lagra register och data, samt utbyta värden mellan register. Var och en av dem arbetar med ett ord, d.v.s. Du kan inte trycka eller poppa en enda byte på stacken. När PUSH exekveras, minskas innehållet i SP-pekaren först med 2, och sedan placeras operanden på adressen SS:SP. När du hoppar från stacken läses först minnet på adressen SS:SP, och sedan ökas SP med 2. Sålunda, när den är full, skiftas toppen av stackpekaren SP till låga adresser, och när den frigörs, till höga adresser . När du arbetar med stacken bör du komma ihåg detaljerna för att använda stackminne ("sist in, först ut"), samt det faktum att detta minne används intensivt vid anrop av procedurer, d.v.s. stackens tillstånd vid den tidpunkt då proceduren avslutas måste vara strikt förenlig med programmets fortsatta drift. Det första villkoret bestämmer i vilken ordning data hämtas från stacken - det måste vara den omvända ordningen i vilken data skickades till stacken. Det andra villkoret innebär faktiskt att SP-pekaren efter att ha avslutat proceduren måste innehålla samma offset som när den gick in i den. Med andra ord bör proceduren inte "glömma" ett extra ord på traven eller ta mer än vad som behövs från den.

LEA-laddningsadressinstruktionen laddar adressen (offset) för den önskade minnesplatsen i ett register. Detta kan också uppnås genom att använda det reserverade ordet OFFSET före variabelnamnet. Till exempel:

mov axe, OFFSET X (Ladda offset X till AX)

lea yxa,X (Samma åtgärd)

Skillnaden är att LEA-kommandot tillåter användning av indexadressering, vilket är särskilt praktiskt när man skickar massdata.

De andra två adressladdningsinstruktionerna, LDS och LES, laddar det första 16-bitarsordet från källan till destinationsregistret och sedan nästa ord i DS- eller ES-registret, dvs. de är utformade för att ladda den fullständiga adressen för operanden (segment och offset).

Aritmetiska kommandon

Mnemonics Formatera En kommentar
Tilläggskommandon
LÄGG TILL ADD-mottagare, källa Vika ihop
ADC ADC-mottagare, källa Vik, lägg till bär
AAA AAA Justera tillägg för ASCII-tabell
DAA DAA Korrekt tillägg för BCD-nummer
INC INC-mottagare Öka med en
Subtraktionskommandon
SUB SUB-mottagare, källa Subtrahera
SBB SBB-mottagare, källa Dra av med lån
A.A.S. A.A.S. Justera subtraktion för ASCII-tabell
DAS DAS Justera subtraktion för BCD-tal
DEC DEC-mottagare Minska med en
N.E.G. NEG-mottagare Omvänt tecken
bygg- och installationsarbete SMR-mottagare, källa Jämföra
Multiplikationsinstruktioner
MUL MUL-källa Multiplicera utan tecken
IMUL IMUL-källa Multiplicera med tecken
AAM AAM Justera multiplikation för ASCII-tabell
Divisionskommandon
DIV DIV-källa Dela utan tecken
IDIV IDIV källa Dela med tecken
AAD AAD Justera divisionen för ASCII-tabellen
Signera förlängningskommandon
CBW CBW Konvertera byte till word
CWD CWD Konvertera ord till dubbelord

När du använder aritmetiska kommandon, kom ihåg att MP kan behandla signerade nummer, osignerade nummer och BCD-nummer. Osignerade tal använder alla bitar för att representera värdet. de där. de är ekvivalenta med Byte- och Word-typerna, medan tecken med tecken i den mest signifikanta positionen lagrar numrets tecken och är ekvivalenta med ShortInt- och Integer-typerna. BCD-nummer använder 4 bitar för varje decimal och kan packas eller packas upp. I det första fallet lagrar en byte 2 decimalsiffror (den mest signifikanta är i den mest signifikanta biten), i den andra - bara en (den mest signifikanta biten används inte). De grundläggande aritmetiska kommandona för MP (ADD, SUB, MUL, DIV) tar inte hänsyn till den binära-decimala formen av tal, så resultatkorrigeringskommandona ingår i MP-arkitekturen.

Bit instruktioner

Mnemonics Formatera En kommentar
Logiska kommandon
OCH OCH handfat, källa Kör OCH
ELLER ELLER handfat, källa Kör ELLER
XOR XOR-mottagare, källa Kör XOR
INTE INTE mottagare Utför INTE
TESTA TEST mottagare, källa Kolla upp
Skift kommandon
SAL/SHL SAL-mottagare, disk Flytta vänster
SAR/SHR SAR-mottagare, räknare Flytta höger
ROL ROL-mottagare, räknare Flytta vänster cykliskt.
ROR ROR-mottagare, räknare Flytta höger cykliskt
RCL RCL-mottagare, räknare Flytta vänster med bär
RCR RCR-mottagare, räknare Flytta åt höger med wrap

Bitinstruktioner används vid beräkning av logiska uttryck, såväl som i fall där det är nödvändigt att ändra enskilda bitar av operanden. De logiska instruktionerna AND, OR, XOR och NOT är ekvivalenta med motsvarande Turbo Pascal-operationer när operanderna är heltalsuttryck. TEST-kommandot utför en heltalsoperation av bitvis summering OCH, men ändrar inte värdena på operanderna, utan ställer bara in flaggor i enlighet med värdet på jämförelseresultatet: återställer CF och OF, ändrar PF, ZF, SF och ändrar inte AF (ZF-flaggan sätts till 1 om båda operanderna innehåller en i minst en motsvarande bit). SHL/SHR-skiftkommandona är ekvivalenta med Turbo Pascal-operationerna med samma namn och skiljer sig från ROLIROR cykliska skiftkommandon genom att de signifikanta bitarna som förskjuts under deras exekvering går förlorade, medan dessa bitar under ett cykliskt skift visas "på andra sidan ”. Till exempel om du kör fragmentet

mov al,1 (Ladda enheten i AL)

shr al,1 (Skift till höger med 1 siffra)

AL-registret kommer att innehålla 0 (det högerskiftade kommer att placeras i CF), medan det efter att ha ersatt SHR-instruktionen med ROR kommer att innehålla värdet $80=128 (det förskjutna kommer att placeras i den mest signifikanta biten i registret ).

Observera att räknaren i skiftinstruktioner kan vara 1 eller antalet skift som anges i CL-registret.

Kontrollöverföringskommandon

Mnemonics Formatera En kommentar
Ovillkorliga hopp
RING UPP CALL namn Gå in i proceduren
RÖTA RET [antal parametrar] Återgå från proceduren
HOPPA JUMP namn
Villkorliga hopp
JA/JNBE JA close_mark Gå om högre (efter att ha jämfört osignerade operander)
JAE/JNB JAE close_mark Hoppa om det är större än eller lika med
JB/JBAE/JC JB close_mark Hoppa över om nedan
JBE/JNA JBE close_label Hoppa om lägre eller lika
JCXZ JCXZ close_mark Gå om CX=0
JE/JZ JE close_mark Gå om lika
JG/JNLE JG close_mark Hoppa om större (efter att ha jämfört signerade operander)
JGE/JNL LGE close_label Hoppa om det är större än eller lika med
JL/JNGE JL close_mark Hoppa över om mindre
JLE/JNG JLE close_label Hoppa om mindre än eller lika
J.N.C. JNC close_label Gå om det inte finns någon överföring
JNE/JNZ JNE close_label Gå om inte lika
JNO JNO close_label Hoppa om det inte finns något spill
JNP/JPO JNP close_label Hoppa om det är udda
JO JO close_mark Gå om överföring
JP/JPE JP close_mark Gå om ens
JS JS close_label Gå om negativt
Cykelkontrollkommandon
SLINGA LOOP close_label Upprepa cykeln
LOOP/LOOPZ LOOPE close_label Upprepa tills det är lika
LOOPNE/LOOPNZ LOOPNE close_label Upprepa tills det är lika

Ovillkorliga hoppkommandon CALL, RET, JMP kan använda en långt eller nära minnesmodell, medan villkorliga hoppkommandon bara kan använda ett litet (inom -128...+127 byte). Med fjärrminnesmodellen (inställd av alternativet Options/Compiler/Force far calls i Turbo Pascal-miljön eller kompilatordirektivet (F+)), utförs både intrasegment- och intersegmentkontrollöverföring, med den nära etta - endast intrasegment.

CALL-instruktionen fungerar enligt följande. Först placeras adressen för instruktionen efter CALL (returadress) på stacken, sedan placeras adressen för procedurens ingångspunkt i IP-registret (eller i CS:IP-paret), så omedelbart efter CALL-instruktionen det första kommandot i proceduren kommer att utföras. Båda adresserna (ingångs- och returpunkter) kommer att vara 16-bitars offset för ett intrasegmentsamtal, eller 32-bitars fullständiga adresser för ett intersegmentsamtal. Alla Pascal-procedurer (funktioner) översatta i (F+)-läge eller som innehåller det reserverade ordet FAR i rubriken måste anropas som långt. För att göra detta måste CALL-instruktionen följas av minnesmodellen:

Procedur MyProc; Långt;

ring FAR MyProc (Kaller ett långt förfarande)

Alla biblioteksrutiner (det vill säga de som deklareras i modulernas gränssnittsdelar) måste anropas på samma sätt. I ett fjärranrop trycks innehållet i CS-kodsegmentet först på stacken, följt av returförskjutningen.

När man avslutar den bortre proceduren, poppar RET-instruktionen både 16-bitars ord från stacken och placerar det första i IP och det andra i CS, och när man avslutar det närmaste, poppar den bara förskjutningen från stacken och placerar den i IP .

Villkorliga greninstruktioner kan överföra kontrollen till en etikett belägen inom närmaste plus eller minus 128 byte från själva instruktionen. Om du behöver överföra kontrollen till en etikett som ligger längre bort i samma segment, eller till en etikett i ett annat segment, placeras en ovillkorlig JMP- eller CAL-instruktion omedelbart efter kommandot för villkorlig överföring, till exempel:

rädsla,0 (Kontrollerar AH)

jne@NotZero (AX=0?)

jmp IsZero (Ja - gå till det bortre märket)

....... (Nej - vi fortsätter jobba)

I tabellen används termen "ovan/under" i relation till jämförelsen av osignerade operander, och "mer än/mindre" används för att jämföra signerade operander.

Eftersom villkorliga hopp implementerar programförgrening baserat på kontrollflaggor, föregås de vanligtvis omedelbart av kommandon som ändrar dessa flaggor, oftast CMP-jämförelsekommandot. Nedan finns CMP - conditional_jump-kombinationer för olika destinations- och källrelationer (första och andra operanden) i CMP-instruktionen:

Till exempel:

pp ah,5 (AX>5?)

ja @AboveS (Ja, mer - låt oss gå vidare)

sida bx, - 3 (VH<=-3 ?}

jle @LessM3 (Ja, mindre än eller lika med)

LOOP/LOOPE/LOOPNE-kommandona används för att organisera loopar. De använder alla innehållet i CX-registret som en repetitionsräknare. LOOP-instruktionen minskar CX med ett och överför kontrollen till slingstartmärket om innehållet i detta register inte är noll. LOOPE/LOOPNE-instruktionerna minskar också CX-räknaren, men överför kontrollen till början av slingan under det kombinerade villkoret att ZF-flaggan sätts (eller återställs) och CX-räknaren inte är lika med noll.

Så här kan du till exempel hitta nollbyten i AOB-matrisen:

AOB: array av byte;

mov ex,It)00 (Vi initierar CX-räknaren)

lea bx,AOB (Placera AOB-adressen i BX)

dec bx (Förbereder cykeln)

(Här är början på granskningscykeln)

@@Test: inc bx (Adressen till nästa byte)

cmp BYTE PTR ,0 (Kontrollerar byten)

loopne ©Test (Stäng slingan)

jnz ©NotZero (Om nollbyte inte hittas)

....... (Hittade en nollbyte)

Strängkommandon

Mnemonics Formatera
Gillade du artikeln? Dela med vänner: