BricoNeo_2025 = { .upgrade = true };

Publié le par illusionrip

Article par Bouz

source: Article sur Delta Island

Hello !

Je me permets de faire un gros update sur les avancées plus ou moins récentes concernant le BricoNeo, car il n’a pas l’air d’avancer beaucoup vu de l’extérieur, alors qu’il se passe pas mal de choses en coulisse. Attention, pavé en approche, mais j’ai mis des images pour rendre ça plus agréable à lire !

Tout d’abord, sachez qu’il y a trois exemplaires de la carte en circulation. L’un est chez moi pour le développement, et les deux autres sont chez mes amis @Illusionrip et @ragefan, qui testent les différents patchs et évolutions. Ils en profitent pour faire du cheerleading (par exemple : “Ohhhhh !!!” ou “Ahhhhh…”) pour me garder motivé.

@ragefan contribue également au développement de la ROM de test côté NeoGeo, on en reparlera plus bas !

Génération de menus

Pour commencer, voici un bout de projet que vous aviez déjà vu et qui m’a permis d’avancer un peu sur le développement de la carte BricoNeo : le menu pour les 161in1. L’idée est de proposer une alternative au menu d’origine, qui est assez peu esthétique.

Cela a été l’occasion de développer plusieurs fonctionnalités pour le BricoNeo, notamment la possibilité d’envoyer des commandes au microcontrôleur depuis le code 68000. C’est ce qui permet de changer de jeu sur la cartouche et de basculer sur un Unibios dans la foulée.

Petit retour sur le processus de création d’un écran de menu :

Tout commence par un patch sur MAME pour comprendre comment fonctionnent les savestates. À partir de là, je prends le savestate que je veux sur un jeu NeoGeo…

------------------------------------------------------------------------------------------

Hello!

I’m giving you a major update on the more or less recent progress regarding BricoNeo because, from the outside, it might not seem like much is happening, while in reality, a lot is going on. Beware, incoming wall of text—but I added images to make it more appealing!

First of all, there are three copies of the board out in the wild. One is with me for development, and the other two are with my friends @Illusionrip and @ragefan, who are testing various patches and improvements. They also help with some cheerleading (for example: “Ohhhhh!!!” or “Ahhhhh…”) to keep me motivated.

@ragefan is also contributing to the development of the test ROM on the NeoGeo side—we’ll talk more about that later!

Menu Generation

To start, here’s a piece of the project you’ve already seen, which helped me move forward with the development of the BricoNeo board: the 161in1 menu. The idea is to offer an alternative to the original menu, which isn’t very visually appealing.

This was an opportunity to develop several features for BricoNeo, including the ability to send commands to the microcontroller from the 68000 code. This is what allows the cartridge to switch to another game and seamlessly transition into Unibios.

A quick rundown of the process of creating a menu screen:

It all starts with a MAME patch to understand how savestates work. From there, I take the savestate I want from a NeoGeo game…

 

BricoNeo_2025 = { .upgrade = true };

Puis-je l’ouvrir dans mon outil maison pour recombiner les sprites comme je le souhaite à partir de plusieurs savestates (et éditer le fix layer) ?

------------------------------------------------------------------------------------------

Can I open it in my custom tool to recombine the sprites as I want from multiple savestates (and edit the fix layer)?

 

BricoNeo_2025 = { .upgrade = true };

Derrière, mon outil peut être appelé en ligne de commande depuis le Makefile de mon projet 68000 sous VS Code, pour générer les données des sprites (tiles, positions, palettes, fix…) sous forme de données en assembleur. Cela permet de les référencer facilement dans le code par la suite.

------------------------------------------------------------------------------------------

Behind the scenes, my tool can be called via the command line from the Makefile of my 68000 project in VS Code to generate sprite data (tiles, positions, palettes, fix…) in assembler data format. This makes it easy to reference them in the code later.

BricoNeo_2025 = { .upgrade = true };

D’un appui sur une touche (F7, en l’occurrence), le code est assemblé et transféré sur le BricoNeo, offrant un résultat en seulement 2 secondes sur le matériel d’origine.

------------------------------------------------------------------------------------------

With a single key press (F7, in this case), the code is assembled and transferred to the BricoNeo, delivering a result in just 2 seconds on the original hardware.

 

BricoNeo_2025 = { .upgrade = true };

Effet surprise que je n’avais pas prévu : comme je récupère toutes les informations de la VRAM depuis le savestate, je récupère aussi les données d’animation des décors. Cela permet de faire des choses assez sympas comme ça, sans écrire de code supplémentaire (si le GIF passe)…

------------------------------------------------------------------------------------------

Unexpected surprise: since I retrieve all the VRAM information from the savestate, I also get the background animation data. This allows me to do some pretty cool stuff like this without writing any extra code (if the GIF works)…

BricoNeo_2025 = { .upgrade = true };

Amélioration de la ROM de diagnostic

Et pendant ce temps-là, l’ami @ragefan développait un patch pour la ROM de diagnostic du BricoNeo. Une faiblesse de mon code de test de la VRAM était qu’il n’était pas très accessible lorsqu’une erreur survenait.

Il écrivait l’adresse en défaut, la donnée attendue et la donnée lue dans la WRAM, obligeant l’utilisateur à dumper trois words de celle-ci pour récupérer les informations sur l’erreur. Non seulement ce n’était pas pratique, mais en plus, cela impliquait d’avoir une WRAM fonctionnelle, ce qui est en contradiction avec l’esprit du BricoNeo : diagnostiquer des parties de la NeoGeo alors que presque rien ne fonctionne.

La dernière version en date intègre donc un joli patch qui affiche, comme les ROM NeoDiag et Unibios, toutes les informations nécessaires pour avancer dans le diagnostic.

Merci @ragefan, c’est top !

------------------------------------------------------------------------------------------

Diagnostic ROM Improvement

Meanwhile, our friend @ragefan was developing a patch for the BricoNeo diagnostic ROM. A weakness of my VRAM test code was that it wasn’t very user-friendly when an error occurred.

It would write the faulty address, the expected data, and the read data into the WRAM, requiring the user to dump three words from it to retrieve the error information. Not only was this inconvenient, but it also required functional WRAM—contradicting the very purpose of BricoNeo: diagnosing NeoGeo components when almost nothing is working.

The latest version now includes a nice patch that, like the NeoDiag and Unibios ROMs, displays all the necessary information to progress with the diagnosis.

Thanks @ragefan, this is awesome!

BricoNeo_2025 = { .upgrade = true };

Un debugger sur NeoGeo !

Et voici maintenant un développement qui m’a pris pas mal d’énergie pour un résultat à l’utilité discutable…

À la base, l’idée du BricoNeo est de comprendre ce qui se passe sur les cartes NeoGeo. Rien de plus frustrant que de devoir deviner ce qui se passe sur la machine à partir d’informations parcellaires.

Seulement voilà : lorsqu’on développe une ROM système, on n’a pas forcément les compétences, les outils ou l’environnement pour la debugger sous émulateur, comme on pourrait le faire pour un jeu vidéo basé sur une cartouche. Et moi, je voulais vraiment pouvoir debugger des choses !

En tombant par hasard sur le flag “Trace” du registre d’état du processeur 68000 dans une documentation technique, j’ai décidé de comprendre comment il fonctionnait.

Voici donc une ébauche de debugger, qui permet d’afficher l’état des registres ainsi que l’emplacement mémoire de l’instruction en cours de lecture.

Ici, on voit en vis-à-vis le code compilé, qui correspond à une boucle, et la version en cours de debug sur un slot MVS via le BricoNeo. On peut observer que le code place la valeur 3 dans le registre D0, puis la décrémente dans une boucle. La trace affiche les valeurs successives de D0, jusqu’à la bascule en négatif.

------------------------------------------------------------------------------------------

A Debugger on NeoGeo!

And now, here’s a development that drained a lot of my energy for a result of somewhat debatable usefulness…

The whole idea behind BricoNeo is to understand what’s happening on NeoGeo boards. There’s nothing more frustrating than having to guess what’s going on inside the machine based on incomplete information.

However, when developing a system ROM, you don’t necessarily have the skills, tools, or environment to debug it using an emulator, like you would for a cartridge-based video game. And I really wanted to be able to debug things!

While randomly stumbling upon the “Trace” flag in the 68000 processor status register within a technical document, I decided to figure out how it actually worked.

So here’s an early version of a debugger that displays the state of the registers and the memory location of the instruction being read.

Here, we can see the compiled code, which is a loop, and the live debugging output on an MVS slot using BricoNeo. The code sets the value 3 in register D0 and decrements it within a loop. The trace shows the successive values of D0, right up to when it flips to negative.

BricoNeo_2025 = { .upgrade = true };

OK, c’est un peu pratique, mais il faut savoir lire les opcodes sous forme hexadécimale. Et personnellement, je ne sais pas faire.

Je me suis donc lancé dans l’écriture d’un désassembleur pour le BricoNeo. Écrit en C++, il est conçu pour fonctionner sur un microcontrôleur RP2040 avec moins de 100 Ko de RAM. Spoiler alert : il est impossible d’utiliser des solutions existantes comme MAME, car elles consomment beaucoup trop de mémoire.

L’objectif de ce désassembleur n’est pas d’être ultra-rapide, mais plutôt de limiter autant que possible la consommation d’espace de stockage et de RAM.

Qu’à cela ne tienne → direction la documentation technique du 68000, et je me suis farci l’intégralité des opcodes ainsi que (quasi ?) tous les modes d’adressage du 68000.

Résultat : c’est beaucoup plus agréable de suivre l’exécution du programme ainsi ! On peut voir l’instruction qui va être exécutée ainsi que les suivantes. C’est aussi l’occasion d’observer les ajustements subtils effectués par VASM, qui transforme les JSR en BSR, le fourbe ! (Mais il a raison).

------------------------------------------------------------------------------------------

OK, it’s somewhat useful, but you need to be able to read opcodes in hexadecimal. And personally, I don’t know how to do that.

So, I started writing a disassembler for BricoNeo. Written in C++, it targets a RP2040 microcontroller with less than 100 KB of RAM. Spoiler alert: you can’t really use existing solutions like MAME because they consume way too much RAM.

The goal of this disassembler isn’t to be blazing fast but rather to minimize storage and RAM usage.

No problem → I turned to the 68000 technical documentation and manually went through the entire opcode set as well as (almost?) all 68000 addressing modes.

The result: following the program execution is way more pleasant this way! You can see the instruction about to be executed along with the next few instructions. It’s also a great way to observe VASM’s sneaky optimizations, like turning JSR into BSR! (And to be fair, it’s right to do so).

BricoNeo_2025 = { .upgrade = true };

Un debugger UTILISABLE sur NeoGeo !

Hier après-midi, je me suis dit que debugger une ROM système perso, c’est cool, mais que ce serait encore plus cool de pouvoir debugger une ROM système SNK ou un Unibios.

J’ai regardé comment étaient déclarés les vecteurs d’interruptions de trace, et il semblerait que toutes les ROM système et certains jeux (je n’ai pas fait de stats précises, mais KOF97 le fait, évidemment) déclarent ce vecteur de trace. C’est très probablement un vestige du développement des jeux.

En gros, pour faire de la trace, il faut trois éléments :

 Une entrée dans le vecteur d’interruption de trace, à la fois dans la ROM système et dans la ROM des jeux (sinon, on ne peut plus tracer une fois que la ROM système effectue un bank switching des vecteurs vers ceux de la cartouche #charabia).

 Du code stub pour gérer l’interruption et, dans mon cas, échanger avec le BricoNeo.

 Le plus important : un bout de code quelque part qui active le mode trace du 68000 en bricolant le registre d’état du processeur.

Quand je bosse sur ma propre ROM, pas de souci :

J’ai un petit fichier assembleur “Trace.s” à ajouter au projet, qui produit 174 octets de code nécessaires aux échanges 68000 ↔ BricoNeo pour exécuter du pas-à-pas. J’ajoute une référence à la routine de trace dans les vecteurs d’interruption de la ROM, et j’utilise une macro “Breakpoint” qui bascule simplement le flag Trace du 68000.

Mais ici, c’est différent. Je n’ai pas la main sur les vecteurs, ce n’est pas mon code qui tourne dans la ROM (donc ma routine de trace n’y est pas), et les jeux n’activent pas naturellement le flag Trace !

La solution technique en bref (je peux détailler si quelqu’un suit encore) :

 Les ROM système courantes (SNK ou Unibios, ce dernier étant un hack d’une ROM SNK) définissent toutes un vecteur de trace. Certains jeux aussi. Ce vecteur pointe toujours vers la même adresse dans la ROM. À cette adresse, il y a du code inutilisé, qui commence par un saut vers une autre section de la ROM. Je vais donc intercepter ce saut et le patcher pour qu’il redirige vers ma propre routine !

 OK, mais où stocker ma routine de trace ? Les ROM système contiennent des Lookup Tables (LUT), des tables de valeurs en dur utilisées pour plein de choses : ramps non linéaires, tables de sinus, suites pseudo-aléatoires… C’est cette dernière qui m’intéresse : elle fait 256 octets et ne contient que des données inutiles. C’est donc un excellent candidat pour accueillir ma routine de 174 octets !

 D’accord, on a maintenant un vecteur d’interruption qui pointe sur notre routine. Mais comment déclencher cette interruption ? En regardant où pointe le vecteur d’interruption RESET, on tombe sur le code exécuté au démarragede la machine. La première instruction est toujours la configuration du registre d’état du 68000. Il suffit donc de patcher cette première instruction pour activer le mode trace !

 Mais alors, on doit patcher les ROM système à chaque fois qu’on les utilise ? Ce n’est pas super pratique…

 Non, le BricoNeo le fait automatiquement à la volée quand on lui demande de passer en mode debug. Il reconnaît la ROM chargée en mémoire et la patche dynamiquement.

Résultat : on obtient la séquence suivante, où l’on observe le patchage et le démarrage du debug sur Unibios + KOF97!

------------------------------------------------------------------------------------------

A USABLE Debugger on NeoGeo!

Yesterday afternoon, I thought debugging my own system ROM was cool, but it would be even cooler to debug an SNK System ROM or an Unibios.

I looked into how trace interrupt vectors are declared, and it turns out that all system ROMs and some games (I didn’t run full stats, but KOF97 does this, of course) declare this trace vector. This is most likely a leftover from the development of the games.

To perform tracing, you need three things:

 A trace interrupt vector entry in both the system ROM and the game ROM (otherwise, as soon as the system ROM bank-switches the vectors to the cartridge, you lose tracing ability #gibberish).

 A stub code to handle the interrupt and, in my case, exchange data with BricoNeo.

 The most important part: some code somewhere that enables 68000 trace mode by tweaking the status register.

When working on my own ROM, no problem:

I have a small “Trace.s” assembler file to add to the project, which produces 174 bytes of code necessary for 68000 ↔ BricoNeo step-by-step execution. I add a reference to the trace routine in the ROM’s interrupt vectors, and I use a simple “Breakpoint” macro to flip the 68000 Trace flag.

But here, things are different. I don’t control the vectors, it’s not my code running in the ROM (so my trace routine isn’t in it), and games don’t activate the Trace flag by default!

The quick technical breakdown (I can elaborate if anyone’s still following):

 Common system ROMs (SNK or Unibios, the latter being a hack of an SNK ROM) all define a trace vector. Some games do as well. It always points to the same address in the ROM. At that address, there’s unused code, which starts with a jump somewhere in the ROM’s code. I’m going to intercept this jump and patch it so that it lands in my own routine!

 OK, but where do I store my trace routine? System ROMs contain lookup tables (LUTs) with hardcoded reference values for various tasks: non-linear ramps, sine tables, pseudo-random number sequences… This last one is perfectbecause it’s 256 bytes of essentially junk data. That makes it a great candidate to store my 174-byte routine!

 Alright, so now we have an interrupt vector pointing to our routine. But how do we trigger this interrupt? By checking where the RESET vector points, we find the startup code executed when the machine boots. The first instruction is always configuring the 68000 status register. So we just patch this first instruction to enable trace mode!

 But wait, do we have to patch system ROMs manually every time? That doesn’t sound practical…

 No, BricoNeo does it automatically on the fly when switching to debug mode. It detects the ROM in memory and patches it dynamically.

The result: we can now observe patching in action and debug startup on Unibios + KOF97!

BricoNeo_2025 = { .upgrade = true };

BricoNeo, ça sert à réparer : une sonde logique / fréquence-mètre

Un autre développement, nettement moins énergivore, mais bien plus utile pour les bricoleurs, réparateurs et diagnostiqueurs : une sorte de sonde logique / fréquence-mètre. L’idée est de pouvoir contrôler facilement les fréquences fournies par les différentes puces et vérifier l’activité sur les lignes de bus ou les signaux d’activation des puces, le tout sans oscilloscope.

Évidemment, ce n’est pas aussi précis qu’un oscilloscope, car cela ne montre pas les conflits de bus, mais c’est beaucoup moins cher et permet déjà de faire pas mal de choses !

L’idée ici est d’utiliser une section PIO (Programmable Input/Output) qui n’est pas utilisée par le BricoNeo dans le cadre de son émulation de ROM, pour en faire quelque chose d’utile.

Étant donné qu’on ne peut pas vraiment debugger les programmes PIO et que le langage est un peu barbare, je me tourne vers Wokwi, un super outil qui permet de tester des conceptions en ligne et de tracer l’activité des registres des machines à états PIO.

Dans cet essai, j’injecte un signal PWM sur une broche du RP2040, et je tente de compter combien de pulsations par seconde elle reçoit.

------------------------------------------------------------------------------------------

BricoNeo is for Repairs: A Logic Probe / Frequency Meter

Here’s another development—far less energy-consuming but way more useful for tinkerers, repairers, and diagnosticians: a logic probe / frequency meter. The idea is to easily check the frequencies provided by different chips and monitor activity on bus lines or chip activation signals, all without needing an oscilloscope.

Of course, it’s not as good as an oscilloscope since it doesn’t show bus conflicts, but it’s much cheaper and still quite handy!

The idea here is to use a PIO (Programmable Input/Output) section that BricoNeo isn’t using in its ROM emulation, to put it to good use.

Since PIO programs can’t really be debugged and the language is a bit rough, I turned to Wokwi, a great tool that lets you test designs online and trace register activity within PIO state machines.

Here, I inject a PWM signal into a RP2040 pin and try to count how many pulses per second it receives.

BricoNeo_2025 = { .upgrade = true };

Une fois que j’ai quelque chose qui semble fonctionner, je porte le code sur le BricoNeo et je m’aperçois que je ne peux pas mesurer de fréquence au-dessus de 23 MHz avec ce code. Manque de bol, la NeoGeo a une horloge principale à 24 MHz !

Quelques gros mots et pas mal de grattage de tête plus tard, je trouve une nouvelle méthode de comptage, et je peux monter aux alentours de 60 MHz (je crois, j’ai oublié). En tout cas, c’est largement au-dessus de la fréquence maximale du système, donc mission accomplie !

L’idée est alors de brancher une sonde (un simple fil) sur la broche “IN” du BricoNeo, conçue pour ça (merci @illusionrip pour la jolie photo !). Cette entrée utilise une entrée libre sur la puce OR compatible 5V du BricoNeo. À la base, cette puce sert à générer l’interruption READ lorsque les broches /CE et /OE de l’EPROM émulée sont activées. #charabia

------------------------------------------------------------------------------------------

Once I had something that seemed to work, I ported the code to BricoNeo, only to realize that I couldn’t measure frequencies above 23 MHz with this approach. Bad luck—the NeoGeo’s main clock runs at 24 MHz!

A few swear words and lots of head-scratching later, I found a new way to count, and now I can go up to around 60 MHz (I think, I forgot). Either way, it’s well above the system’s max frequency, so mission accomplished!

The idea is to connect a probe (just a wire) to the “IN” pin on BricoNeo, which was made for this (thanks @illusionrip for the great photo!). This input uses a free pin on BricoNeo’s 5V-compatible OR gate chip. Originally, this chip is used to generate the READ interrupt when the /CE and /OE pins of the emulated EPROM are activated. #gibberish

BricoNeo_2025 = { .upgrade = true };

En collant la sonde sur n’importe quel point du circuit (tant que ça ne tourne pas en 12V, parce qu’on n’a pas envie de tout cramer), on obtient instantanément la fréquence mesurée dans le terminal connecté au BricoNeo.

Si on détecte une très basse fréquence, on suppose qu’il s’agit d’une tension constante et on affiche simplement l’état de la broche (HIGH ou LOW), ce qui ajoute une fonctionnalité de sonde logique.

Et voilà ma super sonde : je la pointe au hasard sur une patte d’une puce de fast RAM Easy!

------------------------------------------------------------------------------------------

By attaching the probe to any point on the circuit (as long as it’s not running at 12V, because we don’t want to fry everything), we get an instantaneous frequency reading in the terminal connected to BricoNeo.

If we detect a very low frequency, we assume it’s a constant voltage and simply display the pin state (HIGH or LOW), which adds a logic probe function.

And here’s my awesome probe: I randomly place it on a fast RAM chip pin Easy!

BricoNeo_2025 = { .upgrade = true };

L’ami @illusionrip nous fait une démo encore plus convaincante, en mesurant la fréquence du quartz d’une AES.

Les AES ont une fréquence légèrement plus élevée que les 24 MHz des MVS, probablement pour se donner un air plus précieux (et peut-être pour la génération du signal NTSC ?).

Voici le relevé du BricoNeo :

------------------------------------------------------------------------------------------

Our friend @illusionrip gives us an even more convincing demo, measuring the quartz frequency of an AES.

The AES runs at a slightly higher frequency than the 24 MHz found in MVS units, maybe to make them feel special (or for NTSC signal generation?).

Here’s the BricoNeo reading:

BricoNeo_2025 = { .upgrade = true };

Et voici le relevé de son oscilloscope : on n’est pas très loin !

La précision dépendra largement de la qualité du résonateur utilisé sur la carte BricoNeo. Je n’ai pas pris du premier prix, mais l’objectif initial n’était pas non plus de se synchroniser avec un satellite, donc ça fait le job !

------------------------------------------------------------------------------------------

And here’s the oscilloscope reading: we’re pretty close!

The accuracy largely depends on the quality of the resonator used on the BricoNeo board. I didn’t go for the cheapest option, but the original goal wasn’t to sync with a satellite either—so it is what it is!

BricoNeo_2025 = { .upgrade = true };

Un nouveau BricoNeo ?

Et pour finir, j’ai bossé sur la reconception du PCB du BricoNeo, en passant du microcontrôleur RP2040 au nouveau modèle RP2354, principalement pour avoir plus de RAM disponible et simplifier la gestion du stockage de masse, qui est actuellement un sac de nœuds.

Le RP2354 (toujours chez Raspberry Pi) a quatre broches de plus que le RP2040, mais surtout, il a une gestion de l’alimentation très différente, ce qui m’oblige à intégrer de nouveaux composants dans l’espace limité du PCB !

J’ai choisi la variante RP2354 parce qu’elle intègre 2 Mo de mémoire flash directement sur la puce du microcontrôleur (les dies sont superposés, #charabia). Cela me libère un peu de place pour ajouter les composants supplémentaires.

J’ai réussi à concevoir un design qui pourrait fonctionner, mais… la puce n’est pas disponible sur le marché. Elle aurait dû l’être depuis plusieurs mois, mais je l’attends toujours. Notamment chez LCSC, qui fournit les composants pour l’assemblage de la partie supérieure de la carte chez JLCPCB.

Résultat : pour le moment, il va falloir se contenter d’une image de la nouvelle carte !

------------------------------------------------------------------------------------------

A New BricoNeo?

And to wrap things up, I’ve been working on redesigning the BricoNeo PCB, switching from the RP2040 microcontroller to the new RP2354 model, mainly to get more available RAM and make mass storage management easier, which is currently a mess.

The RP2354 (still from Raspberry Pi) has four more pins than the RP2040, but more importantly, it has a very different power management system, which requires adding extra components within the limited PCB space!

I chose the RP2354 variant because it integrates 2MB of flash memory directly on the same chip as the microcontroller (they stacked the dies, #gibberish). This frees up some space to fit the extra components.

I’ve managed to design a layout that should work, but… the chip isn’t available on the market. It was supposed to be for months now, but I’m still waiting for it. Especially from LCSC, which supplies the components for assembling the top part of the board at JLCPCB.

Bottom line: for now, we’ll have to settle for just an image of the new board!

 

BricoNeo_2025 = { .upgrade = true };
Pour être informé des derniers articles, inscrivez vous :
Commenter cet article
A
Elle a l'air sympa, cette petite carte ;).<br /> Merci pour l'exposition!
Répondre