pico6502 Guide Technique

Vue d'ensemble

L'architecture technique du pico6502 est identique à celle du picoZ80 à tous égards, à l'exception de l'interface bus CPU. Le même processeur double cœur RP2350B, le même modèle mémoire PSRAM, la même organisation Flash, le même coprocesseur ESP32 et le même framework de pilotes sont partagés entre les deux produits. Ce guide se concentre sur les différences propres au 6502 et renvoie au Guide Technique picoZ80 pour les sujets entièrement partagés.

Architecture Matérielle

Schéma Bloc

┌─────────────────────────────────────────────────────────────────────────┐
│                            pico6502 Board (v2.3a)                       │
│                                                                         │
│  ┌───────────────┐   PIO 0: A0–A15, D0–D7      ┌───────────────────┐   │
│  │               │◄──────────────────────────►│  DIP-40 Interface │   │
│  │   RP2350B     │   PIO 1: cycle/ctrl/irq     │   (6502 socket)   │   │
│  │  (300 MHz)    │◄──────────────────────────►│  A0–A15, D0–D7   │   │
│  │               │   PIO 2: PHI1/PHI2 clock ──►│  PHI0/RNW/SYNC   │   │
│  │  Core 0       │                             │  IRQ/NMI/RDY/SO  │   │
│  │  (USB/IO/ESP) │                             └───────────────────┘   │
│  │               │◄──QSPI──►[ 8MB PSRAM ]                             │
│  │  Core 1       │◄──SPI───►[ 16MB Flash ]                             │
│  │  (6502 loop)  │                                                     │
│  │               │◄──FSPI──►┌───────────┐                              │
│  │               │◄──UART──►│   ESP32   │◄──SPI──►[ SD Card ]         │
│  └───────────────┘          │  (WiFi)   │──────────[ Web Server ]     │
│                             └───────────┘                              │
│  [ USB Hub ]──USB──►[ RP2350 USB Bridge ]                             │
└─────────────────────────────────────────────────────────────────────────┘

Composants Principaux

Composant Référence Spécification
Processeur principal RP2350B Double Cortex-M33, jusqu’à 300 MHz, 512 Ko de SRAM, 48 GPIO
PSRAM SPI PSRAM 8 Mo, interface QSPI, jusqu’à 133 MHz
Flash SPI NOR Flash 16 Mo, XIP (exécution sur place)
Coprocesseur ESP32-S3 WiFi, carte SD, serveur web, UART 460,8 kbaud vers le RP2350
Carte SD FAT32 Images ROM, images disque, config.json, fichiers web
Interface bus 74AHCT244 / 74AHCT245 Interface bufferisée (v2.3) : des 74AHCT244 pilotent l’adresse et les signaux de contrôle en sortie, un 74AHCT245 gère le bus de données, avec des seuils tolérants au 5 V pour une translation de niveau propre
Alimentation TLV62590BV Buck synchrone, 5 V (broche VCC) → 3,3 V, alimentation de toute la carte
USB Hub intégré Pont USB de stockage de masse RP2350 pour le flashage du firmware

Attribution des GPIO

Les attributions GPIO ci-dessous sont celles de l'interface bufferisée v2.3, tirées de src/M6502.pio — les définitions de broches du firmware constituent la référence de travail, le schéma KiCad (kicad/PICO6502/) étant la source matérielle. Notez les chemins d'horloge aller et retour distincts (CLK1/CLK2 pilotés en sortie ; CLK1_IN/CLK2_IN relus du côté socket des buffers) — voir la section Interface Bus Bufferisée ci-dessous pour en comprendre la raison.
GPIO Signal Direction Notes
0–15 A0–A15 Sortie Bus d’adresses 6502
16–23 D0–D7 Bidir Bus de données 6502, via le 74AHCT245
24 R/~W Sortie Read/Not-Write — l’unique ligne lecture/écriture du 6502 (pas de /RD et /WR séparés)
25 SYNC Sortie Indicateur de fetch d’opcode
26 CLK1_IN Entrée PHI1 tel que présent sur le socket (chemin de retour)
27 /OE_CLK Sortie Validation de sortie du buffer d’horloge
28 DBE Entrée Data Bus Enable depuis le socket hôte (tiré au niveau haut par R26)
29 CLK2_IN Entrée PHI2 tel que présent sur le socket — toutes les machines d’état bus se synchronisent dessus
30 /SO Entrée Set Overflow
31 CLK1 Sortie Pilotage aller de PHI1
32 CLK2 Sortie Pilotage aller de PHI2
33 /IRQ Entrée Interruption masquable (actif bas)
34 RDY Entrée Ready — étirement par wait states
35 CLK0 Entrée Horloge de référence PHI0 depuis l’hôte (mode 6502)
36 /NMI Entrée Interruption non masquable (actif bas)
38 /RES Bidir Reset système (actif bas)
DBE_OVR — le compagnon de forçage de DBE piloté par le firmware — ne réside délibérément pas sur le RP2350 : il se trouve sur la GPIO38 de l'ESP32, ce qui économise une GPIO RP2350 rare. Il est positionné à partir de la clé de configuration dbeoverride via l'ESP32 (voir la Référence de Configuration).

Interface Bus Bufferisée (v2.3)

La carte v2.3 remplace l'interface passive antérieure par une interface entièrement bufferisée — le changement qui a motivé la révision, puisque la carte v2.2 se trompait précisément sur la synchronisation bus et horloge que la bufferisation corrige. La v2.3a est la carte sur laquelle chaque résultat matériel de ce guide a été obtenu. L'ensemble des buffers :
  • Des 74AHCT244 pilotent les lignes d'adresse et de contrôle vers le socket hôte.
  • Un 74AHCT245 gère le bus de données bidirectionnel.
  • Les horloges disposent de chemins aller et retour séparés : le firmware pilote CLK1/CLK2 (GPIO 31/32) vers les buffers et relit CLK1_IN/CLK2_IN (GPIO 26/29) du côté socket. La comparaison de la phase pilotée avec la phase retournée permet de distinguer un buffer mort d'une horloge morte — exactement la distinction que la carte v2.2 ne pouvait pas faire, et la raison d'être de la commande busdiag clk du shell de débogage.
Toutes les machines d'état bus se synchronisent sur CLK2_IN — la phase réellement présente sur le socket dans les deux modes d'horloge — de sorte qu'un seul programme PIO sert aussi bien le 6502 que le 6512, sans variante dépendant du mode.
La mise en haute impédance des buffers suit la broche DBE (Data Bus Enable) du socket, conditionnée par U13A : ~EN = NOR(DBE, DBE_OVR). R26 tire DBE au niveau haut, de sorte qu'un hôte 6502 ordinaire laissant la broche 36 non connectée n'a besoin de rien — les buffers sont activés par défaut. Un hôte qui pilote activement DBE obtient un comportement de haute impédance authentique, et le firmware peut forcer la situation via DBE_OVR (GPIO38 de l'ESP32) lorsque la configuration l'exige.

Architecture Firmware

Organisation de la Mémoire Flash

Identique à l'organisation du picoZ80 :
Partition Plage d’adresses Taille Contenu
Bootloader 0x10000000–0x1001FFFF 128 Ko Pont USB, mise à jour du firmware, sélecteur de partition
App Slot 1 0x10020000–0x1051FFFF 5 Mo Firmware 6502 principal (partition 1)
App Slot 2 0x10520000–0x10A1FFFF 5 Mo Firmware 6502 principal (partition 2)
App Config 1 0x10A20000–0x10C9FFFF 2,5 Mo Images ROM + JSON de configuration minifié (slot 1)
App Config 2 0x10CA0000–0x10F1FFFF 2,5 Mo Images ROM + JSON de configuration minifié (slot 2)
General Config 0x10F20000–0x10FFEFFF 892 Ko Paramètres principaux, espace de travail
Partition Table 0x10FFF000–0x11000000 4 Ko Slot actif, sommes de contrôle, métadonnées
Le firmware 6502 utilise désormais le magasin de configuration en flash exactement comme les modèles Z80 (M6502CPU_configFromFlash reflète Z80CPU_configFromJSON). La commande CFG<n> de l'ESP32 lit config.json depuis la carte, le minifie, stocke le blob avec la définition de machine analysée et l'horloge/la tension du cœur dans la région App Config de la partition, fait pointer la table des partitions vers l'application et redémarre ; le démarrage suivant lit tout depuis la flash sans dépendre de l'ESP32, ce qui est précisément ce qui rend persistants un cpufreq/voltage non par défaut. Le démarrage précoce applique l'horloge stockée avant l'initialisation de la PSRAM ; un démarrage qui se bloque sur le watchdog redémarre dans un mode sans échec à 150 MHz. L'option "persist": "flash" du pilote BBC-B réserve une région non volatile par partition à la fin de la zone General Config, juste en dessous de la table des partitions : un secteur méta (magique, masque de validité, image CMOS de 64 octets) plus 16 slots de RAM latérale (sideways RAM) de 16 Ko — 65 secteurs, ~260 Ko par partition. Le Core 0 l'écrit sur réception d'un message inter-cœurs MSG_WRITE_NVRAM tout en maintenant le Core 1 dans une boucle d'attente résidente en RAM, car flash_range_* bloque le XIP.

Conception Dual-Core

Cœur Responsabilités
Core 0 Pont USB de stockage de masse ; gestion des partitions firmware ; relais des E/S fichiers vers l’ESP32 ; dispatch des commandes UART ESP32 ; service de la file inter-cœurs
Core 1 Boucle chaude d’émulation du bus 6502 — sert les FIFO PIO à chaque cycle bus, résout les adresses par rapport à la carte mémoire, dispatche vers les handlers PSRAM / PHYSICAL / FUNC, appelle les callbacks de poll des pilotes

Cœur d'Émulation CPU

Le cœur 6502 est spécifique : src/M6502.c est un émulateur MOS 6502 écrit en C ANSI, calqué sur le moteur côté Z80 pour une compatibilité directe au sein du firmware pico6502. Il implémente les 151 instructions officielles et tous les opcodes non documentés — y compris le groupe KIL/JAM, qui arrête le processeur émulé exactement comme il bloque un vrai 6502 NMOS. Le cœur n'a aucune dépendance au RP2350 (uniquement les en-têtes C standard), ce qui permet de le compiler nativement pour les tests de conformité côté hôte et pour le simulateur pico6502sim. src/M6502CPU.c est l'orchestrateur bus du Core 1, miroir de Z80CPU.c : handshake FIFO, hold/hold-acknowledge avec une barrière avant l'acquittement, reset forcé, polling des pilotes, et les hooks de pas à pas/points d'arrêt/trace utilisés par le shell de débogage. La boucle d'émulation sert M6502_CYCLES_PER_LOOP (1024) cycles par passe ; l'arithmétique de limite de tranche est protégée contre le débordement, après qu'un bouclage du compteur de cycles 32 bits s'est révélé figer la machine après exactement 42 minutes. Les fonctions chaudes sont __not_in_flash_func (résidentes en RAM) et la table de consultation des drapeaux N/Z réside en RAM : les blocages XIP dus au trafic flash du Core 0 se sont avérés à la fois brider le débit et, une fois, corrompre les enveloppes bus. Le cœur tient des compteurs de retrait (g_m6502InstrCount, g_m6502CycleCount) que la commande cpustat du shell convertit en pourcentage de l'horloge hôte mesurée.
Deux variantes de CPU sont prises en charge — 6502 et 6512 — et elles ne diffèrent que par l'horloge : les deux sont le même processeur au-dessus des broches, avec des registres identiques, un jeu d'instructions identique (opcodes non documentés inclus) et une temporisation par cycle identique, de sorte que rien ne varie entre elles dans le cœur d'émulation. Le 6502 possède un générateur d'horloge intégré — PHI0 arrive sur la broche 37 du socket et le processeur (ici, le firmware) pilote PHI1/PHI2 en sortie ; le 6512 n'a pas de générateur et l'hôte fournit les deux phases. Une surcouche CMOS (m6502CmosOp, activée par cpu->cmos) est sélectionnée par le cputype "65C02" (horloge 6502) ou "65C12" (horloge 6512), et est forcée par le mode Master du pilote BBC-B, car MOS 3.x est du code 65C12. Elle ajoute le mode d'adressage (zp), PHX/PHY/PLX/PLY, INC/DEC A, STZ, TSB/TRB, BIT #/zp,X/abs,X, BRA, les JMP (abs) et JMP (abs,X) au franchissement de page corrigé ; la colonne KIL $x2 devient des NOP de 2 octets et les colonnes $x3/$x7/$xB/$xF des NOP d'un cycle, et les drapeaux N/Z en mode décimal proviennent du résultat ajusté. Les instructions de bits Rockwell sont délibérément absentes, le 65SC12 ne les possédant pas. Le désassembleur suit le cœur sélectionné. Un cputype non reconnu journalise un avertissement et se rabat sur le 6502 NMOS.
Interruptions : /IRQ est sensible au niveau et échantillonné une fois par instruction — la macro SERVICE_REQUESTS() prend un instantané unique des drapeaux IRQ du PIO à chaque fetch d'opcode, et le cœur combine par OU la ligne physique avec une ligne IRQ virtuelle (g_m6502VirtIrq) que lèvent les périphériques virtuels tels que l'ACIA du lecteur de cassette UEF. /NMI est détecté sur front dans la boucle du Core 1, l'état précédent étant initialisé à partir de la broche réelle au démarrage et à chaque reset, afin qu'un niveau bas préexistant ne soit pas pris pour un front (le défaut « NMI vers $0D00 » au démarrage à froid). Les deux prennent sept cycles et vident tout préchargement en cours — le fetch factice abandonné du vrai composant.

Interface Bus PIO

L'interface bus 6502 est implémentée dans M6502.pio et pilotée depuis M6502CPU.c. La différence essentielle avec l'interface Z80 est le schéma d'horloge biphasé non chevauchant du 6502 à la place de l'horloge unique du Z80, et une ligne R/W̄ unique à la place de /RD et /WR. Le côté qui génère les phases dépend de la variante de CPU configurée (voir ci-dessous) — mais dans les deux modes, chaque machine d'état bus se synchronise sur CLK2_IN, la phase PHI2 réellement présente sur le socket hôte, de sorte qu'un seul programme PIO sert les deux variantes.
Le moteur a été reconstruit lors de la mise en service du BBC Model B en septembre 2026 autour d'un principe : le PIO prend chaque décision de temporisation. Sept itérations de logique « d'émission rapide » côté C ont toutes corrompu les données en exécution réelle tout en passant chaque test synthétique, car le défaut résidait dans l'entrelacement des machines d'état et était inaccessible depuis la temporisation C ; la correction a consisté à déplacer la décision de phase dans la machine d'état d'adresse elle-même. Le côté C ne contient désormais plus aucune temporisation bus — la sûreté de temporisation est structurelle, dans l'« échelle » décrite ci-dessous.

Programmes PIO

Bloc PIO Programme Machine d’état Fonction
PIO 0 m6502_addr SM 0 Moteur d’adresse v3 (« échelle + gated + burst unifiés »). Pilote A0–A15 ; possède R/W et SYNC comme groupe SET.
PIO 0 m6502_latch SM 2 Latch d’entrée transparent v4 pour les lectures — en roue libre, un mot de capture par pilotage d’adresse.
PIO 1 m6502_cycle SM 0 Séquenceur de cycles piloté par exec pour le chemin gated (sérialisé) ; RDY est son jmp pin.
PIO 1 m6502_so SM 1 Surveille la broche SO ; positionne l’IRQ PIO 6.
PIO 1 m6502_irq SM 2 Reflète /IRQ dans l’IRQ PIO 5.
PIO 1 m6502_nmi SM 3 Reflète /NMI dans l’IRQ PIO 4.
PIO 2 m6502_clock_6502 / m6502_clock_6512 SM 0 Génère PHI1/PHI2 à partir de PHI0 (6502), ou reste inactif (6512 — phases fournies par l’hôte).
PIO 2 m6502_data SM 1 Pilote des données d’écriture, asservi uniquement à R/W (déplacé du PIO 0 pour libérer de l’espace programme).
Huit machines d'état sont chargées, toutes avec un diviseur d'horloge de 1,0. Les programmes m6502_fetch, m6502_read et m6502_write restent dans le fichier comme séquences de cycle bus de référence ; leurs instructions sont ce que le côté C pousse dans la SM de cycle sous forme de mots out exec sur le chemin gated.

Conventions des Signaux IRQ

Drapeau IRQ PIO Signal Déclenché par Consommé par
IRQ 0 Parking gated m6502_addr à addr_gated — se gare jusqu’à ce que le Core 1 pousse le mot et efface le drapeau Core 1 (E/S, opérations sérialisées et blocs lents)
IRQ 4 (PIO 0) Adresse valide m6502_addr — une adresse de lecture a été pilotée m6502_latch — démarre une capture
IRQ 5 /IRQ m6502_irq — broche basse (niveau) Instantané SERVICE_REQUESTS() du Core 1, une fois par instruction
IRQ 4 (PIO 1) /NMI m6502_nmi — broche basse Détecteur de front du Core 1
IRQ 6 SO m6502_so — broche activée Core 1 (l’effet de bord sur le drapeau V n’est pas encore câblé — les hôtes testés n’utilisent pas SO)

Génération d'Horloge PHI1 / PHI2 — 6502 vs 6512

Le 6502 requiert deux phases d'horloge non chevauchantes, et les deux variantes de CPU prises en charge répartissent la responsabilité différemment :
  • Mode 6502 (cputype "6502" / "65C02") — le vrai composant contient le générateur d'horloge, le pico6502 joue donc ce rôle : la référence PHI0 de l'hôte arrive sur la broche 37 du socket (CLK0, GPIO 35) et le firmware génère et pilote PHI1/PHI2 en sortie à travers les buffers d'horloge (CLK1/CLK2, GPIO 31/32) avec la temporisation de non-chevauchement correcte.
  • Mode 6512 (cputype "6512" / "65C12") — le 6512 n'a pas de générateur intégré ; l'hôte fournit les deux phases et le firmware se contente d'écouter. Un hold de libération de bus DBE est honoré dans ce mode.
Dans les deux cas, les machines d'état bus tirent leur temporisation de CLK1_IN/CLK2_IN (GPIO 26/29) — les phases relues du côté socket des buffers, c'est-à-dire ce que l'hôte voit réellement — de sorte que le même programme PIO sert les deux modes, et la génération d'horloge dans le PIO signifie que la temporisation du bus 6502 n'est jamais affectée par l'activité du Core 1 ni par les blocages de FIFO. Au démarrage, le firmware mesure l'horloge hôte à la fois sur PHI0 et PHI2_IN ; la valeur mesurée est celle par rapport à laquelle cpustat rapporte la vitesse.

Wait States et RDY

Le signal RDY du 6502 insère des wait states — maintenir RDY au niveau bas pendant un cycle de lecture met le processeur en pause jusqu'à son retour au niveau haut. Le pico6502 honore RDY uniquement sur le chemin gated, via la boucle de wait state de la SM de cycle (jmp pin après chaque montée de PHI2) ; le chemin rapide pipeliné ne s'étire pas, car ni le BBC ni l'Apple I ne basculent jamais RDY. Un hôte qui utilise RDY est servi correctement en routant ses régions étirées par le chemin gated (tout bloc qui échoue un jour à la vérification shadow y est automatiquement mis en quarantaine — voir ci-dessous). SYNC est de même au mieux sur le chemin rapide et exact sur les fetches sérialisés ; un mode SYNC exact sélectionnable par persona est conçu mais aucun hôte testé n'en a encore besoin.

Architecture PIO — Comment les Cycles Bus 6502 Sont Recréés

Un vrai 6502 change son adresse 110–225 ns après la descente de PHI2, la maintient pendant tout le cycle, écrit en un seul cycle avec R/W bas sur un seul état haut de PHI2 et le périphérique verrouillant à la descente, et présente des données de lecture qui ne sont valides qu'à la fin de l'état haut de PHI2. Le BBC Model B — dont le système vidéo prend le bus RAM exactement à la descente de PHI2 — est impitoyable sur chacun de ces points, et le moteur ci-dessous est le résultat de la mise en conformité de chacun d'eux.

Le Moteur d'Adresse — Échelle, Gated et Burst
Chaque mot que le Core 1 pousse vers m6502_addr est {instr:16 | ~addr:16} : l'adresse inversée va dans X et le demi-mot supérieur est exécuté avec out exec, 16. Un mot de lecture porte set y, count-1 et enchaîne sur le chemin de lecture ; un mot d'écriture porte jmp wdrive. La machine d'état elle-même dispose de trois façons de devenir visible sur le bus :
  • Échelle — le mode par défaut en roue libre. Après chaque descente authentique de PHI2, la SM attend tADS ([15] cycles), remet R/W au niveau haut, puis interroge sa FIFO sur quatre barreaux (jmp !osre avec des délais de 12, 12 et 24 cycles — interrogations aux cycles approximatifs 17/30/43/68). Un mot qui arrive à temps est piloté sur ce barreau ; la forme asymétrique maintient le pilotage au pire cas à ~206–215 ns, à l'intérieur de l'enveloppe du vrai composant, et la fenêtre se ferme à la dernière interrogation. En dessous d'une horloge système de 250 MHz, le chargeur met les délais à zéro (« mode fresh-fall »). Sans travail en file, la SM reste inactive jusqu'à la descente suivante.
  • Burst — un mot de lecture peut demander jusqu'à six adresses consécutives. Le PIO n'a pas d'additionneur, X contient donc l'adresse inversée, mov pins, !x la pilote et jmp x-- la fait croître ; chaque octet est piloté sur le point de grille descente-plus-tADS suivant et déclenche le latch, de sorte que les octets d'instruction défilent à un cycle par octet avec zéro C dans la boucle.
  • Gated (addr_gated) — le handshake classique sur IRQ PIO 0, désormais en trois instructions : la SM se gare, le Core 1 pousse le mot et la libère, et le pilotage a lieu à la descente authentique suivante avec la temporisation propre de l'échelle. Les accès E/S, les opérations sérialisées et les blocs lents mis en quarantaine l'utilisent.
wdrive pilote l'adresse d'écriture avec R/W bas pendant exactement un cycle et ne lève aucun drapeau d'adresse valide, de sorte que le latch ne capture jamais une écriture — « un cycle, un strobe ». L'enveloppe d'écriture à deux strobes antérieure aux corrections de setup d'adresse écrivait deux fois les registres d'E/S et n'est conservée que comme bouton A/B writestrobes 2. Un chemin de lecture sans capture (rdrive) existe également ; les tests A/B ont montré qu'il coûtait quatre points de vitesse à Elite, il n'est donc utilisé que par le chase d'E/S.

Données d'Écriture et Latch de Lecture Transparent
m6502_data (PIO 2 SM 1) est asservi uniquement à la ligne R/W : wait 0 gpio RNW, pilotage de D0–D7, wait 1 gpio RNW, libération. Le Core 1 met en file le mot de données avant le mot d'adresse correspondant, de sorte que l'ordre de la FIFO les apparie par construction et qu'il n'y a aucune course de fronts entre machines d'état ; les données sont maintenues pendant le tHW du vrai composant (15 cycles PIO, ~44 ns à 340 MHz).
m6502_latch (PIO 0 SM 2) remplace l'ancien schéma « échantillonner quelques cycles après la montée ». Il attend le drapeau d'adresse valide, la montée de PHI2, boucle tant que PHI2 est haut et échantillonne après la descente détectée — le délai du synchroniseur place l'échantillon à la descente réelle +9–12 ns, de manière déterministe. L'analyse à l'analyseur logique a prouvé que les cycles lecture-après-écriture du BBC ne délivrent les données qu'entre montée+227 ns et la descente, la descente est donc le point d'échantillonnage correct ; le maintien des données plus la capacité des broches côté pico (le 74AHCT245 se désactive vers descente+6 ns et les broches conservent alors leur charge) y gardent la valeur valide. L'ancien ordre échantillonner-puis-tester pouvait capturer jusqu'à une boucle avant la descente et lire le silicium lent d'un socket latéral avec des bits encore bas — la corruption « les bits ne font que tomber » à ~10 ppm que le vérificateur shadow a attrapée dans Galaxians. Un balayage matériel à 330 MHz a mesuré pour un settle de 0–1 cycle 2 erreurs RAM sur 4096 contre 16 pour un settle de 4 cycles, d'où le readsettle fixe d'un cycle.

Spéculation — Harvest, Shadow, Serve et Écritures Natives
Tout ce qui figure dans cette sous-section est conditionné par m6502IoKnown() — le fait que la persona (ou le JSON, ou le buspipe iorange du shell) ait déclaré la fenêtre d'E/S de l'hôte. Sans cela, le moteur ne spécule jamais et un hôte ordinaire fonctionne de manière conservatrice ; avec, le BBC Model B exécute Elite et le reste de sa bibliothèque à plein régime.
  • Burst d'opcodes et tampon harvest — g_burstSpec (6 par défaut depuis le pilote BBC) octets de burst d'opcodes spéculatif. Les octets déjà récupérés lorsqu'une opération bus a interrompu le flux sont conservés dans un tampon harvest de 8 entrées et servis aux fetches ultérieurs sans opération bus ; il est invalidé par les écritures dans sa plage, les sauts du flux, les resets et les barrières de carte mémoire.
  • Shadow — un shadow write-through de 64 Ko plus une carte binaire de validité. La ROM se remplit paresseusement à partir du flux bus ; la fenêtre d'E/S n'est jamais mise en shadow. Une écriture dans le registre de pagination (ROMSEL, reconnu via pageregmask car il est partiellement décodé sur $FE30–$FE3F) invalide la région paginée. Les écritures hors de la fenêtre d'écriture shadow déclarée ne font qu'invalider — la découverte, avec Galaxians, que la ROM latérale ignore les écritures. Chaque octet dépilé du bus est comparé au shadow ; g_shadowMismatch doit être nul, et tout bloc de 256 octets qui diverge est mis en quarantaine sur le chemin gated pour le reste de la session.
  • Serve — une fois prouvé (1,38 milliard d'octets servis sans la moindre divergence au cours des tests d'endurance, de pipetest et des jeux), un octet valide en shadow est renvoyé immédiatement au cœur pendant que le cycle bus réel se poursuit. Le bus reste identique octet pour octet à un vrai 6502 ; le cœur cesse de l'attendre.
  • Écritures natives — les écritures sont des mots d'échelle fire-and-forget avec une barrière d'écriture en attente et une contre-pression de FIFO, propres vis-à-vis de l'oracle sous pipetest.
  • Fetches fusionnés — m6502FetchOpcodeFused et les fetches d'opérande sont des appels directs remplaçant les paires de callbacks issue/collect, forcés inline sous optimisation partielle à l'édition de liens.
  • Mode carte inconnue — sans fenêtre déclarée, pipelineFetch fait tout de même chevaucher le fetch de l'opcode suivant avec la fin de l'instruction courante.

Discipline d'E/S — Chase et Parking
Un vrai 6502 laisse son bus d'adresses maintenir la dernière adresse pendant les cycles inactifs, et un passthrough ordinaire fait de même. Sur un BBC équipé d'un coprocesseur PiCoPro sur le Tube, cela redécodait les registres FIFO de l'ULA Tube — un caractère sur cinq de la bannière PiCoPro, puis désynchronisation du protocole et clavier mort. Le chase d'E/S (activé par défaut) fait suivre chaque accès à l'intérieur de la fenêtre d'E/S d'une lecture de parking sans capture à l'adresse de parking, de sorte qu'aucun registre à effet de bord n'est jamais décodé deux fois. L'ancien parking d'adresse (iopark, désactivé par défaut) garait le bus après chaque accès E/S ; un test A/B de nécessité sur le BBC n'a montré aucune différence une fois les corrections de livraison des IRQ en place, il est donc conservé à titre de diagnostic. Lorsqu'une région PHYSICALHW est déclarée, la cible de parking suit la carte mémoire et vaut par défaut $FFFC — le vecteur de reset, de la ROM mappée sur tout 6502 amorçable — modifiable avec ioparkaddr.

Ce que la Mise en Service du BBC a Révélé
La pile de corrections qui a fait passer la carte v2.3a de « rien sur le bus » à un Model B fonctionnel (firmware v1.964, 14 septembre 2026), chaque élément restaurant un comportement d'un vrai 6502, mérite d'être consignée pour le prochain hôte :
  • Discipline d'adresse — l'adresse ne change que sur une descente fraîche de PHI2 plus la mise en forme tADS, offrant un cycle hôte complet de setup d'adresse avant chaque échantillon de lecture.
  • Écritures — SM de données asservie à R/W (pas de courses de fronts entre SM) ; un strobe par écriture.
  • Lectures — le latch transparent : maintien nul, insensible à l'étirement, immunisé contre l'effondrement du maintien après écriture et le basculement du bus vidéo.
  • Interruptions — la livraison IRQ/NMI n'avait jamais été implémentée ; /IRQ est désormais échantillonné en direct à chaque instruction avec une sémantique de niveau (corrigeant une IRQ fantôme passant par un vecteur invalide sur BREAK) ; l'initialisation du front /NMI a corrigé le vecteur de démarrage à froid vers $0D00.
  • Reset — /RES anti-rebond sur 5 ms avec la séquence de reset exécutée après le relâchement (corrigeant le « premier BREAK ignoré ») ; un superviseur de mise sous tension maintient l'hôte en reset jusqu'à ce que les services du Core 0 soient opérationnels (plafond de 15 s) ; R/W est garé au niveau haut à l'initialisation ; le Core 1 est garé en RAM avant toute écriture flash.
  • Compteur de cycles — un bouclage 32 bits figeait la machine après exactement 42 minutes ; l'arithmétique des tranches est désormais protégée contre le débordement.
  • Horloges et tension — la persona Model B est livrée à 340 MHz / 1,20 V ; une autre unité a eu besoin de ~1,35 V pour 330 MHz (1,30 V se bloquait). La marge de tension est propre à chaque carte. Un démarrage qui se bloque retombe à 150 MHz.
Les preuves de tout cela sont dans le dépôt : des captures Kingst LA5016 (*.kvdat, timing/) d'un vrai 6502 et du pico6502 dans le même BBC avec le même PiCoPro — démarrage, Elite, Galaxians, tests d'écriture et séquences BREAK — et les outils d'analyse Python (kvdat.py, kvtiming.py, rdgrid.py/wrgrid.py, wrcheck.py, zoom.py et d'autres) qui en produisent des fiches de temporisation, ainsi que PIPELINE_REDESIGN.md, le journal de conception de la campagne, et les notes sur les états de temporisation du Visual6502 (6502_TIMING.txt).

Modèle Mémoire

Le modèle mémoire du pico6502 est structurellement identique à celui du picoZ80 avec une différence essentielle : le 6502 n'a pas d'espace d'adressage E/S séparé. Il n'y a pas de tableau ioPtr[] dans t_6502PSRAM et pas de section JSON iomap. Tous les registres de périphériques sont mappés dans l'espace d'adressage mémoire de 64 Ko à l'aide de blocs de type FUNC.

Niveau 1 — SRAM RP2350 (Table de Dispatch Rapide)

128 entrées de 32 bits dans _membankPtr[], une par bloc de 512 octets de l'espace d'adressage de 64 Ko. Chaque entrée encode le type de bloc, le numéro de banque PSRAM et l'adresse de base du bloc en un seul mot de 32 bits pour un dispatch en O(1) à chaque cycle bus :
// 32-bit membankPtr encoding
// Bits 31–24: MEMBANK_TYPE_xxx constant (bit flags)
// Bits 23–0:  24-bit PSRAM pool address of the block (bank × 64KB + block base)
//             — for PTR blocks, the memPtr[] table index; for PHYSICAL, the host address
//   e.g. 16K virtual RAM at $4000 in bank 0:
//   membankPtr[blk] = (MEMBANK_TYPE_RAM << 24) | (blk * MEMORY_BLOCK_SIZE);

Niveau 2 — PSRAM (8 Mo)

La PSRAM de 8 Mo est partagée entre la zone RAM/ROM et les tables de pointeurs de fonctions :
typedef struct {
    uint8_t      RAM[MAX_MEMORY_BANKS * MEMORY_PAGE_SIZE]; // 64 banks × 64KB = 4MB RAM/ROM area
    MemoryFunc   memPtr[MEMORY_PAGE_SIZE];                  // 64K per-byte read redirect (PTR type)
    MemoryFunc   memioPtr[MEMORY_PAGE_SIZE];                // 64K memory-mapped handler array (FUNC type)
    // Note: no ioPtr[] — 6502 has no separate I/O space
} t_6502PSRAM;

Niveau 3 — Flash (16 Mo)

La Flash de 16 Mo contient le bootloader, deux slots de firmware applicatif de 5 Mo, deux slots de configuration pour les images ROM et le JSON minifié, la configuration générale et la table des partitions. Voir le tableau Organisation de la Mémoire Flash dans la section Architecture Firmware ci-dessus.

Types de Blocs Mémoire

Constante Valeur Comportement
MEMBANK_TYPE_PHYSICAL 0x01 Passthrough vers le matériel hôte réel
MEMBANK_TYPE_PHYSICAL_VRAM 0x02 RAM vidéo de l’hôte avec wait states configurables
MEMBANK_TYPE_PHYSICAL_HW 0x04 Matériel hôte (fenêtre d’E/S) — jamais l’objet de spéculation ; active le parking d’E/S piloté par la configuration (JSON PHYSICALHW)
MEMBANK_TYPE_RAM 0x08 Lecture/écriture — stockée en PSRAM
MEMBANK_TYPE_VRAM 0x10 Miroir PSRAM de la RAM vidéo avec wait states
MEMBANK_TYPE_ROM 0x20 Lecture seule — stockée en PSRAM ; les écritures sont silencieusement ignorées
MEMBANK_TYPE_FUNC 0x40 Périphérique virtuel — le handler memioPtr[addr] est appelé à chaque accès
MEMBANK_TYPE_PTR 0x80 Redirection par octet via memPtr[] vers le physique, la RAM ou la ROM
Les entrées de carte mémoire peuvent porter une task de type COPYTOPHYSICAL ou COPYTORAM pour copier une région entre la mémoire hôte et la PSRAM à l'initialisation. Le pilote BBC-B utilise les banques 1–4 du pool PSRAM (SW_POOL_BASE = 0x10000, 16 Ko par socket) pour ses seize sockets latéraux virtuels et la banque 0 pour l'espace d'adressage actif ; la carte est repointée à chaque écriture dans ROMSEL tandis que l'écriture du latch elle-même atteint toujours le matériel.

Référence de Configuration

Le pico6502 utilise le même mécanisme de configuration JSON que le picoZ80. Les clés de premier niveau sont "esp32" et "rp2350". La section propre au CPU utilise la clé "6502" (au lieu de "z80") et, exactement comme le z80[] du Z80, c'est un tableau indexé par partition flash — l'index 0 est le slot bootloader inutilisé, les index 1 et 2 configurent les deux partitions applicatives. Le 6502 n'ayant pas d'espace E/S, il n'y a pas de tableau "io" — seulement un tableau "memory" dans chaque entrée de partition.

esp32.core

Clé Type Description
device string Type de CPU — l’un de "Z80", "6502" ou "6512". Sur le pico6502, cette clé pilote la ligne DBE_OVR (GPIO38 de l’ESP32) ; elle doit donc concorder avec le cputype configuré côté RP2350.
mode integer Mode de démarrage par défaut : 0 = client (station), 1 = point d’accès.

esp32.wifi

Clé Type Description
override 0/1 1 = appliquer tous les paramètres wifi de ce bloc ; 0 = utiliser les paramètres NVS.
wifimode string "ap" ou "client".
ssid string Nom du réseau à créer (AP) ou à rejoindre (client).
password string Phrase de passe WiFi.
ip string Adresse IP fixe.
netmask string Masque de sous-réseau.
gateway string Passerelle par défaut.
dhcp 0/1 Mode client uniquement. 1 = DHCP, 0 = IP fixe.
webfs string Répertoire racine du système de fichiers web sur la carte SD (par défaut "webfs").
persist 0/1 1 = réécrire les paramètres résolus dans la NVS à chaque démarrage.

rp2350.core

Clé Type Description
cpufreq integer Horloge système du RP2350 en Hz (par ex. 300000000).
psramfreq integer Horloge SPI de la PSRAM en Hz (par ex. 133000000).
voltage float Tension du cœur RP2350 (par ex. 1.10).

rp2350.6502[ ] — Partitions CPU

Chacune des deux entrées de partition actives porte les clés de niveau CPU à côté de ses tableaux memory et drivers :
Clé Type Description
cputype string "6502", "6512", "65C02" ou "65C12" — sélectionne le mode d’horloge (voir Génération d’Horloge PHI1/PHI2 ci-dessus) et, pour les variantes C, la surcouche d’instructions CMOS. Toute autre valeur journalise Warning: unknown 'cputype' et retombe sur 6502.
dbeoverride 0/1 1 = forcer l’activation des buffers du bus de données via DBE_OVR, pour les hôtes qui ne pilotent pas la broche DBE du socket. Les personas 6502 fournies le positionnent ; l’interface graphique le désactive par défaut pour un 6512.
core object Surcharge par partition de cpufreq / psramfreq / voltage ; le rp2350.core global sert de repli. C’est ici que l’interface web stocke l’horloge et la tension d’une partition.
ioparkaddr hex string Adresse de parking pour le parking/chase d’adresse E/S ; vaut par défaut 0xFFFC dès qu’une région PHYSICALHW est déclarée.
device[] / interlink[] array Le catalogue de périphériques et la netlist partagés, comme sur le Z80 (présents mais vides dans les personas fournies).

rp2350.6502[n].memory[ ]

Définit la carte mémoire du 6502. Tous les périphériques doivent y figurer comme entrées de type FUNC — il n'y a pas de carte d'E/S séparée.
Clé Type Description
enable 0/1 Indique si cette entrée est active.
addr hex string Adresse de début dans l’espace d’adressage 6502 (par ex. "0xE000").
size hex string Taille de la région en octets (par ex. "0x2000").
type string PHYSICAL, PHYSICALVRAM, PHYSICALHW, RAM, VRAM, ROM ou FUNC.
bank integer Numéro de banque PSRAM pour les types RAM/ROM (0–63).
task string COPYTOPHYSICAL / COPYTORAM, ou un handler nommé pour les blocs de type FUNC.
file string Chemin sur la carte SD d’une image ROM à charger au démarrage (type ROM).

rp2350.6502[n].drivers[ ]

Chaque entrée active un pilote de machine nommé (mis en correspondance avec la table virtualFuncMap[] du firmware — Open6502 dans les deux modèles 6502, BBC-B dans le modèle BBC6502), charge optionnellement des images ROM en PSRAM au démarrage, et peut porter des surcharges de réglage du moteur bus ainsi que des options de machine. Le réglage propre au pilote (BBCModelB_Tuning) est appliqué à l'analyse de la configuration avant les clés JSON, de sorte que toute clé de l'objet pilote surcharge une valeur par défaut intégrée ; le panneau Bus Engine Tuning de l'interface web n'émet que les clés que vous définissez.
Clé Type Description
enable 0/1 Indique si cette entrée de pilote est active.
name string Nom du pilote — mis en correspondance sans distinction de casse avec virtualFuncMap[].
rom[] array Images ROM à charger au démarrage. Chaque entrée : enable, file, loadaddr[].
ioregionlo / ioregionhi integer La fenêtre d’E/S de l’hôte (g_ioRegionLo/Hi). La déclarer active la spéculation. BBC-B : 0xFC00–0xFF00.
shadowwrlo / shadowwrhi integer Fenêtre du shadow write-through — la mémoire que ce CPU est seul à écrire. BBC-B : 0x0000–0x8000.
pageregaddr / pageregmask integer Registre de pagination et masque de décodage. BBC-B : ROMSEL 0xFE30 / 0xFFF0.
pageregionlo / pageregionhi integer Région commutée par le registre de pagination. BBC-B : 0x8000–0xC000.
burstspec 1–6 Profondeur du burst d’opcodes spéculatif. BBC-B : 6.
busserve / buspipe / fastfetch boolean Surcharges à trois états (absent = valeur par défaut du pilote) pour le serve shadow, l’émission pipelinée et le fetch rapide.
modelaram, mastermode, ossocket, sockets[], presetdiscroms, persist, if[] — Options de machine BBC-B — voir la page du projet pour chaque clé. if[] lie les interfaces : CMT-UEF (speed, jusqu’à six slots de cassette param[].file), BBC-FDC (chip “8271” ou “1770” ; board “acorn”, “master” ou “watford” pour le 1770, master par défaut lorsque mastermode est positionné ; speed 0/1 ; quatre slots param[] — lecteur 0 face A, lecteur 0 face B, lecteur 1 face A, lecteur 1 face B — chacun { "file", "type": "auto|ssd|dsd|mmb|adf|adl|dds|ddd", "enable" }) BBC-MMC (param[0].file = l’image MMB, par défaut mmb/BEEB.MMB) et BBC-NET (station 0–253, 0 = octet bas de notre IP ; net ; aunport, par défaut 32768 ; server “ip” ou “ip:port” ; serverstation, par défaut 254 ; servernet ; hosts[] de { station, net, ip, port } ; clock).

Entrée rom[] :

Clé Type Description
enable 0/1 Indique s’il faut charger cette image ROM.
file string Chemin sur la carte SD du binaire ROM.
loadaddr[] array Un ou plusieurs descripteurs d’adresse de chargement : enable, position, addr, bank, size.

Framework de Périphériques Virtuels

Le framework de périphériques virtuels est structurellement identique au framework du picoZ80 décrit dans le Guide Technique picoZ80. La différence essentielle est que tous les périphériques virtuels sont enregistrés dans memioPtr[] — il n'existe pas d'équivalent de ioPtr[] pour le 6502.

Signatures des Handlers

// Memory / peripheral handler — installed in memioPtr[addr]
// Called for every access to a MEMBANK_TYPE_FUNC block
typedef uint8_t (*MemoryFunc)(M6502CPU *cpu, bool read, uint16_t addr, uint8_t data);

// Driver init function — registered in virtualFuncMap[]
// Called twice: pass=0 (validation), pass=1 (configuration)
typedef int (*VirtualFunc)(M6502CPU *cpu, t_drvConfig *drvConfig, int pass);

Écrire un Pilote de Périphérique

Pour ajouter un périphérique virtuel au pico6502 :
  1. Créez une fonction handler avec la signature MemoryFunc. Le handler reçoit read=true pour les cycles de lecture 6502 (RNW=1) et read=false pour les cycles d'écriture (RNW=0).
  2. Créez une fonction d'initialisation avec la signature VirtualFunc. Lors de la passe de configuration (pass=1), installez votre handler dans cpu->_6502PSRAM->memioPtr[addr] pour chaque adresse occupée par votre périphérique.
  3. Enregistrez le nom du pilote et la fonction d'initialisation dans la table virtualFuncMap[] de M6502CPU.c.
  4. Ajoutez une entrée de type FUNC dans le tableau memory de config.json pour la plage d'adresses.
  5. Ajoutez une entrée de pilote dans config.json avec le champ "name" correspondant.
Voir le Guide du Développeur pico6502 pour un exemple complet détaillé incluant l'implémentation d'un périphérique VIA6522.
Deux fonctionnalités du framework sont arrivées avec le premier vrai périphérique virtuel, le lecteur de cassette UEF du BBC (src/drivers/Acorn/BBCCMT.c). Un handler peut lever la ligne d'interruption virtuelle g_m6502VirtIrq, que le cœur combine par OU avec le /IRQ physique lors de son échantillonnage par instruction — le lecteur virtualise l'ACIA 6850 ($FE08/09) et l'ULA série ($FE10) avec des entrées FUNC par octet, fournit les octets et les événements de porteuse DCD à la cadence réelle de 1200/300 bauds depuis un g_fastTickHook qui s'exécute dans la boucle du Core 1, et lève la ligne exactement comme le ferait la vraie ACIA ; les écritures passent toujours aux composants physiques, de sorte que le relais moteur réel clique, les bits d'activation d'interruption de l'ACIA physique étant masqués pour qu'elle ne puisse pas submerger l'hôte. Et un pilote peut déclarer un réglage du moteur bus pour sa machine (BBCModelB_Tuning) : la fenêtre d'E/S, la fenêtre d'écriture shadow, le registre de pagination et la profondeur de burst — la « connaissance de la machine » qui permet au moteur de spéculer en toute sécurité, appliquée avant le JSON afin que la configuration puisse la surcharger.
Deux autres interfaces Acorn (octobre 2026) établissent le modèle du stockage virtuel par blocs. Deux modèles de composants indépendants de l'hôte résident dans src/drivers/ : I8271.c, l'Intel 8271 (interface de registres, jeu de commandes complet, sémantique status/result, ports de contrôle des lecteurs, sortie INT), et WD177x.c, les Western Digital WD1770/1772/1773 (commandes de Type I–IV avec taux de pas, mise en rotation, stabilisation et vérification ; Read Address, Read Track et Write Track avec la piste brute agencée comme le décrit la fiche technique, de sorte que *FORMAT fonctionne ; Force Interrupt ; temporisation moteur), tous deux avec une temporisation mécanique optionnelle et tous deux derrière une table de callbacks média abstraite. src/drivers/Acorn/BBCFDC.c est la glu Model B, sélectionnable par configuration : le 8271 en $FE80–$FE9F, ou un 1770 derrière un latch de contrôle des lecteurs dont l'agencement a été lu dans la ROM DFS écrite pour chaque carte — la mise à niveau Acorn Model B (latch $FE80, composant $FE84), la carte Watford Electronics (mêmes adresses, bits de latch différents) et le Master 128 (latch $FE24, composant $FE28, qui sur un hôte Model B serait sinon décodé vers l'ULA vidéo, les écritures ne doivent donc jamais atteindre le bus). Des entrées FUNC recouvrent la fenêtre, les écritures n'étant jamais transmises au bus ; l'INT du composant (8271) ou INTRQ|DRQ (1770) devient un front /NMI sur le cœur émulé via le timer de périphérique du cœur, de sorte que l'état et le NMI changent ensemble un temps d'octet (FM 64 µs, MFM 32 µs) après que l'hôte a consommé l'octet précédent, et qu'un système de fichiers dont le handler NMI touche lui-même le composant ne voit jamais l'événement suivant imbriqué dans le handler. Deux lecteurs mappent leurs faces sur des fichiers image — DFS SSD/DSD, double densité DDS/DDD (16 ou 18 secteurs selon la taille), ADFS ADF (S/M, ou L face après face) et ADL (L entrelacé), ou un disque d'un paquet MMB ; un second fichier simple face dans le slot B d'un lecteur le rend double face, un disque simple face répond à la face 1 par secteur non trouvé, et le type est configuré par slot ou déduit de l'extension et de la taille du fichier sondées au montage sur le Core 0. Tant que l'interface est activée, elle positionne g_m6502NmiPhysMask, de sorte que les fronts /NMI du contrôleur physique sont comptés et ignorés — le périphérique virtuel possède la ligne. src/drivers/Acorn/BBCMMC.c virtualise la carte MMFS : une surcouche partielle du User VIA (le port B, le registre à décalage, les bits de mode SR de l'ACR et le bit 2 de IFR/IER sont virtuels ; le port A, CA1/CA2, les deux timers et les autres bits d'interruption passent au travers, les écritures du port B n'étant jamais répercutées sur le bus), une machine d'état de carte SD SPI annonçant SDHC, et une façade FAT16 dont le répertoire racine contient un BEEB.MMB contigu mappé directement sur les décalages d'octets du fichier MMB. Les deux utilisent le même moteur : un cache de blocs PSRAM à correspondance directe (les 512 Ko du FDC en 0x070000, les 384 Ko du MMC en haut du pool) rempli via MSG_READ_SECTOR avec une seule lecture en vol, en write-back avec les blocs sales vidés 300 ms après leur dernière mise à jour, les réponses étant consultées dans la file inter-cœurs et retirées uniquement lorsqu'elles appartiennent au pilote. La règle de pile héritée du lecteur de cassette s'applique partout : les handlers et le tick rapide s'exécutent à l'intérieur de m6502_run sur le Core 1 et ne font qu'enregistrer les événements ; le poll du pilote, sur la pile de premier niveau, les rapporte.
La quatrième interface Acorn, src/drivers/Acorn/BBCNet.c, porte le modèle sur le réseau. src/drivers/MC6854.c est un modèle au niveau trame, indépendant de l'hôte, de l'ADLC Motorola MC6854 — des FIFO de réception et d'émission qui se remplissent et se vident au débit d'octets de la ligne, la sémantique ADDRESS PRESENT / FRAME VALID / RDA / TDRA / FRAME COMPLETE dans les modes de transfert à 1 et 2 octets, et des activations d'interruption où chaque condition qui monte alors qu'elle est activée affirme IRQ, de sorte qu'une condition persistante ne masque jamais une nouvelle — écrit d'après ce que font réellement NFS 3.60 et ANFS 4.x (CR1 $C1 → CR4 $1E, CR3 $00, CR1 $82, CR2 $67 à l'initialisation ; réception avec RIE et transferts à deux octets ; émission de deux octets par NMI avec TIE/RTS). La glu recouvre l'ADLC en $FEA0–$FEBF et les liaisons de station en $FE18–$FE1F, transforme l'IRQ de l'ADLC en front /NMI, et exécute le handshake à quatre voies Econet (scout, ack, data, ack) trame par trame face à la ROM sur le tick rapide du Core 1, en le traduisant en AUN exactement comme le fait un pont : un scout est acquitté localement après un temps de trame, la trame de données devient un unicast AUN (en-tête de 8 octets — type, port, contrôle, bourrage, séquence 32 bits — dans un datagramme UDP vers le port 32768), l'ack AUN du pair devient la trame finale ; un unicast entrant est présenté comme un scout, l'ack de la ROM amène la trame de données, l'ack final de la ROM repart sous forme d'ack AUN ; les immediates et les broadcasts correspondent à leurs types AUN. La boucle principale du Core 0 (BBCNet_Core0Service) déplace les datagrammes entre deux anneaux PSRAM (8 en sortie, 16 en entrée, slots de 1600 octets en 0x0F0000) et un socket UDP sur l'ESP32, via la même IPC réseau que celle utilisée par la carte Celestite, étendue avec des sockets datagramme, le broadcast et les envois adressés. Les stations sont mappées en IP selon la convention AUN (station = octet bas de l'adresse, port 32768) avec des entrées explicites pour le serveur de fichiers et tout hôte qui ne la suit pas, et les datagrammes entrants sont attribués à une station de la même manière. En mode Master, les numéros de station et de serveur de fichiers sont également écrits dans la CMOS virtuelle.

Coprocesseur ESP32

Le coprocesseur ESP32 est identique en fonction et en implémentation à l'ESP32 du picoZ80 — consultez le Guide Technique picoZ80 — Coprocesseur ESP32 pour la description complète de l'interface carte SD, de la structure du serveur web et du protocole de commandes RP2350–ESP32.

Carte SD

La carte SD est gérée exclusivement par l'ESP32 via SPI. Le RP2350 demande les lectures et écritures de fichiers en postant des messages dans la file inter-cœurs ; le Core 0 relaie ces requêtes à l'ESP32 par UART et renvoie les résultats. L'organisation des répertoires de la carte SD du pico6502 est identique à celle du picoZ80 : webfs/ pour les fichiers web, ROM/ pour les images ROM, et config.json à la racine.

Serveur Web

Le même serveur web Bootstrap 4 à huit pages s'exécute sur l'ESP32, et depuis la mise à jour sensible au CPU, l'interface s'adapte au processeur qu'elle sert réellement : le serveur substitue une variable de modèle %SK_PROCESSOR% à partir du type de périphérique du firmware en cours d'exécution, et le JavaScript s'ajuste en conséquence — la page des personas n'affiche que les personas marquées pour le CPU détecté, l'interface de configuration propose la liste des pilotes 6502 (Open6502, BBC-B) et les noms de broches (les concepts propres au Z80 tels que les régions d'E/S et le diviseur de rafraîchissement étant masqués, et les menus média CMT/QD supprimés via %SK_Z80MEDIAVISIBLE%), ajoute les types de CPU 65C02/65C12, la tension/l'horloge du cœur par partition, le panneau Bus Engine Tuning par pilote, les options BBC-B avec le tableau des sockets latéraux, et le carrousel CMT-UEF à six slots, et p6502.css applique le jeu de couleurs propre au 6502 par-dessus la mise en page partagée de common-ui.css. Une variable de modèle non développée (firmware plus ancien) est traitée comme Z80.

Protocole de Commandes

Le protocole de commandes RP2350–ESP32 est identique pour le picoZ80 et le pico6502 : des trames IPC binaires avec une queue CRC32 sur la liaison FSPI à 60 MHz, chaque réponse validée par rapport à l'opcode et au numéro de séquence de la commande, l'UART à 460,8 kbaud servant de pont console USB-vers-ESP32. Il est défini dans ipc_protocol.h et ESP.c / FSPI.c et documenté dans le Guide du Développeur picoZ80. Sur un hôte 6502, le rapport INF transporte la file de cassettes UEF à la place de la file de cassettes Z80 et signale floppySides = 2 afin que le menu Floppy du tableau de bord affiche Drive 0/1 × Side A/B (la commande CHGD transporte alors un quatrième et un cinquième champ, la face et le type) ; l'interface web masque les menus CMT et Quick Disk propres au Z80 ; les datagrammes AUN du module Econet transitent par les commandes `NET_SOCK` / `NET_SEND` / `NET_RECV`, qui prennent désormais en charge les sockets datagramme UDP avec broadcast et envois adressés ; la commande TIME et la synchronisation MSM5832 sont aujourd'hui réservées au Z80.

Débogage SWD — RP2350

La configuration de débogage SWD du pico6502 est identique à celle du picoZ80 — consultez le Guide Technique picoZ80 — Débogage SWD pour la description complète des connexions matérielles, de la cible OpenOCD personnalisée rp2350_tzpu.cfg et de l'initialisation globale de GDB. Les différences avec le picoZ80 sont :
  • L'ELF du firmware principal est build/bin/model/Base6502/Base6502_0x10020000.elf (et non BaseZ80).
  • L'entrée add-auto-load-safe-path de ~/.gdbinit doit référencer Base6502.

Configuration d'OpenOCD

sudo cp rp2350_tzpu.cfg /usr/local/share/openocd/scripts/target/
openocd -f interface/cmsis-dap.cfg -f target/rp2350_tzpu.cfg -c "adapter speed 5000"

Configuration de GDB

set history save on
set history filename ~/.gdb_history
set history size 65536
add-auto-load-safe-path build/bin/model/Base6502/.gdbinit:build/bin/model/Bootloader/.gdbinit
# Bootloader — Core 0
cd build/bin/model/Bootloader
cp ../../../../.gdbinit.bootloader.3333 .gdbinit
gdb-multiarch Bootloader.elf

# Bootloader — Core 1 (separate terminal)
cd build/bin/model/Bootloader
cp ../../../../.gdbinit.bootloader.3334 .gdbinit
gdb-multiarch Bootloader.elf
# Main firmware — Core 0
cd build/bin/model/Base6502
cp ../../../../.gdbinit.3333 .gdbinit
gdb-multiarch Base6502_0x10020000.elf

# Main firmware — Core 1 (separate terminal)
cd build/bin/model/Base6502
cp ../../../../.gdbinit.3334 .gdbinit
gdb-multiarch Base6502_0x10020000.elf

ESP32 — Débogage USB

Identique au picoZ80 — connectez l'USB au port USB de l'ESP32 et utilisez l'interface USB-JTAG intégrée de l'ESP32-S3 (aucune sonde externe requise).
openocd -f board/esp32s3-builtin.cfg

xtensa-esp32s3-elf-gdb esp32/build/main.elf
(gdb) target extended-remote :3333

Système de Compilation

Le système de compilation est identique à celui du picoZ80 — CMake avec le Pico SDK 2.x ciblant le RP2350. Le pico6502 ne requiert pas la bibliothèque d'émulation Z80 Zeta. Consultez le Guide Technique picoZ80 — Système de Compilation pour la description complète des prérequis, de la configuration Docker pour ESP-IDF et du script de compilation. Les différences propres au pico6502 sont notées ci-dessous.

Cibles de Compilation

Cible Sortie Description
Base6502 Base6502_0x10020000.uf2 Firmware 6502 principal — adresse de chargement du Slot 1
Base6502 Base6502_0x10520000.uf2 Firmware 6502 principal — adresse de chargement du Slot 2
Base6502_DBGSH Base6502_0x10020000_DBGSH.uf2 / _0x10520000_DBGSH.uf2 Builds avec shell de débogage (ICE) des deux slots
BBC6502 BBC6502_0x10020000[_DBGSH].uf2 / _0x10520000[_DBGSH].uf2 Base6502 plus les pilotes Acorn (INCLUDE_ACORN_DRIVERS) : persona BBC-B, lecteur de cassette UEF, contrôleur de disquette virtuel 8271 / WD1770 (DFS, DDFS, ADFS), carte MMFS virtuelle, module Econet virtuel (AUN sur IP), commande shell bbc
Bootloader Bootloader.uf2 Bootloader partagé (identique au picoZ80)
ESP32 main.bin Firmware ESP32 (partagé avec le picoZ80)
Comme chaque modèle de l'arborescence partagée, chaque modèle 6502 compile quatre variantes — deux partitions flash, chacune avec et sans le shell de débogage — via la macro CMake add_m6502_model_targets, qui définit TARGET_CPU_M6502, compile M6502.c et M6502CPU.c avec optimisation partielle à l'édition de liens et lie en -O3. Base6502 (v2.224) ne compile que le jeu de pilotes Open6502 (INCLUDE_OPEN6502_DRIVERS) : un remplacement MOS 6502/6512 ordinaire sans pilotes propres à une machine, utilisé par la réplique Apple I. BBC6502 (v1.049) est identique en cœur et en valeurs par défaut et ajoute INCLUDE_ACORN_DRIVERS (qui tire également les modèles de composants génériques I8271.c, WD177x.c et MC6854.c) ; il réutilise Base6502/main.c, ne différant que par les pilotes de machine compilés et par la gestion de la commande de changement de disquette CHGD,<drive>,<file>[,<side>,<kind>] venant de l'ESP32. build_tzpuPico.sh 6502 compile Base6502 ; BBC6502 est produit par la compilation complète. Les binaires sont nommés d'après la version du modèle (BBC6502_0x10020000_DBGSH_1.048.bin).

Commandes de Compilation

# Set up (once) — from project root
export PICO_PATH=/path/to/pico-sdk
./get_and_build_sdk.sh

# Standard release build of the 6502 model
./build_tzpuPico.sh 6502

# Debug build
./build_tzpuPico.sh 6502 DEBUG

# ESP32 only (using Docker idf54 alias)
cd projects/tzpuPico/esp32
idf54 build

# Flash RP2350 via USB mass-storage (BOOTSEL mode)
cp build/bin/model/Bootloader/Bootloader.uf2 /media/$USER/RPI-RP2/
cp build/bin/model/Base6502/Base6502_0x10020000.uf2 /media/$USER/RPI-RP2/

# Flash ESP32 (initial, via esptool)
esptool.py --chip esp32s3 --port /dev/ttyUSB0 --baud 460800 \
  write_flash 0x0   esp32/build/bootloader/bootloader.bin \
             0x8000 esp32/build/partition_table/partition-table.bin \
             0x10000 esp32/build/main.bin

Architecture du Shell de Débogage

Le shell de débogage (ICE) est un seul fichier source — src/dbgsh.c — compilé une fois par image firmware et rendu indépendant du CPU grâce à une vtable t_CpuOps définie dans src/include/cpu.h. La vtable couvre l'identité (nom du CPU ; la chaîne de drapeaux, NV-BDIZC pour le 6502 contre SZYHXPNC pour le Z80), les drapeaux de capacités, l'accès au banc de registres, l'encodage des instructions (disasmOne/asmOneLine/instrLen/isCall) et les opérations bus, tandis que les macros partagées CPU_MEMBANKPTR()/CPU_PSRAM() gardent les commandes mémoire véritablement indépendantes du CPU. Les limites de points d'arrêt et les constantes associées ont été déplacées dans src/include/dbghooks.h afin que les deux CPU partagent une définition unique. La bannière du shell annonce la personnalité en cours d'exécution : --- pico6502 Debug Shell ---.
Les drapeaux de capacités — hasIoSpace, hasM1Cycle, hasInterruptModes, tous false sur le 6502 — sont ce qui façonne le jeu de commandes : le build Z80 compte 63 commandes, le build 6502 61 (62 sur BBC6502, qui ajoute bbc). Seize commandes propres au Z80 sont exclues de la compilation sur le 6502 (iomap, in, out, fwait, iowait, intcount, dev, stall, sig, dma, ctc, pio, ser, rtc, prn), et busdiag n'est pas supprimée mais remplacée — chaque CPU reçoit sa propre implémentation adaptée à son bus.

Diagnostics de Mise en Service — busdiag et piodbg

Les diagnostics propres au 6502 ont été écrits avant le matériel pour la mise en service de la carte, et se sont développés au fil de la mise en service et de la campagne de vitesse jusqu'à devenir l'instrument avec lequel le moteur a été réglé :
  • busdiag — un instantané de chaque signal du socket et de contrôle des buffers plus les bus d'adresses et de données ; busdiag clk mesure PHI0 / PHI1_IN / PHI2_IN et les compare (distinguant un buffer mort d'une horloge morte) ; busdiag watch [n] prend n instantanés rapides.
  • busprobe <addr> [count] — relit une adresse en boucle pour exposer des lignes de données instables ; busverify <addr> <len> [passes] — relecture sûre pour la ROM d'une plage, signalant les lectures instables et les divergences fetch/read ; memtest avec un mode decay pour les défauts de rafraîchissement.
  • bustest [loops] et wrtest [secs] [mode] [addr] — pilotent des motifs marqués sur R/W, la phase PHI2 et OE_CLK pour la caractérisation à l'analyseur logique ; busstate — PC des machines d'état, FSTAT et drapeaux IRQ en direct pour l'analyse des blocages (il a prouvé que les expériences de pipeline côté C ayant échoué se garaient proprement — le défaut était une corruption de données, pas un interblocage) ; piodbg — le dump du PIO.
  • writestrobes <1|2>, iopark on|off, readsettle <0-31>, readphase latch|hi|lo — les boutons A/B en direct ; buspipe avec on|off, fetch, rungs <1-4>, burst <1-6>, iochase, iorange <lo> <hi>, serve, natwr, wrregion, wrhold <0-31>, wrverify <lo> <hi>|off, slowclr, stats ; pipetest <iters> — le banc oracle d'opérations bus mixtes du Core 1.
  • cpustat — instructions/s et cycles émulés/s par rapport à l'horloge hôte mesurée (appeler deux fois pour obtenir un taux) ; cpuprof — histogramme du temps par instruction par classe de bus. Les statistiques optionnelles (g_dbgStats, recensement par classe d'opération, causes de ratés, histogrammes de latence) coûtent 3–5 % et sont désactivées par défaut ; sans le shell de débogage, elles sont exclues de la compilation.
  • bbc (BBC6502 uniquement) — état de la persona ; mask <0|1>, socket <n> <0|1>, cmosdef, oscheck, tape sel|next|rew|trace|on|off, fdc (état, y compris le composant et la carte) / fdc <drive> <file|eject> [A|B] [auto|ssd|dsd|mmb|adf|adl|dds|ddd] / fdc trace on|off, mmc (état) / mmc trace on|off, net (état : trames, UDP entrant/sortant, acks, délais dépassés, coût SPI par datagramme, latence de réponse) / net trace on|off / net tick <us>.
Le dump des registres 6502 affiche A/X/Y/S (la pile étant rendue sous la forme $01nn), PC, puis P avec sa chaîne de drapeaux, puis l'état IRQ/NMI/arrêt et le compteur de cycles. set flags accepte soit un masque de lettres positionnel NV-BDIZC, soit une valeur hexadécimale brute.

Assembleur / Désassembleur

src/M6502dis.c fournit les deux sens. Sa table d'opcodes à 256 entrées est générée à partir des étiquettes case de M6502.c lui-même, de sorte que le désassemblage correspond toujours à ce que l'émulateur exécute réellement. Les 13 modes d'adressage sont couverts, les opcodes non documentés sont préfixés par *, et l'assembleur monoligne associé accepte textuellement la sortie du désassembleur (* inclus) ainsi que les formes d'opérande $hex, %binary, décimale, suffixe h et 0x. L'appariement est vérifié sur 2048 cas aller-retour — l'invariant est un aller-retour textuel, et non une identité d'octets, car 35 encodages NMOS sont des doublons et ne peuvent pas survivre à un aller-retour octet pour octet. L'API est sans handle CPU (lectures par callbacks), ce qui la rend testable de manière autonome sur l'hôte.

Tests de Conformité

Comme M6502.c et M6502dis.c n'ont aucune dépendance au RP2350, ils sont accompagnés d'une suite de conformité côté hôte dans src/test/M6502/ (m6502_test.c, m6502dis_test.c), compilée avec le compilateur natif :
make -C src/test/M6502 run
La couverture est exhaustive partout où l'exhaustivité est peu coûteuse :
  • ADC/SBC binaires sur l'ensemble des 256 × 256 × 2 combinaisons opérandes/retenue, vérifiées par rapport à l'arithmétique C ordinaire.
  • ADC/SBC décimaux (BCD) sur toutes les paires BCD valides.
  • CMP sur l'ensemble des 256 × 256 paires.
  • L'aller-retour complet désassemblage → assemblage sur les 256 opcodes, avec des exemptions explicites et listées pour les encodages dupliqués.
La suite existe parce qu'elle a attrapé de vrais défauts pendant le développement — un décalage d'une unité sur SBC, un ADC décimal perdant le quartet bas, un SBC décimal non implémenté, et un CMP ne positionnant jamais Z — chacun faux dans tous les cas, ce qui est précisément ce qu'un balayage exhaustif expose instantanément.
Sur le matériel, le moteur bus transporte son propre oracle : le vérificateur shadow compare chaque octet dépilé du bus avec le shadow write-through et le compteur de divergences doit rester à zéro au cours de chaque test d'endurance (Elite, Aviator, Galaxians, MMFS, longues sessions inactives) ; pipetest exécute des opérations bus mixtes face au même oracle ; busverify et memtest vérifient le bus brut ; et les captures Kingst LA5016 d'un vrai 6502 et du pico6502 dans le même BBC sont l'arbitre final de ce que le socket voit réellement. Hôtes vérifiés à ce jour : BBC Model B (v2.3a, 340 MHz) et la réplique Apple I RC6502 (150 MHz ; moniteur Woz et BASIC, quelques erreurs mineures de memtest RAM relevées sur le premier build sérialisé). Pas encore vérifiés sur le matériel : le comportement à l'écran de la surcouche RAM du Model A, le mode Master sur un vrai Master, le mode d'horloge 6512/DBE, et la broche SO.

Sites de Référence

Ressource Lien
Page du projet pico6502 /pico6502/
Manuel Utilisateur pico6502 /pico6502-usermanual/
Guide du Développeur pico6502 /pico6502-developersguide/
Guide Technique picoZ80 /picoz80-technicalguide/
Page du projet picoZ80 /picoz80/
Fiche technique RP2350 datasheets.raspberrypi.com
API Multicore du Pico SDK raspberrypi.github.io/pico-sdk-doxygen
Fiche technique MOS 6502 archive.org
Documentation esptool docs.espressif.com
Bibliothèque cJSON github.com/DaveGamble/cJSON

Avis Réglementaire sur les Communications Sans Fil

Cet appareil intègre un module sans fil ESP32-S3-PICO-1 qui émet dans la bande ISM 2,4 GHz, ce qui en fait un émetteur intentionnel au titre des réglementations de radiofréquence dans le monde entier (incluant le FCC Part 15 Subpart C aux États-Unis et la Directive Équipements Radio 2014/53/UE dans l'Union Européenne).
Bien que le module ESP32-S3-PICO-1 lui-même possède des certifications réglementaires préexistantes (FCC, CE et autres), ces certifications au niveau module ne s'étendent pas automatiquement à un produit fini qui intègre le module. L'exemption de module pré-certifié permet aux amateurs individuels de construire un nombre limité d'appareils pour un usage personnel, expérimental ou éducatif sans obtenir d'autorisation d'équipement séparée.
Limitations Importantes
  • Les appareils assemblés ne doivent pas être vendus, proposés à la vente, offerts ou autrement distribués à des tiers à moins que le produit fini n'ait été testé de manière indépendante et n'ait obtenu sa propre autorisation d'équipement (par ex. FCC ID, marquage CE avec évaluation d'un Organisme Notifié) dans la juridiction concernée.
  • La construction de ce projet pour un usage personnel en quantités limitées est généralement autorisée sous les dispositions d'usage amateur et expérimental (par ex. FCC § 15.23), à condition que l'appareil ne cause pas d'interférences nuisibles.
  • Les exigences réglementaires varient selon les pays. Les constructeurs en dehors des États-Unis doivent consulter leur autorité nationale de radiofréquence pour les règles applicables.
Responsabilité du Constructeur
Il incombe uniquement au constructeur de s'assurer que tout appareil construit à partir de ces conceptions est conforme à toutes les réglementations de radiofréquence applicables dans sa juridiction. L'auteur fournit ces conceptions pour un usage personnel, éducatif et amateur et ne garantit pas qu'un appareil construit à partir de celles-ci satisfait aux exigences réglementaires pour une distribution commerciale.