Newsgroups: alt.folklore.computers
Date: Mon, 14 Sep 1992 01:20:47 -0400 
From: "Joseph M. Newcomer" <jn11+@andrew.cmu.edu>
Subject: Re: The need for an assembler (was: Favorite CPU)

As someone whose career started in an era in which nearly all programming
was done in assembler, I must comment on the need to actually use it
on real machines today.

On the 8088, 68000, and similar machines the machines were so slow, and
the compilers so bad (both for performance and functionality) that it
was unrealistic to write time-critical code in a High Level Language.
Assembler for interrupt routines was the order of the day.  Today it
is less of a problem; by declaration a procedure as an 'interrupt'
procedure (e.g. "interrupt Handler(){...}") in Turbo or Microsoft C
you can get the compiler to generate all the necessary register save
nonsense to allow you to vector the hardware direct to this code.  (I
noticed that in workstation software there are hardly any useful extensions
to C, but then again most of the vendors who don't use GNU have these
strange compilers that compile a C-like language, so asking them to do
anything useful when the compilers barely work is asking too much; anyone
who has ever tried to port C code to VMS using DEC's compiler understands
this).  And the machines are fast enough that the overhead doesn't matter.
Where we still need assembly code is in those places where the "abstract
machine" assumed by the compiler must be violated, such as in context
swap code in which there are periods during which there may not be a
valid stack register.  I find that using DOS the only times I need
assembler are when I'm doing very device-specific things (like tweaking
the VGA registers or doing display-specific system calls), and much
of that is due to the baroque assembly-oriented system interface that
DOS uses.

Something that many new Unix users don't know is that the original
PDP-11 Unix and many of the derived Unices had the same bogus
assembler interfaces; the kernel call level on many of these had
specifications like "the file handle number is in register 1, the
pointer to the output string is in register 2 and the count of bytes
is in register 3.  Upon successful return, the overflow flag is set.
On an error return, the overflow flag is clear and an error code is
in register 0".  (The details may be a bit muzzy after 15 years, but that
is pretty close to what I remember, if not precise register numbers).

When we did Hydra in 1972 we said "The kernel interface will be to a
high-level language".  We were using Bliss-11 (the compiler that later
evolved into Bliss-16, Bliss-32 and Bliss-36) and the interface was
designed to be called using a conventional arguments-on-stack technique.
The problem was that some kernel calls (Kcalls) wanted a variable
number of arguments.  Rather than force the user to build a vector
of arguments "by hand", we used the very powerful Bliss macro system to
allow for Kcalls with variable number of parameters, or of the form
    $SetPage(foo,(pg1,0,pg2,0,pg3,1,pg4,0))
where the second argument was a list of values.  We would then push
these onto the stack with a suitable end marker.  This worked fine until
the Algol-68 group needed to use these calls, and the L* group needed to
use these calls, and...  in Algol-68 there is no concept of variable-length
parameter lists, and L* had its own model of how it used the stack (it
was a stack interpretive language in the same genre as FORTH and
PostScript).  These folks went through various forms of hell to execute
kernel calls.  For all the finger-pointing at the VAX, DEC did one thing
right: they created a single calling convention which was uniform across
all languages and made system calls look like library calls.  We can argue
about the success of the implementation of this, but it was an important
step forward in system architectures.  Before this, it was assumed that
every language group was entitled to create whatever parameter passing
and calling conventions that it felt were "best", with the result that
it was nearly impossible to build mixed-language applications.  I know.
I had to program mixed-language applications in many of them.  IBM tried
this with the 360 series, but with typical IBM ponderousness the linkage
was *so* bad that no compiler writer who professed sanity could use it.
Except, of course, the PL/1 project at IBM which *had* to use it.  I
read the PL/1(F) compiler output, and at the start of each procedure
was a GETMAIN (better known as 'malloc' today) call.  Now GETMAIN wasn't
just a library call; it was a system call.  This is why most PL/1 programs
would use procedures only as a desperate last measure when the entire
program couldn't be written as a single piece of inline code.  This was
because the IBM calling convention was least inefficient (I just couldn't
bring myself to say "most efficient") in a FORTRAN environment, with
statically allocated linkage frames (non-recursive, non-reentrant).
In PL/1, procedures were implicitly recursive and a dynamic stack discipline
was required.  How to maintain the calling convention standard and
satisfy the language specification of PL/1?  Use GETMAIN to create a
new stack frame!  The result was a linked list of stack frames (each frame
contained a pointer to the previous frame...register 13, by convention,
if I recall after 23 years away from the beast).

Well, I've got a few more messages to read and I want to get some sleep
tonight...

                                    joe
Newsgroups: alt.folklore.computers