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

206 lines
5.4 KiB
C++

TileRenderer::TileRenderer()
: BaseRenderer()
{
source = VRAM;
address = 0;
bitDepth = BitDepth::BPP4;
width = 16;
paletteOffset = 0;
useCgramPalette = false;
}
unsigned TileRenderer::addressMask() const {
if(source != Source::VRAM) return 0xffffff;
// 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 0x1ffc0 & sizeMask;
case BitDepth::BPP4: return 0x1ffe0 & sizeMask;
case BitDepth::BPP2: return 0x1fff0 & sizeMask;
case BitDepth::MODE7: return 0;
case BitDepth::MODE7_EXTBG: return 0;
}
return 0;
}
unsigned TileRenderer::nTiles() const {
if(source != Source::VRAM) return int(255 / width + 1) * width;
if(isMode7()) return 256;
unsigned a = address & addressMask();
return (maxAddress() - a) / bytesInbetweenTiles();
}
unsigned TileRenderer::maxAddress() const {
switch(source) {
case Source::VRAM: return min(1 << 17, SNES::memory::vram.size());
case Source::CPU_BUS: return 16 * 1024 * 1024;
case Source::CART_ROM: return SNES::memory::cartrom.size();
case Source::CART_RAM: return SNES::memory::cartram.size();
case Source::SA1_BUS: return SNES::cartridge.has_sa1() ? 16 * 1024 * 1024 : 0;
case Source::SFX_BUS: return SNES::cartridge.has_superfx() ? 8 * 1024 * 1024 : 0;
}
return 0;
}
void TileRenderer::buildCgramPalette() {
const unsigned nColors = colorsPerTile();
paletteOffset &= 0xff;
unsigned start = paletteOffset & (0xff - nColors + 1);
assert(start + nColors < 256);
for(unsigned i = 0; i < nColors; i++) {
palette[i] = rgbFromCgram(start + i);
}
}
void TileRenderer::buildBlackWhitePalette() {
const unsigned nColors = colorsPerTile();
uint8_t delta = 255 / (nColors - 1);
uint8_t pixel = 0;
for(unsigned i = 0; i < nColors; i++) {
palette[i] = qRgb(pixel, pixel, pixel);
pixel += delta;
}
}
void TileRenderer::draw() {
if(!SNES::cartridge.loaded()) { invalidateImage(); return; }
if(bitDepth == BitDepth::NONE) { invalidateImage(); return; }
if(useCgramPalette) {
buildCgramPalette();
} else {
buildBlackWhitePalette();
}
if(width < 8) width = 8;
if(width > 64) width = 64;
if(isMode7()) { drawMode7Tileset(); return; }
if(source == Source::VRAM) { drawVramTileset(); return; }
drawMemorySourceTiles();
}
void TileRenderer::drawVramTileset() {
source = Source::VRAM;
address &= addressMask();
const unsigned height = (nTiles() + width - 1) / width;
initImage(width * 8, height * 8);
QRgb* scanline = (QRgb*)image.scanLine(0);
const unsigned wordsPerScanline = image.bytesPerLine() / 4;
const unsigned bytesPerTile = bytesInbetweenTiles();
// get the absolute address within the current VRAM bank (if expansion is enabled)
const uint8_t *tile = &SNES::memory::vram[address];
const uint8_t *tileEnd = &SNES::memory::vram[0] + maxAddress();
for(unsigned y = 0; y < height; y++) {
QRgb* imgBits = scanline;
scanline += wordsPerScanline * 8;
for(unsigned x = 0; x < width; x++) {
if(tile < tileEnd) {
draw8pxTile(imgBits, wordsPerScanline, tile, 0, 0, 0);
imgBits += 8;
tile += bytesPerTile;
}
}
}
}
void TileRenderer::drawMode7Tileset() {
source = Source::VRAM;
address = 0;
const unsigned height = (256 + width - 1) / width;
initImage(width * 8, height * 8);
QRgb* scanline = (QRgb*)image.scanLine(0);
const unsigned wordsPerScanline = image.bytesPerLine() / 4;
// get the absolute address within the current VRAM bank (if expansion is enabled)
const uint8_t *tile = &SNES::memory::vram[1];
const uint8_t *tileEnd = tile + 256 * 128;
for(unsigned y = 0; y < height; y++) {
QRgb* imgBits = scanline;
scanline += wordsPerScanline * 8;
for(unsigned x = 0; x < width; x++) {
if(tile < tileEnd) {
drawMode7Tile(imgBits, wordsPerScanline, tile);
tile += 128;
imgBits += 8;
}
}
}
}
void TileRenderer::drawMemorySourceTiles() {
typedef SNES::Debugger::MemorySource MemorySource;
if(source == Source::VRAM) source = Source::CPU_BUS;
address &= 0xffffff;
MemorySource memSource = MemorySource::CPUBus;
switch(source) {
case Source::CPU_BUS: memSource = MemorySource::CPUBus; break;
case Source::CART_ROM: memSource = MemorySource::CartROM; break;
case Source::CART_RAM: memSource = MemorySource::CartRAM; break;
case Source::SA1_BUS: memSource = MemorySource::SA1Bus; break;
case Source::SFX_BUS: memSource = MemorySource::SFXBus; break;
}
const unsigned height = nTiles() / width;
initImage(width * 8, height * 8);
QRgb* scanline = (QRgb*)image.scanLine(0);
const unsigned wordsPerScanline = image.bytesPerLine() / 4;
const unsigned bytesPerTile = bytesInbetweenTiles();
unsigned addr = address;
uint8_t tile[64];
if(bytesPerTile > 64) return;
SNES::debugger.bus_access = true;
for(unsigned y = 0; y < height; y++) {
QRgb* imgBits = scanline;
scanline += wordsPerScanline * 8;
for(unsigned x = 0; x < width; x++) {
for(unsigned i = 0; i < bytesPerTile; i++) {
tile[i] = SNES::debugger.read(memSource, addr);
addr++;
}
draw8pxTile(imgBits, wordsPerScanline, tile, 0, 0, 0);
imgBits += 8;
}
}
SNES::debugger.bus_access = false;
}