2011-12-14 7 views
1

私はこのビデオhttp://www.youtube.com/watch?v=y71lli8MS8sを見て、驚くほど見つけました。問題は、彼のオペコードデコードマトリックス関数についてです:C++のマクロ、テンプレート、列挙型の複雑な使い方を理解しようとしています。

template<u16 op> // Execute a single CPU instruction, defined by opcode "op". 
void Ins()  // With template magic, the compiler will literally synthesize >256 different functions. 
{ 
    // 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.) 
    enum { o8 = op/8, o8m = 1 << (op%8) }; 
    // 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(s,code) { enum { \ 
     i=o8m & (s[o8]>90 ? (130+"(),-089<>?BCFGHJLSVWZ[^hlmnxy|}"[s[o8]-94]) \ 
          : (s[o8]-" (("[s[o8]/39])) }; if(i) { code; } } 

    /* Decode address operand */ 
    t("        !", addr = 0xFFFA) // NMI vector location 
    t("        *", addr = 0xFFFC) // Reset vector location 
    t("!        ,", addr = 0xFFFE) // Interrupt vector location 
    t("zy}z{y}zzy}zzy}zzy}zzy}zzy}zzy}z ", addr = RB(PC++)) 
    t("2 yy2 yy2 yy2 yy2 XX2 XX2 yy2 yy ", d = X) // register index 
    t(" 62 62 62 62 om om 62 62 ", d = Y) 
    t("2 y 2 y 2 y 2 y 2 y 2 y 2 y 2 y ", addr=u8(addr+d); d=0; tick())    // add zeropage-index 
    t(" y z!y z y z y z y z y z y z y z ", addr=u8(addr); addr+=256*RB(PC++))  // absolute address 
    t("3 6 2 6 2 6 286 2 6 2 6 2 6 2 6 /", addr=RB(c=addr); addr+=256*RB(wrap(c,c+1)))// indirect w/ page wrap 
    t(" *Z *Z *Z *Z  6z *Z *Z ", Misfire(addr, addr+d)) // abs. load: extra misread when cross-page 
    t(" 4k 4k 4k 4k 6z  4k 4k ", RB(wrap(addr, addr+d)))// abs. store: always issue a misread 
    /* Load source operand */ 
    t("aa__ff__ab__,4 ____ - ____  ", t &= A) // Many operations take A or X as operand. Some try in 
    t("    knnn  4 99 ", t &= X) // error to take both; the outcome is an AND operation. 
    t("    9989 99  ", t &= Y) // sty,dey,iny,tya,cpy 
    t("      4   ", t &= S) // tsx, las 
    t("!!!! !! !! !! ! !! !! !!/", t &= P.raw|pbits; c = t)// php, flag test/set/clear, interrupts 
    t("_^__dc___^__   ed__98 ", c = t; t = 0xFF)  // save as second operand 
    t("vuwvzywvvuwvvuwv zy|zzywvzywv ", t &= RB(addr+d)) // memory operand 
    t(",2 ,2 ,2 ,2 -2 -2 -2 -2 ", t &= RB(PC++)) // immediate operand 
    /* Operations that mogrify memory operands directly */ 
    t(" 88       ", P.V = t & 0x40; P.N = t & 0x80) // bit 
    t(" nink nnnk     ", sb = P.C)  // rol,rla, ror,rra,arr 
    t("nnnknnnk  0     ", P.C = t & 0x80) // rol,rla, asl,slo,[arr,anc] 
    t("  nnnknink     ", P.C = t & 0x01) // lsr,sre, ror,rra,asr 
    t("ninknink       ", t = (t << 1) | (sb * 0x01)) 
    t("  nnnknnnk     ", t = (t >> 1) | (sb * 0x80)) 
    t("     !  kink  ", t = u8(t - 1)) // dec,dex,dey,dcp 
    t("       ! khnk ", t = u8(t + 1)) // inc,inx,iny,isb 
    /* Store modified value (memory) */ 
    t("kgnkkgnkkgnkkgnkzy|J kgnkkgnk ", WB(addr+d, t)) 
    t("     q    ", WB(wrap(addr, addr+d), t &= ((addr+d) >> 8))) // [shx,shy,shs,sha?] 
    /* Some operations used up one clock cycle that we did not account for yet */ 
    t("rpstljstqjstrjst - - - -kjstkjst/", tick()) // nop,flag ops,inc,dec,shifts,stack,transregister,interrupts 
    /* Stack operations and unconditional jumps */ 
    t("  ! ! !     ", tick(); t = Pop())      // pla,plp,rti 
    t("  ! !     ", RB(PC++); PC = Pop(); PC |= (Pop() << 8)) // rti,rts 
    t("   !     ", RB(PC++)) // rts 
    t("! !       /", d=PC+(op?-1:1); Push(d>>8); Push(d))  // jsr, interrupts 
    t("! ! 8 8     /", PC = addr) // jmp, jsr, interrupts 
    t("!!  !      /", Push(t)) // pha, php, interrupts 
    /* Bitmasks */ 
    t("! !! !! !! !! ! !! !! !!/", t = 1) 
    t(" ! !     !! !! ", t <<= 1) 
    t("! ! ! !! !!  ! ! !/", t <<= 2) 
    t(" ! ! ! !  !   ", t <<= 4) 
    t(" !  !   ! !____ ", t = u8(~t)) // sbc, isb,  clear flag 
    t("`^__ !  !    !/", t = c | t) // ora, slo,  set flag 
    t(" !!dc`_ !! ! ! !! !! ! ", t = c & t) // and, bit, rla, clear/test flag 
    t("  _^__      ", t = c^t) // eor, sre 
    /* Conditional branches */ 
    t("  !  !  !  ! ", if(t) { tick(); Misfire(PC, addr = s8(addr) + PC); PC=addr; }) 
    t(" !  !  !  !  ", if(!t) { tick(); Misfire(PC, addr = s8(addr) + PC); PC=addr; }) 
    /* Addition and subtraction */ 
    t("   _^__   ____ ", c = t; t += A + P.C; P.V = (c^t) & (A^t) & 0x80; P.C = t & 0x100) 
    t("      ed__98 ", t = c - t; P.C = ~t & 0x100) // cmp,cpx,cpy, dcp, sbx 
    /* Store modified value (register) */ 
    t("aa__aa__aa__ab__ 4 !____ ____ ", A = t) 
    t("     nnnn 4 ! ", X = t) // ldx, dex, tax, inx, tsx,lax,las,sbx 
    t("     ! 9988 !  ", Y = t) // ldy, dey, tay, iny 
    t("     4 0   ", S = t) // txs, las, shs 
    t("! ! ! !! ! !  ! ! !/", P.raw = t & ~0x30) // plp, rti, flag set/clear 
    /* Generic status flag updates */ 
    t("wwwvwwwvwwwvwxwv 5 !}}||{}wv{{wv ", P.N = t & 0x80) 
    t("wwwv||wvwwwvwxwv 5 !}}||{}wv{{wv ", P.Z = u8(t) == 0) 
    t("    0     ", P.V = (((t >> 5)+1)&2))   // [arr] 
    /* All implemented opcodes are cycle-accurate and memory-access-accurate. 
    * [] means that this particular separate rule exists only to provide the indicated unofficial opcode(s). 
    */ 
} 

私が非常に混乱しているのは、定義ステートメントです。マクロを展開すると、http://codepad.org/bUxdX8MQのようになります。しかし、'130+"(),-089<>?BCFGHJLSVWZ[^hlmnxy|}"'はどのようにして正当なC++コードになりますか?私が知る限り、このような方法で文字列と整数を組み合わせることは違法です。また、この("zy}z{y}zzy}zzy}zzy}zzy}zzy}zzy}z "[o8]>90)は、C++ 11が配列形式で文字列にアクセスするサポートを追加しない限り、全く意味をなさない。新しいことを学ぶことを願っています!

EDIT:回答いただいた皆様、ありがとうございます。私は"blabla"[idx]がC/C++で許されていたことに気づいていませんでした。

+0

注意。 '130+ 'stuff" [i] 'はi番目の文字の値に130を加えます。 CおよびC++ではこれが常にそうでした。私は、そのコードにC++ 11がないと確信しています。 –

+0

その例はこの競技のためのものです:www.ioccc.org。副次的な点では、これは似ています:http://www.ioccc.org/2006/birken/birken.c –

答えて

1

実際にはリテラル

130+"(),-089<>?BCFGHJLSVWZ[^hlmnxy|}"["        !"[o8]-94] 

文字列が最初にC++仕様から、array of charのタイプを持っていなければなりません。

したがって、上記のよう書き換えることができます:あなたはそれが有効なC++であることがわかります

char s1[]="(),-089<>?BCFGHJLSVWZ[^hlmnxy|}"; 
char s2[]="        !"; 
130+s1[s2[o8]-94]; 

1

文字列リテラルはちょうどcharの配列であり、charはちょうど8ビットの整数なので、それらを使って算術演算を実行することは完全に正当です。また、自動的にintにプロモートすることもできます。

ので、1行を取り、それを打破するために:=の右側にあるものは何でも

{ enum { i=             //1 

は積分定数

  o8m &            //2 

o8m = 1 << (op%8)なければならないだろう、定義されました上記。だから我々は0 <= 08m < 8を知っている。我々は、それは...文字通り、この文字列のo8番目の文字は、整数値> 90を持っている場合ので、再びRHSは積分定数

    ("        !"[o8]>90 ? //3 

なり、何かを

(130+               //4 

ビット単位AND演算しています...その結果は、(上記08mand EDになるように)130プラス何か...

"(),-089<>?BCFGHJLSVWZ[^hlmnxy|}"[       //5 

になります...再び私たちがtaです王としてのインデックスを計算するリテラルこの文字列から文字、:

"        !"[o8]-94])     //6 

...リテラルこの他の文字列のo8番目の文字、94少ないです。 文字列リテラルの文字のどれもが値>= 94を持っていないので、これが正しいことを確信していません。負の答えは式の無効なインデックスになります。三元表現の最初の分岐で表現を完了とにかく

、。今、他のために...

: ("        !"[o8]-     //7 
    " (("[              //8 
    "        !"[o8]/39    //9 
    ] 
) 

は今 へのインデックスを与える積分値です。そのインデックスの文字は、から減算され、以外のブランチの最終値が得られます。

) }; 
if(i) { addr = 0xFFFA; } } 

最後に、iの定数値を計算するのが面倒でした。これがゼロ以外の場合は、addrと設定します。

OK、これは恐ろしいことでしたが、ブラウザで括弧が一致するのを見逃してしまいましたが、うまくいけばアイデアを得ることができます。

1

文字列は配列なので、"hello"[ 1 ] == 'e'です。さらに、配列はその最初の要素へのポインタに減衰し、添え字操作はarray[ index ] == * (array + index)と定義されます。この加算によって、最初の要素の後にポインタが生成されます。

#defineの式は、カッコではなく優先順位のルールに依存しており、これを面倒な順序で実行するため、混乱します。これは実際に良いコードの例ではありません。

コンパイル時にコンパイル時に何かを評価する必要はありません。最適化が有効になっているときは、すべてのコンパイル時定数式が最適化されます。それ以外の場合は、デバッガで評価を進めることができます。したがって、ここには再フォーマットされたバージョンがあります:

if ((op % 8) & // low 3 bits select conditions for execution 
    (s[ op/8 ] > 'Z'? // lowercase letters go through complicated mapping 
     ("(),-089<>?BCFGHJLSVWZ[^hlmnxy|}"[ s[ op/8 ] - 'Z' - 4 ]) + 130 
    : s[ op/8 ] - " (("[ s [ op/8 ]/39 ])) { // uppercase is simpler 
    code; 
} 

これはまだかなり難読化されており、非効率的です。私はなぜ作者がこのようなやり方をしているのかわからないが(YouTubeを見なかった)、op8 = op % 8は低い3ビット、すなわち0..7の数字を生成するので、add 130に戻ってから、下位3ビットのサブセットを再度抽出する(今度は&演算子を用いて)。

とにかく...

これはマクロの引数文字列へのインデックスを計算します。

[ s[ op/8 ] - 'Z' - 4 ] 

これは、他の文字にマクロ引数の文字列内の大文字をマップする...なぜ私に聞かないでください。欠陥のあるデザインの証拠のように見える。このレベルでは、文字列は翻訳の必要がない形式でなければなりません。

("(),-089<>?BCFGHJLSVWZ[^hlmnxy|}"[ s[ op/8 ] - 'Z' - 4 ]) 

最後に、番号130が追加されます。 130 % 8 == 2以降、私は2も動作すると思います。私を殴る。また、ここでの加算を実行すると、式の型をintに変更するという副作用がありますが、130がcharの範囲内にあるかどうかは関係ありません。文字列リテラルにバイナリデータを置く

("(),-089<>?BCFGHJLSVWZ[^hlmnxy|}"[ s[ op/8 ] - 'Z' - 4 ]) + 130 

難読化と「コードゴルフ」、できるだけ短いプログラムを作るのスポーツのための好きな戦略です。これは、いずれかのメモリ消費を最適化するための最良の方法ではない、それはより速く作るための方法ない、メモリに読み取り不能とかなりコンパクトなプログラムを作るための方法である、と。数字の表が必要な場合は、数字を16進数または10進数で指定します。文字列リテラルは配列なので、 `[i]は、`完全に有効な「もの」であることを

関連する問題