Subject: Re: {Historical} Lisp 'manually' compiled to assembly
From: rpw3@rpw3.org (Rob Warnock)
Date: Wed, 18 Jan 2006 23:10:07 -0600
Newsgroups: comp.lang.lisp
Message-ID: <2cadnRHQ1p6yglLenZ2dnUVZ_tWdnZ2d@speakeasy.net>
Kenny Tilton  <NOktiltonSPAM@nyc.rr.com> wrote:
+---------------
| Sounds like the toys actually ran, and I have to ask, why not? Why does 
| hand compiling sound unreasonable? The core Lisp functions only had to 
| be built once, and there were not that many. Then translating Lisp to 
| machine language became just a matter of calling those built-ins.
| 
| I think Minti is right when he mentions the generation thing. I still do 
| not believe /I/ once programmed on punch cards or Apple Integer Basic 
| using a cassette player to save my work, and I was the one who did it.
+---------------

When I was learning to program the IBM 1410 [circa 1965!], for quite a
while I did everything in raw *machine language* [really!], which wasn't
as bad as it sounds since the 1410 had *decimal* addressing of memory --
its "100k" characters really *were* locations 00001 through 99999!!
[For some reason(?) there was no location 0.]

It was a "character" machine, not a "byte" machine, and a character
consisted of bits named 1, 2, 4, 8, A, B, W, and C. "C" was actually
character parity, and you couldn't enter it yourself -- the machine
set it automatically. "W" was the "word mark" attribute, and marked
the beginning of a variable-length word. [Both instructions and data
could be variable-length! Yes, bignums in hardware!]  The "1248AB"
gave you a 6-bit graphic character set, thus upper-case only. The
first character of an instruction word was the opcode (always with
the "W" bit turned on), and to the extent possible opcodes were
mapped to characters that were somewhat mnemonic, e.g., "N" was
No-Op, "L" was Load (input from I/O device in "load mode"), etc.
You bootstrapped the machine in the morning by typing the following
string into location zero and hitting "Start" (where the "v"s over
the "L" & "N" are word marks):

    v         v
    L%B000012$N

This read one complete record from magnetic tape drive #0 into
location 12, which you'll notice [counting from 1!] is the location
just after the no-op, and then executed the no-op, and then executed
whatever had been read in, usually the bootstrap for the operating
system on the rest of the tape.

Well, I discovered that writing all of memory out to tape as a single
record wasn't hard to do, either, and I started saving "core images" of
my playing/learning work on a private scratch tape. You could easily
save multiple "core images" on a single tape [append-only, of course,
no update-in-place], and simply skip past the first few to get to the
one you wanted. The hardest part was keeping up-to-date the paper
cheatsheets I had that told me where in memory I'd put the various
bits of test code and library routines for each of the images!  ;-}

Anyway, to address Kenny's point: Once one has handcoded the barest
minimum of utility routines in bare machine code -- output a string,
output a number, input a number, input a letter, save a core image,
or for Lisp, CONS, CAR, CDR, etc. -- from there on up you're already
programming only in subroutine calls, which might as well be some
high-level virtual machine for all you care. I mean, you think "CAR"
and what you write down is a the machine code for a subroutine call
to wherever you had stuck the code for CAR.

[This is a *lot* easier than it sounds if you have a machine with
"readable" instructions and addressing, such as an IBM 1410 or an
LGP-30 (another early favorite for me).]

Even a PDP-10 wasn't *that* hard to code in absolute binary [well,
octal, mostly], since it was a "word" machine with fixed-format
instructions that had field boundaries aligned (mostly) on nice
octal digits. The instructions format was so:

		     1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|        OP       |  AC   |I|   X   |                 Y                 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

OP = op-code, AC = accumulator, I = use indirection, X = index (if plusp),
and Y was an 18-bit address, which was (then) all of memory.

Keying instructions into the console front panel was easy, since the
switches were colored by threes in alternating shades of light & dark
blue. Made is *really* easy to "type in" octal values!

[Not sure this really had a point, other than that maybe every programmer
should have the experience of having/getting to do at least a little
bit of serious work with "bare metal" at least once in their career...]


-Rob

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607