Bonjour à tous

Histoire de ne pas parler à des inconnus, voici le bottin de la jet set.

Modérateurs : Papy.G, fneck, Carl

Avatar de l’utilisateur
hlide
Messages : 3469
Inscription : 29 nov. 2017 10:23

Re: Bonjour à tous

Message par hlide »

Bienvenu à bord !

en parlant d'émulateur :
- quel langage pour le source ?
- plus ou pas plus ?
- le cycle utilisé : plutôt NOP ou T-states ?
Brochiman
Messages : 3405
Inscription : 02 juin 2019 11:26
Localisation : Angers

Re: Bonjour à tous

Message par Brochiman »

Bienvenue
Dmanu78
Messages : 268
Inscription : 20 juin 2020 14:28
Localisation : Yvelines

Re: Bonjour à tous

Message par Dmanu78 »

hlide a écrit : 19 juil. 2020 11:00 Bienvenu à bord !

en parlant d'émulateur :
- quel langage pour le source ?
- plus ou pas plus ?
- le cycle utilisé : plutôt NOP ou T-states ?
Merci 🙂
Le source est en C essentiellement avec un peu de ++, qui me semble être le meilleur compromis pour écrire un émulateur. J’utilise Visual Studio qui est très bien pour moi. Je ne suis pas un informaticien de formation et pour ce « projet » j’ai du tout réapprendre de zéro côté programmation Windows et potasser les docs en ligne assez indigestes de Microsoft. Comme j’aime bien me compliquer la vie, j’utilise exclusivement les API Win32 (pas de MFC et autre surcouche d’API ), y compris pour l’affichage et le son. Ce qui veut dire aussi qu’il tournera sur Win10 exclusivement je pense. Je vérifierai plus tard la comptabilité...
Le cycle de base utilisé est le T-State. Tous les composants du CPC sont émulés indépendamment les uns des autres à partir des signaux reçus sur leur « broches », au rythme des pulsations d’horloge comme en vrai quoi. Par exemple, la lecture ou l’écriture en mémoire se fait par le Gâte Array et non directement par le processeur. Si le processeur demande à lire la RAM sur le cycle T2, le bus d’adresse est renseigné puis les signaux MREQ et RD sont positionnés selon les datasheets du Z80 et l’information sera reçu sur le bus de données en T3 ou T4, ce qui nécessite une bonne synchro entre tous ces éléments. J’aurais pu faire beaucoup plus simple mais je souhaite construire un émulateur aussi fidèle que possible. C’est très stimulant intellectuellement parlant, c’est comme un énorme jeux d’engrenages à assembler. Heureusement que je ne suis pas pressé...
La contre partie c’est qu’il va être gourmand en ressource processeur je pense. On verra une fois les fonctions essentielles codées...dans un certain temps. Wait & See.
Avatar de l’utilisateur
hlide
Messages : 3469
Inscription : 29 nov. 2017 10:23

Re: Bonjour à tous

Message par hlide »

Ça m'intéresse. Je suis très curieux de ce que tu va faire. Tu utilises un outils comme GIT ? je peux t'aider aussi.

Tu émules donc le GA (enfin c'est obligé dans tous les cas) à partir d'une horloge principale à 16 MHz ? et tu laisses le GA dérivait les fréquences nécessaires au CPU, au CRTC et autre puces - en sachant que le CPU est le moins prioritaire du fait que l'accès à la mémoire n'est pas déterministe contrairement aux autres puces et en faisant que le GA fournisse un motif répété "1000" (valeur par pulsation du clock CPU à 4 MHz fournie par le GA) sur le signal /WAIT (c'est ce qui fait que les instructions sont alignées sur du 4 cycles et que les Amstradiens comptent en nombre NOP les instructions) ?

Je pense que cette émulation au plus bas niveau est très intéressante et permettrait de mieux comprendre le fonctionnement réelle de la machine donc une initiative qui m'intéresse.
Dmanu78
Messages : 268
Inscription : 20 juin 2020 14:28
Localisation : Yvelines

Re: Bonjour à tous

Message par Dmanu78 »

Merci pour ton intérêt. :)
Oui tu as parfaitement raison, le GA reste le maître d’orchestre de l’ensemble. La boucle principale de l’émulateur commence là. Le GA est bien cadencé à 16Mhz et fixe la fréquence de fonctionnement des autres composants (Z80A bien sûr, CRTC, PSG...par division de cette fréquence. La génération des différents signaux de synchro Vidéo HBL, VBL et donc du signal d’interruption de 3.3 ms environ émis par la GA derive donc de cette fréquence d’entrée du GA. Le Z80A est emulé par des cycles Haut/ Bas de 4 MHz (Comme décrit dans le data sheet Zilog) et les cycles d’attente Twait sont gérés nativement lors des accès mémoire concurrents (le signal /Wait est envoyé par le GA Lorsqu’il écrit dans la mémoire vidéo tous les 1Mhz (à raison de 2 octets par cycle Hait/Bas de 1Mhz).
Je termine d’écrire toutes les instructions du Z80 mais mon gros challenge maintenant et de les tester par rapport à un vrai Z80 (et notamment le comportement du registre F). J’ai vu qu’il y avait un programme Zexall qui fait ça mais l’implémentation dans mon émulateur n’a pas l’air simple....Un truc à faire dans ma (encore) longue TODO list...
Sinon non je ne connais pas GIT. Je regarderai sur internet. Je ne reste qu’un amateur éclairé en informatique. Merci en tout cas. :)
Avatar de l’utilisateur
hlide
Messages : 3469
Inscription : 29 nov. 2017 10:23

Re: Bonjour à tous

Message par hlide »

GIT permet d'archiver tes modifications sous forme d'un historique et/ou de révisions. C'est un sacré plus pour revenir en arrière, repartir depuis un autre point, etc. Et quand il y a plusieurs développeurs, ça facilite énormément la fusion des modifications.

Il y a le pendant GitHub qui permet le partage du source en public, la clonage d'un projet et la possibilité d'incorporer des modifications des autres clones (ce que l'on nomme "pull request") quand cela est pertinent.
__sam__
Messages : 7923
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: Bonjour à tous

Message par __sam__ »

J'ajoute en plus de GitHub, il y a à présent GitLab qui a la cote.
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Avatar de l’utilisateur
hlide
Messages : 3469
Inscription : 29 nov. 2017 10:23

Re: Bonjour à tous

Message par hlide »

Dmanu78 a écrit : 29 juil. 2020 15:27 les cycles d’attente Twait sont gérés nativement lors des accès mémoire concurrents (le signal /Wait est envoyé par le GA Lorsqu’il écrit dans la mémoire vidéo tous les 1Mhz (à raison de 2 octets par cycle Hait/Bas de 1Mhz)."
De ce que j'ai compris, les fréquences sont dérivées de sorte qu'une pulsation (signal haut puis bas) sur quatre consécutives du CPU peut donner accès au bus (/WAIT = 1 puis les trois autres qui suivent à 0) . Ça a pour conséquence que les instructions semblent s'exécuter par cycle multiples de 4.

Prenons LD I,A suivi de NOP :

Code : Tout sélectionner

/WAIT:      0100     01000     1000100
-------------+--------+---------+--+--
ED47    M1R  |        |         :  |   [LDI I,A]
            1|   T1   |         :  |  
             1---T2   |         :  |
              1  T3   |         :  |
               1 T4   |         :  |
            ----      |         :  |
               4 M1R  |         :  |
                     1|    T1   :  |
                      1----T2   :  |
                       1   T3   :  |
                        1  T4   :  |
                         1 T5   :  |
                     -----      :  |
                         5      :  |
00                         M1R  :  |   [NOP]
                               1:  |   T1
                               [1--|   Twait inserted at T2!
                                 1-|   Twait inserted at T2!
                                  1|   Twait inserted at T2!
                                   1]  T2
                                    1  T3
                                     1 T4                                   
                               -------
                                     7
Soit 16 cycles au lieu de 13. Note que 16 s'arrondit bien à 4 NOP.
Dmanu78
Messages : 268
Inscription : 20 juin 2020 14:28
Localisation : Yvelines

Re: Bonjour à tous

Message par Dmanu78 »

En fait la réalité me semble un peu plus complexe. D’après ce que je comprends le GA envoie bien un /Wait tous les 1 MHz pour accéder à la mémoire écran qui est prioritaire, soit effectivement 1 /Wait fois tous les 4 MHz mais ..pas tout le temps : lorsque le GA affiche la bordure écran, il n’accède pas à la mémoire vidéo, il adresse directement la sortie vidéo et il ne doit pas y avoir de signal /Wait envoyé à ce moment là. Je n’ai pas vu de documentation dans ce sens mais ça semble logique.
Cependant le Z80 ne répond pas forcément à ce signal s’il n’accède pas à la mémoire à ce moment là. Par exemple, durant le cycle M1 de « fetch de l’instruction », le Z80 teste le signal /Wait uniquement à la fin du T2 state après avoir positionné les signaux /MREQ ET /RD. Si ce signal est présent le Z80 ajoute un Cycle d’attente TWait avant le cycle T3 -> çe qui fera T1 T2 Tw T3 pour décoder l’instruction
Mais si le signal /Wait n’est pas actif en fin de T2 (par exemple il apparaît en T3) il ne sera passera rien pour le Z80 et l’accès memoire sera fera bien en 3 T-States. T1 T2 T3
C’est de l’horlogerie Suisse...
Avatar de l’utilisateur
hlide
Messages : 3469
Inscription : 29 nov. 2017 10:23

Re: Bonjour à tous

Message par hlide »

Ok que ce soit avant ou après T2, ça ne change pas grand chose, juste un décalage d'un cycle pour la prise en compte du /WAIT puisque que l'idée c'est que l'on va obtenir un alignement automatique des instructions via le M1R (/M1, /MEMRQ et /RD pour aller quérir l'opcode en DRAM donc sensible au signal /WAIT), le MRD ou le MRW (/MEMRQ et /RD ou /WR). Après que le signal /WAIT dépende réellement d'un accès mémoire réel par une autre puce, il faut que je vérifie avec le VHDL du GA, parce que ton affirmation met à mal l'usage du NOP comme mesure de cycles au lieu du traditionnel T-state chez les Amstradiens.
Dernière modification par hlide le 29 juil. 2020 21:20, modifié 1 fois.
Avatar de l’utilisateur
hlide
Messages : 3469
Inscription : 29 nov. 2017 10:23

Re: Bonjour à tous

Message par hlide »

Bref tour dans le code VHDL résultat d'un reverse-engineering d'un "decapping" du GA40010.

Signal haut si RESET actif

Code : Tout sélectionner

wire reset = ~RESET_N;
SEQUENCER

Code : Tout sélectionner

reg [7:0] S;
Signal haut si S[6] haut et S[7] bas (on peut ignorer l'autre cas exceptionnel)

Code : Tout sélectionner

wire U204 = (reset & ~M1_N & ~IORQ_N & ~RD_N) | (S[6] & ~S[7]);
On découpe la fréquence 16 MHz en 8 états qui se décalent à chaque cycle

Code : Tout sélectionner

always @(posedge clk) begin
	if (cen_16) begin
		S[0] <= ~S[7];
		S[1] <= S[0] | U204;
		S[2] <= S[1] | U204;
		S[3] <= S[2] | U204;
		S[4] <= S[3] | U204;
		S[5] <= S[4] | U204;
		S[6] <= S[5] | U204;
		S[7] <= S[6];
	end
end
Au final la séquence sera déterministe (si on excepte la cas exceptionnel).

SEQUENCER DECODE

Code : Tout sélectionner

always @(posedge clk) begin
	if (cen_16) begin
		PHI_N <= (S[1] ^ S[3]) | (S[5] ^ S[7]);
		RAS_N <= (S[6] | ~S[2]) & S[0];
		CASAD_N <= RAS_N;
	end
end
Donc l'état de CASAD_N dépend uniquement du séquenceur.

Le signal READY connecté au signal /WAIT du CPU est la résultante de

Code : Tout sélectionner

rslatch ready_l(clk, S[3] & ~S[6], CASAD_N, READY);

// simulate an RS flip-flop in the 'clock' domain
module rslatch (
    input clock,
    input s,    // set
    input r,    // reset
    output reg [WIDTH-1:0] q  // output
);
Donc le /WAIT du CPU dépend de fait uniquement du séquenceur.
Dernière modification par hlide le 30 juil. 2020 14:58, modifié 1 fois.
Dmanu78
Messages : 268
Inscription : 20 juin 2020 14:28
Localisation : Yvelines

Re: Bonjour à tous

Message par Dmanu78 »

Hou là C’est intéressant mais tu m’ as perdu. En résumé je comprends que le signal Ready est bien émis périodiquement par le GA ? J’en ai profité pour regarder quelques forums et effectivement les timing du GA font beaucoup parler. Le GA semble bien émettre Systématiquement un Twait toutes les microsecondes à travers le signal Ready peu importe s’il accède ou non à la mémoire, afin de « synchroniser«  le Z80 à chaque cycle de 1 MHz (ce qui est finalement dommage car ça bride les performances du Z80). Le GA reste le maître de l’accès à la mémoire du CPC.
Avatar de l’utilisateur
hlide
Messages : 3469
Inscription : 29 nov. 2017 10:23

Re: Bonjour à tous

Message par hlide »

Oui, le GA est l'orchestrateur : il va générer un signal /WAIT périodique peu importe que l'accès à la mémoire soit réel ou non pour le CPU. Le cycle est sur 4 MHz, pas sur 1 MHz car le séquenceur par rotation complète génère 4 impulsions pour le CPU : 16 / 4 = 4 MHz. Parmi ces 4 impulsions, 1 seul aura le /WAIT (ou le READY) à 1. Les trois autres qui suivent auront 0. Dans les faits, les instructions à 4 ou 8 cycles s'exécuteront sans perte de performance. Seules les instructions ayant des cycles non multiples de 4 peuvent induire des cycles d'attente supplémentaires sur l'instruction suivante à cause des M-cycles de type MR1, MRD ou MWR. D'une manière générale, il faut considérer que ce mécanisme auto-aligne l'exécution de l'instruction sur l'opcode fetching. Donc une suite de N x NOP sera optimal pour N-1 x NOP même si le premier NOP n'était pas aligné. En fait, le désalignement est généralement exceptionnel. 1 NOP sur 4 MHz, c'est effectivement assez proche de 1µs. En équivalence d'un Z80 s'exécutant sans les insertions de Twait, la fréquence moyenne de ce Z80 serait de 3,3 MHz sur l'Amstrad.

Voici un document qui donne les cycles en NOP (= 4 T-states) des instructions pour l'Amstrad : https://wiki.octoate.de/lib/exe/fetch.p ... 131019.pdf

EDIT: ok je vois ce que tu voulais dire par "synchroniser" sur un cycle de 1 MHz (le "cycle" NOP en somme).
Dernière modification par hlide le 30 juil. 2020 14:46, modifié 1 fois.
Dmanu78
Messages : 268
Inscription : 20 juin 2020 14:28
Localisation : Yvelines

Re: Bonjour à tous

Message par Dmanu78 »

Oui exactement. On est bien d’accord. Je commence à: mieux saisir l’approche par « NOP »
Fondamentalement les cycles /Wait sont synchronisés avec la fréquence du CRTC à 1 MHz (piloté par le GA également). En fait, je croyais que le signal /DISPEN en retour du CRTC (Caractères affichables ou non) conditionnait le signal /Wait du GA mais non finalement...
Répondre