7
Assignment Instructions |
The assignment instructions move words, double words, bytes, and arbitrary fields of words between the stack and memory. These include the immediate instructions, which obtain their operands from the code stream, the frame instructions, used to access local and global variables, and the instructions that dereference pointers (direct and indirect). The string and field instructions read and write substructures smaller than a word.
Design Note: In instructions that access both the stack and memory, if both a fault error and a stack error are possible, it is undefined which will occur first.
The immediate instructions load one- or two-word constants onto the stack. Operands (if any) are obtained from the code stream.
LIN1: PROCEDURE = BEGIN Push[177777B]; END;
LINI: PROCEDURE = BEGIN Push[l00000B]; END;
LI00: PROCEDURE = BEGIN PushLong[LONG[O]]; END;
LIn: PROCEDURE [n: [O..lO)] = BEGIN Push[n]; END;
LIB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; Push[alpha]; END;
Note that alpha is not sign-extended.
LINB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; Push[BytePair[377B, alpha]]; END;
LIHB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; Push[BytePair[alpha, 0]]; END;
LIW: PROCEDURE = BEGIN u: UNSPECIFIED = GetCodeWord[]; Push[u]; END;
The local and global frame instructions move one or two words between the stack and the frame. The opcodes differ primarily in their addressing modes: for frequently addressed variables, the offset of the variable in the frame is given by the instruction's opcode. Less frequently referenced frame variables are addressed by a one-byte offset obtained from alpha. Instructions are also provided for generating the address of a local or global variable.
The load local, store local, and put local instructions provide access to the local frame variables of the current context. The local-address instructions each generate a short pointer to a local variable, given its offset in the frame.
LAn: PROCEDURE [n: [0..3,6,8]] = BEGIN Push[LF + n]; END;
LAB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; Push[LF + alpha]; END;
LAW: PROCEDURE = BEGIN word: UNSPECIFIED = GetCodeWord[]; Push[LF + word]; END;
Programming Note: Local variables at offsets larger than 255 words from the base of the frame can be accessed by generating their addresses on the stack and then using the direct pointer instructions defined in §7.3.1.
The load local instructions move one or two words onto the stack from the local frame.
LLn: PROCEDURE [n: [O..ll]] = BEGIN Push[FetchMds[LF + n]]; END;
LLB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; Push[FetchMds[LF + alpha]]; END;
LLDn: PROCEDURE [n: [0..8,10]] = BEGIN Push[FetchMds[LF + n]]; Push[FetchMds[LF + n + 1]]; END;
LLDB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; Push[FetchMds[LF + alpha]]; Push[FetchMds[LF + alpha + 1]]; END;
The store local instructions move one or two words from the stack to the local frame.
SLn: PROCEDURE [n: [o..10]] = BEGIN StoreMds[LF + n] Pop[]; END;
SLB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; StoreMds[LF + alpha] Pop[]; END;
SLDn: PROCEDURE [n: [0..6,8]] = BEGIN StofeMds[LF + n + 1] Pop[]; StoreMds[LF + n] Pop[]; END;
SLDB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; StoreMds[LF + alpha + 1] Pop[]; StoreMds[LF + alpha] Pop[]; END;
The put local instructions move one or two words from the stack into the local frame, leaving its operands on the stack.
PLn: PROCEDURE [n: [0..3]] = BEGIN SLn[n]; Recover[]; END;
PLB: PROCEDURE [n: [O..3]] = BEGIN SLB[]; Recover[]; END;
PLDO: PROCEDURE = BEGIN SLDn[O]; Recover[]; Recover[]; END;
PLDB: PROCEDURE = BEGIN SLDB[]; Recover[]; Recover[]; END;
The Add Local Zero to Immediate Byte instruction adds local zero and a small constant from alpha and pushes the sum on the stack.
AL0IB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; Push[FetchMds[LF] alpha]; END;
The load global and store global instructions provide access to the global frame variables of the current context. The global address instruction generates a short pointer to a global variable, given its offset in the frame.
GAn: PROCEDURE [n: [0..1]] = BEGIN Push[GF + n]; END;
GAB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; Push[GF + alpha]; END;
GAW: PROCEDURE = BEGIN word: UNSPECIFIED = GetCodeWord[]; Push[GF + word]; END;
Programming Note: Global variables at offsets larger than 255 words from the base of the frame can be accessed by generating their addresses on the stack and then using the direct pointer instructions defined in §7.3.1.
The load global instructions move one or two words onto the stack from the global frame.
LGn: PROCEDURE [n: [0..2]] = BEGIN Push[FetchMds[GF + n]]; END;
LGB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; Push[FetchMds[GF + alpha]]; END;
LGDn: PROCEDURE [n: [0,2]] = BEGIN Push[FetchMds[GF+ n]]; Push[FetchMds[GF + n + 1]]; END;
LGDB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; Push[FetchMds[GF + alpha]]; Push[FetchMds[GF + alpha + 1]]; END;
The store global instructions move one or two words from the stack to the global frame.
SGB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; StoreMds[GF + alpha] Pop[]; END;
SGDB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; StoreMds[GF + alpha + 1] Pop[]; StoreMds[GF + alpha] Pop[]; END;
The pointer instructions are divided into two types: direct and indirect. They move a word or pair of words between the stack and memory using a pointer obtained from the stack or from the local or global frame. Most pointer instructions have variants that dereference either short or long pointers.
Implementation Note: In the long-pointer variants of these instructions, any addition to the pointer must be calculated using double-word arithmetic, to account for the case in which ptr + offset may carry into the most significant word of the pointer.
The direct pointer instructions obtain a pointer from the stack and move a single or double word stack operand to or from the specified location. The pointer is usually modified by a small offset contained in the opcode or alpha.
The read direct instructions obtain a long or short pointer from the stack, add to it a small displacement from the opcode or alpha, and perform a single- or double-word push to the stack from memory.
Rn: PROCEDURE [n: [0..1]] = BEGIN ptr: POINTER = Pop[]; Push[FetchMds[ptr + n]]; END;
RB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; ptr: POINTER = Pop[]; Push[FetchMds[ptr + alpha]]; END;
RLO: PROCEDURE = BEGIN ptr: LONG POINTER = PopLong[]; Push[Fetch[ptr]]; END;
RLB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; ptr: LONG POINTER = PopLong[]; Push[Fetch[ptr + LONG[alpha]]]; END;
RD0: PROCEDURE = BEGIN ptr: POINTER = Pop[]; u: UNSPECIFIED = FetchMds[ptr]; v: UNSPECIFIED = FetchMds[ptr + 1]; Push[u]; Push[v]; END;
RDB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; ptr: POINTER = Pop[]; u: UNSPECIFIED = FetchMds[ptr + alpha]; v: UNSPECIFIED = FetchMds[ptr + alpha + 1]; Push[u]; Push[v]; END;
RDL0: PROCEDURE = BEGIN ptr: LONG POINTER = PopLong[]; u: UNSPECIFIED = Fetch[ptr]; v: UNSPECIFIED = Fetch[ptr + 1]; Push[u]; Push[v]; END;
RDLB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; ptr: LONG POINTER = PopLong[]; u: UNSPECIFIED = Fetch[ptr + LONG[alpha]]; v: UNSPECIFIED = Fetch[ptr + LONG[aipha] + 1]; Push[u]; Push[v]; END;
RC: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; offset: CARDINAL = Pop[]; Push[ReadCode[offset + alpha]]; END;
W0: PROCEDURE = BEGIN ptr: POINTER = Pop[]; StoreMds[ptr] Pop[]; END;
WB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; ptr: POINTER = Pop[]; StoreMds[ptr + alpha] Pop[]; END;
WLB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; ptr: LONG POINTER = PopLong[]; Store[ptr + LONG[alpha]] Pop[]; END;
WDB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; ptr: POINTER = Pop[]; StoreMds[ptr + alpha + 1] Pop[]; StoreMds[ptr + alpha] Pop[]; END;
WDLB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; ptr: LONG POINTER = PopLong[]; Store[ptr + LONG[alpha] + 1] Pop[]; Store[ptr + LONG[alpha]] Pop[]; END;
The put swapped direct instructions obtain a short pointer from the stack, add to it a small displacement from the opcode or alpha, and perform a single- or double-word store to memory. They leave the pointer on the stack for use by subsequent instructions. ("Swapped" refers to the order of the address and the data on the stack. The data is given first (from the top down) followed by the address for swapped instructions).
PSB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; u: UNSPECIFIED = Pop[]; ptr: POINTER = Pop[]; StoreMds[ptr + alpha] u; Recover[]; END;
PSD0: PROCEDURE = BEGIN v: UNSPECIFIED = Pop[]; u: UNSPECIFIED = Pop[]; ptr: POINTER = Pop[]; StoreMds[ptr + 1] v; StoreMds[ptr] u; Recover[]; END;
PSDB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; v: UNSPECIFIED = Pop[]; u: UNSPECIFIED = Pop[]; ptr: POINTER = Pop[]; StoreMds[ptr + alpha + 1] v; StoreMds[ptr + alpha] u; Recover[]; END;
PSLB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; u: UNSPECIFIED = Pop[]; ptr: LONG POINTER = PopLong[]; Store[ptr + LONG[alpha]] u; Recover[]; Recover[]; END;
PSDLB: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; v: UNSPECIFIED = Pop[]; u: UNSPECIFIED = Pop[]; ptr: LONG POINTER = PopLong[]; Store[ptr + LONG[alpha] + 1] v; Store[ptr + LONG[alpha]] u; Recover[]; Recover[]; END;
The indirect pointer instructions obtain a pointer from the local or global frame or the stack and move a single- or double-word stack operand to or from the specified location. The pointer is modified by a small offset contained in the opcode or alpha.
The read indirect instructions perform a single- or double-word push using a pointer obtained from the local or global frame. Most of these instructions treat alpha as a pair; pair.left specifies the offset of the pointer in the frame, and pair.right is added to the pointer.
RLI0n: PROCEDURE [n: [0..3]] = BEGIN ptr: POINTER = FetchMds[LF]; Push[FetchMds[ptr + n]; END;
RLIP: PROCEDURE = BEGIN pair: NibblePair = GetCodeByte[]; ptr: POINTER = FetchMds[LF + pair.left]; Push[FetchMds[ptr + pair.right]]; END;
RLILP: PROCEDURE = BEGIN pair: NibblePair = GetCodeByte[]; ptr: LONG POINTER = ReadDblMds[LF + pair.left]; Push[Fetch[ptr + LONG[pair.right]]]; END;
RGIP: PROCEDURE = BEGIN pair: NibblePair = GetCodeByte[]; ptr: POINTER = FetchMds[GF + pair.left]; Push[FetchMds[ptr + pair.right]]; END;
RGILP: PROCEDURE = BEGIN pair: NibblePair = GetCodeByte[]; ptr: LONG POINTER = ReadDblMds[GF + pair.left]; Push[Fetch[ptr + LONG[pair.right]]]; END;
RLDIOO: PROCEDURE = BEGIN ptr: POINTER = FetchMds[LF]; u: UNSPECIFIED = FetchMds[ptr]; v: UNSPECIFIED = FetchMds[ptr + 1]; Push[u]; Push[v]; END;
RLDIP: PROCEDURE = BEGIN pair: NibblePair = GetCodeByte[]; Ptr: POINTER = FetchMds[LF + pair.left]; u: UNSPECIFIED = FetchMds[ptr + pair.right]; v: UNSPECIFIED = FetchMds[ptr + pair.right + 1]; Push[u]; Push[v]; END;
RLDILP: PROCEDURE = BEGIN pair: NibblePair = GetCodeByte[]; ptr: LONG POINTER = ReadDblMds[LF + pair.left]; u: UNSPECIFIED = Fetch[ptr + LONG[pair.right]]; v: UNSPECIFIED = Fetch[ptr + LONG[pair.right] + 1]; Push[u]; Push[v]; END;
WLIP: PROCEDURE = BEGIN pair: NibblePair = GetCodeByte[]; ptr: POINTER = FetchMds[LF + pair.left]; StoreMds[ptr + pair.right] Pop[]; END;
WLILP: PROCEDURE = BEGIN pair: NibblePair = GetCodeByte[]; ptr: LONG POINTER = ReadDblMds[LF + pair.left]; Store[ptr + LONG[pair.right]] Pop[]; END;
WLDILP: PROCEDURE = BEGIN pair: NibblePair = GetCodeByte[]; ptr: LONG POINTER = ReadDblMds[LF + pair.left]; Store[ptr + LONG[pair.right] + 1] Pop[]; Store[ptr + LONG[pair.right]] Pop[]; END;
The string instructions read or write eight-bit bytes contained in packed arrays. The address of the word containing the byte is computed as the sum of a short or long pointer obtained from the stack plus a byte offset divided by two. The offset is the sum of an index taken from the stack and the instruction's alpha byte.
The least significant bit of the offset selects the byte that is read or written; zero specifies the most significant byte. The data byte is obtained from the stack, ignoring the high order byte of the stack word, or written to the stack, clearing the high-order byte. The following routines are used by the string instructions (and elsewhere) to fetch and store a byte:
FetchByte: PROCEDURE [ptr: LONG POINTER, offset: LONG CARDINAL] RETURNS [BYTE] = BEGIN word: BytePair = Fetch[ptr + offset/2]; RETURN[IF (offset MOD 2) = 0 THEN word.left ELSE word.right]; END; StoreByte: PROCEDURE [ptr: LONG POINTER, offset: LONG CARDINAL, data: BYTE] = BEGIN word: BytePair = Fetch[ptr + offset/2]; Store[ptr + offset/2] IF (offset MOD 2) = 0 THEN BytePair[data, word.right] ELSE BytePair[word.left, data]; END;
Implementation Note: In the long-pointer variants of these instructions, the offset must be calculated using double-word arithmetic, to account for the case in which alpha + index may carry into the most significant word of the pointer.
The read string instructions clear the high-order byte of the data word written to the stack.
RS: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; index: CARDINAL = Pop[]; ptr: POINTER = Pop[]; Push[FetchByte[ptr: ptr, offset: alpha + index]]; END;
RLS: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; index: CARDINAL = Pop[]; ptr=: LONG POINTER = PopLong[]; Push[FetchByte[ptr: ptr, offset: LONG[aipha] + LONG[index]]]; END;
The write string instructions ignore the high-order byte of the data word obtained from the stack.
WS: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; index: CARDINAL = Pop[]; ptr: POINTER = Pop[]; data: BYTE = LowByte[Pop[]]; StoreByte[ptr: ptr, offset: alpha + index, data: data]; END;
WLS: PROCEDURE = BEGIN alpha: BYTE = GetCodeByte[]; index: CARDINAL = Pop[]; ptr: LONG POINTER = PopLong[]; data: BYTE = LowByte[Pop[]]; StoreByte[ptr: ptr, offset: LONG[alpha] + LONG[index], data: data]; END;
The field instructions either read or write a field of a word in memory. The word is usually addressed by a short or long pointer found on the stack. The read indirect operations obtain the required pointer from the local frame.
The field is described by a field specifier or a field descriptor, which is usually found in the alpha and beta bytes of the instruction. The stack operations take their field descriptors from the stack. Field specifiers are defined as follows:
FieldSpec: TYPE = MACHINE DEPENDENT RECORD [ pos(0: 0..3): NIBBLE, size(0: 4..7): NIBBLE];The pos specifies the most significant bit of the field (the most significant bit of a word is bit zero), and size is one less than the width of the field in bits (a field never has zero width). Figure 7.2 illustrates some examples of field specifiers.
Note that fields described by field specifiers do not cross word boundaries.
In addition to field specifiers, some instructions include an offset, a quantity added to the pointer to obtain the address of the word containing the field. This offset is included in a field descriptor.
FieldDesc: TYPE = MACHINE DEPENDENT RECORD[ offset(0: 0..7): BYTE, field(0: 8..15): FieldSpec]];
The following routines are used by the field instructions (and elsewhere) to perform the basic functions of field extraction and insertion:
MaskTable: ARRAY [0..WordSize) OF UNSPECIFIED = [ 1, 3, 7, 17B, 37B, 77B, l77B, 377B, 777B,1777B, 3777B, 7777B, 17777B, 37777B, 77777B, 177777B]; ReadField: PROCEDURE [source: UNSPECIFIED, spec: FieldSpec] RETURNS [UNSPECIFIED] = BEGIN shift: CARDINAL[O..WordSize); IF spec.pos + spec.size + 1 > WordSize THEN ERROR; shift WordSize - (spec.pos + spec.size + 1); RETURN[And[Shift[source, -shift], MaskTable[spec.size]]]; END; WriteField: PROCEDURE [dest: UNSPECIFIED, spec: FieldSpec, data: UNSPECIFIED] RETURNS [UNSPECIFIED] = BEGIN mask: UNSPECIFIED; shift: CARDlNAL[O..WordSize); IF spec.pos + spec.size + 1 > WordSize THEN ERROR; shift WordSize-(spec.pos + spec.size + 1); mask Shift[MaskTable[spec.size], shift]; data And[Shift[data, shift], mask]; RETURN[Or[And[dest, Not[mask]], data]]; END;
Design Note: If an instruction contains a field specifier in which pos + size + 1 > WordSize, the results are undefined.
The read field instructions push a field from a word in memory onto the stack. They righ-justify the word and supply high-order zeros if necessary.
RF: PROCEDURE = BEGIN desc: FieldDesc = GetCodeWord[]; ptr: POINTER = Pop[]; Push[ReadField[FetchMds[ptr + desc.offset], desc.field]]; END;
ROF: PROCEDURE = BEGIN spec: FieldSpec = GetCodeByte[]; ptr: POINTER = Pop{]; Push[ReadField[FetchMds[ptr], spec]]; END;
RLF: PROCEDURE = BEGIN desc: FieldDesc = GetCodeWord[]; ptr: LONG POINTER = PopLong[]; Push[ReadField[Fetch[ptr + LONG[desc.offset]], desc.field]]; END;
RLOF: PROCEDURE = BEGIN spec: FieldSpec = GetCodeByte[]; ptr: LONG POINTER = PopLong[]; Push[ReadField[Fetch[ptr], spec]]; END;
RLFS: PROCEDURE = BEGIN desc: FieldDesc = Pop[]; ptr: LONG POINTER = PopLong[]; Push[ReadField[Fetch[ptr + LONG[desc.offset]], desc.field]]; END;
RCFS: PROCEDURE = BEGIN desc: FieldDesc = Pop[]; offset: CARDINAL = Pop[]; Push[ReadField[ReadCode[offset + desc.offset], desc.field]]; END;
Programming Note: The Read Code Field Stack instruction is used for accessing constant structures (arrays and records) located in the current code segment when the offset of the word containing the field is not constant.
RLIPF: PROCEDURE = BEGIN pair: NibblePair = GetCodeByte[]; spec: FieldSpec = GetCodeByte[]; ptr: POINTER = FetchMds[LF + pair.left]; Push[ReadFieldfFetchMds[ptr + pair.right], spec]]; END;
RLILPF: PROCEDURE = BEGIN pair: NibblePair = GetCodeByte[]; spec: FieldSpec = GetCodeByte[]; ptr: LONG POINTER = ReadDblMds[LF + pair.left]; Push[ReadField[Fetch[ptr + LONG[pair.right]], spec]]; END;
The write field instructions pop a value from the stack into a field of a word in memory. The value is right-justified in the field, ignoring leftover significant bits. Write Swapped Zero Field takes the pointer and the data in the opposite order on the stack, so that the pointer can be obtained using a Recover instruction.
WF: PROCEDURE = BEGIN desc: FieldDesc = GetCodeWord[]; ptr: POINTER = Pop[]; data: UNSPECIFIED = Pop[]; StoreMds[ptr + desc.offset] WriteField[ FetchMds[ptr + desc.offset], descfield, data]; END;
WOF: PROCEDURE = BEGIN spec: FieldSpec = GetCodeByte[]; ptr: POINTER = Pop[]; data: UNSPECIFIED = Pop[]; StoreMds[ptr] WriteField[FetchMds[ptr], spec, data]; END;
WLF: PROCEDURE = BEGIN desc: FieldDesc = GetCodeWord[]; ptr: LONG POINTER = PopLong[]; data: UNSPECIFIED = Pop[]; Store[ptr + LONG[desc.offset]] WriteField[ Fetch[ptr + LONG[desc.offset]], desc.field, data]; END;
WLOF: PROCEDURE = BEGIN spec: FieldSpec = GetCodeByte[]; ptr: LONG POINTER = PopLong[]; data: UNSPECIFIED = Pop[]; Store[ptr] WriteField[Fetch[ptr], spec, data]; END;
WLFS: PROCEDURE = BEGIN desc: FieldDesc = Pop[]; ptr: LONG POINTER = PopLong{]; data: UNSPECIFIED = Pop[]; Store[ptr + LONG[desc.offset]] WriteField[ Fetch[ptr + LONG[desc.offset]], desc.field, data]; END;
WSOF: PROCEDURE = BEGIN spec: FieldSpec = GetCodeByte[]; data: UNSPECIFIED = Pop[]; ptr: POINTER = Pop[]; StoreMds[ptr] WriteField[FetchMds[ptr]], spec, data]; END;
The put swapped field instructions leave the pointer on the top of the stack.
PS0F: PROCEDURE = BEGIN WSOF[]; Recover[]; END;
PSF: PROCEDURE = BEGIN desc: FieldDesc = GetCodeWord[]; data: UNSPECIFIED = Pop[]; ptr: POINTER = Pop[]; StoreMds[ptr + desc.offset] WriteField[ FetchMds[ptr + desc.offset], desc.field, data]; Recover[]; END;
PSLF: PROCEDURE = BEGIN desc: FieldDesc = GetCodeWord[]; data: UNSPECIFIED = Pop[]; ptr: LONG POINTER = PopLong[]; Store[ptr + LONG[desc.offset]] WriteField[ Fetch[ptr + LONG[desc.offset]], desc.field, data]; Recover[]; Recover[]; END;Previous [TOC] Next
[last edited 5/20/99 12:08AM]