logo

 

Le Fat-Ass project de la fin d'année, avec peut être un second pour suivre.
Je tente de faire une interface MIDI / Master System qui tient dans une cartouche.

14 Novembre:
Pour l'accès aux données à partir de la cartouche, j'ai choisi d'utiliser les ports d'I/O du Z80, puisque les registres en mémoire demanderaient pas mal d'hardware en plus.
Un ATMega8 se chargerait de la conversion MIDI vers parallèle et de la mémoire tampon pour que Midisms (le programme en cartouche) puisse aller les chercher à chaque VBL.
En regardant le schéma officiel de la Master System, on voit que seules les lignes d'adresse A0, A6 et A7 servent pour les I/O normales.


Exemples:

Une lecture sur le port $DC (pad joueur 1):
Bit 76543210 760
DC= 11011100 110

Adresse le 315-5218 (controleur propriétaire d'I/O pour les ports DB9). C'est lui qui va répondre sur le bus de données.

Sur smspower.org il est écrit que certains jeux utilisent le port $C0 au lieu du port $DC, effectivement, ça ne change rien du tout car les bits 7,6,5 et 0 sont les mêmes.
Bit 76543210 760
C0= 11000000 110

Tous les ports ont alors 15 mirroirs (2^4 bits inutilisés - le port indiqué par Sega), sur la Master System en tout cas. On peut très bien lire le port $C0, $C2, $C4, $C6... au lieu de $DC.
J'ai pu tester ça avec plusieurs ports, y compris $BF et $BE, ça a toujours fonctionné. Et de toutes façons, aucun composant censé réagir à IORQ/ n'est relié à A1, A2, A3 et A4.

Autre exemple, une écriture sur le port $BF (adressage VDP):
Bit 76543210 760

BF= 10111111 101

Le port $7F qui sert à commander le PSG (OUT) et à lire le numéro de ligne TV (IN):

Bit 76543210 760
7F= 01111111 010

Normalement les deux réponses devraient être faites par le VDP (le PSG est dedans), A6 est à 1 car le port $3F sert à autre chose. Il en va de même pour $7E et $3E.

760
000:$3E (mirroirs pairs: $00-$3C)
001:$3F (mirroirs impairs: $21-$3D)
010:$7E (VDP ou 315-5218 ?) (mirroirs pairs: $40-$7C)
011:$7F (VDP ou 315-5218 ?) (mirroirs impairs: $41-$7D)
100:$BE (VDP) (mirroirs pairs: $80-$BC)
101:$BF (VDP) (mirroirs impairs: $81-$BD)
110:$DD (315-5218) (mirrois pairs: $C1-$FF)

111:$DC (315-5218) (mirrois impairs: $C0-$FE)


Une bonne partie du logiciel de debug est écrite, il tourne en émulateur et sur la vraie machine.


Premiers essais de Midisms sur la vraie machine. Les essais du PSG fonctionnent, et le compteur de VBL permet de voir rapidement si quelque chose a foiré.
J'ai enfin mes routines propres pour afficher de l'hexa, du binaire et des petites "progressbars" ! Le seul sprite dans tout ça, c'est la petite note à gauche qui permet de choisir quelle ligne on veut modifier.


La valeur lue sur le port $14 n'était pas vraiment attendue... Chaque IN sur un port "non routé" sur la console retourne justement le numéro de port. Emukon retourne $FF.
IN A,($14) met $14 dans A.
$14 ne devrait avoir aucune raison de se mettre dans A car il n'est écrit que sur le bus d'adresse. Soit j'ai raté un composant, soit j'ai rien compris.

 

15 Novembre:
Breadboard et câblage terminés, tout fonctionne comme prévu à l'exception de la valeur retournée par IN toujours. Y'a un véritable souci mais au moins je sais d'où il vient maintenant.
Hier je me demandais pourquoi IN A,(xx) mettait xx dans A alors que le bus de données ne changeait pas.
En fait, (et là je découvre !) juste avant d'éxecuter le IN a,(xx), le Z80 devait aller lire les opcodes dans la mémoire de la cartouche. L'opcode de IN A,(xx) est la suivante: DB xx.
Ce qui veut dire que le Z80 a lu "$DB", puis "XX", et a exécuté l'instruction. Mais après avoir lu ce "XX", le bus de données ne change pas (la RAM dans ma cartouche maintient le bus a sa dernière valeur),
la lecture que fait IN A,(XX) retourne forcément "XX", puisqu'il traînait encore sur le bus ! C'est simplement le dernier bout d'opcode qui restait coincé dessus, c'est logique après tout. Il n'y a aucune raison
que le bus de données change de valeur entre temps...
Le souci maintenant c'est que le Mega8 réagit parfaitement bien à l'adressage sur A1A2A3A4 avec le décodeur NAND/NOR, mais il ne peut répondre qu'en "demi-tristate" pour rester dans les temps. Ce "demi-tristate" met la broche en vrai tristate si elle doit sortir un "0", et fournit du courant si elle doit sortir un "1". C'est comme ça lorsqu'on met DDRx à $00 en ayant PUD de SFIOR à 0 (Tristate enable, Pull up enable). Concretement, ceci permet de fixer des lignes du bus de données à 1, mais pas de les descendre à 0. Il reste alors le 00010100 du $14 en masque.
Par exemple, si je fais sortir 10101010 sur le Mega8, le IN A,($14) donnera 10111110. Les bits sont coincés à 1.

Tout le problème réside dans le fait que pour pouvoir fixer des bits à 0, il faut que les broches du Mega8 soient toutes en "vraie" I/O, pas de tristate nul part. Alors que quand rien ne lui est demandé, il
faut impérativement que toutes ses broches soient en tristate, sinon il fixerait tout le temps le bus de données. C'est testé et approuvé: tout plante.
Rien que le fait de changer DDRx met le Mega8 en dehors des temps, et le fait répondre après que le Z80 lise le bus. La lecture avec IN A,($14) donne des valeurs instables et fait parfois planter la console.
J'imagine que le Mega8 répond lorsque le Z80 passe à la requête de l'instruction suivante, et lui pourrit les premiers bits.


Le matériel encore raisonablement organisé, avec l'ancien décodeur NAND/NOR, la cartouche RAM, le Mega8 et le bus de données cablé sur le port cartouche.
La LED rouge changeait d'état à chaque fois que Midisms réclamait quelque chose sur le port $14.


Schéma sans décodeur (vite corrigé, toutes les entrées d'adresses ont été mises sur des portes logiques, et leur sortie finale reliée à PD2.)
Croire que le Mega8 était assez rapide pour décoder l'adresse au niveau logiciel et répondre dans les temps, c'était se fourrer le doigt dans le cul jusqu'à l'oeil.

 

16 Novembre:
Pour faire ça proprement j'ai décider d'utiliser un latch. La valeur derrière sera prévue par le Mega8, et il pourra la donner sur le bus de données exactement dans les temps.
J'utiliserais un 74LS373, octuple latch-D inverseur. OE/ passant à l'état bas posera les données sur le bus, pile quand le port sera adressé. OE/ passant à l'état haut remettera les sorties du 74LS373 en tristate.
Ca fait un gros circuit intégré en plus, mais on gagne en fiabilité et de toutes façons, on perd le 4011 puisque je ne vérifie plus qu'une seule ligne d'adresse (A2, qui ne devrait jamais être à 0 pendant une
opération d'I/O). Je pourrais de cette manière lire l'octet sur le latch en lisant le port $1A (00011010) ou ses mirroirs ($00 $02 $08 $0A $10 $12 $18). Encore faut-il que ni le VDP, ni le 315-5218, ne réagisse à
A7A6A5A0=0000... A vérifier.

Il faut aussi que je finisse par tester l'entrée midi et la mémoire tampon sur le Mega8; ça devrait pas être trop dur. Ca sera ça de fait.
Le 373 va être beau à câbler sur si peu de place... Je vois mal la cartouche finale tenir sur de la plaque en bandes, va falloir demander aux chinois.

 

20 Novembre:
Finalement le latch a bien été utile, tout fonctionne comme prévu et la console répond maintenant à deux canaux midi. Ca a bien avancé.
Cette fois-ci, le décodeur NOR (A2) est directement relié à OE/ du 74LS737: plus aucun délai. Le Mega8 est informé de l'accès car il y est lui aussi relié. Entre deux VBL il a donc largement le temps de préparer le prochain octet. Après un peu de discution sur le forum d'smspower.org, j'ai eu confirmation des mirroirs des ports.
Aussi, en lisant de plus près la documentation du Z80, je me suis aperçu qu'il y avait une broche prévue pour lui indiquer quand les données étaient prêtes (WAIT/). Mais l'espoir fut bref, car l'état de cette ligne est samplée pendant un cycle, juste après la descente de IORQ/: le Mega8 aurait jamais le temps de la faire descendre également. A moins que ça devienne un boulot supplémentaire pour les portes NOR... A voir.

Il n'y a pas encore de vraie polyphonie. C'est à dire qu'il y a bien plusieurs notes qui peuvent être jouées, mais chaqu'une sur un canal midi différent. Cela pourra être implémenté sans trop d'éfforts avec une petit liste de "disponibilité" de chaque voix du PSG. Il n'y a pas non plus de vrai buffer MIDI encore, j'ai pu tester la communication jusqu'à 40 notes par seconde et tout c'est bien passé, logique puisque le Mega8 se fait interroger 50 ou 60 fois par seconde (PAL/NTSC). Les doubles-notes ne fonctionnent pas, mais c'est une conséquence de ne pas avoir de buffer. Puisque les deux messages Note-On se suivent, le dernier écrase le premier. Le plus étrange c'est que mon Electribe-A n'a pas l'air d'envoyer ces messages dans l'ordre des numéros de canaux... J'ai alternativement la note du canal 1 ou du canal 2, sans jamais trop savoir. Autre souci que devrait règler le buffer, c'est l'utilisation des potards/pitch mod, qui impliquent de balancer un gros paquet de messages midi pour avoir une réaction rapide.
Il faudra peut être que Midisms aille réclamer les données en dehors du VBL pour obtenir des vitesses plus acceptables (ou lire plus de fois).


Schéma avec simple décodeur (attend que RD/, IORQ/ et A2 soient tous à 0), et latch parallèle.

MCUCR = 0b00000010; // INT0 sur front descendant
GICR = 0b01000000; // INT0 ON

SFIOR |= _BV(PUD);
DDRB = 0x00; // Tristate
PORTB = 0xAA;
PINB = 0b????????

SFIOR $= ~_BV(PUD);
DDRB = 0x00; // Tristate
PORTB = 0xAA;
PINB = 0b1?1?1?1?

DDRB = 0xFF; // Sink/Source
PORTB = 0xAA;
PINB = 0b10101010

Routine d'affichage binaire:

B:8 C:Valeur
Showbinary:
PUSH AF
PUSH BC
LD A,C
AND $80
JR Z,Zero
LD A,$11 ;Tile du "1"
OUT ($BE),A
JP One
Zero:
LD A,$10 ;Tile du "0"
OUT ($BE),A
One:
XOR A
OUT ($BE),A
SLA C
DJNZ Showbinary
POP BC
POP AF
RET

Routine d'affichage hexa:

C:Valeur
Showhex:
PUSH AF
LD A,C
AND $F0
SRL A
SRL A
SRL A
SRL A
CALL Checkalpha
ADD A,$10 ;Tile du "0"
OUT ($BE),A
XOR A
OUT ($BE),A

LD A,C
AND $0F
CALL Checkalpha
ADD A,$10
OUT ($BE),A
XOR A
OUT ($BE),A
POP AF
RET

Checkalpha:
CP $0A
RET M
ADD A,$07 ;Tile du "A" par rapport au tile du "0"
RET

Génerateur de tiles pour la "barre de progression":

Créé 9 tiles (couleur 15), utilise CD BC et AF
LD C,$09
LD D,$00
Ldbars:
LD B,$20 ;4 bitplanes * 8 lignes
Ldbar:
LD A,D
OUT ($be),A
DJNZ Ldbar
SCF
RR D
DEC C
LD A,C
OR A
JR NZ,Ldbars

Routine d'affichage de "barre de progression":

B:Largeur max en tiles C:Valeur
Nécéssite 9 tiles consécutifs, un vide, puis avec une ligne verticale, deux lignes, trois...
Showbar:
PUSH AF
PUSH BC
Showbarlp:
LD A,C
CP $09
JP C,Infer
LD A,$69 ;Tile rempli (neuvième)
OUT ($BE),A
XOR A
OUT ($BE),A
DEC B
LD A,C
SUB $08
LD C,A
CP $00
JR Z,fill
JP Showbarlp

Infer:
LD A,$61 ;Tile vide (premier)
ADD A,C
OUT ($BE),A
XOR A
OUT ($BE),A

Fill:
LD A,$61 ;Tile vide (premier)
OUT ($BE),A
XOR A
OUT ($BE),A
DJNZ fill
POP BC
POP AF
RET

footer
symbol symbol symbol symbol symbol