Again, the choice is yours.
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:
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:
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.
> 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:
PROGRAM (versus a PROCEDURE,
FUNCTION, or COMPOOL).PROGRAM is named HELLO.
That information is just to help out the reader, though.
HALMAT only cares that it's "Symbol #1", and not what it's
particular name is in your imagination.1 M| HELLO:
followed by 1 M| PROGRAM;.2 M| DECLARE I INTEGER;,
which apparently doesn't require any HALMAT instructions.3 M| DECLARE MY_NAME
CHARACTER(20) INITIAL('RON BURKEY');,
"Symbol #3" is a "CHARACTER INITIAL value", whose symbol for
your reading ease is MY_NAME, and whose value is
"RON BURKEY".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 |
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:I bet you're scratching your head over that, aren't you?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;
"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.Exactly as you might guess from our purple highlights!> 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'
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.
|
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:On Windows? Maybe it will work. I don't know, I've only tried it on Linux myself.cd Halmat/emu 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. |
Compile it:HELLO: PROGRAM;
WRITE(6) 'Hello, world!';
CLOSE HELLO;
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:HALSFC --test REALLY_HELLO.hal
yaHALMAT halmat.bin
yaHALMAT optmat.bin
but I accomplished the same thing by adding theHAL_S_FC.py --hal=REALLY_HELLO.hal
--test
switch to the HALSFC command line, so we end up with the
files we wanted anyway:The output in any of these emulations looks exactly the same:yaHALMAT --litfile FILE2.bin --common LIT_CHAR.bin FILE1.bin
But instead, let's try the goofy HELLO.hal we've been using to demonstrate lots of stuff, namelyHello, world!
Compile and run it: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;
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.> 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
Emulating this we get: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;
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
|
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 The executable will appear in the directory virtualagc/yaShuttle/yaGpcIntegration/.cd 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. |
Compile it and run it: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;
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:> 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 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.> 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
N=x field tells you the cumulative
number of HALMAT instructions executed so far.T=x.xx field tells you the total
amount of execution time so far, in microseconds. In fact,
as explained earlier, HALMAT instructions are ideal; i.e., they
exist outside of time, and have no timing associated with
them. But for the purpose of the emulation, we've assigned
each HALMAT instruction a timing, which was obtained by
statistically analyzing the average amount of time that AP-101S
instructions generated from the HALMAT would have taken.
Do you find it odd that in our example above, the time seems to
be stuck at T=60.00? It's not, really.
I just happened to show you a selection involving mostly
declarations, and those don't consume any runtime.[x: yyyy] pertains to
the fact that there may be several HAL/S programs loaded into
the emulator at any given time (see below). This field
tells you that the current instruction belongs to program #x,
whose name is "yyyy".#nn field tells you
where in the raw HALMAT data-stream the current instruction is
located.0xnnn field is the actual numerical
code for the instruction, in hexadecimal.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 switchyaHALMAT2 [OPTIONS] HALMAT_FILE
--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.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 sayyaHALMAT2 [OPTIONS] @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.yaHALMAT2 [OPTIONS] --opt @LIST
Aside: One useful trick is to make a file — let's call it CURRENT — that just contains the single line "current.results". Then invokingActually 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/SyaHALMAT2 @CURRENTalways runs the HAL/S program that you've compiled most recently.
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 |
C MYTABLE.hal |
C SQUROO.hal |
This creates directories for the compiler output products of:HALSFC --clean --archive --parms=TEMPLATE SQUARE.hal
HALSFC --clean --archive --parms=TEMPLATE SQUROO.hal
HALSFC --clean --archive MYTABLE.hal
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: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
Multi-file capability isn't fully implemented yet in yaHALMAT2. HAL/S real-time mechanisms allow multiple> 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
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!--link-only option: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:yaHALMAT2 --link-only MYTABLE.yhla @LIST
"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 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:> 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
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;
Windows users may wonder why the command to start the emulation has the word> 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
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.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 aAnd here's what happens when yaHALMAT2 runs it:TASKre-SCHEDULEitself is not something it's entirely clear from the original documentation that it's something actually allowed. Also, you'll notice that thePROGRAMends immediately after theSCHEDULEstatement, yet theTASKkeeps 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.
In case you wonder about the command-line option> 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
--line-length 132, printouts by default wrap around to the next
line afer 80 columns. This option just increases that to
132 columns instead.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.
Compiling it and linking it like so,C A TEST PROGAM
HELLO_WORLD: PROGRAM;
WRITE(6) 'Hello, world!';
WRITE(6) '';
CLOSE HELLO_WORLD;
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 programHALSFC -o HELLO_WORLD.obj HELLO_WORLD.hal
lnk101 -o HELLO_WORLD.fcm --json-symbols HELLO_WORLD.sym.json HELLO_WORLD.obj
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'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.
|
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:In Windows, replace the final line above withgit clone https://github.com/rburkey2005/yaGPC.git cd yaGPC git checkout yaGPC cd yaGPC make The executable file yaGPC[.exe] must then be put into yournmake /f NMakefile 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.
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...)".
|
Installation of
ap0000a
Not yet publicly available. |
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 The executable will appear in that same directory.cd 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 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--fcosswitch 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:
SCALAR outside the
supported INTEGER range of -32768
through 32767 is converted to an INTEGER, then
FCOS (or --fcos) will clamp it to the
-32767 or 32767, but otherwise it will just be
truncated to 16 bits just by masking it with 0xFFFF. As it
happens, this is not actually a situation that arises all
that often, and won't happen at all in well-designed
software. (Slightly confusingly, the contemporary
documentation, namely the HAL/S-FC
User's Manual, states that the "standard fixup"
in this situation is to clamp at -32768 or
32767. This would make sense, but the actual FCOS
source code, FPMSDERR.asm, does not agree with the
documentation. And in case you're a tech nerd who
worries about these things, the conversion from SCALAR
to INTEGER is largely handled by an AP-101S
instruction called CVFX. Upon overflow
in CVFX, an interrupt is triggered that ends
up being handled in FPMSDERR.asm. If FCOS
isn't there, then that interrupt never occurs because it
never will have been set up.)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--debugswitch.
What are we actually seeing here? Well, starting on the far
left:
[NNNNN] is the number of instructions
executed so far, in decimal. It'll quickly grow far past 5
digits for any realistic program, but we'll give it 5 to start
with. (HELLO.hal takes about 6700 instructions to run to
completion.)T=N.NN is the elapsed (simulated) time so
far, in μs.NNNNN (after the >>) is the absolute
address in memory of the instruction. The AP-101S has halfword
(i.e., 16-bit) memory, so address 0x00000 is the first halfword
in memory, 0x00001 is the second halfword, and so on.START above (or $0HELLO
below) is the name of the "CSECT" to which the instruction
belongs.+NNNNN next to the CSECT
name is the offset into the CSECT at which the
instruction resides, in halfwords. So evidently, CSECT
START is located at address 0x10164, $0HELLO
(below) is located at 0x10000, and so on.XXXX (or XXXX XXXX)
is the hexadecimal value of the halfword (or halfwords) stored
at that address.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:
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.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
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.)
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.
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.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#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;
}
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...
const GpcOps *Ops = &yaHALMAT2_ops; /* run a HALMAT emulation */
...
if (!Ops->initializer(&state, "optmat.bin", NULL, NULL, NULL, NULL, NULL, NULL))
...
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 likeThe first argument, literally "paced_run yagpc2|yahalmat2 FILENAME1 [FILENAME2]
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,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.> 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)
GpcState structures for each of the
five emulators and store them in an array. There's a .gpcID
field in the GpcState structures, not used in the
sample code above, that you might want to set at 1, 2, 3, 4, and
5. You can mix-and-match emulator types.N=5000 microseconds.N, then sleep until it's older than real time
minus N.N,
executing a single instruction in the oldest emulator.
If the execution engine reports a termination, remove the
emulator from the array.| 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:
A text string describing error codes can be obtained by
feeding the error code to the generic function |
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:
|
GpcState
state = { 0 }; |
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, |
(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) |
WRITE(N)
statements. By default, device N=6 is
connect to stdout, and if you're happy with that
then you don't need this callback at all. Otherwise, you
need to provide a function, say void MyOut(void *ioCtx,
int N, const char *text). Your
callback will simply handle "outputting" text
on logical device N. ioCtx
can be anything you like, such as a structure holding filenames,
FILE * pointers to open files, etc., or nothing at
all. It's up to you.READ(N) or READALL(N)
statements. By default, N=5 is stdin,
and if that's all you need, you don't need the callback at
all. If you do need it, say bool MyIn(void
*ioCtx, int N, char *buf, size_t bufSize),
make it read a line of text from logical device N
into buf, NUL-terminating it. Returns
true on success, or false on end-of-file. You might wonder
how a READ or READALL interacts with
HAL/S real time scheduling features? I did!
Scheduling is based on emulated time, not real time, and
no emulated time passes while you're sitting at a READ
waiting for input. In real Shuttle code, there are of course no
READ (or WRITE) statements, but there
are in HAL/S code compiled with HAL/S-360, the
System/360 version of the HAL/S compiler, and interestingly, READ
did not consume time there either. Why? Because on a
System/360 there were no terminals to interactively input data
into a READ anyway; data for a READ
was directly embedded into the Job Control Language (JCL) for
running the HAL/S program as punch-cards, and hence did not
consume real time because it was always instantly
available.
