diff --git a/bsnes/snes/alt/ppu-new/background/background.cpp b/bsnes/snes/alt/ppu-new/background/background.cpp new file mode 100755 index 00000000..1e710260 --- /dev/null +++ b/bsnes/snes/alt/ppu-new/background/background.cpp @@ -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 diff --git a/bsnes/snes/alt/ppu-new/background/background.hpp b/bsnes/snes/alt/ppu-new/background/background.hpp new file mode 100755 index 00000000..c7644ea5 --- /dev/null +++ b/bsnes/snes/alt/ppu-new/background/background.hpp @@ -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; +}; diff --git a/bsnes/snes/alt/ppu-new/cache/cache.cpp b/bsnes/snes/alt/ppu-new/cache/cache.cpp new file mode 100755 index 00000000..3e8e50a3 --- /dev/null +++ b/bsnes/snes/alt/ppu-new/cache/cache.cpp @@ -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 diff --git a/bsnes/snes/alt/ppu-new/cache/cache.hpp b/bsnes/snes/alt/ppu-new/cache/cache.hpp new file mode 100755 index 00000000..a86648be --- /dev/null +++ b/bsnes/snes/alt/ppu-new/cache/cache.hpp @@ -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; +}; diff --git a/bsnes/snes/alt/ppu-new/mmio/mmio.cpp b/bsnes/snes/alt/ppu-new/mmio/mmio.cpp index 6afb0cb5..0c96ee08 100755 --- a/bsnes/snes/alt/ppu-new/mmio/mmio.cpp +++ b/bsnes/snes/alt/ppu-new/mmio/mmio.cpp @@ -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 diff --git a/bsnes/snes/alt/ppu-new/mmio/mmio.hpp b/bsnes/snes/alt/ppu-new/mmio/mmio.hpp index 29f8f556..b796fc49 100755 --- a/bsnes/snes/alt/ppu-new/mmio/mmio.hpp +++ b/bsnes/snes/alt/ppu-new/mmio/mmio.hpp @@ -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(); diff --git a/bsnes/snes/alt/ppu-new/ppu.cpp b/bsnes/snes/alt/ppu-new/ppu.cpp index 07eb5a32..f96c2a7c 100755 --- a/bsnes/snes/alt/ppu-new/ppu.cpp +++ b/bsnes/snes/alt/ppu-new/ppu.cpp @@ -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; } diff --git a/bsnes/snes/alt/ppu-new/ppu.hpp b/bsnes/snes/alt/ppu-new/ppu.hpp index d90fb055..51c788a7 100755 --- a/bsnes/snes/alt/ppu-new/ppu.hpp +++ b/bsnes/snes/alt/ppu-new/ppu.hpp @@ -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; }; diff --git a/bsnes/snes/alt/ppu-new/screen/screen.cpp b/bsnes/snes/alt/ppu-new/screen/screen.cpp index ae1d440e..da10a09c 100755 --- a/bsnes/snes/alt/ppu-new/screen/screen.cpp +++ b/bsnes/snes/alt/ppu-new/screen/screen.cpp @@ -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 diff --git a/bsnes/snes/alt/ppu-new/screen/screen.hpp b/bsnes/snes/alt/ppu-new/screen/screen.hpp index 64d39531..95ea0bc9 100755 --- a/bsnes/snes/alt/ppu-new/screen/screen.hpp +++ b/bsnes/snes/alt/ppu-new/screen/screen.hpp @@ -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; }; diff --git a/bsnes/snes/alt/ppu-new/sprite/sprite.cpp b/bsnes/snes/alt/ppu-new/sprite/sprite.cpp new file mode 100755 index 00000000..c75aa609 --- /dev/null +++ b/bsnes/snes/alt/ppu-new/sprite/sprite.cpp @@ -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 diff --git a/bsnes/snes/alt/ppu-new/sprite/sprite.hpp b/bsnes/snes/alt/ppu-new/sprite/sprite.hpp new file mode 100755 index 00000000..c4659e33 --- /dev/null +++ b/bsnes/snes/alt/ppu-new/sprite/sprite.hpp @@ -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; +}; diff --git a/bsnes/snes/alt/ppu-new/window/window.cpp b/bsnes/snes/alt/ppu-new/window/window.cpp new file mode 100755 index 00000000..c5522dce --- /dev/null +++ b/bsnes/snes/alt/ppu-new/window/window.cpp @@ -0,0 +1,6 @@ +#ifdef PPU_CPP + +PPU::Window::Window(PPU &self) : self(self) { +} + +#endif diff --git a/bsnes/snes/alt/ppu-new/window/window.hpp b/bsnes/snes/alt/ppu-new/window/window.hpp new file mode 100755 index 00000000..10889cb6 --- /dev/null +++ b/bsnes/snes/alt/ppu-new/window/window.hpp @@ -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; +}; diff --git a/bsnes/snes/chip/msu1/msu1.cpp b/bsnes/snes/chip/msu1/msu1.cpp index 76ac88d6..9d5c2c02 100755 --- a/bsnes/snes/chip/msu1/msu1.cpp +++ b/bsnes/snes/chip/msu1/msu1.cpp @@ -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; diff --git a/bsnes/snes/chip/msu1/msu1.hpp b/bsnes/snes/chip/msu1/msu1.hpp index 473b75da..746eacb1 100755 --- a/bsnes/snes/chip/msu1/msu1.hpp +++ b/bsnes/snes/chip/msu1/msu1.hpp @@ -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; diff --git a/bsnes/snes/chip/msu1/serialization.cpp b/bsnes/snes/chip/msu1/serialization.cpp index cf0d639b..2764536f 100755 --- a/bsnes/snes/chip/msu1/serialization.cpp +++ b/bsnes/snes/chip/msu1/serialization.cpp @@ -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); } } diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index 97bed4c0..29c14f1e 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -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; } }