From 9fd379613a92764f2fc0828c9bd6bb2e085e8b90 Mon Sep 17 00:00:00 2001 From: byuu Date: Mon, 1 Oct 2007 09:03:44 +0000 Subject: [PATCH] Update to bsnes v024 release. This is an interim release between some major changes to the video mode support, which may take a long time to complete. It also fixes a bug with CGRAM access timing, re-adds the Sufami Turbo load menu, and adds support for the ST-010 coprocessor, used by F1 Race of Champions. To load Sufami Turbo cartridges, stbios.bin must be placed inside a folder named bios in the bsnes folder. There is not currently a warning if this file is missing. --- readme.txt | 9 +- src/Makefile | 19 +- src/base.h | 2 +- src/cart/cart.cpp | 13 +- src/cart/cart.h | 1 + src/cart/cart_header.cpp | 4 + src/cc.bat | 3 +- src/chip/sdd1/sdd1.cpp | 16 +- src/chip/st010/st010.cpp | 86 +++ src/chip/st010/st010.h | 40 ++ src/chip/st010/st010_data.h | 126 ++++ src/chip/st010/st010_op.cpp | 257 ++++++++ src/config/config.cpp | 53 +- src/config/config.h | 4 +- src/interface.h | 1 + src/lib/libui_gtk_control.h | 8 + src/lib/libui_win_control.h | 8 + src/memory/bmemory/bmemory.cpp | 24 +- src/memory/bmemory/bmemory.h | 3 + src/memory/bmemory/bmemory_mapper_generic.cpp | 12 + src/memory/bmemory/bmemory_rw.cpp | 3 + src/ppu/bppu/bppu_mmio.cpp | 12 +- src/snes/snes.cpp | 53 +- src/snes/video/filter_ntsc.cpp | 3 +- src/snes/video/filter_ntsc.h | 2 +- src/snes/video/filter_ntsc_core.cpp | 598 ------------------ src/snes/video/filter_ntsc_core.h | 192 ------ src/snes/video/ntsc/snes_ntsc.c | 251 ++++++++ src/snes/video/ntsc/snes_ntsc.h | 228 +++++++ src/snes/video/ntsc/snes_ntsc_config.h | 26 + src/snes/video/ntsc/snes_ntsc_impl.h | 439 +++++++++++++ src/ui/config.cpp | 106 ++-- src/ui/lui/event.cpp | 26 + src/ui/lui/event.h | 2 + src/ui/lui/main.cpp | 44 +- src/ui/lui/settings/ui_advanced.cpp | 45 +- src/ui/lui/settings/ui_advanced.h | 22 +- src/ui/lui/settings/ui_cheateditor.cpp | 19 +- src/ui/lui/settings/ui_inputconfig.cpp | 29 +- src/ui/lui/settings/ui_rastersettings.cpp | 39 +- src/ui/lui/settings/ui_settings.cpp | 2 + src/ui/lui/settings/ui_videosettings.cpp | 59 ++ src/ui/lui/settings/ui_videosettings.h | 14 + src/ui/lui/ui.cpp | 2 + src/ui/lui/ui.h | 1 + src/ui/lui/ui_main.cpp | 18 +- src/ui/lui/ui_main.h | 3 + 47 files changed, 1906 insertions(+), 1021 deletions(-) create mode 100644 src/chip/st010/st010.cpp create mode 100644 src/chip/st010/st010.h create mode 100644 src/chip/st010/st010_data.h create mode 100644 src/chip/st010/st010_op.cpp delete mode 100644 src/snes/video/filter_ntsc_core.cpp delete mode 100644 src/snes/video/filter_ntsc_core.h create mode 100644 src/snes/video/ntsc/snes_ntsc.c create mode 100644 src/snes/video/ntsc/snes_ntsc.h create mode 100644 src/snes/video/ntsc/snes_ntsc_config.h create mode 100644 src/snes/video/ntsc/snes_ntsc_impl.h create mode 100644 src/ui/lui/settings/ui_videosettings.cpp create mode 100644 src/ui/lui/settings/ui_videosettings.h diff --git a/readme.txt b/readme.txt index 28445a4e..400d1c66 100644 --- a/readme.txt +++ b/readme.txt @@ -1,5 +1,5 @@ bsnes -Version 0.023 +Version 0.024 Author: byuu -------- @@ -72,8 +72,11 @@ Coprocessor used only by SD Gundam GX DSP-4 Coprocessor used only by Top Gear 3000 -ST010 / ST011 / ST018 -SETA coprocessors used by very few games +ST011 +SETA DSP used only by Quick-move Shogi Match with Nidan Rank-holder Morita + +ST018 +SETA RISC CPU used only by Quick-move Shogi Match with Nidan Rank-holder Morita 2 BS-X (Broadcast Satellite) Add-on unit sold only in Japan that played specially-made games that were diff --git a/src/Makefile b/src/Makefile index 1972d264..e03daddf 100644 --- a/src/Makefile +++ b/src/Makefile @@ -13,7 +13,7 @@ endif ifeq ($(PLATFORM),x-gcc-lui) OS = unix CC = gcc -CFLAGS = -O3 -fomit-frame-pointer -ffast-math -DPLATFORM_X -DCOMPILER_GCC -DPROCESSOR_X86 -DUI_LUI `pkg-config --cflags gtk+-2.0` +CFLAGS = -O3 -fomit-frame-pointer -DPLATFORM_X -DCOMPILER_GCC -DPROCESSOR_X86 -DUI_LUI `pkg-config --cflags gtk+-2.0` AS = nasm ASFLAGS = -f elf LIBS = `pkg-config --libs gtk+-2.0` -lXv -lao @@ -24,7 +24,7 @@ endif ifeq ($(PLATFORM),x-gcc-lui-x64) OS = unix CC = gcc -CFLAGS = -O3 -fomit-frame-pointer -ffast-math -DPLATFORM_X -DCOMPILER_GCC -DPROCESSOR_X86_64 -DUI_LUI `pkg-config --cflags gtk+-2.0` +CFLAGS = -O3 -fomit-frame-pointer -DPLATFORM_X -DCOMPILER_GCC -DPROCESSOR_X86_64 -DUI_LUI `pkg-config --cflags gtk+-2.0` AS = yasm ASFLAGS = -f elf64 LIBS = `pkg-config --libs gtk+-2.0` -lXv -lao @@ -155,7 +155,7 @@ OBJECTS = main.$(OBJ) $(LIBCO).$(OBJ) $(LIBUI).$(OBJ) \ reader.$(OBJ) cart.$(OBJ) cheat.$(OBJ) memory.$(OBJ) bmemory.$(OBJ) \ cpu.$(OBJ) scpu.$(OBJ) smp.$(OBJ) ssmp.$(OBJ) bdsp.$(OBJ) ppu.$(OBJ) \ bppu.$(OBJ) snes.$(OBJ) srtc.$(OBJ) sdd1.$(OBJ) c4.$(OBJ) dsp1.$(OBJ) \ - dsp2.$(OBJ) obc1.$(OBJ) + dsp2.$(OBJ) obc1.$(OBJ) st010.$(OBJ) ifeq ($(GZIP_SUPPORT),true) OBJECTS += adler32.$(OBJ) compress.$(OBJ) crc32.$(OBJ) deflate.$(OBJ) \ @@ -259,12 +259,13 @@ snes.$(OBJ): snes/snes.cpp snes/* snes/scheduler/* snes/video/* snes/audio/* sne ##################### ### special chips ### ##################### -srtc.$(OBJ): chip/srtc/srtc.cpp chip/srtc/* -sdd1.$(OBJ): chip/sdd1/sdd1.cpp chip/sdd1/* -c4.$(OBJ) : chip/c4/c4.cpp chip/c4/* -dsp1.$(OBJ): chip/dsp1/dsp1.cpp chip/dsp1/* -dsp2.$(OBJ): chip/dsp2/dsp2.cpp chip/dsp2/* -obc1.$(OBJ): chip/obc1/obc1.cpp chip/obc1/* +srtc.$(OBJ) : chip/srtc/srtc.cpp chip/srtc/* +sdd1.$(OBJ) : chip/sdd1/sdd1.cpp chip/sdd1/* +c4.$(OBJ) : chip/c4/c4.cpp chip/c4/* +dsp1.$(OBJ) : chip/dsp1/dsp1.cpp chip/dsp1/* +dsp2.$(OBJ) : chip/dsp2/dsp2.cpp chip/dsp2/* +obc1.$(OBJ) : chip/obc1/obc1.cpp chip/obc1/* +st010.$(OBJ): chip/st010/st010.cpp chip/st010/* ############ ### zlib ### diff --git a/src/base.h b/src/base.h index f086fe97..eb814610 100644 --- a/src/base.h +++ b/src/base.h @@ -1,4 +1,4 @@ -#define BSNES_VERSION "0.023" +#define BSNES_VERSION "0.024" #define BSNES_TITLE "bsnes v" BSNES_VERSION #define MEMCORE bMemBus diff --git a/src/cart/cart.cpp b/src/cart/cart.cpp index ac1a6026..1d89e054 100644 --- a/src/cart/cart.cpp +++ b/src/cart/cart.cpp @@ -15,12 +15,13 @@ void Cartridge::load_begin(uint cart_type) { info.type = cart_type; - info.srtc = false; - info.sdd1 = false; - info.c4 = false; - info.dsp1 = false; - info.dsp2 = false; - info.obc1 = false; + info.srtc = false; + info.sdd1 = false; + info.c4 = false; + info.dsp1 = false; + info.dsp2 = false; + info.obc1 = false; + info.st010 = false; info.dsp1_mapper = 0; diff --git a/src/cart/cart.h b/src/cart/cart.h index 5ab19607..10d71bd5 100644 --- a/src/cart/cart.h +++ b/src/cart/cart.h @@ -87,6 +87,7 @@ struct { bool dsp1; bool dsp2; bool obc1; + bool st010; uint dsp1_mapper; diff --git a/src/cart/cart_header.cpp b/src/cart/cart_header.cpp index a677ad9a..32a72b4f 100644 --- a/src/cart/cart_header.cpp +++ b/src/cart/cart_header.cpp @@ -69,6 +69,10 @@ uint8 rom_type = rom[info.header_index + ROM_TYPE]; info.obc1 = true; } + if(mapper == 0x30 && rom_type == 0xf6) { + info.st010 = true; + } + info.cart_mmio = info.c4 | info.dsp1 | info.dsp2 | info.obc1; if(rom[info.header_index + RAM_SIZE] & 7) { diff --git a/src/cc.bat b/src/cc.bat index 9ddfe888..6e98adc1 100644 --- a/src/cc.bat +++ b/src/cc.bat @@ -1,3 +1,4 @@ -@make -r PLATFORM=win-visualc-lui +::@make -r PLATFORM=win-visualc-lui +@make -r PLATFORM=win-visualc-lui GZIP_SUPPORT=true JMA_SUPPORT=true @move bsnes.exe ../bsnes.exe>nul @pause \ No newline at end of file diff --git a/src/chip/sdd1/sdd1.cpp b/src/chip/sdd1/sdd1.cpp index ce10d07a..59afd23e 100644 --- a/src/chip/sdd1/sdd1.cpp +++ b/src/chip/sdd1/sdd1.cpp @@ -43,10 +43,10 @@ uint8 b = (addr >> 16) & 0xff; uint8 SDD1::mmio_read(uint16 addr) { switch(addr) { //>>20 == 0x100000 == 1mb - case 0x4804:return (sdd1.index[0] >> 20) & 7; - case 0x4805:return (sdd1.index[1] >> 20) & 7; - case 0x4806:return (sdd1.index[2] >> 20) & 7; - case 0x4807:return (sdd1.index[3] >> 20) & 7; + case 0x4804: return (sdd1.index[0] >> 20) & 7; + case 0x4805: return (sdd1.index[1] >> 20) & 7; + case 0x4806: return (sdd1.index[2] >> 20) & 7; + case 0x4807: return (sdd1.index[3] >> 20) & 7; } return r_cpu->regs.mdr; @@ -60,10 +60,10 @@ void SDD1::mmio_write(uint16 addr, uint8 data) { } break; //<<20 == 0x100000 == 1mb - case 0x4804:sdd1.index[0] = (data & 7) << 20;break; - case 0x4805:sdd1.index[1] = (data & 7) << 20;break; - case 0x4806:sdd1.index[2] = (data & 7) << 20;break; - case 0x4807:sdd1.index[3] = (data & 7) << 20;break; + case 0x4804: sdd1.index[0] = (data & 7) << 20; break; + case 0x4805: sdd1.index[1] = (data & 7) << 20; break; + case 0x4806: sdd1.index[2] = (data & 7) << 20; break; + case 0x4807: sdd1.index[3] = (data & 7) << 20; break; } } diff --git a/src/chip/st010/st010.cpp b/src/chip/st010/st010.cpp new file mode 100644 index 00000000..f65b7d6b --- /dev/null +++ b/src/chip/st010/st010.cpp @@ -0,0 +1,86 @@ +#include "../../base.h" +#include "st010_data.h" +#include "st010_op.cpp" + +ST010 *st010; + +int16 ST010::sin(int16 theta) { + return sin_table[(theta >> 8) & 0xff]; +} + +int16 ST010::cos(int16 theta) { + return sin_table[((theta + 0x4000) >> 8) & 0xff]; +} + +uint8 ST010::readb(uint16 addr) { + return ram[addr & 0xfff]; +} + +uint16 ST010::readw(uint16 addr) { + return (readb(addr + 0) << 0) | + (readb(addr + 1) << 8); +} + +uint32 ST010::readd(uint16 addr) { + return (readb(addr + 0) << 0) | + (readb(addr + 1) << 8) | + (readb(addr + 2) << 16) | + (readb(addr + 3) << 24); +} + +void ST010::writeb(uint16 addr, uint8 data) { + ram[addr & 0xfff] = data; +} + +void ST010::writew(uint16 addr, uint16 data) { + writeb(addr + 0, data); + writeb(addr + 1, data >> 8); +} + +void ST010::writed(uint16 addr, uint32 data) { + writeb(addr + 0, data); + writeb(addr + 1, data >> 8); + writeb(addr + 2, data >> 16); + writeb(addr + 3, data >> 24); +} + +// + +void ST010::init() { +} + +void ST010::enable() { +} + +void ST010::power() { + reset(); +} + +void ST010::reset() { + memset(ram, 0x00, sizeof ram); +} + +// + +uint8 ST010::read(uint16 addr) { + return readb(addr); +} + +void ST010::write(uint16 addr, uint8 data) { + writeb(addr, data); + + if(addr == 0x0021 && data & 0x80) { + switch(ram[0x0020]) { + case 0x01: op_01(); break; + case 0x02: op_02(); break; + case 0x03: op_03(); break; + case 0x04: op_04(); break; + case 0x05: op_05(); break; + case 0x06: op_06(); break; + case 0x07: op_07(); break; + case 0x08: op_08(); break; + } + + ram[0x0021] &= ~0x80; + } +} diff --git a/src/chip/st010/st010.h b/src/chip/st010/st010.h new file mode 100644 index 00000000..f57bbdfe --- /dev/null +++ b/src/chip/st010/st010.h @@ -0,0 +1,40 @@ +class ST010 { public: +uint8 ram[0x1000]; +static const int16 sin_table[256]; +static const int16 mode7_scale[176]; +static const uint8 arctan[32][32]; +//interfaces to sin table + int16 sin(int16 theta); + int16 cos(int16 theta); + +//interfaces to ram buffer + uint8 readb (uint16 addr); + uint16 readw (uint16 addr); + uint32 readd (uint16 addr); + void writeb(uint16 addr, uint8 data); + void writew(uint16 addr, uint16 data); + void writed(uint16 addr, uint32 data); + +//opcodes + void op_01(); + void op_02(); + void op_03(); + void op_04(); + void op_05(); + void op_06(); + void op_07(); + void op_08(); + + void op_01(int16 x0, int16 y0, int16 &x1, int16 &y1, int16 &quadrant, int16 &theta); + +//base + void init(); + void enable(); + void power(); + void reset(); + + uint8 read (uint16 addr); + void write(uint16 addr, uint8 data); +}; + +extern ST010 *st010; diff --git a/src/chip/st010/st010_data.h b/src/chip/st010/st010_data.h new file mode 100644 index 00000000..df7a00cf --- /dev/null +++ b/src/chip/st010/st010_data.h @@ -0,0 +1,126 @@ +const int16 ST010::sin_table[256] = { + 0x0000, 0x0324, 0x0648, 0x096a, 0x0c8c, 0x0fab, 0x12c8, 0x15e2, + 0x18f9, 0x1c0b, 0x1f1a, 0x2223, 0x2528, 0x2826, 0x2b1f, 0x2e11, + 0x30fb, 0x33df, 0x36ba, 0x398c, 0x3c56, 0x3f17, 0x41ce, 0x447a, + 0x471c, 0x49b4, 0x4c3f, 0x4ebf, 0x5133, 0x539b, 0x55f5, 0x5842, + 0x5a82, 0x5cb3, 0x5ed7, 0x60eb, 0x62f1, 0x64e8, 0x66cf, 0x68a6, + 0x6a6d, 0x6c23, 0x6dc9, 0x6f5e, 0x70e2, 0x7254, 0x73b5, 0x7504, + 0x7641, 0x776b, 0x7884, 0x7989, 0x7a7c, 0x7b5c, 0x7c29, 0x7ce3, + 0x7d89, 0x7e1d, 0x7e9c, 0x7f09, 0x7f61, 0x7fa6, 0x7fd8, 0x7ff5, + 0x7fff, 0x7ff5, 0x7fd8, 0x7fa6, 0x7f61, 0x7f09, 0x7e9c, 0x7e1d, + 0x7d89, 0x7ce3, 0x7c29, 0x7b5c, 0x7a7c, 0x7989, 0x7884, 0x776b, + 0x7641, 0x7504, 0x73b5, 0x7254, 0x70e2, 0x6f5e, 0x6dc9, 0x6c23, + 0x6a6d, 0x68a6, 0x66cf, 0x64e8, 0x62f1, 0x60eb, 0x5ed7, 0x5cb3, + 0x5a82, 0x5842, 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4, + 0x471c, 0x447a, 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33df, + 0x30fb, 0x2e11, 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f1a, 0x1c0b, + 0x18f8, 0x15e2, 0x12c8, 0x0fab, 0x0c8c, 0x096a, 0x0648, 0x0324, + 0x0000, -0x0324, -0x0648, -0x096b, -0x0c8c, -0x0fab, -0x12c8, -0x15e2, + -0x18f9, -0x1c0b, -0x1f1a, -0x2223, -0x2528, -0x2826, -0x2b1f, -0x2e11, + -0x30fb, -0x33df, -0x36ba, -0x398d, -0x3c56, -0x3f17, -0x41ce, -0x447a, + -0x471c, -0x49b4, -0x4c3f, -0x4ebf, -0x5133, -0x539b, -0x55f5, -0x5842, + -0x5a82, -0x5cb3, -0x5ed7, -0x60ec, -0x62f1, -0x64e8, -0x66cf, -0x68a6, + -0x6a6d, -0x6c23, -0x6dc9, -0x6f5e, -0x70e2, -0x7254, -0x73b5, -0x7504, + -0x7641, -0x776b, -0x7884, -0x7989, -0x7a7c, -0x7b5c, -0x7c29, -0x7ce3, + -0x7d89, -0x7e1d, -0x7e9c, -0x7f09, -0x7f61, -0x7fa6, -0x7fd8, -0x7ff5, + -0x7fff, -0x7ff5, -0x7fd8, -0x7fa6, -0x7f61, -0x7f09, -0x7e9c, -0x7e1d, + -0x7d89, -0x7ce3, -0x7c29, -0x7b5c, -0x7a7c, -0x7989, -0x7883, -0x776b, + -0x7641, -0x7504, -0x73b5, -0x7254, -0x70e2, -0x6f5e, -0x6dc9, -0x6c23, + -0x6a6d, -0x68a6, -0x66cf, -0x64e8, -0x62f1, -0x60eb, -0x5ed7, -0x5cb3, + -0x5a82, -0x5842, -0x55f5, -0x539a, -0x5133, -0x4ebf, -0x4c3f, -0x49b3, + -0x471c, -0x447a, -0x41cd, -0x3f17, -0x3c56, -0x398c, -0x36b9, -0x33de, + -0x30fb, -0x2e10, -0x2b1f, -0x2826, -0x2527, -0x2223, -0x1f19, -0x1c0b, + -0x18f8, -0x15e2, -0x12c8, -0x0fab, -0x0c8b, -0x096a, -0x0647, -0x0324 +}; + +const int16 ST010::mode7_scale[176] = { + 0x0380, 0x0325, 0x02da, 0x029c, 0x0268, 0x023b, 0x0215, 0x01f3, + 0x01d5, 0x01bb, 0x01a3, 0x018e, 0x017b, 0x016a, 0x015a, 0x014b, + 0x013e, 0x0132, 0x0126, 0x011c, 0x0112, 0x0109, 0x0100, 0x00f8, + 0x00f0, 0x00e9, 0x00e3, 0x00dc, 0x00d6, 0x00d1, 0x00cb, 0x00c6, + 0x00c1, 0x00bd, 0x00b8, 0x00b4, 0x00b0, 0x00ac, 0x00a8, 0x00a5, + 0x00a2, 0x009e, 0x009b, 0x0098, 0x0095, 0x0093, 0x0090, 0x008d, + 0x008b, 0x0088, 0x0086, 0x0084, 0x0082, 0x0080, 0x007e, 0x007c, + 0x007a, 0x0078, 0x0076, 0x0074, 0x0073, 0x0071, 0x006f, 0x006e, + 0x006c, 0x006b, 0x0069, 0x0068, 0x0067, 0x0065, 0x0064, 0x0063, + 0x0062, 0x0060, 0x005f, 0x005e, 0x005d, 0x005c, 0x005b, 0x005a, + 0x0059, 0x0058, 0x0057, 0x0056, 0x0055, 0x0054, 0x0053, 0x0052, + 0x0051, 0x0051, 0x0050, 0x004f, 0x004e, 0x004d, 0x004d, 0x004c, + 0x004b, 0x004b, 0x004a, 0x0049, 0x0048, 0x0048, 0x0047, 0x0047, + 0x0046, 0x0045, 0x0045, 0x0044, 0x0044, 0x0043, 0x0042, 0x0042, + 0x0041, 0x0041, 0x0040, 0x0040, 0x003f, 0x003f, 0x003e, 0x003e, + 0x003d, 0x003d, 0x003c, 0x003c, 0x003b, 0x003b, 0x003a, 0x003a, + 0x003a, 0x0039, 0x0039, 0x0038, 0x0038, 0x0038, 0x0037, 0x0037, + 0x0036, 0x0036, 0x0036, 0x0035, 0x0035, 0x0035, 0x0034, 0x0034, + 0x0034, 0x0033, 0x0033, 0x0033, 0x0032, 0x0032, 0x0032, 0x0031, + 0x0031, 0x0031, 0x0030, 0x0030, 0x0030, 0x0030, 0x002f, 0x002f, + 0x002f, 0x002e, 0x002e, 0x002e, 0x002e, 0x002d, 0x002d, 0x002d, + 0x002d, 0x002c, 0x002c, 0x002c, 0x002c, 0x002b, 0x002b, 0x002b +}; + +const uint8 ST010::arctan[32][32] = { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x80, 0xa0, 0xad, 0xb3, 0xb6, 0xb8, 0xb9, 0xba, 0xbb, 0xbb, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf }, + { 0x80, 0x93, 0xa0, 0xa8, 0xad, 0xb0, 0xb3, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd }, + { 0x80, 0x8d, 0x98, 0xa0, 0xa6, 0xaa, 0xad, 0xb0, 0xb1, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb7, 0xb8, + 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, 0xbc }, + { 0x80, 0x8a, 0x93, 0x9a, 0xa0, 0xa5, 0xa8, 0xab, 0xad, 0xaf, 0xb0, 0xb2, 0xb3, 0xb4, 0xb5, 0xb5, + 0xb6, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb }, + { 0x80, 0x88, 0x90, 0x96, 0x9b, 0xa0, 0xa4, 0xa7, 0xa9, 0xab, 0xad, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb4, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9 }, + { 0x80, 0x87, 0x8d, 0x93, 0x98, 0x9c, 0xa0, 0xa3, 0xa6, 0xa8, 0xaa, 0xac, 0xad, 0xae, 0xb0, 0xb0, + 0xb1, 0xb2, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8 }, + { 0x80, 0x86, 0x8b, 0x90, 0x95, 0x99, 0x9d, 0xa0, 0xa3, 0xa5, 0xa7, 0xa9, 0xaa, 0xac, 0xad, 0xae, + 0xaf, 0xb0, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7 }, + { 0x80, 0x85, 0x8a, 0x8f, 0x93, 0x97, 0x9a, 0x9d, 0xa0, 0xa2, 0xa5, 0xa6, 0xa8, 0xaa, 0xab, 0xac, + 0xad, 0xae, 0xaf, 0xb0, 0xb0, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb5 }, + { 0x80, 0x85, 0x89, 0x8d, 0x91, 0x95, 0x98, 0x9b, 0x9e, 0xa0, 0xa0, 0xa4, 0xa6, 0xa7, 0xa9, 0xaa, + 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4 }, + { 0x80, 0x84, 0x88, 0x8c, 0x90, 0x93, 0x96, 0x99, 0x9b, 0x9e, 0xa0, 0xa2, 0xa4, 0xa5, 0xa7, 0xa8, + 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xaf, 0xb0, 0xb0, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3 }, + { 0x80, 0x84, 0x87, 0x8b, 0x8e, 0x91, 0x94, 0x97, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa3, 0xa5, 0xa6, + 0xa7, 0xa9, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xae, 0xae, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1, 0xb2, 0xb2 }, + { 0x80, 0x83, 0x87, 0x8a, 0x8d, 0x90, 0x93, 0x96, 0x98, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa3, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xae, 0xae, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1 }, + { 0x80, 0x83, 0x86, 0x89, 0x8c, 0x8f, 0x92, 0x94, 0x96, 0x99, 0x9b, 0x9d, 0x9e, 0xa0, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xae, 0xae, 0xaf, 0xaf, 0xb0 }, + { 0x80, 0x83, 0x86, 0x89, 0x8b, 0x8e, 0x90, 0x93, 0x95, 0x97, 0x99, 0x9b, 0x9d, 0x9e, 0xa0, 0xa1, + 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xac, 0xad, 0xad, 0xae, 0xae, 0xaf }, + { 0x80, 0x83, 0x85, 0x88, 0x8b, 0x8d, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9b, 0x9d, 0x9f, 0xa0, + 0xa1, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa, 0xab, 0xab, 0xac, 0xad, 0xad, 0xae }, + { 0x80, 0x83, 0x85, 0x88, 0x8a, 0x8c, 0x8f, 0x91, 0x93, 0x95, 0x97, 0x99, 0x9a, 0x9c, 0x9d, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa5, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xab, 0xac, 0xad }, + { 0x80, 0x82, 0x85, 0x87, 0x89, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x97, 0x99, 0x9b, 0x9c, 0x9d, + 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xac }, + { 0x80, 0x82, 0x85, 0x87, 0x89, 0x8b, 0x8d, 0x8f, 0x91, 0x93, 0x95, 0x96, 0x98, 0x99, 0x9b, 0x9c, + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab }, + { 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x95, 0x97, 0x98, 0x9a, 0x9b, + 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa }, + { 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x91, 0x93, 0x94, 0x96, 0x97, 0x99, 0x9a, + 0x9b, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa9 }, + { 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8b, 0x8d, 0x8f, 0x90, 0x92, 0x94, 0x95, 0x97, 0x98, 0x99, + 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8 }, + { 0x80, 0x82, 0x84, 0x86, 0x87, 0x89, 0x8b, 0x8d, 0x8e, 0x90, 0x91, 0x93, 0x94, 0x96, 0x97, 0x98, + 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa3, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7 }, + { 0x80, 0x82, 0x84, 0x85, 0x87, 0x89, 0x8a, 0x8c, 0x8e, 0x8f, 0x91, 0x92, 0x94, 0x95, 0x96, 0x98, + 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa5, 0xa5, 0xa6 }, + { 0x80, 0x82, 0x83, 0x85, 0x87, 0x88, 0x8a, 0x8c, 0x8d, 0x8f, 0x90, 0x92, 0x93, 0x94, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa5, 0xa5 }, + { 0x80, 0x82, 0x83, 0x85, 0x86, 0x88, 0x8a, 0x8b, 0x8d, 0x8e, 0x90, 0x91, 0x92, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa4 }, + { 0x80, 0x82, 0x83, 0x85, 0x86, 0x88, 0x89, 0x8b, 0x8c, 0x8e, 0x8f, 0x90, 0x92, 0x93, 0x94, 0x95, + 0x96, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4 }, + { 0x80, 0x82, 0x83, 0x85, 0x86, 0x87, 0x89, 0x8a, 0x8c, 0x8d, 0x8e, 0x90, 0x91, 0x92, 0x93, 0x95, + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9e, 0x9f, 0xa0, 0xa1, 0xa1, 0xa2, 0xa3 }, + { 0x80, 0x81, 0x83, 0x84, 0x86, 0x87, 0x89, 0x8a, 0x8b, 0x8d, 0x8e, 0x8f, 0x90, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9e, 0x9f, 0xa0, 0xa1, 0xa1, 0xa2 }, + { 0x80, 0x81, 0x83, 0x84, 0x86, 0x87, 0x88, 0x8a, 0x8b, 0x8c, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1, 0xa1 }, + { 0x80, 0x81, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8b, 0x8c, 0x8d, 0x8e, 0x90, 0x91, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1 }, + { 0x80, 0x81, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8a, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, + 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0 } +}; diff --git a/src/chip/st010/st010_op.cpp b/src/chip/st010/st010_op.cpp new file mode 100644 index 00000000..4219edb6 --- /dev/null +++ b/src/chip/st010/st010_op.cpp @@ -0,0 +1,257 @@ +//ST-010 emulation code - Copyright (C) 2003 The Dumper, Matthew Kendora, Overload, Feather +//bsnes port - Copyright (C) 2007 byuu + +void ST010::op_01(int16 x0, int16 y0, int16 &x1, int16 &y1, int16 &quadrant, int16 &theta) { + if((x0 < 0) && (y0 < 0)) { + x1 = -x0; + y1 = -y0; + quadrant = -0x8000; + } else if(x0 < 0) { + x1 = y0; + y1 = -x0; + quadrant = -0x4000; + } else if(y0 < 0) { + x1 = -y0; + y1 = x0; + quadrant = 0x4000; + } else { + x1 = x0; + y1 = y0; + quadrant = 0x0000; + } + + while((x1 > 0x1f) || (y1 > 0x1f)) { + if(x1 > 1) { x1 >>= 1; } + if(y1 > 1) { y1 >>= 1; } + } + + if(y1 == 0) { quadrant += 0x4000; } + + theta = (arctan[y1][x1] << 8) ^ quadrant; +} + +// + +void ST010::op_01() { +int16 x0 = readw(0x0000); +int16 y0 = readw(0x0002); +int16 x1, y1, quadrant, theta; + + op_01(x0, y0, x1, y1, quadrant, theta); + + writew(0x0000, x1); + writew(0x0002, y1); + writew(0x0004, quadrant); +//writew(0x0006, y0); //Overload's docs note this write occurs, SNES9x disagrees + writew(0x0010, theta); +} + +void ST010::op_02() { +int16 positions = readw(0x0024); +uint16 *places = (uint16*)(ram + 0x0040); +uint16 *drivers = (uint16*)(ram + 0x0080); + +bool sorted; +uint16 temp; + if(positions > 1) { + do { + sorted = true; + for(int i = 0; i < positions - 1; i++) { + if(places[i] < places[i + 1]) { + temp = places[i + 1]; + places[i + 1] = places[i]; + places[i] = temp; + + temp = drivers[i + 1]; + drivers[i + 1] = drivers[i]; + drivers[i] = temp; + + sorted = false; + } + } + positions--; + } while(!sorted); + } +} + +void ST010::op_03() { +int16 x0 = readw(0x0000); +int16 y0 = readw(0x0002); +int16 multiplier = readw(0x0004); +int32 x1, y1; + + x1 = x0 * multiplier << 1; + y1 = y0 * multiplier << 1; + + writed(0x0010, x1); + writed(0x0014, y1); +} + +void ST010::op_04() { +int16 x = readw(0x0000); +int16 y = readw(0x0002); +int16 square; +//calculate the vector length of (x,y) + square = (int16)sqrt((double)(y * y + x * x)); + + writew(0x0010, square); +} + +void ST010::op_05() { +int32 dx, dy; +int16 a1, b1, c1; +uint16 o1; +bool wrap = false; + +//target (x,y) coordinates +int16 ypos_max = readw(0x00c0); +int16 xpos_max = readw(0x00c2); + +//current coordinates and direction +int32 ypos = readd(0x00c4); +int32 xpos = readd(0x00c8); +uint16 rot = readw(0x00cc); + +//physics +uint16 speed = readw(0x00d4); +uint16 accel = readw(0x00d6); +uint16 speed_max = readw(0x00d8); + +//special condition acknowledgement +int16 system = readw(0x00da); +int16 flags = readw(0x00dc); + +//new target coordinates +int16 ypos_new = readw(0x00de); +int16 xpos_new = readw(0x00e0); + +//mask upper bit + xpos_new &= 0x7fff; + +//get the current distance + dx = xpos_max - (xpos >> 16); + dy = ypos_max - (ypos >> 16); + +//quirk: clear and move in9 + writew(0x00d2, 0xffff); + writew(0x00da, 0x0000); + +//grab the target angle + op_01(dy, dx, a1, b1, c1, (int16&)o1); + +//check for wrapping + if(abs(o1 - rot) > 0x8000) { + o1 += 0x8000; + rot += 0x8000; + wrap = true; + } + +uint16 old_speed = speed; + +//special case + if(abs(o1 - rot) == 0x8000) { + speed = 0x100; + } + +//slow down for sharp curves + else if(abs(o1 - rot) >= 0x1000) { + uint32 slow = abs(o1 - rot); + slow >>= 4; //scaling + speed -= slow; + } + +//otherwise accelerate + else { + speed += accel; + if(speed > speed_max) { + speed = speed_max; //clip speed + } + } + +//prevent negative/positive overflow + if(abs(old_speed - speed) > 0x8000) { + if(old_speed < speed) { speed = 0; } + else speed = 0xff00; + } + +//adjust direction by so many degrees +//be careful of negative adjustments + if((o1 > rot && (o1 - rot) > 0x80) || (o1 < rot && (rot - o1) >= 0x80)) { + if(o1 < rot) { rot -= 0x280; } + else if(o1 > rot) { rot += 0x280; } + } + +//turn of wrapping + if(wrap) { rot -= 0x8000; } + +//now check the distances (store for later) + dx = (xpos_max << 16) - xpos; + dy = (ypos_max << 16) - ypos; + dx >>= 16; + dy >>= 16; + +//if we're in so many units of the target, signal it + if((system && (dy <= 6 && dy >= -8) && (dx <= 126 && dx >= -128)) || (!system && (dx <= 6 && dx >= -8) && (dy <= 126 && dy >= -128))) { + //announce our new destination and flag it + xpos_max = xpos_new & 0x7fff; + ypos_max = ypos_new; + flags |= 0x08; + } + +//update position + xpos -= (cos(rot) * 0x400 >> 15) * (speed >> 8) << 1; + ypos -= (sin(rot) * 0x400 >> 15) * (speed >> 8) << 1; + +//quirk: mask upper byte + xpos &= 0x1fffffff; + ypos &= 0x1fffffff; + + writew(0x00c0, ypos_max); + writew(0x00c2, xpos_max); + writed(0x00c4, ypos); + writed(0x00c8, xpos); + writew(0x00cc, rot); + writew(0x00d4, speed); + writew(0x00dc, flags); +} + +void ST010::op_06() { +int16 multiplicand = readw(0x0000); +int16 multiplier = readw(0x0002); +int32 product; + + product = multiplicand * multiplier << 1; + + writed(0x0010, product); +} + +void ST010::op_07() { +int16 theta = readw(0x0000); + +int16 data; + for(int i = 0, offset = 0; i < 176; i++) { + data = mode7_scale[i] * cos(theta) >> 15; + writew(0x00f0 + offset, data); + writew(0x0510 + offset, data); + + data = mode7_scale[i] * sin(theta) >> 15; + writew(0x0250 + offset, data); + if(data) { data = ~data; } + writew(0x03b0 + offset, data); + + offset += 2; + } +} + +void ST010::op_08() { +int16 x0 = readw(0x0000); +int16 y0 = readw(0x0002); +int16 theta = readw(0x0004); +int16 x1, y1; + + x1 = (y0 * sin(theta) >> 15) + (x0 * cos(theta) >> 15); + y1 = (y0 * cos(theta) >> 15) - (x0 * sin(theta) >> 15); + + writew(0x0010, x1); + writew(0x0012, y1); +} diff --git a/src/config/config.cpp b/src/config/config.cpp index 85dd2619..0ad007b9 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -1,7 +1,10 @@ -Config config_file; - namespace config { +Config& config() { +static Config config; + return config; +} + string file_updatepath(const char *req_file, const char *req_path) { string file(req_file); replace(file, "\\", "/"); @@ -27,69 +30,69 @@ stringarray part; StringSetting Path::base(0, "fs.base_path", "Path that bsnes resides in", ""); -StringSetting Path::rom(&config_file, "path.rom", +StringSetting Path::rom(&config(), "path.rom", "Default path to look for ROM files in (\"\" = use default directory)", ""); -StringSetting Path::save(&config_file, "path.save", +StringSetting Path::save(&config(), "path.save", "Default path for all save RAM and cheat files (\"\" = use current directory)", ""); -StringSetting Path::bios(&config_file, "path.bios", +StringSetting Path::bios(&config(), "path.bios", "Path where BIOS file(s) are located\n" "Supported BIOS files:\n" "stbios.bin - Bandai Sufami Turbo" "", "./bios"); -StringSetting Path::save_ext(&config_file, "path.save_ext", +StringSetting Path::save_ext(&config(), "path.save_ext", "Extension to be used for all save RAM files", "srm"); -IntegerSetting SNES::gamma_ramp(&config_file, "snes.colorfilter.gamma_ramp", +IntegerSetting SNES::gamma_ramp(&config(), "snes.colorfilter.gamma_ramp", "Use precalculated TV-style gamma ramp", IntegerSetting::Boolean, true); -IntegerSetting SNES::sepia(&config_file, "snes.colorfilter.sepia", +IntegerSetting SNES::sepia(&config(), "snes.colorfilter.sepia", "Convert color to sepia tone", IntegerSetting::Boolean, false); -IntegerSetting SNES::grayscale(&config_file, "snes.colorfilter.grayscale", +IntegerSetting SNES::grayscale(&config(), "snes.colorfilter.grayscale", "Convert color to grayscale tone", IntegerSetting::Boolean, false); -IntegerSetting SNES::invert(&config_file, "snes.colorfilter.invert", +IntegerSetting SNES::invert(&config(), "snes.colorfilter.invert", "Invert output image colors", IntegerSetting::Boolean, false); -IntegerSetting SNES::contrast(&config_file, "snes.colorfilter.contrast", +IntegerSetting SNES::contrast(&config(), "snes.colorfilter.contrast", "Contrast", IntegerSetting::Decimal, 0); -IntegerSetting SNES::brightness(&config_file, "snes.colorfilter.brightness", +IntegerSetting SNES::brightness(&config(), "snes.colorfilter.brightness", "Brightness", IntegerSetting::Decimal, 0); -IntegerSetting SNES::gamma(&config_file, "snes.colorfilter.gamma", +IntegerSetting SNES::gamma(&config(), "snes.colorfilter.gamma", "Gamma", IntegerSetting::Decimal, 80); -IntegerSetting SNES::ntsc_merge_fields(&config_file, "snes.ntsc_merge_fields", +IntegerSetting SNES::ntsc_merge_fields(&config(), "snes.ntsc_merge_fields", "Merge fields in NTSC video filter\n" "Set to true if using filter at any refresh rate other than 60hz\n" "", IntegerSetting::Boolean, true); -IntegerSetting SNES::mute(&config_file, "snes.mute", "Mutes SNES audio output when enabled", +IntegerSetting SNES::mute(&config(), "snes.mute", "Mutes SNES audio output when enabled", IntegerSetting::Boolean, false); -IntegerSetting SNES::controller_port0(&config_file, "snes.controller_port_1", +IntegerSetting SNES::controller_port0(&config(), "snes.controller_port_1", "Controller attached to SNES port 1", IntegerSetting::Decimal, ::SNES::DEVICEID_JOYPAD1); -IntegerSetting SNES::controller_port1(&config_file, "snes.controller_port_2", +IntegerSetting SNES::controller_port1(&config(), "snes.controller_port_2", "Controller attached to SNES port 2", IntegerSetting::Decimal, ::SNES::DEVICEID_JOYPAD2); -IntegerSetting CPU::ntsc_clock_rate(&config_file, "cpu.ntsc_clock_rate", +IntegerSetting CPU::ntsc_clock_rate(&config(), "cpu.ntsc_clock_rate", "NTSC S-CPU clock rate (in hz)", IntegerSetting::Decimal, 21477272); -IntegerSetting CPU::pal_clock_rate(&config_file, "cpu.pal_clock_rate", +IntegerSetting CPU::pal_clock_rate(&config(), "cpu.pal_clock_rate", "PAL S-CPU clock rate (in hz)", IntegerSetting::Decimal, 21281370); IntegerSetting CPU::hdma_enable(0, "cpu.hdma_enable", "Enable HDMA effects", IntegerSetting::Boolean, true); -IntegerSetting SMP::ntsc_clock_rate(&config_file, "smp.ntsc_clock_rate", +IntegerSetting SMP::ntsc_clock_rate(&config(), "smp.ntsc_clock_rate", "NTSC S-SMP clock rate (in hz)", IntegerSetting::Decimal, 24606720); -IntegerSetting SMP::pal_clock_rate(&config_file, "smp.pal_clock_rate", +IntegerSetting SMP::pal_clock_rate(&config(), "smp.pal_clock_rate", "PAL S-SMP clock rate (in hz)", IntegerSetting::Decimal, 24606720); -IntegerSetting PPU::Hack::render_scanline_position(&config_file, "ppu.hack.render_scanline_position", +IntegerSetting PPU::Hack::render_scanline_position(&config(), "ppu.hack.render_scanline_position", "Approximate HCLOCK position to render at for scanline-based renderers", IntegerSetting::Decimal, 512); -IntegerSetting PPU::Hack::obj_cache(&config_file, "ppu.hack.obj_cache", +IntegerSetting PPU::Hack::obj_cache(&config(), "ppu.hack.obj_cache", "Cache OAM OBJ attributes one scanline before rendering\n" "This is technically closer to the actual operation of the SNES,\n" "but can cause problems in some games if enabled", IntegerSetting::Boolean, false); -IntegerSetting PPU::Hack::oam_address_invalidation(&config_file, "ppu.hack.oam_address_invalidation", +IntegerSetting PPU::Hack::oam_address_invalidation(&config(), "ppu.hack.oam_address_invalidation", "OAM access address changes during active display, as the S-PPU reads\n" "data to render the display. Thusly, the address retrieved when accessing\n" "OAM during active display is unpredictable. Unfortunately, the exact\n" @@ -97,7 +100,7 @@ IntegerSetting PPU::Hack::oam_address_invalidation(&config_file, "ppu.hack.oam_a "accurate to enable this setting, but one must *not* rely on the actual\n" "address to match hardware under emulation.", IntegerSetting::Boolean, true); -IntegerSetting PPU::Hack::cgram_address_invalidation(&config_file, "ppu.hack.cgram_address_invalidation", +IntegerSetting PPU::Hack::cgram_address_invalidation(&config(), "ppu.hack.cgram_address_invalidation", "CGRAM access address changes during active display (excluding hblank), as\n" "the S-PPU reads data to render the display. Thusly, as with OAM, the access\n" "address is unpredictable. Again, enabling this setting is more hardware\n" diff --git a/src/config/config.h b/src/config/config.h index 5840e3fa..0f1bf1ea 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -1,7 +1,7 @@ -extern Config config_file; - namespace config { +extern Config& config(); + string file_updatepath(const char *, const char *); extern struct Path { diff --git a/src/interface.h b/src/interface.h index fa506f9e..eac1a992 100644 --- a/src/interface.h +++ b/src/interface.h @@ -39,6 +39,7 @@ #include "chip/dsp1/dsp1.h" #include "chip/dsp2/dsp2.h" #include "chip/obc1/obc1.h" +#include "chip/st010/st010.h" extern MMIO mmio_unmapped; #ifdef POLYMORPHISM diff --git a/src/lib/libui_gtk_control.h b/src/lib/libui_gtk_control.h index 44703151..f1b1f639 100644 --- a/src/lib/libui_gtk_control.h +++ b/src/lib/libui_gtk_control.h @@ -147,16 +147,19 @@ class Frame : public Control { public: }; class Label : public Control { public: +enum { ideal_height = 18 }; void create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = ""); void set_text(const char *str); }; class Button : public Control { public: +enum { ideal_height = 30 }; void create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = ""); void set_text(const char *str); }; class Checkbox : public Control { public: +enum { ideal_height = 18 }; void create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = ""); void check(); void uncheck(); @@ -165,12 +168,14 @@ class Checkbox : public Control { public: }; class Radiobox : public Control { public: +enum { ideal_height = 18 }; void create(Window &owner, ControlGroup &group, uint style, uint x, uint y, uint width, uint height, const char *caption = ""); void check(); bool checked(); }; class Editbox : public Control { public: +enum { ideal_height = 30 }; enum { Multiline = (1 << 1), Readonly = (1 << 2), @@ -226,6 +231,7 @@ GtkTreeIter iter; }; class Combobox : public Control { public: +enum { ideal_height = 30 }; void create(Window &owner, uint style, uint x, uint y, uint width, uint height); void add_item(const char *data); int get_selection(); @@ -237,12 +243,14 @@ uint counter; }; class Progressbar : public Control { public: +enum { ideal_height = 30 }; void create(Window &owner, uint style, uint x, uint y, uint width, uint height); void set_progress(uint progress); uint get_progress(); }; class Slider : public Control { public: +enum { ideal_height = 25 }; enum { Horizontal = 0, Vertical = 1, diff --git a/src/lib/libui_win_control.h b/src/lib/libui_win_control.h index 9f471d28..07883449 100644 --- a/src/lib/libui_win_control.h +++ b/src/lib/libui_win_control.h @@ -157,15 +157,18 @@ class Frame : public Control { public: }; class Label : public Control { public: +enum { ideal_height = 16 }; void create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = ""); void set_text(const char *str, ...); }; class Button : public Control { public: +enum { ideal_height = 21 }; void create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = ""); }; class Checkbox : public Control { public: +enum { ideal_height = 15 }; void create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = ""); void check(); void uncheck(); @@ -174,6 +177,7 @@ class Checkbox : public Control { public: }; class Radiobox : public Control { public: +enum { ideal_height = 15 }; void create(Window &owner, ControlGroup &list, uint style, uint x, uint y, uint width, uint height, const char *caption = ""); void check(); bool checked(); @@ -183,6 +187,7 @@ ControlGroup group; }; class Editbox : public Control { public: +enum { ideal_height = 21 }; enum { Multiline = (1 << 1), Readonly = (1 << 2), @@ -235,6 +240,7 @@ uint column_count; }; class Combobox : public Control { public: +enum { ideal_height = 21 }; void create(Window &owner, uint style, uint x, uint y, uint width, uint height); void add_item(const char *data); void set_selection(int index); @@ -243,12 +249,14 @@ class Combobox : public Control { public: }; class Progressbar : public Control { public: +enum { ideal_height = 30 }; void create(Window &owner, uint style, uint x, uint y, uint width, uint height); void set_progress(uint progress); uint get_progress(); }; class Slider : public Control { public: +enum { ideal_height = 25 }; enum Style { Horizontal = 0, Vertical = 1, diff --git a/src/memory/bmemory/bmemory.cpp b/src/memory/bmemory/bmemory.cpp index 8f81071d..3d3650cc 100644 --- a/src/memory/bmemory/bmemory.cpp +++ b/src/memory/bmemory/bmemory.cpp @@ -27,11 +27,12 @@ void bMemBus::load_cart() { return; } - if(cartridge.info.sdd1)cart_map_sdd1(); - if(cartridge.info.c4) cart_map_c4(); - if(cartridge.info.dsp1)cart_map_dsp1(); - if(cartridge.info.dsp2)cart_map_dsp2(); - if(cartridge.info.obc1)cart_map_obc1(); + if(cartridge.info.sdd1) cart_map_sdd1(); + if(cartridge.info.c4) cart_map_c4(); + if(cartridge.info.dsp1) cart_map_dsp1(); + if(cartridge.info.dsp2) cart_map_dsp2(); + if(cartridge.info.obc1) cart_map_obc1(); + if(cartridge.info.st010)cart_map_st010(); cart_map_system(); @@ -57,12 +58,13 @@ char t[256]; dprintf("* Region : %s", (cartridge.info.region == Cartridge::NTSC) ? "NTSC" : "PAL"); strcpy(t, ""); - if(cartridge.info.srtc)strcat(t, "S-RTC, "); - if(cartridge.info.sdd1)strcat(t, "S-DD1, "); - if(cartridge.info.c4) strcat(t, "Cx4, "); - if(cartridge.info.dsp1)strcat(t, "DSP-1, "); - if(cartridge.info.dsp2)strcat(t, "DSP-2, "); - if(cartridge.info.obc1)strcat(t, "OBC-1, "); + if(cartridge.info.srtc) strcat(t, "S-RTC, "); + if(cartridge.info.sdd1) strcat(t, "S-DD1, "); + if(cartridge.info.c4) strcat(t, "Cx4, "); + if(cartridge.info.dsp1) strcat(t, "DSP-1, "); + if(cartridge.info.dsp2) strcat(t, "DSP-2, "); + if(cartridge.info.obc1) strcat(t, "OBC-1, "); + if(cartridge.info.st010)strcat(t, "ST010, "); strrtrim(t, ", "); dprintf("* Coprocessor(s) : %s", (strlen(t) == 0) ? "None" : t); dprintf("* Reset:%0.4x NMI[n]:%0.4x IRQ[n]:%0.4x BRK[n]:%0.4x COP[n]:%0.4x", diff --git a/src/memory/bmemory/bmemory.h b/src/memory/bmemory/bmemory.h index 1a7702bf..51e2b7c6 100644 --- a/src/memory/bmemory/bmemory.h +++ b/src/memory/bmemory/bmemory.h @@ -41,6 +41,8 @@ enum { TYPE_WRAM, TYPE_MMIO, TYPE_CART }; void write_dsp2 (uint32 addr, uint8 data); uint8 read_obc1 (uint32 addr); void write_obc1 (uint32 addr, uint8 data); + uint8 read_st010 (uint32 addr); + void write_st010 (uint32 addr, uint8 data); void cart_map_reset(); void cart_map_system(); @@ -51,6 +53,7 @@ enum { TYPE_WRAM, TYPE_MMIO, TYPE_CART }; void cart_map_dsp1(); void cart_map_dsp2(); void cart_map_obc1(); + void cart_map_st010(); void power(); void reset(); diff --git a/src/memory/bmemory/bmemory_mapper_generic.cpp b/src/memory/bmemory/bmemory_mapper_generic.cpp index 3b5c1e07..788fffa7 100644 --- a/src/memory/bmemory/bmemory_mapper_generic.cpp +++ b/src/memory/bmemory/bmemory_mapper_generic.cpp @@ -178,3 +178,15 @@ void bMemBus::cart_map_obc1() { } } } + +void bMemBus::cart_map_st010() { +//$[68-6f|e8-ef]:[0000-0fff] + for(uint bank = 0x68; bank <= 0x6f; bank++) { + for(uint page = 0x00; page <= 0x0f; page++) { + page_read [0x0000 + (bank << 8) + page] = &bMemBus::read_st010; + page_read [0x8000 + (bank << 8) + page] = &bMemBus::read_st010; + page_write[0x0000 + (bank << 8) + page] = &bMemBus::write_st010; + page_write[0x8000 + (bank << 8) + page] = &bMemBus::write_st010; + } + } +} diff --git a/src/memory/bmemory/bmemory_rw.cpp b/src/memory/bmemory/bmemory_rw.cpp index 35c80a1f..2c6a37bd 100644 --- a/src/memory/bmemory/bmemory_rw.cpp +++ b/src/memory/bmemory/bmemory_rw.cpp @@ -62,3 +62,6 @@ void bMemBus::write_dsp2(uint32 addr, uint8 data) { dsp2->write(addr, data); } uint8 bMemBus::read_obc1 (uint32 addr) { return obc1->read(addr); } void bMemBus::write_obc1(uint32 addr, uint8 data) { obc1->write(addr, data); } + +uint8 bMemBus::read_st010 (uint32 addr) { return st010->read(addr); } +void bMemBus::write_st010(uint32 addr, uint8 data) { st010->write(addr, data); } diff --git a/src/ppu/bppu/bppu_mmio.cpp b/src/ppu/bppu/bppu_mmio.cpp index fbb1c80e..760c70e3 100644 --- a/src/ppu/bppu/bppu_mmio.cpp +++ b/src/ppu/bppu/bppu_mmio.cpp @@ -8,7 +8,7 @@ uint16 bPPU::get_vram_address() { uint16 addr; addr = regs.vram_addr; switch(regs.vram_mapping) { - case 0: break; + case 0: break; //direct case 1: addr = (addr & 0xff00) | ((addr & 0x001f) << 3) | ((addr >> 5) & 7); break; case 2: addr = (addr & 0xfe00) | ((addr & 0x003f) << 3) | ((addr >> 6) & 7); break; case 3: addr = (addr & 0xfc00) | ((addr & 0x007f) << 3) | ((addr >> 7) & 7); break; @@ -124,7 +124,7 @@ uint16 v = r_cpu->vcounter(); //NOTE: CGRAM writes during hblank are valid. During active display, the actual address the //data is written to varies, as the S-PPU itself changes the address. Like OAM, we do not know //the exact algorithm used, but we have zero known examples of any commercial software that -//attempts to do this. Therefore, the addresses are mapped to 0x0000. There is nothing special +//attempts to do this. Therefore, the addresses are mapped to 0x01ff. There is nothing special //about this address, it is simply more accurate to invalidate the 'expected' address than not. uint8 bPPU::cgram_mmio_read(uint16 addr) { @@ -134,8 +134,8 @@ uint8 bPPU::cgram_mmio_read(uint16 addr) { uint16 v = r_cpu->vcounter(); uint16 hc = r_cpu->hclock(); - if(v < (!r_cpu->overscan() ? 225 : 240) && hc > 0 && hc < 1096) { - return cgram_read(0x0000); + if(v < (!r_cpu->overscan() ? 225 : 240) && hc >= 72 && hc < 1096) { + return cgram_read(0x01ff); } return cgram_read(addr); @@ -148,8 +148,8 @@ void bPPU::cgram_mmio_write(uint16 addr, uint8 data) { uint16 v = r_cpu->vcounter(); uint16 hc = r_cpu->hclock(); - if(v < (!r_cpu->overscan() ? 225 : 240) && hc > 0 && hc < 1096) { - return cgram_write(0x0000, data); + if(v < (!r_cpu->overscan() ? 225 : 240) && hc >= 72 && hc < 1096) { + return cgram_write(0x01ff, data); } cgram_write(addr, data); diff --git a/src/snes/snes.cpp b/src/snes/snes.cpp index 6618757c..f6db6388 100644 --- a/src/snes/snes.cpp +++ b/src/snes/snes.cpp @@ -16,12 +16,13 @@ void SNES::runtoframe() { } void SNES::init() { - srtc = new SRTC(); - sdd1 = new SDD1(); - c4 = new C4(); - dsp1 = new DSP1(); - dsp2 = new DSP2(); - obc1 = new OBC1(); + srtc = new SRTC(); + sdd1 = new SDD1(); + c4 = new C4(); + dsp1 = new DSP1(); + dsp2 = new DSP2(); + obc1 = new OBC1(); + st010 = new ST010(); srtc->init(); sdd1->init(); @@ -29,6 +30,7 @@ void SNES::init() { dsp1->init(); dsp2->init(); obc1->init(); + st010->init(); video_init(); audio_init(); @@ -50,12 +52,13 @@ void SNES::power() { r_ppu->power(); r_mem->power(); - if(cartridge.info.srtc)srtc->power(); - if(cartridge.info.sdd1)sdd1->power(); - if(cartridge.info.c4) c4->power(); - if(cartridge.info.dsp1)dsp1->power(); - if(cartridge.info.dsp2)dsp2->power(); - if(cartridge.info.obc1)obc1->power(); + if(cartridge.info.srtc) srtc->power(); + if(cartridge.info.sdd1) sdd1->power(); + if(cartridge.info.c4) c4->power(); + if(cartridge.info.dsp1) dsp1->power(); + if(cartridge.info.dsp2) dsp2->power(); + if(cartridge.info.obc1) obc1->power(); + if(cartridge.info.st010)st010->power(); r_mem->flush_mmio_mappers(); for(int i = 0x2100; i <= 0x213f; i++)r_mem->set_mmio_mapper(i, r_ppu); @@ -65,12 +68,13 @@ void SNES::power() { for(int i = 0x4200; i <= 0x421f; i++)r_mem->set_mmio_mapper(i, r_cpu); for(int i = 0x4300; i <= 0x437f; i++)r_mem->set_mmio_mapper(i, r_cpu); - if(cartridge.info.srtc)srtc->enable(); - if(cartridge.info.sdd1)sdd1->enable(); - if(cartridge.info.c4) c4->enable(); - if(cartridge.info.dsp1)dsp1->enable(); - if(cartridge.info.dsp2)dsp2->enable(); - if(cartridge.info.obc1)obc1->enable(); + if(cartridge.info.srtc) srtc->enable(); + if(cartridge.info.sdd1) sdd1->enable(); + if(cartridge.info.c4) c4->enable(); + if(cartridge.info.dsp1) dsp1->enable(); + if(cartridge.info.dsp2) dsp2->enable(); + if(cartridge.info.obc1) obc1->enable(); + if(cartridge.info.st010)st010->enable(); video_update(); } @@ -84,12 +88,13 @@ void SNES::reset() { r_ppu->reset(); r_mem->reset(); - if(cartridge.info.srtc)srtc->reset(); - if(cartridge.info.sdd1)sdd1->reset(); - if(cartridge.info.c4) c4->reset(); - if(cartridge.info.dsp1)dsp1->reset(); - if(cartridge.info.dsp2)dsp2->reset(); - if(cartridge.info.obc1)obc1->reset(); + if(cartridge.info.srtc) srtc->reset(); + if(cartridge.info.sdd1) sdd1->reset(); + if(cartridge.info.c4) c4->reset(); + if(cartridge.info.dsp1) dsp1->reset(); + if(cartridge.info.dsp2) dsp2->reset(); + if(cartridge.info.obc1) obc1->reset(); + if(cartridge.info.st010)st010->reset(); video_update(); } diff --git a/src/snes/video/filter_ntsc.cpp b/src/snes/video/filter_ntsc.cpp index 2fc19b17..e2532d05 100644 --- a/src/snes/video/filter_ntsc.cpp +++ b/src/snes/video/filter_ntsc.cpp @@ -1,5 +1,4 @@ -#define SNES_NTSC_IN_FORMAT SNES_NTSC_BGR15 -#include "filter_ntsc_core.cpp" +#include "ntsc/snes_ntsc.c" NtscVideoFilter::NtscVideoFilter() { ntsc = 0; diff --git a/src/snes/video/filter_ntsc.h b/src/snes/video/filter_ntsc.h index a25af470..7a79b43e 100644 --- a/src/snes/video/filter_ntsc.h +++ b/src/snes/video/filter_ntsc.h @@ -1,4 +1,4 @@ -#include "filter_ntsc_core.h" +#include "ntsc/snes_ntsc.h" class NtscVideoFilter : public VideoFilter { private: diff --git a/src/snes/video/filter_ntsc_core.cpp b/src/snes/video/filter_ntsc_core.cpp deleted file mode 100644 index 9466b225..00000000 --- a/src/snes/video/filter_ntsc_core.cpp +++ /dev/null @@ -1,598 +0,0 @@ -/* snes_ntsc 0.2.1. http://www.slack.net/~ant/ */ - -/* compilable in C or C++; just change the file extension */ - -//#include "snes_ntsc.h" - -#include -#include - -/* Based on algorithm by NewRisingSun */ -/* Copyright (C) 2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -enum { disable_correction = 0 }; /* for debugging */ - -/* macro constants are used instead of enum in some places to work around compiler bug */ - -/* half normal range to allow for doubled hires pixels */ -#define rgb_unit 0x80 - -/* begin mostly common NES/SNES/SMS code */ - -snes_ntsc_setup_t const snes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 0, 1, 0, 0 }; -snes_ntsc_setup_t const snes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }; -snes_ntsc_setup_t const snes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 0, 1, 0, 0 }; -snes_ntsc_setup_t const snes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 0, 1, 0, 0 }; - -enum { alignment_count = 3 }; /* different pixel alignments with respect to yiq quads */ - -enum { kernel_half = 16 }; -enum { kernel_size = kernel_half * 2 + 1 }; -#define rescale_in 8 -enum { rescale_out = 7 }; - -struct ntsc_impl_t -{ - float to_rgb [snes_ntsc_burst_count] [6]; - float brightness; - float contrast; - float artifacts; - float fringing; - float hue_warping; - float kernel [rescale_out * kernel_size * 2]; -}; - -#define PI 3.14159265358979323846f - -static void init_ntsc_impl( struct ntsc_impl_t* impl, snes_ntsc_setup_t const* setup ) -{ - float kernels [kernel_size * 2]; - - impl->brightness = (float) setup->brightness * (0.4f * rgb_unit); - impl->contrast = (float) setup->contrast * 0.4f + 1.0f; - impl->hue_warping = (float) setup->hue_warping; - - impl->artifacts = (float) setup->artifacts; - if ( impl->artifacts > 0 ) - impl->artifacts *= 0.5f; - impl->artifacts += 1.0f; - - impl->fringing = (float) setup->fringing; - if ( impl->fringing > 0 ) - impl->fringing *= 0.5f; - impl->fringing += 1.0f; - - /* generate luma (y) filter using sinc kernel */ - { - /* sinc with rolloff (dsf) */ - /* double precision avoids instability */ - double const rolloff = 1 + setup->sharpness * 0.004; - double const maxh = 256; - double const pow_a_n = pow( rolloff, maxh ); - float sum; - int i; - /* quadratic mapping to reduce negative (blurring) range */ - double to_angle = setup->resolution + 1; - to_angle = PI / maxh * 0.20 * (to_angle * to_angle + 1); - - kernels [kernel_size * 3 / 2] = (float) maxh; - for ( i = 0; i < kernel_half * 2 + 1; i++ ) - { - int x = i - kernel_half; - double angle = x * to_angle; - /* instability occurs at center point with rolloff very close to 1.0 */ - if ( x || pow_a_n > 1.01 || pow_a_n < 0.99 ) - { - double rolloff_cos_a = rolloff * cos( angle ); - double num = 1 - rolloff_cos_a - - pow_a_n * cos( maxh * angle ) + - pow_a_n * rolloff * cos( (maxh - 1) * angle ); - double den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; - double dsf = num / den; - kernels [kernel_size * 3 / 2 - kernel_half + i] = (float) dsf; - } - } - - /* apply blackman window and find sum */ - sum = 0; - for ( i = 0; i < kernel_half * 2 + 1; i++ ) - { - float x = PI * 2 / (kernel_half * 2) * i; - float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 ); - sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman); - } - - /* normalize kernel */ - sum = 1.0f / sum; - for ( i = 0; i < kernel_half * 2 + 1; i++ ) - { - int x = kernel_size * 3 / 2 - kernel_half + i; - kernels [x] *= sum; - assert( kernels [x] == kernels [x] ); /* catch numerical instability */ - } - } - - /* generate chroma (iq) filter using gaussian kernel */ - { - float const cutoff_factor = -0.03125f; - float cutoff = (float) setup->bleed; - int i; - - if ( cutoff < 0 ) - { - /* keep extreme value accessible only near upper end of scale (1.0) */ - cutoff *= cutoff; - cutoff *= cutoff; - cutoff *= cutoff; - cutoff *= -30.0f / 0.65f; - } - cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff; - - for ( i = -kernel_half; i <= kernel_half; i++ ) - kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff ); - - /* normalize even and odd phases separately */ - for ( i = 0; i < 2; i++ ) - { - float sum = 0; - int x; - for ( x = i; x < kernel_size; x += 2 ) - sum += kernels [x]; - - sum = 1.0f / sum; - for ( x = i; x < kernel_size; x += 2 ) - { - kernels [x] *= sum; - assert( kernels [x] == kernels [x] ); /* catch numerical instability */ - } - } - } - - /* generate linear rescale kernels */ - { - int i; - for ( i = 0; i < rescale_out; i++ ) - { - float* out = &impl->kernel [i * kernel_size * 2]; - float second = 1.0f / rescale_in * (i + 1); - float first = 1.0f - second; - int x; - *out++ = kernels [0] * first; - for ( x = 1; x < kernel_size * 2; x++ ) - *out++ = kernels [x] * first + kernels [x - 1] * second; - } - } - - /* setup decoder matricies */ - { - static float const default_decoder [6] = - { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f }; - float hue = (float) setup->hue * PI; - float sat = (float) setup->saturation + 1.0f; - float const* decoder = setup->decoder_matrix; - int i; - if ( !decoder ) - decoder = default_decoder; - else - hue += PI / 180 * 15; - - for ( i = 0; i < snes_ntsc_burst_count; i++ ) - { - float s = (float) sin( hue ) * sat; - float c = (float) cos( hue ) * sat; - float const* in = decoder; - float* out = impl->to_rgb [i]; - int n; - for ( n = 3; n; --n ) - { - float i = *in++; - float q = *in++; - *out++ = i * c - q * s; - *out++ = i * s + q * c; - } - hue -= PI / 180 * 120; - } - } -} - -/* kernel generation */ - -enum { rgb_kernel_size = snes_ntsc_burst_size / alignment_count }; - -static float const rgb_offset = rgb_unit * 2 + 0.5f; -static ntsc_rgb_t const ntsc_rgb_bias = rgb_unit * 2 * ntsc_rgb_builder; - -#define TO_RGB( y, i, q, to_rgb ) ( \ - ((int) (y + to_rgb [0] * i + to_rgb [1] * q) << 21) +\ - ((int) (y + to_rgb [2] * i + to_rgb [3] * q) << 11) +\ - ((int) (y + to_rgb [4] * i + to_rgb [5] * q) << 1)\ -) - -typedef struct pixel_info_t -{ - int offset; - float negate; - float kernel [4]; -} pixel_info_t; - -#define PIXEL_OFFSET_( ntsc, scaled ) \ - (kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \ - (kernel_size * 2 * scaled)) - -#define PIXEL_OFFSET( ntsc, scaled ) \ - PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\ - (((scaled) + rescale_out * 10) % rescale_out) ),\ - (1.0f - (((ntsc) + 100) & 2)) - -/* Generate pixel at all burst phases and column alignments */ -static void gen_kernel( struct ntsc_impl_t* impl, float y, float i, float q, ntsc_rgb_t* out ) -{ - /* generate for each scanline burst phase */ - float const* to_rgb = impl->to_rgb [0]; - do - { - static pixel_info_t const pixels [alignment_count] = { - { PIXEL_OFFSET( -4, -9 ), { 1.0000f, 1.0000f, .6667f, .0000f } }, - { PIXEL_OFFSET( -2, -7 ), { .3333f, 1.0000f, 1.0000f, .3333f } }, - { PIXEL_OFFSET( 0, -5 ), { .0000f, .6667f, 1.0000f, 1.0000f } }, - }; - - /* Encode yiq into *two* composite signals (to allow control over artifacting). - Convolve these with kernels which: filter respective components, apply - sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack - into integer. */ - pixel_info_t const* pixel = pixels; - do - { - /* negate is -1 when composite starts at odd multiple of 2 */ - float const yy = y * impl->fringing * pixel->negate; - float const ic0 = (i + yy) * pixel->kernel [0]; - float const qc1 = (q + yy) * pixel->kernel [1]; - float const ic2 = (i - yy) * pixel->kernel [2]; - float const qc3 = (q - yy) * pixel->kernel [3]; - - float const factor = impl->artifacts * pixel->negate; - float const ii = i * factor; - float const yc0 = (y + ii) * pixel->kernel [0]; - float const yc2 = (y - ii) * pixel->kernel [2]; - - float const qq = q * factor; - float const yc1 = (y + qq) * pixel->kernel [1]; - float const yc3 = (y - qq) * pixel->kernel [3]; - - float const* k = &impl->kernel [pixel->offset]; - int n; - for ( n = rgb_kernel_size; n; --n ) - { - float i = k[0]*ic0 + k[2]*ic2; - float q = k[1]*qc1 + k[3]*qc3; - float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 + - k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset; - if ( k >= &impl->kernel [kernel_size * 2 * (rescale_out - 1)] ) - k -= kernel_size * 2 * (rescale_out - 1) + 2; - else - k += kernel_size * 2 - 1; - *out++ = TO_RGB( y, i, q, to_rgb ) - ntsc_rgb_bias; - } - } - while ( pixel++ < &pixels [alignment_count - 1] ); - - to_rgb += 6; - - /* rotate -120 degrees */ - { - float const sin_b = -0.866025f; - float const cos_b = -0.5f; - float t; - t = i * cos_b - q * sin_b; - q = i * sin_b + q * cos_b; - i = t; - } - } - while ( to_rgb < impl->to_rgb [snes_ntsc_burst_count] ); -} - -static void merge_fields( ntsc_rgb_t* io ) -{ - int n; - for ( n = snes_ntsc_burst_size; n; --n ) - { - ntsc_rgb_t p0 = io [snes_ntsc_burst_size * 0] + ntsc_rgb_bias; - ntsc_rgb_t p1 = io [snes_ntsc_burst_size * 1] + ntsc_rgb_bias; - ntsc_rgb_t p2 = io [snes_ntsc_burst_size * 2] + ntsc_rgb_bias; - /* merge fields without losing precision */ - io [snes_ntsc_burst_size * 0] = - ((p0 + p1 - ((p0 ^ p1) & ntsc_rgb_builder)) >> 1) - ntsc_rgb_bias; - io [snes_ntsc_burst_size * 1] = - ((p1 + p2 - ((p1 ^ p2) & ntsc_rgb_builder)) >> 1) - ntsc_rgb_bias; - io [snes_ntsc_burst_size * 2] = - ((p2 + p0 - ((p2 ^ p0) & ntsc_rgb_builder)) >> 1) - ntsc_rgb_bias; - ++io; - } -} - -static void correct_errors( ntsc_rgb_t color, ntsc_rgb_t* out ) -{ - int burst; - for ( burst = 0; burst < snes_ntsc_burst_count; burst++ ) - { - int i; - for ( i = 0; i < rgb_kernel_size / 2; i++ ) - { - ntsc_rgb_t error = color - - out [i ] - - out [i + 3 +28] - - out [i + 5 +14] - - out [i + 7 ] - - out [(i+10)%14+28] - - out [(i+12)%14+14]; - - /* distribute error among four kernels */ - ntsc_rgb_t fourth = (error + 2 * ntsc_rgb_builder) >> 2; - fourth &= (ntsc_rgb_bias >> 1) - ntsc_rgb_builder; - fourth -= ntsc_rgb_bias >> 2; - if ( disable_correction ) { out [i] += ntsc_rgb_bias; continue; } - out [i + 3 +28] += fourth; - out [i + 5 +14] += fourth; - out [i + 7 ] += fourth; - out [i ] += error - (fourth * 3); - } - out += alignment_count * rgb_kernel_size; - } -} -/* end common code */ - -void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup ) -{ - float to_float [32]; - int entry; - struct ntsc_impl_t impl; - if ( !setup ) - setup = &snes_ntsc_composite; - init_ntsc_impl( &impl, setup ); - - { - double gamma = 1 - setup->gamma * (setup->gamma > 0 ? 0.5f : 1.5f); - int i; - for ( i = 0; i < 32; i++ ) - to_float [i] = (float) pow( (1 / 31.0) * i, gamma ) * rgb_unit; - } - - for ( entry = 0; entry < snes_ntsc_color_count; entry++ ) - { - /* Reduce number of significant bits of source color. Clearing the - low bits of R and B were least notictable. Modifying green was too - noticeable. */ - int ir = entry >> 8 & 0x1E; - int ig = entry >> 4 & 0x1F; - int ib = entry << 1 & 0x1E; - - if ( setup->bsnes_colortbl ) - { - int bgr15 = (ib << 10) | (ig << 5) | ir; - unsigned long rgb16 = setup->bsnes_colortbl [bgr15]; - ir = rgb16 >> 11 & 0x1E; - ig = rgb16 >> 6 & 0x1F; - ib = rgb16 & 0x1E; - } - - { - float r = to_float [ir]; - float g = to_float [ig]; - float b = to_float [ib]; - - float y = r * 0.299f + g * 0.587f + b * 0.114f; - float i = r * 0.596f - g * 0.275f - b * 0.321f; - float q = r * 0.212f - g * 0.523f + b * 0.311f; - - float iq = i * q; - if ( impl.hue_warping && q && iq <= 0 ) - { - float factor = (iq * impl.hue_warping) / (i * i + q * q); - i -= i * factor; - q += q * factor; - } - - y = y * impl.contrast + impl.brightness; - - { - float yy = y + rgb_offset; - ntsc_rgb_t rgb = TO_RGB( yy, i, q, impl.to_rgb [0] ); - ntsc_rgb_t* out = ntsc->table [entry]; - - gen_kernel( &impl, y, i, q, out ); - - if ( setup->merge_fields ) - merge_fields( out ); - - correct_errors( rgb, out ); - } - } - } -} - -/* Disable 'restrict' keyword by default. If your compiler supports it, put - - #define restrict restrict - -somewhere in a config header, or the equivalent in the command-line: - - -Drestrict=restrict - -If your compiler supports a non-standard version, like __restrict, do this: - - #define restrict __restrict - -Enabling this if your compiler supports it will allow better optimization. */ -#ifndef restrict - #define restrict -#endif - -/* Default to 16-bit RGB input and output */ -#ifndef SNES_NTSC_IN_FORMAT - #define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB16 -#endif - -#ifndef SNES_NTSC_OUT_DEPTH - #define SNES_NTSC_OUT_DEPTH 16 -#endif - -#include - -#if SNES_NTSC_OUT_DEPTH > 16 - #if UINT_MAX == 0xFFFFFFFF - typedef unsigned int snes_ntsc_out_t; - #elif ULONG_MAX == 0xFFFFFFFF - typedef unsigned long snes_ntsc_out_t; - #else - #error "Need 32-bit int type" - #endif -#else - #if USHRT_MAX == 0xFFFF - typedef unsigned short snes_ntsc_out_t; - #else - #error "Need 16-bit int type" - #endif -#endif - -/* useful if you have a linker which doesn't remove unused code from executable */ -#ifndef SNES_NTSC_NO_BLITTERS - -/* Use this as a starting point for writing your own blitter. To allow easy upgrades -to new versions of this library, put your blitter in a separate source file rather -than modifying this one directly. */ - -void snes_ntsc_blit( snes_ntsc_t const* ntsc, unsigned short const* snes_in, - long in_row_width, int burst_phase, int in_width, int in_height, - void* rgb_out, long out_pitch ) -{ - int chunk_count = (in_width - 1) / snes_ntsc_in_chunk; - for ( ; in_height; --in_height ) - { - /* begin row and read first input pixel */ - unsigned short const* line_in = snes_in; - SNES_NTSC_LORES_ROW( ntsc, burst_phase, - snes_ntsc_black, snes_ntsc_black, *line_in++ ); - snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out; - int n; - - /* blit main chunks, each using 3 input pixels to generate 7 output pixels */ - for ( n = chunk_count; n; --n ) - { - /* order of input and output pixels must not be altered */ - SNES_NTSC_PIXEL_IN( 0, line_in [0] ); - SNES_NTSC_LORES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); - SNES_NTSC_LORES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); - - SNES_NTSC_PIXEL_IN( 1, line_in [1] ); - SNES_NTSC_LORES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); - SNES_NTSC_LORES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); - - SNES_NTSC_PIXEL_IN( 2, line_in [2] ); - SNES_NTSC_LORES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); - SNES_NTSC_LORES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); - SNES_NTSC_LORES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); - - line_in += 3; - line_out += 7; - } - - /* you can eliminate the need for the final chunk below by padding - input with three extra black pixels at the end of each row */ - - /* finish final pixels without starting any new ones */ - SNES_NTSC_PIXEL_IN( 0, snes_ntsc_black ); - SNES_NTSC_LORES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); - SNES_NTSC_LORES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); - - SNES_NTSC_PIXEL_IN( 1, snes_ntsc_black ); - SNES_NTSC_LORES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); - SNES_NTSC_LORES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); - - SNES_NTSC_PIXEL_IN( 2, snes_ntsc_black ); - SNES_NTSC_LORES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); - SNES_NTSC_LORES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); - SNES_NTSC_LORES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); - - /* advance burst phase and line pointers */ - burst_phase = (burst_phase + 1) % snes_ntsc_burst_count; - snes_in += in_row_width; - rgb_out = (char*) rgb_out + out_pitch; - } -} - -void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, unsigned short const* snes_in, - long in_row_width, int burst_phase, int in_width, int in_height, - void* rgb_out, long out_pitch ) -{ - int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2); - for ( ; in_height; --in_height ) - { - unsigned short const* line_in = snes_in; - SNES_NTSC_HIRES_ROW( ntsc, burst_phase, - snes_ntsc_black, snes_ntsc_black, snes_ntsc_black, line_in [0], line_in [1] ); - snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out; - int n; - line_in += 2; - - for ( n = chunk_count; n; --n ) - { - SNES_NTSC_PIXEL_IN( 0, line_in [0] ); - SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); - - SNES_NTSC_PIXEL_IN( 1, line_in [1] ); - SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); - - SNES_NTSC_PIXEL_IN( 2, line_in [2] ); - SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); - - SNES_NTSC_PIXEL_IN( 3, line_in [3] ); - SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); - - SNES_NTSC_PIXEL_IN( 4, line_in [4] ); - SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); - - SNES_NTSC_PIXEL_IN( 5, line_in [5] ); - SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); - SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); - - line_in += 6; - line_out += 7; - } - - SNES_NTSC_PIXEL_IN( 0, snes_ntsc_black ); - SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); - - SNES_NTSC_PIXEL_IN( 1, snes_ntsc_black ); - SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); - - SNES_NTSC_PIXEL_IN( 2, snes_ntsc_black ); - SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); - - SNES_NTSC_PIXEL_IN( 3, snes_ntsc_black ); - SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); - - SNES_NTSC_PIXEL_IN( 4, snes_ntsc_black ); - SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); - - SNES_NTSC_PIXEL_IN( 5, snes_ntsc_black ); - SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); - SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); - - burst_phase = (burst_phase + 1) % snes_ntsc_burst_count; - snes_in += in_row_width; - rgb_out = (char*) rgb_out + out_pitch; - } -} - -#endif - diff --git a/src/snes/video/filter_ntsc_core.h b/src/snes/video/filter_ntsc_core.h deleted file mode 100644 index 26381733..00000000 --- a/src/snes/video/filter_ntsc_core.h +++ /dev/null @@ -1,192 +0,0 @@ -/* SNES NTSC composite video to RGB emulator/blitter */ - -/* snes_ntsc 0.2.1 */ - -#ifndef SNES_NTSC_H -#define SNES_NTSC_H - -/* Image parameters, ranging from -1.0 to 1.0 */ -typedef struct snes_ntsc_setup_t -{ - /* Basic parameters */ - double hue; /* -1 = -180 degrees, +1 = +180 degrees */ - double saturation; /* -1 = grayscale, +1 = oversaturated colors */ - double contrast; - double brightness; - double sharpness; /* edge contrast enhancement/blurring */ - - /* Advanced parameters */ - double gamma; - double resolution; /* image resolution */ - double artifacts; /* artifacts caused by color changes */ - double fringing; /* color artifacts caused by brightness changes */ - double bleed; /* color bleed (color resolution reduction) */ - double hue_warping;/* -1 = expand purple & green, +1 = expand orange & cyan */ - int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */ - float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */ - - unsigned long const* bsnes_colortbl; /* temporary feature for bsnes only; set to 0 */ -} snes_ntsc_setup_t; - -/* Video format presets */ -extern snes_ntsc_setup_t const snes_ntsc_composite; /* color bleeding + artifacts */ -extern snes_ntsc_setup_t const snes_ntsc_svideo; /* color bleeding only */ -extern snes_ntsc_setup_t const snes_ntsc_rgb; /* crisp image */ -extern snes_ntsc_setup_t const snes_ntsc_monochrome;/* desaturated + artifacts */ - -/* Initialize and adjust parameters. Can be called multiple times on the same -snes_ntsc_t object. Caller must allocate memory for snes_ntsc_t. Can pass 0 -for either parameter. */ -typedef struct snes_ntsc_t snes_ntsc_t; -void snes_ntsc_init( snes_ntsc_t*, snes_ntsc_setup_t const* setup ); - -/* Blit one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT -and output RGB depth is set by NES_NTSC_OUT_DEPTH. Both default to 16-bit RGB. -In_row_width is the number of pixels to get to the next input row. Out_pitch -is the number of *bytes* to get to the next output row. */ -void snes_ntsc_blit( snes_ntsc_t const*, unsigned short const* snes_in, - long in_row_width, int burst_phase, int in_width, int in_height, - void* rgb_out, long out_pitch ); - -void snes_ntsc_blit_hires( snes_ntsc_t const*, unsigned short const* snes_in, - long in_row_width, int burst_phase, int in_width, int in_height, - void* rgb_out, long out_pitch ); - -/* Number of output pixels written by low-res blitter for given input width. Width -might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded -value. Guaranteed not to round 256 down at all. */ -#define SNES_NTSC_OUT_WIDTH( in_width ) \ - (((in_width) - 1) / snes_ntsc_in_chunk * snes_ntsc_out_chunk + snes_ntsc_out_chunk) - -/* Number of low-res input pixels that will fit within given output width. Might be -rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded -value. */ -#define SNES_NTSC_IN_WIDTH( out_width ) \ - ((out_width) / snes_ntsc_out_chunk * snes_ntsc_in_chunk - snes_ntsc_in_chunk + 1) - - -/* Interface for user-defined custom blitters */ - -enum { snes_ntsc_in_chunk = 3 }; /* number of snes pixels read per chunk */ -enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */ -enum { snes_ntsc_black = 0 }; /* palette index for black */ -enum { snes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */ - -/* Begin outputting row and start three pixels. First pixel will be cut off a bit. -Use snes_ntsc_black for unused pixels. Declares variables, so must be before first -statement in a block (unless you're using C++). */ -#define SNES_NTSC_LORES_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \ - char const* ktable = (char*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (ntsc_rgb_t));\ - int const snes_pixel0_ = (pixel0);\ - ntsc_rgb_t const* kernel0 = SNES_NTSC_IN_FORMAT( snes_pixel0_ );\ - int const snes_pixel1_ = (pixel1);\ - ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( snes_pixel1_ );\ - int const snes_pixel2_ = (pixel2);\ - ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( snes_pixel2_ );\ - ntsc_rgb_t const* kernelx0;\ - ntsc_rgb_t const* kernelx1 = kernel0;\ - ntsc_rgb_t const* kernelx2 = kernel0 - -/* Begin input pixel */ -#define SNES_NTSC_PIXEL_IN( in_index, color_in ) {\ - unsigned n;\ - kernelx##in_index = kernel##in_index;\ - kernel##in_index = (n = (color_in), SNES_NTSC_IN_FORMAT( n ));\ -} - -/* Generate output pixel. Bits can be 24, 16, 15, or 32 (treated as 24): -24: RRRRRRRR GGGGGGGG BBBBBBBB -16: RRRRRGGG GGGBBBBB -15: RRRRRGG GGGBBBBB - 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (raw format; x = junk bits) */ -#define SNES_NTSC_LORES_OUT( x, rgb_out, bits ) {\ - ntsc_rgb_t raw =\ - kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\ - kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\ - SNES_NTSC_CLAMP_( raw, 1 );\ - SNES_NTSC_OUT_( rgb_out, (bits), raw, 1 );\ -} - -/* Hires equivalents */ -#define SNES_NTSC_HIRES_ROW( ntsc, burst, pixel1, pixel2, pixel3, pixel4, pixel5 ) \ - char const* ktable = (char*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (ntsc_rgb_t));\ - int const snes_pixel1_ = (pixel1);\ - ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( snes_pixel1_ );\ - int const snes_pixel2_ = (pixel2);\ - ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( snes_pixel2_ );\ - int const snes_pixel3_ = (pixel3);\ - ntsc_rgb_t const* kernel3 = SNES_NTSC_IN_FORMAT( snes_pixel3_ );\ - int const snes_pixel4_ = (pixel4);\ - ntsc_rgb_t const* kernel4 = SNES_NTSC_IN_FORMAT( snes_pixel4_ );\ - int const snes_pixel5_ = (pixel5);\ - ntsc_rgb_t const* kernel5 = SNES_NTSC_IN_FORMAT( snes_pixel5_ );\ - ntsc_rgb_t const* kernel0 = kernel1;\ - ntsc_rgb_t const* kernelx0;\ - ntsc_rgb_t const* kernelx1 = kernel1;\ - ntsc_rgb_t const* kernelx2 = kernel1;\ - ntsc_rgb_t const* kernelx3 = kernel1;\ - ntsc_rgb_t const* kernelx4 = kernel1;\ - ntsc_rgb_t const* kernelx5 = kernel1 - -#define SNES_NTSC_HIRES_OUT( x, rgb_out, bits ) {\ - ntsc_rgb_t raw =\ - kernel0 [ x ] + kernel2 [(x+5)%7+14] + kernel4 [(x+3)%7+28] +\ - kernelx0 [(x+7)%7+7] + kernelx2 [(x+5)%7+21] + kernelx4 [(x+3)%7+35] +\ - kernel1 [(x+6)%7 ] + kernel3 [(x+4)%7+14] + kernel5 [(x+2)%7+28] +\ - kernelx1 [(x+6)%7+7] + kernelx3 [(x+4)%7+21] + kernelx5 [(x+2)%7+35];\ - SNES_NTSC_CLAMP_( raw, 0 );\ - SNES_NTSC_OUT_( rgb_out, (bits), raw, 0 );\ -} - - -/* private */ - -enum { snes_ntsc_entry_size = 128 }; -enum { snes_ntsc_color_count = 0x2000 }; -typedef unsigned long ntsc_rgb_t; -struct snes_ntsc_t -{ - ntsc_rgb_t table [snes_ntsc_color_count] [snes_ntsc_entry_size]; -}; -enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count }; - -enum { ntsc_rgb_builder = (1L << 21) | (1 << 11) | (1 << 1) }; -enum { snes_ntsc_clamp_mask = ntsc_rgb_builder * 3 / 2 }; -enum { snes_ntsc_clamp_add = ntsc_rgb_builder * 0x101 }; - -#define SNES_NTSC_RGB16( n ) \ - (ntsc_rgb_t*) (ktable + ((n & 0x001E) | (n >> 1 & 0x03E0) | (n >> 2 & 0x3C00)) * \ - (snes_ntsc_entry_size / 2 * sizeof (ntsc_rgb_t))) - -#define SNES_NTSC_BGR15( n ) \ - (ntsc_rgb_t*) (ktable + ((n << 9 & 0x3C00) | (n & 0x03E0) | (n >> 10 & 0x001E)) * \ - (snes_ntsc_entry_size / 2 * sizeof (ntsc_rgb_t))) - -#define SNES_NTSC_CLAMP_( io, shift ) {\ - ntsc_rgb_t sub = io >> (9-shift) & snes_ntsc_clamp_mask;\ - ntsc_rgb_t clamp = snes_ntsc_clamp_add - sub;\ - io |= clamp;\ - clamp -= sub;\ - io &= clamp;\ -} - -#define SNES_NTSC_OUT_( rgb_out, bits, raw, x ) {\ - if ( bits == 16 ) {\ - rgb_out = (raw>>(13-x)& 0xF800)|(raw>>(8-x)&0x07E0)|(raw>>(4-x)&0x001F);\ - rgb_out = ((rgb_out&0xf800)>>11)|((rgb_out&0x07c0)>>1)|((rgb_out&0x001f)<<10);\ - rgb_out = snes.color_lookup_table[rgb_out];\ - } else if ( bits == 24 || bits == 32 ) {\ - rgb_out = (raw>>(5-x)&0xFF0000)|(raw>>(3-x)&0xFF00)|(raw>>(1-x)&0xFF);\ - rgb_out = ((rgb_out&0xf80000)>>19)|((rgb_out&0x00f800)>>6)|((rgb_out&0x0000f8)<<7);\ - rgb_out = snes.color_lookup_table[rgb_out];\ - } else if ( bits == 15 ) {\ - rgb_out = (raw>>(14-x)& 0x7C00)|(raw>>(9-x)&0x03E0)|(raw>>(4-x)&0x001F);\ - rgb_out = ((rgb_out&0x7c00)>>10)|((rgb_out&0x03e0))|((rgb_out&0x001f)<<10);\ - rgb_out = snes.color_lookup_table[rgb_out];\ - } else {\ - rgb_out = raw;\ - }\ -} - -#endif - diff --git a/src/snes/video/ntsc/snes_ntsc.c b/src/snes/video/ntsc/snes_ntsc.c new file mode 100644 index 00000000..815f2155 --- /dev/null +++ b/src/snes/video/ntsc/snes_ntsc.c @@ -0,0 +1,251 @@ +/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */ + +#include "snes_ntsc.h" + +/* Copyright (C) 2006-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +snes_ntsc_setup_t const snes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0 }; +snes_ntsc_setup_t const snes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }; +snes_ntsc_setup_t const snes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0 }; +snes_ntsc_setup_t const snes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 1, 0, 0 }; + +#define alignment_count 3 +#define burst_count 3 +#define rescale_in 8 +#define rescale_out 7 + +#define artifacts_mid 1.0f +#define fringing_mid 1.0f +#define std_decoder_hue 0 + +#define rgb_bits 7 /* half normal range to allow for doubled hires pixels */ +#define gamma_size 32 + +#include "snes_ntsc_impl.h" + +/* 3 input pixels -> 8 composite samples */ +pixel_info_t const snes_ntsc_pixels [alignment_count] = { + { PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } }, + { PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } }, + { PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } }, +}; + +static void merge_kernel_fields( snes_ntsc_rgb_t* io ) +{ + int n; + for ( n = burst_size; n; --n ) + { + snes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias; + snes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias; + snes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias; + /* merge colors without losing precision */ + io [burst_size * 0] = + ((p0 + p1 - ((p0 ^ p1) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias; + io [burst_size * 1] = + ((p1 + p2 - ((p1 ^ p2) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias; + io [burst_size * 2] = + ((p2 + p0 - ((p2 ^ p0) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias; + ++io; + } +} + +static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out ) +{ + int n; + for ( n = burst_count; n; --n ) + { + unsigned i; + for ( i = 0; i < rgb_kernel_size / 2; i++ ) + { + snes_ntsc_rgb_t error = color - + out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] - + out [i + 7] - out [i + 5 +14] - out [i + 3 +28]; + DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 ); + } + out += alignment_count * rgb_kernel_size; + } +} + +void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup ) +{ + int merge_fields; + int entry; + init_t impl; + if ( !setup ) + setup = &snes_ntsc_composite; + init( &impl, setup ); + + merge_fields = setup->merge_fields; + if ( setup->artifacts <= -1 && setup->fringing <= -1 ) + merge_fields = 1; + + for ( entry = 0; entry < snes_ntsc_palette_size; entry++ ) + { + /* Reduce number of significant bits of source color. Clearing the + low bits of R and B were least notictable. Modifying green was too + noticeable. */ + int ir = entry >> 8 & 0x1E; + int ig = entry >> 4 & 0x1F; + int ib = entry << 1 & 0x1E; + + #if SNES_NTSC_BSNES_COLORTBL + if ( setup->bsnes_colortbl ) + { + int bgr15 = (ib << 10) | (ig << 5) | ir; + unsigned long rgb16 = setup->bsnes_colortbl [bgr15]; + ir = rgb16 >> 11 & 0x1E; + ig = rgb16 >> 6 & 0x1F; + ib = rgb16 & 0x1E; + } + #endif + + { + float rr = impl.to_float [ir]; + float gg = impl.to_float [ig]; + float bb = impl.to_float [ib]; + + float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i ); + + int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g ); + snes_ntsc_rgb_t rgb = PACK_RGB( r, g, b ); + + snes_ntsc_rgb_t* out = ntsc->table [entry]; + gen_kernel( &impl, y, i, q, out ); + if ( merge_fields ) + merge_kernel_fields( out ); + correct_errors( rgb, out ); + } + } +} + +#ifndef SNES_NTSC_NO_BLITTERS + +void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width, + int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch ) +{ + int chunk_count = (in_width - 1) / snes_ntsc_in_chunk; + for ( ; in_height; --in_height ) + { + SNES_NTSC_IN_T const* line_in = input; + SNES_NTSC_BEGIN_ROW( ntsc, burst_phase, + snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN( *line_in ) ); + snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out; + int n; + ++line_in; + + for ( n = chunk_count; n; --n ) + { + /* order of input and output pixels must not be altered */ + SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) ); + SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) ); + SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) ); + SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + line_in += 3; + line_out += 7; + } + + /* finish final pixels */ + SNES_NTSC_COLOR_IN( 0, snes_ntsc_black ); + SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, snes_ntsc_black ); + SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, snes_ntsc_black ); + SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + burst_phase = (burst_phase + 1) % snes_ntsc_burst_count; + input += in_row_width; + rgb_out = (char*) rgb_out + out_pitch; + } +} + +void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width, + int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch ) +{ + int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2); + for ( ; in_height; --in_height ) + { + SNES_NTSC_IN_T const* line_in = input; + SNES_NTSC_HIRES_ROW( ntsc, burst_phase, + snes_ntsc_black, snes_ntsc_black, snes_ntsc_black, + SNES_NTSC_ADJ_IN( line_in [0] ), + SNES_NTSC_ADJ_IN( line_in [1] ) ); + snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out; + int n; + line_in += 2; + + for ( n = chunk_count; n; --n ) + { + /* twice as many input pixels per chunk */ + SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) ); + SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) ); + SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) ); + SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 3, SNES_NTSC_ADJ_IN( line_in [3] ) ); + SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 4, SNES_NTSC_ADJ_IN( line_in [4] ) ); + SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 5, SNES_NTSC_ADJ_IN( line_in [5] ) ); + SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + line_in += 6; + line_out += 7; + } + + SNES_NTSC_COLOR_IN( 0, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 1, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 2, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 3, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 4, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH ); + + SNES_NTSC_COLOR_IN( 5, snes_ntsc_black ); + SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH ); + SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH ); + + burst_phase = (burst_phase + 1) % snes_ntsc_burst_count; + input += in_row_width; + rgb_out = (char*) rgb_out + out_pitch; + } +} + +#endif diff --git a/src/snes/video/ntsc/snes_ntsc.h b/src/snes/video/ntsc/snes_ntsc.h new file mode 100644 index 00000000..5338d145 --- /dev/null +++ b/src/snes/video/ntsc/snes_ntsc.h @@ -0,0 +1,228 @@ +/* SNES NTSC video filter */ + +/* snes_ntsc 0.2.2 */ +#ifndef SNES_NTSC_H +#define SNES_NTSC_H + +#include "snes_ntsc_config.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown +in parenthesis and should remain fairly stable in future versions. */ +typedef struct snes_ntsc_setup_t +{ + /* Basic parameters */ + double hue; /* -1 = -180 degrees +1 = +180 degrees */ + double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */ + double contrast; /* -1 = dark (0.5) +1 = light (1.5) */ + double brightness; /* -1 = dark (0.5) +1 = light (1.5) */ + double sharpness; /* edge contrast enhancement/blurring */ + + /* Advanced parameters */ + double gamma; /* -1 = dark (1.5) +1 = light (0.5) */ + double resolution; /* image resolution */ + double artifacts; /* artifacts caused by color changes */ + double fringing; /* color artifacts caused by brightness changes */ + double bleed; /* color bleed (color resolution reduction) */ + int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */ + float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */ + + unsigned long const* bsnes_colortbl; /* undocumented; set to 0 */ +} snes_ntsc_setup_t; + +/* Video format presets */ +extern snes_ntsc_setup_t const snes_ntsc_composite; /* color bleeding + artifacts */ +extern snes_ntsc_setup_t const snes_ntsc_svideo; /* color bleeding only */ +extern snes_ntsc_setup_t const snes_ntsc_rgb; /* crisp image */ +extern snes_ntsc_setup_t const snes_ntsc_monochrome;/* desaturated + artifacts */ + +/* Initializes and adjusts parameters. Can be called multiple times on the same +snes_ntsc_t object. Can pass NULL for either parameter. */ +typedef struct snes_ntsc_t snes_ntsc_t; +void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup ); + +/* Filters one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT +and output RGB depth is set by SNES_NTSC_OUT_DEPTH. Both default to 16-bit RGB. +In_row_width is the number of pixels to get to the next input row. Out_pitch +is the number of *bytes* to get to the next output row. */ +void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, + long in_row_width, int burst_phase, int in_width, int in_height, + void* rgb_out, long out_pitch ); + +void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, + long in_row_width, int burst_phase, int in_width, int in_height, + void* rgb_out, long out_pitch ); + +/* Number of output pixels written by low-res blitter for given input width. Width +might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded +value. Guaranteed not to round 256 down at all. */ +#define SNES_NTSC_OUT_WIDTH( in_width ) \ + ((((in_width) - 1) / snes_ntsc_in_chunk + 1) * snes_ntsc_out_chunk) + +/* Number of low-res input pixels that will fit within given output width. Might be +rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded +value. */ +#define SNES_NTSC_IN_WIDTH( out_width ) \ + (((out_width) / snes_ntsc_out_chunk - 1) * snes_ntsc_in_chunk + 1) + + +/* Interface for user-defined custom blitters */ + +enum { snes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */ +enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */ +enum { snes_ntsc_black = 0 }; /* palette index for black */ +enum { snes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */ + +/* Begins outputting row and starts three pixels. First pixel will be cut off a bit. +Use snes_ntsc_black for unused pixels. Declares variables, so must be before first +statement in a block (unless you're using C++). */ +#define SNES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \ + char const* ktable = \ + (char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\ + SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, SNES_NTSC_IN_FORMAT, ktable ) + +/* Begins input pixel */ +#define SNES_NTSC_COLOR_IN( index, color ) \ + SNES_NTSC_COLOR_IN_( index, color, SNES_NTSC_IN_FORMAT, ktable ) + +/* Generates output pixel. Bits can be 24, 16, 15, 14, 32 (treated as 24), or 0: +24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB) +16: RRRRRGGG GGGBBBBB (5-6-5 RGB) +15: RRRRRGG GGGBBBBB (5-5-5 RGB) +14: BBBBBGG GGGRRRRR (5-5-5 BGR, native SNES format) + 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */ +#define SNES_NTSC_RGB_OUT( index, rgb_out, bits ) \ + SNES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 1 ) + +/* Hires equivalents */ +#define SNES_NTSC_HIRES_ROW( ntsc, burst, pixel1, pixel2, pixel3, pixel4, pixel5 ) \ + char const* ktable = \ + (char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\ + unsigned const snes_ntsc_pixel1_ = (pixel1);\ + snes_ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel1_ );\ + unsigned const snes_ntsc_pixel2_ = (pixel2);\ + snes_ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel2_ );\ + unsigned const snes_ntsc_pixel3_ = (pixel3);\ + snes_ntsc_rgb_t const* kernel3 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel3_ );\ + unsigned const snes_ntsc_pixel4_ = (pixel4);\ + snes_ntsc_rgb_t const* kernel4 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel4_ );\ + unsigned const snes_ntsc_pixel5_ = (pixel5);\ + snes_ntsc_rgb_t const* kernel5 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel5_ );\ + snes_ntsc_rgb_t const* kernel0 = kernel1;\ + snes_ntsc_rgb_t const* kernelx0;\ + snes_ntsc_rgb_t const* kernelx1 = kernel1;\ + snes_ntsc_rgb_t const* kernelx2 = kernel1;\ + snes_ntsc_rgb_t const* kernelx3 = kernel1;\ + snes_ntsc_rgb_t const* kernelx4 = kernel1;\ + snes_ntsc_rgb_t const* kernelx5 = kernel1 + +#define SNES_NTSC_HIRES_OUT( x, rgb_out, bits ) {\ + snes_ntsc_rgb_t raw_ =\ + kernel0 [ x ] + kernel2 [(x+5)%7+14] + kernel4 [(x+3)%7+28] +\ + kernelx0 [(x+7)%7+7] + kernelx2 [(x+5)%7+21] + kernelx4 [(x+3)%7+35] +\ + kernel1 [(x+6)%7 ] + kernel3 [(x+4)%7+14] + kernel5 [(x+2)%7+28] +\ + kernelx1 [(x+6)%7+7] + kernelx3 [(x+4)%7+21] + kernelx5 [(x+2)%7+35];\ + SNES_NTSC_CLAMP_( raw_, 0 );\ + SNES_NTSC_RGB_OUT_( rgb_out, (bits), 0 );\ +} + + +/* private */ +enum { snes_ntsc_entry_size = 128 }; +enum { snes_ntsc_palette_size = 0x2000 }; +typedef unsigned long snes_ntsc_rgb_t; +struct snes_ntsc_t { + snes_ntsc_rgb_t table [snes_ntsc_palette_size] [snes_ntsc_entry_size]; +}; +enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count }; + +#define SNES_NTSC_RGB16( ktable, n ) \ + (snes_ntsc_rgb_t const*) (ktable + ((n & 0x001E) | (n >> 1 & 0x03E0) | (n >> 2 & 0x3C00)) * \ + (snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t))) + +#define SNES_NTSC_BGR15( ktable, n ) \ + (snes_ntsc_rgb_t const*) (ktable + ((n << 9 & 0x3C00) | (n & 0x03E0) | (n >> 10 & 0x001E)) * \ + (snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t))) + +/* common 3->7 ntsc macros */ +#define SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \ + unsigned const snes_ntsc_pixel0_ = (pixel0);\ + snes_ntsc_rgb_t const* kernel0 = ENTRY( table, snes_ntsc_pixel0_ );\ + unsigned const snes_ntsc_pixel1_ = (pixel1);\ + snes_ntsc_rgb_t const* kernel1 = ENTRY( table, snes_ntsc_pixel1_ );\ + unsigned const snes_ntsc_pixel2_ = (pixel2);\ + snes_ntsc_rgb_t const* kernel2 = ENTRY( table, snes_ntsc_pixel2_ );\ + snes_ntsc_rgb_t const* kernelx0;\ + snes_ntsc_rgb_t const* kernelx1 = kernel0;\ + snes_ntsc_rgb_t const* kernelx2 = kernel0 + +#define SNES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\ + snes_ntsc_rgb_t raw_ =\ + kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\ + kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\ + SNES_NTSC_CLAMP_( raw_, shift );\ + SNES_NTSC_RGB_OUT_( rgb_out, bits, shift );\ +} + +/* common ntsc macros */ +#define snes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1)) +#define snes_ntsc_clamp_mask (snes_ntsc_rgb_builder * 3 / 2) +#define snes_ntsc_clamp_add (snes_ntsc_rgb_builder * 0x101) +#define SNES_NTSC_CLAMP_( io, shift ) {\ + snes_ntsc_rgb_t sub = (io) >> (9-(shift)) & snes_ntsc_clamp_mask;\ + snes_ntsc_rgb_t clamp = snes_ntsc_clamp_add - sub;\ + io |= clamp;\ + clamp -= sub;\ + io &= clamp;\ +} + +#define SNES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\ + unsigned color_;\ + kernelx##index = kernel##index;\ + kernel##index = (color_ = (color), ENTRY( table, color_ ));\ +} + +/* x is always zero except in snes_ntsc library */ +/* original routine */ +/* +#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ + if ( bits == 16 )\ + rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ + if ( bits == 24 || bits == 32 )\ + rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\ + if ( bits == 15 )\ + rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\ + if ( bits == 14 )\ + rgb_out = (raw_>>(24-x)& 0x001F)|(raw_>>(9-x)&0x03E0)|(raw_<<(6+x)&0x7C00);\ + if ( bits == 0 )\ + rgb_out = raw_ << x;\ +} +*/ + +/* custom bsnes routine -- hooks into bsnes colortable */ +#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ + if ( bits == 16 ) {\ + rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ + rgb_out = ((rgb_out&0xf800)>>11)|((rgb_out&0x07c0)>>1)|((rgb_out&0x001f)<<10);\ + rgb_out = snes.color_lookup_table[rgb_out];\ + } else if ( bits == 24 || bits == 32 ) {\ + rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\ + rgb_out = ((rgb_out&0xf80000)>>19)|((rgb_out&0x00f800)>>6)|((rgb_out&0x0000f8)<<7);\ + rgb_out = snes.color_lookup_table[rgb_out];\ + } else if ( bits == 15 ) {\ + rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\ + rgb_out = ((rgb_out&0x7c00)>>10)|((rgb_out&0x03e0))|((rgb_out&0x001f)<<10);\ + rgb_out = snes.color_lookup_table[rgb_out];\ + } else {\ + rgb_out = raw_ << x;\ + }\ +} + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/src/snes/video/ntsc/snes_ntsc_config.h b/src/snes/video/ntsc/snes_ntsc_config.h new file mode 100644 index 00000000..58cc92c1 --- /dev/null +++ b/src/snes/video/ntsc/snes_ntsc_config.h @@ -0,0 +1,26 @@ +/* Configure library by modifying this file */ + +#ifndef SNES_NTSC_CONFIG_H +#define SNES_NTSC_CONFIG_H + +/* Format of source pixels */ +/* #define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB16 */ +#define SNES_NTSC_IN_FORMAT SNES_NTSC_BGR15 + +/* The following affect the built-in blitter only; a custom blitter can +handle things however it wants. */ + +/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */ +#define SNES_NTSC_OUT_DEPTH 16 + +/* Type of input pixel values */ +#define SNES_NTSC_IN_T unsigned short + +/* Each raw pixel input value is passed through this. You might want to mask +the pixel index if you use the high bits as flags, etc. */ +#define SNES_NTSC_ADJ_IN( in ) in + +/* For each pixel, this is the basic operation: +output_color = SNES_NTSC_ADJ_IN( SNES_NTSC_IN_T ) */ + +#endif diff --git a/src/snes/video/ntsc/snes_ntsc_impl.h b/src/snes/video/ntsc/snes_ntsc_impl.h new file mode 100644 index 00000000..03e5df96 --- /dev/null +++ b/src/snes/video/ntsc/snes_ntsc_impl.h @@ -0,0 +1,439 @@ +/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */ + +/* Common implementation of NTSC filters */ + +#include +#include + +/* Copyright (C) 2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#define DISABLE_CORRECTION 0 + +#undef PI +#define PI 3.14159265358979323846f + +#ifndef LUMA_CUTOFF + #define LUMA_CUTOFF 0.20 +#endif +#ifndef gamma_size + #define gamma_size 1 +#endif +#ifndef rgb_bits + #define rgb_bits 8 +#endif +#ifndef artifacts_max + #define artifacts_max (artifacts_mid * 1.5f) +#endif +#ifndef fringing_max + #define fringing_max (fringing_mid * 2) +#endif +#ifndef STD_HUE_CONDITION + #define STD_HUE_CONDITION( setup ) 1 +#endif + +#define ext_decoder_hue (std_decoder_hue + 15) +#define rgb_unit (1 << rgb_bits) +#define rgb_offset (rgb_unit * 2 + 0.5f) + +enum { burst_size = snes_ntsc_entry_size / burst_count }; +enum { kernel_half = 16 }; +enum { kernel_size = kernel_half * 2 + 1 }; + +typedef struct init_t +{ + float to_rgb [burst_count * 6]; + float to_float [gamma_size]; + float contrast; + float brightness; + float artifacts; + float fringing; + float kernel [rescale_out * kernel_size * 2]; +} init_t; + +#define ROTATE_IQ( i, q, sin_b, cos_b ) {\ + float t;\ + t = i * cos_b - q * sin_b;\ + q = i * sin_b + q * cos_b;\ + i = t;\ +} + +static void init_filters( init_t* impl, snes_ntsc_setup_t const* setup ) +{ +#if rescale_out > 1 + float kernels [kernel_size * 2]; +#else + float* const kernels = impl->kernel; +#endif + + /* generate luma (y) filter using sinc kernel */ + { + /* sinc with rolloff (dsf) */ + float const rolloff = 1 + (float) setup->sharpness * (float) 0.032; + float const maxh = 32; + float const pow_a_n = (float) pow( rolloff, maxh ); + float sum; + int i; + /* quadratic mapping to reduce negative (blurring) range */ + float to_angle = (float) setup->resolution + 1; + to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1); + + kernels [kernel_size * 3 / 2] = maxh; /* default center value */ + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + int x = i - kernel_half; + float angle = x * to_angle; + /* instability occurs at center point with rolloff very close to 1.0 */ + if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 ) + { + float rolloff_cos_a = rolloff * (float) cos( angle ); + float num = 1 - rolloff_cos_a - + pow_a_n * (float) cos( maxh * angle ) + + pow_a_n * rolloff * (float) cos( (maxh - 1) * angle ); + float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; + float dsf = num / den; + kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5; + } + } + + /* apply blackman window and find sum */ + sum = 0; + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + float x = PI * 2 / (kernel_half * 2) * i; + float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 ); + sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman); + } + + /* normalize kernel */ + sum = 1.0f / sum; + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + int x = kernel_size * 3 / 2 - kernel_half + i; + kernels [x] *= sum; + assert( kernels [x] == kernels [x] ); /* catch numerical instability */ + } + } + + /* generate chroma (iq) filter using gaussian kernel */ + { + float const cutoff_factor = -0.03125f; + float cutoff = (float) setup->bleed; + int i; + + if ( cutoff < 0 ) + { + /* keep extreme value accessible only near upper end of scale (1.0) */ + cutoff *= cutoff; + cutoff *= cutoff; + cutoff *= cutoff; + cutoff *= -30.0f / 0.65f; + } + cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff; + + for ( i = -kernel_half; i <= kernel_half; i++ ) + kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff ); + + /* normalize even and odd phases separately */ + for ( i = 0; i < 2; i++ ) + { + float sum = 0; + int x; + for ( x = i; x < kernel_size; x += 2 ) + sum += kernels [x]; + + sum = 1.0f / sum; + for ( x = i; x < kernel_size; x += 2 ) + { + kernels [x] *= sum; + assert( kernels [x] == kernels [x] ); /* catch numerical instability */ + } + } + } + + /* + printf( "luma:\n" ); + for ( i = kernel_size; i < kernel_size * 2; i++ ) + printf( "%f\n", kernels [i] ); + printf( "chroma:\n" ); + for ( i = 0; i < kernel_size; i++ ) + printf( "%f\n", kernels [i] ); + */ + + /* generate linear rescale kernels */ + #if rescale_out > 1 + { + float weight = 1.0f; + float* out = impl->kernel; + int n = rescale_out; + do + { + float remain = 0; + int i; + weight -= 1.0f / rescale_in; + for ( i = 0; i < kernel_size * 2; i++ ) + { + float cur = kernels [i]; + float m = cur * weight; + *out++ = m + remain; + remain = cur - m; + } + } + while ( --n ); + } + #endif +} + +static float const default_decoder [6] = + { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f }; + +static void init( init_t* impl, snes_ntsc_setup_t const* setup ) +{ + impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset; + impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit; + #ifdef default_palette_contrast + if ( !setup->palette ) + impl->contrast *= default_palette_contrast; + #endif + + impl->artifacts = (float) setup->artifacts; + if ( impl->artifacts > 0 ) + impl->artifacts *= artifacts_max - artifacts_mid; + impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid; + + impl->fringing = (float) setup->fringing; + if ( impl->fringing > 0 ) + impl->fringing *= fringing_max - fringing_mid; + impl->fringing = impl->fringing * fringing_mid + fringing_mid; + + init_filters( impl, setup ); + + /* generate gamma table */ + if ( gamma_size > 1 ) + { + float const to_float = 1.0f / (gamma_size - (gamma_size > 1)); + float const gamma = 1.1333f - (float) setup->gamma * 0.5f; + /* match common PC's 2.2 gamma to TV's 2.65 gamma */ + int i; + for ( i = 0; i < gamma_size; i++ ) + impl->to_float [i] = + (float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness; + } + + /* setup decoder matricies */ + { + float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue; + float sat = (float) setup->saturation + 1; + float const* decoder = setup->decoder_matrix; + if ( !decoder ) + { + decoder = default_decoder; + if ( STD_HUE_CONDITION( setup ) ) + hue += PI / 180 * (std_decoder_hue - ext_decoder_hue); + } + + { + float s = (float) sin( hue ) * sat; + float c = (float) cos( hue ) * sat; + float* out = impl->to_rgb; + int n; + + n = burst_count; + do + { + float const* in = decoder; + int n = 3; + do + { + float i = *in++; + float q = *in++; + *out++ = i * c - q * s; + *out++ = i * s + q * c; + } + while ( --n ); + if ( burst_count <= 1 ) + break; + ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */ + } + while ( --n ); + } + } +} + +/* kernel generation */ + +#define RGB_TO_YIQ( r, g, b, y, i ) (\ + (y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\ + (i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\ + ((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\ +) + +#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\ + r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\ + g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\ + (type) (y + to_rgb [4] * i + to_rgb [5] * q)\ +) + +#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1) + +enum { rgb_kernel_size = burst_size / alignment_count }; +enum { rgb_bias = rgb_unit * 2 * snes_ntsc_rgb_builder }; + +typedef struct pixel_info_t +{ + int offset; + float negate; + float kernel [4]; +} pixel_info_t; + +#if rescale_in > 1 + #define PIXEL_OFFSET_( ntsc, scaled ) \ + (kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \ + (kernel_size * 2 * scaled)) + + #define PIXEL_OFFSET( ntsc, scaled ) \ + PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\ + (((scaled) + rescale_out * 10) % rescale_out) ),\ + (1.0f - (((ntsc) + 100) & 2)) +#else + #define PIXEL_OFFSET( ntsc, scaled ) \ + (kernel_size / 2 + (ntsc) - (scaled)),\ + (1.0f - (((ntsc) + 100) & 2)) +#endif + +extern pixel_info_t const snes_ntsc_pixels [alignment_count]; + +/* Generate pixel at all burst phases and column alignments */ +static void gen_kernel( init_t* impl, float y, float i, float q, snes_ntsc_rgb_t* out ) +{ + /* generate for each scanline burst phase */ + float const* to_rgb = impl->to_rgb; + int burst_remain = burst_count; + y -= rgb_offset; + do + { + /* Encode yiq into *two* composite signals (to allow control over artifacting). + Convolve these with kernels which: filter respective components, apply + sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack + into integer. Based on algorithm by NewRisingSun. */ + pixel_info_t const* pixel = snes_ntsc_pixels; + int alignment_remain = alignment_count; + do + { + /* negate is -1 when composite starts at odd multiple of 2 */ + float const yy = y * impl->fringing * pixel->negate; + float const ic0 = (i + yy) * pixel->kernel [0]; + float const qc1 = (q + yy) * pixel->kernel [1]; + float const ic2 = (i - yy) * pixel->kernel [2]; + float const qc3 = (q - yy) * pixel->kernel [3]; + + float const factor = impl->artifacts * pixel->negate; + float const ii = i * factor; + float const yc0 = (y + ii) * pixel->kernel [0]; + float const yc2 = (y - ii) * pixel->kernel [2]; + + float const qq = q * factor; + float const yc1 = (y + qq) * pixel->kernel [1]; + float const yc3 = (y - qq) * pixel->kernel [3]; + + float const* k = &impl->kernel [pixel->offset]; + int n; + ++pixel; + for ( n = rgb_kernel_size; n; --n ) + { + float i = k[0]*ic0 + k[2]*ic2; + float q = k[1]*qc1 + k[3]*qc3; + float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 + + k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset; + if ( rescale_out <= 1 ) + k--; + else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] ) + k += kernel_size * 2 - 1; + else + k -= kernel_size * 2 * (rescale_out - 1) + 2; + { + int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g ); + *out++ = PACK_RGB( r, g, b ) - rgb_bias; + } + } + } + while ( alignment_count > 1 && --alignment_remain ); + + if ( burst_count <= 1 ) + break; + + to_rgb += 6; + + ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */ + } + while ( --burst_remain ); +} + +static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out ); + +#if DISABLE_CORRECTION + #define CORRECT_ERROR( a ) { out [i] += rgb_bias; } + #define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; } +#else + #define CORRECT_ERROR( a ) { out [a] += error; } + #define DISTRIBUTE_ERROR( a, b, c ) {\ + snes_ntsc_rgb_t fourth = (error + 2 * snes_ntsc_rgb_builder) >> 2;\ + fourth &= (rgb_bias >> 1) - snes_ntsc_rgb_builder;\ + fourth -= rgb_bias >> 2;\ + out [a] += fourth;\ + out [b] += fourth;\ + out [c] += fourth;\ + out [i] += error - (fourth * 3);\ + } +#endif + +#define RGB_PALETTE_OUT( rgb, out_ )\ +{\ + unsigned char* out = (out_);\ + snes_ntsc_rgb_t clamped = (rgb);\ + SNES_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\ + out [0] = (unsigned char) (clamped >> 21);\ + out [1] = (unsigned char) (clamped >> 11);\ + out [2] = (unsigned char) (clamped >> 1);\ +} + +/* blitter related */ + +#ifndef restrict + #if defined (__GNUC__) + #define restrict __restrict__ + #elif defined (_MSC_VER) && _MSC_VER > 1300 + #define restrict __restrict + #else + /* no support for restricted pointers */ + #define restrict + #endif +#endif + +#include + +#if SNES_NTSC_OUT_DEPTH <= 16 + #if USHRT_MAX == 0xFFFF + typedef unsigned short snes_ntsc_out_t; + #else + #error "Need 16-bit int type" + #endif + +#else + #if UINT_MAX == 0xFFFFFFFF + typedef unsigned int snes_ntsc_out_t; + #elif ULONG_MAX == 0xFFFFFFFF + typedef unsigned long snes_ntsc_out_t; + #else + #error "Need 32-bit int type" + #endif + +#endif diff --git a/src/ui/config.cpp b/src/ui/config.cpp index 0b9aa0d4..7c26c405 100644 --- a/src/ui/config.cpp +++ b/src/ui/config.cpp @@ -9,21 +9,21 @@ struct System { static IntegerSetting regulate_speed, speed; static IntegerSetting speed_slowest, speed_slow, speed_normal, speed_fast, speed_fastest; } system; -StringSetting System::video(&config_file, "system.video", "Video hardware interface", ""); -StringSetting System::audio(&config_file, "system.audio", "Audio hardware interface", ""); -StringSetting System::input(&config_file, "system.input", "Input hardware interface", ""); +StringSetting System::video(&config(), "system.video", "Video hardware interface", ""); +StringSetting System::audio(&config(), "system.audio", "Audio hardware interface", ""); +StringSetting System::input(&config(), "system.input", "Input hardware interface", ""); -StringSetting System::video_flags(&config_file, "system.video_flags", "Video hardware interface flags", ""); -StringSetting System::audio_flags(&config_file, "system.audio_flags", "Audio hardware interface flags", ""); -StringSetting System::input_flags(&config_file, "system.input_flags", "Input hardware interface flags", ""); +StringSetting System::video_flags(&config(), "system.video_flags", "Video hardware interface flags", ""); +StringSetting System::audio_flags(&config(), "system.audio_flags", "Audio hardware interface flags", ""); +StringSetting System::input_flags(&config(), "system.input_flags", "Input hardware interface flags", ""); -IntegerSetting System::regulate_speed(&config_file, "system.regulate_speed", "Regulate speed to 60hz (NTSC) / 50hz (PAL)", IntegerSetting::Boolean, true); +IntegerSetting System::regulate_speed(&config(), "system.regulate_speed", "Regulate speed to 60hz (NTSC) / 50hz (PAL)", IntegerSetting::Boolean, true); IntegerSetting System::speed (0, "system.speed", "Current speed regulation setting (1-5)", IntegerSetting::Decimal, 3); -IntegerSetting System::speed_slowest (&config_file, "system.speed_slowest", "Slowest speed setting", IntegerSetting::Decimal, 50); -IntegerSetting System::speed_slow (&config_file, "system.speed_slow", "Slow speed setting", IntegerSetting::Decimal, 75); -IntegerSetting System::speed_normal (&config_file, "system.speed_normal", "Normal speed setting", IntegerSetting::Decimal, 100); -IntegerSetting System::speed_fast (&config_file, "system.speed_fast", "Fast speed setting", IntegerSetting::Decimal, 150); -IntegerSetting System::speed_fastest (&config_file, "system.speed_fastest", "Fastest speed setting", IntegerSetting::Decimal, 200); +IntegerSetting System::speed_slowest (&config(), "system.speed_slowest", "Slowest speed setting", IntegerSetting::Decimal, 50); +IntegerSetting System::speed_slow (&config(), "system.speed_slow", "Slow speed setting", IntegerSetting::Decimal, 75); +IntegerSetting System::speed_normal (&config(), "system.speed_normal", "Normal speed setting", IntegerSetting::Decimal, 100); +IntegerSetting System::speed_fast (&config(), "system.speed_fast", "Fast speed setting", IntegerSetting::Decimal, 150); +IntegerSetting System::speed_fastest (&config(), "system.speed_fastest", "Fastest speed setting", IntegerSetting::Decimal, 200); struct Video { static IntegerSetting synchronize; @@ -34,47 +34,47 @@ struct Video { static IntegerSetting frameskip; static IntegerSetting use_vram; } video; -IntegerSetting Video::synchronize(&config_file, "video.synchronize", "Synchronize to video refresh rate.", IntegerSetting::Boolean, false); +IntegerSetting Video::synchronize(&config(), "video.synchronize", "Synchronize to video refresh rate.", IntegerSetting::Boolean, false); IntegerSetting Video::fullscreen(0, "video.fullscreen", "", IntegerSetting::Boolean, false); -IntegerSetting Video::multiplier(&config_file, "video.multiplier", "Video output size multiplier (1-5x)\n" +IntegerSetting Video::multiplier(&config(), "video.multiplier", "Video output size multiplier (1-5x)\n" "1 = 1x (~256x224)\n" "2 = 2x (~512x448)\n" "etc.", IntegerSetting::Decimal, 2); -IntegerSetting Video::aspect_correction(&config_file, "video.aspect_correction", +IntegerSetting Video::aspect_correction(&config(), "video.aspect_correction", "Correct video aspect ratio\n" "Formula: width = width * video.aspect__x / video.aspect__y", IntegerSetting::Boolean, true); -IntegerSetting Video::region(&config_file, "video.region", "Video output region\n" +IntegerSetting Video::region(&config(), "video.region", "Video output region\n" "0 = NTSC, 1 = PAL", IntegerSetting::Decimal, 0); -IntegerSetting Video::aspect_ntsc_x(&config_file, "video.aspect_ntsc_x", "", IntegerSetting::Decimal, 54); -IntegerSetting Video::aspect_ntsc_y(&config_file, "video.aspect_ntsc_y", "", IntegerSetting::Decimal, 47); -IntegerSetting Video::aspect_pal_x (&config_file, "video.aspect_pal_x", "", IntegerSetting::Decimal, 32); -IntegerSetting Video::aspect_pal_y (&config_file, "video.aspect_pal_y", "", IntegerSetting::Decimal, 23); +IntegerSetting Video::aspect_ntsc_x(&config(), "video.aspect_ntsc_x", "", IntegerSetting::Decimal, 54); +IntegerSetting Video::aspect_ntsc_y(&config(), "video.aspect_ntsc_y", "", IntegerSetting::Decimal, 47); +IntegerSetting Video::aspect_pal_x (&config(), "video.aspect_pal_x", "", IntegerSetting::Decimal, 32); +IntegerSetting Video::aspect_pal_y (&config(), "video.aspect_pal_y", "", IntegerSetting::Decimal, 23); -IntegerSetting Video::hardware_filter(&config_file, "video.hardware_filter", "Video hardware filter\n" +IntegerSetting Video::hardware_filter(&config(), "video.hardware_filter", "Video hardware filter\n" "0 = Point\n" "1 = Linear\n", IntegerSetting::Decimal, 1); -IntegerSetting Video::software_filter(&config_file, "video.software_filter", "Video software filter\n" +IntegerSetting Video::software_filter(&config(), "video.software_filter", "Video software filter\n" "0 = None\n" "1 = NTSC\n" "2 = HQ2x\n" "3 = Scale2x\n", IntegerSetting::Decimal, 0); IntegerSetting Video::frameskip(0, "video.frameskip", "Video frameskip", IntegerSetting::Decimal, 0); -IntegerSetting Video::use_vram(&config_file, "video.use_vram", "Use Video RAM instead of System RAM", IntegerSetting::Boolean, true); +IntegerSetting Video::use_vram(&config(), "video.use_vram", "Use Video RAM instead of System RAM", IntegerSetting::Boolean, true); struct Audio { static IntegerSetting synchronize; static IntegerSetting frequency; static IntegerSetting latency; } audio; -IntegerSetting Audio::synchronize(&config_file, "audio.synchronize", "Synchronize to audio sample rate.", IntegerSetting::Boolean, true); -IntegerSetting Audio::frequency(&config_file, "audio.frequency", "Default audio playback frequency.", IntegerSetting::Decimal, 32000); -IntegerSetting Audio::latency(&config_file, "audio.latency", "Audio playback latency in milliseconds.\n" +IntegerSetting Audio::synchronize(&config(), "audio.synchronize", "Synchronize to audio sample rate.", IntegerSetting::Boolean, true); +IntegerSetting Audio::frequency(&config(), "audio.frequency", "Default audio playback frequency.", IntegerSetting::Decimal, 32000); +IntegerSetting Audio::latency(&config(), "audio.latency", "Audio playback latency in milliseconds.\n" "Specifies how long audio playback is delayed compared to a real SNES.\n" "A delay is necessary to allow smooth audio playback via buffering.\n" "Raising this value may help with audio playback problems, but will decrease\n" @@ -94,7 +94,7 @@ struct Input { } joypad2; } input; -IntegerSetting Input::axis_resistance(&config_file, "input.axis_resistance", +IntegerSetting Input::axis_resistance(&config(), "input.axis_resistance", "Axis resistance for all analog joypads\n" "Affects responsiveness of analog stick movement by specifying what percentage\n" "in any given direction the axis must be pressed to trigger a button press.\n" @@ -106,40 +106,40 @@ IntegerSetting Input::axis_resistance(&config_file, "input.axis_resistance", "Note: Values below 10 or above 90 are not recommended and may not work at all.", IntegerSetting::Decimal, 75); -IntegerSetting Input::allow_invalid_input(&config_file, "input.allow_invalid_input", +IntegerSetting Input::allow_invalid_input(&config(), "input.allow_invalid_input", "Allow up+down and left+right combinations (not recommended)", IntegerSetting::Boolean, false); -StringSetting Input::Joypad1::up (&config_file, "input.joypad1.up", "", "up"); -StringSetting Input::Joypad1::down (&config_file, "input.joypad1.down", "", "down"); -StringSetting Input::Joypad1::left (&config_file, "input.joypad1.left", "", "left"); -StringSetting Input::Joypad1::right (&config_file, "input.joypad1.right", "", "right"); -StringSetting Input::Joypad1::a (&config_file, "input.joypad1.a", "", "x"); -StringSetting Input::Joypad1::b (&config_file, "input.joypad1.b", "", "z"); -StringSetting Input::Joypad1::x (&config_file, "input.joypad1.x", "", "s"); -StringSetting Input::Joypad1::y (&config_file, "input.joypad1.y", "", "a"); -StringSetting Input::Joypad1::l (&config_file, "input.joypad1.l", "", "d"); -StringSetting Input::Joypad1::r (&config_file, "input.joypad1.r", "", "c"); -StringSetting Input::Joypad1::select(&config_file, "input.joypad1.select", "", "rshift"); -StringSetting Input::Joypad1::start (&config_file, "input.joypad1.start", "", "enter"); +StringSetting Input::Joypad1::up (&config(), "input.joypad1.up", "", "up"); +StringSetting Input::Joypad1::down (&config(), "input.joypad1.down", "", "down"); +StringSetting Input::Joypad1::left (&config(), "input.joypad1.left", "", "left"); +StringSetting Input::Joypad1::right (&config(), "input.joypad1.right", "", "right"); +StringSetting Input::Joypad1::a (&config(), "input.joypad1.a", "", "x"); +StringSetting Input::Joypad1::b (&config(), "input.joypad1.b", "", "z"); +StringSetting Input::Joypad1::x (&config(), "input.joypad1.x", "", "s"); +StringSetting Input::Joypad1::y (&config(), "input.joypad1.y", "", "a"); +StringSetting Input::Joypad1::l (&config(), "input.joypad1.l", "", "d"); +StringSetting Input::Joypad1::r (&config(), "input.joypad1.r", "", "c"); +StringSetting Input::Joypad1::select(&config(), "input.joypad1.select", "", "rshift"); +StringSetting Input::Joypad1::start (&config(), "input.joypad1.start", "", "enter"); -StringSetting Input::Joypad2::up (&config_file, "input.joypad2.up", "", "t"); -StringSetting Input::Joypad2::down (&config_file, "input.joypad2.down", "", "g"); -StringSetting Input::Joypad2::left (&config_file, "input.joypad2.left", "", "f"); -StringSetting Input::Joypad2::right (&config_file, "input.joypad2.right", "", "h"); -StringSetting Input::Joypad2::a (&config_file, "input.joypad2.a", "", "k"); -StringSetting Input::Joypad2::b (&config_file, "input.joypad2.b", "", "j"); -StringSetting Input::Joypad2::x (&config_file, "input.joypad2.x", "", "i"); -StringSetting Input::Joypad2::y (&config_file, "input.joypad2.y", "", "u"); -StringSetting Input::Joypad2::l (&config_file, "input.joypad2.l", "", "o"); -StringSetting Input::Joypad2::r (&config_file, "input.joypad2.r", "", "l"); -StringSetting Input::Joypad2::select(&config_file, "input.joypad2.select", "", "lbracket"); -StringSetting Input::Joypad2::start (&config_file, "input.joypad2.start", "", "rbracket"); +StringSetting Input::Joypad2::up (&config(), "input.joypad2.up", "", "t"); +StringSetting Input::Joypad2::down (&config(), "input.joypad2.down", "", "g"); +StringSetting Input::Joypad2::left (&config(), "input.joypad2.left", "", "f"); +StringSetting Input::Joypad2::right (&config(), "input.joypad2.right", "", "h"); +StringSetting Input::Joypad2::a (&config(), "input.joypad2.a", "", "k"); +StringSetting Input::Joypad2::b (&config(), "input.joypad2.b", "", "j"); +StringSetting Input::Joypad2::x (&config(), "input.joypad2.x", "", "i"); +StringSetting Input::Joypad2::y (&config(), "input.joypad2.y", "", "u"); +StringSetting Input::Joypad2::l (&config(), "input.joypad2.l", "", "o"); +StringSetting Input::Joypad2::r (&config(), "input.joypad2.r", "", "l"); +StringSetting Input::Joypad2::select(&config(), "input.joypad2.select", "", "lbracket"); +StringSetting Input::Joypad2::start (&config(), "input.joypad2.start", "", "rbracket"); struct Misc { static IntegerSetting show_frame_counter; } misc; -IntegerSetting Misc::show_frame_counter(&config_file, "misc.show_frame_counter", "Display frame counter", IntegerSetting::Boolean, true); +IntegerSetting Misc::show_frame_counter(&config(), "misc.show_frame_counter", "Display frame counter", IntegerSetting::Boolean, true); }; diff --git a/src/ui/lui/event.cpp b/src/ui/lui/event.cpp index 4e947d5d..fb904381 100644 --- a/src/ui/lui/event.cpp +++ b/src/ui/lui/event.cpp @@ -106,6 +106,32 @@ char fn[PATH_MAX]; window_cheat_editor.refresh(); } +void load_rom_st() { +char fn[PATH_MAX]; + if(load_rom(fn) == false)return; + + if(cartridge.loaded() == true)cartridge.unload(); + cartridge.load_begin(Cartridge::CART_ST); + cartridge.load(fn); + cartridge.load_end(); + snes.power(); + window_cheat_editor.refresh(); +} + +void load_rom_stdual() { +char fn_a[PATH_MAX], fn_b[PATH_MAX]; + if(load_rom(fn_a) == false)return; + if(load_rom(fn_b) == false)return; + + if(cartridge.loaded() == true)cartridge.unload(); + cartridge.load_begin(Cartridge::CART_STDUAL); + cartridge.load(fn_a); + cartridge.load(fn_b); + cartridge.load_end(); + snes.power(); + window_cheat_editor.refresh(); +} + void unload_rom() { if(cartridge.loaded() == true) { cartridge.unload(); diff --git a/src/ui/lui/event.h b/src/ui/lui/event.h index afe5da7f..cda6dfd5 100644 --- a/src/ui/lui/event.h +++ b/src/ui/lui/event.h @@ -8,6 +8,8 @@ void toggle_fullscreen(); bool load_rom(char *fn); void load_rom(); +void load_rom_st(); +void load_rom_stdual(); void unload_rom(); void reset(); void power(); diff --git a/src/ui/lui/main.cpp b/src/ui/lui/main.cpp index 2bbb8854..caba8c93 100644 --- a/src/ui/lui/main.cpp +++ b/src/ui/lui/main.cpp @@ -52,21 +52,46 @@ void set_config_filename() { strcat(config::filename, "/bsnes.cfg"); } +void get_base_path() { +#if defined(PLATFORM_WIN) +char full_name[PATH_MAX]; + GetFullPathName(__argv[0], PATH_MAX, full_name, 0); + +string t; + strcpy(t, full_name); + + if(strlen(t) != 0) { + //remove program name + replace(t, "\\", "/"); + for(int i = strlen(t) - 1; i >= 0; i--) { + if(strptr(t)[i] == '/' || strptr(t)[i] == '\\') { + strptr(t)[i] = 0; + break; + } + } + } + + if(strend(t, "/") == false) { strcat(t, "/"); } + config::path.base = strptr(t); +#endif +} + void run() { while(ui::events_pending() == true) { ui::run(); } if(cartridge.loaded() == true) { snes.runtoframe(); event::update_frame_counter(); + } else { //prevent bsnes from consuming 100% CPU resources when idle + #if defined(PLATFORM_WIN) + Sleep(1); + #elif defined(PLATFORM_X) + usleep(1); + #endif } - -#if defined(PLATFORM_WIN) -//prevent bsnes from consuming 100% CPU resources when idle - else { Sleep(1); } -#endif } #if defined(PLATFORM_WIN) -int __stdcall WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow) { +int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { int argc = __argc; char **argv = __argv; #else @@ -75,10 +100,11 @@ int main(int argc, char *argv[]) { //int main(int argc, char *argv[]) { set_config_filename(); + get_base_path(); ui::init(); - config_file.load(config::filename); - config_file.save(config::filename); //in case program crashes on first run, config file settings can be modified + config::config().load(config::filename); + config::config().save(config::filename); //in case program crashes on first run, config file settings can be modified init_snes(); ui_init(); @@ -95,7 +121,7 @@ int main(int argc, char *argv[]) { event::unload_rom(); - config_file.save(config::filename); + config::config().save(config::filename); term_snes(); ui_term(); ui::term(); diff --git a/src/ui/lui/settings/ui_advanced.cpp b/src/ui/lui/settings/ui_advanced.cpp index 1d5538ea..f971111f 100644 --- a/src/ui/lui/settings/ui_advanced.cpp +++ b/src/ui/lui/settings/ui_advanced.cpp @@ -4,10 +4,10 @@ ui::Control *control = (ui::Control*)param; int pos = list.get_selection(); set_val.enable(pos >= 0); set_def.enable(pos >= 0); - if(pos >= 0 && pos < config_file.list_count) { - desc.set_text(string() << "(default = " << config_file.list[pos]->def << ")\n" << config_file.list[pos]->desc); + if(pos >= 0 && pos < config::config().list_count) { + desc.set_text(string() << "(default = " << config::config().list[pos]->def << ")\n" << config::config().list[pos]->desc); string val; - config_file.list[pos]->get(val); + config::config().list[pos]->get(val); edit_val.set_text(strptr(val)); } } else if(id == ui::Message::Clicked && control == &set_val) { @@ -23,23 +23,23 @@ ui::Control *control = (ui::Control*)param; void AdvancedWindow::read_config(uint pos, string &data) { strcpy(data, "?|?|?"); - if(pos >= config_file.list_count)return; + if(pos >= config::config().list_count)return; string name, val; - name = config_file.list[pos]->name; - config_file.list[pos]->get(val); - if(val != config_file.list[pos]->def) { strcat(name, " (*)"); } + name = config::config().list[pos]->name; + config::config().list[pos]->get(val); + if(val != config::config().list[pos]->def) { strcat(name, " (*)"); } sprintf(data, "%s|%s|%s", strptr(name), - config_file.list[pos]->type == Setting::String ? "String" : "Integer", + config::config().list[pos]->type == Setting::String ? "String" : "Integer", strptr(val) ); } void AdvancedWindow::update(uint pos, const char *data) { - if(pos >= config_file.list_count)return; - config_file.list[pos]->set(data ? data : config_file.list[pos]->def); + if(pos >= config::config().list_count)return; + config::config().list[pos]->set(data ? data : config::config().list[pos]->def); string val; - config_file.list[pos]->get(val); + config::config().list[pos]->get(val); edit_val.set_text(strptr(val)); read_config(pos, val); list.set_item(pos, strptr(val)); @@ -50,20 +50,27 @@ void AdvancedWindow::setup() { create(0, 475, 355); int x = 0, y = 0; - list.create(*this, ui::Listbox::Header | ui::Listbox::VerticalScrollAlways, x, y, 475, 235, "Name|Type|Value"); - y += 240; - desc.create(*this, ui::Editbox::Multiline | ui::Editbox::Readonly, x, y, 475, 80, +int bh = ui::Button::ideal_height; +int eh = ui::Editbox::ideal_height; + bh = max(bh, eh); //set both editbox and button to same size, as they are on the same line +int th = 80; +int lh = 355 - th - bh - 10; + list.create(*this, ui::Listbox::Header | ui::Listbox::VerticalScrollAlways, x, y, 475, lh, "Name|Type|Value"); + y += lh + 5; + + desc.create(*this, ui::Editbox::Multiline | ui::Editbox::Readonly, x, y, 475, th, "\n" "Warning: modifification of certain variables will not take effect until\n" "bsnes is restarted, and corresponding UI elements will not be updated\n" "to reflect changes here. (*) = modified" ); - y += 85; - edit_val.create(*this, 0, x, y, 265, 30, ""); - set_val.create(*this, 0, x + 270, y, 100, 30, "Set"); - set_def.create(*this, 0, x + 375, y, 100, 30, "Default"); + y += th + 5; - for(int i = 0; i < config_file.list_count; i++) { + edit_val.create(*this, 0, x, y, 265, bh, ""); + set_val.create(*this, 0, x + 270, y, 100, bh, "Set"); + set_def.create(*this, 0, x + 375, y, 100, bh, "Default"); + + for(int i = 0; i < config::config().list_count; i++) { string val; read_config(i, val); list.add_item(strptr(val)); diff --git a/src/ui/lui/settings/ui_advanced.h b/src/ui/lui/settings/ui_advanced.h index 5799ba89..5c9a97bc 100644 --- a/src/ui/lui/settings/ui_advanced.h +++ b/src/ui/lui/settings/ui_advanced.h @@ -1,11 +1,11 @@ -class AdvancedWindow : public ui::Window { public: -ui::Listbox list; -ui::Editbox desc; -ui::Editbox edit_val; -ui::Button set_val; -ui::Button set_def; - bool message(uint id, uintptr_t param); - void read_config(uint pos, string &data); - void update(uint pos, const char *data); - void setup(); -} window_advanced; +class AdvancedWindow : public ui::Window { public: +ui::Listbox list; +ui::Editbox desc; +ui::Editbox edit_val; +ui::Button set_val; +ui::Button set_def; + bool message(uint id, uintptr_t param); + void read_config(uint pos, string &data); + void update(uint pos, const char *data); + void setup(); +} window_advanced; diff --git a/src/ui/lui/settings/ui_cheateditor.cpp b/src/ui/lui/settings/ui_cheateditor.cpp index 7a4785a2..cc68ad8a 100644 --- a/src/ui/lui/settings/ui_cheateditor.cpp +++ b/src/ui/lui/settings/ui_cheateditor.cpp @@ -64,16 +64,19 @@ void CheatEditorWindow::setup() { create(0, 475, 355); int x = 0, y = 0; - list.create(*this, ui::Listbox::Header | ui::Listbox::VerticalScrollAlways, x, y, 475, 285, "Status|Code|Description"); - y += 290; +int bh = ui::Button::ideal_height; +int eh = ui::Editbox::ideal_height; +int lh = 355 - bh - eh - 10; + list.create(*this, ui::Listbox::Header | ui::Listbox::VerticalScrollAlways, x, y, 475, lh, "Status|Code|Description"); + y += lh + 5; - add_code.create (*this, 0, x, y, 155, 30, "Add Code"); - toggle_code.create(*this, 0, x + 160, y, 155, 30, "Toggle Status"); - delete_code.create(*this, 0, x + 320, y, 155, 30, "Delete Code"); - y += 35; + add_code.create (*this, 0, x, y, 155, bh, "Add Code"); + toggle_code.create(*this, 0, x + 160, y, 155, bh, "Toggle Status"); + delete_code.create(*this, 0, x + 320, y, 155, bh, "Delete Code"); + y += bh + 5; - code.create(*this, 0, x, y, 155, 30, ""); - desc.create(*this, 0, x + 160, y, 315, 30, ""); + code.create(*this, 0, x, y, 155, eh, ""); + desc.create(*this, 0, x + 160, y, 315, eh, ""); refresh(); } diff --git a/src/ui/lui/settings/ui_inputconfig.cpp b/src/ui/lui/settings/ui_inputconfig.cpp index a66b1f51..98739590 100644 --- a/src/ui/lui/settings/ui_inputconfig.cpp +++ b/src/ui/lui/settings/ui_inputconfig.cpp @@ -131,29 +131,35 @@ void InputConfigWindow::setup() { create(0, 475, 355); int x = 0, y = 0; - lportA.create(*this, 0, x, y, 105, 30, "Controller Port A:"); - portA.create (*this, 0, x + 110, y, 125, 30); +int lh = ui::Label::ideal_height; +int ch = ui::Combobox::ideal_height; +int bh = ui::Button::ideal_height; +int lbh = 355 - lh - ch - bh - 10; + lportA.create(*this, 0, x, y, 235, lh, "Controller Port A:"); + lportB.create(*this, 0, x + 240, y, 235, lh, "Controller Port B:"); + y += lh; + + portA.create (*this, 0, x, y, 235, ch); portA.add_item("None"); portA.add_item("Joypad 1"); portA.add_item("Joypad 2"); portA.set_selection(1); portA.disable(); - lportB.create(*this, 0, x + 240, y, 105, 30, "Controller Port B:"); - portB.create (*this, 0, x + 350, y, 125, 30); + portB.create (*this, 0, x + 240, y, 235, ch); portB.add_item("None"); portB.add_item("Joypad 1"); portB.add_item("Joypad 2"); portB.set_selection(2); portB.disable(); - y += 35; + y += ch + 5; - list.create(*this, ui::Listbox::Header | ui::Listbox::VerticalScrollAlways, x, y, 475, 285, "Name|Value"); + list.create(*this, ui::Listbox::Header | ui::Listbox::VerticalScrollAlways, x, y, 475, lbh, "Name|Value"); for(uint i = 0; i < 24; i++) { list.add_item("???|???"); } - y += 290; + y += lbh + 5; - setkey.create(*this, 0, x, y, 235, 30, "Assign Key"); - clrkey.create(*this, 0, x + 240, y, 235, 30, "Unassign Key"); + setkey.create(*this, 0, x, y, 235, bh, "Assign Key"); + clrkey.create(*this, 0, x + 240, y, 235, bh, "Unassign Key"); refresh_list(); window_input_capture.setup(); @@ -199,6 +205,7 @@ void InputCaptureWindow::show() { } void InputCaptureWindow::setup() { - create(ui::Window::Center, 350, 100, "bsnes Key Capture"); - label.create(*this, 0, 5, 5, 340, 90); +int lh = ui::Label::ideal_height; + create(ui::Window::Center, 350, lh * 3, "bsnes Key Capture"); + label.create(*this, 0, 5, lh, 340, lh); } diff --git a/src/ui/lui/settings/ui_rastersettings.cpp b/src/ui/lui/settings/ui_rastersettings.cpp index f42c7717..4c99f9b5 100644 --- a/src/ui/lui/settings/ui_rastersettings.cpp +++ b/src/ui/lui/settings/ui_rastersettings.cpp @@ -88,29 +88,32 @@ void RasterSettingsWindow::setup() { create(0, 475, 355); int x = 0, y = 0; - lcontrast.create(*this, 0, x, y, 100, 25); - contrast.create (*this, 0, x + 100, y, 375, 25, 192); - y += 25; +int sh = ui::Slider::ideal_height; +int kh = ui::Checkbox::ideal_height; +int bh = ui::Button::ideal_height; + lcontrast.create(*this, 0, x, y, 100, sh); + contrast.create (*this, 0, x + 100, y, 375, sh, 192); + y += sh; - lbrightness.create(*this, 0, x, y, 100, 25); - brightness.create (*this, 0, x + 100, y, 375, 25, 192); - y += 25; + lbrightness.create(*this, 0, x, y, 100, sh); + brightness.create (*this, 0, x + 100, y, 375, sh, 192); + y += sh; - lgamma.create(*this, 0, x, y, 100, 25); - gamma.create (*this, 0, x + 100, y, 375, 25, 191); - y += 25; + lgamma.create(*this, 0, x, y, 100, sh); + gamma.create (*this, 0, x + 100, y, 375, sh, 191); + y += sh; - gamma_ramp.create(*this, 0, x, y, 235, 20, "Gamma ramp"); - sepia.create(*this, 0, x + 240, y, 235, 20, "Sepia"); - y += 20; + gamma_ramp.create(*this, 0, x, y, 235, kh, "Gamma ramp"); + sepia.create(*this, 0, x + 240, y, 235, kh, "Sepia"); + y += kh; - grayscale.create(*this, 0, x, y, 235, 20, "Grayscale"); - invert.create(*this, 0, x + 240, y, 235, 20, "Invert colors"); - y += 20; + grayscale.create(*this, 0, x, y, 235, kh, "Grayscale"); + invert.create(*this, 0, x + 240, y, 235, kh, "Invert colors"); + y += kh + 5; - y += 5; - preset_optimal.create (*this, 0, x, y, 235, 30, "Optimal Preset"); - preset_standard.create(*this, 0, x + 240, y, 235, 30, "Standard Preset"); + preset_optimal.create (*this, 0, x, y, 235, bh, "Optimal Preset"); + preset_standard.create(*this, 0, x + 240, y, 235, bh, "Standard Preset"); + y += bh; sync_ui(); } diff --git a/src/ui/lui/settings/ui_settings.cpp b/src/ui/lui/settings/ui_settings.cpp index 4c830f78..8bda1988 100644 --- a/src/ui/lui/settings/ui_settings.cpp +++ b/src/ui/lui/settings/ui_settings.cpp @@ -1,6 +1,7 @@ void SettingsWindow::setup() { create(ui::Window::Center, 640, 365, "bsnes Configuration Settings"); panel_list.create(*this, 0, 5, 5, 150, 355); +//panel_list.add_item("Video Settings"); panel_list.add_item("Raster Settings"); panel_list.add_item("Input Configuration"); panel_list.add_item("Cheat Code Editor"); @@ -23,6 +24,7 @@ bool SettingsWindow::message(uint id, uintptr_t param) { if(id == ui::Message::Changed && (ui::Control*)param == &panel_list) { switch(panel_list.get_selection()) { + //case 0: panel.attach(window_video_settings); break; case 0: panel.attach(window_raster_settings); break; case 1: panel.attach(window_input_config); break; case 2: panel.attach(window_cheat_editor); break; diff --git a/src/ui/lui/settings/ui_videosettings.cpp b/src/ui/lui/settings/ui_videosettings.cpp new file mode 100644 index 00000000..0fabc663 --- /dev/null +++ b/src/ui/lui/settings/ui_videosettings.cpp @@ -0,0 +1,59 @@ +bool VideoSettingsWindow::message(uint id, uintptr_t param) { + return true; +} + +void VideoSettingsWindow::setup() { + create(0, 475, 355); + +int x = 0, y = 0; +int lh = ui::Label::ideal_height; +int ch = ui::Combobox::ideal_height; +int kh = ui::Checkbox::ideal_height; +int eh = ui::Editbox::ideal_height; + label_select.create(*this, 0, x, y, 475, lh, "Select video mode to configure:"); + y += lh; + mode_select.create(*this, 0, x, y, 475, ch); + mode_select.add_item("Windowed mode"); + mode_select.add_item("Pseudo-fullscreen mode"); + mode_select.add_item("Fullscreen mode"); + y += ch + 5; + + label_region.create(*this, 0, x, y, 235, lh, "Region:"); + label_scalar.create(*this, 0, x + 240, y, 235, lh, "Scale:"); + y += lh; + region.create(*this, 0, x, y, 235, ch); + region.add_item("NTSC"); + region.add_item("PAL"); + scalar.create(*this, 0, x + 240, y, 235, ch); + scalar.add_item("100%"); + scalar.add_item("200%"); + scalar.add_item("300%"); + scalar.add_item("400%"); + scalar.add_item("500%"); + y += ch; + + label_hwfilter.create(*this, 0, x, y, 235, lh, "Hardware filter:"); + label_swfilter.create(*this, 0, x + 240, y, 235, lh, "Software filter:"); + y += lh; + hwfilter.create(*this, 0, x, y, 235, ch); + hwfilter.add_item("Point"); + hwfilter.add_item("Linear"); + swfilter.create(*this, 0, x + 240, y, 235, ch); + swfilter.add_item("None"); + swfilter.add_item("NTSC"); + swfilter.add_item("HQ2x"); + swfilter.add_item("Scale2x"); + y += ch + 5; + + aspect.create(*this, 0, x, y, 235, kh, "Correct aspect ratio"); + vsync.create(*this, 0, x + 240, y, 235, kh, "Sync to vertical retrace"); + y += kh + 5; + + label_fsmode.create(*this, 0, x, y, 475, lh, "Fullscreen mode (width x height x refresh rate):"); + y += lh; + + fs_width.create(*this, 0, x, y, 155, eh, ""); + fs_height.create(*this, 0, x + 160, y, 155, eh, ""); + fs_refresh.create(*this, 0, x + 320, y, 155, eh, ""); + y += eh + 5; +} diff --git a/src/ui/lui/settings/ui_videosettings.h b/src/ui/lui/settings/ui_videosettings.h new file mode 100644 index 00000000..eb4fed9a --- /dev/null +++ b/src/ui/lui/settings/ui_videosettings.h @@ -0,0 +1,14 @@ +class VideoSettingsWindow : public ui::Window { public: +ui::Label label_select; +ui::Combobox mode_select; + +ui::Label label_region, label_scalar, label_hwfilter, label_swfilter; +ui::Combobox region, scalar, hwfilter, swfilter; + +ui::Checkbox aspect, vsync; + +ui::Label label_fsmode; +ui::Editbox fs_width, fs_height, fs_refresh; + bool message(uint, uintptr_t); + void setup(); +} window_video_settings; diff --git a/src/ui/lui/ui.cpp b/src/ui/lui/ui.cpp index 2ab9cf1f..4f8c48ac 100644 --- a/src/ui/lui/ui.cpp +++ b/src/ui/lui/ui.cpp @@ -2,6 +2,7 @@ #include "ui_about.cpp" #include "settings/ui_settings.cpp" +#include "settings/ui_videosettings.cpp" #include "settings/ui_rastersettings.cpp" #include "settings/ui_inputconfig.cpp" #include "settings/ui_cheateditor.cpp" @@ -27,6 +28,7 @@ void ui_init() { window_main.setup(); window_about.setup(); + window_video_settings.setup(); window_raster_settings.setup(); window_input_config.setup(); window_cheat_editor.setup(); diff --git a/src/ui/lui/ui.h b/src/ui/lui/ui.h index 6634a37e..fa58fd11 100644 --- a/src/ui/lui/ui.h +++ b/src/ui/lui/ui.h @@ -2,6 +2,7 @@ #include "ui_about.h" #include "settings/ui_settings.h" +#include "settings/ui_videosettings.h" #include "settings/ui_rastersettings.h" #include "settings/ui_inputconfig.h" #include "settings/ui_cheateditor.h" diff --git a/src/ui/lui/ui_main.cpp b/src/ui/lui/ui_main.cpp index d2d85205..03b89572 100644 --- a/src/ui/lui/ui_main.cpp +++ b/src/ui/lui/ui_main.cpp @@ -32,6 +32,14 @@ ui::Control *control = (ui::Control*)param; event::load_rom(); } + if(control == &menu_file_load_st) { + event::load_rom_st(); + } + + if(control == &menu_file_load_stdual) { + event::load_rom_stdual(); + } + if(control == &menu_file_unload) { event::unload_rom(); } @@ -121,10 +129,14 @@ ui::ControlGroup group; menu.create(*this); menu_file.create(menu, "File"); menu_file_load.create(menu_file, "Load Cartridge ..."); - menu_file_unload.create(menu_file, "Unload"); + menu_file_load_special.create(menu_file, "Load Special"); + menu_file_load_st.create(menu_file_load_special, "Load ST Cartridge ..."); + menu_file_load_stdual.create(menu_file_load_special, "Load ST Dual Cartridge ..."); + menu_file_load_special.finish(); + menu_file_unload.create(menu_file, "Unload Cartridge"); menu_file_sep1.create(menu_file); - menu_file_reset.create(menu_file, "Reset"); - menu_file_power.create(menu_file, "Power"); + menu_file_reset.create(menu_file, "Reset System"); + menu_file_power.create(menu_file, "Power Cycle System"); menu_file_sep2.create(menu_file); menu_file_exit.create(menu_file, "Exit"); menu_file.finish(); diff --git a/src/ui/lui/ui_main.h b/src/ui/lui/ui_main.h index 53ea23ee..c643c24e 100644 --- a/src/ui/lui/ui_main.h +++ b/src/ui/lui/ui_main.h @@ -1,6 +1,9 @@ class MainWindow : public ui::Window { public: ui::MenuGroup menu_file; ui::MenuItem menu_file_load; + ui::MenuGroup menu_file_load_special; + ui::MenuItem menu_file_load_st; + ui::MenuItem menu_file_load_stdual; ui::MenuItem menu_file_unload; ui::MenuSeparator menu_file_sep1; ui::MenuItem menu_file_reset;