summaryrefslogtreecommitdiff
path: root/apps/plugins/rockboy/mem.c
diff options
context:
space:
mode:
authorJens Arnold <amiconn@rockbox.org>2005-03-02 23:49:38 +0000
committerJens Arnold <amiconn@rockbox.org>2005-03-02 23:49:38 +0000
commit384de102469fee4e0792df8fe38586d3206774ed (patch)
treeee5342103e17738acfb8421328ea7c57433f55e6 /apps/plugins/rockboy/mem.c
parent48dad47df98bdec632e8930b6a97359dc2c428f5 (diff)
downloadrockbox-384de102469fee4e0792df8fe38586d3206774ed.zip
rockbox-384de102469fee4e0792df8fe38586d3206774ed.tar.gz
rockbox-384de102469fee4e0792df8fe38586d3206774ed.tar.bz2
rockbox-384de102469fee4e0792df8fe38586d3206774ed.tar.xz
Rockboy - gameboy emulation for rockbox, based on gnuboy. Still a bit early, but already playable on iRiver H1xx and the simulators. The archos recorder version is currently rather slow...
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6104 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/rockboy/mem.c')
-rw-r--r--apps/plugins/rockboy/mem.c578
1 files changed, 578 insertions, 0 deletions
diff --git a/apps/plugins/rockboy/mem.c b/apps/plugins/rockboy/mem.c
new file mode 100644
index 0000000..2e1e0e7
--- /dev/null
+++ b/apps/plugins/rockboy/mem.c
@@ -0,0 +1,578 @@
+
+
+#include "rockmacros.h"
+
+#include "defs.h"
+#include "hw.h"
+#include "regs.h"
+#include "mem.h"
+#include "rtc.h"
+#include "lcd.h"
+#include "lcdc.h"
+#include "sound.h"
+
+struct mbc mbc;
+struct rom rom;
+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;
+ 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 (0 && (R_STAT & 0x03) == 0x03)
+ {
+ map[0x8] = NULL;
+ map[0x9] = NULL;
+ }
+ else
+ {
+ map[0x8] = lcd.vbank[R_VBK & 1] - 0x8000;
+ map[0x9] = lcd.vbank[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);
+ pal_write_dmg(8, 1, b);
+ R_BGP = b;
+ break;
+ case RI_OBP0:
+ if (R_OBP0 == b) break;
+ pal_write_dmg(64, 2, b);
+ R_OBP0 = b;
+ break;
+ case RI_OBP1:
+ if (R_OBP1 == b) break;
+ pal_write_dmg(72, 3, b);
+ 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:
+ stat_write(b);
+ 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;
+ }
+ switch (r)
+ {
+ case RI_BGP:
+ case RI_OBP0:
+ case RI_OBP1:
+ /* printf("palette reg %02X write %02X at LY=%02X\n", r, b, R_LY); */
+ case RI_HDMA1:
+ case RI_HDMA2:
+ case RI_HDMA3:
+ case RI_HDMA4:
+ case RI_HDMA5:
+ /* printf("HDMA %d: %02X\n", r - RI_HDMA1 + 1, b); */
+ break;
+ }
+ /* printf("reg %02X => %02X (%02X)\n", r, REG(r), b); */
+}
+
+
+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:
+ switch (ha & 0xE)
+ {
+ case 0x0:
+ mbc.enableram = ((b & 0x0F) == 0x0A);
+ break;
+ case 0x2:
+ b &= 0x7F;
+ mbc.rombank = b ? b : 1;
+ break;
+ case 0x4:
+ rtc.sel = b & 0x0f;
+ mbc.rambank = b & 0x03;
+ break;
+ case 0x6:
+ rtc_latch(b);
+ break;
+ }
+ break;
+ }
+ mbc.rombank &= (mbc.romsize - 1);
+ mbc.rambank &= (mbc.ramsize - 1);
+ /* printf("%02X\n", mbc.rombank); */
+ 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)
+ {
+ 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 && mbc.type == MBC_HUC3)
+ return 0x01;
+ 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)
+ return sound_read(a & 0xFF);
+ 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();
+}
+
+
+
+
+
+
+