Update to v099r01 release.

byuu says:

Changelog:
- massive cleanups and optimizations on the PPU core
- ~9% speedup over v099 official

This is pretty much it for the low-hanging fruit of speeding up higan. Any
more gains from this point will be extremely hard-fought, unfortunately.
This commit is contained in:
Tim Allen 2016-06-14 20:51:54 +10:00
parent c074c6e064
commit ae5b4c3bb3
24 changed files with 1449 additions and 1656 deletions

View File

@ -9,7 +9,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; static const string Name = "higan";
static const string Version = "099"; static const string Version = "099.01";
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,16 +1,13 @@
#include "mode7.cpp" #include "mode7.cpp"
PPU::Background::Background(PPU &self, uint id) : self(self), id(id) { auto PPU::Background::voffset() const -> uint16 {
if(r.mosaic) return latch.voffset;
return r.voffset;
} }
auto PPU::Background::voffset() const -> uint { auto PPU::Background::hoffset() const -> uint16 {
if(regs.mosaic) return cache.voffset; if(r.mosaic) return latch.hoffset;
return regs.voffset; return r.hoffset;
}
auto PPU::Background::hoffset() const -> uint {
if(regs.mosaic) return cache.hoffset;
return regs.hoffset;
} }
//V = 0, H = 0 //V = 0, H = 0
@ -23,88 +20,88 @@ auto PPU::Background::scanline() -> void {
//H = 28 //H = 28
auto PPU::Background::begin() -> void { auto PPU::Background::begin() -> void {
bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); bool hires = (ppu.r.bgMode == 5 || ppu.r.bgMode == 6);
x = -7; x = -7;
y = self.vcounter(); y = ppu.vcounter();
if(y == 1) { if(y == 1) {
mosaic.vcounter = regs.mosaic + 1; mosaic.vcounter = r.mosaic + 1;
mosaic.voffset = 1; mosaic.voffset = 1;
cache.hoffset = regs.hoffset; latch.hoffset = r.hoffset;
cache.voffset = regs.voffset; latch.voffset = r.voffset;
} else if(--mosaic.vcounter == 0) { } else if(--mosaic.vcounter == 0) {
mosaic.vcounter = regs.mosaic + 1; mosaic.vcounter = r.mosaic + 1;
mosaic.voffset += regs.mosaic + 1; mosaic.voffset += r.mosaic + 1;
cache.hoffset = regs.hoffset; latch.hoffset = r.hoffset;
cache.voffset = regs.voffset; latch.voffset = r.voffset;
} }
tile_counter = (7 - (cache.hoffset & 7)) << hires; tileCounter = (7 - (latch.hoffset & 7)) << hires;
for(unsigned n = 0; n < 8; n++) data[n] = 0; for(auto& d : data) d = 0;
mosaic.hcounter = regs.mosaic + 1; mosaic.hcounter = r.mosaic + 1;
mosaic.hoffset = 0; mosaic.hoffset = 0;
if(regs.mode == Mode::Mode7) return begin_mode7(); if(r.mode == Mode::Mode7) return beginMode7();
if(regs.mosaic == 0) { if(r.mosaic == 0) {
cache.hoffset = regs.hoffset; latch.hoffset = r.hoffset;
cache.voffset = regs.voffset; latch.voffset = r.voffset;
} }
} }
auto PPU::Background::get_tile() -> void { auto PPU::Background::getTile() -> void {
bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); bool hires = (ppu.r.bgMode == 5 || ppu.r.bgMode == 6);
unsigned color_depth = (regs.mode == Mode::BPP2 ? 0 : regs.mode == Mode::BPP4 ? 1 : 2); uint colorDepth = (r.mode == Mode::BPP2 ? 0 : r.mode == Mode::BPP4 ? 1 : 2);
unsigned palette_offset = (self.regs.bgmode == 0 ? id << 5 : 0); uint paletteOffset = (ppu.r.bgMode == 0 ? id << 5 : 0);
unsigned palette_size = 2 << color_depth; uint paletteSize = 2 << colorDepth;
unsigned tile_mask = 0x0fff >> color_depth; uint tileMask = 0x0fff >> colorDepth;
unsigned tiledata_index = regs.tiledata_addr >> (4 + color_depth); uint tiledataIndex = r.tiledataAddress >> (4 + colorDepth);
unsigned tile_height = (regs.tile_size == TileSize::Size8x8 ? 3 : 4); uint tileHeight = (r.tileSize == TileSize::Size8x8 ? 3 : 4);
unsigned tile_width = (!hires ? tile_height : 4); uint tileWidth = (!hires ? tileHeight : 4);
unsigned width = 256 << hires; uint width = 256 << hires;
unsigned hmask = (tile_height == 3 ? width : width << 1); uint hmask = (tileHeight == 3 ? width : width << 1);
unsigned vmask = hmask; uint vmask = hmask;
if(regs.screen_size & 1) hmask <<= 1; if(r.screenSize & 1) hmask <<= 1;
if(regs.screen_size & 2) vmask <<= 1; if(r.screenSize & 2) vmask <<= 1;
hmask--; hmask--;
vmask--; vmask--;
unsigned px = x << hires; uint px = x << hires;
unsigned py = (regs.mosaic == 0 ? y : mosaic.voffset); uint py = (r.mosaic == 0 ? y : mosaic.voffset);
unsigned hscroll = hoffset(); uint hscroll = hoffset();
unsigned vscroll = voffset(); uint vscroll = voffset();
if(hires) { if(hires) {
hscroll <<= 1; hscroll <<= 1;
if(self.regs.interlace) py = (py << 1) + self.field(); if(ppu.r.interlace) py = (py << 1) + ppu.field();
} }
unsigned hoffset = hscroll + px; uint hoffset = hscroll + px;
unsigned voffset = vscroll + py; uint voffset = vscroll + py;
if(self.regs.bgmode == 2 || self.regs.bgmode == 4 || self.regs.bgmode == 6) { if(ppu.r.bgMode == 2 || ppu.r.bgMode == 4 || ppu.r.bgMode == 6) {
uint16 offset_x = (x + (hscroll & 7)); uint16 offsetX = (x + (hscroll & 7));
if(offset_x >= 8) { if(offsetX >= 8) {
unsigned hval = self.bg3.get_tile((offset_x - 8) + (self.bg3.hoffset() & ~7), self.bg3.voffset() + 0); uint hval = ppu.bg3.getTile((offsetX - 8) + (ppu.bg3.hoffset() & ~7), ppu.bg3.voffset() + 0);
unsigned vval = self.bg3.get_tile((offset_x - 8) + (self.bg3.hoffset() & ~7), self.bg3.voffset() + 8); uint vval = ppu.bg3.getTile((offsetX - 8) + (ppu.bg3.hoffset() & ~7), ppu.bg3.voffset() + 8);
unsigned valid_mask = (id == ID::BG1 ? 0x2000 : 0x4000); uint validMask = (id == ID::BG1 ? 0x2000 : 0x4000);
if(self.regs.bgmode == 4) { if(ppu.r.bgMode == 4) {
if(hval & valid_mask) { if(hval & validMask) {
if((hval & 0x8000) == 0) { if((hval & 0x8000) == 0) {
hoffset = offset_x + (hval & ~7); hoffset = offsetX + (hval & ~7);
} else { } else {
voffset = y + hval; voffset = y + hval;
} }
} }
} else { } else {
if(hval & valid_mask) hoffset = offset_x + (hval & ~7); if(hval & validMask) hoffset = offsetX + (hval & ~7);
if(vval & valid_mask) voffset = y + vval; if(vval & validMask) voffset = y + vval;
} }
} }
} }
@ -112,127 +109,127 @@ auto PPU::Background::get_tile() -> void {
hoffset &= hmask; hoffset &= hmask;
voffset &= vmask; voffset &= vmask;
unsigned screen_x = (regs.screen_size & 1 ? 32 << 5 : 0); uint screenX = (r.screenSize & 1 ? 32 << 5 : 0);
unsigned screen_y = (regs.screen_size & 2 ? 32 << 5 : 0); uint screenY = (r.screenSize & 2 ? 32 << 5 : 0);
if(regs.screen_size == 3) screen_y <<= 1; if(r.screenSize == 3) screenY <<= 1;
unsigned tx = hoffset >> tile_width; uint tx = hoffset >> tileWidth;
unsigned ty = voffset >> tile_height; uint ty = voffset >> tileHeight;
uint16 offset = ((ty & 0x1f) << 5) + (tx & 0x1f); uint16 offset = ((ty & 0x1f) << 5) + (tx & 0x1f);
if(tx & 0x20) offset += screen_x; if(tx & 0x20) offset += screenX;
if(ty & 0x20) offset += screen_y; if(ty & 0x20) offset += screenY;
uint16 addr = regs.screen_addr + (offset << 1); uint16 address = r.screenAddress + (offset << 1);
tile = (ppu.vram[addr + 0] << 0) + (ppu.vram[addr + 1] << 8); tile = (ppu.memory.vram[address + 0] << 0) + (ppu.memory.vram[address + 1] << 8);
bool mirror_y = tile & 0x8000; bool mirrorY = tile & 0x8000;
bool mirror_x = tile & 0x4000; bool mirrorX = tile & 0x4000;
priority = (tile & 0x2000 ? regs.priority1 : regs.priority0); priority = r.priority[bool(tile & 0x2000)];
palette_number = (tile >> 10) & 7; paletteNumber = (tile >> 10) & 7;
palette_index = palette_offset + (palette_number << palette_size); paletteIndex = paletteOffset + (paletteNumber << paletteSize);
if(tile_width == 4 && (bool)(hoffset & 8) != mirror_x) tile += 1; if(tileWidth == 4 && (bool)(hoffset & 8) != mirrorX) tile += 1;
if(tile_height == 4 && (bool)(voffset & 8) != mirror_y) tile += 16; if(tileHeight == 4 && (bool)(voffset & 8) != mirrorY) tile += 16;
uint16 character = ((tile & 0x03ff) + tiledata_index) & tile_mask; uint16 character = ((tile & 0x03ff) + tiledataIndex) & tileMask;
if(mirror_y) voffset ^= 7; if(mirrorY) voffset ^= 7;
offset = (character << (4 + color_depth)) + ((voffset & 7) << 1); offset = (character << (4 + colorDepth)) + ((voffset & 7) << 1);
switch(regs.mode) { switch(r.mode) {
case Mode::BPP8: case Mode::BPP8:
data[7] = ppu.vram[offset + 49]; data[1].byte(3) = ppu.memory.vram[offset + 49];
data[6] = ppu.vram[offset + 48]; data[1].byte(2) = ppu.memory.vram[offset + 48];
data[5] = ppu.vram[offset + 33]; data[1].byte(1) = ppu.memory.vram[offset + 33];
data[4] = ppu.vram[offset + 32]; data[1].byte(0) = ppu.memory.vram[offset + 32];
case Mode::BPP4: case Mode::BPP4:
data[3] = ppu.vram[offset + 17]; data[0].byte(3) = ppu.memory.vram[offset + 17];
data[2] = ppu.vram[offset + 16]; data[0].byte(2) = ppu.memory.vram[offset + 16];
case Mode::BPP2: case Mode::BPP2:
data[1] = ppu.vram[offset + 1]; data[0].byte(1) = ppu.memory.vram[offset + 1];
data[0] = ppu.vram[offset + 0]; data[0].byte(0) = ppu.memory.vram[offset + 0];
} }
if(mirror_x) for(unsigned n = 0; n < 8; n++) { if(mirrorX) for(auto n : range(2)) {
//reverse data bits in data[n]: 01234567 -> 76543210 data[n] = ((data[n] >> 4) & 0x0f0f0f0f) | ((data[n] << 4) & 0xf0f0f0f0);
data[n] = ((data[n] >> 4) & 0x0f) | ((data[n] << 4) & 0xf0); data[n] = ((data[n] >> 2) & 0x33333333) | ((data[n] << 2) & 0xcccccccc);
data[n] = ((data[n] >> 2) & 0x33) | ((data[n] << 2) & 0xcc); data[n] = ((data[n] >> 1) & 0x55555555) | ((data[n] << 1) & 0xaaaaaaaa);
data[n] = ((data[n] >> 1) & 0x55) | ((data[n] << 1) & 0xaa);
} }
} }
auto PPU::Background::run(bool screen) -> void { auto PPU::Background::run(bool screen) -> void {
if(self.vcounter() == 0) return; if(ppu.vcounter() == 0) return;
bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); bool hires = (ppu.r.bgMode == 5 || ppu.r.bgMode == 6);
if(screen == Screen::Sub) { if(screen == Screen::Below) {
output.main.priority = 0; output.above.priority = 0;
output.sub.priority = 0; output.below.priority = 0;
if(hires == false) return; if(!hires) return;
} }
if(tile_counter-- == 0) { if(tileCounter-- == 0) {
tile_counter = 7; tileCounter = 7;
get_tile(); getTile();
} }
if(regs.mode == Mode::Mode7) return run_mode7(); if(r.mode == Mode::Mode7) return runMode7();
uint8 palette = get_tile_color(); uint8 palette = getTileColor();
if(x == 0) mosaic.hcounter = 1; if(x == 0) mosaic.hcounter = 1;
if(x >= 0 && --mosaic.hcounter == 0) { if(x >= 0 && --mosaic.hcounter == 0) {
mosaic.hcounter = regs.mosaic + 1; mosaic.hcounter = r.mosaic + 1;
mosaic.priority = priority; mosaic.priority = priority;
mosaic.palette = palette ? palette_index + palette : 0; mosaic.palette = palette ? paletteIndex + palette : 0;
mosaic.tile = tile; mosaic.tile = tile;
} }
if(screen == Screen::Main) x++; if(screen == Screen::Above) x++;
if(mosaic.palette == 0) return; if(mosaic.palette == 0) return;
if(hires == false || screen == Screen::Main) if(regs.main_enable) output.main = mosaic; if(!hires || screen == Screen::Above) if(r.aboveEnable) output.above = mosaic;
if(hires == false || screen == Screen::Sub ) if(regs.sub_enable ) output.sub = mosaic; if(!hires || screen == Screen::Below) if(r.belowEnable) output.below = mosaic;
} }
auto PPU::Background::get_tile_color() -> uint { auto PPU::Background::getTileColor() -> uint {
unsigned color = 0; uint color = 0;
switch(regs.mode) { switch(r.mode) {
case Mode::BPP8: case Mode::BPP8:
color += (data[7] >> 0) & 0x80; data[7] <<= 1; color += data[1] >> 28 & 0x80;
color += (data[6] >> 1) & 0x40; data[6] <<= 1; color += data[1] >> 21 & 0x40;
color += (data[5] >> 2) & 0x20; data[5] <<= 1; color += data[1] >> 14 & 0x20;
color += (data[4] >> 3) & 0x10; data[4] <<= 1; color += data[1] >> 7 & 0x10;
data[1] <<= 1;
case Mode::BPP4: case Mode::BPP4:
color += (data[3] >> 4) & 0x08; data[3] <<= 1; color += data[0] >> 28 & 0x08;
color += (data[2] >> 5) & 0x04; data[2] <<= 1; color += data[0] >> 21 & 0x04;
case Mode::BPP2: case Mode::BPP2:
color += (data[1] >> 6) & 0x02; data[1] <<= 1; color += data[0] >> 14 & 0x02;
color += (data[0] >> 7) & 0x01; data[0] <<= 1; color += data[0] >> 7 & 0x01;
data[0] <<= 1;
} }
return color; return color;
} }
auto PPU::Background::reset() -> void { auto PPU::Background::reset() -> void {
regs.tiledata_addr = (random(0x0000) & 0x07) << 13; r.tiledataAddress = (random(0x0000) & 0x07) << 13;
regs.screen_addr = (random(0x0000) & 0x7c) << 9; r.screenAddress = (random(0x0000) & 0x7c) << 9;
regs.screen_size = random(0); r.screenSize = random(0);
regs.mosaic = random(0); r.mosaic = random(0);
regs.tile_size = random(0); r.tileSize = random(0);
regs.mode = 0; r.mode = 0;
regs.priority0 = 0; for(auto& p : r.priority) p = 0;
regs.priority1 = 0; r.aboveEnable = random(0);
regs.main_enable = random(0); r.belowEnable = random(0);
regs.sub_enable = random(0); r.hoffset = random(0x0000);
regs.hoffset = random(0x0000); r.voffset = random(0x0000);
regs.voffset = random(0x0000);
cache.hoffset = 0; latch.hoffset = 0;
cache.voffset = 0; latch.voffset = 0;
output.main.palette = 0; output.above.palette = 0;
output.main.priority = 0; output.above.priority = 0;
output.sub.palette = 0; output.below.palette = 0;
output.sub.priority = 0; output.below.priority = 0;
mosaic.priority = 0; mosaic.priority = 0;
mosaic.palette = 0; mosaic.palette = 0;
@ -246,37 +243,37 @@ auto PPU::Background::reset() -> void {
x = 0; x = 0;
y = 0; y = 0;
tile_counter = 0; tileCounter = 0;
tile = 0; tile = 0;
priority = 0; priority = 0;
palette_number = 0; paletteNumber = 0;
palette_index = 0; paletteIndex = 0;
for(unsigned n = 0; n < 8; n++) data[n] = 0; for(auto& d : data) d = 0;
} }
auto PPU::Background::get_tile(uint x, uint y) -> uint { auto PPU::Background::getTile(uint x, uint y) -> uint {
bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); bool hires = (ppu.r.bgMode == 5 || ppu.r.bgMode == 6);
unsigned tile_height = (regs.tile_size == TileSize::Size8x8 ? 3 : 4); uint tileHeight = (r.tileSize == TileSize::Size8x8 ? 3 : 4);
unsigned tile_width = (!hires ? tile_height : 4); uint tileWidth = (!hires ? tileHeight : 4);
unsigned width = (!hires ? 256 : 512); uint width = (!hires ? 256 : 512);
unsigned mask_x = (tile_height == 3 ? width : width << 1); uint maskX = (tileHeight == 3 ? width : width << 1);
unsigned mask_y = mask_x; uint maskY = maskX;
if(regs.screen_size & 1) mask_x <<= 1; if(r.screenSize & 1) maskX <<= 1;
if(regs.screen_size & 2) mask_y <<= 1; if(r.screenSize & 2) maskY <<= 1;
mask_x--; maskX--;
mask_y--; maskY--;
unsigned screen_x = (regs.screen_size & 1 ? 32 << 5 : 0); uint screenX = (r.screenSize & 1 ? 32 << 5 : 0);
unsigned screen_y = (regs.screen_size & 2 ? 32 << 5 : 0); uint screenY = (r.screenSize & 2 ? 32 << 5 : 0);
if(regs.screen_size == 3) screen_y <<= 1; if(r.screenSize == 3) screenY <<= 1;
x = (x & mask_x) >> tile_width; x = (x & maskX) >> tileWidth;
y = (y & mask_y) >> tile_height; y = (y & maskY) >> tileHeight;
uint16 offset = ((y & 0x1f) << 5) + (x & 0x1f); uint16 offset = ((y & 0x1f) << 5) + (x & 0x1f);
if(x & 0x20) offset += screen_x; if(x & 0x20) offset += screenX;
if(y & 0x20) offset += screen_y; if(y & 0x20) offset += screenY;
uint16 addr = regs.screen_addr + (offset << 1); uint16 address = r.screenAddress + (offset << 1);
return (ppu.vram[addr + 0] << 0) + (ppu.vram[addr + 1] << 8); return (ppu.memory.vram[address + 0] << 0) + (ppu.memory.vram[address + 1] << 8);
} }

View File

@ -1,46 +1,60 @@
struct Background { struct Background {
Background(uint id) : id(id) {}
alwaysinline auto voffset() const -> uint16;
alwaysinline auto hoffset() const -> uint16;
auto frame() -> void;
auto scanline() -> void;
auto begin() -> void;
auto run(bool screen) -> void;
auto reset() -> void;
auto getTile() -> void;
auto getTileColor() -> uint;
auto getTile(uint x, uint y) -> uint;
alwaysinline auto clip(int n) -> int;
auto beginMode7() -> void;
auto runMode7() -> void;
auto serialize(serializer&) -> void;
struct ID { enum : uint { BG1, BG2, BG3, BG4 }; }; struct ID { enum : uint { BG1, BG2, BG3, BG4 }; };
const uint id; const uint id;
struct Mode { enum : uint { BPP2, BPP4, BPP8, Mode7, Inactive }; }; struct Mode { enum : uint { BPP2, BPP4, BPP8, Mode7, Inactive }; };
struct ScreenSize { enum : uint { Size32x32, Size32x64, Size64x32, Size64x64 }; }; struct ScreenSize { enum : uint { Size32x32, Size32x64, Size64x32, Size64x64 }; };
struct TileSize { enum : uint { Size8x8, Size16x16 }; }; struct TileSize { enum : uint { Size8x8, Size16x16 }; };
struct Screen { enum : uint { Main, Sub }; }; struct Screen { enum : uint { Above, Below }; };
Background(PPU& self, uint id); struct Registers {
uint16 tiledataAddress;
struct Regs { uint16 screenAddress;
uint16 tiledata_addr; uint2 screenSize;
uint16 screen_addr;
uint2 screen_size;
uint4 mosaic; uint4 mosaic;
bool tile_size; bool tileSize;
uint mode; uint mode;
uint priority0; uint priority[2];
uint priority1;
bool main_enable; bool aboveEnable;
bool sub_enable; bool belowEnable;
uint16 hoffset; uint16 hoffset;
uint16 voffset; uint16 voffset;
} regs; } r;
struct Cache { struct Latch {
uint16 hoffset; uint16 hoffset;
uint16 voffset; uint16 voffset;
} cache; } latch;
alwaysinline auto voffset() const -> uint;
alwaysinline auto hoffset() const -> uint;
struct Output { struct Output {
struct Pixel { struct Pixel {
uint priority; //0 = none (transparent) uint priority; //0 = none (transparent)
uint8 palette; uint8 palette;
uint16 tile; uint16 tile;
} main, sub; } above, below;
} output; } output;
struct Mosaic : Output::Pixel { struct Mosaic : Output::Pixel {
@ -54,29 +68,13 @@ struct Background {
int x; int x;
int y; int y;
uint tile_counter; uint tileCounter;
uint tile; uint tile;
uint priority; uint priority;
uint palette_number; uint paletteNumber;
uint palette_index; uint paletteIndex;
uint8 data[8]; uint32 data[2];
}; };
auto frame() -> void;
auto scanline() -> void;
auto begin() -> void;
auto run(bool screen) -> void;
auto reset() -> void;
auto get_tile() -> void;
auto get_tile_color() -> uint;
auto get_tile(uint x, uint y) -> uint;
alwaysinline auto clip(int n) -> int;
auto begin_mode7() -> void;
auto run_mode7() -> void;
auto serialize(serializer&) -> void;
PPU& self;
friend class PPU; friend class PPU;
}; };

View File

@ -4,54 +4,54 @@ auto PPU::Background::clip(int n) -> int {
} }
//H = 28 //H = 28
auto PPU::Background::begin_mode7() -> void { auto PPU::Background::beginMode7() -> void {
cache.hoffset = self.regs.mode7_hoffset; latch.hoffset = ppu.r.hoffsetMode7;
cache.voffset = self.regs.mode7_voffset; latch.voffset = ppu.r.voffsetMode7;
} }
auto PPU::Background::run_mode7() -> void { auto PPU::Background::runMode7() -> void {
signed a = sclip<16>(self.regs.m7a); int a = (int16)ppu.r.m7a;
signed b = sclip<16>(self.regs.m7b); int b = (int16)ppu.r.m7b;
signed c = sclip<16>(self.regs.m7c); int c = (int16)ppu.r.m7c;
signed d = sclip<16>(self.regs.m7d); int d = (int16)ppu.r.m7d;
signed cx = sclip<13>(self.regs.m7x); int cx = (int13)ppu.r.m7x;
signed cy = sclip<13>(self.regs.m7y); int cy = (int13)ppu.r.m7y;
signed hoffset = sclip<13>(cache.hoffset); int hoffset = (int13)latch.hoffset;
signed voffset = sclip<13>(cache.voffset); int voffset = (int13)latch.voffset;
if(Background::x++ & ~255) return; if(Background::x++ & ~255) return;
unsigned x = mosaic.hoffset; uint x = mosaic.hoffset;
unsigned y = self.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 = regs.mosaic + 1; mosaic.hcounter = r.mosaic + 1;
mosaic.hoffset += regs.mosaic + 1; mosaic.hoffset += r.mosaic + 1;
} }
if(self.regs.mode7_hflip) x = 255 - x; if(ppu.r.hflipMode7) x = 255 - x;
if(self.regs.mode7_vflip) y = 255 - y; if(ppu.r.vflipMode7) y = 255 - y;
signed psx = ((a * clip(hoffset - cx)) & ~63) + ((b * clip(voffset - cy)) & ~63) + ((b * y) & ~63) + (cx << 8); int psx = ((a * clip(hoffset - cx)) & ~63) + ((b * clip(voffset - cy)) & ~63) + ((b * y) & ~63) + (cx << 8);
signed psy = ((c * clip(hoffset - cx)) & ~63) + ((d * clip(voffset - cy)) & ~63) + ((d * y) & ~63) + (cy << 8); int psy = ((c * clip(hoffset - cx)) & ~63) + ((d * clip(voffset - cy)) & ~63) + ((d * y) & ~63) + (cy << 8);
signed px = psx + (a * x); int px = psx + (a * x);
signed py = psy + (c * x); int py = psy + (c * x);
//mask pseudo-FP bits //mask pseudo-FP bits
px >>= 8; px >>= 8;
py >>= 8; py >>= 8;
unsigned tile; uint tile;
unsigned palette; uint palette;
switch(self.regs.mode7_repeat) { switch(ppu.r.repeatMode7) {
//screen repetition outside of screen area //screen repetition outside of screen area
case 0: case 0:
case 1: case 1:
px &= 1023; px &= 1023;
py &= 1023; py &= 1023;
tile = ppu.vram[((py >> 3) * 128 + (px >> 3)) << 1]; tile = ppu.memory.vram[((py >> 3) * 128 + (px >> 3)) << 1];
palette = ppu.vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; palette = ppu.memory.vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
break; break;
//palette color 0 outside of screen area //palette color 0 outside of screen area
@ -61,8 +61,8 @@ auto PPU::Background::run_mode7() -> void {
} else { } else {
px &= 1023; px &= 1023;
py &= 1023; py &= 1023;
tile = ppu.vram[((py >> 3) * 128 + (px >> 3)) << 1]; tile = ppu.memory.vram[((py >> 3) * 128 + (px >> 3)) << 1];
palette = ppu.vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; palette = ppu.memory.vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
} }
break; break;
@ -73,31 +73,31 @@ auto PPU::Background::run_mode7() -> void {
} else { } else {
px &= 1023; px &= 1023;
py &= 1023; py &= 1023;
tile = ppu.vram[((py >> 3) * 128 + (px >> 3)) << 1]; tile = ppu.memory.vram[((py >> 3) * 128 + (px >> 3)) << 1];
} }
palette = ppu.vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; palette = ppu.memory.vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
break; break;
} }
unsigned priority; uint priority;
if(id == ID::BG1) { if(id == ID::BG1) {
priority = regs.priority0; priority = r.priority[0];
} else if(id == ID::BG2) { } else if(id == ID::BG2) {
priority = (palette & 0x80 ? regs.priority1 : regs.priority0); priority = r.priority[bool(palette & 0x80)];
palette &= 0x7f; palette &= 0x7f;
} }
if(palette == 0) return; if(palette == 0) return;
if(regs.main_enable) { if(r.aboveEnable) {
output.main.palette = palette; output.above.palette = palette;
output.main.priority = priority; output.above.priority = priority;
output.main.tile = 0; output.above.tile = 0;
} }
if(regs.sub_enable) { if(r.belowEnable) {
output.sub.palette = palette; output.below.palette = palette;
output.sub.priority = priority; output.below.priority = priority;
output.sub.tile = 0; output.below.tile = 0;
} }
} }

View File

@ -1,10 +1,10 @@
//this should only be called by CPU::PPUcounter::tick(); //this should only be called by CPU::PPUcounter::tick();
//keeps track of previous counter positions in history table //keeps track of previous counter positions in history table
void PPUcounter::tick() { auto PPUcounter::tick() -> void {
status.hcounter += 2; //increment by smallest unit of time status.hcounter += 2; //increment by smallest unit of time
if(status.hcounter >= 1360 && status.hcounter == lineclocks()) { if(status.hcounter >= 1360 && status.hcounter == lineclocks()) {
status.hcounter = 0; status.hcounter = 0;
vcounter_tick(); vcounterTick();
} }
history.index = (history.index + 1) & 2047; history.index = (history.index + 1) & 2047;
@ -15,16 +15,16 @@ void PPUcounter::tick() {
//this should only be called by PPU::PPUcounter::tick(n); //this should only be called by PPU::PPUcounter::tick(n);
//allows stepping by more than the smallest unit of time //allows stepping by more than the smallest unit of time
void PPUcounter::tick(unsigned clocks) { auto PPUcounter::tick(uint clocks) -> void {
status.hcounter += clocks; status.hcounter += clocks;
if(status.hcounter >= lineclocks()) { if(status.hcounter >= lineclocks()) {
status.hcounter -= lineclocks(); status.hcounter -= lineclocks();
vcounter_tick(); vcounterTick();
} }
} }
//internal //internal
void PPUcounter::vcounter_tick() { auto PPUcounter::vcounterTick() -> void {
if(++status.vcounter == 128) status.interlace = ppu.interlace(); if(++status.vcounter == 128) status.interlace = ppu.interlace();
if((system.region() == System::Region::NTSC && status.interlace == false && status.vcounter == 262) if((system.region() == System::Region::NTSC && status.interlace == false && status.vcounter == 262)
@ -40,13 +40,13 @@ void PPUcounter::vcounter_tick() {
if(scanline) scanline(); if(scanline) scanline();
} }
bool PPUcounter::field () const { return status.field; } auto PPUcounter::field() const -> bool { return status.field; }
uint16 PPUcounter::vcounter() const { return status.vcounter; } auto PPUcounter::vcounter() const -> uint16 { return status.vcounter; }
uint16 PPUcounter::hcounter() const { return status.hcounter; } auto PPUcounter::hcounter() const -> uint16 { return status.hcounter; }
bool PPUcounter::field (unsigned offset) const { return history.field [(history.index - (offset >> 1)) & 2047]; } auto PPUcounter::field(uint offset) const -> bool { return history.field[(history.index - (offset >> 1)) & 2047]; }
uint16 PPUcounter::vcounter(unsigned offset) const { return history.vcounter[(history.index - (offset >> 1)) & 2047]; } auto PPUcounter::vcounter(uint offset) const -> uint16 { return history.vcounter[(history.index - (offset >> 1)) & 2047]; }
uint16 PPUcounter::hcounter(unsigned offset) const { return history.hcounter[(history.index - (offset >> 1)) & 2047]; } auto PPUcounter::hcounter(uint offset) const -> uint16 { return history.hcounter[(history.index - (offset >> 1)) & 2047]; }
//one PPU dot = 4 CPU clocks //one PPU dot = 4 CPU clocks
// //
@ -54,10 +54,10 @@ uint16 PPUcounter::hcounter(unsigned offset) const { return history.hcounter[(hi
//this does not apply to NTSC non-interlace scanline 240 on odd fields. this is //this does not apply to NTSC non-interlace scanline 240 on odd fields. this is
//because the PPU skips one dot to alter the color burst phase of the video signal. //because the PPU skips one dot to alter the color burst phase of the video signal.
// //
//dot 323 range = { 1292, 1294, 1296 } //dot 323 range = {1292, 1294, 1296}
//dot 327 range = { 1310, 1312, 1314 } //dot 327 range = {1310, 1312, 1314}
uint16 PPUcounter::hdot() const { auto PPUcounter::hdot() const -> uint16 {
if(system.region() == System::Region::NTSC && status.interlace == false && vcounter() == 240 && field() == 1) { if(system.region() == System::Region::NTSC && status.interlace == false && vcounter() == 240 && field() == 1) {
return (hcounter() >> 2); return (hcounter() >> 2);
} else { } else {
@ -65,21 +65,21 @@ uint16 PPUcounter::hdot() const {
} }
} }
uint16 PPUcounter::lineclocks() const { auto PPUcounter::lineclocks() const -> uint16 {
if(system.region() == System::Region::NTSC && status.interlace == false && vcounter() == 240 && field() == 1) return 1360; if(system.region() == System::Region::NTSC && status.interlace == false && vcounter() == 240 && field() == 1) return 1360;
return 1364; return 1364;
} }
void PPUcounter::reset() { auto PPUcounter::reset() -> void {
status.interlace = false; status.interlace = false;
status.field = 0; status.field = 0;
status.vcounter = 0; status.vcounter = 0;
status.hcounter = 0; status.hcounter = 0;
history.index = 0; history.index = 0;
for(unsigned i = 0; i < 2048; i++) { for(auto n : range(2048)) {
history.field [i] = 0; history.field [n] = 0;
history.vcounter[i] = 0; history.vcounter[n] = 0;
history.hcounter[i] = 0; history.hcounter[n] = 0;
} }
} }

View File

@ -10,27 +10,27 @@
//point before this in the frame, which is handled internally by this class at //point before this in the frame, which is handled internally by this class at
//V=128. //V=128.
class PPUcounter { struct PPUcounter {
public: alwaysinline auto tick() -> void;
alwaysinline void tick(); alwaysinline auto tick(uint clocks) -> void;
alwaysinline void tick(unsigned clocks);
alwaysinline bool field () const; alwaysinline auto field() const -> bool;
alwaysinline uint16 vcounter() const; alwaysinline auto vcounter() const -> uint16;
alwaysinline uint16 hcounter() const; alwaysinline auto hcounter() const -> uint16;
inline uint16 hdot() const; inline auto hdot() const -> uint16;
inline uint16 lineclocks() const; inline auto lineclocks() const -> uint16;
alwaysinline bool field (unsigned offset) const; alwaysinline auto field(uint offset) const -> bool;
alwaysinline uint16 vcounter(unsigned offset) const; alwaysinline auto vcounter(uint offset) const -> uint16;
alwaysinline uint16 hcounter(unsigned offset) const; alwaysinline auto hcounter(uint offset) const -> uint16;
inline void reset(); inline auto reset() -> void;
function<void ()> scanline; auto serialize(serializer&) -> void;
void serialize(serializer&);
function<auto () -> void> scanline;
private: private:
inline void vcounter_tick(); inline auto vcounterTick() -> void;
struct { struct {
bool interlace; bool interlace;

View File

@ -1,49 +1,49 @@
auto PPU::getVramAddress() -> uint16 { auto PPU::getVramAddress() -> uint16 {
uint16 addr = regs.vram_addr; uint16 address = r.vramAddress;
switch(regs.vram_mapping) { switch(r.vramMapping) {
case 0: break; //direct mapping case 0: break; //direct mapping
case 1: addr = (addr & 0xff00) | ((addr & 0x001f) << 3) | ((addr >> 5) & 7); break; case 1: address = (address & 0xff00) | ((address & 0x001f) << 3) | ((address >> 5) & 7); break;
case 2: addr = (addr & 0xfe00) | ((addr & 0x003f) << 3) | ((addr >> 6) & 7); break; case 2: address = (address & 0xfe00) | ((address & 0x003f) << 3) | ((address >> 6) & 7); break;
case 3: addr = (addr & 0xfc00) | ((addr & 0x007f) << 3) | ((addr >> 7) & 7); break; case 3: address = (address & 0xfc00) | ((address & 0x007f) << 3) | ((address >> 7) & 7); break;
} }
return (addr << 1); return address << 1;
} }
auto PPU::vramRead(uint addr) -> uint8 { auto PPU::vramRead(uint addr) -> uint8 {
uint8 data = 0x00; uint8 data = 0x00;
if(regs.display_disable || vcounter() >= vdisp()) { if(r.displayDisable || vcounter() >= vdisp()) {
data = vram[addr]; data = memory.vram[addr];
debugger.vram_read(addr, data); debugger.vramRead(addr, data);
} }
return data; return data;
} }
auto PPU::vramWrite(uint addr, uint8 data) -> void { auto PPU::vramWrite(uint addr, uint8 data) -> void {
if(regs.display_disable || vcounter() >= vdisp()) { if(r.displayDisable || vcounter() >= vdisp()) {
vram[addr] = data; memory.vram[addr] = data;
debugger.vram_write(addr, data); debugger.vramWrite(addr, data);
} }
} }
auto PPU::oamRead(uint addr) -> uint8 { auto PPU::oamRead(uint addr) -> uint8 {
uint8 data = oam[addr]; uint8 data = memory.oam[addr];
debugger.oam_read(addr, data); debugger.oamRead(addr, data);
return data; return data;
} }
auto PPU::oamWrite(uint addr, uint8 data) -> void { auto PPU::oamWrite(uint addr, uint8 data) -> void {
oam[addr] = data; memory.oam[addr] = data;
sprite.update(addr, data); oam.update(addr, data);
debugger.oam_write(addr, data); debugger.oamWrite(addr, data);
} }
auto PPU::cgramRead(uint addr) -> uint8 { auto PPU::cgramRead(uint addr) -> uint8 {
uint8 data = cgram[addr]; uint8 data = memory.cgram[addr];
debugger.cgram_read(addr, data); debugger.cgramRead(addr, data);
return data; return data;
} }
auto PPU::cgramWrite(uint addr, uint8 data) -> void { auto PPU::cgramWrite(uint addr, uint8 data) -> void {
cgram[addr] = data; memory.cgram[addr] = data;
debugger.cgram_write(addr, data); debugger.cgramWrite(addr, data);
} }

File diff suppressed because it is too large Load Diff

View File

@ -13,13 +13,13 @@ PPU ppu;
#include "serialization.cpp" #include "serialization.cpp"
PPU::PPU() : PPU::PPU() :
bg1(*this, Background::ID::BG1), bg1(Background::ID::BG1),
bg2(*this, Background::ID::BG2), bg2(Background::ID::BG2),
bg3(*this, Background::ID::BG3), bg3(Background::ID::BG3),
bg4(*this, Background::ID::BG4), bg4(Background::ID::BG4) {
sprite(*this), ppu1.version = 1; //allowed values: 1
window(*this), ppu2.version = 3; //allowed values: 1, 2, 3
screen(*this) {
output = new uint32[512 * 512]; output = new uint32[512 * 512];
output += 16 * 512; //overscan offset output += 16 * 512; //overscan offset
} }
@ -62,7 +62,7 @@ auto PPU::main() -> void {
bg3.run(0); bg3.run(0);
bg4.run(0); bg4.run(0);
if(pixel >= 0) { if(pixel >= 0) {
sprite.run(); oam.run();
window.run(); window.run();
screen.run(); screen.run();
} }
@ -70,7 +70,7 @@ auto PPU::main() -> void {
} }
addClocks(14); addClocks(14);
sprite.tilefetch(); oam.tilefetch();
} else { } else {
addClocks(1052 + 14 + 136); addClocks(1052 + 14 + 136);
} }
@ -88,9 +88,9 @@ auto PPU::addClocks(uint clocks) -> void {
} }
auto PPU::power() -> void { auto PPU::power() -> void {
for(auto& n : vram) n = random(0x00); for(auto& n : memory.vram) n = random(0x00);
for(auto& n : oam) n = random(0x00); for(auto& n : memory.oam) n = random(0x00);
for(auto& n : cgram) n = random(0x00); for(auto& n : memory.cgram) n = random(0x00);
} }
auto PPU::reset() -> void { auto PPU::reset() -> void {
@ -102,93 +102,93 @@ auto PPU::reset() -> void {
function<auto (uint24, uint8) -> void> writer{&PPU::write, this}; function<auto (uint24, uint8) -> void> writer{&PPU::write, this};
bus.map(reader, writer, "00-3f,80-bf:2100-213f"); bus.map(reader, writer, "00-3f,80-bf:2100-213f");
regs.ppu1_mdr = random(0xff); ppu1.mdr = random(0xff);
regs.ppu2_mdr = random(0xff); ppu2.mdr = random(0xff);
regs.vram_readbuffer = random(0x0000); latch.vram = random(0x0000);
regs.oam_latchdata = random(0x00); latch.oam = random(0x00);
regs.cgram_latchdata = random(0x00); latch.cgram = random(0x00);
regs.bgofs_latchdata = random(0x00); latch.bgofs = random(0x00);
regs.mode7_latchdata = random(0x00); latch.mode7 = random(0x00);
regs.counters_latched = false; latch.counters = false;
regs.latch_hcounter = 0; latch.hcounter = 0;
regs.latch_vcounter = 0; latch.vcounter = 0;
regs.oam_iaddr = 0x0000; latch.oamAddress = 0x0000;
regs.cgram_iaddr = 0x00; latch.cgramAddress = 0x00;
//$2100 INIDISP //$2100 INIDISP
regs.display_disable = true; r.displayDisable = true;
regs.display_brightness = 0; r.displayBrightness = 0;
//$2102 OAMADDL //$2102 OAMADDL
//$2103 OAMADDH //$2103 OAMADDH
regs.oam_baseaddr = random(0x0000); r.oamBaseAddress = random(0x0000);
regs.oam_addr = random(0x0000); r.oamAddress = random(0x0000);
regs.oam_priority = random(false); r.oamPriority = random(false);
//$2105 BGMODE //$2105 BGMODE
regs.bg3_priority = false; r.bgPriority = false;
regs.bgmode = 0; r.bgMode = 0;
//$210d BG1HOFS //$210d BG1HOFS
regs.mode7_hoffset = random(0x0000); r.hoffsetMode7 = random(0x0000);
//$210e BG1VOFS //$210e BG1VOFS
regs.mode7_voffset = random(0x0000); r.voffsetMode7 = random(0x0000);
//$2115 VMAIN //$2115 VMAIN
regs.vram_incmode = random(1); r.vramIncrementMode = random(1);
regs.vram_mapping = random(0); r.vramMapping = random(0);
regs.vram_incsize = 1; r.vramIncrementSize = 1;
//$2116 VMADDL //$2116 VMADDL
//$2117 VMADDH //$2117 VMADDH
regs.vram_addr = random(0x0000); r.vramAddress = random(0x0000);
//$211a M7SEL //$211a M7SEL
regs.mode7_repeat = random(0); r.repeatMode7 = random(0);
regs.mode7_vflip = random(false); r.vflipMode7 = random(false);
regs.mode7_hflip = random(false); r.hflipMode7 = random(false);
//$211b M7A //$211b M7A
regs.m7a = random(0x0000); r.m7a = random(0x0000);
//$211c M7B //$211c M7B
regs.m7b = random(0x0000); r.m7b = random(0x0000);
//$211d M7C //$211d M7C
regs.m7c = random(0x0000); r.m7c = random(0x0000);
//$211e M7D //$211e M7D
regs.m7d = random(0x0000); r.m7d = random(0x0000);
//$211f M7X //$211f M7X
regs.m7x = random(0x0000); r.m7x = random(0x0000);
//$2120 M7Y //$2120 M7Y
regs.m7y = random(0x0000); r.m7y = random(0x0000);
//$2121 CGADD //$2121 CGADD
regs.cgram_addr = random(0x0000); r.cgramAddress = random(0x0000);
//$2133 SETINI //$2133 SETINI
regs.mode7_extbg = random(false); r.extbg = random(false);
regs.pseudo_hires = random(false); r.pseudoHires = random(false);
regs.overscan = false; r.overscan = false;
regs.interlace = false; r.interlace = false;
//$213c OPHCT //$213c OPHCT
regs.hcounter = 0; r.hcounter = 0;
//$213d OPVCT //$213d OPVCT
regs.vcounter = 0; r.vcounter = 0;
bg1.reset(); bg1.reset();
bg2.reset(); bg2.reset();
bg3.reset(); bg3.reset();
bg4.reset(); bg4.reset();
sprite.reset(); oam.reset();
window.reset(); window.reset();
screen.reset(); screen.reset();
@ -208,7 +208,7 @@ auto PPU::scanline() -> void {
bg2.scanline(); bg2.scanline();
bg3.scanline(); bg3.scanline();
bg4.scanline(); bg4.scanline();
sprite.scanline(); oam.scanline();
window.scanline(); window.scanline();
screen.scanline(); screen.scanline();
@ -218,10 +218,9 @@ auto PPU::scanline() -> void {
} }
auto PPU::frame() -> void { auto PPU::frame() -> void {
sprite.frame(); oam.frame();
display.interlace = r.interlace;
display.interlace = regs.interlace; display.overscan = r.overscan;
display.overscan = regs.overscan;
} }
auto PPU::refresh() -> void { auto PPU::refresh() -> void {

View File

@ -1,7 +1,7 @@
struct PPU : Thread, PPUcounter { struct PPU : Thread, PPUcounter {
alwaysinline auto interlace() const -> bool { return display.interlace; } alwaysinline auto interlace() const -> bool { return display.interlace; }
alwaysinline auto overscan() const -> bool { return display.overscan; } alwaysinline auto overscan() const -> bool { return display.overscan; }
alwaysinline auto vdisp() const -> uint { return !regs.overscan ? 225 : 240; } alwaysinline auto vdisp() const -> uint { return r.overscan ? 240 : 225; }
PPU(); PPU();
~PPU(); ~PPU();
@ -31,14 +31,13 @@ struct PPU : Thread, PPUcounter {
auto latchCounters() -> void; auto latchCounters() -> void;
auto updateVideoMode() -> void; auto updateVideoMode() -> void;
uint8 vram[64 * 1024]; struct {
uint8 oam[544]; uint8 vram[64 * 1024];
uint8 cgram[512]; uint8 oam[544];
uint8 cgram[512];
} memory;
privileged: privileged:
uint ppu1_version = 1; //allowed: 1
uint ppu2_version = 3; //allowed: 1, 2, 3
uint32* output = nullptr; uint32* output = nullptr;
struct { struct {
@ -52,55 +51,59 @@ privileged:
auto frame() -> void; auto frame() -> void;
auto refresh() -> void; auto refresh() -> void;
struct {
uint version;
uint8 mdr;
} ppu1, ppu2;
struct Latches {
uint16 vram;
uint8 oam;
uint8 cgram;
uint8 bgofs;
uint8 mode7;
bool counters;
bool hcounter;
bool vcounter;
uint10 oamAddress;
uint9 cgramAddress;
} latch;
struct Registers { struct Registers {
uint8 ppu1_mdr;
uint8 ppu2_mdr;
uint16 vram_readbuffer;
uint8 oam_latchdata;
uint8 cgram_latchdata;
uint8 bgofs_latchdata;
uint8 mode7_latchdata;
bool counters_latched;
bool latch_hcounter;
bool latch_vcounter;
uint10 oam_iaddr;
uint9 cgram_iaddr;
//$2100 INIDISP //$2100 INIDISP
bool display_disable; bool displayDisable;
uint4 display_brightness; uint4 displayBrightness;
//$2102 OAMADDL //$2102 OAMADDL
//$2103 OAMADDH //$2103 OAMADDH
uint10 oam_baseaddr; uint10 oamBaseAddress;
uint10 oam_addr; uint10 oamAddress;
bool oam_priority; bool oamPriority;
//$2105 BGMODE //$2105 BGMODE
bool bg3_priority; bool bgPriority;
uint8 bgmode; uint8 bgMode;
//$210d BG1HOFS //$210d BG1HOFS
uint16 mode7_hoffset; uint16 hoffsetMode7;
//$210e BG1VOFS //$210e BG1VOFS
uint16 mode7_voffset; uint16 voffsetMode7;
//$2115 VMAIN //$2115 VMAIN
bool vram_incmode; bool vramIncrementMode;
uint2 vram_mapping; uint2 vramMapping;
uint8 vram_incsize; uint8 vramIncrementSize;
//$2116 VMADDL //$2116 VMADDL
//$2117 VMADDH //$2117 VMADDH
uint16 vram_addr; uint16 vramAddress;
//$211a M7SEL //$211a M7SEL
uint2 mode7_repeat; uint2 repeatMode7;
bool mode7_vflip; bool vflipMode7;
bool mode7_hflip; bool hflipMode7;
//$211b M7A //$211b M7A
uint16 m7a; uint16 m7a;
@ -121,11 +124,11 @@ privileged:
uint16 m7y; uint16 m7y;
//$2121 CGADD //$2121 CGADD
uint9 cgram_addr; uint9 cgramAddress;
//$2133 SETINI //$2133 SETINI
bool mode7_extbg; bool extbg;
bool pseudo_hires; bool pseudoHires;
bool overscan; bool overscan;
bool interlace; bool interlace;
@ -134,7 +137,7 @@ privileged:
//$213d OPVCT //$213d OPVCT
uint16 vcounter; uint16 vcounter;
} regs; } r;
#include "background/background.hpp" #include "background/background.hpp"
#include "screen/screen.hpp" #include "screen/screen.hpp"
@ -145,23 +148,23 @@ privileged:
Background bg2; Background bg2;
Background bg3; Background bg3;
Background bg4; Background bg4;
Sprite sprite; OAM oam;
Window window; Window window;
Screen screen; Screen screen;
friend class PPU::Background; friend class PPU::Background;
friend class PPU::Sprite; friend class PPU::OAM;
friend class PPU::Window; friend class PPU::Window;
friend class PPU::Screen; friend class PPU::Screen;
friend class Scheduler; friend class Scheduler;
struct Debugger { struct Debugger {
hook<auto (uint16, uint8) -> void> vram_read; hook<auto (uint16, uint8) -> void> vramRead;
hook<auto (uint16, uint8) -> void> oam_read; hook<auto (uint16, uint8) -> void> oamRead;
hook<auto (uint16, uint8) -> void> cgram_read; hook<auto (uint16, uint8) -> void> cgramRead;
hook<auto (uint16, uint8) -> void> vram_write; hook<auto (uint16, uint8) -> void> vramWrite;
hook<auto (uint16, uint8) -> void> oam_write; hook<auto (uint16, uint8) -> void> oamWrite;
hook<auto (uint16, uint8) -> void> cgram_write; hook<auto (uint16, uint8) -> void> cgramWrite;
} debugger; } debugger;
}; };

View File

@ -1,133 +1,130 @@
PPU::Screen::Screen(PPU& self) : self(self) {
}
auto PPU::Screen::scanline() -> void { auto PPU::Screen::scanline() -> void {
lineA = self.output + self.vcounter() * 1024; lineA = ppu.output + ppu.vcounter() * 1024;
lineB = lineA + (self.display.interlace ? 0 : 512); lineB = lineA + (ppu.display.interlace ? 0 : 512);
if(self.display.interlace && self.field()) lineA += 512, lineB += 512; if(ppu.display.interlace && ppu.field()) lineA += 512, lineB += 512;
//the first hires pixel of each scanline is transparent //the first hires pixel of each scanline is transparent
//note: exact value initializations are not confirmed on hardware //note: exact value initializations are not confirmed on hardware
math.main.color = get_color(0); math.above.color = paletteColor(0);
math.sub.color = math.main.color; math.below.color = math.above.color;
math.main.color_enable = !(self.window.regs.col_main_mask & 1); math.above.colorEnable = !(ppu.window.r.col.aboveMask & 1);
math.sub.color_enable = !(self.window.regs.col_sub_mask & 1) && regs.back_color_enable; math.below.colorEnable = !(ppu.window.r.col.belowMask & 1) && r.back.colorEnable;
math.transparent = true; math.transparent = true;
math.addsub_mode = false; math.blendMode = false;
math.color_halve = regs.color_halve && !regs.addsub_mode && math.main.color_enable; math.colorHalve = r.colorHalve && !r.blendMode && math.above.colorEnable;
} }
auto PPU::Screen::run() -> void { auto PPU::Screen::run() -> void {
if(self.vcounter() == 0) return; if(ppu.vcounter() == 0) return;
bool hires = self.regs.pseudo_hires || self.regs.bgmode == 5 || self.regs.bgmode == 6; bool hires = ppu.r.pseudoHires || ppu.r.bgMode == 5 || ppu.r.bgMode == 6;
auto sscolor = get_pixel_sub(hires); auto belowColor = below(hires);
auto mscolor = get_pixel_main(); auto aboveColor = above();
*lineA++ = *lineB++ = (self.regs.display_brightness << 15) | (hires ? sscolor : mscolor); *lineA++ = *lineB++ = ppu.r.displayBrightness << 15 | (hires ? belowColor : aboveColor);
*lineA++ = *lineB++ = (self.regs.display_brightness << 15) | (mscolor); *lineA++ = *lineB++ = ppu.r.displayBrightness << 15 | (aboveColor);
} }
auto PPU::Screen::get_pixel_sub(bool hires) -> uint16 { auto PPU::Screen::below(bool hires) -> uint16 {
if(self.regs.display_disable || (!self.regs.overscan && self.vcounter() >= 225)) return 0; if(ppu.r.displayDisable || (!ppu.r.overscan && ppu.vcounter() >= 225)) return 0;
uint priority = 0; uint priority = 0;
if(self.bg1.output.sub.priority) { if(ppu.bg1.output.below.priority) {
priority = self.bg1.output.sub.priority; priority = ppu.bg1.output.below.priority;
if(regs.direct_color && (self.regs.bgmode == 3 || self.regs.bgmode == 4 || self.regs.bgmode == 7)) { if(r.directColor && (ppu.r.bgMode == 3 || ppu.r.bgMode == 4 || ppu.r.bgMode == 7)) {
math.sub.color = get_direct_color(self.bg1.output.sub.palette, self.bg1.output.sub.tile); math.below.color = directColor(ppu.bg1.output.below.palette, ppu.bg1.output.below.tile);
} else { } else {
math.sub.color = get_color(self.bg1.output.sub.palette); math.below.color = paletteColor(ppu.bg1.output.below.palette);
} }
} }
if(self.bg2.output.sub.priority > priority) { if(ppu.bg2.output.below.priority > priority) {
priority = self.bg2.output.sub.priority; priority = ppu.bg2.output.below.priority;
math.sub.color = get_color(self.bg2.output.sub.palette); math.below.color = paletteColor(ppu.bg2.output.below.palette);
} }
if(self.bg3.output.sub.priority > priority) { if(ppu.bg3.output.below.priority > priority) {
priority = self.bg3.output.sub.priority; priority = ppu.bg3.output.below.priority;
math.sub.color = get_color(self.bg3.output.sub.palette); math.below.color = paletteColor(ppu.bg3.output.below.palette);
} }
if(self.bg4.output.sub.priority > priority) { if(ppu.bg4.output.below.priority > priority) {
priority = self.bg4.output.sub.priority; priority = ppu.bg4.output.below.priority;
math.sub.color = get_color(self.bg4.output.sub.palette); math.below.color = paletteColor(ppu.bg4.output.below.palette);
} }
if(self.sprite.output.sub.priority > priority) { if(ppu.oam.output.below.priority > priority) {
priority = self.sprite.output.sub.priority; priority = ppu.oam.output.below.priority;
math.sub.color = get_color(self.sprite.output.sub.palette); math.below.color = paletteColor(ppu.oam.output.below.palette);
} }
if(math.transparent = (priority == 0)) math.sub.color = get_color(0); if(math.transparent = (priority == 0)) math.below.color = paletteColor(0);
if(!hires) return 0; if(!hires) return 0;
if(!math.sub.color_enable) return math.main.color_enable ? math.sub.color : (uint16)0; if(!math.below.colorEnable) return math.above.colorEnable ? math.below.color : (uint16)0;
return addsub( return blend(
math.main.color_enable ? math.sub.color : (uint16)0, math.above.colorEnable ? math.below.color : (uint16)0,
math.addsub_mode ? math.main.color : fixed_color() math.blendMode ? math.above.color : fixedColor()
); );
} }
auto PPU::Screen::get_pixel_main() -> uint16 { auto PPU::Screen::above() -> uint16 {
if(self.regs.display_disable || (!self.regs.overscan && self.vcounter() >= 225)) return 0; if(ppu.r.displayDisable || (!ppu.r.overscan && ppu.vcounter() >= 225)) return 0;
uint priority = 0; uint priority = 0;
if(self.bg1.output.main.priority) { if(ppu.bg1.output.above.priority) {
priority = self.bg1.output.main.priority; priority = ppu.bg1.output.above.priority;
if(regs.direct_color && (self.regs.bgmode == 3 || self.regs.bgmode == 4 || self.regs.bgmode == 7)) { if(r.directColor && (ppu.r.bgMode == 3 || ppu.r.bgMode == 4 || ppu.r.bgMode == 7)) {
math.main.color = get_direct_color(self.bg1.output.main.palette, self.bg1.output.main.tile); math.above.color = directColor(ppu.bg1.output.above.palette, ppu.bg1.output.above.tile);
} else { } else {
math.main.color = get_color(self.bg1.output.main.palette); math.above.color = paletteColor(ppu.bg1.output.above.palette);
} }
math.sub.color_enable = regs.bg1_color_enable; math.below.colorEnable = r.bg1.colorEnable;
} }
if(self.bg2.output.main.priority > priority) { if(ppu.bg2.output.above.priority > priority) {
priority = self.bg2.output.main.priority; priority = ppu.bg2.output.above.priority;
math.main.color = get_color(self.bg2.output.main.palette); math.above.color = paletteColor(ppu.bg2.output.above.palette);
math.sub.color_enable = regs.bg2_color_enable; math.below.colorEnable = r.bg2.colorEnable;
} }
if(self.bg3.output.main.priority > priority) { if(ppu.bg3.output.above.priority > priority) {
priority = self.bg3.output.main.priority; priority = ppu.bg3.output.above.priority;
math.main.color = get_color(self.bg3.output.main.palette); math.above.color = paletteColor(ppu.bg3.output.above.palette);
math.sub.color_enable = regs.bg3_color_enable; math.below.colorEnable = r.bg3.colorEnable;
} }
if(self.bg4.output.main.priority > priority) { if(ppu.bg4.output.above.priority > priority) {
priority = self.bg4.output.main.priority; priority = ppu.bg4.output.above.priority;
math.main.color = get_color(self.bg4.output.main.palette); math.above.color = paletteColor(ppu.bg4.output.above.palette);
math.sub.color_enable = regs.bg4_color_enable; math.below.colorEnable = r.bg4.colorEnable;
} }
if(self.sprite.output.main.priority > priority) { if(ppu.oam.output.above.priority > priority) {
priority = self.sprite.output.main.priority; priority = ppu.oam.output.above.priority;
math.main.color = get_color(self.sprite.output.main.palette); math.above.color = paletteColor(ppu.oam.output.above.palette);
math.sub.color_enable = regs.oam_color_enable && self.sprite.output.main.palette >= 192; math.below.colorEnable = r.oam.colorEnable && ppu.oam.output.above.palette >= 192;
} }
if(priority == 0) { if(priority == 0) {
math.main.color = get_color(0); math.above.color = paletteColor(0);
math.sub.color_enable = regs.back_color_enable; math.below.colorEnable = r.back.colorEnable;
} }
if(!self.window.output.sub.color_enable) math.sub.color_enable = false; if(!ppu.window.output.below.colorEnable) math.below.colorEnable = false;
math.main.color_enable = self.window.output.main.color_enable; math.above.colorEnable = ppu.window.output.above.colorEnable;
if(!math.sub.color_enable) return math.main.color_enable ? math.main.color : (uint16)0; if(!math.below.colorEnable) return math.above.colorEnable ? math.above.color : (uint16)0;
if(regs.addsub_mode && math.transparent) { if(r.blendMode && math.transparent) {
math.addsub_mode = false; math.blendMode = false;
math.color_halve = false; math.colorHalve = false;
} else { } else {
math.addsub_mode = regs.addsub_mode; math.blendMode = r.blendMode;
math.color_halve = regs.color_halve && math.main.color_enable; math.colorHalve = r.colorHalve && math.above.colorEnable;
} }
return addsub( return blend(
math.main.color_enable ? math.main.color : (uint16)0, math.above.colorEnable ? math.above.color : (uint16)0,
math.addsub_mode ? math.sub.color : fixed_color() math.blendMode ? math.below.color : fixedColor()
); );
} }
auto PPU::Screen::addsub(uint x, uint y) -> uint16 { auto PPU::Screen::blend(uint x, uint y) const -> uint16 {
if(!regs.color_mode) { if(!r.colorMode) {
if(!math.color_halve) { if(!math.colorHalve) {
uint sum = x + y; uint sum = x + y;
uint carry = (sum - ((x ^ y) & 0x0421)) & 0x8420; uint carry = (sum - ((x ^ y) & 0x0421)) & 0x8420;
return (sum - carry) | (carry - (carry >> 5)); return (sum - carry) | (carry - (carry >> 5));
@ -137,7 +134,7 @@ auto PPU::Screen::addsub(uint x, uint y) -> uint16 {
} else { } else {
uint diff = x - y + 0x8420; uint diff = x - y + 0x8420;
uint borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420; uint borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420;
if(!math.color_halve) { if(!math.colorHalve) {
return (diff - borrow) & (borrow - (borrow >> 5)); return (diff - borrow) & (borrow - (borrow >> 5));
} else { } else {
return (((diff - borrow) & (borrow - (borrow >> 5))) & 0x7bde) >> 1; return (((diff - borrow) & (borrow - (borrow >> 5))) & 0x7bde) >> 1;
@ -145,13 +142,13 @@ auto PPU::Screen::addsub(uint x, uint y) -> uint16 {
} }
} }
auto PPU::Screen::get_color(uint palette) -> uint16 { auto PPU::Screen::paletteColor(uint palette) const -> uint16 {
palette <<= 1; palette <<= 1;
self.regs.cgram_iaddr = palette; ppu.latch.cgramAddress = palette;
return ppu.cgram[palette + 0] + (ppu.cgram[palette + 1] << 8); return ppu.memory.cgram[palette + 0] + (ppu.memory.cgram[palette + 1] << 8);
} }
auto PPU::Screen::get_direct_color(uint palette, uint tile) -> uint16 { auto PPU::Screen::directColor(uint palette, uint tile) const -> uint16 {
//palette = -------- BBGGGRRR //palette = -------- BBGGGRRR
//tile = ---bgr-- -------- //tile = ---bgr-- --------
//output = 0BBb00GG Gg0RRRr0 //output = 0BBb00GG Gg0RRRr0
@ -160,22 +157,22 @@ auto PPU::Screen::get_direct_color(uint palette, uint tile) -> uint16 {
+ ((palette << 2) & 0x001c) + ((tile >> 9) & 0x0002); + ((palette << 2) & 0x001c) + ((tile >> 9) & 0x0002);
} }
auto PPU::Screen::fixed_color() const -> uint16 { auto PPU::Screen::fixedColor() const -> uint16 {
return (regs.color_b << 10) | (regs.color_g << 5) | (regs.color_r << 0); return r.colorBlue << 10 | r.colorGreen << 5 | r.colorRed << 0;
} }
auto PPU::Screen::reset() -> void { auto PPU::Screen::reset() -> void {
regs.addsub_mode = random(false); r.blendMode = random(false);
regs.direct_color = random(false); r.directColor = random(false);
regs.color_mode = random(false); r.colorMode = random(false);
regs.color_halve = random(false); r.colorHalve = random(false);
regs.bg1_color_enable = random(false); r.bg1.colorEnable = random(false);
regs.bg2_color_enable = random(false); r.bg2.colorEnable = random(false);
regs.bg3_color_enable = random(false); r.bg3.colorEnable = random(false);
regs.bg4_color_enable = random(false); r.bg4.colorEnable = random(false);
regs.oam_color_enable = random(false); r.oam.colorEnable = random(false);
regs.back_color_enable = random(false); r.back.colorEnable = random(false);
regs.color_r = random(0); r.colorBlue = random(0);
regs.color_g = random(0); r.colorGreen = random(0);
regs.color_b = random(0); r.colorRed = random(0);
} }

View File

@ -1,50 +1,45 @@
struct Screen { struct Screen {
uint32* lineA;
uint32* lineB;
struct Regs {
bool addsub_mode;
bool direct_color;
bool color_mode;
bool color_halve;
bool bg1_color_enable;
bool bg2_color_enable;
bool bg3_color_enable;
bool bg4_color_enable;
bool oam_color_enable;
bool back_color_enable;
uint5 color_b;
uint5 color_g;
uint5 color_r;
} regs;
struct Math {
struct Layer {
uint16 color;
bool color_enable;
} main, sub;
bool transparent;
bool addsub_mode;
bool color_halve;
} math;
Screen(PPU& self);
auto scanline() -> void; auto scanline() -> void;
alwaysinline auto run() -> void; alwaysinline auto run() -> void;
auto reset() -> void; auto reset() -> void;
auto get_pixel_sub(bool hires) -> uint16; auto below(bool hires) -> uint16;
auto get_pixel_main() -> uint16; auto above() -> uint16;
auto addsub(uint x, uint y) -> uint16;
alwaysinline auto get_color(uint palette) -> uint16; auto blend(uint x, uint y) const -> uint16;
alwaysinline auto get_direct_color(uint palette, uint tile) -> uint16; alwaysinline auto paletteColor(uint palette) const -> uint16;
alwaysinline auto fixed_color() const -> uint16; alwaysinline auto directColor(uint palette, uint tile) const -> uint16;
alwaysinline auto fixedColor() const -> uint16;
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
PPU& self; uint32* lineA;
uint32* lineB;
struct Registers {
bool blendMode;
bool directColor;
bool colorMode;
bool colorHalve;
struct Layer {
bool colorEnable;
} bg1, bg2, bg3, bg4, oam, back;
uint5 colorBlue;
uint5 colorGreen;
uint5 colorRed;
} r;
struct Math {
struct Screen {
uint16 color;
bool colorEnable;
} above, below;
bool transparent;
bool blendMode;
bool colorHalve;
} math;
friend class PPU; friend class PPU;
}; };

View File

@ -14,107 +14,106 @@ auto PPU::serialize(serializer& s) -> void {
Thread::serialize(s); Thread::serialize(s);
PPUcounter::serialize(s); PPUcounter::serialize(s);
s.array(vram); s.array(memory.vram);
s.array(oam); s.array(memory.oam);
s.array(cgram); s.array(memory.cgram);
s.integer(ppu1_version); s.integer(ppu1.version);
s.integer(ppu2_version); s.integer(ppu1.mdr);
s.integer(ppu2.version);
s.integer(ppu2.mdr);
s.integer(display.interlace); s.integer(display.interlace);
s.integer(display.overscan); s.integer(display.overscan);
s.integer(regs.ppu1_mdr); s.integer(latch.vram);
s.integer(regs.ppu2_mdr); s.integer(latch.oam);
s.integer(latch.cgram);
s.integer(latch.bgofs);
s.integer(latch.mode7);
s.integer(latch.counters);
s.integer(latch.hcounter);
s.integer(latch.vcounter);
s.integer(regs.vram_readbuffer); s.integer(latch.oamAddress);
s.integer(regs.oam_latchdata); s.integer(latch.cgramAddress);
s.integer(regs.cgram_latchdata);
s.integer(regs.bgofs_latchdata);
s.integer(regs.mode7_latchdata);
s.integer(regs.counters_latched);
s.integer(regs.latch_hcounter);
s.integer(regs.latch_vcounter);
s.integer(regs.oam_iaddr); s.integer(r.displayDisable);
s.integer(regs.cgram_iaddr); s.integer(r.displayBrightness);
s.integer(regs.display_disable); s.integer(r.oamBaseAddress);
s.integer(regs.display_brightness); s.integer(r.oamAddress);
s.integer(r.oamPriority);
s.integer(regs.oam_baseaddr); s.integer(r.bgPriority);
s.integer(regs.oam_addr); s.integer(r.bgMode);
s.integer(regs.oam_priority);
s.integer(regs.bg3_priority); s.integer(r.hoffsetMode7);
s.integer(regs.bgmode); s.integer(r.voffsetMode7);
s.integer(regs.mode7_hoffset); s.integer(r.vramIncrementMode);
s.integer(regs.mode7_voffset); s.integer(r.vramMapping);
s.integer(r.vramIncrementSize);
s.integer(regs.vram_incmode); s.integer(r.vramAddress);
s.integer(regs.vram_mapping);
s.integer(regs.vram_incsize);
s.integer(regs.vram_addr); s.integer(r.repeatMode7);
s.integer(r.vflipMode7);
s.integer(r.hflipMode7);
s.integer(regs.mode7_repeat); s.integer(r.m7a);
s.integer(regs.mode7_vflip); s.integer(r.m7b);
s.integer(regs.mode7_hflip); s.integer(r.m7c);
s.integer(r.m7d);
s.integer(r.m7x);
s.integer(r.m7y);
s.integer(regs.m7a); s.integer(r.cgramAddress);
s.integer(regs.m7b);
s.integer(regs.m7c);
s.integer(regs.m7d);
s.integer(regs.m7x);
s.integer(regs.m7y);
s.integer(regs.cgram_addr); s.integer(r.extbg);
s.integer(r.pseudoHires);
s.integer(r.overscan);
s.integer(r.interlace);
s.integer(regs.mode7_extbg); s.integer(r.hcounter);
s.integer(regs.pseudo_hires); s.integer(r.vcounter);
s.integer(regs.overscan);
s.integer(regs.interlace);
s.integer(regs.hcounter);
s.integer(regs.vcounter);
bg1.serialize(s); bg1.serialize(s);
bg2.serialize(s); bg2.serialize(s);
bg3.serialize(s); bg3.serialize(s);
bg4.serialize(s); bg4.serialize(s);
sprite.serialize(s); oam.serialize(s);
window.serialize(s); window.serialize(s);
screen.serialize(s); screen.serialize(s);
} }
auto PPU::Background::serialize(serializer& s) -> void { auto PPU::Background::serialize(serializer& s) -> void {
s.integer(regs.tiledata_addr); s.integer(r.tiledataAddress);
s.integer(regs.screen_addr); s.integer(r.screenAddress);
s.integer(regs.screen_size); s.integer(r.screenSize);
s.integer(regs.mosaic); s.integer(r.mosaic);
s.integer(regs.tile_size); s.integer(r.tileSize);
s.integer(regs.mode); s.integer(r.mode);
s.integer(regs.priority0); s.array(r.priority);
s.integer(regs.priority1);
s.integer(regs.main_enable); s.integer(r.aboveEnable);
s.integer(regs.sub_enable); s.integer(r.belowEnable);
s.integer(regs.hoffset); s.integer(r.hoffset);
s.integer(regs.voffset); s.integer(r.voffset);
s.integer(cache.hoffset); s.integer(latch.hoffset);
s.integer(cache.voffset); s.integer(latch.voffset);
s.integer(output.main.priority); s.integer(output.above.priority);
s.integer(output.main.palette); s.integer(output.above.palette);
s.integer(output.main.tile); s.integer(output.above.tile);
s.integer(output.sub.priority); s.integer(output.below.priority);
s.integer(output.sub.palette); s.integer(output.below.palette);
s.integer(output.sub.tile); s.integer(output.below.tile);
s.integer(x); s.integer(x);
s.integer(y); s.integer(y);
@ -128,32 +127,34 @@ auto PPU::Background::serialize(serializer& s) -> void {
s.integer(mosaic.hcounter); s.integer(mosaic.hcounter);
s.integer(mosaic.hoffset); s.integer(mosaic.hoffset);
s.integer(tile_counter); s.integer(tileCounter);
s.integer(tile); s.integer(tile);
s.integer(priority); s.integer(priority);
s.integer(palette_number); s.integer(paletteNumber);
s.integer(palette_index); s.integer(paletteIndex);
s.array(data); s.array(data);
} }
auto PPU::Sprite::serialize(serializer& s) -> void { auto PPU::OAM::serialize(serializer& s) -> void {
for(auto n : range(128)) { s.integer(r.aboveEnable);
s.integer(list[n].x); s.integer(r.belowEnable);
s.integer(list[n].y); s.integer(r.interlace);
s.integer(list[n].character);
s.integer(list[n].nameselect); s.integer(r.baseSize);
s.integer(list[n].vflip); s.integer(r.nameSelect);
s.integer(list[n].hflip); s.integer(r.tiledataAddress);
s.integer(list[n].priority); s.integer(r.firstSprite);
s.integer(list[n].palette);
s.integer(list[n].size); s.array(r.priority);
}
s.integer(r.timeOver);
s.integer(r.rangeOver);
s.integer(t.x); s.integer(t.x);
s.integer(t.y); s.integer(t.y);
s.integer(t.item_count); s.integer(t.itemCount);
s.integer(t.tile_count); s.integer(t.tileCount);
s.integer(t.active); s.integer(t.active);
for(auto p : range(2)) { for(auto p : range(2)) {
@ -167,124 +168,111 @@ auto PPU::Sprite::serialize(serializer& s) -> void {
s.integer(t.tile[p][n].priority); s.integer(t.tile[p][n].priority);
s.integer(t.tile[p][n].palette); s.integer(t.tile[p][n].palette);
s.integer(t.tile[p][n].hflip); s.integer(t.tile[p][n].hflip);
s.integer(t.tile[p][n].d0); s.integer(t.tile[p][n].data);
s.integer(t.tile[p][n].d1);
s.integer(t.tile[p][n].d2);
s.integer(t.tile[p][n].d3);
} }
} }
s.integer(regs.main_enable); s.integer(output.above.priority);
s.integer(regs.sub_enable); s.integer(output.above.palette);
s.integer(regs.interlace);
s.integer(regs.base_size); s.integer(output.below.priority);
s.integer(regs.nameselect); s.integer(output.below.palette);
s.integer(regs.tiledata_addr);
s.integer(regs.first_sprite);
s.integer(regs.priority0); for(auto n : range(128)) {
s.integer(regs.priority1); s.integer(list[n].x);
s.integer(regs.priority2); s.integer(list[n].y);
s.integer(regs.priority3); s.integer(list[n].character);
s.integer(list[n].nameSelect);
s.integer(regs.time_over); s.integer(list[n].vflip);
s.integer(regs.range_over); s.integer(list[n].hflip);
s.integer(list[n].priority);
s.integer(output.main.priority); s.integer(list[n].palette);
s.integer(output.main.palette); s.integer(list[n].size);
}
s.integer(output.sub.priority);
s.integer(output.sub.palette);
} }
auto PPU::Window::serialize(serializer& s) -> void { auto PPU::Window::serialize(serializer& s) -> void {
s.integer(regs.bg1_one_enable); s.integer(r.bg1.oneEnable);
s.integer(regs.bg1_one_invert); s.integer(r.bg1.oneInvert);
s.integer(regs.bg1_two_enable); s.integer(r.bg1.twoEnable);
s.integer(regs.bg1_two_invert); s.integer(r.bg1.twoInvert);
s.integer(r.bg1.mask);
s.integer(r.bg1.aboveEnable);
s.integer(r.bg1.belowEnable);
s.integer(regs.bg2_one_enable); s.integer(r.bg2.oneEnable);
s.integer(regs.bg2_one_invert); s.integer(r.bg2.oneInvert);
s.integer(regs.bg2_two_enable); s.integer(r.bg2.twoEnable);
s.integer(regs.bg2_two_invert); s.integer(r.bg2.twoInvert);
s.integer(r.bg2.mask);
s.integer(r.bg2.aboveEnable);
s.integer(r.bg2.belowEnable);
s.integer(regs.bg3_one_enable); s.integer(r.bg3.oneEnable);
s.integer(regs.bg3_one_invert); s.integer(r.bg3.oneInvert);
s.integer(regs.bg3_two_enable); s.integer(r.bg3.twoEnable);
s.integer(regs.bg3_two_invert); s.integer(r.bg3.twoInvert);
s.integer(r.bg3.mask);
s.integer(r.bg3.aboveEnable);
s.integer(r.bg3.belowEnable);
s.integer(regs.bg4_one_enable); s.integer(r.bg4.oneEnable);
s.integer(regs.bg4_one_invert); s.integer(r.bg4.oneInvert);
s.integer(regs.bg4_two_enable); s.integer(r.bg4.twoEnable);
s.integer(regs.bg4_two_invert); s.integer(r.bg4.twoInvert);
s.integer(r.bg4.mask);
s.integer(r.bg4.aboveEnable);
s.integer(r.bg4.belowEnable);
s.integer(regs.oam_one_enable); s.integer(r.oam.oneEnable);
s.integer(regs.oam_one_invert); s.integer(r.oam.oneInvert);
s.integer(regs.oam_two_enable); s.integer(r.oam.twoEnable);
s.integer(regs.oam_two_invert); s.integer(r.oam.twoInvert);
s.integer(r.oam.mask);
s.integer(r.oam.aboveEnable);
s.integer(r.oam.belowEnable);
s.integer(regs.col_one_enable); s.integer(r.col.oneEnable);
s.integer(regs.col_one_invert); s.integer(r.col.oneInvert);
s.integer(regs.col_two_enable); s.integer(r.col.twoEnable);
s.integer(regs.col_two_invert); s.integer(r.col.twoInvert);
s.integer(r.col.mask);
s.integer(r.col.aboveMask);
s.integer(r.col.belowMask);
s.integer(regs.one_left); s.integer(r.oneLeft);
s.integer(regs.one_right); s.integer(r.oneRight);
s.integer(regs.two_left); s.integer(r.twoLeft);
s.integer(regs.two_right); s.integer(r.twoRight);
s.integer(regs.bg1_mask); s.integer(output.above.colorEnable);
s.integer(regs.bg2_mask); s.integer(output.below.colorEnable);
s.integer(regs.bg3_mask);
s.integer(regs.bg4_mask);
s.integer(regs.oam_mask);
s.integer(regs.col_mask);
s.integer(regs.bg1_main_enable);
s.integer(regs.bg1_sub_enable);
s.integer(regs.bg2_main_enable);
s.integer(regs.bg2_sub_enable);
s.integer(regs.bg3_main_enable);
s.integer(regs.bg3_sub_enable);
s.integer(regs.bg4_main_enable);
s.integer(regs.bg4_sub_enable);
s.integer(regs.oam_main_enable);
s.integer(regs.oam_sub_enable);
s.integer(regs.col_main_mask);
s.integer(regs.col_sub_mask);
s.integer(output.main.color_enable);
s.integer(output.sub.color_enable);
s.integer(x); s.integer(x);
s.integer(one);
s.integer(two);
} }
auto PPU::Screen::serialize(serializer& s) -> void { auto PPU::Screen::serialize(serializer& s) -> void {
s.integer(regs.addsub_mode); s.integer(r.blendMode);
s.integer(regs.direct_color); s.integer(r.directColor);
s.integer(regs.color_mode); s.integer(r.colorMode);
s.integer(regs.color_halve); s.integer(r.colorHalve);
s.integer(regs.bg1_color_enable); s.integer(r.bg1.colorEnable);
s.integer(regs.bg2_color_enable); s.integer(r.bg2.colorEnable);
s.integer(regs.bg3_color_enable); s.integer(r.bg3.colorEnable);
s.integer(regs.bg4_color_enable); s.integer(r.bg4.colorEnable);
s.integer(regs.oam_color_enable); s.integer(r.oam.colorEnable);
s.integer(regs.back_color_enable); s.integer(r.back.colorEnable);
s.integer(regs.color_b); s.integer(r.colorBlue);
s.integer(regs.color_g); s.integer(r.colorGreen);
s.integer(regs.color_r); s.integer(r.colorRed);
s.integer(math.main.color); s.integer(math.above.color);
s.integer(math.main.color_enable); s.integer(math.above.colorEnable);
s.integer(math.sub.color); s.integer(math.below.color);
s.integer(math.sub.color_enable); s.integer(math.below.colorEnable);
s.integer(math.transparent); s.integer(math.transparent);
s.integer(math.addsub_mode); s.integer(math.blendMode);
s.integer(math.color_halve); s.integer(math.colorHalve);
} }

View File

@ -1,4 +1,4 @@
auto PPU::Sprite::update(uint10 addr, uint8 data) -> void { auto PPU::OAM::update(uint10 addr, uint8 data) -> void {
if(!addr.bit(9)) { if(!addr.bit(9)) {
uint n = addr >> 2; //sprite# uint n = addr >> 2; //sprite#
addr &= 3; addr &= 3;
@ -9,11 +9,11 @@ auto PPU::Sprite::update(uint10 addr, uint8 data) -> void {
} else if(addr == 2) { } else if(addr == 2) {
list[n].character = data; list[n].character = data;
} else { //(addr == 3) } else { //(addr == 3)
list[n].vflip = data.bit (7); list[n].nameSelect = data.bit (0);
list[n].palette = data.bits(1,3);
list[n].priority = data.bits(4,5);
list[n].hflip = data.bit (6); list[n].hflip = data.bit (6);
list[n].priority = data.bits(5,4); list[n].vflip = data.bit (7);
list[n].palette = data.bits(3,1);
list[n].nameselect = data.bit (0);
} }
} else { } else {
uint n = (addr & 0x1f) << 2; //sprite# uint n = (addr & 0x1f) << 2; //sprite#
@ -28,27 +28,27 @@ auto PPU::Sprite::update(uint10 addr, uint8 data) -> void {
} }
} }
auto PPU::Sprite::synchronize() -> void { auto PPU::OAM::synchronize() -> void {
for(auto n : range(544)) update(n, ppu.oam[n]); for(auto n : range(544)) update(n, ppu.memory.oam[n]);
} }
auto PPU::Sprite::Object::width() const -> uint{ auto PPU::OAM::Object::width() const -> uint{
if(size == 0) { if(size == 0) {
static uint width[] = { 8, 8, 8, 16, 16, 32, 16, 16}; static const uint width[] = { 8, 8, 8, 16, 16, 32, 16, 16};
return width[ppu.sprite.regs.base_size]; return width[ppu.oam.r.baseSize];
} else { } else {
static uint width[] = {16, 32, 64, 32, 64, 64, 32, 32}; static const uint width[] = {16, 32, 64, 32, 64, 64, 32, 32};
return width[ppu.sprite.regs.base_size]; return width[ppu.oam.r.baseSize];
} }
} }
auto PPU::Sprite::Object::height() const -> uint { auto PPU::OAM::Object::height() const -> uint {
if(size == 0) { if(size == 0) {
if(ppu.sprite.regs.interlace && ppu.sprite.regs.base_size >= 6) return 16; if(ppu.oam.r.interlace && ppu.oam.r.baseSize >= 6) return 16; //hardware quirk
static uint height[] = { 8, 8, 8, 16, 16, 32, 32, 32}; static const uint height[] = { 8, 8, 8, 16, 16, 32, 32, 32};
return height[ppu.sprite.regs.base_size]; return height[ppu.oam.r.baseSize];
} else { } else {
static uint height[] = {16, 32, 64, 32, 64, 64, 64, 32}; static const uint height[] = {16, 32, 64, 32, 64, 64, 64, 32};
return height[ppu.sprite.regs.base_size]; return height[ppu.oam.r.baseSize];
} }
} }

View File

@ -1,107 +1,102 @@
#include "list.cpp" #include "list.cpp"
PPU::Sprite::Sprite(PPU& self) : self(self) { auto PPU::OAM::addressReset() -> void {
ppu.r.oamAddress = ppu.r.oamBaseAddress;
setFirstSprite();
} }
auto PPU::Sprite::address_reset() -> void { auto PPU::OAM::setFirstSprite() -> void {
self.regs.oam_addr = self.regs.oam_baseaddr; r.firstSprite = !ppu.r.oamPriority ? 0 : ppu.r.oamAddress >> 2;
set_first_sprite();
} }
auto PPU::Sprite::set_first_sprite() -> void { auto PPU::OAM::frame() -> void {
regs.first_sprite = !self.regs.oam_priority ? 0 : self.regs.oam_addr >> 2; r.timeOver = false;
r.rangeOver = false;
} }
auto PPU::Sprite::frame() -> void { auto PPU::OAM::scanline() -> void {
regs.time_over = false;
regs.range_over = false;
}
auto PPU::Sprite::scanline() -> void {
t.x = 0; t.x = 0;
t.y = self.vcounter(); t.y = ppu.vcounter();
t.item_count = 0; t.itemCount = 0;
t.tile_count = 0; t.tileCount = 0;
t.active = !t.active; t.active = !t.active;
auto oam_item = t.item[t.active]; auto oamItem = t.item[t.active];
auto oam_tile = t.tile[t.active]; auto oamTile = t.tile[t.active];
if(t.y == self.vdisp() && !self.regs.display_disable) address_reset(); if(t.y == ppu.vdisp() && !ppu.r.displayDisable) addressReset();
if(t.y >= self.vdisp() - 1) return; if(t.y >= ppu.vdisp() - 1) return;
for(auto n : range(32)) oam_item[n].valid = false; //default to invalid for(auto n : range(32)) oamItem[n].valid = false; //default to invalid
for(auto n : range(34)) oam_tile[n].valid = false; //default to invalid for(auto n : range(34)) oamTile[n].valid = false; //default to invalid
for(auto n : range(128)) { for(auto n : range(128)) {
uint7 sprite = regs.first_sprite + n; uint7 sprite = r.firstSprite + n;
if(!on_scanline(list[sprite])) continue; if(!onScanline(list[sprite])) continue;
if(t.item_count++ >= 32) break; if(t.itemCount++ >= 32) break;
oam_item[t.item_count - 1] = {true, sprite}; oamItem[t.itemCount - 1] = {true, sprite};
} }
if(t.item_count > 0 && oam_item[t.item_count - 1].valid) { if(t.itemCount > 0 && oamItem[t.itemCount - 1].valid) {
ppu.regs.oam_iaddr = 0x0200 + (oam_item[t.item_count - 1].index >> 2); ppu.latch.oamAddress = 0x0200 + (oamItem[t.itemCount - 1].index >> 2);
} }
} }
auto PPU::Sprite::on_scanline(Object& sprite) -> bool { auto PPU::OAM::onScanline(Object& sprite) -> bool {
if(sprite.x > 256 && (sprite.x + sprite.width() - 1) < 512) return false; if(sprite.x > 256 && (sprite.x + sprite.width() - 1) < 512) return false;
int height = sprite.height() >> regs.interlace; int height = sprite.height() >> r.interlace;
if(t.y >= sprite.y && t.y < (sprite.y + height)) return true; if(t.y >= sprite.y && t.y < (sprite.y + height)) return true;
if((sprite.y + height) >= 256 && t.y < ((sprite.y + height) & 255)) return true; if((sprite.y + height) >= 256 && t.y < ((sprite.y + height) & 255)) return true;
return false; return false;
} }
auto PPU::Sprite::run() -> void { auto PPU::OAM::run() -> void {
output.main.priority = 0; output.above.priority = 0;
output.sub.priority = 0; output.below.priority = 0;
auto oam_tile = t.tile[!t.active]; auto oamTile = t.tile[!t.active];
uint priority_table[] = {regs.priority0, regs.priority1, regs.priority2, regs.priority3};
uint x = t.x++; uint x = t.x++;
for(auto n : range(34)) { for(auto n : range(34)) {
auto tile = oam_tile[n]; const auto& tile = oamTile[n];
if(!tile.valid) break; if(!tile.valid) break;
int px = x - sclip<9>(tile.x); int px = x - (int9)tile.x;
if(px & ~7) continue; if(px & ~7) continue;
uint mask = 0x80 >> (!tile.hflip ? px : 7 - px); uint color = 0, shift = tile.hflip ? px : 7 - px;
uint color; color += tile.data >> (shift + 0) & 1;
color = ((bool)(tile.d0 & mask)) << 0; color += tile.data >> (shift + 7) & 2;
color |= ((bool)(tile.d1 & mask)) << 1; color += tile.data >> (shift + 14) & 4;
color |= ((bool)(tile.d2 & mask)) << 2; color += tile.data >> (shift + 21) & 8;
color |= ((bool)(tile.d3 & mask)) << 3;
if(color) { if(color) {
if(regs.main_enable) { if(r.aboveEnable) {
output.main.palette = tile.palette + color; output.above.palette = tile.palette + color;
output.main.priority = priority_table[tile.priority]; output.above.priority = r.priority[tile.priority];
} }
if(regs.sub_enable) { if(r.belowEnable) {
output.sub.palette = tile.palette + color; output.below.palette = tile.palette + color;
output.sub.priority = priority_table[tile.priority]; output.below.priority = r.priority[tile.priority];
} }
} }
} }
} }
auto PPU::Sprite::tilefetch() -> void { auto PPU::OAM::tilefetch() -> void {
auto oam_item = t.item[t.active]; auto oamItem = t.item[t.active];
auto oam_tile = t.tile[t.active]; auto oamTile = t.tile[t.active];
for(int i = 31; i >= 0; i--) { for(int i = 31; i >= 0; i--) {
if(!oam_item[i].valid) continue; if(!oamItem[i].valid) continue;
auto sprite = list[oam_item[i].index]; const auto& sprite = list[oamItem[i].index];
uint tile_width = sprite.width() >> 3; uint tileWidth = sprite.width() >> 3;
int x = sprite.x; int x = sprite.x;
int y = (t.y - sprite.y) & 0xff; int y = (t.y - sprite.y) & 0xff;
if(regs.interlace) y <<= 1; if(r.interlace) y <<= 1;
if(sprite.vflip) { if(sprite.vflip) {
if(sprite.width() == sprite.height()) { if(sprite.width() == sprite.height()) {
@ -113,60 +108,60 @@ auto PPU::Sprite::tilefetch() -> void {
} }
} }
if(regs.interlace) { if(r.interlace) {
y = !sprite.vflip ? y + self.field() : y - self.field(); y = !sprite.vflip ? y + ppu.field() : y - ppu.field();
} }
x &= 511; x &= 511;
y &= 255; y &= 255;
uint16 tiledata_addr = regs.tiledata_addr; uint16 tiledataAddress = r.tiledataAddress;
uint16 chrx = (sprite.character >> 0) & 15; uint16 chrx = (sprite.character >> 0) & 15;
uint16 chry = (sprite.character >> 4) & 15; uint16 chry = (sprite.character >> 4) & 15;
if(sprite.nameselect) { if(sprite.nameSelect) {
tiledata_addr += (256 * 32) + (regs.nameselect << 13); tiledataAddress += (256 * 32) + (r.nameSelect << 13);
} }
chry += (y >> 3); chry += (y >> 3);
chry &= 15; chry &= 15;
chry <<= 4; chry <<= 4;
for(uint tx = 0; tx < tile_width; tx++) { for(uint tx : range(tileWidth)) {
uint sx = (x + (tx << 3)) & 511; uint sx = (x + (tx << 3)) & 511;
if(x != 256 && sx >= 256 && (sx + 7) < 512) continue; if(x != 256 && sx >= 256 && (sx + 7) < 512) continue;
if(t.tile_count++ >= 34) break; if(t.tileCount++ >= 34) break;
uint n = t.tile_count - 1; uint n = t.tileCount - 1;
oam_tile[n].valid = true; oamTile[n].valid = true;
oam_tile[n].x = sx; oamTile[n].x = sx;
oam_tile[n].priority = sprite.priority; oamTile[n].priority = sprite.priority;
oam_tile[n].palette = 128 + (sprite.palette << 4); oamTile[n].palette = 128 + (sprite.palette << 4);
oam_tile[n].hflip = sprite.hflip; oamTile[n].hflip = sprite.hflip;
uint mx = !sprite.hflip ? tx : (tile_width - 1) - tx; uint mx = !sprite.hflip ? tx : (tileWidth - 1) - tx;
uint pos = tiledata_addr + ((chry + ((chrx + mx) & 15)) << 5); uint pos = tiledataAddress + ((chry + ((chrx + mx) & 15)) << 5);
uint16 addr = (pos & 0xffe0) + ((y & 7) * 2); uint16 addr = (pos & 0xffe0) + ((y & 7) * 2);
oam_tile[n].d0 = ppu.vram[addr + 0]; oamTile[n].data.byte(0) = ppu.memory.vram[addr + 0];
oam_tile[n].d1 = ppu.vram[addr + 1]; oamTile[n].data.byte(1) = ppu.memory.vram[addr + 1];
self.addClocks(2); ppu.addClocks(2);
oam_tile[n].d2 = ppu.vram[addr + 16]; oamTile[n].data.byte(2) = ppu.memory.vram[addr + 16];
oam_tile[n].d3 = ppu.vram[addr + 17]; oamTile[n].data.byte(3) = ppu.memory.vram[addr + 17];
self.addClocks(2); ppu.addClocks(2);
} }
} }
if(t.tile_count < 34) self.addClocks((34 - t.tile_count) * 4); if(t.tileCount < 34) ppu.addClocks((34 - t.tileCount) * 4);
regs.time_over |= (t.tile_count > 34); r.timeOver |= (t.tileCount > 34);
regs.range_over |= (t.item_count > 32); r.rangeOver |= (t.itemCount > 32);
} }
auto PPU::Sprite::reset() -> void { auto PPU::OAM::reset() -> void {
for(auto n : range(128)) { for(auto n : range(128)) {
list[n].x = 0; list[n].x = 0;
list[n].y = 0; list[n].y = 0;
list[n].character = 0; list[n].character = 0;
list[n].nameselect = 0; list[n].nameSelect = 0;
list[n].vflip = 0; list[n].vflip = 0;
list[n].hflip = 0; list[n].hflip = 0;
list[n].priority = 0; list[n].priority = 0;
@ -178,8 +173,8 @@ auto PPU::Sprite::reset() -> void {
t.x = 0; t.x = 0;
t.y = 0; t.y = 0;
t.item_count = 0; t.itemCount = 0;
t.tile_count = 0; t.tileCount = 0;
t.active = 0; t.active = 0;
for(auto p : range(2)) { for(auto p : range(2)) {
@ -193,32 +188,26 @@ auto PPU::Sprite::reset() -> void {
t.tile[p][n].priority = 0; t.tile[p][n].priority = 0;
t.tile[p][n].palette = 0; t.tile[p][n].palette = 0;
t.tile[p][n].hflip = 0; t.tile[p][n].hflip = 0;
t.tile[p][n].d0 = 0; t.tile[p][n].data = 0;
t.tile[p][n].d1 = 0;
t.tile[p][n].d2 = 0;
t.tile[p][n].d3 = 0;
} }
} }
regs.main_enable = random(false); r.aboveEnable = random(false);
regs.sub_enable = random(false); r.belowEnable = random(false);
regs.interlace = random(false); r.interlace = random(false);
regs.base_size = random(0); r.baseSize = random(0);
regs.nameselect = random(0); r.nameSelect = random(0);
regs.tiledata_addr = (random(0x0000) & 3) << 14; r.tiledataAddress = (random(0x0000) & 3) << 14;
regs.first_sprite = 0; r.firstSprite = 0;
regs.priority0 = 0; for(auto& p : r.priority) p = 0;
regs.priority1 = 0;
regs.priority2 = 0;
regs.priority3 = 0;
regs.time_over = false; r.timeOver = false;
regs.range_over = false; r.rangeOver = false;
output.main.palette = 0; output.above.palette = 0;
output.main.priority = 0; output.above.priority = 0;
output.sub.palette = 0; output.below.palette = 0;
output.sub.priority = 0; output.below.priority = 0;
} }

View File

@ -1,17 +1,36 @@
struct Sprite { struct OAM {
struct Object { alwaysinline auto addressReset() -> void;
uint9 x; alwaysinline auto setFirstSprite() -> void;
uint8 y; auto frame() -> void;
uint8 character; auto scanline() -> void;
uint1 nameselect; auto run() -> void;
uint1 vflip; auto tilefetch() -> void;
uint1 hflip; auto reset() -> void;
uint2 priority;
uint3 palette; struct Object;
uint1 size; auto onScanline(Object&) -> bool;
alwaysinline auto width() const -> uint;
alwaysinline auto height() const -> uint; //list.cpp
} list[128]; auto update(uint10 addr, uint8 data) -> void;
auto synchronize() -> void;
auto serialize(serializer&) -> void;
struct Registers {
bool aboveEnable;
bool belowEnable;
bool interlace;
uint3 baseSize;
uint2 nameSelect;
uint16 tiledataAddress;
uint7 firstSprite;
uint priority[4];
bool timeOver;
bool rangeOver;
} r;
struct Item { struct Item {
bool valid; bool valid;
@ -19,71 +38,47 @@ struct Sprite {
}; };
struct Tile { struct Tile {
bool valid; bool valid;
uint9 x; uint9 x;
uint2 priority; uint2 priority;
uint8 palette; uint8 palette;
uint1 hflip; uint1 hflip;
uint8 d0, d1, d2, d3; uint32 data;
}; };
struct State { struct State {
uint x; uint x;
uint y; uint y;
uint item_count; uint itemCount;
uint tile_count; uint tileCount;
bool active; bool active;
Item item[2][32]; Item item[2][32];
Tile tile[2][34]; Tile tile[2][34];
} t; } t;
struct Regs {
bool main_enable;
bool sub_enable;
bool interlace;
uint3 base_size;
uint2 nameselect;
uint16 tiledata_addr;
uint7 first_sprite;
uint priority0;
uint priority1;
uint priority2;
uint priority3;
bool time_over;
bool range_over;
} regs;
struct Output { struct Output {
struct Pixel { struct Pixel {
uint priority; //0 = none (transparent) uint priority; //0 = none (transparent)
uint8 palette; uint8 palette;
} main, sub; } above, below;
} output; } output;
Sprite(PPU& self); struct Object {
alwaysinline auto width() const -> uint;
alwaysinline auto height() const -> uint;
//list.cpp uint9 x;
auto update(uint10 addr, uint8 data) -> void; uint8 y;
auto synchronize() -> void; uint8 character;
uint1 nameSelect;
uint1 vflip;
uint1 hflip;
uint2 priority;
uint3 palette;
uint1 size;
} list[128];
//sprite.cpp
alwaysinline auto address_reset() -> void;
alwaysinline auto set_first_sprite() -> void;
auto frame() -> void;
auto scanline() -> void;
auto run() -> void;
auto tilefetch() -> void;
auto reset() -> void;
auto on_scanline(Object&) -> bool;
auto serialize(serializer&) -> void;
PPU& self;
friend class PPU; friend class PPU;
}; };

View File

@ -1,167 +1,107 @@
PPU::Window::Window(PPU& self) : self(self) {
}
auto PPU::Window::scanline() -> void { auto PPU::Window::scanline() -> void {
x = 0; x = 0;
} }
auto PPU::Window::run() -> void { auto PPU::Window::run() -> void {
bool main, sub; bool one = (x >= r.oneLeft && x <= r.oneRight);
one = (x >= regs.one_left && x <= regs.one_right); bool two = (x >= r.twoLeft && x <= r.twoRight);
two = (x >= regs.two_left && x <= regs.two_right);
x++; x++;
test( if(test(r.bg1.oneEnable, one ^ r.bg1.oneInvert, r.bg1.twoEnable, two ^ r.bg1.twoInvert, r.bg1.mask)) {
main, sub, if(r.bg1.aboveEnable) ppu.bg1.output.above.priority = 0;
regs.bg1_one_enable, regs.bg1_one_invert, if(r.bg1.belowEnable) ppu.bg1.output.below.priority = 0;
regs.bg1_two_enable, regs.bg1_two_invert,
regs.bg1_mask, regs.bg1_main_enable, regs.bg1_sub_enable
);
if(main) self.bg1.output.main.priority = 0;
if(sub) self.bg1.output.sub.priority = 0;
test(
main, sub,
regs.bg2_one_enable, regs.bg2_one_invert,
regs.bg2_two_enable, regs.bg2_two_invert,
regs.bg2_mask, regs.bg2_main_enable, regs.bg2_sub_enable
);
if(main) self.bg2.output.main.priority = 0;
if(sub) self.bg2.output.sub.priority = 0;
test(
main, sub,
regs.bg3_one_enable, regs.bg3_one_invert,
regs.bg3_two_enable, regs.bg3_two_invert,
regs.bg3_mask, regs.bg3_main_enable, regs.bg3_sub_enable
);
if(main) self.bg3.output.main.priority = 0;
if(sub) self.bg3.output.sub.priority = 0;
test(
main, sub,
regs.bg4_one_enable, regs.bg4_one_invert,
regs.bg4_two_enable, regs.bg4_two_invert,
regs.bg4_mask, regs.bg4_main_enable, regs.bg4_sub_enable
);
if(main) self.bg4.output.main.priority = 0;
if(sub) self.bg4.output.sub.priority = 0;
test(
main, sub,
regs.oam_one_enable, regs.oam_one_invert,
regs.oam_two_enable, regs.oam_two_invert,
regs.oam_mask, regs.oam_main_enable, regs.oam_sub_enable
);
if(main) self.sprite.output.main.priority = 0;
if(sub) self.sprite.output.sub.priority = 0;
test(
main, sub,
regs.col_one_enable, regs.col_one_invert,
regs.col_two_enable, regs.col_two_invert,
regs.col_mask, true, true
);
switch(regs.col_main_mask) {
case 0: main = true; break;
case 1: break;
case 2: main = !main; break;
case 3: main = false; break;
} }
switch(regs.col_sub_mask) { if(test(r.bg2.oneEnable, one ^ r.bg2.oneInvert, r.bg2.twoEnable, two ^ r.bg2.twoInvert, r.bg2.mask)) {
case 0: sub = true; break; if(r.bg2.aboveEnable) ppu.bg2.output.above.priority = 0;
case 1: break; if(r.bg2.belowEnable) ppu.bg2.output.below.priority = 0;
case 2: sub = !sub; break;
case 3: sub = false; break;
} }
output.main.color_enable = main; if(test(r.bg3.oneEnable, one ^ r.bg3.oneInvert, r.bg3.twoEnable, two ^ r.bg3.twoInvert, r.bg3.mask)) {
output.sub.color_enable = sub; if(r.bg3.aboveEnable) ppu.bg3.output.above.priority = 0;
if(r.bg3.belowEnable) ppu.bg3.output.below.priority = 0;
}
if(test(r.bg4.oneEnable, one ^ r.bg4.oneInvert, r.bg4.twoEnable, two ^ r.bg4.twoInvert, r.bg4.mask)) {
if(r.bg4.aboveEnable) ppu.bg4.output.above.priority = 0;
if(r.bg4.belowEnable) ppu.bg4.output.below.priority = 0;
}
if(test(r.oam.oneEnable, one ^ r.oam.oneInvert, r.oam.twoEnable, two ^ r.oam.twoInvert, r.oam.mask)) {
if(r.oam.aboveEnable) ppu.oam.output.above.priority = 0;
if(r.oam.belowEnable) ppu.oam.output.below.priority = 0;
}
bool value = test(r.col.oneEnable, one ^ r.col.oneInvert, r.col.twoEnable, two ^ r.col.twoInvert, r.col.mask);
bool array[] = {true, value, !value, false};
output.above.colorEnable = array[r.col.aboveMask];
output.below.colorEnable = array[r.col.belowMask];
} }
auto PPU::Window::test( auto PPU::Window::test(bool oneEnable, bool one, bool twoEnable, bool two, uint mask) -> bool {
bool& main, bool& sub, if(!oneEnable) return two && twoEnable;
bool one_enable, bool one_invert, if(!twoEnable) return one;
bool two_enable, bool two_invert, if(mask == 0) return (one | two);
uint8 mask, bool main_enable, bool sub_enable if(mask == 1) return (one & two);
) -> void { return (one ^ two) == 3 - mask;
bool one = Window::one ^ one_invert;
bool two = Window::two ^ two_invert;
bool output;
if(one_enable == false && two_enable == false) {
output = false;
} else if(one_enable == true && two_enable == false) {
output = one;
} else if(one_enable == false && two_enable == true) {
output = two;
} else {
switch(mask) {
case 0: output = (one | two) == 1; break;
case 1: output = (one & two) == 1; break;
case 2: output = (one ^ two) == 1; break;
case 3: output = (one ^ two) == 0; break;
}
}
main = main_enable ? output : false;
sub = sub_enable ? output : false;
} }
auto PPU::Window::reset() -> void { auto PPU::Window::reset() -> void {
regs.bg1_one_enable = random(false); r.bg1.oneEnable = random(false);
regs.bg1_one_invert = random(false); r.bg1.oneInvert = random(false);
regs.bg1_two_enable = random(false); r.bg1.twoEnable = random(false);
regs.bg1_two_invert = random(false); r.bg1.twoInvert = random(false);
regs.bg2_one_enable = random(false); r.bg1.mask = random(0);
regs.bg2_one_invert = random(false); r.bg1.aboveEnable = random(false);
regs.bg2_two_enable = random(false); r.bg1.belowEnable = random(false);
regs.bg2_two_invert = random(false);
regs.bg3_one_enable = random(false);
regs.bg3_one_invert = random(false);
regs.bg3_two_enable = random(false);
regs.bg3_two_invert = random(false);
regs.bg4_one_enable = random(false);
regs.bg4_one_invert = random(false);
regs.bg4_two_enable = random(false);
regs.bg4_two_invert = random(false);
regs.oam_one_enable = random(false);
regs.oam_one_invert = random(false);
regs.oam_two_enable = random(false);
regs.oam_two_invert = random(false);
regs.col_one_enable = random(false);
regs.col_one_invert = random(false);
regs.col_two_enable = random(false);
regs.col_two_invert = random(false);
regs.one_left = random(0x00);
regs.one_right = random(0x00);
regs.two_left = random(0x00);
regs.two_right = random(0x00);
regs.bg1_mask = random(0);
regs.bg2_mask = random(0);
regs.bg3_mask = random(0);
regs.bg4_mask = random(0);
regs.oam_mask = random(0);
regs.col_mask = random(0);
regs.bg1_main_enable = random(false);
regs.bg1_sub_enable = random(false);
regs.bg2_main_enable = random(false);
regs.bg2_sub_enable = random(false);
regs.bg3_main_enable = random(false);
regs.bg3_sub_enable = random(false);
regs.bg4_main_enable = random(false);
regs.bg4_sub_enable = random(false);
regs.oam_main_enable = random(false);
regs.oam_sub_enable = random(false);
regs.col_main_mask = random(0);
regs.col_sub_mask = random(0);
output.main.color_enable = 0; r.bg2.oneEnable = random(false);
output.sub.color_enable = 0; r.bg2.oneInvert = random(false);
r.bg2.twoEnable = random(false);
r.bg2.twoInvert = random(false);
r.bg2.mask = random(0);
r.bg2.aboveEnable = random(false);
r.bg2.belowEnable = random(false);
r.bg3.oneEnable = random(false);
r.bg3.oneInvert = random(false);
r.bg3.twoEnable = random(false);
r.bg3.twoInvert = random(false);
r.bg3.mask = random(0);
r.bg3.aboveEnable = random(false);
r.bg3.belowEnable = random(false);
r.bg4.oneEnable = random(false);
r.bg4.oneInvert = random(false);
r.bg4.twoEnable = random(false);
r.bg4.twoInvert = random(false);
r.bg4.mask = random(0);
r.bg4.aboveEnable = random(false);
r.bg4.belowEnable = random(false);
r.oam.oneEnable = random(false);
r.oam.oneInvert = random(false);
r.oam.twoEnable = random(false);
r.oam.twoInvert = random(false);
r.oam.mask = random(0);
r.oam.aboveEnable = random(false);
r.oam.belowEnable = random(false);
r.col.oneEnable = random(false);
r.col.oneInvert = random(false);
r.col.twoEnable = random(false);
r.col.twoInvert = random(false);
r.col.mask = random(0);
r.col.aboveMask = random(0);
r.col.belowMask = random(0);
r.oneLeft = random(0x00);
r.oneRight = random(0x00);
r.twoLeft = random(0x00);
r.twoRight = random(0x00);
output.above.colorEnable = 0;
output.below.colorEnable = 0;
x = 0; x = 0;
one = 0;
two = 0;
} }

View File

@ -1,89 +1,47 @@
struct Window { struct Window {
struct { auto scanline() -> void;
bool bg1_one_enable; auto run() -> void;
bool bg1_one_invert; auto test(bool oneEnable, bool one, bool twoEnable, bool two, uint mask) -> bool;
bool bg1_two_enable; auto reset() -> void;
bool bg1_two_invert;
bool bg2_one_enable; auto serialize(serializer&) -> void;
bool bg2_one_invert;
bool bg2_two_enable;
bool bg2_two_invert;
bool bg3_one_enable; struct Registers {
bool bg3_one_invert; struct Layer {
bool bg3_two_enable; bool oneEnable;
bool bg3_two_invert; bool oneInvert;
bool twoEnable;
bool twoInvert;
uint2 mask;
bool aboveEnable;
bool belowEnable;
} bg1, bg2, bg3, bg4, oam;
bool bg4_one_enable; struct Color {
bool bg4_one_invert; bool oneEnable;
bool bg4_two_enable; bool oneInvert;
bool bg4_two_invert; bool twoEnable;
bool twoInvert;
uint2 mask;
uint2 aboveMask;
uint2 belowMask;
} col;
bool oam_one_enable; uint8 oneLeft;
bool oam_one_invert; uint8 oneRight;
bool oam_two_enable; uint8 twoLeft;
bool oam_two_invert; uint8 twoRight;
} r;
bool col_one_enable;
bool col_one_invert;
bool col_two_enable;
bool col_two_invert;
uint8 one_left;
uint8 one_right;
uint8 two_left;
uint8 two_right;
uint2 bg1_mask;
uint2 bg2_mask;
uint2 bg3_mask;
uint2 bg4_mask;
uint2 oam_mask;
uint2 col_mask;
bool bg1_main_enable;
bool bg1_sub_enable;
bool bg2_main_enable;
bool bg2_sub_enable;
bool bg3_main_enable;
bool bg3_sub_enable;
bool bg4_main_enable;
bool bg4_sub_enable;
bool oam_main_enable;
bool oam_sub_enable;
uint2 col_main_mask;
uint2 col_sub_mask;
} regs;
struct Output { struct Output {
struct Pixel { struct Pixel {
bool color_enable; bool colorEnable;
} main, sub; } above, below;
} output; } output;
struct { struct {
uint x; uint x;
bool one;
bool two;
}; };
Window(PPU& self);
auto scanline() -> void;
auto run() -> void;
auto reset() -> void;
auto test(
bool& main, bool& sub,
bool one_enable, bool one_invert,
bool two_enable, bool two_invert,
uint8 mask, bool main_enable, bool sub_enable
) -> void;
auto serialize(serializer&) -> void;
PPU& self;
friend class PPU; friend class PPU;
}; };

View File

@ -9,11 +9,6 @@ System system;
#include "random.cpp" #include "random.cpp"
#include "serialization.cpp" #include "serialization.cpp"
auto System::loaded() const -> bool { return _loaded; }
auto System::region() const -> Region { return _region; }
auto System::cpuFrequency() const -> uint { return _cpuFrequency; }
auto System::apuFrequency() const -> uint { return _apuFrequency; }
auto System::run() -> void { auto System::run() -> void {
scheduler.enter(); scheduler.enter();
} }

View File

@ -5,10 +5,10 @@ struct Interface;
struct System { struct System {
enum class Region : bool { NTSC = 0, PAL = 1 }; enum class Region : bool { NTSC = 0, PAL = 1 };
auto loaded() const -> bool; inline auto loaded() const -> bool { return _loaded; }
auto region() const -> Region; inline auto region() const -> Region { return _region; }
auto cpuFrequency() const -> uint; inline auto cpuFrequency() const -> uint { return _cpuFrequency; }
auto apuFrequency() const -> uint; inline auto apuFrequency() const -> uint { return _apuFrequency; }
auto run() -> void; auto run() -> void;
auto runToSave() -> void; auto runToSave() -> void;

View File

@ -72,7 +72,7 @@ Presentation::Presentation() {
settings["Video/ColorEmulation"].setValue(colorEmulation.checked()); settings["Video/ColorEmulation"].setValue(colorEmulation.checked());
if(emulator) emulator->set("Color Emulation", colorEmulation.checked()); if(emulator) emulator->set("Color Emulation", colorEmulation.checked());
}); });
scanlineEmulation.setText("Scanlines").setChecked(settings["Video/ScanlineEmulation"].boolean()).onToggle([&] { scanlineEmulation.setVisible(false).setText("Scanlines").setChecked(settings["Video/ScanlineEmulation"].boolean()).onToggle([&] {
settings["Video/ScanlineEmulation"].setValue(scanlineEmulation.checked()); settings["Video/ScanlineEmulation"].setValue(scanlineEmulation.checked());
if(emulator) emulator->set("Scanline Emulation", scanlineEmulation.checked()); if(emulator) emulator->set("Scanline Emulation", scanlineEmulation.checked());
}); });

View File

@ -1,7 +1,133 @@
#pragma once #pragma once
#include <stdlib.h> #include <nall/algorithm.hpp>
#include <nall/range.hpp>
#include <nall/stdint.hpp> #include <nall/stdint.hpp>
#include <nall/memory/memory.hpp>
namespace nall { namespace memory {
inline auto allocate(uint size) -> void*;
inline auto allocate(uint size, uint8_t data) -> void*;
inline auto resize(void* target, uint size) -> void*;
inline auto free(void* target) -> void;
inline auto compare(const void* target, uint capacity, const void* source, uint size) -> int;
inline auto compare(const void* target, const void* source, uint size) -> int;
inline auto icompare(const void* target, uint capacity, const void* source, uint size) -> int;
inline auto icompare(const void* target, const void* source, uint size) -> int;
inline auto copy(void* target, uint capacity, const void* source, uint size) -> void*;
inline auto copy(void* target, const void* source, uint size) -> void*;
inline auto move(void* target, uint capacity, const void* source, uint size) -> void*;
inline auto move(void* target, const void* source, uint size) -> void*;
inline auto fill(void* target, uint capacity, uint8_t data = 0x00) -> void*;
template<typename T> inline auto assign(T* target) -> void {}
template<typename T, typename U, typename... P> inline auto assign(T* target, const U& value, P&&... p) -> void;
}}
namespace nall {
//implementation notes:
//memcmp, memcpy, memmove have terrible performance on small block sizes (FreeBSD 10.0-amd64)
//as this library is used extensively by nall/string, and most strings tend to be small,
//this library hand-codes these functions instead. surprisingly, it's a substantial speedup
auto memory::allocate(uint size) -> void* {
return malloc(size);
}
auto memory::allocate(uint size, uint8_t data) -> void* {
auto result = malloc(size);
if(result) fill(result, size, data);
return result;
}
auto memory::resize(void* target, uint size) -> void* {
return realloc(target, size);
}
auto memory::free(void* target) -> void {
::free(target);
}
auto memory::compare(const void* target, uint capacity, const void* source, uint size) -> int {
auto t = (int8_t*)target;
auto s = (int8_t*)source;
auto l = min(capacity, size);
while(l--) {
auto x = *t++;
auto y = *s++;
if(x != y) return x - y;
}
return 0;
}
auto memory::compare(const void* target, const void* source, uint size) -> int {
return compare(target, size, source, size);
}
auto memory::icompare(const void* target, uint capacity, const void* source, uint size) -> int {
auto t = (int8_t*)target;
auto s = (int8_t*)source;
auto l = min(capacity, size);
while(l--) {
auto x = *t++;
auto y = *s++;
if(x - 'A' < 26) x += 32;
if(y - 'A' < 26) y += 32;
if(x != y) return x - y;
}
return 0;
}
auto memory::icompare(const void* target, const void* source, uint size) -> int {
return icompare(target, size, source, size);
}
auto memory::copy(void* target, uint capacity, const void* source, uint size) -> void* {
auto t = (uint8_t*)target;
auto s = (uint8_t*)source;
auto l = min(capacity, size);
while(l--) *t++ = *s++;
return target;
}
auto memory::copy(void* target, const void* source, uint size) -> void* {
return copy(target, size, source, size);
}
auto memory::move(void* target, uint capacity, const void* source, uint size) -> void* {
auto t = (uint8_t*)target;
auto s = (uint8_t*)source;
auto l = min(capacity, size);
if(t < s) {
while(l--) *t++ = *s++;
} else {
t += l;
s += l;
while(l--) *--t = *--s;
}
return target;
}
auto memory::move(void* target, const void* source, uint size) -> void* {
return move(target, size, source, size);
}
auto memory::fill(void* target, uint capacity, uint8_t data) -> void* {
auto t = (uint8_t*)target;
while(capacity--) *t++ = data;
return target;
}
template<typename T, typename U, typename... P>
auto memory::assign(T* target, const U& value, P&&... p) -> void {
*target++ = value;
assign(target, forward<P>(p)...);
}
}

View File

@ -1,129 +0,0 @@
#pragma once
#include <nall/algorithm.hpp>
namespace nall {
namespace memory {
inline auto allocate(unsigned size) -> void*;
inline auto allocate(unsigned size, uint8_t data) -> void*;
inline auto resize(void* target, unsigned size) -> void*;
inline auto free(void* target) -> void;
inline auto compare(const void* target, unsigned capacity, const void* source, unsigned size) -> signed;
inline auto compare(const void* target, const void* source, unsigned size) -> signed;
inline auto icompare(const void* target, unsigned capacity, const void* source, unsigned size) -> signed;
inline auto icompare(const void* target, const void* source, unsigned size) -> signed;
inline auto copy(void* target, unsigned capacity, const void* source, unsigned size) -> void*;
inline auto copy(void* target, const void* source, unsigned size) -> void*;
inline auto move(void* target, unsigned capacity, const void* source, unsigned size) -> void*;
inline auto move(void* target, const void* source, unsigned size) -> void*;
inline auto fill(void* target, unsigned capacity, uint8_t data = 0x00) -> void*;
}
}
#include <nall/memory/pool.hpp>
namespace nall {
//implementation notes:
//memcmp, memcpy, memmove have terrible performance on small block sizes (FreeBSD 10.0-amd64)
//as this library is used extensively by nall/string, and most strings tend to be small,
//this library hand-codes these functions instead. surprisingly, it's a substantial speedup
auto memory::allocate(unsigned size) -> void* {
return malloc(size);
}
auto memory::allocate(unsigned size, uint8_t data) -> void* {
auto result = malloc(size);
if(result) fill(result, size, data);
return result;
}
auto memory::resize(void* target, unsigned size) -> void* {
return realloc(target, size);
}
auto memory::free(void* target) -> void {
::free(target);
}
auto memory::compare(const void* target, unsigned capacity, const void* source, unsigned size) -> signed {
auto t = (int8_t*)target;
auto s = (int8_t*)source;
auto l = min(capacity, size);
while(l--) {
auto x = *t++;
auto y = *s++;
if(x != y) return x - y;
}
return 0;
}
auto memory::compare(const void* target, const void* source, unsigned size) -> signed {
return compare(target, size, source, size);
}
auto memory::icompare(const void* target, unsigned capacity, const void* source, unsigned size) -> signed {
auto t = (int8_t*)target;
auto s = (int8_t*)source;
auto l = min(capacity, size);
while(l--) {
auto x = *t++;
auto y = *s++;
if(x - 'A' < 26) x += 32;
if(y - 'A' < 26) y += 32;
if(x != y) return x - y;
}
return 0;
}
auto memory::icompare(const void* target, const void* source, unsigned size) -> signed {
return icompare(target, size, source, size);
}
auto memory::copy(void* target, unsigned capacity, const void* source, unsigned size) -> void* {
auto t = (uint8_t*)target;
auto s = (uint8_t*)source;
auto l = min(capacity, size);
while(l--) *t++ = *s++;
return target;
}
auto memory::copy(void* target, const void* source, unsigned size) -> void* {
return copy(target, size, source, size);
}
auto memory::move(void* target, unsigned capacity, const void* source, unsigned size) -> void* {
auto t = (uint8_t*)target;
auto s = (uint8_t*)source;
auto l = min(capacity, size);
if(t < s) {
while(l--) *t++ = *s++;
} else {
t += l;
s += l;
while(l--) *--t = *--s;
}
return target;
}
auto memory::move(void* target, const void* source, unsigned size) -> void* {
return move(target, size, source, size);
}
auto memory::fill(void* target, unsigned capacity, uint8_t data) -> void* {
auto t = (uint8_t*)target;
while(capacity--) *t++ = data;
return target;
}
}

View File

@ -1,62 +0,0 @@
#pragma once
namespace nall {
namespace memory {
template<unsigned Capacity, unsigned Size>
struct pool_spsc {
signed* list = nullptr;
uint8_t* data = nullptr;
unsigned slot = 0;
pool_spsc() {
list = (signed*)memory::allocate(Capacity * sizeof(signed));
data = (uint8_t*)memory::allocate(Capacity * Size);
for(unsigned n = 0; n < Capacity; n++) list[n] = n;
}
~pool_spsc() {
memory::free(list);
memory::free(data);
}
auto allocate(unsigned size) -> void* {
if(size == 0) return nullptr;
if(size > Size) return memory::allocate(size);
signed offset = list[slot];
if(offset < 0) return memory::allocate(size);
list[slot] = -1;
slot = (slot + 1) % Capacity;
return (void*)(data + offset * Size);
}
auto allocate(unsigned size, uint8_t data) -> void* {
auto result = allocate(size);
memset(result, data, size);
return result;
}
auto resize(void* target, unsigned size) -> void* {
if(target == nullptr) return allocate(size);
signed offset = ((uint8_t*)target - data) / Size;
if(offset < 0 || offset >= Capacity) return memory::resize(target, size);
if(size <= Size) return target;
slot = (slot - 1) % Capacity;
list[slot] = offset;
return memory::allocate(size);
}
auto free(void* target) -> void {
if(target == nullptr) return;
signed offset = ((uint8_t*)target - data) / Size;
if(offset < 0 || offset >= Capacity) return memory::free(target);
slot = (slot - 1) % Capacity;
list[slot] = offset;
}
pool_spsc(const pool_spsc&) = delete;
pool_spsc& operator=(const pool_spsc&) = delete;
};
}
}