1

想要确保我不必返回并重做大量代码......我将每个操作码作为实现 Runnable 的枚举中的一个值。有没有更有效的方法我应该这样做,或者我是否在写轨道上以获得准确运行测试套件 rom 的东西?

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();

    }

}
4

2 回答 2

2

好吧,我认为这是编写 6502 CPU 仿真器的好方法的开始。但它需要一些工作......

问问自己:Java 中的枚举是什么?它有什么用?

它基本上是一个具有固定数量实例的类,因此它非常适合表示静态数据(和行为)并将其分组 - 使用相关方法 - 易于可见、可测试、可修改。

在各种方法中,您可以使用 switch 语句来分解每个操作码的不同寻址模式和操作:

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";
}

如果我们想要指令时间等,我们将不得不添加更多的 switch 语句。

但这是静态数据。这些案例常量应该是枚举属性。这不是应该在枚举中编码的那种数据吗?我认为是这样,编写JACE(Java Apple Computer Emulator)的Brendan Robert 也是如此。他的代码是经过深思熟虑的 Java 枚举的一个很好的例子。

以下是他的 6502 CPU 的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),
    // ...
}

所有的静态数据都很好地组合在一起,很容易看到,准备在案例陈述等中使用。

于 2016-11-21T23:45:57.963 回答
0

我不能说最好或最坏,我只能说我做了什么。

我有一个 OpCode 类,我为每个操作码创建了一个此类的实例(0-255,未定义的操作码是我机器上的 NOP)。

我的 OpCode 包含两种方法。一个代表寻址模式,另一个代表实际指令。

这是我的执行方法:

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

我使用字符串列表构建我的操作码列表,例如ORA abs. ORA映射到我的 CPU 类中的一个逻辑方法,并abs映射到另一个寻址方法。我使用反射来查找方法并将它们填充到我的 OpCode 实例中。

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

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

addrAbsolute将从内存中提取 2 个字节,并将 PC 增加 2,等等。fetchByte获取地址处的值。然后将该值传递给 ORA,ORA 作用于累加器。

最后,我有 256 个操作码,其中包含一个逻辑方法和一个寻址方法。

我的模拟器的核心只是简单地设置初始地址,从该地址获取操作码,增加地址,执行操作码。

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

this作为 CPU 实例)。

请注意,我的是一个软模拟器,它不会争取周期奇偶校验或类似的东西。它不模拟任何特定的硬件(如 C64)。这是一个原始的 6502,有几个专用的内存位置,用于 I/O 到终端。

但这是从我的小脑袋里冒出来的。我没有研究其他模拟器,也没有动力去找出指令中的位模式。我只是为每个可能的操作码和它应该做什么做一个表格。

于 2016-11-20T20:05:58.657 回答