assembly 汇编语言8086如何制作64位整数计算器?

cedebl8k  于 2023-05-23  发布在  其他
关注(0)|答案(2)|浏览(212)

我想用8086汇编语言制作一个64位整数计算器,它具有以下功能:加,减,除,乘。
它应该提供一个菜单,这样如果我们按1,它会增加,如果我们按2,它会减少等。我们可以给予64位的输入值。
问题是我们在编程时只能使用16位寄存器/内存,所以问题是我们如何使用16位寄存器并在控制台窗口中给予64位输入值。
我正在使用KIP.R.IRVINE汇编链接库。
我们必须以这样的方式编码它,它使用16位寄存器/操作数来实现每个功能,如加法,减法,除法和乘法。16 + 16 + 16 + 16 = 64
我是新的汇编语言,如果任何人可以帮助我,使只有1个功能(+,-,/,x)的计算器,我可以使它的其余部分。

ycl3bljg

ycl3bljg1#

这个问题与汇编语言没有什么关系,或者至少是最大的问题。你应该用C或任何你喜欢的语言来制作原型,然后简单地将其转换为另一种语言(在这种情况下是汇编)。
如果你还记得在小学的时候如果我们想加999 + 1

999
   1 +
======

我们会有很多“挑一个”的活动

111
 999
   1 +
======
1000

现在,在小学里可能没有定义的是“携带”和“携带”。把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 + inverse(1)+ 1是一样的,对吗?完全符合ADD逻辑。
不同的指令集利用用于减法运算的进位输出位做不同的事情,基本上在进入加法器的途中,减法装置反转第二操作数并反转进位输入,然后进行正常的加法,在输出途中,虽然如果运算是减法,一些处理器系列反转进位输出位,但是一些不反转进位输出位。你可能需要也可能不需要弄清楚这一点,这取决于你在操作后对进位位做了什么(条件分支,例如如果进位设置则跳转,如果进位未设置则跳转),在这种情况下,如另一张海报所示,一些指令集有一个带借用的减法。就像add then一系列addc操作级联一个无限大的加法一样,你可以使用sub和sbb级联一个无限大的减法。
乘...幸运的是,8086使这变得容易一点,但整体属性是相同的任何地方。如果你看数字(二进制),如果你乘以两个x位宽的数字,结果理想地需要是2*x宽,所以要正确地乘以任何两个16位数字,你需要一个32位的结果。如果您硬件只能执行16位 * 16位= 16位,那么为了轻松地级联乘法器,将高8位归零,并假装执行8位 * 8位= 16位...
小学

abcd
 efgh *
======

我们最后得到了四个数字加在一起,对吗?我用hhhh表示abcdh,gggg表示abcdg,等等。

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的倍数移位的乘法对是hd(想想十进制中的10或100或1000的乘法)。
因此结果的低16位是h
d的低16位。但是必须将hd的高16位添加到其它填充中
下一层是h
c ≤ 16和gd ≤ 16。将这些中的每一个的低16位加在一起,并且还将其加到DH的高16位。用两个字母的组合来表示它们的相乘结果
换句话说,abcd * efgh =

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

将上面表示为hd的hd的低16位加到零上,并直接进入结果,将hd的上半部分与hc和gd的下半部分相加,以成为结果的下一个16位,等等。
如果你想使用1616=32位乘法运算和1616=16+进位加法运算来乘两个64位数,你可以基于上面的内容进行硬编码。
如果您想将结果限制为6464= 64位而不是6464= 128位,则可以将其减半

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位,无论如何)来合成所有上述内容。就像一些处理器一样,你必须在输入上使用乘法的一半大小(上半部分为零)来获得结果的未裁剪答案,并执行上述游戏来正确地执行全宽度乘法。

dm7nw8vv

dm7nw8vv2#

要使用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
    mov bx, 0        ; Must not clobber CF here!

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

使用MASM语法的较短版本:

mov  di, OFFSET operand1  ;  *di -= *si
    mov  si, OFFSET operand2
    mov  cx, 4
    xor  bx, bx               ; This clears CF
a:  mov  ax, [si+bx] 
    sbb  [di+bx], ax
    lea  bx, [bx+2]           ; this does not affect carry flag
    loop a                    ; this does not affect carry flag

如果我们用inc bxinc bx替换lea bx, [bx+2],仍然会少1个字节。

相关问题