% D0Lang.mc Edit history: 29 Oct 81 by Fiala Fix Stack&+/-n _ Stack&+/-n F2.used.twice bug 1 Sep 81 by Fiala Added other RM reg, F2 with ReadPipe 6 Feb 81 by Fiala Added :UNLESS, HIA, LOA, NSALUF, ALURESULT&NSALUF 9 Dec 80 by Fiala Add RV2, RV4, PSET macros 29 Oct 80 by Fiala Added ODDOK and NONQUADOK Naming convention: Internal symbols not used by programmers all end with "@"; symbols defined in the "D0 MicroAssembler" document and Micro builtins don't. % BUILTIN[MACRO,2]; BUILTIN[NEUTRAL,3]; BUILTIN[MEMORY,4]; *Declare Memory[name,wordsize,length,srcmacro, *sinkmacro,tagmacro,postmacro] BUILTIN[TARGET,5]; BUILTIN[DEFAULT,6]; BUILTIN[FIELD,7]; *Declare field (#1 = bit1, #2 = bitn) BUILTIN[PF,10]; *Preassign value to field BUILTIN[SET,11]; *Declare integer and set value BUILTIN[ADD,12]; *Add up to 8 integers BUILTIN[IP,13]; *Integer part of address BUILTIN[IFSE,14]; *If string equal (IFSE[s1,s2,true,false]) BUILTIN[IFA,15]; *If field assigned (IFA[field,true,false]) BUILTIN[IFE,16]; *If integers equal BUILTIN[IFG,17]; *If integer 1 > integer 2 BUILTIN[IDF,20]; *If symbol in symbol table and not unbound address *BUILTIN[IFME,21]; *If memory part of address equals string BUILTIN[ER,22]; *Error message (ER[string,abortflag,integer]) *abortflag = 0 message, 1 fatal, 2 error, 3 warning BUILTIN[LIST,23]; *Set listing mode for memory *BUILTIN[INSERT,24]; *Insert file (Micro predefines this name) BUILTIN[NOT,25]; *1'S complement BUILTIN[REPEAT,26]; *Repeat the text #2 #1 times BUILTIN[OR,27]; *Inclusive or up to 10 integers BUILTIN[XOR,30]; *Exclusive or up to 10 integers BUILTIN[AND,31]; *And up to 10 integers BUILTIN[COMCHAR,32]; *Set comment char for conditional assemblies *BUILTIN[BITTABLE,33]; *Makes #1 a bit table of length #2 bits *BUILTIN[GETBIT,34]; *Is the bit in bittable #1 at pos. #2 *BUILTIN[SETBIT,35]; *SETBIT[table,bit1,nbits,distance,value] *BUILTIN[FINDBIT,36]; *FINDBIT[table,bit1,nbits,distance,hopdistance,nhops] *BUILTIN[MEMBT,37]; *MEMBT[memory,table] creates a bit table for memory BUILTIN[LSHIFT,40]; *Shifts the integer #1 left #2 positions BUILTIN[RSHIFT,41]; *Shifts the integer #1 right #2 positions BUILTIN[FVAL,42]; *FVAL[field] is an integer whose value is the *current contents of the field BUILTIN[SELECT,43]; *#1 is an integer .ge. 0 and .le. 7. evaluates *#2 if #1 = 0, ..., #9 if #1 = 7. Error if #1 > 7 BUILTIN[SETPOST,44]; *Set post-evaluation macro (SETPOST[mem,macro]) BUILTIN[LISTFIELDS,46]; *LISTFIELDS[mem,word] assembles word as for DEFAULT *and the 1-bits denote the right-most bits of fields *in the octal listing. BUILTIN[SETMBEXT,47]; *Set .mb file extension BUILTIN[SUB,50]; *SUB[a1, a2, ... , an] = a1-a2-...-an BUILTIN[EQUATE,51]; *EQUATE[new,old] gives new same definition as old BUILTIN[ASMMODE,52]; *ASMMODE[0] is normal, ASMMODE[1] ignores all *statements except those beginning with ":" BUILTIN[TRACEMODE,53]; *TRACEMODE[n,v] turns on tracing feature n if v ne 0, *off if v=0. n = 0 is trace symbol insertions, *1 is trace all applications of the form name[args] BUILTIN[WHILE,54]; *WHILE[expr,clause] repeatedly executes clause while *expr is non-zero. BUILTIN[M@,2]; *=MACRO (internal use) BUILTIN[F@,7]; *=FIELD BUILTIN[EQ@,51]; *=EQUATE SETMBEXT[DIB]; ER[29.Oct.81--D0Lang.version.14,0]; *IM field definitions F@[MIRMOD@,0,1]; *MEMINS@ and RMOD@ F@[RSEL@,2,5]; F@[MR@,0,5]; *MEMINS@, RMOD@, and RSEL@ F@[F2@,22,25]; *FF[4:7] F@[JC@,26,30]; *Jump control F@[JC0@,26,26]; F@[JC2@,30,30]; F@[JA2@,31,36]; F@[JA7@,36,36]; *F@[IMPARITY@,37,37]; *Parity bit F@[RX@,120,121]; F@[RX1@,121,121]; F@[JA1@,122,123]; *Fields in regular mi's F@[ALF@,6,11]; *ALUF field F@[AF0@,6,6]; *Sign of ALUF for constants F@[BS@,12,13]; *Source for B (0-1 = constants, 2 = T, 3 = other) F@[F1@,14,17]; *FF[0:3] F@[BSF1@,12,17]; *BS@ and F1@ F@[LR@,20,20]; *Load RM F@[LT@,21,21]; *Load T *Fields in memory reference mi's EQ@[TYPE@,ALF@]; *Type of memory reference F@[SRCDES@,12,21]; *Source/destination F@[SRDS03@,12,15]; F@[SRDS67@,20,21]; F@[SRDS7@,21,21]; F@[MRTYP@,0,11]; *Both MR@ and TYPE@ *Extra stuff for MicroD F@[BRKP@,40,40]; *Midas breakpoint F@[@W0@,41,41]; *Place at absolute loc. W0 *F@[GLB@,42,42]; F@[PW0@,43,43]; *Bit 43=1 tells MicroD that 44:47 contain the page *on which the mi should be placed F@[W0@,44,57]; *full address for placement F@[PGE@,44,47]; *page for mi placement F@[RETCL@,60,61]; *Bit encoded: 2 = don't constrain placement of this mi *1 = put W2@ at this mi's location + 1 mod 20 F@[ODDCALL@,62,62]; *Indicates that even though this mi encodes a CALL *in JC@, its inline successor is not constrained to *lie at .+1 in the microstore. F@[SWPAGE@,63,63]; *Loads the PAGE register (for MicroD error chk) F@[W1@,64,77]; *Imaginary address of unconditional or false branch F@[CHPAGE@,101,101]; *Predecessor did LOADPAGE (for MicroD error chk) *F@[EMUL@,102,102]; *Presently unused F@[CND@,103,103]; *Has a branch condition F@[W2@,104,117]; *Imaginary address of conditional branch when true *M@[MRS@,MR@[XOR[RSHIFT[#1,2],14]] RX@[AND[#1,3]]]; *MEMINS@ RMOD@ & RSEL@ M@[BREAKPOINT,BRKP@[1]?]; *Causes Midas to create a bp when loading M@[NOP,PF[BS@,2]]; *For no-op mi's M@[F1T@,ER[F1.used.twice,2]]; M@[FF1@,IFA[F1@,F1T@[],F1@[#1] ]]; M@[F2T@,ER[F2.used.twice,2]]; M@[FF2@,IFA[F2@,F2T@[],F2@[#1] ]]; M@[FZ@,FF1@[RSHIFT[#1,4]] FF2@[AND[#1,17]]]; M@[BSF@,BSF1@[RSHIFT[#1,4]] FF2@[AND[#1,17]]]; M@[TMARGS@,ER[Too.many.args.for.#1,2]]; M@[TBIG@,ER[#1.count.too.big,2]]; M@[TSMALL@,ER[#1.count.too.small,2]]; M@[NOARGS@,ER[No.args.allowed.in.#1,2]]; *Fields for IMMASK assembly F@[CONMSK@,0,17]; *Constraint mask: bit n = 1 => can place at n mod 20 F@[DTLEN@,20,23]; *Dispatch table length - 1 %Neutrals and connection macros: A neutral "RB" distinct from "A" is needed so that expressions such as: DB[RAddr]_(BBFBX[DB[RAddr]]) SALUFOP T, DISP[BBLOOP]; can be compiled. In this case the DB[RAddr] macro cannot leave the neutral "A" because that won't work in the destination part of the clause. This is also the reason for the RB_ macro. % NEUTRAL[A]; *ALUA sources NEUTRAL[A_]; *ALUA destinations NEUTRAL[RB]; *RM addresses or functions of RM addresses NEUTRAL[B]; *H2 sources NEUTRAL[B_]; *H2 destinations NEUTRAL[LU]; *ALU functions NEUTRAL[LU_]; *ALU destinations (RM, T) NEUTRAL[T]; *T register NEUTRAL[?]; *Left by macros that cannot be connected to anything by "_" M@[B_B,B]; M@[B_T,B]; M@[LU_LU,LU]; M@[T_,LT@[1] LU_]; M@[RB_,LR@[1] LU_]; *Micro is supposed to run faster if symbols such as these integers are *defined prior to their appearance in a macro, so define them here. SET[Z@,0]; SET[Z1@,0]; SET[Z2@,0]; SET[Z3@,0]; SET[TSKFLG@,0]; SET[CHPGFLG@,0]; SET[REGIFLAG@,0]; SET[RTNILL@,0]; %Memory reference interlocks only occur when the ALUA source participates in the ALU operation, so default the ALU operation to LU_A when an A destination is loaded; if LU_B appears in the mi, warn him of potential error unless he has put a NOREGILOCKOK clause in the same mi. % M@[NOREGILOCKOK,SET[REGIFLAG@,IFE[REGIFLAG@,1,0,2]] ?]; M@[A_A,PF[ALF@,1] SET[REGIFLAG@,IFG[REGIFLAG@,1,0,1]] A]; EQ@[A_RB,A_A]; *ALU operations defined for (A,RB) op (B,T) M@[LU_B,ALF@[0] LU]; EQ@[LU_T,LU_B]; M@[LU_A,ALF@[1] LU]; EQ@[LU_RB,LU_A]; M@[AANDB,ALF@[2] LU]; EQ@[AANDT,AANDB]; EQ@[RBANDB,AANDB]; EQ@[RBANDT,AANDB]; M@[AORB,ALF@[3] LU]; EQ@[AORT,AORB]; EQ@[RBORB,AORB]; EQ@[RBORT,AORB]; M@[AXORB,ALF@[4] LU]; EQ@[AXORT,AXORB]; EQ@[RBXORB,AXORB]; EQ@[RBXORT,AXORB]; EQ@[A#B,AXORB]; EQ@[A#T,AXORB]; EQ@[RB#B,AXORB]; EQ@[RB#T,AXORB]; M@[AANDNOTB,ALF@[5] LU]; EQ@[AANDNOTT,AANDNOTB]; EQ@[RBANDNOTB,AANDNOTB]; EQ@[RBANDNOTT,AANDNOTB]; M@[AORNOTB,ALF@[6] LU]; EQ@[AORNOTT,AORNOTB]; EQ@[RBORNOTB,AORNOTB]; EQ@[RBORNOTT,AORNOTB]; M@[AXNORB,ALF@[7] LU]; EQ@[AXNORT,AXNORB]; EQ@[RBXNORB,AXNORB]; EQ@[RBXNORT,AXNORB]; EQ@[A=B,AXNORB]; EQ@[A=T,AXNORB]; EQ@[RB=B,AXNORB]; EQ@[RB=T,AXNORB]; M@[A+1,ALF@[10] LU]; EQ@[RB+1,A+1]; M@[A+B,ALF@[11] LU]; EQ@[A+T,A+B]; EQ@[RB+B,A+B]; EQ@[RB+T,A+B]; M@[A+B+1,ALF@[12] LU]; EQ@[A+T+1,A+B+1]; EQ@[RB+B+1,A+B+1]; EQ@[RB+T+1,A+B+1]; M@[A-1,ALF@[13] LU]; EQ@[RB-1,A-1]; M@[A-B,ALF@[14] LU]; EQ@[A-T,A-B]; EQ@[RB-B,A-B]; EQ@[RB-T,A-B]; M@[A-B-1,ALF@[15] LU]; EQ@[A-T-1,A-B-1]; EQ@[RB-B-1,A-B-1]; EQ@[RB-T-1,A-B-1]; *ALUF[16] UNASSIGNED M@[ASALUFOPB,ALF@[17] LU]; EQ@[ASALUFOPT,ASALUFOPB]; EQ@[RBSALUFOPB,ASALUFOPB]; EQ@[RBSALUFOPT,ASALUFOPB]; %Three macros define parameters from which constants, RM values, or IM data can be constructed: MP[NAME,octalstring] makes a parameter of NAME; SP[NAME,P1,P2,P3,P4,P5,P6,P7,P8] makes a parameter NAME equal to the sum of Pn, where the Pn may be parameters or addresses. NSP[NAME,P1,P2,P3,P4,P5,P6,P7,P8] is ones complement of SP. The parameter "NAME" is defined by the integer "NAME!", so it is ok to use "NAME" for a constant as well as a parameter. However, it is illegal to define constants, addresses, etc. with identical names. "Literal" constants such as "322C", "177622C", or "32400C" may be used in mi's without previous definition. Alternatively, constants may be constructed from parameters, integers, and addresses using the following macros: MC[NAME,P1,P2,P3,P4,P5,P6,P7,P8] defines name as a constant with value = sum of parameters Pn; NMC[NAME,P1,P2,P3,P4,P5,P6,P7,P8] is the ones complement of MC. Note: MC and NMC also define NAME as a parameter. % *Fields for initializing 16-bit wide memories F@[E0@,0,3]; F@[E1@,4,17]; *Macro to initialize (16-bit) variables in the target memory. This is *done by writing 32100V (i.e., as a literal). M@[V,E1@[#1] E0@[#2]]; M@[!,0]; M@[MP,SET[#1!,#2]]; M@[PX@,IDF[#1!,#1!,#1]]; M@[DPS@,ADD[PX@[#1],PX@[#2],PX@[#3],PX@[#4],PX@[#5],PX@[#6],PX@[#7],PX@[#8]]]; M@[SP,IFG[#0,11,TMARGS@[#1], SET[#1!,DPS@[#2,#3,#4,#5,#6,#7,#8,#9]]]]; M@[NSP,IFG[#0,11,TMARGS@[#1], SET[#1!,NOT[DPS@[#2,#3,#4,#5,#6,#7,#8,#9]]]]]; M@[MC,IFG[#0,11,TMARGS@[#1], SET[#1!,DPS@[#2,#3,#4,#5,#6,#7,#8,#9]] M@[#1,ADD[#1!]C]]]; M@[NMC,IFG[#0,11,TMARGS@[#1], SET[#1!,NOT[DPS@[#2,#3,#4,#5,#6,#7,#8,#9]]] M@[#1,ADD[#1!]C]]]; M@[$RSETDISPLO,AND[#2,377]C]; M@[$RSETDISPHI,AND[ADD[LSHIFT[#1,14],#2],177400]C]; *For mmmmnnnnC, #1 = nnnn, #2 = mmmm M@[C,IFE[AND[#1,377],0,BS@[1] FF1@[#2] FF2@[RSHIFT[#1,10]], BS@[0] FF1@[RSHIFT[#2#1,4]] FF2@[AND[#1,17]]] B ]; M@[-C,SUB[0,#2#1]C]; %Cycler-masker stuff: Arguments to LDF and DISPATCH macros are POS and SIZE, where POS is the left bit of the field and SIZE the number of bits in the field, identical to Mesa read-field and write-field descriptors; RB is any R-bus source specifiable without using F1 or F2. LDF[RB,POS,SIZE] right-justifies any field. 0 - 17 20 1-bit fields bit 0 to 17 20 - 36 17 2-bit fields bit 0 to 16 37 - 54 16 3-bit fields bit 0 to 15 55 - 71 15 4-bit fields bit 0 to 14 72 -105 14 5-bit fields bit 0 to 13 106 -120 13 6-bit fields bit 0 to 12 121 -132 12 7-bit fields bit 0 to 11 133 -143 11 10-bit fields bit 0 to 10 144 -153 10 11-bit fields bit 0 to 7 154 -162 7 12-bit fields bit 0 to 6 163 -170 6 13-bit fields bit 0 to 5 171 -175 5 14-bit fields bit 0 to 4 176 -201 4 15-bit fields bit 0 to 3 202 -204 3 16-bit fields bit 0 to 2 205 -206 2 17-bit fields bit 0 to 1 DISPATCH[RB,POS,SIZE] loads APC with a field of SIZE <= 4 bits: 207 -226 20 1-bit fields bit 0 to 17 227 -245 17 2-bit fields bit 0 to 16 246 -263 16 3-bit fields bit 0 to 15 264 -300 15 4-bit fields bit 0 to 14 RSH[RB,shiftcount] right-shifts RB by shiftcount 1 to 17. uses LDF[RBsource,0,(20 - shiftcount)] codes LSH[RB,shiftcount] left-shifts RB by shiftcount 1 to 17. 301 -317 17 left shifts of 1, ..., 17 bits LCY[RB,shiftcount] left-cycles RB by shiftcount 1 to 17. 320 -336 17 left cycles of 1, ..., 17 bits RCY[RB,shiftcount] right-cycles RB by shiftcount 1 to 17. uses LCY[RB,(20 - shiftcount)] codes Other strange ones: -- RHMASK[RB] = LDF[RB,10,10] 337 LHMASK[RB] = RB & 177400 340 ZERO = 0 341 FIXVA[RB] = RSH[RB,1] & 40100 -- FORM1[RB] = LDF[RB,17,1] 342 FORM2[RB] = RB & 2 -- FORM3[RB] = LDF[RB,16,2] 343 FORM4[RB] = RB & 4 344 FORM5[RB] = RB & 5 345 FORM6[RB] = RB & 6 -- FORM7[RB] = LDF[RB,15,3] 346 FORM10[RB] = RB & 10 347 FORM-2[RB] = RB & -2 350 FORM-3[RB] = RB & -3 351 FORM-4[RB] = RB & -4 352 FORM-5[RB] = RB & -5 353 FORM-6[RB] = RB & -6 354 FORM-7[RB] = RB & -7 355 FORM-10[RB] = RB & -10 356 NIB0RSH8[RB] = (RB rshift 10) & 360 % M@[ILSIZ@,ER[Illegal.SIZE,2]]; M@[ILPS@,ER[Illegal.POS+SIZE,2]]; M@[LDF,IFG[ADD[#2,#3],20,ILPS@[],IFG[1,#3,ILSIZ@[], SET[Z@,ADD[#2,IFG[#3,7, SELECT[SUB[#3,10],1533,1544,1554,1563,1571,1576,1602,1605], SELECT[#3,100000,1400,1420,1437,1455,1472,1506,1521]]]] BSF@[Z@] #1]]]; *Impose register interlock only for this one M@[DISPATCH,SET[RTNILL@,1] IFG[#3,4,ILSIZ@[],IFG[ADD[#2,#3],20,ILPS@[], SET[Z@,ADD[#2,SELECT[#3,100000,1607,1627,1646,1664]]] BSF@[Z@] A_#1]]]; M@[RSH,IFG[#2,17,TBIG@[RSH],IFG[1,#2,TSMALL@[RSH], SET[Z@,IFG[#2,7,SELECT[SUB[#2,10],1533,1521,1506,1472,1455,1437,1420,1400], SELECT[#2,100000,1605,1602,1576,1571,1563,1554,1544]]] BSF@[Z@] #1]]]; M@[LSH,IFG[#2,17,TBIG@[LSH],IFG[1,#2,TSMALL@[LSH], BSF1@[74] FF2@[#2] #1]]]; M@[RCY,IFG[#2,17,TBIG@[RCY],IFG[1,#2,TSMALL@[RCY], BSF1@[75] FF2@[SUB[17,#2]] #1]]]; M@[LCY,IFG[#2,17,TBIG@[LCY],IFG[1,#2,TSMALL@[LCY], BSF1@[75] FF2@[SUB[#2,1]] #1]]]; M@[RHMASK,BSF1@[66] FF2@[3] #1 ]; M@[LHMASK,BSF1@[75] FF2@[17] #1 ]; M@[ZERO,BSF1@[76] FF2@[0] A]; M@[FIXVA,BSF1@[76] FF2@[1] #1]; M@[FORM1,LDF[#1,17,1]]; M@[FORM2,BSF1@[76] FF2@[2] #1]; M@[FORM3,LDF[#1,16,2]]; M@[FORM4,BSF1@[76] FF2@[3] #1]; M@[FORM5,BSF1@[76] FF2@[4] #1]; M@[FORM6,BSF1@[76] FF2@[5] #1]; M@[FORM7,LDF[#1,15,3]]; M@[FORM10,BSF1@[76] FF2@[6] #1]; M@[FORM-2,BSF1@[76] FF2@[7] #1]; M@[FORM-3,BSF1@[76] FF2@[10] #1]; M@[FORM-4,BSF1@[76] FF2@[11] #1]; M@[FORM-5,BSF1@[76] FF2@[12] #1]; M@[FORM-6,BSF1@[76] FF2@[13] #1]; M@[FORM-7,BSF1@[76] FF2@[14] #1]; M@[FORM-10,BSF1@[76] FF2@[15] #1]; M@[NIB0RSH8,BSF1@[76] FF2@[16] #1]; SET[RL0@,177777]; SET[RL1@,177777]; SET[RL2@,177777]; SET[RL3@,177777]; SET[RL@,177777]; SET[CTASK@,0]; SET[QTASK@,0]; SET[SUBTSK@,0]; SET[RMBASE@,0]; M@[SETTASK,SET[CTASK@,#1] IFG[CTASK@,17,ER[Illegal.SETTASK,2], SELECT[QTASK@,SET[RL0@,RL@],SET[RL1@,RL@],SET[RL2@,RL@],SET[RL3@,RL@]] SET[QTASK@,RSHIFT[CTASK@,2]] SET[SUBTSK@,AND[3,CTASK@]] SET[RMBASE@,LSHIFT[QTASK@,6]] SET[RL@,SELECT[QTASK@,RL0@,RL1@,RL2@,RL3@]]]]; %RM addresses used as sources/destinations execute the RSRC@/RSINK@ macros. RM address is legal if top two bits match QTASK and either the next two address bits are non-0 or the SUBTSK is 0. Below, unmatching quadtask causes a "Value won't fit" error on the field store into RSEL@; the ILLRA@ macro is evaluated when a task ne 0 mod 4 illegally accesses 0-17 in the RM region. Note that RSEL[0:1] must be complemented for the hardware. % M@[ILLRA@,ER[#1.in.0.to.17.of.RM.region.unaddressable.by.task.,2,CTASK@]]; M@[RSINK@,MIRMOD@[0] SET[Z@,XOR[IP[#1],RMBASE@,60]] RX@[AND[Z@,3]] RSEL@[RSHIFT[Z@,2]] IFG[Z@,57,IFE[SUBTSK@,0,,ILLRA@[#1]]] LR@[1] LU_]; M@[RSRC@,MIRMOD@[0] SET[Z@,XOR[IP[#1],RMBASE@,60]] RX@[AND[Z@,3]] RSEL@[RSHIFT[Z@,2]] IFG[Z@,57,IFE[SUBTSK@,0,,ILLRA@[#1]]] RB]; %Memory declarations must have names and sizes agreeing with those in Midas, except that IM must agree with the form expected by MicroD. % M@[W@,]; *Dummy macro required for memory definitions MEMORY[IM,124,10000,W@,W@]; MEMORY[RM,20,400,RSRC@,RSINK@]; MEMORY[IMLOCK,1,10000,W@,W@]; MEMORY[VERSION,20,1,W@,W@]; *MEMORY[DISP,40,10000,W@,W@]; *Dispatch table info passed to MicroD MEMORY[IMMASK,24,10000,W@,W@]; *Fake memory for IM placement constraints %Second arg of LIST controls listing of memories as follows: 1 = (TAG) nnnn nnnn nnnn ... 2 = (TAG) F1_3, F2_4, ... 4 = numerically-ordered list of address symbols 10 = alphabetically-ordered list of address symbols 20 = (TAG) 1nnnnn 1nnnnn ... (16-bit printout iff 1 set also) LISTFIELDS overrules the 1 and 20 numeric printout modes % *List IM in 20-bit units, RM as single word F@[V0@,0,17]; F@[V1@,20,37]; LISTFIELDS[IM,(V0@[1] V1@[1] JA1@[1] W0@[1] W1@[1] W2@[1])]; LISTFIELDS[RM,V0@[1]]; LISTFIELDS[IMMASK,CONMSK@[1] DTLEN@[1]]; LIST[IM,27]; LIST[RM,25]; LIST[,25]; %RM words are allocated in two steps. First, the group of 100 registers that must contain the ones being allocated is declared by SETTASK. Then registers in that group of 100 are allocated as follows: RV[FOO,23,p1,...,p7]; *Creates FOO = RM 23, value sum of params RV[FOO,23]; *Creates FOO = RM 23, no value RV[FOO]; *Creates FOO at last location + 1 RV[FOO,,17575]; *Creates address FOO at location after *last one allocated with value 17575 These macros leave the integer RLi, where i = 0 to 3 (the region) bound to the last displacement allocated, and the integer RL is always equal to RLi for the current region. % M@[RV,SET[RL@,IFSE[#2,,ADD[1,RL@],#2]] IFG[RL@,77,ER[RM.ovf,2]] IFG[#0,2,RM[RLC@,ADD[RMBASE@,RL@]] RLC@[#1: V0@[DPS@[#3,#4,#5,#6,#7,#8,#9]]], RM[#1,ADD[RMBASE@,RL@]]] ]; *Macros to define groups of four/two RM registers. M@[RV4,RV[#1,#5] RV[#2,Add[1,#5]] RV[#3,Add[2,#5]] RV[#4,Add[3,#5]]]; M@[RV2,RV[#1,#3] RV[#2,Add[1,#3]]]; SET[NONQUADOK@,0]; M@[NONQUADOK,SET[NONQUADOK@,1]]; *Suppress next "Not.Quadaligned" warning M@[MERNQ@,IFE[NONQUADOK@,0,ER[WARNING:.#1.not.quadaligned,3], SET[NONQUADOK@,0]]]; M@[QRS@,SET[Z@,XOR[60,IP[#1],RMBASE@]] IFE[AND[Z@,3],0,IFG[Z@,77,ILLRA@[#1],MR@[RSHIFT[ADD[Z@,100],2]]],MERNQ@[]] RB]; *PCF[RMADDR], SB[RMADDR], and DB[RMADDR] are also A sources M@[PCF,RX@[0] QRS@[#1]]; M@[SB, RX@[1] QRS@[#1]]; M@[DB, RX@[2] QRS@[#1]]; *Other register sources *(open-coded MRS@[x] and LDF[RB,P,S] here for speed) *MRS@ LDF[RB,x,y] M@[SSTKP&NSTKP,RX@[3] MR@[34] A]; *103 M@[SSTKP,RX@[3]MR@[34]BSF1@[65]FF2@[13] A]; *103 0,10 M@[NSTKP,RX@[3]MR@[34]BSF1@[66]FF2@[3] A]; *103 10,10 *aluresult = ovf,car,=0,<0 **Note that SALUF is read COMPLEMENTED but not named that way here M@[ALURESULT&NSALUF,RX@[3]MR@[35] A]; *107 M@[ALURESULT,RX@[3]MR@[35]BSF1@[63]FF2@[1] A]; *107 4,4 M@[NSALUF,RX@[3]MR@[35]BSF1@[66]FF2@[3] A]; *107 10,10 M@[MEMSYNDROME,RX@[3]MR@[36] A]; *113 M@[MEMERROR,RX@[3]MR@[37] A]; *117 M@[CYCLE&PCXF,RX@[3]MR@[31] A]; *127 M@[CYCLECONTROL,RX@[3]MR@[31]BSF1@[65]FF2@[13] A]; *127 0,10 M@[DBXREG,RX@[3]MR@[31]BSF1@[62]FF2@[15] A]; *127 0,4 M@[MWXREG,RX@[3]MR@[31]BSF1@[63]FF2@[1] A]; *127 4,4 M@[PCXREG,RX@[3]MR@[31]BSF1@[63]FF2@[5] A]; *127 10,4 M@[PCFREG,RX@[3]MR@[31]BSF1@[63]FF2@[11] A]; *127 14,4 M@[PCF.WORD,RX@[3]MR@[31]BSF1@[62]FF2@[13] A]; *127 14,3 M@[PRINTER,RX@[3]MR@[31]FF2@[0] A]; *127 M@[DBSB,RX@[3]MR@[32]FF2@[0] A]; *133 M@[TIMER,RX@[3]MR@[32] A]; *133 M@[RS232,RX@[3]MR@[33] A]; *137 M@[MNBR,FF2@[0]RX@[3]MR@[33] A]; *137 M@[APCTASK&APC,RX@[3]MR@[24] A]; *143 M@[APCTASK,RX@[3]MR@[24]BSF1@[62]FF2@[15] A]; *143 0,4 M@[APC,RX@[3]MR@[24]BSF1@[67]FF2@[15] A]; *143 4,14 M@[CTASK&NCIA,RX@[3]MR@[25] A]; *147 M@[CTASK,RX@[3]MR@[25]BSF1@[62]FF2@[15] A]; *147 0,4 M@[NCIA,RX@[3]MR@[25]BSF1@[67]FF2@[15] A]; *147 4,14 M@[CSDATA,RX@[3]MR@[26] A]; *153 M@[PAGE&PAR&BOOT,RX@[3]MR@[27] A]; *157 M@[PAGE,RX@[3]MR@[27]BSF1@[62]FF2@[15] A]; *157 0,4 M@[PARITY,RX@[3]MR@[27]BSF1@[63]FF2@[1] A]; *157 4,4 M@[BOOTREASON,RX@[3]MR@[27]BSF1@[66]FF2@[3] A]; *157 10,10 %To get a multi-field word using only one specification , use GETRSPEC[mrs address]. Thus to load T with APCTASK and APC: T _ GETRSPEC[143]; % M@[GETRSPEC,MR@[XOR[RSHIFT[#1,2],14]] RX@[AND[#1,3]] A]; *StkP sources/destinations: *(open-coded MRS@[x] here for speed) M@[STACK,RX@[3] MR@[20] A]; *MRS@[163] M@[STACK_,RX@[3] MR@[20] LR@[1] LU_]; M@[STACK&+1,RX@[3] MR@[21] A]; *167 M@[STACK&+1_,RX@[3] MR@[21] LR@[1] LU_]; M@[STACK&-1,RX@[3] MR@[22] A]; *173 M@[STACK&-1_,RX@[3] MR@[22] LR@[1] LU_]; M@[STACK&-2,RX@[3] MR@[23] A]; *177 M@[STACK&-2_,RX@[3] MR@[23] LR@[1] LU_]; M@[STACK&+2,RX@[3] MR@[20] F2@[3] A]; *163, STACKSHIFT M@[STACK&+2_,RX@[3] MR@[20] F2@[3] LR@[1] LU_]; M@[STACK&+3,RX@[3] MR@[21] F2@[3] A]; *167, STACKSHIFT M@[STACK&+3_,RX@[3] MR@[21] F2@[3] LR@[1] LU_]; M@[STACK&-3,RX@[3] MR@[23] F2@[3] A]; *177, STACKSHIFT M@[STACK&-3_,RX@[3] MR@[23] F2@[3] LR@[1] LU_]; %Functions are divided into the following classes: 1. Group A (currently none) and Group B--only in regular mi's, use F1 and F2. 2. F1 only--only in regular mi's. 3. F2 only--either memory reference or regular mi's. % *Group B functions M@[SPAREFUNCTION,FF1@[7] FF2@[0] ?]; M@[RESETERRORS,FF1@[7] FF2@[1] ?]; M@[INCMPANEL,FF1@[7] FF2@[2] ?]; M@[CLEARMPANEL,FF1@[7] FF2@[3] ?]; M@[GENSRCLOCK,FF1@[7] FF2@[4] ?]; M@[RESETWDT,FF1@[7] FF2@[5] ?]; M@[BOOT,FF1@[7] FF2@[6] ?]; M@[SETFAULT,FF1@[7] FF2@[7] ?]; M@[APCTASK&APC_,FF1@[7] FF2@[10] SET[RTNILL@,1] A_]; M@[RESTORE,FF1@[7] FF2@[11] A_]; M@[RESETFAULT,FF1@[7] FF2@[12] ?]; M@[USECTASK,FF1@[7] FF2@[13] ?]; M@[WRITECS0&2,FF2@[14] FF1@[7] JC@[6] ?]; M@[WRITECS1,FF2@[15] FF1@[7] JC@[6] ?]; M@[READCS,FF2@[16] FF1@[7] JC@[6] ?]; M@[D0OFF,FF1@[7] FF2@[17] ?]; *F1 only *00 take an RM address as argument M@[BBFA,FF2@[0] PF[ALF@,3] FF1@[00] #1]; *A dispatch M@[RS232_,FF1@[1] B_ ]; *02-03 take an RM address as argument M@[LOADTIMER,FF1@[2] A_#1 ]; M@[ADDTOTIMER,FF1@[3] A_#1 ]; *04 unused M@[LOADPAGE,FF1@[5] FF2@[#1] SWPAGE@[1] IFE[CHPGFLG@,1,CHPAGE@[1]] SET[CHPGFLG@,2]]; *06 = Group A; 07 = Group B; 10 = no-op *11-14 take an RM address as argument M@[WFA,FF1@[11] #1]; M@[BBFB,FF1@[12] #1]; M@[WFB,FF1@[13] #1]; M@[RF,FF1@[14] #1]; M@[BBFBX,FF1@[15] #1]; %NEXTINST and NEXTDATA usually require a call in JC@ so that a quadword ovf trap will return to the mi containing the NEXTINST/NEXTDATA; the "ODDCALL" bit is set by these forms so that MicroD will not require the successor to be at .+1. However, sometimes one of the following situations exists: (1) qovf trap is impossible (e.g., because PCF is odd); (2) the value already in TPC is an appropriate return from a qovf trap; (3) a real call is contained in the same mi. In these situations, the CNEXTINST/CNEXTDATA forms are used which impose no constraint upon the branch clause in the same mi. SKIPDATA is like NEXTDATA and CSKIPDATA like CNEXTDATA except that they discard the next byte. % M@[NEXTINST,FF1@[16] RETCL@[1] ODDCALL@[1] JC@[5] RX@[0] QRS@[#1]]; M@[CNEXTINST,FF1@[16] RX@[0] QRS@[#1]]; M@[NEXTDATA,FF1@[17] RETCL@[1] ODDCALL@[1] JC@[5] RX@[0] QRS@[#1]]; M@[CNEXTDATA,FF1@[17] RX@[0] QRS@[#1]]; M@[SKIPDATA,FF1@[17] JC@[5] ODDCALL@[1] ?]; M@[CSKIPDATA,FF1@[17] ?]; *F2 only M@[REGSHIFT,FF2@[0] ?]; M@[STKP_,FF2@[1] A_]; M@[FREEZERESULT,FF2@[2] ?]; *3 is STACKSHIFT (used only by STACK, STACK_, etc.) M@[IOSTROBE,FF2@[3] ?]; *same as STACKSHIFT M@[CYCLECONTROL_,FF2@[4] A_]; M@[SB_,FF2@[5] A_]; M@[DB_,FF2@[6] A_]; *7 is ??? *10 is BRANCHSHIFT M@[SALUF_,FF2@[11] B_ ]; *12 is a no-op M@[MNBR_,FF2@[13] A_ ]; M@[PCF_,FF2@[14] A_ ]; M@[RESETMEMERRS,FF2@[15] ?]; M@[USECOUTASCIN,FF2@[16] ?]; M@[PRINTER_,FF2@[17] A_ ]; %Memory reference clauses are encoded in one of the following forms: PFETCHn[BaseReg,RAddr,F2]; *n = 1, 2, 4 PSTOREn[BaseReg,RAddr,F2]; *n = 1, 2, 4 IOFETCHn[BaseReg,Device,F2]; *n = 4, 20 IOSTOREn[BaseReg,Device,F2]; *n = 4, 20 XMAP[BaseReg,RAddr,F2]; *INPUT, OUTPUT, or READPIPE DevAddr is (H2[10,13] or CTASK[0,3]),,H2[14,17]. *RAddr is assembled into the srcdest mi field; OtherRAddr is not used by *the operation, but will be assembled into the BaseReg field if supplied *(useful for doing R<0 b.c. in same mi or for ALU tests in the next mi). *F2 is not used by READPIPE but will be assembled if supplied. INPUT[RAddr,F2,OtherRAddr]; OUTPUT[RAddr,F2,OtherRAddr]; READPIPE[RAddr,F2,OtherRAddr]; REFRESH[RAddr]; BaseReg must normally be even and meet the same conditions as RSRC@/RSINK@; in the event the programmer really wants to use an odd base register, which will use the same RM word for both halves of the base register, he writes ODDPFETCHn, ODDPSTOREn, ODDIOFETCHn, ODDIOSTOREn, or ODDXMAP. raddr is legal if either it is STACK or (1) every 0 in the top four bits must have a matching 0 in CTASK; (2) for PFETCH2/PSTORE2 raddr must be even; for PFETCH4/PSTORE4 raddr must be quadaligned. D0Lang outputs MEMINS=1; RMOD=1 if displacement from optional F2 arg else RMOD=0 if displacement from T; RSEL=BaseReg & 77; TYPE=kind of reference; SRCDES=0 if STACK else SRCDES=raddr. % M@[WN@,ER[Wrong.number.args.for.reference,2]]; M@[OB@,ER[Base.register.#1.is.odd,2]]; * #1 = basereg, #2 = type, #3 = nargs, #4 = optional F2 M@[BR@,TYPE@[#2] MIRMOD@[#3] SET[Z@,XOR[IP[#1],RMBASE@,60]] RX@[AND[Z@,3]] RSEL@[RSHIFT[Z@,2]] IFG[Z@,57,IFE[SUBTSK@,0,,ILLRA@[#1]]] SELECT[#3,WN@[],WN@[],,FF2@[#4]] IFE[FVAL[RX1@],1,OB@[#1]] ]; M@[EB@,ER[Base.register.#1.is.even,2]]; M@[OBR@,TYPE@[#2] MIRMOD@[#3] SET[Z@,XOR[IP[#1],RMBASE@,60]] RX@[AND[Z@,3]] RSEL@[RSHIFT[Z@,2]] IFG[Z@,57,IFE[SUBTSK@,0,,ILLRA@[#1]]] SELECT[#3,WN@[],WN@[],,FF2@[#4]] IFE[FVAL[RX1@],0,EB@[#1]] ]; M@[MERE0@,ER[#1=0..stack.will.be.used,2]]; M@[MEMR0@,IFSE[#1,STACK,SRCDES@[0],SRCDES@[#1] IFE[IP[#1],0,MERE0@[#1], IFE[OR[FVAL[SRDS03@],CTASK@],FVAL[SRDS03@],,ILLRA@[#1]]]]]; SET[ODDOK@,0]; M@[ODDOK,SET[ODDOK@,1]]; *Suppress next "Not.Even" warning M@[MERNE@,IFE[ODDOK@,0,ER[WARNING:.#1.not.even,3],SET[ODDOK@,0]]]; M@[MEMR1@,IFSE[#1,STACK,SRCDES@[0],SRCDES@[#1] IFE[IP[#1],0,MERE0@[#1], IFE[OR[FVAL[SRDS03@],CTASK@],FVAL[SRDS03@], IFE[FVAL[SRDS7@],0,,MERNE@[#1]],ILLRA@[#1]]]]]; M@[MEMR3@,IFSE[#1,STACK,SRCDES@[0],SRCDES@[#1] IFE[IP[#1],0,MERE0@[#1], IFE[OR[FVAL[SRDS03@],CTASK@],FVAL[SRDS03@], IFE[FVAL[SRDS67@],0,,MERNQ@[#1]],ILLRA@[#1]]]]]; M@[IOUD@,ER[#1.unaddressable.device,2]]; M@[IODV@,SRCDES@[#1] IFE[OR[CTASK@,FVAL[SRDS03@]],FVAL[SRDS03@],,IOUD@[#1]]]; M@[ANYBR@,SET[Z@,XOR[IP[#1],RMBASE@,60]] RX@[AND[Z@,3]] RSEL@[RSHIFT[Z@,2]] IFG[Z@,57,IFE[SUBTSK@,0,,ILLRA@[#1]]]]; *type 0 is unused M@[PFETCH1, BR@[#1, 4,#0,#3] MEMR0@[#2] ?]; M@[PFETCH2, BR@[#1, 5,#0,#3] MEMR1@[#2] ?]; M@[PFETCH4, BR@[#1, 6,#0,#3] MEMR3@[#2] ?]; M@[PSTORE1, BR@[#1,10,#0,#3] MEMR0@[#2] ?]; M@[PSTORE2, BR@[#1,11,#0,#3] MEMR1@[#2] ?]; M@[PSTORE4, BR@[#1,12,#0,#3] MEMR3@[#2] ?]; M@[XMAP, BR@[#1,16,#0,#3] MEMR0@[#2] ?]; M@[IOFETCH4, BR@[#1, 1,#0,#3] IODV@[#2] ?]; M@[IOFETCH16,BR@[#1,14,#0,#3] IODV@[#2] ?]; M@[IOSTORE4, BR@[#1,15,#0,#3] IODV@[#2] ?]; M@[IOSTORE16,BR@[#1,17,#0,#3] IODV@[#2] ?]; *For T addressing in conjunction with an OtherRAddr, leave 2nd arg blank. M@[INPUT,TYPE@[7] MIRMOD@[IFG[#0,1,IFSE[#2,,2,FF2@[#2]3],2]] MEMR0@[#1] IFG[#0,2,ANYBR@[#3]] ?]; M@[OUTPUT,TYPE@[13] MIRMOD@[IFG[#0,1,IFSE[#2,,2,FF2@[#2]3],2]] MEMR0@[#1] IFG[#0,2,ANYBR@[#3]] ?]; M@[READPIPE,TYPE@[2] MIRMOD@[IFG[#0,1,IFSE[#2,,2,FF2@[#2]3],2]] MEMR0@[#1] IFG[#0,2,ANYBR@[#3]] ?]; *No task checks on REFRESH; force displacement of 0 M@[REFRESH,FF2@[0] RX@[AND[IP[#1],3]] MRTYP@[XOR[AND[360,LSHIFT[IP[#1],2]],1703]]?]; M@[ODDPFETCH1, OBR@[#1, 4,#0,#3] MEMR0@[#2] ?]; M@[ODDPFETCH2, OBR@[#1, 5,#0,#3] MEMR1@[#2] ?]; M@[ODDPFETCH4, OBR@[#1, 6,#0,#3] MEMR3@[#2] ?]; M@[ODDPSTORE1, OBR@[#1,10,#0,#3] MEMR0@[#2] ?]; M@[ODDPSTORE2, OBR@[#1,11,#0,#3] MEMR1@[#2] ?]; M@[ODDPSTORE4, OBR@[#1,12,#0,#3] MEMR3@[#2] ?]; M@[ODDXMAP, OBR@[#1,16,#0,#3] MEMR0@[#2] ?]; M@[ODDIOFETCH4, OBR@[#1, 1,#0,#3] IODV@[#2] ?]; M@[ODDIOFETCH16,OBR@[#1,14,#0,#3] IODV@[#2] ?]; M@[ODDIOSTORE4, OBR@[#1,15,#0,#3] IODV@[#2] ?]; M@[ODDIOSTORE16,OBR@[#1,17,#0,#3] IODV@[#2] ?]; EQ@[IOFETCH20,IOFETCH16]; EQ@[IOSTORE20,IOSTORE16]; EQ@[ODDIOFETCH20,ODDIOFETCH16]; EQ@[ODDIOSTORE20,ODDIOSTORE16]; *Control stuff: IM[.,0]; *Location counter for IM M@[.-3,SUB[IP[.],3]]; M@[.+3,ADD[IP[.],3]]; M@[.-2,SUB[IP[.],2]]; M@[.+2,ADD[IP[.],2]]; M@[.-1,SUB[IP[.],1]]; M@[.+1,ADD[IP[.],1]]; M@[ALUERRTST,IFE[TSKFLG@,1,ER[Alu.results.tested.following.RETURN,2]]]; *Macros insert "~@" or "~" before BC names in the program for type checks. *Regular BC'S *Complementary M@[~ALU#0,CND@[1] JC@[0] JA7@[0] ALUERRTST]; EQ@[~@ALU=0,~ALU#0]; M@[~CARRY,CND@[1] JC@[0] JA7@[1] ALUERRTST]; EQ@[~@CARRY',~CARRY]; M@[~ALU<0,CND@[1] JC@[1] JA7@[0] ALUERRTST]; EQ@[~@ALU>=0,~ALU<0]; M@[~H2BIT8',CND@[1] JC@[1] JA7@[1]]; EQ@[~@H2BIT8,~H2BIT8']; M@[~R<0,CND@[1] JC@[2] JA7@[0]]; EQ@[~@R>=0,~R<0]; M@[~R ODD,CND@[1] JC@[2] JA7@[1]]; EQ@[~@R EVEN,~R ODD]; M@[~IOATTEN',CND@[1] JC@[3] JA7@[0]]; EQ@[~@IOATTEN,~IOATTEN']; M@[~MB,CND@[1] JC@[3] JA7@[1]]; EQ@[~@MB',~MB]; M@[~INTPENDING,CND@[1]JC@[0] JA7@[0] FF2@[10]]; EQ@[~@INTPENDING',~INTPENDING]; M@[~OVF',CND@[1]JC@[0]JA7@[1]FF2@[10]ALUERRTST]; EQ@[~@OVF,~OVF']; M@[~BPCCHK,CND@[1] JC@[1] JA7@[0] FF2@[10]]; EQ@[~@BPCCHK',~BPCCHK]; *M@[~UNUSED,CND@[1] JC@[1] JA7@[1] FF2@[10]]; EQ@[~@UNUSED',~UNUSED]; M@[~QUADOVF,CND@[1] JC@[2] JA7@[0] FF2@[10]]; EQ@[~@QUADOVF',~QUADOVF]; M@[~TIMEOUT,CND@[1] JC@[2] JA7@[1] FF2@[10]]; EQ@[~@TIMEOUT',~TIMEOUT]; M@[~,]; EQ@[~@,~]; %Branch and goto macros are now identical and interchangeable. If the next mi to be executed is off-page, the macro must have a "P" following it. Thus use GOTOP[xyz] when the page of xyz differs from that of the current mi (only happens when the mi preceding the current one did LOADPAGE). % M@[DBLGOTO,IDF[~@#3,W1@[#1] W2@[#2] ~@#3,W2@[#1] W1@[#2] ~#3]]; M@[DBLGOTOP,CHPAGE@[1] DBLGOTO[#1,#2,#3]]; *Set only the high and low bits of JC so that a CS rd/wr can overrule with *the value 6. M@[GOTO,IFSE[#2,,W1@[#1] JC0@[1] JC2@[0], IDF[~@#2, W1@[#1] ~@#2, W2@[#1] ~#2 ]]]; M@[GOTOP,CHPAGE@[1] GOTO[#1,#2]]; M@[SKIP,GOTO[.+2,#1]]; M@[SKIPP,GOTOP[.+2,#1]]; *Calls return to (caller's address + 1) mod 20. M@[CALL,IFSE[#2,,RETCL@[1] JC@[5] W1@[#1],NOARGS@[CALL's]]?]; M@[CALLP,CHPAGE@[1] CALL[#1,#2]]; M@[CALLX,ODDCALL@[1] CALL[#1]]; *TASK does a CALL[.+1] in the current mi and forces the next *mi inline to do a RETURN. M@[TASK, RETCL@[1] JC@[5] W2@[.+2] SET[TSKFLG@,4]]; M@[RETN@,IFSE[#1,,RETCL@[2] JC@[6] JA7@[#2],NOARGS@[#3's]]]; M@[RETURN,RETN@[#1#2,0,RETURN]]; M@[NIRET,RETN@[#1#2,1,NIRET]]; *Used after NextInst *External References--argument is an integer M@[GOTOEXTERNAL,IFSE[#2#3,, RETCL@[2] JC@[4] JA1@[AND[3,RSHIFT[#1,6]]] JA2@[AND[#1,77]], ER[No.conditional.external.goto,2]]]; M@[CALLEXTERNAL,IFSE[#2#3,, RETCL@[3] JC@[5] JA1@[AND[3,RSHIFT[#1,6]]] JA2@[AND[#1,77]], ER[no.args.allowed.in.external.call,2]]]; M@[LOADPAGEEXTERNAL,FF1@[5] FF2@[#1]]; M@[DISP,IFSE[#2#3,,JC@[7] W1@[#1],NOARGS@[DISP's]]?]; M@[DISPP,CHPAGE@[1] DISP[#1,#2,#3]]; *To prevent MicroD allocation in reserved locations: * IMRESERVE[page #,first address,number of addresses] F@[LOCK@,0,0]; IMLOCK[LK@,0]; M@[IMRESERVE,IMLOCK[LK@,ADD[LSHIFT[#1,10],#2]] REPEAT[#3,LK@[LOCK@[1]]]]; M@[IMUNRESERVE,IMLOCK[LK@,ADD[LSHIFT[#1,10],#2]] REPEAT[#3,LK@[LOCK@[0]]]]; **??Check this?? M@[MIDASINIT,IMRESERVE[0,0,2] IMRESERVE[0,100,21] IMRESERVE[17,0,400]]; %DISPTABLE[LENGTH,MASK,VALUE] appearing in a statement causes that statement to begin a group of LENGTH consecutively-placed statements, 1 <= LENGTH <= 20. The first statement is placed so that
= VALUE. MASK defaults to 17 and VALUE to 0. Note: LENGTH+VALUE must be <= 20. % IMMASK[MASKLC@,0]; *Location counter for IMMASK M@[DISPTABLE,SET[Z@,SUB[#1,1]] IFE[AND[Z@,177760],0,,ER[DISPTABLE.ill.length]] SET[Z1@,#2] IFE[Z1@,0,SET[Z1@,17]] IFE[AND[#3,Z1@],#3,,ER[DISPTABLE.impossible.constraint]] SET[Z2@,0] SET[Z3@,0] REPEAT[20,IFE[AND[Z2@,Z1@],#3,SET[Z3@,OR[Z3@,RSHIFT[100000,Z2@]]]] SET[Z2@,ADD[Z2@,1]]] IMMASK[MASKLC@,IP[.]] MASKLC@[CONMSK@[Z3@] DTLEN@[Z@]] ]; *The ONPAGE macro changes the default page number for address assignment. M@[ONPAGE,DEFAULT[IM,PGE@[#1]]]; *Force absolute location and change default page M@[AT,@W0@[1] W0@[ADD[#1,#2]] DEFAULT[IM,PGE@[RSHIFT[ADD[#1,#2],10]]]]; M@[OPCODE,@W0@[1] W0@[ADD[2001,LSHIFT[#1,2]]] DEFAULT[IM,PGE@[ADD[4,RSHIFT[#1,6]]]]]; *LOCA[Name,Page,Offset] creates an integer Name pointing at (Page*400)+Offset M@[LOCA,SET[#1,ADD[LSHIFT[#2,10],#3]]]; *HIA[Name,Task] and LOA[Name] evaluate to the high and low constants which *OR'ed together produce the value for APCTask&APC_. M@[HIA,AND[177400,ADD[LSHIFT[#2,14],#1]]C]; M@[LOA,AND[377,#1]C]; %CHPGFLG@ is set to 2 by LOADPAGE (which sets CHPAGE@[1] if CHPGFLG@ was 1); it is 1 here in the mi after the LOADPAGE. TSKFLG@ is 2 in the instr after a TASK, 1 in the instr after that; when it is 2 a RETURN is forced; when 1 the ALUERRTST macro will produce an error. RTNILL@ is 1 when a DISPATCH or APC&APCTASK_ is done. % M@[XX1@,IFE[TSKFLG@,2,RETN@[,0]] SET[TSKFLG@,RSHIFT[TSKFLG@,1]]]; M@[XX2@,IFE[CHPGFLG@,1,CHPAGE@[1] SET[CHPGFLG@,0], SET[CHPGFLG@,IFA[W1@,0,1]]]]; M@[XX3@,IFE[REGIFLAG@,1, IFE[FVAL[ALF@],0,ER[WARNING:..no.register.interlock,3]]] SET[REGIFLAG@,0]]; M@[XX4@,SET[RTNILL@,0] IFE[FVAL[JC@],6, ER[APC.loaded.during.RETURN,2]]]; M@[IMX@,IFE[TSKFLG@,0,,XX1@[]] IFE[CHPGFLG@,0,,XX2@[]] IFE[REGIFLAG@,0,,XX3@[]] IFE[RTNILL@,1,XX4@[]]]; *The default mi modified by ONPAGE DEFAULT[IM,RSEL@[14] BSF1@[50] F2@[12] JC@[4] W1@[7777] W2@[7777] PW0@[1]]; *Macro executed after assembling each mi SETPOST[IM,IMX@]; %IM used as data stuff IM words can be assembled as data using the "LH" (left-half) and "RH" (right-half) macros defined below. Each of these takes up to 8 arguments which are either parameters or integers summed to form the value stored. NOTE: The parity must be correct or Midas will complement the IMParity bit when loading. Ways to assemble data are: TAG: DATA[LH[n1,n2,n3,n4] RH[n5,n6,n7,n8] FIXP@[] AT[...]]; *Obsolete TAG: IMDATA[LH[n1,n2,n3,n4] RH[n5,n6,n7,n8] AT[...]]; *Automatic FIXP@ % M@[LH,IFG[#0,10,TMARGS@[LH]] V0@[DPS@[#1,#2,#3,#4,#5,#6,#7,#8]]]; M@[RH,IFG[#0,10,TMARGS@[RH]] V1@[DPS@[#1,#2,#3,#4,#5,#6,#7,#8]]]; *FIXP@[...] computes parity of data in 0..37 and puts 1 or 0 in RX@ to *make the parity correct. M@[FIXP@,SET[Z@,XOR[FVAL[V0@],FVAL[V1@]]] SET[Z@,XOR[Z@,RSHIFT[Z@,10]]] SET[Z@,XOR[Z@,RSHIFT[Z@,4]]] RX@[SELECT[AND[XOR[Z@,RSHIFT[Z@,2]],3],1,0,0,1]]]; M@[DATA,.[RETCL@[2] #1]]; *Indicate "Return" so no MicroD fixup M@[IMDATA,.[(RETCL@[2] FIXP@[], #1)]]; *Conditional assemblies with :IF's nested to 4 levels SET[ALEV@,0]; *No. nested IF's SET[ASMF@,1]; *1 if assembling, 0 if not assembling SET[ASML@,1]; *1 if assembling at this level, 0 if ignoring SET[L1@,0]; SET[L2@,0]; SET[L3@,0]; SET[G1@,0]; SET[G2@,0]; SET[G3@,0]; M@[IF,SELECT[ALEV@,,SET[L1@,ASML@] SET[G1@,ASMF@], SET[L2@,ASML@] SET[G2@,ASMF@], SET[L3@,ASML@] SET[G3@,ASMF@], ER[:IF/UNLESS.nested.more.than.4.levels,1]] SET[ALEV@,ADD[ALEV@,1]] SET[ASML@,ASMF@] IFE[ASML@,1, IFE[#1,0,ASMMODE[1] SET[ASMF@,0],ASMMODE[0] SET[ASMF@,1]]]]; M@[UNLESS,IF[IFE[#1,0,1,0]]]; M@[NOIF@,ER[No.:IF.preceding.:#1,1]]; M@[ELSEIF,IFE[ALEV@,0,NOIF@[ELSEIF], IFE[ASML@,1,IFE[ASMF@,1,SET[ASMF@,0] SET[ASML@,0] ASMMODE[1], SET[ASMF@,#1] ASMMODE[IFE[ASMF@,0,1,0]]]]]]; M@[ELSE,IFE[ALEV@,0,NOIF@[ELSE], IFE[ASML@,1,IFE[ASMF@,1,SET[ASMF@,0] SET[ASML@,0] ASMMODE[1], ASMMODE[0] SET[ASMF@,1]]]]]; M@[ENDIF,SELECT[ALEV@,NOIF@[ENDIF], SET[ASMF@,1] SET[ASML@,1], SET[ASML@,L1@] SET[ASMF@,G1@], SET[ASML@,L2@] SET[ASMF@,G2@], SET[ASML@,L3@] SET[ASMF@,G3@]] SET[ALEV@,SUB[ALEV@,1]] IFE[ASMF@,1,ASMMODE[0]]]; %:TITLE[s1] prints an error message beginning with s1 showing the IM address at the beginning of assembly, which may help correlate errors with source statements when several sources are assembled at once. :TITLE also resets assembly flags to standard states. The leading ":" will cause an appropriate message to be printed when the entire assembly is suppressed inside a conditional. % M@[MSG@,ER[#1..IM.address.=.,0,IP[.]]]; M@[NAMSG@,ER[#1..not.assembled,0]]; M@[TITLE,IFE[ASMF@,1, SET[TSKFLG@,0] SET[CHPGFLG@,0] SET[REGIFLAG@,0] SET[RTNILL@,0] SET[NONQUADOK@,0] SET[ODDOK@,0] TARGET[.] SETTASK[0] MSG@[#1], NAMSG@[#1]]]; M@[END,IFE[ASMF@,1,MSG@[#1.END],NAMSG@[#1.END]]]; F@[VERS@,0,17]; VERSION[V@,0]; V@[VERS@[1]]; %Obsolete syntax still supported--programmers should convert so these can be deleted eventually. % EQ@[FORMMINUS4,FORM-4]; EQ@[STKP,NSTKP]; EQ@[ALURESULT&SALUF,ALURESULT&NSALUF]; EQ@[SALUF,NSALUF]; EQ@[APC&APCTASK,APCTASK&APC]; EQ@[APC&APCTASK_,APCTASK&APC_]; EQ@[~NOH2BIT8,~H2BIT8']; EQ@[~@NOCARRY,~@CARRY']; EQ@[~NOATTEN,~IOATTEN']; EQ@[~@NOMB,~@MB']; EQ@[~@NOTINTPENDING,~@INTPENDING']; EQ@[~NOOVF,~OVF']; EQ@[~@BPCNOCHK,~@BPCCHK']; EQ@[~@INQUAD,~@QUADOVF']; EQ@[~@NOTIMEOUT,~@TIMEOUT']; M@[NOTASKRTN,RETN@[#1#2,0,NOTASKRTN]]; *Obsolete forms that must be fixed now M@[OBS@,ER[Obsolete.#1,2]]; M@[IDF@,OBS@[IDF@--use.IDF]]; M@[NOT@,OBS@[NOT@--use.NOT]]; M@[REPEAT@,OBS@[REPEAT@--use.REPEAT]]; M@[OR@,OBS@[OR@--use.OR]]; M@[XOR@,OBS@[XOR@--use.XOR]]; M@[AND@,OBS@[AND@--use.AND]]; M@[FLX@,OBS@[FLX@--use.FIELD]]; M@[PSET,OBS@[PSET--use.LOCA]]; M@[NOMIDASINIT,OBS@[NOMIDASINIT--delete.it]]; M@[LANGVERSION,OBS@[LANGVERSION--delete.it]]; M@[MULTDIB,OBS@[MULTDIB--delete.it]]; M@[RCALL,OBS@[RCALL]]; M@[RCALLP,OBS@[RCALLP]]; M@[NEWINST,OBS@[NEWINST]]; M@[ODDBASEOK,OBS@[ODDBASEOK--use.ODDPFETCH1.etc.]]; M@[DBLBRANCH,OBS@[DBLBRANCH--use.DBLGOTO]]; M@[DBLBRANCHP,OBS@[DBLBRANCH--use.DBLGOTOP]]; M@[BRANCH,OBS@[BRANCH--use.GOTO]]; M@[BRANCHP,OBS@[BRANCHP--use.GOTOP]]; (2048)\f5