【问题标题】:assembly Language 8086 How to make 64bit integer Calculator?汇编语言 8086 如何制作 64 位整数计算器?
【发布时间】:2018-11-02 23:07:41
【问题描述】:

我想用 8086 汇编语言制作一个 64 位整数计算器,它具有以下功能:加法、减法、除法、乘法。

它应该提供一个菜单,这样如果我们按下1,它就会相加,如果我们按下2,它就会相减,等等,我们可以给出一个 64 位的输入值。

问题是我们在编程时只能使用 16 位寄存器/内存,所以问题是我们如何使用 16 位寄存器并在控制台窗口中给出 64 位输入值。

我正在使用 KIP.R.IRVINE 程序集链接库。

我们必须对其进行编码,使其对每个功能(如加法、减法、除法和乘法)使用 16 位寄存器/操作数。 16 + 16 + 16 + 16 = 64

我是汇编语言的新手,如果有人可以帮助我只制作计算器的 1 个功能(+、-、/、x),我可以完成其余的。

【问题讨论】:

  • x86 指令集,包括基本算术运算的指令,is well documented。如果您不确定在哪种情况下使用哪些说明,我建议您阅读文档。
  • 谢谢迈克尔..我一定会读到的..

标签: assembly x86 masm x86-16


【解决方案1】:

这个问题与汇编语言几乎没有关系,或者至少与这个问题有很大关系。您应该用 C 或任何您喜欢的语言对其进行原型制作,然后简单地将其转换为另一种语言(在本例中为汇编)。

如果你记得在小学时我们要加 999 + 1

 999
   1 +
======

我们将进行很多“随身携带”

111
 999
   1 +
======
1000

现在小学里可能没有定义的是“carry in”和“carry out”。您将 1 带到下一列的东西就是执行。当您将结转的内容添加到当前列中时,对于此列,现在将相同的数字称为进位。

ab
 c
 d +
====
 e

b 是进位,a 是进位,操作数是 c 和 d。 e 是该列的结果。我们确实在小学学到了我们可以添加无限大的数字,因为我们只需要学习一次添加一列并无限重复。请注意,第一列进位上方的原始问题是空白的,它是隐含的零。 0加9加1等于“0进1”,0是9+1的结果,1是进位。

所以现在做 999 + 1 但人为限制我们一次只能添加两列,也许我们的论文只够两个数字宽,谁知道(处理器有寄存器,在宽度,在您的情况下为 16 位或 16 列宽)。

我们知道答案看起来像这样,有四列,

1110
0999
0001 +
======
1000

一次两列

11    10
09    99
00 +  01 +
====  ====
10    00

首先对最不重要的列使用零作为进位。然后将该使用的任何执行作为下两列的进位,我们可以对无限大的数字无限重复此操作。

在汇编语言中,您经常(但并非总是)有一个 ADD 和一个某种风格的 ADC 指令,无进位相加和有进位相加,正常的加法有一个隐含的零进位,理想情况下有一个进位的进位某个处理器状态寄存器中的标志。那么如果有一个带进位的加法指令,你可以用带进位的加法进行其余的高阶加法,带进位的加法使用进位标志作为进位,并将进位放在进位标志中,所以你可以级联补充。

一清二楚?

减法,计算机中没有真正的减法,你在初学者编程课上学到的疯狂的二进制补码是有原因的。使用二进制补码对数字求反是“反转并加 1”。现在考虑我们的加法运算,加法将两个数字相加并带有进位。如果您要反转第二个操作数,然后将 1 放入进位中怎么办?那是“反转并加1”,对吗?如果我想做与 5 + (-1) 相同的数学 5 - 1 与 5 + invert(1) + 1 相同,对吗?完全符合 ADD 逻辑。

不同的指令集对减法运算的进位位做不同的事情,基本上是在进入加法器的途中,减法意味着反转第二个操作数并反转进位,然后进行正常的加法,虽然有些如果操作是减法,处理器系列会反转执行位,有些则不会。您可能需要也可能不需要根据您在操作后对进位位所做的操作(条件分支,例如如果进位设置跳转,如果进位未设置跳转),在这种情况下,如另一张海报所示,一些指令集合有借位减法。就像 add then 一系列 addc 操作级联一个无限大的加法一样,您可以使用 sub 和 sbb 级联一个无限大的减法。

乘以...幸运的是 8086 使这更容易一些,但整体属性在任何地方都是相同的。如果您查看数字(二进制​​),如果您将两个 x 位宽的数字相乘,理想情况下结果需要是 2*x 宽,因此要正确地将任何两个 16 位数字相乘,您需要一个 32 位的结果。如果你的硬件只能做 16bit * 16bit = 16bit 那么轻松级联乘数为零的高 8 位并假装做 8bit * 8bit = 16bit...

小学

 abcd
 efgh *
======

我们最终得到了四个相加的数字,对吗?我用hhhh代表abcd*h,gggg代表abcd*g等

   abcd
   efgh *
======= 
   hhhh
  gggg
 ffff
eeee    +
==========

当我们做了两列乘法时

 cd
 gh *
====

分为四个乘法步骤

h * d
h * (c*10)
(g*10) * d
(g*10) * (d*10)

使用数学属性是

h * d = h*d
h * (c*10) = 10 * (h*c)
(g*10) * d = 10 * (g*d)
(g*10) * (d*10) = 100 * (g*d)

以 10 为底乘以 10 只是移动一列,乘以 100 是两列,所有这些项目都相加。因此,如果您将乘法操作数分解为可消化的部分并跟踪每个项目需要移动多少,这基本上将该项目放在特定列中,使用我们无限宽的加法,我们已经将内容分解为列或组列。是的,它变成了一个交互过程,如上所述,我们有四件事要添加,我们的级联加法只添加两件事,所以你必须做 3 个宽加法,1)前两个操作数,2)1)的结果加上第三个操作数 3 ) 2) 的结果加上最后一个操作数

假设这些字母中的每一个都是一个 16 位数字

abcd
efgh *
======

上面唯一一个没有与它相关联的 16 倍数移位的乘法对是 h*d。

所以结果的低 16 位是 h*d 的低 16 位。不过,h*d 的高 16 位必须添加到其他内容中

下一层是 h*c

也就是说 abcd * efgh =

000000hd
00000hc0
0000hb00
000ha000
00000gd0
0000gc00
000gb000
00ga0000
0000fd00
000fc000
00fb0000
0fa00000
000ed000
00ec0000
0eb00000
ea000000 +
==========

h*d 的低 16 位(上面表示为 hd)被加到零并直接进入结果 h*d 的上半部分和 h*c 和 g*d 的下半部分被添加到成为结果的下 16 位,依此类推。

如果您想使用 16*16=32 位乘法运算和 16*16=16+进位加法运算将两个 64 位数字相乘,您可以根据上述进行硬编码。

如果您想将结果限制为 64*64=64bits 而不是 64*64=128bits,您可以将其减半

00hd
0hc0
hb00
a000 (h*a)
0gd0
gc00
b000 (g*b)
fd00
c000 (f*c)
d000 + (e*d)
======

我把分工留给你自己琢磨……

首先使用基本操作加减法在高级语言中实现这一点,合成进位

添加函数

unsigned int a,b,c,result,carry;

//addition
a = operand1&0xFFFF;
b = operand2&0xFFFF;
c = a+b;
result = c&0xFFFF;
carry = (c>>16)&1;

还有一个带进位功能的加法

//add with carry 
a=(operand1>>16)&0xFFFF;
b=(operand2>>16)&0xFFFF;
c = a+b+carry_in;
result = c&0xFFFF;
carry = (c>>16)+1;

或者如果你想像硬件一样只做带进位功能的加法,并且在加法步骤中输入 0 表示进位,否则输入进位。

一清二楚?

例如,x86 可能会反转减法的执行,而 arm 可能不会。有些处理器根本没有标志(mips),您必须使用小于寄存器大小的数字来合成上述所有内容(使用 32 位寄存器进行 16 位加法,从而允许保存第 17 位、执行或可能做一次 31 位,无论如何)。就像某些处理器一样,您必须在输入上使用乘法大小的一半(上半部分为零)才能得到结果的未裁剪答案,并执行上述游戏以正确执行全宽度乘法。

【讨论】:

  • 详述我们如何在控制台窗口中提供输入值...在像 c# 这样的高级语言中它很容易..但是在汇编语言中对我来说很难...
  • int 21h 或任何调用或 bios 调用(如果支持)至少有一个具有简单字符输出。你不会自己做这个,这是一个给定的(比上面的简单数学讨论要长得多),所以你要么需要调用一个 C 或其他链接的库,要么进行操作系统调用。从爆破字符开始,然后阅读字符并将它们回显,然后围绕该知识创建一个用户界面,然后解析参数,然后进行上面的数学运算……如果是我,我仍然会首先在 C 中建模大部分...涵盖算法工作
  • 我一定会的,谢谢 dwelch :)
  • 从字面上看,我读过的最详细、最全面的解释。干得好。
【解决方案2】:

要使用 16 位寄存器减去 2 个 64 位值,可以:

lea di, [operand1]  ;;  *di -= *si
lea si, [operand2]

    mov ax, [si]     // subtract initially first 16 bits
    sub [di], ax
    mov cx, 3        // setup loop counter
    xor bx, bx

a:  lea bx, [bx+2]    // advance the pointers without affecting status register
    mov ax, [si+bx]   // subtract 
    sbb [di+bx], ax

    loop a           // this does not affect carry flag

【讨论】:

  • 您必须根据需要进行修改。 (并且要么删除 c 样式的注释内容,要么将它们替换为您的汇编程序支持的 cmets。注释的符号可能是一个冒号 ;。很抱歉给您带来不便。
  • 我选择了带有进位的减法作为基本构建块,因为+ 操作可以很容易地实现,如果理解这些概念(以及进位的原理)。此外,在仅 64 位除法的情况下,64 试 减法 可能是尝试的方法。类似地,shift and add 可能是 64 位数字相乘最方便的算法。将乘法拆分为 4x4 16 位乘法在 IMO 中是多余的,更不用说 Karatsuba 或其他减少基本操作数量的方法了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-27
  • 1970-01-01
  • 1970-01-01
  • 2020-06-07
  • 2010-10-09
  • 1970-01-01
  • 2014-07-15
相关资源
最近更新 更多