Showing posts with label hardware. Show all posts
Showing posts with label hardware. Show all posts

Monday, December 25, 2023

Petite pause HDMA

Yes! I managed to get some HDMA effect applied on my 3-rooms demo. It has absolutely no use in that particular scenery, but it did work. Yet, at first, I couldn't get anything, up to the point where I suspected that my emulator simply had no support for HDMA at all. But then I remembered seeing something very HDMA-esque in WarhawkDS (recently open-sourced). And then before I could even try whether the .nds would work in my emulator, Asie confirmed that she knew another open-source homebrew using HDMA: MegaZeux.

Hahaa! Nous y voilà! J'ai réussi à appliquer un effet "HDMA" dans ma démo de Bilou Dreamland! ça ne sert à rien, c'est d'une esthétique discutable, mais voilà: il y a presque 2 ans que j'ai des notes sur comment faire ce genre de chose dans mon carnet-agenda et que je passe par-dessus en me disant que "ouais, je tenterai ça un de ces 4. C'est complètement le genre de technique qui fait que je suis sur NDS et pas sur playdate ou androïd. Mais c'est McMartin avec son post sur le HDMA de la SNES qui m'a donné envie de bousculer un peu mon absence-de-planning de hobby-coding pour le mettre en oeuvre. Sauf que vous vous en doutez, au premier essai, rien ne marchait. J'avais pourtant quelque chose de quasi-identique à ce setup dans MegaZeux DS ou même warhawk DS.

  DMA1_CR         = 0;
  REG_BG0VOFS_SUB = scroll_table[0];
  DMA1_SRC        = (u32)(scroll_table + 1);
  DMA1_DEST       = (u32)&REG_BG0VOFS_SUB;
  DMA1_CR         = DMA_DST_FIX | DMA_SRC_INC | DMA_REPEAT | DMA_16_BIT |
                    DMA_START_HBL | DMA_ENABLE | 1;

The registers configuration is almost identical to the one I tried in my demo: 16-bit transfer with proper start and repeat setup, and a transfer size of 1 word per line. At that point I started suspecting that something else in my code would break the setup of the DMA transfer. After all we already have channel 0 used for 3D pipeline and channel 3 used by dmaCopy macros. So I picked up devkitpro simplest graphics demo and tried to bring it there instead.

Un petit passage dans les programmes d'exemple de devkitpro. Aucune ne fait du HDMA alors j'essaie d'injecter mon code dedans. Sauf que la première victime n'a aucun plan de décor (donc rien à faire onduler) et est écrit en C (contre du C++ pour mon code). Je m'adapte et je fais la bonne vieille rasterbar (impossible dans le code de Bilou parce que je travaille en mode 4096 couleurs par plan, ce qui veut dire que la palette est hors de la mémoire adressable). Et là, ça marche presque nickel. Il faut juste veiller à programmer la couleur pour la ligne 0 "à la main" avant de commencer la configuration du HDMA qui fera toute les autres lignes parce qu'il se déclenche *à la fin* de chaque ligne, mais pas pendant les lignes virtuelles du délai entre 2 images.

The first one (simple sprite) had no background to wave but it had single palette, so there (unlike with my demo), I could beam values into palette slot 0 and see it draw raster bars on screen. A bit of translation was needed because it was a C example rather than a C++ one, but there it is. changing the background colour like I was driving an Atari 2600 except the DMA does the waits and syncs, and not the CPU.

class HdmaEffect {
  static const size_t N=256;
  static const unsigned CHN = 1;
  s16 data[N];
  size_t offset;
public:
  HdmaEffect(); // intialize data[]
  ~HdmaEffect() {
    DMA_CR(CHN) = 0;
  }

  void Frame() {
    DMA_CR(CHN) = 0; // disable channel
    DMA_SRC(CHN) = (uint32)(data + offset);
    DMA_DEST(CHN) = (uint32) &REG_BG0HOFS_SUB; // mind the & to get register *address*
    DMA_DEST(CHN) = (uint32) BG_PALETTE_SUB;
    DMA_CR(CHN) = DMA_REPEAT | DMA_START_HBL | DMA_SRC_INC | DMA_ENABLE | DMA_DST_FIX | 1;
    offset = (offset + 1) & ((N / 4) - 1);
  }
};

void mainLoop() {
	HdmaEffect hdma;
	
	while(1) {
		swiWaitForVBlank();
		hdma.Frame();
		scanKeys();
		if (keysDown()&KEY_START) break;
	}
}

Mais mon code C++, lui, toujours rien. Il n'est pourtant pas si différent. Je continue à passer d'un exemple à l'autre et je tombe sur un avec un décor (qui refusera mordicus de bouger) et en C++. Toujours rien. Par contre, en reprenant et adaptant le code C, là, je parviendrai à faire onduler le texte de l'écran du bas. Moins sexy, mais c'est un début. Il m'aura fallu un bon réveillon familial et une petite nuit de sommeil pour comprendre la différence fondamentale entre les deux implémentations.

I picked another example featuring a background, this time in C++. I couldn't get any color changed with my C++ code, and I couldn't get the picture waving. But with the adapted C code, it could change colors and I could make the text on the bottom screen waving (although somehow weirdly). We were 24th of December, I had errands to run and a party to attend, so I accidentally shut down the computer and went doing something completely different. It's only when I woke up this morning that I got struck by the difference between the C++ class and the C code. The C++ class will have the source array in a member and it allocates the HdmaEffect object on the stack. But the stack is invisible to DMA operations. I've been tricked by that a good number of times in the past already. One more alloc/free pair was all I needed to get the working screen-waving effect you've seen above. Huzzah! Merry Christmas! May the source be with you all ;-)

La conversion rapide en C utilisait une grosse variable globale pour le tableau contenant les différentes valeurs à streamer dans le registre de scrolling. La version C++, plus propre sur elle, encapsulait ce tableau au sein de l'objet HdmaEffect, lequel pouvait être construit dans une variable locale pour gérer ses ressources (le canal DMA) Rabi-style. Sauf que "local", ça veut dire "sur la pile" et que la pile de la DS est 1) petite et 2) mappée sur de la mémoire plus rapide (type cache L1) logée au coeur du chip ARM ... et donc inaccessible depuis le bus système avec lequel travaille le contrôleur DMA. Eh oui. Un malloc plus tard, j'étais prêt à faire une démo qui marche ^^"

Thursday, June 01, 2023

Déveloper pour DS

Figurez-vous que le youtubeur Nathan Fallet vient de se lancer dans une série de vidéo "live coding" pour faire un petit jeu DS. Son idée est de prendre juste libnds et les outils de devkitpro et de faire une adaptation d'un petit jeu pour smartphone qu'il avait fait il y a quelques années. L'idée est assez proche de ce qu'aurait fait un nouveau venu sur un forum à l'époque, donc j'ai envie de suivre ça.

Son point de départ, c'est l'exemple 'animate simple.nds', où on peut promener un sprite sur chaque écran avec le DPAD en modifiant l'image utilisée par le sprite en fonction de la direction prise.

Pour ce programme, les développeurs ont choisi d'embarquer les images directement dans la section "données en lecture seules" du programme. Là où se trouveraient aussi les chaînes de texte si on avait un texte. Pas d'ouverture de fichier avec ce système-là, mais pas non plus la possibilité de prévoir de charger/décharger des images de la mémoire principale pour y mettre autre-chose. En même temps, comme la DS a 4 beaux gros Méga, on devrait être tranquilles pour un projet modeste.

Il y a un programme (que je n'ai pour ainsi dire jamais utilisé) qui prendra en charge la lecture du fichier .png pour le convertir en données brutes (des tiles et des palettes) exploitables directement par le hardware de la console. Par contre, ce programme ne voit qu'une seule image à la fois. Il peut ranger les couleurs dans la palette, éviter les doublons, etc. mais si vous lui donnez deux .png différents, chaque .png produira son lot de sprites supposant que sa première couleur est en position 1 dans la palette. Essayer d'afficher les deux à la fois donnera de mauvais résultats. Le programme d'exemple masque un peu ça avec son man.png et sa woman.png, parce que dans ce cas précis, chaque personnage est chargé dans la mémoire de son écran. L'un sur le chip "main", l'autre sur le chip "sub". Il ne sauront jamais se rencontrer.

Le programme d'exemple n'utilise que la fonction "sprites" du hardware 2D. Aucun des 4 plans de décor possible n'a été configuré. Mais ça ne veut pas dire qu'on soit condamné à errer dans le noir pour autant.

Comme dans le cas de la Super NES, chacun de ces plans serait scrollable indépendamment des autres et possèderait une "couleur transparente": l'entrée 0 dans la palette de couleur. Mais si aucun plan n'a donné de pixel à afficher, le hardware nous mettra un plan avec la couleur 0. Et cette couleur, elle est trouvée tout simplement dans la mémoire vidéo. Les fichiers d'en-tête fournis avec libnds nous donnent un symbole BG_PALETTE qui permet de lire et d'écrire dans la palette "décor" de l'écran principal.

Ce n'est pas la seule manière de faire, bien sûr. On aurait pu activer un plan de tiles, remplir un tile avec une autre couleur puis remplir la map avec ce tile. On aurait pu activer un plan bitmap et remplir 98K de mémoire vidéo avec la même valeur. On aurait pu activer le moteur 3D et mettre un gros polygone monochrome...

Déplacez le BG_PALETTE[0] dans la boucle principale, modifiez sa valeur d'une itération à l'autre, et vous aurez un fond "stroboscopique". Pour faire un dégradé, par contre, je pense qu'il faudra passer par le HDMA. Mais ça, je n'ai pas encore tenté.

Bon voyage, M. Nathan ;-)

Je note au passage que depuis le temps, libnds s'est dotée d'un mécanisme de gestion des ressources (allocation dynamique de mémoire vidéo pour les sprites) et d'une cache pour la mémoire vidéo contrôlant les sprites (permettant potentiellement de les mettre tous à jour pendant le vblank)

Saturday, January 15, 2022

Le HDMA

Sur SuperNES, un grand nombre d'effets intéressants dépend de la programmation du HDMA: une sorte de 'blitter' synchronisé sur les retours horizontaux (Pour ceux d'entre-vous qui n'ont pas regardé une vidéo sur l'Amiga la semaine dernière, traduisez "un co-processeur déclenché à chaque retour de ligne"). A-t-on quelque-chose d'équivalent sur Nintendo DS ?

The SNES had an interesting circuit that could (among other things) copy an array into one (or a set of) register(s) while synchronizing with screen display: the HDMA. And an impressive number of effects in SNES games were actually involving that circuit. What about the DS? Does it do HDMA as well ?

For those of you who have never programmed an 8-bit or a 16-bit system at metal level, DMA stands for "Direct Memory Access", and it is usually a chip or a circuit of a chip that will peek and poke data on the memory bus without involving the main processor. It can boost memory copies, or deliver samples to the sound chip, for instance. Nintendo Consoles have multiple channels available for DMA transfers, each channel with its own source, destination and count registers to identify what is to be copied and where it goes.

Un DMA, c'est un circuit qui va se faire passer pour le CPU sur un bus et ordonner des lectures ou des écritures de données. C'est surtout utile quand on peut effectuer des transfers entre de la mémoire et des des registres hardware. Le processeur peut alors s'occuper d'autre chose. La première fois que j'ai joué avec ça, c'était pour mon modplayer SoundBlaster.

Les consoles Nintendo ont plusieurs 'canaux' DMA, chacun responsables d'un transfert entre une source et une destination. Il y a un mécanisme de priorité fixe entre ces canaux: si deux canaux essaient d'utiliser le bus mémoire simultanément, celui ayant le numéro le plus élevé est mis en pause le temps que celui ayant le numéro le plus faible ait fini. Le processeur, lui, passera après tous ces transfers DMA.

So I could prepare a list of colours and transfer them by DMA into a palette entry, but that would be of little use: I would have no control over the speed at which new values are written in the palette and thus I would have no idea of the effect it would have on screen. If I check the DMAxCNT GBA registers, I can find a "trigger" field that can be used to force delays while copying data. Everytime the device is done with a part of the transfer, it will wait for some condition to become true before going on. And that condition can be the end of a picture (vblank) or the end of a scan line (hblank of "horizontal blank", and this is where the H of HDMA comes from). By synchronizing my copy-to-palette on new lines, I'll get a nice, amiga-like, sunset raster in the background of my scene.

On retrouve dans les registres de contrôle DMAxCNT du GBA un champ 'trigger' qui permet à un canal de faire une pause entre deux écritures, cette pause s'arrêtant quand une certaine condition apparaît sur la machine. Parmi les conditions possibles, la fin d'une image ou la fin d'une ligne (vblank et hblank, respectivement). On y retrouve aussi des champs permettant de dire si les addresses mémoire doivent évoluer après chaque mot transféré. Si on veut par exemple reprogrammer un registre de scrolling à chaque ligne (pour émuler un effet de profondeur), on demandera que la source avance à chaque transfer (pour parcourir un tableau de valeurs) tout en restant à une adresse fixe pour la destination (le registre de scrolling, tiens!).

La nintendo DS reprend pour l'essentiel le mécanisme de DMA du GBA, sauf qu'on a 4 canaux pour l'ARM9 et 4 autres canaux pour l'ARM7. Seul l'ARM9 peut faire du H-DMA. Il peut aussi se synchroniser sur un nouveau type de trigger (j'aurais dû écrire "déclencheur" dès le départ): l'apparition de place dans la file de transfert vers le moteur 3D (le GXFIFO). La DS a aussi 16 canaux DMA supplémentaires pilotés par les registres sonores de l'ARM7, un pour chaque piste audio, mais ils sont totalement dédiés à cette fonction.

Des transferts DMA, j'en ai déjà fait pas mal dans mes homebrews, le plus souvent pour faire un 'memcopy boosté' entre la mémoire principale et la mémoire vidéo. Dans ce cas-là, le bit 'repeat' était à 0, et le canal s'auto-désactivait une fois le transfert terminé (c'est à dire que les N mots sont copiés). Pour notre 'effet de profondeur', on devrait programmer le canal DMA pour qu'il ne transfère qu'un seul mot (la nouvelle valeur de scrolling) au bon moment, mais activer le mode 'repeat', de sorte qu'un nouveau transfert ait lieu après chaque ligne.

The NintendoDS has DMA hardware very similar to that of the GBA, except it has 4 channels for each CPU, but only the ARM9 can do H-DMA. It can also synchronize on a newly introduced trigger type: room in the 3D FIFO queue. Oh, and while there are 16 more DMA channels to drive sample fetching for the sound hardware, they're controlled through the ARM7 sound registers and are hard-wired for that purpose. 

I already did a good deal of DMA transfers in my DS homebrews, mostly to boost memory copies between main and video memory. In that case, the 'repeat' bit was cleared and the DMA channel would auto-deactivate once the transfer was over (that is, once the N words configured were copied). For an HDMA effect, I would configure N=1, but enable the 'repeat' bit so that a new transfer is started at every scan line. That would work for my raster, or for underwater screen wobbling effect.

(Picture from RGMech Ex channel)
I could also reprogram multiple registers at every line, like coordinates of a window effect to get a curtain effect or a giant ink wave, provided that they would be contiguous in the address space. This is what the "advance and reload" destination mode is for. Everytime a word is written, the target address is incremented but when a next transfer starts (because of the 'repeat' bit), the contents of the destination address is reloaded from configuration registers. Oh, and likely, I'd have to manually reset the source address during the VBlank else it will end up using garbage values after a few frames. And that's it. Fairly simpler than the what the SNES hardware could do with its HDMA table and entries, but I guess the higher amount of RAM of the DS (and GBA?) made such 'run-length-encoded' techniques oversophisticated.

Supposons maintenant qu'on veuille reprogrammer plusieurs registres à chaque nouvelle ligne. Les positions de début et fin de fenêtre, par exemple, pour faire un effet de rideau plus dynamique ou une vague d'encre géante. On peut évidemment utiliser un canal DMA pour chacun, mais s'ils ont le bon goût d'être l'un après l'autre en mémoire, on peut utiliser le dernière mode de gestion de la destination: le mystérieux 'avance et recharge'. Ici on passe au registre suivant après chaque mot transféré, mais quand le transfert redémarre (parce qu'on a activé le bit 'repeat'), on repart du registre-destination qui avait été programmé, et pas de sa valeur à la fin du transfert précédent.

edit: ah oui. Bon évidemment, la 'source' du transfert a continué à augmenter tout au long de l'image, ligne après ligne, et si on ne fait rien, il continuera d'augmenter aussi pendant le 'vblank' (équivalent-lignes inactives après le bas de l'image). Il faudra le reprogrammer avec la nouvelle valeur d'origine (ou une autre) au moment d'attaquer la nouvelle image.




Tuesday, November 23, 2021

Super Bilou 2

 Supposons qu'il soit possible de changer la manière dont SchoolRush affiche son encre qui monte,
histoire de laisser les 3 plans de background disponibles sur Super NES tranquilles.
On pourrait alors s'en sortir avec moins de 1024 tiles pour la constructions du niveau, et un moteur de scrolling plus classique.

Supposons encore qu'il soit possible de convertir le décor-hibou en 4-couleurs-par-tile sans arriver à une qualité décevante. Il resterait alors entre 12 et 20K de 'sprite tiles' 16 couleurs pour se faire un 'Super School Rush'. ça demanderait clairement une gestion dynamique d'une partie de la VRAM pour les sprites, mais ça semble déjà beaucoup plus cool. On pourrait par exemple garder les sprites 8x8 statiques (pieds, mains, gouttes, etc.) et se contenter d'un tableau de bytes pour encoder l'utilisation dynamique des sprites 16x16. ça reste un gros morceau, mais c'est déjà nettement plus 'viable' que demander du streaming de bouts de niveau.

I had a dream of porting Bilou School Rush to the Queen Console: the Super NES. Last study of how that could be done concluded to a bitter 'not a chance unless you've got a team of SNES geniuses along'. The issue is that I can't draw a full line of sprites on the SNES as if it was an additional plane and still hope to see more sprites through. And the inkline in School Rush DS is all made of sprites.

But the idea came back a few days ago with a striking simple observation: what if instead of changing everything to free up a layer for the ink I change the ink itself ?Like replacing the repeated wave spikes with mostly-flat ink and a crossing bigger wave every now and then ?

J'ai deux options pour ça. La première, c'est de trouver une illustration sympa de vague déferlante. Je garde de l'encre principalement plate et statique (construite par du masquage) et je fais passer de temps en temps une déferlante composée d'un gros sprite (32x32) et qui fait le lien avec un niveau d'encre plus haut par-derrière.

La deuxième option, ce serait de modifier la position verticale des sprites utilisés pour la bande d'encre (graphismes gardés à l'identique) de façon à construire un motif en créneau. Après tout, dès que l'encre couvre de toutes façon tout l'écran, peu importe que la limite de 272 pixels/ligne empêche les pieds des bladors d'être dessinés: ils sont cachés dans l'encre, de toutes façons. Il devrait y avoir entre 8 et 9 "créneaux rouges" visibles à l'écran, soit 128 à 144 pixels consommés sur le budget de 272. En fait, pour que ça coince dans ces conditions, il faudrait que les personnages à l'écran couvrent la moitié de la largeur de l'écran (quand on les dé-superpose, évidemment). Je ne pense pas que ça puisse se produire avant le niveau 4.

Eventuellement, on pourrait même imaginer forcer l'encre à rester plus plate autour des personnages qui pataugent dedans histoire de diminuer temporairement le budget-sprites qu'elle nécessite... éventuellement.

Another thing I realized is that only one part of the ink stripe consumes near 100% of the sprite-pixels-per-line budget. On the most interesting part (the spikes), we have nice 16-pixels blank space between two 16-pixels spikes. The NDS graphics absolutely don't take advantage of this, but an SNES port perfectly could. The top-part of the ink (red) then consume about 50% of the sprite-line budget and what happens as soon as the blue part kicks in doesn't matter because there, the ink is obscuring everything anyway. That will make the ink more tricky to animate, with sprites coordinates changing much more often, but that should remain manageable.

Now the next step is to ensure the background owl can still be nice to look at when using only 4 colours per tile.

Sunday, July 25, 2021

I2C

Nah, not precisely that device, but quite close.

I've been lucky working on embedded systems over the last 7 years (wow. Yeah. that many already), and being part of the engineering of one for at least the last 4. I mean, my colleagues really put together chips on a multi-layer PCB following chipmaker's guidelines and their tools advice and their knowledge so that high-speed signals would arrive clean and timely where they are expected.

We eventually received a first batch of prototype and I felt like I was part of the Playstation design team while we were bringing up the board, one chip at a time, adding custom software scripts so that the hardware teams would have signals to monitor and assess whether the hardware produced was meeting the expected quality (sometimes it did, sometimes it didn't. Hopefully, there were plenty of 0-ohm resistor and not-stuffed-pull-up/low resistors on the PCB design to adjust things. Clever they).

Many of the chips we used (like HDMI receiver/transmitters, power management IC, audio CODEC and even the FPGA) are meant to work mostly standalone, but they still need to get configuration at some point. Like telling the codec which samplerate it should run at and whether it should produced 8-bit or 16-bit values. Sometimes we want to read information from them, like the board temperature or the signal resolution the HDMI receiver got. For all this, I had I2C.

And not quite that SoC, but an OMAP anyway...

I2C is a bus, connecting 2 or more chips. I has one master chip and multiple subordinate chips. Communication always happens when the master says so. It either reads (asks for data) or writes (sends data), but there is no 'interrupt' signal flowing back part of the I2C bus, nor can you have sub-to-sub communications over the bus. Unlike ISA that connected my soundBlaster and my trident SVGA cards to my 80386 CPU, the I2C uses only 2 lines: one clock line and one data line. The goal is obviously to make it as cheap as possible for chips to support it.

Let's think of it from a network guy perspective. ISA and PCI busses (and even USB) try to offer as much bandwidth as technically possible because they'll have to transmit bulks of data. I2C is the out-of-band technology you'd use next to a (r/h)igh(t) bandwidth solution you use for your data blasts. The audio CODEC chip had I2S lines dedicated to sound transport. The HDMI chip had BT1120 interface for the same purpose. At best these media busses had additional lines notifying 'start of frame', but you couldn't use them to deliver status information, and most of the time, chips using them were hard-wired as either transmitter or receiver on those lines, so using them to send configuration would be electrically dangerous to the chips.

The brilliant part in I2C is that the two chips using it don't need to agree on any speed nor be clocked with the same crystal: bits from the DATA line are sampled only when the CLK line rises. That gives the opportunity to have special 'START' and 'STOP' encoding that would otherwise be invalid. For instance, changing the DATA line while the CLK line is in a-priori requesting the DATA not to change.

Most of the systems I've worked with had dedicated I2C driving units that would handle bits transmission after you indicated one or more bytes in a FIFO buffer and the device identifier in another register. But you may also simply use a generic-purpose I/O line as DATA and another one as CLK. That will work whatever the speed of your CPU.

Much like ISA soundblaster cards would use only one of 0x220, 0x240, 0x260 or 0x280 port for their control register, an I2C-compatible sound chip would e.g. only react to chip ID 0x22, 0x24, 0x26 or 0x28. And much like you had jumpers on your soundblaster card to pick something else than 0x220 if you happened to have a tape reading device using that port as well (or too soundblaster, lucky weirdo :-D), your I2C chip will typically feature some configuration pin that you can wire to ground or VCC to pick one of the available IDs.

Saturday, February 06, 2021

2048 triangles

Cette histoire de Big Level Objects me turlupinait: est-ce que je ne fais pas le mauvais choix en laissant de côté le hardware 3D de la DS pour dessiner les niveaux ? Plusieurs autres homebrewers ont déjà indiqué qu'ils parvenaient sans trop de difficultés à gérer tout l'affichage d'un platformer 2D à coup de polygones sur la DS.

Bug! (1996) Saturn / PC

Given how natural "big level objects" are in 3D engines and how hackish they are in tiled-based engines, I wanted to give a second chance to the all-in-3D option. I know some homebrewers have succesfully walked to path opposite to that of New Super Mario Bros: sprite entities in 3D worlds that actually render 2.5D. Don't let yourself fooled: it's a tempting trip since the first screenshots of Bug! got printed in my favourite PC magazine.

A second tempting trait is that a 2.5D setup can make the "goal" of a level as clear as Journey's mountain. In a pure 2D platformer, you don't see where you're heading, hence the "walk to the right" stereotype.

Bon, je me suis laissé dire que les calculs pour que les graphismes tombent justes sont assez dégueulasses, ce qui a plutôt tendance à me refroidir.

Mais bon, en 1996, j'étais séduit par l'approche de la 3D par le jeu Bug! (en fin, au moins sur photo dans les magazines). Et depuis Journey, je dois reconnaître que voir dans l'arrière-plan l'objectif à atteindre, c'est une manière intéressante de travailler, tout en proposant un déplacement latéral pour les phases de plate-formes plus exigeantes. Sans doute que si j'avais vu la réalisation de Hercules, j'aurais poussé l'idée plus loin.

Donc, supposons que ... Qu'est-ce qu'elle a dans le ventre, la bête ? 2048 triangles. Ou 1024 quadrilatères (oui, on peut alimenter le chip 3D soit en triangles, soit en quads). Si on donnait un quad par tile à l'écran, il ne resterait plus que 256 polygones pour faire les personnages ou les particules. Pas énorme.

Fuzed by Relminator

One clear drawback is that the computations to get it right are ugly. Everybody who's been trying to do pixel art in Unity eventually says so, and I have no reason to believe it would be otherwise with NDS hardware. I don't mean the maths behind 3D projections here, but the setup to ensure that pixels got rendered as they are intended to, with one cell in memory exactly matching one cell on-screen.

But nonetheless, I looked up the "manuals". The NDS can render 2048 triangles or 1024 quads. That's 130% of  the number of 8x8 pixels tiles I've got on one _plane_ per screen. No doubt that if I go the 2.5D-with-GL path, I'll have to design levels with coarser objects (maybe not as coarse as Relminator's 32x32 pixels cubes), but thinking of some extra quads so that things are effectively 3D and not all flat might be necessary.

Now, to be honest, I don't really see Bilou fitting a cubical world like the one seen in Fuzed (or in Lemmings 3D), nor a origami-like world à la Mario 64. I gave that a try back in the Deluxe Paint days. Picking my best 'dirt' picture and soft-render it like if it was a Wolfenstein view. And to be honest, Badman 3 story that was rendered in a similar way look gorgeous in comparison.

Du coup, je m'attends à devoir travailler avec des objets plus larges, un peu comme ces cubes de 32x32 pixels comme on les voyaient dans le jeu "Fuzed" de Relminator. Là, avec un bon moteur on aurait 3 quads par bloc (5 avec un moteur minimaliste), et je compte un maximum de 62 cubes visibles à l'écran (donc 180 à 300 quads sur les 1024)

Bien sûr, si je veux aussi montrer une deuxième "couche", plus éloignées, les cubes seront plus petits, et donc plus nombreux.

3D made with Deluxe Paint for Badman.

Maintenant, il faut bien admettre un truc: ce genre d'esthétique toute cubique, ça n'a jamais vraiment été mon trip. J'avais fait un essai avec la fonction 'projeter une brush en 3D' de Deluxe Paint aux alentours de 1998 avec ma meilleure "texture" de terre pour refaire la cachette dans les racines de l'arbre creux de la green zone, et c'était pathétiquement catastrophique. Ça pourrait marcher un peu mieux avec la School Zone ou la pyramide, mais on devrait partir sur une esthétique plus proche de Phantom Hourglass (qui ne m'a jamais vraiment séduite non plus).

Bref, faute d'élément motivant, je reste gentiment avec mes p'tits tiles et mes p'tits sprites.

that would be 1024 quads. So we can fill the screen with 32x32 pseudo-tile and we'd have saturated the 3D pipeline of the NDS.


(cf. https://github.com/AntonioND/nitro-engine)

Sunday, February 09, 2020

Thank you, Fabien

It's a true pleasure to keep reading the reports of Fabien Sanglard about the "polygons of Another World", one system after another. He went for people who did the ports, like a great investigator from Pix'n'Love would and mix those live coding moments with technical details about the various 16-bit architectures that make it quite exciting and pleasant to discover.

Of course, there's already a significant amount of things I have learnt about the bowels of Another World: because Eric Chahi himself documented them on his website. Like the language used for game logic, the development of the polygon modelisation tool and so on. That makes it (imho) perfectly for Fabien to focus on getting high-performance drawing primitives. But if you really want to, he covered the virtual machine source code back in 2011. 

For instance the episode about the homebrew GBA port finally allowed me to understand why the heck there were thumb-vs-ARM modes and IWRAM-vs-EWRAM and things alike in gba coding. With a simple diagram of how chips and busses were connected. That could definitely help if I was to port some Bilou games to the GBA later on (likely I'll take closer attention to make that possible with "Bilou Dream Land"). But I enjoyed the description of how the Amiga used its blitter to fill polygons quickly, too. and how the SNES version could have been running at 60 fps if the manager had said yes to a SuperFX chip on the cartridge.


Saturday, September 07, 2019

272 sprite pixels / line.


Si faire une image sur DS c'est un peu comme monter une bibliothèque-étagère contre un mur, alors faire une image sur Super Nintendo, c'est pareil. Sauf que le toit est en pente, qu'il y a un conduit de cheminée qui dépasse, des poutres de toiture apparentes et une tablette d'appui de fenêtre dans le chemin.

Bref, il y a des contraintes à gogo et il faut tenir compte de toutes ces contraintes-là en même temps. Une qui m'a particulièrement perturbé, c'est la limite à 34x8 pixels de sprites par ligne horizontale en plus de la contrainte d'un maximum de 32 sprites par ligne (sur un total de 128 sprites, quand-même. ouf.)

En clair, la console a beau être capable de faire le rendu d'un sprite de 16x16 ou 32x32 pixels d'un coup, ça ne lui donne pas plus la possibilité d'utiliser les sprites pour faire une ligne de vagues couvrant tout l'écran (256 pixels) avec 8 sprites de 32x32. Enfin, si, mais vous n'aurez plus droit qu'à deux personnages sur la même ligne, et pas 24 autres sprites.

Je me suis donc mis à essayer de comprendre comment la structure du chip graphique de la Super Nintendo pouvait bien expliquer ça. L'idée de base, c'est que la console n'a qu'une bande passante limitée à sa mémoire graphique, tout juste suffisante pour aller chercher assez d'informations pour construire une ligne d'image sur le temps qu'une ligne de graphisme est tracée à l'écran par le canon à électrons. J'avais déjà appris que c'était vrai pour les décors -- c'est ce qui explique qu'on doive descendre à 4 couleurs par plans si on veut ajouter plus de plans -- reste à comprendre ce que ça implique pour les sprites.

Nintendo DS and Super Nintendo graphic chips are quite similar in the way they build images. Multiple layers of tiled background plus independently-positioned sprites. But the SNES comes with way more constraints than the DS, and I don't mean resolution or colour count constraint. I mean the 'sprites per scan line' constraint and the 'sprite tiles per scan line' constraint. The NES had only one width for sprite: 8 pixels (one tile). SNES introduced the ability to use 16x16 and even 32x32 pixels sprites but there was still some part of the hardware that could only prefetch a fixed number of sprite tiles per line to do its job.

See, for every pixel on-screen, the graphic chip will have to decide whether one of the sprite's graphics should be shown, or whether background pixel should show through. But the graphics chip has limited bandwidth to the video memory, and most of the time, it uses it nearly to its maximum. That's why we have to drop colours per pixel if we want more background planes, for instance.

Pour chaque pixel que le contrôleur du signal d'écran produit, il va falloir tester les sprites actifs et pour certains (peut-être chacun) d'entre-eux, il faudra regarder si le pixel correspondant est transparent ou opaque. Le premier pixel opaque dans l'ordre de priorité défini par le hardware gagne le match et les autres ne seront pas pris en compte. Ce genre de chose peut être réalisée efficacement avec un bus à collecteur ouvert et un circuit de contrôle des sorties de chaque unité "pixel pour le sprite S" dépendant du résultat des circuits précédents. Mais ça suppose qu'on ait, pendant toute cette phase de génération d'image, accès à l'ensemble des pixels de tous les sprites pour la ligne en cours.

La bande passante de la mémoire n'est pas suffisante pour aller regarder chacune de ces valeurs à chaque pixel, il faut donc supposer qu'il y a à l'intérieur du chip graphique un cache de 8 pixels de large (d'où le x8) pour chaque comparateur et que ce cache est rempli une et une seule fois lors du traîtement de la ligne.

Il me restait à comprendre comment ce genre de préparation est compatible avec des sprites dont on peut modifier la position en cours d'image. Et là, surprise: selon Upsilandre,

[...] Tu peux pas modifier la [Sprite Attributes Table] pendant l'affichage, ça veut dire pas de multiplexage software possible contrairement à la megadrive.

Le principe des sprites au raster c'est que les 34 patterns sont chargé en amont pendant le Hblank dans des registres internes. Ensuite c'est un jeu de priorité cablé dans le hardware qui va fait que tel pixel de tel registre aura la priorité sur les autres.

En arcade c'est mieux, t'as un (double) line buffer donc ils peuvent charger les pattern pendant toute la durée de la scanline précédente (et pas juste pendant le Hblank) c'est ce qui leur donne ces performances bien supérieur

J'aurais cru que la technique de multiplexage -- classique sur C64 ou NES et possible sur NDS -- était omniprésente sur console, et sur la 16-bit de Nintendo aussi. Visiblement non.

The best re-design I can come from assumes that we have an 8-pixels-wide cache of sprite data for each comparator block we put in the chip. Video memory is still laid out as 'bitplanes' in the SNES and accessed 8-bit at the time from commodity RAM chips. Each unit we read gives us data for 8 pixels and reading more of them gives us more bit-per-pixel. What I couldn't come up with was how to make that work with on-the-fly modifiable sprites table. Chatting with Upsilandre killed that fantasy: unlike the NES, the Mega Drive, the NDS and even the Commodore 64, you cannot change the contents of the 'OAM table' describing sprites while the SuperNES is repainting the screen.

Et pour creuser un peu plus loin, les chiffres sont sur http://folk.uio.no/sigurdkn/snes/timing.txt. C'est un peu difficile d'en tirer un nombre d'instructions possibles par ligne retracée à l'écran, mais les chiffres annoncés me portent à croire que le 65xxx dont la Super NES est équipée garde une architecture interne très proche de celle du 6502, avec un bus unique entre les différentes unités internes et du "microcode" qui s'exécute à la fréquence de la master clock de la clock processeur mais qui peut nécessiter un nombre de cycles d'horloge variable (de 3 à 6) pour exécuter une instruction, ce qui donne entre 200 et 500 50 et 73 instructions par ligne retracée à l'écran.

On comprend mieux du coup les ruses de sioux du style "on ne va pas tester toutes les collisions à chaque images" qui régnaient en maître à l'époque..

Saturday, July 21, 2018

Junko Ozawa's Secrets

(re?-)Bienvenue à Upsilandre dans la blogosphère! Quel plaisir de pouvoir découvrir en détail ses analyses des techniques ancestrales de programmation assis confortablement avec ma liseuse. Son analyse de la "basse sunsoft" et l'utilisation du canal 'triangle' pour faire les drums (plutôt que le canal d-PCM comme dans SMB3) était un vrai délice. Du coup, j'ai envie de ressortir cette série de snapshot de l'interview de Junko Ozawa, responsable son et musique sur les jeux arcade Namco.

Some times ago, I found a video featuring an interview of sound designer Junko Ozawa, working at Namco. I was surprised that so early in video game history, there were digital loops send to sound chips. I had in mind that we went square waves -> SID-like waves -> FM chips -> samples playback. Then I found a post by French blogger Upsliandre mentioning old NES sound programming, and he pointed out the term for Junko's technique: wave tables.

Très tôt déjà, alors que les générateurs simplistes de la NES semblaient riches en possibilités et que la FM faisaient ses premiers balbutiements, les machines NAMCO travaillaient déjà avec des échantillons. Enfin presque.

Le hardware était capable de rejouer du contenu par modulation d'amplitude (PCM) sur 16 niveaux (4 bits) mais extrêmement courts ... disons 16 à 32 points par son. Plutôt que d'y envoyer par DMA un son enregistré en live de basse ou de trompette, Madame Ozawa va s'en servir pour comme d'un SID avec des ondes programmables. Des sons totalement synthétiques mais avec une signature unique.

Pendant que Myamoto retranscrivait sur des petits carreaux les traits de Donkey Kong pour faire du pixel art, Madame se livrait au même travail à partir d'un son visualisé à l'oscilloscope pour faire reproduire à son soundchip une "trompette pixelisée" ou un "violon pixelisé".

Unlike what happens with a SoundBlaster, the sound chip of Namco arcade boards isn't streaming digitized samples. Instead, it uses small waveforms made of about 32 4-bit values and plays them as a single loop, again and again. It's much lighter on the 8-bit CPU and memory chips with "slow" clocks: we only have to fill the soundchip's internal register with the sound wave and just update the pitch for the notes we want to play as we would do on a NES soundchip, but we have a wide palette of sounds at our hands.

Somehow, the variety of sounds we can get out of such chips is similar to that of the C64 SID, but the process to have them is different, just like vectrex graphics and NES graphics are different. While Myamoto is converting his Donkey Kong drawings into pixel art by tracing them, Mrs. Ozawa was performing similar pixel-sounds, checking what the instruments look like on the oscilloscope. Possibly easier to do than mixing sines and triangle-likes to get the sound you want ... but less likely to do live phase-modulations as the SID could do.

But power, tone, roundness of the sound is fully customizable. Tower of Duaga got released in 1984 and we are lightyears away from an Atari console. The NES is hardly reaching Pluto in comparison.


La puissance, la tonalité, la rondeur, tout est personnalisable. Ecoutez donc Tower of Duaga de 1984. On est à des années-lumière de la sonorité d'une console atari! et la NES est encore sur Pluton.

Sa bibliotèque de sons, c'est le trésor de Mme Ozawa. Elle les conserve dans un cahier, vu l'absence d'éditeur numérique pour ce genre de choses. Et il faudra bien longtemps avant qu'un synthé ne puisse interpréter en direct les sons imaginés pour le Namco WSG.

[Les wave tables], c'est la base notamment du son PC-Engine dès 87, mais on retrouve ca aussi dans le Famicom Disk en 86 (y a qu'un seul canal mais de bonne résolution, 64x64), dans la Gameboy ou on a une wavetable 32x16 qui remplace le triangle wave de la NES (j'aurais aimé la même chose pour la NES), aussi dans le soundchip SCC de Konami qu'on retrouve dans les cartouches MSX de Konami tel que Contra.
Effectivement, je n'avais pas fait le rapprochement, mais c'est bien la même technique qui permet à la version "Famicom Disk System" de Legend of Zelda d'avoir quelques sons inédits par rapport à sa version NES (dixit Nathan dans "I AM ERROR"). Et on voit bien les patterns un peu exotiques tout en étant très clairement des ondes simples dans la séquence de fin de Link's Awakening.  


Bref, de mon côté (et surtout vu que je suis assez fan des sonorités de Coryoon et Soldier Blade sur PC-Engine), ça me donne furyeusement envie de coder un petit Wave Editor for DS ...

Upsilandre also pointed out that those Wave Tables are the roots of PC-Engine soundchip (1987) -- that's giving this unique vibe to Coryoon and Soldier Blade soundtracks). And that the same trick had been added to the Famicom Disk System in '86, allowing some tricks in the Legend Of Zelda original soundtrack that couldn't be found in the NES version -- as mentioned by Nathan in his 'I AM ERROR' book). Another thing I didn't know was that the GB used that too. Sure, there were GB games that streamed a PCM sample through the 32-sample, 4-bit programmable PCM channel, but in Link's Awakening, it is mostly used as a Namco channel.

Maybe when I'll be a grumpy grand-dad, I'll find time to code a Wave Editor for DS ? Maybe we could morph one wave into another while playing on the DS hardware ? Time will tell.

edit: à suivre dans le post d'Upsilandre: le pixelat audio sur FDS

Wednesday, October 04, 2017

OBJ-WINDOW: Look through the ink

The DS video is made of multiple layers -- this should be no news for you. One of the video registers define which of these layers should be shown on screen, but that register also enables and disable a more obscure feature of the NDS: the windows -- that is, the ability to define multiple regions on screen and give them different set of layers to be shown. When the ink raise in School Rush, I'm not simply painting black squares over the scenery: I actually reduce the window through which the scenery can be seen (with a setup where nothing at all can be seen out of that window).

Petit tour d'horizon dans le document "gbatek" pour voir comment fonctionnent les sprites/fenêtres, une petite particularité de la console DS qui permet de basculer entre deux réglages de visibilité des différentes couches graphiques du jeu. Jusque là, je l'utilisais d'une manière assez simpliste pour dessiner l'encre ou les "rideaux" qui ferment la scène à la fin d'un niveau.

Un simple rectangle dans ce cas-là, qui permet de voir toutes les couches à l'intérieur de la fenêtre et aucune à l'extérieur.

The use I have so far of the "window" feature is pretty basic, but a recent talk with Adrian (GBA developer) made me realise that it would be just perfect to allow the game to give us a hint on where Bilou is when he's swimming up the ink to safety. The trick is that some sprites can be used to create another kind of window. Rather than being rectangular, it can have any shape ... and will be much easier to use than reprogramming the size of the window as we get horizontal interrupts amiga-style.

Pour que l'on puisse voir Bilou nager dans l'encre, il me faudrait donc une deuxième configuration qui laisse voir une partie du niveau tout en gardant assez de noir. Il devrait suffire pour ça de changer un simple bit dans la configuration.

J'hésite un peu quand à la manière d'implémenter ça ... Plus précisément, sur la manière d'indiquer depuis les scripts du jeu que l'on souhaite passer un certain sprite dans le mode "fenêtre". L'idéal serait probablement que l'information soit retenue dans les animations elles-même, mais sans nécessiter de modification de AnimEDS.

En même temps, l'utilisation d'AnimEDS n'est nécessaire que si j'essaie de micro-optimiser et d'éviter un sprite 32x32.

There are a few implementation details I'd like to sort out before implementing that. I already figured out that it should be under the responsibility of *Gob + GobAnim classes, the *Gob being the only class that can manipulate OAM entries (including the OBJ_WINDOW_MODE bits) and GobAnim being a natural place to setup flags indicating that "this appearance of the sprite should be a window (or alpha-blended, or rotated)". I might give it a try with a single 32x32 sprite and dedicated "flags xxx" script entry before I go for something more integrated that could replace the Flicker command currently in use by e.g. inkjets... 

edit: got it working. (by Oct. 14th) Some pixels to edit, now.

Thursday, April 21, 2016

Nathan Am Error

I'm reading a book about the NES: "I AM ERROR" by Nathan Altice. I guess you're not surprised. After all, I've been reading Pix'n'Love take on Donkey Kong, "L'Histoire de Mario" and "the Playstation Revolution". But this book is published at MIT press, and written by someone who has a thesis in arts, not an engineer or a game reviewer.

Yet, he got deep into nitty-gritty details about how tiles are used to build the images produced by the PPU, and how game developers and Nintendo themselves tried and extended the features of the NES as competing micro-computers were trying to bridge the technological gap.

Quelque-part entre les chroniques de la saga Zelda et "la révolution playstation", je vais bientôt ranger un outsider: "I AM ERROR" par Nathan Altice. Outsider parce que ce livre-ci a été pensé comme un ouvrage de référence universitaire par un expert d'une faculté "arts & culture" qui va étudier en détail en quoi les limites de la NES ont influencé les gens qui ont créé dessus, leurs oeuvres et les gens qui ont découverts leurs oeuvres, un peu comme on pourrait écrire un livre sur la technique des pigments dans les peintures de la renaissance et l'impact que le changement de matériel pour les pinceaux a eu sur l'oeuvre de Léonard de Vinci. Ici, bien sûr, il sera plutôt question du nombre de couleurs et de tiles, des possibilités de scrolling puis des puces et astuces que les développeurs de jeu et de chez Nintendo ont utilisé pour faire des histoires toujours plus grandes, dans des environnements toujours plus vastes .

I found it pretty surprising that the first generation of NES game had to be soldered for either horizontal and vertical scrolling. Then, cartridge started to include chips that would allow to switch between the two, like with the custom "UNROM" in Mega Man and the official "MMC1" in subsequent Mega Man games. Of course, I already knew that some cartridges featured more than mere ROMs, be it only for the SuperFX in StarFox. But the NES mappers had more arcane things to do and were not that much advertised. And they came as a second choice, as the initial extension roadmap was the disk system and its video RAM where the PPU expected to find tiles and sprites ROM.

En découvrant les maps des jeux Capcom sur NES, j'avais déjà supposé que la console imposait le choix du sens de scrolling: horizontal ou vertical. J'étais loin d'imaginer que pour faire un jeu tel que Megaman qui alterne phase horizontale sur phase verticale, il aura fallu bricoler les cartouches et leur rajouter une puce de contrôle, parce que Super Mario Bros, par exemple, a soudé le choix d'un scrolling horizontal. Toutes ces puces -- les "mappers" -- sont bien plus fréquentes que le SuperFX de la superNES, mais pourtant il n'en a presque jamais fait mention. C'est un détail pour le joueur, presqu'un secret de fabrication. Un secret peu avouable pour les développeurs qui avaient au départ misé sur le "famicom disk system" qui remplaçait la ROM graphique de la cartouche par une RAM remplie à partir du contenu d'une diskette. Pourtant, c'est bien en ajoutant une puce de RAM que Super Mario 3 est capable de retenir quels blocs ont été cassés (la NES n'ayant que 2KB!, à peine de quoi retenir le contenu de 8 écrans).

Still, if you think about it, Super Mario Bros. was already using the full range of pixels that the PPU could use. Embedding more graphics in the game is useless without a technique that could change which 16KiB slice was used. I suspected such techniques existed by 1998 when I first saw nesticle's video "pattern table" window contents updating as SMB3 played out. Those techniques are reminiscent of memory-paging techniques already in use on mainframes by then, but I was far from imagining the level of sophistications those chips could have, having different page sizes depending on the game's need regarding animation or environment diversity, ultimately embedding even additional sound chips and ways to offer real free, 4-direction scrolling.

Ce n'est qu'à travers ce genre de ruse -- pagination pour passer de l'overworld de Zelda à un des donjons, copie à la volée de sprites présents dans la ROM-programme vers la nouvelle RAM auquel le processeur graphique (PPU) a accès -- que les développeurs NES vont pouvoir faire mieux et plus riche que Super Mario Bros, qui saturait déjà les 16KB de mémoire que le PPU sait utiliser. Ici aussi, j'avais soupçonné ce genre de technique en voyant tourner SMB3 dans Nesticle, mais j'étais loin de me douter de la diversité des approches, des subtilités ajoutant des nouveaux systèmes d'interruptions sur une ligne précise de l'écran (devenue partie intégrante des designs de Nintendo depuis la SNES) ou l'intégration d'une puce sonore additionnelle que la NES utilisera comme elle aurait utilisé "l'entrée micro" du 2eme contrôleur de la Famicom.

En revanche, "comment on fait tenir un niveau sur 100 bytes" restera une curiosité dans ce livre. Dommage pour moi. Heureusement - j'imagine - pour la majorité des étudiants qui commandent le livre sur MIT Press...

I'm just slightly disappointed that "how SMB level were packed to a mere hundred of bytes" is the sole sequence that reveal how NES games were created with such a level of detail. I might have to dig for Megaman disassemblies ...

Saturday, October 25, 2014

Automated OAMs management

Over the summer, I crafted plans for managing larger levels, that would use more than 128 hardware sprites (the OAMs in DS parlance) for all the monsters. Some of those plans use the notion of a "Gob Group", that could be used to spawn several objects at once, at level-specific locations by having them statically placed (unlike shoot-able "dyngobs") but wouldn't be active unless their group is enabled.

Browsing through the game engine code for another purpose on my cybook, it struck me that it would be so easy to have lazy allocation of the hardware sprites as we approach on-screen area and early recycling of those OAMs when we're going far enough from that area. It's now coded. Funny enough, it doesn't affect activation and movement of GOBs in any way: only whether they have hardware sprites assigned to them or not.

done: find a way for Bilou to keep its priority (displays over monsters)

Thursday, June 26, 2014

DarkneSs & Lime ?

Qui dit juillet dit matos qui foire. Je dois renvoyer mon Cybook en SAV (mémoire flash interne défectueuse, je pense), ma chaîne hi-fi ne veut plus lire les CD et mes deux DS lite "DarkneSs" et sa remplaçante "Green Lime" ont disparu dans un repli du sub-espace. J'avais espéré parvenir à re-formater une carte µSD 4Go pour remettre mon iPlayer en service ... même ça, ça coince >_<.

*sigh* Its hardware failure time. I lost the CD player of my HiFi system. I lost the storage of my Cybook. I cannot find back my development DS (green lime) neither its "backup" clone DarkneSs ... The SD-µSD adapter is failing me, I can't make the iPlayer work again with that newer µSD card ... *über-sigh*

J'ai le blues, là.


Sunday, April 14, 2013

Start+R+A

Reprise du boulot oblige, la progression de cette semaine est moins impressionnante, mais peut être tout aussi importante. Si DaySi a brillamment repris le flambeau depuis la chute de DarkneSs, elle se fait de plus en plus capricieuse avec son bouton R. Oh, il fonctionne, pas de soucis de ce côté ... mais il n'est plus très fiable. Il peut être nécessaire de pas mal le titiller pour déclencher quelque-chose, et quand il se décide, il n'est pas rare d'obtenir des "rebonds" du genre "on/off/on/off" en une seule pression du doigt.

Pressing Start+R+A or Start+R+R in SEDS lets you chose between "Save" or "Save As", changing the way a back-up copy of your file is created. When your 'R' button may automatically read as "R+R+R" for a single press, it quickly becomes unmanageable to have coherent file history. So I finally took the time to add some LOAD and SAVE clickable buttons (plus a tool option to invoke cursor mode in SEDS from the stylus) to compensate hardware deficiences of the DS.

Dans la mesure où ce bouton contrôle la "sauvegarde" dans SEDS et AnimEDS, c'est plutôt génant. A plusieurs reprises, j'ai du re-transférer d'anciennes versions de mes livres pendant que j'en faisais la mise à jour pour corriger une réécriture intempestive alors que je cherchais juste à activer le mode curseur (lui aussi déclenché par R). Mais voici donc une mise à jour de mes outils qui offrent des boutons cliquables (au stylet, donc) pour compenser ce genre de problème. A noter que le bon fonctionnement du bouton L, lui, reste incorrigiblement critique.


Friday, April 05, 2013

LEDS update

    Mon filieul (12 ans) -- de passage chez moi -- s'est assez rapidement désintéressé de ses projets de monorail LEGO et m'a laissé entendre qu'il avait son AceKard 2 en poche pour que je lui fasse une mise à jour d'Apple Assault. Après des semaines passées sur Minecraft DS, il était mûr pour se lancer dans du dessin de niveaux et j'en ai donc profité pour lui transmettre l'intégralité de mes outils de développement, avec les spritesheet dernier cri... pour me rendre compte qu'il y avait un très désagréable "guru meditation" au moment de charger les derniers niveaux (variable non-initialisée dans TileTable).

    Je contourne le problème en lui installant plutôt la dernière version "stable", et après avoir essayé les techniques de contre-attaque d'Apple Assault, le voilà lancé dans la réalisation de "Thom.map". Il maîtrise plutôt bien les techniques de base pour placer des blocs sur la map, et a rapidement pigé le fonctionnement du "plan de collision". Le voilà donc qui place quelques blocs-code pour que ses crayons blessent et s'émerveille devant son premier niveau qui prend vie.
      Mon seul regret aura été de ne pas avoir pu lui permettre de placer des monstres sur sa map. J'ai un couac avec l'édition des monstres dans LEDS. Ils apparaissent complètement tordus malgré mes meilleures tentatives pour importer les "miniatures" d'AnimEDS. Après une série de tests en émulateur ce matin, j'arrive à la conclusion désagréable que le SpriteSet est trop grand pour pouvoir accueillir les 48 miniatures d'animation (1Ko chacune) ajoutées au sprites de base.
      One of my lil' nephews asked me to install my map-design tools on his DS earlier this week. And this led to some more flaws to be identified in the version of LEDS I shipped on my birthday. After I fixed a guru meditation error on level loading, I still had to figure out why monsters thumbnails couldn't work right. It's a bit tricky to tell a 12-year-old "don't trust those pictures you see in the level: this is a blador, and that's a sponge bop although they both look like shrunk-down version of Bilou".

      C'est un peu ridicule, parce qu'il n'y a pour l'instant qu'une dizaine d'états initiaux pour Bilou et les 3 monstres de la school zone (spons, blador et inkjet). Mais l'éditeur de niveaux ignore tout ça et tente de faire tenir toutes les miniatures d'animation plus toutes les images individuelles (des fois qu'une animation à l'ancienne les utiliserait). Je pourrais bien sûr rationaliser tout ça et faire en sorte que chaque byte en VRAM soit utile dans l'éditeur ... Mais si la DS n'est pas capable de traiter plus de 1024 images différentes pour les sprites, elle est en revanche parfaitement capable de traiter des sprites plus gros. Jusqu'ici, j'utilisais DISPLAY_SPR_1D_SIZE_64, soit 64 bytes par sprite (8x8 pixels ?). En passant à DISPLAY_SPR_1D_SIZE_128, je perds la possibilité d'utiliser des micro-sprites, mais je peux faire tenir mes 64K d'images dessinées dans SEDS plus jusqu'à 64K de miniatures rajoutées par AnimEDS sans compromis notable puisque SEDS travaille au minimum avec des sprites de 16x16 (donc DISPLAY_SPR_1D_SIZE_256 pourrait même s'envisager).

      After sufficient time was invested into documenting the multi-pass parsing of the command files in the editor, a lunch-testing-session revealed the naked truth: the amount of sprites supported by the video hardware was once again the root cause of the issue. More precisely, it wasn't an issue with the amount of VRAM (the game engine accepts up to 64K of sprites and all the thumbnails won't take more than 48x1KB) nor with the number of objects the hardware can handle, but merely with the number of individual graphics that can be addressed by the OAMs.

      Hopefully enough, it was a mere matter of re-configuring the GPU so that it uses coarser indexing. So far I could precisely point at a single tile (8x8 pixels) in sprite memory, which only used the first 64KB. Yet, it's possible to use up to 16x16 pixels per index (the size of the smallest sprite generated by SEDS. Even with an intermediate and conservative setting of DISPLAY_SPR_1D_SIZE_128, I immedately recover the ability to preview the monsters on the level. Too bad: my nephew is gone :P

      But that's not too much of an issue: as soon as his DS gets in range of a friendly WiFi, all he's got to do is launch his current version of LEDS, and press SELECT while holding L+R on the welcome screen (instead of clicking <go>. That should grab ledsgama.nds, the latest build.


      Seuls les pieds et les mains de Bilou tiennent dans une taille de 8x8 à l'heure actuelle, et ils ne représentent que 12 sprites sur les 1024 autorisés. Une perte d'espace probablement acceptable. Si nécessaire, je pourrais envisager de les transférer en 8x16 plutôt qu'en 16x16 ... on verra ça une fois que je saurai quelle quantité de mémoire vidéo la 3D me demandera ...
      • [done] fix guru meditation when entering edit mode
      • [done] keep VRAM for thumbnails
      • [done] properly render monsters
      • [todo] ignore duplicate GOB entries when reading a level
      • [done] ensure we don't generate duplicate entries.
      • [done, runme] ensure we can read the log as soon as there's an error.
      • [todo, runme] ensure we can move back to SEDS/LEDS/download mode at all time.
      • [wish] "generate .cmd" wizard window.
      • [done] clearer view of which file is loaded/saved/renamed

      Sunday, September 16, 2012

      Linker de Bisounours pour DaySi

      Yes! ça y est! Grâce au matos gracieusement donné par ZeBlackOS -- à savoir un linker de bisounours iplayer -- je peux enfin faire tourner mes homebrews sur la DSi rachetée à Mr. Proper l'an dernier! Vu qu'elle avait même un chargeur de poche pour brancher sur port USB, ç'aurait vraiment été dommage de la cantoner à faire tourner Zuma Revenge pour ma fée :P

      Il pourra se vanter de m'avoir fait suer, note, au point que j'ai fini par craindre de l'avoir mis hors-circuit au moment de lui faire son "upgrade kernel" ... mais non. C'était tout simplement un problème de formattage. Si les gars de SuperCard insistent sur le fait que la carte microSD soit formattée avec leur outil dédié sous Windows, ils ont en fait une bonne raison: la mémoire de la DS étant limitée, il faut que la table d'allocation puisse tenir dedans.

      If you just found an iPlayer, but that it seems to stall after showing a blue logo screen but without going to the screen that says "DS Video Expert", chances are that you overlooked the vendors' recommendation for using their own formatting tool for your media microSD card. Granted, that sounds plain ridiculous. Every OS (decent or not) comes with a FAT formatting tool, and you shouldn't have to install third party to do that, and certainly not on a Microsoft OS!.

      Still, as the DS has limited memory, and given that using their formatting tool *did* unleash the linker's full potential, I can imagine the following scenario: the FAT driver of the linker's firmware is so crude that it *needs* to fit the whole FAT within 1Mo of RAM. A conventional formatting tool will instead try to come with settings that minimizes cluster size, so that you're doing the most of your filesystem... and dismiss FAT size as a significant issue.


      En l'occurence, ici, la carte de 8Go a été formattée avec des clusters de 32Ko (ce qui est modérément large, mais large quand-même), de sorte que la FAT soit juste un rien plus petite que 1Mo ...
      (Bien sûr ça pourrait être un autre détail: taille du répertoire racine, le nombre de clusters réservés ...)

      En toute logique, j'aurais pu obtenir l'effet souhaité sous Linux avec mkdosfs -s 64 -F 32 -f 2 -h 8192 ... Mais pour une autre taille de carte SD, il faut ajuster le paramètre -s en conséquence (#Go*8, je dirais).

      Here's the result of a filesystem analysis after supercard's tool "correctly" formatted my 8GB media card for iPlayer:

      SchoolTest> sudo dosfsck -n -v /dev/mmcblk0p1
      Boot sector contents:
      System ID "        "
      Media byte 0xf8 (hard disk)
         512 bytes per logical sector
       32768 bytes per cluster
        4404 reserved sectors
      First FAT starts at byte 2254848 (sector 4404)
           2 FATs, 32 bit entries
      969728 bytes per FAT (= 1894 sectors)
      Root directory start at cluster 2 (arbitrary size)
      Data area starts at byte 4194304 (sector 8192)
      242304 data clusters (7939817472 bytes)
        8192 hidden sectors
      15515648 sectors total
      dosfsck /tmp/disk.img
      Boot sector contents:
      System ID "mkdosfs"
      Media byte 0xf8 (hard disk)
           512 bytes per logical sector
          4096 bytes per cluster
            32 reserved sectors
      First FAT starts at byte 16384 (sector 32)
             2 FATs, 32 bit entries
       3992576 bytes per FAT (= 7798 sectors)
      Root directory start at cluster 2 (arbitrary size)
      Data area starts at byte 8001536 (sector 15628)
        998046 data clusters (4087996416 bytes)
             0 hidden sectors
       8000000 sectors total
      
      8GB formatted with special tool4GB formatted with mkdosfs under Ubuntu

      Pourquoi "de bisounours" ? Eh bien, parce que ce linker permet de faire tourner les jeux amateurs (dits "homebrew"), mais refusera de lancer les ROMs commerciales. Merci, tata Raymonde. Et merci à Pierrick de m'avoir filé un coup d'EEE main, sur ce coup-là. Et bien sûr Merci à ZeBlackOs pour le linker ^_^
      My last fear is gone, btw: although the linker's documentation mention directories such as MUSIC, VIDEO and HOMEBREW where we're supposed to place our files, it can still launch files that are at the root (e.g. beta versions of my tools) and that have been downloaded by runME (currently in /moving/) Mais du coup, je m'interroge: les difficultés que j'ai à faire tourner de manière fiable mes outils avec ce matériel pourraient-elles être liées à une libfat pas vraiment calibrée pour des clusters de cette taille ?

      Saturday, September 08, 2012

      VRAM_E_LCD

      I previously messed up with the sources of "Tetris Attack" to gain experience with DS development. Among the things Sten used, there was multi-palette display, using an extra bank of VRAM instead of the "regular" PALETTE_BG and PALETTE_SPRITE.

       videoSetMode(MODE_0_2D |
      DISPLAY_BG0_ACTIVE | /* the background */
      // ... more settings
      DISPLAY_BG_EXT_PALETTE);
      // 128K
      vramSetBankA(VRAM_A_MAIN_BG);
      // 128K
      vramSetBankD(VRAM_D_MAIN_BG_0x6020000);
      vramSetBankB(VRAM_B_MAIN_SPRITE);

      The video mode initialisation we have here is fairly standard (note the old-time code with VRAM addresses still exposed to the homebrew coder :P), except for DISPLAY_BG_EXT_PALETTE flag. You can turn extended palettes on/off separately for sprites/backgrounds, or for top/bottom screens, but if you enable it for background, it's used for *all 256 colors backgrounds*. Period.

        // 64K, can hold palettes for all four BG layers
      // allocate for cpu access
      vramSetBankE(VRAM_E_LCD);

      // background
      Decompress((void*)(0x6880000), background_pal_bin);

      // blocks
      Decompress((void*)(0x6880000 + 256*16*2), sprites_pal_bin);
      // an alternate palette with colors dimmed so that we can
      // easily dim the whole playfield when the game is over...
      CreateShadedPalette((u16*)(0x6880000 + 256*17*2),
      (u16*)(0x6880000 + 256*16*2));

      This magic 0x6880000 memory address sits within the 'LCD-mapped' region and is where VRAM bank 'E' appears. Right then, it's "offscreen", somehow, just ready for setup by the CPU. Each palette is 256x2 bytes long, and each background will have access to its 16 palettes at fixed offset. In this game, we had the backdrop on BG0 (thus palette at 0x6880000 to 0x6881E00 = 0x6880000 + 512*15), "blocks" (or apples) on BG1 that has its first palette at offset 512*16 (0x6882000) and its second palette at offset 512*17 (0x6882200). That makes lot of room unused (15 unused palettes for BG0 and 14 unused palettes for BG1), but if you want them to use different palettes (e.g. being converted independently from each other), you don't really have a choice.

        // text
      Decompress((void*)(0x6880000 + 256*16*2*2), font_pal_bin);
      // more background...
      Decompress((void*)(0x6880000 + 256*16*3*2), singlebackground_pal_bin);

      Score and status are displayed with a tiled layer too, implying yet another palette is needed. He could have used a macro XPALETTE(bg,slot) defined as ((void*)(0x6880000 + 256*(16*bg+slot)*2), or (possibly), use bank F/G forcing BG 0 and 2 to share the same 16 slots, but ensuring that those slots in use by BG2 are never needed by BG0 and vice-versa.


      // then allocate it for extended palette use
      vramSetBankE(VRAM_E_BG_EXT_PALETTE);

      Enough setup: we let the video hardware access those fancy colours ^_^