ikj78i 發表於 2024-11-20 04:45:04

用組合語言求兩數的最大公因數

底下的gcd_lcm.asm也是在Windows作業系統下的主控臺程式:.586
.model  flat,stdcall
option  casemap:none
include    c:\masm32\include\windows.inc
include    c:\masm32\include\kernel32.inc
include    c:\masm32\include\user32.inc
include    c:\masm32\include\shlwapi.inc     ;內含 StrToInt 的原型
includelib c:\masm32\lib\shlwapi.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\user32.lib
;*******************************************************************************
.DATA
hOut    DD      ?
hIn     DD      ?
dOut    DD      ?
dIn     DD      ?
n1      DD      ?       ;第一個整數
n2      DD      ?       ;第二個整數
gcd     DD      ?       ;最大公因數
lcm     DD      ?       ;最小公倍數
sBuffer DB      60h dup (0)
sHint   DB      "請輸正整數:"
szFmt   DB      "%u與%u的最大公因數是%u,最小公倍數是%u",0
;*******************************************************************************
.CODE
;-------------------------------------------------------------------------------
input   PROC
  ;顯示「請輸正整數:」的提示文字,並讓使用者輸入數字
        invoke  WriteConsole,hOut,OFFSET sHint,SIZEOF sHint,OFFSET dOut,0
        invoke  ReadConsole,hIn,OFFSET sBuffer,12,OFFSET dIn,0
  ;呼叫StrToInt把使用者輸入的數字變成數值。假如使用者輸入「123」,但在記憶體中
  ;並不是123這個數值,而是31h、32h、33h,這是因為鍵盤輸入的是字元,在電腦中每個
  ;字元(字元可視為一個字,也就是鍵盤上的英文母、阿拉伯數字等),都以一個數來表示
  ;,阿拉伯數字的1是用十六進位的31h表示,阿拉伯數字的2是用十六進位的32h表示,依
  ;此類推,其中的h代表此數為十六進位,b代表二進位,d代表十進位,若數字後沒這些
  ;英文字代表十進位。StrToInt的功用就是把31h、32h、33h轉換為123。
  ;StrToInt只有一個參數,就是這些文字的位址,轉換後結果存於EAX裏
        invoke  StrToInt,OFFSET sBuffer
        ret
input   ENDP
;-------------------------------------------------------------------------------
;利用輾轉相除法求num1、num2的最大公因數。在invoke fnd_gcd,n1,n2之後,n1、n2會分別
;存於num1、num2內,在fnd_gcd副程式內,最好使用num1、num2。
fnd_gcd PROC    num1:DWORD,num2:DWORD
        mov     ecx,num1 ;輾轉相除法的過程是兩數相除後,如果有餘數,那麼下次的除數
        mov     edx,num2 ;是這次的餘數,被除數是這次的除數,再進行除法,直到餘數為
        cmp     ecx,edx  ;零為止,最後一次的除數就是最大公因數
        jb      below   ;比較兩數何者較小
again:  xchg    ecx,edx ;較小的當除數存於ECX,較大的當被除數存EDX
below:  mov     eax,edx ;把被除數放在EAX
        xor     edx,edx ;使EDX變為0
        div     ecx     ;EAX除以ECX得商為EAX,餘數為EDX
        or      edx,edx ;檢查餘數EDX是否為0
        jnz     again   ;若不為零,還須再次進行除法,經過除法後,餘數(EDX)一定比除數(ECX)小
        mov     eax,ecx ;若為零,傳回最後除數即為最大公因數
        ret
fnd_gcd ENDP
;-------------------------------------------------------------------------------
main    PROC
        invoke  GetStdHandle,STD_OUTPUT_HANDLE
        mov     hOut,eax
        invoke  GetStdHandle,STD_INPUT_HANDLE
        mov     hIn,eax
  ;輸入第一個整數,存於n1
        call    input
        mov     n1,eax
  ;輸入第一個整數,存於n2
        call    input
        mov     n2,eax
  ;計算n1、n2的最大公因數,存於gcd內
        invoke  fnd_gcd,n1,n2
        mov     gcd,eax
  ;計算n1、n2的最小公倍數,存於lcm內
        mov     eax,n1
        mul     n2
        div     gcd
        mov     lcm,eax
  ;wsprintf會根據szFmt字串的格式,把n1、n2、gcd、lcm存於sBuffer內。這四個數值會變
  ;成數字字串。假設n1是123,那麼在sBuffer內是31h、32h、33h。szFmt內的%u代表無號數
  ;,可以把無號數是為正數。wsprintf的傳回值是在sBuffer存入多少位元組,此位元組數
  ;會放在EAX傳回
        invoke  wsprintf,OFFSET sBuffer,OFFSET szFmt,n1,n2,gcd,lcm
        invoke  WriteConsole,hOut,OFFSET sBuffer,eax,OFFSET dOut,0
        invoke  ExitProcess,0
main    ENDP
;*******************************************************************************
END     main把它存成gcd_lcm.asm,照下圖淡藍色字輸入指令組譯後,就可執行。
要注意,gcd_lcm並沒有檢查使用者輸入的數值大小所以輸入的數值太大時會出錯,兩數最好要小餘65000。



頁: [1]
查看完整版本: 用組合語言求兩數的最大公因數