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 {