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