* started removing nall-specific components from the fast PPU renderer
* corrected compilation issues with Super Game Boy support
This commit is contained in:
byuu 2019-07-19 00:44:09 +09:00
parent 14d87f6bf3
commit 0623d6ac2b
15 changed files with 345 additions and 346 deletions

View File

@ -32,7 +32,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "bsnes";
static const string Version = "107.8";
static const string Version = "107.9";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org/";

View File

@ -1,10 +1,10 @@
auto PPUfast::Line::renderBackground(PPUfast::IO::Background& self, uint source) -> void {
auto PPU::Line::renderBackground(PPU::IO::Background& self, uint source) -> void {
if(!self.aboveEnable && !self.belowEnable) return;
if(self.tileMode == TileMode::Mode7) return renderMode7(self, source);
if(self.tileMode == TileMode::Inactive) return;
array<bool[256]> windowAbove;
array<bool[256]> windowBelow;
bool windowAbove[256];
bool windowBelow[256];
renderWindow(self.window, self.window.aboveEnable, windowAbove);
renderWindow(self.window, self.window.belowEnable, windowBelow);
@ -23,13 +23,13 @@ auto PPUfast::Line::renderBackground(PPUfast::IO::Background& self, uint source)
uint hscroll = self.hoffset;
uint vscroll = self.voffset;
uint hmask = (width << self.tileSize << bit1(self.screenSize,0)) - 1;
uint vmask = (width << self.tileSize << bit1(self.screenSize,1)) - 1;
uint hmask = (width << self.tileSize << !!(self.screenSize & 1)) - 1;
uint vmask = (width << self.tileSize << !!(self.screenSize & 2)) - 1;
uint y = this->y - (self.mosaicEnable ? this->y % (1 + io.mosaicSize) : 0);
if(hires) {
hscroll <<= 1;
if(io.interlace) y = y << 1 | ppufast.field();
if(io.interlace) y = y << 1 | ppu.field();
}
uint mosaicCounter = 1;
@ -79,7 +79,7 @@ auto PPUfast::Line::renderBackground(PPUfast::IO::Background& self, uint source)
if(tileHeight == 4 && (bool(voffset & 8) ^ bool(mirrorY))) tileNumber += 16;
tileNumber = (tileNumber & 0x03ff) + tiledataIndex & tileMask;
auto tiledata = ppufast.tilecache[self.tileMode] + (tileNumber << 6);
auto tiledata = ppu.tilecache[self.tileMode] + (tileNumber << 6);
tiledata += (voffset & 7 ^ mirrorY) << 3;
for(uint tileX = 0; tileX < 8; tileX++, x++) {
@ -101,7 +101,7 @@ auto PPUfast::Line::renderBackground(PPUfast::IO::Background& self, uint source)
if(self.belowEnable && !windowBelow[x]) plotBelow(x, source, mosaicPriority, mosaicColor);
} else {
uint X = x >> 1;
if(!ppufast.hd()) {
if(!ppu.hd()) {
if(x & 1) {
if(self.aboveEnable && !windowAbove[X]) plotAbove(X, source, mosaicPriority, mosaicColor);
} else {
@ -116,17 +116,16 @@ auto PPUfast::Line::renderBackground(PPUfast::IO::Background& self, uint source)
}
}
auto PPUfast::Line::getTile(PPUfast::IO::Background& self, uint hoffset, uint voffset) -> uint {
auto PPU::Line::getTile(PPU::IO::Background& self, uint hoffset, uint voffset) -> uint {
bool hires = io.bgMode == 5 || io.bgMode == 6;
uint tileHeight = 3 + self.tileSize;
uint tileWidth = !hires ? tileHeight : 4;
uint screenX = (self.screenSize & 1) ? 32 << 5 : 0;
uint screenY = (self.screenSize & 2) ? 32 << 5 + (self.screenSize & 1) : 0;
uint screenX = self.screenSize & 1 ? 32 << 5 : 0;
uint screenY = self.screenSize & 2 ? 32 << 5 + (self.screenSize & 1) : 0;
uint tileX = hoffset >> tileWidth;
uint tileY = voffset >> tileHeight;
uint offset = (tileY & 0x1f) << 5 | (tileX & 0x1f);
if(tileX & 0x20) offset += screenX;
if(tileY & 0x20) offset += screenY;
uint15 address = self.screenAddress + offset;
return ppufast.vram[address];
return ppu.vram[self.screenAddress + offset & 0x7fff];
}

View File

@ -1,28 +1,28 @@
auto PPUfast::latchCounters() -> void {
auto PPU::latchCounters() -> void {
io.hcounter = cpu.hdot();
io.vcounter = cpu.vcounter();
latch.counters = 1;
}
auto PPUfast::vramAddress() const -> uint15 { //uint15 for 64K VRAM; uint16 for 128K VRAM
uint15 address = io.vramAddress;
auto PPU::vramAddress() const -> uint {
uint address = io.vramAddress;
switch(io.vramMapping) {
case 0: return address;
case 1: return bits(address, 8-15) << 8 | bits(address,0-4) << 3 | bits(address,5-7);
case 2: return bits(address, 9-15) << 9 | bits(address,0-5) << 3 | bits(address,6-8);
case 3: return bits(address,10-15) << 10 | bits(address,0-6) << 3 | bits(address,7-9);
case 0: return address & 0x7fff;
case 1: return address & 0x7f00 | address << 3 & 0x00f8 | address >> 5 & 7;
case 2: return address & 0x7e00 | address << 3 & 0x01f8 | address >> 6 & 7;
case 3: return address & 0x7c00 | address << 3 & 0x03f8 | address >> 7 & 7;
}
unreachable;
}
auto PPUfast::readVRAM() -> uint16 {
auto PPU::readVRAM() -> uint16 {
if(!io.displayDisable && cpu.vcounter() < vdisp()) return 0x0000;
auto address = vramAddress();
return vram[address];
}
template<bool Byte>
auto PPUfast::writeVRAM(uint8 data) -> void {
auto PPU::writeVRAM(uint8_t data) -> void {
if(!io.displayDisable && cpu.vcounter() < vdisp()) return;
Line::flush();
auto address = vramAddress();
@ -35,11 +35,11 @@ auto PPUfast::writeVRAM(uint8 data) -> void {
updateTiledata(address);
}
auto PPUfast::updateTiledata(uint address) -> void {
auto word = vram[address];
auto line2bpp = tilecache[TileMode::BPP2] + ((address & 0x7fff) << 3);
auto line4bpp = tilecache[TileMode::BPP4] + ((address & 0x7ff0) << 2) + ((address & 7) << 3);
auto line8bpp = tilecache[TileMode::BPP8] + ((address & 0x7fe0) << 1) + ((address & 7) << 3);
auto PPU::updateTiledata(uint address) -> void {
auto word = vram[address & 0x7fff];
auto line2bpp = tilecache[TileMode::BPP2] + (address << 3 & 0x3fff8);
auto line4bpp = tilecache[TileMode::BPP4] + (address << 2 & 0x1ffc0) + (address << 3 & 0x38);
auto line8bpp = tilecache[TileMode::BPP8] + (address << 1 & 0x0ffc0) + (address << 3 & 0x38);
uint plane4bpp = address >> 2 & 2;
uint plane8bpp = address >> 2 & 6;
for(uint x : range(8)) {
@ -49,20 +49,20 @@ auto PPUfast::updateTiledata(uint address) -> void {
}
}
auto PPUfast::readOAM(uint10 address) -> uint8 {
auto PPU::readOAM(uint10 address) -> uint8 {
if(!io.displayDisable && cpu.vcounter() < vdisp()) address = latch.oamAddress;
return readObject(address);
}
auto PPUfast::writeOAM(uint10 address, uint8 data) -> void {
auto PPU::writeOAM(uint10 address, uint8_t data) -> void {
Line::flush();
//Uniracers 2-player mode hack
//0x0218: Uniracers (2-player mode) hack; requires cycle timing for latch.oamAddress to be correct
if(!io.displayDisable && cpu.vcounter() < vdisp()) address = 0x0218; //latch.oamAddress;
return writeObject(address, data);
}
template<bool Byte>
auto PPUfast::readCGRAM(uint8 address) -> uint8 {
auto PPU::readCGRAM(uint8_t address) -> uint8 {
if(!io.displayDisable
&& cpu.vcounter() > 0 && cpu.vcounter() < vdisp()
&& cpu.hcounter() >= 88 && cpu.hcounter() < 1096
@ -75,7 +75,7 @@ auto PPUfast::readCGRAM(uint8 address) -> uint8 {
}
}
auto PPUfast::writeCGRAM(uint8 address, uint15 data) -> void {
auto PPU::writeCGRAM(uint8_t address, uint15 data) -> void {
if(!io.displayDisable
&& cpu.vcounter() > 0 && cpu.vcounter() < vdisp()
&& cpu.hcounter() >= 88 && cpu.hcounter() < 1096
@ -83,8 +83,8 @@ auto PPUfast::writeCGRAM(uint8 address, uint15 data) -> void {
cgram[address] = data;
}
auto PPUfast::readIO(uint address, uint8 data) -> uint8 {
cpu.synchronize(ppufast);
auto PPU::readIO(uint address, uint8 data) -> uint8 {
cpu.synchronize(ppu);
switch(address & 0xffff) {
@ -97,22 +97,22 @@ auto PPUfast::readIO(uint address, uint8 data) -> uint8 {
}
case 0x2134: { //MPYL
uint24 result = (int16)io.mode7.a * (int8)(io.mode7.b >> 8);
return latch.ppu1.mdr = bit8(result,0);
uint result = (int16_t)io.mode7.a * (int8_t)(io.mode7.b >> 8);
return latch.ppu1.mdr = result >> 0;
}
case 0x2135: { //MPYM
uint24 result = (int16)io.mode7.a * (int8)(io.mode7.b >> 8);
return latch.ppu1.mdr = bit8(result,1);
uint result = (int16_t)io.mode7.a * (int8_t)(io.mode7.b >> 8);
return latch.ppu1.mdr = result >> 8;
}
case 0x2136: { //MPYH
uint24 result = (int16)io.mode7.a * (int8)(io.mode7.b >> 8);
return latch.ppu1.mdr = bit8(result,2);
uint result = (int16_t)io.mode7.a * (int8_t)(io.mode7.b >> 8);
return latch.ppu1.mdr = result >> 16;
}
case 0x2137: { //SLHV
if(cbit1(cpu.pio(),7)) latchCounters();
if(cpu.pio() & 0x80) latchCounters();
return data; //CPU MDR
}
@ -142,9 +142,9 @@ auto PPUfast::readIO(uint address, uint8 data) -> uint8 {
case 0x213b: { //CGDATAREAD
if(io.cgramAddressLatch++ == 0) {
bits(latch.ppu2.mdr,0-7) = readCGRAM<0>(io.cgramAddress);
latch.ppu2.mdr = readCGRAM<0>(io.cgramAddress);
} else {
bits(latch.ppu2.mdr,0-6) = readCGRAM<1>(io.cgramAddress++);
latch.ppu2.mdr = readCGRAM<1>(io.cgramAddress++) & 0x7f | latch.ppu2.mdr & 0x80;
}
return latch.ppu2.mdr;
}
@ -152,10 +152,10 @@ auto PPUfast::readIO(uint address, uint8 data) -> uint8 {
case 0x213c: { //OPHCT
if(latch.hcounter == 0) {
latch.hcounter = 1;
bits(latch.ppu2.mdr,0-7) = bits(io.hcounter,0-7);
latch.ppu2.mdr = io.hcounter;
} else {
latch.hcounter = 0;
bit1(latch.ppu2.mdr,0) = bit1(io.hcounter,8);
latch.ppu2.mdr = io.hcounter >> 8 | latch.ppu2.mdr & 0xfe;
}
return latch.ppu2.mdr;
}
@ -163,34 +163,30 @@ auto PPUfast::readIO(uint address, uint8 data) -> uint8 {
case 0x213d: { //OPVCT
if(latch.vcounter == 0) {
latch.vcounter = 1;
bits(latch.ppu2.mdr,0-7) = bits(io.vcounter,0-7);
latch.ppu2.mdr = io.vcounter;
} else {
latch.vcounter = 0;
bit1(latch.ppu2.mdr,0) = bit1(io.vcounter,8);
latch.ppu2.mdr = io.vcounter >> 8 | latch.ppu2.mdr & 0xfe;
}
return latch.ppu2.mdr;
}
case 0x213e: { //STAT77
bits(latch.ppu1.mdr,0-3) = 1; //PPU1 version
bit1(latch.ppu1.mdr,5) = 0;
bit1(latch.ppu1.mdr,6) = io.obj.rangeOver;
bit1(latch.ppu1.mdr,7) = io.obj.timeOver;
latch.ppu1.mdr = 0x01 | io.obj.rangeOver << 6 | io.obj.timeOver << 7;
return latch.ppu1.mdr;
}
case 0x213f: { //STAT78
latch.hcounter = 0;
latch.vcounter = 0;
bits(latch.ppu2.mdr,0-3) = 3; //PPU2 version
bit1(latch.ppu2.mdr,4) = Region::PAL(); //0 = NTSC, 1 = PAL
if(!cbit1(cpu.pio(),7)) {
bit1(latch.ppu2.mdr,6) = 1;
latch.ppu2.mdr &= 1 << 5;
latch.ppu2.mdr |= 0x03 | Region::PAL() << 4 | field() << 7;
if(!(cpu.pio() & 0x80)) {
latch.ppu2.mdr |= 1 << 6;
} else {
bit1(latch.ppu2.mdr,6) = latch.counters;
latch.ppu2.mdr |= latch.counters << 6;
latch.counters = 0;
}
bit1(latch.ppu2.mdr,7) = field();
return latch.ppu2.mdr;
}
@ -199,22 +195,22 @@ auto PPUfast::readIO(uint address, uint8 data) -> uint8 {
return data;
}
auto PPUfast::writeIO(uint address, uint8 data) -> void {
cpu.synchronize(ppufast);
auto PPU::writeIO(uint address, uint8 data) -> void {
cpu.synchronize(ppu);
switch(address & 0xffff) {
case 0x2100: { //INIDISP
if(io.displayDisable && cpu.vcounter() == vdisp()) oamAddressReset();
io.displayBrightness = bits(data,0-3);
io.displayDisable = bit1(data,7);
io.displayBrightness = data >> 0 & 15;
io.displayDisable = data >> 7 & 1;
return;
}
case 0x2101: { //OBSEL
io.obj.tiledataAddress = bits(data,0-2) << 13;
io.obj.nameselect = bits(data,3-4);
io.obj.baseSize = bits(data,5-7);
io.obj.tiledataAddress = (data & 7) << 13;
io.obj.nameselect = data >> 3 & 3;
io.obj.baseSize = data >> 5 & 7;
return;
}
@ -225,17 +221,17 @@ auto PPUfast::writeIO(uint address, uint8 data) -> void {
}
case 0x2103: { //OAMADDH
io.oamBaseAddress = bit1(data,0) << 9 | (io.oamBaseAddress & 0x01fe);
io.oamPriority = bit1(data,7);
io.oamBaseAddress = (data & 1) << 9 | io.oamBaseAddress & 0x01fe;
io.oamPriority = data >> 7 & 1;
oamAddressReset();
return;
}
case 0x2104: { //OAMDATA
uint1 latchBit = io.oamAddress & 1;
uint10 address = io.oamAddress++;
bool latchBit = io.oamAddress & 1;
uint address = io.oamAddress++;
if(latchBit == 0) latch.oam = data;
if(bit1(address,9)) {
if(address & 0x200) {
writeOAM(address, data);
} else if(latchBit == 1) {
writeOAM((address & ~1) + 0, latch.oam);
@ -246,58 +242,58 @@ auto PPUfast::writeIO(uint address, uint8 data) -> void {
}
case 0x2105: { //BGMODE
io.bgMode = bits(data,0-2);
io.bgPriority = bit1(data,3);
io.bg1.tileSize = bit1(data,4);
io.bg2.tileSize = bit1(data,5);
io.bg3.tileSize = bit1(data,6);
io.bg4.tileSize = bit1(data,7);
io.bgMode = data >> 0 & 7;
io.bgPriority = data >> 3 & 1;
io.bg1.tileSize = data >> 4 & 1;
io.bg2.tileSize = data >> 5 & 1;
io.bg3.tileSize = data >> 6 & 1;
io.bg4.tileSize = data >> 7 & 1;
updateVideoMode();
return;
}
case 0x2106: { //MOSAIC
io.bg1.mosaicEnable = bit1(data,0);
io.bg2.mosaicEnable = bit1(data,1);
io.bg3.mosaicEnable = bit1(data,2);
io.bg4.mosaicEnable = bit1(data,3);
io.mosaicSize = bits(data,4-7);
io.bg1.mosaicEnable = data >> 0 & 1;
io.bg2.mosaicEnable = data >> 1 & 1;
io.bg3.mosaicEnable = data >> 2 & 1;
io.bg4.mosaicEnable = data >> 3 & 1;
io.mosaicSize = data >> 4 & 15;
return;
}
case 0x2107: { //BG1SC
io.bg1.screenSize = bits(data,0-1);
io.bg1.screenAddress = bits(data,2-7) << 10;
io.bg1.screenSize = data >> 0 & 3;
io.bg1.screenAddress = data >> 2 << 10;
return;
}
case 0x2108: { //BG2SC
io.bg2.screenSize = bits(data,0-1);
io.bg2.screenAddress = bits(data,2-7) << 10;
io.bg2.screenSize = data >> 0 & 3;
io.bg2.screenAddress = data >> 2 << 10;
return;
}
case 0x2109: { //BG3SC
io.bg3.screenSize = bits(data,0-1);
io.bg3.screenAddress = bits(data,2-7) << 10;
io.bg3.screenSize = data >> 0 & 3;
io.bg3.screenAddress = data >> 2 << 10;
return;
}
case 0x210a: { //BG4SC
io.bg4.screenSize = bits(data,0-1);
io.bg4.screenAddress = bits(data,2-7) << 10;
io.bg4.screenSize = data >> 0 & 3;
io.bg4.screenAddress = data >> 2 << 10;
return;
}
case 0x210b: { //BG12NBA
io.bg1.tiledataAddress = bits(data,0-3) << 12;
io.bg2.tiledataAddress = bits(data,4-7) << 12;
io.bg1.tiledataAddress = (data & 15) << 12;
io.bg2.tiledataAddress = (data >> 4) << 12;
return;
}
case 0x210c: { //BG34NBA
io.bg3.tiledataAddress = bits(data,0-3) << 12;
io.bg4.tiledataAddress = bits(data,4-7) << 12;
io.bg3.tiledataAddress = (data & 15) << 12;
io.bg4.tiledataAddress = (data >> 4) << 12;
return;
}
@ -362,8 +358,8 @@ auto PPUfast::writeIO(uint address, uint8 data) -> void {
case 0x2115: { //VMAIN
static const uint size[4] = {1, 32, 128, 128};
io.vramIncrementSize = size[data & 3];
io.vramMapping = bits(data,2-3);
io.vramIncrementMode = bit1(data,7);
io.vramMapping = data >> 2 & 3;
io.vramIncrementMode = data >> 7 & 1;
return;
}
@ -392,9 +388,9 @@ auto PPUfast::writeIO(uint address, uint8 data) -> void {
}
case 0x211a: { //M7SEL
io.mode7.hflip = bit1(data,0);
io.mode7.vflip = bit1(data,1);
io.mode7.repeat = bits(data,6-7);
io.mode7.hflip = data >> 0 & 1;
io.mode7.vflip = data >> 1 & 1;
io.mode7.repeat = data >> 6 & 3;
return;
}
@ -444,44 +440,44 @@ auto PPUfast::writeIO(uint address, uint8 data) -> void {
if(io.cgramAddressLatch++ == 0) {
latch.cgram = data;
} else {
writeCGRAM(io.cgramAddress++, bits(data,0-6) << 8 | latch.cgram);
writeCGRAM(io.cgramAddress++, (data & 0x7f) << 8 | latch.cgram);
}
return;
}
case 0x2123: { //W12SEL
io.bg1.window.oneInvert = bit1(data,0);
io.bg1.window.oneEnable = bit1(data,1);
io.bg1.window.twoInvert = bit1(data,2);
io.bg1.window.twoEnable = bit1(data,3);
io.bg2.window.oneInvert = bit1(data,4);
io.bg2.window.oneEnable = bit1(data,5);
io.bg2.window.twoInvert = bit1(data,6);
io.bg2.window.twoEnable = bit1(data,7);
io.bg1.window.oneInvert = data >> 0 & 1;
io.bg1.window.oneEnable = data >> 1 & 1;
io.bg1.window.twoInvert = data >> 2 & 1;
io.bg1.window.twoEnable = data >> 3 & 1;
io.bg2.window.oneInvert = data >> 4 & 1;
io.bg2.window.oneEnable = data >> 5 & 1;
io.bg2.window.twoInvert = data >> 6 & 1;
io.bg2.window.twoEnable = data >> 7 & 1;
return;
}
case 0x2124: { //W34SEL
io.bg3.window.oneInvert = bit1(data,0);
io.bg3.window.oneEnable = bit1(data,1);
io.bg3.window.twoInvert = bit1(data,2);
io.bg3.window.twoEnable = bit1(data,3);
io.bg4.window.oneInvert = bit1(data,4);
io.bg4.window.oneEnable = bit1(data,5);
io.bg4.window.twoInvert = bit1(data,6);
io.bg4.window.twoEnable = bit1(data,7);
io.bg3.window.oneInvert = data >> 0 & 1;
io.bg3.window.oneEnable = data >> 1 & 1;
io.bg3.window.twoInvert = data >> 2 & 1;
io.bg3.window.twoEnable = data >> 3 & 1;
io.bg4.window.oneInvert = data >> 4 & 1;
io.bg4.window.oneEnable = data >> 5 & 1;
io.bg4.window.twoInvert = data >> 6 & 1;
io.bg4.window.twoEnable = data >> 7 & 1;
return;
}
case 0x2125: { //WOBJSEL
io.obj.window.oneInvert = bit1(data,0);
io.obj.window.oneEnable = bit1(data,1);
io.obj.window.twoInvert = bit1(data,2);
io.obj.window.twoEnable = bit1(data,3);
io.col.window.oneInvert = bit1(data,4);
io.col.window.oneEnable = bit1(data,5);
io.col.window.twoInvert = bit1(data,6);
io.col.window.twoEnable = bit1(data,7);
io.obj.window.oneInvert = data >> 0 & 1;
io.obj.window.oneEnable = data >> 1 & 1;
io.obj.window.twoInvert = data >> 2 & 1;
io.obj.window.twoEnable = data >> 3 & 1;
io.col.window.oneInvert = data >> 4 & 1;
io.col.window.oneEnable = data >> 5 & 1;
io.col.window.twoInvert = data >> 6 & 1;
io.col.window.twoEnable = data >> 7 & 1;
return;
}
@ -506,89 +502,89 @@ auto PPUfast::writeIO(uint address, uint8 data) -> void {
}
case 0x212a: { //WBGLOG
io.bg1.window.mask = bits(data,0-1);
io.bg2.window.mask = bits(data,2-3);
io.bg3.window.mask = bits(data,4-5);
io.bg4.window.mask = bits(data,6-7);
io.bg1.window.mask = data >> 0 & 3;
io.bg2.window.mask = data >> 2 & 3;
io.bg3.window.mask = data >> 4 & 3;
io.bg4.window.mask = data >> 6 & 3;
return;
}
case 0x212b: { //WOBJLOG
io.obj.window.mask = bits(data,0-1);
io.col.window.mask = bits(data,2-3);
io.obj.window.mask = data >> 0 & 3;
io.col.window.mask = data >> 2 & 3;
return;
}
case 0x212c: { //TM
io.bg1.aboveEnable = bit1(data,0);
io.bg2.aboveEnable = bit1(data,1);
io.bg3.aboveEnable = bit1(data,2);
io.bg4.aboveEnable = bit1(data,3);
io.obj.aboveEnable = bit1(data,4);
io.bg1.aboveEnable = data >> 0 & 1;
io.bg2.aboveEnable = data >> 1 & 1;
io.bg3.aboveEnable = data >> 2 & 1;
io.bg4.aboveEnable = data >> 3 & 1;
io.obj.aboveEnable = data >> 4 & 1;
return;
}
case 0x212d: { //TS
io.bg1.belowEnable = bit1(data,0);
io.bg2.belowEnable = bit1(data,1);
io.bg3.belowEnable = bit1(data,2);
io.bg4.belowEnable = bit1(data,3);
io.obj.belowEnable = bit1(data,4);
io.bg1.belowEnable = data >> 0 & 1;
io.bg2.belowEnable = data >> 1 & 1;
io.bg3.belowEnable = data >> 2 & 1;
io.bg4.belowEnable = data >> 3 & 1;
io.obj.belowEnable = data >> 4 & 1;
return;
}
case 0x212e: { //TMW
io.bg1.window.aboveEnable = bit1(data,0);
io.bg2.window.aboveEnable = bit1(data,1);
io.bg3.window.aboveEnable = bit1(data,2);
io.bg4.window.aboveEnable = bit1(data,3);
io.obj.window.aboveEnable = bit1(data,4);
io.bg1.window.aboveEnable = data >> 0 & 1;
io.bg2.window.aboveEnable = data >> 1 & 1;
io.bg3.window.aboveEnable = data >> 2 & 1;
io.bg4.window.aboveEnable = data >> 3 & 1;
io.obj.window.aboveEnable = data >> 4 & 1;
return;
}
case 0x212f: { //TSW
io.bg1.window.belowEnable = bit1(data,0);
io.bg2.window.belowEnable = bit1(data,1);
io.bg3.window.belowEnable = bit1(data,2);
io.bg4.window.belowEnable = bit1(data,3);
io.obj.window.belowEnable = bit1(data,4);
io.bg1.window.belowEnable = data >> 0 & 1;
io.bg2.window.belowEnable = data >> 1 & 1;
io.bg3.window.belowEnable = data >> 2 & 1;
io.bg4.window.belowEnable = data >> 3 & 1;
io.obj.window.belowEnable = data >> 4 & 1;
return;
}
case 0x2130: { //CGWSEL
io.col.directColor = bit1(data,0);
io.col.blendMode = bit1(data,1);
io.col.window.belowMask = bits(data,4-5);
io.col.window.aboveMask = bits(data,6-7);
io.col.directColor = data >> 0 & 1;
io.col.blendMode = data >> 1 & 1;
io.col.window.belowMask = data >> 4 & 3;
io.col.window.aboveMask = data >> 6 & 3;
return;
}
case 0x2131: { //CGADDSUB
io.col.enable[Source::BG1 ] = bit1(data,0);
io.col.enable[Source::BG2 ] = bit1(data,1);
io.col.enable[Source::BG3 ] = bit1(data,2);
io.col.enable[Source::BG4 ] = bit1(data,3);
io.col.enable[Source::BG1 ] = data >> 0 & 1;
io.col.enable[Source::BG2 ] = data >> 1 & 1;
io.col.enable[Source::BG3 ] = data >> 2 & 1;
io.col.enable[Source::BG4 ] = data >> 3 & 1;
io.col.enable[Source::OBJ1] = 0;
io.col.enable[Source::OBJ2] = bit1(data,4);
io.col.enable[Source::COL ] = bit1(data,5);
io.col.halve = bit1(data,6);
io.col.mathMode = bit1(data,7);
io.col.enable[Source::OBJ2] = data >> 4 & 1;
io.col.enable[Source::COL ] = data >> 5 & 1;
io.col.halve = data >> 6 & 1;
io.col.mathMode = data >> 7 & 1;
return;
}
case 0x2132: { //COLDATA
if(bit1(data,5)) bits(io.col.fixedColor, 0- 4) = data & 31;
if(bit1(data,6)) bits(io.col.fixedColor, 5- 9) = data & 31;
if(bit1(data,7)) bits(io.col.fixedColor,10-14) = data & 31;
if(data & 0x20) io.col.fixedColor = io.col.fixedColor & 0b11111'11111'00000 | (data & 31) << 0;
if(data & 0x40) io.col.fixedColor = io.col.fixedColor & 0b11111'00000'11111 | (data & 31) << 5;
if(data & 0x80) io.col.fixedColor = io.col.fixedColor & 0b00000'11111'11111 | (data & 31) << 10;
return;
}
case 0x2133: { //SETINI
io.interlace = bit1(data,0);
io.obj.interlace = bit1(data,1);
io.overscan = bit1(data,2);
io.pseudoHires = bit1(data,3);
io.extbg = bit1(data,6);
io.interlace = data >> 0 & 1;
io.obj.interlace = data >> 1 & 1;
io.overscan = data >> 2 & 1;
io.pseudoHires = data >> 3 & 1;
io.extbg = data >> 6 & 1;
updateVideoMode();
return;
}
@ -596,7 +592,7 @@ auto PPUfast::writeIO(uint address, uint8 data) -> void {
}
}
auto PPUfast::updateVideoMode() -> void {
auto PPU::updateVideoMode() -> void {
ppubase.display.vdisp = !io.overscan ? 225 : 240;
switch(io.bgMode) {

View File

@ -1,29 +1,29 @@
uint PPUfast::Line::start = 0;
uint PPUfast::Line::count = 0;
uint PPU::Line::start = 0;
uint PPU::Line::count = 0;
auto PPUfast::Line::flush() -> void {
auto PPU::Line::flush() -> void {
if(Line::count) {
#pragma omp parallel for if(Line::count >= 8)
for(uint y = 0; y < Line::count; y++) {
ppufast.lines[Line::start + y].render();
ppu.lines[Line::start + y].render();
}
Line::start = 0;
Line::count = 0;
}
}
auto PPUfast::Line::render() -> void {
uint y = this->y + (!ppufast.latch.overscan ? 7 : 0);
auto PPU::Line::render() -> void {
uint y = this->y + (!ppu.latch.overscan ? 7 : 0);
auto hd = ppufast.hd();
auto ss = ppufast.ss();
auto scale = ppufast.hdScale();
auto output = ppufast.output + (!hd
? (y * 1024 + (ppufast.interlace() && ppufast.field() ? 512 : 0))
auto hd = ppu.hd();
auto ss = ppu.ss();
auto scale = ppu.hdScale();
auto output = ppu.output + (!hd
? (y * 1024 + (ppu.interlace() && ppu.field() ? 512 : 0))
: (y * 256 * scale * scale)
);
auto width = (!hd
? (!ppufast.hires() ? 256 : 512)
? (!ppu.hires() ? 256 : 512)
: (256 * scale * scale));
if(io.displayDisable) {
@ -33,9 +33,9 @@ auto PPUfast::Line::render() -> void {
bool hires = io.pseudoHires || io.bgMode == 5 || io.bgMode == 6;
auto aboveColor = cgram[0];
auto belowColor = hires ? cgram[0] : (uint16)io.col.fixedColor;
uint xa = (hd || ss) && ppufast.interlace() && ppufast.field() ? 256 * scale * scale / 2 : 0;
uint xb = !(hd || ss) ? 256 : ppufast.interlace() && !ppufast.field() ? 256 * scale * scale / 2 : 256 * scale * scale;
auto belowColor = hires ? cgram[0] : (uint16_t)io.col.fixedColor;
uint xa = (hd || ss) && ppu.interlace() && ppu.field() ? 256 * scale * scale / 2 : 0;
uint xb = !(hd || ss) ? 256 : ppu.interlace() && !ppu.field() ? 256 * scale * scale / 2 : 256 * scale * scale;
for(uint x = xa; x < xb; x++) {
above[x] = {Source::COL, 0, aboveColor};
below[x] = {Source::COL, 0, belowColor};
@ -49,7 +49,7 @@ auto PPUfast::Line::render() -> void {
renderWindow(io.col.window, io.col.window.aboveMask, windowAbove);
renderWindow(io.col.window, io.col.window.belowMask, windowBelow);
auto luma = ppufast.lightTable[io.displayBrightness];
auto luma = ppu.lightTable[io.displayBrightness];
uint curr = 0, prev = 0;
if(hd) for(uint x : range(256 * scale * scale)) {
*output++ = luma[pixel(x / scale & 255, above[x], below[x])];
@ -72,7 +72,7 @@ auto PPUfast::Line::render() -> void {
}
}
auto PPUfast::Line::pixel(uint x, Pixel above, Pixel below) const -> uint16 {
auto PPU::Line::pixel(uint x, Pixel above, Pixel below) const -> uint16_t {
if(!windowAbove[x]) above.color = 0x0000;
if(!windowBelow[x]) return above.color;
if(!io.col.enable[above.source]) return above.color;
@ -80,7 +80,7 @@ auto PPUfast::Line::pixel(uint x, Pixel above, Pixel below) const -> uint16 {
return blend(above.color, below.color, io.col.halve && windowAbove[x] && below.source != Source::COL);
}
auto PPUfast::Line::blend(uint x, uint y, bool halve) const -> uint16 {
auto PPU::Line::blend(uint x, uint y, bool halve) const -> uint16_t {
if(!io.col.mathMode) { //add
if(!halve) {
uint sum = x + y;
@ -100,7 +100,7 @@ auto PPUfast::Line::blend(uint x, uint y, bool halve) const -> uint16 {
}
}
auto PPUfast::Line::directColor(uint paletteIndex, uint paletteColor) const -> uint16 {
auto PPU::Line::directColor(uint paletteIndex, uint paletteColor) const -> uint16_t {
//paletteIndex = bgr
//paletteColor = BBGGGRRR
//output = 0 BBb00 GGGg0 RRRr0
@ -109,25 +109,25 @@ auto PPUfast::Line::directColor(uint paletteIndex, uint paletteColor) const -> u
+ (paletteColor << 7 & 0x6000) + (paletteIndex << 10 & 0x1000); //B
}
auto PPUfast::Line::plotAbove(uint x, uint source, uint priority, uint color) -> void {
if(ppufast.hd()) return plotHD(above, x, source, priority, color, false, false);
auto PPU::Line::plotAbove(uint x, uint source, uint priority, uint color) -> void {
if(ppu.hd()) return plotHD(above, x, source, priority, color, false, false);
if(priority > above[x].priority) above[x] = {source, priority, color};
}
auto PPUfast::Line::plotBelow(uint x, uint source, uint priority, uint color) -> void {
if(ppufast.hd()) return plotHD(below, x, source, priority, color, false, false);
auto PPU::Line::plotBelow(uint x, uint source, uint priority, uint color) -> void {
if(ppu.hd()) return plotHD(below, x, source, priority, color, false, false);
if(priority > below[x].priority) below[x] = {source, priority, color};
}
//todo: name these variables more clearly ...
auto PPUfast::Line::plotHD(Pixel* pixel, uint x, uint source, uint priority, uint color, bool hires, bool subpixel) -> void {
auto scale = ppufast.hdScale();
auto PPU::Line::plotHD(Pixel* pixel, uint x, uint source, uint priority, uint color, bool hires, bool subpixel) -> void {
auto scale = ppu.hdScale();
int xss = hires && subpixel ? scale / 2 : 0;
int ys = ppufast.interlace() && ppufast.field() ? scale / 2 : 0;
int ys = ppu.interlace() && ppu.field() ? scale / 2 : 0;
if(priority > pixel[x * scale + xss + ys * 256 * scale].priority) {
Pixel p = {source, priority, color};
int xsm = hires && !subpixel ? scale / 2 : scale;
int ysm = ppufast.interlace() && !ppufast.field() ? scale / 2 : scale;
int ysm = ppu.interlace() && !ppu.field() ? scale / 2 : scale;
for(int xs = xss; xs < xsm; xs++) {
pixel[x * scale + xs + ys * 256 * scale] = p;
}

View File

@ -1,11 +1,11 @@
auto PPUfast::Line::renderMode7(PPUfast::IO::Background& self, uint source) -> void {
auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void {
//EXTBG is only really used by games to give the mode 7 layer two priority levels
//especially with HD mode 7, it's just wasteful to render BG1 just to be overwritten by BG2
if(io.extbg && source == Source::BG1) return;
//HD mode 7 support
if(!ppufast.hdMosaic() || !self.mosaicEnable || !io.mosaicSize) {
if(ppufast.hdScale() > 1) return renderMode7HD(self, source);
if(!ppu.hdMosaic() || !self.mosaicEnable || !io.mosaicSize) {
if(ppu.hdScale() > 1) return renderMode7HD(self, source);
}
int Y = this->y - (self.mosaicEnable ? this->y % (1 + io.mosaicSize) : 0);
@ -29,8 +29,8 @@ auto PPUfast::Line::renderMode7(PPUfast::IO::Background& self, uint source) -> v
int originX = (a * clip(hoffset - hcenter) & ~63) + (b * clip(voffset - vcenter) & ~63) + (b * y & ~63) + (hcenter << 8);
int originY = (c * clip(hoffset - hcenter) & ~63) + (d * clip(voffset - vcenter) & ~63) + (d * y & ~63) + (vcenter << 8);
array<bool[256]> windowAbove;
array<bool[256]> windowBelow;
bool windowAbove[256];
bool windowBelow[256];
renderWindow(self.window, self.window.aboveEnable, windowAbove);
renderWindow(self.window, self.window.belowEnable, windowBelow);
@ -43,8 +43,8 @@ auto PPUfast::Line::renderMode7(PPUfast::IO::Background& self, uint source) -> v
bool outOfBounds = (pixelX | pixelY) & ~1023;
uint15 tileAddress = tileY * 128 + tileX;
uint15 paletteAddress = ((pixelY & 7) << 3) + (pixelX & 7);
uint8 tile = io.mode7.repeat == 3 && outOfBounds ? 0 : (ppufast.vram[tileAddress] & 0xff);
uint8 palette = io.mode7.repeat == 2 && outOfBounds ? 0 : (ppufast.vram[paletteAddress + (tile << 6)] >> 8);
uint8_t tile = io.mode7.repeat == 3 && outOfBounds ? 0 : ppu.vram[tileAddress] >> 0;
uint8_t palette = io.mode7.repeat == 2 && outOfBounds ? 0 : ppu.vram[paletteAddress + (tile << 6)] >> 8;
uint priority;
if(source == Source::BG1) {

View File

@ -1,6 +1,6 @@
auto PPUfast::Line::renderMode7HD(PPUfast::IO::Background& self, uint source) -> void {
auto PPU::Line::renderMode7HD(PPU::IO::Background& self, uint source) -> void {
const bool extbg = source == Source::BG2;
const uint scale = ppufast.hdScale();
const uint scale = ppu.hdScale();
Pixel pixel;
Pixel* above = &this->above[-1];
@ -9,10 +9,10 @@ auto PPUfast::Line::renderMode7HD(PPUfast::IO::Background& self, uint source) ->
//find the first and last scanline for interpolation
int y_a = y;
int y_b = y;
#define isLineMode7(n) (ppufast.lines[n].io.bg1.tileMode == TileMode::Mode7 && ( \
(ppufast.lines[n].io.bg1.aboveEnable || ppufast.lines[n].io.bg1.belowEnable) \
#define isLineMode7(n) (ppu.lines[n].io.bg1.tileMode == TileMode::Mode7 && ( \
(ppu.lines[n].io.bg1.aboveEnable || ppu.lines[n].io.bg1.belowEnable) \
))
if(ppufast.hdPerspective()) {
if(ppu.hdPerspective()) {
while(y_a > 1 && isLineMode7(y_a)) y_a--; y_a += 1;
while(y_b < 239 && isLineMode7(y_b)) y_b++; y_b -= 8;
} else {
@ -21,13 +21,13 @@ auto PPUfast::Line::renderMode7HD(PPUfast::IO::Background& self, uint source) ->
}
#undef isLineMode7
Line line_a = ppufast.lines[y_a];
Line line_a = ppu.lines[y_a];
float a_a = (int16)line_a.io.mode7.a;
float b_a = (int16)line_a.io.mode7.b;
float c_a = (int16)line_a.io.mode7.c;
float d_a = (int16)line_a.io.mode7.d;
Line line_b = ppufast.lines[y_b];
Line line_b = ppu.lines[y_b];
float a_b = (int16)line_b.io.mode7.a;
float b_b = (int16)line_b.io.mode7.b;
float c_b = (int16)line_b.io.mode7.c;
@ -43,8 +43,8 @@ auto PPUfast::Line::renderMode7HD(PPUfast::IO::Background& self, uint source) ->
y_b = 255 - y_b;
}
array<bool[256]> windowAbove;
array<bool[256]> windowBelow;
bool windowAbove[256];
bool windowBelow[256];
renderWindow(self.window, self.window.aboveEnable, windowAbove);
renderWindow(self.window, self.window.belowEnable, windowBelow);
@ -80,8 +80,8 @@ auto PPUfast::Line::renderMode7HD(PPUfast::IO::Background& self, uint source) ->
//only compute color again when coordinates have changed
if(pixelX != pixelXp || pixelY != pixelYp) {
uint tile = io.mode7.repeat == 3 && ((pixelX | pixelY) & ~1023) ? 0 : (ppufast.vram[(pixelY >> 3 & 127) * 128 + (pixelX >> 3 & 127)] & 0xff);
uint palette = io.mode7.repeat == 2 && ((pixelX | pixelY) & ~1023) ? 0 : (ppufast.vram[(((pixelY & 7) << 3) + (pixelX & 7)) + (tile << 6)] >> 8);
uint tile = io.mode7.repeat == 3 && ((pixelX | pixelY) & ~1023) ? 0 : (ppu.vram[(pixelY >> 3 & 127) * 128 + (pixelX >> 3 & 127)] & 0xff);
uint palette = io.mode7.repeat == 2 && ((pixelX | pixelY) & ~1023) ? 0 : (ppu.vram[(((pixelY & 7) << 3) + (pixelX & 7)) + (tile << 6)] >> 8);
uint priority;
if(!extbg) {
@ -110,7 +110,7 @@ auto PPUfast::Line::renderMode7HD(PPUfast::IO::Background& self, uint source) ->
}
}
if(ppufast.ss()) {
if(ppu.ss()) {
uint divisor = scale * scale;
for(uint p : range(256)) {
uint ab = 0, bb = 0;
@ -139,7 +139,7 @@ auto PPUfast::Line::renderMode7HD(PPUfast::IO::Background& self, uint source) ->
}
//interpolation and extrapolation
auto PPUfast::Line::lerp(float pa, float va, float pb, float vb, float pr) -> float {
auto PPU::Line::lerp(float pa, float va, float pb, float vb, float pr) -> float {
if(va == vb || pr == pa) return va;
if(pr == pb) return vb;
return va + (vb - va) / (pb - pa) * (pr - pa);

View File

@ -1,19 +1,19 @@
auto PPUfast::Line::renderObject(PPUfast::IO::Object& self) -> void {
auto PPU::Line::renderObject(PPU::IO::Object& self) -> void {
if(!self.aboveEnable && !self.belowEnable) return;
array<bool[256]> windowAbove;
array<bool[256]> windowBelow;
bool windowAbove[256];
bool windowBelow[256];
renderWindow(self.window, self.window.aboveEnable, windowAbove);
renderWindow(self.window, self.window.belowEnable, windowBelow);
uint itemCount = 0;
uint tileCount = 0;
for(auto n : range(ppufast.ItemLimit)) items[n].valid = false;
for(auto n : range(ppufast.TileLimit)) tiles[n].valid = false;
for(auto n : range(ppu.ItemLimit)) items[n].valid = false;
for(auto n : range(ppu.TileLimit)) tiles[n].valid = false;
for(auto n : range(128)) {
ObjectItem item{true, self.first + n};
const auto& object = ppufast.objects[item.index];
const auto& object = ppu.objects[item.index];
if(object.size == 0) {
static const uint widths[] = { 8, 8, 8, 16, 16, 32, 16, 16};
@ -33,16 +33,16 @@ auto PPUfast::Line::renderObject(PPUfast::IO::Object& self) -> void {
if((y >= object.y && y < object.y + height)
|| (object.y + height >= 256 && y < (object.y + height & 255))
) {
if(itemCount++ >= ppufast.ItemLimit) break;
if(itemCount++ >= ppu.ItemLimit) break;
items[itemCount - 1] = item;
}
}
for(int n : reverse(range(ppufast.ItemLimit))) {
for(int n : reverse(range(ppu.ItemLimit))) {
const auto& item = items[n];
if(!item.valid) continue;
const auto& object = ppufast.objects[item.index];
const auto& object = ppu.objects[item.index];
uint tileWidth = item.width >> 3;
int x = object.x;
int y = this->y - object.y & 0xff;
@ -59,7 +59,7 @@ auto PPUfast::Line::renderObject(PPUfast::IO::Object& self) -> void {
}
if(self.interlace) {
y = !object.vflip ? y + ppufast.field() : y - ppufast.field();
y = !object.vflip ? y + ppu.field() : y - ppu.field();
}
x &= 511;
@ -85,22 +85,22 @@ auto PPUfast::Line::renderObject(PPUfast::IO::Object& self) -> void {
uint address = tiledataAddress + ((characterY + (characterX + mirrorX & 15)) << 4);
tile.number = address >> 4;
if(tileCount++ >= ppufast.TileLimit) break;
if(tileCount++ >= ppu.TileLimit) break;
tiles[tileCount - 1] = tile;
}
}
ppufast.io.obj.rangeOver |= itemCount > ppufast.ItemLimit;
ppufast.io.obj.timeOver |= tileCount > ppufast.TileLimit;
ppu.io.obj.rangeOver |= itemCount > ppu.ItemLimit;
ppu.io.obj.timeOver |= tileCount > ppu.TileLimit;
uint8 palette[256] = {};
uint8 priority[256] = {};
uint8_t palette[256] = {};
uint8_t priority[256] = {};
for(uint n : range(ppufast.TileLimit)) {
for(uint n : range(ppu.TileLimit)) {
const auto& tile = tiles[n];
if(!tile.valid) continue;
auto tiledata = ppufast.tilecache[TileMode::BPP4] + (tile.number << 6) + ((tile.y & 7) << 3);
auto tiledata = ppu.tilecache[TileMode::BPP4] + (tile.number << 6) + ((tile.y & 7) << 3);
uint tileX = tile.x;
uint mirrorX = tile.hflip ? 7 : 0;
for(uint x : range(8)) {
@ -123,16 +123,16 @@ auto PPUfast::Line::renderObject(PPUfast::IO::Object& self) -> void {
}
}
auto PPUfast::oamAddressReset() -> void {
auto PPU::oamAddressReset() -> void {
io.oamAddress = io.oamBaseAddress;
oamSetFirstObject();
}
auto PPUfast::oamSetFirstObject() -> void {
io.obj.first = !io.oamPriority ? 0 : uint(io.oamAddress >> 2);
auto PPU::oamSetFirstObject() -> void {
io.obj.first = !io.oamPriority ? 0 : io.oamAddress >> 2;
}
auto PPUfast::readObject(uint10 address) -> uint8 {
auto PPU::readObject(uint10 address) -> uint8 {
if(!(address & 0x200)) {
uint n = address >> 2; //object#
address &= 3;
@ -161,7 +161,7 @@ auto PPUfast::readObject(uint10 address) -> uint8 {
}
}
auto PPUfast::writeObject(uint10 address, uint8 data) -> void {
auto PPU::writeObject(uint10 address, uint8 data) -> void {
if(!(address & 0x200)) {
uint n = address >> 2; //object#
address &= 3;

View File

@ -4,7 +4,10 @@ namespace SuperFamicom {
PPU& ppubase = ppu;
PPUfast ppufast;
#define PPU PPUfast
#define ppu ppufast
PPU ppu;
#include "io.cpp"
#include "line.cpp"
#include "background.cpp"
@ -14,18 +17,20 @@ PPUfast ppufast;
#include "window.cpp"
#include "serialization.cpp"
auto PPUfast::interlace() const -> bool { return ppubase.display.interlace; }
auto PPUfast::overscan() const -> bool { return ppubase.display.overscan; }
auto PPUfast::vdisp() const -> uint { return ppubase.display.vdisp; }
auto PPUfast::hires() const -> bool { return latch.hires; }
auto PPUfast::hd() const -> bool { return latch.hd; }
auto PPUfast::ss() const -> bool { return latch.ss; }
auto PPUfast::hdScale() const -> uint { return configuration.hacks.ppu.mode7.scale; }
auto PPUfast::hdPerspective() const -> bool { return configuration.hacks.ppu.mode7.perspective; }
auto PPUfast::hdSupersample() const -> bool { return configuration.hacks.ppu.mode7.supersample; }
auto PPUfast::hdMosaic() const -> bool { return configuration.hacks.ppu.mode7.mosaic; }
auto PPU::interlace() const -> bool { return ppubase.display.interlace; }
auto PPU::overscan() const -> bool { return ppubase.display.overscan; }
auto PPU::vdisp() const -> uint { return ppubase.display.vdisp; }
auto PPU::hires() const -> bool { return latch.hires; }
auto PPU::hd() const -> bool { return latch.hd; }
auto PPU::ss() const -> bool { return latch.ss; }
#undef ppu
auto PPU::hdScale() const -> uint { return configuration.hacks.ppu.mode7.scale; }
auto PPU::hdPerspective() const -> bool { return configuration.hacks.ppu.mode7.perspective; }
auto PPU::hdSupersample() const -> bool { return configuration.hacks.ppu.mode7.supersample; }
auto PPU::hdMosaic() const -> bool { return configuration.hacks.ppu.mode7.mosaic; }
#define ppu ppufast
PPUfast::PPUfast() {
PPU::PPU() {
for(uint l : range(16)) {
for(uint r : range(32)) {
for(uint g : range(32)) {
@ -34,38 +39,38 @@ PPUfast::PPUfast() {
uint ar = (luma * r + 0.5);
uint ag = (luma * g + 0.5);
uint ab = (luma * b + 0.5);
lightTable[l][(r << 10) + (g << 5) + b] = (ab << 10) + (ag << 5) + ar;
lightTable[l][r << 10 | g << 5 | b << 0] = ab << 10 | ag << 5 | ar << 0;
}
}
}
}
tilecache[TileMode::BPP2] = new uint8[4096 * 8 * 8];
tilecache[TileMode::BPP4] = new uint8[2048 * 8 * 8];
tilecache[TileMode::BPP8] = new uint8[1024 * 8 * 8];
tilecache[TileMode::BPP2] = new uint8_t[4096 * 8 * 8];
tilecache[TileMode::BPP4] = new uint8_t[2048 * 8 * 8];
tilecache[TileMode::BPP8] = new uint8_t[1024 * 8 * 8];
for(uint y : range(lines.size())) {
for(uint y : range(240)) {
lines[y].y = y;
}
}
PPUfast::~PPUfast() {
PPU::~PPU() {
delete[] tilecache[TileMode::BPP2];
delete[] tilecache[TileMode::BPP4];
delete[] tilecache[TileMode::BPP8];
}
auto PPUfast::Enter() -> void {
while(true) scheduler.synchronize(), ppufast.main();
auto PPU::Enter() -> void {
while(true) scheduler.synchronize(), ppu.main();
}
auto PPUfast::step(uint clocks) -> void {
auto PPU::step(uint clocks) -> void {
tick(clocks);
Thread::step(clocks);
synchronize(cpu);
}
auto PPUfast::main() -> void {
auto PPU::main() -> void {
scanline();
if(system.frameCounter == 0) {
@ -86,7 +91,7 @@ auto PPUfast::main() -> void {
step(lineclocks() - hcounter());
}
auto PPUfast::scanline() -> void {
auto PPU::scanline() -> void {
if(vcounter() == 0) {
ppubase.display.interlace = io.interlace;
ppubase.display.overscan = io.overscan;
@ -114,7 +119,7 @@ auto PPUfast::scanline() -> void {
}
}
auto PPUfast::refresh() -> void {
auto PPU::refresh() -> void {
if(system.frameCounter == 0) {
auto output = this->output;
uint pitch, width, height;
@ -148,17 +153,17 @@ auto PPUfast::refresh() -> void {
if(system.frameCounter++ >= system.frameSkip) system.frameCounter = 0;
}
auto PPUfast::load() -> bool {
auto PPU::load() -> bool {
return true;
}
auto PPUfast::power(bool reset) -> void {
create(Enter, system.cpuFrequency());
auto PPU::power(bool reset) -> void {
Thread::create(Enter, system.cpuFrequency());
PPUcounter::reset();
memory::fill<uint16>(output, 1024 * 960);
function<auto (uint, uint8) -> uint8> reader{&PPUfast::readIO, this};
function<auto (uint, uint8) -> void> writer{&PPUfast::writeIO, this};
function<uint8 (uint, uint8)> reader{&PPU::readIO, this};
function<void (uint, uint8)> writer{&PPU::writeIO, this};
bus.map(reader, writer, "00-3f,80-bf:2100-213f");
if(!reset) {
@ -174,6 +179,7 @@ auto PPUfast::power(bool reset) -> void {
io = {};
updateVideoMode();
#undef ppu
ItemLimit = !configuration.hacks.ppu.noSpriteLimit ? 32 : 128;
TileLimit = !configuration.hacks.ppu.noSpriteLimit ? 34 : 128;

View File

@ -5,7 +5,9 @@
//* vertical mosaic coordinates are not exact
//* (hardware-mod) 128KB VRAM mode not supported
struct PPUfast : Thread, PPUcounter {
#define PPU PPUfast
struct PPU : Thread, PPUcounter {
alwaysinline auto interlace() const -> bool;
alwaysinline auto overscan() const -> bool;
alwaysinline auto vdisp() const -> uint;
@ -18,8 +20,8 @@ struct PPUfast : Thread, PPUcounter {
alwaysinline auto hdMosaic() const -> bool;
//ppu.cpp
PPUfast();
~PPUfast();
PPU();
~PPU();
static auto Enter() -> void;
alwaysinline auto step(uint clocks) -> void;
@ -47,24 +49,24 @@ public:
bool hd = 0;
bool ss = 0;
uint16 vram = 0;
uint8 oam = 0;
uint8 cgram = 0;
uint16_t vram = 0;
uint8_t oam = 0;
uint8_t cgram = 0;
uint10 oamAddress = 0;
uint8 cgramAddress = 0;
uint8_t cgramAddress = 0;
uint8 mode7 = 0;
uint8_t mode7 = 0;
bool counters = 0;
bool hcounter = 0; //hdot
bool vcounter = 0;
struct PPU {
struct PPUstate {
//serialization.cpp
auto serialize(serializer&) -> void;
uint8 mdr = 0;
uint8 bgofs = 0;
uint8_t mdr = 0;
uint8_t bgofs = 0;
} ppu1, ppu2;
};
@ -114,10 +116,10 @@ public:
//serialization.cpp
auto serialize(serializer&) -> void;
uint8 oneLeft = 0;
uint8 oneRight = 0;
uint8 twoLeft = 0;
uint8 twoRight = 0;
uint8_t oneLeft = 0;
uint8_t oneRight = 0;
uint8_t twoLeft = 0;
uint8_t twoRight = 0;
} window;
struct WindowLayer {
@ -200,8 +202,8 @@ public:
auto serialize(serializer&) -> void;
uint9 x = 0;
uint8 y = 0;
uint8 character = 0;
uint8_t y = 0;
uint8_t character = 0;
bool nameselect = 0;
bool vflip = 0;
bool hflip = 0;
@ -213,16 +215,16 @@ public:
struct ObjectItem {
bool valid = 0;
uint7 index = 0;
uint8 width = 0;
uint8 height = 0;
uint8_t width = 0;
uint8_t height = 0;
};
struct ObjectTile {
bool valid = 0;
uint9 x = 0;
uint8 y = 0;
uint8_t y = 0;
uint2 priority = 0;
uint8 palette = 0;
uint8_t palette = 0;
bool hflip = 0;
uint11 number = 0;
};
@ -235,14 +237,14 @@ public:
//io.cpp
auto latchCounters() -> void;
alwaysinline auto vramAddress() const -> uint15;
alwaysinline auto vramAddress() const -> uint;
alwaysinline auto readVRAM() -> uint16;
template<bool Byte> alwaysinline auto writeVRAM(uint8 data) -> void;
template<bool Byte> alwaysinline auto writeVRAM(uint8_t data) -> void;
alwaysinline auto updateTiledata(uint address) -> void;
alwaysinline auto readOAM(uint10 address) -> uint8;
alwaysinline auto writeOAM(uint10 address, uint8 data) -> void;
template<bool Byte> alwaysinline auto readCGRAM(uint8 address) -> uint8;
alwaysinline auto writeCGRAM(uint8 address, uint15 data) -> void;
alwaysinline auto writeOAM(uint10 address, uint8_t data) -> void;
template<bool Byte> alwaysinline auto readCGRAM(uint8_t address) -> uint8;
alwaysinline auto writeCGRAM(uint8_t address, uint15 data) -> void;
auto readIO(uint address, uint8 data) -> uint8;
auto writeIO(uint address, uint8 data) -> void;
auto updateVideoMode() -> void;
@ -257,14 +259,14 @@ public:
Latch latch;
IO io;
uint16 vram[32 * 1024] = {};
uint16 cgram[256] = {};
uint16_t vram[32 * 1024] = {};
uint16_t cgram[256] = {};
Object objects[128] = {};
//[unserialized]
uint16 output[2304 * 2160] = {};
uint16 lightTable[16][32768] = {};
uint8* tilecache[3] = {}; //bitplane -> bitmap tiledata
uint16_t output[2304 * 2160] = {};
uint16_t lightTable[16][32768] = {};
uint8_t* tilecache[3] = {}; //bitplane -> bitmap tiledata
uint ItemLimit = 0;
uint TileLimit = 0;
@ -272,51 +274,51 @@ public:
//line.cpp
static auto flush() -> void;
auto render() -> void;
auto pixel(uint x, Pixel above, Pixel below) const -> uint16;
auto blend(uint x, uint y, bool halve) const -> uint16;
alwaysinline auto directColor(uint paletteIndex, uint paletteColor) const -> uint16;
auto pixel(uint x, Pixel above, Pixel below) const -> uint16_t;
auto blend(uint x, uint y, bool halve) const -> uint16_t;
alwaysinline auto directColor(uint paletteIndex, uint paletteColor) const -> uint16_t;
alwaysinline auto plotAbove(uint x, uint source, uint priority, uint color) -> void;
alwaysinline auto plotBelow(uint x, uint source, uint priority, uint color) -> void;
alwaysinline auto plotHD(Pixel*, uint x, uint source, uint priority, uint color, bool hires, bool subpixel) -> void;
//background.cpp
auto renderBackground(PPUfast::IO::Background&, uint source) -> void;
auto getTile(PPUfast::IO::Background&, uint hoffset, uint voffset) -> uint;
auto renderBackground(PPU::IO::Background&, uint source) -> void;
auto getTile(PPU::IO::Background&, uint hoffset, uint voffset) -> uint;
//mode7.cpp
auto renderMode7(PPUfast::IO::Background&, uint source) -> void;
auto renderMode7(PPU::IO::Background&, uint source) -> void;
//mode7hd.cpp
auto renderMode7HD(PPUfast::IO::Background&, uint source) -> void;
auto renderMode7HD(PPU::IO::Background&, uint source) -> void;
alwaysinline auto lerp(float pa, float va, float pb, float vb, float pr) -> float;
//object.cpp
auto renderObject(PPUfast::IO::Object&) -> void;
auto renderObject(PPU::IO::Object&) -> void;
//window.cpp
auto renderWindow(PPUfast::IO::WindowLayer&, bool, array<bool[256]>&) -> void;
auto renderWindow(PPUfast::IO::WindowColor&, uint, array<bool[256]>&) -> void;
auto renderWindow(PPU::IO::WindowLayer&, bool enable, bool output[256]) -> void;
auto renderWindow(PPU::IO::WindowColor&, uint mask, bool output[256]) -> void;
//[unserialized]
uint9 y; //constant
IO io;
array<uint16[256]> cgram;
uint16_t cgram[256];
array<ObjectItem[128]> items; //32 on real hardware
array<ObjectTile[128]> tiles; //34 on real hardware; 1024 max (128 * 64-width tiles)
ObjectItem items[128]; //32 on real hardware
ObjectTile tiles[128]; //34 on real hardware; 1024 max (128 * 64-width tiles)
Pixel above[256 * 9 * 9];
Pixel below[256 * 9 * 9];
array<bool[256]> windowAbove;
array<bool[256]> windowBelow;
bool windowAbove[256];
bool windowBelow[256];
//flush()
static uint start;
static uint count;
};
array<Line[240]> lines;
Line lines[240];
//used to help detect when the video output size changes between frames to clear overscan area.
struct Frame {
@ -326,4 +328,6 @@ public:
} frame;
};
extern PPUfast ppufast;
extern PPU ppufast;
#undef PPU

View File

@ -1,4 +1,4 @@
auto PPUfast::serialize(serializer& s) -> void {
auto PPU::serialize(serializer& s) -> void {
Thread::serialize(s);
PPUcounter::serialize(s);
@ -13,7 +13,7 @@ auto PPUfast::serialize(serializer& s) -> void {
Line::count = 0;
}
auto PPUfast::Latch::serialize(serializer& s) -> void {
auto PPU::Latch::serialize(serializer& s) -> void {
s.integer(interlace);
s.integer(overscan);
s.integer(hires);
@ -32,12 +32,12 @@ auto PPUfast::Latch::serialize(serializer& s) -> void {
ppu2.serialize(s);
}
auto PPUfast::Latch::PPU::serialize(serializer& s) -> void {
auto PPU::Latch::PPUstate::serialize(serializer& s) -> void {
s.integer(mdr);
s.integer(bgofs);
}
auto PPUfast::IO::serialize(serializer& s) -> void {
auto PPU::IO::serialize(serializer& s) -> void {
s.integer(displayDisable);
s.integer(displayBrightness);
s.integer(oamBaseAddress);
@ -69,7 +69,7 @@ auto PPUfast::IO::serialize(serializer& s) -> void {
col.serialize(s);
}
auto PPUfast::IO::Mode7::serialize(serializer& s) -> void {
auto PPU::IO::Mode7::serialize(serializer& s) -> void {
s.integer(hflip);
s.integer(vflip);
s.integer(repeat);
@ -83,14 +83,14 @@ auto PPUfast::IO::Mode7::serialize(serializer& s) -> void {
s.integer(voffset);
}
auto PPUfast::IO::Window::serialize(serializer& s) -> void {
auto PPU::IO::Window::serialize(serializer& s) -> void {
s.integer(oneLeft);
s.integer(oneRight);
s.integer(twoLeft);
s.integer(twoRight);
}
auto PPUfast::IO::WindowLayer::serialize(serializer& s) -> void {
auto PPU::IO::WindowLayer::serialize(serializer& s) -> void {
s.integer(oneEnable);
s.integer(oneInvert);
s.integer(twoEnable);
@ -100,7 +100,7 @@ auto PPUfast::IO::WindowLayer::serialize(serializer& s) -> void {
s.integer(belowEnable);
}
auto PPUfast::IO::WindowColor::serialize(serializer& s) -> void {
auto PPU::IO::WindowColor::serialize(serializer& s) -> void {
s.integer(oneEnable);
s.integer(oneInvert);
s.integer(twoEnable);
@ -110,7 +110,7 @@ auto PPUfast::IO::WindowColor::serialize(serializer& s) -> void {
s.integer(belowMask);
}
auto PPUfast::IO::Background::serialize(serializer& s) -> void {
auto PPU::IO::Background::serialize(serializer& s) -> void {
window.serialize(s);
s.integer(aboveEnable);
s.integer(belowEnable);
@ -125,7 +125,7 @@ auto PPUfast::IO::Background::serialize(serializer& s) -> void {
s.array(priority);
}
auto PPUfast::IO::Object::serialize(serializer& s) -> void {
auto PPU::IO::Object::serialize(serializer& s) -> void {
window.serialize(s);
s.integer(aboveEnable);
s.integer(belowEnable);
@ -139,7 +139,7 @@ auto PPUfast::IO::Object::serialize(serializer& s) -> void {
s.array(priority);
}
auto PPUfast::IO::Color::serialize(serializer& s) -> void {
auto PPU::IO::Color::serialize(serializer& s) -> void {
window.serialize(s);
s.array(enable);
s.integer(directColor);
@ -149,7 +149,7 @@ auto PPUfast::IO::Color::serialize(serializer& s) -> void {
s.integer(fixedColor);
}
auto PPUfast::Object::serialize(serializer& s) -> void {
auto PPU::Object::serialize(serializer& s) -> void {
s.integer(x);
s.integer(y);
s.integer(character);

View File

@ -1,6 +1,6 @@
auto PPUfast::Line::renderWindow(PPUfast::IO::WindowLayer& self, bool enable, array<bool[256]>& output) -> void {
auto PPU::Line::renderWindow(PPU::IO::WindowLayer& self, bool enable, bool output[256]) -> void {
if(!enable || (!self.oneEnable && !self.twoEnable)) {
output.fill(0);
memory::fill<bool>(output, 256, 0);
return;
}
@ -32,17 +32,17 @@ auto PPUfast::Line::renderWindow(PPUfast::IO::WindowLayer& self, bool enable, ar
}
}
auto PPUfast::Line::renderWindow(PPUfast::IO::WindowColor& self, uint mask, array<bool[256]>& output) -> void {
auto PPU::Line::renderWindow(PPU::IO::WindowColor& self, uint mask, bool output[256]) -> void {
bool set, clear;
switch(mask) {
case 0: output.fill(1); return; //always
case 0: memory::fill<bool>(output, 256, 1); return; //always
case 1: set = 1, clear = 0; break; //inside
case 2: set = 0, clear = 1; break; //outside
case 3: output.fill(0); return; //never
case 3: memory::fill<bool>(output, 256, 0); return; //never
}
if(!self.oneEnable && !self.twoEnable) {
output.fill(clear);
memory::fill<bool>(output, 256, clear);
return;
}

View File

@ -26,7 +26,7 @@ auto PPU::Object::scanline() -> void {
auto oamTile = t.tile[t.active];
if(t.y == ppu.vdisp() && !ppu.io.displayDisable) addressReset();
if(t.y >= ppu.vdisp() - 1) return;
if(t.y >= ppu.vdisp() - 1 || ppu.io.displayDisable) return;
for(auto n : range(32)) oamItem[n].valid = false;
for(auto n : range(34)) oamTile[n].valid = false;

View File

@ -62,7 +62,7 @@ auto PPU::main() -> void {
bg3.begin();
bg4.begin();
if(vcounter() <= 239) {
if(vcounter() < vdisp()) {
for(int pixel = -7; pixel <= 255; pixel++) {
bg1.run(1);
bg2.run(1);
@ -210,16 +210,17 @@ auto PPU::power(bool reset) -> void {
screen.power();
updateVideoMode();
frame();
}
auto PPU::scanline() -> void {
if(vcounter() == 0) {
frame();
display.interlace = io.interlace;
display.overscan = io.overscan;
bg1.frame();
bg2.frame();
bg3.frame();
bg4.frame();
obj.frame();
}
bg1.scanline();
@ -235,12 +236,6 @@ auto PPU::scanline() -> void {
}
}
auto PPU::frame() -> void {
obj.frame();
display.interlace = io.interlace;
display.overscan = io.overscan;
}
auto PPU::refresh() -> void {
if(system.fastPPU()) {
return ppufast.refresh();

View File

@ -50,7 +50,6 @@ private:
} display;
auto scanline() -> void;
auto frame() -> void;
auto refresh() -> void;
struct {

View File

@ -162,7 +162,7 @@ private:
}
-(void) reshape {
video->output();
video->output(0, 0);
}
@end