bsnes-plus/bsnes/snes/ppu/screen/screen.cpp
devinacker 59253c9e17 make power-on state less deterministic
(a la later bsnes/higan behavior; mostly affects the accuracy core)
2016-10-29 22:46:12 -04:00

191 lines
6.6 KiB
C++

#ifdef PPU_CPP
void PPU::Screen::scanline() {
output = self.output + self.vcounter() * 1024;
if(self.display.interlace && self.field()) output += 512;
//the first hires pixel of each scanline is transparent
//note: exact value initializations are not confirmed on hardware
math.main.color = math.sub.color = get_color(0);
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.transparent = true;
math.addsub_mode = false;
math.color_halve = regs.color_halve && !regs.addsub_mode && math.main.color_enable;
}
void PPU::Screen::run() {
if(ppu.vcounter() == 0) return;
bool hires = self.regs.pseudo_hires || self.regs.bgmode == 5 || self.regs.bgmode == 6;
uint16 sscolor = get_pixel_sub(hires);
uint16 mscolor = get_pixel_main();
*output++ = light_table[self.regs.display_brightness][hires ? sscolor : mscolor];
*output++ = light_table[self.regs.display_brightness][mscolor];
}
uint16 PPU::Screen::get_pixel_sub(bool hires) {
if(self.regs.display_disable) return 0;
unsigned 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);
} else {
math.sub.color = get_color(self.bg1.output.sub.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(self.bg3.output.sub.priority > priority) {
priority = self.bg3.output.sub.priority;
math.sub.color = get_color(self.bg3.output.sub.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(self.oam.output.sub.priority > priority) {
priority = self.oam.output.sub.priority;
math.sub.color = get_color(self.oam.output.sub.palette);
}
if(math.transparent = (priority == 0)) math.sub.color = get_color(0);
if(!hires) return 0;
if(!math.sub.color_enable) return math.main.color_enable ? math.sub.color : 0;
return addsub(math.main.color_enable ? math.sub.color : 0,
math.addsub_mode ? math.main.color : regs.color);
}
uint16 PPU::Screen::get_pixel_main() {
if(self.regs.display_disable) return 0;
unsigned 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);
} else {
math.main.color = get_color(self.bg1.output.main.palette);
}
math.sub.color_enable = regs.bg1_color_enable;
}
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(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(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(self.oam.output.main.priority > priority) {
priority = self.oam.output.main.priority;
math.main.color = get_color(self.oam.output.main.palette);
math.sub.color_enable = (regs.oam_color_enable && self.oam.output.main.palette >= 192);
}
if(priority == 0) {
math.main.color = get_color(0);
math.sub.color_enable = regs.back_color_enable;
}
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 : 0;
if(regs.addsub_mode && math.transparent) {
math.addsub_mode = false;
math.color_halve = false;
} else {
math.addsub_mode = regs.addsub_mode;
math.color_halve = regs.color_halve && math.main.color_enable;
}
return addsub(math.main.color_enable ? math.main.color : 0,
math.addsub_mode ? math.sub.color : regs.color);
}
uint16 PPU::Screen::addsub(unsigned x, unsigned y) {
if(!regs.color_mode) {
if(!math.color_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(!math.color_halve) {
return (diff - borrow) & (borrow - (borrow >> 5));
} else {
return (((diff - borrow) & (borrow - (borrow >> 5))) & 0x7bde) >> 1;
}
}
}
uint16 PPU::Screen::get_color(unsigned palette) {
palette <<= 1;
self.regs.cgram_iaddr = palette;
return memory::cgram[palette + 0] + (memory::cgram[palette + 1] << 8);
}
uint16 PPU::Screen::get_direct_color(unsigned palette, unsigned tile) {
//palette = -------- BBGGGRRR
//tile = ---bgr-- --------
//output = 0BBb00GG Gg0RRRr0
return ((palette << 7) & 0x6000) + ((tile >> 0) & 0x1000)
+ ((palette << 4) & 0x0380) + ((tile >> 5) & 0x0040)
+ ((palette << 2) & 0x001c) + ((tile >> 9) & 0x0002);
}
void PPU::Screen::reset() {
regs.addsub_mode = random(0);
regs.direct_color = random(0);
regs.color_mode = random(0);
regs.color_halve = random(0);
regs.bg1_color_enable = random(0);
regs.bg2_color_enable = random(0);
regs.bg3_color_enable = random(0);
regs.bg4_color_enable = random(0);
regs.oam_color_enable = random(0);
regs.back_color_enable = random(0);
regs.color = random(0);
math.main.color = 0;
math.sub.color = 0;
math.transparent = false;
math.main.color_enable = false;
math.sub.color_enable = false;
math.addsub_mode = false;
math.color_halve = false;
}
PPU::Screen::Screen(PPU &self) : self(self) {
for(unsigned l = 0; l < 16; l++) {
for(unsigned r = 0; r < 32; r++) {
for(unsigned g = 0; g < 32; g++) {
for(unsigned b = 0; b < 32; b++) {
double luma = (double)l / 15.0;
unsigned ar = (luma * r + 0.5);
unsigned ag = (luma * g + 0.5);
unsigned ab = (luma * b + 0.5);
light_table[l][(r << 10) + (g << 5) + b] = (ab << 10) + (ag << 5) + ar;
}
}
}
}
}
#endif