Capitolul 4: Tutorialul limbajului de asamblare al microprocesorului 6502

Capitolul 4 Tutorialul Limbajului De Asamblare Al Microprocesorului 6502



Capitolul 4: Tutorialul limbajului de asamblare al microprocesorului 6502

4.1 Introducere

Microprocesorul 6502 a fost lansat în 1975. A fost folosit ca microprocesor pentru unele computere personale de atunci, cum ar fi Apple II, Commodore 64 și BBC Micro.







Microprocesorul 6502 este încă produs în număr mare astăzi. Nu mai este o unitate centrală de procesare care este folosită în calculatoarele personale (laptop-uri) astăzi, dar este încă produsă în număr mare și folosită în aparatele electronice și electrice și astăzi. Pentru a înțelege arhitecturile mai moderne ale computerelor, este foarte util să examinăm un microprocesor mai vechi, dar destul de de succes, cum ar fi 6502.



Deoarece este simplu de înțeles și programat, este unul dintre cele mai bune (dacă nu cel mai bun) microprocesor de utilizat pentru predarea limbajului de asamblare. Limbajul de asamblare este un limbaj de nivel scăzut care poate fi folosit pentru a programa un computer. Rețineți că limbajul de asamblare pentru un microprocesor este diferit de limbajul de asamblare al altui microprocesor. Limbajul de asamblare al microprocesorului 6502 este predat în acest capitol. Mai precis, 65C02 este cel care este predat, dar este pur și simplu menționat ca 6502.



Un computer celebru din trecut se numește commodore_64. 6502 este un microprocesor din familia 6500. Computerul commodore_64 folosește microprocesorul 6510. Microprocesorul 6510 este de 6500 µP. Setul de instrucțiuni al lui 6502 µP este aproape toate instrucțiunile lui 6510 µP. Cunoașterea acestui capitol și a celui următor se bazează pe computerul commodore_64. Aceste cunoștințe sunt folosite ca bază pentru a explica arhitecturile moderne de computer și sistemele de operare moderne în această parte a cursului de carieră online.





Arhitectura computerului se referă la componentele plăcii de bază a computerului și o explicație a modului în care datele circulă în interiorul fiecărei componente, în special microprocesorul, cum circulă datele între componente și, de asemenea, cum interacționează datele. Singularul pentru date este dat. O modalitate eficientă de a studia arhitectura computerului unui computer este studierea limbajului de asamblare al plăcii de bază.

Se spune că computerul commodore_64 este un computer de 8 biți. Aceasta înseamnă că informațiile sunt stocate, transferate și manipulate sub formă de coduri binare de opt biți.



Diagrama bloc a plăcii de bază Commodore 64
Schema bloc a plăcii de bază commodore 64 este:


Fig 4.1 Diagrama bloc a unității de sistem Commodore_64

Imaginează-ți microprocesorul 6510 ca fiind microprocesorul 6502. Memoria totală este o serie de octeți (8 biți pe octet). Există memoria cu acces aleatoriu (citire/scriere) în care octeții pot fi scrieți sau șterși. Când alimentarea computerului este oprită, toate informațiile din memoria cu acces aleatoriu (RAM) sunt șterse. Există, de asemenea, memoria read-only (ROM). Când alimentarea computerului este oprită, informațiile din ROM rămân (nu sunt șterse).

Există portul de intrare/ieșire (circuit) care se numește dispozitive de intrare/ieșire în diagramă. Acest port nu trebuie confundat cu porturile care sunt vizibile pe suprafețele verticale din stânga și din dreapta sau din față și din spate ale unității de sistem a computerului. Sunt două lucruri diferite. Conexiunile de la acest port interior la periferice precum hard-disk (sau dischetă), tastatură și monitor nu sunt prezentate în diagramă.

Există trei magistrale (grupe de conductori electrici de fire foarte mici) în diagramă. Fiecare fir poate transfera un bit 1 sau bit 0. Magistrala de date, pentru transferul de opt biți la un moment dat (un impuls de ceas) către RAM și portul de intrare/ieșire (dispozitive de intrare/ieșire) este bidirecțională. Autobuzul de date are o lățime de opt biți.

Toate componentele sunt conectate la magistrala de adrese. Autobuzul de adrese este unidirecțional față de microprocesor. Există șaisprezece conductori pentru magistrala de adrese și fiecare transportă un bit (1 sau 0). Șaisprezece biți sunt trimiși într-un singur impuls de ceas.

Există magistrala de control. Unii dintre conductorii magistralei de control ar transfera câte un bit de la microprocesor la celelalte componente. Câteva linii de control transportă biții de la portul de intrare/ieșire (IO) la microprocesor.

Memoria computerului
RAM și ROM sunt considerate ca un singur ansamblu de memorie. Acest ansamblu este reprezentat schematic după cum urmează, unde numerele hexazecimale au prefixul „$”:


Fig 4.11 Dispunerea memoriei pentru computerul Commodore 64

RAM-ul este de la 0000 16 către DFFF 16 care este scris ca de la $0000 la $DFFF. Cu limbajul de asamblare 6502 µP, un număr hexazecimal este prefixat cu „$” și nu este sufixat (subscript) cu 16 sau H sau hex. Orice informație din RAM se oprește atunci când computerul este oprit. ROM-ul începe de la $E000 până la $FFFF. Are subrutine care nu se opresc atunci când computerul este oprit. Aceste subrutine sunt rutinele utilizate în mod obișnuit care ajută la programare. Programul utilizator le apelează (vezi capitolul următor).

Spațiul (octeți) de la $0200 la $D000 este pentru programele utilizatorului. Spațiul de la $D000 la $DFFF este pentru informații care sunt direct legate de periferice (dispozitive de intrare/ieșire). Aceasta face parte din sistemul de operare. Deci, sistemul de operare al computerului commodore-64 este în două părți principale: partea din ROM care nu se oprește niciodată și partea de la $D000 la $DFFF care se oprește când alimentarea este oprită. Aceste date IO (intrare/ieșire) trebuie să fie încărcate de pe un disc de fiecare dată când computerul este pornit. Astăzi, astfel de date sunt numite drivere periferice. Perifericele încep de la portul Input/Output Device prin conexiunile de pe placa de bază la porturile identificabile de pe suprafețele verticale ale computerului la care sunt conectate monitorul, tastatura etc. la și la perifericele în sine (monitor, tastatură etc. .).

Memoria este formată din 2 16 = locații de 65.536 de octeți. În formă hexazecimală, acestea sunt 10000 16 = 10000 H = 10000 hex = locații de 10.000 USD. În calcul, numărarea în baza doi, baza zece, baza șaisprezece etc. începe de la 0 și nu de la 1. Deci, prima locație este de fapt numărul locației de 0000000000000000 2 = 0 10 = 0000 16 = 0000 USD. În limbajul de asamblare 6502 µP, identificarea unei locații de adresă este prefixată cu $ și nu există sufix sau indice. Ultima locație este numărul locației 1111111111111111 2 = 65.535 10 = FFFF 16 = $FFFF și nu 10000000000000000 2 , sau 65.536 10 , sau 10000 16 , sau 10.000 USD. Cele 10000000000000000 2 , 65.536 10 , 10000 16 , sau $10000 oferă numărul total de locații de octeți.

Aici, 2 16 = 65.536 = 64 x 1024 = 64 x 2 10 = 64 Kbytes (Kilobytes). Sufixul 64 din numele Commodore-64 înseamnă 64KB de memorie totală (RAM și ROM). Un octet este de 8 biți, iar cei 8 biți vor intra într-o locație de un octet din memorie.

Cei 64 Kbytes de memorie sunt împărțiți în pagini. Fiecare pagină are 0100 16 = 256 10 locații de octeți. Primele 256 10 = primul 0100 16 locații este pagina 0. Al doilea este pagina 1, al treilea este pagina 2 și așa mai departe.

Pentru a adresa cele 65.536 de locații, sunt necesari 16 biți pentru fiecare locație (adresă). Deci, magistrala de adrese de la microprocesor la memorie este formată din 16 linii; o linie pentru un bit. Un bit este fie 1, fie 0.

Registrele 6502 µP
Un registru este ca celulele de octeți pentru o locație de memorie de octeți. 6502 µP are șase registre: cinci registre de 8 biți și un registru de 16 biți. Registrul pe 16 biți se numește Program Counter, care este abreviat PC. Acesta deține adresa de memorie pentru următoarea instrucțiune. Un program în limbaj de asamblare constă din instrucțiuni care sunt plasate în memorie. Sunt necesari șaisprezece (16) biți diferiți pentru a adresa o anumită locație de octet în memorie. La un anumit impuls de ceas, acești biți sunt trimiși la liniile de adresă de 16 biți ale magistralei de adrese pentru citirea unei instrucțiuni. Toate registrele pentru 6502 µP sunt reprezentate după cum urmează:


Fig. 4.12 Registre 6502 µP

Contorul de programe sau PC-ul poate fi văzut ca un registru de 16 biți în diagramă. Cei opt biți semnificativi mai mici sunt etichetați ca PCL pentru Program Counter Low. Cei opt biți semnificativi mai mari sunt etichetați ca PCH pentru Program Counter High. O instrucțiune în memorie pentru Commodore-64 poate consta din unul, doi sau trei octeți. Cei 16 biți din PC indică următoarea instrucțiune de executat, în memorie. Printre circuitele din microprocesor, două dintre ele sunt numite Unitate logică aritmetică și Decodor de instrucțiuni. Dacă instrucțiunea curentă care este procesată în µP (microprocesor) are o lungime de un octet, aceste două circuite măresc PC-ul pentru următoarea instrucțiune cu 1 unitate. Dacă instrucțiunea curentă care este procesată în µP are doi octeți, adică ocupă doi octeți consecutivi în memorie, aceste două circuite măresc PC-ul pentru următoarea instrucțiune cu 2 unități. Dacă instrucțiunea curentă care este procesată în µP are trei octeți, adică ocupă trei octeți consecutivi în memorie, aceste două circuite măresc PC-ul pentru următoarea instrucțiune cu 3 unități.

Acumulatorul „A” este un registru de uz general pe opt biți care stochează rezultatul majorității operațiilor aritmetice și logice.

Registrele „X” și „Y” sunt folosite fiecare pentru a număra pașii programului. Numărarea în programare începe de la 0. Deci, ele sunt numite ca registre index. Au alte câteva scopuri.

Deși registrul Stack Pointer, „S” are 9 biți, ceea ce este considerat un registru de opt biți. Conținutul său indică o locație de octeți în pagina 1 a memoriei cu acces aleatoriu (RAM). Pagina 1 începe de la octetul $0100 (256 10 ) la octetul $01FF (511 10 ). Când un program rulează, acesta trece de la o instrucțiune la următoarea instrucțiune consecutivă din memorie. Cu toate acestea, acest lucru nu este întotdeauna cazul. Există momente când sare dintr-o zonă de memorie în altă zonă de memorie pentru a continua să ruleze instrucțiunile acolo, consecutiv. Pagina 1 din RAM este folosită ca stivă. Stiva este o zonă mare de memorie RAM care are următoarele adrese pentru continuarea codului de unde există un salt. Codurile cu instrucțiuni de sărituri nu sunt în stivă; sunt în altă parte în memorie. Cu toate acestea, după ce instrucțiunile de salt-la sunt executate, adresele de continuare (nu segmentele de cod) sunt în stivă. Au fost împinși acolo ca urmare a instrucțiunilor de săritură sau ramificare.

Registrul de stare procesor de opt biți al lui P este un tip special de registru. Biții individuali nu sunt legați sau legați unul de altul. Fiecare bit de acolo se numește steag și este apreciat independent de ceilalți. Semnificațiile steagurilor sunt date în cele ce urmează, pe măsură ce este nevoie.

Primul și ultimul indice de bit pentru fiecare registru sunt indicați deasupra fiecărui registru în diagrama anterioară. Numărarea indicelui de biți (poziția) într-un registru începe de la 0 în dreapta.

Pagini de memorie în binar, hexazecimal și zecimal
Următorul tabel arată începutul paginilor de memorie în format binar, hexazecimal și zecimal:

Fiecare pagină are 1.0000.0000 2 număr de octeți care este același cu 100 H număr de octeți care este același cu 256 10 numărul de octeți. În diagrama de memorie anterioară, paginile sunt indicate mergând în sus de la pagina 0 și nu coborând așa cum este indicat în tabel.

Coloanele binare, hexazecimale și zecimale ale acestui tabel oferă adresele locației octeților de memorie în diferitele lor baze. Observați că pentru pagina zero, numai biții pentru octetul inferior sunt necesari pentru a introduce la codificare. Biții pentru octetul superior pot fi omiși, deoarece sunt întotdeauna zerouri (pentru pagina zero). Pentru restul paginilor, ar trebui folosiți biții pentru octetul mai mare.

Restul acestui capitol explică limbajul de asamblare 6502 µP folosind toate informațiile anterioare. Pentru a înțelege rapid limbajul, cititorul trebuie să adună și să scadă în baza șaisprezece în loc de baza zece. De fapt, ar trebui să fie baza a doua, dar calcularea în baza a doua este greoaie. Amintiți-vă că atunci când adăugați două numere în baza doi, o purtare este tot 1 ca în baza zece. Dar când scădem două numere din baza doi, un împrumut este doi și nu zece ca în baza zece. Atunci când se adună două numere în baza șaisprezece, un transport este tot 1 ca în baza zece. Dar când scădem două numere în baza șaisprezece, un împrumut este șaisprezece și nu zece ca în baza zece.

4.2 Instrucțiuni de transfer de date

Luați în considerare următorul tabel al instrucțiunilor de transfer de date în limbajul de asamblare pentru 6502 µP:

Când un octet (8-biți) este copiat dintr-o locație de octeți de memorie în Registrul Acumulator, Registrul X sau Registrul Y, se încarcă. Când un octet este copiat din oricare dintre aceste registre într-o locație de octet de memorie, acesta este transferat. Când un octet este copiat dintr-un registru în altul, acesta este încă transferat. În a doua coloană a tabelului, săgeata arată direcția copiei pentru un octet. Restul celor patru coloane arată diferite moduri de adresare.

O intrare în coloana modului de adresare este codul octet real pentru partea mnemonică corespunzătoare a instrucțiunii în hexazecimal. AE, de exemplu, este codul de octet real pentru LDX, care este de a încărca un octet din memorie în registrul X în modul de adresare absolută, cum ar fi AE 16 = 10101110 2 . Deci, biții pentru LDX într-o locație de octeți de memorie sunt 10101110.

Observați că pentru partea mnemonică LDX a instrucțiunii, există trei octeți posibili, care sunt A2, AE și A6 și fiecare este pentru un anumit mod de adresare. Când octetul care se încarcă în registrul X nu trebuie copiat dintr-o locație de octet de memorie, valoarea trebuie introdusă cu (chiar după) mnemonicul LDX din instrucțiune în hexazecimal sau zecimal. În acest capitol, astfel de valori sunt tastate în hexazecimal. Aceasta este o adresare imediată, deci octetul real din memorie pentru a reprezenta LDX este A2 16 = 10100010 2 si nu AE 16 care este egal cu 10101110 2 .

În tabel, toți octeții din titlurile modului de adresare sunt numiți coduri de operare, care este abreviat ca opcodes. Pot exista mai multe opcode pentru un mnemonic, în funcție de modul de adresare.

Notă: Cuvântul „încărcare” din unitatea de sistem computerizată poate avea două semnificații: se poate referi la încărcarea unui fișier de pe un disc în memoria computerului sau se poate referi la transferul unui octet dintr-o locație de octet de memorie într-un registru de microprocesor .

Există mai multe moduri de adresare decât cele patru din tabel pentru 6502 µP.

Dacă nu se specifică altfel, tot codul de programare al utilizatorului din acest capitol începe de la adresa 0200 16 care este începutul zonei de utilizator din memorie.

Memoria M și acumulatorul A

Memorie către acumulator

Adresare imediată
Următoarea instrucțiune stochează numărul FF 16 = 255 10 în acumulator:

LDA #$FF

„$” nu este folosit doar pentru a identifica o adresă de memorie. În general, este folosit pentru a indica faptul că următorul număr care urmează este hexazecimal. În acest caz, $FF nu este adresa niciunei locații de octeți de memorie. Este numărul 255 10 în hexazecimal. Baza 16 sau oricare dintre celelalte indice echivalente ale sale nu trebuie să fie scrise în instrucțiunile de limbaj de asamblare. „#” indică faptul că ceea ce urmează în continuare este valoarea care trebuie introdusă în registrul acumulatorului. Valoarea poate fi scrisă și în baza zece, dar asta nu se face în acest capitol. „#” înseamnă adresare imediată.

Un mnemonic are o oarecare asemănare cu expresia engleză corespunzătoare. „LDA #$FF” înseamnă încărcarea numărului 255 10 în acumulatorul A. Deoarece aceasta este o adresare imediată din tabelul anterior, LDA este A9 și nu AD sau A5. A9 în binar este 101010001. Deci, dacă A9 pentru LDA este în adresă $0200 în memorie, $FF este în $0301 = 0300 + 1 adresă. #$FF este tocmai operandul pentru mnemonicul LDA.

Adresare absolută
Dacă valoarea lui $FF este în locația $0333 din memorie, instrucțiunea anterioară este:

LDA 0333 USD

Rețineți absența lui #. În acest caz, absența lui # înseamnă că ceea ce urmează este o adresă de memorie și nu valoarea dobânzii (nu valoarea de pus în acumulator). Deci, codul operațional pentru LDA, de data aceasta, este AD și nu A9 sau A5. Operandul pentru LDA aici este adresa $0333 și nu valoarea $FF. $FF se află în locația de 0333 $, care este destul de departe. Instrucțiunea „LDA $0333” ocupă trei locații consecutive în memorie și nu două, așa cum a făcut ilustrația anterioară. „AD” pentru LDA este în locația de 0200 USD. Octetul inferior al 0333, care este 33, se află în locația $0301. Octetul mai mare de $0333, care este 03, se află în locația $0302. Aceasta este o mică endianitate care este folosită de limbajul de asamblare 6502. Limbajele de asamblare ale diferitelor microprocesoare sunt diferite.

Acesta este un exemplu de adresare absolută. $0333 este adresa locației care are $FF. Instrucțiunea constă din trei octeți consecutivi și nu include $FF sau locația reală a octetului acestuia.

Adresare zero-pagină

Să presupunem că valoarea $FF se află în locația de memorie $0050 din pagina zero. Locațiile octeților pentru pagina zero încep de la 0000 USD și se termină la 00FF USD. Acestea sunt 256 10 locații în total. Fiecare pagină a memoriei Commodore-64 este 256 10 lung. Observați că octetul mai mare este zero pentru toate locațiile posibile din spațiul de zero pagini din memorie. Modul de adresare cu pagini zero este același cu modul de adresare absolută, dar octetul mai mare de 00 nu este introdus în instrucțiune. Deci, pentru a încărca $FF din locația $0050 în acumulator, instrucțiunea modului de adresare fără pagini este:

LDA 50 USD

Cu LDA fiind A5 și nu A9 sau AD, A5 16 = 10100101 2 . Amintiți-vă că fiecare octet din memorie este de 8 celule și fiecare celulă adăpostește câte un pic. Instrucțiunea de aici constă din doi octeți consecutivi. A5 pentru LDA se află în locația de memorie de 0200 USD, iar adresa de 50 USD, fără octetul mai mare de 00, se află în locația de 0301 USD. Absența lui 00, care ar fi consumat un octet în memoria totală de 64K, economisește spațiul de memorie.

Acumulator la memorie

Adresare absolută
Următoarea instrucțiune copiază o valoare de octet, oricare ar fi aceasta, din acumulator în locația de memorie de $1444:

SUNT 1444 USD

Se spune că se transferă de la acumulator în memorie. Nu se încarcă. Încărcarea este invers. Octetul opcode pentru STA este 8D 16 = 10001101 2 . Această instrucțiune constă din trei octeți consecutivi în memorie. 8D 16 se află în locația de 0200 USD. Cei 44 16 din adresa de 1444 USD se află în locația de 0201 USD. Și 14 16 este în locația de 0202 USD – Little Endianness. Octetul real care este copiat nu face parte din instrucțiune. 8D și nu 85 pentru adresarea fără pagini (în tabel) sunt folosite aici pentru STA.

Adresare de pagină zero
Următoarea instrucțiune copiază o valoare de octet, oricare ar fi aceasta, din acumulator în locația de memorie de 0050 USD în pagina zero:

STA 0050 USD

Octetul opcode pentru STA aici este 85 16 = 10000101 2 . Această instrucțiune constă din doi octeți consecutivi în memorie. Cei 85 16 este în locație 0200 USD. Cei 50 16 din adresa $0050 se află în locația $0201. Problema endianității nu se pune aici deoarece adresa are doar un octet, care este octetul inferior. Octetul real care este copiat nu face parte din instrucțiune. 85 și nu 8D pentru adresarea de pagini zero sunt folosite aici pentru STA.

Nu are sens să folosești adresarea imediată pentru a transfera un octet de la acumulator într-o locație din memorie. Acest lucru se datorează faptului că valoarea reală, cum ar fi $FF, trebuie să fie citată în instrucțiunea de adresare imediată. Deci, adresarea imediată nu este posibilă pentru transferul unei valori de octet dintr-un registru din µP în orice locație de memorie.

Mnemonice LDX, STX, LDY și STY
LDX și STX sunt similare cu LDA și, respectiv, STA. Dar aici se folosește registrul X și nu registrul A (acumulator). LDY și STY sunt similare cu LDA și, respectiv, STA. Dar aici se folosește registrul Y și nu registrul A. Consultați Tabelul 4.21 pentru fiecare opcode în hexazecimal care corespunde unui anumit mnemonic și unui anumit mod de adresare.

Transferuri de la înregistrare la înregistrare
Cele două seturi anterioare de instrucțiuni din Tabelul 4.21 se ocupă de copierea memoriei/microprocesor-registru (transfer) și copiere a registrului/registrului (transfer). Instrucțiunile TAX, TXA, TAY, TYA, TSX și TXS fac copierea (transferul) din registrul din microprocesor în alt registru al aceluiași microprocesor.

Pentru a copia octetul de la A la X, instrucțiunea este:

IMPOZIT

Pentru a copia octetul de la X la A, instrucțiunea este:

TX

Pentru a copia octetul de la A la Y, instrucțiunea este:

MÂNĂ

Pentru a copia octetul de la Y la A, instrucțiunea este:

TYA

Pentru computerul Commodore 64, stiva este pagina 1 imediat după pagina 0 din memorie. Ca orice altă pagină, este formată din 25610 10 locații de octeți, de la $0100 la $01FF. În mod normal, un program se execută de la o instrucțiune la următoarea instrucțiune consecutivă din memorie. Din când în când, există un salt la un alt segment de cod de memorie (set de instrucțiuni). Zona stivei din memorie (RAM) are următoarele adrese de instrucțiuni de unde au rămas săriturile (sau ramurile) pentru continuarea programului.

Indicatorul de stivă „S” este un registru de 9 biți în 6502 µP. Primul bit (cel mai din stânga) este întotdeauna 1. Toate adresele locației octetului din pagina unu încep cu 1 urmat de 8 biți diferiți pentru cei 256 10 locații. Indicatorul stivei are adresa locației din pagina 1 care are adresa următoarei instrucțiuni pe care programul trebuie să o returneze și cu care trebuie să continue după executarea segmentului de cod curent (sărit la). Deoarece primul bit din toate adresele stivei (pagina unu) începe cu 1, registrul pointerului stivei trebuie să rețină doar cei opt biți rămași. La urma urmei, primul său bit, care este cel mai din stânga bit (al nouălea bit numărând din dreapta), este întotdeauna 1.

Pentru a copia octetul de la S la X, instrucțiunea este:

TSX

Pentru a copia octetul de la X la S, instrucțiunea este:

TXT

Instrucțiunile de la înregistrare la înregistrare nu acceptă niciun operand. Ele constau doar din mnemonic. Fiecare mnemonic are opcode în hexazecimal. Acesta este în modul de adresare implicită deoarece nu există niciun operand (fără adresă de memorie, fără valoare).

Notă: Nu există transfer (copie) de la X la Y sau de la Y la X.

4.3 Operații aritmetice

Circuitul, Unitatea logică aritmetică în 6502 µP, poate adăuga doar două numere de opt biți la un moment dat. Nu scade, nu se înmulțește și nu împarte. Următorul tabel prezintă codurile operaționale și modurile de adresare pentru operațiile aritmetice:

Notă: Toate mnemonicile pentru operații aritmetice și alte tipuri de operații (adică toate mnemonicile 6502) au un octet de cod de operație (op). Dacă există mai multe moduri de adresare pentru mnemonic, ar exista coduri operaționale diferite pentru același mnemonic: unul pentru fiecare mod de adresare. C, D și V din tabel sunt steagurile registrului de stare. Semnificațiile lor vor fi date mai târziu, pe măsură ce va fi nevoie.

Adăugarea numerelor nesemnate
Cu 6502 µP, numerele cu semn sunt numere de complement a doi. Numerele fără semn sunt numere pozitive obișnuite care încep de la zero. Deci, pentru un octet de opt biți, cel mai mic număr fără semn este 00000000 2 = 0 10 = 00 16 iar cel mai mare număr nesemnat este 11111111 2 = 255 10 = FF 16 . Pentru două numere fără semn, adunarea este:

A+M+C→A

Aceasta înseamnă că conținutul de 8 biți al acumulatorului este adăugat de unitatea logică aritmetică la un octet (8 biți) din memorie. După adăugarea lui A și M, transferul la al nouălea bit merge la celula flag de transport din registrul de stare. Orice bit de transport anterior dintr-o adăugare anterioară care se află încă în celula flag de transport din registrul de stare este, de asemenea, adăugat la suma lui A și M, făcând A+M+C→A. Rezultatul este pus înapoi în acumulator.

Dacă adăugarea dobânzii este:

A + M

Și nu este nevoie să adăugați nicio transportare anterioară, indicatorul de transport trebuie șters, care este făcut 0, astfel încât adăugarea să fie:

A+M+0→A la fel ca A+M→A

Notă: Dacă M se adaugă la A și are loc o transportare de 1 deoarece rezultatul este mai mare de 255 10 = 11111111 2 = FF 16 , acesta este un transport nou. Acest nou transport de 1 este trimis automat la celula flag de transport în cazul în care este nevoie de următoarea pereche de opt biți care trebuie însumat (altul A + M).

Cod pentru a adăuga doi opt biți nesemnați
00111111 2 +00010101 2 este la fel cu 3F 16 + 15 16 care este la fel cu 63 10 +21 10 . Rezultatul este 010101002 2 care este la fel cu 54 16 și 84 10 . Rezultatul nu depășește numărul maxim pentru opt biți, care este 255 10 = 11111111 2 = FF 16 . Deci, nu există nicio transportare rezultată a lui 1. Pentru a spune altfel, transportul rezultat este 0. Înainte de adăugare, nu există nicio transportare anterioară a lui 1. Cu alte cuvinte, transportul anterior este 0. Codul pentru a face această adăugare poate fi:

CLC
LDA#$3F
ADC #15$

Notă: În timp ce tastați limbajul de asamblare, tasta „Enter” de la tastatură este apăsată la sfârșitul fiecărei instrucțiuni. Există trei instrucțiuni în acest cod. Prima instrucțiune (CLC) șterge indicatorul de transport în cazul în care o adăugare anterioară are 1. CLC poate fi efectuată numai în modul de adresare implicită. Mnemonicul pentru modul de adresare implicită nu acceptă operand. Aceasta șterge celula de transport din registrul de stare al lui P. Ștergerea înseamnă a da bitul de 0 celulei flag de transport. Următoarele două instrucțiuni din cod folosesc modul de adresare imediată. Cu adresare imediată, există un singur operand pentru mnemonic, care este un număr (și nici o adresă de memorie, nici de registru). Deci, numărul trebuie să fie precedat de „#”. „$” înseamnă că numărul care urmează este hexazecimal.

A doua instrucțiune încarcă numărul 3F 16 în acumulator. Pentru cea de-a treia instrucțiune, circuitul unității aritmetice logice al µP preia transportul anterioară (șters) de 0 (forțat la 0) a celulei flag de transport, a registrului de stare și îl adaugă la 15 16 precum și la valoarea care este deja în 3F 16 acumulator și pune rezultatul complet înapoi în acumulator. În acest caz, rezultă o transportare de 0. ALU (Unitatea logică aritmetică) trimite (pune) 0 în celula flag de transport a registrului de stare. Registrul de stare procesor și registrul de stare înseamnă același lucru. Dacă a rezultat o transportare de 1, ALU trimite 1 la steag-ul de transport al registrului de stare.

Cele trei linii ale codului anterior trebuie să fie în memorie înainte de a fi executate. Codul operațional 1816 pentru CLC (adresare implicită) se află în locația de 0200 de octeți. Codul operațional A9 16 pentru LDA (adresarea imediată) se află în locația de 0201 octeți. Numărul 3F 10 se află în locația de 0202 octeți. Codul operațional 69 16 pentru LDA (adresare imediată) este în locația de 0203 octeți. Numărul 15 10 se află în locația de 0204 octeți.

Notă: LDA este o instrucțiune de transfer (încărcare) și nu o instrucțiune aritmetică (mnemonică).

Cod pentru a adăuga doi șaisprezece biți nesemnați
Toate registrele din 6502 µP sunt în esență registre de opt biți, cu excepția PC-ului (Program Counter) care este de 16 biți. Chiar și registrul de stare are o lățime de 8 biți, deși cei opt biți ai săi nu funcționează împreună. În această secțiune, se ia în considerare adăugarea a doi 16 biți nesemnați, cu o transportare de la prima pereche de opt biți la a doua pereche de opt biți. Purtarea de interes aici este transferul de la poziţia al optulea de biţi la poziţia a noua.

Fie numerele 0010101010111111 2 = 2ABF16 16 = 10.943 10 și 0010101010010101 2 = 2A95 16 = 10.901 10 . Suma este 0101010101010100 2 = 5554 16 = 21.844 10 .

Adăugarea acestor două numere nesemnate în baza doi este după cum urmează:

Următorul tabel arată aceeași adăugare cu transportul lui 1 de la al optulea bit la al nouălea bit, începând din dreapta:

În codificarea acestui lucru, cei doi octeți inferiori sunt adăugați mai întâi. Apoi, ALU (Unitatea logică aritmetică) trimite transportul lui 1 din poziţia al optulea de bit la poziţia a noua de bit, către celula flag de transport din registrul de stare. Rezultatul 0 1 0 1 0 1 0 0 fără transport merge la acumulator. Apoi, a doua pereche de octeți este adăugată cu transportul. Mnemonicul ADC înseamnă adăugarea automată cu transportul anterior. În acest caz, transportul anterior, care este 1, nu trebuie schimbat înainte de a doua adăugare. Pentru prima adăugare, deoarece orice transport anterioară nu face parte din această adăugare completă, trebuie să fie șters (facut 0).

Pentru adăugarea completă a celor două perechi de octeți, prima adăugare este:

A + M + 0 -> A

A doua adăugare este:

A + M + 1 -> A

Deci, indicatorul de transport trebuie șters (dată cu valoarea 0) chiar înainte de prima adăugare. Următorul program al cărui cititor trebuie să citească explicația care urmează utilizează modul de adresare absolută pentru această însumare:

CLC
LDA 0213 USD
ADC 0215 USD
; nicio compensare, deoarece este necesară valoarea indicatorului de transport
STA 0217 USD
LDA 0214 USD
ADC 0216 USD
STA 0218 USD

Rețineți că, cu limbajul de asamblare 6502, un punct și virgulă începe un comentariu. Aceasta înseamnă că în execuția programului, punctul și virgulă și tot ce se află în dreapta acestuia sunt ignorate. Programul care a fost scris anterior este într-un fișier text este salvat cu numele la alegerea programatorului și cu extensia „.asm”. Programul anterior nu este programul exact care merge în memorie pentru execuție. Programul corespunzător din memorie se numește program tradus, unde mnemonicii sunt înlocuiți cu opcodes (octeți). Orice comentariu rămâne în fișierul text în limbajul de asamblare și este eliminat înainte ca programul tradus să ajungă în memorie. De fapt, există două fișiere care sunt salvate astăzi pe disc: fișierul „.asm” și fișierul „.exe”. Fișierul „.asm” este cel din ilustrația anterioară. Fișierul „.exe” este fișierul „.asm” cu toate comentariile eliminate și toate mnemonicile înlocuite cu codurile lor operaționale. Când este deschis într-un editor de text, fișierul „.exe” este de nerecunoscut. Dacă nu se specifică altfel, în scopul acestui capitol, fișierul „.exe” este copiat în memorie începând cu locația 0200 USD. Acesta este celălalt sens al încărcării.

Cele două numere de 16 biți care urmează să fie adăugate ocupă patru octeți în memorie pentru adresare absolută: doi octeți per număr (memoria este o secvență de octeți). Cu adresare absolută, operandul la codul operațional este în memorie. Rezultatul însumării are o lățime de doi octeți și, de asemenea, trebuie să fie plasat în memorie. Aceasta dă un total de 6 10 = 6 16 octeți pentru intrări și ieșiri. Intrările nu sunt de la tastatură și ieșirea nu este de la monitor sau imprimantă. Intrările sunt în memorie (RAM), iar ieșirea (rezultatul însumării) se întoarce în memorie (RAM) în această situație.

Înainte ca un program să fie executat, versiunea tradusă trebuie să fie mai întâi în memorie. Privind codul programului anterior, se poate observa că instrucțiunile fără comentariu alcătuiesc 19 10 = 13 16 octeți. Deci, programul ia de la 0200 $ locații de octeți în memorie la 0200 $ + 13 $ - 1 $ = 0212 $ locații de octeți (începând de la 0200 $ și nu 0201 $ ceea ce implică – 1 $). Adăugarea celor 6 octeți pentru numerele de intrare și de ieșire face ca tot programul să se termine la $0212 + $6 = $0218. Durata totală a programului este de 19 16 = 25 10 .

Octetul inferior al augend-ului ar trebui să fie în adresa $0213, iar octetul mai mare al aceluiași augend ar trebui să fie în adresa $0214 - little endianness. În mod similar, octetul inferior al addend-ului ar trebui să fie în adresa $0215, iar octetul superior al aceluiași addend ar trebui să fie în adresa $0216 - little endianness. Octetul inferior al rezultatului (suma) ar trebui să fie în adresa $0217, iar octetul mai mare al aceluiași rezultat ar trebui să fie în adresa $0218 - little endianness.

Codul operațional 18 16 pentru CLC (adresare implicită) se află în locația octetului de $0200. Codul operațional pentru „LDA $0213”, adică AD 16 pentru LDA (adresare absolută), se află în locația octetului de $0201. Octetul inferior al agendei, care este 10111111, se află în locația octetului de memorie de $0213. Amintiți-vă că fiecare opcode ocupă un octet. Adresa „$0213” a „LDA $0213” se află în locațiile de octeți de $0202 și $0203. Instrucțiunea „LDA $0213” încarcă octetul inferior al agendei în acumulator.

Codul operațional pentru „ADC $0215”, adică 6D 16 pentru ADC (adresare absolută), se află în locația octetului de $0204. Octetul inferior al suplimentului, care este 10010101, se află în locația octetului de $0215. Adresa „$0215” a „ADC $0215” se află în locațiile de octeți de $0205 și $0206. Instrucțiunea „ADC $0215” adaugă octetul inferior al addend-ului la octetul inferior al augend-ului care este deja în acumulator. Rezultatul este plasat înapoi în acumulator. Orice transport după al optulea bit este trimis către flag-ul de transport al registrului de stare. Celula flag de transport nu trebuie ștearsă înainte de a doua adăugare a octeților mai mari. Acest transfer este adăugat automat la suma octeților mai mari. De fapt, un transfer de 0 este adăugat la suma octeților inferiori automat la început (echivalent cu nicio transportare adăugată) din cauza CLC.

Comentariul ia următoarele 48 10 = 30 16 octeți. Cu toate acestea, aceasta rămâne doar în fișierul text „.asm”. Nu ajunge în memorie. Este eliminat prin traducerea care este făcută de asamblator (un program).

Pentru următoarea instrucțiune care este „STA $0217”, codul operațional al STA care este 8D 16 (adresarea absolută) se află în locația octetului de $0207. Adresa „$0217” a lui „STA $0217” se află în locațiile de memorie de $0208 și $0209. Instrucțiunea „STA $0217” copiază conținutul de opt biți al acumulatorului în locația de memorie de $0217.

Octetul mai mare al agendei care este 00101010 se află în locația de memorie de $0214, iar octetul mai mare al agendei care este 00101010 este în locația octetului de $02 16 . Codul operațional pentru „LDA $0214”, care este AD16 pentru LDA (adresare absolută) se află în locația octetului de $020A. Adresa „$0214” a „LDA $0214” se află în locațiile de $020B și $020C. Instrucțiunea „LDA $0214” încarcă octetul superior al agendei în acumulator, ștergând tot ce se află în acumulator.

Opcode pentru „ADC $0216”, care este 6D 16 pentru ADC (adresare absolută) se află în locația octetului de $020D. Adresa „$0216” a „ADC 0216” se află în locațiile de octeți de $020E și $020F. Instrucțiunea „ADC $0216” adaugă octetul mai mare al addend-ului la octetul superior al augend-ului care este deja în acumulator. Rezultatul este plasat înapoi în acumulator. Dacă există un carry de 1, pentru această a doua adăugare, acesta este plasat automat în celula de transport a registrului de stare. Deși transportarea dincolo de al șaisprezecelea bit (stânga) nu este necesară pentru această problemă, este bine să verificați dacă a avut loc o transportare de 1 verificând dacă indicatorul de transport a devenit 1.

Pentru următoarea și ultima instrucțiune care este „STA $0218”, codul operațional al STA care este 8D16 (adresare absolută) se află în locația octetului de $0210. Adresa „$0218” a lui „STA $0218” se află în locațiile de memorie $0211 și $0212. Instrucțiunea „STA $0218” copiază conținutul de opt biți al acumulatorului în locația de memorie de $0218. Rezultatul adunării celor două numere de șaisprezece biți este 0101010101010100, cu octetul mai mic de 01010100 în locația de memorie de $0217 și octetul mai mare de 01010101 în locația de memorie de $0218 - little endianness.

Scădere
Cu 6502 µP, numerele cu semn sunt numere de complement a doi. Numărul de complement doi poate fi de opt biți, șaisprezece biți sau orice multiplu de opt biți. Cu complementul în doi, primul bit din stânga este bitul semn. Pentru un număr pozitiv, acest prim bit este 0 pentru a indica semnul. Restul biților formează numărul în mod normal. Pentru a obține complementul a doi a unui număr negativ, inversați toți biții pentru numărul pozitiv corespunzător și apoi adăugați 1 la rezultatul din capătul din dreapta.

Pentru a scădea un număr pozitiv dintr-un alt număr pozitiv, subtraendul este convertit într-un număr negativ de complement a doi. Apoi, minuendul și noul număr negativ sunt adăugate în mod normal. Deci, scăderea de opt biți devine:

În cazul în care transportul este presupus ca 1. Rezultatul în acumulator este diferența în complementul a doi. Deci, pentru a scădea două numere, trebuie setat indicatorul de transport (facut la 1).

Când scădeți două numere de șaisprezece biți, scăderea se face de două ori, ca și cu adăugarea a două numere de șaisprezece biți. Deoarece scăderea este o formă de adunare cu 6502 µP, la scăderea a două numere de șaisprezece biți, indicatorul de transport este setat o singură dată pentru prima scădere. Pentru a doua scădere, orice setare a steagului de transport se face automat.

Programarea scăderii pentru numere de opt biți sau numere de șaisprezece biți se face în mod similar cu programarea adunării. Cu toate acestea, steagul de transport trebuie setat chiar de la început. Mnemonicul pentru a face acest lucru este:

Scăderea cu numere pozitive de șaisprezece biți
Luați în considerare scăderea cu următoarele numere:

Această scădere nu implică complementul a doi. Deoarece scăderea în 6502 µP se face în complement a doi, scăderea în baza doi se face după cum urmează:

Rezultatul complementului doi este același cu rezultatul obținut din scăderea obișnuită. Cu toate acestea, rețineți că 1 care merge la poziția al șaptesprezecelea bit din dreapta este ignorat. Minuendul și subtraend sunt împărțite în doi biți de opt fiecare. Complementul celor doi de 10010110 al octetului inferior al subtraendului este determinat independent de octetul său superior și de orice transport. Complementul celor doi de 11101011 al octetului superior al subtraendului este determinat independent de octetul său inferior și de orice transport.

Cei 16 biți ai minuendului sunt deja în complementul în doi, începând cu 0 din stânga. Deci, nu are nevoie de nicio ajustare în biți. Cu 6502 µP, octetul inferior al minuendului fără nicio modificare este adăugat octetului inferior al complementului a doi al subtraendului. Octetul inferior al minuendului nu este convertit în complementul a doi deoarece cei șaisprezece biți ai întregului minuend trebuie să fie deja în complementul a doi (cu 0 ca prim bit în stânga). În această primă adăugare, se adaugă o transportare obligatorie de 1 datorită instrucțiunii 1=0 SEC.

În scăderea efectivă curentă, există o transportare de 1 (de adunare) de la al optulea bit la al nouălea bit (de la dreapta). Deoarece aceasta este efectiv scădere, orice bit care ar trebui să fie în flag-ul de transport în registrul de stare este completat (inversat). Deci, purtarea lui 1 devine 0 în steagul C. În cea de-a doua operație, octetul superior al minuendului este adăugat octetului de complement a doi superior al subtraendului. Se adaugă, de asemenea, bitul indicator de transport completat automat al registrului de stare (în acest caz este 0) (la octeții mai mari). Orice 1 care depășește al șaisprezecelea bit din dreapta este ignorat.

Următorul lucru este doar să codificați toată această schemă după cum urmează:

SEC
LDA 0213 USD
SBC 0215 USD
; nicio compensare, deoarece este necesară valoarea indicatorului de transport inversat
STA 0217 USD
LDA 0214 USD
SBC 0216 USD
STA 0218 USD

Amintiți-vă că, cu limbajul de asamblare 6502, un punct și virgulă începe un comentariu care nu este inclus în versiunea de program tradusă în memorie. Cele două numere de 16 biți pentru scădere ocupă patru octeți de memorie cu adresare absolută; doi pe număr (memoria este o serie de octeți). Aceste intrări nu sunt de la tastatură. Rezultatul însumării este de doi octeți și, de asemenea, trebuie să fie plasat în memorie într-un loc diferit. Această ieșire nu ajunge la monitor sau imprimantă; se duce la memorie. Acest lucru dă un total de 6 10 = 6 16 octeți pentru intrări și ieșiri care urmează să fie plasați în memorie (RAM).

Înainte ca un program să fie executat, acesta trebuie să fie mai întâi în memorie. Privind codul programului, se poate observa că instrucțiunile fără comentariu formează 19 10 = 13 16 octeți. Deoarece toate programele din acest capitol încep de la locația de memorie $0200, programul ia de la locația $0200 octet din memorie la locația $0200 + $13 – $1 = $0212 octet (începând de la $0200 și nu $0201). Acest interval nu include regiunea pentru octeții de intrare și de ieșire. Cele două numere de intrare iau 4 octeți, iar numărul de ieșire ia 2 octeți. Adăugarea celor 6 octeți pentru numerele de intrare și de ieșire face ca intervalul pentru programul care se termină la $0212 + $6 = $0218. Durata totală a programului este de 19 16 = 25 10 .

Octetul inferior al minuendului ar trebui să fie în adresa $0213, iar octetul mai mare al aceluiași minuend ar trebui să fie în adresa $0214 - little endianness. În mod similar, octetul inferior al subtraendului ar trebui să fie în adresa $0215, iar octetul mai mare al aceluiași subtrahend ar trebui să fie în adresa $0216 - little endianness. Octetul inferior al rezultatului (diferența) ar trebui să fie în adresa $0217, iar octetul mai mare al aceluiași rezultat ar trebui să fie în adresa $0218 - little endianness.

Opcode-ul 38 16 pentru SEC (adresare implicită) este în adresa de 0200 USD. Se presupune că toate programele din acest capitol încep de la locația de memorie de $0200, anulând orice program care ar fi fost acolo; cu excepția unor dispoziții contrare. Codul operațional pentru „LDA $0213”, adică AD 16 , pentru LDA (adresare absolută) se află în locația $0201 octet. Octetul inferior al minuendului, care este 10111111, se află în locația octetului de memorie de $0213. Amintiți-vă că fiecare opcode ocupă un octet. Adresa „$0213” a „LDA $0213” se află în locațiile de octeți de $0202 și $0203. Instrucțiunea „LDA $0213” încarcă octetul inferior al minuendului în acumulator.

Codul operațional pentru „SBC $0215”, adică ED 16 , pentru SBC (adresare absolută) este în locația $0204 octeți. Octetul inferior al subtraendului, care este 01101010, se află în locația $0215 octeți. Adresa „$0215” a „ADC $0215” se află în locațiile de octeți de $0205 și $0206. Instrucțiunea „SBC $0215” scade octetul inferior al subtraendului din octetul inferior al minuendului care este deja în acumulator. Aceasta este scăderea complementului a doi. Rezultatul este plasat înapoi în acumulator. Complementul (inversiunea) oricărei purtări după al optulea bit este trimis către flag-ul de transport al registrului de stare. Acest flag de transport nu trebuie șters înainte de a doua scădere cu octeții mai mari. Acest transfer este adăugat automat la scăderea octeților mai mari.

Comentariul ia următoarele 57 10 = 3916 16 octeți. Cu toate acestea, aceasta rămâne doar în fișierul text „.asm”. Nu ajunge în memorie. Este eliminat prin traducerea care este făcută de asamblator (un program).

Pentru următoarea instrucțiune, care este „STA $0217”, codul operațional al STA, adică 8D 16 (adresare absolută), se află în locația $0207 octeți. Adresa „$0217” a lui „STA $0217” se află în locațiile de memorie de $0208 și $0209. Instrucțiunea „STA $0217” copiază conținutul de opt biți al acumulatorului în locația de memorie de $0217.

Octetul mai mare al minuendului, care este 00101010, se află în locația de memorie de $0214, iar octetul mai mare al subtraendului, care este 00010101, este în locația octetului de $0216. Codul operațional pentru „LDA $0214”, adică AD 16 pentru LDA (adresare absolută), se află în locația $020A octet. Adresa „$0214” a „LDA $0214” se află în locațiile de $020B și $020C. Instrucțiunea „LDA $0214” încarcă octetul superior al minuendului în acumulator, ștergând orice se află în acumulator.

Codul operațional pentru „SBC $0216”, adică ED 16 pentru SBC (adresare absolută), se află în locația $020D octeți. Adresa „$0216” a „SBC $0216” se află în locațiile de octeți de $020E și $020F. Instrucțiunea „SBC $0216” scade octetul superior al subtraendului din octetul superior al minuendului (complementul a doi) care este deja în acumulator. Rezultatul este plasat înapoi în acumulator. Dacă există o purtare de 1 pentru această a doua scădere, complementul său este plasat automat în celula de transport a registrului de stare. Deși transportul dincolo de al șaisprezecelea bit (stânga) nu este necesar pentru această problemă, este bine să verificați dacă transportul de complement are loc prin verificarea indicatorului de transport.

Pentru următoarea și ultima instrucțiune, care este „STA $0218”, codul operațional al STA, adică 8D 16 (adresare absolută), se află în locația $0210 octeți. Adresa „$0218” a lui „STA $0218” se află în locațiile de memorie $0211 și $0212. Instrucțiunea „STA $0218” copiază conținutul de opt biți al acumulatorului în locația de memorie de $0218. Rezultatul scăderii cu cele două numere de șaisprezece biți este 0001010101010101 cu octetul mai mic de 01010101 în locația de memorie de $0217 și octetul mai mare de 00010101 în locația de memorie de $0218 – little endianness.

6502 µP are circuite doar pentru adunare și indirect pentru scăderea complementului a doi. Nu are circuite pentru înmulțire și împărțire. Pentru a face înmulțirea și împărțirea, trebuie scris un program în limbaj de asamblare cu detalii, inclusiv deplasarea produselor parțiale și a dividendelor parțiale.

4.4 Operații logice

În 6502 µP, mnemonicul pentru OR este ORA și mnemonicul pentru OR exclusiv este EOR. Observați că operațiile logice nu au adresarea implicită. Adresarea implicită nu acceptă operand. Fiecare dintre operatorii logici trebuie să ia doi operanzi. Primul se află în acumulator, iar al doilea este în memorie sau în instrucțiune. Rezultatul (8 biți) este înapoi în acumulator. Primul din acumulator fie este pus acolo printr-o instrucțiune imediată, fie este copiat din memorie cu adresare absolută. În această secțiune, numai adresarea paginii zero este folosită pentru ilustrare. Acești operatori logici sunt toți operatori pe biți.

ȘI
Următorul tabel ilustrează ȘI pe biți în binar, hexazecimal și zecimal:

Toate programele din acest capitol ar trebui să înceapă la locația octetului de memorie de $0200. Cu toate acestea, programele din această secțiune sunt în pagina zero, cu scopul de a ilustra utilizarea paginii zero fără octetul mai mare de 00000000 2 . ANDingul precedent poate fi codificat după cum urmează:

LDA #$9A ; nu din memorie – adresare imediată
ȘI #$CD ; nu din memorie – adresare imediată
STA $30; stochează 88 USD la 0030 USD la bază zero

SAU
Următorul tabel ilustrează OR pe biți în binar, hexazecimal și zecimal:

LDA #$9A ; nu din memorie – adresare imediată
ORA #$CD ; nu din memorie – adresare imediată
STA $30; stochează $ CF la 0030 $ pe bază zero

GRATUIT
Următorul tabel ilustrează XOR pe biți în binar, hexazecimal și zecimal:

LDA #$9A ; nu din memorie – adresare imediată
EOR #$CD ; nu din memorie – adresare imediată
STA $30; stochează 57 USD la 0030 USD la bază zero

4.5 Operații de schimbare și rotire

Mnemonicii și codurile operaționale pentru operatorii de schimbare și rotație sunt:

ASL: Schimbați la stânga cu un bit al acumulatorului sau al locației de memorie, inserând 0 în celula liberă din dreapta.

LSR: Deplasați la dreapta cu un bit al locației acumulatorului sau a memoriei, inserând 0 în celula din stânga liberă.
ROL: Rotiți un bit la stânga acumulatorului sau a locației de memorie, inserând bitul care este abandonat în stânga în celula liberă din dreapta.
ROR: Rotiți cu un bit la dreapta acumulatorului sau locației de memorie, inserând bitul care este eliminat din dreapta în celula liberă din stânga.

Pentru a face o schimbare sau o rotație cu acumulatorul, instrucțiunea este cam așa:

LSR A

Aceasta folosește un alt mod de adresare numit modul de adresare acumulator.

Pentru a face o schimbare sau o rotație cu o locație de memorie de octeți, instrucțiunea este cam așa:

ROR $2BCD

Unde 2BCD este locația de memorie.

Rețineți că nu există un mod de adresare imediată sau implicită pentru deplasare sau rotire. Nu există un mod de adresare imediată, deoarece nu are rost să schimbați sau să rotiți un număr care rămâne doar în instrucțiune. Nu există un mod de adresare implicit, deoarece proiectanții 6502 µP doresc ca doar conținutul acumulatorului (registru A) sau o locație de octet de memorie să fie deplasat sau rotit.

4.6 Modul de adresare relativă

Microprocesorul crește întotdeauna (cu 1, 2 sau 3 unități) Program Counter (PC) pentru a indica următoarea instrucțiune care urmează să fie executată. 6502 µP are o instrucțiune al cărei mnemonic este BVS, ceea ce înseamnă Branch on Overflow Set. PC-ul este format din doi octeți. Această instrucțiune face ca PC-ul să aibă o adresă de memorie diferită pentru următoarea instrucțiune care urmează să fie executată, care nu rezultă dintr-un increment normal. Face acest lucru prin adăugarea sau scăderea unei valori, numită offset, la conținutul computerului. Și astfel, computerul indică apoi o locație de memorie diferită (ramificată) pentru ca computerul să continue executarea de acolo. Offset-ul este un număr întreg de la -128 10 la +127 10 (complementul a doi). Deci, offset-ul poate face saltul să continue în memorie. Dacă este pozitiv sau în urmă în memorie, sau dacă este negativ.

Instrucțiunea BVS preia un singur operand, care este decalajul. BVS folosește adresarea relativă. Luați în considerare următoarea instrucțiune:

BVS 7F $

În baza doi, 7F H este 01111111 2 = 127 10 . Să presupunem că conținutul de pe computer pentru următoarea instrucțiune este de 0300 USD. Instrucțiunea BVS face ca $7F (un număr pozitiv deja în complement de doi) să fie adăugat la $0300 pentru a da $037F. Deci, în loc ca următoarea instrucțiune să fie executată în locația de memorie de $0300, este în locația de memorie de $037F (aproximativ o jumătate de pagină diferență).

Există și alte instrucțiuni de ramură, dar BVS este una foarte bună de utilizat pentru a ilustra adresa relativă. Adresarea relativă se ocupă de instrucțiunile de filială.

4.7 Adresarea indexată și adresarea indirectă separat

Aceste moduri de adresare permit modelului 6502 µP să gestioneze cantitățile enorme de date în perioade scurte de timp cu un număr redus de instrucțiuni. Există locații de 64 KB pentru întreaga memorie Comodore-64. Așadar, pentru a accesa orice locație de octet, de 16 biți, este nevoie de realizarea de doi octeți. Singura excepție de la necesitatea a doi octeți este pentru pagina zero, unde octetul mai mare de $00 este omis pentru a economisi spațiul ocupat de instrucțiune în memorie. Cu un mod de adresare non-page-zero, atât octeții mai mari, cât și cei mai mici ai adresei de memorie pe 16 biți sunt indicați în cea mai mare parte cumva.

Adresarea indexată de bază

Adresarea indexului absolut
Amintiți-vă că registrul X sau Y se numește registru index. Luați în considerare următoarea instrucțiune:

LDA C453,X

Să presupunem că valoarea lui 6 H este în registrul X. Rețineți că 6 nu este introdus nicăieri în instrucțiune. Această instrucțiune adaugă valoarea 6H la C453 H care face parte din instrucțiunea tastată în fișierul text care este încă de asamblat – C453 H + 6 H = C459 H . LDA înseamnă încărcarea unui octet la acumulator. Octetul care trebuie încărcat în acumulator provine de la adresa $C459. $C459, care este suma lui $C453 care este tastat cu instrucțiunea și 6 H care se găsește în registrul X devine adresa efectivă de la care provine octetul de încărcat în acumulator. Daca 6 H a fost în registrul Y, Y este tastat în loc de X în instrucțiune.

În instrucțiunea tastată, $C453 este cunoscut ca adresa de bază și 6 H în registrul X sau Y este cunoscut ca partea de numărare sau index pentru adresa efectivă. Adresa de bază se poate referi la orice adresă de octeți din memorie și la următorii 256 10 adresele pot fi accesate, presupunând că indexul început (sau numărătoarea) în registrul X sau Y este 0. Rețineți că un octet poate oferi un interval continuu de până la 256 10 numere (adică 00000000 2 la 11111111 2 ).

Deci, adresarea absolută adaugă tot ceea ce este deja pus (a fost pus de o altă instrucțiune) în registrul X sau Y la cele 16 adrese care sunt tastate cu instrucțiunea de a obține adresa efectivă. În instrucțiunea tastata, cele două registre de index se disting prin X sau Y care sunt tastate după virgulă. Fie X, fie Y este tastat; nu amândoua.

După ce tot programul este tastat într-un editor de text și salvat cu numele de fișier cu extensia „.asm”, asamblatorul, care este un alt program, trebuie să traducă programul tastat în ceea ce este (încărcat) în memorie. Instrucțiunea anterioară, care este „LDA $C453,X”, ocupă trei locații de octeți în memorie, și nu cinci.

Amintiți-vă că un mnemonic precum LDA poate avea mai mult de un cod operațional (octeți diferiți). Codul operațional pentru instrucțiunea care utilizează registrul X este diferit de codul operațional care utilizează registrul Y. Asamblatorul știe ce cod operațional să folosească pe baza instrucțiunii tastate. Codul operațional de un octet pentru „LDA $C453,X” este diferit de codul operațional de un octet pentru „LDA $C453,Y”. De fapt, codul operațional pentru LDA din „LDA $C453,X” este BD, iar codul operațional pentru LDA din „LDA $C453,9” este BD.

Dacă opcode pentru LDA se află în locația $0200 octeți. Apoi, adresa de 16 biți a lui $C453 ia locațiile de lângă octeți din memorie, care sunt $0201 și $0202. Octetul opcode specific indică dacă este implicat registrul X sau registrul Y. Și astfel, instrucțiunea de limbă asamblată care este „LDA $C453,X” sau „LDA $C453,Y” ocupă trei octeți consecutivi în memorie, și nu patru sau cinci.

Adresare indexată fără pagini
Adresarea indexului de pagină zero este ca adresarea indexului absolut descrisă anterior, dar octetul țintă trebuie să fie doar pe pagina zero (de la $0000 la $00FF). Acum, când aveți de-a face cu pagina zero, octetul mai mare care este întotdeauna 00 H pentru locațiile de memorie este de obicei evitată. Deci, se menționează în mod normal că pagina zero începe de la 00 USD la FF. Și astfel, instrucțiunea anterioară a „LDA $C453,X” este:

LDA 53,X USD

$C4, un octet mai mare care se referă la o pagină deasupra zeroului paginii, nu poate fi folosit în această instrucțiune, deoarece pune octetul țintă așteptat pentru a fi încărcat în octetul acumulat în afara și deasupra paginii zero.

Când valoarea introdusă în instrucțiune este adăugată la valoarea din registrul de index, suma nu ar trebui să dea un rezultat peste zero paginii (FF H ). Deci, este exclus să aveți o instrucțiune precum „LDA $FF, X” și o valoare precum FF H în registrul index deoarece FF H + FF H = 200 H care este locația primului octet (0200 USD) a paginii 2 (a treia pagină) din memorie, este la o distanță mare de pagina 0. Deci, cu adresarea indexată de pagini zero, adresa efectivă trebuie să se afle în pagina zero.

Adresarea indirectă

Salt Adresarea absolută
Înainte de a discuta despre adresarea absolută indirectă, este bine să ne uităm mai întâi la adresarea absolută JMP. Să presupunem că adresa care are valoarea interesului (octetul țintă) este 8765 USD. Acesta este de 16 biți constând din doi octeți: octetul superior care este 87 H iar octetul inferior care este 65 H . Deci, cei doi octeți pentru 8765 USD sunt introduși în computer (contor de programe) pentru următoarea instrucțiune. Ceea ce este introdus în programul (fișierul) în limbaj de asamblare este:

JMP 8765 USD

Programul care se execută în memorie sare de la orice adresă accesată la 8765 USD. Mnemonicul JMP are trei coduri operaționale care sunt 4C, 6C și 7C. Opcode-ul pentru această adresare absolută este 4C. Codul operațional pentru adresarea indirectă absolută JMP este 6C (consultați următoarele ilustrații).

Adresare indirectă absolută
Acesta este utilizat numai cu instrucțiunea de salt (JMP). Să presupunem că adresa care are octetul de interes (octetul țintă) este 8765 USD. Acesta este de 16 biți constând din doi octeți: octetul superior care este 87 H iar octetul inferior care este 65 H . Cu adresarea indirectă absolută, acești doi octeți sunt de fapt localizați în două locații consecutive de octeți în altă parte a memoriei.

Să presupunem că acestea sunt localizate în locațiile de memorie $0210 și $0211. Apoi, octetul inferior al adresei de interes, care este 65 H este în adresa $0210, iar octetul mai mare care este 87 H este la adresa $0211. Aceasta înseamnă că octetul de memorie mai mic de interes merge la o adresă consecutivă mai mică, iar octetul de memorie mai mare de interes merge la adresa consecutivă mai înaltă - little endianness.

Adresa de 16 biți se poate referi la două adrese consecutive din memorie. În această lumină, adresa $0210 se referă la adresele $0210 și $0211. Perechea de adrese $0210 și $0211 deține adresa finală (16 biți a doi octeți) a octetului țintă, cu octetul inferior de 65 H în $0210 și octetul mai mare de 87 H în 0211 USD. Deci, instrucțiunea de salt care este tastata este:

JMP (0210 USD)

Mnemonicul JMP are trei coduri operaționale care sunt 4C, 6C și 7C. Opcode-ul pentru adresarea indirectă absolută este 6C. Ceea ce este introdus în fișierul text este „JMP ($0210)”. Din cauza parantezelor, asamblatorul (traducătorul) folosește opcode 6C pentru JMP, și nu 4C sau 7C.

Cu adresarea indirectă absolută, există de fapt trei regiuni de memorie. Prima regiune poate consta din locațiile de octeți de $0200, $0201 și $0202. Acesta are cei trei octeți pentru instrucțiunea „JMP ($0210)”. A doua regiune, care nu este neapărat lângă prima, constă din cele două locații consecutive de octeți de $0210 și $0211. Este octetul inferior de aici ($0210) care este introdus în instrucțiunile programului în limbaj de asamblare. Dacă adresa de interes este 8765 USD, octetul inferior este 65 H se află în locația $0210 octeți, iar octetul mai mare de 87 H se află în locația $0211 octeți. A treia regiune constă dintr-un singur octet. Este adresa de 8765 USD pentru octetul vizat (octetul final de interes). Perechea de adrese consecutive, $0210 și $0211, deține indicatorul $8765 care este adresa de interes. După interpretarea de calcul, este de 8765 USD care intră în PC (Contor de programe) pentru a accesa octetul țintă.

Adresare indirectă fără pagină
Această adresare este aceeași cu adresarea indirectă absolută, dar indicatorul trebuie să fie în pagina zero. Adresa de octet inferior a regiunii pointerului este ceea ce este în instrucțiunea tastată, după cum urmează:

JMP (50 USD)

Octetul mai mare al indicatorului se află în locația de $51 octeți. Adresa efectivă (punctată) nu trebuie să fie în pagina zero.

Deci, cu adresarea indexului, valoarea dintr-un registru index este adăugată la adresa de bază care este dată în instrucțiunea pentru a avea adresa efectivă. Adresarea indirectă utilizează un pointer.

4.8 Adresare indirectă indexată

Adresare indirectă indexată absolută
Acest mod de adresare este utilizat numai cu instrucțiunea JMP.
Cu adresarea indirectă absolută, există valoarea punctată (octetul) cu propriile sale două adrese consecutive de octeți. Aceste două adrese consecutive formează indicatorul pentru a fi în regiunea pointerului a doi octeți consecutivi din memorie. Octetul inferior al regiunii pointerului este ceea ce este introdus în instrucțiunea între paranteze. Pointerul este adresa valorii punctate. În situația anterioară, 8765 USD este adresa valorii punctate. $0210 (urmat de $0211) este adresa al cărei conținut este $8765, care este indicatorul. Cu modul de adresare indirectă absolută, este ($0210) care este introdus în program (fișier text), inclusiv parantezele.

Pe de altă parte, cu modul de adresare indirectă indexată absolută, octetul de adresă inferior pentru regiunea pointerului este format prin adăugarea valorii din registrul X la adresa tastată. De exemplu, dacă indicatorul se află în locația adresei $0210, instrucțiunea tastată poate fi cam așa:

JMP ($020A,X)

Unde registrul X are valoarea 6 H . 020A H + 6 H = 0210 H . Registrul Y nu este utilizat cu acest mod de adresare.

Adresare indirectă indexată fără pagini
Acest mod de adresare folosește registrul X și nu registrul Y. Cu acest mod de adresare, există încă valoarea ascuțită și indicatorul în regiunea sa de indicator de adresă de doi octeți. Trebuie să existe doi octeți consecutivi în pagina zero pentru indicator. Adresa care este introdusă în instrucțiune este o adresă de un octet. Această valoare este adăugată la valoarea din registrul X și orice transport este eliminat. Rezultatul indică către regiunea pointerului din pagina 0. De exemplu, dacă adresa de interes (indicată) este $8765 și se află în locațiile octeților de $50 și $51 din pagina 0, iar valoarea din registrul X este $30, instrucțiunile tastate sunt cam așa:

LDA (20 USD)

Pentru că 20 USD + 30 USD = 50 USD.

Adresare indexată indirectă
Acest mod de adresare folosește registrul Y și nu registrul X. Cu acest mod de adresare, există încă valoarea punctată și regiunea pointerului, dar conținutul regiunii pointerului funcționează diferit. Trebuie să existe doi octeți consecutivi în pagina zero pentru regiunea pointerului. Adresa inferioară a regiunii pointerului este introdusă în instrucțiune. Acest număr (pereche de octeți) care este conținut în regiunea pointerului este adăugat la valoarea din registrul Y pentru a avea pointerul real. De exemplu, să fie adresa de interes (îndreptată) să fie 8765 USD, valoarea lui 6H să fie în registrul Y, iar numărul (doi octeți) să fie la adresa de 50 H și 51 H . Cei doi octeți împreună sunt $875F, deoarece $875F + $6 = $8765. Instrucțiunea tastata este cam așa:

LDA (50 USD), Y

4.9 Instrucțiuni pentru creșterea, descreșterea și testarea BIT-urilor

Următorul tabel prezintă operațiile instrucțiunilor de creștere și decrementare:

INA și DEA cresc și, respectiv, decrementează acumulatorul. Aceasta se numește adresare la acumulator. INX, DEX, INY și DEY sunt pentru registrele X și, respectiv, Y. Nu iau niciun operand. Deci, folosesc modul de adresare implicit. Creștere înseamnă adăugarea a 1 la octetul de registru sau de memorie. Decrementare înseamnă scăderea a 1 din octetul de registru sau de memorie.

INC și DEC incrementează și decrementează un octet de memorie, respectiv (și nu un registru). Utilizarea adresei de pagină zero în locul adresei absolute înseamnă economisirea memoriei pentru instrucțiune. Adresarea paginii zero este cu un octet mai mică decât adresarea absolută pentru instrucțiunea din memorie. Cu toate acestea, modul de adresare a paginii zero afectează numai pagina zero.

Instrucțiunea BIT testează biții unui octet din memorie cu cei 8 biți din acumulator, dar nu modifică niciunul. Sunt setate doar unele marcaje ale Registrului de stare procesor „P”. Biții locației de memorie specificate sunt logic AND cu cei ai acumulatorului. Apoi, următorii biți de stare sunt setați:

  • N care este bitul 7 și ultimul bit (stânga) al registrului de stare, primește bitul 7 al locației de memorie înainte de ANDing.
  • V care este bitul 6 al registrului de stare primește bitul 6 al locației de memorie înainte de ANDing.
  • Steagul Z al registrului de stare este setat (fac 1) dacă rezultatul AND este zero (00000000 2 ). În caz contrar, se șterge (se face 0).

4.10 Comparați instrucțiunile

Mnemonicele de comparare a instrucțiunilor pentru 6502 µP sunt CMP, CPX și CPY. După fiecare comparație, steagurile N, Z și C ale registrului de stare a procesorului „P” sunt afectate. Steagul N este setat (fac 1) atunci când rezultatul este un număr negativ. Indicatorul Z este setat (fac 1) când rezultatul este zero (000000002). Steagul C este setat (facut 1) atunci când există un transfer de la bitul opt la al nouălea. Următorul tabel oferă o ilustrare detaliată

Înseamnă „mai mare decât”. Prin urmare, tabelul de comparație ar trebui să se explice de la sine.

4.11 Instrucțiuni de salt și ramificare

Următorul tabel rezumă instrucțiunile de salt și ramificare:

Instrucțiunea JMP utilizează adresarea absolută și indirectă. Restul instrucțiunilor din tabel sunt instrucțiuni de ramuri. Folosesc doar adresarea relativă cu 6502 µP. Cu asta, tabelul devine explicit dacă este citit de la stânga la dreapta și de sus în jos.

Rețineți că ramurile pot fi aplicate numai adreselor cuprinse între -128 și +127 de octeți de la adresa dată. Aceasta este o adresare relativă. Atât pentru instrucțiunile JMP, cât și pentru ramuri, contorul de programe (PC) este direct afectat. 6502 µP nu permite ramurilor la o adresă absolută, deși saltul poate face adresarea absolută. Instrucțiunea JMP nu este o instrucțiune de ramură.

Notă: Adresarea relativă este utilizată numai cu instrucțiuni de ramificație.

4.12 Zona stivei

O subrutină este ca unul dintre programele scurte anterioare pentru a adăuga două numere sau a scădea două numere. Zona stivei din memorie începe de la $0100 la $01FF inclusiv. Această zonă se numește pur și simplu stiva. Când microprocesorul execută un salt la instrucțiunea de subrutină (JSR – consultați următoarea discuție), trebuie să știe unde să revină când a terminat. 6502 µP păstrează aceste informații (adresa de retur) în memoria scăzută de la $0100 la $01FF (zona stivei) și utilizează conținutul registrului de pointer al stivei care este „S” în microprocesor ca indicator (9 biți) la ultima adresă returnată. care este stocat în pagina 1 ($0100 până la $01FF) a memoriei. Stack-ul crește de la $01FF și face posibilă imbricarea subrutinelor până la 128 de niveluri adânc.

O altă utilizare a indicatorului de stivă este de a gestiona întreruperi. 6502 µP are pinii etichetați ca IRQ și NMI. Este posibil ca unele semnale electrice mici să fie aplicate acestor pini și să facă ca 6502 µP să renunțe la executarea unui program și să-l facă să înceapă să execute altul. În acest caz, primul program este întrerupt. La fel ca subrutinele, segmentele de cod de întrerupere pot fi imbricate. Procesarea întreruperii este discutată în capitolul următor.

Notă : pointerul de stivă are 8 biți pentru adresa octetului inferior în adresarea locațiilor de la $0100 la $01FF. Octetul mai mare de 00000001 2 este asumat.

Următorul tabel oferă instrucțiuni care leagă indicatorul de stivă „S” cu registrele A, X, Y și P la zona stivei din memorie:

4.13 Apel și întoarcere subrutine

O subrutină este un set de instrucțiuni care realizează un anumit obiectiv. Programul de adunare sau scădere anterior este o subrutină foarte scurtă. Subrutinele sunt uneori numite doar rutine. Instrucțiunea de a apela o subrutină este:

JSR: Salt la subrutină

Instrucțiunea de a reveni dintr-o subrutină este:

RTS: Întoarcere din subrutină

Microprocesorul are tendința de a executa continuu instrucțiunile din memorie, una după alta. Să presupunem că microprocesorul execută în prezent un segment de cod și întâlnește o instrucțiune de salt (JMP) pentru a merge și a executa un segment de cod care este codificat în spatele căruia ar putea fi deja executat. Execută acel segment de cod din spate și continuă să execute toate segmentele de cod (instrucțiuni) după segmentul de cod din spate, până când re-execută din nou segmentul de cod curent și continuă mai jos. JMP nu împinge următoarea instrucțiune în stivă.

Spre deosebire de JMP, JSR împinge adresa următoarei instrucțiuni după sine de pe computer (contor de programe) în stivă. Poziția stivei acestei adrese este plasată în indicatorul stivei „S”. Când o instrucțiune RTS este întâlnită (execută) în subrutină, adresa care este împinsă pe stivă scoate stiva și programul se reia la acea adresă extrasă care este următoarea adresă de instrucțiune chiar înainte de apelul subrutinei. Ultima adresă care este eliminată din stivă este trimisă la contorul de programe. Următorul tabel oferă detalii tehnice ale instrucțiunilor JSR și RTS:

Consultați următoarea ilustrație pentru utilizările JSR și RTS:

4.14 Exemplu de buclă de numărătoare inversă

Următoarea subrutină numără inversă de la $FF la $00 (total de 256 10 conteaza):

începe LDX #$FF ; încărcați X cu $FF = 255
bucla DEX ; X = X – 1
bucla BNE; dacă X nu este zero, atunci mergi la buclă
RTS; întoarcere

Fiecare rând are un comentariu. Comentariile nu intră niciodată în memorie pentru execuție. Asamblatorul (translatorul) care convertește un program în ceea ce este în memorie pentru execuție (rulare) elimină întotdeauna comentariile. Un comentariu începe cu „;” . „Start” și „bucla” din acest program sunt numite etichete. O etichetă identifică (numele) pentru adresa instrucțiunii. Dacă instrucțiunea este o instrucțiune de un singur octet (adresare implicită), eticheta este adresa acelei instrucțiuni. Dacă instrucțiunea este o instrucțiune multiocteți, eticheta identifică primul octet pentru instrucțiunea multiocteți. Prima instrucțiune pentru acest program constă din doi octeți. Presupunând că începe de la adresa 0300 USD, adresa 0300 USD poate fi înlocuită cu „start” în jos în program. A doua instrucțiune (DEX) este o instrucțiune de un singur octet și ar trebui să fie la adresa $0302. Aceasta înseamnă că adresa $0302 poate fi înlocuită cu „buclă”, în jos în program, ceea ce este de fapt așa în „bucla BNE”.

„Bucla BNE” înseamnă ramificarea către adresa dată când steagul Z al registrului de stare este 0. Când valoarea din registrul A sau X sau Y este 00000000 2 , datorită ultimei operații, steagul Z este 1 (setat). Deci, în timp ce este 0 (nu 1), a doua și a treia instrucțiune din program sunt repetate în această ordine. În fiecare secvență repetată, valoarea (numărul întreg) din registrul X este micșorat cu 1. DEX înseamnă X = X – 1. Când valoarea din registrul X este $00 = 00000000 2 , Z devine 1. În acel moment, nu mai există repetarea celor două instrucțiuni. Ultima instrucțiune RTS din program, care este o instrucțiune de un singur octet (adresare implicită), revine din subrutină. Efectul acestei instrucțiuni este de a face adresa contorului de program în stivă pentru codul care urmează să fie executat înainte de apelul subrutinei și de a reveni la contorul de programe (PC). Această adresă este adresa instrucțiunii care urmează să fie executată înainte de apelarea subrutinei.

Notă: Când scrieți un program în limbaj de asamblare pentru 6502 µP, doar o etichetă trebuie să înceapă la începutul unei linii; orice alt cod de linie trebuie deplasat cu cel puțin un spațiu la dreapta.

Apelarea unei subrutine
Ignorând spațiul de memorie ocupat de etichetele anterioare, programul ia 6 octeți de locații consecutive în memorie (RAM) de la 0300 USD la 0305 USD. În acest caz, programul este:

LDX #$FF ; încărcați X cu $FF = 255
DEX ; X = X – 1
BNE 0302 USD; dacă X nu este zero, atunci mergi la buclă
RTS; întoarcere

Începând de la adresa $0200 din memorie poate fi apelul pentru subrutină. Instrucțiunea de apel este:

pornire JSR; începutul este adresa 0300 USD, adică JSR 0300 USD

Subrutina și apelul său care sunt scrise corect în fișierul editor de text este:

începe LDX #$FF; încărcați X cu $FF = 255
bucla DEX ; X = X – 1

bucla BNE; dacă X nu este zero, atunci mergi la buclă
RTS; întoarcere

JSR start: treceți la rutina începând de la 0300 USD

Acum, pot exista multe subrutine într-un program lung. Toate nu pot avea numele „start”. Ar trebui să aibă nume diferite. De fapt, niciunul dintre ei ar putea avea numele „start”. „Start” este folosit aici din motive de predare.

4.15 Traducerea unui program

Traducerea unui program sau asamblarea acestuia înseamnă același lucru. Luați în considerare următorul program:

start LDX #$FF : încărcați X cu $FF = 255
bucla DEX : X = X – 1
Bucla BNE: dacă X nu este zero, atunci mergi în buclă
RTS: întoarcere
JSR start: treceți la rutina începând de la 0300 USD

Acesta este programul care a fost scris anterior. Constă din subrutină, start și apelul la subrutină. Programul numără invers de la 255 10 la 0 10 . Programul începe la adresa de început a utilizatorului de 0200 USD (RAM). Programul este tastat într-un editor de text și este salvat pe disc. Are un nume precum „sample.asm” unde „sample” este numele la alegerea programatorului, dar extensia „.asm” pentru limbajul de asamblare trebuie să fie asociată cu numele fișierului.

Programul asamblat este produs de un alt program care se numește asamblator. Asamblatorul este furnizat de producătorul 6502 µP sau de o terță parte. Asamblatorul reproduce programul în așa fel încât acesta să fie în memorie (RAM) în timp ce se execută (rulează).

Să presupunem că instrucțiunea JSR începe la adresa $0200 și subrutina începe la adresa $0300. Asamblatorul elimină toate comentariile și spațiile albe. Comentariile și spațiile albe irosesc memoria care este întotdeauna rară. O posibilă linie goală între segmentul anterior de cod al subrutinei și apelul subrutinei este un exemplu de spațiu alb. Fișierul asamblat este încă salvat pe disc și este numit ceva de genul „sample.exe”. „Eșantionul” este numele ales de programator, dar extensia „.exe” ar trebui să fie acolo pentru a indica faptul că este un fișier executabil.

Programul asamblat poate fi documentat după cum urmează:

Se spune că producerea unui document ca acesta este asamblarea manuală. Rețineți că comentariile din acest document nu apar în memorie (pentru execuție). Coloana cu adresă din tabel indică adresele de început ale instrucțiunilor din memorie. Rețineți că „JSR start” care este „JSR $0300”, care este de așteptat să fie codificat ca „20 03 00”, este de fapt codificat ca „20 00 03”, cu adresa de octet de memorie inferioară luând octetul inferior din memorie și Adresa de octet de memorie mai mare care ia octetul mai mare din memorie – putina endianness. Codul operațional pentru JSR este 20 16 .

Rețineți că offset-ul unei instrucțiuni de ramificare, cum ar fi BNE, este un număr de complement a doi în intervalul 128 10 la + 127 10 . Deci, „bucla BNE” înseamnă „BNE -1 10 ” care este de fapt „D0 FF” în forma de cod a FF 16 este -1 în complementul a doi care se scrie ca = 11111111 în baza doi. Programul de asamblare înlocuiește etichetele și câmpurile cu numere hexazecimale reale (numerele hexazecimale sunt numere binare care sunt grupate în patru biți). Sunt incluse de fapt adresele reale de unde începe fiecare instrucțiune.

Notă: Instrucțiunea „Pornire JSR” este înlocuită cu instrucțiuni mai scurte care trimit conținutul curent (octeți mari și octeți mici) al contorului programului către stivă cu indicatorul de stivă care este decrementat de două ori (o dată pentru octetul mare și o dată pentru octetul mic) și apoi reîncarcă computerul cu adresa de 0300 USD. Indicatorul stivei indică acum $00FD, presupunând că este inițializat la $01FF.

De asemenea, instrucțiunea RTS este înlocuită cu un număr de instrucțiuni mai scurte care incrementează indicatorul de stivă „S” de două ori (o dată pentru octetul mic și o dată pentru octetul mare) și trage cei doi octeți corespunzători de adresă de la pointerul stivei la PC pentru următoarea instrucțiune.

Notă: Un text de etichetă nu trebuie să aibă mai mult de 8 caractere.

„Bucla BNE” folosește adresarea relativă. Înseamnă să adaugi -3 10 la următorul conținut de contor de program de 0305 USD. Octeții pentru „bucla BNE” sunt „D0 FD” unde FD este complementul a doi de -3 10 .

Notă: Acest capitol nu prezintă toate instrucțiunile pentru 6502 µP. Toate instrucțiunile și detaliile acestora pot fi găsite în documentul intitulat „Familia de microprocesoare SY6500 pe 8 biți”. Există un fișier PDF cu numele „6502.pdf” pentru acest document, care este disponibil gratuit pe Internet. 6502 µP care este descris în acest document este 65C02.

4.16 Întreruperi

Semnalele oricărui dispozitiv care este conectat la porturile externe (suprafața verticală) ale Commodore 64 trebuie să treacă prin circuitele CIA 1 sau CIA 2 (CI) înainte de a ajunge la microprocesorul 6502. Semnalele de la magistrala de date a 6502 µP trebuie să treacă fie prin cipul CIA 1, fie prin cipul CIA 2 înainte de a ajunge la orice dispozitiv extern. CIA înseamnă Complex Interface Adapter. În Fig 4.1 „Diagrama bloc a plăcii de bază Commodore_64”, dispozitivele bloc de intrare/ieșire reprezintă CIA 1 și CIA 2. Când un program rulează, acesta poate fi întrerupt pentru a rula o altă bucată de cod înainte de a continua. Există întrerupere hardware și întrerupere software. Pentru întrerupere hardware, există doi pini de semnal de intrare la 6502 µP. Numele acestor ace sunt IRQ și NMI . Acestea nu sunt linii de date µP. Liniile de date pentru uP sunt D7, D6, D5, D4, D3, D2, D1 și D0; cu D0 pentru bitul cel mai puțin semnificativ și D7 pentru bitul cel mai semnificativ.

IRQ reprezintă Interrupt ReQuest „activ” scăzut. Această linie de intrare la µP este în mod normal ridicată, la aproximativ 5 volți. Când scade la aproximativ 0 volți, aceasta este o solicitare de întrerupere care semnalează µP. De îndată ce cererea este acceptată, linia se întoarce la sus. Acordarea cererii de întrerupere înseamnă că µP se ramifică la codul (subrutină) care se ocupă de întrerupere.

NMI reprezintă întrerupere non-mascabilă „activ” scăzut. În timp ce codul pentru IRQ se execută NMI poate scădea. În acest caz, NMI este gestionat (este executat propriul cod). După aceea, codul pentru IRQ continuă. După codul pentru IRQ se încheie, codul programului principal continuă. Acesta este, NMI întrerupe IRQ manipulator. Semnalul pentru NMI poate fi încă dat µP chiar și atunci când µP este inactiv și nu gestionează nimic sau nu rulează un program principal.

Notă: Este de fapt trecerea de la mare la scăzut, de NMI , asta este NMI semnal – mai multe despre asta mai târziu. IRQ în mod normal provine de la CIA 1 şi NMI de obicei provine de la CIA 2. NMI , care înseamnă întrerupere non-mascabilă, poate fi considerată întrerupere non-stopable.

Gestionarea întreruperilor
Fie că cererea este de la IRQ sau NMI , instrucțiunea curentă trebuie să fie completată. 6502 are doar registrele A, X și Y. În timp ce o subrutină funcționează, este posibil să folosească aceste trei registre împreună. Un handler de întrerupere este încă o subrutină, deși nu este văzută ca atare. După ce instrucțiunea curentă este finalizată, conținutul registrelor A, X și Y pentru 65C02 µP este salvat în stivă. Adresa următoarei instrucțiuni a Contorului de programe este de asemenea trimisă în stivă. Apoi µP se ramifică la codul pentru întrerupere. După aceea, conținutul registrelor A, X și Y este apoi restaurat din stivă în ordinea inversă la care sunt trimise.

Exemplu de codificare pentru o întrerupere
Pentru simplitate, presupuneți că rutina pentru µP IRQ întrerupere este doar pentru a adăuga numerele $01 și $02 și pentru a salva rezultatul lui $03 la adresa de memorie de $0400. Codul este:

ISR PHA
PHX
PHY
;
LDA #$01
ADC #$02
EI COST 0400 USD
;
PLY
PLX
PLA
RTI

ISR este o etichetă și identifică adresa de memorie unde se află instrucțiunea PHA. ISR înseamnă rutină de întrerupere a serviciului. PHA, PHX și PHY trimit conținutul registrelor A, X și Y la stivă cu speranța că va fi nevoie de orice cod (program) care rulează chiar înainte de întrerupere. Următoarele trei instrucțiuni formează nucleul gestionarului de întreruperi. Instrucțiunile PLY, PLX și PLA trebuie să fie în această ordine și aduc înapoi conținutul registrelor Y, X și A. Ultima instrucțiune, care este RTI, (fără operand) returnează continuarea execuției la orice cod (program) care se execută înainte de întrerupere. RTI trage adresa următoarei instrucțiuni a codului care se execută din stivă înapoi la contorul de programe. RTI înseamnă Retur de la întrerupere. Cu asta, gestionarea întreruperilor (subrutinei) sa încheiat.

Întreruperea software-ului
Principala modalitate de a avea o întrerupere software pentru 6502 µP este utilizarea instrucțiunii de adresă implicită BRK. Să presupunem că programul principal rulează și întâlnește instrucțiunea BRK. Din acel moment, adresa următoarei instrucțiuni din PC ar trebui trimisă la stivă pe măsură ce instrucțiunea curentă este finalizată. O subrutină pentru a gestiona instrucțiunile software ar trebui să fie numită „următorul”. Această subrutină de întrerupere ar trebui să împingă conținutul registrului A, X și Y în stivă. După ce nucleul subrutinei este executat, conținutul registrelor A, X și Y ar trebui să fie tras înapoi din stivă în registrele lor de către subrutinele care se completează. Ultima afirmație din rutină este RTI. Conținutul PC-ului este, de asemenea, retras automat din stivă pe PC din cauza RTI.

Compararea și contrastarea subrutinei și a rutinei de serviciu întrerupere
Următorul tabel compară și pune în contrast subrutinele și rutina serviciului de întrerupere:

4.17 Rezumatul modurilor principale de adresare 6502

Fiecare instrucțiune pentru 6502 este de un octet, urmată de zero sau mai mulți operanzi.

Modul de adresare imediată
Cu modul de adresare imediată, după operand, este valoarea și nu o adresă de memorie. Valoarea trebuie să fie precedată de #. Dacă valoarea este în hexazecimal, „#” trebuie să fie urmat de „$”. Instrucțiunile de adresare imediată pentru 65C02 sunt: ​​ADC, AND, BIT, CMP, CPX, CPY, EOR, LDA, LDX, LDY, ORA, SBC. Cititorul ar trebui să consulte documentația pentru 65C02 µP pentru a ști cum să folosească instrucțiunile care sunt enumerate aici și care nu sunt explicate în acest capitol. Un exemplu de instrucțiune este:

LDA # 77 USD

Modul de adresare absolută
În modul de adresare absolută, există un singur operand. Acest operand este adresa valorii din memorie (de obicei în hexazecimal sau o etichetă). Sunt 64K 10 = 65.536 10 adrese de memorie pentru 6502 µP. De obicei, valoarea de un octet se află la una dintre aceste adrese. Instrucțiunile absolute de adresare pentru 65C02 sunt: ​​ADC, AND, ASL, BIT, CMP, CPX, CPY, DEC, EOR, INC, JMP, JSR, LDA, LDX, LDY, LSR, ORA, ROL, ROR, SBC, STA , STX, STY, STZ, TRB, TSB. Cititorul ar trebui să consulte documentația pentru 65C02 µP pentru a ști cum să folosească instrucțiunile care sunt enumerate aici, precum și pentru restul modurilor de adresare care nu sunt explicate în acest capitol. Un exemplu de instrucțiune este:

SUNT 1234 USD

Modul de adresare implicit
În modul de adresare implicită, nu există operand. Orice registru µP implicat este implicat de instrucțiune. Instrucțiunile de adresare implicite pentru 65C02 sunt: ​​BRK, CLC, CLD, CLI, CLV, DEX, DEY, INX, INY, NOP, PHA, PHP, PHX, PHY, PLA, PLP, PLX, PLY, RTI, RTS, SEC , SED, SEI, TAX, TAY, TSX, TXA, TXS, TYA. Un exemplu de instrucțiune este:

DEX: Decrementează registrul X cu o unitate.

Modul de adresare relativă
Modul de adresare relativă se ocupă numai de instrucțiuni de ramificație. În modul de adresare relativă, există un singur operand. Este o valoare de la -128 10 la +127 10 . Această valoare se numește offset. Pe baza semnului, această valoare este adăugată sau scăzută din următoarea instrucțiune a Contorului de programe pentru a rezulta adresa următoarei instrucțiuni intenționate. Instrucțiunile modului de adresă relativă sunt: ​​BCC, BCS, BEQ, BMI, BNE, BPL, BRA, BVC, BVS. Exemplele de instrucțiuni sunt:

BNE $7F : (ramură dacă Z = 0 în registrul de stare, P)

Care adaugă 127 la contorul programului curent (adresa de executat) și începe executarea instrucțiunii la adresa respectivă. În mod similar:

BEQ $F9 : (filiala dacă Z = : în registrul de stare, P)

Care adaugă un -7 la contorul de program curent și începe execuția la noua adresă a contorului de program. Operandul este un număr de complement a doi.

Adresare indexată absolută
În adresarea indexului absolut, conținutul registrului X sau Y este adăugat la adresa absolută dată (oriunde de la $0000 la $FFFF, adică de la 0 10 la 65536 10 ) pentru a avea adresa reală. Această adresă absolută dată se numește adresa de bază. Dacă se folosește registrul X, instrucțiunea de asamblare este cam așa:

LDA C453,X

Dacă se folosește registrul Y, este ceva de genul:

LDA 453 USD, Y

Valoarea pentru registrul X sau Y se numește valoarea de numărare sau index și poate fi oriunde de la 00 USD (0 10 ) la $FF (250 10 ). Nu se numește offset.

Instrucțiunile de adresare a indexului absolut sunt: ​​ADC, AND, ASL (numai X), BIT (cu acumulator și memorie, numai cu X), CMP, DEC (numai memorie și X), EOR, INC (numai memorie și X), LDA , LDX, LDY, LSR (numai X), ORA, ROL (numai X), ROR (numai X), SBC, STA, STZ (numai X).

Adresare indirectă absolută
Acesta este utilizat numai cu instrucțiunea de salt. Cu aceasta, adresa absolută dată are o adresă pointer. Adresa pointerului este formată din doi octeți. Pointerul de doi octeți indică (este adresa) valoarea octetului de destinație din memorie. Deci, instrucțiunea limbajului de asamblare este:

JMP (3456 USD)

Cu paranteze, și $13 este în locația adresei de $3456, în timp ce $EB este în locația adresei de $3457 (= $3456 + 1). Apoi, adresa de destinație este $13EB și $13EB este indicatorul. Valoarea absolută $3456 este între paranteze în instrucțiune, unde 34 este octetul inferior și 56 este octetul superior.

4.18 Crearea unui șir cu limbajul de asamblare 6502 µP

După cum se demonstrează în capitolul următor, după crearea unui fișier în memorie, fișierul poate fi salvat pe disc. Fișierul trebuie să primească un nume. Numele este un exemplu de șir. Există multe alte exemple de șiruri în programare.

Există două moduri principale de a crea un șir de coduri ASCII. În ambele moduri, toate codurile (caracterele) ASCII iau locații consecutive de octeți în memorie. Într-unul din moduri, această secvență de octeți este precedată de un octet întreg care este lungimea (numărul de caractere) din secvența (șir). Pe de altă parte, secvența de caractere este urmată (urmat imediat) de octetul nul care este 00 16 , adică 00 USD. Lungimea șirului (numărul de caractere) nu este indicată în alt mod. Caracterul Null nu este folosit în primul mod.

De exemplu, luați în considerare „te iubesc!” șir fără ghilimele. Lungimea aici este de 11; un spațiu contează ca un octet ASCII (caracter). Să presupunem că șirul trebuie să fie plasat în memorie, primul caracter fiind la adresa $0300.

Următorul tabel arată setarea memoriei șirurilor când primul octet este 11 10 = 0B 16 :

Următorul tabel arată setarea memoriei șirurilor când primul octet este „I” și ultimul octet este Null ($00):

Următoarea instrucțiune poate fi folosită pentru a începe crearea șirului:

EI COST 0300 USD

Să presupunem că primul octet se află în acumulatorul care urmează să fie trimis la adresa de 0300 USD. Această instrucțiune este valabilă pentru ambele cazuri (ambele tipuri de șiruri).

După potrivirea tuturor caracterelor în celulele de memorie, unul câte unul, șirul poate fi citit folosind o buclă. Pentru primul caz, se citește numărul de caractere după lungime. Pentru al doilea caz, caracterele sunt citite de la „I” până când este întâlnit caracterul Null care este „Null”.

4.19 Crearea unei matrice cu limbajul de asamblare 6502 µP

O matrice de numere întregi cu un singur octet constă din locații consecutive de octeți de memorie cu numerele întregi. Apoi, există un indicator care indică locația primului număr întreg. Deci, o matrice de numere întregi constă din două părți: indicatorul și seria de locații.

Pentru o matrice de șiruri, fiecare șir poate fi într-un loc diferit în memorie. Apoi, există locații consecutive de memorie cu pointeri în care fiecare pointer indică prima locație a fiecărui șir. Un pointer în acest caz este format din doi octeți. Dacă un șir începe cu lungimea sa, indicatorul corespunzător indică locația acelei lungimi. Dacă un șir nu începe cu lungimea sa, ci se termină cu un caracter nul, indicatorul corespunzător indică locația primului caracter al șirului. Și există un pointer care indică adresa de octet inferior a primului pointer de pointeri consecutivi. Deci, o matrice de șiruri de caractere constă din trei părți: șirurile din diferite locuri din memorie, pointerii consecutivi corespunzători și pointerul către primul pointer al indicatorilor consecutivi.

4.20 Probleme

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

  1. Scrieți un program în limbaj de asamblare care începe de la 0200 USD pentru 6502 µP și adaugă numerele nesemnate ale lui 2A94 H (adăugați) la 2ABF H (augend). Lăsați intrările și ieșirile să fie în memorie. De asemenea, produceți manual documentul de program asamblat.
  2. Scrieți un program în limbaj de asamblare care începe de la 0200 USD pentru 6502 µP și scade numerele fără semn ale lui 1569 H (subtraend) din 2ABF H (descăzut). Lăsați intrările și ieșirile să fie în memorie. De asemenea, produceți manual documentul de program asamblat.
  3. Scrieți un program în limbaj de asamblare pentru 6502 µP care numără până la 00 USD la 09 USD folosind o buclă. Programul ar trebui să înceapă de la 0200 USD. De asemenea, produceți manual documentul de program asamblat.
  4. Scrieți un program în limbaj de asamblare care începe de la 0200 USD pentru 6502 µP. Programul are două subrutine. Prima subrutină adaugă numerele nesemnate ale 0203 H (augend) și 0102H (addend). Al doilea subprogram adaugă suma din primul subprogram care este 0305H la 0006 H (augend). Rezultatul final este stocat în memorie. Apelați prima subrutină care este FSTSUB și a doua subrutină care este SECSUB. Lăsați intrările și ieșirile să fie în memorie. De asemenea, produceți manual documentul de program asamblat pentru întregul program.
  5. Având în vedere că an IRQ handler adaugă $ 02 la $ 01 la acumulator ca core handling while NMI este eliberat și nucleul de manipulare pentru NMI adaugă $05 până la $04 la acumulator, scrieți un limbaj de asamblare pentru ambii manipulatori, inclusiv apelurile lor. Apelul către IRQ administratorul ar trebui să fie la adresa de 0200 USD. The IRQ handlerul ar trebui să înceapă de la adresa de 0300 USD. The NMI handler ar trebui să înceapă de la adresa de 0400 USD. Rezultatul IRQ handler-ul ar trebui să fie pus la adresa de $0500, iar rezultatul NMI handler-ul trebuie pus la adresa de $0501.
  6. Explicați pe scurt modul în care instrucțiunea BRK este utilizată pentru a produce întreruperile software într-un computer 65C02.
  7. Produceți un tabel care compară și contrastează o subrutină normală cu o rutină de serviciu de întrerupere.
  8. Explicați pe scurt principalele moduri de adresare ale 65C02 µP, având în vedere exemplele de instrucțiuni ale limbajului de asamblare.
  9. a) Scrieți un program în limbajul mașină 6502 pentru a pune „te iubesc!” șir de coduri ASCII din memorie, începând cu adresa $0300 cu lungimea șirului. Programul ar trebui să înceapă de la adresa de 0200 USD. Obțineți fiecare caracter din acumulator unul câte unul, presupunând că sunt trimise acolo, printr-o subrutină. De asemenea, asamblați programul manual. (Dacă trebuie să cunoașteți codurile ASCII pentru „Te iubesc!”. Iată-le: „Eu”:49 16 , spațiu: 20 16 , „l”: 6C 16 , „o”:6F 16 , „în”:76 16 , „e”:65, „y”:79 16 , „în”:75 16 , și „!’:21 16 (Notă: fiecare cod ocupă 1 octet).
    b) Scrieți un program în limbajul mașină 6502 pentru a pune „te iubesc!” șir de coduri ASCII din memorie, începând cu adresa $0300 fără lungimea șirului, dar care se termină cu 00 16 . Programul ar trebui să înceapă de la adresa de 0200 USD. Obțineți fiecare caracter de la acumulator, presupunând că sunt trimise acolo, unul câte unul, de o subrutină. De asemenea, asamblați programul manual.