Introduction

For those who can tolerate such talk, this document serves as the
requirements for processing of machine language by the yaAGC program, as well as for the
assembly language accepted by the yaYUL
program, and that's why this document is versioned.

The original Apollo documentation from which the information presented
here was derived, in roughly descending order of importance, is:

Where referenced below, I'll denote references 1 and 2 as
"Savage&Drake" and "Blair-Smith", respectively. Where I make
statements that I believe to be true, but which I don't think are fully
supported by the original Apollo documentation, I will color the text green to indicate
uncertainty.

The Interpreter vs. the
CPU

If all of the software-provided functionality required for the moon
missions had been written straightforwardly in AGC assembly
language—i.e., in the native language of the AGC's CPU—more memory
would have been needed for program storage
than was actually physically provided within the AGC. It is
important to realize that the AGC had no capability of loading programs
into memory at runtime, except for extremely tiny code fragments (keyed
in manually by the astronauts or uploaded via telemetry uplink). All
of the software needed for the mission was encoded in the "core ropes",
and these had to be manufactured and hermetically sealed within the
computer unit. In other words, all of the software needed to fit within
the 38,912 15-bit words of core memory.

To solve this problem, the designers of the AGC chose to use part of
the precious core memory to implement a virtual
computer-within-the-computer—in much the same way as yaAGC is a virtual computer
within another computer. This virtual computer, the "interpreter",
was a subprogram (within the larger Colossus
or Luminary program) which
when activated, read its own instructions from memory and
executed them. Because each of the interpreter's instructions
represented many AGC assembly-language instructions, a
larger amount of functionality could be fitted into the same amount of
core memory, even accounting for the fact that software was needed to
implement the interpreter. The drawback was that implementing any
particular functionality in interpretive language required far more
execution time than implementing the same functionality directly in AGC
assembly language. But as long as the program ran "fast enough", that
didn't matter very much.

The interpreted code is in some ways much more elegant and useful than
the pure AGC assembly language. For example, the interpreter
allows for a linear address space (which the pure assembly language
does not), and has many "high-level" instructions for doing things like
operating on position vectors or state vectors and performing
trigonometric operations. (Thanks to Onno Hommes for pointing out
that I wasn't giving as much credit to the developers of the
interpreter language as they deserved.)

It is important also to understand that yaAGC has no special support for the
interpreter: yaAGC simply runs
AGC4 machine code and, as far as it is concerned, the interpreter is
just like any other subprogram within Luminary
or Colossus. It "just works"
with interpreter code.

yaYUL, on the other hand, does
require special support for AGC interpreter language.
Interpretive instructions are simply intermixed with assembly-language
instructions within a source-code file. Typically, you'll find a
block of assembly-language instructions, followed by a block of
interpretive instructions, followed by a block of assembly-language
instructions, and so on. Between an assembly-language block and
an interpretive block there will be a call (in assembly language) to
the interpreter subroutine. In other words, the interpreter
subroutine expects to find the code it is going to interpret following
the assembly language instruction which called it. Similarly, a
block of interpretive code ends with a specific interpreter instruction
meaning "stop interpreting and jump to the next location in memory".

Because of this intermixing, this manual describes both AGC assembly
language and AGC interpretive language.

General Formatting Information

The format of source code accepted by yaYUL
differs slightly from that accepted by YUL.
This is partly because without documentation for YUL, determining the exact format
required by it requires more effort than I care for; but also,
formatting restrictions based on strict column alignment—which make a
lot of sense with punch-cards—make little sense now that punch-cards
have gone to their final resting place. (But see the MOD 3C Programmer's Manual, which though
covering an earlier, incompatible version of the AGC, does give some
information on how YUL was
used.)

Here are the principles used in formatting yaYUL source files, along with my
stylistic preferences:

A source-code file is an ASCII (text) file, consisting of a
series of lines.

Blank lines are ignored.

The character '#' is used to begin comments. Everything on a
source line following the '#' symbol is discarded. (In the original YUL, the source code was on punch
cards, with one line of code per card. I believe that the entire card
was a comment if a non-blank character appeared in column 1. I'm not
certain how comments at the end of a line of source code were
recognized; they were either column-dependent, or else the parser
simply gave up after all valid fields had been parsed. In any case, yaYUL does not support any of the
original YUL's specific
commenting methods.)

If the character '$' is encountered in column 1 of a line, then
the line contains the name of a source file which is to be inserted at
that point. For example, if "$INTERRUPT_LEAD_INS.s" is encountered in
column 1, then yaYUL will
process all of the file INTERRUPT_LEAD_INS.s before proceeding to
process the remainder of the original source file. (The original YUL had no such source-code
directive, since it did not process "files".)

Programmer-defined names (such as program labels and constants
being defined) begin in column 1. They are 8 characters or less in
size, and may contain any character except '#', and may not begin with
the character '$'. (For example, "-1/(D)+A" would be a legal constant name or program
label.)

A typical arrangement of items on a line of assembly-code would
be this: optional program label, followed by an AGC opcode or
interpreter instruction, followed by
an operand for the opcode, followed by an optional comment. In some
cases, additional option fields may appear between the operand and the
comment. All fields are separated by whitespace, except that no space
needs to precede the '#' delimiter for a comment. Stylistically,
my preference is for the opcode or interpreter instruction to begin at
the second tab stop, the operand (if any) at the third tab stop,
options to the operand are separated by spaces, and the comment (if
any) to begin at the fifth tab stop, assuming that tab stops are 8
characters apart.

In some cases, a line may have no program label but may have a
field such as "-1", "+1", "+110", etc., preceding the opcode. These
notations appear to serve merely as comments, and are simply ignored by
yaYUL. These notations
cannot begin in column 1, or else they would be taken for program
labels. Stylistically, I prefer that these notations begin at the
first tab stop.

These principles are perhaps best understood by viewing actual
source-code listings, such as ALARM_AND_ABORT.s from the Luminary 131
source code.

Data Representation

Bit Numbering

AGC memory "words" are 15 bits in size—or rather 15 data bits, plus
an odd-parity bit used only by the hardware for error-detection
purposes, and not accessible to software. The original AGC
documentation referenced the individual bits within a word as

15, 14, 13, 3, 2, 1, P,

where bit 15 is numerically the most significant bit, and bit 1 is the
least significant bit. For yaYUL
and yaAGC,
the parity bit is not used and is always equal to 0. Thus, if you
were able to inspect the data being used internally by yaAGC or yaYUL,
you would often find that it is shifted one bit further to the left
than you would otherwise expect. However, all data in source
listings, in yaAGC's --debug
mode, etc., eliminate the parity bit (rather than setting it to 0), and
therefore symbolically appear exactly as one would expect.

Single-Precision (SP, 1's-complement) Format

Most arithmetic operations by the CPU use the 15-bit Single Precision
(SP) data-type, which is a variant of 1's-complement notation.

In SP binary format, bit 15 is the sign and bits 14-1 represent the
data. If bit 15 = 1, the magnitude is negative and is represented
as the one's complement of the positive magnitude (discussed below).
Bit 14 is the high order bit (highest value) and bit one is the low
order bit (lowest value). For arithmetic purposes the value in bits
14-1 is thought of as a fraction. That is, the binary point is between
the sign and bit 14. For instance, a one in bit 14 is equivalent to 1/2
. From a programmer's point of view, the programmer must keep track of
the "imaginary" point's position within the word in accordance with the
appropriate scaling.

Notice that from this definition, zero has two different
representations—as "+0" and as "-0".

Some examples:

+0 has the binary representation 000000000000000.

-0 has the binary representation 111111111111111.

1/2 has the binary representation 01000000000000.

-1/2 has the binary representation 101111111111111.

1/4 has the binary representation 001000000000000.

3/4 has the binary representation 011000000000000.

To represent an SP number within the assembly-language source code, the
constant—i.e., a constant representing a real number—has an optional +/-
sign, 0 or more decimal digits, an optional decimal point, and more
decimal digits.

In some circumstances, numerical constants may optionally be scaled by
factors that are powers of 10 or of 2. To scale by factors of 10, the
option field "En" would be added, where n is a decimal integer such as "-1"
or "9". This scaling field is separated from the decimal number by
whitespace.

Similarly, to scale by factors of 2, the option field "Bn" would be added.

These options may be intermixed on the same line of code. For example, "+656.0 E-4 B3" has the
numerical value 656×10-4×23
. The net result, however, must always be a value whose absolute
value is less than 1.0.

In rare cases, the original source code (for example, of Luminary) has a mutilated form of
this syntax, of which a typical example would be "+656.0E -4B3". While YUL may have been forgiving of this,
yaYUL is not; the spacing of
the options needs to be corrected in such a case.

Double-Precision (DP) and Triple-Precision (TP) Format

The fourteen magnitude bits of the SP format do not always allow us
sufficient precision. To overcome this, we may represent data in a
Double Precision (DP) quantity within
two adjacent words of memory. Since each single precision
word has 14 magnitude bits, the combined
quantity has 28 bits with which to represent the magnitude.
Bits 14-1 of word 1 represent the more-significant bits and bits 14-1
of the
second word represent the less-significant bits.

Bit 15 of
the first word contains the sign of the word, and usually of the value
itself (unless, of course, the first word is +0 or -0). Bit 15 of the
second word will normally be
the same as bit 15 of
the first word but may differ in certain cases. When the
signs of the two words agree, the words add together; when the signs
differ, the words subtract. Every DP value in which the signs of
the words differ can be converted to a representation in which the
signs agree, though the reverse is not true. Some examples may
clarify these points:

Consider a DP value whose more-significant word is +37777 octal
and whose second word is also +37777 octal. This happens to be
the maximum possible positive DP value, 1-2-28.

Consider next a DP value whose more-significant word is +37777
octal and whose less-significant word is -37777 octal (40000
octal). How would one convert this to a representation in which
both words have the same sign? Simply subtract 2-14
(00001 octal) from the first word and add 2-14 (40000 octal,
considered simply as an integet rather than an SP value) to the second
word. This gives a first word of +37776 and a second word of
+00001. I.e., the word-pairs 37777,40000 and 37776,00001
represent the same DP value.

You may wonder why a situation ever arises in which the sign bits of
the words of a DP value don't match, but I do not know the answer.

For even greater accuracy, a quantity may be contained within 3
adjacent words and is called a Triple Precision (TP) quantity. In
essence, we combine 14 additional low order bits so that we have 42
bits to represent
the magnitude. Again, the signs of the 3 words may not match, and
therefore the individual words may add or subtract to/from each
other. There are no CPU
instructions that work with TP quantities, so TP quantities are used
mostly by the interpreter.

Vectors

Another Interpreter convention (rather than a format recognized by the
CPU) is that the x,y,z
components of
a vector can be represented by three consecutive DP
values.

Unsigned-Integer (2's-Complement) Format

There are some cases, such as the CDU counters described in the next
section, where the AGC CPU uses 2's-complement values
rather than the 1's-complement SP and DP values described above.
2's-complement is used by most computers these days, and in particular,
is used by personal computers. Some examples should suffice to
show how an unsigned integer is
represented in 2's-complement:

0 has the binary representation (bits 15-1) 000000000000000.

1 has the binary representation 000000000000001.

214 (16384) has the binary
representation
10000000000000.

215-1 (32767) has the
binary representation
111111111111111.

Double-precision unsigned integers are formed simply by using
two consecutive single-precision unsigned integers, with the
more-significant 15
bits preceding the less-significant 15 bits in memory.

Integer constants within the assembly-language source code are written
either in decimal or in octal form. In some cases, the context
determines which format (octal or decimal) is used; for example, the OCT pseudo-op accepts an octal
constant. In other cases, the structure of the constant itself
determines whether it is octal or decimal:

An octal-formatted number may have an optional leading +/- sign,
and consists of a string of octal digits (0-7).

A decimal-formatted number may have an optional leading +/- sign,
followed by a string of decimal digits (0-9), followed by the character
'D'.

Notice that there is some overlap betweed this definition and that of
Single-Precision (SP) constants above. For example, "12345" could be
either an
octal integer constant or a real constant (but not a decimal integer
constant). The difference is determined from context.

CPU Architecture
(Registers)

All CPU registers are memory mapped (see the next section). The
registers at addresses 00-23 (octal) are central to CPU operations,
from the point of view of the instruction set.

The registers at addresses 24-61 are generically referred to as
"counters". While the counter registers can be modified under
program control, they are typically only set up by the program and are
then subsequently automatically incremented or decremented by events
such as electrical pulses. The TIME1-TIME6 registers are even
more specialized, in that the "pulses" that increment them are actually
provided by an oscillator, so that these counters act as timers.
Many of the counters can be used to trigger interrupts upon overflow,
so that the CPU can use them to detect various hardware conditions or
events without having to continuously poll the hardware.

A reader familiar with the operation of counter/timer registers in
modern CPU types may suppose that incrementing or decrementing the
counter/timer registers is entirely a hardware operation having no
effect on program flow other than that caused directly by reading the
counter/timer registers or by the interrupts generated by the
counter/timer registers. This is not so. Incrementing or
decrementing the counter/timer registers is (and was) actually
implemented
by the use of "unprogrammed sequences",
which are similar to CPU
instructions, but which cannot be directly used by the
programmer. The unprogrammed sequences have names like PINC,
MINC, PCDU, and so on. When an
electrical pulse occurs that is supposed to affect a timer, the CPU
selects an appropriate
unprogrammed sequence (such as PINC),
waits until a safe opportunity
appears to execute the unprogrammed sequence, and then executes
the unprogrammed sequence. For example, an unprogrammed sequence
cannot be executed until a regular instruction or an unprogrammed
sequence already in progress has completed. This is significant
because it has two
side-effects that may be observable:

Counter/timer registers are updated shortly after the hardware
pulses associated with them occur ... but not necessarily immediately after the hardware
pulse occurs. The exact length of time before updating the
counter/timer is unpredictable and, in theory, could be as little as 1
machine cycles up to a large number (like 20) of machine cycles.
A machine cycle is 11.7 microseconds, and thus it is possible for the
time-jitter in updating a counter to be near 0.2 ms. Since the
fastest counters are updated about every 0.3 ms., this jitter can be
very significant. On the other hand, the average delay is much
shorter and most counters are updated much less rarely, and stackup of
the delays is a very rare event, so this effect can usually be ignored.

Incrementing a counter/timer takes
CPU time. Each of the unprogrammed-sequence instructions
which update counters actually take exactly one CPU cycle to
execute. Therefore, the
amount of time used by the CPU exceeds the time needed to execute the
assembly-language instructions. Because there are so many
counter/timer registers, the CPU time required by the unprogrammed
sequences is not necessarily negligible.

It should also be noted that there are two additional timer-counter
registers, HISCALAR and LOSCALAR, which appear as i/o channels rather than as memory-mapped CPU
registers, and therefore are not listed in the following table.

Address
(octal)

Name

Description

00

A

The "accumulator". Almost
every AGC instruction uses or modifies the accumulator in some
way. The accumulator differs from all other memory or i/o
locations addressed by the CPU, in that it is a 16-bit register rather than a
15-bit register. In most cases this is transparent to the
programmer, because the 16th bit is not directly observable or
modifiable, and because data within the accumulator is automatically
adjusted to 15 bits before most uses of it. The 16th bit is
present in order to allow detection of arithmetical overflow or
underflow.

Internally, when there is no overflow, the 16th bit of the accumulator
is a duplicate of the 15th (sign) bit. When a value is loaded
into the accumulator from memory, the value is sign-extended from the
15th bit to the 16th bit. In other words, for positive values the
15th and 16th bits are both 0, while for negative values they are both
1. When overflow occurs, however, the 15th and 16th bits
differ: After an operation that causes positive overflow, the
16th bit is 0 and the 15th bit is 1. After an operation that
causes negative overflow, the 16th bit is 1 and the 15th bit is 0.

Various CPU instructions can detect overflow and alter their actions
somewhat upon finding it. The TS (transfer to storage)
instruction is notable in this regard, because it can actually be used
to provide a branch upon detection of overflow.

In most cases, when data is transferred out of the accumulator to a
true 15-bit register, it is overflow-corrected.
Overflow-correction implies:

The 16th bit of the accumulator is assumed to be the
correct sign bit, and is copied into the 15th bit of the destination
location; and

The positive data and negative data are (separately)
converted to a range of 0 to 214-1. For example, if we
were incrementing +16383, we would find that a positive overflow had
occurred and that we had wrapped around to +0. Similarly, if we
were decrementing -16383, then a negative overflow (an underflow) would
occur, and the data would wrap around to -0. (On the other hand,
incrementing -0 would take us to +1 and decrementing +0 would take us
to -1, without underflow or overflow.)

The wraparound of under/overflowed data is very different from that
which programmers of most modern computers using 2's-complement
arithmetic would expect, since they would expect that incrementing the
maximum positive integer would give the "biggest" negative integer.

01

L

Like the accumulator, this is a 16-bit register rather than a
15-bit register. This means, for example, that like the
accumulator you can generate overflow conditions with instructions like
ADS L; also, the
overflow can be transferred between the accumulator and L with
instructions like XCH L
or LXCH A.

This is the "lower product
register". This is a general-purpose register, but many
instructions pair it with the accumulator in cases where double
precision (DP) operations are performed. In these cases, the A
register holds the more-significant word, and the L register holds the
less-significant word.

Like the accumulator, this is a 16-bit register rather than a
15-bit register. This means, for example, that like the
accumulator you can generate overflow conditions with instructions like
ADS Q; also, the
overflow can be transferred between the accumulator and Q with
instructions like XCH Q
or QXCH A.

This register is intended to
store return addresses of called procedures. Note that the AGC
CPU has no hardware stack, and provides only a single memory location
(the Q register) for storing return addresses. Therefore, it is
not possible to have nested procedure calls unless the contents of the
Q register are stored prior to calling a procedure and then restored
after the procedure returns. The CPU's instruction TC is
used to call a procedure, and the instruction automatically updates the
Q register; similarly, the RETURN instruction returns from a called
procedure by placing the contents of the Q register into the
program-counter register.

The "erasable bank
register". This register contains a 3-bit field that determines
which of the 8 banks of erasable memory (see "Memory Map" below) is
mapped into the address range 1400-1777 (octal). The 15 bits of
the EB register are arranged as follows:

000 0EE E00 000 000

where EEE are the bank-selector bits.

Note that the EEE field of the EB register is duplicated into the BB
register (see below). Changes to the EB register are immediately
automatically mirrored in the BB register, and vice-versa.

04

FB

The "fixed bank register".
This register contains a 5-bit field that determines which of the 36
banks of fixed memory (see "Memory Map" below) is mapped into the
address range 2000-3777 (octal). 5 bits are not, of course,
adequate for selecting among 36 banks, so these 5 bits are supplemented
by a 6th bit (the "super bank" bit) from i/o channel 7. In most
cases, the superbank bit is 0, and so the 5-bit field in the FB
register simply selects from among banks 0-37 (octal). When the
superbank bit is 1, on the other hand, the 5 bits of the FB register
actually select from among banks 0, 1, ..., 27, 40, 41, ..., 47; i.e.,
bank-selection is the same as when the superbank bit is 0, except that
banks 40-47 are used instead of 30-37. Banks 44-47
don't actually exist within the AGC, and so it is mere supposition on
my part that they would be selected.

The 15 bits of the FB register are arranged as follows:

FFF FF0 000 000 000

where FFFFF are the bank-selector bits.

Note that the FFFFF field of the FB register is duplicated into the BB
register (see below). Changes to the FB register are immediately
automatically mirrored in the BB register, and vice-versa.

The program-counter
register. This 12-bit register always indicates the next
instruction to be executed. It is always updated prior to executing the instruction,
so that an instruction which saved the value of the Z register would
actually be the address of the next instruction in memory.

Obviously, a 12-bit register cannot address all of memory. Full
addresses are formed by combining the 12-bits of the Z register with
the 3 bits of the EB register, the 5 bits of the FB register, and the
superbank bit of i/o channel 7. (Refer to "Memory Map" below, and
to the descriptions of the EB and FB registers above.) 12 bits
can represent values from 0-7777 (octal), and these are interpreted as
follows:

0000-1377 (octal). These addresses are used as-is,
and refer to an area of memory known as "unswitched erasable" memory.

1400-1777 (octal). These addresses must be combined
with the EEE field of the EB register, and refer to one of the memory
banks in "switched erasable" memory.

2000-3777 (octal). These addresses must be combined
with the FFFFF field of the FB register and (in some cases) to the
superbank bit of i/o channel 7, and refer to one of the memory banks in
"common fixed" memory.

4000-7777 (octal). These addresses are used as-is,
and refer to an area of memory known as "fixed fixed" memory.

06

BB

The "both banks register".
This register contains a 3-bit field duplicating the EEE field of the
EB register, and a 5-bit field duplicating the FFFFF field of the FB
register. Changes in the EB and FB registers are immediately
automatically mirrored in the BB register, and vice-versa. The
bits are arranged within the register as follows:

This location is not associated
with core memory, but is hardwired to 0. In other words, it
always contains
the value 00000. It is very useful as a source of zeroes, because
most AGC instructions have no "immediate" addressing mode.
(In other words, you can't use a specific numerical value as an
operand, but can only use the address of a memory location as an
operand.)

10

ARUPT

This register is provided as a
convenient location for storing the value of the A register during an
interrupt service routine. However, vectoring to the interrupt
does not automatically load this register, nor does returning from the
interrupt-service routine restore the A register from ARUPT.
These actions must be done under program control by the interrupt
service routine. Interrupts are automatically disabled while
overflow is present in the accumulator, so transfer of data back and
forth between A (16 bits) and ARUPT (15 bits) does not result in loss
of overflow indications.

11

LRUPT

This register is provided as a
convenient location for storing the
value of the L register during an interrupt service routine.
However,
vectoring to the interrupt does not automatically load this register,
nor does returning from the interrupt-service routine restore the L
register from LRUPT. These actions must be done under program
control
by the interrupt service routine.

12

QRUPT

This register is provided as a
convenient location for storing the
value of the Q register during an interrupt service routine.
However,
vectoring to the interrupt does not automatically load this register,
nor does returning from the interrupt-service routine restore the Q
register from QRUPT. These actions must be done under program
control
by the interrupt service routine.

13-14

(spares)

15

ZRUPT

This register stores the return
address of an interrupt service routine. When the CPU vectors to
an interrupt service routine, it automatically transfers the value of
the Z register (the program counter) into the ZRUPT register.
When the interrupt-service routine returns, using the RESUME
instruction, the value of ZRUPT is automatically transferred back into
the Z register.

16

BBRUPT

This register is provided as a
convenient location for storing the
value of the BB register during an interrupt service routine.
However,
vectoring to the interrupt does not automatically load this register,
nor does returning from the interrupt-service routine restore the BB
register from BBRUPT. These actions must be done under program
control
by the interrupt service routine.

17

BRUPT

(Note:
As of 20050820, I no longer perform the instruction substitution
described below. The internal mechanics of yaAGC are sufficiently
different from the true AGC CPU that it should not been needed. I
may restore it later.)

This register stores the value
stored at the return address of an interrupt service
routine. In other words, it is the instruction (not the address of the instruction) which
will be executed when the interrupt-service routine returns. When
the CPU vectors to an interrupt service routine, it
automatically loads this register. When the interrupt-service
routine returns, using the RESUME instruction, the value found in the
BRUPT register will be used as the next instruction.

It may seem from this description that an interrupt service routine can
return to a position in memory from which the interrupt vector
occurred, but can arrange to execute an entirely different instruction
than what is found at that address. I believe that this statement
is true, and obviously such a feature would need to be used with great
care. (However, I don't believe that the BRUPT register was
really provided for this purpose; I believe that the BRUPT register
exists for the purpose of holding instruction which have been altered
by a preceding INDEX instruction, as described below under the
discussion of the instruction set. The true AGC allowed
interrupts to occur between an INDEX instruction and the instruction
affected by the INDEX instruction, and so this provision was
necessary. However, yaAGC
does not allow such interrupts, and conflicts between INDEX and the
interrupt system do not arise in yaAGC.)

20

CYR

The "cycle right register",
which is one of the four so-called "editing" registers. When a
value
is written to this register, the value is automatically cycled
right (with the least significant bit, bit 1, wrapping into bit
15).
For example, if the software attempted to write the following bits to
the CYR register,

abc def ghi jkl mno

then the value actually stored in the CYR register would be

oab cde fgh ijk lmn

21

SR

The "shift right register",
which is one of the four so-called "editing"
registers. When a value is written to this register, the
value is
automatically shifted right (with the most significant bit, bit 15,
being duplicated into bit 14,and the least significant bit, bit 1,
being discarded). For example, if the software attempted to
write the following bits into the SR
register,

abc def ghi jkl mno

then the value actually stored in the SR register would be:

aab cde fgh ijk lmn

This operation corresponds arithmetically to division of a
single-precision (SP) value by 2, as it automatically sign-extends the
result.

22

CYL

The "cycle left register", which
is one of the four so-called "editing"
registers. When a value is read back from this register, the
value is
automatically cycled left (with the most significant bit, bit 15,
wrapping into bit 1). For example, if before readback the
CYL
register
contained the bits

abc def ghi jkl mno

then the value actually stored in the CYL register would be:

bcd efg hij klm noa

23

EDOP

The "edit polish opcode
register", which is one of the four so-called "editing"
registers. This register is used mainly by the interpreter for
decoding interpreted instructions (which are packed two to a word), and
has little value for other purposes. When a value is written to
this register, it is automatically shifted right 7 positions, and the
upper 8 bits are zeroed. Actually, the contents of the bits 8-15
in the
edited result are undefined, as far as I can tell, and making them zero
is my own requirement.

For example, if the software attempts to write the following bits into
the EDOP register,

abc def ghi jkl mno

then the value actually stored in the EDOP register would be:

000 000
00b cde fgh

24

TIME2

TIME1 is
a 15-bit 1's-complement counter which is incremented every 10
ms. TIME1 by itself overflows every 214*10 ms., or 163.84
seconds. Upon overflow of TIME1, the 14-bit counter TIME2 is
automatically incremented. Thus, the two counters together form a
28-bit value which can keep track of time for up to 228*10
ms., or just over 31 days. The TIME1/TIME2 register pair acts as
a master clock for the AGC. yaAGC internally clocks this counter.

25

TIME1

26

TIME3

TIME3 is a 15-bit 1's-complement
counter which is incremented every 10 ms. Upon
overflow, it requests an interrupt (T3RUPT), which results in vectoring
to the interrupt service routine at address 4014 (octal). This
interrupt is used by the "wait-list" for scheduling
multi-tasking.

Incrementing TIME3 is 5 ms. out of phase with incrementing TIME4, so if
their respecitve interrupt service routines don't take longer than 4
ms. to execute, the interrupts for the two will not conflict.
Software typically uses this counter by having the interrupt service
routine reload the counter with a value chosen to insure that the
interrupts occur at a desired rate. For example, to make an
interrupt occur once per second, at every interrupt the counter would
be reloaded with 214-100=16284 (decimal). Because 10
ms. is usually much longer than the amount of time needed to vector to
the interrupt service routine, it is unnecessary in such calculations
to account for the time taken by the interrupt vectoring.

yaAGC internally clocks
this counter.

27

TIME4

TIME4 is a 15-bit 1's-complement
counter which is incremented
every 10 ms. Upon overflow, it requests an interrupt (T4RUPT),
which results in vectoring to the interrupt service routine at address
4020 (octal). The T4RUPT program services the DSKY's
display. (It does not service DSKY keypad.)

Incrementing TIME3 is 5 ms. out of phase with incrementing TIME4, so if
their respecitve interrupt service routines don't take longer than 4
ms. to execute, the interrupts for the two will not conflict.
Software
typically uses this counter by having the interrupt service routine
reload the counter with a value chosen to insure that the interrupts
occur at a desired rate. For example, to make an interrupt occur
once
per second, at every interrupt the counter would be reloaded with 214-100=16284
(decimal). Because 10 ms. is usually much longer than the amount
of
time needed to vector to the interrupt service routine, it is
unnecessary in such calculations to account for the time taken by the
interrupt vectoring.

yaAGC internally clocks
this counter.

30

TIME5

TIME5 is a 15-bit 1's-complement
counter which is incremented
every 10 ms. Upon overflow, it requests an interrupt (T5RUPT),
which results in vectoring to the interrupt service routine at address
4010 (octal). This is used by the digital autopilot (DAP)

Software
typically uses this counter by having the interrupt service routine
reload the counter with a value chosen to insure that the interrupts
occur at a desired rate. For example, to make an interrupt occur
once
per second, at every interrupt the counter would be reloaded with 214-100=16284
(decimal). Because 10 ms. is usually much longer than the amount
of
time needed to vector to the interrupt service routine, it is
unnecessary in such calculations to account for the time taken by the
interrupt vectoring.

yaAGC internally clocks
this counter.

31

TIME6

TIME6 is a 15-bit 1's-complement
counter which is updated
every 1/1600 second by means of a DINCunprogrammed sequence.
There is a
CPU flag which can mask counting of TIME6 on or off. By
writing 1 to
bit 15 of i/o channel 13 (octal), TIME6 counting is enabled;
conversely, by
writing 0 to that bit, the TIME6 counting is disabled. Upon
reaching ±0, the counter requests an interrupt
(T6RUPT),
which results in vectoring to the interrupt service routine at address
4004 (octal), and then turns off the T6RUPT counter-enable bit.

The T6RUPT is used by the digital autopilot (DAP) of the LM to
control the jets of the reaction control system (RCS).

Thus a typical use might be:

Load TIME6 with a desired time interval for firing a jet,
in 1600ths of a second. The maximum allowable count is 37777
octal, or just a little more than 10 seconds.

Enable the counter with bit 15 of i/o channel 13 octal.

After the desired time has passed, a T6RUPT
occurs, and bit 15 of i/o channel 13 (octal) is reset.

yaAGC internally clocks this counter.

32

CDUX

These
counters are used to monitor the orientation of the spacecraft.
Three Control Data Units (CDUs) are dedicated to measuring the 3 gimbal
angles in the Inertial Measurement Unit (IMU). CDUX refers to the
"inner" gimbal angle, CDUY refers to the "middle" gimbal angle, and
CDUZ refers to the "outer" gimbal angle.

The CDUs are like analog-to-digital converters, and convert the analog
angles to digital data comprehended by the CPU. The IMU provides
a measurement platform which is stable with respect to the fixed stars,
and maintains its orientation with respect to the stars even while the
spacecraft itself rotates. The platform is physically mounted on
gimbals, and by measuring the gimbal angles, the orientation of the
spacecraft with respect to the IMU's stable platform can be deduced by
calculation. (Because only 3 gimbals were used, it was possible
for the spacecraft to rotate into positions beyond which the stable
platform could no longer maintain its stability with respect to the
fixed stars, and would thus begin to rotate with the spacecraft.
This condition, "gimbal lock", resulted in an inability to continue
monitoring spacecraft orientation and acceleration, and required
re-entry of all orientation, position, and velocity data into the
computer system. A 4th gimbal would have prevented gimbal lock,
but was not provided for some reason.)

These counters contain 15-bit 2's-complement unsigned values, and
therefore can take values ranging from 0 to 32767 (decimal). The
counters are processed with the PCDU
unprogrammed sequence (see below)
in order to increase the angles by one unit, and are processed with the
MCDU unprogrammed
sequence to decrease the angles by one unit. The units of
measurement are quoted as 40" of arc in Savage&Drake, but actually
they were 39.55078125" of arc,
making the full range come out to exactly 360 degrees.

33

CDUY

34

CDUZ

35

OPTY

These
counters are used to monitor the orientation of the optics subsystem
(i.e., the line of sight) or LM rendezvous radar with respect to the
spacecraft. Two Control Data Units (CDUs) are dedicated to
measuring these relative angles. OPTY refers to the trunnion
angle, whereas OPTX refers to the shaft angle. The CDUs are like
analog-to-digital converters, and convert the analog angles to digital
data comprehended by the CPU.

These counters contain 15-bit 2's-complement unsigned values, and
therefore can take values ranging from 0 to 32767 (decimal). The
counters are processed with the PCDU
unprogrammed sequence (see
below) in order to increase the angles by one unit, and are
processed with the MCDU
unprogrammed sequence to decrease the angles by one unit.
The units of
measurement are quoted in Savage&Drake as 10" of arc for the
optical trunnion angle, or 40" of arc for the radar trunnion angle or
optical or radar shaft angles; but they were actually 9.887695312"
and 39.55078125" of arc, respectively, making the full range come
out to exactly 90 or 360 degrees.

36

OPTX

37

PIPAX

"PIPA"
stands for "Pulsed Integrating Pendulous Accelerometer". There
are 3 PIPAs mounted on the stable platform of the Inertial Management
Unit (IMU). Since the PIPAs are "integrating", they measure
changes in velocity (i.e., "delta-V") rather than acceleration, and the
counters PIPAX, PIPAY, PIPAZ
thus monitor the velocity of the spacecraft (as long as gimbal lock has
not occurred). Savage&Drake quote the units as 5.85 cm./sec
or 1 cm./sec., but do not state the conditions under which the two
different units are used.

I
assume these counters are incremented or decremented with PINC or MINCunprogrammed
sequences, but I
haven't found any documentation for this conjecture yet.

40

PIPAY

41

PIPAZ

42

Q-RHCCTR
(RHCP)
"Pitch"

LM
only. Each of these registers holds a count in 1's-complement
format, indicating the
displacement of the rotational hand controller (RHC) in the pitch,
yaw, or roll axes.
The way this is supposed to work is as follows: There is a
deadband
near the detent, where the count is supposed to be zero. When
outside
of the deadband, the counter is supposed to continually update to
correspond to the angular displacement. The count begins to be
non-zero
after the angle has reached about 2°, is calibrated to a count of
42 at
10° (which is the nominal full-scale position), and increases until
reaching a mechanical stop at 13°. The counts are supposed to
update only if the RHC counts are enabled (bit 8 of output channel 013
set) and when the count is requested (bit 9 of output channel 013
set). In other words, the flight software must enable the
counters and then request new data whenever it wants new data.
Furthermore, the fact that the RHC is out of detent is reported to the
CPU by clearing bit 15 of channel 031 to zero.

In practice, of course, people will be using 3D joysticks intended for
games, rather than the actual LM RHC, so there's no way the yaACA program that manages all
this can enforce these angles. So the way it actually works is
this: yaACA
assumes that the usable range in each axis, as reported by the joystick
driver, is -127 to +127. (Any values outside this range are
simply
forced to be -127 or +127.) A raw value of 13 or less appears in
the
counter register as 0, a raw value of 97 appears in the counter
register as 42, and all other values are scaled linearly from these
reference points. The formula is Counter = (Raw - 13)/2.
The maximum
possible count is thus 57. For negative deflections, of course,
the
same formula applies but is simply negative. This formula is
based
partially on the characteristics of the LM RHC, but is also partially
based on being able to translate from raw joystick values to RHCCTR
registers relatively elegantly.

It is, of course, possible to use MINC and PINC commands to alter the
value of these register, but in the interest of reliablity, yaACA reports the count to yaAGC via fictitious input channels,
0170 (roll) or 0167 (yaw) or 0166
(pitch). The value in the input channel is
a 1's-complement value in the range -57 to +57, and is placed directly
in the counter by yaAGC.

43

P-RHCCTR
(RHCY)
"Yaw"

44

R-RHCCTR
(RHCR)
"Roll"

45

INLINK

This register is used to receive
digital uplink data from a ground station. After the incoming
data word is deposited in the register, the UPRUPT interrupt-request is
set. Correct data is in one of two forms: the value 0
(which the ground station may uplink for error-recovery purposes) or
the triply-redundant bit pattern cccccCCCCCccccc,
where CCCCC is meant to
be the logical complement of ccccc
, which is always a
DSKY-type keycode. Other patterns will be interpreted by the
flight software as corrupted data.

46

RNRAD

47

GYROCTR
(GYROCMD)

These registers are used during
IMU fine alignment to torque the gyro to the the precise alignment
expected by the AGS. (The tolerance of fine alignment is
approximately ±80" of arc.) This register is written
by the flight software with counts (in AGC
1's-complement format) that represent the desired drive on the
currently selected axis. Only one axis can be selected at any
given time—namely, +X, -X, +Y, -Y, +Z, or -Z—using bits 7-9 of
output channel 014. Each count represents ±0.617981" of
arc.
Actual torquing of the gyro does not begin until bit 10 of output
channel 014 is set. (For completeness, note also that bit 6 of
channel 014 is supposed to be set at least 20 ms. prior to any of the
other stuff just mentioned.)

If the torque is supposed to be 1-16383 counts, it should be achieved
in a single burst. However, if it is greater than that, it should
be achieved by bursts of 8192 counts each, with bursts separated by 30
ms. If you examine the Luminary131
software, you'll see that it does exactly this.

Upon detecting a non-zero value in the
GYROCTR register while the gyro activity bit (10)
in
channel 014 is set, the true
AGC would emit a stream of electronic pulses at a rate of 3200 pulses
per second, with a pulse-count equal to the register value. yaAGC behaves simularly, except that
it emits fictitious output
channels 0174-0176, each one of
which can contain multiple pulses, scheduled to roughly correspond to
the 3200 pps. timing. As soon as yaAGC has read the
GYROCTR register, it resets it to zero. I have no idea if the
actual AGC did this or not.

50

CDUXCMD

These
registers are used during IMU coarse alignment to drive the IMU stable
platform to approximately the orientation expected by the AGC.
(The tolerance of coarse alignment is approximately
±1.5°.) These registers are written by the flight
software with counts (in AGC
1's-complement format) that represent the desired drive in each
axis. Each count represents ±0.04375°. (192
counts represent ±8.4 degrees.) The drive sequence does
not actually commence until the corresponding drive-enable bit is set
in output channel 14 (octal). Bit 15 (the most significant bit)
of channel 14 must be set to drive in the X axis, bit 14 in the Y axis,
and bit 13 in the Z axis. yaAGC
does not perform this operation instantly, but instead simulates the
true AGC timing (which emits a burst of 192 count-pulses every 600 ms.
when active), using the fictitious
output channel 0177. Notice that all three axes can be
slewed
simultaneously if multiple drive bits are set in channel 014.

yaAGC zeroes the CDUxCMD registers as soon
as it has read them while the corresponding drive-bit in channel 014 is
set. I have no idea
if the actual AGC behaved this way or not.

51

CDUYCMD

52

CDUZCMD

53

OPTYCMD

54

OPTXCMD

55

THRUST

LM only.

56

LEMONM

LM only.

57

OUTLINK

One might suppose that since the INLINK
register is used for digital uplinks, then the OUTLINK register is used
for digital downlinks. Actually, output channels 013, 034, and
034 are used for digital downlinks. I have no idea at all what
the OUTLINK register is used for, unless it is somehow used in the
downlink process, but not actually accessed by the flight software.

60

ALTM

LM only.

Memory Map

The memory map of the AGC is tricky (to say the least!), and it is
necessarily to thoroughly understand the memory map before code can be
reasonably written or even understood. Mirko Mattioli (thanks
Mirko!) has sent us a diagram of
the structure of the memory map, and it may prove helpful to have
his diagram at hand whilst reading the following description.

Memory may be categorized in several ways. Firstly, it may be
read-only ("fixed", in AGC terminology) or it may be read-write
("erasable"). Both fixed memory and erasable memory were
implemented using magnetic-core technology, but (obviously) using the
cores in very different ways. The AGC also had a memory-like
space called "i/o channels", which could not be used for general
purposes, but which was used to convey commands or data between the CPU
and peripheral devices like the DSKY.

Secondly, the memory (but not the i/o channels) can be categorized as
directly addressable via an address ("unswitched") or as addressable
only via an address plus a bank number ("switched"). This
situation resulted from the fact that 10-bit addresses for erasable
memory and 12-bit addresses for fixed memory were built into the AGC
instruction set, but that the actual amount of memory required
eventually increased far beyond these puny limitations, until
eventually 16-bit addressability was required. With each
successive increase in the amount of supportable memory, the methods
for supporting the additional memory became more and more
ponderous. The CPU registers EB, FB, and BB, and i/o channel 7
(sometimes denoted FEB) were added for no other reason than to support
memory-bank selection.

With these facts in mind, we note that there are 5 basically different
types of memory:

"Unswitched-erasable" memory appears within the address range
0000-1377 (octal). A 15-bit read-write word appears at each of
these addresses, and the memory-bank selection registers (EB, FB, BB,
and FEB) have no effect on them. This area includes all of the CPU registers discussed earlier.

"Switched-erasable" read-write memory appears within the address
range 1400-1777 (octal). There are 8 such memory banks
(designated E0-E7), each containing 400 (octal) 15-bit words, and any
of these banks can be mapped to the address-range 1400-1777 by
manipulating the bits within register EB (or the equivalent bits within
the register BB).

"Common-fixed" read-only memory appears within the address range
2000-3777 (octal). There are 36 (decimal) such banks (designated
00-43 in octal notation), each containing 2000 (octal) 15-bit words,
and any of the banks can be mapped to the address range
2000-3777. Originally, this mapping was performed by manipulating
5 bits within the FB register (or the equivalent bits within the BB
register). However, 5 bits are only capable
of manipulating 32 banks, and as the amount of required read-only
memory increased beyond 32 banks, it was necessary to add an additional
manipulation bit, called the "super-bank bit", within i/o channel 7
(FEB). For banks 00-27, the state of the super-bank bit is
irrelevant. But banks 30-37 (octal) are selected by setting the
super-bank bit to 0 and writing 30-37 to the appropriate bits within
the FB (BB) register. Banks 40-43 (octal), on the other hand, are
selected by setting the super-bank bit to 1 and writing 30-33 (octal)
into the relevant bits of the FB register. (A sharp-eyed reader
may notice that the super-bank mechanism allows 4 more banks than I've
stated,
by setting the super-bank bit and writing 34-37 into the FB register;
however, these extra banks, 44-47, were not physically present in the
AGC. yaAGC does
implement these extra banks, but they are filled with the value 0.)

"Fixed-fixed" read-only memory appears within the address range
4000-7777 (octal). This memory is directly addressable, and
requires no manipulation of the bank-selection registers (EB, FB, BB,
and FEB).

"I/O-channels" appear within the address range 000-777
(octal). The fact that the address range overlaps that used for
unswitched-erasable memory isn't important, since instructions
requiring memory addresses cannot access i/o-channel space, or
vice-versa, so there is no potential ambiguity between the two types of
addresses. The actual set of valid addresses within the 000-777
range, and the number of bits used at each of these addresses, depends
on the actual peripherals attached. Therefore, the set of valid
addresses and the bit-usage varies between the Command Module AGC and
the Lunar Module AGC, and probably also varies somewhat from mission to
mission. (I say "probably", because I don't actually know if it
varied from mission to mission or not.)

But wait (you ask), what about the BB register? I say it is used
for bank selection, but I've not mentioned any functionality for
it. Well, the BB ("both banks") register combined and duplicated
the functionality of the EB ("erasable banks") and FB ("fixed banks")
registers. In other words, if the EB and FB registers were
completely eliminated, the same bank-selection effects could be
obtained by working only with the BB register. Changes to the BB
register are automatically, immediately reflected in changes to the
EB/FB registers, and vice-versa.

As if this scheme is not complicated enough, there is yet another
complication: Small areas of some of these memory spaces overlap,
so that there is actually not as much memory present in the system as
it would seem from the description above.

Unswitched-erasable memory overlaps with banks E0, E1, and E2 of
switched-erasable memory. Switched-erasable bank E0 appears at
unswitched-erasable address 0000 independently of the settings in the
EB register, while switched-erasable bank E1 appears at
unswitched-erasable address 0400 (octal), and switched-erasable bank E2
appears at unswitched-erasable address 1000 (octal). If the
currently-selected switched-erasable bank was E2, for example, the
words stored within the address range 1000-1377 would be identical to
the words stored in the range 1400-1777.

I/O channels 1 and 2 coincide with the unswitched-erasable
addresses 1 and 2, or (as implied by the description above) with
the switched-erasable addressed E0,1 and E0,2. I believe I
have seen references stating that i/o channel 0 is also duplicated with
unswitched-erasable address 0 — i.e., with the accumulator.
However, Blair-Smith and notes in the Luminary131 and Colossus249
source code all seem to agree that only i/o channels 1 and 2 are
duplicated. Since there doesn't seem to be an i/o channel 0, the
point is probably moot.

Interrupt Processing

The AGC has 11 interrupt types. Interrupts are triggered by
external events, but usually not directly so. Various external
signals affect the counter registers (see above), and underflow/overflow
of certain of these counters then trigger the actual interrupts.

An interrupt-vector table appears at address 4000 octal in
fixed-memory, with table entries spaced at intervals of 4 words.
Each interrupt type vectors to its dedicated address in the vector
table.

The digital
autopilot (DAP) for controlling thrust times of the
jets of the reaction control system (RCS).

4010

T5RUPT

Overflow
of counter-timer TIME5.

Used by
the autopilot.

4014

T3RUPT

Overflow
of counter-timer TIME3.

Used by
the task scheduler (WAITLIST).

4020

T4RUPT

Overflow
of counter-timer TIME4.

Used for
various DSKY-related activities such as monitoring the PRO key and
updating display data.

4024

KEYRUPT1

Keystroke
received from DSKY.

The DSKY
transmits codes representing keystrokes to the AGC. Reception of
these codes by the AGC hardware triggers an interrupt.

4030

KEYRUPT2

Keystroke received from secondary DSKY.

In
the CM, there was a second DSKY at the navigator's station, used for
star-sighting data, in conjunction with the Alignment Optical Telescope
(AOT). There was no 2nd DSKY in the LM, but the interrupt was not
re-purposed.

4034

UPRUPT

Uplink
word available in the INLINK register.

Data
transmitted from ground-control for the purpose of controlling or
monitoring the AGC is in the form of a serial data stream which is
assembled in AGC INLINK counter-register. When a word has been
assembled in this fashion, an interrupt is triggered.

Data from the rendezvous radar is
apparently assembled similarly to the uplink data described
above. When a data word is complete, an interrupt is triggered.

4050

RUPT10
(LM)

TBD

Used
for LM landing guidance, but details are TBD

HANDRUPT
(CM)

TBD

Used
for CM hand control, but details are TBD

There is a master interrupt-enable mask, and when interrupts are
disabled the interrupt-vectoring described above does not take
place. Interrupts are globally enabled by the RELINT instruction (see below), and are globally disabled by
the INHINT instruction.

Enabling interrupts globally does not necessarily cause an interrupt to
immediately occur even if one has been requested. Other
conditions
which defer processing of interrupts include:

The Extracode flag is set (see
below) — i.e., if a two-word instruction has been found, but only
the first word of it has been read from memory so far.

The registers contain overflow or underflow
conditions. (Since these are the only 16-bit registers, an
interrupt-service routine would have no way of preserving the full
values of these registers if they contained overflow. When there
is no overflow, the data is effectively 15 bits, and can be saved and
restored without difficulty from normal 15-bit storage locations.)

Following the INDEX
instruction. (The AGC actually allowed interrupts between
an INDEX instruction and
the instruction being indexed, and somehow employed the hidden register
B to avoid problems. However, yaAGC
does not have a B register, and sidesteps the issue by delaying the
interrupt.)

An interrupt-service routine is already in progress, and
not yet terminated by a RESUME
instruction.

Immediately preceding an
INHINT, RELINT, or EXTEND instruction.

When the program
counter is in the range 0-060. (The true
AGC did not inhibit such interrupts, but at least for the present I am
doing so. This should not have any perceptible affect other than
delaying interrupts that occur with Z=0,1,2 for a cycle or two. I
may relax this restriction later.)

When an interrupt request is processed, the following steps are taken:

The current contents of the program counter (Z register) is saved
into the ZRUPT register.

The instruction appearing at the memory location pointed to by
the program counter is saved into the BRUPT register.

Control passes to the appropriate vector-table location, as
listed above.

Execution then continues (with interrupts inhibited) until the
interrupt-service routine returns using the RESUME instruction.

It should also be pointed out that since an interrupt service routine
does not preserve the values of any memory locations not specifically
allocated
to it, including the central registers, it is necessary for an
interrupt-service routine to specifically save all registers which it
intends to use, and then to restore them before returning. The
registers ARUPT, LRUPT, QRUPT, and BBRUPT are provided for the specific
purpose of temporarily storing the A, L, Q, and BB registers during
interrupt processing.

Instruction Representation

Assembly-Language Instructions

By original design, every instruction in the AGC instruction set
occupied precisely one word of memory (including operand), and
consisted of 15 bits as follows:

CCC
AAA AAA AAA AAA

The 3-bit field CCC
represents the instruction type (the "code"), while the 12-bits AAAAAAAAAAAA represent a
memory address. With this simple scheme, there are at most 8
instruction types, each
of which can operate on any memory location (assuming that memory
consists of 4096 words). However, as the number of required
instruction types ballooned, more complex instruction encoding was
introduced, and several different new types of encoding appeared.
The required memory also ballooned but (as described above), the introduction of banking allowed
continued use of 12-bit addressing in the instruction encoding.

Some of the more-primitive instruction types, such as AD (addition), CA (accumlator load), CCS (compare), and so forth,
continue to be encoded by the simple 3+12 encoding mentioned above.

Instructions which operate only on erasable memory, and therefore
can use a truncated 10-bit address space, are encoded with a scheme
like CCC QQA AAA AAA AAA.
Instruction-types in this case can thus be encoded in 5 bits, CCCQQ. The bits QQ are referred to as "quarter
code" or "QC".

Instructions which operate only i/o channels can use and even
more truncated 9-bit address space, and are encoded as CCC PPP AAA AAA AAA. The
field PPP is referred to
as the "peripheral code" or "PC".

Finally, a fourth bit was made available beyond the 15 within the
instruction itself by introducing the concept of an "extracode".
Instructions encoded as described above are referred to as basic instructions.
However, the instruction called EXTEND
was introduced as a kind of escape code, in that any instruction
immediately following the EXTEND
instruction is no longer interpreted by the CPU as being in the set of
"basic instructions", but instead is taken from a completely different
set of instructions called extracodes.
In effect, "basic instructions" use one word of memory, while
"extracodes" use two words of memory.

It is also worth noting that when an instruction operates on
double-precision (DP) values, it necessarily addresses a pair of words
in memory rather than a single memory word. In these cases, the
address field points to the second
word of the pair. For example consider the "double exchange"
instruction DXCH, which
has Code=5 (CCC) and QC=1 (QQ). The instruction "DXCH K", where (say) K is the program label
associated with address 70 octal. This instruction would exchange
the contents of the A,L register pair with the values stored at
addresses 70 and 71. The address field AAAAAAAAAA would be encoded as
00071 rather than 00070. The instruction itself would be encoded
as the octal value 52071. This is, of course, handled
automatically by the assembler and is not usually something of direct
concern to the programmer.

Interpretive Instructions

TBD

AGC4 Instruction Set

AD

Description:

The "Add"
instruction adds the contents of a memory location into the accumulator.

Syntax:

AD K

Operand:

K is the label of a memory
location. It must assemble to a 12-bit memory address.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow depends on the
result of the operation, and can be positive, negative, or none.
The Extracode flag remains clear.

Editing:

Editing is done upon writing to K, if K is CYR, SR, CYL, or EDOP.

Octal:

60000 + K

Notes:

The
accumulator is not overflow-corrected prior to the addition. The contents of K are added to the
accumulator, which retains any overflow that resulted from the addition.

A side-effect of this instruction is that K is rewritten after its value
is written to the accumulator; this means that if K is CYR, SR, CYL, or EDOP,
then it is re-edited.

Note that the normal result of AGC arithmetic such as (+1)+(-1) is -0.

For the special case "AD A",
refer instead to the DOUBLE
instruction.

ADS

Description:

The "Add to Storage"
instruction adds the accumulator to an erasable-memory location (and
vice-versa).

Syntax:

ADS K

Operand:

K is the label of a memory
location. It must assemble to a 10-bit memory address in erasable
memory.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is set according to
the result of the addition.
The Extracode flag remains clear.

Editing:

Editing is done upon writing to K, if K is CYR, SR, CYL, or EDOP.

Octal:

26000 + K

Notes:

The contents of the accumulator
and K are added together,
and the
result is stored both in the accumulator and in K. The accumulator is
neither overflow-corrected prior to the addition nor after it.
However, the sum is overflow-corrected prior to being saved at K if K is a 15-bit register.
If K is a 16-bit
register like L or Q, then the sum is not overflow corrected before
storage.

Note that the normal result of AGC arithmetic such as (+1)+(-1) is -0.

If the destination register is 16-bits (L or Q register), then the
non-overflow-corrected values added.

AUG

Description:

The "Augment"
instruction increments a positive value in an erasable-memory location
in-place by +1, or a negative value by -1.

Syntax:

AUG K

Operand:

K is the label of a memory
location. It must assemble to a 10-bit memory address in erasable
memory.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is set according to
the result of the operation.
The Extracode flag is cleared.

Editing:

Editing is done upon writing to K, if K is CYR, SR, CYL, or EDOP.

Octal:

24000 + K

Notes:

If K
is a 16-bit register like A, L, or Q, then arithmetic is performed on
the
full 15-bit value (plus sign). Otherwise, only the available
14-bit value (plus sign) is used.

If the contents of K
before the operation is greater than or equal to +0, it is incremented
by +1. On the other hand, if it is less than or equal to -0, it
is decremented.

If K is one of the
counter registers which triggers an interrupt upon overflow, then an
oveflow caused by AUG
will trigger the interrupt also. These registers include
TIME3-TIME6. Furthermore, if K is the TIME1 counter and the AUG causes an overflow, the
TIME2 counter will be incremented. Some of the counter registers
such as CDUX-CDUZ are formatted in 2's-complement format, but the AUG instruction is insensitive
to this distinction and always uses normal 1's-complement arithmetic.

BZF

Description:

The "Branch Zero to Fixed"
instruction jumps to a memory location in fixed (as opposed to
erasable) memory if the accumulator is zero.

Syntax:

BZF K

Operand:

K is the label of a memory
location. It must assemble to a 12-bit memory address in fixed
memory. (In other words, the two most significant bits of address
K cannot be 00.)

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

1 MCT (about 11.7 µs) if
the accumulator is plus zero or minus zero, or 2 MCT (about 23.4
µs) if the accumulator is non-zero.

Flags:

The Overflow is not
affected. The Extracode flag is cleared. The Q register
is unaffected.

Editing:

The CYR, SR, CYL, and EDOP
registers are not affected.

Octal:

10000 + K

Notes:

If
the accumulator is
non-zero, then control proceeds to the next
instruction. Only if the accumulator
is plus zero or minus zero
does
the branch to address K
occur. The accumulator (and its stored overflow) are not actually
modified.

Note that if the accumulator contains overflow, then the accumulator is
not treated as being
zero, even if the sign-corrected value would be +0 or -0.
This instruction does not set
up a later
return. Use the TC
instruction instead for that.

Indirect conditional
branch:
For an indirect conditional branch, it is necessary to combine an INDEX instruction with a BZF instruction. Refer to
the entry for the INDEX
instruction.

BZMF

Description:

The "Branch Zero or Minus to
Fixed"
instruction jumps to a memory location in fixed (as opposed to
erasable) memory if the accumulator is zero or negative.

Syntax:

BZMF K

Operand:

K is the label of a memory
location. It must assemble to a 12-bit memory address in fixed
memory. (In other words, the two most significant bits of address
K cannot be 00.)

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

1 MCT (about 11.7 µs) if
the accumulator is zero or negative, or 2 MCT (about 23.4 µs) if
the accumulator is positive non-zero.

Flags:

The Overflow is not
affected. The Extracode flag is cleared. The Q register
is unaffected.

Editing:

The CYR, SR, CYL, and EDOP
registers are not affected.

Octal:

60000 + K

Notes:

If the accumulator
is positive
non-zero, then control proceeds to the next instruction. Only if
the accumulator is plus
zero or negative does the branch to address K occur. The accumulator
and its stored oveflow are not actually modified.

Note that if the accumulator contains +overflow, then the accumulator
is not treated as being zero,
even if the sign-corrected value would be +0. If the accumulator
contains negative overflow, then the value is treated as being negative
non-zero, so the jump is taken.
This instruction does not set
up a later
return. Use the TC
instruction instead for that.

Indirect conditional
branch:
For an indirect conditional branch, it is necessary to combine an INDEX instruction with a BZMF instruction. Refer
to
the entry for the INDEX
instruction.

CA (or CAE or CAF)

Description:

The "Clear and Add"
(or "Clear and Add Erasable" or "Clear and Add Fixed") instruction
moves the contents of a memory location into the accumulator.

Syntax:

CA Kor
CAE Kor
CAF K

Operand:

K is the label of a memory
location. It must assemble to a 12-bit memory address. The CAE or CAF variants differ from the
generic CA, only in that
the assembler is supposed to display error messages if K is not in erasable or fixed
memory, respectively.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is cleared, unless K is the accumulator or the Q
register.
The Extracode flag remains clear.

Editing:

Editing is done upon writing to K, if K is CYR, SR, CYL, or EDOP.

Octal:

30000 + K

Notes:

A
side-effect of this instruction is that K is rewritten after its value
is written to the accumulator; this means that if K is CYR, SR, CYL, or EDOP,
then it is re-edited.

Note that if the source register contains 16-bits (like the L or Q
register),
then all 16 bits will be transferred to the accumulator, and thus the
overflow will be transferred into A. On the other hand, if
the source register is 15 bits, then it will be sign-extended to 16
bits when placed in A.

For the special case "CA A",
refer instead to the NOOP
instruction.

CCS

Description:

The "Count, Compare, and Skip"
instruction stores a variable from erasable memory into the accumulator
(which is decremented), and then performs
one of several jumps based on the original value of the variable.
This is the only "compare" instruction in the AGC instruction set.

Syntax:

CCS K

Operand:

K is
the label of a memory location. It must assemble to a 10-bit
memory
address in erasable memory.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is set according to
the result of the operation. The Extracode flag remains
cleared.

Editing:

The
contents of K is edited
if K is one of the
special
registers CYR, SR, CYL, or EDOP.

Octal:

10000 + K

Notes:

The operation of this
instruction is rather complex:

The "Diminished ABSolute value" of the contents of
memory-location K is
loaded into the A register. The diminished absolute value is
defined as DABS(x)=|x|-1 if |x|>1, or +0 otherwise. (If K is a 16-bit register like A,
L, or Q, then its contents
may contain + or - overflow; overflow correction is not performed prior to the
operation.)

After computing the contents of the accumulator, the
contents of K is
"edited", if K is one of
the registers CYR, SR, CYL, or EDOP, but is otherwise unchanged from
its original value.

A jump is performed, depending on the original (unedited) contents of K: If
greater than +0, execution continues at the next instruction after the CCS. If equal to +0,
execution continues at the 2nd instruction after the CCS. If less than -0,
execution continues at the 3rd instruction after the CCS. If equal to -0,
execution continues at the 4th instruction after the CCS. (If K
is 16 bits, then the original contents may contain + or - overflow; in
this case, the value is treated as + or - non-zero, even if the
sign-corrected value would have been 0.)

A typical use of this instruction would be for loop control, with "CCS A".

Note that the net effect of the way overflow is treated when K is A, L, or Q is to allow
15-bit
loop counters rather than mere 14-bit loop counters. For example,
if A contains +1 with +overflow, then CCS A will place +0 with
+overflow into A, and another CCS
A will place 037777 without overflow into A, and thus no anomaly
is seen when decrementing from +overflow to no overflow.

The overflow of the accumulator will generally be cleared by this
operation except in the kinds of cases decribed in the preceding
paragraph.

COM

Description:

The
"Complement the Contents of A" bitwise complements the accumulator

Syntax:

COM

Operand:

This instruction has no operand.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs).

Flags:

The Overflow is unaffected.
The Extracode flag remains clear.

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unaffected.

Octal:

40000

Notes:

All 16 bits of the accumulator
are complemented. Therefore, in addition to negating the contents
of the register (i.e., converting plus to minus and minus to plus), the
overflow is preserved.

This instruction assembles as "CS
A".

CS

Description:

The "Clear and Subtract"
instruction moves the 1's-complement (i.e., the negative) of a memory
location into the accumulator.

Syntax:

CS K

Operand:

K is the label of a memory
location. It must assemble to a 12-bit memory address.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is cleared, unless K is the accumulator.
The Extracode flag remains clear.

Editing:

Editing is done upon writing to K, if K is CYR, SR, CYL, or EDOP.

Octal:

40000 + K

Notes:

A
side-effect of this instruction is that K is rewritten with its
original value after the accumulator is written; this means that if K is CYR, SR, CYL, or EDOP,
then it is re-edited.
Note that if the source register contains 16 bits (the L or Q
register),
then all 16 bits will be complemented and transferred to the
accumulator, and thus the
overflow in the source register will be inverted and transferred into
A. (For
example, +overflow in Q will turn into -overflow in A.) On the
other hand, if the
source register is 15 bits, then it will be complemented and
sign-extended to 16 bits
when placed in A.

For the special case "CS A",
refer instead to the COM
instruction.

DAS

Description:

The "Double Add to Storage"
instruction does a double-precision (DP) add of the A,L register pair
to a pair of variables in erasable memory.

Syntax:

DAS K

Operand:

K is the label of a memory
location. It must assemble to a 10-bit memory address in erasable
memory. The location K
contains the more-significant word of a pair of variables containing a
DP value, while K+1
contains the less-significant word.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

3 MCT (about 35.1 µs)

Flags:

The Overflow is cleared.
The Extracode flag remains clear.

Editing:

Editing is done if the K,K+1
variable pair overlaps the CYR, SR, CYL, and EDOP
registers.

Octal:

20001 + K

Notes:

A variant on this instruction is
the case "DAS A"
Refer to the DDOUBL
instruction for an explanation of this case.

Prior to the instruction, the A,L register pair and the K,K+1 pair each contain a
double precision (DP) value, with the more-significant word first and
the less-significant word second. The
signs of the contents of A and L
need not
agree, nor need the signs of K
and K+1. (See above.)

16-bit values (the A, L, and Q
registers) are not overflow-corrected prior to the addition. The
words of the sum are overflow-corrected when saved to 15-bit registers
but not when saved to 16-bit registers.

The two DP values are added together, and the result is stored back in
the K,K+1
pair. The signs of the
resulting words need not
agree; the sign of the less significant word is the same as the sign
from an SP addition of the less-significant words. Any overflow
or underflow from addition of the less-significant words rolls over
into the addition of the more-significant words.

If either of K or K+1 are editing registers (CYR,
SR, CYL, or EDOP), then the appropriate editing occurs when K,K+1 are written.

Note that the normal result of AGC arithmetic such as (+1)+(-1) is -0.

After the addition, the L register is set to +0, and the A register is
set to +1, -1, or +0, depending on whether there had been positive
overflow, negative overflow, or no overflow during the addition.

DCA

Description:

The "Double Clear and Add"
instruction
moves the contents of a pair of memory locations into the A,L register
pair.

Syntax:

DCA K

Operand:

K is the label of a memory
location. It must assemble to a 12-bit memory address.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

3 MCT (about 35.1 µs)

Flags:

The Overflow is cleared.
The Extracode flag is cleared.

Editing:

Editing is done after the
operation, if K,K+1
coincides with CYR, SR,
CYL, or EDOP.

Octal:

30001 + K

Notes:

The
value from K is
transferred into the accumulator, while the value from K+1 is transferred into the L
register.

A
side-effect of this instruction is that K,K+1 are rewritten after their
values are written to the A,L register pair; this means that if K or K+1 is CYR, SR, CYL, or EDOP,
then they are re-edited.

The instruction "DCA L" is
an unusual case. Since the less-significant word is
processed first and then the more-significant word, the effect will be
to first load the L register with the contents of the Q register, and
then to load the A register with the contents of L. In other
words, A and L will both be
loaded with the contents of the 16-bit register Q.

On the
other hand, the instruction "DCA
Q" will cause the full 16-bit contents (including overflow) of Q
to be loaded into A, and the 15-bit contents of EB to be sign-extended
to 16 bits and loaded
into L.

Note: The final
contents of the L register will be overflow-corrected.

DCOM

Description:

The
"Double Complement" bitwise complements the register pair A,L

Syntax:

DCOM

Operand:

This instruction has no operand.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

3 MCT (about 35.1 µs).

Flags:

The Overflow is unaffected.
The Extracode flag is cleared.

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unaffected.

Octal:

40001

Notes:

All
16 bits of the accumulator and the L register are
complemented. Therefore, in addition to
negating the DP value (i.e., converting plus to minus
and minus to plus), the overflow is preserved.

This instruction assembles as "DCS
A".

DCS

Description:

The "Double Clear and Subtract"
instruction
moves the 1's-complement (i.e., the negative) of the contents of a pair
of memory locations into the A,L register pair.

Syntax:

DCS K

Operand:

K is the label of a memory
location. It must assemble to a 12-bit memory address.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

3 MCT (about 35.1 µs)

Flags:

The Overflow is cleared.
The Extracode flag is cleared.

Editing:

Editing is done after the
operation, if K,K+1
coincides with CYR, SR,
CYL, or EDOP.

Octal:

40001 + K

Notes:

The
negative of the value from K
is transferred into the accumulator, while the negative of the value
from K+1 is transferred
into the L register.

A
side-effect of this instruction is that K,K+1 are rewritten after their
values are written to the A,L register pair; this means that if K or K+1 is CYR, SR, CYL, or EDOP,
then they are re-edited.

For the special case "DCS A",
refer to the DCOM
instruction.

The instruction "DCS L" is
an unusual case. Since the less-significant word is
processed first
and then the more-significant word, the effect will be to first load
the L register with the negative of the contents of the 16-bit Q
register, and
then to load
the A register with the negative of the contents of L. In other
words, A will be loaded with the contents of Q, and L will be loaded
with the negative of the contents of Q.

On the other hand, the instruction "DCS Q" will load A with the
full 16-bit complement of Q, and will load L with the 15-bit complement
of EB as extended to 16 bits.

Note:
The final contents of the L register will be overflow-corrected.

DDOUBL

Description:

The "Double Precision Double"
instruction — and yes, that's really what it's called — adds the
double-precision (DP) value in the A,L register pair to itself.

Syntax:

DDOUBL

Operand:

This instruction has no operand.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND
instruction.

Timing:

3 MCT (about 35.1 µs).

Flags:

The Overflow is set according to
the result of the operation.
The Extracode flag remains clear.

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unaffected.

Octal:

20001

Notes:

The A,L register pair is treated
as a DP value, and is added to itself, returning a DP value in the A,L
register.

Note that if the accumulator contains overflow prior the addition, the
accumulator will not be
overflow-corrected prior to the addition, and thus the sign of the
resulting sum will not be correct. As Blair-Smith and
Savage&Drake state, the results will be "messy".

Refer to the DAS
instruction for a discussion of mismatched signs in the
less-significant and more-significant words.

DIM

Description:

The "Diminish"
instruction decrements a positive non-zero value in an erasable-memory
location in-place, or increments a negative non-zero value.

Syntax:

DIM K

Operand:

K is the label of a memory
location. It must assemble to a 10-bit memory address in erasable
memory.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is set as a result
of the operation.
The Extracode flag is cleared.

Editing:

Editing is done upon writing to K, if K is CYR, SR, CYL, or EDOP.

Octal:

26000 + K

Notes:

If K
is a 16-bit register like A, L, or Q, then arithmetic is performed on
the
full 15-bit value (plus sign). Otherwise, only the available
14-bit
value (plus sign) is used.

If the contents of K
before the operation is greater than +0, it is decremented
by +1. On the other hand, if it is less than -0, it is
incremented by +1. (For example, 6 would become 5, or -6 would
become -5.) A value of +0 or -0 would be unchanged by the
operation. Note, by the way, that +1 decrements to -0, as is
normal for AGC additions.

If K is one of the
counter registers which triggers an interrupt upon reaching 0, then an
oveflow caused by DIM
will trigger the interrupt also. The register only register so affected
would seem to be TIME6. Some of the counter registers
such as CDUX-CDUZ are formatted in 2's-complement format, but the DIM instruction is insensitive
to this distinction and always uses normal 1's-complement
arithmetic. A sharp-eyed reader may notice that the DIM instruction behaves
simularly to the DINCunprogrammed sequence, and wonder if
it likewise emits POUT, MOUT, and ZOUT output pulses; it does not do so.

DOUBLE

Description:

The
"Double the Contents of A" instruction adds the accumulator to itself.

Syntax:

DOUBLE

Operand:

This instruction has no operand.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 35.4 µs).

Flags:

The Overflow is set according to
the result of the operation.
The Extracode flag remains clear.

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unaffected.

Octal:

60000

Notes:

The value in the accumulator is
added to itself, and then placed back into the accumulator.

Note that if the accumulator contains overflow prior the addition, the
accumulator will not
be overflow-corrected prior to the addition, and thus the sign of the
resulting sum will not be correct.

DTCB

Description:

The
"Double Transfer Control, Switching Both Banks" instruction performs a
jump and switching both fixed and erasable banks, by simultaneously
loading the BB and Z registers.

Syntax:

DTCB

Operand:

This instruction has no operand.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND
instruction.

Timing:

3 MCT (about 35.1 µs).

Flags:

The Overflow is cleared.
The Extracode flag remains clear.

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unaffected.

Octal:

52006

Notes:

This
instruction exchanges the contents of A with Z, and the contents of L
with B. Thus by preloading the A,L register pair, we can
effectively
perform a jump to a different memory bank, and switching both fixed and
erasable banks, whilst preserving the
current address and erasable- and fixed-memory banks for a later return.

The assembler, yaYUL, provides
a pseudo-op 2BCADR for
the purpose of creating data suitable for preloading into A,L.

This assembles as a DXCH
to address 5. Recall that registers 5 and 6 are the Z and BB
registers.

DTCF

Description:

The
"Double Transfer Control, Switching F Bank" instruction performs a jump
to a different fixed memory bank, by simultaneously loading the FB and
Z registers.

Syntax:

DTCF

Operand:

This instruction has no operand.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND
instruction.

Timing:

3 MCT (about 35.1 µs).

Flags:

The Overflow is cleared.
The Extracode flag remains clear.

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unaffected.

Octal:

52005

Notes:

This instruction exchanges the
contents of A with FB, and the contents of L with Z. Thus by
preloading the A,L register pair, we can effectively perform a jump to
a different fixed-memory bank, whilst preserving the current address
and fixed-memory bank for a later return.

The assembler, yaYUL, provides
a pseudo-op 2FCADR for
the purpose of creating data suitable for preloading into A,L.

This assembles as a DXCH
to address 4. Recall that registers 4 and 5 are the FB and Z
registers.

DV

Description:

The "Divide" instruction
performs a division, giving a remainder and a quotient.

Syntax:

DV K

Operand:

K is the label of a memory
location. It must assemble to a 12-bit memory address.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

6 MCT (about 70.2 µs)

Flags:

The Overflow is cleared.
The Extracode flag is cleared.

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unaffected.

Octal:

10000 + K

Notes:

The
accumulator and the L register (and the Q register if K is Q) are overflow-corrected
prior to the operation.

There are two alternate but equally valid ways of looking at this
operation.

The double-precision (DP) contents of the A,L register pair
(A being
the more-significant word and L the less-significant word) are divided
by the single-precision (SP) value in K, leaving an SP quotient in
the
A register pair, and the less-significant word of a DP remainder in the
L register (the more-significant word of the DP remainder being
0). The divisor K
is required to be larger than
the dividend A,L; this is natural since otherwise the quotient would be
larger than or equal to 1.0, and could not be represented as an AGC SP
value (all of which are less than 1.0 in magnitude).

Or: The double-length 1's-complement integer in the
A,L
register pair is divided by the 1's-complement
integer in K, leaving the
quotient in A and the remainder in L. The integer K is required
to be larger than the 1's-complement integer in A; this is
natural since otherwise the quotient would be too large to fit into the
A register.

The signs of the dividend words stored in A and L do not necessarily
agree
with each other.

The sign of
the quotient (A register) is (as usual, according to the rules of
arithmetic) positive if the signs of the
dividend and divisor agree, and is negative if the signs of the
dividend and divisor differ. The sign of the remainder (L
register) is the sign of the dividend.

Note that the sign of the dividend is the sign of the A register prior
to the division, unless A is ±0; in that case, the sign of the
dividend is the sign of the L register. For example, suppose that
A contains +0 and L contains -0; then the overall sign of the dividend
is -0.

If the divisor is equal to the dividend in magnitude, then the A
register will be stored with ±37777, while the L register will
be stored with the divisor.

If the divisor is less than the dividend in magnitude, according to
Savage&Drake, "we get total nonsense", and there is no warning or
indication of the problem; the advice given by Savage&Drake is
simply to make sure (by pre-scaling) that this situation doesn't
occur. I assume that "getting
total nonsense" was accepted for pragmatic reasons, and was not
seriously intended by the designers of the AGC hardware. However,
on the
grounds that Luminary or Colossus code may have relied on
this "total nonsense" (as programmers tend to do), yaAGC
returns random numbers in A and L in this case.

Several numerical examples are given in Smally:

Considered
as Fractional Values (Decimal)

Considered
as Integer Values (Octal)

DP
Dividend A,L

SP
Divisor K

SP
Quotient A

DP
Remainder 0,L

Dividend
A

Dividend
L

Divisor
K

Quotient
A

Remainder
L

+0.4998779334

+0.5

+0.9997558594

+0.0000000037

+17777
(17777)

-37777
(40000)

+20000
(20000)

+37774
(37774)

+00001
(00001)

+0.4998779334

-0.5

-0.9997558594

+0.0000000037

+17777
(17777)

-37777
(40000)

-20000
(57777)

-37774
(40003)

+00001
(00001)

-0.4998779334

+0.5

-0.9997558594

-0.0000000037

-17777
(60000)

+37777
(37777)

+20000
(20000)

-37774
(40003)

-00001
(77776)

-0.4998779334

-0.5

+0.9997558594

-0.0000000037

-17777
(60000)

+37777
(37777)

-20000
(57777)

+37774
(37774)

-00001
(77776)

+0.4999999963

+0.5

+0.9999389648

+0.0000305139

+17777
(17777)

+37777
(37777)

+20000
(20000)

+37777
(37777)

+17777
(17777)

+0.9998779297

+0.9998779297

+0.9999389648

+0.0000610277

+37776
(37776)

+00000
(00000)

+37776
(37776)

+37777
(37777)

+37776
(37776)

-0.0

+0.0

-0.9999389648

-0.0

+00000
(00000)

-00000
(77777)

+00000
(00000)

-37777
(40000)

-00000
(77777)

-0.0

-0.0

+0.9999389648

-0.0

+00000
(00000)

-00000
(77777)

-00000
(77777)

+37777
(37777)

-00000
(77777)

+0.0

+0.0

+0.9999389648

+0.0

-00000
(77777)

+00000
(00000)

+00000
(00000)

+37777
(37777)

+00000
(00000)

+0.0

-0.0

-0.9999389648

+0.0

-00000
(77777)

+00000
(00000)

-00000
(77777)

-37777
(40000)

+00000
(00000)

DXCH

Description:

The "Double Exchange"
instruction exchanges the double-precision (DP) value in the
register-pair A,L with a value stored in the erasable memory variable
pair K,K+1.

Syntax:

DXCH K

Operand:

K is the label of a memory
location. It must assemble to a 10-bit memory address in erasable
memory.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

3 MCT (about 35.1 µs)

Flags:

The Overflow is set according to
the result of the operation.
The Extracode flag remains clear.

Editing:

Editing is done upon writing to K,K+1 if either is CYR, SR,
CYL, or EDOP.

Octal:

52001 + K

Notes:

The
accumulator is stored at address K,
while the value in K is
stored into the
accumulator. The value from the L register is stored into K+1, and vice-versa. If K or K+1 is an editing register
(CYR, SR, CYL, EDOP), then the value from the A or L register is edited
whilst being stored into K
or K+1.

If K is Q, then the full
16-bit values of A and Q are exchanged. Otherwise, A is
overflow-corrected before
being stored in K, and K is sign-extended when placed
in A.

In the case of the "DXCH L"
instruction (in which the source and destination ranges overlap,
Q (full 16 bits, including overflow) goes into A, A goes
into L, and L goes into Q.

Note: The final
contents of the L register will be overflow-corrected.

EDRUPT

Description:

For machine checkout only.

Syntax:

EDRUPT K

Operand:

K is the address of an
instruction. In assembly, only the lower 9 bits are retained, and
the upper 3 bits are cleared. However, the actual value
is irrelevant and is ignored during execution.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

3 MCT (about 35.1 µs)

Flags:

The
Extracode flag is cleared.

Editing:

I presume there is no effect on
CYR, SR, CYL, and EDOP.

Octal:

07000 + KC

Notes:

This instruction
is not listed by Savage&Drake. It
is listed by Blair-Smith, but not explained, other than to say it is
for "machine checkout only". Note that software versions prior to
20050820 incorporated a very different conception of the operation of
this instruction than described below.

Fortunately, I have been able to obtain an explanation directly from
Hugh Blair-Smith: The EDRUPT
instruction is so-called because it was requested by programmer Ed
Smally, and was used only by him. From discussing it with Hugh
and from examining the instruction's "control pulses", it appears to me
that it does this:

Inhibits interrupts until the next RESUME instruction (as if a
hardware interrupt had been encountered).

Loads the Z register into the ZRUPT register.
(Incidentally, so far I've seen only a single instance of this
instruction, in Luminary 131, and the return address is never used.)

Takes the next instruction from address 0 (which presumably
has been pre-loaded with "TC something").

The instruction also shoves
a couple of register values onto the data bus, but does not save them
into memory. My assumption
is that some kind of external instrumentation was able to capture these
values, but that they were irrelevant to normal execution.

EXTEND

Description:

Set the Extracode flag, so that
the next instruction
encountered is taken from the "extracode" instruction set rather than
from the "basic" instruction set.

Syntax:

EXTEND

Operand:

This instruction has no operand.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

1 MCT (about 11.7 µs)

Flags:

The Overflow is not
affected. The Extracode flag is set.

Editing:

The CYR, SR, CYL, and EDOP
registers are not edited.

Octal:

00006

Notes:

Sets the Extracode flag, so
that the next instruction countered will be decoded as an "extracode"
instruction rather than as a "basic" instruction. Since only 3
bits are provided within instruction words to indicate the instruction
type, and yet there are more than 8 instruction types, the "extracode"
method was provided as a means to have 4 bits available to indicate
instruction types, and thus to extend the number of instruction types
which can be represented.

The Extracode flag is
automatically reset as soon as the next instruction after EXTEND is executed, unless the
next instruction is an INDEX
instruction, in which case the next instruction after the INDEX is also treated as
extracode.

Interrupts are automatically disabled while the Extracode flag is
set. Thus, there is no need to fear that an interrupt will occur
between an EXTEND and the
instruction following it, and hence that the wrong instruction will be
affected by the EXTEND.

The encoding of this instruction is a special case of TC K, in which K is the address 00006, however
the action is completely unlike other TC instructions. Note
that a normal TC
instruction to address 00006 can be manufactured by means of
indexing. For example, suppose that some location K contains the numerical value
6. The following sequence of instructions

INDEX KTC A

ends up executing an instruction numerically encoded as 00006 octal,
just is the EXTEND
instruction; however, that instruction is treated literally as a TC instruction addressing
location 00006 rather than as an EXTEND
instruction.

INCR

Description:

The "Increment"
instruction increments an erasable-memory location in-place by +1.

Syntax:

INCR K

Operand:

K is the label of a memory
location. It must assemble to a 10-bit memory address in erasable
memory.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is not affected
unless K is the
accumulator.
The Extracode flag remains clear.

Editing:

Editing is done upon writing to K, if K is CYR, SR, CYL, or EDOP.

Octal:

24000 + K

Notes:

If K
is a 16-bit register like A, L, or Q, then non-overflow-corrected value
is
incremented. In other words, in A, L, or Q, one can increment 0,
1,
..., 037777, 040000 (with + overflow), 040001 (with + overflow), ....
077777 (with + overflow). For 15-bit registers, one can only
increment as high as 037777.

If K is one of the
counter registers which triggers an interrupt upon overflow, then an
oveflow caused by INCR
will trigger the interrupt also. These registers include
TIME3-TIME6. Furthermore, if K is the TIME1 counter and the INCR causes an overflow, the
TIME2 counter will be incremented. Some of the counter registers
such as CDUX-CDUZ are formatted in 2's-complement format, but the INCR instruction is insensitive
to this distinction and always uses normal 1's-complement arithmetic.

INDEX (or NDX)

Description:

The "Index Next Instruction" or
"Index Extracode Instruction" instruction causes the next instruction
to be executed in a modified way from its actual representation in
memory.

Syntax:

INDEX Kor
NDX K

Operand:

K is the label of a memory
location. It must assemble to a 10-bit address in erasable memory
when INDEX is used as a
basic instruction, but cannot be equal to 17octal. It can
assemble to a 12-bit memory address when INDEX is used as an extracode
instruction.

Extracode:

This can be used with as a basic
instruction (without a preceding EXTEND
instruction), or as an extracode instruction (with a preceding EXTEND instruction).

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is not
affected.
The Extracode flag is not affected.

Editing:

Editing is done upon writing to K, if K is CYR, SR, CYL, or EDOP.

Octal:

50000 + K

Notes:

The
idea behind this instruction is that it simulates an indexed addressing
mode. The effect is to:

Retrieve the value stored in K;

Add this value to the next instruction following the INDEX instruction before
executing it, but without actually modifying memory.

This is perhaps best illustrated by an example. Suppose that we
wanted to form a jump-table, from which would could jump to a number of
different places in memory. The code could look like this:

# BEFORE EXECUTING THE FOLLOWING CODE,
# LOAD 1 INTO A TO JUMP TO
LOC1, LOAD 2
# INTO A TO JUMP TO LOC2,
AND SO ON.
# SIMILARLY, LOAD -1 INTO A
TO JUMP TO LOC-1.
# (RECALL ALSO THAT "LOC-1"
IS A LEGAL
# SYMBOL NAME, AND DOES
*NOT* MEAN
# "LOC MINUS 1".)
INDEX A
TC JMPTAB
...
TCF
LOC-2
TCF
LOC-1
JMPTAB TCF LOC0
TCF
LOC1
TCF
LOC2
TCF
LOC3
...

For example, if we loaded the value 3 into the accumulator, then the "INDEX A" instruction would
effectively turn the "TC JMPTAB"
instruction into "TC JMPTAB +3",
which would cause a jump to the "TCF
LOC3" instruction. But the modification to the "TC JMPTAB" instruction caused
by INDEX takes place only
within the musty recesses of the CPU, and not in memory. So the
code listed above could be in fixed memory, and is not self-modifying code.

Note that the usefulness of INDEX
is not limited to TC
instructions, and applies to every instruction type. Indeed,
there are cases where the indexing operation is used to change the
instruction type itself rather than just the operand.

If K is the A, L, or Q
register (i.e., 16-bit registers),
the value is overflow-corrected before use (though the register itself
is not modified).

The INDEX instruction is
the only instruction which does not reset the extracode flag. The
extracode flag, if set by the EXTEND
instruction, will persist through any number of INDEX instructions. This
is useful, of course, for indexing multi-dimensional objects such as
matrices.

A side-effect of this instruction is that K is rewritten after its value
is interrogated; this means that if K is CYR, SR, CYL, or EDOP,
then it is re-edited.

It is worth noting that the INDEX
instruction is the only case where yaAGC
intentionally implements functionality significantly differently from
the true AGC. The true AGC reacts to the INDEX instruction by computing
the modified following instruction, storing it in the hidden CPU
register B, advancing the program counter past the following
instruction, and then executing the instruction it finds in the B
register; it also allows interrupts to occur prior to executing the
code in the B register. yaAGC,
on the other hand, stores the index value itself in a hidden CPU
register, advances the program counter only to the following
instruction, and then adds the hidden index value to the following
instruction when it actually reaches it; but it does not allow an
interrupt between the INDEX
and the following instruction. (Anyone who objects to this
difference is free to send me a patch.)

When the basic form of INDEX
is used (i.e., without a preceding EXTEND), the special case of K being address 017 octal
is not an INDEX
instruction, refer instead to the RESUME
instruction. The extracode version of INDEX, on the other hand, is
never treated as RESUME.

INHINT

Description:

Disable Interrupts.

Syntax:

INHINT

Operand:

This instruction has no operand.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

1 MCT (about 11.7 µs)

Flags:

The Overflow is not
affected. The Extracode flag remains clear.

Editing:

The CYR, SR, CYL, and EDOP
registers are not edited.

Octal:

00004

Notes:

Globally disables
interrupts. Note that interrupts can be
re-enabled
with the RELINT
instruction.

Interrupts are automatically disabled during various conditions which
prove troublesome, so it is not always necessary to explicitly use RELINT. This
automatically disabling of interrupts is completely different from the
flag manipulated by the INHINT
and RELINT
instructions. These additional interrupt-inhibiting
conditions are described above.

The encoding of this instruction is a special case of TC K, in which K is the address 00004, but the
action is completely unlike other TC
instructions. Note that a normal TC instruction to address 00004
can be manufactured by means of indexing. For example, suppose
that some location K
contains the numerical value 4. The following sequence of
instructions

INDEX KTC A

ends up executing an instruction numerically encoded as 00004 octal,
just is the INHINT
instruction; however, that instruction is treated literally as a TC instruction addressing
location 00004 rather than as an INHINT
instruction.

LXCH

Description:

The "Exchange L and K"
instruction exchanges the value in the L register with a value stored
in erasable memory.

Syntax:

LXCH K

Operand:

K is the label of a memory
location. It must assemble to a 10-bit memory address in erasable
memory.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is not affected
unless K is the
accumulator, in which case it is cleared.
The Extracode flag remains clear.

Editing:

Editing is done upon writing to K, if K is CYR, SR, CYL, or EDOP.

Octal:

22000 + K

Notes:

If
K is the
accumulator or the Q register, then the values will be the full 16 bits
of the sources. Otherwise, source data from 15-bit locations will
be sign-extended to 16 bits prior to storage in L, and the data from L
will be overflow-corrected to 15 bits prior to storage in K.

MASK (or MSK)

Description:

The "Mask A by K"
instruction logically ANDs the contents of a memory location bitwise
into the accumulator.

Syntax:

MASK Kor
MSK K

Operand:

K is the label of a memory
location. It must assemble to a 12-bit memory address.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is set according to
the result of the operation.
The Extracode flag remains clear.

Editing:

CYR, SR, CYL, or EDOP are
unchanged.

Octal:

70000 + K

Notes:

If
K is a 16-bit
register (L or Q), then the full 16 bits of K and A are logically anded and
stored in the accumulator. Otherwise, the source register is 15
bits, and the
accumulator is overflow-adjusted
prior to the operation. The contents of K (which remains unchanged) are
then logically ANDed bitwise to the accumulator, and sign-extended to 16 bits for
storage in the accumulator.

MP

K is the label of a memory
location. It must assemble to a 12-bit memory address.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

3 MCT (about 35.1 µs)

Flags:

The Overflow is cleared.
The Extracode flag is cleared.

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unaffected.

Octal:

70000 + K

Notes:

The
accumulator is overflow-adjusted
prior to the operation. The single-precision (SP) contents
of K are then multiplied
by the SP contents of the
accumulator, resulting in a double-precision (DP) value whose
more-significant word is stored into the accumulator and whose
less-significant word is stored into the L register.

The sign of the resulting DP value is just what would be expected
(i.e., positive when multiplying two factors with the same sign, and
negative when multiplying two factors of opposite signs). If one
of the factors is 0, determining the sign of the result (i.e., +0 or
-0) is a little trickier, and is done according to the following rules:

The result is +0, unless

The factor in the accumulator had been ±0 and the
factor in K had been
non-zero of the opposite sign, in which case the result is -0.

The sign of the value placed in the L register is set to agree with the
sign of the value placed in the accumulator.

It is important to remember that the AGC's SP and DP values represent
numbers between (but not including) -1 and +1. Therefore, the
result of a multiplication is always less than either of the factors
which are multiplied together. While you can work with numbers
larger than 1, such as calculating 2×2, the scaling of the the
factors and the result must be carefully considered. For example,
if you wanted to use the MP
instruction with A and K
each containing the octal value 2 (which would really be the SP value
2×2-14), then you would indeed find a result of 4, but
it would be in L register because it would be part of the DP value
4×2-28 rather than just the integer 4.

For the special case "MP A",
refer instead to the SQUARE
instruction.

MSU

Description:

The "Modular Subtract"
instruction forms a normal signed 1's-complement difference from two
unsigned 2's-complement values.

Syntax:

MSU K

Operand:

K is the label of a memory
location. It must assemble to a 10-bit address in erasable
memory.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is cleared.
The Extracode flag is cleared.

Editing:

Editing is done upon output, if K is CYR, SR, CYL, or EDOP.

Octal:

20000 + K

Notes:

The
contents of K are
subtracted from the
accumulator. Both A and K
are assumed to contain 2's-complement unsigned values prior to
the calculation. The result of the subtraction (in the
accumulator) is converted to a 1's-complement value. If K is the 16-bit Q register,
then 16-bit values are used in the calculation; otherwise, the
overflow-corrected value from A is used, and the result is
sign-extended to 16 bit before storing it back into A.

The point of this instruction is that the CDU counters (see above)
monitor gimbal angles in 2's-complement form, yet the CPU can only
perform general calculations in 1's-complement form. This
instruction thus provides a way to convert differences of gimbal angles
to a form in which further calculation can be done.

A side-effect of this instruction is that K is rewritten (with its
original value) after the calculation; this means that if K is CYR, SR, CYL, or EDOP,
then it is re-edited.

NOOP

Description:

No-operation

Syntax:

NOOP

Operand:

This instruction has no operand.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND
instruction.

This instruction takes time to
execute, but has no other effect. The instruction is assembled
differently, depending on whether it is found in erasable memory or in
fixed memory. In erasable memory, it assembles as "CA A" (i.e, as "load the
accumulator with itself"), while in fixed memory it assembles as a jump
(TCF) to the next
instruction. The latter method is apparently considered
preferable, because it requires less CPU cycles, but cannot be used in
erasable memory because the TCF
instruction can only target locations in fixed memory. Of course,
"CA A" could still be used
in fixed memory by coding it directly rather than using the generic NOOP instruction. (Of
course, these issues really relate to the assembler, yaYUL, rather than the CPU, since
the CPU will execute whatever instructions you throw at it.)

OVSK

Description:

The
"Overflow Skip" instruction skips the next instruction if the
accumulator contains overflow.

Syntax:

OVSK

Operand:

This instruction has no operand.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs).

Flags:

The Overflow is not changed.
The Extracode flag remains clear.

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unaffected.

Octal:

54000

Notes:

This instruction simply proceeds
as normal to the next instruction if the accumulator contains no
overflow, but skips the next instruction and goes to the instruction
after that if the accumulator does contain overflow. The
accumulator itself is unchanged.

This instruction is encoded as "TS
A".

QXCH

Description:

The "Exchange Q and K"
instruction exchanges the value in the Q register with a value stored
in erasable memory.

Syntax:

QXCH K

Operand:

K is the label of a memory
location. It must assemble to a 10-bit address in erasable
memory.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is not affected
unless K is the
accumulator.
The Extracode flag is cleared.

Editing:

Editing is done upon writing to K, if K is CYR, SR, CYL, or EDOP.

Octal:

22000 + K

Notes:

If K is the accumulator or L
register, then the
full 16-bit values of A and Q are swapped. Otherwise, the
overflow-corrected value of Q is stored into K, and the contents of K are sign-extended to 16 bits
before storage in Q.

RAND

Description:

The "Read and Mask" instruction
logically bitwise ANDs the contents of an i/o channel into the
accumulator.

Syntax:

RAND KC

Operand:

KC is an i/o-channel
location. It must assemble to a 9-bit address.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is set according to
the result of the operation.
The Extracode flag is cleared

If the source is the 16-bit L or Q register, then the full 16-bit value
is logically ANDed with A. Otherwise, the 15-bit source is
logically ANDed with the overflow-corrected
accumulator, and the result is sign-extended
to 16
bits before storage in A.

READ

Description:

The "Read Channel KC" instruction
moves the contents of an i/o channel into the accumulator.

Syntax:

READ KC

Operand:

KC is an i/o-channel
location. It must assemble to a 9-bit address.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is set according to
the result of the operation.
The Extracode flag is cleared

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unchanged.

Octal:

00000 + KC

Notes:

Refer
to the list of available i/o channels.
If the source is the 16-bit L or Q register, then the full 16-bit value
is moved into A. Otherwise, the 15-bit source is sign-extended to
16 bits before storage in A.

RELINT

Description:

Enable Interrupts.

Syntax:

RELINT

Operand:

This instruction has no operand.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

1 MCT (about 11.7 µs)

Flags:

The Overflow is not
affected. The Extracode flag remains clear.

Editing:

The CYR, SR, CYL, and EDOP
registers are not edited.

Octal:

00003

Notes:

Enables interrupts.
Note that interrupts can be disabled
with the INHINT
instruction.

The encoding of this instruction is a special case of TC K, in which K is the address 00003, but the
action is completely unlike other TC
instructions. Note that a normal TC instruction to address 00003
can be manufactured by means of indexing. For example, suppose
that some location K
contains the numerical value 3. The following sequence of
instructions

INDEX KTC A

ends up executing an instruction numerically encoded as 00003 octal,
just is the RELINT
instruction; however, that instruction is treated literally as a TC instruction addressing
location 00003 rather than as an RELINT
instruction.

RESUME

Description:

Resume Interrupted Program.

Syntax:

RESUME

Operand:

This instruction has no operand.

Extracode:

This is a basic
instruction, and therefore cannot be preceded by EXTEND.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is not
affected. The Extracode flag is cleared.

Editing:

The CYR, SR, CYL, and EDOP
registers are not edited.

Octal:

50017

Notes:

This instruction is used to
return from an interrupt-service routine. Interrupt vectoring
automatically copies the contents of the current instruction pointer (Z
register) into the ZRUPT register and copies the contents of the memory
location pointed to by the Z register (in other words, the value of the
current instruction) into the BRUPT register. Thus, in order to
resume after interrupt, the RESUME
instruction copies the ZRUPT register back to the Z register and then
causes the instruction stored in the BRUPT register to be executed as if it were stored in memory at
the location pointed to by Z.

This oddity of executing the instruction stored in the BRUPT allows the
programmer to execute an arbitrary instruction upon return from
interrupt, rather than executing the instruction originally appearing
in the program flow. Obviously,
you never want to do this. While theoretically providing
this capability, the BRUPT register
actually exists for the purpose of holding instructions which have been
altered
by a preceding INDEX
instruction, but which have not yet been executed when the interrupt
vectoring occurs. This was necessary in the true AGC, because the
AGC allowed
interrupts to occur between an INDEX
instruction and the instruction
affected by the INDEX
instruction, and thus it was necessary to stored the indexed
instruction somehow, so that it could be executed when normal operation
resumed after the interrupt. yaAGC
does not allow interrupts following an INDEX instruction, and
conflicts between INDEX
and the
interrupt system do not arise in yaAGC.
However, the BRUPT register continues to be supported by yaAGC.

The RESUME instruction is
a special case of INDEX,
in which K is the address
00017, but the action is completely unlike other INDEX instructions. (The
logic of using address 00017, by the way, is that address 00017 is the
BRUPT register; see above.) Note
that a normal INDEX
instruction to address 00017 can itself be manufactured by means of
indexing. For example, suppose that some location K1 contains the numerical value
00000 and K2 is the
address 00017 octal. The instruction sequence

INDEX K2Some
instruction

would not index the final instruction as shown, because "INDEX K2" would be interpreted
as RESUME rather than as
an INDEX
instruction. However, the sequence of instructions

INDEX K1INDEX K2Some instruction

converts "INDEX K2" into a
literal INDEX instruction
rather than allowing it to be reinterpreted as RESUME, and thus does index the
final instruction. On the other hand, and probably more
logically, the first code sequence shown would have worked to index the
instruction if preceded by an EXTEND
instruction, since RESUME
is a basic instruction and not an extracode instruction.

Note that any useful interrupt service routine would modify the A
register (and probably the L, Q, and BB registers). Registers
called ARUPT, LRUPT, QRUPT, and BBRUPT are dedicated to holding
register values during the interrupt service routine, yet the act of
vectoring to the interrupt does not automatically
A, L, Q, and BB to ARUPT, LRUPT, QRUPT, and BBRUPT. Nor does the RESUME instruction restore A,
L, Q, and BB from ARUPT, LRUPT, QRUPT, and BBRUPT.
Consequently, most useful interrupt service routines will need to begin
by saving A, L, Q, and BB, and will need to end by restoring them prior
to the RESUME instruction.

Interrupts are disabled during execution of an interrupt service
routine, independently of the use of the INHINT and RELINT instructions.
Interrupts are re-enabled (if not previously disabled by INHINT) when the RESUME instruction is executed.

RETURN

Description:

The "Return from Subroutine"
instruction.

Syntax:

RETURNorTC Q

Operand:

None

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The
Overflow is not
affected. The Extracode flag is not affected (but is
clear). The Q
register is loaded with 00003 (octal).

Editing:

The CYR, SR, CYL, and EDOP
registers are not edited.

Octal:

00002

Notes:

This instruction is similar to
other TC
instructions (see below), but slightly alters the use of the Q
register. (In fact, its behavior is identical to the hypothetical
instruction "TCF Q",
except that the Q register, at address 00002 octal, is outside the
range of allowable targets for the TCF instruction.) The net
effect of this instruction is simply to load the program counter (the Z
register) with the value found in the Q register. The assumption
is that the value found in the Q register is the return address of a
subroutine, because the TC
instruction normally used to activate a subroutine loads the Q register
with such a return address, so the effect is to return from a
subroutine.

Apollo docs such as Blair-Smith or Savage&Drake present it as an
obvious characteristic of the TC
instruction that "TC Q"
will act as a subroutine return, because "TC Q" transfers program control
to address 2, and what is found at address 2 is another TC instruction containing the
subroutine return address. In fact, this is not what would happen
if "TC Q" acted like all
other TC instructions,
since a normal TC
instruction would actually overwrite the Q register with its own return
address, and thus eliminate any return address previously stored there.

ROR

Description:

The "Read and Superimpose"
instruction
logically bitwise ORs the contents of an i/o channel into the
accumulator.

Syntax:

ROR KC

Operand:

KC is an i/o-channel
location. It must assemble to a 9-bit address.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is set according to
the result of the operation.
The Extracode flag is cleared

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unchanged.

Octal:

04000 + KC

Notes:

Refer
to the list of available i/o
channels. One unobvious use of this instruction is to
overcome the lack of a logical-OR instruction with a memory operand, by
recalling that the L and Q registers are duplicated into i/o-channel
space.

If the source is the 16-bit L or Q register, then the full 16-bit value
is logically ORed with A. Otherwise, the 15-bit source is
logically ORed with the overflow-corrected
accumulator, and the result is sign-extended
to 16
bits before storage in A.

RXOR

Description:

The "Read and Invert"
instruction
logically bitwise exclusive-ORs the contents of an i/o channel into the
accumulator.

Syntax:

RXOR KC

Operand:

KC is an i/o-channel
location. It must assemble to a 9-bit address.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is set according to
the result of the operation.
The Extracode flag is cleared

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unchanged.

Octal:

06000 + KC

Notes:

Refer
to the list of available i/o
channels.
One unobvious use of this instruction is to overcome the lack of a
logical-XOR instruction with a memory operand, by recalling that the L
and Q registers are duplicated into i/o-channel space.

If the source is the 16-bit L or Q register, then the full 16-bit value
is logically exclusive-ORed with A. Otherwise, the 15-bit source
is logically exclusive-ORed with the overflow-corrected accumulator,
and the result is sign-extended
to 16
bits before storage in A.

SQUARE

Description:

"Square the Contents of A"

Syntax:

SQUARE

Operand:

This instruction has no operand.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

3 MCT (about 35.1 µs).

Flags:

The Overflow is cleared (but see
notes).
The Extracode flag is cleared.

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unaffected.

Octal:

70000

Notes:

The accumulator is multiplied by
itself, and the result stored back into the accumulator
(more-significant word) and the L register (less-significant word).

Both Blair-Smith and Savage&Drake state that the results are
"messy" if the accumulator contains positive or negative overflow prior
to the operation.

This instruction assembles as "MP
A".

SU

Description:

The "Subtract"
instruction subtracts a memory value from the accumulator.

Syntax:

SU K

Operand:

K is the label of a memory
location. It must assemble to a 10-bit address in erasable
memory.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is set on the basis
of the output of the calculation.
The Extracode flag is cleared.

Editing:

Editing is done upon output, if K is CYR, SR, CYL, or EDOP.

Octal:

60000 + K

Notes:

The
contents of K are
subtracted from the
accumulator, which is not
overflow-corrected in advance of the calculation. If K is the 16-bit L or Q
register, then the full 16-bit non-overflow-corrected value of K is used in the
calculation. Otherwise, K
is sign-extended to 16 bits before the calculation.

Note that the normal result of AGC arithmetic such as 1-1 is -0.

A side-effect of this instruction is that K is rewritten (with its
original value) after the calculation; this means that if K is CYR, SR, CYL, or EDOP,
then it is re-edited.

TC (or TCR)

Description:

The "Transfer Control" (or
"Transfer Control setting up a Return") instruction calls a subroutine,
first preparing for a later return to the instruction following the TC instruction.

Syntax:

TC KorTCR K

Operand:

K is the label of the start of
the subroutine. It must assemble to a 12-bit memory address,
other than 2, 3, 4, or 6.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

1 MCT (about 11.7 µs)

Flags:

The Overflow is not
affected. The Extracode flag is clear after the
instruction. The Q register is
set up with
the address following the instruction.

Editing:

The CYR, SR, CYL, and EDOP
registers are not edited.

Octal:

00000 + K

Notes:

Nesting of subroutine calls:
The AGC is not stack-based, and does not automatically support nesting
of subroutines. Only one level of subroutine-calls is supported
directly by the CPU. Thus, each use of a TC instruction destroys the
subroutine-return which has been set up by prior TC
instructions. If additional levels are required, the program must
specifically allocate storage and save/restore the subroutine-return
information (from/to the Q register).

Indirect jumps:
The TC
instruction may also be used to accomplish an indirect jump (i.e., a
jump to a computed rather than a hard-coded address) by first loading
the A register with the desired 12-bit address, and then using the
instruction TC A.
Note, though, that this will be an indirect jump to the address, rather than an
indirect call to the address.

The reason the latter trick works is a side-effect of the fact that
the instruction TC K
assembles to the octal value K.
When an instruction like TC A
is executed, the CPU does not actually load the program counter with
the contents of the A register, as we might suppose. Instead, it
literally calls the subroutine at memory location 0 (since the A
register has address 0), and what it finds at address 0 is
coincidentally another TC
instruction. A return from the subroutine is not correctly set up
in this case because the return which is set up is for the TC instruction at address 0,
rather than for the TC A
instruction that started the chain of events. Thus, the Q
register
will contain a value of 1. Incidentally, it should also be
obvious that TC A will
require 2 MCT of CPU time instead of the 1 MCT quoted earlier, since it
actually results in twoTC instructions
being executed.

A similar but subtly different effect occurs when using the "TC Q" instruction to return
from a subroutine. Refer to the RETURN instruction for full
details.

Indirect calls:
For an indirect call, it is necessary to combine an INDEX instruction with a TC instruction. Refer to
the entry for the INDEX
instruction.

The XXALQ, XLQ, RETURN, INHINT, RELINT, and EXTEND instructions are encoded
as TC, for K=0, 1, 2, 3, 4, or 6,
respectively. The XXALQ
and XLQ instructions
really behave exactly as you'd expect from the descriptions I've given
of the TC instruction,
whereas RETURN has
slightly different functionality than other TC instructions, and INHINT, RELINT, and EXTEND are completely different
from other TC
instructions.

TCAA

Description:

"Transfer Control to Address in
A"

Syntax:

TCAA

Operand:

This instruction has no operand.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs).

Flags:

The Overflow is cleared.
The Extracode flag remains clear.

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unaffected.

Octal:

54005

Notes:

The low-order 12 bits of the
accumulator are placed into the Z register (causing a jump). If
positive overflow existed, then the accumulator is loaded with the
value +1, and if there had been negative overflow, the accumulator is
loaded with the value -1. The accumulator is unchanged if there
had been no overflow.

This instruction is encoded as "TS
Z".

TCF

Description:

The "Transfer Control to Fixed"
instruction jumps to a memory location in fixed (as opposed to
erasable) memory.

Syntax:

TCF K

Operand:

K is the label of a memory
location. It must assemble to a 12-bit memory address in fixed
memory. (In other words, the two most significant bits of address
K cannot be 00.)

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

1 MCT (about 11.7 µs)

Flags:

The Overflow is not
affected. The Extracode flag remains clear. The Q register
is unaffected

Editing:

The CYR, SR, CYL, and EDOP
registers are not affected.

Octal:

10000 + K

Notes:

This instruction does not set up a later
return. Use the TC
instruction instead for that.

Indirect jumps:
For an indirect jump, it is necessary to combine an INDEX instruction with a TCF instruction. Refer to
the entry for the INDEX
instruction.

TS

Description:

The "Transfer to Storage"
instruction copies the accumulator into memory ... and so much more.

Syntax:

TS K

Operand:

K is the label of a memory
location. It must assemble to a 10-bit memory address in erasable
memory, but not 0 (the accumulator).

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

The value of the accumulator (overflow-corrected if K is not the 16-bit L or Q
register)
is copied into K, at the
same time being
edited if K is CYR, SR,
CYL, or EDOP.

The action of the TS
instruction differs, depending on whether or not the accumulator had
originally contained overflow:

If the contents of the accumulator contained overflow, then
load the accumulator with +1 or -1, depending on whether the overflow
had been positive or negative, respectively. Also, skip over the
next instruction. (In other words, the program counter is
incremented by 2 rather than by the normal 1.)

If, on the other hand, the contents of the accumulator had
no overflow, retain the contents of the accumulator unchanged and
continue to the next instruction.

WAND

Description:

The "Write and Mask" instruction
bitwise logically-ANDs the contents of the accumulator into an i/o
channel, and vice-versa.

Syntax:

WAND KC

Operand:

KC is an i/o-channel
location. It must assemble to a 9-bit address.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is set according to
the result of the operation.
The Extracode flag is cleared

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unchanged.

Octal:

03000 + KC

Notes:

Refer
to the list of available i/o
channels. The bitwise logical-AND of the accumulator and the
i/o channel is copied into both the accumulator and the i/o channel.

If the destination is the 16-bit L or Q register, then the full 16-bit
value
is logically ANDed with A and stored at both A and K. Otherwise,
the 15-bit destination is logically ANDed with the overflow-corrected accumulator
and stored to K, and the
result is sign-extended to
16
bits before storage in A.

WOR

Description:

The "Write and Superimpose"
instruction bitwise logically-ORs the contents of the accumulator into
an i/o channel, and vice-versa.

Syntax:

WOR KC

Operand:

KC is an i/o-channel
location. It must assemble to a 9-bit address.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is set according to
the result of the operation.
The Extracode flag is cleared

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unchanged.

Octal:

05000 + KC

Notes:

Refer
to the list of available i/o
channels. The bitwise logical-OR of the accumulator and the
i/o channel is copied into both the accumulator and the i/o
channel. One unobvious use of this instruction is to overcome the
lack of a
logical-OR instruction with a memory operand, by recalling that the L
and Q registers are duplicated into i/o-channel space.

If the destination is the 16-bit L or Q register, then the full 16-bit
value is logically ORed with A and stored at both A and K. Otherwise,
the 15-bit destination is logically ORed with the overflow-corrected accumulator
and stored to K, and the
result is sign-extended to
16
bits before storage in A.

WRITE

Description:

The "Write Channel KC" instruction
moves the contents of the accumulator into an i/o channel.

Syntax:

WRITE KC

Operand:

KC is an i/o-channel
location. It must assemble to a 9-bit address.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is set according to
the result of the operation.
The Extracode flag is cleared

If the destination is the 16-bit L or Q register, then the full 16-bit
value
of A is stored into K.
Otherwise, the value is overflow-corrected
before storage.

XCH

Description:

The "Exchange A and K"
instruction exchanges the value in the A register with a value stored
in erasable memory.

Syntax:

XCH K

Operand:

K is the label of a memory
location. It must assemble to a 10-bit memory address in erasable
memory.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

2 MCT (about 23.4 µs)

Flags:

The Overflow is set according to
the result of the operation.
The Extracode flag remains clear.

Editing:

Editing is done upon writing to K, if K is CYR, SR, CYL, or EDOP.

Octal:

56000 + K

Notes:

The
accumulator is stored at address K,
while the value in K is
stored into the
accumulator. If K is the 16-bit L or Q register (or the 16-bit A
register), then the full contents of the registers (including overflow)
are exchanged; otherwise, the value of A is overflow-corrected before
being stored in K, and
the value of K is
sign-extended to 16 bits before being stored in A. If K is
an editing register (CYR, SR, CYL, EDOP), then the value from the
accumulator is edited whilst being stored into K.

XLQ

Description:

The "Execute Using L and Q"
instruction is another name for "TC
L".

Syntax:

XLQ

Operand:

None

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

1 MCT (about 11.7 µs)

Flags:

The Overflow is not
affected. The Extracode flag is not affected (but is
clear). The Q register is set up with the address following the
instruction (but see the notes).

Editing:

The CYR, SR, CYL, and EDOP
registers are not edited.

Octal:

00001

Notes:

This instruction is identical to
"TC L" (see above), but
has been given a different name on the suppostition that the programmer
might like to use it to execute a single basic (i.e., non-extracode)
instruction chosen at runtime, without otherwise interrupting program
flow. The way this would be
done is to:

Place the desired basic instruction into the L register
(address 00001 octal).

Use the XLQ
instruction, which will jump to address 00001 (octal) after
automatically loading a return address into the Q register. Thus,
the CPU will subsequently execute the basic instruction at 00001, and
then will return (as if from a subroutine) because of the TC instruction it finds in the
Q register at address 00002.

If this complete sequence occurs as described, the Q
register will be left containing the value 00003, as is typical for
subroutine returns.

Apollo documentation like Blair-Smith or Savage&Drake quote the
timing of this instruction as 2 MCT, by lumping together the timing of
the XLQ and the
terminating TC
instruction (at address 00002), but obviously the timing of the
instruction differs in no way from any other TC instruction.

Blair-Smith implies that the following sequence is preferred to XLQ in most cases:

INDEX L
OCT 0

XXALQ

Description:

The "Execute Extracode Using A,
L, and Q" instruction is another name for "TC A".

Syntax:

XXALQ

Operand:

None

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND instruction.

Timing:

1 MCT (about 11.7 µs)

Flags:

The
Overflow is not
affected. The Extracode flag is not affected (but is
clear). The Q
register is set up with the address following the instruction (but see
the notes).

Editing:

The CYR, SR, CYL, and EDOP
registers are not edited.

Octal:

00000

Notes:

This instruction is identical to
"TC A"
(see above), but has been given a different name on the suppostition
that the programmer might like to use it to execute a single extracode
instruction chosen at runtime, without otherwise interrupting program
flow. The way this would be
done is to:

Place the EXTEND
instruction (00006 octal) into the A register (address 00000 octal).

Place the desired extracode instruction into the L register
(address 00001 octal).

Use the XXALQ
instruction, which will jump to address 00000 (octal) after
automatically loading a return address into the Q register. Thus,
the
CPU will subsequently execute the EXTEND
at address 00000 and the extracode instruction at 00001, and then will
return (as if from a subroutine) because of the TC instruction it finds in the
Q register at address 00002.

If this complete sequence occurs as described, the Q
register will be left containing the value 00003, as is typical for
subroutine returns.

Apollo documentation like Blair-Smith or Savage&Drake quote the
timing of this instruction as 2 MCT, by lumping together the timing of
the XXALQ and the
terminating TC
instruction (at address 00002), but obviously the timing of the
instruction differs in no way from any other TC instruction.
Furthermore, there is no necessity whatever that the instruction at
address 00000 be an EXTEND
instruction, since loading the EXTEND
instruction into the A register is done by the program anyway, and is
not automatically done by the XXALQ
instruction.

Blair-Smith recommends that this functionality is usually provided
better by the sequence:

EXTEND
INDEX L
OCT 0

ZL

Description:

The
"Zero L" instruction writes a value of 0 to the L register.

Syntax:

ZL

Operand:

This instruction has no operand.

Extracode:

This is not an extracode, and
therefore cannot be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs).

Flags:

The Overflow is unaffected.
The Extracode flag remains clear.

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unaffected.

Octal:

22007

Notes:

This assembles as an LXCH to address 7. Recall
that register 7 is hardwired to 0 rather than to a memory location, and
hence always contains the value 0.

ZQ

Description:

The
"Zero Q" instruction writes a value of 0 to the Q register.

Syntax:

ZQ

Operand:

This instruction has no operand.

Extracode:

This is an extracode, and
therefore must be preceded by an EXTEND
instruction.

Timing:

2 MCT (about 23.4 µs).

Flags:

The Overflow is unaffected.
The Extracode flag is cleared.

Editing:

The editing registers CYR, SR,
CYL, or EDOP are unaffected.

Octal:

22007

Notes:

This assembles as a QXCH
to address 7. Recall that register 7 is hardwired to 0 rather
than to
a memory location, and hence always contains the value 0.

Pseudo-Operations

=

Used to assign an alternate name to data:

Name
= Data

The Data may be either an
octal or decimal integer constant or else another symbolic name. The Name has the same constraints as a
program label: any 8 characters, excluding '#' or leading '$'.

See also the EQUALS
pseudo-op, which does essentially the same thing. Page 95 of the
program listing for Luminary
131 attempts to describe the difference between the two. Its says that
"=" is used to assign an alternate name to data ("logical
equivalence"), whereas EQUALS
implies sharing of resources—such as reuse of the same memory
location for two different variables. While interesting from the
standpoint of maintainers of the code, there is no actual behavioral
difference between the two pseudo-operations, as far as I can determine.

An optional field after Data
may be added, separated by whitespace, consisting of a positive or
negative octal constant. (For example, "MYNAME = YOURNAME +1". ) This
essentially means to add the number to Data.

One sometimes sees "=" used with the Data
field, but without a Name. I
believe that statements of this kind are placed in the source code
entirely for documentation purposes, and do not have any effect. An
example would be something like this:

In this example, "= 4" has
been added simply for maintenance purposes to let everyone know that it
has been excluded by design rather than by accident.

1DNADR, -1DNADR

This is a "special downlink opcode" that appears only in downlink lists
rather than in normal instruction flow or in interpretive instruction
flow. Basically, these instructions as a group are used to form a
kind of script describing the data which is supposed to be transmitted
in a downlink packet. A downlink list consists of a sequence of
these special downlink opcodes (1DNADR,
2DNADR, 3DNADR, 4DNADR, 5DNADR, 6DNADR, DNCHAN, or DNPTR), terminated by the
complement of one of them (symbolized by -1DNADR, -2DNADR, -3DNADR, -4DNADR, -5DNADR, -6DNADR, -DNCHAN, or -DNPTR). The basic syntax
is as follows:

1DNADR
AddressLabelor-1DNADR AddressLabel

The specific interpretation is that the two words located at AddressLabel
should be transmitted. The "instruction" is actually treated by
the assembler the identically to the ECADR pseudo-op. The
terminating form of the instruction (-1DNADR) is logically
complemented.

2DEC, 2DEC*

This pseudo-op can be used to embed a double-precision (DP) constant.
See also the DEC
pseudo-op. The basic syntax is as follows:

2DECDoublePrecisionNumber

This may be preceded by a label (starting in column 1) and/or followed
by a comment. The DoublePrecisionNumber
is as defined earlier (with optional scaling factors). Recall that the
DP format represents fractional positive or negative numbers, so only
numbers less than +1 and greater than -1 are theoretically acceptable.
An example would be "2DEC +.4928".
Since this assembles as a DP number, two 15-bit words are created, and yaYUL's internal position counter is
incremented by two.

If the DoublePrecisionNumber
contains no decimal point, the number is assembled in integer format
rather than in DP format if no "En" or "Bn" option is present. Or to
look at it a little differently, an implied B-28 option is added.

Finally, a variation of which the following is an example appears very
frequently: "2DEC* +656.0 E-4 B3*".
This variation assembles to the same octal words as if the '*'
characters were not present. The purpose is apparently to allow
the operand fields to extend into the columns reserved by YUL for comments. In other
words, the comment field begins only after the trailing '*'.
Recall that the original assembler, YUL,
processed source code supplied on punch cards, and that the syntax of
the source code was column-aligned. However, yaYUL allows free-form source code
which is not column-aligned, with comments that are delimited by the
special character '#', and hence has no need for this particular
construct. (Thanks to Julian Webb for this explanation!)

2DNADR, -2DNADR

This is a "special downlink opcode"; refer to the comments for the 1DNADR pseudo-op. The
basic syntax is as follows:

2DNADR
AddressLabelor-2DNADR AddressLabel

The specific interpretation is that the four words located at AddressLabel
should be transmitted. The "instruction" is actually treated by
the assembler the identically to the ECADR pseudo-op, except that
the value 04000 (octal) is added. The terminating form of the
instruction (-2DNADR) is
logically complemented.

2FCADR

This pseudo-op can be used to embed a double-word constant, to be used
later by the DTCF
instruction.
The basic syntax is as follows:

2FCADRProgramLabel

The double-word which is formed contains a first word that is suitable
for loading into the FB register (i.e., it contains the
suitably-shifted fixed-bank number of the ProgramLabel),
while the second word contains a value suitable for loading into the Z
register. In other words, the net effect of the DTCF instruction below is to
jump to MYLABEL, changing
fixed banks as appropriate.

MYJUMP
2FCADR MYLABEL
.
.
.
EXTEND
DCA MYJUMP
DTCF
.
.
.
MYLABEL

Of course, this method cannot by itself account for jump-destinations
in memory banks 40-43, since this would require manipulation of the
superbank bit in i/o channel 7 in addition to manipulations of the FB
registers. Thus, the superbank bit in channel 7 must be
explicitly set in advance if the destination is in banks 40-43.

Actually, the rules for forming the two embedded words is somewhat more
complicated, as the discussion above was simplified in order to
illustrate a typical use. The first word contains not only FB's
bank bits (in bit positions 15-11), but also the 10-bit offset into
that bank (in bits 10-1). (The latter bits are automatically
discarded when loaded into FB.) If the address is in fixed-fixed
memory, then either bank 2 or bank 3 is chosen, as appropriate.
The second word contains the same value that would have
resulted from assembling the instruction "TC ProgramLabel".

3DNADR, -3DNADR

This is a "special downlink opcode"; refer to the comments for the 1DNADR pseudo-op. The
basic syntax is as follows:

3DNADR
AddressLabelor-3DNADR AddressLabel

The specific interpretation is that the six words located at AddressLabel
should be transmitted. The "instruction" is actually treated by
the assembler the identically to the ECADR pseudo-op, except that
the value 10000 (octal) is added. The terminating form of the
instruction (-3DNADR) is
logically complemented.

4DNADR, -4DNADR

This is a "special downlink opcode"; refer to the comments for the 1DNADR pseudo-op. The
basic syntax is as follows:

4DNADR
AddressLabelor-4DNADR AddressLabel

The specific interpretation is that the eight words located at AddressLabel
should be transmitted. The "instruction" is actually treated by
the assembler the identically to the ECADR pseudo-op, except that
the value 14000 (octal) is added. The terminating form of the
instruction (-4DNADR) is
logically complemented.

5DNADR, -5DNADR

This is a "special downlink opcode"; refer to the comments for the 1DNADR pseudo-op. The
basic syntax is as follows:

5DNADR
AddressLabelor-5DNADR AddressLabel

The specific interpretation is that the ten words located at AddressLabel
should be transmitted. The "instruction" is actually treated by
the assembler the identically to the ECADR pseudo-op, except that
the value 20000 (octal) is added. The terminating form of the
instruction (-5DNADR) is
logically complemented.

6DNADR, -6DNADR

This is a "special downlink opcode"; refer to the comments for the 1DNADR pseudo-op. The
basic syntax is as follows:

6DNADR
AddressLabelor-6DNADR AddressLabel

The specific interpretation is that the twelve words located at AddressLabel
should be transmitted. The "instruction" is actually treated by
the assembler the identically to the ECADR pseudo-op, except that
the value 24000 (octal) is added. The terminating form of the
instruction (-6DNADR) is
logically complemented.

BANK

This directive is used in two different ways. When followed by a number
such as

BANK
5

it means that yaYUL's internal
location counter should be repositioned to the first unused location of fixed-memory
bank 5. Only fixed-memory banks (and not erasable-memory banks)
are supported in this way. The bank numbers range from 0 to 43 (octal)
. (Bank numbers 0--37 octal are in "super-bank 0", while bank numbers
40-43 octal are in "super-bank 1".)

If BANK is used without
the numerical operand,

BANK

then the location counter is moved to the first unused location in the current fixed-memory bank.

BLOCK

This directive is similar to BANK.
However, it applies only to "unswitched erasable" memory, which is
BLOCK 0 (address 0), and to "fixed-fixed" memory, which contains BLOCK
2 (address 4000 octal) and BLOCK 3 (address 6000 octal). From the
pattern of addresses, I suppose it this directive might be generalized
to any multiple of octal 2000, but I have not chosen to do so.

BNKSUM

yaYUL simply silenty discards
this directive. It
appears not to have any effect other than printing a count of the
number of words used in the current memory bank. A table appears at the
end of the yaYUL output
listing which contains this information anyway. It very likely
had the side-effect of forcing YUL
to add the bugger word to the end of the memory bank. (yaYUL automatically adds the bugger
word, hence no particular command is needed to force it to do so.)

BNKSUM

COUNT, COUNT*

yaYUL simply silently discards
these directives. Typically one of them
appeared on the next line of source-code following a BANK pseudo-op. Their original
intent (in YUL) seems to have
been to indicate that the code which
followed it should be included within the word counts for a specific
subroutine. (In other
words, so that you could get a table in the
output listing saying that function X used Y words of memory. yaYUL generates no such tables.) The
directive did not seem to generate binary code, nor to affect
generation of binary code.

COUNT
Banknum/ProgramLabelor
COUNT* $$/ProgramLabel

The latter form does not require a memory-bank number because it
assumes the current bank (as defined by the preceding BANK pseudo-op).

DEC, DEC*

This pseudo-op can be used to embed a single-precision (SP)
constant. Except for the fact that it assembles as one 15-bit
word rather than as two 15-bit words, it is essentially the same as the
2DEC pseudo-op.

DNCHAN, -DNCHAN

This is a "special downlink opcode"; refer to the comments for the 1DNADR pseudo-op. The
basic syntax is as follows:

DNCHAN
IoChannelNumberor-DNCHAN IoChannelNumber

The specific runtime interpretation, I believe, is that the value of
the selected i/o channel is read and inserted in the transmission
stream. The "instruction" is compiled as the channel number,
except that the value 34000 (octal) is added. The terminating
form of the instruction (-DNCHAN)
is logically complemented.

DNPTR, -DNPTR

This is a "special downlink opcode"; refer to the comments for the 1DNADR pseudo-op. The
basic syntax is as follows:

DNPTR
AddressLabelor-DNPTR AddressLabel

The specific runtime interpretation is that AddressLabel
points to another downlink list. (I suppose that this other
downlink
list is inserted in the transmission stream, but I'm not really
sure.)
The "instruction" is actually treated by the assembler the identically
to the ADRES pseudo-op,
except that the value 30000 (octal) is added. The terminating
form of the instruction (-DNPTR)
is logically complemented.

EBANK=

This pseudo-op generates no code. Rather, it changes the assumptions of
the assembler as to the erasable bank being used. In most cases, the
assembler uses knowledge of the current erasable bank only for
generating error messages about addressing incompatibilities. However,
it also uses the information directly for the BBCON, 2BCADR, and 2CADR pseudo-ops.

Normally, the `EBANK='
setting persists until a new EBANK=
(or a SETLOC with a
target in erasable memory) is encountered. However, if a BBCON, 2BCADR, or 2CADR pseudo-op immediately
follows the EBANK= line,
then the E-bank assumption applies only to that one pseudo-op and then
immediately reverts to its previous setting.

EQUALS

Used to assign a symbolic name to a value:

SymbolEQUALSValue

The Value may be either an
octal or decimal integer, or else another symbolic name. The Symbol has the same constraints as
a program label: any 8 characters, excluding '#' or leading '$'.

Note that if the Value is a
symbolic name, it need not have been previously defined. In other
words, forward-references are allowed: the operand symbol may be
defined later in the source code. The assembler will make as many
passes as necessary to resolve all symbol references.

See also the "=" pseudo-op, which has similar functionality. Page 95 of
the program listing for Luminary
131 attempts to describe the difference between the two. Its says that
"=" is used to assign an alternate name to data ("logical
equivalence"), whereas EQUALS
implies sharing of resources—such as reuse of the same memory
location for two different variables.

An optional field after Value
may be added, separated by whitespace, consisting of a positive or
negative octal constant. For example, "MYNAME EQUALS YOURNAME +1".
This essentially means to add the number to Value.

One sometimes sees EQUALS
used with the Symbol field,
but without the Value field.
The effect of this is to use the value of the assembler's current
internal location counter.

ERASE

Skips a location in erasable memory:

ERASE

In other words, particularly if preceded by a label (in column 1),
allocates a word of memory in which to store a variable. Alternately,
we can use the syntax:

ERASEn

where n is an octal or
decimal integer constant. This skips n+1
bytes of erasable memory.

The following syntax is also accepted:

ERASEm - n

This isn't really like the other ERASE
commands, as it operates identically to "SETLOC m" and has no effect on the
assembler's location counter. The net
effect is to set the label of the line (if any) to m. The n operand
is discarded, and is present principally for documentation purposes.

MEMORY

MEMORYm - n

Like "ERASE m - n", this operates
identically to "SETLOC m" and has no effect on the
assembler's location counter. The net
effect is to set the label of the line (if any) to m. The n operand
is discarded, and is present principally for documentation purposes.

OCT

This pseudo-op can be used to embed an octal constant. In other words,

OCTOctalNumber

simply places a 15-bit word equal to OctalNumber
(which is a number of up to 5 digits, using only the digits 0--7)
directly into the byte stream.

SETLOC

This pseudo-op changes the value of the position counter being used by
the assembler:

SETLOCAddress

In other words, the next instruction or pseudo-op assembled will be
placed at address Address.
The Address field is either
an octal constant, or else a symbol which the assembler can resolve as
an address. If the Address is
numeric, it will be treated as a pseudo-address—i.e., it must be in
the range 0-117777 (octal)—and the assembler will determine
automatically what memory space to place it in. In most cases this
determination is unambiguous, but in some cases there are two possible
choices. For example, address 4000 (octal) is in both
"fixed-fixed" memory and in bank 2 of "common fixed" memory. In yaYUL, SETLOC will always choose
""fixed-fixed over ""common-fixed memory, and it will always choose
"unswitched "erasable over "switched "erasable memory. Very often
SETLOC will be followed
(on the next line of the source file) by the BANK pseudo-op when Address is the beginning of a bank
in fixed-memory.

SUBRO

This is simply discarded by yaYUL.
Its original purpose (in YUL)
was seemingly to help maintain a list of subroutines for later
printout, for documentation purposes. yaYUL maintains no such list and
prints no such report.

SUBRO
SubroutineName

Interpreter Instruction
Set

(Sorry, but this section is very incomplete right now. So far
I've only recorded the information about binary formats I worked out
while writing the yaYUL assembler.)

Source Code and Encoding

Interpreter source code is written in a very odd way, reflecting the
underlying implementation in which two interpreter opcodes are normally
encoded within a single word of memory, although sometimes only a
single interpreter opcode is stored within a word of memory.
Interpreter opcodes take either 0, 1, or 2 operands, and these operands
are written on the lines following
the opcodes. Therefore, a source line representing one or two
interpreter opcodes may be followed by 0 to 4 lines containing the
operands (each of which assembles to a single word of memory).
For example, here are some typical cases:

In general, each opcode is numerically encoded as a 7-bit value.
The first opcode is stored in bits 7-1 of the 15-bit word, while the
second opcode is stored in bits 14-8. More precisely, the 7-bit
codes from the following table are incremented,
and then stored in bit fields 7-1 or 14-8 (the fields remain 0 if there
is no opcode stored there), and then the entire word is logically
complemented. For example, consider

VSL1
VAD

VSL1 is coded as 004 octal, and VAD is coded as 121 octal, so the
entire line is encoded as the logical complement of
200*(121+1)+(4+1)=24405, or 53372.

The exceptions to this scheme are the STCALL, STODL, STORE, and STOVL instructions, which have
either one or two operands, but the first operand is written (and
encoded) on the same line as
the opcode. For example:

STOVL
OPERAND1OPERNAD2

Another special case is if one of these four instructions is
immediately preceded by the STADR
instruction. In this case the entire 15-bit word is logically
complemented.

Encoding of operands is also pretty tricky. Rather than repeat
these tricky rules over and over in the opcode table below, I'll define
the encoding formats here and give them convenient identifying names.

Operands in erasable memory. We'll
represent the numerical values created by the encoding as e(X), which is defined as
follows:

Unswitched erasable: e(X) = X

Switched erasable: e(X)
= 0400 * ErasableBankNumOfX +
(X - 1400)

(All of the constants are octal.) For example, E7,1764 would
encode to 0400*7+(1764-1400)=3764. Incidentally, note that since
unswitched erasable memory can alternately be addressed as switched
erasable memory in banks E0, E1, or E2, the second rule actually
suffices for all cases.

Operands in fixed memory.
These encode to 15-bit values, and can be used to represent any address
in fixed-memory banks 00-37. (Superbanks 40-43 are not accesible
to the interpreter.) I'll designate this rule by f(X), defined as follows:

Fixed-fixed: f(X) = X

Common-fixed: f(X) =
02000 * FixedBankNumOfX + (X
- 02000)

Again, all of the constants are octal, and because of the way
fixed-fixed memory corresponds to common-fixed memory banks 02 and 03,
the second rule actually suffices for all cases.

Operands representing
switches.Luminary
and Colossus each employ a
large array of single-bit values ("switches" or "flagwords") to store
status
information of various kinds. Some of the interpreter
instructions operate on these switches. The flagwords
themselves are identified by the switch word in which they reside (0-77
octal), and by the bit-position within the word (0-16 octal). The
encoding of these flagword operands is more complex than that of pure
addresses. If the bits of the encoded word are identified as 0WWWWWWNNNNBBBB, then WWWWWW is the switch-word
number and BBBB is the
bit-position number within the word. The field NNNN is the "operation number",
and therefore includes part of the information we'd probably expect to
find in the opcode, thus resulting in the situation that many of the
operations on switch operands actually have the same numerical
opcode. The values for the NNNN
fields are listed in the opcode table below.

In the table below, items in brackets are optional. For example, [Y] might indicate an optional
operand (called "Y").

STCALL

Description:

Syntax:

STCALL X
Y

Operand(s):

X is in unswitched erasable or
in the current erasable bank.

Timing:

Octal:

34001 + e(X)

Notes:

STODL

Description:

Syntax:

STODL X
[Y]

Operand(s):

X is in unswitched erasable or
in the current erasable bank.

Timing:

Octal:

14001 + e(X)

Notes:

STORE

Description:

Syntax:

STORE X

Operand(s):

X is in unswitched erasable or
in the current erasable bank.

Timing:

Octal:

00001 + e(X)

Notes:

STOVL

Description:

Syntax:

STOVL X
[Y]

Operand(s):

X is in unswitched erasable or
in the current erasable bank.

Timing:

Octal:

24001 + e(X)

Notes:

I/O Channels

The following information is largely taken from Luminary and Colossus
program listings.

Address

Luminary

Colossus

0

Not used.

Same as Luminary

1

Duplicate of the L register
(unswitched-erasable address 1).

Same as Luminary

2

Duplicate of the Q register
(unswitched-erasable address 2).

Same as Luminary

3

A 15-bit 1's-complement counter
called HISCALAR, which is incremented every time SCALER1 overflows
(i.e., every 5.12 seconds). It will thus overflow and return to 0
after about 23.3 hours.

4

A 15-bit 1's-complement counter
called LOSCALAR, which is incremented every 1/3200 second. It
will thus overflow every 5.12 seconds. Upon overflow counter
SCALER2 is incremented.

5

PYJETS: An output channel
used by the reaction control system (RCS) for pitch control. Only
bits 1-8 are used.

6

ROLLJETS: An output
channel used by the reaction control system (RCS) for roll
control. Only bits 1-8 are used.

7

SUPERBNK: Bit 7 is the
superbank bit (for memory-bank control). Only bits 5-7 are used,
and they are used as additional memory-bank selection bits.
However, the only combinations of bits 5-7 which are actually
meaningful are 0XX ('X' meaning "don't care") and 100. In the
former case, only memory banks 00-37 (octal) are accessible, by means
of the FB register. In the latter case, however, banks 30-33 are
replaced by banks 40-43. (In yaAGC,
banks 34-37 also become inaccessible.) Other bit combinations
would have been meaningful in the SUPERBNK channel if more core-ropes
had been added to the AGC.

Same as Luminary.

Instruction-Set
Summary Tables

Numerical Summary of the
CPU Instruction Set

Mnemonic

Octal

Extracode?

Address
Constraint

Brief
Description

TC
K
or TCR K

00000
+ K

No

K is 12-bit address.

Call
subroutine K.

XXALQ

00000

No

Special
case of TC K with K=00000

XLQ

00001

No

Special
case of TC K with K=00001

RETURN

00002

No

Special
case of TC K with K=00002

RELINT

00003

No

Special
case of TC K with K=00003

Enable
interrupts.

INHINT

00004

No

Special
case of TC K with K=00004

Disable
interrupts.

EXTEND

00006

No

Special
case of TC K with K=00006

Interpret
the next instruction as
Extracode.

CCS
K

10000
+ K

No

K is 10-bit address.

Count,
compare, and skip.

TCF
K

10000
+ K

No

K is 12-bit address.
Two most-significant bits of K
not 00.

Jump to
address K.

NOOP

10001
+ Current address

No

Special
case of TCF K.Used only in fixed memory.

No-op.

DAS
K

20001
+ K

No

K is 10-bit address.

Double-precision
integer addition.

DDOUBL

20001

No

Special
case of DAS K with K=00000

Double the (A,L) register pair.

LXCH
K

22000
+ K

No

K is 10-bit address.

Exchange
contents of L and K.

ZL

22007

No

Special
case of LXCH K with K=00006

INCR
K

24000
+ K

No

K is 10-bit address.

Increment
value stored at address K.

ADS
K

26000
+ K

No

K is 10-bit address.

Add
contents of A to value at
address K.

CA
K
or CAF K (fixed memory)
or CAE K (erasable memory)

30000
+ K

No

K is 12-bit address.

Copy K to A.

NOOP

30000

No

Special
case of CA K with K=00000.Used only in erasable memory.

No-op.

CS
K

40000
+ K

No

K is 12-bit address.

Copy -K to A.

COM

40000

No

Special
case of CS K with K=00000

INDEX
K

50000
+ K

No

K is 10-bit address.

Indexes the
next instruction.
(I.e., adds the contents of K
to the next instruction before executing it, but without altering the
memory location at which the next instruction is stored.)

RESUME

50017

No

Special
case of INDEX K with K=00017

Resume
interrupted program. (The contents of ZRUPT are loaded into the
program counter, and the contents of BRUPT are executed as if they
were the instruction actually stored at the new program counter.)

DXCH
K

52001
+ K

No

K is 10-bit address.

Double-precision
exchange of the contents of K,K+1 with A,L.

DTCF

52005

No

Special
case of DXCH K with K=00004

DTCB

52006

No

Special
case of DXCH K with K=00005

TS
K

54000
+ K

No

K is 10-bit address.

Copy the
contents of A to location
K.

OVSK

54000

No

Special
case of TS K with K=00000

TCAA

54005

No

Special
case of TS K with K=00005

XCH
K

56000
+ K

No

K is 10-bit address.

Exchange
the contents of A and
location K.

AD
K

60000
+ K

No

K is 12-bit address.

Add the
contents of location K to
A.

DOUBLE

60000

No

Special
case of AD K with K=00000

Doubles the
A register.

MASK
K

70000
+ K

No

K is 12-bit address.

Bitwise
boolean AND the contents of K
to A.

READ
KC

00000
+ KC

Yes

KC is 9-bit i/o channel.

Read i/o
channel KC to A.

WRITE
KC

01000
+ KC

Yes

KC is 9-bit i/o channel.

Write A to i/o channel KC.

RAND
KC

02000
+ KC

Yes

KC is 9-bit i/o channel.

Read i/o
channel KC and bitwise
boolean AND it to A.

WAND
KC

03000
+ KC

Yes

KC is 9-bit i/o channel.

Read i/o
channel KC and bitwise
boolean AND it to A, then
write it to i/o channel KC.

ROR
KC

04000
+ KC

Yes

KC is 9-bit i/o channel.

Read i/o
channel KC and bitwise
boolean OR it to A.

WOR
KC

05000
+ KC

Yes

KC is 9-bit i/o channel.

Read i/o
channel KC and bitwise
boolean OR it to A, then
write it to i/o channel KC.

RXOR
KC

06000
+ KC

Yes

KC is 9-bit i/o channel.

Read i/o
channel KC and bitwise
boolean exclusive-OR it to A.

EDRUPT
KC

07000
+ KC

Yes

KC is 9-bit i/o channel.

(For
machine checkout only. I'm not sure what it's supposed to do.)

DV
K

10000
+ K

Yes

K is 10-bit address.

Divide the
double-precision integer value in (A,L)
by the contents of K,
putting the quotient in A
and the remainder in L.

BZF
K

10000
+ K

Yes

K is 12-bit address.
Two most-significant bits of K
not 00.

MSU
K

20000
+ K

Yes

K is 10-bit address.

QXCH
K

22000
+ K

Yes

K is 10-bit address.

ZQ

22007

Yes

Special
case of QXCH K with K=00007

AUG
K

24000
+ K

Yes

K is 10-bit address.

DIM
K

26000
+ K

Yes

K is 10-bit address.

DCA
K

30001
+ K

Yes

K is 12-bit address.

DCS
K

40001
+ K

Yes

K is 12-bit address.

DCOM

40001

Yes

Special
case of DCS K with K=00000

INDEX
K

50000
+ K

Yes

K is 12-bit address.

SU
K

60000
+ K

Yes

K is 10-bit address.

BZMF
K

60000
+ K

Yes

K is 12-bit address.
Two most-significant bits of K
not 00.

MP
K

70000
+ K

Yes

K is 12-bit address.

SQUARE

70000

Yes

Special
case of MP K with K=00000

Instruction
Map

Code=0

Code=1

Code=2

Code=3

Code=4

Code=5

Code=6

Code=7

Basic
Instructions

QC=0

TC

CCS

DAS

CA

CS

INDEX

AD

MASK

QC=1

TCF

LXCH

DXCH

QC=2

INCR

TS

QC=3

ADS

XCH

Extracodes

QC=0

PC=0

READ

DV

MSU

DCA

DCS

INDEX

SU

MP

PC=1

WRITE

QC=1

PC=2

RAND

BZF

QXCH

BZMF

PC=3

WAND

QC=2

PC=4

ROR

AUG

PC=5

WOR

QC=3

PC=6

RXOR

DIM

PC=7

EDRUPT

Implied-address-code instructions
such as RELINT, INHINT, etc., are not shown.

Unprogrammed Sequences

As mentioned earlier in the CPU
Architecture section, the AGC provides instruction-like entities
called "unprogrammed sequences" which are activated automatically to
modify counter registers upon receiving the proper input electrical
signals, but which cannot be explicitly used under program
control. (Hence, the term "unprogrammed".) In some
cases, the action of modifying the counter also automatically outputs
an electrical signal, independent of the i/o channels. Though
outputting these signals do not really occur as part of what the Apollo
docs refer to as unprogrammed sequences, I include them here anyway,
for reference.

The types and behaviors of these unprogrammed sequences are useful to
know, even if they cannot explicitly be activated by software.

Name

Timing

I/O

Applicable
Counters

Description

PINC

1 MCT (about 11.7 µs)

Input to CPU

TIME1-TIME5

Add +1 in 1's-complement fashion
to a counter. If there is overflow, then the counter is reset to
+0. Note that PINCs for TIME1-TIME5 are triggered by oscillators
(or in the case of TIME2, from overflow of TIME1) and need no external
signal.

PCDU

1 MCT (about 11.7 µs)

Input to CPU

CDUX, CDUY, CDUZ, OPTX, OPTY

Add +1 in 2's-complement fashion
to a counter.

MINC

1 MCT (about 11.7 µs)

Input to CPU

Add -1 in 1's-complement fashion
to a counter. If there is overflow, then the counter is reset to
-0.

MCDU

1 MCT (about 11.7 µs)

Input to CPU

CDUX, CDUY, CDUZ, OPTX, OPTY

Add -1 in 2's-complement fashion
to a counter.

DINC

1 MCT (about 11.7 µs)

Input to CPU

TIME6

This sequence not only counts,
but also emits a signal to peripheral hardware, as follows:

If the counter is already at ±0, then do not
increment or decrement. Output the signals ZOUT and DINC-request.

Whereas if the counter greater than +0 then decrement by
1. Also, output the POUT signal and the DINC-request signal.

Whereas if less than -0 then increment by 1. Also,
output the MOUT signal and the
T6RUPT-enable flag at bit 16 of i/o channel 13 (octal).

SHINC

1 MCT (about 11.7 µs)

Input to CPU

INLINK, RNRAD, OUTLINK

Shift the counter left. If
there is overflow, then set an interrupt request, presumably for
the UPRUPT, RADAR RUPT, or DOWNRUPT interrupt (whichever is appropriate).
This is used for accumulating a 0-bit from a serial-data stream into a
parallel word of data.

SHANC

1 MCT (about 11.7 µs)

Input to CPU

INLINK, RNRAD, OUTLINK

Shift the counter left and
increment by +1. If there is overflow, then set an
interrupt request, presumably for the UPRUPT, RADAR RUPT, or
DOWNRUPT interrupt (whichever is appropriate). This is
used for accumulating a 1-bit from a serial-data stream into a parallel
word of data.

INOTRD

1 MCT (about 11.7 µs)

Input to CPU

None

INOTLD

1 MCT (about 11.7 µs)

Input to CPU

None

FETCH

2 MCT (about 23.4 µs)

Input to CPU

None

STORE

2 MCT (about 23.4 µs)

Input to CPU

None

GOJ

2 MCT (about 23.4 µs)

Input to CPU

None

Jump to location 04000
octal. This sequence is used to force a reset of the AGC. Additionally, the CPU's internal
flip-flop indicating that an ISR is in progress is reset (as if a RESUME instruction had been
executed), since otherwise interrupts would be permanently inhibited if
the GOJ had occurred
during an ISR.

TCSAJ

2 MCT (about 23.4 µs)

Input to CPU

None

POUT

n/a

Output from CPU

TIME6

Indicates that the result of a DINC unprogrammed sequence is
positive.

MOUT

n/a

Output from CPU

TIME6

Indicates that the result of a DINC unprogrammed sequence is
negative.

ZOUT

n/a

Output from CPU

TIME6

Indicates that the result of a DINC unprogrammed sequence is
plus zero or minus zero.

Modification History

v0.50

First controlled version of this
document. It contains a complete specification of the instruction
set (though possibly with errors or uncertainties).

v0.51

Problems discovered while
implementing v0.50 in agc_engine.c have been corrected:

Various spelling and grammatical errors.

In the Add instruction, the special case "CA A" has been replaced by "AD A".

The "DXCH L"
instruction was clarified.

In the Divide instruction, the phrase "do not" had been
omitted from "The signs of the dividend words stored in A and L do not
necessarily agree with each other."

In the BZF
and BZMF instruction, it
is assumed that an overflow-corrected version of the accumulator is
used for the test.

For the AUG
instruction, the wording inadvertently implied that the accumulator
would always contain plus or minus overflow after the operation.

Over-zealous pasting from AUG into DIM (regarding counter
interrupts) has been corrected.

The wording of description of interrupts in INCR and AUG has been changed to avoid
the inference that only
TIME1-TIME6 are affected.

06/05/04: EDRUPT
and GOJ now indicate that
conditions which would inhibit further interrupts are cleared.

Also:

07/05/04: In DXCH,
the
stipulation that the accumulator is not
overflow-corrected has been
changed to indicate the the accumulator is overflow-corrected.

07/07/04: In DV,
the
sign of the output L register has been adjusted in one case where it
had previously been inconsistent.

07/08/04: In BZF
and BZMF, the use of
overflow has
been completely changed. Instead of saying that the
overflow-corrected accumulator is used, I now say that positive or
negative overflow blocks BZF
from jumping, while positive overflow (but not negative overflow)
blocks BZMF from jumping.

07/09/04: In DAS
and DDOUBL, the signs of
the
less-significant and more-significant words of the result are no longer
normalized to match. Also, DAS no longer states that the
accumulator is overflow-corrected prior to the addition.

07/09/04: In DV
with
dividend and divisor equal in magnitude, the remainder is (and has
been) the divisor. But we no longer say in this case that the
sign of the remainder is adjusted to match the dividend.

07/10/04: In MSU,
it is now noted that overflow is cleared, rather than stating that it
is set on the basis of the result.

07/10/04: In DCA
and DCS, the special
cases of "DCA L" and "DCS L" are covered.

0713/04: The 2FCADR
pseudo-op has been added. The Q register is now described as
having 16 bits rather than 15 bits. Many changes to the
descriptions within the instructions of overflow handling have ensued,
in (for example) CCS, ADS, AD, SU, XCH, LXCH, QXCH, etc.

07/17/04: The L register is now 16 bits as
well. Again, extensive related descriptive changes have been
made, including (of especial note) that the final contents of L are
overflow-corrected with the DXCH,
DCA, and DCS instructions.