[MZ-SD2CMT] GitHub link

hlide
Posts: 681
Joined: Thu Jan 25, 2018 9:31 pm

Re: [SD2MZCMT] GitHub link

Post by hlide »

sharpmz wrote: Wed Jun 06, 2018 6:46 am
hlide wrote: Sun Jun 03, 2018 7:45 pm In fact, I saw you only need to read 100 short pulses for a GAP preceding a TAPEMARK.
Same as the MZ monitor does:
A GAP contains up to 22,000 short pulses but the monitor reads and counts only the first 100 pulses ( see the subroutine GAPCK at location $0FE2 ). The remainig pulses are not counted but skipped.
Yep, but the call to MONITOR MOTOR activates the cassette motor (rolling) and waits after a mandatory 2s then returns. The function GAPCK can then read the minimal 100-unit gap. So what happens is a great number of first GAP units are not even read explaining they have big numbers for 22000 or 11000. So the number of GAP units would depend upon the turbo speed and must be adapted to be sure there are not enough GAP units making the loading failed or no extra GAP units making the loading longer.
User avatar
Pacman
Posts: 172
Joined: Mon Feb 05, 2018 2:59 pm

Re: [SD2MZCMT] GitHub link

Post by Pacman »

What is the transfer rate using the turbo x4 method ?
hlide
Posts: 681
Joined: Thu Jan 25, 2018 9:31 pm

Re: [SD2MZCMT] GitHub link

Post by hlide »

For a 44.1 KHz frequency:

- In legacy mode, a short pulse (0) is taken 22 cycles (11 high + 11 low) and a long pulse (1) is taken 42 cycles (21 high + 21 low).

- in turbo mode x4, a short pulse (0) is taken 5 cycles (2 high + 3 low) and a long pulse (1) is taken 8 cycles (5 high + 3 low).

1) if your binary is just containing "0" bits, 22/5 -> transfer rate is 4,4 faster. If it is just containing "1" bits, 42/8 -> 5,25 faster. So you could say it is around 4.825 faster. But in reality, it is rather something like 3.9 faster because a byte read may take a little longer until I can emit a pulse.
hlide
Posts: 681
Joined: Thu Jan 25, 2018 9:31 pm

Re: [SD2MZCMT] GitHub link

Post by hlide »

But the original mzf2wav has a weird way to build its WAV file by issuing first a low value, then a high value instead of the expected high one followed by a low one.

Example:

If you want to issue "SP LP LP SP SP" with mzf2wav, you'll get:

Code: Select all

|   0    |     1     |     1     |   0    |   0    |
     _3__      _5____      _5____     _3__     _3__
 _2_/    \_3__/      \_3__/      \_2_/    \_2_/    \_...
    +-----^   +-----^     +-----^    +-----^  +-----^
          0         1           1          0       0
So we have 0(3+3=6), 1(5+3=8), 1(5+2=7), 0(3+2=5), 0(3+?=?)

Whereas the good ones should have been :

Code: Select all

|   0    |     1     |     1     |   0    |   0    |
 _3__     _5____      _5____      _3__     _3__
/    \_2_/      \_3__/      \_3__/    \_2_/    \_2_/
+-----^  +-----^     +-----^     +-----^  +-----^
      0        1           1           0        0
So we should have 0(3+2=5), 1(5+3=8), 1(5+2=8), 0(3+2=5), 0(3+2=5)

EDIT:

MZF2WAV is indeed correct. The biggest mistake I did is I forgot the PC1 (/WRITE) and PC5 (/READ) pins are inverted READ and WRITE signals.
hlide
Posts: 681
Joined: Thu Jan 25, 2018 9:31 pm

Re: [MZ-SD2CMT] GitHub link

Post by hlide »

FR:

Ok, j'ai profité de ce weekend - non sans quelques difficultés - pour avancer sur mon projet.

J'ai commençé à apporter une modification pour être en mesure de lire un MZF et de le transcrire en SHARP PWM. C'est un travail en cours.

Je voulais garder la même façon de procéder que pour lire le fichier LEP en me servant d'une machine d'état qui donne la période et le niveau à appliquer sur le signal READ. De cette façon, je peux absorber les coûts liés aux opérations d'IHM (par exemple, la barre de progression) et la lecture des octets depuis le SD dans les périodes entre changement de niveau. J'ai galéré au point que j'ai fini par ajouter la version procédurale - très proche de ce que Pacman a écrit pour pouvoir comparer.

Au lancement, je devais utiliser la commande MD400 pour écrire la séquence "CD 27 00 C3 AD 00" en mémoire et lancer la lecture de l'entête par JD400 puis examiner l'entête avec D10F0. Avec ça, j'ai pu progresser - d'abord avec la version procédurale puis la version machine d'état en utilisant le fichier RAMTEST que j'ai écris et qui est très petit.

Je partais du principe que lorsque le signal avait passé le front descendant, on pouvait alors entamer des opérations lourdes d'IHM ou de lecture du SD. Avec la machine d'état, j'effectuais ces opérations quand le niveau prochain était 1 (front montant à venir après la période d'attente qui suit les opérations) mais j'obtenais de temps en temps des signaux 0 qui passaient à 1 ce faisant. J'ai donc inversé la logique et ça fonctionnait mieux. C'est très contre-intuitif, je vais devoir réexaminer ça.

Par contre, que ce soit avec la version procédurale ou la version machine d'état, je n'ai pas lancé SPACE DRIVE V.2 et MAN-HUNT (comme s'il leur manquait un octet ou plus pour terminer). Ce fut une journée très frustante...

Mais je ne m'avoue pas vaincu.

Pourquoi je ne me contente pas de la version procédurale ? parce qu'elle sérialise très fortement les opérations et les changements de signaux au lieu de les "paralléliser" et ne prend pas en compte l'arrêt du moteur en plein milieu, etc.

EN:

Ok, I took advantage of this weekend - not without some difficulties - to move forward on my project.

I started making a modification to be able to read an MZF file and transcribe it into SHARP PWM. It's a work in progress.

I wanted to keep the same procedure as to read the LEP by using a state machine that gives the period and level to apply to the READ signal. This way, I can absorb the costs associated with GUI operations (for example, the progress bar) and reading bytes from the SD in periods between level changes. I struggled to the point that I finally added the procedural version - very close to what Pacman wrote for comparison.

At launch, I had to use the MD400 command to write the sequence "CD 27 00 C3 AD 00" in memory and start reading the header by JD400 then examine the header with D10F0. With that, I was able to progress - first with the procedural version then the state machine version using the RAMTEST file I wrote which is very small.

I assumed that when the signal had passed the falling edge, one could then start heavy GUI or SD reading operations. With the state machine, I performed these operations when the next level was 1 (rising edge to come after the waiting period which follows the operations) but I obtained from time to time signals 0 which passed to 1 doing so. So I reversed the logic and it worked better. It's very counterintuitive, I'm gonna have to re-examine that.

However, either with the procedural version or the state machine version, I did not run SPACE DRIVE V.2 and MAN-HUNT (as if they were missing one byte or more to finish). It was a very frustrating day...

But I don't admit defeat.

Why don't I settle for the procedural version? because it strongly serializes operations and signal changes instead of "parallelizing" them and doesn't take into account stopping the engine in the middle, and so on...
hlide
Posts: 681
Joined: Thu Jan 25, 2018 9:31 pm

Re: [MZ-SD2CMT] GitHub link

Post by hlide »

FR:

Pour revenir au problème que j'ai. J'ai écris un programme que je charge d'abord dans la page vidéo non-affichée du MZ-700. Ce programme me permet de calculer le checksum du dernier programme chargé (et je l'ai par la suite modifié pour qu'il m'affiche des checksums partiels tous les 256 octets lus). Je charge donc ce programme, puis ensuite le programme qui ne veut pas se lancer et j'ai bien une erreur de checksum. En appelant le programme checksum par JD400, il m'affiche les checksums : il y a bien une différence. Premier constat, les programme est bien chargé complètement, pas un bit en plus ou en moins. Deuxième constat, l'erreur est reproductible avec le même résultat en valeur des checksums. Troisième constat, quand le problème apparaît, c'est lié à un bit 0 transformé en 1, augmentant ainsi la valeur du checksum du côté du MZ-700. Quatrième constat, les valeurs max mesurées des périodes haut et bas pour les signaux long et court restent dans les clous : cela conduit à un paradoxe. J'ai mis les tables de comparaison des checksums à la fin du post.

Je parle de la version procédurale qui émet bien un 0 sur le signal READ à la fin de chaque impulsion émise. Le programe lit d'abord un octet du SD puis émet une impulsion longue suivi de 8 impulsions correspondantes à l'octet lu. Si j'arrête la lecture et que je lance programme direment avec Jxxxx, il tourne sans issue malgré les bits transformés (pas suffisamment pour faire crasher le programme en tout cas). J'aurais pensé que la transformation d'un bit 0 en 1 aurait été vue avec la capture de la période maximum pour un signal haut correspondant à un 0 excédant la période attendue. Ben non, c'est dans les clous et pourtant il y a bien des bits qui changent.

Face à cette enigme, j'ai décidé de remplir les octets du programme d'abord dans un tableau (environ 5 Koctet) avant de commencer l'émission du programme et de passer par ce tableau pour émettre les octets du programme. Surprise : plus de problème de checksum !

Donc lire un octet à partir du SD a des effets de bord sur le temps réellement passé à émettre un signal haut ou bas sans que cela soit capturable par micros() !? se peut-il qu'une interruption ait eu lieu entre le moment où on sort de la boucle d'attente pour le signal haut et avant de mettre le signal bas !? et le temps de cette interruption pourrait être assez longue pour faire transformer le bit attendu 0 en un bit 1 vu par le MZ-700 !? si c'est cela, alors je vais devoir considérer une solution plus radicale :

1) rajouter une SRAM I2C/SPI pour lire tout le programme dans cette SRAM puis lire dans cette SRAM pour émettre les octets.

2) ou utiliser un buffer, pas sûr que cela fonctionne.

3) ou utiliser un PWM pour émettre un signal haut sans pertubation.

EN:

To get back to the problem I have. I wrote a program that I first load into the MZ-700's non-displayed video page. This program allows me to calculate the checksum of the last loaded program (and I modified it later so that it displays me partial checksums every 256 bytes read). So I load this program, then the program that doesn't want to run and I do have a checksum error. When calling the checksum program by JD400, it shows me the checksums: there is a difference. First observation, the program is fully loaded, not a bit more or less. Second observation, the error is reproducible with the same result in checksum values. Third observation, when the problem appears, it is related to a bit 0 transformed into 1, thus increasing the checksum value on the MZ-700 side. Fourth observation, the maximum measured values of the high and low periods for the long and short signals remain in the expected ranges: this leads to a paradox. I put the checksum comparison tables at the end of the post.

I am talking about the procedural version which emits a 0 on the READ signal at the end of each pulse emitted. The program first reads one byte from the SD and then emits a long pulse followed by 8 pulses corresponding to the byte read. If I stop the reading and start the program with Jxxxx, it runs without an exit despite the transformed bits (not enough to crash the program anyway). I would have thought that transforming a 0 bit to 1 would have been seen with capturing the maximum period for a high signal corresponding to a 0 exceeding the expected period. Well no, it's in the range and yet there are some bits that change.

Faced with this enigma, I decided to fill the program bytes first in a table (about 5 Kbytes) before starting the program output and to go through this table to output the program bytes. Surprise: no more checksum problems!

So reading a byte from the SD has edge effects on the time actually spent emitting a high or low signal without it being capturable by micros() !? can an interruption have occurred between the time you exit the standby loop for the high signal and before putting the low signal !? and the time of this interruption could be long enough to turn the expected bit 0 into a bit 1 seen by the MZ-700 !? if that's the case, then I'll have to consider a more radical solution:

1) add an I2C/SPI SRAM to read the whole program in this SRAM then read in this SRAM to output the bytes.

2) or use a buffer, not sure if it works.

3) or use a PWM to transmit a high signal without disturbance.

---------------------

FR: Voici les checksums respectivement pour MAN-HUNT et SPACE DRIVE :
EN: Here are the checksums for MAN-HUNT and SPACE DRIVE respectively:

Code: Select all

MAN-HUNT
--------+-------
MZ-700	|Arduino
--------+-------
0040	|0040
03CB	|03CB
076B	|076A -> 1 bit à 0 transformé en 1
0B0E	|0B0D
0DF6	|0DF5
117F	|117E
14DB	|14D9 -> 2 bits à 0 transformé en 1
1813	|1811 -> 3 bits à 0 transformé en 1
1ABE	|1ABC
1DD0	|1DCE
1F13	|1F10

SPACE DRIVE
--------+-------
MZ-700	|Arduino
--------+-------
01F9	|01F9
054C	|054C
090E	|090E
0C6F	|0C6E -> 1 bit à 0 transformé en 1
1007	|1006
1349	|1347 -> 2 bits à 0 transformé en 1
16D0	|16CE
1A1C	|1A19 -> 3 bits à 0 transformé en 1
1D90	|1D8D
1D91	|1D8D -> 4 bits à 0 transformé en 1
1D91	|1D8D
1D92	|1D8D -> 5 bits à 0 transformé en 1
1FC3	|1FBE
22B1	|22AC 
25BB	|25B6
28AE	|28A9
2B06	|2B01
2D72	|2D6D
User avatar
Pacman
Posts: 172
Joined: Mon Feb 05, 2018 2:59 pm

Re: [MZ-SD2CMT] GitHub link

Post by Pacman »

FR:
Ne serait-ce pas lié à l'arduino UNO ? Sur MEGA2560 je n'ai pas ce genre de problème .

EN :
Would not it be related to the arduino UNO? On MEGA2560 I do not have that kind of problem.
hlide
Posts: 681
Joined: Thu Jan 25, 2018 9:31 pm

Re: [MZ-SD2CMT] GitHub link

Post by hlide »

FR:

non, je suis sur MEGA aussi. En fait, l'explication se trouve dans le fait que la librairie SdFat enclenche des interruptions probablement lié à un "look-ahead" qui viennent pertuber le temps passé entre la sortie de la boucle d'attente qui maintient le signal à 1 et la mise à 0 du signal. Il y a donc un temps indéfini qui peut faire que le signal lu par le MZ-700 sera interprété à 1 au lieu de 0. La deuxième boucle ne prend pas en compte ce temps indéfini et donc sort trop tôt - c'est pourquoi les valeurs max que je récupérais avaient l'air bonne. Mais elles sont fausses en réalité. Ca m'oblige à revoir la façon d'émettre ce signal. J'ai une piste qui devrait ne pas être sensible à ces interruptions.

EN:

No, I'm on MEGA too. In fact, the explanation lies in the fact that the SdFat library activates interrupts probably linked to a "look-ahead" which disturb the time elapsed between the exit of the waiting loop which maintains the signal at 1 and the setting to 0 of the signal. So there is an indefinite time that can cause the signal read by the MZ-700 to be interpreted as 1 instead of 0. The second loop does not take into account this indefinite time and therefore exists too early - that's why the max values I was getting looked good. But they are false in reality. It's forcing me to rethink the way I transmit that signal. I have a lead that shouldn't be sensitive to these interruptions.
hlide
Posts: 681
Joined: Thu Jan 25, 2018 9:31 pm

Re: [MZ-SD2CMT] GitHub link

Post by hlide »

Pacman wrote: Fri Jul 06, 2018 5:15 pm Would not it be related to the arduino UNO? On MEGA2560 I do not have that kind of problem.
FR:

Je travaille en ce moment sur la piste en question. J'ai aussi commencé à réfléchir sur le pourquoi tu n'as peut-être pas le problème : tandis que je procède de la même manière que fait le monitor pour sauvegarder, tu utilises plutôt la même manière que fait le monitor pour lire.

Je m'explique. Après avoir lu un octet depuis le SD, j'envoie l'octet en le précédent d'un bit à 1 (donc 9 pulsations envoyées en tout par octet). Tu envoies l'octet en le faisant suivre d'un bit à 1. J'avais noté que c'était le dernier bit (donc le dernier signal) envoyé qui passait à 1 alors qu'il aurait dû être à 0. Donc, je suppose que le fait d'avoir placé ce bit 1 à la fin masque le problème chez toi puisque ce bit sera toujours à 1 avec ou sans le problème. C'est une solution en demi teinte.

Autrement, je suis en mesure d'envoyer une pulsation haute avec une durée très précise. Cette durée est insensible aux interruptions parce que c'est le hardware qui va remettre le signal à bas après cette durée. Cependant je continue d'avoir le problème. A la lumière de cette nouvelle façon d'émettre une pulsation, l'hypothèse que je m'étais fait sur un "look-ahead" ne tient pas (et d'ailleurs, je vois pas mal de chose qui ne colle pas). Je ne vois pas trop ce qui peut créer ce soucis du côté de l'Arduino donc je m'oriente plutôt sur une inadéquation au niveau de la lecture par le MZ-700 lié au fait de faire une lecture d'octet depuis le SD entre l'octet et le marqueur de bit à 1 de l'octet suivant dont le temps passé semble avoir une implication du côté du MZ-700. Je vais donc placer ce marqueur après l'octet et non plus devant.

En:

I'm currently working on the lead in question. I also started to think about why you may not have the problem: while I do the same way the monitor does to save, you use the same way the monitor does to read.

Let me explain. After reading a byte from the SD, I send the byte followed by a bit to 1 (so 9 pulses sent in all per byte). You send the byte preceded by a bit to 1. I noticed that it was the last bit (so the last signal) sent that went to 1 when it should have been 0. So, I guess putting this bit 1 at the end masks the problem with you since this bit will always be 1 with or without the problem. It's a half-tone solution.

Otherwise, I am able to send a high pulse with a very precise duration. This duration is insensitive to interruptions because it is the hardware that will reset the signal to low after this duration. However, I still have the problem. In the light of this new way of emitting a pulse, the hypothesis that I had made myself on a "look-ahead" doesn't hold (and besides, I see a lot of things that don't stick). I don't see too much what can create this concern on the Arduino side so I'm focusing more on a reading mismatch by the MZ-700 related to reading bytes from the SD between the byte and the 1-bit marker of the next byte whose time spent seems to have an implication on the MZ-700 side. I will, therefore, place this marker after the byte and no longer in front.
hlide
Posts: 681
Joined: Thu Jan 25, 2018 9:31 pm

Re: [MZ-SD2CMT] GitHub link

Post by hlide »

So trying to emit bytes as the Monitor is expected to read them instead of saving them solves the issue as far as I can see. I had also a working state machine version. I made some changes to add an ultrafast mode but it probably broke that version. So I must wait for the next weekend to fix it.

I tried to add the Pacman's WRITE+MOTOR+READ algorithm to get a very fast transfer but I don't understand how the assembly code is doing.

From what I read in the source from MZ-700 side:

1) Loop until PC4 == 0 ----> until MOTOR is 0, i.e, SENSE is 1 (comment here seems wrong)

Code: Select all

    0x7E,             //   LD A, (HL)
    0xE6, 0x10,       //   AND $10  (MOTOR ON : SENSE = 0 ?)
    0x20, 0xFB,       //   JR NZ, B:
2) XOR A (<=> LD A,$00) ----> [0:xxx:000:0] Mode BSR, select PC0, set bit to 0 (huh, wait! shouldn't we need to set PC1 to 1 here? i.e, [0:xxx:001:1] and so LD A,$03?)

Code: Select all

    0xAF,             //   XOR A          Front haut
    0x77,             //   LD (HL), A     DWRITE =  1
3) Loop until PC4 == 1 ----> until MOTOR is 1, i.e, SENSE is 0 (comment here seems wrong)

Code: Select all

    0x7E,             //   LD A, (HL)
    0xE6, 0x10,       //   AND $10  (MOTOR ON : SENSE = 1 ?)
    0x28, 0xFB,       //   JR Z, C:
4) LD A, $02 ----> [0:xxx:001:0] Mode BSR, select PC1, set bit to 0

Code: Select all

    0x3E, 0x02,       //   LD A, $02      Front bas
    0x32, 0x02, 0xE0, //   LD ($E002), A

which gives :

Code: Select all

                         ____________
O:WRITE             ____/            \_     

                    _           _______
I:MOTOR (/SENSE)     \_________/

                    _______  _____ ____
I:READ              _______><_____*____
From what I read in the source from Arduino side:

1) Wait until PC1 == 0

Code: Select all

        // Attente demande donnee
        while (digitalRead (MZ_CASSETTE_WRITE) == 0) { }
2) Set PC4 to 1 indirectly (motor on)

Code: Select all

        // Informe le positionnement du bit
        digitalWrite (MZ_CASSETTE_SENSE, LOW) ; // signal /SENSE a 0 (Lecteur disponible SENSE=1)
3) Wait until PC1 == 1

Code: Select all

        // Attente de la lecture de la donnee
        while (digitalRead (MZ_CASSETTE_WRITE) == 1) { }
4) Set PC4 to 0 indirectly (motor off)

Code: Select all

        // Reinitialisation
        digitalWrite (MZ_CASSETTE_SENSE, HIGH) ; // signal /SENSE a 1 (Lecteur non disponible SENSE=0)
which gives :

Code: Select all

                      ____________
I:WRITE             _/            \____

                             ________
O:MOTOR (/SENSE)    ________/        \_

                    ____  _____________
O:READ              ____><_____*_______

Note that PC1 is set to HIGH by default when not used.
Post Reply