📄 pgbmemory.java
字号:
/**
* this source file released under the GNU Public Licence.
* see the accompanying copyright.txt for more information.
* Copyright (C) 2000-2001 Ben Mazur
* modified by retroK, XTale and baka0815 2004 http://aepgb.aep-emu.de/
*/
/**
* PgbMemory controls the entire memory map of the Gameboy.
*
* It also contains some executing code, mostly in cycle(),
* which is responsible for controlling the hardware and
* setting the interrupt flag each CPU cycle.
*
* Also, DMA functions are handled here and SGB calls are
* interpreted here and then (usually) passed on to the
* video hardware.
*/
public final class PgbMemory {
public PgbCart cart;
public PgbVideo video;
public PgbJoypad joy;
public PgbNetplay net;
// by retroK
public SoundChip soundChip;
boolean soundOn;
byte soundIO[];
// end retroK
public static final byte INT_JOYPAD = 0x10;
public static final byte INT_SERIAL = 0x08;
public static final byte INT_TIMER = 0x04;
public static final byte INT_LCD = 0x02;
public static final byte INT_VBLANK = 0x01;
protected byte[] hiRAM;
protected byte[] loRAM;
public byte IF;
public byte IE;
public int tima;
public int tma;
public byte tac;
protected boolean timerOn;
protected int timeCounter;
protected int timeLimit;
public int div;
// how many cycles left until something happens?
public int cycles;
public int cyclesLeft;
public int cyclesSkipped;
// Super Gameboy stuff
protected int sgbBitCounter;
protected int sgbPacketCounter;
protected byte[] sgbBuffer;
protected boolean sgbListening;
protected int sgbCommand;
protected int sgbPackets;
// gameboy color
byte gbcRAM;
int loRAMOffset;
byte gbcSpeed;
byte rHDMA1;
byte rHDMA2;
byte rHDMA3;
byte rHDMA4;
int hdmaSrc;
int hdmaDst;
boolean hdmaDone;
byte hdmaLastMode;
int hdmaStop;
public PgbMemory(
PgbCart cart,
PgbVideo video,
PgbJoypad joy,
PgbNetplay net) {
this.cart = cart;
this.video = video;
this.joy = joy;
this.net = net;
hiRAM = new byte[0x80];
loRAM = new byte[0x8000];
sgbBuffer = new byte[0x80]; // max 8 packets for now
// by retroK add sound
soundOn = true;
soundChip = new SoundChip();
soundIO = new byte[64];
// end by retroK
reset();
}
// by retroK:
public void soundPlay() {
soundChip.outputSound();
}
public int unsign(byte b) {
return (b & 0xff);
}
public void reset() {
tima = 0;
tma = 0;
tac = 0;
timerOn = false;
timeCounter = 0;
timeLimit = 0;
div = 0xAF * 0x100;
IF = 1;
IE = 0;
sgbBitCounter = 0;
sgbPacketCounter = 0;
sgbListening = false;
sgbPackets = 1;
gbcRAM = (byte) 0x00;
loRAMOffset = 0xC000;
gbcSpeed = (byte) 0x7E;
cycles = 0;
cyclesLeft = Integer.MAX_VALUE;
recalcCyclesLeft();
hdmaDone = true;
hdmaLastMode = PgbVideo.STAT_HBLANK;
// by retroK: reset sound
soundIO[16] = -128;
soundIO[17] = -65;
soundIO[18] = -13;
soundIO[20] = -65;
soundIO[22] = 63;
soundIO[23] = 0;
soundIO[25] = -65;
soundIO[26] = 127;
soundIO[27] = -1;
soundIO[28] = -97;
soundIO[30] = -65;
soundIO[32] = -1;
soundIO[33] = 0;
soundIO[34] = 0;
soundIO[35] = -65;
soundIO[36] = 119;
soundIO[37] = -13;
soundIO[38] = -15;
}
/**
* Recaclulates the number of cycles left. Called
* when the timer, LCDC, LCY, or serial control is
* changed.
*/
public void recalcCyclesLeft() {
cyclesLeft = Integer.MAX_VALUE;
// check cycles until timer
if (timerOn && (timeLimit - timeCounter) < cyclesLeft) {
cyclesLeft = (timeLimit - timeCounter);
}
// check cycles until next video mode switch
if (video.lcd_on && video.cyclesLeft() < cyclesLeft) {
cyclesLeft = video.cyclesLeft();
}
// check cycles until next serial check
if (net.cyclesLeft() < cyclesLeft) {
cyclesLeft = net.cyclesLeft();
}
// of course, this can't guess when a joystick
// interrupt will occur.
}
/**
* Advances the state of the hardware as though the number
* of cpu cycles specified had passed and sets the interrupt
* register appropriately.
*
* @param cv the number of cpu cycles to advance.
*/
public final void cycle(int cv) {
cycles += cv;
if (cycles > cyclesLeft) {
// divider register (is this right at all?)
// (does anything really use the divider register?)
div -= cyclesLeft;
if (div < 0) {
div = 0xFF * 0x100;
}
// timer (does timer run w/o timer interrupt enabled?)
if (timerOn) { // yes?
timeCounter += cyclesLeft;
if (timeCounter >= timeLimit) {
timeCounter = 0;
tima++;
}
if (tima > 255) {
tima = tma;
IF |= INT_TIMER;
}
}
// netplay cycle
/*if(true || (IE & INT_SERIAL) == INT_SERIAL)*/ {
IF |= net.cycle(cyclesLeft);
}
// video cycle
if (video.lcd_on) {
IF |= video.cycle(cyclesLeft, this);
}
// hdma transfer
if (!hdmaDone) {
if (hdmaLastMode != PgbVideo.STAT_HBLANK
&& (byte) (video.getStat() & 0x03) == PgbVideo.STAT_HBLANK) {
// transfer 16 bytes
write(hdmaDst++, read(hdmaSrc++));
write(hdmaDst++, read(hdmaSrc++));
write(hdmaDst++, read(hdmaSrc++));
write(hdmaDst++, read(hdmaSrc++));
write(hdmaDst++, read(hdmaSrc++));
write(hdmaDst++, read(hdmaSrc++));
write(hdmaDst++, read(hdmaSrc++));
write(hdmaDst++, read(hdmaSrc++));
write(hdmaDst++, read(hdmaSrc++));
write(hdmaDst++, read(hdmaSrc++));
write(hdmaDst++, read(hdmaSrc++));
write(hdmaDst++, read(hdmaSrc++));
write(hdmaDst++, read(hdmaSrc++));
write(hdmaDst++, read(hdmaSrc++));
write(hdmaDst++, read(hdmaSrc++));
write(hdmaDst++, read(hdmaSrc++));
if (hdmaDst >= hdmaStop) {
hdmaDone = true;
}
}
// eat up the rest of the hblank
//IF |= video.cycle(video.cycles);
hdmaLastMode = (byte) (video.getStat() & 0x03);
}
cycles -= cyclesLeft;
recalcCyclesLeft();
}
}
/**
* Set the timer control.
*/
public void setTac(byte tac) {
timerOn = (tac & 0x04) == 0x04;
timeCounter = 0;
switch (tac & 0x03) {
case 0 :
timeLimit = 1024;
break;
case 1 :
timeLimit = 16;
break;
case 2 :
timeLimit = 64;
break;
case 3 :
timeLimit = 256;
break;
}
//recalcCyclesLeft();
}
/**
* Reads (signed) bytes from GB memory.
*
* @param address the gameboy memory address to read from.
*/
public final byte read(int address) {
switch (address) {
// Joypad Register (R/W)
case 0xFF00 :
return joy.read();
// Serial Transfer Data (R/W)
case 0xFF01 :
return net.getSerialData();
// SIO control (R/W)
case 0xFF02 :
return net.getSerialControl();
// empty GB registers?
case 0xFF03 :
return 0;
// Divider Register (R/W)
case 0xFF04 :
// System.out.println("read Divider Register:" + (byte)(div / 0x100));
return (byte) (div / 0x100);
// Timer counter (R/W)
case 0xFF05 :
//System.out.println("read Timer counter:" + tima);
return (byte) tima;
// Timer Modulo (R/W)
case 0xFF06 :
return (byte) tma;
// Timer Control
case 0xFF07 :
return (byte) tac;
// Interrupt Flag (R/W)
case 0xFF0F :
return IF;
// LCD Control (R/W)
case 0xFF40 :
return video.getLcdc();
// LCDC Status (R/W)
case 0xFF41 :
return video.getStat();
// Scroll Y (R/W)
case 0xFF42 :
return (byte) video.scy;
// Scroll X (R/W)
case 0xFF43 :
return (byte) video.scx;
// LCDC Y-Coordinate (R)
case 0xFF44 :
return (byte) video.ly;
// LY Compare (R/W)
case 0xFF45 :
return (byte) video.lyc;
// BG & Window Palette Data (R/W)
case 0xFF47 :
return (byte) video.bgpal;
// Object Palette 0 Data (R/W)
case 0xFF48 :
return (byte) video.objpal0;
// Object Palette 1 Data (R/W)
case 0xFF49 :
return (byte) video.objpal1;
// Window Y Position (R/W)
case 0xFF4A :
return (byte) video.wy;
// Window X Position (R/W)
case 0xFF4B :
return (byte) video.wx;
// GBC CPU Speed (R/W)
case 0xFF4D :
return gbcGetSpeed();
// GBC VRAM bank
case 0xFF4F :
//System.out.println("read GBC VRAM bank:" + Integer.toHexString(address));
return video.gbcGetVram();
// GBC rHDMA5 (DMA Mode / Control)
case 0xFF55 :
//System.out.println("read GBC rHDMA5 (DMA Mode / Control):" + Integer.toHexString(getHDMAControl() & 0xFF));
return getHDMAControl();
// GBC IR port (R/W)
case 0xFF56 :
return net.getIR();
// Color BG Palette Index (W)
case 0xFF68 :
return video.gbcGetBgpi();
// Color BG Palette Data (W)
case 0xFF69 :
return video.gbcGetBgpd();
// Color OBJ Palette Index (W)
case 0xFF6A :
return video.gbcGetObpi();
// Color OBJ Palette Data (W)
case 0xFF6B :
return video.gbcGetObpd();
//GBC RamBaml
case 0xFF70:
return gbcGetRamBank();
//whats this?
case 0xFF7F:
return 0;
// Interrupt Enable (R/W)
case 0xFFFF:
return IE;
}
// cart ROM
if (address < 0x8000) {
return cart.read(address);
}
// VRAM
if (address < 0xA000) {
return video.read(address);
}
// cart RAM
if (address < 0xC000) {
return cart.read(address);
}
// internal (low) RAM bank 0
if (address < 0xD000) {
return loRAM[address - 0xC000];
}
// internal (low) RAM bank 1+
if (address < 0xE000) {
return loRAM[address - loRAMOffset];
}
// echo RAM
if (address < 0xFE00) {
return loRAM[address - 0xE000];
}
// Object Attribute Memory (OAM)
if (address < 0xFEA0) {
return video.read(address);
}
// empty GB registers? //XXX bis FF80 IO register?
if (address < 0xFF0F) {
return 0;
}
// added by retroK sound:
if (address < 0xFF40) {
//some games look for this satus register, just set them correctly before returning value
if ((address & 0xff) == 38) {
if (soundChip.channel1.getLength() <= 0) soundIO[38] &= ~ 0x01; else soundIO[38] |= 0x01;
if (soundChip.channel2.getLength() <= 0) soundIO[38] &= ~ 0x02; else soundIO[38] |= 0x02;
if (soundChip.channel3.getLength() <= 0) soundIO[38] &= ~ 0x04; else soundIO[38] |= 0x04;
if (soundChip.channel4.getLength() <= 0) soundIO[38] &= ~ 0x08; else soundIO[38] |= 0x08;
}
return soundIO[address & 0xff];
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -