The Digital Equipment Corporation PDP-8Memory Reference Instructions
Part of
the PDP-8 Programmer's Reference Manual
|
00 01 02 03 04 05 06 07 08 09 10 11 ___________________________________ | | | | | | | | | | | | | |__|__|__|__|__|__|__|__|__|__|__|__| | | I| Z| 7 bit | | 3 bit | | Word in Page | | Opcode | Mode | | | Address |
AND M
The contents of memory location M are logically anded with AC, bit by bit. There is no effect on the link bit. All other logical operations must be accomplished with macros.
TAD M
The contents of memory location M are added to AC. A carry out of the high bit of AC will complement the link bit. Most other arithmetic operations must be accomplished with macros.
ISZ M
The contents of memory location M are incremented and placed back in memory. If the result stored in memory is zero, the program counter is incremented; as a result that the next instruction will be skipped if the result is zero. AC and LINK are not modified.
ISZ is frequently used to increment memory addresses; unless the address could wrap around from 7777 to 0000, ISZ is used as if it will never skip. If a wraparound is possible, it must be followed by a no-op.
ISZ allows the construction of a number of fast macros for block operations.
DCA M
The contents of the accumulator are stored in the memory location M; the accumulator is then cleared. There is no effect on the link bit. The corresponding load operation must be accomplished by a macro
JMS P
The contents of PC (a pointer to the next instruction) is stored in memory location P as a return address, and then control is transfered to the location following P. AC and LINK remain unchanged. There is no return instruction; this is done using an indirect jump through P.
JMP P
Control is transferred to memory location P. AC and LINK remain unchanged.
TAD Z ADDR TAD ADDR
When the Z bit of the instruction is zero, page zero addressing mode is used. This allows addressing of memory locations 0000 through 0177 (octal) of the current memory from an instruction located anywhere in theat field. The Z qualifier may be used to indicate that page zero mode is desired, but most PDP-8 assemblers will automatically generate page zero mode if the addressed location is in page zero.
When the Z bit of the instruction is one, current page addressing mode is used. This allows addressing of memory locations in the current page, as determined by the 5 most significant bits of the program counter (more accurately, the 5 most significant bits of the address of the location from which the instruction was fetched). All PDP-8 assemblers will generate current page mode when the addressed location is in the current page.
Direct addressing of locations not on the current page is impossible, but some PDP-8 assemblers will automatically generate indirect references to off-page locations, storing the indirect word at the end of the current page. This usage is considered unsafe because indirection may change the memory field being referenced!
TAD I ADDR
When the I bit of the instruction is zero, direct addressing is used. This allows any word in either page zero or the current page to be referenced as an operand.
When the I bit of the instruction is one, indirect addressing is used. In this mode, the word at the addressed location in page zero or the current page is used as a pointer to the intended operand. This requires one additional memory cycle for the instruction.
All PDP-8 assemblers require the use of the I qualifier to indicate indirect mode. Some PDP-8 assemblers will automatically generate indirect references for those instructions that directly reference off-page locations.
Indirect addressing using locations 0010 through 0017 (octal) has the side effect of incrementing these locations prior to use. This is called autoindexed addressing .
Indirect addressing may be used to reference operands in any memory field, depending on how the DF (data_field) register is set. By default, this is usually the current field, the same field as that from which the instruction was fetched.
If indirect addressing is done through locations 0010 and 0017 (octal) of any memory field, the indirect word will be incremented prior to use. Other than this increment, auto-indexed mode is the same as indirect mode. Autoindex addressing is particularly useful for operations on blocks of consecutive words.
The PDP-8 does not support any immediate addressing mode, however, there are microcoded instructions that can be used to load a number of useful constants in the accumulator in a single machine cycle, and most PDP-8 assemblers support a notation for immediate constants.
TAD (7) / add the constant 7 JMS I [PROC] / call PROC TAD I (LOC) / add the contents of LOC
If an operand is enclosed in parentheses or square brackets, most PDP-8 assemblers will treat the operand as a value intended to be used as an immediate constant. To do this, the indicated value is stored in a memory location, and the instruction is then assembled with the address of that location as an operand.
If parentheses are used, the operand is stored at the end of the current page, while if square brackets are used, the operand is stored at the end of page zero. All common PDP-8 assemblers will attempt to optimize constant storage, checking to see if a constant in question has already been stored at the end of the page in question and re-using that constant if so.
The usage JMS I [PROC]
is a standard way to call an
off-page procedure. This allocates a pointer to PROC
at the
end of page zero; this pointer will be used by all other references to
PROC
that are coded the same way.
The usage TAD I (LOC)
is a standard way to reference a variable
LOC
that is on a different page and that is local in the sense
that it only concerns a small number of routine. In this case, a pointer to
LOC
will be allocated at the end of the current page and shared
only by other references to LOC
made from the same page.
The PDP-8 instruction set is so small that many operations that are usually single instructions on a larger machine must be performed by instruction sequences on the PDP-8. While these are referred to as macros here, the memory resources of the PDP-8 are limited enough that these are usually not coded as given, but rather, carefully folded into other computations.
M
are to be loaded into the
accumulator, the following instruction sequence should be used:
CLA TAD MIn the event that the accumulator is already known to be zero, for example, because the previous instruction was a
DCA
operation, the above can be abbreviated to a simple TAD
instruction. Other sequences are also effective, for example, it is
possible to and M
with -1 to get the same effect.
In general, the PDP-8 does not support immediate operands, but
there are single-cycle microcoded instructions sequences that
load commonly used constants in memory. These are conventionally
defined using the notation NLXXXX
in PAL, where
XXXX
is the octal constant to be loaded. These are
usually defined, as needed, at the start of each PAL program:
NL0000= CLA / 0 NL0001= CLA IAC / 1 NL0002= CLA CLL CML RTL / 2 NL2000= CLA CLL CML RTR / 1024 NL3777= CLA CMA CLL RAR / 2047 NL4000= CLA CLL CML RAR / 2048 NL7777= CLA CMA / -1 or 4095 NL7776= CLA CMA CLL RAL / -2 or 4094 NL7775= CLA CMA CLL RTL / -3 or 4093Given these definitions, the instruction
NL0001
, for
example, will load 1 in the accumulator. Note that the instruction
NL0000
is rarely defined or used (it means no more or
less than CLA), and note that these instructions have ill defined
effects on the LINK bit. Some set it, some reset it, and others
leave it as it was.
M
from
memory is to use De Morgan's law, along with TMP
, a temporary
location.
CMA / complement accumulator DCA TMP / TMP = not(AC) TAD M / get value to or with accumulator CMA / complement it AND TMP / AC = not(M) and not(TMP) CMA / complement result
A clever alternative is to clear all bits from one operand that are set in the other before adding the operands. If both operands are initially in memory, this alternative is faster and no temporary location is needed.
DCA TMP / copy the operand TAD M AND TMP / AC = bits set in both TMP and M CMA AND TMP / AC = TMP - (bits set in both) TAD M / AC = TMP or M
a xor b = (a and not b) or (b and not a)But leads to inefficient code. A better solution rests on the fact that binary addition is based on exclusive or, if only the carry bit can be defeated, as follows:
a xor b = (a + b) - 2*(a and b)The second term computes the bits that will cause carrys; these carry bits are then subtracted from the sum. In PDP-8 assembly language, this can be done as follows, using
TMP
, a temporary location.
DCA TMP TAD TMP AND M CMA IAC CLL RAL TAD TMP TAD M
M
from the accumulator, the value must
be two's complemented and then added to the accumulator. This requires
TMP
, a temporary location.
DCA TMP / set aside accumulator TAD M / get value to subtract from it CMA IAC / 2's complement it TAD TMP / add back saved valueIt is significantly faster to do reverse subtraction, subtracting the accumulator from
M
, the contents of a memory location,
leaving the result in the accumulator.
CMA IAC / 2's complement accumulator TAD M / add operand from memory
CLA DCA TMP TAD W LOOP, CLL RAR SZL ISZ TMP SZA JMP LOOP TAD TMPThe above bit of code will, in the worst case, require as many as 12 iterations of the loop body, at a cost of 6 memory cycles per iteration. Loop unrolling can reduce this, as illustrated below:
CLA DCA TMP TAD W LOOP, CLL RAR / test bit SZL ISZ TMP CLL RAR / test bit SZL ISZ TMP CLL RAR / test bit SZL ISZ TMP SZA / end loop if no more bits JMP LOOP / otherwise iterate TAD TMP
X1
and X2
are assumed to reference
autoincrement locations, and TMP
is any directly addressable
temporary, used as a loop counter. The comments are essentially
transliterations to C.
LOC
and occupying CNT
words.
CLA TAD LOC-1 DCA X1 / X1 = LOC-1 TAD -CNT DCA TMP / TMP = -CNT LOOP, / do { DCA I X1 / M[++X1] = 0 ISZ TMP / TMP++ JMP LOOP / } while (TMP!=0)
SRC
to the block starting at
DST
; CNT
words are
copied.
CLA TAD SRC-1 DCA X1 / X1 = SRC-1 TAD DST-1 DCA X2 / X2 = DST-1 TAD -CNT DCA TMP / setup TMP = -CNT LOOP, / do { TAD I X1 DCA I X2 / M[++X2] = M[++X1] ISZ TMP / TMP++ JMP LOOP / } while (TMP!=0)