17

From this answer by gsg about the usage of certain x86 instructions:

Note that the x86 was originally designed as a Pascal machine, which is why there are instructions to support nested functions (enter, leave), the pascal calling convention in which the callee pops a known number of arguments from the stack (ret K), bounds checking (bound), and so on. Many of these operations are now obsolete.

What exactly does it mean to be a "Pascal machine" in that context? Is it limited to only that which is mentioned? I've never even heard of this term and would like to know more about it. Is there any good write up of "and so on" or historic Intel literature which talks about the relationship of x86 to Pascal?

3 Answers 3

16

What exactly does it mean to be a "Pascal machine" in that context?

It's close but not really the case. It starts with the term Pascal Machine being misused, as this usually describes a software and/or hardware to interpret p-code; today we would call it bytecode, like the Pascal Microengine or a p-code interpreter. This virtual machine was called by its inventor, Urs Ammann, p-Machine as in Pseudo-Machine. Users of the UCSD P-System, which translates to Pascal System, also tend to read p-Machine as Pascal-Machine.

With citing procedure function like ENTER,LEAVE and RET n as original features it also screws the timeline, as these instructions where not part of the original 8086 design, but introduced four years later with the EU (*1) of the 186/286 CPUs. That part of the SO answer seams to try to establish a retro-fitted narrative.

But it is true that the 8086 was built with high level languages in mind. The instruction set is made for easy compiler construction and to support generic high level structures. The ability to easily handle the addressing of stack frames (via BP) is eventually the best example. Others are the (mostly) symmetric addressing, handling of arrays and strings. Of course, all of this is handy for an assembly programmer, but it simplifies the life of compilers as well - and offers a reasonable speed up for their output.

Stephen Morse, the designer of the 8086 describes his approach in a worthwhile interview with PC-World:

Now, for the first time, we were going to look at processor features from a software perspective. The question was not “What features do we have space for?” but “What features do we want in order to make the software more efficient?”

In contrast to previous generations of CPUs, the 8086 was, much like the 6809 of the same time, designed with a clear goal to support HLL.

Is it limited to only that which is mentioned?

Only the mentioned instructions? No, as said, it's about basic aspects of the 8086's instruction set.

I've never even heard of this term and would like to know more about it.

'Cause he made it up - at least in the context used by him.

Is there any good write up of "and so on" or historic Intel literature which talks about the relationship of x86 to Pascal?

As there is no special relationship to Pascal, No. But there is information about its ability to support typical HLL structures - like the stack or arrays - within the 8086 manuals. These are structures found in next to all languages, especially all ALGOL descendants - like PASCAL or C. The fact that Mr. Morse did add chapters for PL/M and Pascal (*2) to his excellent book The 8086/8088 Primer does tell as well.

The aforementioned interview is a great start to learn about the history of the 8086. Give it a read.


*1 - Execution Unit, that part of the 8086 doing all computation. A 80186 combines a second generation EU with a real mode BIU (Bus Interface Unit) while the 80286 got a virtual addressing BIU. This split is eventually the real genius of the 8086's hardware design. Already laying the ground of today's multi function unit CPU design in 1978.

Already, the 8088 of 1979 made use of this design by combining a first generation (8086) EU with an 8 bit wide BIU.

*2 - Book was originally published in 1980 with just the PL/M chapter. The Pascal chapter got added in the second edition in 1982. So if there was a specific language at all, it would have been rather PL/M than Pascal.

7
  • 2
    Exactly — the 8086 was designed with HLLs in mind, but not specifically Pascal (AFAICT); that part of the SO answer is retro-fitted narrative. Stephen P. Morse highlights the 8086’s varied addressing modes as being an advantage for HLL (stack access as you mention, but also array access), as well as string manipulation. The Pascal chapter was added to the second edition of the Primer; if any specific language served as inspiration for the design, it would be PL/M, not Pascal. Commented Jul 6, 2018 at 9:10
  • 2
    Go for it! Feel free to merge my comments into any of your answers. Commented Jul 6, 2018 at 9:16
  • 1
    Your endnote point about the genius in the 8086’s design being in its block splits is excellent. (For reference for other readers, other micro-processor designs at the time tended to connect everything together in multiple ways; see the 6502’s block diagram for example.) That decision didn’t take long at all to show its merits: the 8088 benefited directly. Commented Jul 6, 2018 at 9:33
  • 3
    Not necessarily the most helpful comment, but after finding the linked copy of The 8086 Primer frustrating to navigate, I did a quick search and found that the previous edition is available as an ordinary PDF (or various e-book formats) from the Internet Archive: archive.org/details/The8086Primer . I'm finding that a lot easier to read.
    – Tommy
    Commented Jul 6, 2018 at 15:06
  • 1
    @Tommy I don't think the link to the 8086 Primer is even useful in this context. The book is fine, it also has some added chapters on PL/M and Pascal, but doesn't really go in depth on why the x86 would be useful for those languages. The HLL chapters don't even have a single line of assembly in them, he does explain block and nested scopes, but not which x86 instructions could be used to implement such features. Obviously no ENTER and leave in there as well, because the 8086 didn't have them.
    – tofro
    Commented Jul 6, 2018 at 18:41
4

Pascal was notable at the time for having nested scopes.

You code have, for example:

program test;

var
    t : integer;

procedure p1(x:integer);
  function f1(y:integer) : integer;
    begin
      f1 := x + y;
    end;

begin
  t := f1(10);
end;

begin
  f1(6);
  writeln(t);
end.

So, you can see here that in f1, there are 3 variables in scope: y (from the function), x (from the procedure), and t (the global environment).

Not only are the variables in scope, but the nested functions and procedures as well.

So, a CPU that supports dereferencing a chain of stack frames is much more "Pascal" friendly than one that does not.

C doesn't have this issue, it's a flat namespace. You can certainly have functions call other functions, but the code itself can only reference the local and global scopes, none of the intermediate ones. So, there's a difference of simply doing address arithmetic against a local stack frame, and arithmetic against several that are chained together.

UCSD has a p-code instruction that basically says "give me the value from the 6th offset that's 3 stack frames up". In machine language, you would need to deference stack frame link pointers, repeatedly, to get handle these references.

7
  • 2
    Mind to explain how this is related?
    – Raffzahn
    Commented Jul 6, 2018 at 16:36
  • @Raffzahn It is (somehow) related - The ENTER command has a feature which is pretty unique in that it supports linked lists of stack frames for nested functions/procedures that are, of all compiled languages I know, only can be used by Pascal and Fortran (although most compilers didn't really use it, because it is much slower than handling BP and SP "manually"). The m68k LINK opcode works similarly. For C, BCPL, for example, this feature is not needed.
    – tofro
    Commented Jul 6, 2018 at 16:46
  • @tofro Somehow sounds great in that context, as it's just about one, later added instruction, not realy touching the basic assumption THE 86 is a Pascal machine. I addition, useing Fortran as agument makes again the Pascal claim (and it's base on this instruction) invalide, doesn't it? And BTW (and unrelated), the oh so often cited argument that ENTER is slower is history as well. I was only true until about the time of the Pentium. As soon as CPU cores started to recode and reorder the difference vanished.
    – Raffzahn
    Commented Jul 6, 2018 at 17:04
  • 2
    give me the value from the 6th offset that's 3 stack frames up should be "3 scopes up". Recursive calls add stack frames but the variable references use static scopes.
    – Leo B.
    Commented Jul 7, 2018 at 6:25
  • 1
    The only time nested scopes would necessitate nested stack frames is when inner functions get called recursively by themselves or each other--something that doesn't happen in your example. If there are no inner-recursive calls, a compiler could place each inner function's variables at a fixed displacement from the parent's stack frame.
    – supercat
    Commented Jul 7, 2018 at 17:46
3

One difference between Pascal and the C language as commonly implemented in the days before standardization is that every user-defined Pascal function will always be passed with a fixed set of arguments, while C functions might sometimes be passed more and sometimes fewer, and there was no need for a function to indicate, even retrospectively, how many arguments it had actually been passed. One could thus do something like:

void test(mode, x)
  int mode,x;
{
  if (mode==0) 
    printf("Hello there!\n");
  else
    printf("X was %d\n", x);
}

and safely invoke the function using either test(0) or test(1, 42). Or, if one liked, test(0, 57) though the value 57 would be ignored.

Because Pascal functions always knew how what arguments would be passed, a called function could clean up the stack for the benefit of the caller. Because there are usually more function calls than functions that are called, having the called function clean up the stack would generally save code. When using common semantics, however, a C compiler couldn't generate a function epilogue that would clean up arguments passed by the caller because it wouldn't know how many bytes to pop. While it would have been possible to pass such information as an extra argument, that would have been more expensive than simply leaving cleanup to the calling function. *The 8086 includes a special form of the "RET" instruction, RET n, to facilitate this by popping n bytes of arguments off the stack.

Note that in the days before standardization, there were some C implementations that used the Pascal-style calling convention. On such implementations, a call to "printf" that didn't include enough formatting specifiers to consume all the arguments would jump the rails. The C Standard opted to require that compilers handle calls to functions that are declared as accepting variable argument lists in a way that will ignore extra arguments, perhaps by processing a function like:

void variadicTest(int foo, ...);

as though it were:

void variadicTest(int foo, char *__arguments);

and then having a call like:

variadicTest(1234, 1.2f, 23, "Hello");

get processed as:

struct { double p1, int p2, char *p3} __temp24601 =
   {1.2f, 23, "Hello"};
variadicTest(123, (char*)&__temp24601;

Thus, a conforming C implementation for the 8086 could use the Pascal calling convention and take advantage of the RET n instruction. Functions generated by such an implementation, however, would fail if called from code that expects them to use the C calling convention. The Standard requires that a call to a non-variadic function for which a prototype is visible be treated the same as one for which a prototype is not visible, thus making it impossible for compilers to use the principle "use Pascal convention if a prototype or new-style definition is visible, or the C convention otherwise". Some compilers did support a "pascal" keyword for that purpose, and use of it could improve performance, but it would make code non-portable.

4
  • 1
    The C standard also made it undefined behaviour to call a function with the wrong number or type of arguments, and that functions with variadic arguments are required to have prototypes, therefore the compiler could infer the prototype from the call and still be conforming. However I guess too much code assumed that printf was available without prototype to make it impractical to write a compiler that made use of this restriction.
    – celtschk
    Commented Jul 7, 2018 at 18:36
  • 1
    @celtschk: The Standard requires that a call to a non-variadic function without a prototype behave the same way as a call to such a function with a prototype. A compiler could use Pascal convention for all function calls (handling variadic arguments as I described) but it would be incompatible with pre-compiled functions using the C convention. Interestingly, most higher-level functions in the 1984 Macintosh Toolbox used the Pascal convention.
    – supercat
    Commented Jul 7, 2018 at 18:54
  • The original design of the 8086 was finished in 1978, and the first C standard was 1989. I couldn't find a reference to a CP/M C compiler that was released before 1978, so I don't know how much influence C could have had on the 8086. The typical higher level languages in use then were BASIC and Pascal. Commented Jul 9, 2018 at 18:17
  • @DavidThornley: The year 1989 is rather late in the history of C; the Standard was a response to the fact that the language was already pretty well established. I don't know whether any of C's predecessors like B or BCPL shared its parameter-passing conventions, but it would seem likely. In any case, I can't think of any processor features that would be of benefit to a C compiler that wouldn't also benefit a Pascal compiler.
    – supercat
    Commented Jul 9, 2018 at 18:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .