Inleiding tot Assembler. Werken met registers. Commando's voor adressering en gegevensoverdracht. Rekenkundige bewerkingen met gehele getallen. Methoden voor operandadressering

Operand – het object waarop het machinecommando wordt uitgevoerd.

Operanden in assembleertaal worden beschreven door uitdrukkingen met numerieke en tekstconstanten, labels en variabele-identificatoren met behulp van operatortekens en enkele gereserveerde woorden.

Operanden kunnen worden gecombineerd met rekenkundige, logische, bitsgewijze en attribuutoperatoren om een ​​bepaalde waarde te berekenen of de te beïnvloeden geheugenlocatie te bepalen deze opdracht of richtlijn.

Methoden voor operandadressering

Adresseringsmethoden verwijzen naar bestaande methoden voor het specificeren van het opslagadres van operanden:


De operand is ingesteld op firmwareniveau (standaard operand): In dit geval bevat de opdracht niet expliciet een operand; het algoritme voor opdrachtuitvoering gebruikt enkele standaardobjecten (registers, attributen, enz.).

mul ebx ; eax = eax*ebx, gebruikt impliciet het eax-register


De operand wordt gespecificeerd in de instructie zelf (onmiddellijke operand): De operand maakt deel uit van de instructiecode. Om een ​​dergelijke operand op te slaan, wordt in de opdracht een veld van maximaal 32 bits toegewezen. De onmiddellijke operand kan alleen de tweede operand (bron) zijn. De bestemmingoperand kan zich in het geheugen of in een register bevinden.

verplaats eax, 5; eax = 5;
ebx, 2 toevoegen; ebx = ebx + 2;


De operand bevindt zich in een van de registers (registeroperand): In de code worden opdrachten aangegeven met registernamen. De volgende registers kunnen worden gebruikt:

  • 32-bits registers EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP;
  • 16-bits registers AX, BX, CX, DX, SI, DI, SP, BP;
  • 8-bits registers AN, AL, BH, BL, CH, CL, DH, DL;
  • segmentregisters CS, DS, SS, ES, FS, GS.

eax, ebx toevoegen; eax = eax + ebх
december esi ; esi = esi - 1

De operand bevindt zich in het geheugen. Met deze methode kunt u twee hoofdtypen adressering implementeren:

  • directe adressering;
  • indirecte adressering.

Directe adressering : Het effectieve adres wordt rechtstreeks bepaald door het offsetveld van de machine-instructie, dat 8, 16 of 32 bits groot kan zijn.

verplaats eax, som ; eax = som

De assembler vervangt sum door het overeenkomstige adres dat is opgeslagen in het datasegment (standaard geadresseerd door het ds-register) en plaatst de waarde die is opgeslagen bij sum in het eax-register.

Indirecte adressering heeft op zijn beurt de volgende typen:

  • indirecte basis(register)adressering;
  • indirecte basisadressering (registeradressering) met offset;
  • indirecte indexadressering;
  • indirecte basisindexadressering.

Indirecte basisadressering (register). Met deze adressering kan het effectieve adres van de operand zich in elk register voor algemene doeleinden bevinden, behalve sp/esp en bp/ebp (dit zijn specifieke registers voor het werken met het stapelsegment). Syntactisch in een commando wordt deze adresseringsmodus uitgedrukt door de registernaam tussen vierkante haken te plaatsen.

verplaats eax ; eax = *esi; *esi-waarde op adres esi

Met deze adresseringsmethode kunt u dynamisch een operandadres toewijzen aan een bepaalde machine-instructie en wordt gebruikt bij het organiseren van cyclische berekeningen en bij het werken met datastructuren en arrays.

Indirecte basisadressering (registeradressering) met offset ontworpen om toegang te krijgen tot gegevens met een bekende offset ten opzichte van sommige basis adres, wordt gebruikt om toegang te krijgen tot elementen van structuren wanneer de offset van de elementen vooraf bekend is, in de ontwikkelingsfase van het programma, en het basisadres (start) van de structuur dynamisch moet worden berekend in de fase van programma-uitvoering. Door de inhoud van het basisregister te wijzigen, kunt u toegang krijgen tot elementen met dezelfde naam in verschillende exemplaren van hetzelfde type datastructuren.

verplaats eax, ; eax = *(esi+4)

Indirecte indexadressering. Om een ​​effectief adres te vormen, wordt een van de registers voor algemene doeleinden gebruikt, maar deze heeft de mogelijkheid om de inhoud van het indexregister te schalen.

verplaats eax, mas

Het effectieve adres van de tweede operand wordt berekend door mas+(esi *4) en is de offset vanaf het begin van het datasegment.

De mogelijkheid om te schalen helpt enorm bij het oplossen van het indexeringsprobleem, op voorwaarde dat de grootte van de array-elementen constant is en 1, 2, 4 of 8 bytes bedraagt.

Dit type adressering kan ook met een offset worden gebruikt.

Indirecte basisindexadressering. Het effectieve adres wordt gevormd als de som van de inhoud van twee registers voor algemene doeleinden: basis en index. Deze registers kunnen alle registers voor algemene doeleinden zijn en vaak de inhoud van het indexregister schalen.

verplaats eax

Het effectieve adres van de tweede operand wordt gevormd als esi+edx. De waarde op dit adres wordt in het eax-register geplaatst.

Wanneer indirecte basisindexadressering met een offset wordt gebruikt, wordt het effectieve adres gevormd als de som van drie componenten: de inhoud van het basisregister, de inhoud van het indexregister en de waarde van het offsetveld in de instructie.


De operand is de I/O-poort.
Naast de adresruimte RAM De microprocessor onderhoudt een I/O-adresruimte, die wordt gebruikt om toegang te krijgen tot I/O-apparaten. De I/O-adresruimte is 64 KB. Adressen worden toegewezen voor elk computerapparaat in deze ruimte. De specifieke adreswaarde binnen deze ruimte wordt een I/O-poort genoemd. Fysiek komt de I/O-poort overeen met een hardwareregister (niet te verwarren met een microprocessorregister), waartoe toegang wordt verkregen via speciale elftallen monteur in en uit.

in al, 60 uur ; voeg een byte in van poort 60h

Registers die door een I/O-poort worden geadresseerd, kunnen 8, 16 of 32 bits breed zijn, maar de registerbreedte ligt vast voor een bepaalde poort. Accumulatorregisters eax, ax, al worden gebruikt als informatiebron of ontvanger. De keuze van het register wordt bepaald door de bitgrootte van de poort. Het poortnummer kan worden opgegeven als een directe operand in de in- en uit-instructies of als een waarde in het dx-register. Laatste methode Hiermee kunt u het poortnummer in het programma dynamisch bepalen.

mov dx, 20 uur ; schrijf poortnummer 20h naar dx register
verplaats ,21u ; schrijf de waarde 21h naar het al-register
uit dx, al ; voer de waarde 21h uit naar poort 20h


Adres teller– een specifiek type operand. Dit wordt aangegeven door het $-teken. De specificiteit van deze operand is dat wanneer de assembler-vertaler tegenkomt origineel programma dit teken vervangt het in plaats daarvan de huidige waarde van de adresteller (EIP-register). De adrestellerwaarde vertegenwoordigt de offset van de huidige machine-instructie ten opzichte van het begin van het codesegment dat door het CS-segmentregister wordt geadresseerd. Wanneer de vertaler de volgende assembler-instructie verwerkt, wordt de adresteller verhoogd met de lengte van de gegenereerde machine-instructie. Het verwerken van assembler-richtlijnen verandert de teller niet. Een voorbeeld van het gebruik van een adrestellerwaarde in een opdracht is het volgende fragment:

jmp$+3 ;onvoorwaardelijke sprong naar het mov-commando
nee ; nop-opdrachtlengte is 1 byte
beweeg al, 1

Wanneer je een dergelijke uitdrukking voor springen gebruikt, mag je de lengte van de instructie zelf waarin deze uitdrukking wordt gebruikt niet vergeten, aangezien de waarde van de adresteller overeenkomt met de offset in het codesegment hiervan, en niet met de volgende instructie. In het bovenstaande voorbeeld neemt de jmp-opdracht 2 bytes in beslag. De lengte van deze en enkele andere instructies kan afhangen van welke operanden worden gebruikt. Een instructie met registeroperands zal korter zijn dan een instructie waarbij een van de operanden zich in het geheugen bevindt. In de meeste gevallen kan deze informatie worden verkregen door het formaat van de machine-instructie te kennen.


Structurele operanden gebruikt om toegang te krijgen specifiek onderdeel complex gegevenstype dat een structuur wordt genoemd.

Berichten(vergelijkbaar met structureel type) worden gebruikt om toegang te krijgen beetje veld wat opname. Om toegang te krijgen tot het bitveld van een record, gebruikt u de RECORD-instructie.

Operatoren in assembleertaal

Operanden zijn de elementaire componenten die deel uitmaken van een machine-instructie die de objecten aanduidt waarop een bewerking wordt uitgevoerd. In een algemener geval kunnen operanden worden opgenomen als componenten in complexere formaties uitdrukkingen . Expressies zijn combinaties van operanden en exploitanten , als geheel beschouwd. Het resultaat van het evalueren van een uitdrukking kan het adres zijn van een geheugencel of een constante (absolute) waarde.
De uitvoering van assembler-operatoren bij het evalueren van expressies wordt uitgevoerd in overeenstemming met hun prioriteiten. Bewerkingen met dezelfde prioriteiten worden opeenvolgend van links naar rechts uitgevoerd. Het wijzigen van de volgorde van uitvoering is mogelijk door haakjes te plaatsen die de hoogste prioriteit hebben.

Prioriteit Exploitant
1 lengte, grootte, breedte, masker, (), ,< >
2 .
3 :
4 ptr, offset, seg, dit
5 hoog, laag
6 +, — (unair)
7 *, /, mod, shl, shr
8 +, -, (binair)
9 eq, ne, lt, le, gt, ge
10 niet
11 En
12 of, xor
13 kort, typ

Kenmerken van de belangrijkste operators.

Rekenkundige operatoren. Deze omvatten unaire operatoren + En , binair + En , vermenigvuldigingsoperatoren * , gehele deling / , het verkrijgen van de rest van de divisie mod. Bijvoorbeeld,

maat gelijkwaardig 48 ;arraygrootte in bytes
el eq 4 ;elementgrootte
;het aantal elementen wordt berekend
mov ecx, grootte / el; operator /

Shift-operatoren verschuif de uitdrukking met het opgegeven aantal bits. Bijvoorbeeld,

msk equ 10111011; constante
mov al, msk shr 3; al=00010111 /

Vergelijkingsoperatoren (retourwaarde WAAR of leugen) zijn bedoeld om te vormen logische uitdrukkingen. Booleaanse waarde WAAR komt overeen met een logische, en leugen– logische nul. Logische één is een bitwaarde gelijk aan 1, logische nul is een bitwaarde gelijk aan 0.

maat equ 30; tafelgrootte

mov al , tab_size ge 50 ;al = 0
cmp al , 0 ;if grootte< 50, то
je m1 ;ga naar m1

m1: ...

Als de waarde van size groter is dan of gelijk is aan 50, dan is het resultaat in al 1, anders is het 0. Het cmp-commando vergelijkt de waarde van al met nul en stelt de juiste vlaggen in EFLAGS in. Het je-commando, gebaseerd op de analyse van deze vlaggen, draagt ​​de controle wel of niet over aan het m1-label.

Het doel van vergelijkingsoperatoren wordt gegeven in de tabel

Exploitant Voorwaarde
gelijk ==
ne !=
Het <
le <=
GT >
ge >=

Logische operatoren voer bitsgewijze bewerkingen uit op expressies. Uitdrukkingen moeten constant zijn. Bijvoorbeeld,

L1 gelijk aan 10010011

beweeg al, L1
xor al , 01h ;al=10010010

Index-operator. De vertaler interpreteert de aanwezigheid van vierkante haken als een instructie om de waarde toe te voegen uitdrukkingen voor met betekenis uitdrukkingen, tussen haakjes. Bijvoorbeeld,

mov eax , mas ;eax=*(mas+(esi))

De aanwezigheid van de indexoperator geeft aan de vertaler aan dat hij de waarde op het berekende adres moet verkrijgen.

Operator voor herdefinitie typen ptr gebruikt om het type label of variabele dat door een expressie wordt gedefinieerd, opnieuw te definiëren of te kwalificeren. Het type kan een van de volgende waarden aannemen.

Type Uitleg Doel
byte 1 byte variabel
woord 2 bytes variabel
dwoord 4 bytes variabel
qwoord 8 bytes variabel
seconde 10 bytes variabel
in de buurt van nabij wijzer functie
ver verre wijzer functie

Bijvoorbeeld,

str1 db "Hallo", 0

lea esi, str1

cmp-byte ptr, 0; ==0?

Als u in het voorbeeld een waarde op adres esi wilt vergelijken met een constante, moet u expliciet aangeven welk type gegevens zal worden vergeleken.

Operator voor segmentoverschrijving : (dubbele punt) berekent fysiek adres met betrekking tot een specifiek gespecificeerde segmentcomponent, die kan zijn:

  • segmentregisternaam,
  • segmentnaam uit de overeenkomstige SEGMENT-richtlijn
  • groepsnaam.

Om te selecteren of de volgende instructie moet worden uitgevoerd, analyseert de microprocessor de inhoud van het segmentregister CS, dat het fysieke adres van het begin van het codesegment bevat. Om het adres van een specifieke instructie te verkrijgen, telt de microprocessor de geschaalde (vermenigvuldigd met 16) waarde van het CS-segmentregister op met de inhoud van het EIP-register. De CS:EIP-invoer bevat het adres van de momenteel uitgevoerde opdracht. Operanden in machine-instructies worden op dezelfde manier verwerkt.

Naamgevingsoperator voor structuurtype . (dot) zorgt er ook voor dat de compiler bepaalde berekeningen uitvoert als dit in een expressie voorkomt.

Operator voor het verkrijgen van de segmentcomponent van een expressieadres bijv retourneert het fysieke adres van het segment voor een expressie, wat een label, een variabele, een segmentnaam, een groepsnaam of een symbolische naam kan zijn.

Expressie-offsetoperator gecompenseerd Hiermee kunt u de offsetwaarde van een expressie in bytes verkrijgen ten opzichte van het begin van het segment waarin de expressie is gedefinieerd. Bijvoorbeeld,

Gegevens
str1 db "Hallo" ,0
.code
mov esi, offset str1
beweeg al, ; al = 'P'

Operator voor arraylengte lengte retourneert het aantal elementen dat is opgegeven door de dup-operand. Als er geen dup-operand is, retourneert de lengte-operator de waarde 1. Bijvoorbeeld:

tabl dw 10 dup (?)

mov edx, lengtetabel; edx=10

Exploitant type retourneert het aantal bytes dat overeenkomt met de definitie van de opgegeven variabele:

fldb db?
tabl dw 10 dup (?)

verplaats eax, typ fldb;eax = 1
mov eax , typ tabel ;eax = 2

Exploitant maat geeft het product van lengte en terug soort soort en wordt gebruikt bij het verwijzen naar een variabele met een dup-operand.
Voor het vorige voorbeeld

mov edx , groottetabel ;edx = 20 bytes

Exploitant kort– wijziging van het near-attribuut in het jmp-commando als de overgang de grenzen van +127 en -128 bytes niet overschrijdt. Bijvoorbeeld,

jmp kort label

Als gevolg hiervan reduceert de assembler de machinecode van de operand van twee naar één byte. Deze functie is handig voor korte voorwaartse sprongen, omdat in dit geval de assembler niet zelf de afstand tot het sprongadres kan bepalen en twee bytes reserveert bij afwezigheid van de korte operator.

Exploitant breedte retourneert de grootte in bits van een RECORD-object of zijn veld.

Doelen:

    kennis consolideren van registers voor algemene doeleinden van 32-bit INTEL-processors;

    leer indirecte adressering gebruiken om met RAM te werken;

    Leer de commando's gebruiken om gehele getallen te vermenigvuldigen en te delen.

De belangrijkste belasting bij het bedienen van een computer valt op de processor en het geheugen. De processor voert instructies uit die in het geheugen zijn opgeslagen. Gegevens worden ook in het geheugen opgeslagen. Er is een continue uitwisseling van informatie tussen de processor en het geheugen. De processor heeft een eigen klein geheugen bestaande uit registreert. Een processoropdracht die gegevens in registers gebruikt, wordt veel sneller uitgevoerd dan vergelijkbare opdrachten op gegevens in het geheugen. Om een ​​opdracht uit te voeren, worden daarom vaak eerst de gegevens daarvoor in registers geplaatst. Het resultaat van de opdracht kan indien nodig weer in het geheugen worden geplaatst. Er vindt gegevensuitwisseling tussen geheugen en registers plaats opdrachten doorsturen. Bovendien kunt u gegevens uitwisselen tussen kassa's, gegevens verzenden en ontvangen van externe apparaten. U kunt ook een direct bericht naar een register- en geheugencel sturen. operand- nummer. Daarnaast zijn er opdrachten die kunnen worden gebruikt om gegevens in te voegen en op te halen stapelspeciaal gebied geheugen dat wordt gebruikt om retouradressen op te slaan van functies die aan de functie zijn doorgegeven voor parameters en lokale variabelen.

Adressering en geheugentoewijzing

Voor de processor bestaat al het geheugen uit een reeks cellen van één byte, die elk hun eigen adres hebben. Om met grote getallen te kunnen werken, worden paren cellen gecombineerd tot woorden, paren woorden tot dubbele woorden, paren dubbele woorden tot viervoudige woorden. Meestal werken de programma's bytes, woorden En dubbele woorden(volgens processorregisters van één, twee en vier bytes). Het adres van een woord en dubbelwoord is het adres van zijn lage byte.

Lijst 1 toont een voorbeeld van geheugentoegang met behulp van indirecte adressering. Laten we het in detail bekijken. Merk allereerst op dat het programma een headerbestand bevat , die de headers van alle majors bevat API-functies Windows OS, evenals definitie grote hoeveelheid structuren, variabeletypen (in het bijzonder typedefinitie DWORD, wat er simpelweg op neerkomt niet ondertekend int). Montage-instructies gebruiken variabelen die zijn gedefinieerd door de C-taal. Dit komt door het feit dat de in C ingebouwde assembler geen geheugenreservering toestaat. Het adresseren van geheugen met behulp van variabelen wordt ook wel genoemd directe adressering. Indirecte adressering is als volgt. Als het celadres bijvoorbeeld in een register staat, EAX, dan moet je schrijven om het nummer 100 daarheen te sturen MOV-BYTE PTR, 100. Voorvoegsel BYTE PTR geeft aan dat de bewerking een geheugencel van één byte betreft (u kunt WOORD PTR, DWORD PTR– dit komt overeen met een operand van twee en vier bytes). Gebruik de opdracht om het adres van een geheugencel te achterhalen LEA.

/* headerbestanden opnemen */ #erbij betrekken #erbij betrekken #erbij betrekken /* globale variabelen */ BYTEa= 10; // 8-bit geheel getal zonder teken DWORD-adresRet; // variabele om het adres op te slaan /* hoofdfunctie */ leeg hoofd // (in een 32-bits besturingssysteem duurt het adres van een geheugencel 4 bytes, // Om het adres op te slaan, moet u daarom Extended gebruiken// registers) MOV-adresRet, EAX; // plaats het adres van de variabele in de adresRet-variabele // a, opgeslagen in het EAX-register. Let op: // dit adres verandert elke keer dat het programma wordt gestart MOV-BYTE PTR [EAX], 100; // plaats het op het adres dat is opgeslagen in het EAX-register // het getal 100 - in feite wijzen we het toe aan variabele a// waarde 100); printf("adres van variabele a is %u\n " , adresRet);// druk het adres van variabele a af printf("waarde van variabele a = %u\n " , A) ;// geef de waarde van variabele a weer

_get() ;

) Lijst 1. Dit maakt gebruik van variabele toegang zoals BYTE per index - structuur

BYTE PTR

    . Even later zullen we zien hoe deze techniek wordt gebruikt bij het schrijven van programma's. Opdrachten. Probeer naar het variabele adres te schrijven A, opgeslagen in het register 260 EAH

    , nummer . Welk antwoord heb je gekregen? Waarom? Variabele instellen B type WOORD Variabele instellen DWORD en een variabele 1023 En 70000 C

    . Gebruik indirecte adressering om getallen naar deze variabelen te schrijven respectievelijk. Plaats het in een variabele 70000 Met Lijst 1.:

nummer

, met behulp van een typeaanwijzer B.

    LEA EAX, c; MOV-BYTE PTR [EAX], 70000; Leg uw resultaat uit (onthoud dat het adres van een woord of dubbelwoord het adres van de lage byte is). Doe hetzelfde met behulp van een typeaanwijzer Listing 2 is een programma dat illustreert hoe toegang te krijgen tot variabelen met behulp van pointers. Typ dit programma. Sorteer de opmerkingen. Probeer de elementen van de array te wijzigen. Probeer resultaten uit te voeren in hexadecimaal (in plaats van%u in de functieopmaakregel).

printf() #erbij betrekken gebruik #erbij betrekken %X #erbij betrekken /* gebruik indirecte adressering */// nodig om printf te laten werken // // vereist voor werk _getch();// bevat definities van typen BYTE, WORD, DWORD; BYTE ar[6] = ( 1 , 12 , 128 , 50 , 200 , 10 ) ; statische reeks typ BYTE BYTE a1, a2, a3, a4, a5;// 8-bits getallen zonder teken WOORD b1, b2;// 16-bits getallen zonder teken DWORDc; // 32-bits niet-ondertekend nummer void main // ar om EAX MOV AL, BYTE PTR [EBX] te registreren;// plaats een nummer (type BYTE) in het AL-register // nummer geschreven op het opgeslagen adres // in het EBX-register, dat wil zeggen het eerste element van de array MOV a1, AL; // schrijf de inhoud van het AL-register naar de variabele a/*plaats in variabele a2 het getal geschreven op het adres “begin van de array plus 1 byte”, dat wil zeggen op het adres van het tweede element van de array*/ /* plaats in variabele a4 het nummer geschreven op het adres “het nummer opgeslagen in het EDX-register, beginnend met het nummer geschreven in het EBX-register”, dat wil zeggen het tweede element van de array */ MOV EDX, 1; MOV AL, BYTE PTR [EBX] [EDX]; MOV a4, AL; /* plaats in variabele a5 het getal geschreven op het adres “de som van de getallen geschreven in de EBX- en EDX-registers”, dat wil zeggen het tweede element van de array */ MOV AL, BYTE PTR [EBX+EDX]; MOV a5, AL;/*plaats 2 en 1 array-elementen in variabele b1*/ MOV AX, WORD PTR [EBX]; MOV b1, AX; /*plaats 4 en 3 array-elementen in variabele b2*/ MOV AX, WORD PTR [EBX] + 2; MOV b2, AX;/*plaats array-elementen 6, 5, 4 en 3 in een variabele*/ MOV EAX, DWORD PTR [EBX] + 2; MOVc, EAX; ); printf( "eerste element van array a1 = %u \n ", al) ; printf("tweede element van array a2 = %u \n " , a2) ; printf( "tweede element van array (op een andere manier) a3 = %u \n ", a3) ;

printf(

"tweede element van array (basisadressering) a4 = %u \n ", a4) ; printf("tweede element van array (basisadres - op een andere manier) a5 = %u \n "

, a5) ; printf( "1, 2 elementen van array b1 = %u \n ", b1) ; printf("3, 4 elementen van array b2 = %u \n " , b2) ; printf( "3, 4, 5, 6 elementen van array c = %u \n ", C) ; _get() ; ) Lijst 2. Toegang tot een variabele via een pointer ook gebruikt in talen hoog niveau(heel vaak bij het maken van dynamische arrays). * Wijzer is een variabele die het adres van een andere variabele bevat (een pointer wijst naar een variabele van het type waarvan hij het adres bevat). Er is een één-plaats (unair, dat wil zeggen voor één operand) bewerking waarbij het adres van een variabele wordt overgenomen hoog niveau& (ampersand, zoals in de titel van de Tom&Jerry-tekenfilm). Als we een advertentie hebben * int een , dan kunnen we het bepalen adres

< тип переменной> * < имя указателя>

Dit is de regel voor het declareren van een pointer: een pointer naar een variabele van een bepaald type is een variabele die, wanneer hierop wordt gereageerd door een dereferentiebewerking, de waarde verkrijgt van een variabele van hetzelfde type. Lijst 3 toont een voorbeeld van het gebruik van een aanwijzer in C.

/* het adres van een variabele verkrijgen - C en Assembler vergelijken */ #erbij betrekken gebruik #erbij betrekken %X #erbij betrekken /* gebruik indirecte adressering */ BYTEa= 10; BYTE * cAddr; DWORD asmAddr; BYTE b; ongeldige hoofd-MOV asmAddr, EBX;// plaats de inhoud van het EBX-register in de asmAddr-variabele, // dat wil zeggen adres van variabele a// druk het adres van variabele a af ); cAddr=&a; // schrijf het adres van een variabele van het type BYTE naar een variabele van het type BYTE* b=* cAddr; // verwijder de verwijzing naar variabele a"Assembler: adres van a is %u\n "

, asmAddr);

printf( "C: adres van a is %u\n ", cAddr);

printf("C: waarde van a is %u #erbij betrekken gebruik #erbij betrekken %X #erbij betrekken /* gebruik indirecte adressering */\N" , B) ; _get() ;) Lijst 3. Lijst 4 toont een programma waarmee u de adressen van array-elementen kunt ophalen verschillende soorten betekent C. Besteed aandacht aan de waarden van aangrenzende adressen van array-elementen. /* adressering in arrays */ niet-ondertekende int mas[4] ;// array van gehele getallen van 4 bytes niet-ondertekend int * ptrMas; // verwijzing naar een variabele van het type unsigned int niet-ondertekende short int masShort[4] ; // array van gehele getallen van 2 bytes niet-ondertekende korte int * ptrMasShort; // verwijzing naar een typevariabele niet-ondertekende korte // verwijder de verwijzing naar variabele a int< 4 ; i++ ) printf ("int pointer+%u = %u// verwijder de verwijzing naar variabele a BYTE masBYTE[ 4 ] ; // array van gehele getallen van 1 byte int< 4 ; i++ ) printf (BYTE * ptrMasBYTE;// verwijzing naar een BYTE-variabele void main() ( ptrMas = mas; int< 4 ; i++ ) printf ("byte pointer+%u = %u// verwijder de verwijzing naar variabele a// plaats het adres van het eerste element van de array in de pointer

ptrMasKort = masKort;

ptrMasBYTE = masBYTE; printf("matrix van int);

voor (int i= 0; ik #erbij betrekken gebruik #erbij betrekken #erbij betrekken , i, ptrMas+ i) ; #erbij betrekken #erbij betrekken printf("\n reeks korte int \n " "korte aanwijzer+%u = %u\n " , ik, ptrMasShort+ i) ; printf(" \n reeks BYTE \n "() - aantal bytes. We hebben een array van 10 gehele getallen nodig. Daarom is het totale aantal bytes de grootte van een int (bepaald door de functie sizeof()) vermenigvuldigd met het aantal array-elementen. De constructie (int*) voordat malloc() een cast naar het int*-type uitvoert (dat wil zeggen dat het toegewezen geheugen nu door de compiler wordt beschouwd als een set van 4 bytecellen waarin getallen van het type int zijn opgeslagen) */ ptint = (int *) malloc (10 * grootte van (int ) ); /* vul de array */ voor (int i= 0; ik< 10 ; i++ ) ptint[ i] = i; /*array-elementen weergeven*/ voor (int i= 0; ik< 10 ; i++ ) printf ("%d " , ptint[ i] ) ; free (ptint) ; // maak geheugen vrij// geef de waarde van variabele a weer

Lijst 5.

Oefening. Geef de adressen weer van de elementen van de array die is gemaakt in het programma dat wordt weergegeven in Lijst 5. Probeer een dynamische array te maken zoals dubbele, vul het in, druk de array-elementen en hun adressen af.

Rekenkundige bewerkingen op gehele getallen

Optellen en aftrekken van gehele getallen

Laten we eens kijken naar 3 basiscommando's voor optellen. Team INC voert een verhoging uit, d.w.z. verhoog de inhoud van de operand met 1, bijvoorbeeld INC EAX. Team INC zet vlaggen OF, SF, ZF, AF, PF afhankelijk van de resultaten van de toevoeging. Team TOEVOEGEN voert de optelling van twee operanden uit. Het resultaat wordt naar de eerste operand (ontvanger) geschreven. De eerste operand kan een register of een variabele zijn. De tweede operand is een register, variabele of getal. Het is echter onmogelijk om de optelling op twee variabelen tegelijk uit te voeren. Commando werkt op vlaggen CF, OF, SF, ZF, AF, PF. Het kan worden gebruikt voor ondertekende en niet-ondertekende nummers. Team ADC voert de optelling van twee operanden uit als een commando TOEVOEGEN en een carry-vlag (bit). Met zijn hulp kunt u getallen toevoegen waarvan de grootte groter is dan 32 bits of waarvan de oorspronkelijke lengte van operanden groter is dan 32 bits.

/* gehele getallen optellen */ #erbij betrekken #erbij betrekken // vereist om _gech() te laten werken #erbij betrekken /* gebruik indirecte adressering */ int a, b, c; DWORD d, e, f, m, n, l, k; void main() ( a= 100 ; b=- 200 ; f= 0 ; d= 0xffffffff ; e= 0x00000010 ; m= 0x12345678 ; n= 0xeeeeeeee ; l= 0x11111111 ; k= 0x22222222 ; __asm( /* positieve en negatieve getallen optellen */ MOV EAX, een; */ VOEG EAX toe, b; MOVc, EAX; /* twee toevoegen grote aantallen MOV EAX, e; // EAX = 0x00000010 TOEVOEGEN d, EAX;// resultaat groter dan 4 bytes, dus de CF-vlag // ingesteld op 1: // 0xffffffff // + 0x00000010 // ---------- // dus binnen in dit geval het uitvoeren van de opdracht wordt gereduceerd tot het plaatsen ervan // variabele f-waarde CF /* optelling van twee grote getallen in een paar registers */ MOV EDX, m; // plaatste de belangrijkste 4 bytes van het eerste getal in EDX,//EDX=0x12345678 MOV EAX, n; // plaatste de onderste 4 bytes van het eerste getal in EAX,// EAX=0xeeeeeeee MOV ECX, l; // plaatste de bovenste 4 bytes van het tweede getal in ECX,// ECX=0x11111111 MOV EBX, k; // plaatste de onderste 4 bytes van het eerste nummer in EBX,// EBX=0x22222222 VOEG EAX, EBX toe; // voegde de lage 4 bytes toe MOV n, EAX; ADC EDX, ECX;// heeft de belangrijkste 4 bytes opgeteld // verwijder de verwijzing naar variabele a MOV m, EDX; // verwijder de verwijzing naar variabele a); printf("c=a+b=%d, C) ; printf("f=d+e=%x%x , f, d); printf(

"som van de laagste 4 bytes = %x\n "

, N) ; printf( #erbij betrekken "som van de hoogste 4 bytes = %x\n " #erbij betrekken // vereist om _gech() te laten werken #erbij betrekken /* gebruik indirecte adressering */, M) ; _get() ; ) Lijst 6. /*hele getallen aftrekken*/// nodig om printf() te laten werken int a, b, c; __int64 ik, j, k; void main() ( a= 100 ; b=- 200 ; i= 0x1ffffffff ; j= 0x1fffffffb ; __asm( /* 32-bits getallen aftrekken */ MOV EAX, een; SUB EAX, b; MOVc, EAX;/* 64-bits getallen aftrekken */ MOV EAX, DWORD PTR i; // plaatste het adres van de onderste 4 bytes van nummer i in EAX.// Het nummer 0xffffffff wordt naar dit adres geschreven MOV EDX, DWORD PTR i+ 4 ; // plaatste het adres van de hoogste 4 bytes van nummer i in EDX. MOV EBX, DWORD PTR j; // plaatste het adres van de onderste 4 bytes van het getal j in EBX. // Het nummer 0xffffffb wordt naar dit adres geschreven MOV ECX, DWORD PTR j+ 4 ; // plaatste het adres van de hoogste 4 bytes van het getal j in ECX.// Het nummer 0x00000001 wordt naar dit adres geschreven SUB-EAX, EBX;// trek de onderste 4 bytes af van de onderste 4 bytes van het getal i // verwijder de verwijzing naar variabele a// cijfers j. Deze bewerking heeft invloed op de CF-vlag // verwijder de verwijzing naar variabele a SBB EDX, ECX;

// trek de hoogste 4 bytes af van de hoogste 4 bytes van het getal i

// cijfers j, evenals de CF-vlag

MOV DWORD PTR k, EAX; // plaats de onderste 4 bytes van het resultaat in het geheugen MOV DWORD PTR k+ 4, EDX; // plaats de bovenste 4 bytes van het resultaat in het geheugen); printf("c=a+b=%d, C) ; printf("k=i-j=%I64x , k) ;); _get() ;) // plaats de bovenste 4 bytes van het resultaat in het geheugen Lijst 7.

    Gehele getallen vermenigvuldigen In tegenstelling tot optellen en aftrekken vermenigvuldiging is gevoelig voor het teken van het getal, dus er zijn twee vermenigvuldigingsopdrachten: MUL – voor vermenigvuldiging niet ondertekend cijfers, En IMUL zal gelijk zijn aan 0, anders – 1.

    Gehele getallen vermenigvuldigen dubbel-byte vermenigvuldiging – voor vermenigvuldiging, en het resultaat wordt in een aantal registers geplaatst DX:AX(niet erin EAX, wat misschien logisch lijkt). Dienovereenkomstig, als het resultaat er volledig in past – voor vermenigvuldiging, d.w.z. inhoud DX gelijk is aan 0, dan zijn de vlaggen ook gelijk aan nul IMUL En cijfers,.

    Ten slotte, als de broninstructie de lengte heeft vier bytes vermenigvuldiging EAX, en het resultaat moet in een paar registers worden geplaatst EDX:EAX. Als de inhoud EDX na vermenigvuldiging zal dan gelijk zijn aan nul nulwaarde de vlaggen zullen het ook hebben IMUL En cijfers,.

Team , k) ; heeft er 3 verschillende formaten. Het eerste formaat is vergelijkbaar met de opdracht // plaats de bovenste 4 bytes van het resultaat in het geheugen. Laten we eens kijken naar de andere twee formaten.

IMUL-operand1, operand2

operand1 er moet een register zijn operand2 kan een getal, een register of een variabele zijn. Als resultaat van het uitvoeren van de vermenigvuldiging ( operand1 vermenigvuldigd met operand2 en het resultaat wordt erin geplaatst operand1) kan resulteren in een getal dat niet in de ontvanger past. In dit geval de vlaggen IMUL En AF zal gelijk zijn aan 1 (anders 0).

IMUL-operand1, operand2, operand3

In dit geval operand2(register of variabele) vermenigvuldigd met operand3(getal) en het resultaat wordt ingevoerd operand1(register). Als er tijdens de vermenigvuldiging een overloop optreedt, d.w.z. Als het resultaat niet in de ontvanger past, worden de vlaggen ingesteld IMUL En cijfers,. Het gebruik van vermenigvuldigingscommando's wordt getoond in Lijst 8.

#erbij betrekken "som van de hoogste 4 bytes = %x\n " #erbij betrekken // vereist om _gech() te laten werken #erbij betrekken /* gebruik indirecte adressering */ DWORD a= 100000; __int64b; intc=- 1000; int e; leegte hoofd() ( __asm( /* niet-ondertekende vermenigvuldiging */ MOV EAX, 100000 ;// plaats een getal groter dan 2 bytes in EAX MUL DWORD PTR a; // vermenigvuldig de inhoud van het EAX-register met a,// het resultaat wordt in een aantal registers geplaatst //EDX:EAX MOV DWORD PTR b, EAX; // plaats het in de lage 4 bytes // 8-byte variabele b heeft de onderste 4 bytes van het resultaat MOV DWORD PTR b+ 4, EDX; // plaats het in de bovenste 4 bytes// 8-byte variabele b, de meest significante 4 bytes van het resultaat /* ondertekende vermenigvuldiging */ IMUL EAX, s, 1000; // verwijder de verwijzing naar variabele a// vermenigvuldig c met 1000 en plaats het resultaat in EAX MOV e, EAX;// plaats het resultaat van de vermenigvuldiging in de variabele e // verwijder de verwijzing naar variabele a);

printf("a*100000 = %I64d

, B) ;

// interpreteer het uitvoernummer als __int64 printf("e = %d , e) ; _get() ;

    ) 1 byte Lijst 8. Vermenigvuldigingscommando's gebruiken – voor vermenigvuldiging is gevoelig voor het teken van het getal, dus er zijn twee vermenigvuldigingsopdrachten: Deling van gehele getallen Divisie niet-ondertekende nummers worden gedaan met behulp van de opdracht

    ) 2 bytes DX:AX DIV – voor vermenigvuldiging Deling van gehele getallen DX niet-ondertekende nummers worden gedaan met behulp van de opdracht

    ) 4 bytes. Het commando heeft slechts één operand: dit is de deler. De deler kan een register of een geheugenlocatie zijn. Afhankelijk van de grootte van de deler wordt het dividend gekozen. EDX:EAX DIV EAX Deling van gehele getallen EDX niet-ondertekende nummers worden gedaan met behulp van de opdracht

Ondertekend divisiecommando IDIV volledig vergelijkbaar met de opdracht , e) ;. Het is belangrijk dat voor delingsinstructies de waarden van de rekenkundige bewerkingsvlaggen niet zijn gedefinieerd. De deling kan resulteren in een overflow of een deling door 0. Het besturingssysteem moet de uitzondering afhandelen.

#erbij betrekken "som van de hoogste 4 bytes = %x\n " #erbij betrekken // vereist om _gech() te laten werken #erbij betrekken /* gebruik indirecte adressering */ DWORD a, b, c; void main() (a= 100000;// dividend - 4 bytes __asm(/* niet-ondertekende divisie */ MOV EAX, een;// plaatste de lage 4 bytes van het dividend in het EAX-register MOV EDX, 0;// plaatste de bovenste 4 bytes van het dividend in het EDX-register MOV EBX, 30 ;// plaatste een scheidingslijn in EBX (4 bytes!) DIV EBX;// verdeelde de inhoud van EDX:EAX door // inhoud EBX MOV b, EAX;
  • // plaats het quotiënt in b
  • NASM/YASM vereist een woord wanneer de grootte van de operand niet wordt geïmpliceerd door de andere operand. (Anders oké).

MASM/TASM vereist woord ptr wanneer de grootte van de operand niet wordt geïmpliceerd door de andere operand. (Anders oké).

Elk ervan wordt verstikt door een andere syntaxis.

WAARSCHUWING: Dit is een heel vreemd gebied zonder enige ISO-standaard of direct beschikbare BNF-tabellen; en ik ben geen expert in het navigeren door de mijnenvelden van de propriëtaire MASM-syntaxis.

In uw geval maakt het misschien geen verschil, maar de PTR-operator kan in andere gevallen het volgende bedoelen:

Over het algemeen wordt een PTR-operatorexpressie gedwongen te worden behandeld als een aanwijzer van het opgegeven type:

.DATA num DWORD 0 .CODE mov ax, WORD PTR ; Laad een woordgroottewaarde uit een DWORD Ik denk dat die er ook zijn specifieke vereisten

voor assembler (nasm/tasm/andere asm), en het gebruik van "byte ptr" is draagbaarder.

Controleer ook sectie 4.2.16 in het boek uit India en secties 8.12.3 (en 8.11.3 "Soorten conflicten") in "Programming Language Programming Program".

UPDATE: Dankzij Frank Kotler lijkt het erop dat NASM "een variant van de Intel Assembly-syntaxis gebruikt" (wiki) die de PTR-bewerking niet bevat.

UPDATE1: Er is een originele "ASM86 TAALREFERENTIEHANDLEIDING" van Intel, 1981-1983, PTR-operator gedefinieerd op pagina 4-15

PTR-operator

Syntaxis: Voer de PTR-naam in Beschrijving: De PTR-operator wordt gebruikt om een ​​geheugenreferentie te definiëren bepaald soort . De assembler definieert juiste instructies voor montage op basis van het type operanden in de instructie. Er zijn bepaalde gevallen

MOV WORD PTR , 5 ;ingesteld woord waarnaar wordt verwezen door BX = 5 INC DS:BYTE PTR 10 ;byte verhogen bij offset 10 ;vanaf DS

Dit formulier kan ook worden gebruikt om het typeattribuut van een variabele of label te overschrijven. Als u bijvoorbeeld toegang wilt krijgen tot een reeds gedefinieerde woordvariabele als twee bytes, kunt u het volgende coderen:

MOV CL, BYTE PTR AWORD ;haal eerste byte MOV CL, BYTE PTR AWORD + 1 ;haal tweede byte

Veldwaarden:

type Dit veld kan een van de volgende waarden hebben: BYTE, WORD, DWORD, QWORD, TBYTE, NEAR, FAR

naam Dit veld kan de volgende zijn: 1. Variabelenaam. 2. Labelnaam. 3. Adres of register. 4. Een geheel getal dat de offset vertegenwoordigt.

UPDATE2: Met dank aan Uni van Bitrader uit Stuttgart! Er is een originele MACRO-86 handleiding van Microsoft (1981). Pagina 3-7:

De PTR-operator kan op een andere manier worden gebruikt om een ​​byte op te slaan bij gebruik van voorwaartse referenties. Als u FOO als een persistente constante hebt gedefinieerd, kunt u de volgende instructie invoeren:

MOV, FOO

U kunt FOO onmiddellijk een byte noemen. In dit geval kunt u een van de operatoren invoeren (ze zijn gelijkwaardig):

MOV BYTE PTR, FOO MOV, BYTE PTR FOO

Deze uitspraken geven aan MACRO-86 aan dat FOO onmiddellijk een byte is. Er wordt een kleiner team gevormd.

En pagina 3-16:

Operators overschrijven

Deze operators worden gebruikt om het segment, de offset, het type of de afstand tussen variabelen en labels opnieuw te definiëren.

Index (PTR)

PTR

De PTR-operator overschrijft het type (BYTE, WORD, DWORD) of de afstand (NEAR, FAR) van de operand.

- nieuw attribuut; nieuw type of nieuwe afstand.

is de operand waarvan het attribuut moet worden overschreven.

Het belangrijkste en meest voorkomende gebruik van PTR is ervoor te zorgen dat MACRO-86 begrijpt welk kenmerk een expressie zou moeten hebben. Dit geldt vooral voor het type attribuut. Telkens wanneer u links in uw programma plaatst, wist PTR het afstands- of expressietype. Zo voorkom je fasefouten.

Het tweede gebruik van PTR is het verkrijgen van toegang tot gegevens van een ander type dan het type in de variabelendefinitie. Meestal gebeurt dit in structuren. Als de structuur als WORD is gedefinieerd, maar u het element als byte wilt benaderen, is PTR hiervoor de operator. Een veel eenvoudigere manier is echter om een ​​tweede operator te introduceren die ook de bytestructuur definieert. Dit elimineert de noodzaak om PTR te gebruiken voor elke structuurreferentie. Zie LABEL-richtlijn in paragraaf 4.2.1 Geheugenrichtlijnen.

BEL WORD PTR MOV BYTE PTR ARRAY, (iets) VOEG BYTE PTR FOO,9 toe

Nadat ik dit heb gelezen en enkele syntaxisdefinities uit deze documenten heb doorzocht, geloof ik dat PTR-notatie een must is. Het gebruik van mov BYTE , 0 is onjuist volgens de MACRO-86-handleiding.

Peter, 2003. - 629 d.
Downloaden(directe link) : assembler2003.djvu Vorige 1 .. 40 > .. >> Volgende

O De operator ptr type redefinition wordt gebruikt om het type label of variabele dat door een expressie wordt gedefinieerd, opnieuw te definiëren of te verduidelijken (Figuur 5.10). Het type kan een van de volgende waarden aannemen: byte, word, dword, qword, tbyte, near, far. Wat deze waarden betekenen, leer je later in deze les. Bijvoorbeeld:

d wrd dd 0 * * *

pyu al.byte ptr d_wrd+l ;stuur tweede byte van dubbel

;woorden Laten we dit fragment van het programma uitleggen. De djwrd-variabele is van het type doubleword. Wat moet u doen als u niet toegang wilt hebben tot de volledige waarde van een variabele, maar tot slechts één van de bytes die erin zijn opgenomen (bijvoorbeeld de tweede)? Als je dit probeert te doen met het commando mov al. d_wrd+l, dan zal de vertaler een bericht weergeven over een verkeerde combinatie van operandtypen. Met de ptr-operator kunt u het type rechtstreeks in een opdracht overschrijven en de opdracht uitvoeren.

HTType!-(ptr)-Expressie -

Rijst. 5.10. Typ de syntaxis van de operator negeren

O Operator voor segmentoverschrijving: (dubbele punt) dwingt het berekenen van het fysieke adres relatief aan een specifiek gespecificeerde segmentcomponent: “segmentregisternaam”, “segmentnaam” uit de overeenkomstige SEGMENT-instructie of “groepsnaam” (Fig. 5.11).

Dit punt is erg belangrijk, dus laten we het in meer detail uitleggen. Bij het bespreken van segmentatie hadden we het over het feit dat de microprocessor dat wel is hardwareniveau ondersteunt drie soorten segmenten: code, stapel en data. Wat is deze hardwareondersteuning? Om bijvoorbeeld te selecteren of de volgende opdracht moet worden uitgevoerd, moet de microprocessor alleen naar de inhoud van het segmentregister es kijken. En dit register bevat, zoals we weten, het (nog niet verschoven) fysieke adres van het begin van het commandosegment. Om het adres van een specifieke instructie te verkrijgen, moet de microprocessor de inhoud van es met 16 vermenigvuldigen (wat een verschuiving met vier bits betekent) en de resulterende 20-bits waarde optellen bij de 16-bits inhoud van het ip-register. Ongeveer hetzelfde gebeurt wanneer een microprocessor operanden in een machine-instructie verwerkt. Als het ziet dat de operand een adres is (het effectieve adres, dat slechts een deel van het fysieke adres is), dan weet het in welk segment het moet zoeken - standaard is dit het segment waarvan het startadres in het ds-segment is geschreven register.

Hoe zit het met het stapelsegment? Zie les 2 waar we het doel van registers voor algemene doeleinden hebben beschreven. In de context van onze overweging zijn we geïnteresseerd in de sp- en bp-registers. Als de microprocessor een van deze registers als een operand ziet (of een deel ervan, als de operand een uitdrukking is), genereert hij standaard het fysieke adres van de operand, waarbij hij de inhoud van het ss-register als segmentcomponent gebruikt. Wat betekent de term ‘standaard’? Denk aan de ‘reflexen’ waar we het in les 1 over hadden. Dit is een reeks microprogramma’s in de microprogrammabesturingseenheid, die elk een van de commando’s in het machine-instructiesysteem van de microprocessor uitvoeren. Elke firmware werkt volgens zijn eigen algoritme. Je kunt het natuurlijk niet veranderen, maar je kunt het wel een beetje aanpassen. Dit wordt gedaan met behulp van het optionele machineopdrachtvoorvoegselveld (zie opdrachtformat in les* 2). Als we het eens zijn over hoe de opdracht werkt, ontbreekt dit veld. Als we een wijziging willen aanbrengen (als dit uiteraard is toegestaan ​​voor een specifiek commando) in het werkingsalgoritme van het commando, dan is het noodzakelijk om een ​​passend voorvoegsel te vormen. Een voorvoegsel is een waarde van één byte, waarvan de numerieke waarde het doel ervan bepaalt. De microprocessor herkent gespecificeerde waarde dat deze byte een voorvoegsel is, en verder werk Het microprogramma wordt uitgevoerd rekening houdend met de ontvangen instructies om de werking ervan te corrigeren. Terwijl we de stof in het boek bespreken, zullen we bekend raken met de meeste mogelijke voorvoegsels. Nu zijn we geïnteresseerd in een van hen: het voorvoegsel voor segmentvervanging (herdefinitie). Het doel ervan is om aan de microprocessor (en in wezen aan de firmware) aan te geven dat we het standaardsegment niet willen gebruiken. De ruimte voor een dergelijke herdefinitie is uiteraard beperkt. Het commandosegment kan niet opnieuw worden gedefinieerd, het adres is het volgende uitvoerbare opdracht wordt uniek bepaald door het paar cs: i р. Maar de stack- en datasegmenten zijn mogelijk. Dit is waar de “:” operator voor bedoeld is. De assembler-vertaler, die deze operator verwerkt, genereert het overeenkomstige segmentvervangingsvoorvoegsel van één byte. Bijvoorbeeld:

jmp metl ;bypass is vereist, anders wordt het ind-veld behandeld als een ander commando ind db 5 ;beschrijving van het gegevensveld in het commandosegment

mov al,cs:ind !Door het segment opnieuw te definiëren, kan het werken

;met gegevens gedefinieerd binnen een codesegment

Laten we doorgaan met het vermelden van de operators.

O Naamgevingsoperator voor structuurtype. (dot) zorgt er ook voor dat de compiler bepaalde berekeningen uitvoert als dit in een expressie voorkomt. We zullen deze operator in detail bespreken wanneer we het concept van complexe gegevenstypen introduceren in les 12.

O De operator voor het ontvangen van de segmentcomponent van het adres van een expressie, seg, retourneert het fysieke adres van het segment voor de expressie (Figuur 5.12), wat een label, een variabele, een segmentnaam, een groepsnaam of een symbolische naam.

Rekenkundige bewerkingen- TOEVOEGEN, SUB, MUL, DIV. Veel opcodes voeren berekeningen uit. Je kunt veel ervan herkennen aan hun naam: optellen (optellen), sub (aftrekken), mul (vermenigvuldigen), div (delen).

De add-opcode heeft de volgende syntaxis:

Bestemming, bron toevoegen

Voert de berekening uit: sink = sink + source.

Er zijn ook andere vormen:

ontvanger bron voorbeeld
register register voeg ecx, edx toe
register geheugen ecx toevoegen, dword ptr / ecx toevoegen,
register betekenis voeg ea toe, 102
geheugen betekenis voeg dword ptr, 80 toe
geheugen register voeg dword ptr, edx toe

Deze opdracht is heel eenvoudig. Het voegt de bronwaarde toe aan de bestemmingswaarde en plaatst het resultaat in de bestemming. Andere wiskundige commando's:

Subontvanger, bron (ontvanger = ontvanger - bron)
mul vermenigvuldiger, vermenigvuldiger (vermenigvuldiger = vermenigvuldiger * vermenigvuldiger)
div deler (eax = eax/deler, edx = rest)

Omdat registers alleen gehele waarden kunnen bevatten (dat wil zeggen getallen, en geen drijvende komma), wordt het resultaat van de deling opgesplitst in een quotiënt en een rest. Afhankelijk van de grootte van de bron wordt het quotiënt nu opgeslagen in eax en de rest in edx:
* = Bijvoorbeeld: als dx = 2030h en ax = 0040h, dx: ax = 20300040h. Dx:ax is de waarde van dword, waarbij dx het hoge woord vertegenwoordigt en ax het lage woord. Edx:eax - quadword-waarde (64 bits), waarbij het hoogste dword in edx staat en het laagste dword in eax.

De bron van de splitsingsoperatie kan zijn:

  1. 8-bits register (al, ah, cl,...)
  2. 16-bits register (ax, dx, ...)
  3. 32-bits register (eax, edx, ecx...)
  4. 8-bits waarde uit geheugen (byte ptr)
  5. 16-bits waarde uit geheugen (woord ptr)
  6. 6a 32-bits geheugenwaarde (dword ptr)

De bron kan geen onmiddellijke waarde zijn, omdat de processor dan niet in staat zou zijn de grootte van de bronoperand te bepalen.

Logische bewerkingen met bits - OR, XOR, AND, NOT. Deze opdrachten werken met de bestemming en bron, met uitzondering van de opdracht "NOT". Elke bit in de bestemming wordt vergeleken met dezelfde bit in de bron, en afhankelijk van het commando wordt er een 0 of 1 in de bestemmingsbit geplaatst:

EN(logische AND) stelt de resultaatbit in op 1 als zowel de bronbit als de bestemmingsbit zijn ingesteld op 1.
OF(logische OR) stelt de resultaatbit in op 1 als een van de bits, de bronbit of de bestemmingsbit, is ingesteld op 1.
XOR(NOT OR) stelt de resultaatbit in op 1 als de bronbit verschilt van de bestemmingsbit.
NIET keert de bronbit om.

Voorbeeld:

Verplaatsingsbijl, 3406d
mov dx, 13EAh
xor bijl, dx

Ax = 3406 (decimaal), in binair getal - 0000110101001110.

Dx = 13EA (hexadecimaal), in binair getal - 0001001111101010.

Uitvoering XOR-operaties op deze stukjes:

Bron = 0001001111101010 (dx)

Ontvanger = 0000110101001110 (bijl)

Resultaat = 0001111010100101 (nieuwe waarde in ax)

De nieuwe waarde in ax na het uitvoeren van de opdracht is 0001111010100101 (7845 in decimaal, 1EA5 in hexadecimaal).

Nog een voorbeeld:

Verplaats ecx, FFFF0000h
niet exx

FFFF0000 in binair getal is -
Als je elk bit omkeert, krijg je:
, in hexadecimaal is dit 0000FFFF
Dit betekent dat ecx na de NOT-bewerking 0000FFFFh zal bevatten.

Verhogen/verlagen - INC/DEC. Er zijn 2 zeer eenvoudige commando's, DEC en INC. Deze instructies verhogen of verlagen de inhoud van het geheugen of een register met één. Zet gewoon:

Inc. registreren; registreren = registreren + 1
december register; registreren = registreren - 1
incl. dword ptr; de waarde in wordt met 1 verhoogd.
december dword ptr; de waarde in wordt met 1 verlaagd.

Een ander vergelijkingscommando is test. De Testinstructie voert een EN-bewerking uit op twee operanden en stelt de corresponderende vlaggen in of wist deze, afhankelijk van het resultaat. Het resultaat wordt niet opgeslagen. Test wordt gebruikt om bits te testen, bijvoorbeeld in een register:

Test eex, 100b
jnz-compensatie

Het jnz-commando springt als de derde bit van rechts in het eax-register is ingesteld. Heel vaak wordt het testcommando gebruikt om te controleren of een register nul is:

Test ecx, ecx
jz-compensatie

Het jz-commando springt als ecx = 0.

Een commando dat niets doet is nop. Deze opdracht doet absoluut niets (lege opdracht). Het kost alleen maar ruimte en tijd. Wordt gebruikt om ruimte in een codesegment te reserveren of om een ​​programmavertraging te organiseren.

Uitwisseling van waarden - XCHG. Het XCHG-commando is ook vrij eenvoudig. Doel: uitwisseling van twee waarden tussen registers of tussen registers en geheugen:

Verplaats eax, 237 uur
mov ecx, 978 uur
xchg eax, ecx
als resultaat:
eax = 978 uur
ecx = 237 uur

Dat is het einde van de les. Ik hoop dat deze niet saai was. De volgende les gaat over subroutines.