Section courante

A propos

Section administrative du site

Introduction

En 1977, une nouvelle génération de jeux fait son apparition : les consoles de jeux vidéo ! Bien qu'il ne s'agit pas de la première console vidéo, la première ayant été inventé 2 ans plus, mais il s'agit de la première console de jeux ayant eu du succès sur le marché des consommateurs. Ainsi, il s'est vendu plus de 27 millions de consoles de jeux Atari VCS 2600. Le nom d'Atari resta graver des les mémoires comme une console de jeux vidéo ayant marqué toute une génération de jeunes joueurs.

Fiche technique

Voici les caractéristiques du système «VCS 2600» d'Atari :

Spécification Description
Fabricant Atari
Modèle VCS 2600
Date de fabrication 1977 à 1992
Catégorie Console de jeux
Microprocesseur MOS 6507 à 1,19 MHz
Mémoire vive installée (RAM) 128 octets
Mémoire morte installée (ROM) 4 Ko maximum
Affichage Graphique 160 x 192 pixels (NTSC) ou 160 x 228 pixels (PAL) en 128 couleurs (16 couleurs avec 8 niveaux d'intensité).
Son 2 canaux mono. (puce TIA 6532)
Port d'entrée/sortie 2 connecteurs de contrôle, entrée d'alimentation, sortie TV RF
Média Cartouche
Contrôleur 2 manettes de jeux avec un bouton chacun

Historique

L'Atari 2600 trouve son origine dans le Video Computer System (VCS), une console de jeux vidéo commercialisée par Atari en 1977. À son lancement, la machine n'est donc pas encore officiellement connue sous le nom d'Atari 2600. Le nombre 2600 provient du numéro associé au projet et au modèle de la console, CX2600. Au début des années 1980, Atari adopte progressivement Atari 2600 comme nom commercial afin notamment de mieux distinguer cette machine des nouvelles consoles de la société, en particulier l'Atari 5200.

L'architecture de l'Atari 2600 repose principalement sur trois circuits intégrés. Le premier est le MOS Technology 6507, une version simplifiée du microprocesseur 6502 utilisant un bus d'adresses externe réduit. Le deuxième est le TIA (Television Interface Adaptor), circuit spécialement conçu pour produire l'image et le son ainsi que pour participer à la gestion des contrôleurs de jeu. Le troisième est le MOS Technology 6532 RIOT (RAM-I/O-Timer), qui fournit notamment la mémoire vive de la console, des lignes d'entrée/sortie et une minuterie programmable. Cette architecture extrêmement compacte impose d'importantes contraintes aux programmeurs, mais permet également de réduire le coût de fabrication de la machine.

Les premières cartouches destinées à l'Atari 2600 contiennent généralement seulement 2 Kio de mémoire ROM. L'espace d'adressage directement accessible aux cartouches étant limité, les développeurs mettent progressivement au point différentes techniques permettant de dépasser les capacités prévues à l'origine. La plus importante est la commutation bancaire de mémoire, ou bank switching, qui permet de diviser une ROM plus importante en plusieurs banques et de sélectionner dynamiquement celle qui doit être visible par le processeur. Cette technique rend possibles des cartouches de capacité bien supérieure aux premiers jeux de 2 Kio.

Certaines cartouches plus évoluées incorporent également du matériel supplémentaire, pouvant aller de circuits spécialisés à de véritables coprocesseurs. Une partie des calculs peut ainsi être effectuée directement dans la cartouche plutôt que par le seul 6507 de la console. Combinées à la commutation bancaire et à l'amélioration progressive des techniques de programmation du TIA, ces extensions permettent de produire des jeux considérablement plus ambitieux que ceux imaginés lors de la conception initiale du VCS.

La longévité commerciale de la plateforme est particulièrement remarquable. Apparue en 1977, elle est commercialisée pendant de nombreuses années sous différentes formes et révisions matérielles, notamment avec des boîtiers et des conceptions internes simplifiés. Atari continue ainsi à proposer des variantes de la plateforme jusqu'au début des années 1990, bien après l'apparition de générations de consoles techniquement beaucoup plus puissantes.

L'histoire de l'Atari 2600 ne s'arrête toutefois pas avec la fin de sa commercialisation officielle. La console demeure une plateforme appréciée par la communauté homebrew, qui continue à développer de nouveaux jeux et programmes pour le matériel original. Les programmeurs modernes exploitent non seulement les techniques historiques de programmation du 6507 et du TIA, mais également des systèmes avancés de commutation bancaire et, dans certaines cartouches modernes, des coprocesseurs beaucoup plus puissants. L'Atari 2600 constitue ainsi un exemple particulièrement intéressant d'une architecture dont les possibilités réelles ont largement dépassé les objectifs et les contraintes envisagés lors de sa conception dans les années 1970.

Modèles et révisions

TIA CO10444/CO11903 et variantes régionales différent. Le codage couleur et les oscillateurs ne sont pas interchangeables sans modification.

Architecture fonctionnelle

L'architecture fonctionnelle de l'Atari 2600 repose principalement sur la coopération de trois circuits : le MOS Technology 6507, le TIA (Television Interface Adaptor) et le RIOT 6532 (RAM-I/O-Timer). Le 6507 constitue le processeur central de la console et exécute principalement le programme contenu dans la mémoire ROM de la cartouche. Contrairement à un ordinateur disposant d'un système d'exploitation chargé en mémoire, l'Atari 2600 exécute directement le code fourni par la cartouche. Le processeur communique avec les autres composants de la machine par l'intermédiaire de son bus et doit également consacrer une partie importante de son temps à la génération de l'affichage.

Le TIA constitue le cour des fonctions audiovisuelles de la console. Il est responsable de la production du signal vidéo envoyé au téléviseur ainsi que de la génération du son. Il intervient également dans la lecture de certaines entrées provenant des contrôleurs. Les entrées analogiques, notamment celles utilisées par les contrôleurs de type paddle, ainsi que certaines entrées associées aux boutons, sont accessibles par l'intermédiaire de ses registres. Le TIA ne fonctionne cependant pas comme une carte graphique moderne : il ne possède ni mémoire vidéo suffisamment importante pour conserver une image complète, ni véritable framebuffer représentant l'ensemble de l'écran.

Le RIOT 6532 complète cette architecture en regroupant plusieurs fonctions essentielles. Il fournit les 128 octets de mémoire vive (RAM) directement disponibles dans la console, utilisés notamment pour conserver les variables, les états du jeu, les compteurs et la pile du processeur. Le RIOT comporte également des ports parallèles d'entrée/sortie, employés entre autres pour lire certaines commandes des manettes de jeux et les commutateurs de la console. Enfin, il possède un minuterie programmable, particulièrement utile pour mesurer des intervalles de temps sans devoir utiliser en permanence le processeur pour effectuer le comptage.

Le connecteur de cartouche expose directement une partie importante du bus du 6507. Celui-ci possède un bus de données de 8 bits, permettant de transférer un octet à la fois, et seulement treize lignes d'adresse externes, de A0 à A12. Ces treize lignes permettent théoriquement de distinguer 8192 adresses, soit un espace physique de 8 Kio. Cependant, le décodage matériel de l'Atari 2600 organise cet espace avec des zones et des miroirs, et la fenêtre normalement destinée à la ROM de la cartouche ne représente que 4 Kio à la fois. Cette caractéristique explique pourquoi les cartouches dépassant cette capacité doivent employer des mécanismes supplémentaires, notamment la commutation bancaire (bank switching).

La particularité la plus importante de l'architecture vidéo vient du fonctionnement du TIA. Celui-ci ne mémorise pas une image complète avant de l'envoyer au téléviseur. Il dispose plutôt d'un petit ensemble de registres décrivant des éléments graphiques simples, comme les motifs des joueurs, des missiles, de la balle et du terrain de jeu, auxquels s'ajoutent des registres de couleurs et différents compteurs horizontaux. À mesure que le faisceau vidéo progresse horizontalement, le TIA produit directement les pixels correspondant à l'état courant de ces registres.

Le processeur doit donc travailler en étroite synchronisation avec le balayage vidéo. Pendant la construction de l'image, le 6507 modifie les couleurs, les motifs graphiques et certaines positions au moment précis où ces changements doivent devenir visibles. Une modification effectuée quelques cycles trop tôt ou trop tard peut apparaître à un emplacement différent sur la ligne affichée. Le programme ne se contente donc pas de préparer une image puis de demander au TIA de l'afficher : il participe activement à sa construction, ligne par ligne et souvent cycle par cycle.

Cette méthode de fonctionnement est couramment décrite par l'expression « racing the beam », c'est-à-dire « faire la course avec le faisceau ». Elle constitue l'une des caractéristiques fondamentales de la programmation de l'Atari 2600. Une grande partie du programme d'affichage, souvent appelée kernel vidéo, doit être soigneusement synchronisée afin d'écrire dans les registres du TIA au moment approprié. Cette contrainte rend la programmation graphique particulièrement exigeante, mais elle permet également aux développeurs expérimentés d'obtenir des effets visuels beaucoup plus complexes que ce que le petit nombre de registres graphiques du TIA pourrait laisser supposer.

Horloges NTSC, PAL et SECAM

Le fonctionnement de l'Atari 2600 dépend étroitement du standard de télévision auquel la console est destinée. Les versions NTSC, PAL et SECAM ne se distinguent donc pas uniquement par leur manière de représenter les couleurs : les fréquences d'horloge, le nombre de lignes composant une image et la cadence d'affichage peuvent également varier. Ces différences ont une importance particulière sur l'Atari 2600, puisque le programme exécuté par le 6507 participe directement à la construction du signal vidéo produit par le TIA. Le programme doit ainsi respecter précisément la temporisation correspondant à la version matérielle de la console.

Sur une console NTSC, l'horloge couleur fonctionne à une fréquence d'environ 3,579545 MHz. Cette fréquence constitue une référence fondamentale pour le TIA et la génération du signal vidéo. L'horloge du processeur 6507 est obtenue en divisant cette fréquence par trois. Le processeur fonctionne donc à environ 1,193182 MHz. Autrement dit, pendant qu'un cycle du 6507 s'écoule, trois périodes de l'horloge couleur, généralement appelées color clocks, sont produites.

Une ligne vidéo NTSC complète occupe 228 color clocks, ce qui correspond exactement à 76 cycles du processeur puisque chaque cycle CPU représente trois color clocks. Sur ces 228 périodes, environ 68 color clocks correspondent à la partie de synchronisation et de blank horizontal, c'est-à-dire la portion de la ligne qui n'est pas destinée à l'image visible. Les 160 color clocks restants constituent la partie normalement visible de la ligne. Cette relation entre 228 color clocks et 76 cycles CPU est fondamentale pour la programmation du TIA, car les modifications des registres graphiques doivent souvent être effectuées à des cycles très précis.

Une trame NTSC classique produite par un jeu Atari 2600 comporte généralement 262 lignes. Une organisation courante consiste à commencer par 3 lignes de VSYNC, qui fournissent la synchronisation verticale. Elles sont suivies par 37 autres lignes de VBLANK, pendant lesquelles aucune image de jeu visible n'est normalement produite. Viennent ensuite 192 lignes visibles, qui constituent la zone principale dans laquelle le jeu dessine son image. Enfin, environ 30 lignes d'overscan terminent la trame. Le calcul donne ainsi 3 + 37 + 192 + 30, soit un total de 262 lignes.

Cette organisation n'est cependant pas imposée par un véritable contrôleur vidéo de trames. Le TIA ne compte pas automatiquement les lignes pour déterminer la fin d'une image : c'est essentiellement le logiciel qui décide quand produire les différentes phases du signal. Un programme peut donc générer un nombre de lignes différent de la valeur habituelle de 262. Certains jeux commerciaux et programmes expérimentaux utilisent effectivement des nombres de lignes légèrement différents. Un téléviseur cathodique peut parfois tolérer ces écarts, tandis qu'un téléviseur moderne, un convertisseur vidéo ou un dispositif de capture peut être beaucoup plus exigeant et perdre temporairement ou complètement la synchronisation.

La version PAL de l'Atari 2600 utilise des fréquences et une cadence différentes de celles du modèle NTSC. Une trame PAL comporte généralement environ 312 lignes et fonctionne à une fréquence proche de 50 Hz, contre environ 60 Hz pour le NTSC. Les recommandations couramment données dans le Stella Programmer's Guide utilisent une organisation de 48 lignes de blank comprenant la synchronisation, suivies de 228 lignes visibles et de 36 lignes d'overscan. L'ensemble représente donc 48 + 228 + 36, soit 312 lignes par trame.

Le passage du NTSC au PAL ne consiste donc pas simplement à remplacer quelques valeurs de couleurs. Le nombre de lignes visibles disponible est différent et la cadence générale de la machine change également. Un jeu conçu sans adaptation peut ainsi présenter une hauteur d'image différente, fonctionner à une vitesse différente ou avoir son gameplay affecté par le changement de fréquence. Les temporisations liées aux déplacements, aux animations ou à la logique du jeu doivent donc être examinées lorsqu'un programme est adapté d'un standard à l'autre.

Le son est lui aussi concerné. Les circuits audio du TIA sont liés aux horloges de la machine ; les différences de fréquence entre les versions régionales peuvent donc modifier la hauteur sonore (pitch) produite pour une même configuration des registres audio. Une conversion correcte d'un jeu doit par conséquent tenir compte non seulement du signal vidéo et de la palette, mais également des temporisations du programme et, lorsque nécessaire, des paramètres utilisés pour produire les sons.

La version SECAM introduit une contrainte supplémentaire concernant les couleurs. Le TIA utilisé avec ce standard offre une palette fortement réduite par rapport aux possibilités des versions NTSC et PAL. Une adaptation SECAM ne peut donc pas être réalisée correctement en transposant mécaniquement les valeurs de couleurs d'une version NTSC. Il faut sélectionner les couleurs disponibles les plus appropriées tout en tenant compte des autres caractéristiques temporelles de la version concernée.

Ainsi, lorsqu'un programme Atari 2600 doit fonctionner en NTSC, PAL ou SECAM, il faut considérer l'ensemble du système et non uniquement la palette. Les différences peuvent toucher les fréquences d'horloge, le nombre de lignes par trame, la hauteur de l'image, la fréquence de rafraîchissement, la vitesse du jeu et la hauteur des sons. Cette dépendance directe entre logiciel et standard de télévision constitue une conséquence importante de l'architecture de l'Atari 2600 : puisque le 6507 et le TIA construisent le signal vidéo en temps réel, une modification de la cadence vidéo influence directement une grande partie du fonctionnement du programme.

Processeur MOS 6507

Le processeur central de l'Atari 2600 est le MOS Technology 6507, un membre de la famille du célèbre MOS 6502. Le 6507 a été conçu comme une version plus économique du 6502, avec un boîtier comportant moins de broches et, par conséquent, certaines limitations matérielles. Son ensemble d'instructions et son fonctionnement interne restent essentiellement ceux du 6502, ce qui permet d'utiliser les mêmes principes de programmation en langage assembleur. Les principales différences concernent surtout le nombre de lignes d'adresse physiquement disponibles et les entrées d'interruption exposées par le processeur.

Alors que le 6502 possède normalement 16 lignes d'adresse, de A0 à A15, le 6507 n'en expose que 13, de A0 à A12. Il ne peut donc distinguer physiquement que 213 = 8192 adresses, soit un espace directement adressable de 8 Kio. Sur l'Atari 2600, cet espace correspond aux adresses $0000 à $1FFF. Cette limitation matérielle contribue à la simplicité et au faible coût de la console, mais impose également une organisation particulière de la mémoire et des périphériques.

L'ensemble d'instructions du 6507 demeure toutefois celui d'un processeur dérivé du 6502 et utilise donc des adresses logiques sur 16 bits dans les instructions qui en ont besoin. Le compteur de programme (PC) est lui aussi un registre de 16 bits, et les mécanismes du processeur associés aux vecteurs emploient l'espace d'adressage traditionnel du 6502. Comme les lignes A13, A14 et A15 ne sont pas physiquement présentes sur le 6507, leurs valeurs ne permettent pas de sélectionner de nouvelles zones matérielles. Il en résulte des miroirs d'adresses : différentes adresses logiques sur 16 bits peuvent finalement désigner la même ressource physique lorsque seules les treize lignes inférieures sont prises en considération.

Cette particularité explique notamment le comportement des vecteurs du 6502 sur l'Atari 2600. Les vecteurs normalement situés au sommet de l'espace 16 bits, comme le vecteur de RESET aux adresses $FFFC-$FFFD, sont physiquement vus par le système à travers le miroir correspondant dans l'espace accessible au 6507. Ainsi, même si le processeur manipule une adresse telle que $FFFC, seules les lignes A0 à A12 apparaissent sur son bus d'adresse externe. Pour comprendre correctement la carte mémoire de l'Atari 2600, il faut donc distinguer l'adresse logique générée par le processeur de l'adresse réellement observable par le matériel.

Le 6507 possède les principaux registres caractéristiques de la famille 6502 : A, X, Y, SP, PC et P. Le registre A, ou accumulateur, est un registre de 8 bits utilisé pour une grande partie des opérations arithmétiques et logiques. Les registres X et Y, également sur 8 bits, servent notamment d'index dans plusieurs modes d'adressage, mais peuvent aussi participer à différentes opérations de calcul, de copie ou de contrôle de boucle. Ces trois registres jouent donc un rôle central dans les programmes Atari 2600.

Le registre SP (Stack Pointer) est le pointeur de pile. Comme sur le 6502, la pile se trouve logiquement dans la page mémoire $0100-$01FF, et SP est un registre de 8 bits qui détermine la position à l'intérieur de cette page. Sur l'Atari 2600, la situation est particulière puisque la console ne contient que 128 octets de RAM dans le RIOT 6532 et que son décodage mémoire produit des miroirs. Le fonctionnement de la pile doit donc être compris en tenant compte de cette organisation matérielle spécifique.

Le PC (Program Counter) est un registre de 16 bits contenant l'adresse de la prochaine instruction à exécuter. Il permet au processeur de suivre le déroulement du programme, d'effectuer des branchements et d'appeler des sous-programmes. Enfin, le registre P, appelé registre d'état ou Processor Status, contient plusieurs indicateurs décrivant le résultat des opérations et certains modes de fonctionnement du processeur.

Ces indicateurs sont traditionnellement représentés sous la forme N, V, -, B, D, I, Z, C. N (Negative) reflète le bit de signe de certains résultats, tandis que V (Overflow) indique certains débordements arithmétiques signés. Le symbole - correspond au bit traditionnellement considéré comme inutilisé ou fixé dans certaines représentations du registre d'état. B (Break) est associé notamment à la représentation de l'état lors de certaines opérations impliquant la pile. D (Decimal) active le mode décimal, I (Interrupt Disable) contrôle la prise en compte des interruptions masquables sur les variantes du 6502 qui les exposent, Z (Zero) indique qu'un résultat est nul et C (Carry) représente la retenue utilisée notamment par les additions, soustractions et décalages.

Le mode décimal du 6507 est fonctionnel. Lorsque le drapeau D est activé, certaines opérations arithmétiques, en particulier ADC et SBC, peuvent travailler selon les règles de l'arithmétique BCD (Binary-Coded Decimal). Le programme doit cependant gérer explicitement cet état et ne doit pas supposer que le drapeau D possède toujours la valeur souhaitée. Dans le code d'initialisation d'un programme Atari 2600, exécuter l'instruction CLD afin de désactiver le mode décimal constitue donc une précaution courante et prudente. Cela garantit que les opérations arithmétiques ordinaires commenceront en mode binaire.

Le 6507 utilise également une représentation little-endian pour les valeurs composées de plusieurs octets. Lorsqu'une adresse ou une valeur de 16 bits est stockée en mémoire, l'octet de poids faible est placé à l'adresse la plus basse et l'octet de poids fort à l'adresse suivante. Par exemple, une adresse 16 bits telle que $1234 sera représentée en mémoire par $34, puis $12. Cette convention est importante lors de la manipulation des pointeurs, des tables d'adresses et des vecteurs.

Enfin, le 6507 reste un processeur 8 bits relativement simple et ne possède aucune instruction matérielle de multiplication ou de division. Ces opérations doivent être réalisées par logiciel, généralement à l'aide d'additions, de soustractions, de décalages et de boucles, ou évitées grâce à des tables précalculées et à d'autres optimisations. Cette contrainte prend une importance particulière sur l'Atari 2600, où le processeur doit souvent effectuer ses calculs tout en respectant des temporisations vidéo extrêmement strictes imposées par le TIA.

Registres et modes d'adressage

Le MOS 6507 de l'Atari 2600 reprend les modes d'adressage du 6502, qui déterminent la manière dont une instruction localise son opérande. Selon l'instruction utilisée, la donnée peut être contenue directement dans l'instruction, se trouver dans un registre du processeur ou être récupérée à une adresse mémoire calculée de différentes façons. Les principaux modes disponibles sont les modes implicite, accumulateur, immédiat, page zéro, page zéro indexée par X ou Y, absolu, absolu indexé par X ou Y, indirect pour JMP, indirect indexé (zp,X), indirect indexé (zp),Y et relatif.

Dans le mode implicite, l'opérande n'a pas besoin d'être indiqué explicitement, car il est déterminé par l'instruction elle-même. Des instructions telles que CLC, CLD ou NOP fonctionnent de cette manière. Le mode accumulateur travaille directement sur le registre A ; certaines instructions de décalage et de rotation peuvent ainsi modifier l'accumulateur sans accéder à une donnée en mémoire. Ces modes sont particulièrement simples et nécessitent peu d'octets pour représenter une instruction.

Le mode immédiat place directement une valeur de 8 bits après le code de l'instruction. Une instruction comme LDA #$20, par exemple, charge directement la valeur $20 dans l'accumulateur. Ce mode doit être distingué d'un accès mémoire : $20 représente ici la valeur elle-même et non le contenu de l'adresse $20. Il constitue un moyen rapide de charger des constantes, des masques binaires ou des valeurs destinées aux registres du TIA et du RIOT.

Le mode page zéro, ou zero page, permet d'accéder aux adresses $0000 à $00FF en utilisant une adresse de seulement 8 bits dans l'instruction. Il est généralement plus compact et souvent plus rapide que l'adressage absolu. Sur l'Atari VCS/2600, la RAM physique fournie par le RIOT représente seulement 128 octets et apparaît notamment dans la plage $80 à $FF de la page zéro. Cette zone est donc particulièrement importante pour les variables, les compteurs, les pointeurs et les données temporaires du programme.

Les modes page zéro indexés ajoutent le contenu d'un registre d'index à l'adresse de base. Selon l'instruction, il est possible d'utiliser X ou Y, ce qui donne les formes couramment désignées zero page,X et zero page,Y. Ces modes sont pratiques pour parcourir des tableaux, des structures ou des groupes de variables. Le calcul reste dans la page zéro : si l'addition dépasse $FF, l'adresse revient au début de cette même page plutôt que de continuer dans la page suivante.

Le mode absolu utilise une adresse complète de 16 bits dans l'instruction. Les variantes absolu,X et absolu,Y ajoutent respectivement le contenu du registre X ou Y à cette adresse de base. Elles permettent notamment de parcourir des tables placées dans la ROM de la cartouche. Même si le 6507 n'expose physiquement que treize lignes d'adresse, les instructions conservent le format et les mécanismes d'adressage 16 bits hérités du 6502 ; le décodage matériel de la console détermine ensuite la ressource réellement sélectionnée.

Le 6507 possède également plusieurs formes d'adressage indirect. L'instruction JMP peut employer le mode indirect, dans lequel l'adresse indiquée par l'instruction désigne en réalité l'emplacement contenant l'adresse de destination du saut. Les modes (zp,X) et (zp),Y permettent pour leur part de construire des accès mémoire à partir de pointeurs conservés en page zéro. Dans (zp,X), l'index X intervient avant la lecture du pointeur, tandis que dans (zp),Y, un pointeur est d'abord lu en page zéro puis la valeur de Y est ajoutée à l'adresse obtenue. Ces mécanismes permettent de réaliser des accès dynamiques malgré le nombre limité de registres du processeur.

Le mode relatif est principalement employé par les instructions de branchement conditionnel telles que BEQ, BNE, BCC ou BCS. Plutôt que de contenir une adresse absolue de destination, l'instruction contient un déplacement signé sur 8 bits calculé relativement à l'adresse de l'instruction suivante. Ce mécanisme permet de produire des branchements compacts, particulièrement utiles pour les boucles et les tests conditionnels.

Tous ces modes d'adressage n'ont cependant pas un temps d'exécution parfaitement constant dans toutes les situations. Lors de certaines lectures indexées, l'adresse finale peut franchir une frontière de page mémoire de 256 octets. Par exemple, si une adresse de base proche de $xxFF est additionnée à un index suffisamment grand pour atteindre la page suivante, le processeur peut avoir besoin d'un cycle supplémentaire pour effectuer la lecture. Cette pénalité dépend du mode d'adressage et de l'instruction concernée ; elle ne doit donc pas être appliquée indistinctement à tous les accès indexés.

Les branchements conditionnels présentent eux aussi une durée variable. Lorsqu'une condition n'est pas satisfaite et que le branchement n'est pas effectué, l'instruction conserve son temps minimal. Lorsqu'un branchement est pris, le processeur consomme un cycle supplémentaire. Si la destination du branchement se trouve en plus dans une autre page mémoire que celle correspondant à la poursuite normale de l'exécution, un second cycle supplémentaire est nécessaire. La durée d'un même branchement peut donc varier selon le résultat du test et l'emplacement exact du code en mémoire.

Ces différences de quelques cycles sont particulièrement importantes sur l'Atari 2600. Dans un programme ordinaire, un cycle supplémentaire peut souvent être sans conséquence visible, mais ce n'est pas nécessairement le cas dans le kernel vidéo. Celui-ci doit modifier les registres du TIA à des instants extrêmement précis pendant que l'image est produite. Une lecture qui franchit accidentellement une page ou un branchement qui consomme un cycle supplémentaire peut décaler une écriture au TIA et modifier le résultat affiché. Le programmeur doit donc connaître non seulement le nombre de cycles de chaque instruction, mais également les variations de durée provoquées par les modes d'adressage, les franchissements de page et les branchements.

Ensemble d'instructions

Le MOS 6507 de l'Atari 2600 utilise essentiellement le même ensemble d'instructions que le MOS 6502. Il s'agit d'un processeur 8 bits disposant d'un ensemble relativement compact d'instructions, mais suffisamment polyvalent pour réaliser les opérations nécessaires aux jeux : transferts de données, calculs arithmétiques, opérations logiques, manipulations de bits, branchements, appels de sous-programmes et gestion de la pile. Sur l'Atari 2600, la connaissance précise de chaque instruction est particulièrement importante, car le programme doit souvent tenir compte non seulement du résultat produit, mais également du nombre de cycles processeur consommés.

Les instructions de chargement et de stockage comprennent LDA, LDX, LDY, STA, STX et STY. Les trois premières chargent respectivement les registres A, X et Y avec une valeur, tandis que les trois suivantes écrivent le contenu de ces registres en mémoire. LDA, par exemple, charge l'accumulateur, alors que STA enregistre son contenu à une adresse mémoire. Ces instructions sont extrêmement fréquentes dans les programmes Atari 2600, notamment pour lire des données, manipuler les variables en RAM et écrire dans les registres matériels du TIA ou du RIOT.

Plusieurs instructions permettent également de transférer directement des valeurs entre les registres. TAX copie l'accumulateur A dans X, TAY copie A dans Y, TXA copie X dans A et TYA copie Y dans A. Les instructions TSX et TXS concernent le pointeur de pile : TSX transfère le contenu du Stack Pointer (SP) dans X, tandis que TXS effectue l'opération inverse. Ces transferts permettent de réorganiser rapidement les données sans devoir systématiquement passer par la mémoire.

La pile est manipulée principalement à l'aide de quatre instructions. PHA place le contenu de l'accumulateur sur la pile et PLA le récupère. De manière similaire, PHP empile une représentation du registre d'état P, tandis que PLP restaure les indicateurs du processeur à partir d'une valeur retirée de la pile. La pile intervient également automatiquement lors de certaines opérations comme les appels de sous-programmes et les mécanismes associés aux interruptions et à BRK.

Les principales instructions logiques sont AND, ORA, EOR et BIT. AND effectue un ET logique bit à bit entre l'accumulateur et l'opérande, tandis que ORA effectue un OU logique et EOR un OU exclusif. Ces opérations sont très utiles pour appliquer des masques binaires, activer, désactiver ou inverser certains bits. L'instruction BIT permet de tester certains bits d'une valeur mémoire sans remplacer le contenu de l'accumulateur ; elle modifie certains drapeaux du registre d'état en fonction de la valeur examinée.

Les opérations arithmétiques reposent notamment sur ADC et SBC. ADC effectue une addition en tenant compte du drapeau de retenue C (Carry), tandis que SBC réalise une soustraction en utilisant également ce drapeau. Ces deux instructions peuvent fonctionner en mode binaire ou, lorsque le drapeau D est activé, selon le comportement décimal du processeur. Le 6507 ne possédant pas d'instruction matérielle de multiplication ou de division, les calculs plus complexes doivent être construits à partir de ces opérations élémentaires, de décalages, de boucles ou de tables précalculées.

Les instructions CMP, CPX et CPY servent à effectuer des comparaisons. Elles comparent respectivement une valeur avec les registres A, X ou Y sans modifier directement le registre comparé. Le résultat de la comparaison est représenté par certains drapeaux du registre d'état, notamment N, Z et C. Le programme peut ensuite utiliser une instruction de branchement conditionnel pour déterminer si deux valeurs sont égales, différentes ou ordonnées d'une certaine manière.

Le processeur dispose également d'instructions permettant d'incrémenter ou de décrémenter des valeurs. INC augmente une valeur en mémoire et DEC la diminue. Les instructions INX et INY augmentent respectivement les registres X et Y, tandis que DEX et DEY les diminuent. Ces opérations sont particulièrement utiles pour construire des boucles, parcourir des tables et maintenir des compteurs sans devoir utiliser systématiquement ADC ou SBC.

Les instructions de décalage et de rotation sont ASL, LSR, ROL et ROR. ASL effectue un décalage vers la gauche et LSR un décalage vers la droite. ROL et ROR réalisent des rotations respectivement vers la gauche et vers la droite en faisant intervenir le drapeau Carry. Ces instructions servent à manipuler des champs de bits, à effectuer certaines multiplications ou divisions par deux et à construire des algorithmes arithmétiques plus complexes.

Les branchements conditionnels comprennent BCC, BCS, BEQ, BNE, BMI, BPL, BVC et BVS. Chacun examine un drapeau particulier du registre d'état : BCC et BCS testent C, BEQ et BNE testent Z, BMI et BPL testent N, tandis que BVC et BVS testent V. Ces instructions utilisent un déplacement relatif et permettent de construire les conditions, les boucles et les décisions nécessaires à la logique d'un programme. Leur durée peut varier selon que le branchement est pris ou non et selon qu'il franchit une frontière de page, ce qui est particulièrement important dans un kernel vidéo synchronisé au cycle près.

Les principales instructions de contrôle du programme sont JMP, JSR, RTS, BRK et RTI. JMP effectue un saut vers une autre adresse, tandis que JSR appelle un sous-programme en sauvegardant sur la pile l'information nécessaire au retour. RTS permet ensuite de revenir du sous-programme. BRK déclenche le mécanisme logiciel associé au vecteur d'interruption du 6502, tandis que RTI restaure l'état du processeur et l'adresse de retour après un tel mécanisme d'interruption. Même si le 6507 possède moins de broches d'interruption que le 6502 complet, ces instructions demeurent présentes dans son ensemble d'instructions.

Plusieurs instructions permettent directement de modifier les drapeaux du registre d'état. CLC efface le drapeau Carry et SEC l'active. CLD désactive le mode décimal tandis que SED l'active. CLI et SEI modifient le drapeau Interrupt Disable, respectivement en l'effaçant et en l'activant, même si les possibilités d'interruption externe du 6507 sont réduites par rapport au 6502. CLV efface le drapeau Overflow. Enfin, NOP signifie No Operation : l'instruction ne réalise aucune opération fonctionnelle importante, mais consomme néanmoins des octets et des cycles, ce qui peut être utile pour ajuster précisément une temporisation.

En plus de ces instructions officiellement documentées, la famille 6502 possède plusieurs opcodes non documentés, parfois appelés illegal opcodes ou undocumented opcodes. Ils correspondent à des combinaisons binaires auxquelles la documentation officielle n'attribuait pas d'instruction destinée à être utilisée par les programmeurs, mais qui produisent néanmoins des comportements déterminés par la logique interne du processeur. Certains combinent les effets de plusieurs opérations officielles ou provoquent des séquences particulières d'accès au bus.

L'utilisation de ces opcodes non documentés n'est pas nécessaire pour programmer normalement l'Atari 2600 et peut rendre un programme plus dépendant du comportement précis du processeur. Certains détails, notamment les effets sur le bus, les lectures ou écritures intermédiaires et le comportement de certaines combinaisons, peuvent demander une émulation très précise. Ils présentent toutefois une importance pour la compatibilité historique : certains logiciels peuvent les employer intentionnellement ou dépendre de leur comportement.

Un émulateur Atari 2600 visant une compatibilité élevée doit donc reproduire ces opcodes non documentés, même si un nouveau programme n'a aucune obligation de les utiliser. Il doit idéalement reproduire non seulement le résultat final sur les registres et les drapeaux, mais aussi les cycles d'exécution et les accès au bus pertinents. Sur une architecture aussi dépendante des temporisations que l'Atari 2600, une différence de comportement au niveau d'un opcode peut en effet perturber la synchronisation avec le TIA et produire un affichage différent de celui obtenu sur le matériel original.

Voici le tableau complet des 56 instructions officielles du MOS 6507 :

Instruction Syntaxe Description
ADC ADC opérande Add with Carry. Additionne l'opérande à l'accumulateur A en tenant compte du drapeau Carry C.
AND AND opérande Effectue un ET logique bit à bit entre l'accumulateur A et l'opérande.
ASL ASL A / ASL adresse Arithmetic Shift Left. Décale les bits vers la gauche. Le bit 7 est transféré dans Carry et un 0 entre au bit 0.
BCC BCC étiquette Branch if Carry Clear. Effectue un branchement si C = 0.
BCS BCS étiquette Branch if Carry Set. Effectue un branchement si C = 1.
BEQ BEQ étiquette Branch if Equal. Effectue un branchement si Z = 1.
BIT BIT adresse Teste des bits sans modifier A. Affecte notamment Z à partir de A AND opérande, et copie les bits 7 et 6 de l'opérande dans N et V.
BMI BMI étiquette Branch if Minus. Effectue un branchement si N = 1.
BNE BNE étiquette Branch if Not Equal. Effectue un branchement si Z = 0.
BPL BPL étiquette Branch if Plus. Effectue un branchement si N = 0.
BRK BRK Break. Déclenche la séquence logicielle d'interruption du processeur et utilise le vecteur IRQ/BRK.
BVC BVC étiquette Branch if Overflow Clear. Effectue un branchement si V = 0.
BVS BVS étiquette Branch if Overflow Set. Effectue un branchement si V = 1.
CLC CLC Clear Carry. Met le drapeau C à 0.
CLD CLD Clear Decimal. Met D à 0 et désactive le mode décimal.
CLI CLI Clear Interrupt Disable. Met I à 0.
CLV CLV Clear Overflow. Met V à 0.
CMP CMP opérande Compare l'accumulateur A avec l'opérande par une soustraction logique sans modifier A. Affecte N, Z et C.
CPX CPX opérande Compare le registre X avec l'opérande. Affecte N, Z et C.
CPY CPY opérande Compare le registre Y avec l'opérande. Affecte N, Z et C.
DEC DEC adresse Decrement Memory. Diminue de 1 une valeur en mémoire.
DEX DEX Diminue le registre X de 1.
DEY DEY Diminue le registre Y de 1.
EOR EOR opérande Exclusive OR. Effectue un OU exclusif bit à bit entre A et l'opérande.
INC INC adresse Increment Memory. Augmente de 1 une valeur en mémoire.
INX INX Augmente le registre X de 1.
INY INY Augmente le registre Y de 1.
JMP JMP adresse / JMP (adresse) Effectue un saut inconditionnel vers une nouvelle adresse.
JSR JSR adresse Jump to Subroutine. Appelle un sous-programme et sauvegarde l'adresse de retour sur la pile.
LDA LDA opérande Load Accumulator. Charge une valeur dans l'accumulateur A.
LDX LDX opérande Load X. Charge une valeur dans le registre X.
LDY LDY opérande Load Y. Charge une valeur dans le registre Y.
LSR LSR A / LSR adresse Logical Shift Right. Décale les bits vers la droite. Le bit 0 passe dans Carry et un 0 entre au bit 7.
NOP NOP No Operation. N'effectue aucune opération fonctionnelle, mais consomme du temps processeur.
ORA ORA opérande Effectue un OU logique bit à bit entre A et l'opérande.
PHA PHA Push Accumulator. Place le contenu de A sur la pile.
PHP PHP Push Processor Status. Place une représentation du registre d'état P sur la pile.
PLA PLA Pull Accumulator. Retire une valeur de la pile et la place dans A.
PLP PLP Pull Processor Status. Restaure les drapeaux du registre d'état à partir de la pile.
ROL ROL A / ROL adresse Rotate Left. Effectue une rotation vers la gauche en utilisant le drapeau Carry.
ROR ROR A / ROR adresse Rotate Right. Effectue une rotation vers la droite en utilisant le drapeau Carry.
RTI RTI Return from Interrupt. Restaure l'état et le compteur de programme après une interruption ou un BRK.
RTS RTS Return from Subroutine. Revient d'un sous-programme appelé avec JSR.
SBC SBC opérande Subtract with Carry. Soustrait l'opérande de A en tenant compte du drapeau Carry.
SEC SEC Set Carry. Met C à 1.
SED SED Set Decimal. Met D à 1 et active le mode décimal.
SEI SEI Set Interrupt Disable. Met I à 1.
STA STA adresse Store Accumulator. Écrit le contenu de A en mémoire.
STX STX adresse Store X. Écrit le contenu de X en mémoire.
STY STY adresse Store Y. Écrit le contenu de Y en mémoire.
TAX TAX Transfer A to X. Copie A dans X.
TAY TAY Transfer A to Y. Copie A dans Y.
TSX TSX Transfer Stack Pointer to X. Copie SP dans X.
TXA TXA Transfer X to A. Copie X dans A.
TXS TXS Transfer X to Stack Pointer. Copie X dans SP.
TYA TYA Transfer Y to A. Copie Y dans A.

Voici un tableau complet des familles d'instructions non documentées, avec les principaux noms alternatifs du MOS 6507.

Instruction Syntaxe Description
AHX (SHA) AHX (zp),Y / AHX abs,Y Stockage non documenté combinant A, X et une valeur dérivée de l'octet haut de l'adresse. Son comportement peut être instable, particulièrement lors d'un franchissement de page.
ALR (ASR) ALR #imm Effectue d'abord A = A AND imm, puis un décalage logique à droite de A, comparable à une combinaison de AND et LSR.
ANC ANC #imm Effectue A = A AND imm, comme AND, puis copie le bit de signe du résultat dans le drapeau Carry. Les opcodes $0B et $2B sont généralement associés à cette opération.
ARR ARR #imm Combine un AND immédiat avec une opération apparentée à une rotation vers la droite. Les drapeaux C et V ont un comportement particulier, et le mode décimal rend son fonctionnement plus complexe.
AXS (SBX) AXS #imm Effectue essentiellement (A AND X) - imm et place le résultat dans X. Affecte notamment N, Z et C.
DCP (DCM) DCP opérande Décrémente d'abord la valeur mémoire, puis la compare avec A. Équivaut fonctionnellement à une combinaison de DEC et CMP.
ISC (ISB, INS) ISC opérande Incrémente la valeur mémoire puis effectue une soustraction avec A en utilisant le mécanisme de SBC. Combine donc essentiellement INC et SBC.
JAM (KIL, HLT) JAM Bloque le fonctionnement normal du processeur. Le CPU reste effectivement arrêté jusqu'à un RESET matériel.
LAS (LDS, LAE) LAS abs,Y Effectue un ET entre la valeur mémoire et le pointeur de pile SP, puis place le résultat simultanément dans A, X et SP.
LAX LAX opérande Charge simultanément la même valeur dans A et X. Se comporte essentiellement comme une combinaison de LDA et LDX.
LXA (OAL) LXA #imm Variante immédiate apparentée à LAX. Son résultat dépend de caractéristiques internes du NMOS 6502 et n'est pas considéré aussi fiable que les formes ordinaires de LAX.
NOP* NOP* opérande Famille d'opcodes non documentés ne modifiant généralement pas les registres comme une opération normale, mais pouvant effectuer une lecture mémoire, occuper 1, 2 ou 3 octets et consommer différents nombres de cycles.
RLA RLA opérande Effectue une rotation à gauche de la valeur mémoire puis un AND entre cette nouvelle valeur et A. Combine essentiellement ROL et AND.
RRA RRA opérande Effectue une rotation à droite de la valeur mémoire puis l'additionne à A en tenant compte de Carry. Combine essentiellement ROR et ADC.
SAX (AXS dans certaines anciennes nomenclatures) SAX opérande Stocke en mémoire le résultat de A AND X, sans modifier A ni X.
SHX (XAS) SHX abs,Y Stocke une valeur dérivée de X AND (octet haut de l'adresse + 1). Son comportement peut devenir instable lors du franchissement d'une page.
SHY (SAY) SHY abs,X Stocke une valeur dérivée de Y AND (octet haut de l'adresse + 1). Comme SHX, certains comportements dépendent de la génération de l'adresse sur le bus.
SLO (ASO) SLO opérande Décale la valeur mémoire vers la gauche puis effectue ORA avec A. Combine essentiellement ASL et ORA.
SRE (LSE) SRE opérande Décale la valeur mémoire vers la droite puis effectue EOR avec A. Combine essentiellement LSR et EOR.
TAS (SHS) TAS abs,Y Place A AND X dans le pointeur de pile SP, puis utilise cette valeur avec l'octet haut de l'adresse pour déterminer la valeur écrite en mémoire. Son comportement d'écriture peut être instable.
XAA (ANE) XAA #imm Effectue une combinaison de A, X, l'opérande immédiat et une valeur interne dépendant du silicium. Cette instruction est particulièrement instable et son résultat peut varier selon la puce.

Pile, vecteurs et absence d'IRQ

Le MOS 6507 conserve l'organisation logique de la pile héritée du 6502. Du point de vue du processeur, celle-ci occupe la page mémoire $0100 à $01FF, appelée page 1. Le registre SP (Stack Pointer) est un registre de 8 bits qui désigne la position courante à l'intérieur de cette page. Une valeur de SP égale à $FF correspond donc logiquement à l'adresse $01FF, $FE à $01FE, et ainsi de suite. La pile croît vers les adresses décroissantes : lorsqu'une valeur est empilée, SP est décrémenté, tandis qu'il est incrémenté lorsqu'une valeur est retirée.

Sur l'Atari 2600, cette organisation logique doit toutefois être interprétée en fonction du décodage matériel particulier de la console. Le 6507 n'expose que 13 lignes d'adresse, A0 à A12, et le RIOT 6532 ne fournit que 128 octets de RAM physique. À cause du décodage partiel des adresses et des miroirs qui en résultent, les accès à la pile dans la page $0100-$01FF peuvent finalement atteindre les mêmes cellules physiques que la RAM accessible dans la zone $0080-$00FF. La pile et les variables du programme ne constituent donc pas deux mémoires physiquement séparées : elles doivent se partager les mêmes 128 octets de RAM.

Cette caractéristique impose une gestion prudente de la mémoire. Si la pile devient trop profonde, elle peut atteindre et écraser des variables encore utilisées par le programme. Une convention pratique consiste donc à placer les variables à partir de la partie basse de la RAM disponible, en progressant vers les adresses supérieures, tandis que la pile commence près de l'extrémité opposée, avec SP initialisé près de $FF. Les deux zones peuvent alors progresser l'une vers l'autre. Le programmeur doit s'assurer qu'elles ne se rencontrent jamais, particulièrement lorsqu'il utilise de nombreux appels JSR, des valeurs empilées avec PHA ou PHP, ou d'autres opérations utilisant la pile.

Le 6507 conserve également le mécanisme de vecteurs du 6502. Ceux-ci sont logiquement situés dans les six derniers octets de l'espace d'adressage 16 bits du processeur. Le vecteur NMI occupe les adresses $FFFA-$FFFB, le vecteur RESET les adresses $FFFC-$FFFD, et le vecteur partagé IRQ/BRK les adresses $FFFE-$FFFF. Chaque vecteur contient une adresse de 16 bits enregistrée en format little-endian : l'octet de poids faible est stocké en premier, suivi de l'octet de poids fort.

Le 6507 ne possède cependant que treize lignes d'adresse externes. Les bits supérieurs de l'adresse logique générée par le cour du processeur ne sont donc pas disponibles sur le bus externe. Les adresses logiques $FFFA-$FFFF se replient ainsi, du point de vue des treize lignes A0-A12, sur $1FFA-$1FFF. Ces emplacements se trouvent à la fin de la zone correspondant à la cartouche visible par le processeur. Une cartouche doit donc placer les valeurs appropriées à ces emplacements afin que le 6507 obtienne les bons vecteurs lorsqu'il effectue ses lectures.

Le vecteur RESET, situé logiquement à $FFFC-$FFFD et physiquement reflété vers $1FFC-$1FFD, est particulièrement important. Lors de la réinitialisation, le processeur lit ces deux octets afin de déterminer l'adresse à laquelle commencer l'exécution du programme. Une cartouche Atari 2600 doit donc fournir un vecteur RESET valide pointant vers son code d'initialisation. Sans lui, le processeur ne saurait pas à quelle adresse commencer correctement l'exécution du jeu.

Les vecteurs NMI et IRQ existent toujours dans l'architecture logique du cour 6502, mais la situation matérielle du 6507 est différente. Afin de réduire le nombre de broches et le coût du composant, les entrées d'interruption externes correspondantes du 6502 ne sont normalement pas disponibles/câblées de manière exploitable sur l'Atari 2600. Les jeux ne reposent donc généralement pas sur des interruptions matérielles périodiques pour synchroniser leur fonctionnement. La synchronisation vidéo, la lecture des contrôleurs et une grande partie de la temporisation sont gérées directement par le programme.

L'absence d'une entrée IRQ utilisable ne rend toutefois pas le vecteur IRQ/BRK complètement inutile. L'instruction BRK appartient toujours au ensemble d'instructions du 6507 et déclenche le mécanisme logiciel correspondant du cour 6502. Lorsqu'elle est exécutée, le processeur sauvegarde les informations nécessaires sur la pile puis consulte le vecteur IRQ/BRK situé à $FFFE-$FFFF, physiquement reflété vers $1FFE-$1FFF. Une cartouche qui emploie BRK doit donc fournir une valeur appropriée dans ce vecteur, même si aucune interruption IRQ matérielle n'est normalement utilisée.

Le vecteur NMI, situé à $FFFA-$FFFB et reflété vers $1FFA-$1FFB, subsiste lui aussi dans la disposition logique héritée du 6502, même si l'entrée NMI n'est normalement pas utilisée sur la console. Il est néanmoins courant de prévoir des valeurs cohérentes pour l'ensemble des vecteurs à la fin de la ROM. Le RESET demeure le seul vecteur indispensable au démarrage normal, tandis que le vecteur IRQ/BRK devient pertinent si le programme exécute BRK.

Cette organisation illustre une particularité importante de l'Atari 2600 : le 6507 conserve de nombreux mécanismes internes du 6502, mais son boîtier économique n'expose pas toutes les possibilités matérielles de son processeur parent. La pile reste logiquement dans $0100-$01FF, les vecteurs restent logiquement dans $FFFA-$FFFF et BRK conserve son mécanisme d'interruption, alors que le matériel réel replie ces adresses dans l'espace de 13 bits et ne fournit normalement pas les lignes IRQ et NMI utilisées sur un 6502 complet.

Carte mémoire

Adresse canonique Fonction
$0000--$002C TIA écriture
$0030--$003D TIA lecture
$0080--$00FF RAM RIOT, 128 octets
$0280--$0283 ports RIOT
$0284--$0285 minuterie/état RIOT en lecture
$0294--$0297 programmation la minuterie RIOT
$1000--$1FFF fenêtre cartouche, 4 Kio

Les plages ont de nombreux miroirs. Les symboles assembleur emploient les adresses canoniques ci-dessus pour clarté.

Miroirs et décodage partiel

L'organisation mémoire de l'Atari 2600 est fortement influencée par la simplicité de son matériel. Le MOS 6507 n'expose que treize lignes d'adresse, de A0 à A12, et les différents circuits de la console ne décodent pas nécessairement tous les bits disponibles. Cette technique de décodage partiel permet de réduire le nombre de composants logiques nécessaires et donc le coût de fabrication, mais elle provoque l'apparition de nombreux miroirs d'adresses. Plusieurs adresses différentes peuvent ainsi désigner exactement le même registre ou la même cellule de mémoire physique.

La ligne d'adresse A12 joue un rôle fondamental dans la sélection générale de l'espace mémoire. Elle permet essentiellement de distinguer la zone associée à la cartouche de l'espace interne de la console. Lorsque l'accès concerne l'espace interne, d'autres lignes d'adresse sont utilisées pour déterminer si le processeur communique avec le TIA, la RAM du RIOT 6532 ou les registres d'entrée/sortie et de la minuterie du RIOT. Cependant, toutes les lignes restantes ne participent pas nécessairement au décodage. Les bits ignorés peuvent donc changer de valeur sans modifier le composant réellement sélectionné.

Cette situation explique pourquoi les registres du TIA, du RIOT et la RAM apparaissent à plusieurs endroits dans l'espace d'adressage du processeur. Par exemple, une adresse comme $0100 peut correspondre à un miroir des registres du TIA, alors que $0180 peut correspondre à un miroir de la RAM. La différence vient des lignes d'adresse effectivement examinées par la logique de sélection des composants. Il ne faut donc pas interpréter la carte mémoire de l'Atari 2600 comme une succession de grandes régions totalement indépendantes : une grande partie de l'espace est constituée de répétitions des mêmes ressources matérielles.

Ces miroirs ne constituent pas simplement une curiosité théorique. Ils peuvent être utilisés intentionnellement par les programmes. Deux adresses différentes peuvent produire le même accès matériel tout en permettant au programmeur de profiter d'un mode d'adressage particulier, d'une organisation différente du code ou de certaines contraintes de temporisation. Ils peuvent également apparaître indirectement lorsqu'une instruction génère une adresse intermédiaire différente de son adresse finale.

Cette dernière situation est particulièrement importante avec le 6507/6502, car l'exécution d'une instruction ne se résume pas toujours à une seule lecture ou écriture correspondant au résultat visible de l'instruction. Certaines instructions effectuent des cycles d'accès intermédiaires, souvent appelés dummy reads ou dummy accesses. Ces accès sont produits par le fonctionnement interne du processeur pendant le calcul d'une adresse, l'indexation, certaines opérations de lecture-modification-écriture ou d'autres phases d'exécution.

Sur un ordinateur où la mémoire concernée est une simple RAM, une lecture intermédiaire peut n'avoir aucune conséquence observable. Sur l'Atari 2600, la situation est différente parce qu'une adresse générée pendant un tel cycle peut correspondre à un registre matériel ayant un effet immédiat. Si cette adresse tombe sur un miroir particulier du TIA, l'accès peut toucher un strobe du TIA, c'est-à-dire un registre dont l'accès provoque directement une action matérielle. Des registres comme ceux utilisés pour repositionner certains objets ou contrôler la synchronisation peuvent ainsi être sensibles à la séquence exacte des accès au bus.

Le même problème apparaît avec certaines cartouches utilisant la commutation bancaire (bank switching). Ces cartouches réservent certaines adresses, appelées couramment hotspots, pour sélectionner une banque de ROM. Selon le circuit employé, le simple fait d'accéder à l'une de ces adresses peut modifier la banque actuellement visible. Un accès intermédiaire généré par le processeur peut donc théoriquement atteindre un hotspot et provoquer une commutation bancaire, même si l'adresse finale apparemment visée par l'instruction est différente.

Cette caractéristique est particulièrement importante lors de la conception d'un émulateur Atari 2600. Une émulation simplifiée interprétant une instruction uniquement en fonction de son résultat final - par exemple en calculant directement la valeur obtenue et l'adresse finale - peut manquer certains comportements du matériel original. Pour reproduire correctement les logiciels qui dépendent de ces particularités, l'émulateur doit tenir compte de la séquence réelle des cycles du 6507 et des adresses présentées sur le bus pendant chacun de ces cycles.

Une émulation de haute compatibilité doit donc reproduire non seulement les registres du processeur, les valeurs en mémoire et le nombre total de cycles consommés, mais également les effets de bus pertinents cycle par cycle. Cela inclut les lectures intermédiaires, certaines écritures temporaires des instructions de type lecture-modification-écriture, les accès aux adresses miroirs et les interactions avec les hotspots de commutation bancaire. Dans l'architecture de l'Atari 2600, un accès que le programmeur pourrait considérer comme « inutile » du point de vue du calcul peut en réalité constituer une opération matérielle observable.

Les miroirs et le décodage partiel sont ainsi indissociables de la compréhension précise de l'Atari 2600. Ils résultent d'une architecture volontairement simple et économique, mais produisent des comportements subtils qui peuvent être exploités par les logiciels. Pour un programmeur, ils permettent de comprendre pourquoi plusieurs adresses conduisent au même matériel ; pour un développeur d'émulateur, ils rappellent surtout qu'il ne suffit pas de reproduire le résultat logique des instructions : il faut également reproduire, lorsque cela est nécessaire, ce que le 6507 place réellement sur son bus pendant leur exécution.

TIA : architecture

Le TIA (Television Interface Adaptor) constitue l'un des circuits fondamentaux de l'Atari 2600. Il regroupe une grande partie des fonctions nécessaires à la production de l'image et du son, tout en participant à la lecture des contrôleurs. Contrairement à une puce graphique moderne, le TIA ne dispose pas d'un framebuffer contenant une image complète. Il produit directement le signal destiné au téléviseur pendant que le MOS 6507 exécute le programme du jeu. Cette architecture impose donc une coopération très étroite entre le processeur et le circuit vidéo.

Pour la partie vidéo, le TIA génère notamment les signaux de synchronisation, ainsi que les composantes nécessaires à la production de la luminance et de la chrominance du signal correspondant au standard vidéo de la console. Il contient également la logique permettant de générer les différents objets graphiques, de détecter certaines collisions entre ceux-ci et de contrôler les périodes pendant lesquelles l'image doit être masquée. Le même circuit comporte en outre les fonctions de génération audio et plusieurs entrées utilisées pour les contrôleurs de jeu.

Le TIA possède une connaissance précise de la position horizontale du faisceau pendant la construction d'une ligne. Cette notion est indispensable, puisque les différents objets graphiques doivent apparaître à des positions déterminées sur la ligne courante. Le circuit comporte donc des compteurs et des mécanismes internes liés au déroulement horizontal du signal vidéo. Les programmes peuvent synchroniser leurs écritures dans les registres du TIA avec cette progression afin de modifier l'apparence de la ligne pendant qu'elle est générée.

En revanche, le TIA ne possède pas de véritable compteur vertical de lignes capable de gérer automatiquement une trame complète. Il ne sait pas, par lui-même, que la console vient d'afficher la ligne 50, 100 ou 192 d'une image. Cette responsabilité revient au logiciel exécuté par le 6507. Le programme doit compter les lignes produites et décider lui-même à quel moment commencer et terminer les différentes parties de la trame.

Le logiciel doit notamment commander explicitement les registres VSYNC et VBLANK. VSYNC permet de produire la synchronisation verticale nécessaire pour signaler au téléviseur le commencement d'une nouvelle trame, tandis que VBLANK permet de masquer la partie visible du signal pendant certaines périodes. Le programme organise ainsi successivement la synchronisation verticale, le vertical blank, la zone visible de l'image puis l'overscan. Le nombre de lignes consacré à chacune de ces périodes est déterminé principalement par le logiciel.

Cette absence de compteur vertical automatique explique pourquoi un programme Atari 2600 peut théoriquement produire une trame possédant un nombre inhabituel de lignes. Le TIA continue simplement à générer les lignes demandées jusqu'à ce que le logiciel modifie les signaux appropriés. Le respect des standards NTSC, PAL ou SECAM dépend donc fortement du programme, ce qui contribue à expliquer pourquoi certains jeux produisent des nombres de lignes légèrement différents et pourquoi certains équipements vidéo modernes peuvent éprouver des difficultés à se synchroniser avec eux.

Pour construire l'image, le TIA met à disposition plusieurs générateurs graphiques très simples. Les cinq objets mobiles sont appelés P0, P1, M0, M1 et BL. P0 et P1 désignent respectivement Player 0 et Player 1, les deux principaux objets graphiques programmables. M0 et M1 correspondent aux deux objets Missile, traditionnellement associés aux joueurs correspondants. Enfin, BL représente la Ball, un objet graphique supplémentaire particulièrement simple.

Les objets P0 et P1 disposent chacun d'un motif graphique programmable permettant de représenter un sprite horizontal. Le logiciel peut modifier leur motif, leur couleur, leur position et différents paramètres d'affichage. Les missiles M0 et M1 sont des objets plus simples dont la largeur et la position peuvent être contrôlées, tandis que la balle BL constitue elle aussi un élément graphique de largeur configurable. Malgré leurs noms historiques de Player, Missile et Ball, ces objets ne sont pas limités à représenter réellement des joueurs, des projectiles ou une balle : le programme peut les employer pour construire n'importe quel élément graphique approprié.

À ces cinq objets mobiles s'ajoute le Playfield (PF), qui représente principalement le décor ou l'arrière-plan graphique. Le playfield est défini par un motif de 20 bits. Ces 20 bits décrivent une moitié de la ligne de playfield et sont affichés sur les deux moitiés de l'écran selon la configuration choisie. La seconde moitié peut notamment reproduire la première directement ou être affichée selon un arrangement réfléchi, ce qui permet de créer efficacement des terrains symétriques avec très peu de données.

Le playfield est donc extrêmement compact par rapport à une véritable mémoire bitmap. Au lieu de conserver tous les pixels d'une ligne ou d'un écran, le TIA conserve simplement les bits décrivant le motif courant. Le programme peut toutefois modifier les registres du playfield pendant le balayage. En changeant les valeurs au moment approprié, il devient possible de produire des formes beaucoup plus complexes que ne le laisserait penser un simple motif statique de 20 bits.

Une caractéristique essentielle du TIA est que ses registres conservent leur valeur jusqu'à ce que le logiciel les réécrive. Il n'est donc pas nécessaire de reprogrammer systématiquement tous les paramètres à chaque ligne. Si le programme charge une couleur, un motif ou un paramètre dans un registre et ne le modifie plus, cette valeur continue à être utilisée pendant les lignes suivantes. À l'inverse, le processeur peut réécrire un registre au milieu d'une ligne ou entre deux lignes afin de modifier immédiatement la manière dont le reste de l'image sera généré.

Cette persistance des registres est au cour des techniques graphiques de l'Atari 2600. Un programme peut laisser certains paramètres inchangés pendant plusieurs lignes et en modifier d'autres ligne par ligne, voire pendant une même ligne, à des cycles précisément choisis. Associée à la connaissance horizontale du faisceau par le TIA et au comptage vertical réalisé par le logiciel, cette méthode permet au 6507 de construire l'image progressivement. L'architecture du TIA repose ainsi moins sur l'entreposage d'une image que sur la génération en temps réel d'un flux vidéo contrôlé par le programme.

Construction d'une image

La construction d'une image sur l'Atari 2600 est très différente de celle d'une machine disposant d'une mémoire vidéo complète. Le TIA (Television Interface Adaptor) ne contient pas de framebuffer représentant l'écran entier : le programme exécuté par le MOS 6507 participe directement à la génération du signal vidéo au fur et à mesure de son affichage. Une trame, ou frame, doit donc être organisée par logiciel en plusieurs phases successives correspondant à la synchronisation verticale, au vertical blank, à la partie visible produite par le kernel vidéo et enfin à l'overscan.

La première phase est la synchronisation verticale, ou vertical sync. Le programme active le registre VSYNC du TIA afin de produire le signal permettant au téléviseur d'identifier le commencement d'une nouvelle trame. Sur un programme NTSC conventionnel, cette période dure généralement trois lignes. Le processeur doit contrôler explicitement cette phase, car le TIA ne possède pas de compteur vertical capable de déterminer automatiquement où commence ou se termine une image.

Après la synchronisation vient le vertical blank, ou VBLANK. Pendant cette période, l'affichage visible est masqué et le programme dispose de temps processeur pour préparer la prochaine image. Sur une organisation NTSC typique de 262 lignes, les trois lignes de VSYNC sont généralement suivies d'environ 37 autres lignes de VBLANK avant le commencement de la zone visible. Le nombre exact de lignes reste cependant sous le contrôle du logiciel.

Le temps disponible pendant le vertical blank est particulièrement précieux. Le 6507 peut l'utiliser pour effectuer une partie importante des calculs de logique du jeu, préparer les positions des objets graphiques, mettre à jour les variables, traiter les déplacements et préparer les données qui seront utilisées par le kernel. Il peut également lire les entrées des contrôleurs, mettre à jour certains paramètres du son et effectuer différents travaux qu'il serait difficile d'insérer pendant la portion visible de l'image.

Après le vertical blank commence le kernel visible, c'est-à-dire la partie du programme chargée de produire directement l'image affichée. Dans une configuration NTSC classique, cette zone comporte généralement 192 lignes visibles. Pendant ces lignes, le processeur doit écrire dans les registres du TIA à des cycles soigneusement déterminés afin de modifier les motifs, les couleurs, les positions et les autres paramètres graphiques au moment exact où le faisceau atteint la région correspondante de l'écran.

Le kernel vidéo constitue ainsi l'une des parties les plus contraintes d'un programme Atari 2600. Chaque ligne complète représente 228 color clocks, soit exactement 76 cycles du 6507, puisque trois color clocks correspondent à un cycle processeur. Le programmeur dispose donc d'un budget extrêmement précis de cycles pour exécuter les instructions nécessaires à la génération d'une ligne. Une instruction placée au mauvais endroit ou consommant exceptionnellement un cycle supplémentaire peut déplacer une écriture au TIA et modifier le résultat affiché.

Une fois les lignes visibles terminées, la trame entre dans la période d'overscan. Cette zone n'est normalement pas destinée à contenir une partie utile de l'image. Comme pendant le vertical blank, le processeur peut profiter de ce temps pour exécuter des tâches liées au jeu. Les calculs de positions, la logique générale, la lecture ou le traitement des entrées et les mises à jour audio peuvent ainsi être répartis entre le VBLANK et l'overscan afin de réserver autant de temps que possible au kernel pendant les lignes visibles.

Pour faciliter la synchronisation horizontale, le TIA fournit le registre WSYNC (Wait for SYNC). Lorsqu'un programme effectue une écriture dans WSYNC, le processeur est suspendu jusqu'à la fin de la ligne courante et reprend son exécution au début de la ligne suivante. Le programmeur peut ainsi aligner facilement le commencement d'une portion de code sur une nouvelle ligne vidéo sans devoir calculer manuellement tout le temps restant de la ligne précédente.

Cette suspension ne signifie pas que le TIA cesse de fonctionner. Le circuit vidéo poursuit normalement la génération du signal pendant que le processeur attend. Lorsque la nouvelle ligne commence, le 6507 est libéré et peut reprendre l'exécution de son programme. WSYNC constitue donc un mécanisme essentiel pour construire des kernels réguliers et maintenir une synchronisation stable d'une ligne à l'autre.

Au début d'une ligne NTSC, la zone visible ne commence pas immédiatement. Une ligne comporte environ 68 color clocks de blank horizontal avant les 160 color clocks correspondant à la partie visible. Comme le processeur fonctionne à raison d'un cycle pour trois color clocks, ces 68 color clocks représentent environ 22,67 cycles CPU. Après la reprise du processeur à la suite d'un WSYNC, il existe donc approximativement cette quantité de temps avant que le balayage n'atteigne la portion visible de la ligne.

Cette valeur de 22,67 cycles ne signifie toutefois pas que le programme dispose simplement de 22 ou 23 cycles interchangeables avant chaque affichage. Le processeur travaille avec des cycles entiers tandis que le TIA évolue selon les color clocks, trois fois plus rapides. La relation exacte entre le moment où une instruction effectue son accès au bus et la position du faisceau doit donc être prise en compte lorsqu'une écriture exige une précision de quelques color clocks.

Il faut également tenir compte du fait qu'une écriture dans un registre du TIA ne produit pas nécessairement son effet graphique exactement au même instant que le cycle d'écriture du 6507. Certains registres ou mécanismes internes du TIA possèdent leurs propres délais avant que le changement devienne visible. Le programmeur doit donc connaître non seulement le cycle auquel l'instruction écrit dans le registre, mais aussi le délai propre au registre concerné lorsqu'il souhaite obtenir un positionnement précis.

Cette combinaison de contraintes explique pourquoi la programmation graphique de l'Atari 2600 est souvent décrite comme une programmation cycle par cycle. Le programme doit organiser la trame entière, compter les lignes, utiliser VSYNC et VBLANK, synchroniser les lignes avec WSYNC et placer les écritures au TIA à des moments déterminés. Le VBLANK et l'overscan servent principalement à effectuer les travaux pouvant être différés, tandis que les 192 lignes visibles d'une trame NTSC typique sont consacrées à un kernel dont la temporisation doit rester étroitement synchronisée avec le balayage du téléviseur.

Synchronisation VSYNC, VBLANK et WSYNC

La génération d'une image stable sur l'Atari 2600 dépend d'une synchronisation très précise entre le MOS 6507, le TIA (Television Interface Adaptor) et le téléviseur. Le TIA ne gère pas automatiquement la structure verticale complète d'une trame : c'est le programme qui doit déterminer quand commencent et se terminent la synchronisation verticale, le vertical blank, la zone visible et l'overscan. Les registres VSYNC, VBLANK et WSYNC jouent donc un rôle central dans la construction du signal vidéo.

Le registre VSYNC commande la synchronisation verticale. Son bit 1 permet d'activer le signal correspondant : lorsque ce bit est positionné, le TIA génère l'état de synchronisation verticale nécessaire au téléviseur pour reconnaître la transition vers une nouvelle trame. Lorsque le programme efface ce bit, la période de synchronisation verticale se termine. Sur une trame NTSC conventionnelle, VSYNC est généralement maintenu actif pendant trois lignes complètes.

Le registre VBLANK contrôle principalement la période pendant laquelle la partie visible de l'image doit être masquée. Son bit 1 active le vertical blank : lorsqu'il est positionné, la sortie graphique correspondant aux objets du TIA est supprimée de la zone normalement visible. Le programme utilise cette période pour empêcher l'affichage pendant les phases où il prépare la prochaine image ou exécute des traitements qui ne peuvent pas être réalisés pendant le kernel visible.

VBLANK possède également des fonctions liées aux entrées des contrôleurs. Son bit 7 intervient dans la lecture des contrôleurs analogiques de type paddle. Lorsqu'il est activé, il provoque la décharge des condensateurs associés au circuit de mesure des paddles. Le programme peut ensuite relâcher cette décharge et mesurer le temps nécessaire pour que les entrées atteignent leur seuil logique, ce qui permet de déterminer la position des potentiomètres des paddles.

D'autres bits de VBLANK interviennent dans le comportement des verrous (latches) d'entrée, selon la fonction considérée et la révision du TIA. Ces mécanismes permettent notamment de mémoriser certains événements d'entrée afin que le programme puisse les détecter même si leur état change avant la lecture suivante. VBLANK ne doit donc pas être considéré uniquement comme un simple interrupteur vidéo : il participe également au fonctionnement de certaines entrées de la console.

Une séquence NTSC typique commence en maintenant le blanking actif. Le programme active d'abord VBLANK, de façon à empêcher l'apparition d'éléments graphiques pendant la préparation de la nouvelle trame. Il active ensuite VSYNC, puis maintient la synchronisation verticale pendant trois lignes, généralement en effectuant une écriture dans WSYNC pour chacune d'elles. Après ces trois lignes, le programme désactive VSYNC, tandis que VBLANK reste encore actif.

Le registre WSYNC (Wait for SYNC) permet précisément de synchroniser le processeur avec les lignes vidéo. Une écriture dans WSYNC suspend l'exécution du 6507 jusqu'au commencement de la ligne suivante. Le processeur reprend alors son programme à une position horizontale connue. Cette technique simplifie considérablement le comptage des lignes et constitue l'un des mécanismes fondamentaux utilisés par les kernels Atari 2600.

Une séquence schématique de début de trame NTSC peut donc être résumée ainsi : VBLANK activé, VSYNC activé, trois lignes synchronisées avec WSYNC, puis VSYNC désactivé. Le programme poursuit ensuite la période de vertical blank pendant le nombre de lignes prévu. Durant cette période, il peut effectuer les calculs de logique, préparer les positions des objets, lire les contrôleurs et effectuer d'autres traitements nécessaires à la prochaine image.

Une fois le nombre approprié de lignes de blank écoulé, le programme désactive VBLANK afin de commencer le kernel visible. Dans une organisation NTSC classique, celui-ci produit environ 192 lignes visibles. Le programme compte chacune de ces lignes et exécute le code nécessaire pour modifier les registres du TIA aux cycles appropriés. À la fin du kernel, VBLANK est réactivé afin d'entrer dans la période d'overscan, pendant laquelle l'affichage est de nouveau masqué et le processeur peut poursuivre les traitements du jeu.

Le comptage exact des lignes est essentiel. Il faut prendre en considération chaque ligne générée, y compris celle dont le commencement est provoqué ou encadré par une écriture de synchronisation. Une erreur d'une seule ligne répétée à chaque trame peut produire un nombre total incorrect de lignes et perturber la fréquence verticale. Sur certains téléviseurs cathodiques, de petites variations peuvent rester tolérables, alors que des écrans modernes ou des dispositifs de capture peuvent perdre plus facilement la synchronisation.

Il est également important de distinguer WSYNC de RSYNC. Alors que WSYNC attend la prochaine ligne sans redéfinir arbitrairement la position horizontale du TIA, RSYNC (Reset SYNC) remet le compteur horizontal du TIA à zéro. Il agit donc directement sur la référence utilisée pour la progression horizontale du faisceau.

RSYNC est rarement nécessaire dans un programme Atari 2600 conventionnel. Son utilisation demande beaucoup de prudence, car une écriture effectuée au mauvais moment modifie la structure temporelle de la ligne en cours. Le téléviseur peut alors recevoir une ligne possédant une synchronisation ou une longueur inhabituelle, ce qui peut provoquer une déformation horizontale, un déplacement ou d'autres anomalies visibles.

La synchronisation d'une trame Atari 2600 repose donc largement sur le logiciel. VSYNC définit la période de synchronisation verticale, VBLANK masque l'affichage et participe à certaines fonctions d'entrée, tandis que WSYNC permet au 6507 d'attendre proprement le commencement de la ligne suivante. RSYNC, beaucoup plus spécialisé, permet de remettre à zéro la référence horizontale mais doit être utilisé avec précaution. Le programme doit finalement compter lui-même toutes les lignes et organiser ces registres dans le bon ordre afin de produire une image stable et conforme au standard vidéo utilisé.

Carte des registres TIA en écriture

Registre Description Registre Description Registre Description Registre Description Registre Description
$00 VSYNC $01 VBLANK $02 WSYNC $03 RSYNC    
$04 NUSIZ0 $05 NUSIZ1 $06 COLUP0 $07 COLUP1    
$08 COLUPF $09 COLUBK $0A CTRLPF $0B REFP0 $0C REFP1
$0D PF0 $0E PF1 $0F PF2        
$10 RESP0 $11 RESP1 $12 RESM0 $13 RESM1 $14 RESBL
$15 AUDC0 $16 AUDC1 $17 AUDF0 $18 AUDF1    
$19 AUDV0 $1A AUDV1 $1B GRP0 $1C GRP1    
$1D ENAM0 $1E ENAM1 $1F ENABL        
$20 HMP0 $21 HMP1 $22 HMM0 $23 HMM1 $24 HMBL
$25 VDELP0 $26 VDELP1 $27 VDELBL        
$28 RESMP0 $29 RESMP1 $2A HMOVE $2B HMCLR $2C CXCLR

RESP*/RESM*/RESBL, WSYNC, HMOVE, HMCLR et CXCLR sont des strobes : la donnée écrite importe peu, mais le cycle exact importe énormément.

Carte des registres TIA en lecture

Registre Description
$00 CXM0P Collisions M0-P1/P0
$01 CXM1P Collisions M1-P0/P1
$02 CXP0FB Collisions P0-playfield/ball
$03 CXP1FB Collisions P1-playfield/ball
$04 CXM0FB Collisions M0-playfield/ball
$05 CXM1FB Collisions M1-playfield/ball
$06 CXBLPF Collision ball-playfield
$07 CXPPMM P0-P1 et M0-M1
$08--$0B INPT0--INPT3 paddles
$0C--$0D INPT4--INPT5 boutons de manette de jeux

Ce sont les adresses logiques indiquées par Atari pour une lecture. Les miroirs $30--$3D sont aussi couramment employés : TIA ne décode qu'une partie des lignes d'adresse. Ne pas en déduire que $00--$0D seraient exclusivement des registres d'écriture : le signal R/W sélectionné la banque interne lue ou écrite.

Les bits non pilotes peuvent contenir le dernier bus. Les collisions sont latchées jusqu'à CXCLR; lire ne les efface pas.

Couleurs et standards vidéo

La gestion des couleurs de l'Atari 2600 est assurée par le TIA (Television Interface Adaptor), qui produit directement le signal vidéo destiné au téléviseur. La console ne travaille donc pas avec des valeurs RGB comme les systèmes graphiques modernes. Les couleurs sont plutôt définies en fonction des caractéristiques du signal de télévision analogique, notamment la teinte et la luminance. Le résultat dépend par conséquent du standard vidéo utilisé - NTSC, PAL ou SECAM - mais également du matériel chargé de transformer et d'afficher le signal.

Le TIA possède quatre registres principaux permettant de définir les couleurs : COLUP0, COLUP1, COLUPF et COLUBK. COLUP0 détermine principalement la couleur du Player 0 et de son missile M0, tandis que COLUP1 joue le même rôle pour Player 1 et M1. COLUPF contrôle la couleur du playfield et de la balle BL, sous réserve des modes graphiques sélectionnés, et COLUBK définit la couleur générale de l'arrière-plan.

Chaque registre de couleur contient une valeur de 8 bits, mais tous les bits ne participent pas directement à la sélection de la couleur. Sur les TIA courants, les bits supérieurs déterminent principalement la teinte (hue), tandis que les bits 3 à 1 déterminent la luminance. Le bit 0 est ignoré pour la sélection normale de la couleur. Deux valeurs qui ne diffèrent que par ce dernier bit produisent donc normalement la même couleur.

Sur une console NTSC, le codage permet nominalement de sélectionner 16 teintes, chacune pouvant être associée à 8 niveaux de luminance. On peut donc décrire l'espace théorique comme 16 × 8, soit 128 combinaisons codées de teinte et de luminosité. Il ne faut cependant pas interpréter cela comme 128 couleurs visuellement parfaitement distinctes. Certaines combinaisons peuvent produire des couleurs très proches, des doublons apparents ou des niveaux pratiquement noirs selon le signal obtenu et le dispositif d'affichage.

La luminance permet de produire plusieurs niveaux de luminosité à l'intérieur d'une même famille de teintes. Un programme peut ainsi conserver approximativement une couleur tout en la rendant plus claire ou plus sombre. Cette possibilité est particulièrement utile pour créer des variations visuelles, des ombrages ou simplement différencier plusieurs éléments graphiques malgré les ressources très limitées du TIA.

Le codage des couleurs n'est cependant pas identique entre NTSC et PAL. Une même valeur écrite dans COLUP0, COLUP1, COLUPF ou COLUBK ne correspond donc pas nécessairement à la même teinte sur les deux versions de la console. Le TIA PAL utilise un codage de teintes différent, ce qui signifie qu'un jeu converti depuis le NTSC doit adapter ses valeurs de couleurs s'il veut conserver une apparence proche de celle de la version originale.

La situation est encore différente avec le standard SECAM. Sur les Atari 2600 SECAM, la palette disponible est fortement réduite par rapport aux versions NTSC et PAL. Le système fournit essentiellement huit couleurs fixes, sélectionnées au moyen des valeurs qui correspondent normalement aux différents niveaux de luminance. Le choix des teintes est donc beaucoup plus limité, ce qui oblige souvent les développeurs à repenser les associations de couleurs lorsqu'un jeu est adapté au SECAM.

Pour cette raison, une conversion de couleurs entre NTSC, PAL et SECAM ne devrait pas être réalisée à l'aide d'une simple formule mathématique supposant une correspondance régulière entre les palettes. Il est généralement préférable d'utiliser une table de conversion, dans laquelle chaque couleur d'un standard est associée à la couleur jugée la plus appropriée dans l'autre standard. Cette approche permet de tenir compte des différences réelles de codage et d'obtenir un résultat visuel plus cohérent.

Il faut également garder à l'esprit que les valeurs numériques écrites dans les registres du TIA ne déterminent pas à elles seules la couleur finalement observée. Sur le matériel original, le signal généré par la console passe par différents circuits analogiques avant d'atteindre l'écran. La sortie RF, le modulateur de la console, les caractéristiques du téléviseur, ses réglages et la manière dont il décode le signal peuvent tous modifier la couleur effectivement affichée.

La révision du TIA constitue elle aussi un facteur important. Différentes variantes et révisions du circuit ont existé au cours de la longue période de fabrication de l'Atari 2600, et leurs caractéristiques analogiques ne sont pas nécessairement parfaitement identiques. Deux consoles utilisant théoriquement les mêmes valeurs de registres peuvent donc produire des couleurs légèrement différentes lorsqu'elles sont mesurées sur du matériel réel.

Cette variabilité explique également pourquoi il n'existe pas nécessairement une unique palette RGB moderne pouvant être considérée comme la représentation absolument exacte de l'Atari 2600. Les palettes utilisées par les émulateurs sont généralement construites à partir de mesures, de modèles du signal vidéo ou d'approximations destinées à reproduire l'apparence d'un téléviseur donné. Plusieurs palettes peuvent ainsi être techniquement défendables tout en produisant des résultats visuellement différents.

La gestion des couleurs de l'Atari 2600 doit donc être comprise dans son contexte analogique. Les registres COLUP0, COLUP1, COLUPF et COLUBK définissent une combinaison de teinte et de luminance, avec un bit 0 normalement ignoré, mais le résultat final dépend du standard vidéo et du matériel. Le NTSC offre nominalement 16 teintes × 8 luminances, le PAL emploie un codage de teintes différent, tandis que le SECAM réduit essentiellement le choix à huit couleurs fixes. Pour convertir les couleurs ou les reproduire dans un émulateur, une table adaptée au standard et fondée sur le comportement réel du matériel est donc généralement préférable à une simple formule.

Playfield

Le Playfield (PF) du TIA constitue l'un des principaux moyens de créer les décors et les formes graphiques de fond sur l'Atari 2600. Contrairement à un système bitmap dans lequel chaque pixel de l'écran possède une valeur enregistrée en mémoire, le TIA construit le playfield à partir d'un motif extrêmement compact. Trois registres, PF0, PF1 et PF2, fournissent ensemble 20 bits graphiques décrivant normalement la moitié gauche de la zone visible. Ces bits sont ensuite utilisés par le TIA pour construire également la moitié droite de la ligne.

Les 20 bits ne sont toutefois pas répartis de manière parfaitement uniforme entre les trois registres. PF0 ne fournit que quatre bits graphiques utiles, alors que PF1 et PF2 en fournissent huit chacun. Les quatre autres bits de PF0 ne participent pas au motif du playfield. De plus, les bits graphiques de PF0 sont exploités dans un ordre particulier, et PF1 et PF2 n'ont pas la même orientation lors de leur affichage. Cette disposition provient directement de l'organisation interne du TIA et doit être prise en compte lorsque le programme construit les données graphiques.

Une moitié de playfield comporte donc 20 éléments horizontaux. Comme la zone visible standard possède 160 color clocks, chacun de ces éléments occupe normalement plusieurs color clocks horizontalement. Le playfield est ainsi beaucoup moins précis horizontalement que les objets Player, mais il permet de construire efficacement des murs, plateformes, labyrinthes, terrains de jeu, chiffres ou autres éléments relativement larges avec seulement quelques octets de données.

La manière dont la moitié droite est construite dépend du bit 0 du registre CTRLPF. Dans le mode normal, la séquence des 20 bits de la première moitié est répétée sur la seconde moitié. Le côté droit reproduit alors le motif dans la même organisation générale. Lorsque le bit 0 de CTRLPF sélectionne le mode reflect, la seconde moitié est au contraire réfléchie horizontalement, ce qui permet de produire facilement un décor symétrique.

Cette possibilité est particulièrement adaptée à de nombreux jeux Atari 2600. Un terrain, un labyrinthe ou une aire de jeu symétrique peut être représenté avec seulement 20 bits, le TIA se chargeant de générer automatiquement l'autre moitié. Cette économie de données est importante sur une console disposant de seulement 128 octets de RAM et dont les premières cartouches ne contenaient que quelques kilo-octets de ROM.

Le registre CTRLPF contrôle également plusieurs autres caractéristiques du playfield. Son bit 1 active le mode score. Dans ce mode, le playfield n'utilise plus simplement une couleur unique sur toute sa largeur : la moitié gauche prend la couleur associée à Player 0, tandis que la moitié droite prend celle de Player 1. Plus précisément, les couleurs proviennent des registres COLUP0 et COLUP1. Cette fonction a notamment été prévue pour faciliter l'affichage de zones de score différenciées pour deux joueurs sans nécessiter de sprites supplémentaires.

Le bit 2 de CTRLPF contrôle la priorité graphique entre le playfield et certains objets mobiles. Lorsqu'il active la priorité du playfield, le playfield et la balle sont placés visuellement devant les objets Player 0, Player 1, Missile 0 et Missile 1 lorsque leurs pixels se superposent. Dans l'autre configuration, les joueurs et missiles peuvent apparaître devant le playfield. Ce mécanisme permet de simuler simplement des relations de profondeur entre les différents éléments de l'image.

Les bits 4 et 5 de CTRLPF déterminent quant à eux la largeur de la balle (BL). Ils permettent de sélectionner plusieurs tailles horizontales pour cet objet. La balle partage ainsi certaines caractéristiques de contrôle avec le playfield, même si elle reste un objet graphique distinct possédant son propre mécanisme de positionnement et d'activation.

Si les valeurs de PF0, PF1 et PF2 restent constantes pendant toute une ligne, le TIA produit donc un playfield basé sur un motif de 20 bits répété ou réfléchi. Cette limitation peut sembler importante, puisqu'elle conduit naturellement à une structure identique ou symétrique entre les deux moitiés de l'écran. Cependant, le processeur peut modifier les registres du TIA pendant que la ligne est en cours de génération.

En réécrivant PF0, PF1 et PF2 après que la première moitié a été affichée mais avant que les parties correspondantes de la seconde moitié ne soient produites, le programme peut fournir un nouveau motif de 20 bits pour la partie droite. Il devient ainsi possible de construire un playfield asymétrique de 40 bits, malgré le fait que le TIA ne possède physiquement que 20 bits de registres de motif pour le playfield.

Cette technique demande toutefois une synchronisation extrêmement précise. Les trois registres PF ne sont pas tous lus ou exploités simultanément par le TIA. Chaque registre influence une portion déterminée du playfield au moment où cette portion est générée. Les écritures doivent donc être réalisées dans des fenêtres de cycles bien précises : écrire trop tôt peut modifier une partie de la première moitié qui devait encore être affichée, tandis qu'écrire trop tard peut empêcher les nouvelles données d'être utilisées pour la portion souhaitée de la seconde moitié.

Le nombre de cycles disponibles est d'autant plus limité que le MOS 6507 doit effectuer plusieurs écritures au TIA au cours de la même ligne. Le programmeur doit tenir compte de la durée des instructions, des modes d'adressage et du moment exact où leurs cycles d'écriture atteignent le bus. La création d'un playfield asymétrique constitue ainsi un exemple classique de programmation « racing the beam », dans laquelle le logiciel modifie les données graphiques juste avant qu'elles ne soient nécessaires à l'affichage.

Le playfield de l'Atari 2600 est donc à la fois très simple et remarquablement flexible. PF0, PF1 et PF2 fournissent 20 bits pour la moitié gauche, avec des ordres de bits particuliers ; CTRLPF bit 0 détermine si la moitié droite est répétée ou réfléchie, le bit 1 active le mode score, le bit 2 contrôle la priorité du playfield et de la balle, et les bits 4 et 5 règlent la largeur de la balle. En modifiant les registres PF au moment approprié pendant une ligne, le programme peut enfin dépasser la structure prévue par le matériel et produire un playfield asymétrique de 40 bits.

Players

Le TIA (Television Interface Adaptor) de l'Atari 2600 possède deux objets graphiques principaux appelés Player 0 (P0) et Player 1 (P1). Malgré leur nom, ces objets ne sont pas nécessairement utilisés pour représenter des personnages contrôlés par les joueurs. Ils constituent essentiellement deux générateurs de sprites matériels, que le programme peut employer pour dessiner des personnages, véhicules, ennemis, objets, chiffres ou tout autre élément graphique. Comme pour le reste du système vidéo de l'Atari 2600, leurs possibilités sont limitées en matériel, mais peuvent être considérablement étendues par des modifications effectuées en temps réel par le 6507.

L'apparence horizontale de chaque player est définie par un registre graphique de 8 bits. Le registre GRP0 (Graphics Player 0) contient le motif de Player 0, tandis que GRP1 (Graphics Player 1) contient celui de Player 1. Chaque bit représente un pixel logique du sprite : un bit actif demande l'affichage de la couleur du player à cet emplacement, tandis qu'un bit inactif laisse apparaître l'élément graphique situé derrière lui selon les règles de priorité du TIA.

Un player possède donc naturellement une largeur de 8 pixels logiques, mais cela ne signifie pas qu'il est limité à une seule représentation de huit color clocks. Le TIA peut modifier la taille et le nombre de copies de l'objet grâce aux registres NUSIZ0 et NUSIZ1 (Number-Size). Ces registres permettent de sélectionner différentes configurations dans lesquelles un player peut apparaître une seule fois ou être automatiquement dupliqué plusieurs fois horizontalement, avec différents espacements entre les copies.

NUSIZ0 contrôle Player 0 et NUSIZ1 contrôle Player 1. Selon la configuration choisie, le TIA peut produire une, deux ou trois copies du motif correspondant. Certaines configurations permettent également d'augmenter la largeur horizontale du player, en agrandissant chacun de ses pixels logiques. Le même motif de 8 bits peut ainsi être affiché à sa taille normale ou avec un facteur d'agrandissement horizontal, ce qui permet d'obtenir des objets visuellement plus larges sans avoir à fournir davantage de données graphiques.

L'orientation du motif peut également être modifiée. Les registres REFP0 et REFP1 contrôlent respectivement la réflexion horizontale de Player 0 et Player 1. Leur bit 3 détermine si l'ordre d'affichage des huit bits du motif doit être inversé. Lorsque cette fonction est activée, le sprite apparaît donc comme son image miroir horizontale. Cette possibilité est particulièrement utile pour faire regarder un personnage vers la gauche ou vers la droite sans conserver deux versions de son motif en ROM.

La couleur des players est définie par les registres COLUP0 et COLUP1. COLUP0 fournit la couleur utilisée par Player 0, tandis que COLUP1 fournit celle de Player 1. Comme pour les autres couleurs du TIA, la valeur combine des informations de teinte et de luminance, dont l'interprétation exacte dépend du standard vidéo NTSC, PAL ou SECAM. La couleur demeure active jusqu'à ce que le programme réécrive le registre correspondant.

Le positionnement horizontal des players constitue une particularité importante de l'architecture du TIA. Il n'existe pas de registre classique contenant une coordonnée X directement lisible ou inscriptible. Le programme ne peut donc pas simplement écrire une valeur telle que X = 80 dans un registre de position. À la place, chaque player possède un compteur horizontal interne dont la position est établie relativement à la progression actuelle du faisceau vidéo.

Les registres RESP0 et RESP1 (Reset Player) servent à effectuer ce positionnement grossier. Une écriture dans RESP0 réinitialise ou resynchronise le compteur horizontal de Player 0 relativement à la position actuelle du balayage ; RESP1 effectue la même opération pour Player 1. Le moment exact auquel l'écriture est réalisée détermine donc l'endroit où le player apparaîtra. Le 6507 doit attendre ou consommer un nombre précisément calculé de cycles avant d'effectuer cette écriture.

Cette méthode signifie que la position horizontale d'un player est fondamentalement temporelle. Pour déplacer un objet vers la droite ou vers la gauche, le programme ne modifie pas simplement une coordonnée stockée dans le TIA : il organise ses instructions de manière à atteindre RESP0 ou RESP1 au moment approprié par rapport au faisceau. C'est une nouvelle manifestation du principe de programmation souvent appelé « racing the beam ».

Le positionnement obtenu avec RESP0 ou RESP1 peut ensuite être affiné à l'aide des registres de mouvement horizontal HMP0 et HMP1. Ces registres permettent d'appliquer un ajustement horizontal fin à Player 0 ou Player 1. Le programme combine donc généralement un positionnement grossier obtenu par l'écriture dans RESPx avec une correction fournie par HMPx, puis utilise les mécanismes de mouvement horizontal du TIA pour appliquer cet ajustement.

Cette combinaison permet d'obtenir une position beaucoup plus précise sans devoir placer l'écriture RESPx à n'importe quel color clock. En pratique, les routines de positionnement Atari 2600 utilisent souvent une séquence soigneusement temporisée pour atteindre une position approximative, puis enregistrent dans HMP0 ou HMP1 la correction nécessaire pour atteindre la position horizontale exacte souhaitée.

La situation est encore plus particulière pour le positionnement vertical. Le TIA ne possède aucune coordonnée Y matérielle pour Player 0 ou Player 1. Il n'existe donc aucun registre dans lequel le programme pourrait indiquer directement la ligne de début et la hauteur d'un sprite. Le TIA ne sait d'ailleurs pas lui-même quelle ligne verticale de la trame est actuellement affichée, puisque le comptage vertical est réalisé par le logiciel.

C'est le programme qui crée la position verticale du sprite en choisissant les lignes sur lesquelles il écrit les données graphiques dans GRP0 ou GRP1. Lorsqu'une ligne doit contenir une partie du sprite, le kernel charge dans le registre GRPx le motif correspondant à cette ligne. Pour la ligne suivante, il peut charger un autre octet représentant la ligne suivante du dessin. En répétant cette opération pendant plusieurs lignes vidéo, le programme construit progressivement un sprite possédant une hauteur arbitraire.

En dehors de la zone verticale où le sprite doit apparaître, le programme peut écrire une valeur nulle dans GRP0 ou GRP1, ce qui rend le player graphiquement vide, ou organiser son kernel afin que les données appropriées ne soient affichées que pendant les lignes concernées. La coordonnée Y et la hauteur du sprite sont donc des concepts gérés entièrement par logiciel plutôt que des propriétés matérielles enregistrées dans le TIA.

Cette technique offre paradoxalement une grande souplesse. Puisque les registres GRP0 et GRP1 peuvent être modifiés à chaque ligne, un player n'est pas limité à un motif vertical fixe. Le programme peut choisir une nouvelle valeur de 8 bits pour chaque scanline et ainsi construire des sprites de plusieurs lignes, des animations ou d'autres formes graphiques. Le prix à payer est une consommation importante du temps processeur pendant le kernel visible.

Les players de l'Atari 2600 reposent donc sur une philosophie très différente de celle des systèmes à sprites plus récents. GRP0 et GRP1 fournissent chacun un motif horizontal de 8 bits, NUSIZ0 et NUSIZ1 déterminent le nombre de copies et leur échelle ou espacement, REFP0 et REFP1 bit 3 permettent la réflexion horizontale et COLUP0/COLUP1 définissent leurs couleurs. Horizontalement, leur position résulte d'une écriture temporisée dans RESP0/RESP1, complétée par un ajustement avec HMP0/HMP1. Verticalement, il n'existe aucune coordonnée Y matérielle : c'est le logiciel qui construit entièrement le sprite, ligne après ligne, en modifiant GRP0 et GRP1 au moment approprié.

Missiles et balle

En plus des deux objets graphiques principaux Player 0 (P0) et Player 1 (P1), le TIA (Television Interface Adaptor) de l'Atari 2600 possède trois objets matériels plus simples : Missile 0 (M0), Missile 1 (M1) et la balle (BL, Ball). Ces objets ne disposent pas de motifs graphiques de 8 bits comparables à ceux des players. Ils correspondent plutôt à des formes horizontales simples dont le programme peut contrôler l'activation, la largeur et la position. Malgré leurs noms, les missiles et la balle peuvent naturellement être utilisés pour représenter bien d'autres éléments graphiques.

L'affichage des deux missiles est commandé par les registres ENAM0 et ENAM1 (Enable Missile). Dans chacun de ces registres, le bit 1 détermine si le missile correspondant est actif. Lorsque le bit 1 de ENAM0 est positionné, Missile 0 peut être affiché ; de la même manière, le bit 1 de ENAM1 contrôle Missile 1. En effaçant ce bit, le programme désactive l'objet correspondant.

La balle utilise un mécanisme similaire avec le registre ENABL (Enable Ball). Son bit 1 commande l'activation de la balle. Le programme peut donc faire apparaître ou disparaître cet objet en modifiant simplement ce bit. Comme les registres du TIA conservent normalement leur valeur jusqu'à une nouvelle écriture, l'état d'activation reste en vigueur jusqu'à ce que le logiciel le modifie.

La largeur horizontale des missiles est déterminée par les bits 4 et 5 des registres NUSIZ0 et NUSIZ1. NUSIZ0 contrôle notamment la largeur de Missile 0, tandis que NUSIZ1 remplit la même fonction pour Missile 1. Ces bits permettent de choisir plusieurs largeurs, ce qui autorise le programme à représenter un missile sous la forme d'un trait très fin ou d'un objet progressivement plus large.

La largeur de la balle est contrôlée séparément. Les bits 4 et 5 du registre CTRLPF déterminent sa taille horizontale. CTRLPF remplit donc plusieurs fonctions : en plus de contrôler certaines caractéristiques du playfield, comme la réflexion, le mode score et la priorité graphique, il détermine également la largeur de l'objet Ball.

Les missiles ne possèdent pas leurs propres registres de couleur. Missile 0 utilise la couleur associée à Player 0, c'est-à-dire celle définie par COLUP0, tandis que Missile 1 utilise la couleur de Player 1, définie par COLUP1. Un changement de COLUP0 affecte donc à la fois P0 et M0, et une modification de COLUP1 affecte P1 et M1.

La balle partage quant à elle sa couleur avec le playfield. Elle utilise la couleur définie dans le registre COLUPF. Ainsi, le playfield et la balle possèdent normalement la même valeur de couleur à un instant donné. Comme les registres de couleur peuvent être réécrits pendant la génération de l'image, le logiciel peut néanmoins modifier cette valeur au cours d'une ligne ou entre différentes lignes s'il respecte les contraintes de temporisation du TIA.

Comme pour les players, les missiles ne possèdent pas une simple coordonnée X dans laquelle le programme pourrait écrire leur position horizontale. Le positionnement repose sur des compteurs internes synchronisés avec le balayage. Les registres RESM0 et RESM1 permettent de repositionner respectivement Missile 0 et Missile 1 relativement à la position actuelle du faisceau. Le moment auquel le 6507 effectue l'écriture détermine donc leur positionnement horizontal grossier.

Les registres HMM0 et HMM1 fournissent ensuite un ajustement horizontal fin pour les deux missiles. Ils jouent pour M0 et M1 un rôle comparable à celui de HMP0 et HMP1 pour les players. Le programme peut ainsi combiner une écriture temporisée dans RESMx avec une valeur de mouvement enregistrée dans HMMx afin d'obtenir la position horizontale désirée.

Le TIA propose cependant un mécanisme supplémentaire particulièrement pratique pour associer un missile à son player. Les registres RESMP0 et RESMP1 (Reset Missile to Player) permettent de maintenir le missile correspondant dans une position relative au centre de son player. Le bit 1 de RESMP0 contrôle cette fonction pour Missile 0, tandis que le bit 1 de RESMP1 la contrôle pour Missile 1.

Lorsque le bit 1 de RESMPx est activé, le missile correspondant est verrouillé relativement à son player. Sa position suit alors le mécanisme de positionnement du player plutôt que d'être gérée comme un objet totalement indépendant. Cette fonction est notamment utile pour préparer un projectile : le missile peut être maintenu au niveau du player tant qu'il n'a pas été lancé.

Au moment où le projectile doit être libéré, le programme peut désactiver le verrouillage fourni par RESMPx. Le missile conserve alors une position issue de cette relation et peut ensuite être déplacé indépendamment. Le programme utilise pour cela son propre repositionnement avec RESMx et son réglage de mouvement horizontal HMMx. Il devient ainsi possible de faire partir un projectile depuis le player puis de gérer séparément son déplacement à l'écran.

Comme pour les players, il n'existe pas de véritable coordonnée Y matérielle pour les missiles ou la balle. Leur présence verticale est obtenue par logiciel en activant ou désactivant ENAM0, ENAM1 ou ENABL sur les lignes appropriées. Le programme peut ainsi construire leur hauteur apparente en décidant sur quelles scanlines chaque objet doit être visible.

Les missiles et la balle complètent donc les ressources graphiques très limitées mais flexibles du TIA. ENAM0 et ENAM1 bit 1 activent les missiles, tandis que ENABL bit 1 active la balle. Les bits 4 et 5 de NUSIZ0/NUSIZ1 déterminent la largeur des missiles et les bits 4 et 5 de CTRLPF celle de la balle. M0 et M1 utilisent respectivement les couleurs COLUP0 et COLUP1, alors que BL utilise COLUPF. Enfin, RESMPx bit 1 permet de verrouiller un missile relativement au centre de son player avant de le libérer, tandis que RESMx et HMMx permettent ensuite de le positionner et de le déplacer indépendamment.

Positionnement horizontal

Le positionnement horizontal des objets graphiques de l'Atari 2600 constitue l'une des particularités les plus importantes du TIA (Television Interface Adaptor). Contrairement aux circuits graphiques plus modernes, le TIA ne fournit pas simplement un registre contenant une coordonnée X pour chaque objet. Le programme ne peut donc pas demander directement au matériel de placer Player 0 à une position telle que X = 80. La position est obtenue en synchronisant précisément les écritures du MOS 6507 avec la progression horizontale du faisceau vidéo.

Pour les players, les registres RESP0 et RESP1 (Reset Player) jouent un rôle essentiel. Lorsqu'une écriture est effectuée dans RESP0, le compteur horizontal interne associé à Player 0 est réinitialisé relativement à la position actuelle du balayage. RESP1 réalise la même opération pour Player 1. L'objet ne commence toutefois pas nécessairement exactement au color clock correspondant à l'écriture : le fonctionnement interne du TIA introduit un décalage fixe entre l'instant de l'écriture et la position à laquelle le player apparaît.

La position horizontale dépend donc directement du moment où l'écriture dans RESPx atteint le TIA. Pour déplacer un player vers la droite, le programme attend généralement plus longtemps avant d'effectuer cette écriture ; pour le déplacer vers la gauche, il l'effectue plus tôt. Cette méthode transforme le temps d'exécution des instructions du 6507 en une véritable unité de positionnement graphique.

Une routine classique commence par effectuer une écriture dans WSYNC afin de suspendre le processeur jusqu'au commencement de la ligne suivante. Le 6507 reprend alors son exécution à une position horizontale connue. À partir de ce point de référence, le programme consomme un nombre soigneusement calculé de cycles avant d'écrire dans RESP0, RESP1 ou le registre de repositionnement de l'objet concerné.

Une technique courante de positionnement est fondée sur une boucle souvent décrite comme une routine « divide-by-15 ». La position X désirée est décomposée en une partie grossière correspondant à des groupes d'environ 15 color clocks et en un reste. Une boucle temporisée consomme alors le nombre de cycles nécessaire pour rapprocher le faisceau de la position recherchée. Lorsque la bonne zone est atteinte, le programme écrit dans RESPx afin d'établir la position grossière du player.

Cette méthode ne permet pas à elle seule d'obtenir toutes les positions avec une précision suffisante. Le reste de la division sert donc à calculer une correction de mouvement fin, enregistrée dans le registre HMPx (Horizontal Motion Player). Pour Player 0 et Player 1, il s'agit respectivement de HMP0 et HMP1. Le TIA peut ensuite appliquer cette correction grâce à son mécanisme de mouvement horizontal, ce qui permet d'atteindre la position exacte souhaitée.

Le positionnement horizontal est ainsi généralement réalisé en deux étapes complémentaires. La première fournit une position grossière en synchronisant le code avec le balayage et en écrivant dans RESPx. La seconde fournit une correction fine au moyen de HMPx. Cette combinaison évite d'avoir à produire une écriture de repositionnement à chacun des 160 emplacements visibles possibles uniquement en variant le nombre de cycles du programme.

Le principe ne concerne pas seulement les deux players. Les cinq objets mobiles du TIA - Player 0, Player 1, Missile 0, Missile 1 et Ball - possèdent chacun leur propre mécanisme de compteur horizontal. Les players utilisent RESP0 et RESP1, les missiles RESM0 et RESM1, et la balle RESBL. Leurs corrections de mouvement fin utilisent respectivement les registres HMP0, HMP1, HMM0, HMM1 et HMBL.

Il ne faut cependant pas supposer que les cinq objets réagissent de manière parfaitement identique. Leurs compteurs internes présentent des différences et des décalages subtils. Une écriture réalisée au même instant dans les registres de repositionnement de deux types d'objets ne garantit donc pas nécessairement que leurs bords gauches apparaîtront exactement au même emplacement. Ces différences proviennent du fonctionnement interne du TIA et doivent être prises en considération dans les routines de positionnement précises.

Les positions situées près des bords de la zone visible demandent également une attention particulière. Les compteurs horizontaux du TIA fonctionnent de manière cyclique : lorsqu'un déplacement ou un repositionnement dépasse certaines limites, la position peut s'enrouler (wrap around). Un objet déplacé au-delà d'un bord peut ainsi réapparaître de l'autre côté selon son état, son compteur et le mouvement appliqué. Ce comportement peut être utilisé volontairement, mais il peut également provoquer des résultats inattendus si la routine de positionnement ne le prend pas en compte.

Pour cette raison, une routine de positionnement Atari 2600 ne devrait pas être construite uniquement à partir d'une interprétation théorique des coordonnées. Il est préférable d'utiliser une routine dont les cycles ont été précisément mesurés, avec des valeurs et des décalages connus pour chaque type d'objet. Des tables précalculées peuvent également convertir rapidement une coordonnée X souhaitée en paramètres de positionnement grossier et en valeur de mouvement fin, ce qui économise du temps processeur pendant les portions critiques de la trame.

Les tables sont particulièrement utiles parce que le 6507 ne possède ni multiplication ni division matérielle et que les calculs doivent souvent être effectués dans un budget de cycles très limité. Une table peut par exemple fournir directement la valeur destinée à HMPx à partir du reste obtenu par la routine de positionnement, évitant plusieurs opérations arithmétiques pendant l'exécution.

Enfin, le comportement exact du positionnement doit idéalement être validé sur un véritable TIA. Les émulateurs modernes reproduisent généralement très bien ces mécanismes, mais les détails de temporisation, les décalages internes et certains comportements aux limites peuvent révéler des différences. Tester le programme sur plusieurs émulateurs permet également de repérer une dépendance accidentelle à l'interprétation particulière d'un seul émulateur.

Le positionnement horizontal sur Atari 2600 est donc un mécanisme fondamentalement temporel. Une écriture dans RESPx effectuée lorsque le faisceau atteint une certaine position réinitialise le compteur de l'objet avec un décalage déterminé ; une routine synchronisée par WSYNC, souvent organisée autour d'une méthode divide-by-15, fournit le positionnement grossier ; et le reste est transformé en correction fine dans HMPx. Comme les cinq objets possèdent des compteurs et des offsets légèrement différents et que les positions peuvent s'enrouler près des bords, les routines les plus fiables reposent sur des temporisations mesurées, des tables précalculées et une validation sur matériel réel ainsi que sur plusieurs émulateurs.

Mouvement fin et HMOVE

Le TIA (Television Interface Adaptor) de l'Atari 2600 possède un mécanisme de mouvement horizontal fin permettant d'ajuster avec précision la position de ses cinq objets mobiles : Player 0 (P0), Player 1 (P1), Missile 0 (M0), Missile 1 (M1) et la balle (BL). Ce système complète le positionnement grossier obtenu avec les registres de remise à zéro des compteurs horizontaux, tels que RESP0, RESP1, RESM0, RESM1 et RESBL. Il permet ainsi de positionner les objets à des emplacements qui ne pourraient pas être atteints uniquement en choisissant le cycle auquel leur compteur est réinitialisé.

Chaque objet possède son propre registre de mouvement horizontal. HMP0 (Horizontal Motion Player 0) contrôle Player 0, HMP1 contrôle Player 1, HMM0 et HMM1 contrôlent respectivement Missile 0 et Missile 1, tandis que HMBL contrôle la balle. Ces registres ne déplacent toutefois pas immédiatement les objets au moment où ils sont écrits. Ils servent plutôt à mémoriser les corrections qui seront appliquées lorsque le mécanisme de mouvement du TIA sera déclenché.

La valeur de mouvement est codée dans le nibble supérieur, c'est-à-dire les bits 7 à 4, de chacun de ces registres. Les quatre bits inférieurs ne constituent pas la valeur principale du déplacement. Le nibble supérieur représente un déplacement signé permettant d'obtenir un ajustement compris entre -8 et +7 color clocks, selon le codage particulier employé par le TIA. Un color clock constitue ici l'unité élémentaire de positionnement horizontal du circuit vidéo.

Il est important de ne pas interpréter naïvement ce nibble comme une coordonnée X conventionnelle. Le TIA possède son propre codage du mouvement horizontal, et les valeurs binaires doivent être associées aux déplacements correspondant au fonctionnement réel du circuit. Les routines de positionnement utilisent donc souvent une table précalculée pour convertir un reste ou une correction de position en valeur directement utilisable dans HMP0, HMP1, HMM0, HMM1 ou HMBL.

Une fois les valeurs de mouvement préparées, le programme effectue une écriture dans le registre HMOVE. Cette opération déclenche le mécanisme de mouvement horizontal du TIA et applique simultanément les corrections des cinq objets. Une seule écriture dans HMOVE agit donc sur P0, P1, M0, M1 et BL en utilisant la valeur précédemment enregistrée dans le registre de mouvement propre à chacun.

Cette application simultanée est importante lors de la conception d'un kernel. Même si le programme souhaite déplacer un seul player, une écriture dans HMOVE concerne potentiellement les cinq objets. Les registres de mouvement des autres objets doivent donc contenir des valeurs appropriées, généralement zéro s'ils ne doivent pas être déplacés. Une ancienne valeur laissée dans l'un de ces registres pourrait autrement provoquer un déplacement involontaire lors du prochain HMOVE.

Le registre HMCLR (Horizontal Motion Clear) permet précisément de remettre à zéro les registres de mouvement horizontal. Une écriture dans HMCLR efface les valeurs de HMP0, HMP1, HMM0, HMM1 et HMBL. Cette fonction permet au programme de repartir d'un état connu et d'éviter qu'une correction utilisée précédemment soit appliquée de nouveau lors d'un futur HMOVE.

Le mécanisme de mouvement horizontal du TIA possède cependant une particularité matérielle devenue célèbre : le HMOVE blank. Sur les TIA classiques, une écriture dans HMOVE effectuée au début d'une ligne peut provoquer l'apparition d'une barre noire d'environ huit color clocks sur le bord gauche de la zone visible. Une partie du commencement de la ligne est alors masquée indépendamment des pixels que le programme aurait normalement voulu afficher à cet endroit.

Cette barre noire n'est pas un objet graphique ordinaire comme un player ou le playfield. Elle résulte directement du fonctionnement interne du circuit de mouvement horizontal du TIA. Le mécanisme utilisé pour appliquer les corrections aux compteurs des objets influence temporairement la génération de l'image, ce qui produit cette zone de blanking supplémentaire. Le phénomène est donc lié au moment exact auquel HMOVE est écrit.

De nombreux kernels Atari 2600 effectuent volontairement HMOVE à une position où cette barre se produit de manière prévisible. Dans certains cas, le HMOVE blank est accepté ou même exploité, par exemple lorsque le bord gauche de l'image n'a pas besoin de contenir des informations importantes. Une barre régulière peut être préférable à des comportements de mouvement plus difficiles à contrôler, surtout lorsque le positionnement des objets doit être mis à jour fréquemment.

D'autres kernels cherchent au contraire à éviter le HMOVE blank. Une technique consiste à modifier le moment de l'écriture dans HMOVE, notamment en la plaçant plus tard par rapport au commencement de la ligne. Le choix du cycle est cependant critique : déplacer l'écriture modifie le comportement interne du TIA et peut également influencer la manière dont les corrections horizontales sont appliquées.

Il n'existe donc pas une règle simple selon laquelle toute écriture dans HMOVE produirait systématiquement une barre noire identique. L'effet exact dépend du cycle auquel l'écriture est effectuée, et certains timings produisent des résultats différents. Les routines qui tentent de supprimer ou de contrôler le HMOVE blank doivent par conséquent être conçues avec une précision de cycle comparable à celle utilisée pour les autres techniques avancées de programmation du TIA.

La révision du TIA peut également influencer le résultat. Plusieurs versions du circuit ont été produites pendant la longue durée de fabrication de l'Atari 2600, et certains détails du HMOVE blank et des comportements associés ne sont pas nécessairement identiques sur toutes les puces. Une technique fonctionnant de manière précise sur un TIA particulier peut donc présenter de légères différences sur une autre révision ou sur une reproduction imparfaite du comportement matériel dans un émulateur.

Le mouvement fin de l'Atari 2600 repose ainsi sur une combinaison de registres et de temporisations. HMP0, HMP1, HMM0, HMM1 et HMBL conservent dans leur nibble supérieur une correction signée comprise entre -8 et +7 color clocks selon le codage du TIA ; HMOVE applique simultanément ces cinq corrections, tandis que HMCLR remet les registres de mouvement à zéro. Une écriture HMOVE au début d'une ligne peut en outre provoquer le célèbre HMOVE blank d'environ huit color clocks, effet que les kernels peuvent accepter, exploiter ou tenter d'éviter grâce à un placement soigneusement choisi de l'écriture. Son comportement précis reste dépendant du cycle d'exécution et de la révision du TIA.

Copies, tailles et réflexion

Le TIA (Television Interface Adaptor) de l'Atari 2600 permet d'obtenir davantage d'éléments graphiques à partir de ses ressources matérielles limitées grâce aux registres NUSIZ0 et NUSIZ1 (Number-Size). Ces registres contrôlent respectivement les caractéristiques de Player 0 et Player 1, ainsi que certains paramètres de leurs missiles associés. Le TIA peut ainsi reproduire automatiquement un même player plusieurs fois sur une ligne ou augmenter sa largeur, sans que le processeur ait à redessiner chaque copie individuellement.

Les bits 0 à 2 de NUSIZ0 et NUSIZ1 sélectionnent la configuration du player correspondant. Selon la valeur choisie, le motif de 8 bits contenu dans GRP0 ou GRP1 peut apparaître sous la forme d'un player unique, de deux ou trois copies séparées par différents espacements, ou encore d'un player dont la largeur est doublée ou quadruplée. Le même motif graphique peut donc produire plusieurs configurations horizontales très différentes.

Dans le cas normal, chaque bit du registre GRPx correspond à un pixel logique du player à sa taille de base. Lorsque le mode de largeur double est sélectionné, chaque pixel logique reste actif pendant une durée horizontale plus longue, ce qui produit un sprite deux fois plus large. Avec la largeur quadruple, cet étirement est encore augmenté. Le motif conserve toujours ses huit bits : c'est le TIA qui modifie leur durée d'affichage horizontalement.

Les modes à copies multiples fonctionnent différemment de l'agrandissement. Le TIA conserve la taille normale du motif, mais ses compteurs internes déclenchent plusieurs fois l'affichage du même player au cours de la ligne. Selon les bits 0 à 2 de NUSIZx, deux ou trois exemplaires peuvent ainsi apparaître avec des espacements prédéterminés. Cette fonction permet de produire plusieurs objets apparents à partir d'un seul générateur matériel.

Les missiles M0 et M1 sont liés à cette organisation. Ils partagent le schéma de copies du player auquel ils sont associés. Ainsi, la configuration sélectionnée dans NUSIZ0 influence le comportement de répétition de Missile 0 en relation avec Player 0, tandis que NUSIZ1 joue le même rôle pour Missile 1 et Player 1. Les missiles disposent néanmoins de leur propre réglage de largeur, commandé par les bits 4 et 5 du registre NUSIZx.

La réflexion horizontale des players est contrôlée séparément par les registres REFP0 et REFP1. Leur bit 3 permet d'inverser l'ordre d'affichage du motif de huit bits contenu dans GRP0 ou GRP1. Un player peut donc être affiché normalement ou sous la forme de son miroir horizontal, sans qu'il soit nécessaire de conserver une seconde version inversée de son dessin dans la ROM.

Ces fonctions semblent à première vue définir des propriétés relativement statiques : le programme pourrait choisir une taille, un nombre de copies, une réflexion et un motif au début de la ligne, puis laisser le TIA afficher le résultat. Mais l'architecture de l'Atari 2600 permet une utilisation beaucoup plus avancée, puisque les registres du TIA peuvent être réécrits pendant que la ligne est en cours de génération.

Le programme peut notamment modifier NUSIZ0 ou NUSIZ1 pendant la ligne. Une partie de la ligne peut ainsi être générée avec une configuration de copies ou une taille donnée, puis une autre partie avec une configuration différente. Cette technique permet de détourner un seul générateur Player afin de produire plusieurs formes ou plusieurs objets apparents au cours de la même scanline.

De la même manière, une modification de REFP0 ou REFP1 pendant le balayage peut changer l'orientation du motif pour une portion ultérieure de son affichage. Le programme peut donc modifier dynamiquement la réflexion du player au lieu de conserver cette propriété pendant toute la ligne. Comme toujours sur le TIA, le résultat dépend du moment précis auquel l'écriture atteint le circuit.

Le registre graphique GRP0 ou GRP1 peut lui aussi être réécrit pendant la ligne. Le motif de 8 bits utilisé pour une apparition du player peut ainsi être remplacé avant une apparition ultérieure. Combinée aux modes de copies multiples de NUSIZ, cette technique permet à plusieurs copies normalement identiques d'afficher des motifs différents, donnant l'impression que le TIA possède davantage de sprites matériels qu'il n'en possède réellement.

Ces changements dynamiques ne réinitialisent cependant pas nécessairement toute la logique de génération du player. Les compteurs internes du TIA continuent leur progression pendant que le programme modifie NUSIZ, REFP ou GRP. Une écriture ne doit donc pas être interprétée comme le commencement d'un nouvel objet graphique indépendant. Elle modifie l'état utilisé par un mécanisme matériel qui est déjà en train de parcourir la ligne.

Cette caractéristique rend le cycle exact de l'écriture particulièrement important. Une modification effectuée juste avant que le TIA ne commence à produire une copie peut influencer cette copie, tandis que la même écriture réalisée quelques color clocks plus tard peut n'affecter que la suivante ou produire un comportement intermédiaire. Les frontières entre les pixels, les copies et les phases des compteurs internes constituent ainsi des points critiques.

Les effets produits lorsqu'un registre est modifié exactement à la limite d'un pixel ou d'une copie sont particulièrement importants pour une émulation fidèle. Le TIA fonctionne comme un circuit matériel synchrone dont les différents compteurs, registres et générateurs graphiques évoluent selon l'horloge couleur. Selon le moment précis de l'écriture, une nouvelle valeur peut être prise en compte immédiatement ou seulement à une étape ultérieure du processus de génération.

Cette précision est nécessaire pour reproduire certains kernels de jeux commerciaux. Les développeurs de l'époque ont exploité les comportements temporels du TIA afin de dépasser les limites apparentes des deux players matériels. Un jeu peut donc dépendre volontairement d'une modification de NUSIZ, REFP ou GRP effectuée à un cycle très particulier pour produire son affichage correct.

Pour un émulateur Atari 2600, il ne suffit donc pas de connaître la valeur de ces registres une fois par ligne. Une émulation de haute compatibilité doit reproduire le moment auquel leurs modifications deviennent visibles et maintenir correctement l'état des compteurs internes des players et missiles. Sans cette précision, certains jeux peuvent présenter des sprites manquants, déformés, mal espacés ou différents de ceux produits par un véritable TIA.

Les registres NUSIZ0 et NUSIZ1 constituent ainsi un exemple remarquable de la souplesse obtenue à partir d'un matériel très réduit. Leurs bits 0 à 2 permettent de choisir entre un player simple, plusieurs configurations de deux ou trois copies, et des players de largeur double ou quadruple ; les missiles suivent le schéma de copies du player associé. En combinant ces fonctions avec des modifications de NUSIZ, REFP et GRP pendant la ligne, le programme peut produire plusieurs objets apparents à partir d'un seul générateur, à condition de respecter précisément la progression continue des compteurs du TIA et les effets des écritures aux limites exactes des pixels et des copies.

Priorités, score et collisions

Le TIA (Television Interface Adaptor) de l'Atari 2600 doit déterminer quel élément graphique apparaît lorsqu'au moins deux objets occupent simultanément la même position à l'écran. Il possède pour cela un système de priorités graphiques qui établit l'ordre d'affichage entre les players, les missiles, le playfield et la balle. Ce mécanisme est distinct de la détection des collisions : un objet peut être masqué visuellement par un autre tout en participant néanmoins à une collision détectée par le TIA.

Dans le mode de priorité normal, les players et les missiles sont placés visuellement devant le groupe formé par le playfield et la balle. Lorsqu'un pixel actif d'un player ou d'un missile se superpose à un pixel actif du playfield ou de la balle, c'est donc normalement le premier groupe qui possède la priorité pour déterminer ce qui est visible à cet emplacement.

Le registre CTRLPF permet de modifier cette organisation. Son bit de priorité - le bit 2 - inverse la priorité entre les deux groupes. Lorsque cette fonction est activée, le playfield et la balle passent devant les players et les missiles. Le programme peut ainsi créer simplement un effet de profondeur, par exemple en faisant passer un personnage derrière un mur ou un élément du décor.

CTRLPF contrôle également le mode score au moyen de son bit 1. Lorsque ce mode est activé, la moitié gauche du playfield utilise la couleur de Player 0, provenant de COLUP0, tandis que la moitié droite utilise la couleur de Player 1, provenant de COLUP1. Cette fonction permet notamment de différencier graphiquement les deux côtés d'un terrain ou d'une zone de score sans nécessiter d'objets supplémentaires. Elle modifie la sélection des couleurs, mais ne transforme pas pour autant le playfield en deux nouveaux objets de collision.

Le TIA possède en effet une logique matérielle spécialisée pour détecter les chevauchements entre ses différents objets graphiques. Lorsqu'au même instant deux objets concernés produisent des pixels actifs, le circuit peut mémoriser qu'une collision s'est produite. Cette détection est particulièrement utile pour déterminer, par exemple, qu'un missile a atteint un player, qu'un player touche le playfield ou que les deux players se rencontrent.

Les résultats sont regroupés dans huit registres de collision : CXM0P, CXM1P, CXP0FB, CXP1FB, CXM0FB, CXM1FB, CXBLPF et CXPPMM. Chaque registre contient des bits représentant certaines combinaisons de collisions. Ensemble, ils permettent notamment de détecter les interactions entre missiles et players, players et playfield ou balle, missiles et playfield ou balle, balle et playfield, ainsi que certaines collisions entre les players et entre les missiles.

Une caractéristique essentielle de ce système est que la détection des collisions est largement indépendante de la couleur finalement visible. Le TIA s'intéresse avant tout à l'activité des objets graphiques concernés. Si deux générateurs produisent simultanément des pixels actifs à la même position, une collision peut être enregistrée même si les règles de priorité font qu'un seul des deux objets est réellement visible sur le téléviseur.

Cela signifie également que changer simplement la couleur d'un objet pour lui donner la même valeur que l'arrière-plan ne le supprime pas nécessairement du système de collisions. Un player dont le motif GRPx contient toujours des bits actifs peut continuer à participer aux collisions même si sa couleur est identique à COLUBK et qu'il semble donc invisible à l'oeil. Pour supprimer réellement certaines collisions, il faut désactiver l'objet ou supprimer ses pixels actifs, selon le type d'objet concerné.

Les registres de collision fonctionnent comme des indicateurs mémorisés. Lorsqu'une collision appropriée se produit, le bit correspondant est activé et reste disponible pour être lu ultérieurement par le processeur. Le 6507 n'est donc pas obligé de lire le registre exactement au moment où les deux objets se rencontrent. Cette propriété est essentielle puisque, pendant la zone visible, le processeur est souvent entièrement occupé à respecter les contraintes temporelles du kernel.

Une organisation courante consiste à traiter les collisions sur l'ensemble d'une frame. Au début de la période que le programme souhaite analyser, il effectue une écriture dans CXCLR (Collision Clear) afin d'effacer les indicateurs de collisions précédemment mémorisés. Le TIA commence alors la nouvelle trame avec un état de collision connu.

Le programme laisse ensuite le TIA dessiner toute la zone concernée. Pendant les lignes visibles, les objets sont générés normalement et les différents chevauchements activent progressivement les bits des registres de collision. Il n'est généralement pas nécessaire d'interrompre le kernel pour traiter chaque collision immédiatement : les indicateurs restent mémorisés.

Une fois la zone visible terminée, le programme peut profiter de l'overscan pour lire les registres de collision. Cette période est particulièrement appropriée puisque le processeur n'a plus à consacrer autant de temps à la génération graphique. Le jeu peut examiner les bits nécessaires, déterminer quels objets se sont rencontrés et appliquer la logique correspondante : perte d'une vie, destruction d'un projectile, rebond, augmentation du score, blocage d'un déplacement ou toute autre réaction prévue.

Après avoir utilisé ces informations, le programme prépare la trame suivante et effectue une nouvelle écriture dans CXCLR au moment choisi pour effacer les collisions précédentes. Une séquence typique peut donc être résumée ainsi : effacer avec CXCLR, dessiner la zone complète, lire les collisions pendant l'overscan, appliquer la logique du jeu, puis effacer les indicateurs pour la prochaine frame.

Il est important que l'effacement soit réalisé au bon moment. Si CXCLR est déclenché avant que le programme ait lu les résultats nécessaires, les informations accumulées pendant la trame sont perdues. À l'inverse, si les indicateurs ne sont pas effacés entre deux périodes d'analyse, une collision provenant d'une frame précédente peut rester présente et être interprétée à tort comme une collision nouvelle.

Le système de collision du TIA doit donc être compris séparément du simple résultat visuel. Les priorités graphiques déterminent principalement quel objet apparaît devant un autre, tandis que les registres de collision enregistrent les chevauchements entre les générateurs graphiques. Le mode de priorité de CTRLPF peut placer le playfield et la balle devant les players et missiles, et le mode score peut modifier les couleurs du playfield, sans pour autant éliminer les interactions matérielles entre les objets.

Cette distinction est particulièrement importante pour la programmation et l'émulation de l'Atari 2600 : un objet visuellement caché n'est pas nécessairement absent du point de vue du TIA. Même un objet rendu apparemment invisible en utilisant la couleur du fond peut encore produire une collision. Une gestion fiable consiste donc à utiliser CXCLR pour définir clairement le début de la période de détection, à laisser le TIA accumuler les collisions pendant le rendu, puis à lire les huit registres et traiter leurs résultats avant de les effacer pour la frame suivante.

Vertical Delay et réutilisation des objets

Le TIA (Television Interface Adaptor) de l'Atari 2600 possède un mécanisme appelé Vertical Delay, conçu pour faciliter la réutilisation des objets graphiques d'une ligne à l'autre. Cette fonction est particulièrement importante parce que le TIA ne dispose que de deux players matériels, P0 et P1, auxquels s'ajoutent les missiles et la balle. Grâce à des latches internes, certaines données graphiques peuvent être retardées d'une ligne avant d'être utilisées pour l'affichage, ce qui permet au programme de construire des kernels plus élaborés que ne le suggère le nombre limité d'objets matériels.

Les registres VDELP0 et VDELP1 contrôlent le Vertical Delay de Player 0 et Player 1. Lorsqu'il est utilisé, le motif affiché par le player ne provient plus simplement de la dernière valeur directement écrite dans GRP0 ou GRP1. Le TIA peut sélectionner une valeur conservée dans un latch retardé, ce qui revient à utiliser pour l'affichage une donnée graphique provenant de la ligne précédente. Le programme peut ainsi préparer les données d'un player pendant qu'une valeur antérieure continue à être affichée.

La balle possède un mécanisme comparable commandé par VDELBL. Dans ce cas, le retard vertical concerne son état d'activation associé à ENABL. Le TIA peut conserver l'état précédent dans un latch et l'utiliser pour l'affichage lorsque le Vertical Delay de la balle est activé. Le principe reste donc le même : séparer le moment où le processeur écrit une information de celui où cette information devient celle effectivement utilisée par le générateur graphique.

Le fonctionnement réel est cependant plus subtil qu'un simple registre contenant « la valeur de la ligne précédente ». Le TIA possède plusieurs latches internes, et certaines écritures dans les registres graphiques provoquent également des transferts croisés entre ces latches. Les écritures dans GRP0 et GRP1 ne mettent donc pas seulement à jour le player explicitement désigné : elles participent également au mécanisme qui mémorise et transfère les valeurs nécessaires au Vertical Delay.

En particulier, l'ordre dans lequel GRP0 et GRP1 sont écrits joue un rôle dans la mise à jour des valeurs internes retardées. Une écriture dans l'un de ces registres peut provoquer le transfert d'une valeur précédemment chargée dans un latch utilisé par l'autre player. Le TIA exploite cette organisation afin de rendre possible une alternance efficace des données graphiques entre deux lignes successives.

Ce comportement permet notamment de construire un kernel à deux lignes. Au lieu de devoir préparer indépendamment tous les objets sur chaque scanline, le programme peut organiser les écritures de façon à ce que certaines données soient affichées immédiatement tandis que d'autres soient conservées pour la ligne suivante. Les deux players peuvent ainsi être réutilisés et combinés avec leurs mécanismes de copies pour produire un nombre apparent d'objets supérieur aux deux sprites matériels disponibles.

Cette technique devient particulièrement puissante lorsque le Vertical Delay est combiné aux modes de copies de NUSIZ. Un seul player matériel peut déjà produire plusieurs copies horizontales ; en modifiant ses données graphiques au moment approprié et en exploitant les latches retardés, le kernel peut fournir des motifs différents à différentes apparitions. Avec les deux players, il devient ainsi possible d'afficher plusieurs objets graphiques distincts tout en conservant une structure de kernel organisée sur deux lignes.

Cette méthode est souvent associée au multiplexage des sprites. Le TIA ne gagne évidemment pas de nouveaux générateurs graphiques physiques : P0 et P1 restent les deux seuls players matériels. C'est le programme qui les réutilise à différents moments et qui exploite les latches pour donner l'impression que davantage de sprites indépendants existent à l'écran. Cette technique illustre parfaitement la philosophie de l'Atari 2600, où une grande partie des capacités graphiques avancées provient du timing logiciel plutôt que de fonctions matérielles de haut niveau.

La balle peut participer à cette organisation grâce à VDELBL et aux états enregistrés à partir de ENABL. Là encore, le moment où ENABL est écrit et celui où ses données sont transférées dans le latch approprié sont importants. Le programme doit savoir quelle valeur est actuellement directe, quelle valeur a été mémorisée et laquelle sera sélectionnée pour l'affichage lorsque le Vertical Delay est actif.

La séquence exacte des écritures dans GRP0, GRP1 et ENABL est donc critique. Deux kernels écrivant finalement les mêmes valeurs dans ces registres peuvent produire des résultats différents si l'ordre ou le moment des écritures n'est pas identique. Une écriture peut mettre à jour une valeur immédiatement accessible tout en provoquant simultanément le transfert d'une ancienne valeur vers un autre latch interne. Il faut donc raisonner en termes de séquence d'états du TIA et non simplement en termes de valeur finale des registres.

La précision temporelle reste également essentielle. Comme les écritures sont effectuées pendant que le TIA génère l'image, leur position dans la scanline détermine quelles copies ou quelles parties des objets utilisent les anciennes ou les nouvelles données. Le Vertical Delay ne supprime donc pas les contraintes de programmation cycle par cycle : il fournit plutôt des mécanismes supplémentaires que le kernel peut exploiter à des moments soigneusement déterminés.

Ces particularités ont une conséquence importante pour le développement d'un émulateur Atari 2600. Une implémentation simplifiée qui représenterait GRP0, GRP1 et ENABL comme de simples variables contenant uniquement leur dernière valeur écrite ne serait pas suffisante. Elle perdrait l'état des latches internes et les transferts provoqués par les différentes écritures.

Un tel émulateur pourrait reproduire correctement les jeux utilisant les players de manière simple, mais échouer avec les kernels qui exploitent le Vertical Delay et le multiplexage des sprites. Des objets pourraient apparaître avec une ligne de retard incorrecte, utiliser le mauvais motif, disparaître ou être remplacés par les données d'un autre sprite. Une émulation fidèle doit donc représenter séparément les valeurs courantes et retardées ainsi que les événements qui provoquent leurs transferts.

Le mécanisme de Vertical Delay illustre ainsi l'importance des détails internes du TIA. VDELP0 et VDELP1 permettent de sélectionner des motifs de players retardés d'une ligne, tandis que VDELBL fournit une fonction comparable pour l'état d'activation de la balle. Les écritures dans GRP0 et GRP1 déclenchent en outre des transferts croisés entre certains latches internes, ce qui permet notamment de construire des kernels à deux lignes capables de réutiliser efficacement les deux players.

Pour exploiter correctement cette possibilité, le programme doit respecter une séquence précise d'écritures dans GRP0, GRP1 et ENABL. Cette séquence est aussi importante que les valeurs elles-mêmes. De la même manière, un émulateur visant une compatibilité élevée doit reproduire les latches et leurs transferts plutôt que de mémoriser uniquement la dernière valeur de chaque registre. C'est cette logique interne qui permet aux programmes Atari 2600 de produire certains des sprites multiplexés les plus complexes malgré les ressources graphiques extrêmement réduites du TIA.

Audio TIA

Le TIA (Television Interface Adaptor) de l'Atari 2600 n'est pas uniquement responsable de la génération vidéo : il contient également le système audio de la console. Celui-ci repose sur deux canaux sonores indépendants, généralement désignés comme le canal 0 et le canal 1. Chaque canal possède ses propres registres de contrôle, de fréquence et de volume, ce qui permet au programme de produire deux signaux différents puis de les combiner dans la sortie sonore de la console.

Le premier paramètre de chaque canal est défini par les registres AUDC0 et AUDC1 (Audio Control). AUDC0 contrôle la nature du signal produit par le canal 0, tandis que AUDC1 joue le même rôle pour le canal 1. Ces registres ne sélectionnent pas simplement une forme d'onde parmi quelques oscillateurs classiques : ils déterminent le fonctionnement de différents compteurs, diviseurs et générateurs pseudo-aléatoires internes au TIA.

La fréquence ou, plus exactement, le facteur de division principal de chaque canal est contrôlé par AUDF0 et AUDF1 (Audio Frequency). Ces registres utilisent une valeur de 5 bits, ce qui fournit 32 valeurs possibles. La valeur sélectionnée intervient dans la division de l'horloge alimentant le mécanisme sonore choisi par AUDC. Il faut donc interpréter AUDF comme un paramètre de division plutôt que comme une fréquence exprimée directement en hertz.

Le volume est contrôlé par les registres AUDV0 et AUDV1 (Audio Volume). Chaque registre utilise une valeur de 4 bits, permettant nominalement 16 niveaux, de 0 à 15. Une valeur nulle correspond à un canal silencieux, tandis que les valeurs supérieures augmentent son niveau de sortie. Les deux canaux possèdent leur réglage indépendant, ce qui permet de faire varier séparément leur contribution au signal sonore final.

Il serait cependant incorrect de considérer l'audio du TIA comme deux simples oscillateurs à onde carrée dont le programme choisirait seulement la fréquence et le volume. Le circuit possède une architecture sonore plus particulière. Selon la valeur de AUDC, il peut produire des tons périodiques, différentes formes de bruit pseudo-aléatoire et des signaux issus de plusieurs divisions ou séquences combinées. Cette architecture est en grande partie responsable du caractère sonore très reconnaissable de l'Atari 2600.

Le registre AUDC permet de sélectionner 16 modes audio pour chaque canal. Ces modes utilisent différentes combinaisons de compteurs, de diviseurs et de registres à décalage à rétroaction linéaire, ou LFSR (Linear Feedback Shift Register). Le TIA emploie notamment des séquences basées sur des structures de 4, 5 et 9 bits. Selon le mode choisi, ces circuits peuvent servir seuls ou être combinés avec d'autres divisions afin de produire des comportements sonores différents.

Les LFSR sont particulièrement importants pour les modes de bruit. Un registre à décalage à rétroaction linéaire génère une suite déterministe de bits dont la répétition peut être suffisamment longue ou irrégulière pour donner l'impression d'un signal bruité. En fonction de la longueur du registre et de la manière dont il est combiné avec les autres compteurs du TIA, le résultat peut ressembler à un souffle, un grondement, un crépitement ou un son périodique plus complexe.

Les modes utilisant des séquences de 4 ou 5 bits produisent des motifs relativement courts qui se répètent rapidement. Ils peuvent donc donner des sons possédant une structure périodique nettement perceptible. Le mécanisme de 9 bits permet une séquence beaucoup plus longue et convient davantage à la production de bruits ayant une apparence moins régulière. Le choix de AUDC détermine la manière dont ces différents mécanismes participent à la génération du signal.

Les diviseurs peuvent également être combinés. Certains modes ne se limitent donc pas à envoyer directement la sortie d'un LFSR vers le canal audio : un compteur ou une séquence peut commander ou diviser un autre mécanisme. La relation entre AUDC et AUDF n'est par conséquent pas toujours celle d'un oscillateur traditionnel où doubler une valeur produirait simplement une fréquence deux fois plus élevée. Chaque mode doit être compris en fonction du chemin logique qu'il sélectionne dans le TIA.

La fréquence réelle obtenue dépend également de l'horloge de base de la console. Or cette horloge est liée au standard vidéo couleur. Les versions NTSC, PAL et SECAM n'utilisent pas exactement les mêmes fréquences de référence. Une combinaison identique de valeurs AUDC et AUDF peut donc produire une hauteur sonore différente selon la version régionale de l'Atari 2600.

Cette dépendance est importante lors de la conversion d'un jeu. Adapter une version NTSC au PAL ne consiste pas uniquement à modifier les couleurs et le nombre de lignes vidéo. Si les mêmes paramètres audio sont conservés sans correction, certaines notes ou certains effets sonores peuvent changer de hauteur. Une adaptation précise peut donc nécessiter une conversion des valeurs de fréquence audio en fonction du standard utilisé.

Les deux canaux sont finalement mélangés afin de produire la sortie sonore de la console. Là encore, le comportement du matériel original est plus complexe qu'une simple addition mathématique de deux valeurs numériques. Les caractéristiques électriques du TIA, des composants qui l'entourent et du modulateur influencent la manière dont les niveaux des deux canaux se combinent.

Le mixage est donc non linéaire sur le matériel réel. Additionner simplement AUDV0 et AUDV1 avec une relation parfaitement linéaire peut fournir une approximation acceptable pour une émulation simple, mais ne reproduit pas nécessairement exactement l'amplitude et le caractère du signal obtenu sur une console originale. Certaines combinaisons de volumes peuvent interagir d'une manière qui diffère d'une addition idéale.

Pour un émulateur Atari 2600 visant une bonne fidélité sonore, une approche plus précise consiste à utiliser une table de mixage obtenue à partir de mesures ou d'un modèle du comportement réel. Chaque combinaison de niveaux des deux canaux peut alors être associée à une amplitude de sortie appropriée. Une autre possibilité consiste à employer un modèle analogique représentant les caractéristiques électriques du TIA et du circuit de sortie.

Une émulation fidèle doit également reproduire les compteurs et LFSR plutôt que de remplacer les modes du TIA par des formes d'onde génériques approximatives. Un simple générateur d'onde carrée ou de bruit blanc ne reproduirait pas correctement les périodicités, les séquences et les divisions caractéristiques de AUDC. Les états internes des générateurs font partie du comportement sonore observable de la console.

Le système audio de l'Atari 2600 repose donc sur une architecture relativement petite mais inhabituelle. Les deux canaux utilisent AUDC0/AUDC1 pour choisir parmi 16 modes, AUDF0/AUDF1 pour fournir une division sur 5 bits et AUDV0/AUDV1 pour définir un volume sur 4 bits. Les modes combinent des compteurs, des divisions et des LFSR de 4, 5 et 9 bits afin de produire des tons et différents types de bruits plutôt que de simples ondes carrées. La fréquence obtenue dépend du standard couleur, tandis que le mélange final des deux canaux présente un comportement non linéaire. Pour une reproduction particulièrement fidèle, une table de mixage mesurée ou un modèle analogique constitue donc une meilleure solution qu'une simple addition numérique des deux canaux.

RIOT 6532

Le MOS Technology 6532, généralement appelé RIOT, constitue avec le MOS 6507 et le TIA l'un des trois circuits fondamentaux de l'Atari 2600. L'acronyme RIOT signifie RAM-I/O-Timer et résume les trois fonctions principales réunies dans cette puce : une petite quantité de mémoire vive, des ports d'entrée/sortie parallèles et un timer programmable. Cette intégration permet à Atari de fournir plusieurs fonctions essentielles avec un seul circuit et contribue ainsi à la simplicité matérielle de la console.

Le RIOT contient 128 octets de RAM. Cette mémoire représente pratiquement toute la mémoire vive de travail directement disponible dans une Atari 2600 standard. Le programme l'utilise pour conserver ses variables, compteurs, coordonnées logiques, états du jeu et données temporaires. La pile du 6507 partage également cette RAM physique par l'intermédiaire des miroirs produits par le décodage mémoire de la console. Avec seulement 128 octets disponibles, la gestion de la mémoire constitue donc une contrainte majeure pour les programmeurs.

En plus de cette RAM, le 6532 fournit deux ports parallèles de 8 bits, traditionnellement appelés port A et port B. Chaque port est associé à un DDR (Data Direction Register), c'est-à-dire un registre permettant de déterminer individuellement si chaque ligne doit fonctionner comme une entrée ou comme une sortie. Cette architecture est similaire à celle de nombreux circuits d'interface de l'époque : le registre de données fournit ou lit l'état des lignes, tandis que le DDR définit leur direction.

Sur l'Atari VCS/2600, le port A est principalement associé aux directions des deux manettes de jeux. Les quatre directions de chaque contrôleur - haut, bas, gauche et droite - utilisent ensemble les huit lignes disponibles. Le programme peut donc lire l'état directionnel des deux manettes de jeux par l'intermédiaire de ce port. Les boutons fire, en revanche, sont traités par des entrées du TIA et ne font pas partie de ces huit lignes directionnelles du RIOT.

Le port B est principalement utilisé pour les commutateurs de la console. Il permet notamment au programme de connaître l'état des commandes situées sur le boîtier, comme les fonctions Reset, Select et les commutateurs de difficulté associés aux joueurs. Selon la révision de la console, l'apparence physique de certains de ces commutateurs peut varier, mais leur état reste accessible au logiciel par l'intermédiaire du port B du RIOT.

Les entrées correspondantes fonctionnent généralement en logique active à l'état bas (active low). Les lignes disposent de résistances de rappel vers le niveau haut (pull-ups), de sorte qu'une entrée inactive est normalement lue comme un 1 logique. Lorsqu'une manette de jeux ferme le contact correspondant à une direction ou qu'un commutateur approprié est activé, la ligne est ramenée vers le niveau bas et le programme lit alors un 0. Cette convention doit être prise en compte lors des tests binaires effectués par le logiciel.

Le registre SWCHA, accessible couramment à l'adresse $0280, correspond au registre de données du port A. Une lecture permet notamment d'obtenir l'état des directions des manettes de jeux. Comme les entrées sont actives à l'état bas, un bit à zéro indique normalement que la direction correspondante est actionnée, tandis qu'un bit à un représente son état relâché.

Le registre SWACNT, à l'adresse $0281, constitue le Data Direction Register du port A. Chacun de ses bits détermine la direction de la ligne correspondante de SWCHA. Selon la convention du 6532, le programme peut ainsi sélectionner les lignes utilisées comme entrées et celles utilisées comme sorties. Dans l'utilisation conventionnelle des joysticks, les lignes concernées sont principalement configurées pour être lues comme des entrées.

Le registre SWCHB, situé à $0282, correspond au registre de données du port B. Il permet notamment de lire les commutateurs de la console. Comme pour le port A, plusieurs de ces signaux sont actifs à l'état bas. Le programme peut donc employer des masques binaires pour déterminer quels boutons ou commutateurs sont actuellement activés.

Le registre SWBCNT, à l'adresse $0283, constitue le Data Direction Register du port B. Il joue pour SWCHB le même rôle que SWACNT pour SWCHA, en définissant la direction des différentes lignes du port. Ces deux DDR font partie intégrante du fonctionnement général du 6532, même si l'architecture fixe de la console conduit les jeux à utiliser la plupart des lignes selon des fonctions bien déterminées.

Le RIOT contient également une minuterie décrémentant. Le programme peut charger une valeur initiale dans le mécanisme de temporisation, puis le circuit la diminue automatiquement selon la division d'horloge sélectionnée. Cette fonction permet au 6507 de mesurer un intervalle sans avoir à consacrer en permanence des instructions à un compteur logiciel. Elle est particulièrement utile pendant les périodes où le processeur doit effectuer d'autres calculs.

Le registre INTIM, accessible couramment à l'adresse $0284, permet de lire la valeur courante de la minuterie. Le programme peut ainsi déterminer combien de temps reste avant son expiration ou détecter que le compteur a atteint sa limite. Cette technique est fréquemment utilisée pour gérer le temps disponible pendant certaines portions de la trame, notamment lorsque le programme veut effectuer autant de calculs que possible pendant le vertical blank ou l'overscan.

Le registre TIMINT, situé à $0285, permet d'observer l'état associé à l'expiration de la minuterie. Même si l'Atari 2600 n'utilise normalement pas une ligne IRQ du RIOT pour interrompre directement le 6507, l'indicateur de la minuterie demeure accessible par logiciel. Le programme peut donc effectuer un polling de l'état du RIOT et réagir lorsqu'un intervalle programmé est terminé.

Les adresses $0280 à $0285 sont les adresses couramment utilisées dans les programmes Atari 2600, mais elles doivent être comprises dans le contexte du décodage partiel de la console. Le RIOT ne décode pas nécessairement toutes les lignes d'adresse de manière à rendre chaque adresse de l'espace du 6507 unique. Ses registres peuvent donc apparaître à plusieurs emplacements sous forme de miroirs.

Ces miroirs expliquent pourquoi différentes adresses peuvent finalement sélectionner le même registre physique du RIOT. Les adresses SWCHA = $0280, SWACNT = $0281, SWCHB = $0282, SWBCNT = $0283, INTIM = $0284 et TIMINT = $0285 représentent les emplacements conventionnels utilisés par les logiciels et les documentations, mais elles s'inscrivent dans une carte mémoire comportant plusieurs répétitions dues au décodage incomplet des lignes d'adresse.

Le RIOT 6532 joue donc un rôle essentiel malgré sa simplicité. Il fournit les 128 octets de RAM, les deux ports parallèles de 8 bits avec leurs DDR et le timer décrémentant nécessaires au fonctionnement de la console. Sur l'Atari 2600, le port A sert principalement aux directions des manettes de jeux, le port B aux commutateurs de la console, et les entrées utilisent généralement une logique active à l'état bas avec résistances de pull-up. Les registres SWCHA, SWACNT, SWCHB, SWBCNT, INTIM et TIMINT, accessibles conventionnellement de $0280 à $0285, constituent ainsi l'interface principale entre le 6507 et les fonctions d'entrée/sortie et de temporisation du RIOT.

RAM, pile et variables

L'Atari 2600 ne dispose que de 128 octets de mémoire vive, fournis par le RIOT 6532. Dans la carte mémoire couramment utilisée par les programmes, cette RAM est accessible aux adresses $0080 à $00FF. Cette quantité extrêmement réduite doit contenir pratiquement toutes les données modifiables nécessaires au fonctionnement du jeu. Contrairement aux machines disposant de plusieurs kilo-octets de RAM, un programme Atari 2600 doit donc organiser chaque octet avec beaucoup de soin.

Ces 128 octets servent notamment à conserver l'état du jeu : coordonnées logiques des personnages et objets, vitesses, compteurs, états des ennemis, progression d'une partie et différentes informations utilisées par le kernel. Les positions horizontales et verticales calculées par logiciel doivent généralement être mémorisées quelque part avant d'être transformées en valeurs utilisables par le TIA.

Le score et les autres compteurs du jeu consomment également une partie de cette mémoire. Selon les besoins, le programme peut les conserver sous forme binaire, BCD ou dans une représentation adaptée à leur affichage. Des variables supplémentaires peuvent être nécessaires pour convertir ces valeurs en motifs graphiques pendant la génération de la zone de score.

Les générateurs de nombres pseudo-aléatoires ont eux aussi besoin d'un état conservé en RAM. Même un générateur très compact doit mémoriser une graine ou l'état courant d'un LFSR ou d'un autre algorithme. À cela s'ajoutent les pointeurs, compteurs de boucles, indices, temporaires et variables intermédiaires nécessaires aux différentes routines. Tous ces éléments doivent partager les mêmes 128 octets.

Une technique importante consiste donc à réutiliser la mémoire lorsque les durées de vie des variables ne se chevauchent pas. Une variable temporaire utilisée uniquement pendant le vertical blank peut, par exemple, partager le même emplacement qu'une donnée temporaire utilisée uniquement pendant l'overscan, à condition qu'aucune des deux valeurs n'ait besoin d'être conservée lorsque l'autre est active. Cette forme d'allocation manuelle permet de réduire considérablement la quantité de RAM nécessaire.

Le programmeur peut ainsi considérer certains emplacements comme des zones de travail dont la signification change selon la phase d'exécution. Une même adresse peut servir à un calcul de positionnement, puis être réutilisée plus tard pour une opération audio ou une conversion de score. Cette pratique exige cependant une bonne connaissance de la durée de vie des données, car une réutilisation trop précoce détruirait une valeur encore nécessaire.

La position de la RAM dans $80-$FF présente aussi un avantage important : elle se trouve dans la page zéro du 6502/6507. Le processeur dispose de modes d'adressage particulièrement compacts et rapides pour cette page. Une variable peut donc souvent être manipulée avec une instruction utilisant une adresse sur un seul octet, ce qui économise à la fois de la ROM et des cycles processeur - deux ressources particulièrement précieuses sur l'Atari 2600.

Cette localisation est également essentielle pour les modes d'adressage indirects du 6507. Les pointeurs utilisés par les modes tels que (zp,X) et (zp),Y doivent résider en page zéro. Un pointeur 16 bits nécessite normalement deux octets consécutifs contenant l'octet faible puis l'octet fort de l'adresse. Le programme doit donc réserver correctement ces paires d'octets dans sa RAM.

Une attention particulière est nécessaire lorsque ces pointeurs se trouvent près de la fin de la page zéro. Les mécanismes d'adressage du 6502 possèdent des comportements de rebouclage propres aux adresses sur 8 bits. Un pointeur placé maladroitement à la frontière de la page peut donc aller chercher son second octet à un emplacement inattendu. Il est préférable d'organiser les pointeurs de manière à éviter ce type de débordement, sauf lorsqu'un comportement particulier est volontairement exploité.

La pile du 6507 constitue une contrainte supplémentaire. Logiquement, comme sur le 6502, elle appartient à la plage $0100-$01FF. Cependant, à cause du décodage partiel de l'Atari 2600, cette zone est reflétée sur les mêmes 128 octets physiques du RIOT que ceux utilisés pour les variables dans $0080-$00FF. La pile et les données du jeu doivent donc en pratique se partager la même mémoire physique.

Les appels de sous-programmes avec JSR consomment automatiquement de la place sur cette pile, puisque l'adresse de retour doit y être enregistrée. Les instructions telles que PHA et PHP ajoutent elles aussi des données sur la pile. Une succession trop importante d'appels imbriqués ou d'empilements manuels peut donc faire descendre le pointeur de pile jusque dans la zone utilisée par les variables et provoquer leur écrasement.

Il est par conséquent recommandé de limiter la profondeur des JSR imbriqués. Les routines profondément récursives ou les architectures utilisant de nombreuses couches de sous-programmes sont particulièrement mal adaptées aux contraintes de l'Atari 2600. Les programmes privilégient souvent des structures relativement plates et utilisent avec parcimonie les opérations PHA, PHP, PLA et PLP.

Une convention pratique consiste à placer les variables en partant du bas de la RAM, à partir de $80, tandis que la pile utilise l'extrémité supérieure de cette mémoire. Les variables progressent alors vers les adresses hautes et la pile vers les adresses basses. Le programmeur doit conserver une marge suffisante pour garantir que ces deux zones ne se rencontrent jamais pendant les situations où la pile atteint sa profondeur maximale.

La RAM doit également être explicitement initialisée au démarrage. Son contenu immédiatement après la mise sous tension n'est pas défini par l'architecture et ne doit pas être considéré comme étant automatiquement rempli de zéros. Les cellules peuvent contenir des valeurs indéterminées dépendant du composant, de son état électrique et des conditions de mise sous tension.

Un jeu ne doit donc jamais supposer qu'une variable vaut zéro simplement parce qu'aucune valeur n'y a encore été écrite. Les compteurs, positions, états, pointeurs, générateurs pseudo-aléatoires et autres données dont le programme attend une valeur initiale précise doivent être initialisés explicitement par la routine de démarrage. Cette précaution évite des comportements aléatoires ou différents d'une console à l'autre.

Le registre SP (Stack Pointer) mérite la même attention. Le programme ne doit pas supposer que SP vaut automatiquement $FF après un RESET. Si l'organisation mémoire choisie repose sur une pile commençant à l'extrémité supérieure de la RAM, le logiciel doit initialiser explicitement le pointeur de pile.

Une séquence classique consiste à charger dans le registre X la valeur choisie pour le sommet de pile, puis à utiliser l'instruction TXS (Transfer X to Stack Pointer). Par exemple, charger $FF dans X puis exécuter TXS établit explicitement le pointeur de pile à la position souhaitée. Le programme commence ainsi son exécution avec un état connu au lieu de dépendre d'une valeur laissée par le RESET ou par les conditions précédant celui-ci.

La gestion des 128 octets de $80 à $FF constitue donc une partie fondamentale de la programmation Atari 2600. Cette petite mémoire doit accueillir l'état du jeu, les positions, le score, les états pseudo-aléatoires, les pointeurs, les variables temporaires et la pile. Une utilisation efficace repose sur la réutilisation des variables dont les durées de vie ne se chevauchent pas, une organisation prudente des pointeurs de page zéro et une limitation des appels JSR et des empilements.

Enfin, un programme robuste doit partir du principe que son état initial est indéterminé. La RAM doit être initialisée explicitement et le programme ne doit pas supposer que SP = $FF après le reset. En initialisant volontairement le pointeur de pile avec TXS et toutes les variables nécessaires, le logiciel obtient un environnement de départ reproductible sur le matériel réel comme dans les émulateurs.

Minuterie RIOT

Le RIOT 6532 de l'Atari 2600 contient une minuterie programmable décrémentante qui permet au MOS 6507 de mesurer des intervalles sans devoir consacrer continuellement des instructions à un compteur logiciel. Cette fonction est particulièrement utile sur la console, car certaines parties d'une trame, notamment le VBLANK et l'overscan, possèdent une durée limitée pendant laquelle le processeur doit exécuter la logique du jeu avant de revenir à la génération vidéo précisément synchronisée.

La minuterie fonctionne à partir d'une valeur initiale écrite par le programme. Cette valeur est ensuite décrémentée automatiquement par le RIOT. Le programme peut choisir plusieurs cadences de décrémentation en écrivant la valeur de départ dans l'un de quatre registres. Le registre utilisé détermine combien de cycles processeur doivent s'écouler avant chaque décrémentation du compteur.

Une écriture dans TIM1T, à l'adresse $0294, sélectionne l'intervalle le plus court : le compteur est décrémenté à une cadence correspondant à un cycle processeur par unité. Ce mode fournit donc une temporisation très fine, mais une valeur de compteur donnée couvre une période relativement courte.

Le registre TIM8T, situé à $0295, sélectionne un intervalle de 8 cycles processeur entre les décrémentations. Chaque unité chargée dans le compteur représente ainsi huit cycles avant le décrément suivant. Cette cadence permet de mesurer des périodes plus longues tout en conservant une résolution relativement fine.

Une écriture dans TIM64T, à l'adresse $0296, configure la minuterie avec un intervalle de 64 cycles. Ce mode est particulièrement pratique pour mesurer des portions importantes d'une trame, car une valeur sur 8 bits peut alors représenter un nombre substantiel de cycles sans nécessiter de compteur logiciel supplémentaire.

Enfin, T1024T, situé à $0297, sélectionne un intervalle de 1024 cycles processeur par décrémentation. Il s'agit du mode offrant la plus longue portée temporelle parmi les quatre préscalers disponibles, au prix d'une résolution beaucoup plus faible.

Le programme peut lire l'état courant de la minuterie par l'intermédiaire du registre INTIM, accessible conventionnellement à l'adresse $0284. La valeur retournée représente l'état actuel du compteur. Le 6507 peut donc continuer à effectuer d'autres traitements puis consulter périodiquement INTIM pour déterminer combien de temps reste avant l'expiration de la période programmée.

Le choix du registre de démarrage revient ainsi à sélectionner un préscaler pour le compteur. TIM1T, TIM8T, TIM64T et T1024T utilisent respectivement des intervalles de 1, 8, 64 et 1024 cycles. Cette organisation permet au programmeur de choisir un compromis entre la précision de la temporisation et la durée maximale qu'il souhaite mesurer.

Le comportement du RIOT devient particulièrement important lorsque le compteur arrive à son terme. Après avoir atteint zéro et passé par zéro, la minuterie ne continue pas simplement à fonctionner avec le préscaler qui avait été sélectionné. Elle passe alors à une cadence d'un cycle processeur par décrémentation. Le compteur continue donc d'évoluer rapidement après l'expiration de la période initiale.

Au même moment, le RIOT positionne l'indicateur associé à la minuterie, accessible par TIMINT. Ce drapeau permet au programme de déterminer que la période programmée a expiré. Sur l'Atari 2600, où les interruptions matérielles du 6507 ne sont normalement pas utilisées pour ce mécanisme, le logiciel peut examiner cet état par polling plutôt que d'attendre une véritable IRQ.

Cette phase postérieure à l'expiration est importante pour l'émulation comme pour la programmation. Une lecture de INTIM effectuée après le dépassement de zéro peut retourner une valeur résultant de cette nouvelle cadence rapide. Il ne faut donc pas modéliser la minuterie comme un simple compteur qui atteint zéro puis y reste indéfiniment.

Les lectures et les écritures des registres de minuterie possèdent en outre des effets définis par le fonctionnement du 6532. Certaines opérations peuvent notamment influencer l'état de la minuterie ou de ses indicateurs. Une implémentation fidèle doit donc reproduire le comportement réel du RIOT plutôt que de considérer INTIM, TIMINT et les registres de démarrage comme de simples variables indépendantes.

Une utilisation typique de la minuterie consiste à l'exploiter pendant le VBLANK. Après avoir terminé la synchronisation verticale et déterminé le temps disponible avant le début du kernel, le programme charge une valeur appropriée dans l'un des registres TIM1T, TIM8T, TIM64T ou T1024T. Le RIOT commence alors à décompter pendant que le 6507 poursuit l'exécution de la logique du jeu.

Le processeur peut profiter de cette période pour mettre à jour les positions des objets, traiter les entrées des contrôleurs, calculer les mouvements, mettre à jour les scores, préparer des pointeurs graphiques ou effectuer d'autres travaux. Pendant ce temps, la minuterie continue de fonctionner indépendamment, ce qui évite au programme de devoir compter manuellement chaque cycle consommé par ces routines.

Lorsque les calculs sont terminés, le programme peut lire INTIM et attendre que sa valeur soit proche de zéro, ou utiliser l'état approprié pour déterminer que la période prévue touche à sa fin. Cette méthode permet de conserver une durée globale relativement constante même lorsque les traitements effectués pendant le VBLANK n'utilisent pas exactement le même nombre de cycles à chaque frame.

Une fois le moment approprié atteint, le programme utilise généralement WSYNC pour attendre le début exact d'une nouvelle ligne. Le passage de la minuterie RIOT à WSYNC combine ainsi deux niveaux de synchronisation : le RIOT fournit une temporisation relativement longue pour gérer la durée générale du VBLANK ou de l'overscan, tandis que WSYNC fournit l'alignement précis sur une frontière de scanline nécessaire au commencement du kernel.

La même technique peut être employée pendant l'overscan. Le programme charge la minuterie au début de cette période, exécute la logique qu'il souhaite effectuer après la zone visible, puis attend la fin de l'intervalle. Cela permet de maintenir un nombre de lignes stable d'une frame à l'autre même lorsque la quantité de calcul varie légèrement.

La minuterie du RIOT constitue donc un complément très important à la synchronisation vidéo de l'Atari 2600. Une écriture dans TIM1T $0294, TIM8T $0295, TIM64T $0296 ou T1024T $0297 démarre le compteur avec un intervalle respectif de 1, 8, 64 ou 1024 cycles, tandis que INTIM $0284 permet d'en lire la valeur courante. Après le passage par zéro, le compteur adopte une cadence de un cycle et positionne le drapeau associé dans TIMINT.

Une séquence courante consiste ainsi à charger la minuterie au début du VBLANK ou de l'overscan, exécuter la logique du jeu, surveiller INTIM jusqu'à l'approche de zéro, puis utiliser WSYNC pour aligner précisément le début du kernel. Ce mécanisme permet d'utiliser efficacement le temps processeur disponible tout en conservant la régularité temporelle indispensable à la génération d'un signal vidéo stable.

Manettes de jeux et boutons

L'Atari 2600 possède deux ports destinés aux contrôleurs, généralement désignés comme le port gauche et le port droit. Avec les manettes de jeux numériques standards, chaque contrôleur fournit quatre directions - haut, bas, gauche et droite - ainsi qu'un bouton d'action. Une particularité importante de l'architecture est que les directions et les boutons ne sont pas lus par le même circuit : les directions passent principalement par le RIOT 6532, tandis que les boutons d'action sont lus par des entrées du TIA.

Les huit directions des deux manettes de jeux sont regroupées dans le registre SWCHA du RIOT, accessible conventionnellement à l'adresse $0280. Il s'agit du registre de données du port A du 6532. Comme ce port possède huit lignes, il peut représenter directement les quatre directions de chacun des deux manettes de jeux.

Les bits 7 à 4 de SWCHA correspondent au manettes de jeux branché sur le port gauche, généralement associé au joueur 0. Les quatre bits sont affectés, dans l'ordre, aux directions droite, gauche, bas et haut lorsqu'on les considère de D7 vers D4. Autrement dit, le bit 7 correspond à la droite, le bit 6 à la gauche, le bit 5 au bas et le bit 4 au haut.

Les bits 3 à 0 correspondent de la même manière au manettes de jeux du port droit, généralement associé au joueur 1. Le bit 3 représente la droite, le bit 2 la gauche, le bit 1 le bas et le bit 0 le haut. Une seule lecture de SWCHA permet donc au programme de connaître simultanément l'état des huit lignes directionnelles des deux contrôleurs.

Ces entrées fonctionnent en logique active à l'état bas. Lorsqu'une direction n'est pas actionnée, la ligne correspondante est normalement maintenue au niveau logique 1 par les résistances de rappel (pull-ups). Lorsqu'une direction est pressée, le contact du manettes de jeux relie la ligne de manière à produire un niveau logique 0. Le programme doit donc rechercher des bits à zéro pour identifier les directions actuellement actionnées.

Par exemple, pour la manette de jeux gauche, un bit 4 à zéro signifie que la direction haut est actionnée, tandis qu'un bit 7 à zéro indique la direction droite. Si aucune direction n'est pressée sur les deux manettes de jeux, les huit entrées correspondantes de SWCHA sont normalement au niveau haut et la lecture donne donc $FF, sous réserve de la configuration correcte du port.

Le sens d'utilisation des lignes du port A est déterminé par SWACNT, le Data Direction Register associé à SWCHA. Pour utiliser les lignes comme entrées destinées aux manettes de jeux standards, le programme configure normalement SWACNT à $00. Les huit lignes du port A sont alors placées en mode entrée, ce qui permet de lire correctement les contacts des deux manettes de jeux.

Les boutons d'action ne sont pas inclus dans SWCHA. Ils sont raccordés à des entrées du TIA. Le bouton de manettes de jeux gauche est lu par le registre INPT4, tandis que celui de la manette de jeux droit est lu par INPT5. Dans les deux cas, c'est principalement le bit 7 du registre qui représente l'état logique de l'entrée.

Ces boutons utilisent eux aussi une logique active à l'état bas. Pour le contrôleur gauche, le bit 7 de INPT4 vaut normalement 1 lorsque le bouton n'est pas pressé et passe à 0 lorsqu'il est actionné. Le bit 7 de INPT5 fournit le même comportement pour le bouton du contrôleur droit. Les autres bits lus dans ces registres ne doivent pas être interprétés comme des bits supplémentaires du bouton.

Cette séparation entre le RIOT et le TIA signifie qu'une routine complète de lecture d'une manette de jeux doit généralement consulter deux sources matérielles. SWCHA fournit les quatre directions de chaque contrôleur, tandis que INPT4 et INPT5 fournissent leurs boutons d'action respectifs. Le programme peut ensuite combiner ces informations pour construire l'état logique complet de chaque contrôleur.

Une autre caractéristique importante des manettes de jeux Atari standards est que leurs directions correspondent à des contacts électriques indépendants. Le matériel de la console ne transforme pas automatiquement ces contacts en une direction unique. Il est donc parfaitement possible, du point de vue électrique, que plusieurs bits directionnels soient actifs simultanément.

Une combinaison diagonale constitue l'exemple le plus courant : haut et droite peuvent être actifs en même temps, ce qui produit deux bits à zéro dans SWCHA. Le logiciel peut alors interpréter cette combinaison comme un déplacement diagonal lorsque le jeu le permet. De même, d'autres combinaisons de contacts peuvent théoriquement être présentées au port.

Même des combinaisons qui seraient difficiles ou impossibles à obtenir avec la mécanique d'un modèle particulier de la manette de jeux ne doivent pas nécessairement être interdites par le matériel émulé. Un autre contrôleur, un adaptateur, un périphérique personnalisé ou une connexion électrique différente peut présenter plusieurs lignes actives simultanément. Le RIOT ne possède pas de logique destinée à imposer une règle telle que « une seule direction horizontale et une seule direction verticale peuvent être actives ».

Cette distinction est particulièrement importante pour un émulateur Atari 2600. L'émulateur doit reproduire l'état des lignes électriques vues par la console, plutôt que d'introduire artificiellement des limitations correspondant à la mécanique supposée de la manette de jeux. Si l'interface d'entrée indique simultanément plusieurs directions, l'émulation du RIOT doit pouvoir présenter les bits correspondants simultanément à zéro dans SWCHA.

Le système d'entrée des manettes de jeux standards peut donc être résumé simplement : SWCHA bits 7 à 4 contient les directions de la manettes de jeux gauche et bits 3 à 0 celles du joystick droit ; pour chaque contrôleur, les quatre directions sont actives à l'état bas. INPT4 bit 7 représente le bouton gauche et INPT5 bit 7 le bouton droit, eux aussi actifs à l'état bas. SWACNT est normalement réglé à $00 afin de configurer les huit lignes directionnelles comme entrées.

Pour une émulation fidèle, il faut enfin conserver le caractère indépendant de ces lignes. Plusieurs directions peuvent être électriquement actives simultanément, et aucune restriction artificielle ne devrait être ajoutée au niveau du RIOT ou du TIA. L'émulateur doit présenter au logiciel Atari 2600 les mêmes combinaisons de signaux que celles que le matériel réel serait capable de recevoir.

Paddles, clavier et autres contrôleurs

L'Atari 2600 ne se limite pas aux manettes de jeux numériques standards. Ses deux ports de contrôleurs ont été utilisés avec plusieurs types de périphériques exploitant différemment les lignes d'entrée du TIA et du RIOT 6532. Parmi les accessoires les plus importants figurent les paddles, le Keyboard Controller, le Driving Controller, ainsi que plusieurs contrôleurs spécialisés. Pour une émulation complète, il ne suffit donc pas de représenter chaque port comme un simple manette de jeux à quatre directions et un bouton.

Les paddles sont des contrôleurs analogiques utilisant un potentiomètre rotatif. Contrairement au manettes de jeux, qui fournit directement des états logiques correspondant aux directions, la position d'un paddle est déterminée par la mesure d'une constante de temps résistive-capacitive. Le TIA fournit pour cela quatre entrées analogiques, INPT0 à INPT3, permettant de gérer jusqu'à quatre paddles, soit généralement deux paddles par port de contrôleur.

Le principe repose sur un condensateur associé à chaque entrée et sur la résistance variable du potentiomètre du paddle. Pour commencer une mesure, le programme utilise le bit 7 du registre VBLANK afin de provoquer la décharge des condensateurs. Cette opération place les circuits de mesure dans un état initial connu avant de commencer la temporisation.

Le programme libère ensuite la décharge en modifiant le bit 7 de VBLANK. Les condensateurs commencent alors à se charger à travers les résistances associées aux paddles. La vitesse de cette charge dépend de la position du potentiomètre : une résistance différente entraîne une durée différente avant que la tension n'atteigne le seuil logique reconnu par l'entrée du TIA.

Le logiciel surveille alors le bit 7 du registre INPT0, INPT1, INPT2 ou INPT3 correspondant au paddle mesuré. Il compte le temps écoulé entre la libération de la décharge et le changement d'état de cette entrée. Ce temps constitue la mesure de la position du potentiomètre. Le TIA ne fournit donc pas directement une valeur numérique représentant l'angle du paddle : c'est le programme qui transforme une durée de charge analogique en position utilisable par le jeu.

Cette méthode explique pourquoi la valeur obtenue peut dépendre de plusieurs facteurs. La résistance du potentiomètre, les caractéristiques des condensateurs et les tolérances des composants influencent naturellement la durée mesurée. Deux contrôleurs physiques peuvent donc présenter de légères différences même lorsqu'ils semblent être placés à une position comparable.

La cadence du programme intervient également, puisque le 6507 mesure cette durée en fonction de son propre déroulement temporel. Enfin, la région vidéo de la console peut avoir une influence sur la conversion entre le temps physique et les cycles disponibles, les systèmes NTSC, PAL et SECAM n'utilisant pas exactement les mêmes fréquences de référence. Une émulation correcte des paddles doit donc reproduire leur comportement temporel plutôt que les considérer simplement comme des axes numériques sans relation avec le temps.

Un autre périphérique important est l'Atari Keyboard Controller. Malgré son nom, il ne s'agit pas d'un clavier alphanumérique complet, mais d'un contrôleur comportant 12 touches, généralement organisées comme un pavé de trois colonnes sur quatre rangées. Il a notamment été utilisé par des logiciels nécessitant davantage d'entrées distinctes qu'une manette de jeux conventionnel.

Ces douze touches sont organisées sous forme de matrice. Le programme sélectionne certaines lignes de cette matrice à l'aide des signaux disponibles sur le port de contrôleur, puis lit les autres lignes afin de déterminer quelle touche établit une connexion. Le fonctionnement combine donc des lignes reliées au RIOT avec des entrées accessibles par le TIA. Le logiciel doit effectuer un balayage de la matrice plutôt que simplement lire douze bits indépendants.

Le Driving Controller utilise encore une autre méthode. Extérieurement, il peut rappeler un paddle puisqu'il possède une commande rotative, mais son fonctionnement électrique et son protocole sont différents. Il fournit des signaux en quadrature, c'est-à-dire deux signaux numériques dont la relation de phase permet de déterminer à la fois le mouvement et son sens.

Lorsque l'utilisateur tourne le Driving Controller, les deux lignes changent d'état selon une séquence déterminée. En observant l'ordre de ces transitions, le programme peut savoir si la commande tourne dans un sens ou dans l'autre. Cette technique diffère fondamentalement du paddle : le paddle fournit une position absolue par mesure RC, tandis que le Driving Controller fournit principalement une information de déplacement relatif et de direction grâce à la quadrature.

D'autres périphériques ont encore exploité les possibilités électriques des ports. Le Booster Grip, par exemple, ajoute des commandes supplémentaires par rapport au manettes de jeux Atari standard. Il permet de disposer de boutons additionnels en utilisant des lignes du connecteur normalement exploitées autrement par certains types de contrôleurs.

Il existe également des manettes de jeux à deux boutons et différents accessoires qui réutilisent les lignes disponibles selon leurs propres conventions. Le logiciel compatible doit alors savoir que le contrôleur connecté n'est pas un manettes de jeux standard, faute de quoi certaines entrées pourraient être interprétées incorrectement ou simplement ignorées.

Le Kid's Controller constitue un autre exemple de périphérique spécialisé. Comme le Keyboard Controller et d'autres accessoires, il fournit davantage de commandes que la manette de jeux conventionnel et utilise une organisation particulière des lignes du port. Le programme doit appliquer le protocole correspondant au contrôleur pour interpréter correctement les entrées.

Il n'existe donc pas un protocole universel permettant à l'Atari 2600 de demander au périphérique connecté : « quel type de contrôleur es-tu ? ». Le matériel a été conçu à une époque où les jeux savaient généralement à l'avance quel périphérique ils devaient utiliser. La configuration du contrôleur fait donc largement partie des caractéristiques du logiciel.

Cette particularité est importante pour un émulateur Atari 2600. Essayer de détecter automatiquement le périphérique uniquement à partir des lectures effectuées par le programme peut être ambigu ou insuffisant. Une solution fiable consiste à déterminer ou configurer le type de contrôleur en fonction du jeu, soit à partir d'une base de données connue, soit grâce à une option choisie par l'utilisateur.

Une émulation précise doit ensuite reproduire le protocole électrique approprié. Les paddles nécessitent une simulation de leur temporisation RC sur INPT0-INPT3, le Keyboard Controller exige le balayage de sa matrice de 12 touches, et le Driving Controller doit produire les séquences numériques de quadrature appropriées. Le Booster Grip, les manettes de jeux à boutons supplémentaires, le Kid's Controller et les autres accessoires doivent de même présenter aux lignes RIOT/TIA les états attendus par le logiciel original.

Les ports de contrôleurs de l'Atari 2600 doivent ainsi être considérés comme des interfaces électriques polyvalentes plutôt que comme de simples prises pour manettes de jeux. Les paddles utilisent INPT0 à INPT3 et des condensateurs dont la décharge est commandée par le bit 7 de VBLANK ; après leur libération, le programme mesure le temps nécessaire au changement du bit 7 de l'entrée correspondante, valeur qui dépend notamment de la résistance, de la cadence et de la région. Le Keyboard Controller organise ses 12 touches en matrice, tandis que le Driving Controller produit des signaux en quadrature. D'autres périphériques, comme le Booster Grip, les manettes de jeux à deux boutons et le Kid's Controller, possèdent leurs propres conventions. Pour cette raison, le type de contrôleur doit généralement être détecté à partir des besoins connus du jeu ou explicitement configuré.

Commutateurs de console

En plus des contrôleurs connectés aux deux ports, l'Atari 2600 possède plusieurs commutateurs directement intégrés à la console. Ceux-ci permettent notamment de réinitialiser une partie, de sélectionner un mode de jeu, de choisir entre les modes couleur et noir et blanc, et de définir le niveau de difficulté associé à chacun des deux joueurs. Le programme peut connaître leur état par l'intermédiaire du RIOT 6532, principalement grâce au registre SWCHB.

Le registre SWCHB, accessible conventionnellement à l'adresse $0282, correspond au port B du RIOT. Plusieurs de ses bits sont reliés aux commutateurs physiques de la console. Comme pour les directions des joysticks lues par SWCHA, une grande partie de ces signaux utilise une logique active à l'état bas. Un programme doit donc tenir compte de la polarité propre à chaque fonction lorsqu'il interprète la valeur lue.

Les deux bits de poids fort sont consacrés aux commutateurs de difficulté. Le bit 6 correspond au niveau de difficulté du joueur 0, c'est-à-dire généralement le joueur utilisant le contrôleur gauche, tandis que le bit 7 correspond au niveau de difficulté du joueur 1, associé au contrôleur droit. Chaque commutateur possède deux positions traditionnellement désignées A et B.

La signification exacte des positions A et B dépend du jeu. Le matériel ne définit pas lui-même ce que signifie « difficile » ou « facile » : il fournit simplement au programme l'état du commutateur. Un jeu peut utiliser cette information pour modifier la vitesse d'un projectile, la taille d'un objet, le comportement d'un adversaire, le niveau de difficulté ou toute autre règle. Certains logiciels n'utilisent d'ailleurs pas ces commutateurs.

Le bit 3 de SWCHB correspond au commutateur Color/BW, c'est-à-dire couleur/noir et blanc. Sur les premières versions de la console, ce commutateur était destiné à adapter certains jeux aux téléviseurs couleur ou monochromes. Le TIA ne transforme toutefois pas automatiquement toute l'image en noir et blanc lorsque ce commutateur est actionné. Il appartient au logiciel de lire le bit et de modifier son affichage s'il souhaite tenir compte de la position choisie.

Cette distinction est importante : le commutateur Color/BW est essentiellement une entrée destinée au programme, et non un traitement vidéo matériel imposé après la génération de l'image. Un jeu peut donc modifier ses couleurs, utiliser des motifs différents ou même attribuer une autre fonction au commutateur. Sur certaines révisions plus tardives de la console, son étiquetage ou son usage physique peut également différer.

Le bit 1 de SWCHB correspond au commutateur Select. Il permet généralement de parcourir les différentes variantes proposées par un jeu : nombre de joueurs, niveau, type de partie ou autres options. Comme la fonction exacte dépend entièrement du logiciel, deux cartouches peuvent interpréter Select de façons très différentes.

Le bit 0 correspond au commutateur Reset. Celui-ci sert généralement à démarrer ou recommencer une partie, mais il ne doit pas être confondu avec le RESET matériel du processeur 6507. L'action sur ce commutateur est visible par le programme au moyen de SWCHB; c'est donc normalement le logiciel du jeu qui détecte cette entrée et décide de réinitialiser son propre état.

La disposition principale des bits de SWCHB peut ainsi être résumée comme suit : bit 7 pour la difficulté de P1, bit 6 pour la difficulté de P0, bit 3 pour Color/BW, bit 1 pour Select et bit 0 pour Reset. Les autres bits ne correspondent pas, dans l'utilisation standard de la VCS, à ces cinq commandes principales et ne doivent pas être inventés comme des boutons supplémentaires.

Les commutateurs Reset et Select présentent une particularité supplémentaire. Sur certains boîtiers de l'Atari 2600, ils sont momentanés : l'utilisateur les actionne brièvement et ils reviennent mécaniquement à leur position de repos lorsqu'ils sont relâchés. Ils ne se comportent donc pas comme les commutateurs de difficulté, qui restent normalement dans la position A ou B sélectionnée.

Du point de vue du logiciel, cependant, Reset et Select apparaissent comme des niveaux logiques maintenus pendant toute la durée de leur actionnement. Tant que l'utilisateur maintient le commutateur, le programme peut lire l'état actif lors de plusieurs passages successifs. Il ne reçoit pas automatiquement un événement unique correspondant à un « clic ».

Cette caractéristique peut obliger le jeu à effectuer une forme de détection de transition. Si une routine exécutée à chaque frame réagit directement tant que Select reste actif, elle pourrait par exemple faire défiler plusieurs options extrêmement rapidement pendant un seul appui. Le programme peut donc mémoriser l'état précédent et ne déclencher l'action qu'au passage de l'état relâché à l'état pressé.

Un debouncing logique peut également être appliqué lorsque cela est nécessaire. Les contacts mécaniques réels peuvent produire de brèves transitions électriques lorsqu'ils changent d'état. Le logiciel peut ignorer ces fluctuations ou imposer une petite logique de stabilisation afin qu'un seul actionnement physique soit interprété comme une seule commande.

Pour un émulateur Atari 2600, il est important de reproduire l'interface telle qu'elle est perçue par le logiciel. Reset et Select ne doivent pas nécessairement être transformés artificiellement en événements instantanés : pendant qu'une touche de l'ordinateur ou un bouton de manette représentant l'un de ces commutateurs est maintenu, le bit correspondant de SWCHB peut rester dans son état actif. Le jeu conserve alors la responsabilité de gérer cette durée comme sur le matériel original.

Les commutateurs de difficulté et Color/BW doivent également être représentés comme des états persistants, puisque leur position reste normalement sélectionnée jusqu'à une nouvelle intervention de l'utilisateur. Un émulateur peut présenter ces fonctions sous forme de boutons, de cases ou de raccourcis clavier, mais les valeurs exposées au programme doivent correspondre aux bits appropriés du RIOT.

Les commutateurs de console constituent donc une partie intégrante de l'interface logicielle de l'Atari 2600. Dans SWCHB, le bit 6 représente la difficulté de P0, le bit 7 celle de P1, le bit 3 Color/BW, le bit 1 Select et le bit 0 Reset. Les niveaux de difficulté sont traditionnellement interprétés comme les positions A et B, tandis que la signification exacte de chaque choix reste déterminée par le jeu.

Enfin, Reset et Select peuvent être momentanés physiquement tout en apparaissant comme des niveaux maintenus pour le logiciel pendant leur actionnement. Une routine peut donc devoir détecter les transitions ou effectuer un debouncing logique pour éviter plusieurs actions involontaires. Comme pour de nombreux éléments de l'Atari 2600, le matériel fournit ici des signaux relativement simples et laisse au programme la responsabilité d'en définir précisément le comportement.

Connecteur cartouche

Le connecteur cartouche de l'Atari 2600 constitue l'interface directe entre la console et le circuit électronique contenu dans la cartouche. Sur une cartouche simple, son rôle principal consiste à permettre au MOS 6507 de lire le programme entreposé dans une ROM. Cependant, le connecteur expose suffisamment de signaux du bus pour permettre des conceptions beaucoup plus évoluées, notamment des systèmes de commutation bancaire (bank switching), de la RAM supplémentaire, des coprocesseurs et même, dans certains cas, des fonctions audio additionnelles.

Le connecteur donne accès aux treize lignes d'adresse A0 à A12 du 6507. Contrairement au 6502 complet, le 6507 n'expose que ces treize bits d'adresse sur ses broches externes. Il peut donc sélectionner directement 8192 positions d'adresse physiques, même si les instructions du cour processeur continuent à manipuler des adresses logiques sur 16 bits. Cette limitation explique les nombreux miroirs présents dans la carte mémoire de la console.

Les huit lignes de données D0 à D7 sont également accessibles sur le connecteur. Elles constituent le bus de données 8 bits par lequel le processeur reçoit les octets provenant de la ROM de la cartouche. Sur des cartouches plus complexes comportant de la RAM ou d'autres circuits, ces mêmes lignes peuvent également transporter des données écrites par le processeur vers le matériel de la cartouche.

Le connecteur expose en outre les signaux nécessaires pour déterminer la nature et le moment des transactions effectuées sur le bus, notamment les informations de lecture/écriture ainsi que des signaux liés à l'horloge ou à la phase du processeur, conformément au brochage matériel de la VCS. Une cartouche active doit observer ces signaux correctement afin de présenter ses données au bon moment et d'éviter de piloter le bus lorsqu'un autre composant doit répondre.

L'alimentation nécessaire aux circuits de la cartouche est également fournie par la console. Le connecteur comporte notamment une alimentation +5 V et une masse (GND). Une cartouche ROM traditionnelle peut donc fonctionner directement à partir de l'alimentation de la VCS sans nécessiter de source électrique externe.

Dans la configuration la plus simple, une cartouche contient essentiellement une ROM directement adressée par le 6507. L'espace de cartouche est sélectionné lorsque la ligne d'adresse appropriée indique la moitié supérieure de l'espace physique accessible. Une ROM simple apparaît ainsi dans la plage $1000 à $1FFF, soit une fenêtre de 4 Kio.

Cette organisation correspond particulièrement bien aux cartouches de 4 Kio, qui peuvent être directement mappées dans cette fenêtre sans mécanisme de commutation bancaire. Les premières cartouches encore plus petites, par exemple celles de 2 Kio, peuvent apparaître avec des miroirs à l'intérieur de cette zone lorsque toutes les lignes d'adresse disponibles ne sont pas décodées par la ROM ou la logique de la cartouche.

La limite de la fenêtre $1000-$1FFF ne signifie toutefois pas qu'une cartouche Atari 2600 est limitée à 4 Kio de contenu. Lorsque les jeux ont commencé à nécessiter davantage de ROM, les fabricants ont ajouté de la logique permettant de sélectionner dynamiquement différentes portions d'une mémoire plus grande. C'est le principe du bank switching : plusieurs banques physiques partagent la même fenêtre d'adressage du 6507, et le programme choisit celle qui doit être visible à un instant donné.

Les cartouches utilisant cette technique observent certains accès du processeur à des adresses particulières, souvent appelées hotspots. Une lecture ou une écriture sur l'un de ces emplacements peut provoquer la sélection d'une nouvelle banque. La signification de l'accès dépend du circuit de cartouche employé : certains systèmes réagissent à une lecture, d'autres à une écriture, et certains peuvent être sensibles à tout accès au hotspot.

Cette caractéristique signifie qu'une cartouche avancée ne se comporte plus comme une simple mémoire passive. Elle peut observer la séquence des cycles du bus, reconnaître des adresses particulières et modifier son état interne en conséquence. Le résultat d'une lecture ultérieure dans $1000-$1FFF dépend alors de la banque actuellement sélectionnée.

La présence de hotspots rend également importants les accès intermédiaires ou dummy accesses produits par le 6507. Une instruction peut présenter sur le bus une adresse qui n'est pas simplement son adresse finale apparente. Si cette adresse correspond à un hotspot, la cartouche peut réagir et changer de banque. Une émulation fidèle doit donc reproduire les accès au bus pertinents, et non uniquement le résultat logique final des instructions.

Des conceptions plus avancées peuvent également ajouter de la RAM dans la cartouche. Celle-ci fournit au programme davantage de mémoire que les 128 octets du RIOT. Selon le schéma utilisé, certaines plages ou certains hotspots permettent de sélectionner les zones de lecture et d'écriture de cette RAM. Le programme doit alors respecter le protocole particulier défini par le matériel de la cartouche.

Certaines cartouches modernes ou historiquement sophistiquées vont encore plus loin en intégrant un coprocesseur. Celui-ci peut effectuer des calculs, préparer des données ou assister le 6507 dans des tâches qui seraient trop coûteuses pour le processeur seul. Le connecteur cartouche devient alors une interface entre la VCS et un véritable sous-système électronique capable d'observer et de servir le bus de la console.

Il est également possible d'ajouter des fonctions audio dans ou par l'intermédiaire d'un matériel de cartouche approprié. De telles extensions ne font pas partie du TIA standard et nécessitent une conception adaptée à la manière dont le signal supplémentaire est produit ou intégré au système. Elles illustrent néanmoins le fait que le connecteur peut être exploité pour bien davantage qu'une simple ROM.

Toutes ces extensions doivent respecter les caractéristiques électriques du matériel Atari. Les niveaux logiques, la direction des lignes, les périodes pendant lesquelles un circuit peut piloter le bus et les contraintes temporelles doivent correspondre au schéma de la console. Deux circuits qui tenteraient de piloter simultanément une ligne de données avec des niveaux opposés pourraient provoquer une contention électrique et éventuellement endommager le matériel.

Le brochage doit également être respecté avec une grande attention lors de la conception d'une cartouche ou d'un adaptateur. Le connecteur de la VCS n'est pas conçu comme une interface moderne protégée contre toutes les erreurs de manipulation. Il ne faut notamment pas supposer qu'il possède une protection complète contre une insertion inversée, un mauvais câblage ou un court-circuit entre l'alimentation et une autre ligne.

Une cartouche expérimentale mal conçue peut donc présenter un risque réel pour la console, la cartouche elle-même ou les deux. La présence du +5 V, de la masse et de plusieurs lignes directement liées au bus rend particulièrement importante la vérification du brochage avant toute connexion au matériel original. Les signaux doivent être utilisés conformément au schéma électrique Atari et aux caractéristiques des composants concernés.

Pour un émulateur Atari 2600, le connecteur cartouche doit lui aussi être considéré comme davantage qu'une simple source d'octets ROM. Une émulation de base peut mapper une image de 2 ou 4 Kio dans $1000-$1FFF, mais une compatibilité étendue exige de représenter les différents circuits de bank switching, leurs hotspots, les extensions RAM et, lorsque cela est nécessaire, les coprocesseurs ou fonctions supplémentaires.

Le connecteur cartouche constitue ainsi l'un des principaux moyens par lesquels l'architecture initialement très limitée de l'Atari 2600 a pu évoluer. Il expose A0-A12, D0-D7, les signaux de lecture/écriture et d'horloge ou de phase, ainsi que le +5 V et la masse, selon le brochage de la console. Une ROM simple décode principalement la fenêtre $1000-$1FFF, tandis que les cartes plus évoluées peuvent observer les lectures et écritures sur des hotspots, effectuer du bank switching et ajouter de la RAM, un coprocesseur ou des capacités audio.

Cette flexibilité doit cependant être utilisée en respectant strictement le matériel. Le connecteur n'offre pas les protections que l'on attendrait d'une interface moderne contre l'inversion ou les courts-circuits, et toute extension physique doit respecter les niveaux électriques, le timing et le brochage définis par Atari. C'est cette interface relativement simple mais directement reliée au bus qui a permis aux cartouches de repousser progressivement les limites de la VCS bien au-delà de ce que permettaient les premières ROM de quelques kilo-octets.

ROM 2 Kio et 4 Kio

Les premières cartouches de l'Atari 2600 utilisent une organisation particulièrement simple : la ROM est directement présentée au MOS 6507 dans la fenêtre mémoire réservée à la cartouche. Cette fenêtre occupe normalement les adresses $1000 à $1FFF, soit 4 Kio de l'espace physique de 13 bits accessible par le processeur. Une cartouche de 2 Kio ou de 4 Kio peut donc fonctionner sans mécanisme de commutation bancaire complexe.

Une ROM de 4 Kio correspond exactement à la taille de cette fenêtre. Ses 4096 octets peuvent être directement associés aux adresses $1000-$1FFF. L'octet situé à l'offset $000 de l'image ROM apparaît à $1000, celui situé à l'offset $001 à $1001, et ainsi de suite jusqu'au dernier octet de l'image, situé à l'offset $FFF, qui apparaît à l'adresse $1FFF.

Cette correspondance directe rend les cartouches de 4 Kio particulièrement simples du point de vue matériel. Les douze bits d'adresse inférieurs nécessaires pour sélectionner l'un des 4096 octets peuvent être utilisés par la ROM, tandis que la sélection générale de l'espace cartouche détermine quand celle-ci doit répondre au processeur. Aucun registre de banque n'est nécessaire puisque la totalité du programme tient dans la fenêtre disponible.

Une ROM de 2 Kio, en revanche, ne possède que 2048 octets alors que la fenêtre cartouche en contient 4096. La ROM ne décode donc pas suffisamment de lignes d'adresse pour distinguer les deux moitiés de cette fenêtre. Le même contenu apparaît par conséquent deux fois, sous forme de miroir.

Autrement dit, les 2 Kio de données sont visibles une première fois dans une moitié de la fenêtre, puis de nouveau dans l'autre. Deux adresses séparées de $0800 peuvent ainsi sélectionner le même octet physique de ROM. Ce comportement n'exige aucune copie supplémentaire dans la cartouche : il résulte simplement du décodage incomplet de l'adresse.

Ce mécanisme est particulièrement important pour les vecteurs du 6507. Le cour du processeur conserve les vecteurs hérités du 6502 aux adresses logiques $FFFA-$FFFF : NMI à $FFFA-$FFFB, RESET à $FFFC-$FFFD et IRQ/BRK à $FFFE-$FFFF. Comme seules les lignes A0 à A12 sont exposées par le 6507, ces adresses se replient physiquement vers $1FFA-$1FFF, c'est-à-dire les six derniers octets visibles de la fenêtre cartouche.

Dans une image ROM de 4 Kio, les six octets correspondant aux vecteurs se trouvent donc directement à la fin du fichier, aux offsets $0FFA-$0FFF. Ils apparaissent dans l'espace du 6507 à $1FFA-$1FFF. Le vecteur RESET, indispensable au démarrage, occupe ainsi les octets visibles à $1FFC-$1FFD.

Pour une cartouche de 2 Kio, le principe reste identique du point de vue du processeur, mais il faut tenir compte du miroir. Puisque le contenu de 2 Kio est répété dans la fenêtre de 4 Kio, les adresses $1FFA-$1FFF correspondent aux six derniers octets de la ROM de 2 Kio, par l'intermédiaire de ce miroir. Les vecteurs doivent donc être placés aux emplacements équivalents de l'image de 2 Kio afin qu'ils apparaissent correctement aux adresses attendues par le 6507.

Le vecteur RESET est particulièrement important, car c'est lui que le processeur consulte lorsqu'il commence l'exécution après une réinitialisation. Une image ROM mal construite dont les derniers octets ne fournissent pas un vecteur RESET valide peut envoyer le processeur vers une adresse incorrecte et empêcher le programme de démarrer normalement.

Contrairement à de nombreux formats de cartouches ou d'images disque plus récents, une image brute de cartouche Atari 2600 ne contient normalement aucun en-tête standardisé décrivant son matériel. Le fichier peut n'être qu'une succession des octets présents dans la ROM originale. Il n'existe donc pas nécessairement, au début du fichier, une structure indiquant « cartouche 4 Kio », « mapper F8 » ou « présence de RAM ».

Pour les images les plus simples, la taille du fichier fournit déjà une indication importante. Une image de 2048 octets correspond naturellement à une ROM de 2 Kio, tandis qu'une image de 4096 octets peut correspondre à une ROM simple de 4 Kio. Pour des cartouches plus grandes, cependant, la taille seule ne suffit généralement pas à déterminer précisément le circuit utilisé.

Les cartouches employant du bank switching peuvent utiliser différentes méthodes pour sélectionner leurs banques. Le logiciel accède alors à des adresses particulières, ou hotspots, qui modifient l'état du circuit de cartouche. Lorsqu'un émulateur ne dispose d'aucune métadonnée, il peut analyser le contenu de la ROM pour rechercher des signatures ou des accès caractéristiques à ces hotspots.

Cette détection reste cependant une heuristique. Une suite d'octets ressemblant à une instruction accédant à un hotspot peut apparaître dans des données qui ne sont jamais exécutées, tandis qu'un programme peut utiliser un mécanisme d'une manière difficile à reconnaître par une analyse simple. Plusieurs schémas de cartouche peuvent également partager la même taille de ROM.

Pour obtenir une identification plus fiable, un émulateur peut consulter une base de données de cartouches connues. Une empreinte ou un hachage de l'image permet alors d'associer exactement la ROM à un titre et au matériel attendu. La base peut préciser le type de bank switching, la présence éventuelle de RAM, le standard vidéo, le contrôleur utilisé et d'autres propriétés impossibles à déterminer avec certitude à partir des seuls octets de la ROM.

Des métadonnées externes peuvent également fournir ces informations. Un format d'accompagnement, une configuration choisie par l'utilisateur ou les informations d'un système de gestion de ROM peuvent indiquer explicitement le type de cartouche. Cette approche évite d'avoir à deviner le matériel à chaque chargement.

Pour un émulateur Atari 2600, il est donc important de séparer le contenu brut de la ROM de la description du matériel qui l'accompagne. Une image de cartouche originale ne possède pas nécessairement de champ interne permettant d'identifier son mapper. L'émulateur doit déterminer cette configuration à partir de la taille, de l'analyse des signatures et hotspots, d'une base de données ou de métadonnées externes.

Les cartouches simples de 2 Kio et 4 Kio représentent le cas le plus direct. Une ROM de 2 Kio est reflétée deux fois dans la fenêtre de 4 Kio, tandis qu'une ROM de 4 Kio occupe directement $1000-$1FFF. Dans les deux cas, les vecteurs doivent apparaître dans les six derniers octets visibles, correspondant à $1FFA-$1FFF; pour une ROM de 2 Kio, ces emplacements sont obtenus par le miroir du contenu.

Cette absence d'en-tête explique enfin pourquoi deux fichiers Atari 2600 de taille identique ne doivent pas automatiquement être considérés comme utilisant le même matériel. À mesure que les cartouches sont devenues plus sophistiquées, la ROM brute a cessé de suffire à décrire entièrement leur fonctionnement. Une émulation robuste doit donc combiner taille du fichier, détection des accès caractéristiques, bases de données et métadonnées, plutôt que de supposer que l'organisation de la cartouche est explicitement enregistrée dans son image ROM.

Bankswitch F8, F6 et F4

Lorsque les jeux Atari 2600 ont commencé à dépasser les 4 Kio directement accessibles dans la fenêtre cartouche $1000-$1FFF, il est devenu nécessaire d'utiliser un mécanisme de commutation bancaire, ou bank switching. Le principe consiste à diviser une ROM plus grande en plusieurs banques de 4 Kio, puis à ne rendre visible qu'une seule de ces banques à la fois dans l'espace $1000-$1FFF. Le programme peut remplacer la banque actuellement visible en effectuant un accès à certaines adresses particulières appelées hotspots.

Parmi les méthodes les plus courantes figurent les schémas Atari standards F8, F6 et F4. Leur nom provient directement des adresses utilisées pour sélectionner les banques. Ces trois systèmes suivent le même principe général, mais permettent respectivement de gérer des ROM de 8 Kio, 16 Kio et 32 Kio.

Le schéma F8 est destiné aux cartouches de 8 Kio. La ROM est divisée en deux banques de 4 Kio, chacune pouvant être présentée dans la fenêtre $1000-$1FFF. Un accès à l'adresse $1FF8 sélectionne la banque 0, tandis qu'un accès à $1FF9 sélectionne la banque 1. Dès qu'une banque est sélectionnée, elle devient celle que le 6507 voit dans la fenêtre cartouche.

Le schéma F6 étend ce principe aux ROM de 16 Kio. La ROM contient alors quatre banques de 4 Kio. Les quatre hotspots consécutifs situés de $1FF6 à $1FF9 permettent de sélectionner les banques 0 à 3. Ainsi, $1FF6 sélectionne la banque 0, $1FF7 la banque 1, $1FF8 la banque 2 et $1FF9 la banque 3.

Le schéma F4 permet d'utiliser une ROM de 32 Kio, divisée en huit banques de 4 Kio. Les hotspots occupent les adresses $1FF4-$1FFB. Ils correspondent successivement aux huit banques : $1FF4 sélectionne la banque 0, $1FF5 la banque 1, et ainsi de suite jusqu'à $1FFB, qui sélectionne la banque 7.

Schéma Taille ROM Banques Hotspots
F8 8 Kio 2 × 4 Kio $1FF8 → banque 0, $1FF9 → banque 1
F6 16 Kio 4 × 4 Kio $1FF6-$1FF9 → banques 0 à 3
F4 32 Kio 8 × 4 Kio $1FF4-$1FFB → banques 0 à 7

Dans ces systèmes, le changement de banque est provoqué par un accès au hotspot. Il ne faut pas nécessairement comprendre cet accès comme une écriture dans un registre conventionnel. Selon la logique électronique utilisée dans la cartouche, une lecture ou une écriture de l'adresse concernée peut suffire à modifier l'état du sélecteur de banque. Ce qui importe est le cycle présenté sur le bus.

Cette propriété est particulièrement importante pour une émulation fidèle du 6507. Le processeur peut effectuer certains accès intermédiaires, souvent appelés dummy accesses, au cours de l'exécution d'une instruction. Même si un tel accès n'est pas évident lorsqu'on examine uniquement le résultat final de l'instruction, il apparaît réellement sur le bus d'un processeur physique.

Si l'adresse de cet accès intermédiaire correspond à un hotspot F8, F6 ou F4, le circuit de la cartouche peut donc changer de banque. Un émulateur qui exécuterait les instructions de manière purement abstraite, sans reproduire les accès au bus pertinents, risquerait de manquer cette commutation et de poursuivre l'exécution avec la mauvaise banque.

Cette question est parfois décrite en parlant de lectures spéculatives ou fictives (dummy reads) du 6502/6507. Du point de vue du programme, la valeur obtenue pendant ce cycle peut sembler inutile ; du point de vue du matériel de la cartouche, en revanche, l'adresse présentée sur le bus est bien réelle et peut déclencher le hotspot. Le bank switching doit donc être modélisé au niveau des cycles d'accès au bus lorsque la compatibilité recherchée l'exige.

Une autre particularité concerne le démarrage de la console. Après un RESET, le 6507 doit lire son vecteur RESET aux adresses logiques $FFFC-$FFFD, qui se replient vers $1FFC-$1FFD dans l'espace physique de la cartouche. La banque visible au moment du démarrage doit donc contenir un vecteur valide permettant au processeur de commencer l'exécution.

Dans les schémas Atari standards, la banque de reset est normalement la dernière banque. Pour F8, il s'agit donc généralement de la banque 1 ; pour F6, de la banque 3 ; et pour F4, de la banque 7. Cette organisation permet aux six derniers octets de cette banque, qui correspondent à $1FFA-$1FFF, de fournir les vecteurs nécessaires lorsque la console démarre.

Le programme peut ensuite commuter vers d'autres banques au moyen des hotspots. Comme seule une banque est visible à la fois dans $1000-$1FFF, le logiciel doit organiser soigneusement les transitions entre banques. Le code responsable de la commutation doit notamment s'assurer que l'exécution puisse continuer correctement après la sélection de la nouvelle banque.

Un détail matériel plus subtil concerne la valeur retournée par une lecture du hotspot lui-même. Lorsqu'un accès à $1FF8, $1FF9 ou à un autre hotspot provoque une commutation, il existe une question de timing : la donnée placée sur le bus pendant ce cycle provient-elle de la banque qui était active avant la commutation ou de la banque nouvellement sélectionnée ?

La réponse peut dépendre du circuit de bank switching et de son timing exact. Selon la manière dont la logique de sélection est réalisée, le changement de banque peut devenir effectif suffisamment tôt ou suffisamment tard pour influencer la donnée retournée pendant le cycle qui a lui-même provoqué la commutation.

Les programmes correctement conçus évitent donc de dépendre de cette valeur. L'accès au hotspot est utilisé pour son effet secondaire de sélection de banque, et non pour obtenir un octet significatif. Une fois la nouvelle banque sélectionnée, le programme effectue ensuite les lectures nécessaires à des adresses dont le contenu est défini.

Cette règle est également utile pour les émulateurs. Il est souhaitable de reproduire aussi fidèlement que possible le comportement du matériel pris en charge, mais un logiciel conforme aux conventions F8, F6 ou F4 ne devrait normalement pas exiger une valeur particulière provenant directement du cycle de commutation. En revanche, l'émulateur doit absolument reproduire le moment et la cause de la sélection de banque, y compris lorsque le hotspot est touché par un accès au bus qui n'est pas une lecture explicitement utile au programme.

Les schémas F8, F6 et F4 constituent ainsi une extension simple mais extrêmement efficace de l'architecture originale de l'Atari 2600. F8 fournit deux banques de 4 Kio pour une ROM de 8 Kio, sélectionnées par $1FF8-$1FF9; F6 fournit quatre banques pour 16 Kio, sélectionnées par $1FF6-$1FF9; et F4 fournit huit banques pour 32 Kio, sélectionnées par $1FF4-$1FFB.

La dernière banque est normalement celle visible au reset, afin que les vecteurs situés à la fin de la fenêtre cartouche soient accessibles au démarrage. Enfin, les hotspots peuvent réagir à tout accès réel au bus, y compris certains accès dummy. La donnée retournée pendant le cycle de commutation peut provenir de l'ancienne ou de la nouvelle banque selon le circuit employé ; les logiciels correctement conçus évitent donc de dépendre de cette valeur et utilisent l'accès uniquement pour son effet de commutation bancaire.

Autres bankswitch et RAM cartouche

Les systèmes F8, F6 et F4 ne représentent qu'une partie des mécanismes de bank switching utilisés sur l'Atari 2600. Comme l'architecture de la console ne définit pas elle-même un contrôleur de banques universel, les fabricants de cartouches pouvaient concevoir leur propre logique électronique. Plusieurs systèmes incompatibles sont ainsi apparus pour dépasser la fenêtre de 4 Kio accessible directement à $1000-$1FFF, ajouter de la RAM ou organiser la ROM d'une manière mieux adaptée à certains jeux.

Parmi ces systèmes figurent notamment FE, associé à certains jeux d'Activision, E0, utilisé par Parker Brothers, 3F, associé notamment aux cartouches de Tigervision, ainsi que les schémas UA, CV, FA, F0 et plusieurs variantes. Chacun possède ses propres règles de sélection des banques. Un émulateur ne peut donc pas traiter toutes les ROM de plus de 4 Kio comme de simples variantes de F8, F6 ou F4.

Le schéma FE utilise une méthode de commutation différente des hotspots placés à la fin de la fenêtre cartouche. La logique de la cartouche observe certains accès du 6507 et modifie la banque visible lorsqu'une condition particulière est rencontrée. Ce système est historiquement associé à des titres d'Activision et constitue un exemple important de mapper qu'un émulateur doit reconnaître séparément des schémas Atari standards.

Le système E0, notamment employé par Parker Brothers, est plus sophistiqué parce qu'il permet de travailler avec plusieurs segments de la fenêtre cartouche. Au lieu de remplacer nécessairement la totalité des 4 Kio visibles par une seule banque, certaines portions de l'espace peuvent être sélectionnées séparément. Cette organisation permet de conserver une partie du code fixe tout en changeant d'autres segments selon les besoins du programme.

Cette approche segmentée donne au logiciel davantage de souplesse. Une routine commune peut, par exemple, rester accessible en permanence dans une portion fixe de la fenêtre tandis que d'autres zones présentent différentes parties de la ROM. L'émulateur doit alors mémoriser plusieurs états de sélection plutôt qu'un simple numéro représentant une banque globale de 4 Kio.

Le schéma 3F, historiquement associé à Tigervision, utilise encore une autre technique. Sa sélection de banque est déclenchée par une écriture dans une adresse basse de l'espace mémoire, plutôt que par un hotspot situé uniquement vers $1FFx. Cela est possible en raison du décodage partiel de l'Atari 2600 et de la capacité du matériel de la cartouche à observer les cycles apparaissant sur le bus.

Ce comportement montre une nouvelle fois pourquoi l'émulation du bus est importante. Une écriture que le programme semble effectuer vers une zone normalement associée au TIA ou à un miroir interne peut également être observée par la logique de la cartouche. Le mapper 3F peut alors interpréter la valeur ou l'adresse correspondante comme une commande de sélection de banque.

D'autres systèmes, tels que UA et CV, emploient leurs propres méthodes de décodage et d'organisation de la mémoire. Ils doivent être considérés comme des types de cartouches distincts. Le nom du mapper représente donc non seulement une taille de ROM, mais surtout un protocole matériel indiquant comment la cartouche réagit aux lectures et écritures du 6507.

Le schéma FA, également connu dans le contexte du système CBS RAM Plus, est particulièrement intéressant parce qu'il ne se limite pas à augmenter la quantité de ROM accessible. Il ajoute également de la RAM dans la cartouche, offrant ainsi au programme une mémoire supplémentaire au-delà des seulement 128 octets fournis par le RIOT 6532.

Cette RAM supplémentaire peut être extrêmement précieuse sur une Atari 2600. Elle peut servir à conserver davantage d'état de jeu, des tables temporaires, des données graphiques ou d'autres informations qui seraient difficiles à faire tenir dans la RAM interne. Son accès doit toutefois respecter le protocole défini par le circuit de cartouche.

Le schéma F0, ainsi que diverses variantes développées au fil du temps, ajoute encore d'autres méthodes de sélection. Certaines techniques sont apparues sur les cartouches commerciales originales, tandis que d'autres ont été utilisées plus tard par des développements homebrew, des multicartouches ou du matériel moderne. La liste des mappers Atari 2600 est donc sensiblement plus vaste que les seuls systèmes F8, F6 et F4.

Une difficulté particulière apparaît lorsqu'une cartouche contient de la RAM. Le bus de données du 6507 est partagé entre le processeur et les différents circuits qui peuvent y répondre. Pendant une lecture, la mémoire doit placer sa valeur sur les lignes D0-D7. Pendant une écriture, c'est au contraire le processeur qui place la valeur sur ces lignes.

Certaines conceptions de cartouches utilisent donc des ports ou fenêtres d'adresse séparés pour la lecture et l'écriture de la RAM. Une plage peut servir à écrire dans la RAM sans que celle-ci tente de piloter simultanément le bus, tandis qu'une autre plage permet de relire son contenu. Cette séparation contribue à éviter une contention du bus, c'est-à-dire une situation où le processeur et la mémoire essaieraient de forcer simultanément des valeurs différentes sur les mêmes lignes de données.

Le programme doit connaître cette organisation. Une écriture effectuée dans une fenêtre peut modifier un octet de RAM, tandis que la lecture de ce même octet doit éventuellement être réalisée à une adresse différente. Une implémentation d'émulateur qui traiterait cette mémoire comme une RAM ordinaire directement lisible et inscriptible à la même adresse pourrait donc produire un comportement incorrect.

La diversité de ces circuits crée un problème important lors du chargement d'une image ROM brute. Comme les fichiers Atari 2600 ne possèdent généralement aucun en-tête standard décrivant leur matériel, l'émulateur doit déterminer quel mapper doit être utilisé. Or la taille du fichier ne suffit pas à fournir cette information.

Deux ROM de taille identique peuvent employer des schémas de commutation totalement différents. Une image de même capacité peut par exemple dépendre d'un système Atari standard, d'un mapper segmenté ou d'un mécanisme déclenché par des écritures particulières. Choisir le mapper uniquement à partir du nombre d'octets du fichier peut donc conduire à une cartouche qui démarre incorrectement ou ne fonctionne pas du tout.

L'analyse du code et la recherche de signatures caractéristiques peuvent aider à identifier certains systèmes, mais cette méthode reste heuristique. Une séquence ressemblant à un accès de bank switching peut apparaître accidentellement dans des données, tandis qu'un accès réel peut être généré d'une manière difficile à reconnaître statiquement.

Les émulateurs utilisent donc fréquemment des hashes de ROM associés à une base de données de cartouches connues. Le contenu du fichier est soumis à une fonction de hachage, puis l'empreinte obtenue est recherchée dans une base contenant le mapper et éventuellement d'autres propriétés du jeu. Cette approche permet d'identifier précisément les dumps déjà répertoriés, même lorsque plusieurs schémas utilisent exactement la même taille de fichier.

Une option utilisateur reste également importante. Une ROM inconnue, modifiée, traduite, développée récemment ou absente de la base peut nécessiter une sélection manuelle du mapper. L'émulateur peut proposer une détection automatique par défaut tout en permettant à l'utilisateur d'imposer explicitement FE, E0, 3F, UA, CV, FA, F0 ou un autre schéma pris en charge.

Pour les collections de ROM, les projets homebrew et les tests d'émulation, il est donc préférable de documenter le schéma de cartouche avec chaque ROM. Conserver uniquement le fichier binaire sans indiquer son matériel peut rendre son identification plus difficile plusieurs années plus tard, particulièrement lorsqu'il s'agit d'une ROM personnalisée qui n'existe dans aucune base de données publique.

L'écosystème Atari 2600 comporte ainsi de nombreux mécanismes de cartouche au-delà des classiques F8, F6 et F4. FE d'Activision, E0 de Parker Brothers avec ses segments, 3F de Tigervision et ses écritures dans l'espace bas, UA, CV, FA/CBS RAM Plus, F0 et leurs variantes utilisent des protocoles différents. Certains ajoutent également de la RAM, parfois accessible par des fenêtres distinctes de lecture et d'écriture afin d'éviter les conflits sur le bus.

Cette diversité explique pourquoi une taille de ROM identique ne permet pas d'identifier avec certitude le mapper. Une émulation robuste doit combiner détection heuristique, hashes et bases de données, métadonnées et possibilité de configuration manuelle. Lorsqu'une ROM est archivée ou distribuée dans un contexte de développement, son schéma de bank switching devrait donc être documenté explicitement avec elle.

DPC, Supercharger et extensions modernes

L'architecture originale de l'Atari 2600 est extrêmement limitée : un processeur MOS 6507, seulement 128 octets de RAM dans le RIOT et un TIA dont l'affichage doit être alimenté avec une précision de quelques cycles. Pourtant, le connecteur cartouche permet d'ajouter une électronique beaucoup plus sophistiquée qu'une simple ROM avec bank switching. Dès l'époque commerciale de la console, certains jeux ont ainsi utilisé du matériel supplémentaire dans leur cartouche. Les développements homebrew modernes ont poussé cette idée beaucoup plus loin avec de véritables coprocesseurs.

L'un des exemples historiques les plus célèbres est le système DPC (Display Processor Chip) employé par Pitfall II: Lost Caverns d'Activision. Cette cartouche ne contient pas uniquement une ROM conventionnelle : elle ajoute une logique spécialisée destinée à assister le 6507 dans la récupération et la manipulation des données nécessaires au jeu. Le DPC permet ainsi à Pitfall II d'obtenir des résultats particulièrement ambitieux pour une machine aussi limitée.

Le DPC fournit notamment plusieurs mécanismes appelés data fetchers. Ceux-ci peuvent être considérés comme des générateurs ou lecteurs de données matériels possédant leurs propres compteurs. Au lieu d'obliger constamment le 6507 à calculer une adresse, lire un octet, mettre à jour un pointeur et recommencer, le programme peut configurer ces mécanismes puis récupérer plus efficacement une succession de valeurs. Cette assistance est particulièrement intéressante lorsque le processeur doit préparer rapidement les données destinées au kernel vidéo.

Les compteurs associés aux fetchers permettent au DPC de parcourir ou de générer certaines séquences de données avec beaucoup moins d'intervention du 6507. Le circuit possède également des fonctions particulières liées à la génération de valeurs et au traitement nécessaire à Pitfall II. Le DPC doit donc être émulé comme un véritable composant possédant un état interne, et non comme un simple mapper sélectionnant différentes banques de ROM.

Le DPC apporte également des possibilités de génération sonore supplémentaires. Pitfall II est notamment connu pour sa musique plus élaborée que celle de nombreux jeux Atari 2600 traditionnels. Cette fonction complète les deux canaux audio normalement fournis par le TIA et constitue un autre exemple de la manière dont une cartouche peut étendre les capacités apparentes de la console.

L'idée d'utiliser du matériel dans la cartouche pour assister le 6507 a été reprise beaucoup plus tard par la communauté homebrew. Le système DPC+ constitue une évolution moderne de cette philosophie. Il conserve l'idée de fetchers et de services spécialisés accessibles au programme Atari, mais exploite les possibilités d'un matériel de cartouche beaucoup plus puissant que celui disponible lors de la commercialisation originale de la console.

Les cartouches modernes peuvent notamment contenir un microcontrôleur ARM fonctionnant à une vitesse sans commune mesure avec les quelque 1,19 MHz du 6507 NTSC. Ce coprocesseur peut effectuer des calculs qui seraient beaucoup trop coûteux pour le processeur original : logique du jeu, transformations de données, préparation graphique, calculs mathématiques, gestion de structures plus importantes ou génération de données destinées à l'affichage.

Cette architecture ne signifie cependant pas que le processeur ARM remplace complètement le 6507. Une approche courante consiste à répartir le travail entre les deux processeurs. Le coprocesseur moderne effectue les calculs complexes et prépare les informations nécessaires, tandis qu'un programme exécuté par le 6507 continue à réaliser les opérations qui doivent être synchronisées avec le matériel original.

Cette distinction est fondamentale pour comprendre ces extensions. Sur une Atari 2600 standard, c'est toujours le TIA qui génère le signal vidéo. Le TIA doit continuer à recevoir ses écritures de registres au moment approprié pendant chaque scanline. Un coprocesseur ARM présent dans la cartouche ne dispose pas simplement d'un framebuffer qu'il pourrait transmettre directement au téléviseur à la place du TIA.

Le coprocesseur prépare donc généralement les données que le kernel 6507 consommera. Il peut, par exemple, calculer à l'avance les valeurs graphiques nécessaires aux différentes lignes de l'image et les rendre accessibles rapidement au 6507. Le kernel peut alors consacrer ses précieux cycles à lire ces valeurs et à les écrire dans GRP0, GRP1, les registres du playfield, les couleurs ou les autres registres du TIA au moment exact.

Cette répartition permet de conserver les contraintes fondamentales de la machine tout en déplaçant une grande partie du travail général vers un processeur beaucoup plus puissant. Le résultat peut donner l'impression que l'Atari 2600 réalise des opérations extrêmement complexes, alors que la partie critique de la génération vidéo reste fondamentalement un kernel 6507 pilotant le TIA.

Plusieurs technologies modernes ont développé cette approche, notamment CDF et CDFJ, ainsi que BUS, ACE et d'autres systèmes. Il ne faut toutefois pas considérer tous ces noms comme de simples variantes interchangeables d'un même mapper. Ce sont des plateformes ou architectures de cartouche distinctes, avec leurs propres interfaces, organisations mémoire, services et méthodes de communication entre le programme 6507 et le matériel supplémentaire.

Pour un émulateur, cette distinction est importante. Émuler DPC+, CDF/CDFJ, BUS ou ACE ne consiste pas simplement à ajouter quelques hotspots de bank switching. Selon le système, il peut être nécessaire de reproduire des fetchers, des zones mémoire particulières, des opérations réalisées par le coprocesseur, des échanges de données avec le 6507 et différentes contraintes de synchronisation.

Une autre extension historique importante est le Starpath Supercharger. Contrairement à une cartouche ROM traditionnelle, le Supercharger permet de charger des jeux à partir de cassettes audio. Les programmes sont distribués sous la forme de signaux enregistrés sur cassette, puis chargés par l'intermédiaire du matériel du Supercharger.

Le Supercharger ajoute également de la RAM et son propre mécanisme de commutation mémoire. Le jeu chargé depuis la cassette peut être transféré dans cette mémoire puis présenté au 6507 selon l'organisation prévue par le matériel. Cette conception permet de disposer d'un environnement sensiblement différent de celui d'une cartouche ROM Atari conventionnelle et constitue une extension importante de l'architecture originale.

L'émulation du Supercharger doit donc reproduire plusieurs aspects : le comportement de sa RAM, son système de commutation et, lorsque l'émulateur accepte directement les enregistrements ou images correspondantes, le mécanisme permettant de représenter le chargement depuis cassette. Là encore, le Supercharger doit être considéré comme une plateforme matérielle particulière plutôt que comme une simple ROM plus grande.

Des cartouches modernes comme Harmony et UnoCart répondent à un autre besoin : permettre de charger et d'exécuter de nombreux jeux ou programmes homebrew sur une véritable Atari 2600 à partir d'un seul périphérique. Ces multicartouches utilisent une électronique moderne capable de reproduire ou d'émuler le comportement de nombreux types de cartouches historiques.

Lorsqu'un programme utilisant F8, F6, F4, 3F ou un autre système compatible est sélectionné, la multicartouche configure son matériel afin de présenter au 6507 le comportement attendu. Elle doit répondre aux hotspots, sélectionner les banques et éventuellement fournir les fonctions supplémentaires nécessaires. Une même cartouche physique peut ainsi se comporter successivement comme plusieurs types de cartouches Atari différentes.

Harmony et UnoCart illustrent donc la différence entre le matériel interne d'une cartouche moderne et le matériel que celle-ci présente à la console. La cartouche peut contenir un microcontrôleur rapide et beaucoup plus de mémoire que n'en possédait une cartouche des années 1980, mais du point de vue du 6507 elle doit reproduire les accès, banques et comportements attendus par le jeu chargé.

Toutes ces extensions ne changent pas un principe fondamental : sur une console Atari 2600 de base, l'affichage reste finalement produit par le couple 6507/TIA. Le coprocesseur de la cartouche peut préparer des données, accélérer des calculs, fournir de la mémoire, réaliser certaines fonctions spécialisées ou assister la génération sonore, mais il ne remplace pas directement le fonctionnement vidéo du TIA.

Le kernel exécuté par le 6507 doit donc toujours respecter les contraintes temporelles de la console et alimenter le TIA avec les données appropriées. Même lorsqu'un puissant processeur ARM travaille dans la cartouche, l'image finale conserve ainsi une caractéristique essentielle de l'Atari 2600 : elle résulte des écritures synchronisées effectuées vers le TIA pendant le balayage vidéo.

Les extensions de cartouche montrent finalement jusqu'où l'architecture ouverte et relativement directe du bus de l'Atari 2600 peut être poussée. Le DPC de Pitfall II ajoute des fetchers, des compteurs et des fonctions sonores ; DPC+ et les coprocesseurs ARM modernes peuvent calculer la logique et préparer les données graphiques avant de les fournir à un kernel 6507 ; CDF/CDFJ, BUS, ACE et d'autres technologies constituent leurs propres plateformes ; le Starpath Supercharger charge des programmes depuis cassette tout en ajoutant de la RAM et de la commutation mémoire ; enfin, des multicartouches comme Harmony et UnoCart peuvent reproduire de nombreux schémas de cartouches.

Malgré toute cette puissance supplémentaire, la séparation des rôles demeure essentielle : le coprocesseur assiste la console, mais ne génère pas directement la vidéo à la place du TIA. Tant que l'on parle d'une Atari 2600 standard, le 6507 reste responsable du kernel qui pilote le TIA, ce dernier demeurant le circuit qui transforme finalement ces opérations minutieusement synchronisées en signal vidéo.

Format des images ROM

Les images de cartouches de l'Atari 2600 sont généralement conservées sous la forme de fichiers extrêmement simples. L'extension la plus courante est .bin, pour binary. Dans la majorité des cas, un tel fichier contient directement les octets bruts de la ROM de la cartouche, dans le même ordre logique que celui attendu par le matériel. Contrairement à de nombreux formats utilisés par des consoles plus récentes, aucune structure complexe n'est nécessaire pour représenter une cartouche Atari 2600 élémentaire.

Les tailles classiques sont notamment 2, 4, 8, 16 et 32 Kio, mais cette liste ne constitue pas une limite du format. Les premières cartouches peuvent contenir seulement 2 ou 4 Kio, tandis que les systèmes de bank switching permettent d'utiliser 8, 16, 32 Kio ou davantage. Les cartouches plus sophistiquées et les développements homebrew modernes peuvent utiliser des images encore plus grandes, notamment lorsqu'ils reposent sur des mappers évolués ou des coprocesseurs.

Une caractéristique essentielle des fichiers .bin Atari 2600 est l'absence de header universel. Le premier octet du fichier est normalement un octet appartenant réellement à l'image de la ROM ; il n'est pas précédé d'une structure standard indiquant le nom du jeu, le type de cartouche, le standard vidéo ou le contrôleur à utiliser. Le fichier brut ne contient donc pas nécessairement suffisamment d'informations pour décrire à lui seul tout le matériel nécessaire à son exécution.

L'extension .a26 est également rencontrée pour les images Atari 2600. Elle permet notamment d'identifier plus facilement le système auquel appartient le fichier, mais elle ne signifie pas automatiquement que celui-ci possède un en-tête riche en métadonnées. Les fichiers .a26 restent souvent des images brutes, comparables dans leur organisation interne aux fichiers .bin. Il ne faut donc pas déterminer la structure du fichier uniquement à partir de son extension.

Cette absence d'en-tête pose peu de problèmes pour les cartouches les plus simples. Une image de 2 Kio peut généralement être interprétée comme une ROM reflétée dans la fenêtre cartouche, tandis qu'une image de 4 Kio peut être directement placée dans $1000-$1FFF. La situation devient toutefois beaucoup plus complexe lorsque plusieurs architectures de cartouches utilisent la même taille de ROM.

Une image de 8, 16 ou 32 Kio n'indique pas nécessairement, par sa seule taille, le mécanisme de bank switching qu'elle utilise. Les systèmes F8, F6, F4 et de nombreux autres schémas possèdent leurs propres hotspots et règles de commutation. Deux fichiers de taille identique peuvent donc nécessiter des mappers complètement différents.

Les émulateurs doivent par conséquent compléter les informations contenues dans le fichier brut. Stella, par exemple, peut s'appuyer sur des métadonnées et une base de données de propriétés afin d'associer une ROM connue aux paramètres nécessaires à son fonctionnement. Ces informations peuvent préciser le type de cartouche ou mapper, la région vidéo, les contrôleurs attendus et différentes particularités d'affichage ou de compatibilité.

La région constitue une information importante. Une ROM peut être conçue pour NTSC, PAL ou, plus rarement, SECAM. Le fichier brut ne possède pas nécessairement un champ indiquant explicitement cette information. Un émulateur peut tenter de la déterminer à partir du comportement du programme, utiliser une base de données ou laisser l'utilisateur choisir manuellement le standard approprié.

Le type de contrôleur constitue un autre exemple d'information externe utile. Un jeu peut attendre des joysticks standards, des paddles, un Driving Controller, un Keyboard Controller ou un autre périphérique. Puisqu'une ROM brute ne contient pas nécessairement une déclaration normalisée de cette exigence, une base de données peut indiquer quels périphériques doivent être connectés virtuellement aux ports gauche et droit.

Certaines métadonnées utilisées par Stella peuvent également spécifier le comportement appelé phosphor. Cette option concerne la manière dont l'émulateur reproduit certains effets visuels résultant de la persistance et de l'alternance des images sur les téléviseurs à tube cathodique. Certains jeux alternent rapidement des objets ou des images entre plusieurs frames et comptent sur la persistance visuelle pour que le résultat soit perçu comme une image plus stable ou combinée.

Des corrections ou propriétés particulières peuvent aussi être associées à certaines ROM. Elles permettent à l'émulateur de tenir compte de comportements spécifiques connus pour un jeu donné sans avoir à inscrire ces informations dans le dump original. Cette séparation entre les octets de la ROM et les métadonnées présente l'avantage de préserver exactement l'image originale de la cartouche.

Pour gérer correctement une collection, il est particulièrement utile de conserver un hash cryptographique, idéalement un SHA-256, pour chaque image. Le SHA-256 calcule une empreinte à partir de l'intégralité du fichier. Deux fichiers strictement identiques produisent la même empreinte, tandis qu'une modification, même minime, produit normalement une valeur différente.

Le hash permet ainsi d'identifier précisément un dump particulier, indépendamment de son nom de fichier. Un fichier nommé game.bin peut être renommé sans changer son SHA-256, tandis qu'un fichier portant exactement le même nom mais contenant un octet différent possédera une autre empreinte. Cette propriété est particulièrement utile pour associer une image ROM aux entrées d'une base de données.

La taille exacte du fichier devrait également être conservée. Il est préférable de noter le nombre d'octets plutôt que de se limiter à une indication approximative comme « 8 Kio ». La taille peut aider à identifier un dump incorrect, une duplication involontaire de banques, un fichier tronqué ou une image contenant des données supplémentaires.

Il est également souhaitable de conserver la provenance de l'image. Celle-ci peut indiquer, par exemple, qu'il s'agit d'un dump effectué à partir d'une cartouche particulière, d'une version publiée officiellement par son détenteur, d'une image fournie avec un projet homebrew ou d'un fichier provenant d'une collection d'archives. Cette information aide à distinguer plusieurs révisions d'un même programme et à évaluer l'origine d'un fichier.

Le mapper doit être documenté lorsque celui-ci est connu. Pour une ROM simple, l'information peut indiquer 2K ou 4K; pour une cartouche à commutation bancaire, elle peut préciser F8, F6, F4, E0, 3F, FE, FA ou un autre schéma. Pour les développements plus récents, elle peut également identifier DPC+, CDF/CDFJ ou toute autre plateforme nécessaire à l'exécution.

La région vidéo devrait être conservée avec ces informations, par exemple NTSC, PAL ou SECAM. Cette donnée est importante parce que le standard choisi influence non seulement les couleurs, mais également la cadence des frames, le nombre de lignes, les fréquences de référence et potentiellement la hauteur des sons produits par le TIA.

Pour une conservation rigoureuse, une fiche associée à chaque ROM peut donc contenir au minimum : SHA-256, taille exacte, provenance, mapper et région. D'autres champs peuvent naturellement être ajoutés, notamment le nom du jeu, sa révision, les contrôleurs nécessaires et les propriétés particulières d'émulation.

Il est préférable que ces métadonnées restent séparées du dump original. Ajouter directement un en-tête propriétaire à une ROM brute transforme le fichier : sa taille change, son SHA-256 change et il ne représente plus exactement les octets récupérés de la mémoire originale. Un logiciel qui attend une ROM brute peut également ne plus reconnaître correctement le fichier.

Cela ne signifie pas qu'un format enrichi avec un en-tête soit nécessairement inutile. Un outil peut parfaitement créer une représentation contenant des métadonnées supplémentaires. Toutefois, si l'on choisit cette approche, il est fortement recommandé de conserver parallèlement le dump brut original, intact, afin de toujours disposer d'une copie de référence.

Cette précaution est particulièrement importante dans une perspective d'archivage et de préservation. Le dump brut représente les données originales, tandis que le mapper, la région, le type de contrôleur et les autres informations décrivent la manière dont ces données doivent être interprétées. Mélanger les deux en modifiant irréversiblement le fichier original complique les comparaisons avec d'autres dumps et bases de données.

Le format des images Atari 2600 reste donc volontairement simple : les fichiers .bin contiennent généralement des octets bruts, avec des tailles fréquentes de 2, 4, 8, 16 ou 32 Kio, voire davantage, et il n'existe aucun en-tête universel. L'extension .a26 est elle aussi souvent utilisée pour des données brutes et ne garantit pas la présence de métadonnées internes.

Les informations nécessaires à une émulation précise peuvent être fournies séparément par les métadonnées de Stella, une base de données ou une configuration utilisateur : mapper, région, contrôleur, phosphor et corrections particulières. Pour chaque ROM, il est enfin recommandé de conserver son SHA-256, sa taille, sa provenance, son mapper et sa région, et surtout de préserver le dump original intact lorsqu'un format propriétaire enrichi ou un en-tête supplémentaire est créé.

Initialisation correcte

Le démarrage d'un programme sur Atari 2600 doit être effectué avec beaucoup de rigueur. Après un RESET, le logiciel ne doit pas supposer que la RAM, les registres du TIA ou ceux du RIOT 6532 contiennent automatiquement des zéros ou des valeurs adaptées au jeu. Une initialisation explicite permet de placer la console dans un état connu avant de commencer la génération normale des images et l'exécution de la logique du jeu.

L'une des premières instructions généralement exécutées est SEI (Set Interrupt Disable). Elle positionne le drapeau I du registre d'état du 6507 afin de désactiver la prise en compte des interruptions IRQ masquables. Sur une Atari 2600 standard, les interruptions matérielles ne constituent normalement pas la base du fonctionnement du programme, mais établir explicitement cet état permet de démarrer avec un contexte processeur bien défini.

Le programme doit également exécuter CLD (Clear Decimal Mode). Le 6507, comme le 6502 dont il dérive, possède un mode décimal utilisé par ADC et SBC. Il ne faut pas supposer que ce mode se trouve nécessairement dans l'état désiré après le reset. CLD garantit que les opérations arithmétiques commencent en mode binaire normal, jusqu'à ce que le programme choisisse éventuellement d'activer le mode décimal avec SED.

Le pointeur de pile SP doit lui aussi être initialisé explicitement. Une séquence classique consiste à charger $FF dans le registre X puis à exécuter TXS, ce qui place le sommet logique de la pile à l'endroit prévu. Cette précaution est particulièrement importante sur l'Atari 2600, car la pile logique du 6507 se replie, en raison du décodage mémoire, sur les mêmes 128 octets physiques du RIOT que ceux utilisés pour les variables.

La RAM située conventionnellement à $80-$FF doit ensuite être placée dans un état connu. Le programme peut parcourir ces 128 octets dans une boucle et les effacer, généralement en y écrivant zéro. Les positions des objets, compteurs, états du jeu, pointeurs, valeurs pseudo-aléatoires et autres variables ne doivent jamais dépendre du contenu électrique aléatoire ou indéterminé présent au démarrage.

Le même principe s'applique au TIA. Les registres nécessaires au programme doivent recevoir explicitement leurs valeurs initiales. Il ne faut pas partir du principe que les players, missiles, balle, playfield, couleurs, mouvements horizontaux, états audio ou autres fonctions se trouvent automatiquement dans un état neutre. Un registre laissé dans un état indéterminé pourrait produire momentanément des objets, des couleurs, des sons ou des comportements inattendus.

Les fonctions du RIOT 6532 utilisées par le jeu doivent également être configurées explicitement. Cela concerne notamment les registres de direction des ports, les entrées/sorties et la minuterie lorsque celle-ci est employée. L'objectif général est simple : aucun état important du TIA, du RIOT ou de la RAM ne doit être supposé nul uniquement parce que la console vient d'être réinitialisée.

Pendant cette phase d'initialisation, il est préférable de maintenir l'affichage dans le vertical blank. Le programme active donc VBLANK afin d'empêcher l'apparition à l'écran des états graphiques intermédiaires pendant qu'il prépare ses registres et ses variables. Le spectateur ne voit ainsi pas les données indéterminées qui pourraient autrement être générées avant que le kernel soit prêt.

Le programme doit ensuite commencer à produire une trame vidéo stable. Cette étape est importante parce que, sur l'Atari 2600, le logiciel participe directement à la construction de la synchronisation verticale et au nombre de scanlines de chaque frame. Le TIA ne possède pas un contrôleur vidéo autonome capable de générer automatiquement une trame NTSC ou PAL complète pendant que le processeur effectue son initialisation.

Une fois l'état matériel initialisé et la synchronisation vidéo établie, le programme peut entrer dans sa boucle principale de frame. Cette boucle doit être organisée autour des différentes phases de la trame : VSYNC, VBLANK, traitement logique, positionnement des objets, kernel visible et overscan.

La première étape consiste à générer correctement VSYNC, afin de fournir la synchronisation verticale attendue par le téléviseur. Pour une trame NTSC typique, le signal est maintenu pendant trois scanlines. VBLANK reste actif pendant cette période et pendant les lignes de blanking supplémentaires nécessaires avant le début de la zone visible.

Le VBLANK fournit une période particulièrement utile pour effectuer les calculs qui ne peuvent pas être réalisés pendant le kernel visible. Le programme peut y mettre à jour la logique du jeu : mouvements, compteurs, intelligence des ennemis, score, animations, préparation des pointeurs graphiques et autres opérations nécessaires à la prochaine image.

La minuterie du RIOT est souvent utilisée pour contrôler la durée de cette phase. Le programme charge par exemple TIM64T avec une valeur appropriée, exécute sa logique puis surveille INTIM. Cette organisation permet d'utiliser efficacement le temps disponible tout en rejoignant ensuite une frontière de scanline connue avec WSYNC.

Le positionnement horizontal des objets est également préparé ou exécuté dans une phase où suffisamment de cycles sont disponibles. Les positions logiques calculées par le jeu doivent être transformées en temporisations et valeurs utilisables par RESP0, RESP1, RESM0, RESM1, RESBL, ainsi que par les registres de mouvement fin HMP0, HMP1, HMM0, HMM1 et HMBL.

Lorsque la période de blanking est terminée, le programme désactive VBLANK et commence le kernel visible. Pour une image NTSC conventionnelle, cette portion comporte souvent 192 lignes visibles. Le 6507 doit alors écrire dans les registres du TIA à des cycles précisément déterminés afin de construire le playfield, les players, les missiles, la balle et les couleurs de chaque ligne.

Pendant cette partie, la régularité temporelle devient extrêmement importante. Une branche conditionnelle prenant un nombre de cycles différent peut déplacer une écriture TIA et provoquer une déformation visible. Les kernels sont donc généralement construits de manière à ce que les différents chemins d'exécution respectent un budget de cycles soigneusement calculé.

À la fin de la zone visible commence l'overscan. VBLANK peut être réactivé afin de masquer cette partie de la trame. Le processeur dispose alors de nouveau de temps pour effectuer des tâches qui n'ont pas besoin d'être synchronisées avec les pixels visibles.

L'overscan constitue notamment un moment approprié pour traiter les collisions et les entrées. Les registres de collision du TIA peuvent être lus après avoir accumulé les chevauchements produits pendant la zone visible. Le programme peut également lire les manettes de jeux, boutons et commutateurs de console, puis utiliser ces informations pour préparer l'état logique de la frame suivante.

La répartition exacte des tâches entre VBLANK et overscan dépend naturellement du jeu. Certains programmes lisent les contrôleurs plus tôt ou effectuent une partie de leur logique à un autre moment. Ce qui importe est de conserver la structure temporelle globale de la trame.

Le nombre total de lignes doit en particulier rester constant d'une frame à l'autre. Une trame NTSC conventionnelle peut par exemple contenir 262 scanlines. Si une branche de la logique du jeu prend davantage de temps et provoque accidentellement l'ajout ou la suppression d'une ligne, la durée de la frame change.

Il ne faut donc jamais laisser une différence de gameplay modifier involontairement la structure vidéo. Par exemple, la présence de dix ennemis au lieu de neuf, une collision ou l'appui sur un bouton ne devrait pas faire varier le nombre total de scanlines. Les chemins de code dont la durée varie doivent être compensés, limités à une période contrôlée ou synchronisés avec une minuterie afin que la trame suivante commence toujours au bon moment.

Cette stabilité est importante pour le matériel réel. Un téléviseur CRT possède une certaine tolérance, et de nombreux jeux Atari ont utilisé des timings inhabituels, mais des variations excessives ou irrégulières peuvent provoquer une perte de synchronisation, un déplacement vertical de l'image ou des problèmes avec certains téléviseurs, convertisseurs vidéo et systèmes de capture.

Une structure typique de la boucle principale peut donc être représentée conceptuellement par :

L'initialisation correcte d'une Atari 2600 repose donc sur une règle essentielle : ne jamais dépendre d'un état matériel supposé au démarrage. Après le reset, le programme exécute typiquement SEI et CLD, initialise explicitement SP, efface la RAM $80-$FF, configure les registres nécessaires du TIA et du RIOT, active VBLANK et établit une trame vidéo stable avant d'entrer dans le fonctionnement normal du jeu.

La boucle principale peut ensuite enchaîner VSYNC/VBLANK, minuterie et logique, positionnement horizontal, kernel visible, overscan, traitement des collisions et lecture des entrées. Quelle que soit la quantité de travail demandée par la logique du jeu, le programme doit enfin veiller à conserver un nombre total de lignes constant, afin que les différentes branches du gameplay ne perturbent pas la synchronisation vidéo.

Kernel assembleur minimal

Extrait DASM, 192 lignes de fond :

  1. Frame:
  2.         lda #2
  3.         sta VBLANK
  4.         sta VSYNC
  5.         sta WSYNC
  6.         sta WSYNC
  7.         sta WSYNC
  8.         lda #0
  9.         sta VSYNC
  10.  
  11.         ldx #37
  12. Blank: sta WSYNC
  13.         dex
  14.         bne Blank
  15.  
  16.         lda #0
  17.         sta VBLANK
  18.         ldx #192
  19. Visible:
  20.         sta WSYNC
  21.         dex
  22.         bne Visible
  23.  
  24.         lda #2
  25.         sta VBLANK
  26.         ldx #30
  27. Over:  sta WSYNC
  28.         dex
  29.         bne Over
  30.         jmp Frame

Selon convention de comptage, l'écriture VBLANK/VSYNC et la première WSYNC peuvent changer le total d'une ligne. Mesurer la sortie et ajuster les compteurs; une ROM complète ci-dessous utilise une convention cohérente de build/test.

Positionnement d'un joueur

Routine classique divide-by-15, A contient X logique :

  1. PositionP0:
  2.         sta WSYNC
  3.         sec
  4. .loop: sbc #15
  5.         bcs .loop
  6.         eor #7
  7.         asl
  8.         asl
  9.         asl
  10.         asl
  11.         sta HMP0
  12.         sta RESP0
  13.         sta WSYNC
  14.         sta HMOVE
  15.         rts

L'ordre RESP0/HMP0 et constantes varient avec la convention de coordonnée et le cycle de boucle. Cette routine est un point de départ a calibrer, pas une garantie pixel absolue pour tous objets.

Lecture des entrées et minuterie

  1.         lda SWCHA
  2.         and #%00010000        ; manette de jeux gauche : haut (bit 4), actif bas
  3.         bne NotPressed
  4.         ; direction active bas
  5. NotPressed:
  6.  
  7.         lda INPT4
  8.         bmi FireReleased      ; bit 7=1 relache
  9.         ; feu presse
  10. FireReleased:

Attente de minuterie sans dépasser : charger TIM64T avec budget, effectuer logique, puis lire INTIM. Une boucle BMI/BNE doit tenir compte du passage sous zéro et du fait que la minuterie continue a cadence 1.

Son et collisions en assembleur

Démarrer un son :

  1.         lda #$04             ; mode AUDC a choisir par table/oreille
  2.         sta AUDC0
  3.         lda #$10
  4.         sta AUDF0
  5.         lda #$0F
  6.         sta AUDV0

Collision P0-playfield :

  1.         bit CXP0FB           ; bit 7 selon collision P0-PF
  2.         bpl NoHit
  3.         ; traiter impact
  4. NoHit: lda #0
  5.         sta CXCLR

Couper le canal avec AUDV0=0. AUDC/AUDF ne suivent pas une formule unique de fréquence a cause des diviseurs/polynômes.

ROM complète avec DASM

Squelette 4 Kio :

  1.         processor 6502
  2.         include "vcs.h"
  3.         include "macro.h"
  4.  
  5.         seg Code
  6.         org $F000
  7. Reset:
  8.         sei
  9.         cld
  10.         lda #0
  11.         tax
  12. Clear:  sta $00,x            ; TIA et les 128 octets RIOT via leurs miroirs
  13.         inx
  14.         bne Clear
  15.         ldx #$FF
  16.         txs
  17.         lda #$2E
  18.         sta COLUBK
  19.  
  20. Main:   ; inserer ici une frame de 262 lignes comme chapitre 40
  21.         jmp Main
  22.  
  23.         org $FFFA
  24.         .word Reset           ; NMI non cable
  25.         .word Reset           ; RESET
  26.         .word Reset           ; IRQ/BRK

Commande typique :

dasm demo.asm -f3 -odemo.bin -ldemo.lst -sdemo.sym

Le format -f3 produit le binaire brut. Completer Main avec un kernel stable avant test sur cartouche.

Techniques avancées de kernel

La programmation graphique avancée de l'Atari 2600 repose en grande partie sur l'exploitation extrêmement précise du kernel, c'est-à-dire la portion de code exécutée par le 6507 pendant les lignes visibles de l'image. Le TIA ne possédant ni framebuffer ni système de sprites conventionnel, le programme doit modifier ses registres pendant que le faisceau parcourt l'écran. Les développeurs ont ainsi mis au point de nombreuses techniques permettant de dépasser les capacités qui semblent, à première vue, imposées par le matériel.

Une première technique importante consiste à produire un playfield asymétrique. Normalement, les registres PF0, PF1 et PF2 définissent seulement les 20 bits de la moitié gauche du playfield, tandis que la moitié droite répète ou reflète ces mêmes données selon CTRLPF. Le matériel semble donc limiter naturellement le décor à une structure symétrique ou répétée.

Il est cependant possible de réécrire PF0, PF1 et PF2 pendant la scanline, après que le TIA a généré la première moitié de l'écran mais avant qu'il ne produise les portions correspondantes de la seconde. En fournissant de nouvelles valeurs aux moments appropriés, le programme peut faire apparaître un motif différent dans la moitié droite. Le résultat est un playfield asymétrique pouvant représenter jusqu'à 40 bits logiques différents sur une ligne, au prix d'un timing extrêmement serré.

Cette technique exige de connaître précisément le moment où chaque portion de PF0, PF1 et PF2 est utilisée par le TIA. Une écriture effectuée trop tôt peut modifier une partie de la première moitié, tandis qu'une écriture trop tardive peut laisser apparaître les anciennes données dans la seconde. Les cycles disponibles entre ces écritures déterminent également la quantité de logique que le 6507 peut exécuter pendant le kernel.

Une autre technique célèbre permet de produire des sprites à six chiffres, notamment pour afficher un score. Le TIA ne possède que deux players matériels, mais les modes de copies de NUSIZ, les registres graphiques GRP0/GRP1 et le mécanisme de Vertical Delay (VDEL) permettent de réutiliser plusieurs fois ces générateurs au cours d'une même ligne.

Le programme prépare les motifs représentant les chiffres puis réécrit GRP0 et GRP1 à des cycles soigneusement choisis. Les copies matérielles des players peuvent alors recevoir successivement des motifs différents, donnant l'impression que plusieurs sprites indépendants sont affichés. Les latches internes et le Vertical Delay permettent d'organiser les données entre les deux players et entre des lignes successives. Cette technique est couramment employée pour construire des affichages de score comportant plusieurs chiffres malgré la présence de seulement deux players physiques.

Le repositionnement d'un player en milieu de ligne constitue une autre manière de multiplier les objets apparents. Une écriture dans RESP0 ou RESP1 réinitialise le compteur horizontal du player relativement à la position actuelle du faisceau. Le programme peut donc laisser un player produire une première apparition, puis écrire dans RESPx pendant la même scanline afin de le repositionner et éventuellement de le faire apparaître ailleurs.

Une telle technique doit tenir compte du fonctionnement continu des compteurs internes du TIA. Une écriture dans RESPx n'équivaut pas simplement à charger une coordonnée X : elle agit au moment précis où elle atteint le circuit. Le nombre de cycles séparant les différentes opérations détermine donc directement la position des objets obtenus.

Les changements multiples de couleur pendant une même ligne sont également très utilisés. Les registres COLUP0, COLUP1, COLUPF et COLUBK peuvent être réécrits pendant le balayage. Un objet ou une portion de décor qui ne possède normalement qu'une couleur matérielle peut ainsi présenter plusieurs couleurs horizontalement si le programme modifie le registre au bon moment.

La même idée peut être appliquée verticalement en changeant les couleurs entre les scanlines. En combinant les changements horizontaux et verticaux, les programmeurs peuvent créer des dégradés, des bandes, des zones colorées ou des détails graphiques qui semblent largement dépasser le nombre de couleurs que les registres du TIA permettraient de conserver simultanément.

Les techniques dites « Venetian blinds », ou stores vénitiens, exploitent quant à elles l'alternance de lignes. Au lieu d'afficher tous les éléments graphiques sur chaque scanline, le kernel peut afficher certaines données sur une ligne et d'autres sur la suivante. Visuellement, les informations sont alors entrelacées sous forme de fines bandes horizontales.

Cette alternance permet notamment de réduire la quantité de travail que le 6507 doit accomplir sur chaque scanline. Une partie des objets ou des détails peut être traitée sur les lignes paires et une autre sur les lignes impaires. Selon le motif, le téléviseur, la distance d'observation et la persistance visuelle, l'utilisateur peut percevoir un résultat plus riche que ce qui est réellement produit sur une seule ligne.

Le célèbre HMOVE blank peut lui aussi être intégré volontairement à la conception d'un kernel. Une écriture dans HMOVE effectuée à certains moments, notamment près du début de la ligne, peut provoquer une barre sombre sur le bord gauche de l'image. Certains programmes cherchent à cacher cet artefact dans une bordure ou une zone naturellement sombre du décor.

D'autres kernels peuvent au contraire exploiter volontairement les HMOVE bars comme partie intégrante de leur présentation. Dans les deux cas, la position de l'écriture HMOVE doit être contrôlée précisément, puisque l'apparence de l'effet dépend du cycle et peut également varier selon la révision du TIA.

Le flicker constitue une solution classique au nombre limité d'objets matériels. Le TIA ne possède que cinq objets mobiles fondamentaux - P0, P1, M0, M1 et BL - mais un jeu peut avoir besoin de représenter davantage d'éléments indépendants. Le programme peut alors décider de ne pas afficher tous les objets à chaque frame.

Par exemple, certains objets sont dessinés pendant une frame et d'autres pendant la suivante. Si cette alternance est suffisamment rapide, la persistance de la vision et celle de l'affichage peuvent donner l'impression que tous sont présents simultanément, bien qu'ils clignotent en réalité entre plusieurs frames. Plus le nombre d'objets multiplexés est élevé ou plus la cadence d'affichage de chacun diminue, plus le scintillement devient perceptible.

Les développeurs peuvent améliorer cette technique en choisissant dynamiquement quels objets doivent être affichés. Un objet important, proche du joueur ou impliqué dans une collision peut recevoir une priorité supérieure, tandis que plusieurs objets secondaires se partagent les frames restantes. Le flicker devient ainsi un véritable mécanisme d'ordonnancement des ressources graphiques.

Les kernels complexes utilisent fréquemment des tableaux indexés par ligne. Au lieu d'effectuer des calculs coûteux pendant les 76 cycles processeur disponibles sur une scanline NTSC, le programme prépare à l'avance des tableaux contenant les valeurs dont le kernel aura besoin : motifs graphiques, couleurs, données de playfield, pointeurs ou états d'activation.

Le numéro de ligne, ou un index dérivé de celui-ci, permet alors de récupérer rapidement la donnée correspondante. Cette technique déplace une partie du travail vers le VBLANK ou l'overscan, où le processeur dispose de davantage de liberté. Le kernel visible devient principalement une succession extrêmement optimisée de lectures et d'écritures vers le TIA.

Le code auto-modifié pose en revanche un problème particulier sur l'Atari 2600. La console ne possède que 128 octets de RAM, et cette mémoire doit déjà accueillir les variables et la pile. Il est donc impossible, en pratique, d'y placer une quantité importante de code dynamiquement modifiable comme on pourrait le faire sur un ordinateur disposant de plusieurs kilo-octets de RAM.

Les programmeurs peuvent toutefois obtenir certains avantages similaires en manipulant les opérandes, pointeurs et données utilisés par le kernel. Les pointeurs en page zéro permettent de sélectionner rapidement différentes données graphiques, tandis que les tables placées en ROM peuvent fournir des valeurs précalculées. Sur les cartouches à bank switching, différentes banques peuvent également contenir des variantes de code ou de données adaptées à certaines situations.

L'objectif reste le même que pour du code auto-modifié classique : réduire le nombre d'opérations nécessaires pendant la portion critique du programme. Plutôt que de calculer une valeur dans le kernel, le programme peut préparer auparavant un pointeur ou un index qui permettra de récupérer directement la bonne donnée au moment nécessaire.

Enfin, certains programmes utilisent les opcodes non documentés du 6507. Comme le cour du 6507 dérive directement du 6502 NMOS, plusieurs codes d'opération qui ne figurent pas dans la documentation officielle produisent néanmoins des comportements reproductibles. Certains combinent les effets de plusieurs opérations officielles ou offrent des caractéristiques temporelles utiles.

Dans un kernel où chaque cycle compte, un opcode non documenté stable peut permettre d'effectuer en une séquence compacte une combinaison d'opérations qui demanderait davantage d'instructions officielles. Il peut également être choisi parce que son nombre de cycles et ses accès au bus correspondent exactement au timing recherché.

Cette pratique doit toutefois être utilisée avec prudence. Tous les opcodes non documentés ne possèdent pas le même degré de stabilité, et certains comportements peuvent dépendre de détails électriques ou de la variante du processeur. Un programme destiné au 6507 original peut exploiter les opcodes dont le comportement est suffisamment connu et reproductible, mais un émulateur visant une compatibilité élevée doit reproduire ceux effectivement utilisés par les logiciels.

Les techniques avancées de kernel reposent donc sur une idée commune : transformer le temps processeur et le moment exact des écritures en ressources graphiques. Les réécritures de PF0/PF1/PF2 permettent des playfields asymétriques ; GRP0/GRP1 et le Vertical Delay rendent possibles les affichages à plusieurs chiffres ; RESPx permet de réutiliser un player au cours d'une ligne ; et les changements de registres de couleur multiplient les possibilités visuelles.

À ces méthodes s'ajoutent les Venetian blinds, l'alternance de scanlines, l'utilisation ou la dissimulation des HMOVE bars, le flicker pour représenter plus d'objets que le TIA n'en fournit physiquement, ainsi que les tables indexées par ligne pour alléger le kernel. Malgré l'impossibilité pratique de conserver beaucoup de code auto-modifié dans les 128 octets de RAM, les pointeurs, tables ROM et mécanismes de bank switching permettent d'obtenir une grande partie des optimisations recherchées. Enfin, les opcodes non documentés suffisamment stables peuvent être exploités pour économiser des cycles ou combiner certaines opérations, à condition de connaître précisément leur comportement.

Optimisation et comptage des cycles

Sur l'Atari 2600, l'optimisation du code ne consiste pas seulement à rendre un programme plus rapide ou plus compact. Pendant le kernel visible, le temps d'exécution fait directement partie de la génération de l'image. Le MOS 6507 et le TIA fonctionnent de manière étroitement synchronisée : une écriture dans un registre du TIA quelques cycles trop tôt ou trop tard peut déplacer un sprite, modifier une couleur au mauvais endroit ou déformer une partie du playfield. Le comptage précis des cycles processeur constitue donc une technique fondamentale de programmation de la console.

Lors de la conception d'un kernel, il est utile d'annoter chaque instruction avec son nombre de cycles. Le programmeur peut ainsi additionner progressivement le temps consommé depuis le début de la scanline et déterminer exactement à quel moment chaque accès au TIA sera effectué. Cette méthode permet également d'identifier immédiatement les instructions ou les chemins conditionnels susceptibles de perturber le timing.

Par exemple, une instruction STA zp, c'est-à-dire un stockage utilisant l'adressage page zéro, nécessite normalement 3 cycles processeur. La même opération avec une adresse absolue, STA abs, demande 4 cycles. Dans un kernel où quelques cycles peuvent déterminer la position d'une modification graphique, économiser un seul cycle en utilisant judicieusement la page zéro peut être déterminant.

Une instruction comme LDA #, utilisant l'adressage immédiat, nécessite 2 cycles. De même, DEX, qui décrémente le registre X, demande 2 cycles. Ces durées fixes rendent ces instructions relativement simples à intégrer dans un kernel : une fois leur position déterminée, leur coût temporel reste prévisible.

Les branches conditionnelles demandent davantage d'attention. Une branche qui n'est pas prise consomme normalement 2 cycles, tandis qu'une branche prise en consomme 3 lorsque la destination reste dans la même page mémoire. Une même séquence de code peut donc avoir une durée différente selon le résultat d'une comparaison ou l'état d'un drapeau du processeur.

À cela s'ajoute le problème du franchissement de page (page crossing). Pour une branche prise, si l'adresse de destination se trouve dans une autre page de 256 octets que celle correspondant à la poursuite normale de l'exécution, un cycle supplémentaire est nécessaire. Une branche peut donc coûter 2, 3 ou 4 cycles selon qu'elle n'est pas prise, qu'elle est prise sans franchissement de page ou qu'elle est prise avec franchissement de page.

Certaines instructions de lecture indexée peuvent elles aussi subir un cycle supplémentaire lorsqu'elles franchissent une frontière de page. Le programmeur doit donc connaître non seulement les instructions employées, mais aussi leurs modes d'adressage et les adresses effectivement atteintes pendant l'exécution.

Cette variabilité est particulièrement dangereuse dans le kernel. Une routine qui fonctionne correctement avec une valeur d'index peut soudainement décaler ses écritures au TIA lorsqu'une autre valeur provoque un franchissement de page. Le problème peut n'apparaître que dans certaines situations du jeu, ce qui rend ce type de bogue difficile à identifier sans analyse précise des cycles.

Pour limiter ces risques, il est courant de placer les boucles critiques du kernel à l'intérieur d'une même page mémoire lorsque cela permet d'éviter les pénalités variables liées aux branches. Le placement du code dans la ROM devient alors lui-même un élément de l'optimisation temporelle. Deux versions contenant exactement les mêmes instructions peuvent ne pas présenter exactement le même timing si elles sont placées différemment par rapport à une frontière de page.

Le programmeur doit également examiner tous les chemins possibles d'une branche conditionnelle. Il ne suffit pas de compter les cycles du cas le plus fréquent. Une collision, un compteur arrivé à zéro, la présence d'un sprite ou une autre condition peut faire emprunter un chemin plus long qui n'est exécuté qu'occasionnellement.

Il faut donc systématiquement vérifier le chemin d'exécution le plus long. Si une scanline dispose encore de trois cycles dans le cas normal mais qu'une branche particulière en ajoute quatre, le kernel dépasse son budget même si ce dépassement ne se produit qu'une fois toutes les quelques secondes.

Dans certains kernels, les différents chemins sont volontairement équilibrés en cycles. Si une branche effectue davantage de travail qu'une autre, le chemin plus court peut recevoir des instructions supplémentaires, par exemple des NOP, afin que les deux possibilités rejoignent la suite du kernel au même cycle. Le but n'est alors pas d'exécuter le code aussi rapidement que possible, mais de rendre son temps d'exécution parfaitement prévisible.

Le budget fondamental d'une scanline NTSC est de 76 cycles du 6507, correspondant à 228 color clocks du TIA puisque chaque cycle processeur représente trois color clocks. Cette relation permet de convertir facilement le timing CPU en position approximative du faisceau horizontal.

Le début d'une scanline comprend la période de blanking horizontal. Après une synchronisation par WSYNC, le processeur reprend au début de la ligne et dispose d'environ 22,67 cycles CPU correspondant aux 68 color clocks précédant la portion visible. Les écritures effectuées pendant cette période peuvent donc préparer les registres nécessaires avant que le faisceau atteigne les pixels visibles.

Il faut néanmoins se souvenir que les effets des registres TIA ne correspondent pas toujours naïvement au cycle de l'instruction. Le TIA possède ses propres compteurs et délais internes. Le comptage des cycles doit donc être combiné à une connaissance du moment exact auquel une écriture devient visible pour le registre concerné.

Une bonne méthode de développement consiste à produire un listing assembleur contenant les adresses et les cycles. Le programmeur peut alors voir où chaque instruction est placée en ROM, vérifier les frontières de page et annoter le nombre de cycles consommés depuis un point de synchronisation connu.

Un listing peut par exemple être étudié conceptuellement de cette manière :

  1.         STA WSYNC       ; synchronisation sur la ligne
  2.         LDA #$0E        ; 2 cycles
  3.         STA COLUBK      ; 3 cycles si accès page zéro TIA
  4.         DEX             ; 2 cycles
  5.         BNE Loop        ; 3 cycles si prise, +1 si page franchie

L'objectif n'est pas seulement de connaître le coût individuel des instructions, mais de calculer le cycle cumulé auquel chaque opération importante atteint le bus. Les écritures dans PF0, PF1, PF2, GRP0, GRP1, RESPx, COLUx ou HMOVE peuvent ainsi être positionnées avec précision.

L'émulateur Stella constitue un outil particulièrement utile pour ce travail. Son débogueur permet d'observer l'exécution du 6507, les registres du TIA, les adresses et le timing. Le développeur peut avancer dans le programme, placer des points d'arrêt et comparer le moment réel des écritures avec celui prévu lors du comptage manuel.

Les color clocks constituent une unité particulièrement pratique pour analyser les effets graphiques. Comme le TIA fonctionne à trois fois la fréquence du 6507, une différence d'un seul cycle processeur correspond à trois color clocks. Pour certaines techniques de kernel, cette différence suffit à produire un déplacement clairement visible.

Les outils d'émulation ne doivent toutefois pas être la seule référence lorsqu'une grande fidélité au matériel est recherchée. Une fois le kernel stabilisé dans Stella, il est souhaitable de le tester sur une véritable Atari 2600, notamment avec un téléviseur CRT ou un système de capture approprié.

Cette vérification permet de détecter des comportements qui peuvent être moins évidents dans un émulateur : effets dépendant de la révision du TIA, particularités de synchronisation, HMOVE bars, comportement des couleurs ou tolérance du signal vidéo. Les périphériques modernes de capture peuvent également être plus stricts qu'un CRT face à une trame légèrement irrégulière.

Le dépassement des 76 cycles d'une scanline constitue l'une des erreurs les plus importantes à détecter. Le 6507 ne s'arrête pas automatiquement lorsqu'il atteint la fin de la ligne. Si le code continue à s'exécuter, il poursuit simplement son travail pendant la scanline suivante.

Une écriture TIA prévue vers la fin d'une ligne mais exécutée après le budget disponible se retrouve donc sur la ligne suivante. Toutes les opérations suivantes peuvent à leur tour être décalées, ce qui produit un glissement du timing. Selon la structure du kernel, le résultat peut être une déformation locale, un sprite déplacé, un playfield incorrect ou une désynchronisation de plusieurs lignes.

WSYNC permet de retrouver une frontière de scanline connue, mais il ne corrige pas magiquement un kernel dont les opérations critiques dépassent leur budget avant d'atteindre cette synchronisation. Chaque portion du code visible doit donc être conçue pour terminer ses opérations aux moments prévus.

L'optimisation Atari 2600 doit ainsi être pensée comme une combinaison de vitesse, placement mémoire et déterminisme temporel. Il faut annoter les instructions - par exemple STA zp à 3 cycles, STA abs à 4, LDA # et DEX à 2, ainsi que les branches à 2 cycles lorsqu'elles ne sont pas prises et 3 lorsqu'elles le sont, avec les pénalités supplémentaires de franchissement de page.

Les boucles critiques doivent autant que possible être disposées de manière à éviter les cycles variables, et tous les chemins d'exécution doivent être examinés en portant une attention particulière au chemin le plus long. Un listing contenant les adresses et les cycles, le débogueur de Stella et l'observation en color clocks permettent ensuite de vérifier le résultat.

Enfin, la validation sur une console réelle, un CRT ou un dispositif de capture reste précieuse pour les kernels les plus exigeants. Sur Atari 2600, dépasser les 76 cycles disponibles par scanline ne constitue pas simplement une petite perte de performance : l'exécution continue sur la ligne suivante et décale les écritures TIA, transformant directement une erreur de timing du processeur en erreur visible à l'écran.

Conseils pour un émulateur

La création d'un émulateur Atari 2600 fidèle exige de reproduire non seulement le résultat logique des instructions et des registres, mais aussi une grande partie du comportement temporel du matériel. La console repose sur une interaction extrêmement étroite entre le 6507, le TIA, le RIOT 6532 et l'électronique de la cartouche. De nombreux jeux commerciaux exploitent des comportements qui ne sont visibles qu'au niveau des cycles du bus ou des color clocks. Une émulation trop abstraite peut donc faire fonctionner les programmes simples tout en échouant avec les kernels les plus sophistiqués.

Émulation du 6507

Le MOS 6507 devrait idéalement être émulé avec une précision cycle par cycle. Il ne suffit pas de prendre une instruction, d'en calculer immédiatement le résultat puis d'ajouter son nombre total de cycles à un compteur. Les différents cycles qui composent l'instruction peuvent provoquer de véritables accès au bus et ces accès peuvent avoir des effets sur le TIA, le RIOT ou la cartouche.

Les dummy reads et dummy writes sont particulièrement importants. Certaines instructions du 6502/6507 produisent des lectures ou écritures intermédiaires qui semblent inutiles du point de vue du résultat final de l'instruction. Elles existent néanmoins réellement sur le bus. Si l'adresse concernée correspond à un registre à effet secondaire ou à un hotspot de bank switching, cet accès intermédiaire peut modifier l'état du matériel.

Les franchissements de page (page crossings) doivent eux aussi être reproduits correctement. Certains modes d'adressage indexés nécessitent un cycle supplémentaire lorsque l'adresse effective passe d'une page de 256 octets à une autre. Les branches conditionnelles ont également des durées différentes selon qu'elles sont prises ou non et selon qu'elles franchissent une frontière de page. Ces variations modifient directement le moment auquel les accès suivants atteignent le TIA.

Le mode décimal du 6507 doit être implémenté. Les instructions ADC et SBC ne produisent pas les mêmes résultats lorsque le drapeau D est actif. Même si beaucoup de jeux commencent par exécuter CLD, un émulateur compatible ne doit pas supposer que le mode décimal n'est jamais utilisé.

Les opcodes non documentés doivent également être pris en charge lorsqu'une compatibilité élevée est recherchée. Plusieurs de ces opcodes sont suffisamment stables sur les processeurs NMOS de la famille 6502 pour avoir été utilisés par certains logiciels. Il faut reproduire non seulement leurs résultats sur les registres et les drapeaux, mais aussi leur durée et, lorsque cela importe, leurs accès au bus.

Il faut enfin considérer le résidu du bus de données (data bus residue). Toutes les lectures d'une adresse partiellement ou non pilotée ne produisent pas nécessairement une valeur artificielle fixe comme $00 ou $FF. Selon le circuit et les lignes effectivement pilotées, certains bits peuvent refléter des valeurs précédemment présentes sur le bus. Ce comportement peut être important pour certains registres TIA et pour les tests particulièrement sensibles au matériel.

Émulation du TIA

Le TIA est la partie la plus exigeante de l'émulation Atari 2600. Une stratégie efficace consiste à faire progresser son état de trois color clocks pour chaque cycle du 6507. Cette relation correspond au fonctionnement fondamental de la console : le TIA évolue à la fréquence couleur tandis que le processeur fonctionne à un tiers de cette fréquence.

Les compteurs internes des objets doivent être reproduits. Les players P0 et P1, les missiles M0 et M1 ainsi que la balle BL ne possèdent pas simplement des coordonnées X stockées dans des registres. Leur apparition résulte de compteurs horizontaux et de circuits internes dont l'état est modifié par des opérations comme RESP0, RESP1, RESM0, RESM1 et RESBL.

Les delayed latches sont également indispensables. Les registres VDELP0, VDELP1 et VDELBL, ainsi que les interactions entre GRP0, GRP1 et ENABL, permettent de conserver et de transférer certaines valeurs entre des latches internes. Les kernels qui produisent des sprites multiplexés ou des affichages de score complexes dépendent précisément de ce comportement.

Les opérations RESP et HMOVE doivent être reproduites avec une précision suffisante au niveau du cycle. Le résultat d'une écriture de repositionnement dépend du moment où elle est effectuée relativement au balayage horizontal. De même, l'application de HMOVE et des registres HMP0, HMP1, HMM0, HMM1 et HMBL ne peut pas être réduite à une simple addition abstraite à une coordonnée X.

Cela comprend le fameux HMOVE blank. Sur les TIA classiques, certaines écritures de HMOVE peuvent produire une zone sombre d'environ huit color clocks sur la gauche de la ligne. Des jeux peuvent chercher à masquer cet artefact ou, au contraire, l'exploiter volontairement. L'émulateur doit donc reproduire l'effet en fonction du moment de l'écriture plutôt que l'ajouter systématiquement à chaque HMOVE.

Le playfield doit lui aussi évoluer pendant la ligne. Les écritures dans PF0, PF1 et PF2 peuvent intervenir pendant le balayage et produire un playfield asymétrique. Les modifications de CTRLPF, des couleurs et d'autres registres doivent de la même manière pouvoir influencer uniquement la partie de la scanline qui n'a pas encore été générée.

Les modes de copies et de tailles des players définis par NUSIZ0 et NUSIZ1 nécessitent une représentation correcte des compteurs internes. Une modification de NUSIZ, GRP ou REFP en milieu de ligne peut affecter les copies suivantes sans que les événements précédents soient recalculés.

Les priorités et collisions doivent également être évaluées conformément à l'état des objets au moment où leurs pixels se rencontrent. Les collisions ne doivent pas être déduites uniquement de l'image finale affichée : un objet peut contribuer aux latches de collision même si sa couleur le rend visuellement difficile à distinguer du fond.

Pour toutes ces raisons, un émulateur qui exécute d'abord une scanline entière puis effectue ensuite un rendu global de cette ligne à partir de l'état final des registres sera insuffisant pour les logiciels avancés. Les jeux peuvent modifier plusieurs fois le playfield, les sprites, les couleurs et les positions pendant une seule ligne. Le rendu doit donc conserver une granularité temporelle permettant de reproduire ces changements en milieu de scanline.

Émulation du RIOT 6532

Le RIOT 6532 doit reproduire les 128 octets de RAM et leurs différents miroirs causés par le décodage partiel des adresses. Une adresse miroir ne doit pas créer une nouvelle zone mémoire indépendante : elle doit accéder au même emplacement physique que l'adresse principale correspondante.

Les deux ports parallèles et leurs DDR (Data Direction Registers) doivent être correctement modélisés. Le résultat d'une lecture dépend de la configuration entrée/sortie des différentes lignes, de l'état des périphériques connectés et, selon le cas, des niveaux de sortie programmés.

La minuterie RIOT doit reproduire ses différents préscalers de 1, 8, 64 et 1024 cycles. Après le chargement d'une valeur dans TIM1T, TIM8T, TIM64T ou T1024T, le compteur doit évoluer selon la cadence correspondante.

Le comportement lors de l'underflow est particulièrement important. Lorsque la minuterie passe par zéro, elle adopte une cadence de décrémentation d'un cycle et modifie l'état associé à TIMINT. Les effets précis des lectures et écritures des registres de minuterie doivent suivre le comportement du 6532 plutôt que celui d'un simple compteur logiciel.

Les entrées analogiques, notamment celles utilisées par les paddles, doivent elles aussi évoluer avec le temps émulé. Un paddle ne fournit pas directement une valeur numérique. Le logiciel commande la décharge des condensateurs avec VBLANK, libère celle-ci puis mesure le délai nécessaire avant le changement d'état de INPT0-INPT3. L'émulateur doit donc convertir la position du périphérique en comportement temporel compatible avec cette mesure.

Émulation des cartouches

Le sous-système de cartouche doit recevoir les véritables accès au bus produits par le CPU. Les hotspots des systèmes F8, F6, F4, E0, 3F et autres peuvent réagir à des lectures ou écritures qui n'apparaissent pas forcément comme des opérations importantes dans le code source.

Les dummy accesses du 6507 doivent donc atteindre le mapper. Un accès intermédiaire à $1FF8, par exemple, peut réellement déclencher un changement de banque sur une cartouche dont le circuit répond à cette adresse. Filtrer les accès pour ne transmettre au mapper que les lectures ou écritures considérées comme « utiles » par l'émulateur produirait un comportement différent du matériel.

Les éventuels conflits de bus doivent être reproduits pour les systèmes qui en dépendent. Lorsqu'une cartouche et le processeur participent simultanément à l'état des lignes de données, la valeur effectivement observable peut dépendre de la conception électrique du circuit. Il ne faut pas supposer que toutes les cartouches fonctionnent comme une mémoire idéale sans interaction avec le bus.

Les cartouches comportant de la RAM peuvent également posséder des ports de lecture et d'écriture distincts. L'émulateur doit reproduire cette organisation au lieu de présenter automatiquement une RAM conventionnelle lisible et inscriptible à la même adresse.

Les extensions comme DPC nécessitent leur propre modèle matériel. Les fetchers, compteurs et fonctions supplémentaires du DPC ne peuvent pas être reproduits uniquement par un changement de banque. La même logique s'applique aux architectures modernes telles que DPC+, CDF/CDFJ, BUS, ACE et autres lorsque l'émulateur souhaite les prendre en charge.

Le type de cartouche ne doit surtout pas être déterminé uniquement à partir de la taille de la ROM. Plusieurs mappers peuvent utiliser des fichiers de dimensions identiques. Une bonne stratégie consiste à employer un hash de la ROM et une base de données de cartouches connues, complétés par des heuristiques et une possibilité de sélection manuelle lorsque le fichier n'est pas reconnu.

Vidéo et standards régionaux

L'émulateur doit disposer de profils adaptés aux standards NTSC, PAL et SECAM. Il ne suffit pas de remplacer une palette de couleurs par une autre. Les fréquences, le nombre habituel de scanlines, la cadence verticale et certaines caractéristiques du signal diffèrent selon la région.

Il faut également accepter les trames comportant un nombre irrégulier de lignes. Le TIA ne possède pas de compteur vertical imposant automatiquement 262 lignes en NTSC ou environ 312 en PAL. C'est le programme qui décide quand générer VSYNC. Certains jeux produisent donc des timings non standards, et l'émulateur ne doit pas artificiellement forcer toutes les frames à une longueur fixe.

La reproduction de la phase couleur et de la palette mérite également une attention particulière. Les couleurs du TIA résultent du fonctionnement du circuit et du standard vidéo ; elles ne constituent pas simplement une table RGB abstraite universelle. Des profils de palette peuvent être proposés pour représenter différentes mesures, révisions matérielles ou préférences d'affichage.

Le traitement SECAM doit lui aussi respecter ses particularités, notamment sa palette beaucoup plus limitée, plutôt que d'être considéré comme une simple variation arbitraire des couleurs NTSC.

Émulation audio

L'audio du TIA doit être généré à partir du comportement réel de ses compteurs et de ses LFSR (Linear Feedback Shift Registers). Les différents modes sélectionnés par AUDC0 et AUDC1 utilisent des combinaisons de compteurs et de séquences pseudo-aléatoires ; les deux canaux ne doivent donc pas être modélisés comme de simples oscillateurs carrés.

Les LFSR doivent produire les séquences correctes, avec leurs longueurs et rétroactions appropriées. Les diviseurs AUDF0/AUDF1, les modes AUDC0/AUDC1 et les volumes AUDV0/AUDV1 doivent ensuite être combinés en respectant le timing du TIA.

La fréquence audio dépend également de la fréquence de référence de la console, et donc du standard régional. Une émulation qui utilise exactement les mêmes fréquences internes pour NTSC et PAL risque de produire une hauteur sonore incorrecte.

Le signal final peut ensuite recevoir un filtrage configurable. Cette étape permet de représenter, selon les objectifs de l'émulateur, un signal relativement brut ou une restitution davantage comparable au chemin analogique de la console, au modulateur RF et au téléviseur. Il est préférable que ce filtrage reste configurable plutôt que d'imposer une seule interprétation sonore.

Trace et débogage au niveau des color clocks

Enfin, un émulateur destiné au développement devrait exposer une trace au niveau des color clocks. Le développeur doit pouvoir savoir non seulement quelle instruction est exécutée, mais aussi à quel cycle CPU et à quel color clock une lecture ou une écriture atteint le matériel.

Une telle trace peut indiquer l'adresse du 6507, l'opcode, le cycle interne de l'instruction, le type d'accès au bus, la scanline, le color clock courant et les changements de registres TIA. Elle devient particulièrement précieuse pour diagnostiquer un RESPx décalé, un HMOVE effectué au mauvais moment, une réécriture de playfield trop tardive ou un hotspot déclenché par un dummy read.

L'architecture générale d'un émulateur fidèle peut donc être pensée autour d'une horloge commune : le 6507 avance cycle par cycle, et chaque cycle fait progresser le TIA de trois color clocks, tout en faisant évoluer le RIOT, la minuterie, les entrées et le matériel de cartouche selon les accès réellement produits. Cette approche permet de conserver les interactions temporelles qui caractérisent la machine.

La règle essentielle est finalement de ne pas émuler l'Atari 2600 comme une machine où le CPU calcule une image qui serait ensuite affichée. Le CPU, le TIA, le RIOT et la cartouche évoluent ensemble pendant la construction même du signal. Le respect des dummy reads/writes, des changements en milieu de ligne, des compteurs TIA, des hotspots, de l'underflow RIOT, des entrées analogiques et des profils NTSC/PAL/SECAM est ce qui permet de passer d'un émulateur capable d'exécuter quelques ROM simples à une implémentation réellement compatible avec les techniques utilisées par les jeux Atari 2600 les plus exigeants.

Liste des jeux vidéo

Bien que les jeux Space Invaders, Asteroids et Pac-Man soit très connu sur cette console. La console de jeux d'Atari 2600 propose un nombre impressionnant de jeux. Voici la liste de ceux-ci :

Nom Fabricant Année Description
2 Pak Special - Dolphin, Pigs 'N Wolf Neckermann / HES 1990 Compilation réunissant deux jeux sur une même cartouche, commercialisée dans la gamme économique de compilations HES pour Atari 2600.
2 Pak Special Black - Challenge,Surfing Neckermann / HES 1990 Compilation à cartouche noire regroupant Challenge et Surfing, permettant de proposer deux expériences de jeu sur un seul support.
2 Pak Special Blue - Dungeon Master,Creature Strike Neckermann / HES 1992 Compilation HES réunissant Dungeon Master et Creature Strike, deux jeux distribués ensemble sur une cartouche destinée au marché tardif.
2 Pak Special Dark Blue - Planet Patrol,Wall Defender Neckermann / HES 1990 Compilation à cartouche bleu foncé regroupant Planet Patrol et Wall Defender, deux jeux d'action proposés sur le même support.
2 Pak Special Light Green - Hoppy,Alien Force Neckermann / HES 1992 Compilation tardive réunissant Hoppy et Alien Force, distribuée dans la gamme colorée des cartouches doubles de HES.
2 Pak Special Magenta - CaveBlast,City War Neckermann / HES 1992 Compilation à cartouche magenta comprenant CaveBlast et City War, regroupant deux titres d'action destinés à l'Atari 2600.
2 Pak Special Orange - Space Voyage,Fire Alert Neckermann / HES 1992 Compilation HES à cartouche orange réunissant Space Voyage et Fire Alert afin de proposer deux jeux sur une cartouche.
2 Pak Special Yellow - Star Warrior,Frogger Neckermann / HES 1990 Compilation à cartouche jaune regroupant Star Warrior et Frogger, ce dernier étant une adaptation du célèbre jeu d'arcade.
2-in-1 - Freeway and Tennis Digitel   Compilation non officielle réunissant deux jeux d'Activision : Freeway, consacré à la traversée d'une route, et le jeu sportif Tennis.
2-in-1 - Frostbite and River Raid     Compilation réunissant Frostbite et River Raid, deux célèbres jeux Activision, sur une même image ou cartouche multicartouche.
3-D Tic-Tac-Toe Atari 1980 Adaptation tridimensionnelle du morpion dans laquelle les joueurs placent leurs pièces sur plusieurs grilles afin de réaliser un alignement spatial.
32 in 1 Atari 1988 Multicartouche PAL regroupant trente-deux programmes Atari 2600, principalement des jeux anciens, destinée notamment aux marchés européens et australiens.
4 Pak     Multicartouche connue sous plusieurs variantes de couleur et regroupant quatre programmes Atari 2600 sur un même support de jeu.
9 to 5 20th Century Fox   Jeu inspiré du film 9 to 5, développé pour Atari 2600 mais resté inédit commercialement et connu principalement comme prototype.
Advanced Dungeons and Dragons: Tower of Mystery M-Network 1983, prototype Jeu d'action-RPG où le joueur devait descendre d'une mystérieuse tour, protéger son trésor et survivre aux créatures rencontrées.
Advanced Dungeons and Dragons M-Network 1984, prototype Désignation associée aux projets Advanced Dungeons & Dragons de M Network, dont plusieurs adaptations Atari 2600 ne furent jamais commercialisées.
Adventure Atari 1979 Jeu d'aventure pionnier où le joueur explore des labyrinthes, récupère des objets et affronte des dragons pour retrouver un calice magique.
Adventures of Tron Mattel 1982 Jeu d'action inspiré de Tron dans lequel le joueur dirige Tron sur plusieurs niveaux remplis d'ennemis et de dangers.
Air Raid Men-A-Vision 1982 Rare jeu de tir où le joueur défend une ville contre des vaisseaux extraterrestres en utilisant des canons antiaériens mobiles.
Air Raiders M-Network 1982 Simulation simplifiée de combat aérien où le joueur pilote un avion, attaque ses adversaires et doit surveiller carburant et munitions.
Air-Sea Battle Atari 1977 Collection de jeux de tir proposant plusieurs variantes de combats aériens et navals pour un ou deux joueurs.
Alien 20th Century Fox 1982 Adaptation du film Alien combinant des labyrinthes inspirés de Pac-Man avec des séquences permettant d'échapper aux créatures extraterrestres.
Alpha Beam With Ernie Atari 1983 Jeu éducatif de Sesame Street dans lequel Ernie aide les jeunes joueurs à reconnaître et sélectionner les lettres de l'alphabet.
Amidar Parker Brothers 1982 Adaptation du jeu d'arcade de Konami où le joueur parcourt une grille pour remplir des cases tout en évitant ses ennemis.
Armor Ambush M-Network 1982 Jeu de combat de chars pour deux joueurs proposant des champs de bataille variés, des obstacles et des affrontements tactiques directs.
Artillery Duel/Chuck Norris Superkicks Xonox 1983 Cartouche double Xonox réunissant le jeu stratégique Artillery Duel et le jeu d'action et d'arts martiaux Chuck Norris Superkicks.
Artillery Duel/Ghost Manor Xonox 1983 Cartouche double réunissant les affrontements balistiques d'Artillery Duel et l'aventure horrifique Ghost Manor, tous deux sélectionnables par le joueur.
Artillery Duel/Spike's Peak Xonox 1983 Cartouche double Xonox combinant les duels de canons d'Artillery Duel avec l'escalade et les obstacles de Spike's Peak.
Artillery Duel Xonox 1983 Jeu stratégique opposant deux pièces d'artillerie dont les joueurs règlent l'angle et la puissance afin d'atteindre leur adversaire.
Astérix (PAL) Atari 1983 Adaptation PAL d'un jeu apparenté à Taz, où Astérix se déplace rapidement entre plusieurs rangées pour récupérer des objets.
Asteroids Atari 1981 Adaptation du classique d'arcade où le joueur pilote un vaisseau spatial, détruit des astéroïdes et évite leurs fragments et soucoupes.
Astroblast M-Network 1982 Jeu de tir spatial où le joueur protège la surface d'une planète contre des météorites et divers vaisseaux ennemis.
Astrochase Parker Brothers 1984 Jeu de tir spatial où le joueur défend la Terre contre des vagues de vaisseaux extraterrestres évoluant autour du système solaire.
Atlantis Imagic 1982 Jeu de tir où le joueur défend la cité d'Atlantis contre des vagues de vaisseaux ennemis avant sa destruction.
Backgammon Atari 1979 Adaptation électronique du célèbre jeu de plateau où deux joueurs déplacent leurs pions selon les résultats obtenus avec les dés.
Bachelor Party Mystique 1982 Jeu pour adultes inspiré de Breakout, dans lequel le joueur contrôle une raquette et fait rebondir des personnages à l'écran.
Bank Heist 20th Century Fox 1983 Jeu d'action où le joueur conduit une voiture, dévalise des banques et tente d'échapper aux voitures de police lancées à sa poursuite.
Bank Heist Action Hi-Tech 1983 Édition de Bank Heist distribuée sous le label Action Hi-Tech, conservant les poursuites automobiles et les cambriolages du jeu original.
Bank Heist Action Hi-Tech 1983 Autre entrée ou variante d'édition du même jeu, dans lequel le joueur vole des banques tout en évitant les forces policières.
Barnstorming Activision 1982 Jeu de pilotage où un petit avion traverse des granges, évite oiseaux et obstacles et tente d'obtenir le meilleur temps.
BASIC Programming Atari 1979 Cartouche d'initiation permettant de découvrir certains principes élémentaires de programmation BASIC à l'aide des contrôleurs de la console.
Basic Math Atari 1977 Programme éducatif proposant des exercices simples d'addition, soustraction, multiplication et division afin de pratiquer les opérations arithmétiques élémentaires.
Basketball Atari 1978 Jeu de basketball pour un ou deux joueurs proposant déplacements, interceptions, tirs au panier et affrontements sur un terrain simplifié.
Battlezone Atari 1983 Adaptation du jeu d'arcade où le joueur pilote un char en vue subjective et combat chars et autres véhicules ennemis.
Beamrider Activision 1983 Jeu de tir futuriste où le joueur se déplace sur des faisceaux d'énergie et détruit des vagues successives d'ennemis extraterrestres.
Beat Em and Eat Em Mystique 1982 Jeu pour adultes dans lequel le joueur déplace des personnages au bas de l'écran pour attraper des éléments tombant verticalement.
Berzerk Stern Electronics, Inc. 1982 Adaptation du jeu d'arcade de Stern où le joueur traverse des labyrinthes, détruit des robots et échappe au dangereux Evil Otto.
Big Bird's Egg Catch Atari 1983 Jeu éducatif de Sesame Street où Big Bird doit attraper des oufs tout en exerçant reconnaissance, comptage et coordination.
Blackjack Atari 1977 Adaptation du blackjack permettant de jouer aux cartes contre la banque, avec mises et règles simplifiées adaptées à la console.
BMX Airmaster Atari 1989 Jeu de BMX où le joueur réalise sauts et figures acrobatiques sur différents parcours afin d'accumuler des points et progresser.
Bobby Is Going Home Bit Corporation 1983 Jeu de plates-formes où Bobby doit rentrer chez lui en franchissant différents écrans remplis d'obstacles, d'ennemis et de dangers.
Bowling Atari 1979 Simulation de bowling proposant plusieurs variantes où le joueur positionne son personnage, lance la boule et tente d'abattre les quilles.
Brain Games Activision 1978 Collection de jeux de mémoire et de logique utilisant notamment sons, symboles et séquences afin de tester les capacités du joueur.
Breakout Atari 1978 Adaptation du célèbre jeu Atari où une balle rebondissante doit détruire un mur de briques à l'aide d'une raquette.
Bridge Activision 1980 Adaptation du bridge permettant au joueur de pratiquer ce célèbre jeu de cartes avec enchères et gestion stratégique de sa main.
Bump 'n' Jump M-Network 1983 Jeu de course et d'action où le joueur percute les véhicules adverses ou saute par-dessus obstacles et portions dangereuses.
Bumper Bash Spectravision 1983 Simulation de flipper où le joueur maintient une bille en mouvement et accumule des points en frappant cibles et bumpers.
Burgertime M-Network 1982 Adaptation du jeu d'arcade où le cuisinier Peter Pepper construit des hamburgers tout en évitant aliments ennemis et obstacles.
California Games Epyx 1988 Compilation sportive proposant plusieurs disciplines inspirées de la Californie, notamment BMX, surf, skateboard et autres épreuves décontractées.
Canyon Bomber Atari 1979 Jeu d'action où avions et dirigeables survolent un canyon et larguent des bombes afin de détruire des cibles numérotées.
Carnival Coleco 1982 Adaptation du jeu d'arcade de Sega proposant un stand de tir où le joueur vise animaux, cibles mobiles et objets bonus.
Casino Atari 1978 Compilation de jeux de cartes et de hasard permettant notamment de jouer au blackjack et au poker avec plusieurs participants.
Catch US Games 1982 Jeu simple basé sur la capture d'objets ou de projectiles, publié pour Atari 2600 sous le label US Games.
Centipede Atari 1982 Adaptation du jeu d'arcade où le joueur détruit un mille-pattes descendant parmi des champignons tout en combattant diverses créatures.
Challenge of Nexar Spectravision 1982 Jeu de tir en perspective où le joueur doit éliminer des cibles extraterrestres apparaissant rapidement dans différentes zones de l'écran.
Championship Soccer Atari 1980 Jeu de football proposant des matchs pour un ou deux joueurs, avec déplacements, passes et tirs représentés dans un environnement simplifié.
Chase the Chuck Wagon Spectravision 1983 Jeu promotionnel où le chien Chuckie doit traverser des labyrinthes, récupérer de la nourriture et éviter les dangers pour atteindre son repas.
Checkers Activision 1980 Adaptation du jeu de dames permettant d'affronter l'ordinateur ou un autre joueur en appliquant les déplacements et captures traditionnels.
China Syndrome Spectravision 1983 Jeu d'action où le joueur doit empêcher la fusion de réacteurs nucléaires en intervenant rapidement dans plusieurs installations menacées.
Chopper Command Activision 1982 Jeu de tir où le joueur pilote un hélicoptère, protège des convois terrestres et détruit avions et hélicoptères ennemis.
Chuck Norris Superkicks/Ghost Manor Xonox 1983 Cartouche double Xonox réunissant les combats de Chuck Norris Superkicks et l'aventure surnaturelle de Ghost Manor.
Chuck Norris Superkicks/Spike's Peak Xonox 1983 Cartouche double combinant les arts martiaux de Chuck Norris Superkicks avec l'escalade périlleuse et les obstacles de Spike's Peak.
Chuck Norris Superkicks Xonox 1983 Jeu d'action et d'arts martiaux où Chuck Norris progresse dans différents environnements, affronte des ennemis et perfectionne ses techniques.
Circus Atari Atari 1980 Jeu inspiré de Circus où deux clowns utilisent une balançoire pour éclater des rangées de ballons colorés en hauteur.
Coconuts Telesys 1982 Jeu d'action où le joueur contrôle un personnage devant éviter les noix de coco lancées par un singe depuis un arbre.
Codebreaker Atari 1978 Jeu de réflexion proposant des variantes de décodage où le joueur doit découvrir une combinaison secrète à partir d'indices successifs.
Combat Atari 1977 Jeu emblématique pour deux joueurs proposant de nombreux affrontements entre chars, avions et autres véhicules dans différentes arènes de combat.
Commando Raid VidTec 1982 Jeu de tir où le joueur défend une ville contre des parachutistes et hélicoptères ennemis à l'aide de canons mobiles.
Commando Activision 1988 Adaptation du jeu d'arcade de Capcom où un soldat progresse en territoire ennemi en combattant des vagues de soldats adverses.
Communist Mutants From Space Starpath 1982 Jeu de tir spatial pour Supercharger où le joueur combat des vagues de mutants extraterrestres et protège l'humanité contre leur invasion.
Computer Revenge M-Network 1982 Jeu de stratégie et d'affrontement électronique où le joueur doit déjouer son adversaire dans une compétition contrôlée par ordinateur.
Congo Bongo Sega 1983 Adaptation du jeu d'arcade de Sega où l'explorateur traverse plusieurs environnements en évitant animaux, obstacles et dangers variés.
Concentration Atari 1978 Jeu de mémoire inspiré du principe de Concentration, où les joueurs doivent retrouver des paires cachées et mémoriser leurs positions.
Contenders Activision prototype Prototype Atari 2600 attribué à Activision, connu dans les catalogues de prototypes mais n'ayant pas bénéficié d'une commercialisation normale.
Cookie Monster Munch Atari 1983 Jeu éducatif de Sesame Street où Cookie Monster récupère des biscuits en suivant des chemins afin d'exercer reconnaissance et coordination.
Cosmic Ark Imagic 1982 Suite thématique d'Atlantis où le joueur pilote une arche spatiale, combat des ennemis puis récupère des créatures sur différentes planètes.
Cosmic Commuter Activision 1984 Jeu d'action où le joueur pilote un véhicule spatial, transporte des passagers et évite divers obstacles dans un environnement futuriste.
Cosmic Creeps Telesys 1982 Jeu d'action spatial alternant plusieurs séquences où le joueur protège des personnages et combat des créatures extraterrestres envahissantes.
Crab Control Action Hi-Tech 1983 Jeu d'action où le joueur contrôle un crabe et doit réagir rapidement aux objets et dangers apparaissant dans son environnement.
Crackpots Activision 1983 Jeu d'action où Potsy doit empêcher des insectes de pénétrer dans un immeuble en lançant des pots depuis les fenêtres.
Crash Dive 20th Century Fox 1983 Jeu d'action sous-marin où le joueur pilote un submersible, récupère des trésors et combat différentes menaces dans les profondeurs.
Crazy Climber Atari 1982 Adaptation du jeu d'arcade où le joueur escalade des gratte-ciel tout en évitant obstacles et objets lancés depuis les fenêtres.
Cross Force Spectravision 1982 Jeu de tir où deux vaisseaux placés aux extrémités de l'écran croisent leurs tirs pour éliminer les ennemis apparaissant entre eux.
Crossbow Atari 1987 Adaptation du jeu d'arcade d'Exidy où le joueur protège des aventuriers en détruisant à l'arbalète les dangers sur leur parcours.
Crypts Of Chaos 20th Century Fox 1982 Jeu d'exploration fantastique où le joueur parcourt des labyrinthes en vue subjective, combat des monstres et recherche des trésors cachés.
Crystal Castles Atari 1984 Adaptation du jeu d'arcade où Bentley Bear explore des châteaux isométriques, récupère des gemmes et évite de nombreux ennemis.
Custer's Revenge Mystique 1982 Jeu pour adultes controversé dans lequel le joueur dirige Custer à travers un écran rempli de projectiles pour atteindre sa destination.
Dark Cavern M-Network 1982 Jeu d'action inspiré de Night Stalker où le joueur explore un labyrinthe, récupère des armes et affronte diverses créatures.
Dark Chambers Atari 1988 Jeu d'action et d'exploration où le joueur traverse des donjons, combat des monstres, récupère des objets et recherche les sorties.
Deadly Disks Mattel 1982 Adaptation de Tron: Deadly Discs où Tron lance son disque contre des adversaires tout en esquivant leurs attaques dans une arène.
Death Trap Avalon Hill 1983 Jeu d'action où le joueur évolue dans un environnement dangereux et doit survivre à différentes menaces tout en accumulant des points.
Decathlon Activision 1983 Jeu sportif proposant dix épreuves d'athlétisme, dont courses, sauts et lancers, avec performances mesurées et compétition entre joueurs.
Defender Atari 1982 Adaptation du classique d'arcade de Williams où le joueur pilote un vaisseau et protège des humanoïdes contre les envahisseurs extraterrestres.
Defender II Atari 1988 Adaptation de Stargate, suite de Defender, où le joueur protège des humanoïdes tout en combattant de nombreuses vagues extraterrestres.
Demolition Derby Telesys 1983 Jeu de combat automobile où le joueur percute les véhicules adverses dans une arène tout en essayant de préserver sa voiture.
Demon Attack Imagic 1982 Jeu de tir spatial où le joueur détruit des vagues de démons extraterrestres capables de changer de formation et d'attaquer.
Demons To Diamonds Atari 1982 Jeu de tir où deux joueurs transforment des démons en diamants en visant les créatures correspondant à leur propre couleur.
Desert Falcon Atari 1987 Jeu d'action en perspective où un faucon survole un désert, combat des créatures et collecte des hiéroglyphes aux pouvoirs spéciaux.
Diagnostic Cartridge Atari 1978 Cartouche technique destinée au diagnostic et à la maintenance, permettant de vérifier certaines fonctions matérielles d'une console Atari 2600.
Dig Dug Atari 1983 Adaptation du jeu d'arcade Namco où le joueur creuse des galeries et élimine ses ennemis en les gonflant ou écrasant.
Dishaster Zimag 1983 Jeu d'adresse où le joueur doit maintenir plusieurs assiettes en rotation simultanément et intervenir avant qu'elles ne tombent.
Dodge 'Em Atari 1980 Jeu de conduite dans un labyrinthe routier où le joueur collecte des points tout en évitant une voiture circulant en sens contraire.
Dolphin Activision 1983 Jeu d'action aquatique où un dauphin utilise les sons pour repérer les dangers, éviter les hippocampes et échapper au calmar.
Donkey Kong Coleco 1982 Adaptation du classique de Nintendo où Mario gravit des structures, évite les tonneaux de Donkey Kong et tente de sauver Pauline.
Donkey Kong Jr. Coleco 1983 Adaptation du jeu Nintendo où Donkey Kong Jr. grimpe aux lianes, évite ses ennemis et tente de libérer Donkey Kong.
Double Dragon Activision 1989 Adaptation du célèbre beat'em up où Billy Lee affronte différents adversaires dans les rues afin de délivrer Marian de ses ravisseurs.
Double Dunk Atari 1989 Jeu de basketball en deux contre deux proposant tirs, passes, interceptions et différentes stratégies offensives et défensives sur demi-terrain.
Dragonfire Imagic 1982 Jeu d'action médiéval où le joueur traverse des ponts dangereux puis récupère des trésors dans des salles gardées par des dragons.
Dragonstomper Starpath 1982 Jeu de rôle pour Supercharger où le joueur explore le royaume, améliore son personnage, rassemble des objets et affronte un dragon.
Dragster Activision 1980 Jeu de course où le joueur contrôle accélération et changements de vitesse d'un dragster afin d'obtenir le meilleur temps possible.
E.T. the Extra-Terrestrial Atari 1982 Jeu d'aventure où E.T. explore plusieurs écrans, recherche les pièces d'un téléphone et tente finalement de retourner chez lui.
Earth Dies Screaming 20th Century Fox 1983 Jeu de combat futuriste où le joueur défend la Terre contre des envahisseurs extraterrestres en utilisant différents systèmes d'armement.
Eggomania US Games 1982 Jeu d'adresse où le joueur attrape des oufs lancés par un oiseau avant de pouvoir riposter contre celui-ci.
Enduro Activision 1983 Jeu de course automobile où le pilote dépasse un nombre imposé de véhicules malgré brouillard, neige, nuit et conditions changeantes.
Entombed US Games 1982 Jeu de labyrinthe où un explorateur descend continuellement dans des passages générés, évite les ennemis et utilise des objets pour survivre.
Escape From The Mindmaster Starpath 1982 Jeu pour Supercharger mêlant labyrinthes en pseudo-3D, épreuves de mémoire et réflexion tandis que le joueur cherche à s'échapper.
Espial Tigervision 1983 Jeu de tir à défilement où le joueur pilote un vaisseau, détruit des formations ennemies et évite leurs nombreux projectiles.
F-18 vs. Aliens Action Hi-Tech 1983 Jeu de tir où le joueur pilote un avion F-18 et combat des forces extraterrestres lors d'affrontements aériens rapides.
Fall Guy 20th Century Fox 1983 Jeu d'action inspiré de la série télévisée The Fall Guy, dans lequel le joueur accomplit différentes séquences de cascades.
Fantastic Voyage 20th Century Fox 1982 Jeu inspiré du film où un vaisseau miniaturisé voyage dans un corps humain et détruit les menaces rencontrées dans l'organisme.
Fast Eddie Sirius Software 1982 Jeu de plates-formes où Eddie grimpe entre plusieurs étages, collecte des objets et évite des créatures mobiles pour atteindre son objectif.
Fast Food Telesys 1982 Jeu d'action où le joueur contrôle une bouche affamée devant dévorer différents aliments tout en évitant les dangereux cornichons violets.
Fathom Imagic 1983 Aventure fantastique où le joueur alterne entre dauphin et oiseau afin de récupérer des éléments et délivrer une princesse emprisonnée.
Final Approach Apollo 1982 Jeu de simulation où le joueur agit comme contrôleur aérien et doit guider plusieurs avions vers des atterrissages sans collision.
Fire Fighter Imagic 1982 Jeu d'action où un pompier doit atteindre rapidement un immeuble en flammes, sauver une victime et maîtriser l'incendie avant expiration du temps.
Fireball Starpath 1982 Jeu pour Supercharger inspiré de Breakout, où le joueur contrôle un personnage jonglant avec des boules pour détruire différentes formations.
Fishing Derby Activision 1980 Jeu de pêche pour deux joueurs où chacun tente d'attraper rapidement des poissons tout en évitant le requin qui rôde.
Flag Capture Atari 1978 Jeu de réflexion où les joueurs explorent une grille, interprètent différents indices et tentent de découvrir le drapeau caché avant leur adversaire.
Flash Gordon 20th Century Fox 1983 Jeu d'action où Flash Gordon explore Spider City, détruit des ennemis et des capsules tout en secourant des astronautes prisonniers.
Football Atari 1978 Jeu de football américain pour deux joueurs proposant différentes formations offensives et défensives dans une représentation simplifiée du terrain.
Freeway Activision 1981 Jeu d'action où le joueur dirige une poule devant traverser plusieurs voies de circulation sans être percutée par les véhicules.
Frogger Parker Brothers 1982 Adaptation du classique d'arcade où une grenouille traverse une route encombrée puis une rivière dangereuse afin d'atteindre ses refuges.
Frogger II: Threeedeep! Parker Brothers 1984 Suite de Frogger proposant plusieurs environnements où la grenouille évolue sous l'eau, à la surface puis dans les airs.
Frogger, The Official Starpath 1983 Version améliorée de Frogger pour Supercharger, où la grenouille traverse circulation et rivière pour rejoindre ses refuges en sécurité.
Frogs And Flies M-Network 1982 Jeu pour deux joueurs où des grenouilles sautent afin d'attraper des insectes au vol avant leur adversaire.
Frontline Zellers 1983 Jeu de combat où un soldat progresse en territoire ennemi, utilise différentes armes et affronte fantassins et véhicules adverses.
Frostbite Activision 1983 Jeu d'action où Frostbite Bailey saute sur des blocs de glace flottants pour construire un igloo avant la tombée de la nuit.
Fun With Numbers Atari 1977 Programme éducatif destiné aux jeunes joueurs proposant des exercices simples d'arithmétique et de reconnaissance des nombres avec plusieurs difficultés.
GI Joe: Cobra Strike Parker Brothers 1983 Jeu d'action où les membres de G.I. Joe défendent leur position contre Cobra et son gigantesque serpent robotisé.
Galaxian Namco 1983 Adaptation du jeu d'arcade Namco où le joueur détruit des formations extraterrestres dont certains vaisseaux plongent directement vers lui.
Galaxy Invaders Action Hi-Tech 1983 Jeu de tir spatial inspiré des classiques du genre où le joueur défend sa position contre plusieurs vagues d'envahisseurs extraterrestres.
Gangster Alley Spectravision 1982 Jeu de tir où le joueur surveille les fenêtres d'un immeuble et doit éliminer les gangsters sans atteindre les innocents.
Gangster Ruine Spectravision 1983 Variante ou réédition de Gangster Alley sous un titre différent, conservant le principe consistant à identifier et abattre les gangsters.
Garfield Atari 1984, prototype Jeu inédit mettant en scène Garfield dans une aventure Atari 2600 développée par Atari mais jamais commercialisée à l'époque.
Gas Hog Spectravision 1983 Jeu d'action où le joueur pilote un véhicule, combat des ennemis et doit régulièrement récupérer du carburant pour poursuivre sa mission.
Ghost Manor/Spike's Peak Xonox 1983 Cartouche double Xonox réunissant l'aventure surnaturelle Ghost Manor et le jeu d'escalade et d'obstacles Spike's Peak.
Ghost Manor Xonox 1983 Jeu d'aventure horrifique où le joueur traverse différents environnements surnaturels, combat des créatures et tente de délivrer son compagnon.
Ghostbusters Activision 1985 Adaptation du film où le joueur équipe son équipe de chasseurs de fantômes, capture des spectres et protège New York.
Golf Atari 1980 Jeu de golf proposant un parcours de neuf trous où le joueur sélectionne direction et puissance afin d'obtenir le meilleur score.
Gopher US Games 1982 Jeu d'action où un fermier protège ses carottes contre un gopher creusant des tunnels et surgissant régulièrement dans son potager.
Gorf CBS Electronics 1982 Adaptation du jeu d'arcade proposant plusieurs phases de combat spatial où le joueur affronte différentes formations et vaisseaux extraterrestres.
Grand Prix Activision 1982 Jeu de course automobile à défilement horizontal où le joueur évite ses concurrents et obstacles pour réaliser le meilleur temps.
Gravitar Atari 1983 Adaptation du jeu d'arcade où le joueur pilote un vaisseau soumis à la gravité et accomplit des missions sur différentes planètes.
Gremlins Atari 1984 Jeu inspiré du film où le joueur affronte les Gremlins et tente d'empêcher les Mogwai de se transformer en créatures dangereuses.
Guardian Apollo 1982 Jeu de tir où le joueur protège une planète contre des forces extraterrestres en détruisant les ennemis menaçant sa surface.
Gyruss Parker Brothers 1984 Adaptation du jeu d'arcade Konami où le vaisseau évolue autour d'un tunnel spatial et combat des formations ennemies vers la Terre.
Hero Activision 1984 Jeu d'action où Roderick Hero explore des mines, utilise son hélicoptère dorsal et sa dynamite pour secourir des mineurs prisonniers.
Halloween Wizard Video 1983 Jeu d'horreur inspiré du film où une gardienne d'enfants fuit un meurtrier et tente de protéger les enfants dans une maison.
Hangman Atari 1978 Adaptation électronique du jeu du pendu où les joueurs proposent des lettres afin de découvrir un mot avant épuisement des essais.
Haunted House Atari 1982 Jeu d'aventure où le joueur explore un manoir obscur, recherche les morceaux d'une urne et évite fantômes, chauves-souris et araignées.
Home Run Atari 1978 Jeu de baseball simplifié permettant à deux joueurs de frapper, lancer et déplacer leurs équipes selon plusieurs configurations de terrain.
Human Cannonball Atari 1978 Jeu d'adresse où le joueur règle angle et puissance d'un canon afin de projeter un personnage dans un récipient cible.
Hunt and Score Atari 1978 Jeu de mémoire où les joueurs retournent des cases afin de retrouver des paires correspondantes parmi plusieurs configurations possibles.
Ice Hockey Activision 1981 Jeu de hockey rapide pour deux joueurs où chaque équipe contrôle deux patineurs afin de récupérer le palet et marquer.
Ikari Warriors Atari 1990 Adaptation tardive du jeu d'arcade SNK où le joueur progresse en territoire ennemi, combat des soldats et récupère diverses armes.
Impossible Game Telesys 1983, prototype Jeu de réflexion inédit où le joueur devait trouver le chemin correct à travers plusieurs grilles comportant chacune trente-six cases.
In Search Of The Golden Skull M-Network 1983, prototype Jeu d'aventure inédit où le joueur recherche un crâne d'or en traversant combats aériens, marécages et labyrinthes de pyramides.
Incredible Hulk Parker Brothers 1983, prototype Projet inédit inspiré de Hulk où Bruce Banner devait surmonter différents dangers et se transformer progressivement en puissant géant vert.
Indy 500 Atari 1977 Jeu de course pour deux joueurs utilisant les contrôleurs Driving, avec plusieurs circuits et variantes de compétition automobile.
Infiltrate Apollo 1982 Jeu d'action où un agent secret s'infiltre dans un bâtiment, utilise des ascenseurs et affronte les gardes présents aux étages.
International Soccer M-Network 1982 Jeu de football permettant à deux joueurs de contrôler leurs équipes, effectuer passes et tirs et tenter de marquer des buts.
Intuition Tigervision 1983, prototype Projet éducatif inédit utilisant des formes semi-abstraites afin de stimuler les réactions et d'exercer les capacités mentales du joueur.
James Bond 007 Parker Brothers 1983 Jeu d'action inspiré de plusieurs films de James Bond, combinant conduite, combats, obstacles et missions dans différents environnements.
James Bond As Seen In Octopussy Parker Brothers 1983, prototype Projet James Bond inédit inspiré d'Octopussy, développé séparément du jeu commercial James Bond 007 mais abandonné avant commercialisation.
Jawbreaker Tigervision 1982 Jeu d'action où une bouche parcourt plusieurs rangées, mange des friandises et évite les ennemis dans un principe proche de Pac-Man.
Joust Williams Electronics 1983 Adaptation du jeu d'arcade Williams où des chevaliers montés sur autruches volantes affrontent leurs adversaires en contrôlant soigneusement leur altitude.
Jr. Pac-Man Atari 1986 Adaptation du jeu d'arcade où Jr. Pac-Man parcourt de vastes labyrinthes défilants, mange des points et évite les fantômes.
Jungle Hunt Atari 1983 Adaptation du jeu d'arcade Taito où un explorateur franchit lianes, rivière, rochers et cannibales afin de délivrer une femme captive.
Jungle Raid Starpath 1983, prototype Projet destiné au Supercharger de Starpath, développé pendant les dernières années de l'entreprise mais jamais commercialisé comme jeu régulier.
Kaboom! Activision 1981 Jeu d'adresse où le joueur déplace des seaux pour récupérer les bombes lancées par le Mad Bomber avant leur explosion.
Kangaroo Atari 1983 Adaptation du jeu d'arcade où une mère kangourou grimpe entre plusieurs plates-formes, combat des singes et recherche son petit.
Keystone Kapers Activision 1983 Jeu d'action où l'agent Keystone Kelly poursuit le voleur Harry Hooligan à travers les différents étages d'un grand magasin.
Killer Satellites Starpath 1983 Jeu de tir pour Supercharger où le joueur défend la Terre contre trois satellites extraterrestres en affrontant leurs systèmes de défense.
King Kong Tigervision 1982 Jeu inspiré de Donkey Kong où le joueur escalade un immeuble, évite différents obstacles et tente d'atteindre le sommet.
Klax Atari 1990 Adaptation tardive du jeu d'arcade où le joueur récupère des tuiles colorées et les aligne pour former des combinaisons appelées Klax.
Kool-Aid Man M-Network 1983 Jeu promotionnel où Kool-Aid Man doit protéger une piscine et capturer les Thirsties tout en récupérant différents objets utiles.
Kreuzfeuer Spectravision 1982 Édition allemande de Cross Fire, jeu de tir où le joueur combat des ennemis venant de plusieurs directions dans une arène.
Krull Atari 1983 Jeu d'action inspiré du film Krull, proposant plusieurs séquences où le héros combat des ennemis et tente de sauver Lyssa.
Kung-Fu Master Activision 1987 Adaptation du jeu d'arcade où Thomas traverse plusieurs étages remplis d'adversaires afin de délivrer Sylvia, retenue prisonnière.
Kyphus Apollo 1983, prototype Jeu de labyrinthe inédit développé par Apollo, dans lequel le joueur devait explorer différents passages et affronter des dangers souterrains.
Laser Blast Activision 1981 Jeu de tir où le joueur pilote une soucoupe extraterrestre et détruit des installations terrestres équipées de puissants canons laser.
Laser Gates Imagic 1983 Jeu de tir à défilement horizontal où un vaisseau traverse des cavernes dangereuses remplies d'obstacles, ennemis et barrières laser.
Laser Volley Zellers 1983 Jeu de tir où le joueur défend sa position contre des formations ennemies en utilisant un canon laser mobile.
Lochjaw Apollo 1982 Jeu aquatique où un plongeur récupère des diamants dans un labyrinthe sous-marin tout en évitant un dangereux requin.
Lock 'N Chase M-Network 1982 Adaptation du jeu d'arcade où un voleur dévalise un labyrinthe bancaire, ferme temporairement des passages et échappe aux policiers.
Locomotion M-Network 1983, prototype Adaptation du jeu de réflexion où le joueur déplace des portions de voie ferrée afin de permettre à une locomotive d'atteindre sa destination.
London Blitz Avalon Hill 1983 Jeu de réflexion où le joueur explore Londres pendant les bombardements et doit localiser puis désamorcer des bombes avant leur explosion.
Lord Of The Rings: Journey To Rivendell Parker Brothers 1983, prototype Projet inédit inspiré de Tolkien où Frodon devait voyager vers Rivendell en évitant différents dangers et les redoutables Cavaliers Noirs.
Lost Luggage Apollo 1982 Jeu d'action où un bagagiste doit récupérer les valises tombant de tapis roulants avant qu'elles n'atteignent le sol.
M A S H 20th Century Fox 1983 Jeu inspiré de la série télévisée alternant pilotage d'hélicoptère, sauvetage de blessés et opérations chirurgicales réalisées sous pression.
MAD US Games 1982 Jeu d'action où le joueur défend plusieurs installations terrestres contre des missiles et vaisseaux ennemis menaçant de les détruire.
Mangia Spectravision 1983 Jeu humoristique où un enfant doit faire disparaître la nourriture servie par sa mère sans trop manger ni exploser.
Marauder Tigervision 1982 Jeu de tir où le joueur pilote un véhicule dans une forteresse ennemie, détruit ses défenses et tente d'atteindre son objectif.
Marine Wars Konami 1983 Jeu de combat naval où deux adversaires utilisent différents navires et armes afin de détruire la flotte ennemie.
Mario Bros. Atari 1983 Adaptation du jeu d'arcade Nintendo où Mario et Luigi éliminent des créatures dans les égouts en frappant les plates-formes.
Master Builder Spectravision 1983 Jeu d'action où le joueur construit progressivement un bâtiment en transportant des matériaux tout en évitant différents dangers et obstacles.
Masters of the Universe: Power of He-Man M-Network 1983 Jeu d'action où Musclor pilote son véhicule puis affronte Skeletor dans différentes séquences inspirées de l'univers Masters of the Universe.
Math Gran Prix Atari 1982 Jeu éducatif où les bonnes réponses à des problèmes arithmétiques permettent de faire progresser une voiture dans une course automobile.
Maze Chase US Games 1982 Jeu de labyrinthe où le joueur parcourt différents passages, poursuit ses objectifs et tente d'éviter les adversaires qui le traquent.
Maze Craze: A Game Of Cops And Robbers Atari 1980 Jeu de labyrinthe pour deux joueurs où policiers et voleurs cherchent la sortie dans des dédales proposant de nombreuses variantes.
McDonald's Parker Brothers 1983, prototype Projet promotionnel où Ronald McDonald devait nourrir des extraterrestres affamés et reconstruire les arches, mais le jeu resta inachevé.
Megaforce 20th Century Fox 1982 Jeu d'action inspiré du film où le joueur pilote un véhicule futuriste, détruit les forces ennemies et accomplit différentes missions.
Megamania Activision 1982 Jeu de tir frénétique où le joueur détruit des vagues d'objets insolites, chacune possédant ses propres formations et mouvements.
Meltdown 20th Century Fox 1983, prototype Jeu inédit où le joueur intervient dans un réacteur nucléaire et doit contrôler des particules dangereuses avant qu'elles provoquent une catastrophe.
Midnight Magic Atari 1986 Jeu de flipper inspiré de David's Midnight Magic, proposant bumpers, cibles, rampes et mécanismes permettant d'accumuler un score élevé.
Millipede Atari 1984 Adaptation du jeu d'arcade où le joueur combat mille-pattes, araignées et autres créatures envahissant progressivement un champ de champignons.
Miner 2049er II Tigervision 1983 Suite de Miner 2049er proposant trois nouvelles mines où Bounty Bob doit parcourir les plates-formes et éviter différents dangers.
Miner 2049er Tigervision 1982 Jeu de plates-formes où Bounty Bob explore des mines, parcourt toutes les surfaces et évite les créatures radioactives rencontrées.
Miniature Golf Atari 1979 Jeu de minigolf où les joueurs déterminent direction et puissance de leurs coups afin de franchir différents obstacles avec précision.
Missile Command Atari 1980 Adaptation du classique d'arcade où le joueur utilise des missiles antimissiles pour protéger six villes contre des attaques nucléaires.
Missile Control Video Gems 1983 Jeu de défense inspiré de Missile Command où le joueur intercepte des projectiles ennemis avant qu'ils ne détruisent ses installations.
Missile Intercept US Games 1982 Jeu de défense où le joueur doit intercepter des missiles ennemis et protéger ses positions contre des vagues d'attaques successives.
Mission Survive Video Gems 1983 Jeu d'action où le joueur doit survivre à différentes menaces et progresser en évitant ou détruisant les dangers rencontrés.
Mogul Maniac Amiga 1983 Jeu de ski utilisant le contrôleur Paddle, où le joueur descend une piste de slalom en franchissant correctement les portes.
Montezuma's Revenge: Starring Panama Joe Parker Brothers 1984 Jeu de plates-formes où Panama Joe explore une pyramide, récupère clés et trésors et évite serpents, crânes et nombreux pièges.
Moon Patrol Atari 1984 Adaptation du jeu d'arcade où le joueur conduit un véhicule lunaire, franchit des cratères et détruit différentes menaces extraterrestres.
Moonsweeper Imagic 1983 Jeu de tir spatial où le joueur explore différents systèmes planétaires, combat des ennemis et secourt des mineurs prisonniers.
Moto Rodeo Atari 1990 Jeu de monster trucks où le joueur franchit voitures, rampes et obstacles en contrôlant accélération et position de son véhicule.
Motocross Racer Xonox 1984 Jeu de course motocross proposant différentes perspectives où le joueur franchit obstacles et concurrents pour terminer rapidement chaque parcours.
Mouse Trap Exidy 1982 Adaptation du jeu d'arcade Exidy où une souris parcourt un labyrinthe, récupère du fromage et échappe aux chats poursuivants.
Mr. Do! Windmill Software 1983 Adaptation du jeu d'arcade où Mr. Do creuse des galeries, collecte des cerises et utilise sa balle contre les monstres.
Mr Do's Castle Parker Brothers 1984 Adaptation du jeu d'arcade où Mr. Do parcourt un château, utilise des marteaux et fait tomber des blocs sur ses ennemis.
Mr Postman Bit Corp 1983 Jeu d'action où un facteur doit traverser plusieurs environnements dangereux et franchir différents obstacles afin d'effectuer sa livraison.
Ms. Pac-Man Atari 1983 Adaptation du célèbre jeu d'arcade où Ms. Pac-Man parcourt plusieurs labyrinthes, mange des gommes et évite quatre fantômes.
Mystic Castle M-Network 1983, prototype Projet inédit de M Network mettant en scène une aventure dans un château mystérieux, développé mais jamais commercialisé normalement sur Atari 2600.
Name This Game VidTec 1982 Jeu d'action sous-marin où un plongeur doit repousser requins et autres dangers tout en protégeant son alimentation en air.
Night Driver Atari 1980 Jeu de conduite en vue subjective où le joueur suit une route nocturne matérialisée par des balises et évite les accidents.
Night Stalker Mattel 1982 Jeu de labyrinthe où le joueur récupère des armes et combat robots et créatures tout en évitant leurs attaques.
No Escape! Imagic 1983 Jeu d'action inspiré de la mythologie grecque où le joueur détruit des créatures en faisant tomber des briques depuis le plafond.
Nova Blast Imagic 1983 Jeu de tir où le joueur pilote un vaisseau, protège plusieurs cités et affronte des vagues successives de forces extraterrestres.
Obélix (PAL) Atari 1983 Jeu d'action PAL où Astérix assomme des Romains pendant qu'Obélix récupère leurs casques, avec difficulté progressivement croissante.
Ocean City Defender Zellers    
Off the Wall Atari    
Oink! Activision    
Oscar's Trash Race Atari    
Othello Atari    
Out Of Control Avalon Hill    
Outlaw Atari    
Pac-Man Atari    
Panda Chase Home Vision    
Party Mix Starpath    
Pele's Championship Soccer Atari    
Pele's Soccer Atari    
Pengo Atari 1983  
Pete Rose Baseball Absolute Entertainment    
Phasor Patrol Starpath    
Phoenix Atari 1980  
Picnic US Games    
Pick'n'Pile Salu    
Piece O'Cake US Games    
Pigs In Space Atari    
Piraten-Schiff Spectravision    
Pitfall II: Lost Caverns Activision    
Pitfall! Activision    
Planet Patrol Spectravision    
Planeten Patrouile Spectravision    
Polaris Tigervision    
Pole Position Atari    
Pompeii Apollo    
Pooyan Konami    
Popeye Parker Brothers    
Porky's Parker Brothers    
Pressure Cooker Activision    
Private Eye Activision    
Q*bert's Qubes Parker Brothers    
Q*bert Atari 1982  
Q*bert Parker Brothers    
Quadrun Atari    
Quest for Quintana Roo Telesys    
Quick Step Imagic    
Rabbit Transit Starpath    
Racquet Ball Apollo    
Radar Lock Atari    
Radar Zellers    
Raft Rider US Games    
Raiders of the Lost Ark Atari    
Ram It Telesys    
Rampage Activision    
Reactor Parker Brothers    
Realsports Baseball Atari    
Realsports Boxing Atari    
Realsports Football (PAL) Atari    
Realsports Football Atari    
Realsports Soccer Atari    
Realsports Volleyball Atari    
Revenge Of The Beefsteak Tomatoes 20th Century Fox    
Riddle Of The Sphinx Imagic    
River Patrol Tigervision    
River Raid II Activision    
Road Runner Atari    
Robin Hood/Sir Lancelot - The Joust Xonox    
Robin Hood Xonox    
Robot Commando Raid VidTec    
Robot Tank Activision    
Roc'N Rope Konami, Coleco 1983  
Rubik's Cube Atari    
Scraper Caper Tigervision    
Sea Battle M-Network    
Seaquest Activision    
Secret Quest Atari    
Sentinel Atari    
Shark Attack Apollo    
Shootin' Gallery Imagic    
Shuttle Orbiter Avalon Hill    
Signal Tracing Cartridge Atari    
Sir Lancelot Xonox    
Six Pack 20th Century Fox    
Skate Boardin': A Radical Adventure Absolute Entertainment    
Skeet Shoot Apollo    
Skiing Activision    
Sky Diver Atari    
Sky Jinks Activision    
Sky Lancer Tigervision    
Sky Patrol Imagic    
Sky Skipper Parker Brothers    
Slot Machine Atari    
Slot Racers Atari    
Smurf: Rescue in Gargamel's Castle Coleco 1982  
Sneak 'N Peek VidTec    
Snoopy and The Red Baron Atari    
Solar Storm Imagic    
Solaris Atari    
Sorcerer's Apprentice Atari    
Space Attack M-Network 1982  
Space Cavern Apollo    
Space Chase Apollo    
Space Grid Action Hi-Tech    
Space Invaders Atari 1978  
Space Jockey VidTec    
Space Maze Telesys    
Space Shuttle: A Journey Into Space Activision    
Space War Atari    
Spacemaster X-7 20th Century Fox    
Spider Fighter Activision    
Spider-Man Parker Brothers    
Spike's Peak Xonox    
Springer Tigervision    
Sprintmaster Atari    
Squeeze Box US Games    
Squoosh Apollo    
Stampede Activision    
Star Raiders Atari 1979  
Star Ship Atari    
Star Strike M-Network    
Star Voyager Imagic    
Star Wars: Return of the Jedi - Death Star Battle Parker Brothers    
Star Wars: Return of the Jedi - Ewok Adventure Parker Brothers    
Star Wars: Jedi Arena Parker Brothers    
Star Wars: The Arcade Game Parker Brothers    
Star Wars: The Empire Strikes Back Parker Brothers    
Stargunner Telesys    
Starmaster Activision    
Steeple Chase Video Gems    
Strategy X Konami    
Strawberry Shortcake: Musical Match-ups Parker Brothers    
Street Racer Atari    
Subterranea Imagic    
Suicide Mission Starpath    
Summer Games Epyx Inc 1984  
Super Baseball Atari    
Super Baumeister Spectravision    
Super Breakout Atari    
Super Challenge Baseball M-Network    
Super Cobra Parker Brothers    
Super Crush Tigervision    
Super Football Atari    
Superman Atari    
Surf's Up Amiga    
Surfer's Paradise: But Danger Below! Video Gems    
Surround Atari    
Survival Island Starpath    
Sword Of Saros Starpath    
Swordquest: Earthworld Atari    
Swordquest: Fireworld Atari    
Swordquest: Waterworld Atari    
Tank City Action Hi-Tech    
Tapeworm Spectravision    
Tax Avoiders American Videogame    
Taz Atari    
Tennis Activision    
Texas Chainsaw Massacre Wizard Video    
The Fly US Games    
Threshold Tigervision    
Thrust Jeremy Smith    
Title Match Pro Wrestling Absolute Entertainment    
Tomarc The Barbarian Xonox    
Tomcat: The F-14 Fighter Simulator Absolute Entertainment    
Towering Inferno US Games    
Track and Field Atari    
Treasure Below Video Gems    
Trick Shot Imagic    
Tron Deadly Discs/Adventures Of Tron M-Network    
Tron Deadly Discs M-Network    
Tunnel Runner CBS Electronics    
Turmoil 20th Century Fox    
Turmoil Zellers    
Tutankham Parker Brothers    
Universal Chaos Telegames    
Up n' Down Sega    
Vanguard Atari    
Venture Atari    
Video Checkers Atari    
Video Chess Atari    
Video Cube Atari    
Video Olympics Atari    
Video Pinball Atari    
Wabbit Apollo    
Wall Ball Avalon Hill    
War Zone Action Hi-Tech    
Warlords Atari    
Weird Bird US Games    
Wing War Imagic    
Winter Games Epix    
Wizard of Wor CBS Electronics    
Word Zapper VidTec    
Xenophobe Atari    
Xevious Namco 1982  
Yars' Revenge Atari    
Zaxxon Sega/Coleco 1982  
Zoo Fun Home Vision    

Voir également

Référence global de l'utilisation de la mémoire des consoles de jeux ATARI 2600

Dernière mise à jour : Mercredi, le 6 août 2014