Update to v104r11 release.

byuu says:

Changelog:

  - sfc/ppu/background: minor code cleanup and simplification
  - sfc/ppu/background: $2106 MOSAIC register was implemented
    incorrectly
  - sfc/ppu/background: fixed mosaic effects in hires mode (temporary
    fix)
  - sfc/ppu/background: fixed mosaic effects in interlace mode [Cydrak]

Errata:

  - sfc/ppu/background/background.cpp:48: should be
    `if(!mosaic.enable) {`

Turns out there is only one mosaic size, and the other four bits are
per-BG mosaic enable. This matters a lot for hires/interlace, as
mosaicSize=0 (2x2) is not the same thing as mosaicEnable=false (1x1).

Although I've now implemented this, I really don't like how my mosaic
implementation works right now. I tried to redesign the entire system,
and completely failed. So I started over from v104r10 again and instead
went with a more evolutionary improvement for now. I'll keep trying.

Also, the combination of mosaic + offset-per-tile is still sketchy, as
is mode 6 offset-per-tile. I'll get to those in the future as well.
This commit is contained in:
Tim Allen 2017-09-05 10:56:52 +10:00
parent 28060d3a69
commit 3dce3aa3c8
6 changed files with 128 additions and 131 deletions

View File

@ -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 = "104.10"; static const string Version = "104.11";
static const string Author = "byuu"; static const string Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "http://byuu.org/"; static const string Website = "http://byuu.org/";

View File

@ -1,13 +1,16 @@
#include "mode7.cpp" #include "mode7.cpp"
uint4 PPU::Background::Mosaic::size;
auto PPU::Background::hires() const -> bool {
return ppu.io.bgMode == 5 || ppu.io.bgMode == 6;
}
auto PPU::Background::voffset() const -> uint16 { auto PPU::Background::voffset() const -> uint16 {
if(io.mosaic) return latch.voffset; return mosaic.enable ? latch.voffset : io.voffset;
return io.voffset;
} }
auto PPU::Background::hoffset() const -> uint16 { auto PPU::Background::hoffset() const -> uint16 {
if(io.mosaic) return latch.hoffset; return mosaic.enable ? latch.hoffset : io.hoffset;
return io.hoffset;
} }
//V = 0, H = 0 //V = 0, H = 0
@ -20,76 +23,73 @@ auto PPU::Background::scanline() -> void {
//H = 28 //H = 28
auto PPU::Background::begin() -> void { auto PPU::Background::begin() -> void {
bool hires = (ppu.io.bgMode == 5 || ppu.io.bgMode == 6);
x = -7; x = -7;
y = ppu.vcounter(); y = ppu.vcounter();
tileCounter = 7 - (io.hoffset & 7) << hires();
for(auto& word : data) word = 0;
if(y == 1) { if(y == 1) {
mosaic.vcounter = io.mosaic + 1; mosaic.vcounter = mosaic.size + 1;
mosaic.voffset = 1; mosaic.voffset = 1;
latch.hoffset = io.hoffset; latch.hoffset = io.hoffset;
latch.voffset = io.voffset; latch.voffset = io.voffset;
} else if(--mosaic.vcounter == 0) { } else if(--mosaic.vcounter == 0) {
mosaic.vcounter = io.mosaic + 1; mosaic.vcounter = mosaic.size + 1;
mosaic.voffset += io.mosaic + 1; mosaic.voffset += mosaic.size + 1;
latch.hoffset = io.hoffset; latch.hoffset = io.hoffset;
latch.voffset = io.voffset; latch.voffset = io.voffset;
} }
tileCounter = (7 - (latch.hoffset & 7)) << hires; mosaic.hcounter = mosaic.size + 1;
for(auto& d : data) d = 0;
mosaic.hcounter = io.mosaic + 1;
mosaic.hoffset = 0; mosaic.hoffset = 0;
if(io.mode == Mode::Mode7) return beginMode7(); if(io.mode == Mode::Mode7) return beginMode7();
if(io.mosaic == 0) { if(mosaic.size == 0) {
latch.hoffset = io.hoffset; latch.hoffset = io.hoffset;
latch.voffset = io.voffset; latch.voffset = io.voffset;
} }
} }
auto PPU::Background::getTile() -> void { auto PPU::Background::getTile() -> void {
bool hires = (ppu.io.bgMode == 5 || ppu.io.bgMode == 6); uint colorDepth = io.mode == Mode::BPP2 ? 0 : io.mode == Mode::BPP4 ? 1 : 2;
uint paletteOffset = ppu.io.bgMode == 0 ? id << 5 : 0;
uint colorDepth = (io.mode == Mode::BPP2 ? 0 : io.mode == Mode::BPP4 ? 1 : 2);
uint paletteOffset = (ppu.io.bgMode == 0 ? id << 5 : 0);
uint paletteSize = 2 << colorDepth; uint paletteSize = 2 << colorDepth;
uint tileMask = ppu.vram.mask >> (3 + colorDepth); uint tileMask = ppu.vram.mask >> 3 + colorDepth;
uint tiledataIndex = io.tiledataAddress >> (3 + colorDepth); uint tiledataIndex = io.tiledataAddress >> 3 + colorDepth;
uint tileHeight = (io.tileSize == TileSize::Size8x8 ? 3 : 4); uint tileHeight = io.tileSize == TileSize::Size8x8 ? 3 : 4;
uint tileWidth = (!hires ? tileHeight : 4); uint tileWidth = !hires() ? tileHeight : 4;
uint width = 256 << hires; uint width = 256 << hires();
uint hmask = (tileHeight == 3 ? width : width << 1); uint hmask = tileHeight == 3 ? width : width << 1;
uint vmask = hmask; uint vmask = hmask;
if(io.screenSize & 1) hmask <<= 1; if(io.screenSize & 1) hmask <<= 1;
if(io.screenSize & 2) vmask <<= 1; if(io.screenSize & 2) vmask <<= 1;
hmask--; hmask--;
vmask--; vmask--;
uint px = x << hires; uint px = x << hires();
uint py = (io.mosaic == 0 ? y : mosaic.voffset); uint py = mosaic.enable ? (uint)mosaic.voffset : y;
uint hscroll = hoffset(); uint hscroll = hoffset();
uint vscroll = voffset(); uint vscroll = voffset();
if(hires) { if(hires()) {
hscroll <<= 1; hscroll <<= 1;
if(ppu.io.interlace) py = (py << 1) + ppu.field(); if(ppu.io.interlace) py = py << 1 | (ppu.field() & !mosaic.enable); //todo: temporary vmosaic hack
} }
uint hoffset = hscroll + px; uint hoffset = hscroll + px;
uint voffset = vscroll + py; uint voffset = vscroll + py;
if(ppu.io.bgMode == 2 || ppu.io.bgMode == 4 || ppu.io.bgMode == 6) { if(ppu.io.bgMode == 2 || ppu.io.bgMode == 4 || ppu.io.bgMode == 6) {
uint16 offsetX = (x + (hscroll & 7)); uint16 offsetX = x + (hscroll & 7);
if(offsetX >= 8) { if(offsetX >= 8) {
uint hval = ppu.bg3.getTile((offsetX - 8) + (ppu.bg3.hoffset() & ~7), ppu.bg3.voffset() + 0); uint hval = ppu.bg3.getTile((offsetX - 8) + (ppu.bg3.hoffset() & ~7), ppu.bg3.voffset() + 0);
uint vval = ppu.bg3.getTile((offsetX - 8) + (ppu.bg3.hoffset() & ~7), ppu.bg3.voffset() + 8); uint vval = ppu.bg3.getTile((offsetX - 8) + (ppu.bg3.hoffset() & ~7), ppu.bg3.voffset() + 8);
uint validMask = (id == ID::BG1 ? 0x2000 : 0x4000); uint validMask = id == ID::BG1 ? 0x2000 : 0x4000;
if(ppu.io.bgMode == 4) { if(ppu.io.bgMode == 4) {
if(hval & validMask) { if(hval & validMask) {
@ -109,31 +109,31 @@ auto PPU::Background::getTile() -> void {
hoffset &= hmask; hoffset &= hmask;
voffset &= vmask; voffset &= vmask;
uint screenX = (io.screenSize & 1 ? 32 << 5 : 0); uint screenX = io.screenSize & 1 ? 32 << 5 : 0;
uint screenY = (io.screenSize & 2 ? 32 << 5 : 0); uint screenY = io.screenSize & 2 ? 32 << 5 : 0;
if(io.screenSize == 3) screenY <<= 1; if(io.screenSize == 3) screenY <<= 1;
uint tx = hoffset >> tileWidth; uint tx = hoffset >> tileWidth;
uint ty = voffset >> tileHeight; uint ty = voffset >> tileHeight;
uint16 offset = ((ty & 0x1f) << 5) + (tx & 0x1f); uint16 offset = (ty & 0x1f) << 5 | (tx & 0x1f);
if(tx & 0x20) offset += screenX; if(tx & 0x20) offset += screenX;
if(ty & 0x20) offset += screenY; if(ty & 0x20) offset += screenY;
uint16 address = io.screenAddress + offset; uint16 address = io.screenAddress + offset;
tile = ppu.vram[address]; tile = ppu.vram[address];
bool mirrorY = tile & 0x8000; bool mirrorY = tile.bit(15);
bool mirrorX = tile & 0x4000; bool mirrorX = tile.bit(14);
priority = io.priority[bool(tile & 0x2000)]; priority = io.priority[tile.bit(13)];
paletteNumber = (tile >> 10) & 7; paletteNumber = tile.bits(10,12);
paletteIndex = paletteOffset + (paletteNumber << paletteSize); paletteIndex = paletteOffset + (paletteNumber << paletteSize);
if(tileWidth == 4 && (bool)(hoffset & 8) != mirrorX) tile += 1; if(tileWidth == 4 && (bool)(hoffset & 8) != mirrorX) tile += 1;
if(tileHeight == 4 && (bool)(voffset & 8) != mirrorY) tile += 16; if(tileHeight == 4 && (bool)(voffset & 8) != mirrorY) tile += 16;
uint16 character = ((tile & 0x03ff) + tiledataIndex) & tileMask; uint16 character = tile.bits(0,9) + tiledataIndex & tileMask;
if(mirrorY) voffset ^= 7; if(mirrorY) voffset ^= 7;
offset = (character << (3 + colorDepth)) + (voffset & 7); offset = (character << 3 + colorDepth) + (voffset & 7);
switch(io.mode) { switch(io.mode) {
case Mode::BPP8: case Mode::BPP8:
@ -146,20 +146,19 @@ auto PPU::Background::getTile() -> void {
} }
if(mirrorX) for(auto n : range(2)) { if(mirrorX) for(auto n : range(2)) {
data[n] = ((data[n] >> 4) & 0x0f0f0f0f) | ((data[n] << 4) & 0xf0f0f0f0); data[n] = (data[n] >> 4 & 0x0f0f0f0f) | (data[n] << 4 & 0xf0f0f0f0);
data[n] = ((data[n] >> 2) & 0x33333333) | ((data[n] << 2) & 0xcccccccc); data[n] = (data[n] >> 2 & 0x33333333) | (data[n] << 2 & 0xcccccccc);
data[n] = ((data[n] >> 1) & 0x55555555) | ((data[n] << 1) & 0xaaaaaaaa); data[n] = (data[n] >> 1 & 0x55555555) | (data[n] << 1 & 0xaaaaaaaa);
} }
} }
auto PPU::Background::run(bool screen) -> void { auto PPU::Background::run(bool screen) -> void {
if(ppu.vcounter() == 0) return; if(ppu.vcounter() == 0) return;
bool hires = (ppu.io.bgMode == 5 || ppu.io.bgMode == 6);
if(screen == Screen::Below) { if(screen == Screen::Below) {
output.above.priority = 0; output.above.priority = 0;
output.below.priority = 0; output.below.priority = 0;
if(!hires) return; if(!hires()) return;
} }
if(tileCounter-- == 0) { if(tileCounter-- == 0) {
@ -169,19 +168,26 @@ auto PPU::Background::run(bool screen) -> void {
if(io.mode == Mode::Mode7) return runMode7(); if(io.mode == Mode::Mode7) return runMode7();
uint8 palette = getTileColor(); uint8 color = getTileColor();
if(x == 0) mosaic.hcounter = 1; Pixel pixel;
if(x >= 0 && --mosaic.hcounter == 0) { pixel.priority = priority;
mosaic.hcounter = io.mosaic + 1; pixel.palette = color ? paletteIndex + color : 0;
mosaic.priority = priority; pixel.tile = tile;
mosaic.palette = palette ? paletteIndex + palette : 0;
mosaic.tile = tile; if(x == 0) {
mosaic.hcounter = 1;
mosaic.pixel = pixel;
} else if(x >= 1 && screen == Screen::Above && --mosaic.hcounter == 0) {
mosaic.hcounter = mosaic.size + 1;
mosaic.pixel = pixel;
} else if(mosaic.enable) {
pixel = mosaic.pixel;
} }
if(screen == Screen::Above) x++; if(screen == Screen::Above) x++;
if(mosaic.palette == 0) return; if(pixel.palette == 0) return;
if(!hires || screen == Screen::Above) if(io.aboveEnable) output.above = mosaic; if(!hires() || screen == Screen::Above) if(io.aboveEnable) output.above = pixel;
if(!hires || screen == Screen::Below) if(io.belowEnable) output.below = mosaic; if(!hires() || screen == Screen::Below) if(io.belowEnable) output.below = pixel;
} }
auto PPU::Background::getTileColor() -> uint { auto PPU::Background::getTileColor() -> uint {
@ -207,34 +213,24 @@ auto PPU::Background::getTileColor() -> uint {
} }
auto PPU::Background::power() -> void { auto PPU::Background::power() -> void {
io = {};
io.tiledataAddress = (random() & 0x0f) << 12; io.tiledataAddress = (random() & 0x0f) << 12;
io.screenAddress = (random() & 0xfc) << 8; io.screenAddress = (random() & 0xfc) << 8;
io.screenSize = random(); io.screenSize = random();
io.mosaic = random();
io.tileSize = random(); io.tileSize = random();
io.mode = 0;
for(auto& p : io.priority) p = 0;
io.aboveEnable = random(); io.aboveEnable = random();
io.belowEnable = random(); io.belowEnable = random();
io.hoffset = random(); io.hoffset = random();
io.voffset = random(); io.voffset = random();
latch.hoffset = 0; latch = {};
latch.voffset = 0;
output.above.palette = 0; output.above = {};
output.above.priority = 0; output.below = {};
output.below.palette = 0;
output.below.priority = 0;
mosaic.priority = 0; mosaic = {};
mosaic.palette = 0; mosaic.size = random();
mosaic.tile = 0; mosaic.enable = random();
mosaic.vcounter = 0;
mosaic.voffset = 0;
mosaic.hcounter = 0;
mosaic.hoffset = 0;
x = 0; x = 0;
y = 0; y = 0;
@ -244,29 +240,28 @@ auto PPU::Background::power() -> void {
priority = 0; priority = 0;
paletteNumber = 0; paletteNumber = 0;
paletteIndex = 0; paletteIndex = 0;
for(auto& d : data) d = 0; for(auto& word : data) word = 0;
} }
auto PPU::Background::getTile(uint x, uint y) -> uint { auto PPU::Background::getTile(uint x, uint y) -> uint {
bool hires = (ppu.io.bgMode == 5 || ppu.io.bgMode == 6); uint tileHeight = io.tileSize == TileSize::Size8x8 ? 3 : 4;
uint tileHeight = (io.tileSize == TileSize::Size8x8 ? 3 : 4); uint tileWidth = !hires() ? tileHeight : 4;
uint tileWidth = (!hires ? tileHeight : 4); uint width = !hires() ? 256 : 512;
uint width = (!hires ? 256 : 512); uint maskX = tileHeight == 3 ? width : width << 1;
uint maskX = (tileHeight == 3 ? width : width << 1);
uint maskY = maskX; uint maskY = maskX;
if(io.screenSize & 1) maskX <<= 1; if(io.screenSize & 1) maskX <<= 1;
if(io.screenSize & 2) maskY <<= 1; if(io.screenSize & 2) maskY <<= 1;
maskX--; maskX--;
maskY--; maskY--;
uint screenX = (io.screenSize & 1 ? 32 << 5 : 0); uint screenX = io.screenSize & 1 ? 32 << 5 : 0;
uint screenY = (io.screenSize & 2 ? 32 << 5 : 0); uint screenY = io.screenSize & 2 ? 32 << 5 : 0;
if(io.screenSize == 3) screenY <<= 1; if(io.screenSize == 3) screenY <<= 1;
x = (x & maskX) >> tileWidth; x = (x & maskX) >> tileWidth;
y = (y & maskY) >> tileHeight; y = (y & maskY) >> tileHeight;
uint16 offset = ((y & 0x1f) << 5) + (x & 0x1f); uint16 offset = (y & 0x1f) << 5 | (x & 0x1f);
if(x & 0x20) offset += screenX; if(x & 0x20) offset += screenX;
if(y & 0x20) offset += screenY; if(y & 0x20) offset += screenY;

View File

@ -1,8 +1,9 @@
struct Background { struct Background {
Background(uint id) : id(id) {} Background(uint id) : id(id) {}
alwaysinline auto voffset() const -> uint16; alwaysinline auto hires() const -> bool;
alwaysinline auto hoffset() const -> uint16; alwaysinline auto hoffset() const -> uint16;
alwaysinline auto voffset() const -> uint16;
auto frame() -> void; auto frame() -> void;
auto scanline() -> void; auto scanline() -> void;
@ -31,14 +32,13 @@ struct Background {
uint16 tiledataAddress; uint16 tiledataAddress;
uint16 screenAddress; uint16 screenAddress;
uint2 screenSize; uint2 screenSize;
uint4 mosaic; uint1 tileSize;
bool tileSize;
uint mode; uint8 mode;
uint priority[2]; uint8 priority[2];
bool aboveEnable; uint1 aboveEnable;
bool belowEnable; uint1 belowEnable;
uint16 hoffset; uint16 hoffset;
uint16 voffset; uint16 voffset;
@ -49,32 +49,39 @@ struct Background {
uint16 voffset; uint16 voffset;
} latch; } latch;
struct Output {
struct Pixel { struct Pixel {
uint priority; //0 = none (transparent) uint8 priority; //0 = none (transparent)
uint8 palette; uint8 palette;
uint16 tile; uint16 tile;
} above, below; } above, below;
struct Output {
Pixel above;
Pixel below;
} output; } output;
struct Mosaic : Output::Pixel { struct Mosaic {
uint vcounter; static uint4 size;
uint voffset; uint1 enable;
uint hcounter;
uint hoffset; uint16 vcounter;
uint16 hcounter;
uint16 voffset;
uint16 hoffset;
Pixel pixel;
} mosaic; } mosaic;
struct {
int x; int x;
int y; int y;
uint tileCounter; uint3 tileCounter;
uint tile; uint16 tile;
uint priority; uint8 priority;
uint paletteNumber; uint3 paletteNumber;
uint paletteIndex; uint8 paletteIndex;
uint32 data[2]; uint32 data[2];
};
friend class PPU; friend class PPU;
}; };

View File

@ -25,8 +25,8 @@ auto PPU::Background::runMode7() -> void {
uint y = ppu.bg1.mosaic.voffset; //BG2 vertical mosaic uses BG1 mosaic size uint y = ppu.bg1.mosaic.voffset; //BG2 vertical mosaic uses BG1 mosaic size
if(--mosaic.hcounter == 0) { if(--mosaic.hcounter == 0) {
mosaic.hcounter = io.mosaic + 1; mosaic.hcounter = mosaic.size + 1;
mosaic.hoffset += io.mosaic + 1; mosaic.hoffset += mosaic.size + 1;
} }
if(ppu.io.hflipMode7) x = 255 - x; if(ppu.io.hflipMode7) x = 255 - x;

View File

@ -238,11 +238,11 @@ auto PPU::writeIO(uint24 addr, uint8 data) -> void {
//MOSAIC //MOSAIC
case 0x2106: { case 0x2106: {
uint mosaicSize = data.bits(4,7); bg1.mosaic.enable = data.bit(0);
bg1.io.mosaic = data.bit(0) ? mosaicSize : 0; bg2.mosaic.enable = data.bit(1);
bg2.io.mosaic = data.bit(1) ? mosaicSize : 0; bg3.mosaic.enable = data.bit(2);
bg3.io.mosaic = data.bit(2) ? mosaicSize : 0; bg4.mosaic.enable = data.bit(3);
bg4.io.mosaic = data.bit(3) ? mosaicSize : 0; Background::Mosaic::size = data.bits(4,7);
return; return;
} }

View File

@ -93,15 +93,11 @@ auto PPU::Background::serialize(serializer& s) -> void {
s.integer(io.tiledataAddress); s.integer(io.tiledataAddress);
s.integer(io.screenAddress); s.integer(io.screenAddress);
s.integer(io.screenSize); s.integer(io.screenSize);
s.integer(io.mosaic);
s.integer(io.tileSize); s.integer(io.tileSize);
s.integer(io.mode); s.integer(io.mode);
s.array(io.priority); s.array(io.priority);
s.integer(io.aboveEnable); s.integer(io.aboveEnable);
s.integer(io.belowEnable); s.integer(io.belowEnable);
s.integer(io.hoffset); s.integer(io.hoffset);
s.integer(io.voffset); s.integer(io.voffset);
@ -111,23 +107,22 @@ auto PPU::Background::serialize(serializer& s) -> void {
s.integer(output.above.priority); s.integer(output.above.priority);
s.integer(output.above.palette); s.integer(output.above.palette);
s.integer(output.above.tile); s.integer(output.above.tile);
s.integer(output.below.priority); s.integer(output.below.priority);
s.integer(output.below.palette); s.integer(output.below.palette);
s.integer(output.below.tile); s.integer(output.below.tile);
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);
s.integer(mosaic.pixel.palette);
s.integer(mosaic.pixel.tile);
s.integer(x); s.integer(x);
s.integer(y); s.integer(y);
s.integer(mosaic.priority);
s.integer(mosaic.palette);
s.integer(mosaic.tile);
s.integer(mosaic.vcounter);
s.integer(mosaic.voffset);
s.integer(mosaic.hcounter);
s.integer(mosaic.hoffset);
s.integer(tileCounter); s.integer(tileCounter);
s.integer(tile); s.integer(tile);
s.integer(priority); s.integer(priority);