[编程工具]masm32(9.00)+RadAsm2.2.0.9
[调试工具]OD1.10
[调试平台]WinXP/SP2

  这次再发一个ASM程序:计算圆周率,比上一个难度大了点儿,主要是完成了N(N>1000000000)位十进制大数的加、减、除运算及显示输出。编写代码用了我一天的业余时间,调试则用了两天,惨啊,明明是4*4*5*DATAMAX/1000==04C4B400h,DATAMAX=1000000000,我写成
mov dword ptr[eax],4*4*5*DATAMAX/1000  竟然没编译出我想要的
mov dword ptr[eax],04C4B400h      让我费了很多时间才发现,编译器有bug??
  总算调试通过了,了了我多年的一个心愿。在还没有见过计算机为何物时就知道可以用它计算圆周率,后来用VC++编写了一个计算程序,速度一直不够快,就想着哪一天能用ASM写一个多好,现在总算完成了,速度也比较满意。如果我有时间有精力,而且兴趣还没有消失的话,以后会再写一些东西,希望大家不要打击我,我会慢慢进步的。

;***********************************************************************************************************
;计算原理:
;PI=PI=16x(1/5-1/(5^3/3)+1/(5^5/5)-1/(5^7/7)+...)-4x(1/239-1/(239^3/3)+1/(239^5/5)-1/(239^7/7)+...)
;PI=4x(4x5/25-239/57121)/1-4x(4x5/25^2-239/57121^2)/3+4x(4x5/25^3-239/57121^3)/5-...
;每一个dword型数据只能放9位十进制数,用N个dword数存储9N位计算结果
;y=(4*4*5/25^a),z=(4*239/57121^a),c=(y-z)/a,x+=c 或 x-=c,a+=2(a初始为1),循环计算直到y=0结束
;***********************************************************************************************************
;“计算圆周率.Asm”文件

.386
.model flatstdcall  ;32 bit memory model
option casemap :none  ;case sensitive

include 计算圆周率.inc

.code

start:
    invoke InitCommonControls
    invoke GetModuleHandle,NULL
    invoke DialogBoxParam,eax,IDD_DIALOG_MAIN,NULL,addr DlgProc,NULL
    invoke ExitProcess,0

;########################################################################

_CalPI    proc    uses edi esi ebx
    LOCAL    @hMemX,@pMemX,@hMemY,@pMemY,@hMemZ,@pMemZ,@hMemC,@pMemC
    LOCAL    @dwDigit,@dwCalLen,@dwA,@dwI,@dwTime

    invoke    GetDlgItemInt,hWndDlg,IDC_DIGIT,0,0
    .if    eax<1
        invoke    SetDlgItemInt,hWndDlg,IDC_DIGIT,1000,0
        mov    eax,1000
    .endif
    add    eax,4
    mov    @dwDigit,eax    ;要计算的位数
    
    ;mov    ecx,eax
    ;shr    ecx,7
    ;add    eax,ecx        ;要多算几位,因为最后若干位不准确
    mov    ecx,DATALEN    ;要多算几位,因为最后若干位不准确
    add    eax,ecx
    div    ecx    ;每四字节存放 DATALEN 位十进制的结果
    test    edx,edx
    jz    @F
    inc    eax
@@:    shl    eax,2
    mov    @dwCalLen,eax    ;内存数组的大小

    add    eax,4
    invoke    GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,eax
    mov    @hMemY,eax
    invoke    GlobalLock,eax
    mov    @pMemY,eax
    mov    dword ptr[eax],04C4B400h;4*4*5*DATAMAX/1000,不能写成mov dword ptr[eax],4*4*5*DATAMAX/1000,编译器有bug??
    mov    ecx,@dwCalLen
    inc    dword ptr[eax+ecx]    ;此为计算结束的标志
    
    invoke    GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,ecx
    mov    @hMemZ,eax
    invoke    GlobalLock,eax
    mov    @pMemZ,eax
    mov    dword ptr[eax],038FB6700h;4*239*DATAMAX/1000 同上,编译器有bug??
    
    invoke    GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,@dwCalLen
    mov    @hMemC,eax
    invoke    GlobalLock,eax
    mov    @pMemC,eax
    
    invoke    GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,@dwCalLen
    mov    @hMemX,eax
    invoke    GlobalLock,eax
    mov    @pMemX,eax
    
    xor    eax,eax
    mov    @dwI,eax    ;循环计数器初始化
    inc    eax
    mov    @dwA,eax    ;A初始化
    invoke    GetTickCount
    mov    @dwTime,eax
    
    ;**************************************************************************
CAL_START:
    mov    ebx,DATAMAX    ;开始计算Y/=25
    mov    esi,25
    xor    edx,edx
    mov    ecx,@dwI
    mov    edi,@pMemY
CAL_Y:
    mov    eax,edx
    mul    ebx
    add    eax,dword ptr[edi+ecx]
    adc    edx,0
    div    esi
    mov    dword ptr[edi+ecx],eax
    add    ecx,4
    cmp    ecx,@dwCalLen
    jb    CAL_Y

    mov    ecx,@dwI
CAL_SETPOS:
    cmp    dword ptr[edi+ecx],0
    jnz    CAL_ISEND
    add    ecx,4
    jmp    CAL_SETPOS
CAL_ISEND:
    cmp    ecx,@dwCalLen
    jae    CAL_END

    mov    @dwI,ecx
    
    mov    esi,57121    ;开始计算Z/=57121
    xor    edx,edx
    mov    edi,@pMemZ
CAL_Z:
    mov    eax,edx
    mul    ebx
    add    eax,dword ptr[edi+ecx]
    adc    edx,0
    div    esi
    mov    dword ptr[edi+ecx],eax
    add    ecx,4
    cmp    ecx,@dwCalLen
    jb    CAL_Z

    clc    ;开始计算C=Y-Z
    mov    esi,@pMemC
    mov    ebx,@pMemY
CAL_C:
    lahf
    sub    ecx,4
    cmp    ecx,@dwI
    jl    CAL_CCF
    sahf
    mov    eax,dword ptr[ebx+ecx]
    sbb    eax,dword ptr[edi+ecx]
    jnc    CAL_CC
    add    eax,DATAMAX
    stc
CAL_CC:
    mov    dword ptr[esi+ecx],eax
    jmp    CAL_C
CAL_CCF:
    ;sahf
    ;jc    CAL_CDIVA
    ;dec    dword ptr[esi+ecx]
CAL_CDIVA:
    xor    edx,edx        ;开始计算C/=A
    mov    ecx,@dwI
    mov    edi,@dwA
    mov    ebx,DATAMAX
CAL_CA:
    mov    eax,edx
    mul    ebx
    add    eax,dword ptr[esi+ecx]
    adc    edx,0
    div    edi
    mov    dword ptr[esi+ecx],eax
    add    ecx,4
    cmp    ecx,@dwCalLen
    jb    CAL_CA    

    mov    edi,@pMemX
    test    @dwA,2        ;判断(A/2)是奇数还是偶数
    jz    CAL_AO

CAL_A1:
    lahf        ;开始计算X-=C  (A/2)是奇数时
    sub    ecx,4
    cmp    ecx,@dwI
    jl    CAL_ACF1
    sahf
    mov    eax,dword ptr[edi+ecx]
    sbb    eax,dword ptr[esi+ecx]
    jnc    CAL_AA1
    add    eax,ebx
    stc
CAL_AA1:
    mov    dword ptr[edi+ecx],eax
    jmp    CAL_A1
CAL_ACF1:
    sahf
    jnc    CAL_NEXTA
    dec    dword ptr[edi+ecx]
    jmp    CAL_NEXTA

CAL_AO:
    lahf        ;开始计算X+=C  (A/2)是偶数时
    sub    ecx,4
    cmp    ecx,@dwI
    jl    CAL_ACFO
    sahf
    mov    eax,dword ptr[edi+ecx]
    adc    eax,dword ptr[esi+ecx]
    cmp    ebx,eax
    ja    CAL_AAO
    sub    eax,ebx
    stc
CAL_AAO:
    mov    dword ptr[edi+ecx],eax
    jmp    CAL_AO
CAL_ACFO:
    sahf
    jnc    CAL_NEXTA
    inc    dword ptr[edi+ecx]

CAL_NEXTA:
    add    @dwA,2
    jmp    CAL_START
    ;**************************************************************************

CAL_END:
    invoke    GetTickCount
    xor    edx,edx
    sub    eax,@dwTime
    mov    ecx,1000
    div    ecx
    mov    ecx,@dwA
    shr    ecx,1
    inc    ecx
    invoke    wsprintf,@pMemC,addr lpStr,ecx,eax,edx
    
    invoke    SetDlgItemText,hWndDlg,IDC_TIME,@pMemC
    invoke    GlobalUnlock,@pMemC
    invoke    GlobalFree,@hMemC
    invoke    GlobalUnlock,@pMemY 
    invoke    GlobalFree,@hMemY
    invoke    GlobalUnlock,@pMemZ 
    invoke    GlobalFree,@hMemZ 

    mov    eax,@dwDigit
    add    eax,DATALEN
    invoke    GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,eax
    mov    @hMemC,eax
    invoke    GlobalLock,eax
    mov    @pMemC,eax

    mov    edi,eax
    mov    ecx,@dwDigit
    add    ecx,DATALEN-1
    mov    al,030h
    rep    stosb
    
    mov    edi,@pMemC
    mov    ecx,10
    mov    esi,@pMemX
    xor    edx,edx
    mov    @dwI,edx
    mov    ebx,DATALEN
SHOW_DATA:
    mov    eax,dword ptr[esi]    ;显示X中计算结果
SHOW_CHAR:
    div    ecx
    dec    ebx
    add    byte ptr[edi+ebx],dl
    xor    edx,edx
    test    eax,eax
    jnz    SHOW_CHAR

    mov    eax,@dwI
    cmp    eax,@dwDigit
    jae    OK    
    add    esi,4
    mov    ebx,DATALEN
    add    @dwI,ebx
    add    edi,ebx
    jmp    SHOW_DATA

OK:
    mov    ecx,@dwDigit
    mov    edi,@pMemC
    mov    byte ptr[edi+ecx-1],0
    mov    cl,byte ptr[edi+2]
    mov    byte ptr[edi+1],cl
    mov    byte ptr[edi+2],'.'
    invoke    GlobalUnlock,@pMemX 
    invoke    GlobalFree,@hMemX
    inc    edi
    invoke    SetDlgItemText,hWndDlg,IDC_PI,edi
    
    invoke    GlobalUnlock,@pMemC
    invoke    GlobalFree,@hMemC

    ret

_CalPI    endp
DlgProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM

    mov        eax,uMsg
    .if eax==WM_INITDIALOG
        mov    eax,hWin
        mov    hWndDlg,eax
    .elseif eax==WM_COMMAND
        mov    eax,wParam 
            .if lParam!=0 
                   mov edx,eax
                   shr edx,16 
                    .if dx==BN_CLICKED ;按钮消息处理
                        .if ax==IDC_CAL ;计算
                        call    _CalPI
                        .endif
                    .endif
                .endif
    .elseif eax==WM_CLOSE
        invoke EndDialog,hWin,0
    .else
        mov        eax,FALSE
        jmp        @F
    .endif
    mov        eax,TRUE
@@:
    ret

DlgProc endp

end start

;***********************************************************************************
;“计算圆周率.Inc”文件

include windows.inc
include kernel32.inc
include user32.inc
include Comctl32.inc
include shell32.inc

includelib kernel32.lib
includelib user32.lib
includelib Comctl32.lib
includelib shell32.lib

DlgProc            PROTO    :HWND,:UINT,:WPARAM,:LPARAM

.const

DATALEN                equ 9
DATAMAX                equ 1000000000
;计算圆周率.dlg
IDD_DIALOG_MAIN            equ 101
IDC_GRP1            equ 1003
IDC_STC1            equ 1001
IDC_DIGIT            equ 1002
IDC_CAL                equ 1004
IDC_TIME            equ 1005
IDC_PI                equ 1006

;计算圆周率.Rc
MY_ICON                equ 10

;#########################################################################

.data?
hWndDlg                HWND ?

;#########################################################################

.data
lpStr                db '计算%d次,用时%d.%d秒',0