mirror of https://github.com/bsnes-emu/bsnes.git
Update to v068r07 release.
(there was no r06 release posted to the WIP thread) byuu says: New PPU renderer is coming along. Lots of new ideas, especially with regards to the way the background renders in tiles rather than in pixels. That skips the need for tile caching and compares, and it even gets scrolling right. The sprite item+tile lists are computing, but they are not rendering yet. It's a good deal faster for now, obviously, because 90% of the PPU features are missing.
This commit is contained in:
parent
2bafd18a5a
commit
0bf6c40d1f
|
@ -0,0 +1,107 @@
|
|||
#ifdef PPU_CPP
|
||||
|
||||
void PPU::Background::render() {
|
||||
if(regs.mode == Mode::Inactive) return;
|
||||
if(regs.mode == Mode::Mode7) return;
|
||||
if(regs.main_enable == false && regs.sub_enable == false) return;
|
||||
|
||||
const unsigned opt_valid_bit = (id == ID::BG1 ? 0x2000 : id == ID::BG2 ? 0x4000 : 0x0000);
|
||||
const unsigned bgpal_index = (self.regs.bgmode == 0 ? id << 5 : 0);
|
||||
|
||||
const unsigned pal_size = 2 << regs.mode;
|
||||
const unsigned tile_mask = 0x0fff >> regs.mode;
|
||||
const unsigned tiledata_index = regs.tiledata_addr >> (4 + regs.mode);
|
||||
|
||||
const bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6);
|
||||
const signed width = !hires ? 256 : 512;
|
||||
|
||||
const unsigned tile_height = regs.tile_size ? 4 : 3;
|
||||
const unsigned tile_width = hires ? 4 : tile_height;
|
||||
|
||||
unsigned mask_x = (tile_height == 4 ? width << 1 : width);
|
||||
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 scy = (regs.screen_size & 2 ? 32 << 5 : 0);
|
||||
unsigned scx = (regs.screen_size & 1 ? 32 << 5 : 0);
|
||||
if(regs.screen_size == 3) scy <<= 1;
|
||||
|
||||
unsigned y = self.vcounter();
|
||||
unsigned hscroll = regs.hoffset;
|
||||
unsigned vscroll = regs.voffset;
|
||||
|
||||
if(hires) {
|
||||
hscroll <<= 1;
|
||||
if(self.regs.interlace) y = (y << 1) + self.field();
|
||||
}
|
||||
|
||||
unsigned hval, vval;
|
||||
unsigned tile_pri, tile_num;
|
||||
unsigned pal_index, pal_num;
|
||||
unsigned hoffset, voffset, opt_x, col;
|
||||
bool mirror_x, mirror_y;
|
||||
|
||||
const bool is_opt_mode = (self.regs.bgmode == 2 || self.regs.bgmode == 4 || self.regs.bgmode == 6);
|
||||
const bool is_direct_color_mode = (self.screen.regs.direct_color == true && id == ID::BG1 && (self.regs.bgmode == 3 || self.regs.bgmode == 4));
|
||||
|
||||
signed x = 0 - (hscroll & 7);
|
||||
while(x < width) {
|
||||
hoffset = x + hscroll;
|
||||
voffset = y + vscroll;
|
||||
|
||||
hoffset &= mask_x;
|
||||
voffset &= mask_y;
|
||||
|
||||
unsigned tile_x = hoffset >> tile_width;
|
||||
unsigned tile_y = voffset >> tile_height;
|
||||
unsigned tile_pos = ((tile_y & 0x1f) << 5) + (tile_x & 0x1f);
|
||||
if(tile_y & 0x20) tile_pos += scy;
|
||||
if(tile_x & 0x20) tile_pos += scx;
|
||||
const unsigned tiledata_addr = regs.screen_addr + (tile_pos << 1);
|
||||
|
||||
tile_num = (memory::vram[tiledata_addr + 0] << 0) + (memory::vram[tiledata_addr + 1] << 8);
|
||||
mirror_y = tile_num & 0x8000;
|
||||
mirror_x = tile_num & 0x4000;
|
||||
tile_pri = tile_num & 0x2000 ? regs.priority1 : regs.priority0;
|
||||
pal_num = (tile_num >> 10) & 7;
|
||||
pal_index = bgpal_index + (pal_num << pal_size);
|
||||
|
||||
if(tile_width == 4) {
|
||||
if((bool)(hoffset & 8) != mirror_x) tile_num++;
|
||||
}
|
||||
|
||||
if(tile_height == 4) {
|
||||
if((bool)(voffset & 8) != mirror_y) tile_num += 16;
|
||||
}
|
||||
|
||||
tile_num &= 0x03ff;
|
||||
tile_num += tiledata_index;
|
||||
tile_num &= tile_mask;
|
||||
|
||||
if(mirror_y) voffset ^= 7;
|
||||
signed step = mirror_x ? -1 : +1;
|
||||
unsigned plot_x = x + (mirror_x ? 7 : 0);
|
||||
|
||||
uint8 *tiledata = self.cache.tile(regs.mode, tile_num);
|
||||
tiledata += ((voffset & 7) * 8);
|
||||
|
||||
for(unsigned n = 0; n < 8; n++) {
|
||||
unsigned col = *tiledata++;
|
||||
if(col) {
|
||||
unsigned color = self.screen.get_palette(pal_index + col);
|
||||
self.screen.output.plot_main(plot_x, color, tile_pri);
|
||||
}
|
||||
plot_x += step;
|
||||
}
|
||||
|
||||
x += 8;
|
||||
}
|
||||
}
|
||||
|
||||
PPU::Background::Background(PPU &self, unsigned id) : self(self), id(id) {
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,33 @@
|
|||
class Background {
|
||||
struct ID { enum { BG1, BG2, BG3, BG4 }; };
|
||||
struct Mode { enum { BPP2, BPP4, BPP8, Mode7, Inactive }; };
|
||||
struct ScreenSize { enum { Size32x32, Size32x64, Size64x32, Size64x64 }; };
|
||||
struct TileSize { enum { Size8x8, Size16x16 }; };
|
||||
|
||||
struct Regs {
|
||||
unsigned mode;
|
||||
unsigned priority0;
|
||||
unsigned priority1;
|
||||
|
||||
bool tile_size;
|
||||
unsigned mosaic;
|
||||
|
||||
unsigned screen_addr;
|
||||
unsigned screen_size;
|
||||
unsigned tiledata_addr;
|
||||
|
||||
unsigned hoffset;
|
||||
unsigned voffset;
|
||||
|
||||
bool main_enable;
|
||||
bool sub_enable;
|
||||
} regs;
|
||||
|
||||
void render();
|
||||
|
||||
const unsigned id;
|
||||
Background(PPU &self, unsigned id);
|
||||
|
||||
PPU &self;
|
||||
friend class PPU;
|
||||
};
|
|
@ -0,0 +1,123 @@
|
|||
#ifdef PPU_CPP
|
||||
|
||||
uint8* PPU::Cache::tile_2bpp(unsigned tile) {
|
||||
if(tilevalid[0][tile] == 0) {
|
||||
tilevalid[0][tile] = 1;
|
||||
uint8 *output = (uint8*)tiledata[0] + (tile << 6);
|
||||
unsigned offset = tile << 4;
|
||||
unsigned y = 8;
|
||||
unsigned color, d0, d1;
|
||||
while(y--) {
|
||||
d0 = memory::vram[offset + 0];
|
||||
d1 = memory::vram[offset + 1];
|
||||
#define render_line(mask) \
|
||||
color = !!(d0 & mask) << 0; \
|
||||
color |= !!(d1 & mask) << 1; \
|
||||
*output++ = color
|
||||
render_line(0x80);
|
||||
render_line(0x40);
|
||||
render_line(0x20);
|
||||
render_line(0x10);
|
||||
render_line(0x08);
|
||||
render_line(0x04);
|
||||
render_line(0x02);
|
||||
render_line(0x01);
|
||||
#undef render_line
|
||||
offset += 2;
|
||||
}
|
||||
}
|
||||
return tiledata[0] + (tile << 6);
|
||||
}
|
||||
|
||||
uint8* PPU::Cache::tile_4bpp(unsigned tile) {
|
||||
if(tilevalid[1][tile] == 0) {
|
||||
tilevalid[1][tile] = 1;
|
||||
uint8 *output = (uint8*)tiledata[1] + (tile << 6);
|
||||
unsigned offset = tile << 5;
|
||||
unsigned y = 8;
|
||||
unsigned color, d0, d1, d2, d3;
|
||||
while(y--) {
|
||||
d0 = memory::vram[offset + 0];
|
||||
d1 = memory::vram[offset + 1];
|
||||
d2 = memory::vram[offset + 16];
|
||||
d3 = memory::vram[offset + 17];
|
||||
#define render_line(mask) \
|
||||
color = !!(d0 & mask) << 0; \
|
||||
color |= !!(d1 & mask) << 1; \
|
||||
color |= !!(d2 & mask) << 2; \
|
||||
color |= !!(d3 & mask) << 3; \
|
||||
*output++ = color
|
||||
render_line(0x80);
|
||||
render_line(0x40);
|
||||
render_line(0x20);
|
||||
render_line(0x10);
|
||||
render_line(0x08);
|
||||
render_line(0x04);
|
||||
render_line(0x02);
|
||||
render_line(0x01);
|
||||
#undef render_line
|
||||
offset += 2;
|
||||
}
|
||||
}
|
||||
return tiledata[1] + (tile << 6);
|
||||
}
|
||||
|
||||
uint8* PPU::Cache::tile_8bpp(unsigned tile) {
|
||||
if(tilevalid[2][tile] == 0) {
|
||||
tilevalid[2][tile] = 1;
|
||||
uint8 *output = (uint8*)tiledata[2] + (tile << 6);
|
||||
unsigned offset = tile << 6;
|
||||
unsigned y = 8;
|
||||
unsigned color, d0, d1, d2, d3, d4, d5, d6, d7;
|
||||
while(y--) {
|
||||
d0 = memory::vram[offset + 0];
|
||||
d1 = memory::vram[offset + 1];
|
||||
d2 = memory::vram[offset + 16];
|
||||
d3 = memory::vram[offset + 17];
|
||||
d4 = memory::vram[offset + 32];
|
||||
d5 = memory::vram[offset + 33];
|
||||
d6 = memory::vram[offset + 48];
|
||||
d7 = memory::vram[offset + 49];
|
||||
#define render_line(mask) \
|
||||
color = !!(d0 & mask) << 0; \
|
||||
color |= !!(d1 & mask) << 1; \
|
||||
color |= !!(d2 & mask) << 2; \
|
||||
color |= !!(d3 & mask) << 3; \
|
||||
color |= !!(d4 & mask) << 4; \
|
||||
color |= !!(d5 & mask) << 5; \
|
||||
color |= !!(d6 & mask) << 6; \
|
||||
color |= !!(d7 & mask) << 7; \
|
||||
*output++ = color
|
||||
render_line(0x80);
|
||||
render_line(0x40);
|
||||
render_line(0x20);
|
||||
render_line(0x10);
|
||||
render_line(0x08);
|
||||
render_line(0x04);
|
||||
render_line(0x02);
|
||||
render_line(0x01);
|
||||
#undef render_line
|
||||
offset += 2;
|
||||
}
|
||||
}
|
||||
return tiledata[2] + (tile << 6);
|
||||
}
|
||||
|
||||
uint8* PPU::Cache::tile(unsigned bpp, unsigned tile) {
|
||||
switch(bpp) {
|
||||
case 0: return tile_2bpp(tile);
|
||||
case 1: return tile_4bpp(tile);
|
||||
case 2: return tile_8bpp(tile);
|
||||
}
|
||||
}
|
||||
|
||||
PPU::Cache::Cache(PPU &self) : self(self) {
|
||||
tiledata[0] = new uint8[262144]();
|
||||
tiledata[1] = new uint8[131072]();
|
||||
tiledata[2] = new uint8[ 65536]();
|
||||
tilevalid[0] = new uint8[ 4096]();
|
||||
tilevalid[1] = new uint8[ 2048]();
|
||||
tilevalid[2] = new uint8[ 1024]();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
class Cache {
|
||||
public:
|
||||
uint8 *tiledata[3];
|
||||
uint8 *tilevalid[3];
|
||||
|
||||
uint8* tile_2bpp(unsigned tile);
|
||||
uint8* tile_4bpp(unsigned tile);
|
||||
uint8* tile_8bpp(unsigned tile);
|
||||
uint8* tile(unsigned bpp, unsigned tile);
|
||||
|
||||
Cache(PPU &self);
|
||||
|
||||
PPU &self;
|
||||
friend class PPU;
|
||||
};
|
|
@ -1,6 +1,11 @@
|
|||
#ifdef PPU_CPP
|
||||
|
||||
void PPU::latch_counters() {}
|
||||
void PPU::latch_counters() {
|
||||
regs.hcounter = cpu.hdot();
|
||||
regs.vcounter = cpu.vcounter();
|
||||
regs.counters_latched = true;
|
||||
}
|
||||
|
||||
bool PPU::interlace() const { return false; }
|
||||
bool PPU::overscan() const { return false; }
|
||||
bool PPU::hires() const { return false; }
|
||||
|
@ -23,8 +28,13 @@ uint8 PPU::vram_read(unsigned addr) {
|
|||
}
|
||||
|
||||
void PPU::vram_write(unsigned addr, uint8 data) {
|
||||
if(regs.display_disable) { memory::vram[addr] = data; return; }
|
||||
if(cpu.vcounter() >= display.height) { memory::vram[addr] = data; return; }
|
||||
if(regs.display_disable || cpu.vcounter() >= display.height) {
|
||||
memory::vram[addr] = data;
|
||||
cache.tilevalid[0][addr >> 4] = false;
|
||||
cache.tilevalid[1][addr >> 5] = false;
|
||||
cache.tilevalid[2][addr >> 6] = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 PPU::oam_read(unsigned addr) {
|
||||
|
@ -36,9 +46,9 @@ uint8 PPU::oam_read(unsigned addr) {
|
|||
|
||||
void PPU::oam_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x0200) addr &= 0x021f;
|
||||
if(regs.display_disable) { memory::oam[addr] = data; return; }
|
||||
if(cpu.vcounter() >= display.height) { memory::oam[addr] = data; return; }
|
||||
memory::oam[0x0218] = data;
|
||||
if(!regs.display_disable && cpu.vcounter() < display.height) addr = 0x0218;
|
||||
memory::oam[addr] = data;
|
||||
oam.update_list(addr, data);
|
||||
}
|
||||
|
||||
uint8 PPU::cgram_read(unsigned addr) {
|
||||
|
@ -49,6 +59,104 @@ void PPU::cgram_write(unsigned addr, uint8 data) {
|
|||
memory::cgram[addr] = data;
|
||||
}
|
||||
|
||||
void PPU::mmio_update_video_mode() {
|
||||
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;
|
||||
oam.regs.priority0 = 3; oam.regs.priority1 = 6; oam.regs.priority2 = 9; oam.regs.priority3 = 12;
|
||||
} 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;
|
||||
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;
|
||||
|
||||
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;
|
||||
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.regs.priority3 = 8;
|
||||
} 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;
|
||||
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.regs.priority3 = 8;
|
||||
} 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;
|
||||
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.regs.priority3 = 8;
|
||||
} 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;
|
||||
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.regs.priority3 = 8;
|
||||
} 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;
|
||||
oam.regs.priority0 = 1; oam.regs.priority1 = 3; oam.regs.priority2 = 4; oam.regs.priority3 = 6;
|
||||
} break;
|
||||
|
||||
case 7: {
|
||||
if(regs.mode7_extbg == false) {
|
||||
bg1.regs.mode = Background::Mode::Mode7;
|
||||
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 = 2;
|
||||
oam.regs.priority0 = 1; oam.regs.priority1 = 3; oam.regs.priority2 = 4; oam.regs.priority3 = 5;
|
||||
} else {
|
||||
bg1.regs.mode = Background::Mode::Mode7;
|
||||
bg2.regs.mode = Background::Mode::Mode7;
|
||||
bg3.regs.mode = Background::Mode::Inactive;
|
||||
bg4.regs.mode = Background::Mode::Inactive;
|
||||
bg1.regs.priority0 = 3; bg1.regs.priority1 = 3;
|
||||
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
|
||||
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.regs.priority3 = 7;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 PPU::mmio_read(unsigned addr) {
|
||||
cpu.synchronize_ppu();
|
||||
|
||||
|
@ -85,7 +193,7 @@ uint8 PPU::mmio_read(unsigned addr) {
|
|||
case 0x2138: { //OAMDATAREAD
|
||||
regs.ppu1_mdr = oam_read(regs.oam_addr);
|
||||
regs.oam_addr = (regs.oam_addr + 1) & 0x03ff;
|
||||
//TODO: handle OAM address reset here
|
||||
oam.set_first();
|
||||
return regs.ppu1_mdr;
|
||||
}
|
||||
|
||||
|
@ -120,6 +228,51 @@ uint8 PPU::mmio_read(unsigned addr) {
|
|||
regs.cgram_addr = (regs.cgram_addr + 1) & 0x01ff;
|
||||
return regs.ppu2_mdr;
|
||||
}
|
||||
|
||||
case 0x213c: { //OPHCT
|
||||
if(regs.latch_hcounter == 0) {
|
||||
regs.ppu2_mdr = regs.hcounter & 0xff;
|
||||
} else {
|
||||
regs.ppu2_mdr = (regs.ppu2_mdr & 0xfe) | (regs.hcounter >> 8);
|
||||
}
|
||||
regs.latch_hcounter ^= 1;
|
||||
return regs.ppu2_mdr;
|
||||
}
|
||||
|
||||
case 0x213d: { //OPVCT
|
||||
if(regs.latch_vcounter == 0) {
|
||||
regs.ppu2_mdr = regs.vcounter & 0xff;
|
||||
} else {
|
||||
regs.ppu2_mdr = (regs.ppu2_mdr & 0xfe) | (regs.vcounter >> 8);
|
||||
}
|
||||
regs.latch_vcounter ^= 1;
|
||||
return regs.ppu2_mdr;
|
||||
}
|
||||
|
||||
case 0x213e: { //STAT77
|
||||
regs.ppu1_mdr &= 0x10;
|
||||
regs.ppu1_mdr |= oam.regs.time_over << 7;
|
||||
regs.ppu1_mdr |= oam.regs.range_over << 6;
|
||||
regs.ppu1_mdr |= 0x01; //version
|
||||
return regs.ppu1_mdr;
|
||||
}
|
||||
|
||||
case 0x213f: { //STAT78
|
||||
regs.latch_hcounter = 0;
|
||||
regs.latch_vcounter = 0;
|
||||
|
||||
regs.ppu2_mdr &= 0x20;
|
||||
regs.ppu2_mdr |= cpu.field() << 7;
|
||||
if((cpu.pio() & 0x80) == 0) {
|
||||
regs.ppu2_mdr |= 0x40;
|
||||
} else if(regs.counters_latched) {
|
||||
regs.ppu2_mdr |= 0x40;
|
||||
regs.counters_latched = false;
|
||||
}
|
||||
regs.ppu2_mdr |= (system.region() == System::Region::NTSC ? 0 : 1) << 4;
|
||||
regs.ppu2_mdr |= 0x03; //version
|
||||
return regs.ppu2_mdr;
|
||||
}
|
||||
}
|
||||
|
||||
return cpu.regs.mdr;
|
||||
|
@ -130,23 +283,30 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
|
|||
|
||||
switch(addr & 0xffff) {
|
||||
case 0x2100: { //INIDISP
|
||||
//TODO: handle OAM address reset here
|
||||
if(regs.display_disable && vcounter() == display.height) oam.address_reset();
|
||||
regs.display_disable = data & 0x80;
|
||||
regs.display_brightness = data & 0x0f;
|
||||
screen.light_table = screen.light_tables[regs.display_brightness];
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2101: { //OBSEL
|
||||
oam.regs.base_size = (data >> 5) & 7;
|
||||
oam.regs.nameselect = (data >> 3) & 3;
|
||||
oam.regs.tiledata_addr = (data & 3) << 14;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2102: { //OAMADDL
|
||||
regs.oam_baseaddr = (regs.oam_baseaddr & 0x0100) | (data << 0);
|
||||
//TODO: handle OAM address reset here
|
||||
oam.address_reset();
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2103: { //OAMADDH
|
||||
regs.oam_priority = data & 0x80;
|
||||
regs.oam_baseaddr = ((data & 1) << 8) | (regs.oam_baseaddr & 0x00ff);
|
||||
//TODO: handle OAM address reset here
|
||||
oam.address_reset();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -160,7 +320,117 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
|
|||
oam_write((regs.oam_addr & ~1) + 1, data);
|
||||
}
|
||||
regs.oam_addr = (regs.oam_addr + 1) & 0x03ff;
|
||||
//TODO: handle OAM address reset here
|
||||
oam.set_first();
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2105: { //BGMODE
|
||||
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;
|
||||
mmio_update_video_mode();
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2106: { //MOSAIC
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2107: { //BG1SC
|
||||
bg1.regs.screen_addr = (data & 0x7c) << 9;
|
||||
bg1.regs.screen_size = data & 3;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2108: { //BG2SC
|
||||
bg2.regs.screen_addr = (data & 0x7c) << 9;
|
||||
bg2.regs.screen_size = data & 3;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2109: { //BG3SC
|
||||
bg3.regs.screen_addr = (data & 0x7c) << 9;
|
||||
bg3.regs.screen_size = data & 3;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x210a: { //BG4SC
|
||||
bg4.regs.screen_addr = (data & 0x7c) << 9;
|
||||
bg4.regs.screen_size = data & 3;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x210b: { //BG12NBA
|
||||
bg1.regs.tiledata_addr = (data & 0x07) << 13;
|
||||
bg2.regs.tiledata_addr = (data & 0x70) << 9;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x210c: { //BG34NBA
|
||||
bg3.regs.tiledata_addr = (data & 0x07) << 13;
|
||||
bg4.regs.tiledata_addr = (data & 0x70) << 9;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x210d: { //BG1HOFS
|
||||
regs.mode7_hoffset = (data << 8) | regs.mode7_latchdata;
|
||||
regs.mode7_latchdata = data;
|
||||
|
||||
bg1.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg1.regs.hoffset >> 8) & 7);
|
||||
regs.bgofs_latchdata = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x210e: { //BG1VOFS
|
||||
regs.mode7_voffset = (data << 8) | regs.mode7_latchdata;
|
||||
regs.mode7_latchdata = data;
|
||||
|
||||
bg1.regs.voffset = (data << 8) | regs.bgofs_latchdata;
|
||||
regs.bgofs_latchdata = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x210f: { //BG2HOFS
|
||||
bg2.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg2.regs.hoffset >> 8) & 7);
|
||||
regs.bgofs_latchdata = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2110: { //BG2VOFS
|
||||
bg2.regs.voffset = (data << 8) | regs.bgofs_latchdata;
|
||||
regs.bgofs_latchdata = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2111: { //BG3HOFS
|
||||
bg3.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg3.regs.hoffset >> 8) & 7);
|
||||
regs.bgofs_latchdata = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2112: { //BG3VOFS
|
||||
bg3.regs.voffset = (data << 8) | regs.bgofs_latchdata;
|
||||
regs.bgofs_latchdata = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2113: { //BG4HOFS
|
||||
bg4.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg4.regs.hoffset >> 8) & 7);
|
||||
regs.bgofs_latchdata = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2114: { //BG4VOFS
|
||||
bg4.regs.voffset = (data << 8) | regs.bgofs_latchdata;
|
||||
regs.bgofs_latchdata = data;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -262,6 +532,149 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
|
|||
regs.cgram_addr = (regs.cgram_addr + 1) & 0x01ff;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2123: { //W12SEL
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2124: { //W34SEL
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2125: { //WOBJSEL
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2126: { //WH0
|
||||
window.regs.one_left = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2127: { //WH1
|
||||
window.regs.one_right = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2128: { //WH2
|
||||
window.regs.two_left = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2129: { //WH3
|
||||
window.regs.two_right = data;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x212a: { //WBGLOG
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x212b: { //WOBJLOG
|
||||
window.regs.col_mask = (data >> 2) & 3;
|
||||
window.regs.oam_mask = (data >> 0) & 3;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x212c: { //TM
|
||||
oam.regs.main_enable = data & 0x10;
|
||||
bg4.regs.main_enable = data & 0x08;
|
||||
bg3.regs.main_enable = data & 0x04;
|
||||
bg2.regs.main_enable = data & 0x02;
|
||||
bg1.regs.main_enable = data & 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x212d: { //TS
|
||||
oam.regs.sub_enable = data & 0x10;
|
||||
bg4.regs.sub_enable = data & 0x08;
|
||||
bg3.regs.sub_enable = data & 0x04;
|
||||
bg2.regs.sub_enable = data & 0x02;
|
||||
bg1.regs.sub_enable = data & 0x01;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x212e: { //TMW
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x212f: { //TSW
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2130: { //CGWSEL
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2131: { //CGADDSUB
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2132: { //COLDATA
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
case 0x2133: { //SETINI
|
||||
regs.mode7_extbg = data & 0x40;
|
||||
regs.pseudo_hires = data & 0x08;
|
||||
regs.overscan = data & 0x04;
|
||||
oam.regs.interlace = data & 0x02;
|
||||
regs.interlace = data & 0x01;
|
||||
mmio_update_video_mode();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,18 +686,72 @@ void PPU::mmio_reset() {
|
|||
regs.vram_readbuffer = 0;
|
||||
regs.oam_latchdata = 0;
|
||||
regs.cgram_latchdata = 0;
|
||||
regs.bgofs_latchdata = 0;
|
||||
regs.mode7_latchdata = 0;
|
||||
|
||||
regs.counters_latched = 0;
|
||||
regs.latch_hcounter = 0;
|
||||
regs.latch_vcounter = 0;
|
||||
|
||||
oam.regs.first_sprite = 0;
|
||||
|
||||
//$2100
|
||||
regs.display_disable = true;
|
||||
regs.display_brightness = 0;
|
||||
screen.light_table = screen.light_tables[regs.display_brightness];
|
||||
|
||||
//$2101
|
||||
oam.regs.base_size = 0;
|
||||
oam.regs.nameselect = 0;
|
||||
oam.regs.tiledata_addr = 0;
|
||||
|
||||
//$2102-$2103
|
||||
regs.oam_baseaddr = 0;
|
||||
regs.oam_addr = 0;
|
||||
regs.oam_priority = 0;
|
||||
|
||||
//$2105
|
||||
bg4.regs.tile_size = 0;
|
||||
bg3.regs.tile_size = 0;
|
||||
bg2.regs.tile_size = 0;
|
||||
bg1.regs.tile_size = 0;
|
||||
regs.bg3_priority = 0;
|
||||
regs.bgmode = 0;
|
||||
|
||||
//$2106
|
||||
bg4.regs.mosaic = 0;
|
||||
bg3.regs.mosaic = 0;
|
||||
bg2.regs.mosaic = 0;
|
||||
bg1.regs.mosaic = 0;
|
||||
|
||||
//$2107-$210a
|
||||
bg1.regs.screen_addr = 0;
|
||||
bg1.regs.screen_size = 0;
|
||||
bg2.regs.screen_addr = 0;
|
||||
bg2.regs.screen_size = 0;
|
||||
bg3.regs.screen_addr = 0;
|
||||
bg3.regs.screen_size = 0;
|
||||
bg4.regs.screen_addr = 0;
|
||||
bg4.regs.screen_size = 0;
|
||||
|
||||
//$210b-$210c
|
||||
bg1.regs.tiledata_addr = 0;
|
||||
bg2.regs.tiledata_addr = 0;
|
||||
bg3.regs.tiledata_addr = 0;
|
||||
bg4.regs.tiledata_addr = 0;
|
||||
|
||||
//$210d-$2114
|
||||
regs.mode7_hoffset = 0;
|
||||
regs.mode7_voffset = 0;
|
||||
bg1.regs.hoffset = 0;
|
||||
bg1.regs.voffset = 0;
|
||||
bg2.regs.hoffset = 0;
|
||||
bg2.regs.voffset = 0;
|
||||
bg3.regs.hoffset = 0;
|
||||
bg3.regs.voffset = 0;
|
||||
bg4.regs.hoffset = 0;
|
||||
bg4.regs.voffset = 0;
|
||||
|
||||
//$2115
|
||||
regs.vram_incmode = 0;
|
||||
regs.vram_mapping = 0;
|
||||
|
@ -308,6 +775,113 @@ void PPU::mmio_reset() {
|
|||
|
||||
//$2121
|
||||
regs.cgram_addr = 0;
|
||||
|
||||
//$2123-$2125
|
||||
window.regs.bg1_one_enable = 0;
|
||||
window.regs.bg1_one_invert = 0;
|
||||
window.regs.bg1_two_enable = 0;
|
||||
window.regs.bg1_two_invert = 0;
|
||||
|
||||
window.regs.bg2_one_enable = 0;
|
||||
window.regs.bg2_one_invert = 0;
|
||||
window.regs.bg2_two_enable = 0;
|
||||
window.regs.bg2_two_invert = 0;
|
||||
|
||||
window.regs.bg3_one_enable = 0;
|
||||
window.regs.bg3_one_invert = 0;
|
||||
window.regs.bg3_two_enable = 0;
|
||||
window.regs.bg3_two_invert = 0;
|
||||
|
||||
window.regs.bg4_one_enable = 0;
|
||||
window.regs.bg4_one_invert = 0;
|
||||
window.regs.bg4_two_enable = 0;
|
||||
window.regs.bg4_two_invert = 0;
|
||||
|
||||
window.regs.oam_one_enable = 0;
|
||||
window.regs.oam_one_invert = 0;
|
||||
window.regs.oam_two_enable = 0;
|
||||
window.regs.oam_two_invert = 0;
|
||||
|
||||
window.regs.col_one_enable = 0;
|
||||
window.regs.col_one_invert = 0;
|
||||
window.regs.col_two_enable = 0;
|
||||
window.regs.col_two_invert = 0;
|
||||
|
||||
//$2126-$2129
|
||||
window.regs.one_left = 0;
|
||||
window.regs.one_right = 0;
|
||||
window.regs.two_left = 0;
|
||||
window.regs.two_right = 0;
|
||||
|
||||
//$212a-$212b
|
||||
window.regs.bg1_mask = 0;
|
||||
window.regs.bg2_mask = 0;
|
||||
window.regs.bg3_mask = 0;
|
||||
window.regs.bg4_mask = 0;
|
||||
window.regs.oam_mask = 0;
|
||||
window.regs.col_mask = 0;
|
||||
|
||||
//$212c
|
||||
bg1.regs.main_enable = 0;
|
||||
bg2.regs.main_enable = 0;
|
||||
bg3.regs.main_enable = 0;
|
||||
bg4.regs.main_enable = 0;
|
||||
oam.regs.main_enable = 0;
|
||||
|
||||
//$212d
|
||||
bg1.regs.sub_enable = 0;
|
||||
bg2.regs.sub_enable = 0;
|
||||
bg3.regs.sub_enable = 0;
|
||||
bg4.regs.sub_enable = 0;
|
||||
oam.regs.sub_enable = 0;
|
||||
|
||||
//$212e
|
||||
window.regs.bg1_main_enable = 0;
|
||||
window.regs.bg2_main_enable = 0;
|
||||
window.regs.bg3_main_enable = 0;
|
||||
window.regs.bg4_main_enable = 0;
|
||||
window.regs.oam_main_enable = 0;
|
||||
|
||||
//$212f
|
||||
window.regs.bg1_sub_enable = 0;
|
||||
window.regs.bg2_sub_enable = 0;
|
||||
window.regs.bg3_sub_enable = 0;
|
||||
window.regs.bg4_sub_enable = 0;
|
||||
window.regs.oam_sub_enable = 0;
|
||||
|
||||
//$2130
|
||||
window.regs.col_main_mask = 0;
|
||||
window.regs.col_sub_mask = 0;
|
||||
screen.regs.addsub_mode = 0;
|
||||
screen.regs.direct_color = 0;
|
||||
|
||||
//$2131
|
||||
screen.regs.color_mode = 0;
|
||||
screen.regs.color_halve = 0;
|
||||
screen.regs.back_color_enable = 0;
|
||||
screen.regs.oam_color_enable = 0;
|
||||
screen.regs.bg4_color_enable = 0;
|
||||
screen.regs.bg3_color_enable = 0;
|
||||
screen.regs.bg2_color_enable = 0;
|
||||
screen.regs.bg1_color_enable = 0;
|
||||
|
||||
//$2132
|
||||
screen.regs.color_b = 0;
|
||||
screen.regs.color_g = 0;
|
||||
screen.regs.color_r = 0;
|
||||
|
||||
//$2133
|
||||
regs.mode7_extbg = 0;
|
||||
regs.pseudo_hires = 0;
|
||||
regs.overscan = 0;
|
||||
oam.regs.interlace = 0;
|
||||
regs.interlace = 0;
|
||||
|
||||
//$213e
|
||||
oam.regs.time_over = 0;
|
||||
oam.regs.range_over = 0;
|
||||
|
||||
mmio_update_video_mode();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -6,8 +6,13 @@ struct Regs {
|
|||
uint16 vram_readbuffer;
|
||||
uint8 oam_latchdata;
|
||||
uint8 cgram_latchdata;
|
||||
uint8 bgofs_latchdata;
|
||||
uint8 mode7_latchdata;
|
||||
|
||||
bool counters_latched;
|
||||
bool latch_hcounter;
|
||||
bool latch_vcounter;
|
||||
|
||||
//$2100
|
||||
bool display_disable;
|
||||
unsigned display_brightness;
|
||||
|
@ -17,6 +22,16 @@ struct Regs {
|
|||
uint16 oam_addr;
|
||||
bool oam_priority;
|
||||
|
||||
//$2105
|
||||
bool bg3_priority;
|
||||
unsigned bgmode;
|
||||
|
||||
//$210d
|
||||
uint16 mode7_hoffset;
|
||||
|
||||
//$210e
|
||||
uint16 mode7_voffset;
|
||||
|
||||
//$2115
|
||||
bool vram_incmode;
|
||||
unsigned vram_mapping;
|
||||
|
@ -40,6 +55,18 @@ struct Regs {
|
|||
|
||||
//$2121
|
||||
uint16 cgram_addr;
|
||||
|
||||
//$2133
|
||||
bool mode7_extbg;
|
||||
bool pseudo_hires;
|
||||
bool overscan;
|
||||
bool interlace;
|
||||
|
||||
//$213c
|
||||
uint16 hcounter;
|
||||
|
||||
//$213d
|
||||
uint16 vcounter;
|
||||
} regs;
|
||||
|
||||
uint16 get_vram_addr();
|
||||
|
@ -52,6 +79,7 @@ void oam_write(unsigned addr, uint8 data);
|
|||
uint8 cgram_read(unsigned addr);
|
||||
void cgram_write(unsigned addr, uint8 data);
|
||||
|
||||
void mmio_update_video_mode();
|
||||
uint8 mmio_read(unsigned addr);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
void mmio_reset();
|
||||
|
|
|
@ -6,6 +6,10 @@ namespace SNES {
|
|||
PPU ppu;
|
||||
|
||||
#include "mmio/mmio.cpp"
|
||||
#include "cache/cache.cpp"
|
||||
#include "background/background.cpp"
|
||||
#include "sprite/sprite.cpp"
|
||||
#include "window/window.cpp"
|
||||
#include "screen/screen.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
|
@ -48,6 +52,12 @@ void PPU::add_clocks(unsigned clocks) {
|
|||
|
||||
void PPU::render_scanline() {
|
||||
if(regs.display_disable) return screen.render_black();
|
||||
screen.scanline();
|
||||
bg1.render();
|
||||
bg2.render();
|
||||
bg3.render();
|
||||
bg4.render();
|
||||
oam.render();
|
||||
screen.render();
|
||||
}
|
||||
|
||||
|
@ -77,7 +87,15 @@ void PPU::reset() {
|
|||
mmio_reset();
|
||||
}
|
||||
|
||||
PPU::PPU() : screen(*this) {
|
||||
PPU::PPU() :
|
||||
cache(*this),
|
||||
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) {
|
||||
surface = new uint16[512 * 512];
|
||||
output = surface + 16 * 512;
|
||||
}
|
||||
|
|
|
@ -24,8 +24,19 @@ private:
|
|||
uint16 *output;
|
||||
|
||||
#include "mmio/mmio.hpp"
|
||||
#include "cache/cache.hpp"
|
||||
#include "background/background.hpp"
|
||||
#include "sprite/sprite.hpp"
|
||||
#include "window/window.hpp"
|
||||
#include "screen/screen.hpp"
|
||||
|
||||
Cache cache;
|
||||
Background bg1;
|
||||
Background bg2;
|
||||
Background bg3;
|
||||
Background bg4;
|
||||
Sprite oam;
|
||||
Window window;
|
||||
Screen screen;
|
||||
|
||||
struct Display {
|
||||
|
@ -37,6 +48,9 @@ private:
|
|||
void add_clocks(unsigned clocks);
|
||||
void render_scanline();
|
||||
|
||||
friend class PPU::Cache;
|
||||
friend class PPU::Background;
|
||||
friend class PPU::Sprite;
|
||||
friend class PPU::Screen;
|
||||
friend class Video;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,19 @@
|
|||
#ifdef PPU_CPP
|
||||
|
||||
unsigned PPU::Screen::get_palette(unsigned color) {
|
||||
color <<= 1;
|
||||
return (memory::cgram[color + 0] << 0) + (memory::cgram[color + 1] << 8);
|
||||
}
|
||||
|
||||
void PPU::Screen::scanline() {
|
||||
for(unsigned x = 0; x < 256; x++) {
|
||||
output.main[x].priority = 0;
|
||||
output.main[x].color = 0;
|
||||
output.sub[x].priority = 0;
|
||||
output.sub[x].color = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::Screen::render_black() {
|
||||
uint16 *data = self.output + self.vcounter() * 1024;
|
||||
memset(data, 0, self.display.width << 1);
|
||||
|
@ -7,11 +21,8 @@ void PPU::Screen::render_black() {
|
|||
|
||||
void PPU::Screen::render() {
|
||||
uint16 *data = self.output + self.vcounter() * 1024;
|
||||
uint16 color = memory::cgram[0];
|
||||
color |= memory::cgram[1] << 8;
|
||||
color = light_table[color];
|
||||
for(unsigned i = 0; i < 256; i++) {
|
||||
data[i] = color;
|
||||
data[i] = light_table[output.main[i].color];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,4 +49,20 @@ PPU::Screen::~Screen() {
|
|||
delete[] light_tables;
|
||||
}
|
||||
|
||||
void PPU::Screen::Output::plot_main(unsigned x, unsigned color, unsigned priority) {
|
||||
if(x & 256) return;
|
||||
if(priority > main[x].priority) {
|
||||
main[x].priority = priority;
|
||||
main[x].color = color;
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::Screen::Output::plot_sub(unsigned x, unsigned color, unsigned priority) {
|
||||
if(x & 256) return;
|
||||
if(priority > sub[x].priority) {
|
||||
sub[x].priority = priority;
|
||||
sub[x].color = color;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,17 +1,43 @@
|
|||
class Screen {
|
||||
public:
|
||||
struct Regs {
|
||||
bool addsub_mode;
|
||||
bool direct_color;
|
||||
|
||||
bool color_mode;
|
||||
bool color_halve;
|
||||
bool back_color_enable;
|
||||
bool oam_color_enable;
|
||||
bool bg4_color_enable;
|
||||
bool bg3_color_enable;
|
||||
bool bg2_color_enable;
|
||||
bool bg1_color_enable;
|
||||
|
||||
unsigned color_b;
|
||||
unsigned color_g;
|
||||
unsigned color_r;
|
||||
} regs;
|
||||
|
||||
struct Output {
|
||||
struct {
|
||||
unsigned color;
|
||||
unsigned priority;
|
||||
} main[256], sub[256];
|
||||
|
||||
void plot_main(unsigned x, unsigned color, unsigned priority);
|
||||
void plot_sub(unsigned x, unsigned color, unsigned priority);
|
||||
} output;
|
||||
|
||||
uint16 **light_tables;
|
||||
uint16 *light_table;
|
||||
|
||||
unsigned get_palette(unsigned color);
|
||||
|
||||
void scanline();
|
||||
void render_black();
|
||||
void render();
|
||||
Screen(PPU &self);
|
||||
~Screen();
|
||||
|
||||
private:
|
||||
PPU &self;
|
||||
uint16 **light_tables;
|
||||
uint16 *light_table;
|
||||
|
||||
struct Regs {
|
||||
} regs;
|
||||
|
||||
friend class PPU;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
#ifdef PPU_CPP
|
||||
|
||||
void PPU::Sprite::update_list(unsigned addr, uint8 data) {
|
||||
if(addr < 0x0200) {
|
||||
unsigned i = addr >> 2;
|
||||
switch(addr & 3) {
|
||||
case 0: list[i].x = (list[i].x & 0x0100) | data; break;
|
||||
case 1: list[i].y = (data + 1) & 0xff; break;
|
||||
case 2: list[i].character = data; break;
|
||||
case 3: list[i].vflip = data & 0x80;
|
||||
list[i].hflip = data & 0x40;
|
||||
list[i].priority = (data >> 4) & 3;
|
||||
list[i].palette = (data >> 1) & 7;
|
||||
list[i].use_nameselect = data & 0x01;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
unsigned i = (addr & 0x1f) << 2;
|
||||
list[i + 0].x = ((data & 0x01) << 8) | (list[i + 0].x & 0xff);
|
||||
list[i + 0].size = data & 0x02;
|
||||
list[i + 1].x = ((data & 0x04) << 8) | (list[i + 1].x & 0xff);
|
||||
list[i + 1].size = data & 0x08;
|
||||
list[i + 2].x = ((data & 0x10) << 8) | (list[i + 2].x & 0xff);
|
||||
list[i + 2].size = data & 0x20;
|
||||
list[i + 3].x = ((data & 0x40) << 8) | (list[i + 3].x & 0xff);
|
||||
list[i + 3].size = data & 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::Sprite::address_reset() {
|
||||
self.regs.oam_addr = self.regs.oam_baseaddr << 1;
|
||||
set_first();
|
||||
}
|
||||
|
||||
void PPU::Sprite::set_first() {
|
||||
regs.first_sprite = (self.regs.oam_priority == false ? 0 : (self.regs.oam_addr >> 2) & 127);
|
||||
}
|
||||
|
||||
bool PPU::Sprite::on_scanline(unsigned sprite) {
|
||||
auto &s = list[sprite];
|
||||
if(s.x > 256 && (s.x + s.width - 1) < 512) return false;
|
||||
signed height = (regs.interlace == false ? s.height : s.height >> 1);
|
||||
if(self.vcounter() >= s.y && self.vcounter() < (s.y + height)) return true;
|
||||
if((s.y + height) >= 256 && self.vcounter() < ((s.y + height) & 255)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void PPU::Sprite::render() {
|
||||
for(unsigned i = 0; i < 128; i++) {
|
||||
if(list[i].size == 0) {
|
||||
static unsigned width[] = { 8, 8, 8, 16, 16, 32, 16, 16 };
|
||||
static unsigned height[] = { 8, 8, 8, 16, 16, 32, 32, 32 };
|
||||
list[i].width = width[regs.base_size];
|
||||
list[i].height = height[regs.base_size];
|
||||
} else {
|
||||
static unsigned width[] = { 16, 32, 64, 32, 64, 64, 32, 32 };
|
||||
static unsigned height[] = { 16, 32, 64, 32, 64, 64, 64, 32 };
|
||||
list[i].width = width[regs.base_size];
|
||||
list[i].height = height[regs.base_size];
|
||||
if(regs.interlace && regs.base_size >= 6) list[i].height = 16;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned itemcount = 0;
|
||||
unsigned tilecount = 0;
|
||||
memset(output.priority, 0xff, 256);
|
||||
memset(itemlist, 0xff, 32);
|
||||
for(unsigned i = 0; i < 34; i++) tilelist[i].tile = 0xffff;
|
||||
|
||||
for(unsigned i = 0; i < 128; i++) {
|
||||
unsigned s = (regs.first_sprite + i) & 127;
|
||||
if(on_scanline(s) == false) continue;
|
||||
if(itemcount++ >= 32) break;
|
||||
itemlist[itemcount - 1] = s;
|
||||
}
|
||||
|
||||
for(signed i = 31; i >= 0; i--) {
|
||||
if(itemlist[i] == 0xff) continue;
|
||||
auto &s = list[itemlist[i]];
|
||||
unsigned tile_width = s.width >> 3;
|
||||
signed x = s.x;
|
||||
signed y = (self.vcounter() - s.y) & 0xff;
|
||||
if(regs.interlace) y <<= 1;
|
||||
|
||||
if(s.vflip) {
|
||||
if(s.width == s.height) {
|
||||
y = (s.height - 1) - y;
|
||||
} else {
|
||||
y = (y < s.width) ? ((s.width - 1) - y) : (s.width + ((s.width - 1) - (y - s.width)));
|
||||
}
|
||||
}
|
||||
|
||||
if(regs.interlace) {
|
||||
y = (s.vflip == false) ? (y + self.field()) : (y - self.field());
|
||||
}
|
||||
|
||||
x &= 511;
|
||||
y &= 255;
|
||||
|
||||
uint16 tdaddr = regs.tiledata_addr;
|
||||
uint16 chrx = (s.character >> 0) & 15;
|
||||
uint16 chry = (s.character >> 4) & 15;
|
||||
if(s.use_nameselect) {
|
||||
tdaddr += (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(tilecount++ >= 34) break;
|
||||
|
||||
unsigned n = tilecount - 1;
|
||||
tilelist[n].x = sx;
|
||||
tilelist[n].y = y;
|
||||
tilelist[n].priority = s.priority;
|
||||
tilelist[n].palette = 128 + (s.palette << 4);
|
||||
tilelist[n].hflip = s.hflip;
|
||||
|
||||
unsigned mx = (s.hflip == false) ? tx : ((tile_width - 1) - tx);
|
||||
unsigned pos = tdaddr + ((chry + ((chrx + mx) & 15)) << 5);
|
||||
tilelist[n].tile = (pos >> 5) & 0x07ff;
|
||||
}
|
||||
}
|
||||
|
||||
regs.time_over |= (tilecount > 34);
|
||||
regs.range_over |= (itemcount > 32);
|
||||
}
|
||||
|
||||
PPU::Sprite::Sprite(PPU &self) : self(self) {
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,60 @@
|
|||
class Sprite {
|
||||
struct Regs {
|
||||
unsigned priority0;
|
||||
unsigned priority1;
|
||||
unsigned priority2;
|
||||
unsigned priority3;
|
||||
|
||||
unsigned base_size;
|
||||
unsigned nameselect;
|
||||
unsigned tiledata_addr;
|
||||
unsigned first_sprite;
|
||||
|
||||
bool main_enable;
|
||||
bool sub_enable;
|
||||
|
||||
bool interlace;
|
||||
|
||||
bool time_over;
|
||||
bool range_over;
|
||||
} regs;
|
||||
|
||||
struct List {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned x;
|
||||
unsigned y;
|
||||
unsigned character;
|
||||
bool use_nameselect;
|
||||
bool vflip;
|
||||
bool hflip;
|
||||
unsigned palette;
|
||||
unsigned priority;
|
||||
bool size;
|
||||
} list[128];
|
||||
|
||||
uint8 itemlist[32];
|
||||
struct TileList {
|
||||
unsigned x;
|
||||
unsigned y;
|
||||
unsigned priority;
|
||||
unsigned palette;
|
||||
unsigned tile;
|
||||
bool hflip;
|
||||
} tilelist[34];
|
||||
|
||||
struct Output {
|
||||
uint8 palette[256];
|
||||
uint8 priority[256];
|
||||
} output;
|
||||
|
||||
void update_list(unsigned addr, uint8 data);
|
||||
void address_reset();
|
||||
void set_first();
|
||||
bool on_scanline(unsigned sprite);
|
||||
void render();
|
||||
Sprite(PPU &self);
|
||||
|
||||
PPU &self;
|
||||
friend class PPU;
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
#ifdef PPU_CPP
|
||||
|
||||
PPU::Window::Window(PPU &self) : self(self) {
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,66 @@
|
|||
class Window {
|
||||
struct Regs {
|
||||
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;
|
||||
|
||||
unsigned one_left;
|
||||
unsigned one_right;
|
||||
|
||||
unsigned two_left;
|
||||
unsigned two_right;
|
||||
|
||||
unsigned bg1_mask;
|
||||
unsigned bg2_mask;
|
||||
unsigned bg3_mask;
|
||||
unsigned bg4_mask;
|
||||
unsigned oam_mask;
|
||||
unsigned col_mask;
|
||||
|
||||
bool bg1_main_enable;
|
||||
bool bg2_main_enable;
|
||||
bool bg3_main_enable;
|
||||
bool bg4_main_enable;
|
||||
bool oam_main_enable;
|
||||
|
||||
bool bg1_sub_enable;
|
||||
bool bg2_sub_enable;
|
||||
bool bg3_sub_enable;
|
||||
bool bg4_sub_enable;
|
||||
bool oam_sub_enable;
|
||||
|
||||
unsigned col_main_mask;
|
||||
unsigned col_sub_mask;
|
||||
} regs;
|
||||
|
||||
Window(PPU &self);
|
||||
|
||||
PPU &self;
|
||||
friend class PPU;
|
||||
};
|
|
@ -20,8 +20,12 @@ void MSU1::enter() {
|
|||
if(mmio.audio_play) {
|
||||
if(audiofile.open()) {
|
||||
if(audiofile.end()) {
|
||||
if(!mmio.audio_repeat) mmio.audio_play = false;
|
||||
audiofile.seek(mmio.audio_offset = 58);
|
||||
if(!mmio.audio_repeat) {
|
||||
mmio.audio_play = false;
|
||||
audiofile.seek(mmio.audio_offset = 8);
|
||||
} else {
|
||||
audiofile.seek(mmio.audio_offset = mmio.audio_loop_offset);
|
||||
}
|
||||
} else {
|
||||
mmio.audio_offset += 4;
|
||||
left = audiofile.readl(2);
|
||||
|
@ -49,7 +53,7 @@ void MSU1::enable() {
|
|||
audio.coprocessor_frequency(44100.0);
|
||||
|
||||
if(datafile.open()) datafile.close();
|
||||
datafile.open(string() << cartridge.basename() << ".msu", file::mode_read);
|
||||
datafile.open(string(cartridge.basename(), ".msu"), file::mode_read);
|
||||
}
|
||||
|
||||
void MSU1::power() {
|
||||
|
@ -125,10 +129,14 @@ void MSU1::mmio_write(unsigned addr, uint8 data) {
|
|||
if(addr == 0x2005) {
|
||||
mmio.audio_track = (mmio.audio_track & 0x00ff) | (data << 8);
|
||||
if(audiofile.open()) audiofile.close();
|
||||
char track[16];
|
||||
sprintf(track, "-%u", mmio.audio_track);
|
||||
if(audiofile.open(string() << cartridge.basename() << track << ".wav", file::mode_read)) {
|
||||
audiofile.seek(mmio.audio_offset = 58); //skip WAV header
|
||||
if(audiofile.open(string(cartridge.basename(), "-", mmio.audio_track, ".pcm"), file::mode_read)) {
|
||||
uint32 header = audiofile.readm(4);
|
||||
if(header != 0x4d535531) { //verify 'MSU1' header
|
||||
audiofile.close();
|
||||
} else {
|
||||
mmio.audio_offset = 8;
|
||||
mmio.audio_loop_offset = 8 + audiofile.readl(4) * 4;
|
||||
}
|
||||
}
|
||||
mmio.audio_busy = false;
|
||||
mmio.audio_repeat = false;
|
||||
|
|
|
@ -27,8 +27,11 @@ private:
|
|||
struct MMIO {
|
||||
uint32 data_offset;
|
||||
uint32 audio_offset;
|
||||
uint32 audio_loop_offset;
|
||||
|
||||
uint16 audio_track;
|
||||
uint8 audio_volume;
|
||||
|
||||
bool data_busy;
|
||||
bool audio_busy;
|
||||
bool audio_repeat;
|
||||
|
|
|
@ -5,22 +5,23 @@ void MSU1::serialize(serializer &s) {
|
|||
|
||||
s.integer(mmio.data_offset);
|
||||
s.integer(mmio.audio_offset);
|
||||
s.integer(mmio.audio_loop_offset);
|
||||
|
||||
s.integer(mmio.audio_track);
|
||||
s.integer(mmio.audio_volume);
|
||||
|
||||
s.integer(mmio.data_busy);
|
||||
s.integer(mmio.audio_busy);
|
||||
s.integer(mmio.audio_repeat);
|
||||
s.integer(mmio.audio_play);
|
||||
|
||||
if(datafile.open()) datafile.close();
|
||||
if(datafile.open(string() << cartridge.basename() << ".msu", file::mode_read)) {
|
||||
if(datafile.open(string(cartridge.basename(), ".msu"), file::mode_read)) {
|
||||
datafile.seek(mmio.data_offset);
|
||||
}
|
||||
|
||||
if(audiofile.open()) audiofile.close();
|
||||
char track[16];
|
||||
sprintf(track, "-%u", mmio.audio_track);
|
||||
if(audiofile.open(string() << cartridge.basename() << track << ".wav", file::mode_read)) {
|
||||
if(audiofile.open(string(cartridge.basename(), "-", mmio.audio_track, ".pcm"), file::mode_read)) {
|
||||
audiofile.seek(mmio.audio_offset);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
namespace SNES {
|
||||
namespace Info {
|
||||
static const char Name[] = "bsnes";
|
||||
static const char Version[] = "068.05";
|
||||
static const char Version[] = "068.06";
|
||||
static const unsigned SerializerVersion = 13;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue