% 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
- 1720 1-bit fields bit 0 to 17
20
- 3617 2-bit fields bit 0 to 16
37
- 5416 3-bit fields bit 0 to 15
55
- 7115 4-bit fields bit 0 to 14
72
-10514 5-bit fields bit 0 to 13
106
-12013 6-bit fields bit 0 to 12
121
-13212 7-bit fields bit 0 to 11
133
-14311 10-bit fields bit 0 to 10
144
-15310 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
-22620 1-bit fields bit 0 to 17
227
-24517 2-bit fields bit 0 to 16
246
-26316 3-bit fields bit 0 to 15
264
-30015 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
-31717 left shifts of 1, ..., 17 bits

LCY[RB,shiftcount] left-cycles RB by shiftcount 1 to 17.
320
-33617 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];
*1030,10
M@[NSTKP,RX@[3]MR@[34]BSF1@[66]FF2@[3] A];
*10310,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];
*1074,4
M@[NSALUF,RX@[3]MR@[35]BSF1@[66]FF2@[3] A];
*10710,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];
*1270,10
M@[DBXREG,RX@[3]MR@[31]BSF1@[62]FF2@[15] A];
*1270,4
M@[MWXREG,RX@[3]MR@[31]BSF1@[63]FF2@[1] A];
*1274,4
M@[PCXREG,RX@[3]MR@[31]BSF1@[63]FF2@[5] A];
*12710,4
M@[PCFREG,RX@[3]MR@[31]BSF1@[63]FF2@[11] A];
*12714,4
M@[PCF.WORD,RX@[3]MR@[31]BSF1@[62]FF2@[13] A];
*12714,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];
*1430,4
M@[APC,RX@[3]MR@[24]BSF1@[67]FF2@[15] A];
*1434,14
M@[CTASK&NCIA,RX@[3]MR@[25] A];
*147
M@[CTASK,RX@[3]MR@[25]BSF1@[62]FF2@[15] A];
*1470,4
M@[NCIA,RX@[3]MR@[25]BSF1@[67]FF2@[15] A];
*1474,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];
*1570,4
M@[PARITY,RX@[3]MR@[27]BSF1@[63]FF2@[1] A];
*1574,4
M@[BOOTREASON,RX@[3]MR@[27]BSF1@[66]FF2@[3] A];
*15710,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
<address AND MASK> = 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]];