Table of Contents

Introduction

A "link editor" (or just "linker") is a program used to combine a set of "object files" created by compilers and assemblers generated from separate source code files into a single executable "load file" that can be directly run on a computer after being loaded into the computer's memory.  In the specific case of Space Shuttle flight software under the umbrella of the Virtual AGC Project, the roughly 1500 source-code files are from the flight software known as PFS or PASS and (potentially) BFS.  They're written in the HAL/S high-level language and the AP-101 assembly language, and compiled/assembled by the HALSFC compiler or ASM101S assembler, thus leaving us with around 1500 object-code files that need to be linked into a single load file.  But even if (say) you had written your own custom HAL/S program, and you did so using a single source-code file, thus creating a single object file, that object file would still need to be "linked" to produce an AP-101S load file.  That's because what distinguishes an object file from a load file is that a load file inhabits a specific area of computer memory at explicit memory addresses, whereas an object file leaves all questions of the specific memory addresses it occupies open; it is the linking process which turns the open-questions of addressing in the object files into the specific memory addresses a program needs if it's going to run properly.

Don Schmidt (thanks, Don!) has been kind enough to write a program he calls lnk101 that we can use for this purpose. 
Disclaimer:  I shouldn't imply that lnk101 was written by or for Virtual AGC.  It was at least begun for Don's own purposes, and thus existed in some form when I first heard mention of it, but Don was kind enough to pay attention to my many ideas on the subject as his development proceeded, so lnk101 is indeed fit for our use within the Virtual AGC suite of Space Shuttle development tools.  But note that it does not reside within Virtual AGC software repositories, nor is Virtual AGC in control of it, nor is anything I say about it official.  It is in Don's own repository, as is public commentary of his own about its use.
Regarding the contemporary linker used for the original Space Shuttle software development, to my knowledge it has not survived, either in source code or otherwise.  Thus lnk101 is an entirely-new creation.  It is a Python 3 program that should work on any computer having the Python 3 language installed.

Regarding the IBM AP-101B vs AP-101S computers, the AP-101B was used in earlier Shuttle flights, while the AP-101S was used in later flights.  I think that lnk101 is agnostic on the question of AP-101B vs AP-101S, since the choice of instruction sets is the province of the HAL/S compiler rather than of the linkder.

I am not aware of surviving documentation describing the format of AP-101S object files specifically, nor of any surviving AP-101 object-code files from the Shuttle era.  In any case, I think that such object code would have been produced not as "files" in a mass-storage system, but rather as decks of computer punch-cards punched directly by the original HAL/S compiler (HAL/S-FC) or AP-101 assembler.  So it is possible that the decks of punch cards could still exist, hidden away somewhere, even if not as online files.  In any case, I have no access to them, and have heard nary a hint of their existence, and I could be mistaken in my inferences about them anyway.

The original linker is referred to in the contemporary documentation simply as "the AP-101 link editor", and the contemporary documentation goes on to say that "until the final version of the linkage editor is available to support all features of the HAL/S-FC compiler, an interim program, known as HALLINK-FC, will be supplied to perform the link edit functions".  As usual, satisfactory documentation for neither "the AP-101 link editor" nor for HALLINK-FC (nor for its System/360-target predecessor HALLINK) is available to us.  Indeed, I do not even know if the "the AP-101 link editor" was ever completed satisfactorily to supersede HALLINK-FC. My intuition, based on nothing concrete, is that it was not, and that HALLINK-FC was used until the end of the Shuttle project.  But since we have access to neither of those linkers, any intuition of mine about them is without value.

On the other hand, in the Virtual AGC Project we have created a modern port (from the XPL/I programming language into the Python programming language) of the original HAL/S compiler.  That being the case, we can in principle use the compiler to convert HAL/S source code into AP-101S object files and apply reverse engineering on those object files to clarify issues regarding the object-file format.  In attempting that kind of reverse engineering, it quickly becomes apparent that the AP-101S object-file format is based upon the format of early object files for the IBM System/360, if not strictly identical to it.  In other words, we can use the well-documented System/360 format as our guide, and occasionally depart from it as needed on an ad hoc basis.  Some relevant references are listed in the next section.
Aside:  It appears to me that in IBM parlance, what I call an "object file" would be a referred to as an "object deck", presumably because it was originally directly punched onto a stack of computer punch-cards.  I'm not telling you this just to be pedantic — though obviously, that's a thing I do — but rather to let you know that if you google it yourself, you're likely to have much better luck searching for "decks" instead of "files".

As usual, not all of the System/360 features are needed for actual legacy AP-101S software, so I won't claim that every feature provided by a full System/360 linker is necessarily implemented in lnk101; that's up to Don anyway.  Contrariwise, some non-System/360 AP-101S-specific features appear in the object files.  For example, "records" in a System/360 object file are described as being of 6 types, known as ESD, TXT, RLD, SYM, XSD, and END, but the XSD type wasn't introduced until the 1990's in order to deal with the needs of C compilers, and hence XSD records don't appear at all in HAL/S or AP-101S object files.  But I won't trouble myself or bore you with an exhaustive list of such differences here. 

To facilitate whatever reverse engineering is needed, I've provided a Python 3 program that can parse the object files produced by the HAL/S compiler (or by ASM101S) and report on all of the data found within them; and Don does similarly.  This will be covered in more detail later.

Backup vs Primary Flight Software (BFS vs PFS)

Let's get a clarification to my comments above out of the way.

Recall that Space Shuttle flight software came in two distinct flavors, namely the Primary Flight Software (referred to as PFS, or loosely but more commonly as PASS) and the Backup Flight Software (BFS).  PFS and BFS were developed mostly independently of each other — by different companies even, namely Intermetrics vs Rockwell —, but more than that, their development tools differed as well.  For example, although PFS and BFS nominally were eventually compiled using the same HAL/S compiler (HAL/S-FC), in fact there were two incompatible versions:  HAL/S-FC (for PFS) and HAL/S-BFC (for BFS).   And the fact that there are two separate compilers doesn't affect just PFS and BFS code, but any HAL/S program we may write:  We always have a choice of which of the two flavors of the compiler to use!

More importantly for us in the context of the present discussion, back in the day, PFS and BFS used completely different contemporary linker programs.  Outside of this one section you're reading now, both this entire page and specifically the lnk101 linker program described on this page, are presently for object code generated by the PASS/PFS flavor the HAL/S compiler.  Object code generated by the BFS version of the compiler won't even be mentioned outside of this one section you're reading right now ... well, with one brief return when reverse engineering is discussed later. 

Why?  Well, back in the day, rather than using the AP-101 link editor, BFS was linked using a different program called PILOT (Program Integration and Loading Tool).  Furthermore, while object code produced by the PFS version of HAL/S-FC was in a modified System/360 format, object code produced by HAL/S-BFC was instead in PILOT format.  The two object-code formats are incompatible.  In other words, neither the AP-101 link editor nor its intended replacement lnk101 are compatible with PILOT object files.  At least, not at present.

That said, I hope that someday there will be a modern version of PILOT that can be used to link BFS-style object code ... perhaps it can be called yaPILOT?  Maybe?  Yes, that has a nice ring to it!  But better, I'd prefer that lnk101 be extended in that direction.

One hurdle is that as far as I know, there is no surviving information whatsoever about PILOT, and in particular we have no documentary information about its object-file format.  To be frank, I'm proud of myself for even figuring out that it was called PILOT to begin with.  The acronym "PILOT" is, of course, quite unfortunate from the point of view of trying to find available information via a search engine like Google, since all you'll discover is information about how to pilot a Space Shuttle.  I'm afraid this reveals a certain weakness in today's treatment of knowledge, in that we don't have many knowledgeable people any longer, but rather we have people with borrowed knowledge attained via search engines.  And A.I., of course, for those of us who can't even use search engines very effectively.  But what about when the search engines fail because the search terms are too common?  Well, that's a good way to lose knowledge, even if it's sitting in plain view.  But I doubt it would have helped if PILOT had a less-common name or if we had more native experts on PILOT to consult, since I doubt that there's any surviving information about PILOT online anyway.  If you have any such documentation, send it to me! 

But don't lose all hope.  Since the object-code generator in PASS2 of the HAL/S-BFC source code can produce PILOT-compatible output, it's still possible to attain some understanding of the PILOT format via reverse engineering it, and indeed perhaps a sufficient amount to implement a linker!  I have done at least some of the reverse engineering, and have embedded my findings in a program I call coPILOT.py.  Catchy, eh?  This script can read object files created by the HAL/S-BFC, and parse them into a human readable form.  Moreover, there's additional commentary embedded within coPILOT.py's source code to document some aspects of the format that aren't evident in the reports it outputs.  If you want to read more about coPILOT.py, I'd suggest referring to the discussion in issue #1273 in our Virtual AGC GitHub repository.

And now we leave the topic of BFS in order to resume our discussion of PASS/PFS-style object code and its linker.

References

Installation of lnk101

As mentioned earlier, the AP-101S linker we have is called lnk101, by Don Schmidt.  To install it, and a variety of useful programs that come with it, I would suggest this:

I know this works on the versions of Linux I personally use.  Concerning Mac OS or Windows I have no knowledge.

Note:  At the present writing, the software in Don's repository is under development, hence the installation process still has what I consider inconveniences that I hope won't persist into a production version.  Inconveniences?  If you are reading this page at all, it's likely you will already have installed all of the Shuttle-related development tools (HALSFC, ASM101S, XCOM-I) on your own computer.  But installing lnk101 using the instructions above will at the moment install duplicates of all of these Virtual AGC development tools, sometimes modified, sometimes behind in features, sometimes ahead in features (which will hopefully be merged back upstream to Virtual AGC sometime soon), all of which will be time-consuming for you, and none of which you will personally use.  But as long as none of as any of these duplications work their way into your PATH, having them there doesn't do you any particular harm.

Use of lnk101

There are a couple of scenarios in which I would envisage using the linker.  I'll cover them both in the subsections below. 

Most commonly, I'd think that you'd want to take an object file or set of object files comprising a complete program, and link them to create a load file that you could run in an AP-101S emulator.  At present, I'm still working towards full compilation/assembly of all PASS/PFS source-code files, so it's not actually possible to do this yet with PASS.  But it's certainly possible to do so with a HAL/S program you've written yourself.  The other potential use is something that I need to do myself, which is to take a single object file that doesn't represent a complete program itself, but to nevertheless link it as if it (and all of the not-actually-present other object files upon which it relies) reside at some specific locations in memory of my own choosing.  That's capability is vital for testing the compiler and assembler.

By the way, if you were immersed in the IBM mainframe world, as you would be if you were one of the original developers of Space Shuttle flight software, you would expect that since the linker intakes IBM System/360-like object files it would also emit IBM System/360-like load files.  That's certainly what the contemporary linked editor did and as the contemporary AP-101S loader would expect.  But lnk101 does not do that.  Instead, it emits what it refers to as an "FCM" file.  (I'm not sure what that stands for.  Perhaps Flight Computer Memory.)  At any rate, it's simply a binary file that's an image of the entire AP-101S memory.  Not coincidentally, this is also the format expected by the available AP-101S emulators, gpc-batch and gpc-gui.

Scenario 1:  Fully Linking a Complete Program, and Running It

Suppose for the sake of discussion that you have a HAL/S program residing in a single source-code file, perhaps HELLO_WORLD.hal:

C A TEST PROGAM
  HELLO_WORLD: PROGRAM;
WRITE(6) 'Hello, world!';
WRITE(6) '';
CLOSE HELLO_WORLD;

In the simplest manner available right now, you might compile it, link it, and run it as the following table describes:

Command-Line Instruction
Explanation
HALSFC -o HELLO_WORLD.obj HELLO_WORLD.hal
HALSFC is, of course, our HAL/S compiler, whose more-complete usage instructions are found here.
lnk101 -o HELLO_WORLD.fcm --json-symbols HELLO_WORLD.sym.json -L path/to/ZCON -L path/to/RUN HELLO_WORLD.obj
If our program had consisted of several object files that we had compiled separately, rather than just a single file, then we could have listed all of them on the command line.  The option "--json-symbols FILENAME.json" tells the linker to output a symbol table that will be used below by the emuator(s); without it, the emulator(s) will complain.  The "-L LIBRARY" options give the names of "libraries" that will be searched during the linking process when unresolved symbol names for external procedures or variables are encountered.  A "library" is a directory containing AP-101S object files.  The two libraries you almost-definitely need for any linking are the RUN library and the ZCON library, which provide the PASS runtime library and the pointers (ZCON's, discussed later) for them.  Those libraries are built during the lnk101-installation procedure discussed earlier, as built from AP-101 assembly-language source code here and here, and reside in the build directories nsts-sdl-dps/build/runtime/RUN/ and nsts-sdl-dps/build/runtime/ZCON/ respectively.  Typing that every time you want to link something is not very convenient, so personally I just copy them (or symlink to them) in whatever my working directory is, so that I can just use "-L RUN -L ZCON".
gpc-batch --no-trace --outfile6 HELLO_WORLD.txt HELLO_WORLD.fcm
The program gpc-batch is an AP-101S emulator and debugger that operates from a command line. It will be available as part of the lnk101 installation. We just want to run our program without a lot of messages from gpc-batch, so we use the --no-trace switch to turn most of its running commentary about what instructions it's executing, what the CPU register values are, and so on.  The option "--outfile6 FILENAME.txt" tells it the name if the file into which output from the HAL/S WRITE(6) statements should go.
cat HELLO_WORLD.txt
Among other things, the cat program is used in Linux or Mac OS to print a file to the display.  In Windows, the equivalent command (for our immediate purposes) is type.

Each of these programs has additional alternatives that could be explored using their --help command-line switches.  For example, if we ran gpc-batch instead as

gpc-batch --no-trace --interactive HELLO_WORLD.fcm

then we could have seen messages immediately as they were emitted, or input directly from the keyboard (if the HAL/S program allowed for it), rather than having to deal with all of the i/o through files.

Another possibility would be to use the graphical (GUI) form of the emulator:

gpc-gui HELLO_WORLD.fcm

That opens up a window that looks something like the following:


Just hit the Run hot-button and you'll see it change (look at the TERMINAL pane near the bottom of the window) to


Curiously, there's no obvious way to exit the GUI emulator — the unobvious way is to hit ctrl-Q on the keyboard —, but I'm assured that'll be corrected by the time you're likely to be reading this.

If HELLO_WORLD.hal had been a more-interactive program, in which it not only used WRITE(6) to display messages but also READ(5) to get keyboard input from you, you could have typed your input into the pane at the bottom of the window, where it now shows the message "(waiting for program input...)".

Scenario 2:  Linking a Single Object File That Is Not a Complete Program

In this scenario, you have one (or a few) object file(s) that form part of a program, but you don't have 100% of the files that comprise a complete program.  Or you have them but just want to focus our attention on a few of them.  You could still try to link such object files using the technique described in the preceding section, but the code in those files would undoubtedly reference external symbols.  The linker wouldn't know how to resolve those external references, so there wouldn't be much point to the exercise of running the linker in the first place.

But wait!  Perhaps you could provide the linker with a list of all the CSECTs it will encounter, and the memory addresses at which you want those CSECTs to reside.  If the linker had that information, then it could do its job.  

How would a need to do this arise?  Well, imagine the following situation.  Suppose you have a lot of HAL/S and AP-101S assembly-language source code files, comprising some program.  In my case, that happens to be the source code for version OI340600 of the Space Shuttle's PASS.  If I were in a position to compile/assemble every single source-code file in OI340600 with 100% accuracy, and could figure out such details as the order in which the object files needed to be linked, then in principle I could use the preceding section to link that entirety of OI340600 in a single operation.  But even if the HAL/S compiler and the linker works 100% perfectly, that's still a big task, and very time-consuming as well — not to mention that at this writing I've only been able to compile about 2/3 of the HAL/S source code files, and need to resolve some issues to compile the rest of them.  So as a first step, I'd prefer to see the development tools do their thing piecemeal rather than in one huge lump.  In other words, I very much want to deal with just one HAL/S source-code file at a time, and resolve any issues I encounter in that simplified scenario before moving onward and upward to combining 1000+ oebject files.

But how would I know where all of the CSECTs were "supposed to" go in memory?   I have an additional thing, a disassembly of GPC memory contents for OI340600, made during the Shuttle era by a program called MAFGEN.  Such a disassembly can be parsed to obtain a list of all CSECTs and their locations in memory, using a utility I've written called unlinkMAFGEN2.py.  Of course, in a project of your own devising, using your own source-code files, you could produce such a file of CSECTs/addresses of your own, and we'll come back to the subject of the format of that file shortly.  I face that problem myself, in that while I have a MAFGEN disassembly of OI340600, I do not have one for OI301700, which is another PASS version for which I have source code.

Actually, I've ignored an important detail in what I just said, which is that there are multiple possible memory loads of PASS executables.  What I mean by that is that these different memory loads involve different selections of PASS source code, and involve different levels of functionality.  Any given one of the five GPCs may be loaded with any one of these memory loads at any given time, according to the flight phase.  Perhaps the following illustration that represents how the Space Shuttle's flight phases are represented in a software model will make the idea clearer; the different memory loads correspond to the pink boxes in the graphic:



Thus, there is not just one MAFGEN disassembly for OI340600, but rather 8 of them, with naming like "DASS_XXX":
Similarly, there is not just one list of CSECTs and their memory addresses as produced by unlinkMAFGEN2.py, but rather 8 of them with names of the form "csects-XXX.json":  csects-SSW.json, csects-G16.json, and so on.

To illustrate the linking process, let's consider just a single HAL/S source-code file from OI340600 that I've chosen basically at random, namely APPLSRC/GV7RTL.hal.  Incidentally, GV7RTL appears only in memory load G16.  The steps to compile and link this file might look like this:
HALSFC APPLSRC/GV7RTL.hal --test --force --parms=LIST,SRN,TEMPLATE,NOLFXI,REGOPT,CARDTYPE=FCRM -o GV7RTL.obj
lnk101 GV7RTL.obj -o GV7RTL.fcm -L RUN -L ZCON --json-symbols GV7RTL.json --external-syms csects-G16.json

In other words, from the HAL/S source GV7RTL.hal, produce an object file GV7RTL.obj, and then link GV7RTL.obj to produce an AP-101S memory image file GV7RTL.fcm.  These operations succeed, but ... so what?  Now we've got GV7RTL linked and ready as a memory image, but what can we do with it?  We certainly couldn't run it in isolation, without the rest of PASS, even if we happened to know what GV7RTL is supposed to do!  (At least I don't, and forgive me for projecting, but I carelessly assume that you don't either.)  If this were your own HAL/S project rather than mine, you might just be pleased that it had succeeded at all, and leave it at that.

In the case of OI340600, though, I've left out a crucial detail of significance, which is that beyond just providing us with CSECT addresses, the MAFGEN disassemblies also contain the complete contents of GPC memory in hexadecimal form ... though admittedly it's a bit of a chore to extract it.  But extracting the memory image is valuable in and of itself, since in principle you could run that dumped GPC memory in an AP-101S emulator as-is, without even compiling anything in the first place! 

Did I bury the lede on that?  Should I have revealed earlier that compiling, assembling, and linking PASS source code is an unnecessary waste of time?  No, because it's not!  While we have MAFGEN disassemblies for OI340600, we don't have them for any other PASS or BFS version, and in particular don't have them for OI301700.  So while having the MAFGEN disassemblies is certainly very valuable in the sense of making sure our entire system of Virtual AGC development tools is working fine, it's not a solution for getting an executable version of BFS or for any version of PASS other than OI340600.  If we ever expect OI301700 to run, we'd need all the compilation, assembly, and linking we can get, and it better be 100% exact.  Hopefully after perfecting it with OI340600, it will be!

But I digress!  An additional feature of unlinkMAFGEN.py is that beyond just extracting lists of CSECTs vs addresses, at the same time it also extracts the complete memory dumps (G16.fcm, G2.fcm, and so on), in the form of files compatible with Don Schmidt's emulator.  And not only that, among the tools we can find in Don Schmidt's repository is one he calls fcmcmp.  Given a list of CSECTs, fcmcmp will compare two memory-image files, but only the contents of the CSECTs that are relevant.  In our example of GV7RTL, the memory dump for load G16 as a whole is huge, while that for GV7RTL is small in comparison, but only the 11 CSECTs that end up being included in GV7RTL are actually compared, while the remainder of G16 is simply ignored.
Note:  There are other subtleties that fcmcmp helps us to deal with as well.  In principle, before any programs are loaded into it — i.e., prior to the Initial Program Load (IPL) —, every (halfword) location in AP-101S memory at (halfword) addresses less than 0x20000 is loaded with the default value 0xC9FB, while each location at a higher address is loaded with the default value 0xC6C6.  When a program is loaded into the AP-101S memory, by design it's kind of like a swiss cheese, in that not every memory location in every CSECT is necessarily loaded with a specific value: some locations might simply be left open, and whatever value had been preloaded at the location still "shines through" the opening.  Then too, the IPL process may involve moving around chunks of memory from one location to another.  The net effect is that sometimes an otherwise-uninitialized location in memory may contain a value of 0xC9FB, or it may contain a value of 0xC6C6, or frequently may even have become initialized by the IPL to 0x0000.  But fcmcmp sees through this little trick, and so during the comparison process it will match 0x0000 against either of 0xC6C6 or 0xC9FB without treating them as errors.
What does such a comparison look like?  Well, for GV7RTL, the invocation is like so:
fcmcmp --csect-table csect-G16.json GV7RTL.json GV7RTL.fcm G16.fcm
And the results of the comparison?  Like so:
Note: images differ in size (147478 vs 330394 halfwords)
OK: #QVV6D3 @ 003CA (2 halfwords)
OK: #QMM1DNP @ 004EE (2 halfwords)
OK: #ZGV7RTL @ 00516 (2 halfwords)
OK: #QMV6DN @ 00518 (2 halfwords)
OK: #QVO6DN @ 0051A (2 halfwords)
OK: #DGV7RTL @ 0B4EC (28 halfwords)
OK: VO6DN @ 100F8 (20 halfwords)
OK: MV6DN @ 1010C (24 halfwords)
OK: #CGV7RTL @ 1016A (176 halfwords)
OK: MM1DNP @ 120B6 (18 halfwords)
OK: VV6D3 @ 24006 (16 halfwords)

PASS: all 11 sections match

And our conclusions?  At least for GV7RTL.hal and for flight phase OPS 1 (load G16), the HAL/S compiler, the linker, and the extracted memory dump from the GPC via the MAFGEN disassembly and unlinkMAFGEN2.py, all agree perfectly.  The system works!  It only remains to check the other ~1500 or so source-code files vs all 8 memory loads, and our confidence for OI340600 will be complete.

I promised earlier to describe the format of the CSECT/address files, so that you might form one of your own if you wanted to use the partial-linking technique in this section on a project of your own.  This information is in the form of a so-called JSON file that's laid out as
{
"CSECT1": { ... },
"CSECT2": { ... },
"CSECT3": { ... },
...
}

where CSECT1, CSECT1, and so on, are the names of the CSECTs.  And not just the CSECTs in the object files you're not supplying, but in the ones you are supplying the linker as well.  As far as the entry for any given CSECT is concerned, it'll be laid out like this:

{
"start": N1,
"end": N2,
"hal": "NAME",
"type": "TYPE",
"invariant": B,
"contents": {
"SYMBOL1": M1,
"SYMBOL2": M2,
 "SYMBOL3": M3, ...
}
}

It's valuable to note that the fields in these objects were originally determined by whatever information I thought it was feasible for unlinkMAFGEN2.py to extract from a MAFGEN disassembly, and not necessarily by the minimum amount of information I thought would be needed for linking purposes.  In other words, there's extra stuff here that you wouldn't need for linking, and therefore wouldn't need to add to your own CSECT/address file.  Fields not needed by the linker will be ignored, and not all of the fields shown here will necessarily be present or used by the linker.  Provide what you can, and then react to whatever complaints the linker throws at you!  But any symbol that's referenced by your object code will definitely have to be available within the CSECT/address file or else lnk101 will fail.

Reverse-Engineering Assistance

To facilitate reverse engineering of the AP-101 object-file format, I've provided a Python 3 module (readObject101S.py), which also can be used as a stand-alone program, to parse the object files produced by the HAL/S compiler (or by ASM101S).  And Don has provided an alternative called ibmobjdump.

For example, consider this HAL/S program 140-STATISTICS from the book Programming in HAL/S:
  STATISTICS:
PROCEDURE(DATA) ASSIGN(LO_VAL, HI_VAL, MEAN);
DECLARE DATA ARRAY(*) SCALAR;
DECLARE SCALAR,
LO_VAL, HI_VAL, MEAN;
LO_VAL = MIN(DATA);
HI_VAL = MAX(DATA);
MEAN = SUM(DATA) / SIZE(DATA);
CLOSE STATISTICS;

Upon compiling this file with the modern HAL/S compiler (HALSFC),

HALSFC 140-STATISTICS.hal --parms="LIST,VARSYM,DECK,CARDTYPE=YC"

we get not only a code-generation report (pass2.rpt), which in abridged form looks like the following,

PAGE 2
SYMBOL TYPE ID ADDR LEN(HEX) LEN(DEC) BLOCK NAME


#CSTATIS SD 0001 000000 00001D 29 STATISTICS
#ZSTATIS SD 0002 000000 000002 2
#DSTATIS SD 0003 000000 000002 2
#QEMIN ER 0004
#QEMAX ER 0005
#QESUM ER 0006
------------------------------------------------------------------------------------------------------------------------------------------------------
PAGE 3
LOC CODE EFFAD LABEL INSN OPERANDS SYMBOLIC OPERAND


0000000 ST#1 EQU *
00000 #ZSTATIS CSECT ESDID= 0002
00000 00000E00 DC A'00000E00' STATISTICS
00000 #CSTATIS CSECT ESDID= 0001
0000000 STATISTI EQU * STATISTICS
00000 E9F3 0000 LHI R1,0() TIME: 0.25; #DSTATIS
00002 B914 0005 STH R1,5(R0) TIME: 0.5
00003 E0FB 0018 IAL R0,24() TIME: 0.5
00005 EB01 0000 LA R3,0(R1) TIME: 0.25
00006 BB24 0009 STH R3,9(R0) TIME: 0.5
0000007 ST#2 EQU *
0000007 ST#3 EQU *
0000007 ST#4 EQU *
00007 9A30 000C LH R2,12(R0) TIME: 0.25; DATA
00008 1D1C 000E L R5,14(R0) TIME: 0.25; DATA+2
00009 E4F7 3800 BAL@# R4,0(R1,R3) TIME: 10.0 (SEE POO); #QEMIN
0000B 9B40 0010 LH R3,16(R0) TIME: 0.25; LO_VAL
0000C 3803 0000 STE F0,0(R3) TIME: 0.5
000000D ST#5 EQU *
0000D 9A30 000C LH R2,12(R0) TIME: 0.25; DATA
0000E 1D1C 000E L R5,14(R0) TIME: 0.25; DATA+2
0000F E4F7 3800 BAL@# R4,0(R1,R3) TIME: 10.0 (SEE POO); #QEMAX
00011 9B48 0012 LH R3,18(R0) TIME: 0.25; HI_VAL
00012 3803 0000 STE F0,0(R3) TIME: 0.5
0000013 ST#6 EQU *
00013 9A30 000C LH R2,12(R0) TIME: 0.25; DATA
00014 1D1C 000E L R5,14(R0) TIME: 0.25; DATA+2
00015 E4F7 3800 BAL@# R4,0(R1,R3) TIME: 10.0 (SEE POO); #QESUM
00017 1E1C 000E L R6,14(R0) TIME: 0.25; DATA+2
00018 3AEE CVFL F2,R6 TIME: 1.75
00019 68E2 DER F0,F2 TIME: 7.25
0001A 9B50 0014 LH R3,20(R0) TIME: 0.25; MEAN
0001B 3803 0000 STE F0,0(R3) TIME: 0.5
000001C ST#7 EQU *
000001C LBL#2 EQU *
0001C 97E8 SRET 7,R0 TIME: 17.5
00002 #DSTATIS CSECT ESDID= 0003
00002 000000 ORG *-2
00000 000002 ORG *+2
END

but also an object file (cards.bin).  Parsing that object file using readObject101S.py,

readObject101S.py cards.bin

we obtain something similar to the following report:

0000: 	type=SYM ident="I**20001" size=002E
0050: type=SYM ident="I**20002" size=002E
00A0: type=SYM ident="I**20003" size=0022
00F0: type=ESD ident="I**20004" size=0030 esdid=0001
symbol1: name="#CSTATIS" type=SD address=000000 length=003A AMODE24 RMODE24 RW
symbol2: name="#ZSTATIS" type=SD address=000000 length=0004 AMODE24 RMODE24 RW
symbol3: name="#DSTATIS" type=SD address=000000 length=0004 AMODE24 RMODE24 RW
0140: type=ESD ident="I**20005" size=0030 esdid=0004
symbol1: name="#QEMIN " type=ER
symbol2: name="#QEMAX " type=ER
symbol3: name="#QESUM " type=ER
0190: type=TXT ident="I**20006" offset=000000 size=0004 esdid=0002
data: 00 00 0E 00
01E0: type=TXT ident="I**20007" offset=000000 size=0004 esdid=0003
data: 00 00 00 16
0230: type=TXT ident="I**20008" offset=000000 size=0038 esdid=0001
data: E9 F3 00 00 B9 14 E0 FB 00 18 EB 01 BB 24 9A 30
1D 1C E4 F7 38 00 9B 40 38 03 9A 30 1D 1C E4 F7
38 00 9B 48 38 03 9A 30 1D 1C E4 F7 38 00 1E 1C
3A EE 68 E2 9B 50 38 03
0280: type=TXT ident="I**20009" offset=000038 size=0002 esdid=0001
data: 97 E8
02D0: type=RLD ident="I**20010" size=0028
relocation=0003 position=0001 flags=(0,0,A,1,0,0) address=000002
relocation=0004 position=0001 flags=(0,0,A,1,0,0) address=000014
relocation=0005 position=0001 flags=(0,0,A,1,0,0) address=000020
relocation=0006 position=0001 flags=(0,0,A,1,0,0) address=00002C
relocation=0001 position=0002 flags=(0,0,V,1,0,0) address=000000
0320: type=END ident="I**20011" idrType="2"
translator="HAL/SREL3 V0 24331"
processor="RSB-XCOM-I000924239"
--------------------------------------------------------------------------------
SYM-Record Summary:
CONTROL offset=000000 name="#CSTATIS"
DUMMY offset=000000 name="STACK"
DATA offset=000030 name="STACKEND" datatype=H
DUMMY offset=000000 name="HALS/FC"
DUMMY offset=000000 name="HALS/END"
CONTROL offset=000000 name="#CSTATIS"
INSTRUCTION offset=000002 name="D24331"
INSTRUCTION offset=000002 name="T2883796"
CONTROL offset=000000 name="#ZSTATIS"
DATA offset=000000 datatype=Z
CONTROL offset=000000 name="#DSTATIS"
DATA offset=000000 datatype=Z

Whereas ibmobjdump,
ibmobjdump -x cards.bin
instead produces the report
SYM  46 bytes of packed symbol data
SYM 46 bytes of packed symbol data
SYM 34 bytes of packed symbol data
ESD [ 1] SD #CSTATIS addr=00000 len=29 hw
ESD [ 2] SD #ZSTATIS addr=00000 len=2 hw
ESD [ 3] SD #DSTATIS addr=00000 len=2 hw
ESD [ 4] ER #QEMIN
ESD [ 5] ER #QEMAX
ESD [ 6] ER #QESUM
TXT [ 2] #ZSTATIS addr=00000 4 bytes
00000: 00 00 0E 00
TXT [ 3] #DSTATIS addr=00000 4 bytes
00000: 00 00 00 16
TXT [ 1] #CSTATIS addr=00000 56 bytes
00000: E9 F3 00 00 B9 14 E0 FB 00 18 EB 01 BB 24 9A 30
00008: 1D 1C E4 F7 38 00 9B 40 38 03 9A 30 1D 1C E4 F7
00010: 38 00 9B 48 38 03 9A 30 1D 1C E4 F7 38 00 1E 1C
00018: 3A EE 68 E2 9B 50 38 03
TXT [ 1] #CSTATIS addr=0001C 2 bytes
0001C: 97 E8
RLD YCON(+) #DSTATIS -> #CSTATIS addr=00001 flags=00
RLD YCON(+) #QEMIN -> #CSTATIS addr=0000A flags=00
RLD YCON(+) #QEMAX -> #CSTATIS addr=00010 flags=00
RLD YCON(+) #QESUM -> #CSTATIS addr=00016 flags=00
RLD ZCON/addr(+) #CSTATIS -> #ZSTATIS addr=00000 flags=10
END

Of course, the documentation needs to be consulted even to understand the parsed information in the report in any detail.  Nevertheless, even on a superficial reading, this report tells us that the object file contains 11 "records", of type SYM (3), ESD (2), TXT (4), RLD (1), and END (1), and reveals how various features easily visible in the HAL/S source code are represented in the object file.  You may notice that the report doesn't really provide much information about individual SYM (symbol-table) records.  That's because all SYM records need to be conjoined end-to-end, and it is only the conjoined record that contains parsable information; whereas the individual SYM records are not separately parsable.  Parsing the conjoined record is where the "SYM-Record Summary" at the end of the report comes from.

In the reverse-engineering report we see items obviously related in a general way to the HAL/S source code, though the relationship is not immediately clear in detail, such as the symbols #CSTATIS, #ZSTATIS, #DSTATIS, #QEMIN, #QEMAX, and #QESUM.  Some of the details are filled in by the "Basic HAL/S Programming" course's section "HAL/S CSECTS".  That course explains that each HAL/S "compilation unit" is automatically assigned a 6-character "generic name" by removing all underscores and truncating to 6 characters.  (It's up to the programmer to insure that all of the generic names are unique!)  Since in the example we're using the block being compiled is named STATISTICS, the generic name is just "STATIS".  The compiler then generates names for the various control sections it likes to create by prefixing various 2-character strings to the generic name.  You can see the hopefully-full list of prefixes at the hyperlink just given, but the ones relevant to our particular example are:
Aside:  A COMSUB is a HAL/S compilation unit containing data or code (such our STATISTICS PROCEDURE) accessible by other compilation units.  ZCON code and REMOTE data are "far away" in the AP-101S address space from the code accessing them, and hence are accessed by different AP-101S instruction types from "nearby" data or code. The functions EMIN, EMAX, and ESUM are AP-101S assembly-language functions from the HAL/S runtime library.

Alas, the object file is also seen to have a number of items not directly observable in the original HAL/S code but rather presumably transparently generated by the HAL/S compiler itself; examples are the symbols STACK, STACKEND, HALS/FC, HALS/END, D24324, and so on.

Some of these items (symbols STACK, STACKEND) appear in the assembly-language source code for HAL/S runtime-library files like ACOS, and thus apparently relate to common features HAL/S uses internally.  Specifically, I believe the report may be telling us that the DSECT called STACK is 0x30 (STACKEND) halfwords in length. 

Aside: Recall that because of the nature of the HAL/S language, the HAL/S compiler can determine at compile time the maximum stack required by any compilation unit, although the assembler cannot do so for assembly-language  files.

Some other non-obvious symbols (D24324, T4041882) seem by my reckoning to relate to embedded datestamps and timestamps:
Still other symbols (such as HALS/FC and HALS/END) remain to be interpreted.  Of course, I can invent explanations for anything: Perhaps the presence of symbol HALS/FC is simply the compiler's way of telling us that the compilation was performed by the HAL/S-FC compiler (as opposed to the HAL/S-360 compiler).  But then, perhaps not.  Really, I haven't a clue!  If we're lucky, time will tell.  I suppose that if reverse engineering obsolete object-file formats were easy, those formats would be included as free bonus items in boxes of breakfast cereal.

See the source code for readObject101S.py for further documentation, if any.
Aside:  Earlier when I mentioned the topic of reverse engineering the PILOT format used for BFS object files, and in particular the possible assistance offered by the tool coPILOT.py, I threatened to briefly return to the topic of BFS later on.  It's now later on!  You may find the comparison of the BFS object code to the PFS object code interesting. When 140-STATISTICS is compiled by the BFS compiler rather than the PFS compiler, two object files are produced:  cards/#CSTATIS and cards/#ZSTATIS.  The assembly language produced is also slightly different, but only in that an extra instruction "SVC 0()" is inserted at the very end, so I won't bore you with yet another printout of the generated assembly language.  The following is how coPILOT.py parses the two object files.  I don't think that a symbol table for local symbols is included in the object files, even when the compiler's VARSYM option is selected.  Another thing to note is that coPILOT.py reports all addresses and sizes or lengths using halfword addressing, whereas readObject101S.py does so by byte addressing.  Thus addresses and sizes or lengths reported by coPILOT.py are roughly half of the corresponding ones reported by readObject101S.py.  Oh well, a different day (or year! (or decade!)), a different design choice! 
Member cards/#CSTATIS
0000: Start
origin=0000
0004: CSD (CSECT Definition)
esdid=1 #CSTATIS length=001F stacksize=0018 flags=800A HAL=STATISTICS
001E: CSD (CSECT Definition)
esdid=2 #DSTATIS length=0002 stacksize=0000 flags=4000
002E: EXR (External Reference Definition)
esdid=3 #QEMIN version=0003 tbd=8000
003C: EXR (External Reference Definition)
esdid=4 #QEMAX version=0003 tbd=8000
004A: EXR (External Reference Definition)
esdid=5 #QESUM version=0003 tbd=8000
0058: PTX (Protected text)
offset=0000 csect=2 #DSTATIS
0000
0060: PTX (Protected text)
offset=0000 csect=1 #CSTATIS
E9F3 0000 B914 E0FB 0018 EB01 BB24
0074: PTX (Protected text)
offset=0007 csect=1 #CSTATIS
9A30 1D1C E4F7 3800 9B40 3803 9A30 1D1C E4F7 3800 9B48 3803 9A30 1D1C E4F7 3800
1E1C 3AEE 68E2 9B50 3803 C9FB 0000 97E8
00AA: RLD (Relocation data)
rel.id=2 #DSTATIS pos.id=1 #CSTATIS flags=0010 address=0001
rel.id=3 #QEMIN pos.id=1 #CSTATIS flags=0020 address=000A
rel.id=4 #QEMAX pos.id=1 #CSTATIS flags=0020 address=0010
rel.id=5 #QESUM pos.id=1 #CSTATIS flags=0020 address=0016
00CC: END
cards=10
hal compiler="HAL/S BFC-"
hal version=1700
timestamp=2026-02-16 06:53:10
xpl compiler="RSB-XCOM-I"
xpl version=0.90
Member cards/#ZSTATIS
0000: Start
origin=0000
0004: EXR (External Reference Definition)
esdid=6 #CSTATIS version=0000 tbd=800A HAL=STATISTICS
001C: CSD (CSECT Definition)
esdid=7 #ZSTATIS length=0002 stacksize=0001 flags=400A HAL=STATISTICS
0036: PTX (Protected text)
offset=0000 csect=2 #DSTATIS
0000 0E00
0040: RLD (Relocation data)
rel.id=1 #CSTATIS pos.id=2 #DSTATIS flags=002C address=0000
004A: END
cards=5
hal compiler="HAL/S BFC-"
hal version=1700
timestamp=2026-02-16 06:53:10
xpl compiler="RSB-XCOM-I"
xpl version=0.90

Unexpected Hurdles

Here are some things I find that differ between AP-101 object files and System/360 object files, or at least which are unexpected from reading the System/360 documentation.  Some of these things you'll already have seen above (but without explanation) in the reports produced by reverse-engineering software developed partially using the inferences drawn below.  But reverse engineering is a messy, iterative process, so it's seldom easy to give a clean end-to-end narrative.

Partial Linking By the Assembler Itself

The expected:  In System/360 assembly language, code and data are partitioned into a series of named "control sections", generically referred to as CSECTs.  It should be the case that from the assembler's point of view, "addresses" begin at 0 and increment upward in each section, and that the linker program subsequently reassigns different starting addresses to the sections generated by the assembler, while adjusting all of the addresses referenced within the various sections accordingly.

The unexpected:  However, it is clear from the surviving legacy AP-101S assembly listings that there are departures from these principles.  Consider the following excerpt of the legacy assembly listing for the SQRT function of the HAL/S-FC runtime library:
                                 18 SQRT     AMAIN INTSIC=YES                                               00001300
19+***********************************************************************
20+*
21+* PRIMARY ENTRY POINT
22+*
23+***********************************************************************
00000 24+SQRT CSECT 01-AMAIN
26 * COMPUTES SQUARE ROOT IN SINGLE PRECISION 00001400
27 INPUT F0 SCALAR SP 00001500
0000000 28+F0 EQU 0 01-INPUT
30 OUTPUT F0 SCALAR SP 00001600
32 WORK R1,R5,R6,R7,F1,F2,F3 00001700
0000001 33+R1 EQU 1 01-WORK
0000005 34+R5 EQU 5 01-WORK
0000006 35+R6 EQU 6 01-WORK
0000007 36+R7 EQU 7 01-WORK
0000001 37+F1 EQU 1 01-WORK
0000002 38+F2 EQU 2 01-WORK
0000003 39+F3 EQU 3 01-WORK
00000 E9F3 0032 0032 41 LA R1,A 00001800
0000032 42 USING A,R1 00001900
00002 7AE0 43 START LER F2,F0 00002000
00003 DE88 0026 0022 44 BNP ERROR ARGUMENT NEGATIVE OR ZERO 00002100
45 * 00002200
00004 27E8 46 LFXR R7,F0 TRANSFER TO GENERAL REGISTER 00002300
00005 76E6 47 XR R6,R6 00002400
00006 FE1C 0007 48 SLDL R6,7 Q & MANTISSA IN R7 00002500
00007 F660 0018 49 SLL R6,24 CHAR OF ANSWER - (Q+32) IN R6 00002600
00008 1FE7 50 LR R7,R7 CHECK FOR Q=1 00002700
00009 DD0C 000D 0003 51 BCF 5,GORP 00002800
52 * 00002900
0000A B0E6 0100 0100 53 AHI R6,X'0100' ADD 1 TO CHAR FOR Q=1 00003000
0000C E909 0002 54 LA R1,2(R1) FULLWORD INDEX 00003100
55 * 00003200
0000D 1DE6 56 GORP LR R5,R6 CHARACTERISTIC OF ANSWER IN R5 00003300
0000E F705 0001 57 SRA R7,1 FIXED M/2 + 'QQ' IN TOP BITS 00003400
0000F 0711 003A 58 A R7,C ADD C/2, AND ELIMINATE 'QQ' 00003500
00010 1E09 0036 59 L R6,B -B/2 OR -B/8 AT BIT 7(MANTISSA POS.) 00003600
00011 4EE7 60 DR R6,R7 (R6) = (4**(-Q))(-B/(C+M)) AT BIT 7 00003700
00012 0601 0032 61 A R6,A A OR A/4 AT BIT 7 + CHAR=32 00003800
00013 06E5 62 AR R6,R5 RESTORE CHARACTERISTIC OF ANSWER 00003900
00014 29EE 63 LFLR F1,R6 00004000
64 * 00004100
65 DROP R1 00004200
66 * 00004300
67 * TWO PASSES OF THE NEWTON-RAPHSON ITERATION 00004400
68 * 00004500
00015 68E1 69 DER F0,F1 00004600
00016 50E1 70 AER F0,F1 00004700
00017 7BF7 0013 002C 0013 71 LE F3,FHALF 00004800
00019 60E3 72 MER F0,F3 00004900
0001A 6AE0 73 DER F2,F0 00005000
74 * 00005100
0001B B6E6 FF00 FF00 75 NHI R6,X'FF00' PUT CHARACTERISTIC OF 00005200
0001D 06F7 000F 002E 000F 76 A R6,ROUND ANSWER IN ROUND 00005300
0001F 29EE 77 LFLR F1,R6 DIGIT, AND ADD 00005400
00020 50E1 78 AER F0,F1 TO INTERMEDIATE RESULT 00005500
79 * 00005600
00021 58E2 80 SER F0,F2 00005700
00022 60E3 81 MER F0,F3 00005800
SQRT -- SINGLE PRECISION SQUARE ROOT FUNCTION PAGE 3
LOC OBJECT CODE ADR1 ADR2 SOURCE STATEMENT AP101S 3.0 09.39 07/22/05
00023 50E2 82 AER F0,F2 ANSWER IN F0 00005900
83 * 00006000
84 EXIT AEXIT AND RETURN 00006100
85+*********RETURN TO CALLER**********************************************
00024 86+EXIT DS 0H 01-AEXIT
00024 9914 0005 87+ LH 1,5(0) RESTORE PROGRAM DATA BASE 01-AEXIT
00025 C7EC 88+$RET1 BCRE 7,4 RETURN TO CALLER 01-AEXIT
89+***********************************************************************
91 * 00006200
00026 DC0E 0024 0003 92 ERROR BCB 4,EXIT EXIT IF ARG=0 00006300
93 AERROR 5 ARGUMENT<0 00006400
94+*********ISSUE SEND ERROR SVC******************************************
00027 C9FB 0030 0030 95+ SVC AERROR1 ISSUE SEND ERROR SVC 01-AERRO
96+*********SEND ERROR SVC RETURNS CONTROL FOR STANDARD FIXUP*************
00029 78E8 97 LECR F0,F0 FIXUP: GET |ARG| 00006500
0002A DFA6 0002 0029 98 B START AND TRY AGAIN 00006600
99 * 00006700
0002B C9FB
0002C 40800000 100 FHALF DC E'0.5' 00006800
0002E 00000001 101 ROUND DC X'00000001' 00006900
102 * 00007000
103 ADATA 00007100
104+*********DATA CSECT****************************************************
00030 105+ LTORG 02-ERRPA
106+****************ERROR PARAMETER AREA***********************************
00030 107+#LSQRT CSECT 02-ERRPA
108+*** SQRT SENDS THE FOLLOWING ERROR 02-ERRPA
110+*** ERROR NUMBER 5 IN GROUP 4 02-ERRPA
00030 0014 112+AERROR1 DC H'20' SVC CODE FOR SEND ERROR 02-ERRPA
00031 0405 113+ DC Y(4*256+5) 8 BIT GROUP AND NUMBER 02-ERRPA
114+****************END OF ERROR PARAMETER AREA****************************
00032 115+#LSQRT CSECT 01-ADATA
00032 21AE7D00 116 A DC X'21AE7D00' 1.6815948=A + X'20' 00007200
00034 206B9F40 117 DC X'206B9F40' 0.4203987=A/4 + X'20' 00007300
00036 FF5B02F1 118 B DC X'FF5B02F1' -1.2889728=B 00007400
00038 FFD6C0BD 119 DC X'FFD6C0BD' -0.3222432=B/4 00007500
0003A 35CFC610 120 C DC X'35CFC610' 0.8408065=C/2 00007600
0003C 75CFC610 121 DC X'75CFC610' 0.8408065=C/2 + X'40' 00007700
122 ACLOSE 00007800
123+ END 01-ACLOS

As I've highlighted in red, there are two CSECTs in this excerpt, and they are called "SQRT" and "#LSQRT".  (The #LSQRT section has two separate CSECT statements, but don't be distracted by that, as it's not significant in respect to the points I want to make.  Also, don't be distracted by the lines I've highlighted in green, which I'll discuss in a moment.)

Unlike the expectation we (or at least I) would have, The #LSQRT section's addresses do not restart at 0, and instead the assembler continues to assign it addresses which continue incremented from the end of the SQRT section.  It is as if the assembler has already performed the step of partially linking these two sections of the code.  Why is this?

One might argue that while whoever coded the original assembler thought (for some unknown reason) that it was a good idea to present the assembly report in this fashion, it really doesn't make any difference:  As long as the object file produced by the assembler still represents these as independent sections, the linker can still reassign the addresses however it likes, with the result that after the final linking it may no longer be the case that the #LSQRT section immediately follows the SQRT section in memory.

Alas, that rationalization is incorrect.  Consider now the lines highlighted in green, and in particular this instruction:

LA	R1,A

For the assembler to assemble an instruction like this, in which a "base register" is not explicitly stated as an operand itself, it needs to somehow determine for itself the appropriate base register.  There are only two ways this can happen:

  1. If the operand (A in this case) is in that same code section as the instruction itself, in which case the appropriate base register is general register 3.
  2. Or else the operand is in an address block which has been made known the assembler via a previous USING pseudo-op.  The USING pseudo-op informs the assembler as to what assumption it can make about the contents of the various general registers.

In this example, though, neither of these conditions obtains.  As for condition #1, the instruction appears in the SQRT code section, but its operand (A) appears in a different section, namely #LSQRT.  As for condition #2, the only USING pseudo-op follows the instruction rather than precedes it, so there is no way for the assembler to make any assumptions about register contents.

How, then, has the assembler been able to assemble this LA instruction?  Well, perhaps there are possibilities that haven't occurred to me, but it appears to me that there's only one explanation:  The assembler really has already linked the SQRT and #LSQRT code sections, so that they are in a fixed relationship to each other, and in particular there is really no separate #LSQRT section as such in the object file output by the assembler.  I.e., #LSQRT has become just a label for an address within the SQRT section, and the actual linker program cannot change this at any later time.

Non-System/360 Datatype 0x84

To understand the comments below, it may help to refer back to the earlier example of a reverse-engineering report.

In so-called SYM records within an object file, each symbol representing "data" has an associated "datatype", consisting of a single-byte number.  For example, the datatype 0x00 represents character data (C), 0x04 represents hexadecimal data (X), 0x18 represents single-precision floating-point (E), and so on.  However, there's one datatype in AP-101S object files, 0x84, that doesn't appear in the available system/360 object-file documentation, leading us to speculate that it represents some AP-101S-only datatype not available on System/360. 

As it happens, there's discussion on our page about the modern AP-101 assembler that concerns the available datatypes.  That discussion points out a single AP-101S-specific datatype, Z, that's speculated to be unrelated to the System/360 datatype also designated as Z ("zoned decimal", 0x34).  For AP-101S, it was speculated that the Z datatype instead was something called a ZCON, which as kind of long-range address constant for items not present in nearby memory and hence addressable by only a restricted class of instructions. 

We are thus tempted to equate datatype 0x84 in SYM records with AP-101S ZCONs.  At the moment this is pure speculation that perhaps doesn't fit perfectly, but the reverse-engineering report nevertheless treats it as factual and reacts to these datatypes as follows:

Encoding of Character Data in Object Decks

In System/360 object files, all character data is encoded in EBCDIC.  This is not true for AP-101 object files. Some text is encoded instead in the character set of the Space Shuttle's Display Electronics Unit (DEU).

The DEU character set is depicted in the table below.  It is an ASCII-like character set, in the sense that almost everywhere the printable characters or control codes overlap with printable ASCII characters or control codes, the numerical encoding matches ASCII as well.

Reverse engineering reveals at least some of the cases in which DEU encoding is used in place of EBCDIC in the object files:

Classification of Textual Data
Encoding
Textual data used only at compile/assembly/link time, but not accessible to the executable AP-101 program itself at runtime.  Examples of this kind of data include (but are not limited to):
  • Names of symbols appearing in the source code or transparently generated by the compiler or assembler.
  • Names of object-file record types ("SYM", "ESD", "TXT", "REL", "END").
  • "Ident" fields (columns 72-80) of assembly-language source-code cards.
EBCDIC
Character constants appearing in HAL/S source code, such as DECLARE CHARACTER CONSTANT statements, DECLARE CHARACTER INITIAL constants, or character literals. DEU
Character data appearing in AP-101 assembly-language source code, such as DC pseudo-ops or so-called "literals" like "=C'...'".
TBD (currently, assumed EBCDIC)

Consider this HAL/S program adapted from the book Programming in HAL/S, with a few of its character-string literals highlighted in various colors:
  ROOTS: PROGRAM;
DECLARE SCALAR, A, B, C, D, ROOT1, ROOT2;
DO WHILE TRUE;
WRITE(6) ;
WRITE(6) 'Enter A, B, C (0,0,0 to quit):';
READ(5) A, B, C;
IF A = 0 AND B = 0 AND C = 0 THEN
DO;
WRITE(6) 'Quitting ...';
EXIT;
END;
D = B**2 - 4 A C;
IF D >= 0 THEN
DO;
D = D**0.5;
ROOT1 = (-B + D) / (2 A);
ROOT2 = (-B - D) / (2 A);
WRITE(6) 'Real roots of', A, 'X**2 +', B, 'X +', C,
'are:', ROOT1, ROOT2;
WRITE(6) 'Check:',
A ROOT1**2 + B ROOT1 + C,
A ROOT2**2 + B ROOT2 + C;
END;
ELSE
DO;
TEMPORARY RE, IM, RE2, IM2;
D = (-D)**0.5;
RE = -B / (2 A);
IM = D / (2 A);
WRITE(6) 'Complex roots of', A, 'X**2 +', B, 'X +',
C, 'are:', RE, '+/-', IM, 'i';
RE2 = RE**2 - IM**2; /* Square of RE2 +/- IM2 i. */
IM2 = 2 RE IM;
WRITE(6) 'Check:', A RE2 + B RE + C, '+/-',
A IM2 + B IM, 'i';
END;
END;
CLOSE ROOTS;
In comparison, here's an excerpt from the reverse-engineering report on the object-code file, with just some of the TXT records, and some portions of the TXT records that seemingly correspond to the literal strings highlighted in colors matching the HAL/S listing above:
	.
.
.
05A0: type=TXT ident="I**20019" offset=000024 size=0038 esdid=0003
data: 00 01 69 00 00 03 2B 2F 2D 00 00 10 43 6F 6D 70
6C 65 78 20 72 6F 6F 74 73 20 6F 66
00 06 43 68
65 63 6B 3A
00 04 61 72 65 3A 00 03 58 20 2B 00
00 06 58 2A 2A 32 20 2B
05F0: type=TXT ident="I**20020" offset=00005C size=0036 esdid=0003
data: 00 0D 52 65 61 6C 20 72 6F 6F 74 73 20 6F 66 00
00 0C 51 75 69 74 74 69 6E 67 20 2E 2E 2E 00 1E
45 6E 74 65 72 20 41 2C 20 42 2C 20 43 20 28 30
2C 30 2C 30 20 74

0640: type=TXT ident="I**20021" offset=000092 size=0008 esdid=0003
data: 6F 20 71 75 69 74 29 3A
.
.
.
You can verify for yourself, if you like, that the colored portions are encoded as DEU/ASCII, and hopefully take my word for it (since I wrote it!) that the reverse-engineering report generator uses EBCDIC-to-ASCII conversion to print out messages such as ident="I**20021".
Aside:  There's no easy way, as far as I know, using just information within the object decks themselves, to easily locate individual unnamed string literals within the TXT records of the object decks.  The way I did it above, was to assume that ASCII coding was used, and to look for the ASCII strings ... which I admit was a bit of a cheat, even though it doesn't invalidate the conclusions in any way.  In the next section, we'll learn enough to infer that the these strings should have been found in a CSECT called #DROOTS.  And in portions of the reverse-engineering report that I've omitted above, we would have seen that there is in fact a CSECT called #DROOTS and that it is assigned the ESDID 0003, which is the case in the excerpt above.  So indeed, everything is as it should be in this the best of all possible worlds.

PROGRAM Compilation Units

Object files generated from HAL/S source code containing a PROGRAM (i.e., the top-level compilation unit of a program, rather than a mere component of a program such as the PROCEDURE) have object files differing from the format described in the System/360 documentation and roughly covered in examples of reverse engineering above.

Such an object file begins with a block of records in the almost-System/360 format we've already described, but differs after an END record is encountered.  After an END record, we enter undocumented terra incognita, in which we can only rely upon whatever reverse engineering we can apply.

Consider this sample HAL/S source code for a PROGRAM based on one found in the book Programming in HAL/S:

  SIMPLE: PROGRAM;
DECLARE PI CONSTANT (3.14159266);
DECLARE R SCALAR;
WRITE(6) 'Input R to compute PI R**2, or -1 to quit.';
DO WHILE TRUE;
READ(5) R;
IF R < 0 THEN EXIT;
WRITE(6) 'R =', R, 'and PI R**2 =', PI R**2;
END;
CLOSE SIMPLE;

After compiling this file and parsing the object file, we find extraneous material following the END record.  If we pretend that this extraneous material consists of EBCDIC characters — which is only partially true —, it could be translated into ASCII essentially as follows:

" STACK $0SIMPLE                                                                 "
"cSYM a aaaSTART I**20018"
"cESD a abSTART aaaa aa $0SIMPLEcaaa I**20019"
"cTXT aaa a abU3aa I**20020"
"cRLD a acabaaac I**20021"
"cEND aaa ab 2HAL/SREL3 V0 24329RSB-XCOM-I000924239 I**20022"

Aside:  Strictly speaking, this isn't an ASCII translation of the extraneous data, because it has a few flourishes of my own to make it more readable.  Specifically, the enclosing quotes are mine, the non-printable control characters encoded numerically as 0x00 (NUL), 0x01 (SOH), and 0x02 (STX) are represented by the lower-case characters "a", "b", and "c", respectively, and there aren't actually any newlines.  But we can get the gist of it!

We need to digress for a moment before trying to make some sense of this extraneous nonsense.  In the earlier discussion of the object-file format, I glossed over certain simple facts that I assumed you could glean from the System/360 documentation if you were actually interested.  But those facts become relevant to the discussion now, so let state them:

So superficially at least, the extraneous material at the end of the object file starts with an aberrant record that does not begin with 0x02.  But after that aberrant record, the remaining extraneous material seems to follow the rules as if they comprised a completely separate object file.

Taking all of that into account, here's the kind of reverse-engineering report we actually see.  The extraneous material is highlighted in green, and I've invented a new record type (HDR, for "header") for what I've so far been calling the "aberrant" record:

0000: 	type=SYM ident="I**20001" size=002E
0050: type=SYM ident="I**20002" size=002E
00A0: type=SYM ident="I**20003" size=0029
00F0: type=ESD ident="I**20004" size=0030 esdid=0001
symbol1: name="$0SIMPLE" type=SD address=000000 length=0060 AMODE24 RMODE24 RW
symbol2: name="#ESIMPLE" type=SD address=000000 length=000C AMODE24 RMODE24 RW
symbol3: name="#DSIMPLE" type=SD address=000000 length=0052 AMODE24 RMODE24 RW
0140: type=ESD ident="I**20005" size=0030 esdid=0004
symbol1: name="@0SIMPLE" type=ER
symbol2: name="#QIOINIT" type=ER
symbol3: name="#QCOUT " type=ER
0190: type=ESD ident="I**20006" size=0020 esdid=0007
symbol1: name="#QEIN " type=ER
symbol2: name="#QEOUT " type=ER
01E0: type=TXT ident="I**20007" offset=000000 size=000C esdid=0002
data: 00 00 00 00 00 00 07 00 00 00 00 05
0230: type=TXT ident="I**20008" offset=000008 size=0004 esdid=0003
data: 00 01 00 12
0280: type=TXT ident="I**20009" offset=000000 size=0038 esdid=0001
data: E8 F3 00 00 E9 F3 00 00 B9 14 E0 FB 00 14 EB 11
BB 24 BE E8 BD E5 D0 FF 38 00 EA 4D D0 FF 38 00
BE E7 75 E5 D0 FF 38 00 EA 0B D0 FF 38 00 78 07
DD 04 DF 50 BE E8 BD E5
02D0: type=TXT ident="I**20010" offset=000038 size=0028 esdid=0001
data: D0 FF 38 00 EA 41 D0 FF 38 00 78 07 D0 FF 38 00
EA 21 D0 FF 38 00 7F 07 67 E7 67 05 78 E7 D0 FF
38 00 DF 7A C9 F9 00 00
0320: type=TXT ident="I**20011" offset=000000 size=0002 esdid=0003
data: 00 15
0370: type=TXT ident="I**20012" offset=000004 size=0004 esdid=0003
data: 41 32 43 F6
03C0: type=TXT ident="I**20013" offset=000010 size=0036 esdid=0003
data: 00 0D 61 6E 64 20 50 49 20 52 2A 2A 32 20 3D 00
00 03 52 20 3D 00 00 2A 49 6E 70 75 74 20 52 20
74 6F 20 63 6F 6D 70 75 74 65 20 50 49 20 52 2A
2A 32 2C 20 6F 72
0410: type=TXT ident="I**20014" offset=000046 size=000C esdid=0003
data: 20 2D 31 20 74 6F 20 71 75 69 74 2E
0460: type=RLD ident="I**20015" size=0038
relocation=0004 position=0001 flags=(0,0,A,1,0,0) address=000002
relocation=0003 position=0001 flags=(0,0,A,1,0,0) address=000006
relocation=0005 position=0001 flags=(0,0,A,1,0,0) address=000018
relocation=0006 position=0001 flags=(0,0,A,1,0,0) address=00001E
relocation=0005 position=0001 flags=(0,0,A,1,0,0) address=000026
relocation=0007 position=0001 flags=(0,0,A,1,0,0) address=00002C
relocation=0005 position=0001 flags=(0,0,A,1,0,0) address=00003A
04B0: type=RLD ident="I**20016" size=0030
relocation=0006 position=0001 flags=(0,0,A,1,0,0) address=000040
relocation=0008 position=0001 flags=(0,0,A,1,0,0) address=000046
relocation=0006 position=0001 flags=(0,0,A,1,0,0) address=00004C
relocation=0008 position=0001 flags=(0,0,A,1,0,0) address=000058
relocation=0001 position=0002 flags=(0,0,V,1,0,0) address=000004
relocation=0003 position=0002 flags=(0,1,A,1,0,0) address=000004
0500: type=END ident="I**20017" idrType="2"
translator="HAL/SREL3 V0 24331"
processor="RSB-XCOM-I000924239"
0550: type=HDR ident=" " length=15
text=" STACK $0SIMPLE"
055F: type=SYM ident="I**20018" size=0009
05AF: type=ESD ident="I**20019" size=0020 esdid=0001
symbol1: name="START " type=SD address=000000 length=0004 AMODE24 RMODE24 RW
symbol2: name="$0SIMPLE" type=ER
05FF: type=TXT ident="I**20020" offset=000000 size=0004 esdid=0001
data: E4 F3 00 00
064F: type=RLD ident="I**20021" size=0008
relocation=0002 position=0001 flags=(0,0,A,1,0,0) address=000002
069F: type=END ident="I**20022" entryAddress=000000 esdid=0001 idrType="2"
translator="HAL/SREL3 V0 24331"
processor="RSB-XCOM-I000924239"

--------------------------------------------------------------------------------
SYM-Record Summary:
CONTROL offset=000000 name="$0SIMPLE"
DUMMY offset=000000 name="STACK"
DATA offset=000028 name="STACKEND" datatype=H
DUMMY offset=000000 name="HALS/FC"
DUMMY offset=000000 name="HALS/END"
CONTROL offset=000000 name="$0SIMPLE"
INSTRUCTION offset=000002 name="D24331"
INSTRUCTION offset=000002 name="T2904859"
CONTROL offset=000000 name="#ESIMPLE"
DATA offset=000000 datatype=Z
CONTROL offset=000000 name="#DSIMPLE"
DATA offset=000000 datatype=Z
DATA offset=00000C name="R" datatype=E
CONTROL offset=000000 name="START"



This page is available under the Creative Commons No Rights Reserved License
Last modified by Ronald Burkey on 2026-03-31

Virtual AGC is
              hosted by ibiblio.org