Update to v068r07 release.

(there was no r06 release posted to the WIP thread)

byuu says:

New PPU renderer is coming along. Lots of new ideas, especially with
regards to the way the background renders in tiles rather than in
pixels. That skips the need for tile caching and compares, and it even
gets scrolling right.

The sprite item+tile lists are computing, but they are not rendering
yet.

It's a good deal faster for now, obviously, because 90% of the PPU
features are missing.
This commit is contained in:
Tim Allen 2010-09-01 23:20:05 +10:00
parent 2bafd18a5a
commit 0bf6c40d1f
18 changed files with 1280 additions and 36 deletions

View File

@ -0,0 +1,107 @@
#ifdef PPU_CPP
void PPU::Background::render() {
if(regs.mode == Mode::Inactive) return;
if(regs.mode == Mode::Mode7) return;
if(regs.main_enable == false && regs.sub_enable == false) return;
const unsigned opt_valid_bit = (id == ID::BG1 ? 0x2000 : id == ID::BG2 ? 0x4000 : 0x0000);
const unsigned bgpal_index = (self.regs.bgmode == 0 ? id << 5 : 0);
const unsigned pal_size = 2 << regs.mode;
const unsigned tile_mask = 0x0fff >> regs.mode;
const unsigned tiledata_index = regs.tiledata_addr >> (4 + regs.mode);
const bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6);
const signed width = !hires ? 256 : 512;
const unsigned tile_height = regs.tile_size ? 4 : 3;
const unsigned tile_width = hires ? 4 : tile_height;
unsigned mask_x = (tile_height == 4 ? width << 1 : width);
unsigned mask_y = mask_x;
if(regs.screen_size & 1) mask_x <<= 1;
if(regs.screen_size & 2) mask_y <<= 1;
mask_x--;
mask_y--;
unsigned scy = (regs.screen_size & 2 ? 32 << 5 : 0);
unsigned scx = (regs.screen_size & 1 ? 32 << 5 : 0);
if(regs.screen_size == 3) scy <<= 1;
unsigned y = self.vcounter();
unsigned hscroll = regs.hoffset;
unsigned vscroll = regs.voffset;
if(hires) {
hscroll <<= 1;
if(self.regs.interlace) y = (y << 1) + self.field();
}
unsigned hval, vval;
unsigned tile_pri, tile_num;
unsigned pal_index, pal_num;
unsigned hoffset, voffset, opt_x, col;
bool mirror_x, mirror_y;
const bool is_opt_mode = (self.regs.bgmode == 2 || self.regs.bgmode == 4 || self.regs.bgmode == 6);
const bool is_direct_color_mode = (self.screen.regs.direct_color == true && id == ID::BG1 && (self.regs.bgmode == 3 || self.regs.bgmode == 4));
signed x = 0 - (hscroll & 7);
while(x < width) {
hoffset = x + hscroll;
voffset = y + vscroll;
hoffset &= mask_x;
voffset &= mask_y;
unsigned tile_x = hoffset >> tile_width;
unsigned tile_y = voffset >> tile_height;
unsigned tile_pos = ((tile_y & 0x1f) << 5) + (tile_x & 0x1f);
if(tile_y & 0x20) tile_pos += scy;
if(tile_x & 0x20) tile_pos += scx;
const unsigned tiledata_addr = regs.screen_addr + (tile_pos << 1);
tile_num = (memory::vram[tiledata_addr + 0] << 0) + (memory::vram[tiledata_addr + 1] << 8);
mirror_y = tile_num & 0x8000;
mirror_x = tile_num & 0x4000;
tile_pri = tile_num & 0x2000 ? regs.priority1 : regs.priority0;
pal_num = (tile_num >> 10) & 7;
pal_index = bgpal_index + (pal_num << pal_size);
if(tile_width == 4) {
if((bool)(hoffset & 8) != mirror_x) tile_num++;
}
if(tile_height == 4) {
if((bool)(voffset & 8) != mirror_y) tile_num += 16;
}
tile_num &= 0x03ff;
tile_num += tiledata_index;
tile_num &= tile_mask;
if(mirror_y) voffset ^= 7;
signed step = mirror_x ? -1 : +1;
unsigned plot_x = x + (mirror_x ? 7 : 0);
uint8 *tiledata = self.cache.tile(regs.mode, tile_num);
tiledata += ((voffset & 7) * 8);
for(unsigned n = 0; n < 8; n++) {
unsigned col = *tiledata++;
if(col) {
unsigned color = self.screen.get_palette(pal_index + col);
self.screen.output.plot_main(plot_x, color, tile_pri);
}
plot_x += step;
}
x += 8;
}
}
PPU::Background::Background(PPU &self, unsigned id) : self(self), id(id) {
}
#endif

View File

@ -0,0 +1,33 @@
class Background {
struct ID { enum { BG1, BG2, BG3, BG4 }; };
struct Mode { enum { BPP2, BPP4, BPP8, Mode7, Inactive }; };
struct ScreenSize { enum { Size32x32, Size32x64, Size64x32, Size64x64 }; };
struct TileSize { enum { Size8x8, Size16x16 }; };
struct Regs {
unsigned mode;
unsigned priority0;
unsigned priority1;
bool tile_size;
unsigned mosaic;
unsigned screen_addr;
unsigned screen_size;
unsigned tiledata_addr;
unsigned hoffset;
unsigned voffset;
bool main_enable;
bool sub_enable;
} regs;
void render();
const unsigned id;
Background(PPU &self, unsigned id);
PPU &self;
friend class PPU;
};

123
bsnes/snes/alt/ppu-new/cache/cache.cpp vendored Executable file
View File

@ -0,0 +1,123 @@
#ifdef PPU_CPP
uint8* PPU::Cache::tile_2bpp(unsigned tile) {
if(tilevalid[0][tile] == 0) {
tilevalid[0][tile] = 1;
uint8 *output = (uint8*)tiledata[0] + (tile << 6);
unsigned offset = tile << 4;
unsigned y = 8;
unsigned color, d0, d1;
while(y--) {
d0 = memory::vram[offset + 0];
d1 = memory::vram[offset + 1];
#define render_line(mask) \
color = !!(d0 & mask) << 0; \
color |= !!(d1 & mask) << 1; \
*output++ = color
render_line(0x80);
render_line(0x40);
render_line(0x20);
render_line(0x10);
render_line(0x08);
render_line(0x04);
render_line(0x02);
render_line(0x01);
#undef render_line
offset += 2;
}
}
return tiledata[0] + (tile << 6);
}
uint8* PPU::Cache::tile_4bpp(unsigned tile) {
if(tilevalid[1][tile] == 0) {
tilevalid[1][tile] = 1;
uint8 *output = (uint8*)tiledata[1] + (tile << 6);
unsigned offset = tile << 5;
unsigned y = 8;
unsigned color, d0, d1, d2, d3;
while(y--) {
d0 = memory::vram[offset + 0];
d1 = memory::vram[offset + 1];
d2 = memory::vram[offset + 16];
d3 = memory::vram[offset + 17];
#define render_line(mask) \
color = !!(d0 & mask) << 0; \
color |= !!(d1 & mask) << 1; \
color |= !!(d2 & mask) << 2; \
color |= !!(d3 & mask) << 3; \
*output++ = color
render_line(0x80);
render_line(0x40);
render_line(0x20);
render_line(0x10);
render_line(0x08);
render_line(0x04);
render_line(0x02);
render_line(0x01);
#undef render_line
offset += 2;
}
}
return tiledata[1] + (tile << 6);
}
uint8* PPU::Cache::tile_8bpp(unsigned tile) {
if(tilevalid[2][tile] == 0) {
tilevalid[2][tile] = 1;
uint8 *output = (uint8*)tiledata[2] + (tile << 6);
unsigned offset = tile << 6;
unsigned y = 8;
unsigned color, d0, d1, d2, d3, d4, d5, d6, d7;
while(y--) {
d0 = memory::vram[offset + 0];
d1 = memory::vram[offset + 1];
d2 = memory::vram[offset + 16];
d3 = memory::vram[offset + 17];
d4 = memory::vram[offset + 32];
d5 = memory::vram[offset + 33];
d6 = memory::vram[offset + 48];
d7 = memory::vram[offset + 49];
#define render_line(mask) \
color = !!(d0 & mask) << 0; \
color |= !!(d1 & mask) << 1; \
color |= !!(d2 & mask) << 2; \
color |= !!(d3 & mask) << 3; \
color |= !!(d4 & mask) << 4; \
color |= !!(d5 & mask) << 5; \
color |= !!(d6 & mask) << 6; \
color |= !!(d7 & mask) << 7; \
*output++ = color
render_line(0x80);
render_line(0x40);
render_line(0x20);
render_line(0x10);
render_line(0x08);
render_line(0x04);
render_line(0x02);
render_line(0x01);
#undef render_line
offset += 2;
}
}
return tiledata[2] + (tile << 6);
}
uint8* PPU::Cache::tile(unsigned bpp, unsigned tile) {
switch(bpp) {
case 0: return tile_2bpp(tile);
case 1: return tile_4bpp(tile);
case 2: return tile_8bpp(tile);
}
}
PPU::Cache::Cache(PPU &self) : self(self) {
tiledata[0] = new uint8[262144]();
tiledata[1] = new uint8[131072]();
tiledata[2] = new uint8[ 65536]();
tilevalid[0] = new uint8[ 4096]();
tilevalid[1] = new uint8[ 2048]();
tilevalid[2] = new uint8[ 1024]();
}
#endif

15
bsnes/snes/alt/ppu-new/cache/cache.hpp vendored Executable file
View File

@ -0,0 +1,15 @@
class Cache {
public:
uint8 *tiledata[3];
uint8 *tilevalid[3];
uint8* tile_2bpp(unsigned tile);
uint8* tile_4bpp(unsigned tile);
uint8* tile_8bpp(unsigned tile);
uint8* tile(unsigned bpp, unsigned tile);
Cache(PPU &self);
PPU &self;
friend class PPU;
};

View File

@ -1,6 +1,11 @@
#ifdef PPU_CPP
void PPU::latch_counters() {}
void PPU::latch_counters() {
regs.hcounter = cpu.hdot();
regs.vcounter = cpu.vcounter();
regs.counters_latched = true;
}
bool PPU::interlace() const { return false; }
bool PPU::overscan() const { return false; }
bool PPU::hires() const { return false; }
@ -23,8 +28,13 @@ uint8 PPU::vram_read(unsigned addr) {
}
void PPU::vram_write(unsigned addr, uint8 data) {
if(regs.display_disable) { memory::vram[addr] = data; return; }
if(cpu.vcounter() >= display.height) { memory::vram[addr] = data; return; }
if(regs.display_disable || cpu.vcounter() >= display.height) {
memory::vram[addr] = data;
cache.tilevalid[0][addr >> 4] = false;
cache.tilevalid[1][addr >> 5] = false;
cache.tilevalid[2][addr >> 6] = false;
return;
}
}
uint8 PPU::oam_read(unsigned addr) {
@ -36,9 +46,9 @@ uint8 PPU::oam_read(unsigned addr) {
void PPU::oam_write(unsigned addr, uint8 data) {
if(addr & 0x0200) addr &= 0x021f;
if(regs.display_disable) { memory::oam[addr] = data; return; }
if(cpu.vcounter() >= display.height) { memory::oam[addr] = data; return; }
memory::oam[0x0218] = data;
if(!regs.display_disable && cpu.vcounter() < display.height) addr = 0x0218;
memory::oam[addr] = data;
oam.update_list(addr, data);
}
uint8 PPU::cgram_read(unsigned addr) {
@ -49,6 +59,104 @@ void PPU::cgram_write(unsigned addr, uint8 data) {
memory::cgram[addr] = data;
}
void PPU::mmio_update_video_mode() {
switch(regs.bgmode) {
case 0: {
bg1.regs.mode = Background::Mode::BPP2; bg1.regs.priority0 = 8; bg1.regs.priority1 = 11;
bg2.regs.mode = Background::Mode::BPP2; bg2.regs.priority0 = 7; bg2.regs.priority1 = 10;
bg3.regs.mode = Background::Mode::BPP2; bg3.regs.priority0 = 2; bg3.regs.priority1 = 5;
bg4.regs.mode = Background::Mode::BPP2; bg4.regs.priority0 = 1; bg4.regs.priority1 = 4;
oam.regs.priority0 = 3; oam.regs.priority1 = 6; oam.regs.priority2 = 9; oam.regs.priority3 = 12;
} break;
case 1: {
bg1.regs.mode = Background::Mode::BPP4;
bg2.regs.mode = Background::Mode::BPP4;
bg3.regs.mode = Background::Mode::BPP2;
bg4.regs.mode = Background::Mode::Inactive;
if(regs.bg3_priority) {
bg1.regs.priority0 = 5; bg1.regs.priority1 = 8;
bg2.regs.priority0 = 4; bg2.regs.priority1 = 7;
bg3.regs.priority0 = 1; bg3.regs.priority1 = 10;
oam.regs.priority0 = 2; oam.regs.priority1 = 3; oam.regs.priority2 = 6; oam.regs.priority3 = 9;
} else {
bg1.regs.priority0 = 6; bg1.regs.priority1 = 9;
bg2.regs.priority0 = 5; bg2.regs.priority1 = 8;
bg3.regs.priority0 = 1; bg3.regs.priority1 = 3;
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 7; oam.regs.priority3 = 10;
}
} break;
case 2: {
bg1.regs.mode = Background::Mode::BPP4;
bg2.regs.mode = Background::Mode::BPP4;
bg3.regs.mode = Background::Mode::Inactive;
bg4.regs.mode = Background::Mode::Inactive;
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.regs.priority3 = 8;
} break;
case 3: {
bg1.regs.mode = Background::Mode::BPP8;
bg2.regs.mode = Background::Mode::BPP4;
bg3.regs.mode = Background::Mode::Inactive;
bg4.regs.mode = Background::Mode::Inactive;
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.regs.priority3 = 8;
} break;
case 4: {
bg1.regs.mode = Background::Mode::BPP8;
bg2.regs.mode = Background::Mode::BPP2;
bg3.regs.mode = Background::Mode::Inactive;
bg4.regs.mode = Background::Mode::Inactive;
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.regs.priority3 = 8;
} break;
case 5: {
bg1.regs.mode = Background::Mode::BPP4;
bg2.regs.mode = Background::Mode::BPP2;
bg3.regs.mode = Background::Mode::Inactive;
bg4.regs.mode = Background::Mode::Inactive;
bg1.regs.priority0 = 3; bg1.regs.priority1 = 7;
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.regs.priority3 = 8;
} break;
case 6: {
bg1.regs.mode = Background::Mode::BPP4;
bg2.regs.mode = Background::Mode::Inactive;
bg3.regs.mode = Background::Mode::Inactive;
bg4.regs.mode = Background::Mode::Inactive;
bg1.regs.priority0 = 2; bg1.regs.priority1 = 5;
oam.regs.priority0 = 1; oam.regs.priority1 = 3; oam.regs.priority2 = 4; oam.regs.priority3 = 6;
} break;
case 7: {
if(regs.mode7_extbg == false) {
bg1.regs.mode = Background::Mode::Mode7;
bg2.regs.mode = Background::Mode::Inactive;
bg3.regs.mode = Background::Mode::Inactive;
bg4.regs.mode = Background::Mode::Inactive;
bg1.regs.priority0 = 2; bg1.regs.priority1 = 2;
oam.regs.priority0 = 1; oam.regs.priority1 = 3; oam.regs.priority2 = 4; oam.regs.priority3 = 5;
} else {
bg1.regs.mode = Background::Mode::Mode7;
bg2.regs.mode = Background::Mode::Mode7;
bg3.regs.mode = Background::Mode::Inactive;
bg4.regs.mode = Background::Mode::Inactive;
bg1.regs.priority0 = 3; bg1.regs.priority1 = 3;
bg2.regs.priority0 = 1; bg2.regs.priority1 = 5;
oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.regs.priority3 = 7;
}
} break;
}
}
uint8 PPU::mmio_read(unsigned addr) {
cpu.synchronize_ppu();
@ -85,7 +193,7 @@ uint8 PPU::mmio_read(unsigned addr) {
case 0x2138: { //OAMDATAREAD
regs.ppu1_mdr = oam_read(regs.oam_addr);
regs.oam_addr = (regs.oam_addr + 1) & 0x03ff;
//TODO: handle OAM address reset here
oam.set_first();
return regs.ppu1_mdr;
}
@ -120,6 +228,51 @@ uint8 PPU::mmio_read(unsigned addr) {
regs.cgram_addr = (regs.cgram_addr + 1) & 0x01ff;
return regs.ppu2_mdr;
}
case 0x213c: { //OPHCT
if(regs.latch_hcounter == 0) {
regs.ppu2_mdr = regs.hcounter & 0xff;
} else {
regs.ppu2_mdr = (regs.ppu2_mdr & 0xfe) | (regs.hcounter >> 8);
}
regs.latch_hcounter ^= 1;
return regs.ppu2_mdr;
}
case 0x213d: { //OPVCT
if(regs.latch_vcounter == 0) {
regs.ppu2_mdr = regs.vcounter & 0xff;
} else {
regs.ppu2_mdr = (regs.ppu2_mdr & 0xfe) | (regs.vcounter >> 8);
}
regs.latch_vcounter ^= 1;
return regs.ppu2_mdr;
}
case 0x213e: { //STAT77
regs.ppu1_mdr &= 0x10;
regs.ppu1_mdr |= oam.regs.time_over << 7;
regs.ppu1_mdr |= oam.regs.range_over << 6;
regs.ppu1_mdr |= 0x01; //version
return regs.ppu1_mdr;
}
case 0x213f: { //STAT78
regs.latch_hcounter = 0;
regs.latch_vcounter = 0;
regs.ppu2_mdr &= 0x20;
regs.ppu2_mdr |= cpu.field() << 7;
if((cpu.pio() & 0x80) == 0) {
regs.ppu2_mdr |= 0x40;
} else if(regs.counters_latched) {
regs.ppu2_mdr |= 0x40;
regs.counters_latched = false;
}
regs.ppu2_mdr |= (system.region() == System::Region::NTSC ? 0 : 1) << 4;
regs.ppu2_mdr |= 0x03; //version
return regs.ppu2_mdr;
}
}
return cpu.regs.mdr;
@ -130,23 +283,30 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
switch(addr & 0xffff) {
case 0x2100: { //INIDISP
//TODO: handle OAM address reset here
if(regs.display_disable && vcounter() == display.height) oam.address_reset();
regs.display_disable = data & 0x80;
regs.display_brightness = data & 0x0f;
screen.light_table = screen.light_tables[regs.display_brightness];
return;
}
case 0x2101: { //OBSEL
oam.regs.base_size = (data >> 5) & 7;
oam.regs.nameselect = (data >> 3) & 3;
oam.regs.tiledata_addr = (data & 3) << 14;
return;
}
case 0x2102: { //OAMADDL
regs.oam_baseaddr = (regs.oam_baseaddr & 0x0100) | (data << 0);
//TODO: handle OAM address reset here
oam.address_reset();
return;
}
case 0x2103: { //OAMADDH
regs.oam_priority = data & 0x80;
regs.oam_baseaddr = ((data & 1) << 8) | (regs.oam_baseaddr & 0x00ff);
//TODO: handle OAM address reset here
oam.address_reset();
return;
}
@ -160,7 +320,117 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
oam_write((regs.oam_addr & ~1) + 1, data);
}
regs.oam_addr = (regs.oam_addr + 1) & 0x03ff;
//TODO: handle OAM address reset here
oam.set_first();
return;
}
case 0x2105: { //BGMODE
bg4.regs.tile_size = data & 0x80;
bg3.regs.tile_size = data & 0x40;
bg2.regs.tile_size = data & 0x20;
bg1.regs.tile_size = data & 0x10;
regs.bg3_priority = data & 0x08;
regs.bgmode = data & 0x07;
mmio_update_video_mode();
return;
}
case 0x2106: { //MOSAIC
unsigned mosaic_size = (data >> 4) & 15;
bg4.regs.mosaic = (data & 0x08 ? mosaic_size : 0);
bg3.regs.mosaic = (data & 0x04 ? mosaic_size : 0);
bg2.regs.mosaic = (data & 0x02 ? mosaic_size : 0);
bg1.regs.mosaic = (data & 0x01 ? mosaic_size : 0);
return;
}
case 0x2107: { //BG1SC
bg1.regs.screen_addr = (data & 0x7c) << 9;
bg1.regs.screen_size = data & 3;
return;
}
case 0x2108: { //BG2SC
bg2.regs.screen_addr = (data & 0x7c) << 9;
bg2.regs.screen_size = data & 3;
return;
}
case 0x2109: { //BG3SC
bg3.regs.screen_addr = (data & 0x7c) << 9;
bg3.regs.screen_size = data & 3;
return;
}
case 0x210a: { //BG4SC
bg4.regs.screen_addr = (data & 0x7c) << 9;
bg4.regs.screen_size = data & 3;
return;
}
case 0x210b: { //BG12NBA
bg1.regs.tiledata_addr = (data & 0x07) << 13;
bg2.regs.tiledata_addr = (data & 0x70) << 9;
return;
}
case 0x210c: { //BG34NBA
bg3.regs.tiledata_addr = (data & 0x07) << 13;
bg4.regs.tiledata_addr = (data & 0x70) << 9;
return;
}
case 0x210d: { //BG1HOFS
regs.mode7_hoffset = (data << 8) | regs.mode7_latchdata;
regs.mode7_latchdata = data;
bg1.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg1.regs.hoffset >> 8) & 7);
regs.bgofs_latchdata = data;
return;
}
case 0x210e: { //BG1VOFS
regs.mode7_voffset = (data << 8) | regs.mode7_latchdata;
regs.mode7_latchdata = data;
bg1.regs.voffset = (data << 8) | regs.bgofs_latchdata;
regs.bgofs_latchdata = data;
return;
}
case 0x210f: { //BG2HOFS
bg2.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg2.regs.hoffset >> 8) & 7);
regs.bgofs_latchdata = data;
return;
}
case 0x2110: { //BG2VOFS
bg2.regs.voffset = (data << 8) | regs.bgofs_latchdata;
regs.bgofs_latchdata = data;
return;
}
case 0x2111: { //BG3HOFS
bg3.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg3.regs.hoffset >> 8) & 7);
regs.bgofs_latchdata = data;
return;
}
case 0x2112: { //BG3VOFS
bg3.regs.voffset = (data << 8) | regs.bgofs_latchdata;
regs.bgofs_latchdata = data;
return;
}
case 0x2113: { //BG4HOFS
bg4.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg4.regs.hoffset >> 8) & 7);
regs.bgofs_latchdata = data;
return;
}
case 0x2114: { //BG4VOFS
bg4.regs.voffset = (data << 8) | regs.bgofs_latchdata;
regs.bgofs_latchdata = data;
return;
}
@ -262,6 +532,149 @@ void PPU::mmio_write(unsigned addr, uint8 data) {
regs.cgram_addr = (regs.cgram_addr + 1) & 0x01ff;
return;
}
case 0x2123: { //W12SEL
window.regs.bg2_two_enable = data & 0x80;
window.regs.bg2_two_invert = data & 0x40;
window.regs.bg2_one_enable = data & 0x20;
window.regs.bg2_one_invert = data & 0x10;
window.regs.bg1_two_enable = data & 0x08;
window.regs.bg1_two_invert = data & 0x04;
window.regs.bg1_one_enable = data & 0x02;
window.regs.bg1_one_invert = data & 0x01;
return;
}
case 0x2124: { //W34SEL
window.regs.bg4_two_enable = data & 0x80;
window.regs.bg4_two_invert = data & 0x40;
window.regs.bg4_one_enable = data & 0x20;
window.regs.bg4_one_invert = data & 0x10;
window.regs.bg3_two_enable = data & 0x08;
window.regs.bg3_two_invert = data & 0x04;
window.regs.bg3_one_enable = data & 0x02;
window.regs.bg3_one_invert = data & 0x01;
return;
}
case 0x2125: { //WOBJSEL
window.regs.col_two_enable = data & 0x80;
window.regs.col_two_invert = data & 0x40;
window.regs.col_one_enable = data & 0x20;
window.regs.col_one_invert = data & 0x10;
window.regs.oam_two_enable = data & 0x08;
window.regs.oam_two_invert = data & 0x04;
window.regs.oam_one_enable = data & 0x02;
window.regs.oam_one_invert = data & 0x01;
return;
}
case 0x2126: { //WH0
window.regs.one_left = data;
return;
}
case 0x2127: { //WH1
window.regs.one_right = data;
return;
}
case 0x2128: { //WH2
window.regs.two_left = data;
return;
}
case 0x2129: { //WH3
window.regs.two_right = data;
return;
}
case 0x212a: { //WBGLOG
window.regs.bg4_mask = (data >> 6) & 3;
window.regs.bg3_mask = (data >> 4) & 3;
window.regs.bg2_mask = (data >> 2) & 3;
window.regs.bg1_mask = (data >> 0) & 3;
return;
}
case 0x212b: { //WOBJLOG
window.regs.col_mask = (data >> 2) & 3;
window.regs.oam_mask = (data >> 0) & 3;
return;
}
case 0x212c: { //TM
oam.regs.main_enable = data & 0x10;
bg4.regs.main_enable = data & 0x08;
bg3.regs.main_enable = data & 0x04;
bg2.regs.main_enable = data & 0x02;
bg1.regs.main_enable = data & 0x01;
return;
}
case 0x212d: { //TS
oam.regs.sub_enable = data & 0x10;
bg4.regs.sub_enable = data & 0x08;
bg3.regs.sub_enable = data & 0x04;
bg2.regs.sub_enable = data & 0x02;
bg1.regs.sub_enable = data & 0x01;
return;
}
case 0x212e: { //TMW
window.regs.oam_main_enable = data & 0x10;
window.regs.bg4_main_enable = data & 0x08;
window.regs.bg3_main_enable = data & 0x04;
window.regs.bg2_main_enable = data & 0x02;
window.regs.bg1_main_enable = data & 0x01;
return;
}
case 0x212f: { //TSW
window.regs.oam_sub_enable = data & 0x10;
window.regs.bg4_sub_enable = data & 0x08;
window.regs.bg3_sub_enable = data & 0x04;
window.regs.bg2_sub_enable = data & 0x02;
window.regs.bg1_sub_enable = data & 0x01;
return;
}
case 0x2130: { //CGWSEL
window.regs.col_main_mask = (data >> 6) & 3;
window.regs.col_sub_mask = (data >> 4) & 3;
screen.regs.addsub_mode = data & 0x02;
screen.regs.direct_color = data & 0x01;
return;
}
case 0x2131: { //CGADDSUB
screen.regs.color_mode = data & 0x80;
screen.regs.color_halve = data & 0x40;
screen.regs.back_color_enable = data & 0x20;
screen.regs.oam_color_enable = data & 0x10;
screen.regs.bg4_color_enable = data & 0x08;
screen.regs.bg3_color_enable = data & 0x04;
screen.regs.bg2_color_enable = data & 0x02;
screen.regs.bg1_color_enable = data & 0x01;
return;
}
case 0x2132: { //COLDATA
if(data & 0x80) screen.regs.color_b = data & 0x1f;
if(data & 0x40) screen.regs.color_g = data & 0x1f;
if(data & 0x20) screen.regs.color_r = data & 0x1f;
return;
}
case 0x2133: { //SETINI
regs.mode7_extbg = data & 0x40;
regs.pseudo_hires = data & 0x08;
regs.overscan = data & 0x04;
oam.regs.interlace = data & 0x02;
regs.interlace = data & 0x01;
mmio_update_video_mode();
return;
}
}
}
@ -273,18 +686,72 @@ void PPU::mmio_reset() {
regs.vram_readbuffer = 0;
regs.oam_latchdata = 0;
regs.cgram_latchdata = 0;
regs.bgofs_latchdata = 0;
regs.mode7_latchdata = 0;
regs.counters_latched = 0;
regs.latch_hcounter = 0;
regs.latch_vcounter = 0;
oam.regs.first_sprite = 0;
//$2100
regs.display_disable = true;
regs.display_brightness = 0;
screen.light_table = screen.light_tables[regs.display_brightness];
//$2101
oam.regs.base_size = 0;
oam.regs.nameselect = 0;
oam.regs.tiledata_addr = 0;
//$2102-$2103
regs.oam_baseaddr = 0;
regs.oam_addr = 0;
regs.oam_priority = 0;
//$2105
bg4.regs.tile_size = 0;
bg3.regs.tile_size = 0;
bg2.regs.tile_size = 0;
bg1.regs.tile_size = 0;
regs.bg3_priority = 0;
regs.bgmode = 0;
//$2106
bg4.regs.mosaic = 0;
bg3.regs.mosaic = 0;
bg2.regs.mosaic = 0;
bg1.regs.mosaic = 0;
//$2107-$210a
bg1.regs.screen_addr = 0;
bg1.regs.screen_size = 0;
bg2.regs.screen_addr = 0;
bg2.regs.screen_size = 0;
bg3.regs.screen_addr = 0;
bg3.regs.screen_size = 0;
bg4.regs.screen_addr = 0;
bg4.regs.screen_size = 0;
//$210b-$210c
bg1.regs.tiledata_addr = 0;
bg2.regs.tiledata_addr = 0;
bg3.regs.tiledata_addr = 0;
bg4.regs.tiledata_addr = 0;
//$210d-$2114
regs.mode7_hoffset = 0;
regs.mode7_voffset = 0;
bg1.regs.hoffset = 0;
bg1.regs.voffset = 0;
bg2.regs.hoffset = 0;
bg2.regs.voffset = 0;
bg3.regs.hoffset = 0;
bg3.regs.voffset = 0;
bg4.regs.hoffset = 0;
bg4.regs.voffset = 0;
//$2115
regs.vram_incmode = 0;
regs.vram_mapping = 0;
@ -308,6 +775,113 @@ void PPU::mmio_reset() {
//$2121
regs.cgram_addr = 0;
//$2123-$2125
window.regs.bg1_one_enable = 0;
window.regs.bg1_one_invert = 0;
window.regs.bg1_two_enable = 0;
window.regs.bg1_two_invert = 0;
window.regs.bg2_one_enable = 0;
window.regs.bg2_one_invert = 0;
window.regs.bg2_two_enable = 0;
window.regs.bg2_two_invert = 0;
window.regs.bg3_one_enable = 0;
window.regs.bg3_one_invert = 0;
window.regs.bg3_two_enable = 0;
window.regs.bg3_two_invert = 0;
window.regs.bg4_one_enable = 0;
window.regs.bg4_one_invert = 0;
window.regs.bg4_two_enable = 0;
window.regs.bg4_two_invert = 0;
window.regs.oam_one_enable = 0;
window.regs.oam_one_invert = 0;
window.regs.oam_two_enable = 0;
window.regs.oam_two_invert = 0;
window.regs.col_one_enable = 0;
window.regs.col_one_invert = 0;
window.regs.col_two_enable = 0;
window.regs.col_two_invert = 0;
//$2126-$2129
window.regs.one_left = 0;
window.regs.one_right = 0;
window.regs.two_left = 0;
window.regs.two_right = 0;
//$212a-$212b
window.regs.bg1_mask = 0;
window.regs.bg2_mask = 0;
window.regs.bg3_mask = 0;
window.regs.bg4_mask = 0;
window.regs.oam_mask = 0;
window.regs.col_mask = 0;
//$212c
bg1.regs.main_enable = 0;
bg2.regs.main_enable = 0;
bg3.regs.main_enable = 0;
bg4.regs.main_enable = 0;
oam.regs.main_enable = 0;
//$212d
bg1.regs.sub_enable = 0;
bg2.regs.sub_enable = 0;
bg3.regs.sub_enable = 0;
bg4.regs.sub_enable = 0;
oam.regs.sub_enable = 0;
//$212e
window.regs.bg1_main_enable = 0;
window.regs.bg2_main_enable = 0;
window.regs.bg3_main_enable = 0;
window.regs.bg4_main_enable = 0;
window.regs.oam_main_enable = 0;
//$212f
window.regs.bg1_sub_enable = 0;
window.regs.bg2_sub_enable = 0;
window.regs.bg3_sub_enable = 0;
window.regs.bg4_sub_enable = 0;
window.regs.oam_sub_enable = 0;
//$2130
window.regs.col_main_mask = 0;
window.regs.col_sub_mask = 0;
screen.regs.addsub_mode = 0;
screen.regs.direct_color = 0;
//$2131
screen.regs.color_mode = 0;
screen.regs.color_halve = 0;
screen.regs.back_color_enable = 0;
screen.regs.oam_color_enable = 0;
screen.regs.bg4_color_enable = 0;
screen.regs.bg3_color_enable = 0;
screen.regs.bg2_color_enable = 0;
screen.regs.bg1_color_enable = 0;
//$2132
screen.regs.color_b = 0;
screen.regs.color_g = 0;
screen.regs.color_r = 0;
//$2133
regs.mode7_extbg = 0;
regs.pseudo_hires = 0;
regs.overscan = 0;
oam.regs.interlace = 0;
regs.interlace = 0;
//$213e
oam.regs.time_over = 0;
oam.regs.range_over = 0;
mmio_update_video_mode();
}
#endif

View File

@ -6,8 +6,13 @@ struct Regs {
uint16 vram_readbuffer;
uint8 oam_latchdata;
uint8 cgram_latchdata;
uint8 bgofs_latchdata;
uint8 mode7_latchdata;
bool counters_latched;
bool latch_hcounter;
bool latch_vcounter;
//$2100
bool display_disable;
unsigned display_brightness;
@ -17,6 +22,16 @@ struct Regs {
uint16 oam_addr;
bool oam_priority;
//$2105
bool bg3_priority;
unsigned bgmode;
//$210d
uint16 mode7_hoffset;
//$210e
uint16 mode7_voffset;
//$2115
bool vram_incmode;
unsigned vram_mapping;
@ -40,6 +55,18 @@ struct Regs {
//$2121
uint16 cgram_addr;
//$2133
bool mode7_extbg;
bool pseudo_hires;
bool overscan;
bool interlace;
//$213c
uint16 hcounter;
//$213d
uint16 vcounter;
} regs;
uint16 get_vram_addr();
@ -52,6 +79,7 @@ void oam_write(unsigned addr, uint8 data);
uint8 cgram_read(unsigned addr);
void cgram_write(unsigned addr, uint8 data);
void mmio_update_video_mode();
uint8 mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8 data);
void mmio_reset();

View File

@ -6,6 +6,10 @@ namespace SNES {
PPU ppu;
#include "mmio/mmio.cpp"
#include "cache/cache.cpp"
#include "background/background.cpp"
#include "sprite/sprite.cpp"
#include "window/window.cpp"
#include "screen/screen.cpp"
#include "serialization.cpp"
@ -48,6 +52,12 @@ void PPU::add_clocks(unsigned clocks) {
void PPU::render_scanline() {
if(regs.display_disable) return screen.render_black();
screen.scanline();
bg1.render();
bg2.render();
bg3.render();
bg4.render();
oam.render();
screen.render();
}
@ -77,7 +87,15 @@ void PPU::reset() {
mmio_reset();
}
PPU::PPU() : screen(*this) {
PPU::PPU() :
cache(*this),
bg1(*this, Background::ID::BG1),
bg2(*this, Background::ID::BG2),
bg3(*this, Background::ID::BG3),
bg4(*this, Background::ID::BG4),
oam(*this),
window(*this),
screen(*this) {
surface = new uint16[512 * 512];
output = surface + 16 * 512;
}

View File

@ -24,8 +24,19 @@ private:
uint16 *output;
#include "mmio/mmio.hpp"
#include "cache/cache.hpp"
#include "background/background.hpp"
#include "sprite/sprite.hpp"
#include "window/window.hpp"
#include "screen/screen.hpp"
Cache cache;
Background bg1;
Background bg2;
Background bg3;
Background bg4;
Sprite oam;
Window window;
Screen screen;
struct Display {
@ -37,6 +48,9 @@ private:
void add_clocks(unsigned clocks);
void render_scanline();
friend class PPU::Cache;
friend class PPU::Background;
friend class PPU::Sprite;
friend class PPU::Screen;
friend class Video;
};

View File

@ -1,5 +1,19 @@
#ifdef PPU_CPP
unsigned PPU::Screen::get_palette(unsigned color) {
color <<= 1;
return (memory::cgram[color + 0] << 0) + (memory::cgram[color + 1] << 8);
}
void PPU::Screen::scanline() {
for(unsigned x = 0; x < 256; x++) {
output.main[x].priority = 0;
output.main[x].color = 0;
output.sub[x].priority = 0;
output.sub[x].color = 0;
}
}
void PPU::Screen::render_black() {
uint16 *data = self.output + self.vcounter() * 1024;
memset(data, 0, self.display.width << 1);
@ -7,11 +21,8 @@ void PPU::Screen::render_black() {
void PPU::Screen::render() {
uint16 *data = self.output + self.vcounter() * 1024;
uint16 color = memory::cgram[0];
color |= memory::cgram[1] << 8;
color = light_table[color];
for(unsigned i = 0; i < 256; i++) {
data[i] = color;
data[i] = light_table[output.main[i].color];
}
}
@ -38,4 +49,20 @@ PPU::Screen::~Screen() {
delete[] light_tables;
}
void PPU::Screen::Output::plot_main(unsigned x, unsigned color, unsigned priority) {
if(x & 256) return;
if(priority > main[x].priority) {
main[x].priority = priority;
main[x].color = color;
}
}
void PPU::Screen::Output::plot_sub(unsigned x, unsigned color, unsigned priority) {
if(x & 256) return;
if(priority > sub[x].priority) {
sub[x].priority = priority;
sub[x].color = color;
}
}
#endif

View File

@ -1,17 +1,43 @@
class Screen {
public:
struct Regs {
bool addsub_mode;
bool direct_color;
bool color_mode;
bool color_halve;
bool back_color_enable;
bool oam_color_enable;
bool bg4_color_enable;
bool bg3_color_enable;
bool bg2_color_enable;
bool bg1_color_enable;
unsigned color_b;
unsigned color_g;
unsigned color_r;
} regs;
struct Output {
struct {
unsigned color;
unsigned priority;
} main[256], sub[256];
void plot_main(unsigned x, unsigned color, unsigned priority);
void plot_sub(unsigned x, unsigned color, unsigned priority);
} output;
uint16 **light_tables;
uint16 *light_table;
unsigned get_palette(unsigned color);
void scanline();
void render_black();
void render();
Screen(PPU &self);
~Screen();
private:
PPU &self;
uint16 **light_tables;
uint16 *light_table;
struct Regs {
} regs;
friend class PPU;
};

View File

@ -0,0 +1,135 @@
#ifdef PPU_CPP
void PPU::Sprite::update_list(unsigned addr, uint8 data) {
if(addr < 0x0200) {
unsigned i = addr >> 2;
switch(addr & 3) {
case 0: list[i].x = (list[i].x & 0x0100) | data; break;
case 1: list[i].y = (data + 1) & 0xff; break;
case 2: list[i].character = data; break;
case 3: list[i].vflip = data & 0x80;
list[i].hflip = data & 0x40;
list[i].priority = (data >> 4) & 3;
list[i].palette = (data >> 1) & 7;
list[i].use_nameselect = data & 0x01;
break;
}
} else {
unsigned i = (addr & 0x1f) << 2;
list[i + 0].x = ((data & 0x01) << 8) | (list[i + 0].x & 0xff);
list[i + 0].size = data & 0x02;
list[i + 1].x = ((data & 0x04) << 8) | (list[i + 1].x & 0xff);
list[i + 1].size = data & 0x08;
list[i + 2].x = ((data & 0x10) << 8) | (list[i + 2].x & 0xff);
list[i + 2].size = data & 0x20;
list[i + 3].x = ((data & 0x40) << 8) | (list[i + 3].x & 0xff);
list[i + 3].size = data & 0x80;
}
}
void PPU::Sprite::address_reset() {
self.regs.oam_addr = self.regs.oam_baseaddr << 1;
set_first();
}
void PPU::Sprite::set_first() {
regs.first_sprite = (self.regs.oam_priority == false ? 0 : (self.regs.oam_addr >> 2) & 127);
}
bool PPU::Sprite::on_scanline(unsigned sprite) {
auto &s = list[sprite];
if(s.x > 256 && (s.x + s.width - 1) < 512) return false;
signed height = (regs.interlace == false ? s.height : s.height >> 1);
if(self.vcounter() >= s.y && self.vcounter() < (s.y + height)) return true;
if((s.y + height) >= 256 && self.vcounter() < ((s.y + height) & 255)) return true;
return false;
}
void PPU::Sprite::render() {
for(unsigned i = 0; i < 128; i++) {
if(list[i].size == 0) {
static unsigned width[] = { 8, 8, 8, 16, 16, 32, 16, 16 };
static unsigned height[] = { 8, 8, 8, 16, 16, 32, 32, 32 };
list[i].width = width[regs.base_size];
list[i].height = height[regs.base_size];
} else {
static unsigned width[] = { 16, 32, 64, 32, 64, 64, 32, 32 };
static unsigned height[] = { 16, 32, 64, 32, 64, 64, 64, 32 };
list[i].width = width[regs.base_size];
list[i].height = height[regs.base_size];
if(regs.interlace && regs.base_size >= 6) list[i].height = 16;
}
}
unsigned itemcount = 0;
unsigned tilecount = 0;
memset(output.priority, 0xff, 256);
memset(itemlist, 0xff, 32);
for(unsigned i = 0; i < 34; i++) tilelist[i].tile = 0xffff;
for(unsigned i = 0; i < 128; i++) {
unsigned s = (regs.first_sprite + i) & 127;
if(on_scanline(s) == false) continue;
if(itemcount++ >= 32) break;
itemlist[itemcount - 1] = s;
}
for(signed i = 31; i >= 0; i--) {
if(itemlist[i] == 0xff) continue;
auto &s = list[itemlist[i]];
unsigned tile_width = s.width >> 3;
signed x = s.x;
signed y = (self.vcounter() - s.y) & 0xff;
if(regs.interlace) y <<= 1;
if(s.vflip) {
if(s.width == s.height) {
y = (s.height - 1) - y;
} else {
y = (y < s.width) ? ((s.width - 1) - y) : (s.width + ((s.width - 1) - (y - s.width)));
}
}
if(regs.interlace) {
y = (s.vflip == false) ? (y + self.field()) : (y - self.field());
}
x &= 511;
y &= 255;
uint16 tdaddr = regs.tiledata_addr;
uint16 chrx = (s.character >> 0) & 15;
uint16 chry = (s.character >> 4) & 15;
if(s.use_nameselect) {
tdaddr += (256 * 32) + (regs.nameselect << 13);
}
chry += (y >> 3);
chry &= 15;
chry <<= 4;
for(unsigned tx = 0; tx < tile_width; tx++) {
unsigned sx = (x + (tx << 3)) & 511;
if(x != 256 && sx >= 256 && (sx + 7) < 512) continue;
if(tilecount++ >= 34) break;
unsigned n = tilecount - 1;
tilelist[n].x = sx;
tilelist[n].y = y;
tilelist[n].priority = s.priority;
tilelist[n].palette = 128 + (s.palette << 4);
tilelist[n].hflip = s.hflip;
unsigned mx = (s.hflip == false) ? tx : ((tile_width - 1) - tx);
unsigned pos = tdaddr + ((chry + ((chrx + mx) & 15)) << 5);
tilelist[n].tile = (pos >> 5) & 0x07ff;
}
}
regs.time_over |= (tilecount > 34);
regs.range_over |= (itemcount > 32);
}
PPU::Sprite::Sprite(PPU &self) : self(self) {
}
#endif

View File

@ -0,0 +1,60 @@
class Sprite {
struct Regs {
unsigned priority0;
unsigned priority1;
unsigned priority2;
unsigned priority3;
unsigned base_size;
unsigned nameselect;
unsigned tiledata_addr;
unsigned first_sprite;
bool main_enable;
bool sub_enable;
bool interlace;
bool time_over;
bool range_over;
} regs;
struct List {
unsigned width;
unsigned height;
unsigned x;
unsigned y;
unsigned character;
bool use_nameselect;
bool vflip;
bool hflip;
unsigned palette;
unsigned priority;
bool size;
} list[128];
uint8 itemlist[32];
struct TileList {
unsigned x;
unsigned y;
unsigned priority;
unsigned palette;
unsigned tile;
bool hflip;
} tilelist[34];
struct Output {
uint8 palette[256];
uint8 priority[256];
} output;
void update_list(unsigned addr, uint8 data);
void address_reset();
void set_first();
bool on_scanline(unsigned sprite);
void render();
Sprite(PPU &self);
PPU &self;
friend class PPU;
};

View File

@ -0,0 +1,6 @@
#ifdef PPU_CPP
PPU::Window::Window(PPU &self) : self(self) {
}
#endif

View File

@ -0,0 +1,66 @@
class Window {
struct Regs {
bool bg1_one_enable;
bool bg1_one_invert;
bool bg1_two_enable;
bool bg1_two_invert;
bool bg2_one_enable;
bool bg2_one_invert;
bool bg2_two_enable;
bool bg2_two_invert;
bool bg3_one_enable;
bool bg3_one_invert;
bool bg3_two_enable;
bool bg3_two_invert;
bool bg4_one_enable;
bool bg4_one_invert;
bool bg4_two_enable;
bool bg4_two_invert;
bool oam_one_enable;
bool oam_one_invert;
bool oam_two_enable;
bool oam_two_invert;
bool col_one_enable;
bool col_one_invert;
bool col_two_enable;
bool col_two_invert;
unsigned one_left;
unsigned one_right;
unsigned two_left;
unsigned two_right;
unsigned bg1_mask;
unsigned bg2_mask;
unsigned bg3_mask;
unsigned bg4_mask;
unsigned oam_mask;
unsigned col_mask;
bool bg1_main_enable;
bool bg2_main_enable;
bool bg3_main_enable;
bool bg4_main_enable;
bool oam_main_enable;
bool bg1_sub_enable;
bool bg2_sub_enable;
bool bg3_sub_enable;
bool bg4_sub_enable;
bool oam_sub_enable;
unsigned col_main_mask;
unsigned col_sub_mask;
} regs;
Window(PPU &self);
PPU &self;
friend class PPU;
};

View File

@ -20,8 +20,12 @@ void MSU1::enter() {
if(mmio.audio_play) {
if(audiofile.open()) {
if(audiofile.end()) {
if(!mmio.audio_repeat) mmio.audio_play = false;
audiofile.seek(mmio.audio_offset = 58);
if(!mmio.audio_repeat) {
mmio.audio_play = false;
audiofile.seek(mmio.audio_offset = 8);
} else {
audiofile.seek(mmio.audio_offset = mmio.audio_loop_offset);
}
} else {
mmio.audio_offset += 4;
left = audiofile.readl(2);
@ -49,7 +53,7 @@ void MSU1::enable() {
audio.coprocessor_frequency(44100.0);
if(datafile.open()) datafile.close();
datafile.open(string() << cartridge.basename() << ".msu", file::mode_read);
datafile.open(string(cartridge.basename(), ".msu"), file::mode_read);
}
void MSU1::power() {
@ -125,10 +129,14 @@ void MSU1::mmio_write(unsigned addr, uint8 data) {
if(addr == 0x2005) {
mmio.audio_track = (mmio.audio_track & 0x00ff) | (data << 8);
if(audiofile.open()) audiofile.close();
char track[16];
sprintf(track, "-%u", mmio.audio_track);
if(audiofile.open(string() << cartridge.basename() << track << ".wav", file::mode_read)) {
audiofile.seek(mmio.audio_offset = 58); //skip WAV header
if(audiofile.open(string(cartridge.basename(), "-", mmio.audio_track, ".pcm"), file::mode_read)) {
uint32 header = audiofile.readm(4);
if(header != 0x4d535531) { //verify 'MSU1' header
audiofile.close();
} else {
mmio.audio_offset = 8;
mmio.audio_loop_offset = 8 + audiofile.readl(4) * 4;
}
}
mmio.audio_busy = false;
mmio.audio_repeat = false;

View File

@ -27,8 +27,11 @@ private:
struct MMIO {
uint32 data_offset;
uint32 audio_offset;
uint32 audio_loop_offset;
uint16 audio_track;
uint8 audio_volume;
bool data_busy;
bool audio_busy;
bool audio_repeat;

View File

@ -5,22 +5,23 @@ void MSU1::serialize(serializer &s) {
s.integer(mmio.data_offset);
s.integer(mmio.audio_offset);
s.integer(mmio.audio_loop_offset);
s.integer(mmio.audio_track);
s.integer(mmio.audio_volume);
s.integer(mmio.data_busy);
s.integer(mmio.audio_busy);
s.integer(mmio.audio_repeat);
s.integer(mmio.audio_play);
if(datafile.open()) datafile.close();
if(datafile.open(string() << cartridge.basename() << ".msu", file::mode_read)) {
if(datafile.open(string(cartridge.basename(), ".msu"), file::mode_read)) {
datafile.seek(mmio.data_offset);
}
if(audiofile.open()) audiofile.close();
char track[16];
sprintf(track, "-%u", mmio.audio_track);
if(audiofile.open(string() << cartridge.basename() << track << ".wav", file::mode_read)) {
if(audiofile.open(string(cartridge.basename(), "-", mmio.audio_track, ".pcm"), file::mode_read)) {
audiofile.seek(mmio.audio_offset);
}
}

View File

@ -1,7 +1,7 @@
namespace SNES {
namespace Info {
static const char Name[] = "bsnes";
static const char Version[] = "068.05";
static const char Version[] = "068.06";
static const unsigned SerializerVersion = 13;
}
}