From 5b4702afc44dc861302a82afd15b85775ceeb5bf Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sat, 4 Sep 2010 13:36:03 +1000 Subject: [PATCH] Update to v068r12 release. (there was no r11 release posted to the WIP thread) byuu says: This took ten hours of mind boggling insanity to pull off. It upgrades the S-PPU dot-based renderer to fetch one tile, and then output all of its pixels before fetching again. It sounds easy enough, but it's insanely difficult. I ended up taking one small shortcut, in that rather than fetch at -7, I fetch at the first instance where a tile is needed to plot to x=0. So if you have {-3 to +4 } as a tile, it fetches at -3. That won't work so well on hardware, if two BGs fetch at the same X offset, they won't have time. I have had no luck staggering the reads at BG1=-7, BG3=-5, etc. While I can shift and fetch just fine, what happens is that when a new tile is fetched in, that gives a new palette, priority, etc; and this ends up happening between two tiles which results in the right-most edges of the screen ending up with the wrong colors and such. Offset-per-tile is cheap as always. Although looking at it, I'm not sure how BG3 could pre-fetch, especially with the way one or two OPT modes can fetch two tiles. There's no magic in Hoffset caching yet, so the SMW1 pixel issue is still there. Mode 7 got a bugfix, it was off-by-one horizontally from the mosaic code. After re-designing the BG mosaic, I ended up needing a separate mosaic for Mode7, and in the process I fixed that bug. The obvious change is that the Chrono Trigger Mode7->Mode2 transition doesn't cause the pendulum to jump anymore. Windows were simplified just a tad. The range testing is shared for all modes now. Ironically, it's a bit slower, but I'll take less code over more speed for the accuracy core. Speaking of speed, because there's so much less calculations per pixel for BGs, performance for the entire emulator has gone up by 30% in the accuracy core. Pretty neat overall, I can maintain 60fps in all but, yeah you can guess can't you? --- bsnes/Makefile | 2 +- bsnes/snes/ppu/background/background.cpp | 303 ++++++++++++----------- bsnes/snes/ppu/background/background.hpp | 51 ++-- bsnes/snes/ppu/background/mode7.cpp | 30 ++- bsnes/snes/ppu/mmio/mmio.cpp | 34 +-- bsnes/snes/ppu/mmio/mmio.hpp | 2 +- bsnes/snes/ppu/ppu.cpp | 17 +- bsnes/snes/ppu/ppu.hpp | 22 +- bsnes/snes/ppu/screen/screen.cpp | 6 +- bsnes/snes/ppu/screen/screen.hpp | 14 +- bsnes/snes/ppu/serialization.cpp | 36 ++- bsnes/snes/ppu/sprite/sprite.cpp | 10 +- bsnes/snes/ppu/sprite/sprite.hpp | 19 +- bsnes/snes/ppu/window/window.cpp | 34 +-- bsnes/snes/ppu/window/window.hpp | 27 +- bsnes/snes/snes.hpp | 2 +- 16 files changed, 335 insertions(+), 274 deletions(-) diff --git a/bsnes/Makefile b/bsnes/Makefile index 760ea410..da0794e7 100755 --- a/bsnes/Makefile +++ b/bsnes/Makefile @@ -1,6 +1,6 @@ include nall/Makefile snes := snes -profile := performance +profile := accuracy ui := qt # compiler diff --git a/bsnes/snes/ppu/background/background.cpp b/bsnes/snes/ppu/background/background.cpp index 8b1e84cc..d1b2ca5d 100755 --- a/bsnes/snes/ppu/background/background.cpp +++ b/bsnes/snes/ppu/background/background.cpp @@ -6,40 +6,27 @@ void PPU::Background::frame() { } void PPU::Background::scanline() { + bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); + x = -8 << hires; + y = self.vcounter(); + edge = 7 - (regs.hoffset & 7); + for(unsigned n = 0; n < 8; n++) data[n] = 0; + if(self.vcounter() == 1) { - t.mosaic_vcounter = regs.mosaic + 1; - t.mosaic_y = 1; - } else if(--t.mosaic_vcounter == 0) { - t.mosaic_vcounter = regs.mosaic + 1; - t.mosaic_y += regs.mosaic + 1; + mosaic_vcounter = regs.mosaic + 1; + mosaic_voffset = 1; + } else if(--mosaic_vcounter == 0) { + mosaic_vcounter = regs.mosaic + 1; + mosaic_voffset += regs.mosaic + 1; } - t.mosaic_x = 0; - t.mosaic_hcounter = regs.mosaic + 1; + mosaic_hcounter = 0; + mosaic_hoffset = 0; } -void PPU::Background::run() { - if(self.vcounter() == 0) return; +void PPU::Background::get_tile() { bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); - if((self.hcounter() & 2) == 0) { - output.main.priority = 0; - output.sub.priority = 0; - } else if(hires == false) { - return; - } - - if(regs.mode == Mode::Inactive) return; - if(regs.main_enabled == false && regs.sub_enabled == false) return; - - unsigned x = t.mosaic_x; - unsigned y = t.mosaic_y; - if(--t.mosaic_hcounter == 0) { - t.mosaic_hcounter = regs.mosaic + 1; - t.mosaic_x += regs.mosaic + 1; - } - if(regs.mode == Mode::Mode7) return run_mode7(); - unsigned color_depth = (regs.mode == Mode::BPP2 ? 0 : regs.mode == Mode::BPP4 ? 1 : 2); unsigned palette_offset = (self.regs.bgmode == 0 ? (id << 5) : 0); unsigned palette_size = 2 << color_depth; @@ -47,9 +34,10 @@ void PPU::Background::run() { unsigned tiledata_index = regs.tiledata_addr >> (4 + color_depth); unsigned tile_height = (regs.tile_size == TileSize::Size8x8 ? 3 : 4); - unsigned tile_width = (!hires ? tile_height : 4); + unsigned tile_width = (!hires ? tile_height : 4); unsigned width = (!hires ? 256 : 512); + unsigned mask_x = (tile_height == 3 ? width : (width << 1)); unsigned mask_y = mask_x; if(regs.screen_size & 1) mask_x <<= 1; @@ -57,15 +45,18 @@ void PPU::Background::run() { mask_x--; mask_y--; + unsigned px = x; + unsigned py = mosaic_voffset; + unsigned hscroll = regs.hoffset; unsigned vscroll = regs.voffset; if(hires) { hscroll <<= 1; - if(self.regs.interlace) y = (y << 1) + self.field(); + if(self.regs.interlace) py = (py << 1) + self.field(); } - unsigned hoffset = hscroll + x; - unsigned voffset = vscroll + y; + unsigned hoffset = hscroll + px; + unsigned voffset = vscroll + py; if(self.regs.bgmode == 2 || self.regs.bgmode == 4 || self.regs.bgmode == 6) { uint16 opt_x = (x + (hscroll & 7)); @@ -93,60 +84,167 @@ void PPU::Background::run() { hoffset &= mask_x; voffset &= mask_y; - unsigned tile_number = get_tile(hoffset, voffset); - bool mirror_y = tile_number & 0x8000; - bool mirror_x = tile_number & 0x4000; - unsigned priority = (tile_number & 0x2000 ? regs.priority1 : regs.priority0); - unsigned palette_number = (tile_number >> 10) & 7; - unsigned palette_index = palette_offset + (palette_number << palette_size); + unsigned screen_x = (regs.screen_size & 1 ? (32 << 5) : 0); + unsigned screen_y = (regs.screen_size & 2 ? (32 << 5) : 0); + if(regs.screen_size == 3) screen_y <<= 1; - if(tile_width == 4 && (bool)(hoffset & 8) != mirror_x) tile_number += 1; - if(tile_height == 4 && (bool)(voffset & 8) != mirror_y) tile_number += 16; - tile_number &= 0x03ff; - tile_number += tiledata_index; - tile_number &= tile_mask; + unsigned tx = hoffset >> tile_width; + unsigned ty = voffset >> tile_height; + + uint16 offset = ((ty & 0x1f) << 5) + (tx & 0x1f); + if(tx & 0x20) offset += screen_x; + if(ty & 0x20) offset += screen_y; + + uint16 addr = regs.screen_addr + (offset << 1); + tile = (memory::vram[addr + 0] << 0) + (memory::vram[addr + 1] << 8); + bool mirror_y = tile & 0x8000; + bool mirror_x = tile & 0x4000; + priority = (tile & 0x2000 ? regs.priority1 : regs.priority0); + palette_number = (tile >> 10) & 7; + palette_index = palette_offset + (palette_number << palette_size); + + if(tile_width == 4 && (bool)(hoffset & 8) != mirror_x) tile += 1; + if(tile_height == 4 && (bool)(voffset & 8) != mirror_y) tile += 16; + uint16 character = ((tile & 0x03ff) + tiledata_index) & tile_mask; - if(mirror_x) hoffset ^= 7; if(mirror_y) voffset ^= 7; + offset = (character << (4 + color_depth)) + ((voffset & 7) << 1); - uint8 color = get_color(hoffset, voffset, tile_number); - if(color == 0) return; + if(regs.mode >= Mode::BPP2) { + data[0] = memory::vram[offset + 0]; + data[1] = memory::vram[offset + 1]; + } + if(regs.mode >= Mode::BPP4) { + data[2] = memory::vram[offset + 16]; + data[3] = memory::vram[offset + 17]; + } + if(regs.mode >= Mode::BPP8) { + data[4] = memory::vram[offset + 32]; + data[5] = memory::vram[offset + 33]; + data[6] = memory::vram[offset + 48]; + data[7] = memory::vram[offset + 49]; + } - color += palette_index; + if(mirror_x) for(unsigned n = 0; n < 8; n++) { + data[n] = ((data[n] >> 4) & 0x0f) | ((data[n] << 4) & 0xf0); + data[n] = ((data[n] >> 2) & 0x33) | ((data[n] << 2) & 0xcc); + data[n] = ((data[n] >> 1) & 0x55) | ((data[n] << 1) & 0xaa); + } +} + +void PPU::Background::run() { + if(self.vcounter() == 0) return; + bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); + + if((self.hcounter() & 2) == 0) { + output.main.priority = 0; + output.sub.priority = 0; + } else if(hires == false) { + return; + } + + if(regs.mode == Mode::Inactive) return; + if(regs.main_enable == false && regs.sub_enable == false) return; + + if(regs.mode == Mode::Mode7) return run_mode7(); + + if((x++ & 7) == edge) get_tile(); + + uint8 palette = get_tile_color(); + if(x >= 0 && mosaic_hcounter++ >= regs.mosaic) { + mosaic_hcounter = 0; + mosaic_palette = palette; + } + if(mosaic_palette == 0) return; if(hires == false) { - if(regs.main_enabled) { + if(regs.main_enable) { output.main.priority = priority; - output.main.palette = color; - output.main.tile = tile_number; + output.main.palette = palette_index + mosaic_palette; + output.main.tile = tile; } - if(regs.sub_enabled) { + if(regs.sub_enable) { output.sub.priority = priority; - output.sub.palette = color; - output.sub.tile = tile_number; + output.sub.palette = palette_index + mosaic_palette; + output.sub.tile = tile; } } else { if(x & 1) { - if(regs.main_enabled) { + if(regs.main_enable) { output.main.priority = priority; - output.main.palette = color; - output.main.tile = tile_number; + output.main.palette = palette_index + mosaic_palette; + output.main.tile = tile; } } else { - if(regs.sub_enabled) { + if(regs.sub_enable) { output.sub.priority = priority; - output.sub.palette = color; - output.sub.tile = tile_number; + output.sub.palette = palette_index + mosaic_palette; + output.sub.tile = tile; } } } } +unsigned PPU::Background::get_tile_color() { + unsigned color = 0; + if(regs.mode >= Mode::BPP2) { + color += (data[0] & 0x80) ? 0x01 : 0; data[0] <<= 1; + color += (data[1] & 0x80) ? 0x02 : 0; data[1] <<= 1; + } + if(regs.mode >= Mode::BPP4) { + color += (data[2] & 0x80) ? 0x04 : 0; data[2] <<= 1; + color += (data[3] & 0x80) ? 0x08 : 0; data[3] <<= 1; + } + if(regs.mode >= Mode::BPP8) { + color += (data[4] & 0x80) ? 0x10 : 0; data[4] <<= 1; + color += (data[5] & 0x80) ? 0x20 : 0; data[5] <<= 1; + color += (data[6] & 0x80) ? 0x40 : 0; data[6] <<= 1; + color += (data[7] & 0x80) ? 0x80 : 0; data[7] <<= 1; + } + return color; +} + +void PPU::Background::reset() { + regs.tiledata_addr = 0; + regs.screen_addr = 0; + regs.screen_size = 0; + regs.mosaic = 0; + regs.tile_size = 0; + regs.mode = 0; + regs.priority0 = 0; + regs.priority1 = 0; + regs.main_enable = 0; + regs.sub_enable = 0; + regs.hoffset = 0; + regs.voffset = 0; + + output.main.palette = 0; + output.main.priority = 0; + output.sub.palette = 0; + output.sub.priority = 0; + + x = 0; + y = 0; + edge = 0; + + mosaic_vcounter = 0; + mosaic_voffset = 0; + mosaic_hcounter = 0; + mosaic_hoffset = 0; + mosaic_palette = 0; + + tile = 0; + priority = 0; + palette_number = 0; + palette_index = 0; + for(unsigned n = 0; n < 8; n++) data[n] = 0; +} + unsigned PPU::Background::get_tile(unsigned x, unsigned y) { bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); unsigned tile_height = (regs.tile_size == TileSize::Size8x8 ? 3 : 4); - unsigned tile_width = (!hires ? tile_height : 4); + unsigned tile_width = (!hires ? tile_height : 4); unsigned width = (!hires ? 256 : 512); unsigned mask_x = (tile_height == 3 ? width : (width << 1)); unsigned mask_y = mask_x; @@ -162,89 +260,12 @@ unsigned PPU::Background::get_tile(unsigned x, unsigned y) { x = (x & mask_x) >> tile_width; y = (y & mask_y) >> tile_height; - uint16 pos = ((y & 0x1f) << 5) + (x & 0x1f); - if(x & 0x20) pos += screen_x; - if(y & 0x20) pos += screen_y; + uint16 offset = ((y & 0x1f) << 5) + (x & 0x1f); + if(x & 0x20) offset += screen_x; + if(y & 0x20) offset += screen_y; - uint16 addr = regs.screen_addr + (pos << 1); - return memory::vram[addr + 0] + (memory::vram[addr + 1] << 8); -} - -unsigned PPU::Background::get_color(unsigned x, unsigned y, uint16 offset) { - unsigned mask = 0x80 >> (x & 7); - - switch(regs.mode) { - case Background::Mode::BPP2: { - offset = (offset * 16) + ((y & 7) * 2); - - unsigned d0 = memory::vram[offset + 0]; - unsigned d1 = memory::vram[offset + 1]; - - return (((bool)(d0 & mask)) << 0) - + (((bool)(d1 & mask)) << 1); - } - - case Background::Mode::BPP4: { - offset = (offset * 32) + ((y & 7) * 2); - - unsigned d0 = memory::vram[offset + 0]; - unsigned d1 = memory::vram[offset + 1]; - unsigned d2 = memory::vram[offset + 16]; - unsigned d3 = memory::vram[offset + 17]; - - return (((bool)(d0 & mask)) << 0) - + (((bool)(d1 & mask)) << 1) - + (((bool)(d2 & mask)) << 2) - + (((bool)(d3 & mask)) << 3); - } - - case Background::Mode::BPP8: { - offset = (offset * 64) + ((y & 7) * 2); - - unsigned d0 = memory::vram[offset + 0]; - unsigned d1 = memory::vram[offset + 1]; - unsigned d2 = memory::vram[offset + 16]; - unsigned d3 = memory::vram[offset + 17]; - unsigned d4 = memory::vram[offset + 32]; - unsigned d5 = memory::vram[offset + 33]; - unsigned d6 = memory::vram[offset + 48]; - unsigned d7 = memory::vram[offset + 49]; - - return (((bool)(d0 & mask)) << 0) - + (((bool)(d1 & mask)) << 1) - + (((bool)(d2 & mask)) << 2) - + (((bool)(d3 & mask)) << 3) - + (((bool)(d4 & mask)) << 4) - + (((bool)(d5 & mask)) << 5) - + (((bool)(d6 & mask)) << 6) - + (((bool)(d7 & mask)) << 7); - } - }; -} - -void PPU::Background::reset() { - t.mosaic_x = 0; - t.mosaic_y = 0; - t.mosaic_hcounter = 0; - t.mosaic_vcounter = 0; - - regs.tiledata_addr = 0; - regs.screen_addr = 0; - regs.screen_size = 0; - regs.mosaic = 0; - regs.tile_size = 0; - regs.mode = 0; - regs.priority0 = 0; - regs.priority1 = 0; - regs.main_enabled = 0; - regs.sub_enabled = 0; - regs.hoffset = 0; - regs.voffset = 0; - - output.main.palette = 0; - output.main.priority = 0; - output.sub.palette = 0; - output.sub.priority = 0; + uint16 addr = regs.screen_addr + (offset << 1); + return (memory::vram[addr + 0] << 0) + (memory::vram[addr + 1] << 8); } PPU::Background::Background(PPU &self, unsigned id) : self(self), id(id) { diff --git a/bsnes/snes/ppu/background/background.hpp b/bsnes/snes/ppu/background/background.hpp index 8e2ad81b..faa24f59 100755 --- a/bsnes/snes/ppu/background/background.hpp +++ b/bsnes/snes/ppu/background/background.hpp @@ -1,6 +1,4 @@ class Background { -public: - PPU &self; struct ID { enum { BG1, BG2, BG3, BG4 }; }; unsigned id; @@ -8,14 +6,7 @@ public: struct ScreenSize { enum { Size32x32, Size32x64, Size64x32, Size64x64 }; }; struct TileSize { enum { Size8x8, Size16x16 }; }; - struct { - unsigned mosaic_x; - unsigned mosaic_y; - unsigned mosaic_hcounter; - unsigned mosaic_vcounter; - } t; - - struct { + struct Regs { unsigned tiledata_addr; unsigned screen_addr; unsigned screen_size; @@ -26,33 +17,53 @@ public: unsigned priority0; unsigned priority1; - bool main_enabled; - bool sub_enabled; + bool main_enable; + bool sub_enable; unsigned hoffset; unsigned voffset; } regs; - struct { - struct { + struct Output { + struct Pixel { unsigned priority; //0 = none (transparent) unsigned palette; unsigned tile; } main, sub; } output; + struct { + signed x; + signed y; + signed edge; + + unsigned mosaic_vcounter; + unsigned mosaic_voffset; + unsigned mosaic_hcounter; + unsigned mosaic_hoffset; + unsigned mosaic_palette; + + unsigned tile; + unsigned priority; + unsigned palette_number; + unsigned palette_index; + uint8 data[8]; + }; + void frame(); void scanline(); void run(); - unsigned get_tile(unsigned x, unsigned y); - unsigned get_color(unsigned x, unsigned y, uint16 offset); void reset(); + void get_tile(); + unsigned get_tile_color(); + unsigned get_tile(unsigned x, unsigned y); + signed clip(signed n); + void run_mode7(); + void serialize(serializer&); Background(PPU &self, unsigned id); -private: - //mode7.cpp - signed clip(signed n); - void run_mode7(); + PPU &self; + friend class PPU; }; diff --git a/bsnes/snes/ppu/background/mode7.cpp b/bsnes/snes/ppu/background/mode7.cpp index cb8dcac6..df170bab 100755 --- a/bsnes/snes/ppu/background/mode7.cpp +++ b/bsnes/snes/ppu/background/mode7.cpp @@ -16,8 +16,15 @@ void PPU::Background::run_mode7() { signed hoffset = sclip<13>(self.regs.mode7_hoffset); signed voffset = sclip<13>(self.regs.mode7_voffset); - unsigned x = t.mosaic_x; - unsigned y = self.bg1.t.mosaic_y; //BG2 vertical mosaic uses BG1 mosaic size + if(++Background::x & ~255) return; + unsigned x = mosaic_hoffset; + unsigned y = self.bg1.mosaic_voffset; //BG2 vertical mosaic uses BG1 mosaic size + + if(mosaic_hcounter++ == regs.mosaic) { + mosaic_hcounter = 0; + mosaic_hoffset += regs.mosaic + 1; + } + if(self.regs.mode7_hflip) x = 255 - x; if(self.regs.mode7_vflip) y = 255 - y; @@ -41,11 +48,12 @@ void PPU::Background::run_mode7() { py &= 1023; tile = memory::vram[((py >> 3) * 128 + (px >> 3)) << 1]; palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; - } break; + break; + } //palette color 0 outside of screen area case 2: { - if(px < 0 || px > 1023 || py < 0 || py > 1023) { + if((px | py) & ~1023) { palette = 0; } else { px &= 1023; @@ -53,11 +61,12 @@ void PPU::Background::run_mode7() { tile = memory::vram[((py >> 3) * 128 + (px >> 3)) << 1]; palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; } - } break; + break; + } //character 0 repetition outside of screen area case 3: { - if(px < 0 || px > 1023 || py < 0 || py > 1023) { + if((px & py) & ~1023) { tile = 0; } else { px &= 1023; @@ -65,7 +74,8 @@ void PPU::Background::run_mode7() { tile = memory::vram[((py >> 3) * 128 + (px >> 3)) << 1]; } palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; - } break; + break; + } } unsigned priority; @@ -78,14 +88,16 @@ void PPU::Background::run_mode7() { if(palette == 0) return; - if(regs.main_enabled) { + if(regs.main_enable) { output.main.palette = palette; output.main.priority = priority; + output.main.tile = 0; } - if(regs.sub_enabled) { + if(regs.sub_enable) { output.sub.palette = palette; output.sub.priority = priority; + output.sub.tile = 0; } } diff --git a/bsnes/snes/ppu/mmio/mmio.cpp b/bsnes/snes/ppu/mmio/mmio.cpp index 959a6d71..9490eb85 100755 --- a/bsnes/snes/ppu/mmio/mmio.cpp +++ b/bsnes/snes/ppu/mmio/mmio.cpp @@ -19,26 +19,26 @@ uint16 PPU::get_vram_address() { } uint8 PPU::vram_read(unsigned addr) { - if(regs.display_disabled || vcounter() >= (!regs.overscan ? 225 : 240)) { + if(regs.display_disable || vcounter() >= (!regs.overscan ? 225 : 240)) { return memory::vram[addr]; } return 0x00; } void PPU::vram_write(unsigned addr, uint8 data) { - if(regs.display_disabled || vcounter() >= (!regs.overscan ? 225 : 240)) { + if(regs.display_disable || vcounter() >= (!regs.overscan ? 225 : 240)) { memory::vram[addr] = data; } } uint8 PPU::oam_read(unsigned addr) { - if(!regs.display_disabled && vcounter() < (!regs.overscan ? 225 : 240)) addr = regs.ioamaddr; + if(!regs.display_disable && vcounter() < (!regs.overscan ? 225 : 240)) addr = regs.ioamaddr; if(addr & 0x0200) addr &= 0x021f; return memory::oam[addr]; } void PPU::oam_write(unsigned addr, uint8 data) { - if(!regs.display_disabled && vcounter() < (!regs.overscan ? 225 : 240)) addr = regs.ioamaddr; + if(!regs.display_disable && vcounter() < (!regs.overscan ? 225 : 240)) addr = regs.ioamaddr; if(addr & 0x0200) addr &= 0x021f; memory::oam[addr] = data; oam.update(addr, data); @@ -164,8 +164,8 @@ void PPU::mmio_update_video_mode() { //INIDISP void PPU::mmio_w2100(uint8 data) { - if(regs.display_disabled && vcounter() == (!regs.overscan ? 225 : 240)) oam.address_reset(); - regs.display_disabled = data & 0x80; + if(regs.display_disable && vcounter() == (!regs.overscan ? 225 : 240)) oam.address_reset(); + regs.display_disable = data & 0x80; regs.display_brightness = data & 0x0f; } @@ -491,20 +491,20 @@ void PPU::mmio_w212b(uint8 data) { //TM void PPU::mmio_w212c(uint8 data) { - oam.regs.main_enabled = data & 0x10; - bg4.regs.main_enabled = data & 0x08; - bg3.regs.main_enabled = data & 0x04; - bg2.regs.main_enabled = data & 0x02; - bg1.regs.main_enabled = data & 0x01; + 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; } //TS void PPU::mmio_w212d(uint8 data) { - oam.regs.sub_enabled = data & 0x10; - bg4.regs.sub_enabled = data & 0x08; - bg3.regs.sub_enabled = data & 0x04; - bg2.regs.sub_enabled = data & 0x02; - bg1.regs.sub_enabled = data & 0x01; + 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; } //TMW @@ -703,7 +703,7 @@ void PPU::mmio_reset() { regs.icgramaddr = 0; //$2100 INIDISP - regs.display_disabled = true; + regs.display_disable = true; regs.display_brightness = 0; //$2102 OAMADDL diff --git a/bsnes/snes/ppu/mmio/mmio.hpp b/bsnes/snes/ppu/mmio/mmio.hpp index 7a57af7e..26ad888d 100755 --- a/bsnes/snes/ppu/mmio/mmio.hpp +++ b/bsnes/snes/ppu/mmio/mmio.hpp @@ -15,7 +15,7 @@ struct { uint16 icgramaddr; //$2100 INIDISP - bool display_disabled; + bool display_disable; unsigned display_brightness; //$2102 OAMADDL diff --git a/bsnes/snes/ppu/ppu.cpp b/bsnes/snes/ppu/ppu.cpp index 437c654d..32e864e9 100755 --- a/bsnes/snes/ppu/ppu.cpp +++ b/bsnes/snes/ppu/ppu.cpp @@ -38,10 +38,11 @@ void PPU::enter() { } scanline(); - add_clocks(88); + add_clocks(56); if(vcounter() <= (!regs.overscan ? 224 : 239)) { - for(unsigned n = 0; n < 256; n++) { + add_clocks(4); + for(unsigned pixel = 1; pixel < 8 + 256; pixel++) { bg1.run(); bg2.run(); bg3.run(); @@ -52,19 +53,21 @@ void PPU::enter() { bg2.run(); bg3.run(); bg4.run(); - oam.run(); - window.run(); - screen.run(); + if(pixel >= 8) { + oam.run(); + window.run(); + screen.run(); + } add_clocks(2); } add_clocks(22); oam.tilefetch(); } else { - add_clocks(1024 + 22 + 136); + add_clocks(1056 + 22 + 136); } - add_clocks(lineclocks() - 88 - 1024 - 22 - 136); + add_clocks(lineclocks() - 56 - 1056 - 22 - 136); } } diff --git a/bsnes/snes/ppu/ppu.hpp b/bsnes/snes/ppu/ppu.hpp index 82b98ae6..29b2bce6 100755 --- a/bsnes/snes/ppu/ppu.hpp +++ b/bsnes/snes/ppu/ppu.hpp @@ -18,6 +18,17 @@ public: ~PPU(); private: + uint16 *surface; + uint16 *output; + + uint8 ppu1_version; + uint8 ppu2_version; + + struct { + bool interlace; + bool overscan; + } display; + #include "background/background.hpp" #include "mmio/mmio.hpp" #include "screen/screen.hpp" @@ -32,17 +43,6 @@ private: Window window; Screen screen; - uint16 *surface; - uint16 *output; - - uint8 ppu1_version; - uint8 ppu2_version; - - struct { - bool interlace; - bool overscan; - } display; - static void Enter(); void add_clocks(unsigned); diff --git a/bsnes/snes/ppu/screen/screen.cpp b/bsnes/snes/ppu/screen/screen.cpp index 0e8fb921..6506612b 100755 --- a/bsnes/snes/ppu/screen/screen.cpp +++ b/bsnes/snes/ppu/screen/screen.cpp @@ -12,10 +12,10 @@ void PPU::Screen::run() { *output++ = color; *output++ = color; } else { - color = get_pixel(true); - *output++ = color; color = get_pixel(false); *output++ = color; + color = get_pixel(true); + *output++ = color; } } @@ -146,7 +146,7 @@ uint16 PPU::Screen::get_pixel(bool swap) { //======== output = light_table[self.regs.display_brightness][output]; - if(self.regs.display_disabled) output = 0x0000; + if(self.regs.display_disable) output = 0x0000; return output; } diff --git a/bsnes/snes/ppu/screen/screen.hpp b/bsnes/snes/ppu/screen/screen.hpp index 3472dec0..1de619fe 100755 --- a/bsnes/snes/ppu/screen/screen.hpp +++ b/bsnes/snes/ppu/screen/screen.hpp @@ -1,9 +1,7 @@ class Screen { -public: - PPU &self; uint16 *output; - struct { + struct Regs { bool addsub_mode; bool direct_color; @@ -25,13 +23,15 @@ public: void run(); void reset(); - void serialize(serializer&); - Screen(PPU &self); - -private: uint16 light_table[16][32768]; uint16 get_pixel(bool swap); uint16 addsub(unsigned x, unsigned y, bool halve); uint16 get_color(unsigned palette); uint16 get_direct_color(unsigned palette, unsigned tile); + + void serialize(serializer&); + Screen(PPU &self); + + PPU &self; + friend class PPU; }; diff --git a/bsnes/snes/ppu/serialization.cpp b/bsnes/snes/ppu/serialization.cpp index b4b341bf..53f0f1b5 100755 --- a/bsnes/snes/ppu/serialization.cpp +++ b/bsnes/snes/ppu/serialization.cpp @@ -37,7 +37,7 @@ void PPU::serialize(serializer &s) { s.integer(regs.ioamaddr); s.integer(regs.icgramaddr); - s.integer(regs.display_disabled); + s.integer(regs.display_disable); s.integer(regs.display_brightness); s.integer(regs.oam_baseaddr); @@ -89,11 +89,6 @@ void PPU::serialize(serializer &s) { void PPU::Background::serialize(serializer &s) { s.integer(id); - s.integer(t.mosaic_x); - s.integer(t.mosaic_y); - s.integer(t.mosaic_hcounter); - s.integer(t.mosaic_vcounter); - s.integer(regs.tiledata_addr); s.integer(regs.screen_addr); s.integer(regs.screen_size); @@ -104,8 +99,8 @@ void PPU::Background::serialize(serializer &s) { s.integer(regs.priority0); s.integer(regs.priority1); - s.integer(regs.main_enabled); - s.integer(regs.sub_enabled); + s.integer(regs.main_enable); + s.integer(regs.sub_enable); s.integer(regs.hoffset); s.integer(regs.voffset); @@ -117,6 +112,22 @@ void PPU::Background::serialize(serializer &s) { s.integer(output.sub.priority); s.integer(output.sub.palette); s.integer(output.sub.tile); + + s.integer(x); + s.integer(y); + s.integer(edge); + + s.integer(mosaic_vcounter); + s.integer(mosaic_voffset); + s.integer(mosaic_hcounter); + s.integer(mosaic_hoffset); + s.integer(mosaic_palette); + + s.integer(tile); + s.integer(priority); + s.integer(palette_number); + s.integer(palette_index); + s.array(data); } void PPU::Sprite::serialize(serializer &s) { @@ -153,8 +164,8 @@ void PPU::Sprite::serialize(serializer &s) { } } - s.integer(regs.main_enabled); - s.integer(regs.sub_enabled); + s.integer(regs.main_enable); + s.integer(regs.sub_enable); s.integer(regs.interlace); s.integer(regs.base_size); @@ -178,8 +189,6 @@ void PPU::Sprite::serialize(serializer &s) { } void PPU::Window::serialize(serializer &s) { - s.integer(t.x); - s.integer(regs.bg1_one_enable); s.integer(regs.bg1_one_invert); s.integer(regs.bg1_two_enable); @@ -239,6 +248,9 @@ void PPU::Window::serialize(serializer &s) { s.integer(output.main.color_enable); s.integer(output.sub.color_enable); + s.integer(x); + s.integer(one); + s.integer(two); } void PPU::Screen::serialize(serializer &s) { diff --git a/bsnes/snes/ppu/sprite/sprite.cpp b/bsnes/snes/ppu/sprite/sprite.cpp index 8501eee0..a3cec2f2 100755 --- a/bsnes/snes/ppu/sprite/sprite.cpp +++ b/bsnes/snes/ppu/sprite/sprite.cpp @@ -23,7 +23,7 @@ void PPU::Sprite::scanline() { auto oam_item = t.item[t.active]; auto oam_tile = t.tile[t.active]; - if(t.y == (!self.regs.overscan ? 225 : 240) && self.regs.display_disabled == false) address_reset(); + if(t.y == (!self.regs.overscan ? 225 : 240) && self.regs.display_disable == false) address_reset(); if(t.y >= (!self.regs.overscan ? 224 : 239)) return; memset(oam_item, 0xff, 32); //default to invalid @@ -72,12 +72,12 @@ void PPU::Sprite::run() { color |= ((bool)(tile.d3 & mask)) << 3; if(color) { - if(regs.main_enabled) { + if(regs.main_enable) { output.main.palette = tile.palette + color; output.main.priority = priority_table[tile.priority]; } - if(regs.sub_enabled) { + if(regs.sub_enable) { output.sub.palette = tile.palette + color; output.sub.priority = priority_table[tile.priority]; } @@ -189,8 +189,8 @@ void PPU::Sprite::reset() { } } - regs.main_enabled = 0; - regs.sub_enabled = 0; + regs.main_enable = 0; + regs.sub_enable = 0; regs.interlace = 0; regs.base_size = 0; diff --git a/bsnes/snes/ppu/sprite/sprite.hpp b/bsnes/snes/ppu/sprite/sprite.hpp index d861e724..8383d2c9 100755 --- a/bsnes/snes/ppu/sprite/sprite.hpp +++ b/bsnes/snes/ppu/sprite/sprite.hpp @@ -1,7 +1,4 @@ class Sprite { -public: - PPU &self; - struct SpriteItem { uint16 x; uint16 y; @@ -36,9 +33,9 @@ public: TileItem tile[2][34]; } t; - struct { - bool main_enabled; - bool sub_enabled; + struct Regs { + bool main_enable; + bool sub_enable; bool interlace; uint8 base_size; @@ -55,8 +52,8 @@ public: bool range_over; } regs; - struct { - struct { + struct Output { + struct Pixel { unsigned priority; //0 = none (transparent) unsigned palette; } main, sub; @@ -73,9 +70,11 @@ public: void tilefetch(); void reset(); + bool on_scanline(SpriteItem&); + void serialize(serializer&); Sprite(PPU &self); -private: - bool on_scanline(SpriteItem&); + PPU &self; + friend class PPU; }; diff --git a/bsnes/snes/ppu/window/window.cpp b/bsnes/snes/ppu/window/window.cpp index 34966e3e..68a57a44 100755 --- a/bsnes/snes/ppu/window/window.cpp +++ b/bsnes/snes/ppu/window/window.cpp @@ -1,11 +1,14 @@ #ifdef PPU_CPP void PPU::Window::scanline() { - t.x = 0; + x = 0; } void PPU::Window::run() { bool main, sub; + one = (x >= regs.one_left && x <= regs.one_right); + two = (x >= regs.two_left && x <= regs.two_right); + x++; test( main, sub, @@ -75,8 +78,6 @@ void PPU::Window::run() { output.main.color_enable = main; output.sub.color_enable = sub; - - t.x++; } void PPU::Window::test( @@ -85,24 +86,21 @@ void PPU::Window::test( bool two_enable, bool two_invert, uint8 mask, bool main_enable, bool sub_enable ) { - unsigned x = t.x; + bool one = Window::one ^ one_invert; + bool two = Window::two ^ two_invert; bool output; if(one_enable == false && two_enable == false) { output = false; } else if(one_enable == true && two_enable == false) { - output = (x >= regs.one_left && x <= regs.one_right) ^ one_invert; + output = one; } else if(one_enable == false && two_enable == true) { - output = (x >= regs.two_left && x <= regs.two_right) ^ two_invert; - } else { - bool one = (x >= regs.one_left && x <= regs.one_right) ^ one_invert; - bool two = (x >= regs.two_left && x <= regs.two_right) ^ two_invert; - switch(mask) { - case 0: output = (one | two) == 1; break; - case 1: output = (one & two) == 1; break; - case 2: output = (one ^ two) == 1; break; - case 3: output = (one ^ two) == 0; break; - } + output = two; + } else switch(mask) { + case 0: output = (one | two) == 1; break; + case 1: output = (one & two) == 1; break; + case 2: output = (one ^ two) == 1; break; + case 3: output = (one ^ two) == 0; break; } main = main_enable ? output : false; @@ -110,7 +108,6 @@ void PPU::Window::test( } void PPU::Window::reset() { - t.x = 0; regs.bg1_one_enable = false; regs.bg1_one_invert = false; regs.bg1_two_enable = false; @@ -157,8 +154,13 @@ void PPU::Window::reset() { regs.oam_sub_enable = 0; regs.col_main_mask = 0; regs.col_sub_mask = 0; + output.main.color_enable = 0; output.sub.color_enable = 0; + + x = 0; + one = 0; + two = 0; } PPU::Window::Window(PPU &self) : self(self) { diff --git a/bsnes/snes/ppu/window/window.hpp b/bsnes/snes/ppu/window/window.hpp index 8092da21..498a2a26 100755 --- a/bsnes/snes/ppu/window/window.hpp +++ b/bsnes/snes/ppu/window/window.hpp @@ -1,11 +1,4 @@ class Window { -public: - PPU &self; - - struct { - unsigned x; - } t; - struct { bool bg1_one_enable; bool bg1_one_invert; @@ -64,24 +57,32 @@ public: uint8 col_sub_mask; } regs; - struct { - struct { + struct Output { + struct Pixel { bool color_enable; } main, sub; } output; + struct { + unsigned x; + bool one; + bool two; + }; + void scanline(); void run(); void reset(); - void serialize(serializer&); - Window(PPU &self); - -private: void test( bool &main, bool &sub, bool one_enable, bool one_invert, bool two_enable, bool two_invert, uint8 mask, bool main_enable, bool sub_enable ); + + void serialize(serializer&); + Window(PPU &self); + + PPU &self; + friend class PPU; }; diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index d5498e40..b6d0427e 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.10"; + static const char Version[] = "068.12"; static const unsigned SerializerVersion = 13; } }