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 {
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 License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

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

View File

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

View File

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

View File

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