% D0Lang.mc
Edit history:
10 Jul 80 by Fiala	Added WHILE, IMMASK, DISPTABLE
 2 Jun 80 by Fiala	Added CNEXTINST
30 Apr 80 by Fiala	Allowed RM to be referenced (for br cond) in
			Input, Output, and ReadPipe memory refs.

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[10.Jul.80--D0Lang.version.8,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];	*Does a call from an odd location; indicates that
			*even though this mi has the ostensible form of a
			*CALL, it does not actually transfer control to W2.
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@]]]
];

M@[MERNQ@,ER[WARNING:.#1.not.quadaligned,3]];
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
M@[ALURESULT&SALUF,RX@[3]MR@[35] A];			*107
  M@[ALURESULT,RX@[3]MR@[35]BSF1@[63]FF2@[1] A];	*107	4,4
  M@[SALUF,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] FF2@[3] A];	*163, STACKSHIFT
M@[STACK&+2←,RX@[3] MR@[20] FF2@[3] LR@[1] LU←];
M@[STACK&+3,RX@[3] MR@[21] FF2@[3] A];	*167, STACKSHIFT
M@[STACK&+3←,RX@[3] MR@[21] FF2@[3] LR@[1] LU←];
M@[STACK&-3,RX@[3] MR@[23] FF2@[3] A];	*177, STACKSHIFT
M@[STACK&-3←,RX@[3] MR@[23] FF2@[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[raddr,F2];	*deviceaddr is (H2[10,13] or CTASK[0,3]),,H2[14,17]
OUTPUT[raddr,F2];
READPIPE[raddr];
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]]]]];

M@[MERNE@,ER[WARNING:.#1.not.even,3]];
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]]];

*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] ?];
*An RM address may be independently specified for a branch condition with
*INPUT, OUTPUT, and READPIPE (except when DF2 addressing is used)
*If no RM address is specified RX@ and RSEL@ will select RM 0.
M@[INPUT, MIRMOD@[IFG[#0,1,FF2@[#2]3,2]] TYPE@[7] MEMR0@[#1] ?];
M@[OUTPUT,MIRMOD@[IFG[#0,1,FF2@[#2]3,2]] TYPE@[13] MEMR0@[#1] ?];
M@[READPIPE,MIRMOD@[2] TYPE@[2] MEMR0@[#1] ?];
*No task checks on REFRESH
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]];

*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]]];

%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's.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@[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]
    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@[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@[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]];