Page Numbers: Yes X: 530 Y: 10.5" First Page: 32
Margins: Top: 1.3" Bottom: 1"
Heading:
Dorado MicroassemblerEdward R. Fiala21 July 1980
28. RM and STK Clauses
The hardware complicates references to RM by providing only four bits of RM address in the instruction (The Block bit in combination with these four encodes stack reference options for the emulator task). The remaining four address bits come from the RBase register which the programmer must load appropriately before the reference.
Micro will flag an error if an RM read reference is not in the 20-word RM region believed to be pointed at by RBase, and it will use the change-RBase-for-write FF decodes (FF > 77) for RM write references outside the current region. To disable error checking, the programmer must define and reference regionless RVREL addresses, as discussed in the "Assembling Data For RM" section; RVREL addresses should be used only in a section of code which has multiple entries that setup RBase with different values.
The current region is specified by the following statements:
DontKnowRBase;*Contents of RBase is unknown--any read reference is an error
KnowRBase[RGNNAME];
*RBase points at the RM region RGNNAME
KnowRBase[RADDR];
*RBase points at the RM region containing the address RADDR
The TITLE statement also declares DontKnowRBase.
In addition, RBase may be loaded, as discussed earlier, by a clause (FF > 77) of the form:
RBase←RBase[RADDR], -or-
RBase←RBase[RGNNAME],
These both load RBase and declare KnowRBase[RGNNAME], so subsequent instructions will be assembled assuming that the newly-loaded region is in RBase; this is normally what the programmer wants.
Since (at t0 of the entry instruction for an opcode) the IFU initializes RBase to point at the RM 0 or RM 20 region, the programmer should usually insert a KnowRBase[RGN0] or KnowRBase[RGN1] declaration before this.
It is permitted to both read and write RM in one instruction. Normally, the read and write addresses are identical. However, a block of 20 functions (FF < 100) changes the RM address for the write, permitting different registers in the current region to be read and written. These functions are also used when the stack is referenced during the read portion of the instruction and a register in the current RM region during the write part. However, there is no way to read from RM and write the STK in the same instruction.
Another block of 20 functions (FF > 77) changes the RBase part of the RM write address. The assembler will output one of these functions when it believes that RBase does not contain the proper value for writing the selected register.
Warning: If you inadvertently write an illegal statement like:
RADDR2←Fetch←RADDR1; -or-
RADDR2←Fetch←T; -or-
RADDR2←Store←T, MD←Q;
where RADDR2 is not in the current RM region, the assembler will produce a garbage instruction because the change-RBase-for-write function produced for RADDR2← will clobber the FF[0:1] field which must not be clobbered by anything to the left of the Fetch← or Store←. The assembler will not flag this error.
RM addresses can be used as sources for A or B destinations, and this doesn’t require any extra fields in the instruction. RM addresses can be used in Alu phrases, and, in this case, the RM address has to be enclosed in "()".
RM addresses can be destinations for Alu operations, for the Input, InputNoPE, Cnt, Pointers, TIOA&StkP, ALUFMem, and ALUFMRW functions, and for MD--they can also be destinations for Alu sources. For these simply write the register name followed by "←".
Some examples of RM clauses are the following:
RTEMP1←RTEMP0,
uses FF because address written different from address read
Fetch←RTEMP0, Q←RTEMP0,
routes RTEMP0 onto A for a Fetch and onto B to save it in Q. These have to be written as separate clauses.
RTEMP0←(ALUFMRW←RTEMP0), AluF[17]
loads ALUFM[17] from RTEMP0 and saves the old value of ALUFM[17] in RTEMP0.
RTEMP0←Input
routes IOB data onto PD and store in RTEMP0.
It is (just barely) conceivable that you may wish to create a constant whose value is an RM address. To do this you can use the following kludge:
B←IP[RADDR]C
This puts a constant whose value is the address of RADDR onto B.
References to STK are illegal except in the emulator task. The operations read the word pointed to by StkP, then adjust the stack pointer in the ways below, then write the selected item (if any) at the modified or unmodified address. When you write at the modified address, the assembler will automatically supply the ModStkPBeforeW FF decode.
StkP[0:1] select one of two separate stacks, and StkP[2:7] address the word in the stack selected by StkP[0:1].
The hardware StkError signal occurs, waking up task 17, before any instruction in which StkP overflows or underflows. However, when StkP is initially 0, underflow should usually occur when TOS (top-of-stack) is referenced in that instruction but not when the pointer is incremented without reference. Hence, the assembler outputs a different code in RStk when incrementing StkP without reference than when incrementing in conjunction with a read of TOS. In addition, an example in the "Instruction Fetch Unit" chapter of the hardware manual shows several situations where TOS is copied into T without knowing whether the stack is empty; in this case the programmer wants to disable the underflow check, even though he is referencing TOS.
The stuff provided by the assembler assumes that your program will use the stack in the manner envisioned by the hardware design, as follows: An empty stack is represented by StkP containing 0; StkP will sensibly point at either the last item pushed or the item before that, according to programming convention; a push increments StkP and writes in one instruction (or increments StkP in one instruction and writes in the next according to programming convention); a pop can read the item being popped in the same instruction if desired.
Names in the first column below are "sources" for reading the top STK entry; the name modifier, "&+n" or "&-n" controls StkP modification, which always occurs after the top stack entry is read. Second column names are used instead of first column names when it is permissible for the stack to be empty--this aims at the case when TOS is copied into T without knowing whether or not the stack is empty. The third column are "destinations" for writing the top STK entry; here also StkP is modified after the top stack entry is written. The fourth column are destinations that modify StkP before writing; they use the ModStkPBeforeW function (FF<100) to do this. Finally, the fifth column are used to modify StkP in an instruction which makes no reference to STK.
Mod StkPMod StkPMod StkPMod StkP
Read StackNo StkP=0 UFLAfter writeBefore writeNo ref
Stack&+3StackNOUFL&+3Stack&+3←Stack+3←StkP+3
Stack&+2StackNOUFL&+2Stack&+2←Stack+2←StkP+2
Stack&+1StackNOUFL&+1Stack&+1←Stack+1←StkP+1
StackStackNOUFLStack←
Stack&-1Stack&-1←Stack-1←StkP-1
Stack&-2Stack&-2←Stack-2←StkP-2
Stack&-3Stack&-3←Stack-3←StkP-3
Stack&-4Stack&-4←Stack-4←StkP-4
The RStk[0] bit will wind up equal to 1 whenever the StkP=0 underflow check should be made by the hardware. When the stack appears as both a source and a destination in the instruction, the modifiers must match, so the Stack&+i source can only be used with the Stack+i← or Stack&+i← destinations.
StkP← may be loaded from B. RestoreStkP is a standalone function, written as a separate clause.
29. Shifter Clauses
The shifter may be used in two ways. The first way specifies the shift operation and the other control information for the shift in a single instruction. This uses up the ASel, BSel, and FF fields of the instruction while allowing data in either T or a selected RM address to source the shifter.
The second method specifies that shift controls loaded into SHC by a previous instruction (via ShC←B, WF←A, or RF←A) be used. In this case only the ASel field is used up by the shift operation.
The semantic shifter operations when shift controls are specified in FF are as follows (The right-most 20 bits of the 40-bit quantity participating in a cycle are the result.):
a.Left shift data in RM or in T by 0-17;
b.
Right shift data in RM or in T by 0-17;
c.
Right-justify (or load) an arbitrary field from RM or T;
d.
Right cycle the 40-bit quantity RM.T, T.RM, RM.RM, or T.T by 0-17;
e.
Left cycle the 40-bit quantity RM.T, T.RM, RM.RM, or T.T by 0-17;
f.
Deposit RM or T into an arbitrary field of a word coming from MD.
g.
Deposit RM or T into an arbitrary field of a word of zeroes.
For these, the assembler fabricates an FF value describing the shift, sets BSel to cause the FF-controlled shift, and forces ASel to select a shift. "LdF" stands for "load-field" and "DpF" for "deposit-field". The assembler defines the following macros:
T←Lsh[x,count,y];*Invokes ShiftRMask (or ShMDRMask)
T←Rsh[x,count,y];
*Invokes ShiftLMask (or ShMDLMask)
T←LdF[x,size,pos,y];
*Invokes ShiftLMask (or ShMDLMask)
T←DpF[x,size,pos,y];
*Invokes ShiftBothMasks (or ShMDBothMasks)
T←Rcy[u,v,count];
*Invokes ShiftNoMask
T←Lcy[u,v,count];
*Invokes ShiftNoMask
In the above:
count =distance of the shift or cycle (0 <= count <= 17)
size =
number of bits in the field
pos =
number of bits to the right of the field
x =
source for the shift (RADDR or T)
y =
value replacing masked-out bits (0 or MD, defaulted to 0 if the arg is omitted)
u and v =
T and an RADDR in any combination
count =
distance of the shift.
The macros given above invoke the Alu operation in ALUFM 16 which should be "not A"; the equivalent macros named XLsh, XRsh, XLdF, XDpF, XRcy, and XLcy invoke the Alu operation in ALUFM 17.
The above pseudo-operations include all of the conceptual shift options which are possible when the shift function is carried out in the same instruction with loading ShC. There are no other clever uses of FF-controlled shifts that I am aware of.
Notes:
1) The hardware does not allow an arithmetic Alu operation in conjunction with the Shift...Mask, and the assembler does not check for this.
2) The Alu<0, Alu=0, Carry’, and Overflow’ branch conditions apply to the output of the Alu before it has gone through the masker, so the value tested by these is generally different from the value produced by the shift-and-mask. However, when ShiftNoMask is used, the Alu=0 branch condition will still apply.
The hardware also has three ways of loading ShC←. These are implemented by functions. For these you write a separate clause as follows:
RF←A,Read-field (do ShiftLMask later)
WF←A,
Write-field (do ShMDBothMasks later)
ShC←B,
General
where "B" is any B source and "A" any A source.
You should study the shift control figure in the hardware manual to absorb how these work. The most general control of the shifter (ShC←B) allows the 40-bit input quantity to be specified as either T..T, T..R, R..T, or R..R. In other words, you can carry out either a 20-bit or 40-bit cycle by choosing the shifter input control bits appropriately. In addition, if you use the RIsID or TIsID (FF < 100) function in the instruction that carries out the shift, you can replace either the T or RM/STK component of the shift by ID, conceivably useful.
The read-field and write-field operations were intended to support corresponding Mesa operations, which put 8-bit field descriptors on A (usually from ID). Since these use both FF and A, while providing no capabilities beyond that of the FF-controlled shift, it won’t normally be convenient to use them in other contexts.
For situations when you need more flexibility than is provided by the FF-controlled shifts, the assembler defines the following macro for constructing complete 20-bit shifter-control constants in RM:
RVSH[NAME,LMASK,RMASK,TRSEL,COUNT];
"NAME" is the name of the RM variable, TRSEL is 0 to specify R..R as the 40-bit input to the shifter, 1 to specify R..T, 2 for T..R, and 3 for T..T. The interpretation of the other arguments is discussed in the hardware manual. Having constructed such a descriptor, you can load SHC← from it as follows:
ShC←NAME
The shift hardware forces the Alu operation to be the one defined by ALUFM 16 or ALUFM 17. By convention, ALUFM 16 contains the "not A" Alu operation--this is the one ordinarily required because the shifter output appears complemented on A and is normally routed straight through the Alu to the masker.
ALUFM 17 is (will probably be) reserved as a variable. BitBlt will load ALUFM 17 with assorted controls to accomplish the strange things it does.
The Shift...Mask and ShMD...Mask functions may be written with the RADDR input to the shifter as an argument, or the RADDR argument may be omitted if irrelevant or specified elsewhere in the instruction, as follows:
For Aluf = 16For Aluf = 17
ShiftNoMask[RADDR]XShiftNoMask[RADDR]
ShiftLMask[RADDR]
XShiftLMask[RADDR]
ShiftRMask[RADDR]
XShiftRMask[RADDR]
ShiftBothMasks[RADDR]
XShiftBothMasks[RADDR]
ShMDLMask[RADDR]
XShMDLMask[RADDR]
ShMDRMask[RADDR]
XShMDRMask[RADDR]
ShMDBothMasks[RADDR]
XShMDBothMask[RADDR]
where RADDR may be any RM address including the stack sources discussed earlier. The difference between Shift... and ShMD... is that the former replaces masked out bits with 0, while the latter replaces masked out bits with MD from the memory. All of these shift-and-mask functions are treated like Alu operations, so the result may be routed into an RM address or T.
Here are some examples:
RTEMP←ShiftLMaskRM address specified elsewhere in the instruction
T←ShiftRMask[RTEMP]
RM address specified in the shift expression
RTEMP←Lsh[RTEMP,3,0];
Use of Lsh macro--masked out bits replaced by zeroes
RTEMP←Lsh[RTEMP,3]
Use of Lsh macro--masked out bits replaced by zeroes
RTEMP←Lsh[T,3];
Use of Lsh macro with source data for shift from T
T←Lsh[T,3,MD];
Use of Lsh with MD replacing masked-out bits
T←Rsh[RTEMP,17];
Right-shift data in RTEMP 17 positions
T←Rcy[RTEMP,T,3];
Right-cycle the 40-bit quantity RTEMP..T by 3 positions
T←Lcy[T,RTEMP,3];
Left-cycle T..RTEMP 3 positions
T←DpF[T,3,10,MD];
Deposit T[15:17] into MD[6:10]
T←LdF[RTEMP,2,12];
Right-justify RTEMP[4:5] and leave result in T
When the shifter is used, the 4-way multiplexor for other A inputs is normally disabled by the hardware. However, if you write another A source clause to the left of the shift-and-mask clause in the instruction statement, then that source (coded by the FF field) will be ORed with the shifter output on A before going through the Alu, e.g.:
A←T, T←ShiftLMask[RTEMP]’Or’ shifter output with data from T
would shift the 40-bit quantity RTEMP..T according to the control in ShC, OR that with T, NOT this in the Alu, clear some of the left-most bits in the result according to the LMask specified in ShC, and finally load this into T. Since the shifter output is complemented on A, the actual data appearing at the masker inputs is [shifter and not T]. Hence, the shifted data is masked once by "not T" and then again by the selected masks.
30. ALU Clauses
The operations performed by the Alu were given in the section on ALUFM. In "not A and not B", for example, "A" and "B" may be, respectively, any A or B sources.
Rule: The safe way to write the Alu phrase is to enclose the A and B sources in "()", e.g., "not (RADDR) and not (T)". However, you may omit the "()" around "ID", "T", "Q" or "MD", if you want to (and assembly is slightly quicker when you omit the "()").
Rule: You must not write "not A and B" as "(not A) and B". In other words, it is illegal to put random "()" in the Alu phrase, even though that may clarify the meaning. If you tried to do this, the assembler would recognize "not A" as an Alu phrase and then give you an error like "PDANDB undefined". The "()" are only legal around the "A" and "B" parts of ALU expressions.
Rule: When used in conjunction with the Alu Lsh 1, Lcy 1, Rsh 1, Rcy 1, BRsh 1, or ARsh 1 functions, the above would be written like "(not (RADDR) and not (T)) Lsh 1"; i.e., the entire Alu operation is enclosed in "()" followed by the "Lsh 1" or whatever.
The last carry out of the Alu can be xor’ed with the carry from the ALUFM memory. This is caused by the XorSavedCarry function, written as a separate clause. Carry20 is also written as a separate clause.
If you have not defined the arithmetic Alu operation with the kind of carry bit you need, then you must explicitly write XorCarry as a separate clause in the instruction. For example,
(RTEMP)+T, XorCarry;*Equivalent to (RTEMP)+T+1
(RTEMP)-T, XorCarry;
*Equivalent to (RTEMP)-T-1
(RTEMP)-T-1, XorCarry;
*Equivalent to (RTEMP)-T
The legal destinations for an Alu source expression are:
RADDR←Any RM destination
Stack←or any other stack destination--emulator only
T←
PD←This no-op destination is necessary when the Alu operation is "A" or "B" (i.e., A straight through or B straight through) and the Alu output is not being loaded by any real destination. It ensures that the source gets routed through the Alu, which might be necessary for an Alu branch condition in the next instruction.
The Input and InputNoPE functions (FF < 100), ALUFMem, ALUFMRW←, Cnt, Pointers, ShC, TIOA&StkP functions (FF > 77) and shifter operations discussed in the last section may be used instead of an Alu operation (These are alternative inputs to the hardware’s PD path.). Hence, they can feed RADDR← or T← destinations.
Here are some examples of other Alu clauses:
T←((RTEMP)+T) Lsh 1Use of Alu lshift 1 function
T←T Rsh 1
Use of Alu rshift 1 function
T←(T) Rsh 1
"()" are optional around "T"
T←RTEMP
"()" are optional around a single source
T←(RTEMP) Lcy 1
"()" required around an RM address with anything else
T←((RTEMP)+T) Rcy 1
RTEMP←(RTEMP) BRsh 1
Alu rshift 1 bringing Alu carry into bit 0
RTEMP←(RTEMP) ARsh 1
Arithmetic rshift 1 preserving sign
T←T xor (377C)
T←ID, Carry20
Carry20 function is separate clause
T←ALUFMem, Aluf[17]
Save ALUFM[17] in T
T←0H
Literal reference to then contents of the ALUFM location containing 0
T←(ALUFMRW←RTEMP), Aluf[17]
Saves ALUFM[17] in T, loads it from RTEMP
Warning: If you erroneously write an instruction statement that routes the Alu through PD and also routes Input or ALUFMem through PD, the assembler won’t give you any error message.
31. Memory References
Memory references are initiated with A clauses as discussed earlier--the assembler does not make any distinction between the hardware’s Mar bus and the A bus.
From the viewpoint of what can be encoded in an instruction, it is convenient to distinguish Fetch← and Store← from other references; only these two allow the displacement to be sourced from T or an RM address while FF remains available for use as a constant or a long branch; only these two allow the displacement to be sourced from T, ID, MD, or Q using only FF[0:1], so FF[2:7] remain available for encoding another function. All other references require FF to be used when specifying any source other than an RM address. Since FF[0:1] encode alternate sources for the displacement or alternate references, only the first 100 functions can be used in the same instruction; functions 100 to 377 cannot be encoded.
The Store← and Map← references not only use a displacement on A but also accept data on B. However, if you forget to route data onto B, the assembler won’t flag your error.
In the same statement with Store←, you should normally write "DBuf←bsource" to show explicitly that the data on B (bsource) is intended for the Store←; similarly with Map←, you should write "MapBuf←bsource." The "DBuf←" and "MapBuf←" are just for readability, since the hardware will load from B regardless of what you write in the instruction statement (DBuf/MapBuf are the names of the buffer registers in the memory section that get loaded when you do a Store←/Map←).
At t0 of the entry instruction for an opcode, MemBase is loaded by the IFU with either a MemBX-relative value between 0 and 3 or an absolute number between 34 and 37. Base register 37 is used as the code base by the IFU. The FlipMemBase function loads MemBase with its current value xor 1 and MemBase←small constant loads with any value between 0 and 37.
After a Fetch←, MD may be read in any of the following ways:
RTEMP←MDLoad into any RM or STK register
T←MD
adest←MDAny A destination (i.e., Alu or another memory request)
bdest←MDAny B destination
Implicit use by ShMD...Mask
The time required for a memory reference not confined to the cache (i.e., a cache reference that misses or an io reference) is about 1.7 ms. A Fetch← reference confined to the cache finishes in two cycles, which means that MD can be loaded into RM or T in the next instruction, or onto A or B or used in ShMD...Mask in the second instruction after the Fetch← without being held.
32. Standalone Functions, Block, and Breakpoint
Rule: Standalone clauses should be put to the right of A clauses in an instruction statement; the assembler will generate correct output regardless of where the clause appears, but an FF > 77 function to the left of a memory reference clause (which is an error) will not be flagged, if you violate the rule.
The following summarizes standalone functions, each written as a separate clause.
BreakPoint(Not a function)--causes loading with bad parity in both halves of IM, interpreted as a breakpoint by Midas
Block(Not a function)--causes the Block bit to be set in the instruction (only legal in non-emulator tasks)
RestoreStkPFF > 77--restores StkP to the value saved after the last IFU dispatch
XorCarryFF < 100--complement the carryin from the ALUFM ram
XorSavedCarryFF < 100--xor carryin from the ALUFM ram with the carryout of the last instruction executed by this task.
Carry20FF < 100--OR 1 into the carry into Alu[13] (The hardware Alu is composed of four four-bit IC’s; this function OR’s 1 into the carry out of the low-order IC.)
FreezeBCFF < 100--freeze task-specific branch conditions
TIsIDFF < 100
RIsIDFF < 100
FlipMemBaseFF < 100--MemBase←MemBase xor 1
MultiplyFF < 100
TaskingOnFF > 77--enabling tasking is delayed until the next instruction for the current task is executed
TaskingOffFF > 77
UseDMDFF > 77--execute the manifold operation for the current DMux address
DivideFF > 77
CDivideFF > 77
IFUResetFF > 77--reset the IFU
LoadMCR[A,B]FF > 77--Routes first arg onto A, second onto B; loads MCR from the appropriate bits off of each bus.
LoadTestSyndromeFF > 77--loads TestSyndrome from DBuf (used after a Store←).
RescheduleFF > 77--cause the second IFUJump to enter a trap vector
RescheduleNowFF > 77--cause the next IFUJump to enter a trap vector
NoRescheduleFF > 77--turns off the ReSchedule condition
IFUTickFF > 77--generates the next clock for the IFU testing stuff.
AckJunkTWFF > 77
TIOA[device]FF > 77--loads TIOA[5:7] from FF[5:7].
Wakeup[taskx]FF = 360 to 377--issue a wakeup to taskx, previously defined by TASKN[taskx,n].
Notify[n]same as notify, but n is an integer 0 to 17; the Wakeup form should ordinarily be used (exception: control section diagnostics)
Cnt-1FF < 100--uses the Cnt=0&-1 branch condition function for its side-effect without imposing any placement constraint on the successor. The successor must be forced to lie at an odd location (e.g., by using DispTable).
* The TGetsMD, ModStkPBeforeW, and ReadMAP functions are never written explicitly by programmers; the ReadMAP function is imposed automatically by the RMap← reference, ModStkPBeforeW by the appropriate stack operations, and TGetsMD when both T and an RM address are loaded from MD.
33. Branching
This section discusses branch clauses in instruction statements, declarations which affect branching, and dispatch clauses.
Micro assembles instructions for an imaginary machine identical to Dorado but with additional fields assembled for its postprocessor. The imaginary machine is characterized by full-size 14-bit branch addresses in instructions and 14-bit program addresses in IFUM. MicroD places instructions and transforms the .Dib file for the imaginary machine into a .Mb file for Dorado. Algorithms used by MicroD are described in the appendix.
33.1. What the Branch Hardware Does
Dorado implements three kinds of control transfers determined by the value in the JCN field of an instruction: jumps, returns, and IFU jumps; jumps may be "local," "global," "long," or "conditional." The processor always branches or returns--the hardware contains no concept of not-branching or of falling through to the next instruction.
Returns and IFU jumps load the Link register unconditionally; jumps load Link iff the target address is 0 mod 20. For all of these, the value loaded into Link, if any, is ((.+1) & 77) + (. & 7700); i.e., Link is loaded with caller’s address +1 (carries not propagating beyond the low six bits).
For reasons that will be apparent, it is convenient to view the microstore as composed of 100 pages of 100 words each. Local jumps transfer control to any location on the current page, global jumps to location 0 on any page, and long jumps to any location in the microstore (using the FF field to extend JCN).
An explicit branch clause may be unconditional or conditional. When conditional, the branch address is executed next, if the condition is false, or the branch address OR 1, if the condition is true. The decision to load Link (i.e., Call or Goto) is based upon the false branch address.
Branch conditions may be encoded as functions (FF < 100), in the JCN field, or both (when two BC’s are specified, the true path takes if either condition is true). When encoded in JCN, the false branch address must be at locations 4, 6, 10, ... , or 36 in the current page. When the BC is coded only in FF, the false branch address can be at any even location in the same page or at a global location.
The locations which are multiples of 4 are IFU targets. Namely, it is possible to origin an IFU entry vector at these points.
Dispatches allow an instruction to modify the branch address of the next instruction for the same task. The address modification consists of "OR"ing bits computed by the dispatch with the branch address computed in cycle i+1.
33.2. Branch Clauses
The assembly language has IFUJump and Return constructs analogous to the underlying hardware operations. However, the complications surrounding jumps are, for the most part, concealed from the programmer.
If the programmer doesn’t specify any branch clause in the instruction statement, the assembler will fabricate a jump to the next instruction inline. Several constructs of the form:
Goto[ba, bc1, bc2] -or-
DblGoto[batrue,bafalse,bc1,bc2]
are defined (see below), where both branch conditions are optional in the "Goto" form, and the second branch condition is optional in the "DblGoto" form. "Goto" indicates that the Link register must not be modified and "Call" that Link must loaded with the address of the next instruction inline; "Branch" is deliberately indefinite about whether a "Goto" or "Call" is done.
Branch addresses for these may be either instruction tags or one of the following special symbols: .-3 .-2 .-1 . .+1 .+2 .+3, where "." refers to the current instruction and the others are relative to this inline. [It is obviously possible to define .-4, .+4, .-5, etc., but my feeling is that it is bad style to jump further than +/- 3 without using a tag. If anyone finds this inconvenient, please let me know.]
Branch condition arguments may be either "regular" (one of the 10 in the hardware manual) or "complementary" (complements of the 10 in the hardware manual). The branch conditions are named as follows:
RegularComplementary
Alu=0Alu#0
Alu<0
Alu>=0
Cnt=0&-1
Cnt#0&-1Also decrements Cnt after testing for the branch
R<0
R>=0
R odd
R even
Carry’
Carry
Reschedule
Reschedule’(Emulator task only)
IOAtten’
IOAtten(io tasks only--same encoding as Reschedule)
Overflow
Overflow’(FF encoding only)
Alu<=0Alu>0Combination of Alu=0 in FF and Alu<0 in JCN
When complementary branch conditions are used, the assembler simply reverses the order of the branch tags. Hence, DblGoto[T1,T2,com C1, com C2] = DblGoto[T2,T1,C1,C2]. This is provided as a programming convenience.
Warning: If two branch conditions appear in a statement, they must be both regular or both complementary. When two regular branch conditions are used, the true path takes if either is true. However, when two complementary branch conditions are used, the true path takes only when both are true. Don’t get confused by this.
The "Top Level" and "Subroutine" declarations control assembler error checking. In Top Level mode, calls and dispatches are legal, returns are illegal, and branches may have target addresses that lie on either call or goto locations. In Subroutine mode, calls and dispatches are illegal, returns are legal, and branch targets are required to be at goto locations.
The assembler constructs are given below, where "<>" denote optional args; C1 and C2 either two hardware branch conditions or complements of two hardware branch conditions:
Return[<C1>]To Link and smashes Link--illegal in Top Level mode. A branch condition (uses FF < 100) makes sense only when the caller has skip/noskip return points created by an SCall, DblSCall, or SCoReturn.
CoReturn[<C1>]Like Return but Link←.+1 and next instruction inline placed at .+1.
DblBranch[T1,T2,C1<,C2>]To T1 if C1 or C2 true, else to T2. T1 will be placed at T2 OR 1; placement of T2 is limited to goto locations in Subroutine mode, else unconstrained.
DblGoto[T1,T2,C1<,C2>]like DblBranch[T1,T2,C1<,C2>] constraining T2 placement to goto locations.
DblCall[T1,T2,C1<,C2>]like DblBranch[T1,T2,C1<,C2>], constraining next instruction inline to be at .+1, and limiting T2 to call locations. Illegal in Subroutine mode.
Branch[T1<,C1<,C2>>]To T1 if C1 or C2 is true or if both branch conditions are omitted; otherwise to next instruction inline. When conditional, T1 will be placed at .+1 OR 1. In Subroutine mode, either .+1 (conditional) or T1 (unconditional) constrained to goto locations.
Goto[T1<,C1<,C2>>]like Branch[T1<,C1<,C2>>] constrains either T1 (unconditional) or next instruction inline (conditional) to goto locations.
Call[T1<,C1<,C2>>]like Branch[T1<,C1<,C2>>]; illegal in Subroutine mode; complementary BC’s illegal; constrains next instruction inline to be at .+1; constrains placement of either T1 (unconditional) or next instruction inline (conditional) to call locations. Discussed below.
IFUJump[i<,C1>]Dispatch to the i’th entry vector of the next opcode (An error is flagged if i >= the entry vector size specified by the last InsSet declaration). A branch condition would only be used if a conditional exit programming convention is followed, as discussed in the hardware manual; complementary BC’s are illegal.
DblSCall[T1,T2,C1<,C2>]= DblCall[...] and forces odd placement of the instruction and placement of the next two instructions inline at .+1 and .+2 so that the subroutine can do skip/noskip Return by using a branch condition.
SCall[T1<,C1<,C2>>]= Call[T1<,C1<,C2>>] and forces odd placement of the instruction and placement of the next two instructions inline at .+1 and .+2 so that the subroutine being called can do skip/noskip Return by using a branch condition. Complementary branch conditions are illegal.
SCoReturn[<C1>]= CoReturn but forces placement at an odd location and placement of the next two instructions inline at .+1 and .+2. A complementary branch condition is illegal and use of any branch condition only makes sense when the caller entered by means of SCall or SCoReturn.
--No branch clause = Branch[.+1]
A Branch while Top Level is in force imposes less placement constraints on the target instruction(s) because it permits either the Call or Goto locations to be used.
DblBranch, DblGoto, and DblCall are expected to be less frequent than Branch, Goto, and Call because programmers ordinarily think of branching or falling-through rather than branching to one of a pair of instructions.
For an unconditional top level Branch, MicroD outputs a long call or long goto if the FF field is unused, and imposes no constraint on the placement of either the instruction or its target. If FF is used, then the branch target will have to be in one of the 100 same-page or 100 global branch locations reachable by a JCN branch.
An unconditional Call is assembled as a long call if FF is unused. In this case, the branch address may be any call location. If FF is used, then the target address has to be one of the 3 call locations in the same page or one of the 100 global call locations. The next instruction inline is placed at .+1 within the page.
A conditional Call is just barely possible. It requires the next instruction inline to be simultaneously at the true branch address xor 1 and at the address of the caller +1. Since the true branch address must be at a location with four low bits equal 0001, these conditions are only met at three positions within a page (e.g., the Call, false target, and true target may be placed at 17, 20, and 21; at 37, 40, and 41; or at 57, 60, and 61 in the page). This implies that complementary BC’s are illegal with Call, nor can you encode two consecutive instructions each containing a conditional Call, nor can you have more than one conditional call to a single subroutine.
It is also impossible to have a Call in an instruction which is the false target of a conditional Branch because the return of the Call would be to the true target of the previous conditional branch.
An unconditional Return branches to Link, normally containing the address of the caller +1. There is no placement constraint on an instruction containing a Return.
A conditional Return goes to Link if the branch condition is false or to Link or’ed with 1 if the condition is true. This allows a skip/noskip return to the caller, which only makes sense if the caller imposed the necessary placement constraints on his two successor instructions by using an SCall, DblSCall, or SCoReturn.
An unconditional Goto is assembled as a long goto if FF is unused. If FF is used, then the branch address has to be one of the 74 goto locations in the same page.
Suggestion: In programs that nearly fill the control store, or in smaller programs that have large instruction clusters, you should carefully use Branch rather than Goto in Top Level mode to give MicroD greater freedom in placing instructions; in Subroutine mode, you may use either Branch or Goto, but I suggest that you pick a consistent convention: either always use Goto or always Branch.
33.3. Dispatch Clauses
The assembly language defines the following dispatch clauses:
BDispatch←B10-way dispatch on B[15:17]
BigBDispatch←B
400-way dispatch on B[10:17]
Multiply is also a dispatch. Dispatches OR bits into the branch address computed by the next instruction for the same task. This means that the programmer must impose the necessary constraints on the target instructions in the dispatch table himself--MicroD won’t do it for him. This is done using placement declarations as discussed in the next section.
34. Placement Declarations
When an IFU location is assembled, the address to which it dispatches is automatically marked as an IFU entry--no explicit declaration is required when assembling that instruction. In other words:
IFUReg[n,TAG,..otherjunk..]; *Opcode n PUSH2
automatically makes TAG an IFU entry. When you specified the opcode set with InsSet[i,n], you declared that there would be n entries for each IFU dispatch in the instruction set. You must put the n entries in sequence in the source, with the first at TAG and the others after that.
Global entries are declared by a "Global" clause in a statement, e.g.:
DONEXT: Return, T←ID, Global;
Global declarations cause placement at one of the 100 global call locations in the microstore. Global placement must be explicitly declared--MicroD handles most placement automatically, but it does not automatically assign globals.
Placement requirements for instructions in a dispatch table (i.e., of instuctions which are the targets of BDispatch←B, BigBDispatch←B, etc., or of a computed Return) may be declared either through using "At" on every instruction in the table (see below), or, under suitable conditions, using the DispTable macro.
DispTable[LENGTH,MASK,VALUE] appearing as a clause in an instruction statement causes that statement to begin a group of LENGTH consecutively-placed statements, 1 <= LENGTH <= 20. The first statement is placed so that [address and MASK] = VALUE. MASK defaults to [next power of 2 >= LENGTH] - 1, and VALUE defaults to 0. Note that LENGTH+VALUE must be <= 20.
A 10-way BDispatch might be written as follows:
BDispatch←RTEMP;
... , Goto[SWITCH];

SWITCH:
... , DispTable[10];*B[15:17] = 0
... ;*B[15:17] = 1
...
... ;*B[15:17]=7
where the three instructions in the dispatch need not be consecutive in the assembly source.
An instruction containing the clause "At[N]" will be forced by the assembler to appear at absolute location N in the microstore. "At[N1,N2]" in an instruction is equivalent to At[Add[N1,N2]]. "At" will be necessary for the special IFU trap locations and for instructions in dispatch tables that do not meet the constraints of the DispTable macro above. The currently reserved locations in IM are given in the hardware manual.
Warning: In addition, because instruction addressess are unknown during assembly, it is illegal to create parameters, constants, or RM data referring in any way to absolute locations. To do this, you must manually locate each affected instruction with "At" and do arithmetic on integers with the same values as the instruction locations. This will probably be required for the startup instructions of all tasks, which must be loaded into Link for a LdTPC←.
Normally, MicroD automatically chooses the page assignment for an instruction not constrained by "At"--the TITLE statement enables automatic page assignment. However, the following macros are available for constraining the page on which an instruction is placed:
OnPage[n];as a separate statement will constrain MicroD to place subsequently assembled instructions on locations on page n (i.e., in the range n*100 through n*100 + 77).
AutoPage[n];undoes OnPage and allows MicroD freedom to place anywhere.
OnPage may be useful in dealing with microcode overlays, as discussed later.
In addition to the above placement macros, there are two macros for "reserving" and "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.
35. Microcode Overlays
The barest minimum provisions are made for microcode overlays. Because we cannot handle dynamic relocation, non-conflicting placements must be made for the resident system and all overlays that may be used with it. There are several ways that safe placement may be accomplished:
First, MicroD can write an xxOccupied.Mc file in which all locations used in the resident system loadup are indicated by IMReserve statements. The xxOccupied.Mc file can be loaded with an overlay to ensure safe placement by MicroD of code in the overlay. A disadvantage of this method is that whenever the system microcode is modified, all overlays using this method must be regenerated.
Next, the resident microcode can itself reserve regions of the microstore with IMReserve so that overlays confining themselves to the reserved area need not be regenerated when a new system is released.
Thirdly, throwaway initialization code may be manually placed by means of OnPage or At declarations that totally fill some pages of the microstore, and these pages are available to overlays after execution. If this method is used in conjunction with one of the first two methods, IMUnreserve declarations can be used to free up the pages filled with the throwaway code.
Finally, particular instructions in the resident system may be overwritten by particular instructions in an overlay; these must be manually placed with "At" declarations in both the system microcode and the overlay.