16

The Elements of Computing Systems を読んで勉強していますが、ある時点で立ち往生しています。次の 5 つの命令をスキップする章のサンプルは、ここにあります。

とにかく、私は仮想マシン (またはアセンブリ トランスレータへのバイト コード) を実装しようとしていますが、次の 5 つの命令を 1 点スキップすることで立ち往生しています。

アセンブリ表記については、こちらを参照してください。

目標は、特定のバイト コードをこのアセンブリ コードに変換するトランスレータを実装することです。

私が成功した例は、バイトコードです

push constant 5

これは次のように翻訳されます。

@5
D=A
@256
M=D

私が言ったように、Hack のアセンブリ言語は私が提供したリンクにありますが、基本的には次のとおりです。

@5  // Load constant 5 to Register A
D=A // Assign the value in Reg A to Reg D
@256// Load constant 256 to Register A
M=D // Store the value found in Register D to Memory Location[A]

さて、これはかなり簡単でした。定義により、メモリ位置 256 はスタックのトップです。そう

push constant 5
push constant 98

に翻訳されます:

@5
D=A
@256
M=D
@98
D=A
@257
M=D

これはすべて問題ありません..

また、もう 1 つ例を挙げたいと思います。

push constant 5
push constant 98
add

は次のように翻訳されます。

@5
D=A
@256
M=D
@98
D=A
@257
M=D
@257  // Here starts the translation for 'add' // Load top of stack to A
D=M   // D = M[A] 
@256  // Load top of stack to A 
A=M   // A = M[A]
D=D+A
@256
M=D

かなり明確だと思います。

ただし、バイトコードをどのように変換できるかわかりません

eq

アセンブリへ。eq の定義は次のとおりです。

コマンドのうち 3 つ (eq、gt、lt) はブール値を返します。VM は、true と false をそれぞれ -1 (マイナス 1、0xFFFF) と 0 (ゼロ、0x0000) として表します。

したがって、レジスタ A と D にそれぞれ 2 つの値をポップする必要がありますが、これは非常に簡単です。しかし、値をチェックして結果が true の場合は 1 をプッシュし、結果が false の場合は 0 をプッシュするアセンブリ コードを作成するにはどうすればよいでしょうか?

Hack Computer でサポートされているアセンブリ コードは次のとおりです。

ここに画像の説明を入力 ここに画像の説明を入力 ここに画像の説明を入力

私は次のようなことができます:

push constant 5
push constant 6
sub

スタックにプッシュされた 2 つの値が等しい場合は値 0 を保持し、そうでない場合は !0 を保持しますが、それはどのように役立ちますか? D&AまたはD&Mを使用してみましたが、どちらもあまり役に立ちませんでした..

条件付きジャンプを導入することもできますが、ジャンプ先の命令をどのように知る必要があるのでしょうか? Hack Assembly コードには、「次の 5 つの命令をスキップする」などのようなものはありません。

[Spektre による編集] 私が見たターゲット プラットフォームの概要

  • 16 ビット フォン ノイマン アーキテクチャ (アドレスは 15 ビット、16 ビット ワード アクセス)
  • データメモリ 32KW (読み取り/書き込み)
  • 命令 (プログラム) メモリ 32KW (読み取り専用)
  • ネイティブ 16 ビット レジスタ A、D
  • 0x0000 ~ 0x000F のデータ メモリにマッピングされた汎用 16 ビット レジスタ R0 ~ R15
  • これらは、次の目的にも使用される可能性が最も高いです。SP(R0),LCL(R1),ARG(R2),This(R3),That(R4)
  • 画面は 0x4000-0x5FFF (512x256 B/W ピクセル 8KW) のデータ メモリにマップされます。
  • キーボードは 0x6000 のデータ メモリにマップされます (最後にキーを押した場合は ASCII コード?)

ここに画像の説明を入力

4

2 に答える 2

10

Hack CPU をより明確に定義する別の章があるようです。それは言います:

ハックCPUは、第2章で規定したALUと、データレジスタ(D)、アドレスレジスタ(A)、プログラムカウンタ(PC)と呼ばれる3つのレジスタから構成されています。D と A は、第 4 章で指定された Hack マシン語に従って、 A=D-1 、 D=D|A などの算術および論理命令によって操作できる汎用 16 ビット レジスタです。 -レジスタはデータ値を格納するためだけに使用され、A レジスタの内容は、命令のコンテキストに応じて、データ値、RAM アドレス、または ROM アドレスの 3 つの異なる方法で解釈できます。

したがって、どうやら「M」アクセスは、A によって制御される RAM ロケーションへ のものです。私が見逃していた間接アドレス指定があります。 これですべてがクリックされます。

その混乱が解消されたので、OPの質問を処理できるようになりました(はるかに簡単です)。

スタックを使用したサブルーチン呼び出しの実装から始めましょう。

     ; subroutine calling sequence
     @returnaddress   ; sets the A register
     D=A
     @subroutine
     0 ; jmp
  returnaddress:

     ...

  subroutine: ; D contains return address
  ; all parameters must be passed in memory locations, e.g, R1-R15
  ; ***** subroutine entry code *****
     @STK
     AM=M+1         ; bump stack pointer; also set A to new SP value
     M=D            ; write the return address into the stack
  ; **** subroutine entry code end ***
     <do subroutine work using any or all registers>
  ; **** subroutine exit code ****
     @STK
     AM=M-1         ; move stack pointer back
     A=M            ; fetch entry from stack
     0; jmp         ; jmp to return address
  ; **** subroutine exit code end ****

「プッシュ定数」命令は、スタック内の動的な場所に格納するように簡単に変換できます。

     @<constant>  ; sets A register
     D=A         ; save the constant someplace safe
     @STK
     AM=M+1         ; bump stack pointer; also set A to new SP value
     M=D            ; write the constant into the stack

定数をプッシュするサブルーチンを作成したい場合:

   pushR2: ; value to push in R2
     @R15           ; save return address in R15
     M=D            ; we can't really use the stack,...
     @R2            ; because we are pushing on it
     D=M
     @STK
     AM=M+1         ; bump stack pointer; also set A to new SP value
     M=D            ; write the return address into the stack
     @R15
     A=M
     0 ; jmp

そして、「プッシュ定数」ルーチンを呼び出すには:

     @<constant>
     D=A
     @R2
     M=D
     @returnaddress   ; sets the A register
     D=A
     @pushR2
     0 ; jmp
  returnaddress:

変数値 X をプッシュするには:

     @X
     D=M
     @R2
     M=D
     @returnaddress   ; sets the A register
     D=A
     @pushR2
     0 ; jmp
  returnaddress:

スタックから D レジスタに値をポップするサブルーチン:

   popD:
     @R15           ; save return address in R15
     M=D            ; we can't really use the stack,...
     @STK
     AM=M-1         ; decrement stack pointer; also set A to new SP value
     D=M            ; fetch the popped value
     @R15
     A=M
     0 ; jmp

ここで、OP の最初の要求であった「EQ」計算を実行します。

EQ: ; compare values on top of stack, return boolean in D
      @R15         ; save return address
      M=D
      @EQReturn1
      D=A
      @PopD
      0; jmp
@EQReturn1:
      @R2
      M=D        ; save first popped value
      @EQReturn2
      D=A
      @PopD
      0; jmp
@EQReturn2:
      ; here D has 2nd popped value, R2 has first
      @R2
      D=D-M
      @EQDone
      equal; jmp
      @AddressOfXFFFF
      D=M
EQDone: ; D contains 0 or FFFF here
      @R15
      A=M         ; fetch return address
      0; jmp

すべてを一緒に入れて:

     @5           ; push constant 5
     D=A
     @R2
     M=D
     @returnaddress1
     D=A
     @pushR2
     0 ; jmp
  returnaddress1:

     @X                ; now push X
     D=M
     @R2
     M=D
     @returnaddress2 
     D=A
     @pushR2
     0 ; jmp
  returnaddress2:

     @returnaddress3   ; pop and compare the values
     D=A
     @EQ
     0 ; jmp
  returnaddress3:

この時点で、OP は D をスタックにプッシュするコードを生成できます。

     @R2                ; push D onto stack
     M=D
     @returnaddress4 
     D=A
     @pushR2
     0 ; jmp
  returnaddress4:

または、D の値で分岐するコードを生成できます。

     @jmptarget
     EQ ; jmp
于 2015-05-16T10:39:16.960 に答える
1

最後のコメントで書いたように、分岐の少ない方法があるため、オペランドからの戻り値を直接計算する必要があります

eq今のような簡単な操作をしましょう

  • 私がそれを正しくしたら、eq a,d次のようなものですa=(a==d)
  • true0xFFFFと false は0x0000
  • したがって、この場合a==da-d==0これを直接使用できます

    1. 計算するa=a-d
    2. ORのすべてのビットのカスケードを計算するa

      • 結果が 0 の場合は 0 を返す
      • 結果が 1 の場合は 0xFFFF を返す
      • これは、テーブルまたは0-OR_Cascade(a)
    3. ORカスケード_

      • あなたの説明にはビットシフト操作が見当たりません
      • a+aそのため、代わりに使用する必要がありますa<<1
      • 右シフトが必要な場合は、2 除算を実装する必要があります。

したがって、これを要約すると、次eq a,dのようになります。

  • a=a-d;
  • a=(a|(a>>1)|(a>>2)|...|(a>>15))&1
  • a=0-a;
  • これをアセンブリにエンコードするだけです
  • 分割またはシフトが直接サポートされていないため、これの方が良い場合があります
  • a=a-d;
  • a=(a|(a<<1)|(a<<2)|...|(a<<15))&0x8000
  • a=0-(a>>15);

より低い比較とより大きな比較ははるかに複雑です

  • 減算のキャリーフラグを計算する必要があります
  • または結果の符号を使用 (結果の MSB)
  • オペランドを 15 ビットに制限すると、それはちょうど 15 番目のビットになります。
  • 完全な 16 ビット オペランドの場合、結果の 16 番目のビットを計算する必要があります。
  • そのためには、かなりの論理回路と ALU の加算原理を知る必要があります。
  • または、値を 8 ビット ペアに分割し、2x8 ビット減算カスケードを実行します。
  • そうa=a-dなります:
  • sub al,dl
  • sbc ah,dh
  • キャリー/サインは、アクセス可能な結果の8番目のビットにあります
于 2015-05-12T20:56:26.187 に答える