From c67fb2c726bb3b846c1be51ef7c91edf2719c9a6 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sat, 2 Jun 2018 12:47:37 +1000 Subject: [PATCH] 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. --- higan/emulator/emulator.hpp | 2 +- higan/sfc/ppu-fast/background.cpp | 12 +- higan/sfc/ppu-fast/io.cpp | 105 +++++---- higan/sfc/ppu-fast/line.cpp | 32 ++- higan/sfc/ppu-fast/mode7.cpp | 4 +- higan/sfc/ppu-fast/object.cpp | 21 +- higan/sfc/ppu-fast/ppu.cpp | 47 ++-- higan/sfc/ppu-fast/ppu.hpp | 252 ++++++++++++--------- higan/sfc/ppu-fast/serialization.cpp | 156 +++++++++++++ higan/systems/Super Famicom.sys/boards.bml | 6 +- higan/target-bsnes/program/interface.cpp | 4 +- 11 files changed, 438 insertions(+), 203 deletions(-) diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index f0388436..fa440e01 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { 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 License = "GPLv3"; static const string Website = "https://byuu.org/"; diff --git a/higan/sfc/ppu-fast/background.cpp b/higan/sfc/ppu-fast/background.cpp index 46bd08c0..3ecab3fd 100644 --- a/higan/sfc/ppu-fast/background.cpp +++ b/higan/sfc/ppu-fast/background.cpp @@ -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 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) { hscroll <<= 1; 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 mirrorY = tileNumber & 0x8000 ? 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 paletteIndex = paletteBase + (paletteNumber << paletteShift) & 0xff; - if(tileWidth == 4 && (hoffset & 8) - 1 != mirrorX) tileNumber += 1; - if(tileHeight == 4 && (voffset & 8) - 1 != mirrorY) tileNumber += 16; + if(tileWidth == 4 && (bool(hoffset & 8) ^ bool(mirrorX))) tileNumber += 1; + if(tileHeight == 4 && (bool(voffset & 8) ^ bool(mirrorY))) tileNumber += 16; tileNumber = (tileNumber & 0x03ff) + tiledataIndex & tileMask; 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++) { if(x & width) continue; //x < 0 || x >= width - if(--mosaicCounter == 0) { + if(!self.mosaicEnable || --mosaicCounter == 0) { mosaicCounter = 1 + io.mosaicSize; mosaicPalette = tiledata[tileX ^ mirrorX]; mosaicPriority = tilePriority; diff --git a/higan/sfc/ppu-fast/io.cpp b/higan/sfc/ppu-fast/io.cpp index 52b215b6..d19ec204 100644 --- a/higan/sfc/ppu-fast/io.cpp +++ b/higan/sfc/ppu-fast/io.cpp @@ -23,9 +23,13 @@ auto PPU::readVRAM() -> uint16 { auto PPU::writeVRAM(uint1 byte, uint8 data) -> void { if(!io.displayDisable && cpu.vcounter() < vdisp()) return; + Line::flush(); auto address = vramAddress(); vram[address].byte(byte) = data; + updateTiledata(address); +} +auto PPU::updateTiledata(uint15 address) -> void { auto word = vram[address]; 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); @@ -48,6 +52,7 @@ auto PPU::readOAM(uint10 address) -> uint8 { } auto PPU::writeOAM(uint10 address, uint8 data) -> void { + Line::flush(); if(!io.displayDisable && cpu.vcounter() < vdisp()) address = latch.oamAddress; 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 0x2124: case 0x2125: case 0x2126: case 0x2128: case 0x2129: case 0x212a: { - return ppu1.mdr; + return latch.ppu1.mdr; } case 0x2134: { //MPYL 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 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 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 @@ -102,77 +107,77 @@ auto PPU::readIO(uint24 address, uint8 data) -> uint8 { } case 0x2138: { //OAMDATAREAD - ppu1.mdr = readOAM(io.oamAddress++); + data = readOAM(io.oamAddress++); oamSetFirstObject(); - return ppu1.mdr; + return latch.ppu1.mdr = data; } case 0x2139: { //VMDATALREAD - ppu1.mdr = latch.vram.byte(0); + data = latch.vram.byte(0); if(io.vramIncrementMode == 0) { latch.vram = readVRAM(); io.vramAddress += io.vramIncrementSize; } - return ppu1.mdr; + return latch.ppu1.mdr = data; } case 0x213a: { //VMDATAHREAD - ppu1.mdr = latch.vram.byte(1); + data = latch.vram.byte(1); if(io.vramIncrementMode == 1) { latch.vram = readVRAM(); io.vramAddress += io.vramIncrementSize; } - return ppu1.mdr; + return latch.ppu1.mdr = data; } case 0x213b: { //CGDATAREAD if(io.cgramAddressLatch++ == 0) { - ppu2.mdr.bits(0,7) = readCGRAM(0, io.cgramAddress); + latch.ppu2.mdr.bits(0,7) = readCGRAM(0, io.cgramAddress); } 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 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 { - 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 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 { - 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 - ppu1.mdr.bits(0,3) = ppu1.version; - ppu1.mdr.bit(5) = 0; - ppu1.mdr.bit(6) = io.obj.rangeOver; - ppu1.mdr.bit(7) = io.obj.timeOver; - return ppu1.mdr; + latch.ppu1.mdr.bits(0,3) = 1; //PPU1 version + latch.ppu1.mdr.bit(5) = 0; + latch.ppu1.mdr.bit(6) = io.obj.rangeOver; + latch.ppu1.mdr.bit(7) = io.obj.timeOver; + return latch.ppu1.mdr; } case 0x213f: { //STAT78 latch.hcounter = 0; latch.vcounter = 0; - ppu2.mdr.bits(0,3) = ppu2.version; - ppu2.mdr.bit(4) = Region::PAL(); //0 = NTSC, 1 = PAL + latch.ppu2.mdr.bits(0,3) = 3; //PPU2 version + latch.ppu2.mdr.bit(4) = Region::PAL(); //0 = NTSC, 1 = PAL if(!cpu.pio().bit(7)) { - ppu2.mdr.bit(6) = 1; + latch.ppu2.mdr.bit(6) = 1; } else { - ppu2.mdr.bit(6) = latch.counters; + latch.ppu2.mdr.bit(6) = latch.counters; latch.counters = 0; } - ppu2.mdr.bit(7) = field(); - return ppu2.mdr; + latch.ppu2.mdr.bit(7) = field(); + return latch.ppu2.mdr; } } @@ -286,9 +291,9 @@ auto PPU::writeIO(uint24 address, uint8 data) -> void { io.mode7.hoffset = data << 8 | latch.mode7; latch.mode7 = data; - io.bg1.hoffset = data << 8 | (latch.bgofsPPU1 & ~7) | (latch.bgofsPPU2 & 7); - latch.bgofsPPU1 = data; - latch.bgofsPPU2 = data; + io.bg1.hoffset = data << 8 | (latch.ppu1.bgofs & ~7) | (latch.ppu2.bgofs & 7); + latch.ppu1.bgofs = data; + latch.ppu2.bgofs = data; return; } @@ -296,47 +301,47 @@ auto PPU::writeIO(uint24 address, uint8 data) -> void { io.mode7.voffset = data << 8 | latch.mode7; latch.mode7 = data; - io.bg1.voffset = data << 8 | latch.bgofsPPU1; - latch.bgofsPPU1 = data; + io.bg1.voffset = data << 8 | latch.ppu1.bgofs; + latch.ppu1.bgofs = data; return; } case 0x210f: { //BG2HOFS - io.bg2.hoffset = data << 8 | (latch.bgofsPPU1 & ~7) | (latch.bgofsPPU2 & 7); - latch.bgofsPPU1 = data; - latch.bgofsPPU2 = data; + io.bg2.hoffset = data << 8 | (latch.ppu1.bgofs & ~7) | (latch.ppu2.bgofs & 7); + latch.ppu1.bgofs = data; + latch.ppu2.bgofs = data; return; } case 0x2110: { //BG2VOFS - io.bg2.voffset = data << 8 | latch.bgofsPPU1; - latch.bgofsPPU1 = data; + io.bg2.voffset = data << 8 | latch.ppu1.bgofs; + latch.ppu1.bgofs = data; return; } case 0x2111: { //BG3HOFS - io.bg3.hoffset = data << 8 | (latch.bgofsPPU1 & ~7) | (latch.bgofsPPU2 & 7); - latch.bgofsPPU1 = data; - latch.bgofsPPU2 = data; + io.bg3.hoffset = data << 8 | (latch.ppu1.bgofs & ~7) | (latch.ppu2.bgofs & 7); + latch.ppu1.bgofs = data; + latch.ppu2.bgofs = data; return; } case 0x2112: { //BG3VOFS - io.bg3.voffset = data << 8 | latch.bgofsPPU1; - latch.bgofsPPU1 = data; + io.bg3.voffset = data << 8 | latch.ppu1.bgofs; + latch.ppu1.bgofs = data; return; } case 0x2113: { //BG4HOFS - io.bg4.hoffset = data << 8 | (latch.bgofsPPU1 & ~7) | (latch.bgofsPPU2 & 7); - latch.bgofsPPU1 = data; - latch.bgofsPPU2 = data; + io.bg4.hoffset = data << 8 | (latch.ppu1.bgofs & ~7) | (latch.ppu2.bgofs & 7); + latch.ppu1.bgofs = data; + latch.ppu2.bgofs = data; return; } case 0x2114: { //BG4VOFS - io.bg4.voffset = data << 8 | latch.bgofsPPU1; - latch.bgofsPPU1 = data; + io.bg4.voffset = data << 8 | latch.ppu1.bgofs; + latch.ppu1.bgofs = data; return; } diff --git a/higan/sfc/ppu-fast/line.cpp b/higan/sfc/ppu-fast/line.cpp index 33a6af81..d92d3489 100644 --- a/higan/sfc/ppu-fast/line.cpp +++ b/higan/sfc/ppu-fast/line.cpp @@ -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 { bool hires = io.pseudoHires || io.bgMode == 5 || io.bgMode == 6; @@ -16,7 +30,8 @@ auto PPU::Line::render() -> void { renderBackground(io.bg4, Source::BG4); 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 luma = io.displayBrightness << 15; @@ -29,13 +44,14 @@ auto PPU::Line::render() -> void { renderWindow(io.col.window, io.col.window.belowMask, windowBelow); 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)) { - output[x << 1 | 0] = - output[x << 1 | 1] = luma | pixel(x, above[x], below[x]); + auto color = luma | pixel(x, above[x], below[x]); + *output++ = color; + *output++ = color; } else for(uint x : range(256)) { - output[x << 1 | 0] = luma | pixel(x, below[x], above[x]); - output[x << 1 | 1] = luma | pixel(x, above[x], below[x]); + *output++ = luma | pixel(x, below[x], above[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 { - 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 { - if(priority >= below[x].priority) below[x] = {source, priority, color}; + if(priority > below[x].priority) below[x] = {source, priority, color}; } diff --git a/higan/sfc/ppu-fast/mode7.cpp b/higan/sfc/ppu-fast/mode7.cpp index 06c7c6bd..6543818f 100644 --- a/higan/sfc/ppu-fast/mode7.cpp +++ b/higan/sfc/ppu-fast/mode7.cpp @@ -1,5 +1,5 @@ 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 a = (int16)io.mode7.a; @@ -47,7 +47,7 @@ auto PPU::Line::renderMode7(PPU::IO::Background& self, uint source) -> void { palette &= 0x7f; } - if(--mosaicCounter == 0) { + if(!self.mosaicEnable || --mosaicCounter == 0) { mosaicCounter = 1 + io.mosaicSize; mosaicPalette = palette; mosaicPriority = priority; diff --git a/higan/sfc/ppu-fast/object.cpp b/higan/sfc/ppu-fast/object.cpp index a27547e7..87ee5d04 100644 --- a/higan/sfc/ppu-fast/object.cpp +++ b/higan/sfc/ppu-fast/object.cpp @@ -9,8 +9,8 @@ auto PPU::Line::renderObject(PPU::IO::Object& self) -> void { uint itemCount = 0; uint tileCount = 0; - for(auto n : range(32)) items[n].valid = false; - for(auto n : range(34)) tiles[n].valid = false; + for(auto& item : items) item.valid = false; + for(auto& tile : tiles) tile.valid = false; for(auto n : range(128)) { 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.timeOver |= tileCount > 34; + uint8 palette[256]; + uint8 priority[256]; + for(uint n : range(34)) { const auto& tile = tiles[n]; if(!tile.valid) continue; @@ -105,16 +108,20 @@ auto PPU::Line::renderObject(PPU::IO::Object& self) -> void { tileX &= 511; if(tileX < 256) { if(uint color = tiledata[x ^ mirrorX]) { - uint source = tile.palette < 192 ? Source::OBJ1 : Source::OBJ2; - uint priority = 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); + palette[tileX] = tile.palette + color; + priority[tileX] = self.priority[tile.priority]; } } 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 { diff --git a/higan/sfc/ppu-fast/ppu.cpp b/higan/sfc/ppu-fast/ppu.cpp index 84b4e29c..209c9ef9 100644 --- a/higan/sfc/ppu-fast/ppu.cpp +++ b/higan/sfc/ppu-fast/ppu.cpp @@ -13,9 +13,6 @@ PPU ppu; #include PPU::PPU() { - ppu1.version = 1; - ppu2.version = 3; - output = new uint32[512 * 512]; output += 16 * 512; //overscan offset @@ -25,18 +22,12 @@ PPU::PPU() { for(uint y : range(240)) { lines[y].y = y; - lines[y].outputLo = output + (y * 2 + 0) * 512; - lines[y].outputHi = output + (y * 2 + 1) * 512; } } PPU::~PPU() { output -= 16 * 512; //overscan offset delete[] output; - - delete[] tilecache[TileMode::BPP2]; - delete[] tilecache[TileMode::BPP4]; - delete[] tilecache[TileMode::BPP8]; } auto PPU::Enter() -> void { @@ -54,24 +45,25 @@ auto PPU::main() -> void { uint y = vcounter(); step(512); if(y >= 1 && y <= vdisp()) { - memory::copy(&lines[y].cgram, &cgram, sizeof(cgram)); - memory::copy(&lines[y].io, &io, sizeof(io)); - //lines[y].render(); + memcpy(&lines[y].io, &io, sizeof(io)); + memcpy(&lines[y].cgram, &cgram, sizeof(cgram)); + if(!Line::count) Line::start = y; + Line::count++; } step(lineclocks() - hcounter()); } auto PPU::scanline() -> void { if(vcounter() == 0) { - frame.interlace = io.interlace; - frame.overscan = io.overscan; - frame.hires = false; + latch.interlace = io.interlace; + latch.overscan = io.overscan; + latch.hires = false; io.obj.timeOver = false; io.obj.rangeOver = false; } 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) { @@ -79,11 +71,7 @@ auto PPU::scanline() -> void { } if(vcounter() == 240) { - const uint limit = vdisp(); - #pragma omp parallel for - for(uint y = 1; y < limit; y++) { - lines[y].render(); - } + Line::flush(); scheduler.exit(Scheduler::Event::Frame); } } @@ -94,7 +82,9 @@ auto PPU::refresh() -> void { auto pitch = 512 << !interlace(); auto width = 256 << hires(); auto height = 240 << interlace(); + if(!hires()) Emulator::video.setEffect(Emulator::Video::Effect::ColorBleed, false); 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 { @@ -109,6 +99,21 @@ auto PPU::power(bool reset) -> void { function uint8> reader{&PPU::readIO, this}; function void> writer{&PPU::writeIO, this}; 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; } } diff --git a/higan/sfc/ppu-fast/ppu.hpp b/higan/sfc/ppu-fast/ppu.hpp index bef8fa2c..ea21277d 100644 --- a/higan/sfc/ppu-fast/ppu.hpp +++ b/higan/sfc/ppu-fast/ppu.hpp @@ -2,13 +2,13 @@ //limitations: //* mid-scanline effects not support -//* mid-frame OAM changes not supported -//* range-time over flags not reported in real-time +//* vertical mosaic coordinates are not exact +//* (hardware-mod) 128KB VRAM mode not supported struct PPU : Thread, PPUcounter { - alwaysinline auto interlace() const -> bool { return frame.interlace; } - alwaysinline auto overscan() const -> bool { return frame.overscan; } - alwaysinline auto hires() const -> bool { return frame.hires; } + alwaysinline auto interlace() const -> bool { return latch.interlace; } + alwaysinline auto overscan() const -> bool { return latch.overscan; } + alwaysinline auto hires() const -> bool { return latch.hires; } alwaysinline auto vdisp() const -> uint { return !io.overscan ? 225 : 240; } //ppu.cpp @@ -27,49 +27,43 @@ struct PPU : Thread, PPUcounter { auto serialize(serializer&) -> void; public: - uint32* output = nullptr; - uint8* tilecache[3] = {}; //bitplane -> bitmap tiledata - uint16 vram[32 * 1024]; - uint16 cgram[256]; - - struct { - uint4 version; - uint8 mdr; - } ppu1, ppu2; + 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 Latch { + //serialization.cpp + auto serialize(serializer&) -> void; + + uint1 interlace; + uint1 overscan; + uint1 hires; + uint16 vram; uint8 oam; uint8 cgram; - uint8 bgofsPPU1; - uint8 bgofsPPU2; + + uint10 oamAddress; + uint8 cgramAddress; + uint8 mode7; uint1 counters; uint1 hcounter; //hdot uint1 vcounter; - uint10 oamAddress; - uint8 cgramAddress; - } latch; + struct PPU { + //serialization.cpp + auto serialize(serializer&) -> void; - //io.cpp - auto latchCounters() -> void; - alwaysinline auto vramAddress() const -> uint15; - 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 }; }; + uint8 mdr; + uint8 bgofs; + } ppu1, ppu2; + }; struct IO { + //serialization.cpp + auto serialize(serializer&) -> void; + uint1 displayDisable; uint4 displayBrightness; uint10 oamBaseAddress; @@ -91,34 +85,10 @@ public: uint1 pseudoHires; 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 { + //serialization.cpp + auto serialize(serializer&) -> void; + uint1 hflip; uint1 vflip; uint2 repeat; @@ -132,7 +102,46 @@ public: uint16 voffset; } 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 { + //serialization.cpp + auto serialize(serializer&) -> void; + WindowLayer window; uint1 aboveEnable; uint1 belowEnable; @@ -148,6 +157,9 @@ public: } bg1, bg2, bg3, bg4; struct Object { + //serialization.cpp + auto serialize(serializer&) -> void; + WindowLayer window; uint1 aboveEnable; uint1 belowEnable; @@ -162,6 +174,9 @@ public: } obj; struct Color { + //serialization.cpp + auto serialize(serializer&) -> void; + WindowColor window; uint1 enable[7]; uint1 directColor; @@ -170,21 +185,12 @@ public: uint1 mathMode; //0 = add; 1 = sub uint15 fixedColor; } 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 { + //serialization.cpp + auto serialize(serializer&) -> void; + uint9 x; uint8 y; uint8 character; @@ -194,12 +200,66 @@ public: uint2 priority; uint3 palette; 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 Pixel; - //line.cpp + static auto flush() -> void; auto render() -> void; auto pixel(uint x, Pixel above, Pixel below) 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::WindowColor&, uint, bool*) -> void; - uint9 y; - uint32* outputLo = nullptr; - uint32* outputHi = nullptr; + //[unserialized] + uint9 y; //constant - uint15 cgram[256]; IO io; + uint15 cgram[256]; - struct ObjectItem { - uint1 valid; - uint7 index; - uint8 width; - uint8 height; - } items[32]; + ObjectItem items[32]; + ObjectTile tiles[34]; - struct ObjectTile { - uint1 valid; - 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]; + Pixel above[256]; + Pixel below[256]; bool windowAbove[256]; bool windowBelow[256]; + + //flush() + static uint start; + static uint count; } lines[240]; }; diff --git a/higan/sfc/ppu-fast/serialization.cpp b/higan/sfc/ppu-fast/serialization.cpp index 6abea464..ef96687c 100644 --- a/higan/sfc/ppu-fast/serialization.cpp +++ b/higan/sfc/ppu-fast/serialization.cpp @@ -1,4 +1,160 @@ auto PPU::serialize(serializer& s) -> void { Thread::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); } diff --git a/higan/systems/Super Famicom.sys/boards.bml b/higan/systems/Super Famicom.sys/boards.bml index db280d56..feec2a76 100644 --- a/higan/systems/Super Famicom.sys/boards.bml +++ b/higan/systems/Super Famicom.sys/boards.bml @@ -1,5 +1,5 @@ database - revision: 2018-05-17 + revision: 2018-06-01 //Boards (Production) @@ -565,7 +565,7 @@ board: SHVC-YJ0N-01 //Boards (Generic) database - revision: 2018-05-16 + revision: 2018-06-01 board: ARM-LOROM-RAM memory type=ROM content=Program @@ -885,7 +885,7 @@ board: SPC7110-RAM-EPSONRTC memory type=RAM content=Save map address=00-3f,80-bf:6000-7fff mask=0xe000 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 board: ST-LOROM diff --git a/higan/target-bsnes/program/interface.cpp b/higan/target-bsnes/program/interface.cpp index 61a16bf3..b1525329 100644 --- a/higan/target-bsnes/program/interface.cpp +++ b/higan/target-bsnes/program/interface.cpp @@ -211,8 +211,8 @@ auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint heig pitch >>= 2; if(presentation->overscanCropping.checked()) { - data += 16 * pitch; - height -= 32; + if(height == 240) data += 8 * pitch, height -= 16; + if(height == 480) data += 16 * pitch, height -= 32; } if(video->lock(output, length, width, height)) {