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.
This commit is contained in:
Tim Allen 2012-04-13 21:49:32 +10:00
parent 303a0a67d0
commit d423ae0a29
15 changed files with 328 additions and 223 deletions

View File

@ -1,12 +1,13 @@
#ifndef BASE_HPP
#define BASE_HPP
static const char Version[] = "087.21";
static const char Version[] = "087.22";
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>
#include <nall/any.hpp>
#include <nall/array.hpp>
#include <nall/bitarray.hpp>
#include <nall/dl.hpp>
#include <nall/dsp.hpp>
#include <nall/endian.hpp>

View File

@ -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;

View File

@ -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;

View File

@ -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 };
}
}

View File

@ -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:

View File

@ -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] };
}
}

View File

@ -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() {

View File

@ -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();

View File

@ -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();
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

79
bsnes/nall/bitarray.hpp Executable file
View File

@ -0,0 +1,79 @@
#ifndef NALL_BITARRAY_HPP
#define NALL_BITARRAY_HPP
#include <nall/stdint.hpp>
//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<const bitarray&>(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

View File

@ -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() {

View File

@ -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 {