2882b26a99
1) Adapt Rockboy to smaller screens (H10, X5, and iPod Nano). 2) Add the ability to use a preset palette on color targets. Choose 'Set Palette' from the main menu. 3) Clean up the code to remove any unused code and variables. 4) Changed tabs to spaces. 5) Disable reading and writing sound when sound is disabled. 6) Disbable writing to the RTC since it is not implemented yet. 7) Minor optimizations from WAC gnuboy CE and iBoy. 8) Massive clean up of code to make it appear consistent. 9) Change all C++ style comments to C style. 10) Completely reorganize dynarec. Add fixmes to all unimplemented opcodes. Add debug writes for all opcodes. Attempt to implement a few opcodes myself. 11) Silence some warnings when built using dynarec. 12) Minor reshuffling of IRAM, may or not offer a speed increase. 13) Include fixes found in the short-lived gnuboy CVS. All in all, there's about a 10% improvement on my test roms when sound is disabled and slight improvement with sound. Especially noticable when there are few sprites on screen and less action is occurring. See FS #6567. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12216 a1c6a512-1295-4272-9138-f99709370657
571 lines
13 KiB
C
571 lines
13 KiB
C
|
|
|
|
#include "rockmacros.h"
|
|
|
|
#include "defs.h"
|
|
#include "hw.h"
|
|
#include "regs.h"
|
|
#include "mem.h"
|
|
#include "rtc-gb.h"
|
|
#include "lcd-gb.h"
|
|
#include "lcdc.h"
|
|
#include "sound.h"
|
|
|
|
struct mbc mbc IBSS_ATTR;
|
|
struct rom rom IBSS_ATTR;
|
|
struct ram ram;
|
|
|
|
|
|
/*
|
|
* In order to make reads and writes efficient, we keep tables
|
|
* (indexed by the high nibble of the address) specifying which
|
|
* regions can be read/written without a function call. For such
|
|
* ranges, the pointer in the map table points to the base of the
|
|
* region in host system memory. For ranges that require special
|
|
* processing, the pointer is NULL.
|
|
*
|
|
* mem_updatemap is called whenever bank changes or other operations
|
|
* make the old maps potentially invalid.
|
|
*/
|
|
|
|
void mem_updatemap()
|
|
{
|
|
int n;
|
|
static byte **map;
|
|
|
|
map = mbc.rmap;
|
|
map[0x0] = rom.bank[0];
|
|
map[0x1] = rom.bank[0];
|
|
map[0x2] = rom.bank[0];
|
|
map[0x3] = rom.bank[0];
|
|
if (mbc.rombank < mbc.romsize)
|
|
{
|
|
map[0x4] = rom.bank[mbc.rombank] - 0x4000;
|
|
map[0x5] = rom.bank[mbc.rombank] - 0x4000;
|
|
map[0x6] = rom.bank[mbc.rombank] - 0x4000;
|
|
map[0x7] = rom.bank[mbc.rombank] - 0x4000;
|
|
}
|
|
else map[0x4] = map[0x5] = map[0x6] = map[0x7] = NULL;
|
|
if (R_VBK & 1)
|
|
{
|
|
map[0x8] = lcd.vbank[1] - 0x8000;
|
|
map[0x9] = lcd.vbank[1] - 0x8000;
|
|
}
|
|
else
|
|
{
|
|
map[0x8] = lcd.vbank[0]/*[R_VBK & 1]*/ - 0x8000;
|
|
map[0x9] = lcd.vbank[0]/*[R_VBK & 1]*/ - 0x8000;
|
|
|
|
}
|
|
if (mbc.enableram && !(rtc.sel&8))
|
|
{
|
|
map[0xA] = ram.sbank[mbc.rambank] - 0xA000;
|
|
map[0xB] = ram.sbank[mbc.rambank] - 0xA000;
|
|
}
|
|
else map[0xA] = map[0xB] = NULL;
|
|
map[0xC] = ram.ibank[0] - 0xC000;
|
|
n = R_SVBK & 0x07;
|
|
map[0xD] = ram.ibank[n?n:1] - 0xD000;
|
|
map[0xE] = ram.ibank[0] - 0xE000;
|
|
map[0xF] = NULL;
|
|
|
|
map = mbc.wmap;
|
|
map[0x0] = map[0x1] = map[0x2] = map[0x3] = NULL;
|
|
map[0x4] = map[0x5] = map[0x6] = map[0x7] = NULL;
|
|
map[0x8] = map[0x9] = NULL;
|
|
if (mbc.enableram && !(rtc.sel&8))
|
|
{
|
|
map[0xA] = ram.sbank[mbc.rambank] - 0xA000;
|
|
map[0xB] = ram.sbank[mbc.rambank] - 0xA000;
|
|
}
|
|
else map[0xA] = map[0xB] = NULL;
|
|
map[0xC] = ram.ibank[0] - 0xC000;
|
|
n = R_SVBK & 0x07;
|
|
map[0xD] = ram.ibank[n?n:1] - 0xD000;
|
|
map[0xE] = ram.ibank[0] - 0xE000;
|
|
map[0xF] = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* ioreg_write handles output to io registers in the FF00-FF7F,FFFF
|
|
* range. It takes the register number (low byte of the address) and a
|
|
* byte value to be written.
|
|
*/
|
|
|
|
void ioreg_write(byte r, byte b)
|
|
{
|
|
if (!hw.cgb)
|
|
{
|
|
|
|
switch (r)
|
|
{
|
|
case RI_VBK:
|
|
case RI_BCPS:
|
|
case RI_OCPS:
|
|
case RI_BCPD:
|
|
case RI_OCPD:
|
|
case RI_SVBK:
|
|
case RI_KEY1:
|
|
case RI_HDMA1:
|
|
case RI_HDMA2:
|
|
case RI_HDMA3:
|
|
case RI_HDMA4:
|
|
case RI_HDMA5:
|
|
return;
|
|
}
|
|
}
|
|
switch(r)
|
|
{
|
|
case RI_TIMA:
|
|
case RI_TMA:
|
|
case RI_TAC:
|
|
case RI_SCY:
|
|
case RI_SCX:
|
|
case RI_WY:
|
|
case RI_WX:
|
|
REG(r) = b;
|
|
break;
|
|
case RI_BGP:
|
|
if (R_BGP == b) break;
|
|
/* pal_write_dmg(0, 0, b); */ /*Disabled in iBoy and WAC-gnuboyCE */
|
|
/* pal_write_dmg(8, 1, b); */ /*Disabled in iBoy and WAC-gnuboyCE */
|
|
R_BGP = b;
|
|
break;
|
|
case RI_OBP0:
|
|
if (R_OBP0 == b) break;
|
|
/* pal_write_dmg(64, 2, b); */ /*Disabled in iBoy and WAC-gnuboyCE */
|
|
R_OBP0 = b;
|
|
break;
|
|
case RI_OBP1:
|
|
if (R_OBP1 == b) break;
|
|
/* pal_write_dmg(72, 3, b); */ /*Disabled in iBoy and WAC-gnuboyCE */
|
|
R_OBP1 = b;
|
|
break;
|
|
case RI_IF:
|
|
case RI_IE:
|
|
REG(r) = b & 0x1F;
|
|
break;
|
|
case RI_P1:
|
|
REG(r) = b;
|
|
pad_refresh();
|
|
break;
|
|
case RI_SC:
|
|
/* FIXME - this is a hack for stupid roms that probe serial */
|
|
if ((b & 0x81) == 0x81)
|
|
{
|
|
R_SB = 0xff;
|
|
hw_interrupt(IF_SERIAL, IF_SERIAL);
|
|
hw_interrupt(0, IF_SERIAL);
|
|
}
|
|
R_SC = b; /* & 0x7f; */
|
|
break;
|
|
case RI_DIV:
|
|
REG(r) = 0;
|
|
break;
|
|
case RI_LCDC:
|
|
lcdc_change(b);
|
|
break;
|
|
case RI_STAT:
|
|
REG(r) = (REG(r) & 0x07) | (b & 0x78);
|
|
stat_trigger();
|
|
break;
|
|
case RI_LYC:
|
|
REG(r) = b;
|
|
stat_trigger();
|
|
break;
|
|
case RI_VBK:
|
|
REG(r) = b | 0xFE;
|
|
mem_updatemap();
|
|
break;
|
|
case RI_BCPS:
|
|
R_BCPS = b & 0xBF;
|
|
R_BCPD = lcd.pal[b & 0x3F];
|
|
break;
|
|
case RI_OCPS:
|
|
R_OCPS = b & 0xBF;
|
|
R_OCPD = lcd.pal[64 + (b & 0x3F)];
|
|
break;
|
|
case RI_BCPD:
|
|
R_BCPD = b;
|
|
pal_write(R_BCPS & 0x3F, b);
|
|
if (R_BCPS & 0x80) R_BCPS = (R_BCPS+1) & 0xBF;
|
|
break;
|
|
case RI_OCPD:
|
|
R_OCPD = b;
|
|
pal_write(64 + (R_OCPS & 0x3F), b);
|
|
if (R_OCPS & 0x80) R_OCPS = (R_OCPS+1) & 0xBF;
|
|
break;
|
|
case RI_SVBK:
|
|
REG(r) = b & 0x07;
|
|
mem_updatemap();
|
|
break;
|
|
case RI_DMA:
|
|
hw_dma(b);
|
|
break;
|
|
case RI_KEY1:
|
|
REG(r) = (REG(r) & 0x80) | (b & 0x01);
|
|
break;
|
|
case RI_HDMA1:
|
|
REG(r) = b;
|
|
break;
|
|
case RI_HDMA2:
|
|
REG(r) = b & 0xF0;
|
|
break;
|
|
case RI_HDMA3:
|
|
REG(r) = b & 0x1F;
|
|
break;
|
|
case RI_HDMA4:
|
|
REG(r) = b & 0xF0;
|
|
break;
|
|
case RI_HDMA5:
|
|
hw_hdma_cmd(b);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
byte ioreg_read(byte r)
|
|
{
|
|
switch(r)
|
|
{
|
|
case RI_SC:
|
|
r = R_SC;
|
|
R_SC &= 0x7f;
|
|
return r;
|
|
case RI_P1:
|
|
case RI_SB:
|
|
case RI_DIV:
|
|
case RI_TIMA:
|
|
case RI_TMA:
|
|
case RI_TAC:
|
|
case RI_LCDC:
|
|
case RI_STAT:
|
|
case RI_SCY:
|
|
case RI_SCX:
|
|
case RI_LY:
|
|
case RI_LYC:
|
|
case RI_BGP:
|
|
case RI_OBP0:
|
|
case RI_OBP1:
|
|
case RI_WY:
|
|
case RI_WX:
|
|
case RI_IE:
|
|
case RI_IF:
|
|
return REG(r);
|
|
case RI_VBK:
|
|
case RI_BCPS:
|
|
case RI_OCPS:
|
|
case RI_BCPD:
|
|
case RI_OCPD:
|
|
case RI_SVBK:
|
|
case RI_KEY1:
|
|
case RI_HDMA1:
|
|
case RI_HDMA2:
|
|
case RI_HDMA3:
|
|
case RI_HDMA4:
|
|
case RI_HDMA5:
|
|
if (hw.cgb) return REG(r);
|
|
default:
|
|
return 0xff;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Memory bank controllers typically intercept write attempts to
|
|
* 0000-7FFF, using the address and byte written as instructions to
|
|
* change rom or sram banks, control special hardware, etc.
|
|
*
|
|
* mbc_write takes an address (which should be in the proper range)
|
|
* and a byte value written to the address.
|
|
*/
|
|
|
|
void mbc_write(int a, byte b)
|
|
{
|
|
byte ha = (a>>12);
|
|
|
|
/* printf("mbc %d: rom bank %02X -[%04X:%02X]-> ", mbc.type, mbc.rombank, a, b); */
|
|
switch (mbc.type)
|
|
{
|
|
case MBC_MBC1:
|
|
switch (ha & 0xE)
|
|
{
|
|
case 0x0:
|
|
mbc.enableram = ((b & 0x0F) == 0x0A);
|
|
break;
|
|
case 0x2:
|
|
if ((b & 0x1F) == 0) b = 0x01;
|
|
mbc.rombank = (mbc.rombank & 0x60) | (b & 0x1F);
|
|
break;
|
|
case 0x4:
|
|
if (mbc.model)
|
|
{
|
|
mbc.rambank = b & 0x03;
|
|
break;
|
|
}
|
|
mbc.rombank = (mbc.rombank & 0x1F) | ((int)(b&3)<<5);
|
|
break;
|
|
case 0x6:
|
|
mbc.model = b & 0x1;
|
|
break;
|
|
}
|
|
break;
|
|
case MBC_MBC2: /* is this at all right? */
|
|
if ((a & 0x0100) == 0x0000)
|
|
{
|
|
mbc.enableram = ((b & 0x0F) == 0x0A);
|
|
break;
|
|
}
|
|
if ((a & 0xE100) == 0x2100)
|
|
{
|
|
mbc.rombank = b & 0x0F;
|
|
break;
|
|
}
|
|
break;
|
|
case MBC_MBC3:
|
|
switch (ha & 0xE)
|
|
{
|
|
case 0x0:
|
|
mbc.enableram = ((b & 0x0F) == 0x0A);
|
|
break;
|
|
case 0x2:
|
|
if ((b & 0x7F) == 0) b = 0x01;
|
|
mbc.rombank = b & 0x7F;
|
|
break;
|
|
case 0x4:
|
|
rtc.sel = b & 0x0f;
|
|
mbc.rambank = b & 0x03;
|
|
break;
|
|
case 0x6:
|
|
rtc_latch(b);
|
|
break;
|
|
}
|
|
break;
|
|
case MBC_RUMBLE:
|
|
switch (ha & 0xF)
|
|
{
|
|
case 0x4:
|
|
case 0x5:
|
|
/* FIXME - save high bit as rumble state */
|
|
/* mask off high bit */
|
|
b &= 0x7;
|
|
break;
|
|
}
|
|
/* fall thru */
|
|
case MBC_MBC5:
|
|
switch (ha & 0xF)
|
|
{
|
|
case 0x0:
|
|
case 0x1:
|
|
mbc.enableram = ((b & 0x0F) == 0x0A);
|
|
break;
|
|
case 0x2:
|
|
if ((b & 0xFF) == 0) b = 0x01;
|
|
mbc.rombank = (mbc.rombank & 0x100) | (b & 0xFF);
|
|
break;
|
|
case 0x3:
|
|
mbc.rombank = (mbc.rombank & 0xFF) | ((int)(b&1)<<8);
|
|
break;
|
|
case 0x4:
|
|
case 0x5:
|
|
mbc.rambank = b & 0x0f;
|
|
break;
|
|
}
|
|
break;
|
|
case MBC_HUC1: /* FIXME - this is all guesswork -- is it right??? */
|
|
switch (ha & 0xE)
|
|
{
|
|
case 0x0:
|
|
mbc.enableram = ((b & 0x0F) == 0x0A);
|
|
break;
|
|
case 0x2:
|
|
if ((b & 0x1F) == 0) b = 0x01;
|
|
mbc.rombank = (mbc.rombank & 0x60) | (b & 0x1F);
|
|
break;
|
|
case 0x4:
|
|
if (mbc.model)
|
|
{
|
|
mbc.rambank = b & 0x03;
|
|
break;
|
|
}
|
|
mbc.rombank = (mbc.rombank & 0x1F) | ((int)(b&3)<<5);
|
|
break;
|
|
case 0x6:
|
|
mbc.model = b & 0x1;
|
|
break;
|
|
}
|
|
break;
|
|
case MBC_HUC3: /* FIXME - this is all guesswork -- is it right??? */
|
|
switch (ha & 0xE)
|
|
{
|
|
case 0x0:
|
|
mbc.enableram = ((b & 0x0F) == 0x0A);
|
|
break;
|
|
case 0x2:
|
|
if (!b) b = 1;
|
|
mbc.rombank = b;
|
|
break;
|
|
case 0x4:
|
|
if (mbc.model)
|
|
{
|
|
mbc.rambank = b & 0x03;
|
|
break;
|
|
}
|
|
break;
|
|
case 0x6:
|
|
mbc.model = b & 1;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
mbc.rombank &= (mbc.romsize - 1);
|
|
mbc.rambank &= (mbc.ramsize - 1);
|
|
mem_updatemap();
|
|
}
|
|
|
|
|
|
/*
|
|
* mem_write is the basic write function. Although it should only be
|
|
* called when the write map contains a NULL for the requested address
|
|
* region, it accepts writes to any address.
|
|
*/
|
|
|
|
void mem_write(int a, byte b)
|
|
{
|
|
int n;
|
|
byte ha = (a>>12) & 0xE;
|
|
|
|
/* printf("write to 0x%04X: 0x%02X\n", a, b); */
|
|
switch (ha)
|
|
{
|
|
case 0x0:
|
|
case 0x2:
|
|
case 0x4:
|
|
case 0x6:
|
|
mbc_write(a, b);
|
|
break;
|
|
case 0x8:
|
|
/* if ((R_STAT & 0x03) == 0x03) break; */
|
|
vram_write(a & 0x1FFF, b);
|
|
break;
|
|
case 0xA:
|
|
if (!mbc.enableram) break;
|
|
if (rtc.sel&8)
|
|
{
|
|
rtc_write(b);
|
|
break;
|
|
}
|
|
ram.sbank[mbc.rambank][a & 0x1FFF] = b;
|
|
break;
|
|
case 0xC:
|
|
if ((a & 0xF000) == 0xC000)
|
|
{
|
|
ram.ibank[0][a & 0x0FFF] = b;
|
|
break;
|
|
}
|
|
n = R_SVBK & 0x07;
|
|
ram.ibank[n?n:1][a & 0x0FFF] = b;
|
|
break;
|
|
case 0xE:
|
|
if (a < 0xFE00)
|
|
{
|
|
mem_write(a & 0xDFFF, b);
|
|
break;
|
|
}
|
|
if ((a & 0xFF00) == 0xFE00)
|
|
{
|
|
/* if (R_STAT & 0x02) break; */
|
|
if (a < 0xFEA0) lcd.oam.mem[a & 0xFF] = b;
|
|
break;
|
|
}
|
|
/* return writehi(a & 0xFF, b); */
|
|
if (a >= 0xFF10 && a <= 0xFF3F)
|
|
{
|
|
if(options.sound)
|
|
sound_write(a & 0xFF, b);
|
|
break;
|
|
}
|
|
if ((a & 0xFF80) == 0xFF80 && a != 0xFFFF)
|
|
{
|
|
ram.hi[a & 0xFF] = b;
|
|
break;
|
|
}
|
|
ioreg_write(a & 0xFF, b);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* mem_read is the basic read function...not useful for much anymore
|
|
* with the read map, but it's still necessary for the final messy
|
|
* region.
|
|
*/
|
|
|
|
byte mem_read(int a)
|
|
{
|
|
int n;
|
|
byte ha = (a>>12) & 0xE;
|
|
|
|
/* printf("read %04x\n", a); */
|
|
switch (ha)
|
|
{
|
|
case 0x0:
|
|
case 0x2:
|
|
return rom.bank[0][a];
|
|
case 0x4:
|
|
case 0x6:
|
|
return rom.bank[mbc.rombank][a & 0x3FFF];
|
|
case 0x8:
|
|
/* if ((R_STAT & 0x03) == 0x03) return 0xFF; */
|
|
return lcd.vbank[R_VBK&1][a & 0x1FFF];
|
|
case 0xA:
|
|
if (!mbc.enableram)
|
|
return 0xFF;
|
|
if (rtc.sel&8)
|
|
return rtc.regs[rtc.sel&7];
|
|
return ram.sbank[mbc.rambank][a & 0x1FFF];
|
|
case 0xC:
|
|
if ((a & 0xF000) == 0xC000)
|
|
return ram.ibank[0][a & 0x0FFF];
|
|
n = R_SVBK & 0x07;
|
|
return ram.ibank[n?n:1][a & 0x0FFF];
|
|
case 0xE:
|
|
if (a < 0xFE00) return mem_read(a & 0xDFFF);
|
|
if ((a & 0xFF00) == 0xFE00)
|
|
{
|
|
/* if (R_STAT & 0x02) return 0xFF; */
|
|
if (a < 0xFEA0) return lcd.oam.mem[a & 0xFF];
|
|
return 0xFF;
|
|
}
|
|
/* return readhi(a & 0xFF); */
|
|
if (a == 0xFFFF) return REG(0xFF);
|
|
if (a >= 0xFF10 && a <= 0xFF3F)
|
|
{
|
|
if(options.sound)
|
|
return sound_read(a & 0xFF);
|
|
else
|
|
return 1;
|
|
}
|
|
if ((a & 0xFF80) == 0xFF80)
|
|
return ram.hi[a & 0xFF];
|
|
return ioreg_read(a & 0xFF);
|
|
}
|
|
return 0xff; /* not reached */
|
|
}
|
|
|
|
void mbc_reset(void)
|
|
{
|
|
mbc.rombank = 1;
|
|
mbc.rambank = 0;
|
|
mbc.enableram = 0;
|
|
mem_updatemap();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|