package com.pixelutilitys.arcade.emulators.AEPgb; /** * this source file released under the GNU Public Licence. * see the accompanying copyright.txt for more information * Copyright (C) 2000-2001 Ben Mazur */ /** * PgbCachedIndexedVideo is a subclass of PgbIndexedVideo * that keeps copies of the tiles, background maps and * sprites cached. * * It should be faster in most circumstances, as it does not * go back to the memory every time a tile is accessed. * Particularly, it does not have to blend the two display * bytes together for each line in the tile. */ public class PgbCachedVideo extends PgbBasicVideo { // here's the memory byte[][][][] tiles; byte[][][] map; byte[][][] sprites; int lastTile; public PgbCachedVideo() { tiles = new byte[2][384][8][8]; // [bank][num][y][x] map = new byte[4][256][256]; // map, y, x sprites = new byte[40][16][8]; } /** * the heart of the operation. monitors VRAM writes * and updates the appropriate caches */ public void write(int address, byte towrite) { super.write(address, towrite); // tile storage if(address >= 0x8000 && address < 0x9800) { // this updates tiles in almost all circumstances checkTile(address); } // tile map at 9800 else if(address >= 0x9800 && address < 0x9C00) { updateMap(0, (address - 0x9800) & 0x1F, (address - 0x9800) >> 5, vram[address - 0x8000] + 256); updateMap(1, (address - 0x9800) & 0x1F, (address - 0x9800) >> 5, vram[address - 0x8000] & 0xFF); } // tile map at 9C00 else if(address >= 0x9C00 && address < 0xA000) { updateMap(2, (address - 0x9C00) & 0x1F, (address - 0x9C00) >> 5, vram[address - 0x8000] + 256); updateMap(3, (address - 0x9C00) & 0x1F, (address - 0x9C00) >> 5, vram[address - 0x8000] & 0xFF); } // OAM else if(address >= 0xFE00 && address < 0xFEA0) { if((address & 0x02) == 0x02) { updateObj((address - 0xFE00) >> 2); } } } /** * override to draw from the cache */ void doBgLine(int line) { int bgline, middle; byte[] srcmap; bgline = (line + scy) & 0xFF; srcmap = map[(chr_mode ? 1 : 0) + (bg_mode ? 2 : 0)][bgline]; if(scx < 97) { System.arraycopy(srcmap, scx, screenMemory, line * 160, 160); } else { middle = 256 - scx; System.arraycopy(srcmap, scx, screenMemory, line * 160, middle); System.arraycopy(srcmap, 0, screenMemory, line * 160 + middle, 160 - middle); } } /** * override to draw from the cache */ void doWinLine(int line) { if(wy > line || wx > 166) { return; } byte[] srcmap = map[(chr_mode ? 1 : 0) + (win_mode ? 2 : 0)][line - wy]; int ws, ss, wl; if(wx < 7) { ws = 7 - wx; ss = 0; wl = 160 - ws; } else { ws = 0; ss = wx - 7; wl = 160 - ss; } System.arraycopy(srcmap, ws, screenMemory, line * 160 + ss, wl); } /** * override to draw from cache */ void doObjLine(int line) { int s, so, sx, sy; for(s = 39; s >= 0; s--) { so = s * 4; sx = oam[so + 1] & 0xFF; sy = oam[so] & 0xFF; if((sx > 0 && sy > 0) && sy <= line + 16 && sy > line + (16 - obj_siz)) { // draw boolean shidden = (oam[so + 3] & 0x80) == 0x80; byte[] ssrc = sprites[s][line - sy + 16]; for(int p = 0; p < 8; p++) { // grrr... transparency... int spx = sx - 8 + p; byte sp = ssrc[p]; if((sp & 0x03) > 0 && spx < 160 && spx >= 0 && (!(shidden || (getBackgroundAttr(spx, line) & 0x80) == 0x80) || ((screenMemory[line * 160 + spx] & 0x03) == 0))) { screenMemory[line * 160 + spx] = sp; } } } } } /** * updates the screen maps at the specified coordinates */ void updateMap(int which, int x, int y, int tilenum) { // draw the tile on the map if(PgbSettings.system == PgbSettings.SYS_GBC) { // stupid GBC tile routine int i, mx, my, ty; int attribute = getAttr(x, y, (which & 2) == 2);//(which & 2) == 2 ? vram[0x3C00 + x + y * 32] : vram[0x3800 + x + y * 32]; byte pal = (byte)((attribute & 0x07) << 3); int bank = (attribute & 0x08) == 0x08 ? 1 : 0; for(i = 0; i < 8; i++) { my = y * 8 + i; mx = x * 8; ty = ((attribute & 0x40) == 0x40) ? 7 - i : i; if((attribute & 0x20) == 0x20){ // hflip map[which][my][mx++] = (byte)(pal | tiles[bank][tilenum][ty][7]); map[which][my][mx++] = (byte)(pal | tiles[bank][tilenum][ty][6]); map[which][my][mx++] = (byte)(pal | tiles[bank][tilenum][ty][5]); map[which][my][mx++] = (byte)(pal | tiles[bank][tilenum][ty][4]); map[which][my][mx++] = (byte)(pal | tiles[bank][tilenum][ty][3]); map[which][my][mx++] = (byte)(pal | tiles[bank][tilenum][ty][2]); map[which][my][mx++] = (byte)(pal | tiles[bank][tilenum][ty][1]); map[which][my][mx] = (byte)(pal | tiles[bank][tilenum][ty][0]); } else { // !hflip map[which][my][mx++] = (byte)(pal | tiles[bank][tilenum][ty][0]); map[which][my][mx++] = (byte)(pal | tiles[bank][tilenum][ty][1]); map[which][my][mx++] = (byte)(pal | tiles[bank][tilenum][ty][2]); map[which][my][mx++] = (byte)(pal | tiles[bank][tilenum][ty][3]); map[which][my][mx++] = (byte)(pal | tiles[bank][tilenum][ty][4]); map[which][my][mx++] = (byte)(pal | tiles[bank][tilenum][ty][5]); map[which][my][mx++] = (byte)(pal | tiles[bank][tilenum][ty][6]); map[which][my][mx] = (byte)(pal | tiles[bank][tilenum][ty][7]); } } } else { // mono tile copy int mx = x * 8; int my = y * 8; System.arraycopy(tiles[0][tilenum][0], 0, map[which][my++],mx, 8); System.arraycopy(tiles[0][tilenum][1], 0, map[which][my++],mx, 8); System.arraycopy(tiles[0][tilenum][2], 0, map[which][my++],mx, 8); System.arraycopy(tiles[0][tilenum][3], 0, map[which][my++],mx, 8); System.arraycopy(tiles[0][tilenum][4], 0, map[which][my++],mx, 8); System.arraycopy(tiles[0][tilenum][5], 0, map[which][my++],mx, 8); System.arraycopy(tiles[0][tilenum][6], 0, map[which][my++],mx, 8); System.arraycopy(tiles[0][tilenum][7], 0, map[which][my],mx, 8); } } /** * updates the specified sprite cache */ public void updateObj(int onum) { int tnum = oam[(onum << 2) + 2] & (obj_mode ? 0xFE : 0xFF); byte attr = oam[(onum << 2) + 3]; int bank; byte pal; if(PgbSettings.system == PgbSettings.SYS_GBC) { bank = attr >> 3 & 0x01; pal = (byte)((attr << 3 & 0x38) | 0x04); } else { bank = 0; pal = (byte)((attr >> 1 & 0x08) | 0x04); } boolean vflip = (attr & 0x40) == 0x40; boolean hflip = (attr & 0x20) == 0x20; int ts = (tnum * 16) + (0x2000 * bank); for(int y = 0; y < obj_siz; y++) { int ty = vflip ? obj_siz - y - 1 : y; copyTileLineArray(ts + ty * 2, hflip, pal, sprites[onum][y], 0); } /* tnum += vflip ? 1 : 0; for(int y = 0; y < 8; y++) { int ty = vflip ? 7 - y : y; for(int x = 0; x < 8; x++) { int tx = hflip ? 7 - x : x; sprites[onum][y][x] = (byte)(pal | tiles[bank][tnum][ty][tx]); } } if(obj_mode) { tnum += vflip ? -1 : 1; for(int y = 8; y < 16; y++) { int ty = vflip ? 15 - y : y - 8; for(int x = 0; x < 8; x++) { int tx = hflip ? 7 - x : x; sprites[onum][y][x] = (byte)(pal | tiles[bank][tnum][ty][tx]); } } } */ } /** * check if we need to redraw this tile * this isn't exactly perfect, but it'll do. */ void checkTile(int address) { int tilenum = (address - 0x8000) / 16; if((address & 0x0F) == 0x0F) { // if it's the last byte of a tile doTile(tilenum, gbcVram & 0x01); } else { // if they've left one off in the middle if(tilenum != lastTile) { doTile(lastTile, gbcVram & 0x01); } } lastTile = tilenum; } /** * updates the tile cache */ void doTile(int tilenum, int bank) { int ts = tilenum * 16 + 0x2000 * bank; copyTileLineArray(ts , tiles[bank][tilenum][0], 0); copyTileLineArray(ts + 2, tiles[bank][tilenum][1], 0); copyTileLineArray(ts + 4, tiles[bank][tilenum][2], 0); copyTileLineArray(ts + 6, tiles[bank][tilenum][3], 0); copyTileLineArray(ts + 8, tiles[bank][tilenum][4], 0); copyTileLineArray(ts + 10, tiles[bank][tilenum][5], 0); copyTileLineArray(ts + 12, tiles[bank][tilenum][6], 0); copyTileLineArray(ts + 14, tiles[bank][tilenum][7], 0); scanUpdateTile(tilenum); } /** * Scans the screen maps and updates a tile when tile * data is changed. * * This is sort of the weakness of the tile/map caching * scheme -- whenever a tile is changed, you must then * update all instances of the tile in the map. This * makes it pretty easy, tho. */ void scanUpdateTile(int tilenum) { byte tn = (byte)(tilenum); for(int i = 0x1800; i < 0x1C00; i++) { if(vram[i] == tn) { if(tilenum > 127) { updateMap(0, (i - 0x1800) & 0x1F, (i - 0x1800) >> 5, tilenum); } if(tilenum < 256) { updateMap(1, (i - 0x1800) & 0x1F, (i - 0x1800) >> 5, tilenum); } } } for(int i = 0x1C00; i < 0x2000; i++) { if(vram[i] == tn) { if(tilenum > 127) { updateMap(2, (i - 0x1C00) & 0x1F, (i - 0x1C00) >> 5, tilenum); } if(tilenum < 256) { updateMap(3, (i - 0x1C00) & 0x1F, (i - 0x1C00) >> 5, tilenum); } } } } }