This overlaps with experience with the first Fortran.
About 1957 at Livermore I was rummaging around the filing cabinets for punched cards. I came upon a drawer mostly full of cards with a small deck of binary cards at the front wrapped in a rubber band. The deck was in binary and started with a familiar loader and was less than an inch thick; the rest of the cards were symbolic (Hollerith.) There were a few pages of description of a new assembler format for the IBM 704 that differed considerably from the NYAP from IBM that we were using. I guessed that the binary was the assembler proper and that the rest was the source for it. That turned out to be the case.
There are three programmer’s whose assembler code I have found especially rewarding to read:
Fools ignore complexity. Pragmatists suffer it. Some can avoid it. Geniuses remove it.
I was soon able to adapt the assembler for our use; we did not have the source for NYAP. Indeed this was perhaps the first large program I had seen where the output of the assembler would actually run correctly. Debugged programs had generally been patched versions of assembler output. The programmer would patch by punching a few binary commands on a card and adding that to the end of the binary deck from the assembler. He would hopefully annotate the assembler listing so that the same change would be made to the source. The debugging cycle did not generally afford an assembly step. The cost of reading 2000 cards thru a card reader at 150 cards/min and then printing at 75 lines/min made that infeasible. In one (just one) extreme case the programmer threw out the source assuming that he would never be able to afford to assemble it again. Here is a bit more detail on the assembler’s function.
We developed an alternative debug cycle for the Stretch which was to manually annotate the assembler listing, make a corresponding card deck defining the symbolic change, or add changes to the change deck used in the previous debug cycle, bring the symbolic source tape and change deck to the machine and assemble or compile without a new listing. The assembler and compiler listing included line numbers to make preparation of change decks easy. When the annotations became burdensome, a new listing and magnetic tape was made and the change deck was retired and a new one began to grow. The Fortran compiler worked the same way.
The assembly listing (printout) had one output line per card from the input deck. Such a card would generally specify a machine instruction. The corresponding line in the listing would provide the machine address at which the assembler had decided to place the instruction, and the instruction, both in octal. Then a decimal line number incremented by one for each card, followed by source from the card. Some cards would have a ‘pseudo op’ such as org n which would tell the assembler to put subsequent instructions starting at location n. Other pseudo ops would reserve blocks of storage with assigned names.
Some assemblers provided symbolic cross references. Debugging was done at a desk away from the computer with an assembler listing and a binary (octal) printout of memory (dump) at the time of the program failure. This was feasible when memory cost one dollar per bit. Today assemblers do not produce listings.