簡體   English   中英

兩個 16 位數字的 GCD(最大公約數)

[英]GCD (Greatest Common Divisor) for two 16-bit numbers

這里的目標是找到兩個以小端表示法存儲的 16 位數字的 GCD。 這些數字存儲在以下 memory 單元格中:

  • 第一個數字:0x3000-0x3001
  • 秒數:0x4000-0x4001
  • 結果應該 go 變成:0x5000-0x5001

以下示例適用於 8 位數字:

ORG 0000H

MOV 30H, #09
MOV 40H, #06
MOV A, 30H
MOV B, 40H
CJNE A, B, next
LJMP stop

next:
  JNC loop
  MOV A, R2
  MOV B, R1
  
loop:
  MOV R3, B
  DIV AB
  MOV A, R3  
  MOV R7, B
  CJNE R7, #00H, loop
  
stop:
  MOV 50H, A
       
END

問題:如何修改此代碼以對兩個 16 位數字進行操作? 我是否需要對單個字節進行操作,然后使用數據指針 ( DPTR ) 將它們加載/移動到所需的 memory 單元?

(我正在使用 µVision IDE)

計算最大公約數的一種方法是使用歐幾里得算法 要獲得兩個 16 位數字的 GCD,需要擴大我們在 8051 上非常有限的DIV AB 。這並非不可能,但我選擇使用根本不需要除法的算法。

二進制 GCD 算法

我們可以避免 8051 有限的除法能力,並使用一種算法,通過一系列移位和減法來代替模數計算。 下面是二進制 GCD 算法在 BASIC 中的樣子:

Procedure BinaryGCD
  ; Calculates R% = GCD(A%,B%)
  If A%=0 Or B%=0 Then
    R% = 1
  Else
    C% = 0
    While Even(A%) And Even(B%)
      Inc C%
      A% = Shr(A%,1)
      B% = Shr(B%,1)
    Wend
    Do
      While Even(A%)
        A% = Shr(A%,1)
      Wend
      While Even(B%)
        B% = Shr(B%,1)
      Wend
      Exit If A%=B%
      If A%>B% Then
        Sub A%,B%
      Else
        Sub B%,A%
      Endif
    Loop
    R% = Shl(A%,C%)
  Endif
Return

實施說明

存儲在外部 memory 中的數字被復制到內部 memory 的位地址區。 為什么是這樣? 使用內部 memory 避免了必須一直操作 16 位地址的麻煩。 並且專門使用位地址區域,允許編寫有效的代碼來測試偶數/奇數條件。 在位地址區之外存儲將需要更多字節和更多周期,更不用說它會破壞累加器。 相比:

; Multiprecision number starts at 58h (not bit addressable)
MOV     A, #1
ANL     A, 58h
JNZ     IsOdd
; Uses 6 bytes and consumes 4 cycles

; Multiprecision number starts at 28h (bit addressable)
JB      64, IsOdd
; Uses 3 bytes and consumes 2 cycles

請注意,我的程序可以處理從字節到 qword 以及介於兩者之間的所有無符號多精度數字。 我首先創建了一系列方便的子程序來處理多字節數。 許多這些子例程被調用一次,因此也可以很容易地內聯。 為了生成可讀的程序,我選擇不這樣做。

子程序

在()
對於每個子程序, R0R1DPTR寄存器在輸入時都是指向所涉及數字開頭的指針(最低有效字節,因為這是 little endean)。

出去()
mpLoadmpStore返回時, R1DPTR都將前進,以便能夠處理相鄰項目而無需重新加載指針寄存器。
mpTest返回時,累加器A很重要。 如果A為零,則提交的數字為零。
mpCmp返回時,累加器A和進位標志C很重要。 如果A為零,則提交的數字彼此相等。 否則,明確的C表示第一個數字 ( @R0 ) 大於第二個數字 ( @R1 ),反之亦然,對於一組C

模組()
這里列出了子程序使用但不返回記錄值的所有寄存器。

; Copies a multiprecision number from external memory to internal memory
; IN(R1,DPTR) OUT(R1,DPTR) MOD(A,R2)
mpLoad:
  MOV     R2, #MPC
Load:
  MOVX    A, @DPTR
  MOV     @R1, A
  INC     DPTR
  INC     R1
  DJNZ    R2, Load
  RET

; Copies a multiprecision number from internal memory to external memory
; IN(R1,DPTR) OUT(R1,DPTR) MOD(A,R2)
mpStore:
  MOV     R2, #MPC
Store:
  MOV     A, @R1
  MOVX    @DPTR, A
  INC     DPTR
  INC     R1
  DJNZ    R2, Store
  RET

; Doubles a multiprecision number
; IN(R1) OUT() MOD(A,R1,R2)
mpShl:
  MOV     R2, #MPC
  CLR     C
Shl:
  MOV     A, @R1
  RLC     A
  MOV     @R1, A
  INC     R1
  DJNZ    R2, Shl
  RET

; Halves a multiprecision number
; IN(R1) OUT() MOD(A,R2)
mpShr:
  MOV     R2, #MPC
  MOV     A, R1
  ADD     A, R2   ; -> C == 0
  MOV     R1, A
Shr:
  DEC     R1
  MOV     A, @R1
  RRC     A
  MOV     @R1, A
  DJNZ    R2, Shr
  RET

; Tests if a multiprecision number is zero
; IN(R1) OUT(A) MOD(R1,R2)
mpTest:
  MOV     R2, #MPC
  MOV     A, #0
Test:
  ORL     A, @R1
  INC     R1
  DJNZ    R2, Test
  RET

; Compares two multiprecision numbers
; IN(R0,R1) OUT(A,C) MOD(R0,R1,R2)
mpCmp:
  MOV     R2, #MPC
  MOV     A, R1
  ADD     A, R2
  MOV     R1, A
  MOV     A, R0
  ADD     A, R2   ; -> C == 0
  MOV     R0, A
Cmp:
  DEC     R0
  DEC     R1
  MOV     A, @R0
  SUBB    A, @R1
  JNZ     CmpRet
  DJNZ    R2, Cmp
CmpRet:
  RET

; Subtracts two multiprecision numbers
; IN(R0,R1) OUT() MOD(A,R0,R1,R2)
mpSub:
  MOV     R2, #MPC
  CLR     C
Sub:
  MOV     A, @R0
  SUBB    A, @R1
  MOV     @R0, A
  INC     R0
  INC     R1
  DJNZ    R2, Sub
  RET

主要代碼

您可以輕松地將其變成一個成熟的程序。

MPC     EQU 2              ; Number of bytes per number aka precision
NumX    EQU 20h            ; Internal memory storage address for first number
NumY    EQU 28h            ; Internal memory storage address for second number
; -------------------------
  MOV     R1, #NumX
  MOV     DPTR, #3000h     ; External memory storage address for first number
  LCALL   mpLoad
  MOV     R1, #NumY
  MOV     DPTR, #4000h     ; External memory storage address for second number
  LCALL   mpLoad
; -------------------------
  MOV     R3, #MPC
  MOV     R0, #NumX
  MOV     R1, #NumX
  LCALL   mpTest           ; -> A
  JZ      SetOne
  MOV     R1, #NumY
  LCALL   mpTest           ; -> A
  JNZ     Begin
SetOne:
  INC     A                ; 0 -> 1, 255 -> 0, 255 -> 0, ...
  MOV     @R0, A
  MOV     A, #255
  INC     R0
  DJNZ    R3, SetOne
  SJMP    Copy
; -------------------------
Begin:
  MOV     R3, #0           ; Bits
While1:
  JB      0, While3        ; Jump if NumX[bit0] == 1
  JB      64, While2       ; Jump if NumY[bit0] == 1
  INC     R3               ; Bits++
  MOV     R1, #NumX
  LCALL   mpShr            ; X >> 1
  MOV     R1, #NumY
  LCALL   mpShr            ; Y >> 1
  SJMP    While1
; -------------------------
While2:
  JB      0, While3        ; Jump if NumX[bit0] == 1
  MOV     R1, #NumX
  LCALL   mpShr            ; X >> 1
  SJMP    While2
; - - - - - - - - - - - - -
While3:
  JB      64, Compare      ; Jump if NumY[bit0] == 1
  MOV     R1, #NumY
  LCALL   mpShr            ; Y >> 1
  SJMP    While3
; - - - - - - - - - - - - -
Compare:
  MOV     R0, #NumX
  MOV     R1, #NumY
  LCALL   mpCmp            ; -> A C
  JZ      Equal
  MOV     R0, #NumX
  MOV     R1, #NumY
  JNC     Minus            ; Do (X - Y)
  MOV     R0, #NumY
  MOV     R1, #NumX
Minus:
  LCALL   mpSub            ; Did (X - Y) or (Y - X)
  SJMP    While2
; -------------------------
Equal:
  MOV     A, R3            ; Bits
  JZ      Copy
Scale:                     ; X << Bits
  MOV     R1, #NumX
  LCALL   mpShl            ; X << 1
  DJNZ    R3, Scale
; -------------------------
Copy:
  MOV     R1, #NumX
  MOV     DPTR, #5000h     ; External memory storage address for resulting number
  LCALL   mpStore
; -------------------------
EXIT:
  NOP
  SJMP $

; Here you add the subroutines

END

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM