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

View File

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

View File

@ -1,46 +1,60 @@
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 }; };
const uint id;
struct Mode { enum : uint { BPP2, BPP4, BPP8, Mode7, Inactive }; };
struct ScreenSize { enum : uint { Size32x32, Size32x64, Size64x32, Size64x64 }; };
struct TileSize { enum : uint { Size8x8, Size16x16 }; };
struct Screen { enum : uint { Main, Sub }; };
struct Screen { enum : uint { Above, Below }; };
Background(PPU& self, uint id);
struct Regs {
uint16 tiledata_addr;
uint16 screen_addr;
uint2 screen_size;
struct Registers {
uint16 tiledataAddress;
uint16 screenAddress;
uint2 screenSize;
uint4 mosaic;
bool tile_size;
bool tileSize;
uint mode;
uint priority0;
uint priority1;
uint priority[2];
bool main_enable;
bool sub_enable;
bool aboveEnable;
bool belowEnable;
uint16 hoffset;
uint16 voffset;
} regs;
} r;
struct Cache {
struct Latch {
uint16 hoffset;
uint16 voffset;
} cache;
alwaysinline auto voffset() const -> uint;
alwaysinline auto hoffset() const -> uint;
} latch;
struct Output {
struct Pixel {
uint priority; //0 = none (transparent)
uint8 palette;
uint16 tile;
} main, sub;
} above, below;
} output;
struct Mosaic : Output::Pixel {
@ -54,29 +68,13 @@ struct Background {
int x;
int y;
uint tile_counter;
uint tileCounter;
uint tile;
uint priority;
uint palette_number;
uint palette_index;
uint8 data[8];
uint paletteNumber;
uint paletteIndex;
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;
};

View File

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

View File

@ -1,10 +1,10 @@
//this should only be called by CPU::PPUcounter::tick();
//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
if(status.hcounter >= 1360 && status.hcounter == lineclocks()) {
status.hcounter = 0;
vcounter_tick();
vcounterTick();
}
history.index = (history.index + 1) & 2047;
@ -15,16 +15,16 @@ void PPUcounter::tick() {
//this should only be called by PPU::PPUcounter::tick(n);
//allows stepping by more than the smallest unit of time
void PPUcounter::tick(unsigned clocks) {
auto PPUcounter::tick(uint clocks) -> void {
status.hcounter += clocks;
if(status.hcounter >= lineclocks()) {
status.hcounter -= lineclocks();
vcounter_tick();
vcounterTick();
}
}
//internal
void PPUcounter::vcounter_tick() {
auto PPUcounter::vcounterTick() -> void {
if(++status.vcounter == 128) status.interlace = ppu.interlace();
if((system.region() == System::Region::NTSC && status.interlace == false && status.vcounter == 262)
@ -40,13 +40,13 @@ void PPUcounter::vcounter_tick() {
if(scanline) scanline();
}
bool PPUcounter::field () const { return status.field; }
uint16 PPUcounter::vcounter() const { return status.vcounter; }
uint16 PPUcounter::hcounter() const { return status.hcounter; }
auto PPUcounter::field() const -> bool { return status.field; }
auto PPUcounter::vcounter() const -> uint16 { return status.vcounter; }
auto PPUcounter::hcounter() const -> uint16 { return status.hcounter; }
bool PPUcounter::field (unsigned offset) const { return history.field [(history.index - (offset >> 1)) & 2047]; }
uint16 PPUcounter::vcounter(unsigned offset) const { return history.vcounter[(history.index - (offset >> 1)) & 2047]; }
uint16 PPUcounter::hcounter(unsigned offset) const { return history.hcounter[(history.index - (offset >> 1)) & 2047]; }
auto PPUcounter::field(uint offset) const -> bool { return history.field[(history.index - (offset >> 1)) & 2047]; }
auto PPUcounter::vcounter(uint offset) const -> uint16 { return history.vcounter[(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
//
@ -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
//because the PPU skips one dot to alter the color burst phase of the video signal.
//
//dot 323 range = { 1292, 1294, 1296 }
//dot 327 range = { 1310, 1312, 1314 }
//dot 323 range = {1292, 1294, 1296}
//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) {
return (hcounter() >> 2);
} 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;
return 1364;
}
void PPUcounter::reset() {
auto PPUcounter::reset() -> void {
status.interlace = false;
status.field = 0;
status.vcounter = 0;
status.hcounter = 0;
history.index = 0;
for(unsigned i = 0; i < 2048; i++) {
history.field [i] = 0;
history.vcounter[i] = 0;
history.hcounter[i] = 0;
for(auto n : range(2048)) {
history.field [n] = 0;
history.vcounter[n] = 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
//V=128.
class PPUcounter {
public:
alwaysinline void tick();
alwaysinline void tick(unsigned clocks);
struct PPUcounter {
alwaysinline auto tick() -> void;
alwaysinline auto tick(uint clocks) -> void;
alwaysinline bool field () const;
alwaysinline uint16 vcounter() const;
alwaysinline uint16 hcounter() const;
inline uint16 hdot() const;
inline uint16 lineclocks() const;
alwaysinline auto field() const -> bool;
alwaysinline auto vcounter() const -> uint16;
alwaysinline auto hcounter() const -> uint16;
inline auto hdot() const -> uint16;
inline auto lineclocks() const -> uint16;
alwaysinline bool field (unsigned offset) const;
alwaysinline uint16 vcounter(unsigned offset) const;
alwaysinline uint16 hcounter(unsigned offset) const;
alwaysinline auto field(uint offset) const -> bool;
alwaysinline auto vcounter(uint offset) const -> uint16;
alwaysinline auto hcounter(uint offset) const -> uint16;
inline void reset();
function<void ()> scanline;
void serialize(serializer&);
inline auto reset() -> void;
auto serialize(serializer&) -> void;
function<auto () -> void> scanline;
private:
inline void vcounter_tick();
inline auto vcounterTick() -> void;
struct {
bool interlace;

View File

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

View File

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

View File

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

View File

@ -1,50 +1,45 @@
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;
alwaysinline auto run() -> void;
auto reset() -> void;
auto get_pixel_sub(bool hires) -> uint16;
auto get_pixel_main() -> uint16;
auto addsub(uint x, uint y) -> uint16;
alwaysinline auto get_color(uint palette) -> uint16;
alwaysinline auto get_direct_color(uint palette, uint tile) -> uint16;
alwaysinline auto fixed_color() const -> uint16;
auto below(bool hires) -> uint16;
auto above() -> uint16;
auto blend(uint x, uint y) const -> uint16;
alwaysinline auto paletteColor(uint palette) const -> uint16;
alwaysinline auto directColor(uint palette, uint tile) const -> uint16;
alwaysinline auto fixedColor() const -> uint16;
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;
};

View File

@ -14,107 +14,106 @@ auto PPU::serialize(serializer& s) -> void {
Thread::serialize(s);
PPUcounter::serialize(s);
s.array(vram);
s.array(oam);
s.array(cgram);
s.array(memory.vram);
s.array(memory.oam);
s.array(memory.cgram);
s.integer(ppu1_version);
s.integer(ppu2_version);
s.integer(ppu1.version);
s.integer(ppu1.mdr);
s.integer(ppu2.version);
s.integer(ppu2.mdr);
s.integer(display.interlace);
s.integer(display.overscan);
s.integer(regs.ppu1_mdr);
s.integer(regs.ppu2_mdr);
s.integer(latch.vram);
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(regs.oam_latchdata);
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(latch.oamAddress);
s.integer(latch.cgramAddress);
s.integer(regs.oam_iaddr);
s.integer(regs.cgram_iaddr);
s.integer(r.displayDisable);
s.integer(r.displayBrightness);
s.integer(regs.display_disable);
s.integer(regs.display_brightness);
s.integer(r.oamBaseAddress);
s.integer(r.oamAddress);
s.integer(r.oamPriority);
s.integer(regs.oam_baseaddr);
s.integer(regs.oam_addr);
s.integer(regs.oam_priority);
s.integer(r.bgPriority);
s.integer(r.bgMode);
s.integer(regs.bg3_priority);
s.integer(regs.bgmode);
s.integer(r.hoffsetMode7);
s.integer(r.voffsetMode7);
s.integer(regs.mode7_hoffset);
s.integer(regs.mode7_voffset);
s.integer(r.vramIncrementMode);
s.integer(r.vramMapping);
s.integer(r.vramIncrementSize);
s.integer(regs.vram_incmode);
s.integer(regs.vram_mapping);
s.integer(regs.vram_incsize);
s.integer(r.vramAddress);
s.integer(regs.vram_addr);
s.integer(r.repeatMode7);
s.integer(r.vflipMode7);
s.integer(r.hflipMode7);
s.integer(regs.mode7_repeat);
s.integer(regs.mode7_vflip);
s.integer(regs.mode7_hflip);
s.integer(r.m7a);
s.integer(r.m7b);
s.integer(r.m7c);
s.integer(r.m7d);
s.integer(r.m7x);
s.integer(r.m7y);
s.integer(regs.m7a);
s.integer(regs.m7b);
s.integer(regs.m7c);
s.integer(regs.m7d);
s.integer(regs.m7x);
s.integer(regs.m7y);
s.integer(r.cgramAddress);
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(regs.pseudo_hires);
s.integer(regs.overscan);
s.integer(regs.interlace);
s.integer(regs.hcounter);
s.integer(regs.vcounter);
s.integer(r.hcounter);
s.integer(r.vcounter);
bg1.serialize(s);
bg2.serialize(s);
bg3.serialize(s);
bg4.serialize(s);
sprite.serialize(s);
oam.serialize(s);
window.serialize(s);
screen.serialize(s);
}
auto PPU::Background::serialize(serializer& s) -> void {
s.integer(regs.tiledata_addr);
s.integer(regs.screen_addr);
s.integer(regs.screen_size);
s.integer(regs.mosaic);
s.integer(regs.tile_size);
s.integer(r.tiledataAddress);
s.integer(r.screenAddress);
s.integer(r.screenSize);
s.integer(r.mosaic);
s.integer(r.tileSize);
s.integer(regs.mode);
s.integer(regs.priority0);
s.integer(regs.priority1);
s.integer(r.mode);
s.array(r.priority);
s.integer(regs.main_enable);
s.integer(regs.sub_enable);
s.integer(r.aboveEnable);
s.integer(r.belowEnable);
s.integer(regs.hoffset);
s.integer(regs.voffset);
s.integer(r.hoffset);
s.integer(r.voffset);
s.integer(cache.hoffset);
s.integer(cache.voffset);
s.integer(latch.hoffset);
s.integer(latch.voffset);
s.integer(output.main.priority);
s.integer(output.main.palette);
s.integer(output.main.tile);
s.integer(output.above.priority);
s.integer(output.above.palette);
s.integer(output.above.tile);
s.integer(output.sub.priority);
s.integer(output.sub.palette);
s.integer(output.sub.tile);
s.integer(output.below.priority);
s.integer(output.below.palette);
s.integer(output.below.tile);
s.integer(x);
s.integer(y);
@ -128,32 +127,34 @@ auto PPU::Background::serialize(serializer& s) -> void {
s.integer(mosaic.hcounter);
s.integer(mosaic.hoffset);
s.integer(tile_counter);
s.integer(tileCounter);
s.integer(tile);
s.integer(priority);
s.integer(palette_number);
s.integer(palette_index);
s.integer(paletteNumber);
s.integer(paletteIndex);
s.array(data);
}
auto PPU::Sprite::serialize(serializer& s) -> void {
for(auto n : range(128)) {
s.integer(list[n].x);
s.integer(list[n].y);
s.integer(list[n].character);
s.integer(list[n].nameselect);
s.integer(list[n].vflip);
s.integer(list[n].hflip);
s.integer(list[n].priority);
s.integer(list[n].palette);
s.integer(list[n].size);
}
auto PPU::OAM::serialize(serializer& s) -> void {
s.integer(r.aboveEnable);
s.integer(r.belowEnable);
s.integer(r.interlace);
s.integer(r.baseSize);
s.integer(r.nameSelect);
s.integer(r.tiledataAddress);
s.integer(r.firstSprite);
s.array(r.priority);
s.integer(r.timeOver);
s.integer(r.rangeOver);
s.integer(t.x);
s.integer(t.y);
s.integer(t.item_count);
s.integer(t.tile_count);
s.integer(t.itemCount);
s.integer(t.tileCount);
s.integer(t.active);
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].palette);
s.integer(t.tile[p][n].hflip);
s.integer(t.tile[p][n].d0);
s.integer(t.tile[p][n].d1);
s.integer(t.tile[p][n].d2);
s.integer(t.tile[p][n].d3);
s.integer(t.tile[p][n].data);
}
}
s.integer(regs.main_enable);
s.integer(regs.sub_enable);
s.integer(regs.interlace);
s.integer(output.above.priority);
s.integer(output.above.palette);
s.integer(regs.base_size);
s.integer(regs.nameselect);
s.integer(regs.tiledata_addr);
s.integer(regs.first_sprite);
s.integer(output.below.priority);
s.integer(output.below.palette);
s.integer(regs.priority0);
s.integer(regs.priority1);
s.integer(regs.priority2);
s.integer(regs.priority3);
s.integer(regs.time_over);
s.integer(regs.range_over);
s.integer(output.main.priority);
s.integer(output.main.palette);
s.integer(output.sub.priority);
s.integer(output.sub.palette);
for(auto n : range(128)) {
s.integer(list[n].x);
s.integer(list[n].y);
s.integer(list[n].character);
s.integer(list[n].nameSelect);
s.integer(list[n].vflip);
s.integer(list[n].hflip);
s.integer(list[n].priority);
s.integer(list[n].palette);
s.integer(list[n].size);
}
}
auto PPU::Window::serialize(serializer& s) -> void {
s.integer(regs.bg1_one_enable);
s.integer(regs.bg1_one_invert);
s.integer(regs.bg1_two_enable);
s.integer(regs.bg1_two_invert);
s.integer(r.bg1.oneEnable);
s.integer(r.bg1.oneInvert);
s.integer(r.bg1.twoEnable);
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(regs.bg2_one_invert);
s.integer(regs.bg2_two_enable);
s.integer(regs.bg2_two_invert);
s.integer(r.bg2.oneEnable);
s.integer(r.bg2.oneInvert);
s.integer(r.bg2.twoEnable);
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(regs.bg3_one_invert);
s.integer(regs.bg3_two_enable);
s.integer(regs.bg3_two_invert);
s.integer(r.bg3.oneEnable);
s.integer(r.bg3.oneInvert);
s.integer(r.bg3.twoEnable);
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(regs.bg4_one_invert);
s.integer(regs.bg4_two_enable);
s.integer(regs.bg4_two_invert);
s.integer(r.bg4.oneEnable);
s.integer(r.bg4.oneInvert);
s.integer(r.bg4.twoEnable);
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(regs.oam_one_invert);
s.integer(regs.oam_two_enable);
s.integer(regs.oam_two_invert);
s.integer(r.oam.oneEnable);
s.integer(r.oam.oneInvert);
s.integer(r.oam.twoEnable);
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(regs.col_one_invert);
s.integer(regs.col_two_enable);
s.integer(regs.col_two_invert);
s.integer(r.col.oneEnable);
s.integer(r.col.oneInvert);
s.integer(r.col.twoEnable);
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(regs.one_right);
s.integer(regs.two_left);
s.integer(regs.two_right);
s.integer(r.oneLeft);
s.integer(r.oneRight);
s.integer(r.twoLeft);
s.integer(r.twoRight);
s.integer(regs.bg1_mask);
s.integer(regs.bg2_mask);
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(output.above.colorEnable);
s.integer(output.below.colorEnable);
s.integer(x);
s.integer(one);
s.integer(two);
}
auto PPU::Screen::serialize(serializer& s) -> void {
s.integer(regs.addsub_mode);
s.integer(regs.direct_color);
s.integer(r.blendMode);
s.integer(r.directColor);
s.integer(regs.color_mode);
s.integer(regs.color_halve);
s.integer(regs.bg1_color_enable);
s.integer(regs.bg2_color_enable);
s.integer(regs.bg3_color_enable);
s.integer(regs.bg4_color_enable);
s.integer(regs.oam_color_enable);
s.integer(regs.back_color_enable);
s.integer(r.colorMode);
s.integer(r.colorHalve);
s.integer(r.bg1.colorEnable);
s.integer(r.bg2.colorEnable);
s.integer(r.bg3.colorEnable);
s.integer(r.bg4.colorEnable);
s.integer(r.oam.colorEnable);
s.integer(r.back.colorEnable);
s.integer(regs.color_b);
s.integer(regs.color_g);
s.integer(regs.color_r);
s.integer(r.colorBlue);
s.integer(r.colorGreen);
s.integer(r.colorRed);
s.integer(math.main.color);
s.integer(math.main.color_enable);
s.integer(math.sub.color);
s.integer(math.sub.color_enable);
s.integer(math.above.color);
s.integer(math.above.colorEnable);
s.integer(math.below.color);
s.integer(math.below.colorEnable);
s.integer(math.transparent);
s.integer(math.addsub_mode);
s.integer(math.color_halve);
s.integer(math.blendMode);
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)) {
uint n = addr >> 2; //sprite#
addr &= 3;
@ -9,11 +9,11 @@ auto PPU::Sprite::update(uint10 addr, uint8 data) -> void {
} else if(addr == 2) {
list[n].character = data;
} 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].priority = data.bits(5,4);
list[n].palette = data.bits(3,1);
list[n].nameselect = data.bit (0);
list[n].vflip = data.bit (7);
}
} else {
uint n = (addr & 0x1f) << 2; //sprite#
@ -28,27 +28,27 @@ auto PPU::Sprite::update(uint10 addr, uint8 data) -> void {
}
}
auto PPU::Sprite::synchronize() -> void {
for(auto n : range(544)) update(n, ppu.oam[n]);
auto PPU::OAM::synchronize() -> void {
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) {
static uint width[] = { 8, 8, 8, 16, 16, 32, 16, 16};
return width[ppu.sprite.regs.base_size];
static const uint width[] = { 8, 8, 8, 16, 16, 32, 16, 16};
return width[ppu.oam.r.baseSize];
} else {
static uint width[] = {16, 32, 64, 32, 64, 64, 32, 32};
return width[ppu.sprite.regs.base_size];
static const uint width[] = {16, 32, 64, 32, 64, 64, 32, 32};
return width[ppu.oam.r.baseSize];
}
}
auto PPU::Sprite::Object::height() const -> uint {
auto PPU::OAM::Object::height() const -> uint {
if(size == 0) {
if(ppu.sprite.regs.interlace && ppu.sprite.regs.base_size >= 6) return 16;
static uint height[] = { 8, 8, 8, 16, 16, 32, 32, 32};
return height[ppu.sprite.regs.base_size];
if(ppu.oam.r.interlace && ppu.oam.r.baseSize >= 6) return 16; //hardware quirk
static const uint height[] = { 8, 8, 8, 16, 16, 32, 32, 32};
return height[ppu.oam.r.baseSize];
} else {
static uint height[] = {16, 32, 64, 32, 64, 64, 64, 32};
return height[ppu.sprite.regs.base_size];
static const uint height[] = {16, 32, 64, 32, 64, 64, 64, 32};
return height[ppu.oam.r.baseSize];
}
}

View File

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

View File

@ -1,17 +1,36 @@
struct Sprite {
struct Object {
uint9 x;
uint8 y;
uint8 character;
uint1 nameselect;
uint1 vflip;
uint1 hflip;
uint2 priority;
uint3 palette;
uint1 size;
alwaysinline auto width() const -> uint;
alwaysinline auto height() const -> uint;
} list[128];
struct OAM {
alwaysinline auto addressReset() -> void;
alwaysinline auto setFirstSprite() -> void;
auto frame() -> void;
auto scanline() -> void;
auto run() -> void;
auto tilefetch() -> void;
auto reset() -> void;
struct Object;
auto onScanline(Object&) -> bool;
//list.cpp
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 {
bool valid;
@ -19,71 +38,47 @@ struct Sprite {
};
struct Tile {
bool valid;
uint9 x;
uint2 priority;
uint8 palette;
uint1 hflip;
uint8 d0, d1, d2, d3;
bool valid;
uint9 x;
uint2 priority;
uint8 palette;
uint1 hflip;
uint32 data;
};
struct State {
uint x;
uint y;
uint item_count;
uint tile_count;
uint itemCount;
uint tileCount;
bool active;
Item item[2][32];
Tile tile[2][34];
} 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 Pixel {
uint priority; //0 = none (transparent)
uint8 palette;
} main, sub;
} above, below;
} output;
Sprite(PPU& self);
struct Object {
alwaysinline auto width() const -> uint;
alwaysinline auto height() const -> uint;
//list.cpp
auto update(uint10 addr, uint8 data) -> void;
auto synchronize() -> void;
uint9 x;
uint8 y;
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;
};

View File

@ -1,167 +1,107 @@
PPU::Window::Window(PPU& self) : self(self) {
}
auto PPU::Window::scanline() -> void {
x = 0;
}
auto PPU::Window::run() -> void {
bool main, sub;
one = (x >= regs.one_left && x <= regs.one_right);
two = (x >= regs.two_left && x <= regs.two_right);
bool one = (x >= r.oneLeft && x <= r.oneRight);
bool two = (x >= r.twoLeft && x <= r.twoRight);
x++;
test(
main, sub,
regs.bg1_one_enable, regs.bg1_one_invert,
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;
if(test(r.bg1.oneEnable, one ^ r.bg1.oneInvert, r.bg1.twoEnable, two ^ r.bg1.twoInvert, r.bg1.mask)) {
if(r.bg1.aboveEnable) ppu.bg1.output.above.priority = 0;
if(r.bg1.belowEnable) ppu.bg1.output.below.priority = 0;
}
switch(regs.col_sub_mask) {
case 0: sub = true; break;
case 1: break;
case 2: sub = !sub; break;
case 3: sub = false; break;
if(test(r.bg2.oneEnable, one ^ r.bg2.oneInvert, r.bg2.twoEnable, two ^ r.bg2.twoInvert, r.bg2.mask)) {
if(r.bg2.aboveEnable) ppu.bg2.output.above.priority = 0;
if(r.bg2.belowEnable) ppu.bg2.output.below.priority = 0;
}
output.main.color_enable = main;
output.sub.color_enable = sub;
if(test(r.bg3.oneEnable, one ^ r.bg3.oneInvert, r.bg3.twoEnable, two ^ r.bg3.twoInvert, r.bg3.mask)) {
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(
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 {
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::test(bool oneEnable, bool one, bool twoEnable, bool two, uint mask) -> bool {
if(!oneEnable) return two && twoEnable;
if(!twoEnable) return one;
if(mask == 0) return (one | two);
if(mask == 1) return (one & two);
return (one ^ two) == 3 - mask;
}
auto PPU::Window::reset() -> void {
regs.bg1_one_enable = random(false);
regs.bg1_one_invert = random(false);
regs.bg1_two_enable = random(false);
regs.bg1_two_invert = random(false);
regs.bg2_one_enable = random(false);
regs.bg2_one_invert = random(false);
regs.bg2_two_enable = 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);
r.bg1.oneEnable = random(false);
r.bg1.oneInvert = random(false);
r.bg1.twoEnable = random(false);
r.bg1.twoInvert = random(false);
r.bg1.mask = random(0);
r.bg1.aboveEnable = random(false);
r.bg1.belowEnable = random(false);
output.main.color_enable = 0;
output.sub.color_enable = 0;
r.bg2.oneEnable = random(false);
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;
one = 0;
two = 0;
}

View File

@ -1,89 +1,47 @@
struct Window {
struct {
bool bg1_one_enable;
bool bg1_one_invert;
bool bg1_two_enable;
bool bg1_two_invert;
auto scanline() -> void;
auto run() -> void;
auto test(bool oneEnable, bool one, bool twoEnable, bool two, uint mask) -> bool;
auto reset() -> void;
bool bg2_one_enable;
bool bg2_one_invert;
bool bg2_two_enable;
bool bg2_two_invert;
auto serialize(serializer&) -> void;
bool bg3_one_enable;
bool bg3_one_invert;
bool bg3_two_enable;
bool bg3_two_invert;
struct Registers {
struct Layer {
bool oneEnable;
bool oneInvert;
bool twoEnable;
bool twoInvert;
uint2 mask;
bool aboveEnable;
bool belowEnable;
} bg1, bg2, bg3, bg4, oam;
bool bg4_one_enable;
bool bg4_one_invert;
bool bg4_two_enable;
bool bg4_two_invert;
struct Color {
bool oneEnable;
bool oneInvert;
bool twoEnable;
bool twoInvert;
uint2 mask;
uint2 aboveMask;
uint2 belowMask;
} col;
bool oam_one_enable;
bool oam_one_invert;
bool oam_two_enable;
bool oam_two_invert;
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;
uint8 oneLeft;
uint8 oneRight;
uint8 twoLeft;
uint8 twoRight;
} r;
struct Output {
struct Pixel {
bool color_enable;
} main, sub;
bool colorEnable;
} above, below;
} output;
struct {
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;
};

View File

@ -9,11 +9,6 @@ System system;
#include "random.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 {
scheduler.enter();
}

View File

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

View File

@ -72,7 +72,7 @@ Presentation::Presentation() {
settings["Video/ColorEmulation"].setValue(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());
if(emulator) emulator->set("Scanline Emulation", scanlineEmulation.checked());
});

View File

@ -1,7 +1,133 @@
#pragma once
#include <stdlib.h>
#include <nall/range.hpp>
#include <nall/algorithm.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;
};
}
}