Assembly 打印整数的平方和(TASM)
我试图解决汇编(TASM)中的一个简单任务,即: 单词中有一个带范围的自然数,用这个数字确定二级数字的和 我想将添加Assembly 打印整数的平方和(TASM),assembly,x86-16,tasm,Assembly,X86 16,Tasm,我试图解决汇编(TASM)中的一个简单任务,即: 单词中有一个带范围的自然数,用这个数字确定二级数字的和 我想将添加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 6131
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,然后返回:并结束。缺少计算数字平方的代码。创建一个子过程,该子过程以
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
但在此之前
添加dx,30小时;Пибббббббббббббб
您需要做的最后一件事是以十进制表示法显示二进制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
,例如,从末尾开始转换为缓冲区,然后进行一次打印系统调用。(). 我不推荐你在问题中展示的那种笨重的推送/弹出2循环方法,但它很流行而且确实有效。无论如何,mov ax,cx
会将总和放入ax
您甚至可以通过使用一个10除1推的循环来获得一些代码重用,就像您在中所做的那样。第一次,用它来获得你弹出的数字,然后平方->求和。第二次,使用它生成总和的数字,然后弹出并打印。(但是编写一个在堆栈上留下可变数量内容的函数是一件棘手的事情;您可以在函数的开头弹出返回地址,然后按下/返回。或者只是将其设置为一个使用两次的宏,以便将两个位置都内联。)
如果您想与8086兼容,但要针对更新的Intel CPU进行调整(其中,因此成本与3 mov指令大致相同):您可以使用mov-si,ax
/mov-ax,dx
,然后是mul/add,然后是mov-ax,si
,而不是xchg-ax,dx
。对于实际的远古8086,xchg
非常棒:更小的指令速度更快(除了像mul和div这样非常慢的指令),而xchg
-与ax相比只有1个字节
当然,如果你真的关心速度,你会用它除以10。对于实际的8086,其中mul
非常慢(但没有div
慢),您可以使用正方形查找表在该部分保存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]
或者只使用一个字节表,用1字节的额外代码大小交换较小的表和1字节较少的加载数据(8088上的盈亏平衡,预取差异除外):
重复除以10一次只提取数字1(与dx中的余数相同),最不重要的第一位。加法是关联的,所以这很好;推送和分离的弹出循环部分是不必要的。为它制作一个函数似乎过于复杂了。特别是如果你有一个386兼容的,imul-dx,dx
是很容易的,但是即使没有它,一对mov
指令和一个备用寄存器(或者它周围的xchg-ax,dx
)也能让你mulal
(在不接触dx的情况下产生ax)/添加cx,ax
。为什么要在内存中保留[sum]
?x86有足够的寄存器用于此操作。您甚至可以使用mov-si,dx
/shl-si,1
/add cx、[table+bx]
为正方形查找表编制索引,这在真正的8086上会更快。@PeterCordes的确,为每个基本任务创建单独的函数是无效的,但它有利于理解和调试。对于asm的初学者来说,在他们的第一堂课中不应该被优化策略所困扰。优化是写作/思考asm并发布答案的有趣之处。考虑优化甚至有助于理解编译器为何以这种方式生成代码。此外,更少的指令通常更容易理解——一些优化将代码简化为有趣的部分,去除了噪音。(然而,与其他观点不同,查表的想法不属于这一类。我只是想把这个想法写在某个地方。我想我应该把它放在我的答案中,因为我决定写一个。)我觉得很尴尬,但是你能写一个完整的程序来输出这个数量吗?因为我的老师给了我在DOs中输出数字的代码,而没有解释它是如何工作的,我对如何将我的代码与您的代码结合起来感到困惑solutions@Dsyder:不,谢谢,我对帮你做完整的家庭作业不感兴趣。此外,在AX中输入一个数字后,您实际上已经有了可以输出数字的代码。如果你想理解重复除以10的事情,请仔细阅读我的答案和/或思考会发生什么。还详细介绍了数学知识。非常感谢,我能够将你的答案和我的代码结合起来,结果很好!不幸的是,我无法推翻你的答案:(但我非常感谢你
mov si, dx
add cl, [table + si]
adc ch, 0 ; carry to the high half of CX