Vous êtes déjà arrivé jusqu'ici?? Wow mes féliciations!! Maintenant ça risque de devenir un peu plus corsé... Fonctionnement de la PPU (Picture Processing Unit): -------------------------------------------------- Petite intro: Comprendre le fonctionnement de la PPU c'est un peu une manière de comprendre comment une console fonctionne, à savoir décharger le maximum de tâches à ses coprocesseurs. Dans le cas présent de toute façon la NES a un fonctionnement extrèmement simple puisque la PPU est le seul véritable coprocesseur. 1. Partage de la VRAM: La NES dispose de 16 ko de mémoire video (et pas 32 ko comme on peut le voir souvent). D'ailleurs n'essayez absolument pas de donner au registre d'adresse de la PPU une adresse sur plus de 14 bits, car les $C000 octets suivants sont juste des miroirs. Cette mémoire est complétement dédiée au fonctionnement de la PPU, et n'est pas directement accessible par le processeur central. Par conséquent, pour pouvoir accéder à cette mémoire, on dispose de toute une batterie de pseudo-registres qui sont en fait utilisés tels des adresses en mémoire centrale. Ces pseudo-registres permettent en fait de piloter la PPU pour l'affichage des sprites, faire des scrollings, régler les couleurs, ect... Voilà comment est partagée la mémoire: $0 -> $FFF : Table des graphismes #0 (Pattern Table) d'une taille de $1000 (4096) $1000 -> $1FFF : Table des graphismes #1 d'une taille de $1000 (4096) -> Remarque: si la rom à un mapper particulier (défini dans l'header), il y a moyen que ces tables de graphismes soient en fait 2 banques de la rom (CHR-ROM), ou plus, ce sera expliqué plus loin avec l'utilisation du Multi-Memory Controller. Quoi qu'il en soit ca n'affectera que les $2000 premiers octets de la VRAM. $2000 -> $23BF : Table de nommage #0 (Name Table) d'une taille de $3C0 (960) $23C0 -> $23FF : Table d'attribution #0 (Attribute Table) d'une taille de $40 (64) $2400 -> $27BF : Table de nommage #1 d'une taille de $3C0 $27C0 -> $27FF : Table d'attribution #1 d'une taille de $40 $2800 -> $2BBF : Table de nommage #2 d'une taille de $3C0 $2BC0 -> $2BFF : Table d'attribution #2 d'une taille de $40 $2C00 -> $2FBF : Table de nommage #3 d'une taille de $3C0 $2FC0 -> $2FFF : Table d'attribution #3 d'une taille de $40 $3000 -> $3EFF : Non utilisé $3F00 -> $3F0F : Palette des images d'une taille de $10 (16) $3F10 -> $3F1F : Palette des sprites d'une taille de $10 $3F20 -> $3FFF : Miroir des palettes d'une taille de $EO (224) Au total $4000 octets soit 16384 octets. Quelques détails au niveau des miroirs utilisés dans la VRAM: - les tables de nommage sont soumis à un effet de miroir, qui sera expliqué plus loin. - même chose pour les palettes. 2. Tables des graphismes: Il s'agit en fait d'abord d'expliquer comment sont stockées les données graphiques en mémoire video. Les données sont organisées en "tiles", de 8x8 pixels. Pour chaque tile les couleurs sont codées sur 2 bits, ainsi pour chaque tile on a besoin de $10 octets (16). On se sert des 8 premiers octets pour code le premier bit de chaque pixel et du second pour le deuxieme bit. Voilà ce que ça donne plus concrètement: Premier bit Deuxième bit Représentation de la tile 1. #00011000b -> $18 9. #10000001b -> $81 2 . . 1 1 . . 2 2. #01000010b -> $42 10. #00000000b -> $00 . 1 . . . . 1 . 3. #10100101b -> $A5 11. #00100100b -> $24 1 . 3 . . 3 . 1 4. #10000001b -> $81 12. #00000000b -> $00 1 . . . . . . 1 5. #01000010b -> $42 13. #00011000b -> $18 . 1 . 2 2 . 1 . 6. #01000010b -> $42 14. #00000000b -> $00 . 1 . . . . 1 . 7. #00111100b -> $3C 15. #00000000b -> $00 . . 1 1 1 1 . . 8. #00000000b -> $00 16. #00011000b -> $18 . . . 2 2 . . . Donc la VRAM contient 2 tables des graphismes pouvant chacune contenir 256 tiles (4096 octets). Soit un total de 512 tiles stockables possibles en mémoire. Pour pouvoir les afficher on va avoir recours aux tables de nommage. 3. Tables de nommage: Les tables de nommage sont en fait des matrices de tiles, et vont constituer la base de l'affichage. La NES a en effet un affichage de 32*30 tiles (soit en pixels 256*240). Donc 32*30=960 soit la mémoire disponible pour une table de nommage. Chaque octet codé dans la table de nommage sera un index vers une tile dans la table des graphismes. Remarque: sur la version NTSC de la NES, les 8 premières et dernières lignes ne sont pas affichées. 4. Tables d'attribution: Le rôle des tables d'attribution est complémentaire à celui des tables de nommage. Comme vu précedemment les couleurs des tiles sont codées sur 2 bits. Mais le système de couleur de la PPU permet un affichage en 16 couleurs, donc codage 4 bits. Pour pouvoir accéder à ces 16 couleurs on a recours aux tables d'attribution. Voilà comment ces tables fonctionnent: -Chaque octet dans la table d'attribution retient les 2 bits de poids fort de chaque couleur dans un groupe 4*4 tiles. Pour un groupe de 4 tiles on a ainsi 2 bits dans un octet de la table d'attribution. On peut controler cette equivalence: taille de la table d'attribution -> 64 octets nombre de tiles stockables dans une table de nommage -> 960 Cela donne shématiquement: octet de la table d'attribution #0 33221100 ||||||+--------- tiles #0 à #3 ||||+----------- tiles #4 à #7 ||+------------- tiles #8 à #B +--------------- tiles #C à #F Remarque: le numero de tile #? ne correspond pas à l'index de la tile dans la table de nommage car en réalité les couleurs ne sont pas attribuées séquentiellement mais par blocs de 4 tiles. Complété avec les 2 bits de poids faibles situés dans la table des graphismes on obtient la couleur de la palette qui sera alors affichée à l'écran. 5. Fonctionnement des palettes: La PPU utilise 2 palettes : -la palette des images déstinées à l'affichage des objets statiques d'arrière plan. -la palette des sprites déstinée comme son nom l'indique à l'affichage des sprites. Chacune des palette à une taille de 16 octets. Il ne s'agit pas de réelles valeurs RGB mais une référence à une palette interne d'une variété de 52 couleurs possibles. D'autre part les palettes ne sont pas réellement de 16 octets, car les adresses $3F00, $3F04, $3F08 ect... tous les 4 octets renvois en fait à la même couleur! Une couleurs très particulière car c'est elle qui va définir la transparence. Ce qui limite la palette au nombre de couleurs effectif de 13 couleurs Comme on dispose de 13 couleurs en réalité on peut en afficher jusqu'a 25 (une couleur étant commune aux 2 palettes) à l'ecran en même temps (fond d'écran + sprites) Pour chaque octet de la palette voilà comment proceder: uullhhhh |||||||| ||||+----- 4 premiers bits -> défini la valeur chromatique de la couleur: |||| -> attention cette valeur doit être comprise entre 1 et C (12) (sinon -> couleur noire) |||| -> mettre 0 permet d'afficher une nuance de gris selon la luminance choisie ||+------- ces 2 bits définissent la luminance, niveau de luminosité choisi +--------- ces derniers bits ne sont pas utilisés Voilà les codes correspondant aux couleurs pour les valeur chromatiques: $00 = gris $05 = rose $0A = vert $01 = bleu clair $06 = rouge $0B = vert foncé $02 = bleu cyan $07 = ocre $0C = bleu ardoise $03 = bleu foncé $08 = marron $04 = violet $09 = vert clair Les niveaux de luminosité peuvent semblibement faire varier ces couleurs Il existe un très bon tableau pour voir chaque couleur associée à chaque code sur ce site: http://www.sfu.ca/~ccovell/NESTechFAQ.html 6. Fonctionnement des miroirs: En réalité la VRAM de la NES dispose d'assez de mémoire pour seulement 2 tables de nommage et 2 tables d'attribution. Il en résulte que 2 tables sont en fait des "miroirs" des 2 autres tables. Chaque entrée dans une table est automatiquement alors faites dans l'autre. On dispose de 3 modes pour les miroirs (ils sont définis dans l'header pour une rom) : -le premier mode désactive simplement cet effet, et autorise l'utilisation d'une seule table de nommage (et d'attribution). -le second mode, le mode "horizontal", permet d'utiliser les tables #0 et #2, la table #1 est un miroir de la table #0 et la table #3 est un miroir de la table #2. -le dernier mode, le mode "vertical", permet d'utiliser les tables #0 et #1, la table #2 est un miroir de la table #0 et la table #3 est un miroir de la table #1. Les palettes aussi disposent de miroirs, et chaque intervalle à partir de l'adresse $3F20 jusqu'a $3FFF sont des miroirs respectifs des 2 palettes tous les $10 octets (16). 7. Scrolling de l'arrière plan: Comme toute bonne console la NES dispose d'une fonction de scrolling cablée en hardware, ce qui permet un scrolling irréprochable, contrairement à ce que l'on peut obtenir sur un PC. Etant donnée qu'une table de nommage équivaut à la taille d'un ecran, il va falloir utiliser la deuxieme table de nommage à notre disposition. On peut faire scroller l'arrière plan sur 2 axes seulement: horizontalement et verticalement. La résolution est de toute facon de 256*240. Voila shématiquement ce que l'on obtient: Horizontal: Vertical: 0 512 -> 256*2 +---------+0 +---------+---------+ | | | | | | A | | A | B | | | | | | +---------+ | | | | | +---------+---------+ | B | | | +---------+480 -> 240*2 Pour définir quelle table de nommage on utilise pour "A" on a recours aux bits 0 et 1 du registre $2000 qui sera expliqué plus loin. Logiquement "B" sera l'autre table de nommage. Le type de miroir utilisé est défini dans l'header: Horizontal: Table #0 -> Miroir Table #1 Table #2 -> Miroir Table #3 Vertical: Table #0 -> Miroir Table #2 Table #1 -> Miroir Table #3 Le shéma pour le scrolling des tables est le suivant: Table de nommage #2 Table de nommage #3 Table de nommage #0 Table de nommage #1 L'utilisation du scrolling s'effectue par l'écriture successive de valeurs 8 bits dans le registre $2005. La première valeur ecrite correspond au scrolling vertical, par conséquent elle doit être impérativement inférieure ou égale 239 (du fait de la limitation de la résolution de la NES). La seconde valeur écrite juste après correspond au scrolling horizontal. 8. Fonctionnement des sprites: La PPU dispose de capacités particulières pour la gestion des sprites. Cela va permettre de simplifier l'affichage de sprites à l'écran puisqu'ils seront complètement independant de l'arrière plan sur lequel ils vont apparaitre. Il y a une mémoire incluse permettant de stocker les informations de 64 sprites, qui peuvent être d'une taille de 8*8 ou 8*16 pixels (8->largeur, 16->longueur). Les données des sprites sont contenues dans la table des graphismes. Pour permettre le stockage de ces informations la NES dispose d'un espace mémoire complétement indépendant de la VRAM et de la mémoire centrale. Cette mémoire est de 256 octets, à raison de 4 octets par sprite (donc capacité pour 64). ----------------------------------------------------- Pour un sprite: -Octet 0 -> Coordonnée Y du point où le sprite se trouve, c'est à dire l'ordonnée. Ce point est le point du coin haut-gauche du sprite: point ->+-------+ | | | | | | +-------+ -Octet 1 -> Il s'agit de l'index dans la table des graphismes des données du sprite. S'utilise de la même façon que la table de nommage. Attention les sprites en 8*16 ont une particularité -> voir plus bas. -Octet 2 -> Cet octet contient une série de flags qu'il convient d'expliciter: vhp000cc -> octet divisé par flags flag v: Flip Vertical -> mis à 1 active le flip vertical (mouvement rapide), sinon désactivé. flag h: Flip Horizontal -> même chose horizontalement. flag p: Priorité d'arrière-plan -> mis à 1 si le sprite doit être caché derrière, sinon laisser à 0. flag 0: Inutilisé flag c: Les 2 bits de poids forts de la couleur, car on ne dispose pas ici des tables d'attributions... Ce flag fonctionne comme les tables d'attribution (voir précédemment). -Octet 3 -> Coordonnée X du point où le sprite se trouve, c'est à dire l'abcisse. ----------------------------------------------------- L'utilisation des sprites d'une taille de 8*16 est différente car leur taille fait le double d'une tile normale (8*8). Par conséquent l'adressage par le biais de l'octet d'index dans une valeur de sprite sera interprété différement et on accedera ainsi soit à la première table des graphismes (Pattern #0 à l'adresse en VRAM $0000) ou bien à la seconde table des graphismes (Pattern #1 à l'adresse en VRAM $1000). On peut uniquement afficher 8 sprites à la fois sur une "ligne" (scanline), c'est à dire si les sprites ont les mêmes coordonnées en abcisse(X). Techniquement la PPU peut cependant difficilement gérer plus de sprites que cela. 9. VBlank et HBlank: VBlank est pour Vertical Blanking, et HBlank pour Horizontal Blanking. Le blanking c'est le scintillement emis par chaque projection de pixels par le cannon à electrons sur l'écran. La PPU a comme chaque périphérique d'affichage de console un taux de rafraichissement. Ce taux est de 60 hertz (60x/secondes) pour une NES NTSC et 50 hertz pour une PAL. Shématiquement le cannon à élecron va ecrire sur la TV les lignes de gauche à droite et une fois arrivé au bout de la ligne il doit revenir à gauche pour ecrire la ligne suivante -> cette période est appelée HBlank. Une fois tout l'écran complété il doit revenir tout en haut de l'écran pour procéder à l'affichage de l'écran suivant. Cette période est appelé VBlank. On peut voir si la PPU est en période de VBlank en controlant le bit #7 du registre $2002(il est alors à 1), tout comme on peut mettre celui ci à zero. Celui ci est remis à 0 à la fin du VBlank. Le bit #6 du registre $2002 est appelé Hit Flag, et sert a controler à l'affichage d'un sprite quand est-ce que l'on va ecrire la première ligne du sprite (il sera alors mis à 1). Si la coordonnée en ordonnée (Y) du sprite est 12, ce flag sera mis à 1 lors de l'écriture de la ligne 12. Il est automatiquement remis à 0 lors du VBlank. On utilise souvent le contrôle du VBlank comme un timer, à défaut d'y avoir une quelquonque horloge sur la NES. Fonctionnement des autres peripheriques: --------------------------------------- 1. Pseudo-Audio Processing Unit: Comme son nom l'indique la Pseudo-Audio Processing Unit (pAPU) permet à la NES de sortir du son. Le "Pseudo" vient du fait qu'en réalité il n'existe pas de véritable composant indépendant ayant en charge le traitement du son (à la différence de la SUPERNES et de son SPC700). Le son est en fait traité directement par le processeur et une série de composants de base, les synthèses étant transmises par des registres commandés directement depuis le processeur. La NES dispose de 4 voix de synthèses FM et une voix pour le son digital (PCM). Parmi les voies de synthèses, on compte 2 voix de rythme (pulse), une voix de triangle, et un canal de bruit. Les voix de rythme ont la possibilité de produire du son dans les fréquences de 54,6 Hz à 12,4 Khz. Il y a possibilité de régler la fréquence de sifflement et la durée du cycle. La voix de triangle peut produire du son entre les fréquences 27.3 Hz et 55.9 Khz dans une résolution de 4-bits (donc 16 tonalitées différentes). Ce canal dispose d'un compteur linéaire ce qui permet de le désactiver après une certaine période d'utilisation. Le canal de bruit lui a la faculté de produire des sons à des fréquences aléatoires entre 29.3 Hz et 447 KHz. On peut l'utiliser pour produire des nombres aléatoires en sortie du canal (utile pour les jeux). Fonctionnement du canal digital (DMC): Le canal DMC répond à un fonctionnement bien particulier en comparaison des autres canaux sonores de la pAPU. 3. Joypads Je ne m'interesserais ici qu'au fonctionnement des 2 joypads de base. Les autres périphériques pourraient éventuellement être interessants au cas où vous travaillerez sur un émulateur, auquel cas le contenu de cette documentation se révelerait bien insuffisante. La NES supporte donc 2 joypads à la fois (ou un joypad et le Zapper, le pistolet à laser). Ces 2 joypads correspondent respectivement aux registres $4016 et $4017. Le fonctionnement des joypads est particulier en comparaison des autres registres de périphérique. Pour pouvoir utiliser ces registres ont doit d'abord les remettre à l'état initial en écrivant successivement 1 puis 0 dans le registre adéquate. Ensuite on doit réalisé le nombre de lecture nécessaire pour accéder à la touche qui nous interesse de vérifier qu'elle a été préssé. Nombre de lectures défini comme suit: Pour chaque registre (soit $4016 ou $4017): $4016: Joypad #1 Joypad #3 (*) 1 -> A 9 -> A 17 ->----+ 2 -> B 10 -> B 18 -> |___ ces 4 valeurs sont utilisées pour 3 -> SELECT 11 -> SELECT 19 -> | définir quel manette est connectée ("signature") 4 -> START 12 -> START 20 ->----+ 5 -> HAUT 13 -> HAUT 21 -> 0 6 -> BAS 14 -> BAS 22 -> 0 7 -> GAUCHE 15 -> GAUCHE 23 -> 0 8 -> DROITE 16 -> DROITE 24 -> 0 * -> le joypad #3 est ici uniquement possible si on utilise l'adaptateur 4 manettes. Pareil pour le registre $4017, le Joypad #1 équivaut au Joypad #2 " " " " , le Joypad #3 équivaut au Joypad #4 Si on utilise pas l'adaptateur 4 manettes les valeurs 9 à 16 sont inutiles. Imaginons que nous devions voir si la touche SELECT a été préssée sur le joypad #2, on aura alors le code assembleur suivant: LDA #$01 --| STA $4017 |---> remise à 0 du port #2 LDA #$00 --| STA $4017 LDA $4017 --| LDA $4017 |---> 3 lectures pour SELECT LDA $4017 --| AND #$01 ------> on ne récupère que le bit #0 (voir details dans "Détails sur les registres de periphériques") Si la valeur contenue dans l'accumulateur est à 1 alors la touche SELECT a été cochée. Signature: On peut vérifier en effectuant un certain nombre de lecture (17) le nombre de joypads connectés. Ainsi on procède comme suit: %0000 -> aucun joypad ou périphérique connecté %0001 -> joypad connecté au port #1 ($4016) #0010 -> joypad connecté au port #2 ($4017) Remarque: les 4 bits correspondent aux valeurs de 17 à 20. On obtient cette valeur après 17 lectures dans le registre $4016 ou $4017 C'est a peu près tout ce qu'il faut savoir pour les joypads. D'autres types de périphériques d'entrée ont un fonctionnement légèrement différent cependant ils utilisent quoi qu'il arrive les ports $4016 et $4017. Détails sur les registres de periphériques: ------------------------------------------ On a pas directement accès aux périphériques comme la PPU ou bien les joypads, il faut trouver en moyen à partir de la programmation du processeur central pour piloter ces périphériques. Pour cela on utilise une série de pseudo-registres qui sont en fait des adresses mémoire. Ces adresses sont données en 16 bits. Selon le périphérique on va devoir ecrire (cas de la PPU) ou bien lire (généralement pour la pAPU). On va aussi devoir parfois proceder a une ecriture successive de 2 valeurs. 1. Registres PPU: +--------------------------------------------------------------------------------------------------------+ |Registre $2000: Registre de contrôle de la PPU #1 (Ecriture seulement) | masque: vmsbpiNN | | v = Mis à 1 ce bit éxecute automatique une routine NMI (Non Maskable Interrupt) lors d'un VBlank | Rappel: l'adresse de cette routine se trouve en $FFFA dans la PRG-ROM. | | m = Selection PPU | Positionné à Maitre à 0, Esclave à 1. (inutilisé apparament) | | s = Taille des sprites | Si le bit est à 0 -> sprites en 8x8 (taille standard d'une tile) | " " " -> sprites en 8x16 | | b = Adresse de la table des graphismes utilisée pour l'arrière plan en VRAM | 0 = $0000 | 1 = $1000 | | p = Adresse de la table des graphismes utilisée pour les sprites en VRAM | 0 = $0000 | 1 = $1000 | | i = Incrémentation d'adresse PPU | 0 = incrémentation de 1 | 1 = incrémentation de 32 | | NN = Adresse de la table de nommage (Name Table) utilisée en VRAM | 00 = $2000 | 01 = $2400 | 10 = $2800 | 11 = $2C00 +--------------------------------------------------------------------------------------------------------+ |Registre $2001: Registre de contrôle de la PPU (Ecriture seulement) | masque: fffvVsbt | | fff = Couleur de teint utilisée par defaut ("Colour Emphasis") | 000 = Noir (pas de teint) | 001 = Rouge | 010 = Bleu | 100 = Vert | Remarque: ce n'est pas encore correctement émulé sur certains émulateurs. | | v = Visibilité des sprites (mettre à 1 par défaut pour pouvoir afficher des sprites) | | V = Visibilité de l'arrière plan (mettre à 1 par défaut pour pouvoir afficher l'arrière plan) | | s = Clipping des sprites | 0 = les sprites ne sont pas affichés sur les 8 pixels gauches | 1 = pas de clipping | | b = Clipping de l'arrière-plan | 0 = l'arrière-plan n'est pas affiché sur les 8 pixels gauches | 1 = pas de clipping | | t = Type d'affichage | 0 = affichage couleur | 1 = affichage noir et blanc +--------------------------------------------------------------------------------------------------------+ |Registre $2002: Registre de Status de la PPU (Lecture seulement) | masque: vhsw---- (-> 4 bits de poids faibles non utilisés) | | v = Période de VBlank | Ce bit est mis à 1 durant une période de VBlank. | Il est mis à 0 dès qu'on fait une lecture de ce registre pour | éviter de reéxecuter une routine dans un même VBlank. | Remarque: très utile pour s'en servir comme timer. | | h = Occurence Sprite #0 | Ce bit est mis à 1 dès que le VBlank à atteint la position | du sprite #0 (du moins le premier pixel non transparent). | Il est mis à 0 après chaque VBlank. | Voir l'excellente FAQ de Chris Covell pour en savoir d'avantage. | | s = Compteur de sprites par ligne | 0 = 8 ou moins de 8 sprites par ligne | 1 = plus de 8 sprites par ligne | | w = Flag d'ecriture en VRAM | Ce bit mis à 1 indique que la PPU ignore les ecritures faites en VRAM. | | Remarque: Utilisez ce registre SYSTEMATIQUEMENT pour ecrire des données en VRAM. | Il n'est pas conseillé d'ecrire des données hors d'un VBlank durant l'execution. +--------------------------------------------------------------------------------------------------------+ |Registre $2003: Registre d'adresse en SPR-RAM (Ecriture seulement) | | Permet d'écrire l'adresse 8 bits utilisée par le registre $2004 | pour accéder à une case mémoire en SPR-RAM. +--------------------------------------------------------------------------------------------------------+ |Registre $2004: Registre d'entrée-sortie en SPR-RAM (Lecture-Ecriture) | | Permet d'écrire ou de lire la case d'adresse définie par le registre $2003. +--------------------------------------------------------------------------------------------------------+ |Registre $2005: Registre de Scrolling d'arrière-plan (Ecriture seulement) | | L'utilisation de ce registre se fait par 2 écritures successives. | | 1ère valeur: Scrolling horizontal | 2ème valeur: Scrolling vertical | Voir la partie sur les scrollings pour plus d'informations +--------------------------------------------------------------------------------------------------------+ |Registre $2006: Registre d'adresse VRAM (Ecriture seulement) | | Il s'agit de l'adresse en VRAM que va utiliser le registre $2007. | L'écriture dans ce registre doit s'effectuer par 2 écritures successives | étant donné la limitation des bus à 8 bits encore une fois. | | 1ère valeur: 8 bits de poids fort de l'adresse | 2ème valeur: 8 bits de poids faible de l'adresse | | REMARQUE: Attention à l'incrémentation d'adresse PPU! A chaque ecriture dans le registre | $2007, l'adresse du registre $2006 est incrémentée soit de 1 ou de 32 (selon le registre PPU #1) +--------------------------------------------------------------------------------------------------------+ |Registre $2007: Registre d'entrée-sortie en VRAM (Lecture-Ecriture) | | C'est le registre d'accès à la PPU le plus utilisé. | C'est par celui ce que vont passer toutes les ecritures pour les tables des graphismes, | de nommage ou d'attribution, bref toutes les données contenues en VRAM. | Il s'utilise de paire avec le registre $2006 qui défini l'offset en VRAM. +--------------------------------------------------------------------------------------------------------+ Gardez à l'esprit que tous les transferts sont efféctués en 8 bits, ce qui inclue de nombreuses boucles si vous avez des données conséquentes à copier. Ainsi pour la SPR-RAM une autre solution gagne en facilité, expliqué juste en dessous. Registre spécial: Pour accéder à la SPR-RAM on dispose d'une autre alternative qui facilite grandement les transferts car il permet de copier un bloc fixe de 256 octets de données qu'on aurait par exemple stocké en RAM ou en PRG-ROM. Il s'agit d'un registre DMA (Direct Memory Access) semblabe à ceux utilisés pour le son digital. Ce registre est à l'adresse $4014. La formule est simple: $100 * (opérande stockée dans le registre $4014) = adresse de début du bloc de 256 octets ($100) Il est conseillé d'utiliser ce registre pour améliorer les performances du programme. 2. Registres pAPU +--------------------------------------------------------------------------------------------------------+ |Registre $4000: Registre de contrôle #1 de la voix de rythme #1 (Ecriture seulement) | | masque:%ccvessss | ||||+---- Taux d'echantillonage en lecture | |||+----- Selection d'envellope (0 = variée, 1 = fixée) | ||+------ Vague maintenue: répétion automatique (0 = non, 1 = oui) | +-------- Accomplissement du cycle | 00 = 87,5 % | 00 = 75,0 % | 00 = 50,0 % | 00 = 25,0 % +--------------------------------------------------------------------------------------------------------+ |Registre $4001: Registre de contrôle #2 de la voix de rythme #1 (Ecriture seulement) | | masque:%fsssmrrr | |||||+--- étalement de la fréquence: 0 est le minimum, 7 le maximum | ||||| Remarque: voir selon bit #3 pour basse ou haute fréquence | ||||+---- méthode de fréquence (0 -> de haut vers bas, 1-> de bas vers haut) | |+------- vitesse de changement de fréquence | +-------- fréquence de sifflement | 0 -> les bits #0 à #6 sont ignorés | 1 -> les bits #0 à #6 sont pris en compte | +--------------------------------------------------------------------------------------------------------+ |Registre $4002: Registre de fréquence #1 de la voix de rythme #1 (Ecriture seulement) | | Note: la fréquence des voix de rythme sont étalées sur 11 bits. | Ce registre permet de déterminer la valeur des 8 bits de | poids faible de la fréquence. | | masque:%ffffffff -> 8 bits de poids faible de la fréquence | +--------------------------------------------------------------------------------------------------------+ |Registre $4003: Registre de fréquence #2 de la voix de rythme #1 (Ecriture seulement) | | masque: %tttttfff | |||||+--- les 3 bits de poids fort de la fréquence de la voix de rythme #2 | +-------- temps d'activité de la voix +--------------------------------------------------------------------------------------------------------+ |Registre $4004: Registre de contrôle #1 de la voix de rythme #2 (Ecriture seulement) | | Son fonctionnement est identique à la voix de rythme #1 (voir registre $4000) | +--------------------------------------------------------------------------------------------------------+ |Registre $4005: Registre de contrôle #2 de la voix de rythme #2 (Ecriture seulement) | | Son fonctionnement est identique à la voix de rythme #1 (voir registre $4001) | +--------------------------------------------------------------------------------------------------------+ |Registre $4006: Registre de fréquence #1 de la voix de rythme #2 (Ecriture seulement) | | Son fonctionnement est identique à la voix de rythme #1 (voir registre $4002) | +--------------------------------------------------------------------------------------------------------+ |Registre $4007: Registre de fréquence #2 de la voix de rythme #2 (Ecriture seulement) | | Son fonctionnement est identique à la voix de rythme #1 (voir registre $4003) | +--------------------------------------------------------------------------------------------------------+ |Registre $4008: Registre de contrôle #1 de la voix de triangle (Ecriture seulement) | | masque:%vccccccc | |+------- compteur linéaire de chargement du registre (voir précision précedemment) | +-------- 0 = longueur d'horloge désactivé | 1 = démarrage linéaire du compteur +--------------------------------------------------------------------------------------------------------+ |Registre $4009: Registre de contrôle #2 de la voix de triangle (Ecriture seulement) | | Ce registre n'est pas utilisé. | +--------------------------------------------------------------------------------------------------------+ |Registre $400A: Registre de fréquence #1 de la voix de triangle (Ecriture seulement) | | Note: la fréquence de la voix de triangle s'étale sur 11 bits. | Ce registre permet de déterminer la valeur des 8 bits de | poids faible de la fréquence. | | masque:%ffffffff -> 8 bits de poids faible de la fréquence | +--------------------------------------------------------------------------------------------------------+ |Registre $400B: Registre de fréquence #2 de la voix de triangle (Ecriture seulement) | | masque: %tttttfff | |||||+--- les 3 bits de poids fort de la fréquence de la voix de triangle | +-------- temps d'activité de la voix +--------------------------------------------------------------------------------------------------------+ |Registre $400C: Registre de contrôle #1 du canal de bruit (Ecriture seulement) | | masque:%00vessss | ||||+---- Taux d'echantillonage en lecture | |||+----- Selection d'envellope (0 = variée, 1 = fixée) | ||+------ Vague maintenue: répétion automatique (0 = non, 1 = oui) | +-------- Non utilisés +--------------------------------------------------------------------------------------------------------+ |Registre $400D: Registre de contrôle #2 du canal de bruit (Ecriture seulement) | | Ce registre n'est pas utilisé. | +--------------------------------------------------------------------------------------------------------+ |Registre $400E: Registre de fréquence #1 du canal de bruit (Ecriture seulement) | | Note: la fréquence du canal de bruit s'étale sur 11 bits. | Ce registre permet de déterminer la valeur des 8 bits de | poids faible de la fréquence. | | masque:%ffffffff -> 8 bits de poids faible de la fréquence | +--------------------------------------------------------------------------------------------------------+ |Registre $400F: Registre de fréquence #2 du canal de bruit (Ecriture seulement) | | masque: %tttttfff | |||||+--- les 3 bits de poids fort de la fréquence du canal de bruit | +-------- temps d'activité de la voix +--------------------------------------------------------------------------------------------------------+ |Registre $4010: Registre de contrôle du canal de son digital (DMC) (Ecriture seulement) | | masque:%i???ssss | ||||+---- fréquence d'échantillonage utilisée | |+------- fonction inconnue | +-------- génére une interruption de type IRQ à l'execution (peut être masquée) | +--------------------------------------------------------------------------------------------------------+ |Registre $4011: Registre du volume du canal de son digital DMC (Ecriture seulement) | | Les 8 bits sont utilisés pour définir le volume du canal. | +--------------------------------------------------------------------------------------------------------+ |Registre $4012: Registre d'adresse des données digitales du canal DMC (Ecriture seulement) | | Ces 8 bits détermine l'endroit où on doit lire les données pour | le canal DMC. | Les données seront lues dans la 2ème banque de PRG-ROM, c'est à dire de $C000 à $FFFF. | La formule pour trouver l'adresse est la suivante: | ((valeur 8 bits stockée dans $4012) * 64) + $C000 | Rappel: 64 = $40 | +--------------------------------------------------------------------------------------------------------+ |Registre $4013: Registre de longueur des données digitales (Ecriture seulement) | | Définit la longueur de l'échantillon digital à lire à l'adresse $4012 | La formule pour calculer sa taille est: | ((valeur 8 bits stockée dans $4013) * 16) + 1 | +--------------------------------------------------------------------------------------------------------+ |Registre $4014: Registre d'accès direct à la mémoire (DMA) SPR-RAM (Ecriture seulement) | | Voir PPU - Registre spécial. | +--------------------------------------------------------------------------------------------------------+ |Registre $4015: Registre d'état de la PPU (Lecture-Ecriture) | | En lecture, ce registre à seulement une fonction: | Seul le bit #0 est important, si il est à 1 c'est que la PPU est en cours d'utilisation | | En écriture: | masque:%000edcba | |||||||+- voix de rythme #1 (0 = désactivée, 1 = activée) | ||||||+-- voix de rythme #2 (0 = désactivée, 1 = activée) | |||||+--- voix de triangle (0 = désactivée, 1 = activée) | ||||+---- canal de bruit (0 = désactivée, 1 = activée) | |||+----- canal digital (0 = désactivée, 1 = activée) | +-------- bits non utilisés | | +--------------------------------------------------------------------------------------------------------+ Les registres de contrôle des canaux sonores servent juste à configurer les canaux (généralement ceci est fait une seule fois avant de jouer la chanson). Les "notes" sont envoyées par le biais des registres de fréquences avec la fréquence utilisée ainsi que la durée. Le mieux que je puisse vous conseiller c'est de tester ces canaux et d'essayer d'implémenter des partitions récupérées dans des fichiers NSF. Il existe de nombreuses documentations qui expliquent comment procéder. Il existe également un logiciel sur le site NES Development permettant facilement d'écrire une musique et de l'implémenter ensuite dans une rom. 3. Registres Joypads +--------------------------------------------------------------------------------------------------------+ |Registre $4016: Registre de contrôle du Joypad #1 (Lecture-Ecriture) | | En lecture: | %???zteed | |||||||+----- donnée du joypad (voir détails périphériques) | |||||+------- lire des données du port d'expansion (utilisé par certains périphériques) | ||||+-------- indique la gachette du Zapper a été préssée | |||+--------- indique qu'un sprite se trouve devant le viseur du Zapper | +------------ utilisation inconnue | | En écriture: | %?????ees | |||||||+----- Mise à jour état des touches du joypad (nécessaire pour lecture des touches) | ||||||| 0 = efface l'état | ||||||| 1 = reset l'état | ||||||| Voir détails précedemment. | |||||+------- écriture de données dans le port d'expansion | +------------ utisation inconnue +--------------------------------------------------------------------------------------------------------+ |Registre $4017: Registre de contrôle du Joypad #2 (Lecture-Ecriture) | | Son fonctionnement est identique à celui du registre précedent. | +--------------------------------------------------------------------------------------------------------+ Autres choses utiles: -------------------- 1. Structure iNes: Ici ne sera abordé que la structure iNes, qui utilise un header de 16 octets des fichiers .NES. Cet header ne fait pas vraiment parti de la rom mais contient des informations indispensables à son fonctionnement pour un emulateur (notamment si on utilise un mapper particulier). C'est un standart de fait et il existe d'autres formats (voir meilleurs). L'header iNes est de 16 octets($10): Adresse |Taille |Contenu | | 0 |3 |Contient les 3 caractères ASCII 'NES' | | 3 |1 |Contient la valeur $1A | | 4 |1 |Nombre de pages de PRG-ROM de 16 ko dans la rom | | 5 |1 |Nombre de pages de CHR-ROM de 8 ko dans la rom | | 6 |1 |Octet de controle de la ROM #1: | |masque: %mmmmvtsm | | |||||||+-- Miroir utilisé en VRAM (0=horizontal,1=vertical) | | ||||||+--- utilisation de SRAM ou non pour les sauvegardes (1=oui) | | |||||+---- presence d'un trainer de 512 octets | | ||||+----- Possibilité d'utiliser 4 Name Table indépendantes en VRAM | | +--------- 4 bits de poids faible du mapper utilisé | | 7 |1 |Octet de controle de la ROM #2: | |masque: %mmmm0000 | | ||||+------ ces 4 bits sont toujours à 0 | | +---------- 4 bits de poids faible du mapper utilisé | | | | | |Remarque: les octets suivants sont ignorés par la plupart des émulateurs et doivent rester à 0 --------------------------------------------------------------------------------------------------------------- Normalement il est d'usage de faire suivre le code (PRG-ROM) juste après l'header. Les pages CHR-ROM suivront donc logiquement et seront automatiquement chargées dans les tables des graphismes. L'utilisation de la CHR-ROM annihile toute possibilité d'écrire dans la VRAM de $0000 à $2000 sous peine de provoquer une erreur de bus, cette mémoire étant dorénavant considérée comme en disponible en lecture seule. On peut ne mettre le code qu'en utilisant que la deuxième page également. Si vous utilisez +de 2 page de PRG-ROM et +de 2 pages de CHR-ROM il vous faudra utiliser un mapper. Si vous n'utilisez qu'une page de PRG-ROM il s'agit toujours de la page commençant en $C000. 2. Multi-Memory Controler: L'utilisation du Multi-Memory Controller est indispensable pour obtenir une taille de rom >40 ko. Son utilisation va de paire avec ce que l'on appele un mapper. Le mapper utilisé est défini dans l'header. Son support par les émulateurs fut longtemps un problème. Il n'y aura pas beaucoup de détails sur cette partie car la mémoire disponible en standard par une rom est largement suffisante pour une démo. Tous les mappers sont détaillés à cette adresse: http://free.prohosting.com/~nintendo/mappers.nfo Détails sur les méthodes d'assemblage: ------------------------------------- La NES est une machine très ancienne et relativement limitée, néanmoins en assembleur il est possible de tirer d'une machine de nombreuses ressources puisque l'on travaille directement avec le matériel. Mais il convient alors t'integrer des méthodes de programmation particulières, ainsi que d'autres propres au système proprement dit. Il ne faut surtout pas hésiter à ménager la mémoire disponible en utilisant abondamment le masquage de bit pour les valeurs ne nécéssitant pas beaucoup de combinaisons. C'est à dire utiliser un octet pour définir sur des plages de bits (flags) des valeurs. On peut procéder ainsi: Si le monstre reponds masque suivant: ____________ | | | | bbbb bbb b | | +---- bit de vérification d'activation du monstre | +------- flag de 3 bits pour la vie du monstre +----------- flag de 4 bits pour le type de monstre ;on veut coder un monstre ;le nombre maximum de point de vie possible pour un monstre dans mon jeu est 3 ;il y a 15 monstres différents et on doit aussi avoir une valeur pour voir si il est actif ou non LDA monstre_1 AND #$F0 LSR A LSR A LSR A LSR A PHA JSR type_monstre ;routine pour identifier le monstre LDA monstre_1 AND #$0E LSR A PHA JSR degats_monstre Sur NES notamment, on ne dispose pas d'horloge intégré comme sur un micro-ordinateur (du moins à partir de 1990 la plupart en ont été équipé). Par contre pour palier à ce défaut, on peut utiliser le signal VBlank cablé en hardware répondant à partir d'un registre particulier et d'un routine d'interruption. Deux moyens répondant toutes deux à des besoins différents. Il faut savoir que sur la NES réelle écrire en mémoire vidéo hors que lors du déroulement du VBlank est une source de bug innérante. Par conséquent on est obligé de s'imposer une certaine rigueur de programmation. Rigueur qui n'est pas obligatoirement reproductible lorsque l'on n'a comme seul moyen de test un émulateur. Il existe 2 moyens pour utiliser le VBlank: -soit activer le bit #7 du registre $2000 de la PPU pour generer automatique une interruption non masquable et donc appeler la sous routine d'adresse définie par le vecteur situé en $FFFA. Ceci fait vous pouvez placer absolument tout le code que vous voulez du moment que ca n'excede pas la durée du VBlank en éxecution qui est d'environ 16 millisecondes. Ca suffit normalement pour placer une quantité de code relativement suffisante. Il faut bien réaliser que cette routine sera éxécuté 60x par seconde (pour une machine NTSC, par défaut sur la plupart des émulateurs). -l'autre moyen c'est de controler lorsqu'un VBlank survient par le biais du bit #7 du registre $2002. Il suffit de créer une routine qui boucle indéfinimment tant que cette condition n'est pas satisfaite. Normalement cela ne pose pas de problème. Attention si vous utilisez également les NMI ainsi que cette méthode car la NMI sera toujours satisfaite d'abord et ensuite uniquement votre routine sera éxecuté. En outre après la lecture du registre $2002 le bit #7 sera automatiquement remis à 0. Un détail qui a une importance majeure puisqu'il va éviter de réexecuter une seconde fois la routine, ce qui fausserait complétement le programme. Epilogue: -------- Je tiens à remercier tous les auteurs des documentations déjà disponibles en anglais, notamment Y0shi, Chris Covell dont la plupart de ce document à été constitué, ainsi que la plupart des auteurs de documentations disponibles sur l'excellent site NES Development: http://nesdev.parodius.com. Sans eux rien n'aurait été possible. Je tiens également à remercier ceux qui vont utiliser et distribuer ce document, et j'encourage vivement ce type d'initiatives. J'éspère sincérement qu'il vous sera utile autant qu'il m'a été agréable de l'écrire. Détails et remarques: crispysix@wanadoo.fr ------------------------- c'est ------------------ terminé ---------------------------