10 个用户在程序集中输入数字的平均值

Average of 10 user entered numbers in assembly

这段汇编代码应该计算 10 个用户输入的 4 位数字的平均值,但我不知道为什么无论我输入什么数字,它总是给我 2340 作为平均值。

data segment
    sum dw 2dup(0)
    msg1 db 'Enter 10 numbers:', 0dh,0ah,'$'
    msg2 db 'Average= ','$'
ends

stack segment
    dw   10dup(0)
ends

code segment
    assume cs:code,ds:data,ss:stack
    main proc far 

    mov ax, data
    mov ds, ax

    mov ax, stack
    mov ss, ax

    mov ah, 09h
    mov dx, offset msg1
    int 21h

    mov ch, 10

    NextNumber: 
    mov cl, 4
    mov di, 0  

    get:
    mov ah, 07h
    int 21h

    cmp al, 30h  
    jb get
    cmp al, 39h
    ja get

    mov ah, 02h
    mov dl, al
    int 21h

    sub al, 30h

    mov bl, al
    mov ax, 10
    mul di

    mov bh, 0
    add ax, bx

    mov di, ax   

    dec cl  

    jnz get

    add sum, di
    mov sum+2, 0
    adc sum+2, 0 

    mov ah, 02h
    mov dl, 0ah
    int 21h
    mov dl, 0dh
    int 21h

    dec ch

    jnz NextNumber

    mov dx, sum+2
    mov ax, sum
    mov bx, 10
    div bx

    mov ah, 09h
    mov dx, offset msg2
    int 21h

    mov cx, 4
    mov bx, 10

    next1: mov dx, 0
    div bx

    push dx

    dec cx
    jnz next1

    mov cx,4

    next2: pop dx
    add dl, 30h 
    mov ah, 02h
    int 21h

    dec cx
    jnz next2

    mov ah, 4ch
    int 21h 

    main endp

ends

end main

接下来是一个从键盘获取10个数字并显示平均值的程序。使用 EMU8086 制作并提供完整注释以帮助您理解。希望对你有帮助:

.stack 100h
;------------------------------------------
.data
;------------------------------------------
msj1  db 13,10,'Enter a number (4 digits): $'
str   db 6 ;MAX NUMBER OF CHARACTERS ALLOWED (5).
      db ? ;LENGTH (NUMBER OF CHARACTERS ENTERED BY USER).
      db 6 dup (?) ;CHARACTERS ENTERED BY USER. 
buf   db 6 dup('$') ;WILL HOLD NUMBERS WITH 5 DIGITS OR LESS.
suma  dw 0  ;SUMATORY OF 10 NUMBERS.
msj2  db 13,10,'Average : $'
count db ? ;JUST A COUNTER.
;------------------------------------------
.code          
;INITIALIZE DATA SEGMENT.
  mov  ax, @data
  mov  ds, ax
;------------------------------------------        
;CAPTURE 10 NUMBERS 
  call clear_screen
  mov  count, 10 ;WE WILL CAPTURE 10 NUMBERS.
ten_numbers:
;DISPLAY MESSAGE.
  mov  ah, 9
  mov  dx, offset msj1
  int  21h
;CAPTURE NUMBER AS STRING.
  mov  ah, 0Ah
  mov  dx, offset str
  int  21h
;CONVERT CAPTURED NUMBER FROM STRING TO NUMERIC.
  mov  si, offset str ;PARAMETER FOR STRING2NUMBER.
  call string2number ;NUMBER RETURNS IN BX. 
;ADD TO SUMATORY.
  add  suma, bx
;REPEAT PROCESS FOR NEXT NUMBER.
  dec  count
  jnz  ten_numbers  ;IF LAST OPERATION WAS NOT ZERO, JUMP
;------------------------------------------
;AVERAGE.
  mov  ax, suma
  mov  bl, 10
  div  bl  ;AX / BL  =  QUOTIENT:AL, REMAINDER=AH.
;CONVERT QUOTIENT INTO STRING TO DISPLAY.
  call dollars ;FILLS BUF WITH DOLLARS (REQUIRED TO DISPLAY).
  mov  ah, 0  ;CLEAR AH TO USE AX.
  call number2string ;CONVERT AX. STRING RETURNS IN "BUF".
;DISPLAY MESSAGE.
  mov  ah, 9
  mov  dx, offset msj2
  int  21h
;DISPLAY AVERAGE.
  mov  ah, 9
  mov  dx, offset buf
  int  21h
;------------------------------------------
;WAIT FOR ANY KEY.
  mov  ah,7
  int  21h
;------------------------------------------
;FINISH THE PROGRAM.
  mov  ax, 4c00h
  int  21h           

;------------------------------------------
;NUMBER TO CONVERT MUST ENTER IN AX.
;ALGORITHM : EXTRACT DIGITS ONE BY ONE, STORE
;THEM IN STACK, THEN EXTRACT THEM IN REVERSE
;ORDER TO CONSTRUCT STRING (BUF).
proc number2string
  mov  bx, 10 ;DIGITS ARE EXTRACTED DIVIDING BY 10.
  mov  cx, 0 ;COUNTER FOR EXTRACTED DIGITS.
cycle1:       
  mov  dx, 0 ;NECESSARY TO DIVIDE BY BX.
  div  bx ;DX:AX / 10 = AX:QUOTIENT DX:REMAINDER.
  push dx ;PRESERVE DIGIT EXTRACTED FOR LATER.
  inc  cx ;INCREASE COUNTER FOR EVERY DIGIT EXTRACTED.
  cmp  ax, 0  ;IF NUMBER IS
  jne  cycle1 ;NOT ZERO, LOOP. 
;NOW RETRIEVE PUSHED DIGITS.
  mov  si, offset buf
cycle2:  
  pop  dx        
  add  dl, 48 ;CONVERT DIGIT TO CHARACTER.
  mov  [ si ], dl
  inc  si
  loop cycle2  

  ret
endp  
;------------------------------------------
;CONVERT STRING TO NUMBER IN BX.
;SI MUST ENTER POINTING TO THE STRING.
proc string2number
;MAKE SI TO POINT TO THE LEAST SIGNIFICANT DIGIT.
  inc  si ;POINTS TO THE NUMBER OF CHARACTERS ENTERED.
  mov  cl, [ si ] ;NUMBER OF CHARACTERS ENTERED.                                         
  mov  ch, 0 ;CLEAR CH, NOW CX==CL.
  add  si, cx ;NOW SI POINTS TO LEAST SIGNIFICANT DIGIT.
;CONVERT STRING.
  mov  bx, 0
  mov  bp, 1 ;MULTIPLE OF 10 TO MULTIPLY EVERY DIGIT.
repeat:
;CONVERT CHARACTER.                    
  mov  al, [ si ] ;CHARACTER TO PROCESS.
  sub  al, 48 ;CONVERT ASCII CHARACTER TO DIGIT.
  mov  ah, 0 ;CLEAR AH, NOW AX==AL.
  mul  bp ;AX*BP = DX:AX.
  add  bx,ax ;ADD RESULT TO BX. 
;INCREASE MULTIPLE OF 10 (1, 10, 100...).
  mov  ax, bp
  mov  bp, 10
  mul  bp ;AX*10 = DX:AX.
  mov  bp, ax ;NEW MULTIPLE OF 10.  
;CHECK IF WE HAVE FINISHED.
  dec  si ;NEXT DIGIT TO PROCESS.
  loop repeat ;COUNTER CX-1, IF NOT ZERO, REPEAT.

  ret 
endp    
;------------------------------------------
;FILLS VARIABLE BUF WITH '$'.
;USED BEFORE CONVERT NUMBERS TO STRING, BECAUSE
;THESE STRINGS WILL BE DISPLAYED.
proc dollars                 
  mov  si, offset buf
  mov  cx, 6
six_dollars:      
  mov  bl, '$'
  mov  [ si ], bl
  inc  si
  loop six_dollars

  ret
endp  
;------------------------------------------
proc clear_screen
  mov  ah,0
  mov  al,3
  int  10H
  ret
endp

程序string2numbernumber2string 将在您以后的程序中非常有用。注意它们的工作方式:string2number 获取一个用 0AH 捕获的字符串,returns 它在 BX 中,number2string 从 AX 中获取数字和 returns 它在一个名为 "buff" 的变量中(用“$”填充)。

有什么问题尽管问。

stack segment
 dw   10 dup (0)
ends

这么小的堆栈是行不通的...

mov ax, stack
mov ss, ax

...当然除非你忘记设置 SP 寄存器,因为现在程序可以认为它有足够大的堆栈。

add sum, di
mov sum+2, 0
adc sum+2, 0

上面的代码片段显示了第一个问题。您不想连续将 sum 变量的高位字归零。只需删除 mov sum+2, 0 行。 sum 变量在数据段中,被编译器初始化为零。因此,从零开始不需要 运行 时间的努力。

mov dx, sum+2
mov ax, sum
mov bx, 10
div bx

mov ah, 09h
mov dx, offset msg2
int 21h

此代码片段显示了第二个问题。您设法计算出平均值并将其保存在 AX 寄存器中,但随后您通过调用 DOS 函数来显示一条消息来破坏 AX 寄存器!只需颠倒这些操作的顺序即可。所以先显示,再计算平均值。