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:
Tim Allen 2013-12-11 22:19:17 +11:00
parent 0f78acffd7
commit 1361820dd8
16 changed files with 395 additions and 313 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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