mirror of https://github.com/bsnes-emu/bsnes.git
Update to bsnes v034 release.
For this release: SPC7110 emulation speed has been greatly optimized, massive improvements to HDMA timing have been implemented, Multitap support was added, and the user interface was polished a bit more. Changelog: - SPC7110 decompression code updated to latest version by neviksti and converted to a state machine; SPC7110 overhead is now identical to S-DD1 overhead (eg ~5% speed hit over standard games) - Fixed a major bug in SPC7110 data port emulation that was crashing Super Power League 4 [Jonas Quinn] - HDMA trigger point corrected to H=1104, bus sync timing corrected - All illegal DMA A-bus accesses should now be properly blocked - DMA state machine rewritten, greatly simplified - Major corrections to HDMA run timing; fixes flickering bugs in Mecarobot Golf and Super Mario Kart - Emulator now defaults to 2/1/3 SNES (CPU/PPU1/PPU2 revision numbers) - Multitap emulation added, can be attached to either or both controller ports; user interface updated to reflect this - Status messages (cartridge loaded / unloaded, UPS patch applied, etc) now appear in status bar - Added advanced configuration option, "input.analog_axis_resistance", to control gamepad analog stick sensitivity Also, the SPC7110 emulator download link below was removed: if you are looking for this, please download the bsnes v034 source code, which has the most up-to-date version in the src/chip/spc7110 folder.
This commit is contained in:
parent
100ef3a271
commit
dd83559786
|
@ -53,22 +53,19 @@ information in the header. The lack of such a header indicates said file falls
|
|||
under the bsnes license.
|
||||
|
||||
HQ2x filter, author: MaxST, license: LGPL
|
||||
JMA decompressor, author: NSRT Team, license: GPL (*)
|
||||
JMA decompressor, author: NSRT Team, license: GPL*
|
||||
NTSC filter, author: blargg, license: LGPL
|
||||
zlib decompressor, license: zlib license
|
||||
|
||||
(*) bsnes has received an exemption from the copyright holder to use this work.
|
||||
(* bsnes has received an exemption from the copyright holder to use this work.)
|
||||
|
||||
The software also includes works which have been released to the public domain,
|
||||
which are not bound to any licensing agreements. Below is a complete list of all
|
||||
such software.
|
||||
|
||||
libco, author: byuu
|
||||
libui, author: byuu
|
||||
OBC-1 emu, author: byuu
|
||||
S-DD1 decompressor, author: Andreas Naive
|
||||
SPC7110 decompressor, author: neviksti
|
||||
S-RTC emu, author: byuu
|
||||
|
||||
Any software listed above as exemptions may be relicensed individually from
|
||||
bsnes under their respective terms. However, no bsnes licensed portions can be
|
||||
|
|
17
readme.txt
17
readme.txt
|
@ -1,10 +1,11 @@
|
|||
bsnes
|
||||
Version: 0.033
|
||||
Version: 0.034
|
||||
Author: byuu
|
||||
|
||||
========
|
||||
General:
|
||||
========
|
||||
|
||||
bsnes is a Super Nintendo / Super Famicom emulator that began on
|
||||
October 14th, 2004.
|
||||
|
||||
|
@ -16,6 +17,7 @@ Please see license.txt for important licensing information.
|
|||
==============
|
||||
Configuration:
|
||||
==============
|
||||
|
||||
bsnes has two configuration files: bsnes.cfg, for program settings; and
|
||||
locale.cfg, for localization.
|
||||
|
||||
|
@ -39,8 +41,10 @@ mode.
|
|||
==================
|
||||
Known Limitations:
|
||||
==================
|
||||
|
||||
S-CPU
|
||||
- Multiply / divide register delays not implemented
|
||||
- "Glitch" when reading joypad registers during auto polling not implemented
|
||||
|
||||
S-PPU
|
||||
- Uses scanline-based renderer. This is very inaccurate, but few (if any)
|
||||
|
@ -59,6 +63,7 @@ Hardware Bugs
|
|||
=====================
|
||||
Unsupported Hardware:
|
||||
=====================
|
||||
|
||||
SA-1
|
||||
Coprocessor used in many popular games, including:
|
||||
- Dragon Ball Z Hyper Dimension
|
||||
|
@ -87,7 +92,15 @@ Cartridge passthrough used for playing Gameboy games
|
|||
========================
|
||||
Unsupported Controllers:
|
||||
========================
|
||||
|
||||
Mouse
|
||||
Super Scope
|
||||
Justifier
|
||||
Multitap (4-port and 5-port)
|
||||
|
||||
=============
|
||||
Contributors:
|
||||
=============
|
||||
|
||||
Andreas Naive, anomie, blargg, DMV27, FitzRoy, GIGO, Jonas Quinn, kode54, krom,
|
||||
mudlord, Nach, neviksti, Overload, RedDwarf, Richard Bannister, tetsuo55, TRAC,
|
||||
zones
|
||||
|
|
15
src/base.h
15
src/base.h
|
@ -1,4 +1,4 @@
|
|||
#define BSNES_VERSION "0.033"
|
||||
#define BSNES_VERSION "0.034"
|
||||
#define BSNES_TITLE "bsnes v" BSNES_VERSION
|
||||
|
||||
#define BUSCORE sBus
|
||||
|
@ -39,18 +39,5 @@ using namespace nall;
|
|||
//platform-specific global functions
|
||||
void alert(const char*, ...);
|
||||
void dprintf(const char*, ...);
|
||||
void dprintf(uint, const char*, ...);
|
||||
|
||||
namespace source {
|
||||
enum {
|
||||
none = 0,
|
||||
debug,
|
||||
cpu,
|
||||
ppu,
|
||||
smp,
|
||||
dsp,
|
||||
bus,
|
||||
};
|
||||
};
|
||||
|
||||
#include "interface.h"
|
||||
|
|
|
@ -38,6 +38,7 @@ void Cartridge::load_begin(CartridgeType cart_type) {
|
|||
stB.rom_size = stB.ram_size = 0;
|
||||
|
||||
info.type = cart_type;
|
||||
info.patched = false;
|
||||
|
||||
info.bsxbase = false;
|
||||
info.bsxcart = false;
|
||||
|
|
|
@ -71,6 +71,7 @@ public:
|
|||
uint32 crc32;
|
||||
char filename[PATH_MAX * 4];
|
||||
char name[128];
|
||||
bool patched;
|
||||
|
||||
Region region;
|
||||
MemoryMapper mapper;
|
||||
|
|
|
@ -87,8 +87,6 @@ char* Cartridge::get_cheat_filename(const char *source, const char *extension) {
|
|||
}
|
||||
|
||||
bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size, CompressionMode compression) {
|
||||
dprintf("* Loading \"%s\" ...", fn);
|
||||
|
||||
if(fexists(fn) == false) return false;
|
||||
|
||||
Reader::Type filetype = Reader::Normal;
|
||||
|
|
|
@ -5,6 +5,7 @@ void Cartridge::read_header() {
|
|||
uint index = info.header_index;
|
||||
uint8 mapper = rom[index + MAPPER];
|
||||
uint8 rom_type = rom[index + ROM_TYPE];
|
||||
uint8 rom_size = rom[index + ROM_SIZE];
|
||||
uint8 company = rom[index + COMPANY];
|
||||
uint8 region = rom[index + REGION] & 0x7f;
|
||||
|
||||
|
@ -99,13 +100,14 @@ void Cartridge::read_header() {
|
|||
info.obc1 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0xf6) {
|
||||
//TODO: both ST010 and ST011 share the same mapper + rom_type.
|
||||
//need way to determine which is which.
|
||||
//for now, default to supported ST010.
|
||||
if(mapper == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
|
||||
info.st010 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0xf6 && rom_size < 10) {
|
||||
info.st011 = true;
|
||||
}
|
||||
|
||||
if(mapper == 0x30 && rom_type == 0xf5) {
|
||||
info.st018 = true;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ void Cartridge::load_cart_normal(const char *filename) {
|
|||
if(load_file(get_patch_filename(cart.fn, "ups"), data, size, CompressionInspect) == true) {
|
||||
apply_patch(data, size, cart.rom, cart.rom_size);
|
||||
delete[] data;
|
||||
info.patched = true;
|
||||
}
|
||||
|
||||
info.crc32 = crc32_calculate(cart.rom, cart.rom_size);
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
@make platform=win compiler=mingw32-gcc
|
||||
::@make platform=win compiler=mingw32-gcc enable_gzip=true enable_jma=true
|
||||
::@make platform=win compiler=mingw32-gcc
|
||||
@make platform=win compiler=mingw32-gcc enable_gzip=true enable_jma=true
|
||||
@pause
|
||||
|
|
|
@ -1,697 +0,0 @@
|
|||
const uint8_t SPC7110Codec::EvolutionTable[53][4] = {
|
||||
//prob, nextlps, nextmps, toggle invert
|
||||
{0x5a, 1, 1,1}, //0 l,m
|
||||
{0x25, 6, 2,0}, //1 l,m
|
||||
{0x11, 8, 3,0}, //2 l,m
|
||||
{0x08, 10, 4,0}, //3 ,m
|
||||
{0x03, 12, 5,0}, //4 ,m
|
||||
{0x01, 15, 5,0}, //5 ,m
|
||||
|
||||
{0x5a, 7, 7,1}, //6 l,
|
||||
{0x3f, 19, 8,0}, //7 l,m
|
||||
{0x2c, 21, 9,0}, //8 l,m
|
||||
{0x20, 22, 10,0}, //9 ,m
|
||||
{0x17, 23, 11,0}, //10 ,m
|
||||
{0x11, 25, 12,0}, //11 ,m
|
||||
{0x0c, 26, 13,0}, //12 ,m
|
||||
{0x09, 28, 14,0}, //13 ,m
|
||||
{0x07, 29, 15,0}, //14 ,m
|
||||
{0x05, 31, 16,0}, //15 ,m
|
||||
{0x04, 32, 17,0}, //16 ,m
|
||||
{0x03, 34, 18,0}, //17 ,m
|
||||
{0x02, 35, 5,0}, //18 ,m
|
||||
|
||||
{0x5a, 20, 20,1}, //19 l,m
|
||||
{0x48, 39, 21,0}, //20 l,m
|
||||
{0x3a, 40, 22,0}, //21 l,m
|
||||
{0x2e, 42, 23,0}, //22 l,m
|
||||
{0x26, 44, 24,0}, //23 l,m
|
||||
{0x1f, 45, 25,0}, //24 l,m
|
||||
{0x19, 46, 26,0}, //25 l,m
|
||||
{0x15, 25, 27,0}, //26 l,m
|
||||
{0x11, 26, 28,0}, //27 l,m
|
||||
{0x0e, 26, 29,0}, //28 l,m
|
||||
{0x0b, 27, 30,0}, //29 ,m
|
||||
{0x09, 28, 31,0}, //30 ,m
|
||||
{0x08, 29, 32,0}, //31 l,m
|
||||
{0x07, 30, 33,0}, //32 l,m
|
||||
{0x05, 31, 34,0}, //33 l,m <--- changed lps
|
||||
{0x04, 33, 35,0}, //34 ,m ... this is NOT skipped
|
||||
{0x04, 33, 36,0}, //35 ,m
|
||||
{0x03, 34, 37,0}, //36 ,m
|
||||
{0x02, 35, 38,0}, //37 ,m ... this is NOT skipped
|
||||
{0x02, 36, 5,0}, //38 ,m
|
||||
|
||||
{0x58, 39, 40,1}, //39 l,m
|
||||
{0x4d, 47, 41,0}, //40 l,m
|
||||
{0x43, 48, 42,0}, //41 ,m
|
||||
{0x3b, 49, 43,0}, //42 ,m
|
||||
{0x34, 50, 44,0}, //43 l,m
|
||||
{0x2e, 51, 45,0}, //44 l,m
|
||||
{0x29, 44, 46,0}, //45 l,m
|
||||
{0x25, 45, 24,0}, //46 ,m
|
||||
|
||||
{0x56, 47, 48,1}, //47 l,m
|
||||
{0x4f, 47, 49,0}, //48 l,m
|
||||
{0x47, 48, 50,0}, //49 l,m
|
||||
{0x41, 49, 51,0}, //50 l,m
|
||||
{0x3c, 50, 52,0}, //51 l,m
|
||||
{0x37, 51, 43,0} //52 ,m
|
||||
};
|
||||
|
||||
const uint8_t SPC7110Codec::Mode2ContextTable[32][4] = {
|
||||
// "bit" = (lps^invert)
|
||||
//next_0, use ref pixel, next_1, use ref pixel
|
||||
// if use ref pixel, then add on the 0-4 bell number grouping
|
||||
{1, 0, 2, 0}, //0
|
||||
|
||||
{3, 1, 8, 1}, //1 prev bit 0
|
||||
{13,0, 14,0}, //2 prev bit 1
|
||||
|
||||
{15,0, 16,0}, //3 prev bit 00
|
||||
{17,0, 18,0}, //4
|
||||
{19,0, 20,0}, //5
|
||||
{21,0, 22,0}, //6
|
||||
{23,0, 24,0}, //7
|
||||
{25,0, 26,0}, //8 prev bit 01
|
||||
{25,0, 26,0}, //9
|
||||
{25,0, 26,0}, //10
|
||||
{25,0, 26,0}, //11
|
||||
{25,0, 26,0}, //12
|
||||
{27,0, 28,0}, //13 prev bit 10
|
||||
{29,0, 30,0}, //14 prev bit 11
|
||||
|
||||
{31,0, 31,0}, //15 000 ref group 0
|
||||
{31,0, 31,0}, //16 001 ref group 0
|
||||
{31,0, 31,0}, //17 000 ref group 1
|
||||
{31,0, 31,0}, //18 001 ref group 1
|
||||
{31,0, 31,0}, //19 000 ref group 2
|
||||
{31,0, 31,0}, //20 001 ref group 2
|
||||
{31,0, 31,0}, //21 000 ref group 3
|
||||
{31,0, 31,0}, //22 001 ref group 3
|
||||
{31,0, 31,0}, //23 000 ref group 4
|
||||
{31,0, 31,0}, //24 001 ref group 4
|
||||
{31,0, 31,0}, //25 010
|
||||
{31,0, 31,0}, //26 011
|
||||
{31,0, 31,0}, //27 100
|
||||
{31,0, 31,0}, //28 101
|
||||
{31,0, 31,0}, //29 110
|
||||
{31,0, 31,0}, //30 111
|
||||
|
||||
{31,0, 31,0} //31 -- used as a trap for testing purposes --
|
||||
};
|
||||
|
||||
#define PROB(x) EvolutionTable[Contexts[x].index][0]
|
||||
#define NEXT_LPS(x) EvolutionTable[Contexts[x].index][1]
|
||||
#define NEXT_MPS(x) EvolutionTable[Contexts[x].index][2]
|
||||
#define TOGGLE_INVERT(x) EvolutionTable[Contexts[x].index][3]
|
||||
#define BIT(x,y) ((x>>y)&1)
|
||||
|
||||
void SPC7110Codec::decomp_mode0(int len) {
|
||||
uint8_t *datain = buffer;
|
||||
uint8_t *dataout = output;
|
||||
static const unsigned NUM_CONTEXTS = 30;
|
||||
|
||||
uint8 top,val;
|
||||
uint8 con,mps,prob;
|
||||
uint8 flag_lps,shift,mask;
|
||||
|
||||
int out=0;
|
||||
int inverts=0;
|
||||
int lps=0;
|
||||
|
||||
unsigned char in;
|
||||
int in_count;
|
||||
|
||||
int i,bit;
|
||||
|
||||
//setup
|
||||
top=0xFF;
|
||||
|
||||
val=*datain;
|
||||
datain++;
|
||||
|
||||
in=*datain;
|
||||
datain++;
|
||||
in_count=8;
|
||||
|
||||
//reset context states
|
||||
for(i=0;i<NUM_CONTEXTS;i++)
|
||||
{
|
||||
Contexts[i].index=0;
|
||||
Contexts[i].invert=0;
|
||||
}
|
||||
|
||||
for(i=0;i<len;i++)
|
||||
{
|
||||
if(i==-1800)
|
||||
{
|
||||
int k;
|
||||
printf("\nEvolution table:\n");
|
||||
//for(k=0;k<53;k++)
|
||||
//printf(" %d,%d //%d\n",SeenEvolution[k][0],SeenEvolution[k][1],k);
|
||||
}
|
||||
|
||||
|
||||
for(bit=0;bit<8;bit++)
|
||||
{
|
||||
//get context
|
||||
mask = (1<<(bit&3)) - 1;
|
||||
con = mask + ((inverts&mask)^(lps&mask));
|
||||
if(bit>3)
|
||||
con+=15;
|
||||
|
||||
//get PROB and MPS
|
||||
prob = PROB(con);
|
||||
mps = (BIT(out,15) ^ Contexts[con].invert);
|
||||
|
||||
if(i>=15 && i<=18 && 0)
|
||||
printf("byte %d bit %d: val=%.2X top=%.2X prob=%.2X mps=%d con=%d state=%d\n",
|
||||
i,bit,val,top,prob,mps,con,Contexts[con].index);
|
||||
|
||||
//get bit
|
||||
if (val <= top-prob)
|
||||
{
|
||||
//mps
|
||||
top = top - prob;
|
||||
out = (out << 1) + mps;
|
||||
|
||||
flag_lps=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//lps
|
||||
val = val - (top - (prob - 1));
|
||||
top = prob - 1;
|
||||
out = (out << 1) + 1-mps;
|
||||
|
||||
flag_lps=1;
|
||||
}
|
||||
|
||||
// renormalize
|
||||
shift=0;
|
||||
while(top<0x7F) // NOTE: not 0x80, it's a strange border case
|
||||
{
|
||||
shift++;
|
||||
|
||||
top = (top<<1)+1;
|
||||
val = (val<<1)+(in>>7);
|
||||
|
||||
in = (in<<1);
|
||||
if(--in_count==0)
|
||||
{
|
||||
in=*datain;
|
||||
datain++;
|
||||
in_count=8;
|
||||
}
|
||||
}
|
||||
|
||||
//update processing info
|
||||
lps = (lps<<1) + flag_lps;
|
||||
inverts = (inverts<<1) + Contexts[con].invert;
|
||||
|
||||
//update context state
|
||||
if(flag_lps & TOGGLE_INVERT(con))
|
||||
Contexts[con].invert ^= 1;
|
||||
|
||||
if(flag_lps)
|
||||
{
|
||||
//SeenEvolution[Contexts[con].index][0]=1;
|
||||
Contexts[con].index = NEXT_LPS(con);
|
||||
}
|
||||
else if(shift)
|
||||
{
|
||||
//SeenEvolution[Contexts[con].index][1]=1;
|
||||
Contexts[con].index = NEXT_MPS(con);
|
||||
}
|
||||
}
|
||||
|
||||
//save byte
|
||||
*dataout = (out & 0xFF);
|
||||
dataout++;
|
||||
}
|
||||
}
|
||||
|
||||
void SPC7110Codec::decomp_mode1(int len) {
|
||||
uint8_t *datain = buffer;
|
||||
uint8_t *dataout = output;
|
||||
static const unsigned NUM_CONTEXTS = 15;
|
||||
|
||||
int pixelorder[4]={0,1,2,3};
|
||||
int realorder[4];
|
||||
int a,b,c;
|
||||
int m,n;
|
||||
|
||||
uint8 top,val;
|
||||
uint8 con,prob;
|
||||
uint8 flag_lps,shift;
|
||||
|
||||
int out=0;
|
||||
int inverts=0;
|
||||
int lps=0;
|
||||
|
||||
unsigned char in;
|
||||
int in_count;
|
||||
int in_len=0;
|
||||
|
||||
int i,j,pixel;
|
||||
|
||||
//setup
|
||||
top=0xFF;
|
||||
|
||||
val=datain[in_len++];
|
||||
|
||||
in=datain[in_len++];
|
||||
in_count=8;
|
||||
|
||||
//reset context states
|
||||
for(i=0;i<NUM_CONTEXTS;i++)
|
||||
{
|
||||
Contexts[i].index=0;
|
||||
Contexts[i].invert=0;
|
||||
}
|
||||
|
||||
for(i=0;i<len;i+=2)
|
||||
{
|
||||
if(i!=0)
|
||||
{
|
||||
//turn pixel data into bitplanes
|
||||
//and save as output
|
||||
*dataout = (BIT(out,15)<<7) + (BIT(out,13)<<6) + (BIT(out,11)<<5) + (BIT(out,9)<<4)
|
||||
+ (BIT(out,7)<<3) + (BIT(out,5)<<2) + (BIT(out,3)<<1) + BIT(out,1);
|
||||
dataout++;
|
||||
*dataout = (BIT(out,14)<<7) + (BIT(out,12)<<6) + (BIT(out,10)<<5) + (BIT(out,8)<<4)
|
||||
+ (BIT(out,6)<<3) + (BIT(out,4)<<2) + (BIT(out,2)<<1) + BIT(out,0);
|
||||
dataout++;
|
||||
}
|
||||
|
||||
for(pixel=0;pixel<8;pixel++)
|
||||
{
|
||||
//get first symbol context
|
||||
a = ((out >> (1*2)) & 0x3);
|
||||
b = ((out >> (7*2)) & 0x3);
|
||||
c = ((out >> (8*2)) & 0x3);
|
||||
if(a==b && b==c)
|
||||
con=0;
|
||||
else if (a==b && b!=c)
|
||||
con=1;
|
||||
else if (a!=b && b==c)
|
||||
con=2;
|
||||
else if (a==c && b!=c)
|
||||
con=3;
|
||||
else
|
||||
con=4;
|
||||
|
||||
//update pixel order
|
||||
for(m=0;m<4;m++)
|
||||
if(pixelorder[m]==a)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=pixelorder[n-1];
|
||||
pixelorder[n-1]=pixelorder[n];
|
||||
pixelorder[n]=j;
|
||||
}
|
||||
|
||||
|
||||
//get PROB
|
||||
prob = PROB(con);
|
||||
|
||||
//get symbol
|
||||
if (val <= top-prob)
|
||||
{
|
||||
//mps
|
||||
top = top - prob;
|
||||
flag_lps=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//lps
|
||||
val = val - (top - (prob - 1));
|
||||
top = prob - 1;
|
||||
flag_lps=1;
|
||||
}
|
||||
|
||||
// renormalize
|
||||
shift=0;
|
||||
while(top<0x7F)
|
||||
{
|
||||
shift++;
|
||||
|
||||
top = (top<<1)+1;
|
||||
val = (val<<1)+(in>>7);
|
||||
|
||||
in = (in<<1);
|
||||
if(--in_count==0)
|
||||
{
|
||||
in=datain[in_len++];
|
||||
in_count=8;
|
||||
}
|
||||
}
|
||||
|
||||
//update processing info
|
||||
lps = (lps<<1) + flag_lps;
|
||||
inverts = (inverts<<1) + Contexts[con].invert;
|
||||
|
||||
//update context state
|
||||
if(flag_lps & TOGGLE_INVERT(con))
|
||||
Contexts[con].invert ^= 1;
|
||||
if(flag_lps)
|
||||
Contexts[con].index = NEXT_LPS(con);
|
||||
else if(shift)
|
||||
Contexts[con].index = NEXT_MPS(con);
|
||||
|
||||
//get context of second symbol
|
||||
con = 5 + con*2 + ((lps^inverts)&1);
|
||||
|
||||
//get PROB
|
||||
prob = PROB(con);
|
||||
|
||||
//get symbol
|
||||
if (val <= top-prob)
|
||||
{
|
||||
//mps
|
||||
top = top - prob;
|
||||
flag_lps=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//lps
|
||||
val = val - (top - (prob - 1));
|
||||
top = prob - 1;
|
||||
flag_lps=1;
|
||||
}
|
||||
|
||||
// renormalize
|
||||
shift=0;
|
||||
while(top<0x7F)
|
||||
{
|
||||
shift++;
|
||||
|
||||
top = (top<<1)+1;
|
||||
val = (val<<1)+(in>>7);
|
||||
|
||||
in = (in<<1);
|
||||
if(--in_count==0)
|
||||
{
|
||||
in=datain[in_len++];
|
||||
in_count=8;
|
||||
}
|
||||
}
|
||||
|
||||
//calculate the real pixel order
|
||||
for(m=0;m<4;m++)
|
||||
realorder[m]=pixelorder[m];
|
||||
//shift refence pixel c value to top
|
||||
for(m=0;m<4;m++)
|
||||
if(realorder[m]==c)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=realorder[n-1];
|
||||
realorder[n-1]=realorder[n];
|
||||
realorder[n]=j;
|
||||
}
|
||||
//shift refence pixel b value to top
|
||||
for(m=0;m<4;m++)
|
||||
if(realorder[m]==b)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=realorder[n-1];
|
||||
realorder[n-1]=realorder[n];
|
||||
realorder[n]=j;
|
||||
}
|
||||
//shift refence pixel a value to top
|
||||
for(m=0;m<4;m++)
|
||||
if(realorder[m]==a)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=realorder[n-1];
|
||||
realorder[n-1]=realorder[n];
|
||||
realorder[n]=j;
|
||||
}
|
||||
|
||||
|
||||
//update processing info
|
||||
lps = (lps<<1) + flag_lps;
|
||||
inverts = (inverts<<1) + Contexts[con].invert;
|
||||
|
||||
//update context state
|
||||
if(flag_lps & TOGGLE_INVERT(con))
|
||||
Contexts[con].invert ^= 1;
|
||||
if(flag_lps)
|
||||
Contexts[con].index = NEXT_LPS(con);
|
||||
else if(shift)
|
||||
Contexts[con].index = NEXT_MPS(con);
|
||||
|
||||
//get pixel
|
||||
b=realorder[(lps^inverts)&3];
|
||||
out = (out<<2) + b;
|
||||
}
|
||||
}
|
||||
|
||||
//turn pixel data into bitplanes
|
||||
//and save as output.. BUT don't save second byte unless asked to
|
||||
*dataout = (BIT(out,15)<<7) + (BIT(out,13)<<6) + (BIT(out,11)<<5) + (BIT(out,9)<<4)
|
||||
+ (BIT(out,7)<<3) + (BIT(out,5)<<2) + (BIT(out,3)<<1) + BIT(out,1);
|
||||
dataout++;
|
||||
if((len&1)==0)
|
||||
{
|
||||
*dataout = (BIT(out,14)<<7) + (BIT(out,12)<<6) + (BIT(out,10)<<5) + (BIT(out,8)<<4)
|
||||
+ (BIT(out,6)<<3) + (BIT(out,4)<<2) + (BIT(out,2)<<1) + BIT(out,0);
|
||||
dataout++;
|
||||
}
|
||||
|
||||
if(in_count==8)
|
||||
in_len--;
|
||||
//printf("Used %d bytes of input.\n",in_len);
|
||||
//return in_len;
|
||||
}
|
||||
|
||||
void SPC7110Codec::decomp_mode2(int len) {
|
||||
uint8_t *datain = buffer;
|
||||
uint8_t *dataout = output;
|
||||
static const unsigned NUM_CONTEXTS = 32;
|
||||
|
||||
int pixelorder[16]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
|
||||
int realorder[16];
|
||||
int a,b,c;
|
||||
int m,n;
|
||||
|
||||
uint8 bitplanebuffer[16];
|
||||
uint8 buf_idx=0;
|
||||
|
||||
uint8 top,val,prob;
|
||||
uint8 con,refcon;
|
||||
uint8 flag_lps,invertbit,shift;
|
||||
|
||||
int out=0;
|
||||
int out2=0;
|
||||
int inverts=0;
|
||||
int lps=0;
|
||||
|
||||
unsigned char in;
|
||||
int in_count;
|
||||
int in_len=0;
|
||||
|
||||
int i,j,pixel,bit;
|
||||
|
||||
//setup
|
||||
top=0xFF;
|
||||
|
||||
val=datain[in_len++];
|
||||
|
||||
in=datain[in_len++];
|
||||
in_count=8;
|
||||
|
||||
//reset context states
|
||||
for(i=0;i<NUM_CONTEXTS;i++)
|
||||
{
|
||||
Contexts[i].index=0;
|
||||
Contexts[i].invert=0;
|
||||
}
|
||||
|
||||
for(i=0;i<len;i+=2)
|
||||
{
|
||||
for(pixel=0;pixel<8;pixel++)
|
||||
{
|
||||
//get first symbol context
|
||||
a = ((out >> (0*4)) & 0x0F);
|
||||
b = ((out >> (7*4)) & 0x0F);
|
||||
c = ((out2>> (0*4)) & 0x0F);
|
||||
if(a==b && b==c)
|
||||
refcon=0;
|
||||
else if (a==b && b!=c)
|
||||
refcon=1;
|
||||
else if (a!=b && b==c)
|
||||
refcon=2;
|
||||
else if (a==c && b!=c)
|
||||
refcon=3;
|
||||
else
|
||||
refcon=4;
|
||||
|
||||
con=0;
|
||||
|
||||
//update pixel order
|
||||
for(m=0;m<16;m++)
|
||||
if(pixelorder[m]==a)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=pixelorder[n-1];
|
||||
pixelorder[n-1]=pixelorder[n];
|
||||
pixelorder[n]=j;
|
||||
}
|
||||
|
||||
//calculate the real pixel order
|
||||
for(m=0;m<16;m++)
|
||||
realorder[m]=pixelorder[m];
|
||||
//shift refence pixel c value to top
|
||||
for(m=0;m<16;m++)
|
||||
if(realorder[m]==c)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=realorder[n-1];
|
||||
realorder[n-1]=realorder[n];
|
||||
realorder[n]=j;
|
||||
}
|
||||
//shift refence pixel b value to top
|
||||
for(m=0;m<16;m++)
|
||||
if(realorder[m]==b)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=realorder[n-1];
|
||||
realorder[n-1]=realorder[n];
|
||||
realorder[n]=j;
|
||||
}
|
||||
//shift refence pixel a value to top
|
||||
for(m=0;m<16;m++)
|
||||
if(realorder[m]==a)
|
||||
break;
|
||||
for(n=m;n>0;n--)
|
||||
{
|
||||
j=realorder[n-1];
|
||||
realorder[n-1]=realorder[n];
|
||||
realorder[n]=j;
|
||||
}
|
||||
|
||||
//get 4 symbols
|
||||
for(bit=0;bit<4;bit++)
|
||||
{
|
||||
//get PROB
|
||||
prob = PROB(con);
|
||||
|
||||
//get symbol
|
||||
if (val <= top-prob)
|
||||
{
|
||||
//mps
|
||||
top = top - prob;
|
||||
flag_lps=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//lps
|
||||
val = val - (top - (prob - 1));
|
||||
top = prob - 1;
|
||||
flag_lps=1;
|
||||
}
|
||||
|
||||
// renormalize
|
||||
shift=0;
|
||||
while(top<0x7F)
|
||||
{
|
||||
shift++;
|
||||
|
||||
top = (top<<1)+1;
|
||||
val = (val<<1)+(in>>7);
|
||||
|
||||
in = (in<<1);
|
||||
if(--in_count==0)
|
||||
{
|
||||
in=datain[in_len++];
|
||||
in_count=8;
|
||||
}
|
||||
}
|
||||
|
||||
//update processing info
|
||||
lps = (lps<<1) + flag_lps;
|
||||
invertbit = Contexts[con].invert;
|
||||
inverts = (inverts<<1) + Contexts[con].invert;
|
||||
|
||||
//update context state
|
||||
if(flag_lps & TOGGLE_INVERT(con))
|
||||
Contexts[con].invert ^= 1;
|
||||
if(flag_lps)
|
||||
Contexts[con].index = NEXT_LPS(con);
|
||||
else if(shift)
|
||||
Contexts[con].index = NEXT_MPS(con);
|
||||
|
||||
//get next context
|
||||
if(Mode2ContextTable[con][2*(flag_lps^invertbit)+1])
|
||||
con=Mode2ContextTable[con][2*(flag_lps^invertbit)]+refcon;
|
||||
else
|
||||
con=Mode2ContextTable[con][2*(flag_lps^invertbit)];
|
||||
}
|
||||
|
||||
//get pixel
|
||||
b=realorder[(lps^inverts)&0x0F];
|
||||
out2 = (out2<<4) + ((out>>28)&0x0F);
|
||||
out = (out<<4) + b;
|
||||
}
|
||||
|
||||
//cludge to convert pixel data into bitplanes and respect len parameter for output buf
|
||||
*dataout = (BIT(out,31)<<7) + (BIT(out,27)<<6) + (BIT(out,23)<<5) + (BIT(out,19)<<4)
|
||||
+ (BIT(out,15)<<3) + (BIT(out,11)<<2) + (BIT(out,7)<<1) + BIT(out,3);
|
||||
dataout++;
|
||||
|
||||
if((i+1)<len)
|
||||
{
|
||||
*dataout = (BIT(out,30)<<7) + (BIT(out,26)<<6) + (BIT(out,22)<<5) + (BIT(out,18)<<4)
|
||||
+ (BIT(out,14)<<3) + (BIT(out,10)<<2) + (BIT(out,6)<<1) + BIT(out,2);
|
||||
dataout++;
|
||||
}
|
||||
|
||||
bitplanebuffer[buf_idx++] =
|
||||
(BIT(out,29)<<7) + (BIT(out,25)<<6) + (BIT(out,21)<<5) + (BIT(out,17)<<4)
|
||||
+ (BIT(out,13)<<3) + (BIT(out,9)<<2) + (BIT(out,5)<<1) + BIT(out,1);
|
||||
bitplanebuffer[buf_idx++] =
|
||||
(BIT(out,28)<<7) + (BIT(out,24)<<6) + (BIT(out,20)<<5) + (BIT(out,16)<<4)
|
||||
+ (BIT(out,12)<<3) + (BIT(out,8)<<2) + (BIT(out,4)<<1) + BIT(out,0);
|
||||
|
||||
if(buf_idx==16)
|
||||
{
|
||||
for(m=0;m<16 && i+2<len;m++,i++)
|
||||
{
|
||||
*dataout = bitplanebuffer[m];
|
||||
dataout++;
|
||||
}
|
||||
|
||||
buf_idx=0;
|
||||
}
|
||||
}
|
||||
|
||||
if(in_count==8)
|
||||
in_len--;
|
||||
//printf("Used %d bytes of input.\n",in_len);
|
||||
//return in_len;
|
||||
}
|
||||
|
||||
#undef PROB
|
||||
#undef NEXT_LPS
|
||||
#undef NEXT_MPS
|
||||
#undef TOGGLE_INVERT
|
||||
#undef BIT
|
||||
|
||||
SPC7110Codec::SPC7110Codec() {
|
||||
buffer = new(zeromemory) uint8_t[65536];
|
||||
output = new(zeromemory) uint8_t[65536];
|
||||
}
|
||||
|
||||
SPC7110Codec::~SPC7110Codec() {
|
||||
delete[] buffer;
|
||||
delete[] output;
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
class SPC7110Codec {
|
||||
public:
|
||||
uint8_t *buffer;
|
||||
uint8_t *output;
|
||||
|
||||
void decomp_mode0(int len);
|
||||
void decomp_mode1(int len);
|
||||
void decomp_mode2(int len);
|
||||
|
||||
SPC7110Codec();
|
||||
~SPC7110Codec();
|
||||
|
||||
private:
|
||||
static const uint8_t EvolutionTable[53][4];
|
||||
static const uint8_t Mode2ContextTable[32][4];
|
||||
struct ContextState {
|
||||
uint8_t index;
|
||||
uint8_t invert;
|
||||
} Contexts[32];
|
||||
};
|
|
@ -0,0 +1,511 @@
|
|||
#ifdef SPC7110_CPP
|
||||
|
||||
uint8 SPC7110Decomp::read() {
|
||||
if(decomp_buffer_length == 0) {
|
||||
//decompress at least (decomp_buffer_size / 2) bytes to the buffer
|
||||
switch(decomp_mode) {
|
||||
case 0: mode0(false); break;
|
||||
case 1: mode1(false); break;
|
||||
case 2: mode2(false); break;
|
||||
default: return 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 data = decomp_buffer[decomp_buffer_rdoffset++];
|
||||
decomp_buffer_rdoffset &= decomp_buffer_size - 1;
|
||||
decomp_buffer_length--;
|
||||
return data;
|
||||
}
|
||||
|
||||
void SPC7110Decomp::write(uint8 data) {
|
||||
decomp_buffer[decomp_buffer_wroffset++] = data;
|
||||
decomp_buffer_wroffset &= decomp_buffer_size - 1;
|
||||
decomp_buffer_length++;
|
||||
}
|
||||
|
||||
uint8 SPC7110Decomp::dataread() {
|
||||
unsigned size = memory::cartrom.size() - 0x100000;
|
||||
while(decomp_offset >= size) decomp_offset -= size;
|
||||
return memory::cartrom.read(0x100000 + decomp_offset++);
|
||||
}
|
||||
|
||||
void SPC7110Decomp::init(unsigned mode, unsigned offset, unsigned index) {
|
||||
decomp_mode = mode;
|
||||
decomp_offset = offset;
|
||||
|
||||
decomp_buffer_rdoffset = 0;
|
||||
decomp_buffer_wroffset = 0;
|
||||
decomp_buffer_length = 0;
|
||||
|
||||
//reset context states
|
||||
for(unsigned i = 0; i < 32; i++) {
|
||||
context[i].index = 0;
|
||||
context[i].invert = 0;
|
||||
}
|
||||
|
||||
switch(decomp_mode) {
|
||||
case 0: mode0(true); break;
|
||||
case 1: mode1(true); break;
|
||||
case 2: mode2(true); break;
|
||||
}
|
||||
|
||||
//decompress up to requested output data index
|
||||
while(index--) read();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void SPC7110Decomp::mode0(bool init) {
|
||||
static uint8 val, in, span;
|
||||
static int out, inverts, lps, in_count;
|
||||
|
||||
if(init == true) {
|
||||
out = inverts = lps = 0;
|
||||
span = 0xff;
|
||||
val = dataread();
|
||||
in = dataread();
|
||||
in_count = 8;
|
||||
return;
|
||||
}
|
||||
|
||||
while(decomp_buffer_length < (decomp_buffer_size >> 1)) {
|
||||
for(unsigned bit = 0; bit < 8; bit++) {
|
||||
//get context
|
||||
uint8 mask = (1 << (bit & 3)) - 1;
|
||||
uint8 con = mask + ((inverts & mask) ^ (lps & mask));
|
||||
if(bit > 3) con += 15;
|
||||
|
||||
//get prob and mps
|
||||
unsigned prob = probability(con);
|
||||
unsigned mps = (((out >> 15) & 1) ^ context[con].invert);
|
||||
|
||||
//get bit
|
||||
unsigned flag_lps;
|
||||
if(val <= span - prob) { //mps
|
||||
span = span - prob;
|
||||
out = (out << 1) + mps;
|
||||
flag_lps = 0;
|
||||
} else { //lps
|
||||
val = val - (span - (prob - 1));
|
||||
span = prob - 1;
|
||||
out = (out << 1) + 1 - mps;
|
||||
flag_lps = 1;
|
||||
}
|
||||
|
||||
//renormalize
|
||||
unsigned shift = 0;
|
||||
while(span < 0x7f) {
|
||||
shift++;
|
||||
|
||||
span = (span << 1) + 1;
|
||||
val = (val << 1) + (in >> 7);
|
||||
|
||||
in <<= 1;
|
||||
if(--in_count == 0) {
|
||||
in = dataread();
|
||||
in_count = 8;
|
||||
}
|
||||
}
|
||||
|
||||
//update processing info
|
||||
lps = (lps << 1) + flag_lps;
|
||||
inverts = (inverts << 1) + context[con].invert;
|
||||
|
||||
//update context state
|
||||
if(flag_lps & toggle_invert(con)) context[con].invert ^= 1;
|
||||
if(flag_lps) context[con].index = next_lps(con);
|
||||
else if(shift) context[con].index = next_mps(con);
|
||||
}
|
||||
|
||||
//save byte
|
||||
write(out);
|
||||
}
|
||||
}
|
||||
|
||||
void SPC7110Decomp::mode1(bool init) {
|
||||
static int pixelorder[4], realorder[4];
|
||||
static uint8 in, val, span;
|
||||
static int out, inverts, lps, in_count;
|
||||
|
||||
if(init == true) {
|
||||
for(unsigned i = 0; i < 4; i++) pixelorder[i] = i;
|
||||
out = inverts = lps = 0;
|
||||
span = 0xff;
|
||||
val = dataread();
|
||||
in = dataread();
|
||||
in_count = 8;
|
||||
return;
|
||||
}
|
||||
|
||||
while(decomp_buffer_length < (decomp_buffer_size >> 1)) {
|
||||
for(unsigned pixel = 0; pixel < 8; pixel++) {
|
||||
//get first symbol context
|
||||
unsigned a = ((out >> (1 * 2)) & 3);
|
||||
unsigned b = ((out >> (7 * 2)) & 3);
|
||||
unsigned c = ((out >> (8 * 2)) & 3);
|
||||
unsigned con = (a == b) ? (b != c) : (b == c) ? 2 : 4 - (a == c);
|
||||
|
||||
//update pixel order
|
||||
unsigned m, n;
|
||||
for(m = 0; m < 4; m++) if(pixelorder[m] == a) break;
|
||||
for(n = m; n > 0; n--) pixelorder[n] = pixelorder[n - 1];
|
||||
pixelorder[0] = a;
|
||||
|
||||
//calculate the real pixel order
|
||||
for(m = 0; m < 4; m++) realorder[m] = pixelorder[m];
|
||||
|
||||
//rotate reference pixel c value to top
|
||||
for(m = 0; m < 4; m++) if(realorder[m] == c) break;
|
||||
for(n = m; n > 0; n--) realorder[n] = realorder[n - 1];
|
||||
realorder[0] = c;
|
||||
|
||||
//rotate reference pixel b value to top
|
||||
for(m = 0; m < 4; m++) if(realorder[m] == b) break;
|
||||
for(n = m; n > 0; n--) realorder[n] = realorder[n - 1];
|
||||
realorder[0] = b;
|
||||
|
||||
//rotate reference pixel a value to top
|
||||
for(m = 0; m < 4; m++) if(realorder[m] == a) break;
|
||||
for(n = m; n > 0; n--) realorder[n] = realorder[n - 1];
|
||||
realorder[0] = a;
|
||||
|
||||
//get 2 symbols
|
||||
for(unsigned bit = 0; bit < 2; bit++) {
|
||||
//get prob
|
||||
unsigned prob = probability(con);
|
||||
|
||||
//get symbol
|
||||
unsigned flag_lps;
|
||||
if(val <= span - prob) { //mps
|
||||
span = span - prob;
|
||||
flag_lps = 0;
|
||||
} else { //lps
|
||||
val = val - (span - (prob - 1));
|
||||
span = prob - 1;
|
||||
flag_lps = 1;
|
||||
}
|
||||
|
||||
//renormalize
|
||||
unsigned shift = 0;
|
||||
while(span < 0x7f) {
|
||||
shift++;
|
||||
|
||||
span = (span << 1) + 1;
|
||||
val = (val << 1) + (in >> 7);
|
||||
|
||||
in <<= 1;
|
||||
if(--in_count == 0) {
|
||||
in = dataread();
|
||||
in_count = 8;
|
||||
}
|
||||
}
|
||||
|
||||
//update processing info
|
||||
lps = (lps << 1) + flag_lps;
|
||||
inverts = (inverts << 1) + context[con].invert;
|
||||
|
||||
//update context state
|
||||
if(flag_lps & toggle_invert(con)) context[con].invert ^= 1;
|
||||
if(flag_lps) context[con].index = next_lps(con);
|
||||
else if(shift) context[con].index = next_mps(con);
|
||||
|
||||
//get next context
|
||||
con = 5 + (con << 1) + ((lps ^ inverts) & 1);
|
||||
}
|
||||
|
||||
//get pixel
|
||||
b = realorder[(lps ^ inverts) & 3];
|
||||
out = (out << 2) + b;
|
||||
}
|
||||
|
||||
//turn pixel data into bitplanes
|
||||
unsigned data = morton_2x8(out);
|
||||
write(data >> 8);
|
||||
write(data >> 0);
|
||||
}
|
||||
}
|
||||
|
||||
void SPC7110Decomp::mode2(bool init) {
|
||||
static int pixelorder[16], realorder[16];
|
||||
static uint8 bitplanebuffer[16], buffer_index;
|
||||
static uint8 in, val, span;
|
||||
static int out0, out1, inverts, lps, in_count;
|
||||
|
||||
if(init == true) {
|
||||
for(unsigned i = 0; i < 16; i++) pixelorder[i] = i;
|
||||
buffer_index = 0;
|
||||
out0 = out1 = inverts = lps = 0;
|
||||
span = 0xff;
|
||||
val = dataread();
|
||||
in = dataread();
|
||||
in_count = 8;
|
||||
return;
|
||||
}
|
||||
|
||||
while(decomp_buffer_length < (decomp_buffer_size >> 1)) {
|
||||
for(unsigned pixel = 0; pixel < 8; pixel++) {
|
||||
//get first symbol context
|
||||
unsigned a = ((out0 >> (0 * 4)) & 15);
|
||||
unsigned b = ((out0 >> (7 * 4)) & 15);
|
||||
unsigned c = ((out1 >> (0 * 4)) & 15);
|
||||
unsigned con = 0;
|
||||
unsigned refcon = (a == b) ? (b != c) : (b == c) ? 2 : 4 - (a == c);
|
||||
|
||||
//update pixel order
|
||||
unsigned m, n;
|
||||
for(m = 0; m < 16; m++) if(pixelorder[m] == a) break;
|
||||
for(n = m; n > 0; n--) pixelorder[n] = pixelorder[n - 1];
|
||||
pixelorder[0] = a;
|
||||
|
||||
//calculate the real pixel order
|
||||
for(m = 0; m < 16; m++) realorder[m] = pixelorder[m];
|
||||
|
||||
//rotate reference pixel c value to top
|
||||
for(m = 0; m < 16; m++) if(realorder[m] == c) break;
|
||||
for(n = m; n > 0; n--) realorder[n] = realorder[n - 1];
|
||||
realorder[0] = c;
|
||||
|
||||
//rotate reference pixel b value to top
|
||||
for(m = 0; m < 16; m++) if(realorder[m] == b) break;
|
||||
for(n = m; n > 0; n--) realorder[n] = realorder[n - 1];
|
||||
realorder[0] = b;
|
||||
|
||||
//rotate reference pixel a value to top
|
||||
for(m = 0; m < 16; m++) if(realorder[m] == a) break;
|
||||
for(n = m; n > 0; n--) realorder[n] = realorder[n - 1];
|
||||
realorder[0] = a;
|
||||
|
||||
//get 4 symbols
|
||||
for(unsigned bit = 0; bit < 4; bit++) {
|
||||
//get prob
|
||||
unsigned prob = probability(con);
|
||||
|
||||
//get symbol
|
||||
unsigned flag_lps;
|
||||
if(val <= span - prob) { //mps
|
||||
span = span - prob;
|
||||
flag_lps = 0;
|
||||
} else { //lps
|
||||
val = val - (span - (prob - 1));
|
||||
span = prob - 1;
|
||||
flag_lps = 1;
|
||||
}
|
||||
|
||||
//renormalize
|
||||
unsigned shift = 0;
|
||||
while(span < 0x7f) {
|
||||
shift++;
|
||||
|
||||
span = (span << 1) + 1;
|
||||
val = (val << 1) + (in >> 7);
|
||||
|
||||
in <<= 1;
|
||||
if(--in_count == 0) {
|
||||
in = dataread();
|
||||
in_count = 8;
|
||||
}
|
||||
}
|
||||
|
||||
//update processing info
|
||||
lps = (lps << 1) + flag_lps;
|
||||
unsigned invertbit = context[con].invert;
|
||||
inverts = (inverts << 1) + invertbit;
|
||||
|
||||
//update context state
|
||||
if(flag_lps & toggle_invert(con)) context[con].invert ^= 1;
|
||||
if(flag_lps) context[con].index = next_lps(con);
|
||||
else if(shift) context[con].index = next_mps(con);
|
||||
|
||||
//get next context
|
||||
con = mode2_context_table[con][flag_lps ^ invertbit] + (con == 1 ? refcon : 0);
|
||||
}
|
||||
|
||||
//get pixel
|
||||
b = realorder[(lps ^ inverts) & 0x0f];
|
||||
out1 = (out1 << 4) + ((out0 >> 28) & 0x0f);
|
||||
out0 = (out0 << 4) + b;
|
||||
}
|
||||
|
||||
//convert pixel data into bitplanes
|
||||
unsigned data = morton_4x8(out0);
|
||||
write(data >> 24);
|
||||
write(data >> 16);
|
||||
bitplanebuffer[buffer_index++] = data >> 8;
|
||||
bitplanebuffer[buffer_index++] = data >> 0;
|
||||
|
||||
if(buffer_index == 16) {
|
||||
for(unsigned i = 0; i < 16; i++) write(bitplanebuffer[i]);
|
||||
buffer_index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const uint8 SPC7110Decomp::evolution_table[53][4] = {
|
||||
//{ prob, nextlps, nextmps, toggle invert },
|
||||
|
||||
{ 0x5a, 1, 1, 1 },
|
||||
{ 0x25, 6, 2, 0 },
|
||||
{ 0x11, 8, 3, 0 },
|
||||
{ 0x08, 10, 4, 0 },
|
||||
{ 0x03, 12, 5, 0 },
|
||||
{ 0x01, 15, 5, 0 },
|
||||
|
||||
{ 0x5a, 7, 7, 1 },
|
||||
{ 0x3f, 19, 8, 0 },
|
||||
{ 0x2c, 21, 9, 0 },
|
||||
{ 0x20, 22, 10, 0 },
|
||||
{ 0x17, 23, 11, 0 },
|
||||
{ 0x11, 25, 12, 0 },
|
||||
{ 0x0c, 26, 13, 0 },
|
||||
{ 0x09, 28, 14, 0 },
|
||||
{ 0x07, 29, 15, 0 },
|
||||
{ 0x05, 31, 16, 0 },
|
||||
{ 0x04, 32, 17, 0 },
|
||||
{ 0x03, 34, 18, 0 },
|
||||
{ 0x02, 35, 5, 0 },
|
||||
|
||||
{ 0x5a, 20, 20, 1 },
|
||||
{ 0x48, 39, 21, 0 },
|
||||
{ 0x3a, 40, 22, 0 },
|
||||
{ 0x2e, 42, 23, 0 },
|
||||
{ 0x26, 44, 24, 0 },
|
||||
{ 0x1f, 45, 25, 0 },
|
||||
{ 0x19, 46, 26, 0 },
|
||||
{ 0x15, 25, 27, 0 },
|
||||
{ 0x11, 26, 28, 0 },
|
||||
{ 0x0e, 26, 29, 0 },
|
||||
{ 0x0b, 27, 30, 0 },
|
||||
{ 0x09, 28, 31, 0 },
|
||||
{ 0x08, 29, 32, 0 },
|
||||
{ 0x07, 30, 33, 0 },
|
||||
{ 0x05, 31, 34, 0 },
|
||||
{ 0x04, 33, 35, 0 },
|
||||
{ 0x04, 33, 36, 0 },
|
||||
{ 0x03, 34, 37, 0 },
|
||||
{ 0x02, 35, 38, 0 },
|
||||
{ 0x02, 36, 5, 0 },
|
||||
|
||||
{ 0x58, 39, 40, 1 },
|
||||
{ 0x4d, 47, 41, 0 },
|
||||
{ 0x43, 48, 42, 0 },
|
||||
{ 0x3b, 49, 43, 0 },
|
||||
{ 0x34, 50, 44, 0 },
|
||||
{ 0x2e, 51, 45, 0 },
|
||||
{ 0x29, 44, 46, 0 },
|
||||
{ 0x25, 45, 24, 0 },
|
||||
|
||||
{ 0x56, 47, 48, 1 },
|
||||
{ 0x4f, 47, 49, 0 },
|
||||
{ 0x47, 48, 50, 0 },
|
||||
{ 0x41, 49, 51, 0 },
|
||||
{ 0x3c, 50, 52, 0 },
|
||||
{ 0x37, 51, 43, 0 },
|
||||
};
|
||||
|
||||
const uint8 SPC7110Decomp::mode2_context_table[32][2] = {
|
||||
//{ next 0, next 1 },
|
||||
|
||||
{ 1, 2 },
|
||||
|
||||
{ 3, 8 },
|
||||
{ 13, 14 },
|
||||
|
||||
{ 15, 16 },
|
||||
{ 17, 18 },
|
||||
{ 19, 20 },
|
||||
{ 21, 22 },
|
||||
{ 23, 24 },
|
||||
{ 25, 26 },
|
||||
{ 25, 26 },
|
||||
{ 25, 26 },
|
||||
{ 25, 26 },
|
||||
{ 25, 26 },
|
||||
{ 27, 28 },
|
||||
{ 29, 30 },
|
||||
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
{ 31, 31 },
|
||||
|
||||
{ 31, 31 },
|
||||
};
|
||||
|
||||
uint8 SPC7110Decomp::probability (unsigned n) { return evolution_table[context[n].index][0]; }
|
||||
uint8 SPC7110Decomp::next_lps (unsigned n) { return evolution_table[context[n].index][1]; }
|
||||
uint8 SPC7110Decomp::next_mps (unsigned n) { return evolution_table[context[n].index][2]; }
|
||||
bool SPC7110Decomp::toggle_invert(unsigned n) { return evolution_table[context[n].index][3]; }
|
||||
|
||||
unsigned SPC7110Decomp::morton_2x8(unsigned data) {
|
||||
//reverse morton lookup: de-interleave two 8-bit values
|
||||
//15, 13, 11, 9, 7, 5, 3, 1 -> 15- 8
|
||||
//14, 12, 10, 8, 6, 4, 2, 0 -> 7- 0
|
||||
return morton16[0][(data >> 0) & 255] + morton16[1][(data >> 8) & 255];
|
||||
}
|
||||
|
||||
unsigned SPC7110Decomp::morton_4x8(unsigned data) {
|
||||
//reverse morton lookup: de-interleave four 8-bit values
|
||||
//31, 27, 23, 19, 15, 11, 7, 3 -> 31-24
|
||||
//30, 26, 22, 18, 14, 10, 6, 2 -> 23-16
|
||||
//29, 25, 21, 17, 13, 9, 5, 1 -> 15- 8
|
||||
//28, 24, 20, 16, 12, 8, 4, 0 -> 7- 0
|
||||
return morton32[0][(data >> 0) & 255] + morton32[1][(data >> 8) & 255]
|
||||
+ morton32[2][(data >> 16) & 255] + morton32[3][(data >> 24) & 255];
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void SPC7110Decomp::reset() {
|
||||
//mode 3 is invalid; this is treated as a special case to always return 0x00
|
||||
//set to mode 3 so that reading decomp port before starting first decomp will return 0x00
|
||||
decomp_mode = 3;
|
||||
|
||||
decomp_buffer_rdoffset = 0;
|
||||
decomp_buffer_wroffset = 0;
|
||||
decomp_buffer_length = 0;
|
||||
}
|
||||
|
||||
SPC7110Decomp::SPC7110Decomp() {
|
||||
decomp_buffer = new uint8_t[decomp_buffer_size];
|
||||
reset();
|
||||
|
||||
//initialize reverse morton lookup tables
|
||||
for(unsigned i = 0; i < 256; i++) {
|
||||
#define map(x, y) (((i >> x) & 1) << y)
|
||||
//2x8-bit
|
||||
morton16[1][i] = map(7, 15) + map(6, 7) + map(5, 14) + map(4, 6)
|
||||
+ map(3, 13) + map(2, 5) + map(1, 12) + map(0, 4);
|
||||
morton16[0][i] = map(7, 11) + map(6, 3) + map(5, 10) + map(4, 2)
|
||||
+ map(3, 9) + map(2, 1) + map(1, 8) + map(0, 0);
|
||||
//4x8-bit
|
||||
morton32[3][i] = map(7, 31) + map(6, 23) + map(5, 15) + map(4, 7)
|
||||
+ map(3, 30) + map(2, 22) + map(1, 14) + map(0, 6);
|
||||
morton32[2][i] = map(7, 29) + map(6, 21) + map(5, 13) + map(4, 5)
|
||||
+ map(3, 28) + map(2, 20) + map(1, 12) + map(0, 4);
|
||||
morton32[1][i] = map(7, 27) + map(6, 19) + map(5, 11) + map(4, 3)
|
||||
+ map(3, 26) + map(2, 18) + map(1, 10) + map(0, 2);
|
||||
morton32[0][i] = map(7, 25) + map(6, 17) + map(5, 9) + map(4, 1)
|
||||
+ map(3, 24) + map(2, 16) + map(1, 8) + map(0, 0);
|
||||
#undef map
|
||||
}
|
||||
}
|
||||
|
||||
SPC7110Decomp::~SPC7110Decomp() {
|
||||
delete[] decomp_buffer;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,45 @@
|
|||
class SPC7110Decomp {
|
||||
public:
|
||||
uint8 read();
|
||||
void init(unsigned mode, unsigned offset, unsigned index);
|
||||
void reset();
|
||||
|
||||
SPC7110Decomp();
|
||||
~SPC7110Decomp();
|
||||
|
||||
private:
|
||||
unsigned decomp_mode;
|
||||
unsigned decomp_offset;
|
||||
|
||||
//read() will spool chunks half the size of decomp_buffer_size
|
||||
enum { decomp_buffer_size = 64 }; //must be >= 64, and must be a power of two
|
||||
uint8 *decomp_buffer;
|
||||
unsigned decomp_buffer_rdoffset;
|
||||
unsigned decomp_buffer_wroffset;
|
||||
unsigned decomp_buffer_length;
|
||||
|
||||
void write(uint8 data);
|
||||
uint8 dataread();
|
||||
|
||||
void mode0(bool init);
|
||||
void mode1(bool init);
|
||||
void mode2(bool init);
|
||||
|
||||
static const uint8 evolution_table[53][4];
|
||||
static const uint8 mode2_context_table[32][2];
|
||||
|
||||
struct ContextState {
|
||||
uint8 index;
|
||||
uint8 invert;
|
||||
} context[32];
|
||||
|
||||
uint8 probability(unsigned n);
|
||||
uint8 next_lps(unsigned n);
|
||||
uint8 next_mps(unsigned n);
|
||||
bool toggle_invert(unsigned n);
|
||||
|
||||
unsigned morton16[2][256];
|
||||
unsigned morton32[4][256];
|
||||
unsigned morton_2x8(unsigned data);
|
||||
unsigned morton_4x8(unsigned data);
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
#include "../../base.h"
|
||||
#define SPC7110_CPP
|
||||
|
||||
#include "codec.cpp"
|
||||
#include "decomp.cpp"
|
||||
|
||||
const unsigned SPC7110::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||
|
||||
|
@ -30,9 +30,7 @@ void SPC7110::reset() {
|
|||
r480b = 0x00;
|
||||
r480c = 0x00;
|
||||
|
||||
memset(codec.output, 0, 65536);
|
||||
memset(codec.buffer, 0, 65536);
|
||||
decomp_offset = 0;
|
||||
decomp.reset();
|
||||
|
||||
r4811 = 0x00;
|
||||
r4812 = 0x00;
|
||||
|
@ -189,7 +187,7 @@ uint8 SPC7110::mmio_read(uint addr) {
|
|||
counter--;
|
||||
r4809 = counter;
|
||||
r480a = counter >> 8;
|
||||
return codec.output[(decomp_offset++) & 0xffff];
|
||||
return decomp.read();
|
||||
}
|
||||
case 0x4801: return r4801;
|
||||
case 0x4802: return r4802;
|
||||
|
@ -213,7 +211,7 @@ uint8 SPC7110::mmio_read(uint addr) {
|
|||
//==============
|
||||
|
||||
case 0x4810: {
|
||||
if(r481x != 0x1f) return 0x00;
|
||||
if(r481x != 0x07) return 0x00;
|
||||
|
||||
unsigned addr = data_pointer();
|
||||
unsigned adjust = data_adjust();
|
||||
|
@ -248,7 +246,7 @@ uint8 SPC7110::mmio_read(uint addr) {
|
|||
case 0x4817: return r4817;
|
||||
case 0x4818: return r4818;
|
||||
case 0x481a: {
|
||||
if(r481x != 0x1f) return 0x00;
|
||||
if(r481x != 0x07) return 0x00;
|
||||
|
||||
unsigned addr = data_pointer();
|
||||
unsigned adjust = data_adjust();
|
||||
|
@ -349,27 +347,7 @@ void SPC7110::mmio_write(uint addr, uint8 data) {
|
|||
+ (memory::cartrom.read(addr + 2) << 8)
|
||||
+ (memory::cartrom.read(addr + 3) << 0);
|
||||
|
||||
//this can technically be 65536, but it has never been observed higher than 32768 ...
|
||||
//really need a way to determine both compressed and decompressed lengths, though.
|
||||
static const unsigned max_length = 32768;
|
||||
|
||||
offset = datarom_addr(offset);
|
||||
for(unsigned i = 0; i < max_length; i++) codec.buffer[i] = memory::cartrom.read(offset + i);
|
||||
|
||||
#if 0
|
||||
printf("decompression: 4805=$%0.2x,4806=$%0.2x,4807=$%0.2x,4808=$%0.2x,480b=$%0.2x\n",
|
||||
r4805, r4806, r4807, r4808, r480b);
|
||||
printf("table=$%0.6x,index=%3d,length=%5d,mode=%d,offset=$%0.6x\n",
|
||||
table, r4804, length, mode, offset);
|
||||
#endif
|
||||
|
||||
switch(mode) {
|
||||
case 0: codec.decomp_mode0(max_length); break;
|
||||
case 1: codec.decomp_mode1(max_length); break;
|
||||
case 2: codec.decomp_mode2(max_length); break;
|
||||
}
|
||||
|
||||
decomp_offset = (r4805 + (r4806 << 8)) << mode;
|
||||
decomp.init(mode, offset, (r4805 + (r4806 << 8)) << mode);
|
||||
r480c = 0x80;
|
||||
} break;
|
||||
|
||||
|
@ -388,7 +366,6 @@ void SPC7110::mmio_write(uint addr, uint8 data) {
|
|||
case 0x4813: r4813 = data; r481x |= 0x04; break;
|
||||
case 0x4814: {
|
||||
r4814 = data;
|
||||
r481x |= 0x08;
|
||||
r4814_latch = true;
|
||||
if(!r4815_latch) break;
|
||||
if(!(r4818 & 2)) break;
|
||||
|
@ -406,7 +383,6 @@ void SPC7110::mmio_write(uint addr, uint8 data) {
|
|||
} break;
|
||||
case 0x4815: {
|
||||
r4815 = data;
|
||||
r481x |= 0x10;
|
||||
r4815_latch = true;
|
||||
if(!r4814_latch) break;
|
||||
if(!(r4818 & 2)) break;
|
||||
|
@ -425,7 +401,7 @@ void SPC7110::mmio_write(uint addr, uint8 data) {
|
|||
case 0x4816: r4816 = data; break;
|
||||
case 0x4817: r4817 = data; break;
|
||||
case 0x4818: {
|
||||
if(r481x != 0x1f) break;
|
||||
if(r481x != 0x07) break;
|
||||
|
||||
r4818 = data;
|
||||
r4814_latch = r4815_latch = false;
|
||||
|
@ -640,26 +616,18 @@ void SPC7110::mmio_write(uint addr, uint8 data) {
|
|||
}
|
||||
|
||||
uint8 SPC7110::read(uint addr) {
|
||||
//$[00-0f|80-8f]:[8000-ffff], $[c0-cf]:[0000-ffff] mapped directly to memory::cartrom
|
||||
|
||||
if((addr & 0xffe000) == 0x006000 || (addr & 0xffe000) == 0x306000) {
|
||||
//$[00|30]:[6000-7fff]
|
||||
return memory::cartram.read(addr & 0x1fff);
|
||||
}
|
||||
|
||||
if((addr & 0x708000) == 0x008000) {
|
||||
//$[00-0f|80-8f]:[8000-ffff]
|
||||
return memory::cartrom.read(addr & 0x0fffff);
|
||||
}
|
||||
|
||||
if((addr & 0xff0000) == 0x500000) {
|
||||
//$[50]:[0000-ffff]
|
||||
return mmio_read(0x4800);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0xc00000) {
|
||||
//$[c0-cf]:[0000-ffff]
|
||||
return memory::cartrom.read(addr & 0x0fffff);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0xd00000) {
|
||||
//$[d0-df]:[0000-ffff]
|
||||
return memory::cartrom.read(dx_offset + (addr & 0x0fffff));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*****
|
||||
* SPC7110 emulator - version 0.1 (2008-07-19)
|
||||
* SPC7110 emulator - version 0.03 (2008-08-10)
|
||||
* Copyright (c) 2008, byuu and neviksti
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
|
@ -15,7 +15,7 @@
|
|||
* or in connection with the use or performance of this software.
|
||||
*****/
|
||||
|
||||
#include "codec.h"
|
||||
#include "decomp.h"
|
||||
|
||||
class SPC7110 : public MMIO, public Memory {
|
||||
public:
|
||||
|
@ -41,6 +41,10 @@ public:
|
|||
uint8 read (uint addr);
|
||||
void write(uint addr, uint8 data);
|
||||
|
||||
//spc7110decomp
|
||||
void decomp_init();
|
||||
uint8 decomp_read();
|
||||
|
||||
SPC7110();
|
||||
|
||||
private:
|
||||
|
@ -60,8 +64,7 @@ private:
|
|||
uint8 r480b; //decompression control register
|
||||
uint8 r480c; //decompression status
|
||||
|
||||
SPC7110Codec codec;
|
||||
uint16 decomp_offset;
|
||||
SPC7110Decomp decomp;
|
||||
|
||||
//==============
|
||||
//data port unit
|
||||
|
|
|
@ -58,15 +58,15 @@ string_setting Path::cheat(config(), "path.cheat",
|
|||
string_setting Path::bsx(config(), "path.bsx", "", "");
|
||||
string_setting Path::st(config(), "path.st", "", "");
|
||||
|
||||
integral_setting SNES::controller_port0(config(), "snes.controller_port_1",
|
||||
"Controller attached to SNES port 1", integral_setting::decimal, ::SNES::Input::DeviceIDJoypad1);
|
||||
integral_setting SNES::controller_port1(config(), "snes.controller_port_2",
|
||||
"Controller attached to SNES port 2", integral_setting::decimal, ::SNES::Input::DeviceIDJoypad2);
|
||||
integral_setting SNES::controller_port1(config(), "snes.controller_port1",
|
||||
"Controller attached to SNES port 1", integral_setting::decimal, ::SNES::Input::DeviceJoypad);
|
||||
integral_setting SNES::controller_port2(config(), "snes.controller_port2",
|
||||
"Controller attached to SNES port 2", integral_setting::decimal, ::SNES::Input::DeviceJoypad);
|
||||
|
||||
integral_setting CPU::ntsc_clock_rate(config(), "cpu.ntsc_clock_rate",
|
||||
"NTSC S-CPU clock rate (in hz)", integral_setting::decimal, 21477272);
|
||||
integral_setting CPU::pal_clock_rate(config(), "cpu.pal_clock_rate",
|
||||
"PAL S-CPU clock rate (in hz)", integral_setting::decimal, 21281370);
|
||||
"PAL S-CPU clock rate (in hz)", integral_setting::decimal, 21281370);
|
||||
integral_setting CPU::wram_init_value(config(), "cpu.wram_init_value",
|
||||
"Value to initialize 128k WRAM to upon power cycle.\n"
|
||||
"Note that on real hardware, this value is undefined; meaning it can vary\n"
|
||||
|
@ -81,13 +81,10 @@ integral_setting CPU::wram_init_value(config(), "cpu.wram_init_value",
|
|||
"which do not properly initialize WRAM upon power cycle.\n",
|
||||
integral_setting::hex, 0x55);
|
||||
|
||||
integral_setting CPU::hdma_enable("cpu.hdma_enable",
|
||||
"Enable HDMA effects", integral_setting::boolean, true);
|
||||
|
||||
integral_setting SMP::ntsc_clock_rate(config(), "smp.ntsc_clock_rate",
|
||||
"NTSC S-SMP clock rate (in hz)", integral_setting::decimal, 24606720);
|
||||
"NTSC S-SMP clock rate (in hz)", integral_setting::decimal, 32040 * 768);
|
||||
integral_setting SMP::pal_clock_rate(config(), "smp.pal_clock_rate",
|
||||
"PAL S-SMP clock rate (in hz)", integral_setting::decimal, 24606720);
|
||||
"PAL S-SMP clock rate (in hz)", integral_setting::decimal, 32040 * 768);
|
||||
|
||||
integral_setting PPU::Hack::render_scanline_position(config(), "ppu.hack.render_scanline_position",
|
||||
"Approximate HCLOCK position to render at for scanline-based renderers",
|
||||
|
|
|
@ -15,14 +15,13 @@ extern struct Path {
|
|||
} path;
|
||||
|
||||
extern struct SNES {
|
||||
static integral_setting controller_port0;
|
||||
static integral_setting controller_port1;
|
||||
static integral_setting controller_port2;
|
||||
} snes;
|
||||
|
||||
extern struct CPU {
|
||||
static integral_setting ntsc_clock_rate, pal_clock_rate;
|
||||
static integral_setting wram_init_value;
|
||||
static integral_setting hdma_enable;
|
||||
} cpu;
|
||||
|
||||
extern struct SMP {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "dcpu.cpp"
|
||||
|
||||
CPU::CPU() {
|
||||
cpu_version = 1;
|
||||
cpu_version = 2;
|
||||
}
|
||||
|
||||
CPU::~CPU() {
|
||||
|
|
|
@ -15,7 +15,8 @@ public:
|
|||
virtual uint16 hcounter() = 0;
|
||||
virtual uint16 hdot() = 0;
|
||||
|
||||
virtual uint8 pio_status() = 0;
|
||||
virtual uint8 pio() = 0;
|
||||
virtual bool joylatch() = 0;
|
||||
virtual uint8 port_read(uint8 port) = 0;
|
||||
virtual void port_write(uint8 port, uint8 value) = 0;
|
||||
|
||||
|
|
|
@ -5,52 +5,51 @@ void sCPU::dma_add_clocks(uint clocks) {
|
|||
add_clocks(clocks);
|
||||
}
|
||||
|
||||
/*****
|
||||
* used by both DMA and HDMA
|
||||
*
|
||||
* DMA address bus A cannot read from or write to the following addresses :
|
||||
* $[00-3f|80-bf]:43[00-7f] <DMA control registers>
|
||||
* $[00-3f|80-bf]:420b <DMA enable register>
|
||||
* $[00-3f|80-bf]:420c <HDMA enable register>
|
||||
*
|
||||
* WRAM<>WRAM transfers via $2180 are also illegal
|
||||
*****/
|
||||
bool sCPU::dma_addr_valid(uint32 abus) {
|
||||
//reads from B-bus or S-CPU registers are invalid
|
||||
if((abus & 0x40ff00) == 0x2100) return false; //$[00-3f|80-bf]:[2100-21ff]
|
||||
if((abus & 0x40fe00) == 0x4000) return false; //$[00-3f|80-bf]:[4000-41ff]
|
||||
if((abus & 0x40ffe0) == 0x4200) return false; //$[00-3f|80-bf]:[4200-421f]
|
||||
if((abus & 0x40ff80) == 0x4300) return false; //$[00-3f|80-bf]:[4300-437f]
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8 sCPU::dma_read(uint32 abus) {
|
||||
if(dma_addr_valid(abus) == false) return 0x00; //does not return S-CPU MDR
|
||||
return bus.read(abus);
|
||||
}
|
||||
|
||||
void sCPU::dma_transfer(bool direction, uint8 bbus, uint32 abus) {
|
||||
if(direction == 0) {
|
||||
//a->b transfer (to $21xx)
|
||||
if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) {
|
||||
//illegal WRAM->WRAM transfer
|
||||
//illegal WRAM->WRAM transfer (bus conflict)
|
||||
//read most likely occurs; no write occurs
|
||||
//read is irrelevant, as it has no observable effect on emulation
|
||||
} else if((abus & 0x40ff00) == 0x2100 || (abus & 0x40ff80) == 0x4300
|
||||
|| (abus & 0x40ffff) == 0x420b || (abus & 0x40ffff) == 0x420c) {
|
||||
//illegal register access
|
||||
bus.write(0x2100 | bbus, regs.mdr); //TODO: verify if MDR is written here
|
||||
//read is irrelevent, as it cannot be observed by software
|
||||
dma_add_clocks(8);
|
||||
} else {
|
||||
//valid transfer
|
||||
bus.write(0x2100 | bbus, bus.read(abus));
|
||||
dma_add_clocks(4);
|
||||
uint8 data = dma_read(abus);
|
||||
dma_add_clocks(4);
|
||||
bus.write(0x2100 | bbus, data);
|
||||
}
|
||||
} else {
|
||||
//b->a transfer (from $21xx)
|
||||
if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) {
|
||||
//illegal WRAM->WRAM transfer
|
||||
//illegal WRAM->WRAM transfer (bus conflict)
|
||||
//no read occurs; write does occur
|
||||
//does not write MDR as expected
|
||||
//TODO: 0x00 was observed on hardware; verify if other values are possible
|
||||
bus.write(abus, 0x00);
|
||||
} else if((abus & 0x40ff00) == 0x2100 || (abus & 0x40ff80) == 0x4300
|
||||
|| (abus & 0x40ffff) == 0x420b || (abus & 0x40ffff) == 0x420c) {
|
||||
//illegal register access
|
||||
bus.write(abus, regs.mdr); //TODO: verify if MDR is written here
|
||||
dma_add_clocks(8);
|
||||
bus.write(abus, 0x00); //does not write S-CPU MDR
|
||||
} else {
|
||||
//valid transfer
|
||||
bus.write(abus, bus.read(0x2100 | bbus));
|
||||
dma_add_clocks(4);
|
||||
uint8 data = bus.read(0x2100 | bbus);
|
||||
dma_add_clocks(4);
|
||||
if(dma_addr_valid(abus) == true) {
|
||||
bus.write(abus, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//each byte *always* consumes 8 clocks, even if transfer is invalid and no read and/or write occurs
|
||||
dma_add_clocks(8);
|
||||
cycle_edge();
|
||||
}
|
||||
|
||||
|
@ -120,10 +119,22 @@ inline void sCPU::dma_write(uint8 i, uint8 index) {
|
|||
}
|
||||
}
|
||||
|
||||
uint8 sCPU::dma_enabled_channels() {
|
||||
uint8 r = 0;
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
if(channel[i].dma_enabled) r++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void sCPU::dma_run() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
dma_add_clocks(8);
|
||||
cycle_edge();
|
||||
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
if(channel[i].dma_enabled == false) continue;
|
||||
dma_add_clocks(8);
|
||||
cycle_edge();
|
||||
|
||||
if(cartridge.info.sdd1 == true) {
|
||||
sdd1.dma_begin(i, (channel[i].srcbank << 16) | (channel[i].srcaddr), channel[i].xfersize);
|
||||
|
@ -138,7 +149,7 @@ void sCPU::dma_run() {
|
|||
channel[i].xfersize, channel[i].xfersize ? channel[i].xfersize : 65536);
|
||||
}
|
||||
|
||||
uint index = 0;
|
||||
unsigned index = 0;
|
||||
do {
|
||||
dma_write(i, dma_bbus(i, index++));
|
||||
} while(channel[i].dma_enabled && channel[i].xfersize);
|
||||
|
@ -158,7 +169,7 @@ inline bool sCPU::hdma_active(uint8 i) {
|
|||
}
|
||||
|
||||
inline bool sCPU::hdma_active_after(uint8 i) {
|
||||
for(int n = i + 1; n < 8; n++) {
|
||||
for(unsigned n = i + 1; n < 8; n++) {
|
||||
if(hdma_active(n) == true) return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -166,7 +177,7 @@ inline bool sCPU::hdma_active_after(uint8 i) {
|
|||
|
||||
inline uint8 sCPU::hdma_enabled_channels() {
|
||||
uint8 r = 0;
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
if(channel[i].hdma_enabled) r++;
|
||||
}
|
||||
return r;
|
||||
|
@ -174,55 +185,57 @@ inline uint8 sCPU::hdma_enabled_channels() {
|
|||
|
||||
inline uint8 sCPU::hdma_active_channels() {
|
||||
uint8 r = 0;
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
if(hdma_active(i) == true) r++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void sCPU::hdma_update(uint8 i) {
|
||||
channel[i].hdma_line_counter = bus.read(hdma_addr(i));
|
||||
channel[i].hdma_line_counter = dma_read(hdma_addr(i));
|
||||
dma_add_clocks(8);
|
||||
|
||||
channel[i].hdma_completed = (channel[i].hdma_line_counter == 0);
|
||||
channel[i].hdma_do_transfer = !channel[i].hdma_completed;
|
||||
|
||||
if(channel[i].hdma_indirect) {
|
||||
channel[i].hdma_iaddr = bus.read(hdma_addr(i)) << 8;
|
||||
channel[i].hdma_iaddr = dma_read(hdma_addr(i)) << 8;
|
||||
dma_add_clocks(8);
|
||||
|
||||
if(!channel[i].hdma_completed || hdma_active_after(i)) {
|
||||
channel[i].hdma_iaddr >>= 8;
|
||||
channel[i].hdma_iaddr |= bus.read(hdma_addr(i)) << 8;
|
||||
channel[i].hdma_iaddr |= dma_read(hdma_addr(i)) << 8;
|
||||
dma_add_clocks(8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::hdma_run() {
|
||||
static uint8 hdma_xferlen[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
|
||||
for(int i = 0; i < 8; i++) {
|
||||
dma_add_clocks(8);
|
||||
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
if(hdma_active(i) == false) continue;
|
||||
channel[i].dma_enabled = false; //HDMA run during DMA will stop DMA mid-transfer
|
||||
dma_add_clocks(8);
|
||||
|
||||
if(channel[i].hdma_do_transfer) {
|
||||
int xferlen = hdma_xferlen[channel[i].xfermode];
|
||||
for(int index = 0; index < xferlen; index++) {
|
||||
if(bool(config::cpu.hdma_enable) == true) {
|
||||
dma_transfer(channel[i].direction, dma_bbus(i, index),
|
||||
!channel[i].hdma_indirect ? hdma_addr(i) : hdma_iaddr(i));
|
||||
} else {
|
||||
dma_add_clocks(8);
|
||||
cycle_edge();
|
||||
}
|
||||
static const unsigned transfer_length[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
|
||||
unsigned length = transfer_length[channel[i].xfermode];
|
||||
for(unsigned index = 0; index < length; index++) {
|
||||
unsigned addr = !channel[i].hdma_indirect ? hdma_addr(i) : hdma_iaddr(i);
|
||||
dma_transfer(channel[i].direction, dma_bbus(i, index), addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
if(hdma_active(i) == false) continue;
|
||||
|
||||
channel[i].hdma_line_counter--;
|
||||
channel[i].hdma_do_transfer = bool(channel[i].hdma_line_counter & 0x80);
|
||||
if((channel[i].hdma_line_counter & 0x7f) == 0) {
|
||||
hdma_update(i);
|
||||
} else {
|
||||
dma_add_clocks(8);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,15 +243,17 @@ void sCPU::hdma_run() {
|
|||
}
|
||||
|
||||
void sCPU::hdma_init_reset() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
channel[i].hdma_completed = false;
|
||||
channel[i].hdma_do_transfer = false;
|
||||
}
|
||||
}
|
||||
|
||||
void sCPU::hdma_init() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(!channel[i].hdma_enabled)continue;
|
||||
dma_add_clocks(8);
|
||||
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
if(!channel[i].hdma_enabled) continue;
|
||||
channel[i].dma_enabled = false; //HDMA init during DMA will stop DMA mid-transfer
|
||||
|
||||
channel[i].hdma_addr = channel[i].srcaddr;
|
||||
|
@ -253,7 +268,7 @@ void sCPU::hdma_init() {
|
|||
*****/
|
||||
|
||||
void sCPU::dma_power() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
channel[i].dmap = 0xff;
|
||||
channel[i].direction = 1;
|
||||
channel[i].hdma_indirect = true;
|
||||
|
@ -277,7 +292,7 @@ void sCPU::dma_power() {
|
|||
}
|
||||
|
||||
void sCPU::dma_reset() {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
channel[i].dma_enabled = false;
|
||||
channel[i].hdma_enabled = false;
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@
|
|||
} channel[8];
|
||||
|
||||
void dma_add_clocks(uint clocks);
|
||||
bool dma_addr_valid(uint32 abus);
|
||||
uint8 dma_read(uint32 abus);
|
||||
void dma_transfer(bool direction, uint8 bbus, uint32 abus);
|
||||
|
||||
uint8 dma_bbus(uint8 i, uint8 index);
|
||||
|
@ -56,6 +58,7 @@
|
|||
void dma_transfertobusb(uint8 i, uint8 bbus);
|
||||
void dma_transfertobusa(uint8 i, uint8 bbus);
|
||||
void dma_write(uint8 i, uint8 index);
|
||||
uint8 dma_enabled_channels();
|
||||
void dma_run();
|
||||
|
||||
bool hdma_active(uint8 i);
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
#ifdef SCPU_CPP
|
||||
|
||||
uint8 sCPU::pio_status() {
|
||||
return status.pio;
|
||||
}
|
||||
uint8 sCPU::pio() { return status.pio; }
|
||||
bool sCPU::joylatch() { return status.joypad_strobe_latch; }
|
||||
|
||||
//WMDATA
|
||||
uint8 sCPU::mmio_r2180() {
|
||||
|
@ -55,7 +54,7 @@ void sCPU::mmio_w4016(uint8 data) {
|
|||
//realtime or buffered status of joypadN.b
|
||||
uint8 sCPU::mmio_r4016() {
|
||||
uint8 r = regs.mdr & 0xfc;
|
||||
r |= (uint8)snes.input.port_read(0);
|
||||
r |= snes.input.port_read(0) & 3;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -65,7 +64,7 @@ uint8 sCPU::mmio_r4016() {
|
|||
//1-0 = Joypad serial data
|
||||
uint8 sCPU::mmio_r4017() {
|
||||
uint8 r = (regs.mdr & 0xe0) | 0x1c;
|
||||
r |= (uint8)snes.input.port_read(1);
|
||||
r |= snes.input.port_read(1) & 3;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -143,19 +142,16 @@ void sCPU::mmio_w420a(uint8 data) {
|
|||
|
||||
//DMAEN
|
||||
void sCPU::mmio_w420b(uint8 data) {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
channel[i].dma_enabled = !!(data & (1 << i));
|
||||
}
|
||||
if(data) {
|
||||
status.dma_state = DMASTATE_DMASYNC;
|
||||
status.dma_pending = true;
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
channel[i].dma_enabled = data & (1 << i);
|
||||
}
|
||||
if(data) status.dma_pending = true;
|
||||
}
|
||||
|
||||
//HDMAEN
|
||||
void sCPU::mmio_w420c(uint8 data) {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
channel[i].hdma_enabled = !!(data & (1 << i));
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
channel[i].hdma_enabled = data & (1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
uint8 mmio_read(uint addr);
|
||||
void mmio_write(uint addr, uint8 data);
|
||||
|
||||
uint8 pio_status();
|
||||
uint8 pio();
|
||||
bool joylatch();
|
||||
|
||||
uint8 mmio_r2180();
|
||||
uint8 mmio_r4016();
|
||||
|
|
|
@ -15,13 +15,13 @@ public:
|
|||
} event;
|
||||
|
||||
struct {
|
||||
uint nmi_hold;
|
||||
uint irq_hold;
|
||||
unsigned nmi_hold;
|
||||
unsigned irq_hold;
|
||||
|
||||
uint nmi_fire;
|
||||
uint irq_fire;
|
||||
uint irq_delay;
|
||||
uint hw_math;
|
||||
unsigned nmi_fire;
|
||||
unsigned irq_fire;
|
||||
unsigned irq_delay;
|
||||
unsigned hw_math;
|
||||
|
||||
alwaysinline void set(uint &ctr, uint clocks) {
|
||||
if(clocks >= ctr) { ctr = clocks; }
|
||||
|
@ -36,55 +36,50 @@ public:
|
|||
}
|
||||
} counter;
|
||||
|
||||
enum {
|
||||
DMASTATE_INACTIVE,
|
||||
DMASTATE_DMASYNC,
|
||||
DMASTATE_RUN,
|
||||
DMASTATE_CPUSYNC,
|
||||
};
|
||||
enum DMA_State { DMA_Inactive, DMA_Run, DMA_CPUsync };
|
||||
|
||||
struct {
|
||||
//core
|
||||
uint8 opcode;
|
||||
bool in_opcode;
|
||||
uint8 opcode;
|
||||
bool in_opcode;
|
||||
|
||||
uint clock_count;
|
||||
unsigned clock_count;
|
||||
|
||||
//timing
|
||||
uint16 vcounter, hcounter;
|
||||
uint16 field_lines, line_clocks;
|
||||
|
||||
bool line_rendered;
|
||||
bool line_rendered;
|
||||
uint16 line_render_position;
|
||||
|
||||
bool dram_refreshed;
|
||||
bool dram_refreshed;
|
||||
uint16 dram_refresh_position;
|
||||
|
||||
bool hdmainit_triggered;
|
||||
bool hdmainit_triggered;
|
||||
uint16 hdmainit_trigger_position;
|
||||
|
||||
bool hdma_triggered;
|
||||
bool hdma_triggered;
|
||||
|
||||
uint16 irq_delay;
|
||||
|
||||
bool nmi_valid;
|
||||
bool nmi_line;
|
||||
bool nmi_transition;
|
||||
bool nmi_pending;
|
||||
bool nmi_valid;
|
||||
bool nmi_line;
|
||||
bool nmi_transition;
|
||||
bool nmi_pending;
|
||||
|
||||
uint16 virq_trigger_pos, hirq_trigger_pos;
|
||||
bool irq_valid;
|
||||
bool irq_line;
|
||||
bool irq_transition;
|
||||
bool irq_pending;
|
||||
bool irq_valid;
|
||||
bool irq_line;
|
||||
bool irq_transition;
|
||||
bool irq_pending;
|
||||
|
||||
//dma
|
||||
uint dma_counter;
|
||||
uint dma_clocks;
|
||||
uint dma_state;
|
||||
bool dma_pending;
|
||||
bool hdma_pending;
|
||||
bool hdmainit_pending;
|
||||
unsigned dma_counter;
|
||||
unsigned dma_clocks;
|
||||
bool dma_pending;
|
||||
bool hdma_pending;
|
||||
bool hdma_mode; //0 = init, 1 = run
|
||||
DMA_State dma_state;
|
||||
|
||||
//mmio
|
||||
|
||||
|
@ -92,20 +87,20 @@ public:
|
|||
uint32 wram_addr;
|
||||
|
||||
//$4016-$4017
|
||||
bool joypad_strobe_latch;
|
||||
bool joypad_strobe_latch;
|
||||
uint32 joypad1_bits;
|
||||
uint32 joypad2_bits;
|
||||
|
||||
//$4200
|
||||
bool nmi_enabled;
|
||||
bool hirq_enabled, virq_enabled;
|
||||
bool auto_joypad_poll;
|
||||
bool nmi_enabled;
|
||||
bool hirq_enabled, virq_enabled;
|
||||
bool auto_joypad_poll;
|
||||
|
||||
//$4201
|
||||
uint8 pio;
|
||||
uint8 pio;
|
||||
|
||||
//$4202-$4203
|
||||
uint8 mul_a, mul_b;
|
||||
uint8 mul_a, mul_b;
|
||||
|
||||
//$4204-$4206
|
||||
uint16 div_a;
|
||||
|
@ -119,10 +114,10 @@ public:
|
|||
uint16 r4216;
|
||||
|
||||
//$4218-$421f
|
||||
uint8 joy1l, joy1h;
|
||||
uint8 joy2l, joy2h;
|
||||
uint8 joy3l, joy3h;
|
||||
uint8 joy4l, joy4h;
|
||||
uint8 joy1l, joy1h;
|
||||
uint8 joy2l, joy2h;
|
||||
uint8 joy3l, joy3h;
|
||||
uint8 joy4l, joy4h;
|
||||
} status;
|
||||
|
||||
void power();
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
#ifdef SCPU_CPP
|
||||
|
||||
void sCPU::run_auto_joypad_poll() {
|
||||
uint16_t joy1 = 0, joy2 = 0;
|
||||
uint16 joy1 = 0, joy2 = 0, joy3 = 0, joy4 = 0;
|
||||
for(unsigned i = 0; i < 16; i++) {
|
||||
joy1 |= (uint16_t)snes.input.port_read(0) ? (0x8000 >> i) : 0;
|
||||
joy2 |= (uint16_t)snes.input.port_read(1) ? (0x8000 >> i) : 0;
|
||||
uint8 port0 = snes.input.port_read(0);
|
||||
uint8 port1 = snes.input.port_read(1);
|
||||
|
||||
joy1 |= (port0 & 1) ? (0x8000 >> i) : 0;
|
||||
joy2 |= (port1 & 1) ? (0x8000 >> i) : 0;
|
||||
joy3 |= (port0 & 2) ? (0x8000 >> i) : 0;
|
||||
joy4 |= (port1 & 2) ? (0x8000 >> i) : 0;
|
||||
}
|
||||
|
||||
status.joy1l = joy1;
|
||||
|
@ -13,11 +18,11 @@ void sCPU::run_auto_joypad_poll() {
|
|||
status.joy2l = joy2;
|
||||
status.joy2h = joy2 >> 8;
|
||||
|
||||
status.joy3l = 0x00;
|
||||
status.joy3h = 0x00;
|
||||
status.joy3l = joy3;
|
||||
status.joy3h = joy3 >> 8;
|
||||
|
||||
status.joy4l = 0x00;
|
||||
status.joy4h = 0x00;
|
||||
status.joy4l = joy4;
|
||||
status.joy4h = joy4 >> 8;
|
||||
}
|
||||
|
||||
#endif //ifdef SCPU_CPP
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
#ifdef SCPU_CPP
|
||||
|
||||
#define ntsc_color_burst_phase_shift_scanline() ( \
|
||||
snes.region() == SNES::NTSC && status.vcounter == 240 && \
|
||||
ppu.interlace() == false && ppu.field() == 1 \
|
||||
)
|
||||
|
||||
#include "irq.cpp"
|
||||
#include "joypad.cpp"
|
||||
|
@ -22,6 +17,12 @@ uint sCPU::dma_counter() { return (status.dma_counter + status.hcounter) & 7; }
|
|||
* Dot 323 range = { 1292, 1294, 1296 }
|
||||
* Dot 327 range = { 1310, 1312, 1314 }
|
||||
*****/
|
||||
|
||||
#define ntsc_color_burst_phase_shift_scanline() ( \
|
||||
snes.region() == SNES::NTSC && status.vcounter == 240 && \
|
||||
ppu.interlace() == false && ppu.field() == 1 \
|
||||
)
|
||||
|
||||
uint16 sCPU::hdot() {
|
||||
if(ntsc_color_burst_phase_shift_scanline() == true) return (status.hcounter >> 2);
|
||||
return (status.hcounter - ((status.hcounter > 1292) << 1) - ((status.hcounter > 1310) << 1)) >> 2;
|
||||
|
@ -55,15 +56,7 @@ void sCPU::scanline() {
|
|||
|
||||
//dram refresh occurs once every scanline
|
||||
status.dram_refreshed = false;
|
||||
if(cpu_version == 2) {
|
||||
if(ntsc_color_burst_phase_shift_scanline() == false) {
|
||||
if(status.dram_refresh_position == 534) {
|
||||
status.dram_refresh_position = 538;
|
||||
} else {
|
||||
status.dram_refresh_position = 534;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(cpu_version == 2) status.dram_refresh_position = 530 + 8 - dma_counter();
|
||||
|
||||
//hdma triggers once every visible scanline
|
||||
status.line_rendered = false;
|
||||
|
@ -86,8 +79,7 @@ void sCPU::frame() {
|
|||
|
||||
status.vcounter = 0;
|
||||
status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1;
|
||||
//interlaced even fields have one extra scanline
|
||||
//(263+262=525 NTSC, 313+312=625 PAL)
|
||||
//interlaced even fields have one extra scanline (263+262=525 NTSC, 313+312=625 PAL)
|
||||
if(ppu.interlace() == true && ppu.field() == 0) status.field_lines++;
|
||||
|
||||
status.hdmainit_triggered = false;
|
||||
|
@ -101,20 +93,19 @@ void sCPU::frame() {
|
|||
/*****
|
||||
* precycle_edge()
|
||||
*
|
||||
* Used for DMA/HDMA bus synchronization
|
||||
* Used for H/DMA bus synchronization
|
||||
*****/
|
||||
alwaysinline void sCPU::precycle_edge() {
|
||||
if(status.dma_state == DMASTATE_CPUSYNC) {
|
||||
status.dma_state = DMASTATE_INACTIVE;
|
||||
uint n = status.clock_count - (status.dma_clocks % status.clock_count);
|
||||
add_clocks(n ? n : status.clock_count);
|
||||
void sCPU::precycle_edge() {
|
||||
if(status.dma_state == DMA_CPUsync) {
|
||||
add_clocks(status.clock_count - (status.dma_clocks % status.clock_count));
|
||||
status.dma_state = DMA_Inactive;
|
||||
}
|
||||
}
|
||||
|
||||
/*****
|
||||
* cycle_edge()
|
||||
*
|
||||
* Used to test for HDMA, which can trigger on the edge of every opcode cycle.
|
||||
* Used to test for H/DMA, which can trigger on the edge of every opcode cycle.
|
||||
*****/
|
||||
void sCPU::cycle_edge() {
|
||||
if(status.line_rendered == false) {
|
||||
|
@ -129,47 +120,55 @@ void sCPU::cycle_edge() {
|
|||
status.hdmainit_triggered = true;
|
||||
hdma_init_reset();
|
||||
if(hdma_enabled_channels()) {
|
||||
if(status.dma_state == DMASTATE_INACTIVE) {
|
||||
status.dma_state = DMASTATE_DMASYNC;
|
||||
status.hdmainit_pending = true;
|
||||
} else {
|
||||
hdma_init();
|
||||
}
|
||||
status.hdma_pending = true;
|
||||
status.hdma_mode = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(status.hdma_triggered == false) {
|
||||
if(status.hcounter >= 1106) {
|
||||
if(status.hcounter >= 1104) {
|
||||
status.hdma_triggered = true;
|
||||
if(hdma_active_channels()) {
|
||||
if(status.dma_state == DMASTATE_INACTIVE) {
|
||||
status.dma_state = DMASTATE_DMASYNC;
|
||||
status.hdma_pending = true;
|
||||
} else {
|
||||
hdma_run();
|
||||
}
|
||||
status.hdma_pending = true;
|
||||
status.hdma_mode = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(status.dma_state) {
|
||||
case DMASTATE_INACTIVE: break;
|
||||
|
||||
case DMASTATE_DMASYNC: {
|
||||
status.dma_state = DMASTATE_RUN;
|
||||
} break;
|
||||
|
||||
case DMASTATE_RUN: {
|
||||
status.dma_state = DMASTATE_CPUSYNC;
|
||||
status.dma_clocks = 8 - dma_counter() + 8;
|
||||
add_clocks(status.dma_clocks);
|
||||
|
||||
if(status.hdmainit_pending) { hdma_init(); status.hdmainit_pending = false; }
|
||||
if(status.hdma_pending) { hdma_run(); status.hdma_pending = false; }
|
||||
if(status.dma_pending) { dma_run(); status.dma_pending = false; }
|
||||
|
||||
} break;
|
||||
}
|
||||
|
||||
//H/DMA pending && DMA inactive?
|
||||
//.. Run one full CPU cycle
|
||||
//.. HDMA pending && HDMA enabled ? DMA sync + HDMA run
|
||||
//.. DMA pending && DMA enabled ? DMA sync + DMA run
|
||||
//.... HDMA during DMA && HDMA enabled ? DMA sync + HDMA run
|
||||
//.. Run one bus CPU cycle
|
||||
//.. CPU sync
|
||||
|
||||
if(status.dma_state == DMA_Run) {
|
||||
if(status.hdma_pending) {
|
||||
status.hdma_pending = false;
|
||||
if(hdma_enabled_channels()) {
|
||||
dma_add_clocks(8 - dma_counter()); //DMA sync
|
||||
status.hdma_mode == 0 ? hdma_init() : hdma_run();
|
||||
status.dma_state = DMA_CPUsync;
|
||||
}
|
||||
}
|
||||
|
||||
if(status.dma_pending) {
|
||||
status.dma_pending = false;
|
||||
if(dma_enabled_channels()) {
|
||||
dma_add_clocks(8 - dma_counter()); //DMA sync
|
||||
dma_run();
|
||||
status.dma_state = DMA_CPUsync;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(status.dma_state == DMA_Inactive) {
|
||||
if(status.dma_pending || status.hdma_pending) {
|
||||
status.dma_clocks = 0;
|
||||
status.dma_state = DMA_Run;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,7 +211,7 @@ void sCPU::timing_reset() {
|
|||
status.line_clocks = 1364;
|
||||
|
||||
status.line_rendered = false;
|
||||
status.line_render_position = min(1112U, (uint)config::ppu.hack.render_scanline_position);
|
||||
status.line_render_position = min(1112U, (unsigned)config::ppu.hack.render_scanline_position);
|
||||
|
||||
status.dram_refreshed = false;
|
||||
status.dram_refresh_position = (cpu_version == 1) ? 530 : 538;
|
||||
|
@ -236,11 +235,12 @@ void sCPU::timing_reset() {
|
|||
|
||||
update_interrupts();
|
||||
|
||||
status.dma_counter = 0;
|
||||
status.dma_state = DMASTATE_INACTIVE;
|
||||
status.dma_pending = false;
|
||||
status.hdma_pending = false;
|
||||
status.hdmainit_pending = false;
|
||||
status.dma_counter = 0;
|
||||
status.dma_clocks = 0;
|
||||
status.dma_pending = false;
|
||||
status.hdma_pending = false;
|
||||
status.hdma_mode = 0;
|
||||
status.dma_state = DMA_Inactive;
|
||||
|
||||
history.reset();
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
void sDSP::brr_decode(voice_t &v) {
|
||||
//state.t_brr_byte = ram[v.brr_addr + v.brr_offset] cached from previous clock cycle
|
||||
int nybbles = (state.t_brr_byte << 8) + ram[(uint16)(v.brr_addr + v.brr_offset + 1)];
|
||||
int nybbles = (state.t_brr_byte << 8) + memory::apuram.read(uint16(v.brr_addr + v.brr_offset + 1));
|
||||
|
||||
const int filter = (state.t_brr_header >> 2) & 3;
|
||||
const int scale = (state.t_brr_header >> 4);
|
||||
|
|
|
@ -12,17 +12,19 @@ int sDSP::echo_output(bool channel) {
|
|||
}
|
||||
|
||||
void sDSP::echo_read(bool channel) {
|
||||
uint8 *in = &ram[state.t_echo_ptr + channel * 2];
|
||||
int s = (int16)((in[1] << 8) + in[0]);
|
||||
unsigned addr = state.t_echo_ptr + channel * 2;
|
||||
uint8 lo = memory::apuram.read(uint16(addr + 0));
|
||||
uint8 hi = memory::apuram.read(uint16(addr + 1));
|
||||
int s = (int16)((hi << 8) + lo);
|
||||
state.echo_hist[channel].write(state.echo_hist_pos, s >> 1);
|
||||
}
|
||||
|
||||
void sDSP::echo_write(bool channel) {
|
||||
if(!(state.t_echo_disabled & 0x20)) {
|
||||
uint8 *out = &ram[state.t_echo_ptr + channel * 2];
|
||||
unsigned addr = state.t_echo_ptr + channel * 2;
|
||||
int s = state.t_echo_out[channel];
|
||||
out[0] = (uint8)(s);
|
||||
out[1] = (uint8)(s >> 8);
|
||||
memory::apuram.write(uint16(addr + 0), (uint8)(s >> 0));
|
||||
memory::apuram.write(uint16(addr + 1), (uint8)(s >> 8));
|
||||
}
|
||||
|
||||
state.t_echo_out[channel] = 0;
|
||||
|
|
|
@ -249,8 +249,6 @@ void sDSP::write(uint8 addr, uint8 data) {
|
|||
/* initialization */
|
||||
|
||||
void sDSP::power() {
|
||||
ram = (uint8*)smp.get_spcram_handle(); //TODO: move to sMemory
|
||||
|
||||
memset(&state.regs, 0, sizeof state.regs);
|
||||
state.echo_hist_pos = 0;
|
||||
state.every_other_sample = false;
|
||||
|
|
|
@ -12,9 +12,6 @@ public:
|
|||
~sDSP();
|
||||
|
||||
private:
|
||||
//external
|
||||
uint8 *ram;
|
||||
|
||||
//USE_STATE_MACHINE variable
|
||||
unsigned phase_index;
|
||||
|
||||
|
|
|
@ -22,9 +22,11 @@ void sDSP::voice_1(voice_t &v) {
|
|||
|
||||
void sDSP::voice_2(voice_t &v) {
|
||||
//read sample pointer (ignored if not needed)
|
||||
const uint8 *entry = &ram[state.t_dir_addr];
|
||||
if(!v.kon_delay) entry += 2;
|
||||
state.t_brr_next_addr = (entry[1] << 8) + entry[0];
|
||||
uint16 addr = state.t_dir_addr;
|
||||
if(!v.kon_delay) addr += 2;
|
||||
uint8 lo = memory::apuram.read(uint16(addr + 0));
|
||||
uint8 hi = memory::apuram.read(uint16(addr + 1));
|
||||
state.t_brr_next_addr = ((hi << 8) + lo);
|
||||
|
||||
state.t_adsr0 = VREG(adsr0);
|
||||
|
||||
|
@ -43,8 +45,8 @@ void sDSP::voice_3a(voice_t &v) {
|
|||
}
|
||||
|
||||
void sDSP::voice_3b(voice_t &v) {
|
||||
state.t_brr_byte = ram[(uint16)(v.brr_addr + v.brr_offset)];
|
||||
state.t_brr_header = ram[(uint16)(v.brr_addr)];
|
||||
state.t_brr_byte = memory::apuram.read(uint16(v.brr_addr + v.brr_offset));
|
||||
state.t_brr_header = memory::apuram.read(uint16(v.brr_addr));
|
||||
}
|
||||
|
||||
void sDSP::voice_3c(voice_t &v) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <nall/bit.hpp>
|
||||
#include <nall/static.hpp>
|
||||
#include <nall/traits.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
|
@ -27,21 +28,21 @@ private:
|
|||
|
||||
public:
|
||||
inline operator T() const { return data; }
|
||||
inline T operator ++(int) { T r = data; data = uclip<bits>(data + 1); return r; }
|
||||
inline T operator --(int) { T r = data; data = uclip<bits>(data - 1); return r; }
|
||||
inline T& operator ++() { data = uclip<bits>(data + 1); return *this; }
|
||||
inline T& operator --() { data = uclip<bits>(data - 1); return *this; }
|
||||
inline T& operator =(const T i) { data = uclip<bits>(i); return *this; }
|
||||
inline T& operator |=(const T i) { data = uclip<bits>(data | i); return *this; }
|
||||
inline T& operator ^=(const T i) { data = uclip<bits>(data ^ i); return *this; }
|
||||
inline T& operator &=(const T i) { data = uclip<bits>(data & i); return *this; }
|
||||
inline T& operator<<=(const T i) { data = uclip<bits>(data << i); return *this; }
|
||||
inline T& operator>>=(const T i) { data = uclip<bits>(data >> i); return *this; }
|
||||
inline T& operator +=(const T i) { data = uclip<bits>(data + i); return *this; }
|
||||
inline T& operator -=(const T i) { data = uclip<bits>(data - i); return *this; }
|
||||
inline T& operator *=(const T i) { data = uclip<bits>(data * i); return *this; }
|
||||
inline T& operator /=(const T i) { data = uclip<bits>(data / i); return *this; }
|
||||
inline T& operator %=(const T i) { data = uclip<bits>(data % i); return *this; }
|
||||
inline T operator ++(int) { T r = data; data = uclip<bits>(data + 1); return r; }
|
||||
inline T operator --(int) { T r = data; data = uclip<bits>(data - 1); return r; }
|
||||
inline T operator ++() { return data = uclip<bits>(data + 1); }
|
||||
inline T operator --() { return data = uclip<bits>(data - 1); }
|
||||
inline T operator =(const T i) { return data = uclip<bits>(i); }
|
||||
inline T operator |=(const T i) { return data = uclip<bits>(data | i); }
|
||||
inline T operator ^=(const T i) { return data = uclip<bits>(data ^ i); }
|
||||
inline T operator &=(const T i) { return data = uclip<bits>(data & i); }
|
||||
inline T operator<<=(const T i) { return data = uclip<bits>(data << i); }
|
||||
inline T operator>>=(const T i) { return data = uclip<bits>(data >> i); }
|
||||
inline T operator +=(const T i) { return data = uclip<bits>(data + i); }
|
||||
inline T operator -=(const T i) { return data = uclip<bits>(data - i); }
|
||||
inline T operator *=(const T i) { return data = uclip<bits>(data * i); }
|
||||
inline T operator /=(const T i) { return data = uclip<bits>(data / i); }
|
||||
inline T operator %=(const T i) { return data = uclip<bits>(data % i); }
|
||||
|
||||
inline uint_t() : data(0) {}
|
||||
inline uint_t(const T i) : data(uclip<bits>(i)) {}
|
||||
|
@ -68,36 +69,26 @@ private:
|
|||
|
||||
public:
|
||||
inline operator T() const { return data; }
|
||||
inline T operator ++(int) { T r = data; data = sclip<bits>(data + 1); return r; }
|
||||
inline T operator --(int) { T r = data; data = sclip<bits>(data - 1); return r; }
|
||||
inline T& operator ++() { data = sclip<bits>(data + 1); return *this; }
|
||||
inline T& operator --() { data = sclip<bits>(data - 1); return *this; }
|
||||
inline T& operator =(const T i) { data = sclip<bits>(i); return *this; }
|
||||
inline T& operator |=(const T i) { data = sclip<bits>(data | i); return *this; }
|
||||
inline T& operator ^=(const T i) { data = sclip<bits>(data ^ i); return *this; }
|
||||
inline T& operator &=(const T i) { data = sclip<bits>(data & i); return *this; }
|
||||
inline T& operator<<=(const T i) { data = sclip<bits>(data << i); return *this; }
|
||||
inline T& operator>>=(const T i) { data = sclip<bits>(data >> i); return *this; }
|
||||
inline T& operator +=(const T i) { data = sclip<bits>(data + i); return *this; }
|
||||
inline T& operator -=(const T i) { data = sclip<bits>(data - i); return *this; }
|
||||
inline T& operator *=(const T i) { data = sclip<bits>(data * i); return *this; }
|
||||
inline T& operator /=(const T i) { data = sclip<bits>(data / i); return *this; }
|
||||
inline T& operator %=(const T i) { data = sclip<bits>(data % i); return *this; }
|
||||
inline T operator ++(int) { T r = data; data = sclip<bits>(data + 1); return r; }
|
||||
inline T operator --(int) { T r = data; data = sclip<bits>(data - 1); return r; }
|
||||
inline T operator ++() { return data = sclip<bits>(data + 1); }
|
||||
inline T operator --() { return data = sclip<bits>(data - 1); }
|
||||
inline T operator =(const T i) { return data = sclip<bits>(i); }
|
||||
inline T operator |=(const T i) { return data = sclip<bits>(data | i); }
|
||||
inline T operator ^=(const T i) { return data = sclip<bits>(data ^ i); }
|
||||
inline T operator &=(const T i) { return data = sclip<bits>(data & i); }
|
||||
inline T operator<<=(const T i) { return data = sclip<bits>(data << i); }
|
||||
inline T operator>>=(const T i) { return data = sclip<bits>(data >> i); }
|
||||
inline T operator +=(const T i) { return data = sclip<bits>(data + i); }
|
||||
inline T operator -=(const T i) { return data = sclip<bits>(data - i); }
|
||||
inline T operator *=(const T i) { return data = sclip<bits>(data * i); }
|
||||
inline T operator /=(const T i) { return data = sclip<bits>(data / i); }
|
||||
inline T operator %=(const T i) { return data = sclip<bits>(data % i); }
|
||||
|
||||
inline int_t() : data(0) {}
|
||||
inline int_t(const T i) : data(sclip<bits>(i)) {}
|
||||
};
|
||||
|
||||
typedef int_t<24> int24_t;
|
||||
typedef uint_t<24> uint24_t;
|
||||
typedef int_t<48> int48_t;
|
||||
typedef uint_t<48> uint48_t;
|
||||
|
||||
} //namespace nall
|
||||
|
||||
using nall::int24_t;
|
||||
using nall::uint24_t;
|
||||
using nall::int48_t;
|
||||
using nall::uint48_t;
|
||||
|
||||
#endif //ifndef NALL_VARINT_HPP
|
||||
|
|
|
@ -2,6 +2,7 @@ class Input {
|
|||
public:
|
||||
enum Setting {
|
||||
Handle,
|
||||
AnalogAxisResistance,
|
||||
};
|
||||
|
||||
virtual bool cap(Setting) { return false; }
|
||||
|
|
|
@ -25,15 +25,18 @@ public:
|
|||
|
||||
struct {
|
||||
HWND handle;
|
||||
unsigned analog_axis_resistance;
|
||||
} settings;
|
||||
|
||||
bool cap(Input::Setting setting) {
|
||||
if(setting == Input::Handle) return true;
|
||||
if(setting == Input::AnalogAxisResistance) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uintptr_t get(Input::Setting setting) {
|
||||
if(setting == Input::Handle) return (uintptr_t)settings.handle;
|
||||
if(setting == Input::AnalogAxisResistance) return settings.analog_axis_resistance;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -42,6 +45,12 @@ public:
|
|||
settings.handle = (HWND)param;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(setting == Input::AnalogAxisResistance) {
|
||||
settings.analog_axis_resistance = param;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -79,9 +88,9 @@ public:
|
|||
memcpy(keystate + index, js.rgbButtons, 128);
|
||||
|
||||
//map d-pad axes
|
||||
int resistance = 75; //config::input.axis_resistance;
|
||||
int resistance = settings.analog_axis_resistance;
|
||||
resistance = max(1, min(99, resistance));
|
||||
resistance = int32_t(double(resistance) * 32768.0 / 100.0);
|
||||
resistance = int(double(resistance) * 32768.0 / 100.0);
|
||||
int resistance_lo = 0x7fff - resistance;
|
||||
int resistance_hi = 0x8000 + resistance;
|
||||
keystate[index + 0x80] = (js.lY <= resistance_lo) ? 0x80 : 0x00;
|
||||
|
@ -297,6 +306,9 @@ public:
|
|||
di = 0;
|
||||
di_key = 0;
|
||||
for(int i = 0; i < DIRECTINPUT_JOYMAX; i++) di_joy[i] = 0;
|
||||
|
||||
settings.handle = 0;
|
||||
settings.analog_axis_resistance = 75;
|
||||
}
|
||||
|
||||
~pInputDI() { term(); }
|
||||
|
|
|
@ -54,6 +54,29 @@ public:
|
|||
Display *display;
|
||||
#endif
|
||||
|
||||
struct {
|
||||
unsigned analog_axis_resistance;
|
||||
} settings;
|
||||
|
||||
bool cap(Input::Setting setting) {
|
||||
if(setting == Input::AnalogAxisResistance) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uintptr_t get(Input::Setting setting) {
|
||||
if(setting == Input::AnalogAxisResistance) return settings.analog_axis_resistance;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool set(Input::Setting setting, uintptr_t param) {
|
||||
if(setting == Input::AnalogAxisResistance) {
|
||||
settings.analog_axis_resistance = param;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool key_down(uint16_t key) {
|
||||
#if !defined(_WIN32)
|
||||
#define map(i) (keystate[i >> 3] & (1 << (i & 7)))
|
||||
|
@ -220,6 +243,10 @@ public:
|
|||
joystate[i][joy_left] = false;
|
||||
joystate[i][joy_right] = false;
|
||||
|
||||
int resistance = settings.analog_axis_resistance;
|
||||
resistance = max(1, min(99, resistance));
|
||||
resistance = int(double(resistance) * 32768.0 / 100.0);
|
||||
|
||||
//only poll X,Y axes for D-pad, left analog and right analog.
|
||||
//note 1: right analog is swapped on some controllers, this cannot be helped.
|
||||
//note 2: some controllers report more axes than physically exist.
|
||||
|
@ -229,11 +256,11 @@ public:
|
|||
for(int a = 0; a < min(axes, 6); a++) {
|
||||
int value = SDL_JoystickGetAxis(joy[i], a);
|
||||
if((a & 1) == 0) { //X axis
|
||||
joystate[i][joy_left] |= value < -16384;
|
||||
joystate[i][joy_right] |= value > +16384;
|
||||
joystate[i][joy_left] |= value < -resistance;
|
||||
joystate[i][joy_right] |= value > +resistance;
|
||||
} else { //Y axis
|
||||
joystate[i][joy_up] |= value < -16384;
|
||||
joystate[i][joy_down] |= value > +16384;
|
||||
joystate[i][joy_up] |= value < -resistance;
|
||||
joystate[i][joy_down] |= value > +resistance;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,10 +295,15 @@ public:
|
|||
pInputSDL(InputSDL &self_) : self(self_) {
|
||||
for(int i = 0; i < 16; i++) joy[i] = 0;
|
||||
clear();
|
||||
|
||||
settings.analog_axis_resistance = 75;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
bool InputSDL::cap(Setting setting) { return p.cap(setting); }
|
||||
uintptr_t InputSDL::get(Setting setting) { return p.get(setting); }
|
||||
bool InputSDL::set(Setting setting, uintptr_t param) { return p.set(setting, param); }
|
||||
bool InputSDL::key_down(uint16_t key) { return p.key_down(key); }
|
||||
void InputSDL::clear() { p.clear(); }
|
||||
void InputSDL::poll() { p.poll(); }
|
||||
|
|
|
@ -2,6 +2,10 @@ class pInputSDL;
|
|||
|
||||
class InputSDL : public Input {
|
||||
public:
|
||||
bool cap(Setting);
|
||||
uintptr_t get(Setting);
|
||||
bool set(Setting, uintptr_t);
|
||||
|
||||
bool key_down(uint16_t key);
|
||||
|
||||
void clear();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
video.wgl
|
||||
author: byuu, krom
|
||||
authors: byuu, krom, mudlord
|
||||
license: public domain
|
||||
date: 2008-03-26
|
||||
*/
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
namespace memory {
|
||||
MMIOAccess mmio;
|
||||
StaticRAM wram(128 * 1024);
|
||||
StaticRAM apuram(64 * 1024);
|
||||
|
||||
UnmappedMemory memory_unmapped;
|
||||
UnmappedMMIO mmio_unmapped;
|
||||
|
|
|
@ -115,8 +115,9 @@ class Bus { public:
|
|||
};
|
||||
|
||||
namespace memory {
|
||||
extern MMIOAccess mmio;
|
||||
extern StaticRAM wram;
|
||||
extern MMIOAccess mmio; //S-CPU, S-PPU
|
||||
extern StaticRAM wram; //S-CPU
|
||||
extern StaticRAM apuram; //S-SMP, S-DSP
|
||||
|
||||
extern UnmappedMemory memory_unmapped;
|
||||
extern UnmappedMMIO mmio_unmapped;
|
||||
|
|
|
@ -33,12 +33,13 @@ void sBus::map_generic() {
|
|||
} break;
|
||||
|
||||
case Cartridge::SPC7110ROM: {
|
||||
map(MapDirect, 0x00, 0x00, 0x6000, 0x7fff, spc7110);
|
||||
map(MapDirect, 0x00, 0x0f, 0x8000, 0xffff, spc7110);
|
||||
map(MapDirect, 0x30, 0x30, 0x6000, 0x7fff, spc7110);
|
||||
map(MapDirect, 0x50, 0x50, 0x0000, 0xffff, spc7110);
|
||||
map(MapDirect, 0x80, 0x8f, 0x8000, 0xffff, spc7110);
|
||||
map(MapDirect, 0xc0, 0xff, 0x0000, 0xffff, spc7110);
|
||||
map(MapDirect, 0x00, 0x00, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic
|
||||
map(MapShadow, 0x00, 0x0f, 0x8000, 0xffff, memory::cartrom); //program ROM
|
||||
map(MapDirect, 0x30, 0x30, 0x6000, 0x7fff, spc7110); //save RAM w/custom logic
|
||||
map(MapDirect, 0x50, 0x50, 0x0000, 0xffff, spc7110); //decompression MMIO port
|
||||
map(MapShadow, 0x80, 0x8f, 0x8000, 0xffff, memory::cartrom); //program ROM
|
||||
map(MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, memory::cartrom); //program ROM
|
||||
map(MapDirect, 0xd0, 0xff, 0x0000, 0xffff, spc7110); //MMC-controlled data ROM
|
||||
} break;
|
||||
|
||||
case Cartridge::BSXROM: {
|
||||
|
|
|
@ -578,7 +578,7 @@ uint32 r;
|
|||
|
||||
//SLHV
|
||||
uint8 bPPU::mmio_r2137() {
|
||||
if(cpu.pio_status() & 0x80) {
|
||||
if(cpu.pio() & 0x80) {
|
||||
latch_counters();
|
||||
}
|
||||
return cpu.regs.mdr;
|
||||
|
@ -679,7 +679,7 @@ uint8 r = 0x00;
|
|||
regs.latch_vcounter = 0;
|
||||
|
||||
r |= field() << 7;
|
||||
if(!(cpu.pio_status() & 0x80)) {
|
||||
if(!(cpu.pio() & 0x80)) {
|
||||
r |= 0x40;
|
||||
} else if(regs.counters_latched == true) {
|
||||
r |= 0x40;
|
||||
|
|
|
@ -37,7 +37,7 @@ PPU::PPU() {
|
|||
status.frames_executed = 0;
|
||||
|
||||
ppu1_version = 1;
|
||||
ppu2_version = 1;
|
||||
ppu2_version = 3;
|
||||
}
|
||||
|
||||
PPU::~PPU() {
|
||||
|
|
|
@ -5,7 +5,6 @@ public:
|
|||
virtual void enter() = 0;
|
||||
|
||||
SMPRegs regs;
|
||||
uint8 spcram[65536];
|
||||
static const uint8 iplrom[64];
|
||||
enum {
|
||||
FLAG_N = 0x80, FLAG_V = 0x40, FLAG_P = 0x20, FLAG_B = 0x10,
|
||||
|
@ -18,7 +17,6 @@ public:
|
|||
virtual uint8 port_read(uint8 port) = 0;
|
||||
virtual void port_write(uint8 port, uint8 value) = 0;
|
||||
|
||||
virtual uint8* get_spcram_handle() { return spcram; }
|
||||
virtual void power() = 0;
|
||||
virtual void reset() = 0;
|
||||
|
||||
|
@ -27,6 +25,6 @@ public:
|
|||
void disassemble_opcode(char *output);
|
||||
inline uint16 __relb(int8 offset, int op_len);
|
||||
|
||||
SMP() { memset(spcram, 0, 65536); }
|
||||
SMP() {}
|
||||
virtual ~SMP() {}
|
||||
};
|
||||
|
|
|
@ -2,27 +2,27 @@
|
|||
|
||||
alwaysinline
|
||||
uint8 sSMP::ram_read(uint16 addr) {
|
||||
if(addr < 0xffc0) return spcram[addr];
|
||||
if(status.iplrom_enabled == false) return spcram[addr];
|
||||
if(addr < 0xffc0) return memory::apuram.read(addr);
|
||||
if(status.iplrom_enabled == false) return memory::apuram.read(addr);
|
||||
return iplrom[addr & 0x3f];
|
||||
}
|
||||
|
||||
alwaysinline
|
||||
void sSMP::ram_write(uint16 addr, uint8 data) {
|
||||
//writes to $ffc0-$ffff always go to spcram, even if the iplrom is enabled
|
||||
spcram[addr] = data;
|
||||
memory::apuram.write(addr, data);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
alwaysinline
|
||||
uint8 sSMP::port_read(uint8 port) {
|
||||
return spcram[0xf4 + (port & 3)];
|
||||
return memory::apuram.read(0xf4 + (port & 3));
|
||||
}
|
||||
|
||||
alwaysinline
|
||||
void sSMP::port_write(uint8 port, uint8 data) {
|
||||
spcram[0xf4 + (port & 3)] = data;
|
||||
memory::apuram.write(0xf4 + (port & 3), data);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -87,14 +87,8 @@ uint8 sSMP::op_busread(uint16 addr) {
|
|||
t2.stage3_ticks = 0;
|
||||
} break;
|
||||
}
|
||||
} else if(addr < 0xffc0) {
|
||||
r = spcram[addr];
|
||||
} else {
|
||||
if(status.iplrom_enabled == true) {
|
||||
r = iplrom[addr & 0x3f];
|
||||
} else {
|
||||
r = spcram[addr];
|
||||
}
|
||||
r = ram_read(addr);
|
||||
}
|
||||
|
||||
return r;
|
||||
|
@ -112,17 +106,17 @@ void sSMP::op_buswrite(uint16 addr, uint8 data) {
|
|||
|
||||
//multiplier table may not be 100% accurate, some settings crash
|
||||
//the processor due to S-SMP <> S-DSP bus access misalignment
|
||||
static uint8 clock_speed_tbl[16] =
|
||||
{ 3, 5, 9, 17, 4, 6, 10, 18, 6, 8, 12, 20, 10, 12, 16, 24 };
|
||||
//static uint8 clock_speed_tbl[16] =
|
||||
//{ 3, 5, 9, 17, 4, 6, 10, 18, 6, 8, 12, 20, 10, 12, 16, 24 };
|
||||
|
||||
status.clock_speed = 24 * clock_speed_tbl[data >> 4] / 3;
|
||||
//status.clock_speed = 24 * clock_speed_tbl[data >> 4] / 3;
|
||||
status.mmio_disabled = !!(data & 0x04);
|
||||
status.ram_writable = !!(data & 0x02);
|
||||
|
||||
if((data >> 4) != 0) {
|
||||
dprintf(source::smp, "* S-SMP critical warning: $00f0 (TEST) clock speed control modified!");
|
||||
dprintf(source::smp, "* S-SMP may crash on hardware as a result!");
|
||||
}
|
||||
//if((data >> 4) != 0) {
|
||||
//dprintf("* S-SMP critical warning: $00f0 (TEST) clock speed control modified!");
|
||||
//dprintf("* S-SMP may crash on hardware as a result!");
|
||||
//}
|
||||
} break;
|
||||
|
||||
case 0xf1: { //CONTROL
|
||||
|
|
|
@ -6,13 +6,11 @@
|
|||
#include "timing/timing.cpp"
|
||||
|
||||
void sSMP::power() {
|
||||
//SNES hardware SPCRAM contains pseudo-random data upon power up
|
||||
//for(unsigned i = 0; i < 65536; i += 64) {
|
||||
// memset(spcram + i, 0x00, 32);
|
||||
// memset(spcram + i + 32, 0xff, 32);
|
||||
//}
|
||||
|
||||
memset(spcram, 0x00, 65536);
|
||||
for(unsigned i = 0; i < 65536; i++) {
|
||||
//SNES hardware APURAM contains pseudo-random data upon power up
|
||||
//memory::apuram.write(i, (i & 32) ? 0xff : 0x00);
|
||||
memory::apuram.write(i, 0x00);
|
||||
}
|
||||
|
||||
//targets not initialized/changed upon reset
|
||||
t0.target = 0;
|
||||
|
|
|
@ -1,102 +1,78 @@
|
|||
#ifdef SNES_CPP
|
||||
|
||||
bool SNES::Input::port_read(bool port) {
|
||||
if(port == 0) {
|
||||
switch(input.port0_device) {
|
||||
case DeviceNone: return false;
|
||||
#ifdef SNES_CPP
|
||||
|
||||
default: {
|
||||
if(input.port0_devicebitpos < input.port0_devicebits) {
|
||||
return input.port0_bits[input.port0_devicebitpos++];
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch(input.port1_device) {
|
||||
case DeviceNone: return false;
|
||||
uint8 SNES::Input::port_read(bool port) {
|
||||
switch(port_device[port]) {
|
||||
case DeviceJoypad: {
|
||||
if(port_counter0[port] >= 16) return 1;
|
||||
|
||||
default: {
|
||||
if(input.port1_devicebitpos < input.port1_devicebits) {
|
||||
return input.port1_bits[input.port1_devicebitpos++];
|
||||
unsigned deviceid = port == 0 ? DeviceIDJoypad1 : DeviceIDJoypad2;
|
||||
return snesinterface.input_poll(deviceid, port_counter0[port]++);
|
||||
} //case DeviceJoypad
|
||||
|
||||
case DeviceMultitap: {
|
||||
if(cpu.joylatch()) return 2; //when latch is high -- data2 = 1, data1 = 0
|
||||
|
||||
unsigned deviceidx, deviceid0, deviceid1;
|
||||
if(port == 0) {
|
||||
if(cpu.pio() & 0x40) {
|
||||
deviceidx = port_counter0[port];
|
||||
if(deviceidx >= 16) return 3;
|
||||
port_counter0[port]++;
|
||||
|
||||
deviceid0 = DeviceIDMultitap1A;
|
||||
deviceid1 = DeviceIDMultitap1B;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
deviceidx = port_counter1[port];
|
||||
if(deviceidx >= 16) return 3;
|
||||
port_counter1[port]++;
|
||||
|
||||
deviceid0 = DeviceIDMultitap1C;
|
||||
deviceid1 = DeviceIDMultitap1D;
|
||||
}
|
||||
} else {
|
||||
if(cpu.pio() & 0x80) {
|
||||
deviceidx = port_counter0[port];
|
||||
if(deviceidx >= 16) return 3;
|
||||
port_counter0[port]++;
|
||||
|
||||
deviceid0 = DeviceIDMultitap2A;
|
||||
deviceid1 = DeviceIDMultitap2B;
|
||||
} else {
|
||||
deviceidx = port_counter1[port];
|
||||
if(deviceidx >= 16) return 3;
|
||||
port_counter1[port]++;
|
||||
|
||||
deviceid0 = DeviceIDMultitap2C;
|
||||
deviceid1 = DeviceIDMultitap2D;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (snesinterface.input_poll(deviceid0, deviceidx) << 0)
|
||||
| (snesinterface.input_poll(deviceid1, deviceidx) << 1);
|
||||
} //case DeviceMultitap
|
||||
}
|
||||
|
||||
//no device connected
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SNES::Input::port_set_deviceid(bool port, uint deviceid) {
|
||||
if(port == 0) {
|
||||
switch(deviceid) {
|
||||
case DeviceIDNone: {
|
||||
input.port0_device = DeviceNone;
|
||||
} break;
|
||||
|
||||
case DeviceIDJoypad1:
|
||||
case DeviceIDJoypad2: {
|
||||
input.port0_device = DeviceJoypad;
|
||||
input.port0_devicebits = 16;
|
||||
} break;
|
||||
}
|
||||
|
||||
memset(input.port0_bits, 0, sizeof(input.port0_bits));
|
||||
input.port0_devicebitpos = 0;
|
||||
input.port0_deviceid = deviceid;
|
||||
} else {
|
||||
switch(deviceid) {
|
||||
case DeviceIDNone: {
|
||||
input.port1_device = DeviceNone;
|
||||
} break;
|
||||
|
||||
case DeviceIDJoypad1:
|
||||
case DeviceIDJoypad2: {
|
||||
input.port1_device = DeviceJoypad;
|
||||
input.port1_devicebits = 16;
|
||||
} break;
|
||||
}
|
||||
|
||||
memset(input.port1_bits, 0, sizeof(input.port1_bits));
|
||||
input.port1_devicebitpos = 0;
|
||||
input.port1_deviceid = deviceid;
|
||||
}
|
||||
void SNES::Input::port_set_device(bool port, unsigned device) {
|
||||
port_device[port] = device;
|
||||
port_counter0[port] = 0;
|
||||
port_counter1[port] = 0;
|
||||
}
|
||||
|
||||
void SNES::Input::poll() {
|
||||
snesinterface.input_poll();
|
||||
|
||||
bool *p0 = input.port0_bits;
|
||||
bool *p1 = input.port1_bits;
|
||||
|
||||
switch(input.port0_device) {
|
||||
case DeviceNone: break;
|
||||
|
||||
default: {
|
||||
for(int i = 0; i < input.port0_devicebits; i++) {
|
||||
*p0++ = snesinterface.input_poll(input.port0_deviceid, i);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
switch(input.port1_device) {
|
||||
case DeviceNone: break;
|
||||
|
||||
default: {
|
||||
for(int i = 0; i < input.port1_devicebits; i++) {
|
||||
*p1++ = snesinterface.input_poll(input.port1_deviceid, i);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
input.port0_devicebitpos = 0;
|
||||
input.port1_devicebitpos = 0;
|
||||
port_counter0[0] = 0;
|
||||
port_counter1[0] = 0;
|
||||
port_counter0[1] = 0;
|
||||
port_counter1[1] = 0;
|
||||
}
|
||||
|
||||
void SNES::Input::init() {
|
||||
port_set_deviceid(0, config::snes.controller_port0);
|
||||
port_set_deviceid(1, config::snes.controller_port1);
|
||||
}
|
||||
|
||||
port_set_device(0, config::snes.controller_port1);
|
||||
port_set_device(1, config::snes.controller_port2);
|
||||
}
|
||||
|
||||
#endif //ifdef SNES_CPP
|
||||
|
|
|
@ -3,12 +3,21 @@ public:
|
|||
enum Device {
|
||||
DeviceNone,
|
||||
DeviceJoypad,
|
||||
DeviceMultitap,
|
||||
};
|
||||
|
||||
enum {
|
||||
DeviceIDNone,
|
||||
DeviceIDJoypad1,
|
||||
DeviceIDJoypad2,
|
||||
DeviceIDMultitap1A,
|
||||
DeviceIDMultitap1B,
|
||||
DeviceIDMultitap1C,
|
||||
DeviceIDMultitap1D,
|
||||
DeviceIDMultitap2A,
|
||||
DeviceIDMultitap2B,
|
||||
DeviceIDMultitap2C,
|
||||
DeviceIDMultitap2D,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -20,16 +29,11 @@ public:
|
|||
JoypadL = 10, JoypadR = 11,
|
||||
};
|
||||
|
||||
struct {
|
||||
uint port0_device, port0_devicebits, port0_devicebitpos, port0_deviceid;
|
||||
uint port1_device, port1_devicebits, port1_devicebitpos, port1_deviceid;
|
||||
|
||||
bool port0_bits[256];
|
||||
bool port1_bits[256];
|
||||
} input;
|
||||
|
||||
bool port_read(bool port);
|
||||
void port_set_deviceid(bool port, uint deviceid);
|
||||
uint8 port_read(bool port);
|
||||
void port_set_device(bool port, unsigned device);
|
||||
void init();
|
||||
void poll();
|
||||
|
||||
private:
|
||||
unsigned port_device[2], port_counter0[2], port_counter1[2];
|
||||
} input;
|
||||
|
|
|
@ -12,7 +12,7 @@ class SNESInterface { public:
|
|||
|
||||
function<bool ()> input_ready;
|
||||
void input_poll();
|
||||
bool input_poll(unsigned deviceid, unsigned button);
|
||||
bool input_poll(unsigned deviceid, unsigned id);
|
||||
|
||||
void init();
|
||||
void term();
|
||||
|
|
|
@ -4,7 +4,7 @@ uintptr_t AboutWindow::close(Event) {
|
|||
}
|
||||
|
||||
void AboutWindow::setup() {
|
||||
create(Window::AutoCenter, 360, 135, translate["About bsnes ..."]);
|
||||
create(Window::AutoCenter, 283, 76, translate["About bsnes ..."]);
|
||||
set_icon(48, 48, (uint32_t*)resource::icon48);
|
||||
|
||||
icon.create(0, 48, 48);
|
||||
|
@ -13,17 +13,11 @@ void AboutWindow::setup() {
|
|||
<< translate["Author"] << ": byuu\n"
|
||||
<< translate["Project began: October 14th, 2004"]
|
||||
);
|
||||
contributors.create(0, 350, 75, string()
|
||||
<< translate["Contributors:"] << "\n"
|
||||
<< " Andreas Naive, anomie, blargg, DMV27, GIGO, kode54,\n"
|
||||
<< " neviksti, Nach, Overload, Richard Bannister, TRAC, zones\n"
|
||||
<< "\n"
|
||||
<< translate["Localization by: byuu"]
|
||||
);
|
||||
localization.create(0, 273, 18, translate["Localization by: byuu"]);
|
||||
|
||||
attach(icon, 5, 5);
|
||||
attach(about, 58, 5);
|
||||
attach(contributors, 5, 58);
|
||||
attach(localization, 5, 58);
|
||||
|
||||
on_close = bind(&AboutWindow::close, this);
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
class AboutWindow : public Window {
|
||||
public:
|
||||
Canvas icon;
|
||||
Label about;
|
||||
Label contributors;
|
||||
static const char about_text[1024];
|
||||
static const char contributors_text[1024];
|
||||
Label about;
|
||||
Label localization;
|
||||
|
||||
void setup();
|
||||
uintptr_t close(Event);
|
||||
|
|
|
@ -49,6 +49,14 @@ uintptr_t MainWindow::event(Event e) {
|
|||
event::power();
|
||||
}
|
||||
|
||||
if(e.widget == &menu_system_controller_port1_none) { event::update_controller_port1(0); }
|
||||
if(e.widget == &menu_system_controller_port1_joypad) { event::update_controller_port1(1); }
|
||||
if(e.widget == &menu_system_controller_port1_multitap) { event::update_controller_port1(2); }
|
||||
|
||||
if(e.widget == &menu_system_controller_port2_none) { event::update_controller_port2(0); }
|
||||
if(e.widget == &menu_system_controller_port2_joypad) { event::update_controller_port2(1); }
|
||||
if(e.widget == &menu_system_controller_port2_multitap) { event::update_controller_port2(2); }
|
||||
|
||||
if(e.widget == &menu_file_exit) {
|
||||
event(Event(Event::Close));
|
||||
}
|
||||
|
@ -133,11 +141,31 @@ void MainWindow::setup() {
|
|||
menu_file_load_special.attach(menu_file_load_bsc.create(string() << translate["Load BS-X Slotted Cartridge"] << " ..."));
|
||||
menu_file_load_special.attach(menu_file_load_st.create(string() << translate["Load Sufami Turbo Cartridge"] << " ..."));
|
||||
menu_file.attach(menu_file_unload.create(translate["Unload Cartridge"]));
|
||||
|
||||
menu_file.attach(menu_file_sep1.create());
|
||||
menu_file.attach(menu_file_reset.create(translate["Reset"]));
|
||||
menu_file_power.create(translate["Power Cycle"]);
|
||||
if(config::advanced.enable) menu_file.attach(menu_file_power);
|
||||
|
||||
menu_file.attach(menu_file_sep2.create());
|
||||
menu_file.attach(menu_system_controller_port1.create(translate["Controller Port 1"]));
|
||||
group.add(&menu_system_controller_port1_none);
|
||||
group.add(&menu_system_controller_port1_joypad);
|
||||
group.add(&menu_system_controller_port1_multitap);
|
||||
menu_system_controller_port1.attach(menu_system_controller_port1_none.create (group, translate["None"]));
|
||||
menu_system_controller_port1.attach(menu_system_controller_port1_joypad.create (group, translate["Joypad"]));
|
||||
menu_system_controller_port1.attach(menu_system_controller_port1_multitap.create(group, translate["Multitap"]));
|
||||
group.reset();
|
||||
menu_file.attach(menu_system_controller_port2.create(translate["Controller Port 2"]));
|
||||
group.add(&menu_system_controller_port2_none);
|
||||
group.add(&menu_system_controller_port2_joypad);
|
||||
group.add(&menu_system_controller_port2_multitap);
|
||||
menu_system_controller_port2.attach(menu_system_controller_port2_none.create (group, translate["None"]));
|
||||
menu_system_controller_port2.attach(menu_system_controller_port2_joypad.create (group, translate["Joypad"]));
|
||||
menu_system_controller_port2.attach(menu_system_controller_port2_multitap.create(group, translate["Multitap"]));
|
||||
group.reset();
|
||||
|
||||
menu_file.attach(menu_file_sep3.create());
|
||||
menu_file.attach(menu_file_exit.create(translate["Exit"]));
|
||||
|
||||
attach(menu_settings.create(translate["Settings"]));
|
||||
|
@ -206,8 +234,8 @@ void MainWindow::setup() {
|
|||
|
||||
menu_settings.attach(menu_settings_sep1.create());
|
||||
menu_settings.attach(menu_settings_mute.create(translate["Mute Audio Output"]));
|
||||
menu_settings.attach(menu_settings_sep2.create());
|
||||
|
||||
menu_settings.attach(menu_settings_sep2.create());
|
||||
menu_settings.attach(menu_settings_emuspeed.create(translate["Emulation Speed"]));
|
||||
group.add(&menu_settings_emuspeed_slowest);
|
||||
group.add(&menu_settings_emuspeed_slow);
|
||||
|
@ -222,8 +250,6 @@ void MainWindow::setup() {
|
|||
menu_settings_emuspeed.attach(menu_settings_emuspeed_fastest.create(group, translate["200%"]));
|
||||
menu_settings_emuspeed.attach(menu_settings_emuspeed_disabled.create(group, translate["Uncapped"]));
|
||||
group.reset();
|
||||
|
||||
menu_settings.attach(menu_settings_sep3.create());
|
||||
menu_settings.attach(menu_settings_config.create(string() << translate["Configuration"] << " ..."));
|
||||
|
||||
attach(menu_misc.create(translate["Misc"]));
|
||||
|
@ -252,6 +278,14 @@ void MainWindow::setup() {
|
|||
menu_file_reset.on_tick =
|
||||
menu_file_power.on_tick =
|
||||
|
||||
menu_system_controller_port1_none.on_tick =
|
||||
menu_system_controller_port1_joypad.on_tick =
|
||||
menu_system_controller_port1_multitap.on_tick =
|
||||
|
||||
menu_system_controller_port2_none.on_tick =
|
||||
menu_system_controller_port2_joypad.on_tick =
|
||||
menu_system_controller_port2_multitap.on_tick =
|
||||
|
||||
menu_settings_videomode_1x.on_tick =
|
||||
menu_settings_videomode_2x.on_tick =
|
||||
menu_settings_videomode_3x.on_tick =
|
||||
|
@ -301,6 +335,18 @@ void MainWindow::setup() {
|
|||
void MainWindow::sync() {
|
||||
event::load_video_settings();
|
||||
|
||||
switch(config::snes.controller_port1) { default:
|
||||
case SNES::Input::DeviceNone: menu_system_controller_port1_none.check(); break;
|
||||
case SNES::Input::DeviceJoypad: menu_system_controller_port1_joypad.check(); break;
|
||||
case SNES::Input::DeviceMultitap: menu_system_controller_port1_multitap.check(); break;
|
||||
}
|
||||
|
||||
switch(config::snes.controller_port2) {
|
||||
case SNES::Input::DeviceNone: menu_system_controller_port2_none.check(); break;
|
||||
case SNES::Input::DeviceJoypad: menu_system_controller_port2_joypad.check(); break;
|
||||
case SNES::Input::DeviceMultitap: menu_system_controller_port2_multitap.check(); break;
|
||||
}
|
||||
|
||||
switch(event::video_settings.multiplier) { default:
|
||||
case 1: menu_settings_videomode_1x.check(); break;
|
||||
case 2: menu_settings_videomode_2x.check(); break;
|
||||
|
|
|
@ -11,6 +11,15 @@ public:
|
|||
MenuItem menu_file_reset;
|
||||
MenuItem menu_file_power;
|
||||
MenuSeparator menu_file_sep2;
|
||||
MenuGroup menu_system_controller_port1;
|
||||
MenuRadioItem menu_system_controller_port1_none;
|
||||
MenuRadioItem menu_system_controller_port1_joypad;
|
||||
MenuRadioItem menu_system_controller_port1_multitap;
|
||||
MenuGroup menu_system_controller_port2;
|
||||
MenuRadioItem menu_system_controller_port2_none;
|
||||
MenuRadioItem menu_system_controller_port2_joypad;
|
||||
MenuRadioItem menu_system_controller_port2_multitap;
|
||||
MenuSeparator menu_file_sep3;
|
||||
MenuItem menu_file_exit;
|
||||
|
||||
MenuGroup menu_settings;
|
||||
|
@ -55,7 +64,6 @@ public:
|
|||
MenuRadioItem menu_settings_emuspeed_fast;
|
||||
MenuRadioItem menu_settings_emuspeed_fastest;
|
||||
MenuRadioItem menu_settings_emuspeed_disabled;
|
||||
MenuSeparator menu_settings_sep3;
|
||||
MenuItem menu_settings_config;
|
||||
|
||||
MenuGroup menu_misc;
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
uintptr_t MessageWindow::close(Event) {
|
||||
hide();
|
||||
return false;
|
||||
}
|
||||
|
||||
void MessageWindow::setup() {
|
||||
create(Window::AutoCenter, 400, 100, "");
|
||||
message.create(0, 390, 60, "");
|
||||
ok.create(0, 100, 25, translate["Ok"]);
|
||||
attach(message, 5, 5);
|
||||
attach(ok, 295, 70);
|
||||
|
||||
on_close = ok.on_tick = bind(&MessageWindow::close, this);
|
||||
}
|
||||
|
||||
void MessageWindow::show(const char *message_, const char *title_) {
|
||||
message.set_text(message_);
|
||||
set_text(translate[title_]);
|
||||
focus();
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
class MessageWindow : public Window {
|
||||
public:
|
||||
Label message;
|
||||
Button ok;
|
||||
|
||||
void setup();
|
||||
void show(const char *message, const char *title = "Warning");
|
||||
uintptr_t close(Event);
|
||||
} window_message;
|
|
@ -105,14 +105,19 @@ integral_setting Audio::mute(config(), "audio.mute", "Mute audio playback", inte
|
|||
struct Input {
|
||||
static integral_setting capture_mode;
|
||||
static integral_setting allow_invalid_input;
|
||||
static integral_setting analog_axis_resistance;
|
||||
|
||||
struct Joypad1 {
|
||||
static string_setting up, down, left, right, a, b, x, y, l, r, select, start;
|
||||
} joypad1;
|
||||
struct Joypad1 { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } joypad1;
|
||||
struct Joypad2 { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } joypad2;
|
||||
|
||||
struct Joypad2 {
|
||||
static string_setting up, down, left, right, a, b, x, y, l, r, select, start;
|
||||
} joypad2;
|
||||
struct Multitap1A { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap1a;
|
||||
struct Multitap1B { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap1b;
|
||||
struct Multitap1C { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap1c;
|
||||
struct Multitap1D { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap1d;
|
||||
struct Multitap2A { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap2a;
|
||||
struct Multitap2B { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap2b;
|
||||
struct Multitap2C { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap2c;
|
||||
struct Multitap2D { static string_setting up, down, left, right, a, b, x, y, l, r, select, start; } multitap2d;
|
||||
|
||||
struct GUI {
|
||||
static string_setting load;
|
||||
|
@ -144,6 +149,15 @@ integral_setting Input::allow_invalid_input(config(), "input.allow_invalid_input
|
|||
"Enabling this option can trigger bugs in certain games.\n",
|
||||
integral_setting::boolean, false);
|
||||
|
||||
integral_setting Input::analog_axis_resistance(config(), "input.analog_axis_resistance",
|
||||
"Resistance required to activate analog stick in any given direction.\n"
|
||||
"This value ranges from 1 (1%, virtually no resistance) to 99 (99%, near full resistance.)\n"
|
||||
"For instance, a value of 50 means that to register 'left', the analog stick must be moved\n"
|
||||
"50% between the center and the left.\n"
|
||||
"Less resistance allows for more fluid movement; whereas more resistance helps to prevent\n"
|
||||
"accidental movements, eg attempting to press up whilst bumping the stick slightly left.",
|
||||
integral_setting::decimal, 50);
|
||||
|
||||
string_setting Input::Joypad1::up (config(), "input.joypad1.up", "", "up");
|
||||
string_setting Input::Joypad1::down (config(), "input.joypad1.down", "", "down");
|
||||
string_setting Input::Joypad1::left (config(), "input.joypad1.left", "", "left");
|
||||
|
@ -170,6 +184,31 @@ string_setting Input::Joypad2::r (config(), "input.joypad2.r", "", "l")
|
|||
string_setting Input::Joypad2::select(config(), "input.joypad2.select", "", "lbracket");
|
||||
string_setting Input::Joypad2::start (config(), "input.joypad2.start", "", "rbracket");
|
||||
|
||||
#define DeclMultitap(uname, lname) \
|
||||
string_setting Input::uname::up (config(), "input.multitap" lname ".up", "", "none"); \
|
||||
string_setting Input::uname::down (config(), "input.multitap" lname ".down", "", "none"); \
|
||||
string_setting Input::uname::left (config(), "input.multitap" lname ".left", "", "none"); \
|
||||
string_setting Input::uname::right (config(), "input.multitap" lname ".right", "", "none"); \
|
||||
string_setting Input::uname::a (config(), "input.multitap" lname ".a", "", "none"); \
|
||||
string_setting Input::uname::b (config(), "input.multitap" lname ".b", "", "none"); \
|
||||
string_setting Input::uname::x (config(), "input.multitap" lname ".x", "", "none"); \
|
||||
string_setting Input::uname::y (config(), "input.multitap" lname ".y", "", "none"); \
|
||||
string_setting Input::uname::l (config(), "input.multitap" lname ".l", "", "none"); \
|
||||
string_setting Input::uname::r (config(), "input.multitap" lname ".r", "", "none"); \
|
||||
string_setting Input::uname::select(config(), "input.multitap" lname ".select", "", "none"); \
|
||||
string_setting Input::uname::start (config(), "input.multitap" lname ".start", "", "none");
|
||||
|
||||
DeclMultitap(Multitap1A, "1a")
|
||||
DeclMultitap(Multitap1B, "1b")
|
||||
DeclMultitap(Multitap1C, "1c")
|
||||
DeclMultitap(Multitap1D, "1d")
|
||||
DeclMultitap(Multitap2A, "2a")
|
||||
DeclMultitap(Multitap2B, "2b")
|
||||
DeclMultitap(Multitap2C, "2c")
|
||||
DeclMultitap(Multitap2D, "2d")
|
||||
|
||||
#undef DeclMultitap
|
||||
|
||||
string_setting Input::GUI::load (config(), "input.gui.load", "", "none");
|
||||
string_setting Input::GUI::pause (config(), "input.gui.pause", "", "f12");
|
||||
string_setting Input::GUI::reset (config(), "input.gui.reset", "", "none");
|
||||
|
@ -186,18 +225,10 @@ string_setting Input::GUI::toggle_statusbar (config(), "input.gui.toggle_status
|
|||
struct Misc {
|
||||
static integral_setting opacity;
|
||||
static integral_setting status_enable;
|
||||
static string_setting status_text;
|
||||
} misc;
|
||||
|
||||
integral_setting Misc::opacity(config(), "misc.opacity", "Opacity of user interface windows", integral_setting::decimal, 100);
|
||||
integral_setting Misc::status_enable(config(), "misc.status_enable", "Display information statusbar", integral_setting::boolean, true);
|
||||
string_setting Misc::status_text(config(), "misc.status_text",
|
||||
"Text to print inside statusbar\n"
|
||||
"%n = cartridge file name\n"
|
||||
"%t = internal cartridge header name\n"
|
||||
"%f = executed frames per second\n"
|
||||
"%m = maximum frames per second"
|
||||
"", "%n : %f / %m");
|
||||
|
||||
struct Advanced {
|
||||
static integral_setting enable;
|
||||
|
|
104
src/ui/event.cpp
104
src/ui/event.cpp
|
@ -7,7 +7,7 @@ void keydown(uint16_t key) {
|
|||
app.pause = !app.pause; //toggle pause state
|
||||
if(app.pause) {
|
||||
audio.clear();
|
||||
if(cartridge.loaded()) update_status();
|
||||
if(cartridge.loaded()) status.update();
|
||||
}
|
||||
}
|
||||
if(key == input_manager.gui.reset) reset();
|
||||
|
@ -138,31 +138,38 @@ void update_emulation_speed(int speed) {
|
|||
window_main.sync();
|
||||
}
|
||||
|
||||
void update_status() {
|
||||
if(!cartridge.loaded()) {
|
||||
window_main.status.set_text("");
|
||||
} else if(app.pause || app.autopause) {
|
||||
window_main.status.set_text("(paused)");
|
||||
} else if(ppu.status.frames_updated) {
|
||||
ppu.status.frames_updated = false;
|
||||
void update_controller_port1(int device) {
|
||||
unsigned current_device = config::snes.controller_port1;
|
||||
unsigned new_device;
|
||||
|
||||
unsigned max_framerate = snes.region() == SNES::NTSC ? 60 : 50;
|
||||
switch(config::system.emulation_speed) {
|
||||
case 0: max_framerate = unsigned(0.50 * max_framerate); break;
|
||||
case 1: max_framerate = unsigned(0.75 * max_framerate); break;
|
||||
case 2: break;
|
||||
case 3: max_framerate = unsigned(1.50 * max_framerate); break;
|
||||
case 4: max_framerate = unsigned(2.00 * max_framerate); break;
|
||||
case 5: max_framerate = 0; break;
|
||||
}
|
||||
|
||||
string output = (const char*)config::misc.status_text;
|
||||
replace(output, "%f", string() << (int)ppu.status.frames_executed);
|
||||
replace(output, "%m", string() << (int)max_framerate);
|
||||
replace(output, "%n", cartridge.info.filename);
|
||||
replace(output, "%t", cartridge.info.name);
|
||||
window_main.status.set_text(output);
|
||||
switch(device) { default:
|
||||
case 0: new_device = SNES::Input::DeviceNone; break;
|
||||
case 1: new_device = SNES::Input::DeviceJoypad; break;
|
||||
case 2: new_device = SNES::Input::DeviceMultitap; break;
|
||||
}
|
||||
|
||||
if(new_device != current_device) {
|
||||
snes.input.port_set_device(0, config::snes.controller_port1 = new_device);
|
||||
}
|
||||
|
||||
window_main.sync();
|
||||
}
|
||||
|
||||
void update_controller_port2(int device) {
|
||||
unsigned current_device = config::snes.controller_port2;
|
||||
unsigned new_device;
|
||||
|
||||
switch(device) { default:
|
||||
case 0: new_device = SNES::Input::DeviceNone; break;
|
||||
case 1: new_device = SNES::Input::DeviceJoypad; break;
|
||||
case 2: new_device = SNES::Input::DeviceMultitap; break;
|
||||
}
|
||||
|
||||
if(new_device != current_device) {
|
||||
snes.input.port_set_device(1, config::snes.controller_port2 = new_device);
|
||||
}
|
||||
|
||||
window_main.sync();
|
||||
}
|
||||
|
||||
void update_video_settings() {
|
||||
|
@ -313,20 +320,25 @@ void load_cart_normal(const char *filename) {
|
|||
if(cartridge.loaded() == true) cartridge.unload();
|
||||
cartridge.load_cart_normal(filename);
|
||||
|
||||
//warn if unsupported hardware detected
|
||||
string message = translate["Unsupported $ chip detected."];
|
||||
const char *name;
|
||||
if(cartridge.info.superfx) { name = "SuperFX"; replace(message, "$", name); alert(message); }
|
||||
if(cartridge.info.sa1) { name = "SA-1"; replace(message, "$", name); alert(message); }
|
||||
if(cartridge.info.st011) { name = "ST011"; replace(message, "$", name); alert(message); }
|
||||
if(cartridge.info.st018) { name = "ST018"; replace(message, "$", name); alert(message); }
|
||||
|
||||
app.pause = false;
|
||||
snes.power();
|
||||
window_main.menu_file_unload.enable();
|
||||
window_main.menu_file_reset.enable();
|
||||
window_main.menu_file_power.enable();
|
||||
window_cheat_editor.refresh();
|
||||
|
||||
status.flush();
|
||||
string t = translate["Loaded $."];
|
||||
replace(t, "$", cartridge.info.filename);
|
||||
status.enqueue(t);
|
||||
if(cartridge.info.patched) status.enqueue(translate["UPS patch applied."]);
|
||||
|
||||
//warn if unsupported hardware detected
|
||||
string message = translate["Warning: unsupported $ chip detected."];
|
||||
if(cartridge.info.superfx) { replace(message, "$", "SuperFX"); status.enqueue(message); }
|
||||
if(cartridge.info.sa1) { replace(message, "$", "SA-1"); status.enqueue(message); }
|
||||
if(cartridge.info.st011) { replace(message, "$", "ST011"); status.enqueue(message); }
|
||||
if(cartridge.info.st018) { replace(message, "$", "ST018"); status.enqueue(message); }
|
||||
}
|
||||
|
||||
void load_cart_bsx(const char *base, const char *slot) {
|
||||
|
@ -341,6 +353,11 @@ void load_cart_bsx(const char *base, const char *slot) {
|
|||
window_main.menu_file_reset.enable();
|
||||
window_main.menu_file_power.enable();
|
||||
window_cheat_editor.refresh();
|
||||
|
||||
status.flush();
|
||||
string t = translate["Loaded $."];
|
||||
replace(t, "$", cartridge.info.filename);
|
||||
status.enqueue(t);
|
||||
}
|
||||
|
||||
void load_cart_bsc(const char *base, const char *slot) {
|
||||
|
@ -355,6 +372,11 @@ void load_cart_bsc(const char *base, const char *slot) {
|
|||
window_main.menu_file_reset.enable();
|
||||
window_main.menu_file_power.enable();
|
||||
window_cheat_editor.refresh();
|
||||
|
||||
status.flush();
|
||||
string t = translate["Loaded $."];
|
||||
replace(t, "$", cartridge.info.filename);
|
||||
status.enqueue(t);
|
||||
}
|
||||
|
||||
void load_cart_st(const char *base, const char *slotA, const char *slotB) {
|
||||
|
@ -369,6 +391,11 @@ void load_cart_st(const char *base, const char *slotA, const char *slotB) {
|
|||
window_main.menu_file_reset.enable();
|
||||
window_main.menu_file_power.enable();
|
||||
window_cheat_editor.refresh();
|
||||
|
||||
status.flush();
|
||||
string t = translate["Loaded $."];
|
||||
replace(t, "$", cartridge.info.filename);
|
||||
status.enqueue(t);
|
||||
}
|
||||
|
||||
void unload_rom() {
|
||||
|
@ -377,31 +404,36 @@ void unload_rom() {
|
|||
video.clear();
|
||||
audio.clear();
|
||||
}
|
||||
event::update_status();
|
||||
window_main.menu_file_unload.disable();
|
||||
window_main.menu_file_reset.disable();
|
||||
window_main.menu_file_power.disable();
|
||||
window_cheat_editor.refresh();
|
||||
|
||||
status.flush();
|
||||
string t = translate["Unloaded $."];
|
||||
replace(t, "$", cartridge.info.filename);
|
||||
status.enqueue(t);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if(cartridge.loaded() == true) {
|
||||
snes.reset();
|
||||
dprintf("* Reset");
|
||||
status.flush();
|
||||
status.enqueue(translate["Reset"]);
|
||||
}
|
||||
}
|
||||
|
||||
void power() {
|
||||
if(cartridge.loaded() == true) {
|
||||
snes.power();
|
||||
dprintf("* Power");
|
||||
status.flush();
|
||||
status.enqueue(translate["Power cycle"]);
|
||||
}
|
||||
}
|
||||
|
||||
void quit() {
|
||||
app.term = true;
|
||||
window_about.hide();
|
||||
window_message.hide();
|
||||
window_settings.hide();
|
||||
window_bsxloader.hide();
|
||||
window_stloader.hide();
|
||||
|
|
|
@ -23,7 +23,9 @@ void update_software_filter(uint);
|
|||
void update_frameskip(int);
|
||||
void update_emulation_speed(int);
|
||||
|
||||
void update_status();
|
||||
void update_controller_port1(int);
|
||||
void update_controller_port2(int);
|
||||
|
||||
void update_video_settings();
|
||||
void update_opacity();
|
||||
void toggle_fullscreen();
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
class InputManager {
|
||||
public:
|
||||
public:
|
||||
// 0 = Joypad 1
|
||||
// 1 = Joypad 2
|
||||
//2-5 = Multitap 1
|
||||
//6-9 = Multitap 2
|
||||
struct Joypad {
|
||||
struct Button {
|
||||
uint16_t value;
|
||||
bool state;
|
||||
} up, down, left, right, a, b, x, y, l, r, select, start;
|
||||
} joypad1, joypad2;
|
||||
} joypad[10];
|
||||
|
||||
struct GUI {
|
||||
uint16_t load;
|
||||
|
@ -24,7 +28,7 @@ public:
|
|||
|
||||
void bind();
|
||||
void poll();
|
||||
bool get_status(uint device, uint button);
|
||||
bool get_status(unsigned deviceid, unsigned id);
|
||||
|
||||
void refresh();
|
||||
function<void (uint16_t)> on_keydown;
|
||||
|
@ -53,41 +57,39 @@ void InputManager::refresh() {
|
|||
}
|
||||
|
||||
void InputManager::bind() {
|
||||
joypad1.up.value = input_find(config::input.joypad1.up);
|
||||
joypad1.down.value = input_find(config::input.joypad1.down);
|
||||
joypad1.left.value = input_find(config::input.joypad1.left);
|
||||
joypad1.right.value = input_find(config::input.joypad1.right);
|
||||
joypad1.a.value = input_find(config::input.joypad1.a);
|
||||
joypad1.b.value = input_find(config::input.joypad1.b);
|
||||
joypad1.x.value = input_find(config::input.joypad1.x);
|
||||
joypad1.y.value = input_find(config::input.joypad1.y);
|
||||
joypad1.l.value = input_find(config::input.joypad1.l);
|
||||
joypad1.r.value = input_find(config::input.joypad1.r);
|
||||
joypad1.select.value = input_find(config::input.joypad1.select);
|
||||
joypad1.start.value = input_find(config::input.joypad1.start);
|
||||
|
||||
joypad1.up.state = joypad1.down.state = joypad1.left.state = joypad1.right.state =
|
||||
joypad1.a.state = joypad1.b.state = joypad1.x.state = joypad1.y.state =
|
||||
joypad1.l.state = joypad1.r.state = joypad1.select.state = joypad1.start.state =
|
||||
false;
|
||||
#define map(i, n) \
|
||||
joypad[i].up.value = input_find(config::input.n.up); \
|
||||
joypad[i].down.value = input_find(config::input.n.down); \
|
||||
joypad[i].left.value = input_find(config::input.n.left); \
|
||||
joypad[i].right.value = input_find(config::input.n.right); \
|
||||
joypad[i].a.value = input_find(config::input.n.a); \
|
||||
joypad[i].b.value = input_find(config::input.n.b); \
|
||||
joypad[i].x.value = input_find(config::input.n.x); \
|
||||
joypad[i].y.value = input_find(config::input.n.y); \
|
||||
joypad[i].l.value = input_find(config::input.n.l); \
|
||||
joypad[i].r.value = input_find(config::input.n.r); \
|
||||
joypad[i].select.value = input_find(config::input.n.select); \
|
||||
joypad[i].start.value = input_find(config::input.n.start);
|
||||
|
||||
joypad2.up.value = input_find(config::input.joypad2.up);
|
||||
joypad2.down.value = input_find(config::input.joypad2.down);
|
||||
joypad2.left.value = input_find(config::input.joypad2.left);
|
||||
joypad2.right.value = input_find(config::input.joypad2.right);
|
||||
joypad2.a.value = input_find(config::input.joypad2.a);
|
||||
joypad2.b.value = input_find(config::input.joypad2.b);
|
||||
joypad2.x.value = input_find(config::input.joypad2.x);
|
||||
joypad2.y.value = input_find(config::input.joypad2.y);
|
||||
joypad2.l.value = input_find(config::input.joypad2.l);
|
||||
joypad2.r.value = input_find(config::input.joypad2.r);
|
||||
joypad2.select.value = input_find(config::input.joypad2.select);
|
||||
joypad2.start.value = input_find(config::input.joypad2.start);
|
||||
map(0, joypad1)
|
||||
map(1, joypad2)
|
||||
map(2, multitap1a)
|
||||
map(3, multitap1b)
|
||||
map(4, multitap1c)
|
||||
map(5, multitap1d)
|
||||
map(6, multitap2a)
|
||||
map(7, multitap2b)
|
||||
map(8, multitap2c)
|
||||
map(9, multitap2d)
|
||||
|
||||
joypad2.up.state = joypad2.down.state = joypad2.left.state = joypad2.right.state =
|
||||
joypad2.a.state = joypad2.b.state = joypad2.x.state = joypad2.y.state =
|
||||
joypad2.l.state = joypad2.r.state = joypad2.select.state = joypad2.start.state =
|
||||
false;
|
||||
#undef map
|
||||
|
||||
for(unsigned i = 0; i < 10; i++) {
|
||||
joypad[i].up.state = joypad[i].down.state = joypad[i].left.state = joypad[i].right.state =
|
||||
joypad[i].a.state = joypad[i].b.state = joypad[i].x.state = joypad[i].y.state =
|
||||
joypad[i].l.state = joypad[i].r.state = joypad[i].select.state = joypad[i].start.state =
|
||||
false;
|
||||
}
|
||||
|
||||
gui.load = input_find(config::input.gui.load);
|
||||
gui.pause = input_find(config::input.gui.pause);
|
||||
|
@ -104,68 +106,55 @@ void InputManager::bind() {
|
|||
}
|
||||
|
||||
void InputManager::poll() {
|
||||
joypad1.up.state = input.key_down(joypad1.up.value);
|
||||
joypad1.down.state = input.key_down(joypad1.down.value);
|
||||
joypad1.left.state = input.key_down(joypad1.left.value);
|
||||
joypad1.right.state = input.key_down(joypad1.right.value);
|
||||
joypad1.a.state = input.key_down(joypad1.a.value);
|
||||
joypad1.b.state = input.key_down(joypad1.b.value);
|
||||
joypad1.x.state = input.key_down(joypad1.x.value);
|
||||
joypad1.y.state = input.key_down(joypad1.y.value);
|
||||
joypad1.l.state = input.key_down(joypad1.l.value);
|
||||
joypad1.r.state = input.key_down(joypad1.r.value);
|
||||
joypad1.select.state = input.key_down(joypad1.select.value);
|
||||
joypad1.start.state = input.key_down(joypad1.start.value);
|
||||
|
||||
joypad2.up.state = input.key_down(joypad2.up.value);
|
||||
joypad2.down.state = input.key_down(joypad2.down.value);
|
||||
joypad2.left.state = input.key_down(joypad2.left.value);
|
||||
joypad2.right.state = input.key_down(joypad2.right.value);
|
||||
joypad2.a.state = input.key_down(joypad2.a.value);
|
||||
joypad2.b.state = input.key_down(joypad2.b.value);
|
||||
joypad2.x.state = input.key_down(joypad2.x.value);
|
||||
joypad2.y.state = input.key_down(joypad2.y.value);
|
||||
joypad2.l.state = input.key_down(joypad2.l.value);
|
||||
joypad2.r.state = input.key_down(joypad2.r.value);
|
||||
joypad2.select.state = input.key_down(joypad2.select.value);
|
||||
joypad2.start.state = input.key_down(joypad2.start.value);
|
||||
for(unsigned i = 0; i < 10; i++) {
|
||||
joypad[i].up.state = input.key_down(joypad[i].up.value);
|
||||
joypad[i].down.state = input.key_down(joypad[i].down.value);
|
||||
joypad[i].left.state = input.key_down(joypad[i].left.value);
|
||||
joypad[i].right.state = input.key_down(joypad[i].right.value);
|
||||
joypad[i].a.state = input.key_down(joypad[i].a.value);
|
||||
joypad[i].b.state = input.key_down(joypad[i].b.value);
|
||||
joypad[i].x.state = input.key_down(joypad[i].x.value);
|
||||
joypad[i].y.state = input.key_down(joypad[i].y.value);
|
||||
joypad[i].l.state = input.key_down(joypad[i].l.value);
|
||||
joypad[i].r.state = input.key_down(joypad[i].r.value);
|
||||
joypad[i].select.state = input.key_down(joypad[i].select.value);
|
||||
joypad[i].start.state = input.key_down(joypad[i].start.value);
|
||||
}
|
||||
}
|
||||
|
||||
bool InputManager::get_status(uint device, uint button) {
|
||||
switch(device) {
|
||||
case SNES::Input::DeviceIDJoypad1: {
|
||||
switch(button) {
|
||||
case SNES::Input::JoypadUp: return joypad1.up.state;
|
||||
case SNES::Input::JoypadDown: return joypad1.down.state;
|
||||
case SNES::Input::JoypadLeft: return joypad1.left.state;
|
||||
case SNES::Input::JoypadRight: return joypad1.right.state;
|
||||
case SNES::Input::JoypadA: return joypad1.a.state;
|
||||
case SNES::Input::JoypadB: return joypad1.b.state;
|
||||
case SNES::Input::JoypadX: return joypad1.x.state;
|
||||
case SNES::Input::JoypadY: return joypad1.y.state;
|
||||
case SNES::Input::JoypadL: return joypad1.l.state;
|
||||
case SNES::Input::JoypadR: return joypad1.r.state;
|
||||
case SNES::Input::JoypadSelect: return joypad1.select.state;
|
||||
case SNES::Input::JoypadStart: return joypad1.start.state;
|
||||
}
|
||||
} break;
|
||||
bool InputManager::get_status(unsigned deviceid, unsigned id) {
|
||||
//=======
|
||||
//Joypads
|
||||
//=======
|
||||
int index = -1;
|
||||
switch(deviceid) {
|
||||
case SNES::Input::DeviceIDJoypad1: index = 0; break;
|
||||
case SNES::Input::DeviceIDJoypad2: index = 1; break;
|
||||
case SNES::Input::DeviceIDMultitap1A: index = 2; break;
|
||||
case SNES::Input::DeviceIDMultitap1B: index = 3; break;
|
||||
case SNES::Input::DeviceIDMultitap1C: index = 4; break;
|
||||
case SNES::Input::DeviceIDMultitap1D: index = 5; break;
|
||||
case SNES::Input::DeviceIDMultitap2A: index = 6; break;
|
||||
case SNES::Input::DeviceIDMultitap2B: index = 7; break;
|
||||
case SNES::Input::DeviceIDMultitap2C: index = 8; break;
|
||||
case SNES::Input::DeviceIDMultitap2D: index = 9; break;
|
||||
}
|
||||
|
||||
case SNES::Input::DeviceIDJoypad2: {
|
||||
switch(button) {
|
||||
case SNES::Input::JoypadUp: return joypad2.up.state;
|
||||
case SNES::Input::JoypadDown: return joypad2.down.state;
|
||||
case SNES::Input::JoypadLeft: return joypad2.left.state;
|
||||
case SNES::Input::JoypadRight: return joypad2.right.state;
|
||||
case SNES::Input::JoypadA: return joypad2.a.state;
|
||||
case SNES::Input::JoypadB: return joypad2.b.state;
|
||||
case SNES::Input::JoypadX: return joypad2.x.state;
|
||||
case SNES::Input::JoypadY: return joypad2.y.state;
|
||||
case SNES::Input::JoypadL: return joypad2.l.state;
|
||||
case SNES::Input::JoypadR: return joypad2.r.state;
|
||||
case SNES::Input::JoypadSelect: return joypad2.select.state;
|
||||
case SNES::Input::JoypadStart: return joypad2.start.state;
|
||||
}
|
||||
} break;
|
||||
if(index >= 0) {
|
||||
switch(id) {
|
||||
case SNES::Input::JoypadUp: return joypad[index].up.state;
|
||||
case SNES::Input::JoypadDown: return joypad[index].down.state;
|
||||
case SNES::Input::JoypadLeft: return joypad[index].left.state;
|
||||
case SNES::Input::JoypadRight: return joypad[index].right.state;
|
||||
case SNES::Input::JoypadA: return joypad[index].a.state;
|
||||
case SNES::Input::JoypadB: return joypad[index].b.state;
|
||||
case SNES::Input::JoypadX: return joypad[index].x.state;
|
||||
case SNES::Input::JoypadY: return joypad[index].y.state;
|
||||
case SNES::Input::JoypadL: return joypad[index].l.state;
|
||||
case SNES::Input::JoypadR: return joypad[index].r.state;
|
||||
case SNES::Input::JoypadSelect: return joypad[index].select.state;
|
||||
case SNES::Input::JoypadStart: return joypad[index].start.state;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -49,8 +49,8 @@ void SNESInterface::input_poll() {
|
|||
input_manager.poll();
|
||||
}
|
||||
|
||||
bool SNESInterface::input_poll(uint deviceid, uint button) {
|
||||
return input_manager.get_status(deviceid, button);
|
||||
bool SNESInterface::input_poll(unsigned deviceid, unsigned id) {
|
||||
return input_manager.get_status(deviceid, id);
|
||||
}
|
||||
|
||||
//core
|
||||
|
|
|
@ -32,9 +32,11 @@ using namespace libhiro;
|
|||
*****/
|
||||
|
||||
#include "ui.h"
|
||||
#include "status.h"
|
||||
#include "event.h"
|
||||
|
||||
#include "ui.cpp"
|
||||
#include "status.cpp"
|
||||
#include "event.cpp"
|
||||
|
||||
void alert(const char *s, ...) {
|
||||
|
@ -44,7 +46,7 @@ void alert(const char *s, ...) {
|
|||
vsprintf(str, s, args);
|
||||
va_end(args);
|
||||
|
||||
window_message.show(str);
|
||||
status.enqueue(str);
|
||||
}
|
||||
|
||||
void dprintf(const char *s, ...) {
|
||||
|
@ -56,15 +58,6 @@ void dprintf(const char *s, ...) {
|
|||
fprintf(stdout, "%s\r\n", str);
|
||||
}
|
||||
|
||||
void dprintf(uint source, const char *s, ...) {
|
||||
char str[4096];
|
||||
va_list args;
|
||||
va_start(args, s);
|
||||
vsprintf(str, s, args);
|
||||
va_end(args);
|
||||
fprintf(stdout, "[%d]: %s\r\n", source, str);
|
||||
}
|
||||
|
||||
void get_paths(const char *image) {
|
||||
char temp[PATH_MAX] = "";
|
||||
realpath(image, temp);
|
||||
|
@ -103,7 +96,6 @@ void set_config_filenames() {
|
|||
strcat(filename, "/bsnes.cfg");
|
||||
}
|
||||
strcpy(config::bsnes_cfg, filename);
|
||||
fprintf(stdout, "Config file: %s\n", config::bsnes_cfg);
|
||||
|
||||
//locate locale.cfg
|
||||
strcpy(filename, config::path.base);
|
||||
|
@ -115,11 +107,11 @@ void set_config_filenames() {
|
|||
strcat(filename, "/locale.cfg");
|
||||
}
|
||||
strcpy(config::locale_cfg, filename);
|
||||
fprintf(stdout, "Locale file: %s\n", config::locale_cfg);
|
||||
}
|
||||
|
||||
void run() {
|
||||
while(hiro().pending()) hiro().run();
|
||||
status.update();
|
||||
input_manager.refresh();
|
||||
|
||||
if(config::input.capture_mode == 2) {
|
||||
|
@ -127,7 +119,6 @@ void run() {
|
|||
if(app.autopause == false && inactive == true) {
|
||||
app.autopause = true;
|
||||
audio.clear();
|
||||
if(cartridge.loaded()) event::update_status();
|
||||
} else if(app.autopause == true && inactive == false) {
|
||||
app.autopause = false;
|
||||
}
|
||||
|
@ -140,7 +131,6 @@ void run() {
|
|||
usleep(20 * 1000);
|
||||
} else {
|
||||
snes.runtoframe();
|
||||
event::update_status();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,9 +55,8 @@ void AdvancedWindow::load() {
|
|||
string name = config::config().list[i]->name;
|
||||
|
||||
//blacklist (omit/hide options that can be configured through the standard UI)
|
||||
if(strbegin(name, "file.")) continue;
|
||||
if(strbegin(name, "path.")) continue;
|
||||
if(strbegin(name, "snes.controller_port_")) continue;
|
||||
if(strbegin(name, "snes.controller_port")) continue;
|
||||
if(strpos(name, "colorfilter.") >= 0) continue;
|
||||
if(name == "misc.status_enable") continue;
|
||||
if(name == "system.emulation_speed") continue;
|
||||
|
@ -66,6 +65,7 @@ void AdvancedWindow::load() {
|
|||
if(name == "audio.mute") continue;
|
||||
if(name == "input.capture_mode") continue;
|
||||
if(strbegin(name, "input.joypad")) continue;
|
||||
if(strbegin(name, "input.multitap")) continue;
|
||||
if(strbegin(name, "input.gui")) continue;
|
||||
|
||||
string value_, default_;
|
||||
|
|
|
@ -12,43 +12,114 @@ void InputConfigWindow::setup() {
|
|||
capture_focus.create (group, 0, 155, 18, translate["Ignore input"]);
|
||||
capture_pause.create (group, 0, 155, 18, translate["Pause emulation"]);
|
||||
|
||||
list.create(Listbox::Header | Listbox::VerticalScrollAlways, 475, 284, string() << translate["Name"] << "\t" << translate["Value"]);
|
||||
config_type.create(0, 235, 25);
|
||||
config_type.add_item(translate["Controller Port 1"]);
|
||||
config_type.add_item(translate["Controller Port 2"]);
|
||||
config_type.add_item(translate["User Interface"]);
|
||||
config_type.set_selection(0);
|
||||
|
||||
config_subtype.create(0, 235, 25);
|
||||
refresh_subtype();
|
||||
|
||||
list.create(Listbox::Header | Listbox::VerticalScrollAlways, 475, 254, string() << translate["Name"] << "\t" << translate["Value"]);
|
||||
setkey.create(0, 235, 25, translate["Assign Key"]);
|
||||
setkey.disable();
|
||||
clrkey.create(0, 235, 25, translate["Unassign Key"]);
|
||||
clrkey.disable();
|
||||
|
||||
unsigned y = 0;
|
||||
attach(capture_mode, 0, y); y += 18;
|
||||
attach(capture_always, 0, y);
|
||||
attach(capture_focus, 160, y);
|
||||
attach(capture_pause, 320, y); y += 18 + 5;
|
||||
attach(list, 0, y); y += 284 + 5;
|
||||
attach(setkey, 0, y);
|
||||
attach(clrkey, 240, y); y += 25 + 5;
|
||||
attach(capture_mode, 0, y); y += 18;
|
||||
attach(capture_always, 0, y);
|
||||
attach(capture_focus, 160, y);
|
||||
attach(capture_pause, 320, y); y += 18 + 5;
|
||||
attach(config_type, 0, y);
|
||||
attach(config_subtype, 240, y); y += 25 + 5;
|
||||
attach(list, 0, y); y += 254 + 5;
|
||||
attach(setkey, 0, y);
|
||||
attach(clrkey, 240, y); y += 25 + 5;
|
||||
|
||||
capture_always.on_tick = bind(&InputConfigWindow::capture_change, this);
|
||||
capture_focus.on_tick = bind(&InputConfigWindow::capture_change, this);
|
||||
capture_pause.on_tick = bind(&InputConfigWindow::capture_change, this);
|
||||
list.on_change = bind(&InputConfigWindow::list_change, this);
|
||||
list.on_activate = bind(&InputConfigWindow::set_tick, this);
|
||||
setkey.on_tick = bind(&InputConfigWindow::set_tick, this);
|
||||
clrkey.on_tick = bind(&InputConfigWindow::clr_tick, this);
|
||||
capture_always.on_tick = bind(&InputConfigWindow::capture_change, this);
|
||||
capture_focus.on_tick = bind(&InputConfigWindow::capture_change, this);
|
||||
capture_pause.on_tick = bind(&InputConfigWindow::capture_change, this);
|
||||
config_type.on_change = bind(&InputConfigWindow::type_change, this);
|
||||
config_subtype.on_change = bind(&InputConfigWindow::subtype_change, this);
|
||||
list.on_change = bind(&InputConfigWindow::list_change, this);
|
||||
list.on_activate = bind(&InputConfigWindow::set_tick, this);
|
||||
setkey.on_tick = bind(&InputConfigWindow::set_tick, this);
|
||||
clrkey.on_tick = bind(&InputConfigWindow::clr_tick, this);
|
||||
|
||||
if(config::input.capture_mode == 1) capture_focus.check();
|
||||
else if(config::input.capture_mode == 2) capture_pause.check();
|
||||
else config::input.capture_mode = 0; //capture_always
|
||||
|
||||
for(unsigned i = 0; i < inputcount; i++) list.add_item("???\t???");
|
||||
refresh_list();
|
||||
window_input_capture.setup();
|
||||
}
|
||||
|
||||
InputConfigWindow::InputType InputConfigWindow::get_input_type(unsigned &length) {
|
||||
unsigned type = config_type.get_selection();
|
||||
unsigned subtype = config_subtype.get_selection();
|
||||
|
||||
switch(type) {
|
||||
case 0: {
|
||||
switch(subtype) {
|
||||
case 0: length = 12; return Port1_Joypad;
|
||||
case 1: length = 12; return Port1_Multitap1;
|
||||
case 2: length = 12; return Port1_Multitap2;
|
||||
case 3: length = 12; return Port1_Multitap3;
|
||||
case 4: length = 12; return Port1_Multitap4;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 1: {
|
||||
switch(subtype) {
|
||||
case 0: length = 12; return Port2_Joypad;
|
||||
case 1: length = 12; return Port2_Multitap1;
|
||||
case 2: length = 12; return Port2_Multitap2;
|
||||
case 3: length = 12; return Port2_Multitap3;
|
||||
case 4: length = 12; return Port2_Multitap4;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 2: {
|
||||
switch(subtype) {
|
||||
case 0: length = 12; return UI_General;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
return TypeUnknown;
|
||||
}
|
||||
|
||||
void InputConfigWindow::refresh_subtype() {
|
||||
config_subtype.reset();
|
||||
|
||||
switch(config_type.get_selection()) {
|
||||
case 0:
|
||||
case 1: {
|
||||
config_subtype.add_item(translate["Joypad"]);
|
||||
config_subtype.add_item(translate["Multitap Port 1"]);
|
||||
config_subtype.add_item(translate["Multitap Port 2"]);
|
||||
config_subtype.add_item(translate["Multitap Port 3"]);
|
||||
config_subtype.add_item(translate["Multitap Port 4"]);
|
||||
} break;
|
||||
|
||||
case 2: {
|
||||
config_subtype.add_item(translate["General"]);
|
||||
} break;
|
||||
}
|
||||
|
||||
config_subtype.set_selection(0);
|
||||
}
|
||||
|
||||
void InputConfigWindow::refresh_list() {
|
||||
for(unsigned i = 0; i < inputcount; i++) {
|
||||
list.reset();
|
||||
unsigned length;
|
||||
get_input_type(length);
|
||||
for(unsigned i = 0; i < length; i++) {
|
||||
const char *name;
|
||||
acquire(i, name);
|
||||
list.set_item(i, string() << name << "\t" << input_find(get_value(i)));
|
||||
list.add_item(string() << name << "\t" << input_find(get_value(i)));
|
||||
}
|
||||
list.autosize_columns();
|
||||
}
|
||||
|
@ -60,6 +131,17 @@ uintptr_t InputConfigWindow::capture_change(Event e) {
|
|||
return true;
|
||||
}
|
||||
|
||||
uintptr_t InputConfigWindow::type_change(Event) {
|
||||
refresh_subtype();
|
||||
refresh_list();
|
||||
return true;
|
||||
}
|
||||
|
||||
uintptr_t InputConfigWindow::subtype_change(Event) {
|
||||
refresh_list();
|
||||
return true;
|
||||
}
|
||||
|
||||
uintptr_t InputConfigWindow::list_change(Event) {
|
||||
int pos = list.get_selection();
|
||||
setkey.enable(pos >= 0);
|
||||
|
@ -69,14 +151,14 @@ uintptr_t InputConfigWindow::list_change(Event) {
|
|||
|
||||
uintptr_t InputConfigWindow::set_tick(Event) {
|
||||
int pos = list.get_selection();
|
||||
if(pos < 0 || pos >= inputcount) return true;
|
||||
if(pos < 0) return true;
|
||||
window_input_capture.index = pos;
|
||||
string message = translate["Press a key to assign to $ ..."];
|
||||
const char *name;
|
||||
acquire(pos, name);
|
||||
replace(message, "$", name);
|
||||
window_input_capture.label.set_text(message);
|
||||
window_input_capture.canvas.show(pos < 24); //only show joypad graphic if setting joypad 1/2 button
|
||||
window_input_capture.canvas.show(config_type.get_selection() < 2); //only show joypad graphic if setting joypad button
|
||||
window_input_capture.show();
|
||||
return true;
|
||||
}
|
||||
|
@ -130,54 +212,63 @@ InputCaptureWindow::InputCaptureWindow() {
|
|||
/* Misc */
|
||||
|
||||
string_setting& InputConfigWindow::acquire(unsigned index, const char *&name) {
|
||||
switch(index) {
|
||||
case 0: name = translate["Joypad 1 Up"]; return config::input.joypad1.up;
|
||||
case 1: name = translate["Joypad 1 Down"]; return config::input.joypad1.down;
|
||||
case 2: name = translate["Joypad 1 Left"]; return config::input.joypad1.left;
|
||||
case 3: name = translate["Joypad 1 Right"]; return config::input.joypad1.right;
|
||||
case 4: name = translate["Joypad 1 A"]; return config::input.joypad1.a;
|
||||
case 5: name = translate["Joypad 1 B"]; return config::input.joypad1.b;
|
||||
case 6: name = translate["Joypad 1 X"]; return config::input.joypad1.x;
|
||||
case 7: name = translate["Joypad 1 Y"]; return config::input.joypad1.y;
|
||||
case 8: name = translate["Joypad 1 L"]; return config::input.joypad1.l;
|
||||
case 9: name = translate["Joypad 1 R"]; return config::input.joypad1.r;
|
||||
case 10: name = translate["Joypad 1 Select"]; return config::input.joypad1.select;
|
||||
case 11: name = translate["Joypad 1 Start"]; return config::input.joypad1.start;
|
||||
#define map(n, lname) \
|
||||
case n: { \
|
||||
switch(index) { \
|
||||
case 0: name = translate["Up"]; return config::input.lname.up; \
|
||||
case 1: name = translate["Down"]; return config::input.lname.down; \
|
||||
case 2: name = translate["Left"]; return config::input.lname.left; \
|
||||
case 3: name = translate["Right"]; return config::input.lname.right; \
|
||||
case 4: name = translate["A"]; return config::input.lname.a; \
|
||||
case 5: name = translate["B"]; return config::input.lname.b; \
|
||||
case 6: name = translate["X"]; return config::input.lname.x; \
|
||||
case 7: name = translate["Y"]; return config::input.lname.y; \
|
||||
case 8: name = translate["L"]; return config::input.lname.l; \
|
||||
case 9: name = translate["R"]; return config::input.lname.r; \
|
||||
case 10: name = translate["Select"]; return config::input.lname.select; \
|
||||
case 11: name = translate["Start"]; return config::input.lname.start; \
|
||||
} \
|
||||
} break;
|
||||
|
||||
case 12: name = translate["Joypad 2 Up"]; return config::input.joypad2.up;
|
||||
case 13: name = translate["Joypad 2 Down"]; return config::input.joypad2.down;
|
||||
case 14: name = translate["Joypad 2 Left"]; return config::input.joypad2.left;
|
||||
case 15: name = translate["Joypad 2 Right"]; return config::input.joypad2.right;
|
||||
case 16: name = translate["Joypad 2 A"]; return config::input.joypad2.a;
|
||||
case 17: name = translate["Joypad 2 B"]; return config::input.joypad2.b;
|
||||
case 18: name = translate["Joypad 2 X"]; return config::input.joypad2.x;
|
||||
case 19: name = translate["Joypad 2 Y"]; return config::input.joypad2.y;
|
||||
case 20: name = translate["Joypad 2 L"]; return config::input.joypad2.l;
|
||||
case 21: name = translate["Joypad 2 R"]; return config::input.joypad2.r;
|
||||
case 22: name = translate["Joypad 2 Select"]; return config::input.joypad2.select;
|
||||
case 23: name = translate["Joypad 2 Start"]; return config::input.joypad2.start;
|
||||
unsigned length;
|
||||
switch(get_input_type(length)) { default:
|
||||
map(Port1_Joypad, joypad1)
|
||||
map(Port1_Multitap1, multitap1a)
|
||||
map(Port1_Multitap2, multitap1b)
|
||||
map(Port1_Multitap3, multitap1c)
|
||||
map(Port1_Multitap4, multitap1d)
|
||||
|
||||
case 24: name = translate["Load Cartridge"]; return config::input.gui.load;
|
||||
case 25: name = translate["Pause Emulation"]; return config::input.gui.pause;
|
||||
case 26: name = translate["Reset System"]; return config::input.gui.reset;
|
||||
case 27: name = translate["Power Cycle System"]; return config::input.gui.power;
|
||||
case 28: name = translate["Exit Emulator"]; return config::input.gui.quit;
|
||||
case 29: name = translate["Emulation Speed Decrease"]; return config::input.gui.speed_decrease;
|
||||
case 30: name = translate["Emulation Speed Increase"]; return config::input.gui.speed_increase;
|
||||
case 31: name = translate["Frameskip Decrease"]; return config::input.gui.frameskip_decrease;
|
||||
case 32: name = translate["Frameskip Increase"]; return config::input.gui.frameskip_increase;
|
||||
case 33: name = translate["Toggle Fullscreen"]; return config::input.gui.toggle_fullscreen;
|
||||
case 34: name = translate["Toggle Menubar"]; return config::input.gui.toggle_menubar;
|
||||
case 35: name = translate["Toggle Statusbar"]; return config::input.gui.toggle_statusbar;
|
||||
map(Port2_Joypad, joypad2)
|
||||
map(Port2_Multitap1, multitap2a)
|
||||
map(Port2_Multitap2, multitap2b)
|
||||
map(Port2_Multitap3, multitap2c)
|
||||
map(Port2_Multitap4, multitap2d)
|
||||
|
||||
case UI_General: {
|
||||
switch(index) {
|
||||
case 0: name = translate["Load Cartridge"]; return config::input.gui.load;
|
||||
case 1: name = translate["Pause Emulation"]; return config::input.gui.pause;
|
||||
case 2: name = translate["Reset System"]; return config::input.gui.reset;
|
||||
case 3: name = translate["Power Cycle System"]; return config::input.gui.power;
|
||||
case 4: name = translate["Exit Emulator"]; return config::input.gui.quit;
|
||||
case 5: name = translate["Emulation Speed Decrease"]; return config::input.gui.speed_decrease;
|
||||
case 6: name = translate["Emulation Speed Increase"]; return config::input.gui.speed_increase;
|
||||
case 7: name = translate["Frameskip Decrease"]; return config::input.gui.frameskip_decrease;
|
||||
case 8: name = translate["Frameskip Increase"]; return config::input.gui.frameskip_increase;
|
||||
case 9: name = translate["Toggle Fullscreen"]; return config::input.gui.toggle_fullscreen;
|
||||
case 10: name = translate["Toggle Menubar"]; return config::input.gui.toggle_menubar;
|
||||
case 11: name = translate["Toggle Statusbar"]; return config::input.gui.toggle_statusbar;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
#undef map
|
||||
|
||||
name = "";
|
||||
static string_setting notfound("", "", "");
|
||||
return notfound;
|
||||
}
|
||||
|
||||
const int InputConfigWindow::inputcount = 36;
|
||||
|
||||
uint InputConfigWindow::get_value(uint index) {
|
||||
const char *name;
|
||||
return input_find(acquire(index, name));
|
||||
|
|
|
@ -4,20 +4,44 @@ public:
|
|||
Radiobox capture_always;
|
||||
Radiobox capture_focus;
|
||||
Radiobox capture_pause;
|
||||
Combobox config_type;
|
||||
Combobox config_subtype;
|
||||
Listbox list;
|
||||
Button setkey;
|
||||
Button clrkey;
|
||||
|
||||
void setup();
|
||||
void refresh_subtype();
|
||||
void refresh_list();
|
||||
|
||||
enum InputType {
|
||||
TypeUnknown,
|
||||
|
||||
Port1_Joypad,
|
||||
Port1_Multitap1,
|
||||
Port1_Multitap2,
|
||||
Port1_Multitap3,
|
||||
Port1_Multitap4,
|
||||
|
||||
Port2_Joypad,
|
||||
Port2_Multitap1,
|
||||
Port2_Multitap2,
|
||||
Port2_Multitap3,
|
||||
Port2_Multitap4,
|
||||
|
||||
UI_General,
|
||||
};
|
||||
|
||||
InputType get_input_type(unsigned &length);
|
||||
|
||||
uintptr_t capture_change(Event);
|
||||
uintptr_t type_change(Event);
|
||||
uintptr_t subtype_change(Event);
|
||||
uintptr_t list_change(Event);
|
||||
uintptr_t set_tick(Event);
|
||||
uintptr_t clr_tick(Event);
|
||||
|
||||
string_setting& acquire(unsigned index, const char *&name);
|
||||
static const int inputcount;
|
||||
string_setting& acquire(unsigned index, const char *&name);
|
||||
uint get_value(uint index);
|
||||
void set_value(uint index, uint16 value);
|
||||
} window_input_config;
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
//========
|
||||
//ROM path
|
||||
//========
|
||||
|
||||
uintptr_t PathSettingsWindow::selectpath_rom(Event) {
|
||||
char t[PATH_MAX];
|
||||
if(hiro().folder_select(&window_settings, t) == true) {
|
||||
|
@ -13,10 +17,9 @@ uintptr_t PathSettingsWindow::defaultpath_rom(Event) {
|
|||
return true;
|
||||
}
|
||||
|
||||
uintptr_t PathSettingsWindow::autodetect_tick(Event) {
|
||||
config::file.autodetect_type = autodetect.checked();
|
||||
return true;
|
||||
}
|
||||
//==============
|
||||
//UPS patch path
|
||||
//==============
|
||||
|
||||
uintptr_t PathSettingsWindow::selectpath_patch(Event) {
|
||||
char t[PATH_MAX];
|
||||
|
@ -33,10 +36,9 @@ uintptr_t PathSettingsWindow::defaultpath_patch(Event) {
|
|||
return true;
|
||||
}
|
||||
|
||||
uintptr_t PathSettingsWindow::bypass_crc32_tick(Event) {
|
||||
config::file.bypass_patch_crc32 = bypass_crc32.checked();
|
||||
return true;
|
||||
}
|
||||
//=============
|
||||
//save RAM path
|
||||
//=============
|
||||
|
||||
uintptr_t PathSettingsWindow::selectpath_save(Event) {
|
||||
char t[PATH_MAX];
|
||||
|
@ -53,6 +55,10 @@ uintptr_t PathSettingsWindow::defaultpath_save(Event) {
|
|||
return true;
|
||||
}
|
||||
|
||||
//==========
|
||||
//cheat path
|
||||
//==========
|
||||
|
||||
uintptr_t PathSettingsWindow::selectpath_cheat(Event) {
|
||||
char t[PATH_MAX];
|
||||
if(hiro().folder_select(&window_settings, t) == true) {
|
||||
|
@ -75,13 +81,11 @@ void PathSettingsWindow::setup() {
|
|||
rompath.create(Editbox::Readonly, 265, 25);
|
||||
romselect.create(0, 100, 25, translate["Select"]);
|
||||
romdefault.create(0, 100, 25, translate["Default"]);
|
||||
autodetect.create(0, 475, 18, translate["Auto-detect file compression type (ignore file extension)"]);
|
||||
|
||||
lpatchpath.create(0, 475, 18, translate["Default UPS patch path:"]);
|
||||
patchpath.create(Editbox::Readonly, 265, 25);
|
||||
patchselect.create(0, 100, 25, translate["Select"]);
|
||||
patchdefault.create(0, 100, 25, translate["Default"]);
|
||||
bypass_crc32.create(0, 475, 18, translate["Bypass CRC32 patch validation (not recommended)"]);
|
||||
|
||||
lsavepath.create(0, 475, 18, translate["Default save RAM path:"]);
|
||||
savepath.create(Editbox::Readonly, 265, 25);
|
||||
|
@ -97,14 +101,12 @@ void PathSettingsWindow::setup() {
|
|||
attach(lrompath, 0, y); y += 18;
|
||||
attach(rompath, 0, y);
|
||||
attach(romselect, 270, y);
|
||||
attach(romdefault, 375, y); y += 25;
|
||||
attach(autodetect, 0, y); y += 18 + 5;
|
||||
attach(romdefault, 375, y); y += 25 + 5;
|
||||
|
||||
attach(lpatchpath, 0, y); y += 18;
|
||||
attach(patchpath, 0, y);
|
||||
attach(patchselect, 270, y);
|
||||
attach(patchdefault, 375, y); y += 25;
|
||||
attach(bypass_crc32, 0, y); y += 18 + 5;
|
||||
attach(patchdefault, 375, y); y += 25 + 5;
|
||||
|
||||
attach(lsavepath, 0, y); y += 18;
|
||||
attach(savepath, 0, y);
|
||||
|
@ -118,11 +120,9 @@ void PathSettingsWindow::setup() {
|
|||
|
||||
romselect.on_tick = bind(&PathSettingsWindow::selectpath_rom, this);
|
||||
romdefault.on_tick = bind(&PathSettingsWindow::defaultpath_rom, this);
|
||||
autodetect.on_tick = bind(&PathSettingsWindow::autodetect_tick, this);
|
||||
|
||||
patchselect.on_tick = bind(&PathSettingsWindow::selectpath_patch, this);
|
||||
patchdefault.on_tick = bind(&PathSettingsWindow::defaultpath_patch, this);
|
||||
bypass_crc32.on_tick = bind(&PathSettingsWindow::bypass_crc32_tick, this);
|
||||
|
||||
saveselect.on_tick = bind(&PathSettingsWindow::selectpath_save, this);
|
||||
savedefault.on_tick = bind(&PathSettingsWindow::defaultpath_save, this);
|
||||
|
@ -131,9 +131,7 @@ void PathSettingsWindow::setup() {
|
|||
cheatdefault.on_tick = bind(&PathSettingsWindow::defaultpath_cheat, this);
|
||||
|
||||
rompath.set_text(config::path.rom);
|
||||
autodetect.check(config::file.autodetect_type);
|
||||
patchpath.set_text(config::path.patch);
|
||||
bypass_crc32.check(config::file.bypass_patch_crc32);
|
||||
savepath.set_text(config::path.save);
|
||||
cheatpath.set_text(config::path.cheat);
|
||||
}
|
||||
|
|
|
@ -4,13 +4,11 @@ public:
|
|||
Editbox rompath;
|
||||
Button romselect;
|
||||
Button romdefault;
|
||||
Checkbox autodetect;
|
||||
|
||||
Label lpatchpath;
|
||||
Editbox patchpath;
|
||||
Button patchselect;
|
||||
Button patchdefault;
|
||||
Checkbox bypass_crc32;
|
||||
|
||||
Label lsavepath;
|
||||
Editbox savepath;
|
||||
|
@ -24,11 +22,9 @@ public:
|
|||
|
||||
uintptr_t selectpath_rom(Event);
|
||||
uintptr_t defaultpath_rom(Event);
|
||||
uintptr_t autodetect_tick(Event);
|
||||
|
||||
uintptr_t selectpath_patch(Event);
|
||||
uintptr_t defaultpath_patch(Event);
|
||||
uintptr_t bypass_crc32_tick(Event);
|
||||
|
||||
uintptr_t selectpath_save(Event);
|
||||
uintptr_t defaultpath_save(Event);
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
void Status::update() {
|
||||
string output;
|
||||
|
||||
if(queue[0].valid == true) {
|
||||
output = queue[0].text;
|
||||
time_t currenttime = time(0);
|
||||
if((int)(currenttime - displaytime) >= 3) {
|
||||
displaytime = currenttime;
|
||||
|
||||
//message displayed for 3 seconds or more, remove from queue
|
||||
for(unsigned i = 0; i < 15; i++) {
|
||||
queue[i].valid = queue[i + 1].valid;
|
||||
queue[i].text = queue[i + 1].text;
|
||||
}
|
||||
queue[15].valid = false;
|
||||
queue[15].text = "";
|
||||
}
|
||||
} else if(!cartridge.loaded()) {
|
||||
output = "";
|
||||
} else if(app.pause || app.autopause) {
|
||||
output = "Paused";
|
||||
} else if(ppu.status.frames_updated) {
|
||||
ppu.status.frames_updated = false;
|
||||
|
||||
unsigned max_framerate = snes.region() == SNES::NTSC ? 60 : 50;
|
||||
switch(config::system.emulation_speed) {
|
||||
case 0: max_framerate = unsigned(0.50 * max_framerate); break;
|
||||
case 1: max_framerate = unsigned(0.75 * max_framerate); break;
|
||||
case 2: max_framerate = unsigned(1.00 * max_framerate); break;
|
||||
case 3: max_framerate = unsigned(1.50 * max_framerate); break;
|
||||
case 4: max_framerate = unsigned(2.00 * max_framerate); break;
|
||||
case 5: max_framerate = 0; break;
|
||||
}
|
||||
|
||||
output = string() << cartridge.info.filename << " : " << (int)ppu.status.frames_executed;
|
||||
if(max_framerate != 0) {
|
||||
output << " / ";
|
||||
output << (int)max_framerate;
|
||||
}
|
||||
} else {
|
||||
//no operation
|
||||
return;
|
||||
}
|
||||
|
||||
if(text != output) {
|
||||
//refresh status text if it has changed since last redraw
|
||||
text = output;
|
||||
window.status.set_text(text);
|
||||
}
|
||||
}
|
||||
|
||||
void Status::flush() {
|
||||
for(unsigned i = 0; i < 16; i++) {
|
||||
queue[i].valid = false;
|
||||
queue[i].text = "";
|
||||
}
|
||||
}
|
||||
|
||||
bool Status::enqueue(const char *message) {
|
||||
unsigned index = 0;
|
||||
while(index < 16) {
|
||||
if(queue[index].valid == false) break;
|
||||
index++;
|
||||
}
|
||||
//queue full?
|
||||
if(index >= 16) return false;
|
||||
|
||||
queue[index].valid = true;
|
||||
queue[index].text = message;
|
||||
displaytime = time(0);
|
||||
update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Status::Status() : window(window_main) {
|
||||
flush();
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
class Status {
|
||||
public:
|
||||
void update();
|
||||
void flush();
|
||||
bool enqueue(const char *message);
|
||||
|
||||
Status();
|
||||
|
||||
private:
|
||||
Window &window;
|
||||
string text;
|
||||
|
||||
struct Queue {
|
||||
bool valid;
|
||||
string text;
|
||||
} queue[16];
|
||||
time_t displaytime;
|
||||
} status;
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include "base/main.cpp"
|
||||
#include "base/about.cpp"
|
||||
#include "base/message.cpp"
|
||||
|
||||
#include "loader/bsxloader.cpp"
|
||||
#include "loader/stloader.cpp"
|
||||
|
@ -20,7 +19,6 @@ void ui_init() {
|
|||
|
||||
window_main.setup();
|
||||
window_about.setup();
|
||||
window_message.setup();
|
||||
|
||||
window_bsxloader.setup();
|
||||
window_stloader.setup();
|
||||
|
@ -45,6 +43,7 @@ void ui_init() {
|
|||
video.set(Video::Synchronize, false);
|
||||
audio.set(Audio::Handle, window_main.handle());
|
||||
input.set(Input::Handle, window_main.handle());
|
||||
input.set(Input::AnalogAxisResistance, config::input.analog_axis_resistance);
|
||||
//sets Audio::Synchronize and Audio::Frequency
|
||||
event::update_emulation_speed(config::system.emulation_speed);
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ nall::dictionary translate;
|
|||
|
||||
#include "base/main.h"
|
||||
#include "base/about.h"
|
||||
#include "base/message.h"
|
||||
|
||||
#include "loader/bsxloader.h"
|
||||
#include "loader/stloader.h"
|
||||
|
|
Loading…
Reference in New Issue