mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
c074c6e064
commit
ae5b4c3bb3
|
@ -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/";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
});
|
||||
|
|
134
nall/memory.hpp
134
nall/memory.hpp
|
@ -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)...);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue