打印整数的平方数字之和 (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
我正在尝试解决一个简单的装配任务 (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下只能输出一个数字,不能再多了,老师给的。
;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 循环来实现一些代码重用,就像在
如果你想与 8086 兼容,但要针对更新的 Intel CPU 进行调整(其中 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