0

足し算、引き算、割り算、掛け算の機能を持つ 8086 アセンブリ言語で 64 ビットの整数計算機を作りたいと思っています。

1押すと加算、 を押す2と減算などを行い、64 ビットの入力値を与えることができるように、メニューを提供する必要があります。

問題は、プログラミング中に 16 ビット レジスタ/メモリしか使用できないことです。したがって、問題は、16 ビット レジスタを使用して、コンソール ウィンドウで 64 ビットの入力値を与える方法です。

KIP.R.IRVINE アセンブリ リンク ライブラリを使用しています。

加算、減算、除算、乗算などの機能ごとに 16 ビットのレジスタ/オペランドを使用するようにコーディングする必要があります。16 + 16 + 16 + 16 = 64

私はアセンブリ言語を初めて使用します。電卓の 1 つの機能 (+、-、​​/、x) だけを作成するのに誰かが助けてくれれば、残りを作成できます。

4

2 に答える 2

3

この問題は、アセンブリ言語とはほとんど関係がありません。少なくとも、問題の大部分を占めています。これを C または好みの言語でプロトタイプ化し、それを別の言語 (この場合はアセンブリ) に変換するだけです。

999 + 1を追加したい場合は、小学校で覚えている場合

 999
   1 +
======

私たちはたくさんの「1つを運ぶ」ことをするつもりです

111
 999
   1 +
======
1000

小学校ではおそらく定義されていなかったのは、「持ち込み」と「持ち出し」です。1を次の列に運ぶことがキャリーアウトです。持ち越されたものを現在の列に追加すると、その同じ数がこの列のキャリーインと呼ばれるようになりました。

ab
 c
 d +
====
 e

b はキャリーイン、a はキャリーアウト、オペランドは c と d です。e は、その列の結果です。一度に 1 つの列を追加し、それを無限に繰り返すことだけを学ぶ必要があったため、無限に大きな数を追加できることを小学校で学びました。最初の列のキャリーインの上の元の問題は空白であり、暗黙のゼロであることに注意してください。0 プラス 9 プラス 1 は「0 が 1 を運ぶ」に等しく、0 は 9+1 の結果であり、1 はキャリーアウトです。

999 + 1 を実行しますが、一度に 2 つの列しか追加できないという人為的な制限を設けます。おそらく、紙は 2 つの数値に対して十分な幅しかありません (プロセッサには、幅に固定の制限があるレジスタがあります。あなたの場合は16ビット、または16列幅)。

答えは 4 つの列で次のようになることがわかっています。

1110
0999
0001 +
======
1000

一度に 2 つの列

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

最初にキャリーインのゼロを使用して最下位の列を実行します。次に、次の2つの列へのキャリーインとしてその使用から実行されるものは何でも、無限に大きな数に対してこれを無限に繰り返すことができます。

アセンブリ言語では、多くの場合 (常にではありません) ADD と ADC 命令があり、キャリーなしの加算とキャリーありの加算があります。通常の加算には暗黙のゼロキャリーインがあり、理想的にはいくつかのプロセッサステータスレジスタのどこかに。次に、キャリー付きの加算命令がある場合は、キャリー付きの加算で残りの高次加算を行います。キャリー付きの加算は、キャリーフラグをキャリーインとして使用し、キャリーアウトをキャリーフラグに入れるため、カスケードできます追加。

泥のように透明?

引き算、コンピューターには実際の引き算はありません。初級プログラミング クラスで学んだクレイジーな 2 の補数には理由があります。2 の補数を使用して数値を否定することは、「反転して 1 を加算する」ことでした。次に、加算演算について考えてみましょう。加算は、2 つの数値をキャリーインで加算します。2 番目のオペランドを逆にして、キャリーインに 1 を入れるとどうなるでしょうか? それは「反転して1を足す」ですよね?5 + invert(1) + 1 と同じ 5 + (-1) と同じ数学 5 - 1 を実行したい場合はどうなりますか? ADD ロジックに完全に適合します。

異なる命令セットは、減算操作のキャリーアウトビットで異なることを行います。基本的に加算器に入る途中で、減算は2番目のオペランドを反転し、キャリーインを反転し、通常の加算を行いますが、一部のプロセッサフ​​ァミリは反転します演算が減算の場合は実行ビット、そうでないものもあります。操作後にキャリービットをどうするかによって、それを理解する必要がある場合とそうでない場合があります(たとえば、キャリーが設定されている場合はジャンプ、キャリーが設定されていない場合はジャンプなどの条件分岐)、この場合、他のポスターが示したように、いくつかの指示セットには借用による減算があります。add の後に一連の addc 操作を実行して無限大の加算をカスケードするように、sub と sbb を使用して無限大の減算をカスケードできます。

乗算...幸いなことに、8086 ではこれが少し簡単になりますが、全体的なプロパティはどこでも同じです。x ビット幅の 2 つの数値を乗算する場合に (バイナリで) 数値を見ると、結果は理想的には 2*x 幅である必要があるため、2 つの 16 ビット数値を適切に乗算するには、32 ビットの結果が必要です。ハードウェアが 16 ビット * 16 ビット = 16 ビットしか実行できない場合、その乗算器を簡単にカスケードして上位 8 ビットをゼロにし、8 ビット * 8 ビット = 16 ビットを実行するふりをします...

小学校

 abcd
 efgh *
======

最終的に合計した 4 つの数字になりましたよね? hhhh を使用して abcd*h を表し、gggg を使用して abcd*g などを表しています。

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

2 列の乗算を行った場合でも

 cd
 gh *
====

それは4つの乗算ステップに分割されました

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 を掛けると 1 列シフトするだけで、100 を掛けると 2 列になり、これらすべての項目が加算されます。したがって、乗算オペランドを消化可能な部分に分割し、各項目をシフトする必要がある量を追跡すると、基本的にその項目が特定の列に配置されます。無限に広い加算を使用して、既に項目を列またはグループに分割しています列。はい、上記のように、追加するものが 4 つあり、カスケード加算では 2 つしか追加されないため、3 つの広い加算を行う必要があるため、1) 最初の 2 つのオペランド、2) 1 の結果、および 3 番目のオペランド 3 の結果として、インタラクティブなプロセスになります。 ) 2) の結果と最後のオペランド

これらの文字はそれぞれ 16 ビットの数字であるとします

abcd
efgh *
======

上記の唯一の乗算ペアは、それに関連付けられた 16 の倍数によるシフトの風味を持たない (10 進数で 10 または 100 または 1000 を乗算すると考えてください) h*d です。

したがって、結果の下位 16 ビットは、h*d の下位 16 ビットです。ただし、h*d の上位 16 ビットは他のものに追加する必要があります

次のレイヤーは h*c<<16 および g*d<<16 です。それぞれの下位 16 ビットが加算され、d*h の上位 16 ビットにも加算されます。2文字の組み合わせを使用して乗算結果を表す

つまり abcd * efgh =

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

上記の hd として表される h*d の下位 16 ビットは、0 だけに追加され、結果に直接入ります h*d の上半分と h*c および g*d の下半分が追加されて、次のビットになります結果の 16 ビットなど。

16*16=32 ビットの乗算演算と 16*16=16+キャリー加算演算を使用して 2 つの 64 ビット数を乗算する場合は、上記に基づいてハードコーディングできます。

結果を 64*64=128 ビットではなく 64*64=64 ビットに制限したい場合は、それを半分にカットできます。

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 は、たとえば、減算でキャリーアウトを反転する場合があり、おそらくアームはそうではありません。一部のプロセッサはフラグをまったく持たず (ミップ)、レジスタ サイズよりも小さい数値を使用して上記のすべてを合成する必要があります (32 ビット レジスタを使用して 16 ビットの加算を行い、17 番目のビットを保存する場所を確保します。一度に 31 ビット、なんでも)。一部のプロセッサと同様に、入力で乗算の半分のサイズ (上半分をゼロ) を使用して結果のクリップされていない答えを取得し、上記のゲームを実行して全幅の乗算を適切に実行する必要があります。

于 2013-04-10T19:26:24.153 に答える
1

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
于 2013-04-10T15:34:25.060 に答える