tranZPUter FusionX Guide du Developpeur

Presentation

La pile logicielle du tranZPUter FusionX est organisee en trois couches distinctes qui forment ensemble un systeme complet d'emulation Z80 et de materiel virtuel. Chaque couche a une responsabilite clairement definie, et l'ajout du support pour une nouvelle machine hote — ou l'extension d'une existante — necessite des modifications coordonnees sur les trois couches.
  1. Modules noyau Linux (z80drv.ko, ttymzdrv.ko) — ecrits en C, construits contre l'arbre noyau Linux 4.9-rt SigmaStar. z80drv execute la boucle de distribution de l'emulation Z80 sur un coeur CPU isole et communique avec le CPLD via SPI et GPIO. ttymzdrv fournit une interface TTY pour le clavier et l'ecran de caracteres du Sharp MZ.
  2. Utilitaires espace utilisateur (z80ctrl, k64fcpu, sharpbiter) — ecrits en C, compiles de maniere croisee avec arm-linux-gnueabihf-gcc. Ces utilitaires demarrent, arretent et configurent le module noyau a l'execution ; k64fcpu agit comme un daemon emulant l'interface du processeur virtuel K64F ; sharpbiter arbitre l'acces au clavier et a l'ecran entre le logiciel de la machine hote et la console Linux.
  3. RTL CPLD (VHDL) — construit avec Altera Quartus II 13.0.1 SP1 Web Edition. Le CPLD est directement sur le bus Z80 de la machine hote et agit comme interface materielle entre les signaux physiques du bus Z80 et le SOM SigmaStar (System-on-Module). Il capture chaque cycle de bus, signale le SOM et pilote le bus de donnees Z80 pour le compte du SOM lors des cycles de lecture.
L'ajout du support pour une nouvelle machine necessite des modifications aux trois couches : une nouvelle definition de carte memoire et un module materiel virtuel dans le pilote noyau, une nouvelle variante VHDL CPLD compilee pour le brochage de bus et le timing de la machine cible, et un script de demarrage qui les relie.

Arbre source

Tout le code source est organise sous la racine du depot FusionX/. La disposition des repertoires ci-dessous montre les fichiers cles avec lesquels un developpeur devra travailler. Les repertoires de variantes de construction (src.mz80a/, src.mz700/, etc.) contiennent un Makefile et des liens symboliques vers les fichiers source communs dans src/ ; seuls les fichiers specifiques a la machine different entre les variantes.
FusionX/
├── CPLD/
│   └── v1.0/
│       ├── MZ80A/build/          Quartus II project for Sharp MZ-80A
│       ├── MZ700/build/          Quartus II project for Sharp MZ-700
│       ├── MZ2000/build/         Quartus II project for Sharp MZ-2000
│       ├── PCW8256/build/        Quartus II project for Amstrad PCW-8256
│       ├── tzpuFusionX.vhd           Main RTL (FSMs, SPI, bus interface, video/audio)
│       ├── tzpuFusionX_Toplevel.vhd  Top-level entity and I/O pin assignments
│       └── tzpuFusionX_pkg.vhd       Shared package (types, constants, generics)
└── software/
    ├── linux/
    │   └── Build_FusionX.sh      Master build script (U-boot + kernel + rootfs + apps)
    └── FusionX/
        └── src/
            ├── z80drv/
            │   ├── src.mz80a/    Makefile and symlinks for MZ-80A build variant
            │   ├── src.mz700/    MZ-700 build variant
            │   ├── src.mz2000/   MZ-2000 build variant
            │   ├── src.pcw/      PCW-8256 build variant
            │   └── src/          Common source files:
            │       ├── z80driver.c      Main kernel module: dispatch loop, memory/IO routing
            │       ├── z80driver.h      Data structures, memory map constants per machine
            │       ├── z80io.c          HAL: SPI write path, GPIO read path, CPLD communication
            │       ├── emumz.c          Zeta Z80 instruction execution wrapper
            │       ├── z80vhw_mz80a.c   MZ-80A virtual hardware
            │       ├── z80vhw_mz700.c   MZ-700 virtual hardware
            │       ├── z80vhw_mz2000.c  MZ-2000 virtual hardware
            │       ├── z80vhw_pcw.c     PCW-8256 virtual hardware
            │       ├── z80vhw_rfs.c     ROM Filing System virtual device
            │       └── z80vhw_tzpu.c    tranZPUter SW virtual hardware (K64F stub)
            ├── ttymz/
            │   ├── Makefile
            │   └── ttymzdrv.c           MZ keyboard/display TTY driver
            └── utils/
                ├── z80ctrl.c            z80drv control utility
                ├── k64fcpu.c            K64F virtual CPU daemon
                └── sharpbiter.c         Keyboard/display arbiter daemon

Configuration de l'environnement de developpement

L'environnement de construction du FusionX necessite trois chaines d'outils separees : un compilateur croise ARM pour les utilitaires espace utilisateur et les modules noyau, le SDK SigmaStar pour l'acces aux en-tetes du noyau, et Altera Quartus II pour la synthese CPLD. Ceux-ci peuvent tous etre installes sur un hote Linux x86-64 standard (Debian 12 / Ubuntu 22.04 LTS ou ulterieur est recommande). Un environnement d'execution Java est egalement requis pour l'assembleur Z80 GLASS.

Configuration et construction automatisees (recommande)

Tout ce que les etapes manuelles ci-dessous installent a la main — le compilateur croise ARM, l'environnement d'execution Java pour l'assembleur GLASS, les dependances de construction du noyau et la chaine d'outils Quartus II — est installe et pilote automatiquement par le script d'installation fourni pour votre plateforme. Chaque script est autonome : copiez le seul fichier correspondant a votre plateforme et executez-le. Il installe les prerequis, clone le depot avec ses sous-modules (par defaut ~/FusionX), recupere le bundle de contenu SharpSoft (TZFS_Files.zip, ~110 Mo), prepare la chaine d'outils et propose de lancer la premiere construction. Utilisez-le pour un environnement operationnel en une seule etape ; les instructions manuelles detaillees qui suivent restent la reference pour les configurations personnalisees, la comprehension des outils individuels et l'integration continue (CI).
Script Plateforme Remarques
setup_FusionX.sh Linux (natif) / macOS (Docker) Sous Linux propose une chaine d’outils native (Java JRE + la chaine de compilation croisee Linaro gcc-linaro-5.5.0-2017.10-arm-linux-gnueabihf vers /opt/arm-linux-gnueabihf + les dependances de construction du noyau) ou des images Docker reproductibles (fusionx-build:latest, fusionx-quartus:13.0.1) ; sous macOS utilise Docker. Le CPLD se construit toujours via l’image Docker Quartus (MAX7000AE).
setup_FusionX_windows.cmd Windows 10/11 Lanceur a double-clic pour l’installation native PowerShell. Pas de Docker, pas de WSL2.
setup_FusionX_windows_native.ps1 Windows 10/11 (natif, sans WSL2) Installe via winget Git for Windows + Temurin 17 JRE ; localise un Quartus II 13.x existant (MAX7000AE — non installe automatiquement, avertit s’il est absent) ; met en place une distribution WSL1 (par defaut Ubuntu) pour les composants uniquement Linux.
setup_FusionX_wsl1.sh WSL1 / Ubuntu (appele par le script Windows) Provisionne la distribution WSL1 : dependances de construction, extras d’image, bibliotheques 32 bits, python2 pour le SDK SigmaStar et la chaine d’outils ARM Linaro ; ecrit /etc/profile.d/fusionx.sh (CROSS_COMPILE=arm-linux-gnueabihf-, ARCH=arm).
Linux / macOS :
chmod +x setup_FusionX.sh
./setup_FusionX.sh
Windows 10/11 — double-cliquez sur setup_FusionX_windows.cmd, ou depuis PowerShell :
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\setup_FusionX_windows_native.ps1
La construction est pilotee par build.sh, qui selectionne une chaine d'outils native, une image Docker ou une distribution WSL1 par composant selon l'hote (Linux : tout natif ; macOS : tout Docker ; Windows : ROMs/CPLD natifs + WSL1 pour les parties uniquement Linux). Executez-le sans argument pour tout construire, ou passez un indicateur pour un seul composant :
Indicateur build.sh Construit Sortie
--asm --tzfs --cpm ROMs Z80 / TZFS / CP/M (Java + GLASS glass-0.5.1.jar) software/roms/*.bin (dont cpm223_*.bin)
--drivers Modules noyau z80drv / ttymz + applications Objets arm-linux-gnueabihf
--spi Outils SPI mspi_main
--cpld Bit streams du CPLD (MZ80A/MZ700/MZ2000/PCW8256) .pof sous CPLD/v1.0/<machine>/build/output_files/
--image image SD Linux complete SigmaStar SSD202 (u-boot + noyau 4.9 + Buildroot) image sous software/linux/.../images
(aucun) / --all tout
./build.sh                 # tous les composants
./build.sh --cpld          # uniquement les bit streams du CPLD
./build.sh --help          # lister toutes les options
Surcharges d'environnement pratiques (toutes optionnelles) :
Variable Role
FUSIONX_REPO_URL Depot a cloner (par defaut https://git.eaw.app/eaw/tzpuFusionX.git).
FUSIONX_METHOD Force une seule methode de construction pour tous les composants : native, docker ou wsl.
FUSIONX_FILES_URL URL du bundle de contenu SharpSoft (par defaut le TZFS_Files.zip partage).
FUSIONX_TOOLCHAIN_URL / FUSIONX_TOOLCHAIN_DIR URL de telechargement / repertoire d’installation de la chaine d’outils ARM Linaro (par defaut /opt/arm-linux-gnueabihf).
FUSIONX_DIR Construire dans une copie existante au lieu de cloner.
FUSIONX_WSL_DISTRO Nom de la distribution WSL1 (par defaut Ubuntu).
FUSIONX_ASSUME_YES Accepter toutes les invites de maniere non interactive (utilise par le lanceur Windows).
Vous preferez installer la chaine d'outils vous-meme, ou avez besoin d'une configuration personnalisee ou CI ? Les etapes manuelles ci-dessous installent exactement ce que les scripts automatisent.

Prerequis

Installez les outils de construction de base sur votre hote Linux x86-64 :
sudo apt update
sudo apt install -y make bc libssl-dev git default-jre docker.io u-boot-tools
Ajoutez votre utilisateur au groupe docker pour pouvoir executer les conteneurs Docker sans sudo :
sudo usermod -aG docker $USER
# Deconnectez-vous et reconnectez-vous pour que le changement de groupe prenne effet

Compilateur croise ARM (Linaro GCC 5.5)

Les modules noyau et utilitaires espace utilisateur du FusionX doivent etre compiles avec Linaro GCC 5.5, qui correspond a la chaine d'outils utilisee pour construire le noyau Linux 4.9-rt SigmaStar. Le paquet arm-linux-gnueabihf-gcc standard de la distribution est une version plus recente de GCC et ne doit pas etre utilise — les differences d'ABI entre les versions de GCC peuvent causer des defaillances subtiles a l'execution dans les modules noyau.
# Telecharger Linaro GCC 5.5-2017.10 pour ARM hard-float
wget https://releases.linaro.org/components/toolchain/binaries/5.5-2017.10/arm-linux-gnueabihf/gcc-linaro-5.5.0-2017.10-x86_64_arm-linux-gnueabihf.tar.xz

# Extraire vers /opt (necessite root)
sudo mkdir -p /opt/arm-linux-gnueabihf
sudo tar xJf gcc-linaro-5.5.0-2017.10-x86_64_arm-linux-gnueabihf.tar.xz \
    -C /opt/arm-linux-gnueabihf --strip-components=1

# Ajouter au PATH (ajoutez ceci a ~/.bashrc pour la persistance)
export PATH="/opt/arm-linux-gnueabihf/bin:$PATH"

# Verifier
arm-linux-gnueabihf-gcc --version
# Sortie attendue : arm-linux-gnueabihf-gcc (Linaro GCC 5.5-2017.10) 5.5.0
La chaine d'outils occupe environ 613 Mo sur disque. Les binaires cles sont arm-linux-gnueabihf-gcc, arm-linux-gnueabihf-g++ et arm-linux-gnueabihf-ld. Le script build.sh et tous les Makefiles s'attendent a ce que ceux-ci soient disponibles sur le PATH sans prefixe de chemin absolu.

Images Docker Quartus II (constructions CPLD)

Les bitstreams CPLD sont compiles avec Altera Quartus II 13.0.1 SP1 Web Edition, qui est la derniere version a supporter la famille CPLD MAX7000AE utilisee sur la carte FusionX. Plutot que d'installer Quartus II nativement (il necessite les bibliotheques 32 bits d'Ubuntu 16.04), l'approche recommandee est de construire une image Docker qui encapsule l'installation complete de Quartus II. Cette image peut etre utilisee pour des sessions GUI interactives et la compilation CI/CD sans interface.
Construction de l'image Docker Quartus II 13.0.1 :
# Creer un repertoire de construction avec le Dockerfile et les fichiers de support
mkdir -p quartus-docker/files
cd quartus-docker

# Creer le Dockerfile (voir ci-dessous) comme Dockerfile.13.0.1
# Placer les fichiers suivants dans le sous-repertoire files/ :
#   license.dat              - Fichier de licence Quartus (factice pour Web Edition)
#   quartus2.ini             - Configuration Quartus
#   quartus2.qreg            - Parametres de registre Quartus
#   quartus_web_rules_file.txt - Regles Web edition
#   libjtag_hw_arrow.so      - Bibliotheque Arrow USB Blaster (optionnel, pour la programmation)
#   70-usb.rules             - Regles udev USB Blaster (optionnel)

# Construire l'image (telecharge ~4 Go d'installateurs Quartus pendant la construction)
docker build -f Dockerfile.13.0.1 \
    --build-arg user_uid=$(id -u) \
    --build-arg user_gid=$(id -g) \
    --build-arg user_name=$(whoami) \
    -t quartus-ii-13.0.1 .
Le Dockerfile est base sur Ubuntu 16.04 (Xenial) et telecharge l'installateur Quartus II 13.0.1 SP1 Web Edition, le programmateur, les fichiers d'aide et les paquets de support de composants (MAX, Cyclone, Arria) directement depuis les serveurs de telechargement Altera pendant la construction. L'image Docker resultante fait environ 15 a 20 Go.
Dockerfile.13.0.1 :
FROM ubuntu:xenial
ENV DEBIAN_FRONTEND=noninteractive

ARG TARGET_DOWNLOAD_DIR=/tmp/
ARG INSTALLATION_DIR=/opt/altera
ARG ALTERA_DOWNLOAD_SITE=http://download.altera.com/akdlm/software/acdsinst
ARG QUARTUS_VERSION_INSTALLER=/13.0sp1/232/ib_installers/
ARG QUARTUS=QuartusSetupWeb-13.0.1.232.run
ARG QUARTUS_PROGRAMMER=QuartusProgrammerSetup-13.0.1.232.run
ARG QUARTUS_HELP=QuartusHelpSetup-13.0.1.232.run
ARG QUARTUS_DEVICE_FILES="cyclone_web-13.0.1.232.qdz max-13.0.1.232.qdz"

# Installer les bibliotheques 32 bits requises par Quartus II
RUN dpkg --add-architecture i386 && apt-get update && \
    apt-get install --no-install-recommends -y \
        ca-certificates wget make locales \
        libstdc++6:i386 libc6:i386 libx11-dev:i386 libxext-dev:i386 \
        libxau-dev:i386 libxdmcp-dev:i386 libfreetype6:i386 \
        libxtst6:i386 libxi6:i386 fontconfig:i386 expat:i386 \
        lib32ncurses5-dev libfontconfig1 libglib2.0-0 \
        libncurses5:i386 libsm6 libsm6:i386 libssl-dev \
        libxext6:i386 libxft2:i386 libxrender1 libzmq3-dev \
        libxrender-dev:i386 openjdk-8-jdk pkg-config && \
    rm -rf /var/lib/apt/lists/*

RUN echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen && locale-gen

# Telecharger et installer Quartus II
RUN wget -q --directory-prefix=${TARGET_DOWNLOAD_DIR} \
        ${ALTERA_DOWNLOAD_SITE}/${QUARTUS_VERSION_INSTALLER}/${QUARTUS} \
        ${ALTERA_DOWNLOAD_SITE}/${QUARTUS_VERSION_INSTALLER}/${QUARTUS_PROGRAMMER} \
        ${ALTERA_DOWNLOAD_SITE}/${QUARTUS_VERSION_INSTALLER}/${QUARTUS_HELP} && \
    for f in ${QUARTUS_DEVICE_FILES}; do \
        wget -q --directory-prefix=${TARGET_DOWNLOAD_DIR} \
            ${ALTERA_DOWNLOAD_SITE}/${QUARTUS_VERSION_INSTALLER}/${f}; \
    done

RUN chmod +x ${TARGET_DOWNLOAD_DIR}${QUARTUS} \
             ${TARGET_DOWNLOAD_DIR}${QUARTUS_PROGRAMMER} \
             ${TARGET_DOWNLOAD_DIR}${QUARTUS_HELP} && \
    ${TARGET_DOWNLOAD_DIR}${QUARTUS}            --mode unattended --installdir ${INSTALLATION_DIR}/ && \
    ${TARGET_DOWNLOAD_DIR}${QUARTUS_PROGRAMMER} --mode unattended --installdir ${INSTALLATION_DIR}/ && \
    ${TARGET_DOWNLOAD_DIR}${QUARTUS_HELP}       --mode unattended --installdir ${INSTALLATION_DIR}/ && \
    rm -rf ${TARGET_DOWNLOAD_DIR}/* ${INSTALLATION_DIR}/uninstall ${INSTALLATION_DIR}/logs/*

COPY ./files/license.dat ${INSTALLATION_DIR}/
RUN echo "export PATH=\$PATH:${INSTALLATION_DIR}/quartus/bin" >> /root/.bashrc

CMD ${INSTALLATION_DIR}/quartus/bin/quartus --64bit
Compiler un bitstream CPLD depuis la ligne de commande (sans interface) :
# Compiler le bitstream CPLD MZ-80A (depuis la racine du depot FusionX)
docker run --rm --net=host \
    -v "$(pwd):$(pwd)" \
    -w "$(pwd)/CPLD/v1.0/MZ80A/build" \
    quartus-ii-13.0.1 \
    /opt/altera/quartus/bin/quartus_sh --flow compile tzpuFusionX_MZ80A

# Le fichier .pof de sortie sera dans CPLD/v1.0/MZ80A/build/output_files/
ls CPLD/v1.0/MZ80A/build/output_files/tzpuFusionX_MZ80A.pof
Le drapeau --net=host est requis pour les verifications de licence Quartus. Le drapeau -v monte le repertoire hote dans le conteneur au meme chemin, de sorte que les chemins des fichiers du projet Quartus restent valides. La commande quartus_sh --flow compile execute le pipeline complet de synthese, placement, analyse de timing et generation du fichier de programmation sans interface graphique.
Quatre cibles CPLD sont supportees. Compilez chacune avec :
# Compiler les quatre cibles CPLD
for target in MZ80A MZ700 MZ2000 PCW8256; do
    echo "==> Compilation CPLD : ${target}"
    docker run --rm --net=host \
        -v "$(pwd):$(pwd)" \
        -w "$(pwd)/CPLD/v1.0/${target}/build" \
        quartus-ii-13.0.1 \
        /opt/altera/quartus/bin/quartus_sh --flow compile "tzpuFusionX_${target}"
done
Executer l'interface graphique Quartus (interactive, pour modifier le VHDL ou les affectations de broches) :
# Activer le transfert X11 pour le conteneur Docker
xhost +local:docker

docker run --rm --net=host \
    -e DISPLAY=$DISPLAY \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    -v "$(pwd):$(pwd)" \
    -w "$(pwd)/CPLD/v1.0/MZ80A/build" \
    quartus-ii-13.0.1 \
    /opt/altera/quartus/bin/quartus --64bit

Resume de la chaine d'outils

Composant Outil Version Installation
Assembleur Z80 GLASS 0.5.1 Integre dans software/tools/glass-0.5.1.jar (necessite Java)
Compilateur croise ARM Linaro GCC 5.5.0 Installation manuelle dans /opt/arm-linux-gnueabihf/
Modules noyau make + kbuild Linux 4.9-rt Source noyau incluse dans le depot
Outils SPI arm-linux-gnueabihf-gcc 5.5.0 Meme chaine d’outils Linaro que ci-dessus
Bitstreams CPLD Quartus II 13.0.1 SP1 Image Docker (voir ci-dessus)
Image SD Linux Build_FusionX.sh Arbre noyau complet + chaine d’outils ARM
Orchestration de construction build.sh Racine du depot

Clonage et construction

# Cloner le depot (inclut l'arbre noyau complet — environ 1,5 Go)
git clone https://git.eaw.app/eaw/tzpuFusionX.git
cd tzpuFusionX

# Initialiser les sous-modules git (bibliotheques d'emulation CPU Z80 et 6502)
git submodule update --init --recursive

# Tout construire (ROMs Z80, TZFS, CP/M, modules noyau, outils SPI, bitstreams CPLD)
./build.sh --all

# Ou construire des composants individuels :
./build.sh --asm           # ROMs assembleur Z80 et fichiers MZF
./build.sh --tzfs          # ROMs TZFS pour MZ-80A/700/2000
./build.sh --cpm           # Binaires CP/M 2.2
./build.sh --drivers       # Modules noyau (z80drv, ttymzdrv) et applications espace utilisateur
./build.sh --spi           # Outils SPI (mspi_main)
./build.sh --cpld          # Bitstreams CPLD (necessite image Docker Quartus)
./build.sh --image         # Image carte SD Linux (necessite arbre noyau complet)
./build.sh --clean         # Supprimer tous les artefacts de construction
Le script build.sh detecte automatiquement les outils disponibles et saute les etapes de construction lorsque les prerequis ne sont pas remplis. Par exemple, si le compilateur croise ARM n'est pas dans le PATH, les constructions de pilotes et SPI sont sautees avec un message. Si l'image Docker Quartus n'est pas disponible, les constructions CPLD sont sautees. Cela permet des constructions partielles sur des machines qui n'ont qu'un sous-ensemble des chaines d'outils installees.
La construction produit les sorties suivantes :
Sortie Emplacement Contenu
ROMs Monitor software/roms/*.rom ROMs SA-1510, 1Z-013A, Kuma, MZ-2000/800 IPL
Fichiers MZF software/roms/*.mzf MS BASIC, SA-5510, programmes de test
ROMs TZFS software/roms/tzfs_*.rom Firmware TZFS pour chaque machine cible
Binaires CP/M software/roms/cpm223_*.bin CP/M 2.2 avec CBIOS pour chaque cible
Modules noyau software/FusionX/modules/*.ko z80drv.ko, ttymzdrv.ko (derniere cible construite)
Applications espace utilisateur software/FusionX/bin/ z80ctrl, k64fcpu, sharpbiter
Outils SPI software/FusionX/src/spitools/mspi_main Utilitaire de diagnostic SPI
Bitstreams CPLD CPLD/v1.0/*/build/output_files/*.pof Un .pof par machine cible
Image SD Linux software/linux/project/image/output/images/ sdrootfs.tar.gz, SigmastarUpgrade*.bin

Configuration du SDK

Le SDK SigmaStar fournit un environnement de construction complet pour le SOM SSD202, incluant U-boot, le noyau patche PREEMPT_RT et un systeme de fichiers racine base sur Buildroot. L'arbre source complet du noyau est inclus dans le depot sous software/linux/kernel/, avec des artefacts de construction pre-generes (en-tetes de config, fixdep, modpost) pour que les constructions de modules noyau externes fonctionnent sans d'abord compiler le noyau entier. Le script de construction maitre Build_FusionX.sh orchestre le pipeline de construction complet.
# S'assurer que le compilateur croise ARM est dans le PATH
export PATH="/opt/arm-linux-gnueabihf/bin:$PATH"

# Construire une image flash NAND complete pour le SOM SSD202
cd software/linux/
./Build_FusionX.sh -f nand -p ssd202 -o 2D06
Le drapeau -f nand selectionne la sortie flash NAND, -p ssd202 selectionne la plateforme SigmaStar SSD202 et -o 2D06 specifie la revision du composant. Les images de sortie sont placees dans project/image/output/images/ et peuvent etre ecrites sur la NAND via le mecanisme de mise a jour par carte SD decrit dans la section OTA ci-dessous.

Construction du module noyau

La commande build.sh --drivers construit les deux modules noyau (z80drv.ko et ttymzdrv.ko) et les applications espace utilisateur pour les trois machines cibles (MZ-80A, MZ-700, MZ-2000). Chaque repertoire de variante de construction contient un Makefile qui reference les fichiers source communs. Pour construire manuellement pour une cible specifique :
# Construire z80drv pour la cible MZ-80A
cd software/FusionX/src/z80drv
make MZ80A

# Copier le module construit vers une carte FusionX en fonctionnement via SSH
scp modules/z80drv.ko root@192.168.1.100:/apps/FusionX/modules/

# Sur la carte FusionX : decharger l'ancien module et charger le nouveau
rmmod z80drv
insmod /apps/FusionX/modules/z80drv.ko
Apres le chargement du nouveau module, verifiez dmesg pour les erreurs d'initialisation. La construction du module noyau necessite les en-tetes pre-generes dans software/linux/kernel/include/generated/ et software/linux/kernel/arch/arm/include/generated/ — ceux-ci sont commites dans le depot pour que les constructions de modules fonctionnent sans d'abord compiler le noyau complet.

Integration continue (Jenkins)

Qu'est-ce que le CI/CD ? L'Integration Continue / Livraison Continue (CI/CD) est une pratique ou chaque modification de code poussee vers un depot declenche automatiquement une sequence de construction et de test sur un serveur dedie. Au lieu d'executer manuellement les scripts de construction sur votre machine de developpement et de telecharger les fichiers de version a la main, un serveur CI le fait pour vous — clonant le depot, executant chaque etape de construction, empaquetant les resultats et publiant une version telechargeable. Si quelque chose casse, le serveur envoie immediatement une notification par e-mail. Cela detecte les problemes tot (par exemple, un fichier manquant qui existait sur votre machine locale mais n'a jamais ete commite) et garantit que chaque version est construite a partir d'un point de depart propre et reproductible.
Le projet FusionX utilise Jenkins — un serveur d'automatisation open source populaire — fonctionnant sur un VPS (Virtual Private Server). Jenkins lui-meme fonctionne dans un conteneur Docker pour une configuration facile et la portabilite, et il lance un second conteneur Docker pour la compilation CPLD Quartus II. Cette section guide a travers l'installation complete depuis un serveur vierge.

Exigences du serveur
Vous aurez besoin d'un serveur Linux (Debian, Ubuntu ou similaire) avec :
  • Au moins 2 Go de RAM (4 Go recommandes — la compilation Quartus est gourmande en memoire)
  • 20 Go d'espace disque libre (donnees Jenkins, images Docker, artefacts de construction)
  • Docker et Docker Compose installes
  • Acces reseau a votre depot Gitea (ou GitHub)
  • Un nom de domaine ou une adresse IP statique (pour les callbacks de webhook)
# Installer Docker sur Debian/Ubuntu
sudo apt update && sudo apt install -y docker.io docker-compose
sudo systemctl enable docker
sudo systemctl start docker

# Permettre a votre utilisateur d'executer les commandes Docker sans sudo
sudo usermod -aG docker $USER
# Deconnectez-vous et reconnectez-vous pour que le changement de groupe prenne effet
Pour la configuration complete de Jenkins, les scripts de pipeline, la configuration des webhooks Gitea et les details des conteneurs freres, veuillez consulter le Guide du developpeur en anglais qui contient les scripts complets du pipeline Jenkins, les Dockerfiles et les instructions de configuration detaillees.

Structures de donnees cles

Les structures de donnees definies dans z80driver.h forment l'epine dorsale de l'emulation. Les comprendre est essentiel avant de modifier le pilote ou d'ajouter une nouvelle cible machine.

Carte memoire

Chaque machine supportee possede un ensemble de constantes preprocesseur definissant ses regions memoire, suivi d'un tableau de structures t_memRegion que la boucle de distribution utilise a l'execution. Les constantes sont protegees par la definition TARGET_* appropriee, qui est definie par le Makefile de la variante.
// z80driver.h — exemple de carte memoire MZ-80A
#define MZ80A_MONITOR_ROM_ADDR    0x0000
#define MZ80A_MONITOR_ROM_SIZE    0x1000  // 4KB system monitor
#define MZ80A_VRAM_ADDR           0xD000  // Video RAM start
#define MZ80A_VRAM_SIZE           0x0800  // 2KB VRAM
#define MZ80A_IO_KEYBOARD         0xE000  // Keyboard I/O port

typedef struct {
    uint32_t  baseAddr;       // Debut de l'espace d'adressage Z80
    uint32_t  size;           // Taille de la region en octets
    uint8_t  *data;           // Pointeur vers le tampon memoire emule
    uint8_t   type;           // MEM_ROM, MEM_RAM, MEM_VHARDWARE, MEM_PHYSICAL
    void     (*read_fn)(uint16_t addr, uint8_t *data);   // Gestionnaire de lecture materiel virtuel
    void     (*write_fn)(uint16_t addr, uint8_t data);   // Gestionnaire d'ecriture materiel virtuel
} t_memRegion;
Le champ type controle le comportement de la distribution : les regions MEM_ROM retournent les donnees du tampon et ignorent silencieusement les ecritures ; les regions MEM_RAM permettent les lectures et ecritures dans le tampon ; les regions MEM_VHARDWARE appellent les gestionnaires read_fn/write_fn au lieu de toucher le tampon ; les regions MEM_PHYSICAL transmettent le cycle au vrai materiel sur le bus de la machine hote.

Requete de cycle de bus

Lorsque le CPLD signale un cycle de bus en attente via GPIO, la couche HAL lit l'adresse et les lignes de controle et remplit une structure t_busCycle. Cette structure est ensuite transmise a la boucle de distribution pour le routage.
// Informations de cycle de bus capturees depuis les broches GPIO du CPLD
typedef struct {
    uint16_t  address;        // Bus d'adresses Z80 A0-A15
    uint8_t   data;           // Bus de donnees Z80 D0-D7
    uint8_t   busType;        // BUS_MREQ_RD, BUS_MREQ_WR, BUS_IORQ_RD, BUS_IORQ_WR, BUS_M1
    bool      isRead;         // true = cycle de lecture, false = cycle d'ecriture
} t_busCycle;

Interface de module materiel virtuel

Chaque fichier z80vhw_*.c implemente un ensemble fixe de fonctions. La boucle de distribution appelle ces fonctions lorsqu'un cycle de bus cible une region enregistree comme MEM_VHARDWARE. Les fonctions d'interface doivent toutes etre presentes dans chaque module materiel virtuel, meme si certaines sont des operations nulles pour une machine particuliere.
// Chaque z80vhw_*.c implemente ces fonctions :

// Appele une fois au chargement du module — enregistrer les regions memoire/E/S et les gestionnaires
int vhw_init(void);

// Gestionnaire de lecture memoire — appele par la boucle de distribution pour les regions mappees en memoire
uint8_t vhw_mem_read(uint16_t addr);

// Gestionnaire d'ecriture memoire
void vhw_mem_write(uint16_t addr, uint8_t data);

// Gestionnaire de lecture E/S (non utilise pour les machines basees sur 6502, toujours present pour Z80)
uint8_t vhw_io_read(uint16_t port);

// Gestionnaire d'ecriture E/S
void vhw_io_write(uint16_t port, uint8_t data);

// Appele lors du RESET Z80 — reinitialiser l'etat materiel
void vhw_reset(void);

// Appele periodiquement depuis la boucle de distribution — pour les timers, l'emulation sonore, etc.
void vhw_tick(uint64_t cycles);
La fonction vhw_init() est responsable de l'appel a register_mem_region() et register_io_handler() pour indiquer a la boucle de distribution quelles plages d'adresses ce module gere. Les enregistrements effectues dans vhw_init() persistent pour toute la duree de vie du module noyau.

z80driver.c — Boucle de distribution

La boucle de distribution est le chemin critique de l'ensemble du systeme FusionX. Elle s'execute comme un thread noyau affecte au CPU1 (isole via isolcpus=1 dans les arguments de demarrage du noyau), ce qui empeche le planificateur Linux de la preempter pendant les cycles de bus Z80. Chaque cycle machine Z80 que le CPLD intercepte doit etre traite par cette boucle dans le temps de maintien du Z80, qui a 4 MHz est d'environ 250 ns.
A chaque iteration, la boucle effectue les etapes suivantes : elle lit les lignes GPIO du CPLD pour obtenir le cycle de bus en attente (adresse et type de cycle) ; recherche l'adresse dans la table de regions memoire enregistrees ; et distribue vers le gestionnaire approprie — retournant les donnees ROM, accedant au tampon RAM noyau, appelant un gestionnaire de materiel virtuel, ou invoquant la bibliotheque Z80 Zeta pour les cycles de fetch d'opcode. Pour les cycles de lecture, l'octet de reponse est ecrit en retour au CPLD via SPI. La maintenance periodique (appel de vhw_tick()) est effectuee toutes les 2048 iterations.
// Boucle de distribution simplifiee (z80driver.c)
static int z80_emulation_thread(void *data)
{
    while (!kthread_should_stop()) {
        // 1. Attendre que le CPLD signale un cycle de bus en attente (IRQ GPIO ou interrogation)
        t_busCycle cycle = cpld_read_bus_cycle();    // lit les broches GPIO

        // 2. Distribuer en fonction du type de cycle et de l'adresse
        if (cycle.busType == BUS_M1) {
            // Fetch d'opcode — executer une instruction Z80 via la bibliotheque Zeta
            zeta_run_one_instruction(&z80_state);

        } else if (cycle.busType == BUS_MREQ_RD) {
            // Lecture memoire
            uint8_t data = dispatch_mem_read(cycle.address);
            cpld_write_data(data);                   // ecriture SPI vers CPLD

        } else if (cycle.busType == BUS_MREQ_WR) {
            // Ecriture memoire
            dispatch_mem_write(cycle.address, cycle.data);

        } else if (cycle.busType == BUS_IORQ_RD) {
            // Lecture E/S
            uint8_t data = dispatch_io_read(cycle.address & 0xFF);
            cpld_write_data(data);

        } else if (cycle.busType == BUS_IORQ_WR) {
            // Ecriture E/S
            dispatch_io_write(cycle.address & 0xFF, cycle.data);
        }

        // 3. Maintenance periodique
        if ((cycle_count++ & 0x7FF) == 0)
            vhw_tick(cycle_count);
    }
    return 0;
}
Les fonctions dispatch_mem_read() et dispatch_mem_write() effectuent une recherche lineaire dans la table t_memRegion enregistree pour trouver la region couvrant l'adresse demandee. Pour la performance, la table devrait etre ordonnee avec les regions les plus frequemment accedees en premier (typiquement RAM, puis ROM, puis materiel virtuel). Une optimisation future pourrait remplacer la recherche lineaire par une table de recherche a 256 entrees indexee par les 8 bits superieurs de l'adresse.

Ajouter une nouvelle machine

L'ajout du support pour une nouvelle machine hote est la tache de developpeur la plus courante. Le processus implique cinq etapes discretes couvrant les trois couches de la pile logicielle. L'exemple ci-dessous utilise un portage hypothetique du Sinclair ZX Spectrum 48K pour illustrer chaque etape concretement.

Etape 1 — Definir la carte memoire dans z80driver.h

Ajoutez un nouveau bloc #ifdef TARGET_* a z80driver.h definissant les regions memoire, les ports d'E/S et toutes les constantes specifiques a la machine necessaires au module materiel virtuel. Suivez la convention de nommage etablie par les definitions de machines existantes.
#ifdef TARGET_SPECTRUM48K
  #define TARGET_NAME           "Spectrum48K"
  #define SPECTRUM_ROM_ADDR     0x0000
  #define SPECTRUM_ROM_SIZE     0x4000   // 16KB Spectrum ROM
  #define SPECTRUM_RAM_ADDR     0x4000
  #define SPECTRUM_RAM_SIZE     0xC000   // 48KB RAM
  #define SPECTRUM_ULA_PORT     0xFE    // ULA I/O port (keyboard, border, speaker, tape)
#endif

Etape 2 — Creer le module materiel virtuel

Creez un nouveau fichier z80vhw_spectrum48k.c dans software/FusionX/src/z80drv/src/. Ce fichier doit implementer toutes les fonctions de l'interface de materiel virtuel. Le squelette ci-dessous montre une implementation complete de l'ULA Spectrum 48K couvrant le balayage de la matrice clavier, la couleur de bordure, le haut-parleur et la sortie MIC.
Pour le code source complet du squelette, les instructions de creation du repertoire de variante de construction, la creation de la variante VHDL CPLD, le script de demarrage et les guides d'ajout de peripheriques materiels virtuels, veuillez consulter le Guide du developpeur en anglais.

Ajouter un peripherique materiel virtuel

L'ajout d'un nouveau peripherique virtuel a un module de machine existant est une tache incrementale courante — par exemple, ajouter une RTC virtuelle, un port serie virtuel ou une puce son virtuelle a une machine qui n'en avait pas a l'origine. Le processus implique : choisir une plage d'adresses de port E/S non utilisee, definir une disposition de registres, implementer les gestionnaires de lecture/ecriture et enregistrer les gestionnaires dans vhw_init().
L'exemple ci-dessous ajoute une horloge temps reel (RTC) virtuelle au module materiel virtuel MZ-80A aux ports E/S 0xB0-0xB7. La RTC expose huit registres d'un octet de large pour les secondes, minutes, heures, jour, mois, annee et deux registres de reserve.
// Dans z80vhw_mz80a.c — ajouter une RTC virtuelle au port E/S 0xB0-0xB7

typedef struct {
    uint8_t seconds, minutes, hours, day, month, year;
} t_virtualRTC;

static t_virtualRTC rtc = {0};

static uint8_t rtc_read(uint16_t port) {
    switch (port & 0x07) {
        case 0: return rtc.seconds;
        case 1: return rtc.minutes;
        case 2: return rtc.hours;
        case 3: return rtc.day;
        case 4: return rtc.month;
        case 5: return rtc.year;
        default: return 0xFF;
    }
}

static void rtc_write(uint16_t port, uint8_t data) {
    switch (port & 0x07) {
        case 0: rtc.seconds = data; break;
        case 1: rtc.minutes = data; break;
        case 2: rtc.hours   = data; break;
        case 3: rtc.day     = data; break;
        case 4: rtc.month   = data; break;
        case 5: rtc.year    = data; break;
        default: break;
    }
}

// Dans vhw_init() :
register_io_handler(0xB0, 0x08, rtc_read, rtc_write);
Le deuxieme argument de register_io_handler() est la taille de la plage de ports (8 ports, 0xB0-0xB7). La boucle de distribution appellera rtc_read() ou rtc_write() pour tout cycle E/S dont l'adresse de port tombe dans cette plage.

Modifier le CPLD

Le RTL CPLD dans tzpuFusionX.vhd implemente plusieurs machines a etats finis et protocoles de communication que les developpeurs peuvent avoir besoin d'etendre ou de modifier. Les raisons les plus courantes de modifier le CPLD sont : ajouter de nouveaux codes de commande SPI pour de nouvelles fonctionnalites communiquees entre le SOM et le CPLD, ajuster la synchronisation du bus pour une nouvelle machine hote, et ajouter l'insertion d'etats d'attente pour les machines avec memoire contestee (comme l'ULA du Spectrum).

Ajouter une nouvelle commande SPI

Les codes de commande SPI sont definis comme des constantes dans tzpuFusionX_pkg.vhd et decodes dans le processus recepteur SPI de tzpuFusionX.vhd. Pour ajouter une nouvelle commande, definissez d'abord la constante dans le fichier package, puis ajoutez une clause when a l'instruction case du decodeur SPI.
-- Dans tzpuFusionX_pkg.vhd — ajouter une nouvelle constante de commande
constant CMD_SET_BORDER : std_logic_vector(7 downto 0) := x"42";

-- Dans le processus decodeur SPI de tzpuFusionX.vhd
when CMD_SET_BORDER =>
    border_colour <= spi_data_in(2 downto 0);

Compilation croisee des utilitaires utilisateur

Les utilitaires espace utilisateur (z80ctrl, k64fcpu, sharpbiter) sont des programmes C simples qui peuvent etre compiles de maniere croisee avec une seule invocation gcc pour un test rapide. Pour les constructions de production, l'approche preferee est d'ajouter l'utilitaire comme paquet Buildroot pour qu'il soit automatiquement reconstruit et inclus lorsque l'image complete du systeme de fichiers racine est generee.
# Compiler z80ctrl de maniere croisee pour un test rapide
arm-linux-gnueabihf-gcc -o z80ctrl z80ctrl.c -lpthread

# Copier vers une carte FusionX en fonctionnement via SSH
scp z80ctrl root@192.168.1.100:/apps/FusionX/bin/

Mise a jour firmware OTA

Le FusionX supporte un mecanisme de mise a jour firmware over-the-air (OTA) base sur la carte SD. Le processus de mise a jour est gere par le bootloader U-boot (ou un script init Linux precoce), qui detecte une image de mise a jour valide sur la carte SD et la flashe sur la NAND avant de passer la main au systeme d'exploitation principal.

Construction de l'image de mise a jour

cd software/linux/
./Build_FusionX.sh -f nand -p ssd202 -o 2D06 -m 256

Preparation de la carte SD

# Preparer la carte SD (en supposant que la carte SD est montee sur /media/sdcard)
mkdir -p /media/sdcard/FusionX/upgrade/
cp project/image/output/images/uboot.img       /media/sdcard/FusionX/upgrade/
cp project/image/output/images/kernel.img      /media/sdcard/FusionX/upgrade/
cp project/image/output/images/rootfs.ubifs    /media/sdcard/FusionX/upgrade/
cp project/image/output/images/FusionX_apps.tar /media/sdcard/FusionX/upgrade/
touch /media/sdcard/FusionX/upgrade/DO_UPGRADE
sync
umount /media/sdcard

Pieges courants

  • Blocage sur CPU0 dans z80drv : tout appel systeme Linux (E/S fichier, allocation memoire via kmalloc avec GFP_KERNEL, mise en veille) depuis du code s'executant dans la boucle de distribution z80drv sur le CPU1 risque la preemption du planificateur ou le blocage. Utilisez uniquement des API noyau securisees et non bloquantes dans le chemin critique.
  • Non-correspondance de variante du bitstream CPLD : compiler le CPLD avec les affectations de broches MZ-80A et le programmer dans du materiel connecte a un MZ-700 cause une synchronisation de bus et un decodage d'adresses incorrects. Verifiez toujours les parametres du projet Quartus II avant la programmation.
  • Isolation CPU non active : si isolcpus=1 n'est pas present dans les arguments de demarrage du noyau, le planificateur Linux preemptera le thread de distribution z80drv pendant les cycles de bus Z80, causant le blocage ou le plantage de la machine hote.
  • Decalage d'horloge SPI : le bus SPI du SOM fonctionne a 50 MHz, ce qui necessite une disposition PCB soignee avec des longueurs de piste appariees et un plan de masse adequat sous les lignes SPI.
  • Image ROM manquante : z80ctrl --loadrom peut signaler le succes meme si le fichier ROM n'est pas trouve sur la carte SD. Verifiez toujours avec dmesg | grep z80drv que le module noyau signale un chargement ROM reussi avant de demarrer le Z80.
  • Frequence de vhw_tick : vhw_tick() est appele environ toutes les 2048 cycles Z80, pas toutes les 2048 microsecondes. A 4 MHz, cela correspond a environ 0,5 ms par tick.
  • Latence de lecture GPIO : le chemin de lecture GPIO utilise pour capturer le bus d'adresses Z80 et le type de cycle a un overhead d'environ 200 ns par lecture sur le SOM SigmaStar. C'est suffisamment rapide pour le fonctionnement Z80 a 4 MHz mais peut etre marginal pour les machines fonctionnant a des vitesses d'horloge superieures.

Sites de reference

Ressource Lien
Page du projet tranZPUter FusionX /tranzputer-fusionx/
Manuel utilisateur tranZPUter FusionX /tranzputer-fusionx-usermanual/
Guide technique tranZPUter FusionX /tranzputer-fusionx-technicalguide/
Page produit SigmaStar SSD202 sigmastar.com.tw
Fiche technique famille Altera MAX7000AE intel.com
Bibliotheque d’emulation Z80 Zeta github.com/redcode/Zeta
Quartus II 13.0.1 SP1 Web Edition logiciel ancien intel.com
Correctif Linux PREEMPT_RT wiki.linuxfoundation.org/realtime
Projet Buildroot buildroot.org
Chaine d’outils arm-linux-gnueabihf packages.ubuntu.com/gcc-arm-linux-gnueabihf

Avis reglementaire sans fil

Cet appareil integre un emetteur-recepteur sans fil SSW101B 2,4 GHz IEEE 802.11 b/g/n (integre dans le SOM SigmaStar SSD202), ce qui en fait un emetteur intentionnel au regard des reglementations radiofrequences mondiales (y compris la FCC Part 15 Subpart C aux Etats-Unis et la Directive sur les equipements radioelectriques 2014/53/EU dans l'Union europeenne).
Bien que le module SOM porte des certifications reglementaires preexistantes, ces certifications au niveau du module ne s'etendent pas automatiquement a un produit fini qui incorpore le module. L'exemption de module pre-certifie permet aux hobbyistes individuels de construire un nombre limite d'appareils a des fins personnelles, experimentales ou educatives sans obtenir une autorisation d'equipement separee.
Limitations importantes
  • Les appareils assembles ne doivent pas etre vendus, proposes a la vente, offerts ou autrement distribues a des tiers a moins que le produit fini n'ait ete teste independamment et n'ait obtenu sa propre autorisation d'equipement (par ex. FCC ID, marquage CE avec evaluation par un organisme notifie) dans la juridiction concernee.
  • La construction de ce projet a des fins personnelles en quantites limitees est generalement autorisee en vertu des dispositions pour hobbyistes et usage experimental (par ex. FCC § 15.23), a condition que l'appareil ne cause pas d'interference nuisible.
  • Les exigences reglementaires varient selon les pays. Les constructeurs en dehors des Etats-Unis doivent consulter leur autorite nationale de radiofrequences pour les regles applicables.
Responsabilite du constructeur
Il est de la seule responsabilite du constructeur de s'assurer que tout appareil construit a partir de ces conceptions est conforme a toutes les reglementations radiofrequences applicables dans sa juridiction. L'auteur fournit ces conceptions a des fins personnelles, educatives et de loisir et ne fait aucune declaration indiquant qu'un appareil construit a partir de celles-ci satisfait aux exigences reglementaires pour la distribution commerciale.