mirror of https://github.com/bsnes-emu/bsnes.git
Update to bsnes v063r09 release.
So that's about 24 solid hours worth of programming in two days. Holy fuck am I exhausted. Don't expect the last bits any time soon. Missing features: - Mode 7 renderer - OAM previous-line caching - offset-per-tile mode - some edge cases in color add/sub - hires - interlace - overscan - S-PPU control over VRAM, OAM, CGRAM during active display Speed hit is about as bad as I had feared. 172fps with scanline rendering to 80fps with dot rendering. I'm guessing that with optimizations I can make it to ~100-110fps.
This commit is contained in:
parent
efa7879c6d
commit
0a3fdc404d
|
@ -1,5 +1,16 @@
|
|||
#ifdef SPPU_CPP
|
||||
|
||||
void sPPU::Background::scanline() {
|
||||
if(self.vcounter() == 1) {
|
||||
regs.mosaic_y = 1;
|
||||
regs.mosaic_countdown = 0;
|
||||
} else {
|
||||
if(!regs.mosaic || !regs.mosaic_countdown) regs.mosaic_y = self.vcounter();
|
||||
if(!regs.mosaic_countdown) regs.mosaic_countdown = regs.mosaic + 1;
|
||||
regs.mosaic_countdown--;
|
||||
}
|
||||
}
|
||||
|
||||
void sPPU::Background::run() {
|
||||
output.main.valid = false;
|
||||
output.sub.valid = false;
|
||||
|
|
|
@ -14,6 +14,7 @@ public:
|
|||
unsigned screen_size;
|
||||
unsigned mosaic;
|
||||
unsigned mosaic_y;
|
||||
unsigned mosaic_countdown;
|
||||
bool tile_size;
|
||||
|
||||
unsigned mode;
|
||||
|
@ -35,6 +36,7 @@ public:
|
|||
} main, sub;
|
||||
} output;
|
||||
|
||||
void scanline();
|
||||
void run();
|
||||
unsigned get_color(unsigned x, unsigned y, uint16 offset);
|
||||
|
||||
|
|
|
@ -57,19 +57,23 @@ bool sPPU::hires() const {
|
|||
|
||||
//INIDISP
|
||||
void sPPU::mmio_w2100(uint8 data) {
|
||||
if(regs.display_disabled && vcounter() == 225) oam.address_reset();
|
||||
regs.display_disabled = data & 0x80;
|
||||
regs.display_brightness = data & 0x0f;
|
||||
}
|
||||
|
||||
//OBSEL
|
||||
void sPPU::mmio_w2101(uint8 data) {
|
||||
oam.regs.base_size = (data >> 5) & 7;
|
||||
oam.regs.nameselect = (data >> 3) & 3;
|
||||
oam.regs.tiledata_addr = (data & 3) << 14;
|
||||
}
|
||||
|
||||
//OAMADDL
|
||||
void sPPU::mmio_w2102(uint8 data) {
|
||||
regs.oam_baseaddr &= 0x0100;
|
||||
regs.oam_baseaddr |= (data << 0);
|
||||
regs.oam_addr = regs.oam_baseaddr << 1;
|
||||
regs.oam_firstsprite = (regs.oam_priority == false ? 0 : (regs.oam_addr >> 2) & 127);
|
||||
oam.address_reset();
|
||||
}
|
||||
|
||||
//OAMADDH
|
||||
|
@ -77,8 +81,7 @@ void sPPU::mmio_w2103(uint8 data) {
|
|||
regs.oam_priority = data & 0x80;
|
||||
regs.oam_baseaddr &= 0x00ff;
|
||||
regs.oam_baseaddr |= (data & 1) << 8;
|
||||
regs.oam_addr = regs.oam_baseaddr << 1;
|
||||
regs.oam_firstsprite = (regs.oam_priority == false ? 0 : (regs.oam_addr >> 2) & 127);
|
||||
oam.address_reset();
|
||||
}
|
||||
|
||||
//OAMDATA
|
||||
|
@ -93,7 +96,7 @@ void sPPU::mmio_w2104(uint8 data) {
|
|||
}
|
||||
|
||||
regs.oam_addr = (regs.oam_addr + 1) & 0x03ff;
|
||||
regs.oam_firstsprite = (regs.oam_priority == false ? 0 : (regs.oam_addr >> 2) & 127);
|
||||
oam.regs.first_sprite = (regs.oam_priority == false ? 0 : (regs.oam_addr >> 2) & 127);
|
||||
}
|
||||
|
||||
//BGMODE
|
||||
|
@ -111,6 +114,7 @@ void sPPU::mmio_w2105(uint8 data) {
|
|||
bg2.regs.mode = Background::Mode::BPP2; bg2.regs.priority0 = 7; bg2.regs.priority1 = 10;
|
||||
bg3.regs.mode = Background::Mode::BPP2; bg3.regs.priority0 = 2; bg3.regs.priority1 = 5;
|
||||
bg4.regs.mode = Background::Mode::BPP2; bg4.regs.priority0 = 1; bg4.regs.priority1 = 4;
|
||||
oam.regs.priority0 = 3; oam.regs.priority1 = 6; oam.regs.priority2 = 9; oam.regs.priority3 = 12;
|
||||
} break;
|
||||
|
||||
case 1: {
|
||||
|
@ -122,10 +126,12 @@ void sPPU::mmio_w2105(uint8 data) {
|
|||
bg1.regs.priority0 = 5; bg1.regs.priority1 = 8;
|
||||
bg2.regs.priority0 = 4; bg2.regs.priority1 = 7;
|
||||
bg3.regs.priority0 = 1; bg3.regs.priority1 = 10;
|
||||
oam.regs.priority0 = 2; oam.regs.priority1 = 3; oam.regs.priority2 = 6; oam.regs.priority3 = 9;
|
||||
} else {
|
||||
bg1.regs.priority0 = 6; bg1.regs.priority1 = 9;
|
||||
bg2.regs.priority0 = 5; bg2.regs.priority1 = 8;
|
||||
bg3.regs.priority0 = 1; bg3.regs.priority1 = 3;
|
||||
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 7; oam.regs.priority3 = 10;
|
||||
}
|
||||
} break;
|
||||
|
||||
|
@ -136,6 +142,7 @@ void sPPU::mmio_w2105(uint8 data) {
|
|||
bg4.regs.mode = Background::Mode::Inactive;
|
||||
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
|
||||
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
|
||||
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.regs.priority3 = 8;
|
||||
} break;
|
||||
|
||||
case 3: {
|
||||
|
@ -145,6 +152,7 @@ void sPPU::mmio_w2105(uint8 data) {
|
|||
bg4.regs.mode = Background::Mode::Inactive;
|
||||
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
|
||||
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
|
||||
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.regs.priority3 = 8;
|
||||
} break;
|
||||
|
||||
case 4: {
|
||||
|
@ -154,6 +162,7 @@ void sPPU::mmio_w2105(uint8 data) {
|
|||
bg4.regs.mode = Background::Mode::Inactive;
|
||||
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
|
||||
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
|
||||
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.regs.priority3 = 8;
|
||||
} break;
|
||||
|
||||
case 5: {
|
||||
|
@ -163,6 +172,7 @@ void sPPU::mmio_w2105(uint8 data) {
|
|||
bg4.regs.mode = Background::Mode::Inactive;
|
||||
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
|
||||
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
|
||||
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.regs.priority3 = 8;
|
||||
} break;
|
||||
|
||||
case 6: {
|
||||
|
@ -171,6 +181,7 @@ void sPPU::mmio_w2105(uint8 data) {
|
|||
bg3.regs.mode = Background::Mode::Inactive;
|
||||
bg4.regs.mode = Background::Mode::Inactive;
|
||||
bg1.regs.priority0 = 2; bg1.regs.priority1 = 5;
|
||||
oam.regs.priority0 = 1; oam.regs.priority1 = 3; oam.regs.priority2 = 4; oam.regs.priority3 = 6;
|
||||
} break;
|
||||
|
||||
case 7: {
|
||||
|
@ -180,11 +191,11 @@ void sPPU::mmio_w2105(uint8 data) {
|
|||
|
||||
//MOSAIC
|
||||
void sPPU::mmio_w2106(uint8 data) {
|
||||
regs.mosaic_size = (data >> 4) & 15;
|
||||
bg4.regs.mosaic = (data & 0x08 ? regs.mosaic_size : 0);
|
||||
bg3.regs.mosaic = (data & 0x04 ? regs.mosaic_size : 0);
|
||||
bg2.regs.mosaic = (data & 0x02 ? regs.mosaic_size : 0);
|
||||
bg1.regs.mosaic = (data & 0x01 ? regs.mosaic_size : 0);
|
||||
unsigned mosaic_size = (data >> 4) & 15;
|
||||
bg4.regs.mosaic = (data & 0x08 ? mosaic_size : 0);
|
||||
bg3.regs.mosaic = (data & 0x04 ? mosaic_size : 0);
|
||||
bg2.regs.mosaic = (data & 0x02 ? mosaic_size : 0);
|
||||
bg1.regs.mosaic = (data & 0x01 ? mosaic_size : 0);
|
||||
}
|
||||
|
||||
//BG1SC
|
||||
|
@ -370,35 +381,79 @@ void sPPU::mmio_w2122(uint8 data) {
|
|||
regs.cgram_addr = (regs.cgram_addr + 1) & 0x01ff;
|
||||
}
|
||||
|
||||
//W12SEL
|
||||
void sPPU::mmio_w2123(uint8 data) {
|
||||
window.regs.bg2_two_enable = data & 0x80;
|
||||
window.regs.bg2_two_invert = data & 0x40;
|
||||
window.regs.bg2_one_enable = data & 0x20;
|
||||
window.regs.bg2_one_invert = data & 0x10;
|
||||
window.regs.bg1_two_enable = data & 0x08;
|
||||
window.regs.bg1_two_invert = data & 0x04;
|
||||
window.regs.bg1_one_enable = data & 0x02;
|
||||
window.regs.bg1_one_invert = data & 0x01;
|
||||
}
|
||||
|
||||
//W34SEL
|
||||
void sPPU::mmio_w2124(uint8 data) {
|
||||
window.regs.bg4_two_enable = data & 0x80;
|
||||
window.regs.bg4_two_invert = data & 0x40;
|
||||
window.regs.bg4_one_enable = data & 0x20;
|
||||
window.regs.bg4_one_invert = data & 0x10;
|
||||
window.regs.bg3_two_enable = data & 0x08;
|
||||
window.regs.bg3_two_invert = data & 0x04;
|
||||
window.regs.bg3_one_enable = data & 0x02;
|
||||
window.regs.bg3_one_invert = data & 0x01;
|
||||
}
|
||||
|
||||
//WOBJSEL
|
||||
void sPPU::mmio_w2125(uint8 data) {
|
||||
window.regs.col_two_enable = data & 0x80;
|
||||
window.regs.col_two_invert = data & 0x40;
|
||||
window.regs.col_one_enable = data & 0x20;
|
||||
window.regs.col_one_invert = data & 0x10;
|
||||
window.regs.oam_two_enable = data & 0x08;
|
||||
window.regs.oam_two_invert = data & 0x04;
|
||||
window.regs.oam_one_enable = data & 0x02;
|
||||
window.regs.oam_one_invert = data & 0x01;
|
||||
}
|
||||
|
||||
//WH0
|
||||
void sPPU::mmio_w2126(uint8 data) {
|
||||
window.regs.one_left = data;
|
||||
}
|
||||
|
||||
//WH1
|
||||
void sPPU::mmio_w2127(uint8 data) {
|
||||
window.regs.one_right = data;
|
||||
}
|
||||
|
||||
//WH2
|
||||
void sPPU::mmio_w2128(uint8 data) {
|
||||
window.regs.two_left = data;
|
||||
}
|
||||
|
||||
//WH3
|
||||
void sPPU::mmio_w2129(uint8 data) {
|
||||
window.regs.two_right = data;
|
||||
}
|
||||
|
||||
//WBGLOG
|
||||
void sPPU::mmio_w212a(uint8 data) {
|
||||
window.regs.bg4_mask = (data >> 6) & 3;
|
||||
window.regs.bg3_mask = (data >> 4) & 3;
|
||||
window.regs.bg2_mask = (data >> 2) & 3;
|
||||
window.regs.bg1_mask = (data >> 0) & 3;
|
||||
}
|
||||
|
||||
//WOBJLOG
|
||||
void sPPU::mmio_w212b(uint8 data) {
|
||||
window.regs.col_mask = (data >> 2) & 3;
|
||||
window.regs.oam_mask = (data >> 0) & 3;
|
||||
}
|
||||
|
||||
//TM
|
||||
void sPPU::mmio_w212c(uint8 data) {
|
||||
oam.regs.main_enabled = data & 0x10;
|
||||
bg4.regs.main_enabled = data & 0x08;
|
||||
bg3.regs.main_enabled = data & 0x04;
|
||||
bg2.regs.main_enabled = data & 0x02;
|
||||
|
@ -407,28 +462,62 @@ void sPPU::mmio_w212c(uint8 data) {
|
|||
|
||||
//TS
|
||||
void sPPU::mmio_w212d(uint8 data) {
|
||||
oam.regs.sub_enabled = data & 0x10;
|
||||
bg4.regs.sub_enabled = data & 0x08;
|
||||
bg3.regs.sub_enabled = data & 0x04;
|
||||
bg2.regs.sub_enabled = data & 0x02;
|
||||
bg1.regs.sub_enabled = data & 0x01;
|
||||
}
|
||||
|
||||
//TMW
|
||||
void sPPU::mmio_w212e(uint8 data) {
|
||||
window.regs.oam_main_enable = data & 0x10;
|
||||
window.regs.bg4_main_enable = data & 0x08;
|
||||
window.regs.bg3_main_enable = data & 0x04;
|
||||
window.regs.bg2_main_enable = data & 0x02;
|
||||
window.regs.bg1_main_enable = data & 0x01;
|
||||
}
|
||||
|
||||
//TSW
|
||||
void sPPU::mmio_w212f(uint8 data) {
|
||||
window.regs.oam_sub_enable = data & 0x10;
|
||||
window.regs.bg4_sub_enable = data & 0x08;
|
||||
window.regs.bg3_sub_enable = data & 0x04;
|
||||
window.regs.bg2_sub_enable = data & 0x02;
|
||||
window.regs.bg1_sub_enable = data & 0x01;
|
||||
}
|
||||
|
||||
//CGWSEL
|
||||
void sPPU::mmio_w2130(uint8 data) {
|
||||
window.regs.col_main_mask = (data >> 6) & 3;
|
||||
window.regs.col_sub_mask = (data >> 4) & 3;
|
||||
screen.regs.addsub_mode = data & 0x02;
|
||||
screen.regs.direct_color = data & 0x01;
|
||||
}
|
||||
|
||||
//CGADDSUB
|
||||
void sPPU::mmio_w2131(uint8 data) {
|
||||
screen.regs.color_mode = data & 0x80;
|
||||
screen.regs.color_halve = data & 0x40;
|
||||
screen.regs.back_color_enable = data & 0x20;
|
||||
screen.regs.oam_color_enable = data & 0x10;
|
||||
screen.regs.bg4_color_enable = data & 0x08;
|
||||
screen.regs.bg3_color_enable = data & 0x04;
|
||||
screen.regs.bg2_color_enable = data & 0x02;
|
||||
screen.regs.bg1_color_enable = data & 0x01;
|
||||
}
|
||||
|
||||
//COLDATA
|
||||
void sPPU::mmio_w2132(uint8 data) {
|
||||
if(data & 0x80) screen.regs.color_b = data & 0x1f;
|
||||
if(data & 0x40) screen.regs.color_g = data & 0x1f;
|
||||
if(data & 0x20) screen.regs.color_r = data & 0x1f;
|
||||
screen.regs.color_rgb = (screen.regs.color_b << 10) + (screen.regs.color_g << 5) + screen.regs.color_r;
|
||||
}
|
||||
|
||||
//SETINI
|
||||
void sPPU::mmio_w2133(uint8 data) {
|
||||
oam.regs.interlace = data & 0x02;
|
||||
}
|
||||
|
||||
//MPYL
|
||||
|
@ -462,7 +551,7 @@ uint8 sPPU::mmio_r2137() {
|
|||
uint8 sPPU::mmio_r2138() {
|
||||
regs.ppu1_mdr = oam_read(regs.oam_addr);
|
||||
regs.oam_addr = (regs.oam_addr + 1) & 0x03ff;
|
||||
regs.oam_firstsprite = (regs.oam_priority == false ? 0 : (regs.oam_addr >> 2) & 127);
|
||||
oam.regs.first_sprite = (regs.oam_priority == false ? 0 : (regs.oam_addr >> 2) & 127);
|
||||
return regs.ppu1_mdr;
|
||||
}
|
||||
|
||||
|
@ -531,7 +620,8 @@ uint8 sPPU::mmio_r213d() {
|
|||
//STAT77
|
||||
uint8 sPPU::mmio_r213e() {
|
||||
regs.ppu1_mdr &= 0x10;
|
||||
//missing time over, range over flags
|
||||
regs.ppu1_mdr |= oam.regs.time_over << 7;
|
||||
regs.ppu1_mdr |= oam.regs.range_over << 6;
|
||||
regs.ppu1_mdr |= ppu1_version & 0x0f;
|
||||
return regs.ppu1_mdr;
|
||||
}
|
||||
|
@ -558,7 +648,6 @@ void sPPU::mmio_reset() {
|
|||
regs.ppu1_mdr = 0xff;
|
||||
regs.ppu2_mdr = 0xff;
|
||||
|
||||
regs.mosaic_countdown = 0;
|
||||
regs.vram_readbuffer = 0x0000;
|
||||
regs.oam_latchdata = 0x00;
|
||||
regs.cgram_latchdata = 0x00;
|
||||
|
@ -577,15 +666,11 @@ void sPPU::mmio_reset() {
|
|||
regs.oam_baseaddr = 0x0000;
|
||||
regs.oam_addr = 0x0000;
|
||||
regs.oam_priority = false;
|
||||
regs.oam_firstsprite = 0;
|
||||
|
||||
//$2105 BGMODE
|
||||
regs.bg3_priority = false;
|
||||
regs.bgmode = 0;
|
||||
|
||||
//$2106 MOSAIC
|
||||
regs.mosaic_size = 0;
|
||||
|
||||
//$2115 VMAIN
|
||||
regs.vram_incmode = 1;
|
||||
regs.vram_mapping = 0;
|
||||
|
|
|
@ -2,7 +2,6 @@ struct {
|
|||
uint8 ppu1_mdr;
|
||||
uint8 ppu2_mdr;
|
||||
|
||||
unsigned mosaic_countdown;
|
||||
uint16 vram_readbuffer;
|
||||
uint8 oam_latchdata;
|
||||
uint8 cgram_latchdata;
|
||||
|
@ -21,15 +20,11 @@ struct {
|
|||
uint16 oam_baseaddr;
|
||||
uint16 oam_addr;
|
||||
bool oam_priority;
|
||||
uint8 oam_firstsprite;
|
||||
|
||||
//$2105 BGMODE
|
||||
bool bg3_priority;
|
||||
uint8 bgmode;
|
||||
|
||||
//$2106 MOSAIC
|
||||
uint8 mosaic_size;
|
||||
|
||||
//$2115 VMAIN
|
||||
bool vram_incmode;
|
||||
uint8 vram_mapping;
|
||||
|
|
|
@ -5,33 +5,123 @@ void sPPU::Screen::scanline() {
|
|||
}
|
||||
|
||||
void sPPU::Screen::run() {
|
||||
unsigned priority = 0;
|
||||
uint16 color;
|
||||
enum source_t { BG1, BG2, BG3, BG4, OAM, BACK };
|
||||
|
||||
unsigned priority_sub = 0;
|
||||
uint16 color_sub;
|
||||
source_t source_sub;
|
||||
|
||||
if(self.bg1.output.sub.valid) {
|
||||
priority_sub = self.bg1.output.sub.priority;
|
||||
color_sub = self.bg1.output.sub.color;
|
||||
source_sub = BG1;
|
||||
}
|
||||
if(self.bg2.output.sub.valid && self.bg2.output.sub.priority > priority_sub) {
|
||||
priority_sub = self.bg2.output.sub.priority;
|
||||
color_sub = self.bg2.output.sub.color;
|
||||
source_sub = BG2;
|
||||
}
|
||||
if(self.bg3.output.sub.valid && self.bg3.output.sub.priority > priority_sub) {
|
||||
priority_sub = self.bg3.output.sub.priority;
|
||||
color_sub = self.bg3.output.sub.color;
|
||||
source_sub = BG3;
|
||||
}
|
||||
if(self.bg4.output.sub.valid && self.bg4.output.sub.priority > priority_sub) {
|
||||
priority_sub = self.bg4.output.sub.priority;
|
||||
color_sub = self.bg4.output.sub.color;
|
||||
source_sub = BG4;
|
||||
}
|
||||
if(self.oam.output.sub.valid && self.oam.output.sub.priority > priority_sub) {
|
||||
priority_sub = self.oam.output.sub.priority;
|
||||
color_sub = self.oam.output.sub.color;
|
||||
source_sub = OAM;
|
||||
}
|
||||
if(priority_sub == 0) {
|
||||
if(self.regs.bgmode == 5 || self.regs.bgmode == 6) {
|
||||
color_sub = memory::cgram[0] + (memory::cgram[1] << 8);
|
||||
} else {
|
||||
color_sub = regs.color_rgb;
|
||||
}
|
||||
source_sub = BACK;
|
||||
}
|
||||
|
||||
unsigned priority_main = 0;
|
||||
uint16 color_main;
|
||||
source_t source_main;
|
||||
|
||||
if(self.bg1.output.main.valid) {
|
||||
priority = self.bg1.output.main.priority;
|
||||
color = self.bg1.output.main.color;
|
||||
priority_main = self.bg1.output.main.priority;
|
||||
color_main = self.bg1.output.main.color;
|
||||
source_main = BG1;
|
||||
if(regs.bg1_color_enable && self.window.output.sub.color_enable) color_main = addsub(color_main, color_sub);
|
||||
}
|
||||
if(self.bg2.output.main.valid && self.bg2.output.main.priority > priority) {
|
||||
priority = self.bg2.output.main.priority;
|
||||
color = self.bg2.output.main.color;
|
||||
if(self.bg2.output.main.valid && self.bg2.output.main.priority > priority_main) {
|
||||
priority_main = self.bg2.output.main.priority;
|
||||
color_main = self.bg2.output.main.color;
|
||||
source_main = BG2;
|
||||
if(regs.bg2_color_enable && self.window.output.sub.color_enable) color_main = addsub(color_main, color_sub);
|
||||
}
|
||||
if(self.bg3.output.main.valid && self.bg3.output.main.priority > priority) {
|
||||
priority = self.bg3.output.main.priority;
|
||||
color = self.bg3.output.main.color;
|
||||
if(self.bg3.output.main.valid && self.bg3.output.main.priority > priority_main) {
|
||||
priority_main = self.bg3.output.main.priority;
|
||||
color_main = self.bg3.output.main.color;
|
||||
source_main = BG3;
|
||||
if(regs.bg3_color_enable && self.window.output.sub.color_enable) color_main = addsub(color_main, color_sub);
|
||||
}
|
||||
if(self.bg4.output.main.valid && self.bg4.output.main.priority > priority) {
|
||||
priority = self.bg4.output.main.priority;
|
||||
color = self.bg4.output.main.color;
|
||||
if(self.bg4.output.main.valid && self.bg4.output.main.priority > priority_main) {
|
||||
priority_main = self.bg4.output.main.priority;
|
||||
color_main = self.bg4.output.main.color;
|
||||
source_main = BG4;
|
||||
if(regs.bg4_color_enable && self.window.output.sub.color_enable) color_main = addsub(color_main, color_sub);
|
||||
}
|
||||
if(priority == 0) {
|
||||
color = (memory::cgram[1] << 8) + (memory::cgram[0] << 0);
|
||||
if(self.oam.output.main.valid && self.oam.output.main.priority > priority_main) {
|
||||
priority_main = self.oam.output.main.priority;
|
||||
color_main = self.oam.output.main.color;
|
||||
source_main = OAM;
|
||||
if(self.oam.output.main.palette >= 192) {
|
||||
if(regs.oam_color_enable && self.window.output.sub.color_enable) color_main = addsub(color_main, color_sub);
|
||||
}
|
||||
}
|
||||
if(priority_main == 0) {
|
||||
color_main = memory::cgram[0] + (memory::cgram[1] << 8);
|
||||
source_main = BACK;
|
||||
if(regs.back_color_enable && self.window.output.sub.color_enable) color_main = addsub(color_main, color_sub, !regs.addsub_mode || source_sub != BACK);
|
||||
}
|
||||
|
||||
color = light_table[self.regs.display_brightness][color];
|
||||
if(self.regs.display_disabled) color = 0;
|
||||
*output++ = color;
|
||||
*output++ = color;
|
||||
if(self.window.output.main.color_enable == false) {
|
||||
if(self.window.output.sub.color_enable == false) {
|
||||
color_main = 0x0000;
|
||||
goto plot;
|
||||
}
|
||||
color_main = 0x0000;
|
||||
}
|
||||
|
||||
color_main = light_table[self.regs.display_brightness][color_main];
|
||||
if(self.regs.display_disabled) color_main = 0;
|
||||
plot:
|
||||
*output++ = color_main;
|
||||
*output++ = color_main;
|
||||
}
|
||||
|
||||
uint16 sPPU::Screen::addsub(unsigned x, unsigned y, bool allow_halve) {
|
||||
bool halve = allow_halve && regs.color_halve && self.window.output.main.color_enable;
|
||||
|
||||
if(!regs.color_mode) {
|
||||
if(!halve) {
|
||||
unsigned sum = x + y;
|
||||
unsigned carry = (sum - ((x ^ y) & 0x0421)) & 0x8420;
|
||||
return (sum - carry) | (carry - (carry >> 5));
|
||||
} else {
|
||||
return (x + y - ((x ^ y) & 0x0421)) >> 1;
|
||||
}
|
||||
} else {
|
||||
unsigned diff = x - y + 0x8420;
|
||||
unsigned borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420;
|
||||
if(!halve) {
|
||||
return (diff - borrow) & (borrow - (borrow >> 5));
|
||||
} else {
|
||||
return (((diff - borrow) & (borrow - (borrow >> 5))) & 0x7bde) >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sPPU::Screen::Screen(sPPU &self) : self(self) {
|
||||
|
|
|
@ -3,6 +3,25 @@ public:
|
|||
sPPU &self;
|
||||
uint16 *output;
|
||||
|
||||
struct {
|
||||
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;
|
||||
|
||||
uint8 color_b;
|
||||
uint8 color_g;
|
||||
uint8 color_r;
|
||||
uint16 color_rgb;
|
||||
} regs;
|
||||
|
||||
void scanline();
|
||||
void run();
|
||||
|
||||
|
@ -10,4 +29,5 @@ public:
|
|||
|
||||
private:
|
||||
uint16 light_table[16][32768];
|
||||
uint16 addsub(unsigned x, unsigned y, bool allow_halve = true);
|
||||
};
|
||||
|
|
|
@ -6,6 +6,8 @@ namespace SNES {
|
|||
#include "background/background.cpp"
|
||||
#include "mmio/mmio.cpp"
|
||||
#include "screen/screen.cpp"
|
||||
#include "sprite/sprite.cpp"
|
||||
#include "window/window.cpp"
|
||||
|
||||
#if !defined(DEBUGGER)
|
||||
sPPU ppu;
|
||||
|
@ -19,28 +21,14 @@ void sPPU::enter() {
|
|||
|
||||
add_clocks(88);
|
||||
|
||||
//mosaic
|
||||
if(vcounter() == 1) {
|
||||
bg1.regs.mosaic_y = 1;
|
||||
bg2.regs.mosaic_y = 1;
|
||||
bg3.regs.mosaic_y = 1;
|
||||
bg4.regs.mosaic_y = 1;
|
||||
} else {
|
||||
if(!bg1.regs.mosaic || !regs.mosaic_countdown) bg1.regs.mosaic_y = vcounter();
|
||||
if(!bg2.regs.mosaic || !regs.mosaic_countdown) bg2.regs.mosaic_y = vcounter();
|
||||
if(!bg3.regs.mosaic || !regs.mosaic_countdown) bg3.regs.mosaic_y = vcounter();
|
||||
if(!bg4.regs.mosaic || !regs.mosaic_countdown) bg4.regs.mosaic_y = vcounter();
|
||||
if(!regs.mosaic_countdown) regs.mosaic_countdown = regs.mosaic_size + 1;
|
||||
regs.mosaic_countdown--;
|
||||
}
|
||||
|
||||
if(vcounter() >= 1 && vcounter() <= 224) {
|
||||
screen.scanline();
|
||||
for(unsigned n = 0; n < 256; n++) {
|
||||
bg1.run();
|
||||
bg2.run();
|
||||
bg3.run();
|
||||
bg4.run();
|
||||
oam.run();
|
||||
window.run();
|
||||
screen.run();
|
||||
add_clocks(4);
|
||||
}
|
||||
|
@ -76,11 +64,19 @@ void sPPU::reset() {
|
|||
|
||||
void sPPU::scanline() {
|
||||
if(vcounter() == 0) frame();
|
||||
bg1.scanline();
|
||||
bg2.scanline();
|
||||
bg3.scanline();
|
||||
bg4.scanline();
|
||||
oam.scanline();
|
||||
window.scanline();
|
||||
screen.scanline();
|
||||
}
|
||||
|
||||
void sPPU::frame() {
|
||||
PPU::frame();
|
||||
system.frame();
|
||||
oam.frame();
|
||||
}
|
||||
|
||||
sPPU::sPPU() :
|
||||
|
@ -88,6 +84,8 @@ bg1(*this, Background::ID::BG1),
|
|||
bg2(*this, Background::ID::BG2),
|
||||
bg3(*this, Background::ID::BG3),
|
||||
bg4(*this, Background::ID::BG4),
|
||||
oam(*this),
|
||||
window(*this),
|
||||
screen(*this) {
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,15 @@ public:
|
|||
#include "background/background.hpp"
|
||||
#include "mmio/mmio.hpp"
|
||||
#include "screen/screen.hpp"
|
||||
#include "sprite/sprite.hpp"
|
||||
#include "window/window.hpp"
|
||||
|
||||
Background bg1;
|
||||
Background bg2;
|
||||
Background bg3;
|
||||
Background bg4;
|
||||
Sprite oam;
|
||||
Window window;
|
||||
Screen screen;
|
||||
|
||||
void enter();
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
#ifdef SPPU_CPP
|
||||
|
||||
void sPPU::Sprite::address_reset() {
|
||||
self.regs.oam_addr = self.regs.oam_baseaddr << 1;
|
||||
regs.first_sprite = (self.regs.oam_priority == false ? 0 : (self.regs.oam_addr >> 2) & 127);
|
||||
}
|
||||
|
||||
void sPPU::Sprite::frame() {
|
||||
regs.time_over = false;
|
||||
regs.range_over = false;
|
||||
}
|
||||
|
||||
void sPPU::Sprite::scanline() {
|
||||
state.x = 0;
|
||||
state.y = self.vcounter();
|
||||
|
||||
if(state.y == 225 && self.regs.display_disabled == false) address_reset();
|
||||
if(state.y < 1 || state.y > 224) return;
|
||||
|
||||
const uint8 *tableA = memory::oam.data();
|
||||
const uint8 *tableB = memory::oam.data() + 512;
|
||||
|
||||
for(unsigned i = 0; i < 128; i++) {
|
||||
bool x = *tableB & (1 << ((i & 3) << 1));
|
||||
bool size = *tableB & (2 << ((i & 3) << 1));
|
||||
|
||||
switch(regs.base_size) {
|
||||
case 0: list[i].width = (!size ? 8 : 16);
|
||||
list[i].height = (!size ? 8 : 16);
|
||||
break;
|
||||
case 1: list[i].width = (!size ? 8 : 32);
|
||||
list[i].height = (!size ? 8 : 32);
|
||||
break;
|
||||
case 2: list[i].width = (!size ? 8 : 64);
|
||||
list[i].height = (!size ? 8 : 64);
|
||||
break;
|
||||
case 3: list[i].width = (!size ? 16 : 32);
|
||||
list[i].height = (!size ? 16 : 32);
|
||||
break;
|
||||
case 4: list[i].width = (!size ? 16 : 64);
|
||||
list[i].height = (!size ? 16 : 64);
|
||||
break;
|
||||
case 5: list[i].width = (!size ? 32 : 64);
|
||||
list[i].height = (!size ? 32 : 64);
|
||||
break;
|
||||
case 6: list[i].width = (!size ? 16 : 32);
|
||||
list[i].height = (!size ? 32 : 64);
|
||||
break;
|
||||
case 7: list[i].width = (!size ? 16 : 32);
|
||||
list[i].height = (!size ? 32 : 32);
|
||||
break;
|
||||
}
|
||||
|
||||
list[i].x = (x << 8) + tableA[0];
|
||||
list[i].y = (tableA[1] + 1) & 0xff;
|
||||
list[i].character = tableA[2];
|
||||
list[i].vflip = tableA[3] & 0x80;
|
||||
list[i].hflip = tableA[3] & 0x40;
|
||||
list[i].priority = (tableA[3] >> 4) & 3;
|
||||
list[i].palette = (tableA[3] >> 1) & 7;
|
||||
list[i].nameselect = tableA[3] & 1;
|
||||
|
||||
tableA += 4;
|
||||
if((i & 3) == 3) tableB++;
|
||||
}
|
||||
|
||||
state.item_count = 0;
|
||||
state.tile_count = 0;
|
||||
memset(state.output_priority, 0xff, 256);
|
||||
memset(state.item_list, 0xff, 32);
|
||||
for(unsigned i = 0; i < 34; i++) state.tile_list[i].tile = 0xffff;
|
||||
|
||||
for(unsigned i = 0; i < 128; i++) {
|
||||
state.active_sprite = (i + regs.first_sprite) & 127;
|
||||
if(on_scanline() == false) continue;
|
||||
if(state.item_count++ >= 32) break;
|
||||
state.item_list[state.item_count - 1] = (i + regs.first_sprite) & 127;
|
||||
}
|
||||
|
||||
for(signed i = 31; i >= 0; i--) {
|
||||
if(state.item_list[i] == 0xff) continue;
|
||||
state.active_sprite = state.item_list[i];
|
||||
load_tiles();
|
||||
}
|
||||
|
||||
regs.time_over |= (state.tile_count > 34);
|
||||
regs.range_over |= (state.item_count > 32);
|
||||
|
||||
for(unsigned i = 0; i < 34; i++) {
|
||||
if(state.tile_list[i].tile == 0xffff) continue;
|
||||
render_tile(i);
|
||||
}
|
||||
}
|
||||
|
||||
void sPPU::Sprite::run() {
|
||||
output.main.valid = false;
|
||||
output.sub.valid = false;
|
||||
|
||||
unsigned x = state.x++;
|
||||
|
||||
if(state.output_priority[x] != 0xff) {
|
||||
unsigned priority_table[] = { regs.priority0, regs.priority1, regs.priority2, regs.priority3 };
|
||||
unsigned priority = priority_table[state.output_priority[x]];
|
||||
unsigned palette = state.output_palette[x] << 1;
|
||||
|
||||
if(regs.main_enabled) {
|
||||
output.main.valid = true;
|
||||
output.main.color = memory::cgram[palette + 0] + (memory::cgram[palette + 1] << 8);
|
||||
output.main.palette = state.output_palette[x];
|
||||
output.main.priority = priority;
|
||||
}
|
||||
|
||||
if(regs.sub_enabled) {
|
||||
output.sub.valid = true;
|
||||
output.sub.color = memory::cgram[palette + 0] + (memory::cgram[palette + 1] << 8);
|
||||
output.sub.palette = state.output_palette[x];
|
||||
output.sub.priority = priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool sPPU::Sprite::on_scanline() {
|
||||
SpriteItem &sprite = list[state.active_sprite];
|
||||
if(sprite.x > 256 && (sprite.x + sprite.width - 1) < 512) return false;
|
||||
|
||||
signed height = (regs.interlace == false ? sprite.height : (sprite.height >> 1));
|
||||
if(state.y >= sprite.y && state.y < (sprite.y + height)) return true;
|
||||
if((sprite.y + height) >= 256 && state.y < ((sprite.y + height) & 255)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void sPPU::Sprite::load_tiles() {
|
||||
SpriteItem &sprite = list[state.active_sprite];
|
||||
unsigned tile_width = sprite.width >> 3;
|
||||
signed x = sprite.x;
|
||||
signed y = (state.y - sprite.y) & 0xff;
|
||||
|
||||
if(sprite.vflip) {
|
||||
if(sprite.width == sprite.height) {
|
||||
y = (sprite.height - 1) - y;
|
||||
} else {
|
||||
y = (y < sprite.width) ? ((sprite.width - 1) - y) : (sprite.width + ((sprite.width - 1) - (y - sprite.width)));
|
||||
}
|
||||
}
|
||||
|
||||
if(regs.interlace) {
|
||||
y = (sprite.vflip == false ? y + self.field() : y - self.field());
|
||||
}
|
||||
|
||||
x &= 511;
|
||||
y &= 255;
|
||||
|
||||
uint16 tiledata_addr = regs.tiledata_addr;
|
||||
uint16 chrx = (sprite.character >> 0) & 15;
|
||||
uint16 chry = (sprite.character >> 4) & 15;
|
||||
if(sprite.nameselect) {
|
||||
tiledata_addr += (256 * 32) + (regs.nameselect << 13);
|
||||
}
|
||||
chry += (y >> 3);
|
||||
chry &= 15;
|
||||
chry <<= 4;
|
||||
|
||||
for(unsigned tx = 0; tx < tile_width; tx++) {
|
||||
unsigned sx = (x + (tx << 3)) & 511;
|
||||
if(x != 256 && sx >= 256 && (sx + 7) < 512) continue;
|
||||
|
||||
if(state.tile_count++ >= 34) break;
|
||||
unsigned n = state.tile_count - 1;
|
||||
state.tile_list[n].x = sx;
|
||||
state.tile_list[n].y = y;
|
||||
state.tile_list[n].priority = sprite.priority;
|
||||
state.tile_list[n].palette = 128 + (sprite.palette << 4);
|
||||
state.tile_list[n].hflip = sprite.hflip;
|
||||
|
||||
unsigned mx = (sprite.hflip == false) ? tx : ((tile_width - 1) - tx);
|
||||
unsigned pos = tiledata_addr + ((chry + ((chrx + mx) & 15)) << 5);
|
||||
state.tile_list[n].tile = (pos >> 5) & 0x07ff;
|
||||
}
|
||||
}
|
||||
|
||||
void sPPU::Sprite::render_tile(unsigned tile) {
|
||||
TileItem &item = state.tile_list[tile];
|
||||
|
||||
unsigned sx = item.x;
|
||||
uint16 addr = (item.tile << 5) + ((item.y & 7) * 2);
|
||||
for(unsigned x = 0; x < 8; x++) {
|
||||
sx &= 511;
|
||||
if(sx < 256) {
|
||||
unsigned px = (item.hflip == false ? x : (7 - x));
|
||||
unsigned mask = 0x80 >> (px & 7);
|
||||
|
||||
uint8 d0 = memory::vram[addr + 0];
|
||||
uint8 d1 = memory::vram[addr + 1];
|
||||
uint8 d2 = memory::vram[addr + 16];
|
||||
uint8 d3 = memory::vram[addr + 17];
|
||||
|
||||
unsigned color;
|
||||
color = ((bool)(d0 & mask)) << 0;
|
||||
color |= ((bool)(d1 & mask)) << 1;
|
||||
color |= ((bool)(d2 & mask)) << 2;
|
||||
color |= ((bool)(d3 & mask)) << 3;
|
||||
|
||||
if(color) {
|
||||
color += item.palette;
|
||||
state.output_palette[sx] = color;
|
||||
state.output_priority[sx] = item.priority;
|
||||
}
|
||||
}
|
||||
sx++;
|
||||
}
|
||||
}
|
||||
|
||||
sPPU::Sprite::Sprite(sPPU &self) : self(self) {
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,79 @@
|
|||
class Sprite {
|
||||
public:
|
||||
sPPU &self;
|
||||
|
||||
struct {
|
||||
bool main_enabled;
|
||||
bool sub_enabled;
|
||||
bool interlace;
|
||||
|
||||
uint8 base_size;
|
||||
uint8 nameselect;
|
||||
uint16 tiledata_addr;
|
||||
uint8 first_sprite;
|
||||
|
||||
unsigned priority0;
|
||||
unsigned priority1;
|
||||
unsigned priority2;
|
||||
unsigned priority3;
|
||||
|
||||
bool time_over;
|
||||
bool range_over;
|
||||
} regs;
|
||||
|
||||
struct SpriteItem {
|
||||
uint8 width;
|
||||
uint8 height;
|
||||
uint16 x;
|
||||
uint16 y;
|
||||
uint8 character;
|
||||
bool nameselect;
|
||||
bool vflip;
|
||||
bool hflip;
|
||||
uint8 palette;
|
||||
uint8 priority;
|
||||
} list[128];
|
||||
|
||||
struct TileItem {
|
||||
uint16 x;
|
||||
uint16 y;
|
||||
uint16 priority;
|
||||
uint16 palette;
|
||||
uint16 tile;
|
||||
bool hflip;
|
||||
};
|
||||
|
||||
struct State {
|
||||
unsigned x;
|
||||
unsigned y;
|
||||
|
||||
unsigned item_count;
|
||||
unsigned tile_count;
|
||||
uint8 output_palette[256];
|
||||
uint8 output_priority[256];
|
||||
uint8 item_list[32];
|
||||
TileItem tile_list[34];
|
||||
unsigned active_sprite;
|
||||
} state;
|
||||
|
||||
struct {
|
||||
struct {
|
||||
bool valid;
|
||||
uint16 color;
|
||||
unsigned palette;
|
||||
unsigned priority;
|
||||
} main, sub;
|
||||
} output;
|
||||
|
||||
void address_reset();
|
||||
void frame();
|
||||
void scanline();
|
||||
void run();
|
||||
|
||||
Sprite(sPPU &self);
|
||||
|
||||
private:
|
||||
bool on_scanline();
|
||||
void load_tiles();
|
||||
void render_tile(unsigned tile);
|
||||
};
|
|
@ -0,0 +1,115 @@
|
|||
#ifdef SPPU_CPP
|
||||
|
||||
void sPPU::Window::scanline() {
|
||||
state.x = 0;
|
||||
}
|
||||
|
||||
void sPPU::Window::run() {
|
||||
bool main, sub;
|
||||
|
||||
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.valid = false;
|
||||
if(sub) self.bg1.output.sub.valid = false;
|
||||
|
||||
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.valid = false;
|
||||
if(sub) self.bg2.output.sub.valid = false;
|
||||
|
||||
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.valid = false;
|
||||
if(sub) self.bg3.output.sub.valid = false;
|
||||
|
||||
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.valid = false;
|
||||
if(sub) self.bg4.output.sub.valid = false;
|
||||
|
||||
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.oam.output.main.valid = false;
|
||||
if(sub) self.oam.output.sub.valid = false;
|
||||
|
||||
test(
|
||||
main, sub,
|
||||
regs.col_one_enable, regs.col_one_invert,
|
||||
regs.col_two_enable, regs.col_two_invert,
|
||||
regs.col_mask, true, true
|
||||
);
|
||||
|
||||
switch(regs.col_main_mask) {
|
||||
case 0: main = true; break;
|
||||
case 1: break;
|
||||
case 2: main = !main; break;
|
||||
case 3: main = false; break;
|
||||
}
|
||||
|
||||
switch(regs.col_sub_mask) {
|
||||
case 0: sub = true; break;
|
||||
case 1: break;
|
||||
case 2: sub = !sub; break;
|
||||
case 3: sub = false; break;
|
||||
}
|
||||
|
||||
output.main.color_enable = main;
|
||||
output.sub.color_enable = sub;
|
||||
|
||||
state.x++;
|
||||
}
|
||||
|
||||
void sPPU::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
|
||||
) {
|
||||
unsigned x = state.x;
|
||||
bool output;
|
||||
|
||||
if(one_enable == false && two_enable == false) {
|
||||
output = false;
|
||||
} else if(one_enable == true && two_enable == false) {
|
||||
output = (x >= regs.one_left && x <= regs.one_right) ^ one_invert;
|
||||
} else if(one_enable == false && two_enable == true) {
|
||||
output = (x >= regs.two_left && x <= regs.two_right) ^ two_invert;
|
||||
} else {
|
||||
bool one = (x >= regs.one_left && x <= regs.one_right) ^ one_invert;
|
||||
bool two = (x >= regs.two_left && x <= regs.two_right) ^ two_invert;
|
||||
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;
|
||||
}
|
||||
|
||||
sPPU::Window::Window(sPPU &self) : self(self) {
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,85 @@
|
|||
class Window {
|
||||
public:
|
||||
sPPU &self;
|
||||
|
||||
struct {
|
||||
bool bg1_one_enable;
|
||||
bool bg1_one_invert;
|
||||
bool bg1_two_enable;
|
||||
bool bg1_two_invert;
|
||||
|
||||
bool bg2_one_enable;
|
||||
bool bg2_one_invert;
|
||||
bool bg2_two_enable;
|
||||
bool bg2_two_invert;
|
||||
|
||||
bool bg3_one_enable;
|
||||
bool bg3_one_invert;
|
||||
bool bg3_two_enable;
|
||||
bool bg3_two_invert;
|
||||
|
||||
bool bg4_one_enable;
|
||||
bool bg4_one_invert;
|
||||
bool bg4_two_enable;
|
||||
bool bg4_two_invert;
|
||||
|
||||
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;
|
||||
|
||||
uint8 bg1_mask;
|
||||
uint8 bg2_mask;
|
||||
uint8 bg3_mask;
|
||||
uint8 bg4_mask;
|
||||
uint8 oam_mask;
|
||||
uint8 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;
|
||||
|
||||
uint8 col_main_mask;
|
||||
uint8 col_sub_mask;
|
||||
} regs;
|
||||
|
||||
struct {
|
||||
unsigned x;
|
||||
} state;
|
||||
|
||||
struct {
|
||||
struct {
|
||||
bool color_enable;
|
||||
} main, sub;
|
||||
} output;
|
||||
|
||||
void scanline();
|
||||
void run();
|
||||
|
||||
Window(sPPU &self);
|
||||
|
||||
private:
|
||||
void 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
|
||||
);
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
static const char bsnesVersion[] = "063.08";
|
||||
static const char bsnesVersion[] = "063.09";
|
||||
static const char bsnesTitle[] = "bsnes";
|
||||
static const unsigned bsnesSerializerVersion = 9;
|
||||
|
||||
|
|
Loading…
Reference in New Issue