.TOC "ADD, SUB" .DCODE 270: R-PF, AC, J/ADD ;ADD I-PF, AC, J/ADD ;ADDI RPW, M, J/ADD ;ADDM RPW, B, J/ADD ;ADDB .UCODE =0****00**** ADD: AR_AR*AC0,AD/A+B,AD FLAGS,EXIT = .DCODE 274: R-PF, AC, J/SUB ;SUB I-PF, AC, J/SUB ;SUBI RPW, M, J/SUB ;SUBM RPW, B, J/SUB ;SUBB .UCODE =0****00**** SUB: AR_AC0,BR/AR = AR_AR-BR,AD FLAGS,EXIT .TOC "MUL, IMUL" .DCODE 220: R, AC, J/IMUL ;IMUL I, AC, J/IMUL ;IMULI [416] RW, M, J/IMULM ;IMULM [424] RW, B, J/IMULM ;IMULB [424] .UCODE ; ; In days of old when knights were bold and PDP-10s were alive and ; kicking, someone decided to try to optimize IMULtIplication of a ; positive number by a positive number. That attempt failed because ; the code necessary to test for overflow in the general case becomes ; complex enough to cost much of the time saved by shortening the ; multiplication loop. We are now improving on the original idea a ; bit by optimizing all IMUL(I)s of a positive by a positive, as long ; as we can quickly guarantee that no overflow will occur. This ; requires that the high order 19 bits of the multiplier and the ; high order 18 bits of the multiplicand be zero. [416][424] ; =0****00**00 IMUL: MQ_AR,ARX_AC0,SC_#,#/18., ;MQ_multiplier, ARX_multiplicand FE/SCAD,SKP AR18 ; Too much multiplier? =10 ARL_ARL,ARR_SHIFT,SC_FE-1,J/IMFAST;Maybe not. Test further, set SC IMULM: MQ_AR,AR_AC0,CLR ARX,FE_-1,J/IMLONG;[424] Yes, or IMULM/B. No opt = ; IMFAST: AR_ARX,ARL/SH,ARX_0.M,FE_-1, ;Finish setup (FE to force MUL DISP SKP AR NZ ; to work.) Can we optimize? =010 BR_AR LONG,AR_0S,FE_#,#/-8, ;Yes. Set for truncated loop and MUL DISP,CALL [MULP] ; start (note that SC has 17.) IMLONG: BR_AR LONG,AR_0S,FE_#,#/-17., ;No. Do the full loop, starting MUL DISP,CALL [MULP] ; here AR_SHIFT,I FETCH,J/STORAC ;[424] Short IMUL(I). Load AC SC_#,#/35.,SKP AR SIG ;Long IMULx. Did we overflow? =0 IMDONE: AR_SHIFT,B WRITE,J/ST6 ;[424] No. Just store product SET AROV,AR_SIGN,J/IMDONE ;Yes. Set overflow, force good sign .DCODE 224: R, DBL AC, J/MUL ;MUL I, DBL AC, J/MUL ;MULI RW, M, J/MUL ;MULM RW, DBL B, J/MUL ;MULB .UCODE =0****00*000 MUL: MQ_AR,CLR ARX,AR_AC0, ;MULTIPLIER TO MQ. Set multiplicand FE_#,#/-18.,CALL,J/MULSUB ; and step count. Call subroutine =100 SC_#,#/35.,GEN AR*BR,AD/AND, ;[421] M'IER NEG, CHECK M'CAND SKP AD0 ; and product =110 SC_#,#/35.,EXIT ;STORE DOUBLE RESULT SET AROV,EXIT ;[421] MUST HAVE SQUARED 400000,,0 = .TOC "MULTIPLY SUBROUTINE" ; ENTER WITH MULTIPLIER IN MQ, ; MULTIPLICAND IN AR!ARX, MINUS STEP COUNT IN FE ; RETURNS PRODUCT IN AR!ARX!MQ. ; RETURN 4, 6 TELLS SIGN OF MULTIPLIER ; 4 AND 6 ARE USED SO CALLER CAN IGNORE ; DIFFERENCE BY ALIGNMENT OF CALL LOC'N ;[TIME=4+2(-FE)+(# OF ARITH STEPS)] ... IF FE=-18, 40-58. ; ;Recall: ; MUL "FE_FE+1,DISP/MUL,MQ/MQ*.25" ; MULSUB: BR_AR LONG,AR_0S,ARX_0S, ;M'CAND TO BR LONG, CLEAR PROD MUL,J/MULP ;START THE MULTIPLICATION =000 ;GRAB AN 8-WORD BLOCK MULP: (AR+ARX+MQ)*2,FE_SC,RETURN6 ;XADDR MACHINE HAS (AR+ARX+MQ)*2,FE_SC,RETURN6 ; NO "CRA MUL DONE" (AR+ARX+MQ)*2,FE_SC,RETURN6 (AR+ARX+MQ)*2,FE_SC,RETURN6 ;DISCARD REDUNDANT SIGN BIT AR_AR*.25 LONG,MUL,J/MULP ;M'IER BITS 00 AFTER POS STEP AR_(AR+BR)*.25,ARX/ADX*.25, ;01 AFTER + MUL,J/MULP AR_(AR-2BR)*.25,ARX/ADX*.25, ;10 AFTER + MUL,J/MULM AR_(AR-BR)*.25,ARX/ADX*.25, MUL,J/MULM ;11 AFTER + =000 ;ANOTHER 8-WORD BLOCK FOR MULM: (AR+ARX+MQ)*2,FE_SC,RETURN4 ; AFTER SUBTRACTION STEPS (AR+ARX+MQ)*2,FE_SC,RETURN4 (AR+ARX+MQ)*2,FE_SC,RETURN4 (AR+ARX+MQ)*2,FE_SC,RETURN4 ;M'IER WAS NEGATIVE AR_(AR+BR)*.25,ARX/ADX*.25, ;M'IER BITS 00 AFTER NEG STEP MUL,J/MULP AR_(AR+2BR)*.25,ARX/ADX*.25, ;01 AFTER - MUL,J/MULP AR_(AR-BR)*.25,ARX/ADX*.25, ;10 AFTER - MUL,J/MULM AR_AR*.25 LONG,MUL,J/MULM ;11 AFTER - ;HERE TO CONTINUE A LONG MULTIPLICATION ; WITH PARTIAL PRODUCT IN AR LONG MULREE: AD/0S,MUL,J/MULP ;DIVE IN WITHOUT CLOBBERING AR .TOC "DIV, IDIV" .DCODE 230: R, DBL AC, J/IDIV ;IDIV I, DBL AC, J/IDIV ;IDIVI RW, M, J/IDIV ;IDIVM RW, DBL B, J/IDIV ;IDIVB 234: R, DBL AC, J/DIV ;DIV I, DBL AC, J/DIV ;DIVI RW, M, J/DIV ;DIVM RW, DBL B, J/DIV ;DIVB .UCODE =0****00*000 DIV: BR/AR,AR_AC1*2,ARL/AD*2, ;DIVISOR TO BR, LOW DIVIDEND TO AR ARX+MQ_0.M,CALL.M,J/DIV1 ;[422] GET HIGH DIVIDEND =010 ;[422] IDIV: BR/AR,ARR+MQ_0.S,ARX_AC0,ARL/AD,;[421] BR_divisor; fetch dividend SC_1,SKP AD0,CALL [IDIVGO] ; Isolate top half. Is it < 0? =110 ARX_AR,AR_-BRX,SC_#,#/36.,EXIT ;Remainder to ARX, negate quotient ARX_AR,AR_BRX,SC_#,#/36.,EXIT ;HERE FOR POS QUOTIENT = ;HERE ON DIVIDE TO SET UP DIVIDEND DIV1: BRX/ARX,ARX_AR,AR_AC0, ;CLR BRX, DIVIDEND IN AR LONG FE_#,#/33.,TIME/3T, ;SETUP ITERATION COUNT SIGNS DISP,J/DIVS1 ;ENTER SUBR ; ; Start of divide subroutine for IDIVx. We will optimize the ; division by taking only 19 (instead of 36) divide steps if the ; top half of the absolute value of the dividend is zero. Enter ; skipping if the dividend is positive. This routine will set ; up for the division and go to DIVS1 (or DIVS2) to begin the ; actual division. The latter take care of the subroutine return. ; =0 IDIVGO: AR_ARX,ARX/MQ,FE_#,#/33., ;Recover positive dividend, set SKP AR NZ,J/IDVOPT ; long step count. Can we optimize? ARX_-AC0,ARR_0.M,ARL/AD, ;Negative dividend. Start building FE_#,#/12,SKP AD0 ; step count. Is it max neg? =0 AR_ARX,ARX/MQ,FE_#,#/33., ;No. Set long step count and SKP AR NZ,J/IDVOPT ; test for optimization MQ_1,TIME/2T,ARX/MQ,FE_FE+#,#/27,;Yes. Set up for positive version AR CTL/0,EXP TST/0,J/IDVLNG ;and long count (avoid conflict) ; =0 IDVOPT: BRX/ARX,AR_0S,ARX_AR SWAP,FE_#, ;Can optimize. Left adjust dividend #/16.,SIGNS DISP,J/DIVS1 ; Set short divide count and go IDVLNG: BRX/ARX,AR_MQ,ARL/AD,ARX_SHIFT, ;Dividend too big. Kill sign bit, MQ_0.M,SIGNS DISP,J/DIVS1 ; clear MQ, set AR (FE already 33.) .TOC "INTEGER DIVIDE SUBROUTINE" ; ENTER WITH SIGNS DISPATCH OF DIVISOR AND DIVIDEND, ; DIVISOR IN BR, BRX CLR; DIVIDEND IN AR!ARX ; STEP COUNT IN FE (# OF QUOTIENT BITS -2) ; If no divide, check for the maximum negative number as a ; quotient, and force it if it is there; otherwise, just set ; no divide. Exit the instruction in either case. [420] ; OTHERWISE, RETURN WITH SIGNED REMAINDER IN AR, ; POSITIVE QUOTIENT IN BRX AND MQ. ; RETURN 6 IF QUOTIENT SHOULD BE NEGATIVE, ; RETURN 7 IF QUOTIENT SHOULD BE POSITIVE. ;[TIME=14+3(FE)+3(D'END NEG)+3(RESTORE REQ'D)+1(REMAINDER NEG)] ; ... IF FE=33, 113-120 ; ;Recall: ; DIVIDE "FE_FE-1,DISP/DIV,MQ/MQ*2" ; =1100 DIVS1: DIVIDE,AR_2(AR-BR), ARX/ADX*2,J/DIVS3 ;BOTH D'END AND D'SOR POS AR_-AR LONG,J/DIVS1 ;MAKE POS DIVIDEND, THEN CHK DIVS2: DIVIDE,AR_2(AR+BR), ARX/ADX*2,J/DIVS4 ;D'END POS, D'SOR NEG AR_-AR LONG,J/DIVS2 =0010 DIVS3: DIVIDE,AR_2(AR+BR),ARX/ADX*2, ARL/AD*2,CALL.M,J/DIVLP ;START DIVIDING AR_AR*.25 LONG,FE_#,#/40, ;[420] Possible overflow, but CALL [MAXDIV] ; might be -2**-35 quotient AR_-BR,BRX/ARX,RETURN6 ;D'END NEG, SO NEGATE QUO & REM BRX/ARX,RETURN7 ;EVERYTHING POSITIVE =1111 MQ_ARX-BR ;Possible legal quotient. Check rem =1110 SC_#,#/36.,GEN MQ*AC0,AD/ORC, ;and make sure dividend was negative SKP AD0,CALL [MAXCHK] ;with remainder < divisor ARX_-BRX,EXIT ;Looks OK. Negate remainder ; =0010 DIVS4: DIVIDE,AR_2(AR-BR),ARX/ADX*2, ARL/AD*2,CALL.M,J/DIVLP ;BEGIN DIVISION FOR REAL BITS AR_AR*.25 LONG,FE_#,#/40, ;[420] Usually overflow, but might CALL [MAXDIV] ; be in range BRX/ARX,RETURN6 ;NEGATE QUO AR_-BR,BRX/ARX,RETURN7 ;NEGATE REM =1111 MQ_ARX+BR ;Look at bottom of dividend (must =1110 SC_#,#/36.,GEN MQ*AC0,AD/ORCA, ; < |divisor|); also original dividend SKP AD0,CALL [MAXCHK] ; must > 0 EXIT ;All conditions met. Force quotient ; ; MAXDIV, MAXCHK--Helper subroutines to check for a quotient of ; -2**35. This copies the low dividend half to BRX, sets up the ; quotient (in case we have to generate it) and tests the high ; dividend half to see if the first DIVIDE step generated a zero ; result. If it did not, we have a true overflow and we exit ; sideways; otherwise we return 17. The latter portion is reused ; for a subsequent test of the remainder. ; MAXDIV: BRX/ARX,P_FE,GEN AR*2 LONG, ;Set up quotient, save remainder SKP AD NZ ; Did first step generate a zero? =0 MAXCHK: RETURN17 ;Yes. Look more closely SET NO DIVIDE,I FETCH,J/NOP ;No. Must be a real overflow .TOC "BASIC DIVIDE LOOP" ; THE LOOP ITSELF IS AN INNER SUBROUTINE, TO MAKE IT SUITABLE ; FOR USE IN DOUBLE-LENGTH DIVISION. ; THE DOUBLE LENGTH REMAINDER IS RETURNED IN BR!BRX (RESTORED) ; THE SINGLE LENGTH QUOTIENT (LOW PART IF DBL-LEN DIVISION) IN ARX ; RETURN 6 IF QUOTIENT (REALLY AC0.XOR.BR) NEGATIVE, OR 7 IF POSITIVE ;[TIME=12+3(FE)+3(RESTORE REQ'D)] ... IF FE=33, 111-114. =000 DIVLP: DIVIDE,AR_2(AR+BR),ARX/ADX*2,J/DIVLP DIVIDE,AR_2(AR-BR),ARX/ADX*2,J/DIVLP DIV-: DIVIDE,AR_2(AR-BR),ARX/ADX*2,J/DIVLP DIV+: DIVIDE,AR_2(AR+BR),ARX/ADX*2,J/DIVLP DIVIDE,AR_AR+BR,ARX/ADX,J/DIVX DIVIDE,AR_AR-BR,ARX/ADX,J/DIVX DIVIDE,AR_AR-BR,ARX/ADX,J/DIVX ;NO SHIFT ON FINAL STEP DIVIDE,AR_AR+BR,ARX/ADX,J/DIVX ;HERE AFTER FINAL DIVIDE STEP ; MQ HAS POSITIVE FORM QUOTIENT ; AR!ARX HAS REMAINDER, EXCEPT THAT IT MUST BE RESTORED IF IT IS ; NEGATIVE (IT'S NEGATIVE IF THERE WAS NO CARRY ON FINAL STEP) ; THE ORIGINAL DIVIDEND IS STILL IN AC0, SO WE CHECK ITS SIGN ; TO DETERMINE WHETHER TO NEGATE THE (RESTORED) REMAINDER. =100 DIVX: AR_AR+BR LONG ;RESTORE REMAIN WITH POS D'SOR BR_AR LONG,ARX/MQ,FE_SC, ;LONG REMAIN TO BR, QUO TO ARX SKP AC0+,RETURN6 ;RETURN TESTING D'END SIGN AR_AR-BR LONG ;RESTORE REMAIN WITH NEG D'SOR BR_AR LONG,ARX/MQ,FE_SC, SKP AC0-,RETURN6 ;SUBROUTINE FOR FIRST PART OF LONG DIVISIONS ; ENTER AT DDVSUB WITH SKP BR0 ; RETURN3 IF SHOULD RESUME WITH ADD STEP ; RETURN5 IF SHOULD RESUME WITH SUBTRACT =000 DDVLP: AR_2(AR+BR),ARX/ADX*2,DIVIDE,J/DDVLP AR_2(AR-BR),ARX/ADX*2,DIVIDE,J/DDVLP DDVSUB: AR_2(AR-BR),ARX/ADX*2,DIVIDE,J/DDVLP AR_2(AR+BR),ARX/ADX*2,DIVIDE,J/DDVLP AR_MQ,MQ_AR,FE_#,#/32.,RETURN3 AR_MQ,MQ_AR,FE_#,#/32.,RETURN5 AR_MQ,MQ_AR,FE_#,#/32.,RETURN5 AR_MQ,MQ_AR,FE_#,#/32.,RETURN3 .TOC "DOUBLE INTEGER ARITHMETIC -- DADD, DSUB, DMUL, DDIV" .DCODE 114: R, B/0, J/DASMD ;DADD R, B/2, J/DASMD ;DSUB R, B/4, J/DASMD ;DMUL R, J/DDIV ;DDIV .UCODE ;HERE FOR DOUBLE WORD ADD, SUBTRACT, MULTIPLY, OR DIVIDE ;ENTER WITH (E) IN AR, E IN VMA =0****00**00 DDIV: ARX_AC3,CLR MQ,J/DDIV0 ;GET LOWEST PART OF D'END DASMD: BR/AR,AR_AC1*2,ARL/AD*2, ;HIGH MEM WORD TO BR VMA_VMA+1,LOAD ARX, ;ASK FOR LOW WORD MQ_0.S,CALL.S,J/XFERW ;AND WAIT FOR IT =11 ARX_ARX*2 ;SHIFT LOW MEM WORD LEFT = BRX/ARX,ARX_AR,AR_AC0, ;ALL DATA IN PLACE SC_#,#/35.,B DISP ;DO THE OPERATION ;HERE WITH (E) IN BR, (E+1)*2 IN BRX ; (AC) IN AR, (AC+1)*2 IN ARX =00* AR_AR+BR LONG,AD FLAGS,J/ST2AC ;[430] DADD AR_AR-BR LONG,AD FLAGS,J/ST2AC ;[430] DSUB MQ_SHIFT,AR_0S,ARX_0S, ;DMUL, USE AC1 AS INITIAL M'IER FE_#,#/-18.,J/DMULT ;SETUP STEP COUNT = ;HERE FOR DOUBLE WORD MULTIPLY =00* DMULT: AD/0S,MUL,CALL.M,J/MULP ;BEGIN MULTIPLY =10* AR_AR+BR LONG ;CANCEL EFFECTS OF LOW BIT 0 MQ_AR,AR_MQ ;EXCH HI AND LOW PRODUCT WORDS ;HERE AFTER 1ST CALL ON MPY SUBR. SAVE LOW WORD OF PROD, GET HIGH M'IER AC3_AR ;LOW WORD OF PRODUCT AR_AC0 ;GET HIGH M'IER WORD =000 MQ_AR,AR_MQ,CALL, ;DIVE IN AGAIN FE_#,#/-18.,J/MULREE ;CONTINUE THE MULTIPLY =100 GEN AR*BR,AD/AND,SKP AD0 ;SKP IF M'IER, M'CAND, & PROD NEG =110 DMUL1: AC0_AR,AR_SIGN, SC_#,#/35.,J/DMUL2 ;STORE HIGH WORD OF PRODUCT SET AROV,J/DMUL1 ;MULTIPLY NOW COMPLETE, STORE RESULTS WITH PROPER SIGN IN BIT 0 DMUL2: BR/AR,AR_SHIFT ;GET 2ND WITH SIGN, SAVE SIGN AC1_AR,AR_ARX,ARX/MQ ;READY TO BUILD 3RD WORD ARX_SHIFT,AR_BR,MQ_MQ*2 ;SIGNIFICANT BITS TO ARX, SIGN TO AR AR_SHIFT,ARX_AC3, ;3RD WORD IN AR, GET LOW MQ_MQ*.25 ;EXTRA PROD BIT TO MQ 35 AC2_AR,AR_MQ ;,I FETCH WHEN TIMING FIXED =0* ARX_SHIFT,AR_BR,I FETCH, ;LOW WORD AND SIGN READY CALL,J/SHIFT ; GET LOW WORD TO AR STRAC3: AC3_AR,FINISH ;GANZ GETAN ;HERE FOR DOUBLE INTEGER DIVISION ;AR HAS (E), ARX HAS (AC3), AND MQ IS CLEAR DDIV0: T0_AR,AR_ARX,ARX_ARX*8,SC_1 ;SAVE (E) IN T0 BRX/ARX,ARX_SHIFT, ;AC3 3-35 TO BRX, 1-2 TO ARX AR_AC2,SC_#,#/2 ;GET AC2 READY AR_SHIFT,BR/AR, ;AC2 BITS 2-35 WITH AC3 1-2 ARX_AC1,VMA_VMA+1 ;READY TO GET (E+1) BR/AR,AR_ARX,ARX_BR*2, ;LOW DOUBLE WORD NOW IN BR LONG SC_1,FE_1 ARX_SHIFT,AR_AC0,SKP AD0 ;HIGH DOUBLEWORD IN AR LONG =0 DDIV1: BR_AR LONG,AR_BRX,ARX_BR, ;HI POS D'END TO BR LOAD AR,J/DDIV2 ;GET LOW D'SOR READY BR_AR LONG,AR_-BR LONG, ;NEGATE LOW D'END FE_-1,SKP CRY0 ;TEST FOR CARRY PROPAGATION =0 BR_AR LONG,AR_BR COMP LONG,J/DDIV1 BR_AR LONG,AR_-BR LONG,J/DDIV1 ;FINISH NEGATION OF D'END =0* DDIV2: T1_AR,MQ_ARX,ARX_0S, ;LOWEST D'END TO T1, NEXT TO MQ CALL,J/XFERW ; WAIT FOR (E+1) ARX_SHIFT,AR_T0,SKP FE0 ;DIVISOR NOW IN AR LONG =0 AR_BR LONG,BR_AR LONG, ;PUT OPERANDS IN PLACE FOR DIV SIGNS DISP,J/DDIV3 ;TEST D'SOR SIGN AR_BR LONG,BR_AR LONG,SET SR2, ;NOTE D'END NEGATIVE SIGNS DISP,J/DDIV3 ;HERE WITH THE DIVISOR IN BR LONG, ; THE HIGH PART OF THE MAGNITUDE OF THE DIVIDEND IN AR LONG, ; AND THE LOW PART OF THE MAGNITUDE OF THE DIVIDEND IN MQ AND T1 ; SKIP IF DIVISOR NEGATIVE, & CHECK FOR NO-DIVIDE. =1011 DDIV3: AR_2(AR-BR),ARX/ADX*2,MQ_MQ*2, ;SEE IF FIRST DIVIDE STEP SKP CRY0,J/DDIV4 ; GENERATES A 1 AR_2(AR+BR),ARX/ADX*2,MQ_MQ*2,SKP CRY0 =000 DDIV4: FE_#,#/33.,SKP BR0,CALL,J/DDVLP ;GO DO FIRST HALF OF DIVIDE SET NO DIVIDE,I FETCH,J/NOP ;[422] TOO MANY QUOTIENT BITS =011 AC1_AR,CLR SC,J/DDIV6 ;SAVE HI QUOTIENT IN AC1 =101 AC1_AR,SC_1S ;SET FLAG FOR RESUMPTION = DDIV6: AR_T1 ;GET LOWEST DIVIDEND BITS =100 MQ_AR,AR_MQ,CALL, ;FINISH DIVISION, GENERATING SKP SC0,J/DIVLP ; 35 MORE QUOTIENT BITS =110 AR_AC1,SR DISP,SET SR3,J/DDVX1 ;QUOTIENT NEGATIVE. NOTE AR_AC1,SR DISP ;HERE'S HIGH PART OF QUOTIENT =1101 DDVX1: BR_AR LONG,AR_BR LONG,J/DDVX2 ;POS REMAINDER. GO STORE BR_AR LONG,AR_-BR LONG,J/DDVX2 ;NEGATE REMAINDER DDVX2: AC2_AR,AR_SIGN,SC_#,#/35. AR_SHIFT,SR DISP ;GET LOW WORD OF REM. TEST QUO SIGN =1110 AC3_AR,AR_BR,ARX/ADX*2,J/ST2AC ;[430] GET QUO, SQUEEZE OUT HOLE AC3_AR,AR_-BR,ARX/ADX*2,AD LONG,;GET NEGATIVE QUOTIENT J/ST2AC ;[430]