The Spectrum keyboard with its rubber keys, which each could have up to five different functions was unique. It was an improvement on its predecessor the ZX80/ZX81 which had a flat keyboard, but it was not made for fast typing. Anyway, with an emulator you can have any keyboard you like - it only needs to be mapped to the Spectrum keyboard.
The spectrum keyboard is connected to the Z80 via IO-port 0xFE, with groups of five keys (half-rows) each connected to a port address. When a key is pressed, the bit with the same index as the key has within the half-row is reset, and then set when the key is released. Read more about this here. In SoftSpectrum 48 this is handled in the InPort class where there are variables for every possible keyboard IN-port address. These variables are modified every time a key is pressed or released. When the Z80 queries the IN-port it is directed here and provided with the current value for the port address in question. In this way, the Spectrum keyboard is mapped to a PC keyboard, but since the Spectrum commands and special characters are not painted on the PC keyboard it is a bit difficult to work with it. To make things easier I have included the Gosh Wonderful ROM, which allows for entering ZX BASIC commands letter by letter. I have also mapped most special characters on the PC keyboard, accessible via the right shift key, to the Spectrum, as well as the arrow keys. This means that you can probably work with the emulator without having to consult the Spectrum keyboard layout too much.
2 Comments
When I started this project I had absolutely no idea about how to implement the Spectrum beeper. It did indeed turn out to be quite a challenge, partly because I didn't know anything about coding audio at all and partly because of the way the Spectrum beeper works.
How does the beeper work? In Sinclair BASIC, you can activate the beeper with the Beep command. For example "Beep 2,8" will produce a 2 second tone in G#. Sound is generated by a small speaker (beeper) which is controlled by on/off signals produced by the Spectrum. These on/off signals produce a square wave (in theory at least) with a tone corresponding to the frequency of the on/off signals - a 1-bit sound generator. There is a special routine in ROM for controlling the beeper, and it is precisely timed, based on the Z80 clock frequency. Of course, it is possible to control the beeper directly via machine code, which many games do, often producing multi channel sounds. It should also be noted that the ROM routine halts the computer during audio playback to ensure correct timing. In games this is impractical, so programmers had to create sound routines that could produce consistent sounds even during gameplay. How to emulate the beeper? As mentioned above timing is essential. The problem is that an emulator would be challenged to time every Z80 instruction exactly - at least as far as I know. The SoftSpectrum Z80 emulation timing is based on the CPU interrupt period, which is 1/50.88 second. The emulator processes the same number of instructions as a real Spectrum during an interrupt period. In effect the emulator processes a batch of Z80 instructions once every 50th second, but at much higher speed than a real Z80 CPU, so the instructions are processed in bursts. During the rest of the time it does other things like update the screen, handle keyboard input etc. Plugging the emulator directly to a 1-bit audio generator would of course produce very strange sounds due to the emulator processing speed, so something more needs to be done. The solution I found was to send the signals from the Z80 meant for the beeper to a buffered array, where the length of each pulse corresponds to a number of bytes with a value for on or off in the array. The array is processed by an audio generator (NAudio) to create a sound. While this should work in theory I put down many hours trying to get a decent sound from the emulator. No matter what I did I couldn't get rid of intermittent "stutters" and other artifacts. Finally I stumbled upon a discussion somewhere on the web about the lack of precision in the .NET timer class. Apparently it is well known that it lacks the precision or priority for this kind of application, causing irregular interrupt periods, which led to gaps in the audio feed. Luckily I found the Multimedia Timer class at Codeproject.com, which saved my day! Actually I still have to de-tune the beeper somewhat to avoid stutters, but this should not be noticeable. Update 29 December 2016: The de-tuning mentioned above is no longer needed, after I have improved the beeper functions. |
Archives
November 2020
Categories
All
|