【问题标题】:Program to find GCD in x86 assembly language用 x86 汇编语言查找 GCD 的程序
【发布时间】:2016-03-06 04:36:51
【问题描述】:

编辑:代码现在运行,感谢您的帮助。它可能不漂亮,但它有效

我必须编写一个程序来查找两个数字的 GCD,提示符是这样的。 “您将编写一个包含函数的程序,该函数将使用欧几里得算法计算最大公约数函数,定义如下:

      GCD(a,0) = a 
      GCD(a,b) = GCD(b, a mod b) for b > 0

您的功能是递归计算 GCD(a,b) 并返回 ax 中的值。函数的输入应该通过将 a 和 b 的值压入堆栈来完成。

您的主程序将:

在屏幕上打印程序说明 在屏幕上显示提示 从键盘接受 a 和 b 的值(使用您之前编写的 DEC_IN 过程。) 将 a 和 b 的值传递给函数 使用适当的提示打印出 GCD(a,b) 的值(使用您之前编写的 DEC_OUT 过程。) 询问用户是否希望重复该过程。” 我相信我已经完成了大部分目标,但是当运行我的程序时,我在输入第二个整数后就冻结了。 任何帮助将不胜感激 这是我的代码:

; program to calculate gcd of two inputs
    org 100h
section .data
prompt0: db "This is a program to caculate the GCD of two inputs $"
prompt1: db "Please enter integer X: $"
prompt2: db "Please enter integer Y: $"
prompt3: db "The GCD is:    $"
intX     dw 0
intY     dw 0
gcd      dw 0

section .text
    mov ah,9        ; print prompt
    mov dx,prompt0
    int     21h
    mov ah,9        ; print prompt
    mov dx,prompt1
    int     21h
    call    dec_in      ; read value into bx
    mov [intX], bx
    mov ah,9        ; print prompt
    mov dx,prompt2
    int     21h
    call    dec_in      ; read value into bx
    mov [intY], bx
    call    calc_GCD
    mov     bx, [gcd]
    mov ah,9        ; print output label
    mov dx,prompt3
    int     21h
    call    dec_out     ; display the value in bx (gcd)

dec_in: 
    ; save registers
    push    ax
    push    dx

    xor bx,bx       ; bx holds accumulated input
    mov ah,1        ; read char fcn
    int 21h     ; read it into al
while1: 
    cmp al,0Dh      ; char = CR?
    je  finis       ; if so, we are done
    push    ax      ; save the character read
    mov ax,10       ; set up for multiply
    mul bx      ; dx:ax <- bx * 10
    mov bx,ax       ; put 16-bit result back in bx (assume no overflow)
    pop ax      ; restore the char read
    and ax,000Fh    ; convert character '0'-'9' to value 0-9
    add bx,ax       ; add value to accumulated input
    mov ah,1        ; read char fcn
    int 21h     ; read next char into al
    jmp while1      ; loop until done
finis:  
    ; restore registers
    pop dx
    pop ax
    ret

dec_out:
    ; save registers we will be using
    push    ax
    push    bx
    push    cx
    push    dx

    xor cx,cx       ; cx counts digits, initially zero
rept:
    mov ax,bx       ; set up to divide by by 10
    xor dx,dx       ; must have a 32 bit (unsigned) dividend
    mov bx,10       ; divisor will be in bx
    div bx      ; quotient will be in ax, remainder in dx
    push    dx      ; push remainder on stack
    inc cx      ; we generated another digit, so count it
    mov bx,ax       ; the quotient goes back in bx
    cmp ax,0        ; clever way to test if quotient is zero
    jne rept        ; if not, generate next digit
    mov ah,2        ; display character function
for2:               ; loop cx times
    pop dx      ; pop digit to print
    or  dl,30h      ; convert the digit to print to ASCII code
    int 21h     ; display the character
    loop    for2        ; and keep going until all digits displayed

    ; restore registers
    pop dx
    pop cx
    pop bx
    pop ax
    ret

calc_GCD:
    mov   ax, [intY]        
        cmp   ax, 0       
        jne   chk_swap      ; check if swap is needed
        mov   ax, [intX]    
        mov   [gcd], ax ; move result into gcd
        ret
chk_swap:
         mov   ax, [intX]   ;store 
         mov   bx, [intY]   
         cmp   ax, bx       
         jl    swap     
         jnl   loop     
swap:
      mov   ax, [intX]      
      mov   bx, [intY]      
          ;temp
          mov   cx, [intY]      
      ; intY = intX
          ; intX = temp
          mov   bx, ax      
          mov   ax, cx      
          mov   [intX], ax      
          mov   [intY], bx      
          jmp   loop            
loop:
          mov   dx, [intX]      
          shr   dx, 16      
          mov   ax, [intX]      
          mov   bx, [intY]      
          div   bx          
          mov   di, [intX]      
          mov   si, [intY]      
          mov   di, si      
          mov   [intX], di      
          mov   [intY], dx      
          jmp   calc_GCD        

【问题讨论】:

  • 你用调试器单步执行了吗? Turbo 调试器运行良好,但您也可以使用 debug.exe(DOS 调试器)。
  • mov eax, [intX] ;store
  • jl swap 后面紧跟jnl loop 并不代表精湛的工艺。删除jl swap
  • jmp loop 后面紧跟loop: 也不代表精湛的工艺。
  • 这段代码有很多问题,我怀疑给你工作代码是你学习所需内容的最佳解决方案。我认为这个问题和代码更适合让您的班级有助教或教授坐下来,并指出您需要理解的课程材料才能正确地重写代码。

标签: assembly nasm dos


【解决方案1】:

无需换成 GCD。如果除数大于被除数,则商为零,余数为被除数,因此 GCD 的第一步会在需要时自动进行交换。

没有必要继续将中间结果存储到 intX 和 intY 中。只需使用寄存器计算 GCD,直到余数为 0,则之前的余数就是 GCD。

;                                       ;ax, bx contain the two numbers
gcd0:   xor     dx,dx                   ;divide
        div     bx
        mov     ax,bx                   ;ax = new dividend = old divisor
        mov     bx,dx                   ;bx = new remainder
        test    bx,bx                   ;loop if remainder != 0
        jnz     gcd0
;                                       ;ax = gcd

最大循环数为斐波那契数:46368、28657、gcd = 1

【讨论】:

  • 使用test bx,bx,而不是or bx,bx。那太糟了! stackoverflow.com/a/33724806/224132
  • @PeterCordes - 这是旧的 8088 代码。它会对 8088 处理器产生影响吗?我改变了答案。
  • @rcgldr : 在 8088/8086 上它们都是 3 个时钟周期用于 reg,reg 并且都编码为 2 个字节
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 2015-04-05
  • 2017-01-03
相关资源
最近更新 更多