
Stills from The Red Square, animation on Sinclair ZX Spectrum (2006) (2 minutes)



Stills from Alpha Omega, animation on Sinclair ZX Spectrum (2009) (1 minute, 50 seconds).
Alpha Omega began with another animation and drawing entitled 'Barmouth, Portstewart, 2002' which itself was a response to a recurring dream. I come from Ireland but live in England, and for years my home in dreams was situated somewhere between the two - on board the ferry forever and ever, stopping only in the middle of the night to pick up passengers, others adrift, in need of rescue, and no one ever disembarked.

The dream occurred so often I wanted to capture the images within it. This began an interest in pixellating the sea and sky. In seeking how to best represent these within the confines of 256 x 192 pixels, I became interested in a plethora of mathematical concepts. My knowledge is still embarrassingly shallow but my interest is keen and passion resolute.
These plethora of concepts fall under the heading of algorithmic information theory, a term associated with Gregory Chaitin of the IBM Thomas J. Watson Research Center. While still a teenager Chaitin discovered the Omega number which, in short, is the halting probability of a randomly generated computer program. His work is a continuation of Godel's and Turing's.

Algorithmic information theory sees the universe as a quantum computer processing itself as information. Temperature, for instance, is a measure of information. The number of bits needed to describe the regular structure of ice is relatively small compared with the number of bits needed to describe the chaotic movements of the molecules in steam. If the second law of thermodynamics holds true then the universe will approach a state described by 2^∞ bits.

Frank Tipler's Omega Point theory states that the universe's computation power will become infinite as it collapses into singularity. By this stage our descendants will have harnessed the quantum computer nature of the universe, and will be able to run artificial intelligence versions of everyone forever. This is where algorithmic information theory becomes quasi-religious. Tipler's Omega Point theory has its origins in the writings of Pierre Teilhard de Chardin (1881-1955), Jesuit priest and scientist, who envisioned a future of self-conscious and reflective unity beyond animalistic being in what he called a noosphere.

I should point out that at no time have I heard or read any mention of Omega Point theory by Chaitin who describes himself primarily as a computer programmer and then as a meta-mathematician. There is, however, a connection beyond the use of the word Omega. The Omega number is theoretical, its absolute value unknown. The problem being that the closer the calculations are to its answer, the longer it takes to calculate. In fact it will take infinitely long to find its value. However, as with Turing's uncomputable real numbers, Tipler's infinitely large expansion of the universe's computing power allows these calculations to be made.
Omega Point theory is of course wildly speculative. Despite Tipler's claims of its proof in physics, it still reads like a religious prophesy of technological determinism. Surprisingly for me, I find it easier to consume Teilhard's version as something which is to be striven for and spiritually dedicated to. In this animation, Alpha Omega, it is the audacity within Tipler's theory which is being satirised. It is, admittedly, audacious to create animation on such an apparently redundant machine as the ZX Spectrum, but, to raise the spectre of my pretension, learning machine code programming over the last seven years has involved spiritual dedication.


Compression plays a central role in algorithmic information theory. To compress information is to enact an understanding of it. The compression algorithm is that understanding.
Gregory Chaitin :
I think of a scientific theory as a binary computer program for calculating the observations, which are also written in binary. And you have a law of nature if there is compression, if the experimental data is compressed into a computer program that has a smaller number of bits than are in the data that it explains. The greater the degree of compression, the better the law, the more you understand the data.
But if the experimental data cannot be compressed, if the smallest program for calculating it is just as large as it is... then the data is lawless, unstructured, patternless, not amenable to scientific study, incomprehensible. In a word, random, irreducible!

Gregory Chaitin :
Compression techniques are useless if they are applied to noise, to the mad jumble that you get if an antenna is disconnected, because there is absolutely no pattern to compress away.
Another way to put it is that the most informative picture is one in which each pixel is a complete surprise.

Above is some further evidence of the Spectrum running the animation. The BASIC program which you can see runs two files of machine code. Each one has a sequencer routine which runs smaller sequences of even smaller routines embedded with calls to other even smaller routines, and scattered amongst this hodge-podge of code are the bitmaps. Each time an animation is made new routines are designed which are then re-used in a library for the next project resulting in the bottom of the code looking relatively elegant, but then the rest becomes spaghetti of increasing complexity towards the top. If ever an animation is made of complete code elegance it would probably be the last one I would want to make. It would signify that there wasn't anything new to learn.

You can tell from the clarity of the stills that the animation isn't just being run on a Spectrum but, instead, it is an emulation. Actually very accurate to the real thing but it allows me to work on a laptop with all the benefits, i.e. fitting the work around a busy life, grabbing bits of time in a cafe before the day job and in a library at lunchtime. No nervous tension during cassette loading! Having spent the last 7 years using the old method on the real machine, an emulation has been a liberation. Besides, the idea of running one system inside another is appealing. It is very Omega Point.
QUDS
Quds is a development of Uds. Like its predecessor it uses 8-bit values as wavelengths to create sound. The Q in Quds is for seQuential. Unlike its predecessor, the sound it plays will, within a margin of drift, be the same length in time no matter the data being read, so it can be used as the basis of a sound sequencer.
Where Uds finds a zero, it will halt. If Quds finds a zero it will return to the beginning of the data and keep going. So when does it stop? Two variables, initially one a copy of the other, are used to determine duration; Total and Current Total. A typical value for these would be 24*256. This is the sum total of the wavelengths put through the Quds sound engine before it terminates.
There are two benefits from using Quds instead of Uds. Firstly is the independently-determined duration and secondly, it does not have to rely on large samples of data to sculpt sound. In fact it does not need any devoted Udata information. Quds can be pointed at any part of memory and interesting sound will emerge. However, WARNING! Remember that where Quds finds a zero it will loop. So if the FIRST piece of data is zero, then it will loop indefinitely as it subtracts nothing from the duration limit.
So firstly, here is the Z80 assembly code and its opcodes in decimal. Preceding the routine are the two variables Total and Current total. They are referred to in the routine by their low and high-order byte addresses as totL, totH, curtL and curtH. The address for qudata (the address of the sound data) is inserted at quds+1 (low-order byte) and quds+2 (high-order byte). At quds+10 is something called stretch with a default of 1, its shortest value. Increasing this number (POKE quds+10, 4 for instance) will stretch the sound, but will also increase the inaccuracies in the timing. Another variable hidden in the routine is at quds+72. It has a default of 5 for tuning balance but it could be adjusted for serious detuning of the sound. If you are not interested in assembly, don't worry about this listing, further below is the BASIC code for compiling the routine, creating a sound and a way of triggering it.
Total : 0 24
Current total: 0 24
Quds: LD HL, udata 33 qudataL qudataH
XOR A 175
LD A, (HL) 126
OR 0 246 0
JRZ -9 40 247
LD BC, stretch 1 1 0
PUSH BC 197
LD B, 0 6 0
LD C, (HL) 78
PUSH HL 229
LD HL, (current total) 237 107 curtL curtH
SBC HL, BC 237 66
JPP quds+57 242 targL targH
LD A, L 125
NEG 237 68
LD B, 0 6 0
LD C, A 79
PUSH BC 197
LD BC, 9 1 9 0
DEC BC 11
LD A, C 121
OR B 176
JRNZ -5 32 251
POP BC 193
DEC BC 11
LD A, C 121
OR B 176
JRNZ -15 32 241
LD HL, (total) 237 107 totL totH
LD (current total), HL 34 curtL curtH
POP HL 225
POP BC 193
RET 201
LD (current total), HL 34 curtL curtH
POP HL 225
PUSH BC 197
LD A, 16 62 16
OUT (254), A 211 254
DEC BC 11
LD A, C 121
OR B 176
JRNZ -5 32 251
LD BC, 5 1 5 0
DEC BC 11
LD A, C 121
OR B 176
JRNZ -5 32 251
LD A, 7 62 7
OUT (254), A 211 254
POP BC 193
DEC BC 11
LD A, C 121
OR B 176
JRNZ -5 32 251
POP BC 193
DEC BC 11
LD A, C 121
OR B 176
JRNZ -83 32 173
INC HL 35
JR -95 24 161
BASIC Compiler of Quds
10 LET quds = 32772
20 LET qudata = quds + 256
30 LET qudataH = INT (qudata / 256)
40 LET qudataL = qudata - (qudataH * 256)
50 LET target = quds + 57
60 LET targH = INT (target / 256)
70 LET targL = target - (targH * 256)
80 LET tot = quds - 4
90 LET totH = INT (totH / 256)
100 LET totL = tot - (totH * 256)
110 LET curt = quds - 2
120 LET curtH = INT (curt / 256)
130 LET curtL = curt - (curt * 256)
140 LET a = quds - 4
150 RESTORE 220
160 READ x
170 IF x = -1 THEN GOTO 500
180 POKE a, x
190 LET a = a + 1
200 GOTO 150
210 REM ____Variables
220 DATA 0, 24, 0, 24
230 REM ____Quds
240 DATA 33, qudataL, qudataH
250 DATA 175, 126, 246, 0, 40, 247
260 DATA 1, 1, 0, 197
270 DATA 6, 0, 78
280 DATA 229
290 DATA 237, 107, curtL, curtH
300 DATA 237, 66
310 DATA 242, targL, targH
320 DATA 125, 237, 68, 6, 0, 79, 197
330 DATA 1, 9, 0, 11, 121, 176, 32, 251
340 DATA 193, 11, 121, 176, 32, 241
350 DATA 237, 107, totL, totH
360 DATA 34, curtL, curtH
370 DATA 225, 193, 201
380 DATA 34, curtL, curtH
390 DATA 225
400 DATA 197
410 DATA 62, 16, 211, 254
420 DATA 11, 121, 176, 32, 251
430 DATA 1, 5, 0, 11, 121, 176, 32, 251
440 DATA 62, 7, 211, 254
450 DATA 193, 11, 121, 176, 32, 251
460 DATA 193, 11, 121, 176, 32, 173
470 DATA 35
480 DATA 24, 161
490 DATA -1
500 REM _______Sound Generator
510 RESTORE 580
520 LET a = qudata
530 READ x
540 IF x = -1 THEN POKE a, 0: GOTO 640
550 POKE a, x
560 LET a = a + 1
570 GOTO 530
580 DATA 1, 2, 3, 4, 5, 6, 7, 8, 9
590 DATA 32, 34, 32, 30, 64
600 DATA 32, 34, 32, 30, 60
610 DATA 32, 34, 32, 30, 64
620 DATA 32, 34, 32, 30, 68
630 DATA -1
640 REM _______Sound Trigger
650 FOR m = 5 TO 45 STEP 10
660 POKE quds + 72, m
670 FOR n = 1 TO 4
680 POKE quds + 10, n
690 RANDOMIZE USR quds
700 NEXT n
710 NEXT m
720 GOTO 650
Sound Generation (lines 500 to 630)
This is where a demonstration sound is created by reading the data values held in lines 580 to 620 and poking them into memory at the address previously defined as qudata at line 20. This method of shaping the sound, wavelength by wavelength, is the most detailed and it is usual to instead use an equation for drum sounds, but the method shown hear is the most transparent. Experimenting by changing the values held after DATA will give a better understanding of how the whole routine works. It doesn't matter how many or how few values you choose to put here as long as there is at least one and the data has -1 at the end. Notice also that when the sound is triggered, you can actually see the wavelengths in the border region of the screen. This is because port 254 on the ZX Spectrum is connected to both to the sound output and the border.
Sound Triggering (lines 640 to 720)
Here the code is demonstrating the use of the two variables of detune (quds+72) and stretch (quds+10), but the sound needs only to be triggered by RANDOMIZE USR quds.
Featured in the August 2008 issue of Computer Music magazine is an article on lo-fi production in which I give a tip for creating sound on a ZX Spectrum. As it is written in Z80 assembly, I thought this blog might give further enlightenment, and some workable examples in BASIC and machine code.
Below is a BASIC program written for a ZX Spectrum. It compiles a machine code routine called "uds" which is short for User-Defined Soundreader. Its main purpose is to play sound effects.
The lines beginning with the BASIC command DATA hold the machine code opcodes and data for this routine.
Lines 530 to 600 are where the sound effect is created. Here this is given as an example, but really any data in any part of the memory will do. The sound stops being read when uds finds a zero.
At line 10 uds is positioned into memory at address 32768 which might seem like a strange number, but it is exactly half way through the total machine memory, and begins to make more sense if viewed as hexadecimal or as higher and lower order bytes in decimal - 128, 0, i.e. 128 * 256 + 0 = 32768. Perhaps this is running before walking. The point being that you can change this number, but I would recommend that if you do, make it higher so that it doesn't clash with any BASIC program.
Line 20 defines where this particular sound effect is placed in memory, again it could be anywhere, but here its "a bit further on from uds", and the address is called udata.
Line 50 defines the variable "duration" with a default of 1. The sound can be stretched by increasing this number up to a maximum of 255. But it will lose its identity pretty quickly, and I usually don't go beyond 16.
Line 320 contains the number 8. This value I refer to as the inner wheel tuning and it is meant to balance the sound. Again this value can be increased up to 255 to lower the overall tuning.
310 and 440 contain the value 251. It can be changed to a value between and including 244 to 251. I think of this as being an octave ladder. 244 being the lowest.
After the routine is compiled and the sound effect created, it will be triggered and then pause until you press the keyboard, then triggered again.
Well that's all for now, folks. I'll update this blog with further explanations, examples and links.
10 LET uds = 32768
20 LET udata = uds + 256
30 LET udataH = INT (udata / 256)
40 LET udataL = udata - (256 * udataH)
50 LET duration = 1
60 LET a = uds
70 RESTORE 140
80 READ x
90 IF x = -1 THEN CLS: PRINT a, a - 32768: GOTO 530
100 POKE a, x
110 LET a = a + 1
120 GOTO 80
130 REM _______uds
140 DATA 33, udataL, udataH
150 DATA 175
160 DATA 126
170 DATA 246, 0
180 DATA 32, 1
190 DATA 201
200 DATA 1, duration, 0
210 DATA 197
220 DATA 6, 0
230 DATA 78
240 DATA 197
250 DATA 62, 16
260 DATA 211, 254
270 DATA 0, 0, 0, 0, 0, 0, 0
280 DATA 11
290 DATA 121
300 DATA 176
310 DATA 32, 251
320 DATA 1, 8, 0
330 DATA 11
340 DATA 121
350 DATA 176
360 DATA 32, 251
370 DATA 62, 7
380 DATA 211, 254
390 DATA 193
400 DATA 0, 0, 0, 0, 0, 0, 0
410 DATA 11
420 DATA 121
430 DATA 176
440 DATA 32, 251
450 DATA 193
460 DATA 11
470 DATA 121
480 DATA 176
490 DATA 32, 204
500 DATA 35
510 DATA 24, 191
520 DATA -1
530 REM ___________udata
540 LET length = 254
550 FOR n=0 TO length STEP 2
560 LET ang = (n / length)*360
570 POKE udata + n, n + 1
580 POKE udata + n + 1, 128 + 32 * SIN (ang * PI / 45)
590 NEXT n
600 POKE udata + 255, 0
610 REM __________trigger
620 RANDOMIZE USR uds
630 PAUSE 0
640 GOTO 620