Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 38 additions & 23 deletions src/gbaEmu/CPU.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,21 @@ public CPU(Memory memory) {
this.register = new Register();
this.timer = new Timer();
}
public int executeNextOp() {
short nextOp = this.register.pc;
this.register.pc++;
opMap.get(nextOp).run();
return opCycles.get(nextOp);
}
public int executeNextOp() {
int opcode = memory.readMemory(register.pc & 0xFFFF) & 0xFF;
register.pc++;
if (opcode == 0xCB) {
int cb = memory.readMemory(register.pc & 0xFFFF) & 0xFF;
register.pc++;
opcode = (0xCB << 8) | cb;
}
Runnable op = opMap.get(opcode);
if (op == null) {
Log.fatalf(String.format("Unknown opcode: 0x%X", opcode));
}
op.run();
return opCycles.getOrDefault(opcode, 4);
}
public void run() throws InterruptedException {
while (true) {
update();
Expand Down Expand Up @@ -101,17 +110,19 @@ private void doInterrupt(int id) {
case 3:
register.pc = 0x58;
break;
case 4:
register.pc = 60;
break;
case 4:
register.pc = 0x60;
break;
default:
Log.fatalf("unknown interrupt: " + id);
}
}
public int initOpcodes() {
opcodes = new Opcodes();
opMap = new HashMap<>();
opCycles = new HashMap<>();
public int initOpcodes() {
opcodes = new Opcodes();
opcodes.cpu = this;
opcodes.memory = this.memory;
opMap = new HashMap<>();
opCycles = new HashMap<>();
opMap.put(0x7F, () -> opcodes.OP7F());
opCycles.put(0x7F, 4);
for (int i = 0x78; i <= 0x7D; i++) {
Expand Down Expand Up @@ -479,10 +490,14 @@ public int initOpcodes() {
opCycles.put(0x2F, 4);
opMap.put(0x3F, () -> opcodes.OP3F());
opCycles.put(0x3F, 4);
opMap.put(0x37, () -> opcodes.OP37());
opCycles.put(0x37, 4);
opMap.put(0x00, () -> opcodes.OP00());
opCycles.put(0x00, 4);
opMap.put(0x37, () -> opcodes.OP37());
opCycles.put(0x37, 4);
opMap.put(0x76, () -> opcodes.OP76());
opCycles.put(0x76, 4);
opMap.put(0x10, () -> opcodes.OP10());
opCycles.put(0x10, 4);
opMap.put(0x00, () -> opcodes.OP00());
opCycles.put(0x00, 4);
opMap.put(0x07, () -> opcodes.OP07());
opCycles.put(0x07, 4);
opMap.put(0x17, () -> opcodes.OP17());
Expand Down Expand Up @@ -1017,12 +1032,12 @@ public byte getValue8() {
register.pc++;
return ans;
}
public short getValue16() {
int value1 = memory.readMemory(register.pc);
int value2 = memory.readMemory(register.pc + 1);
register.pc += 2;
return (short) (value1 << 8 + value2);
}
public short getValue16() {
int low = memory.readMemory(register.pc & 0xFFFF) & 0xFF;
int high = memory.readMemory((register.pc + 1) & 0xFFFF) & 0xFF;
register.pc += 2;
return (short) ((high << 8) | low);
}
public int decrementHL() {
int hl = register.hl();
hl--;
Expand Down
50 changes: 30 additions & 20 deletions src/gbaEmu/Memory.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
package gbaEmu;

public class Memory {
byte[] mainMemory;
CPU cpu;
public Memory() {
mainMemory = new byte[0x10000];
}
byte[] mainMemory;
CPU cpu;
public Memory() {
mainMemory = new byte[0x10000];
}

/**
* Copy the supplied ROM bytes into the memory area starting at 0x0000.
* Only the first 32KB of the ROM are mapped as this emulator does not
* implement any banking logic.
*/
public void loadRom(byte[] romData) {
int len = Math.min(romData.length, 0x8000);
System.arraycopy(romData, 0, mainMemory, 0, len);
}
public byte readMemory(int address) {
return mainMemory[address];
}
Expand All @@ -17,19 +27,19 @@ public void memoryIncrement(int address) {
mainMemory[address]++;
}
// 16 bit stack push
public int stackPush(short value) {
cpu.register.sp--;
writeMemory(cpu.register.sp, (byte) (value >> 8));
cpu.register.sp--;
writeMemory(cpu.register.sp, (byte) (value & 0xFF));
return 0;
}
//16 bit stack pop
public short stackPop() {
cpu.register.sp++;
int low = readMemory(cpu.register.sp);
cpu.register.sp++;
int high = readMemory(cpu.register.sp);
return (short) (high << 8 | low);
}
public int stackPush(short value) {
cpu.register.sp--;
writeMemory(cpu.register.sp & 0xFFFF, (byte) (value >> 8));
cpu.register.sp--;
writeMemory(cpu.register.sp & 0xFFFF, (byte) (value & 0xFF));
return 0;
}
//16 bit stack pop
public short stackPop() {
int low = readMemory(cpu.register.sp & 0xFFFF);
cpu.register.sp++;
int high = readMemory(cpu.register.sp & 0xFFFF);
cpu.register.sp++;
return (short) ((high << 8) | (low & 0xFF));
}
}
58 changes: 34 additions & 24 deletions src/gbaEmu/Opcodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -1117,24 +1117,24 @@ public int OP3B() {
cpu.putSP(cpu.register.sp - 1);
return 0;
}
public int OPCB() {
byte n = cpu.getValue8();
if (cpu.opMap.containsKey(0xCB << 2 + n)) {
Runnable op = cpu.opMap.get(0xCB << 2 + n);
op.run();
} else {
return -1;
}
return cpu.opCycles.get(0xCB << 2 + n);
}
private byte swapByteAndSetFlags(byte a) {
byte res = (byte) (((a & 0xF) << 4) | ((a & 0xF0) >> 4));
cpu.register.nf = false;
cpu.register.hf = false;
cpu.register.hf = false;
cpu.register.zf = res == 0;
return res;
}
public int OPCB() {
byte n = cpu.getValue8();
int op = (0xCB << 8) | (n & 0xFF);
if (cpu.opMap.containsKey(op)) {
Runnable run = cpu.opMap.get(op);
run.run();
return cpu.opCycles.get(op);
}
return -1;
}
private byte swapByteAndSetFlags(byte a) {
byte res = (byte) (((a & 0xF) << 4) | ((a & 0xF0) >> 4));
cpu.register.nf = false;
cpu.register.hf = false;
cpu.register.cf = false;
cpu.register.zf = res == 0;
return res;
}
public int OPCB37() {
cpu.register.a = swapByteAndSetFlags(cpu.register.a);
return 0;
Expand Down Expand Up @@ -1183,12 +1183,22 @@ public int OP3F() {
cpu.register.cf = !cpu.register.cf;
return 0;
}
public int OP37() {
cpu.register.nf = false;
cpu.register.hf = false;
cpu.register.cf = true;
return 0;
}
public int OP37() {
cpu.register.nf = false;
cpu.register.hf = false;
cpu.register.cf = true;
return 0;
}
// HALT - put CPU into low power mode until an interrupt occurs
public int OP76() {
cpu.halt = true;
return 0;
}
// STOP instruction is treated the same as HALT in this simplified emulator
public int OP10() {
cpu.halt = true;
return 0;
}
public int OP00() {
return 0;
}
Expand Down
22 changes: 9 additions & 13 deletions src/gbaEmu/Rom.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,15 @@
import java.util.Arrays;

public class Rom {
byte[] content;
public byte[] readRom(String filename) {
try (InputStream inputStream = new FileInputStream(filename);) {
content = inputStream.readAllBytes();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return content;
}
byte[] content = new byte[0];
public byte[] readRom(String filename) {
try (InputStream inputStream = new FileInputStream(filename)) {
content = inputStream.readAllBytes();
} catch (IOException e) {
throw new RuntimeException("Failed to read ROM file: " + filename, e);
}
return content;
}
public String getTitle() {
String titleString;
if (content.length == 0 ) {
Expand Down
11 changes: 5 additions & 6 deletions src/gbaEmu/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ public static boolean testBit(byte n, int pos) {
assert pos >= 0: "testbit parameter must be positive";
return (n & (1 << pos)) > 0;
}
public static byte clearBit(byte n, int pos) {
assert pos >= 0: "clearbit parameter must be positive";
byte allSetTemplate = (byte) 0xFFFF;
byte clearTemplate = (byte) (((byte) (1 << pos)) ^ allSetTemplate);
return (byte) (pos & clearTemplate);
}
public static byte clearBit(byte n, int pos) {
assert pos >= 0: "clearbit parameter must be positive";
byte clearTemplate = (byte) ~(1 << pos);
return (byte) (n & clearTemplate);
}
public static byte getVal(byte val, int pos) {
return (byte) ((val >> pos) & 1);
}
Expand Down
Loading