bsnes-plus/bsnes/ui-qt/debugger/ppu/tilemap-renderer.cpp

200 lines
5.7 KiB
C++

TilemapRenderer::TilemapRenderer()
: BaseRenderer()
{
screenMode = 0;
layer = 0;
hires = false;
tileAddr = 0;
screenAddr = 0;
screenSizeX = false;
screenSizeY = false;
tileSize = false;
}
void TilemapRenderer::updateBitDepth() {
layer = layer & 3;
screenMode = screenMode & 7;
bitDepth = bitDepthForLayer(screenMode, layer);
}
unsigned TilemapRenderer::nLayersInMode() const {
const static unsigned layers[8] = { 4, 3, 2, 2, 2, 2, 1, 2 };
return layers[screenMode & 7];
}
unsigned TilemapRenderer::tileSizePx() const {
if(!isMode7()) {
return tileSize ? 16 : 8;
} else {
return 8;
}
}
void TilemapRenderer::loadScreenMode() {
screenMode = SNES::ppu.bg_mode() & 7;
if(screenMode == 7) {
layer = SNES::ppu.mode7_extbg();
}
}
void TilemapRenderer::loadTilemapSettings() {
layer = layer & 3;
updateBitDepth();
if(screenMode < 7) {
unsigned ss = SNES::ppu.bg_screen_size(layer);
hires = (screenMode == 5 || screenMode == 6);
screenAddr = SNES::ppu.bg_screen_addr(layer);
tileAddr = SNES::ppu.bg_tile_addr(layer);
screenSizeX = ss & 1;
screenSizeY = ss & 2;
tileSize = SNES::ppu.bg_tile_size(layer);
}
else {
hires = false;
screenAddr = 0;
tileAddr = 0;
screenSizeX = false;
screenSizeY = false;
tileSize = false;
}
}
void TilemapRenderer::drawTilemap() {
buildPalette();
if(isMode7()) { drawMode7Tilemap(); return; }
if(bitDepth == BitDepth::NONE) { invalidateImage(); return; }
unsigned mapSizeY = tileSize ? 512 : 256;
unsigned mapSizeX = mapSizeY << hires;
unsigned width = mapSizeX * (screenSizeX + 1);
unsigned height = mapSizeY * (screenSizeY + 1);
initImage(width, height);
unsigned addr = screenAddr;
for(unsigned y = 0; y < height; y += mapSizeY) {
for(unsigned x = 0; x < width; x += mapSizeX) {
drawMap(addr, x, y);
addr += 0x800;
}
}
}
void TilemapRenderer::drawMap(unsigned mapAddr, unsigned startX, unsigned startY) {
unsigned ts = tileSize ? 16 : 8;
unsigned wordsPerScanline = image.bytesPerLine() / 4;
mapAddr = mapAddr & 0x1f800;
// get the absolute address within the current VRAM bank (if expansion is enabled)
const uint8_t *map = &SNES::memory::vram[mapAddr];
for(unsigned ty = 0; ty < 32; ty++) {
QRgb* imgBits = (QRgb*)image.scanLine(startY + ty * ts) + startX;
for(unsigned tx = 0; tx < 32; tx++) {
drawMapTile(imgBits, wordsPerScanline, map);
imgBits += (ts << hires);
map += 2;
}
}
}
void TilemapRenderer::drawMapTile(QRgb* imgBits, const unsigned wordsPerScanline, const uint8_t* map) {
unsigned ts = tileSize ? 16 : 8;
uint16_t tile = map[0] | (map[1] << 8);
unsigned c = tile & 0x03ff;
uint8_t pal = (tile >> 10) & 7;
bool hFlip = tile & 0x4000;
bool vFlip = tile & 0x8000;
switch(bitDepth) {
case BitDepth::BPP8: pal = 0; break;
case BitDepth::BPP4: pal = pal * 16; break;
case BitDepth::BPP2: pal = pal * 4 + ((screenMode == 0) ? layer * 32 : 0); break;
}
if(tileSize == false) {
drawMap8pxTile(imgBits, wordsPerScanline, c, pal, hFlip, vFlip);
} else {
// 16x16 tile
unsigned c1 = c;
unsigned c2 = (c & 0x3f0) | ((c + 1) & 0x00f);
if (hFlip) { swap(c1, c2); }
unsigned c3 = c1 + 0x010;
unsigned c4 = c2 + 0x010;
if (vFlip) { swap(c1, c3); swap(c2, c4); }
QRgb* row2Bits = imgBits + wordsPerScanline * 8;
drawMap8pxTile(imgBits + 0, wordsPerScanline, c1, pal, hFlip, vFlip);
drawMap8pxTile(imgBits + (8 << hires), wordsPerScanline, c2, pal, hFlip, vFlip);
drawMap8pxTile(row2Bits + 0, wordsPerScanline, c3, pal, hFlip, vFlip);
drawMap8pxTile(row2Bits + (8 << hires), wordsPerScanline, c4, pal, hFlip, vFlip);
}
}
unsigned TilemapRenderer::characterAddress(unsigned c) const {
// keep VRAM addresses limited to 16 bits if VRAM expansion isn't supported (or enabled)
const unsigned sizeMask = (SNES::PPU::SupportsVRAMExpansion && !(SNES::cpu.pio() & 1))
? 0x1ffff : 0xffff;
switch(bitDepth) {
case BitDepth::BPP8: return (tileAddr + c * 64) & 0x1ffc0 & sizeMask;
case BitDepth::BPP4: return (tileAddr + c * 32) & 0x1ffe0 & sizeMask;
case BitDepth::BPP2: return (tileAddr + c * 16) & 0x1fff0 & sizeMask;
case BitDepth::MODE7: return (c & 0xff) * 128 + 1;
case BitDepth::MODE7_EXTBG: return (c & 0xff) * 128 + 1;
}
return 0;
}
void TilemapRenderer::drawMap8pxTile(QRgb* imgBits, const unsigned wordsPerScanline, unsigned c, unsigned palOffset, bool hFlip, bool vFlip) {
unsigned addr = characterAddress(c);
// get the absolute address within the current VRAM bank (if expansion is enabled)
const uint8_t *tile = &SNES::memory::vram[addr];
if(hires) {
unsigned addr2 = characterAddress(c + 1);
const uint8_t *tile2 = &SNES::memory::vram[addr2];
if (hFlip) { swap(tile, tile2); }
draw8pxTile(imgBits, wordsPerScanline, tile, palOffset, hFlip, vFlip);
draw8pxTile(imgBits + 8, wordsPerScanline, tile2, palOffset, hFlip, vFlip);
} else {
draw8pxTile(imgBits, wordsPerScanline, tile, palOffset, hFlip, vFlip);
}
}
void TilemapRenderer::drawMode7Tilemap() {
initImage(1024, 1024);
QRgb* scanline = (QRgb*)image.scanLine(0);
unsigned wordsPerScanline = image.bytesPerLine() / 4;
// get the absolute address of the current VRAM bank (if expansion is enabled)
const uint8_t *map = &SNES::memory::vram[0];
for(unsigned ty = 0; ty < 128; ty++) {
QRgb* imgBits = scanline;
scanline += wordsPerScanline * 8;
for(unsigned tx = 0; tx < 128; tx++) {
unsigned c = *map;
const uint8_t *tile = &SNES::memory::vram[c * 128 + 1];
drawMode7Tile(imgBits, wordsPerScanline, tile);
map += 2;
imgBits += 8;
}
}
}