Table of Contents

Introduction

Elsewhere we've talked about how to compile HAL/S-language programs, how to assemble AP-101S assembly-language programs, and how to link AP-101S object-code files to get executable AP-101S memory images.  The important topic of how to actually run any of these programs without an actual AP-101S computer and a Space Shuttle to provide the computer's peripheral devices remains.  Let's tackle that now.

The answer is emulation.  We provide "virtual machines" that run within your own computer — whether Linux, Mac OS, or Windows —, but which can run HAL/S or AP-101S code in some fashion.  There are several alternative ways to accomplish this feat.  You may consider these choices either a blessing or a curse, but you can try out any or all of them before deciding which approach is most suitable for your own purposes.

There are two broad classes of emulators available, namely those developed externally to the Virtual AGC Project as personal projects of their respective developers, versus those which are internal to the Virtual AGC Project.  However — to give credit where it's due! —, even the internal emulators haven't truly been developed by Virtual AGC, but instead have been created by A.I. coding agents (under direction, of course!) and are in some sense derivatives of the externally-developed emulators.  There are advantages and disadvantages to either.  With the external emulators, you're directly benefiting from the experience and insights of their developers.  With the internal emulators, you benefit from a faster maintenance cycle.  Or perhaps more accurately, we have control over maintenance for the internal emulators, and no control at all over maintenance for the external emulators.  The choice is yours.

Another useful classification relates to the nature of the executable programs being emulated.  Some of the emulators accept AP-101S memory images as alluded to above, and pretend to be an AP-101S computer, directly executing AP-101S machine code. Other emulators instead accept programs in a form that's known as the HALMAT Intermediate Language, and execute instructions of the HALMAT language.  Where would you get such a "HALMAT" program from, and why in the world would you want to?  Getting your hands on HALMAT code is not a problem, because HALMAT is the natural way that the HAL/S compiler communicates among its various passes; in other words, HALMAT is going to be produced by the HAL/S compilation process automatically, whether you happen to care about it or not.  There are various pro's and con's related to emulation via HALMAT versus via AP-101S:

Again, the choice is yours.

Installation

Refer directly to the particular document section below for the specific emulator you'd like to install.  You can choose to install any or all of the available emulators.  Or none of them, of course, though if so, I'd question why you're even reading this at all.

Why do I not just tell you what to install, rather than giving you this rigamarole about all of the possible emulators you might install if you had they slightest idea which of them were "better" or "worse"?  There are lots of shades of gray here.  I'm not unbiased, and being an egotist myself, I naturally gravitate towards the ones I had some personal involvement with, so you have to take anything I say with a grain of salt.  At this writing, my suggestions would be:

The Mystery of HALMAT

Supporting HALMAT is quite a bit trickier than supporting AP-101S machine language, not because of its structure, but for historical reasons.  So a few words of explanation are in order.

As you already know, the original HAL/S compiler used for converting Space Shuttle flight software written in the high-level language known as HAL/S into assembly-language object code for the Space Shuttle's general purpose computers was partitioned into several successive passes as follows:

Given that any emulation is going to be run on modern computers that are many times faster than the original AP-101S computers, with incredibly more memory, one might question the continued need for "optimization" at all.  So if you were going to run a HAL/S program by emulating its HALMAT rather than its AP-101S machine code, your development pipeline could conceivably reduce from

Compiler PASS1 → OPT → PASS2 → link → AP-101S emulator
to just

Compiler PASS1 → HALMAT emulator

What makes the latter even more compelling, at least at first glance, is that we have two separate versions of the HAL/S compiler's PASS1, and in the second pipeline can use the version of the compiler that's easier to install.

Sounds promising.  But alas, not perfect!  Where this concept has fallen apart up to now is the inadequacy of surviving documentation about HALMAT.  Without a satisfactory definition of the HALMAT language, how might we be expected to create an emulator for it?  On the other hand,  some HALMAT documentation has survived:
  1. A complete description was written up in 1971.  As the document itself explains, it "is essentially a handbook of an intermediate language HALMAT produced and used during the operation of the HAL360-V1 compiler".  In case it isn't obvious, HAL360-V1 was Intermetrics's very first attempt at a HAL compiler — not yet HAL/S —, producing object code running on an IBM System/360 computer rather than an AP-101S computer.  Actually, the document says only that HAL360-V1 ran on an IBM System/360, and doesn't explicitly say that the compiled form of the HAL software ran on a System/360; but the document nowhere mentions the AP-101 computer, and that seems very telling!  Besides which, the document specifically calls HAL360-V1 a "preliminary version", or as we might think of it, a proof of concept.  The problem is, that being "preliminary", details changed later!  How much was changed or added we can't tell from a superficial examination, but changes there definitely were.
  2. Intermetrics's much later document IR-60, "HAL/S-360 Compiler System Specification", has a very large Appendix A that specifies the HALMAT instructions in detail.  We have no assurance that this is the final form of the HALMAT language, but all later documentation refers back to IR-60 as the authoritative source, so I think we should probably recognize it as such as well.  Unfortunately, the only copy of IR-60 which has survived to fall into our hands is Revision 5 of the document, IR-60-5 from 1977, and this version contains only the pages that changed between Revision 4 and Revision 5.  The complete Appendix A, which from the page markings must contain at least 118 pages, has only 20 pages in our surviving copy!  Then too, a Revision 6 is known to have been in the JSC library at one time — though not any longer — and so potentially there could have been changes later even than in what has survived in our copy.  It could be that more-complete copies of IR-60 still exist somewhere, perhaps in the hands of a collector, but for now we have no reason to believe that they weren't all simply thrown away.
  3. Though not "documentation" as such, the HAL/S-FC compiler passes PASS1, OPT, and PASS2 all outputted and/or inputted HALMAT files, so potentially one could get additional information about HALMAT's individual instructions or groupings of instructions by looking through HAL/S-FC source code.
  4. Moreover, PASS1 has the optional capability of interleaving HAL/S statements with the derived HALMAT in the compilation reports it generates.  Similarly, PASS2 has the optional capability of interleaving HALMAT with the assembly-language instructions it generates in the compilation reports it generates.  (And even OPT, which normally doesn't output much of a report, has an analogous capability.)  Therefore, by crafting sample HAL/S source-code files and compiling them, it's possible to deduce more detail about HALMAT instructions as well.

Thus although complete documentation about HALMAT in a nicely-packaged, authoritative document seems to have escaped us, it is not necessarily clear that we couldn't regenerate a complete description of the language through reverse engineering.  And if we could do that, perhaps a HALMAT emulator could become a reality.  It is, however, very tricky to do so, or to justify without advance guarantee of success.

Zane Hambly (go, Zane!) made the first key steps in that direction.  Zane's key idea was to provide a parser that roughly served to parse the source code of the original HAL/S compiler (which was written in a computer language I call XPL/I, though the original developers misleadingly called it XPL).  This allowed picking out all references to how HALMAT was generated or used by the original compiler.  Zane's synthesis of all of that led to a preliminary description of HALMAT and its many instructions is in the form of a single document describing all that was then known or suspected about HALMAT.  Indeed, he went farther than that, and created a preliminary HALMAT emulator based on the synthesized specification.  The emulator itself is called yaHALMAT, and as we discussed in the installation section above, resides in Zane's own repository.  At this writing, the fly in the ointment is that after the extremely-rapid initial development of the HALMAT spec and of yaHALMAT, life and priorities intervened and progress came to a halt.  Zane seems clear that it will resume, and we'll keep our fingers crossed that he's right.  In the meantime, the work that has been done so far serves as a very powerful proof-of-concept.

The hiatus in yaHALMAT development just mentioned has motivated me to follow-up Zane's work by leveraging AI, and specifically ANTHROP\C Claude (using LLM Sonnet 5).  While this is pretty far-out thinking, Claude has proven up to the task, managing (albeit with guidance) to create both new documentation for HALMAT and a new emulator (yaHALMAT2, based loosely on yaHALMAT).

There's another tool for understanding HALMAT as well.  If you've read much of our discussions of the HAL/S language and the development tools provided for it, you'll have seen various examples of actual HALMAT.  But those examples have always been in the form of incomprehensible numerical codes provided in one form or another by reports output by the HAL/S compiler.  [Nota bene: Aside from being incomprehensible in themselves, they are also sometimes actually wrong, in that HALMAT instructions may be duplicated, may be shown out of order, and so on.]  Those bursts of HALMAT code, unsurprisingly, have little value to us today, or at least not as-is.  To help bridge the comprehensibility gap a little, I've written a little utility called unHALMAT.py that produces a surprisingly useful and fairly human-friendly version of the HALMAT produced by the compiler, by combining it with other available information like symbol tables, literal files, and so on.  Here's what unHALMAT.py can do with the HALMAT file generated by compiling our ubiquitous HELLO.hal program:
> HALSFC --clean --archive --parms=SRN,LSTALL,ADDRS,LISTING2 HELLO.hal
Results stored in the folder "archive.results/HALSFC HELLO.hal 2026-07-20 06-51-35.results".
Accessible also as "current.results" until next HALSFC run.
For previous runs see also "current-1.results", "current-2.results".
Compilation successful.
> cd current.results
> unHALMAT.py halmat.bin
7200 bytes read from halmat.bin
RECORD 0
HALMAT #0 (0x005, Start of HALMAT block): PXRC(1) 00 0 0
HALMAT #1 | 05C 00 00( 0) 00 00 00 01 00 00 (Matching XREC is at HALMAT #92)
HALMAT #2 (0x02B, Start of PROGRAM definition): MDEF(1) 00 0 0
HALMAT #3 | 001 00 01(SYT) 00 00 00 01 00 00 (Symbol #1: HELLO, PROGRAM LABEL)
HALMAT #4 (0x004, HAL/S statement marker): SMRK(1) 00 1 0
HALMAT #5 | 001 00 00( 0) 00 00 01 01 00 01 (HAL/S statement #1)

1 M| HELLO: |
1 M| PROGRAM; |

HALMAT #6 (0x004, HAL/S statement marker): SMRK(1) 00 1 0
HALMAT #7 | 002 00 00( 0) 00 00 00 01 00 00 (HAL/S statement #2)

2 M| DECLARE I INTEGER; |

HALMAT #8 (0x004, HAL/S statement marker): SMRK(1) 00 1 0
HALMAT #9 | 003 00 00( 0) 00 00 00 01 00 00 (HAL/S statement #3)

3 M| DECLARE POOKIE CHARACTER(20); |

HALMAT #10 (0x841, CHARACTER INITIAL value): CINT(2) 02 0 0
HALMAT #11 | 004 00 01(SYT) 00 00 00 01 00 00 (Symbol #4: MY_NAME, CHARACTER ALIGNED STATIC INITIAL)
HALMAT #12 | 003 00 05(LIT) 00 00 00 01 00 00 (Literal #3: Type CHARACTER, 'RON BURKEY')
HALMAT #13 (0x004, HAL/S statement marker): SMRK(1) 00 1 0
HALMAT #14 | 004 00 00( 0) 00 00 00 01 00 00 (HAL/S statement #4)

4 M| DECLARE MY_NAME CHARACTER(20) INITIAL('RON BURKEY'); |

HALMAT #15 (0x004, HAL/S statement marker): SMRK(1) 00 1 0
HALMAT #16 | 005 00 00( 0) 00 00 00 01 00 00 (HAL/S statement #5)

5 M| DECLARE INTEGER, J; |

HALMAT #17 (0x004, HAL/S statement marker): SMRK(1) 00 1 0
HALMAT #18 | 006 00 00( 0) 00 00 00 01 00 00 (HAL/S statement #6)

6 M| REPLACE PRINTER BY "6"; |

HALMAT #19 (0x031, End of DECLAREs): EDCL(0) 01 1 0
HALMAT #20 (0x025, Start of argument list): XXST(1) 00 1 0
HALMAT #21 | 002 00 06(IMD) 00 00 00 01 00 00 (Immediate integer data 2 0x2)
HALMAT #22 (0x027, Argument): XXAR(1) 00 1 0
HALMAT #23 | 005 02 05(LIT) 00 00 00 01 00 00 (Literal #5: Type CHARACTER, 'THE BEGINNING')
HALMAT #24 (0x021, WRITE statement): WRIT(1) 00 0 0
HALMAT #25 | 006 00 06(IMD) 00 00 00 01 00 00 (Immediate integer data 6 0x6)
HALMAT #26 (0x026, End of argument list): XXND(0) 00 0 0
HALMAT #27 (0x004, HAL/S statement marker): SMRK(1) 00 1 0
HALMAT #28 | 007 00 00( 0) 00 00 01 01 00 01 (HAL/S statement #7)

7 M| WRITE(PRINTER) 'THE BEGINNING'; |
.
.
.

 HALMAT #79 (0x025, Start of argument list): XXST(1) 00 1 0
HALMAT #80 | 002 00 06(IMD) 00 00 00 01 00 00 (Immediate integer data 2 0x2)
HALMAT #81 (0x027, Argument): XXAR(1) 00 1 0
HALMAT #82 | 011 02 05(LIT) 00 00 00 01 00 00 (Literal #17: Type CHARACTER, 'THE END')
HALMAT #83 (0x021, WRITE statement): WRIT(1) 00 0 0
HALMAT #84 | 006 00 06(IMD) 00 00 00 01 00 00 (Immediate integer data 6 0x6)
HALMAT #85 (0x026, End of argument list): XXND(0) 00 0 0
HALMAT #86 (0x004, HAL/S statement marker): SMRK(1) 00 1 0
HALMAT #87 | 00E 00 00( 0) 00 00 01 01 00 01 (HAL/S statement #14)

14 M| WRITE(6) 'THE END'; |

HALMAT #88 (0x030): CLOS(1) 00 0 0
HALMAT #89 | 001 00 01(SYT) 00 00 00 01 00 00 (Symbol #1: HELLO, PROGRAM LABEL)
HALMAT #90 (0x004, HAL/S statement marker): SMRK(1) 00 1 0
HALMAT #91 | 00F 00 00( 0) 00 00 01 01 00 01 (HAL/S statement #15)

15 M| CLOSE HELLO; |

HALMAT #92 (0x002, End of HALMAT block): XREC final

Human-friendly, you say?  What kind of dope dream is that?  Well, let's just go through a few lines of it:

HALMAT Emulators

One characteristic of HALMAT files is that they don't quite contain all of the information needed to run the program.  The thing they're missing is the values of literals, and HALMAT emulators have to be supplied that information separately from the HALMAT file itself.  The values of literals are actually provided in two files, both of which are needed, with filenames differing according to the compiler or compiler pass that produced them. One of the files contains the values and indications of datatypes for all of the literals, except that the textual data for CHARACTER literals instead appears in the second of the files, with the first file providing only an index into the second file.  Too complex for your tastes?  Mine too!  But I'm not the one who set it up that way.  Here's a handy lookup table for the default filenames involved:
Compiler/Pass
HALMAT Filename
First Literal File
Second Literal File
HALSFC-PASS1
halmat.bin
litfile0.bin
COMMON0.out.bin.gz
HALSFC-OPT
optmat.bin
litfile2.bin
COMMON2.out.bin.gz
HAL_S_FC.py
FILE1.bin
FILE2.bin
LIT_CHAR.bin

But ... literals?  What are they?  If you're actually interested, here's a long digression to sate your curiosity. There are 3 types of literals in HAL/S: numeric literals (corresponding to INTEGER and SCALAR datatypes), string literals (CHARACTER datatype), and bit literals (BIT datatype).  It's best to illustrate them by looking at the source-code itself, where I've highlighted all of the literals in purple:
DEBUG ¢C¢D¢E¢F
HELLO: PROGRAM;
DECLARE I INTEGER;
DECLARE MY_NAME CHARACTER(20) INITIAL('RON BURKEY');
DECLARE INTEGER, J;
REPLACE PRINTER BY "6";
WRITE(PRINTER) 'THE BEGINNING';
DO FOR I = 1 TO 5;
WRITE(PRINTER) I, 'HELLO, WORLD!';
DO FOR J = 2 TO 8 BY 2;
WRITE(PRINTER) ' ', J, MY_NAME||' SAYS ISN''T THIS FUN?';
END;
END;
WRITE(6) 'THE END';
CLOSE HELLO;
I bet you're scratching your head over that, aren't you?    You're asking why "6" is not a literal but PRINTER is one?  Remember that in HAL/S, something like REPLACE PRINTER BY "6" is really a preprocessor instruction causing every instance of PRINTER to be replaced by 6.  So as far as the HALMAT is concerned, there isn't any such thing as "PRINTER", there's only 6, whereas to the compiler the opposite is true.

In fact, we have a handy utility (unLitfile) that parses these litfiles into human-readable form, so you can see exactly what literals they are communicating:
> unLitfile litfile0.bin
Literal 1: FIXED 42140000,00000000 ' 2.000000000000000E+01'
Literal 2: STRING 'RON BURKEY'
Literal 3: FIXED 41600000,00000000 ' 6.000000000000000E+00'
Literal 4: STRING 'THE BEGINNING'
Literal 5: FIXED 41100000,00000000 ' 1.000000000000000E+00'
Literal 6: FIXED 41500000,00000000 ' 5.000000000000000E+00'
Literal 7: FIXED 41600000,00000000 ' 6.000000000000000E+00'
Literal 8: STRING 'HELLO, WORLD!'
Literal 9: FIXED 41200000,00000000 ' 2.000000000000000E+00'
Literal 10: FIXED 41800000,00000000 ' 8.000000000000000E+00'
Literal 11: FIXED 41200000,00000000 ' 2.000000000000000E+00'
Literal 12: FIXED 41600000,00000000 ' 6.000000000000000E+00'
Literal 13: STRING ' '
Literal 14: STRING ' SAYS ISN'T THIS FUN?'
Literal 15: FIXED 41600000,00000000 ' 6.000000000000000E+00'
Literal 16: STRING 'THE END'
Exactly as you might guess from our purple highlights!
Aside: Beware, though, because the litfile format doesn't allow determination of the total number of literals contained within it — you just have to know! —, so unLitfile doesn't know for sure where the list ends.  There may be extra literals at the end of the list that look like real literals but have little to do with anything in the HAL/S source code.  More precisely: the litfile is formatted as a series of "pages" of 130 literals each, and generally speaking, any unused areas in the final page will be duplicates of literals found in corresponding locations on the penultimate page.  If there are 130 or less literals, as in the example above, all is well.  If there are more than 130, but not an exact multiple of 130, there will be bogus literals at the end.
But you don't really need unLitfile if you're just curious about how literals are communicated between compiler passes.  When ¢E is present in a DEBUG directive in the HAL/S file, it causes the compiler to print the entire literal table in the report (pass1.rpt) it emits from its parsing path.  Not surprisingly, you get the same info as from unLitfile, albeit with a little less info ... but with the added benefit that since the compiler actually knows how many literals there are, it doesn't print any bogus ones at the end of the list!
L I T E R A L   T A B L E   D U M P:

LOC TYPE LITERAL

1 ARITH 4214000000000000
2 CHAR RON BURKEY
3 ARITH 4160000000000000
4 CHAR THE BEGINNING
5 ARITH 4110000000000000
6 ARITH 4150000000000000
7 ARITH 4160000000000000
8 CHAR HELLO, WORLD!
9 ARITH 4120000000000000
10 ARITH 4180000000000000
11 ARITH 4120000000000000
12 ARITH 4160000000000000
13 CHAR
14 CHAR SAYS ISN'T THIS FUN?
15 ARITH 4160000000000000
16 CHAR THE END

The other little mystery, if you've kept your eyes open, is that unLitfile seems to be implying that literals like 1, 8, or 6 are really floating-point.  Or at least, are stored that way.   It's true.  The numeric literals, whether SCALAR or INTEGER, double or single precision, are stored in the litfiles as double-precision floating point in so-called IBM Hexadecimal (HFP) format.  BIT literals, on the other hand, of which there are none in our example, are all stored as their own pristine selves.

Zane Hambly's Emulator (yaHALMAT)

My illustrating comments here are frozen at the yaHALMAT emulator's condition when I first wrote about it, so you may find some differences if you try out the emulator now.  All of Zane's material is in his own GitHub repositoryThat's also where bugs should be reported, as "issues" in the repository.

Installation of yaHALMAT

You need first to clone the source tree from GitHub (or download it via a zipfile) into a local directory in your filesystem.  For the sake of discussion, let's suppose that the local directory is called Halmat.  If you've chosen to call it something else, then substitute the correct name instead.  To build it on Linux or Mac OS, do this:
cd Halmat/emu
make clean yaHALMAT
On Windows?  Maybe it will work.  I don't know, I've only tried it on Linux myself.

Finally, once compiled, unless you want to invoke yaHALMAT by using its full pathname, you'll probably want to copy or link yaHALMAT[.exe] somewhere into your existing path, or else change your PATH variable to include the directory that yaHALMAT is in.  I'll let you to figure out that part.


Assuming you've done all that, it's time to play!  I'll give a few examples of my own below, although Zane has some of his own as well.

What's the first thing to try out?  You know the answer, so why even ask?  Here's a program I call REALLY_HELLO.hal (to distinguish it from the entirely-different sample program I foolishly called HELLO.hal that we've been using throughout many of our discussions of HAL/S):
 HELLO: PROGRAM;
WRITE(6) 'Hello, world!';
CLOSE HELLO;
Compile it:
HALSFC --test REALLY_HELLO.hal
And now we want to run it.  As we saw above, there are several different HALMAT files we could use, so there are several different yaHALMAT invocations we could use.  Running the unoptimized HALMAT from HALSFC would be done like this:
yaHALMAT halmat.bin
Whereas running the optimized HALMAT would look like:
yaHALMAT optmat.bin
Or we could use the unoptimized HALMAT from HAL_S_FC.py.  In principle, the compilation command for that would have been
HAL_S_FC.py --hal=REALLY_HELLO.hal
but I accomplished the same thing by adding the --test switch to the HALSFC command line, so we end up with the files we wanted anyway:
yaHALMAT --litfile FILE2.bin --common LIT_CHAR.bin FILE1.bin
The output in any of these emulations looks exactly the same:
Hello, world!
But instead, let's try the goofy HELLO.hal we've been using to demonstrate lots of stuff, namely
DEBUG ¢C¢D¢E¢F
HELLO: PROGRAM;
DECLARE I INTEGER;
DECLARE MY_NAME CHARACTER(20) INITIAL('RON BURKEY');
DECLARE INTEGER, J;
REPLACE PRINTER BY "6";
WRITE(PRINTER) 'THE BEGINNING';
DO FOR I = 1 TO 5;
WRITE(PRINTER) I, 'HELLO, WORLD!';
DO FOR J = 2 TO 8 BY 2;
WRITE(PRINTER) ' ', J, MY_NAME||' SAYS ISN''T THIS FUN?';
END;
END;
WRITE(6) 'THE END';
CLOSE HELLO;
Compile and run it:
> HALSFC HELLO.hal
Compilation successful. Results in "HALSFC HELLO.hal Tue 02-24-2026 6-50-07.00.results".
> yaHALMAT halmat.bin
THE BEGINNING
1 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
0 RON BURKEY SAYS ISN'T THIS FUN?
0 RON BURKEY SAYS ISN'T THIS FUN?
0 RON BURKEY SAYS ISN'T THIS FUN?
0 RON BURKEY SAYS ISN'T THIS FUN?
0 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
0 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
0 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
0 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
0 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
THE END

Is this printout correct?  No it is not, and I'll leave it as an exercise for the reader to figure out how it differs from what it should be.  As the first emulator out of the gate, so to speak, let's give yaHALMAT the credit it deserves!  But ... there are bugs.

And just for OCD's sake, a final example source-code example:
 POWER_TABLE: PROGRAM;
WRITE(6) ' N N**2 N**3 N**4 N**5';
WRITE(6) ' ---------- ---------- ---------- ---------- ----------';
DO FOR TEMPORARY N = 1 TO 10;
WRITE(6) N, N N, N**3, N**4, N**5;
END;
CLOSE POWER_TABLE;
Emulating this we get:
          N       N**2       N**3       N**4       N**5
---------- ---------- ---------- ---------- ----------
1 1 1 1 1
2 4 8 16 32
3 9 27 81 243
4 16 64 256 1024
5 25 125 625 3125
6 36 216 1296 7776
7 49 343 2401 16807
8 64 512 4096 32768
9 81 729 6561 59049
10 100 1000 10000 100000

Claude's Emulator (yaHALMAT2)

This is where Virtual AGC throws its halmat into the ring.  (Ouch!)  As I mentioned earlier, the "Claude" in this case is ANTHROP\C Claude.  The emulator developed by Claude, under direction, is yaHALMAT2, and its code resides the Virtual AGC Project's source tree.

The first step in developing yaHALMAT2 was to regenerate complete HALMAT documentation, using not only the technique pioneered earlier by Zane Hambly, but also extensive compilation of HAL/S test code, comparison of HALMAT to generated object code, and referring to additional documentation not specific to HALMAT that might not have been previously consulted ... in other words, to hit the problem with an enormous helping of obsessive attention to detail.  After all of that had been completed, and only then, a final cross-check was done against Zane's earlier reconstructed documentation.  While there were discrepancies (i.e., errors) on both sides when the cross-check was performed, they were surprisingly few, which should give us some confidence in the results.  Of course, Claude's errors have been corrected by the time you've read this.

Installation of yaHALMAT2

I'll assume you have a local clone of the Virtual AGC source tree from GitHub, and for the sake of argument I'll refer to that local directory as virtualagc.  But use the actual directory name in the instructions below.

To build it on Linux or Mac OS, simply
cd virtualagc/yaShuttle/yaGpcIntegration
make clean all
The executable will appear in the directory virtualagc/yaShuttle/yaGpcIntegration/.

On Windows, you should be able to build it either in an MSYS2 environment (for which the instructions should be the same as above), or else in a Windows "developer" terminal using Visual C.  In the latter case, I'm told you have to use the command "nmake /f NMakefile".  But I've only built yaHALMAT2 on Linux, so you can regard anything I say about Mac OS or Windows as speculative.  The default on Linux, Mac, or Windows with MSYS2 is to use clang as the compiler if available, falling back on gcc.  Regardless of the platform, you can choose any compiler you want by specifying it on the make/nmake command line with the CC variable.  I have not tried that myself.

Finally, once compiled, unless you want to invoke yaHALMAT2 by using its full pathname, you'll probably want to copy or link yaHALMAT2[.exe] somewhere into your existing path, or else change your PATH variable to include the directory that yaHALMAT2 is in.  I'll let you to figure out that part.

The new, full HALMAT documentation I mentioned can be seen on GitHub.  As a slap in the face to the current hubbub about how "human" A.I. is, this new documentation looks very much as though a machine wrote it ... or at least someone with little interest in engaging the reader.  With that's said, it's kind of fascinating to look at, and indeed very informative if you're the kind of person who really likes to get at the nuts and bolts of everything.  If you're that person, you might be interested in the document that describes the current state of development, or the document used to plan development.  All of this documentation is in so-called "markdown" format to make it easy to update as further corrections become necessary, as well as being extensively annotated to describe how the results were arrived at.  I'd recommend just looking at it directly on GitHub if you're interested.  Of course, you could also download it and view it locally if you have a markdown viewer, though I'd note that not all markdown viewers are as capable as GitHub's.  In other words, your results may vary.

The next step was to proceed to implementation of the emulator itself, called yaHALMAT2 to distinguish itself from Zane's yaHALMAT.  To give credit where credit is due, Claude concluded that yaHALMAT had a nice, clean design, and therefore may have based yaHALMAT2 at least partially on it.

Enough yapping!  Let's compile and emulate our ever-present HELLO.hal sample code that's not a "HELLO" program at all, namely (in its latest incarnation as of this writing):
 HELLO: PROGRAM;
DECLARE I INTEGER;
DECLARE POOKIE CHARACTER(20);
DECLARE MY_NAME CHARACTER(20) INITIAL('RON BURKEY');
DECLARE INTEGER, J;
REPLACE PRINTER BY "6";
WRITE(PRINTER) 'THE BEGINNING';
DO FOR I = 1 TO 5;
WRITE(PRINTER) I, 'HELLO, WORLD!';
DO FOR J = 2 TO 8 BY 2;
WRITE(PRINTER) ' ', J, MY_NAME||' SAYS ISN''T THIS FUN?';
END;
END;
WRITE(6) 'THE END';
CLOSE HELLO;

Compile it and run it:
> HALSFC  HELLO.hal
Results stored in the folder "HALSFC HELLO.hal 2026-07-20 11-50-02.results".
Accessible also as "current.results" until next HALSFC run.
For previous runs see also "current-1.results", "current-2.results".
Compilation successful.
> yaHALMAT2 halmat.bin
THE BEGINNING
1 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
4 RON BURKEY SAYS ISN'T THIS FUN?
6 RON BURKEY SAYS ISN'T THIS FUN?
8 RON BURKEY SAYS ISN'T THIS FUN?
2 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
4 RON BURKEY SAYS ISN'T THIS FUN?
6 RON BURKEY SAYS ISN'T THIS FUN?
8 RON BURKEY SAYS ISN'T THIS FUN?
3 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
4 RON BURKEY SAYS ISN'T THIS FUN?
6 RON BURKEY SAYS ISN'T THIS FUN?
8 RON BURKEY SAYS ISN'T THIS FUN?
4 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
4 RON BURKEY SAYS ISN'T THIS FUN?
6 RON BURKEY SAYS ISN'T THIS FUN?
8 RON BURKEY SAYS ISN'T THIS FUN?
5 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
4 RON BURKEY SAYS ISN'T THIS FUN?
6 RON BURKEY SAYS ISN'T THIS FUN?
8 RON BURKEY SAYS ISN'T THIS FUN?
THE END
The emulator also has a debugging mode built into it, modeled after the gdb debugger.  Below, I load halmat.bin in debugging mode and single step through a few HALMAT instructions:
> yaHALMAT2 --debug halmat.bin
yaHALMAT2 debugger. Type 'help' for commands.
1 M| HELLO: |HELLO
1 M| PROGRAM; |HELLO
N=0 T=0.00 [0: HELLO] #0 0x005 PXRC numop=1 tag=0x00 copt=0x0 ; Record header, points to closing XREC
[0] data=0x005C(92) qual= 0 tag1=0x00 tag2=0x0
(halmat) step
step
N=1 T=0.00 [0: HELLO] #2 0x02B MDEF numop=1 tag=0x00 copt=0x0 ; Program definition header
[0] data=0x0001(1) qual=SYT tag1=0x00 tag2=0x0
(halmat) step
step
N=2 T=60.00 [0: HELLO] #4 0x004 SMRK numop=1 tag=0x00 copt=0x0 ; Statement marker
[0] data=0x0001(1) qual= 0 tag1=0x00 tag2=0x1
(halmat) step
step
2 M| DECLARE I INTEGER; |HELLO
N=3 T=60.00 [0: HELLO] #6 0x004 SMRK numop=1 tag=0x00 copt=0x0 ; Statement marker
[0] data=0x0002(2) qual= 0 tag1=0x00 tag2=0x0
(halmat) step
step
3 M| DECLARE POOKIE CHARACTER(20); |HELLO
N=4 T=60.00 [0: HELLO] #8 0x004 SMRK numop=1 tag=0x00 copt=0x0 ; Statement marker
[0] data=0x0003(3) qual= 0 tag1=0x00 tag2=0x0
(halmat) step
step
4 M| DECLARE MY_NAME CHARACTER(20) INITIAL('RON BURKEY'); |HELLO
N=5 T=60.00 [0: HELLO] #10 0x841 CINT numop=2 tag=0x02 copt=0x0 ; Character initialize
[0] data=0x0004(4) qual=SYT tag1=0x00 tag2=0x0
[1] data=0x0003(3) qual=LIT tag1=0x00 tag2=0x0
(halmat) step
step
N=6 T=60.00 [0: HELLO] #13 0x004 SMRK numop=1 tag=0x00 copt=0x0 ; Statement marker
[0] data=0x0004(4) qual= 0 tag1=0x00 tag2=0x0
(halmat) step
step
5 M| DECLARE INTEGER, J; |HELLO
N=7 T=60.00 [0: HELLO] #15 0x004 SMRK numop=1 tag=0x00 copt=0x0 ; Statement marker
[0] data=0x0005(5) qual= 0 tag1=0x00 tag2=0x0
(halmat) step
step
6 M| REPLACE PRINTER BY "6"; |HELLO
N=8 T=60.00 [0: HELLO] #17 0x004 SMRK numop=1 tag=0x00 copt=0x0 ; Statement marker
[0] data=0x0006(6) qual= 0 tag1=0x00 tag2=0x0
(halmat) step
step
7 M| WRITE(PRINTER) 'THE BEGINNING'; |HELLO
N=9 T=60.00 [0: HELLO] #19 0x031 EDCL numop=0 tag=0x01 copt=0x0 ; End-of-declarations marker
(halmat) step
step
N=10 T=60.00 [0: HELLO] #20 0x025 XXST numop=1 tag=0x00 copt=0x0 ; I/O statement start, carries I/O-kind code
[0] data=0x0002(2) qual=IMD tag1=0x00 tag2=0x0
(halmat) kill
step
execution stopped
(halmat) quit
step
The colors are adjustable via the command line, or can be disabled entirely.  HALMAT instructions aren't just dumped on you willy-nilly in binary, but are instead parsed into a somewhat human-readable format like the unHALMAT.py utility discussed earlier, along with embedding the HAL/S source code so you can see the larger context.

What do all of the fields in the printout above mean? 
But I'm bored with HELLO.hal!  Let's look at some other examples that aren't quite so lame.

Another feature supported by yaHALMAT2 is loading multiple HALMAT files at once.  Or to put it loosely, yaHALMAT2 can act like an on-the-fly "linker" for HALMAT files.  Each HALMAT file corresponds to a HAL/S source-code file, and very few significant programs are written as a single source-code file, so this is a necessary feature.

The yaHALMAT2 command line supports two different formats for selecting the HALMAT file(s) to load for emulation.  The syntax we used above supports only a single file:
yaHALMAT2 [OPTIONS] HALMAT_FILE
Pragmatically speaking, while HALMAT_FILE could be anything you desire, it's actually always halmat.bin, optmat.bin, or FILE1.bin.  The problem with directly extending this kind of command-line format to multiple HALMAT files is that the compiler has to move all of the files you need elsewhere in order to avoid cluttering up the current working directory, and therefore if you had to list all of the files you needed directly in the yaHALMAT2 command line, it would rapidly become a nightmare.  A typical strategy is to perform all of your HAL/S compilations with the HALSFC command-line switch --clean, or possibly --clean --archive.  This strategy creates a new subdirectory for each compilation and shoves all of the files that were generated into it.

We can work around this when we invoke yaHALMAT2 by using the alternate syntax
yaHALMAT2 [OPTIONS] @LIST
That's for running unoptimized HALMAT (output by the compiler's PASS1), whereas to run optimized HALMAT (output by the compilers OPT pass) we could instead say
yaHALMAT2 [OPTIONS] --opt @LIST
But the results are indistinguishable.  Here, LIST is the name of a file that contains a list of directories of the kind that HALSFC produces for its output products. 
Aside: One useful trick is to make a file — let's call it CURRENT — that just contains the single line "current.results".  Then invoking yaHALMAT2 @CURRENT always runs the HAL/S program that you've compiled most recently.
Actually demonstrating how this works in practice is a little more involved that our simple HELLO.hal example, so bear with me!  Let's imagine a HAL/S PROGRAM that prints a table of squares and square roots of integers, and that for some inexplicable reason we've decided to provide two external functions, one that squares a number, and one that computes a square root.  Here's what our code might look like:

C SQUARE.hal
SQUARE: FUNCTION(X) SCALAR;
DECLARE X SCALAR;
RETURN X X;
CLOSE SQUARE;
C MYTABLE.hal
D INCLUDE TEMPLATE SQUARE
D INCLUDE TEMPLATE SQUROO
MYTABLE: PROGRAM;
DO FOR TEMPORARY I = 1 TO 10;
WRITE(6) I, SQUARE(I), SQU_ROOT(I);
END;
CLOSE MYTABLE;
C SQUROO.hal
SQU_ROOT: FUNCTION(X) SCALAR;
DECLARE X SCALAR;
RETURN SQRT(X);
CLOSE SQU_ROOT;
Compile it:
HALSFC --clean --archive --parms=TEMPLATE SQUARE.hal
HALSFC --clean --archive --parms=TEMPLATE SQUROO.hal
HALSFC --clean --archive MYTABLE.hal
This creates directories for the compiler output products of:
For you, of course, the directory names would be different, since they'd have different dates/times.  Next you have to create a file called "LIST" that points to these directories:
archive.results/HALSFC SQUARE.hal 2026-07-20 18-20-58.results
archive.results/HALSFC SQUROO.hal 2026-07-20 18-19-01.results
archive.results/HALSFC MYTABLE.hal 2026-07-20 18-21-04.results
Notice that there are spaces in these directory names as stored in LIST, but no surrounding quotation marks are used.  (Quotation marks would break it!)  Run it:
> yaHALMAT2 @LIST
1 1.0000000E+00 1.0000000E+00
2 4.0000000E+00 1.4142132E+00
3 9.0000000E+00 1.7320499E+00
4 1.6000000E+01 2.0000000E+00
5 2.5000000E+01 2.2360678E+00
6 3.6000000E+01 2.4494896E+00
7 4.9000000E+01 2.6457510E+00
8 6.4000000E+01 2.8284264E+00
9 8.1000000E+01 3.0000000E+00
10 1.0000000E+02 3.1622772E+00
Multi-file capability isn't fully implemented yet in yaHALMAT2.  HAL/S real-time mechanisms allow multiple PROGRAMs to be simultaneously running, while flipping back and forth between them, whereas all we have implemented so far is a single PROGRAM plus multiple COMPOOLs, PROCEDUREs, and FUNCTIONs.  First things first!

Actually, I gave yaHALMAT2 short shrift earlier when I said that it could act as an "on-the-fly" HALMAT linker.  While that's true, it can act as an actual HALMAT linker as well.  What do I mean by that?  Well, while this @LIST mechanism may be reasonably-efficient for development purposes, it's not very convenient for actually distributing a program in HALMAT form to anybody.  If a program consisted of (say) 10 HAL/S source-code files, to give the program's HALMAT to anybody we'd have to send out 40 files generated by the HAL/S compiler, some of them big ones, stored in a rigid directory hierarchy.  Sure, we could do it — that's what zipfiles are for, after all — but it seems like a bit of overkill.  We can bypass that problem by running yaHALMAT2 with the --link-only option:
yaHALMAT2 --link-only MYTABLE.yhla @LIST
This doesn't perform any emulation, but instead just produces a new file (MYTABLE.yhla) that we can think of as the "linked" form of the program.  And MYTABLE.yhla doesn't just contain the HALMAT instructions, but it also contains the literal files, as well as the HAL/S source-code lines for display in the emulator's debugger ... i.e., it's entirely self-contained.  Obviously, there is no pre-existing file format for "linked" HALMAT, and never was one, so Claude invented one for us, and documented it too.  Why the filename extension ".yhla"?  I asked, using up hundreds of precious tokens, and got this answer: 
"YHLA" is my own abbreviation for YaHalmat Linked Archive — I picked it as the 4-byte magic when I specified the container format for the implementing agent, following the convention of short ASCII magic-number prefixes (like "PK" for zip, "GZ"-ish headers for gzip). No deeper significance than that.
Makes sense to me.  Anyway, you can run a YHLA without any particular to-do; yaHALMAT2 just recognizes from the embedded magic numbers that it's a YHLA file rather than a HALMAT file:
> yaHALMAT2 MYTABLE.yhla
1 1.0000000E+00 1.0000000E+00
2 4.0000000E+00 1.4142132E+00
3 9.0000000E+00 1.7320499E+00
4 1.6000000E+01 2.0000000E+00
5 2.5000000E+01 2.2360678E+00
6 3.6000000E+01 2.4494896E+00
7 4.9000000E+01 2.6457510E+00
8 6.4000000E+01 2.8284264E+00
9 8.1000000E+01 3.0000000E+00
10 1.0000000E+02 3.1622772E+00
yaHALMAT2 also supports HAL/S's so-called "real time" features.  Consider the following example program, which simply counts up from 1 to 20, once per second:
 COUNTUP: PROGRAM;
DECLARE I INTEGER INITIAL(1);

NEXT: TASK;
WRITE(6) I;
I = I + 1;
CLOSE NEXT;

SCHEDULE NEXT PRIORITY(80), REPEAT EVERY 1.0;
WAIT 19.5;
CLOSE COUNTUP;
> HALSFC --clean --archive COUNTUP.hal
> # Note that LISTC contains a single entry: current.results
> time yaHALMAT2 @LISTC
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

real 0m19.555s
user 0m0.045s
sys 0m0.010s
Windows users may wonder why the command to start the emulation has the word time in front of it.  It has nothing to do with using the SCHEDULE or WAIT feature.  Rather, in Linux or Mac OS that just means to time how long the program took, which was 19.555 seconds.  And why 19.5 seconds?  Because it only takes 19 seconds to count from 1 to 20 if you do so once per second.

Here's another example which is somewhat similar, but which illustrates a different use of real-time features, not to mention matrix multiplication, subscript slicing, printing of vectors, and repetition factors in initializers.  It performs the silly functionality of taking a "random" 5×5 orthogonal matrix (i.e., a rotation matrix in 5 dimensions) that I got from Google's A.I. assistant, and just prints several powers of it.  Who wouldn't want to do that?
 MPOWERS: PROGRAM;
DECLARE P5D MATRIX(5,5) INITIAL(4#(1,5#0),1);
DECLARE M5D MATRIX(5,5) INITIAL(
-0.2877, 0.0722, -0.6645, -0.6217, -0.2898,
0.1356, -0.8101, -0.4713, 0.2571, 0.1928,
0.2684, 0.2374, -0.3093, 0.5534, -0.6853,
0.3257, 0.5176, -0.4731, 0.1254, 0.6217,
-0.8489, 0.1197, -0.1294, 0.4748, 0.1508);
DECLARE COUNT INTEGER INITIAL(0);

PRINT_MATRIX_5D: PROCEDURE(M);
DECLARE M MATRIX(5,5);
DO FOR TEMPORARY I = 1 TO 5;
WRITE(6) '', M$(I,*);
END;
CLOSE PRINT_MATRIX_5D;

WRITE(6) 'Powers of a "random" 5D rotation matrix:';
WRITE(6) '';

MUL5D: TASK;
WRITE(6) COUNT;
CALL PRINT_MATRIX_5D(P5D);
WRITE(6) '';
COUNT=COUNT+1;
IF COUNT > 5 THEN
CANCEL MUL5D;
P5D = M5D P5D;
SCHEDULE MUL5D IN 1.0 PRIORITY(80) DEPENDENT;
CLOSE MUL5D;

SCHEDULE MUL5D PRIORITY(80) DEPENDENT;
CLOSE MPOWERS;
Aside: For full disclosure, I should add that having a TASK re-SCHEDULE itself is not something it's entirely clear from the original documentation that it's something actually allowed.  Also, you'll notice that the PROGRAM ends immediately after the SCHEDULE statement, yet the TASK keeps running, which is again something we're not 100% certain about.  Both questions could be argued either way.  The HAL/S compiler certainly allows both.
And here's what happens when yaHALMAT2 runs it:
> HALSFC MPOWERS.hal
> yaHALMAT2 --line-length 132 halmat.bin
Powers of a "random" 5D rotation matrix:

0
1.0000000E+00 0.0000000E+00 0.0000000E+00 0.0000000E+00 0.0000000E+00
0.0000000E+00 1.0000000E+00 0.0000000E+00 0.0000000E+00 0.0000000E+00
0.0000000E+00 0.0000000E+00 1.0000000E+00 0.0000000E+00 0.0000000E+00
0.0000000E+00 0.0000000E+00 0.0000000E+00 1.0000000E+00 0.0000000E+00
0.0000000E+00 0.0000000E+00 0.0000000E+00 0.0000000E+00 1.0000000E+00

1
-2.8770000E-01 7.2199941E-02 -6.6450000E-01 -6.2169999E-01 -2.8979999E-01
1.3559997E-01 -8.1009996E-01 -4.7129995E-01 2.5709999E-01 1.9279999E-01
2.6839995E-01 2.3740000E-01 -3.0929995E-01 5.5339998E-01 -6.8529999E-01
3.2569999E-01 5.1760000E-01 -4.7309995E-01 1.2539995E-01 6.2169999E-01
-8.4889996E-01 1.1969995E-01 -1.2939996E-01 4.7479999E-01 1.5079999E-01

2
-4.2266667E-02 -5.9349424E-01 6.9430482E-01 -3.8586676E-01 1.2246466E-01
-3.5528886E-01 7.1031874E-01 2.9088461E-01 -4.2961472E-01 3.1641096E-01
6.3395023E-01 -4.1957617E-02 -3.6770761E-01 -5.3297925E-01 4.2065722E-01
-6.3741559E-01 -3.6878169E-01 -4.5381731E-01 -2.0317852E-02 5.0133419E-01
2.5235707E-01 7.4828267E-02 3.0356145E-01 6.1806560E-01 6.7569083E-01

3
-1.1160338E-01 4.5750028E-01 2.5975823E-01 2.6767653E-01 -7.9940951E-01
-1.3191772E-01 -7.1651912E-01 -2.6347093E-02 6.6083968E-01 -1.7880785E-01
-8.1745660E-01 -2.3305011E-01 -9.0033829E-02 -4.7551090E-01 -2.0773649E-01
-4.2062706E-01 1.9448555E-01 6.8247473E-01 2.8581071E-01 4.8759234E-01
-3.5327041E-01 4.3045807E-01 -6.7669034E-01 4.2866218E-01 2.1940875E-01

4
9.2966521E-01 -2.7415204E-01 -2.4499685E-01 -1.5235722E-02 -1.1599779E-02
3.0074650E-01 8.8532001E-01 1.4399827E-01 -1.1881304E-01 3.0202043E-01
2.0088887E-01 -1.6259080E-01 9.3272901E-01 2.4020857E-01 -7.3284745E-02
9.7340941E-03 1.8039769E-01 -2.2155517E-01 9.5653683E-01 -5.7088077E-02
-6.8258524E-02 -2.8672779E-01 9.9819899E-03 1.1374819E-01 9.4869202E-01

5
-3.6551166E-01 2.2177559E-01 -4.0406817E-01 -7.9145658E-01 -1.6559845E-01
-2.2290856E-01 -6.8664449E-01 -6.4450681E-01 2.4883038E-01 -4.3470025E-02
3.1094873E-01 4.8320842E-01 -4.4951409E-01 3.4480387E-01 -5.9047794E-01
3.2220203E-01 2.9023510E-01 -4.6811306E-01 1.0564327E-02 7.6986158E-01
-7.8485996E-01 4.0215373E-01 8.3014206E-04 4.3894553E-01 1.7143917E-01

In case you wonder about the command-line option --line-length 132, printouts by default wrap around to the next line afer 80 columns.  This option just increases that to 132 columns instead.

AP-101B/S Emulators

The emulators presented below accept an AP-101S memory image as input, as opposed (say) to an IBM "load file".  To produce such a memory image, it's necessary first to compile or assemble all of the individual source code modules comprising the program, and then to use a linker program to combine the separate object files generated by compilation/assembly.  Please refer to our linker page for more information for more specifics.  Below, for simplicity of exposition, we'll just suppose that you've compiled a HAL/S program called MYPROGRAM.hal, and have produced a memory image from it that's called MYPROGRAM.fcm, with ".fcm" being the conventional filename extension we use for memory images.

Specifically, we'll use the following test code called HELLO_WORLD.hal.

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

Compiling it and linking it like so,

HALSFC -o HELLO_WORLD.obj HELLO_WORLD.hal
lnk101 -o HELLO_WORLD.fcm --json-symbols HELLO_WORLD.sym.json HELLO_WORLD.obj
gives us HELLO_WORLD.fcm.  What is the HELLO_WORLD.sym.json file?  It's an output from the linker, and it's basically a symbol table.  But it's something the emulators need, because it contains the entry address starting the program
Aside: Why didn't we use the IBM "load file" format rather than the memory-image FCM format? If we had, the JSON symbol table wouldn't have been necessary, because a load file contains the entry address and other stuff besides. The answer is, I don't know.  It worked, and as long as it continues to work, there's little incentive to do it differently.

Don Schmidt's Emulator (gpc)

Don's emulator, gpc, can be run in either a simple mode that just executes the code without fanfare, or else in a fancy graphical debugging mode. 

Installation of gpc

To my recollection, I have lost the ability to build gpc from source, but perhaps the author's instructions might work for you.  I think what worked for me was to download the pre-built "AppImage" for Linux, rename it "gpc", and copy it into my path.  There are also pre-built Mac OS "dmg" files at the same link, though I don't know how to use them myself.  As far as Windows is concerned, I haven't a clue as to what you have to do.


Let's just look at the simple mode first:

gpc run --interactive HELLO_WORLD.fcm

This results in:

Hello, world!

*** HAL/S PROGRAM HALT (SVC 0)

The --interactive switch tells the emulator to accept keyboard input on device 5 (as in the HAL/S READ(5) ...;) and output to device 6 (as in the HAL/S WRITE(6) ...;).  In this example, we could have just omitted it.

Or if we were to try the same thing but with our ubiquitous but misnamed HELLO.hal, we'd get this instead:

THE BEGINNING
1 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
4 RON BURKEY SAYS ISN'T THIS FUN?
6 RON BURKEY SAYS ISN'T THIS FUN?
8 RON BURKEY SAYS ISN'T THIS FUN?
2 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
4 RON BURKEY SAYS ISN'T THIS FUN?
6 RON BURKEY SAYS ISN'T THIS FUN?
8 RON BURKEY SAYS ISN'T THIS FUN?
3 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
4 RON BURKEY SAYS ISN'T THIS FUN?
6 RON BURKEY SAYS ISN'T THIS FUN?
8 RON BURKEY SAYS ISN'T THIS FUN?
4 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
4 RON BURKEY SAYS ISN'T THIS FUN?
6 RON BURKEY SAYS ISN'T THIS FUN?
8 RON BURKEY SAYS ISN'T THIS FUN?
5 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
4 RON BURKEY SAYS ISN'T THIS FUN?
6 RON BURKEY SAYS ISN'T THIS FUN?
8 RON BURKEY SAYS ISN'T THIS FUN?
THE END

*** HAL/S PROGRAM HALT (SVC 0)

There are actually lots of command-line options, which you can view with the command gpc help run.

Installation of yaGPC

It's probably not worth mentioning when you already have gpc, but we also have an A.I.-generated port (from the Javascript language to the C language) for the "gpc run ..."  command mentioned above, in the form of a program called yaGPC.  It was created solely as an intermediate step in creating the yaGPC2 emulator, but it does have some slight advantages over "gpc run"  in terms of timing, resource utilization, and perhaps (depending on your outlook) ease of installation, if you're the kind of person who has git installed on their computer.  To install it, do this:
git clone https://github.com/rburkey2005/yaGPC.git
cd yaGPC
git checkout yaGPC
cd yaGPC
make
In Windows, replace the final line above with
nmake /f NMakefile
The executable file yaGPC[.exe] must then be put into your PATH.

After installation, you can just replace any use of "git run ..." by "yaGPC ...".  You can also get a complete list of options via "yaGPC --help".

yaGPC does not support the "gpc gui ..." mode discussed below.  There is no substitute.

The second way to run the emulator is, as I mentioned, with a GUI debugging interface.  You could use a command like,

gpc gui HELLO_WORLD.fcm

resulting in:

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


No obvious way to exit the GUI emulator in these old screenshots, but when you actually run the program you'll see a Quit button near the top right.

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...)".

Dan Weaver's Emulator (ap0000a)

Installation of ap0000a

Not yet publicly available.

TBD

Claude's Emulator (yaGPC2)

yaGPC2 is an AP-101B/S emulator created by the ANTHROP\C Claude A.I. coding agent, under direction, intended to partially extend Don Schmidt's gpc emulator.  Only gpc's batch mode is handled, and not its GUI debugger, hence the "partial" qualifier.  But why?  What's wrong with gpc's batch mode?  Nothing!  It's lovely.  But that doesn't mean that it's fit for all use cases.  A use case that's significant to us is integration into spaceflight-simulation software such as Orbiter.  As a Javascript program, gpc is a poor choice for that particular use.  There is also a pragmatic concern, in that we would like to maintain bug-fix and feature parity between HALMAT and AP-101S emulators, which is tricky when independent developers are involved, each with their own schedules, priorities, and opinions.  Having control over development, and allowing A.I. to perform all of that work, which is the case for both our yaHALMAT2 and yaGPC2 emulators, makes achieving that parity simpler and easier.

Installation of yaGPC2

I'll assume you have a local clone of the Virtual AGC source tree from GitHub, and for the sake of argument I'll refer to that local directory as virtualagc.  But use the actual directory name in the instructions below.

To build it on Linux or Mac OS, simply
cd virtualagc/yaShuttle/yaGpcIntegration
make clean all
The executable will appear in that same directory.

On Windows, you should be able to build it either in an MSYS2 environment (for which the instructions should be the same as above), or else in a Windows "developer" terminal using Visual C.  In the latter case, I'm told that instead of make you have to use "nmake /f NMakefile".  But I've only built yaGPC2 on Linux, so you can regard anything I say about Mac OS or Windows as speculative.  The default on Linux, Mac, or Windows with MSYS2 is to use clang as the compiler if available, falling back on gcc.  Regardless of the platform, you can choose any compiler you want by specifying it on the make/nmake command line with the CC variable.  I have not tried that myself.

Finally, once compiled, unless you want to invoke yaGPC2 by using its full pathname, you'll probably want to copy or link yaGPC2[.exe] somewhere into your existing path, or else change your PATH variable to include the directory that yaGPC2 is in.  I'll let you to figure out that part.


Usage of  yaGPC2 is basically just like "gpc run ..." as described earlier, but "yaGPC2 ..." instead.  In particular, you can use "yaGPC2 --help" to get more usage info.

Aside:  With that said, there is a new command-line switch, "--fcos" that addresses an implementation conundrum.  If you were using yaGPC2 to run Space Shuttle flight software, it would from time-to-time execute instructions in the underlying operating system (FCOS), whereas if you were using yaGPC2 to run "general purpose" software like Hello World, the underlying operating system would not be present.  Certain AP-101S instructions behave differently in those two cases.  The HALMAT emulator, yaHALMAT2, isn't affected by this, because it has no knowledge of the operating system anyway, so it always behaves as if the operating system were present.  In a situation like that, the behavior of yaHALMAT2 and yaGPC2 might differ from time to time.  What the --fcos switch does is to tell yaGPC2 to simulate enough of FCOS's presence to agree with yaHALMAT2. I'm aware that that explanation makes it all sound a bit ominous, but in fact it's rather benign.  The only situation known so far in which this has any effect is the following:

And there are other new command-line switches as well.

I won't bother to show you yet one more time what yaGPC2 looks like when emulating our standard HELLO.hal program, because it looks just like all of the other emulators when it does.  Boring!  It does have a build-in command-line-based debugger with gdb-style commands.  Just add --debug to the command line.  So let's look at that for a moment, with HELLO.hal (naturally!).

> yaGPC2 --debug --interactive --no-trace --no-verbose --symbols HELLO-lnk101.json --line-width 240 --source-map HELLO.srcmap.json HELLO.fcm
--- Registers (step 0, bank 0) ---
R00=00000000 R01=00000000 R02=00000000 R03=00000000
R04=00000000 R05=00000000 R06=00000000 R07=00000000
FP0=00000000 FP1=00000000 FP2=00000000 FP3=00000000
FP4=00000000 FP5=00000000 FP6=00000000 FP7=00000000
PSW1=81640020 PSW2=00080000 NIA=10164 CC=0 BSR=2 DSR=0
[ 0] T=0.00 >> 10164 START +00000: e4f3 8000 BAL 4,X'8000'
gpc>
Aside: Where did I get that ridiculously-complex command line, you ask?  It's not as hard as it sounds, at least if you have a single HAL/S file you want to run.  I just use the script compileLinkRun thusly, "compileLinkRun --debug --filename=HELLO.hal", and it generates all of the commands for compiling, linking, and running. If I don't want to debug, I just leave out the --debug switch.

What are we actually seeing here?  Well, starting on the far left:

Not too different from any other command-line debugger.   Let's run through a few instructions:

gpc> step
[ 1] T=3.75 >> 10000 $0HELLO +00000: e8f3 021e LHI 0,X'021e' R04: 00000000->81660020, PSW1: 81640020->80000020
HAL/S 1: HELLO: PROGRAM;
gpc> step
[ 2] T=4.00 >> 10002 $0HELLO +00002: e9f3 014e LHI 1,X'014e' R00: 00000000->021e0000, PSW1: 80000020->80020020
gpc> step
[ 3] T=4.25 >> 10004 $0HELLO +00004: b914 STH 1,X'0005'(0) R01: 00000000->014e0000, PSW1: 80020020->80040020
gpc> step
[ 4] T=4.75 >> 10005 $0HELLO +00005: e0fb IAL 0,X'0028' PSW1: 80040020->80050020
gpc> step
[ 5] T=5.25 >> 10007 $0HELLO +00007: eb11 LA 3,X'0004'(1) R00: 021e0000->021e0028, PSW1: 80050020->80070020
gpc>

So a little screen space is saved, by just showing the registers which have changed, rather than insisting on showing you all registers every time.  Nice!  Of course, you could see all of the registers any time you liked, just by using the command regs.  And notice that it has displayed a line of HAL/S source code too. 

There's a feature, "htrace on", which yaGPC2 has but which is missing from normal gdb, which enables printing of a short record for every instruction executed.  That can be useful if want to execute a batch of them at once:

gpc> htrace on
Trace enabled
gpc> step 100
[ 5] T=5.50 010007 $0HELLO +0007: eb11 LA 3,X'0004'(1) R03: 00000000->01520000, PSW1: 80070020->80080020
[ 6] T=6.00 010008 $0HELLO +0008: bb24 STH 3,X'0009'(0) PSW1: 80080020->80090020
HAL/S 7: WRITE(PRINTER) 'THE BEGINNING';
[ 7] T=6.25 010009 $0HELLO +0009: eef3 0006 LHI 6,X'0006' R06: 00000000->00060000, PSW1: 80090020->800b0020
[ 8] T=6.50 01000b $0HELLO +000b: edf3 0003 LHI 5,X'0003' R05: 00000000->00030000, PSW1: 800b0020->800d0020
[ 9] T=31.00 01000d $0HELLO +000d: d0ff 394a SCAL 0,X'014a'(1,) R00: 021e0028->02460012, PSW1: 800d0020->810e0020
[ 10] T=34.00 01010e IOINIT +0000: b624 0000 NIST X'0009'(0),X'0000' PSW1: 810e0020->81100020
[ 11] T=34.50 010110 IOINIT +0002: bef3 STH 6,X'019c' PSW1: 81100020->81120020
[ 12] T=35.00 010112 IOINIT +0004: bdf3 STH 5,X'019a' PSW1: 81120020->81140020
[ 13] T=38.75 010114 IOINIT +0006: e4f7 0038 BAL 4,X'0038' R04: 81660020->81160020, PSW1: 81140020->814e0020
[ 14] T=39.00 01014e IOINIT +0040: c7e4 BCR 7,4 PSW1: 814e0020->81160020
[ 15] T=56.50 010116 IOINIT +0008: 97e8 SRET 7,0 R00: 02460012->021e0028, R04: 81160020->81660020,
PSW1: 81160020->800f0020
[ 16] T=56.75 01000f $0HELLO +000f: eaf1 LA 2,X'003b'(1) R02: 00000000->01890000, PSW1: 800f0020->80110020
.
.
.
[ 77] T=157.18 010121 IOINIT +0013: b624 0000 NIST X'0009'(0),X'0000' PSW1: 81210020->81230020
[ 78] T=157.18 010123 IOINIT +0015: df16 BCB 7,X'0005' PSW1: 81230020->811f0020
[ 79] T=157.43 01011f IOINIT +0011: c0e0 BCR 0,0 PSW1: 811f0020->81200020
[ 80] T=174.93 010120 IOINIT +0012: 97e8 SRET 7,0 R00: 02580012->02460012, PSW1: 81200020->810d0020
[ 81] T=192.43 01010d COUTP +0013: 97e8 SRET 7,0 R00: 02460012->021e0028, R01: 00000000->014e0000,
R02: 01900000->01890000, R03: 00000000->01520000,
R04: 81070020->81660020, R05: 47000000->00030000,
R06: 000d0000->00060000, PSW1: 810d0020->80130020
HAL/S 8: DO FOR I = 1 TO 5;
[ 82] T=192.68 010013 $0HELLO +0013: eff3 0001 LHI 7,X'0001' R07: 00000000->00010000, PSW1: 80130020->80150020
[ 83] T=192.93 010015 $0HELLO +0015: dfb4 BCF 7,X'002d' PSW1: 80150020->80430020
HAL/S 13: END;
[ 84] T=193.43 010043 $0HELLO +0043: bf19 STH 7,X'0006'(1) PSW1: 80430020->80440020
[ 85] T=193.68 010044 $0HELLO +0044: b5e7 0005 CHI 7,X'0005' CC: 0->3, PSW1: 80440020->8046c020
[ 86] T=193.68 010046 $0HELLO +0046: dec6 BCB 6,X'0031' PSW1: 8046c020->8016c020
HAL/S 9: 1 WRITE(PRINTER) I, 'HELLO, WORLD!';
[ 87] T=193.93 010016 $0HELLO +0016: eef3 0006 LHI 6,X'0006' PSW1: 8016c020->8018c020
[ 88] T=194.18 010018 $0HELLO +0018: edf3 0003 LHI 5,X'0003' PSW1: 8018c020->801ac020
[ 89] T=218.68 01001a $0HELLO +001a: d0ff 394a SCAL 0,X'014a'(1,) R00: 021e0028->02460012, PSW1: 801ac020->810ec020
[ 90] T=221.68 01010e IOINIT +0000: b624 0000 NIST X'0009'(0),X'0000' CC: 3->0, PSW1: 810ec020->81100020
[ 91] T=222.18 010110 IOINIT +0002: bef3 STH 6,X'019c' PSW1: 81100020->81120020
[ 92] T=222.68 010112 IOINIT +0004: bdf3 STH 5,X'019a' PSW1: 81120020->81140020
[ 93] T=226.43 010114 IOINIT +0006: e4f7 0038 BAL 4,X'0038' R04: 81660020->81160020, PSW1: 81140020->814e0020
[ 94] T=226.68 01014e IOINIT +0040: c7e4 BCR 7,4 PSW1: 814e0020->81160020
[ 95] T=244.18 010116 IOINIT +0008: 97e8 SRET 7,0 R00: 02460012->021e0028, R04: 81160020->81660020,
CC: 0->3, PSW1: 81160020->801cc020
[ 96] T=244.43 01001c $0HELLO +001c: 1de7 LR 5,7 R05: 00030000->00010000, CC: 3->1,
PSW1: 801cc020->801d4020
[ 97] T=268.93 01001d $0HELLO +001d: d0ff 3948 SCAL 0,X'0148'(1,) R00: 021e0028->02460012, PSW1: 801d4020->81174020
[ 98] T=271.93 010117 IOINIT +0009: b624 0000 NIST X'0009'(0),X'0000' CC: 1->0, PSW1: 81174020->81190020
[ 99] T=272.43 010119 IOINIT +000b: bdf3 STH 5,X'019c' PSW1: 81190020->811b0020
[ 100] T=272.68 01011b IOINIT +000d: eef3 000a LHI 6,X'000a' R06: 00060000->000a0000, PSW1: 811b0020->811d0020
[ 101] T=273.18 01011d IOINIT +000f: bef3 STH 6,X'019a' PSW1: 811d0020->811f0020
THE BEGINNING
[ 102] T=273.43 01011f IOINIT +0011: c0e0 BCR 0,0 PSW1: 811f0020->81200020
[ 103] T=290.93 010120 IOINIT +0012: 97e8 SRET 7,0 R00: 02460012->021e0028, R06: 000a0000->00060000,
CC: 0->1, PSW1: 81200020->801f4020
[ 104] T=291.18 01001f $0HELLO +001f: eacd LA 2,X'0033'(1) R02: 01890000->01810000, PSW1: 801f4020->80204020
--- Registers (step 105, bank 0) ---
R00=021e0028 R01=014e0000 R02=01810000 R03=01520000
R04=81660020 R05=00010000 R06=00060000 R07=00010000
FP0=00000000 FP1=00000000 FP2=00000000 FP3=00000000
FP4=00000000 FP5=00000000 FP6=00000000 FP7=00000000
PSW1=80204020 PSW2=00080000 NIA=10020 CC=1 BSR=2 DSR=0
[ 105] T=291.18 >> 10020 $0HELLO +00020: d0ff 3946 SCAL 0,X'0146'(1,)
gpc>

There are some interesting features in the trace above that you might amuse yourself by pondering.  Four lines are bolded in the excerpt above.  Notice that the first of these 4 lines is the one that caused the last of the 4 (a simple text message) to be printed.  Why did it take so long to print?  Why are there several statements in between the command and the printed line?  Don't rationalize that it's because some other code did the printing, and that it took a while to spit it out, because you're already looking at every single instruction executed.  Now look at the middle two bolded lines:  Where is all of the stuff that's supposed to go between the DO FOR and its END?  In case you don't remember — and why would you? —, that stuff looks like the bolded stuff below:

          8 M|   DO FOR I = 1 TO 5;                                                                                |HELLO
9 M| 1 WRITE(PRINTER) I, 'HELLO, WORLD!'; |HELLO
^^^^^^^
10 M| 1 DO FOR J = 2 TO 8 BY 2; |HELLO

E| , |
11 M| 2 WRITE(PRINTER) ' ', J, MY_NAME || ' SAYS ISN''T THIS FUN?'; |HELLO
^^^^^^^
12 M| 1 END; |ST#10
13 M| END; |ST#8
Where did it go?  I'll give you a hint: If I thought you were interested in seeing it, I would have included a lot more of the run, and you would have seen the missing lines farther down the page ... and indeed, you would have seen them several times.

At any rate, I'm not here to teach you how to debug an assembly-language program, so I'll end my little demo at this point.

Accuracy Of the Emulators

I can only speak to the accuracy of the in-house emulators, yaHALMAT2 and yaGPC2.  I've already mentioned that it's basically impossible for yaHALMAT2 to have an exact timing model, so the "accuracy" I'm referring to is behavioral accuracy.

The two programs represent very different development approaches, with one emulating HALMAT and the other emulating AP-101S assembly language, so a principal tool in finding bugs in them has been to directly compare the results they produce when confronted with the same HAL/S source code.  Because A.I. coding agents are endlessly patient, it was feasible to track down every discrepancy that came up in such a comparison, and to eliminate all of the associated bug(s) in whichever (or both!) of the emulators was demonstrated to be at fault.  This not to say that it was possible to obtain 100% agreement in all cases. With even the slightest thought about it, it should be obvious (for example) that it is impossible to guarantee exact timing agreement between the two, besides which there are a few ways in which which AP-101S results depend on specific hardware quirks that cannot be duplicated in an abstraction like HALMAT.  Nevertheless, this stress testing gives us (by which I mean me) some confidence that the emulators work pretty well.  Others have expressed caution.  You too, of course, can form your own personal opinion.

At this writing, the emulators have no ability to communicate with Space Shuttle peripheral devices such as the DEU.  At some point in the not-too-distant future, that capability will be built into both emulators.  For now, their usefulness is limited to "general purpose" HAL/S programming rather than Space Shuttle programming specifically.

A bit of collateral damage from the stress-testing approach was that actual bugs were found in the AP-101S assembly-language programs comprising the run-time library (RTL) supplied with the original HAL/S compiler, and thus inherited by us.  Most of these bugs related to delivery of wrong but "close" numerical results for library functions like SQRT and transcendental functions (SIN, COS, LOG, etc.).  A function for finding strings within other strings (CTIME) actually doubled the search length, and so if you were searching (say) string C1 for string C2, you might actually end up finding a match in the strlen(C1) characters following C1.  The worst of the bugs made it highly unlikely that you could compute matrix determinants or inverses that were even vaguely correct for 4×4 matrices.  Reluctantly, I concluded that the RTL itself needed to be fixed, at least to the extent needed to support testing of the emulators.  These RTL fixes were effected in a very innocuous way, though, in that they only take effect when assembling the RTL using our own ASM101S assembler — and not, for example, with the original Intermetrics assembler —, and that even with ASM101S they can be disabled with a simple command-line switch ... and probably will be disabled for building full images of PASS.  (For what it's worth, the affected RTL source-code files are CINDEX.asm, MM14SN.asm, MM6SN.asm, MV6SN.asm, VV6S3.asm, and VX6S3.asm.  Just search for the string "&ASM101S" to see the differences, and explanations thereof.)

Integration Into Spaceflight Simulators Or Other Software

I'll confine my remarks in this section to the yaHALMAT2 and yaGPC2 emulators.  These two emulators have an architecture and a mutual consistency that hopefully can assist anybody wishing to bypass our stand-alone programs entirely, in order to integrate one or both styles of emulation into other software such as a Space Shuttle simulator.  For example, a Space Shuttle had five separate General Purpose Computers (GPC), all intercommunicating and running the same or different software, and the architecture of these emulators is such that you can achieve that integration relatively easily, even to the extent of mixing and matching which of the two emulators is used for which of the individual GPC's in the simulated spacecraft.  On the other hand, that same architecture allows for the much simpler task of creating a stand-alone single emulator with a built-in debugger, such as the yaHALMAT2 and yaGPC2 programs themselves.

Let's look at some simple examples of how you might integrate emulators like this into your software.  These examples will be very simplified, because I don't want to clutter them up with irrelevant details.  For example, we're going to suppose you don't want a debugger, that all your files are just exactly in the directories we want them, that you magically have a function now_us() which returns the time (in microseconds) since program start, that you have another function sleep_us() which "sleeps" for a desired number of microseconds, and that you're fine running HAL/S programs that use default WRITE(6) and READ(5) i/o.

First, let's just consider how you might write a program running a single AP-101S emulator instance, with some hard-coded filenames indicating the specific HAL/S program (HELLO.hal) that you want to run in your emulator.  It's set up to run 500 instructions, sleep a little, run 500 more instructions, and so on, in a rhythm that roughly tracks real time:
#include "yaGpcIntegration.h"

int
main(void) {
const GpcOps *Ops = &yaGPC2_ops; /* run an AP-101S emulation */
GpcState state = {0}; /* allocate a state structure for the emulator */

if (!Ops->initializer(&state, "hello.fcm", "hello-lnk101.json", NULL, NULL, NULL, NULL, NULL))
return 1; /* error! */

double wallStartUs = now_us(); /* timestamp for program start */
GpcEngineStatus status = GPC_ENGINE_RUNNING;

while (status >= GPC_ENGINE_RUNNING) {
for (int i = 0; i < 500 && status >= GPC_ENGINE_RUNNING; i++) {
status = Ops->engine(&state);
if (status < 0)
return 1; /* error! */
}
double wallElapsedUs = now_us() - wallStartUs; /* time since startup */
double aheadUs = state.elapsedTime - wallElapsedUs;
if (aheadUs > 0) /* have we reached real time yet? */
sleep_us(aheadUs); /* if so, we need to sleep for a while.
}

Ops->release(&state);

return 0;
}
That's it!  How might we change it to instead have a HALMAT emulation of the very same HELLO.hal?  I've bold-faced everything you need to change, so you get no prize for guessing.  We just make the changes
    ...
const GpcOps *Ops = &yaHALMAT2_ops; /* run a HALMAT emulation */
...
if (!Ops->initializer(&state, "optmat.bin", NULL, NULL, NULL, NULL, NULL, NULL))
...
We may as well discuss how to actually build with such code too.  What you need is all in the Virtual AGC source tree, in directory yaShuttle/yaGpcIntegration/.  In fact, the installation instructions above for yaHALMAT2 (or for yaGPC2, since the instructions are identical) have already insured that that directory has link-libraries for building your own code.  But there you'll also find the header file yaGpcIntegration.h, improved source code and executable for the sample above, and of course, the contents of the Makefile itself to show you how to structure your own build.  The improved source code and the executable are found in the samples/ subdirectory, and are used like
paced_run yagpc2|yahalmat2 FILENAME1 [FILENAME2]
The first argument, literally "yagpc2" or "yahalmat2", naturally selects whether to run an AP-101S emulator or a HALMAT emulator.  FILENAME1 is either the name of a memory-image file created by the linker (.fcm) or a HALMAT file created by the compiler (halmat.bin, optmat.bin, or FILE1.bin) respectively.  FILENAME2 is needed only in the yagpc2 case, and specifies the symbol-table file created by the linker (.json); it's required rather than optional, because it's where the program's entry address comes from.  But for example,
> paced_run yagpc2 HELLO.fcm HELLO-lnk101.json
THE BEGINNING
1 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
4 RON BURKEY SAYS ISN'T THIS FUN?
6 RON BURKEY SAYS ISN'T THIS FUN?
8 RON BURKEY SAYS ISN'T THIS FUN?
2 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
4 RON BURKEY SAYS ISN'T THIS FUN?
6 RON BURKEY SAYS ISN'T THIS FUN?
8 RON BURKEY SAYS ISN'T THIS FUN?
3 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
4 RON BURKEY SAYS ISN'T THIS FUN?
6 RON BURKEY SAYS ISN'T THIS FUN?
8 RON BURKEY SAYS ISN'T THIS FUN?
4 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
4 RON BURKEY SAYS ISN'T THIS FUN?
6 RON BURKEY SAYS ISN'T THIS FUN?
8 RON BURKEY SAYS ISN'T THIS FUN?
5 HELLO, WORLD!
2 RON BURKEY SAYS ISN'T THIS FUN?
4 RON BURKEY SAYS ISN'T THIS FUN?
6 RON BURKEY SAYS ISN'T THIS FUN?
8 RON BURKEY SAYS ISN'T THIS FUN?
THE END
stopped (yagpc2): halted: clean, expected program termination (elapsedTime=10970.23 us)
The "elapsed time" it prints at the end is actually emulated time, i.e. how long the emulator thinks the timings of all instructions add up to.

To run (say) five emulators simultaneously within the loop is a bit trickier, because the timings of all the GPC's will differ, but not terribly so.  I'll just explain that in pseudo-code for now, and perhaps develop some realistic sample code for it later.  Basically:
In other words, you execute a burst of instructions, followed by sleeping for about 10 ms, followed by a burst of instructions, and so on.  Simple-minded scheduling?  Sure!  Use something better if you've got it.

So what are all the functions we've been seeing in the sample? 

Description
Function
The "engine" that executes a single "instruction", which you should understand to mean as either a HALMAT instruction or an AP-101S instruction.  It returns a numeric code that summarizes the current state, according to the following general theme:
  • 0 means that there was no error or warning in executing the instruction, and it's okay to keep executing more instructions.
  • 1-999 means that there was a warning or informational message from the emulator, but that it's okay to execute more instructions.
  • 1000- means that there was a warning that's particular to conditions specified in Space Shuttle documentation.
  • -1 means that the program whose run is being emulated has terminated normally, and no further execution is possible.
  • Other negative values are other fatal errors indicating that no further execution is possible.

A text string describing error codes can be obtained by feeding the error code to the generic function gpc_engine_status_message.

engine(GpcState *state)
Initialize the state structure that holds the entire state of a single instance of an emulator.  The state structure must have been allocated by calling program prior to invoking the initializer, which merely initializes the structure in-place:
  • programPath is the filename of a file containing "optimized HALMAT" for yaHALMAT2-style emulation (produced by the OPT pass of the HAL/S compiler HALSFC), or an AP-101S memory image for yaGPC2-style emulation (produced by the lnk101 linker).
  • symbolsPath is not used in yaHALMAT2-style emulation (can be NULL), but is the filename of a JSON symbol-table report for yaGPC2-style emulation (produced by the lnk101 linker)
  • servicer is an optional callback function you must provide as an integrator, to handle how the emulated GPC communicates with its peripheral devices within the simulated Space Shuttle.  We'll talk about that later on.  If you're just doing general HAL/S programming, it can be NULL.
  • servicerCtx is a pointer to an optional structure used by the servicer callback-function you provide, for any purpose whatever that you need.  It's structure is entirely up to you.  It can be NULL if your servicer doesn't need it for anything.
  • output is an optional callback function which you must provide as an integrator, to handle output from a HAL/S WRITE statement.  If you're willing to accept the default that WRITE(6) goes to stdout, or if you're making a simulation that has no terminal devices, you can simply use NULL.
  • input is an optional callback function which you must provide as an integrator, to handle input from a HAL/S READ statement.  If you're willing to accept the default that READ(5) comes from stdin, or if you're making a simulation that has no terminal devices, you can simply use NULL.
  • ioCtx is a pointer to an optional structure used by the output and input callback-functions you provide, for any purpose whatever that you need.  It's structure is entirely up to you.  It can be NULL if your output and input  don't need it for anything.
GpcState state = { 0 };
initializer(GpcState *state,
            const char *programPath,
            const char *symbolsPath,
            GpcServicerFn servicer,
            void *servicerCtx,
            GpcOutputFn output,
            GpcInputFn input,
            void *ioCtx)
Releases (frees all allocated memory) internal to the state structure.  If you allocate the state structure itself (say, with malloc), rather than declaring it statically, you'll have to free it yourself after release'ing it.
release(GpcState *state)
(Optional.)  The command-line debugger, with gdb-style commands, to the extent feasible.  Returns true on failure.
debugger(GpcState *state,
         void *dbgState)
(Only if the debugger function is used.)  Creates (allocates memory for) and initializes the dbgState structure which holds the entire state information of the debugger, returning a pointer to the structure on success or NULL on failure.  The sourceMapPath argument is not used for yaHALMAT2-style emulation, and may be NULL.  For yaGPC2-style emulation it is the name of a file created by the utility gen_source_map.py that holds the text of all of the HAL/S source-code lines displayed while using the debugger.
void *debuggerStateCreate(const char *sourceMapPath)
(Only if the debugger function is used.)  Releases (frees all memory for) the dbgState.
debuggerStateDestroy(void *dbgState)

What I haven't talked about so far is "callback" functions you need to provide as an integrator, of which three different types are allowed for, but none are used in any of the sample code above.  The three types of callback functions are:




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

Virtual AGC is
              hosted by ibiblio.org