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 #ifndef BASE_HPP
#define BASE_HPP #define BASE_HPP
static const char Version[] = "087.21"; static const char Version[] = "087.22";
#include <nall/platform.hpp> #include <nall/platform.hpp>
#include <nall/algorithm.hpp> #include <nall/algorithm.hpp>
#include <nall/any.hpp> #include <nall/any.hpp>
#include <nall/array.hpp> #include <nall/array.hpp>
#include <nall/bitarray.hpp>
#include <nall/dl.hpp> #include <nall/dl.hpp>
#include <nall/dsp.hpp> #include <nall/dsp.hpp>
#include <nall/endian.hpp> #include <nall/endian.hpp>

View File

@ -56,7 +56,8 @@ void Cartridge::EEPROM::write(bool bit) {
} }
void Cartridge::EEPROM::power() { void Cartridge::EEPROM::power() {
for(auto &bit : data) bit = 0; data.resize(64 * 1024);
data.clear();
size = 6; size = 6;
mode = Mode::Wait; mode = Mode::Wait;

View File

@ -1,5 +1,5 @@
struct EEPROM { struct EEPROM {
bool data[64 * 1024]; bitarray data;
unsigned size; unsigned size;
enum class Mode : unsigned { Wait, Command, ReadAddress, ReadValidate, ReadData, WriteAddress, WriteData, WriteValidate } mode; enum class Mode : unsigned { Wait, Command, ReadAddress, ReadValidate, ReadData, WriteAddress, WriteData, WriteValidate } mode;

View File

@ -1,34 +1,32 @@
void PPU::render_backgrounds() { void PPU::render_backgrounds() {
switch(regs.control.bgmode) { switch(regs.control.bgmode) {
case 0: case 0:
render_background_linear(3); render_background_linear(regs.bg[3]);
render_background_linear(2); render_background_linear(regs.bg[2]);
render_background_linear(1); render_background_linear(regs.bg[1]);
render_background_linear(0); render_background_linear(regs.bg[0]);
break; break;
case 1: case 1:
render_background_affine(2); render_background_affine(regs.bg[2]);
render_background_linear(1); render_background_linear(regs.bg[1]);
render_background_linear(0); render_background_linear(regs.bg[0]);
break; break;
case 2: case 2:
render_background_affine(3); render_background_affine(regs.bg[3]);
render_background_affine(2); render_background_affine(regs.bg[2]);
break; break;
case 3: case 4: case 5: case 3: case 4: case 5:
render_background_bitmap(2); render_background_bitmap(regs.bg[2]);
break; break;
} }
} }
void PPU::render_background_linear(unsigned bgnumber) { void PPU::render_background_linear(Registers::Background &bg) {
if(regs.control.enablebg[bgnumber] == false) return; if(regs.control.enable[bg.id] == false) return;
auto &bg = regs.bg[bgnumber]; auto &output = layer[bg.id];
bgnumber = 1 + bgnumber;
uint9 voffset = regs.vcounter + bg.voffset; uint9 voffset = regs.vcounter + bg.voffset;
uint9 hoffset = bg.hoffset; uint9 hoffset = bg.hoffset;
voffset = (voffset / (1 + regs.mosaic.bgvsize)) * (1 + regs.mosaic.bgvsize);
unsigned basemap = bg.control.screenbaseblock << 11; unsigned basemap = bg.control.screenbaseblock << 11;
unsigned basechr = bg.control.characterbaseblock << 14; 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)]; uint8 color = data[px++ ^ (tile.hflip ? 7 : 0)];
if(color) { if(color) {
if(bg.control.colormode == 0) draw(x, bgnumber, bg.control.priority, pram[tile.palette * 16 + color]); if(bg.control.colormode == 0) output[x] = { true, false, 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 == 1) output[x] = { true, false, bg.control.priority, pram[color] };
} }
} }
} }
void PPU::render_background_affine(unsigned bgnumber) { void PPU::render_background_affine(Registers::Background &bg) {
if(regs.control.enablebg[bgnumber] == false) return; if(regs.control.enable[bg.id] == false) return;
auto &bg = regs.bg[bgnumber]; auto &output = layer[bg.id];
bgnumber = 1 + bgnumber;
unsigned basemap = bg.control.screenbaseblock << 11; unsigned basemap = bg.control.screenbaseblock << 11;
unsigned basechr = bg.control.characterbaseblock << 14; unsigned basechr = bg.control.characterbaseblock << 14;
@ -96,7 +93,7 @@ void PPU::render_background_affine(unsigned bgnumber) {
if(tx < screensize && ty < screensize) { if(tx < screensize && ty < screensize) {
uint8 character = vram[basemap + ty * screensize + tx]; uint8 character = vram[basemap + ty * screensize + tx];
uint8 color = vram[basechr + (character * 64) + py * 8 + px]; 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; fx += bg.pa;
@ -107,9 +104,9 @@ void PPU::render_background_affine(unsigned bgnumber) {
bg.ly += bg.pd; bg.ly += bg.pd;
} }
void PPU::render_background_bitmap(unsigned bgnumber) { void PPU::render_background_bitmap(Registers::Background &bg) {
if(regs.control.enablebg[bgnumber] == false) return; if(regs.control.enable[bg.id] == false) return;
auto &bg = regs.bg[bgnumber]; auto &output = layer[bg.id];
uint1 depth = regs.control.bgmode != 4; //0 = 8-bit (Mode 4), 1 = 15-bit (Mode 3, Mode 5) 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; 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 || color) { //8bpp color 0 is transparent; 15bpp color is always opaque
if(depth == 0) color = pram[color]; if(depth == 0) color = pram[color];
if(depth == 1) color = color & 0x7fff; 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 //WININ
case 0x04000048: return regs.window[0].in; case 0x04000048: return regs.windowflags[In0];
case 0x04000049: return regs.window[1].in; case 0x04000049: return regs.windowflags[In1];
case 0x0400004a: return regs.window[0].out; case 0x0400004a: return regs.windowflags[Out];
case 0x0400004b: return regs.windowobj.in; case 0x0400004b: return regs.windowflags[Obj];
//BLTCNT //BLTCNT
case 0x04000050: return regs.blend.control >> 0; 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; case 0x04000001: regs.control = (regs.control & 0x00ff) | (byte << 8); return;
//GRSWP //GRSWP
case 0x04000002: regs.greenswap = byte & 0x01; return; case 0x04000002: regs.greenswap = byte >> 0; return;
case 0x04000003: return; case 0x04000003: return;
//DISPSTAT //DISPSTAT
case 0x04000004: case 0x04000004:
regs.status.irqvblank = byte & (1 << 3); regs.status.irqvblank = byte >> 3;
regs.status.irqhblank = byte & (1 << 4); regs.status.irqhblank = byte >> 4;
regs.status.irqvcoincidence = byte & (1 << 5); regs.status.irqvcoincidence = byte >> 5;
return; return;
case 0x04000005: case 0x04000005:
regs.status.vcompare = byte; regs.status.vcompare = byte;
@ -167,12 +167,12 @@ void PPU::write(uint32 addr, uint8 byte) {
case 0x04000047: regs.window[1].y1 = byte; return; case 0x04000047: regs.window[1].y1 = byte; return;
//WININ //WININ
case 0x04000048: regs.window[0].in = byte; return; case 0x04000048: regs.windowflags[In0] = byte; return;
case 0x04000049: regs.window[1].in = byte; return; case 0x04000049: regs.windowflags[In1] = byte; return;
//WINOUT //WINOUT
case 0x0400004a: regs.window[0].out = regs.window[1].out = byte; return; case 0x0400004a: regs.windowflags[Out] = byte; return;
case 0x0400004b: regs.windowobj.in = byte; return; case 0x0400004b: regs.windowflags[Obj] = byte; return;
//MOSAIC //MOSAIC
case 0x0400004c: case 0x0400004c:

View File

@ -1,7 +1,7 @@
void PPU::render_objects() { 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]; auto &obj = object[n];
uint8 py = regs.vcounter - obj.y; uint8 py = regs.vcounter - obj.y;
if(py >= obj.height << obj.affinesize) continue; //offscreen if(py >= obj.height << obj.affinesize) continue; //offscreen
@ -13,34 +13,26 @@ void PPU::render_objects() {
} }
void PPU::render_object_linear(Object &obj) { void PPU::render_object_linear(Object &obj) {
auto &output = layer[OBJ];
uint8 py = regs.vcounter - obj.y; uint8 py = regs.vcounter - obj.y;
if(obj.vflip) py ^= obj.height - 1; 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 rowsize = regs.control.objmapping == 0 ? 32 >> obj.colors : obj.width / 8;
unsigned baseaddr = 0x10000 + obj.character * 32; unsigned baseaddr = 0x10000 + obj.character * 32;
uint9 sx = obj.x; uint9 sx = obj.x;
for(unsigned x = 0; x < obj.width; x++, sx++) { for(unsigned x = 0; x < obj.width; x++, sx++) {
if(sx >= 240) continue;
unsigned px = x; unsigned px = x;
if(obj.hflip) px ^= obj.width - 1; if(obj.hflip) px ^= obj.width - 1;
unsigned offset = (py / 8) * rowsize + (px / 8); if(sx < 240) {
if(obj.colors == 0) offset = baseaddr + offset * 32 + (py & 7) * 4 + (px & 7) / 2; render_object_pixel(obj, sx, px, py, rowsize, baseaddr);
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]);
} }
} }
} }
void PPU::render_object_affine(Object &obj) { void PPU::render_object_affine(Object &obj) {
auto &output = layer[OBJ];
uint8 py = regs.vcounter - obj.y; uint8 py = regs.vcounter - obj.y;
unsigned rowsize = regs.control.objmapping == 0 ? 32 >> obj.colors : obj.width / 8; 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; unsigned py = (fy >> 8) + centery;
if(sx < 240 && px < obj.width && py < obj.height) { if(sx < 240 && px < obj.width && py < obj.height) {
unsigned offset = (py / 8) * rowsize + (px / 8); render_object_pixel(obj, sx, px, py, rowsize, baseaddr);
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]);
}
} }
fx += pa; fx += pa;
fy += pc; 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.x2 = 0;
w.y1 = 0; w.y1 = 0;
w.y2 = 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.bghsize = 0;
regs.mosaic.bgvsize = 0; regs.mosaic.bgvsize = 0;
regs.mosaic.objhsize = 0; regs.mosaic.objhsize = 0;
@ -103,16 +103,24 @@ void PPU::scanline() {
} }
if(regs.vcounter < 160) { 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) { if(regs.control.forceblank) {
render_forceblank(); render_forceblank();
} else { } 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_objects();
render_backgrounds();
render_screen(); render_screen();
} }
} }
@ -138,6 +146,11 @@ void PPU::frame() {
PPU::PPU() { PPU::PPU() {
output = new uint16[240 * 160]; output = new uint16[240 * 160];
blur = 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() { PPU::~PPU() {

View File

@ -27,17 +27,18 @@ struct PPU : Thread, MMIO {
void oam_write(uint32 addr, uint32 size, uint32 word); void oam_write(uint32 addr, uint32 size, uint32 word);
void render_backgrounds(); void render_backgrounds();
void render_background_linear(unsigned bgnumber); void render_background_linear(Registers::Background&);
void render_background_affine(unsigned bgnumber); void render_background_affine(Registers::Background&);
void render_background_bitmap(unsigned bgnumber); void render_background_bitmap(Registers::Background&);
void render_objects(); void render_objects();
void render_object_linear(Object&); void render_object_linear(Object&);
void render_object_affine(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_forceblank();
void render_screen(); 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); unsigned blend(unsigned above, unsigned eva, unsigned below, unsigned evb);
PPU(); PPU();

View File

@ -6,32 +6,32 @@ PPU::Registers::Control::operator uint16() const {
| (hblank << 5) | (hblank << 5)
| (objmapping << 6) | (objmapping << 6)
| (forceblank << 7) | (forceblank << 7)
| (enablebg[0] << 8) | (enable[BG0] << 8)
| (enablebg[1] << 9) | (enable[BG1] << 9)
| (enablebg[2] << 10) | (enable[BG2] << 10)
| (enablebg[3] << 11) | (enable[BG3] << 11)
| (enableobj << 12) | (enable[OBJ] << 12)
| (enablebgwindow[0] << 13) | (enablewindow[In0] << 13)
| (enablebgwindow[1] << 14) | (enablewindow[In1] << 14)
| (enableobjwindow << 15) | (enablewindow[Obj] << 15)
); );
} }
uint16 PPU::Registers::Control::operator=(uint16 source) { uint16 PPU::Registers::Control::operator=(uint16 source) {
bgmode = source & 0x0007; bgmode = source >> 0;
cgbmode = source & 0x0008; cgbmode = source >> 3;
frame = source & 0x0010; frame = source >> 4;
hblank = source & 0x0020; hblank = source >> 5;
objmapping = source & 0x0040; objmapping = source >> 6;
forceblank = source & 0x0080; forceblank = source >> 7;
enablebg[0] = source & 0x0100; enable[BG0] = source >> 8;
enablebg[1] = source & 0x0200; enable[BG1] = source >> 9;
enablebg[2] = source & 0x0400; enable[BG2] = source >> 10;
enablebg[3] = source & 0x0800; enable[BG3] = source >> 11;
enableobj = source & 0x1000; enable[OBJ] = source >> 12;
enablebgwindow[0] = source & 0x2000; enablewindow[In0] = source >> 13;
enablebgwindow[1] = source & 0x4000; enablewindow[In1] = source >> 14;
enableobjwindow = source & 0x8000; enablewindow[Obj] = source >> 15;
return operator uint16(); return operator uint16();
} }
@ -48,12 +48,12 @@ PPU::Registers::Status::operator uint16() const {
} }
uint16 PPU::Registers::Status::operator=(uint16 source) { uint16 PPU::Registers::Status::operator=(uint16 source) {
vblank = source & 0x0001; vblank = source >> 0;
hblank = source & 0x0002; hblank = source >> 1;
vcoincidence = source & 0x0004; vcoincidence = source >> 2;
irqvblank = source & 0x0008; irqvblank = source >> 3;
irqhblank = source & 0x0010; irqhblank = source >> 4;
irqvcoincidence = source & 0x0020; irqvcoincidence = source >> 5;
vcompare = source >> 8; vcompare = source >> 8;
return operator uint16(); return operator uint16();
} }
@ -83,56 +83,56 @@ uint16 PPU::Registers::BackgroundControl::operator=(uint16 source) {
PPU::Registers::WindowFlags::operator uint8() const { PPU::Registers::WindowFlags::operator uint8() const {
return ( return (
(enablebg[0] << 0) (enable[BG0] << 0)
| (enablebg[1] << 1) | (enable[BG1] << 1)
| (enablebg[2] << 2) | (enable[BG2] << 2)
| (enablebg[3] << 3) | (enable[BG3] << 3)
| (enableobj << 4) | (enable[OBJ] << 4)
| (enablesfx << 5) | (enable[SFX] << 5)
); );
} }
uint8 PPU::Registers::WindowFlags::operator=(uint8 source) { uint8 PPU::Registers::WindowFlags::operator=(uint8 source) {
enablebg[0] = source & 0x01; enable[BG0] = source >> 0;
enablebg[1] = source & 0x02; enable[BG1] = source >> 1;
enablebg[2] = source & 0x04; enable[BG2] = source >> 2;
enablebg[3] = source & 0x08; enable[BG3] = source >> 3;
enableobj = source & 0x10; enable[OBJ] = source >> 4;
enablesfx = source & 0x20; enable[SFX] = source >> 5;
return operator uint8(); return operator uint8();
} }
PPU::Registers::BlendControl::operator uint16() const { PPU::Registers::BlendControl::operator uint16() const {
return ( return (
(above[1] << 0) (above[BG0] << 0)
| (above[2] << 1) | (above[BG1] << 1)
| (above[3] << 2) | (above[BG2] << 2)
| (above[4] << 3) | (above[BG3] << 3)
| (above[0] << 4) | (above[OBJ] << 4)
| (above[5] << 5) | (above[SFX] << 5)
| (mode << 6) | (mode << 6)
| (below[1] << 8) | (below[BG0] << 8)
| (below[2] << 9) | (below[BG1] << 9)
| (below[3] << 10) | (below[BG2] << 10)
| (below[4] << 11) | (below[BG3] << 11)
| (below[0] << 12) | (below[OBJ] << 12)
| (below[5] << 13) | (below[SFX] << 13)
); );
} }
uint16 PPU::Registers::BlendControl::operator=(uint16 source) { uint16 PPU::Registers::BlendControl::operator=(uint16 source) {
above[1] = source & (1 << 0); above[BG0] = source >> 0;
above[2] = source & (1 << 1); above[BG1] = source >> 1;
above[3] = source & (1 << 2); above[BG2] = source >> 2;
above[4] = source & (1 << 3); above[BG3] = source >> 3;
above[0] = source & (1 << 4); above[OBJ] = source >> 4;
above[5] = source & (1 << 5); above[SFX] = source >> 5;
mode = source >> 6; mode = source >> 6;
below[1] = source & (1 << 8); below[BG0] = source >> 8;
below[2] = source & (1 << 9); below[BG1] = source >> 9;
below[3] = source & (1 << 10); below[BG2] = source >> 10;
below[4] = source & (1 << 11); below[BG3] = source >> 11;
below[0] = source & (1 << 12); below[OBJ] = source >> 12;
below[5] = source & (1 << 13); below[SFX] = source >> 13;
return operator uint16(); 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 Registers {
struct Control { struct Control {
uint3 bgmode; uint3 bgmode;
bool cgbmode; uint1 cgbmode;
bool frame; uint1 frame;
bool hblank; uint1 hblank;
bool objmapping; uint1 objmapping;
bool forceblank; uint1 forceblank;
bool enablebg[4]; uint1 enable[5];
bool enableobj; uint1 enablewindow[3];
bool enablebgwindow[2];
bool enableobjwindow;
operator uint16() const; operator uint16() const;
uint16 operator=(uint16 source); uint16 operator=(uint16 source);
Control& operator=(const Control&) = delete; Control& operator=(const Control&) = delete;
} control; } control;
bool greenswap; uint1 greenswap;
struct Status { struct Status {
bool vblank; uint1 vblank;
bool hblank; uint1 hblank;
bool vcoincidence; uint1 vcoincidence;
bool irqvblank; uint1 irqvblank;
bool irqhblank; uint1 irqhblank;
bool irqvcoincidence; uint1 irqvcoincidence;
uint8 vcompare; uint8 vcompare;
operator uint16() const; operator uint16() const;
@ -59,12 +60,11 @@ struct Registers {
//internal //internal
int28 lx, ly; int28 lx, ly;
unsigned id;
} bg[4]; } bg[4];
struct WindowFlags { struct WindowFlags {
bool enablebg[4]; uint1 enable[6];
bool enableobj;
bool enablesfx;
operator uint8() const; operator uint8() const;
uint8 operator=(uint8 source); uint8 operator=(uint8 source);
@ -74,12 +74,9 @@ struct Registers {
struct Window { struct Window {
uint8 x1, x2; uint8 x1, x2;
uint8 y1, y2; uint8 y1, y2;
WindowFlags in, out;
} window[2]; } window[2];
struct ObjectWindow { WindowFlags windowflags[4];
WindowFlags in;
} windowobj;
struct Mosaic { struct Mosaic {
uint4 bghsize; uint4 bghsize;
@ -89,8 +86,8 @@ struct Registers {
} mosaic; } mosaic;
struct BlendControl { struct BlendControl {
bool above[6]; uint1 above[6];
bool below[6]; uint1 below[6];
uint2 mode; uint2 mode;
operator uint16() const; operator uint16() const;

View File

@ -12,54 +12,75 @@ void PPU::render_screen() {
uint16 *last = blur + regs.vcounter * 240; uint16 *last = blur + regs.vcounter * 240;
for(unsigned x = 0; x < 240; x++) { 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: //determine active window
case 0: //none if(regs.control.enablewindow[In0] || regs.control.enablewindow[In1] || regs.control.enablewindow[Obj]) {
break; flags = (uint8)regs.windowflags[Out];
case 1: //blend if(regs.control.enablewindow[Obj] && windowmask[Obj][x]) flags = (uint8)regs.windowflags[Obj];
if(regs.blend.control.above[above[x].priority & 7] && regs.blend.control.below[below[x].priority & 7]) { if(regs.control.enablewindow[In1] && windowmask[In1][x]) flags = (uint8)regs.windowflags[In1];
color = blend(above[x].color, regs.blend.eva, below[x].color, regs.blend.evb); if(regs.control.enablewindow[In0] && windowmask[In0][x]) flags = (uint8)regs.windowflags[In0];
} }
break;
case 2: //brighten //priority sorting: find topmost two pixels
if(regs.blend.control.above[above[x].priority & 7]) { unsigned a = 5, b = 5;
color = blend(above[x].color, 16 - regs.blend.evy, 0x7fff, regs.blend.evy); for(signed p = 3; p >= 0; p--) {
} for(signed l = 5; l >= 0; l--) {
break; if(layer[l][x].enable && layer[l][x].priority == p && flags.enable[l]) {
case 3: //darken b = a;
if(regs.blend.control.above[above[x].priority & 7]) { a = l;
color = blend(above[x].color, 16 - regs.blend.evy, 0x0000, regs.blend.evy); }
} }
} }
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); line[x] = ((last[x] >> 1) & 0x3def) + ((color >> 1) & 0x3def);
last[x] = color; last[x] = color;
} }
} }
void PPU::draw(unsigned x, unsigned layer, unsigned priority, unsigned color) { void PPU::render_window(unsigned w) {
priority = (priority << 3) | layer; unsigned y = regs.vcounter;
if(priority <= above[x].priority) { unsigned y1 = regs.window[w].y1, y2 = regs.window[w].y2;
below[x] = above[x]; unsigned x1 = regs.window[w].x1, x2 = regs.window[w].x2;
above[x] = { priority, color };
return;
}
if(priority <= below[x].priority) { if(y2 < y1 || y2 > 160) y2 = 160;
below[x] = { priority, color }; if(x2 < x1 || x2 > 240) x2 = 240;
return;
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) { unsigned PPU::blend(unsigned above, unsigned eva, unsigned below, unsigned evb) {
uint5 ar = above >> 0, ag = above >> 5, ab = above >> 10; 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 r = (ar * eva + br * evb) >> 4;
unsigned g = ((ag * eva) + (bg * evb)) >> 4; unsigned g = (ag * eva + bg * evb) >> 4;
unsigned b = ((ab * eva) + (bb * evb)) >> 4; unsigned b = (ab * eva + bb * evb) >> 4;
return min(31, r) << 0 | min(31, g) << 5 | min(31, b) << 10; return min(31, r) << 0 | min(31, g) << 5 | min(31, b) << 10;
} }

View File

@ -1,10 +1,11 @@
struct Pixel { struct Pixel {
bool enable;
bool translucent;
unsigned priority; unsigned priority;
unsigned color; unsigned color;
}; } layer[6][240];
Pixel above[240]; bool windowmask[3][240];
Pixel below[240];
struct Object { struct Object {
uint8 y; 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() { bool Interface::cartridgeLoaded() {
switch(mode()) { if(core) return core->cartridgeLoaded();
case Mode::NES: return nes.cartridgeLoaded();
case Mode::SNES: return snes.cartridgeLoaded();
case Mode::GB: return gb.cartridgeLoaded();
case Mode::GBA: return gba.cartridgeLoaded();
}
return false; return false;
} }
@ -125,13 +120,7 @@ void Interface::unloadCartridge() {
stateManager->save(game.filename("states.bsa", ".bsa"), 0u); stateManager->save(game.filename("states.bsa", ".bsa"), 0u);
setCheatCodes(); setCheatCodes();
switch(mode()) { if(core) core->unloadCartridge();
case Mode::NES: nes.unloadCartridge(); break;
case Mode::SNES: snes.unloadCartridge(); break;
case Mode::GB: gb.unloadCartridge(); break;
case Mode::GBA: gba.unloadCartridge(); break;
}
cartridgeTitle = ""; cartridgeTitle = "";
utility->setMode(mode = Mode::None); utility->setMode(mode = Mode::None);
} }
@ -186,12 +175,7 @@ bool Interface::loadState(unsigned slot) {
} }
void Interface::setCheatCodes(const lstring &list) { void Interface::setCheatCodes(const lstring &list) {
switch(mode()) { if(core) return core->setCheats(list);
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);
}
} }
string Interface::sha256() { string Interface::sha256() {

View File

@ -1,12 +1,17 @@
#include "palette.hpp" #include "palette.hpp"
struct InterfaceCore { struct InterfaceCore {
virtual bool cartridgeLoaded() = 0;
virtual void unloadCartridge() = 0;
virtual void power() = 0; virtual void power() = 0;
virtual void reset() = 0; virtual void reset() = 0;
virtual void run() = 0; virtual void run() = 0;
virtual serializer serialize() = 0; virtual serializer serialize() = 0;
virtual bool unserialize(serializer&) = 0; virtual bool unserialize(serializer&) = 0;
virtual void setCheats(const lstring &list = lstring{}) = 0;
}; };
struct CartridgePath { struct CartridgePath {