mirror of https://github.com/bsnes-emu/bsnes.git
Update to bsnes v063r08 release.
No binary, this is just a point release. I have basic lores BG1-4 rendering with mosaic added. No offset-per- tile, no windowing, no color math (requires windowing), no sprites, no hires, no interlace, no mode7. It's enough to see how powerful the concept is already, though. - Battle Blaze intro looks just fine (can't beat a battle because I can't see my sprites or save states yet) - Dai Kaijuu Monogatari II stat bar looks fine (no duplicated line) - Super Metroid looks fine (no extra status bar line) - Air Strike Patrol shows the translucent shadow for your plane (but the left-hand scrolling is glitchy ... not sure what's up yet) Speed is ... yeah, it's bad. About 50-60% speed. But it can get better, I'm being really lazy and completely recomputing everything for each pixel. A very large number of these operations can be cached. I'm going to wait until the renderer matches the quality of the scanline-renderer before optimizing; and I'm not going to push too far on optimizing this (eg I probably won't bring back the tiledata planar->packed conversion cache.) I'm designing this similar to MooglyGuy's N64 renderer, putting each component in its own class. So far I'm really liking the concept.
This commit is contained in:
parent
b11f22f517
commit
efa7879c6d
BIN
bsnes-bppu.exe
BIN
bsnes-bppu.exe
Binary file not shown.
BIN
bsnes-sppu.exe
BIN
bsnes-sppu.exe
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
snes_core = sMemory sCPU sSMP sDSP bPPU
|
||||
snes_core = sMemory sCPU sSMP sDSP sPPU
|
||||
|
||||
snes_objects := libco
|
||||
snes_objects += snes-system
|
||||
|
|
|
@ -75,6 +75,7 @@ void sCPU::mmio_w4200(uint8 data) {
|
|||
|
||||
//WRIO
|
||||
void sCPU::mmio_w4201(uint8 data) {
|
||||
scheduler.sync_cpuppu();
|
||||
if((status.pio & 0x80) && !(data & 0x80)) {
|
||||
ppu.latch_counters();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
#ifdef SPPU_CPP
|
||||
|
||||
void sPPU::Background::run() {
|
||||
output.main.valid = false;
|
||||
output.sub.valid = false;
|
||||
if(regs.mode == Mode::Inactive) return;
|
||||
if(regs.main_enabled == false && regs.sub_enabled == false) return;
|
||||
|
||||
if(self.hcounter() < 88) return;
|
||||
if(self.hcounter() >= 1112) return;
|
||||
if(self.hcounter() & 2) return;
|
||||
|
||||
unsigned x = (self.hcounter() - 88) >> 2;
|
||||
unsigned y = regs.mosaic_y;
|
||||
|
||||
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);
|
||||
|
||||
bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6);
|
||||
unsigned width = (!hires ? 256 : 512);
|
||||
|
||||
unsigned tile_height = (regs.tile_size == TileSize::Size8x8 ? 3 : 4);
|
||||
unsigned tile_width = (!hires ? tile_height : 4);
|
||||
|
||||
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--;
|
||||
|
||||
unsigned screen_y = (regs.screen_size & 2 ? (32 << 5) : 0);
|
||||
unsigned screen_x = (regs.screen_size & 1 ? (32 << 5) : 0);
|
||||
if(regs.screen_size == 3) screen_y <<= 1;
|
||||
|
||||
unsigned hscroll = regs.hoffset;
|
||||
unsigned vscroll = regs.voffset;
|
||||
if(hires) {
|
||||
hscroll <<= 1;
|
||||
}
|
||||
|
||||
unsigned hoffset = hscroll + mosaic_table[regs.mosaic][x];
|
||||
unsigned voffset = vscroll + y;
|
||||
|
||||
hoffset &= mask_x;
|
||||
voffset &= mask_y;
|
||||
|
||||
unsigned tile_number; {
|
||||
unsigned tx = hoffset >> tile_width;
|
||||
unsigned ty = voffset >> tile_height;
|
||||
|
||||
uint16 pos = ((ty & 0x1f) << 5) + (tx & 0x1f);
|
||||
if(ty & 0x20) pos += screen_y;
|
||||
if(tx & 0x20) pos += screen_x;
|
||||
|
||||
uint16 addr = regs.screen_addr + (pos << 1);
|
||||
tile_number = memory::vram[addr + 0] + (memory::vram[addr + 1] << 8);
|
||||
}
|
||||
|
||||
bool mirror_y = tile_number & 0x8000;
|
||||
bool mirror_x = tile_number & 0x4000;
|
||||
unsigned priority = (tile_number & 0x2000 ? regs.priority1 : regs.priority0);
|
||||
unsigned palette_number = (tile_number >> 10) & 7;
|
||||
unsigned palette_index = palette_offset + (palette_number << palette_size);
|
||||
|
||||
if(tile_width == 4 && (bool)(hoffset & 8) != mirror_x) tile_number += 1;
|
||||
if(tile_height == 4 && (bool)(voffset & 8) != mirror_y) tile_number += 16;
|
||||
tile_number &= 0x03ff;
|
||||
tile_number += tiledata_index;
|
||||
tile_number &= tile_mask;
|
||||
|
||||
if(mirror_y) voffset ^= 7;
|
||||
if(mirror_x) hoffset ^= 7;
|
||||
|
||||
unsigned color = get_color(hoffset, voffset, tile_number);
|
||||
if(color == 0) return;
|
||||
|
||||
unsigned palette_addr = (palette_index + color) << 1;
|
||||
unsigned output_color = memory::cgram[palette_addr + 0] + (memory::cgram[palette_addr + 1] << 8);
|
||||
|
||||
if(regs.main_enabled) {
|
||||
output.main.valid = true;
|
||||
output.main.color = output_color;
|
||||
output.main.priority = priority;
|
||||
}
|
||||
|
||||
if(regs.sub_enabled) {
|
||||
output.sub.valid = true;
|
||||
output.sub.color = output_color;
|
||||
output.sub.priority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned sPPU::Background::get_color(unsigned x, unsigned y, uint16 offset) {
|
||||
unsigned mask = 0x80 >> (x & 7);
|
||||
|
||||
switch(regs.mode) {
|
||||
case Background::Mode::BPP2: {
|
||||
offset = (offset * 16) + ((y & 7) * 2);
|
||||
|
||||
unsigned d0 = memory::vram[offset + 0];
|
||||
unsigned d1 = memory::vram[offset + 1];
|
||||
|
||||
return (((bool)(d0 & mask)) << 0)
|
||||
+ (((bool)(d1 & mask)) << 1);
|
||||
}
|
||||
|
||||
case Background::Mode::BPP4: {
|
||||
offset = (offset * 32) + ((y & 7) * 2);
|
||||
|
||||
unsigned d0 = memory::vram[offset + 0];
|
||||
unsigned d1 = memory::vram[offset + 1];
|
||||
unsigned d2 = memory::vram[offset + 16];
|
||||
unsigned d3 = memory::vram[offset + 17];
|
||||
|
||||
return (((bool)(d0 & mask)) << 0)
|
||||
+ (((bool)(d1 & mask)) << 1)
|
||||
+ (((bool)(d2 & mask)) << 2)
|
||||
+ (((bool)(d3 & mask)) << 3);
|
||||
}
|
||||
|
||||
case Background::Mode::BPP8: {
|
||||
offset = (offset * 64) + ((y & 7) * 2);
|
||||
|
||||
unsigned d0 = memory::vram[offset + 0];
|
||||
unsigned d1 = memory::vram[offset + 1];
|
||||
unsigned d2 = memory::vram[offset + 16];
|
||||
unsigned d3 = memory::vram[offset + 17];
|
||||
unsigned d4 = memory::vram[offset + 32];
|
||||
unsigned d5 = memory::vram[offset + 33];
|
||||
unsigned d6 = memory::vram[offset + 48];
|
||||
unsigned d7 = memory::vram[offset + 49];
|
||||
|
||||
return (((bool)(d0 & mask)) << 0)
|
||||
+ (((bool)(d1 & mask)) << 1)
|
||||
+ (((bool)(d2 & mask)) << 2)
|
||||
+ (((bool)(d3 & mask)) << 3)
|
||||
+ (((bool)(d4 & mask)) << 4)
|
||||
+ (((bool)(d5 & mask)) << 5)
|
||||
+ (((bool)(d6 & mask)) << 6)
|
||||
+ (((bool)(d7 & mask)) << 7);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
sPPU::Background::Background(sPPU &self, unsigned id) : self(self), id(id) {
|
||||
//generate mosaic table
|
||||
for(unsigned m = 0; m < 16; m++) {
|
||||
for(unsigned x = 0; x < 4096; x++) {
|
||||
mosaic_table[m][x] = (x / (m + 1)) * (m + 1);
|
||||
}
|
||||
}
|
||||
|
||||
regs.tiledata_addr = 0x0000;
|
||||
regs.screen_addr = 0x0000;
|
||||
regs.screen_size = ScreenSize::Size32x32;
|
||||
regs.mosaic = 0;
|
||||
regs.mosaic_y = 0;
|
||||
regs.tile_size = TileSize::Size8x8;
|
||||
|
||||
regs.mode = Mode::BPP2;
|
||||
regs.priority0 = 0;
|
||||
regs.priority1 = 0;
|
||||
|
||||
regs.main_enabled = false;
|
||||
regs.sub_enabled = false;
|
||||
}
|
||||
|
||||
uint16 sPPU::Background::mosaic_table[16][4096];
|
||||
|
||||
#endif
|
|
@ -0,0 +1,45 @@
|
|||
class Background {
|
||||
public:
|
||||
sPPU &self;
|
||||
struct ID { enum { BG1, BG2, BG3, BG4 }; };
|
||||
unsigned id;
|
||||
|
||||
struct Mode { enum { BPP2, BPP4, BPP8, Mode7, Inactive }; };
|
||||
struct ScreenSize { enum { Size32x32, Size32x64, Size64x32, Size64x64 }; };
|
||||
struct TileSize { enum { Size8x8, Size16x16 }; };
|
||||
|
||||
struct {
|
||||
unsigned tiledata_addr;
|
||||
unsigned screen_addr;
|
||||
unsigned screen_size;
|
||||
unsigned mosaic;
|
||||
unsigned mosaic_y;
|
||||
bool tile_size;
|
||||
|
||||
unsigned mode;
|
||||
unsigned priority0;
|
||||
unsigned priority1;
|
||||
|
||||
bool main_enabled;
|
||||
bool sub_enabled;
|
||||
|
||||
unsigned hoffset;
|
||||
unsigned voffset;
|
||||
} regs;
|
||||
|
||||
struct {
|
||||
struct {
|
||||
bool valid;
|
||||
uint16 color;
|
||||
unsigned priority;
|
||||
} main, sub;
|
||||
} output;
|
||||
|
||||
void run();
|
||||
unsigned get_color(unsigned x, unsigned y, uint16 offset);
|
||||
|
||||
Background(sPPU &self, unsigned id);
|
||||
|
||||
private:
|
||||
static uint16 mosaic_table[16][4096];
|
||||
};
|
|
@ -1,8 +1,8 @@
|
|||
#ifdef SPPU_CPP
|
||||
|
||||
void sPPU::latch_counters() {
|
||||
regs.hcounter = cpu.hdot();
|
||||
regs.vcounter = cpu.vcounter();
|
||||
regs.hcounter = hdot();
|
||||
regs.vcounter = vcounter();
|
||||
regs.counters_latched = true;
|
||||
}
|
||||
|
||||
|
@ -96,52 +96,179 @@ void sPPU::mmio_w2104(uint8 data) {
|
|||
regs.oam_firstsprite = (regs.oam_priority == false ? 0 : (regs.oam_addr >> 2) & 127);
|
||||
}
|
||||
|
||||
//BGMODE
|
||||
void sPPU::mmio_w2105(uint8 data) {
|
||||
bg4.regs.tile_size = (data & 0x80);
|
||||
bg3.regs.tile_size = (data & 0x40);
|
||||
bg2.regs.tile_size = (data & 0x20);
|
||||
bg1.regs.tile_size = (data & 0x10);
|
||||
regs.bg3_priority = (data & 0x08);
|
||||
regs.bgmode = (data & 0x07);
|
||||
|
||||
switch(regs.bgmode) {
|
||||
case 0: {
|
||||
bg1.regs.mode = Background::Mode::BPP2; bg1.regs.priority0 = 8; bg1.regs.priority1 = 11;
|
||||
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;
|
||||
} break;
|
||||
|
||||
case 1: {
|
||||
bg1.regs.mode = Background::Mode::BPP4;
|
||||
bg2.regs.mode = Background::Mode::BPP4;
|
||||
bg3.regs.mode = Background::Mode::BPP2;
|
||||
bg4.regs.mode = Background::Mode::Inactive;
|
||||
if(regs.bg3_priority) {
|
||||
bg1.regs.priority0 = 5; bg1.regs.priority1 = 8;
|
||||
bg2.regs.priority0 = 4; bg2.regs.priority1 = 7;
|
||||
bg3.regs.priority0 = 1; bg3.regs.priority1 = 10;
|
||||
} 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;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 2: {
|
||||
bg1.regs.mode = Background::Mode::BPP4;
|
||||
bg2.regs.mode = Background::Mode::BPP4;
|
||||
bg3.regs.mode = Background::Mode::Inactive;
|
||||
bg4.regs.mode = Background::Mode::Inactive;
|
||||
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
|
||||
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
|
||||
} break;
|
||||
|
||||
case 3: {
|
||||
bg1.regs.mode = Background::Mode::BPP8;
|
||||
bg2.regs.mode = Background::Mode::BPP4;
|
||||
bg3.regs.mode = Background::Mode::Inactive;
|
||||
bg4.regs.mode = Background::Mode::Inactive;
|
||||
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
|
||||
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
|
||||
} break;
|
||||
|
||||
case 4: {
|
||||
bg1.regs.mode = Background::Mode::BPP8;
|
||||
bg2.regs.mode = Background::Mode::BPP2;
|
||||
bg3.regs.mode = Background::Mode::Inactive;
|
||||
bg4.regs.mode = Background::Mode::Inactive;
|
||||
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
|
||||
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
|
||||
} break;
|
||||
|
||||
case 5: {
|
||||
bg1.regs.mode = Background::Mode::BPP4;
|
||||
bg2.regs.mode = Background::Mode::BPP2;
|
||||
bg3.regs.mode = Background::Mode::Inactive;
|
||||
bg4.regs.mode = Background::Mode::Inactive;
|
||||
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
|
||||
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
|
||||
} break;
|
||||
|
||||
case 6: {
|
||||
bg1.regs.mode = Background::Mode::BPP4;
|
||||
bg2.regs.mode = Background::Mode::Inactive;
|
||||
bg3.regs.mode = Background::Mode::Inactive;
|
||||
bg4.regs.mode = Background::Mode::Inactive;
|
||||
bg1.regs.priority0 = 2; bg1.regs.priority1 = 5;
|
||||
} break;
|
||||
|
||||
case 7: {
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
//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);
|
||||
}
|
||||
|
||||
//BG1SC
|
||||
void sPPU::mmio_w2107(uint8 data) {
|
||||
bg1.regs.screen_addr = (data & 0x7c) << 9;
|
||||
bg1.regs.screen_size = data & 3;
|
||||
}
|
||||
|
||||
//BG2SC
|
||||
void sPPU::mmio_w2108(uint8 data) {
|
||||
bg2.regs.screen_addr = (data & 0x7c) << 9;
|
||||
bg2.regs.screen_size = data & 3;
|
||||
}
|
||||
|
||||
//BG3SC
|
||||
void sPPU::mmio_w2109(uint8 data) {
|
||||
bg3.regs.screen_addr = (data & 0x7c) << 9;
|
||||
bg3.regs.screen_size = data & 3;
|
||||
}
|
||||
|
||||
//BG4SC
|
||||
void sPPU::mmio_w210a(uint8 data) {
|
||||
bg4.regs.screen_addr = (data & 0x7c) << 9;
|
||||
bg4.regs.screen_size = data & 3;
|
||||
}
|
||||
|
||||
//BG12NBA
|
||||
void sPPU::mmio_w210b(uint8 data) {
|
||||
bg1.regs.tiledata_addr = (data & 0x07) << 13;
|
||||
bg2.regs.tiledata_addr = (data & 0x70) << 9;
|
||||
}
|
||||
|
||||
//BG34NBA
|
||||
void sPPU::mmio_w210c(uint8 data) {
|
||||
bg3.regs.tiledata_addr = (data & 0x07) << 13;
|
||||
bg4.regs.tiledata_addr = (data & 0x70) << 9;
|
||||
}
|
||||
|
||||
//BG1HOFS
|
||||
void sPPU::mmio_w210d(uint8 data) {
|
||||
bg1.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg1.regs.hoffset >> 8) & 7);
|
||||
regs.bgofs_latchdata = data;
|
||||
}
|
||||
|
||||
//BG1VOFS
|
||||
void sPPU::mmio_w210e(uint8 data) {
|
||||
bg1.regs.voffset = (data << 8) | regs.bgofs_latchdata;
|
||||
regs.bgofs_latchdata = data;
|
||||
}
|
||||
|
||||
//BG2HOFS
|
||||
void sPPU::mmio_w210f(uint8 data) {
|
||||
bg2.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg2.regs.hoffset >> 8) & 7);
|
||||
regs.bgofs_latchdata = data;
|
||||
}
|
||||
|
||||
//BG2VOFS
|
||||
void sPPU::mmio_w2110(uint8 data) {
|
||||
bg2.regs.voffset = (data << 8) | regs.bgofs_latchdata;
|
||||
regs.bgofs_latchdata = data;
|
||||
}
|
||||
|
||||
//BG3HOFS
|
||||
void sPPU::mmio_w2111(uint8 data) {
|
||||
bg3.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg3.regs.hoffset >> 8) & 7);
|
||||
regs.bgofs_latchdata = data;
|
||||
}
|
||||
|
||||
//BG3VOFS
|
||||
void sPPU::mmio_w2112(uint8 data) {
|
||||
bg3.regs.voffset = (data << 8) | regs.bgofs_latchdata;
|
||||
regs.bgofs_latchdata = data;
|
||||
}
|
||||
|
||||
//BG4HOFS
|
||||
void sPPU::mmio_w2113(uint8 data) {
|
||||
bg4.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg4.regs.hoffset >> 8) & 7);
|
||||
regs.bgofs_latchdata = data;
|
||||
}
|
||||
|
||||
//BG4VOFS
|
||||
void sPPU::mmio_w2114(uint8 data) {
|
||||
bg4.regs.voffset = (data << 8) | regs.bgofs_latchdata;
|
||||
regs.bgofs_latchdata = data;
|
||||
}
|
||||
|
||||
//VMAIN
|
||||
|
@ -270,10 +397,20 @@ void sPPU::mmio_w212a(uint8 data) {
|
|||
void sPPU::mmio_w212b(uint8 data) {
|
||||
}
|
||||
|
||||
//TM
|
||||
void sPPU::mmio_w212c(uint8 data) {
|
||||
bg4.regs.main_enabled = data & 0x08;
|
||||
bg3.regs.main_enabled = data & 0x04;
|
||||
bg2.regs.main_enabled = data & 0x02;
|
||||
bg1.regs.main_enabled = data & 0x01;
|
||||
}
|
||||
|
||||
//TS
|
||||
void sPPU::mmio_w212d(uint8 data) {
|
||||
bg4.regs.sub_enabled = data & 0x08;
|
||||
bg3.regs.sub_enabled = data & 0x04;
|
||||
bg2.regs.sub_enabled = data & 0x02;
|
||||
bg1.regs.sub_enabled = data & 0x01;
|
||||
}
|
||||
|
||||
void sPPU::mmio_w212e(uint8 data) {
|
||||
|
@ -405,7 +542,7 @@ uint8 sPPU::mmio_r213f() {
|
|||
regs.latch_vcounter = 0;
|
||||
|
||||
regs.ppu2_mdr &= 0x20;
|
||||
regs.ppu2_mdr |= cpu.field() << 7;
|
||||
regs.ppu2_mdr |= field() << 7;
|
||||
if((cpu.pio() & 0x80) == 0) {
|
||||
regs.ppu2_mdr |= 0x40;
|
||||
} else if(regs.counters_latched) {
|
||||
|
@ -421,9 +558,11 @@ 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;
|
||||
regs.bgofs_latchdata = 0x00;
|
||||
regs.m7_latchdata = 0x00;
|
||||
regs.counters_latched = false;
|
||||
regs.latch_hcounter = 0;
|
||||
|
@ -440,6 +579,13 @@ void sPPU::mmio_reset() {
|
|||
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,9 +2,11 @@ struct {
|
|||
uint8 ppu1_mdr;
|
||||
uint8 ppu2_mdr;
|
||||
|
||||
unsigned mosaic_countdown;
|
||||
uint16 vram_readbuffer;
|
||||
uint8 oam_latchdata;
|
||||
uint8 cgram_latchdata;
|
||||
uint8 bgofs_latchdata;
|
||||
uint8 m7_latchdata;
|
||||
bool counters_latched;
|
||||
bool latch_hcounter;
|
||||
|
@ -21,6 +23,13 @@ struct {
|
|||
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;
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
#ifdef SPPU_CPP
|
||||
|
||||
void sPPU::Screen::scanline() {
|
||||
output = self.output + self.vcounter() * 1024;
|
||||
}
|
||||
|
||||
void sPPU::Screen::run() {
|
||||
unsigned priority = 0;
|
||||
uint16 color;
|
||||
|
||||
if(self.bg1.output.main.valid) {
|
||||
priority = self.bg1.output.main.priority;
|
||||
color = self.bg1.output.main.color;
|
||||
}
|
||||
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.bg3.output.main.valid && self.bg3.output.main.priority > priority) {
|
||||
priority = self.bg3.output.main.priority;
|
||||
color = self.bg3.output.main.color;
|
||||
}
|
||||
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(priority == 0) {
|
||||
color = (memory::cgram[1] << 8) + (memory::cgram[0] << 0);
|
||||
}
|
||||
|
||||
color = light_table[self.regs.display_brightness][color];
|
||||
if(self.regs.display_disabled) color = 0;
|
||||
*output++ = color;
|
||||
*output++ = color;
|
||||
}
|
||||
|
||||
sPPU::Screen::Screen(sPPU &self) : self(self) {
|
||||
//generate light table
|
||||
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][(b << 10) + (g << 5) + r] = (ab << 10) + (ag << 5) + ar;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,13 @@
|
|||
class Screen {
|
||||
public:
|
||||
sPPU &self;
|
||||
uint16 *output;
|
||||
|
||||
void scanline();
|
||||
void run();
|
||||
|
||||
Screen(sPPU &self);
|
||||
|
||||
private:
|
||||
uint16 light_table[16][32768];
|
||||
};
|
|
@ -3,7 +3,9 @@
|
|||
#define SPPU_CPP
|
||||
namespace SNES {
|
||||
|
||||
#include "background/background.cpp"
|
||||
#include "mmio/mmio.cpp"
|
||||
#include "screen/screen.cpp"
|
||||
|
||||
#if !defined(DEBUGGER)
|
||||
sPPU ppu;
|
||||
|
@ -17,14 +19,29 @@ 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) {
|
||||
uint16_t *buffer = output + vcounter() * 1024;
|
||||
screen.scanline();
|
||||
for(unsigned n = 0; n < 256; n++) {
|
||||
uint16 color = (memory::cgram[1] << 8) + (memory::cgram[0] << 0);
|
||||
color = light_table[regs.display_brightness][color];
|
||||
if(regs.display_disabled) color = 0;
|
||||
*buffer++ = color;
|
||||
*buffer++ = color;
|
||||
bg1.run();
|
||||
bg2.run();
|
||||
bg3.run();
|
||||
bg4.run();
|
||||
screen.run();
|
||||
add_clocks(4);
|
||||
}
|
||||
} else {
|
||||
|
@ -36,20 +53,12 @@ void sPPU::enter() {
|
|||
}
|
||||
|
||||
void sPPU::add_clocks(unsigned clocks) {
|
||||
#if 0
|
||||
//asynchronous execution
|
||||
tick(clocks);
|
||||
scheduler.addclocks_ppu(clocks);
|
||||
scheduler.sync_ppucpu();
|
||||
#else
|
||||
//synchronous execution
|
||||
clocks >>= 1;
|
||||
while(clocks--) {
|
||||
tick(2);
|
||||
scheduler.addclocks_ppu(2);
|
||||
scheduler.sync_ppucpu();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void sPPU::power() {
|
||||
|
@ -74,21 +83,12 @@ void sPPU::frame() {
|
|||
system.frame();
|
||||
}
|
||||
|
||||
sPPU::sPPU() {
|
||||
//generate light table for INIDISP::d3-d0
|
||||
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][(b << 10) + (g << 5) + r] = (ab << 10) + (ag << 5) + ar;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sPPU::sPPU() :
|
||||
bg1(*this, Background::ID::BG1),
|
||||
bg2(*this, Background::ID::BG2),
|
||||
bg3(*this, Background::ID::BG3),
|
||||
bg4(*this, Background::ID::BG4),
|
||||
screen(*this) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
class sPPU : public PPU {
|
||||
public:
|
||||
#include "background/background.hpp"
|
||||
#include "mmio/mmio.hpp"
|
||||
#include "screen/screen.hpp"
|
||||
|
||||
Background bg1;
|
||||
Background bg2;
|
||||
Background bg3;
|
||||
Background bg4;
|
||||
Screen screen;
|
||||
|
||||
void enter();
|
||||
void add_clocks(unsigned);
|
||||
|
@ -12,9 +20,6 @@ public:
|
|||
void frame();
|
||||
|
||||
sPPU();
|
||||
|
||||
private:
|
||||
uint16 light_table[16][32768];
|
||||
};
|
||||
|
||||
#if !defined(DEBUGGER)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
static const char bsnesVersion[] = "063.07";
|
||||
static const char bsnesVersion[] = "063.08";
|
||||
static const char bsnesTitle[] = "bsnes";
|
||||
static const unsigned bsnesSerializerVersion = 9;
|
||||
|
||||
|
@ -6,7 +6,7 @@ static const unsigned bsnesSerializerVersion = 9;
|
|||
#define CORE_SCPU
|
||||
#define CORE_SSMP
|
||||
#define CORE_SDSP
|
||||
#define CORE_BPPU
|
||||
#define CORE_SPPU
|
||||
|
||||
//S-DSP can be encapsulated into a state machine using #define magic
|
||||
//this avoids ~2.048m co_switch() calls per second (~5% speedup)
|
||||
|
|
Loading…
Reference in New Issue