2016-11-20 143 views
1

想要確保我不必回去並重做大塊代碼......我將每個操作碼都作爲實現Runnable的枚舉中的值。有沒有更有效的方法,我應該這樣做,還是我在寫軌道上準確地運行測試套件?這是在JAVA中實現6502 CPU模擬器的「好」方法嗎?

package com.codeblox.nes.cpu; 

public class CPU { 

    private byte x, y, ac, pcl, pch; 
    private short pc; 
    private boolean debugEnabled = false, isGood = true; 
    private static byte [] mainMem = new byte [0x10000]; 
    public Opcode opcode; 

    CPU(boolean debugEnabled){ 
     opcode =Opcode.nop; 
     pc = 0; 
     this.debugEnabled = debugEnabled; 

    } 

    public enum Opcode implements Runnable{ 


     adc(){public void run(){System.out.println("adc");}}, 
     and(){public void run(){System.out.println("and");}}, 
     asl(){public void run(){System.out.println("asl");}}, 
     bcc(){public void run(){System.out.println("bcc");}}, 
     bcs(){public void run(){System.out.println("bcs");}}, 
     beq(){public void run(){System.out.println("beq");}}, 
     bit(){public void run(){System.out.println("bit");}}, 
     bmi(){public void run(){System.out.println("bmi");}}, 
     bne(){public void run(){System.out.println("bne");}}, 
     bpl(){public void run(){System.out.println("bpl");}}, 
     brk(){public void run(){System.out.println("brk");}}, 
     bvc(){public void run(){System.out.println("bvc");}}, 
     bvs(){public void run(){System.out.println("bvs");}}, 
     clc(){public void run(){System.out.println("clc");}}, 
     cld(){public void run(){System.out.println("cld");}}, 
     cli(){public void run(){System.out.println("cli");}}, 
     clv(){public void run(){System.out.println("clv");}}, 
     cmp(){public void run(){System.out.println("cmp");}}, 
     cpx(){public void run(){System.out.println("cpx");}}, 
     cpy(){public void run(){System.out.println("cpy");}}, 
     dec(){public void run(){System.out.println("dec");}}, 
     dex(){public void run(){System.out.println("dex");}}, 
     dey(){public void run(){System.out.println("dey");}}, 
     eor(){public void run(){System.out.println("eor");}}, 
     inc(){public void run(){System.out.println("inc");}}, 
     inx(){public void run(){System.out.println("inx");}}, 
     iny(){public void run(){System.out.println("iny");}}, 
     jmp(){public void run(){System.out.println("jmp");}}, 
     jsr(){public void run(){System.out.println("jsr");}}, 
     lda(){public void run(){System.out.println("lda");}}, 
     ldx(){public void run(){System.out.println("ldx");}}, 
     ldy(){public void run(){System.out.println("ldy");}}, 
     lsr(){public void run(){System.out.println("lsr");}}, 
     nop(){public void run(){System.out.println("nop");}}, 
     ora(){public void run(){System.out.println("ora");}}, 
     pha(){public void run(){System.out.println("pha");}}, 
     php(){public void run(){System.out.println("php");}}, 
     pla(){public void run(){System.out.println("pla");}}, 
     plp(){public void run(){System.out.println("plp");}}, 
     rol(){public void run(){System.out.println("rol");}}, 
     ror(){public void run(){System.out.println("ror");}}, 
     rti(){public void run(){System.out.println("rti");}}, 
     rts(){public void run(){System.out.println("rts");}}, 
     sbc(){public void run(){System.out.println("sbc");}}, 
     sec(){public void run(){System.out.println("sec");}}, 
     sed(){public void run(){System.out.println("sed");}}, 
     sei(){public void run(){System.out.println("sei");}}, 
     sta(){public void run(){System.out.println("sta");}}, 
     stx(){public void run(){System.out.println("stx");}}, 
     sty(){public void run(){System.out.println("sty");}}, 
     tax(){public void run(){System.out.println("tax");}}, 
     tay(){public void run(){System.out.println("tay");}}, 
     tsx(){public void run(){System.out.println("tsx");}}, 
     txa(){public void run(){System.out.println("txa");}}, 
     txs(){public void run(){System.out.println("txs");}}, 
     tya(){public void run(){System.out.println("tya");}}, 
     ; 

     public String mnemonic = ""; 
     public String addressMode; 
     public byte code; 
     public byte data; 

     Opcode(){ 

      this.mnemonic = new String(); 

     } 

     public void print(){ 

      System.out.printf("Opcode: %02X %s %s\n", 
           this.code, 
           this.mnemonic.toUpperCase(), 
           this.addressMode); 

     } 

     public String getMode00(byte opcode){ 

      switch(opcode){ 

       case 0x00: return "Immediate"; 
       case 0x04: return "ZeroPaged"; 
       case 0x0C: return "Absolute"; 
       case 0x14: return "IndexedZeroPagedX"; 
       case 0x1C: return "IndexedAbsoluteX"; 
       default: return "Type 0 undefined"; 

      } 

     } 

     public String getMode01(byte opcode){ 

      switch(opcode){ 

       case 0x00: return "InirectIndexedZeroPagedX"; 
       case 0x04: return "ZeroPaged"; 
       case 0x08: return "Immediate"; 
       case 0x0C: return "Absolute"; 
       case 0x10: return "IndrectedZeroPagedY"; 
       case 0x14: return "IndexedZeroPagedX"; 
       case 0x18: return "IndexedAbsoluteY"; 
       case 0x1C: return "IndexedAbsoluteX"; 
       default: return "Type 1 Undefined";   

      } 

     } 

     public String getMode02(byte opcode){ 

      switch(opcode){ 

       case 0x00: return "Immediate"; 
       case 0x04: return "ZeroPaged"; 
       case 0x08: return "Accumulator"; 
       case 0x0C: return "Absolute"; 
       case 0x14: return "IndexedZeroPagedX"; 
       case 0x1C: return "IndexedAbsoluteX"; 
       default: return "Type 2 Undefined"; 

      } 

     } 

     public String getMode03(byte opcode){ return "";} 

     public void decode(){ 

      switch(this.code & 0x03){ 

       case 0x00: this.addressMode = getMode00((byte)(this.code & 0x1C)); break; 
       case 0x01: this.addressMode = getMode01((byte)(this.code & 0x1C)); break; 
       case 0x02: this.addressMode = getMode02((byte)(this.code & 0x1C)); break; 
       case 0x03: this.addressMode = getMode03((byte)(this.code & 0x1C)); break; 
       default: break; 


      } 


     } 

    } 


    public void init(){ 

     pc = 0; 

    } 

    public void start(){ 

     while(isGood){ 


      opcode.code = readMem(pc++); 
      CPU.Opcode.valueOf(opcode.mnemonic).run(); 

     } 

     if(!isGood){ 

      System.err.println("isGood == false"); 

     } 

    } 

    public byte readMem(short ptr){ 

     return mainMem[ptr]; 

    } 

    public byte readMem(short ptr, byte addressMode){ 

     return mainMem[ptr]; 

    } 

    public void exec(){ 

     opcode.decode(); 

     switch(opcode.code & 0xFF){ 

      case 0x69: case 0x65: case 0x75: 
      case 0x6D: case 0x7D: case 0x79: 
      case 0x61: case 0x71: opcode.mnemonic = "adc"; break; 

      case 0x29: case 0x25: case 0x35: 
      case 0x2D: case 0x3D: case 0x39: 
      case 0x21: case 0x31: opcode.mnemonic = "and"; break; 

      case 0x0A: case 0x06: case 0x16: 
      case 0x0E: case 0x1E: opcode.mnemonic = "asl"; break; 



      default: opcode.mnemonic = null; 

     } 

     //Opcodes.valueOf(this.mnemonic).run(); 

    } 

    public void testOpcodes(){ 

     opcode.code = 0; 

     while((opcode.code & 0xFF) < 0xFF){ 

      //System.out.printf("PC = 0x%04X \n", PC); 
      exec(); 
      if(opcode.mnemonic != null) 
       opcode.print(); 
       //Opcode.valueOf(opcode.mnemonic).run(); 

      opcode.code++; 

     } 


    } 
    public static void main(String[] args) { 
     // TODO Auto-generated method stub 

     CPU cpu = new CPU(true); 
     cpu.init(); 
     cpu.testOpcodes(); 

    } 

} 
+1

我說Runnable接口是錯誤的選擇。您的OpCodes必須獲取操作數(如果有的話),更改cpu中的寄存器並更改程序指針,因此要實現的方法必須具有(至少)cpu,RAM對象和當前程序指針作爲參數,並返回新ProgramPointer。 –

+3

代碼審查問題應發佈在[codereview](http://codereview.stackexchange.com)網站上。 – Kayaman

+0

我試着將它移動到codereview,但有一個冷卻時間定時器,需要在帖子之間等待40分鐘。對於那個很抱歉。蒂莫西 - 我可以讓記憶和每個寄存器都是靜態的。這樣,他們可以從我的項目中的任何地方訪問。否則,我可以將它從一個枚舉移動到它自己的類中,併爲每個引用CPU的操作碼指定一個參數。 – Codeblox

回答

0

我不能說最好或最壞的,我只能說我做了什麼。

我有一個OpCode類,並且我爲每個操作碼(0-255,未定義的操作碼都是我機器上的NOP)創建了這個類的一個實例。

我的操作碼包含兩種方法。一個代表尋址模式,另一個代表實際指令。

這裏是我的執行方法:

public void execute(CPU cpu) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { 
    Integer value = (Integer) addrMethod.invoke(cpu); 
    opMethod.invoke(cpu, value); 
} 

我建立我的操作碼列表與字符串列表,如ORA absORA映射到我的CPU類中的邏輯方法,並且abs映射到另一種尋址方法。我使用反射來查找方法並將它們填充到我的OpCode實例中。

public void ORA(int value) { 
    acc = acc | value; 
    setFlagsNZ(acc); 
} 

public int fetchAbsolute() { 
    int addr = addrAbsolute(); 
    return fetchByte(addr); 
} 

addrAbsolute將拉動2個字節的內存,並加2的PC,等等。 fetchByte獲取地址處的值。然後將該值傳遞給作用於累加器的ORA。

最後,我有256個操作碼的邏輯方法,以及尋址的方法。

我的模擬器的核心是簡單地設置初始地址,從該地址獲取操作碼,增加地址,執行操作碼。

int code = mem.fetchByte(pc++); 
OpCode op = Instructions.opCodes[code]; 
op.execute(this); 

this是CPU實例)。

注意,我的軟件是一個軟件模擬器,它並不追求週期平價或類似的東西。它不模擬任何特定的硬件(如C64)。這是一個原始的6502,帶有一些專用存儲器位置,用於I/O到終端。

但這是我小腦袋裏出來的東西。我沒有研究其他模擬器,我沒有動力去指出指令中的位模式。我只是爲每個可能的操作碼和它應該做的事製作一張表格。

1

嗯,我想這也是寫一個6502 CPU仿真的好方法開始。但它需要一些工作...

問問自己:什麼是Java中的枚舉?它有什麼好處?

它基本上是一個固定數量的實例類,所以這是偉大的代表靜態數據(和行爲)和分組 - 與相關方法 - 是很容易看到,可測試,可修改。

在不同的方法,你有打出來的不同的尋址模式和操作每個操作碼開關語句:

switch(opcode) { 
    case 0x00: return "Immediate"; 
    case 0x04: return "ZeroPaged"; 
    case 0x0C: return "Absolute"; 
    case 0x14: return "IndexedZeroPagedX"; 
    case 0x1C: return "IndexedAbsoluteX"; 
    default: return "Type 0 undefined"; 
} 

,我們將不得不增加更多的開關語句如果我們想說明時間等

但這是靜態數據。這些情況常量應該是枚舉屬性。這不是應該在枚舉中編碼的那種數據嗎?我想是的,布倫丹羅伯特也是如此,他寫了JACE, the Java Apple Computer Emulator。他的代碼是深思熟慮的Java枚舉的一個很好的例子。

這裏有his 6502 CPU's OPCODE枚舉的前幾行:

public enum OPCODE { 
    ADC_IMM(0x0069, COMMAND.ADC, MODE.IMMEDIATE, 2), 
    ADC_ZP(0x0065, COMMAND.ADC, MODE.ZEROPAGE, 3), 
    ADC_ZP_X(0x0075, COMMAND.ADC, MODE.ZEROPAGE_X, 4), 
    // ... 
} 

所有靜態數據組合在一起很好,很容易看到,準備在case語句等使用

相關問題