Web pdp-10.trailing-edge.com

Trailing-Edge - PDP-10 Archives - klu2_442 - arith.mic
There are 5 other files named arith.mic in the archive. Click here to see a list.
```.TOC	"ADD, SUB"

.DCODE
.UCODE

=0****00****
=

.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
```
```.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
=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
(AR+ARX+MQ)*2,FE_SC,RETURN6	; NO "CRA MUL DONE"
(AR+ARX+MQ)*2,FE_SC,RETURN6

AR_AR*.25 LONG,MUL,J/MULP	;M'IER BITS 00 AFTER POS STEP
MUL,J/MULP
MUL,J/MULM
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
MUL,J/MULP
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]
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?
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),
AR_-AR LONG,J/DIVS2

=0010
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
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
DIVIDE,AR_AR-BR,ARX/ADX,J/DIVX		;NO SHIFT ON FINAL STEP

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

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*
=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
=110
DMUL1:	AC0_AR,AR_SIGN,
SC_#,#/35.,J/DMUL2	;STORE HIGH WORD OF PRODUCT
SET AROV,J/DMUL1

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_SHIFT,BR/AR,			;AC2 BITS 2-35 WITH AC3 1-2
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
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
=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