想要确保我不必返回并重做大块代码......我将每个操作码作为实现Runnable的枚举中的值。有没有一种更有效的方法我应该这样做,还是我在写作轨道上获得准确运行测试套件的东西?
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();
}
}
答案 0 :(得分:1)
好吧,我认为这是编写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, 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语句等中使用。
答案 1 :(得分: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);
}
我使用字符串列表构建我的OpCodes列表,例如ORA abs
。 ORA
映射到我的CPU类中的逻辑方法,abs
映射到另一个寻址方法。我使用reflect来查找方法并将它们填入我的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连接到终端。
但这是我的小脑袋里传出来的。我没有学习其他模拟器,我没有动力在指令中找出位模式。我只是为每个可能的操作码和它应该做的事情制作一个表。