From aaffd000a416eca67b09f5c0f4ac4a649b4e7da3 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Fri, 28 Oct 2011 00:30:19 +1100 Subject: [PATCH] Update to v083r06 release. byuu says: All cores: Video classes have internal->{RGB30,24,16,15} palette generation support All cores: video output is now RGB24, all filters except HQ2x were updated to reflect this (HQ2x will be very hard) NES: MMC5 CHR mapping fixes (Bandit Kings, RTK2, Uchuu Keibitai SDF) [Cydrak] NES: MMC5 vertical split screen support (Uchuu Keibitai SDF) [Cydrak] Game Boy + Game Boy Color: fixed a potential freezing bug when loading save states (re-create cothreads on state load; was implied when using SGB mode.) Game Boy Color: fixed freezing bug with Zelda: LA opening (SVBK is readable.) Game Boy Color: more accurate colors (better than GiiBii, probably worse than KiGB) SNES: luminance of zero is no longer pure black, as on real hardware. This is possible thanks to using RGB888 output now. The current major problems I'd like to solve: - Zelda: Link's Awakening music when Link first wakes up in the house is atrociously bad - Shin Megami Tensei: Devil Children - White Book (Shiro no Sho) plays music at 50% speed; yet Black Book (Kuro no Sho) does not ... one of my favorite games, so it'd be great to fix it --- bsnes/gameboy/Makefile | 3 +- bsnes/gameboy/apu/apu.cpp | 6 +- bsnes/gameboy/cpu/cpu.cpp | 2 +- bsnes/gameboy/cpu/mmio/mmio.cpp | 46 ++++++++++---- bsnes/gameboy/cpu/timing/opcode.cpp | 6 +- bsnes/gameboy/cpu/timing/timing.cpp | 18 +++--- bsnes/gameboy/gameboy.hpp | 1 + bsnes/gameboy/lcd/lcd.cpp | 4 +- bsnes/gameboy/system/serialization.cpp | 1 + bsnes/gameboy/video/video.cpp | 82 ++++++++++++++++++++++++ bsnes/gameboy/video/video.hpp | 17 +++++ bsnes/nes/Makefile | 3 +- bsnes/nes/cartridge/chip/mmc5.cpp | 63 ++++++++++++------- bsnes/nes/nes.hpp | 1 + bsnes/nes/system/serialization.cpp | 2 +- bsnes/nes/video/video.cpp | 87 ++++++++++++++++++++++++++ bsnes/nes/video/video.hpp | 11 ++++ bsnes/snes/system/serialization.cpp | 2 +- bsnes/snes/video/video.cpp | 49 +++++++++++++++ bsnes/snes/video/video.hpp | 8 +++ bsnes/ui/interface/gameboy.cpp | 34 +++------- bsnes/ui/interface/interface.cpp | 15 +++-- bsnes/ui/interface/interface.hpp | 8 +-- bsnes/ui/interface/nes.cpp | 82 +++--------------------- bsnes/ui/interface/nes.hpp | 9 --- bsnes/ui/interface/snes.cpp | 37 +++-------- bsnes/ui/interface/snes.hpp | 6 -- bsnes/ui/main.cpp | 2 +- bsnes/ui/settings/video.cpp | 2 + 29 files changed, 397 insertions(+), 210 deletions(-) create mode 100755 bsnes/gameboy/video/video.cpp create mode 100755 bsnes/gameboy/video/video.hpp create mode 100755 bsnes/nes/video/video.cpp create mode 100755 bsnes/nes/video/video.hpp diff --git a/bsnes/gameboy/Makefile b/bsnes/gameboy/Makefile index 6b55bc6e..977c1050 100755 --- a/bsnes/gameboy/Makefile +++ b/bsnes/gameboy/Makefile @@ -1,7 +1,7 @@ gameboy_objects := gameboy-interface gameboy-system gameboy-scheduler gameboy_objects += gameboy-memory gameboy-cartridge gameboy_objects += gameboy-cpu gameboy-apu gameboy-lcd -gameboy_objects += gameboy-cheat +gameboy_objects += gameboy-cheat gameboy-video objects += $(gameboy_objects) obj/gameboy-interface.o: $(gameboy)/interface/interface.cpp $(call rwildcard,$(gameboy)/interface/) @@ -13,3 +13,4 @@ obj/gameboy-cpu.o: $(gameboy)/cpu/cpu.cpp $(call rwildcard,$(gameboy)/cpu/) obj/gameboy-apu.o: $(gameboy)/apu/apu.cpp $(call rwildcard,$(gameboy)/apu/) obj/gameboy-lcd.o: $(gameboy)/lcd/lcd.cpp $(call rwildcard,$(gameboy)/lcd/) obj/gameboy-cheat.o: $(gameboy)/cheat/cheat.cpp $(call rwildcard,$(gameboy)/cheat/) +obj/gameboy-video.o: $(gameboy)/video/video.cpp $(call rwildcard,$(gameboy)/video/) diff --git a/bsnes/gameboy/apu/apu.cpp b/bsnes/gameboy/apu/apu.cpp index ce2c2d31..9875e0e4 100755 --- a/bsnes/gameboy/apu/apu.cpp +++ b/bsnes/gameboy/apu/apu.cpp @@ -47,14 +47,12 @@ void APU::main() { master.run(); interface->audioSample(master.center, master.left, master.right); - - clock += 2; - if(clock >= 0) co_switch(scheduler.active_thread = cpu.thread); + if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread); } } void APU::power() { - create(Main, 8 * 1024 * 1024); + create(Main, 4 * 1024 * 1024); for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this; for(auto &n : mmio_data) n = 0x00; diff --git a/bsnes/gameboy/cpu/cpu.cpp b/bsnes/gameboy/cpu/cpu.cpp index b846e4a5..306e3aa1 100755 --- a/bsnes/gameboy/cpu/cpu.cpp +++ b/bsnes/gameboy/cpu/cpu.cpp @@ -94,7 +94,7 @@ void CPU::interrupt_exec(uint16 pc) { } void CPU::power() { - create(Main, 8 * 1024 * 1024); + create(Main, 4 * 1024 * 1024); for(unsigned n = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror) diff --git a/bsnes/gameboy/cpu/mmio/mmio.cpp b/bsnes/gameboy/cpu/mmio/mmio.cpp index b123562c..d3104103 100755 --- a/bsnes/gameboy/cpu/mmio/mmio.cpp +++ b/bsnes/gameboy/cpu/mmio/mmio.cpp @@ -3,7 +3,8 @@ unsigned CPU::wram_addr(uint16 addr) const { addr &= 0x1fff; if(addr < 0x1000) return addr; - return (status.wram_bank * 0x1000) + (addr & 0x0fff); + auto bank = status.wram_bank + (status.wram_bank == 0); + return (bank * 0x1000) + (addr & 0x0fff); } void CPU::mmio_joyp_poll() { @@ -82,13 +83,37 @@ uint8 CPU::mmio_read(uint16 addr) { return 0x02; } - if(addr == 0xff6c) return 0xfe | status.ff6c; - if(addr == 0xff72) return status.ff72; - if(addr == 0xff73) return status.ff73; - if(addr == 0xff74) return status.ff74; - if(addr == 0xff75) return 0x8f | status.ff75; - if(addr == 0xff76) return 0x00; - if(addr == 0xff77) return 0x00; + if(addr == 0xff6c) { //??? + return 0xfe | status.ff6c; + } + + if(addr == 0xff70) { //SVBK + return status.wram_bank; + } + + if(addr == 0xff72) { //??? + return status.ff72; + } + + if(addr == 0xff73) { //??? + return status.ff73; + } + + if(addr == 0xff74) { //??? + return status.ff74; + } + + if(addr == 0xff75) { //??? + return 0x8f | status.ff75; + } + + if(addr == 0xff76) { //??? + return 0x00; + } + + if(addr == 0xff77) { //??? + return 0x00; + } if(addr == 0xffff) { //IE return (status.interrupt_enable_joypad << 4) @@ -158,7 +183,7 @@ void CPU::mmio_write(uint16 addr, uint8 data) { if(addr == 0xff46) { //DMA for(unsigned n = 0x00; n <= 0x9f; n++) { bus.write(0xfe00 + n, bus.read((data << 8) + n)); - add_clocks(8); + add_clocks(4); } return; } @@ -194,7 +219,7 @@ void CPU::mmio_write(uint16 addr, uint8 data) { if(status.dma_mode == 0) do { bus.write(status.dma_target++, bus.read(status.dma_source++)); - add_clocks(8); + add_clocks(4); } while(--status.dma_length); return; } @@ -230,7 +255,6 @@ void CPU::mmio_write(uint16 addr, uint8 data) { if(addr == 0xff70) { //SVBK status.wram_bank = data & 0x07; - if(status.wram_bank == 0) status.wram_bank = 1; return; } diff --git a/bsnes/gameboy/cpu/timing/opcode.cpp b/bsnes/gameboy/cpu/timing/opcode.cpp index 135212b7..db6c721f 100755 --- a/bsnes/gameboy/cpu/timing/opcode.cpp +++ b/bsnes/gameboy/cpu/timing/opcode.cpp @@ -2,20 +2,20 @@ void CPU::op_io() { cycle_edge(); - add_clocks(8 >> status.speed_double); + add_clocks(4 >> status.speed_double); } uint8 CPU::op_read(uint16 addr) { cycle_edge(); uint8 r = bus.read(addr); - add_clocks(8 >> status.speed_double); + add_clocks(4 >> status.speed_double); return r; } void CPU::op_write(uint16 addr, uint8 data) { cycle_edge(); bus.write(addr, data); - add_clocks(8 >> status.speed_double); + add_clocks(4 >> status.speed_double); } void CPU::cycle_edge() { diff --git a/bsnes/gameboy/cpu/timing/timing.cpp b/bsnes/gameboy/cpu/timing/timing.cpp index fcdf153f..54e17f08 100755 --- a/bsnes/gameboy/cpu/timing/timing.cpp +++ b/bsnes/gameboy/cpu/timing/timing.cpp @@ -11,17 +11,17 @@ void CPU::add_clocks(unsigned clocks) { scheduler.exit(Scheduler::ExitReason::StepEvent); status.clock += clocks; - if(status.clock >= 8 * 1024 * 1024) { - status.clock -= 8 * 1024 * 1024; + if(status.clock >= 4 * 1024 * 1024) { + status.clock -= 4 * 1024 * 1024; cartridge.mbc3.second(); } - //8MHz / N(hz) - 1 = mask - if((status.clock & 31) == 0) timer_262144hz(); - if((status.clock & 127) == 0) timer_65536hz(); - if((status.clock & 511) == 0) timer_16384hz(); - if((status.clock & 1023) == 0) timer_8192hz(); - if((status.clock & 2047) == 0) timer_4096hz(); + //4MHz / N(hz) - 1 = mask + if((status.clock & 15) == 0) timer_262144hz(); + if((status.clock & 63) == 0) timer_65536hz(); + if((status.clock & 255) == 0) timer_16384hz(); + if((status.clock & 511) == 0) timer_8192hz(); + if((status.clock & 1023) == 0) timer_4096hz(); lcd.clock -= clocks; if(lcd.clock <= 0) co_switch(scheduler.active_thread = lcd.thread); @@ -81,7 +81,7 @@ void CPU::hblank() { if(status.dma_mode == 1 && status.dma_length) { for(unsigned n = 0; n < 16; n++) { bus.write(status.dma_target++, bus.read(status.dma_source++)); - add_clocks(8); + add_clocks(4); } status.dma_length -= 16; } diff --git a/bsnes/gameboy/gameboy.hpp b/bsnes/gameboy/gameboy.hpp index 2f279205..01893319 100755 --- a/bsnes/gameboy/gameboy.hpp +++ b/bsnes/gameboy/gameboy.hpp @@ -108,6 +108,7 @@ namespace GameBoy { #include #include #include + #include }; #endif diff --git a/bsnes/gameboy/lcd/lcd.cpp b/bsnes/gameboy/lcd/lcd.cpp index d4132bb2..b53132a8 100755 --- a/bsnes/gameboy/lcd/lcd.cpp +++ b/bsnes/gameboy/lcd/lcd.cpp @@ -25,7 +25,7 @@ void LCD::main() { scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); } - add_clocks(8); + add_clocks(4); status.lx += 4; if(status.lx >= 456) scanline(); @@ -82,7 +82,7 @@ unsigned LCD::hflip(unsigned data) const { } void LCD::power() { - create(Main, 8 * 1024 * 1024); + create(Main, 4 * 1024 * 1024); for(unsigned n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM for(unsigned n = 0xfe00; n <= 0xfe9f; n++) bus.mmio[n] = this; //OAM diff --git a/bsnes/gameboy/system/serialization.cpp b/bsnes/gameboy/system/serialization.cpp index c61b1593..c7ca0f8b 100755 --- a/bsnes/gameboy/system/serialization.cpp +++ b/bsnes/gameboy/system/serialization.cpp @@ -29,6 +29,7 @@ bool System::unserialize(serializer &s) { if(version != Info::SerializerVersion) return false; //if(crc32 != 0) return false; + power(); serialize_all(s); return true; } diff --git a/bsnes/gameboy/video/video.cpp b/bsnes/gameboy/video/video.cpp new file mode 100755 index 00000000..3cbcd249 --- /dev/null +++ b/bsnes/gameboy/video/video.cpp @@ -0,0 +1,82 @@ +#include + +#define VIDEO_CPP +namespace GameBoy { + +Video video; + +unsigned Video::palette_dmg(unsigned color) const { + unsigned R = monochrome[color][0] * 1023.0; + unsigned G = monochrome[color][1] * 1023.0; + unsigned B = monochrome[color][2] * 1023.0; + + return (R << 20) + (G << 10) + (B << 0); +} + +unsigned Video::palette_sgb(unsigned color) const { + unsigned R = (3 - color) * 341; + unsigned G = (3 - color) * 341; + unsigned B = (3 - color) * 341; + + return (R << 20) + (G << 10) + (B << 0); +} + +unsigned Video::palette_cgb(unsigned color) const { + unsigned r = (color >> 0) & 31; + unsigned g = (color >> 5) & 31; + unsigned b = (color >> 10) & 31; + + unsigned R = (r * 26 + g * 4 + b * 2); + unsigned G = ( g * 24 + b * 8); + unsigned B = (r * 6 + g * 4 + b * 22); + + R = min(960, R); + G = min(960, G); + B = min(960, B); + + return (R << 20) + (G << 10) + (B << 0); +} + +void Video::generate(Format format) { + 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); + + if(format == Format::RGB24) { + for(unsigned n = 0; n < (1 << 15); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 6) & 0xff0000) + ((color >> 4) & 0x00ff00) + ((color >> 2) & 0x0000ff); + } + } + + if(format == Format::RGB16) { + for(unsigned n = 0; n < (1 << 15); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 14) & 0xf800) + ((color >> 9) & 0x07e0) + ((color >> 5) & 0x001f); + } + } + + if(format == Format::RGB15) { + for(unsigned n = 0; n < (1 << 15); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 15) & 0x7c00) + ((color >> 10) & 0x03e0) + ((color >> 5) & 0x001f); + } + } +} + +Video::Video() { + palette = new unsigned[1 << 15]; +} + +Video::~Video() { + delete[] palette; +} + +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 }, +}; + +} diff --git a/bsnes/gameboy/video/video.hpp b/bsnes/gameboy/video/video.hpp new file mode 100755 index 00000000..131b2868 --- /dev/null +++ b/bsnes/gameboy/video/video.hpp @@ -0,0 +1,17 @@ +struct Video { + enum class Format : unsigned { RGB30, RGB24, RGB16, RGB15 }; + unsigned *palette; + + unsigned palette_dmg(unsigned color) const; + unsigned palette_sgb(unsigned color) const; + unsigned palette_cgb(unsigned color) const; + + void generate(Format format); + Video(); + ~Video(); + +private: + static const double monochrome[4][3]; +}; + +extern Video video; diff --git a/bsnes/nes/Makefile b/bsnes/nes/Makefile index 6bf1c78f..ef680573 100755 --- a/bsnes/nes/Makefile +++ b/bsnes/nes/Makefile @@ -1,6 +1,6 @@ nes_objects := nes-interface nes-system nes-scheduler nes-input nes_objects += nes-memory nes-cartridge nes-cpu nes-apu nes-ppu -nes_objects += nes-cheat +nes_objects += nes-cheat nes-video objects += $(nes_objects) obj/nes-interface.o: $(nes)/interface/interface.cpp $(call rwildcard,$(nes)/interface/) @@ -13,3 +13,4 @@ obj/nes-cpu.o: $(nes)/cpu/cpu.cpp $(call rwildcard,$(nes)/cpu/) obj/nes-apu.o: $(nes)/apu/apu.cpp $(call rwildcard,$(nes)/apu/) obj/nes-ppu.o: $(nes)/ppu/ppu.cpp $(call rwildcard,$(nes)/ppu/) obj/nes-cheat.o: $(nes)/cheat/cheat.cpp $(call rwildcard,$(nes)/cheat/) +obj/nes-video.o: $(nes)/video/video.cpp $(call rwildcard,$(nes)/video/) diff --git a/bsnes/nes/cartridge/chip/mmc5.cpp b/bsnes/nes/cartridge/chip/mmc5.cpp index 2c8d8dea..6f62e4f9 100755 --- a/bsnes/nes/cartridge/chip/mmc5.cpp +++ b/bsnes/nes/cartridge/chip/mmc5.cpp @@ -17,7 +17,7 @@ uint2 prgram_write_protect[2]; //$5102,$5103 uint2 exram_mode; //$5104 uint2 nametable_mode[4]; //$5105 uint8 fillmode_tile; //$5106 -uint2 fillmode_color; //$5107 +uint8 fillmode_color; //$5107 bool ram_select; //$5113 uint2 ram_bank; //$5113 @@ -54,6 +54,10 @@ bool sprite_8x16; uint8 exbank; uint8 exattr; +bool vs_fetch; +uint8 vs_vpos; +uint8 vs_hpos; + void main() { while(true) { if(scheduler.sync == Scheduler::SynchronizeMode::All) { @@ -188,6 +192,8 @@ void prg_write(unsigned addr, uint8 data) { case 0x5107: fillmode_color = data & 3; + fillmode_color |= fillmode_color << 2; + fillmode_color |= fillmode_color << 4; break; case 0x5113: @@ -296,6 +302,10 @@ unsigned chr_bg_addr(unsigned addr) { } } +unsigned chr_vs_addr(unsigned addr) { + return (vs_bank * 0x1000) + (addr & 0x0ff8) + (vs_vpos & 7); +} + void blank() { in_frame = false; } @@ -316,11 +326,14 @@ void scanline() { } uint8 ciram_read(unsigned addr) { + if(vs_fetch && (hcounter & 2) == 0) return exram[vs_vpos / 8 * 32 + vs_hpos / 8]; + if(vs_fetch && (hcounter & 2) != 0) return exram[vs_vpos / 32 * 8 + vs_hpos / 32 + 0x03c0]; + switch(nametable_mode[(addr >> 10) & 3]) { case 0: return ppu.ciram_read(0x0000 | (addr & 0x03ff)); case 1: return ppu.ciram_read(0x0400 | (addr & 0x03ff)); case 2: return exram_mode < 2 ? exram[addr & 0x03ff] : 0x00; - case 3: return (hcounter & 2) == 0 ? fillmode_tile : (uint8)fillmode_color; + case 3: return (hcounter & 2) == 0 ? fillmode_tile : fillmode_color; } } @@ -337,14 +350,21 @@ uint8 chr_read(unsigned addr) { && (chr_access[3] & 0x2000)) scanline(); if(in_frame == false) { + vs_fetch = false; if(addr & 0x2000) return ciram_read(addr); - return 0x00; + return board.chrrom.read(chr_active ? chr_bg_addr(addr) : chr_sprite_addr(addr)); } - unsigned mode = nametable_mode[(addr >> 10) & 3]; + bool bg_fetch = (hcounter < 256 || hcounter >= 320); uint8 result = 0x00; if((hcounter & 7) == 0) { + vs_hpos = hcounter >= 320 ? hcounter - 320 : hcounter + 16; + vs_vpos = vcounter + vs_scroll; + vs_fetch = vs_enable && bg_fetch && exram_mode < 2 + && (vs_side ? vs_hpos / 8 >= vs_tile : vs_hpos / 8 < vs_tile); + if(vs_vpos >= 240) vs_vpos -= 240; + result = ciram_read(addr); exbank = (chr_bank_hi << 6) | (exram[addr & 0x03ff] & 0x3f); @@ -353,21 +373,12 @@ uint8 chr_read(unsigned addr) { exattr |= exattr << 4; } else if((hcounter & 7) == 2) { result = ciram_read(addr); - - if((hcounter < 256 || hcounter >= 320) && exram_mode == 1) { - result = exattr; - } + if(bg_fetch && exram_mode == 1) result = exattr; } else { - if(sprite_8x16 == false) { - result = board.chrrom.read(chr_active ? chr_bg_addr(addr) : chr_sprite_addr(addr)); - } - else if(hcounter < 256) result = board.chrrom.read(chr_bg_addr(addr)); - else if(hcounter < 320) result = board.chrrom.read(chr_sprite_addr(addr)); - else /* hcounter < 340*/result = board.chrrom.read(chr_bg_addr(addr)); - - if((hcounter < 256 || hcounter >= 320) && exram_mode == 1) { - result = board.chrrom.read(exbank * 0x1000 + addr); - } + if(vs_fetch) result = board.chrrom.read(chr_vs_addr(addr)); + else if(sprite_8x16 ? bg_fetch : chr_active) result = board.chrrom.read(chr_bg_addr(addr)); + else result = board.chrrom.read(chr_sprite_addr(addr)); + if(bg_fetch && exram_mode == 1) result = board.chrrom.read(exbank * 0x1000 + (addr & 0x0fff)); } hcounter += 2; @@ -376,9 +387,11 @@ uint8 chr_read(unsigned addr) { void chr_write(unsigned addr, uint8 data) { if(addr & 0x2000) { - unsigned mode = nametable_mode[(addr >> 10) & 3]; - if(mode == 0) ppu.ciram_write(0x0000 | (addr & 0x03ff), data); - if(mode == 1) ppu.ciram_write(0x0400 | (addr & 0x03ff), data); + switch(nametable_mode[(addr >> 10) & 3]) { + case 0: return ppu.ciram_write(0x0000 | (addr & 0x03ff), data); + case 1: return ppu.ciram_write(0x0400 | (addr & 0x03ff), data); + case 2: exram[addr & 0x03ff] = data; break; + } } } @@ -426,6 +439,10 @@ void reset() { exbank = 0; exattr = 0; + + vs_fetch = 0; + vs_vpos = 0; + vs_hpos = 0; } void serialize(serializer &s) { @@ -467,6 +484,10 @@ void serialize(serializer &s) { s.integer(exbank); s.integer(exattr); + + s.integer(vs_fetch); + s.integer(vs_vpos); + s.integer(vs_hpos); } MMC5(Board &board) : Chip(board) { diff --git a/bsnes/nes/nes.hpp b/bsnes/nes/nes.hpp index 99fca75a..7e5d0627 100755 --- a/bsnes/nes/nes.hpp +++ b/bsnes/nes/nes.hpp @@ -111,6 +111,7 @@ namespace NES { #include #include #include + #include #include } diff --git a/bsnes/nes/system/serialization.cpp b/bsnes/nes/system/serialization.cpp index 8f867d31..d22ab8f8 100755 --- a/bsnes/nes/system/serialization.cpp +++ b/bsnes/nes/system/serialization.cpp @@ -27,7 +27,7 @@ bool System::unserialize(serializer &s) { if(version != Info::SerializerVersion) return false; //if(crc32 != 0) return false; - reset(); + power(); serialize_all(s); return true; } diff --git a/bsnes/nes/video/video.cpp b/bsnes/nes/video/video.cpp new file mode 100755 index 00000000..f90626a3 --- /dev/null +++ b/bsnes/nes/video/video.cpp @@ -0,0 +1,87 @@ +#include + +#define VIDEO_CPP +namespace NES { + +Video video; + +unsigned Video::palette30( + unsigned n, double saturation, double hue, + double contrast, double brightness, double gamma +) { + signed color = (n & 0x0f), level = color < 0xe ? (n >> 4) & 3 : 1; + + static const double black = 0.518, white = 1.962, attenuation = 0.746; + static const double levels[8] = { + 0.350, 0.518, 0.962, 1.550, + 1.094, 1.506, 1.962, 1.962, + }; + + double lo_and_hi[2] = { + levels[level + 4 * (color == 0x0)], + levels[level + 4 * (color < 0xd)], + }; + + double y = 0.0, i = 0.0, q = 0.0; + auto wave = [](signed p, signed color) { return (color + p + 8) % 12 < 6; }; + for(signed p = 0; p < 12; p++) { + double spot = lo_and_hi[wave(p, color)]; + + if(((n & 0x040) && wave(p, 12)) + || ((n & 0x080) && wave(p, 4)) + || ((n & 0x100) && wave(p, 8)) + ) spot *= attenuation; + + double v = (spot - black) / (white - black); + + v = (v - 0.5) * contrast + 0.5; + v *= brightness / 12.0; + + y += v; + i += v * std::cos((3.141592653 / 6.0) * (p + hue)); + q += v * std::sin((3.141592653 / 6.0) * (p + hue)); + } + + i *= saturation; + q *= saturation; + + auto gammaAdjust = [=](double f) { return f < 0.0 ? 0.0 : std::pow(f, 2.2 / gamma); }; + return (uclamp<10>(1023.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q)) << 20) + + (uclamp<10>(1023.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q)) << 10) + + (uclamp<10>(1023.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q)) << 0); +} + +void Video::generate(Format format) { + for(unsigned n = 0; n < (1 << 9); n++) palette[n] = palette30(n, 2.0, 0.0, 1.0, 1.0, 1.8); + + if(format == Format::RGB24) { + for(unsigned n = 0; n < (1 << 9); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 6) & 0xff0000) + ((color >> 4) & 0x00ff00) + ((color >> 2) & 0x0000ff); + } + } + + if(format == Format::RGB16) { + for(unsigned n = 0; n < (1 << 9); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 14) & 0xf800) + ((color >> 9) & 0x07e0) + ((color >> 5) & 0x001f); + } + } + + if(format == Format::RGB15) { + for(unsigned n = 0; n < (1 << 9); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 15) & 0x7c00) + ((color >> 10) & 0x03e0) + ((color >> 5) & 0x001f); + } + } +} + +Video::Video() { + palette = new unsigned[1 << 9]; +} + +Video::~Video() { + delete[] palette; +} + +} diff --git a/bsnes/nes/video/video.hpp b/bsnes/nes/video/video.hpp new file mode 100755 index 00000000..6fec2bf9 --- /dev/null +++ b/bsnes/nes/video/video.hpp @@ -0,0 +1,11 @@ +struct Video { + enum class Format : unsigned { RGB30, RGB24, RGB16, RGB15 }; + unsigned *palette; + + unsigned palette30(unsigned, double, double, double, double, double); + void generate(Format format); + Video(); + ~Video(); +}; + +extern Video video; diff --git a/bsnes/snes/system/serialization.cpp b/bsnes/snes/system/serialization.cpp index ba12302f..f7d6f3b1 100755 --- a/bsnes/snes/system/serialization.cpp +++ b/bsnes/snes/system/serialization.cpp @@ -34,7 +34,7 @@ bool System::unserialize(serializer &s) { //if(crc32 != cartridge.crc32()) return false; if(strcmp(profile, Info::Profile)) return false; - reset(); + power(); serialize_all(s); return true; } diff --git a/bsnes/snes/video/video.cpp b/bsnes/snes/video/video.cpp index 341f4c15..9666f194 100755 --- a/bsnes/snes/video/video.cpp +++ b/bsnes/snes/video/video.cpp @@ -2,6 +2,55 @@ Video video; +unsigned Video::palette30(unsigned color) { + unsigned l = (color >> 15) & 15; + unsigned b = (color >> 10) & 31; + unsigned g = (color >> 5) & 31; + unsigned r = (color >> 0) & 31; + + double L = (1.0 + l) / 16.0; + unsigned R = L * ((r << 5) + (r << 0)); + unsigned G = L * ((g << 5) + (g << 0)); + unsigned B = L * ((b << 5) + (b << 0)); + + return (R << 20) + (G << 10) + (B << 0); +} + +void Video::generate(Format format) { + for(unsigned n = 0; n < (1 << 19); n++) palette[n] = palette30(n); + + if(format == Format::RGB24) { + for(unsigned n = 0; n < (1 << 19); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 6) & 0xff0000) + ((color >> 4) & 0x00ff00) + ((color >> 2) & 0x0000ff); + } + } + + if(format == Format::RGB16) { + for(unsigned n = 0; n < (1 << 19); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 14) & 0xf800) + ((color >> 9) & 0x07e0) + ((color >> 5) & 0x001f); + } + } + + if(format == Format::RGB15) { + for(unsigned n = 0; n < (1 << 19); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 15) & 0x7c00) + ((color >> 10) & 0x03e0) + ((color >> 5) & 0x001f); + } + } +} + +Video::Video() { + palette = new unsigned[1 << 19]; +} + +Video::~Video() { + delete[] palette; +} + +//internal + const uint8_t Video::cursor[15 * 15] = { 0,0,0,0,0,0,1,1,1,0,0,0,0,0,0, 0,0,0,0,1,1,2,2,2,1,1,0,0,0,0, diff --git a/bsnes/snes/video/video.hpp b/bsnes/snes/video/video.hpp index b044e48f..f01cac65 100755 --- a/bsnes/snes/video/video.hpp +++ b/bsnes/snes/video/video.hpp @@ -1,4 +1,12 @@ struct Video { + enum class Format : unsigned { RGB30, RGB24, RGB16, RGB15 }; + unsigned *palette; + + unsigned palette30(unsigned color); + void generate(Format format); + Video(); + ~Video(); + private: bool hires; unsigned line_width[240]; diff --git a/bsnes/ui/interface/gameboy.cpp b/bsnes/ui/interface/gameboy.cpp index 2f434714..9dc3988e 100755 --- a/bsnes/ui/interface/gameboy.cpp +++ b/bsnes/ui/interface/gameboy.cpp @@ -20,6 +20,7 @@ bool InterfaceGameBoy::loadCartridge(GameBoy::System::Revision revision, const s } GameBoy::interface = this; + GameBoy::video.generate(GameBoy::Video::Format::RGB24); interface->loadCartridge(::Interface::Mode::GameBoy); return true; } @@ -53,35 +54,18 @@ bool InterfaceGameBoy::loadState(const string &filename) { // void InterfaceGameBoy::videoRefresh(const uint16_t *data) { - static uint16_t output[160 * 144]; + static uint32_t output[160 * 144]; - if(GameBoy::system.cgb() == false) { //L2 - static uint32_t palette[] = { - 0x9bbc0f, 0x8bac0f, 0x306230, 0x0f380f - }; - - for(unsigned y = 0; y < 144; y++) { - const uint16_t *sp = data + y * 160; - uint16_t *dp = output + y * 160; - for(unsigned x = 0; x < 160; x++) { - uint32_t color = palette[*sp++]; - *dp++ = ((color & 0xf80000) >> 9) | ((color & 0x00f800) >> 6) | ((color & 0x0000f8) >> 3); - } + for(unsigned y = 0; y < 144; y++) { + const uint16_t *sp = data + y * 160; + uint32_t *dp = output + y * 160; + for(unsigned x = 0; x < 160; x++) { + uint16_t color = *sp++; + *dp++ = GameBoy::video.palette[color]; } } - if(GameBoy::system.cgb() == true) { //BGR555 - for(unsigned y = 0; y < 144; y++) { - const uint16_t *sp = data + y * 160; - uint16_t *dp = output + y * 160; - for(unsigned x = 0; x < 160; x++) { - uint16_t color = *sp++; - *dp++ = ((color >> 10) & 0x001f) | (color & 0x03e0) | ((color << 10) & 0x7c00); - } - } - } - - interface->videoRefresh(output, 160 * 2, 160, 144); + interface->videoRefresh(output, 160 * 4, 160, 144); } void InterfaceGameBoy::audioSample(int16_t csample, int16_t lsample, int16_t rsample) { diff --git a/bsnes/ui/interface/interface.cpp b/bsnes/ui/interface/interface.cpp index 3b766360..bd2231ca 100755 --- a/bsnes/ui/interface/interface.cpp +++ b/bsnes/ui/interface/interface.cpp @@ -7,15 +7,15 @@ Interface *interface = 0; Filter filter; -void Filter::render(const uint16_t *input, unsigned inputPitch, unsigned inputWidth, unsigned inputHeight) { +void Filter::render(const uint32_t *input, unsigned inputPitch, unsigned inputWidth, unsigned inputHeight) { width = inputWidth, height = inputHeight; dl_size(width, height); dl_render(data, pitch, input, inputPitch, inputWidth, inputHeight); } Filter::Filter() { - data = new uint16_t[2048 * 2048]; - pitch = 2048 * sizeof(uint16_t); + data = new uint32_t[2048 * 2048]; + pitch = 2048 * sizeof(uint32_t); } Filter::~Filter() { @@ -215,8 +215,7 @@ bool Interface::loadFile(const string &filename, uint8_t *&data, unsigned &size) return true; } -//RGB555 input -void Interface::videoRefresh(const uint16_t *input, unsigned inputPitch, unsigned width, unsigned height) { +void Interface::videoRefresh(const uint32_t *input, unsigned inputPitch, unsigned width, unsigned height) { uint32_t *output; unsigned outputPitch; @@ -229,13 +228,13 @@ void Interface::videoRefresh(const uint16_t *input, unsigned inputPitch, unsigne } if(video.lock(output, outputPitch, width, height)) { - inputPitch >>= 1, outputPitch >>= 2; + inputPitch >>= 2, outputPitch >>= 2; for(unsigned y = 0; y < height; y++) { - const uint16_t *sp = input + y * inputPitch; + const uint32_t *sp = input + y * inputPitch; uint32_t *dp = output + y * outputPitch; for(unsigned x = 0; x < width; x++) { - *dp++ = palette[*sp++]; + *dp++ = *sp++; //palette[*sp++]; } } diff --git a/bsnes/ui/interface/interface.hpp b/bsnes/ui/interface/interface.hpp index 83f37164..bce54fa9 100755 --- a/bsnes/ui/interface/interface.hpp +++ b/bsnes/ui/interface/interface.hpp @@ -6,12 +6,12 @@ struct Filter : public library { function dl_size; - function dl_render; - void render(const uint16_t*, unsigned, unsigned, unsigned); + function dl_render; + void render(const uint32_t*, unsigned, unsigned, unsigned); Filter(); ~Filter(); - uint16_t *data; + uint32_t *data; unsigned pitch; unsigned width; unsigned height; @@ -47,7 +47,7 @@ struct Interface : property { Interface(); bool loadFile(const string &filename, uint8_t *&data, unsigned &size); - void videoRefresh(const uint16_t *input, unsigned inputPitch, unsigned width, unsigned height); + void videoRefresh(const uint32_t *input, unsigned inputPitch, unsigned width, unsigned height); string baseName; // = "/path/to/cartridge" (no extension) lstring slotName; diff --git a/bsnes/ui/interface/nes.cpp b/bsnes/ui/interface/nes.cpp index 03229b75..5b5c2c0a 100755 --- a/bsnes/ui/interface/nes.cpp +++ b/bsnes/ui/interface/nes.cpp @@ -37,6 +37,7 @@ bool InterfaceNES::loadCartridge(const string &filename) { } interface->loadCartridge(::Interface::Mode::NES); + NES::video.generate(NES::Video::Format::RGB24); return true; } @@ -71,14 +72,14 @@ bool InterfaceNES::loadState(const string &filename) { // void InterfaceNES::videoRefresh(const uint16_t *data) { - static uint16_t output[256 * 240]; + static uint32_t output[256 * 240]; for(unsigned y = 0; y < 240; y++) { const uint16_t *sp = data + y * 256; - uint16_t *dp = output + y * 256; + uint32_t *dp = output + y * 256; for(unsigned x = 0; x < 256; x++) { - uint32_t color = palette[*sp++]; - *dp++ = ((color & 0xf80000) >> 9) | ((color & 0x00f800) >> 6) | ((color & 0x0000f8) >> 3);; + uint32_t color = *sp++; + *dp++ = NES::video.palette[color]; } } @@ -87,7 +88,7 @@ void InterfaceNES::videoRefresh(const uint16_t *data) { unsigned osh = config->video.maskOverscanVertical; for(unsigned y = 0; y < 240; y++) { - uint16_t *dp = output + y * 256; + uint32_t *dp = output + y * 256; if(y < osh || y >= 240 - osh) { memset(dp, 0, 256 * 2); } else { @@ -97,7 +98,7 @@ void InterfaceNES::videoRefresh(const uint16_t *data) { } } - interface->videoRefresh(output, 256 * 2, 256, 240); + interface->videoRefresh(output, 256 * 4, 256, 240); } void InterfaceNES::audioSample(int16_t sample) { @@ -113,72 +114,3 @@ int16_t InterfaceNES::inputPoll(bool port, unsigned device, unsigned id) { if(port == 0 && device == 0) return inputManager->nes.port1.gamepad.poll(id); return 0; } - -// - -//n = BGRCCCCCC (BGR=emphasis bits; C=color) -unsigned InterfaceNES::paletteColor( - unsigned n, double saturation, double hue, - double contrast, double brightness, double gamma -) { - signed color = (n & 0x0f), level = color < 0xe ? (n >> 4) & 3 : 1; - - static const double black = 0.518, white = 1.962, attenuation = 0.746; - static const double levels[8] = { - 0.350, 0.518, 0.962, 1.550, - 1.094, 1.506, 1.962, 1.962, - }; - - double lo_and_hi[2] = { - levels[level + 4 * (color == 0x0)], - levels[level + 4 * (color < 0xd)], - }; - - double y = 0.0, i = 0.0, q = 0.0; - auto wave = [](signed p, signed color) { return (color + p + 8) % 12 < 6; }; - for(signed p = 0; p < 12; p++) { - double spot = lo_and_hi[wave(p, color)]; - - if(((n & 0x040) && wave(p, 12)) - || ((n & 0x080) && wave(p, 4)) - || ((n & 0x100) && wave(p, 8)) - ) spot *= attenuation; - - double v = (spot - black) / (white - black); - - v = (v - 0.5) * contrast + 0.5; - v *= brightness / 12.0; - - y += v; - i += v * std::cos((3.141592653 / 6.0) * (p + hue)); - q += v * std::sin((3.141592653 / 6.0) * (p + hue)); - } - - i *= saturation; - q *= saturation; - - auto gammaAdjust = [=](double f) { return f < 0.0 ? 0.0 : std::pow(f, 2.2 / gamma); }; - return (uclamp<8>(255.0 * gammaAdjust(y + 0.946882 * i + 0.623557 * q)) << 16) - + (uclamp<8>(255.0 * gammaAdjust(y + -0.274788 * i + -0.635691 * q)) << 8) - + (uclamp<8>(255.0 * gammaAdjust(y + -1.108545 * i + 1.709007 * q)) << 0); -} - -InterfaceNES::InterfaceNES() { - for(unsigned n = 0; n < 512; n++) { - palette[n] = paletteColor(n, 2.0); - } - - for(unsigned e = 1; e < 8; e++) { - static const double rfactor[8] = { 1.000, 1.239, 0.794, 1.019, 0.905, 1.023, 0.741, 0.750 }; - static const double gfactor[8] = { 1.000, 0.915, 1.086, 0.980, 1.026, 0.908, 0.987, 0.750 }; - static const double bfactor[8] = { 1.000, 0.743, 0.882, 0.653, 1.277, 0.979, 0.101, 0.750 }; - for(unsigned n = 0; n < 64; n++) { - unsigned c = palette[n]; - uint8_t r = c >> 16, g = c >> 8, b = c >> 0; - r = uclamp<8>((unsigned)(r * rfactor[e])); - g = uclamp<8>((unsigned)(g * gfactor[e])); - b = uclamp<8>((unsigned)(b * bfactor[e])); - palette[e * 64 + n] = (r << 16) | (g << 8) | (b << 0); - } - } -} diff --git a/bsnes/ui/interface/nes.hpp b/bsnes/ui/interface/nes.hpp index 5f28a624..ae7654f6 100755 --- a/bsnes/ui/interface/nes.hpp +++ b/bsnes/ui/interface/nes.hpp @@ -10,13 +10,4 @@ struct InterfaceNES : NES::Interface { void videoRefresh(const uint16_t *data); void audioSample(int16_t sample); int16_t inputPoll(bool port, unsigned device, unsigned id); - - InterfaceNES(); - -private: - unsigned palette[512]; - unsigned paletteColor( - unsigned color, double saturation = 1.0, double hue = 0.0, - double contrast = 1.0, double brightness = 1.0, double gamma = 1.8 - ); }; diff --git a/bsnes/ui/interface/snes.cpp b/bsnes/ui/interface/snes.cpp index 537d92de..88a288ca 100755 --- a/bsnes/ui/interface/snes.cpp +++ b/bsnes/ui/interface/snes.cpp @@ -39,6 +39,7 @@ bool InterfaceSNES::loadCartridge(const string &basename) { loadMemory(); interface->loadCartridge(::Interface::Mode::SNES); + SNES::video.generate(SNES::Video::Format::RGB24); return true; } @@ -63,6 +64,7 @@ bool InterfaceSNES::loadSatellaviewSlottedCartridge(const string &basename, cons loadMemory(); interface->loadCartridge(::Interface::Mode::SNES); + SNES::video.generate(SNES::Video::Format::RGB24); return true; } @@ -87,6 +89,7 @@ bool InterfaceSNES::loadSatellaviewCartridge(const string &basename, const strin loadMemory(); interface->loadCartridge(::Interface::Mode::SNES); + SNES::video.generate(SNES::Video::Format::RGB24); return true; } @@ -115,6 +118,7 @@ bool InterfaceSNES::loadSufamiTurboCartridge(const string &basename, const strin loadMemory(); interface->loadCartridge(::Interface::Mode::SNES); + SNES::video.generate(SNES::Video::Format::RGB24); return true; } @@ -142,6 +146,7 @@ bool InterfaceSNES::loadSuperGameBoyCartridge(const string &basename, const stri loadMemory(); interface->loadCartridge(::Interface::Mode::SNES); + SNES::video.generate(SNES::Video::Format::RGB24); return true; } @@ -194,7 +199,7 @@ bool InterfaceSNES::loadState(const string &filename) { // void InterfaceSNES::videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) { - static uint16_t output[512 * 480]; + static uint32_t output[512 * 480]; unsigned width = 256 << hires; unsigned height = 240 << interlace; @@ -206,9 +211,9 @@ void InterfaceSNES::videoRefresh(const uint32_t *data, bool hires, bool interlac for(unsigned y = 0; y < height; y++) { const uint32_t *sp = data + y * pitch; - uint16_t *dp = output + y * 512; + uint32_t *dp = output + y * 512; for(unsigned x = 0; x < width; x++) { - *dp++ = palette[*sp++]; + *dp++ = SNES::video.palette[*sp++]; } } @@ -217,7 +222,7 @@ void InterfaceSNES::videoRefresh(const uint32_t *data, bool hires, bool interlac unsigned osh = config->video.maskOverscanVertical << interlace; for(unsigned y = 0; y < height; y++) { - uint16_t *dp = output + y * 512; + uint32_t *dp = output + y * 512; if(y < osh || y >= height - osh) { memset(dp, 0, width * 2); } else { @@ -227,7 +232,7 @@ void InterfaceSNES::videoRefresh(const uint32_t *data, bool hires, bool interlac } } - interface->videoRefresh(output, 512 * 2, width, height); + interface->videoRefresh(output, 512 * 4, width, height); } void InterfaceSNES::audioSample(int16_t lsample, int16_t rsample) { @@ -279,25 +284,3 @@ string InterfaceSNES::path(SNES::Cartridge::Slot slot, const string &hint) { void InterfaceSNES::message(const string &text) { MessageWindow::information(*mainWindow, text); } - -InterfaceSNES::InterfaceSNES() { - //{llll bbbbb ggggg rrrrr} -> { rrrrr ggggg bbbbb } - palette = new uint32_t[16 * 32 * 32 * 32]; - for(unsigned l = 0; l < 16; l++) { - for(unsigned r = 0; r < 32; r++) { - for(unsigned g = 0; g < 32; g++) { - for(unsigned b = 0; b < 32; b++) { - double luma = (double)l / 15.0; - unsigned ar = (luma * r + 0.5); - unsigned ag = (luma * g + 0.5); - unsigned ab = (luma * b + 0.5); - palette[(l << 15) + (r << 10) + (g << 5) + (b << 0)] = (ab << 10) + (ag << 5) + (ar << 0); - } - } - } - } -} - -InterfaceSNES::~InterfaceSNES() { - delete[] palette; -} diff --git a/bsnes/ui/interface/snes.hpp b/bsnes/ui/interface/snes.hpp index 4c1a0c81..52711396 100755 --- a/bsnes/ui/interface/snes.hpp +++ b/bsnes/ui/interface/snes.hpp @@ -20,10 +20,4 @@ struct InterfaceSNES : SNES::Interface { string path(SNES::Cartridge::Slot slot, const string &hint); void message(const string &text); - - InterfaceSNES(); - ~InterfaceSNES(); - -private: - unsigned *palette; }; diff --git a/bsnes/ui/main.cpp b/bsnes/ui/main.cpp index 81d5790e..dfbb1986 100755 --- a/bsnes/ui/main.cpp +++ b/bsnes/ui/main.cpp @@ -27,7 +27,7 @@ void Application::run() { } Application::Application(int argc, char **argv) { - title = "bsnes v083.05"; + title = "bsnes v083.06"; application = this; quit = false; diff --git a/bsnes/ui/settings/video.cpp b/bsnes/ui/settings/video.cpp index ce780753..0fd915a6 100755 --- a/bsnes/ui/settings/video.cpp +++ b/bsnes/ui/settings/video.cpp @@ -31,11 +31,13 @@ VideoSettings::VideoSettings() { RadioBox::group(fullScreen[0], fullScreen[1], fullScreen[2]); append(title, ~0, 0, 5); + #if 0 append(colorAdjustment, ~0, 0); append(brightness, ~0, 0); append(contrast, ~0, 0); append(gamma, ~0, 0); append(gammaRamp, ~0, 0, 5); + #endif append(overscanAdjustment, ~0, 0); append(overscanHorizontal, ~0, 0); append(overscanVertical, ~0, 0, 5);