4

私はエミュレーションプログラミングを学ぼうとしています。私は CHIP-8 エミュレーター、40 歳未満の命令を実行し、自分の音楽のために生きてきました。私は今、SNES のようなもう少し複雑なことをしたいと考えています。私が直面している問題は、膨大な数の CPU 命令です。wiki.SuperFamicom.org 65c816の説明書一覧を見ると、後頭部の痛みのように見えます。また、さまざまなインターネット ページで、CPU がエミュレーターの中で最も実装しやすい部分であるというメモをあちこちで見てきました。

自分のやり方が間違っていたので大変だったのだろうと思い、周りを見回して簡単な実装を見つけました。SNES エミュレーターを 15 分で作成できます。これは約 900 行のコードです。簡単に作業できます。

というわけで、15分ほどでSNES Emulatorのソースから、CPUの命令がどこにあるのかを見つけました。見た目は思っていたよりずっとシンプル。よくわかりませんが、大量のコードとは対照的に、数行のコードです。私が最初に気付くのは、指示にはそれぞれ 1 つの含意しかないということです。スーパーファミコンの表を見ると、

ADC #const
ADC (_db_),X
ADC (_db_,X)
ADC addr
ADC long
...

そして、(私が思うに)それらすべてのエミュレータソースは次のとおりです。

// Note: op 0x100 means "NMI", 0x101 means "Reset", 0x102 means "IRQ". They are implemented in terms of "BRK".
// User is responsible for ensuring that WB() will not store into memory while Reset is being processed.
unsigned addr=0, d=0, t=0xFF, c=0, sb=0, pbits = op<0x100 ? 0x30 : 0x20;

// Define the opcode decoding matrix, which decides which micro-operations constitute
// any particular opcode. (Note: The PLA of 6502 works on a slightly different principle.)
const unsigned o8 = op / 32, o8m = 1u << (op%32);
// Fetch op'th item from a bitstring encoded in a data-specific variant of base64,
// where each character transmits 8 bits of information rather than 6.
// This peculiar encoding was chosen to reduce the source code size.
// Enum temporaries are used in order to ensure compile-time evaluation.
#define t(w8,w7,w6,w5,w4,w3,w2,w1,w0) if( \
        (o8<1?w0##u : o8<2?w1##u  : o8<3?w2##u : o8<4?w3##u : \
         o8<5?w4##u : o8<6?w5##u  : o8<7?w6##u : o8<8?w7##u : w8##u) & o8m)

t(0,0xAAAAAAAA,0x00000000,0x00000000,0x00000000,0xAAAAA2AA,0x00000000,0x00000000,0x00000000) { c = t; t += A + P.C; P.V = (c^t) & (A^t) & 0x80; P.C = t & 0x100; }

要するに、私の一般的な質問:

  • CPU 命令の驚異的な宇宙パワーを小さなコードに凝縮

15 分間のソースで SNES エミュレーターに固有の質問 (上記に投稿された部分):

  • 命令をどのようにt(0, 0xAAAAAAAA, 0x00000000, ....)解析しますか? ステートメントは表示ifされますが、引数の番号がどこから来たのか、またはそれらがコード全体に何を意味するのかわかりません。
  • なぜ?o8 = op / 32_o8m = 1u << (op%32)
  • 2 バイトのオペランドを持つ、または3 バイトのオペランドを持つADChasのオペコード。そして、コードは両方のケースを含意しますか?ADC #constADC addrt(0, 0xAAAAAAAA, ...)

私が尋ねている間:

  • dp_dp_およびsrに表示されるADC dpADC (_dp_)およびは何をADC sr,S意味しますか?
  • ADC (_dp_,X)とはどう違いADC dp,Xますか?(上記の質問を考えると、おそらく冗長です。)
4

1 に答える 1

4

このすべてにお答えすることはできませんがdp、ダイレクト ページの略です。つまり、命令は、ダイレクト ページ内のメモリ アドレスであるシングルバイト オペランドを取ります。ダイレクト ページ アドレッシングは、6502 のゼロ ページ アドレッシング モードを拡張したもので、1 バイト アドレスが$00を介してメモリ位置を参照して$FFいました。6502 の 16 ビット版には、基本的にゼロ ページを別の場所に再配置する構成レジスタがあります。

リンク先の wiki ページでdpは、表の一部に下線があり、その他はイタリック体になっています。それらはすべてイタリックにすることを意図しており、wiki マークアップが機能していないと思います。[編集] リンクを簡単に確認すると、この仮定が裏付けられます (wiki ソースでは、すべてにアンダースコアが付いています)。だから、そこには何も読まないでください。

6502 アセンブリとその派生物では、ADC dp,X意味... 代わりに具体的な例を見てみましょう...レジスタの値にADC $10,X加算してアドレスを取得し、そのアドレスから値をロードしてアキュムレータに追加することを意味します。追加レベルの間接化を追加します。アドレスを取得し、そのアドレスから値をロードし、ロードされた値を別のアドレスとして解釈し、そのアドレスから値をロードてアキュムレータに追加します。括弧で囲まれたオペランドは、常に間接的なレベルを追加します。$10XADC ($10,X)$10X

使用可能なモードには(dp,X)andが含まれ(dp),Yており、カンマとレジスタに対する括弧の配置が重要であることに注意してください。(dp),Yの値を最初にロードされた値にY追加して、2 番目のロードで使用するアドレスを取得します。

そのエミュレーターに関しては...コードゴルフは読みやすさの向上につながりません! あなたが投稿した部分はそれ自体で実際に理解できるとは思いませんし、残りの部分を追跡して読む気もありません. しかし、tマクロの重要な概念はbitstringです。その引数は、それぞれ 32 ビットの長さで合計 288 ビットの一連の 9 ビットマスクです。したがって、考えられるすべてのオペコード (256 個) と最初のコメントで述べた 3 つの疑似オペコードは、この 288 ビット長のビット文字列の 1 ビットで表され、29 ビットが残ります。

それは と の構造を説明していo8ますo8m。8 ビット値は、3 ビット部分 ( に提供される 8 つの引数から引数を選択するためt) と 5 ビット部分 (選択された引数から単一ビットを選択するため) に分割されます。大きな?:チェーンは、最初の選択とその組み合わせを&行い1 << ...、選択選択を行います。

そして、ああ、変数tも呼び出されているのを見てください。マクロとは関係ありません。彼らに同じ名前を付けることは、ただ残酷でした。

たぶん、そのビット文字列が何をしているかを理解できるでしょう。オペコードが低い数値の場合o8(上位ビット) は 0 になるため、?:チェーンはマクロの最後のw0引数であるを使用します。オペコードが増加するにつれて、選択された引数は引数リストを左に移動し、次に...セレクターも同様に右から開始し、左に移動します (は右端のビット、は次のビット、など)。条件は次のようになります。選択したビットが 1 の場合は true。値は次のとおりです。w1w2o8m& (1<<0)& (1<<1)if

0,          # opcodes $100 and up
0xAAAAAAAA, # opcodes $E0 to $FF
0x00000000, # opcodes $C0 to $DF
0x00000000, # opcodes $A0 to $BF
0x00000000, # opcodes $80 to $9F
0xAAAAA2AA, # opcodes $60 to $7F
0x00000000, # opcodes $40 to $5F
0x00000000, # opcodes $20 to $3F
0x00000000  # opcodes $00 to $1F

またはバイナリで

0,                                  # opcodes $100 and up
0b10101010101010101010101010101010, # opcodes $E0 to $FF
0b00000000000000000000000000000000, # opcodes $C0 to $DF
0b00000000000000000000000000000000, # opcodes $A0 to $BF
0b00000000000000000000000000000000, # opcodes $80 to $9F
0x10101010101010101010001010101010, # opcodes $60 to $7F
0b00000000000000000000000000000000, # opcodes $40 to $5F
0b00000000000000000000000000000000, # opcodes $20 to $3F
0b00000000000000000000000000000000  # opcodes $00 to $1F

各行を右から左に読むと、1 は次のオペコードに対応する位置にあります。$61 $63 $65 $67 $69 $6D $6F $71 $73 $75 $77 $79 $7B $7D $7F $E1 $E3 $E5 $E7 $E9 $EB $ED $EF $F1 $F3 $F5 $F7 $F9 $FB $FD $FF

うーん...ADCSBCオペコードのリストに似ていますが、一部が間違っています。

ああ(ついにあきらめて、エミュレーターコードをもう少し調べました)これはSNESエミュレーターではなくNESエミュレーターなので、6502個のオペコードしかありません。

于 2013-06-22T23:34:46.203 に答える