Digging into YM3806
In the previous post I got the PSR-70 original firmware modified so that it dumps to serial port all register writes done to OPQ chip (YM3806). Now I’m getting the data I wanted and plenty of it! A simple keyboard key-down or key-up produce only under 20 register writes but changing sound from the front panel results in about 300 writes. Why 300, there are only 256 registers? Yes, but there are many registers which are written several times.
The raw dumps from the modified firmware are quite hard to read, they just list the register address and written value in hex in the order they happened:
1F=20 5E=1F 56=1F 4E=20 46=20 5E=81 56=81 4E=81 46=81 9E=1F …
To make some sense out of this I wrote a small python script to analyze the dump. It lists all registers in numerical order and all writes done to that register. In the script output the first column is OPQ register number, second column is the number of writes to that register and then a list of the written values.
This simple format makes it much more easy to detect various patterns in the data. Even from this one dump it is easy to notice that register 05H is somehow special, because it is written 8 times, all other only one or two times. And it looks like the same value is written always in 8 consecutive registers; OPQ has 8 voices, is it a coincidence or not… This kind of guesswork can be continued but to get real results and proofs, something else must be done.
Next thing would be to test the dumps: can I get the same sounds out just by repeating the dumped writes? I wrote another python script which takes a dump and generates a stupid assembly program which executes the same writes in the same order. An example of this kind of program is in the Github. By running this program only, without the original firmware, the produced sound is identical to the original, judging by ear. This proves the dumps contain all the necessary information to control the OPQ chip. Fine!
By modifying the generated assembly program it would be possible to do testing like “what if I change this register value”. But it would be cumbersome to each time compile and download a new program. What is needed now is a program with a minimal CLI (command line user interface) for changing the register values effortlessly.
At this point a small visit to hardware side was needed. So far I have used only serial output, which was done by taking the TTL level serial out from the PSR-70 board to Arduino acting as a speed converter, as explained in the previous post. But now that I will be using a CLI, it will need also a serial input. The PSR-70 midi-in port looks more healthy than the midi-out, so I added a small single-transistor driver to Arduino serial port to drive data to the midi-in. Now it is possible to communicate with the programs in a civilized way.
Moving to SDCC
After thinking it for a while, the CLI based test program started to look unnecessary complex to write in assembly. It is time to start using C. I have used the SDCC compiler a lot for my 8051 projects and it can produce code also for Z80, so it is a natural choice for this. I have never used SDCC with Z80 but there’s a first time for everything.
I wrote a basic HelloWorld in C to prove the compiling environment is working. There was no big problems in getting it running. Next I ported my old MiniOS environment to PSR-70 hardware. MiniOS is a minimal “OS” which provides timers and buffered serial communication for the application. Maybe OS is a bit overstatement for this but anyway. After getting this up and running I had a basic environment for writing programs in C for PSR-70 hardware.
The
first version of the program for changing the register values was
quite simple: when it starts, it writes the startup sequence captured
from the original firmware to the OPQ. You can command it to start or
stop playing, i.e. sending the
captured key-down and key-up
sequences. And the most important thing: there is a command to change any of the OPQ register values on the fly.
With this basic setup you can theoretically find out the functions of each OPQ register: just make a change to a register value and listen what happened to the output sound. In practice, it is not that straightforward: there are all kinds of interdependencies between the registers, and changing one value does not immediately affect anything but may have indirect effects. For example, if you change a modulating operator’s release time longer, but the carrier operator’s release time is short, you won’t notice any change in the sound. There are lots of similar combinations which make the analyzing tricky.
The current version of the program is in the Github. It contains many additions to the basic program, added little by little when understanding of the OPQ has grown.
Analyzing the OPQ functionality
Armed with the test program and using the captured sequences as a starting point I started to dig into OPQ. And of course, an oscilloscope is always a necessary equipment, here analyzing the envelope generator functionality:
The whole process is like solving a jigsaw puzzle, piece by piece, the big picture getting clearer step by step.
My shortcoming in this project is that I don’t have much experience in FM synths generally. I have not owned any “factory-made” FM synths, but I have built one using OPL3 (YMF262 chip) so I know it well. That’s why I used it as my main reference. But OPL is actually quite different from OPQ, it is not a very good reference. OPL is basically a 2-operator synth, with a limited support for 4-operator voices. The 4-op support in OPL3 is quite a mess, looks more like an afterthought.
Another synth I studied was the Dexed softsynth which should be an accurate copy of DX7. But also DX7 is quite different from OPQ. It is 6-op synth and for example the envelope generators are far from OPQ.
Only in very late phase of the project, when I already had found out most of the features of OPQ, I began to understand that Yamaha does have a bunch of 4-op synths and they are quite close to OPQ: DX11, DX21, DX27, DX100, TX81Z. I found this in-depth programming tutorial of DX11 from YouTube and was amazed how close everything is, looking from the user interface. Even the bit widths of the parameters are the same.
The OPM sound chip (YM2151) has an application manual available. It is very close to the OPP (YM2164) according to this. But still, they both are different from OPQ. To put it simple: from register 60H upwards the chips are almost identical, from 60H downwards there are big differences.
One of the oddities I cannot understand is the last algorithm (algorithm #7). All Yamaha 4-op synths seem to have the same 8 algorithms. Also OPQ has them, except the last one. According to all sources, the last algorithm is like the one on the left hand side of the picture below. I have tested and retested this and I’m sure that in OPQ it is like the right hand side. This is bothering me a lot but I’m believing my own tests.
ROM2, again
Last time the area in the beginning of ROM2 was still unknown. It looks like it is containing some data, not code. After continuing to analyze the code, I began to understand the contents: it contains the sound data. The PSR-70 has 32 sounds and a few sounds are used for auto accompaniment. For example, the automatic bass guitar sound is none of the user playable sounds. These tables contain about 80 ready defined sounds, so there are quite many unused sounds. I have not yet tried to listen to them.
PSR-60 ROMs
I was exchanging mail with Edward from D-Tech, who has an overwhelming list of Yamaha chips on his site. He asked me to disassemble the Yamaha PSR-60 ROMs, which are available in binary form here. I did it and the results are in my Github.
The PSR-60 main ROM (IC109, EPROM in PSR-70) is different in binary but judged from the disassembly, contents is mostly the same. Most probably it is a recompilation of the same assembly source, containing only the necessary modifications because of PSR-60 hardware differences (4 octave keyboard etc.) ROM2 (IC110) is identical. Because it contains the sound definitions, this proves the sounds are identical.
Finally: the Programmer’s Guide
The actual result of this reverse engineering project, “Programmer’s Guide to Yamaha OPQ FM Synthesizer” is now in version 1.0. It contains the things I was able to dig up. There may be all kinds of misunderstandings and omissions, but I believe most of the information is correct.
If someone has ideas or better information, please feel free to leave comments below in the comment section.
In the next post we will leave the OPQ alone and start analyzing the other main sound chip, the RYP4 (YM2154).
Nice job on figuring this out. I just recently wrote a set of emulators for the OPM/OPN/OPL series of chips for the MAME project, which emulates games as well as several synths. Looking over what you've done, the OPQ seems to be a bit of a mix of OPM and OPN. I've made a first pass at trying to add the OPQ support as you've described, making a few assumptions that certain things behave like OPM vs OPN, and also a few wild guesses (like how detune works since I have no examples of a 6-bit detune parameter to compare with).
ReplyDeleteNext step would be to run some data through it to see how it sounds. I'll probably experiment first with hacking up your UART-driven code to just call to my emulator instead. Any chance you can provide high-quality samples of what the output should sound like for your tests? I may have some suggestions for further tests if you're willing.
Thanks for the comment. I appreciate that you are preserving these chips in the software form.
DeleteI could provide you samples, just define what parameters and values you'd like to hear.
Maybe we should open up a discussion in email for more details. I'll contact you using some other media.
Sure, you can drop me an email at firstname@firstnamelastname.com :)
Delete