Contexte du projet

Le développeur a lancé le projet lucyNES en juin 2024, en consacrant son temps libre pendant sept mois avant de le mettre en pause en janvier 2025. L’objectif était de créer un émulateur capable de faire tourner des titres comme Battletoads et des centaines d’autres jeux, tout en explorant la syntaxe et les contraintes du langage Odin, peu utilisé dans des projets de cette ampleur.

Architecture et défis d’émulation

Le cœur de l’émulateur repose sur la reproduction fidèle du CPU 6502 et du PPU (Picture Processing Unit) du NES. La synchronisation entre ces deux unités est cruciale : le CPU et le PPU fonctionnent en parallèle et échangent des signaux à chaque cycle d’horloge. Une dérive de quelques cycles entraîne des artefacts graphiques ou des plantages, d’où l’importance d’une « timing » précise. Le projet supporte les mappers NROM, MMC1, UXROM, CNROM, MMC3, GxROM et AxROM, qui étendent la cartouche avec de la RAM supplémentaire ou des capacités audio. Une implémentation incomplète de certains mappers empêche encore certains jeux de démarrer.

Utilisation d’Odin et choix d’implémentation

Odin, langage fortement typé et proche du C, a été choisi pour sa simplicité syntaxique et son contrôle bas‑niveau. L’absence de promotion automatique d’entiers a généré des bugs lors d’opérations bit‑wise, obligeant le développeur à insérer des cast explicites. La gestion de la mémoire repose sur trois allocateurs : un allocateur temporaire réinitialisé chaque trame, un heap global pour les objets persistants, et un « NES Arena » dédié aux structures liées à une session de jeu. La sérialisation d’états de jeu utilise la bibliothèque CBOR, mais a nécessité la définition de handlers personnalisés pour des types complexes, comme le registre PPU :

PpuCtrl :: struct #raw_union {
    // VPHB SINN
    using flags: bit_field u8 {
        n: u8 | 2,
        i: u8 | 1,
        s: u8 | 1, // sprite pattern table address for 8x8 sprites (0: $0000, 1: $1000)
        b: u8 | 1, // bg pattern table address (0: $0000, 1: $1000)
        h: u8 | 1, // sprite size (0: 8x8, 1: 8x16)
        p: u8 | 1,
        v: u8 | 1, // vblank NMI enable
    },
    reg:          u8,
}

Ce type montre la capacité d’Odin à déclarer des unions brutes avec des champs de bits, ce qui simplifie la modélisation des registres matériels.

Limites et perspectives

Le son reste partiel : le canal DMC n’est pas implémenté, ce qui affecte la plupart des titres audio‑intensifs. Certaines implémentations de mappers sont encore déficientes, limitant la compatibilité avec des jeux plus rares. Le développeur envisage d’ajouter de nouveaux mappers, d’améliorer le rendu audio et de finaliser la prise en charge du DMC. Tant que le projet restera « open‑source », la communauté pourra contribuer à combler ces lacunes.