I just rediscovered my first computer, a Sinclair ZX Spectrum (good picture) which I bought back in 1983 or 1984 (I have no trace of the exact date, unfortunately). The machine was a perfect machine to learn programming on in my opinion, consisting of little more than a Z80 processor with memory, bit-mapped display (with a famously odd-ball addressing scheme and color handling) and ultra-simple sound output and input. Most of my friends in the end bought Commodore C64 machines, which had more powerful graphics and sound hardware, but a processor that was much less fun to program.
The Spectrum came with a built-in BASIC interpreter that are up the bottom 16kB of the 64kB addressing space. The BASIC was actually fairly powerful and easy-to-use, and included a very fun programming textbook. I just reread that textbook, and it is quite strikingly well-written and manages to cover both basic computer-science-style programming and deep close-to-the-machine and real-time programming in a compact 150 pages. There is no credit to a particular author in the book I have (Swedish translation by a group of people at Ord & Form here in Uppsala), but an online scan credits Steven Vickers.
Overall, the book does a very good job of explaining both the Sinclair BASIC interpreter, as well as some general programming concepts. And how the machine works deep down. Here are some highlights.
The way that functions are explained made me laugh out loud (quoting the original English according to the online resource I found):
Consider the sausage machine. You put a lump of meat in at one end, turn a handle, and out comes a sausage at the other end. A lump of pork gives a pork sausage, a lump of fish gives a fish sausage, and a load of beef a beef sausage.
Functions are practically indistinguishable from sausage machines but there is a difference: they work on numbers and strings instead of meat. You supply one value (called the argument), mince it up by doing some calculations on it, and eventually get another value, the result.
This is a very good analogy to introduce functions, in a fairly gender-neutral way and assuming no mathematical background on part of the user. I wonder if I ever cared to define any functions… as I recall it, GOSUB was as far as I got in sophistication before leaving BASIC and starting writing everything in Z80 assembler.
Reading a Changing Counter
Later on in the book, a suggestion is made to get a better time counter. Essentially, by reading a 24-bit value incrementing at 50 Hz, using three separate PEEK commands:
(65536*PEEK 23674+256*PEEK 23673+PEEK 23672)/50
This code does subject you to the issue of reading during an update and counter wrap from one byte to the next. This complication is cheerfully noted after a complete program using this feature as a by-the-way-the-code-is-correct by a small loop. There are many such small gems in the book, essential tricks and caveats that still apply to programming close to the hardware and doing real-time programming.
We have No Clock
The ZX Spectrum, as most computers of its day, did not have a real-time clock on board. Instead, you had to time things using the 50Hz screen refresh interrupt (which is what the code in previous section reads) or by just adjusting your code to wait appropriately. One example program is supposed to show a clock updating every second. However, getting to a second is a bit tricky when there is no firm time-base to work on. The suggesting instead is this:
This clock will run down after about 55.5 hours because of line 60, but you can easily make it run longer. Note how the timing is controlled by line 210. You might expect PAUSE 50 to make it tick one a second, but the computing takes a bit of time as well and has to be allowed for. This is best done by trial and error, timing the computer clock against a real one, and adjusting line 210 until they agree. (You can’t do this very accurately; an adjustment of one frame in one second is 2% or half an hour in a day.)
So what we have here is a classic “how long must I wait to get a steady pace of execution for my periodic code?” issue familiar to anyone using an RTOS and trying to determine how long to sleep a process for until the next periodic interval starts. Once again, illustrated in ten lines of BASIC and a few lines of text.
Repeatability and Randomness
In Chapter 11, randomness using the built-in random function is introduced. This was a 16-bit periodic pseudo-random system where you could set the random seed to put start the random generation at any particular point in a 100% repeatable manner. Indeed, the book introduces the concept of doing this in order to ensure repeatable execution of a program relying on randomness. This sounds familiar from my previous post on simulation determinism:
If you had a program with RND in it and it also had some mistakes that you had not found, then it would help to use RANDOMIZE like this so that the program behaved the same way each time you ran it.
Delving into Details
The BASIC manual does not try to hide how the BASIC interpreter works internally, rather on the contrary, including a list of all system variables so that they can be POKEd and changed if need be. It also explains the data layout of all variable types (the Spectrum BASIC used 32-bit integers, impressive for an 8-bit machine, and 40-bit floating point numbers).
The only useful information for me in the way I used the machine as a pure assembly-language target was the memory layout of the screen. Which is bizarre to say the least. It had a separate black-and-white bitmap and color overlay for every 8×8 character. And each scan line was NOT consecutive with the previous in memory, but rather offset by 256 bytes. I think the idea was to make it easy to insert a single character into the bitmap by just incrementing the high byte of the address by one for each pixel line.
To make that work, the screen was divided into three pieces of 8 character lines/2048 bytes each (8 character lines of 8 pixel lines each, each with 32×8 bits of data). The sprite drawing routines that would work with that to plot a 16×16 sprite in any screen position was amazing code, to say the least. I independently invented unrolled loops to make this run fast. And misused the POP instruction to pull in data to draw (it took 11 cycles, rather than 12 as a more usual register-indirect load would do) — provided that you turned off interrupts or data corruption would ensue.
These were fun programming times. It shaped my programming habits for a decade, culminating in an infamous 68000 assembler hack in my first undergrad year at Uppsala, with some 50 pages of very tight and complicated assembly language making use of most features of the 68k instruction set… poor TA.
On it goes like that. The chapters are short but very much to the point, and a large number of fairly subtle concepts are introduced. The Spectrum was a good school for a budding programmer, with no compiler, no safety net, but also no way to break the machine in a way that a reboot would not cure.
I actually recommend seeking out the aforementioned online repository for the Spectrum programming book, it is almost still in shape to be used as an introduction to real-time and embedded programming.
I am currently trying to figure out what is a good ZX Spectrum emulator to relive these old times if I can find time. The hardware unit I have depends on an analog tape drive to load software… and I gave up analog sound back in 1993. As if the magnetic tapes which have been in storage for 20 years have any chance of containing useful data anyway.