Google
 

Trailing-Edge - PDP-10 Archives - klu2_442 - byte.mic
There are 5 other files named byte.mic in the archive. Click here to see a list.
	.TOC	"Single Byte Instructions:  ILDB, LDB"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;									;
;	The following code represents a complete overhauling of the	;
;	byte oriented PDP-10 instructions.  These instructions have	;
;	been reworked with one and two word global byte pointers in	;
;	mind.  Special emphasis has been placed on high speed oper-	;
;	ation of the one word byte pointers, even where	that has meant	;
;	spending a substantial amount of CRAM; TWGs, by contrast,	;
;	have just been made to work.					;
;									;
;	The approach used for OWLs has been to minimize the amount	;
;	of computation that is not overlapped with memory reference.	;
;	This has been done by carefully initializing the SC and FE	;
;	in such a manner that the next shift count can be computed	;
;	while the current shift is taking place.  The OWG code dis-	;
;	patches into CRAM tables which set up these counts.  This	;
;	requires a lot of CRAM (one word for each possible OWG for	;
;	both loading and depositing bytes), but it eliminates the	;
;	requirement for a memory access to look up that information	;
;	in the EPT.							;
;									;
;						--QQSV			;
;									;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;
;	ILDB--Increment a byte pointer (in memory), then load the byte
;	it specifies into AC.
;	LDB--Load byte specified by byte pointer into AC.
;	The constraints immediately below are necessary to make IBP
;	work.
;
	.DCODE
134:	RW,	AC,	J/ILDB		;ILDB
	R,	AC,	J/LDB		;LDB--No write test
	.UCODE

=0****000000
ILDB:	ARX_AR,SC_P-#,#/37.,		;Save word for later dispatch
		BYTE DISP,CALL [INCRBP]	; Test for OWG, increment BP
=100
LDB:	MEM_AR,ARX_AR,			;Await possible pointer store
		SC_P-#,#/37.,BYTE DISP	; Save P; split OWL, OWG, TWG
=1100	GEN AR,EXT BYTE READ,SC_FE#,	;An OWG. Start reading the word
		AR0-3 DISP,J/OWGLDB	; Split the low and high OWGs
	MEM_AR,SET FPD,FE_S,		;A simple OWL. Save S and unwind
		EA MOD DISP,CALL [LDEA]	; byte pointer EA
	GEN AR,EXT BYTE READ,SC_FE#,	;An OWG (bit 12 is irrelevant)
		AR0-3 DISP,J/OWGLDB	; Split the low and high OWGs
	MEM_AR,SKP -VMA SEC0		;A TWG, maybe. Not in section 0
=11100	FE_S,SET FPD,
		EA MOD DISP,CALL [LDEA]	;No TWGs in section 0 (treat as OWL)
	FE_S,READ BP2			;Real TWG. Treat as global indirect
	SET FPD,CALL [LEAIND]
=11111	FIN LOAD,SC_#-SC,#/-1,		;Wait for byte word. SC = 36-(P+S)
		SKP SC0,I FETCH		; Does byte go off the top?
=
=0	CLR FPD,ARX_AR,AR_0S,SC_FE+SC,	;Yes. Byte is at top of word; try
		SKP SCAD0,J/SHFLOD	; to truncate it. Test if P off top
OWGLOD:	CLR FPD,ARX_SHIFT,AR_0S,	;Normal byte. Put at top of ARX;
		SC_FE#,J/SHFLOD		; SC = S. Set for final shift
;
;	Load byte from an OWG.  Split P&S in range 45-57 octal (in which
;	case we optimize for a byte size of 6) or 60-77 (optimize for size
;	7.)  (Unfortunately, we can't reasonably optimize for size 8 bytes,
;	as they run from 54 to 60, thus including both ranges.)  The idea
;	here is to set up the shift counts that will be required for the
;	actual byte.  Thus, SC_36.-(P+S) and FE_S.
;
=1011
OWGLDB:	FE_#,#/6,SH DISP,J/OWGLOW	;Range is 45-57. Assume size 6
	FE_#,#/7,SH DISP,J/OWGHIG	;Range is 60-77. Assume size 7
;
=00000
OWGLOW:					;Dummy label (40-44 are OWLs)
=00101	FIN LOAD,I FETCH,CLR FPD,	;45 S=6, P=36 (bad). Clear the AC
	    CLR ARX,SC_#,#/36.,J/SHFLOD	;
	FIN LOAD,I FETCH,CLR SC,J/OWGLOD;46 S=6, P=30
	FIN LOAD,I FETCH,SC_#,#/6,J/OWGLOD;47 S=6, P=24
	FIN LOAD,I FETCH,SC_#,#/12.,J/OWGLOD;50 S=6, P=18
	FIN LOAD,I FETCH,SC_#,#/18.,J/OWGLOD;51 S=6, P=12
	FIN LOAD,I FETCH,SC_#,#/24.,J/OWGLOD;52 S=6, P=6
	FIN LOAD,I FETCH,SC_#,#/30.,J/OWGLOD;53 S=6, P=0

	FIN LOAD,I FETCH,CLR FPD,	;54 S=8, P=36 (bad). Clear AC with
	    CLR ARX,SC_#,#/36.,J/SHFLOD	; later shift
	CLR SC,J/SIZE8L			;55 S=8, P=28. Correct the size
	SC_#,#/8,J/SIZE8L		;56 S=8, P=20
	SC_#,#/16.,J/SIZE8L		;57 S=8, P=12
OWGHIG:	SC_#,#/24.,J/SIZE8L		;60 S=8, P=4

	FIN LOAD,I FETCH,CLR FPD,	;61 S=7, P=36 (bad). Clear AC with
	    CLR ARX,SC_#,#/36.,J/SHFLOD	; later shift
	FIN LOAD,I FETCH,CLR SC,J/OWGLOD;62 S=7, P=29. Ready to shift
	FIN LOAD,I FETCH,SC_#,#/7,J/OWGLOD;63 S=7, P=22
	FIN LOAD,I FETCH,SC_#,#/14.,J/OWGLOD;64 S=7, P=15
	FIN LOAD,I FETCH,SC_#,#/21.,J/OWGLOD;65 S=7, P=8
	FIN LOAD,I FETCH,SC_#,#/28.,J/OWGLOD;66 S=7, P=1

	FIN LOAD,I FETCH,CLR FPD,	;67 S=9, P=36 (bad). Clear the AC
	    CLR ARX,SC_#,#/36.,J/SHFLOD
	CLR SC,J/SIZE9L			;70 S=9, P=27. Correct size
	SC_#,#/9,J/SIZE9L		;71 S=9, P=18
	SC_#,#/18.,J/SIZE9L		;72 S=9, P=9
	SC_#,#/27.,J/SIZE9L		;73 S=9, P=0

	FIN LOAD,I FETCH,CLR FPD,	;74 S=18, P=36 (bad). Clear AC
	    CLR ARX,SC_#,#/36.,J/SHFLOD
	FIN LOAD,I FETCH,CLR FPD,J/HLRZ	;75 S=18, P=18. This is HLRZ, folks
	FIN LOAD,I FETCH,CLR FPD,J/HRRZ	;76 S=18, P=0. Same as HRRZ

	AR_MEM,J/ILLOWG			;77 Illegal. Force UUO
;
SIZE8L:	FIN LOAD,I FETCH,FE_#,#/8,J/OWGLOD;Fix up all size 8 bytes
SIZE9L:	FIN LOAD,I FETCH,FE_#,#/9,J/OWGLOD;Do the same for size 9
	.TOC	"Single Byte Instructions:  DPB, IDPB"
;
;	IDPB--Increment a byte pointer (in memory), then store the rightmost
;	bits of the AC into the byte it specifies.
;	DPB--Store the rightmost bits of the AC into byte specified by
;	pointer.
;	The constraints immediately below are necessary to make IBP
;	work.
;
	.DCODE
136:	RW,	M,	J/IDPB		;IDPB
	R,	M,	J/DPB		;DPB--No write test if no increment
	.UCODE

=0****000000
IDPB:	ARX_AR,SC_P-#,#/37.,BYTE DISP,	;Save for dispatch later, test
		CALL [INCRBP]		; for OWG, increment pointer
=100
DPB:	MEM_AR,ARX_AR,SC_P-#,#/37.,	;Await possible pointer store
		BYTE DISP		; Save P; test OWL, OWG, TWG
=1100	GEN AR,EXT BYTE RPW,SC_FE#,	;An OWG. Start byte read; SC_2
		AR0-3 DISP,J/OWGDPB	; Split into OWG groups
	MEM_AR,SET FPD,FE_-S,		;An OWL. Save byte size
		EA MOD DISP,J/DPEA	; and compute byte word address
	GEN AR,EXT BYTE RPW,SC_FE#,	;An OWG. See above
		AR0-3 DISP,J/OWGDPB	; (Bit 12 is irrelevant here)
	MEM_AR,SKP -VMA SEC0,J/DEPTWG	;Maybe a TWG. Never in section 0
336:					;Constrain for parity (see DPEA)
GUDSIZ:	FE_-SC-1,SC_FE,MQ_FM[AC0],J/DEPBYT;Copy byte to MQ; FE_36-P, SC_-S
337:	FE_#+SC,#/1,J/GUDSIZ		;Size too large. Force to 36-P
=
=0
DEPTWG:	MEM_AR,SET FPD,FE_-S,		;No TWGs allowed in section 0
		EA MOD DISP,J/DPEA
	FE_-S,READ BP2			;A TWG. Start reading second word
	SET FPD,J/DEAIND		;And dive into indirection loop
;
;	At this point, we have FE = 36-P and SC = -S with memory being
;	loaded into both AR and ARX.  Also, both S and P have been forced
;	into the range 0:36.  The deposit is done with three shifts:
;
;	Shift 1:  AR and ARX have memory; shift count = 36-P
;	Shift 2:  AR has byte to deposit, ARX has previous shift;
;		  shift count = 36-S
;	Shift 3:  AR and ARX have previous shift; shift count = P+S
;
DEPBYT:	AR_MEM,ARX_MEM,TIME/3T,		;Wait for memory load
		SC_FE,FE_#+SC,#/36.	;SC_36-P, FE_36-S
DEPOWG:	AR_MQ,ARX_SHIFT,		;Fetch byte, do first shift
		SC_FE,FE_#-SC,#/72.	;SC_36-S, FE_72-(36-P) = 36+P
	AR_SHIFT,ARX_SHIFT,SC_FE-SC	;Next shift; SC_(36+P)-(36-S) = P+S
RELMEM: AR_SHIFT,STORE,CLR FPD,
		SR_0,J/STMEM		;Last shift; store and clear FPD
;
;	Deposit byte with an OWG.  Once again, P&S gets split into the
;	ranges 45-57 octal (optimized for size 6) and 60-77 (optimized
;	for size 7).  In addition to setting SC to 36-P and FE to 36-S,
;	this code also copies AC to MQ.  Since MQ_FM[] uses the # field,
;	this is accomplished by reading the AC into ARX and then copying
;	it to the MQ just before the cache can step on ARX with the
;	byte data.  The timing for this is a tad hairy, but it seems to
;	work.
;
=1011
OWGDPB:	ARX_FM[AC0],FE_#,#/30.,SH DISP,	;Low range OWG. Assume size 6
		TIME/3T,J/ODLOW		;Fetch byte to store
	ARX_FM[AC0],FE_#,#/29.,SH DISP,	;High range. Assume size 7
		TIME/3T,J/ODHIGH
;
=00000
ODLOW:					;Another dummy (40-44 are OWLs)
=00101	AR_MEM,CLR SC,J/RELMEM		;45 S=6, P=36 (bad). Release memory
	MQ_ARX,AR_MEM,SC_#,#/6,J/DEPOWG	;46 S=6, P=30. Copy byte to MQ
	MQ_ARX,AR_MEM,SC_#,#/12.,J/DEPOWG;47 S=6, P=24
	MQ_ARX,AR_MEM,SC_#,#/18.,J/DEPOWG;50 S=6, P=18
	MQ_ARX,AR_MEM,SC_#,#/24.,J/DEPOWG;51 S=6, P=12
	MQ_ARX,AR_MEM,SC_#,#/30.,J/DEPOWG;52 S=6, P=6
	MQ_ARX,AR_MEM,SC_#,#/36.,J/DEPOWG;53 S=6, P=0

	AR_MEM,CLR SC,J/RELMEM		;54 S=8, P=36. Just release memory
	MQ_ARX,SC_#,#/8,J/SIZE8D	;55 S=8, P=28. Copy byte, fix size
	MQ_ARX,SC_#,#/16.,J/SIZE8D	;56 S=8, P=20
	MQ_ARX,SC_#,#/24.,J/SIZE8D	;57 S=8, P=12
ODHIGH:	MQ_ARX,SC_#,#/32.,J/SIZE8D	;60 S=8, P=4

	AR_MEM,CLR SC,J/RELMEM		;61 S=7, P=36 (bad). Release memory
	MQ_ARX,AR_MEM,SC_#,#/7,J/DEPOWG	;62 S=7, P=29. Copy byte to MQ
	MQ_ARX,AR_MEM,SC_#,#/14.,J/DEPOWG;63 S=7, P=22
	MQ_ARX,AR_MEM,SC_#,#/21.,J/DEPOWG;64 S=7, P=15
	MQ_ARX,AR_MEM,SC_#,#/28.,J/DEPOWG;65 S=7, P=8
	MQ_ARX,AR_MEM,SC_#,#/35.,J/DEPOWG;66 S=7, P=1

	AR_MEM,CLR SC,J/RELMEM		;67 S=9, P=36, no good. Let go!
	MQ_ARX,SC_#,#/9,J/SIZE9D	;70 S=9, P=27. Copy byte, fix size
	MQ_ARX,SC_#,#/18.,J/SIZE9D	;71 S=9, P=18
	MQ_ARX,SC_#,#/27.,J/SIZE9D	;72 S=9, P=9
	MQ_ARX,SC_#,#/36.,J/SIZE9D	;73 S=9, P=0

	AR_MEM,CLR SC,J/RELMEM		;74 S=18, P=36. Just unpause memory
	AR_MEM,CLR FPD,J/HRLM		;75 S=18, P=18. Treat as HRLM
	AR_MEM,CLR FPD,J/HLL		;76 S=18, P=0. Treat as HRRM

	FIN LOAD,STORE,J/ILLOWG		;77 Illegal byte pointer. UUO it
;
SIZE8D:	AR_MEM,FE_#,#/28.,J/DEPOWG	;Fix FE for size 8 bytes
SIZE9D:	AR_MEM,FE_#,#/27.,J/DEPOWG	;Same for size 9
	.TOC	"Single Byte Instructions:  IBP, ADJBP"
;
;	IBP--Increment a byte pointer (in memory).
;	ADJBP--Adjust a one or two word byte pointer from memory by an
;	amount specified by the (non zero) AC.
;	Both of these instructions key off of the same op code (133);
;	they are distinguished by ADJBP having a non zero AC field.
;
;	The IBP case is rather simple.
;
	.DCODE
133:	R,	B/0,	J/IBP		;IBP and ADJBP--must adjoin FSC
	.UCODE

1503:					;[345] In same block of 8 as FSC
IBP:	SC_P-#,#/37.,ARX_0S,SKP AC EQ 0	;[407] Test for OWG. IBP or ADJBP?
=11010
IBPTST:	SC_-S,MQ_ARX,SKP SC0,J/ADJBP	;[407] ADJBP. Clear MQ0. OWG?
	SKP SC0,CALL [INCRBP]		;IBP. Test for OWG and do it
=11111	FIN STORE,CLR FPD,I FETCH,J/NOP	;Tidy up and leave
;
;	ADJBP is handled separately for OWGs and OWL/TWGs.  We consider
;	the latter case first.
;	Step 1:  figure out the byte capacity of a word.  This is broken
;	into the capacity to the left of the current byte (including the
;	byte itself) and the capacity to the right of the byte.  If these
;	add up to zero, then the byte can't fit in a word, and we return
;	to the user with no divide set.  If the byte size is zero, we
;	return with the pointer as is.
;	For this version, we compute the capacities by using repeated
;	subtraction.  Since the numbers involved are typically no greater
;	than five or six (and are never bigger than 36) this will be faster
;	than division.
=0
ADJBP:	FE_P,ARX_2+MQ0,AR0-3 DISP,J/ADJOWG;[407] OWG. Split on range
	FE_P,SC/SCAD,ARX_0S,SKP SC0	;OWL/TWG. Is the size zero?
=0	SKP -VMA SEC0,J/OWLCPY		;Yes. Test for possible TWG
	MQ_ARX,FE_FE-S,SKP SCAD0	;No. Clear MQ. Bytes to the right?
=0
CAPLOW:	ARX_ARX+1,FE_FE-S,SKP SCAD0,	;Yes. Count the byte and look
		J/CAPLOW		; for another one
	BR/AR,BRX/ARX,ARX_-2+MQ0,	;No more. Save count and pointer
		P_#-SC,#/36.		; and set up next count
=0
CAPHGH:	P_P-S,ARX_ARX+1,SKP SCAD0,	;Count possible byte on left,
		J/CAPHGH		; saving alignment info
	T0_AR,AR_ARX+BRX+1		;All counted. Get total capacity
	SKP AR NZ			;Will any bytes fit into word?
=0	SET NO DIVIDE,I FETCH,J/NOP	;[422] No. This is pretty silly
;
;	Step 2:  generate a modified adjustment count and compute the
;	number of words to move and the relative byte position within
;	the word.  All adjustments are done relative to the first byte
;	in the word, so that the resulting quotient is the actual
;	number of words to add to the base address.  If the adjustment
;	is negative, however, we must back up the quotient by one and
;	offset the remainder by the capacity if it is non zero.
;
;	In order to speed up the division, the absolute value of the
;	modified adjustment is broken into ranges of up to 63, 64 to
;	2**18-1, and 2**18 or greater.  This lets us use step counts of
;	7, 19, and 36, respectively, saving a lot of time for the most
;	common cases.
;
;	For this portion of the work, OWGs and OWLs are identical.
;
ADJOIN:	ARX_ARX+FM[AC0],SC_#,#/30.	;Compute modified adjustment
	T1_AR,BR/AR,AR_ARX,BRX/ARX,	;Divisor is capacity. Is modified
		ARX_0S,SIGNS DISP,TIME/2T; adjustment negative?
=0111	AC0_AR,BRX/ARX,ARX_AR (AD),	;No. Clear BRX; use adjustment as
		ARL_ARL.S,ARR+MQ_0.S,	; dividend, and look at high order
		J/POSADJ		; half of dividend for speedup
	AC0_AR,BRX/ARX,ARX_-BRX,	;Yes. Negate adjustment for both
		ARL/ADX,ARR+MQ_0.S	; dividend and test
POSADJ:	AR_ARX (ADX),ARX_SHIFT,		;Generate high order 30 bits of
		FE_#,#/36.,SKP AR NZ	; dividend. Are high 18 bits zero?
=0	ARX_SHIFT,SC_FE,		;Yes. Align low six bits to top of
		SKP ARX NZ,J/SMALDV	; word. Is that enough?
	ARX_AR*2,CLR AR,FE_#,#/33.,SC_FE;Need long division. Align dividend
=000
ADJDIV:	DIVIDE,AR_2(AR-BR),ARX/ADX*2,	;Do first divide step
		CALL [DIVS3]		; and call subroutine for the rest
=010
SMALDV:	AR_0S,FE_#,#/4,CALL [DIVS1]	;Very short division is adequate
	ARX_AR SWAP,AR_0S,FE_#,#/16.,	;Medium size needed. Put significant
		J/ADJDIV		; dividend bits in proper spot
;
;	Return from division is either 6 (negative dividend) or 7
;	(non negative dividend).  We tuck the negative offset code in
;	at 4 and 5 for convenience.
;
NEGADJ:	ARX_-BRX,J/ADJUST		;Zero remainder. Negate quotient
	AR_AR+FM[T1],J/ADJUST		;Non zero. Offset by capacity
;
;	On exit from division, AR has the signed remainder and ARX and
;	BRX have the positive quotient.  If the dividend was negative,
;	we must either negate the quotient or negate and subtract one
;	(thus one's complementing it) depending upon whether there was
;	a non zero remainder.
;
	ARX_BRX COMP,SKP AR0,J/NEGADJ	;Negative dividend. Complement
					; quotient and test remainder
;
;	Step 3:  add the final quotient to the address, and offset the
;	byte into the word by the adjusted remainder.  To do this, we
;	must finally differentiate an OWL from a TWG.  (Recall that we
;	saved most of the original byte pointer (including bit 12) in
;	T0 before we did the division.)  In any event, for an OWL we
;	add the quotient to the right half of the byte pointer; for a
;	TWG we fetch the second word and then add the quotient to bits
;	6-35 if it's global, to bits 18-35 if it's local.
;
;	After this, we subtract the byte pointer S field from (36 - the
;	alignment information left in the P field) precisely remainder
;	times (recall that division copied SC, preloaded with 36, into
;	FE when it finished).  That's about it.
;
;	OWGs split off their separate way.
;
ADJUST:	MQ_AR,AR_T0,SR DISP		;Remainder to MQ. OWG or OWL/TWG?
=1110	BR/AR,SC_P+S,MQ_MQ-1,		;OWL/TWG. Copy pointer, add first
		BYTE DISP,J/ADJTWG	; S, generate count. Perhaps TWG?
	FE_P+1,BR/AR,AR_MQ,		;An OWG. Grab initial P&S and
		I FETCH,J/SNATCH	; set up quotient addition
;
=101
ADJTWG:	FE_FE-SC,ARL_ARL,ARR_ARX+BR,	;OWL. Adjust address; initialize P
		ARX/MQ,J/ADJP
	FE_FE-SC,AR_ARX,ARX_AR (AD),	;Perhaps TWG. Move quotient to AR
		SKP -VMA SEC0		; No TWGs allowed in section 0
=00	ARL_ARXL,ARR_AR+BR,		;Section 0. An OWL in TWG's clothing
		ARX/MQ,J/ADJP
	BR/AR,BRX/ARX,VMA_VMA+1,LOAD AR,;A real TWG. Keep quotient and
		CALL [XFERW]		; remainder while fetching address
=11	SC_P,AR_AR+BR,ARX_AR,SKP AR0	;Assume global address. Is it?
=0	P_SC,J/TWGDUN			;Yes. Use 30 bit addition
	ARL_ARXL,ARR_ARX+BR		;No. Foolish, but 18 bits is enough
TWGDUN:	AC1_AR,AR_BRX,ARX/MQ		;Store address; restore first word
;
;	Address has been adjusted.  Adjust P by remainder bytes.
;
=100
ADJP:	FE_FE-S,P_SCAD,ARX_ARX-1,	;Step to next byte and count
		SKP ARX0,J/ADJP		; down the remainder
TWGCPY:	I FETCH,J/STORAC		;Adjustment done. Load AC0
;
;	If the byte size is zero, we just load AC0, or ACs 0 and 1 if it's
;	a TWG.
;
=111	VMA_VMA+1,LOAD AR,J/TWJUNK	;[413][424] A TWG. Use DMOVE code
;
=0
OWLCPY:	I FETCH,J/STORAC		;Section 0, an OWL. Just load AC0
	BYTE DISP,TIME/2T,J/TWGCPY	;Not section 0. Test AR12 for TWG
;
;	OWGs use the same basic algorithm as OWLs and TWGs, but the
;	actual implementation of steps 1 and 3 is quite different.
;	Step 1:  get the byte capacity of the word and current offset
;	of the OWG within the word.  Note that OWGs may be split into
;	ranges of sizes, with the capacity identical for each OWG within
;	a range.  The current offset within the word can be computed by
;	subtracting the range base + 1 from the P&S field.  The range base
;	is saved in the OWG for later final adjustment.  The capacity is
;	computed in a rather wry way:  ARX is initially loaded with the
;	capacity - 4; later, AR is set to -1.  When AR*4 is subtracted
;	from ARX, AR*4 will be -4 as long as ARX was positive.  The only
;	negative case is for a capacity of 2 (for 18 bit bytes); that one
;	is special cased.
;
=1000
ADJOWG:					;40:43. No OWGs here
=1001	ARX_2+MQ0,TIME/2T,P_#,#/45,	;44:47. Size 6: capacity 6, base 45
		SC/SCAD,J/OWGCOM	;[407]
	ARX_2+MQ0,TIME/2T,P_#,#/45,	;50:53. More size 6
		SC/SCAD,J/OWGCOM	;[407]
	ARX_0S,P_#,#/54,SC/SCAD,J/OWGCOM;54:57. Size 8: capacity 4, base 54
	GEN FE-#,#/61,SKP SCAD0,J/EIGHT7;60:63. Either size 8 or size 7
	GEN FE-#,#/67,SKP SCAD0,J/SEVEN9;64:67. Either size 7 or size 9
	ARX_0S,P_#,#/67,SC/SCAD,J/OWGCOM;70:73. Size 9: capacity 4, base 67
	GEN FE-#,#/77,SKP SCAD0		;74:77. Is this an illegal pointer?
=0	AR_BR,J/UUO			;77 is no good. UUO it
	BRX/ARX,ARX_1S,P_#,#/74,SC/SCAD,;74:76. Size 18: capacity 2, base 74
		J/OWGCOM		; Save size; force ARX negative
;
=0
EIGHT7:	ARX_1,TIME/2T,P_#,#/61,SC/SCAD,	;61:63. Size 7: capacity 5, base 61
		J/OWGCOM
	ARX_0S,P_#,#/54,SC/SCAD,J/OWGCOM;60 is the last size 8 byte
;
=0
SEVEN9:	ARX_0S,P_#,#/67,SC/SCAD,J/OWGCOM;67 is the first size 9 byte
	ARX_1,TIME/2T,P_#,#/61,SC/SCAD	;64:66 are the last size 7 bytes
OWGCOM:	T0_AR,AR_1S,FE_FE-SC-1		;Save pointer; find initial offset
	P_FE,ARX_ARX-AR*4,SKP ARX0	;Try to get capacity. Is it 2?
=0	BRX/ARX,ARX_AR,AR_SIGN,		;No. Save it; set up offset and
		SC_#,#/6,J/OFSHFT	; shift count for offset generation
	ARX_AR,AR_SIGN,SC_#,#/6		;Yes. Size was loaded above
OFSHFT:	AR_BRX,ARX_SHIFT,SR_1,J/ADJOIN	;Mark OWG and rejoin for step 2
;
;	Step 3: add the final quotient to the address, and offset the OWG
;	into the word by remainder bytes.  Since this becomes a simple
;	integer add, this portion is rather trivial.
;
SNATCH:	SC_EA,AR_ARX+BR,SR_0		;Grab offset; adjust address
	P_FE+SC,J/STAC			;Add proper offset to P&S. Done!
.TOC	"Subroutines for Single Byte Instructions"
;
;	INCRBP--Subroutine to increment a byte pointer.  The first (or
;	only) word of the relevant pointer is in AR.  Call with SC_P-#,
;	#/37.,BYTE DISP, thus testing for OWG and first part done
;	simultaneously.  If FPD is set, this routine returns 4 without
;	doing anything; otherwise, the pointer will be incremented and
;	the store will have been started.  Return 4 if an OWL or TWG
;	must recompute SC or on any OWG, 15 if an OWL and SC is OK, and
;	17 if possibly a TWG with SC OK.  Note that ARX must have the
;	first byte pointer word on exit if this is an OWL or TWG.
;
=010					;Test FPD and OWG
INCRBP:	SC_FE#,SET FPD,AR0-3 DISP,J/OWGINC;OWG, no FPD. SC_2; test edges
	P_P-S,BYTE DISP,J/BYTINC	;No OWG, no FPD. Check for overflow
	RETURN4				;OWG, FPD. Forget it
	RETURN4				;No OWG, FPD. No increment needed
;
;	Either OWL or TWG.  Check which; if no overflow, it doesn't really
;	matter.
;
=100
BYTINC:	SC_P-#,#/37.,STORE,RETURN15	;OWL, no overflow. Store and leave
	FE_#,#/36.,GEN AR+1,		;OWL, overflow. Compute new P and
		TIME/2T,J/OWLINC	; set up new address portion
	SC_P-#,#/37.,STORE,RETURN17	;TWG, no overflow. Just like OWL
	FE_#,#/36.,GEN AR+1,TIME/2T,	;TWG, overflow. Compute new P and
		SKP -VMA SEC0		; test for valid TWG
=0
OWLINC:	P_FE-S,ARR_AR+1,TIME/2T,STORE,	;OWL. Increment address, set new P
		J/SNARFP
	P_FE-S.S,VMA_VMA+1,LOAD AR	;TWG. Set new P, fetch second word
	ARX_AR,AR_MEM			;Save first word, await second
	SC_P,BR/AR,SKP AR0,AR_AR+1	;Increment address, check I/EFIW
=0	P_SC#,STORE,J/STORTG		;EFIW. Do full global increment
	ARL_BRL,STORE			;IFIW. Just increment right half
STORTG:	FIN STORE,AR_ARX,VMA_VMA-1,	;Finish second word
		STORE,RETURN4		; and store first
;
SNARFP:	ARX_AR,SC_P-#,#/37.,RETURN15	;[351] Save offset P, new pointer
;
;	An OWG.  53, 60, 66, 73, 76, and 77 need special handling.
;	All others just tick the P&S field.
;
=1000
OWGINC:					;40:43. No OWGs here
=1001	P_P+1,STORE,RETURN4		;44:47. No special handling
	GEN AR+1,GEN P-#,#/53,		;50:53. 53 becomes 46
		SKP SCAD NE,J/OVER6
	P_P+1,STORE,RETURN4		;54:57. No special handling
	GEN AR+1,GEN P-#,#/60,		;60:63. 60 becomes 55
		SKP SCAD NE,J/OVER8
	GEN AR+1,GEN P-#,#/66,		;64:67. 66 becomes 62
		SKP SCAD NE,J/OVER7
	GEN AR+1,GEN P-#,#/73,		;70:73. 73 becomes 70
		SKP SCAD NE,J/OVER9
	GEN AR+1,SC_P+1,SH DISP		;74:77. Test low P&S bits
=1100	P_SC#,STORE,RETURN4		;74 becomes 75. Store and leave
NXTOWG:	P_SC#,STORE,RETURN4		;75 becomes 76. Store and leave
	AR_AR+1,TIME/2T,SC_#,#/75,	;76 becomes 75. Increment address
		J/NXTOWG		; first
ILLOWG:	MEM_AR,CLR FPD,J/IOCHK		;[414] 77 Illegal byte pointer
;
=0
OVER6:	AR_AR+1,TIME/2T,SC_#,#/46,J/NXTOWG;53. Increment address first
	P_P+1,STORE,RETURN4		;Others just tick P&S
;
=0
OVER7:	AR_AR+1,TIME/2T,SC_#,#/62,J/NXTOWG;66. Increment address first
	P_P+1,STORE,RETURN4		;Others just tick P&S
;
=0
OVER8:	AR_AR+1,TIME/2T,SC_#,#/55,J/NXTOWG;60. Increment address first
	P_P+1,STORE,RETURN4		;Others just tick P&S
;
=0
OVER9:	AR_AR+1,TIME/2T,SC_#,#/70,J/NXTOWG;73. Increment address first
	P_P+1,STORE,RETURN4		;Others just tick P&S
;
;	LDEA--Subroutine to compute the effective address of a byte from
;	a one word local byte pointer.  Called with the byte pointer in ARX
;	and EA MOD DISP on it.
;	LEAIND--Entry point for two word global EA calculation on the
;	second pointer word.  Called with READ BP2 on the second pointer
;	word.
;	Both entries return 37 with the byte being loaded into AR, and
;	with the FE added to SC.
;	Warning:  two of the words below (LDEA+1, LEAIND+5) cannot have
;	their parity generated directly by the assembler.  The SKP AR0 macro
;	can be used to force correct parity.  It will be ignored, since
;	J/37.
;
=1100
LDEA:	GEN AR,BYTE LOAD,		;No index, no indirect. Load byte
		SC_FE+SC,RETURN37	; word
	GEN AR+XR,INDEXED,BYTE LOAD,	;Index, no indirect. Add index
		SC_FE+SC,RETURN37	; register to generate byte address
	GEN AR,BYTE INDRCT,		;No index, indirect. Test for
		SKP INTRPT,J/LEAIND	; interrupt
	GEN AR+XR,INDEXED,BYTE INDRCT,	;[350] Index, indirect. Add index
		SKP INTRPT		; register and test for interrupt
=00
LEAIND:	ARX_MEM,LONG EN,CALL [BYTIND]	;No interrupt. Unwind indirection
	ARX_MEM,TAKE INTRPT		;Interrupted. Blow this place
	XR,EA MOD DISP,TIME/3T,J/LDEA	;Local word at end. Untangle it
	XR,EA MOD DISP,TIME/3T		;Global word at end. Indexed?
=1110	GEN ARX,GLOBAL,BYTE LOAD,	;No indexing. Read global word
		SC_FE+SC,RETURN37	; and add FE to SC
	GEN ARX+XR,GLOBAL,BYTE LOAD,	;Indexing. Add index to address
		SC_FE+SC,RETURN37,	; and do otherwise the same
		SKP AR0			; (This forces odd parity)
;
;	DPEA--Routine to compute the effective address of a byte from
;	a one word local byte pointer to be used in a deposit operation.
;	Entered with the byte pointer in ARX and EA MOD DISP on it.
;	DEAIND--Entry point for two word global EA calculation on the
;	second pointer word.  Entered with READ BP2 on the second pointer
;	word.
;	Both entries return to GUDSIZ testing the sign of FE-SC-1.
;	[340] This code has been desubroutinized for now, since it
;	must be called from an odd address.
;	WARNING:  two of the words below (DPEA+1, DEAIND+5) cannot
;	have their parity generated by the assembler.  Since the SKIP
;	field is already busy, we use the MQ field and set MQ/SH when
;	we need to generate parity.  GUDSIZ is constrained to be at an
;	address with even parity, so we don't have to worry about things
;	moving around. [412]
;
=1100
DPEA:	GEN AR,BYTE RPW,GEN FE-SC-1,	;No index, no indirect. Load byte
		SKP SCAD0,J/GUDSIZ	; word, test word underflow
	GEN AR+XR,INDEXED,BYTE RPW,	;Index, no indirect. Add index
		GEN FE-SC-1,SKP SCAD0,	; register, load byte, test word
		MQ/SH,J/GUDSIZ		; underflow, and force odd parity
	GEN AR,BYTE INDRCT,		;No index, indirect. Start read
		SKP INTRPT,J/DEAIND	; and test for interrupt
	GEN AR+XR,INDEXED,BYTE INDRCT,	;Index, indirect. Add index
		SKP INTRPT		; register, read, test interrupt
=00
DEAIND:	ARX_MEM,LONG EN,CALL [BYTIND]	;No interrupt. Unwind indirection
	ARX_MEM,TAKE INTRPT		;Interrupted. Blast out sideways
	XR,EA MOD DISP,TIME/3T,J/DPEA	;Local word at end. Decode further
	XR,EA MOD DISP,TIME/3T		;Global end word. Indexed?
=1110	GEN ARX,GLOBAL,BYTE RPW,	;No index. Read byte word
		GEN FE-SC-1,SKP SCAD0,	; and test byte underflow
		J/GUDSIZ
	GEN ARX+XR,GLOBAL,BYTE RPW,	;Index. Add it in, read byte word,
		GEN FE-SC-1,SKP SCAD0,	; and test byte underflow
		J/GUDSIZ		;Can force odd parity here
;
;	BYTIND--Subroutine to unwind some indirection for an OWL or
;	a TWG.  Call with current indirect word in ARX.  Return 2 if
;	final word is local (possibly indirected), 3 if it is global.
;	Return 0 if final word is global indirect, in which case we
;	will dive back in again if no interrupt is pending.
;
BYTIND:	AR_ARX,XR,EA MOD DISP,TIME/3T	;Dispatch on global indirection
=0011	XR,EA MOD DISP,TIME/3T,J/GLBIND	;Global indirect. Test indexing
	RETURN3				;Global, no indirect. Done for now
	FE_#,#/24,J/PF24		;Both bits 0 and 1 set. No good
	RETURN2				;Local word. Let main line handle it
;
=1110
GLBIND:	GEN ARX,GLOBAL,BYTE INDRCT,	;No indexing. Fetch next word in
		SKP INTRPT,RETURN0	; loop, testing for interrupt
	GEN ARX+XR,GLOBAL,BYTE INDRCT,	;Indexing. Add in index and do
		SKP INTRPT,RETURN0	; similarly