|
|
|
Virtual
AGC and AGS
Developer
Info |
|
|
|
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:
- CM AGC (yaAGC acting as a
server): Listens to ports 19697-19706.
- Commander's DSKY (yaDSKY
acting as client): Port 19697.
- Navigation DSKY (yaDSKY
acting as client): Port 19698.
- LM AGC (yaAGC acting as
client): Port 19699.
- Mission control (yaTelemetry
acting as client): Port 19700.
- IMU: Port 19701.
- AOT: Port 19702.
- LM AGC (yaAGC acting as a
server): Listens to ports 19797-19806.
- DSKY (yaDSKY acting as
client): Port 19797.
- Mission control (yaTelemetry
acting as client): Port 19800.
- IMU: Port 19801.
- AOT: Port 19802.
- ACA: Port 19803.
- AGS (yaAGS acting as a
client): Port 19804.
- LM AGS (yaAGS acting as a
server): Listens to ports 19897-19906
- DEDA (yaDEDA acting as
client): Port 19897.
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:
- To output 4 bits of data, the software loads data into the DEDA
shift register (i/o address 2200) and then the DEDA Shift Out discrete
bit is set (software outputs anything to i/o address 2600). The
DEDA Shift Out bit is automatically reset without any action by the
software. The shift operation takes 80 microseconds to complete,
so the DEDA shift register and DEDA Shift Out/In bits shouldn't be
messed with for 80 microseconds.
- To input 4 bits of data, the software sets the DEDA Shift
In discrete bit (software outputs anything to i/o address 2500), waits
for the shift operation to complete (80 microseconds), and then reads
data from the DEDA shift
register (i/o address 2200). The DEDA Shift In bit is
automatically reset without any action by the software.
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:
- 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.
- 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.
- 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.
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:
- The address of the fictitious "Gyro Pulse" i/o channel is 0177
(octal). Under appropriate conditions (see below) this fictitious
i/o channel is output by the yaAGC
program automatically, transparently to the Colossus or Luminary program.
- The 15-bit data (ddd dddddd
dddddd) of the channel is subdivided into 2 parts: the
most-significant 4 bits are the same as bits 6-9 of channel 14, and the
least-significant 11 bits contain a count of the number of gyro pulses
since the last time channel 177 was output. (Bit 15 will be the
same as "Gyro Sign Minus", bit 14 will be "Gyro Selection a", bit 13
will be "Gyro Selection b", and bit 12 will be "Gyro Enable".)
- Channel 177 is output while Gyro Activity is 1 and the GYROCTR
counter register is non-zero, nominally every
1/4 second. (The count will nominally be 01440, out of a maximum
allowable 03777.) The GYROCTR register is decremented by the
number of pulsed emitted when this happens.
- If any of bits 6-10 in channel 14 change, channel 177 is output
immediately with either the remainder of the pulses from GYROCTR or
else a pro-rated number of pulses based on elapsed time.
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:
- We define new fictitious, i/o channels at addresses
0174, 0175, and 0176 (octal), for the X, Y, and Z axes
respectively. The yaAGC
program automatically writes to these output channels every 600 ms. as
long as any of the Drive IMU CDU bits in channel 14 are active while
the corresponding CDUxCMD registers are non-zero.
- The 15-bit data (ddd dddddd
dddddd) of the channel is the number of IMU CDU
drive pulses
since the last time the fictitious channel was output. The
most-significant bit indicates the direction of movement (0 is positive
and 1 is negative), while the least-significant 14 bits represent the
count. For example, 192 in the positive direction would be 000
000011 000000, while 192 in the negative direction would be 100 000011
000000.
- Usually, the pulse-count will be 192
(decimal). However, at the end of the drive sequence, there
will generally be a smaller count.
- The CDUxCMD registers 50,51,52 will automatically be counted down
by yaAGC. Also, the IMU
CDU drive bits will be automatically reset to 0 when the sequence
completes. I hope this
corresponds to real AGC's behavior, but I don't really know.
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 yaDSKY. yaDSKY 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:
- Pushbutton widgets with graphical legends.
- Pixel-map widgets which can be loaded from files at runtime.
- The ability to execute a function at regular intervals.
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
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