4

Reading the report on the first Algol 60 compiler in the world, namely the Dijkstra-Zonneveld ALGOL 60 compiler for the Electrologica X1, which contains the Pascal source faithfully reproducing its behavior (that is, interpreting the input stream using Friden Flexowriter encoding), allowed a friend of mine and myself to bring it to life in the XXI century, making it read ASCII/UTF-8, with not much effort.

The code it generates can be partially understood, thanks to the "OPC table" in the report (Appendix D, page 329 of the PDF). Everything looks reasonable, except one thing: strings to be printed appear as bare literals before the opcode of the "print" function:

_b_e_g_i_n
    print(|<Hello Algol|>);
_e_n_d

becomes

10144       96  = OPC START
10145  1379884  = 'l' 'e' 'H'
10146  6101013  = ' ' 'o' 'l'
10147  1053989  = 'g' 'l' 'A'
10148 16717080  = \377 'l' 'o'
10149      103  = OPC print
10150       97  = OPC STOP

That is problematic, as the characters 0 to 9 were encoded as 0 to 9, then 'a' to 'z' as 10 to 35, then A to Z as 37 to 62, etc, most coinciding with meaningful OPC codes.

Therefore, for example, an occurrence of c00 as the first 3 characters in a literal string to be printed (or passed as a parameter to a procedure), should be interpreted as

12 RET RETURN

Initially, when the procedure fill_result_list is called, which adds a word to the object code being formed, there is a difference between procedural OPC codes (>=8) and OPC codes (<= 3) denoting literals and instructions with various levels of relocation required for its address field. But, when the memory contents of the post-relocation binary are printed at the very end of the execution, the difference between procedural OPC and a piece of a string with two zero characters is lost.

Was it indeed a case of "If it does not work, just don't do it", or our understanding of the way the X1 Algol threaded code worked is incomplete?

10
  • When trying to open the x1-algol-compiler .pdf files I get the error message "Error rendering embedded code Invalid PDF"
    – hptsb
    Commented Jul 29 at 8:40
  • @hptsb You may want to mention the tool you were using. Chrome opens everything fine for me.
    – Leo B.
    Commented Jul 29 at 15:50
  • While I like the question, "how well" sounds like it can only be asking for an opinion.
    – dave
    Commented Jul 29 at 21:16
  • 1
    Where is the quoted code from? Just prior to execution, or the text that goes into the loader phase? If the latter, then maybe some of the bits of each word of the 'text' are where the OPC value would be, but are in the range 0 to 3, in which case the loader interprets them differently. "For an operator from the complex the value of w is irrelevant, otherwise it is an X1 instruction (or in a few cases a constant or a code word), to be incorporated into the object program." See start of chapter 5.
    – dave
    Commented Jul 29 at 21:46
  • @dave The numerical part of the quoted code is printed by the compiler in the last lines of the Pascal program (mimicking what would be punched on a tape, I presume). It is apparently what goes into the loader. The comments were added manually. Indeed, there will be a difference between small OPC with a value of w within the procedural OPC range, and a procedural OPC, as shown in the table on page 51 of the PDF; perhaps it is a deficiency of the Pascal program that it does not make that distinction when printing the object code.
    – Leo B.
    Commented Jul 30 at 0:00

2 Answers 2

3

You already found the solution, but to elaborate a bit:

The original compiler was pure X1 machine code, the pascal compiler is a reverse engineered variant with some differences. In particular, it simulates part of the operations with an array for an internal storage, and this array is dumped out at the end. This is not what would have been punched, rather it's a mix of complex OPC codes that would have been replaced by the loader, and the actual X1 instructions after OPC modification (which is why the OPC is not included in the dump).

It's a lot easier to read the dump if one converts the output to octal, and dissassembles the X1 instructions.

The internal procedure print was intended to be used on numbers. Here is an example program:

_b_e_g_i_n
  _i_n_t_e_g_e_r i, j;
  i := 1;
  j := 2;
  print(i);
  print(3);
_e_n_d

and the compiled code in octal with disassembly:

  023640:       0140  START
  023641: 0422000212    2B 000212 A  
  023642:        021  TIAS
  023643: 0422023662    2B 023662 A  
  023644:        042  TIRS
  023645:       0125  ST
  023646: 0422000213    2B 000213 A  
  023647:        021  TIAS
  023650: 0422023663    2B 023663 A  
  023651:        042  TIRS
  023652:       0125  ST
  023653: 0422000212    2B 000212 A  
  023654:        042  TIRS
  023655:       0147  print
  023656: 0422023664    2B 023664 A  
  023657:        042  TIRS
  023660:       0147  print
  023661:       0141  STOP
  023662:         01  
  023663:         02  
  023664:         03  

You can see that there is a constant list at the end, and space for i and j is reserved in 0212 and 0213. The constants are pushed to the evaluation stack via TIRS from the address in B, the address of a static (global) variable is pushed via TIAS, also with the address in B.

The internal function print takes exactly one numerical argument and prints it, it works on arbitrary expressions, just like the assignment before. (There is actually no syntax error if you supply multiple arguments, but the generated code doesn't make sense, it just pushes all arguments with no way for print to know how many arguments are pushed).

As the Algol 60 report explains, you cannot do much with strings; all you can do is to pass them around until they end up as parameters to a "non-Algol" function. I assume the library function PRINTTEXT is such a function.

Here is how string argument passing looks:

_b_e_g_i_n
  _i_n_t_e_g_e_r x;

  _p_r_o_c_e_d_u_r_e A(s);
  _s_t_r_i_n_g s;
  _b_e_g_i_n
  _e_n_d;

  x := 1;
  A(|<XXXXX|>);

_e_n_d

with the code

  023640:       0140  START
  023641: 0522023645    2T 023645 A  ; jump to 023645
  023642: 0422000001    2B 000001 A  
  023643:       0131  SCC
  023644:        014  RET
  023645: 0422000212    2B 000212 A  
  023646:        021  TIAS
  023647: 0422023663    2B 023663 A  
  023650:        042  TIRS
  023651:       0125  ST
  023652: 0422023642    2B 023642 A  
  023653: 0522023660    2T 023660 A  ; jump to 023660
  023654:  017036074                 ; string
  023655:  077636074                 ; string
  023656:        015  EIS
  023657:   04023654    0A 023654 B  
  023660:  022000001    2A 000001 A  
  023661:        011  ETMP
  023662:       0141  STOP
  023663:         01  

The assignment to x is only there to see where the main part starts. So to call A with a string parameter, it loads the procedure address into register B, jumps over the string constant, places the address of the string in the parameter block, loads the number of parameters into A, and calls the procedure via "etransmark" ETMP.

I would assume it would work the same with PRINTTEXT, but as I do not have a library tape (which the compiler asks for if you use PRINTTEXT), I cannot try it out.

3
  • If you did the disassembly programmatically, would you be so kind to share the source code?
    – Leo B.
    Commented Jul 30 at 8:18
  • 1
    @LeoB. it's a quickly thrown-together Perl program, which parses the output of the pascal program. I can do a merge request for the git repo, if that would work for you.
    – dirkt
    Commented Jul 30 at 8:23
  • Sure, please do.
    – Leo B.
    Commented Jul 30 at 9:20
3

It was a case of pilot error. Strings were not intended to be printed using the internal procedure print; they were supposed to be printed by the predefined library procedure PRINTTEXT; when it is used, a jump is generated around the string literal, just as it is for used-defined procedures with string arguments.

1
  • Duh, I should have guessed that. There are similarities with KDF9 Whetstone Algol (hardly surprising, Randell and Russell got briefed by Dijkstra), and it has separate output routines depending on the argument type -- write text versus write in this case.
    – dave
    Commented Jul 30 at 12:54

You must log in to answer this question.

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