From d423ae0a29bf3f9fede1219af6388d5845eb1aa9 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Fri, 13 Apr 2012 21:49:32 +1000 Subject: [PATCH] Update to v087r22 release. byuu says: Changelog: - fixed below pixel green channel on color blending - added semi-transparent objects [Exophase's method] - added full support for windows (both inputs, OBJ windows, and output, with optional color effect disable) - EEPROM uses nall::bitarray now to be friendlier to saving memory to disk - removed incomplete mosaic support for now (too broken, untested) - improved sprite priority. Hopefully it's right now. Just about everything should look great now. It took 25 days, but we finally have the BIOS rendering correctly. In order to do OBJ windows, I had to drop my above/below buffers entirely. I went with the nuclear option. There's separate layers for all BGs and objects. I build the OBJ window table during object rendering. So as a result, after rendering I go back and apply windows (and the object window that now exists.) After that, I have to do a painful Z-buffer select of the top two most important pixels. Since I now know the layers, the blending enable tests are a lot nicer, at least. But this obviously has quite a speed hit: 390fps to 325fps for Mr. Driller 2 title screen. TONC says that "bad" window coordinates do really insane things. GBAtek says it's a simple y2 < y1 || y2 > 160 ? 160 : y2; x2 < x1 || x2 > 240 ? 240 : x2; I like the GBAtek version more, so I went with that. I sure hope it's right ... but my guess is the hardware does this with a counter that wraps around or something. Also, say you have two OBJ mode 2 sprites that overlap each other, but with different priorities. The lower (more important) priority sprite has a clear pixel, but the higher priority sprite has a set pixel. Do we set the "inside OBJ window" flag to true here? Eg does the value OR, or does it hold the most important sprite's pixel value? Cydrak suspects it's OR-based, I concur from what I can see. Mosaic, I am at a loss. I really need a lot more information in order to implement it. For backgrounds, does it apply to the Vcounter of the entire screen? Or does it apply post-scroll? Or does it even apply after every adjust in affine/bitmap modes? I'm betting the hcounter background mosaic starts at the leftmost edge of the screen, and repeats previous pixels to apply the effect. Like SNES, very simple. For sprites, the SNES didn't have this. Does the mosaic grid start at (0,0) of the screen, or at (0,0) of each sprite? The latter will look a lot nicer, but be a lot more complex. Is mosaic on affine objects any different than mosaic of linear(tiled) objects? With that out of the way, we still have to fix the CPU memory access timing, add the rest of the CPU penalty cycles, the memory rotation / alignment / extend behavior needs to be fixed, the shifter desperately needs to be moved from loops to single shift operations, and I need to add flash memory support. --- bsnes/base/base.hpp | 3 +- bsnes/gba/cartridge/eeprom.cpp | 3 +- bsnes/gba/cartridge/memory.hpp | 2 +- bsnes/gba/ppu/background.cpp | 49 +++++---- bsnes/gba/ppu/mmio.cpp | 24 ++--- bsnes/gba/ppu/object.cpp | 55 +++++----- bsnes/gba/ppu/ppu.cpp | 31 ++++-- bsnes/gba/ppu/ppu.hpp | 9 +- bsnes/gba/ppu/registers.cpp | 132 ++++++++++++------------ bsnes/gba/ppu/registers.hpp | 47 ++++----- bsnes/gba/ppu/screen.cpp | 83 +++++++++------ bsnes/gba/ppu/state.hpp | 7 +- bsnes/nall/bitarray.hpp | 79 ++++++++++++++ bsnes/target-ui/interface/interface.cpp | 22 +--- bsnes/target-ui/interface/interface.hpp | 5 + 15 files changed, 328 insertions(+), 223 deletions(-) create mode 100755 bsnes/nall/bitarray.hpp diff --git a/bsnes/base/base.hpp b/bsnes/base/base.hpp index 5058a6b2..dadc3f5a 100755 --- a/bsnes/base/base.hpp +++ b/bsnes/base/base.hpp @@ -1,12 +1,13 @@ #ifndef BASE_HPP #define BASE_HPP -static const char Version[] = "087.21"; +static const char Version[] = "087.22"; #include #include #include #include +#include #include #include #include diff --git a/bsnes/gba/cartridge/eeprom.cpp b/bsnes/gba/cartridge/eeprom.cpp index 5cfefa44..efefbd92 100755 --- a/bsnes/gba/cartridge/eeprom.cpp +++ b/bsnes/gba/cartridge/eeprom.cpp @@ -56,7 +56,8 @@ void Cartridge::EEPROM::write(bool bit) { } void Cartridge::EEPROM::power() { - for(auto &bit : data) bit = 0; + data.resize(64 * 1024); + data.clear(); size = 6; mode = Mode::Wait; diff --git a/bsnes/gba/cartridge/memory.hpp b/bsnes/gba/cartridge/memory.hpp index 15d192ea..055ed475 100755 --- a/bsnes/gba/cartridge/memory.hpp +++ b/bsnes/gba/cartridge/memory.hpp @@ -1,5 +1,5 @@ struct EEPROM { - bool data[64 * 1024]; + bitarray data; unsigned size; enum class Mode : unsigned { Wait, Command, ReadAddress, ReadValidate, ReadData, WriteAddress, WriteData, WriteValidate } mode; diff --git a/bsnes/gba/ppu/background.cpp b/bsnes/gba/ppu/background.cpp index 8f2c8e6e..878cf0f5 100755 --- a/bsnes/gba/ppu/background.cpp +++ b/bsnes/gba/ppu/background.cpp @@ -1,34 +1,32 @@ void PPU::render_backgrounds() { switch(regs.control.bgmode) { case 0: - render_background_linear(3); - render_background_linear(2); - render_background_linear(1); - render_background_linear(0); + render_background_linear(regs.bg[3]); + render_background_linear(regs.bg[2]); + render_background_linear(regs.bg[1]); + render_background_linear(regs.bg[0]); break; case 1: - render_background_affine(2); - render_background_linear(1); - render_background_linear(0); + render_background_affine(regs.bg[2]); + render_background_linear(regs.bg[1]); + render_background_linear(regs.bg[0]); break; case 2: - render_background_affine(3); - render_background_affine(2); + render_background_affine(regs.bg[3]); + render_background_affine(regs.bg[2]); break; case 3: case 4: case 5: - render_background_bitmap(2); + render_background_bitmap(regs.bg[2]); break; } } -void PPU::render_background_linear(unsigned bgnumber) { - if(regs.control.enablebg[bgnumber] == false) return; - auto &bg = regs.bg[bgnumber]; - bgnumber = 1 + bgnumber; +void PPU::render_background_linear(Registers::Background &bg) { + if(regs.control.enable[bg.id] == false) return; + auto &output = layer[bg.id]; uint9 voffset = regs.vcounter + bg.voffset; uint9 hoffset = bg.hoffset; - voffset = (voffset / (1 + regs.mosaic.bgvsize)) * (1 + regs.mosaic.bgvsize); unsigned basemap = bg.control.screenbaseblock << 11; unsigned basechr = bg.control.characterbaseblock << 14; @@ -70,16 +68,15 @@ void PPU::render_background_linear(unsigned bgnumber) { uint8 color = data[px++ ^ (tile.hflip ? 7 : 0)]; if(color) { - if(bg.control.colormode == 0) draw(x, bgnumber, bg.control.priority, pram[tile.palette * 16 + color]); - if(bg.control.colormode == 1) draw(x, bgnumber, bg.control.priority, pram[color]); + if(bg.control.colormode == 0) output[x] = { true, false, bg.control.priority, pram[tile.palette * 16 + color] }; + if(bg.control.colormode == 1) output[x] = { true, false, bg.control.priority, pram[color] }; } } } -void PPU::render_background_affine(unsigned bgnumber) { - if(regs.control.enablebg[bgnumber] == false) return; - auto &bg = regs.bg[bgnumber]; - bgnumber = 1 + bgnumber; +void PPU::render_background_affine(Registers::Background &bg) { + if(regs.control.enable[bg.id] == false) return; + auto &output = layer[bg.id]; unsigned basemap = bg.control.screenbaseblock << 11; unsigned basechr = bg.control.characterbaseblock << 14; @@ -96,7 +93,7 @@ void PPU::render_background_affine(unsigned bgnumber) { if(tx < screensize && ty < screensize) { uint8 character = vram[basemap + ty * screensize + tx]; uint8 color = vram[basechr + (character * 64) + py * 8 + px]; - if(color) draw(x, bgnumber, bg.control.priority, pram[color]); + if(color) output[x] = { true, false, bg.control.priority, pram[color] }; } fx += bg.pa; @@ -107,9 +104,9 @@ void PPU::render_background_affine(unsigned bgnumber) { bg.ly += bg.pd; } -void PPU::render_background_bitmap(unsigned bgnumber) { - if(regs.control.enablebg[bgnumber] == false) return; - auto &bg = regs.bg[bgnumber]; +void PPU::render_background_bitmap(Registers::Background &bg) { + if(regs.control.enable[bg.id] == false) return; + auto &output = layer[bg.id]; uint1 depth = regs.control.bgmode != 4; //0 = 8-bit (Mode 4), 1 = 15-bit (Mode 3, Mode 5) unsigned basemap = regs.control.bgmode == 3 ? 0 : 0xa000 * regs.control.frame; @@ -132,7 +129,7 @@ void PPU::render_background_bitmap(unsigned bgnumber) { if(depth || color) { //8bpp color 0 is transparent; 15bpp color is always opaque if(depth == 0) color = pram[color]; if(depth == 1) color = color & 0x7fff; - draw(x, bgnumber, bg.control.priority, color); + output[x] = { true, false, bg.control.priority, color }; } } diff --git a/bsnes/gba/ppu/mmio.cpp b/bsnes/gba/ppu/mmio.cpp index e3e8fe1f..2b74e5d0 100755 --- a/bsnes/gba/ppu/mmio.cpp +++ b/bsnes/gba/ppu/mmio.cpp @@ -28,10 +28,10 @@ uint8 PPU::read(uint32 addr) { } //WININ - case 0x04000048: return regs.window[0].in; - case 0x04000049: return regs.window[1].in; - case 0x0400004a: return regs.window[0].out; - case 0x0400004b: return regs.windowobj.in; + case 0x04000048: return regs.windowflags[In0]; + case 0x04000049: return regs.windowflags[In1]; + case 0x0400004a: return regs.windowflags[Out]; + case 0x0400004b: return regs.windowflags[Obj]; //BLTCNT case 0x04000050: return regs.blend.control >> 0; @@ -50,14 +50,14 @@ void PPU::write(uint32 addr, uint8 byte) { case 0x04000001: regs.control = (regs.control & 0x00ff) | (byte << 8); return; //GRSWP - case 0x04000002: regs.greenswap = byte & 0x01; return; + case 0x04000002: regs.greenswap = byte >> 0; return; case 0x04000003: return; //DISPSTAT case 0x04000004: - regs.status.irqvblank = byte & (1 << 3); - regs.status.irqhblank = byte & (1 << 4); - regs.status.irqvcoincidence = byte & (1 << 5); + regs.status.irqvblank = byte >> 3; + regs.status.irqhblank = byte >> 4; + regs.status.irqvcoincidence = byte >> 5; return; case 0x04000005: regs.status.vcompare = byte; @@ -167,12 +167,12 @@ void PPU::write(uint32 addr, uint8 byte) { case 0x04000047: regs.window[1].y1 = byte; return; //WININ - case 0x04000048: regs.window[0].in = byte; return; - case 0x04000049: regs.window[1].in = byte; return; + case 0x04000048: regs.windowflags[In0] = byte; return; + case 0x04000049: regs.windowflags[In1] = byte; return; //WINOUT - case 0x0400004a: regs.window[0].out = regs.window[1].out = byte; return; - case 0x0400004b: regs.windowobj.in = byte; return; + case 0x0400004a: regs.windowflags[Out] = byte; return; + case 0x0400004b: regs.windowflags[Obj] = byte; return; //MOSAIC case 0x0400004c: diff --git a/bsnes/gba/ppu/object.cpp b/bsnes/gba/ppu/object.cpp index 7cfbe1af..e029ffc7 100755 --- a/bsnes/gba/ppu/object.cpp +++ b/bsnes/gba/ppu/object.cpp @@ -1,7 +1,7 @@ void PPU::render_objects() { - if(regs.control.enableobj == false) return; + if(regs.control.enable[OBJ] == false) return; - for(signed n = 127; n >= 0; n--) { + for(unsigned n = 0; n < 128; n++) { auto &obj = object[n]; uint8 py = regs.vcounter - obj.y; if(py >= obj.height << obj.affinesize) continue; //offscreen @@ -13,34 +13,26 @@ void PPU::render_objects() { } void PPU::render_object_linear(Object &obj) { + auto &output = layer[OBJ]; uint8 py = regs.vcounter - obj.y; if(obj.vflip) py ^= obj.height - 1; - py = (py / (1 + regs.mosaic.objvsize)) * (1 + regs.mosaic.objvsize); unsigned rowsize = regs.control.objmapping == 0 ? 32 >> obj.colors : obj.width / 8; unsigned baseaddr = 0x10000 + obj.character * 32; uint9 sx = obj.x; for(unsigned x = 0; x < obj.width; x++, sx++) { - if(sx >= 240) continue; unsigned px = x; if(obj.hflip) px ^= obj.width - 1; - unsigned offset = (py / 8) * rowsize + (px / 8); - if(obj.colors == 0) offset = baseaddr + offset * 32 + (py & 7) * 4 + (px & 7) / 2; - if(obj.colors == 1) offset = baseaddr + offset * 64 + (py & 7) * 8 + (px & 7); - - uint8 color = vram[offset]; - if(obj.colors == 0) color = (px & 1) ? color >> 4 : color & 15; - - if(color) { - if(obj.colors == 0) draw(sx, 0, obj.priority, pram[256 + obj.palette * 16 + color]); - if(obj.colors == 1) draw(sx, 0, obj.priority, pram[256 + color]); + if(sx < 240) { + render_object_pixel(obj, sx, px, py, rowsize, baseaddr); } } } void PPU::render_object_affine(Object &obj) { + auto &output = layer[OBJ]; uint8 py = regs.vcounter - obj.y; unsigned rowsize = regs.control.objmapping == 0 ? 32 >> obj.colors : obj.width / 8; @@ -68,20 +60,33 @@ void PPU::render_object_affine(Object &obj) { unsigned py = (fy >> 8) + centery; if(sx < 240 && px < obj.width && py < obj.height) { - unsigned offset = (py / 8) * rowsize + (px / 8); - if(obj.colors == 0) offset = baseaddr + offset * 32 + (py & 7) * 4 + (px & 7) / 2; - if(obj.colors == 1) offset = baseaddr + offset * 64 + (py & 7) * 8 + (px & 7); - - uint8 color = vram[offset]; - if(obj.colors == 0) color = (px & 1) ? color >> 4 : color & 15; - - if(color) { - if(obj.colors == 0) draw(sx, 0, obj.priority, pram[256 + obj.palette * 16 + color]); - if(obj.colors == 1) draw(sx, 0, obj.priority, pram[256 + color]); - } + render_object_pixel(obj, sx, px, py, rowsize, baseaddr); } fx += pa; fy += pc; } } + +void PPU::render_object_pixel(Object &obj, unsigned x, unsigned px, unsigned py, unsigned rowsize, unsigned baseaddr) { + auto &output = layer[OBJ]; + + unsigned offset = (py / 8) * rowsize + (px / 8); + if(obj.colors == 0) offset = baseaddr + offset * 32 + (py & 7) * 4 + (px & 7) / 2; + if(obj.colors == 1) offset = baseaddr + offset * 64 + (py & 7) * 8 + (px & 7); + + uint8 color = vram[offset]; + if(obj.colors == 0) color = (px & 1) ? color >> 4 : color & 15; + + if(color == 0) return; //transparent + + if(obj.mode == 2) { + windowmask[Obj][x] = true; + return; + } + + if(output[x].enable == false || obj.priority < output[x].priority) { + if(obj.colors == 0) output[x] = { true, obj.mode == 1, obj.priority, pram[256 + obj.palette * 16 + color] }; + if(obj.colors == 1) output[x] = { true, obj.mode == 1, obj.priority, pram[256 + color] }; + } +} diff --git a/bsnes/gba/ppu/ppu.cpp b/bsnes/gba/ppu/ppu.cpp index 5c9ef9a1..12dd1445 100755 --- a/bsnes/gba/ppu/ppu.cpp +++ b/bsnes/gba/ppu/ppu.cpp @@ -63,10 +63,10 @@ void PPU::power() { w.x2 = 0; w.y1 = 0; w.y2 = 0; - w.in = 0; - w.out = 0; } - regs.windowobj.in = 0; + for(auto &f : regs.windowflags) { + f = 0; + } regs.mosaic.bghsize = 0; regs.mosaic.bgvsize = 0; regs.mosaic.objhsize = 0; @@ -103,16 +103,24 @@ void PPU::scanline() { } if(regs.vcounter < 160) { - for(unsigned x = 0; x < 240; x++) { - above[x] = { (3 << 3) | 5, pram[0] }; - below[x] = { (3 << 3) | 5, pram[0] }; - } - if(regs.control.forceblank) { render_forceblank(); } else { - render_backgrounds(); + for(unsigned x = 0; x < 240; x++) { + windowmask[0][x] = false; + windowmask[1][x] = false; + windowmask[2][x] = false; + layer[OBJ][x].enable = false; + layer[BG0][x].enable = false; + layer[BG1][x].enable = false; + layer[BG2][x].enable = false; + layer[BG3][x].enable = false; + layer[SFX][x] = { true, false, 3, pram[0] }; + } + render_window(0); + render_window(1); render_objects(); + render_backgrounds(); render_screen(); } } @@ -138,6 +146,11 @@ void PPU::frame() { PPU::PPU() { output = new uint16[240 * 160]; blur = new uint16[240 * 160]; + + regs.bg[0].id = BG0; + regs.bg[1].id = BG1; + regs.bg[2].id = BG2; + regs.bg[3].id = BG3; } PPU::~PPU() { diff --git a/bsnes/gba/ppu/ppu.hpp b/bsnes/gba/ppu/ppu.hpp index 494e04b0..650d988a 100755 --- a/bsnes/gba/ppu/ppu.hpp +++ b/bsnes/gba/ppu/ppu.hpp @@ -27,17 +27,18 @@ struct PPU : Thread, MMIO { void oam_write(uint32 addr, uint32 size, uint32 word); void render_backgrounds(); - void render_background_linear(unsigned bgnumber); - void render_background_affine(unsigned bgnumber); - void render_background_bitmap(unsigned bgnumber); + void render_background_linear(Registers::Background&); + void render_background_affine(Registers::Background&); + void render_background_bitmap(Registers::Background&); void render_objects(); void render_object_linear(Object&); void render_object_affine(Object&); + void render_object_pixel(Object&, unsigned x, unsigned px, unsigned py, unsigned rowsize, unsigned baseaddr); void render_forceblank(); void render_screen(); - void draw(unsigned x, unsigned layer, unsigned priority, unsigned color); + void render_window(unsigned window); unsigned blend(unsigned above, unsigned eva, unsigned below, unsigned evb); PPU(); diff --git a/bsnes/gba/ppu/registers.cpp b/bsnes/gba/ppu/registers.cpp index 13057f9f..9908f76d 100755 --- a/bsnes/gba/ppu/registers.cpp +++ b/bsnes/gba/ppu/registers.cpp @@ -6,32 +6,32 @@ PPU::Registers::Control::operator uint16() const { | (hblank << 5) | (objmapping << 6) | (forceblank << 7) - | (enablebg[0] << 8) - | (enablebg[1] << 9) - | (enablebg[2] << 10) - | (enablebg[3] << 11) - | (enableobj << 12) - | (enablebgwindow[0] << 13) - | (enablebgwindow[1] << 14) - | (enableobjwindow << 15) + | (enable[BG0] << 8) + | (enable[BG1] << 9) + | (enable[BG2] << 10) + | (enable[BG3] << 11) + | (enable[OBJ] << 12) + | (enablewindow[In0] << 13) + | (enablewindow[In1] << 14) + | (enablewindow[Obj] << 15) ); } uint16 PPU::Registers::Control::operator=(uint16 source) { - bgmode = source & 0x0007; - cgbmode = source & 0x0008; - frame = source & 0x0010; - hblank = source & 0x0020; - objmapping = source & 0x0040; - forceblank = source & 0x0080; - enablebg[0] = source & 0x0100; - enablebg[1] = source & 0x0200; - enablebg[2] = source & 0x0400; - enablebg[3] = source & 0x0800; - enableobj = source & 0x1000; - enablebgwindow[0] = source & 0x2000; - enablebgwindow[1] = source & 0x4000; - enableobjwindow = source & 0x8000; + bgmode = source >> 0; + cgbmode = source >> 3; + frame = source >> 4; + hblank = source >> 5; + objmapping = source >> 6; + forceblank = source >> 7; + enable[BG0] = source >> 8; + enable[BG1] = source >> 9; + enable[BG2] = source >> 10; + enable[BG3] = source >> 11; + enable[OBJ] = source >> 12; + enablewindow[In0] = source >> 13; + enablewindow[In1] = source >> 14; + enablewindow[Obj] = source >> 15; return operator uint16(); } @@ -48,12 +48,12 @@ PPU::Registers::Status::operator uint16() const { } uint16 PPU::Registers::Status::operator=(uint16 source) { - vblank = source & 0x0001; - hblank = source & 0x0002; - vcoincidence = source & 0x0004; - irqvblank = source & 0x0008; - irqhblank = source & 0x0010; - irqvcoincidence = source & 0x0020; + vblank = source >> 0; + hblank = source >> 1; + vcoincidence = source >> 2; + irqvblank = source >> 3; + irqhblank = source >> 4; + irqvcoincidence = source >> 5; vcompare = source >> 8; return operator uint16(); } @@ -83,56 +83,56 @@ uint16 PPU::Registers::BackgroundControl::operator=(uint16 source) { PPU::Registers::WindowFlags::operator uint8() const { return ( - (enablebg[0] << 0) - | (enablebg[1] << 1) - | (enablebg[2] << 2) - | (enablebg[3] << 3) - | (enableobj << 4) - | (enablesfx << 5) + (enable[BG0] << 0) + | (enable[BG1] << 1) + | (enable[BG2] << 2) + | (enable[BG3] << 3) + | (enable[OBJ] << 4) + | (enable[SFX] << 5) ); } uint8 PPU::Registers::WindowFlags::operator=(uint8 source) { - enablebg[0] = source & 0x01; - enablebg[1] = source & 0x02; - enablebg[2] = source & 0x04; - enablebg[3] = source & 0x08; - enableobj = source & 0x10; - enablesfx = source & 0x20; + enable[BG0] = source >> 0; + enable[BG1] = source >> 1; + enable[BG2] = source >> 2; + enable[BG3] = source >> 3; + enable[OBJ] = source >> 4; + enable[SFX] = source >> 5; return operator uint8(); } PPU::Registers::BlendControl::operator uint16() const { return ( - (above[1] << 0) - | (above[2] << 1) - | (above[3] << 2) - | (above[4] << 3) - | (above[0] << 4) - | (above[5] << 5) - | (mode << 6) - | (below[1] << 8) - | (below[2] << 9) - | (below[3] << 10) - | (below[4] << 11) - | (below[0] << 12) - | (below[5] << 13) + (above[BG0] << 0) + | (above[BG1] << 1) + | (above[BG2] << 2) + | (above[BG3] << 3) + | (above[OBJ] << 4) + | (above[SFX] << 5) + | (mode << 6) + | (below[BG0] << 8) + | (below[BG1] << 9) + | (below[BG2] << 10) + | (below[BG3] << 11) + | (below[OBJ] << 12) + | (below[SFX] << 13) ); } uint16 PPU::Registers::BlendControl::operator=(uint16 source) { - above[1] = source & (1 << 0); - above[2] = source & (1 << 1); - above[3] = source & (1 << 2); - above[4] = source & (1 << 3); - above[0] = source & (1 << 4); - above[5] = source & (1 << 5); - mode = source >> 6; - below[1] = source & (1 << 8); - below[2] = source & (1 << 9); - below[3] = source & (1 << 10); - below[4] = source & (1 << 11); - below[0] = source & (1 << 12); - below[5] = source & (1 << 13); + above[BG0] = source >> 0; + above[BG1] = source >> 1; + above[BG2] = source >> 2; + above[BG3] = source >> 3; + above[OBJ] = source >> 4; + above[SFX] = source >> 5; + mode = source >> 6; + below[BG0] = source >> 8; + below[BG1] = source >> 9; + below[BG2] = source >> 10; + below[BG3] = source >> 11; + below[OBJ] = source >> 12; + below[SFX] = source >> 13; return operator uint16(); } diff --git a/bsnes/gba/ppu/registers.hpp b/bsnes/gba/ppu/registers.hpp index 03cc08fd..7ce05b5e 100755 --- a/bsnes/gba/ppu/registers.hpp +++ b/bsnes/gba/ppu/registers.hpp @@ -1,30 +1,31 @@ +enum : unsigned { OBJ = 0, BG0 = 1, BG1 = 2, BG2 = 3, BG3 = 4, SFX = 5 }; +enum : unsigned { In0 = 0, In1 = 1, Obj = 2, Out = 3 }; + struct Registers { struct Control { uint3 bgmode; - bool cgbmode; - bool frame; - bool hblank; - bool objmapping; - bool forceblank; - bool enablebg[4]; - bool enableobj; - bool enablebgwindow[2]; - bool enableobjwindow; + uint1 cgbmode; + uint1 frame; + uint1 hblank; + uint1 objmapping; + uint1 forceblank; + uint1 enable[5]; + uint1 enablewindow[3]; operator uint16() const; uint16 operator=(uint16 source); Control& operator=(const Control&) = delete; } control; - bool greenswap; + uint1 greenswap; struct Status { - bool vblank; - bool hblank; - bool vcoincidence; - bool irqvblank; - bool irqhblank; - bool irqvcoincidence; + uint1 vblank; + uint1 hblank; + uint1 vcoincidence; + uint1 irqvblank; + uint1 irqhblank; + uint1 irqvcoincidence; uint8 vcompare; operator uint16() const; @@ -59,12 +60,11 @@ struct Registers { //internal int28 lx, ly; + unsigned id; } bg[4]; struct WindowFlags { - bool enablebg[4]; - bool enableobj; - bool enablesfx; + uint1 enable[6]; operator uint8() const; uint8 operator=(uint8 source); @@ -74,12 +74,9 @@ struct Registers { struct Window { uint8 x1, x2; uint8 y1, y2; - WindowFlags in, out; } window[2]; - struct ObjectWindow { - WindowFlags in; - } windowobj; + WindowFlags windowflags[4]; struct Mosaic { uint4 bghsize; @@ -89,8 +86,8 @@ struct Registers { } mosaic; struct BlendControl { - bool above[6]; - bool below[6]; + uint1 above[6]; + uint1 below[6]; uint2 mode; operator uint16() const; diff --git a/bsnes/gba/ppu/screen.cpp b/bsnes/gba/ppu/screen.cpp index 296d4da0..7867066f 100755 --- a/bsnes/gba/ppu/screen.cpp +++ b/bsnes/gba/ppu/screen.cpp @@ -12,54 +12,75 @@ void PPU::render_screen() { uint16 *last = blur + regs.vcounter * 240; for(unsigned x = 0; x < 240; x++) { - uint16 color = above[x].color; + Registers::WindowFlags flags; + flags = ~0; //enable all layers if no windows are enabled - switch(regs.blend.control.mode) { default: - case 0: //none - break; - case 1: //blend - if(regs.blend.control.above[above[x].priority & 7] && regs.blend.control.below[below[x].priority & 7]) { - color = blend(above[x].color, regs.blend.eva, below[x].color, regs.blend.evb); - } - break; - case 2: //brighten - if(regs.blend.control.above[above[x].priority & 7]) { - color = blend(above[x].color, 16 - regs.blend.evy, 0x7fff, regs.blend.evy); - } - break; - case 3: //darken - if(regs.blend.control.above[above[x].priority & 7]) { - color = blend(above[x].color, 16 - regs.blend.evy, 0x0000, regs.blend.evy); + //determine active window + if(regs.control.enablewindow[In0] || regs.control.enablewindow[In1] || regs.control.enablewindow[Obj]) { + flags = (uint8)regs.windowflags[Out]; + if(regs.control.enablewindow[Obj] && windowmask[Obj][x]) flags = (uint8)regs.windowflags[Obj]; + if(regs.control.enablewindow[In1] && windowmask[In1][x]) flags = (uint8)regs.windowflags[In1]; + if(regs.control.enablewindow[In0] && windowmask[In0][x]) flags = (uint8)regs.windowflags[In0]; + } + + //priority sorting: find topmost two pixels + unsigned a = 5, b = 5; + for(signed p = 3; p >= 0; p--) { + for(signed l = 5; l >= 0; l--) { + if(layer[l][x].enable && layer[l][x].priority == p && flags.enable[l]) { + b = a; + a = l; + } } } + auto &above = layer[a]; + auto &below = layer[b]; + bool blendabove = regs.blend.control.above[a]; + bool blendbelow = regs.blend.control.below[b]; + unsigned color = above[x].color; + + //perform blending, if needed + if(flags.enable[SFX] == false) { + } else if(above[x].translucent && blendbelow) { + color = blend(above[x].color, regs.blend.eva, below[x].color, regs.blend.evb); + } else if(regs.blend.control.mode == 1 && blendabove && blendbelow) { + color = blend(above[x].color, regs.blend.eva, below[x].color, regs.blend.evb); + } else if(regs.blend.control.mode == 2 && blendabove) { + color = blend(above[x].color, 16 - regs.blend.evy, 0x7fff, regs.blend.evy); + } else if(regs.blend.control.mode == 3 && blendabove) { + color = blend(above[x].color, 16 - regs.blend.evy, 0x0000, regs.blend.evy); + } + + //output pixel; blend with previous pixel to simulate GBA LCD blur line[x] = ((last[x] >> 1) & 0x3def) + ((color >> 1) & 0x3def); last[x] = color; } } -void PPU::draw(unsigned x, unsigned layer, unsigned priority, unsigned color) { - priority = (priority << 3) | layer; +void PPU::render_window(unsigned w) { + unsigned y = regs.vcounter; - if(priority <= above[x].priority) { - below[x] = above[x]; - above[x] = { priority, color }; - return; - } + unsigned y1 = regs.window[w].y1, y2 = regs.window[w].y2; + unsigned x1 = regs.window[w].x1, x2 = regs.window[w].x2; - if(priority <= below[x].priority) { - below[x] = { priority, color }; - return; + if(y2 < y1 || y2 > 160) y2 = 160; + if(x2 < x1 || x2 > 240) x2 = 240; + + if(y >= y1 && y < y2) { + for(unsigned x = x1; x < x2; x++) { + windowmask[w][x] = true; + } } } unsigned PPU::blend(unsigned above, unsigned eva, unsigned below, unsigned evb) { uint5 ar = above >> 0, ag = above >> 5, ab = above >> 10; - uint5 br = below >> 0, bg = below >> 6, bb = below >> 10; + uint5 br = below >> 0, bg = below >> 5, bb = below >> 10; - unsigned r = ((ar * eva) + (br * evb)) >> 4; - unsigned g = ((ag * eva) + (bg * evb)) >> 4; - unsigned b = ((ab * eva) + (bb * evb)) >> 4; + unsigned r = (ar * eva + br * evb) >> 4; + unsigned g = (ag * eva + bg * evb) >> 4; + unsigned b = (ab * eva + bb * evb) >> 4; return min(31, r) << 0 | min(31, g) << 5 | min(31, b) << 10; } diff --git a/bsnes/gba/ppu/state.hpp b/bsnes/gba/ppu/state.hpp index 5b22467b..cbbb6350 100755 --- a/bsnes/gba/ppu/state.hpp +++ b/bsnes/gba/ppu/state.hpp @@ -1,10 +1,11 @@ struct Pixel { + bool enable; + bool translucent; unsigned priority; unsigned color; -}; +} layer[6][240]; -Pixel above[240]; -Pixel below[240]; +bool windowmask[3][240]; struct Object { uint8 y; diff --git a/bsnes/nall/bitarray.hpp b/bsnes/nall/bitarray.hpp new file mode 100755 index 00000000..849690f9 --- /dev/null +++ b/bsnes/nall/bitarray.hpp @@ -0,0 +1,79 @@ +#ifndef NALL_BITARRAY_HPP +#define NALL_BITARRAY_HPP + +#include + +//statically-sized bit array +//no bounds-checking on read/write +//packed into uint8_t array (8 bits per byte) + +namespace nall { + +struct bitarray { + uint8_t *pool; + unsigned poolsize; + + uint8_t* data() { return pool; } + const uint8_t* data() const { return pool; } + unsigned size() const { return poolsize; } + unsigned bytesize() const { return (poolsize >> 3) + ((poolsize & 7) > 0); } + + void reset() { + if(pool) free(pool); + pool = nullptr; + poolsize = 0u; + } + + void resize(unsigned allocsize) { + if(allocsize == poolsize) return; + pool = (uint8_t*)realloc(pool, allocsize); + poolsize = allocsize; + } + + bool operator[](unsigned offset) const { + return pool[offset >> 3] & (0x80 >> (offset & 7)); + } + + void set() { + memset(pool, 0xff, (poolsize >> 3) + ((poolsize & 7) > 0)); + } + + void set(unsigned offset) { + pool[offset >> 3] |= 0x80 >> (offset & 7); + } + + void clear() { + memset(pool, 0, (poolsize >> 3) + ((poolsize & 7) > 0)); + } + + void clear(unsigned offset) { + pool[offset >> 3] &=~0x80 >> (offset & 7); + } + + void set(unsigned offset, bool data) { + data ? set(offset) : clear(offset); + } + + struct bit { + bitarray &array; + unsigned offset; + operator bool() const { return const_cast(array)[offset]; } + bit& operator=(bool data) { array.set(offset, data); return *this; } + bit& operator=(const bit& data) { return operator=((bool)data); } + bit(bitarray &array, unsigned offset) : array(array), offset(offset) {} + }; + + bit operator[](unsigned offset) { + return bit(*this, offset); + } + + bitarray() : pool(nullptr), poolsize(0u) {} + bitarray(unsigned allocsize) { + pool = (uint8_t*)malloc((allocsize >> 3) + ((allocsize & 7) > 0)); + poolsize = allocsize; + } +}; + +} + +#endif diff --git a/bsnes/target-ui/interface/interface.cpp b/bsnes/target-ui/interface/interface.cpp index cb814391..4a2346a2 100755 --- a/bsnes/target-ui/interface/interface.cpp +++ b/bsnes/target-ui/interface/interface.cpp @@ -80,12 +80,7 @@ void Interface::updateDSP() { } bool Interface::cartridgeLoaded() { - switch(mode()) { - case Mode::NES: return nes.cartridgeLoaded(); - case Mode::SNES: return snes.cartridgeLoaded(); - case Mode::GB: return gb.cartridgeLoaded(); - case Mode::GBA: return gba.cartridgeLoaded(); - } + if(core) return core->cartridgeLoaded(); return false; } @@ -125,13 +120,7 @@ void Interface::unloadCartridge() { stateManager->save(game.filename("states.bsa", ".bsa"), 0u); setCheatCodes(); - switch(mode()) { - case Mode::NES: nes.unloadCartridge(); break; - case Mode::SNES: snes.unloadCartridge(); break; - case Mode::GB: gb.unloadCartridge(); break; - case Mode::GBA: gba.unloadCartridge(); break; - } - + if(core) core->unloadCartridge(); cartridgeTitle = ""; utility->setMode(mode = Mode::None); } @@ -186,12 +175,7 @@ bool Interface::loadState(unsigned slot) { } void Interface::setCheatCodes(const lstring &list) { - switch(mode()) { - case Mode::NES: return nes.setCheats(list); - case Mode::SNES: return snes.setCheats(list); - case Mode::GB: return gb.setCheats(list); - case Mode::GBA: return gba.setCheats(list); - } + if(core) return core->setCheats(list); } string Interface::sha256() { diff --git a/bsnes/target-ui/interface/interface.hpp b/bsnes/target-ui/interface/interface.hpp index d336d2a7..39995f72 100755 --- a/bsnes/target-ui/interface/interface.hpp +++ b/bsnes/target-ui/interface/interface.hpp @@ -1,12 +1,17 @@ #include "palette.hpp" struct InterfaceCore { + virtual bool cartridgeLoaded() = 0; + virtual void unloadCartridge() = 0; + virtual void power() = 0; virtual void reset() = 0; virtual void run() = 0; virtual serializer serialize() = 0; virtual bool unserialize(serializer&) = 0; + + virtual void setCheats(const lstring &list = lstring{}) = 0; }; struct CartridgePath {