Home
yaAGS et al.
Download
Links
Change log
Bug and issues
Developer info
FAQ
Virtual AGC and AGS
Developer Info  

yaAGC
yaYUL
yaDSKY
yaOtherStuff
Luminary
Colossus
Language Manual
Physical Implementations

Contents


Interfacing yaAGC to yaDSKY, yaAGS to yaDEDA, or Other Simulated Hardware, in General

The method used by yaAGC or yaAGS to interface to virtual hardware, such as yaDSKY, has been chosen to be as generic as possible (to promote portability). For example, the same method is used virtually unchanged on both Linux and on MS-Windows.

At the same time, the system is as modular as possible, with yaAGC and yaDSKY being separate standalone programs running on the same computer or (theoretically) on different computers. This promotes the possibility of changing or even completely replacing yaDSKY---or of introducing new stand-alone programs for simulating other Apollo hardware---without changing yaAGC itself.

yaAGC and yaDSKY are, respectively, a server and client communicating with each other over an IP network via the mechanism of sockets. At boot time, the yaDSKY client (or clients for other simulated hardware) connects to the yaAGC server. The only necessity for a client program representing a hardware simulation is to be able to connect to the corrrect port on the server, and to communicate input or output data in an acceptable format.  (But the socket interface could be replaced with relatively little effort---say, to a shared-memory interface.  See below.)

The real AGC received input from hardware, or outputted control signals to hardware, by means of instructions which accessed "i/o channels" in distinction to main memory. yaAGC mimics this behavior by simply translating "output channel" assembly-language instructions to server broadcasts, and by making data received from clients available to subsequent "input channel" assembly-language instructions. Except for a few i/o channels that correspond to known functionality within the AGC itself, yaAGC can thus interact with simulated hardware in a completely generic way, without assigning any interpretation to the data other than the interpretations assigned by the Luminary, Colossus, or other Apollo software being executed by it.

This method does impose certain response-time limitations on AGC i/o, but does not impose any practical bandwidth limitation.

By default, the yaAGC server listens for new connections on ports 19697-19701. (This can be changed by a command-line switch in invoking yaAGC.) Similarly, hardware simulations like yaDSKY attempt to connect to port 19697 at boot time. By default, the clients assume that the server is running on the same machine as they are (i.e., on "localhost"), but are able to change their assumptions about the server's IP address and about the port number with command-line switches.

This socket mechanism is almost completely generic at the application-programmer's level, and has been chosen for precisely that reason. In other words, it can be used on UNIX-like systems and on Microsoft Windows almost without change. For more information, I'd suggest looking at the yaAGC source code, and (if you develop in a UNIX-like environment) at `man 2 socket', `man 2 send', `man 2 recv', etc.  Or, look at the helpfiles in the Win32 SDK. Essentially the only differences between Linux and Win32 versions involve initialization of the socket system, and of configuring the communication to be non-blocking.

Using LM Hardware-Simulation Programs with Multiple AGCs or the AGS

Note that some of the LM hardware could be placed under control of either the LM AGC or the CM AGC; also, the two AGCs could be interconnected to exchange the setup-data of the two. To simulate such a situation, it will (of course) be necessary to run two copies of yaAGC simultaneously, at different port addresses. Simulation software for such hardware will obviously want to connect to both yaAGC servers, and to apply some rationale as to which of them to listen to at any given time. Since I don't presently plan to personally implement such additional simulations, the details are left as an exercise to the reader.

However, to avoid conflict or confusion among developers, I would suggest the following default use of ports:
Note that for the case of communication between the AGC and AGS, it is necessary to use either yaAGC socket protocol or yaAGS socket protocol to exchange data.  We choose in this case to allow yaAGS to use yaAGC protocol.  A similar situation may arise where the AGC and AGS use the same peripheral device -- for example, receiving the same gimbal-angle increment/decrement pulses.  Rather than forcing the peripheral device to output the same data in two different protocols, it is more reasonable for yaAGS simply to interpret yaAGC protocol.

Obviously, suggestions for improvement are welcomed.

Use of yaAGC as Embedded Software

This is quite simple, in principle. If your embedded system is running a POSIX-compliant operating system, you can just go ahead and run yaAGC. If not---if, for example, you have no operating system---here's what you need to do:

Provide replacements for the send and recv functions, so that when yaAGC issues these commands, the appropriate hardware is controlled. Also, provided stubs for other socket-related functions like socket, so that they don't do anything.  Alternately, replace the ChannelInput, ChannelOutput, and ChannelRoutine functions, as described below.

Instead of using the main program provided with yaAGC, you'll instead use libyaAGC.a as a library and provide your own main function. Provide startup code to initialize any hardware peripherals you're providing, and to issue timer interrupts every 11.7  µs. Your own main function doesn't really need to do any more than this.

Upon receiving a timer interrupt, vector to the agc_engine function. This will result in one AGC memory cycle occurring every 11.7  µs., and of the simulated software (Luminary, Colossus, or whatever) issuing send or recv commands (or ChannelOutput and ChannelInput commands) to interact with whatever hardware you've provided.

send/recv Protocol

As mentioned above, yaAGC and yaAGS send data to simulated hardware using the send function, and the simulated hardware receives it using the recv function and vice versa. Every write to an "output channel" by the AGC or AGS results in a send. (Or, to improve bandwidth by reducing overhead, multiple output-channel writes can be ganged into a single send.) It only remains to understand the format of the transmitted data.  If for some reason you can't abide this approach, replace the ChannelInput, ChannelOutput, and ChannelRoutine functions as described below to eliminate the socket-based interface.

yaAGC vs. yaAGS Packets

The yaAGC and yaAGS programs each use a system of 4-byte packets for i/o.  The packets of the two programs are distinguishable by the two-bit "signatures" that appear in the most-significant bits of each byte.  Therefore, both types of packets can appear on the same socket connection, without fear of misinterpretation.  This is important, for example, on the socket connection by which the yaAGC and yaAGC intercommunicate.

The signature bits of a yaAGC packet look like
00xxxxxx 01xxxxxx 10xxxxxx 11xxxxxx
whereas the signature bits of a yaAGS packet look like
00xxxxxx 11xxxxxx 10xxxxxx 01xxxxxx

Obviously there is some room here for defining additional types of packets in the future, just by insuring that the 00 signature byte always comes first, and rearranging the other three signature bytes.

yaAGS packets and yaAGC packets are further defined in the following sections.

Packets for Socket Implementation of AGS I/O System

Packets input or output by yaAGS for implementation of the i/o system are of the form, as represented in bits:

00tttttt 11dddddd 10dddddd 01dddddd

The field tttttt represents the type of the packet (00-77 octal), whereas the 18-bit field dddddddddddddddddd represents the packet's data.

Specifically, the tttttt field is interpreted as follows (all numbers in octal):

tttttt I/O Direction
I/O Address
Significant Bits
Interpretation
00
Input (to CPU)
2001
15
PGNS theta integrator.
01
Input 2002
15
PGNS phi integrator.
02
Input 2004
15
PGNS psi integrator.
03
Input 2010
n/a
(Not used)
04
Input 2020
8 + 8
Discrete Input Word 1.  The bit positions are as follows.  Note that only the bits labeled "discrete" actually appear in the i/o register at address 2020.  The bits labeled "mask" are used to indicate which of the "discrete" bits are valid.  In other words, the only bits of i/o register 2020 that change are those with a "mask" of 1.  The bits with a mask of 0 do not change.  This scheme is used because different peripheral devices, connected to the CPU on different sockets, may control different discretes.  Thus, several different peripherals might send the CPU Discrete Input Word 1 packets, each affecting only the bits of i/o register that are specifically under their control, without any conflict.

Note:  The discrete inputs are active when 0 and inactive when 1.  The mask bits are active when 1 and inactive when 0.

Bitmask
(octal)
Interpretation
200000 Downlink Telemetry Stop discrete.  Note that yaAGS will automatically activate this flag when  a downlink telemetry word is received, so there is no need to set it separately.
100000 Output Telemetry Stop discrete
040000 Follow-Up discrete
020000 Automatic discrete
010000 Descent Engine On discrete
004000 Ascent Engine On discrete
002000 Abort discrete
001000 Abort Stage discrete
000200
Downlink Telemetry Stop mask
000100
Output Telemetry Stop mask
000040
Follow-Up mask
000020
Automatic mask
000010
Descent Engine On mask
000004
Ascent Engine On mask
000002
Abort mask
000001
Abort Stage mask
05
Input 2040
7 + 7
Discrete Input Word 2.  The bit positions are as follows.  The "mask" bits vs. the "discrete" bits are explained above.

Note:  The discrete inputs are active when 0 and inactive when 1.  The mask bits are active when 1 and inactive when 0.

Bitmask
(octal)
Interpretation
200000 GSE Discrete 1
100000
GSE Discrete 2
040000 GSE Discrete 3
020000 DEDA Clear discrete
010000 DEDA Hold discrete
004000 DEDA Enter discrete
002000 DEDA Readout discrete
000200
GSE mask 1
000100
GSE mask 2
000040
GSE mask 3
000020
DEDA Clear mask
000010
DEDA Hold mask
000004
DEDA Enter mask
000002
DEDA Readout mask
06
Input 2100
n/a
(Not used)
07
Input 2200
4
DEDA
10
Input 6001
n/a
(Not used)
11
Input 6002
11
delta-integral-q counter.  This register is automatically reset after being read by the CPU (with an INP instruction).
12
Input 6004
11
delta-integral-r counter.  This register is automatically reset after being read by the CPU (with an INP instruction).
13
Input 6010
11
delta-integral-p counter.  This register is automatically reset after being read by the CPU (with an INP instruction).
14
Input 6020
11
delta-Vx counter.  This register is automatically reset after being read by the CPU (with an INP instruction).
15
Input 6040
11
delta-Vy counter.  This register is automatically reset after being read by the CPU (with an INP instruction).
16
Input 6100
11
delta-Vz counter.  This register is automatically reset after being read by the CPU (with an INP instruction).
17
Input 6200
18
Downlink Telemetry.  After this register is read by the CPU (with an INP instruction), the Downlink Telemetry Stop discrete (see above) is automatically reset.
20
Output (from CPU)
2001
10
sin theta.  Can be negative (requires sign-extension to full 18-bit value).
21
Output 2002
10
cos theta.  Can be negative (requires sign-extension to full 18-bit value).
22
Output 2004
10
sin phi.  Can be negative (requires sign-extension to full 18-bit value).
23
Output 2010
10
cos phi.  Can be negative (requires sign-extension to full 18-bit value).
24
Output 2020
10
sin psi.  Can be negative (requires sign-extension to full 18-bit value).
25
Output 2040
10
cos psi.  Can be negative (requires sign-extension to full 18-bit value).
26
Output 2100
n/a
(Not used)
27
Output 2200
4
DEDA
30
Output 6001
10
Ex.  Can be negative (requires sign-extension to full 18-bit value).
31
Output 6002
10
Ey.  Can be negative (requires sign-extension to full 18-bit value).
32
Output 6004
10
Ez.  Can be negative (requires sign-extension to full 18-bit value).
33
Output 6010
15
Altitude, Altitude Rate.  Can be negative (requires sign-extension to full 18-bit value).
34
Output 6020
9
Lateral Velocity.  Can be negative (requires sign-extension to full 18-bit value).
35
Output 6040
n/a
(Not used)
36
Output 6100
18
Output Telemetry Word 2.  The full "Output Telemetry Word" is a 24 bit value, comprising "Output Telemetry Word 1" and "Output Telemetry Word 2".  Bits 0-17 are stored in Output Telemetry Word 2 and bits 6-23 are stored in Output Telemetry Word 1, so there is some overlap between the two.  These overlapping fields are supposed to be logically-OR'd together.
37
Output 6200
18
Output Telemetry Word 1.  (See above.)
40
Output
24XX
25XX
26XX
30XX
64XX
70XX
11
Output discretes.  This is a combination of all discretes output by the CPU.  The packet is output any time one of the discretes changes state internally to the CPU, which is any time i/o occurs to one of the associated i/o addresses.  The output discretes are mapped into the output word as follows, in terms of the bits that represent their states.

Note:   To be consistent with the discrete inputs (above), these bits are 0 when active and 1 when inactive.

Bitmask
(octal)
Discrete
Address
for
Set
Address
for
Reset
000001
Ripple Carry Inhibit
2410
3010
000002
Altitude
2420
3040*
000004
Altitude Rate
2440
3040
000010
DEDA Shift In
2500
n/a**
000020
DEDA Shift Out
2600
n/a**
000040
GSE Discrete 4
6401
7001
000100
GSE Discrete 5
6402
7002
000200
GSE Discrete 6
6404
7004
000400
Test Mode Failure
6410
7010
001000
Engine Off
6420
7020
002000
Engine On
6440
7040
*The Altitude discrete is documented as being
reset by writing to i/o address 3040, but it's easy
to come to the conclusion that that's a misprint,
and that 3020 is the correct address.  I don't
think it's a misprint.  I think 3040 is correct.
**These outputs are automatically reset by the
CPU, and therefore it is assumed that they are
always inactive unless specifically seen to be
active.

The input registers marked as "counters" or "integrators" don't receive their values directly from the date fields of the i/o packets.  Rather, the packet's data field contains a code that indicates the kind of operation to be performed, as follows.  Note that the difference between the "integrators" and "counters" is that counters merely increment (by one), whereas "integrators" can be zeroed or can count up or down (by one).

0 Clears the register
1
Increments the register
777777 (-1)
Decrements the register

We actually implement this so that 0 clears the register, but any other value simply adds to the register.  (The value will be automatically scaled so that increments of +/-1 automatically apply at the least-significant bit of the counter or integrator register, regardless of how many bits of precision the register has.

Working with the DEDA

Working with the DEDA is somewhat trickier from a developer's standpoint than working with the DSKY.  The DSKY is simply a dumb terminal, so every depression of a key generates a message to the CPU, and every lighting of an indicator or a digit is the direct response of the DSKY to a message from the CPU.  The DEDA is more complex, and contains more intelligence than the DSKY.  It is more similar to a terminal that buffers entire lines of data.  To avoid data loss, it is strongly advised to follow this paradigm if you want to implement your own DEDA or a replacement for the socket interface.

Let's consider the interaction between the true AEA and DEDA (as opposed to yaAGS and yaDEDA).

The first thing to know about the AEA-DEDA interaction is that data is not spontaneously transferred between the AEA and DEDA.  (Actually, depression of the CLR key or the HOLD key is immediately communicated to the CPU, so we won't worry about those simple cases any further.)  Every 4-bit transfer, in either direction, is triggered explicitly by the program running in the AEA as follows:
Next, you should know that no data transfers between the AEA and DEDA are actually 4-bits long, so each transfer consists of a packet of the 4-bit transfers described above.  There are three types of data transfers between the AEA and DEDA:
  1. Transfer of a 3-octal-digit address from the DEDA to the AEA.  From the user standpoint, the following key sequence is used:  CLEAR OctalDigit OctalDigit OctalDigit READOUT.  The DEDA buffers the 3-octal-digit address and causes the digits to appear in the 3-digit top display.  It is the appearance of the DEDA Readout discrete input that triggers the AEA software to ask for three 4-bit transfers to input the data.  The digits of the address are transmitted in the left-to-right order, and are encoded as you might expect:  0000 for '0', 0001 for '1', 0010 for '2', and so on.
  2. Transfer of a 3-octal-digit address plus 5-signed-digit data from the DEDA to the AEA.  From the user standpoint, the following key sequence is used:  CLEAR OctalDigit OctalDigit OctalDigit +/- Digit Digit Digit Digit Digit ENTR.  Either octal or decimal digits may be used; the AEA software will determine the interpretation of the data (octal vs. decimal) on the basis of the 3-octal-digit address.  This DEDA automatically mirrors the address and data into the top 3-digit display and bottom 5-digit (plus sign) display as it is entered.  It is the appearance of the DEDA Enter discrete input that triggers the AEA software to ask for the nine 4-bit transfers to input the data.  The numerical data is coded as you might expect.  The '+' sign is encoded as 0 and the '-' sign is encoded as 1.
  3. Transfer of a 3-octal-digit address plus 5-signed-digit data from the AEA to the DEDA.  When the DEDA receives this data, it simply overwrites the numerical displays.  The data is encoded exactly as described in #2 above.  A binary code of 1111 may appear in place of any digit or the +/- sign, and indicates that that digit of the display is blanked.
The AEA has no way to set the OPR ERR indicator.  This is handled by the intelligence of the DEDA itself.  Any key-sequence by the astronauts other than #1 or #2 above will cause the OPR ERR to light.  When the OPR ERR lamp is lit, any subsequent data transfers to the AEA via the DEDA shift register are 1111, which is not a legal value otherwise.  The OPR ERR lamp is cleared only by hitting the CLEAR key.

Now let's consider how this affects emulation of the CPU and the DEDA.  The problem which this causes us is that it is very difficult to meet the timing constraints if the yaAGS-to-yaDEDA interaction closely models what has been described above.  There is only an 80 microsecond window from the time the CPU requests DEDA shift-register data, and the time that data must be available to the CPU.  This sounds as if it is a problem with the socket interface, but actually it is a problem with the fact that yaAGS and yaDEDA are separate programs, probably running on the same physical CPU.  It is therefore necessary to buffer incoming yaDEDA data within the yaAGS program, and to provide the interactions described within the yaAGS program, between the CPU and the buffer.  Because I implemented yaDEDA prior to realizing that this was a problem, the interaction between the yaAGS data buffer and yaDEDA also uses this same sequence of operations.  This explanation seems rather incoherent to me, but I can't think of a better way to describe it right now; I'll try to improve the description later.

Packets for Socket Implementation of AGC I/O System

The format is quite simple: each data packet consists of 4 bytes, arranged in bit-fields as follows (in order of transmission):

00utpppp 01pppddd 10dddddd 11dddddd

The AGC theoretically use i/o-channel addresses from 0-511 (decimal), so a 9-bit code would uniquely identifies any i/o port. It turns out that a much smaller range i/o-channel addresses was actually used, so we provide merely an 7-bit code ppppppp for identifying the i/o port. Similarly, dddddddddddddddd is the 15-bit data code. In all cases, the most-significant bit is the leftmost one. The LM used only channels  0-35 (octal). I've not yet found a comprehensive list of CM i/o channels; if such a list extends beyond   0--177 (octal), then this scheme will have to be altered.  (But that seems unlikely.)

The bit t is always zero for i/o channel operations.  However, the CPU receives additional inputs that cannot be accessed via i/o channel operations.  These additional inputs are signals from the spacecraft, which the CPU's "hardware" interprets as requests to automatically alter its various counter registers.  We set t=1 to indicate a packet that represents such a counter-modification request signal.  In this case, ppppppp represents the counter number (which should be in the range 32-60 octal), and dddddddddddddddd is the type of "unprogrammed sequence" which should be applied to the counter.  (Admittedly, the allowable counter registers and unprogrammed-sequence operations that apply to them should be incorporated into yaAGC, but since I'm not sure yet what they all are, I provide this more-flexible approach instead.)  For a list of the available counter registers and the list of available "unprogrammed sequences", refer to the CPU Architecture section of the assembly-language manual.  Numerically, unprogrammed sequences are represented as follows:

Unprogrammed
Sequence
dddddddddddddddd
(octal)

Comment
PINC
000

PCDU (slow)
001
(20051017 and later.)  If the target counter is CDUX, CDUY, or CDUZ,  yaAGC buffers these commands and applies them to the counter at a rate of 400 counts per second.
MINC
002

MCDU (slow)
003
(20051017 and later.)  If the target counter is CDUX, CDUY, or CDUZ,  yaAGC buffers these commands and applies them to the counter at a rate of 400 counts per second.
DINC
004

SHINC
005

SHANC
006

INOTRD
007

INOTLD
010

FETCH
011

STORE
012

GOJ
013

TCSAJ
014

POUT
015

MOUT
016

ZOUT
017

n/a
020

PCDU (fast)
021
(20051017 and later.)  If the target counter is CDUX, CDUY, or CDUZ,  yaAGC buffers these commands and applies them to the counter at a rate of 6400 counts per second.
n/a
022

MCDU (fast)
023
(20051017 and later.)  If the target counter is CDUX, CDUY, or CDUZ,  yaAGC buffers these commands and applies them to the counter at a rate of 6400 counts per second.

(As an example, the CPU might receive a DINC message for a certain counter, which would cause it to update the counter, which might cause it to output a POUT message indicating that the counter was still positive, none of which would be under program control.)

The bit u is covered in the following section. For i/o-channel operations by the CPU, or for counter-pulse inputs, this bit is 0.

As many 4-byte packets as desired may be packed into a single send. Of course, this must be done judiciously: you have to ask yourself, for example, what the yaAGC is likely to do if receives within a single transmission both a packet indicating that a given input signal is turned on and another packet saying it has now been turned off.

The same protocol is used by the client as by the server, since there is no possibility of confusing data from the two.

Though it isn't really a significant point, since yaAGC is long past the point of being used in a high-reliability environment, the protocol has been designed to be fairly robust: it is always possible to distinguish the ordering of bytes within a packet (in case bytes are lost in transmission), so that corrupted packets can in some cases be discarded.

yaAGC Input-Channel Bitmasks

The bit u in the send/recv protocol doesn't relate to anything in the AGC, but rather addresses a potential flaw in the socket-based scheme for interconnecting the various simulations.

Consider the following possibility. Suppose that two or more hardware-simulation programs communicate with the yaAGC, but that one of the input channels is used by two different hardware-simulations. In other words, suppose that there are input channels for which some of the bit positions are controlled by one of the hardware simulations, while other of the bit positions are controlled by another of the hardware simulations.

As a concrete example, consider LM input channel 32 (octal). Bit 14 of that channel indicates that the PROCEED key is pressed (which would relate to the DSKY simulation), whereas bit positions 1-10 relate to engines (and not the DSKY simulation; let's say that they relate to an "engine simulation"). Now, we don't want messages from yaDSKY on channel 32 (octal) to affect these engine bits, nor do we want messages from the engine simulation to affect the PROCEED-key bit.

The workaround for this problem is to allow the various hardware simulations to optionally send yaAGC a bitmask for each channel, telling yaAGC which bit positions of the channel it intends to affect. Whenever yaAGC receives an input-channel message from a hardare simulation, it applies the bitmask to the data, and only looks at the data bits that correspond to that bitmask.

A hardware simulation informs yaAGC of a desired bitmask by transmitting a message with u=1. Such a message does not convey input-channel data; rather, it sets the bitmask for channel ppppppp to ddddddddddddddd. The bitmask is specific to the hardware simulation transmitting it only, and stays in effect forever---or at least until another message with u=1 is received for that channel. So typically, the hardware simulation would want to transmit the bitmasks just once, immediately upon connecting to yaAGC. However, they could theoretically be sent prior to every message.

By default, all bitmasks are 77777 (octal), meaning that every hardware simulation is capable of affecting every bit-position in every input channel it chooses to transmit. If the socket connection between a hardware simulation and yaAGC is broken, so that the hardware simulation program needs to reconnect, the bitmasks for that socket are all reset to the default, and therefore need to be resent by the hardware simulation upon reconnection.

In terms of the concrete example described above, yaDSKY would want to set a bitmask of 20000 (octal) for channel 32 (octal), to limit its effects to bit 14, while a LM engine simulation would want to set a bitmask of 01777 (octal)  for that channel, to limit its effects to bits 1-10.

Finally, note that packets transmitted by yaAGC always have u=0. It is expected that the individual hardware simulations are clever enough to figure out for themselves which bits of the output channels are applicable.

Fictitious I/O Channels

For the purpose of communicating various types of information to/from yaAGC which do not fall under the classification of true i/o channels or counter registers, I've invented various fictitious i/o channels that exist only within yaAGC and do not exist in the true AGC.  These fictitious i/o channels are listed in the table below.

I/O Channel Address (octal)
Input/Output
Description
0177
Output from CPU
Used for simulating the 3200 pulse-per-second signal emitted by the CPU for torquing the gyro during IMU fine alignment.  Described fully in the discussion of i/o channel 014 below.
0176
Output from CPU
Used for simulating the 3200 pulse-per-second signal emitted by the CPU in response to counts placed in the CDUZCMD counter register (052) during IMU coarse alignment.  Described fully in the discussion of i/o channel 014 below.
0175
Output from CPU
Same, but for CDUYCMD (051).
0174
Output from CPU
Same, but for CDUZCMD (050).
0173
Input to CPU
This is for simulating the digital uplink.  The data payload of input channel 0173 is deposited directly in the INLINK counter register (045), and at the same time an UPRUPT interrupt is triggered.  Valid data has one of the following bit-patterns:
00000 00000 00000
or
ccccc CCCCC ccccc
where CCCCC indicates the logical complement of ccccc.  (In other words, the 15-bit data contains 3 copies of the same 5-bit field, one of which is complemented.)  The all-zero pattern is used by the ground-station for clearing the INLINK register after detection of error.  In the other pattern, the ccccc field is supposed to be one of the values used by the DSKY for communicating keystrokes to the AGC.  (Refer to the discussion of input channel 015 below.)  That is to say, the ground-station sends data to the AGC in the form of DSKY keystrokes which have been redundantly encoded to aid error-detection.

Incidentally, for experimentation, the yaDSKY program has a setting (--test-uplink) which causes it to communicate keystrokes to yaAGC via the digital uplink rather than its normal channel 015.

Note:  No similar mechanism is needed for telemetry downlinks, because downlinks are already handled perfectly well by the normal CPU output-channel mechanism.  Output channel 013, bit 7, contains the "downlink telemetry word order bit", output channel 034 contains the first downlink telemetry data word, and output channel 035 contains the second downlink telemetry data word.  These three items together comprise the 31 bits for each downlinked word.  (The real AGC added parity bits and filler bits to form 40 bits of data, but those are pretty irrelevant to the simulated CPU.)  Transfer of the LM state-vector to the AEA (P47) is also handled by this mechanism.  I'm not sure yet how transfer of the state-vector between the CM AGC and the LM AGC (V66) is handled; I'm beginning to think it was downlinked to a ground station, translated, and then uplinked.
0172
Output from CPU
Used for driving the optics shaft angle.  If a count is loaded into the CPU's OPTXCMD counter register (054), and the appropriate drive bit is set (bit 11 of channel 014, see below), then the count in the OPTXCMD register is simply output as channel 0172, and the OPTXCMD register is zeroed.
0171
Output from CPU
Used for driving the optics trunnion angle.  If a count is loaded into the CPU's OPTYCMD counter register (053), and the appropriate drive bit is set (bit 12 of channel 014, see below), then the count in the OPTYCMD register is simply output as channel 0171, and the OPTYCMD register is zeroed.
0170
i/o
Holds a count in 1's-complement format, in the range -57 to +57, indicating the displacement of the rotational hand controller (RHC) in the roll axis.  yaAGC places this value directly into the RHCR counter register (044).  yaAGC also immediately re-emits this as an output channel, for the benefit of programs like yaAGS or LM_Simulator that need to know the activity of the RHC.
0167
i/o
Same, but for the yaw axis (RHCY counter register, 043).
0166
i/o
Same, but for the pitch axis (RHCP counter register, 042).
0165
Output from CPU
(Version 20050903 and after.)  A "heartbeat" signal output from time to time by the CPU.  Contains the current value of the TIME1 counter register.  TIME1 increments every 10 ms., but it should not be assumed that channel 0165 is output every 10 ms.  By default, it is output every 20 ticks of TIME1, and thus is output every 200 ms. of simulated time.  (The default can be overridden using the "--heartbeat" command-line parameter of yaAGC.)  Channel 0165  may be output more often, at the occurrence of critical events such as the thrusters firing or ceasing to fire, for which more exact timing is needed.  Conversely, it may be output less often if the CPU is being run faster than real time (see below).  But it is guaranteed to be output at least once every 16383 ticks of TIME1.  Note that TIME1 wraps around after 16384 ticks.  Thus if the same value is output twice in succession, it means that no time (or at least no timer tick) has elapsed between the two outputs.

Client software which has an awareness of time, such as LM_Simulator, yaUniverse, yaAGS, etc., should use channel 0165 as a timebase, rather than the system clock of the PC.  If so, several advantages will be realized:
  • The simulation can be sped up or slowed down simply by speeding up or slowing down yaAGC.  Client software will automatically adjust to yaAGC's speed.
  • The simulated peripherals will be automatically paused or resumed if the simulated CPU is halted or resumed through its "--debug" interface.
0164
Input to CPU
(Version 20050903 and after.)  Used to set the ratio of simulated time to real time.  The value is in hundredths, so a value of 1 means to run at 1/100 speed, a value of 10 means to run at 1/10 speed, a value of 100 means to run in real time, and so on.  Typical values would be 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, and 10000.  The largest possible value is 16383, which means to run at 163.83 speed.  At startup, the CPU defaults to running in real time (100), unless overridden by the "--speed" command-line parameter of yaAGC.

The special value of 0 is used to halt the simulated CPU, which will remain halted until a non-zero value is input.

If simulated peripherals use output channel 0165 rather than the PC clock as time base (see above), then these timing changes automatically propagate throughout the simulation, and are not confined to the CPU.

yaAGC I/O Channel Specifics

The real AGC CPU interfaced with peripheral hardware by means of instructions that access "i/o channels" rather than main memory.

In this section, I describe the exact assumptions used in Virtual AGC about the assignment of I/O channels to simulated hardware. Or to put it differently, I present information about how the real AGC assigned I/O channels, but I don't bother too much about channels for which I am not providing simulated hardware. In some cases, the channel interpretations differ between the LM and CM (or for all I know, from one Apollo mission to the next), so I try to document the differences, where I can, by adding document references underlined.  For example, LC would mean that both reference documents L and C supported the interpretation.

The following documents are referenced:

L
Lunar-Module, Luminary 131 (1C) program listing. Within that listing, i/o channels are summarize on pp. 59-65; the "PINBALL" program, which drives the DSKY, lists DSKY bitcodes on pp. 403-404.
C
(At some point, will be a command-module program listing, but I haven't actually worked with it yet.)

Table of I/O Channels

Output Channel 10 (octal)
Used for driving the DSKY's 7-segment displays. Each time a value is output in channel 10 (octal) , it controls a pair of 7-segment displays. The output code contains both an identifier of the 7-segment pair which is supposed to be controlled, and the data which is supposed to be displayed in that pair.

Output-code positions.

Hopefully, the accompanying figure will clarify which 7-segment displays (and sign bits) appear at various positions on the DSKY. The 15-bit code output in i/o channel 10 (octal) can be represented in bit-fields as AAAABCCCCCDDDDD, where AAAA indicates the digit-pair, B sets or resets a +/- sign, CCCCC is the value for the left-hand digit of the pair, and DDDDD is the value for the right-hand digit of the pair.  By the way, it is unclear to me how the +/- signs can be blanked, using the commands outlined below. It seems as though it would involve sending two output-channel commands, (say) with both 1+ and 1- bits zeroed. (That is the approach taken in yaDSKY: for each sign bit, the most recent 1+ and 1- flags are saved. If both are 0, then the +/- sign is blank; if 1+ is set and 1- is not, then the '+' sign is displayed; if just the 1- flag is set, or if both 1+ and 1- flags are set, the '-' sign is displayed.)

AAAA
B
CCCCC
Represents
DDDDD
Represents
1011 (binary) = 11 (decimal)

Digit M1
Digit M2
1010 (binary) = 10 (decimal)
Digit V1
Digit V2
1001 (binary) = 9 (decimal)
Digit N1
Digit N2
1000 (binary) = 8 (decimal)

Digit 11
0111 (binary) = 7 (decimal) 1+
Digit 12
Digit 13
0110 (binary) = 6 (decimal) 1-
Digit 14
Digit 15
0101 (binary) = 5 (decimal) 2+
Digit 21
Digit 22
0100 (binary) = 4 (decimal) 2-
Digit 23
Digit 24
0011 (binary) = 3 (decimal)
Digit 25
Digit 31
0010 (binary) = 2 (decimal) 3+
Digit 32
Digit 33
0001 (binary)  = 1 (decimal) 3-
Digit 34
Digit 35
1100 (binary) = 12 (decimal) This is an exception, departing from the BCCCCCDDDDD pattern.  Instead:
  • Bit 3 lights the  "VEL" indicator.
  • Bit 4 lights the "NO ATT" indicator.
  • Bit 5 lights the  "ALT" indicator.
  • Bit 6 lights the "GIMBAL LOCK" indicator.
  • Bit 8 lights the "TRACKER" indicator.
  • Bit 9 lights the "PROG" indicator.

Value for
CCCCC or DDDDD
Displays as
00000 (binary) = 0 (decimal)
Blank
10101 (binary) = 21 (decimal)
0
00011 (binary) = 3 (decimal)
1
11001 (binary) = 25 (decimal)
2
11011 (binary) = 27 (decimal)
3
01111 (binary) = 15 (decimal)
4
11110 (binary) = 30 (decimal)
5
11100 (binary) = 28 (decimal)
6
10011 (binary) = 19 (decimal)
7
11101 (binary) = 29 (decimal)
8
11111 (binary) = 31 (decimal)
9

For example, to display "+12345" in Register 1, the DSKY would receive the following words on output channel 10 (octal) :
 100000000000011, 011111100111011, 011000111111110 (all binary).

Output Channel 11 (octal)
Contains various flag bits used for driving individual indicator lamps, and for other purposes.  Bits 1-7 are latching, while bits 8-15 are presumably one-time strobes. The bits which are relevant to proposed Virtual AGC software are:

Output Bit
Usage
2
DSKY: Lights the "COMP ACTY" indicator.
3
DSKY:  Lights the "UPLINK ACTY" indicator.
4
DSKY: Lights the "TEMP" indicator.
5
DSKY: Lights the "KEY REL" indicator.  I believe that this indicator flashes at a 1.5 cycle-per-second rate when illuminated.
6
DSKY: Flashes the VERB/NOUN display areas.  For a long time, I believed that this meant to flash the NOUN and VERB labels, but now I believe it means to flash the digits in the NOUN and VERB areas.  From AC's "APOLLO GUIDANCE AND NAVIGATION SYSTEM LUNAR MODULE STUDENT STUDY GUIDE", I believe that the rate of flashing is 1.5 cycles per second.
7
DSKY: Lights the "OPR ERR" indicator.  I believe that this indicator flashes at a 1.5 cycle-per-second rate when illuminated.

Output Channel 12 (octal)
Output
Bit
CM
Usage
LM
Usage
1
Zero Optics CDU
Zero RR CDU
2
Enable Optics Error Counter
Enable RR Error Counter
3

Horizontal Velocity Low Scale
4
Coarse Align Enable
(Same as CM)
5
Zero IMU CDU's
(Same as CM)
6
Enable IMU Error Counters
(Same as CM)
7
TVC Enable
Display Inertial Data
8


9
Enable SIVB Takeover +Pitch Gimbal Trim
10
Zero Optics -Pitch Gimbal Trim
11
Disengage Optics DAC +Roll Gimbal Trim
12

-Roll Gimbal Trim
13
SIVB Inj Sequence Start LR Pos Command
14
SIVB Cutoff RR Enable Auto Track
15
ISS Turn-on Delay Completed



Output Channel 13 (octal)
Output Bit
Usage
10
DSKY:  Tests alarms and DSKY lights.  I'm not sure what this means, exactly, so right now I'm just ignoring it ....
11
DSKY:  Lights the "STANDBY" indicator.  (But I don't think it's correct.)

The other bits of this channel will not be described here, as they do not relate to any simulations currently planned for Virtual AGC.

Output Channel 14 (octal)
This channel contains the following bits:
Output
Bit
CM
Usage
LM
Usage
1
(Not used)

2
(Not used)
3
(Not used)
4
(Not used)
5
(Not used)
6
Gyro Enable
(Same as CM)
7
Gyro Selection b*
(Same as CM)
8
Gyro Selection a*
(Same as CM)
9
Gyro Sign Minus
(Same as CM)
10
Gyro Activity
(Same as CM)
11
Drive OCDU Shaft
(Same as CM)
12
Drive OCDU Trunnion
(Same as CM)
13
Drive IMU CDU Z
(Same as CM)
14
Drive IMU CDU Y
(Same as CM)
15
Drive IMU CDU X
(Same as CM)
*The "Gyro Selection a" and "Gyro Selection b" registers are paired together as follows:
  • If a=0 and b=0, then no gyro is being driven.
  • If a=0 and b=1, then the X gyro is being driven.
  • if a=1 and b=0, then the Y gyro is being driven.
  • if a=1 and b=1, then the Z gyro is being driven.

The gyro activity in IMU fine-alignment presents a special problem for the recommended socket interface between the CPU and peripherals --- and, I suppose, any other reasonable interface --- in that the method the CPU uses to drive the gyros is to output a 3200 pulse-per-second signal when the Gyro Activity output bit is active, with the assumption that the gyros will know how many of the gyro-driving pulses have been emitted.  This leaves the software emulating the gyro with the problem of determining the exact number of pulses, which it cannot really do without some additional assistance.  (Thanks to Stephan Hotto for pointing this out.)  I therefore provide a workaround for this problem, which should be useful regardless of the nature of the interface between the emulated CPU and gyro, and should be insensitive to halting the CPU with the debugger.  The method is to implement a fictitious output port which does not appear in the actual CPU, that has the following properties:
For IMU Coarse Alignment, the IMU CDU X,Y,Z bits (13,14,15) bits present similar difficulties.  When the IMU CDU drive bits are set, the number of output pulses specified by the CDUXCMD, CDUYCMD, and CDUZCMD registers (addresses 50,51,52) is supposed to be emitted.  These pulses are supposed to be emitted in bursts of 192 pulses each (at 3200 pulses per second, thus occupying 60 ms.), with gaps of 540 ms. between bursts.  It is rather inefficient in emulation terms to emit individual pulses at 3200 pps, so we implement this as follows:
Note that yaAGC does not attempt to condition this behavior on bits 4 & 6 of channel 12, which are normally supposed to be prerequisites for the IMU CDU drive sequence.  In other words, the pulses will be output regardless of the settings of bits 4 & 6 of channel 12.

Input Channel 15 (octal)
Used for inputting keystrokes from the DSKY. There are 19 keys, and a 5-bit keycode appears in bits 5-1 of this input channel.  A keystroke triggers interrupt #5, causing the software to examine the channel. However, the interrupt mechanism occurs entirely within yaAGC, and should not be of concern to a DSKY developer. I believe that the keycode appears when the key is newly pressed, and then disappears, but I can't prove that at the moment.

Key
Keycode
0
10000 (binary) = 16 (decimal) = 20 (octal)
1
00001 (binary) = 1 (decimal) = 1 (octal)
2
00010 (binary) = 2 (decimal) = 2 (octal)
3
00011 (binary) = 3 (decimal) = 3 (octal)
4
00100 (binary) = 4 (decimal) = 4 (octal)
5
00101 (binary) = 5 (decimal) = 5 (octal)
6
00110 (binary) = 6 (decimal) = 6 (octal)
7
00111 (binary) = 7 (decimal) = 7 (octal)
8
01000 (binary) = 8 (decimal) = 10 (octal)
9
01001 (binary) = 9 (decimal) = 11 (octal)
VERB
10001 (binary) = 17 (decimal) = 21 (octal)
RSET
10010 (binary) = 18 (decimal) = 22 (octal)
KEY REL
11001 (binary) = 25 (decimal) = 31 (octal)
+
11010 (binary) = 26 (decimal) = 32 (octal)
-
11011 (binary) = 27 (decimal) = 33 (octal)
ENTR
11100 (binary) = 28 (decimal) = 34 (octal)
CLR
11110 (binary) = 30 (decimal) = 36 (octal)
NOUN
11111 (binary) = 31 (decimal) = 37 (octal)
PRO or STBY
(See below.)

Input Channel 32 (octal)
Bit 14 set indicates that the PRO (STBY) key is currently pressed.  The logic is inverted, so that the bit becomes 0 when the key is pressed, and is 1 when the key is not pressed.

The other bits of this channel relate to the engines, and will not be described here since Virtual AGC presently includes no engine simulations.

Internals of the yaDSKY Program

Anyone interested in creating additional back-end hardware simulations for yaAGC, or in altering or replacing the yaDSKY program, will probably be interested in the architecture and other internal details of yaDSKYyaDSKY is actually an extremely simple program to understand and to modify. Though targeted for a UNIX-type operating environment (such as Linux), all design tradeoffs have been made in favor of simplicity and portability, as opposed to performance or aesthetics.

Interface to yaAGC

As mentioned earlier, the AGC interacted with hardware such as the DSKY through the AGC's "i/o channel" mechanism. The i/o-channel mechanism, in turn, either drove control signals through wires or else received signals from peripheral devices through wires.

For simulation purposes, the "wires" (and hence the AGC i/o channels) are replaced by the mechanism of "sockets".  yaAGC acts as a "server" to which client simulations like yaDSKY can connect. By default, yaAGC listens for connections on 5 ports, 19697-19701. One simulated-hardware client can connect on each port, so up to 5 hardware simulations can be attached to the simulated computer at any time. (The number 5 is arbitrary, and can be increased by recompilation of yaAGC.)

yaAGC does not distinguish between the ports, so any type of hardware simulation---if there ever is more than one---can connect on any port. Or, multiple simulations of the same kind can connect. For example, two or more DSKY simulations can be run at the same time from one AGC simulation.

Software Architecture

The yaDSKY program is event-driven. What this means, in essence, is that when a keyboard key is pressed in the simulation, the yaDSKY client transmits a message via socket to the yaAGC server; yaAGC interprets this message as data on an "input channel". Similarly, when yaAGC wishes to put data on an "output channel", it transmits a message via socket to yaDSKY (and other clients); yaDSKY interprets this message in terms of the desired conditions of its indicator lights, and drives those lights accordingly. These actions all take place in the source file callbacks.c.

For portability and reliability purposes, the design choice has been made additionally to have a function (called Pulse, in main.c) which is executed at regular intervals---nominally, every 50 milliseconds. This function handles establishing a connection from the server, or disconnecting from it. Also, it polls for new incoming socket data.

Some functions which are used in common with the yaAGC program---namely, some socket-manipulation functions and some functions for creating or parsing the data packets passed through the sockets---appear in the yaAGC source code rather than in the yaDSKY source code. In building yaAGC, both a stand-alone program and a linkable library (libyaAGC.a) are created. A hardware-simulation like yaDSKY can use these functions by linking to the library. The available library functions are described below.

GUI

As may be deduced from the description above, yaDSKY's graphical user interface is highly independent of any underlying computations or communications which are occurring. It hardly matters what method is used to create the GUI. Basically, any graphical toolkit having the following features can be used:
In fact, the gtk+ graphical toolkit (2.0 or higher) has been used, and the glade tool (2.0 or higher) has been used to create the GUI.

Indicator Legends and Configuration (ini) file

Indicator positions.

The indicator legends in the upper-left quadrant of the yaDSKY panel are not hard-coded into yaDSKY, but rather are provided as a set of graphics files. Therefore, they can be changed at runtime, to indicate the differences between an LM simulation vs. a CM simulation, or possibly the differences between different Apollo missions.

There are two graphics files for each indicator---one lit and one dark. Therefore, each set of legends consists of 28 graphics files.  These graphic files, along with the graphic file for the PRO key (i.e., the keypad key between CLR and KEY REL) and the relationships of the indicator lamps to CPU "output channels", is controlled by a configuration file loaded by yaDSKY at startup.  By default, the configuration file is LM.ini, but additional configuration files (CM.ini and CM0.ini at this writing) also exist, and may be selected on the yaDSKY command line.  Full explanation of creation/modification of configuration files appears in the comments within LM.ini.

The indicator-lamp graphics files are in the XPM format, and are 84×40 RGB. (I created the distribution files using The GIMP.) For the purpose of building yaDSKY, all graphic files need to be stored in the directory yaDSKY/src/pixmaps (assuming that yaDSKY source code is in a directory named yaDSKY.)  Furthermore, the graphics need to be installed in a particular directory to be accessible by yaDSKY at runtime; if yaDSKY is built as the distribution version is built, this directory is /usr/local/share/yadsky/pixmaps, but copying the graphic files into this directory is automatically done with "make install".

Virtual AGC Library API

If for some reason you don't care to use the provided yaAGC/yaDSKY/yaAGS/yaDEDA software but wish to use the underlying CPU simulation engine, or if you want to create add-ons to Virtual AGC without hacking, you can simplify your work by building a C program around the functions provided in the library libyaAGC.a.  Undoubtedly, the library can also be trivially modified to allow use by C++ programs; if anyone has an overwhelming urge for C++ compatibility, let me know.  To use the library functions, simply #include "agc_engine.h" and then add "-lyaAGC" to your gcc command-line.

Note that the debug interface (breakpoints, single-stepping, etc.) is provided by the yaAGC application program rather than by the library, so if you want a debug interface you'll have to implement your own.  The only exception to this is that backtrace functionality is built into the library.

Useful Datatypes and Constants

agc_t (yaAGC)

This datatype is a structure intended to hold all information about the current state of the emulated AGC CPU, such as the contents of memory, the contents of i/o registers, and so forth.  Pointers to this structure are passed explicitly to all functions in the library which need to know the CPU state.  Placing all of this information in a structure, rather than keeping it as global data, makes it much easier to write a monolithic program that simultaneously emulates multiple AGCs, such as a CM AGC and an LM AGC.  Eventually, I'll probably describe a lot of the fields in this structure; for right now, just take a look at agc_engine.h if you want to know more.

int8_t, int16_t, uint16_t (yaAGC /yaAGS/peripherals)

In most cases when an integer is needed, the software employs the native int or unsigned datatypes.  However, where it is important to know the exact integer precision, the int8_t, int16_t, or uint16_t datatypes are used.  In particular, emulated memory (including CPU central registers and counter registers) are of the int16_t datatype.  Important note:  In most cases, data stored in int16_t format for use in yaAGC will be in the 1's-complement format used by the AGC CPU rather than in the (probably) 2's-complement format used by your native CPU.  (This is not a problem with 2's-complement yaAGS internal data.)  Therefore, when operations are performed on int16_t data, you must take special care to insure that the operations are appropriate to AGC integers.  For example, if you had two integers i1 and i2 in AGC format, the C-language operation i1+i2 will often not produce the correct sum in AGC format; the appropriate operation would instead be AddSP16(i1,i2) (see below).  Another very important point to understand is the concept of "overflow".  Integers in AGC format are 1's complement integers occupying the least significant 15 bits of an int16_t value.  The 16th bit is usually an exact copy of the 15th bit.  However, if there is overflow from an operation, then the 15th and 16th bits are opposites, and it is the 16th bit which is considered to be the correct sign.  With 2's-complement arithmetic, we normally allow additive overflow to occur in such a way that incrementing the largest positive number rolls around to the largest negative number.  With AGC 1's-complement arithmetic, however, incrementing the largest positive number rolls around to +0 "with overflow".  Consider the following simple example:  The largest possible positive value of an AGC single-precision integer is 214-1=16383.  Therefore, the operation of 16383+1 will overflow.  In terms of bits, this operation results in 01 000000 000000.  The mismatch between the most significant bits is an indication of overflow.  The actual value is given by the 16th bit and the lowest 14 bits, or +0.  To "overflow correct" this value would be to eliminate the overflow mismatch by copying bit 16 into bit 15.  There is a value in many cases to retaining the overflow without correcting it, and many AGC instructions do not correct the overflow; however, an explanation of all the things you can do with non-overflow-corrected values is a bit beyond our scope here.

CPU-to-Peripheral Interconnections API

This is perhaps the most important part of the AGC API, because it is disatisfaction with (or lack of understanding of) Virtual AGC's CPU-to-peripheral interface which most commonly causes people to hack yaAGC rather than interfacing to it gracefully, and thus depriving themselves of easily using future improvements to the software.  As described above, the default CPU-to-peripheral interface involves using yaAGC as a server for socket connections, and the peripheral devices (such as yaDSKY) as clients.  This socket interface has many advantages, not least of which being that it is very portable and reasonably-elegantly emulates the wired connections of the original hardware.  However, if for some reason the socket interface is ineffective or distasteful for your purposes, you can completely replace it by some other type of interface (such as shared memory) simply by replacing 3 library functions.  In fact, you don't even need to replace them in the library:  Just create and link in your own versions of these functions, and the linker won't even both to try and load the default functions from the library; start with the file NullAPI.c and fill in the template functions I've p