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.
This commit is contained in:
byuu 2007-10-01 09:03:44 +00:00
parent aabf52d678
commit 9fd379613a
47 changed files with 1906 additions and 1021 deletions

View File

@ -1,5 +1,5 @@
bsnes bsnes
Version 0.023 Version 0.024
Author: byuu Author: byuu
-------- --------
@ -72,8 +72,11 @@ Coprocessor used only by SD Gundam GX
DSP-4 DSP-4
Coprocessor used only by Top Gear 3000 Coprocessor used only by Top Gear 3000
ST010 / ST011 / ST018 ST011
SETA coprocessors used by very few games 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) BS-X (Broadcast Satellite)
Add-on unit sold only in Japan that played specially-made games that were Add-on unit sold only in Japan that played specially-made games that were

View File

@ -13,7 +13,7 @@ endif
ifeq ($(PLATFORM),x-gcc-lui) ifeq ($(PLATFORM),x-gcc-lui)
OS = unix OS = unix
CC = gcc 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 AS = nasm
ASFLAGS = -f elf ASFLAGS = -f elf
LIBS = `pkg-config --libs gtk+-2.0` -lXv -lao LIBS = `pkg-config --libs gtk+-2.0` -lXv -lao
@ -24,7 +24,7 @@ endif
ifeq ($(PLATFORM),x-gcc-lui-x64) ifeq ($(PLATFORM),x-gcc-lui-x64)
OS = unix OS = unix
CC = gcc 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 AS = yasm
ASFLAGS = -f elf64 ASFLAGS = -f elf64
LIBS = `pkg-config --libs gtk+-2.0` -lXv -lao 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) \ reader.$(OBJ) cart.$(OBJ) cheat.$(OBJ) memory.$(OBJ) bmemory.$(OBJ) \
cpu.$(OBJ) scpu.$(OBJ) smp.$(OBJ) ssmp.$(OBJ) bdsp.$(OBJ) ppu.$(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) \ 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) ifeq ($(GZIP_SUPPORT),true)
OBJECTS += adler32.$(OBJ) compress.$(OBJ) crc32.$(OBJ) deflate.$(OBJ) \ 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 ### ### special chips ###
##################### #####################
srtc.$(OBJ): chip/srtc/srtc.cpp chip/srtc/* srtc.$(OBJ) : chip/srtc/srtc.cpp chip/srtc/*
sdd1.$(OBJ): chip/sdd1/sdd1.cpp chip/sdd1/* sdd1.$(OBJ) : chip/sdd1/sdd1.cpp chip/sdd1/*
c4.$(OBJ) : chip/c4/c4.cpp chip/c4/* c4.$(OBJ) : chip/c4/c4.cpp chip/c4/*
dsp1.$(OBJ): chip/dsp1/dsp1.cpp chip/dsp1/* dsp1.$(OBJ) : chip/dsp1/dsp1.cpp chip/dsp1/*
dsp2.$(OBJ): chip/dsp2/dsp2.cpp chip/dsp2/* dsp2.$(OBJ) : chip/dsp2/dsp2.cpp chip/dsp2/*
obc1.$(OBJ): chip/obc1/obc1.cpp chip/obc1/* obc1.$(OBJ) : chip/obc1/obc1.cpp chip/obc1/*
st010.$(OBJ): chip/st010/st010.cpp chip/st010/*
############ ############
### zlib ### ### zlib ###

View File

@ -1,4 +1,4 @@
#define BSNES_VERSION "0.023" #define BSNES_VERSION "0.024"
#define BSNES_TITLE "bsnes v" BSNES_VERSION #define BSNES_TITLE "bsnes v" BSNES_VERSION
#define MEMCORE bMemBus #define MEMCORE bMemBus

View File

@ -15,12 +15,13 @@ void Cartridge::load_begin(uint cart_type) {
info.type = cart_type; info.type = cart_type;
info.srtc = false; info.srtc = false;
info.sdd1 = false; info.sdd1 = false;
info.c4 = false; info.c4 = false;
info.dsp1 = false; info.dsp1 = false;
info.dsp2 = false; info.dsp2 = false;
info.obc1 = false; info.obc1 = false;
info.st010 = false;
info.dsp1_mapper = 0; info.dsp1_mapper = 0;

View File

@ -87,6 +87,7 @@ struct {
bool dsp1; bool dsp1;
bool dsp2; bool dsp2;
bool obc1; bool obc1;
bool st010;
uint dsp1_mapper; uint dsp1_mapper;

View File

@ -69,6 +69,10 @@ uint8 rom_type = rom[info.header_index + ROM_TYPE];
info.obc1 = true; info.obc1 = true;
} }
if(mapper == 0x30 && rom_type == 0xf6) {
info.st010 = true;
}
info.cart_mmio = info.c4 | info.dsp1 | info.dsp2 | info.obc1; info.cart_mmio = info.c4 | info.dsp1 | info.dsp2 | info.obc1;
if(rom[info.header_index + RAM_SIZE] & 7) { if(rom[info.header_index + RAM_SIZE] & 7) {

View File

@ -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 @move bsnes.exe ../bsnes.exe>nul
@pause @pause

View File

@ -43,10 +43,10 @@ uint8 b = (addr >> 16) & 0xff;
uint8 SDD1::mmio_read(uint16 addr) { uint8 SDD1::mmio_read(uint16 addr) {
switch(addr) { switch(addr) {
//>>20 == 0x100000 == 1mb //>>20 == 0x100000 == 1mb
case 0x4804:return (sdd1.index[0] >> 20) & 7; case 0x4804: return (sdd1.index[0] >> 20) & 7;
case 0x4805:return (sdd1.index[1] >> 20) & 7; case 0x4805: return (sdd1.index[1] >> 20) & 7;
case 0x4806:return (sdd1.index[2] >> 20) & 7; case 0x4806: return (sdd1.index[2] >> 20) & 7;
case 0x4807:return (sdd1.index[3] >> 20) & 7; case 0x4807: return (sdd1.index[3] >> 20) & 7;
} }
return r_cpu->regs.mdr; return r_cpu->regs.mdr;
@ -60,10 +60,10 @@ void SDD1::mmio_write(uint16 addr, uint8 data) {
} }
break; break;
//<<20 == 0x100000 == 1mb //<<20 == 0x100000 == 1mb
case 0x4804:sdd1.index[0] = (data & 7) << 20;break; case 0x4804: sdd1.index[0] = (data & 7) << 20; break;
case 0x4805:sdd1.index[1] = (data & 7) << 20;break; case 0x4805: sdd1.index[1] = (data & 7) << 20; break;
case 0x4806:sdd1.index[2] = (data & 7) << 20;break; case 0x4806: sdd1.index[2] = (data & 7) << 20; break;
case 0x4807:sdd1.index[3] = (data & 7) << 20;break; case 0x4807: sdd1.index[3] = (data & 7) << 20; break;
} }
} }

86
src/chip/st010/st010.cpp Normal file
View File

@ -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;
}
}

40
src/chip/st010/st010.h Normal file
View File

@ -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;

126
src/chip/st010/st010_data.h Normal file
View File

@ -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 }
};

257
src/chip/st010/st010_op.cpp Normal file
View File

@ -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);
}

View File

@ -1,7 +1,10 @@
Config config_file;
namespace config { namespace config {
Config& config() {
static Config config;
return config;
}
string file_updatepath(const char *req_file, const char *req_path) { string file_updatepath(const char *req_file, const char *req_path) {
string file(req_file); string file(req_file);
replace(file, "\\", "/"); replace(file, "\\", "/");
@ -27,69 +30,69 @@ stringarray part;
StringSetting Path::base(0, "fs.base_path", StringSetting Path::base(0, "fs.base_path",
"Path that bsnes resides in", ""); "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)", ""); "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)", ""); "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" "Path where BIOS file(s) are located\n"
"Supported BIOS files:\n" "Supported BIOS files:\n"
"stbios.bin - Bandai Sufami Turbo" "stbios.bin - Bandai Sufami Turbo"
"", "./bios"); "", "./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"); "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); "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); "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); "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); "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); "Contrast", IntegerSetting::Decimal, 0);
IntegerSetting SNES::brightness(&config_file, "snes.colorfilter.brightness", IntegerSetting SNES::brightness(&config(), "snes.colorfilter.brightness",
"Brightness", IntegerSetting::Decimal, 0); "Brightness", IntegerSetting::Decimal, 0);
IntegerSetting SNES::gamma(&config_file, "snes.colorfilter.gamma", IntegerSetting SNES::gamma(&config(), "snes.colorfilter.gamma",
"Gamma", IntegerSetting::Decimal, 80); "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" "Merge fields in NTSC video filter\n"
"Set to true if using filter at any refresh rate other than 60hz\n" "Set to true if using filter at any refresh rate other than 60hz\n"
"", IntegerSetting::Boolean, true); "", 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::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); "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); "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); "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); "PAL S-CPU clock rate (in hz)", IntegerSetting::Decimal, 21281370);
IntegerSetting CPU::hdma_enable(0, "cpu.hdma_enable", IntegerSetting CPU::hdma_enable(0, "cpu.hdma_enable",
"Enable HDMA effects", IntegerSetting::Boolean, true); "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); "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); "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", "Approximate HCLOCK position to render at for scanline-based renderers",
IntegerSetting::Decimal, 512); 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" "Cache OAM OBJ attributes one scanline before rendering\n"
"This is technically closer to the actual operation of the SNES,\n" "This is technically closer to the actual operation of the SNES,\n"
"but can cause problems in some games if enabled", "but can cause problems in some games if enabled",
IntegerSetting::Boolean, false); 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" "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" "data to render the display. Thusly, the address retrieved when accessing\n"
"OAM during active display is unpredictable. Unfortunately, the exact\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" "accurate to enable this setting, but one must *not* rely on the actual\n"
"address to match hardware under emulation.", "address to match hardware under emulation.",
IntegerSetting::Boolean, true); 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" "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" "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" "address is unpredictable. Again, enabling this setting is more hardware\n"

View File

@ -1,7 +1,7 @@
extern Config config_file;
namespace config { namespace config {
extern Config& config();
string file_updatepath(const char *, const char *); string file_updatepath(const char *, const char *);
extern struct Path { extern struct Path {

View File

@ -39,6 +39,7 @@
#include "chip/dsp1/dsp1.h" #include "chip/dsp1/dsp1.h"
#include "chip/dsp2/dsp2.h" #include "chip/dsp2/dsp2.h"
#include "chip/obc1/obc1.h" #include "chip/obc1/obc1.h"
#include "chip/st010/st010.h"
extern MMIO mmio_unmapped; extern MMIO mmio_unmapped;
#ifdef POLYMORPHISM #ifdef POLYMORPHISM

View File

@ -147,16 +147,19 @@ class Frame : public Control { public:
}; };
class Label : 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 create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
void set_text(const char *str); void set_text(const char *str);
}; };
class Button : public Control { public: 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 create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
void set_text(const char *str); void set_text(const char *str);
}; };
class Checkbox : public Control { public: 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 create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
void check(); void check();
void uncheck(); void uncheck();
@ -165,12 +168,14 @@ class Checkbox : public Control { public:
}; };
class Radiobox : 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 create(Window &owner, ControlGroup &group, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
void check(); void check();
bool checked(); bool checked();
}; };
class Editbox : public Control { public: class Editbox : public Control { public:
enum { ideal_height = 30 };
enum { enum {
Multiline = (1 << 1), Multiline = (1 << 1),
Readonly = (1 << 2), Readonly = (1 << 2),
@ -226,6 +231,7 @@ GtkTreeIter iter;
}; };
class Combobox : public Control { public: class Combobox : public Control { public:
enum { ideal_height = 30 };
void create(Window &owner, uint style, uint x, uint y, uint width, uint height); void create(Window &owner, uint style, uint x, uint y, uint width, uint height);
void add_item(const char *data); void add_item(const char *data);
int get_selection(); int get_selection();
@ -237,12 +243,14 @@ uint counter;
}; };
class Progressbar : 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 create(Window &owner, uint style, uint x, uint y, uint width, uint height);
void set_progress(uint progress); void set_progress(uint progress);
uint get_progress(); uint get_progress();
}; };
class Slider : public Control { public: class Slider : public Control { public:
enum { ideal_height = 25 };
enum { enum {
Horizontal = 0, Horizontal = 0,
Vertical = 1, Vertical = 1,

View File

@ -157,15 +157,18 @@ class Frame : public Control { public:
}; };
class Label : 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 create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
void set_text(const char *str, ...); void set_text(const char *str, ...);
}; };
class Button : public Control { public: 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 = ""); void create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
}; };
class Checkbox : public Control { public: 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 create(Window &owner, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
void check(); void check();
void uncheck(); void uncheck();
@ -174,6 +177,7 @@ class Checkbox : public Control { public:
}; };
class Radiobox : 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 create(Window &owner, ControlGroup &list, uint style, uint x, uint y, uint width, uint height, const char *caption = "");
void check(); void check();
bool checked(); bool checked();
@ -183,6 +187,7 @@ ControlGroup group;
}; };
class Editbox : public Control { public: class Editbox : public Control { public:
enum { ideal_height = 21 };
enum { enum {
Multiline = (1 << 1), Multiline = (1 << 1),
Readonly = (1 << 2), Readonly = (1 << 2),
@ -235,6 +240,7 @@ uint column_count;
}; };
class Combobox : public Control { public: class Combobox : public Control { public:
enum { ideal_height = 21 };
void create(Window &owner, uint style, uint x, uint y, uint width, uint height); void create(Window &owner, uint style, uint x, uint y, uint width, uint height);
void add_item(const char *data); void add_item(const char *data);
void set_selection(int index); void set_selection(int index);
@ -243,12 +249,14 @@ class Combobox : public Control { public:
}; };
class Progressbar : 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 create(Window &owner, uint style, uint x, uint y, uint width, uint height);
void set_progress(uint progress); void set_progress(uint progress);
uint get_progress(); uint get_progress();
}; };
class Slider : public Control { public: class Slider : public Control { public:
enum { ideal_height = 25 };
enum Style { enum Style {
Horizontal = 0, Horizontal = 0,
Vertical = 1, Vertical = 1,

View File

@ -27,11 +27,12 @@ void bMemBus::load_cart() {
return; return;
} }
if(cartridge.info.sdd1)cart_map_sdd1(); if(cartridge.info.sdd1) cart_map_sdd1();
if(cartridge.info.c4) cart_map_c4(); if(cartridge.info.c4) cart_map_c4();
if(cartridge.info.dsp1)cart_map_dsp1(); if(cartridge.info.dsp1) cart_map_dsp1();
if(cartridge.info.dsp2)cart_map_dsp2(); if(cartridge.info.dsp2) cart_map_dsp2();
if(cartridge.info.obc1)cart_map_obc1(); if(cartridge.info.obc1) cart_map_obc1();
if(cartridge.info.st010)cart_map_st010();
cart_map_system(); cart_map_system();
@ -57,12 +58,13 @@ char t[256];
dprintf("* Region : %s", (cartridge.info.region == Cartridge::NTSC) ? "NTSC" : "PAL"); dprintf("* Region : %s", (cartridge.info.region == Cartridge::NTSC) ? "NTSC" : "PAL");
strcpy(t, ""); strcpy(t, "");
if(cartridge.info.srtc)strcat(t, "S-RTC, "); if(cartridge.info.srtc) strcat(t, "S-RTC, ");
if(cartridge.info.sdd1)strcat(t, "S-DD1, "); if(cartridge.info.sdd1) strcat(t, "S-DD1, ");
if(cartridge.info.c4) strcat(t, "Cx4, "); if(cartridge.info.c4) strcat(t, "Cx4, ");
if(cartridge.info.dsp1)strcat(t, "DSP-1, "); if(cartridge.info.dsp1) strcat(t, "DSP-1, ");
if(cartridge.info.dsp2)strcat(t, "DSP-2, "); if(cartridge.info.dsp2) strcat(t, "DSP-2, ");
if(cartridge.info.obc1)strcat(t, "OBC-1, "); if(cartridge.info.obc1) strcat(t, "OBC-1, ");
if(cartridge.info.st010)strcat(t, "ST010, ");
strrtrim(t, ", "); strrtrim(t, ", ");
dprintf("* Coprocessor(s) : %s", (strlen(t) == 0) ? "None" : 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", dprintf("* Reset:%0.4x NMI[n]:%0.4x IRQ[n]:%0.4x BRK[n]:%0.4x COP[n]:%0.4x",

View File

@ -41,6 +41,8 @@ enum { TYPE_WRAM, TYPE_MMIO, TYPE_CART };
void write_dsp2 (uint32 addr, uint8 data); void write_dsp2 (uint32 addr, uint8 data);
uint8 read_obc1 (uint32 addr); uint8 read_obc1 (uint32 addr);
void write_obc1 (uint32 addr, uint8 data); 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_reset();
void cart_map_system(); void cart_map_system();
@ -51,6 +53,7 @@ enum { TYPE_WRAM, TYPE_MMIO, TYPE_CART };
void cart_map_dsp1(); void cart_map_dsp1();
void cart_map_dsp2(); void cart_map_dsp2();
void cart_map_obc1(); void cart_map_obc1();
void cart_map_st010();
void power(); void power();
void reset(); void reset();

View File

@ -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;
}
}
}

View File

@ -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); } uint8 bMemBus::read_obc1 (uint32 addr) { return obc1->read(addr); }
void bMemBus::write_obc1(uint32 addr, uint8 data) { obc1->write(addr, data); } 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); }

View File

@ -8,7 +8,7 @@ uint16 bPPU::get_vram_address() {
uint16 addr; uint16 addr;
addr = regs.vram_addr; addr = regs.vram_addr;
switch(regs.vram_mapping) { switch(regs.vram_mapping) {
case 0: break; case 0: break; //direct
case 1: addr = (addr & 0xff00) | ((addr & 0x001f) << 3) | ((addr >> 5) & 7); break; 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 2: addr = (addr & 0xfe00) | ((addr & 0x003f) << 3) | ((addr >> 6) & 7); break;
case 3: addr = (addr & 0xfc00) | ((addr & 0x007f) << 3) | ((addr >> 7) & 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 //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 //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 //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. //about this address, it is simply more accurate to invalidate the 'expected' address than not.
uint8 bPPU::cgram_mmio_read(uint16 addr) { uint8 bPPU::cgram_mmio_read(uint16 addr) {
@ -134,8 +134,8 @@ uint8 bPPU::cgram_mmio_read(uint16 addr) {
uint16 v = r_cpu->vcounter(); uint16 v = r_cpu->vcounter();
uint16 hc = r_cpu->hclock(); uint16 hc = r_cpu->hclock();
if(v < (!r_cpu->overscan() ? 225 : 240) && hc > 0 && hc < 1096) { if(v < (!r_cpu->overscan() ? 225 : 240) && hc >= 72 && hc < 1096) {
return cgram_read(0x0000); return cgram_read(0x01ff);
} }
return cgram_read(addr); return cgram_read(addr);
@ -148,8 +148,8 @@ void bPPU::cgram_mmio_write(uint16 addr, uint8 data) {
uint16 v = r_cpu->vcounter(); uint16 v = r_cpu->vcounter();
uint16 hc = r_cpu->hclock(); uint16 hc = r_cpu->hclock();
if(v < (!r_cpu->overscan() ? 225 : 240) && hc > 0 && hc < 1096) { if(v < (!r_cpu->overscan() ? 225 : 240) && hc >= 72 && hc < 1096) {
return cgram_write(0x0000, data); return cgram_write(0x01ff, data);
} }
cgram_write(addr, data); cgram_write(addr, data);

View File

@ -16,12 +16,13 @@ void SNES::runtoframe() {
} }
void SNES::init() { void SNES::init() {
srtc = new SRTC(); srtc = new SRTC();
sdd1 = new SDD1(); sdd1 = new SDD1();
c4 = new C4(); c4 = new C4();
dsp1 = new DSP1(); dsp1 = new DSP1();
dsp2 = new DSP2(); dsp2 = new DSP2();
obc1 = new OBC1(); obc1 = new OBC1();
st010 = new ST010();
srtc->init(); srtc->init();
sdd1->init(); sdd1->init();
@ -29,6 +30,7 @@ void SNES::init() {
dsp1->init(); dsp1->init();
dsp2->init(); dsp2->init();
obc1->init(); obc1->init();
st010->init();
video_init(); video_init();
audio_init(); audio_init();
@ -50,12 +52,13 @@ void SNES::power() {
r_ppu->power(); r_ppu->power();
r_mem->power(); r_mem->power();
if(cartridge.info.srtc)srtc->power(); if(cartridge.info.srtc) srtc->power();
if(cartridge.info.sdd1)sdd1->power(); if(cartridge.info.sdd1) sdd1->power();
if(cartridge.info.c4) c4->power(); if(cartridge.info.c4) c4->power();
if(cartridge.info.dsp1)dsp1->power(); if(cartridge.info.dsp1) dsp1->power();
if(cartridge.info.dsp2)dsp2->power(); if(cartridge.info.dsp2) dsp2->power();
if(cartridge.info.obc1)obc1->power(); if(cartridge.info.obc1) obc1->power();
if(cartridge.info.st010)st010->power();
r_mem->flush_mmio_mappers(); r_mem->flush_mmio_mappers();
for(int i = 0x2100; i <= 0x213f; i++)r_mem->set_mmio_mapper(i, r_ppu); 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 = 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); for(int i = 0x4300; i <= 0x437f; i++)r_mem->set_mmio_mapper(i, r_cpu);
if(cartridge.info.srtc)srtc->enable(); if(cartridge.info.srtc) srtc->enable();
if(cartridge.info.sdd1)sdd1->enable(); if(cartridge.info.sdd1) sdd1->enable();
if(cartridge.info.c4) c4->enable(); if(cartridge.info.c4) c4->enable();
if(cartridge.info.dsp1)dsp1->enable(); if(cartridge.info.dsp1) dsp1->enable();
if(cartridge.info.dsp2)dsp2->enable(); if(cartridge.info.dsp2) dsp2->enable();
if(cartridge.info.obc1)obc1->enable(); if(cartridge.info.obc1) obc1->enable();
if(cartridge.info.st010)st010->enable();
video_update(); video_update();
} }
@ -84,12 +88,13 @@ void SNES::reset() {
r_ppu->reset(); r_ppu->reset();
r_mem->reset(); r_mem->reset();
if(cartridge.info.srtc)srtc->reset(); if(cartridge.info.srtc) srtc->reset();
if(cartridge.info.sdd1)sdd1->reset(); if(cartridge.info.sdd1) sdd1->reset();
if(cartridge.info.c4) c4->reset(); if(cartridge.info.c4) c4->reset();
if(cartridge.info.dsp1)dsp1->reset(); if(cartridge.info.dsp1) dsp1->reset();
if(cartridge.info.dsp2)dsp2->reset(); if(cartridge.info.dsp2) dsp2->reset();
if(cartridge.info.obc1)obc1->reset(); if(cartridge.info.obc1) obc1->reset();
if(cartridge.info.st010)st010->reset();
video_update(); video_update();
} }

View File

@ -1,5 +1,4 @@
#define SNES_NTSC_IN_FORMAT SNES_NTSC_BGR15 #include "ntsc/snes_ntsc.c"
#include "filter_ntsc_core.cpp"
NtscVideoFilter::NtscVideoFilter() { NtscVideoFilter::NtscVideoFilter() {
ntsc = 0; ntsc = 0;

View File

@ -1,4 +1,4 @@
#include "filter_ntsc_core.h" #include "ntsc/snes_ntsc.h"
class NtscVideoFilter : public VideoFilter { class NtscVideoFilter : public VideoFilter {
private: private:

View File

@ -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 <assert.h>
#include <math.h>
/* 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 <limits.h>
#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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,439 @@
/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
/* Common implementation of NTSC filters */
#include <assert.h>
#include <math.h>
/* 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 <limits.h>
#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

View File

@ -9,21 +9,21 @@ struct System {
static IntegerSetting regulate_speed, speed; static IntegerSetting regulate_speed, speed;
static IntegerSetting speed_slowest, speed_slow, speed_normal, speed_fast, speed_fastest; static IntegerSetting speed_slowest, speed_slow, speed_normal, speed_fast, speed_fastest;
} system; } system;
StringSetting System::video(&config_file, "system.video", "Video hardware interface", ""); StringSetting System::video(&config(), "system.video", "Video hardware interface", "");
StringSetting System::audio(&config_file, "system.audio", "Audio hardware interface", ""); StringSetting System::audio(&config(), "system.audio", "Audio hardware interface", "");
StringSetting System::input(&config_file, "system.input", "Input 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::video_flags(&config(), "system.video_flags", "Video hardware interface flags", "");
StringSetting System::audio_flags(&config_file, "system.audio_flags", "Audio hardware interface flags", ""); StringSetting System::audio_flags(&config(), "system.audio_flags", "Audio hardware interface flags", "");
StringSetting System::input_flags(&config_file, "system.input_flags", "Input 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 (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_slowest (&config(), "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_slow (&config(), "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_normal (&config(), "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_fast (&config(), "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_fastest (&config(), "system.speed_fastest", "Fastest speed setting", IntegerSetting::Decimal, 200);
struct Video { struct Video {
static IntegerSetting synchronize; static IntegerSetting synchronize;
@ -34,47 +34,47 @@ struct Video {
static IntegerSetting frameskip; static IntegerSetting frameskip;
static IntegerSetting use_vram; static IntegerSetting use_vram;
} video; } 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::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" "1 = 1x (~256x224)\n"
"2 = 2x (~512x448)\n" "2 = 2x (~512x448)\n"
"etc.", "etc.",
IntegerSetting::Decimal, 2); 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" "Correct video aspect ratio\n"
"Formula: width = width * video.aspect_<region>_x / video.aspect_<region>_y", "Formula: width = width * video.aspect_<region>_x / video.aspect_<region>_y",
IntegerSetting::Boolean, true); 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", "0 = NTSC, 1 = PAL",
IntegerSetting::Decimal, 0); IntegerSetting::Decimal, 0);
IntegerSetting Video::aspect_ntsc_x(&config_file, "video.aspect_ntsc_x", "", IntegerSetting::Decimal, 54); IntegerSetting Video::aspect_ntsc_x(&config(), "video.aspect_ntsc_x", "", IntegerSetting::Decimal, 54);
IntegerSetting Video::aspect_ntsc_y(&config_file, "video.aspect_ntsc_y", "", IntegerSetting::Decimal, 47); IntegerSetting Video::aspect_ntsc_y(&config(), "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_x (&config(), "video.aspect_pal_x", "", IntegerSetting::Decimal, 32);
IntegerSetting Video::aspect_pal_y (&config_file, "video.aspect_pal_y", "", IntegerSetting::Decimal, 23); 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" "0 = Point\n"
"1 = Linear\n", "1 = Linear\n",
IntegerSetting::Decimal, 1); 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" "0 = None\n"
"1 = NTSC\n" "1 = NTSC\n"
"2 = HQ2x\n" "2 = HQ2x\n"
"3 = Scale2x\n", "3 = Scale2x\n",
IntegerSetting::Decimal, 0); IntegerSetting::Decimal, 0);
IntegerSetting Video::frameskip(0, "video.frameskip", "Video frameskip", 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 { struct Audio {
static IntegerSetting synchronize; static IntegerSetting synchronize;
static IntegerSetting frequency; static IntegerSetting frequency;
static IntegerSetting latency; static IntegerSetting latency;
} audio; } audio;
IntegerSetting Audio::synchronize(&config_file, "audio.synchronize", "Synchronize to audio sample rate.", IntegerSetting::Boolean, true); IntegerSetting Audio::synchronize(&config(), "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::frequency(&config(), "audio.frequency", "Default audio playback frequency.", IntegerSetting::Decimal, 32000);
IntegerSetting Audio::latency(&config_file, "audio.latency", "Audio playback latency in milliseconds.\n" 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" "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" "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" "Raising this value may help with audio playback problems, but will decrease\n"
@ -94,7 +94,7 @@ struct Input {
} joypad2; } joypad2;
} input; } 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" "Axis resistance for all analog joypads\n"
"Affects responsiveness of analog stick movement by specifying what percentage\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" "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.", "Note: Values below 10 or above 90 are not recommended and may not work at all.",
IntegerSetting::Decimal, 75); 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)", "Allow up+down and left+right combinations (not recommended)",
IntegerSetting::Boolean, false); IntegerSetting::Boolean, false);
StringSetting Input::Joypad1::up (&config_file, "input.joypad1.up", "", "up"); StringSetting Input::Joypad1::up (&config(), "input.joypad1.up", "", "up");
StringSetting Input::Joypad1::down (&config_file, "input.joypad1.down", "", "down"); StringSetting Input::Joypad1::down (&config(), "input.joypad1.down", "", "down");
StringSetting Input::Joypad1::left (&config_file, "input.joypad1.left", "", "left"); StringSetting Input::Joypad1::left (&config(), "input.joypad1.left", "", "left");
StringSetting Input::Joypad1::right (&config_file, "input.joypad1.right", "", "right"); StringSetting Input::Joypad1::right (&config(), "input.joypad1.right", "", "right");
StringSetting Input::Joypad1::a (&config_file, "input.joypad1.a", "", "x"); StringSetting Input::Joypad1::a (&config(), "input.joypad1.a", "", "x");
StringSetting Input::Joypad1::b (&config_file, "input.joypad1.b", "", "z"); StringSetting Input::Joypad1::b (&config(), "input.joypad1.b", "", "z");
StringSetting Input::Joypad1::x (&config_file, "input.joypad1.x", "", "s"); StringSetting Input::Joypad1::x (&config(), "input.joypad1.x", "", "s");
StringSetting Input::Joypad1::y (&config_file, "input.joypad1.y", "", "a"); StringSetting Input::Joypad1::y (&config(), "input.joypad1.y", "", "a");
StringSetting Input::Joypad1::l (&config_file, "input.joypad1.l", "", "d"); StringSetting Input::Joypad1::l (&config(), "input.joypad1.l", "", "d");
StringSetting Input::Joypad1::r (&config_file, "input.joypad1.r", "", "c"); StringSetting Input::Joypad1::r (&config(), "input.joypad1.r", "", "c");
StringSetting Input::Joypad1::select(&config_file, "input.joypad1.select", "", "rshift"); StringSetting Input::Joypad1::select(&config(), "input.joypad1.select", "", "rshift");
StringSetting Input::Joypad1::start (&config_file, "input.joypad1.start", "", "enter"); StringSetting Input::Joypad1::start (&config(), "input.joypad1.start", "", "enter");
StringSetting Input::Joypad2::up (&config_file, "input.joypad2.up", "", "t"); StringSetting Input::Joypad2::up (&config(), "input.joypad2.up", "", "t");
StringSetting Input::Joypad2::down (&config_file, "input.joypad2.down", "", "g"); StringSetting Input::Joypad2::down (&config(), "input.joypad2.down", "", "g");
StringSetting Input::Joypad2::left (&config_file, "input.joypad2.left", "", "f"); StringSetting Input::Joypad2::left (&config(), "input.joypad2.left", "", "f");
StringSetting Input::Joypad2::right (&config_file, "input.joypad2.right", "", "h"); StringSetting Input::Joypad2::right (&config(), "input.joypad2.right", "", "h");
StringSetting Input::Joypad2::a (&config_file, "input.joypad2.a", "", "k"); StringSetting Input::Joypad2::a (&config(), "input.joypad2.a", "", "k");
StringSetting Input::Joypad2::b (&config_file, "input.joypad2.b", "", "j"); StringSetting Input::Joypad2::b (&config(), "input.joypad2.b", "", "j");
StringSetting Input::Joypad2::x (&config_file, "input.joypad2.x", "", "i"); StringSetting Input::Joypad2::x (&config(), "input.joypad2.x", "", "i");
StringSetting Input::Joypad2::y (&config_file, "input.joypad2.y", "", "u"); StringSetting Input::Joypad2::y (&config(), "input.joypad2.y", "", "u");
StringSetting Input::Joypad2::l (&config_file, "input.joypad2.l", "", "o"); StringSetting Input::Joypad2::l (&config(), "input.joypad2.l", "", "o");
StringSetting Input::Joypad2::r (&config_file, "input.joypad2.r", "", "l"); StringSetting Input::Joypad2::r (&config(), "input.joypad2.r", "", "l");
StringSetting Input::Joypad2::select(&config_file, "input.joypad2.select", "", "lbracket"); StringSetting Input::Joypad2::select(&config(), "input.joypad2.select", "", "lbracket");
StringSetting Input::Joypad2::start (&config_file, "input.joypad2.start", "", "rbracket"); StringSetting Input::Joypad2::start (&config(), "input.joypad2.start", "", "rbracket");
struct Misc { struct Misc {
static IntegerSetting show_frame_counter; static IntegerSetting show_frame_counter;
} misc; } 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);
}; };

View File

@ -106,6 +106,32 @@ char fn[PATH_MAX];
window_cheat_editor.refresh(); 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() { void unload_rom() {
if(cartridge.loaded() == true) { if(cartridge.loaded() == true) {
cartridge.unload(); cartridge.unload();

View File

@ -8,6 +8,8 @@ void toggle_fullscreen();
bool load_rom(char *fn); bool load_rom(char *fn);
void load_rom(); void load_rom();
void load_rom_st();
void load_rom_stdual();
void unload_rom(); void unload_rom();
void reset(); void reset();
void power(); void power();

View File

@ -52,21 +52,46 @@ void set_config_filename() {
strcat(config::filename, "/bsnes.cfg"); 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() { void run() {
while(ui::events_pending() == true) { ui::run(); } while(ui::events_pending() == true) { ui::run(); }
if(cartridge.loaded() == true) { if(cartridge.loaded() == true) {
snes.runtoframe(); snes.runtoframe();
event::update_frame_counter(); 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) #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; int argc = __argc;
char **argv = __argv; char **argv = __argv;
#else #else
@ -75,10 +100,11 @@ int main(int argc, char *argv[]) {
//int main(int argc, char *argv[]) { //int main(int argc, char *argv[]) {
set_config_filename(); set_config_filename();
get_base_path();
ui::init(); ui::init();
config_file.load(config::filename); config::config().load(config::filename);
config_file.save(config::filename); //in case program crashes on first run, config file settings can be modified config::config().save(config::filename); //in case program crashes on first run, config file settings can be modified
init_snes(); init_snes();
ui_init(); ui_init();
@ -95,7 +121,7 @@ int main(int argc, char *argv[]) {
event::unload_rom(); event::unload_rom();
config_file.save(config::filename); config::config().save(config::filename);
term_snes(); term_snes();
ui_term(); ui_term();
ui::term(); ui::term();

View File

@ -4,10 +4,10 @@ ui::Control *control = (ui::Control*)param;
int pos = list.get_selection(); int pos = list.get_selection();
set_val.enable(pos >= 0); set_val.enable(pos >= 0);
set_def.enable(pos >= 0); set_def.enable(pos >= 0);
if(pos >= 0 && pos < config_file.list_count) { if(pos >= 0 && pos < config::config().list_count) {
desc.set_text(string() << "(default = " << config_file.list[pos]->def << ")\n" << config_file.list[pos]->desc); desc.set_text(string() << "(default = " << config::config().list[pos]->def << ")\n" << config::config().list[pos]->desc);
string val; string val;
config_file.list[pos]->get(val); config::config().list[pos]->get(val);
edit_val.set_text(strptr(val)); edit_val.set_text(strptr(val));
} }
} else if(id == ui::Message::Clicked && control == &set_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) { void AdvancedWindow::read_config(uint pos, string &data) {
strcpy(data, "?|?|?"); strcpy(data, "?|?|?");
if(pos >= config_file.list_count)return; if(pos >= config::config().list_count)return;
string name, val; string name, val;
name = config_file.list[pos]->name; name = config::config().list[pos]->name;
config_file.list[pos]->get(val); config::config().list[pos]->get(val);
if(val != config_file.list[pos]->def) { strcat(name, " (*)"); } if(val != config::config().list[pos]->def) { strcat(name, " (*)"); }
sprintf(data, "%s|%s|%s", sprintf(data, "%s|%s|%s",
strptr(name), strptr(name),
config_file.list[pos]->type == Setting::String ? "String" : "Integer", config::config().list[pos]->type == Setting::String ? "String" : "Integer",
strptr(val) strptr(val)
); );
} }
void AdvancedWindow::update(uint pos, const char *data) { void AdvancedWindow::update(uint pos, const char *data) {
if(pos >= config_file.list_count)return; if(pos >= config::config().list_count)return;
config_file.list[pos]->set(data ? data : config_file.list[pos]->def); config::config().list[pos]->set(data ? data : config::config().list[pos]->def);
string val; string val;
config_file.list[pos]->get(val); config::config().list[pos]->get(val);
edit_val.set_text(strptr(val)); edit_val.set_text(strptr(val));
read_config(pos, val); read_config(pos, val);
list.set_item(pos, strptr(val)); list.set_item(pos, strptr(val));
@ -50,20 +50,27 @@ void AdvancedWindow::setup() {
create(0, 475, 355); create(0, 475, 355);
int x = 0, y = 0; int x = 0, y = 0;
list.create(*this, ui::Listbox::Header | ui::Listbox::VerticalScrollAlways, x, y, 475, 235, "Name|Type|Value"); int bh = ui::Button::ideal_height;
y += 240; int eh = ui::Editbox::ideal_height;
desc.create(*this, ui::Editbox::Multiline | ui::Editbox::Readonly, x, y, 475, 80, 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,
"<description>\n" "<description>\n"
"Warning: modifification of certain variables will not take effect until\n" "Warning: modifification of certain variables will not take effect until\n"
"bsnes is restarted, and corresponding UI elements will not be updated\n" "bsnes is restarted, and corresponding UI elements will not be updated\n"
"to reflect changes here. (*) = modified" "to reflect changes here. (*) = modified"
); );
y += 85; y += th + 5;
edit_val.create(*this, 0, x, y, 265, 30, "<current value>");
set_val.create(*this, 0, x + 270, y, 100, 30, "Set");
set_def.create(*this, 0, x + 375, y, 100, 30, "Default");
for(int i = 0; i < config_file.list_count; i++) { edit_val.create(*this, 0, x, y, 265, bh, "<current value>");
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; string val;
read_config(i, val); read_config(i, val);
list.add_item(strptr(val)); list.add_item(strptr(val));

View File

@ -1,11 +1,11 @@
class AdvancedWindow : public ui::Window { public: class AdvancedWindow : public ui::Window { public:
ui::Listbox list; ui::Listbox list;
ui::Editbox desc; ui::Editbox desc;
ui::Editbox edit_val; ui::Editbox edit_val;
ui::Button set_val; ui::Button set_val;
ui::Button set_def; ui::Button set_def;
bool message(uint id, uintptr_t param); bool message(uint id, uintptr_t param);
void read_config(uint pos, string &data); void read_config(uint pos, string &data);
void update(uint pos, const char *data); void update(uint pos, const char *data);
void setup(); void setup();
} window_advanced; } window_advanced;

View File

@ -64,16 +64,19 @@ void CheatEditorWindow::setup() {
create(0, 475, 355); create(0, 475, 355);
int x = 0, y = 0; int x = 0, y = 0;
list.create(*this, ui::Listbox::Header | ui::Listbox::VerticalScrollAlways, x, y, 475, 285, "Status|Code|Description"); int bh = ui::Button::ideal_height;
y += 290; 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"); add_code.create (*this, 0, x, y, 155, bh, "Add Code");
toggle_code.create(*this, 0, x + 160, y, 155, 30, "Toggle Status"); toggle_code.create(*this, 0, x + 160, y, 155, bh, "Toggle Status");
delete_code.create(*this, 0, x + 320, y, 155, 30, "Delete Code"); delete_code.create(*this, 0, x + 320, y, 155, bh, "Delete Code");
y += 35; y += bh + 5;
code.create(*this, 0, x, y, 155, 30, "<code>"); code.create(*this, 0, x, y, 155, eh, "<code>");
desc.create(*this, 0, x + 160, y, 315, 30, "<description>"); desc.create(*this, 0, x + 160, y, 315, eh, "<description>");
refresh(); refresh();
} }

View File

@ -131,29 +131,35 @@ void InputConfigWindow::setup() {
create(0, 475, 355); create(0, 475, 355);
int x = 0, y = 0; int x = 0, y = 0;
lportA.create(*this, 0, x, y, 105, 30, "Controller Port A:"); int lh = ui::Label::ideal_height;
portA.create (*this, 0, x + 110, y, 125, 30); 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("None");
portA.add_item("Joypad 1"); portA.add_item("Joypad 1");
portA.add_item("Joypad 2"); portA.add_item("Joypad 2");
portA.set_selection(1); portA.set_selection(1);
portA.disable(); portA.disable();
lportB.create(*this, 0, x + 240, y, 105, 30, "Controller Port B:"); portB.create (*this, 0, x + 240, y, 235, ch);
portB.create (*this, 0, x + 350, y, 125, 30);
portB.add_item("None"); portB.add_item("None");
portB.add_item("Joypad 1"); portB.add_item("Joypad 1");
portB.add_item("Joypad 2"); portB.add_item("Joypad 2");
portB.set_selection(2); portB.set_selection(2);
portB.disable(); 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("???|???"); } 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"); setkey.create(*this, 0, x, y, 235, bh, "Assign Key");
clrkey.create(*this, 0, x + 240, y, 235, 30, "Unassign Key"); clrkey.create(*this, 0, x + 240, y, 235, bh, "Unassign Key");
refresh_list(); refresh_list();
window_input_capture.setup(); window_input_capture.setup();
@ -199,6 +205,7 @@ void InputCaptureWindow::show() {
} }
void InputCaptureWindow::setup() { void InputCaptureWindow::setup() {
create(ui::Window::Center, 350, 100, "bsnes Key Capture"); int lh = ui::Label::ideal_height;
label.create(*this, 0, 5, 5, 340, 90); create(ui::Window::Center, 350, lh * 3, "bsnes Key Capture");
label.create(*this, 0, 5, lh, 340, lh);
} }

View File

@ -88,29 +88,32 @@ void RasterSettingsWindow::setup() {
create(0, 475, 355); create(0, 475, 355);
int x = 0, y = 0; int x = 0, y = 0;
lcontrast.create(*this, 0, x, y, 100, 25); int sh = ui::Slider::ideal_height;
contrast.create (*this, 0, x + 100, y, 375, 25, 192); int kh = ui::Checkbox::ideal_height;
y += 25; 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); lbrightness.create(*this, 0, x, y, 100, sh);
brightness.create (*this, 0, x + 100, y, 375, 25, 192); brightness.create (*this, 0, x + 100, y, 375, sh, 192);
y += 25; y += sh;
lgamma.create(*this, 0, x, y, 100, 25); lgamma.create(*this, 0, x, y, 100, sh);
gamma.create (*this, 0, x + 100, y, 375, 25, 191); gamma.create (*this, 0, x + 100, y, 375, sh, 191);
y += 25; y += sh;
gamma_ramp.create(*this, 0, x, y, 235, 20, "Gamma ramp"); gamma_ramp.create(*this, 0, x, y, 235, kh, "Gamma ramp");
sepia.create(*this, 0, x + 240, y, 235, 20, "Sepia"); sepia.create(*this, 0, x + 240, y, 235, kh, "Sepia");
y += 20; y += kh;
grayscale.create(*this, 0, x, y, 235, 20, "Grayscale"); grayscale.create(*this, 0, x, y, 235, kh, "Grayscale");
invert.create(*this, 0, x + 240, y, 235, 20, "Invert colors"); invert.create(*this, 0, x + 240, y, 235, kh, "Invert colors");
y += 20; y += kh + 5;
y += 5; preset_optimal.create (*this, 0, x, y, 235, bh, "Optimal Preset");
preset_optimal.create (*this, 0, x, y, 235, 30, "Optimal Preset"); preset_standard.create(*this, 0, x + 240, y, 235, bh, "Standard Preset");
preset_standard.create(*this, 0, x + 240, y, 235, 30, "Standard Preset"); y += bh;
sync_ui(); sync_ui();
} }

View File

@ -1,6 +1,7 @@
void SettingsWindow::setup() { void SettingsWindow::setup() {
create(ui::Window::Center, 640, 365, "bsnes Configuration Settings"); create(ui::Window::Center, 640, 365, "bsnes Configuration Settings");
panel_list.create(*this, 0, 5, 5, 150, 355); 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("Raster Settings");
panel_list.add_item("Input Configuration"); panel_list.add_item("Input Configuration");
panel_list.add_item("Cheat Code Editor"); 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) { if(id == ui::Message::Changed && (ui::Control*)param == &panel_list) {
switch(panel_list.get_selection()) { switch(panel_list.get_selection()) {
//case 0: panel.attach(window_video_settings); break;
case 0: panel.attach(window_raster_settings); break; case 0: panel.attach(window_raster_settings); break;
case 1: panel.attach(window_input_config); break; case 1: panel.attach(window_input_config); break;
case 2: panel.attach(window_cheat_editor); break; case 2: panel.attach(window_cheat_editor); break;

View File

@ -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, "<width>");
fs_height.create(*this, 0, x + 160, y, 155, eh, "<height>");
fs_refresh.create(*this, 0, x + 320, y, 155, eh, "<refresh_rate>");
y += eh + 5;
}

View File

@ -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;

View File

@ -2,6 +2,7 @@
#include "ui_about.cpp" #include "ui_about.cpp"
#include "settings/ui_settings.cpp" #include "settings/ui_settings.cpp"
#include "settings/ui_videosettings.cpp"
#include "settings/ui_rastersettings.cpp" #include "settings/ui_rastersettings.cpp"
#include "settings/ui_inputconfig.cpp" #include "settings/ui_inputconfig.cpp"
#include "settings/ui_cheateditor.cpp" #include "settings/ui_cheateditor.cpp"
@ -27,6 +28,7 @@ void ui_init() {
window_main.setup(); window_main.setup();
window_about.setup(); window_about.setup();
window_video_settings.setup();
window_raster_settings.setup(); window_raster_settings.setup();
window_input_config.setup(); window_input_config.setup();
window_cheat_editor.setup(); window_cheat_editor.setup();

View File

@ -2,6 +2,7 @@
#include "ui_about.h" #include "ui_about.h"
#include "settings/ui_settings.h" #include "settings/ui_settings.h"
#include "settings/ui_videosettings.h"
#include "settings/ui_rastersettings.h" #include "settings/ui_rastersettings.h"
#include "settings/ui_inputconfig.h" #include "settings/ui_inputconfig.h"
#include "settings/ui_cheateditor.h" #include "settings/ui_cheateditor.h"

View File

@ -32,6 +32,14 @@ ui::Control *control = (ui::Control*)param;
event::load_rom(); 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) { if(control == &menu_file_unload) {
event::unload_rom(); event::unload_rom();
} }
@ -121,10 +129,14 @@ ui::ControlGroup group;
menu.create(*this); menu.create(*this);
menu_file.create(menu, "File"); menu_file.create(menu, "File");
menu_file_load.create(menu_file, "Load Cartridge ..."); 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_sep1.create(menu_file);
menu_file_reset.create(menu_file, "Reset"); menu_file_reset.create(menu_file, "Reset System");
menu_file_power.create(menu_file, "Power"); menu_file_power.create(menu_file, "Power Cycle System");
menu_file_sep2.create(menu_file); menu_file_sep2.create(menu_file);
menu_file_exit.create(menu_file, "Exit"); menu_file_exit.create(menu_file, "Exit");
menu_file.finish(); menu_file.finish();

View File

@ -1,6 +1,9 @@
class MainWindow : public ui::Window { public: class MainWindow : public ui::Window { public:
ui::MenuGroup menu_file; ui::MenuGroup menu_file;
ui::MenuItem menu_file_load; 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::MenuItem menu_file_unload;
ui::MenuSeparator menu_file_sep1; ui::MenuSeparator menu_file_sep1;
ui::MenuItem menu_file_reset; ui::MenuItem menu_file_reset;