Capitolul 6: Bazele arhitecturii calculatoarelor moderne cu limbaj de asamblare

Capitolul 6 Bazele Arhitecturii Calculatoarelor Moderne Cu Limbaj De Asamblare



6.1 Introducere

Calculatoarele moderne de uz general sunt de două tipuri: CISC și RISC. CISC înseamnă Complex Instruction Set Computer. RISK înseamnă computer cu set de instrucțiuni redus. Microprocesoarele 6502 sau 6510, după cum se aplică computerului Commodore-64, seamănă mai mult cu arhitectura RISC decât cu arhitectura CISC.

Calculatoarele RISC au, în general, instrucțiuni în limbaj de asamblare mai scurte (în funcție de numărul de octeți) în comparație cu calculatoarele CISC.







Notă : Indiferent dacă are de-a face cu CISC, RISC sau computer vechi, un periferic începe de la un port intern și merge spre exterior printr-un port extern de pe suprafața verticală a unității de sistem a computerului (unitatea de bază) și către dispozitivul extern.



O instrucțiune tipică a unui computer CISC poate fi văzută ca unirea mai multor instrucțiuni scurte de limbaj de asamblare într-o instrucțiune de limbaj de asamblare mai lungă, ceea ce face ca instrucțiunea rezultată să fie complexă. În special, un computer CISC încarcă operanzii din memorie în registrele microprocesorului, efectuează o operație și apoi stochează rezultatul înapoi în memorie, totul într-o singură instrucțiune. Pe de altă parte, acestea sunt cel puțin trei instrucțiuni (scurtare) pentru computerul RISC.



Există două serii populare de calculatoare CISC: computerele cu microprocesor Intel și computerele cu microprocesor AMD. AMD înseamnă Advanced Micro Devices; este o companie producătoare de semiconductori. Seria de microprocesoare Intel, în ordinea dezvoltării, sunt 8086, 8088, 80186, 80286, 80386, 80486, Pentium, Core, i Series, Celeron și Xeon. Instrucțiunile limbajului de asamblare pentru primele microprocesoare Intel, cum ar fi 8086 și 8088, nu sunt foarte complexe. Cu toate acestea, ele sunt complexe pentru noile microprocesoare. Microprocesoarele recente AMD pentru seria CISC sunt Ryzen, Opteron, Athlon, Turion, Phenom și Sempron. Microprocesoarele Intel și AMD sunt cunoscute ca microprocesoare x86.





ARM înseamnă Advanced RISC Machine. Arhitecturile ARM definesc o familie de procesoare RISC care sunt potrivite pentru utilizare într-o mare varietate de aplicații. În timp ce multe microprocesoare Intel și AMD sunt utilizate în computerele personale desktop, multe procesoare ARM servesc ca procesoare încorporate în sisteme critice pentru siguranță, cum ar fi frâne antiblocare auto și ca procesoare de uz general în ceasuri inteligente, telefoane portabile, tablete și laptopuri. . Deși ambele tipuri de microprocesoare pot fi văzute în dispozitivele mici și mari, microprocesoarele RISC se găsesc mai mult în dispozitivele mici decât în ​​dispozitivele mari.

Computer Word
Dacă se spune că un computer este un computer de 32 de biți, înseamnă că informațiile sunt stocate, transferate și manipulate sub formă de coduri binare de treizeci și doi de biți în partea interioară a plăcii de bază. De asemenea, înseamnă că registrele de uz general din microprocesorul computerului au o lățime de 32 de biți. Registrele A, X și Y ale microprocesorului 6502 sunt registre de uz general. Au o lățime de opt biți, așa că computerul Commodore-64 este un computer cu cuvinte pe opt biți.



Niște vocabular
Calculatoare X86

Semnificațiile octet, cuvânt, cuvânt dublu, cuvânt patru și cuvânt dublu sunt următoarele pentru computerele x86:

  • octet : 8 biți
  • Cuvânt : 16 biți
  • Cuvânt dublu : 32 de biți
  • Quadword : 64 de biți
  • Cuvânt dublu patru : 128 de biți

Calculatoare ARM
Semnificațiile octet, jumătate de cuvânt, cuvânt și cuvânt dublu sunt după cum urmează pentru computerele ARM:

  • octet : 8 biți
  • Deveniți jumătate : 16 biți
  • Cuvânt : 32 de biți
  • Cuvânt dublu : 64 de biți

Trebuie remarcate diferențele și asemănările pentru numele (și valorile) x86 și ARM.

Notă : numerele întregi de semn din ambele tipuri de computere sunt complementul a doi.

Locație de memorie
Cu computerul Commodore-64, o locație de memorie este de obicei de un octet, dar poate fi ocazional de doi octeți consecutivi când se iau în considerare pointerii (adresare indirectă). Cu un computer x86 modern, o locație de memorie este de 16 octeți consecutivi atunci când aveți de-a face cu un cuvânt dublu de 16 octeți (128 de biți), 8 octeți consecutivi când aveți de-a face cu un cuvânt patru dublu de 8 octeți (64 de biți), 4 octeți consecutivi când aveți de-a face cu un cuvânt dublu de 8 octeți (64 biți). 4 octeți (32 de biți), 2 octeți consecutivi când aveți de-a face cu un cuvânt de 2 octeți (16 biți) și 1 octet când aveți de-a face cu un octet (8 biți). Cu un computer ARM modern, o locație de memorie este de 8 octeți consecutivi atunci când aveți de-a face cu un cuvânt dublu de 8 octeți (64 de biți), 4 octeți consecutivi când aveți de-a face cu un cuvânt de 4 octeți (32 biți), 2 octeți consecutivi când aveți de-a face cu un cuvânt cu jumătate de cuvânt. de 2 octeți (16 biți) și 1 octet când se lucrează cu un octet (8 biți).

Acest capitol explică ce este comun în arhitecturile CISC și RISC și care sunt diferențele dintre acestea. Acest lucru se face în comparație cu 6502 µP și computerul commodore-64 unde este aplicabil.

6.2 Diagrama bloc a plăcii de bază a PC-ului modern

PC înseamnă Personal Computer. Următoarea este o diagramă bloc de bază generică pentru o placă de bază modernă cu un singur microprocesor pentru un computer personal. Reprezintă o placă de bază CISC sau RISC.


Fig. 6.21 Diagrama bloc de bază a plăcii de bază a PC-ului modern

Trei porturi interne sunt prezentate în diagramă, dar există mai multe în practică. Fiecare port are un registru care poate fi văzut ca portul însuși. Fiecare circuit de port are cel puțin un alt registru care poate fi numit „registru de stare”. Registrul de stare indică portul programului care trimite semnalul de întrerupere către microprocesor. Există un circuit controler de întrerupere (nu este prezentat) care diferențiază între diferitele linii de întrerupere de la diferitele porturi și are doar câteva linii la µP.

HD.C în diagramă înseamnă Hard Drive Card. NIC înseamnă Network Interface Card. Cardul de hard disk (circuitul) este conectat la hard disk-ul care se află în interiorul unității de bază (unitatea de sistem) a computerului modern. Placa de interfață de rețea (circuitul) este conectată printr-un cablu extern la un alt computer. În diagramă, există un port și un DMA (consultați ilustrația următoare) care sunt conectate la placa de hard disk și/sau la placa de interfață de rețea. DMA înseamnă Acces direct la memorie.

Amintiți-vă de la capitolul computer Commodore-64 că, pentru a trimite octeții din memorie către unitatea de disc sau alt computer, fiecare octet trebuie copiat într-un registru din microprocesor înainte de a fi copiat în portul intern corespunzător și apoi automat. la dispozitiv. Pentru a primi octeții de pe unitatea de disc sau alt computer în memorie, fiecare octet trebuie să fie copiat din registrul portului intern corespunzător într-un registru al microprocesorului înainte de a fi copiat în memorie. În mod normal, aceasta durează mult dacă numărul de octeți din flux este mare. Soluția pentru transfer rapid este utilizarea accesului direct la memorie (circuit) fără a trece prin microprocesor.

Circuitul DMA este între port și HD. C sau NIC. Cu accesul direct la memorie al circuitului DMA, transferul de fluxuri mari de octeți are loc direct între circuitul DMA și memorie (RAM), fără participarea continuă a microprocesorului. DMA folosește magistrala de adrese și magistrala de date în locul µP. Durata totală a transferului este mai scurtă decât în ​​cazul în care ar trebui utilizat µP hard. Atât HD C. cât și NIC folosesc DMA atunci când au un flux mare de date (octeți) pentru transfer cu RAM (memoria).

GPU înseamnă Graphics Processing Unit. Acest bloc de pe placa de bază este responsabil pentru trimiterea textului și a imaginilor în mișcare sau statice pe ecran.

Cu computerele moderne (PC-uri), nu există memorie doar pentru citire (ROM). Există, totuși, BIOS-ul sau UEFI care este un fel de RAM nevolatilă. Informațiile din BIOS sunt de fapt menținute de o baterie. Bateria este cea care menține și cronometrul, la ora și data potrivite pentru computer. UEFI a fost inventat după BIOS și a înlocuit BIOS-ul, deși BIOS-ul este încă destul de relevant în computerele moderne. Vom discuta mai multe despre acestea mai târziu!

În PC-urile moderne, magistralele de adrese și de date dintre µP și circuitele portului intern (și memorie) nu sunt magistrale paralele. Sunt magistrale seriale care au nevoie de doi conductori pentru transmiterea într-un sens și alți doi conductori pentru transmiterea în sens opus. Aceasta înseamnă, de exemplu, că 32 de biți pot fi trimiși în serie (un bit după altul) în ambele direcții.

Dacă transmisia în serie este doar într-o singură direcție cu doi conductori (două linii), se spune că este semi-duplex. Dacă transmisia serială este în ambele sensuri cu patru conductori, o pereche în ambele direcții, se spune că este full-duplex.

Întreaga memorie a computerului modern constă încă dintr-o serie de locații de octeți: opt biți pe octet. Un computer modern are un spațiu de memorie de cel puțin 4 giga octeți = 4 x 210 x 2 10 x 2 10 = 4 x 1.073.741.824 10 octeți = 4 x 1024 10/sub> x 1024 10 x 1024 10 = 4 x 1.073.741.824 10 .

Notă : Deși nu este afișat niciun circuit de temporizator pe placa de bază anterioară, toate plăcile de bază moderne au circuite de temporizator.

6.3 Bazele arhitecturii computerului x64

6.31 Setul de registre x64
Microprocesorul pe 64 de biți al seriei de microprocesoare x86 este un microprocesor pe 64 de biți. Este destul de modern să înlocuiască procesorul pe 32 de biți din aceeași serie. Registrele de uz general ale microprocesorului pe 64 de biți și numele lor sunt după cum urmează:


Fig. 6.31 Registre de uz general pentru x64

În ilustrația dată sunt prezentate șaisprezece (16) registre de uz general. Fiecare dintre aceste registre are o lățime de 64 de biți. Privind registrul din colțul din stânga sus, cei 64 de biți sunt identificați ca RAX. Primii 32 de biți ai acestui registru (din dreapta) sunt identificați ca EAX. Primii 16 biți ai acestui registru (din dreapta) sunt identificați ca AX. Al doilea octet (din dreapta) al aceluiași registru este identificat ca AH (H înseamnă aici ridicat). Și primul octet (din același registru) este identificat ca AL (L aici înseamnă scăzut). Privind registrul din colțul din dreapta jos, cei 64 de biți sunt identificați ca R15. Primii 32 de biți ai aceluiași registru sunt identificați ca R15D. Primii 16 biți ai aceluiași registru sunt identificați ca R15W. Și primul octet este identificat ca R15B. Numele celorlalte registre (și subregistre) sunt explicate în mod similar.

Există unele diferențe între Intel și AMD µPs. Informațiile din această secțiune sunt pentru Intel.

Cu 6502 µP, registrul Program Counter (nu este accesibil direct) care deține următoarea instrucțiune care urmează să fie executată are o lățime de 16 biți. Aici (x64), contorul programului se numește Indicator de instrucțiuni și are o lățime de 64 de biți. Este etichetat ca RIP. Aceasta înseamnă că x64 µP poate adresa până la 264 = 1,844674407 x 1019 (de fapt 18,446,744,073,709,551,616) locații de octeți de memorie. RIP nu este un registru de uz general.

Stack Pointer Register sau RSP se numără printre cele 16 registre de uz general. Indică ultima intrare în stivă din memorie. Ca și în cazul 6502 µP, stiva pentru x64 crește în jos. Cu x64, stiva din RAM este folosită pentru a stoca adresele de retur pentru subrutine. Este, de asemenea, folosit pentru stocarea „spațiului umbră” (consultați următoarea discuție).

6502 µP are un registru de stare a procesorului pe 8 biți. Echivalentul în x64 se numește registru RFLAGS. Acest registru stochează steagurile care sunt utilizate pentru rezultatele operațiunilor și pentru controlul procesorului (µP). Are o lățime de 64 de biți. Cei 32 de biți mai mari sunt rezervați și nu sunt utilizați în prezent. Următorul tabel oferă numele, indexul și semnificațiile biților utilizați în mod obișnuit în registrul RFLAGS:

Tabelul 6.31.1
Cele mai utilizate steaguri RFLAGS (biți)
Simbol Pic Nume Scop
CF 0 Transporta Este setat dacă o operație aritmetică generează un transfer sau un împrumut din bitul cel mai semnificativ al rezultatului; sters altfel. Acest flag indică o condiție de depășire pentru aritmetica cu numere întregi fără semn. Este folosit și în aritmetica cu precizie multiplă.
PF 2 Paritate Este setat dacă octetul cel mai puțin semnificativ al rezultatului conține un număr par de 1 biți; sters altfel.
DE 4 Regla Este setat dacă o operație aritmetică generează o transportare sau un împrumut din bitul 3 al rezultatului; sters altfel. Acest steag este folosit în aritmetica zecimală codificată binar (BCD).
ZF 6 Zero Este setat dacă rezultatul este zero; sters altfel.
SF 7 Semn Este setat dacă este egal cu bitul cel mai semnificativ al rezultatului, care este bitul semn al unui număr întreg cu semn (0 indică o valoare pozitivă și 1 indică o valoare negativă).
DE unsprezece Revărsare Este setat dacă rezultatul întregului este un număr pozitiv prea mare sau un număr negativ prea mic (excluzând bitul semn) pentru a se potrivi în operandul de destinație; sters altfel. Acest flag indică o condiție de depășire pentru aritmetica cu numere întregi cu semn (complementul a doi).
DF 10 Direcţie Este setat dacă instrucțiunile șirului de direcție funcționează (creștere sau descreștere).
ID douăzeci și unu Identificare Este setat dacă schimbarea denotă prezența instrucțiunii CPUID.

În plus față de cele optsprezece registre de 64 de biți care sunt indicate anterior, arhitectura x64 µP are opt registre lățime de 80 de biți pentru aritmetica în virgulă mobilă. Aceste opt registre pot fi folosite și ca registre MMX (consultați următoarea discuție). Există, de asemenea, șaisprezece registre pe 128 de biți pentru XMM (consultați următoarea discuție).

Asta nu este totul despre registre. Există mai multe registre x64 care sunt registre de segmente (în cea mai mare parte neutilizate în x64), registre de control, registre de gestionare a memoriei, registre de depanare, registre de virtualizare, registre de performanță care urmăresc tot felul de parametri interni (accesări/eșecuri în cache, micro-operații executate, sincronizare). , și mult mai mult).

SIMD

SIMD înseamnă Single Instruction Multiple Data. Aceasta înseamnă că o instrucțiune de limbaj de asamblare poate acționa asupra mai multor date în același timp într-un singur microprocesor. Luați în considerare următorul tabel:

1 2 3 4 5 6 7 8
+ 9 10 unsprezece 12 13 14 cincisprezece 16
= 10 12 14 16 18 douăzeci 22 24

În acest tabel, sunt adăugate opt perechi de numere în paralel (în aceeași durată) pentru a da opt răspunsuri. O instrucțiune de limbaj de asamblare poate face cele opt adăugiri întregi paralele în registrele MMX. Un lucru similar se poate face cu registrele XMM. Deci, există instrucțiuni MMX pentru numere întregi și instrucțiuni XMM pentru floats.

6.32 Harta memoriei și x64

Cu indicatorul de instrucțiuni (contor de program) având 64 de biți, aceasta înseamnă că 264 = 1,844674407 x 1019 locații de octeți de memorie pot fi adresate. În hexazecimal, cea mai mare locație de octeți este FFFF,FFFF,FFFF,FFFF16. Niciun computer obișnuit de astăzi nu poate oferi un spațiu de memorie (complet) atât de mare. Deci, o hartă de memorie adecvată pentru computerul x64 este următoarea:

Observați că decalajul de la 0000,8000,0000,000016 la FFFF,7FFF,FFFF,FFFF16 nu are locații de memorie (fără bănci de memorie RAM). Aceasta este o diferență de FFFF,0000,0000,000116, care este destul de mare. Jumătatea înaltă canonică are sistemul de operare, în timp ce jumătatea inferioară canonică are programele utilizatorului (aplicațiile) și datele. Sistemul de operare este format din două părți: un UEFI mic (BIOS) și o parte mare care sunt încărcate de pe hard disk. Următorul capitol vorbește mai mult despre sistemele de operare moderne. Rețineți asemănarea cu această hartă de memorie și cea pentru Commodore-64, când 64KB ar fi putut arăta ca o mulțime de memorie.

În acest context, sistemul de operare este numit aproximativ „kernel”. Nucleul este similar cu Kernal-ul computerului Commodore-64, dar are mult mai multe subrutine.

Endianness pentru x64 este little endian, ceea ce înseamnă că pentru o locație, adresa inferioară indică octetul de conținut inferior din memorie.

6.33 Moduri de adresare în limbaj de asamblare pentru x64

Modurile de adresare sunt modalitățile prin care o instrucțiune poate accesa registrele µP și memoria (inclusiv registrele portului intern). X64 are multe moduri de adresare, dar aici sunt abordate doar modurile de adresare utilizate în mod obișnuit. Sintaxa generală pentru o instrucțiune de aici este:

opcode destinație, sursă

Numerele zecimale sunt scrise fără prefix sau sufix. La 6502, sursa este implicită. X64 are mai multe op-codes decât 6502, dar unele dintre opcodes au aceleași mnemonice. Instrucțiunile individuale x64 sunt de lungime variabilă și pot varia în dimensiune de la 1 la 15 octeți. Modurile de adresare utilizate în mod obișnuit sunt următoarele:

Modul de adresare imediată
Aici, operandul sursă este o valoare reală și nu o adresă sau o etichetă. Exemplu (citește comentariul):

ADAUGĂ EAX, 14; adăugați zecimalul 14 la 32 de biți EAX de 64 de biți RAX, răspunsul rămâne în EAX (destinație)

Înregistrați-vă pentru a înregistra modul de adresare
Exemplu:

ADD R8B, AL; adăugați AL de 8 biți de RAX la R8B de R8 pe 64 de biți – răspunsurile rămân în R8B (destinație)

Modul de adresare indirectă și indexată
Adresarea indirectă cu 6502 µP înseamnă că locația adresei date în instrucțiune are adresa efectivă (pointerul) a locației finale. Un lucru similar se întâmplă cu x64. Adresarea indexului cu 6502 µP înseamnă că conținutul unui registru µP este adăugat la adresa dată în instrucțiunea pentru a avea adresa efectivă. Un lucru similar se întâmplă cu x64. De asemenea, cu x64, conținutul registrului poate fi și înmulțit cu 1 sau 2 sau 4 sau 8 înainte de a fi adăugat la adresa dată. Instrucțiunea mov (copie) a x64 poate combina atât adresarea indirectă, cât și adresarea indexată. Exemplu:

MOV R8W, 1234[8*RAX+RCX] ; mutați cuvântul la adresa (8 x RAX + RCX) + 1234

Aici, R8W are primii 16 biți ai lui R8. Adresa dată este 1234. Registrul RAX are un număr de 64 de biți care este înmulțit cu 8. Rezultatul este adăugat la conținutul registrului RCX de 64 de biți. Acest al doilea rezultat este adăugat la adresa dată care este 1234 pentru a obține adresa efectivă. Numărul din locația adresei efective este mutat (copiat) în primul loc de 16 biți (R8W) al registrului R8, înlocuind orice era acolo. Observați utilizarea parantezelor pătrate. Amintiți-vă că un cuvânt în x64 are o lățime de 16 biți.

Adresare relativă RIP
Pentru 6502 µP, adresarea relativă este utilizată numai cu instrucțiuni de ramificație. Acolo, singurul operand al codului operațional este un offset care este adăugat sau scăzut la conținutul Contorului programului pentru adresa de instrucțiune efectivă (nu adresa de date). Un lucru similar se întâmplă cu x64, unde Contorul de program este numit Indicator de instrucțiuni. Instrucțiunea cu x64 nu trebuie să fie doar o instrucțiune de ramură. Un exemplu de adresare relativă RIP este:

MOV AL, [RIP]

AL de RAX are un număr semnat de 8 biți care este adăugat sau scazut din conținutul în RIP (pointer de instrucțiune pe 64 de biți) pentru a indica următoarea instrucțiune. Rețineți că sursa și destinația sunt schimbate în mod excepțional în această instrucțiune. Rețineți, de asemenea, utilizarea parantezelor drepte care se referă la conținutul RIP.

6.34 Instrucțiuni frecvent utilizate pentru x64

În următorul tabel, * înseamnă diferite sufixe posibile ale unui subset de coduri operaționale:

Tabelul 6.34.1
Instrucțiuni frecvent utilizate în x64
Opcode Sens
MOV Mutați (copiați) în/din/între memorie și registre
CMOV* Diverse mișcări condiționate
XCHG schimb valutar
BSWAP Schimb de octeți
PUSH/POP Utilizarea stivei
ADD/ADC Adăugați/cu transport
SUB/SBC Scădere/cu purtare
MUL/IMUL Înmulțire/nesemnat
DIV/IDIV Împărțire/nesemnată
INC/DEC Crește/Decrementează
NEG Nega
CMP Comparaţie
ȘI/SAU/XOR/NU Operații pe biți
SHR/SAR Schimbați la dreapta logic/aritmetic
SHL/SAL Schimbați la stânga logic/aritmetic
ROR/ROL Rotiți la dreapta/la stânga
RCR/RCL Rotiți dreapta/stânga prin bitul de transport
BT/BTS/BTR Testare biți/și setați/și resetare
JMP Salt necondiționat
JE/JNE/JC/JNC/J* Sari daca egal/nu este egal/car/nu transport/multi altii
MĂS/MĂS/MĂS Buclă cu ECX
CALL/RET Subrutină de apel/retur
NOP Nicio operațiune
CPUID Informații CPU

X64 are instrucțiuni de înmulțire și împărțire. Are circuite hardware de multiplicare și divizare în µP. 6502 µP nu are circuite hardware de multiplicare și divizare. Este mai rapid să faci înmulțirea și împărțirea prin hardware decât prin software (inclusiv deplasarea biților).

Instrucțiuni pentru șiruri
Există o serie de instrucțiuni șir, dar singura care trebuie discutată aici este instrucțiunea MOVS (pentru mutare șir) pentru a copia un șir care începe la adresa C000 H . Pentru a începe de la adresa C100 H , utilizați următoarele instrucțiuni:

MOVS [C100H], [C000H]

Observați sufixul H pentru hexazecimal.

6.35 Buclă în x64

6502 µP are instrucțiuni de ramificare pentru buclă. O instrucțiune de ramură sare la o locație de adresă care are noua instrucțiune. Locația adresei poate fi numită „buclă”. x64 are instrucțiuni LOOP/LOOPE/LOOPNE pentru bucla. Aceste cuvinte rezervate în limbaj de asamblare nu trebuie confundate cu eticheta „buclă” (fără ghilimele). Comportamentul este următorul:

LOOP decrementează ECX și verifică dacă ECX nu este zero. Dacă acea condiție (zero) este îndeplinită, se trece la o etichetă specificată. În caz contrar, eșuează (continuați cu restul instrucțiunilor în discuția următoare).

LOOPE decrementează ECX și verifică dacă ECX nu este zero (de exemplu poate fi 1) și ZF este setat (la 1). Dacă aceste condiții sunt îndeplinite, sare la etichetă. În caz contrar, cade.

LOOPNE decrementează ECX și verifică dacă ECX nu este zero și ZF NU ESTE setat (adică să fie zero). Dacă aceste condiții sunt îndeplinite, se trece la etichetă. În caz contrar, cade.

Cu x64, registrul RCX sau părțile secundare ale acestuia, cum ar fi ECX sau CX, dețin numărul întreg. Cu instrucțiunile LOOP, contorul face în mod normal numărătoarea inversă, scăzând cu 1 pentru fiecare salt (buclă). În următorul segment de cod în buclă, numărul din registrul EAX crește de la 0 la 10 în zece iterații, în timp ce numărul din ECX numără (descrește) de 10 ori (citiți comentariile):

MOV EAX, 0;
MOV ECX, 10; numărătoare inversă de 10 ori implicit, o dată pentru fiecare iterație
eticheta:
INC EAX ; incrementa EAX ca corp buclă
etichetă LOOP ; decrementați EAX și, dacă EAX nu este zero, re-execuți corpul buclei de la „label:”

Codarea buclei începe de la „label:”. Observați utilizarea colonului. Codarea buclei se termină cu „eticheta LOOP” care spune decrement EAX. Dacă conținutul său nu este zero, reveniți la instrucțiunea de după „etichetă:” și reexecuți orice instrucțiune (toate instrucțiunile corpului) care vine în jos până la „eticheta LOOP”. Rețineți că „eticheta” poate avea încă un alt nume.

6.36 Intrare/Ieșire x64

Această secțiune a capitolului tratează trimiterea datelor către un port de ieșire (intern) sau primirea datelor de la un port de intrare (intern). Chipsetul are porturi pe opt biți. Orice două porturi consecutive de 8 biți pot fi tratate ca un port de 16 biți, iar orice patru porturi consecutive pot fi un port de 32 de biți. În acest mod, procesorul poate transfera 8, 16 sau 32 de biți către sau de la un dispozitiv extern.

Informațiile pot fi transferate între procesor și un port intern în două moduri: folosind ceea ce este cunoscut sub numele de intrare/ieșire mapată în memorie sau folosind un spațiu separat de adrese de intrare/ieșire. I/O mapat în memorie este ca ceea ce se întâmplă cu procesorul 6502, unde adresele porturilor fac de fapt parte din întreg spațiul de memorie. În acest caz, atunci când trimiteți datele la o anumită locație de adresă, acestea merg la un port și nu la o bancă de memorie. Porturile pot avea un spațiu de adrese I/O separat. În acest ultim caz, toate băncile de memorie au adresele lor de la zero. Există un interval de adrese separat de la 0000H la FFFF16. Acestea sunt folosite de porturile din chipset. Placa de bază este programată pentru a nu face confuzii între I/O mapat în memorie și spațiul de adrese I/O separat.

I/O mapat cu memorie
Prin aceasta, porturile sunt considerate locații de memorie, iar opcode-urile normale de utilizat între memorie și µP sunt folosite pentru transferul de date între µP și porturi. Deci, pentru a muta un octet dintr-un port de la adresa F000H în registrul µP RAX:EAX:AX:AL, procedați în felul următor:

MOV AL, [F000H]

Un șir poate fi mutat din memorie într-un port și invers. Exemplu:

MOVS [F000H], [C000H]; sursa este C000H, iar destinația este portul la F000H.

Spațiu de adrese I/O separat

Cu aceasta, trebuie utilizate instrucțiunile speciale pentru intrare și ieșire.

Transferarea articolelor individuale
Registrul procesorului pentru transfer este RAX. De fapt, este RAX:EAX pentru cuvânt dublu, RAX:EAX:AX ​​pentru cuvânt și RAX:EAX:AX:AL pentru octet. Deci, pentru a transfera un octet dintr-un port la FFF0h, la RAX:EAX:AX:AL, tastați următoarele:

ÎN AL, [FFF0H]

Pentru transferul invers, tastați următoarele:

OUT [FFF0H], AL

Deci, pentru articole individuale, instrucțiunile sunt IN și OUT. Adresa portului poate fi dată și în registrul RDX:EDX:DX.

Transferul șirurilor
Un șir poate fi transferat din memorie într-un port chipset și invers. Pentru a transfera un șir dintr-un port de la adresa FFF0H în memorie, începeți de la C100H, tastați:

INS [ESI], [DX]

care are același efect ca:

INS [EDI], [DX]

Programatorul ar trebui să pună adresa portului de doi octeți a lui FFF0H în registrul RDX:EDX:Dx și ar trebui să pună adresa de doi octeți a lui C100H în registrul RSI:ESI sau RDI:EDI. Pentru transferul invers, procedați după cum urmează:

INS [DX], [ESI]

care are același efect ca:

INS [DX], [EDI]

6.37 Stiva în x64

La fel ca procesorul 6502, procesorul x64 are și o stivă în RAM. Stiva pentru x64 poate fi 2 16 = 65.536 de octeți lungime sau poate fi 2 32 = 4.294.967.296 de octeți lungime. De asemenea, crește în jos. Când conținutul unui registru este împins în stivă, numărul din indicatorul stivei RSP este micșorat cu 8. Amintiți-vă că o adresă de memorie pentru x64 are o lățime de 64 de biți. Valoarea din indicatorul stivei din µP indică către următoarea locație din stiva din RAM. Când conținutul unui registru (sau o valoare dintr-un operand) este scos din stivă într-un registru, numărul din indicatorul stivei RSP crește cu 8. Sistemul de operare decide dimensiunea stivei și unde începe în RAM. și crește în jos. Amintiți-vă că o stivă este o structură Last-In-First-Out (LIFO) care crește în jos și se micșorează în sus în acest caz.

Pentru a împinge conținutul registrului µP RBX în stivă, procedați în felul următor:

PUSH RBX

Pentru a readuce ultima intrare din stivă în RBX, procedați în felul următor:

POP RBX

6.38 Procedura în x64

Subrutina din x64 se numește „procedură”. Stiva este folosită aici mai mult decât este folosită pentru 6502 µP. Sintaxa pentru o procedură x64 este:

proc_name:
organism de procedură

dreapta

Înainte de a continua, observați că opcode-urile și etichetele pentru o subrutină x64 (instrucțiunile limbajului de asamblare în general) nu fac distincție între majuscule și minuscule. Acesta este proc_name este același cu PROC_NAME. Ca și 6502, numele procedurii (eticheta) începe la începutul unei noi linii în editorul de text pentru limbajul de asamblare. Acesta este urmat de două puncte și nu de spațiu și cod operațional ca în cazul 6502. Urmează corpul subrutinei, care se termină cu RET și nu RTS ca în cazul 6502 µP. Ca și în cazul 6502, fiecare instrucțiune din corp, inclusiv RET, nu începe la începutul liniei sale. Rețineți că o etichetă de aici poate avea mai mult de 8 caractere. Pentru a apela această procedură, de deasupra sau dedesubtul procedurii tastate, procedați în felul următor:

Apelați proc_name

Cu 6502, numele etichetei este doar tip pentru apelare. Totuși, aici se tastează cuvântul rezervat „CALL” sau „call”, urmat de numele procedurii (subrutinei) după un spațiu.

Când aveți de-a face cu proceduri, există de obicei două proceduri. O procedură o cheamă pe cealaltă. Procedura care apelează (are instrucțiunea de apelare) se numește „apelant”, iar procedura care este numită se numește „apelat”. Există o convenție (reguli) de urmat.

Regulile apelantului

Apelantul trebuie să respecte următoarele reguli atunci când invocă o subrutină:

1. Înainte de a apela o subrutină, apelantul ar trebui să salveze conținutul anumitor registre care sunt desemnate ca fiind salvate de apelant în stiva. Registrele salvate de apelant sunt R10, R11 și orice registre în care sunt introduși parametrii (RDI, RSI, RDX, RCX, R8, R9). Dacă conținutul acestor registre trebuie să fie păstrat în apelul subrutinei, împingeți-le în stivă în loc să le salvați apoi în RAM. Acestea trebuie făcute deoarece registrele trebuie folosite de către apelat pentru a șterge conținutul anterior.

2. Dacă procedura constă în adăugarea a două numere, de exemplu, cele două numere sunt parametrii care trebuie trecuți în stivă. Pentru a trece parametrii subrutinei, puneți șase dintre ei în următoarele registre în ordine: RDI, RSI, RDX, RCX, R8, R9. Dacă există mai mult de șase parametri în subrutină, împingeți restul pe stivă în ordine inversă (adică, primul parametru). Deoarece stiva crește în jos, primul dintre parametrii suplimentari (de fapt, al șaptelea parametru) este stocat la cea mai mică adresă (această inversare a parametrilor a fost folosită istoric pentru a permite ca funcțiile (subrutinele) să fie transmise cu un număr variabil de parametri).

3. Pentru a apela subprocedura (procedura), utilizați instrucțiunea de apelare. Această instrucțiune plasează adresa de retur peste parametrii de pe stivă (poziția cea mai de jos) și ramurile la codul subrutinei.

4. După revenirea subrutinei (adică imediat după instrucțiunea de apel), apelantul trebuie să elimine orice parametri suplimentari (dincolo de cei șase care sunt stocați în registre) din stivă. Aceasta restabilește stiva la starea sa înainte de efectuarea apelului.

5. Apelantul se poate aștepta să găsească valoarea returnată (adresa) a subrutinei în registrul RAX.

6. Apelantul restabilește conținutul registrelor salvate de apelant (R10, R11 și oricare din registrele de trecere a parametrilor) prin scoaterea lor din stivă. Apelantul poate presupune că niciun alt registre nu a fost modificat de subrutină.

Datorită modului în care este structurată convenția de apelare, este de obicei cazul ca unii (sau majoritatea) acești pași să nu facă nicio modificare în stivă. De exemplu, dacă există șase sau mai puțini parametri, nimic nu este împins în stivă în acel pas. De asemenea, programatorii (și compilatorii) păstrează în mod obișnuit rezultatele la care le pasă în afara registrelor salvate de apelant în pașii 1 și 6 pentru a preveni împingerile și popsurile în exces.

Există alte două moduri de a trece parametrii într-o subrutină, dar acestea nu vor fi abordate în acest curs de carieră online. Unul dintre ele folosește stiva în sine în locul registrelor de uz general.

Regulile apelatului

Definiția subrutinei apelate ar trebui să respecte următoarele reguli:

1. Alocați variabilele locale (variabilele care sunt dezvoltate în cadrul procedurii) folosind registrele sau făcând spațiu pe stivă. Amintiți-vă că teancul crește în jos. Deci, pentru a face spațiu în partea de sus a stivei, indicatorul stivei ar trebui să fie decrementat. Cantitatea cu care indicatorul stivei este decrementat depinde de numărul necesar de variabile locale. De exemplu, dacă sunt necesare un float local și un lung local (12 octeți în total), indicatorul de stivă trebuie să fie decrementat cu 12 pentru a face spațiu pentru aceste variabile locale. Într-un limbaj de nivel înalt precum C, aceasta înseamnă să declarați variabilele fără a atribui (inițializa) valorile.

2. În continuare, trebuie salvate valorile oricăror registre care sunt desemnate salvate de apelat (registre de uz general care nu sunt salvate de apelant) care sunt utilizate de funcție. Pentru a salva registrele, împingeți-le pe stivă. Registrele salvate de apelat sunt RBX, RBP și R12 până la R15 (RSP este, de asemenea, păstrat prin convenția de apel, dar nu trebuie să fie împins pe stivă în timpul acestui pas).

După efectuarea acestor trei acțiuni, operarea efectivă a subrutinei poate continua. Când subrutina este gata să revină, regulile convenției de apel continuă.

3. Când subprogramul este terminat, valoarea returnată pentru subprogram ar trebui să fie plasată în RAX dacă nu este deja acolo.

4. Subrutina trebuie să restabilească vechile valori ale oricăror registre salvate de apelat (RBX, RBP și R12 până la R15) care au fost modificate. Conținutul registrului este restaurat prin scoaterea lor din stivă. Rețineți că registrele ar trebui să apară în ordinea inversă în care au fost împinse.

5. Apoi, dealocam variabilele locale. Cel mai simplu mod de a face acest lucru este să adăugați la RSP aceeași sumă care a fost scăzută din acesta la pasul 1.

6. În final, ne întoarcem la apelant executând o instrucțiune ret. Această instrucțiune va găsi și elimina adresa de retur corespunzătoare din stivă.

Un exemplu de corpul subrutinei apelantului pentru a apela o altă subrutină care este „myFunc” este următorul (citiți comentariile):

; Doriți să apelați o funcție „myFunc” care necesită trei
; parametru întreg. Primul parametru este în RAX.
; Al doilea parametru este constanta 456. Al treilea
; parametrul este în locația de memorie „variabil”

push rdi ; rdi va fi un parametru, așa că îl salvează
; long retVal = myFunc ( x , 456 , z ) ;

mov rdi , rax ; pune primul parametru în RDI
mov rsi, 456; pune al doilea parametru în RSI
mov rdx , [variabil] ; pune al treilea parametru în RDX

apelează myFunc; apelați funcția

pop rdi ; restabiliți valoarea RDI salvată
; valoarea returnată a myFunc este acum disponibilă în RAX

Un exemplu de funcție de apelat (myFunc) este (citește comentariile):

myFunc:
; ∗∗∗ Prolog standard al subrutinei ∗∗∗
sub rsp, 8; spațiu pentru o variabilă locală de 64 de biți (rezultat) folosind codul operațional „sub”.

push rbx ; salvare apelat-salvare registre
push rbp; ambele vor fi folosite de myFunc

; ∗∗∗ Subrutină Corp ∗∗∗
mov rax , rdi ; parametrul 1 la RAX
mov rbp , rsi ; parametrul 2 la RBP
mov rbx , rdx ; parametrul 3 la rb x
mov [ rsp + 1 6 ] , rbx ; pune rbx în variabila locală
se adaugă [ rsp + 1 6 ] , rbp ; adăugați rbp în variabila locală
mov rax , [ rsp +16 ] ; mută conținutul variabilei locale în RAX
; (valoare de returnare/rezultat final)

; ∗∗∗ Epilog subrutine standard ∗∗∗
pop rbp; recuperați registrele de salvare a apelatului
pop rbx ; invers față de când este împins
adăugați rsp, 8; dealocarea variabilelor locale. 8 înseamnă 8 octeți
ret ; scoateți valoarea maximă din stivă, săriți acolo

6.39 Întreruperi și excepții pentru x64

Procesorul oferă două mecanisme pentru întreruperea execuției programului, întreruperi și excepții:

  • O întrerupere este un eveniment asincron (se poate întâmpla în orice moment) care este de obicei declanșat de un dispozitiv I/O.
  • O excepție este un eveniment sincron (se întâmplă pe măsură ce codul este executat, preprogramat, pe baza unei apariții) care este generat atunci când procesorul detectează una sau mai multe condiții predefinite în timpul executării unei instrucțiuni. Sunt specificate trei clase de excepții: defecte, capcane și abandonuri.

Procesorul răspunde la întreruperi și excepții în esență în același mod. Când este semnalată o întrerupere sau o excepție, procesorul oprește execuția programului sau sarcinii curente și trece la o procedură de gestionare care este scrisă special pentru a gestiona condiția de întrerupere sau excepție. Procesorul accesează procedura de gestionare printr-o intrare în Tabelul Descriptor de întreruperi (IDT). Când handlerul a finalizat gestionarea întreruperii sau a excepției, controlul programului este returnat programului sau sarcinii întrerupte.

Sistemul de operare, driverele executive și/sau dispozitivele gestionează în mod normal întreruperile și excepțiile independent de programele sau sarcinile aplicației. Programele de aplicație pot, totuși, să acceseze manevrele de întrerupere și excepții care sunt încorporate într-un sistem de operare sau să îl execute prin apeluri în limbaj de asamblare.

Sunt definite optsprezece (18) întreruperi și excepții predefinite, care sunt asociate cu intrările din IDT. Două sute douăzeci și patru (224) de întreruperi definite de utilizator pot fi, de asemenea, făcute și asociate cu tabelul. Fiecare întrerupere și excepție din IDT este identificată cu un număr care se numește „vector”. Tabelul 6.39.1 listează întreruperile și excepțiile cu intrări în IDT și vectorii lor respectivi. Vectorii de la 0 la 8, de la 10 la 14 și de la 16 la 19 sunt întreruperile și excepțiile predefinite. Vectorii de la 32 la 255 sunt pentru întreruperile definite de software (utilizator) care sunt fie pentru întreruperi software, fie pentru întreruperi hardware mascate.

Când procesorul detectează o întrerupere sau o excepție, face unul dintre următoarele lucruri:

  • Executați un apel implicit la o procedură de gestionare
  • Executați un apel implicit la o sarcină de gestionare

6.4 Noțiunile de bază ale arhitecturii computerului ARM pe 64 de biți

Arhitecturile ARM definesc o familie de procesoare RISC care sunt potrivite pentru utilizare într-o mare varietate de aplicații. ARM este o arhitectură de încărcare/stocare care necesită încărcarea datelor din memorie într-un registru înainte ca orice procesare, cum ar fi o operație ALU (Arithmetic Logic Unit), să poată avea loc cu acesta. O instrucțiune ulterioară stochează rezultatul înapoi în memorie. În timp ce acest lucru ar putea părea un pas înapoi față de arhitecturile x86 și x64, care operează direct pe operanzii din memorie într-o singură instrucțiune (folosind registre de procesor, desigur), abordarea încărcare/stocare, în practică, permite mai multe operații secvențiale. să fie efectuată cu viteză mare pe un operand odată ce acesta este încărcat într-unul dintre numeroasele registre ale procesorului. Procesoarele ARM au opțiunea de little endianness sau big-endianness. Setarea implicită ARM 64 este little-endian, care este configurația utilizată în mod obișnuit de sistemele de operare. Arhitectura ARM pe 64 de biți este modernă și este setată să înlocuiască arhitectura ARM pe 32 de biți.

Notă : Fiecare instrucțiune pentru ARM µP pe 64 de biți are o lungime de 4 octeți (32 de biți).

6.41 Setul de registru ARM pe 64 de biți
Există 31 de registre cu scop general de 64 de biți pentru ARM µP pe 64 de biți. Următoarea diagramă prezintă registrele de uz general și unele registre importante:


Fig.4.11.1 Uz general pe 64 de biți și unele registre importante

Registrele de uz general sunt denumite X0 până la X30. Prima parte de 32 de biți pentru fiecare registru este denumită de la W0 la W30. Când diferența dintre 32 de biți și 64 de biți nu este accentuată, se folosește prefixul „R”. De exemplu, R14 se referă la W14 sau X14.

6502 µP are un contor de program pe 16 biți și poate adresa 2 16 locații ale octeților de memorie. ARM µP pe 64 de biți are un contor de programe pe 64 de biți și poate adresa până la 2 64 = 1,844674407 x 1019 (de fapt 18.446.744.073.709.551.616) locații de octeți de memorie. Contorul programului deține adresa următoarei instrucțiuni de executat. Lungimea instrucțiunii ARM64 sau AArch64 este de obicei de patru octeți. Procesorul crește automat acest registru cu patru după ce fiecare instrucțiune este preluată din memorie.

Registrul Stack Pointer sau SP nu se numără printre cele 31 de registre de uz general. Indicatorul de stivă al oricărei arhitecturi indică ultima intrare de stivă din memorie. Pentru ARM-64, stiva crește în jos.

6502 µP are un registru de stare a procesorului pe 8 biți. Echivalentul din ARM64 se numește registru PSTATE. Acest registru stochează steagurile care sunt utilizate pentru rezultatele operațiunilor și pentru controlul procesorului (µP). Are o lățime de 32 de biți. Următorul tabel oferă numele, indexul și semnificațiile biților utilizați în mod obișnuit în registrul PSTATE:

Tabelul 6.41.1
Cele mai utilizate steaguri PSTATE (biți)
Simbol Pic Scop
M 0-3 Mod: nivelul actual de privilegii de execuție (USR, SVC și așa mai departe).
T 4 Thumb: Este setat dacă setul de instrucțiuni T32 (Thumb) este activ. Dacă este clar, setul de instrucțiuni ARM este activ. Codul utilizatorului poate seta și șterge acest bit.
ȘI 9 Endianness: setarea acestui bit activează modul big-endian. Dacă este clar, modul little-endian este activ. Implicit este modul little-endian.
Q 27 Indicatorul de saturație cumulativă: este setat dacă, la un moment dat dintr-o serie de operații, are loc o depășire sau o saturație
ÎN 28 Flag overflow: este setat dacă operația a dus la un overflow semnat.
C 29 Indicatorul de transport: indică dacă adăugarea a produs un transfer sau scăderea a produs un împrumut.
CU 30 Indicatorul zero: este setat dacă rezultatul unei operații este zero.
N 31 Flag negativ: este setat dacă rezultatul unei operații este negativ.

ARM-64 µP are multe alte registre.

SIMD
SIMD înseamnă o singură instrucțiune, date multiple. Aceasta înseamnă că o instrucțiune de limbaj de asamblare poate acționa asupra mai multor date în același timp într-un singur microprocesor. Există treizeci și două de registre cu lățime de 128 de biți pentru utilizare cu SIMD și operațiuni în virgulă mobilă.

6.42 Maparea memoriei
RAM și DRAM sunt ambele memorii cu acces aleatoriu. DRAM funcționează mai lent decât RAM. DRAM este mai ieftin decât RAM. Dacă există mai mult de 32 de gigaocteți (GB) de DRAM continuă în memorie, vor exista mai multe probleme de gestionare a memoriei: 32 GB = 32 x 1024 x 1024 x 1024 de octeți. Pentru un întreg spațiu de memorie care este mult mai mare de 32 GB, DRAM-ul de peste 32 GB ar trebui să fie intercalat cu RAM-uri pentru o gestionare mai bună a memoriei. Pentru a înțelege harta memoriei ARM-64, ar trebui să înțelegeți mai întâi harta memoriei de 4 GB pentru unitatea centrală de procesare (CPU) ARM pe 32 de biți. CPU înseamnă µP. Pentru un computer pe 32 de biți, spațiul maxim adresabil în memorie este de 2 32 = 4 x 2 10 x 2 10 x 2 10 = 4 x 1024 x 1024 x 1024 = 4.294.967.296 = 4 GB.

Hartă memorie ARM pe 32 de biți
Harta memoriei pentru un ARM pe 32 de biți este:

Pentru un computer pe 32 de biți, dimensiunea maximă a întregii memorie este de 4 GB. De la adresa de 0 GB la adresa de 1 GB sunt locațiile sistemului de operare ROM, RAM și I/O. Întreaga idee a adreselor ROM OS, RAM și I/O este similară cu situația Commodore-64 cu un posibil CPU 6502. ROM-ul OS pentru Commodore-64 se află la capătul superior al spațiului de memorie. Sistemul de operare ROM de aici este mult mai mare decât cel al lui Commodore-64 și se află la începutul întregului spațiu de adrese de memorie. În comparație cu alte computere moderne, sistemul de operare ROM de aici este complet, în sensul că este comparabil cu cantitatea de sistem de operare din hard disk-urile lor. Există două motive principale pentru a avea sistemul de operare în circuitele integrate ROM: 1) CPU-urile ARM sunt utilizate mai ales în dispozitive mici, cum ar fi smartphone-urile. Multe hard disk-uri sunt mai mari decât smartphone-urile și alte dispozitive mici, 2) pentru securitate. Când sistemul de operare se află în memoria numai pentru citire, acesta nu poate fi corupt (părți suprascrise) de către hackeri. Secțiunile RAM și secțiunile de intrare/ieșire sunt, de asemenea, foarte mari în comparație cu cele ale lui Commodore-64.

Când alimentarea este pornită cu sistemul de operare ROM pe 32 de biți, sistemul de operare trebuie să înceapă la (pornire de la) adresa 0x00000000 sau adresa 0xFFFF0000 dacă HiVEC este activat. Deci, atunci când alimentarea este pornită după faza de resetare, hardware-ul procesorului încarcă 0x00000000 sau 0xFFFF0000 în Contorul de programe. Prefixul „0x” înseamnă hexazecimal. Adresa de pornire a procesoarelor ARMv8 pe 64 de biți este o implementare definită. Cu toate acestea, autorul îl sfătuiește pe inginerul de calculatoare să înceapă de la 0x00000000 sau 0xFFFF0000 de dragul compatibilității cu versiunea anterioară.

De la 1 GB la 2 GB este intrarea/ieșirea mapată. Există o diferență între I/O mapat și doar I/O care se găsesc între 0 GB și 1 GB. Cu I/O, adresa pentru fiecare port este fixată ca și în cazul Commodore-64. Cu I/O mapat, adresa pentru fiecare port nu este neapărat aceeași pentru fiecare operațiune a computerului (dinamică).

De la 2 GB la 4 GB este DRAM. Aceasta este memoria RAM așteptată (sau obișnuită). DRAM înseamnă RAM dinamică, nu este sensul schimbării adresei în timpul funcționării computerului, ci în sensul că valoarea fiecărei celule din memoria RAM fizică trebuie să fie reîmprospătată la fiecare impuls de ceas.

Notă :

  • De la 0x0000,0000 la 0x0000, FFFF este ROM-ul sistemului de operare.
  • De la 0x0001,0000 la 0x3FFF,FFFF, pot fi mai multe ROM, apoi RAM și apoi niște I/O.
  • De la 0x4000,0000 la 0x7FFF,FFFF, este permisă un I/O suplimentar și/sau I/O mapat.
  • De la 0x8000,0000 la 0xFFFF, FFFF este DRAM-ul așteptat.

Acestea înseamnă că DRAM-ul așteptat nu trebuie să înceapă de la limita de memorie de 2 GB, în practică. De ce ar trebui programatorul să respecte limitele ideale atunci când nu există suficiente bănci RAM fizice care sunt introduse pe placa de bază? Acest lucru se datorează faptului că clientul nu are suficienți bani pentru toate băncile RAM.

Hartă memorie ARM pe 36 de biți
Pentru un computer ARM pe 64 de biți, toți cei 32 de biți sunt utilizați pentru a adresa întreaga memorie. Pentru un computer ARM pe 64 de biți, primii 36 de biți pot fi utilizați pentru a adresa întreaga memorie care, în acest caz, este de 2 36 = 68.719.476.736 = 64 GB. Aceasta este deja multă memorie. Calculatoarele obișnuite de astăzi nu au nevoie de această cantitate de memorie. Aceasta nu este încă până la intervalul maxim de memorie care poate fi accesat pe 64 de biți. Harta memoriei pentru 36 de biți pentru procesorul ARM este:

De la adresa de 0 GB la adresa de 4 GB este harta memoriei pe 32 de biți. „Rezervat” înseamnă neutilizat și este păstrat pentru utilizare ulterioară. Nu trebuie să fie bănci de memorie fizică care sunt introduse pe placa de bază pentru spațiul respectiv. Aici, DRAM și I/O mapat au aceleași semnificații ca și pentru harta memoriei pe 32 de biți.

Următoarea situație poate fi întâlnită în practică:

  • 0x1 0000 0000 – 0x3 FFFF FFFF; rezervat. 12 GB de spațiu de adrese sunt rezervați pentru utilizare ulterioară.
  • 0x4 0000 0000 – 0x7 FFFF FFFF; I/O mapat. 16 GB de spațiu de adrese sunt disponibile pentru I/O mapate dinamic.
  • 0x8 0000 0000 – 0x8 7FFF FFFF FFFF; Hole sau DRAM. 2 GB de spațiu de adrese pot conține oricare dintre următoarele:
    • Orificiu pentru a activa partiționarea dispozitivului DRAM (așa cum este descris în discuția următoare).
    • DRAM.
  • 0x8 8000 0000 – 0xF FFFF FFFF; DRAM. 30 GB spațiu de adresă pentru DRAM.

Această hartă de memorie este un superset al hărții de adrese pe 32 de biți, spațiul suplimentar fiind împărțit ca 50% DRAM (1/2) cu o gaură opțională în ea și 25% spațiu I/O mapat și spațiu rezervat (1/4). ). Restul de 25% (1/4) este pentru harta memoriei pe 32 de biți ½ + ¼ + ¼ = 1.

Notă : De la 32 de biți la 360 de biți este o adăugare de 4 biți la partea cea mai semnificativă a 36 de biți.

Hartă memorie pe 40 de biți
Harta de adrese pe 40 de biți este un superset al hărții de adrese pe 36 de biți și urmează același model de 50% DRAM al unei găuri opționale din ea, 25% spațiu I/O mapat și spațiu rezervat și restul celor 25% spațiu pentru harta de memorie anterioară (36 de biți). Diagrama pentru harta memoriei este:

Dimensiunea găurii este 544 – 512 = 32 GB. Următoarea situație poate fi întâlnită în practică:

  • 0x10 0000 0000 – 0x3F FFFF FFFF; rezervat. 192 GB de spațiu de adrese sunt rezervați pentru utilizare ulterioară.
  • 0x40 0000 0000 – 0x7F FFFF FFFF; cartografiat. I/O 256 GB de spațiu de adrese sunt disponibile pentru I/O mapate dinamic.
  • 0x80 0000 0000 – 0x87 FFFF FFFF; gaură sau DRAM. 32 GB de spațiu de adrese pot conține oricare dintre următoarele:
    • Orificiu pentru a activa partiționarea dispozitivului DRAM (așa cum este descris în următoarea discuție)
    • DRAM
  • 0x88 0000 0000 – 0xFF FFFF FFFF; DRAM. 480 GB spațiu de adresă pentru DRAM.

Notă : De la 36 de biți la 40 de biți este o adăugare de 4 biți la partea cea mai semnificativă a 36 de biți.

gaura DRAM
În harta memoriei dincolo de 32 de biți, este fie o gaură DRAM, fie o continuare a DRAM din partea de sus. Când este o gaură, este de apreciat după cum urmează: gaura DRAM oferă o modalitate de a partiționa un dispozitiv DRAM mare în mai multe intervale de adrese. Orificiul opțional DRAM este propus la începutul limitei superioare a adresei DRAM. Acest lucru permite o schemă de decodare simplificată atunci când partiționați un dispozitiv DRAM de capacitate mare în regiunea inferioară adresată fizic.

De exemplu, o parte DRAM de 64 GB este subdivizată în trei regiuni, cu decalajele de adresă efectuate printr-o simplă scădere în biții de adresă de ordin înalt, după cum urmează:

Tabelul 6.42.1
Exemplu de partiționare DRAM de 64 GB cu găuri
Adresele fizice în SoC Decalaj Adresă DRAM internă
2 GB (hartă pe 32 de biți) 0x00 8000 0000 – 0x00 FFFF FFFF -0x00 8000 0000 0x00 0000 0000 – 0x00 7FFF FFFF
30 GBytes (hartă pe 36 de biți) 0x08 8000 0000 – 0x0F FFFF FFFF -0x08 0000 0000 0x00 8000 0000 – 0x07 FFFF FFFF
32 GB (hartă pe 40 de biți) 0x88 0000 0000 – 0x8F FFFF FFFF -0x80 0000 0000 0x08 0000 0000 – 0x0F FFFF FFFF

Hărți propuse de memorie adresată pe 44 și 48 de biți pentru procesoarele ARM
Să presupunem că un computer personal are 1024 GB (= 1 TB) de memorie; asta e prea multa memorie. Și astfel, hărțile de memorie adresate pe 44 și 48 de biți pentru procesoarele ARM pentru 16 TB și, respectiv, 256 TB, sunt doar propuneri pentru nevoile viitoare ale computerelor. De fapt, aceste propuneri pentru procesoarele ARM urmează aceeași împărțire a memoriei după raport ca hărțile de memorie anterioare. Adică: 50% DRAM cu o gaură opțională în ea, 25% spațiu I/O mapat și spațiu rezervat și restul de 25% spațiu pentru harta de memorie anterioară.

Hărțile de memorie adresate pe 52 de biți, 56 de biți, 60 de biți și 64 de biți urmează să fie propuse pentru ARM 64 de biți pentru viitorul îndepărtat. Dacă oamenii de știință din acel moment mai consideră utilă partiționarea 50: 25: 25 a întregului spațiu de memorie, ei vor menține raportul.

Notă : SoC înseamnă System-on-Chip care se referă la circuite din cipul µP care altfel nu ar fi fost acolo.

SRAM sau Static Random Access Memory este mai rapidă decât DRAM-ul mai tradițional, dar necesită mai multă zonă de siliciu. SRAM nu necesită reîmprospătare. Cititorul își poate imagina RAM ca SRAM.

6.43 Moduri de adresare în limbaj de asamblare pentru ARM 64
ARM este o arhitectură de încărcare/stocare care necesită încărcarea datelor din memorie într-un registru al procesorului înainte ca orice procesare, cum ar fi o operație logică aritmetică, să poată avea loc cu acesta. O instrucțiune ulterioară stochează rezultatul înapoi în memorie. Deși acest lucru ar putea părea un pas înapoi față de x86 și arhitecturile sale ulterioare x64, care operează direct pe operanzii din memorie într-o singură instrucțiune, în practică, abordarea încărcare/stocare permite efectuarea mai multor operații secvențiale la viteză mare pe un operand odată ce este încărcat într-unul dintre numeroasele registre de procesor.

Formatul limbajului de asamblare ARM are asemănări și diferențe cu seria x64 (x86).

  • Decalaj : O constantă semnată poate fi adăugată la registrul de bază. Offset-ul este introdus ca parte a instrucțiunii. De exemplu: ldr x0, [rx, #10] încarcă r0 cu cuvântul la adresa r1+10.
  • Inregistreaza-te : O creștere nesemnată care este stocată într-un registru poate fi adăugată sau scăzută din valoarea dintr-un registru de bază. De exemplu: ldr r0, [x1, x2] încarcă r0 cu cuvântul la adresa x1+x2. Oricare dintre registre poate fi considerat ca fiind registrul de bază.
  • Registrul scalat : O creștere într-un registru este deplasată la stânga sau la dreapta cu un număr specificat de poziții de biți înainte de a fi adăugată sau scăzută din valoarea registrului de bază. De exemplu: ldr x0, [x1, x2, lsl #3] încarcă r0 cu cuvântul la adresa r1+(r2×8). Deplasarea poate fi o deplasare logică la stânga sau la dreapta (lsl sau lsr) care inserează biți zero în pozițiile de biți libere sau o deplasare aritmetică la dreapta (asr) care reproduce bitul de semn în pozițiile libere.

Când sunt implicați doi operanzi, destinația vine înaintea (în stânga) sursei (există câteva excepții de la aceasta). Codurile operaționale pentru limbajul de asamblare ARM nu fac distincție între majuscule și minuscule.

Mod de adresare ARM64 imediată
Exemplu:

mov r0, #0xFF000000 ; Încărcați valoarea de 32 de biți FF000000h în r0

O valoare zecimală este fără 0x, dar este totuși precedată de #.

Înregistrează-te direct
Exemplu:

mov x0, x1 ; Copiați x1 în x0

Înregistrare indirectă
Exemplu:

str x0, [x3] ; Stocați x0 la adresa din x3

Înregistrare indirectă cu Offset
Exemple:

ldr x0, [x1, #32] ; Încărcați r0 cu valoarea la adresa [r1+32]; r1 este registrul de bază
str x0, [x1, #4] ; Stocați r0 la adresa [r1+4]; r1 este registrul de bază; numerele sunt baza 10

Înregistrare indirectă cu offset (pre-incrementat)
Exemple:

ldr x0, [x1, #32]! ; Încărcați r0 cu [r1+32] și actualizați r1 la (r1+32)
str x0, [x1, #4]! ; Stocați r0 la [r1+4] și actualizați r1 la (r1+4)

Rețineți utilizarea semnului „!” simbol.

Înregistrare indirectă cu offset (post-incrementat)
Exemple:

ldr x0, [x1], #32 ; Încărcați [x1] la x0, apoi actualizați x1 la (x1+32)
str x0, [x1], #4; Stocați x0 la [x1], apoi actualizați x1 la (x1+4)

Înregistrare dublă indirectă
Adresa operandului este suma unui registru de bază și a unui registru incremental. Numele registrului sunt înconjurate de paranteze drepte.
Exemple:

ldr x0, [x1, x2] ; Încărcați x0 cu [x1+x2]
str x0, [rx, x2] ; Stocați x0 în [x1+x2]

Modul de adresare relativă
În modul de adresare relativă, instrucțiunea efectivă este următoarea instrucțiune din Contorul de programe, plus un index. Indicele poate fi pozitiv sau negativ.
Exemplu:

ldr x0, [buc, #24]

Aceasta înseamnă registrul de încărcare X0 cu cuvântul care este indicat de conținutul PC plus 24.

6.44 Câteva instrucțiuni utilizate în mod obișnuit pentru ARM 64
Iată instrucțiunile utilizate în mod obișnuit:

6.45 Buclă

Ilustrare
Următorul cod continuă să adauge valoarea din registrul X10 la valoarea din X9 până când valoarea din X8 este zero. Să presupunem că toate valorile sunt numere întregi. Valoarea din X8 este scăzută cu 1 în fiecare iterație:

buclă:
CBZ X8, săriți
ADAUGĂ X9, X9, X10; primul X9 este destinația și al doilea X9 este sursa
SUB X8, X8, #1; primul X8 este destinația și al doilea X8 este sursa
bucla B
ocolire:

Ca și în cazul 6502 µP și X64 µP, eticheta din ARM 64 µP începe la începutul liniei. Restul instrucțiunilor încep la unele spații după începutul liniei. Cu x64 și ARM 64, eticheta este urmată de două puncte și de o nouă linie. În timp ce cu 6502, eticheta este urmată de o instrucțiune după un spațiu. În codul anterior, prima instrucțiune care este „CBZ X8, skip” înseamnă că, dacă valoarea din X8 este zero, continuați la eticheta „skip:”, sărind instrucțiunile intermediare și continuând cu restul instrucțiunilor de mai jos. 'ocolire:'. „Bucla B” este un salt necondiționat la eticheta „buclă”. Orice alt nume de etichetă poate fi folosit în locul „buclă”.

Deci, ca și în cazul 6502 µP, utilizați instrucțiunile de ramificare pentru a avea o buclă cu ARM 64.

6.46 ARM 64 Intrare/Ieșire
Toate perifericele ARM (porturile interne) sunt mapate în memorie. Aceasta înseamnă că interfața de programare este un set de registre adresate memoriei (porturi interne). Adresa unui astfel de registru este un decalaj față de o anumită adresă de bază de memorie. Acest lucru este similar cu modul în care 6502 face intrarea/ieșirea. ARM nu are opțiunea pentru spațiu de adrese I/O separat.

6.47 Teanc de ARM 64
ARM 64 are o stivă în memorie (RAM) într-un mod similar cu cel al 6502 și x64. Cu toate acestea, cu ARM64, nu există niciun opcode push sau pop. Stiva din ARM 64 crește, de asemenea, în jos. Adresa din indicatorul stivei indică imediat după ultimul octet al ultimei valori care este plasată în stivă.

Motivul pentru care nu există un cod de operare pop sau push generic pentru ARM64 este că ARM 64 își gestionează stiva în grupuri de 16 octeți consecutivi. Cu toate acestea, valorile există în grupuri de octeți de un octet, doi octeți, patru octeți și 8 octeți. Deci, o valoare poate fi plasată în stivă, iar restul locurilor (locații de octeți) pentru a compensa 16 octeți sunt umplute cu octeți inactivi. Acest lucru are dezavantajul de a pierde memoria. O soluție mai bună este să umpleți locația de 16 octeți cu valori mai mici și să aveți un cod de programator scris care urmărește de unde provin valorile din locația de 16 octeți (înregistrări). Acest cod suplimentar este necesar și pentru a trage înapoi valorile. O alternativă la aceasta este să umpleți două registre de uz general de 8 octeți cu valori diferite și apoi să trimiteți conținutul celor doi registre de 8 octeți într-o stivă. Un cod suplimentar este încă necesar aici pentru a urmări valorile mici specifice care intră în stivă și părăsesc stiva.

Următorul cod stochează patru date de 4 octeți în stivă:

str w0, [sp, #-4]!
str w1, [sp, #-8]!
str w2, [sp, #-12]!
str w3, [sp, #-16]!

Primii patru octeți (w) ai registrelor – x0, x1, x2 și x3 – sunt trimiși în locații de 16 octeți consecutivi din stivă. Rețineți că folosiți „str” și nu „push”. Observați simbolul exclamației de la sfârșitul fiecărei instrucțiuni. Deoarece stiva de memorie crește în jos, prima valoare de patru octeți începe la o poziție care este cu minus patru octeți sub poziția anterioară a indicatorului de stivă. Urmează restul valorilor de patru octeți, în jos. Următorul segment de cod va face echivalentul corect (și în ordine) cu apariția celor patru octeți:

ldr w3, [sp], #0
ldr w2, [sp], #4
ldr w1, [sp], #8
ldr w0, [sp], #12

Rețineți că folosiți codul operațional ldr în loc de pop. De asemenea, rețineți că simbolul exclamației nu este folosit aici.

Toți octeții din X0 (8 octeți) și X1 (8 octeți) pot fi trimiși către locația de 16 octeți din stivă, după cum urmează:

stp x0, x1, [sp, #-16]! ; 8 + 8 = 16

În acest caz, registrele x2 (w2) și x3 (w3) nu sunt necesare. Toți octeții căutați sunt în registrele X0 și X2. Observați codul operațional stp pentru stocarea perechilor de conținut de registru în RAM. Rețineți și simbolul exclamației. Echivalentul pop este:

ldp x0, x1, [sp], #0

Nu există niciun semn de exclamare pentru această instrucțiune. Rețineți opcode LDP în loc de LDR pentru încărcarea a două locații consecutive de date din memorie în două registre µP. De asemenea, rețineți că copierea din memorie într-un registru µP se încarcă, care nu trebuie confundată cu încărcarea unui fișier de pe disc în RAM, iar copierea dintr-un registru µP în RAM este stocarea.

6.48 Subprogram
O subrutină este un bloc de cod care îndeplinește o sarcină, opțional pe baza unor argumente și returnează opțional un rezultat. Prin convenție, registrele R0 la R3 (patru registre) sunt folosite pentru a transmite argumentele (parametrii) unei subrutine, iar R0 este folosit pentru a transmite un rezultat înapoi apelantului. O subrutină care necesită mai mult de 4 intrări utilizează stiva pentru intrările suplimentare. Pentru a apela o subrutină, utilizați legătura sau instrucțiunea de ramificare condiționată. Sintaxa pentru instrucțiunea de legătură este:

Eticheta BL

Unde BL este codul operațional și eticheta reprezintă începutul (adresa) subrutinei. Această ramură este necondiționată, înainte sau înapoi în 128 MB. Sintaxa pentru instrucțiunea de ramificare condiționată este:

B.cond etichetă

Unde cond este condiția, de exemplu, eq (egal) sau ne (nu este egal). Următorul program are subrutina doadd care adaugă valorile a două argumente și returnează un rezultat în R0:

AREA subrută, COD, READONLY ; Denumiți acest bloc de cod
INTRARE ; Marcați prima instrucțiune de executat
porniți MOV r0, #10; Configurați parametrii
MOV r1, #3
BL doadd ; Subrutină de apel
opri MOV r0, #0x18 ; angel_SWIreason_ReportException
LDR r1, =0x20026; ADP_Stopped_ApplicationExit
SVC #0x123456; ARM semihosting (fost SWI)
doadd ADD r0, r0, r1 ; Cod de subrutină
BX lr ; Întoarcerea din subrutină
;
SFÂRŞIT ; Marcați sfârșitul fișierului

Numerele de adăugat sunt zecimalele 10 și zecimale 3. Primele două linii din acest bloc de cod (program) vor fi explicate mai târziu. Următoarele trei linii trimit 10 la registrul R0 și 3 la registrul R1 și apelează, de asemenea, subrutina doadd. „Doadd” este eticheta care conține adresa de la începutul subrutinei.

Subprogramul este format din doar două linii. Prima linie adaugă conținutul 3 al lui R la conținutul 10 al lui R0, ceea ce permite rezultatul lui 13 în R0. A doua linie cu opcode BX și operand LR revine din subrutină la codul apelantului.

DREAPTA
Opcode-ul RET din ARM 64 încă se ocupă de subrutină, dar funcționează diferit față de RTS în 6502 sau RET pe x64 sau combinația „BX LR” din ARM 64. În ARM 64, sintaxa pentru RET este:

DREPT {Xn}

Această instrucțiune oferă programului posibilitatea de a continua cu o subrutină care nu este subrutina apelantului sau pur și simplu să continue cu o altă instrucțiune și următorul său segment de cod. Xn este un registru de uz general care deține adresa la care programul ar trebui să continue. Această instrucțiune se ramifică necondiționat. Este implicit la conținutul X30 dacă nu este dat Xn.

Apel de procedură Standard
Dacă programatorul dorește ca codul său să interacționeze cu un cod care este scris de altcineva sau cu un cod care este produs de un compilator, programatorul trebuie să fie de acord cu persoana respectivă sau cu scriitorul compilatorului asupra regulilor de utilizare a registrului. Pentru arhitectura ARM, aceste reguli sunt numite Procedure Call Standard sau PCS. Acestea sunt acorduri între cele două sau trei părți. PCS specifică următoarele:

  • Ce registre µP sunt folosite pentru a trece argumentele în funcție (subrutină)
  • Ce registre µP sunt folosite pentru a returna rezultatul la funcția care efectuează apelarea, cunoscută sub numele de apelant
  • Care µP înregistrează funcția care este apelată, care este cunoscută sub numele de apelat, poate deteriora
  • Care µP înregistrează apelatul nu poate corupe

6.49 Întreruperi
Există două tipuri de circuite de control de întrerupere disponibile pentru procesorul ARM:

  • Controler de întrerupere standard: handler-ul de întrerupere determină ce dispozitiv necesită întreținere citind un registru bitmap al dispozitivului în controlerul de întrerupere.
  • Controler de întrerupere vectorială (VIC): prioritizează întreruperile și simplifică determinarea dispozitivului care a cauzat întreruperile. După asocierea unei priorități și a unei adrese de gestionare cu fiecare întrerupere, VIC-ul afirmă un semnal de întrerupere către procesor numai dacă prioritatea unei noi întreruperi este mai mare decât gestionarea întreruperilor care se execută în prezent.

Notă : Excepția se referă la eroare. Detaliile pentru controlerul de întrerupere vectorială pentru computerul ARM pe 32 de biți sunt următoarele (64 de biți este similar):

Tabelul 6.49.1
Excepție/Întrerupere a vectorului ARM pentru computer pe 32 de biți
Excepție/Întrerupere Mână Scurtă Abordare Adresă înaltă
Resetați RESET 0x00000000 0xffff0000
Instrucțiune nedefinită UNDEF 0x00000004 0xffff0004
Întreruperea software-ului SWI 0x00000008 0xffff0008
Avortarea preluării pabt 0x0000000C 0xffff000C
Data abort DABT 0x00000010 0xffff0010
Rezervat 0x00000014 0xffff0014
Solicitare de întrerupere IRQ 0x00000018 0xffff0018
Solicitare de întrerupere rapidă FIQ 0x0000001C 0xffff001C

Acesta arată ca aranjamentul pentru arhitectura 6502 unde NMI , BR , și IRQ poate avea pointeri în pagina zero, iar rutinele corespunzătoare sunt în sus în memorie (ROM OS). Scurte descrieri ale rândurilor din tabelul anterior sunt următoarele:

RESET
Acest lucru se întâmplă când procesorul pornește. Inițializează sistemul și setează stivele pentru diferite moduri de procesor. Este excepția cu cea mai mare prioritate. La intrarea în handler-ul de resetare, CPSR este în modul SVC și ambii biți IRQ și FIQ sunt setați la 1, maschând orice întreruperi.

DATA ABORT
A doua cea mai mare prioritate. Acest lucru se întâmplă atunci când încercăm să citim/scriem într-o adresă nevalidă sau să accesăm permisiunea de acces greșită. La intrarea în Data Abort Handler, IRQ-urile vor fi dezactivate (I-bit set 1) și FIQ va fi activat. IRQ-urile sunt mascate, dar FIQ-urile sunt ținute demascate.

FIQ
Întreruperea cu cea mai mare prioritate, IRQ și FIQ, sunt dezactivate până când FIQ este gestionat.

IRQ
Întreruperea cu prioritate înaltă, handler-ul IRQ, este introdusă numai dacă nu există FIQ în curs și anularea datelor.

Anulare pre-preluare
Acest lucru este similar cu anularea datelor, dar se întâmplă la eșecul de preluare a adresei. La intrarea în handler, IRQ-urile sunt dezactivate, dar FIQ-urile rămân activate și se pot întâmpla în timpul unei întreruperi pre-preluare.

SWI
O excepție de întrerupere software (SWI) apare atunci când instrucțiunea SWI este executată și niciuna dintre celelalte excepții cu prioritate mai mare nu a fost semnalată.

Instrucțiune nedefinită
Excepția Undefined Instruction apare atunci când o instrucțiune care nu se află în setul de instrucțiuni ARM sau Thumb ajunge la etapa de execuție a conductei și nici una dintre celelalte excepții nu a fost semnalată. Aceasta este aceeași prioritate ca SWI, așa cum se poate întâmpla la un moment dat. Aceasta înseamnă că instrucțiunea care este executată nu poate fi atât o instrucțiune SWI, cât și o instrucțiune nedefinită în același timp.

ARM Gestionarea excepțiilor
Următoarele evenimente apar atunci când are loc o excepție:

  • Stocați CPSR în SPSR al modului de excepție.
  • PC-ul este stocat în LR-ul modului de excepție.
  • Registrul de legătură este setat la o anumită adresă pe baza instrucțiunii curente. De exemplu: pentru ISR, LR = ultima instrucțiune executată + 8.
  • Actualizați CPSR despre excepție.
  • Setați computerul la adresa de gestionare a excepțiilor.

6.5 Instrucțiuni și date

Datele se referă la variabile (etichete cu valorile lor) și matrice și alte structuri care sunt similare matricei. Șirul este ca o matrice de caractere. O serie de numere întregi este văzută într-unul din capitolele anterioare. Instrucțiunile se referă la codurile operaționale și operanzii acestora. Un program poate fi scris cu codurile operaționale și datele amestecate într-o secțiune continuă a memoriei. Această abordare are dezavantaje, dar nu este recomandată.

Un program trebuie scris mai întâi cu instrucțiunile, urmate de date (pluralul de date este date). Separația dintre instrucțiuni și date poate fi de doar câțiva octeți. Pentru un program, atât instrucțiunile, cât și datele pot fi în una sau două secțiuni separate în memorie.

6.6 Arhitectura Harvard

Unul dintre primele calculatoare se numește Harvard Mark I (1944). O arhitectură strictă Harvard utilizează un spațiu de adresă pentru instrucțiunile programului și un spațiu separat de adrese diferit pentru date. Aceasta înseamnă că există două amintiri separate. Următoarele arată arhitectura:


Figura 6.71 Arhitectura Harvard

Unitatea de control face decodarea instrucțiunilor. Unitatea logică aritmetică (ALU) face operațiile aritmetice cu logica combinațională (porți). ALU face, de asemenea, operațiunile logice (de exemplu, schimbarea).

Cu microprocesorul 6502, o instrucțiune merge mai întâi la microprocesor (unitatea de control) înainte ca datumul (singular pentru date) să ajungă la registrul µP înainte ca acestea să interacționeze. Aceasta necesită cel puțin două impulsuri de ceas și nu este un acces simultan la instrucțiune și la datum. Pe de altă parte, arhitectura Harvard oferă acces simultan la instrucțiuni și date, atât instrucțiunea, cât și datul intrând în µP în același timp (opcode la unitatea de control și datum la registrul µP), salvând cel puțin un impuls de ceas. Aceasta este o formă de paralelism. Această formă de paralelism este utilizată în memoria cache hardware a plăcilor de bază moderne (consultați următoarea discuție).

6.7 Memoria cache

Memoria cache (RAM) este o regiune de memorie de mare viteză (în comparație cu viteza memoriei principale) care stochează temporar instrucțiunile programului sau datele pentru utilizare ulterioară. Memoria cache funcționează mai rapid decât memoria principală. De obicei, aceste instrucțiuni sau elemente de date sunt preluate din memoria principală recentă și este posibil să fie necesare din nou în scurt timp. Scopul principal al memoriei cache este de a crește viteza accesării repetate a acelorași locații de memorie principală. Pentru a fi eficientă, accesarea elementelor din cache trebuie să fie mult mai rapidă decât accesarea sursei inițiale a instrucțiunilor sau a datelor, care este denumită Magazin de rezervă.

Când stocarea în cache este în uz, fiecare încercare de a accesa o locație de memorie principală începe cu o căutare a memoriei cache. Dacă articolul solicitat este prezent, procesorul îl preia și îl folosește imediat. Aceasta se numește cache Hit. Dacă căutarea în cache nu reușește (o pierdere a memoriei cache), instrucțiunea sau elementul de date trebuie să fie preluat din stocul de rezervă (memoria principală). În procesul de recuperare a articolului solicitat, o copie este adăugată în cache pentru o utilizare anticipată în viitorul apropiat.

Unitatea de gestionare a memoriei
Unitatea de gestionare a memoriei (MMU) este un circuit care gestionează memoria principală și registrele de memorie aferente de pe placa de bază. În trecut, era un circuit integrat separat pe placa de bază; dar astăzi, este de obicei parte a microprocesorului. MMU ar trebui să gestioneze, de asemenea, cache-ul (circuitul), care este, de asemenea, o parte a microprocesorului astăzi. Circuitul cache este un circuit integrat separat în trecut.

RAM statică
RAM statică (SRAM) are un timp de acces substanțial mai rapid decât DRAM, deși în detrimentul unor circuite mult mai complexe. Celulele de biți SRAM ocupă mult mai mult spațiu pe matrița circuitului integrat decât celulele unui dispozitiv DRAM care este capabil să stocheze o cantitate echivalentă de date. Memoria principală (RAM) constă de obicei din DRAM (RAM dinamică).

Memoria cache îmbunătățește performanța computerului deoarece mulți algoritmi care sunt executați de sistemele de operare și aplicații prezintă localitatea de referință. Localitatea de referință se referă la reutilizarea datelor care au fost accesate recent. Aceasta este denumită localitate temporală. Pe o placă de bază modernă, memoria cache se află în același circuit integrat ca și microprocesorul. Memoria principală (DRAM) este departe și este accesibilă prin autobuze. Localitatea de referință se referă și la localitatea spațială. Localitatea spațială are de-a face cu viteza mai mare de acces la date din cauza proximității fizice.

De regulă, regiunile memoriei cache sunt mici (în număr de locații de octeți) în comparație cu stocarea de rezervă (memoria principală). Dispozitivele de memorie cache sunt proiectate pentru viteză maximă, ceea ce înseamnă, în general, că sunt mai complexe și mai costisitoare pe bit decât tehnologia de stocare a datelor care este utilizată în stocul de rezervă. Datorită dimensiunii lor limitate, dispozitivele de memorie cache tind să se umple rapid. Când un cache nu are o locație disponibilă pentru a stoca o intrare nouă, o intrare mai veche trebuie să fie eliminată. Controlerul de cache folosește o politică de înlocuire a memoriei cache pentru a selecta ce intrare din cache va fi suprascrisă de noua intrare.

Scopul memoriei cache a microprocesorului este de a maximiza procentul de accesări cache în timp, oferind astfel cea mai mare rată susținută de execuție a instrucțiunilor. Pentru a atinge acest obiectiv, logica de stocare în cache trebuie să determine ce instrucțiuni și date vor fi plasate în cache și reținute pentru utilizare în viitorul apropiat.

Logica de stocare în cache a procesorului nu are asigurarea că un element de date din cache va fi folosit din nou odată ce a fost introdus în cache.

Logica stocării în cache se bazează pe probabilitatea ca, datorită localității temporale (repetă în timp) și spațială (spațială), există șanse foarte mari ca datele din cache să fie accesate în viitorul apropiat. În implementările practice pe procesoarele moderne, accesările în cache apar de obicei la 95 până la 97% din accesările la memorie. Deoarece latența memoriei cache este o mică parte din latența DRAM, o rată mare de accesare a cache-ului duce la o îmbunătățire substanțială a performanței în comparație cu un design fără cache.

Un oarecare paralelism cu Cache
După cum am menționat anterior, un program bun în memorie are instrucțiunile separate de date. În unele sisteme cache, există un circuit cache în „stânga” procesorului și există un alt circuit cache în „dreapta” procesorului. Cache-ul din stânga se ocupă de instrucțiunile unui program (sau aplicație), iar cache-ul din dreapta se ocupă de datele aceluiași program (sau aceleiași aplicații). Acest lucru duce la o mai bună viteză crescută.

6.8 Procese și fire

Atât calculatoarele CISC, cât și RISC au procese. Un proces este pe software. Un program care rulează (execută) este un proces. Sistemul de operare vine cu propriile sale programe. În timp ce computerul funcționează, rulează și programele sistemului de operare care permit computerului să funcționeze. Acestea sunt procese ale sistemului de operare. Utilizatorul sau programatorul își poate scrie propriile programe. Când programul utilizatorului rulează, este un proces. Nu contează dacă programul este scris în limbaj de asamblare sau în limbaj de nivel înalt precum C sau C++. Toate procesele (utilizator sau OS) sunt gestionate de un alt proces numit „scheduler”.

Un fir este ca un sub-proces aparținând unui proces. Un proces poate începe și împărțit în fire și apoi continuă ca un singur proces. Un proces fără fire poate fi considerat firul principal. Procesele și firele lor sunt gestionate de același planificator. Planificatorul în sine este un program atunci când este rezident pe discul sistemului de operare. Când rulează în memorie, planificatorul este un proces.

6.9 Multiprocesare

Threadurile sunt gestionate aproape ca procese. Multiprocesare înseamnă rularea mai mult de un proces în același timp. Există computere cu un singur microprocesor. Există computere cu mai mult de un microprocesor. Cu un singur microprocesor, procesele și/sau firele de execuție folosesc același microprocesor într-o manieră de intercalare (sau de tăiere în timp). Asta înseamnă că un proces folosește procesorul și se oprește fără a termina. Un alt proces sau fir folosește procesorul și se oprește fără a termina. Apoi, un alt proces sau fir folosește microprocesorul și se oprește fără a termina. Acest lucru continuă până când toate procesele și firele de execuție care au fost puse în coadă de planificator au avut o parte din procesor. Aceasta este denumită multiprocesare concomitentă.

Când există mai mult de un microprocesor, există o multiprocesare paralelă, spre deosebire de concurență. În acest caz, fiecare procesor rulează un anumit proces sau fir, diferit de ceea ce rulează celălalt procesor. Toate procesoarele de pe aceeași placă de bază își execută diferite procese și/sau fire diferite în același timp în multiprocesare paralelă. Procesele și firele de execuție în multiprocesare paralelă sunt încă gestionate de planificator. Multiprocesarea paralelă este mai rapidă decât multiprocesarea simultană.

În acest moment, cititorul se poate întreba cum procesarea paralelă este mai rapidă decât procesarea simultană. Acest lucru se datorează faptului că procesoarele partajează (trebuie să folosească în momente diferite) aceeași memorie și porturi de intrare/ieșire. Ei bine, cu utilizarea memoriei cache, funcționarea generală a plăcii de bază este mai rapidă.

6.10 Paginare

Unitatea de gestionare a memoriei (MMU) este un circuit care este aproape de microprocesor sau în cipul microprocesorului. Se ocupă de harta memoriei sau de paginare și alte probleme de memorie. Nici 6502 µP, nici computerul Commodore-64 nu au un MMU în sine (deși există încă o anumită gestionare a memoriei în Commodore-64). Commodore-64 gestionează memoria prin paginare unde fiecare pagină este 256 10 octeți lungime (100 16 octeți lungi). Nu era obligatoriu ca acesta să gestioneze memoria prin paginare. Ar putea avea doar o hartă de memorie și apoi programe care se potrivesc în diferitele lor zone desemnate. Ei bine, paginarea este o modalitate de a oferi o utilizare eficientă a memoriei fără a avea multe secțiuni de memorie care nu pot avea date sau program.

Arhitectura computerului x86 386 a fost lansată în 1985. Magistrala de adrese are o lățime de 32 de biți. Deci, un total de 2 32 = 4.294.967.296 spațiu de adrese este posibil. Acest spațiu de adrese este împărțit în 1.048.576 pagini = 1.024 KB pagini. Cu acest număr de pagini, o pagină este formată din 4.096 octeți = 4 KB. Următorul tabel prezintă paginile de adrese fizice pentru arhitectura x86 pe 32 de biți:

Tabelul 6.10.1
Pagini fizice adresabile pentru arhitectura x86
Adrese de bază 16 Pagini Adrese de bază 10
FFFFF000 – FFFFFFFF Pagina 1.048.575 4.294.963.200 – 4.294.967.295
FFFFE000 – FFFFEFFF Pagina 1.044.479 4.294.959.104 – 4.294.963.199
FFFFD000 – FFFFDFFF Pagina 1.040.383 4.294.955.008 – 4.294.959.103
|
|
|
|
|
|
|
|
|
00002000 – 00002FFF Pagina 2 8.192 – 12.288
00001000 – 00001FFF Pagina 1 4.096 – 8.191
00000000 – 00000FFF Pagina 0 0 – 4.095

O aplicație de astăzi constă din mai mult de un program. Un program poate dura mai puțin de o pagină (mai puțin de 4096) sau poate dura două sau mai multe pagini. Deci, o aplicație poate lua una sau mai multe pagini în care fiecare pagină are 4096 de octeți. Diferiți oameni pot scrie o aplicație, fiecare persoană fiind alocată uneia sau mai multor pagini.

Observați că pagina 0 este de la 00000000H la 00000FFF
pagina 1 este de la 00001000H la 00001FFFH, pagina 2 este de la 00002000 H – 00002FFF H , și așa mai departe. Pentru un computer pe 32 de biți, există două registre pe 32 de biți în procesor pentru adresarea fizică a paginii: unul pentru adresa de bază și celălalt pentru adresa indexului. Pentru a accesa locațiile octeților din pagina 2, de exemplu, registrul pentru adresa de bază ar trebui să fie 00002 H care sunt primii 20 de biți (din stânga) pentru adresele de pornire din pagina 2. Restul biților în intervalul 000 H la FFF H sunt în registrul numit „registru index”. Deci, toți octeții din pagină pot fi accesați doar prin creșterea conținutului din registrul de index de la 000 H la FFF H . Conținutul din registrul de index este adăugat la conținutul care nu se modifică în registrul de bază pentru a obține adresa efectivă. Această schemă de adresare a indexului este valabilă pentru celelalte pagini.

Cu toate acestea, nu așa este scris programul în limbaj de asamblare pentru fiecare pagină. Pentru fiecare pagină, programatorul scrie codul începând de la pagina 000 H la pagina FFF H . Deoarece codul din diferite pagini sunt conectate, compilatorul folosește adresarea indexului pentru a conecta toate adresele asociate din diferite pagini. De exemplu, presupunând că pagina 0, pagina 1 și pagina 2 sunt pentru o aplicație și fiecare are 555 H adresa care este conectată între ele, compilatorul se compilează în așa fel încât atunci când 555 H din pagina 0 este de accesat, 00000 H va fi în registrul de bază și 555 H va fi în registrul index. Când 555 H de la pagina 1 este de accesat, 00001 H va fi în registrul de bază și 555 H va fi în registrul index. Când 555 H de la pagina 2 este de accesat, 00002 H va fi în registrul de bază și 555H va fi în registrul index. Acest lucru este posibil deoarece adresele pot fi identificate folosind etichete (variabile). Diferiții programatori trebuie să convină asupra numelui etichetelor care vor fi folosite pentru diferitele adrese de conectare.

Pagina de memorie virtuală
Paginare, așa cum a fost descris anterior, poate fi modificată pentru a crește dimensiunea memoriei într-o tehnică denumită „Memorie virtuală a paginii”. Presupunând că toate paginile de memorie fizică, așa cum s-a descris anterior, au ceva (instrucțiuni și date), nu toate paginile sunt active în prezent. Paginile care nu sunt active în prezent sunt trimise pe hard disk și sunt înlocuite cu paginile de pe hard disk care trebuie să ruleze. În acest fel, dimensiunea memoriei crește. Pe măsură ce computerul continuă să funcționeze, paginile care devin inactive sunt schimbate cu paginile de pe hard disk, care pot fi încă paginile care au fost trimise din memorie pe disc. Toate acestea sunt realizate de Unitatea de gestionare a memoriei (MMU).

6.11 Probleme

Cititorul este sfătuit să rezolve toate problemele dintr-un capitol înainte de a trece la capitolul următor.

1) Prezentați asemănările și diferențele dintre arhitecturile computerelor CISC și RISC. Numiți câte un exemplu de computer SISC și RISC.

2) a) Care sunt următoarele denumiri pentru calculatorul CISC în termeni de biți: octet, cuvânt, cuvânt dublu, cuvânt patru și cuvânt patru dublu.
b) Care sunt următoarele nume pentru computerul RISC în termeni de biți: octet, jumătate de cuvânt, cuvânt și cuvânt dublu.
c) Da sau Nu. Doubleword și quadword înseamnă aceleași lucruri în ambele arhitecturi CISC și RISC?

3 a) Pentru x64, numărul de octeți pentru instrucțiunile limbajului de asamblare variază de la ce la ce?
b) Numărul de octeți pentru toate instrucțiunile în limbaj de asamblare pentru ARM 64 este fix? Dacă da, care este numărul de octeți pentru toate instrucțiunile?

4) Enumerați cele mai frecvent utilizate instrucțiuni în limbajul de asamblare pentru x64 și semnificațiile acestora.

5) Enumerați cele mai frecvent utilizate instrucțiuni în limbajul de asamblare pentru ARM 64 și semnificațiile acestora.

6) Desenați o diagramă bloc etichetată a vechiului computer Harvard Architecture. Explicați modul în care instrucțiunile și caracteristicile sale de date sunt utilizate în memoria cache a computerelor moderne.

7) Faceți diferența între un proces și un fir și dați numele procesului care se ocupă de procesele și firele de execuție în majoritatea sistemelor informatice.

8) Explicați pe scurt ce este multiprocesarea.

9) a) Explicați paginarea ca fiind aplicabilă arhitecturii computerului x86 386 µP.
b) Cum poate fi modificată această paginare pentru a mări dimensiunea întregii memorie?