pico6502 Technischer Leitfaden

English

Hinweis: Das pico6502-Projekt befindet sich in einer fruehen Entwicklungsphase. Hardware, PIO-Bus-Interface, Speichermodell und Build-System sind vollstaendig implementiert und funktionsfaehig. Maschinen-Persona-Treiber sind in einem fruehen Stadium. Dieser Leitfaden beschreibt die technische Architektur wie sie heute besteht.

Uebersicht

Die technische Architektur des pico6502 ist in allen Punkten identisch mit dem picoZ80, ausser dem CPU-Bus-Interface. Der gleiche RP2350B Dual-Core-Prozessor, das gleiche PSRAM-Speichermodell, das gleiche Flash-Layout, der gleiche ESP32-Koprozessor und das gleiche Treiberframework werden von beiden Produkten geteilt. Dieser Leitfaden konzentriert sich auf die 6502-spezifischen Unterschiede und verweist fuer vollstaendig gemeinsame Themen auf den picoZ80 Technischen Leitfaden.

Hardwarearchitektur

Blockdiagramm

┌─────────────────────────────────────────────────────────────────────────┐
│                            pico6502 Board (v2.2)                        │
│                                                                         │
│  ┌───────────────┐   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 ]                             │
└─────────────────────────────────────────────────────────────────────────┘

Hauptkomponenten

Komponente Bauteil Spezifikation
Hauptprozessor RP2350B Dual Cortex-M33, bis 300 MHz, 512KB SRAM, 48 GPIO
PSRAM SPI PSRAM 8MB, QSPI-Interface, bis 133 MHz
Flash SPI NOR Flash 16MB, XIP (Execute-in-Place)
Koprozessor ESP32-S3 WiFi, SD-Karte, Webserver, 460,8kBaud UART zum RP2350
SD-Karte FAT32 ROM-Images, Disk-Images, config.json, Web-Dateien
Bus-Interface Widerstandsnetzwerk Serienwiderstande fuer 5V - 3,3V Pegelanpassung
Stromversorgung TLV62590BV Synchroner Abwaertswandler, 5V (VCC-Pin) auf 3,3V, gesamte Platinenversorgung
USB Integrierter Hub RP2350 USB-Massenspeicher-Bruecke fuer Firmware-Flashen

GPIO-Zuweisung

Die unten stehenden GPIO-Zuweisungen zeigen das typische Layout der pico6502 v2.2-Platine. Der KiCad-Schaltplan (kicad/PICO6502/PICO6502_Schematic.pdf) ist die massgebliche Quelle.
GPIO Signal Richtung Hinweise
0–15 A0–A15 Ausgang 6502 Adressbus — getrieben von PIO 0 m6502_addr
16–23 D0–D7 Bidirektional 6502 Datenbus — getrieben/abgetastet von PIO 0 m6502_data
24 PHI0 Eingang Externer Takteingang fuer PIO 2
25 PHI1 Ausgang Phase-1-Taktausgang — erzeugt von PIO 2
26 PHI2 Ausgang Phase-2-Taktausgang — erzeugt von PIO 2
27 RNW Eingang Read/Not-Write — abgetastet von PIO 1
28 SYNC Ausgang Opcode-Fetch-Indikator — getrieben von PIO 1
29 IRQ Eingang Maskierbarer Interrupt (aktiv low) — ueberwacht von PIO 1
30 NMI Eingang Nicht-maskierbarer Interrupt (aktiv low) — ueberwacht von PIO 1
31 RDY Eingang Ready-Signal — abgetastet von PIO 1
32 SO Eingang Set Overflow — ueberwacht von PIO 1
33 RESET Bidirektional System-Reset (aktiv low)
34–39 Flash SPI NOR Flash (XIP)
40–45 PSRAM QSPI PSRAM
46–47 ESP32 FSPI SCLK, CS zum ESP32
48–49 ESP32 UART TX/RX 460,8kBaud zum ESP32

Firmwarearchitektur

Flash-Speicherlayout

Identisch mit dem picoZ80-Layout:
Partition Adressbereich Groesse Inhalt
Bootloader 0x10000000–0x1001FFFF 128KB USB-Bruecke, Firmware-Update, Partitionsauswahl
App Slot 1 0x10020000–0x1051FFFF 5MB 6502-Hauptfirmware (Partition 1)
App Slot 2 0x10520000–0x10A1FFFF 5MB 6502-Hauptfirmware (Partition 2)
App Config 1 0x10A20000–0x10C9FFFF 2.5MB ROM-Images + minimierte Config-JSON (Slot 1)
App Config 2 0x10CA0000–0x10F1FFFF 2.5MB ROM-Images + minimierte Config-JSON (Slot 2)
General Config 0x10F20000–0x10FFEFFF 892KB Kerneinstellungen, Scratch-Bereich
Partition Table 0x10FFF000–0x11000000 4KB Aktiver Slot, Pruefsummen, Metadaten

Dual-Core-Design

Kern Verantwortlichkeiten
Core 0 USB-Massenspeicher-Bruecke; Firmware-Partitionsverwaltung; Datei-I/O-Relay zum ESP32; ESP32-UART-Befehlsverteilung; Inter-Core-Queue-Bedienung
Core 1 6502-Bus-Emulations-Hauptschleife — bedient PIO-FIFOs fuer jeden Buszyklus, loest Adressen gegen die Speicherbelegung auf, leitet an PSRAM / PHYSICAL / FUNC-Handler weiter, ruft Treiber-Poll-Callbacks auf

PIO-Bus-Interface

Das 6502-Bus-Interface ist in M6502.pio ueber alle drei RP2350-PIO-Bloecke implementiert. Der Hauptunterschied zum Z80-Interface ist das Zweiphasen-nicht-ueberlappende Taktschema des 6502: PHI0 ist ein externer Takteingang; PIO 2 erzeugt daraus PHI1 und PHI2. Dies stellt korrektes 6502-Bus-Timing unabhaengig vom Core 1-Scheduling sicher.

PIO-Programme

PIO-Block Programm Zustandsmaschinen Funktion
PIO 0 m6502_addr SM 0 Gibt 16-Bit-Adresse A0-A15 aus; signalisiert IRQ 0 (Zyklusstart)
PIO 0 m6502_data SM 1 Treibt oder tastet D0-D7 ab; signalisiert IRQ 1 (Datenphase); Tri-State-Steuerung ueber RNW
PIO 1 m6502_cycle SM 0 Uebergeordneter Buszyklus-Sequenzer; IRQ 7 = Ausfuehrungsschleifen-Austritt
PIO 1 m6502_fetch SM 1 Opcode-Fetch-Zyklus (SYNC aktiviert, PHI2 hoch, RNW=1)
PIO 1 m6502_read SM 1 Speicher-Lese-Zyklus
PIO 1 m6502_write SM 2 Speicher-Schreib-Zyklus
PIO 1 m6502_irq SM 2 Ueberwacht IRQ-Pin; signalisiert PIO IRQ 5 bei Aktivierung
PIO 1 m6502_nmi SM 3 Ueberwacht NMI-Pin; signalisiert PIO IRQ 4
PIO 1 m6502_so SM 3 Ueberwacht SO (Set Overflow)-Pin; signalisiert PIO IRQ 6
PIO 2 m6502_clock_6502 SM 0 Erzeugt PHI1/PHI2-Zweiphasentakte aus externem PHI0-Eingang

IRQ-Signalkonventionen

PIO IRQ Flag Signal Ausgeloest durch Verarbeitet von
IRQ 0 Zyklusstart m6502_addr — Adresse gueltig bei PHI1 Core 1 Dispatch-Schleife
IRQ 1 Datenphase m6502_data — Datenbus aktiv Core 1 Dispatch-Schleife
IRQ 4 NMI m6502_nmi — NMI-Pin low Core 1 NMI-Handler
IRQ 5 IRQ m6502_irq — IRQ-Pin low Core 1 IRQ-Handler
IRQ 6 SO m6502_so — SO-Pin high Core 1 SO-Handler
IRQ 7 Schleifen-Austritt Software-Anfrage m6502_cycle — verlaesst Ausfuehrungsschleife

PHI1/PHI2-Takterzeugung

Der 6502 benoetigt zwei nicht-ueberlappende Taktphasen. PIO 2 fuehrt das m6502_clock_6502-Programm aus, das einen externen PHI0-Eingang auf einem dedizierten GPIO akzeptiert und PHI1 und PHI2 mit dem korrekten Nicht-Ueberlappungs-Timing erzeugt. Die Widmung eines ganzen PIO-Blocks fuer die Takterzeugung stellt sicher, dass das 6502-Bus-Timing niemals durch Core 1-Aktivitaet oder FIFO-Stalls beeinflusst wird.

Wartezustaende und RDY

Das RDY-Signal des 6502 kann zur Einfuegung von Wartezustaenden verwendet werden — das Halten von RDY auf Low waehrend eines Lesezyklus pausiert den Prozessor, bis RDY wieder High wird. Der pico6502 kann RDY programmatisch von Core 1 aus aktivieren, um Wartezustaende beim Zugriff auf langsamere virtuelle Geraete (z.B. Diskettenemulation) einzufuegen. Der tcycwait-Parameter im Speicherbelegungs-JSON-Eintrag gibt die Anzahl der einzufuegenden Wartezustaende fuer einen bestimmten Speicherbereich an.

PIO-Architektur — Wie 6502-Buszyklen nachgebildet werden

Das PIO-Subsystem des RP2350 bildet zyklusgenaues 6502-Bus-Timing mit 10 Zustandsmaschinen ueber alle drei PIO-Bloecke nach. Die Architektur unterscheidet sich in zwei grundlegenden Punkten vom picoZ80-PIO: Der 6502 verwendet ein Zweiphasen-nicht-ueberlappendes Taktschema (PHI1/PHI2) anstelle eines Einzeltakts und ein einzelnes R/W̄ (Read/Not-Write)-Signal anstelle separater /RD- und /WR-Signale.

Zweiphasen-Takterzeugung
Der 6502 benoetigt zwei nicht-ueberlappende Takte — PHI1 und PHI2. Ein externes PHI0-Signal liefert die Referenz; PIO 2 erzeugt daraus PHI1 und PHI2. Die m6502_clock_6502-Zustandsmaschine (PIO 2 SM 0) implementiert dies:
PHI0 (input):  ──┐        ┌──┐        ┌──
                 │        │  │        │
                 └────────┘  └────────┘

PHI1 (output):    ┌──────┐              ┌──────┐
                  │      │              │      │
               ───┘      └──────────────┘      └───

PHI2 (output): ───┐              ┌──────┐              ┌──
                  │              │      │              │
                  └──────────────┘      └──────────────┘
                  ↑              ↑      ↑
                  │              │      │
            PHI0 faellt    PHI0 steigt  PHI0 faellt
            → kurze Luecke → kurze Luecke → kurze Luecke
            → PHI1 steigt  → PHI1 faellt
                           → PHI2 steigt
Die SM verwendet jmp pin zur Erkennung von PHI0-Flanken und set pins zum Treiben von PHI1 und PHI2 mit den korrekten Nicht-Ueberlappungs-Luecken. Die [1]-Verzoegerungsannotation auf der ersten set pins-Instruktion erzeugt die Nicht-Ueberlappungsperiode — beide Takte sind fuer ein kurzes Intervall waehrend der Uebergaenge auf Low. Die Widmung eines ganzen PIO-Blocks fuer die Takterzeugung stellt sicher, dass das 6502-Bus-Timing niemals durch FIFO-Stalls oder Core 1-Latenz beeinflusst wird.

Der out exec, 32-Mechanismus
Wie der picoZ80 verwendet der pico6502 dynamische Instruktionsinjektion ueber out exec — aber mit einem wesentlichen Unterschied: Die 6502-Zyklus-SM verwendet out exec, 32 (32-Bit-Instruktionen) anstelle der Z80-Version out exec, 16. Dies bietet die vollstaendige 32-Bit-PIO-Instruktionskodierung einschliesslich Side-Set-Bits, Verzoegerungswerten und erweiterten Operanden.
Die m6502_cycle-SM (PIO 1 SM 0) orchestriert alle Buszyklen:
// m6502_cycle SM program (PIO 1 SM 0):
//
// start_cycle:
//     wait 0 gpio CLK2      ; Sync to PHI2 falling edge (start of new cycle)
//     irq set 0             ; Signal "ready for new cycle"
//     wait 0 irq 0          ; Wait for C code to load address and clear IRQ 0
// cycle_exec:
//     out exec, 32           ; Pull 32-bit instruction from FIFO, execute it
//     jmp cycle_exec         ; Repeat
// cycle_end:
//     irq set 7              ; Flag end of execution loop
// cycle_exec2:
//     out exec, 32           ; Continue executing injected instructions
//     jmp cycle_exec2        ; Loop until JMP start_cycle
Der C-Code berechnet 32-Bit-Instruktionssequenzen fuer jeden Zyklustyp (Fetch, Read, Write) vor und schiebt sie in den FIFO. Die Zyklus-SM zieht und fuehrt jede Instruktion der Reihe nach aus und steuert die SYNC- und R/W̄-Signale, waehrend sie sich ueber IRQ-Flags mit den Adress- und Daten-SMs koordiniert.

6502-Buszyklustypen
Der 6502 hat drei Buszyklustypen, die alle durch den Zustand von R/W̄ und SYNC gesteuert werden:
Opcode Fetch (m6502_fetch):
PHI1:      ┌──────┐              ┌──
           │      │              │
        ───┘      └──────────────┘
PHI2:   ───┐              ┌──────┐
           │              │      │
           └──────────────┘      └───
A0–A15: ══╤═══ PC address ══════════╗   (gueltig bei steigender PHI1-Flanke)
R/W̄:    ──┤── high (read) ─────────╢
SYNC:   ──┤── high (fetch) ────────╢   (SYNC high = Opcode-Fetch)
D0–D7:  ══════════════════╤════════╗   (abgetastet bei fallender PHI2-Flanke)
                       opcode


Memory Read (m6502_read):
           Gleiches Timing wie Fetch, aber SYNC = low.


Memory Write (m6502_write):
PHI1:      ┌──────┐              ┌──
           │      │              │
        ───┘      └──────────────┘
PHI2:   ───┐              ┌──────┐
           │              │      │
           └──────────────┘      └───
A0–A15: ══╤═══ address ═════════════╗   (gueltig bei steigender PHI1-Flanke)
R/W̄:    ──┤── low (write) ─────────╢   (R/W̄ low = Schreiben)
SYNC:   ──┤── low ─────────────────╢
D0–D7:  ══════════════╤═══ data ═══╗   (getrieben waehrend PHI2 high)
Wesentliche Unterschiede zur Z80-PIO-Implementierung:
  • Einzelnes R/W̄-Signal — ersetzt die separaten /RD- und /WR-Signale des Z80. Die m6502_fetch- und m6502_read-SMs setzen R/W̄ high; m6502_write setzt es low.
  • SYNC-Signal — der 6502 aktiviert SYNC waehrend Opcode-Fetches, um diese von gewoehnlichen Lesevorgaengen zu unterscheiden. Die m6502_fetch-SM setzt SYNC high; m6502_read und m6502_write lassen es low.
  • PHI2-basierte Datenabtastung — der Datenbus wird bei der fallenden PHI2-Flanke (Ende des Zyklus) abgetastet, nicht bei einer bestimmten T-State-Taktflanke wie beim Z80. Die m6502_data-SM wartet auf wait 0 gpio CLK2, um die Datenerfassung zeitlich abzustimmen.
  • RDY-basierte Wartezustaende — anstelle des /WAIT-Signals des Z80 verwendet der 6502 RDY. Wenn RDY waehrend eines Lesezyklus low ist, pausiert der Prozessor. Die Fetch- und Read-SMs pruefen RDY ueber jmp pin nach jeder steigenden PHI2-Flanke.
  • Kein Refresh-Zyklus — der 6502 hat keinen DRAM-Refresh-Zyklus, sodass Fetch-Zyklen einfacher sind (keine T3-T4-Refresh-Phase).
  • Keine I/O-spezifischen Zyklen — der 6502 hat keine IN/OUT-Instruktionen. Alle Peripheriezugriffe erfolgen ueber speicherabgebildete Lese- und Schreibvorgaenge.

Signalueberwachungs-Zustandsmaschinen
Drei dedizierte SMs ueberwachen die asynchronen Eingangssignale des 6502:
  • m6502_irq (PIO 1 SM 2) — ueberwacht den /IRQ-Pin. Wenn /IRQ low wird, setzt sie PIO IRQ 5 und haelt es, bis der Pin wieder high wird, dann loescht sie das Flag. Core 1 prueft IRQ 5, um zu entscheiden, ob eine Interrupt-Vektor-Fetch-Sequenz injiziert werden soll.
  • m6502_nmi (PIO 1 SM 3) — ueberwacht /NMI und setzt/loescht PIO IRQ 4. Der NMI-Handler in Core 1 schiebt die entsprechende Buszyklus-Sequenz zum Vektorieren durch $FFFA/$FFFB.
  • m6502_so (PIO 1 SM 3) — ueberwacht den SO (Set Overflow)-Pin und setzt/loescht PIO IRQ 6. Dies wird von einigen 6502-Systemen (insbesondere dem Commodore-Diskettenlaufwerk) verwendet, um Data-Ready-Ereignisse zu signalisieren.
Alle drei SMs verwenden das gleiche Muster: jmp pin zur Erkennung des aktiv werdenden Pins, irq set zur Signalisierung an Core 1, dann jmp pin in einer Schleife zur Erkennung des inaktiv werdenden Pins, gefolgt von irq clear. Dies wandelt asynchrone Flankenereignisse in synchrone IRQ-Flags um, die Core 1 effizient in der Hauptschleife abfragen kann.

Speichermodell

Das pico6502-Speichermodell ist strukturell identisch mit dem picoZ80-Modell mit einem entscheidenden Unterschied: Der 6502 hat keinen separaten I/O-Adressraum. Es gibt kein ioPtr[]-Array in t_6502PSRAM und keinen iomap-JSON-Abschnitt. Alle Peripherieregister werden in den 64KB-Speicheradressraum mittels FUNC-Typ-Bloecken abgebildet.

Stufe 1 — RP2350 SRAM (Schnelle Dispatch-Tabelle)

128 x 32-Bit-Eintraege in _membankPtr[], einer pro 512-Byte-Block des 64KB-Adressraums. Jeder Eintrag kodiert den Blocktyp, die PSRAM-Banknummer und die Block-Basisadresse in einem einzigen 32-Bit-Wort fuer O(1)-Dispatch bei jedem Buszyklus:
// 32-bit membankPtr encoding
// Bits 31–24: MEMBANK_TYPE_xxx constant
// Bits 23–16: PSRAM bank number (0–63)
// Bits 15–0:  Block base address >> 9 (i.e. upper 7 bits of 16-bit address)
#define MEMBANK_ENCODE(type, bank, base) (((type) << 24) | ((bank) << 16) | ((base) >> 9))

Stufe 2 — PSRAM (8MB)

Die 8MB PSRAM werden zwischen dem RAM/ROM-Bereich und den Funktionszeiger-Tabellen aufgeteilt:
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;

Stufe 3 — Flash (16MB)

Der 16MB-Flash speichert den Bootloader, zwei 5MB-Anwendungsfirmware-Slots, zwei Konfigurationsslots fuer ROM-Images und minimierte JSON, allgemeine Konfiguration und die Partitionstabelle. Siehe die Flash-Speicherlayout-Tabelle im Abschnitt Firmwarearchitektur oben.

Speicherblocktypen

Konstante Wert Verhalten
MEMBANK_TYPE_PHYSICAL 0 Durchleitung zur realen Host-Hardware — RP2350 gibt Bus frei
MEMBANK_TYPE_PHYSICAL_VRAM 1 Host-Video-RAM mit konfigurierbaren Wartezustaenden
MEMBANK_TYPE_RAM 2 Lesen/Schreiben — durch PSRAM-Bank unterstuetzt
MEMBANK_TYPE_ROM 3 Nur-Lesen — durch PSRAM-Bank unterstuetzt; Schreibzugriffe werden stillschweigend verworfen
MEMBANK_TYPE_VRAM 4 PSRAM-Video-RAM-Spiegel mit Wartezustaenden
MEMBANK_TYPE_FUNC 5 Virtuelles Geraet — memioPtr[addr]-Handler wird bei jedem Zugriff aufgerufen
MEMBANK_TYPE_PTR 6 Pro-Byte-Umleitung — memPtr[addr]-Handler fuer Lesezugriffe

Konfigurationsreferenz

Der pico6502 verwendet den gleichen JSON-Konfigurationsmechanismus wie der picoZ80. Die Top-Level-Schluessel sind "esp32" und "rp2350". Der CPU-spezifische Abschnitt verwendet den Schluessel "6502" (statt "z80"). Da der 6502 keinen I/O-Adressraum hat, gibt es kein "io"-Array — nur ein "memory"-Array.

esp32.core

Schluessel Typ Beschreibung
device string CPU-Geraetetyp. Muss "6502" fuer den pico6502 sein.
mode integer Standard-Bootmodus: 0 = Client (Station), 1 = Access Point.

esp32.wifi

Schluessel Typ Beschreibung
override 0/1 1 = alle WiFi-Einstellungen aus diesem Block anwenden; 0 = NVS-Einstellungen verwenden.
wifimode string "ap" oder "client".
ssid string Netzwerkname zum Erstellen (AP) oder Beitreten (Client).
password string WiFi-Passphrase.
ip string Feste IP-Adresse.
netmask string Subnetzmaske.
gateway string Standard-Gateway.
dhcp 0/1 Nur Client-Modus. 1 = DHCP, 0 = feste IP.
webfs string Web-Dateisystem-Stammverzeichnis auf SD-Karte (Standard "webfs").
persist 0/1 1 = aufgeloeste Einstellungen bei jedem Start in NVS schreiben.

rp2350.core

Schluessel Typ Beschreibung
cpufreq integer RP2350-Systemtakt in Hz (z.B. 300000000).
psramfreq integer PSRAM-SPI-Takt in Hz (z.B. 133000000).
voltage float RP2350-Kernspannung (z.B. 1.10).

rp2350.6502.memory[ ]

Definiert die 6502-Speicherbelegung. Alle Peripheriegeraete muessen hier als FUNC-Typ-Eintraege erscheinen — es gibt keine separate I/O-Zuordnung.
Schluessel Typ Beschreibung
enable 0/1 Ob dieser Eintrag aktiv ist.
addr hex string Startadresse im 6502-Adressraum (z.B. "0xE000").
size hex string Bereichsgroesse in Bytes (z.B. "0x2000").
type string PHYSICAL, PHYSICAL_VRAM, RAM, ROM, FUNC oder PTR.
bank integer PSRAM-Banknummer fuer RAM/ROM-Typen (0-63).
tcycwait integer Einzufuegende Wartezustaende (RDY-Stretching) fuer diesen Bereich.
task string Benannter Task-/Treiber-Handler fuer FUNC-Typ-Bloecke.
file string SD-Karten-Pfad zu einem ROM-Image zum Laden beim Booten (ROM-Typ).

rp2350.6502.drivers[ ]

Treibereintraege fuer den 6502 sind einfacher als die fuer den Z80. Jeder Eintrag aktiviert einen benannten Treiber (abgeglichen gegen die virtualFuncMap[]-Tabelle der Firmware) und laedt optional ROM-Images beim Booten in das PSRAM.
Schluessel Typ Beschreibung
enable 0/1 Ob dieser Treibereintrag aktiv ist.
name string Treibername — wird gross-/kleinschreibungsunabhaengig gegen virtualFuncMap[] abgeglichen.
rom[] array ROM-Images zum Laden beim Booten. Jeder Eintrag: enable, file, loadaddr[].

rom[]-Eintrag:

Schluessel Typ Beschreibung
enable 0/1 Ob dieses ROM-Image geladen wird.
file string SD-Karten-Pfad zur ROM-Binaerdatei.
loadaddr[] array Ein oder mehrere Ladeadressen-Deskriptoren: enable, position, addr, bank, size.

Framework fuer virtuelle Geraete

Das Framework fuer virtuelle Geraete ist strukturell identisch mit dem picoZ80-Framework, beschrieben im picoZ80 Technischen Leitfaden. Der wesentliche Unterschied ist, dass alle virtuellen Geraete in memioPtr[] registriert werden — es gibt kein ioPtr[]-Aequivalent fuer den 6502.

Handler-Signaturen

// 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);

Einen Peripherietreiber schreiben

Um ein virtuelles Peripheriegeraet fuer den pico6502 hinzuzufuegen:
  1. Erstellen Sie eine Handler-Funktion mit der MemoryFunc-Signatur. Der Handler empfaengt read=true fuer 6502-Lesezyklen (RNW=1) und read=false fuer Schreibzyklen (RNW=0).
  2. Erstellen Sie eine Init-Funktion mit der VirtualFunc-Signatur. Im Konfigurationsdurchlauf (pass=1) installieren Sie Ihren Handler in cpu->_6502PSRAM->memioPtr[addr] fuer jede Adresse, die Ihr Peripheriegeraet belegt.
  3. Registrieren Sie den Treibernamen und die Init-Funktion in der virtualFuncMap[]-Tabelle in M6502CPU.c.
  4. Fuegen Sie einen FUNC-Typ-Eintrag im memory-Array der config.json fuer den Adressbereich hinzu.
  5. Fuegen Sie einen Treibereintrag in config.json mit dem passenden "name"-Feld hinzu.
Siehe den pico6502 Entwicklerhandbuch fuer ein vollstaendiges Beispiel einschliesslich einer VIA6522-Peripherie-Implementierung.

ESP32-Koprozessor

Der ESP32-Koprozessor ist in Funktion und Implementierung identisch mit dem picoZ80-ESP32 — siehe den picoZ80 Technischen Leitfaden — ESP32-Koprozessor fuer die vollstaendige Beschreibung des SD-Karten-Interfaces, der Webserverstruktur und des RP2350-ESP32-Befehlsprotokolls.

SD-Karte

Die SD-Karte wird ausschliesslich vom ESP32 ueber SPI verwaltet. Der RP2350 fordert Datei-Lese- und Schreibvorgaenge an, indem er Nachrichten an die Inter-Core-Queue sendet; Core 0 leitet diese Anfragen ueber UART an den ESP32 weiter und gibt die Ergebnisse zurueck. Das SD-Karten-Verzeichnislayout fuer den pico6502 ist identisch mit dem picoZ80: webfs/ fuer Web-Dateien, ROM/ fuer ROM-Images und config.json im Stammverzeichnis.

Webserver

Der gleiche siebensseitige Bootstrap-4-Webserver laeuft auf dem ESP32. Die Weboberflaeche erkennt automatisch den verbundenen Geraetetyp ueber das esp32.core.device-Feld in config.json und passt ihr CSS-Design und ihre Beschriftungen entsprechend an (das p6502.css-Stylesheet wendet das 6502-spezifische Farbschema an).

Befehlsprotokoll

Das RP2350-ESP32-Befehlsprotokoll ist fuer picoZ80 und pico6502 identisch. Befehle werden ueber eine 460,8kBaud-UART-Verbindung ausgetauscht (unterstuetzt durch 50MHz FSPI fuer Massendatentransfer). Das Protokoll ist in ESP.c / ESP.h definiert und wird von beiden Firmware-Varianten geteilt.

SWD-Debugging — RP2350

Das SWD-Debugging-Setup fuer den pico6502 ist identisch mit dem picoZ80 — siehe den picoZ80 Technischen Leitfaden — SWD-Debugging fuer die vollstaendige Beschreibung der Hardwareverbindungen, des benutzerdefinierten rp2350_tzpu.cfg OpenOCD-Targets und der globalen GDB-Initialisierung. Die Unterschiede zum picoZ80 sind:
  • Die Hauptfirmware-ELF ist build/bin/model/BaseM6502/BaseM6502_0x10020000.elf (nicht BaseZ80).
  • Der add-auto-load-safe-path-Eintrag in ~/.gdbinit muss BaseM6502 referenzieren.

OpenOCD-Setup

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"

GDB-Konfiguration

set history save on
set history filename ~/.gdb_history
set history size 65536
add-auto-load-safe-path build/bin/model/BaseM6502/.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 (separates Terminal)
cd build/bin/model/Bootloader
cp ../../../../.gdbinit.bootloader.3334 .gdbinit
gdb-multiarch Bootloader.elf
# Hauptfirmware — Core 0
cd build/bin/model/BaseM6502
cp ../../../../.gdbinit.3333 .gdbinit
gdb-multiarch BaseM6502_0x10020000.elf

# Hauptfirmware — Core 1 (separates Terminal)
cd build/bin/model/BaseM6502
cp ../../../../.gdbinit.3334 .gdbinit
gdb-multiarch BaseM6502_0x10020000.elf

ESP32 — USB-Debugging

Identisch mit dem picoZ80 — verbinden Sie USB mit dem ESP32-USB-Port und verwenden Sie das integrierte USB-JTAG-Interface des ESP32-S3 (kein externer Probe erforderlich).
openocd -f board/esp32s3-builtin.cfg

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

Build-System

Das Build-System ist identisch mit dem picoZ80 — CMake mit Pico SDK 2.x fuer den RP2350. Der pico6502 benoetigt nicht die Zeta Z80-Emulator-Bibliothek. Siehe den picoZ80 Technischen Leitfaden — Build-System fuer die vollstaendige Beschreibung der Voraussetzungen, des Docker-Setups fuer ESP-IDF und des Build-Skripts. Die pico6502-spezifischen Unterschiede sind unten aufgefuehrt.

Build-Ziele

Ziel Ausgabe Beschreibung
BaseM6502 BaseM6502_0x10020000.uf2 6502-Hauptfirmware — Slot-1-Ladeadresse
BaseM6502 BaseM6502_0x10520000.uf2 6502-Hauptfirmware — Slot-2-Ladeadresse
Bootloader Bootloader.uf2 Gemeinsamer Bootloader (gleich wie picoZ80)
ESP32 main.bin ESP32-Firmware (geteilt mit picoZ80)

Build-Befehle

# Setup (einmalig) — vom Projektstammverzeichnis
export PICO_PATH=/path/to/pico-sdk
./get_and_build_sdk.sh

# Standard Release Build
./build_tzpuPico.sh

# Debug Build
./build_tzpuPico.sh DEBUG

# Vollstaendiger Build einschliesslich ESP32-Firmware
./build_tzpuPico.sh ALL

# Nur ESP32 (mit Docker idf54-Alias)
cd projects/tzpuPico/esp32
idf54 build

# RP2350 ueber USB-Massenspeicher flashen (BOOTSEL-Modus)
cp build/bin/model/Bootloader/Bootloader.uf2 /media/$USER/RPI-RP2/
cp build/bin/model/BaseM6502/BaseM6502_0x10020000.uf2 /media/$USER/RPI-RP2/

# ESP32 flashen (initial, ueber 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

Referenz-Websites

Ressource Link
pico6502 Projektseite /pico6502/
pico6502 Benutzerhandbuch /pico6502-usermanual/
pico6502 Entwicklerhandbuch /pico6502-developersguide/
picoZ80 Technischer Leitfaden /picoz80-technicalguide/
picoZ80 Projektseite /picoz80/
RP2350 Datasheet datasheets.raspberrypi.com
Pico SDK Multicore API raspberrypi.github.io/pico-sdk-doxygen
MOS 6502 Datasheet archive.org
esptool Dokumentation docs.espressif.com
cJSON Library github.com/DaveGamble/cJSON

Regulatorischer Hinweis fuer Funkgeraete

Dieses Geraet enthaelt ein ESP32-S3-PICO-1-Funkmodul, das im 2,4 GHz ISM-Band sendet und es damit zu einem beabsichtigten Strahler gemaess den Funkfrequenzvorschriften weltweit macht (einschliesslich FCC Part 15 Subpart C in den Vereinigten Staaten und der Funkanlagenrichtlinie 2014/53/EU in der Europaeischen Union).
Obwohl das ESP32-S3-PICO-1-Modul selbst ueber bestehende regulatorische Zertifizierungen verfuegt (FCC, CE und andere), erstrecken sich diese Modulzertifizierungen nicht automatisch auf ein fertiges Produkt, das das Modul enthaelt. Die Vorzertifizierungs-Ausnahme erlaubt es einzelnen Hobbyisten, eine begrenzte Anzahl von Geraeten fuer den persoenlichen, experimentellen oder bildungsbezogenen Gebrauch zu bauen, ohne eine separate Geraetezulassung zu erhalten.
Wichtige Einschraenkungen
  • Zusammengebaute Geraete duerfen nicht verkauft, zum Verkauf angeboten, verschenkt oder anderweitig an Dritte verteilt werden, es sei denn, das fertige Produkt wurde unabhaengig getestet und hat eine eigene Geraetezulassung erhalten.
  • Der Bau dieses Projekts fuer den persoenlichen Gebrauch in begrenzten Stueckzahlen ist im Allgemeinen unter Hobbyisten- und Experimentiervorschriften gestattet (z.B. FCC § 15.23), vorausgesetzt das Geraet verursacht keine schaedlichen Stoerungen.
  • Regulatorische Anforderungen variieren je nach Land. Bauherren ausserhalb der Vereinigten Staaten sollten ihre nationale Funkfrequenzbehoerde konsultieren.
Verantwortung des Bauherrn
Es liegt in der alleinigen Verantwortung des Bauherrn sicherzustellen, dass jedes aus diesen Designs konstruierte Geraet allen anwendbaren Funkfrequenzvorschriften in seiner Gerichtsbarkeit entspricht. Der Autor stellt diese Designs fuer den persoenlichen, bildungsbezogenen und Hobbygebrauch zur Verfuegung und gibt keine Zusicherung, dass ein aus ihnen gebautes Geraet die regulatorischen Anforderungen fuer den kommerziellen Vertrieb erfuellt.