/** Execute the specified number of Gameboy instructions. Use '-1' to execute forever */ public final void execute(int numInstr) { terminate = false; running = true; graphicsChip.startTime = System.currentTimeMillis(); int b1, b2, b3, offset; long t; for (int r = 0; (r != numInstr) && (!terminate); r++) { t = System.currentTimeMillis(); instrCount++; b1 = JavaBoy.unsign(addressRead(pc)); offset = addressRead(pc + 1); b3 = JavaBoy.unsign(addressRead(pc + 2)); b2 = JavaBoy.unsign((short) offset); // stats.addExecution(b1); instructionManager.execute(b1, b2, b3, offset); if (ieDelay != -1) { if (ieDelay > 0) { ieDelay--; } else { interruptsEnabled = true; ieDelay = -1; } } if (interruptsEnabled) { checkInterrupts(); } cartridge.update(); initiateInterrupts(); if ((t - initialTime) > checkpointTime) { initialTime = t; saveCheckpointInterrupt = true; } if (saveInterrupt) { saveState(".stsv"); saveInterrupt = false; } if (loadStateInterrupt) { loadState(".stsv"); loadStateInterrupt = false; } if (saveCheckpointInterrupt) { saveState(".cksv"); saveCheckpointInterrupt = false; } if (loadCheckpointInterrupt) { loadState(".cksv"); loadCheckpointInterrupt = false; } } running = false; terminate = false; }
/** Resets the CPU to it's power on state. Memory contents are not cleared. */ public void reset() { checkEnableGbc(); setDoubleSpeedCpu(false); graphicsChip.dispose(); cartridge.reset(); interruptsEnabled = false; ieDelay = -1; pc = 0x0100; sp = 0xFFFE; f = 0xB0; gbcRamBank = 1; instrCount = 0; if (gbcFeatures) { registers[a] = 0x11; } else { registers[a] = 0x01; } for (int r = 0; r < 0x8000; r++) { mainRam[r] = 0; } setBC(0x0013); setDE(0x00D8); setHL(0x014D); JavaBoy.debugLog("CPU reset"); ioHandler.reset(); }
public void saveState(String extension) { String directory = (cartridge.romFileName + extension); try { FileOutputStream fl = new FileOutputStream(directory); DataOutputStream sv = new DataOutputStream(fl); saveData(sv, directory); // write battery ram cartridge.saveData(sv, directory); // write graphic memory graphicsChip.saveData(sv, directory); // write io state ioHandler.saveData(sv, directory); // stats.printStats(); sv.close(); fl.close(); } catch (FileNotFoundException e) { System.out.println("Dmgcpu.saveState: Could not open file " + directory); System.out.println("Error Message: " + e.getMessage()); System.exit(-1); } catch (IOException e) { System.out.println("Dmgcpu.saveState: Could not write to file " + directory); System.out.println("Error Message: " + e.getMessage()); System.exit(-1); } System.out.println("Saved stage!"); }
/** * Performs a CPU address space write. Maps all of the relevant object into the right parts of * memory. */ public final void addressWrite(int addr, int data) { switch (addr & 0xF000) { case 0x0000: case 0x1000: case 0x2000: case 0x3000: case 0x4000: case 0x5000: case 0x6000: case 0x7000: if (!running) { cartridge.debuggerAddressWrite(addr, data); } else { cartridge.addressWrite(addr, data); } break; case 0x8000: case 0x9000: graphicsChip.addressWrite(addr - 0x8000, (byte) data); break; case 0xA000: case 0xB000: cartridge.addressWrite(addr, data); break; case 0xC000: mainRam[addr - 0xC000] = (byte) data; break; case 0xD000: mainRam[addr - 0xD000 + (gbcRamBank * 0x1000)] = (byte) data; break; case 0xE000: mainRam[addr - 0xE000] = (byte) data; break; case 0xF000: if (addr < 0xFE00) { try { mainRam[addr - 0xE000] = (byte) data; } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Address error: " + addr + " pc = " + JavaBoy.hexWord(pc)); } } else if (addr < 0xFF00) { oam[addr - 0xFE00] = (byte) data; } else { ioHandler.ioWrite(addr - 0xFF00, (short) data); } break; } }
/** * Perform a CPU address space read. This maps all the relevant objects into the correct parts of * the memory */ public final short addressRead(int addr) { addr = addr & 0xFFFF; switch ((addr & 0xF000)) { case 0x0000: case 0x1000: case 0x2000: case 0x3000: case 0x4000: case 0x5000: case 0x6000: case 0x7000: return cartridge.addressRead(addr); case 0x8000: case 0x9000: return graphicsChip.addressRead(addr - 0x8000); case 0xA000: case 0xB000: return cartridge.addressRead(addr); case 0xC000: return (mainRam[addr - 0xC000]); case 0xD000: return (mainRam[addr - 0xD000 + (gbcRamBank * 0x1000)]); case 0xE000: return mainRam[addr - 0xE000]; case 0xF000: if (addr < 0xFE00) { return mainRam[addr - 0xE000]; } else if (addr < 0xFF00) { return (short) (oam[addr - 0xFE00] & 0x00FF); } else { return ioHandler.ioRead(addr - 0xFF00); } default: System.out.println("Tried to read address " + addr + ". pc = " + JavaBoy.hexWord(pc)); return 0xFF; } }
public void loadState(String extension) { String directory = cartridge.romFileName + extension; try { reset(); FileInputStream fl = new FileInputStream(directory); DataInputStream sv = new DataInputStream(fl); // write cpu data loadData(sv, directory); // write battery ram cartridge.loadData(sv, directory); // write graphic memory graphicsChip.loadData(sv, directory); // writes io state ioHandler.loadData(sv, directory); sv.close(); fl.close(); } catch (FileNotFoundException ex) { System.out.println("Dmgcpu.loadState: Could not open file " + directory); System.out.println("Error Message: " + ex.getMessage()); System.exit(-1); } catch (IOException ex) { System.out.println("Dmgcpu.loadState: Could not read file " + directory); System.out.println("Error Message: " + ex.getMessage()); System.exit(-1); } System.out.println("Loaded stage!"); }
/** Check for interrupts that need to be initiated */ public final void initiateInterrupts() { if (timaEnabled && ((instrCount % instrsPerTima) == 0)) { if (JavaBoy.unsign(ioHandler.registers[05]) == 0) { ioHandler.registers[05] = ioHandler.registers[06]; // Set TIMA // modulo if ((ioHandler.registers[0xFF] & INT_TIMA) != 0) triggerInterrupt(INT_TIMA); } ioHandler.registers[05]++; } if ((instrCount % INSTRS_PER_DIV) == 0) { ioHandler.registers[04]++; } if ((instrCount % INSTRS_PER_HBLANK) == 0) { // LCY Coincidence // The +1 is due to the LCY register being just about to be incremented int cline = JavaBoy.unsign(ioHandler.registers[0x44]) + 1; if (cline == 152) cline = 0; if (((ioHandler.registers[0xFF] & INT_LCDC) != 0) && ((ioHandler.registers[0x41] & 64) != 0) && (JavaBoy.unsign(ioHandler.registers[0x45]) == cline) && ((ioHandler.registers[0x40] & 0x80) != 0) && (cline < 0x90)) { triggerInterrupt(INT_LCDC); } // Trigger on every line if (((ioHandler.registers[0xFF] & INT_LCDC) != 0) && ((ioHandler.registers[0x41] & 0x8) != 0) && ((ioHandler.registers[0x40] & 0x80) != 0) && (cline < 0x90)) { triggerInterrupt(INT_LCDC); } if ((gbcFeatures) && (ioHandler.hdmaRunning)) { ioHandler.performHdma(); } if (JavaBoy.unsign(ioHandler.registers[0x44]) == 143) { for (int r = 144; r < 170; r++) { graphicsChip.notifyScanline(r); } if (((ioHandler.registers[0x40] & 0x80) != 0) && ((ioHandler.registers[0xFF] & INT_VBLANK) != 0)) { triggerInterrupt(INT_VBLANK); if (((ioHandler.registers[0x41] & 16) != 0) && ((ioHandler.registers[0xFF] & INT_LCDC) != 0)) { triggerInterrupt(INT_LCDC); } } boolean speedThrottle = true; if (!JavaBoy.runningAsApplet) { GameBoyScreen g = (GameBoyScreen) applet; speedThrottle = g.viewSpeedThrottle.getState(); } if ((speedThrottle) && (graphicsChip.frameWaitTime >= 0)) { try { java.lang.Thread.sleep(graphicsChip.frameWaitTime); } catch (InterruptedException e) { // Nothing. } } } graphicsChip.notifyScanline(JavaBoy.unsign(ioHandler.registers[0x44])); ioHandler.registers[0x44] = (byte) (JavaBoy.unsign(ioHandler.registers[0x44]) + 1); if (JavaBoy.unsign(ioHandler.registers[0x44]) >= 153) { ioHandler.registers[0x44] = 0; if (soundChip != null) soundChip.outputSound(); graphicsChip.frameDone = false; if (JavaBoy.runningAsApplet) { ((JavaBoy) (applet)).drawNextFrame(); } else { ((GameBoyScreen) (applet)).repaint(); } try { while (!graphicsChip.frameDone) { java.lang.Thread.sleep(1); } } catch (InterruptedException e) { // Nothing. } } } }
/** Clear up memory */ public void dispose() { graphicsChip.dispose(); }