Retro-ingénierie d'une carte Z80 : partie 4 - premiers codes en assembleur

Bonjour à tous,

Il y a de nombreux mois, j'ai récupéré une ancienne centrale téléphonique analogique à base de Z80.
Comme c'est le week-end, j'en profite pour me repencher sur ce projet personnel.

Après un assez long travail de rétro-ingénierie, création d'une EEPROM (pour remplacer l'UVProm), ajout d'une interface homme-machine...  Le projet vient de connaître une avancée majeure puisque j'y ai exécuté mes premiers codes assembleurs.

Premier code assembleur Z80 exécuté sur la carte

Si la petite histoire de cette carte vous intéresse, vous pouvez consulter les articles suivants:

  1. Retro-Ingénierie d'une carte Z80 : partie 3 - EEPROM et Interface Homme-Machine
  2. Retro-ingénierie d'une carte Z80 : partie 2
  3. Retro-ingénierie d'une carte Z80
  4. Récupération: du Z80 dans une vieille centrale téléphonique analogique (DTMF)

Programmeur d'EEPROM

Mon plus gros et celui qui m'aura arrêté le plus longtemps est le programmeur d'EEPROM. Un qui soit capable de fonctionner sous Linux. 

Après de longue recherche, j'ai fini par opter pour un XGecu Pro (un programmeur multi-fonctionnel) que l'on peu aussi installer sous Linux à l'aide de Wine. 


Si cela vous intéresse, j'ai documenté l'installation et l'utilisation des EEPROM créée pour le projet.

Source: Wiki de MCHobby

  • XGecu sous Linux
    Installer Xgpro de votre programmeur universel XEGU T56, T48, TL866II plus sous Linux (Ubuntu/Mint)
  • XGecu pratique
    Programmer une EEPROM GLS27SF512 pour remplacer une ROM 27C512

HASKEL-Z80 / Z80-ASM

J'ai complété le dépôt du projet avec le sous répertoire Z80-ASM. Ce dernier contient un readme.md détaillant les différentes étapes de programmation.

Après quelques recherche, j'ai utilisé z80asm (z80asm manpage @ Ubuntu) pour assembler mes exemples.

C'est l'assembleur utilisé par John Winans dans son projet Z80 retro (voir sa chaîne YouTube). S'il a été capable d'écrire le Bios CPM avec z80asm alors ont doit pouvoir tout assembler.

Ce qu'il y a de bien avec z80asm c'est qu'il produit aussi un fichier .lst avec le détail de l'assemblage des instructions (et résultat binaire).

Autre intérêt de z80asm est qu'il est possible de le combiner avec un MakeFile pour produire le binaire à téléverser sur l'EEPROM.

PROGS=\
 01_minimal.bin \
 02_in_out.bin

all: $(PROGS)

clean:
  rm -f ldr
  rm -f *.lst *.bin *.hex

DATE := $(shell date --rfc-3339=seconds)
GIT_VERSION := $(shell git describe --long --dirty; git show -s --format='%ci')
%.bin: %.asm
  cat $< | sed -e "s/@@DATE@@/$(DATE)/g" -e "s/@@GIT_VERSION@@/$(GIT_VERSION)/g" | z80asm - -o $@ --list=$(basename $@).lst

world: clean all

01_minimal.bin: 01_minimal.asm
02_in_out.bin: 02_in_out.asm

Documentation des cartes

N'hésitez pas à naviguer dans les différents répertoires du dépôt pour trouver les schémas et informations utiles des différentes cartes.

https://github.com/mchobby/kicad-public-projects/tree/main/HASKEL-Z80

Conditions de tests

Préambule

Le système d'origine n'a aucune interface d'interaction avec l'humain (pas de LED, pas de bouton, pas d'afficheur) pour la simple raison que c'est une centrale téléphonique analogique.

L'interface de la carte CPU est assurée par une carte analogique par l'intermédiaire de nombreux GPIO exposés par des PIO (des composants Z80 spécialisés pour créer des entrées/sorties). 

Cela réclame donc une programmation assembleur plus importante... par forcement évident pour un néophyte.

Interface homme-machine simplifiée

Appris de mes curiosité avec le RC2014 (une autre machine Z80), il est possible d'utiliser quelques composants avec une ou deux instructions assembleur!

Les instructions assembleurs IN (lire des entrées) ou OUT (modifier des sorties) permettent donc d'interagir avec l'environnement très facilement.

C'est ainsi qu'est née la carte CPU-BOARD-AddOn disponible sur le dépôt GitHub.

Premier test : allumer les LEDs

le programme assembleur 01-minimal.asm se résume en deux lignes.
Il envoi la valeur $0F (15 en décimal, 00001111 en binaire) pour activer les 4 sorties de poids faible (à droite) vers le périphérique de sortie destiné aux LEDs (sur le port $1D)

org     0x0000          ; Cold reset Z80 entry point.

loop:
    ld a, $0f    ; Lit LED 0, 1, 2, 3
    out ($1d),a  ; LED port
    jp loop

Comme il est interdit d'écrire une valeur directement sur un port, la valeur est chargée dans l'accumulateur (A) puis la valeur de l'accumulateur est envoyé vers le périphérique connecté sur le port $1D (qui sont les LEDs).

Et voici le résultat

Premier code assembleur Z80 exécuté sur la carte

Pourquoi une boucle?

L'instruction jp loop permet d'exécuter encore et encore l'activation des LEDs.
Cela n'est pas nécessaire d'un point de vue fonctionnel. Le programme pourrait utiliser un halt à la place du jump (instruction jp).

J'ai cependant eu un problème de faux contact, ce que j'ai fini par découvrir après avoir presque tout démonté... et plus de 5 heures de débogage.
Contre toutes évidences, le code ne produisait pas le résultat attendu!

En cas de problème, il faut essayer de comprendre ce qui ne fonctionne pas normalement alors que l'ensemble ne dispose pas encore d'interface utilisateur!
 

C'est pour cela il est beaucoup plus facile de travailler sur une section de code au fonctionnement répétitif!
Cela permet sonder tranquillement les différents signaux du Z80 et de la carte à l'aide d'un oscilloscope... ils sont supposés être répétitif ;-) .

Dans le cas d'une exécution unique avec une instruction HALT, nous n'aurions que 20-30 ms de diagnostique sous la main. Et il faudrait redémarrer le programme en pressant le bouton Reset

Deuxième test : Lire l'état des boutons

le programme assembleur 02-in-out.asm se résume également en deux lignes.

Il effectue une lecture sur le port  $1C pour acquérir l'état des 8 bits... donc des boutons qui y sont branchés.

Ensuite il envoi la valeur obtenue vers port $1D (où sont connectés les LEDs).

Ce programme est donc supposé allumer les LEDs pour lesquelles le bouton correspondant est pressé.

org     0x0000          ; Cold reset Z80 entry point.

loop:
  in a,($1c)   ; Read buttons 
  out ($1d),a  ; Apply to LEDs 
  jp loop

xxx

Ce qui produit le résultat suivant lorsque le programme est en cours d'exécution

Deuxième code assembleur Z80 exécuté sur la carte

Ensuite

A partir de maintenant les explorations et tests vont pourvoir se poursuivre avec:

  • la RAM de 2 Kio
  • les 3x PIO présents sur la CPU-BOARD
  • la 2ieme & 3ieme ROM.
  • Ajouter un SIO (UART)
    sur le connecteur RC2014 présent sur le CPU-BOARD-AddOn 
  • Utiliser le générateur de tonalité présent sur la CPU-BOARD (voir schéma)

Aucun commentaire