Update to v093r10 release.

byuu says:

Changelog:
- Game Boy (Color): STAT OAM+HBlank IRQs only trigger during LY=0-143
  with display enabled
  - fixes backgrounds and text in Wacky Races
- Game Boy (Color): fixed underflow in window clamping
  - fixes Wacky Races, Prehistorik Man, Alleyway, etc
- Game Boy (Color): LCD OAM DMA was running too slow
  - fixes Shin Megami Tensei - Devichil - Kuro no Sho
- Game Boy Advance: removed built-in frame blending; display emulation
  shaders will handle this going forward
- Game Boy Advance: added Game Boy Player emulation
  - currently the screen is tinted red during rumble, no actual gamepad
    rumble support yet
  - this is going to be slow, as we have to hash the frame to detect the
    GBP logo, it'll be optional later on
- Emulator::Interface::Palette can now output a raw palette (for Display
  Emulation shaders only)
  - color channels are not yet split up, it's just the raw packed value
This commit is contained in:
Tim Allen 2013-12-20 22:40:39 +11:00
parent 926a39d701
commit 84fab07756
46 changed files with 270 additions and 118 deletions

View File

@ -3,7 +3,7 @@
namespace Emulator {
static const char Name[] = "higan";
static const char Version[] = "093.09";
static const char Version[] = "093.10";
static const char Author[] = "byuu";
static const char License[] = "GPLv3";
static const char Website[] = "http://byuu.org/";

View File

@ -51,7 +51,7 @@ struct Interface {
virtual void loadRequest(unsigned, string) {}
virtual void saveRequest(unsigned, string) {}
virtual uint32_t videoColor(unsigned, uint16_t, uint16_t, uint16_t) { return 0u; }
virtual void videoRefresh(const uint32_t*, unsigned, unsigned, unsigned) {}
virtual void videoRefresh(const uint32_t*, const uint32_t*, unsigned, unsigned, unsigned) {}
virtual void audioSample(int16_t, int16_t) {}
virtual int16_t inputPoll(unsigned, unsigned, unsigned) { return 0; }
virtual unsigned dipSettings(const Markup::Node&) { return 0; }
@ -66,7 +66,7 @@ struct Interface {
void loadRequest(unsigned id, string path) { return bind->loadRequest(id, path); }
void saveRequest(unsigned id, string path) { return bind->saveRequest(id, path); }
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) { return bind->videoColor(source, red, green, blue); }
void videoRefresh(const uint32_t* data, unsigned pitch, unsigned width, unsigned height) { return bind->videoRefresh(data, pitch, width, height); }
void videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch, unsigned width, unsigned height) { return bind->videoRefresh(palette, data, pitch, width, height); }
void audioSample(int16_t lsample, int16_t rsample) { return bind->audioSample(lsample, rsample); }
int16_t inputPoll(unsigned port, unsigned device, unsigned input) { return bind->inputPoll(port, device, input); }
unsigned dipSettings(const Markup::Node& node) { return bind->dipSettings(node); }
@ -107,7 +107,8 @@ struct Interface {
virtual void cheatSet(const lstring& = lstring{}) {}
//utility functions
virtual void paletteUpdate(bool colorEmulation) {}
enum class PaletteMode : unsigned { None, Standard, Emulation };
virtual void paletteUpdate(PaletteMode mode) {}
//debugger functions
virtual bool tracerEnable(bool) { return false; }

View File

@ -115,8 +115,8 @@ void Interface::cheatSet(const lstring& list) {
cheat.synchronize();
}
void Interface::paletteUpdate(bool colorEmulation) {
video.generate_palette(colorEmulation);
void Interface::paletteUpdate(PaletteMode mode) {
video.generate_palette(mode);
}
Interface::Interface() {

View File

@ -45,7 +45,7 @@ struct Interface : Emulator::Interface {
void cheatSet(const lstring&);
void paletteUpdate(bool colorEmulation);
void paletteUpdate(PaletteMode mode);
Interface();

View File

@ -324,7 +324,7 @@ void PPU::raster_pixel() {
}
if(raster_enable() == false) palette = 0;
output[status.lx] = video.palette[(status.emphasis << 6) | cgram_read(palette)];
output[status.lx] = (status.emphasis << 6) | cgram_read(palette);
}
void PPU::raster_sprite() {

View File

@ -8,7 +8,7 @@ System system;
void System::run() {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
interface->videoRefresh(ppu.buffer, 4 * 256, 256, 240);
interface->videoRefresh(video.palette, ppu.buffer, 4 * 256, 256, 240);
}
}
@ -36,7 +36,7 @@ void System::runthreadtosave() {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
interface->videoRefresh(ppu.buffer, 4 * 256, 256, 240);
interface->videoRefresh(video.palette, ppu.buffer, 4 * 256, 256, 240);
}
}
}

View File

@ -6,12 +6,18 @@ namespace Famicom {
Video video;
void Video::generate_palette(bool color_emulation) {
for(unsigned n = 0; n < (1 << 9); n++) palette[n] = generate_color(n, 2.0, 0.0, 1.0, 1.0, color_emulation ? 1.8 : 2.2);
void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
for(unsigned n = 0; n < (1 << 9); n++) {
switch(mode) {
case Emulator::Interface::PaletteMode::None: palette[n] = n; break;
case Emulator::Interface::PaletteMode::Standard: palette[n] = generate_color(n, 2.0, 0.0, 1.0, 1.0, 2.2); break;
case Emulator::Interface::PaletteMode::Emulation: palette[n] = generate_color(n, 2.0, 0.0, 1.0, 1.0, 1.8); break;
}
}
}
Video::Video() {
palette = new unsigned[1 << 9]();
palette = new uint32_t[1 << 9]();
}
Video::~Video() {

View File

@ -1,6 +1,6 @@
struct Video {
unsigned* palette = nullptr;
void generate_palette(bool color_emulation);
uint32_t* palette = nullptr;
void generate_palette(Emulator::Interface::PaletteMode mode);
Video();
~Video();

View File

@ -8,15 +8,15 @@ void CPU::op_io() {
uint8 CPU::op_read(uint16 addr) {
cycle_edge();
add_clocks(4);
if(oamdma.active) return hram[addr & 0x7f];
if(oamdma.active && (addr < 0xff80 || addr == 0xffff)) return 0x00;
return bus.read(addr);
}
void CPU::op_write(uint16 addr, uint8 data) {
cycle_edge();
add_clocks(4);
if(oamdma.active) hram[addr & 0x7f] = data;
else bus.write(addr, data);
if(oamdma.active && (addr < 0xff80 || addr == 0xffff)) return;
bus.write(addr, data);
}
void CPU::cycle_edge() {

View File

@ -6,7 +6,7 @@
void CPU::add_clocks(unsigned clocks) {
if(oamdma.active) {
for(unsigned n = 0; n < clocks; n++) {
for(unsigned n = 0; n < 4 * clocks; n++) {
bus.write(0xfe00 + oamdma.offset, bus.read((oamdma.bank << 8) + oamdma.offset));
if(++oamdma.offset == 160) {
oamdma.active = false;

View File

@ -131,8 +131,8 @@ void Interface::cheatSet(const lstring& list) {
cheat.synchronize();
}
void Interface::paletteUpdate(bool colorEmulation) {
video.generate_palette(colorEmulation);
void Interface::paletteUpdate(PaletteMode mode) {
video.generate_palette(mode);
}
Interface::Interface() {

View File

@ -58,7 +58,7 @@ struct Interface : Emulator::Interface {
void cheatSet(const lstring&);
void paletteUpdate(bool colorEmulation);
void paletteUpdate(PaletteMode mode);
Interface();

View File

@ -91,7 +91,7 @@ void PPU::cgb_run() {
}
uint32* output = screen + status.ly * 160 + px++;
*output = video.palette[color];
*output = color;
}
void PPU::cgb_run_bg() {
@ -115,10 +115,10 @@ void PPU::cgb_run_bg() {
}
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 scrolly = status.ly - status.wy;
unsigned scrollx = px + 7 - status.wx;
if(scrolly >= 144u) return; //also matches underflow (scrolly < 0)
if(scrollx >= 160u) return; //also matches underflow (scrollx < 0)
unsigned tx = scrollx & 7;
if(tx == 0 || px == 0) cgb_read_tile(status.window_tilemap_select, scrollx, scrolly, window.attr, window.data);

View File

@ -78,7 +78,7 @@ void PPU::dmg_run() {
}
uint32* output = screen + status.ly * 160 + px++;
*output = video.palette[color];
*output = color;
}
void PPU::dmg_run_bg() {
@ -96,10 +96,10 @@ void PPU::dmg_run_bg() {
}
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 scrolly = status.ly - status.wy;
unsigned scrollx = px + 7 - status.wx;
if(scrolly >= 144u) return; //also matches underflow (scrolly < 0)
if(scrollx >= 160u) return; //also matches underflow (scrollx < 0)
unsigned tx = scrollx & 7;
if(tx == 0 || px == 0) dmg_read_tile(status.window_tilemap_select, scrollx, scrolly, window.data);

View File

@ -25,30 +25,20 @@ void PPU::main() {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
//X = 0
if(status.display_enable) {
if(status.display_enable && status.ly < 144) {
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
add_clocks(92);
if(status.ly < 144) {
add_clocks(92);
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);
} else {
add_clocks(456);
}
add_clocks(204);
scanline();
}
}

View File

@ -11,7 +11,7 @@ void System::run() {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
interface->videoRefresh(ppu.screen, 4 * 160, 160, 144);
interface->videoRefresh(video.palette, ppu.screen, 4 * 160, 160, 144);
}
}
@ -35,7 +35,7 @@ void System::runthreadtosave() {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
interface->videoRefresh(ppu.screen, 4 * 160, 160, 144);
interface->videoRefresh(video.palette, ppu.screen, 4 * 160, 160, 144);
}
}
}

View File

@ -5,15 +5,15 @@ namespace GameBoy {
Video video;
void Video::generate_palette(bool color_emulation) {
this->color_emulation = color_emulation;
void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
this->mode = mode;
if(system.dmg()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_dmg(n);
if(system.sgb()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_sgb(n);
if(system.cgb()) for(unsigned n = 0; n < (1 << 15); n++) palette[n] = palette_cgb(n);
}
Video::Video() {
palette = new unsigned[1 << 15]();
palette = new uint32_t[1 << 15]();
}
Video::~Video() {
@ -21,7 +21,9 @@ Video::~Video() {
}
unsigned Video::palette_dmg(unsigned color) const {
if(color_emulation == false) {
if(mode == Emulator::Interface::PaletteMode::None) return color;
if(mode == Emulator::Interface::PaletteMode::Standard) {
unsigned L = (3 - color) * 21845;
return interface->videoColor(color, L, L, L);
}
@ -34,19 +36,17 @@ unsigned Video::palette_dmg(unsigned color) const {
}
unsigned Video::palette_sgb(unsigned color) const {
unsigned R = (3 - color) * 21845;
unsigned G = (3 - color) * 21845;
unsigned B = (3 - color) * 21845;
return interface->videoColor(color, R, G, B);
return color;
}
unsigned Video::palette_cgb(unsigned color) const {
if(mode == Emulator::Interface::PaletteMode::None) return color;
unsigned r = (color >> 0) & 31;
unsigned g = (color >> 5) & 31;
unsigned b = (color >> 10) & 31;
if(color_emulation == false) {
if(mode == Emulator::Interface::PaletteMode::Standard) {
unsigned R = (r << 11) | (r << 6) | (r << 1) | (r >> 4);
unsigned G = (g << 11) | (g << 6) | (g << 1) | (g >> 4);
unsigned B = (b << 11) | (b << 6) | (b << 1) | (b >> 4);
@ -74,10 +74,10 @@ unsigned Video::palette_cgb(unsigned color) const {
const uint16 Video::monochrome[4][3] = {
#if defined(DMG_PALETTE_GREEN)
{0x9a9a, 0xbbbb, 0x0505},
{0x7878, 0x8484, 0x0505},
{0x1d1d, 0x5555, 0x1d1d},
{0x0505, 0x2525, 0x0505},
{0xaeae, 0xd9d9, 0x2727},
{0x5858, 0xa0a0, 0x2828},
{0x2020, 0x6262, 0x2929},
{0x1a1a, 0x4545, 0x2a2a},
#elif defined(DMG_PALETTE_YELLOW)
{0xffff, 0xf7f7, 0x7b7b},
{0xb5b5, 0xaeae, 0x4a4a},

View File

@ -1,12 +1,12 @@
struct Video {
uint32_t* palette;
void generate_palette(bool color_emulation);
uint32_t* palette = nullptr;
void generate_palette(Emulator::Interface::PaletteMode mode);
Video();
~Video();
private:
bool color_emulation;
Emulator::Interface::PaletteMode mode;
static const uint16 monochrome[4][3];
uint32_t palette_dmg(unsigned color) const;
uint32_t palette_sgb(unsigned color) const;

View File

@ -1,5 +1,5 @@
gba_objects := gba-memory gba-interface gba-scheduler gba-system
gba_objects += gba-video gba-cartridge
gba_objects += gba-video gba-cartridge gba-player
gba_objects += gba-cpu gba-ppu gba-apu
objects += $(gba_objects)
@ -9,6 +9,7 @@ obj/gba-scheduler.o: $(gba)/scheduler/scheduler.cpp $(call rwildcard,$(gba)/sche
obj/gba-system.o: $(gba)/system/system.cpp $(call rwildcard,$(gba)/system)
obj/gba-video.o: $(gba)/video/video.cpp $(call rwildcard,$(gba)/video)
obj/gba-cartridge.o: $(gba)/cartridge/cartridge.cpp $(call rwildcard,$(gba)/cartridge)
obj/gba-player.o: $(gba)/player/player.cpp $(call rwildcard,$(gba)/player)
obj/gba-cpu.o: $(gba)/cpu/cpu.cpp $(call rwildcard,$(gba)/cpu)
obj/gba-ppu.o: $(gba)/ppu/ppu.cpp $(call rwildcard,$(gba)/ppu)
obj/gba-apu.o: $(gba)/apu/apu.cpp $(call rwildcard,$(gba)/apu)

View File

@ -47,8 +47,9 @@ uint8 CPU::read(uint32 addr) {
case 0x04000122: case 0x04000123:
case 0x04000124: case 0x04000125:
case 0x04000126: case 0x04000127: {
auto& data = regs.serial.data[(addr >> 1) & 3];
if(auto data = player.read()) return data() >> ((addr & 3) << 3);
unsigned shift = (addr & 1) * 8;
auto& data = regs.serial.data[(addr >> 1) & 3];
return data >> shift;
}
@ -62,11 +63,13 @@ uint8 CPU::read(uint32 addr) {
//KEYINPUT
case 0x04000130:
if(auto result = player.keyinput()) return result() >> 0;
for(unsigned n = 0; n < 8; n++) result |= interface->inputPoll(0, 0, n) << n;
if((result & 0xc0) == 0xc0) result &= ~0xc0; //up+down cannot be pressed simultaneously
if((result & 0x30) == 0x30) result &= ~0x30; //left+right cannot be pressed simultaneously
return result ^ 0xff;
case 0x04000131:
if(auto result = player.keyinput()) return result() >> 8;
result |= interface->inputPoll(0, 0, 8) << 0;
result |= interface->inputPoll(0, 0, 9) << 1;
return result ^ 0x03;
@ -241,6 +244,7 @@ void CPU::write(uint32 addr, uint8 byte) {
case 0x04000122: case 0x04000123:
case 0x04000124: case 0x04000125:
case 0x04000126: case 0x04000127: {
player.write(byte, addr & 3);
auto& data = regs.serial.data[(addr >> 1) & 3];
unsigned shift = (addr & 1) * 8;
data = (data & ~(255 << shift)) | (byte << shift);

View File

@ -53,6 +53,7 @@ namespace GameBoyAdvance {
#include <gba/scheduler/scheduler.hpp>
#include <gba/system/system.hpp>
#include <gba/cartridge/cartridge.hpp>
#include <gba/player/player.hpp>
#include <gba/cpu/cpu.hpp>
#include <gba/ppu/ppu.hpp>
#include <gba/apu/apu.hpp>

View File

@ -109,8 +109,8 @@ bool Interface::unserialize(serializer& s) {
return system.unserialize(s);
}
void Interface::paletteUpdate(bool colorEmulation) {
video.generate_palette(colorEmulation);
void Interface::paletteUpdate(PaletteMode mode) {
video.generate_palette(mode);
}
Interface::Interface() {

View File

@ -43,7 +43,7 @@ struct Interface : Emulator::Interface {
serializer serialize();
bool unserialize(serializer&);
void paletteUpdate(bool colorEmulation);
void paletteUpdate(PaletteMode mode);
Interface();

93
gba/player/player.cpp Normal file
View File

@ -0,0 +1,93 @@
#include <gba/gba.hpp>
namespace GameBoyAdvance {
//Game Boy Player emulation
#include "serialization.cpp"
Player player;
void Player::power() {
status.enable = false;
status.rumble = false;
status.logoDetected = false;
status.logoCounter = 0;
status.packet = 0;
status.send = 0;
status.recv = 0;
}
void Player::frame() {
uint32 hash = crc32_calculate((const uint8*)ppu.output, 240 * 160 * sizeof(uint32));
status.logoDetected = (hash == 0x7776eb55);
if(status.logoDetected) {
status.enable = true;
status.logoCounter = (status.logoCounter + 1) % 3;
status.packet = 0;
}
if(status.enable == false) return;
if(cpu.regs.joybus.settings == 0x0000 && cpu.regs.serial.control == 0x5088) {
status.packet = (status.packet + 1) % 17;
switch(status.packet) {
case 0: status.send = 0x0000494e; break;
case 1: status.send = 0xb6b1494e; break;
case 2: status.send = 0xb6b1494e; break;
case 3: status.send = 0xb6b1544e; break;
case 4: status.send = 0xabb1544e; break;
case 5: status.send = 0xabb14e45; break;
case 6: status.send = 0xb1ba4e45; break;
case 7: status.send = 0xb1ba4f44; break;
case 8: status.send = 0xb0bb4f44; break;
case 9: status.send = 0xb0bb8002; break;
case 10: status.send = 0x10000010; break;
case 11: status.send = 0x20000013; break;
case 12: status.send = 0x30000003; break;
case 13: status.send = 0x30000003; break;
case 14: status.send = 0x30000003; break;
case 15: status.send = 0x30000003; break;
case 16: status.send = 0x30000003; break;
}
cpu.regs.irq.flag.serial = true;
}
if(status.rumble) {
//todo: support actual gamepad rumble; for now, color screen red during rumble
for(unsigned n = 0; n < 240 * 160; n++) ppu.output[n] &= 0x001f;
}
}
optional<uint16> Player::keyinput() {
if(status.logoDetected == false) return false;
switch(status.logoCounter) {
case 0: return {true, 0x03ff};
case 1: return {true, 0x03ff};
case 2: return {true, 0x030f};
}
unreachable;
}
optional<uint32> Player::read() {
if(status.enable == false) return false;
return {true, status.send};
}
void Player::write(uint8 byte, uint2 addr) {
if(status.enable == false) return;
unsigned shift = addr << 3;
status.recv &= ~(255 << shift);
status.recv |= byte << shift;
if(addr == 3 && status.packet == 15) {
status.rumble = (status.recv & 0xff) == 0x26; //on = 0x26, off = 0x04
}
}
}

24
gba/player/player.hpp Normal file
View File

@ -0,0 +1,24 @@
struct Player {
struct Status {
bool enable;
bool rumble;
bool logoDetected;
unsigned logoCounter;
unsigned packet;
uint32 send;
uint32 recv;
} status;
void power();
void frame();
optional<uint16> keyinput();
optional<uint32> read();
void write(uint8 byte, uint2 addr);
void serialize(serializer& s);
};
extern Player player;

View File

@ -0,0 +1,11 @@
void Player::serialize(serializer& s) {
s.integer(status.enable);
s.integer(status.rumble);
s.integer(status.logoDetected);
s.integer(status.logoCounter);
s.integer(status.packet);
s.integer(status.send);
s.integer(status.recv);
}

View File

@ -44,7 +44,7 @@ void PPU::step(unsigned clocks) {
void PPU::power() {
create(PPU::Enter, 16777216);
for(unsigned n = 0; n < 240 * 160; n++) output[n] = 0, blur[n] = 0;
for(unsigned n = 0; n < 240 * 160; n++) output[n] = 0;
for(unsigned n = 0; n < 1024; n += 2) pram_write(n, Half, 0x0000);
for(unsigned n = 0; n < 1024; n += 2) oam_write(n, Half, 0x0000);
@ -149,12 +149,12 @@ void PPU::scanline() {
}
void PPU::frame() {
player.frame();
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
PPU::PPU() {
output = new uint32[240 * 160];
blur = new uint16[240 * 160];
regs.bg[0].id = BG0;
regs.bg[1].id = BG1;
@ -164,7 +164,6 @@ PPU::PPU() {
PPU::~PPU() {
delete[] output;
delete[] blur;
}
}

View File

@ -4,7 +4,6 @@ struct PPU : Thread, MMIO {
#include "registers.hpp"
#include "state.hpp"
uint32* output;
uint16* blur;
static void Enter();
void main();

View File

@ -1,15 +1,12 @@
void PPU::render_forceblank() {
uint32* line = output + regs.vcounter * 240;
uint16* last = blur + regs.vcounter * 240;
for(unsigned x = 0; x < 240; x++) {
line[x] = video.palette[(0x7fff + last[x] - ((0x7fff ^ last[x]) & 0x0421)) >> 1];
last[x] = 0x7fff;
line[x] = 0x7fff;
}
}
void PPU::render_screen() {
uint32* line = output + regs.vcounter * 240;
uint16* last = blur + regs.vcounter * 240;
if(regs.bg[0].control.mosaic) render_mosaic_background(BG0);
if(regs.bg[1].control.mosaic) render_mosaic_background(BG1);
@ -58,9 +55,8 @@ void PPU::render_screen() {
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] = video.palette[(color + last[x] - ((color ^ last[x]) & 0x0421)) >> 1];
last[x] = color;
//output pixel
line[x] = color;
}
}

View File

@ -44,6 +44,7 @@ void System::serialize_all(serializer& s) {
ppu.serialize(s);
apu.serialize(s);
bus.serialize(s);
player.serialize(s);
}
void System::serialize_init() {

View File

@ -15,6 +15,7 @@ void System::term() {
void System::power() {
bus.power();
player.power();
cpu.power();
ppu.power();
apu.power();
@ -39,7 +40,7 @@ void System::run() {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) break;
}
interface->videoRefresh(ppu.output, 4 * 240, 240, 160);
interface->videoRefresh(video.palette, ppu.output, 4 * 240, 240, 160);
}
void System::runtosave() {
@ -62,7 +63,7 @@ void System::runthreadtosave() {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
interface->videoRefresh(ppu.output, 4 * 240, 240, 160);
interface->videoRefresh(video.palette, ppu.output, 4 * 240, 240, 160);
}
}
}

View File

@ -4,14 +4,22 @@ namespace GameBoyAdvance {
Video video;
void Video::generate_palette(bool color_emulation) {
//todo: implement LCD color emulation
void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
for(unsigned color = 0; color < (1 << 15); color++) {
if(mode == Emulator::Interface::PaletteMode::None) {
palette[color] = color;
continue;
}
unsigned B = (color >> 10) & 31;
unsigned G = (color >> 5) & 31;
unsigned R = (color >> 0) & 31;
if(color_emulation) {
if(mode == Emulator::Interface::PaletteMode::Standard) {
R = R << 11 | R << 6 | R << 1 | R >> 4;
G = G << 11 | G << 6 | G << 1 | G >> 4;
B = B << 11 | B << 6 | B << 1 | B >> 4;
} else {
R = curve[R];
G = curve[G];
B = curve[B];
@ -47,10 +55,6 @@ void Video::generate_palette(bool color_emulation) {
R = R << 8 | R;
G = G << 8 | G;
B = B << 8 | B;
} else {
R = R << 11 | R << 6 | R << 1 | R >> 4;
G = G << 11 | G << 6 | G << 1 | G >> 4;
B = B << 11 | B << 6 | B << 1 | B >> 4;
}
palette[color] = interface->videoColor(color, R, G, B);
@ -58,7 +62,7 @@ void Video::generate_palette(bool color_emulation) {
}
Video::Video() {
palette = new uint32[1 << 15]();
palette = new uint32_t[1 << 15]();
}
Video::~Video() {

View File

@ -1,6 +1,6 @@
struct Video {
unsigned* palette;
void generate_palette(bool color_emulation);
uint32_t* palette = nullptr;
void generate_palette(Emulator::Interface::PaletteMode mode);
Video();
~Video();

View File

@ -93,7 +93,7 @@ inline void PPU::render_line_output() {
if(!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) {
for(unsigned x = 0; x < 256; x++) {
curr = (regs.display_brightness << 15) | get_pixel_normal(x);
*ptr++ = video.palette[curr];
*ptr++ = curr;
}
} else {
for(unsigned x = 0, prev = 0; x < 256; x++) {
@ -101,11 +101,11 @@ inline void PPU::render_line_output() {
//blending code is left for reference purposes
curr = (regs.display_brightness << 15) | get_pixel_swap(x);
*ptr++ = video.palette[curr]; //(prev + curr - ((prev ^ curr) & 0x0421)) >> 1;
*ptr++ = curr; //(prev + curr - ((prev ^ curr) & 0x0421)) >> 1;
//prev = curr;
curr = (regs.display_brightness << 15) | get_pixel_normal(x);
*ptr++ = video.palette[curr]; //(prev + curr - ((prev ^ curr) & 0x0421)) >> 1;
*ptr++ = curr; //(prev + curr - ((prev ^ curr) & 0x0421)) >> 1;
//prev = curr;
}
}

View File

@ -120,12 +120,12 @@ void PPU::Screen::render() {
if(!self.regs.pseudo_hires && self.regs.bgmode != 5 && self.regs.bgmode != 6) {
for(unsigned i = 0; i < 256; i++) {
data[i] = video.palette[self.regs.display_brightness << 15 | get_pixel_main(i)];
data[i] = self.regs.display_brightness << 15 | get_pixel_main(i);
}
} else {
for(unsigned i = 0; i < 256; i++) {
*data++ = video.palette[self.regs.display_brightness << 15 | get_pixel_sub(i)];
*data++ = video.palette[self.regs.display_brightness << 15 | get_pixel_main(i)];
*data++ = self.regs.display_brightness << 15 | get_pixel_sub(i);
*data++ = self.regs.display_brightness << 15 | get_pixel_main(i);
}
}
}

View File

@ -73,7 +73,7 @@ void ICD2::reset() {
joyp14lock = 0;
pulselock = true;
GameBoy::video.generate_palette(false);
GameBoy::video.generate_palette(Emulator::Interface::PaletteMode::None);
GameBoy::system.init();
GameBoy::system.power();
}

View File

@ -338,8 +338,8 @@ void Interface::cheatSet(const lstring& list) {
cheat.synchronize();
}
void Interface::paletteUpdate(bool colorEmulation) {
video.generate_palette(colorEmulation);
void Interface::paletteUpdate(PaletteMode mode) {
video.generate_palette(mode);
}
bool Interface::tracerEnable(bool trace) {

View File

@ -115,7 +115,7 @@ struct Interface : Emulator::Interface {
void cheatSet(const lstring&);
void paletteUpdate(bool colorEmulation);
void paletteUpdate(PaletteMode mode);
//debugger functions
bool tracerEnable(bool);

View File

@ -20,13 +20,12 @@ void PPU::Screen::scanline() {
void PPU::Screen::run() {
if(ppu.vcounter() == 0) return;
auto palette = &video.palette[self.regs.display_brightness << 15];
bool hires = self.regs.pseudo_hires || self.regs.bgmode == 5 || self.regs.bgmode == 6;
auto sscolor = get_pixel_sub(hires);
auto mscolor = get_pixel_main();
*output++ = palette[hires ? sscolor : mscolor];
*output++ = palette[mscolor];
*output++ = (self.regs.display_brightness << 15) | (hires ? sscolor : mscolor);
*output++ = (self.regs.display_brightness << 15) | (mscolor);
}
uint16 PPU::Screen::get_pixel_sub(bool hires) {

View File

@ -2,14 +2,19 @@
Video video;
void Video::generate_palette(bool color_emulation) {
void Video::generate_palette(Emulator::Interface::PaletteMode mode) {
for(unsigned color = 0; color < (1 << 19); color++) {
if(mode == Emulator::Interface::PaletteMode::None) {
palette[color] = color;
continue;
}
unsigned l = (color >> 15) & 15;
unsigned b = (color >> 10) & 31;
unsigned g = (color >> 5) & 31;
unsigned r = (color >> 0) & 31;
if(color_emulation == true) {
if(mode == Emulator::Interface::PaletteMode::Emulation) {
r = gamma_ramp[r];
g = gamma_ramp[g];
b = gamma_ramp[b];
@ -30,7 +35,7 @@ void Video::generate_palette(bool color_emulation) {
}
Video::Video() {
palette = new unsigned[1 << 19]();
palette = new uint32_t[1 << 19]();
}
Video::~Video() {
@ -126,6 +131,7 @@ void Video::update() {
//overscan: when disabled, shift image down (by scrolling video buffer up) to center image onscreen
//(memory before ppu.output is filled with black scanlines)
interface->videoRefresh(
video.palette,
ppu.output - (ppu.overscan() ? 0 : 7 * 1024),
4 * (1024 >> ppu.interlace()),
256 << hires,

View File

@ -1,6 +1,6 @@
struct Video {
unsigned* palette;
void generate_palette(bool color_emulation);
uint32_t* palette;
void generate_palette(Emulator::Interface::PaletteMode mode);
Video();
~Video();

View File

@ -51,7 +51,7 @@ uint32_t Interface::videoColor(unsigned source, uint16_t r, uint16_t g, uint16_t
return 0u;
}
void Interface::videoRefresh(const uint32_t* data, unsigned pitch, unsigned width, unsigned height) {
void Interface::videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch, unsigned width, unsigned height) {
uint32_t* output;
unsigned outputPitch;
@ -59,7 +59,11 @@ void Interface::videoRefresh(const uint32_t* data, unsigned pitch, unsigned widt
pitch >>= 2, outputPitch >>= 2;
for(unsigned y = 0; y < height; y++) {
memcpy(output + y * outputPitch, data + y * pitch, 4 * width);
const uint32_t* sp = data + y * pitch;
uint32_t* dp = output + y * outputPitch;
for(unsigned x = 0; x < width; x++) {
*dp++ = palette[*sp++];
}
}
if(system().information.overscan && config->video.maskOverscan.enable) {

View File

@ -3,7 +3,7 @@ struct Interface : Emulator::Interface::Bind {
void loadRequest(unsigned id, string path);
void saveRequest(unsigned id, string path);
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue);
void videoRefresh(const uint32_t* data, unsigned pitch, unsigned width, unsigned height);
void videoRefresh(const uint32_t* palette, const uint32_t* data, unsigned pitch, unsigned width, unsigned height);
void audioSample(int16_t lsample, int16_t rsample);
int16_t inputPoll(unsigned port, unsigned device, unsigned input);
unsigned dipSettings(const Markup::Node& node);

View File

@ -60,5 +60,5 @@ void VideoSettings::synchronize() {
overscanHorizontal.value.setText({config->video.maskOverscan.horizontal, "px"});
overscanVertical.value.setText({config->video.maskOverscan.vertical, "px"});
if(program->active) system().paletteUpdate(config->video.colorEmulation);
utility->updatePalette();
}

View File

@ -91,7 +91,6 @@ void Utility::load() {
cheatEditor->load({pathname[0], "cheats.bml"});
stateManager->load({pathname[0], "bsnes/states.bsa"}, 1);
system().paletteUpdate(config->video.colorEmulation);
synchronizeDSP();
resize();
@ -180,6 +179,18 @@ void Utility::synchronizeRuby() {
synchronizeDSP();
}
void Utility::updatePalette() {
if(program->active == nullptr) return;
if(config->video.shader == "Display Emulation") {
system().paletteUpdate(Emulator::Interface::PaletteMode::None);
} else if(config->video.colorEmulation) {
system().paletteUpdate(Emulator::Interface::PaletteMode::Emulation);
} else {
system().paletteUpdate(Emulator::Interface::PaletteMode::Standard);
}
}
void Utility::updateShader() {
if(config->video.shader == "None") {
video.set(Video::Shader, (const char*)"");
@ -187,7 +198,6 @@ void Utility::updateShader() {
} else if(config->video.shader == "Blur") {
video.set(Video::Shader, (const char*)"");
video.set(Video::Filter, Video::FilterLinear);
return;
} else if(config->video.shader == "Display Emulation") {
if(program->active) {
string pathname = program->path("Video Shaders/");
@ -206,6 +216,7 @@ void Utility::updateShader() {
} else {
video.set(Video::Shader, (const char*)config->video.shader);
}
updatePalette();
}
void Utility::resize(bool resizeWindow) {

View File

@ -21,6 +21,7 @@ struct Utility {
void synchronizeDSP();
void synchronizeRuby();
void updatePalette();
void updateShader();
void resize(bool resizeWindow = false);
void toggleFullScreen();