mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-06 12:16:47 +02:00
Update to v106r34 release.
byuu says: Changelog: - sfc/ppu-fast: - don't use mosaicSize unless mosaicEnable is set - fix background tiles that aren't 8x8 in size - flush (render) queued lines whenever VRAM or OAM are modified mid-frame - queue tile outputs to buffer for object rendering final pass - fix object window mask indexing - disable color bleed when output width is 256 pixels - handle reset(bool) events - implemented save states - icarus: fixed SPC7110-RAM-EPSONRTC mapping typo [hex_usr] - bsnes: fixed overscan masking mode when output height is 240 Todo: - sfc/ppu-fast: should not have deleted the tilecache freeing in ~PPU() - ruby/input/carbon: change setPath() call to setPathID() Errata: - Rendering Ranger R2 crashes at startup, seems to be an issue with the expansion port device Bug reports on the new fast SNES PPU are now welcome.
This commit is contained in:
@@ -12,7 +12,7 @@ using namespace nall;
|
|||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "106.33";
|
static const string Version = "106.34";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "https://byuu.org/";
|
static const string Website = "https://byuu.org/";
|
||||||
|
@@ -27,7 +27,7 @@ auto PPU::Line::renderBackground(PPU::IO::Background& self, uint source) -> void
|
|||||||
uint hmask = (width << self.tileSize << self.screenSize.bit(0)) - 1;
|
uint hmask = (width << self.tileSize << self.screenSize.bit(0)) - 1;
|
||||||
uint vmask = (width << self.tileSize << self.screenSize.bit(1)) - 1;
|
uint vmask = (width << self.tileSize << self.screenSize.bit(1)) - 1;
|
||||||
|
|
||||||
uint y = this->y - this->y % (1 + io.mosaicSize);
|
uint y = this->y - (self.mosaicEnable ? this->y % (1 + io.mosaicSize) : 0);
|
||||||
if(hires) {
|
if(hires) {
|
||||||
hscroll <<= 1;
|
hscroll <<= 1;
|
||||||
if(io.interlace) y = y << 1 | ppu.field();
|
if(io.interlace) y = y << 1 | ppu.field();
|
||||||
@@ -72,20 +72,20 @@ auto PPU::Line::renderBackground(PPU::IO::Background& self, uint source) -> void
|
|||||||
uint tileNumber = getTile(self, hoffset, voffset);
|
uint tileNumber = getTile(self, hoffset, voffset);
|
||||||
uint mirrorY = tileNumber & 0x8000 ? 7 : 0;
|
uint mirrorY = tileNumber & 0x8000 ? 7 : 0;
|
||||||
uint mirrorX = tileNumber & 0x4000 ? 7 : 0;
|
uint mirrorX = tileNumber & 0x4000 ? 7 : 0;
|
||||||
uint tilePriority = tileNumber & 0x2000 ? self.priority[1] : self.priority[0];
|
uint tilePriority = self.priority[bool(tileNumber & 0x2000)];
|
||||||
uint paletteNumber = tileNumber >> 10 & 7;
|
uint paletteNumber = tileNumber >> 10 & 7;
|
||||||
uint paletteIndex = paletteBase + (paletteNumber << paletteShift) & 0xff;
|
uint paletteIndex = paletteBase + (paletteNumber << paletteShift) & 0xff;
|
||||||
|
|
||||||
if(tileWidth == 4 && (hoffset & 8) - 1 != mirrorX) tileNumber += 1;
|
if(tileWidth == 4 && (bool(hoffset & 8) ^ bool(mirrorX))) tileNumber += 1;
|
||||||
if(tileHeight == 4 && (voffset & 8) - 1 != mirrorY) tileNumber += 16;
|
if(tileHeight == 4 && (bool(voffset & 8) ^ bool(mirrorY))) tileNumber += 16;
|
||||||
tileNumber = (tileNumber & 0x03ff) + tiledataIndex & tileMask;
|
tileNumber = (tileNumber & 0x03ff) + tiledataIndex & tileMask;
|
||||||
|
|
||||||
auto tiledata = ppu.tilecache[self.tileMode] + (tileNumber << 6);
|
auto tiledata = ppu.tilecache[self.tileMode] + (tileNumber << 6);
|
||||||
tiledata += ((voffset & 7) ^ mirrorY) << 3;
|
tiledata += (voffset & 7 ^ mirrorY) << 3;
|
||||||
|
|
||||||
for(uint tileX = 0; tileX < 8; tileX++, x++) {
|
for(uint tileX = 0; tileX < 8; tileX++, x++) {
|
||||||
if(x & width) continue; //x < 0 || x >= width
|
if(x & width) continue; //x < 0 || x >= width
|
||||||
if(--mosaicCounter == 0) {
|
if(!self.mosaicEnable || --mosaicCounter == 0) {
|
||||||
mosaicCounter = 1 + io.mosaicSize;
|
mosaicCounter = 1 + io.mosaicSize;
|
||||||
mosaicPalette = tiledata[tileX ^ mirrorX];
|
mosaicPalette = tiledata[tileX ^ mirrorX];
|
||||||
mosaicPriority = tilePriority;
|
mosaicPriority = tilePriority;
|
||||||
|
@@ -23,9 +23,13 @@ auto PPU::readVRAM() -> uint16 {
|
|||||||
|
|
||||||
auto PPU::writeVRAM(uint1 byte, uint8 data) -> void {
|
auto PPU::writeVRAM(uint1 byte, uint8 data) -> void {
|
||||||
if(!io.displayDisable && cpu.vcounter() < vdisp()) return;
|
if(!io.displayDisable && cpu.vcounter() < vdisp()) return;
|
||||||
|
Line::flush();
|
||||||
auto address = vramAddress();
|
auto address = vramAddress();
|
||||||
vram[address].byte(byte) = data;
|
vram[address].byte(byte) = data;
|
||||||
|
updateTiledata(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PPU::updateTiledata(uint15 address) -> void {
|
||||||
auto word = vram[address];
|
auto word = vram[address];
|
||||||
auto line2bpp = tilecache[TileMode::BPP2] + (address.bits(3,14) << 6) + (address.bits(0,2) << 3);
|
auto line2bpp = tilecache[TileMode::BPP2] + (address.bits(3,14) << 6) + (address.bits(0,2) << 3);
|
||||||
auto line4bpp = tilecache[TileMode::BPP4] + (address.bits(4,14) << 6) + (address.bits(0,2) << 3);
|
auto line4bpp = tilecache[TileMode::BPP4] + (address.bits(4,14) << 6) + (address.bits(0,2) << 3);
|
||||||
@@ -48,6 +52,7 @@ auto PPU::readOAM(uint10 address) -> uint8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::writeOAM(uint10 address, uint8 data) -> void {
|
auto PPU::writeOAM(uint10 address, uint8 data) -> void {
|
||||||
|
Line::flush();
|
||||||
if(!io.displayDisable && cpu.vcounter() < vdisp()) address = latch.oamAddress;
|
if(!io.displayDisable && cpu.vcounter() < vdisp()) address = latch.oamAddress;
|
||||||
return writeObject(address, data);
|
return writeObject(address, data);
|
||||||
}
|
}
|
||||||
@@ -78,22 +83,22 @@ auto PPU::readIO(uint24 address, uint8 data) -> uint8 {
|
|||||||
case 0x2116: case 0x2118: case 0x2119: case 0x211a:
|
case 0x2116: case 0x2118: case 0x2119: case 0x211a:
|
||||||
case 0x2124: case 0x2125: case 0x2126: case 0x2128:
|
case 0x2124: case 0x2125: case 0x2126: case 0x2128:
|
||||||
case 0x2129: case 0x212a: {
|
case 0x2129: case 0x212a: {
|
||||||
return ppu1.mdr;
|
return latch.ppu1.mdr;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x2134: { //MPYL
|
case 0x2134: { //MPYL
|
||||||
uint24 result = (int16)io.mode7.a * (int8)(io.mode7.b >> 8);
|
uint24 result = (int16)io.mode7.a * (int8)(io.mode7.b >> 8);
|
||||||
return ppu1.mdr = result.byte(0);
|
return latch.ppu1.mdr = result.byte(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x2135: { //MPYM
|
case 0x2135: { //MPYM
|
||||||
uint24 result = (int16)io.mode7.a * (int8)(io.mode7.b >> 8);
|
uint24 result = (int16)io.mode7.a * (int8)(io.mode7.b >> 8);
|
||||||
return ppu1.mdr = result.byte(1);
|
return latch.ppu1.mdr = result.byte(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x2136: { //MPYH
|
case 0x2136: { //MPYH
|
||||||
uint24 result = (int16)io.mode7.a * (int8)(io.mode7.b >> 8);
|
uint24 result = (int16)io.mode7.a * (int8)(io.mode7.b >> 8);
|
||||||
return ppu1.mdr = result.byte(2);
|
return latch.ppu1.mdr = result.byte(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x2137: { //SLHV
|
case 0x2137: { //SLHV
|
||||||
@@ -102,77 +107,77 @@ auto PPU::readIO(uint24 address, uint8 data) -> uint8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 0x2138: { //OAMDATAREAD
|
case 0x2138: { //OAMDATAREAD
|
||||||
ppu1.mdr = readOAM(io.oamAddress++);
|
data = readOAM(io.oamAddress++);
|
||||||
oamSetFirstObject();
|
oamSetFirstObject();
|
||||||
return ppu1.mdr;
|
return latch.ppu1.mdr = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x2139: { //VMDATALREAD
|
case 0x2139: { //VMDATALREAD
|
||||||
ppu1.mdr = latch.vram.byte(0);
|
data = latch.vram.byte(0);
|
||||||
if(io.vramIncrementMode == 0) {
|
if(io.vramIncrementMode == 0) {
|
||||||
latch.vram = readVRAM();
|
latch.vram = readVRAM();
|
||||||
io.vramAddress += io.vramIncrementSize;
|
io.vramAddress += io.vramIncrementSize;
|
||||||
}
|
}
|
||||||
return ppu1.mdr;
|
return latch.ppu1.mdr = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x213a: { //VMDATAHREAD
|
case 0x213a: { //VMDATAHREAD
|
||||||
ppu1.mdr = latch.vram.byte(1);
|
data = latch.vram.byte(1);
|
||||||
if(io.vramIncrementMode == 1) {
|
if(io.vramIncrementMode == 1) {
|
||||||
latch.vram = readVRAM();
|
latch.vram = readVRAM();
|
||||||
io.vramAddress += io.vramIncrementSize;
|
io.vramAddress += io.vramIncrementSize;
|
||||||
}
|
}
|
||||||
return ppu1.mdr;
|
return latch.ppu1.mdr = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x213b: { //CGDATAREAD
|
case 0x213b: { //CGDATAREAD
|
||||||
if(io.cgramAddressLatch++ == 0) {
|
if(io.cgramAddressLatch++ == 0) {
|
||||||
ppu2.mdr.bits(0,7) = readCGRAM(0, io.cgramAddress);
|
latch.ppu2.mdr.bits(0,7) = readCGRAM(0, io.cgramAddress);
|
||||||
} else {
|
} else {
|
||||||
ppu2.mdr.bits(0,6) = readCGRAM(1, io.cgramAddress++);
|
latch.ppu2.mdr.bits(0,6) = readCGRAM(1, io.cgramAddress++);
|
||||||
}
|
}
|
||||||
return ppu2.mdr;
|
return latch.ppu2.mdr;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x213c: { //OPHCT
|
case 0x213c: { //OPHCT
|
||||||
if(latch.hcounter++ == 0) {
|
if(latch.hcounter++ == 0) {
|
||||||
ppu2.mdr.bits(0,7) = io.hcounter.bits(0,7);
|
latch.ppu2.mdr.bits(0,7) = io.hcounter.bits(0,7);
|
||||||
} else {
|
} else {
|
||||||
ppu2.mdr.bit(0) = io.hcounter.bit(8);
|
latch.ppu2.mdr.bit(0) = io.hcounter.bit(8);
|
||||||
}
|
}
|
||||||
return ppu2.mdr;
|
return latch.ppu2.mdr;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x213d: { //OPVCT
|
case 0x213d: { //OPVCT
|
||||||
if(latch.vcounter++ == 0) {
|
if(latch.vcounter++ == 0) {
|
||||||
ppu2.mdr.bits(0,7) = io.vcounter.bits(0,7);
|
latch.ppu2.mdr.bits(0,7) = io.vcounter.bits(0,7);
|
||||||
} else {
|
} else {
|
||||||
ppu2.mdr.bit(0) = io.vcounter.bit(8);
|
latch.ppu2.mdr.bit(0) = io.vcounter.bit(8);
|
||||||
}
|
}
|
||||||
return ppu2.mdr;
|
return latch.ppu2.mdr;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x213e: { //STAT77
|
case 0x213e: { //STAT77
|
||||||
ppu1.mdr.bits(0,3) = ppu1.version;
|
latch.ppu1.mdr.bits(0,3) = 1; //PPU1 version
|
||||||
ppu1.mdr.bit(5) = 0;
|
latch.ppu1.mdr.bit(5) = 0;
|
||||||
ppu1.mdr.bit(6) = io.obj.rangeOver;
|
latch.ppu1.mdr.bit(6) = io.obj.rangeOver;
|
||||||
ppu1.mdr.bit(7) = io.obj.timeOver;
|
latch.ppu1.mdr.bit(7) = io.obj.timeOver;
|
||||||
return ppu1.mdr;
|
return latch.ppu1.mdr;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x213f: { //STAT78
|
case 0x213f: { //STAT78
|
||||||
latch.hcounter = 0;
|
latch.hcounter = 0;
|
||||||
latch.vcounter = 0;
|
latch.vcounter = 0;
|
||||||
ppu2.mdr.bits(0,3) = ppu2.version;
|
latch.ppu2.mdr.bits(0,3) = 3; //PPU2 version
|
||||||
ppu2.mdr.bit(4) = Region::PAL(); //0 = NTSC, 1 = PAL
|
latch.ppu2.mdr.bit(4) = Region::PAL(); //0 = NTSC, 1 = PAL
|
||||||
if(!cpu.pio().bit(7)) {
|
if(!cpu.pio().bit(7)) {
|
||||||
ppu2.mdr.bit(6) = 1;
|
latch.ppu2.mdr.bit(6) = 1;
|
||||||
} else {
|
} else {
|
||||||
ppu2.mdr.bit(6) = latch.counters;
|
latch.ppu2.mdr.bit(6) = latch.counters;
|
||||||
latch.counters = 0;
|
latch.counters = 0;
|
||||||
}
|
}
|
||||||
ppu2.mdr.bit(7) = field();
|
latch.ppu2.mdr.bit(7) = field();
|
||||||
return ppu2.mdr;
|
return latch.ppu2.mdr;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -286,9 +291,9 @@ auto PPU::writeIO(uint24 address, uint8 data) -> void {
|
|||||||
io.mode7.hoffset = data << 8 | latch.mode7;
|
io.mode7.hoffset = data << 8 | latch.mode7;
|
||||||
latch.mode7 = data;
|
latch.mode7 = data;
|
||||||
|
|
||||||
io.bg1.hoffset = data << 8 | (latch.bgofsPPU1 & ~7) | (latch.bgofsPPU2 & 7);
|
io.bg1.hoffset = data << 8 | (latch.ppu1.bgofs & ~7) | (latch.ppu2.bgofs & 7);
|
||||||
latch.bgofsPPU1 = data;
|
latch.ppu1.bgofs = data;
|
||||||
latch.bgofsPPU2 = data;
|
latch.ppu2.bgofs = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,47 +301,47 @@ auto PPU::writeIO(uint24 address, uint8 data) -> void {
|
|||||||
io.mode7.voffset = data << 8 | latch.mode7;
|
io.mode7.voffset = data << 8 | latch.mode7;
|
||||||
latch.mode7 = data;
|
latch.mode7 = data;
|
||||||
|
|
||||||
io.bg1.voffset = data << 8 | latch.bgofsPPU1;
|
io.bg1.voffset = data << 8 | latch.ppu1.bgofs;
|
||||||
latch.bgofsPPU1 = data;
|
latch.ppu1.bgofs = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x210f: { //BG2HOFS
|
case 0x210f: { //BG2HOFS
|
||||||
io.bg2.hoffset = data << 8 | (latch.bgofsPPU1 & ~7) | (latch.bgofsPPU2 & 7);
|
io.bg2.hoffset = data << 8 | (latch.ppu1.bgofs & ~7) | (latch.ppu2.bgofs & 7);
|
||||||
latch.bgofsPPU1 = data;
|
latch.ppu1.bgofs = data;
|
||||||
latch.bgofsPPU2 = data;
|
latch.ppu2.bgofs = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x2110: { //BG2VOFS
|
case 0x2110: { //BG2VOFS
|
||||||
io.bg2.voffset = data << 8 | latch.bgofsPPU1;
|
io.bg2.voffset = data << 8 | latch.ppu1.bgofs;
|
||||||
latch.bgofsPPU1 = data;
|
latch.ppu1.bgofs = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x2111: { //BG3HOFS
|
case 0x2111: { //BG3HOFS
|
||||||
io.bg3.hoffset = data << 8 | (latch.bgofsPPU1 & ~7) | (latch.bgofsPPU2 & 7);
|
io.bg3.hoffset = data << 8 | (latch.ppu1.bgofs & ~7) | (latch.ppu2.bgofs & 7);
|
||||||
latch.bgofsPPU1 = data;
|
latch.ppu1.bgofs = data;
|
||||||
latch.bgofsPPU2 = data;
|
latch.ppu2.bgofs = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x2112: { //BG3VOFS
|
case 0x2112: { //BG3VOFS
|
||||||
io.bg3.voffset = data << 8 | latch.bgofsPPU1;
|
io.bg3.voffset = data << 8 | latch.ppu1.bgofs;
|
||||||
latch.bgofsPPU1 = data;
|
latch.ppu1.bgofs = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x2113: { //BG4HOFS
|
case 0x2113: { //BG4HOFS
|
||||||
io.bg4.hoffset = data << 8 | (latch.bgofsPPU1 & ~7) | (latch.bgofsPPU2 & 7);
|
io.bg4.hoffset = data << 8 | (latch.ppu1.bgofs & ~7) | (latch.ppu2.bgofs & 7);
|
||||||
latch.bgofsPPU1 = data;
|
latch.ppu1.bgofs = data;
|
||||||
latch.bgofsPPU2 = data;
|
latch.ppu2.bgofs = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0x2114: { //BG4VOFS
|
case 0x2114: { //BG4VOFS
|
||||||
io.bg4.voffset = data << 8 | latch.bgofsPPU1;
|
io.bg4.voffset = data << 8 | latch.ppu1.bgofs;
|
||||||
latch.bgofsPPU1 = data;
|
latch.ppu1.bgofs = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,3 +1,17 @@
|
|||||||
|
uint PPU::Line::start = 0;
|
||||||
|
uint PPU::Line::count = 0;
|
||||||
|
|
||||||
|
auto PPU::Line::flush() -> void {
|
||||||
|
if(Line::count) {
|
||||||
|
#pragma omp parallel for
|
||||||
|
for(uint y = 0; y < Line::count; y++) {
|
||||||
|
ppu.lines[Line::start + y].render();
|
||||||
|
}
|
||||||
|
Line::start = 0;
|
||||||
|
Line::count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto PPU::Line::render() -> void {
|
auto PPU::Line::render() -> void {
|
||||||
bool hires = io.pseudoHires || io.bgMode == 5 || io.bgMode == 6;
|
bool hires = io.pseudoHires || io.bgMode == 5 || io.bgMode == 6;
|
||||||
|
|
||||||
@@ -16,7 +30,8 @@ auto PPU::Line::render() -> void {
|
|||||||
renderBackground(io.bg4, Source::BG4);
|
renderBackground(io.bg4, Source::BG4);
|
||||||
renderObject(io.obj);
|
renderObject(io.obj);
|
||||||
|
|
||||||
auto output = !ppu.interlace() || !ppu.field() ? outputLo : outputHi;
|
auto output = ppu.output + y * 1024;
|
||||||
|
if(ppu.interlace() && ppu.field()) output += 512;
|
||||||
auto width = !ppu.hires() ? 256 : 512;
|
auto width = !ppu.hires() ? 256 : 512;
|
||||||
auto luma = io.displayBrightness << 15;
|
auto luma = io.displayBrightness << 15;
|
||||||
|
|
||||||
@@ -29,13 +44,14 @@ auto PPU::Line::render() -> void {
|
|||||||
renderWindow(io.col.window, io.col.window.belowMask, windowBelow);
|
renderWindow(io.col.window, io.col.window.belowMask, windowBelow);
|
||||||
|
|
||||||
if(width == 256) for(uint x : range(width)) {
|
if(width == 256) for(uint x : range(width)) {
|
||||||
output[x] = luma | pixel(x, above[x], below[x]);
|
*output++ = luma | pixel(x, above[x], below[x]);
|
||||||
} else if(!hires) for(uint x : range(256)) {
|
} else if(!hires) for(uint x : range(256)) {
|
||||||
output[x << 1 | 0] =
|
auto color = luma | pixel(x, above[x], below[x]);
|
||||||
output[x << 1 | 1] = luma | pixel(x, above[x], below[x]);
|
*output++ = color;
|
||||||
|
*output++ = color;
|
||||||
} else for(uint x : range(256)) {
|
} else for(uint x : range(256)) {
|
||||||
output[x << 1 | 0] = luma | pixel(x, below[x], above[x]);
|
*output++ = luma | pixel(x, below[x], above[x]);
|
||||||
output[x << 1 | 1] = luma | pixel(x, above[x], below[x]);
|
*output++ = luma | pixel(x, above[x], below[x]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,9 +90,9 @@ auto PPU::Line::directColor(uint palette, uint tile) const -> uint15 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::Line::plotAbove(uint x, uint source, uint priority, uint color) -> void {
|
auto PPU::Line::plotAbove(uint x, uint source, uint priority, uint color) -> void {
|
||||||
if(priority >= above[x].priority) above[x] = {source, priority, color};
|
if(priority > above[x].priority) above[x] = {source, priority, color};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::Line::plotBelow(uint x, uint source, uint priority, uint color) -> void {
|
auto PPU::Line::plotBelow(uint x, uint source, uint priority, uint color) -> void {
|
||||||
if(priority >= below[x].priority) below[x] = {source, priority, color};
|
if(priority > below[x].priority) below[x] = {source, priority, color};
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void {
|
auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void {
|
||||||
int Y = this->y - this->y % (1 + io.mosaicSize);
|
int Y = this->y - (self.mosaicEnable ? this->y % (1 + io.mosaicSize) : 0);
|
||||||
int y = !io.mode7.vflip ? Y : 255 - Y;
|
int y = !io.mode7.vflip ? Y : 255 - Y;
|
||||||
|
|
||||||
int a = (int16)io.mode7.a;
|
int a = (int16)io.mode7.a;
|
||||||
@@ -47,7 +47,7 @@ auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void {
|
|||||||
palette &= 0x7f;
|
palette &= 0x7f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(--mosaicCounter == 0) {
|
if(!self.mosaicEnable || --mosaicCounter == 0) {
|
||||||
mosaicCounter = 1 + io.mosaicSize;
|
mosaicCounter = 1 + io.mosaicSize;
|
||||||
mosaicPalette = palette;
|
mosaicPalette = palette;
|
||||||
mosaicPriority = priority;
|
mosaicPriority = priority;
|
||||||
|
@@ -9,8 +9,8 @@ auto PPU::Line::renderObject(PPU::IO::Object& self) -> void {
|
|||||||
|
|
||||||
uint itemCount = 0;
|
uint itemCount = 0;
|
||||||
uint tileCount = 0;
|
uint tileCount = 0;
|
||||||
for(auto n : range(32)) items[n].valid = false;
|
for(auto& item : items) item.valid = false;
|
||||||
for(auto n : range(34)) tiles[n].valid = false;
|
for(auto& tile : tiles) tile.valid = false;
|
||||||
|
|
||||||
for(auto n : range(128)) {
|
for(auto n : range(128)) {
|
||||||
ObjectItem item{true, self.first + n};
|
ObjectItem item{true, self.first + n};
|
||||||
@@ -94,6 +94,9 @@ auto PPU::Line::renderObject(PPU::IO::Object& self) -> void {
|
|||||||
ppu.io.obj.rangeOver |= itemCount > 32;
|
ppu.io.obj.rangeOver |= itemCount > 32;
|
||||||
ppu.io.obj.timeOver |= tileCount > 34;
|
ppu.io.obj.timeOver |= tileCount > 34;
|
||||||
|
|
||||||
|
uint8 palette[256];
|
||||||
|
uint8 priority[256];
|
||||||
|
|
||||||
for(uint n : range(34)) {
|
for(uint n : range(34)) {
|
||||||
const auto& tile = tiles[n];
|
const auto& tile = tiles[n];
|
||||||
if(!tile.valid) continue;
|
if(!tile.valid) continue;
|
||||||
@@ -105,16 +108,20 @@ auto PPU::Line::renderObject(PPU::IO::Object& self) -> void {
|
|||||||
tileX &= 511;
|
tileX &= 511;
|
||||||
if(tileX < 256) {
|
if(tileX < 256) {
|
||||||
if(uint color = tiledata[x ^ mirrorX]) {
|
if(uint color = tiledata[x ^ mirrorX]) {
|
||||||
uint source = tile.palette < 192 ? Source::OBJ1 : Source::OBJ2;
|
palette[tileX] = tile.palette + color;
|
||||||
uint priority = self.priority[tile.priority];
|
priority[tileX] = self.priority[tile.priority];
|
||||||
color = cgram[tile.palette + color];
|
|
||||||
if(self.aboveEnable && !windowAbove[x]) plotAbove(tileX, source, priority, color);
|
|
||||||
if(self.belowEnable && !windowBelow[x]) plotBelow(tileX, source, priority, color);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tileX++;
|
tileX++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(uint x : range(256)) {
|
||||||
|
if(!priority[x]) continue;
|
||||||
|
uint source = palette[x] < 192 ? Source::OBJ1 : Source::OBJ2;
|
||||||
|
if(self.aboveEnable && !windowAbove[x]) plotAbove(x, source, priority[x], cgram[palette[x]]);
|
||||||
|
if(self.belowEnable && !windowBelow[x]) plotBelow(x, source, priority[x], cgram[palette[x]]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::oamAddressReset() -> void {
|
auto PPU::oamAddressReset() -> void {
|
||||||
|
@@ -13,9 +13,6 @@ PPU ppu;
|
|||||||
#include <sfc/ppu/counter/serialization.cpp>
|
#include <sfc/ppu/counter/serialization.cpp>
|
||||||
|
|
||||||
PPU::PPU() {
|
PPU::PPU() {
|
||||||
ppu1.version = 1;
|
|
||||||
ppu2.version = 3;
|
|
||||||
|
|
||||||
output = new uint32[512 * 512];
|
output = new uint32[512 * 512];
|
||||||
output += 16 * 512; //overscan offset
|
output += 16 * 512; //overscan offset
|
||||||
|
|
||||||
@@ -25,18 +22,12 @@ PPU::PPU() {
|
|||||||
|
|
||||||
for(uint y : range(240)) {
|
for(uint y : range(240)) {
|
||||||
lines[y].y = y;
|
lines[y].y = y;
|
||||||
lines[y].outputLo = output + (y * 2 + 0) * 512;
|
|
||||||
lines[y].outputHi = output + (y * 2 + 1) * 512;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PPU::~PPU() {
|
PPU::~PPU() {
|
||||||
output -= 16 * 512; //overscan offset
|
output -= 16 * 512; //overscan offset
|
||||||
delete[] output;
|
delete[] output;
|
||||||
|
|
||||||
delete[] tilecache[TileMode::BPP2];
|
|
||||||
delete[] tilecache[TileMode::BPP4];
|
|
||||||
delete[] tilecache[TileMode::BPP8];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::Enter() -> void {
|
auto PPU::Enter() -> void {
|
||||||
@@ -54,24 +45,25 @@ auto PPU::main() -> void {
|
|||||||
uint y = vcounter();
|
uint y = vcounter();
|
||||||
step(512);
|
step(512);
|
||||||
if(y >= 1 && y <= vdisp()) {
|
if(y >= 1 && y <= vdisp()) {
|
||||||
memory::copy(&lines[y].cgram, &cgram, sizeof(cgram));
|
memcpy(&lines[y].io, &io, sizeof(io));
|
||||||
memory::copy(&lines[y].io, &io, sizeof(io));
|
memcpy(&lines[y].cgram, &cgram, sizeof(cgram));
|
||||||
//lines[y].render();
|
if(!Line::count) Line::start = y;
|
||||||
|
Line::count++;
|
||||||
}
|
}
|
||||||
step(lineclocks() - hcounter());
|
step(lineclocks() - hcounter());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::scanline() -> void {
|
auto PPU::scanline() -> void {
|
||||||
if(vcounter() == 0) {
|
if(vcounter() == 0) {
|
||||||
frame.interlace = io.interlace;
|
latch.interlace = io.interlace;
|
||||||
frame.overscan = io.overscan;
|
latch.overscan = io.overscan;
|
||||||
frame.hires = false;
|
latch.hires = false;
|
||||||
io.obj.timeOver = false;
|
io.obj.timeOver = false;
|
||||||
io.obj.rangeOver = false;
|
io.obj.rangeOver = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(vcounter() > 0 && vcounter() < vdisp()) {
|
if(vcounter() > 0 && vcounter() < vdisp()) {
|
||||||
frame.hires |= io.pseudoHires || io.bgMode == 5 || io.bgMode == 6;
|
latch.hires |= io.pseudoHires || io.bgMode == 5 || io.bgMode == 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(vcounter() == vdisp() && !io.displayDisable) {
|
if(vcounter() == vdisp() && !io.displayDisable) {
|
||||||
@@ -79,11 +71,7 @@ auto PPU::scanline() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(vcounter() == 240) {
|
if(vcounter() == 240) {
|
||||||
const uint limit = vdisp();
|
Line::flush();
|
||||||
#pragma omp parallel for
|
|
||||||
for(uint y = 1; y < limit; y++) {
|
|
||||||
lines[y].render();
|
|
||||||
}
|
|
||||||
scheduler.exit(Scheduler::Event::Frame);
|
scheduler.exit(Scheduler::Event::Frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,7 +82,9 @@ auto PPU::refresh() -> void {
|
|||||||
auto pitch = 512 << !interlace();
|
auto pitch = 512 << !interlace();
|
||||||
auto width = 256 << hires();
|
auto width = 256 << hires();
|
||||||
auto height = 240 << interlace();
|
auto height = 240 << interlace();
|
||||||
|
if(!hires()) Emulator::video.setEffect(Emulator::Video::Effect::ColorBleed, false);
|
||||||
Emulator::video.refresh(output, pitch * sizeof(uint32), width, height);
|
Emulator::video.refresh(output, pitch * sizeof(uint32), width, height);
|
||||||
|
if(!hires()) Emulator::video.setEffect(Emulator::Video::Effect::ColorBleed, settings.blurEmulation);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::load(Markup::Node node) -> bool {
|
auto PPU::load(Markup::Node node) -> bool {
|
||||||
@@ -109,6 +99,21 @@ auto PPU::power(bool reset) -> void {
|
|||||||
function<auto (uint24, uint8) -> uint8> reader{&PPU::readIO, this};
|
function<auto (uint24, uint8) -> uint8> reader{&PPU::readIO, this};
|
||||||
function<auto (uint24, uint8) -> void> writer{&PPU::writeIO, this};
|
function<auto (uint24, uint8) -> void> writer{&PPU::writeIO, this};
|
||||||
bus.map(reader, writer, "00-3f,80-bf:2100-213f");
|
bus.map(reader, writer, "00-3f,80-bf:2100-213f");
|
||||||
|
|
||||||
|
if(!reset) {
|
||||||
|
for(auto address : range(32768)) {
|
||||||
|
vram[address] = 0x0000;
|
||||||
|
updateTiledata(address);
|
||||||
|
}
|
||||||
|
for(auto& color : cgram) color = 0x0000;
|
||||||
|
for(auto& object : objects) object = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
latch = {};
|
||||||
|
io = {};
|
||||||
|
|
||||||
|
Line::start = 0;
|
||||||
|
Line::count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
//limitations:
|
//limitations:
|
||||||
//* mid-scanline effects not support
|
//* mid-scanline effects not support
|
||||||
//* mid-frame OAM changes not supported
|
//* vertical mosaic coordinates are not exact
|
||||||
//* range-time over flags not reported in real-time
|
//* (hardware-mod) 128KB VRAM mode not supported
|
||||||
|
|
||||||
struct PPU : Thread, PPUcounter {
|
struct PPU : Thread, PPUcounter {
|
||||||
alwaysinline auto interlace() const -> bool { return frame.interlace; }
|
alwaysinline auto interlace() const -> bool { return latch.interlace; }
|
||||||
alwaysinline auto overscan() const -> bool { return frame.overscan; }
|
alwaysinline auto overscan() const -> bool { return latch.overscan; }
|
||||||
alwaysinline auto hires() const -> bool { return frame.hires; }
|
alwaysinline auto hires() const -> bool { return latch.hires; }
|
||||||
alwaysinline auto vdisp() const -> uint { return !io.overscan ? 225 : 240; }
|
alwaysinline auto vdisp() const -> uint { return !io.overscan ? 225 : 240; }
|
||||||
|
|
||||||
//ppu.cpp
|
//ppu.cpp
|
||||||
@@ -27,49 +27,43 @@ struct PPU : Thread, PPUcounter {
|
|||||||
auto serialize(serializer&) -> void;
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
uint32* output = nullptr;
|
struct Source { enum : uint { BG1, BG2, BG3, BG4, OBJ1, OBJ2, COL }; };
|
||||||
uint8* tilecache[3] = {}; //bitplane -> bitmap tiledata
|
struct TileMode { enum : uint { BPP2, BPP4, BPP8, Mode7, Inactive }; };
|
||||||
uint16 vram[32 * 1024];
|
struct ScreenMode { enum : uint { Above, Below }; };
|
||||||
uint16 cgram[256];
|
|
||||||
|
|
||||||
struct {
|
|
||||||
uint4 version;
|
|
||||||
uint8 mdr;
|
|
||||||
} ppu1, ppu2;
|
|
||||||
|
|
||||||
struct Latch {
|
struct Latch {
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
|
uint1 interlace;
|
||||||
|
uint1 overscan;
|
||||||
|
uint1 hires;
|
||||||
|
|
||||||
uint16 vram;
|
uint16 vram;
|
||||||
uint8 oam;
|
uint8 oam;
|
||||||
uint8 cgram;
|
uint8 cgram;
|
||||||
uint8 bgofsPPU1;
|
|
||||||
uint8 bgofsPPU2;
|
uint10 oamAddress;
|
||||||
|
uint8 cgramAddress;
|
||||||
|
|
||||||
uint8 mode7;
|
uint8 mode7;
|
||||||
uint1 counters;
|
uint1 counters;
|
||||||
uint1 hcounter; //hdot
|
uint1 hcounter; //hdot
|
||||||
uint1 vcounter;
|
uint1 vcounter;
|
||||||
|
|
||||||
uint10 oamAddress;
|
struct PPU {
|
||||||
uint8 cgramAddress;
|
//serialization.cpp
|
||||||
} latch;
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
//io.cpp
|
uint8 mdr;
|
||||||
auto latchCounters() -> void;
|
uint8 bgofs;
|
||||||
alwaysinline auto vramAddress() const -> uint15;
|
} ppu1, ppu2;
|
||||||
alwaysinline auto readVRAM() -> uint16;
|
};
|
||||||
alwaysinline auto writeVRAM(uint1 byte, uint8 data) -> void;
|
|
||||||
alwaysinline auto readOAM(uint10 address) -> uint8;
|
|
||||||
alwaysinline auto writeOAM(uint10 address, uint8 data) -> void;
|
|
||||||
alwaysinline auto readCGRAM(uint1 byte, uint8 address) -> uint8;
|
|
||||||
alwaysinline auto writeCGRAM(uint8 address, uint15 data) -> void;
|
|
||||||
auto readIO(uint24 address, uint8 data) -> uint8;
|
|
||||||
auto writeIO(uint24 address, uint8 data) -> void;
|
|
||||||
auto updateVideoMode() -> void;
|
|
||||||
|
|
||||||
struct Source { enum : uint { BG1, BG2, BG3, BG4, OBJ1, OBJ2, COL }; };
|
|
||||||
struct TileMode { enum : uint { BPP2, BPP4, BPP8, Mode7, Inactive }; };
|
|
||||||
struct ScreenMode { enum : uint { Above, Below }; };
|
|
||||||
|
|
||||||
struct IO {
|
struct IO {
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
uint1 displayDisable;
|
uint1 displayDisable;
|
||||||
uint4 displayBrightness;
|
uint4 displayBrightness;
|
||||||
uint10 oamBaseAddress;
|
uint10 oamBaseAddress;
|
||||||
@@ -91,34 +85,10 @@ public:
|
|||||||
uint1 pseudoHires;
|
uint1 pseudoHires;
|
||||||
uint1 extbg;
|
uint1 extbg;
|
||||||
|
|
||||||
struct WindowLayer {
|
|
||||||
uint1 oneEnable;
|
|
||||||
uint1 oneInvert;
|
|
||||||
uint1 twoEnable;
|
|
||||||
uint1 twoInvert;
|
|
||||||
uint2 mask;
|
|
||||||
uint1 aboveEnable;
|
|
||||||
uint1 belowEnable;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WindowColor {
|
|
||||||
uint1 oneEnable;
|
|
||||||
uint1 oneInvert;
|
|
||||||
uint1 twoEnable;
|
|
||||||
uint1 twoInvert;
|
|
||||||
uint2 mask;
|
|
||||||
uint2 aboveMask;
|
|
||||||
uint2 belowMask;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Window {
|
|
||||||
uint8 oneLeft;
|
|
||||||
uint8 oneRight;
|
|
||||||
uint8 twoLeft;
|
|
||||||
uint8 twoRight;
|
|
||||||
} window;
|
|
||||||
|
|
||||||
struct Mode7 {
|
struct Mode7 {
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
uint1 hflip;
|
uint1 hflip;
|
||||||
uint1 vflip;
|
uint1 vflip;
|
||||||
uint2 repeat;
|
uint2 repeat;
|
||||||
@@ -132,7 +102,46 @@ public:
|
|||||||
uint16 voffset;
|
uint16 voffset;
|
||||||
} mode7;
|
} mode7;
|
||||||
|
|
||||||
|
struct Window {
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
|
uint8 oneLeft;
|
||||||
|
uint8 oneRight;
|
||||||
|
uint8 twoLeft;
|
||||||
|
uint8 twoRight;
|
||||||
|
} window;
|
||||||
|
|
||||||
|
struct WindowLayer {
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
|
uint1 oneEnable;
|
||||||
|
uint1 oneInvert;
|
||||||
|
uint1 twoEnable;
|
||||||
|
uint1 twoInvert;
|
||||||
|
uint2 mask;
|
||||||
|
uint1 aboveEnable;
|
||||||
|
uint1 belowEnable;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WindowColor {
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
|
uint1 oneEnable;
|
||||||
|
uint1 oneInvert;
|
||||||
|
uint1 twoEnable;
|
||||||
|
uint1 twoInvert;
|
||||||
|
uint2 mask;
|
||||||
|
uint2 aboveMask;
|
||||||
|
uint2 belowMask;
|
||||||
|
};
|
||||||
|
|
||||||
struct Background {
|
struct Background {
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
WindowLayer window;
|
WindowLayer window;
|
||||||
uint1 aboveEnable;
|
uint1 aboveEnable;
|
||||||
uint1 belowEnable;
|
uint1 belowEnable;
|
||||||
@@ -148,6 +157,9 @@ public:
|
|||||||
} bg1, bg2, bg3, bg4;
|
} bg1, bg2, bg3, bg4;
|
||||||
|
|
||||||
struct Object {
|
struct Object {
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
WindowLayer window;
|
WindowLayer window;
|
||||||
uint1 aboveEnable;
|
uint1 aboveEnable;
|
||||||
uint1 belowEnable;
|
uint1 belowEnable;
|
||||||
@@ -162,6 +174,9 @@ public:
|
|||||||
} obj;
|
} obj;
|
||||||
|
|
||||||
struct Color {
|
struct Color {
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
WindowColor window;
|
WindowColor window;
|
||||||
uint1 enable[7];
|
uint1 enable[7];
|
||||||
uint1 directColor;
|
uint1 directColor;
|
||||||
@@ -170,21 +185,12 @@ public:
|
|||||||
uint1 mathMode; //0 = add; 1 = sub
|
uint1 mathMode; //0 = add; 1 = sub
|
||||||
uint15 fixedColor;
|
uint15 fixedColor;
|
||||||
} col;
|
} col;
|
||||||
} io;
|
};
|
||||||
|
|
||||||
struct Frame {
|
|
||||||
uint1 interlace;
|
|
||||||
uint1 overscan;
|
|
||||||
uint1 hires;
|
|
||||||
} frame;
|
|
||||||
|
|
||||||
//object.cpp
|
|
||||||
auto oamAddressReset() -> void;
|
|
||||||
auto oamSetFirstObject() -> void;
|
|
||||||
auto readObject(uint10 address) -> uint8;
|
|
||||||
auto writeObject(uint10 address, uint8 data) -> void;
|
|
||||||
|
|
||||||
struct Object {
|
struct Object {
|
||||||
|
//serialization.cpp
|
||||||
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
uint9 x;
|
uint9 x;
|
||||||
uint8 y;
|
uint8 y;
|
||||||
uint8 character;
|
uint8 character;
|
||||||
@@ -194,12 +200,66 @@ public:
|
|||||||
uint2 priority;
|
uint2 priority;
|
||||||
uint3 palette;
|
uint3 palette;
|
||||||
uint1 size;
|
uint1 size;
|
||||||
} objects[128];
|
};
|
||||||
|
|
||||||
|
struct ObjectItem {
|
||||||
|
uint1 valid;
|
||||||
|
uint7 index;
|
||||||
|
uint8 width;
|
||||||
|
uint8 height;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ObjectTile {
|
||||||
|
uint1 valid;
|
||||||
|
uint9 x;
|
||||||
|
uint8 y;
|
||||||
|
uint2 priority;
|
||||||
|
uint8 palette;
|
||||||
|
uint1 hflip;
|
||||||
|
uint11 number;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Pixel {
|
||||||
|
uint source;
|
||||||
|
uint priority;
|
||||||
|
uint color;
|
||||||
|
};
|
||||||
|
|
||||||
|
//io.cpp
|
||||||
|
auto latchCounters() -> void;
|
||||||
|
alwaysinline auto vramAddress() const -> uint15;
|
||||||
|
alwaysinline auto readVRAM() -> uint16;
|
||||||
|
alwaysinline auto writeVRAM(uint1 byte, uint8 data) -> void;
|
||||||
|
alwaysinline auto updateTiledata(uint15 address) -> void;
|
||||||
|
alwaysinline auto readOAM(uint10 address) -> uint8;
|
||||||
|
alwaysinline auto writeOAM(uint10 address, uint8 data) -> void;
|
||||||
|
alwaysinline auto readCGRAM(uint1 byte, uint8 address) -> uint8;
|
||||||
|
alwaysinline auto writeCGRAM(uint8 address, uint15 data) -> void;
|
||||||
|
auto readIO(uint24 address, uint8 data) -> uint8;
|
||||||
|
auto writeIO(uint24 address, uint8 data) -> void;
|
||||||
|
auto updateVideoMode() -> void;
|
||||||
|
|
||||||
|
//object.cpp
|
||||||
|
auto oamAddressReset() -> void;
|
||||||
|
auto oamSetFirstObject() -> void;
|
||||||
|
auto readObject(uint10 address) -> uint8;
|
||||||
|
auto writeObject(uint10 address, uint8 data) -> void;
|
||||||
|
|
||||||
|
//[serialized]
|
||||||
|
Latch latch;
|
||||||
|
IO io;
|
||||||
|
|
||||||
|
uint16 vram[32 * 1024];
|
||||||
|
uint15 cgram[256];
|
||||||
|
Object objects[128];
|
||||||
|
|
||||||
|
//[unserialized]
|
||||||
|
uint32* output = nullptr;
|
||||||
|
uint8* tilecache[3] = {}; //bitplane -> bitmap tiledata
|
||||||
|
|
||||||
struct Line {
|
struct Line {
|
||||||
struct Pixel;
|
|
||||||
|
|
||||||
//line.cpp
|
//line.cpp
|
||||||
|
static auto flush() -> void;
|
||||||
auto render() -> void;
|
auto render() -> void;
|
||||||
auto pixel(uint x, Pixel above, Pixel below) const -> uint15;
|
auto pixel(uint x, Pixel above, Pixel below) const -> uint15;
|
||||||
auto blend(uint x, uint y, bool halve) const -> uint15;
|
auto blend(uint x, uint y, bool halve) const -> uint15;
|
||||||
@@ -221,38 +281,24 @@ public:
|
|||||||
auto renderWindow(PPU::IO::WindowLayer&, bool, bool*) -> void;
|
auto renderWindow(PPU::IO::WindowLayer&, bool, bool*) -> void;
|
||||||
auto renderWindow(PPU::IO::WindowColor&, uint, bool*) -> void;
|
auto renderWindow(PPU::IO::WindowColor&, uint, bool*) -> void;
|
||||||
|
|
||||||
uint9 y;
|
//[unserialized]
|
||||||
uint32* outputLo = nullptr;
|
uint9 y; //constant
|
||||||
uint32* outputHi = nullptr;
|
|
||||||
|
|
||||||
uint15 cgram[256];
|
|
||||||
IO io;
|
IO io;
|
||||||
|
uint15 cgram[256];
|
||||||
|
|
||||||
struct ObjectItem {
|
ObjectItem items[32];
|
||||||
uint1 valid;
|
ObjectTile tiles[34];
|
||||||
uint7 index;
|
|
||||||
uint8 width;
|
|
||||||
uint8 height;
|
|
||||||
} items[32];
|
|
||||||
|
|
||||||
struct ObjectTile {
|
Pixel above[256];
|
||||||
uint1 valid;
|
Pixel below[256];
|
||||||
uint9 x;
|
|
||||||
uint8 y;
|
|
||||||
uint2 priority;
|
|
||||||
uint8 palette;
|
|
||||||
uint1 hflip;
|
|
||||||
uint11 number;
|
|
||||||
} tiles[34];
|
|
||||||
|
|
||||||
struct Pixel {
|
|
||||||
uint source;
|
|
||||||
uint priority;
|
|
||||||
uint color;
|
|
||||||
} above[256], below[256];
|
|
||||||
|
|
||||||
bool windowAbove[256];
|
bool windowAbove[256];
|
||||||
bool windowBelow[256];
|
bool windowBelow[256];
|
||||||
|
|
||||||
|
//flush()
|
||||||
|
static uint start;
|
||||||
|
static uint count;
|
||||||
} lines[240];
|
} lines[240];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,4 +1,160 @@
|
|||||||
auto PPU::serialize(serializer& s) -> void {
|
auto PPU::serialize(serializer& s) -> void {
|
||||||
Thread::serialize(s);
|
Thread::serialize(s);
|
||||||
PPUcounter::serialize(s);
|
PPUcounter::serialize(s);
|
||||||
|
|
||||||
|
latch.serialize(s);
|
||||||
|
io.serialize(s);
|
||||||
|
s.array(vram);
|
||||||
|
s.array(cgram);
|
||||||
|
for(auto& object : objects) object.serialize(s);
|
||||||
|
|
||||||
|
for(auto address : range(32768)) updateTiledata(address);
|
||||||
|
Line::start = 0;
|
||||||
|
Line::count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PPU::Latch::serialize(serializer& s) -> void {
|
||||||
|
s.integer(interlace);
|
||||||
|
s.integer(overscan);
|
||||||
|
s.integer(hires);
|
||||||
|
s.integer(vram);
|
||||||
|
s.integer(oam);
|
||||||
|
s.integer(cgram);
|
||||||
|
s.integer(oamAddress);
|
||||||
|
s.integer(cgramAddress);
|
||||||
|
s.integer(mode7);
|
||||||
|
s.integer(counters);
|
||||||
|
s.integer(hcounter);
|
||||||
|
s.integer(vcounter);
|
||||||
|
ppu1.serialize(s);
|
||||||
|
ppu2.serialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PPU::Latch::PPU::serialize(serializer& s) -> void {
|
||||||
|
s.integer(mdr);
|
||||||
|
s.integer(bgofs);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PPU::IO::serialize(serializer& s) -> void {
|
||||||
|
s.integer(displayDisable);
|
||||||
|
s.integer(displayBrightness);
|
||||||
|
s.integer(oamBaseAddress);
|
||||||
|
s.integer(oamAddress);
|
||||||
|
s.integer(oamPriority);
|
||||||
|
s.integer(bgPriority);
|
||||||
|
s.integer(bgMode);
|
||||||
|
s.integer(mosaicSize);
|
||||||
|
s.integer(vramIncrementMode);
|
||||||
|
s.integer(vramMapping);
|
||||||
|
s.integer(vramIncrementSize);
|
||||||
|
s.integer(vramAddress);
|
||||||
|
s.integer(cgramAddress);
|
||||||
|
s.integer(cgramAddressLatch);
|
||||||
|
s.integer(hcounter);
|
||||||
|
s.integer(vcounter);
|
||||||
|
s.integer(interlace);
|
||||||
|
s.integer(overscan);
|
||||||
|
s.integer(pseudoHires);
|
||||||
|
s.integer(extbg);
|
||||||
|
|
||||||
|
mode7.serialize(s);
|
||||||
|
window.serialize(s);
|
||||||
|
bg1.serialize(s);
|
||||||
|
bg2.serialize(s);
|
||||||
|
bg3.serialize(s);
|
||||||
|
bg4.serialize(s);
|
||||||
|
obj.serialize(s);
|
||||||
|
col.serialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PPU::IO::Mode7::serialize(serializer& s) -> void {
|
||||||
|
s.integer(hflip);
|
||||||
|
s.integer(vflip);
|
||||||
|
s.integer(repeat);
|
||||||
|
s.integer(a);
|
||||||
|
s.integer(b);
|
||||||
|
s.integer(c);
|
||||||
|
s.integer(d);
|
||||||
|
s.integer(x);
|
||||||
|
s.integer(y);
|
||||||
|
s.integer(hoffset);
|
||||||
|
s.integer(voffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PPU::IO::Window::serialize(serializer& s) -> void {
|
||||||
|
s.integer(oneLeft);
|
||||||
|
s.integer(oneRight);
|
||||||
|
s.integer(twoLeft);
|
||||||
|
s.integer(twoRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PPU::IO::WindowLayer::serialize(serializer& s) -> void {
|
||||||
|
s.integer(oneEnable);
|
||||||
|
s.integer(oneInvert);
|
||||||
|
s.integer(twoEnable);
|
||||||
|
s.integer(twoInvert);
|
||||||
|
s.integer(mask);
|
||||||
|
s.integer(aboveEnable);
|
||||||
|
s.integer(belowEnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PPU::IO::WindowColor::serialize(serializer& s) -> void {
|
||||||
|
s.integer(oneEnable);
|
||||||
|
s.integer(oneInvert);
|
||||||
|
s.integer(twoEnable);
|
||||||
|
s.integer(twoInvert);
|
||||||
|
s.integer(mask);
|
||||||
|
s.integer(aboveMask);
|
||||||
|
s.integer(belowMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PPU::IO::Background::serialize(serializer& s) -> void {
|
||||||
|
window.serialize(s);
|
||||||
|
s.integer(aboveEnable);
|
||||||
|
s.integer(belowEnable);
|
||||||
|
s.integer(mosaicEnable);
|
||||||
|
s.integer(tiledataAddress);
|
||||||
|
s.integer(screenAddress);
|
||||||
|
s.integer(screenSize);
|
||||||
|
s.integer(tileSize);
|
||||||
|
s.integer(hoffset);
|
||||||
|
s.integer(voffset);
|
||||||
|
s.integer(tileMode);
|
||||||
|
s.array(priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PPU::IO::Object::serialize(serializer& s) -> void {
|
||||||
|
window.serialize(s);
|
||||||
|
s.integer(aboveEnable);
|
||||||
|
s.integer(belowEnable);
|
||||||
|
s.integer(interlace);
|
||||||
|
s.integer(baseSize);
|
||||||
|
s.integer(nameselect);
|
||||||
|
s.integer(tiledataAddress);
|
||||||
|
s.integer(first);
|
||||||
|
s.integer(rangeOver);
|
||||||
|
s.integer(timeOver);
|
||||||
|
s.array(priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PPU::IO::Color::serialize(serializer& s) -> void {
|
||||||
|
window.serialize(s);
|
||||||
|
s.array(enable);
|
||||||
|
s.integer(directColor);
|
||||||
|
s.integer(blendMode);
|
||||||
|
s.integer(halve);
|
||||||
|
s.integer(mathMode);
|
||||||
|
s.integer(fixedColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto PPU::Object::serialize(serializer& s) -> void {
|
||||||
|
s.integer(x);
|
||||||
|
s.integer(y);
|
||||||
|
s.integer(character);
|
||||||
|
s.integer(nameselect);
|
||||||
|
s.integer(vflip);
|
||||||
|
s.integer(hflip);
|
||||||
|
s.integer(priority);
|
||||||
|
s.integer(palette);
|
||||||
|
s.integer(size);
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
database
|
database
|
||||||
revision: 2018-05-17
|
revision: 2018-06-01
|
||||||
|
|
||||||
//Boards (Production)
|
//Boards (Production)
|
||||||
|
|
||||||
@@ -565,7 +565,7 @@ board: SHVC-YJ0N-01
|
|||||||
//Boards (Generic)
|
//Boards (Generic)
|
||||||
|
|
||||||
database
|
database
|
||||||
revision: 2018-05-16
|
revision: 2018-06-01
|
||||||
|
|
||||||
board: ARM-LOROM-RAM
|
board: ARM-LOROM-RAM
|
||||||
memory type=ROM content=Program
|
memory type=ROM content=Program
|
||||||
@@ -885,7 +885,7 @@ board: SPC7110-RAM-EPSONRTC
|
|||||||
memory type=RAM content=Save
|
memory type=RAM content=Save
|
||||||
map address=00-3f,80-bf:6000-7fff mask=0xe000
|
map address=00-3f,80-bf:6000-7fff mask=0xe000
|
||||||
rtc manufacturer=Epson
|
rtc manufacturer=Epson
|
||||||
map address=00-3f,80-bf:4800-4842
|
map address=00-3f,80-bf:4840-4842
|
||||||
memory type=RTC content=Time manufacturer=Epson
|
memory type=RTC content=Time manufacturer=Epson
|
||||||
|
|
||||||
board: ST-LOROM
|
board: ST-LOROM
|
||||||
|
@@ -211,8 +211,8 @@ auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint heig
|
|||||||
|
|
||||||
pitch >>= 2;
|
pitch >>= 2;
|
||||||
if(presentation->overscanCropping.checked()) {
|
if(presentation->overscanCropping.checked()) {
|
||||||
data += 16 * pitch;
|
if(height == 240) data += 8 * pitch, height -= 16;
|
||||||
height -= 32;
|
if(height == 480) data += 16 * pitch, height -= 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(video->lock(output, length, width, height)) {
|
if(video->lock(output, length, width, height)) {
|
||||||
|
Reference in New Issue
Block a user