打印整数的平方数字之和 (TASM)

Print sum of squared digits of an integer (TASM)

我正在尝试解决一个简单的装配任务 (TASM),即:

There is a natural number with a range in the word, determine the sum of digits in the second degree by this number.

我想把6^2 + 1^2 + 3^2 + 1^2 + 3^2.

相加的结果输出到DOS

下面的代码在DOS下只能输出一个数字,不能再多了,老师给的。

;stack segments
stk segment stack 
    db  128 dup(?)
stk ends

;data segment 
data    segment para public 'data'
x   dw  61313
DThousands  dw  ?
Thousands   dw  ?
Hundreds    dw  ?
Decades     dw  ?
Units       dw  ?
result      dw  ?
data    ends    

;command segment 
code    segment para public 'code'      
    assume  cs:code, ds:data, ss:stk
begin:  
    mov ax, data
    mov ds, ax
    mov ax, x ; заносим число x в регистр ax
    mov result, ax ; заносим в зарезервированный участок памяти result значение из ax
    mov     ax, result ; меняем значение
    xor cx, cx  ;MOV CX, 0
    mov bx, 10 ; bx = 10
m_do_while:
    xor dx, dx ; обнуление dx
    div bx ; деление ax на bx
    push    dx ; заталкиваем dx в стек
    inc cx ; увеличиваем cx на 1
    cmp ax, 0 ; сравниваем регистр ax с нулем
    jne m_do_while ; выполняем условный переход
    mov ah, 2 ; помещаем в регистр ah 2
m_for:
    pop dx ; достаем из стека значение dx
    add dx, 30h ; прибавляем к dx 30h
    int 21h ; системное прерывание 
    loop    m_for ; цикл
back:
;end of program
    mov ax, 4C00h
    int 21h
code    ends
    end begin   

首先你必须将该数字除以 10000,如果它 > 9999,它将是你的第一个字符,接下来你将结果除以任何基址寄存器并找到下一个字符。

您的程序从 x 中获取二进制数,以十进制格式正确显示它:61313 然后转到 back: 并结束。缺少计算数字平方的代码。
创建一个子过程,将 dx 中的二进制数字作为其输入,将其与自身相乘并将平方累加到 sum 中。子过程不应破坏任何寄存器:

SquareDX:     ; Let [sum] += DX*DX
     PUSH AX  
     PUSH DX
       MOV AX,DX
       MUL DX
       ADD [sum],AX   
     POP DX
     POP AX
     RET


之后调用子过程 pop dx ; достаем из стека значение dx
但之前
add dx, 30h ; прибавляем к dx 30h

您需要做的最后一件事是以十进制表示法显示二进制 sum,与您显示二进制 x.

完全相同

您已经使用该打印循环一次获取一个数字。加法是关联的,所以无论你以什么顺序得到它们,你都可以从最低有效数字开始添加。

digit_sum:
    mov   ax, x       ; input in AX
    mov   bx, 10      ; base 10
    xor   cx, cx      ; sum
.sumloop:
    xor   dx, dx
    div   bx          ; quotient in AX,  remainder (the digit) in DX

  ;; With 386
    ;imul  dx, dx      ; requires 386
    ;add   cx, dx      ; sum += digit^2

  ;; Without 386
    xchg   ax, dx
    mul    al          ; result in AX.  DX untouched.  single-digit numbers fit in AL
    add    cx, ax      ; sum += digit^2
    mov    ax, dx

    test  ax, ax
    jne  .sumloop

;;; sum in CX
    ret

然后高效地打印cx,例如通过从末尾开始转换为缓冲区,然后进行一次打印系统调用。 (How do I print an integer in Assembly Level Programming without printf from the c library?)。我不推荐您在问题中展示的那种笨拙的 push/pop 2 循环方法,但它很受欢迎并且确实有效。无论如何,mov ax, cx 会将总和放入 AX。

您甚至可以通过使用 divide-by-10-and-push 循环来实现一些代码重用,就像在 中那样。第一次,用它来获取你弹出和平方的数字 - >总和。第二次,用它来生成总和的数字,然后弹出并打印。 (但是编写一个在堆栈上留下可变数量的东西的函数是很棘手的;你可以在函数的开头弹出 return 地址,然后是 push/ret。或者只是让它成为一个宏,你使用两次,所以它内联了两个地方。)


如果你想与 8086 兼容,但要针对更新的 Intel CPU 进行调整(其中 因此成本与 3 个 mov 指令大致相同):你可以使用 [=16= 而不是 xchg ax,dx =17=] / mov ax, dx,然后是 mul/add,然后是 mov ax, si。对于实际的古代 8086,xchg 很棒:越小越快(除了像 mul 和 div 这样非常慢的指令)并且 xchg-with-ax 只有 1 个字节。

当然,如果你真的关心速度,你会使用 a multiplicative inverse 到 divide 10。对于实际的 8086,其中 mul 非常慢(但不像 div) 慢,您可以使用正方形的查找-table 来保存该部分的 mul:

    ; given a digit in DX, add its square to CX, indexing a table of words
    mov si, dx
    shl si,1
    add cx, [table + si]

或者仅使用 table 字节,用 1 字节的额外代码大小换取更小的 table 和少 1 字节的数据加载(在 8088 上达到收支平衡,预取差异除外):

    mov si, dx
    add cl, [table + si]
    adc ch, 0              ; carry to the high half of CX