Trailing-Edge
-
PDP-10 Archives
-
BB-AS80D-SM_1986
-
microcode-sources/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