Contents


Blocks for All Occasions

The design of the AGC went through several stages, which for the sake of this discussion we'll call "blocks".  However, the different block designs, while similar in many ways, were quite different in others.  Rather than try to cover all of them on this one page, the different blocks are covered in separate pages:

However, this page confines itself just to the Block 2 design.


Introduction

This manual covers AGC4 assembly-language, as accepted by the yaYUL assembler program.  Additionally, AGC4 interpreter-language is covered.

The original Apollo documentation from which the information presented here was derived, in roughly descending order of importance, is:
  1. E-2052:  AGC4 Basic Training Manual, Volume I, by Bernard Savage and Alice Drake, January 1967.
  2. AGC4 Memo #9, Block II Instructions, by Hugh Blair-Smith:
  3. Appendices to AGC4 Memo #8, by Hugh Blair-Smith, February 1965.
  4. R-700: MIT's Role In Project Apollo Volume III: Computer Subsystem, by Eldon C. Hall, August 1972
  5. Aurora 12 assembly listing, November 10, 1966
  6. Block II Electrical Schematics and ICDs, 1966
  7. E-1077:  Preliminary MOD 3C Programmers Manual, by R. Alonso, J. H. Laning, Jr., and H. Blair-Smith, November 1961.
  8. Keyboard and Display Program and Operation, by Alan I. Greene and Robert J. Filene, June 1, 1967.
  9. Block II AGC Self-Check and Show-Banksum, by Edwin D. Smally, December 1966.
  10. Luminary 131 (1C) assembly listing,  December 19, 1969. 
  11. Colossus 1A (build 249) assembly listing, October 28, 1968.
  12. User's Guide to the General Assembler Program (GAP), March 1970.
  13. Guide to Op Codes Recognized by YUL System for BLK2 and AGC, by Hugh Blair-Smith, October 10, 1967.  Or a later version (December 13) within the full source code for YUL.
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 (36K of core rope and 2K of RAM).

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:
These principles are perhaps best understood by viewing actual source-code listings, such as ALARM_AND_ABORT.agc from the Luminary 131 source code.

But ... Some Formatting Notes Regarding the Original AGC4 Assembly Language

An example of some kinds of things supported by Yul or GAP but not supported by yaYUL are provisions for formatting the output assembly listing.  (By the way, notice that I said Yul rather than YUL in the preceding sentence.  That's because "Yul" is an abbreviation—of "Yuletide—and not an acronym like "GAP", and so it really isn't appropriate to put it in all-caps, and I don't think the original developers did so.  But I've only thought of this belatedly, and am too lazy to go back and change it everywhere.  So that's the one and only time I won't call the original assembler "YUL".) 

I personally didn't think that formatting commands were appropriate in the modern age, when computer-program listings are views on a computer screen, rather than as printed out on fixed-length paper pages.  Plus, I didn't have any information about them when I wrote yaYUL anyway, and they seemed pretty mysterious.

But not everyone agrees with me about the significance of such formatting, and there's room for more than one opinion, even though I end up winning since I wrote yaYUL. ☺  Here's what YUL's author actually has to say, from a personal communication, regarding the formatting conventions of the source code and the formatting commands available in YUL, and the question of whether multiple consecutive blank lines are of any importance in AGC source code.  This may be interesting to you if you try to read our online page images of AGC assembly listings.  (Note that the reference below to "##Page" refers to the AGC source code we've created from those page images, and not to the original assembly listings.)

"One question is whether a manual page break falls into your definition of 'blank lines' in which case there could be any number of consecutive 'blank lines' up to fifty-something for a very short page. I believe the only way such page breaks were put in the code was to begin a remarks card with P instead of R in column 1, thereby forcing it to be the first line on a new page. That certainly implied a fairly major change of topic. On the other hand, it appears this info isn't entirely lost because somebody has put in what looks like a full set of page number indicators in the form ## Page 1234. They look as if they appear at the original automatic page breaks in most cases (as they should do because the symbol table and other indexy things are keyed to page numbers), but for manual page breaks they seem to appear after the page-top remarks cards and just ahead of the first non-remark code on that page. A (small) pity, that, since it would have expressed the purpose of the manual page breaks better if it had preceded the page-top remarks. But not a big thing.

"Within the code on a page, the blank lines were put in under control of an unprinted digit in card column 8, between the card number field and the location field. A 2 there forced a double-space (1 blank line) after its card, a 3 there forced a triple-space (2 blank lines), and I'm pretty sure the values 4 through 8 were never used, though they were defined and supported. Double-spacing separated groups of assembler control commands from actual code-generating lines, or within code generally meant that the line above it (often a branch instruction) was not always followed at run time by the line below it, and triple-spacing was generally a small change of topic, not big enough to induce a manual page break. So yes, triple-spacing did mark differences in sections of code, but I don't see that much was lost by reducing them to double spacing. If your friends would like to restore the triple-spacings, they could (aside from peeking at sites with scanned hard-copy pages) limit their attention to blank lines that are preceded by a non-remark and followed by a remark line. Within that set, an educated guess as to where topic breaks occur will get pretty close.

"Having said all that, if you think there were any non-page-break cases with more than two consecutive blank lines, I like to hear about them.

"And yes, I am indeed the right fellow to ask. These minutiae were part of my determination to make the code easy to read. I can't think of any reasons for blank lines beyond those stated above, and I'd be pretty surprised if anybody else could.

"The reason for providing double-spacing and triple-spacing logic was to make it unnecessary to have physical blank cards in the decks, which were frequently pulled out, carried to and from keypunches, and put back. A Steelcase file drawer of 3000 punched cards was a heavy mutha.

"You can see evidence of the above in the fact that in the normal flow of card numbers, the one after such a spacing is generally 1 more than that of the card before
—no missing numbers due to spacing. But having said that, I have to explain why the card number following a remark over 72 columns wide is generally 2 more than the one you can see on the remark line. That's because the only way to make such a remark was to have a second card, with a 9 in column 8, whose columns 9-48 were printed in print positions 81-120 of the previous line. That feature was called 'right print'."

Hugh Blair-Smith



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:
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:
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:
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:
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. Registers from addresses 00-07 are flip-flops (well, except for 07) internal to the processor; all other registers are specially handled erasable memory locations. Whenever these flip-flop registers are accessed, their contents get copied to their corresponding erasable memory locations. This link is one-way; erasable memory locations 00-07 can never be read, as all attempts to do so will be redirected to the corresponding flip-flops.

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:
  1. 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.
  2. 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:
  1. 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
  2. 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 orLXCH A For a dozen years, the L register was described as being in this space as being a 16-bit register like the accumulator, our AGC simulator treated it as such, and our "validation suite" of AGC software did so too.  An intrepid correspondent (thanks Mike Stewart!) recognized that while it may be a 16-bit register, the 16th bits is in fact discarded and cannot be queried by software.  Consequently, for software versions 2016-08-23 and later, the L register is now 15 bits, as the AGC programming gods apparently intended.

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.

Note that erasable-memory location 01 is automatically duplicated as i/o channel 01, and vice-versa.
02
Q
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 (technically it does an indirect jump through Q, but the net effect is the same).

Note that erasable-memory location 02 is automatically duplicated as i/o channel 02, and vice-versa.
03
EB
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).  Internally, the two point to the same set of flip-flops, and simply provide different means of accessing them.
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 7th 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, however, do not exist. Though the AGC can internally calculate addresses in this range, the rope selection circuitry gives up and decides to select no rope at all when given such an address.  The appropriate strands are still cycled, and a read is still attempted, but we can only guess as to what sort of data would be returned with no ropes selected. Presumably they would read all 0 or all 1, and trip the parity fail alarm, causing a reset.

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).  Internally, the two point to the same set of flip-flops, and simply provide different means of accessing them.

Refer also to i/o channel 07.
05
Z
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.  Internally, it points to the same flip-flops as the EB and FB registers, so changing it directly changes the other two.  The bits are arranged within the register as follows:
FFF FF0 000 000 EEE

Refer also to i/o channel 07.
07
(no name)
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 the fact that overflow would be lost by writing A to ARUPT is not a concern.
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
SAMPTIME
These registers store copies of TIME1 and TIME2, respectively, as sampled in the waitlist interrupt service routine. They are used for Noun 65 displays.
15
ZRUPT
This register stores the return address, plus one, 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.  Though the zeroing of these bits is undefined in the reference documentation, it nevertheless was the behavior of the hardware.

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.  It is incremented synchronously with TIME1.  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 7.5 ms. out of phase with incrementing TIME4 (i.e., TIME4 increments 7.5ms after TIME3).  Thus, if the TIME3 interrupt service routine doesn't take any longer than 6ms. or so, 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 7.5 ms. out of phase with incrementing TIME4 (i.e., TIME4 increments 7.5ms after TIME3).  Thus, the TIME4 interrupt service routine doesn't take any longer than 2 ms. or so, 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. It is incremented 5 ms. out of ph ase with TIME1 and TIME3.  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:
  1. 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.
  2. Enable the counter with bit 15 of i/o channel 13 octal.
  3. After the desired time has passed, a T6RUPT occurs, and bit 15 of i/o channel 13 (octal) is reset.
yaAGCinternally 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.

These counters are incremented or decremented with PINC or MINC unprogrammed sequences.
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.

yaAGCzeroes 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, and OUTLINK apparently was not used at all, at least according to the description of it in the symbolic information listing document (p. 33).  That document says
Not used. Originally intended for use to provide "crosslink" capability for serial binary data to cell 00458 of another computer ....
and then proceeds with a lengthy explanation of exactly how to use the "unused" register that I won't bother to cover here.
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:
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.

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.

Vector  Address  (octal)
Interrupt  Name
Trigger Condition
Description
4000
(boot)
Power-up or GOJ signal.
This is where the program begins executing at power-up, and where hardware resets cause execution to go.
4004
T6RUPT Counter-register TIME6 decremented to 0. 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.
4040
DOWNRUPT The downlink shift register is ready for new data (output channels 34 & 35).
Used for telemetry-downlink.
4044
RADAR RUPT Automatically generated inside the AGC after a set pulse sequence has been sent to the radars.
Data from the rendezvous radar is assembled similarly to the uplink data described above.  When a data word is complete, an interrupt is triggered.
4050
RUPT10, aka HANDRUPT
Selectable from three possible sources:
Trap 31A, Trap 31B, and Trap 32.
Used for the hand controller. Only trap 31A is ever used.

Trap 31-A (enabled by resetting CH13 bit 12): Causes a RUPT10 when any of CH31 bits 1-6 are set.
Trap 31-B (enabled by resetting CH13 bit 13): Causes a RUPT10 when any of CH31 bits 7-12 are set.
Trap 32 (enabled by resetting CH13 bit 14): Causes a RUPT10 when any of CH32 bits 1-10 are set.


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:
When an interrupt request is processed, the following steps are taken:
  1. The current contents of the program counter (Z register) is saved into the ZRUPT register.
  2. The instruction appearing at the memory location pointed to by the program counter is saved into the BRUPT register.
  3. Control passes to the appropriate vector-table location, as listed above.
  4. 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.
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 K
or
CAE K
or
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:
  1. 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.)
  2. 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. 
  3. A jump is performed, depending on the original (unedited) contents of K: If greater than +0 or positive overflow exists, 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 or negative overflow exists, 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 16-bit loop counters rather than mere 15-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. If K has negative overflow going into CCS, the absolute value operation will change it into positive overflow. All overflow conditions are taken into account before this operation; thus, if a given K has negative overflow, the negative branch of CCS is taken.

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, but swtiches type (i.e., negative overflow will become positive overflow).

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 A 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 loaded into L.

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 all 15 bits of 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 in the accumulator 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.


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

A sharp-eyed reader may notice that the DIM instruction behaves simularly to the DINC unprogrammed sequence, and wonder if it likewise emits POUT, MOUT, and ZOUT output pulses; it does not do so. Because ZOUT is one of the conditions for T6RUPT to occur, DIM TIME6 cannot trigger the interrupt.  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.

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. 

This instruction assembles as "AD A".

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 10-bit memory address, and must therefore be in a subrange of erasable memory.
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 Q register if K is Q) is considered as overflow-corrected during this operation.

There are two alternate but equally valid ways of looking at this operation. 
  1. 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).
  2. 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 dividend is ±0 but the divisor is non-zero, the L register remains unchanged and the A register is assigned 0 with a sign according to the rules above.

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

If both the dividend and the divisor are ±0, the A register will be stored with ±37777, and the L register will remain unchanged.

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.  The AGC Monitor (or equivalent external ground support equipment) was able to capture these values, but 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 K
TC 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 K
or
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:
  1. Retrieve the value stored in K;
  2. 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 K
TC 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 K
or
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

Description:
The "Multiply" instruction multiplies two single-precision (SP) values to give a double-precision (DP) value.
Syntax:
MP 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:
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:
  1. The result is +0, unless
  2. 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.
Timing:
2 MCT (about 23.4 µs) if executed from erasable memory, 1 MCT (about 11.7 µs) if executed from fixed memory.
Flags:
The Overflow is unaffected.  The Extracode flag is cleared. 
Editing:
The editing registers CYR, SR, CYL, or EDOP are unaffected.
Octal:
30000 (in erasable memory)
or
10000 + next instruction address (in fixed memory)
Notes:
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
Editing:
The editing registers CYR, SR, CYL, or EDOP are unchanged.
Octal:
02000 + 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 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 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 K
TC 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 K2
Some 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 K1
INDEX K2
Some 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:
RETURN
or
TC 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 the same as all other TC instructions. It makes use of an indirect jump through register Q. (see below). This works because the TC instruction reads the instruction at its target address before updating Q.

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 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 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 K
or
TCR 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 two TC 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, INHINTRELINT, 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.
Timing:
2 MCT (about 23.4 µs)
Flags:
The Overflow is cleared.  The Extracode flag remains clear. 
Editing:
Editing is done upon writing to K if K is CYR, SR, CYL, or EDOP.
Octal:
54000 + K
Notes:
The special case "TS A" acts somewhat differently; refer to the OVSK instruction instead.

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 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 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
Editing:
The editing registers CYR, SR, CYL, or EDOP are unchanged.
Octal:
01000 + KC
Notes:
Refer to the list of available i/o channels.

If the destination is the 16-bit 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:
  1. Place the desired basic instruction into the L register (address 00001 octal).
  2. 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. 
  3. 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:
  1. Place the EXTEND instruction (00006 octal) into the A register (address 00000 octal).
  2. Place the desired extracode instruction into the L register (address 00001 octal).
  3. 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. 
  4. 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:

# Example of names for bits in a register.
FIFORDY = 1
FIFOFUL = 2
        = 4
FIFOHAF = 8

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 AddressLabel
or
-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:

2DEC DoublePrecisionNumber

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 AddressLabel
or
-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:

2FCADR ProgramLabel

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 AddressLabel
or
-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 AddressLabel
or
-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 AddressLabel
or
-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 AddressLabel
or
-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. The symbolic listing information document (p. 99) tells us that:
Unfortunately, I didn't really have the full information on this topic when the yaYUL assembler was written, and only implemented BLOCK 0, BLOCK 2, and BLOCK 3, given that only those cases were ever used in actual AGC code.  Perhaps I'll update yaYUL accordingly with the full functionality someday.

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/ProgramLabel
or
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 IoChannelNumber
or
-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 AddressLabel
or
-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:

SymbolEQUALS Value

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:

ERASE n

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

The following syntax is also accepted:

ERASE m - 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

MEMORY m - 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,

OCT OctalNumber

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.

SECSIZ

(Versions 2016-09-29 and later.)  This is an experimental concept for a new pseudo-op not present in the original AGC code, but which could be possibly be of value in the process of transcribing hard-copies or scanned AGC assembly listings to ASCII source code that can be assembled later with yaYUL.  It provides a way to instruct the assembler, after SETLOC, BANK, or BLOCK pseudo-ops have been used, how many words that section of memory will contain before the nextSETLOC, BANK, or BLOCK pseudo-op is encountered.  What this does is to allow the assembler to potentially assemble AGC source code which has only been partially transcribed, but to still locate all symbols and code at their proper locations in memory in spite of that (though not necessarily with their proper numerical values, since not all operands may be resolvable).  After the code has been entered completely, the SECSIZ pseudo-ops can be either retained or discarded without affecting the assembly process.

If a BANK pseudo-op is used together with a SETLOC, then SECSIZ must follow the BANK rather than the SETLOC. Here is an example of usage, with alternating uses of block 2 and block 3.  Arbitrary SECSIZ pseudo-ops have been added, but you can see that whenever a switch is made back to block 2 or 3 from block 3 or 2, an amount of space equivalent to the SECSIZ has been allocated, rather than just the space needed for the instructions explicitly present:

000005,000005: ???????                        A                  EQUALS   0
000006,000006:
000007,000007:    4000                                           BLOCK    2
000008,000008:    4000                                           SECSIZ   25
000009,000009:    4000           60000        FIRST              AD       A
000010,000010:    4001           60000                           AD       A
000011,000011:
000012,000012:    6000                                           BLOCK    3
000013,000013:    6000                                           SECSIZ   64
000014,000014:    6000           56000        SECOND             XCH      A
000015,000015:    6001                                           BANK
000016,000016:    6064                                           SECSIZ   6
000017,000017:    6064           60000        SECONDB            AD       A
000018,000018:
000019,000019:    4000                                           SETLOC   FIRST
000020,000020:    4000                                           BANK
000021,000021:    4025                                           SECSIZ   12
000022,000022:    4025           60000        THIRD              AD       A
000023,000023:
000024,000024:    6072                                           BLOCK    3
000025,000025:    6072                                           SECSIZ   14
000026,000026:    6072           60000        FOURTH             AD       A
000027,000027:
000028,000028:    6106                                           BLOCK    3
000029,000029:    6106                                           SECSIZ   31
000030,000030:    6106           60000        FIFTH              AD       A
000031,000031:
000032,000032: 02,2037                                           BANK     2
000033,000033: 02,2037                                           SECSIZ   17
000034,000034: 02,2037           60000        SIXTH              AD       A
000035,000035: 02,2040                                           BANK
000036,000036: 02,2056                                           SECSIZ   18
000037,000037: 02,2056           60000        SEVENTH            AD       A

SETLOC

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

SETLOC Address

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

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:

OPCODE1
          OPERAND1
OPCODE1   OPCODE2
          OPERAND1
          OPERAND2
          OPERAND3
OPCODE1   OPCODE2

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    OPERAND1
         OPERNAD2

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:
(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:
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.

As I mentioned above, the STCALL, STODL, STORE, and STOVL instructions are special cases in some sense, and the table below summarizes what I was able to learn about them.  In the table, 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:


Mark Schulman's Gloss

Actually — confession time! — the truth is that I (Ron Burkey) have spent very little time figuring out what the various interpreter instructions do, so I can't really say much more about them than I've said above.  Fortunately, Mark Schulman (thanks, Mark!) has stepped in to fill in the gaps and provide a pretty comprehensive explanation of how it all works.  This entire section, which I've turned brown to make it stand out, has been written by Mark with only minimal editing (for format) by me.  I'm sure there's some overlap with what I said about the interpreter above, but I've made no attempt to edit Mark's content.  At any rate, I'll turn it over to Mark here:

Because the AGC used a 3-bit opcode, the number of instructions was limited. The hardware designers used various tricks (such as the EXTEND instruction) to increase the number of opcodes — there were 40 — but the AGC's instruction set was still limited. Additionally, important operations (such as trig functions and vector manipulation) are not supported by the hardware, which meant that performing navigation calculations required an enormous number of subroutine calls.

To solve these problems, as well as get around the general unwieldiness of writing large programs for the AGC, the developers implemented an interpreter that simulated a computer that was an easier and more natural fit for navigation calculations. The machine simulated by the Interpreter is quite different from the AGC, with its own set of registers, more powerful instructions, and a different memory model.

The result is that some software in the AGC is written using the AGC's native instructions, and some of the software is written using the instructions that are understood by the Interpreter. YUL (the assembler) understands both sets of instructions, but it is up to the programmer to use the proper instructions at the proper place in the code, as described below.

The Interpreter includes these features:

The software that implements the Interpreter occupies most of fixed bank 00, some of 01, and some fixed-fixed memory.

Starting / Exiting the Interpreter

A machine-code program starts a block of interpreted code by calling the INTPRET subroutine with this assembler instruction:

TC INTPRET

The Interpreter expects the TC INTPRET instruction to be immediately followed by a block of Interpeter code. The Interpreter reads and executes this code until it encounters an EXIT Interpreter instruction. The Interpreter assumes that the memory word following the EXIT instruction is an AGC machine instruction, and execution continues immediately after the EXIT.

The typical structure of a program containing Interpreter code looks like this:

... machine code ...
TC INTPRET
... Interpreter code ...
EXIT
... machine code ...

It is vitally important that a programmer use the appropriate instructions in the appropriate context. If a programmer uses an Interpreter instruction in the middle of AGC code without starting the Interpreter, the AGC will misinterpret the instruction. Similarly, if a programmer uses a native AGC instruction inside a block of Interpreter code, a similar misinterpretation will occur.

An interpreted program can call a machine code subroutine by executing an RTB instruction (which branches to a machine code location). The RTB instruction stores a return address in the Interpreter. When the machine code subroutine has finished executing, it returns to the interpreted code by executing this instruction:

TC DANZIG

Ron's note:  In general, the DANZIG routine is the dispatcher that determines the next action to take after executing an interpreted instruction.  In other words, it decides the next interpreted instruction to execute.  Calling DANZIG as shown above is simply a special case in which the preceding code was basic code instead of interpretive code.  But why DANZIG?  I'd often wondered about that, but after a dozen years of wondering about it and inventing my own little stories about it, I finally thought to ask someone about it ... and as luck would have it, the first person I thought to ask was able to give me the answer. Hugh Blair-Smith, responding to my apology about asking him something outside of his bailiwick, said the following:
"Outside my bailiwick, you say? Nay sir, I'm fairly sure I personally coined the term in that context. The interpretive language, in both Block I and Block II, was formatted in 'Polish notation,' where one word was occupied by two operation codes and the next few words held as many addresses as those op codes needed. The additional gimmick was that the logic in the interpreter that picked up addresses could tell when it was looking at an op code word instead, in which case it understood that the address it was thinking of wasn't necessary, and it should pop an operand off the stack instead. That's how it implemented Lukasiewicz's parenthesis-free notation, spending a little logic to save words of memory.

"So .......... the dispatcher is the 'gateway to Poland' and as any student of World War II history knows, the bogus Polish provocation, and the Wehrmacht's all-too-ready charge across the border, occurred in Danzig. It was many years before I understood that 'Danzig' was a German respelling of the city known to any proper Pole as 'Gdansk.'

"But it was 'Danzig' on the maps and globes of my childhood, so established by the Treaty of Versailles to give Poland a corridor to the Baltic Sea, and that's why!"
So there you have it.

Interpreter Registers

The Interpreter simulates these registers:

MPAC Multi-purpose accumulator. The MPAC can contain single-, double-, and triple-precision values, and double-precision vectors. The Interpreter keeps track of the type of value currently stored in the MPAC.
OVFIND An overflow indicator register.
ADRLOC A program counter.
QPRET Return address register.
X1, X2 Two index registers
S1, S2 Two step registers, primarily used as loop counters

There are five sets of registers, so the Interpreter can support up to five simultaneous programs.

Stack

The Interpreter simulates a push-down stack, which can contain up to 38 values.

Switches

The Interpreter supports 120 binary (on/off) switches, number 0 - 119 (decimal).

Memory

The AGC hardware uses a complex and cumbersome memory model which divides both erasable and fixed memory into a series of banks. Banking registers are required to specify which banks of memory are being accessed. The Interpreter relieves the programmer of dealing with most of this complexity within interpreted code. An interpreted program can access roughly half of erasable memory and half of fixed memory.

The Interpreter organizes memory into these three areas:

These rules apply:

The memory organization presented to a program differs, depending on whether a program is located in low fixed memory or high fixed memory:

Address (oct) Size (dec) Program in Low Fixed Memory Program in High Fixed Memory
00000 - 01377 768 Unswitched erasable memory (i.e. erasable banks 0 - 2)
01400 - 01777 256 Currently-selected erasable bank*
02000 - 07777 3K Not usable Fixed banks 21 - 23
10000 - 37777 13K Fixed banks 04 - 17 Fixed banks 24 - 37

*This is the erasable memory bank that is selected by the AGC's EBANK register.

Since a 15-bit address can't access enough memory locations the include all the fixed banks, the designers who wrote the Interpreter came up with this scheme: When an interpreted program runs, an internal register called INTBIT15 is set to 1 if the program is in high fixed memory; 0 otherwise. The Interpreter prepends this bit onto data addresses if the address is 2000 (octal) or above. The net effect is that a 15-bit address enables any program in low fixed memory to reach any address in low fixed memory, and any program in high fixed memory to reach any address in high fixed memory.

Indirect Branching

The Interpreter doesn't support indexing on branch instructions, but it does support a method of indirection. The Interpreter requires that all executable code reside in fixed memory, and that erasable memory is used only for data. If the argument for a branch instruction works out to an address in erasable memory, then the assumption is that the location in erasable memory contains an address the value in the erasable memory location is read and the Interpreter branches to that address. This continues any number of times, until the address resolves to an address in fixed memory.

Instructions

Interpreter instructions pack two 7-bit opcodes into a single word, like so:

1 opcode 1 opcode 2
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1

This word is then followed by whatever arguments are required by the first opcode, and then by whatever arguments are required for the second opcode.

The 127 possible opcodes are divided into four categories, specified by the two low-order bits in each opcode. Roughly, the lowest bit bit 1 specifies whether the opcode requires an argument, and bit 2 determines whether the argument may be indexed. Specifically, the four categories work like this:

Bit 2 & 1 Description
00 Instruction does not take an argument and are not indexed.
01 Instructions that take an argument and are not indexed. The argument is always an address (never a value), and can be taken either from the word following the instruction, or from the stack. The Interpreter determines where to get its argument by looking at the word following the instruction. It counts on the fact that instruction words always have bit 15 set to 1, and arguments that don't involve indexing (and instructions in this class never involve indexing) always have their bit 15 set to zero. So if the word following the instruction has its 15 bit set to 0, it's the argument for this instruction; if it's set to 1, it's the next instruction and the argument should be pulled off the stack. [What's not clear to me is how to handle the case in which opcode 1 is in class 01, and opcode 2 is in class 11. It seems that there could be a problem if the intention is to get opcode 1's argument from the stack, but opcode 2's argument follows the instruction.]
10 Branch and index instructions. This class is handled as a special case, and the individual bits do not have their usual meaning.
11 Instruction requires an address and may be indexed. Bit 15 of the argument determines which index register is used (0=X1, 1=X2).

Store instructions break the usual pattern of packing two 7-bit opcodes into a word. They consist of a single opcode with a 10-bit address in erasable memory.

Instruction List

The following table describes each Interpreter opcode. The "Args" column indicates whether the instruction requires 0, 1, or 2 arguments, and whether the arguments are addresses or constants.

The documentations didn't provide the numeric opcodes for any of the instructions, so it's hard to tell which of the four classes each opcode falls into.

Opcode Args X, Y Description
ABS
Absolute Value. Replaces the scalar value in the MPAC with its absolute value.
ABVAL
Vector Absolute Value. Replaces the vector value in the MPAC with its absolute value as a scalar. [Documentation goes into detail.]
ACOS
Arc-cosine. Multiplies the MPAC by 2, takes the arcsine, divides by 2p, and leaves the result in the MPAC.
ASIN
Arc-sine. Multiplies the MPAC by 2, takes the arcsine, multiplies by 2p, and leaves the result in the MPAC.
AXC C Loads the complement of a constant into one of the index registers. [See Note 1 below]
AXT C Loads a constant into one of the index registers. [See Note 1 below]
BDDV A Backwards Double Divide. Divide the double-precision value in X by the MPAC and leave the result in the MPAC.
BDSU A Backwards Double Subtract. Subtracts the MPAC from the double-precision value at X and leaves the result in the MPAC.
BHIZ A Branch High Order Zero. Branch to X if the single-precision value in the MPAC is zero.
BMN A Branch Minus. Branch to X if the triple-precision number in the MPAC is negative.
BOF A, A Branch to Y if switch X is off.
BOFCLR A, A Clear switch X and branch to Y if the switch was initially off.
BOFINV A, A Invert switch X and branch to Y if the switch was originally off.
BOFSET A, A Set switch X and branch to Y if the switch was initially off.
BON A, A Branch to Y if switch X is on.
BONCLR A, A Clear switch X and branch to Y if the switch was initially on.
BONINV A, A Invert switch X and branch to Y if the switch was originally on.
BONSET A, A Set switch X and branch to Y if the switch was initially on.
BOV A Branch on Overflow. If the OVFIND register is not zero, then reset it to zero and branch to X.
BOVB A Brand on Overflow to Basic. If the OVFIND register is not zero, then reset it to zero and call a regular machine language subroutine (as if an RTB had been executed).
BPL A Branch Plus. Branch to X if the triple-precision number in the MPAC is greater than or equal to zero.
BVSU A Backwards Vector Subtract. Subtracts the MPAC from the vector at X and leaves the result in the MPAC.
BZE A Branch Zero. Branch to X if the triple-precision number in the MPAC is zero.
CALL A Calls a subroutine. Transfers to location X, and loads the return address into the QPRET register.
CCALL A, A Computed CALL. Works like CGOTO, but also puts the return address in the QPRET register, like CALL.
CGOTO A, A Computed GOTO. X must point to erasable memory. The value at X is added to Y (which must be in fixed memory), and control transfers there. Like GOTO, if CGOTO occurs as the first opcode in an instruction word, then the second opcode must be blank.
CLEAR A Clear switch X.
CLRGO A, A Clear switch X and branch to location Y. Essentially a CLEAR and GOTO in one instruction.
COS
Cosine. Multiplies 2p by the value of the MPAC, takes the cosine, multiplies that by 0.5, and leaves the result in the MPAC.
DAD A Double Add. Adds the double-precision value at X to the MPAC.
DCOMP
Complement. Replaces the scalar value in the MPAC with its complement.
DDV A Double Divide. Divide the MPAC by the double-precision value in X.
DLOAD A Load a double-precision value into the MPAC
DMP A Double Multiply. Multiplies the MPAC by the double-precision value at X and leaves the product in the MPAC.
DMPR A Double Multiply and Round. Multiplies the MPAC by the double-precision value at X and leaves the product in the MPAC, rounded to double precision.
DOT A Vector Dot Product. [tl;dr]
DSQ
Double Square. Squares the value in the MPAC.
DSU A Double Subtract. Subtracts the double-precision value at X from the MPAC.
EXIT
Exit the Interpreter and resume executing normal machine code at the next instruction.
GOTO A Transfer control to a new location. If GOTO occurs in the first opcode of a word, then the 2nd opcode must be blank. Note that a subroutine can return using GOTO if it previously saved the contents of the QPRET register in a memory location.
INCR C Adds a constant Y to one of the index registers. [See Note 1.]
INVERT A Invert (flip) switch X.
INVGO A, A Invert (flip) switch X and branch to location Y.
LXC A Loads one of the index registers with the complement of the contents of erasable location Y. [See Note 1.]
LXA A Loads one of the index registers with the contents of erasable location Y. [See Note 1.]
MXV A Matrix Post-Multiplication by Vector.
NORM A Normalize. The value of the MPAC is shifted left until the value is greater than or equal to 0.5. Memory location X is set to the complement of the number of places the value was shifted left. If the starting value of the MPAC is 0, then -0 is stored in X and the MPAC remains unchanged.
PDDL A Push Down and Double Load. Push the value of the MPAC register onto the stack, and load the MPAC with double-precision value at X.
PDVL A Push Down and Vector Load. Push the value of the MPAC register onto the stack, and load the MPAC with the vector at X.
PUSH
Push. Pushes the contents of the MPAC onto the stack.
ROUND
Rounds the triple-precision value in the MPAC to a double-precision value.
RTB A Return to Basic. Essentially this instruction allows the interpreted program to call a subroutine outside the Interpreter (i.e. written in normal machine code). X is the address of the subroutine. The subroutine returns to the Interpreter by executing a TC Q. Alternatively, the subroutine can exit by executing TC DANZIG. [I assume DANZIG is some predefined entry point into the Interpreter.]
RVQ
Return via QPRET. Returns to the location specified in the QPRET register. This is the normal way to return from a subroutine, although if the QPRET register had previously been saved to a memory location, then GOTO can be used instead.
SET A Set Switch. Sets a switch to 1.
SETGO A, A Sets a switch to 1 and then jumps to address Y. (Essentially a SET and GOTO in one instruction.)
SETPD C Set Push Loc.
SIGN A Sign Test. If the double-precision value at X is negative, then the value in the MPAC is replaced with the MPAC's complement.
SIN
Sine. Multiplies 2p by the value of the MPAC, takes the sine, multiplies that by 0.5, and leaves the result in the MPAC.
SL C Shift Left. Shift the MPAC left by the number of places in X. The documentation contains a lengthy discussion of the valid values for X.
SL1 ... SL4
Shift Left. Shifts the contents of the MPAC 1 to 4 places left. (There are four separate opcodes here that differ only in how many places the value is shifted.)
SL1R ... SL4R
Shift Left and Round. Shifts the contents of the MPAC 1 to 4 places left and rounds. [Not sure what it means to round a number that's shifted left.] (There are four separate opcodes here that differ only in how many places the value is shifted.)
SLOAD A Load a single-precision value into the MPAC, but sets the MPAC's type to double precision.
SLR C Shift Left and Round. Shift the MPAC left by the number of places in X and rounded. The documentation contains a lengthy discussion of the valid values for X.
SQRT
Square Root. The contents of the MPAC are replaced by the square root of the MPAC.
SR C Shift Right. Shift the MPAC right by the number of places in X. The documentation contains a lengthy discussion of the valid values for X.
SR1 ... SR4
Shift Right. Shifts the contents of the MPAC 1 to 4 places right. (There are four separate opcodes here that differ only in how many places the value is shifted.)
SR1R ... SR4R
Shift Right and Round. Shifts the contents of the MPAC 1 to 4 places right and rounds. (There are four separate opcodes here that differ only in how many places the value is shifted.)
SRR C Shift Right and Round. Shift the MPAC right by the number of places in X, and rounded. The documentation contains a lengthy discussion of the valid values for X.
SSP A Set Single Precision. Stores a constant Y in single-precision location X.
STCALL A, A Store the MPAC into a memory location X and then call subroutine at location Y.
STODL A, A Store the MPAC to a memory location, and then reload MPAC with a new double-precision value.
STORE A Stores the contents of the MPAC into erasable memory, according to the current type.
STOVL A, A Store the MPAC to a memory location, and then reload MPAC with a new vector value.
STQ A Store the QPRET register in the word at location X. This instruction is used when one subroutine calls another it gives the subroutine the ability to save the current return address before it gets replaced.
SXA A Stores one of the index registers at location Y. [See Note 1.]
TAD A Triple Add. Adds the trip;e-precision value at X to the MPAC.
TIX A Count and Branch on Index. If subtracting step register X from index register X would yield a positive (non-zero) number, then the subtraction takes place, leaving the result in the index register, and a branch takes place to Y. This instruction enables the creation of a loop counter. [See Note 1.]
TLOAD A Load a triple-precision value into the MPAC
UNIT
Causes the vector in the MPAC to be replaced by the quotient of the MPAC vector divided by twice the absolute value of the MPAC vector.
VAD A Vector Add. Adds the vector at X to the MPAC.
VCOMP
Complement Vector. Replaces the vector value in the MPAC with its complement.
VDEF
Vector Define. Somehow takes the double-precision value in the MPAC and converts it into a vector.
VDSC A Vector Divide by Scalar.
VLOAD A Load a vector into the MPAC
VPROJ A Vector Projection.
VSL C Vector Shift Left. [tl;dr]
VSL1 ... VSL8
Vector Shift Left by 1 - 8 places. (There are eight separate opcodes here that differ only in how many places the value is shifted.)
VSQ
Vector Square. Squares the vector quantity in the MPAC, replacing it with a double-precision number. [Don't understand what it means to square a vector.]
VSR C Vector Shift Right. [tl;dr]
VSR1 ... VSR8
Vector Shift Right by 1 - 8 places. (There are eight separate opcodes here that differ only in how many places the value is shifted.)
VSU A Vector Subtract. Subtracts the vector at X from the MPAC and leaves the result in the MPAC.
VXM A Matrix Pre-Multiplication by Vector.
VXSC A Vector Times Scalar. [tl;dr]
VXV A Vector Cross Product.
XAD A Adds the contents of Y to one of the index registers. [See Note 1.]
XCHX A Exchanges one of the index registers with the contents of an erasable memory location. [See Note 1.]
XSU A Subtracts the contents of Y from one of the index registers. [See Note 1.]
Notes:
[1] The documentation doesn't specify how the instruction determines which index register or step register (1 or 2) is being referenced. In the assembly language the programmer has to specify a 1 or 2, but does it by placing it immediately after the opcode mnemonic ("CLEAR,2"), rather than treating it as an operand ("CLEAR 2"). It's possible that there are two distinct numeric opcodes for each instruction that references an index register or step register.


I/O Channels

The following information is largely taken from Luminary and Colossus program listings.  However, many additional i/o channels are covered on the developer page.

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.  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; however, with the available ropes, setting them simply causes the AGC to not select any rope at all (see the discussion in the FB register description).
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:
  1. If the counter is already at ±0, then do not increment or decrement.  Output the signal ZOUT and clear the T6RUPT-enable flag at bit 15 of i/o channel 13 (octal).
  2. Whereas if the counter greater than +0 then decrement by 1.   Also, output the POUT signal.
  3. Whereas if less than -0 then increment by 1.  Also, output the MOUT signal.
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.




This page is available under the Creative Commons No Rights Reserved License
Last modified by Ronald Burkey on 2024-05-14.

Virtual AGC is hosted
              by ibiblio.org