o cleanup
This commit is contained in:
346
tools/bsnes/ppu/bppu/bppu.cpp
Executable file
346
tools/bsnes/ppu/bppu/bppu.cpp
Executable file
@@ -0,0 +1,346 @@
|
||||
#include <../base.hpp>
|
||||
#define BPPU_CPP
|
||||
|
||||
#include "bppu_mmio.cpp"
|
||||
#include "bppu_render.cpp"
|
||||
|
||||
void bPPU::enter() {
|
||||
loop:
|
||||
//H = 0 (initialize)
|
||||
scanline();
|
||||
if(ivcounter() == 0) frame();
|
||||
add_clocks(10);
|
||||
|
||||
//H = 10 (OAM address reset)
|
||||
if(ivcounter() == (!overscan() ? 225 : 240)) {
|
||||
if(regs.display_disabled == false) {
|
||||
regs.oam_addr = regs.oam_baseaddr << 1;
|
||||
regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127;
|
||||
}
|
||||
}
|
||||
add_clocks(502);
|
||||
|
||||
//H = 512 (render)
|
||||
render_scanline();
|
||||
add_clocks(640);
|
||||
|
||||
//H = 1152 (cache OBSEL)
|
||||
cache.oam_basesize = regs.oam_basesize;
|
||||
cache.oam_nameselect = regs.oam_nameselect;
|
||||
cache.oam_tdaddr = regs.oam_tdaddr;
|
||||
add_clocks(ilineclocks() - 1152); //seek to start of next scanline
|
||||
|
||||
goto loop;
|
||||
}
|
||||
|
||||
void bPPU::add_clocks(unsigned clocks) {
|
||||
tock(clocks);
|
||||
scheduler.addclocks_ppu(clocks);
|
||||
}
|
||||
|
||||
void bPPU::scanline() {
|
||||
snes.scanline();
|
||||
line = ivcounter();
|
||||
|
||||
if(line == 0) {
|
||||
//RTO flag reset
|
||||
regs.time_over = false;
|
||||
regs.range_over = false;
|
||||
}
|
||||
|
||||
if(line == 1) {
|
||||
//mosaic reset
|
||||
for(int bg = BG1; bg <= BG4; bg++) regs.bg_y[bg] = 1;
|
||||
regs.mosaic_countdown = regs.mosaic_size + 1;
|
||||
regs.mosaic_countdown--;
|
||||
} else {
|
||||
for(int bg = BG1; bg <= BG4; bg++) {
|
||||
if(!regs.mosaic_enabled[bg] || !regs.mosaic_countdown) regs.bg_y[bg] = line;
|
||||
}
|
||||
if(!regs.mosaic_countdown) regs.mosaic_countdown = regs.mosaic_size + 1;
|
||||
regs.mosaic_countdown--;
|
||||
}
|
||||
}
|
||||
|
||||
void bPPU::render_scanline() {
|
||||
#ifdef FAST_FRAMESKIP
|
||||
//note: this bypasses RTO status flag calculations, which is observable by software
|
||||
if(status.render_output == false) return;
|
||||
#endif
|
||||
|
||||
if(line >= 1 && line < (!overscan() ? 225 : 240)) {
|
||||
render_line_oam_rto();
|
||||
render_line();
|
||||
}
|
||||
}
|
||||
|
||||
void bPPU::frame() {
|
||||
PPU::frame();
|
||||
snes.frame();
|
||||
|
||||
if(ifield() == 0) {
|
||||
display.interlace = regs.interlace;
|
||||
regs.scanlines = (regs.overscan == false) ? 224 : 239;
|
||||
}
|
||||
}
|
||||
|
||||
void bPPU::power() {
|
||||
PPU::power();
|
||||
|
||||
for(unsigned i = 0; i < memory::vram.size(); i++) memory::vram[i] = 0x00;
|
||||
for(unsigned i = 0; i < memory::oam.size(); i++) memory::oam[i] = 0x00;
|
||||
for(unsigned i = 0; i < memory::cgram.size(); i++) memory::cgram[i] = 0x00;
|
||||
flush_tiledata_cache();
|
||||
|
||||
region = (snes.region() == SNES::NTSC ? 0 : 1); //0 = NTSC, 1 = PAL
|
||||
|
||||
//$2100
|
||||
regs.display_disabled = 1;
|
||||
regs.display_brightness = 15;
|
||||
|
||||
//$2101
|
||||
regs.oam_basesize = 0;
|
||||
regs.oam_nameselect = 0;
|
||||
regs.oam_tdaddr = 0x0000;
|
||||
|
||||
cache.oam_basesize = 0;
|
||||
cache.oam_nameselect = 0;
|
||||
cache.oam_tdaddr = 0x0000;
|
||||
|
||||
//$2102-$2103
|
||||
regs.oam_baseaddr = 0x0000;
|
||||
regs.oam_addr = 0x0000;
|
||||
regs.oam_priority = false;
|
||||
regs.oam_firstsprite = 0;
|
||||
|
||||
//$2104
|
||||
regs.oam_latchdata = 0x00;
|
||||
|
||||
//$2105
|
||||
regs.bg_tilesize[BG1] = 0;
|
||||
regs.bg_tilesize[BG2] = 0;
|
||||
regs.bg_tilesize[BG3] = 0;
|
||||
regs.bg_tilesize[BG4] = 0;
|
||||
regs.bg3_priority = 0;
|
||||
regs.bg_mode = 0;
|
||||
|
||||
//$2106
|
||||
regs.mosaic_size = 0;
|
||||
regs.mosaic_enabled[BG1] = false;
|
||||
regs.mosaic_enabled[BG2] = false;
|
||||
regs.mosaic_enabled[BG3] = false;
|
||||
regs.mosaic_enabled[BG4] = false;
|
||||
regs.mosaic_countdown = 0;
|
||||
|
||||
//$2107-$210a
|
||||
regs.bg_scaddr[BG1] = 0x0000;
|
||||
regs.bg_scaddr[BG2] = 0x0000;
|
||||
regs.bg_scaddr[BG3] = 0x0000;
|
||||
regs.bg_scaddr[BG4] = 0x0000;
|
||||
regs.bg_scsize[BG1] = SC_32x32;
|
||||
regs.bg_scsize[BG2] = SC_32x32;
|
||||
regs.bg_scsize[BG3] = SC_32x32;
|
||||
regs.bg_scsize[BG4] = SC_32x32;
|
||||
|
||||
//$210b-$210c
|
||||
regs.bg_tdaddr[BG1] = 0x0000;
|
||||
regs.bg_tdaddr[BG2] = 0x0000;
|
||||
regs.bg_tdaddr[BG3] = 0x0000;
|
||||
regs.bg_tdaddr[BG4] = 0x0000;
|
||||
|
||||
//$210d-$2114
|
||||
regs.bg_ofslatch = 0x00;
|
||||
regs.m7_hofs = regs.m7_vofs = 0x0000;
|
||||
regs.bg_hofs[BG1] = regs.bg_vofs[BG1] = 0x0000;
|
||||
regs.bg_hofs[BG2] = regs.bg_vofs[BG2] = 0x0000;
|
||||
regs.bg_hofs[BG3] = regs.bg_vofs[BG3] = 0x0000;
|
||||
regs.bg_hofs[BG4] = regs.bg_vofs[BG4] = 0x0000;
|
||||
|
||||
//$2115
|
||||
regs.vram_incmode = 1;
|
||||
regs.vram_mapping = 0;
|
||||
regs.vram_incsize = 1;
|
||||
|
||||
//$2116-$2117
|
||||
regs.vram_addr = 0x0000;
|
||||
|
||||
//$211a
|
||||
regs.mode7_repeat = 0;
|
||||
regs.mode7_vflip = false;
|
||||
regs.mode7_hflip = false;
|
||||
|
||||
//$211b-$2120
|
||||
regs.m7_latch = 0x00;
|
||||
regs.m7a = 0x0000;
|
||||
regs.m7b = 0x0000;
|
||||
regs.m7c = 0x0000;
|
||||
regs.m7d = 0x0000;
|
||||
regs.m7x = 0x0000;
|
||||
regs.m7y = 0x0000;
|
||||
|
||||
//$2121
|
||||
regs.cgram_addr = 0x0000;
|
||||
|
||||
//$2122
|
||||
regs.cgram_latchdata = 0x00;
|
||||
|
||||
//$2123-$2125
|
||||
regs.window1_enabled[BG1] = false;
|
||||
regs.window1_enabled[BG2] = false;
|
||||
regs.window1_enabled[BG3] = false;
|
||||
regs.window1_enabled[BG4] = false;
|
||||
regs.window1_enabled[OAM] = false;
|
||||
regs.window1_enabled[COL] = false;
|
||||
|
||||
regs.window1_invert [BG1] = false;
|
||||
regs.window1_invert [BG2] = false;
|
||||
regs.window1_invert [BG3] = false;
|
||||
regs.window1_invert [BG4] = false;
|
||||
regs.window1_invert [OAM] = false;
|
||||
regs.window1_invert [COL] = false;
|
||||
|
||||
regs.window2_enabled[BG1] = false;
|
||||
regs.window2_enabled[BG2] = false;
|
||||
regs.window2_enabled[BG3] = false;
|
||||
regs.window2_enabled[BG4] = false;
|
||||
regs.window2_enabled[OAM] = false;
|
||||
regs.window2_enabled[COL] = false;
|
||||
|
||||
regs.window2_invert [BG1] = false;
|
||||
regs.window2_invert [BG2] = false;
|
||||
regs.window2_invert [BG3] = false;
|
||||
regs.window2_invert [BG4] = false;
|
||||
regs.window2_invert [OAM] = false;
|
||||
regs.window2_invert [COL] = false;
|
||||
|
||||
//$2126-$2129
|
||||
regs.window1_left = 0x00;
|
||||
regs.window1_right = 0x00;
|
||||
regs.window2_left = 0x00;
|
||||
regs.window2_right = 0x00;
|
||||
|
||||
//$212a-$212b
|
||||
regs.window_mask[BG1] = 0;
|
||||
regs.window_mask[BG2] = 0;
|
||||
regs.window_mask[BG3] = 0;
|
||||
regs.window_mask[BG4] = 0;
|
||||
regs.window_mask[OAM] = 0;
|
||||
regs.window_mask[COL] = 0;
|
||||
|
||||
//$212c-$212d
|
||||
regs.bg_enabled[BG1] = false;
|
||||
regs.bg_enabled[BG2] = false;
|
||||
regs.bg_enabled[BG3] = false;
|
||||
regs.bg_enabled[BG4] = false;
|
||||
regs.bg_enabled[OAM] = false;
|
||||
regs.bgsub_enabled[BG1] = false;
|
||||
regs.bgsub_enabled[BG2] = false;
|
||||
regs.bgsub_enabled[BG3] = false;
|
||||
regs.bgsub_enabled[BG4] = false;
|
||||
regs.bgsub_enabled[OAM] = false;
|
||||
|
||||
//$212e-$212f
|
||||
regs.window_enabled[BG1] = false;
|
||||
regs.window_enabled[BG2] = false;
|
||||
regs.window_enabled[BG3] = false;
|
||||
regs.window_enabled[BG4] = false;
|
||||
regs.window_enabled[OAM] = false;
|
||||
regs.sub_window_enabled[BG1] = false;
|
||||
regs.sub_window_enabled[BG2] = false;
|
||||
regs.sub_window_enabled[BG3] = false;
|
||||
regs.sub_window_enabled[BG4] = false;
|
||||
regs.sub_window_enabled[OAM] = false;
|
||||
|
||||
//$2130
|
||||
regs.color_mask = 0;
|
||||
regs.colorsub_mask = 0;
|
||||
regs.addsub_mode = false;
|
||||
regs.direct_color = false;
|
||||
|
||||
//$2131
|
||||
regs.color_mode = 0;
|
||||
regs.color_halve = false;
|
||||
regs.color_enabled[BACK] = false;
|
||||
regs.color_enabled[OAM] = false;
|
||||
regs.color_enabled[BG4] = false;
|
||||
regs.color_enabled[BG3] = false;
|
||||
regs.color_enabled[BG2] = false;
|
||||
regs.color_enabled[BG1] = false;
|
||||
|
||||
//$2132
|
||||
regs.color_r = 0x00;
|
||||
regs.color_g = 0x00;
|
||||
regs.color_b = 0x00;
|
||||
regs.color_rgb = 0x0000;
|
||||
|
||||
//$2133
|
||||
regs.mode7_extbg = false;
|
||||
regs.pseudo_hires = false;
|
||||
regs.overscan = false;
|
||||
regs.scanlines = 224;
|
||||
regs.oam_interlace = false;
|
||||
regs.interlace = false;
|
||||
|
||||
//$2137
|
||||
regs.hcounter = 0;
|
||||
regs.vcounter = 0;
|
||||
regs.latch_hcounter = 0;
|
||||
regs.latch_vcounter = 0;
|
||||
regs.counters_latched = false;
|
||||
|
||||
//$2139-$213a
|
||||
regs.vram_readbuffer = 0x0000;
|
||||
|
||||
//$213e
|
||||
regs.time_over = false;
|
||||
regs.range_over = false;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void bPPU::reset() {
|
||||
PPU::reset();
|
||||
PPU::frame();
|
||||
|
||||
display.interlace = false;
|
||||
display.overscan = false;
|
||||
regs.scanlines = 224;
|
||||
|
||||
memset(sprite_list, 0, sizeof(sprite_list));
|
||||
|
||||
//open bus support
|
||||
regs.ppu1_mdr = 0xff;
|
||||
regs.ppu2_mdr = 0xff;
|
||||
|
||||
//bg line counters
|
||||
regs.bg_y[0] = 0;
|
||||
regs.bg_y[1] = 0;
|
||||
regs.bg_y[2] = 0;
|
||||
regs.bg_y[3] = 0;
|
||||
}
|
||||
|
||||
bPPU::bPPU() {
|
||||
alloc_tiledata_cache();
|
||||
|
||||
for(int l = 0; l < 16; l++) {
|
||||
for(int i = 0; i < 4096; i++) {
|
||||
mosaic_table[l][i] = (i / (l + 1)) * (l + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for(int l = 0; l < 16; l++) {
|
||||
double m = (double)l / 15.0;
|
||||
for(int i = 0; i < 32 * 32; i++) {
|
||||
int r = (int)((double)((i) & 31) * m + 0.5);
|
||||
int g = (int)((double)((i >> 5) & 31) * m + 0.5);
|
||||
r = max(0, min(31, r));
|
||||
g = max(0, min(31, g));
|
||||
if(i < 32) light_table_b[l][i] = (r << 10);
|
||||
light_table_gr[l][i] = (g << 5) | (r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bPPU::~bPPU() {
|
||||
free_tiledata_cache();
|
||||
}
|
||||
254
tools/bsnes/ppu/bppu/bppu.hpp
Executable file
254
tools/bsnes/ppu/bppu/bppu.hpp
Executable file
@@ -0,0 +1,254 @@
|
||||
class bPPU : public PPU {
|
||||
public:
|
||||
void enter();
|
||||
void add_clocks(unsigned clocks);
|
||||
|
||||
uint8 region;
|
||||
unsigned line;
|
||||
|
||||
enum { NTSC = 0, PAL = 1 };
|
||||
enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5, COL = 5 };
|
||||
enum { SC_32x32 = 0, SC_64x32 = 1, SC_32x64 = 2, SC_64x64 = 3 };
|
||||
|
||||
struct {
|
||||
bool interlace;
|
||||
bool overscan;
|
||||
} display;
|
||||
|
||||
struct {
|
||||
//open bus support
|
||||
uint8 ppu1_mdr, ppu2_mdr;
|
||||
|
||||
//bg line counters
|
||||
uint16 bg_y[4];
|
||||
|
||||
//$2100
|
||||
bool display_disabled;
|
||||
uint8 display_brightness;
|
||||
|
||||
//$2101
|
||||
uint8 oam_basesize;
|
||||
uint8 oam_nameselect;
|
||||
uint16 oam_tdaddr;
|
||||
|
||||
//$2102-$2103
|
||||
uint16 oam_baseaddr;
|
||||
uint16 oam_addr;
|
||||
bool oam_priority;
|
||||
uint8 oam_firstsprite;
|
||||
|
||||
//$2104
|
||||
uint8 oam_latchdata;
|
||||
|
||||
//$2105
|
||||
bool bg_tilesize[4];
|
||||
bool bg3_priority;
|
||||
uint8 bg_mode;
|
||||
|
||||
//$2106
|
||||
uint8 mosaic_size;
|
||||
bool mosaic_enabled[4];
|
||||
uint16 mosaic_countdown;
|
||||
|
||||
//$2107-$210a
|
||||
uint16 bg_scaddr[4];
|
||||
uint8 bg_scsize[4];
|
||||
|
||||
//$210b-$210c
|
||||
uint16 bg_tdaddr[4];
|
||||
|
||||
//$210d-$2114
|
||||
uint8 bg_ofslatch;
|
||||
uint16 m7_hofs, m7_vofs;
|
||||
uint16 bg_hofs[4];
|
||||
uint16 bg_vofs[4];
|
||||
|
||||
//$2115
|
||||
bool vram_incmode;
|
||||
uint8 vram_mapping;
|
||||
uint8 vram_incsize;
|
||||
|
||||
//$2116-$2117
|
||||
uint16 vram_addr;
|
||||
|
||||
//$211a
|
||||
uint8 mode7_repeat;
|
||||
bool mode7_vflip;
|
||||
bool mode7_hflip;
|
||||
|
||||
//$211b-$2120
|
||||
uint8 m7_latch;
|
||||
uint16 m7a, m7b, m7c, m7d, m7x, m7y;
|
||||
|
||||
//$2121
|
||||
uint16 cgram_addr;
|
||||
|
||||
//$2122
|
||||
uint8 cgram_latchdata;
|
||||
|
||||
//$2123-$2125
|
||||
bool window1_enabled[6];
|
||||
bool window1_invert [6];
|
||||
bool window2_enabled[6];
|
||||
bool window2_invert [6];
|
||||
|
||||
//$2126-$2129
|
||||
uint8 window1_left, window1_right;
|
||||
uint8 window2_left, window2_right;
|
||||
|
||||
//$212a-$212b
|
||||
uint8 window_mask[6];
|
||||
|
||||
//$212c-$212d
|
||||
bool bg_enabled[5], bgsub_enabled[5];
|
||||
|
||||
//$212e-$212f
|
||||
bool window_enabled[5], sub_window_enabled[5];
|
||||
|
||||
//$2130
|
||||
uint8 color_mask, colorsub_mask;
|
||||
bool addsub_mode;
|
||||
bool direct_color;
|
||||
|
||||
//$2131
|
||||
bool color_mode, color_halve;
|
||||
bool color_enabled[6];
|
||||
|
||||
//$2132
|
||||
uint8 color_r, color_g, color_b;
|
||||
uint16 color_rgb;
|
||||
|
||||
//$2133
|
||||
//overscan and interlace are checked once per frame to
|
||||
//determine if entire frame should be interlaced/non-interlace
|
||||
//and overscan adjusted. therefore, the variables act sort of
|
||||
//like a buffer, but they do still affect internal rendering
|
||||
bool mode7_extbg;
|
||||
bool pseudo_hires;
|
||||
bool overscan;
|
||||
uint16 scanlines;
|
||||
bool oam_interlace;
|
||||
bool interlace;
|
||||
|
||||
//$2137
|
||||
uint16 hcounter, vcounter;
|
||||
bool latch_hcounter, latch_vcounter;
|
||||
bool counters_latched;
|
||||
|
||||
//$2139-$213a
|
||||
uint16 vram_readbuffer;
|
||||
|
||||
//$213e
|
||||
bool time_over, range_over;
|
||||
uint16 oam_itemcount, oam_tilecount;
|
||||
} regs;
|
||||
|
||||
struct {
|
||||
//$2101
|
||||
uint8 oam_basesize;
|
||||
uint8 oam_nameselect;
|
||||
uint16 oam_tdaddr;
|
||||
} cache;
|
||||
|
||||
alwaysinline bool interlace() const { return display.interlace; }
|
||||
alwaysinline bool overscan() const { return display.overscan; }
|
||||
alwaysinline bool hires() const { return (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6); }
|
||||
|
||||
uint16 get_vram_address();
|
||||
uint8 vram_mmio_read (uint16 addr);
|
||||
void vram_mmio_write (uint16 addr, uint8 data);
|
||||
uint8 oam_mmio_read (uint16 addr);
|
||||
void oam_mmio_write (uint16 addr, uint8 data);
|
||||
uint8 cgram_mmio_read (uint16 addr);
|
||||
void cgram_mmio_write(uint16 addr, uint8 data);
|
||||
|
||||
void mmio_w2100(uint8 value); //INIDISP
|
||||
void mmio_w2101(uint8 value); //OBSEL
|
||||
void mmio_w2102(uint8 value); //OAMADDL
|
||||
void mmio_w2103(uint8 value); //OAMADDH
|
||||
void mmio_w2104(uint8 value); //OAMDATA
|
||||
void mmio_w2105(uint8 value); //BGMODE
|
||||
void mmio_w2106(uint8 value); //MOSAIC
|
||||
void mmio_w2107(uint8 value); //BG1SC
|
||||
void mmio_w2108(uint8 value); //BG2SC
|
||||
void mmio_w2109(uint8 value); //BG3SC
|
||||
void mmio_w210a(uint8 value); //BG4SC
|
||||
void mmio_w210b(uint8 value); //BG12NBA
|
||||
void mmio_w210c(uint8 value); //BG34NBA
|
||||
void mmio_w210d(uint8 value); //BG1HOFS
|
||||
void mmio_w210e(uint8 value); //BG1VOFS
|
||||
void mmio_w210f(uint8 value); //BG2HOFS
|
||||
void mmio_w2110(uint8 value); //BG2VOFS
|
||||
void mmio_w2111(uint8 value); //BG3HOFS
|
||||
void mmio_w2112(uint8 value); //BG3VOFS
|
||||
void mmio_w2113(uint8 value); //BG4HOFS
|
||||
void mmio_w2114(uint8 value); //BG4VOFS
|
||||
void mmio_w2115(uint8 value); //VMAIN
|
||||
void mmio_w2116(uint8 value); //VMADDL
|
||||
void mmio_w2117(uint8 value); //VMADDH
|
||||
void mmio_w2118(uint8 value); //VMDATAL
|
||||
void mmio_w2119(uint8 value); //VMDATAH
|
||||
void mmio_w211a(uint8 value); //M7SEL
|
||||
void mmio_w211b(uint8 value); //M7A
|
||||
void mmio_w211c(uint8 value); //M7B
|
||||
void mmio_w211d(uint8 value); //M7C
|
||||
void mmio_w211e(uint8 value); //M7D
|
||||
void mmio_w211f(uint8 value); //M7X
|
||||
void mmio_w2120(uint8 value); //M7Y
|
||||
void mmio_w2121(uint8 value); //CGADD
|
||||
void mmio_w2122(uint8 value); //CGDATA
|
||||
void mmio_w2123(uint8 value); //W12SEL
|
||||
void mmio_w2124(uint8 value); //W34SEL
|
||||
void mmio_w2125(uint8 value); //WOBJSEL
|
||||
void mmio_w2126(uint8 value); //WH0
|
||||
void mmio_w2127(uint8 value); //WH1
|
||||
void mmio_w2128(uint8 value); //WH2
|
||||
void mmio_w2129(uint8 value); //WH3
|
||||
void mmio_w212a(uint8 value); //WBGLOG
|
||||
void mmio_w212b(uint8 value); //WOBJLOG
|
||||
void mmio_w212c(uint8 value); //TM
|
||||
void mmio_w212d(uint8 value); //TS
|
||||
void mmio_w212e(uint8 value); //TMW
|
||||
void mmio_w212f(uint8 value); //TSW
|
||||
void mmio_w2130(uint8 value); //CGWSEL
|
||||
void mmio_w2131(uint8 value); //CGADDSUB
|
||||
void mmio_w2132(uint8 value); //COLDATA
|
||||
void mmio_w2133(uint8 value); //SETINI
|
||||
uint8 mmio_r2134(); //MPYL
|
||||
uint8 mmio_r2135(); //MPYM
|
||||
uint8 mmio_r2136(); //MPYH
|
||||
uint8 mmio_r2137(); //SLHV
|
||||
uint8 mmio_r2138(); //OAMDATAREAD
|
||||
uint8 mmio_r2139(); //VMDATALREAD
|
||||
uint8 mmio_r213a(); //VMDATAHREAD
|
||||
uint8 mmio_r213b(); //CGDATAREAD
|
||||
uint8 mmio_r213c(); //OPHCT
|
||||
uint8 mmio_r213d(); //OPVCT
|
||||
uint8 mmio_r213e(); //STAT77
|
||||
uint8 mmio_r213f(); //STAT78
|
||||
|
||||
uint8 mmio_read(unsigned addr);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
|
||||
void latch_counters();
|
||||
|
||||
//PPU render functions
|
||||
#include "bppu_render.hpp"
|
||||
|
||||
uint16 light_table_b[16][32];
|
||||
uint16 light_table_gr[16][32 * 32];
|
||||
uint16 mosaic_table[16][4096];
|
||||
void render_line();
|
||||
|
||||
void update_oam_status();
|
||||
//required functions
|
||||
void run();
|
||||
void scanline();
|
||||
void render_scanline();
|
||||
void frame();
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
bPPU();
|
||||
~bPPU();
|
||||
};
|
||||
839
tools/bsnes/ppu/bppu/bppu_mmio.cpp
Executable file
839
tools/bsnes/ppu/bppu/bppu_mmio.cpp
Executable file
@@ -0,0 +1,839 @@
|
||||
#ifdef BPPU_CPP
|
||||
|
||||
void bPPU::latch_counters() {
|
||||
regs.hcounter = hdot();
|
||||
regs.vcounter = vcounter();
|
||||
regs.counters_latched = true;
|
||||
}
|
||||
|
||||
uint16 bPPU::get_vram_address() {
|
||||
uint16 addr = regs.vram_addr;
|
||||
switch(regs.vram_mapping) {
|
||||
case 0: break; //direct mapping
|
||||
case 1: addr = (addr & 0xff00) | ((addr & 0x001f) << 3) | ((addr >> 5) & 7); break;
|
||||
case 2: addr = (addr & 0xfe00) | ((addr & 0x003f) << 3) | ((addr >> 6) & 7); break;
|
||||
case 3: addr = (addr & 0xfc00) | ((addr & 0x007f) << 3) | ((addr >> 7) & 7); break;
|
||||
}
|
||||
return (addr << 1);
|
||||
}
|
||||
|
||||
//NOTE: all VRAM writes during active display are invalid. Unlike OAM and CGRAM, they will
|
||||
//not be written anywhere at all. The below address ranges for where writes are invalid have
|
||||
//been validated on hardware, as has the edge case where the S-CPU MDR can be written if the
|
||||
//write occurs during the very last clock cycle of vblank.
|
||||
|
||||
uint8 bPPU::vram_mmio_read(uint16 addr) {
|
||||
uint8 data;
|
||||
|
||||
if(regs.display_disabled == true) {
|
||||
data = memory::vram[addr];
|
||||
} else {
|
||||
uint16 v = vcounter();
|
||||
uint16 h = hcounter();
|
||||
uint16 ls = ((snes.region() == SNES::NTSC ? 525 : 625) >> 1) - 1;
|
||||
if(interlace() && !field()) ls++;
|
||||
|
||||
if(v == ls && h == 1362) {
|
||||
data = 0x00;
|
||||
} else if(v < (!overscan() ? 224 : 239)) {
|
||||
data = 0x00;
|
||||
} else if(v == (!overscan() ? 224 : 239)) {
|
||||
if(h == 1362) {
|
||||
data = memory::vram[addr];
|
||||
} else {
|
||||
data = 0x00;
|
||||
}
|
||||
} else {
|
||||
data = memory::vram[addr];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void bPPU::vram_mmio_write(uint16 addr, uint8 data) {
|
||||
if(regs.display_disabled == true) {
|
||||
memory::vram[addr] = data;
|
||||
} else {
|
||||
uint16 v = vcounter();
|
||||
uint16 h = hcounter();
|
||||
if(v == 0) {
|
||||
if(h <= 4) {
|
||||
memory::vram[addr] = data;
|
||||
} else if(h == 6) {
|
||||
memory::vram[addr] = cpu.regs.mdr;
|
||||
} else {
|
||||
//no write
|
||||
}
|
||||
} else if(v < (!overscan() ? 225 : 240)) {
|
||||
//no write
|
||||
} else if(v == (!overscan() ? 225 : 240)) {
|
||||
if(h <= 4) {
|
||||
//no write
|
||||
} else {
|
||||
memory::vram[addr] = data;
|
||||
}
|
||||
} else {
|
||||
memory::vram[addr] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//NOTE: OAM accesses during active display are rerouted to 0x0218 ... this can be considered
|
||||
//a hack. The actual address varies during rendering, as the S-PPU reads in data itself for
|
||||
//processing. Unfortunately, we have yet to determine how this works. The algorithm cannot be
|
||||
//reverse engineered using a scanline renderer such as this, and at this time, there does not
|
||||
//exist a more accurate SNES PPU emulator to work from. The only known game to actually access
|
||||
//OAM during active display is Uniracers. It expects accesses to map to offset 0x0218.
|
||||
//It was decided by public consensus to map writes to this address to match Uniracers, primarily
|
||||
//because it is the only game observed to do this, but also because mapping to this address does
|
||||
//not contradict any of our findings, because we have no findings whatsoever on this behavior.
|
||||
//Think of this what you will, I openly admit that this is a hack. But it is more accurate than
|
||||
//writing to the 'expected' address set by $2102,$2103, and will catch problems in software that
|
||||
//accidentally accesses OAM during active display by virtue of not returning the expected data.
|
||||
|
||||
uint8 bPPU::oam_mmio_read(uint16 addr) {
|
||||
addr &= 0x03ff;
|
||||
if(addr & 0x0200) addr &= 0x021f;
|
||||
uint8 data;
|
||||
|
||||
if(regs.display_disabled == true) {
|
||||
data = memory::oam[addr];
|
||||
} else {
|
||||
if(vcounter() < (!overscan() ? 225 : 240)) {
|
||||
data = memory::oam[0x0218];
|
||||
} else {
|
||||
data = memory::oam[addr];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void bPPU::oam_mmio_write(uint16 addr, uint8 data) {
|
||||
addr &= 0x03ff;
|
||||
if(addr & 0x0200) addr &= 0x021f;
|
||||
|
||||
if(regs.display_disabled == true) {
|
||||
memory::oam[addr] = data;
|
||||
} else {
|
||||
if(vcounter() < (!overscan() ? 225 : 240)) {
|
||||
memory::oam[0x0218] = data;
|
||||
} else {
|
||||
memory::oam[addr] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//NOTE: CGRAM writes during hblank are valid. During active display, the actual address the
|
||||
//data is written to varies, as the S-PPU itself changes the address. Like OAM, we do not know
|
||||
//the exact algorithm used, but we have zero known examples of any commercial software that
|
||||
//attempts to do this. Therefore, the addresses are mapped to 0x01ff. There is nothing special
|
||||
//about this address, it is simply more accurate to invalidate the 'expected' address than not.
|
||||
|
||||
uint8 bPPU::cgram_mmio_read(uint16 addr) {
|
||||
addr &= 0x01ff;
|
||||
uint8 data;
|
||||
|
||||
if(regs.display_disabled == true) {
|
||||
data = memory::cgram[addr];
|
||||
} else {
|
||||
uint16 v = vcounter();
|
||||
uint16 h = hcounter();
|
||||
if(v < (!overscan() ? 225 : 240) && h >= 72 && h < 1096) {
|
||||
data = memory::cgram[0x01ff] & 0x7f;
|
||||
} else {
|
||||
data = memory::cgram[addr];
|
||||
}
|
||||
}
|
||||
|
||||
if(addr & 1) data &= 0x7f;
|
||||
return data;
|
||||
}
|
||||
|
||||
void bPPU::cgram_mmio_write(uint16 addr, uint8 data) {
|
||||
addr &= 0x01ff;
|
||||
if(addr & 1) data &= 0x7f;
|
||||
|
||||
if(regs.display_disabled == true) {
|
||||
memory::cgram[addr] = data;
|
||||
} else {
|
||||
uint16 v = vcounter();
|
||||
uint16 h = hcounter();
|
||||
if(v < (!overscan() ? 225 : 240) && h >= 72 && h < 1096) {
|
||||
memory::cgram[0x01ff] = data & 0x7f;
|
||||
} else {
|
||||
memory::cgram[addr] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//INIDISP
|
||||
void bPPU::mmio_w2100(uint8 value) {
|
||||
if(regs.display_disabled == true && vcounter() == (!overscan() ? 225 : 240)) {
|
||||
regs.oam_addr = regs.oam_baseaddr << 1;
|
||||
regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127;
|
||||
}
|
||||
|
||||
regs.display_disabled = !!(value & 0x80);
|
||||
regs.display_brightness = value & 15;
|
||||
}
|
||||
|
||||
//OBSEL
|
||||
void bPPU::mmio_w2101(uint8 value) {
|
||||
regs.oam_basesize = (value >> 5) & 7;
|
||||
regs.oam_nameselect = (value >> 3) & 3;
|
||||
regs.oam_tdaddr = (value & 3) << 14;
|
||||
}
|
||||
|
||||
//OAMADDL
|
||||
void bPPU::mmio_w2102(uint8 data) {
|
||||
regs.oam_baseaddr = (regs.oam_baseaddr & ~0xff) | (data << 0);
|
||||
regs.oam_baseaddr &= 0x01ff;
|
||||
regs.oam_addr = regs.oam_baseaddr << 1;
|
||||
regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127;
|
||||
}
|
||||
|
||||
//OAMADDH
|
||||
void bPPU::mmio_w2103(uint8 data) {
|
||||
regs.oam_priority = !!(data & 0x80);
|
||||
regs.oam_baseaddr = (regs.oam_baseaddr & 0xff) | (data << 8);
|
||||
regs.oam_baseaddr &= 0x01ff;
|
||||
regs.oam_addr = regs.oam_baseaddr << 1;
|
||||
regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127;
|
||||
}
|
||||
|
||||
//OAMDATA
|
||||
void bPPU::mmio_w2104(uint8 data) {
|
||||
if(regs.oam_addr & 0x0200) {
|
||||
oam_mmio_write(regs.oam_addr, data);
|
||||
} else if((regs.oam_addr & 1) == 0) {
|
||||
regs.oam_latchdata = data;
|
||||
} else {
|
||||
oam_mmio_write((regs.oam_addr & ~1) + 0, regs.oam_latchdata);
|
||||
oam_mmio_write((regs.oam_addr & ~1) + 1, data);
|
||||
}
|
||||
|
||||
regs.oam_addr++;
|
||||
regs.oam_addr &= 0x03ff;
|
||||
regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127;
|
||||
}
|
||||
|
||||
//BGMODE
|
||||
void bPPU::mmio_w2105(uint8 value) {
|
||||
regs.bg_tilesize[BG4] = !!(value & 0x80);
|
||||
regs.bg_tilesize[BG3] = !!(value & 0x40);
|
||||
regs.bg_tilesize[BG2] = !!(value & 0x20);
|
||||
regs.bg_tilesize[BG1] = !!(value & 0x10);
|
||||
regs.bg3_priority = !!(value & 0x08);
|
||||
regs.bg_mode = (value & 7);
|
||||
}
|
||||
|
||||
//MOSAIC
|
||||
void bPPU::mmio_w2106(uint8 value) {
|
||||
regs.mosaic_size = (value >> 4) & 15;
|
||||
regs.mosaic_enabled[BG4] = !!(value & 0x08);
|
||||
regs.mosaic_enabled[BG3] = !!(value & 0x04);
|
||||
regs.mosaic_enabled[BG2] = !!(value & 0x02);
|
||||
regs.mosaic_enabled[BG1] = !!(value & 0x01);
|
||||
}
|
||||
|
||||
//BG1SC
|
||||
void bPPU::mmio_w2107(uint8 value) {
|
||||
regs.bg_scaddr[BG1] = (value & 0x7c) << 9;
|
||||
regs.bg_scsize[BG1] = value & 3;
|
||||
}
|
||||
|
||||
//BG2SC
|
||||
void bPPU::mmio_w2108(uint8 value) {
|
||||
regs.bg_scaddr[BG2] = (value & 0x7c) << 9;
|
||||
regs.bg_scsize[BG2] = value & 3;
|
||||
}
|
||||
|
||||
//BG3SC
|
||||
void bPPU::mmio_w2109(uint8 value) {
|
||||
regs.bg_scaddr[BG3] = (value & 0x7c) << 9;
|
||||
regs.bg_scsize[BG3] = value & 3;
|
||||
}
|
||||
|
||||
//BG4SC
|
||||
void bPPU::mmio_w210a(uint8 value) {
|
||||
regs.bg_scaddr[BG4] = (value & 0x7c) << 9;
|
||||
regs.bg_scsize[BG4] = value & 3;
|
||||
}
|
||||
|
||||
//BG12NBA
|
||||
void bPPU::mmio_w210b(uint8 value) {
|
||||
regs.bg_tdaddr[BG1] = (value & 0x07) << 13;
|
||||
regs.bg_tdaddr[BG2] = (value & 0x70) << 9;
|
||||
}
|
||||
|
||||
//BG34NBA
|
||||
void bPPU::mmio_w210c(uint8 value) {
|
||||
regs.bg_tdaddr[BG3] = (value & 0x07) << 13;
|
||||
regs.bg_tdaddr[BG4] = (value & 0x70) << 9;
|
||||
}
|
||||
|
||||
//BG1HOFS
|
||||
void bPPU::mmio_w210d(uint8 value) {
|
||||
regs.m7_hofs = (value << 8) | regs.m7_latch;
|
||||
regs.m7_latch = value;
|
||||
|
||||
regs.bg_hofs[BG1] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG1] >> 8) & 7);
|
||||
regs.bg_ofslatch = value;
|
||||
}
|
||||
|
||||
//BG1VOFS
|
||||
void bPPU::mmio_w210e(uint8 value) {
|
||||
regs.m7_vofs = (value << 8) | regs.m7_latch;
|
||||
regs.m7_latch = value;
|
||||
|
||||
regs.bg_vofs[BG1] = (value << 8) | (regs.bg_ofslatch);
|
||||
regs.bg_ofslatch = value;
|
||||
}
|
||||
|
||||
//BG2HOFS
|
||||
void bPPU::mmio_w210f(uint8 value) {
|
||||
regs.bg_hofs[BG2] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG2] >> 8) & 7);
|
||||
regs.bg_ofslatch = value;
|
||||
}
|
||||
|
||||
//BG2VOFS
|
||||
void bPPU::mmio_w2110(uint8 value) {
|
||||
regs.bg_vofs[BG2] = (value << 8) | (regs.bg_ofslatch);
|
||||
regs.bg_ofslatch = value;
|
||||
}
|
||||
|
||||
//BG3HOFS
|
||||
void bPPU::mmio_w2111(uint8 value) {
|
||||
regs.bg_hofs[BG3] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG3] >> 8) & 7);
|
||||
regs.bg_ofslatch = value;
|
||||
}
|
||||
|
||||
//BG3VOFS
|
||||
void bPPU::mmio_w2112(uint8 value) {
|
||||
regs.bg_vofs[BG3] = (value << 8) | (regs.bg_ofslatch);
|
||||
regs.bg_ofslatch = value;
|
||||
}
|
||||
|
||||
//BG4HOFS
|
||||
void bPPU::mmio_w2113(uint8 value) {
|
||||
regs.bg_hofs[BG4] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG4] >> 8) & 7);
|
||||
regs.bg_ofslatch = value;
|
||||
}
|
||||
|
||||
//BG4VOFS
|
||||
void bPPU::mmio_w2114(uint8 value) {
|
||||
regs.bg_vofs[BG4] = (value << 8) | (regs.bg_ofslatch);
|
||||
regs.bg_ofslatch = value;
|
||||
}
|
||||
|
||||
//VMAIN
|
||||
void bPPU::mmio_w2115(uint8 value) {
|
||||
regs.vram_incmode = !!(value & 0x80);
|
||||
regs.vram_mapping = (value >> 2) & 3;
|
||||
switch(value & 3) {
|
||||
case 0: regs.vram_incsize = 1; break;
|
||||
case 1: regs.vram_incsize = 32; break;
|
||||
case 2: regs.vram_incsize = 128; break;
|
||||
case 3: regs.vram_incsize = 128; break;
|
||||
}
|
||||
}
|
||||
|
||||
//VMADDL
|
||||
void bPPU::mmio_w2116(uint8 value) {
|
||||
regs.vram_addr = (regs.vram_addr & 0xff00) | value;
|
||||
uint16 addr = get_vram_address();
|
||||
regs.vram_readbuffer = vram_mmio_read(addr + 0);
|
||||
regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8;
|
||||
}
|
||||
|
||||
//VMADDH
|
||||
void bPPU::mmio_w2117(uint8 value) {
|
||||
regs.vram_addr = (value << 8) | (regs.vram_addr & 0x00ff);
|
||||
uint16 addr = get_vram_address();
|
||||
regs.vram_readbuffer = vram_mmio_read(addr + 0);
|
||||
regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8;
|
||||
}
|
||||
|
||||
//VMDATAL
|
||||
void bPPU::mmio_w2118(uint8 value) {
|
||||
uint16 addr = get_vram_address();
|
||||
vram_mmio_write(addr, value);
|
||||
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
|
||||
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
|
||||
bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1;
|
||||
|
||||
if(regs.vram_incmode == 0) {
|
||||
regs.vram_addr += regs.vram_incsize;
|
||||
}
|
||||
}
|
||||
|
||||
//VMDATAH
|
||||
void bPPU::mmio_w2119(uint8 value) {
|
||||
uint16 addr = get_vram_address() + 1;
|
||||
vram_mmio_write(addr, value);
|
||||
bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1;
|
||||
bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1;
|
||||
bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1;
|
||||
|
||||
if(regs.vram_incmode == 1) {
|
||||
regs.vram_addr += regs.vram_incsize;
|
||||
}
|
||||
}
|
||||
|
||||
//M7SEL
|
||||
void bPPU::mmio_w211a(uint8 value) {
|
||||
regs.mode7_repeat = (value >> 6) & 3;
|
||||
regs.mode7_vflip = !!(value & 0x02);
|
||||
regs.mode7_hflip = !!(value & 0x01);
|
||||
}
|
||||
|
||||
//M7A
|
||||
void bPPU::mmio_w211b(uint8 value) {
|
||||
regs.m7a = (value << 8) | regs.m7_latch;
|
||||
regs.m7_latch = value;
|
||||
}
|
||||
|
||||
//M7B
|
||||
void bPPU::mmio_w211c(uint8 value) {
|
||||
regs.m7b = (value << 8) | regs.m7_latch;
|
||||
regs.m7_latch = value;
|
||||
}
|
||||
|
||||
//M7C
|
||||
void bPPU::mmio_w211d(uint8 value) {
|
||||
regs.m7c = (value << 8) | regs.m7_latch;
|
||||
regs.m7_latch = value;
|
||||
}
|
||||
|
||||
//M7D
|
||||
void bPPU::mmio_w211e(uint8 value) {
|
||||
regs.m7d = (value << 8) | regs.m7_latch;
|
||||
regs.m7_latch = value;
|
||||
}
|
||||
|
||||
//M7X
|
||||
void bPPU::mmio_w211f(uint8 value) {
|
||||
regs.m7x = (value << 8) | regs.m7_latch;
|
||||
regs.m7_latch = value;
|
||||
}
|
||||
|
||||
//M7Y
|
||||
void bPPU::mmio_w2120(uint8 value) {
|
||||
regs.m7y = (value << 8) | regs.m7_latch;
|
||||
regs.m7_latch = value;
|
||||
}
|
||||
|
||||
//CGADD
|
||||
void bPPU::mmio_w2121(uint8 value) {
|
||||
regs.cgram_addr = value << 1;
|
||||
}
|
||||
|
||||
//CGDATA
|
||||
//note: CGRAM palette data format is 15-bits
|
||||
//(0,bbbbb,ggggg,rrrrr). Highest bit is ignored,
|
||||
//as evidenced by $213b CGRAM data reads.
|
||||
//
|
||||
//anomie indicates writes to CGDATA work the same
|
||||
//as writes to OAMDATA's low table. need to verify
|
||||
//this on hardware.
|
||||
void bPPU::mmio_w2122(uint8 value) {
|
||||
if(!(regs.cgram_addr & 1)) {
|
||||
regs.cgram_latchdata = value;
|
||||
} else {
|
||||
cgram_mmio_write((regs.cgram_addr & 0x01fe), regs.cgram_latchdata);
|
||||
cgram_mmio_write((regs.cgram_addr & 0x01fe) + 1, value & 0x7f);
|
||||
}
|
||||
regs.cgram_addr++;
|
||||
regs.cgram_addr &= 0x01ff;
|
||||
}
|
||||
|
||||
//W12SEL
|
||||
void bPPU::mmio_w2123(uint8 value) {
|
||||
regs.window2_enabled[BG2] = !!(value & 0x80);
|
||||
regs.window2_invert [BG2] = !!(value & 0x40);
|
||||
regs.window1_enabled[BG2] = !!(value & 0x20);
|
||||
regs.window1_invert [BG2] = !!(value & 0x10);
|
||||
regs.window2_enabled[BG1] = !!(value & 0x08);
|
||||
regs.window2_invert [BG1] = !!(value & 0x04);
|
||||
regs.window1_enabled[BG1] = !!(value & 0x02);
|
||||
regs.window1_invert [BG1] = !!(value & 0x01);
|
||||
}
|
||||
|
||||
//W34SEL
|
||||
void bPPU::mmio_w2124(uint8 value) {
|
||||
regs.window2_enabled[BG4] = !!(value & 0x80);
|
||||
regs.window2_invert [BG4] = !!(value & 0x40);
|
||||
regs.window1_enabled[BG4] = !!(value & 0x20);
|
||||
regs.window1_invert [BG4] = !!(value & 0x10);
|
||||
regs.window2_enabled[BG3] = !!(value & 0x08);
|
||||
regs.window2_invert [BG3] = !!(value & 0x04);
|
||||
regs.window1_enabled[BG3] = !!(value & 0x02);
|
||||
regs.window1_invert [BG3] = !!(value & 0x01);
|
||||
}
|
||||
|
||||
//WOBJSEL
|
||||
void bPPU::mmio_w2125(uint8 value) {
|
||||
regs.window2_enabled[COL] = !!(value & 0x80);
|
||||
regs.window2_invert [COL] = !!(value & 0x40);
|
||||
regs.window1_enabled[COL] = !!(value & 0x20);
|
||||
regs.window1_invert [COL] = !!(value & 0x10);
|
||||
regs.window2_enabled[OAM] = !!(value & 0x08);
|
||||
regs.window2_invert [OAM] = !!(value & 0x04);
|
||||
regs.window1_enabled[OAM] = !!(value & 0x02);
|
||||
regs.window1_invert [OAM] = !!(value & 0x01);
|
||||
}
|
||||
|
||||
//WH0
|
||||
void bPPU::mmio_w2126(uint8 value) {
|
||||
regs.window1_left = value;
|
||||
}
|
||||
|
||||
//WH1
|
||||
void bPPU::mmio_w2127(uint8 value) {
|
||||
regs.window1_right = value;
|
||||
}
|
||||
|
||||
//WH2
|
||||
void bPPU::mmio_w2128(uint8 value) {
|
||||
regs.window2_left = value;
|
||||
}
|
||||
|
||||
//WH3
|
||||
void bPPU::mmio_w2129(uint8 value) {
|
||||
regs.window2_right = value;
|
||||
}
|
||||
|
||||
//WBGLOG
|
||||
void bPPU::mmio_w212a(uint8 value) {
|
||||
regs.window_mask[BG4] = (value >> 6) & 3;
|
||||
regs.window_mask[BG3] = (value >> 4) & 3;
|
||||
regs.window_mask[BG2] = (value >> 2) & 3;
|
||||
regs.window_mask[BG1] = (value ) & 3;
|
||||
}
|
||||
|
||||
//WOBJLOG
|
||||
void bPPU::mmio_w212b(uint8 value) {
|
||||
regs.window_mask[COL] = (value >> 2) & 3;
|
||||
regs.window_mask[OAM] = (value ) & 3;
|
||||
}
|
||||
|
||||
//TM
|
||||
void bPPU::mmio_w212c(uint8 value) {
|
||||
regs.bg_enabled[OAM] = !!(value & 0x10);
|
||||
regs.bg_enabled[BG4] = !!(value & 0x08);
|
||||
regs.bg_enabled[BG3] = !!(value & 0x04);
|
||||
regs.bg_enabled[BG2] = !!(value & 0x02);
|
||||
regs.bg_enabled[BG1] = !!(value & 0x01);
|
||||
}
|
||||
|
||||
//TS
|
||||
void bPPU::mmio_w212d(uint8 value) {
|
||||
regs.bgsub_enabled[OAM] = !!(value & 0x10);
|
||||
regs.bgsub_enabled[BG4] = !!(value & 0x08);
|
||||
regs.bgsub_enabled[BG3] = !!(value & 0x04);
|
||||
regs.bgsub_enabled[BG2] = !!(value & 0x02);
|
||||
regs.bgsub_enabled[BG1] = !!(value & 0x01);
|
||||
}
|
||||
|
||||
//TMW
|
||||
void bPPU::mmio_w212e(uint8 value) {
|
||||
regs.window_enabled[OAM] = !!(value & 0x10);
|
||||
regs.window_enabled[BG4] = !!(value & 0x08);
|
||||
regs.window_enabled[BG3] = !!(value & 0x04);
|
||||
regs.window_enabled[BG2] = !!(value & 0x02);
|
||||
regs.window_enabled[BG1] = !!(value & 0x01);
|
||||
}
|
||||
|
||||
//TSW
|
||||
void bPPU::mmio_w212f(uint8 value) {
|
||||
regs.sub_window_enabled[OAM] = !!(value & 0x10);
|
||||
regs.sub_window_enabled[BG4] = !!(value & 0x08);
|
||||
regs.sub_window_enabled[BG3] = !!(value & 0x04);
|
||||
regs.sub_window_enabled[BG2] = !!(value & 0x02);
|
||||
regs.sub_window_enabled[BG1] = !!(value & 0x01);
|
||||
}
|
||||
|
||||
//CGWSEL
|
||||
void bPPU::mmio_w2130(uint8 value) {
|
||||
regs.color_mask = (value >> 6) & 3;
|
||||
regs.colorsub_mask = (value >> 4) & 3;
|
||||
regs.addsub_mode = !!(value & 0x02);
|
||||
regs.direct_color = !!(value & 0x01);
|
||||
}
|
||||
|
||||
//CGADDSUB
|
||||
void bPPU::mmio_w2131(uint8 value) {
|
||||
regs.color_mode = !!(value & 0x80);
|
||||
regs.color_halve = !!(value & 0x40);
|
||||
regs.color_enabled[BACK] = !!(value & 0x20);
|
||||
regs.color_enabled[OAM] = !!(value & 0x10);
|
||||
regs.color_enabled[BG4] = !!(value & 0x08);
|
||||
regs.color_enabled[BG3] = !!(value & 0x04);
|
||||
regs.color_enabled[BG2] = !!(value & 0x02);
|
||||
regs.color_enabled[BG1] = !!(value & 0x01);
|
||||
}
|
||||
|
||||
//COLDATA
|
||||
void bPPU::mmio_w2132(uint8 value) {
|
||||
if(value & 0x80) regs.color_b = value & 0x1f;
|
||||
if(value & 0x40) regs.color_g = value & 0x1f;
|
||||
if(value & 0x20) regs.color_r = value & 0x1f;
|
||||
|
||||
regs.color_rgb = (regs.color_r)
|
||||
| (regs.color_g << 5)
|
||||
| (regs.color_b << 10);
|
||||
}
|
||||
|
||||
//SETINI
|
||||
void bPPU::mmio_w2133(uint8 value) {
|
||||
regs.mode7_extbg = !!(value & 0x40);
|
||||
regs.pseudo_hires = !!(value & 0x08);
|
||||
regs.overscan = !!(value & 0x04);
|
||||
regs.oam_interlace = !!(value & 0x02);
|
||||
regs.interlace = !!(value & 0x01);
|
||||
|
||||
display.overscan = regs.overscan;
|
||||
}
|
||||
|
||||
//MPYL
|
||||
uint8 bPPU::mmio_r2134() {
|
||||
uint32 r;
|
||||
r = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
|
||||
regs.ppu1_mdr = r;
|
||||
return regs.ppu1_mdr;
|
||||
}
|
||||
|
||||
//MPYM
|
||||
uint8 bPPU::mmio_r2135() {
|
||||
uint32 r;
|
||||
r = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
|
||||
regs.ppu1_mdr = r >> 8;
|
||||
return regs.ppu1_mdr;
|
||||
}
|
||||
|
||||
//MPYH
|
||||
uint8 bPPU::mmio_r2136() {
|
||||
uint32 r;
|
||||
r = ((int16)regs.m7a * (int8)(regs.m7b >> 8));
|
||||
regs.ppu1_mdr = r >> 16;
|
||||
return regs.ppu1_mdr;
|
||||
}
|
||||
|
||||
//SLHV
|
||||
uint8 bPPU::mmio_r2137() {
|
||||
if(cpu.pio() & 0x80) {
|
||||
latch_counters();
|
||||
}
|
||||
return cpu.regs.mdr;
|
||||
}
|
||||
|
||||
//OAMDATAREAD
|
||||
uint8 bPPU::mmio_r2138() {
|
||||
regs.ppu1_mdr = oam_mmio_read(regs.oam_addr);
|
||||
|
||||
regs.oam_addr++;
|
||||
regs.oam_addr &= 0x03ff;
|
||||
regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127;
|
||||
|
||||
return regs.ppu1_mdr;
|
||||
}
|
||||
|
||||
//VMDATALREAD
|
||||
uint8 bPPU::mmio_r2139() {
|
||||
uint16 addr = get_vram_address();
|
||||
regs.ppu1_mdr = regs.vram_readbuffer;
|
||||
if(regs.vram_incmode == 0) {
|
||||
addr &= 0xfffe;
|
||||
regs.vram_readbuffer = vram_mmio_read(addr + 0);
|
||||
regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8;
|
||||
regs.vram_addr += regs.vram_incsize;
|
||||
}
|
||||
return regs.ppu1_mdr;
|
||||
}
|
||||
|
||||
//VMDATAHREAD
|
||||
uint8 bPPU::mmio_r213a() {
|
||||
uint16 addr = get_vram_address() + 1;
|
||||
regs.ppu1_mdr = regs.vram_readbuffer >> 8;
|
||||
if(regs.vram_incmode == 1) {
|
||||
addr &= 0xfffe;
|
||||
regs.vram_readbuffer = vram_mmio_read(addr + 0);
|
||||
regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8;
|
||||
regs.vram_addr += regs.vram_incsize;
|
||||
}
|
||||
return regs.ppu1_mdr;
|
||||
}
|
||||
|
||||
//CGDATAREAD
|
||||
//note: CGRAM palette data is 15-bits (0,bbbbb,ggggg,rrrrr)
|
||||
//therefore, the high byte read from each color does not
|
||||
//update bit 7 of the PPU2 MDR.
|
||||
uint8 bPPU::mmio_r213b() {
|
||||
if(!(regs.cgram_addr & 1)) {
|
||||
regs.ppu2_mdr = cgram_mmio_read(regs.cgram_addr) & 0xff;
|
||||
} else {
|
||||
regs.ppu2_mdr &= 0x80;
|
||||
regs.ppu2_mdr |= cgram_mmio_read(regs.cgram_addr) & 0x7f;
|
||||
}
|
||||
regs.cgram_addr++;
|
||||
regs.cgram_addr &= 0x01ff;
|
||||
return regs.ppu2_mdr;
|
||||
}
|
||||
|
||||
//OPHCT
|
||||
uint8 bPPU::mmio_r213c() {
|
||||
if(!regs.latch_hcounter) {
|
||||
regs.ppu2_mdr = regs.hcounter & 0xff;
|
||||
} else {
|
||||
regs.ppu2_mdr &= 0xfe;
|
||||
regs.ppu2_mdr |= (regs.hcounter >> 8) & 1;
|
||||
}
|
||||
regs.latch_hcounter ^= 1;
|
||||
return regs.ppu2_mdr;
|
||||
}
|
||||
|
||||
//OPVCT
|
||||
uint8 bPPU::mmio_r213d() {
|
||||
if(!regs.latch_vcounter) {
|
||||
regs.ppu2_mdr = regs.vcounter & 0xff;
|
||||
} else {
|
||||
regs.ppu2_mdr &= 0xfe;
|
||||
regs.ppu2_mdr |= (regs.vcounter >> 8) & 1;
|
||||
}
|
||||
regs.latch_vcounter ^= 1;
|
||||
return regs.ppu2_mdr;
|
||||
}
|
||||
|
||||
//STAT77
|
||||
uint8 bPPU::mmio_r213e() {
|
||||
uint8 r = 0x00;
|
||||
r |= (regs.time_over) ? 0x80 : 0x00;
|
||||
r |= (regs.range_over) ? 0x40 : 0x00;
|
||||
r |= (regs.ppu1_mdr & 0x10);
|
||||
r |= (ppu1_version & 0x0f);
|
||||
regs.ppu1_mdr = r;
|
||||
return regs.ppu1_mdr;
|
||||
}
|
||||
|
||||
//STAT78
|
||||
uint8 bPPU::mmio_r213f() {
|
||||
uint8 r = 0x00;
|
||||
regs.latch_hcounter = 0;
|
||||
regs.latch_vcounter = 0;
|
||||
|
||||
r |= field() << 7;
|
||||
if(!(cpu.pio() & 0x80)) {
|
||||
r |= 0x40;
|
||||
} else if(regs.counters_latched == true) {
|
||||
r |= 0x40;
|
||||
regs.counters_latched = false;
|
||||
}
|
||||
r |= (regs.ppu2_mdr & 0x20);
|
||||
r |= (region << 4); //0 = NTSC, 1 = PAL
|
||||
r |= (ppu2_version & 0x0f);
|
||||
regs.ppu2_mdr = r;
|
||||
return regs.ppu2_mdr;
|
||||
}
|
||||
|
||||
uint8 bPPU::mmio_read(unsigned addr) {
|
||||
scheduler.sync_cpuppu();
|
||||
|
||||
switch(addr & 0xffff) {
|
||||
case 0x2104:
|
||||
case 0x2105:
|
||||
case 0x2106:
|
||||
case 0x2108:
|
||||
case 0x2109:
|
||||
case 0x210a:
|
||||
case 0x2114:
|
||||
case 0x2115:
|
||||
case 0x2116:
|
||||
case 0x2118:
|
||||
case 0x2119:
|
||||
case 0x211a:
|
||||
case 0x2124:
|
||||
case 0x2125:
|
||||
case 0x2126:
|
||||
case 0x2128:
|
||||
case 0x2129:
|
||||
case 0x212a: return regs.ppu1_mdr;
|
||||
case 0x2134: return mmio_r2134(); //MPYL
|
||||
case 0x2135: return mmio_r2135(); //MPYM
|
||||
case 0x2136: return mmio_r2136(); //MPYH
|
||||
case 0x2137: return mmio_r2137(); //SLHV
|
||||
case 0x2138: return mmio_r2138(); //OAMDATAREAD
|
||||
case 0x2139: return mmio_r2139(); //VMDATALREAD
|
||||
case 0x213a: return mmio_r213a(); //VMDATAHREAD
|
||||
case 0x213b: return mmio_r213b(); //CGDATAREAD
|
||||
case 0x213c: return mmio_r213c(); //OPHCT
|
||||
case 0x213d: return mmio_r213d(); //OPVCT
|
||||
case 0x213e: return mmio_r213e(); //STAT77
|
||||
case 0x213f: return mmio_r213f(); //STAT78
|
||||
}
|
||||
|
||||
//return 0x00;
|
||||
return cpu.regs.mdr;
|
||||
}
|
||||
|
||||
void bPPU::mmio_write(unsigned addr, uint8 data) {
|
||||
scheduler.sync_cpuppu();
|
||||
|
||||
switch(addr & 0xffff) {
|
||||
case 0x2100: mmio_w2100(data); return; //INIDISP
|
||||
case 0x2101: mmio_w2101(data); return; //OBSEL
|
||||
case 0x2102: mmio_w2102(data); return; //OAMADDL
|
||||
case 0x2103: mmio_w2103(data); return; //OAMADDH
|
||||
case 0x2104: mmio_w2104(data); return; //OAMDATA
|
||||
case 0x2105: mmio_w2105(data); return; //BGMODE
|
||||
case 0x2106: mmio_w2106(data); return; //MOSAIC
|
||||
case 0x2107: mmio_w2107(data); return; //BG1SC
|
||||
case 0x2108: mmio_w2108(data); return; //BG2SC
|
||||
case 0x2109: mmio_w2109(data); return; //BG3SC
|
||||
case 0x210a: mmio_w210a(data); return; //BG4SC
|
||||
case 0x210b: mmio_w210b(data); return; //BG12NBA
|
||||
case 0x210c: mmio_w210c(data); return; //BG34NBA
|
||||
case 0x210d: mmio_w210d(data); return; //BG1HOFS
|
||||
case 0x210e: mmio_w210e(data); return; //BG1VOFS
|
||||
case 0x210f: mmio_w210f(data); return; //BG2HOFS
|
||||
case 0x2110: mmio_w2110(data); return; //BG2VOFS
|
||||
case 0x2111: mmio_w2111(data); return; //BG3HOFS
|
||||
case 0x2112: mmio_w2112(data); return; //BG3VOFS
|
||||
case 0x2113: mmio_w2113(data); return; //BG4HOFS
|
||||
case 0x2114: mmio_w2114(data); return; //BG4VOFS
|
||||
case 0x2115: mmio_w2115(data); return; //VMAIN
|
||||
case 0x2116: mmio_w2116(data); return; //VMADDL
|
||||
case 0x2117: mmio_w2117(data); return; //VMADDH
|
||||
case 0x2118: mmio_w2118(data); return; //VMDATAL
|
||||
case 0x2119: mmio_w2119(data); return; //VMDATAH
|
||||
case 0x211a: mmio_w211a(data); return; //M7SEL
|
||||
case 0x211b: mmio_w211b(data); return; //M7A
|
||||
case 0x211c: mmio_w211c(data); return; //M7B
|
||||
case 0x211d: mmio_w211d(data); return; //M7C
|
||||
case 0x211e: mmio_w211e(data); return; //M7D
|
||||
case 0x211f: mmio_w211f(data); return; //M7X
|
||||
case 0x2120: mmio_w2120(data); return; //M7Y
|
||||
case 0x2121: mmio_w2121(data); return; //CGADD
|
||||
case 0x2122: mmio_w2122(data); return; //CGDATA
|
||||
case 0x2123: mmio_w2123(data); return; //W12SEL
|
||||
case 0x2124: mmio_w2124(data); return; //W34SEL
|
||||
case 0x2125: mmio_w2125(data); return; //WOBJSEL
|
||||
case 0x2126: mmio_w2126(data); return; //WH0
|
||||
case 0x2127: mmio_w2127(data); return; //WH1
|
||||
case 0x2128: mmio_w2128(data); return; //WH2
|
||||
case 0x2129: mmio_w2129(data); return; //WH3
|
||||
case 0x212a: mmio_w212a(data); return; //WBGLOG
|
||||
case 0x212b: mmio_w212b(data); return; //WOBJLOG
|
||||
case 0x212c: mmio_w212c(data); return; //TM
|
||||
case 0x212d: mmio_w212d(data); return; //TS
|
||||
case 0x212e: mmio_w212e(data); return; //TMW
|
||||
case 0x212f: mmio_w212f(data); return; //TSW
|
||||
case 0x2130: mmio_w2130(data); return; //CGWSEL
|
||||
case 0x2131: mmio_w2131(data); return; //CGADDSUB
|
||||
case 0x2132: mmio_w2132(data); return; //COLDATA
|
||||
case 0x2133: mmio_w2133(data); return; //SETINI
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ifdef BPPU_CPP
|
||||
150
tools/bsnes/ppu/bppu/bppu_render.cpp
Executable file
150
tools/bsnes/ppu/bppu/bppu_render.cpp
Executable file
@@ -0,0 +1,150 @@
|
||||
#ifdef BPPU_CPP
|
||||
|
||||
#include "bppu_render_cache.cpp"
|
||||
#include "bppu_render_windows.cpp"
|
||||
#include "bppu_render_bg.cpp"
|
||||
#include "bppu_render_oam.cpp"
|
||||
#include "bppu_render_mode7.cpp"
|
||||
#include "bppu_render_addsub.cpp"
|
||||
#include "bppu_render_line.cpp"
|
||||
|
||||
//this function can be used to disable BG / OAM layer rendering (currently unused)
|
||||
bool bPPU::render_enabled(uint8 bg, uint8 pri) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Mode 0: ->
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
|
||||
BG4B, BG3B, OAM0, BG4A, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode0() {
|
||||
render_line_bg(BG1, COLORDEPTH_4, 8, 11);
|
||||
render_line_bg(BG2, COLORDEPTH_4, 7, 10);
|
||||
render_line_bg(BG3, COLORDEPTH_4, 2, 5);
|
||||
render_line_bg(BG4, COLORDEPTH_4, 1, 4);
|
||||
render_line_oam(3, 6, 9, 12);
|
||||
}
|
||||
|
||||
/*
|
||||
Mode 1 (pri=1): ->
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
|
||||
BG3B, OAM0, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3, BG3A
|
||||
|
||||
Mode 1 (pri=0): ->
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
|
||||
BG3B, OAM0, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode1() {
|
||||
if(regs.bg3_priority) {
|
||||
render_line_bg(BG1, COLORDEPTH_16, 5, 8);
|
||||
render_line_bg(BG2, COLORDEPTH_16, 4, 7);
|
||||
render_line_bg(BG3, COLORDEPTH_4, 1, 10);
|
||||
render_line_oam(2, 3, 6, 9);
|
||||
} else {
|
||||
render_line_bg(BG1, COLORDEPTH_16, 6, 9);
|
||||
render_line_bg(BG2, COLORDEPTH_16, 5, 8);
|
||||
render_line_bg(BG3, COLORDEPTH_4, 1, 3);
|
||||
render_line_oam(2, 4, 7, 10);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Mode 2: ->
|
||||
1, 2, 3, 4, 5, 6, 7, 8
|
||||
BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode2() {
|
||||
render_line_bg(BG1, COLORDEPTH_16, 3, 7);
|
||||
render_line_bg(BG2, COLORDEPTH_16, 1, 5);
|
||||
render_line_oam(2, 4, 6, 8);
|
||||
}
|
||||
|
||||
/*
|
||||
Mode 3: ->
|
||||
1, 2, 3, 4, 5, 6, 7, 8
|
||||
BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode3() {
|
||||
render_line_bg(BG1, COLORDEPTH_256, 3, 7);
|
||||
render_line_bg(BG2, COLORDEPTH_16, 1, 5);
|
||||
render_line_oam(2, 4, 6, 8);
|
||||
}
|
||||
|
||||
/*
|
||||
Mode 4: ->
|
||||
1, 2, 3, 4, 5, 6, 7, 8
|
||||
BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode4() {
|
||||
render_line_bg(BG1, COLORDEPTH_256, 3, 7);
|
||||
render_line_bg(BG2, COLORDEPTH_4, 1, 5);
|
||||
render_line_oam(2, 4, 6, 8);
|
||||
}
|
||||
|
||||
/*
|
||||
Mode 5: ->
|
||||
1, 2, 3, 4, 5, 6, 7, 8
|
||||
BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode5() {
|
||||
render_line_bg(BG1, COLORDEPTH_16, 3, 7);
|
||||
render_line_bg(BG2, COLORDEPTH_4, 1, 5);
|
||||
render_line_oam(2, 4, 6, 8);
|
||||
}
|
||||
|
||||
/*
|
||||
Mode 6: ->
|
||||
1, 2, 3, 4, 5, 6
|
||||
OAM0, BG1B, OAM1, OAM2, BG1A, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode6() {
|
||||
render_line_bg(BG1, COLORDEPTH_16, 2, 5);
|
||||
render_line_oam(1, 3, 4, 6);
|
||||
}
|
||||
|
||||
/*
|
||||
Mode7: ->
|
||||
1, 2, 3, 4, 5
|
||||
OAM0, BG1n, OAM1, OAM2, OAM3
|
||||
|
||||
Mode 7 EXTBG: ->
|
||||
1, 2, 3, 4, 5, 6, 7
|
||||
BG2B, OAM0, BG1n, OAM1, BG2A, OAM2, OAM3
|
||||
*/
|
||||
void bPPU::render_line_mode7() {
|
||||
if(regs.mode7_extbg == false) {
|
||||
render_line_mode7(BG1, 2, 2);
|
||||
render_line_oam(1, 3, 4, 5);
|
||||
} else {
|
||||
render_line_mode7(BG1, 3, 3);
|
||||
render_line_mode7(BG2, 1, 5);
|
||||
render_line_oam(2, 4, 6, 7);
|
||||
}
|
||||
}
|
||||
|
||||
void bPPU::render_line() {
|
||||
if(regs.display_disabled == true) {
|
||||
render_line_clear();
|
||||
return;
|
||||
}
|
||||
|
||||
flush_pixel_cache();
|
||||
build_window_tables(COL);
|
||||
update_bg_info();
|
||||
|
||||
switch(regs.bg_mode) {
|
||||
case 0: render_line_mode0(); break;
|
||||
case 1: render_line_mode1(); break;
|
||||
case 2: render_line_mode2(); break;
|
||||
case 3: render_line_mode3(); break;
|
||||
case 4: render_line_mode4(); break;
|
||||
case 5: render_line_mode5(); break;
|
||||
case 6: render_line_mode6(); break;
|
||||
case 7: render_line_mode7(); break;
|
||||
}
|
||||
|
||||
render_line_output();
|
||||
}
|
||||
|
||||
#endif //ifdef BPPU_CPP
|
||||
97
tools/bsnes/ppu/bppu/bppu_render.hpp
Executable file
97
tools/bsnes/ppu/bppu/bppu_render.hpp
Executable file
@@ -0,0 +1,97 @@
|
||||
//bppu_render.cpp
|
||||
inline bool render_enabled(uint8 bg, uint8 pri);
|
||||
inline void render_line_mode0();
|
||||
inline void render_line_mode1();
|
||||
inline void render_line_mode2();
|
||||
inline void render_line_mode3();
|
||||
inline void render_line_mode4();
|
||||
inline void render_line_mode5();
|
||||
inline void render_line_mode6();
|
||||
inline void render_line_mode7();
|
||||
|
||||
//bppu_render_cache.cpp
|
||||
enum { COLORDEPTH_4 = 0, COLORDEPTH_16 = 1, COLORDEPTH_256 = 2 };
|
||||
enum { TILE_2BIT = 0, TILE_4BIT = 1, TILE_8BIT = 2 };
|
||||
|
||||
struct _pixel {
|
||||
//bgr555 color data for main/subscreen pixels: 0x0000 = transparent / use palette color # 0
|
||||
//needs to be bgr555 instead of palette index for direct color mode ($2130 bit 0) to work
|
||||
uint16 src_main, src_sub;
|
||||
//indicates source of palette # for main/subscreen (BG1-4, OAM, or back)
|
||||
uint8 bg_main, bg_sub;
|
||||
//color_exemption -- true when bg == OAM && palette index >= 192, disables color add/sub effects
|
||||
uint8 ce_main, ce_sub;
|
||||
//priority level of src_n. to set src_n,
|
||||
//the priority of the pixel must be >pri_n
|
||||
uint8 pri_main, pri_sub;
|
||||
} pixel_cache[256];
|
||||
|
||||
uint8 *bg_tiledata[3];
|
||||
uint8 *bg_tiledata_state[3]; //0 = valid, 1 = dirty
|
||||
|
||||
void render_bg_tile(uint8 color_depth, uint16 tile_num);
|
||||
inline void flush_pixel_cache();
|
||||
void alloc_tiledata_cache();
|
||||
void flush_tiledata_cache();
|
||||
void free_tiledata_cache();
|
||||
|
||||
//bppu_render_windows.cpp
|
||||
struct _window {
|
||||
uint8 main[256], sub[256];
|
||||
} window[6];
|
||||
|
||||
void build_window_table(uint8 bg, bool mainscreen);
|
||||
void build_window_tables(uint8 bg);
|
||||
|
||||
//bppu_render_bg.cpp
|
||||
struct {
|
||||
uint16 tw, th; //tile width, height
|
||||
uint16 mx, my; //screen mask x, y
|
||||
uint16 scx, scy; //sc index offsets
|
||||
} bg_info[4];
|
||||
|
||||
void update_bg_info();
|
||||
uint16 bg_get_tile(uint8 bg, uint16 x, uint16 y);
|
||||
void render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri1_pos);
|
||||
|
||||
//bppu_render_oam.cpp
|
||||
struct sprite_item {
|
||||
uint8 width, height;
|
||||
uint16 x, y;
|
||||
uint8 character;
|
||||
bool use_nameselect;
|
||||
bool vflip, hflip;
|
||||
uint8 palette;
|
||||
uint8 priority;
|
||||
} sprite_list[128], *spr;
|
||||
|
||||
uint8 oam_itemlist[32];
|
||||
struct oam_tileitem {
|
||||
uint16 x, y, pri, pal, tile;
|
||||
bool hflip;
|
||||
} oam_tilelist[34];
|
||||
|
||||
enum { OAM_PRI_NONE = 4 };
|
||||
uint8 oam_line_pal[256], oam_line_pri[256];
|
||||
|
||||
void build_sprite_list();
|
||||
bool is_sprite_on_scanline();
|
||||
void load_oam_tiles();
|
||||
void render_oam_tile(int tile_num);
|
||||
void render_line_oam_rto();
|
||||
void render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos);
|
||||
void render_line_oam_lores(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos);
|
||||
|
||||
//bppu_render_mode7.cpp
|
||||
void render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos);
|
||||
|
||||
//bppu_render_addsub.cpp
|
||||
inline uint16 addsub(uint32 x, uint32 y, bool halve);
|
||||
|
||||
//bppu_render_line.cpp
|
||||
inline uint16 get_palette(uint8 index);
|
||||
inline uint16 get_direct_color(uint8 p, uint8 t);
|
||||
inline uint16 get_pixel_normal(uint32 x);
|
||||
inline uint16 get_pixel_swap(uint32 x);
|
||||
void render_line_output();
|
||||
void render_line_clear();
|
||||
25
tools/bsnes/ppu/bppu/bppu_render_addsub.cpp
Executable file
25
tools/bsnes/ppu/bppu/bppu_render_addsub.cpp
Executable file
@@ -0,0 +1,25 @@
|
||||
#ifdef BPPU_CPP
|
||||
|
||||
//color addition / subtraction
|
||||
//thanks go to blargg for the optimized algorithms
|
||||
inline uint16 bPPU::addsub(uint32 x, uint32 y, bool halve) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
211
tools/bsnes/ppu/bppu/bppu_render_bg.cpp
Executable file
211
tools/bsnes/ppu/bppu/bppu_render_bg.cpp
Executable file
@@ -0,0 +1,211 @@
|
||||
#ifdef BPPU_CPP
|
||||
|
||||
//called once at the start of every rendered scanline
|
||||
void bPPU::update_bg_info() {
|
||||
const unsigned hires = (regs.bg_mode == 5 || regs.bg_mode == 6);
|
||||
const unsigned width = (!hires ? 256 : 512);
|
||||
|
||||
for(unsigned bg = 0; bg < 4; bg++) {
|
||||
bg_info[bg].th = (regs.bg_tilesize[bg] ? 4 : 3);
|
||||
bg_info[bg].tw = (hires ? 4 : bg_info[bg].th);
|
||||
|
||||
bg_info[bg].mx = (bg_info[bg].th == 4 ? (width << 1) : width);
|
||||
bg_info[bg].my = bg_info[bg].mx;
|
||||
if(regs.bg_scsize[bg] & 0x01) bg_info[bg].mx <<= 1;
|
||||
if(regs.bg_scsize[bg] & 0x02) bg_info[bg].my <<= 1;
|
||||
bg_info[bg].mx--;
|
||||
bg_info[bg].my--;
|
||||
|
||||
bg_info[bg].scy = (regs.bg_scsize[bg] & 0x02) ? (32 << 5) : 0;
|
||||
bg_info[bg].scx = (regs.bg_scsize[bg] & 0x01) ? (32 << 5) : 0;
|
||||
if(regs.bg_scsize[bg] == 3) bg_info[bg].scy <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint16 bPPU::bg_get_tile(uint8 bg, uint16 x, uint16 y) {
|
||||
x = (x & bg_info[bg].mx) >> bg_info[bg].tw;
|
||||
y = (y & bg_info[bg].my) >> bg_info[bg].th;
|
||||
|
||||
uint16 pos = ((y & 0x1f) << 5) + (x & 0x1f);
|
||||
if(y & 0x20) pos += bg_info[bg].scy;
|
||||
if(x & 0x20) pos += bg_info[bg].scx;
|
||||
|
||||
const uint16 addr = regs.bg_scaddr[bg] + (pos << 1);
|
||||
return memory::vram[addr] + (memory::vram[addr + 1] << 8);
|
||||
}
|
||||
|
||||
#define setpixel_main(x) \
|
||||
if(pixel_cache[x].pri_main < tile_pri) { \
|
||||
pixel_cache[x].pri_main = tile_pri; \
|
||||
pixel_cache[x].bg_main = bg; \
|
||||
pixel_cache[x].src_main = col; \
|
||||
pixel_cache[x].ce_main = false; \
|
||||
}
|
||||
|
||||
#define setpixel_sub(x) \
|
||||
if(pixel_cache[x].pri_sub < tile_pri) { \
|
||||
pixel_cache[x].pri_sub = tile_pri; \
|
||||
pixel_cache[x].bg_sub = bg; \
|
||||
pixel_cache[x].src_sub = col; \
|
||||
pixel_cache[x].ce_sub = false; \
|
||||
}
|
||||
|
||||
void bPPU::render_line_bg(uint8 bg, uint8 color_depth, uint8 pri0_pos, uint8 pri1_pos) {
|
||||
if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
//are layers disabled by user?
|
||||
if(render_enabled(bg, 0) == false) pri0_pos = 0;
|
||||
if(render_enabled(bg, 1) == false) pri1_pos = 0;
|
||||
//nothing to render?
|
||||
if(!pri0_pos && !pri1_pos) return;
|
||||
|
||||
const bool bg_enabled = regs.bg_enabled[bg];
|
||||
const bool bgsub_enabled = regs.bgsub_enabled[bg];
|
||||
|
||||
const uint16 opt_valid_bit = (bg == BG1) ? 0x2000 : (bg == BG2) ? 0x4000 : 0x0000;
|
||||
const uint8 bgpal_index = (regs.bg_mode == 0 ? (bg << 5) : 0);
|
||||
|
||||
const uint8 pal_size = 2 << color_depth; //<<2 (*4), <<4 (*16), <<8 (*256)
|
||||
const uint16 tile_mask = 0x0fff >> color_depth; //0x0fff, 0x07ff, 0x03ff
|
||||
//4 + color_depth = >>(4-6) -- / {16, 32, 64 } bytes/tile
|
||||
//index is a tile number count to add to base tile number
|
||||
const unsigned tiledata_index = regs.bg_tdaddr[bg] >> (4 + color_depth);
|
||||
|
||||
const uint8 *bg_td = bg_tiledata[color_depth];
|
||||
const uint8 *bg_td_state = bg_tiledata_state[color_depth];
|
||||
|
||||
const uint8 tile_width = bg_info[bg].tw;
|
||||
const uint8 tile_height = bg_info[bg].th;
|
||||
const uint16 mask_x = bg_info[bg].mx; //screen width mask
|
||||
const uint16 mask_y = bg_info[bg].my; //screen height mask
|
||||
|
||||
uint16 y = regs.bg_y[bg];
|
||||
uint16 hscroll = regs.bg_hofs[bg];
|
||||
uint16 vscroll = regs.bg_vofs[bg];
|
||||
|
||||
const unsigned hires = (regs.bg_mode == 5 || regs.bg_mode == 6);
|
||||
const unsigned width = (!hires ? 256 : 512);
|
||||
|
||||
if(hires) {
|
||||
hscroll <<= 1;
|
||||
if(regs.interlace) y = (y << 1) + ifield();
|
||||
}
|
||||
|
||||
uint16 hval, vval;
|
||||
uint16 tile_pri, tile_num;
|
||||
uint8 pal_index, pal_num;
|
||||
uint16 hoffset, voffset, opt_x, col;
|
||||
bool mirror_x, mirror_y;
|
||||
|
||||
const uint8 *tile_ptr;
|
||||
const uint16 *mtable = mosaic_table[regs.mosaic_enabled[bg] ? regs.mosaic_size : 0];
|
||||
const bool is_opt_mode = (regs.bg_mode == 2 || regs.bg_mode == 4 || regs.bg_mode == 6);
|
||||
const bool is_direct_color_mode = (regs.direct_color == true && bg == BG1 && (regs.bg_mode == 3 || regs.bg_mode == 4));
|
||||
|
||||
build_window_tables(bg);
|
||||
const uint8 *wt_main = window[bg].main;
|
||||
const uint8 *wt_sub = window[bg].sub;
|
||||
|
||||
uint16 prev_x = 0xffff, prev_y = 0xffff, prev_optx = 0xffff;
|
||||
for(uint16 x = 0; x < width; x++) {
|
||||
hoffset = mtable[x] + hscroll;
|
||||
voffset = y + vscroll;
|
||||
|
||||
if(is_opt_mode) {
|
||||
opt_x = (x + (hscroll & 7));
|
||||
|
||||
//tile 0 is unaffected by OPT mode...
|
||||
if(opt_x >= 8) {
|
||||
//cache tile data in hval, vval if possible
|
||||
if((opt_x >> 3) != (prev_optx >> 3)) {
|
||||
prev_optx = opt_x;
|
||||
|
||||
hval = bg_get_tile(BG3, (opt_x - 8) + (regs.bg_hofs[BG3] & ~7), regs.bg_vofs[BG3]);
|
||||
if(regs.bg_mode != 4) {
|
||||
vval = bg_get_tile(BG3, (opt_x - 8) + (regs.bg_hofs[BG3] & ~7), regs.bg_vofs[BG3] + 8);
|
||||
}
|
||||
}
|
||||
|
||||
if(regs.bg_mode == 4) {
|
||||
if(hval & opt_valid_bit) {
|
||||
if(!(hval & 0x8000)) {
|
||||
hoffset = opt_x + (hval & ~7);
|
||||
} else {
|
||||
voffset = y + hval;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(hval & opt_valid_bit) {
|
||||
hoffset = opt_x + (hval & ~7);
|
||||
}
|
||||
if(vval & opt_valid_bit) {
|
||||
voffset = y + vval;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hoffset &= mask_x;
|
||||
voffset &= mask_y;
|
||||
|
||||
if((hoffset >> 3) != prev_x || (voffset >> 3) != prev_y) {
|
||||
prev_x = (hoffset >> 3);
|
||||
prev_y = (voffset >> 3);
|
||||
|
||||
tile_num = bg_get_tile(bg, hoffset, voffset); //format = vhopppcc cccccccc
|
||||
mirror_y = (tile_num & 0x8000);
|
||||
mirror_x = (tile_num & 0x4000);
|
||||
tile_pri = (tile_num & 0x2000) ? pri1_pos : pri0_pos;
|
||||
pal_num = ((tile_num >> 10) & 7);
|
||||
pal_index = bgpal_index + (pal_num << pal_size);
|
||||
|
||||
if(tile_width == 4) { //16x16 horizontal tile mirroring
|
||||
if((bool)(hoffset & 8) != mirror_x) tile_num++;
|
||||
}
|
||||
|
||||
if(tile_height == 4) { //16x16 vertical tile mirroring
|
||||
if((bool)(voffset & 8) != mirror_y) tile_num += 16;
|
||||
}
|
||||
|
||||
tile_num &= 0x03ff;
|
||||
tile_num += tiledata_index;
|
||||
tile_num &= tile_mask;
|
||||
|
||||
if(bg_td_state[tile_num] == 1) {
|
||||
render_bg_tile(color_depth, tile_num);
|
||||
}
|
||||
|
||||
if(mirror_y) voffset ^= 7; //invert y tile pos
|
||||
tile_ptr = bg_td + (tile_num * 64) + ((voffset & 7) * 8);
|
||||
}
|
||||
|
||||
if(mirror_x) hoffset ^= 7; //invert x tile pos
|
||||
col = *(tile_ptr + (hoffset & 7));
|
||||
if(col) {
|
||||
if(is_direct_color_mode) {
|
||||
col = get_direct_color(pal_num, col);
|
||||
} else {
|
||||
col = get_palette(col + pal_index);
|
||||
}
|
||||
|
||||
if(!hires) {
|
||||
if(bg_enabled == true && !wt_main[x]) { setpixel_main(x); }
|
||||
if(bgsub_enabled == true && !wt_sub[x]) { setpixel_sub(x); }
|
||||
} else {
|
||||
int hx = x >> 1;
|
||||
if(x & 1) {
|
||||
if(bg_enabled == true && !wt_main[hx]) { setpixel_main(hx); }
|
||||
} else {
|
||||
if(bgsub_enabled == true && !wt_sub[hx]) { setpixel_sub(hx); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef setpixel_main
|
||||
#undef setpixel_sub
|
||||
|
||||
#endif //ifdef BPPU_CPP
|
||||
151
tools/bsnes/ppu/bppu/bppu_render_cache.cpp
Executable file
151
tools/bsnes/ppu/bppu/bppu_render_cache.cpp
Executable file
@@ -0,0 +1,151 @@
|
||||
#ifdef BPPU_CPP
|
||||
|
||||
#define render_bg_tile_line_2bpp(mask) \
|
||||
col = !!(d0 & mask) << 0; \
|
||||
col += !!(d1 & mask) << 1; \
|
||||
*dest++ = col
|
||||
|
||||
#define render_bg_tile_line_4bpp(mask) \
|
||||
col = !!(d0 & mask) << 0; \
|
||||
col += !!(d1 & mask) << 1; \
|
||||
col += !!(d2 & mask) << 2; \
|
||||
col += !!(d3 & mask) << 3; \
|
||||
*dest++ = col
|
||||
|
||||
#define render_bg_tile_line_8bpp(mask) \
|
||||
col = !!(d0 & mask) << 0; \
|
||||
col += !!(d1 & mask) << 1; \
|
||||
col += !!(d2 & mask) << 2; \
|
||||
col += !!(d3 & mask) << 3; \
|
||||
col += !!(d4 & mask) << 4; \
|
||||
col += !!(d5 & mask) << 5; \
|
||||
col += !!(d6 & mask) << 6; \
|
||||
col += !!(d7 & mask) << 7; \
|
||||
*dest++ = col
|
||||
|
||||
void bPPU::render_bg_tile(uint8 color_depth, uint16 tile_num) {
|
||||
uint8 mask, d0, d1, d2, d3, d4, d5, d6, d7, col;
|
||||
int x, y;
|
||||
uint32 pos;
|
||||
uint8 *dest;
|
||||
|
||||
switch(color_depth) {
|
||||
case COLORDEPTH_4: {
|
||||
dest = (uint8*)bg_tiledata[TILE_2BIT] + tile_num * 64;
|
||||
pos = tile_num * 16;
|
||||
y = 8;
|
||||
while(y--) {
|
||||
d0 = memory::vram[pos ];
|
||||
d1 = memory::vram[pos + 1];
|
||||
render_bg_tile_line_2bpp(0x80);
|
||||
render_bg_tile_line_2bpp(0x40);
|
||||
render_bg_tile_line_2bpp(0x20);
|
||||
render_bg_tile_line_2bpp(0x10);
|
||||
render_bg_tile_line_2bpp(0x08);
|
||||
render_bg_tile_line_2bpp(0x04);
|
||||
render_bg_tile_line_2bpp(0x02);
|
||||
render_bg_tile_line_2bpp(0x01);
|
||||
pos += 2;
|
||||
}
|
||||
bg_tiledata_state[TILE_2BIT][tile_num] = 0;
|
||||
} break;
|
||||
|
||||
case COLORDEPTH_16: {
|
||||
dest = (uint8*)bg_tiledata[TILE_4BIT] + tile_num * 64;
|
||||
pos = tile_num * 32;
|
||||
y = 8;
|
||||
while(y--) {
|
||||
d0 = memory::vram[pos ];
|
||||
d1 = memory::vram[pos + 1];
|
||||
d2 = memory::vram[pos + 16];
|
||||
d3 = memory::vram[pos + 17];
|
||||
render_bg_tile_line_4bpp(0x80);
|
||||
render_bg_tile_line_4bpp(0x40);
|
||||
render_bg_tile_line_4bpp(0x20);
|
||||
render_bg_tile_line_4bpp(0x10);
|
||||
render_bg_tile_line_4bpp(0x08);
|
||||
render_bg_tile_line_4bpp(0x04);
|
||||
render_bg_tile_line_4bpp(0x02);
|
||||
render_bg_tile_line_4bpp(0x01);
|
||||
pos += 2;
|
||||
}
|
||||
bg_tiledata_state[TILE_4BIT][tile_num] = 0;
|
||||
} break;
|
||||
|
||||
case COLORDEPTH_256: {
|
||||
dest = (uint8*)bg_tiledata[TILE_8BIT] + tile_num * 64;
|
||||
pos = tile_num * 64;
|
||||
y = 8;
|
||||
while(y--) {
|
||||
d0 = memory::vram[pos ];
|
||||
d1 = memory::vram[pos + 1];
|
||||
d2 = memory::vram[pos + 16];
|
||||
d3 = memory::vram[pos + 17];
|
||||
d4 = memory::vram[pos + 32];
|
||||
d5 = memory::vram[pos + 33];
|
||||
d6 = memory::vram[pos + 48];
|
||||
d7 = memory::vram[pos + 49];
|
||||
render_bg_tile_line_8bpp(0x80);
|
||||
render_bg_tile_line_8bpp(0x40);
|
||||
render_bg_tile_line_8bpp(0x20);
|
||||
render_bg_tile_line_8bpp(0x10);
|
||||
render_bg_tile_line_8bpp(0x08);
|
||||
render_bg_tile_line_8bpp(0x04);
|
||||
render_bg_tile_line_8bpp(0x02);
|
||||
render_bg_tile_line_8bpp(0x01);
|
||||
pos += 2;
|
||||
}
|
||||
bg_tiledata_state[TILE_8BIT][tile_num] = 0;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef render_bg_tile_line_2bpp
|
||||
#undef render_bg_tile_line_4bpp
|
||||
#undef render_bg_tile_line_8bpp
|
||||
|
||||
void bPPU::flush_pixel_cache() {
|
||||
uint16 main = get_palette(0);
|
||||
uint16 sub = (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6)
|
||||
? main
|
||||
: regs.color_rgb;
|
||||
|
||||
unsigned i = 255;
|
||||
do {
|
||||
pixel_cache[i].src_main = main;
|
||||
pixel_cache[i].src_sub = sub;
|
||||
pixel_cache[i].bg_main = BACK;
|
||||
pixel_cache[i].bg_sub = BACK;
|
||||
pixel_cache[i].ce_main = false;
|
||||
pixel_cache[i].ce_sub = false;
|
||||
pixel_cache[i].pri_main = 0;
|
||||
pixel_cache[i].pri_sub = 0;
|
||||
} while(i--);
|
||||
}
|
||||
|
||||
void bPPU::alloc_tiledata_cache() {
|
||||
bg_tiledata[TILE_2BIT] = new(zeromemory) uint8_t[262144];
|
||||
bg_tiledata[TILE_4BIT] = new(zeromemory) uint8_t[131072];
|
||||
bg_tiledata[TILE_8BIT] = new(zeromemory) uint8_t[ 65536];
|
||||
bg_tiledata_state[TILE_2BIT] = new(zeromemory) uint8_t[ 4096];
|
||||
bg_tiledata_state[TILE_4BIT] = new(zeromemory) uint8_t[ 2048];
|
||||
bg_tiledata_state[TILE_8BIT] = new(zeromemory) uint8_t[ 1024];
|
||||
}
|
||||
|
||||
//marks all tiledata cache entries as dirty
|
||||
void bPPU::flush_tiledata_cache() {
|
||||
for(unsigned i = 0; i < 4096; i++) bg_tiledata_state[TILE_2BIT][i] = 1;
|
||||
for(unsigned i = 0; i < 2048; i++) bg_tiledata_state[TILE_4BIT][i] = 1;
|
||||
for(unsigned i = 0; i < 1024; i++) bg_tiledata_state[TILE_8BIT][i] = 1;
|
||||
}
|
||||
|
||||
void bPPU::free_tiledata_cache() {
|
||||
delete[] bg_tiledata[TILE_2BIT];
|
||||
delete[] bg_tiledata[TILE_4BIT];
|
||||
delete[] bg_tiledata[TILE_8BIT];
|
||||
delete[] bg_tiledata_state[TILE_2BIT];
|
||||
delete[] bg_tiledata_state[TILE_4BIT];
|
||||
delete[] bg_tiledata_state[TILE_8BIT];
|
||||
}
|
||||
|
||||
#endif //ifdef BPPU_CPP
|
||||
140
tools/bsnes/ppu/bppu/bppu_render_line.cpp
Executable file
140
tools/bsnes/ppu/bppu/bppu_render_line.cpp
Executable file
@@ -0,0 +1,140 @@
|
||||
#ifdef BPPU_CPP
|
||||
|
||||
inline uint16 bPPU::get_palette(uint8 index) {
|
||||
unsigned addr = index << 1;
|
||||
return memory::cgram[addr] + (memory::cgram[addr + 1] << 8);
|
||||
}
|
||||
|
||||
inline uint16 bPPU::get_direct_color(uint8 p, uint8 t) {
|
||||
//p = 00000bgr <palette data>
|
||||
//t = BBGGGRRR <tilemap data>
|
||||
//r = 0BBb00GGGg0RRRr0 <return data>
|
||||
return ((t & 7) << 2) | ((p & 1) << 1) |
|
||||
(((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) |
|
||||
((t >> 6) << 13) | ((p >> 2) << 12);
|
||||
}
|
||||
|
||||
inline uint16 bPPU::get_pixel_normal(uint32 x) {
|
||||
_pixel *p = &pixel_cache[x];
|
||||
uint16 src_main, src_sub;
|
||||
uint8 bg_sub;
|
||||
src_main = p->src_main;
|
||||
|
||||
if(!regs.addsub_mode) {
|
||||
bg_sub = BACK;
|
||||
src_sub = regs.color_rgb;
|
||||
} else {
|
||||
bg_sub = p->bg_sub;
|
||||
src_sub = p->src_sub;
|
||||
}
|
||||
|
||||
if(!window[COL].main[x]) {
|
||||
if(!window[COL].sub[x]) {
|
||||
return 0x0000;
|
||||
}
|
||||
src_main = 0x0000;
|
||||
}
|
||||
|
||||
if(!p->ce_main && regs.color_enabled[p->bg_main] && window[COL].sub[x]) {
|
||||
bool halve = false;
|
||||
if(regs.color_halve && window[COL].main[x]) {
|
||||
if(regs.addsub_mode && bg_sub == BACK);
|
||||
else {
|
||||
halve = true;
|
||||
}
|
||||
}
|
||||
return addsub(src_main, src_sub, halve);
|
||||
}
|
||||
|
||||
return src_main;
|
||||
}
|
||||
|
||||
inline uint16 bPPU::get_pixel_swap(uint32 x) {
|
||||
_pixel *p = &pixel_cache[x];
|
||||
uint16 src_main, src_sub;
|
||||
uint8 bg_sub;
|
||||
src_main = p->src_sub;
|
||||
|
||||
if(!regs.addsub_mode) {
|
||||
bg_sub = BACK;
|
||||
src_sub = regs.color_rgb;
|
||||
} else {
|
||||
bg_sub = p->bg_main;
|
||||
src_sub = p->src_main;
|
||||
}
|
||||
|
||||
if(!window[COL].main[x]) {
|
||||
if(!window[COL].sub[x]) {
|
||||
return 0x0000;
|
||||
}
|
||||
src_main = 0x0000;
|
||||
}
|
||||
|
||||
if(!p->ce_sub && regs.color_enabled[p->bg_sub] && window[COL].sub[x]) {
|
||||
bool halve = false;
|
||||
if(regs.color_halve && window[COL].main[x]) {
|
||||
if(regs.addsub_mode && bg_sub == BACK);
|
||||
else {
|
||||
halve = true;
|
||||
}
|
||||
}
|
||||
return addsub(src_main, src_sub, halve);
|
||||
}
|
||||
|
||||
return src_main;
|
||||
}
|
||||
|
||||
inline void bPPU::render_line_output() {
|
||||
uint16 *ptr = (uint16*)output + (line * 1024) +
|
||||
((interlace() && ifield()) ? 512 : 0);
|
||||
uint16 *luma_b = light_table_b [regs.display_brightness];
|
||||
uint16 *luma_gr = light_table_gr[regs.display_brightness];
|
||||
uint16 curr, prev;
|
||||
|
||||
if(!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) {
|
||||
if(regs.display_brightness == 15) {
|
||||
for(int x = 0; x < 256; x++) {
|
||||
*ptr++ = get_pixel_normal(x);
|
||||
}
|
||||
} else {
|
||||
for(int x = 0; x < 256; x++) {
|
||||
curr = get_pixel_normal(x);
|
||||
*ptr++ = luma_b[curr >> 10] + luma_gr[curr & 0x3ff];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(regs.display_brightness == 15) {
|
||||
for(int x = 0, prev = 0; x < 256; x++) {
|
||||
curr = get_pixel_swap(x);
|
||||
*ptr++ = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1;
|
||||
prev = curr;
|
||||
|
||||
curr = get_pixel_normal(x);
|
||||
*ptr++ = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1;
|
||||
prev = curr;
|
||||
|
||||
}
|
||||
} else {
|
||||
for(int x = 0, prev = 0; x < 256; x++) {
|
||||
curr = get_pixel_swap(x);
|
||||
curr = luma_b[curr >> 10] + luma_gr[curr & 0x3ff];
|
||||
*ptr++ = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1;
|
||||
prev = curr;
|
||||
|
||||
curr = get_pixel_normal(x);
|
||||
curr = luma_b[curr >> 10] + luma_gr[curr & 0x3ff];
|
||||
*ptr++ = (prev + curr - ((prev ^ curr) & 0x0421)) >> 1;
|
||||
prev = curr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void bPPU::render_line_clear() {
|
||||
uint16 *ptr = (uint16*)output + (line * 1024) +
|
||||
((interlace() && ifield()) ? 512 : 0);
|
||||
uint16 width = (!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) ? 256 : 512;
|
||||
memset(ptr, 0, width * 2 * sizeof(uint16));
|
||||
}
|
||||
|
||||
#endif //ifdef BPPU_CPP
|
||||
148
tools/bsnes/ppu/bppu/bppu_render_mode7.cpp
Executable file
148
tools/bsnes/ppu/bppu/bppu_render_mode7.cpp
Executable file
@@ -0,0 +1,148 @@
|
||||
#ifdef BPPU_CPP
|
||||
|
||||
/*****
|
||||
* bsnes mode7 renderer
|
||||
*
|
||||
* base algorithm written by anomie
|
||||
* bsnes implementation written by byuu
|
||||
*
|
||||
* supports mode 7 + extbg + rotate + zoom +
|
||||
* direct color + scrolling + m7sel + windowing + mosaic
|
||||
* interlace and pseudo-hires support are automatic via main rendering routine
|
||||
*****/
|
||||
|
||||
//13-bit sign extend
|
||||
//--s---vvvvvvvvvv -> ssssssvvvvvvvvvv
|
||||
#define CLIP(x) ( ((x) & 0x2000) ? ( (x) | ~0x03ff) : ((x) & 0x03ff) )
|
||||
|
||||
void bPPU::render_line_mode7(uint8 bg, uint8 pri0_pos, uint8 pri1_pos) {
|
||||
if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) return;
|
||||
|
||||
//are layers disabled by user?
|
||||
if(render_enabled(bg, 0) == false) pri0_pos = 0;
|
||||
if(render_enabled(bg, 1) == false) pri1_pos = 0;
|
||||
|
||||
//nothing to render?
|
||||
if(!pri0_pos && !pri1_pos) return;
|
||||
|
||||
int32 px, py;
|
||||
int32 tx, ty, tile, palette;
|
||||
|
||||
int32 a = sclip<16>(regs.m7a);
|
||||
int32 b = sclip<16>(regs.m7b);
|
||||
int32 c = sclip<16>(regs.m7c);
|
||||
int32 d = sclip<16>(regs.m7d);
|
||||
|
||||
int32 cx = sclip<13>(regs.m7x);
|
||||
int32 cy = sclip<13>(regs.m7y);
|
||||
int32 hofs = sclip<13>(regs.m7_hofs);
|
||||
int32 vofs = sclip<13>(regs.m7_vofs);
|
||||
|
||||
int _pri, _x;
|
||||
bool _bg_enabled = regs.bg_enabled[bg];
|
||||
bool _bgsub_enabled = regs.bgsub_enabled[bg];
|
||||
|
||||
build_window_tables(bg);
|
||||
uint8 *wt_main = window[bg].main;
|
||||
uint8 *wt_sub = window[bg].sub;
|
||||
|
||||
int32 y = (regs.mode7_vflip == false ? line : 255 - line);
|
||||
|
||||
uint16 *mtable_x, *mtable_y;
|
||||
if(bg == BG1) {
|
||||
mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0];
|
||||
mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0];
|
||||
} else { //bg == BG2
|
||||
//Mode7 EXTBG BG2 uses BG1 mosaic enable to control vertical mosaic,
|
||||
//and BG2 mosaic enable to control horizontal mosaic...
|
||||
mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG2] == true) ? regs.mosaic_size : 0];
|
||||
mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0];
|
||||
}
|
||||
|
||||
int32 psx = ((a * CLIP(hofs - cx)) & ~63) + ((b * CLIP(vofs - cy)) & ~63) + ((b * mtable_y[y]) & ~63) + (cx << 8);
|
||||
int32 psy = ((c * CLIP(hofs - cx)) & ~63) + ((d * CLIP(vofs - cy)) & ~63) + ((d * mtable_y[y]) & ~63) + (cy << 8);
|
||||
for(int32 x = 0; x < 256; x++) {
|
||||
px = psx + (a * mtable_x[x]);
|
||||
py = psy + (c * mtable_x[x]);
|
||||
|
||||
//mask floating-point bits (low 8 bits)
|
||||
px >>= 8;
|
||||
py >>= 8;
|
||||
|
||||
switch(regs.mode7_repeat) {
|
||||
case 0: //screen repetition outside of screen area
|
||||
case 1: { //same as case 0
|
||||
px &= 1023;
|
||||
py &= 1023;
|
||||
tx = ((px >> 3) & 127);
|
||||
ty = ((py >> 3) & 127);
|
||||
tile = memory::vram[(ty * 128 + tx) << 1];
|
||||
palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
|
||||
} break;
|
||||
case 2: { //palette color 0 outside of screen area
|
||||
if(px < 0 || px > 1023 || py < 0 || py > 1023) {
|
||||
palette = 0;
|
||||
} else {
|
||||
px &= 1023;
|
||||
py &= 1023;
|
||||
tx = ((px >> 3) & 127);
|
||||
ty = ((py >> 3) & 127);
|
||||
tile = memory::vram[(ty * 128 + tx) << 1];
|
||||
palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
|
||||
}
|
||||
} break;
|
||||
case 3: { //character 0 repetition outside of screen area
|
||||
if(px < 0 || px > 1023 || py < 0 || py > 1023) {
|
||||
tile = 0;
|
||||
} else {
|
||||
px &= 1023;
|
||||
py &= 1023;
|
||||
tx = ((px >> 3) & 127);
|
||||
ty = ((py >> 3) & 127);
|
||||
tile = memory::vram[(ty * 128 + tx) << 1];
|
||||
}
|
||||
palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1];
|
||||
} break;
|
||||
}
|
||||
|
||||
if(bg == BG1) {
|
||||
_pri = pri0_pos;
|
||||
} else {
|
||||
_pri = (palette >> 7) ? pri1_pos : pri0_pos;
|
||||
palette &= 0x7f;
|
||||
}
|
||||
|
||||
if(!palette) continue;
|
||||
|
||||
_x = (regs.mode7_hflip == false) ? (x) : (255 - x);
|
||||
|
||||
uint32 col;
|
||||
if(regs.direct_color == true && bg == BG1) {
|
||||
//direct color mode does not apply to bg2, as it is only 128 colors...
|
||||
col = get_direct_color(0, palette);
|
||||
} else {
|
||||
col = get_palette(palette);
|
||||
}
|
||||
|
||||
if(regs.bg_enabled[bg] == true && !wt_main[_x]) {
|
||||
if(pixel_cache[_x].pri_main < _pri) {
|
||||
pixel_cache[_x].pri_main = _pri;
|
||||
pixel_cache[_x].bg_main = bg;
|
||||
pixel_cache[_x].src_main = col;
|
||||
pixel_cache[_x].ce_main = false;
|
||||
}
|
||||
}
|
||||
if(regs.bgsub_enabled[bg] == true && !wt_sub[_x]) {
|
||||
if(pixel_cache[_x].pri_sub < _pri) {
|
||||
pixel_cache[_x].pri_sub = _pri;
|
||||
pixel_cache[_x].bg_sub = bg;
|
||||
pixel_cache[_x].src_sub = col;
|
||||
pixel_cache[_x].ce_sub = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef CLIP
|
||||
|
||||
#endif //ifdef BPPU_CPP
|
||||
224
tools/bsnes/ppu/bppu/bppu_render_oam.cpp
Executable file
224
tools/bsnes/ppu/bppu/bppu_render_oam.cpp
Executable file
@@ -0,0 +1,224 @@
|
||||
#ifdef BPPU_CPP
|
||||
|
||||
void bPPU::build_sprite_list() {
|
||||
uint8 *tableA = memory::oam.handle();
|
||||
uint8 *tableB = memory::oam.handle() + 512;
|
||||
|
||||
for(unsigned i = 0; i < 128; i++) {
|
||||
unsigned x = !!(*tableB & (1 << ((i & 3) << 1))); //0x01, 0x04, 0x10, 0x40
|
||||
bool size = !!(*tableB & (2 << ((i & 3) << 1))); //0x02, 0x08, 0x20, 0x80
|
||||
|
||||
switch(cache.oam_basesize) {
|
||||
case 0: sprite_list[i].width = (!size) ? 8 : 16;
|
||||
sprite_list[i].height = (!size) ? 8 : 16;
|
||||
break;
|
||||
case 1: sprite_list[i].width = (!size) ? 8 : 32;
|
||||
sprite_list[i].height = (!size) ? 8 : 32;
|
||||
break;
|
||||
case 2: sprite_list[i].width = (!size) ? 8 : 64;
|
||||
sprite_list[i].height = (!size) ? 8 : 64;
|
||||
break;
|
||||
case 3: sprite_list[i].width = (!size) ? 16 : 32;
|
||||
sprite_list[i].height = (!size) ? 16 : 32;
|
||||
break;
|
||||
case 4: sprite_list[i].width = (!size) ? 16 : 64;
|
||||
sprite_list[i].height = (!size) ? 16 : 64;
|
||||
break;
|
||||
case 5: sprite_list[i].width = (!size) ? 32 : 64;
|
||||
sprite_list[i].height = (!size) ? 32 : 64;
|
||||
break;
|
||||
case 6: sprite_list[i].width = (!size) ? 16 : 32;
|
||||
sprite_list[i].height = (!size) ? 32 : 64;
|
||||
if(regs.oam_interlace && !size) sprite_list[i].height = 16;
|
||||
//32x64 height is not affected by oam_interlace setting
|
||||
break;
|
||||
case 7: sprite_list[i].width = (!size) ? 16 : 32;
|
||||
sprite_list[i].height = (!size) ? 32 : 32;
|
||||
if(regs.oam_interlace && !size) sprite_list[i].height = 16;
|
||||
break;
|
||||
}
|
||||
|
||||
sprite_list[i].x = (x << 8) + tableA[0];
|
||||
sprite_list[i].y = (tableA[1] + 1) & 0xff;
|
||||
sprite_list[i].character = tableA[2];
|
||||
sprite_list[i].vflip = !!(tableA[3] & 0x80);
|
||||
sprite_list[i].hflip = !!(tableA[3] & 0x40);
|
||||
sprite_list[i].priority = (tableA[3] >> 4) & 3;
|
||||
sprite_list[i].palette = (tableA[3] >> 1) & 7;
|
||||
sprite_list[i].use_nameselect = tableA[3] & 1;
|
||||
|
||||
tableA += 4;
|
||||
if((i & 3) == 3) tableB++;
|
||||
}
|
||||
}
|
||||
|
||||
bool bPPU::is_sprite_on_scanline() {
|
||||
//if sprite is entirely offscreen and doesn't wrap around to the left side of the screen,
|
||||
//then it is not counted. this *should* be 256, and not 255, even though dot 256 is offscreen.
|
||||
if(spr->x > 256 && (spr->x + spr->width - 1) < 512) return false;
|
||||
|
||||
int spr_height = (regs.oam_interlace == false) ? (spr->height) : (spr->height >> 1);
|
||||
if(line >= spr->y && line < (spr->y + spr_height)) return true;
|
||||
if((spr->y + spr_height) >= 256 && line < ((spr->y + spr_height) & 255)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void bPPU::load_oam_tiles() {
|
||||
uint16 tile_width = spr->width >> 3;
|
||||
int x = spr->x;
|
||||
int y = (line - spr->y) & 0xff;
|
||||
if(regs.oam_interlace == true) {
|
||||
y <<= 1;
|
||||
}
|
||||
|
||||
if(spr->vflip == true) {
|
||||
if(spr->width == spr->height) {
|
||||
y = (spr->height - 1) - y;
|
||||
} else {
|
||||
y = (y < spr->width) ? ((spr->width - 1) - y) : (spr->width + ((spr->width - 1) - (y - spr->width)));
|
||||
}
|
||||
}
|
||||
|
||||
if(regs.oam_interlace == true) {
|
||||
y = (spr->vflip == false) ? (y + ifield()) : (y - ifield());
|
||||
}
|
||||
|
||||
x &= 511;
|
||||
y &= 255;
|
||||
|
||||
uint16 tdaddr = cache.oam_tdaddr;
|
||||
uint16 chrx = (spr->character ) & 15;
|
||||
uint16 chry = (spr->character >> 4) & 15;
|
||||
if(spr->use_nameselect == true) {
|
||||
tdaddr += (256 * 32) + (cache.oam_nameselect << 13);
|
||||
}
|
||||
chry += (y >> 3);
|
||||
chry &= 15;
|
||||
chry <<= 4;
|
||||
|
||||
for(unsigned tx = 0; tx < tile_width; tx++) {
|
||||
unsigned sx = (x + (tx << 3)) & 511;
|
||||
//ignore sprites that are offscreen, x==256 is a special case that loads all tiles in OBJ
|
||||
if(x != 256 && sx >= 256 && (sx + 7) < 512) continue;
|
||||
|
||||
if(regs.oam_tilecount++ > 34) break;
|
||||
unsigned n = regs.oam_tilecount - 1;
|
||||
oam_tilelist[n].x = sx;
|
||||
oam_tilelist[n].y = y;
|
||||
oam_tilelist[n].pri = spr->priority;
|
||||
oam_tilelist[n].pal = 128 + (spr->palette << 4);
|
||||
oam_tilelist[n].hflip = spr->hflip;
|
||||
|
||||
unsigned mx = (spr->hflip == false) ? tx : ((tile_width - 1) - tx);
|
||||
unsigned pos = tdaddr + ((chry + ((chrx + mx) & 15)) << 5);
|
||||
oam_tilelist[n].tile = (pos >> 5) & 0x07ff;
|
||||
}
|
||||
}
|
||||
|
||||
void bPPU::render_oam_tile(int tile_num) {
|
||||
oam_tileitem *t = &oam_tilelist[tile_num];
|
||||
uint8 *oam_td = (uint8*)bg_tiledata[COLORDEPTH_16];
|
||||
uint8 *oam_td_state = (uint8*)bg_tiledata_state[COLORDEPTH_16];
|
||||
|
||||
if(oam_td_state[t->tile] == 1) {
|
||||
render_bg_tile(COLORDEPTH_16, t->tile);
|
||||
}
|
||||
|
||||
unsigned sx = t->x;
|
||||
uint8 *tile_ptr = (uint8*)oam_td + (t->tile << 6) + ((t->y & 7) << 3);
|
||||
for(unsigned x = 0; x < 8; x++) {
|
||||
sx &= 511;
|
||||
if(sx < 256) {
|
||||
unsigned col = *(tile_ptr + ((t->hflip == false) ? x : (7 - x)));
|
||||
if(col) {
|
||||
col += t->pal;
|
||||
oam_line_pal[sx] = col;
|
||||
oam_line_pri[sx] = t->pri;
|
||||
}
|
||||
}
|
||||
sx++;
|
||||
}
|
||||
}
|
||||
|
||||
void bPPU::render_line_oam_rto() {
|
||||
build_sprite_list();
|
||||
|
||||
regs.oam_itemcount = 0;
|
||||
regs.oam_tilecount = 0;
|
||||
memset(oam_line_pri, OAM_PRI_NONE, 256);
|
||||
memset(oam_itemlist, 0xff, 32);
|
||||
for(int s = 0; s < 34; s++) oam_tilelist[s].tile = 0xffff;
|
||||
|
||||
for(int s = 0; s < 128; s++) {
|
||||
spr = &sprite_list[(s + regs.oam_firstsprite) & 127];
|
||||
if(is_sprite_on_scanline() == false) continue;
|
||||
if(regs.oam_itemcount++ > 32) break;
|
||||
oam_itemlist[regs.oam_itemcount - 1] = (s + regs.oam_firstsprite) & 127;
|
||||
}
|
||||
|
||||
for(int s = 31; s >= 0; s--) {
|
||||
if(oam_itemlist[s] == 0xff) continue;
|
||||
spr = &sprite_list[oam_itemlist[s]];
|
||||
load_oam_tiles();
|
||||
}
|
||||
|
||||
regs.time_over |= (regs.oam_tilecount > 34);
|
||||
regs.range_over |= (regs.oam_itemcount > 32);
|
||||
}
|
||||
|
||||
void bPPU::render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) {
|
||||
if(regs.bg_enabled[OAM] == false && regs.bgsub_enabled[OAM] == false) return;
|
||||
|
||||
//are layers disabled by user?
|
||||
if(render_enabled(OAM, 0) == false) pri0_pos = 0;
|
||||
if(render_enabled(OAM, 1) == false) pri1_pos = 0;
|
||||
if(render_enabled(OAM, 2) == false) pri2_pos = 0;
|
||||
if(render_enabled(OAM, 3) == false) pri3_pos = 0;
|
||||
//nothing to render?
|
||||
if(!pri0_pos && !pri1_pos && !pri2_pos && !pri3_pos) return;
|
||||
|
||||
for(int s = 0; s < 34; s++) {
|
||||
if(oam_tilelist[s].tile == 0xffff) continue;
|
||||
render_oam_tile(s);
|
||||
}
|
||||
|
||||
render_line_oam_lores(pri0_pos, pri1_pos, pri2_pos, pri3_pos);
|
||||
}
|
||||
|
||||
#define setpixel_main(x) \
|
||||
if(pixel_cache[x].pri_main < pri) { \
|
||||
pixel_cache[x].pri_main = pri; \
|
||||
pixel_cache[x].bg_main = OAM; \
|
||||
pixel_cache[x].src_main = get_palette(oam_line_pal[x]); \
|
||||
pixel_cache[x].ce_main = (oam_line_pal[x] < 192); \
|
||||
}
|
||||
#define setpixel_sub(x) \
|
||||
if(pixel_cache[x].pri_sub < pri) { \
|
||||
pixel_cache[x].pri_sub = pri; \
|
||||
pixel_cache[x].bg_sub = OAM; \
|
||||
pixel_cache[x].src_sub = get_palette(oam_line_pal[x]); \
|
||||
pixel_cache[x].ce_sub = (oam_line_pal[x] < 192); \
|
||||
}
|
||||
|
||||
void bPPU::render_line_oam_lores(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) {
|
||||
bool bg_enabled = regs.bg_enabled[OAM];
|
||||
bool bgsub_enabled = regs.bgsub_enabled[OAM];
|
||||
|
||||
build_window_tables(OAM);
|
||||
uint8 *wt_main = window[OAM].main;
|
||||
uint8 *wt_sub = window[OAM].sub;
|
||||
|
||||
int pri_tbl[4] = { pri0_pos, pri1_pos, pri2_pos, pri3_pos };
|
||||
for(int x = 0; x < 256; x++) {
|
||||
if(oam_line_pri[x] == OAM_PRI_NONE) continue;
|
||||
|
||||
int pri = pri_tbl[oam_line_pri[x]];
|
||||
if(bg_enabled == true && !wt_main[x]) { setpixel_main(x); }
|
||||
if(bgsub_enabled == true && !wt_sub[x]) { setpixel_sub(x); }
|
||||
}
|
||||
}
|
||||
|
||||
#undef setpixel_main
|
||||
#undef setpixel_sub
|
||||
|
||||
#endif //ifdef BPPU_CPP
|
||||
99
tools/bsnes/ppu/bppu/bppu_render_windows.cpp
Executable file
99
tools/bsnes/ppu/bppu/bppu_render_windows.cpp
Executable file
@@ -0,0 +1,99 @@
|
||||
#ifdef BPPU_CPP
|
||||
|
||||
void bPPU::build_window_table(uint8 bg, bool mainscreen) {
|
||||
uint8 set = 1, clr = 0;
|
||||
uint8 *wtbl = (mainscreen == true) ? window[bg].main : window[bg].sub;
|
||||
|
||||
if(bg != COL) {
|
||||
if(mainscreen == true && regs.window_enabled[bg] == false) {
|
||||
memset(wtbl, 0, 256);
|
||||
return;
|
||||
}
|
||||
if(mainscreen == false && regs.sub_window_enabled[bg] == false) {
|
||||
memset(wtbl, 0, 256);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
switch((mainscreen == true) ? regs.color_mask : regs.colorsub_mask) {
|
||||
case 0: { //always
|
||||
memset(wtbl, 1, 256);
|
||||
} return;
|
||||
|
||||
case 3: { //never
|
||||
memset(wtbl, 0, 256);
|
||||
} return;
|
||||
|
||||
case 1: { //inside window only
|
||||
set = 1;
|
||||
clr = 0;
|
||||
} break;
|
||||
|
||||
case 2: { //outside window only
|
||||
set = 0;
|
||||
clr = 1;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
uint16 window1_left = regs.window1_left;
|
||||
uint16 window1_right = regs.window1_right;
|
||||
uint16 window2_left = regs.window2_left;
|
||||
uint16 window2_right = regs.window2_right;
|
||||
|
||||
if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == false) {
|
||||
memset(wtbl, clr, 256);
|
||||
return;
|
||||
}
|
||||
|
||||
if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == false) {
|
||||
if(regs.window1_invert[bg] == true)swap(set, clr);
|
||||
for(int x = 0; x < 256; x++) {
|
||||
wtbl[x] = (x >= window1_left && x <= window1_right) ? set : clr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == true) {
|
||||
if(regs.window2_invert[bg] == true)swap(set, clr);
|
||||
for(int x = 0; x < 256; x++) {
|
||||
wtbl[x] = (x >= window2_left && x <= window2_right) ? set : clr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == true)
|
||||
int w1_mask, w2_mask; //1 = masked, 0 = not masked
|
||||
|
||||
for(int x = 0; x < 256; x++) {
|
||||
w1_mask = (x >= window1_left && x <= window1_right);
|
||||
if(regs.window1_invert[bg] == true)w1_mask = !w1_mask;
|
||||
|
||||
w2_mask = (x >= window2_left && x <= window2_right);
|
||||
if(regs.window2_invert[bg] == true)w2_mask = !w2_mask;
|
||||
|
||||
switch(regs.window_mask[bg]) {
|
||||
case 0: { //WINDOWMASK_OR:
|
||||
wtbl[x] = ((w1_mask | w2_mask) == 1) ? set : clr;
|
||||
} break;
|
||||
|
||||
case 1: { //WINDOWMASK_AND:
|
||||
wtbl[x] = ((w1_mask & w2_mask) == 1) ? set : clr;
|
||||
} break;
|
||||
|
||||
case 2: { //WINDOWMASK_XOR:
|
||||
wtbl[x] = ((w1_mask ^ w2_mask) == 1) ? set : clr;
|
||||
} break;
|
||||
|
||||
case 3: { //WINDOWMASK_XNOR:
|
||||
wtbl[x] = ((w1_mask ^ w2_mask) == 0) ? set : clr;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bPPU::build_window_tables(uint8 bg) {
|
||||
build_window_table(bg, true);
|
||||
build_window_table(bg, false);
|
||||
}
|
||||
|
||||
#endif //ifdef BPPU_CPP
|
||||
50
tools/bsnes/ppu/counter.cpp
Executable file
50
tools/bsnes/ppu/counter.cpp
Executable file
@@ -0,0 +1,50 @@
|
||||
#ifdef PPU_CPP
|
||||
|
||||
//wrappers to allow PPUcounter::tick()/tock() to be inlined
|
||||
bool PPUcounter::region() const { return snes.region() == SNES::NTSC ? 0 : 1; }
|
||||
bool PPUcounter::interlace() const { return ppu.interlace(); }
|
||||
void PPUcounter::scanline() { cpu.scanline(); }
|
||||
|
||||
//one PPU dot = 4 CPU clocks
|
||||
//
|
||||
//PPU dots 323 and 327 are 6 CPU clocks long.
|
||||
//this does not apply to NTSC non-interlace scanline 240 on odd fields. this is
|
||||
//because the PPU skips one dot to alter the color burst phase of the video signal.
|
||||
//
|
||||
//dot 323 range = { 1292, 1294, 1296 }
|
||||
//dot 327 range = { 1310, 1312, 1314 }
|
||||
|
||||
uint16 PPUcounter::hdot() const {
|
||||
if(region() == 0 && interlace() == false && status.vcounter == 240 && status.field == 1) {
|
||||
return (status.hcounter >> 2);
|
||||
} else {
|
||||
return (status.hcounter - ((status.hcounter > 1292) << 1) - ((status.hcounter > 1310) << 1)) >> 2;
|
||||
}
|
||||
}
|
||||
|
||||
uint16 PPUcounter::lineclocks() const {
|
||||
if(region() == 0 && interlace() == false && vcounter() == 240 && status.field == 1) return 1360;
|
||||
return 1364;
|
||||
}
|
||||
|
||||
uint16 PPUcounter::ilineclocks() const {
|
||||
if(region() == 0 && interlace() == false && ivcounter() == 240 && status.field == 1) return 1360;
|
||||
return 1364;
|
||||
}
|
||||
|
||||
void PPUcounter::reset() {
|
||||
status.field = 0;
|
||||
status.vcounter = 0;
|
||||
status.hcounter = 0;
|
||||
|
||||
history.index = 0;
|
||||
history.ppudiff = 0;
|
||||
|
||||
for(unsigned i = 0; i < 2048; i++) {
|
||||
history.field [i] = 0;
|
||||
history.vcounter[i] = 0;
|
||||
history.hcounter[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
73
tools/bsnes/ppu/counter.hpp
Executable file
73
tools/bsnes/ppu/counter.hpp
Executable file
@@ -0,0 +1,73 @@
|
||||
class PPUcounter {
|
||||
public:
|
||||
alwaysinline void tick() {
|
||||
history.ppudiff += 2;
|
||||
status.hcounter += 2;
|
||||
|
||||
if(status.hcounter >= 1360 && status.hcounter == lineclocks()) {
|
||||
status.hcounter = 0;
|
||||
status.vcounter++;
|
||||
if((region() == 0 && interlace() == false && status.vcounter == 262)
|
||||
|| (region() == 0 && interlace() == true && status.vcounter == 263)
|
||||
|| (region() == 0 && interlace() == true && status.vcounter == 262 && status.field == 1)
|
||||
|| (region() == 1 && interlace() == false && status.vcounter == 312)
|
||||
|| (region() == 1 && interlace() == true && status.vcounter == 313)
|
||||
|| (region() == 1 && interlace() == true && status.vcounter == 312 && status.field == 1)
|
||||
) {
|
||||
status.vcounter = 0;
|
||||
status.field = !status.field;
|
||||
}
|
||||
|
||||
scanline();
|
||||
}
|
||||
|
||||
history.index = (history.index + 1) & 2047;
|
||||
history.field [history.index] = status.field;
|
||||
history.vcounter[history.index] = status.vcounter;
|
||||
history.hcounter[history.index] = status.hcounter;
|
||||
}
|
||||
|
||||
alwaysinline void tock(unsigned clocks) {
|
||||
history.ppudiff -= clocks;
|
||||
}
|
||||
|
||||
//timing information relative to S-CPU
|
||||
alwaysinline bool field () const { return status.field; }
|
||||
alwaysinline uint16 vcounter() const { return status.vcounter; }
|
||||
alwaysinline uint16 hcounter() const { return status.hcounter; }
|
||||
uint16 hdot() const;
|
||||
uint16 lineclocks() const;
|
||||
|
||||
//timing history information relative to S-CPU
|
||||
alwaysinline bool field (unsigned offset) const { return history.field [(history.index - (offset >> 1)) & 2047]; }
|
||||
alwaysinline uint16 vcounter(unsigned offset) const { return history.vcounter[(history.index - (offset >> 1)) & 2047]; }
|
||||
alwaysinline uint16 hcounter(unsigned offset) const { return history.hcounter[(history.index - (offset >> 1)) & 2047]; }
|
||||
|
||||
//timing information relative to S-PPU
|
||||
alwaysinline bool ifield() const { return history.field [(history.index - (history.ppudiff >> 1)) & 2047]; }
|
||||
alwaysinline uint16 ivcounter() const { return history.vcounter[(history.index - (history.ppudiff >> 1)) & 2047]; }
|
||||
alwaysinline uint16 ihcounter() const { return history.hcounter[(history.index - (history.ppudiff >> 1)) & 2047]; }
|
||||
uint16 ilineclocks() const;
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
bool region() const;
|
||||
bool interlace() const;
|
||||
void scanline();
|
||||
|
||||
struct {
|
||||
bool field;
|
||||
uint16 vcounter;
|
||||
uint16 hcounter;
|
||||
} status;
|
||||
|
||||
struct {
|
||||
bool field[2048];
|
||||
uint16 vcounter[2048];
|
||||
uint16 hcounter[2048];
|
||||
|
||||
unsigned index;
|
||||
signed ppudiff;
|
||||
} history;
|
||||
};
|
||||
47
tools/bsnes/ppu/ppu.cpp
Executable file
47
tools/bsnes/ppu/ppu.cpp
Executable file
@@ -0,0 +1,47 @@
|
||||
#include <../base.hpp>
|
||||
#define PPU_CPP
|
||||
|
||||
#include "counter.cpp"
|
||||
|
||||
void PPU::enable_renderer(bool r) { status.render_output = r; }
|
||||
bool PPU::renderer_enabled() { return status.render_output; }
|
||||
|
||||
void PPU::frame() {
|
||||
status.frame_executed = true;
|
||||
|
||||
static int32 fr = 0, fe = 0;
|
||||
static time_t prev, curr;
|
||||
fe++;
|
||||
if(status.render_output)fr++;
|
||||
|
||||
time(&curr);
|
||||
if(curr != prev) {
|
||||
status.frames_updated = true;
|
||||
status.frames_rendered = fr;
|
||||
status.frames_executed = fe;
|
||||
fr = fe = 0;
|
||||
}
|
||||
prev = curr;
|
||||
}
|
||||
|
||||
void PPU::power() {
|
||||
ppu1_version = snes.config.ppu1.version;
|
||||
ppu2_version = snes.config.ppu2.version;
|
||||
}
|
||||
|
||||
void PPU::reset() {
|
||||
memset(output, 0, 512 * 480 * sizeof(uint16));
|
||||
}
|
||||
|
||||
PPU::PPU() {
|
||||
output = new(zeromemory) uint16[512 * 480];
|
||||
|
||||
status.render_output = true;
|
||||
status.frames_updated = false;
|
||||
status.frames_rendered = 0;
|
||||
status.frames_executed = 0;
|
||||
}
|
||||
|
||||
PPU::~PPU() {
|
||||
delete[] output;
|
||||
}
|
||||
42
tools/bsnes/ppu/ppu.hpp
Executable file
42
tools/bsnes/ppu/ppu.hpp
Executable file
@@ -0,0 +1,42 @@
|
||||
#include "counter.hpp"
|
||||
|
||||
class PPU : public PPUcounter, public MMIO {
|
||||
public:
|
||||
virtual void enter() = 0;
|
||||
|
||||
uint16 *output;
|
||||
|
||||
struct {
|
||||
bool render_output;
|
||||
|
||||
bool frame_executed;
|
||||
bool frames_updated;
|
||||
unsigned frames_rendered;
|
||||
unsigned frames_executed;
|
||||
} status;
|
||||
|
||||
//PPU1 version number
|
||||
//* 1 is known
|
||||
//* reported by $213e
|
||||
uint8 ppu1_version;
|
||||
|
||||
//PPU2 version number
|
||||
//* 1 and 3 are known
|
||||
//* reported by $213f
|
||||
uint8 ppu2_version;
|
||||
|
||||
virtual bool interlace() const = 0;
|
||||
virtual bool overscan() const = 0;
|
||||
virtual bool hires() const = 0;
|
||||
|
||||
virtual void latch_counters() = 0;
|
||||
|
||||
virtual void frame();
|
||||
virtual void power();
|
||||
virtual void reset();
|
||||
virtual void enable_renderer(bool r);
|
||||
virtual bool renderer_enabled();
|
||||
|
||||
PPU();
|
||||
virtual ~PPU();
|
||||
};
|
||||
Reference in New Issue
Block a user