.TOC "MULDIV.MIC -- Multiply and Divide Instructions" .TOC "Revision 3.4" ; George Mills, Bob Supnik, Mike Uhler .nobin ;**************************************************************************** ;* * ;* COPYRIGHT (c) 1985, 1986, 1987, 1988, 1989 BY * ;* DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS. * ;* ALL RIGHTS RESERVED. * ;* * ;* THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED * ;* ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH THE * ;* INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER * ;* COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY * ;* OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY * ;* TRANSFERRED. * ;* * ;* THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE * ;* AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT * ;* CORPORATION. * ;* * ;* DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS * ;* SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL. * ;* * ;**************************************************************************** .TOC " Revision History" ; Edit Date Who Description ; ---- --------- --- --------------------- ; 4 09-May-89 REC Deoptimize MULx2 and MULx3 and add a nop to the first ; cycle of both execution flows to guarantee that we ; get the correct value of DL for case branching. ; 3 28-Dec-87 GMU Added restriction about .vx specifier operands. ; 2 25-Sep-87 GMU Removed FPU& macro from the MULX2.R.. entry point to ; reflect the fact that MULL2 has been deoptimized. ; Only MULB2 and MULW2 enter thru MULX2.R..; MULL2 ; always enters thru MULX2.. after passing thru the ; specifier flows where the second operand is transferrred ; to the F-chip. ; (3)1 24-Aug-87 RMS Saved word; pass 1 code freeze. ; ; 4 01-Jul-87 GMU Removed DIVLx as an F-chip instruction. ; 3 18-Nov-86 GMU Make sure MAP.IIII is done for hot MULLx flow. ; 2 14-Nov-86 GMU Optimize MUL{B,W,L}{2,3}. ; (2)1 12-Sep-86 RMS Initial production microcode. .bin ;= BEGIN MULDIV .nobin ; This module implements integer multiply and divide. ; The instructions implemented here are: ; ; Opcode Instruction N Z V C Exceptions ; ------ ----------- ------- ---------- ; ; 86 DIVB2 divr.rb, quo.mb * * * 0 iov, idvz ; C6 DIVL2 divr.rl, quo.ml * * * 0 iov, idvz ; A6 DIVW2 divr.rw, quo.mw * * * 0 iov, idvz ; ; 87 DIVB3 divr.rb, divd.rb, quo.wb * * * 0 iov, idvz ; C7 DIVL3 divr.rl, divd.rl, quo.wl * * * 0 iov, idvz ; A7 DIVW3 divr.rw, divd.rw, quo.ww * * * 0 iov, idvz ; ; 7B EDIV divr.rl, divd.rq, quo.wl, rem.wl * * * 0 iov, idvz ; ; 7A EMUL mulr.rl, muld.rl, add.rl, prod.wq * * 0 0 ; ; 84 MULB2 mulr.rb, prod.mb * * * 0 iov ; C4 MULL2 mulr.rl, prod.ml * * * 0 iov ; A4 MULW2 mulr.rw, prod.mw * * * 0 iov ; ; 85 MULB3 mulr.rb, muld.rb, prod.wb * * * 0 iov ; C5 MULL3 mulr.rl, muld.rl, prod.wl * * * 0 iov ; A5 MULW3 mulr.rw, muld.rw, prod.ww * * * 0 iov ; .TOC " MULx2, MULx3, EMUL" ; These instructions multiply two integers. ; ; Mnemonic Opcode Operation Spec AT/DL CC Dispatch BCOND ; -------- ------ --------- ---- ----- -- -------- ----- ; MULB2 84 prod.mb <-- mulr.rb * prod.mb 2o rm/bb iiii MULX2.. -- ; MULW2 A4 prod.mw <-- mulr.rw * prod.mw 2o rm/ww iiii MULX2.. -- ; MULL2 C4 prod.ml <-- mulr.rl * prod.ml 2 rm/ll iiii MULX2.. -- ; ; MULB3 85 prod.wb <-- mulr.rb * muld.rb 3o rrw/bbb iiii MULX3.. -- ; MULW3 A5 prod.ww <-- mulr.rw * muld.rw 3o rrw/www iiii MULX3.. -- ; MULL3 C5 prod.wl <-- mulr.rl * muld.rl 3o rrw/lll iiii MULX3.. -- ; ; EMUL 7A prod.wq <-- mulr.rl * muld.rl + 4 rrrw/lllq iiii MULX3.. -- ; sext(add.rl) ; ; MULx2 -- entry conditions from specifier flows: ; MD.T0(S1) = first (multiplicand) operand ; MD.T2(S2) = second (multiplier/product) operand, if memory ; VA = address of second (multiplier/product) operand, if memory ; RN = register number of second specifier ; DL = data type of second operand ; ; MULx3 -- entry conditions from specifier flows: ; MD.T0(S1) = first (multiplicand) operand ; MD.T2(S2) = second (multiplier) operand ; VA = address of third (product) operand, if memory ; RN = register number of third specifier ; DL = data type of third operand ; ; EMUL -- entry conditions from specifier flows: ; MD.T0(S1) = first (multiplicand) operand ; MD.T2(S2) = second (multiplier) operand ; MD.T4(S3) = third (add) operand ; VA = address of fourth (product) operand, if memory ; RN = register number of fourth specifier ; DL = data type of fourth operand (quadword) ; ; Exit conditions: ; The PSL condition codes are set. ; The result has been written to the destination memory location or register. ; ; Condition codes: ; N <-- product LSS 0 ; Z <-- product EQL 0 ; V <-- overflow ; C <-- 0 ; ; Size/performance tradeoffs: ; None. ; .bin ; MULx2 operation: ; ; dst.mx <-- src.rx * dst.mx ; Note: This entry point is used for all MULx2 instructions. ; Due to a hardware restriction, the the microword at the ; entry point of this flow can not case on the value of DL ; in the first cycle of the flow. Therefore, the microword ; at the entry point simply delays a cycle before continuing ; with the flow. MULX2..: ;********** Hardware dispatch **********; NOP, ; wait one cycle GOTO [MULX2.CONT] ; MULX2.CONT: ;---------------------------------------; [Q] <-- [MD.T2], LEN(DL), ; Q <-- multiplier, test sign MAP.IIII, ; >> Q write, next cycle not mul/div CASE [FPU.DL] AT [MULBX.WARM], ; case on FPU present, data length sim wbus.nzvc <-- k[0] ; MULx3 operation: ; ; dst.wx <-- src1.rx * src2.rx ; EMUL operation: ; ; prod.wq <-- mulr.rl * muld.rl + sext(add.rl) ; Note: This entry point is used for all MULx3 instructions. ; Due to a hardware restriction, the the microword at the ; entry point of this flow can not case on the value of DL ; in the first cycle of the flow. Therefore, the microword ; at the entry point simply delays a cycle before continuing ; with the flow. MULX3..: ;********** Hardware dispatch **********; NOP, ; wait one cycle GOTO [MULX3.CONT] ; MULX3.CONT: ;---------------------------------------; [Q] <-- [MD.T2], LEN(DL), ; Q <-- multiplier, test sign MAP.IIII, ; >> Q write, next cycle not mul/div CASE [FPU.DL] AT [MULBX.WARM], ; case on FPU present, data length sim wbus.nzvc <-- k[0] ; MULBx, continued. ; At this point, ; MD.T0<7:0> = multiplicand ; Q<7:0> = multiplier ; WBUS CC = set from multiplier ;= ALIGNLIST 000* (MULBX.WARM, MULWX.WARM, ;= MULLX.WARM, EMUL.WARM, ;= MULBX.HOT, MULWX.HOT, ;= MULLX.HOT, EMUL.HOT) MULBX.WARM: ;---------------------------------------; fpu.dl<2:0> = 000 (no FPU, byte): [MD.T1] <-- 000000[00], ; clear hi-order result HOLD WBUS CC, ; block update of WBUS CCs GOTO [MULBX.COMMON] ; join common flow MULBX.HOT: ;---------------------------------------; fpu.dl<2:0> = 100 (FPU, byte): [MD.T1] <-- 000000[00], ; clear hi-order result HOLD WBUS CC, ; block update of WBUS CCs GOTO [MULBX.COMMON] ; join common flow MULBX.COMMON: ;---------------------------------------; [MD.T0] <-- [MD.T0] LSH [24.], ; left justify multiplicand in MD.T0 HOLD WBUS CC, ; block update of WBUS CCs CALL [INT.MULT.BYTE] ; MD.T1<31:16> <-- MD.T0<31:24> * Q<7:0> ;---------------------------------------; [MD.T0] <-- ZEXT.[MD.T1] RSH [16.] ; MD.T0<7:0> <-- result ;---------------------------------------; [MD.T1] <-- ZEXT.[MD.T1] RSH [24.] ; MD.T1<7:0> <-- extended result ;---------------------------------------; [WBUS] <-- [MD.T0] LSH [24.], ; set shifter.sign to sign of result SET PSL CC, LONG, MAP.IIII, ; set PSL CCs, PSL map is iiii GOTO [MULX.CONTINUE] ; join common post processing ; MULWx, continued. ; At this point, ; MD.T0<15:0> = multiplicand ; Q<15:0> = multiplier ; WBUS CC = set from multiplier MULWX.WARM: ;---------------------------------------; fpu.dl<2:0> = 001 (no FPU, word): [MD.T1] <-- 000000[00], ; clear hi-order result HOLD WBUS CC, ; block update of WBUS CCs GOTO [MULWX.COMMON] ; join common flow MULWX.HOT: ;---------------------------------------; fpu.dl<2:0> = 101 (FPU, word): [MD.T1] <-- 000000[00], ; clear hi-order result HOLD WBUS CC, ; block update of WBUS CCs GOTO [MULWX.COMMON] ; join common flow MULWX.COMMON: ;---------------------------------------; [MD.T0] <-- [MD.T0] LSH [16.], ; left justify multiplicand in MD.T0 HOLD WBUS CC, ; block update of WBUS CCs CALL [INT.MULT.WORD] ; MD.T1<31:0> <-- MD.T0<31:16> * Q<15:0> ;---------------------------------------; [MD.T0] <-- [MD.T1], LEN(DL) ; MD.T0<15:0> <-- result ;---------------------------------------; [MD.T1] <-- ZEXT.[MD.T1] RSH [16.] ; MD.T1<15:0> <-- extended result ;---------------------------------------; [WBUS] <-- [MD.T0] LSH [16.], ; set shifter.sign to sign of result SET PSL CC, LONG, MAP.IIII, ; set PSL CCs, PSL map is iiii GOTO [MULX.CONTINUE] ; join common post processing ; MULLx, continued. ; At this point, ; MD.T0 = multiplicand ; Q = multiplier ; WBUS CC = set from multiplier ; MULLX warm path, hot path in FPOINT.MIC. MULLX.WARM: ;---------------------------------------; fpu.dl<2:0> = 010 (no FPU, long): [MD.T1] <-- 000000[00], ; clear hi-order result HOLD WBUS CC, ; block update of WBUS CCs CALL [INT.MULT.LONG] ; MD.T1'Q <-- MD.T0 * Q ;---------------------------------------; [MD.T0] <-- [Q] LSH [0], ; retrieve result, set shifter.sign ; >> Q read, prev cycle not mul/div SET PSL CC, LONG, MAP.IIII ; set PSL CCs, PSL map is iiii MULX.CONTINUE: ;---------------------------------------; [WBUS] <-- [SHIFTER.SIGN] XOR [MD.T1], ; check that extended result = extended sign PSL(V) <-- NOT WBUS(Z), LEN(DL), ; set overflow based on WBUS.Z ; >> PSL override, no decode this cycle CASE [INT.RMODE] AT [WRITE.MEM] ; case on memory vs register ; EMUL, continued. ; At this point, ; MD.T0 = multiplicand ; MD.T4 = adder ; Q = multiplier ; WBUS CC = set from multiplier EMUL.HOT: ;---------------------------------------; fpu.dl<2:0> = 111 (FPU, quad): [MD.T1] <-- [MD.T4], ; input adder into calculation HOLD WBUS CC, ; block update of WBUS CCs CALL [INT.MULT.LONG] ; MD.T1'Q <-- MD.T0 * Q + MD.T1 ;---------------------------------------; [MD.T0] <-- [Q], ; retrieve low order result ; >> Q read, prev cycle not mul/div MAP.IIII, ; set PSL map to iiii CASE [INT.RMODE] AT [WRITE.MEM.SET.PSL.CC] ; go write MD.T1'MD.T0 and set PSL CCs EMUL.WARM: ;---------------------------------------; fpu.dl<2:0> = 011 (no FPU, quad): [MD.T1] <-- [MD.T4], ; input adder into calculation HOLD WBUS CC, ; block update of WBUS CCs CALL [INT.MULT.LONG] ; MD.T1'Q <-- MD.T0 * Q + MD.T1 ;---------------------------------------; [MD.T0] <-- [Q], ; retrieve low order result ; >> Q read, prev cycle not mul/div MAP.IIII, ; set PSL map to iiii CASE [INT.RMODE] AT [WRITE.MEM.SET.PSL.CC] ; go write MD.T1'MD.T0 and set PSL CCs .nobin .TOC " Integer Multiply Subroutines" ; These subroutines multiply the left-justified byte/word/longword in MD.T0 ; by the byte/word/longword in Q to produce a double length result in MD.T1'Q. ; ; They are used by MULx2, MULx3, EMUL, and INDEX. ; ; Entry conditions: ; MD.T0 = multiplicand ; MD.T1 = 0 or adder ; Q = multiplier ; WBUS CC = set from multiplier ; ; Exit conditions: ; MD.T1'Q = MD.T0 * Q + MD.T1 ; .bin ; Integer multiply subroutine operation: ; ; emul -- MD.T1'Q <-- MD.T0 * Q + MD.T0 ; long -- MD.T1'Q <-- MD.T0 * Q ; word -- MD.T1<31:0> <-- MD.T0<31:16> * Q<15:0> ; byte -- MD.T1<31:16> <-- MD.T0<31:24> * Q<7:0> INT.MULT.LONG: ;---------------------------------------; wbus.z = 0: [MD.T1] <-- [MD.T1] SMUL [MD.T0], ; do signed multiply step ; >> SMUL, Q not written prev cycle HOLD WBUS CC, ; block update of WBUS CCs CALL [INT.MULT.7.STEPS] ; call subroutine to do 7 steps ;---------------------------------------; [MD.T1] <-- [MD.T1] SMUL [MD.T0], ; do signed multiply step HOLD WBUS CC, ; block update of WBUS CCs CALL [INT.MULT.7.STEPS] ; call subroutine to do 7 steps INT.MULT.WORD: ;---------------------------------------; [MD.T1] <-- [MD.T1] SMUL [MD.T0], ; do signed multiply step ; >> SMUL, prev cycle SMUL or ; >> Q not written HOLD WBUS CC, ; block update of WBUS CCs CALL [INT.MULT.7.STEPS] ; call subroutine to do 7 steps INT.MULT.BYTE: ;---------------------------------------; [MD.T1] <-- [MD.T1] SMUL [MD.T0], ; do signed multiply step ; >> SMUL, prev cycle SMUL or ; >> Q not written HOLD WBUS CC ; block update of WBUS CCs ;---------------------------------------; [MD.T1] <-- [MD.T1] SMUL [MD.T0], ; do signed multiply step HOLD WBUS CC, ; block update of WBUS CCs CALL [INT.MULT.3.STEPS] ; call subroutine to do 3 steps ;---------------------------------------; [MD.T1] <-- [MD.T1] SMUL [MD.T0], ; do signed multiply step CALL [INT.MULT.1.STEP] ; call subroutine to do 1 step ;---------------------------------------; [MD.T1] <-- [MD.T1] SMUL [MD.T0], ; do signed multiply step CASE [WBUS.NZV] AT [INT.MULT.POS] ; case on pos vs neg multiplier ;= ALIGNLIST 01** (INT.MULT.POS, INT.MULT.NEG) ; WBUS.NZVC set by MOVE --> V = C = 0 INT.MULT.POS: WAIT.ONE.CYCLE: ;---------------------------------------; wbus.n = 0: RETURN ; return to caller INT.MULT.NEG: ;---------------------------------------; wbus.n = 1: [MD.T1] <-- [MD.T1] - [MD.T0], ; compensate for negative sign RETURN ; return to caller ; These subroutines performs 7, 3, or 1 multiply step(s), ; by nesting. INT.MULT.7.STEPS: ;---------------------------------------; [MD.T1] <-- [MD.T1] SMUL [MD.T0], ; do signed multiply step HOLD WBUS CC, ; block update of WBUS CCs CALL [INT.MULT.3.STEPS] ; call subroutine to do 3 steps ; fall through to do 3 steps INT.MULT.3.STEPS: ;---------------------------------------; [MD.T1] <-- [MD.T1] SMUL [MD.T0], ; do signed multiply step HOLD WBUS CC, ; block update of WBUS CCs CALL [INT.MULT.1.STEP] ; call subroutine to do 1 step ; fall through to do 1 step INT.MULT.1.STEP: ;---------------------------------------; [MD.T1] <-- [MD.T1] SMUL [MD.T0], ; do signed multiply step HOLD WBUS CC, ; block update of WBUS CCs RETURN ; return .nobin .TOC " DIVx2, DIVx3" ; These instructions divide two integers and return the quotient. ; ; Mnemonic Opcode Operation Spec AT/DL CC Dispatch BCOND ; -------- ------ --------- ---- ----- -- -------- ----- ; DIVB2 86 quo.mb <-- quo.mb / divr.rb 2o rm/bb iiii DIVX2.. -- ; DIVW2 A6 quo.mw <-- quo.mw / divr.rw 2o rm/ww iiii DIVX2.. -- ; DIVL2 C6 quo.ml <-- quo.ml / divr.rl 2o rm/ll iiii DIVX2.. -- ; ; DIVB3 87 quo.wb <-- divd.rb / divr.rb 3o rrw/bbb iiii DIVX3.. -- ; DIVW3 A7 quo.ww <-- divd.rw / divr.rw 3o rrw/www iiii DIVX3.. -- ; DIVL3 C7 quo.wl <-- divd.rl / divr.rl 3o rrw/lll iiii DIVX3.. -- ; ; DIVx2 -- entry conditions from specifier flows: ; MD.T0(S1) = first (divisor) operand ; MD.T2(S2) = second (dividend/quotient) operand, if memory ; VA = address of second (dividend/quotient) operand, if memory ; RN = register number of second specifier ; DL = data type of second operand ; ; DIVx3 -- entry conditions from specifier flows: ; MD.T0(S1) = first (divisor) operand ; MD.T2(S2) = second (dividend) operand ; VA = address of third (quotient) operand, if memory ; RN = register number of third specifier ; DL = data type of third operand ; ; Exit conditions: ; The PSL condition codes are set. ; The result has been written to the destination memory location or register. ; ; Condition codes: ; N <-- product LSS 0 ; Z <-- product EQL 0 ; V <-- overflow or divide by zero ; C <-- 0 ; ; Size/performance tradeoffs: ; None. ; .bin ; DIVx2 operation: ; ; quo.mx <-- quo.mx / divr.rx DIVX2.R..: ;********** Hardware dispatch **********; [MD.T2] <-- [G.RN], LEN(DL), ; test sign of dividend, copy to MD SET PSL CC, MAP.IIII, ; set PSL CCs, PSL map is iiii CALL [INTEGER.DIVIDE], ; perform integer divide sim wbus.nzvc <-- k[0] ;---------------------------------------; [G.RN] <-- [MD.T2], LEN(DL), ; write result to register LAST CYCLE ; decode next instruction DIVX2..: ;********** Hardware dispatch **********; [WBUS] <-- [MD.T2], LEN(DL), ; test sign of dividend SET PSL CC, MAP.IIII, ; set PSL CCs, PSL map is iiii CALL [INTEGER.DIVIDE], ; perform integer divide sim wbus.nzvc <-- k[0] ;---------------------------------------; MEM (VA)&, [WBUS] <-- [MD.T2], LEN(DL), ; write result to memory LAST CYCLE ; decode next instruction ; DIVx3 operation: ; ; quo.wx <-- divd.rx / divr.rx DIVX3.R..: ;********** Hardware dispatch **********; [WBUS] <-- [MD.T2], LEN(DL), ; test sign of dividend SET PSL CC, MAP.IIII, ; set PSL CCs, PSL map is iiii CALL [INTEGER.DIVIDE], ; perform integer divide sim wbus.nzvc <-- k[0] ;---------------------------------------; [G.RN] <-- [MD.T2], LEN(DL), ; write result to register LAST CYCLE ; decode next instruction DIVX3..: ;********** Hardware dispatch **********; [WBUS] <-- [MD.T2], LEN(DL), ; test sign of dividend SET PSL CC, MAP.IIII, ; set PSL CCs, PSL map is iiii CALL [INTEGER.DIVIDE], ; perform integer divide sim wbus.nzvc <-- k[0] ;---------------------------------------; MEM (VA)&, [WBUS] <-- [MD.T2], LEN(DL), ; write result to memory LAST CYCLE ; decode next instruction .nobin .TOC " EDIV" ; This instruction divides two integers and returns both the quotient ; and the remainder. ; ; Mnemonic Opcode Operation Spec AT/DL CC Dispatch BCOND ; -------- ------ --------- ---- ----- -- -------- ----- ; EDIV 7B quo.wl <-- divd.rq / divr.rl 4 rrvw/lqll iiii EDIV.. -- ; rem.wl <-- rem(divd.rq,divr.rl) ; ; Entry conditions from specifier flows: ; MD.T0(S1) = first (divisor) operand ; MD.T3'MD.T2(S2+1'S2) = second (dividend) operand ; MD.T4(S3) = address of third (quotient) operand, if memory ; MD.T5(S3+1) = register number + 1 of third (quotient) specifier, if register ; MD.T6(S4) = VA = address of fourth (remainder) operand, if memory ; RN = register number of fourth specifier ; DL = data type of fourth operand (longword) ; STATE<2> = 1 if third specifier is register mode, 0 otherwise ; ; Note: Because the .vl specifier for this instruction follows a specifier whose ; DL is quadword, the SPEC.AV.REG flow correctly stores the register number+1 ; into MD.T5. This operation depends on the fact that the SPEC.AV.REG case is ; on DL<1>, which groups DL=L and DL=Q together. ; ; Exit conditions: ; The PSL condition codes are set. ; The result has been written to the destination memory locations or registers. ; ; Condition codes: ; N <-- product LSS 0 ; Z <-- product EQL 0 ; V <-- overflow or divide by zero ; C <-- 0 ; ; Size/performance tradeoffs: ; None. ; ; Note: EDIV has two destination operands and must assure the accessibility of both before ; writing either. ; Note: EDIV clobbers the PSL condition codes before establishing the write ; accessibility of the destination operand. ; .bin ; EDIV operation: ; ; quo.wl <-- divd.rq / divr.rl ; rem.wl <-- rem(divd.rq, divr.rl) EDIV..: ;********** Hardware dispatch **********; [WBUS] <-- [MD.T0], LONG, ; test sign of divisor MAP.IIII, ; set PSL map to iiii CALL [EXTENDED.DIVIDE], ; perform extended divide sim wbus.nzvc <-- k[0] ; At this point, ; MD.T1'MD.T0 = second result (qw only if EMODD/G, rip) ; MD.T2 = first result ; MD.T4 = address of first result, if memory ; SC = register number + 1 of first result, if register ; VA = address of second result, if memory ; RN = register number of second result, if register ; STATE<2> = 1 if first result is register mode ;---------------------------------------; [MD.T6] <-- [VA], ; save VA of second result CASE [STATE2-0] AT [EDIV.THIRD.MEM] ; case on first result memory vs register ;= ALIGNLIST 011* (EDIV.THIRD.MEM, EDIV.THIRD.RMODE) EDIV.THIRD.MEM: ;---------------------------------------; STATE<2> = 0 --> memory: VA.WCHK&, [VA.BUS] <-- [MD.T4], LONG, ; write check address of first result CASE [INT.RMODE] AT [EDIV.THIRD.MEM.FOURTH.MEM], ; case on second result mem vs reg sim addr [ea] [3] ;= ALIGNLIST 110* (EDIV.THIRD.MEM.FOURTH.MEM, EDIV.THIRD.MEM.FOURTH.RMODE) EDIV.THIRD.MEM.FOURTH.MEM: ;---------------------------------------; rmode = 0: MEM (VA)&, [WBUS] <-- [MD.T2], LONG ; write first result to memory ;---------------------------------------; VA.WCHK&, [VA.BUS] <-- [MD.T6], ; restore VA to address of second result LEN(DL), ; write check address of second result GOTO [WRITE.MEM], ; go write second result to memory sim addr [ea] [4] EDIV.THIRD.MEM.FOURTH.RMODE: ;---------------------------------------; rmode = 1: MEM (VA)&, [WBUS] <-- [MD.T2], LONG, ; write first result to memory GOTO [WRITE.REG] ; go write second result to register EDIV.THIRD.RMODE: ;---------------------------------------; STATE<2> = 1 --> register: CASE [SC5-3] AT [RV.15.0..6] ; case on RV 15,0..6 vs 7..14 ; EDIV, continued. ; First result is register mode, breakout to store result. ; Note that saved register number was incremented by 1! ;= ALIGNLIST **0* (RV.15.0..6, RV.7..14) ; SC<5:4> = 00 --> SC<5:3> = 00? RV.15.0..6: ;---------------------------------------; sc<3> = 0 (RV 15, 0 to 6): CASE [SC2-0] AT [RV.15] ; case on RV 15, 0 to 6 ;= ALIGNLIST 000* (RV.15, RV.0, RV.1, RV.2, ;= RV.3, RV.4, RV.5, RV.6) RV.15: ;---------------------------------------; sc<2:0> = 000: RESERVED ADDRESSING MODE ; attempt to write PC, fault RV.0: ;---------------------------------------; sc<2:0> = 001: [G.0] <-- [MD.T2], LONG, ; write first result to R0 CASE [INT.RMODE] AT [RV.WRITE.MEM] ; go write second result to memory RV.1: ;---------------------------------------; sc<2:0> = 010: [G.1] <-- [MD.T2], LONG, ; write first result to R1 CASE [INT.RMODE] AT [RV.WRITE.MEM] ; go write second result to memory RV.2: ;---------------------------------------; sc<2:0> = 011: [G.2] <-- [MD.T2], LONG, ; write first result to R2 CASE [INT.RMODE] AT [RV.WRITE.MEM] ; go write second result to memory RV.3: ;---------------------------------------; sc<2:0> = 100: [G.3] <-- [MD.T2], LONG, ; write first result to R3 CASE [INT.RMODE] AT [RV.WRITE.MEM] ; go write second result to memory RV.4: ;---------------------------------------; sc<2:0> = 101: [G.4] <-- [MD.T2], LONG, ; write first result to R4 CASE [INT.RMODE] AT [RV.WRITE.MEM] ; go write second result to memory RV.5: ;---------------------------------------; sc<2:0> = 110: [G.5] <-- [MD.T2], LONG, ; write first result to R5 CASE [INT.RMODE] AT [RV.WRITE.MEM] ; go write second result to memory RV.6: ;---------------------------------------; sc<2:0> = 111: [G.6] <-- [MD.T2], LONG, ; write first result to R6 CASE [INT.RMODE] AT [RV.WRITE.MEM] ; go write second result to memory ; EDIV, continued. ; First result is register mode, breakout to store result. RV.7..14: ;---------------------------------------; sc<3> = 1 (RV 7 to 14): CASE [SC2-0] AT [RV.7] ; case on RV 7 to 14 ;= ALIGNLIST 000* (RV.7, RV.8, RV.9, RV.10, ;= RV.11, RV.12, RV.13, RV.14) RV.7: ;---------------------------------------; sc<2:0> = 000: [G.7] <-- [MD.T2], LONG, ; write first result to R7 CASE [INT.RMODE] AT [RV.WRITE.MEM] ; go write second result to memory RV.8: ;---------------------------------------; sc<2:0> = 001: [G.8] <-- [MD.T2], LONG, ; write first result to R8 CASE [INT.RMODE] AT [RV.WRITE.MEM] ; go write second result to memory RV.9: ;---------------------------------------; sc<2:0> = 010: [G.9] <-- [MD.T2], LONG, ; write first result to R9 CASE [INT.RMODE] AT [RV.WRITE.MEM] ; go write second result to memory RV.10: ;---------------------------------------; sc<2:0> = 011: [G.10] <-- [MD.T2], LONG, ; write first result to R10 CASE [INT.RMODE] AT [RV.WRITE.MEM] ; go write second result to memory RV.11: ;---------------------------------------; sc<2:0> = 100: [G.11] <-- [MD.T2], LONG, ; write first result to R11 CASE [INT.RMODE] AT [RV.WRITE.MEM] ; go write second result to memory RV.12: ;---------------------------------------; sc<2:0> = 101: [G.12] <-- [MD.T2], LONG, ; write first result to R12 CASE [INT.RMODE] AT [RV.WRITE.MEM] ; go write second result to memory RV.13: ;---------------------------------------; sc<2:0> = 110: [G.13] <-- [MD.T2], LONG, ; write first result to R13 CASE [INT.RMODE] AT [RV.WRITE.MEM] ; go write second result to memory RV.14: ;---------------------------------------; sc<2:0> = 111: [SP] <-- [MD.T2], LONG, ; write first result to SP CASE [INT.RMODE] AT [RV.WRITE.MEM] ; go write second result to memory ; EDIV, continued. ; Common exit for RV destination. ; This exit point can't be combined with the normal WRITE.MEM exit ; due to constraint problems. ; At this point, ; MD.T1'MD.T0 = second result ; VA = address of second result, if memory mode ; G.RN = register number of second result, if register mode ;= ALIGNLIST 110* (RV.WRITE.MEM, RV.WRITE.REG) RV.WRITE.MEM: ;---------------------------------------; rmode = 0: MEM (VA)&, [WBUS] <-- [MD.T0], LEN(DL), ; write MD.T0 to memory LAST CYCLE IF DL.BWL, ; if bwl, decode next instruction GOTO [WRITE.MEM.QW] ; if q, write MD.T1 to memory RV.WRITE.REG: ;---------------------------------------; rmode = 1: [G.RN] <-- [MD.T0], LEN(DL), ; write MD.T0 to register RN <-- RN + 1, ; increment RN for quad result LAST CYCLE IF DL.BWL, ; if bwl, decode next instruction GOTO [WRITE.REG.QW] ; if q, write MD.T1 to register .nobin .TOC " Integer Divide Subroutine" ; The integer divide subroutine divides the byte/word/longword in MD.T2 by the ; byte/word/longword in MD.T0. ; ; The routine uses a non-restoring divide which executes at the rate of one ; bit for every microcycle. ; ; Entry conditions: ; MD.T0 = divisor ; MD.T2 = dividend ; DL = data type of operands ; WBUS CC = set from dividend via MOVE ; PSL CC = set from dividend via MOVE ; PSL map = iiii ; ; Exit conditions: ; MD.T0 = remainder ; MD.T2 = result (quotient if no trap, dividend if trap) ; MD.T3 = trashed ; MD.T6 = trashed ; Q = trashed ; PSL CC = set from quotient, including PSL.V ; .bin ; Integer divide subroutine operation: ; ; MD.T0 = MD.T2 / MD.T0 ; MD.T2 = remainder (MD.T2, MD.T0) INTEGER.DIVIDE: ;---------------------------------------; [WBUS] <-- [MD.T0], LEN(DL), ; test sign/zero of divisor CASE [FPU.DL] AT [INTDIV.SHIFT.BYTE], ; case on byte/word/longword divide sim wbus.nzvc <-- k[0] ;= ALIGNLIST 100* (INTDIV.SHIFT.BYTE, INTDIV.SHIFT.WORD, ;= INTDIV.SHIFT.LONG, ) INTDIV.SHIFT.BYTE: ;---------------------------------------; fpu.dl<1:0> = 00: [MD.T3] <-- [MD.T2] LSH [24.], ; shift dividend left 24 bits GOTO [INTDIV.COMMON] ; join common flow INTDIV.SHIFT.WORD: ;---------------------------------------; fpu.dl<1:0> = 01: [MD.T3] <-- [MD.T2] LSH [16.], ; shift dividend left 16 bits GOTO [INTDIV.COMMON] ; join common flow INTDIV.SHIFT.LONG: ;---------------------------------------; fpu.dl<1:0> = 10: [MD.T3] <-- [MD.T2], ; transfer dividend to common MD GOTO [INTDIV.COMMON] ; join common flow INTDIV.COMMON: ;---------------------------------------; NOP, ; wait for WBUS CCs CASE [WBUS.NZV] AT [INTDIV.DIVD.POS] ; case on sign of dividend ;= ALIGNLIST 01** (INTDIV.DIVD.POS, INTDIV.DIVD.NEG) ; WBUS.NZVC set by MOVE --> V = C = 0 INTDIV.DIVD.POS: ;---------------------------------------; wbus.n = 0: [Q] <-- [MD.T3], ; copy dividend to Q ; >> Q write, next cycle not mul/div CASE [WBUS.NZV] AT [INTDIV.DIVR.POS] ; case on sign of divisor INTDIV.DIVD.NEG: ;---------------------------------------; wbus.n = 1: [Q] <-- -[MD.T3], ; negate dividend to Q ; >> Q write, next cycle not mul/div STATE0 <-- 1, ; set flag for later CASE [WBUS.NZV] AT [INTDIV.DIVR.POS] ; case on sign of divisor ; Integer divide, continued. ; Finish sign adjustments. ; MD.T0 = divisor ; MD.T2 = dividend ; Q = !dividend!, left justified ; PSL CC = set from MD.T2 ; STATE<0> = sign of dividend ;= ALIGNLIST 00** (INTDIV.DIVR.POS, INTDIV.DIVR.ZERO, ;= INTDIV.DIVR.NEG, ) ; WBUS.NZVC set by MOVE --> V = C = 0 INTDIV.DIVR.ZERO: ;---------------------------------------; wbus.nz = 01: [TRAP] <-- 000000[ARITH.TRAP.INTDIV], ; set divide by zero trap MAP.JIZJ, ; disable int ovrflo trap with jizj map GOTO [DIVIDE.BY.ZERO] ; join common error flows INTDIV.DIVR.NEG: ;---------------------------------------; wbus.nz = 10: [MD.T0] <-- -[MD.T0], LEN(DL), ; divisor is neg, negate it, zero extend STATE1 <-- 1 ; flag it for later INTDIV.DIVR.POS: ;---------------------------------------; wbus.nz = 00: [MD.T3] <-- [MD.T3] - [MD.T3], LONG, ; clear high dividend, set wbus.nzvc = 0101 CASE [FPU.DL] AT [INTDIV.BYTE] ; case on byte/word/longword divide ; Integer divide, continued. ; Start integer divide. ; At this point, ; MD.T0 = !divisor! ; MD.T2 = dividend ; MD.T3'Q = 0'!dividend! ; STATE<1:0> = signs of divisor, dividend ; WBUS CC = 0101 ;= ALIGNLIST 100* (INTDIV.BYTE, INTDIV.WORD, ;= INTDIV.LONG, ) INTDIV.BYTE: ;---------------------------------------; fpu.dl<2:0> = 000: [MD.T3] <-- [MD.T3] UDIV [MD.T0], ; do unsigned divide step ; >> UDIV, Q not written prev cycle GOTO [INTDIV.8.STEPS] ; go finish 8 bit divide INTDIV.WORD: ;---------------------------------------; fpu.dl<2:0> = 001: [MD.T3] <-- [MD.T3] UDIV [MD.T0], ; do unsigned divide step ; >> UDIV, Q not written prev cycle GOTO [INTDIV.16.STEPS] ; go finish 16 bit divide INTDIV.LONG: ;---------------------------------------; fpu.dl<2:0> = 010: [MD.T3] <-- [MD.T3] UDIV [MD.T0], ; do unsigned divide step ; >> UDIV, Q not written prev cycle GOTO [INTDIV.32.STEPS] ; go do warm divide ; Integer divide, continued. ; One divide step (out of n+1) has been executed. ; ; At this point, ; MD.T0 = !divisor! ; MD.T2 = dividend ; MD.T3'Q = !dividend! ; STATE<1:0> = signs of divisor, dividend ; WBUS CC = set from first divide step INTDIV.32.STEPS: ;---------------------------------------; [MD.T3] <-- [MD.T3] UDIV [MD.T0], ; do unsigned divide step CALL [DIVIDE.15.STEPS] ; do next fifteen divide steps INTDIV.16.STEPS: ;---------------------------------------; [MD.T3] <-- [MD.T3] UDIV [MD.T0], ; do unsigned divide step CALL [DIVIDE.7.STEPS] ; do next seven divide steps INTDIV.8.STEPS: ;---------------------------------------; [MD.T3] <-- [MD.T3] UDIV [MD.T0], ; do unsigned divide step CALL [DIVIDE.7.STEPS] ; do next seven divide steps ; Integer divide, continued. ; Cleanup for integer divide. ; At this point, ; Q = quotient ; STATE<1:0> = signs of divisor, dividend ; Q cannot be read this cycle. ;---------------------------------------; NOP, ; wait for Q CASE [STATE2-0] AT [INTDIV.STATE.00] ; case on signs ;= ALIGNLIST 100* (INTDIV.STATE.00, INTDIV.STATE.01, ;= INTDIV.STATE.10, INTDIV.STATE.11) INTDIV.STATE.00: ;---------------------------------------; divisor +, dividend +: [MD.T2] <-- [Q], LEN(DL), ; zero extend quotient ; >> Q read, prev cycle not mul/div SET PSL CC, ; set PSL CCs RETURN ; exit to caller INTDIV.STATE.01: ;---------------------------------------; divisor +, dividend -: [MD.T6] <-- -[Q], LEN(DL), ; negate and zero extend quotient ; >> Q read, prev cycle not mul/div GOTO [DIVIDE.NEG.NO.OVERFLOW] ; go set PSL CCs and return INTDIV.STATE.10: ;---------------------------------------; divisor -, dividend +: [MD.T6] <-- -[Q], LEN(DL), ; negate and zero extend quotient ; >> Q read, prev cycle not mul/div GOTO [DIVIDE.NEG.NO.OVERFLOW] ; go set PSL CCs and return ;INTDIV.STATE.11: ; divisor -, dividend -: ; must check for overflow ; see extended divide flows .nobin .TOC " Extended Divide Subroutine" ; The extended divide subroutine divides the quadword in MD.T3'MD.T2 by the ; longword in MD.T0. ; ; The routine uses a non-restoring divide which executes at the rate of one ; bit for every microcycle. ; ; Entry conditions: ; MD.T0 = divisor ; MD.T2 = dividend (low-order) ; MD.T3 = dividend (hi order) ; WBUS CC = set from divisor via MOVE ; DL = longword ; PSL map = iiii ; ; Exit conditions: ; MD.T0 = remainder ; MD.T2 = result (quotient if no trap, dividend<31:0> if trap) ; MD.T3 = trashed ; MD.T6 = trashed ; SC = set from MD.T5 ; PSL CC = set from quotient, including PSL.V ; .bin ; Extended divide subroutine operation: ; ; MD.T0 = MD.T3'MD.T2 / MD.T0 ; MD.T2 = rem (MD.T3'MD.T2, MD.T0) EXTENDED.DIVIDE: ;---------------------------------------; [WBUS] <-- [MD.T3], LONG, ; test sign of high-order dividend sim wbus.nzvc <-- k[0] ;---------------------------------------; [Q] <-- [MD.T2], ; test zero of low-order dividend, copy to Q ; >> Q write, next cycle not mul/div SET PSL CC, LONG, ; set PSL CC, PSL map is iiii sim wbus.nzvc <-- k[0] ;---------------------------------------; NOP, ; wait for WBUS CCs CASE [WBUS.NZV] AT [EXTDIV.DIVR.POS] ; case on sign/zero of divisor ;= ALIGNLIST 00** (EXTDIV.DIVR.POS, EXTDIV.DIVR.ZERO, ;= EXTDIV.DIVR.NEG, ) ; WBUS.NZVC set by MOVE --> V = C = 0 EXTDIV.DIVR.POS: ;---------------------------------------; wbus.nz = 00: NOP, ; wait for WBUS CCs CASE [WBUS.NZV] AT [EXTDIV.DIVD.POS] ; case on sign of high-order dividend EXTDIV.DIVR.NEG: ;---------------------------------------; wbus.nz = 10: [MD.T0] <-- -[MD.T0], ; negate divisor STATE1 <-- 1, ; set flag for later CASE [WBUS.NZV] AT [EXTDIV.DIVD.POS] ; case on sign of high-order dividend EXTDIV.DIVR.ZERO: ;---------------------------------------; wbus.nz = 01: [TRAP] <-- 000000[ARITH.TRAP.INTDIV], ; set divide by zero trap code MAP.JIZJ, ; disable integer overflow trap GOTO [DIVIDE.BY.ZERO] ; join common error flows ; Extended divide subroutine, continued. ; Finish sign adjustment. ; At this point, ; MD.T0 = !divisor! ; MD.T3'MD.T2 = dividend ; MD.T5 = RV, if third specifier was register mode ; Q = low-order dividend ; PSL CC = set from low-order dividend ; STATE<1> = sign of divisor ;= ALIGNLIST 01** (EXTDIV.DIVD.POS, EXTDIV.DIVD.NEG) ; WBUS.NZVC set by MOVE --> V = C = 0 EXTDIV.DIVD.NEG: ;---------------------------------------; wbus.n = 1: [Q] <-- -[MD.T2], ; start negate of MD.T3'MD.T2 ; >> Q write, next cycle not mul/div STATE0 <-- 1, ; note for later CASE [WBUS.NZV] AT [EXTDIV.DIVD.NEG.NZERO] ; case on low-order dividend zero ;= ALIGNLIST 10** (EXTDIV.DIVD.NEG.NZERO, EXTDIV.DIVD.NEG.ZERO) ; WBUS.NZVC set by MOVE --> V = C = 0 EXTDIV.DIVD.NEG.NZERO: ;---------------------------------------; wbus.z = 0: [MD.T3] <-- NOT [MD.T3], ; finish neg of MD.T3'MD.T2 with complement GOTO [EXTDIV.DIVD.POS] ; join common code EXTDIV.DIVD.NEG.ZERO: ;---------------------------------------; wbus.z = 1: [MD.T3] <-- -[MD.T3], ; finish negate of MD.T3'MD.T2 with negate GOTO [EXTDIV.DIVD.POS] ; join common code ; Extended divide, continued. ; Perform overflow check, start divide. ; At this point, ; MD.T0 = !divisor! ; MD.T2 = low-order dividend ; MD.T3'Q = !dividend! ; MD.T5 = RV, if third specifier was register mode ; PSL CC = set from low-order dividend ; STATE<1:0> = signs of divisor, dividend ; The following compare is intended to check whether divd < divr, that is, ; if divd - divr < 0 (--> wbus.c = 0). However, to start up the divide ; algorithm, wbus.c must be 1. Accordingly, the ONE'S COMPLEMENT of ; divd - divr is calculated, e.g., divr - divd - 1. For allocation reasons, ; the test is wbus.n = 0, as seen in the following table: ; ; divd-divr wbus.nzc divr-divd-1 wbus.nzc ; --------- ------- ----------- ------- ; divd < divr 100 ok positive or 0 0X1 ok ; divd = divr 011 bad -1 100 bad ; divd > divr 001 bad negative 100 bad ; Note that this relationship is only good over the ranges ; [1 <= divr <= 80000000] and [0 <= divd <= 80000000]. EXTDIV.DIVD.POS: ;---------------------------------------; wbus.n = 0: [WBUS] <-- ([MD.T0] - [MD.T3] - 1), ; try reverse subtraction step LONG, ; of divr - divd - 1 sim wbus.nzvc <-- k[0] ;---------------------------------------; [MD.T3] <-- [MD.T3] UDIV [MD.T0], ; do unsigned divide step ; >> UDIV, Q not written prev cycle CALL [DIVIDE.1.STEP] ; do next one divide step ;---------------------------------------; [MD.T3] <-- [MD.T3] UDIV [MD.T0], ; do unsigned divide step CASE [WBUS.NZV] AT [EXTDIV.QUAD] ; if initial sub is >0, can do divide .TOC " Common Divide Code" ; Here if the divide has failed (overflow or divide by zero). ; At this point, ; MD.T2 = low-order dividend (will be result) ; MD.T5 = RV, if third specifier was register mode ; PSL CC = set from low-order dividend ; PSL map = jizj to disable integer overflow trap DIVIDE.BY.ZERO: ;---------------------------------------; SET VAX TRAP REQUEST, ; set trap request CALL [FLUSH.AND.RETURN] ; flush IB to notify I Box of trap ; Overflow. PSL map is set to iiii, enabling overflow trap. ;= ALIGNLIST 011* (EXTDIV.QUAD, DIVIDE.OVERFLOW) DIVIDE.OVERFLOW: ;---------------------------------------; wbus.n = 1: [SC] <-- [MD.T5] OR 000000[80], LONG, ; copy RV to SC<3:0>, WBUS will be non-zero PSL(V) <-- NOT WBUS(Z), ; set PSL.V ; >> PSL override, no decode this cycle sim sc <-- [1] ;---------------------------------------; [MD.T0] <-- 000000[00], ; return zero as remainder RETURN ; return to caller ; Extended divide, continued. ; Continue extended divide, first 3 steps (of 33) have been executed. ; At this point, ; MD.T0 = !divisor! ; MD.T2 = dividend<31:0> ; MD.T3'Q = !dividend! ; MD.T5 = RV, if third specifier was register mode EXTDIV.QUAD: ;---------------------------------------; wbus.n = 0: [MD.T3] <-- [MD.T3] UDIV [MD.T0], ; do unsigned divide step CALL [DIVIDE.1.STEP] ; do next one divide step ;---------------------------------------; [MD.T3] <-- [MD.T3] UDIV [MD.T0], ; do unsigned divide step CALL [DIVIDE.3.STEPS] ; do next three divide steps ;---------------------------------------; [MD.T3] <-- [MD.T3] UDIV [MD.T0], ; do unsigned divide step CALL [DIVIDE.7.STEPS] ; do next seven divide steps ;---------------------------------------; [MD.T3] <-- [MD.T3] UDIV [MD.T0], ; do unsigned divide step CALL [DIVIDE.15.STEPS] ; do next fifteen divide steps ;---------------------------------------; [MD.T3] <-- [K1]!![MD.T3] RSH [1] ; undo last shift of remainder INTDIV.STATE.11: ;---------------------------------------; divisor -, dividend -: [MD.T6] <-- -[Q], LONG, ; negate quotient to test for overflow ; >> Q read, prev cycle not mul/div sim wbus.nzvc <-- k[0] ;---------------------------------------; [SC] <-- [MD.T5], ; load SC with RV for EDIV test CASE [WBUS.NZC] AT [EXTDIV.REM.NEG], ; case on last step positive or negative sim sc <-- [1] ; Extended divide, continued. ; Cleanup for extended divide. ; At this point, ; MD.T0 = !divisor! ; MD.T2 = low order dividend ; MD.T3 = remainder with sign bit forced on ; SC = RV, if third specifier was register mode ; MD.T6 = negated quotient ; STATE<1:0> = signs of divisor, dividend ;= ALIGNLIST 110* (EXTDIV.REM.NEG, EXTDIV.REM.POS) EXTDIV.REM.POS: ;---------------------------------------; wbus.c = 1: [MD.T0] <-- [MD.T3] ANDNOT [80]000000, ; fix up remainder from last divide step CASE [STATE2-0] AT [EXTDIV.STATE.00] ; case on signs of divisor, dividend EXTDIV.REM.NEG: ;---------------------------------------; wbus.c = 0: [MD.T0] <-- [MD.T0] + [MD.T3], ; fix up remainder from last divide step CASE [STATE2-0] AT [EXTDIV.STATE.00] ; case on signs of divisor, dividend ;= ALIGNLIST 100* (EXTDIV.STATE.00, EXTDIV.STATE.01, ;= EXTDIV.STATE.10, EXTDIV.STATE.11) EXTDIV.STATE.00: ;---------------------------------------; divisor +, dividend +: CASE [WBUS.NZV] AT [DIVIDE.NZV.000] ; case on divisor geq 0 EXTDIV.STATE.01: ;---------------------------------------; divisor +, dividend -: [MD.T0] <-- -[MD.T0], ; remainder gets sign of dividend CASE [WBUS.NZV] AT [DIVIDE.NEG.OVERFLOW]; case on quotient leq 0 EXTDIV.STATE.10: ;---------------------------------------; divisor -, dividend +: CASE [WBUS.NZV] AT [DIVIDE.NEG.OVERFLOW]; case on quotient leq 0 EXTDIV.STATE.11: ;---------------------------------------; divisor -, dividend -: [MD.T0] <-- -[MD.T0], ; remainder gets sign of dividend CASE [WBUS.NZV] AT [DIVIDE.NZV.000] ; case on divisor geq 0 ; Overflow test cases. ; Quotient SHOULD BE positive (divisor and dividend had same sign). ; For DIV, +/+ cannot generate overflow. ; For DIV, -/- CAN generate overflow if dividend = largest neg num, divisor = -1. ; For EDIV, either case can generate overflow. ; In any case, a negative quotient represents OVERFLOW. ; At this point, ; MD.T0 = remainder, sign adjusted, if extended divide ; MD.T6 = negated quotient ; Note that the quotient was tested by negating it. To test for quotient geq 0: ; ; quotient was + --> negated quotient is negative with no overflow ; quotient was 0 --> negated quotient is 0 ; quotient was - --> negated quotient is + OR ; negated quotient is - with overflow ;= ALIGNLIST 000* (DIVIDE.NZV.000, , ;= DIVIDE.NZV.010, , ;= DIVIDE.NZV.100, DIVIDE.NZV.101, ;= , ) ; WBUS.NZVC set by NEGATE --> NZV = 001, 011, 110, 111 impossible DIVIDE.NZV.000: ;---------------------------------------; wbus.nzv = 000: GOTO [DIVIDE.OVERFLOW] ; overflow, go process DIVIDE.NZV.010: ;---------------------------------------; wbus.nzv = 010: [MD.T2] <-- [K0], LEN(DL), ; result is zero SET PSL CC, ; set PSL CCs RETURN ; exit to caller DIVIDE.NZV.100: ;---------------------------------------; wbus.nzv = 100: [MD.T2] <-- -[MD.T6], LEN(DL), ; result is non-zero, return positive form ; (can't overflow because V did not get set, ; can't carry out because non-zero) SET PSL CC, ; set PSL CCs RETURN ; exit to caller DIVIDE.NZV.101: ;---------------------------------------; wbus.nzv = 101: GOTO [DIVIDE.OVERFLOW] ; overflow, go process ; Overflow test cases, continued. ; Quotient SHOULD BE negative (divisor and dividend had opposite signs). ; For DIV, -/+ and +/- cannot generate overflow. ; For EDIV, either case can generate overflow. ; In any case, a positive non-zero quotient represents OVERFLOW. ; At this point, ; MD.T0 = remainder, sign adjusted ; MD.T6 = negated quotient ;= ALIGNLIST 001* (DIVIDE.NEG.OVERFLOW, DIVIDE.NEG.ZERO, ;= DIVIDE.NEG.NO.OVERFLOW, ) DIVIDE.NEG.OVERFLOW: ;---------------------------------------; wbus.nz = 00: GOTO [DIVIDE.OVERFLOW] ; overflow, go process DIVIDE.NEG.ZERO: ;---------------------------------------; wbus.nz = 01: [MD.T2] <-- [K0], LEN(DL), ; result is zero SET PSL CC, ; set PSL CCs RETURN ; exit to caller DIVIDE.NEG.NO.OVERFLOW: ;---------------------------------------; wbus.nz = 10: [MD.T2] <-- [MD.T6], LEN(DL), ; return negated quotient SET PSL CC, ; set PSL CCs RETURN ; exit to caller ; Subroutine to perform fifteen divide steps (with entries for 7, 3, and 1). DIVIDE.15.STEPS: ;---------------------------------------; [MD.T3] <-- [MD.T3] UDIV [MD.T0], ; do unsigned divide step CALL [DIVIDE.7.STEPS] ; do next seven divide steps DIVIDE.7.STEPS: ;---------------------------------------; [MD.T3] <-- [MD.T3] UDIV [MD.T0], ; do unsigned divide step CALL [DIVIDE.3.STEPS] ; do next three divide steps DIVIDE.3.STEPS: ;---------------------------------------; [MD.T3] <-- [MD.T3] UDIV [MD.T0], ; do unsigned divide step CALL [DIVIDE.1.STEP] ; do next divide step DIVIDE.1.STEP: ;---------------------------------------; [MD.T3] <-- [MD.T3] UDIV [MD.T0], LONG, ; do unsigned divide step RETURN, ; return to caller sim wbus.nzvc <-- k[4] ;= END MULDIV