2011-12-14 35 views
1

我剛看到這個視頻http://www.youtube.com/watch?v=y71lli8MS8s,發現它很棒。問題是關於他的操作碼解碼矩陣功能:試圖理解在C++中宏,模板和枚舉的複雜用法11

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). 
    */ 
} 

我發現非常混亂的是define語句。當宏擴展時,它看起來像這樣http://codepad.org/bUxdX8MQ。但'130+"(),-089<>?BCFGHJLSVWZ[^hlmnxy|}"'怎麼能成爲合法的C++代碼?就我所知,以這種方式組合字符串和整數是非法的。此外,("zy}z{y}zzy}zzy}zzy}zzy}zzy}zzy}z "[o8]>90)根本沒有意義,除非C++ 11以數組方式添加對訪問字符串字符的支持。希望能學到新東西!

編輯:謝謝大家的迴應。我不知道"blabla"[idx]現在在C/C++中是允許的,我認爲代碼有意義。

+0

請注意,文字字符串是一個數組,所以``stuff「[i]`是完全有效的; `130+'東西'[i]`給第i個角色的價值增加了​​130。在C和C++中一直如此;我相當確定該代碼中沒有C++ 11。 – 2011-12-14 17:04:11

回答

1

事實上它應該是

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

字符串文字確實有array of char類型,從第一C++規範。

所以上面的可以改寫爲:

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

你可以看到,這是合法的C++。

1

字符串文字只是char數組,字符只是8位整數,所以對它們進行算術是完全合法的。他們也可以自動升級到int

因此,要採取一行,並把它分解:

{ enum { i=             //1 

無論是在=的右側必須是一個整型常量

  o8m &            //2 

o8m = 1 << (op%8),定義以上;所以我們知道0 <= 08m < 8。我們是按位安定它的東西,於是再次RHS將是一個整型常量

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

如果這個字符串的o8個字符具有整數值> 90然後...

(130+               //4 

......那麼結果(是and版以上08m)將是130加東西...

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

...我們再次是TA王從這個字符串中的字符,並計算指數:

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

...這個其他字符串文字的o8個字符,少94。 我不確定這會做正確的事情,因爲該字符串文字中沒有任何字符看起來具有值>= 94,否定答案將是表達式的無效索引。

無論如何,這完成表達,其是三元表達的第一分支。現在對於別的...

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

現在是給出了一個索引的積分值;該索引處的字符從中減去以給出其他分支的最終值。

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

因此,我們終於完成了辛苦計算i的常數值。如果它不爲零,則設置addr

好吧,這太可怕了,我錯過了瀏覽器中的括號匹配,但希望你明白了。

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生成的是低三位,即0到7之間的數字,所以似乎沒有任何操作,如添加然後再次提取低三位的一個子集(這次用&運算符)。

反正...

此計算索引到宏的參數字符串:

[ 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 

把二進制數據轉換成字符串文字是混淆和「代碼高爾夫」,使程序儘可能短的運動最喜歡的策略。這是一種使程序不可讀且內存相當緊湊的方法,它可以使內存更快,而不是優化內存消耗的最佳方式。如果您需要一個數字表格,請將它們指定爲十六進制或十進制數字。