mirror of
https://github.com/devinacker/bsnes-plus.git
synced 2025-04-02 10:52:46 -04:00
200 lines
5.7 KiB
C++
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;
|
|
}
|
|
}
|
|
}
|