;
; ASMPI by Rich Geldreich, December 1992 - June 1993
; (A FAST all-assembly PI calculator.)
; Thanks to Victor Yui for suggesting the division optimization.
; (This program has only been verified to 10,000 digits.)
;

.286
IDEAL           ; Assembled with TASM v3.0 (sorry MASM guys!)
MODEL SMALL
DOSSEG
STACK 512

DATASEG
;-------------------------------------------------------------------------------
;The following equates specify how many digits of PI are to be calculated.
;I used the QuickBASIC program MAKESZES.BAS to calculate these values.
;(The maximum number of digits this program can handle is about 36,000
;because of memory limitations.)

;NumDigits=564
;MPVSize=224
;Last1=801
;Last2=225

;NumDigits=1064
;MPVSize=448
;Last1=1588
;Last2=436

;NumDigits=2564
;MPVSize=1056
;Last1=3949
;Last2=1069

;NumDigits=5064
;MPVSize=2080
;Last1=7883
;Last2=2124

;NumDigits=10064
;MPVSize=4160
;Last1=15752
;Last2=4234

NumDigits=20064
MPVSize=8320
Last1=31489
Last2=8453

;NumDigits=36064
;MPVSize=14976
;Last1=56669
;Last2=15204


;MPV stands for: multiple precision variable.

EVEN
MPV1            db      MPVSize         dup(0)
MPV2            db      MPVSize         dup(0)
MPV3            db      MPVSize         dup(0)
MPV4            db      MPVSize         dup(0)

MPVDivZero      dw      0
MPVDivZeroSize  dw      0
MPVMulZero      dw      0
MPVMulZeroSize  dw      0

Divisor         dw      0
OddEven         db      0

Max             dw      0

TwiddlePos      dw      0
TwiddleStuff    db      'Ä','\','|','/'
TwiddleCount    db      0

Message1        db      'Machin Series PI Calculator',10,13
                db      'by Rich Geldreich, June 1993',10,13,'$'

Message2        db      'Initializing... $'
Message3        db      13,'Calculating group one... $'
Message4        db      '    ',13,'Calculating group two... $'
Message5        db      '    ',13,'Conterting result to decimal... $'
Message6        db      13,'                                     ',13,10,13,'$'

Message7        db      'Assembled for ','$'
Message8        db      ' digits.',10,13,'$'
;-------------------------------------------------------------------------------
FARDATA DigitSeg
Digits          db      NumDigits       dup(0)
ENDS
;-------------------------------------------------------------------------------

CODESEG
ASSUME DS:@DATA, ES:@DATA, CS:@CODE

Start:          Cld

                Mov     ax, @DATA
                Mov     ds, ax
                Mov     es, ax

                Mov     ah, 9
                Mov     dx, offset Message1
                Int     021h

                Call    PrintNumDigits

                Call    MPVLOAD10           ;calculate 10^numdigits in binary
                Call    TERM1               ;calculate term 1
                Call    TERM2               ;calculate term 2
                Call    MPVPRINT            ;convert result to decimal and
                                            ;print to screen
                Mov     ax, 04C00h          ;exit to dos
                Int     021h
;-------------------------------------------------------------------------------
EVEN
PROC            MPVDIV                      ;divides an MPV by BX

                Std

                Mov     si, [MPVDivZero]
                Mov     dx, [MPVDivZeroSize]
                Xor     ax, ax

                Sub     di, dx
                Sub     di, dx

@@10:           Cmp     dx, MPVSize/2       ;track how big the MPV is
                Je      @@20
                Cmp     [si], ax
                Jne     @@20
                Dec     si
                Dec     si
                Inc     dx
                Stosw
                Jmp     @@10

@@20:           Mov     [MPVDivZero], si
                Mov     [MPVDivZeroSize], dx

                Mov     cx, MPVSize/2
                Sub     cx, dx

                Xor     dx, dx
                Push    cx
                And     cx, 15
                Jcxz    @@40
@@30:           Lodsw
                Div     bx
                Stosw
                Loop    @@30
@@40:           Pop     cx

                Shr     cx, 4
                Jz      @@Exit
EVEN
@@50:           REPT    16
                Lodsw
                Div     bx
                Stosw
                ENDM
                Loop    @@50

@@Exit:         Cld
                Ret
ENDP            MPVDIV
;-------------------------------------------------------------------------------
EVEN
PROC            MPVMUL                      ;multiplies an MPV by BX

                Mov     si, [MPVMulZero]
                Mov     cx, [MPVMulZeroSize]

@@01:           Cmp     [word si], 0        ;track how big the MPV is
                Je      @@02
                Inc     si
                Inc     si
                Dec     cx
                Jnz     @@01
@@02:
                Mov     [MPVMulZero], si
                Mov     [MPVMulZeroSize], cx

                Neg     cx
                Add     cx, MPVSize/2

                Mov     di, offset MPV4
                Mov     si, offset MPV4

                Xor     bp, bp

                Push    cx
                And     cx, 7
                Jz      @@03
@@04:           Lodsw
                Mul     bx
                Add     ax, bp
                Adc     dx, 0
                Stosw
                Mov     bp, dx
                Loop    @@04
@@03:           Pop     cx
                Shr     cx, 3
                Jnz     @@10
                Jmp     @@Exit

EVEN
@@10:           REPT    8
                Lodsw
                Mul     bx
                Add     ax, bp
                Adc     dx, 0
                Stosw
                Mov     bp, dx
                ENDM

                Loop    @@10

@@Exit:         Mov     ax, bp
                Stosw

                Ret
ENDP            MPVMUL
;-------------------------------------------------------------------------------
EVEN
PROC            MPVSHIFT                    ;shifts an MPV over to the left 1 time
                Mov     cx, (MPVSize/2)/16

                Mov     dx, 32

                Clc
EVEN
@@10:           Rcl     [word ds:si], 1
                Rcl     [word ds:si+2], 1
                Rcl     [word ds:si+4], 1
                Rcl     [word ds:si+6], 1

                Rcl     [word ds:si+8], 1
                Rcl     [word ds:si+10], 1
                Rcl     [word ds:si+12], 1
                Rcl     [word ds:si+14], 1

                Rcl     [word ds:si+16], 1
                Rcl     [word ds:si+18], 1
                Rcl     [word ds:si+20], 1
                Rcl     [word ds:si+22], 1

                Rcl     [word ds:si+24], 1
                Rcl     [word ds:si+26], 1
                Rcl     [word ds:si+28], 1
                Rcl     [word ds:si+30], 1

                Lahf
                Add     si, dx
                Sahf

                Loop    @@10

                Ret

ENDP            MPVSHIFT
;-------------------------------------------------------------------------------
EVEN
PROC            MPVCOPY                     ;copies an MPV
                Mov     cx, MPVSize/2

                Rep     Movsw

                Ret
ENDP            MPVCOPY
;-------------------------------------------------------------------------------
EVEN
PROC            MPVADD                      ;adds an MPV with another MPV

                Mov     cx, MPVSize/2
                Sub     cx, [MPVDivZeroSize]
                Jnz     @@10
                Ret
@@10:
                Mov     dx, 32

                Push    cx
                And     cx, 15
                Jz      @@30
                Clc
@@20:           Lodsw
                Adc     [ds:di], ax
                Inc     di
                Inc     di
                Loop    @@20
@@30:           Pop     cx

                Lahf
                Shr     cx, 4
                Jnz     @@40
                Ret
@@40:           Sahf

EVEN
@@50:           Lodsw
                Adc     [ds:di], ax
                Lodsw
                Adc     [ds:di+2], ax
                Lodsw
                Adc     [ds:di+4], ax
                Lodsw
                Adc     [ds:di+6], ax
                Lodsw
                Adc     [ds:di+8], ax
                Lodsw
                Adc     [ds:di+10], ax
                Lodsw
                Adc     [ds:di+12], ax
                Lodsw
                Adc     [ds:di+14], ax

                Lodsw
                Adc     [ds:di+16], ax
                Lodsw
                Adc     [ds:di+18], ax
                Lodsw
                Adc     [ds:di+20], ax
                Lodsw
                Adc     [ds:di+22], ax
                Lodsw
                Adc     [ds:di+24], ax
                Lodsw
                Adc     [ds:di+26], ax
                Lodsw
                Adc     [ds:di+28], ax
                Lodsw
                Adc     [ds:di+30], ax

                Lahf
                Add     di, dx
                Sahf

                Loop    @@50

@@60:           Jnc     @@70
                Adc     [word ds:di], 0
                Inc     di
                Inc     di
                Jmp     @@60

@@70:           Ret

ENDP            MPVADD
;-------------------------------------------------------------------------------
EVEN
PROC            MPVSUB                      ;subtracts an MPV from another MPV

                Mov     cx, MPVSize/2
                Sub     cx, [MPVDivZeroSize]
                Jnz     @@10
                Ret
@@10:
                Mov     dx, 32

                Push    cx
                And     cx, 15
                Jz      @@30
                Clc
@@20:           Lodsw
                Sbb     [ds:di], ax
                Inc     di
                Inc     di
                Loop    @@20
@@30:           Pop     cx

                Lahf
                Shr     cx, 4
                Jnz     @@40
                Ret
@@40:           Sahf

EVEN
@@50:           Lodsw
                Sbb     [ds:di], ax
                Lodsw
                Sbb     [ds:di+2], ax
                Lodsw
                Sbb     [ds:di+4], ax
                Lodsw
                Sbb     [ds:di+6], ax
                Lodsw
                Sbb     [ds:di+8], ax
                Lodsw
                Sbb     [ds:di+10], ax
                Lodsw
                Sbb     [ds:di+12], ax
                Lodsw
                Sbb     [ds:di+14], ax

                Lodsw
                Sbb     [ds:di+16], ax
                Lodsw
                Sbb     [ds:di+18], ax
                Lodsw
                Sbb     [ds:di+20], ax
                Lodsw
                Sbb     [ds:di+22], ax
                Lodsw
                Sbb     [ds:di+24], ax
                Lodsw
                Sbb     [ds:di+26], ax
                Lodsw
                Sbb     [ds:di+28], ax
                Lodsw
                Sbb     [ds:di+30], ax

                Lahf
                Add     di, dx
                Sahf

                Loop    @@50

@@60:           Jnc     @@70
                Sbb     [word ds:di], 0
                Inc     di
                Inc     di
                Jmp     @@60

@@70:           Ret

ENDP            MPVSUB
;-------------------------------------------------------------------------------
PROC            MPVLOAD10                   ;initializes MPV4 to a power of 10

                Mov     ah, 9
                Mov     dx, offset Message2
                Int     021h

                Mov     [word ds:MPV4+2], 03B9Ah
                Mov     [word ds:MPV4], 0CA00h

                Mov     [word MPVMulZero], offset MPV4
                Mov     [word MPVMulZeroSize], MPVSize/2
EVEN
@@10:           Mov     bx, 10000
                Call    MPVMUL

                Call    Twiddle

                Cmp     [word ds:MPV4+MPVSize-12], 0 ;fudge factor = 12 bytes
                Je      @@10

                Ret
ENDP            MPVLOAD10
;-------------------------------------------------------------------------------
PROC            MPVPRINT                    ;prints an MPV to the screen in decimal
                Mov     ah, 9
                Mov     dx, offset Message5
                Int     021h

                Mov     cx, NumDigits/4
                Mov     [ds:Max], cx

                Mov     di, offset Digits+NumDigits-1
                Mov     [ds:MPVDivZero], offset MPV2+MPVSize-2
                Mov     [ds:MPVDivZeroSize], 0

EVEN
@@10:           Push    cx di

                Mov     di, offset MPV2+MPVSize-2
                Mov     bx, 10000
                Call    MPVDIV

                Pop     di cx

                Mov     ax, DigitSeg
                Mov     ds, ax

                Mov     ax, dx
                Mov     bx, 10

                REPT    4

                Xor     dx, dx
                Div     bx
                Add     dl, '0'
                Mov     [ds:di], dl
                Dec     di

                ENDM

                Mov     ax, @DATA
                Mov     ds, ax

                Mov     ax, NumDigits/4
                Sub     ax, cx
                Call    PERCENT

                Loop    @@10

                Mov     ah, 9
                Mov     dx, offset Message6
                Int     021h

                Mov     cx, NumDigits

                Mov     ax, DigitSeg
                Mov     ds, ax
                Mov     si, di
                Inc     si

@@20:           Lodsb
                Cmp     al, '0'
                Jne     @@30
                Loop    @@20
                Jmp     short @@Exit
@@30:

                Mov     dl, al
                Mov     ah, 2
                Int     021h

                Dec     cx
                Jz      @@Exit

@@40:           Lodsb                   ;this is slower than doing a single
                Mov     dl, al          ;DOS call to print the entire number,
                Mov     ah, 2           ;but that's good because it doesn't
                Int     021h            ;zip up the screen as fast
                Loop    @@40

@@Exit:         Ret
ENDP            MPVPRINT
;-------------------------------------------------------------------------------
PROC            TERM1                   ;calculates the first term of the Machin series
                Mov     ah, 9
                Mov     dx, offset Message3
                Int     021h

                Mov     [ds:Max], Last1

                Mov     si, offset MPV4
                Mov     di, offset MPV1
                Call    MPVCOPY

                Mov     [ds:OddEven], -1
                Mov     [ds:Divisor], 1
                Mov     [ds:MPVDivZero], offset MPV1+MPVSize-2
                Mov     [ds:MPVDivZeroSize], 0

EVEN
@@10:
                Mov     di, offset MPV1+MPVSize-2

                Mov     bx, 5
                Cmp     [ds:Divisor], 1
                Je      @@20
                Mov     bx, 25
@@20:           Call    MPVDIV

                Mov     di, offset MPV3+MPVSize-2
                Mov     bx, [ds:Divisor]
                Call    MPVDIV

                Mov     si, offset MPV3
                Mov     di, offset MPV2

                Cmp     [ds:OddEven], 0
                Je      @@30
                Call    MPVADD
                Jmp     short @@40
EVEN
@@30:           Call    MPVSUB

@@40:
                Not     [ds:OddEven]

                Mov     ax, [ds:Divisor]
                Inc     ax
                Inc     ax
                Mov     [ds:Divisor], ax

                Cmp     ax, Last1
                Jae     @@Done
                Push    offset @@10
                Jmp     PERCENT
@@Done:
                Mov     si, offset MPV2
                Call    MPVSHIFT
                Mov     si, offset MPV2
                Call    MPVSHIFT

                Ret
ENDP            TERM1
;-------------------------------------------------------------------------------
PROC            TERM2                    ;calculates the second term of the Machin series
                Mov     ah, 9
                Mov     dx, offset Message4
                Int     021h

                Mov     si, offset MPV4
                Mov     di, offset MPV1
                Call    MPVCOPY

                Mov     [ds:Max], Last2

                Mov     [ds:OddEven], 0
                Mov     [ds:Divisor], 1
                Mov     [ds:MPVDivZero], offset MPV1+MPVSize-2
                Mov     [ds:MPVDivZeroSize], 0
EVEN
@@10:
                Mov     di, offset MPV1+MPVSize-2
                Mov     bx, 239
                Cmp     [ds:Divisor], 1
                Je      @@20
                Mov     bx, 239*239
@@20:           Call    MPVDIV

                Mov     di, offset MPV3+MPVSize-2
                Mov     bx, [ds:Divisor]
                Call    MPVDIV

                Mov     si, offset MPV3
                Mov     di, offset MPV2

                Cmp     [ds:OddEven], 0
                Je      @@30
                Call    MPVADD
                Jmp     short @@40
EVEN
@@30:           Call    MPVSUB

@@40:

                Not     [ds:OddEven]

                Mov     ax, [ds:Divisor]
                Inc     ax
                Inc     ax
                Mov     [ds:Divisor], ax

                Cmp     ax, Last2
                Jae     @@Done
                Push    offset @@10
                Jmp     PERCENT
@@Done:

                Mov     si, offset MPV2
                Call    MPVSHIFT
                Mov     si, offset MPV2
                Call    MPVSHIFT

                Ret
ENDP            TERM2
;-------------------------------------------------------------------------------
EVEN
PROC            TWIDDLE
                Mov     al, [ds:TwiddleCount]
                Inc     ax
                And     al, 01Fh
                Mov     [byte ds:TwiddleCount], al
                Jz      @@Update
                Ret

@@Update:       Mov     bx, [ds:TwiddlePos]
                Mov     dl, [ds:bx+TwiddleStuff]
                Inc     bx
                And     bl, 3
                Mov     [ds:TwiddlePos], bx

                Mov     ah, 2
                Int     021h
                Mov     dl, 8
                Mov     ah, 2
                Int     021h

                Ret
ENDP            TWIDDLE
;-------------------------------------------------------------------------------
PROC            PERCENT
                Mov     bl, [ds:TwiddleCount]
                Inc     bl
                Mov     [byte ds:TwiddleCount], bl
                Jz      @@Update
                Ret
@@Update:
                Push    cx

                Mov     bx, 100
                Mul     bx
                Div     [ds:Max]

                Mov     bp, 10
                Xor     cx, cx

@@CalcNumber:   Xor     dx, dx
                Div     bp
                Add     dl, '0'
                Push    dx
                Inc     cx
                And     ax, ax
                Jnz     @@CalcNumber

                Mov     bp, cx
                Mov     ah, 2
@@PrintNumber:
                Pop     dx
                Int     021h
                Loop    @@PrintNumber

                Mov     dl, '%'
                Int     021h

                Mov     cx, bp
                Inc     cx

                Mov     dl, 8
@@BackSpace:    Int     021h
                Loop    @@BackSpace

                Pop     cx

                Ret
ENDP            PERCENT
;-------------------------------------------------------------------------------
PROC            PrintNumDigits

                Mov     ah, 9
                Mov     dx, offset Message7
                Int     021h

                Mov     ax, NumDigits
                Mov     bx, 10
                Xor     cx, cx

@@10:           Cwd
                Div     bx
                Add     dl, '0'
                Push    dx
                Inc     cx
                And     ax, ax
                Jnz     @@10

                Mov     ah, 2

@@20:           Pop     dx
                Int     021h
                Loop    @@20

                Mov     ah, 9
                Mov     dx, offset Message8
                Int     021h

                Ret

ENDP            PrintNumDigits
;-------------------------------------------------------------------------------
END
