diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp index 4eb218594..905d6f0df 100644 --- a/emulator/emulator.hpp +++ b/emulator/emulator.hpp @@ -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/"; diff --git a/emulator/interface.hpp b/emulator/interface.hpp index 8089d68b3..b83ca2a60 100644 --- a/emulator/interface.hpp +++ b/emulator/interface.hpp @@ -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; } diff --git a/fc/interface/interface.cpp b/fc/interface/interface.cpp index d1d797a32..cbb541464 100644 --- a/fc/interface/interface.cpp +++ b/fc/interface/interface.cpp @@ -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() { diff --git a/fc/interface/interface.hpp b/fc/interface/interface.hpp index ddbff62d7..63e9194ab 100644 --- a/fc/interface/interface.hpp +++ b/fc/interface/interface.hpp @@ -45,7 +45,7 @@ struct Interface : Emulator::Interface { void cheatSet(const lstring&); - void paletteUpdate(bool colorEmulation); + void paletteUpdate(PaletteMode mode); Interface(); diff --git a/fc/ppu/ppu.cpp b/fc/ppu/ppu.cpp index dcae5631a..b4de38a5e 100644 --- a/fc/ppu/ppu.cpp +++ b/fc/ppu/ppu.cpp @@ -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() { diff --git a/fc/system/system.cpp b/fc/system/system.cpp index e80ef123d..4a86bea11 100644 --- a/fc/system/system.cpp +++ b/fc/system/system.cpp @@ -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); } } } diff --git a/fc/video/video.cpp b/fc/video/video.cpp index 5ffbc4662..0df6e5b75 100644 --- a/fc/video/video.cpp +++ b/fc/video/video.cpp @@ -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() { diff --git a/fc/video/video.hpp b/fc/video/video.hpp index cd9e66c09..f49930baf 100644 --- a/fc/video/video.hpp +++ b/fc/video/video.hpp @@ -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(); diff --git a/gb/cpu/memory.cpp b/gb/cpu/memory.cpp index b19fd3681..bee647575 100644 --- a/gb/cpu/memory.cpp +++ b/gb/cpu/memory.cpp @@ -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() { diff --git a/gb/cpu/timing.cpp b/gb/cpu/timing.cpp index 0f31a62c2..d07b70628 100644 --- a/gb/cpu/timing.cpp +++ b/gb/cpu/timing.cpp @@ -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; diff --git a/gb/interface/interface.cpp b/gb/interface/interface.cpp index de2ce6169..23d7b89e3 100644 --- a/gb/interface/interface.cpp +++ b/gb/interface/interface.cpp @@ -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() { diff --git a/gb/interface/interface.hpp b/gb/interface/interface.hpp index 95e54d044..f9a5ca5ff 100644 --- a/gb/interface/interface.hpp +++ b/gb/interface/interface.hpp @@ -58,7 +58,7 @@ struct Interface : Emulator::Interface { void cheatSet(const lstring&); - void paletteUpdate(bool colorEmulation); + void paletteUpdate(PaletteMode mode); Interface(); diff --git a/gb/ppu/cgb.cpp b/gb/ppu/cgb.cpp index 86aab56f4..9d04aa3a3 100644 --- a/gb/ppu/cgb.cpp +++ b/gb/ppu/cgb.cpp @@ -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); diff --git a/gb/ppu/dmg.cpp b/gb/ppu/dmg.cpp index 48e34b20c..6b8905d67 100644 --- a/gb/ppu/dmg.cpp +++ b/gb/ppu/dmg.cpp @@ -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); diff --git a/gb/ppu/ppu.cpp b/gb/ppu/ppu.cpp index 1c5565e0e..27ddc002e 100644 --- a/gb/ppu/ppu.cpp +++ b/gb/ppu/ppu.cpp @@ -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(); } } diff --git a/gb/system/system.cpp b/gb/system/system.cpp index d50ee23c7..c8a23c825 100644 --- a/gb/system/system.cpp +++ b/gb/system/system.cpp @@ -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); } } } diff --git a/gb/video/video.cpp b/gb/video/video.cpp index ade0a00b0..4c0a4e78c 100644 --- a/gb/video/video.cpp +++ b/gb/video/video.cpp @@ -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}, diff --git a/gb/video/video.hpp b/gb/video/video.hpp index e980e8f01..90b1f666b 100644 --- a/gb/video/video.hpp +++ b/gb/video/video.hpp @@ -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; diff --git a/gba/Makefile b/gba/Makefile index 946e69d91..c540fd12d 100644 --- a/gba/Makefile +++ b/gba/Makefile @@ -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) diff --git a/gba/cpu/mmio.cpp b/gba/cpu/mmio.cpp index 81334794b..4ba17e2d4 100644 --- a/gba/cpu/mmio.cpp +++ b/gba/cpu/mmio.cpp @@ -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); diff --git a/gba/gba.hpp b/gba/gba.hpp index 1861a755c..db5c97f05 100644 --- a/gba/gba.hpp +++ b/gba/gba.hpp @@ -53,6 +53,7 @@ namespace GameBoyAdvance { #include #include #include + #include #include #include #include diff --git a/gba/interface/interface.cpp b/gba/interface/interface.cpp index 9a3bc2bfa..50991dc3d 100644 --- a/gba/interface/interface.cpp +++ b/gba/interface/interface.cpp @@ -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() { diff --git a/gba/interface/interface.hpp b/gba/interface/interface.hpp index 68bef457a..8553e19a3 100644 --- a/gba/interface/interface.hpp +++ b/gba/interface/interface.hpp @@ -43,7 +43,7 @@ struct Interface : Emulator::Interface { serializer serialize(); bool unserialize(serializer&); - void paletteUpdate(bool colorEmulation); + void paletteUpdate(PaletteMode mode); Interface(); diff --git a/gba/player/player.cpp b/gba/player/player.cpp new file mode 100644 index 000000000..093636aec --- /dev/null +++ b/gba/player/player.cpp @@ -0,0 +1,93 @@ +#include + +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 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 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 + } +} + +} diff --git a/gba/player/player.hpp b/gba/player/player.hpp new file mode 100644 index 000000000..c9280620a --- /dev/null +++ b/gba/player/player.hpp @@ -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 keyinput(); + optional read(); + void write(uint8 byte, uint2 addr); + + void serialize(serializer& s); +}; + +extern Player player; diff --git a/gba/player/serialization.cpp b/gba/player/serialization.cpp new file mode 100644 index 000000000..bf6c7601e --- /dev/null +++ b/gba/player/serialization.cpp @@ -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); +} diff --git a/gba/ppu/ppu.cpp b/gba/ppu/ppu.cpp index 330f41957..e0ac955c0 100644 --- a/gba/ppu/ppu.cpp +++ b/gba/ppu/ppu.cpp @@ -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; } } diff --git a/gba/ppu/ppu.hpp b/gba/ppu/ppu.hpp index fbad6c2b5..7d801ebdf 100644 --- a/gba/ppu/ppu.hpp +++ b/gba/ppu/ppu.hpp @@ -4,7 +4,6 @@ struct PPU : Thread, MMIO { #include "registers.hpp" #include "state.hpp" uint32* output; - uint16* blur; static void Enter(); void main(); diff --git a/gba/ppu/screen.cpp b/gba/ppu/screen.cpp index 590142295..107e31ffb 100644 --- a/gba/ppu/screen.cpp +++ b/gba/ppu/screen.cpp @@ -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; } } diff --git a/gba/system/serialization.cpp b/gba/system/serialization.cpp index 0d344ad9a..a8d648e84 100644 --- a/gba/system/serialization.cpp +++ b/gba/system/serialization.cpp @@ -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() { diff --git a/gba/system/system.cpp b/gba/system/system.cpp index 062fc32f5..1e948707a 100644 --- a/gba/system/system.cpp +++ b/gba/system/system.cpp @@ -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); } } } diff --git a/gba/video/video.cpp b/gba/video/video.cpp index fda63117d..c9abe4ce4 100644 --- a/gba/video/video.cpp +++ b/gba/video/video.cpp @@ -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() { diff --git a/gba/video/video.hpp b/gba/video/video.hpp index 9d1628553..10dc5c117 100644 --- a/gba/video/video.hpp +++ b/gba/video/video.hpp @@ -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(); diff --git a/sfc/alt/ppu-balanced/render/line.cpp b/sfc/alt/ppu-balanced/render/line.cpp index 03466fc10..298013d89 100644 --- a/sfc/alt/ppu-balanced/render/line.cpp +++ b/sfc/alt/ppu-balanced/render/line.cpp @@ -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; } } diff --git a/sfc/alt/ppu-performance/screen/screen.cpp b/sfc/alt/ppu-performance/screen/screen.cpp index dd775b6df..622bb231e 100644 --- a/sfc/alt/ppu-performance/screen/screen.cpp +++ b/sfc/alt/ppu-performance/screen/screen.cpp @@ -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); } } } diff --git a/sfc/chip/icd2/icd2.cpp b/sfc/chip/icd2/icd2.cpp index 6493b55ae..4bb619e51 100644 --- a/sfc/chip/icd2/icd2.cpp +++ b/sfc/chip/icd2/icd2.cpp @@ -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(); } diff --git a/sfc/interface/interface.cpp b/sfc/interface/interface.cpp index e8afe0a19..626654d36 100644 --- a/sfc/interface/interface.cpp +++ b/sfc/interface/interface.cpp @@ -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) { diff --git a/sfc/interface/interface.hpp b/sfc/interface/interface.hpp index 964c8f094..8494ef63b 100644 --- a/sfc/interface/interface.hpp +++ b/sfc/interface/interface.hpp @@ -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); diff --git a/sfc/ppu/screen/screen.cpp b/sfc/ppu/screen/screen.cpp index 7906298e4..b5f0001c3 100644 --- a/sfc/ppu/screen/screen.cpp +++ b/sfc/ppu/screen/screen.cpp @@ -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) { diff --git a/sfc/system/video.cpp b/sfc/system/video.cpp index 9c3d00007..f086c1ada 100644 --- a/sfc/system/video.cpp +++ b/sfc/system/video.cpp @@ -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, diff --git a/sfc/system/video.hpp b/sfc/system/video.hpp index ac1c3260f..05ac2f056 100644 --- a/sfc/system/video.hpp +++ b/sfc/system/video.hpp @@ -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(); diff --git a/target-ethos/interface/interface.cpp b/target-ethos/interface/interface.cpp index 8891c405d..cd74947fd 100644 --- a/target-ethos/interface/interface.cpp +++ b/target-ethos/interface/interface.cpp @@ -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) { diff --git a/target-ethos/interface/interface.hpp b/target-ethos/interface/interface.hpp index 15ca39be5..b7083229c 100644 --- a/target-ethos/interface/interface.hpp +++ b/target-ethos/interface/interface.hpp @@ -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); diff --git a/target-ethos/settings/video.cpp b/target-ethos/settings/video.cpp index 6ec953edc..6d33aadf2 100644 --- a/target-ethos/settings/video.cpp +++ b/target-ethos/settings/video.cpp @@ -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(); } diff --git a/target-ethos/utility/utility.cpp b/target-ethos/utility/utility.cpp index 0212b08c3..dd7020783 100644 --- a/target-ethos/utility/utility.cpp +++ b/target-ethos/utility/utility.cpp @@ -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) { diff --git a/target-ethos/utility/utility.hpp b/target-ethos/utility/utility.hpp index f433b8673..82490808c 100644 --- a/target-ethos/utility/utility.hpp +++ b/target-ethos/utility/utility.hpp @@ -21,6 +21,7 @@ struct Utility { void synchronizeDSP(); void synchronizeRuby(); + void updatePalette(); void updateShader(); void resize(bool resizeWindow = false); void toggleFullScreen();