diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp index 99ccfda6..63faea03 100644 --- a/bsnes/emulator/emulator.hpp +++ b/bsnes/emulator/emulator.hpp @@ -29,13 +29,13 @@ using namespace nall; namespace Emulator { static const string Name = "bsnes"; - static const string Version = "114"; + static const string Version = "114.1"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org"; //incremented only when serialization format changes - static const string SerializerVersion = "112"; + static const string SerializerVersion = "114.1"; namespace Constants { namespace Colorburst { diff --git a/bsnes/sfc/ppu-fast/background.cpp b/bsnes/sfc/ppu-fast/background.cpp index 792e5be0..cb880bd6 100644 --- a/bsnes/sfc/ppu-fast/background.cpp +++ b/bsnes/sfc/ppu-fast/background.cpp @@ -1,15 +1,3 @@ -//single-threaded -auto PPU::Line::cacheBackground(PPU::IO::Background& bg) -> void { - if(y == 1) { - bg.mosaicCounter = ppu.io.mosaicSize + 1; - bg.mosaicOffset = 1; - } else if(--bg.mosaicCounter == 0) { - bg.mosaicCounter = ppu.io.mosaicSize + 1; - bg.mosaicOffset += ppu.io.mosaicSize + 1; - } -} - -//parallelized auto PPU::Line::renderBackground(PPU::IO::Background& self, uint8 source) -> void { if(!self.aboveEnable && !self.belowEnable) return; if(self.tileMode == TileMode::Mode7) return renderMode7(self, source); @@ -39,10 +27,14 @@ auto PPU::Line::renderBackground(PPU::IO::Background& self, uint8 source) -> voi uint hmask = (width << self.tileSize << !!(self.screenSize & 1)) - 1; uint vmask = (width << self.tileSize << !!(self.screenSize & 2)) - 1; - uint y = self.mosaicEnable ? self.mosaicOffset : this->y; + uint y = this->y; + if(self.mosaicEnable) y -= io.mosaic.size - io.mosaic.counter; if(hires) { hscroll <<= 1; - if(io.interlace) y = y << 1 | field(); + if(io.interlace) { + y = y << 1 | field(); + if(self.mosaicEnable) y -= io.mosaic.size - io.mosaic.counter + ppu.field(); + } } uint mosaicCounter = 1; @@ -103,7 +95,7 @@ auto PPU::Line::renderBackground(PPU::IO::Background& self, uint8 source) -> voi for(uint tileX = 0; tileX < 8; tileX++, x++) { if(x & width) continue; //x < 0 || x >= width - if(!self.mosaicEnable || --mosaicCounter == 0) { + if(--mosaicCounter == 0) { uint color, shift = mirrorX ? tileX : 7 - tileX; /*if(self.tileMode >= TileMode::BPP2)*/ { color = data >> shift + 0 & 1; @@ -120,7 +112,7 @@ auto PPU::Line::renderBackground(PPU::IO::Background& self, uint8 source) -> voi color += data >> shift + 49 & 128; } - mosaicCounter = 1 + io.mosaicSize; + mosaicCounter = self.mosaicEnable ? io.mosaic.size : 1; mosaicPalette = color; mosaicPriority = tilePriority; if(directColorMode) { diff --git a/bsnes/sfc/ppu-fast/io.cpp b/bsnes/sfc/ppu-fast/io.cpp index 9db01ea0..7447be79 100644 --- a/bsnes/sfc/ppu-fast/io.cpp +++ b/bsnes/sfc/ppu-fast/io.cpp @@ -248,11 +248,17 @@ auto PPU::writeIO(uint address, uint8 data) -> void { } case 0x2106: { //MOSAIC + bool enable = io.bg1.mosaicEnable || io.bg2.mosaicEnable || io.bg3.mosaicEnable || io.bg4.mosaicEnable; 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; + io.mosaic.enable = data >> 0 & 15; + io.mosaic.size = (data >> 4 & 15) + 1; + if(!enable && (data >> 0 & 15)) { + //mosaic vcounter is reloaded when mosaic becomes enabled + io.mosaic.counter = io.mosaic.size + 1; + } return; } diff --git a/bsnes/sfc/ppu-fast/line.cpp b/bsnes/sfc/ppu-fast/line.cpp index 7341728c..f14a9899 100644 --- a/bsnes/sfc/ppu-fast/line.cpp +++ b/bsnes/sfc/ppu-fast/line.cpp @@ -26,12 +26,13 @@ auto PPU::Line::flush() -> void { } auto PPU::Line::cache() -> void { - cacheBackground(ppu.io.bg1); - cacheBackground(ppu.io.bg2); - cacheBackground(ppu.io.bg3); - cacheBackground(ppu.io.bg4); - uint y = ppu.vcounter(); + if(y == 1) { + ppu.io.mosaic.counter = ppu.io.mosaic.enable ? ppu.io.mosaic.size + 1 : 0; + } + if(ppu.io.mosaic.counter && !--ppu.io.mosaic.counter) { + ppu.io.mosaic.counter = ppu.io.mosaic.enable ? ppu.io.mosaic.size + 0 : 0; + } if(ppu.io.displayDisable || y >= ppu.vdisp()) { io.displayDisable = true; } else { diff --git a/bsnes/sfc/ppu-fast/mode7.cpp b/bsnes/sfc/ppu-fast/mode7.cpp index 7b59bbda..c5251c17 100644 --- a/bsnes/sfc/ppu-fast/mode7.cpp +++ b/bsnes/sfc/ppu-fast/mode7.cpp @@ -1,11 +1,12 @@ auto PPU::Line::renderMode7(PPU::IO::Background& self, uint8 source) -> void { //HD mode 7 support - if(!ppu.hdMosaic() || !self.mosaicEnable || !io.mosaicSize) { + if(!ppu.hdMosaic() || !self.mosaicEnable || !io.mosaic.size) { if(ppu.hdScale() > 1) return renderMode7HD(self, source); } - int Y = self.mosaicEnable ? self.mosaicOffset : this->y; + int Y = this->y; int y = !io.mode7.vflip ? Y : 255 - Y; + if(self.mosaicEnable) y -= io.mosaic.size - io.mosaic.counter; int a = (int16)io.mode7.a; int b = (int16)io.mode7.b; @@ -50,8 +51,8 @@ auto PPU::Line::renderMode7(PPU::IO::Background& self, uint8 source) -> void { palette &= 0x7f; } - if(!self.mosaicEnable || --mosaicCounter == 0) { - mosaicCounter = 1 + io.mosaicSize; + if(--mosaicCounter == 0) { + mosaicCounter = self.mosaicEnable ? io.mosaic.size : 1; mosaicPalette = palette; mosaicPriority = priority; if(io.col.directColor && source == Source::BG1) { diff --git a/bsnes/sfc/ppu-fast/ppu.hpp b/bsnes/sfc/ppu-fast/ppu.hpp index f93b39a3..61e8aea5 100644 --- a/bsnes/sfc/ppu-fast/ppu.hpp +++ b/bsnes/sfc/ppu-fast/ppu.hpp @@ -85,7 +85,6 @@ public: bool oamPriority = 0; bool bgPriority = 0; uint8 bgMode = 0; - uint8 mosaicSize = 0; bool vramIncrementMode = 0; uint8 vramMapping = 0; uint8 vramIncrementSize = 0; @@ -99,6 +98,15 @@ public: bool pseudoHires = 0; bool extbg = 0; + struct Mosaic { + //serialization.cpp + auto serialize(serializer&) -> void; + + uint8 enable = 0; + uint8 size = 1; + uint8 counter = 0; + } mosaic; + struct Mode7 { //serialization.cpp auto serialize(serializer&) -> void; @@ -161,8 +169,6 @@ public: bool aboveEnable = 0; bool belowEnable = 0; bool mosaicEnable = 0; - uint16 mosaicCounter = 0; - uint16 mosaicOffset = 0; uint16 tiledataAddress = 0; uint16 screenAddress = 0; uint8 screenSize = 0; @@ -293,7 +299,6 @@ public: alwaysinline auto plotHD(Pixel*, uint x, uint8 source, uint8 priority, uint16 color, bool hires, bool subpixel) -> void; //background.cpp - auto cacheBackground(PPU::IO::Background&) -> void; auto renderBackground(PPU::IO::Background&, uint8 source) -> void; auto getTile(PPU::IO::Background&, uint hoffset, uint voffset) -> uint; diff --git a/bsnes/sfc/ppu-fast/serialization.cpp b/bsnes/sfc/ppu-fast/serialization.cpp index 7dbdb6b0..75248288 100644 --- a/bsnes/sfc/ppu-fast/serialization.cpp +++ b/bsnes/sfc/ppu-fast/serialization.cpp @@ -44,7 +44,6 @@ auto PPU::IO::serialize(serializer& s) -> void { s.integer(oamPriority); s.integer(bgPriority); s.integer(bgMode); - s.integer(mosaicSize); s.integer(vramIncrementMode); s.integer(vramMapping); s.integer(vramIncrementSize); @@ -58,6 +57,7 @@ auto PPU::IO::serialize(serializer& s) -> void { s.integer(pseudoHires); s.integer(extbg); + mosaic.serialize(s); mode7.serialize(s); window.serialize(s); bg1.serialize(s); @@ -68,6 +68,12 @@ auto PPU::IO::serialize(serializer& s) -> void { col.serialize(s); } +auto PPU::IO::Mosaic::serialize(serializer& s) -> void { + s.integer(enable); + s.integer(size); + s.integer(counter); +} + auto PPU::IO::Mode7::serialize(serializer& s) -> void { s.integer(hflip); s.integer(vflip); @@ -114,8 +120,6 @@ auto PPU::IO::Background::serialize(serializer& s) -> void { s.integer(aboveEnable); s.integer(belowEnable); s.integer(mosaicEnable); - s.integer(mosaicCounter); - s.integer(mosaicOffset); s.integer(tiledataAddress); s.integer(screenAddress); s.integer(screenSize); diff --git a/bsnes/sfc/ppu/background.cpp b/bsnes/sfc/ppu/background.cpp index a03cc28f..09db47e3 100644 --- a/bsnes/sfc/ppu/background.cpp +++ b/bsnes/sfc/ppu/background.cpp @@ -1,5 +1,4 @@ #include "mode7.cpp" -uint4 PPU::Background::Mosaic::size; auto PPU::Background::hires() const -> bool { return ppu.io.bgMode == 5 || ppu.io.bgMode == 6; @@ -11,26 +10,13 @@ auto PPU::Background::frame() -> void { //H = 0 auto PPU::Background::scanline() -> void { - if(ppu.vcounter() == 1) { - mosaic.vcounter = mosaic.size + 1; - mosaic.voffset = 1; - latch.hoffset = io.hoffset; - latch.voffset = io.voffset; - } else if(--mosaic.vcounter == 0) { - mosaic.vcounter = mosaic.size + 1; - mosaic.voffset += mosaic.size + 1; - latch.hoffset = io.hoffset; - latch.voffset = io.voffset; - } - - mosaic.hcounter = mosaic.size + 1; + mosaic.hcounter = ppu.mosaic.size; mosaic.hoffset = 0; if(io.mode == Mode::Mode7) return beginMode7(); - if(mosaic.size == 0) { - latch.hoffset = io.hoffset; - latch.voffset = io.voffset; - } + + latch.hoffset = io.hoffset; + latch.voffset = io.voffset; nameTableIndex = 0; characterIndex = 0; @@ -52,13 +38,17 @@ auto PPU::Background::fetchNameTable() -> void { int x = (ppu.hcounter() & ~31) >> 2; uint hpixel = x << hires(); - uint vpixel = mosaic.enable ? (uint)mosaic.voffset : ppu.vcounter(); + uint vpixel = ppu.vcounter(); + uint hscroll = latch.hoffset; + uint vscroll = latch.voffset; - uint hscroll = mosaic.enable ? latch.hoffset : io.hoffset; - uint vscroll = mosaic.enable ? latch.voffset : io.voffset; + if(mosaic.enable) vpixel -= ppu.mosaic.voffset(); if(hires()) { hscroll <<= 1; - if(ppu.io.interlace) vpixel = vpixel << 1 | (ppu.field() && !mosaic.enable); + if(ppu.io.interlace) { + vpixel = vpixel << 1 | ppu.field(); + if(mosaic.enable) vpixel -= ppu.mosaic.voffset() + ppu.field(); + } } bool repeated = false; @@ -214,10 +204,10 @@ auto PPU::Background::run(bool screen) -> void { uint x = ppu.hcounter() - 56 >> 2; if(x == 0) { - mosaic.hcounter = mosaic.size + 1; + mosaic.hcounter = ppu.mosaic.size; mosaic.pixel = pixel; } else if((!hires() || screen == Screen::Below) && --mosaic.hcounter == 0) { - mosaic.hcounter = mosaic.size + 1; + mosaic.hcounter = ppu.mosaic.size; mosaic.pixel = pixel; } else if(mosaic.enable) { pixel = mosaic.pixel; @@ -246,6 +236,5 @@ auto PPU::Background::power() -> void { output.below = {}; mosaic = {}; - mosaic.size = random(); mosaic.enable = random(); } diff --git a/bsnes/sfc/ppu/background.hpp b/bsnes/sfc/ppu/background.hpp index d952277f..4b752774 100644 --- a/bsnes/sfc/ppu/background.hpp +++ b/bsnes/sfc/ppu/background.hpp @@ -61,15 +61,9 @@ struct Background { } output; struct Mosaic { - static uint4 size; uint1 enable; - - uint16 vcounter; uint16 hcounter; - - uint16 voffset; uint16 hoffset; - Pixel pixel; } mosaic; diff --git a/bsnes/sfc/ppu/io.cpp b/bsnes/sfc/ppu/io.cpp index e809c785..fa07c7bd 100644 --- a/bsnes/sfc/ppu/io.cpp +++ b/bsnes/sfc/ppu/io.cpp @@ -262,11 +262,15 @@ auto PPU::writeIO(uint addr, uint8 data) -> void { //MOSAIC case 0x2106: { + if(!mosaic.enable() && (data >> 0 & 15)) { + //mosaic vcounter is reloaded when mosaic becomes enabled + mosaic.vcounter = (data >> 4 & 15) + 2; + } bg1.mosaic.enable = data >> 0 & 1; bg2.mosaic.enable = data >> 1 & 1; bg3.mosaic.enable = data >> 2 & 1; bg4.mosaic.enable = data >> 3 & 1; - Background::Mosaic::size = data >> 4 & 15; + mosaic.size = (data >> 4 & 15) + 1; return; } diff --git a/bsnes/sfc/ppu/main.cpp b/bsnes/sfc/ppu/main.cpp index d2330d7c..07cbc54d 100644 --- a/bsnes/sfc/ppu/main.cpp +++ b/bsnes/sfc/ppu/main.cpp @@ -17,6 +17,7 @@ auto PPU::main() -> void { obj.frame(); } + mosaic.scanline(); bg1.scanline(); bg2.scanline(); bg3.scanline(); diff --git a/bsnes/sfc/ppu/mode7.cpp b/bsnes/sfc/ppu/mode7.cpp index 3af38b9b..af246453 100644 --- a/bsnes/sfc/ppu/mode7.cpp +++ b/bsnes/sfc/ppu/mode7.cpp @@ -21,13 +21,12 @@ auto PPU::Background::runMode7() -> void { int voffset = (int13)latch.voffset; uint x = mosaic.hoffset; - uint y = !mosaic.enable ? ppu.vcounter() : ppu.bg1.mosaic.voffset; //BG2 vertical mosaic uses BG1 mosaic size + uint y = ppu.vcounter(); + if(ppu.bg1.mosaic.enable) y -= ppu.mosaic.voffset(); //BG2 vertical mosaic uses BG1 mosaic enable - if(!mosaic.enable) { - mosaic.hoffset += 1; - } else if(--mosaic.hcounter == 0) { - mosaic.hcounter = mosaic.size + 1; - mosaic.hoffset += mosaic.size + 1; + if(--mosaic.hcounter == 0) { + mosaic.hcounter = ppu.mosaic.size; + mosaic.hoffset += ppu.mosaic.size; } if(ppu.io.hflipMode7) x = 255 - x; diff --git a/bsnes/sfc/ppu/mosaic.cpp b/bsnes/sfc/ppu/mosaic.cpp new file mode 100644 index 00000000..79fe4d51 --- /dev/null +++ b/bsnes/sfc/ppu/mosaic.cpp @@ -0,0 +1,26 @@ +auto PPU::Mosaic::enable() const -> bool { + if(ppu.bg1.mosaic.enable) return true; + if(ppu.bg2.mosaic.enable) return true; + if(ppu.bg3.mosaic.enable) return true; + if(ppu.bg4.mosaic.enable) return true; + return false; +} + +auto PPU::Mosaic::voffset() const -> uint { + return size - vcounter; +} + +//H = 0 +auto PPU::Mosaic::scanline() -> void { + if(ppu.vcounter() == 1) { + vcounter = enable() ? size + 1 : 0; + } + if(vcounter && !--vcounter) { + vcounter = enable() ? size + 0 : 0; + } +} + +auto PPU::Mosaic::power() -> void { + size = (random() & 15) + 1; + vcounter = 0; +} diff --git a/bsnes/sfc/ppu/mosaic.hpp b/bsnes/sfc/ppu/mosaic.hpp new file mode 100644 index 00000000..261739e6 --- /dev/null +++ b/bsnes/sfc/ppu/mosaic.hpp @@ -0,0 +1,13 @@ +struct Mosaic { + //mosaic.cpp + alwaysinline auto enable() const -> bool; + alwaysinline auto voffset() const -> uint; + auto scanline() -> void; + auto power() -> void; + + //serialization.cpp + auto serialize(serializer&) -> void; + + uint5 size; + uint5 vcounter; +}; diff --git a/bsnes/sfc/ppu/ppu.cpp b/bsnes/sfc/ppu/ppu.cpp index 6ec524ec..dc8d135f 100644 --- a/bsnes/sfc/ppu/ppu.cpp +++ b/bsnes/sfc/ppu/ppu.cpp @@ -5,6 +5,7 @@ namespace SuperFamicom { PPU ppu; #include "main.cpp" #include "io.cpp" +#include "mosaic.cpp" #include "background.cpp" #include "object.cpp" #include "window.cpp" @@ -173,6 +174,7 @@ auto PPU::power(bool reset) -> void { //$213d OPVCT io.vcounter = 0; + mosaic.power(); bg1.power(); bg2.power(); bg3.power(); diff --git a/bsnes/sfc/ppu/ppu.hpp b/bsnes/sfc/ppu/ppu.hpp index f7eac0f7..353acf27 100644 --- a/bsnes/sfc/ppu/ppu.hpp +++ b/bsnes/sfc/ppu/ppu.hpp @@ -153,11 +153,13 @@ private: uint16 vcounter; } io; + #include "mosaic.hpp" #include "background.hpp" #include "object.hpp" #include "window.hpp" #include "screen.hpp" + Mosaic mosaic; Background bg1; Background bg2; Background bg3; diff --git a/bsnes/sfc/ppu/serialization.cpp b/bsnes/sfc/ppu/serialization.cpp index 777ca41d..30723a8a 100644 --- a/bsnes/sfc/ppu/serialization.cpp +++ b/bsnes/sfc/ppu/serialization.cpp @@ -73,6 +73,7 @@ auto PPU::serialize(serializer& s) -> void { s.integer(io.hcounter); s.integer(io.vcounter); + mosaic.serialize(s); bg1.serialize(s); bg2.serialize(s); bg3.serialize(s); @@ -82,6 +83,11 @@ auto PPU::serialize(serializer& s) -> void { screen.serialize(s); } +auto PPU::Mosaic::serialize(serializer& s) -> void { + s.integer(size); + s.integer(vcounter); +} + auto PPU::Background::serialize(serializer& s) -> void { s.integer(io.tiledataAddress); s.integer(io.screenAddress); @@ -105,11 +111,8 @@ auto PPU::Background::serialize(serializer& s) -> void { s.integer(output.below.palette); s.integer(output.below.paletteGroup); - s.integer(mosaic.size); s.integer(mosaic.enable); - s.integer(mosaic.vcounter); s.integer(mosaic.hcounter); - s.integer(mosaic.voffset); s.integer(mosaic.hoffset); s.integer(mosaic.pixel.priority);