2016-11-20 107 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语句等使用

相关问题