O programovacím jazyku C se někdy s nadsázkou říká, že je to „přenositelný assembler“ [1]. Programy napsané v jazyce C skutečně mohou být za určitých okolností velmi efektivní, ovšem v tomto případě do značné míry záleží na kvalitě céčkového překladače (a taktéž na kvalitě programátora). V dnešním článku si ve stručnosti řekneme, jak je tomu v případě osmibitových domácích mikropočítačů Atari.
Co se dozvíte v článku
- Ideální mikroprocesor pro provoz programů napsaných v céčku
- Specifické vlastnosti mikroprocesoru MOS 6502: život autorů překladačů céčka zde není snadný
- Překladače a cross překladače pro mikroprocesory MOS 6502
- Deep Blue C
- Knihovna pro práci se sprity
- Jednoduché demo dodávané s Deep Blue C
- Překladač cc65
- Knihovna peekpoke pro cc65
- První demonstrační příklad: změna barvy pozadí s využitím knihovny peekpoke
- Překlad prvního demonstračního příkladu: C → assembler → objektový soubor → spustitelný soubor
- Struktury definované v hlavičkovém souboru atari.h
- Druhý demonstrační příklad: nastavení barvy pozadí modifikací prvku struktury OS
- Vykreslení spritu na obrazovku s využitím makra POKE
- Vykreslení spritu na obrazovku s využitím struktur definovaných v atari.h
- Pohyb spritu s jeho synchronizací s hodinami reálného času
- První úprava příkladu: přímá práce s ukazateli
- Druhá úprava příkladu: využití makra PEEK
- Repositář s demonstračními příklady
- Odkazy na Internetu
Připomeňme si, že tyto počítače jsou založeny na slavném osmibitovém mikroprocesoru MOS 6502, jehož instrukční kód jsme si do jisté míry přiblížili v seriálu o vývoji her a grafických dem pro herní konzoli NES a většinu instrukcí jsme využili i v tomto seriálu (ovšem prozatím jsme vynechali některé adresovací režimy, které uvidíme právě v kódu generovaném překladači jazyka C).
Obrázek 1: Přebal slavného prvního vydání knihy o programovacím jazyku C.
Ideální mikroprocesor pro provoz programů napsaných v céčku
Překladače a cross překladače programovacího jazyka C existují pro prakticky každou počítačovou platformu i pro prakticky každý typ mikroprocesoru. Výjimkou samozřejmě není ani osmibitový mikroprocesor MOS 6502. Ovšem na tomto místě je nutné poznamenat, že právě u podobných typů mikroprocesorů, jako je MOS 6502, stojí tvůrci překladačů před nelehkým úkolem, protože programátorský model MOS 6502 se v mnoha ohledech odlišuje od „ideálního céčkového mikroprocesoru“ A jak by vlastně měl takový ideální procesor vhodný pro jazyk C vypadat? Především by měl obsahovat velký počet pracovních registrů, dále by měl umět vykonávat instrukce pro snadnou manipulaci s obsahem zásobníku (což umožňuje předávání parametrů i práci s lokálními proměnnými), pracovní registry by mohly být použity při adresování (což ovšem znamená, že by měly mít šířku aspoň šestnáct bitů), dále by měl podporovat adresování s offsetem (násobeným ×1, ×2, ×4 apod.) a ideálně by se při adresování mohla provádět inkrementace či dekrementace offsetu, opět o hodnotu 1, 2, 4 apod.
Obrázek 2: Přebal druhého vydání knihy o jazyku C (tentokrát již o ANSI/ISO C).
Navíc by operační paměť měla být viditelná jako lineární prostor s buňkami s postupně rostoucími adresami(„flat model“ – adresování přes segment přineslo více problémů než užitku). Tomuto ideálu se kdysi přiblížil čip Motorola 68000 a dále RISCové procesory, z nichž mnohé byly navrženy právě s ohledem na sémantické možnosti jazyka C. Z osmibitových mikroprocesorů měl k ideální architektuře nejblíže pravděpodobně čip Motorola 6809, který ovšem na trh přišel pozdě a navíc byl v porovnání s MOS 6502 příliš drahý, takže se masivně neujal.
Obrázek 3: Vývojové prostředí překladače Borland C++ v DOSu (Intel 8086). Podporován je i jazyk C.
Specifické vlastnosti mikroprocesoru MOS 6502: život autorů překladačů céčka zde není snadný
Z programátorského hlediska se mikroprocesor MOS 6502 dosti podstatným způsobem odklání od ideálního mikroprocesoru navrženého s ohledem na překladače jazyka C. Navíc se tento mikroprocesor odlišuje jak od konkurenčního Intelu 8080 (i od později vydaného Zilogu Z80 – ten je pro běh programů v jazyku C o něco lépe uzpůsoben), tak i od pozdější řady Intel 8088/8086. Zatímco konkurenční mikroprocesor 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 šestnáctibitových registrových párů, měl MOS 6502 pouze jeden osmibitový akumulátor (registr A) a dva taktéž osmibitové index-registry pojmenované X a Y. Oba zmíněné typy mikroprocesorů samozřejmě obsahovaly další speciální registry, jako ukazatel na vrchol zásobníku (S), programový čítač (PC) a příznakový registr (F).
Na první pohled by se mohlo zdát, že počet pracovních 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, což je z dnešního pohledu takřka neuvěřitelné). U mnoha instrukcí je navíc 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. A navíc jeden adresní režim spojený s index registrem Y umožňuje relativně snadný přístup do polí.
Obrázek 4: Prostředí překladače Watcom C, opět určené pro systém DOS a mikroprocesory Intel řady 8086.
Překladače a cross překladače pro mikroprocesory MOS 6502
Některé překladače programovacího jazyka C určené pro osmibitové mikroprocesory MOS 6502 byly navrženy přímo pro běh na strojích osazených tímto čipem. To je v současnosti zcela normální situace (příkladem jsou aplikace pro PC, které se překládají na PC), ovšem v případě mikroprocesoru MOS 6502 se museli tvůrci překladačů vypořádat s pomalým čipem (hodinová frekvence na některých počítačích nedosahovala ani 1 MHz, na Atari byla nastavena na přibližně 1,75 MHz), instrukční sadou, která C příliš nepodporovala (viz předchozí kapitoly), velmi malou kapacitou paměti a navíc i relativně pomalým externím paměťovým médiem (typicky se používala disketa, protože kazetové verze C by byly ještě problematičtější). V důsledku kombinace všech těchto omezení se jednalo spíše o projekty určené pro amatérské použití, zatímco profesionální software stále vznikal v assembleru popř. v předminule a minule popsaném programovacím jazyku Action!
Ovšem to v žádném případě neznamená, že by „céčko“ pro osmibitové Atari vůbec neexistovalo. Nabízeno bylo hned několik překladačů. Jednou z prvních implementací překladače C pro MOS 6502 je C/65 od slavné firmy Optimized Systems Software (OSS). Taktéž se na tomto místě musíme zmínit o známém překladači Aztec C, jenž byl portovaný na velké množství různých typů mikropočítačů, zapomenout nesmíme ani na Deep Blue C (viz též https://en.wikipedia.org/wiki/Deep_Blue_C) pro osmibitové počítače Atari. V případě těchto překladačů se autoři museli vyrovnat s faktem, že znaková sada ATASCII neobsahovala složené závorky, takže se za ně musela hledat náhrada. Teoreticky byly k dispozici takzvané „trojznaky“ (trigraphs, viz zde), ovšem v případě Deep Blue C se používaly spíše „dvojznaky“, což konkrétně znamená, že zápis vypadal například takto:
main()
$(
printf("Hello World!");
$)
Zajímavější jsou z dnešního pohledu cross compilery a cross assemblery. Tyto typy nástrojů jsou velmi často používané i dnes, zejména v oblasti mikrořadičů, digitálních signálových procesorů nebo mobilních telefonů (viz například Scratchbox). Ovšem tato technologie se používala již na začátku osmibitové éry. Například vývoj her pro herní konzoli Atari 2600 (Atari Video Computer System neboli Atari VCS) byl prováděn na minipočítači. Ovšem i později některé firmy vyvíjely profesionální software pro Atari, C64 i další osmibitové mikropočítače na výkonnějších strojích, na nichž se prováděl i překlad.
Obrázek 5: Obsah disku či diskety s nainstalovaným překladačem Lattice C.
Dobrým a možná i typickým příkladem jsou právě cross překladače programovacího jazyka C. Tvorbou těchto cross překladačů se zabývala například společnost Manx Software Systems, jejíž překladače céčka (Aztec C) určené pro IBM PC s DOSem i pro osobní mikropočítače Macintosh dokázaly provádět cross překlad na osmibitové mikropočítače Commodore C64 a Apple II. Na chvíli se u Aztec C zastavme, i když přímo nesouvisí s osmibitovými Atari.
Obrázek 6: Lattice C: nástroj provádějící první fázi překladu.
Aztec C totiž byl ve své době velmi úspěšný překladač, jenž existoval jak ve verzi pro osmibitové mikroprocesory (MOS 6502, Zilog Z-80), tak i pro mikroprocesory 16bitové a 32bitové. Tento překladač byl později velmi úspěšný na Amize, kde byl používán, společně s Lattice C, prakticky až do faktického zániku této platformy. Ovšem například na konkurenčním IBM PC jeho sláva netrvala dlouho, především z toho důvodu, že firma Microsoft považovala segment překladačů za poměrně důležitý a snažila se vytlačit jakoukoli konkurenci z trhu (i když ve skutečnosti v té době ještě neměla vlastní céčkový překladač). Společnosti Manx Software Systems se postupně zmenšoval počet platforem, na něž bylo možné překladač prodávat a přechod na podporu vestavěných systémů již přišel dosti pozdě. A právě pro cross překlad se Aztec C může používat dodnes (běží v DOSu, takže dnes vlastně taktéž v emulovaném prostředí).
Obrázek 7: Lattice C: nástroje provádějící další fáze překladu a linkingu.
Podobným stylem byl řešen i Microsoft C původně vytvořený společností, která stála za slavným Lattice C. Ostatně Lattice C byl s velkou pravděpodobností vůbec prvním překladačem céčka pro IBM PC (pochází z roku 1982). Ten byl později převeden i na Amigu, dále se rozšířil i na minipočítače a mainframy společnosti IBM. Firma Microsoft překladač Lattice C nabízela pod svým názvem MSC (Microsoft C) a teprve verze MSC 4.0 byla skutečně vytvořena přímo programátory z Microsoftu. Lattice C byl používán i při portaci aplikací z operačního systému CP/M na DOS (dnes je však možné pouze odhadnout, kolik kódu bylo skutečně napsáno v céčku a kolik kódu vzniklo transformací assembleru).
Obrázek 8: Celé Lattice C se vejde na jedinou disketu (formát HD).
Deep Blue C
Jedním z relativně úspěšných překladačů programovacího jazyka C, který bylo možné spustit přímo na počítačích Atari (nejednalo se tedy o cross překladače), je Deep Blue C. Tento překladač byl dodáván na disketě. Kromě samotného překladače tato disketa obsahovala i linker, několik hlavičkových souborů a jedno či dvě dema (v závislosti na verzi Deep Blue C). Na disketě je i klasický DOS, takže ovládání (kopie, spouštění souborů, listing) nebyly nijak překvapivé a zvládl je každý bohatší atarista (neboli atarista vlastnící disketovou jednotku):
Obrázek 9: Disketa s Deep Blue C obsahuje i DOS.
Obrázek 10: Obsah diskety s Deep Blue C.
Samotný překladač po svém spuštění vyžaduje zadat jméno zdrojového souboru (na disketě). Přeložený program je uložen zpět na disketu, kde je ho možné slinkovat a následně i spustit:
Obrázek 11: Překladač Deep Blue C v akci.
Knihovna pro práci se sprity
Na disketě s překladačem Deep Blue C je možné (kromě dalších více či méně užitečných souborů) nalézt i knihovnu určenou pro práci se sprity, resp. přesněji řečeno s PMG (Player Missile Graphics). Podívejme se, jak tato knihovna vypadá, abychom měli lepší představu o tom, jak vlastně programy psané pro Deep Blue C vypadají (výše zmíněné omezení znakové sady ATASCII atd.). Za povšimnutí stojí především časté volání funkcí poke, peek a move. První dvě zmíněné funkce znají všichni programátoři používající BASIC: POKE je v BASICu příkaz (nikoli funkce) pro zápis bajtu na určenou adresu a naopak PEEK je funkce pro přečtení bajtu ze zadané adresy. A move sice v BASICu většinou chybí, ale její funkce je zřejmá – přesun bloku dat, přičemž zdrojová a cílová oblast se nesmí překrývat:
#define RAMTOP 0x6a
#define PCOLR0 0x2c0
#define SDMCTL 0x22f
#define GRACTL 0xd01d
#define CHBAS 0x2f4
#define PMBASE 0xd407
#define SIZEP0 0xd008
#define HPOSP0 0xd000
#define P0PF 0xd004
#define P0PL 0xd00c
#define HITCLR 0xd01e
#define OCSET 0xe000
#define GRAFP0 0xd00d
#define RANDOM 0xd20a
char *playbase,*charbase,
playsize;
/* initialize players
*/
pmcinit()
$(
int i;
charbase=(peek(RAMTOP)-12)<<8;
poke(RAMTOP,peek(RAMTOP)-16);
graphics(0);
pmgraphics(0);
move(OCSET,charbase,0x400);
poke(CHBAS,charbase>>8);
poke(PMBASE,(charbase+1024)>>8);
$)
/* flush players
*/
pmcflush()
$(
pmgraphics(0);
poke(CHBAS,OCSET>>8);
poke(RAMTOP,peek(RAMTOP)+16);
graphics(0);
$)
/* set up one or two line resolution
* players
*/
pmgraphics(i)
int i;
$(
int j;
if(i)$(
poke(SDMCTL,i==1 ? 0x3a : 0x2a);
poke(GRACTL,2);
playsize=(i==1 ? 8 : 7);
playbase=charbase+(i==1
? 0x800 : 0x600);
for(j=0;j<4;++j)$(
poke(HPOSP0+j,0);
pmclear(j);
$)
$)
else$(
poke(SDMCTL,0x22);
poke(GRACTL,0);
for(j=0;j<4;++j)$(
poke(HPOSP0+j,0);
poke(GRAFP0+j,0);
$)
$)
$)
hitclear()
$(
poke(HITCLR,0);
$)
pmcolor(n,c,i)
char n,c,i;
$(
poke(PCOLR0+n,(c<<4)+i);
$)
pmwidth(n,w)
char n,w;
$(
poke(SIZEP0+n,w);
$)
hitp2pf(f,t)
char f,t;
$(
return (1<<t) & peek(P0PF+f) ? 1 : 0;
$)
hitp2pl(f,t)
char f,t;
$(
return (1<<t) & peek(P0PL+f) ? 1 : 0;
$)
pmclear(n)
char n;
$(
clear(playbase+(n<<playsize),
1<<playsize);
$)
chget(c,s)
char c,*s;
$(
move((c<<3)+charbase,s,8);
$)
choget(c,s)
char c,*s;
$(
move((c<<3)+OCSET,s,8);
$)
chput(c,s)
char c,*s;
$(
move(s,(c<<3)+charbase,8);
$)
plmove(n,x,y,shape)
char n,x,y,*shape;
$(
char *base;
move(shape+1,playbase+(n<<playsize)
+y,*shape);
poke(HPOSP0+n,x);
$)
pladdr(n)
char n;
$(
return playbase+(n<<playsize);
$)
Jednoduché demo dodávané s Deep Blue C
Ještě předtím, než se zaměříme na další překladače jazyka C, si ukažme demo, které bylo taktéž dodáváno na disketě s překladačem Deep Blue C. Jedná se o jednoduché (skutečně velmi jednoduché – neočekávejte žádné zázraky) grafické demo, ve kterém se pomocí klávesnice „vykopne“ míček, který se následně odráží od okrajů obrazovky:
Obrázek 12: Jednoduché grafické demo naprogramované v Deep Blue C.
Obrázek 13: Jednoduché grafické demo naprogramované v Deep Blue C.
Při pohledu do zdrojového kódu dema si povšimněte, že názvy většiny volaných funkcí do značné míry odpovídají příkazům BASICu (práce s grafikou a zvukem). Používají se ale i funkce definované ve výše uvedené knihovně pro práci se sprity:
/*
* Demo the player graphics
* (C) 1982 John H Palevich
*/
char key;
char *ballshape;
main()
$(
char i;
key=copen("K:",'r');
pmcinit();
graphics(7);
pmgraphics(1);
cprints("\fBouncing Ball Demo\n");
cprints("Press arrow keys to move ball,\n");
cprints("Use 'q' to quit.");
color(1);plot(0,0);drawto(159,0);
plot(0,79);drawto(159,79);
color(2);plot(0,0);drawto(0,79);
plot(159,0);drawto(159,79);
for(i=0;i<4;++i)$(
pmcolor(i,4*i,6);
pmwidth(i,0);
$)
ballshape="\20\0\0\0\0\0\6\11\11\11\11\6\0\0\0\0\0";
ballhack();
pmcflush();
cclose(key);
$)
ballhack()
$(
char xbounce,ybounce,i,px,py,dx,dy;
i=dx=dy=0;
px=py=100;
hitclear();
for(;;)$(
i=(i+1)&3;
plmove(i,px,py,ballshape);
if(peek(764)!=255)$(
switch(cgetc(key))$(
case '-':
case '':
dy=-1;
break;
case '=':
case '':
dy=1;
break;
case '':
case '+':
dx=-1;
break;
case '':
case '*':
dx=1;
break;
case 'q':
case 'Q':
sound(0,0,0,0);
sound(1,0,0,0);
return;
default:
dx=dy=0;
$)
$)
px+=dx;
py+=dy;
if((xbounce=hitp2pf(i,1)) |
(ybounce=hitp2pf(i,0)))$(
hitclear();
if(xbounce)$(
/* hit side */
dx=(px<128 ? 1 : -1);
sound(0,50,10,10);
$)
else sound(0,0,0,0);
if(ybounce)$(
/* hit top/bot */
dy=(py<100 ? 1 : -1);
sound(1,100,10,10);
$)
else sound(1,0,0,0);
$)
else$(
sound(0,0,0,0);
sound(1,0,0,0);
$)
$)
$)
Překladač cc65
Pravděpodobně nejznámějším cross překladačem céčka pro mikroprocesory MOS 6502, je překladač programovacího jazyka C, který se jmenuje cc65. Ve skutečnosti se však nejedná pouze o čistý překladač céčka, ale o sadu dalších vývojářských nástrojů, mezi něž patří i cross assembler ca65, dále linker ld65, disassembler da65, simulátor procesorů 6502 sim65 atd. (samotný assembler a linker čtenáři tohoto seriálu velmi dobře znají). Překladač cc65 je možné použít nejenom pro tvorbu aplikací pro osmibitové mikropočítače Atari, ale i pro osmibitové stroje firmy Commodore (VIC20, C64, C128 atd.), osmibitové mikropočítače řady Apple II, herní konzoli NES, konzoli TurboGrafx-16 atd.
Jazyk akceptovaný překladačem cc65 do určité míry odpovídá ISO standardu jazyka C (a tím pádem i původnímu ANSI standardu, dokonce je podporováno několik vlastností z C99); standard je vnucen přepínačem –standard. Existuje však několik dosti podstatných rozdílů a nedostatků cc65, mezi něž patří neexistence datových typů float a double (a tím pádem i celá část céčka, která předepisuje konverze s těmito typy atd.). Dále existuje omezení při definicích funkcí – funkce nemohou vracet struktury ani unie; struktury dokonce není možné předávat hodnotou (což ale většinou nevadí, právě naopak). Kromě toho je sice možné použít modifikátor volatile, ovšem ten nemá žádný podstatný význam (což je v případě mikroprocesorů MOS 6502 a jejich možností pochopitelné). Naproti tomu má či může mít velký vliv modifikátor register, který naopak mnoho moderních překladačů již nepotřebuje či dokonce zcela ignoruje.
Naopak mezi rozšíření cc65 oproti standardu jazyka C (jak ANSI, tak i ISO normy) patří podpora bloků psaných v assembleru, podpora pseudoproměnných A a AX (což je primární registr mikroprocesoru, tedy akumulátor, v případě AX rozšířený na šestnáct bitů přes registr X). Podporovány jsou konstanty zapsané ve dvojkové soustavě (0b101) a použít lze i počítaná goto (což je částečně převzato z GCC).
Knihovna peekpoke pro cc65
Při popisu překladače Deep Blue C jsme se zmínili o funkcích peek a poke, které odpovídají BASICovému příkazu POKE a funkci PEEK. Podobné konstrukce jsou dostupné i pro překladač cc65. V tomto případě se jedná o makra definovaná v hlavičkovém souboru peekpoke.h. Ten vypadá jednoduše, ale v praxi se s použitím těchto maker setkám poměrně často:
/* PEEK and POKE macros for those who want to write BASIC code in C */ ... ... ... #ifndef _PEEKPOKE_H #define _PEEKPOKE_H /*****************************************************************************/ /* Macros */ /*****************************************************************************/ #define POKE(addr,val) (*(unsigned char*) (addr) = (val)) #define POKEW(addr,val) (*(unsigned*) (addr) = (val)) #define PEEK(addr) (*(unsigned char*) (addr)) #define PEEKW(addr) (*(unsigned*) (addr)) /* End of peekpoke.h */ #endif
První demonstrační příklad: změna barvy pozadí s využitím knihovny peekpoke
Konečně se dostáváme k praktickým ukázkám použití překladače cc65. První program, který si ukážeme, je založen na výše zmíněném makru POKE. S využitím tohoto makra nastavíme barvu pozadí v textovém režimu:
#include <peekpoke.h>
#define COLOR2 0x02C6
void main(void) {
POKE(COLOR2, 0x42);
while (1) {
}
}
Překlad prvního demonstračního příkladu: C → assembler → objektový soubor → spustitelný soubor
Pro překlad demonstračního příkladu, jehož zdrojový kód byl ukázán v předchozí kapitole, využijeme soubor Makefile s těmito pravidly:
INCLUDE_PATH=/usr/share/cc65/include
execs := background_color.xex \
poke.xex \
sprite1.xex \
sprite2.xex \
sprite3.xex \
sprite4.xex \
sprite5.xex
all: $(execs)
clean:
rm -f *.o
rm -f *.xex
.PHONY: all clean
%.o: %.c
%.xex: %.o
cl65 -t atari -Oirs $< -o $@ -m $(basename $<).map
%.o: %.asm
ca65 $< -o $@
%.asm: %.c
cc65 -I ${INCLUDE_PATH} -Oirs -t atari $< -o $@
Překlad je nejdříve proveden do assembleru, následně assembler provede další překlad do objektového souboru s koncovkou .o a následně linker vytvoří spustitelný soubor s koncovkou .xex. Ten lze spustit v emulátoru Atari:
Obrázek 14: Změna barvy pozadí standardního textového režimu programem vytvořeným v jazyce C.
Mezivýsledkem je soubor již výše zmíněný soubor zapsaný v assembleru ca65:
;
; File generated by cc65 v 2.18 - Fedora 2.19-12.fc42
;
.fopt compiler,"cc65 v 2.18 - Fedora 2.19-12.fc42"
.setcpu "6502"
.smart on
.autoimport on
.case on
.debuginfo off
.importzp sp, sreg, regsave, regbank
.importzp tmp1, tmp2, tmp3, tmp4, ptr1, ptr2, ptr3, ptr4
.macpack longbranch
.forceimport __STARTUP__
.export _main
; ---------------------------------------------------------------
; void __near__ main (void)
; ---------------------------------------------------------------
.segment "CODE"
.proc _main: near
.segment "CODE"
lda #$42
sta $02C6
L000B: jmp L000B
.endproc
Struktury definované v hlavičkovém souboru atari.h
Modifikace řídicích registrů s využitím makra POKE je sice možná, ale nejedná se o příliš čitelný kód. Ovšem existuje i alternativní řešení. V hlavičkovém souboru atari.h (a v souborech, které jsou do atari.h vkládány při překladu) je definováno několik datových struktur umístěných na konkrétní adresy v adresovém rozsahu osmibitových mikropočítačů Atari. Jedná se především o struktury nazvané OS, GTIA_READ, GTIA_WRITE (ty se v paměti překrývají) a ANTIC:
Struktura OS je umístěna na začátku adresového prostoru a obsahuje proměnné používané operačním systémem:
#define OS (*(struct __os*)0x0000)
struct __os {
...
...
...
unsigned char pcolr0; // = $02C0 1-byte player-missile 0 color/luminance
unsigned char pcolr1; // = $02C1 1-byte player-missile 1 color/luminance
unsigned char pcolr2; // = $02C2 1-byte player-missile 2 color/luminance
unsigned char pcolr3; // = $02C3 1-byte player-missile 3 color/luminance
unsigned char color0; // = $02C4 1-byte playfield 0 color/luminance
unsigned char color1; // = $02C5 1-byte playfield 1 color/luminance
unsigned char color2; // = $02C6 1-byte playfield 2 color/luminance
unsigned char color3; // = $02C7 1-byte playfield 3 color/luminance
unsigned char color4; // = $02C8 1-byte background color/luminance
...
...
...
Struktury GTIA_READ a GTIA_WRITE jsou (nepřekvapivě) mapovány do prostoru registrů čipu GTIA:
#define GTIA_WRITE (*(struct __gtia_write*)0xD000)
struct __gtia_write {
unsigned char hposp0; /* 0x00: horizontal position of player 0 */
unsigned char hposp1; /* 0x01: horizontal position of player 1 */
unsigned char hposp2; /* 0x02: horizontal position of player 2 */
unsigned char hposp3; /* 0x03: horizontal position of player 3 */
unsigned char hposm0; /* 0x04: horizontal position of missile 0 */
unsigned char hposm1; /* 0x05: horizontal position of missile 1 */
unsigned char hposm2; /* 0x06: horizontal position of missile 2 */
unsigned char hposm3; /* 0x07: horizontal position of missile 3 */
unsigned char sizep0; /* 0x08: size of player 0 */
unsigned char sizep1; /* 0x09: size of player 1 */
unsigned char sizep2; /* 0x0A: size of player 2 */
unsigned char sizep3; /* 0x0B: size of player 3 */
unsigned char sizem; /* 0x0C: size of missiles */
unsigned char grafp0; /* 0x0D: graphics shape player 0 (used when ANTIC is not instructed to use DMA; see DMACTL) */
unsigned char grafp1; /* 0x0E: graphics shape player 1 */
unsigned char grafp2; /* 0x0F: graphics shape player 2 */
unsigned char grafp3; /* 0x10: graphics shape player 3 */
unsigned char grafm; /* 0x11: graphics shape missiles */
unsigned char colpm0; /* 0x12: color player and missile 0 */
unsigned char colpm1; /* 0x13: color player and missile 1 */
unsigned char colpm2; /* 0x14: color player and missile 2 */
unsigned char colpm3; /* 0x15: color player and missile 3 */
unsigned char colpf0; /* 0x16: color playfield 0 */
unsigned char colpf1; /* 0x17: color playfield 1 */
unsigned char colpf2; /* 0x18: color playfield 2 */
unsigned char colpf3; /* 0x19: color playfield 3 */
unsigned char colbk; /* 0x1A: color background */
unsigned char prior; /* 0x1B: priority selection */
unsigned char vdelay;
/* 0x1C: vertical delay -- one-line resolution movement of
** vertical position of an object when two line resolution display is enabled
*/
unsigned char gractl; /* 0x1D: stick/paddle latch, p/m control */
unsigned char hitclr; /* 0x1E: clear p/m collision */
unsigned char consol; /* 0x1F: builtin speaker */
};
A konečně struktura ANTIC je mapována do adresového prostoru řídicích registrů čipu ANTIC:
#define ANTIC (*(struct __antic*)0xD400)
struct __antic {
unsigned char dmactl; /* (W) direct memory access control */
unsigned char chactl; /* (W) character mode control */
unsigned char dlistl; /* display list pointer low-byte */
unsigned char dlisth; /* display list pointer high-byte */
unsigned char hscrol; /* (W) horizontal scroll enable */
unsigned char vscrol; /* (W) vertical scroll enable */
unsigned char unuse0; /* unused */
unsigned char pmbase; /* (W) msb of p/m base address (for when DMACTL has player and/or missile DMA enabled) */
unsigned char unuse1; /* unused */
unsigned char chbase; /* (W) msb of character set base address */
unsigned char wsync; /* (W) wait for horizontal synchronization */
unsigned char vcount; /* (R) vertical line counter */
unsigned char penh; /* (R) light pen horizontal position */
unsigned char penv; /* (R) light pen vertical position */
unsigned char nmien; /* (W) non-maskable interrupt enable */
union {
/* (W) ("NMIRES") nmi reset -- clears the interrupt request register;
** resets all of the NMI status together
*/
unsigned char nmires;
/* (R) ("NMIST") nmi status -- holds cause for the NMI interrupt */
unsigned char nmist;
};
};
V hlavičkovém souboru lze taktéž nalézt velké množství více či méně užitečných konstant, například:
/* Playfield modes: */ #define DMACTL_PLAYFIELD_NONE 0x00 #define DMACTL_PLAYFIELD_NARROW 0x01 /* e.g., 32 bytes per scanline with thick borders */ #define DMACTL_PLAYFIELD_NORMAL 0x02 /* e.g., 40 bytes per scanline with normal borders */ #define DMACTL_PLAYFIELD_WIDE 0x03 /* e.g., 48 bytes per scanline with no borders (overscan) */
Druhý demonstrační příklad: nastavení barvy pozadí modifikací prvku struktury OS
Vyzkoušejme si nyní, jak se využije struktura OS při modifikaci barvy pozadí ve standardním textovém režimu počítačů Atari. Je to snadné – postačuje provést modifikaci prvku OS.color2, což je čitelnější, než použití makra POKE:
#include <atari.h>
void main(void) {
OS.color2 = 0x42;
while (1) {
}
}
Překlad provedeme s využitím známého souboru Makefile:
INCLUDE_PATH=/usr/share/cc65/include
%.o: %.c
%.xex: %.o
cl65 -t atari -Oirs $< -o $@ -m $(basename $<).map
%.o: %.asm
ca65 $< -o $@
%.asm: %.c
cc65 -I ${INCLUDE_PATH} -Oirs -t atari $< -o $@
Zajímavé je, že takto upravený program je přeložen naprosto stejným způsobem jako program využívající makro POKE. Z tohoto pohledu tedy za poněkud vyšší úroveň abstrakce „neplatíme“ ani pamětí, ani strojovým časem:
;
; File generated by cc65 v 2.18 - Fedora 2.19-12.fc42
;
.fopt compiler,"cc65 v 2.18 - Fedora 2.19-12.fc42"
.setcpu "6502"
.smart on
.autoimport on
.case on
.debuginfo off
.importzp sp, sreg, regsave, regbank
.importzp tmp1, tmp2, tmp3, tmp4, ptr1, ptr2, ptr3, ptr4
.macpack longbranch
.forceimport __STARTUP__
.export _main
; ---------------------------------------------------------------
; void __near__ main (void)
; ---------------------------------------------------------------
.segment "CODE"
.proc _main: near
.segment "CODE"
lda #$42
sta $02C6
L0028: jmp L0028
.endproc
Vykreslení spritu na obrazovku s využitím makra POKE
Pokračujme dále. Inspirovat se budeme dalším demonstračním příkladem, který jsme již vytvořili jak v assembleru, tak i v programovacím jazyku Action! Tento příklad po svém překladu a spuštění zobrazí sprite (konkrétně prvního hráče) bez specifikace jeho bitmapy. První řešení bude opět postavené na použití makra POKE. Implementace je snadná – potřebujeme pouze modifikovat čtyři registry čipu GTIA a jeden stínový registr s barvou hráče:
#include <peekpoke.h>
#define HPOSP0 0xD000
#define PCOLR0 0x02C0
#define GRAFP0 0xD00D
#define GRACTL 0xD01D
void main(void) {
POKE(HPOSP0, 128);
POKE(PCOLR0, (5 << 4) + 8);
POKE(GRAFP0, 0xFF);
POKE(GRACTL, 0);
while (1) {
}
}
Výsledek po spuštění v emulátoru počítačů Atari:
Obrázek 15: Nepohyblivý sprite zobrazený programem napsaným v jazyce C.
Zajímavé bude prozkoumat výslednou sekvenci instrukcí vygenerovanou céčkovým překladačem. Vše je opět tak jednoduché, že se zde nenachází prostor pro další optimalizace:
;
; File generated by cc65 v 2.18 - Fedora 2.19-12.fc42
;
.fopt compiler,"cc65 v 2.18 - Fedora 2.19-12.fc42"
.setcpu "6502"
.smart on
.autoimport on
.case on
.debuginfo off
.importzp sp, sreg, regsave, regbank
.importzp tmp1, tmp2, tmp3, tmp4, ptr1, ptr2, ptr3, ptr4
.macpack longbranch
.forceimport __STARTUP__
.export _main
; ---------------------------------------------------------------
; void __near__ main (void)
; ---------------------------------------------------------------
.segment "CODE"
.proc _main: near
.segment "CODE"
lda #$80
sta $D000
lda #$58
sta $02C0
lda #$FF
sta $D00D
lda #$00
sta $D01D
L001B: jmp L001B
.endproc
Vykreslení spritu na obrazovku s využitím struktur definovaných v atari.h
Samozřejmě je možné namísto expanzí makra POKE provést buď přímé zápisy do řídicích či stínových registrů (přes ukazatel) nebo využít výše zmíněné struktury OS a GTIA_WRITE. Pokusme se použít datové struktury umístěné na konkrétní adresy v operační paměti. Příklad se do značné míry zjednoduší:
#include <atari.h>
void main(void) {
GTIA_WRITE.hposp0 = 128;
OS.pcolr0 = (5 << 4) + 8;
GTIA_WRITE.grafp0 = 0xFF;
GTIA_WRITE.gractl = 0;
while (1) {
}
}
Zajímat nás opět bude kvalita překladu. A zde opět platí, že se ve výsledném kódu nenachází žádné části, které by bylo možné více optimalizovat:
;
; File generated by cc65 v 2.18 - Fedora 2.19-12.fc42
;
.fopt compiler,"cc65 v 2.18 - Fedora 2.19-12.fc42"
.setcpu "6502"
.smart on
.autoimport on
.case on
.debuginfo off
.importzp sp, sreg, regsave, regbank
.importzp tmp1, tmp2, tmp3, tmp4, ptr1, ptr2, ptr3, ptr4
.macpack longbranch
.forceimport __STARTUP__
.export _main
; ---------------------------------------------------------------
; void __near__ main (void)
; ---------------------------------------------------------------
.segment "CODE"
.proc _main: near
.segment "CODE"
lda #$80
sta $D000
lda #$58
sta $02C0
lda #$FF
sta $D00D
lda #$00
sta $D01D
L0032: jmp L0032
.endproc
Pohyb spritu s jeho synchronizací s hodinami reálného času
V dalším demonstračním příkladu hráče (sprite) „rozpohybujeme“. V nekonečné programové smyčce budeme postupně zvyšovat hodnotu bajtu obsahujícího horizontální pozici hráče. Tuto hodnotu v každé iteraci smyčky zapíšeme do registru HPOSP0 čipu GTIA:
while (1) {
x++;
GTIA_WRITE.hposp0 = x;
}
Pohyb hráče je však příliš rychlý (ano – Atari může být někdy až moc rychlé) a proto do smyčky zařadíme volání procedury WSYNC čekající na začátek vykreslení nového snímku:
while (1) {
x++;
GTIA_WRITE.hposp0 = x;
wsync();
}
Samotná funkce WSYNC detekuje změnu hodnoty reprezentující hodiny „tikající“ na základě zobrazených snímků:
void wsync(void) {
unsigned char clk = OS.rtclok[2];
unsigned char old = clk;
while (old == clk) {
clk = OS.rtclok[2];
}
}
Následuje výpis celého zdrojového kódu tohoto demonstračního příkladu:
#include <atari.h>
void wsync(void) {
unsigned char clk = OS.rtclok[2];
unsigned char old = clk;
while (old == clk) {
clk = OS.rtclok[2];
}
}
void main(void) {
unsigned char x = 128;
GTIA_WRITE.hposp0 = x;
OS.pcolr0 = (5 << 4) + 8;
GTIA_WRITE.grafp0 = 0xFF;
GTIA_WRITE.gractl = 0;
while (1) {
x++;
GTIA_WRITE.hposp0 = x;
wsync();
}
}
Výsledek po překladu a spuštění v emulátoru počítačů Atari:
Obrázek 16: Pohyblivý sprite vytvořený programem napsaným v jazyce C.
Nyní už nebude výsledný strojový kód tak „krásný“ jako v předchozích příkladech. Nepříjemný je především fakt, že lokální proměnné jsou uloženy na zásobníku a tak je nutné obsah zásobníku (nepřímo) adresovat. To lze ovšem napravit. Jak? To si řekneme příště:
;
; File generated by cc65 v 2.18 - Fedora 2.19-12.fc42
;
.fopt compiler,"cc65 v 2.18 - Fedora 2.19-12.fc42"
.setcpu "6502"
.smart on
.autoimport on
.case on
.debuginfo off
.importzp sp, sreg, regsave, regbank
.importzp tmp1, tmp2, tmp3, tmp4, ptr1, ptr2, ptr3, ptr4
.macpack longbranch
.forceimport __STARTUP__
.export _wsync
.export _main
; ---------------------------------------------------------------
; void __near__ wsync (void)
; ---------------------------------------------------------------
.segment "CODE"
.proc _wsync: near
.segment "CODE"
lda $0014
jsr pusha
ldy #$00
lda (sp),y
jsr pusha
jmp L0027
L0046: lda $0014
ldy #$01
sta (sp),y
L0027: ldy #$00
lda (sp),y
iny
cmp (sp),y
beq L0046
jmp incsp2
.endproc
; ---------------------------------------------------------------
; void __near__ main (void)
; ---------------------------------------------------------------
.segment "CODE"
.proc _main: near
.segment "CODE"
lda #$80
jsr pusha
ldy #$00
lda (sp),y
sta $D000
lda #$58
sta $02C0
lda #$FF
sta $D00D
sty $D01D
L003C: ldy #$00
lda (sp),y
clc
adc #$01
sta (sp),y
sta $D000
jsr _wsync
jmp L003C
.endproc
První úprava příkladu: přímá práce s ukazateli
Vzhledem k tomu, jakým způsobem překladač cc65 ve výchozím nastavení pracuje s lokálními proměnnými, asi vám bude zřejmé, že následující úprava (přímé použití ukazatele) situaci nijak nezlepší, spíše naopak:
#include <atari.h>
void wsync(void) {
unsigned char *clk = 0x14;
unsigned char old = *clk;
while (old == *clk) {
}
}
void main(void) {
unsigned char x = 128;
GTIA_WRITE.hposp0 = x;
OS.pcolr0 = (5 << 4) + 8;
GTIA_WRITE.grafp0 = 0xFF;
GTIA_WRITE.gractl = 0;
while (1) {
x++;
GTIA_WRITE.hposp0 = x;
wsync();
}
}
Při pohledu na vygenerovaný strojový kód je zřejmé, že funkce wsync skutečně není přeložena příliš optimálně (zejména v porovnání s výsledkem, který dokáže vygenerovat překladač jazyka Action! – o to je situace směšnější):
;
; File generated by cc65 v 2.18 - Fedora 2.19-12.fc42
;
.fopt compiler,"cc65 v 2.18 - Fedora 2.19-12.fc42"
.setcpu "6502"
.smart on
.autoimport on
.case on
.debuginfo off
.importzp sp, sreg, regsave, regbank
.importzp tmp1, tmp2, tmp3, tmp4, ptr1, ptr2, ptr3, ptr4
.macpack longbranch
.forceimport __STARTUP__
.export _wsync
.export _main
; ---------------------------------------------------------------
; void __near__ wsync (void)
; ---------------------------------------------------------------
.segment "CODE"
.proc _wsync: near
.segment "CODE"
lda #$14
jsr pusha0
ldy #$01
lda (sp),y
sta ptr1+1
lda (sp,x)
sta ptr1
lda (ptr1,x)
jsr pusha
L0023: ldx #$00
lda (sp,x)
jsr pusha0
ldy #$04
lda (sp),y
sta ptr1+1
dey
lda (sp),y
sta ptr1
lda (ptr1,x)
jsr tosicmp0
beq L0023
jmp incsp3
.endproc
; ---------------------------------------------------------------
; void __near__ main (void)
; ---------------------------------------------------------------
.segment "CODE"
.proc _main: near
.segment "CODE"
lda #$80
jsr pusha
ldy #$00
lda (sp),y
sta $D000
lda #$58
sta $02C0
lda #$FF
sta $D00D
sty $D01D
L0036: ldy #$00
lda (sp),y
clc
adc #$01
sta (sp),y
sta $D000
jsr _wsync
jmp L0036
.endproc
Druhá úprava příkladu: využití makra PEEK
Na závěr si vyzkoušejme, jak se situace zlepší či naopak zhorší, pokud budeme volat makro PEEK namísto přímého přístupu do paměti (resp. stínového registru) přes ukazatel. Stále pracujeme s lokální proměnnou, což nám naznačí, že výsledek nebude optimální (pokud nezměníme chování překladače):
#include <atari.h>
#include <peekpoke.h>
#define RTCLK 0x14
void wsync(void) {
unsigned char old = PEEK(RTCLK);
while (old == PEEK(RTCLK)) {
}
}
void main(void) {
unsigned char x = 128;
GTIA_WRITE.hposp0 = x;
OS.pcolr0 = (5 << 4) + 8;
GTIA_WRITE.grafp0 = 0xFF;
GTIA_WRITE.gractl = 0;
while (1) {
x++;
GTIA_WRITE.hposp0 = x;
wsync();
}
}
Vygenerovaná sekvence instrukcí je opticky kratší a (pokud ignorujeme volání subrutiny pusha) je i vlastní programová smyčka pro synchronizaci vytvořena uspokojivě. Na druhou stranu je zde prostor pro další optimalizace (tak, jak kód vygeneroval překladač, by ho neumělá inteligence nevytvořila):
;
; File generated by cc65 v 2.18 - Fedora 2.19-12.fc42
;
.fopt compiler,"cc65 v 2.18 - Fedora 2.19-12.fc42"
.setcpu "6502"
.smart on
.autoimport on
.case on
.debuginfo off
.importzp sp, sreg, regsave, regbank
.importzp tmp1, tmp2, tmp3, tmp4, ptr1, ptr2, ptr3, ptr4
.macpack longbranch
.forceimport __STARTUP__
.export _wsync
.export _main
; ---------------------------------------------------------------
; void __near__ wsync (void)
; ---------------------------------------------------------------
.segment "CODE"
.proc _wsync: near
.segment "CODE"
lda $0014
jsr pusha
ldy #$00
L0044: lda (sp),y
cmp $0014
beq L0044
jmp incsp1
.endproc
; ---------------------------------------------------------------
; void __near__ main (void)
; ---------------------------------------------------------------
.segment "CODE"
.proc _main: near
.segment "CODE"
lda #$80
jsr pusha
ldy #$00
lda (sp),y
sta $D000
lda #$58
sta $02C0
lda #$FF
sta $D00D
sty $D01D
L0039: ldy #$00
lda (sp),y
clc
adc #$01
sta (sp),y
sta $D000
jsr _wsync
jmp L0039
.endproc
Repositář s demonstračními příklady
Všechny demonstrační příklady, s nimiž jsme se v předchozích článcích i v článku dnešním seznámili a které jsou určeny pro překlad s využitím assembleru ca65, jsou dostupné, jak je zvykem, na GitHubu. V tabulce níže jsou uvedeny odkazy na jednotlivé zdrojové kódy příkladů psané v assembleru i „listingy“ vygenerované samotným assemblerem, ze kterých je patrné, jakým způsobem se jednotlivé příklady přeložily do výsledného XEX souboru:
Odkazy na Internetu
- MOS 6502 instruction set
http://www.6502.org/users/obelisk/6502/instructions.html - EXE File Format Description
https://gury.atari8.info/refs/file_formats_exe.php - XEX Filter – A toolkit to analyze and manipulate Atari binary files
https://www.vitoco.cl/atari/xex-filter/index.html - chkxex.py
https://raw.githubusercontent.com/seban-slt/tcx_tools/refs/heads/master/chkxex.py - ca65 Users Guide
https://cc65.github.io/doc/ca65.html - cc65 Users Guide
https://cc65.github.io/doc/cc65.html - ld65 Users Guide
https://cc65.github.io/doc/ld65.html - da65 Users Guide
https://cc65.github.io/doc/da65.html - Překladače jazyka C pro historické osmibitové mikroprocesory
https://www.root.cz/clanky/prekladace-jazyka-c-pro-historicke-osmibitove-mikroprocesory/ - Překladače programovacího jazyka C pro historické osmibitové mikroprocesory (2)
https://www.root.cz/clanky/prekladace-programovaciho-jazyka-c-pro-historicke-osmibitove-mikroprocesory-2/ - Getting Started Programming in C: Coding a Retro Game with C Part 2
https://retrogamecoders.com/getting-started-with-c-cc65/ - NES game development in 6502 assembly – Part 1
https://kibrit.tech/en/blog/nes-game-development-part-1 - 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/ - Minimal NES example using ca65
https://github.com/bbbradsmith/NES-ca65-example - List of 6502-based Computers and Consoles
https://www.retrocompute.co.uk/list-of-6502-based-computers-and-consoles/ - 6502 – the first RISC µP
http://ericclever.com/6500/ - 3 Generations of Game Machine Architecture
http://www.atariarchives.org/dev/CGEXPO99.html - “Hello, world” from scratch on a 6502 — Part 1
https://www.youtube.com/watch?v=LnzuMJLZRdU - A Tour of 6502 Cross-Assemblers
https://bumbershootsoft.wordpress.com/2016/01/31/a-tour-of-6502-cross-assemblers/ - Adventures with ca65
https://atariage.com/forums/topic/312451-adventures-with-ca65/ - example ca65 startup code
https://atariage.com/forums/topic/209776-example-ca65-startup-code/ - 6502 PRIMER: Building your own 6502 computer
http://wilsonminesco.com/6502primer/ - 6502 Instruction Set
https://www.masswerk.at/6502/6502_instruction_set.html - Chip Hall of Fame: MOS Technology 6502 Microprocessor
https://spectrum.ieee.org/tech-history/silicon-revolution/chip-hall-of-fame-mos-technology-6502-microprocessor - Single-board computer
https://en.wikipedia.org/wiki/Single-board_computer - www.6502.org
http://www.6502.org/ - 6502 PRIMER: Building your own 6502 computer – clock generator
http://wilsonminesco.com/6502primer/ClkGen.html - Great Microprocessors of the Past and Present (V 13.4.0)
http://www.cpushack.com/CPU/cpu.html - Jak se zrodil procesor?
https://www.root.cz/clanky/jak-se-zrodil-procesor/ - Osmibitové mikroprocesory a mikrořadiče firmy Motorola (1)
https://www.root.cz/clanky/osmibitove-mikroprocesory-a-mikroradice-firmy-motorola-1/ - 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/ - 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/ - 25 Microchips That Shook the World
https://spectrum.ieee.org/tech-history/silicon-revolution/25-microchips-that-shook-the-world - Comparison of instruction set architectures
https://en.wikipedia.org/wiki/Comparison_of_instruction_set_architectures - How To Start Learning Atari 8 Bit Assembly For Free
https://forums.atariage.com/topic/300732-how-to-start-learning-atari-8-bit-assembly-for-free/ - WUDSN (Demo Group)
https://www.wudsn.com/ - Machine Language For Beginners
https://www.atariarchives.org/mlb/ - Assembly language: all about I/O
https://www.atarimagazines.com/v3n8/AllAbout_IO.html - Sedmdesátiny assemblerů: lidsky čitelný strojový kód
https://www.root.cz/clanky/sedmdesatiny-assembleru-lidsky-citelny-strojovy-kod/ - Color names
https://atariwiki.org/wiki/Wiki.jsp?page=Color%20names - ATASCII
https://en.wikipedia.org/wiki/ATASCII - Put characters in display ram isn't ATASCII?
https://forums.atariage.com/topic/359973-put-characters-in-display-ram-isnt-atascii/ - ATASCII And Internal Character Code Values
https://www.atariarchives.org/mapping/appendix10.php - Reading ATASCII from the keyboard in assembly
https://forums.atariage.com/topic/361733-reading-atascii-from-the-keyboard-in-assembly/ - Why does the 6502 JSR instruction only increment the return address by 2 bytes?
https://retrocomputing.stackexchange.com/questions/19543/why-does-the-6502-jsr-instruction-only-increment-the-return-address-by-2-bytes - Pushing return address to stack off by 1 byte
https://forums.atariage.com/topic/378206-pushing-return-address-to-stack-off-by-1-byte/ - Intel x86 documentation has more pages than the 6502 has transistors
https://www.righto.com/2013/09/intel-x86-documentation-has-more-pages.html - Clearing a Section of Memory
http://www.6502.org/source/general/clearmem.htm - Practical Memory Move Routines by Bruce Clark
http://www.6502.org/source/general/memory_move.html - 6502 Assembly Programming Guide
https://neumont-gamedev.github.io/posts/retrogamedev-6502-guide/ - Off-by-one error
https://en.wikipedia.org/wiki/Off-by-one_error - 6502 cycle times
https://www.nesdev.org/wiki/6502_cycle_times - Atari TIA
http://www.atarihq.com/danb/tia.shtml - TIA Playfield
http://www.atarihq.com/danb/TIA/Playfield.shtml - Atari Inc.:
ANTIC C012296 (NTSC) Revision D
Atari Incorporated, Sunnyvale CA, 1982 - Atari Inc.:
GTIA C014805 (NTSC) Revision A
Atari Incorporated, Sunnyvale CA, 1982 - Atari 5200
http://www.atariage.com/software_search.html?SystemID=5200 - Atari 5200 Hardware and Accessories
http://www.atariage.com/5200/archives/hardware.html - Atari 5200 Screenshots
http://www.atariage.com/system_items.html?SystemID=5200&ItemTypeID=SCREENSHOT - History of video game consoles (second generation): Wikipedia
http://en.wikipedia.org/wiki/History_of_video_game_consoles_(second_generation) - Atari 5200: Wikipedia
http://en.wikipedia.org/wiki/Atari_5200 - Player-Missile Graphics
https://www.atariarchives.org/agagd/chapter5.php - Sprite (computer graphics)
https://en.wikipedia.org/wiki/Sprite_(computer_graphics) - Atari Graphics Demonstrations by Underground Software, 1985 | Atari 8 bit Demo
https://www.youtube.com/watch?v=h7N9EYSyCkw - Atari 8-bit Display List Interrupts: A Complete(ish) Tutorial
https://playermissile.com/dli_tutorial/ - Atari Assembler Editor manual
https://atariwiki.org/wiki/attach/Atari%20Assembler%20Editor/ATARI%20Assembler%20Editor%20User-s%20Manual-OCR.pdf - Atari 8-bit Fine Scrolling: A Complete(ish) Tutorial
https://playermissile.com/scrolling_tutorial/index.html - Atari Fine Scrolling
https://www.atarimagazines.com/compute/issue67/338_1_Atari_Fine_Scrolling.php - CTIA / GTIA Pinout Diagram
https://user.xmission.com/~trevin/atari/gtia_pinout.html - GTIA Modes
https://page6.org/archive/issue_02/page_10.htm - 56 graphic modes
https://www.atari800xl.eu/docs/kb/kb-hardware-0005-atari-8bit-56-graphic-modes.html - UNLOCKING THE 56 GRAPHIC MODES
https://www.atarimagazines.com/v3n5/allmodes.html - GTIA Modes 9, 10 & 11 + ANTIC data and color clocks
https://forums.atariage.com/topic/366256-gtia-modes-9–10–11-antic-data-and-color-clocks/ - ANTIC, GTIA and timing info
https://atarimax.com/jindroush.atari.org/atanttim.html - 6.10 Cycle timing
https://github.com/AnimaInCorpore/A8E/blob/main/AHRM/6.%20CTIA-GTIA/10.%20Cycle%20timing.md - Doug Neubauer
https://en.wikipedia.org/wiki/Doug_Neubauer - POKEY
https://en.wikipedia.org/wiki/POKEY - Inside the Atari 800XL
https://www.goto10retro.com/p/800xl-inside - Grayscale Project – „Jam Session“ – Chiptune Visualization / Atari SAP
https://www.youtube.com/watch?v=Qx-AHgvwrHo&list=PL92E73FD91764173B - POKEY MUSIC ( ATARI XL / XE ) =+ MUSIC FROM POLAND += DEMO
https://www.youtube.com/watch?v=5PswfMjMop4&list=RD5PswfMjMop4&start_radio=1 - Atari.org
http://www.atari.org/ - Atari POKEY
http://www.absoluteastronomy.com/topics/Atari_POKEY - Chiptune
http://www.absoluteastronomy.com/topics/Chiptune - ASAP – Another Slight Atari Player
http://asap.sourceforge.net/ - Atari SAP music archive
http://asma.atari.org/ - RASTER Music Tracker (RMT)
http://raster.infos.cz/atari/rmt/rmt.htm - POKEY explorer
https://github.com/ivop/pokey-explorer - POKEY C012294 Documentation
https://7800.8bitdev.org/index.php/POKEY_C012294_Documentation - Pokey Registers
https://user.xmission.com/~trevin/atari/pokey_regs.html - POKEY CO12294
http://krap.pl/mirrorz/atari/homepage.ntlworld.com/kryten_droid/Atari/800XL/atari_hw/pokey.htm - De Re Atari: SOUND
https://www.atariarchives.org/dere/chapt07.php#H7_3_5 - Paddle (game controller)
https://en.wikipedia.org/wiki/Paddle_(game_controller) - Interfacing Your Atari
https://www.atariarchives.org/creativeatari/Interfacing_Your_Atari.php - Atari SIO
https://en.wikipedia.org/wiki/Atari_SIO - Atari Chips
https://atarimax.com/jindroush.atari.org/achip.html - Table 4: Keyboard Code Index, KEYCODE: ATASCII
https://www.atariarchives.org/c3ba/kcindex.php - SIO2PC
http://raster.atariportal.cz/hw/sio2pc.htm - Using Serial Input/Output
https://www.atarimagazines.com/compute/issue64/insight_atari.php - SIO ROUTINES
https://www.atariarchives.org/mmm/sio.php - ATARI 8 bit Serial Input/Output (SIO)
https://allpinouts.org/pinouts/connectors/serial/atari-8-bit-serial-input-output-sio/ - SIOspecs
https://archive.org/details/SIOspecs - Interview with Joe Decuir
http://www.atarimuseum.com/articles/joedecuir.html - USB AND OS/2
http://www.os2voice.org/VNL/past_issues/VNL1199H/vnewsf5.htm - Standards and specs: The ins and outs of USB
http://www.ibm.com/developerworks/power/library/pa-spec7.html - USB Class Codes
http://www.usb.org/developers/defined_class - Universal Serial Bus
http://en.wikipedia.org/wiki/Usb - SMBus.org
http://www.smbus.org - SMBus FAQ
http://www.smbus.org/faq/faq.htm - System Management Bus
http://en.wikipedia.org/wiki/System_Management_Bus - SMBus Specifications
http://www.smbus.org/specs - SMBus Control Method Interface Specification Version 1.0
http://www.smbus.org/specs/smbus_cmi10.pdf - System Management Bus (SMBus) Specification Version 2.0
http://www.smbus.org/specs/smbus20.pdf - Atari Connection Machine
http://acm.atariportal.cz/s/con8bit/8bits - Atari Program Recorder
https://en.wikipedia.org/wiki/Atari_Program_Recorder - THE ATARI CASSETTE
https://www.atariarchives.org/dere/chaptC.php - Turbo-2000
https://www.atari-800.cz/turbo-2000 - Atari XC12 FSK Decoder
https://zeninstruments.blogspot.com/2024/08/atari-xc12-fsk-decoder.html - Clock recovery
https://en.wikipedia.org/wiki/Clock_recovery - Jiří Richter (programátor)
https://cs.wikipedia.org/wiki/Ji%C5%99%C3%AD_Richter_(program%C3%A1tor) - Spectrum tape interface
https://sinclair.wiki.zxnet.co.uk/wiki/Spectrum_tape_interface - How Tape Loading Works
https://lemmings.info/how-tape-loading-works/ - CIO Reference
https://atarifoundry.com/docs/forge/advanced-topics/cio-reference - The central input-output system in Atari computers
https://www.atariarchives.org/alp/chapter_9.php - ATR: chpt.1: Central Input/Output (CIO)
https://www.atariarchives.org/cfn/12/02/0010.php - NOTE and POINT
https://atarifoundry.com/docs/forge/advanced-topics/note-point - Customizing the Atari Operating System Device Handlers: Part 1
https://www.atarimagazines.com/v8n2/customhandlers.html - Customizing the Atari Operating System Device Handlers: Part II
https://www.atarimagazines.com/v8n3/OperatingSystemHandlers.html - Spectrum tape interface
https://sinclair.wiki.zxnet.co.uk/wiki/Spectrum_tape_interface - Turbo 2000
http://wiki.oldcomp.cz/index.php?title=Turbo_2000 - Původní manuál k systému Turbo 2000
http://wiki.oldcomp.cz/images/6/65/Zak_praha_1987_p2_turbo2000.pdf - Mark and space
https://en.wikipedia.org/wiki/Mark_and_space - Obdélníkový průběh
https://cs.wikipedia.org/wiki/Obd%C3%A9ln%C3%ADkov%C3%BD_pr%C5%AFb%C4%9Bh - All you ever wanted to know about: Atari Cartridges
https://atariwiki.org/wiki/Wiki.jsp?page=Cartridges - Manuál k MAC/65
https://data.atariwiki.org/DOC/MAC-65_and_BUG-65_Rev._1.1-OSS-OCR.pdf - Manuál k DDT
https://atariwiki.org/wiki/attach/DDT/DDT_1.0_Manual.pdf - MAC/65 na Atari wiki
https://www.atariwiki.org/wiki/Wiki.jsp?page=Mac65 - Madass – home page
https://mads.atari8.info/ - Mad-Assembler (GitHub)
https://github.com/tebe6502/Mad-Assembler - The 6502/65C02/65C816 Instruction Set Decoded
https://llx.com/Neil/a2/opcodes.html - Action
https://atariwiki.org/wiki/Wiki.jsp?page=Action#top - Action: manuál
https://www.atarimania.com/documents/atari-action-reference-manual.pdf - Getting in on the Action 1
https://atariwiki.org/wiki/Wiki.jsp?page=Getting%20in%20on%20the%20Action%201 - Getting in on the Action 2
https://atariwiki.org/wiki/Wiki.jsp?page=Getting%20in%20on%20the%20Action%202 - Category:Action! na RosettaCode
https://rosettacode.org/wiki/Category:Action! - Deep Blue C Compiler
https://www.atarimania.com/games/atari-400–800-xl-xe-utilities-deep-blue-c-compiler-12478 - DEEP BLUE C Manual
https://www.atariarchives.org/APX/showdocs.php?cat=20166 - C/65
https://www.atarimania.com/games/atari-400–800-xl-xe-utilities-c-65–19204 - Getting Started Programming in C: Coding a Retro Game with C Part 2
https://retrogamecoders.com/getting-started-with-c-cc65/
