Adresní módy 6502

Procesor 6502 je, jak jsme si už řekli, velmi intenzivně závislý na práci s pamětí. Jeho instrukční sada není, jako u procesoru 8080, tvořena spoustou instrukcí, které se od sebe liší podle toho, jestli (například) čtou z paměti přímo adresované (LDA), nepřímo adresované dvojicí HL (MOV A,M) nebo nepřímo adresované dvojicemi BC / DE (LDAX). U procesoru 6502 je jediná instrukce, která se jmenuje LDA, a její funkce je: „Do registru A (akumulátoru) zkopíruj hodnotu operandu“.

Co je ale hodnotou operandu? To právě určuje onen adresní mód. Podle toho, jaký mód použijeme, tak se vyhodnotí operand.

IMMEDIATE (imm)

Tento mód říká, že operandem je ta hodnota, která je na následující pozici za operačním kódem. Instrukce, které používají tento mód, zabírají dva byty (první je operační kód, druhý hodnota)

V assembleru se použití tohoto módu označuje zapsáním znaku # před operand: LDA #23, LDA #0, LDA #23h, LDA #$5A

IMPLIED (imp)

Též „implicitní mód“. Instrukce samotná pracuje vždy se stejným operandem – registrem, hodnotou na zásobníku apod. Proto není potřeba žádný operand dodávat.

ABSOLUTE (abs)

Operandem je hodnota v paměti, která leží na adrese MMNNh, kde NN je byte, uvedený za operačním kódem na druhé pozici, MM na třetí pozici. Instrukce s absolutním adresováním tedy zabírají tři bajty. V assembleru se zapisuje tento mód plnou adresou: LDA 1234h, LDA $1234

ZERO PAGE (zp)

Operandem je hodnota v paměti, která leží na adrese 00NNh, kde NN je byte, uvedený za operačním kódem. Instrukce, které adresují zero page, mají dva bajty. Zapisují se stejně jako předchozí, tedy adresou: LDA 12h, LDA $12

Nojo, ale! Jak překladač pozná, jestli chci použít mód abs nebo zp, když napíšu „LDA 12h“? Co když mám na mysli 0012h a chci plnou adresu? A jak to pozná, když je adresa zadaná symbolicky, tedy „LDA promenna“? Adresa by měla být absolutní, ale když je „promenna“ v nulté stránce, může použít zp… a jak to zjistí, když třeba v tu chvíli hodnota ještě není známá? Řešení jsou různá. Assembler se může řídit podle toho, jak je hodnota zapsaná (0012h je abs, 12h je zp). Dál může použít speciální instrukci pro definici proměnných v zero page (EQU pro normální, EZP pro zp). Může použít speciální zápis, kterým natvrdo řekne, že vyžaduje adresaci ZP (např. pomocí hvězdičky: LDA *promenna). Může využít další průběh, ve kterém optimalizuje instrukce, které používají absolutní mód a jejichž operand je menší než 0100h…

Já jsem v ASM80 použil následující postup: Pokud je hodnota zapsaná přímo, nebo pokud je už dřív v kódu jednoznačně definovaná, tak se pro adresy mezi 0000h a 00FFh použije zero page. Pokud je adresa v tu chvíli ještě neznámá, používá se absolutní mód, ale lze vynutit zero page pomocí zápisu s hvězdičkou.

ABSOLUTE INDEXED (abx, aby)

Operandem je hodnota v paměti. Její adresa je získána tak, že se k adrese, zapsané v dvou následujících bajtech (jako u absolutního adresování) přičte hodnota registru X nebo Y. Výsledek je použit jako adresa do paměti. V assembleru se zapíše jako číslo, následované čárkou a znakem X (nebo Y): LDA 12h,X

ZERO PAGE INDEXED (zpx, zpy)

Operandem je hodnota v nulté stránce paměti. Její adresa je získána tak, že se k bajtu, následujícímu za operačním kódem, přičte hodnota registru X nebo Y. Výsledek se ořízne na osm bitů (pokud tedy vyjde např. 0106h, bude to 06h) a je použit jako adresa v zero page. V assembleru se zapíše jako číslo, následované čárkou a znakem X (nebo Y): LDA 12h,X

RELATIVE (rel)

Operandem je adresa, která je spočítána tak, že se k aktuální hodnotě registru PC přičte hodnota bajtu, který je zapsaný za operačním kódem. Jeho hodnota je brána jako číslo se znaménkem (00 = 0, 07Fh = +127, 080h = -128, 0FFh = -1). Toto adresování se používá u podmíněných skoků.

INDIRECT (ind)

Nepřímé adresování (indirect) spočívá v tom, že adresa toho místa, o které jde, je uložená v paměti na nějaké úplně jiné adrese, a ta je zadána. Instrukce, které využívají nepřímé adresování, zabírají tři bajty – první je operační kód, druhý je nižší a třetí vyšší bajt nepřímé adresy. Procesor vezme tuto nepřímou adresu (NA) a přečte si obsah dvou buněk (NA a NA+1) z paměti. Tento obsah dá dohromady cílovou (efektivní) adresu.

Nepřímé adresování může použít pouze instrukce JMP. Zapíše se pak např. jako JMP (1234h). V takovém případě vezme procesor obsah na adrese 1234h (řekněme, že to je 0DAh) a na adrese o 1 vyšší, tedy 1235h (řekněme, že tam je 0DEh). Tyto dvě hodnoty dají dohromady adresu 0DEDAh, a na tu se skočí.

Aby nebyly věci tak jednoduché, tak procesor 6502 obsahuje chybu, která způsobuje, že nepřímá adresa, končící na FFh (např. 12FFh), nevezme vyšší část adresy z 1300h, ale z 1200h. Ve skutečnosti totiž pouze přičte ke spodnímu bajtu 1, ale případný přenos do vyšší části adresy ignoruje.

INDEXED INDIRECT (izx)

Tento mód se v assembleru zapisuje jako (nn,X) – tedy podobně jako indexovaný mód, ale v závorkách. LDA (12h,X) vezme parametr (12h), k němu přičte hodnotu registru X a získá nepřímou adresu v zero page (podobně jako u zero page indexed). Na rozdíl od „zpx“ tím ale nic nekončí – z paměti na nepřímé adrese (nezapomeňte – v zero page!) jsou načteny dva bajty a z nich je složena cílová adresa.

Zůstaňme u instrukce LDA (12h,X) a řekněme, že v registru X je hodnota 3. Co se stane? Procesor vezme parametr 12h a k němu přičte obsah registru X (12h+03h = 15h), aby získal nepřímou adresu do nulté stránky (0015h). Z adresy 0015h načte nižší bajt výsledné adresy, z adresy 0016h vyšší bajt. Nakonec do registru A uloží obsah paměti na této výsledné adrese.

INDIRECT INDEXED (izy)

Tento mód je podobný předchozímu, ale liší se v pořadí operací „sečtu“ a „přečtu nepřímou adresu“. Zapisuje se (nn),Y. Vše osvětlí příklad, řekněme instrukce LDA (12h),Y. Instrukce vezme parametr (12h) a považuje ho za adresu v zero page. Z té načte dva bajty (resp. z adres 0012h a 0013h) a získá tak nepřímou adresu. K ní přičte hodnotu registru Y a výsledek je cílová adresa, ze které se přečte požadovaný bajt do registru A.

Uff.

Já vím. První setkání s adresními módy je pro nezvyklého člověka ukrutná nálož. První půlka ještě jde, ale nepřímé adresování s indexováním je už docela makačka na představivost. Nakonec se ale do toho vpravíte, nebojte – i když to není přímočaré jako u 8080.

Bohužel problém je, že tyto adresní módy nejsou úplně ortogonální. Instrukci LDA („ulož do registru A hodnotu“) můžete použít s módy IMM (bezprostředně zadaná hodnota), ABS (dvoubajtová adresa), ABX a ABY (dvoubajtová adresa s indexem v X, Y), ZP (jednobajtová adresa do zero page), ZPX (jednobajtová indexovaná adresa do ZP), ZPY… moment, ZPY použít nelze! Proč? No, prostě nelze. Zato můžete využít indexovaně-nepřímé IZX a IZY.

Navíc si všimněte, že je (nn,X), ale není (nn,Y) – na druhou stranu je (nn),Y, ale není (nn),X. Indexové registry nejsou tedy plně ortogonální.

Proto si budeme u každé instrukce ukazovat, jaké adresní módy s nimi lze využít.

Není 65 jako 02!

Ještě jednu věc je potřeba předeslat. 6502 má hnedle několik verzí, které se od sebe docela podstatně liší.

  • MOS6502 – „původní“, originální 6502, použitá např. v Commodore PET nebo KIM-1.
  • 6502C – 6502 s vývodem HALT (použitá v počítačích Atari)
  • 6510 – 6502 s přidaným portem, využitá v Commodore C64
  • 8500 – CMOS verze 6510 (Commodore C64C a C64G)
  • 8502 – rychlejší 8500 (až 2 MHz – C128)
  • 7501 – HMOS-1 verze 6502 (C16/C116/Plus4)
  • 8501 – HMOS-2 verze 6502

Tyto procesory jsou softwarově kompatibilní. Zajímavost je, že procesor, původně vyvinutý jako univerzální společností MOS Technology, se po koupi této společnosti firmou Commodore vyvíjel především tak, aby vyhovoval autorům domácích počítačů od Commodoru.

Na trhu se objevily i další procesory, odvozené z 6502:

  • 65C02 – neplést s 6502C! Tato verze přidala některé instrukce
  • 65SC02 – zmenšená verze 65C02, která má opět některé instrukce odebrané.
  • 65CE02 – rozšířená verze 65C02 (použita v počítači Commodore C65 – tento počítač jste pravděpodobně nikdy neviděli, jejich počet se odhaduje na 50 až 2000 kusů a na eBay se prodával jeden v dubnu 2013 za cenu přesahující 17.000 EUR, tedy cca půlmilion Kč.)
  • 65816 – hybridní procesor, který rozšiřuje 65C02 o šestnáctibitové instrukce.

Tyto procesory jsou „rozšířenou 6502“ a chovají se jinak, zejména u „nedokumentovaných“ instrukcí (některé z nich mají jiný význam, jiné se chovají jako NOP a nezaseknou celý procesor jako u 6502).

V následujícím popisu se budu věnovat té první skupině, tedy „originál 6502“.

Líbil se vám článek? Podpořte autora na Patreonu
Příspěvek byl publikován v rubrice 6502. Můžete si uložit jeho odkaz mezi své oblíbené záložky.

7 komentáře u Adresní módy 6502

  1. Roman Bórik napsal:

    S mikroprocesorom 6502 som sa nikdy nestretol a ani som ho nikdy nevyhľadával, takže som rád, že sa vďaka tomuto seriálu niečo o ňom dozviem.

    Neviem, ako je to pri 6502 riešené hardvérovo, ale čo sa týka absolútneho a zeropage adresovania, dovolil by som si tvrdiť, že inštrukcie LDA a8 a LDA a16 by mali byť pri a16 < 100h úplne ekvivalentné a teda v záujme šetrenia času vykonania i spotrebovanej pamäti, by mal prekladač automaticky zvoliť kratšiu alternatívu, teda zeropage adresovanie. Pripúšťam ale, že zápis s *a8 môže byť vhodný pre "čitateľnosť" a zároveň pre kontrolu na úrovni prekladu, či daný argument je menší ako 100h.

    Pri ABSOLUTE INDEXED zrejme nemá byť predposledná veta "Výsledek je použit jako adresa v zero page. ". Aspoň mi to nedáva zmysel, keďže adresa má byť v dvoch bytoch.

    • Martin Malý napsal:

      Za opravu díky. Ohledně ZP vs ABS: Představ si následující úryvek

      LDA promenna


      promenna EQU 12h

      Assembler při prvním průchodu ještě netuší, že promenna je <100h, takže ji překládá jako ABS. Při druhém průchodu už se ví, že je <100h, tak by ji měl přeložit jako ZP. Čímž se ale všechno, co je ZA touto adresou, posune o -1. Což je OK, jen je to potřeba celé přepočítat.

      A právě tenhle přepočet ještě nemám, ale už se na něm pracuje. 🙂

  2. Ondřej Súkup napsal:

    mám pocit že 65C02 byla použita např v Atari řady XE

Napsat komentář

Vaše emailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *