===============================================================================
= Son Son II (Stage Maps) =
= 16/04/10 =
= par Tanuki =
= pour Necstasy =
===============================================================================
- Sommaire -
------------
1) Introduction
2) Les macro-blocs
3) Tables de définition des macro-blocs
4) Organisation d'une map
5) Derniers détails
1) Introduction
---------------
Nous avons déjà vu que malgré que la BAT n'utilise que des tiles 8x8, nombreux
d'entre eux sont regroupés par 4 dans la VRAM pour former des blocs 16x16.
Nous allons voir maintenant leur utilisation.
2) Les macro-blocs
------------------
Un macro-bloc regroupe 4 blocs et associe à chacun une palette et un attribut.
+---+---+
| A | B | disposition des 4 blocs
+---+---+
| C | D | (1 macro-bloc = 32x32 pixels)
+---+---+
Un macro-bloc est défini par 8 octets d'information (2 par bloc):
+--------+ +-------------+
0 | bloc A | -> n + 0 | idx de bloc |
+--------+ +-------------+
2 | bloc B | 7 4 3 0
+--------+ +------+------+
4 | bloc C | n + 1 | pal |attrib|
+--------+ +------+------+
6 | bloc D |
+--------+
L'index de bloc:
C'est le numéro de bloc dans la 'BG tile area' soustrait de la valeur $36.
En fin de compte, seules les valeurs $00 à $69 sont valides car ce sont les
106 derniers blocs disponibles.
L'index de palette:
Les 4 bits sont placés dans les poids forts pour faciliter leurs transferts
dans la BAT.
L'attribut:
L'interaction avec un bloc se joue au moyen de 5 attributs:
+---+-------------+
| n | attribut |
+---+-------------+
| 0 | neutre |
| 1 | solide |
| 2 | agrippement |
| 3 | * |
| 4 | dommage |
+---+-------------+
(*) il faut que je fasse quelques recherches mais il semblerait que celà
évite au joueur de se manger un angle droit lorsqu'il cherche à monter à
des endroits.
3) Tables de définition des macro-blocs
---------------------------------------
Le macro-bloc est la plus petite unité de construction d'une map.
Chaque stage peut définir jusqu'à 256 macro-blocs ($00~$FF).
Une table de définition a une taille fixe de 256 x 8 = 2KB.
Voici les 7 tables disponibles dans la ROM:
+-------+------+-------------+
| stage | bank | range |
+-------+------+-------------+
| 1 | $06 | $0000~$07FF |
| 2 | $06 | $0800~$0FFF |
| 3 | $06 | $1000~$17FF |
| 4 | $06 | $1800~$1FFF |
| 5 | $07 | $0000~$07FF |
| 6 | $07 | $0800~$0FFF |
| 7 | $07 | $1000~$17FF |
+-------+------+-------------+
4) Organisation d'une map
-------------------------
Comme chaque stage utilise au maximum 256 macro-blocs, un index est de 8 bits.
D'abord les index sont regroupés en 8x8 pour obtenir des secteurs
(zones virtuelles de 256x256 pixels).
Ensuite ces secteurs sont regroupés en 8x8 pour obtenir la map finale
(zone virtuelle de 2048x2048 pixels).
une map un secteur
(8x8 secteurs) (8x8 index)
+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+
|00|01|02|..|05|06|07| -> |00|01|02|..|05|06|07|
+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+
|08|09|..| |..|0E|0F| |08|09|..| |..|0E|0F|
+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+
|10|..| | | | |17| |10|..| | | | |17|
+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+
|..| | | | | |..| |..| | | | | |..|
+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+
|28|..| | | |..|2F| |28|..| | | |..|2F|
+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+
|30|31|..| |..|36|37| |30|31|..| |..|36|37|
+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+
|38|39|3A|..|3D|3E|3F| |38|39|3A|..|3D|3E|3F|
+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+
Une map a une taille fixe de (8 x 8)(8 x 8) = 4KB.
Voici les 7 maps de ce format disponibles dans la ROM:
+-------+------+-------------+
| stage | bank | range |
+-------+------+-------------+
| 1 | $0C | $0000~$0FFF |
| 2 | $0C | $1000~$1FFF |
| 3 | $0D | $0000~$0FFF |
| 4 | $0D | $1000~$1FFF |
| 5 | $0E | $0000~$0FFF |
| 6 | $0E | $1000~$1FFF |
| 7 | $0F | $0000~$0FFF |
+-------+------+-------------+
5) Derniers détails
-------------------
- la map est chargée dans la moitié supérieure de la RAM ($3000~$3FFF).
===============================================================================
= EOF =
===============================================================================
Dernière modification par Tanuki le ven. 16 avr. 2010 17:03, modifié 1 fois.
Je comprends pas tout mais ça m'a l'air énorme comme boulot. C'est d'autant plus intéressant qu'il s'agit d'un des meilleurs (si ce n'est le meilleur) jeu de plateforme de la machine.
Ce n'est pas vraiment énorme pour le moment (Touko parle sûrement des fichiers pcx des maps du post précédent, dont j'avoue être assez fier ), par contre ici, ce n'est que l'aspect le plus simple des maps (je serai vraiment content lorsque j'aurai découvert comment marche le scrolling multi-directionnel).
Note:
Dans les docs, je n'explique pas forcément les choses très clairement, et met aussi des schémas pour éviter de décrire les choses avec des tonnes de lignes (mais je suis là aussi pour répondre à toutes vos questions).
J'ai fait un petit programme qui charge et interprète les données graphiques de la ROM.
Ensuite il exporte le résultat dans un fichier PCX (mon format préféré ).
La map 8bpp (de 4MB) sur ton écran est juste l'interprétation d'une simple map de 4KB de la ROM.
===============================================================================
= Son Son II (Stage Pals) =
= 10/05/10 =
= par Tanuki =
= pout Necstasy =
===============================================================================
- Sommaire -
------------
1) Introduction
2) Chargement groupé des palettes
3) Chargement des couleurs dans la RAM
3) Séquence de chargement
4) Les palettes des stages
5) Derniers détails
1) Introduction
---------------
Comme il a été déjà mentionné, le jeu Son Son II n'utilise que des graphismes
3-bpp, donc 8 couleurs par palette. De ce fait aucune couleur d'index $8~$F
n'est jamais initialisée ou utilisée par le jeu.
Je vais expliquer comment le jeu charge les palettes au tout début d'un stage.
2) Chargement groupé des palettes
---------------------------------
Le jeu charge les palettes du jeu en deux temps:
en premier celles de l'arrière-plan puis ensuite celles des sprites.
Les palettes (BG/SP) sont d'abord chargées à partir de la ROM, puis altérées
en fonction d'un indice de luminosité, et enfin rangées temporairement en RAM.
L'indice de luminosité (vous vous en doutez) va servir à produire les effets
de fondu.
Ensuite quand arrive le bon moment, les palettes sont envoyées vers le VCE.
Le même espace de RAM est utilisé à la fois pour les palettes BG et SP:
(rappel: 1 palette = 8 x 2 = 16 octets)
+---------+--------------++---------+--------------+
| address | pal (BG/SP) || address | pal (BG/SP) |
+---------+--------------++---------+--------------+
| $2EA0 | $0 || $2F20 | $8 |
| $2EB0 | $1 || $2F30 | $9 |
| $2EC0 | $2 || $2F40 | $A |
| $2ED0 | $3 || $2F50 | $B |
| $2EE0 | $4 || $2F60 | $C |
| $2EF0 | $5 || $2F70 | $D |
| $2F00 | $6 || $2F80 | $E |
| $2F10 | $7 || $2F90 | $F |
+---------+--------------++---------+--------------+
3) Chargement des couleurs dans la RAM
--------------------------------------
Le jeu utilise la fonction $1396 (banque $02) en utilisant les arguments
suivants:
$2028: adresse de la palette dans la ROM (lsb) --+
$2029: adresse de la palette dans la ROM (msb) --+ source
$202A: adresse de la palette dans la RAM (lsb) --+
$202B: adresse de la palette dans la RAM (msb) --+ destination
$2037: nombre total de couleurs x 2
reg X: indice de luminosité (0~7)
La fonction utilise une table pré-calculée au format suivant:
($15FA: 8x8 octets)
0 1 2 3 4 5 6 7
+---------------
0 |0 0 0 0 0 0 0 0 1/8 (black)
1 |0 0 0 0 1 1 1 1 2/8
2 |0 0 0 1 1 1 2 2 3/8
3 |0 0 1 1 2 2 3 3 4/8
4 |0 0 1 1 2 3 3 4 5/8
5 |0 0 1 2 3 3 4 5 6/8
6 |0 0 1 2 3 4 5 6 7/8
7 |0 1 2 3 4 5 6 7 8/8 (normal)
Pour chaque couleur:
le code extrait les 3 composantes RGB, utilise la table sur chacune d'elle,
puis regroupe et copie le résultat dans la RAM.
Donc pour faire simple (à partir de la table):
- X est l'index de ligne (la luminosité)
- R, G, ou B est l'index de colonne (la composante altérée)
+---+---+---+ +---+---+---+
(ROM) | G | R | B | | G | R | B | (RAM)
+---+---+---+ +---+---+---+
| | | | | |
| | +-----> TABLE[X][B] >-------------+
| +---------> TABLE[X][R] >---------+
+-------------> TABLE[X][G] >-----+
3) Séquence de chargement
-------------------------
Une fois les palettes en RAM, le jeu utilise une des deux fonctions suivantes:
(banque $02)
- $13FC: transfère les 16 palettes (BG) vers le VCE.
- $14EB: transfère les 16 palettes (SP) vers le VCE.
Au final la séquence de chargement complète est la suivante:
(pour X allant de 0 à 7)
- chargement des 16 palettes (BG) en RAM
- attente (2 frames)
- transfert des palettes vers le VCE
- chargement des 16 palettes (SP) en RAM
- attente (1 frame)
- transfert des palettes vers le VCE
4) Les palettes des stages
--------------------------
Voici les palettes utilisées lors du chargement des stages:
(banque $01)
+-------+------------+---------------+
| stage | pals | start address |
+-------+------------+---------------+
| x | BG $0 ~ $4 | $0000 |
| 1 | BG $5 ~ $F | $00DE |
| 2 | BG $5 ~ $F | $018E |
| 3 | BG $5 ~ $F | $023E |
| 4 | BG $5 ~ $F | $02EE |
| 5 | BG $5 ~ $F | $039E |
| 6 | BG $5 ~ $F | $044E |
| 7 | BG $5 ~ $F | $04FE |
+-------+------------+---------------+
| x | SP $0 ~ $7 | $0050 |
| 1 | SP $8 ~ $F | $05BC |
| 2 | SP $8 ~ $F | $063C |
| 3 | SP $8 ~ $F | $06BC |
| 4 | SP $8 ~ $F | $073C |
| 5 | SP $8 ~ $F | $07BC |
| 6 | SP $8 ~ $F | $083C |
| 7 | SP $8 ~ $F | $08BC |
+-------+------------+---------------+
x: mêmes palettes pour chaque stage
5) Derniers détails
-------------------
- la palette SP $0 est celle du personnage principal.
(celle du cochon également, et d'autres choses)
- tous les index $0 de chaque palette (SP) ont une couleur.
(à part la première, je ne sais pas à quoi elles servent)
===============================================================================
= EOF =
===============================================================================
Dernière modification par Tanuki le ven. 14 mai 2010 05:52, modifié 1 fois.
Je ne suis pas encore arrivé dans cette section du jeu (barre d'état/vie/continue...).
En ce moment je suis sur les sprites, de plus il me faut souvent beaucoup de temps pour comprendre certain morceaux de code: celui qui construit la map en VRAM, par exemple, mis bout à bout, faisait presque 200 lignes.
Mais quand j'y serai, il suffira sûrement d'un simple patch avec 2 ou 3 octets de changés pour avoir une barre de vie figée ou des continues illimités.
Je t'enverai un MP quand j'y serai (mais dans quelle année ?).
Dernière modification par Tanuki le lun. 10 mai 2010 15:40, modifié 1 fois.
===============================================================================
= Son Son II (Pad Proc) =
= 13/02/10 =
= par Tanuki =
= pour Necstasy =
===============================================================================
- Sommaire -
------------
1) Introduction
2) Utilisation des variables
3) La phase de lecture
4) Interprétation des données
5) Mapping des données
6) Les grandes lignes du code
7) Derniers détails
1) Introduction
---------------
Ici nous allons nous intérésser à la fonction qui lit la manette.
Les explications qui vont suivre ne s'appliquent pas uniquement à Son Son II,
mais également à d'autres jeux PC-Engine.
En effet comme ils utilisent le même code générique gérant le multi-tap, il n'y
a que les localités du code et des variables qui changent d'un jeu à l'autre.
2) Utilisation des variables
----------------------------
La première chose à rappeler, c'est que les données de la manette arrivent avec
des 0 lorsqu'il y a quelque chose de pressé, et des 1 dans la cas contraire.
Donc une fois que les données ont été lues à partir du port, la première chose
à faire est de complémenter les bits pour ramener le tout à une logique plus
proche de la notre:
data <- NOT(data) ; rappel: A XOR $FF = NOT(A)
A partir de là nous obtenons:
1 = pressé
0 = non pressé
Le code à besoin de trois variables 8-bit pour gérer un pad:
- une pour stocker les anciennes données (old_dat)
- une pour stocker les données actuelles (cur_dat)
- une pour stocker uniquement les nouvelles pressions (new_dat)
Il faut préciser que puisque la RAM est entièrement mise à zéro lors de
l'initialisation du jeu, ces trois variables sont nulles lors de leur première
utilisation.
3) La phase de lecture
----------------------
Les anciennes données sont d'abord sauvegardées:
old_dat <- cur_dat
Ensuite vient la lecture du port et l'inversion des données:
cur_dat <- IO_PORT
cur_dat <- NOT(cur_dat)
Puis enfin la déduction des nouvelles pressions:
new_dat <- (old_dat XOR cur_dat) AND cur_dat
4) Interprétation des données
-----------------------------
note: le détail sur l'attribution des bits se trouve dans la section 6.
On commence à zéro (la manette est neutre):
old_dat = %00000000
cur_dat = %00000000 ; rien de pressé
new_dat = %00000000
Maintenant je presse le bouton I:
old_dat = %00000000
cur_dat = %00000001 ; le bouton I est pressé
new_dat = %00000001 ; le bouton I vient d'être pressé
Maintenant je presse en même temps le bouton II:
old_dat = %00000001
cur_dat = %00000011 ; les boutons I et II sont pressés
new_dat = %00000010 ; le bouton II vient d'être pressé
Et je relâche tout:
old_dat = %00000011
cur_dat = %00000000 ; rien de pressé
new_dat = %00000000
5) Mapping des données
----------------------
Puisque le multi-tap peut gérer jusqu'à cinq pads, nous avons donc:
3 x 5 = 15 octets
auquel nous ajoutons une variable supplémentaire:
15 + 1 = 16 octets
Voici le mapping pour Son Son II:
+------------+
$2016 | pad #1 |
$2017 | pad #2 |
$2018 | pad #3 | current
$2019 | pad #4 |
$201A | pad #5 |
+------------+
$201B | pad #1 |
$201C | pad #2 |
$201D | pad #3 | old
$201E | pad #4 |
$201F | pad #5 |
+------------+
$2020 | pad #1 |
$2021 | pad #2 |
$2022 | pad #3 | new
$2023 | pad #4 |
$2024 | pad #5 |
+------------+
$2025 | pad_flags |
+------------+
Bien sûr Son Son II n'interprète que les données du pad #1.
6) Les grandes lignes du code
-----------------------------
La fonction se trouve à l'adresse $FEDE (banque $00) et est principalement
utilisée par la routine IRQ.
Le code commence par une manipulation pour ré-initialiser le compteur interne
du multi-tap (s'il n'est pas là, celà désactive/active simplement la sortie
du multiplexeur de la manette).
Ensuite pour chaque pad (#1 à #5):
- il sauvegarde les anciennes données.
- il lit l'état des directions à partir du port
(qu'il masque et place dans les poids forts de la variable)
- il lit l'état des boutons à partir du port
(qu'il masque et ajoute aux poids faibles de la variable)
Ce qui nous donne la disposition suivante:
7 6 5 4 3 2 1 0
+------+------+------+------+------+------+------+------+
| Left | Down |Right | Up | Run |Select| II | I |
+------+------+------+------+------+------+------+------+
| | | |
+---------------------+ +---------------------+
directions boutons
Entre la sélection et la lecture des données, le code attend à peu prés
9 cycles (~= 1.257µs en mode 7.16MHz).
- ensuite il complémente le tout.
- et enfin déduit les nouvelles pressions.
Une manette directement plugée sur le port de la console sera lue cinq fois de
suite (comme étant les pads #1 à #5);
Une fois la lecture des cinq manettes effectuées, il parcourt la variable
'pad_flags' pour savoir lesquelles ont le droit de forcer le jeu à faire un
reset:
7 6 5 4 3 2 1 0
+----+----+----+----+----+----+----+----+
| - | - | - |pad5|pad4|pad3|pad2|pad1|
+----+----+----+----+----+----+----+----+
Si le bit corespondant à une manette est à 1, alors le code teste les données
de cette dernière pour savoir si elles contiennent la séquence Run+Select.
Conditions requises:
cur_dat = %00001100 ; seuls Run et Select sont pressés
new_dat = %00000100 ; seul Select vient d'être pressé
Si c'est la cas, le code se branche à l'emplacement dans le jeu correspondant
à un soft-reset ($E013 pour Son Son II).
7) Derniers détails
-------------------
- La différence entre le hard et soft reset (pour Son Son II) est que pour
l'un, toute la RAM est mise à zéro, alors que l'autre conserve la plage
$2FF8~$2FFF (celle-ci contient principalement le hi-score de 7 chiffres).
- Voici pour exemple d'autres jeux utilisant exactement le même code:
Coryoon
Detana!!! TwinBee
Gradius
Gunhed
Hana Taka Daka!
Magical Chase (J)
Marchen Maze
Mizubaku Daibouken
Parodius Da!
PC Genjin
PC Genjin 2
PC Genjin 3
...
===============================================================================
= EOF =
===============================================================================