2016-11-20 1 views
1

Je veux m'assurer que je n'aurai pas à revenir en arrière et à refaire de gros morceaux de code ... J'ai chaque opcode comme valeur dans une énumération qui implémente Runnable. Y at-il un moyen plus efficace que je devrais le faire ou suis-je sur la piste d'écriture pour obtenir quelque chose qui exécute une suite de tests avec précision?Est-ce la "bonne" façon d'implémenter un émulateur de CPU 6502 en JAVA?

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

Je dis que l'interface 'Runnable' est le mauvais choix. Vos OpCodes doivent aller chercher des opérandes (s'ils le sont), changer les registres dans le CPU et changer le pointeur du programme, donc la méthode à implémenter doit avoir (au moins) un CPU, un objet RAM et le pointeur actuel du programme comme paramètres. ProgramPointer. –

+3

Les questions d'examen du code devraient être affichées sur le site [codereview] (http://codereview.stackexchange.com). – Kayaman

+0

J'ai essayé de le déplacer vers codereview mais il y a un temps de recharge qui nécessite d'attendre 40 minutes entre les messages. Désolé pour ça. Timothy - Je pourrais rendre la mémoire et chaque registre statique. De cette façon, ils sont accessibles depuis n'importe où dans mon projet. Sinon, je peux simplement déplacer ceci d'une énumération à sa propre classe avec un argument pour chaque opcode qui fait référence au CPU. – Codeblox

Répondre

0

Je ne peux pas parler mieux ou pire, je ne peux parler que de ce que j'ai fait.

J'ai une classe OpCode, et je crée une instance de cette classe pour chaque opcode (0-255, les opcodes non définis sont des NOP sur ma machine).

Mon OpCode contient deux méthodes. L'un représente le mode d'adressage, l'autre l'instruction réelle.

Voici ma méthode execute:

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

Je construis ma liste de OpCodes avec une liste de chaînes, telles que ORA abs. ORA est mappé à une méthode logique dans ma classe CPU et abs est mappé à une autre méthode d'adressage. J'utilise la réflexion pour rechercher les méthodes et les insérer dans mes instances OpCode.

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

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

addrAbsolute va tirer les 2 octets de mémoire, et incrémenter le PC 2, entre autres. fetchByte obtient la valeur à l'adresse. La valeur est ensuite transmise à ORA, qui agit sur l'accumulateur. En fin de compte, j'ai 256 opcodes avec une méthode pour la logique, et une méthode pour l'adressage.

Le cœur de mon simulateur consiste simplement à définir l'adresse initiale, extraire l'opcode de cette adresse, incrémenter l'adresse, exécuter l'opcode.

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

(this étant l'instance de l'unité centrale). L'esprit, le mien est un simulateur doux, il ne vise pas la parité de cycle ou n'importe quoi de pareil. Il ne simule aucun matériel spécifique (comme un C64). C'est un 6502 brut avec quelques emplacements de mémoire dédiés que E/S à un terminal.

Mais c'est ce qui est sorti de ma petite tête. Je n'ai pas étudié d'autres simulateurs, je n'étais pas motivé pour aller chercher des schémas de bits dans les instructions. Je fais juste une table pour chaque opcode possible et ce qu'il était censé faire.

1

Eh bien, je pense que c'est le début d'un bon moyen d'écrire un émulateur de CPU 6502. Mais il faut un peu de travail ...

Demandez-vous: Qu'est-ce qu'une énumération en Java? Et à quoi ça sert? Il s'agit essentiellement d'une classe avec un nombre fixe d'instances, ce qui permet de représenter les données statiques (et le comportement) et de les regrouper - avec les méthodes associées - pour être facilement visible, testable et modifiable.

Dans diverses méthodes que vous avez des instructions switch qui décomposent les différents modes d'adressage et les opérations pour chaque 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"; 
} 

Et nous devrions ajouter des instructions switch si nous voulions fois des instructions etc.

Mais ce sont des données statiques. Ces constantes de cas devraient être des propriétés enum. N'est-ce pas le genre de données qui devraient être encodées dans l'énumération? Je pense ainsi, ainsi que Brendan Robert, qui a écrit JACE, the Java Apple Computer Emulator. Son code est un excellent exemple d'une énumération Java bien pensée.

Voici les premières lignes de his 6502 CPU's OPCODE ENUM:

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

Toutes les données statiques sont regroupées bien ensemble, facilement visible, prêt à être utilisé dans les déclarations de cas, etc.