So this is something that I’ve always loved, loading data by tape. It’s incredibly clever, and really simple – while at the same time, being really complicated!
I guess everyone knows the basics of it, high and low sounds on tape, that correspond to a 0 or a 1. But how does it all actually work? Well lets look at the Spectrum loader as a broad stroke – while not getting down into actual z80. This overview should help understand the process, and why it is, how it is….
So above is the normal loading sequence. The first bit you get a sync tone, then you get a little loading of the header data. Next you get another sync tone, then loading of the actual program.
So first, what does the sync tone do? Well, this gets the program into the loop for actually loading. The tape loading looks for pulses of a certain size, and counts out a number of them. These tones will be reasonably large – much larger than data pulses. When it hits the minimum number of sync pulses, it decides we’re about to load some data and waits for those syncs tones to end.
So exactly how does it check for a sync tone? With a low tone being a 0, and a high tone being a 1. Here we can see a little mock-up of a tape loading header. At the start there is just noise, with pulses of different sizes, then the header starts and it settles down into an even pulse width.
The computer – lets assume the spectrum, monitors the EAR port and times the difference between when it’s a 0 (low tone) and when it’s a 1 (a high tone). If it is, then it’ll count how many pulses it finds so that it hits a minimum number of them at which point, it then decides it’s a proper sync header and then loops until it stops being this pulse size.
If while searching for that initial sync pulse it’s not the correct timing (with a little error margin), then it jumps back to looking. If it’s found pulses of the correct width, but not the correct number – it errors while counting, then again it’ll go back to looking and reset it’s counter.
Whenever the sync tone ends, then there may be a single pulse to separate the header from data, but that’s up to the load/save program. Now, in reality, this sync pulse may be quite wide, much wider than data pulses, but the width is again up to the program and what it thinks it can detect.
Next is the data – mainly the header. This is exactly the same as the pulse, except that it doesn’t need to “find” the pulses, it assumes the next one in, is the start of the data.
What we see here, are 2 pulses. A 0 and a 1. You’ll notice the second, a 1, is bigger, but why? Well, like the sync pulses, the program actually times the width of a pulse to determine if it’s a 0 or 1. It basically looks for a pulse width of a 1, but if it gets an edge too early, then it knows it’s read a 0 instead. So imagine the distance between a low and a high, is 100 ticks, it’ll be waiting for 100 ticks until it gets the next edge (that transition from a low to a high). If that edge comes early – in this case about 50, we know that’s a 0.
When it works out an actual 0 or 1, it then shifts this data into a byte. If we get a stream of 0 and 1s (like above), then we’ll start with %00000000, then we get the first 0, so our byte is shifted left by one (so still %0000000), then the the 0 is OR’d in. Of course you don’t need to actually set the zero, so we just shift and move onto the next pulse. It then detects a 1, so the %00000000 is again shifted left by one, and the 1 we just received is OR’d in giving us %00000001. If we get the same sequence again, then we shift our current value left by 1 again, giving us %00000010, and then as it’s a zero, we move to waiting on a pulse again. Once we know it’s a 1, we again shift out byte left by 1, giving us %00000100, and then OR in a 1, giving us %00000101.
This then continues for a whole byte, at which point we then save this byte into memory. When loading the header, the program will specify the number of bytes to load, and once it’s loaded that number, it’ll stop and return.
That header will store (in the case of the Spectrum), a filename, and the program length, and possibly the start address – though that can be overridden. It’ll then print that on screen as “Program: ??????” and then start the process again, looking for a sync, then the data – this time, trying to load in the number of bytes the header specified.
And that’s all there is to it. Very clever, and so simple. But how are turbos done? What about the C64 versions that play music etc?
Well, turbo’s on the spectrum are really simple.
Above is an old magazine listing from Popular Computing Weekly 17-23May, 1984 where this mystery was explained. Some smart cookie disassembled the Spectrum ROM routine that Loads/Saves, and simply halved all those loop counters. Yep, that’s all it is. When it was looking for a pulse width of 100 cycles, they reduced this to 50 (not actual numbers – see above). The original ZX Spectrum loading was 1500 baud, and this simple change sped up the loading routine to 3000 baud – exactly what Spectrum turbo loaders loaded at. Simple eh?
So what about the C64? Well, that wasn’t quite as simple. Tape routines on that were written from scratch, as the ROM routines were terrible, but the C64 had a secret weapon – it could generate an interrupt when it detected an “edge” on EAR port. That means when the pulse was low on the ear, it could generate an interrupt whenever that value on the EAR changed to a high (creating an edge). Using the hardware timers, you could setup that interrupt with a timer running looking for a 1, and again, if an interrupt was generated before that timer ran out – we had a 0. You then did all the same bit shifting and storing inside the IRQ routine, freeing up the CPU’s main loop to basically sit and do nothing.
You can see there are several ways you could count edges, and possibly even have it do the shifts for you. I never did this, I just did an IRQ based on the edge detection.
But this was still perfect, it meant the main CPU was sitting in a simple loop, waiting for the tape to finish loading. So… what could it do instead?
Well, how about play some music? Yep… this is how the CPU was able to play all that amazing loading music, having the interrupts jump in to load ever edge found, while the main loop (not under interrupt) played the music.
This was even extended in later years to play mini-games. I did a lovely little Pac-Man game that was almost the loading process for Blood Money, but I’d sped the tape loading up too much, and Psygnosis got nervous about all those returns, and I had to use a stock turbo loader – which was a shame.
But this interrupt driven tape loading meant you could leave the screen on – as the timers were hardware based and didn’t care about the screen delaying the CPU with it’s DMA access, and you could do some very cool little play things, instead of a boring blank screen.
I don’t know about anyone else, but I’d often load Delta and stop the tape, causing a loading error, but letting me play with the amazing Rob Hubbard Mix-e-load program, where you could make up your own tunes. I loved this.
Anyway, I hope you enjoyed this little diversion…. I love tape loaders, they are very clever, and you can do some super cool things with them.