Archive for the ‘computer’ Category

GPL v3 loophole

Thursday, July 31st, 2008

For the most part I like the v3 GPL - it makes some things clearer and closes a few loopholes that allow people to violate the spirit of GPLv2 while sticking to the letter of it.

The one part I don't like is the anti-Tivoization clause. Not because I think it's draconian, but because I think that it is ineffective. Suppose you are Tivo and you want your users to only be able to use your signed version of some particular piece of GPLv3 software on it. All you have to do is split into two companies (Tivo Hardware and Tivo Software).

Tivo Hardware doesn't distribute any GPLv3 code at all - it just ships boxes which download software from some internet site, check that it is signed correctly and then install it. Tivo Hardware is therefore not bound by GPLv3. Tivo Software just modifies GPLv3 software they way they want to, signs it it with some private key and then uploads to some internet site (the same one the Tivo boxes happen to download from). Tivo Software are not "conveying an object code with or specifically for use in a User Product", they're just distributing signed code as many (if not all) GNU/Linux distributions do. That it happens to be the same key that Tivo Hardware boxes accept is irrelevant - that's not Tivo Software's fault, Tivo Hardware could do that with anyone's public key.

If there was a way that the Tivoization clause could work, someone could really mess with the FSF by releasing a piece of hardware that didn't include any GNU software but would only run GNU software signed by the FSF.

Given that this clause is so easily circumvented it might as well not be there to simplify the license. While I appreciate what the FSF are trying to do with this clause I don't think there is any way to make it work reliably without being overly draconian. The "distribution to business" compromise is also weird and could possibly be thought of as a "code smell".

The other minor gripe I have with GPLv3 is that it is much longer and more difficult to understand than GPLv2. I guess that's an inevitable (and unfortunate) consequence of making it more lawyer-proof, though.

Destructors

Sunday, July 27th, 2008

I think that of all the features of C++, destructors are probably my favorite. They seem simple enough (code that is run when an object is destroyed, ensuring resources are cleaned up) but when you look into the consequences of that, they change the entire game and mean that C++, while it looks at first glance quite similar to C, is a very different language altogether.

RAII gets talked about a lot but the real meat of it is RRID - the destruction part.

For one thing, they mean that some things can now happen implicitly, rather than having to be explicitly coded. That's a good thing - it means the higher-level code can be terser and terser code is less likely to contain bugs. A common complaint of C programmers learning C++ for the first time is that they can't see exactly what's going on by looking at the code any more - but what they haven't yet realized is that that is a good thing.

For another thing, they make exceptions possible, with all the benefits they bring, and without the overhead, non-orthogonality and non-determinism of garbage collection.

Because destructors are not like normal functions, there are some things that you should not do with them (like throw exceptions from them). One way to think about this is that normal functions (including constructors) do "forward" work and destructors to "reverse" work (undoing things, releasing resources etc.). The reverse work should never be able to fail because that means that neither forward progress nor reverse progress can be made - it means the program is irretrievably broken and must be terminated immediately. Hacks like uncaught_exception() should not be used - if you're tempted to use it is a sign that you're trying to make forward progress in a destructor and that you should re-think your design.

Another way of writing "reverse direction" code in some languages is "finally". It's too bad that C++ doesn't have this (you either have to catch and rethrow or write a special object) - there are occasions when you want to do some one-off cleanup and don't want to have to write a whole class just to get a destructor, and the "finally" notation makes it clearer than the catch/rethrow notation. I wouldn't give up destructors for finally, though, as some languages do.

Homogeneous computer

Saturday, July 26th, 2008

I was thinking about FPGAs recently, and that got me wondering about the possibility of designing a computer architecture that is completely homogeneous - that is, composed of a number of identical cells arranged in a square grid. Some of the cells (presumably those on the edges or even just a corner or two) would be connected to IO devices (perhaps directly, perhaps via a bus for a more general machine).

Each cell would have some kind of logic and a small amount of memory (enough to remember how it is connected to adjacent cells and to use as actual user memory).

We would like to be able to have different pieces of the machine doing different things at the same time, so cells should be independent (except where we explicitly connect it to an adjacent cell).

We also want to be able to have the hardware reconfigure itself (so while group-of-cells-A is doing something else, group-of-cells-B can be reprogramming group-of-cells-C). In order to do this, we need to make it possible for each cell to be able to address any other cell that its connected to.

If we have some string of cells connected together in a line, how do we differentiate between data sent all the way to the end of the line and programming instructions sent to a particular cell somewhere along the line? For a chip with 2n cells we need to have at least n+1 bits connecting each cell to its neighbours - n data bits (x) and one "program" bit (p). If a cell in "interconnect" mode sees an input of (p,x)==(0,x) then it passes (0,x) out the other side. If it sees (1,x>0) then it passes (1,x-1) out and if it sees (1,0) then it is reprogrammed. I guess we'd also need some additional data bits to tell the cell what mode it should be in after reprogramming. A cell with 4m input bits and m output bits has requires 24mm bits to completely describe its truth table and this is clearly going to be larger than m so obviously we can't program it with any arbitrary truth table in one go - we'd either have to upload it in pieces or limit the possible cell modes to some useful subset of the possible truth tables.

This is probably an incredibly inefficient way to build a computer, since for any given state a large number of the cells are likely to be acting as just interconnects or just memory and only using a tiny proportion of their capacity. But having the cells be identical might mean that doesn't matter so much if we can build a very large number of them very cheaply.

A variation on this architecture might be to have the cells arranged three-dimensionally in a cube rather than than on a flat 2D surface. Current photolithographic fabrication technologies aren't quite up to this yet (the fattest chips are only maybe 10 layers thick) but I expect that the nanoassembly and microfluidic channels for integrated cooling that will be required to make fully 3D chips aren't too far off. When that happens the chips will be so complex that we'll have to use concepts like repetition and fractal-like scale invariance to a greater extent to make their designs tractable. In other words, chips will start to look more biological - more like living tissue. Perhaps a more homogeneous computer design will come into its own in that era.

Concrete data type

Friday, July 25th, 2008

A handy thing to have around (and something I want to put into the UnityALFE language) is a data type for representing physical quantities. This keeps track of a real number plus powers of metres, seconds, kilograms, amps, Kelvins (and maybe others, including user-defined ones). Two values of type Concrete can always be multiplied or divided but can only be added or subtracted if their dimensions match, and can only be converted to other types if they are dimensionless.

Common units of measurement and physical constants can be given as constants. Because the units are tracked automatically you can do things like:

(2*metre+5*inch)/metre

and get the right answer.

Usually the compiler should be able to check the dimensions at compile time and elide them like other type information, or give a compile error for expressions like:

(2*metre+5)/metre

Along similar lines, the log() function could be considered to return a value of type Concrete with some special non-zero dimension. Then you can (and indeed must) specify to which base the logarithm should be by dividing by another logarithm (e.g. log(x)/log(e), log(x)/log(10) or log(x)/log(2)). This syntax is rather more verbose than the usual method of having separate functions for common bases (log(), lg(), ln() etc.) but I find that this is more than made up for by the fact that one doesn't have to remember which function corresponds to which base - it's self-describing.

Another useful type for scientific/engineering work would be a value with confidence interval (e.g. 5±1 meaning "distributed normally with a mean of 5 and a standard deviation of 1"). There are well-defined rules for doing arithmetic with these. A generalization of this to other distribution functions might also be useful.

Twenty years of programming

Thursday, July 24th, 2008

I recently came across an archive of old files that I hadn't looked at in a while, and found what I think are the first programs I ever wrote. In particular, the oldest program I can find that I'm sure I wrote all by myself (that wasn't written by a relative or typed in from a book) is dated 24th July 1988, 5:23pm. It's possible that I started it earlier than that or that other programs written at the same time predate it - the only timestamp supported by the DOS filing system was "last modified".

"More or Less" was written in Locomotive BASIC2 which ran under GEM on our Amstrad PC1512. GEM and BASIC2 (and therefore "More or Less") can still be run on a modern PC with the help of the instructions here and DOSBox (when run under Windows XP the instruction pointer seems to end up pointing to non-code data for some reason, and Vista graphics drivers don't support graphical DOS programs).

"More or Less" is a rough clone of a piece of educational software of the same name that ran on the BBC micros at school. I found a copy of the original - the copyright information says:
MLESS / More or Less
Microelectronics Education Programme
MICROPRIMER Software Pack 4
(c) CET 1982 /ISBN 0-86184-085-2
Program idea: A Straker, I Drysdale, A J Curtis
Programmed at C.E.C.C.
Editor: Bob Coates & Five Ways Software
Version 1.0 / 1 November 1982
Works with BBC Micro 32k (480Z and SPECTRUM versions also available)

The program displays two numbers and you have to say whether the left one is more than, less than or the same as the right one.

Not particularly challenging (even for 9 year olds) but I think the BBC version at least kept track of how long you took. I intended to add more features but (like so many of my programs since) I never got around to finishing it.

Another interesting feature of the BBC version was the intro/title screen. This printed "More or Less" in letters that grew and shrunk in height so that the line formed by the tops of the letters bulged in and out:

(kind of a primitive version of texture mapping I suppose). I wanted to reproduce this effect, but my programming skills were not up to it at the time. But I recall being quite proud of what I came up with instead - the title was centered, printed in a large font, double-spaced and the letters appeared one by one, each one accompanied by a beep (without a third party driver that I wouldn't come across for several years, BASIC2 was only capable of generating that one sound programmatically).

My version did have a few interesting features - it asked for the player's name and addressed him/her by name for right and wrong answers. The program maintained a table of scores in a disk file (not sorted, though, and not necessarily "high scores" - I didn't know about arrays at that point).

I'm sure I had written some other programs around the same time or earlier, but I guess I wasn't sufficiently proud of them to save them. In my last year at Goring primary school (which ended around the same time I wrote "More or Less") I also wrote some Logo programs to draw letters of the alphabet with turtle graphics. These were not particularly sophisticated (just sequences of forward and turn instructions, plus loops for the curved bits) but were probably the first actual programs I wrote. I don't have any record of them, though.

My first made-up language

Wednesday, July 23rd, 2008

Years ago (more than a dozen), I tried to write (in C) an interpreter/compiler for a dialect of BASIC that I made up. It was based on BBC BASIC and the BBC's graphics facilities (which I was very familiar with at the time) but it would have run on the PC and had some additional features:

  • Operators and control structures from C
  • More string and array processing facilities
  • More graphics commands
  • Interactive editor and debugger
  • Commands for accessing PC facilities (interrupts, calling native code etc.)
  • Built-in help
  • Facilities for storing chunks of code as a library
  • Complex numbers
  • Self-modification and introspection facilities

The last of these is what I want to write about today.

As a child I used many of the 8-bit BASIC variants which had line numbers and it always irked me that there were some things you could do in immediate mode that you couldn't do from a running program, such as editing the program. Why was it that typing:

PRINT "HELLO"

printed "HELLO" immediately and:

10 PRINT "HELLO"

printed "HELLO" when the program was run but

10 10 PRINT "HELLO"

didn't create a program that replaced itself with the program '10 PRINT "HELLO"' when it was run? While doing so didn't seem terribly useful it seemed to me that an interpreter would be just as easy (if not easier) to write with this facility than without it, and that it was an unnatural ommission.

Along similar lines, my dialect had an "INTERPRET" command which took a string and ran it as BASIC code and a "PROGRAM$" array which contained the lines of the program.

I got some way in to writing a parser for it but as I didn't know how to write a parser back then I got horribly tangled trying to write code for each possible combination of current state and next piece of input.

The similarities between this and the UnityALFE language that I'm in the (very slow and gradual) process of writing haven't escaped me.

Modular emulator

Monday, July 14th, 2008

Among the many programs I'd like to write is a replacement for MESS (and possibly also MAME while I'm at it). Don't get me wrong, I think these are fantastic pieces of software but I do have some problems with them that can really only be solved by starting my own emulation project rather than by contributing to them. In no particular order:

  • I like writing software from scratch.
  • They are written in C with all kinds of macro trickery to make it more object orientated. I'd rather write it in C++ or some language of my own devising.
  • I don't like the unskippable startup screens that MAME and MESS use. I'd like to set up a PC emulator using a free clone BIOS and DOS and distribute it as a turnkey method for running old games like Digger.
  • I'd like to make it possible to "build" emulated machines at run time (without having to create a driver etc.). You'd be able to say "connect up this CPU, this sound hardware, this video hardware and load this ROM at this address" and it would all work. The emulator would come with a set of pre-written modules, it would have a language for designing modules and plugging them together and possibly even a graphical designer for wiring things up.
  • MAME and MESS timeslice much more coarsely than I'd like to. They emulate the CPU for a while (until a new frame or an interrupt starts usually) then see what the screen has done in that time, what sound was output in that time and so on. I'd like to timeslice on a cycle by cycle basis for more accurate emulation (so raster effects can be done with horizontal pixel accuracy) and to enable emulation of things like the prefetch cache on the 8088 (the lack of which makes MESS about 30% too fast).This sounds like it would make emulation very slow, but in fact if we organized it well and all the code fits into the CPU cache, we'd be doing no more work than MESS is now.

Generations of screen sizes

Sunday, July 6th, 2008

If you make a list of common screen resolutions and plot them on a log/log scale, you will notice that a regular pattern emerges - given an x by y resolution, x*2 by y*2 also tends to be common. However the sizes are not evenly distributed throughout logarithmic spaces - there are gaps which suggest different "generations" of monitor sizes, each monitor having 4 times the number of pixels as the corresponding monitor in the previous generation. I suspect this is because of the influence of TV - the resolutions are clustered around powers of 2 of TV resolutions.

Generation 1 - 256-480 pixels wide, 180-300 pixels tall (non-interlaced TV, handhelds)
Generation 2 - 512-960 pixels wide, 360-600 pixels tall (interlaced TV, older computer monitors)
Generation 3 - 1024-1920 pixels wide, 720-1200 pixels tall (most current computer monitors)
Generation 4 - 2048-3840 pixels wide, 1440-2400 pixels tall ("Q" standards - very high end monitors)
Generation 5 - 4096-7680 pixels wide, 2880-4800 pixels tall ("H" standards - no monitors exist yet)

Screen aspect ratios as musical notes

Saturday, July 5th, 2008

Aspect ratios of televisions and computer monitors tend to be ratios of small integers. On my desk right now I have 3 monitors that are 4:3 and one that is 8:5.

Another thing involving ratios of small integers is musical intervals in Just Intonation.

C 1:1
C# 16:15
D 9:8
D# 6:5
E 5:4 5:4 monitors exist but are not common
F 4:3 the most common non-widescreen aspect ratio
G 3:2 3:2 monitors exist but are not common
G# 8:5 the most common computer widescreen format
A 5:3 5:3 monitors exist but are not common
A# 16:9 HDTV format
B 15:8

Lispy composable compiler

Friday, July 4th, 2008

When I finally write the compiler for my super-language, I want to make it nice and modular - i.e. make it very easy to compile different languages, target different architectures and include different optimizations.

In order to achieve this I will need to have some kind of intermediate form in which pieces of code in various states of compilation to be transmitted from one module to another.

This form should be as simple as possible but should also make it possible to express all the complexities that occur in real code. In short, it will need to be a tree structure.

While I'm not convinced that the Lisp idea of "code without syntax" is a great one (I think every available visual/syntactical clue makes it easier to understand unfamiliar code) I have to admit that the lisp data structures (SExps and Lists) are perfect for this role. I wouldn't expect people to be reading large quantities of this code but it's handy to be able to print it out for debugging purposes.

A Symbol (what lispers call a SExp, S-Expression or Symbolic Expression) is either an Integer, a String, an Atom, a List or a Vector. Integers are what you'd expect them to be. Strings are used to hold things like variable names. Atoms are displayed as strings but treated as unique, opaque integers internally. All you do with an Atom is compare it to another Atom and see if they are the same. See Stringy Identifiers for another perspective on these. This is not quite the same as the lisp usage of the word, where integers are also atoms.

A List (delimited with parentheses) is effectively an object. It consists one or more Symbols, the first of which must be an Atom. It has a type, which is represented by its Atom and the number and types of the following Symbols.

A Vector [delimited with square brackets] is effectively an array of length possibly not known until run-time. This can be used for a sequence of statements (for example) or for the bytes in the compiled program before it is output to disk. Lisp doesn't distinguish between lists and vectors but I think it is useful to keep them separate.

It is not a coincidence that this is quite similar to the RTL of GCC. Many of the same problems are being solved, and I was learning about RTL when I came up with this. However, this structure is a little more general-purpose and a little simpler. In particular it can also be used for front-ends.

These expressions will generally be manipulated by pattern matchers - that is, we search through the program for an object matching some characteristics, perform some transformation on it and then substitute the result for the original object.

For example, here is a part of an x86 assembler written in a hypothetical language which has support for these Symbols built in:

    with(x) {
        match (`jmp` d:Int):
            if (isSByte(d))
                becomes (`db` [0xeb d]);
            else
                becomes (`db` [0xe9 bytesFromDWord(d)]);
        ...
    }

This matches a List which consists of the `jmp` atom followed by an integer (which we will call d). We pass that integer to a function and (depending on the result) we emit one of two possible forms of the x86 JMP instruction. As "bytesFromDWord" is known to be a function and not an Symbol, it is evaluated and the result placed into the vector rather than two Symbols (bytesFromDWord and (d)) being created and placed into the vector.

This works just as well for the front end. The "unless" statement that I have written about before can be broken down using this code:

    match (`unless` condition): becomes (`if` (`!` condition));

And we should have this optimization as well, in case condition is itself a negated condition:

    match (`!` (`!` x)): becomes (x);

Quite a lot of the compiler can be written very easily and tersely this way. There are some complications to do with the order in which transformations are applied, though - we would rather avoid having to specify that explicitly and let the computer figure it out. But the order might depend in quite a complicated way on the context. One way to deal with this might be to keep the pre-transformed Symbol around and continue to match against it even after it has been replaced. Another might be for transformations to transform other transformations as they are applied to the Symbols. I haven't quite figured this bit out yet. This seems like one of those problems that might be technically intractable but always works out to be fast enough in practice.

With some cleverness, it ought to be possible to write a very good compiler this way. Great speed is possible as no string handling is done after the initial parsing phase and symbol table lookups (e.g. we don't write out assembler instructions and rely on a separate assembler to assemble them, though we could still easily obtain assembly code if necessary by having a separate output module.) Many speed improvements are possible (e.g. by performing some transformations when the compiler is compiled).

But the really great thing about this scheme is that code for the compiler is completely self-describing - it's exactly the code one would hope to be able to write.

Having a set format for code also enables the scenario I wrote about in Compiler compilers.

Finally, this structure works just as well for disassemblers and decompilers as it does for compilers and assemblers. Some of the algorithms will be different most of the transformations probably cannot be reversed directly but many of the same patterns show up.