Page Numbers: Yes X: 530 Y: 10.5" First Page: 17
Margins: Top: 1.3" Bottom: 1"
Heading:
D0 MicroAssemblerEdward R. Fiala11 July 1980
16. Assembling Data for RM
RM addresses are allocated by RV statements in one of the following ways:
RV[name, disp, P1, P2, ... , P7];
RV[name, , P1, P2, ... , P7];
RV[name, disp];
RV[name];
RV[name, , value];
The first argument "name" is the name of the RM address to which you will subsequently refer in instructions.
The second argument "disp" is a displacement between 0 and 77. This specifies the low six bits of the RM address. The top two bits are determined by the top two bits of the task number, declared by the last SetTask statement. If "disp" is omitted, the RM address is allocated at the last location plus 1.
The remaining 7 arguments are parameters or integers summed to determine the value loaded into that location. If all of these are omitted, then the location will be uninitialized.
Avoid assigning useless initial values to variables because this will prevent the "Compare" function in Midas (which compares the microstore image against what you loaded) from reporting fictitious errors. In a system microprogram (as opposed to a diagnostic), any occurrence of a variable with an initial value is probably a programming error since it requires reloading the microcode to restore the initial value. Hence, you probably should initialize such registers with your program.
Also, boot microcode does not allow any memories except IM to be initialized from the boot EPROM, and current software for loading microcode overlays is also limited to IM. For these reasons the usual practice is to put code for initializing RM into a "throwaway" page which can be overwritten by something else after it is executed.
The hardware imposes a number of strange constraints upon RM placement. For example, addresses used as base registers must (usually) be even, addresses for PFetch4/PStore4 must (usually) be origined 0 mod 4, and addresses for PFetch2/PStore2 must (usually) be at even locations (exception: stack double-words). Also, RM is partitioned so that only locations 0 to 77 are accessible to tasks 0 to 3, 100 to 177 to tasks 4 to 7, 200 to 277 to tasks 10 to 13, and 300 to 377 to tasks 14 to 17. Tasks 1 to 3 in each group of 4 are further limited because the task number is OR’ed into high address bits in various ways. These constraints will be a source of many program bugs.
You must be careful to assign a "disp" that satisfies all the uses of each RM address. If you screw up, the assembler will indicate an error when you illegally reference the RM location in an instruction.
Note: Suppose that you want tasks 10, 11, 12, and 13 to share a section of microcode but use independent RM locations. Then you do a SetTask[10] before that section of the program, and you allocate a block of RM locations in the range 100-117 and refer to these locations in the program; you also allocate parallel blocks of RM locations in the ranges 120-137, 140-157, and 160-177 for use by tasks 11, 12, and 13, respectively. In this way, the program will do what you want. If the four tasks have some other RM locations that are shared, allocate these in the range 160-177, so that they will be accessible to all four tasks.
Sometimes you may want to use several different names to refer to the same RM location. To do this, define the first name with RV, as above; you can then define synonyms in several ways, one of which is as follows:
RM[FOO1, IP[FOO]];
This defines the address FOO1 at the same location as the (previously-defined) address FOO.
17. Assembling Data Items In the Instruction Memory
If you do not want to clutter RM with infrequently referenced constants or variables, and if you are willing to cope with the hardware kludges for reading/writing the instruction memory as data, then you can store data items in IM.
To assemble a table of data in the instruction memory:
Set[T1Loc,100];
IMData[(TABLE1: LH[P1, ... , P8] RH[P1, ... , P8], At[T1Loc])];
IMData[(LH[P1, ... , P8] RH[P1, ... , P8], At[T1Loc,1])];
...
where TABLE1 is an IM address symbol equal to the location of the first instruction in the table, P1, ..., P8 are parameters or integers. LH stores the sum of up to 8 parameters in the left-half of the IM word and RH, in the right-half; these macros do not allow you to specify the remaining four bits of the 44-bit instruction--the assembler will ensure that the data has valid parity by storing a 0 or 1 in the low-order bit of the RX@ field (which is one of the four bits you cannot specify). "At" is discused in the "Placement" section later.
18. General Comments on Instruction Statements
The general forms of an instruction statement are as follows:
TAG:branch clause, T←rmaddr←(A phrase) and (B phrase), function, placement;
TAG:branch clause, T←A phrase, B phrase, function, placement;
TAG:branch clause, A phrase, RMAddr←B phrase, function, placement;
-or-
TAG:branch clause, PFetch1[rbase, rdest, f2], placement;
TAG:branch clause, PFetch1[rbase, rdest], f2, placement;
where the first three examples are "regular" instructions and the last two, "memory reference" instructions.
Rule: TAG is an optional IM address symbol or label; it must appear first in the instruction statement.
TAG may be referenced from branch clauses in other instructions, as discussed in the "Branching" section, and it will appear in the output file for use in debugging.
Rule: Clause order is totally arbitrary; it doesn’t matter which ones appear first in the statement. However, you might wish to follow a consistent ordering convention for program readability.
Placement clauses, A phrases, B phrases, Alu clauses, and memory reference clauses are discussed in separate sections and only outlined here.
The Alu operation in the first example is a function of both H1 and H2; it involves only H1 in the second, and only H2 in the third. In this manual, the H1 and H2 data paths are referred to as "A" and "B," respectively, and the output of the Alu, loaded into H3P, is referred to as "LU."
Rule: In an Alu operation involving both A and B, the A phrase must appear to the left of the B phrase.
Rule: An F1 or F2 function that either sources or sinks A will appear in the A phrase; one that either sources or sinks B will appear in the B phrase; one that involves neither A nor B will appear as a separate clause in the instruction.
Data-routing clauses have one or more "←"’s in them and require parentheses in some places to cause evaluation in the correct order. One of these clauses is evaluated from right-to-left, or from "sources" to "sinks."
If there is only one source and one sink in the clause, no problem: simply write "sink←source", e.g.:
T←RMAddr,The assembler figures out how to route data from the RM address RMAddr onto A, through the Alu, and into T.
RMAddr←34C,Again the assembler figures out how to construct the constant 34, route it onto B, through the Alu, and into the RM address RMAddr.
When you have A or B phrases embedded in Alu expressions, then you have to use parentheses, e.g.:
T←(StkP←RMAddr)+1,The assembler routes RMAddr onto A, loads StkP from A with an F2 function, selects the A+1 Alu operation, and sets the LT bit in the instruction so that T will be loaded from the Alu.
T←(LoadTimer[RMAddr])+(SAluf←T)
The assembler routes RMAddr onto A, selects the LoadTimer function in F1, routes T onto B, selects the SAluf← function in F2, selects the "A+B" Alu operation, and sets the LT bit to load T from the Alu output.
In assembling the first clause above, the assembler proceeds in the following way:
a.RMAddr is looked up first and recognized as an RM address. Error checks ensure that RMAddr can legitimately be referenced by the current task. If so, the proper value is assembled for the RSel, RMod, RX, and MemIns fields of the instruction. The assembler’s macros leave a neutral symbol "RB" at the end of this evaluation to check for routing errors later.
b."StkP←" is looked up next; this macro expands to store the correct code in the F2 field of the instruction and leaves a neutral symbol "A←" to check for routing errors.
c."A←RB" is looked up; because routing RB onto A is legal, this "connection macro" is defined and expands to leave the neutral "A." (If the routing had been illegal, then Micro would have printed an error message like "A←RB undefined.")
d."A+1" is looked up; this macro expands to store the correct code in the AluF instruction field and leaves behind the neutral symbol "LU" to check for routing errors.
e.Then "T←" is looked up; it is a macro that expands to store a 1 into the LT field of the instruction, leaving the neutral "LU←."
f.Finally, "LU←LU" is looked up; it is a "connection" macro that does nothing and leaves behind the neutral "LU"; since there is no more text in the clause this final neutral is thrown away.
Note: The "()" in the first example above are not optional. If you omit them, the assembler would look up "RMAddr+1", which would be undefined.
One general idea in the above is that at each stage the source is routed only as far as necessary to load it into the destination.
Note:T←StkP←RMAddr,is legal
T←StkP←(RMAddr),is legal
T←(StkP←RMAddr),is legal
StkP←T←RMAddr,is illegal
The last clause above is illegal because, by the time the assembler recognizes StkP←, it has already routed the source data past A and through the Alu, and there is no path from the Alu to StkP. The assembler is not clever enough to remember that the data originally started on A.
Here are some more "()" examples:
T←(RMAddr)+T,is legal--"()" are required around RM addresses in combination with other stuff.
T←RMAddr,is legal--"()" optional around an RM address all by itself
T←(RMAddr)+(T),is legal--"()" optional around "T."
T←((RMAddr)+T),is legal--extra "()" around an entire source always OK
(T←(RMAddr)+T),is legal
T←T+(RMAddr),is illegal--you cannot reverse the A and B phrases.
RMAddr←(RMAddr)+(2C)is legal--"()" required around constants in combination with other stuff.
The last two examples at the beginning of this section show the form of "memory reference" instructions. In the first example, the F2 field is used instead of T to specify the displacement for the reference; in the second example, T is used as the displacement so F2 is available to specify some function. Memory reference clauses are discussed later.
19. RM and STK Phrases
The hardware complicates RM references by providing only six bits of RM address in the instruction. The remaining two address bits come from the task number. The programmer must declare the task number with SetTask before any references.
RM addresses can source A and can be used in Alu phrases. In this case, the RM address has to be enclosed in "()."
RM addresses can be used as sinks for Alu operations and Alu sources (which the assembler routes through the Alu). For these simply write the register name followed by "←".
Some examples of clauses involving RM are as follows:
RMAddr←(RMAddr)+TUse in an Alu expression and as a sink.
RMAddr←TThe assembler automatically routes T through the Alu and into RMAddr.
PCF[RMAddr]←PCF[RMAddr]You really mean RMAddr←PCF[RMAddr], but due to the vagaries of the assembler you write it this way.
T←(PCF[RMAddr])+TUse with cycler-masker operation PCF.
T←PCF[RMAddr]+T"()" not mandatory with these.
T←RMAddr"()" optional when RMAddr not combined with other text.
DB←RMAddrUse with an A sink.
T←(SB←RMAddr)+TUse with an A sink inside an Alu operation.
The assembler will detect all illegal RM references. In other words, if the RM word is unaddressable by the currently selected task, or if it is required to be even or quadaligned, the assembler will check for this and indicate any error.
References to the stack always read the word pointed to by StkP, then adjust the stack pointer in the ways discussed in the "A Phrases" section; if the stack is written, the modified address is used for the write. The various Stack terms may be used interchangeably with RM addresses where they are legal. Also note the following:
Stack&+1←(Stack&+1)+Tis legal.
Stack&+1←(Stack)+Tis illegal.
The last example is illegal because you must use the same StkP modifier in both read and write parts of the instruction.
Occasionally you may wish to create a constant whose value is an RM address. To do this you can use the following kludge:
B←IP[RMAddr]C
This puts a constant whose value is the address of RMAddr onto B.
20. Cycler-Masker Phrases
The semantic cycler-masker operations, operating on RM data, are as follows:
Lsh[RA, n]Left shift the RM address RA n positions (n = 1 to 17).
Rsh[RA, n]Right shift RA n positions (n = 1 to 17).
LdF[RA, pos, size]Right-justify or load an arbitrary field from RA; POS is the left bit of the field and SIZE is the number of bits in the field.
Rcy[RA, n]Right cycle RA n positions (n = 1 to 17).
Lcy[RA, n]Left cycle RA n positions (n = 1 to 17).
Dispatch[RA, pos, size]Loads APC with any field of size less than 4 bits from RA; the following instruction must contain a Disp control clause to carry out the dispatch to the location at Page[0:3],,JA[0:3],,APC[14:17].
RHMask[RA]= RA & 377.
LHMask[RA]= RA & 177400.
Zero= 0.
FixVA[RA]= Rsh[RA,1] & 40100.
Formn[RA]= RA & n (n = 0 to 10).
Form-n[RA]= RA & -n (n = 2 to 10).
Nib0Rsh8[RA]= Rsh[RA,10] & 360, which is the first 4-bit nibble in RA right-shifted 10.
For these operations, the assembler fabricates values for F1, F2, and BSel to cause the appropriate shift and mask. RA may be any normal RM address or one of the special ones that have RMOD=1 and don’t use F1 or F2 in their specification.
In addition to the cycler-masker phrases discussed here, the various forms of the NextData and NextInst functions discussed later use the cycler-masker to extract either the left or right byte from the selected RM word.
21. A Phrases
All of the A sources use the RMod and RSel instruction fields; some also use the F1 and F2 fields in conjunction with these. An A source can be any of the following:
ADummy source for clause splitting. It indicates that the source for some sink is Alua (You should never need to use this because normally the Alua sink and source are written together separated by "←" or the Alua source is embedded in the Alu phrase.).
RAddrAn RM address.
StackRM addressed by StkP
Stack&-nRM addressed by StkP; StkP←StkP-n (n = 1, 2, or 3) after the read (and before the write, if any).
Stack&+nRM addressed by StkP; StkP←StkP+n (n = 1, 2, or 3) after the read (and before the write, if any).
PCF[RAddr]RAddr must be quadaligned--i.e., its low two address bits must be 0.
SB[RAddr]RAdr must be quadaligned.
DB[RAddr]RAdr must be quadaligned.
NextInst[RAddr]Instruction dispatch; incorporates both the NextInst F1 function and the PCF[RAddr] cycler masker operation, so RAddr must be quadaligned. See the "NextData and NextInst" section.
CNextInst[RAddr]Like NextInst; see the "NextData and NextInst" section.
NextData[RAddr]Incorporates both the NextData F1 function and the PCF[RAddr] cycler masker operation, so RAddr must be quadaligned; see the "NextData and NextInst" section.
CNextData[RAddr]Like NextData; see the "NextData and NextInst" section.
BBFA[RAddr]Also a dispatch (no special restrictions on RAddr); uses F1 and F2.
BBFB[RAddr]Uses F1.
WFA[RAddr]Uses F1.
WFB[RAddr]Uses F1.
RF[RAddr]Uses F1.
BBFX[RAddr]Uses F1.
The A sources below use RMod and RSel to specify external R-bus sources and use the cycler-masker to extract individual registers from multi-field sources.
GetRSpec[n]Used to place a multi-field word on A. n is a 7-bit value whose leading bit is placed in RMOD and next 6 bits in RSEL. You should normally prefer to use one of the explicit macros given below instead of GetRSpec[n].
SStkP&NStkP= GetRSpec[103]
SStkPSaved StkP; uses F1 and F2.
NStkPStkP (ones comlemented); uses F1 and F2.
ALUResult&SAluF
ALUResultUses F1 and F2.
SAluFUses F1 and F2.
MemSyndrome
MemError
Cycle&PCXF= GetRSpec[127]
CycleControlUses F1 and F2 (= DBXReg..MWXReg)
DBXRegUses F1 and F2.
MWXRegUses F1 and F2.
PCXRegUses F1 and F2
PCFRegUses F1 and F2.
PrinterUses F2.
Timer= GetRSpec[133]
DBSBUses F2.
RS232
MNBRUses F2.
APCTask&APC= GetRSpec[143]
APCTaskUses F1 and F2.
APCUses F1 and F2.
APC&APCTask= GetRSpec[143] (synonym)
CTask&NCIA
CTaskUses F1 and F2.
NCIAUses F1 and F2 (= CIA ones complemented).
CSData
Page&Par&Boot= GetRSpec[157]
PageUses F1 and F2.
ParityUses F1 and F2.
BootReasonUses F1 and F2.
Rule: Those of the above sources that accept a normal RM address as an argument, namely PCF[RA], SB[RA], DB[RA], WFA[RA], BBFB[RA], WFB[RA], RF[RA], and BBFBX[RA], require that this same form be used when writing RA in the same instruction; i.e.:
PCF[RMAddr]←(PCF[RMAddr])+Tis legal
RMAddr←(PCF[RMaddr])+Tis illegal
This unfortunate kludge is required because PCF[RMAddr] will set RMOD to 1, while RMAddr← will set RMOD to 0, causing an assembly error.
Sinks for A are the Alu and a number of other registers selected by functions. A sinks can be any of the following:
any Alu expression or Alu sink (T←, RAddr←, or LU←)
A←No-op sink--simply routes the selected A source onto Alua.
APCTask&APC←Uses F1 and F2
Restore←Uses F1 and F2
StkP←Uses F2
CycleControl←Uses F2
SB←Uses F2
DB←Uses F2
MNBR←Uses F1 and F2
PCF←Uses F1 and F2
Printer←Uses F2
21. B Phrases
B sources can be any of the following:
BDummy source for clause splitting
ConstantsUses FF--see earlier constant section
T
B sinks can be any of the following:
B←No-op destination--simply routes source onto B.
RS232←Uses F1
SAluF←Uses both F1 and F2.
CTD←Uses both F1 and F2.
any Alu expression or Alu sink
23. Alu Clauses
In the Alu operations given below, the "A" and "B" components may be any A and B phrases, respectively:
B
A
A and B
A or B
A xor BA#B
A and not B
A or not B
A xnor BA=B
A+1
A+B
A+B+1
A-1
A-B
A-B-1
A SAlufOp B
Rule: For all Alu operations except the "A" and "B" operations, the A phrase must always be enclosed in "()"; the B phrase must be enclosed in parentheses unless it is T; "()" are optional around T.
You must not, for example, write "A and not B" as "A and (not B)"; in other words, it is illegal to put random "()" in the name of the Alu expression, even though that may clarify the meaning. If you tried to do this, the assembler would fail to recognize "not B" and flag an error. The "()" are only legal around the "A" and "B" parts of the Alu phrase.
The arithmetic operations, "A-1," "A+1," "A+B," "A+B+1," "A-B," and "A-B-1" may optionally be accompanied by the standalone clause "UseCOutAsCIn," which causes the Alu carry-in to be the carry-out of the last Alu operation for the same task. The value of the carry-out may be frozen across instructions by means of the FreezeResult standalone function.
"UseCOutAsCIn" cannot be used with the "A" operation because the logical rather than the arithmetic form of the "A" operation is used by the hardware.
Legal sinks for Alu phrases are:
RAddr←Any RM address
Stack←Stack sinks
Stack&+n←
Stack&-n←
T←
LU←This null sink is necessary when the Alu operation must be "A" or "B" for a subsequent branch condition or to interlock/not interlock an RM reference, even though no real destination is being loaded with the Alu output.
Here are some examples of Alu clauses:
T←(RAddr)+T"()" optional around T, mandatory around RAddr.
T←RAddr"()" optional around RAddr uncombined.
T←(RAddr)also ok.
RAddr←(RAddr) xor (377C)"()" mandatory.
RAddr←T←377C"()" optional when constant uncombined.
T←RAddr←(377C)also ok.
24. Memory Reference Instructions
Memory reference instructions have a different form from regular instructions, as discussed in the hardware manual. Branch and placement clauses are identical to those in regular instructions, and the F2 clause, if any, is identical to that in a regular instruction. The rest of the instruction is a single clause in one of the following forms:
PFetch1[RBase, RAddr<, F2>]OddPFetch1[RBase, RAddr<, F2>]
PFetch2[RBase, RAddr<, F2>]OddPFetch2[RBase, RAddr<, F2>]
PFetch4[RBase, RAddr<, F2>]OddPFetch4[RBase, RAddr<, F2>]
PStore1[RBase, RAddr<, F2>]OddPStore1[RBase, RAddr<, F2>]
PStore2[RBase, RAddr<, F2>]OddPStore1[RBase, RAddr<, F2>]
PStore4[RBase, RAddr<, F2>]OddPStore1[RBase, RAddr<, F2>]
IOFetch4[RBase, Device<, F2>]OddIOFetch4[RBase, RAddr<, F2>]
IOFetch20[RBase, Device<, F2>]OddIOFetch20[RBase, RAddr<, F2>]
IOStore4[RBase, Device<, F2>]OddIOStore4[RBase, RAddr<, F2>]
IOStore20[RBase, Device<, F2>]OddIOStore20[RBase, RAddr<, F2>]
XMap[RBase, RAddr<, F2>]OddXMap[RBase, RAddr<, F2>]
Input[RAddr<, F2>]
Output[RAddr<, F2>]
ReadPipe[RAddr]
Refresh[RAddr]
IOStore16, OddIOStore16, IOFetch16, and OddIOFetch16 are synonyms for IOStore20, OddIOStore20, IOFetch20, and OddIOFetch20, respectively.
In these clauses, "RBase" is an RM address subject to the same constraints as an RM reference in a regular instruction; i.e., it must be in the group of 100 accessible to the current task and must not be in the subrange 0-17 of that group unless the task selected by SetTask is 0 mod 4; in addition, it must normally be an even address, as discussed in the hardware manual. When it must be even, you write, for example, "PFetch1[ ... ]"; in the rare case when it must be odd, you write "OddPFetch1[ ... ]." Also, the various forms of RM addressing that require RMod=1 cannot be used when specifying a base register (i.e., Stack, Stack&+n, Stack&-n, PCF[..], SB[..], and DB[..] won’t work); this is illegal because the DF2 bit in memory reference instructions coincides with RMod in regular instructions.
When the optional F2 argument is omitted, the displacement relative to the base register is taken from T, and you may use F2 for an unrelated function in a separate clause. If you supply the F2 argument, which must be an integer less than 20, that value is stored in the F2 field of the instruction and used instead of T. See the hardware manual for details on how this works.
Note that memory instructions use the Alu to add the low-half base register to the displacement; the result of this addition may be tested in the next instruction if desired. For example, it is sometimes useful to branch on the Alu carry in the next instruction, so that the high part of the base register can be updated when crossing a 64K memory boundary.
The RAddr argument may be either "Stack" or an RM address accessible to the task. Since the current task is OR’ed into the top 4 bits of the 8-bit SrcDest field that specifies RAddr, RAddr is further constrained to satisfy the relation: (RAddr or (CTask lshift 4)) = RAddr. In addition, RAddr must not be 0 (which is the value assembled for "Stack"); for PFetch2/PStore2 it must be even and for PFetch4/PStore4 it must be quadaligned.
"Must be even" and "must be quadaligned" are a little too strong. If you violate this rule, the assembler will print a warning message which you are free to ignore. In this case, the hardware will transfer the double-word/quad-word into the two/four RM locations beginning with the one you specify and wrapping around at 20-word boundaries (i.e., if you specify RM 36 as the address for a PFetch4, the data will be delivered to 36, 37, 20 and 21). The reason for the warning is that RM interlocking will not occur correctly, as discussed in the next section.
The Input and Output operations use the SrcDest instruction field to specify the RAddr argument they require, but do not use any base register. Consequently, you are permitted to specify an RM address as a separate clause, as in a regular instruction--this might be useful if you want to use either the R Odd or R<0 branch condition in the same instruction, or if you can somehow setup an Alu result for a branch condition in the next instruction, or if you want to interlock a preceding PFetch or PStore for some reason. You may not, however, specify any RM address that requires RMod=1. The assembler defaults the RM address to 0 if you don’t specify one.
25. RM Interlocking
The hardware "interlocks" RM references to ensure that following a memory reference you neither read an RM word before the memory controller has filled it from storage nor overwrite an RM word before the memory controller has deposited it in storage; an instruction will be repeatedly aborted and reexecuted until it is safe to proceed. However, the hardware interlock is not foolproof, so there are some programming requirements of which you must be aware.
First, every instruction is reading some RM word (word 0 in the current 100-word region, if you don’t explicitly mention an RM address), but the hardware read interlock will happen only when the Alu operation involves A somehow--no interlock occurs on an LU←T Alu operation, which does not involve A. However, sometimes you will utilize an RM word without routing it into the Alu. For example:
StkP←RAddr, RAddr←T;Load an A destination from RAddr, LU←T
RAddr, Skip[R Odd];Use the R<0 or R Odd branch conditions
In these situations, if RAddr is involved in a preceding fetch-type memory reference (PFetch1/2/4, ReadPipe, XMap, or Input) which is still in MC1/MC2, the hardware interlock will not occur and the instruction will erroneously procede without waiting for the memory controller to fill the word from storage.
The assembler has some features to help avoid programming errors in this situation. First, the default Alu operation in an instruction is LU←T, but when you write a clause which loads some A destination from RM, the default is changed to LU←A which will invoke the read interlock. In other words:
StkP←RAddr;= LU←StkP←RAddr, so the interlock will occur
A←RAddr;= LU←A-RAddr, so the interlock will occur
RAddr, Skip[R Odd];didn’t say A←RAddr, so LU←T is still the default and interlock will NOT occur--potential undetected programming error here.
StkP←RAddr, RAddr←T;LU←A default overruled by LU←T, so no interlock will occur, but the assembler will issue a "No register interlock" warning to you in this case; you can ignore the warning if you want to, but...
StkP←RAddr, RAddr←T, NoRegILockOK;
suppresses the "no register interlock" warning message; you should add the "NoRegILockOK" clause to instructions which do not invoke the register interlock if you are sure that not having the register interlock is ok.
Another set of potential problems occurs when the two RM words involved in PFetch2/PStore2 are not an even-odd pair or when the four RM words affected by a PFetch4/PStore4 are not quadaligned (i.e., the first of the four words does not have two low zeroes in its address). For example, suppose a PFetch2/PStore2 is made to the Stack; if the first of the two RM words is even, all is well and the hardware interlock will abort references to either of the two words involved in the reference; however, if the first of the two RM words is odd, then the hardware will NOT abort references to the second word involved in the reference (It will instead erroneously abort references to the RM word preceding the first word). Similar problems occur when the first RM word of a PFetch4/PStore4 is not quad-aligned.
To assembler will flag potential problems in these situations with a warning message "WARNING: RAddr not even" on a PFetch2/PStore2 or "WARNING: RAddr not quadaligned" on a PFetch4/PStore4 for which the RM address is not aligned. Unlike the example given above, there is no way to suppress these warnings, even though it is legitimate to make unaligned references in some situations. These warning will not occur on PFetch2/PStore2 to the stack.
When you reference a stack word that is potentially the second word of an unaligned and incomplete PFetch2, you have to be very careful to use the Stack&-n form of reference; when you write any stack word that is potentially the second word of an unaligned and incomplete PStore2, you must use the Stack&+n form of reference. These are discussed in the hardware manual.
26. Standalone Functions
The following summarizes standalone functions, each written as a separate clause in the instruction:
BreakPointCauses the instruction to be breakpointed by Midas when debugging.
NopAssembles a no-op instruction.
SpareFunctionUses F1 and F2 (undefined)
ResetErrorsUses F1 and F2
IncMPanelUses F1 and F2
ClearMPanelUses F1 and F2
GenSRClockUses F1 and F2
ResetWDTUses F1 and F2
BootUses F1 and F2
SetFaultUses F1 and F2
ResetFaultUses F1 and F2
UseCTaskUses F1 and F2
WriteCS0&2Uses F1 and F2
WriteCS1Uses F1 and F2
ReadCSUses F1 and F2
D0OffUses F1 and F2
RegShiftUses F2 (ordinarily not written explicitly becuase the sources that require it are individually named, so you probably will never use this)
FreezeResultUses F2
IOStrobeUses F2
ResetMemErrsUses F2
UseCOutAsCInUses F2
SkipDataSee the "NextData and NextInst" section.
CSkipDataSee the "NextData and NextInst" section.
LoadPage[n]Uses F1 and F2; 0 <= n <= 17, where n is an integer equal to the page number; this is used when an ordinary branch clause is used in the next instruction.
LoadPageExternal[n]Like LoadPage; this must be used when the branch clause in the next instruction is a GotoExternal or CallExternal.
At[n1,n2]See the "Placement Declarations" section.
Opcode[n]See the "Placement Declarations" section.
27. Branching
This section discusses branch and dispatch clauses, which are assembled into the JC and JA fields of the instruction. At assembly time, D0Lang produces the correct JC field and sometimes the correct JA field or correct low bit of JA; however, most branch clauses indicate the required branch linkages in extra fields assembled only for MicroD. MicroD then determines a satisfactory placement for the instructions and fills out JA correctly.
The hardware defines goto, call, disp, return, and conditional goto, each represented by codes in the JC instruction field; conditional goto’s may specify any one of eight branch conditions. The least significant bit of JA is used to modify the branch condition on a conditional goto and to modify the action of a return.
The assembly language encodes these possibilities in the following kinds of branch clauses:
DblGoto[t1, t2, bc]Jumps to t1 if the branch condition bc is true, else to t2; MicroD will place t2 at an even location and t1 at the adjacent odd location; the targets must be on the same 400-word microstore page as the instruction containing the DblGoto.
Goto[t1<, bc>]Jumps to t1 if the optional bc is either omitted or true, else falls through to the next instruction inline. t1 will be placed on the same page as the current instruction. If bc is given, the next instrucion inline will be placed at an even location and t1 at the adjacent odd location.
Skip[<bc>]= Goto[.+2<,bc>]
Call[t1]Loads TPC[current task] with the address of the next instruction inline and branches to t1; MicroD will place the next instruction inline at (. & 7400)+((.+1) & 17) and will place t1 on the same page.
Disp[t1]Will dispatch to the the table origined at (t1 & 7760); the programmer must manually place all instructions in the dispatch table with At clauses. The dispatch will transfer control to (t1 & 7760) OR’ed with the low four bits of APC. The instruction immediately preceding Disp must normally be a Dispatch (but conceivably UseCTask or APCTask&APC← might be used instead of Dispatch in some clever situations).
ReturnReturns to the address in APC (JA.7=0).
NIRetReturns to the address in APC (JA.7=1) which will contain 2001+(4*ByteCode); this form of return is used after NextInst.
TaskSpecial kludge that does Call[.+1] in the current instruction and forces a Return in the next instruction inline (which must not have any branch clause); the second instruction inline will be placed at .+1.
GotoExternal[n]Puts a goto code in JC and stores the integer n in JA; no placement constraints imposed and no error checking; usually used after LoadPageExternal[m].
CallExternal[n]Puts a call code in JC and stores the integer n in JA; no placement constraints imposed and no error checking; usually used after LoadPageExternal[m].
DblGotoP[t1, t2, bc]= DblGoto with the on-page constraints removed; the preceding instruction must contain an apropriate LoadPage; no error checking.
GotoP[t1, bc]= Goto with the on-page constraint removed.
CallP[t1]= Call with the on-page constraint removed.
DispP[t1]= Disp with the on-page constraint removed.
SkipP[t1]= Skip with the on-page constraint removed.
In the above forms, t1 and t2 may be instruction labels or one of the following special symbols:
.+3current address + 3.
.+2current address + 2.
.+1current address + 1.
.the address of the current instruction.
.-1current address - 1.
.-2current address - 2.
.-3current address - 3.
Branch conditions may be any of the following:
RegularComplementary
ALU#0ALU=0
CarryCarry’
ALU<0ALU>=0
H2Bit8’H2Bit8
R<0R>=0
R OddR Even
IOAtten’IOAtten
MBMB’
IntPendingIntPending’Uses F2
Ovf’OvfUses F2
BPCChkBPCChk’Uses F2
QuadOvfQuadOvf’Uses F2
TimeOutTimeOut’Uses F2
The complementary form of the branch conditions is provided as a programming convenience. The effect of using a complementary branch condition is to interchange the even-odd constraints on the target pair.
28. NextData and NextInst
The assembler contains a number of macros and placement features to support the NextData and NextInst hardware functions. These functions trap to absolute location 0 in the microstore when the quadword buffer containing data from the opcode stream is exhausted; on a trap, the instruction address in CIA will be saved in TPC iff the instruction containing NextData/NextInst has a Call encoded in its JC field.
The system microcode deals with this situation as follows: First, the trap instruction at 0 contains a "LoadPage[0], Goto[377]" where the Goto sends control to location 377 on the page containing the NextData/NextInst that trapped. Next, the instruction at 377 on that page will contain a "PFetch4[PC,IBuf,4], GotoP[BufferRefill]" or whatever, where BufferRefill is on page 0.
For this reason it is usually necessary to encode a Call in JC to ensure that the trap subroutine will restart at the correct place. However, this will usually be a "fake call" because there is no intention to return from a subroutine to .+1 in the instruction stream. Consequently, the assembler has a bit in its output data format (the OddCall bit) which tells MicroD that, even though the current instruction contains a call, it is not necessary to place the next instruction inline at .+1 in the microstore. Some of the macros discussed below set this bit to avoid imposing an unnecessary placement constraint on the program.
The macros defined for these two functions are as follows:
A←NextInst[RAddr]Instruction dispatch on the next opcode in the opcode stream; PCF[RAddr] addressing selects the word in the quadword-buffer beginning at RAddr containing the next byte; the NextInst F1 function causes the cycler-masker to select the byte in that word according to the low bit of PCF; forces a fake Call to be encoded in JC. It is not necessary to specify any branch clause if the successor is the next instruction inline, but if any branch clause is specified, it must be a (fake) Call.
A←CNextInst[RAddr]Like NextInst, but the JC field is not constrained and any branch clause may be used. However, unless either a buffer refill trap is impossible (because the BPCChk branch condition is false or because PCF is known to be odd) or the return address in TPC has already been setup to cope with a trap, you will have to write an explicit Call into the instruction to ensure that a buffer refill trap will return to reexecute this instruction.
A←NextData[RAddr]Like NextInst but uses the NextData function rather than the NextInst function to obtain the next byte in the opcode stream.
A←CNextData[RAddr]Like CNextInst but uses the NextData function.
SkipDataUses the NextData function to skip the next byte in the opcode stream without referencing it; forces a fake Call to be encoded in JC; does not impose PCF addressing, so any RM address can be referenced (but because the NextData function will use the cycler-masker to select either the left or right byte of the selected RM address, your use of that RM address is limited).
CSkipDataLike SkipData, but does not impose any constraint on JC; any branch clause may be used. If the branch clause is a Call, it will be "real" and the successor will be the next instruction inline; if not a Call, you must be sure that either a trap is impossible or the return address already in TPC is an appropriate return from the trap subroutine.
Here are some examples using these macros:
lu ← NextInst[IBuf];*Successor to NextInst is .+1
Push:Stack&+1 ← T, NIRet;
lu ← NextInst[IBuf], Call[Push];*Successor to NextInst is at the label "Push"
T ← CNextData[IBuf], Call[FixPointer];*Real call with NextData function
T ← (Stack&-1) + T, Goto[Push];
Call[FixT];*Call unnessary because return for possible trap
T ← CNextData[IBuf], Skip[IntPending];*already setup
lu ← Cycle&PCXF, FreezeResult, Skip[R Even];
CSkipData, DblGoto[A,B,Alu<0];*Trap impossible because PCF is odd
DblGoto[A,B,Alu<0];
Note: It is not necessary for NIRet to occur in the instruction immediately after the NextInst/CNextInst; it is all right for other instructions to intervene.
29. Placement Declarations
Currently, you must specify the page on which each instruction is placed. This is done by a declaration of the form:
OnPage[n];
where the integer n is the page number (0 to 17). Subsequently assembled instructions will be placed on the specified page. The OnPage declaration may appear only as a separate statement.
Two other declarations may appear only as clauses in an instruction being assembled; these specify the exact placement of the instruction and also do OnPage, so that subsequently assembled instructions will be placed on the same page. These are:
At[n1]places the current instruction at location n1.
At[n1, n2]places the current instruction at location n1+n2.
Opcode[n]places the current instruction at 2001+(4*n), where n is the opcode (0 to 377)
DispTable[Length,Mask,Value] appearing in an instruction statement causes that instruction to begin a group of Length consecutively-placed statements, where 1 <= Length <= 20. The first instruction will be placed at a location such that And[location,Mask] = Value, where Mask defaults to 17 and Value to 0. It is required that Length+Value be less than 21. Placement of the instruction containing DispTable will be further constrained by whatever OnPage declaration is in force.
Note that DispTable will not handle all dispatch tables; it requires that a table be no longer than 20 with no "holes" and that the instructions in the table appear as consecutive statements in the source program. Whenever these conditions cannot be met, At clauses must be used for the instructions in the table.
Opcode declarations are required for the entry instructions of opcodes; At declarations are required for trap instructions, for instructions in dispatch tables that cannot use DispTable for some reason, and in a few other situations, but MicroD will take care of normal placement constraints automatically.
To assist in defining absolute locations for dispatch tables and manually placed instructions the "LOCA" macro is defined:
Loca[Name,Page,Offset]defines Name as an integer equal to Page*400 + Offset, where Page and Offset are integers.
In addition to the above placement declarations, there are two macros for "reserving" or "unreserving" locations in IM. These macros, which direct MicroD to avoid placing instructions in particular microstore locations, are as follows:
IMReserve[p,w,n]where p, w, and n are integers reserves n locations beginning at word w on page p.
IMUnReserve[p,w,n]unreserves n locations beginning at word w on page p.
For diagnostics, the declaration statement "MidasInit;" may be issued to reserve the several locations on page 0 and all of page 17 for use by the debugger Midas.
30. Tasking Considerations
Since task switching occurs only when an "unrestricted" return is executed, each task must be sure to execute instructions containing returns often enough to satisfy the timing requirements of tasks which are higher (or sometimes lower) in priority. Here "unrestricted" means that the instruction preceding return did not do APCTask&APC← or UseCTask, which disallow task switching.
When an unrestricted return is executed, the highest priority different task requesting service will run, or the emulator if no other tasks are requesting. This has several implications:
First, when a high priority task such as the display driver executes a return when it has not yet blocked, it will lose control to some other task (usually the emulator) until that task does a return. This means that it should not do too many returns else the accumulated interference of lower priority tasks will add up to so large a value that its timing requirements are not satisfied.
Secondly, each task must be sure to execute Returns frequently enough that higher priority tasks receive adequate service.
We can illustrate this with an example. Suppose that the timing requirement of the display driver is that upon issuing a wakeup request it be granted 100 cycles out of the next 360 cycles and that it executes one intermediate return before its final return after blocking. Initial service will be delayed until whatever task is running executes a return, and further delayed until all higher priority tasks are satisfied. At the intermediate return, a lower (or higher task) will run first and then higher tasks until they are satisfied. The total interference of all these other tasks must be less than 260 cycles.
The emulator task for the current AMesa system microcode attempts to satisfy this requirement by tasking at least once every 46 cycles.
Another programming consideration is the exact placement of returns. It is frequently possible to choose one of several places; one good place is immediately before an instruction that will be aborted--e.g., before an instruction that will read an RM word for which a PFetch has just been started or write an RM word for which a PStore has just been started. An undesirable placement is where the current task could proceed because it is not issuing memory references but where another task might experience an abort because MC1 is busy.
31. Microcode Overlays
The barest minimum provisions are made for microcode overlays. When assembling basic system microcode, the IMReserve macro discussed in the "Placement Declarations" section may be used to disallow the use of any set of IM locations by MicroD. Also, any pages totally unused by the system microcode or containing throwaway initialization code are available to overlays. Finally, particular instructions in the system overwritten by particular instructions in an overlay must be manually placed with "At" declarations in both the system microcode and the overlay.
An overlay must use IMReserve to prevent its use of any locations on pages that it shares with system microcode. MicroD has a switch that will write a xxOccupied.Mc file containing IMReserve statements.
Any instructions in the system branched to from an overlay must be manually placed with "At" and vice versa. Then, instead of using LoadPage[n] and Goto[n] or Call[n], you must use LoadPageExternal[n] and GotoExternal[n] or CallExternal[n] in instructions that branch between the system and an overlay.
32. Recent Hardware and Assembler Changes
1.The SUB, EQUATE, ASMMODE, TRACEMODE, and WHILE Micro builtins are a recent addition; the "warning" code for the ER builtin is a recent addition.
2.Some names have been assigned to R-bus sources that were formerly referred to by GetRSpec[n] phrases (e.g., Cycle&PCXF, SStkP&NStkP, CTask&NCIA, and Page&Par&Boot).
3.Bugs in use of Goto with read/write control store were fixed.
4.Some error messages for PFetch2/PStore2 and PFetch4/PStore4 were changed to warnings.
5.The use of independently specified RM addresses with Input[..] and Output[..] was recently made possible.
6.OddBaseOK was removed and OddPFetch1, etc. were recently added.
7.SkipData, CSkipData, CNextInst, Loca, IMData, negative constants, Formn, Form-n, and Nib0Rsh8 are recent additions to the assembler.
8.IF, ELSEIF, ELSE, ENDIF conditional assembly stuff is new.
9.The IMMASK memory handled by MicroD and the DispTable macro which outputs placement constraints for dispatch tables are new.