Hlavní navigace

Základy tvorby her pro herní konzoli NES: mikroprocesor 6502 a assembler ca65

7. 6. 2022
Doba čtení: 28 minut

Sdílet

 Autor: Depositphotos
Ve druhém článku o tvorbě her a grafických či zvukových dem pro osmibitovou herní konzoli NES se seznámíme s programátorským modelem mikroprocesoru MOS 6502 (resp. Ricoh 2A03.) a vysvětlíme si, jak vlastně skutečně pracuje kostra reálného programu pro NES.

Obsah

1. Základy tvorby her pro herní konzoli NES: mikroprocesor 6502 a assembler ca65

2. Hlavička obrazu ROM

3. Mapa paměti NESu

4. Realizace obsluhy přerušení pro všechny tři typy přerušení procesoru 6502

5. Programátorský model mikroprocesoru MOS 6502

6. Registry a příznakové bity mikroprocesoru MOS 6502

7. Adresovací režimy, využití registrů X a Y pro adresování

8. Aritmetické a logické instrukce

9. Skoky a rozvětvení

10. Instrukce pro přesuny dat

11. Manipulace s příznakovými bity

12. Zbývající instrukce

13. Kde si lze možnosti 6502 otestovat bez nutnosti pochopit strukturu NESu?

14. Druhý pohled na kostru programu

15. Nastavení stavu mikroprocesoru po resetu

16. Nastavení řídicích registrů

17. Trik pro čekání na zobrazení dalšího snímku

18. Trik pro vymazání obsahu RAM

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Základy tvorby her pro herní konzoli NES: mikroprocesor 6502 a assembler ca65

Na úvodní článek o vývoji her nebo grafických a zvukových dem pro slavnou osmibitovou herní konzoli NES dnes navážeme. Podrobněji si popíšeme dvě technologie, které je nutné alespoň do určité míry znát (prozatím nám však budou stačit skutečně jen základní znalosti). Konkrétně se jedná o osmibitový mikroprocesor MOS 6502 a taktéž o assembler, v našem případě konkrétně o assembler nazvaný ca65. Připomeňme si, že pro vývoj pro NES nám bude postačovat jen základní sada nástrojů (a nijak rozsáhlých či komplikovaných):

  1. Libovolný programátorský textový editor
  2. Assembler ca65
  3. Linker cl65
  4. Libovolný emulátor NESu (podporující formát INES – což dnes umí každý emulátor)

V závěru úvodního článku jsme si ukázali, jak vypadá kostra programu určeného pro NES zapsaná v assembleru. Jedná se o nepatrně delší kód, a to z toho prostého důvodu, že NES neobsahuje žádný operační systém ani BIOS, takže se programátor musí nejprve zabývat inicializací hardware – tedy především čipu PPU (grafika).

Celý kód, který jehož význam si postupně vysvětlíme (a vylepšíme), vypadá následovně:

; ---------------------------------------------------------------------
; Definice hlavičky obrazu ROM
; ---------------------------------------------------------------------
 
; Size of PRG in units of 16 KiB.
prg_npage = 1
 
; Size of CHR in units of 8 KiB.
chr_npage = 1
 
; INES mapper number.
mapper = 0
 
; Mirroring (0 = horizontal, 1 = vertical)
mirroring = 1
 
.segment "INES"
        .byte $4e, $45, $53, $1a
        .byte prg_npage
        .byte chr_npage
        .byte ((mapper & $0f) << 4) | (mirroring & 1)
        .byte mapper & $f0
.code
 
 
 
; ---------------------------------------------------------------------
; Blok paměti s definicí dlaždic 8x8 pixelů
; ---------------------------------------------------------------------
 
.segment "CHR0a"
.segment "CHR0b"
 
 
 
; ---------------------------------------------------------------------
; Programový kód rutin pro NMI, RESET a IRQ volaných automaticky CPU
;
; viz též https://www.pagetable.com/?p=410
; ---------------------------------------------------------------------
 
; Obslužná rutina pro NMI (nemaskovatelné přerušení, vertical blank)
 
.proc nmi
        rti                     ; návrat z přerušení
.endproc
 
 
 
; Obslužná rutina pro IRQ (maskovatelné přerušení)
 
.proc irq
        rti                     ; návrat z přerušení
.endproc
 
 
 
; Obslužná rutina pro RESET
 
.proc reset
        ; nastavení stavu CPU
        sei                     ; zákaz přerušení
        cld                     ; vypnutí dekadického režimu (není podporován)
 
        ldx #$ff
        txs                     ; vrchol zásobníku nastaven na 0xff (první stránka)
 
        ; nastavení řídicích registrů
        ldx #$00
        stx $2000               ; nastavení PPUCTRL = 0
        stx $2001               ; nastavení PPUMASK = 0
        stx $4015               ; nastavení APUSTATUS = 0
 
        ; čekání na vnitřní inicializaci PPU (dva snímky)
wait1:  bit $2002               ; test obsahu registru PPUSTATUS 
        bpl wait1               ; skok, pokud je příznak N nulový
wait2:  bit $2002               ; test obsahu registru PPUSTATUS 
        bpl wait2               ; skok, pokud je příznak N nulový
 
        ; vymazání obsahu RAM
        lda #$00                ; vynulování registru A
loop:   sta $000, x             ; vynulování X-tého bajtu v nulté stránce
        sta $100, x
        sta $200, x
        sta $300, x
        sta $400, x
        sta $500, x
        sta $600, x
        sta $700, x             ; vynulování X-tého bajtu v sedmé stránce
        inx                     ; přechod na další bajt
        bne loop                ; po přetečení 0xff -> 0x00 konec smyčky
 
        ; čekání na dokončení dalšího snímku, potom může začít herní smyčka
wait3:  bit $2002               ; test obsahu registru PPUSTATUS 
        bpl wait3               ; skok, pokud je příznak N nulový
 
        ; vlastní herní smyčka je prozatím prázdná
game_loop:
        jmp game_loop           ; nekonečná smyčka (později rozšíříme)
.endproc
 
 
 
; ---------------------------------------------------------------------
; Tabulka vektorů CPU
; ---------------------------------------------------------------------
 
.segment "VECTOR"
.addr nmi
.addr reset
.addr irq
 
 
 
; ---------------------------------------------------------------------
; Finito
; ---------------------------------------------------------------------

Důležitá je i konfigurace linkeru, tedy (zjednodušeně řečeno) mapování logických jmen segmentů na fyzické adresy a určení, které segmenty jsou zapisovatelné a které nikoli (tedy zda se jedná o RAM či o ROM):

MEMORY {
    ZP:     start = $0000, size = $0100, type = rw;
    RAM:    start = $0300, size = $0400, type = rw;
    HEADER: start = $0000, size = $0010, type = rw,
            file = %O, fill = yes;
    PRG0:   start = $8000, size = $4000, type = ro,
            file = %O, fill = yes;
    CHR0a:  start = $0000, size = $1000, type = ro,
            file = %O, fill = yes;
    CHR0b:  start = $1000, size = $1000, type = ro,
            file = %O, fill = yes;
}
 
SEGMENTS {
    ZEROPAGE: load = ZP, type = zp;
    BSS:    load = RAM, type = bss;
    HEADER: load = HEADER, type = ro, align = $10;
    CODE:   load = PRG0, type = ro;
    VECTORS: load = PRG0, type = ro, start = $BFFA;
    CHR0a:  load = CHR0a, type = ro;
    CHR0b:  load = CHR0b, type = ro;
}
Poznámka: jak jste si již mohli všimnout, uvozují se ve světě procesorů MOS 6502 hexadecimální hodnoty znakem dolaru, například $BFFA. Tohoto způsobu zápisu se budeme držet i v dnešním článku.

2. Hlavička obrazu ROM

Teoreticky je možné, aby assembler vygeneroval přesnou kopii paměti NESu. Tj. výsledkem by byl binární soubor o velikosti přesně 65536 bajtů obsahující jak RAM a ROM, tak i všechny řídicí registry (a opakující se bloky paměti). Ve skutečnosti se však dnes používá poměrně flexibilní formát INES, který obsahuje libovolné bloky paměti a jeho velikost je tak proměnlivá. Navíc podporuje mapování dalších paměťových bloků pro hry s větší ROM. Důležité pro nás je, že tento formát obsahuje hlavičku s důležitými informacemi, kterou musíme naplnit. Tato hlavička bude v assembleru reprezentována obsahem segmentu nazvaného „INES“ a obsahuje velikost ROM pro program, velikost ROM pro dlaždice (grafiku) atd.:

; ---------------------------------------------------------------------
; Definice hlavičky obrazu ROM
; ---------------------------------------------------------------------
 
; Size of PRG in units of 16 KiB.
prg_npage = 1
 
; Size of CHR in units of 8 KiB.
chr_npage = 1
 
; INES mapper number.
mapper = 0
 
; Mirroring (0 = horizontal, 1 = vertical)
mirroring = 1
 
.segment "INES"
        .byte $4e, $45, $53, $1a
        .byte prg_npage
        .byte chr_npage
        .byte ((mapper & $0f) << 4) | (mirroring & 1)
        .byte mapper & $f0
.code

Po překladu příkladu (viz úvodní článek) získáme binární soubor nazvaný ctnes.nes, jehož obsah si můžeme prohlédnout libovolným hexa prohlížečem. Standardním hexa prohlížečem na Linuxu je od (nenechte se zmást jeho jménem, které klame – jedná se i o hexa prohlížeč):

$ od -t x1 ctnes.nes

Důležitých je prvních šestnáct bajtů, které skutečně obsahují kýženou hlavičku:

0000000 4e 45 53 1a 01 01 01 00 00 00 00 00 00 00 00 00
0000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0040000 00 00 00 00 00 00 00 00 00 00 00 10 02 10 01 10
0040020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0050020 40 40 78 d8 a2 ff 9a a2 00 8e 00 20 8e 01 20 8e
0050040 15 40 2c 02 20 10 fb 2c 02 20 10 fb a9 00 95 00
0050060 9d 00 01 9d 00 02 9d 00 03 9d 00 04 9d 00 05 9d
0050100 00 06 9d 00 07 e8 d0 e6 2c 02 20 10 fb 4c 3d 10
0050120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*

3. Mapa paměti NESu

Pro vývoj pro NES je vhodné seznámit s mapou paměti, přesněji řečeno obsazení adres tak, jak je vidí mikroprocesor:

+-----------------------+ $FFFF
| Vektory NMI/RESET/IRQ |
+-----------------------+ $FFFA
|                       |
|                       |
|  Cartridge ROM (PRG)  |
|  32kB                 |
|                       |
|                       |
+-----------------------+ $8000
|                       |
|  Cartridge RAM (WRAM) |
|  8kB (+expansion ROM) |
|                       |
+-----------------------+ $4020
|  Řídicí registry APU  |
|  20B (jen 20 bajtů)   |
+-----------------------+ $4000
|  Řídicí registry PPU  |
|  8B (jen osm bajtů)   |
+-----------------------+ $2000
|                       |
|                       |
| (zrcadlená RAM)       |
|                       |
|                       |
+-----------------------+ $0800
|  Interní paměť RAM    |
|  2kB                  |
|.......................| $0200
|  Stack 256B           |
|.......................| $0100
|  Zero Page 256B       |
+-----------------------+ $0000

V nejvyšších šesti bajtech jsou uloženy vektory (adresy) přerušovacích subrutin volaných automaticky při detekci přerušení či při resetu (inicializaci) CPU:

$FFFA-$FFFB = NMI vector
$FFFC-$FFFD = Reset vector
$FFFE-$FFFF = IRQ/BRK vector

4. Realizace obsluhy přerušení pro všechny tři typy přerušení procesoru 6502

Mikroprocesor MOS 6502 podporuje tři typy přerušení:

  1. NMI (nemaskovatelné hardwarové přerušení)
  2. RESET (reset mikroprocesoru)
  3. IRQ (maskovatelné přerušení nebo instrukce BRK)

Vzhledem k tomu, že v NESu není žádný operační systém ani BIOS, musíme obsluhu přerušení realizovat vlastními prostředky. Vektory přerušení, tj. adresy subrutin (podprogramů) pro obsluhu přerušení, jsou uloženy na šesti nejvyšších adresách paměti, což pro nás v assembleru znamená obsah (mini)segmentu „VECTOR“:

; ---------------------------------------------------------------------
; Tabulka vektorů CPU
; ---------------------------------------------------------------------
 
.segment "VECTOR"
.addr nmi
.addr reset
.addr irq

Tento segment skutečně obsahuje jen trojici adres, konkrétně adres subrutin. Subrutina pro NMI bude prozatím obsahovat jen instrukci RTI znamenající návrat z přerušení:

; Obslužná rutina pro NMI (nemaskovatelné přerušení, vertical blank)
 
.proc nmi
        rti                     ; návrat z přerušení
.endproc

Podobně jednoduchá bude obsluha IRQ – opět se vyvolá jen jediná instrukce RTI:

; Obslužná rutina pro IRQ (maskovatelné přerušení)
 
.proc irq
        rti                     ; návrat z přerušení
.endproc

Ovšem složitější již bude obslužná rutina přerušení RESET. Proč tomu tak je? Tuto rutinu zavolá mikroprocesor po skutečném RESETu, ovšem i po zapnutí herní konzole. Je to tedy vstupní bod do naší hry či dema a můžeme v něm realizovat všechny inicializace a dokonce i spustit hlavní herní smyčku (viz další text):

; Obslužná rutina pro RESET
 
.proc reset
        ...
        ...
        ...
        ; vlastní herní smyčka je prozatím prázdná
game_loop:
        jmp game_loop           ; nekonečná smyčka (později rozšíříme)
.endproc
Poznámka: podrobnější popis této subrutiny bude uveden ve čtrnácté kapitole.

5. Programátorský model mikroprocesoru MOS 6502

Z programátorského hlediska se mikroprocesor MOS 6502 dosti podstatným způsobem odlišuje jak od konkurenčního Intelu 8080 (i od později vydaného Zilogu Z80), tak i od čipu RCA-1802. Zatímco procesor Intel 8080 obsahoval poměrně rozsáhlou sadu obecně použitelných osmibitových registrů (A, B, C, D, E, H a L), které se u některých instrukcí kombinovaly do 16bitových registrových párů, měl 6502 pouze jeden osmibitový akumulátor (registr A) a dva taktéž osmibitové index-registry pojmenované X a Y. Oba zmíněné typy procesorů samozřejmě obsahovaly další speciální registry, jako ukazatel na vrchol zásobníku (SP), programový čítač (PC) a příznakový registr (F).

Na první pohled by se mohlo zdát, že počet registrů mikroprocesoru MOS 6502 je zcela nedostatečný pro provádění většiny aritmetických či logických operací. Ve skutečnosti tomu tak není, protože tento procesor podporuje načtení druhého operandu z operační paměti (rychlost RAM nebyla tak limitujícím faktorem, jako je tomu dnes – ve skutečnosti byl přístup do RAM dvojnásobně rychlý v porovnání s mikroprocesorem). U mnoha instrukcí je podporován větší počet adresovacích režimů, celkově je možné operandy strojových instrukcí adresovat třinácti navzájem odlišnými způsoby. Při adresování se často používají oba index-registry, které je možné inkrementovat a dekrementovat – tím je umožněno provádění blokových přenosů dat, mazání souvislé oblasti paměti atd.

Mikroprocesor MOS 6502 také zavádí pojem takzvané nulté stránky paměti, která byla důsledně využita v instrukční sadě. Jedná se o prvních 256 bytů operační paměti, kterou je možné adresovat zjednodušeným způsobem. Adresa libovolné buňky z nulté stránky paměti je totiž uložena na jednom byte v operačním kódu instrukce, takže celá instrukce může být kratší (typicky pouze dva byte). Současně je i provádění instrukcí adresujících nultou stránku paměti rychlejší než při šestnáctibitovém adresování (například se provádí pouze osmibitové sčítání atd.). Z tohoto důvodu se můžeme na nultou stránku paměti dívat jako na pole 256 registrů, resp. alternativně na 128 plnohodnotně využitelných 16bitových ukazatelů (musíme si opět uvědomit, že operační paměti byly v té době stejně rychlé jako procesor, takže čtení či zápis dat do paměti byla záležitost jednoho či dvou cyklů). Myšlenka nulté stránky paměti byla dále rozšířena v procesoru Motorola 6809, kde se však tato stránka dala v adresovatelné paměti posouvat na libovolné místo, podobně jako v pokračovateli 6502 – 16bitovém čipu 65816 (použit například v herní konzoli SNES).

6. Registry a příznakové bity mikroprocesoru MOS 6502

„The 65×x processors are not ruined with too many registers.“

V předchozí kapitole jsme si řekli, že MOS 6502 obsahoval pouze minimální, ovšem ještě prakticky použitelný počet registrů. Všechny tyto registry, a to jak registry pracovní, tak i speciální, jsou vypsány v následující tabulce:

# Registr Šířka Význam
1 A 8 bitů akumulátor (pracovní registr)
2 X 8 bitů index registr
3 Y 8 bitů index registr
4 SP 8 bitů část ukazatele na vrchol zásobníku (+ $0100)
5 PC 16 bitů čítač instrukcí
6 P 7/8 bitů příznakový a stavový registr

Většina aritmetických a logických operací používala jako jeden z operandů akumulátor, tedy registr A. Druhý operand byl typicky načítán z operační paměti. Přitom se pro adresování velmi často používaly index registry X a Y. Ukazatel na vrchol zásobníku SP (či jen S) dokázal adresovat zásobník v rozsahu $0100 až $01FF, tedy 256 bajtů (takzvanou první stránku paměti). A příznakový registr P měl obsazen jen sedm bitů:

Bit Zkratka Označení Význam
7 N Negative záporný výsledek předchozí operace (popř. nastavený sedmý bit)
6 V oVerflow přetečení do sedmého bitu (popř. nastavený šestý bit)
5 neobsazeno
4 B Break rozlišení přerušení od instrukce BRK či PHP
3 D Decimal režim výpočtů: binární versus BCD (decimální)
2 I Interrupt zákaz přerušení
1 Z Zero nulový výsledek předchozí operace
0 C Carry přenos při předchozí operaci
Poznámka: dá se říci, že „aritmetické“ příznakové bity N, V, Z, a C byly převzaty a použity při návrhu procesorů ARM, protože ARM byl vytvořen právě jako náhrada za 6502 v počítačích pro Britskou veřejnost.

7. Adresovací režimy, využití registrů X a Y pro adresování

Adresovací režimy odlišují osmibitový mikroprocesor MOS 6502 od naprosté většiny ostatních mikroprocesorů a umožňují použít (zcela) odlišný styl programování založený na efektivním použití nulté stránky paměti a obou index registrů XY. Existuje celkem třináct adresovacích režimů, ovšem již na tomto místě je nutné podotknout, že žádná instrukce nevyužívá všechny tyto režimy. Některé adresovací režimy jsou určeny pouze pro skoky, další pro implicitní operandy atd.:

# Zápis Název Assembler Stručný popis
1 A accumulator INS A operandem je přímo akumulátor (instrukce tedy nemá žádný explicitně zapsaný operand)
2 abs absolute INS $HHLL za instrukcí následuje šestnáctibitová adresa, na níž je operand uložen
3 abs,X absolute, X-indexed INS $HHLL,X za instrukcí následuje šestnáctibitová adresa, která je přičtena k X
4 abs,Y absolute, Y-indexed INS $HHLL,Y za instrukcí následuje šestnáctibitová adresa, která je přičtena k Y
5 # immediate INS #$BB za instrukcí následuje bajt s osmibitovou konstantou
6 impl implied INS operand je odvozen přímo z instrukce, například INX
7 ind indirect INS ($HHLL) nepřímá adresace přes adresu uloženou za instrukcí (ta je ukazatelem), nepřímý skok
8 X,ind X-indexed, indirect INS ($LL,X) efektivní adresa je spočtena z hodnoty uložené na (LL+X)
9 ind,Y indirect, Y-indexed INS ($LL),Y efektivní adresa je spočtena z hodnoty uložené na LL, k výsledku se přičte Y
10 rel relative INS $BB použito u relativních skoků; za instrukcí je jeden bajt reprezentující offset se znaménkem
11 zpg zeropage INS $LL operand je uložen na nulté stránce na adrese LL
12 zpg,X zeropage, X-indexed INS $LL,X operand je uložen na nulté stránce na adrese LL+X
13 zpg,Y zeropage, Y-indexed INS $LL,Y operand je uložen na nulté stránce na adrese LL+Y
Poznámka: použití registrů X a Y není zcela symetrické, protože se od sebe liší podporou, resp. nepodporou adresovacího režimu X,ind a ind,Y. Zejména režim číslo 9 lze použít pro operace s poli, přesuny bloků, mazání bloků atd. (je to zcela typická součást MOS 6502 ukazující na promyšlený návrh celého čipu).

Ukažme si nyní zajímavé použití již výše zmíněného režimu číslo 9. Prozatím ovšem nemáme znalosti HW NESu, takže si ukažme, jakým způsobem se na osmibitových počítačích Atari vypíše znak „A“ do levého horního rohu. To je zdánlivě snadné – v textovém režimu prostě na první adresu video paměti zapíšeme kód znaku „A“, což je v ATASCII hodnota 33. Ovšem jak zjistit adresu video paměti, která se odlišuje podle použitého počítače (kapacita RAM může být 16kB, 48kB či 64kB), stavu cartridge atd.? Systém zajišťuje, že tato adresa je uložena v nulté stránce paměti, konkrétně na adresách $88 a $89. Na adresu uloženou na těchto adresách tedy musíme zapsat hodnotu 33. Jedno z možných (plně funkčních) řešení vypadá takto (registr Y je nutné použít, ovšem jeho hodnotu ponecháme na nule, takže adresu neovlivní – Y použijeme při tisku celého řetězce):

.include "atari.inc"
 
.CODE
 
.proc main
        lda #33                 ; ATASCII hodnota znaku "A"
        ldy #0                  ; vynulovat registr Y
        sta (88),y              ; tisk znaku "A" na první místo na obrazovce
                                ; (adresa Video RAM je na adresách 88 a 89)
loop:   jmp loop
end:
.endproc
 
 
.segment "EXEHDR"
.word   $ffff
.word   main
.word   main::end - 1
 
 
.segment "AUTOSTRT"
.word   RUNAD                   ; definováno v atari.h
.word   RUNAD+1
.word   main

Obrázek 1: Předchozí program po spuštění.

8. Aritmetické a logické instrukce

Mikroprocesor MOS 6502 obsahuje pouze 56 instrukcí, přičemž mnoho instrukcí podporuje větší množství adresovacích režimů a tudíž i více variant (i tak však zdaleka není obsazeno všech 256 možných kombinací – ty byly postupně obsazovány v dalších procesorech, popř. na původním MOS 6502 měly sice oficiálně nedokumentovanou, ovšem logickou/očekávanou funkci – například načtení registru A i X jedinou instrukcí atd.). Nejprve si popíšeme aritmetické a logické instrukce mikroprocesoru MOS 6502. Většina dále popsaných instrukcí jako svůj první operand akceptuje akumulátor a druhým operandem může být konstanta, popř. hodnota načtená z operační paměti s využitím výše popsaných adresovacích režimů. Výjimkou jsou instrukce s jediným operandem, v nichž nemusí vystupovat akumulátor, popř. instrukce, v nichž je přímo operand vyjádřen názvem instrukce (INX atd.):

# Instrukce Plné jméno Popis
1 ADC add with carry součet hodnoty s akumulátorem (včetně přetečení)
2 SBC subtract with carry odečtení hodnoty od akumulátoru (včetně výpůjčky)
3 AND and with accumulator logická operace AND s akumulátorem
4 ORA or with accumulator logická operace OR s akumulátorem
5 EOR exclusive or with accumulator logická operace XOR s akumulátorem
       
6 INC increment zvýšení hodnoty o 1 (kupodivu nelze provést přímo s akumulátorem, ovšem s pamětí ano)
7 INX increment X zvýšení hodnoty index registru X o 1
8 INY increment Y zvýšení hodnoty index registru Y o 1
9 DEC decrement snížení hodnoty o 1 (opět nelze provést s akumulátorem)
10 DEX decrement X snížení hodnoty index registru X o 1
11 DEY decrement Y snížení hodnoty index registru Y o 1
       
12 CMP compare with accumulator odečtení hodnoty od akumulátoru bez zápisu výsledku
13 CPX compare with X odečtení hodnoty od index registru X bez zápisu výsledku
14 CPY compare with Y odečtení hodnoty od index registru Y bez zápisu výsledku
15 BIT bit test logické AND bez uložení výsledků (změní se jen příznakové bity)
       
16 ASL arithmetic shift left aritmetický posun doleva o jeden bit
17 LSR logical shift right logický posun doprava o jeden bit
18 ROL rotate left rotace doleva o jeden bit
19 ROR rotate right rotace doprava o jeden bit
Poznámka: instrukce CMP, CPX, CPY a BIT nastavují příznakové bity a setkáme se s nimi později.

9. Skoky a rozvětvení

Následuje další skupina instrukcí. Konkrétně se jedná o instrukce skoku, popř. skoku a výskoku ze subrutiny (podprogramu). Skákat je možné v rámci celé adresovatelné RAM, tedy v rozsahu plných 64kB:

# Instrukce Plné jméno Popis
20 JMP jump skok (existuje několik adresovacích režimů této instrukce)
21 JSR jump to subroutine skok do podprogramu s uložením návratové adresy na zásobník
22 RTS return from subroutine návrat z podprogramu
23 RTI return from interrupt návrat z prerušovací rutiny (již jsme tuto instrukci potkali)

Relativní skoky v rámci rozsahu –128 až 127 jsou provedeny na základě vyhodnocení nějaké podmínky, konkrétně testování zvoleného příznakového bitu. Oproti Motorole 6800, ze které 6502 ideově vychází, byl počet podmíněných skoků snížen na polovinu, takže některé kombinace podmínek neexistují (včetně BRA a BRN):

# Instrukce Plné jméno Popis
24 BCC branch on carry clear rozvětvení za podmínky C==0
25 BCS branch on carry set rozvětvení za podmínky C==1
26 BEQ branch on equal (zero set) rozvětvení za podmínky Z==1
27 BMI branch on minus (negative set) rozvětvení za podmínky N==1
28 BNE branch on not equal (zero clear) rozvětvení za podmínky Z==0
29 BPL branch on plus (negative clear) rozvětvení za podmínky N==0
30 BVC branch on overflow clear rozvětvení za podmínky O==0
31 BVS branch on overflow set rozvětvení za podmínky O==1
Poznámka: povšimněte si neexistence podmíněných skoků, kde by podmínka byla tvořena kombinací příznakových bitů. Typickými příklady z jiných architektur by byly instrukce související s aritmetikou s dvojkovým doplňkem (tedy s potenciálně zápornými hodnotami), jejichž porovnání je složitější.

10. Instrukce pro přesuny dat

Další skupinou instrukcí jsou instrukce určené pro přesuny dat mezi operační pamětí a registry, popř. mezi registry navzájem. Jedná se o poměrně velké množství instrukcí, které jsou zvláštní tím, že instrukce pro načtení dat modifikují příznakové bity N a Z, takže již pouhé načtení hodnoty zjistí, zda se jedná o hodnotu zápornou nebo nulovou (resp. nezápornou či nenulovou):

# Instrukce Plné jméno Popis
32 LDA load accumulator načtení bajtu do akumulátoru
33 LDX load X načtení bajtu do registru X
34 LDY load Y načtení bajtu do registru Y
       
35 STA store accumulator uložení hodnoty akumulátoru
36 STX store X uložení hodnoty registru X
37 STY store Y uložení hodnoty registru Y
       
38 TAX transfer accumulator to X přesun X=A
39 TAY transfer accumulator to Y přesun Y=A
40 TSX transfer stack pointer to X přesun X=SP
41 TXA transfer X to accumulator přesun A=X
42 TXS transfer X to stack pointer přesun SP=X
43 TYA transfer Y to accumulator přesun A=Y
       
44 PHA push accumulator uložení akumulátoru na zásobník
45 PHP push processor status (SR) uložení příznaků na zásobník
46 PLA pull accumulator obnovení akumulátoru ze zásobníku
47 PLP pull processor status (SR) obnovení příznaků ze zásobníku
Poznámka: povšimněte si určité neortogonality, resp. nesymetrie v této části instrukční sady. Například instrukce T.. neobsahují všechny možné kombinace; příkladem je neexistující instrukce TXY apod. Dále chybí některé adresovací režimy u LDX a LDY v porovnání s LDA:
LDA
    immediate     LDA #oper
    zeropage      LDA oper
    zeropage,X    LDA oper,X
    absolute      LDA oper
    absolute,X    LDA oper,X
    absolute,Y    LDA oper,Y
    (indirect,X)  LDA (oper,X)
    (indirect),Y  LDA (oper),Y
 
LDX
    immediate     LDX #oper
    zeropage      LDX oper
    zeropage,Y    LDX oper,Y
    absolute      LDX oper
    absolute,Y    LDX oper,Y
 
LDY
    immediate     LDY #oper
    zeropage      LDY oper
    zeropage,X    LDY oper,X
    absolute      LDY oper
    absolute,X    LDY oper,X

11. Manipulace s příznakovými bity

Několik instrukcí manipuluje přímo s příznakovými bity. Jedná se o následující instrukce:

# Instrukce Plné jméno Operace provedená instrukcí
48 CLC clear carry nastavení C=0
49 CLD clear decimal nastavení D=0
50 CLI clear interrupt disable nastavení I=0
51 CLV clear overflow nastavení V=0
       
52 SEC set carry nastavení C=1
53 SED set decimal nastavení D=1
54 SEI set interrupt disable nastavení I=1

Mnemotechnika pojmenování těchto instrukcí je jednoduchá – CL znamená clear kdežto SE znamená set.

Poznámka: povšimněte si, že zdaleka ne všechny příznaky je možné přímo nastavit nebo vynulovat (celkově bychom totiž potřebovali čtrnáct instrukcí). Ovšem mnoho příznaků je nastavováno či nulováno jinými instrukcemi, například BIT či CMP, čehož je možné využít (při výpočtech vlastně musíme přímo manipulovat jen s příznakem C kvůli rotacím).

12. Zbývající instrukce

Zbývá nám popsat už jen dvě instrukce nezařazené do žádné výše uvedené skupiny. Jedná se o tyto instrukce:

# Instrukce Plné jméno Popis
55 NOP no operation přechod na další instrukci
56 BRK break / interrupt uložení PC a SR na zásobník, zastavení (návrat do monitoru)

Instrukce BRK je velmi zvláštní. Samotný operační kód má délku jednoho bajtu, ovšem pokud procesor tuto instrukci vykoná, zapíše na zásobník adresu o další bajt (jedničku) zvětšenou. To znamená, že při návratu z obsluhy přerušení (protože BRK je vlastně softwarové přerušení) se zdá, jakoby BRK měla délku dva bajty. Tento bajt navíc lze použít například pro rozlišení, o které přerušení se jedná atd. A navíc je operační kód instrukce BRK roven $00. Pokud je paměť (až na program) vynulována a provede se skok kamkoli mimo program, dojde ihned k jeho zastavení – což je opět užitečné, zejména pokud je 6502 použit v kritických aplikacích. Mnohé jiné architektury mají operační kód $00 vyhrazen pro NOP, což znamená, že při špatném skoku bude procesor interpretovat tyto NOPy až narazí na nějakou náhodnou instrukci/instrukce, které vykoná (což nechceme).

13. Kde si lze možnosti 6502 otestovat bez nutnosti pochopit strukturu NESu?

Instrukční sadu mikroprocesoru MOS 6502 si lze dokonce odzkoušet na prakticky jakémkoli současném počítači či tabletu, a to bez nutnosti instalace assembleru a/nebo simulátoru (popř. emulátoru některého osmibitového domácího mikropočítače nebo herní konzole). Na stránce http://6502asm.com/ se totiž nachází vydařený simulátor virtuálního počítače vybaveného jednoduchým displejem, klávesnicí a v neposlední řadě právě mikroprocesorem MOS 6502. Tento simulátor, jenž byl naprogramovaný Stianem Sorengem, obsahuje editor (ve skutečnosti se v současné verzi jedná o pouhé textové pole umístěné na HTML stránce), do něhož je možné zapsat program v jazyku symbolických instrukcí a následně tento program přeložit vestavěným assemblerem a poté i spustit. Na adrese $fe se nachází generátor náhodných číel, na adrese $ff pak kód stisknuté klávesy. Jediným výstupním médiem je rastrový obrázek 32×32 pixelů uložený od adresy $200 do $5ff (1024 bajtů), ovšem jen spodní bity obsahují barvu.

Poznámka: to je vše – popis tohoto (pseudo)počítače se skutečně vlezl do jediného odstavce! NES je v tomto ohledu velmi odlišný.

Příklad vykreslení svislých pruhů na „obrazovku“:

    LDA #0       ; zapisovaná hodnota
    LDX #0       ; počitadlo a současně i adresa pro zápis
LOOP:
    STA $200,X   ; vykreslení do první čtvrtiny obrazovky
    STA $300,X
    STA $400,X
    STA $500,X   ; vykreslení do čtvrté čtvrtiny obrazovky
    ADC #1       ; index vykreslované barvy
    INX          ; přechod na další pixel
BNE LOOP         ; skonči po zápisu 256 pixelů
Poznámka: pokuste se použít index Y pro vnější smyčku, aby nebylo nutné provádět čtyři zápisy ručně.

14. Druhý pohled na kostru programu

Nyní již máme dostatek informací pro pochopení kostry programu, kterou jsme si uvedli minule. Připomeňme si, že se jedná o plně funkční kód, který ihned po spuštění herní konzole (nebo jejího emulátoru) provede inicializaci mikroprocesoru, následně inicializaci PPU a APU (grafika a zvuky), vymaže operační paměť a spustí nekonečnou (prozatím prázdnou) herní smyčku. Jednotlivé části tohoto kódu jsou vysvětleny v navazujících kapitolách:

; Obslužná rutina pro RESET
 
.proc reset
        ; nastavení stavu CPU
        sei                     ; zákaz přerušení
        cld                     ; vypnutí dekadického režimu (není podporován)
 
        ldx #$ff
        txs                     ; vrchol zásobníku nastaven na 0xff (první stránka)
 
        ; nastavení řídicích registrů
        ldx #$00
        stx $2000               ; nastavení PPUCTRL = 0
        stx $2001               ; nastavení PPUMASK = 0
        stx $4015               ; nastavení APUSTATUS = 0
 
        ; čekání na vnitřní inicializaci PPU (dva snímky)
wait1:  bit $2002               ; test obsahu registru PPUSTATUS 
        bpl wait1               ; skok, pokud je příznak N nulový
wait2:  bit $2002               ; test obsahu registru PPUSTATUS 
        bpl wait2               ; skok, pokud je příznak N nulový
 
        ; vymazání obsahu RAM
        lda #$00                ; vynulování registru A
loop:   sta $000, x             ; vynulování X-tého bajtu v nulté stránce
        sta $100, x
        sta $200, x
        sta $300, x
        sta $400, x
        sta $500, x
        sta $600, x
        sta $700, x             ; vynulování X-tého bajtu v sedmé stránce
        inx                     ; přechod na další bajt
        bne loop                ; po přetečení 0xff -> 0x00 konec smyčky
 
        ; čekání na dokončení dalšího snímku, potom může začít herní smyčka
wait3:  bit $2002               ; test obsahu registru PPUSTATUS 
        bpl wait3               ; skok, pokud je příznak N nulový
 
        ; vlastní herní smyčka je prozatím prázdná
game_loop:
        jmp game_loop           ; nekonečná smyčka (později rozšíříme)
.endproc

15. Nastavení stavu mikroprocesoru po resetu

Víme již, že jak nastavení hardware, tak i vlastního mikroprocesoru po resetu (a tedy i po spuštění konzole) musíme jako vývojáři zajistit sami. Nejprve se podívejme na způsob nastavení stavu mikroprocesoru. Typicky ihned na začátku zakážeme maskovatelné přerušení instrukcí SEI (Set Interrupt Disable Status) a následně pro jistotu vypneme dekadický režim výpočtů (BCD), jenž stejně není čipem Ricoh 2A03 podporován (a tedy jeho povolení by byla nedefinovaná operace):

; nastavení stavu CPU
sei                     ; zákaz přerušení
cld                     ; vypnutí dekadického režimu (není podporován)

Následuje malý trik, kterým se vlastně vymaže celý zásobník. Nastavíme totiž adresu vrcholu zásobníku na adresu $1ff, přičemž prefix $100 je řešen samotným procesorem (nastavujeme jen spodních osm bitů). Takto ušetříme několik bajtů na zásobníku, protože vlastně zahodíme jak výplňový bajt, tak i adresu návratu ze subrutiny reset (ta končí nekonečnou smyčkou, tudíž z ní nikdy nevyskočíme):

ldx #$ff
txs                     ; vrchol zásobníku nastaven na 0x1ff (první stránka)
Poznámka: registr Y je jediným registrem, který lze použít pro přímý přesun hodnoty do registru S/SP. Jedná se o jeden z případů neortogonality instrukční sady MOS 6502.

16. Nastavení řídicích registrů

Před spuštěním herní smyčky je nutné nastavit i některé řídicí registry PPU (grafika) i APU (hudba a zvuky). Zejména je nutné vynulovat registr PPUCTRL. Zápisem nuly mj. zajistíme, že se nebude generovat NMI (nemaskovatelné přerušení) po každém zobrazeném snímku. Další registr, který vynulujeme, se jmenuje PPUMASK. Zápisem nuly mj. zakážeme zobrazení všech grafických objektů, tj. jak pozadí, tak i spritů. Nulu zapíšeme i do řídicího registru nazvaného SND_CHN neboli plným jménem „Sound channels enable and status“. Ten je (z pohledu mikroprocesoru) namapován na adresu $4015 a zápisem nuly vypneme zvuky:

; nastavení řídicích registrů
ldx #$00
stx $2000               ; nastavení PPUCTRL = 0
stx $2001               ; nastavení PPUMASK = 0
stx $4015               ; nastavení APUSTATUS = 0
Poznámka: samozřejmě zde můžeme použít akumulátor A a zapisovat nuly instrukcí STA.

17. Trik pro čekání na zobrazení dalšího snímku

Před provedením dalších operací je nutné počkat na stabilizaci grafického čipu. Typicky je tato operace vyřešena počkáním na vykreslení dvou snímků. Jak je však možné toto čekání realizovat v praxi? Realizace je založena na opakovaném čtení registru PPUSTATUS, který je mapován na adresu $2002. Při čtení nás bude zajímat nejvyšší (sedmý) bit, jenž je nastaven na jedničku ve chvíli, kdy probíhá takzvaný vertical blank neboli vertikální zatemnění obrazu po vykreslení snímku. Zajímavé je, že po přečtení stavového registru je tento bit automaticky smazán. Algoritmus čekání na snímek tedy bude vypadat takto:

  1. Přečti obsah sedmého bitu registru PPUSTATUS
  2. Pokud je obsah nulový, skok na bod 1
  3. …jsme ve vertikálním zatemnění – snímek byl vykreslen

A při realizaci kroku 1 a 2 využijeme elegantní trik procesoru 6502. Použijeme totiž instrukci BIT, která načte obsah paměti na specifikované adrese a (kromě dalších operací) zkopíruje obsah sedmého bitu z načtené hodnoty do příznakového bitu N (negative). Test na nulovost tedy v tomto případě zajistí instrukce BPL (branch if plus):

CS24_early

wait1:  bit $2002               ; test obsahu registru PPUSTATUS 
        bpl wait1               ; skok, pokud je příznak N nulový
Poznámka: instrukce BIT umožňuje i provedení dalších triků, s nimiž se seznámíme příště.

18. Trik pro vymazání obsahu RAM

Po zapnutí herní konzole je obecně obsah RAM náhodný. Minimálně pro ladicí účely je vhodné paměť promazat, už jen z toho důvodu, že hodnota $00 znamená operační kód instrukce BRK. Existuje mnoho možností, jak zajistit přepsání bloku paměti konstantní hodnotou a žádný z těchto způsobů není příliš intuitivní. Je tomu tak z toho důvodu, že indexové registry mají šířku jen osmi bitů a případná přímá změna adresy v kódu instrukce není možná, pokud je tento kód uložen v ROM. Ovšem díky tomu, že kapacita RAM u NES je rovna pouze dvěma kilobajtům, což odpovídá osmi stránkám paměti (po 256 bajtech), můžeme „otrocky“ smazat těchto osm stránek například takto:

        ; vymazání obsahu RAM
        lda #$00                ; vynulování registru A
loop:   sta $000, x             ; vynulování X-tého bajtu v nulté stránce
        sta $100, x
        sta $200, x
        sta $300, x
        sta $400, x
        sta $500, x
        sta $600, x
        sta $700, x             ; vynulování X-tého bajtu v sedmé stránce
        inx                     ; přechod na další bajt
        bne loop                ; po přetečení 0xff -> 0x00 konec smyčky
Poznámka: u moderních architektur je podobný kód obecně velmi špatný, protože nevyužívá „burst“ přenosu a navíc může zbytečně přetěžovat cache (vyrovnávací paměť). Ovšem výhodou MOS 6502 i dalších osmibitových CPU je fakt, že podobné techniky většinou vůbec nepodporují a proto je zde RAM skutečně „random access memory“, což o moderních architekturách neplatí ani náhodou.

19. Repositář s demonstračními příklady

Demonstrační příklady napsané v assembleru, které jsou určené pro překlad pomocí ca65, byly uložen do Git repositáře, který je dostupný na adrese https://github.com/tisnik/8bit-fame. Příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý (dnes již poměrně rozsáhlý) repositář:

# Příklad Stručný popis Adresa
1 example01.asm zdrojový kód příkladu tvořeného kostrou aplikace pro NES https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/example01.asm
2 example02.asm použití standardní konfigurace linkeru pro konzoli NES https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/example02.asm
3 example03.asm symbolická jména řídicích registrů PPU https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/example03.asm
4 example04.asm zjednodušený zápis lokálních smyček https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/example04.asm
       
5 link.cfg konfigurace segmentů pro linker ld65 https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/link.cfg
6 Makefile Makefile pro překlad příkladů https://github.com/tisnik/8bit-fame/blob/master/NES-ca65/Makefile

20. Odkazy na Internetu

  1. NesDev.org
    https://www.nesdev.org/
  2. How to Program an NES game in C
    https://nesdoug.com/
  3. Getting Started Programming in C: Coding a Retro Game with C Part 2
    https://retrogamecoders.com/getting-started-with-c-cc65/
  4. „Game Development in Eight Bits“ by Kevin Zurawel
    https://www.youtube.com/wat­ch?v=TPbroUDHG0s&list=PLcGKfGE­EONaBjSfQaSiU9yQsjPxxDQyV8&in­dex=4
  5. Game Development for the 8-bit NES: A class by Bob Rost
    http://bobrost.com/nes/
  6. Game Development for the 8-bit NES: Lecture Notes
    http://bobrost.com/nes/lectures.php
  7. NES Graphics Explained
    https://www.youtube.com/wat­ch?v=7Co_8dC2zb8
  8. NES GAME PROGRAMMING PART 1
    https://rpgmaker.net/tuto­rials/227/?post=240020
  9. NES 6502 Programming Tutorial – Part 1: Getting Started
    https://dev.xenforo.relay­.cool/index.php?threads/nes-6502-programming-tutorial-part-1-getting-started.858389/
  10. Minimal NES example using ca65
    https://github.com/bbbradsmith/NES-ca65-example
  11. List of 6502-based Computers and Consoles
    https://www.retrocompute.co.uk/list-of-6502-based-computers-and-consoles/
  12. History of video game consoles (second generation): Wikipedia
    http://en.wikipedia.org/wi­ki/History_of_video_game_con­soles_(second_generation)
  13. 6502 – the first RISC µP
    http://ericclever.com/6500/
  14. 3 Generations of Game Machine Architecture
    http://www.atariarchives.or­g/dev/CGEXPO99.html
  15. bee – The Multi-Console Emulator
    http://www.thebeehive.ws/
  16. Nerdy Nights Mirror
    https://nerdy-nights.nes.science/
  17. NES Development Day 1: Creating a ROM
    https://www.moria.us/blog/2018/03/nes-development
  18. How to Start Making NES Games
    https://www.matthughson.com/2021/11/17/how-to-start-making-nes-games/
  19. ca65 Users Guide
    https://cc65.github.io/doc/ca65.html
  20. cc65 Users Guide
    https://cc65.github.io/doc/cc65.html
  21. ld65 Users Guide
    https://cc65.github.io/doc/ld65.html
  22. da65 Users Guide
    https://cc65.github.io/doc/da65.html
  23. Nocash NES Specs
    http://nocash.emubase.de/everynes.htm
  24. Nintendo Entertainment System
    http://cs.wikipedia.org/wiki/NES
  25. Nintendo Entertainment System Architecture
    http://nesdev.icequake.net/nes.txt
  26. NesDev
    http://nesdev.parodius.com/
  27. 2A03 technical reference
    http://nesdev.parodius.com/2A03%20techni­cal%20reference.txt
  28. NES Dev wiki: 2A03
    http://wiki.nesdev.com/w/in­dex.php/2A03
  29. Ricoh 2A03
    http://en.wikipedia.org/wi­ki/Ricoh_2A03
  30. 2A03 pinouts
    http://nesdev.parodius.com/2A03_pi­nout.txt
  31. 27c3: Reverse Engineering the MOS 6502 CPU (en)
    https://www.youtube.com/wat­ch?v=fWqBmmPQP40
  32. “Hello, world” from scratch on a 6502 — Part 1
    https://www.youtube.com/wat­ch?v=LnzuMJLZRdU
  33. A Tour of 6502 Cross-Assemblers
    https://bumbershootsoft.wor­dpress.com/2016/01/31/a-tour-of-6502-cross-assemblers/
  34. Nintendo Entertainment System (NES)
    https://8bitworkshop.com/doc­s/platforms/nes/
  35. Question about NES vectors and PPU
    https://archive.nes.science/nesdev-forums/f10/t4154.xhtml
  36. How do mapper chips actually work?
    https://archive.nes.science/nesdev-forums/f9/t13125.xhtml
  37. INES
    https://www.nesdev.org/wiki/INES
  38. NES Basics and Our First Game
    http://thevirtualmountain­.com/nes/2017/03/08/nes-basics-and-our-first-game.html
  39. Where is the reset vector in a .nes file?
    https://archive.nes.science/nesdev-forums/f10/t17413.xhtml
  40. CPU memory map
    https://www.nesdev.org/wi­ki/CPU_memory_map
  41. How to make NES music
    http://blog.snugsound.com/2008/08/how-to-make-nes-music.html
  42. Nintendo Entertainment System Architecture
    http://nesdev.icequake.net/nes.txt
  43. MIDINES
    http://www.wayfar.net/0×f00000_o­verview.php
  44. FamiTracker
    http://famitracker.com/
  45. nerdTracker II
    http://nesdev.parodius.com/nt2/
  46. How NES Graphics work
    http://nesdev.parodius.com/nesgfx.txt
  47. NES Technical/Emulation/Development FAQ
    http://nesdev.parodius.com/NES­TechFAQ.htm
  48. Adventures with ca65
    https://atariage.com/forum­s/topic/312451-adventures-with-ca65/
  49. example ca65 startup code
    https://atariage.com/forum­s/topic/209776-example-ca65-startup-code/
  50. 6502 PRIMER: Building your own 6502 computer
    http://wilsonminesco.com/6502primer/
  51. 6502 Instruction Set
    https://www.masswerk.at/6502/6502_in­struction_set.html
  52. Chip Hall of Fame: MOS Technology 6502 Microprocessor
    https://spectrum.ieee.org/tech-history/silicon-revolution/chip-hall-of-fame-mos-technology-6502-microprocessor
  53. Single-board computer
    https://en.wikipedia.org/wiki/Single-board_computer
  54. www.6502.org
    http://www.6502.org/
  55. 6502 PRIMER: Building your own 6502 computer – clock generator
    http://wilsonminesco.com/6502pri­mer/ClkGen.html
  56. Great Microprocessors of the Past and Present (V 13.4.0)
    http://www.cpushack.com/CPU/cpu.html
  57. Jak se zrodil procesor?
    https://www.root.cz/clanky/jak-se-zrodil-procesor/
  58. Osmibitové mikroprocesory a mikrořadiče firmy Motorola (1)
    https://www.root.cz/clanky/osmibitove-mikroprocesory-a-mikroradice-firmy-motorola-1/
  59. Mikrořadiče a jejich použití v jednoduchých mikropočítačích
    https://www.root.cz/clanky/mikroradice-a-jejich-pouziti-v-jednoduchych-mikropocitacich/
  60. Mikrořadiče a jejich aplikace v jednoduchých mikropočítačích (2)
    https://www.root.cz/clanky/mikroradice-a-jejich-aplikace-v-jednoduchych-mikropocitacich-2/
  61. 25 Microchips That Shook the World
    https://spectrum.ieee.org/tech-history/silicon-revolution/25-microchips-that-shook-the-world
  62. Comparison of instruction set architectures
    https://en.wikipedia.org/wi­ki/Comparison_of_instructi­on_set_architectures
  63. INES header format
    https://github.com/camsau­l/nesasm/blob/master/ines_he­ader_format.txt

Byl pro vás článek přínosný?

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.