BeeBMIDI (Part 4)
Building E&MM's MIDI interface for the Beeb? Jay Chapman's got some further information you should know.
A look at how our MIDI interface for the BBC home micro receives MIDI data and the usefulness of interrupts.
The simplest method of receiving MIDI data via MIDI In involves looking at the 'Receive Register Full' bit in the 6850 ACIA Status Register to see if a byte has arrived. This is both useful and very easy to understand (and has already been described several times in the pages of E&MM). Unfortunately, it is not consistently fast enough on the BBC Micro, for reasons which will be explained below.
To be more exact, the problem is not simply one of sheer speed but one of response. The BeeBMIDI interface and BBC Micro certainly have sufficient speed provided the micro responds fast enough when a byte arrives, but the crux of the problem is that the computer might not 'choose' to look to see if a byte is present for some time after the byte actually arrives. By the time the micro looks, and notices that a byte has arrived, the next byte might also have arrived, and one of the bytes, the second, will be lost (this is a simplification - for those interested, it is the arrival of the start bit of the third byte which causes the loss of the second).
Those of you who are using the DX7DUMP program published in E&MM August will already have had some experience of the problem described above: you may have noticed that it can take several attempts to receive voice data from the DX7. What happens is that sometimes the Beeb is not fast enough and one or more data bytes are lost. DX7DUMP uses the checksum calculation to make sure we know when the data has been received correctly so that we're not left with any bad data, and deals with the problem by simply retrying until a 'clean' copy of the data arrives. This is the method used by some more automatic communication systems where data is checked on arrival; if it is corrupted, the receiver asks for a retransmission. Since the Yamaha DX7 doesn't understand any such commands sent over MIDI, the DX7DUMP program has to use its human user as an intermediary: you keep pushing the voice selection keypad until the 'voice received OK' message appears.
This is where the problem really rears its ugly head.
By real time input, I mean any input where a guaranteed response time is required to avoid corruption of the data being input. There are several situations in which we are interested in real time input when using a microcomputer with MIDI, and these include (a) receiving voice (or other parameter) data from a synth, (b) receiving rhythm track data from a drum machine, (c) receiving keyboard and controller data during a (real time) performance, and (d) receiving such data during step time input.
Points (a), (b) and (d) relate to the DX7DUMP problem discussed above, in the sense that if the data is not received correctly, and provided that you can tell that it wasn't, there is no real harm done apart from the inconvenience of organising a second attempt. The real subject matter of this article applies more importantly to point (c), because in this case you can't retry. If you lose some data then you've already blown it; you can't stop your performance (in front of all those thousands of fans, you know) saying 'whoops - hold on lads - lost a byte there - can we do that last bit again?'.
Before we spend time solving the problem, perhaps we should look at what pain it could cause us.
The extent of the pain depends on which byte you lose, the format of the data being received, and the reaction your particular synth has to the given situation. We need a little understanding of the MIDI protocol to see what is happening.
First off, have a look at the streams of bytes in Figure 1: note that '&' before a number indicates hexadecimal. Let's assume that Stream A is part of what we are supposed to receive from the synth via MIDI In. Having 'recorded' the data in the micro's memory, we can then 'replay' it by sending it back to the synth on MIDI Out. Those of you that are on form will realise that the timing associated with the data (ie. when the notes were actually played) also needs sorting out, but we'll ignore that for the sake of simplicity. For those not over-familiar with MIDI Protocol, Stream A tells the synth (or the micro) that: &90 - 'key on' information follows; &3C, &40 - the key middle C, whose number is &3C, should be struck with medium strength/velocity because the &40 is out of a possible &7F ie. use 64/127ths of full strength (&40 = 64 decimal, &7F = 127 decimal); &90 -'key on' again; &3C, &00 - turn middle C off since a 'key on' velocity of zero actually means turn the key off! Note that Stream A does not use Running Status where only the first &90 would have been required.
Moving on to Stream B, we see that the first &3C byte has been lost during reception. So what will happen when the micro sends the altered sequence back to the synth? Simple. The synth will see the &90 and expect a pair of bytes (key number and velocity) to follow. The &40 will be taken as the (wrong) key number to play, but since a new status byte (the second &90) is seen instead of the expected velocity byte, the synth will probably just ignore the misformed 'note'. So nothing too terrible has happened: one note of our magnum opus has been lost, but we'll live.
In Stream C the &00 byte is lost, which will certainly create trouble on most synths since, assuming the misformed 'pair' is ignored as above, the middle C note is now never turned off at all. If you're using a sound that sustains while a key is held down, such as an electric organ, you'll have a drone note continuing for ever. If too many notes are left on in this way, my DX7 gets a form of the hiccups and I have to turn it off and back on again to restore it to sanity. If, on the other hand, you're using a sound that decays (a piano, say), then you won't hear the drone, but one set of sound generators is still tied up forever!
If you MIDI 'record' and 'playback' some notes played on a Roland JX3P and lose the &00 byte on the way, the drone would be cut off because the JX3P sends an 'All Notes Off' message after every batch of completed notes, which turns off any drones on 'playback'.
Unfortunately, the DX7 ignores such messages.
Note that in Stream D, losing the second &90 byte does no harm, since MIDI Running Status allows the receiver to assume that the status continues to be that of the first &90. If the byte lost had been a new status we might have been in trouble. We might have ended up replaying pitch-bend information for a different channel as key on/key off information for the synth on MIDI Channel 1. I suppose it's one way to get into avant garde electronic music...
Finally, have a look at Figure 2, where Running Status is being used. Stream A might be several hundred bytes long, and after the first status byte, &90, consists of key number/velocity pairs (shown in Stream B) which start off by 'playing' middle C (&3C, &40) and then turning it off (&3C, &00) followed by the D above middle C being on (&3E, &40) and then off (&3E, &00), and so on, ad infinitum. Stream C shows the new stream formed when the first &00 byte is lost, and Stream D shows the new key number/velocity pairing.
We now have middle C on (&3C, &40) but never off, followed by middle C on again with slightly less velocity (&3C, &3E), followed by the E above middle C on (&40, &3E) but never off, followed by... well, the whole of your brilliantly played keyboard extravaganza using randomly pitched drone notes.
These problems are caused not because the micro isn't fast enough but because it simply didn't choose to look to see if a byte had arrived. 'Why does the micro choose not to look?', I hear you ask. Well, there are two distinct reasons.
The simpler of the two to understand is down to the programmer trying to organise the real time input. Once a byte has arrived, there's still a lot of work to do. The current byte must be put away in the correct place in the sequence of bytes being stored, and data pointers and counters must be kept up to date. And what about the legality of the current byte? - is it a status byte? - do we have a change of running status? - has the clock which is timing the arrival of these bytes ticked? - must a timemark be stored? - has the user pressed the abort button? - ... I could go on.
All of the above take time, naturally, and yet if this 'housekeeping' were not done, the MIDI In data bytes would be useless, so the programmer (and therefore the micro) 'chooses' not to be looking for a byte arriving all the time. Sometimes it just takes too much time, and when we check the 6850 ACIA we find we've had an Overrun error and have lost at least one byte.
The second reason will become apparent as we progress.
This article won't teach you everything you need to know about interrupts, but it should provide you with a common-sense understanding of what goes on. We will assume that the 6502 CPU in the BBC Micro is executing our real time input program, ie. doing the housekeeping already mentioned and expecting data bytes to arrive over MIDI In. How do we arrange for any bytes that arrive during the housekeeping to be kept safe until we can deal with them? Simple. We arrange for the housekeeping to be interrupted.
When a byte arrives in the 6850 ACIA from MIDI In, the 6850 NIRQ (Not Interrupt ReQuest) line is used to signal to the 6502, and to cut a long story short, the 6502 stops what it's doing - executing the housekeeping - and starts running another routine that we have previously set up. This routine grabs the byte from the ACIA, stores it safely; and then lets the 6502 continue with the housekeeping. In this way the byte is dealt with as soon as it arrives: the housekeeping routine can pick the byte up from storage some time later but the ACIA is already 'empty' and can safely accept the next byte.
So, interrupts allow us to do two things at the same time, since the housekeeping routine doesn't know that the interrupt has occurred or that the other routine has been executed. In fact, the BBC Micro makes extensive use of interrupts to perform a number of 'background' tasks keeping the clock pseudofunction TIME ticking along, for instance.
We can now talk about the second reason why a program might not choose to look to see if a byte has arrived over MIDI In.
Imagine that an interrupt occurs just after some other interrupt took place. If the first interrupt's routine has not yet finished, the 6502 stops executing this routine and hands control over to the routine for the second interrupt. When the second routine finishes, control is handed back to the first routine when it will continue from where it was interrupted and - with luck - finish and hand control back to the original program. Unless another interrupt occurs first...
It can be extremely inconvenient, not to say disastrous, to let an interrupt routine be interrupted in this way. However, if it does happen, we can prevent an interrupt being noticed by 'masking' it out, and we'll examine this process in more detail next month. Now, if an interrupt occurs just before our MIDI In interrupt, and the first routine masks out interrupts and then takes so long before enabling interrupts once more that the next MIDI In byte is overwritten (by the one following it) then we're in trouble yet again.
Since the BBC Micro makes extensive use of interrupts, bytes do occasionally get lost if we use maskable ones. However, the 6502 has a second sort of interrupt called 'NMI' ('Non Maskable Interrupt'). If you use NMI, your interrupt will definitely be dealt with immediately, though because of the potential for disruption (ie. the chances of crashing your programs if the thing is used incorrectly) presented by NMI, it should be used carefully. In fact, very little is said about its use - even in the Advanced User Guide - so read next month's article to find out what to do.
This non-maskable interrupt is accessible via the NNMI (or Not NMI) line on the BBC's 1 MHz bus. In order that those souls brave enough can use NMI, our BeeBMIDI interface PCB has been amended to allow the use of a wire link to specify which 1MHz bus interrupt line (NIRQ or NNMI) is connected to the Interrupt Request pin of the 6850 ACIA. Figures 3 and 4 show the relevant part of the PCB's copper side before and after amendment.
If you already have an original board, you can cut the original PCB track to the NIRQ line and solder a short link on the track side of the board to the NNMI pin (instead of the NIRQ one) to give the same effect - and since I'm still using the original original BeeBMIDI interface, that's just what I have had to do!
If you don't want to use interrupts, don't worry about the amendment to the board: don't make either of the links and it won't affect you.
This month was the theory. Next month sees the actual code to handle both IRQ and NMI interrupts and the necessary buffers, pointers, counters and communication with the main program. Don't miss it.
BeebMIDI 1 (E&MM June '84) contained the technological and constructional details of this MIDI Interface for the BBC Model B micro, Part 2 (E&MM July '84) continued with a full parts list and some MIDI software routines, while BeebMIDI 3 (E&MM Aug '84) featured a Patch Dump for the Yamaha DX7.
The PCB is available from EmmSoft, E&MM, (Contact Details), at £4.95 (inclusive of VAT and postage and packing).
Gear in this article:
Feature by Jay Chapman
Previous article in this issue:
Next article in this issue:
mu:zines is the result of thousands of hours of effort, and will require many thousands more going forward to reach our goals of getting all this content online.
If you value this resource, you can support this project - it really helps!