mirror of https://github.com/bsnes-emu/bsnes.git
Update to v093r08 release.
byuu says: Changelog: - Game Boy and Game Boy Color now have a weak hipass filter to remove DC bias (or whatever) - Game Boy and Game Boy Color now have cycle-based PPU renderers instead of scanline-based renderers - improved Game Boy color emulation palette contrast - fixed GTK+ ListView selection bug - fixed a typo when saving states (should say "Saved to slot N", not "Save to slot N")
This commit is contained in:
parent
0f78acffd7
commit
1361820dd8
|
@ -3,7 +3,7 @@
|
|||
|
||||
namespace Emulator {
|
||||
static const char Name[] = "higan";
|
||||
static const char Version[] = "093.07";
|
||||
static const char Version[] = "093.08";
|
||||
static const char Author[] = "byuu";
|
||||
static const char License[] = "GPLv3";
|
||||
static const char Website[] = "http://byuu.org/";
|
||||
|
|
|
@ -46,6 +46,10 @@ void APU::main() {
|
|||
noise.run();
|
||||
master.run();
|
||||
|
||||
hipass(master.center, master.center_bias);
|
||||
hipass(master.left, master.left_bias);
|
||||
hipass(master.right, master.right_bias);
|
||||
|
||||
interface->audioSample(master.left, master.right);
|
||||
|
||||
clock += cpu.frequency;
|
||||
|
@ -53,6 +57,11 @@ void APU::main() {
|
|||
}
|
||||
}
|
||||
|
||||
void APU::hipass(int16& sample, int64& bias) {
|
||||
bias += ((((int64)sample << 16) - (bias >> 16)) * 57593) >> 16;
|
||||
sample = sclamp<16>(sample - (bias >> 32));
|
||||
}
|
||||
|
||||
void APU::power() {
|
||||
create(Main, 2 * 1024 * 1024);
|
||||
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
|
||||
|
|
|
@ -17,6 +17,7 @@ struct APU : Thread, MMIO {
|
|||
|
||||
static void Main();
|
||||
void main();
|
||||
void hipass(int16& sample, int64& bias);
|
||||
void power();
|
||||
|
||||
uint8 mmio_read(uint16 addr);
|
||||
|
|
|
@ -5,6 +5,8 @@ void APU::Master::run() {
|
|||
center = 0;
|
||||
left = 0;
|
||||
right = 0;
|
||||
|
||||
center_bias = left_bias = right_bias = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -81,6 +83,10 @@ void APU::Master::power() {
|
|||
center = 0;
|
||||
left = 0;
|
||||
right = 0;
|
||||
|
||||
center_bias = 0;
|
||||
left_bias = 0;
|
||||
right_bias = 0;
|
||||
}
|
||||
|
||||
void APU::Master::serialize(serializer& s) {
|
||||
|
@ -101,6 +107,10 @@ void APU::Master::serialize(serializer& s) {
|
|||
s.integer(center);
|
||||
s.integer(left);
|
||||
s.integer(right);
|
||||
|
||||
s.integer(center_bias);
|
||||
s.integer(left_bias);
|
||||
s.integer(right_bias);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,6 +17,10 @@ struct Master {
|
|||
int16 left;
|
||||
int16 right;
|
||||
|
||||
int64 center_bias;
|
||||
int64 left_bias;
|
||||
int64 right_bias;
|
||||
|
||||
void run();
|
||||
void write(unsigned r, uint8 data);
|
||||
void power();
|
||||
|
|
248
gb/ppu/cgb.cpp
248
gb/ppu/cgb.cpp
|
@ -1,34 +1,24 @@
|
|||
#ifdef PPU_CPP
|
||||
|
||||
void PPU::cgb_render() {
|
||||
for(auto& pixel : pixels) {
|
||||
pixel.color = 0x7fff;
|
||||
pixel.palette = 0;
|
||||
pixel.origin = Pixel::Origin::None;
|
||||
}
|
||||
|
||||
if(status.display_enable) {
|
||||
cgb_render_bg();
|
||||
if(status.window_display_enable) cgb_render_window();
|
||||
if(status.ob_enable) cgb_render_ob();
|
||||
}
|
||||
|
||||
uint32* output = screen + status.ly * 160;
|
||||
for(unsigned n = 0; n < 160; n++) output[n] = video.palette[pixels[n].color];
|
||||
interface->lcdScanline();
|
||||
}
|
||||
|
||||
//Attributes:
|
||||
//BG attributes:
|
||||
//0x80: 0 = OAM priority, 1 = BG priority
|
||||
//0x40: vertical flip
|
||||
//0x20: horizontal flip
|
||||
//0x08: VRAM bank#
|
||||
//0x07: palette#
|
||||
void PPU::cgb_read_tile(bool select, unsigned x, unsigned y, unsigned& tile, unsigned& attr, unsigned& data) {
|
||||
|
||||
//OB attributes:
|
||||
//0x80: 0 = OBJ above BG, 1 = BG above OBJ
|
||||
//0x40: vertical flip
|
||||
//0x20: horizontal flip
|
||||
//0x08: VRAM bank#
|
||||
//0x07: palette#
|
||||
|
||||
void PPU::cgb_read_tile(bool select, unsigned x, unsigned y, unsigned& attr, unsigned& data) {
|
||||
unsigned tmaddr = 0x1800 + (select << 10);
|
||||
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
|
||||
|
||||
tile = vram[0x0000 + tmaddr];
|
||||
unsigned tile = vram[0x0000 + tmaddr];
|
||||
attr = vram[0x2000 + tmaddr];
|
||||
|
||||
unsigned tdaddr = attr & 0x08 ? 0x2000 : 0x0000;
|
||||
|
@ -47,127 +37,127 @@ void PPU::cgb_read_tile(bool select, unsigned x, unsigned y, unsigned& tile, uns
|
|||
if(attr & 0x20) data = hflip(data);
|
||||
}
|
||||
|
||||
void PPU::cgb_render_bg() {
|
||||
unsigned iy = (status.ly + status.scy) & 255;
|
||||
unsigned ix = status.scx, tx = ix & 7;
|
||||
void PPU::cgb_scanline() {
|
||||
px = 0;
|
||||
|
||||
unsigned tile, attr, data;
|
||||
cgb_read_tile(status.bg_tilemap_select, ix, iy, tile, attr, data);
|
||||
|
||||
for(unsigned ox = 0; ox < 160; ox++) {
|
||||
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
unsigned palette = ((attr & 0x07) << 2) + index;
|
||||
unsigned color = 0;
|
||||
color |= bgpd[(palette << 1) + 0] << 0;
|
||||
color |= bgpd[(palette << 1) + 1] << 8;
|
||||
color &= 0x7fff;
|
||||
|
||||
pixels[ox].color = color;
|
||||
pixels[ox].palette = index;
|
||||
pixels[ox].origin = (attr & 0x80 ? Pixel::Origin::BGP : Pixel::Origin::BG);
|
||||
|
||||
ix = (ix + 1) & 255;
|
||||
tx = (tx + 1) & 7;
|
||||
if(tx == 0) cgb_read_tile(status.bg_tilemap_select, ix, iy, tile, attr, data);
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::cgb_render_window() {
|
||||
if(status.ly - status.wy >= 144u) return;
|
||||
if(status.wx >= 167u) return;
|
||||
unsigned iy = status.wyc++;
|
||||
unsigned ix = (7 - status.wx) & 255, tx = ix & 7;
|
||||
|
||||
unsigned tile, attr, data;
|
||||
cgb_read_tile(status.window_tilemap_select, ix, iy, tile, attr, data);
|
||||
|
||||
for(unsigned ox = 0; ox < 160; ox++) {
|
||||
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
unsigned palette = ((attr & 0x07) << 2) + index;
|
||||
unsigned color = 0;
|
||||
color |= bgpd[(palette << 1) + 0] << 0;
|
||||
color |= bgpd[(palette << 1) + 1] << 8;
|
||||
color &= 0x7fff;
|
||||
|
||||
if(ox - (status.wx - 7) < 160u) {
|
||||
pixels[ox].color = color;
|
||||
pixels[ox].palette = index;
|
||||
pixels[ox].origin = (attr & 0x80 ? Pixel::Origin::BGP : Pixel::Origin::BG);
|
||||
}
|
||||
|
||||
ix = (ix + 1) & 255;
|
||||
tx = (tx + 1) & 7;
|
||||
if(tx == 0) cgb_read_tile(status.window_tilemap_select, ix, iy, tile, attr, data);
|
||||
}
|
||||
}
|
||||
|
||||
//Attributes:
|
||||
//0x80: 0 = OBJ above BG, 1 = BG above OBJ
|
||||
//0x40: vertical flip
|
||||
//0x20: horizontal flip
|
||||
//0x08: VRAM bank#
|
||||
//0x07: palette#
|
||||
void PPU::cgb_render_ob() {
|
||||
const unsigned Height = (status.ob_size == 0 ? 8 : 16);
|
||||
unsigned sprite[10], sprites = 0;
|
||||
sprites = 0;
|
||||
|
||||
//find first ten sprites on this scanline
|
||||
for(unsigned s = 0; s < 40; s++) {
|
||||
unsigned sy = oam[(s << 2) + 0] - 16;
|
||||
unsigned sx = oam[(s << 2) + 1] - 8;
|
||||
for(unsigned n = 0; n < 40 * 4; n += 4) {
|
||||
Sprite& s = sprite[sprites];
|
||||
s.y = oam[n + 0] - 16;
|
||||
s.x = oam[n + 1] - 8;
|
||||
s.tile = oam[n + 2] & ~status.ob_size;
|
||||
s.attr = oam[n + 3];
|
||||
|
||||
sy = status.ly - sy;
|
||||
if(sy >= Height) continue;
|
||||
s.y = status.ly - s.y;
|
||||
if(s.y >= Height) continue;
|
||||
|
||||
sprite[sprites++] = s;
|
||||
if(sprites == 10) break;
|
||||
if(s.attr & 0x40) s.y ^= (Height - 1);
|
||||
unsigned tdaddr = (s.attr & 0x08 ? 0x2000 : 0x0000) + (s.tile << 4) + (s.y << 1);
|
||||
s.data = vram[tdaddr + 0] << 0;
|
||||
s.data |= vram[tdaddr + 1] << 8;
|
||||
if(s.attr & 0x20) s.data = hflip(s.data);
|
||||
|
||||
if(++sprites == 10) break;
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::cgb_run() {
|
||||
ob.color = 0;
|
||||
ob.palette = 0;
|
||||
ob.priority = 0;
|
||||
|
||||
unsigned color = 0x7fff;
|
||||
if(status.display_enable) {
|
||||
cgb_run_bg();
|
||||
if(status.window_display_enable) cgb_run_window();
|
||||
if(status.ob_enable) cgb_run_ob();
|
||||
|
||||
if(ob.palette == 0) {
|
||||
color = bg.color;
|
||||
} else if(bg.palette == 0) {
|
||||
color = ob.color;
|
||||
} else if(status.bg_enable == 0) {
|
||||
color = ob.color;
|
||||
} else if(bg.priority) {
|
||||
color = bg.color;
|
||||
} else if(ob.priority) {
|
||||
color = ob.color;
|
||||
} else {
|
||||
color = bg.color;
|
||||
}
|
||||
}
|
||||
|
||||
//render backwards, so that first sprite has highest priority
|
||||
for(signed s = sprites - 1; s >= 0; s--) {
|
||||
unsigned n = sprite[s] << 2;
|
||||
unsigned sy = oam[n + 0] - 16;
|
||||
unsigned sx = oam[n + 1] - 8;
|
||||
unsigned tile = oam[n + 2] & ~status.ob_size;
|
||||
unsigned attr = oam[n + 3];
|
||||
uint32* output = screen + status.ly * 160 + px++;
|
||||
*output = video.palette[color];
|
||||
}
|
||||
|
||||
sy = status.ly - sy;
|
||||
if(sy >= Height) continue;
|
||||
if(attr & 0x40) sy ^= (Height - 1);
|
||||
void PPU::cgb_run_bg() {
|
||||
unsigned scrolly = (status.ly + status.scy) & 255;
|
||||
unsigned scrollx = (px + status.scx) & 255;
|
||||
unsigned tx = scrollx & 7;
|
||||
if(tx == 0 || px == 0) cgb_read_tile(status.bg_tilemap_select, scrollx, scrolly, background.attr, background.data);
|
||||
|
||||
unsigned tdaddr = (attr & 0x08 ? 0x2000 : 0x0000) + (tile << 4) + (sy << 1), data = 0;
|
||||
data |= vram[tdaddr++] << 0;
|
||||
data |= vram[tdaddr++] << 8;
|
||||
if(attr & 0x20) data = hflip(data);
|
||||
unsigned index = 0;
|
||||
index |= (background.data & (0x0080 >> tx)) ? 1 : 0;
|
||||
index |= (background.data & (0x8000 >> tx)) ? 2 : 0;
|
||||
unsigned palette = ((background.attr & 0x07) << 2) + index;
|
||||
unsigned color = 0;
|
||||
color |= bgpd[(palette << 1) + 0] << 0;
|
||||
color |= bgpd[(palette << 1) + 1] << 8;
|
||||
color &= 0x7fff;
|
||||
|
||||
for(unsigned tx = 0; tx < 8; tx++) {
|
||||
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
if(index == 0) continue;
|
||||
bg.color = color;
|
||||
bg.palette = index;
|
||||
bg.priority = background.attr & 0x80;
|
||||
}
|
||||
|
||||
unsigned palette = ((attr & 0x07) << 2) + index;
|
||||
unsigned color = 0;
|
||||
color |= obpd[(palette << 1) + 0] << 0;
|
||||
color |= obpd[(palette << 1) + 1] << 8;
|
||||
color &= 0x7fff;
|
||||
void PPU::cgb_run_window() {
|
||||
if(status.ly - status.wy >= 144u) return;
|
||||
if(status.wx >= 167u) return;
|
||||
unsigned scrolly = (status.ly - status.wy) & 255;
|
||||
unsigned scrollx = (px + 7 - status.wx) & 255;
|
||||
unsigned tx = scrollx & 7;
|
||||
if(tx == 0 || px == 0) cgb_read_tile(status.window_tilemap_select, scrollx, scrolly, window.attr, window.data);
|
||||
|
||||
unsigned ox = sx + tx;
|
||||
if(ox < 160) {
|
||||
//When LCDC.D0 (BG enable) is off, OB is always rendered above BG+Window
|
||||
if(status.bg_enable) {
|
||||
if(attr & 0x80) {
|
||||
if(pixels[ox].origin == Pixel::Origin::BG || pixels[ox].origin == Pixel::Origin::BGP) {
|
||||
if(pixels[ox].palette > 0) continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
pixels[ox].color = color;
|
||||
pixels[ox].palette = index;
|
||||
pixels[ox].origin = Pixel::Origin::OB;
|
||||
}
|
||||
}
|
||||
unsigned index = 0;
|
||||
index |= (window.data & (0x0080 >> tx)) ? 1 : 0;
|
||||
index |= (window.data & (0x8000 >> tx)) ? 2 : 0;
|
||||
unsigned palette = ((window.attr & 0x07) << 2) + index;
|
||||
unsigned color = 0;
|
||||
color |= bgpd[(palette << 1) + 0] << 0;
|
||||
color |= bgpd[(palette << 1) + 1] << 8;
|
||||
color &= 0x7fff;
|
||||
|
||||
bg.color = color;
|
||||
bg.palette = index;
|
||||
bg.priority = window.attr & 0x80;
|
||||
}
|
||||
|
||||
void PPU::cgb_run_ob() {
|
||||
//render backwards, so that first sprite has priority
|
||||
for(signed n = sprites - 1; n >= 0; n--) {
|
||||
Sprite& s = sprite[n];
|
||||
|
||||
signed tx = px - s.x;
|
||||
if(tx < 0 || tx > 7) continue;
|
||||
|
||||
unsigned index = 0;
|
||||
index |= (s.data & (0x0080 >> tx)) ? 1 : 0;
|
||||
index |= (s.data & (0x8000 >> tx)) ? 2 : 0;
|
||||
if(index == 0) continue;
|
||||
|
||||
unsigned palette = ((s.attr & 0x07) << 2) + index;
|
||||
unsigned color = 0;
|
||||
color |= obpd[(palette << 1) + 0] << 0;
|
||||
color |= obpd[(palette << 1) + 1] << 8;
|
||||
color &= 0x7fff;
|
||||
|
||||
ob.color = color;
|
||||
ob.palette = index;
|
||||
ob.priority = !(s.attr & 0x80);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
213
gb/ppu/dmg.cpp
213
gb/ppu/dmg.cpp
|
@ -1,24 +1,12 @@
|
|||
#ifdef PPU_CPP
|
||||
|
||||
void PPU::dmg_render() {
|
||||
for(auto& pixel : pixels) {
|
||||
pixel.color = 0;
|
||||
pixel.palette = 0;
|
||||
pixel.origin = Pixel::Origin::None;
|
||||
}
|
||||
//OB attributes:
|
||||
//0x80: 0 = OBJ above BG, 1 = BG above OBJ
|
||||
//0x40: vertical flip
|
||||
//0x20: horizontal flip
|
||||
//0x10: palette#
|
||||
|
||||
if(status.display_enable) {
|
||||
if(status.bg_enable) dmg_render_bg();
|
||||
if(status.window_display_enable) dmg_render_window();
|
||||
if(status.ob_enable) dmg_render_ob();
|
||||
}
|
||||
|
||||
uint32* output = screen + status.ly * 160;
|
||||
for(unsigned n = 0; n < 160; n++) output[n] = video.palette[pixels[n].color];
|
||||
interface->lcdScanline();
|
||||
}
|
||||
|
||||
uint16 PPU::dmg_read_tile(bool select, unsigned x, unsigned y) {
|
||||
void PPU::dmg_read_tile(bool select, unsigned x, unsigned y, unsigned& data) {
|
||||
unsigned tmaddr = 0x1800 + (select << 10), tdaddr;
|
||||
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
|
||||
if(status.bg_tiledata_select == 0) {
|
||||
|
@ -27,117 +15,118 @@ uint16 PPU::dmg_read_tile(bool select, unsigned x, unsigned y) {
|
|||
tdaddr = 0x0000 + (vram[tmaddr] << 4);
|
||||
}
|
||||
tdaddr += (y & 7) << 1;
|
||||
return (vram[tdaddr + 0] << 0) | (vram[tdaddr + 1] << 8);
|
||||
data = vram[tdaddr + 0] << 0;
|
||||
data |= vram[tdaddr + 1] << 8;
|
||||
}
|
||||
|
||||
void PPU::dmg_render_bg() {
|
||||
unsigned iy = (status.ly + status.scy) & 255;
|
||||
unsigned ix = status.scx, tx = ix & 7;
|
||||
unsigned data = dmg_read_tile(status.bg_tilemap_select, ix, iy);
|
||||
void PPU::dmg_scanline() {
|
||||
px = 0;
|
||||
|
||||
for(unsigned ox = 0; ox < 160; ox++) {
|
||||
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
|
||||
pixels[ox].color = bgp[palette];
|
||||
pixels[ox].palette = palette;
|
||||
pixels[ox].origin = Pixel::Origin::BG;
|
||||
|
||||
ix = (ix + 1) & 255;
|
||||
tx = (tx + 1) & 7;
|
||||
|
||||
if(tx == 0) data = dmg_read_tile(status.bg_tilemap_select, ix, iy);
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::dmg_render_window() {
|
||||
if(status.ly - status.wy >= 144u) return;
|
||||
if(status.wx >= 167u) return;
|
||||
unsigned iy = status.wyc++;
|
||||
unsigned ix = (7 - status.wx) & 255, tx = ix & 7;
|
||||
unsigned data = dmg_read_tile(status.window_tilemap_select, ix, iy);
|
||||
|
||||
for(unsigned ox = 0; ox < 160; ox++) {
|
||||
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
|
||||
if(ox - (status.wx - 7) < 160u) {
|
||||
pixels[ox].color = bgp[palette];
|
||||
pixels[ox].palette = palette;
|
||||
pixels[ox].origin = Pixel::Origin::BG;
|
||||
}
|
||||
|
||||
ix = (ix + 1) & 255;
|
||||
tx = (tx + 1) & 7;
|
||||
|
||||
if(tx == 0) data = dmg_read_tile(status.window_tilemap_select, ix, iy);
|
||||
}
|
||||
}
|
||||
|
||||
//Attributes:
|
||||
//0x80: 0 = OBJ above BG, 1 = BG above OBJ
|
||||
//0x40: vertical flip
|
||||
//0x20: horizontal flip
|
||||
//0x10: palette#
|
||||
void PPU::dmg_render_ob() {
|
||||
const unsigned Height = (status.ob_size == 0 ? 8 : 16);
|
||||
unsigned sprite[10], sprites = 0;
|
||||
sprites = 0;
|
||||
|
||||
//find first ten sprites on this scanline
|
||||
for(unsigned s = 0; s < 40; s++) {
|
||||
unsigned sy = oam[(s << 2) + 0] - 16;
|
||||
unsigned sx = oam[(s << 2) + 1] - 8;
|
||||
for(unsigned n = 0; n < 40 * 4; n += 4) {
|
||||
Sprite& s = sprite[sprites];
|
||||
s.y = oam[n + 0] - 16;
|
||||
s.x = oam[n + 1] - 8;
|
||||
s.tile = oam[n + 2] & ~status.ob_size;
|
||||
s.attr = oam[n + 3];
|
||||
|
||||
sy = status.ly - sy;
|
||||
if(sy >= Height) continue;
|
||||
s.y = status.ly - s.y;
|
||||
if(s.y >= Height) continue;
|
||||
|
||||
sprite[sprites++] = s;
|
||||
if(sprites == 10) break;
|
||||
if(s.attr & 0x40) s.y ^= (Height - 1);
|
||||
unsigned tdaddr = (s.tile << 4) + (s.y << 1);
|
||||
s.data = vram[tdaddr + 0] << 0;
|
||||
s.data |= vram[tdaddr + 1] << 8;
|
||||
if(s.attr & 0x20) s.data = hflip(s.data);
|
||||
|
||||
if(++sprites == 10) break;
|
||||
}
|
||||
|
||||
//sort by X-coordinate; when equal, lower address comes first
|
||||
for(unsigned x = 0; x < sprites; x++) {
|
||||
for(unsigned y = x + 1; y < sprites; y++) {
|
||||
signed sx = oam[(sprite[x] << 2) + 1] - 8;
|
||||
signed sy = oam[(sprite[y] << 2) + 1] - 8;
|
||||
if(sy < sx) std::swap(sprite[x], sprite[y]);
|
||||
//sort by X-coordinate
|
||||
for(unsigned lo = 0; lo < sprites; lo++) {
|
||||
for(unsigned hi = lo + 1; hi < sprites; hi++) {
|
||||
if(sprite[hi].x < sprite[lo].x) std::swap(sprite[lo], sprite[hi]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::dmg_run() {
|
||||
bg.color = 0;
|
||||
bg.palette = 0;
|
||||
|
||||
ob.color = 0;
|
||||
ob.palette = 0;
|
||||
|
||||
unsigned color = 0;
|
||||
if(status.display_enable) {
|
||||
if(status.bg_enable) dmg_run_bg();
|
||||
if(status.window_display_enable) dmg_run_window();
|
||||
if(status.ob_enable) dmg_run_ob();
|
||||
|
||||
if(ob.palette == 0) {
|
||||
color = bg.color;
|
||||
} else if(bg.palette == 0) {
|
||||
color = ob.color;
|
||||
} else if(ob.priority) {
|
||||
color = ob.color;
|
||||
} else {
|
||||
color = bg.color;
|
||||
}
|
||||
}
|
||||
|
||||
//render backwards, so that first sprite has highest priority
|
||||
for(signed s = sprites - 1; s >= 0; s--) {
|
||||
unsigned n = sprite[s] << 2;
|
||||
unsigned sy = oam[n + 0] - 16;
|
||||
unsigned sx = oam[n + 1] - 8;
|
||||
unsigned tile = oam[n + 2] & ~status.ob_size;
|
||||
unsigned attr = oam[n + 3];
|
||||
uint32* output = screen + status.ly * 160 + px++;
|
||||
*output = video.palette[color];
|
||||
}
|
||||
|
||||
sy = status.ly - sy;
|
||||
if(sy >= Height) continue;
|
||||
if(attr & 0x40) sy ^= (Height - 1);
|
||||
void PPU::dmg_run_bg() {
|
||||
unsigned scrolly = (status.ly + status.scy) & 255;
|
||||
unsigned scrollx = (px + status.scx) & 255;
|
||||
unsigned tx = scrollx & 7;
|
||||
if(tx == 0 || px == 0) dmg_read_tile(status.bg_tilemap_select, scrollx, scrolly, background.data);
|
||||
|
||||
unsigned tdaddr = (tile << 4) + (sy << 1), data = 0;
|
||||
data |= vram[tdaddr++] << 0;
|
||||
data |= vram[tdaddr++] << 8;
|
||||
if(attr & 0x20) data = hflip(data);
|
||||
unsigned index = 0;
|
||||
index |= (background.data & (0x0080 >> tx)) ? 1 : 0;
|
||||
index |= (background.data & (0x8000 >> tx)) ? 2 : 0;
|
||||
|
||||
for(unsigned tx = 0; tx < 8; tx++) {
|
||||
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
|
||||
| ((data & (0x8000 >> tx)) ? 2 : 0);
|
||||
if(palette == 0) continue;
|
||||
bg.color = bgp[index];
|
||||
bg.palette = index;
|
||||
}
|
||||
|
||||
unsigned ox = sx + tx;
|
||||
if(ox < 160) {
|
||||
if(attr & 0x80) {
|
||||
if(pixels[ox].origin == Pixel::Origin::BG) {
|
||||
if(pixels[ox].palette > 0) continue;
|
||||
}
|
||||
}
|
||||
pixels[ox].color = obp[(bool)(attr & 0x10)][palette];
|
||||
pixels[ox].palette = palette;
|
||||
pixels[ox].origin = Pixel::Origin::OB;
|
||||
}
|
||||
}
|
||||
void PPU::dmg_run_window() {
|
||||
if(status.ly - status.wy >= 144u) return;
|
||||
if(status.wx >= 167u) return;
|
||||
unsigned scrolly = (status.ly - status.wy) & 255;
|
||||
unsigned scrollx = (px + 7 - status.wx) & 255;
|
||||
unsigned tx = scrollx & 7;
|
||||
if(tx == 0 || px == 0) dmg_read_tile(status.window_tilemap_select, scrollx, scrolly, window.data);
|
||||
|
||||
unsigned index = 0;
|
||||
index |= (window.data & (0x0080 >> tx)) ? 1 : 0;
|
||||
index |= (window.data & (0x8000 >> tx)) ? 2 : 0;
|
||||
|
||||
bg.color = bgp[index];
|
||||
bg.palette = index;
|
||||
}
|
||||
|
||||
void PPU::dmg_run_ob() {
|
||||
//render backwards, so that first sprite has priority
|
||||
for(signed n = sprites - 1; n >= 0; n--) {
|
||||
Sprite& s = sprite[n];
|
||||
|
||||
signed tx = px - s.x;
|
||||
if(tx < 0 || tx > 7) continue;
|
||||
|
||||
unsigned index = 0;
|
||||
index |= (s.data & (0x0080 >> tx)) ? 1 : 0;
|
||||
index |= (s.data & (0x8000 >> tx)) ? 2 : 0;
|
||||
if(index == 0) continue;
|
||||
|
||||
ob.color = obp[(bool)(s.attr & 0x10)][index];
|
||||
ob.palette = index;
|
||||
ob.priority = !(s.attr & 0x80);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,22 +25,36 @@ void PPU::main() {
|
|||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
add_clocks(4);
|
||||
status.lx += 4;
|
||||
if(status.lx >= 456) scanline();
|
||||
|
||||
if(status.display_enable && status.lx == 0) {
|
||||
//X = 0
|
||||
if(status.display_enable) {
|
||||
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||
}
|
||||
|
||||
if(status.display_enable && status.lx == 252) {
|
||||
add_clocks(92);
|
||||
|
||||
if(status.ly < 144) {
|
||||
for(unsigned n = 0; n < 160; n++) {
|
||||
system.cgb() ? cgb_run() : dmg_run();
|
||||
add_clocks(1);
|
||||
}
|
||||
} else {
|
||||
//Vblank
|
||||
add_clocks(160);
|
||||
}
|
||||
|
||||
//X = 252
|
||||
if(status.display_enable) {
|
||||
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||
cpu.hblank();
|
||||
}
|
||||
|
||||
add_clocks(204);
|
||||
scanline();
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::add_clocks(unsigned clocks) {
|
||||
status.lx += clocks;
|
||||
clock += clocks * cpu.frequency;
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
|
||||
co_switch(scheduler.active_thread = cpu.thread);
|
||||
|
@ -48,17 +62,17 @@ void PPU::add_clocks(unsigned clocks) {
|
|||
}
|
||||
|
||||
void PPU::scanline() {
|
||||
status.lx -= 456;
|
||||
status.lx = 0;
|
||||
if(++status.ly == 154) frame();
|
||||
|
||||
if(status.ly < 144) {
|
||||
system.cgb() ? cgb_scanline() : dmg_scanline();
|
||||
}
|
||||
|
||||
if(status.display_enable && status.interrupt_lyc == true) {
|
||||
if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||
}
|
||||
|
||||
if(status.ly < 144) {
|
||||
system.cgb() == false ? dmg_render() : cgb_render();
|
||||
}
|
||||
|
||||
if(status.display_enable && status.ly == 144) {
|
||||
cpu.interrupt_raise(CPU::Interrupt::Vblank);
|
||||
if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
|
||||
|
@ -69,7 +83,6 @@ void PPU::frame() {
|
|||
cpu.mmio_joyp_poll();
|
||||
|
||||
status.ly = 0;
|
||||
status.wyc = 0;
|
||||
scheduler.exit(Scheduler::ExitReason::FrameEvent);
|
||||
}
|
||||
|
||||
|
@ -106,13 +119,6 @@ void PPU::power() {
|
|||
bus.mmio[0xff6b] = this; //OBPD
|
||||
}
|
||||
|
||||
for(auto& n : screen) n = 0x0000;
|
||||
for(auto& n : pixels) {
|
||||
n.color = 0;
|
||||
n.palette = 0;
|
||||
n.origin = Pixel::Origin::None;
|
||||
}
|
||||
|
||||
for(auto& n : vram) n = 0x00;
|
||||
for(auto& n : oam) n = 0x00;
|
||||
for(auto& n : bgp) n = 0x00;
|
||||
|
@ -122,7 +128,6 @@ void PPU::power() {
|
|||
for(auto& n : obpd) n = 0x0000;
|
||||
|
||||
status.lx = 0;
|
||||
status.wyc = 0;
|
||||
|
||||
status.display_enable = 0;
|
||||
status.window_tilemap_select = 0;
|
||||
|
@ -152,6 +157,31 @@ void PPU::power() {
|
|||
|
||||
status.obpi_increment = 0;
|
||||
status.obpi = 0;
|
||||
|
||||
for(auto& n : screen) n = 0x0000;
|
||||
|
||||
bg.color = 0;
|
||||
bg.palette = 0;
|
||||
bg.priority = 0;
|
||||
|
||||
ob.color = 0;
|
||||
ob.palette = 0;
|
||||
ob.priority = 0;
|
||||
|
||||
for(auto& s : sprite) {
|
||||
s.x = 0;
|
||||
s.y = 0;
|
||||
s.tile = 0;
|
||||
s.attr = 0;
|
||||
s.data = 0;
|
||||
}
|
||||
sprites = 0;
|
||||
|
||||
background.attr = 0;
|
||||
background.data = 0;
|
||||
|
||||
window.attr = 0;
|
||||
window.data = 0;
|
||||
}
|
||||
|
||||
PPU::PPU() {
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
struct PPU : Thread, MMIO {
|
||||
uint8 vram[16384]; //GB = 8192, GBC = 16384
|
||||
uint8 oam[160];
|
||||
uint8 bgp[4];
|
||||
uint8 obp[2][4];
|
||||
uint8 bgpd[64];
|
||||
uint8 obpd[64];
|
||||
|
||||
struct Status {
|
||||
unsigned lx;
|
||||
unsigned wyc;
|
||||
|
||||
//$ff40 LCDC
|
||||
bool display_enable;
|
||||
|
@ -50,19 +56,33 @@ struct PPU : Thread, MMIO {
|
|||
} status;
|
||||
|
||||
uint32 screen[160 * 144];
|
||||
|
||||
struct Pixel {
|
||||
enum class Origin : unsigned { None, BG, BGP, OB };
|
||||
uint16 color;
|
||||
uint8 palette;
|
||||
Origin origin;
|
||||
} pixels[160];
|
||||
bool priority;
|
||||
};
|
||||
Pixel bg;
|
||||
Pixel ob;
|
||||
|
||||
uint8 vram[16384]; //GB = 8192, GBC = 16384
|
||||
uint8 oam[160];
|
||||
uint8 bgp[4];
|
||||
uint8 obp[2][4];
|
||||
uint8 bgpd[64];
|
||||
uint8 obpd[64];
|
||||
struct Sprite {
|
||||
unsigned x;
|
||||
unsigned y;
|
||||
unsigned tile;
|
||||
unsigned attr;
|
||||
unsigned data;
|
||||
};
|
||||
Sprite sprite[10];
|
||||
unsigned sprites;
|
||||
|
||||
unsigned px;
|
||||
|
||||
struct Background {
|
||||
unsigned attr;
|
||||
unsigned data;
|
||||
};
|
||||
Background background;
|
||||
Background window;
|
||||
|
||||
static void Main();
|
||||
void main();
|
||||
|
@ -78,18 +98,20 @@ struct PPU : Thread, MMIO {
|
|||
void mmio_write(uint16 addr, uint8 data);
|
||||
|
||||
//dmg.cpp
|
||||
void dmg_render();
|
||||
uint16 dmg_read_tile(bool select, unsigned x, unsigned y);
|
||||
void dmg_render_bg();
|
||||
void dmg_render_window();
|
||||
void dmg_render_ob();
|
||||
void dmg_read_tile(bool select, unsigned x, unsigned y, unsigned& data);
|
||||
void dmg_scanline();
|
||||
void dmg_run();
|
||||
void dmg_run_bg();
|
||||
void dmg_run_window();
|
||||
void dmg_run_ob();
|
||||
|
||||
//cgb.cpp
|
||||
void cgb_render();
|
||||
void cgb_read_tile(bool select, unsigned x, unsigned y, unsigned& tile, unsigned& attr, unsigned& data);
|
||||
void cgb_render_bg();
|
||||
void cgb_render_window();
|
||||
void cgb_render_ob();
|
||||
void cgb_read_tile(bool select, unsigned x, unsigned y, unsigned& attr, unsigned& data);
|
||||
void cgb_scanline();
|
||||
void cgb_run();
|
||||
void cgb_run_bg();
|
||||
void cgb_run_window();
|
||||
void cgb_run_ob();
|
||||
|
||||
void power();
|
||||
|
||||
|
|
|
@ -3,13 +3,6 @@
|
|||
void PPU::serialize(serializer& s) {
|
||||
Thread::serialize(s);
|
||||
|
||||
s.array(screen);
|
||||
for(auto& pixel : pixels) {
|
||||
s.integer(pixel.color);
|
||||
s.integer(pixel.palette);
|
||||
s.integer((unsigned&)pixel.origin);
|
||||
}
|
||||
|
||||
s.array(vram);
|
||||
s.array(oam);
|
||||
s.array(bgp);
|
||||
|
@ -19,7 +12,6 @@ void PPU::serialize(serializer& s) {
|
|||
s.array(obpd);
|
||||
|
||||
s.integer(status.lx);
|
||||
s.integer(status.wyc);
|
||||
|
||||
s.integer(status.display_enable);
|
||||
s.integer(status.window_tilemap_select);
|
||||
|
@ -51,6 +43,30 @@ void PPU::serialize(serializer& s) {
|
|||
|
||||
s.integer(status.obpi_increment);
|
||||
s.integer(status.obpi);
|
||||
|
||||
s.array(screen);
|
||||
|
||||
s.integer(bg.color);
|
||||
s.integer(bg.palette);
|
||||
s.integer(bg.priority);
|
||||
|
||||
s.integer(ob.color);
|
||||
s.integer(ob.palette);
|
||||
s.integer(ob.priority);
|
||||
|
||||
for(auto& o : sprite) {
|
||||
s.integer(o.x);
|
||||
s.integer(o.y);
|
||||
s.integer(o.tile);
|
||||
s.integer(o.attr);
|
||||
}
|
||||
s.integer(sprites);
|
||||
|
||||
s.integer(background.attr);
|
||||
s.integer(background.data);
|
||||
|
||||
s.integer(window.attr);
|
||||
s.integer(window.data);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -26,9 +26,9 @@ unsigned Video::palette_dmg(unsigned color) const {
|
|||
return interface->videoColor(color, L, L, L);
|
||||
}
|
||||
|
||||
unsigned R = monochrome[color][0] * 65535.0;
|
||||
unsigned G = monochrome[color][1] * 65535.0;
|
||||
unsigned B = monochrome[color][2] * 65535.0;
|
||||
unsigned R = monochrome[color][0];
|
||||
unsigned G = monochrome[color][1];
|
||||
unsigned B = monochrome[color][2];
|
||||
|
||||
return interface->videoColor(color, R, G, B);
|
||||
}
|
||||
|
@ -68,11 +68,27 @@ unsigned Video::palette_cgb(unsigned color) const {
|
|||
return interface->videoColor(color, R, G, B);
|
||||
}
|
||||
|
||||
const double Video::monochrome[4][3] = {
|
||||
{0.605, 0.734, 0.059},
|
||||
{0.543, 0.672, 0.059},
|
||||
{0.188, 0.383, 0.188},
|
||||
{0.059, 0.219, 0.059},
|
||||
#define DMG_PALETTE_GREEN
|
||||
//#define DMG_PALETTE_YELLOW
|
||||
//#define DMG_PALETTE_WHITE
|
||||
|
||||
const uint16 Video::monochrome[4][3] = {
|
||||
#if defined(DMG_PALETTE_GREEN)
|
||||
{0x9a9a, 0xbbbb, 0x0505},
|
||||
{0x7878, 0x8484, 0x0505},
|
||||
{0x1d1d, 0x5555, 0x1d1d},
|
||||
{0x0505, 0x2525, 0x0505},
|
||||
#elif defined(DMG_PALETTE_YELLOW)
|
||||
{0xffff, 0xf7f7, 0x7b7b},
|
||||
{0xb5b5, 0xaeae, 0x4a4a},
|
||||
{0x6b6b, 0x6969, 0x3131},
|
||||
{0x2121, 0x2020, 0x1010},
|
||||
#else //DMG_PALETTE_WHITE
|
||||
{0xffff, 0xffff, 0xffff},
|
||||
{0xaaaa, 0xaaaa, 0xaaaa},
|
||||
{0x5555, 0x5555, 0x5555},
|
||||
{0x0000, 0x0000, 0x0000},
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ struct Video {
|
|||
|
||||
private:
|
||||
bool color_emulation;
|
||||
static const double monochrome[4][3];
|
||||
static const uint16 monochrome[4][3];
|
||||
uint32_t palette_dmg(unsigned color) const;
|
||||
uint32_t palette_sgb(unsigned color) const;
|
||||
uint32_t palette_cgb(unsigned color) const;
|
||||
|
|
|
@ -104,18 +104,12 @@ void pListView::setSelected(bool selected) {
|
|||
|
||||
void pListView::setSelection(unsigned selection) {
|
||||
GtkTreeSelection* treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget));
|
||||
GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget));
|
||||
gtk_tree_selection_unselect_all(treeSelection);
|
||||
GtkTreeIter iter;
|
||||
if(gtk_tree_model_get_iter_from_string(model, &iter, string(selection)) == false) return;
|
||||
gtk_tree_selection_select_iter(treeSelection, &iter);
|
||||
|
||||
//scroll window to selected item
|
||||
char* path = gtk_tree_model_get_string_from_iter(model, &iter);
|
||||
GtkTreePath* treePath = gtk_tree_path_new_from_string(path);
|
||||
gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(subWidget), treePath, nullptr, true, 0.5, 0.0);
|
||||
gtk_tree_path_free(treePath);
|
||||
g_free(path);
|
||||
GtkTreePath* path = gtk_tree_path_new_from_string(string{selection});
|
||||
gtk_tree_selection_select_path(treeSelection, path);
|
||||
gtk_tree_view_set_cursor(GTK_TREE_VIEW(subWidget), path, nullptr, false);
|
||||
gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(subWidget), path, nullptr, true, 0.5, 0.0);
|
||||
gtk_tree_path_free(path);
|
||||
}
|
||||
|
||||
void pListView::setText(unsigned selection, unsigned position, string text) {
|
||||
|
|
|
@ -24,7 +24,8 @@ void pComboButton::reset() {
|
|||
}
|
||||
|
||||
void pComboButton::setGeometry(Geometry geometry) {
|
||||
pWidget::setGeometry(geometry);
|
||||
//height = minimum drop-down list height; use CB_SETITEMHEIGHT to control actual widget height
|
||||
pWidget::setGeometry({geometry.x, geometry.y, geometry.width, 1});
|
||||
RECT rc;
|
||||
GetWindowRect(hwnd, &rc);
|
||||
unsigned adjustedHeight = geometry.height - ((rc.bottom - rc.top) - SendMessage(hwnd, CB_GETITEMHEIGHT, (WPARAM)-1, 0));
|
||||
|
|
|
@ -46,7 +46,7 @@ void ICD2::unload() {
|
|||
|
||||
void ICD2::power() {
|
||||
audio.coprocessor_enable(true);
|
||||
audio.coprocessor_frequency(4 * 1024 * 1024);
|
||||
audio.coprocessor_frequency(2 * 1024 * 1024);
|
||||
}
|
||||
|
||||
void ICD2::reset() {
|
||||
|
|
|
@ -128,7 +128,7 @@ void Utility::saveState(unsigned slot) {
|
|||
if(s.size() == 0) return;
|
||||
directory::create({pathname[0], "bsnes/"});
|
||||
if(file::write({pathname[0], "bsnes/state-", slot, ".bsa"}, s.data(), s.size()) == false);
|
||||
showMessage({"Save to slot ", slot});
|
||||
showMessage({"Saved to slot ", slot});
|
||||
}
|
||||
|
||||
void Utility::loadState(unsigned slot) {
|
||||
|
|
Loading…
Reference in New Issue