mirror of https://github.com/bsnes-emu/bsnes.git
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
This commit is contained in:
parent
118a393c4c
commit
aaffd000a4
|
@ -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/)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -108,6 +108,7 @@ namespace GameBoy {
|
|||
#include <gameboy/apu/apu.hpp>
|
||||
#include <gameboy/lcd/lcd.hpp>
|
||||
#include <gameboy/cheat/cheat.hpp>
|
||||
#include <gameboy/video/video.hpp>
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
#include <gameboy/gameboy.hpp>
|
||||
|
||||
#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 },
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -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/)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -111,6 +111,7 @@ namespace NES {
|
|||
#include <nes/apu/apu.hpp>
|
||||
#include <nes/ppu/ppu.hpp>
|
||||
#include <nes/cheat/cheat.hpp>
|
||||
#include <nes/video/video.hpp>
|
||||
#include <nes/interface/interface.hpp>
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
#include <nes/nes.hpp>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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++];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
|
||||
struct Filter : public library {
|
||||
function<void (unsigned&, unsigned&)> dl_size;
|
||||
function<void (uint16_t*, unsigned, const uint16_t*, unsigned, unsigned, unsigned)> dl_render;
|
||||
void render(const uint16_t*, unsigned, unsigned, unsigned);
|
||||
function<void (uint32_t*, unsigned, const uint32_t*, unsigned, unsigned, unsigned)> 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> {
|
|||
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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue