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:
Tim Allen 2018-06-02 12:47:37 +10:00
parent 5d29700fa1
commit c67fb2c726
11 changed files with 438 additions and 203 deletions

View File

@ -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/";

View File

@ -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;

View File

@ -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;
}

View File

@ -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};
}

View File

@ -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;

View File

@ -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 {

View File

@ -13,9 +13,6 @@ PPU ppu;
#include <sfc/ppu/counter/serialization.cpp>
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<auto (uint24, uint8) -> uint8> reader{&PPU::readIO, this};
function<auto (uint24, uint8) -> 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;
}
}

View File

@ -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];
};

View File

@ -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);
}

View File

@ -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

View File

@ -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)) {