Compare commits

...
6 Commits
Author SHA1 Message Date
byuu c9ca01fe20 Update to bsnes v023 release.
I've recently fixed a bug in bsnes that I feel is serious enough to warrant a new release, even though little else has changed.
I attempted to build this release with MinGW, but ran into problems with profiling and JMA support, so this release was built with Visual C++ once again.
Changelog:
    - Fixed serious bug in S-SMP incw and decw instructions -- fixes sound bug in Emerald Dragon
    - Added Nach's MinGW fixes -- can now be compiled with MinGW/GCC3 or MinGW/GCC4
    - Fixed const char* cast warnings in GCC 4.2, thanks to [vEX] for the feedback
    - Updated source to use latest libraries for libco, libui, etc.
    - Added new advanced options to adjust aspect ratio correction
    - Cleaned up source code a bit
2007-09-16 19:30:35 +00:00
byuu becf122aaa Update to bsnes v022r04? release.
Ok, I've posted a new WIP with the Emerald Dragon bug
fix. This time, I'm not posting the WIP publically. My last request
not to link directly to the file elsewhere was ignored, and ended up
on at least two emulation news sites (I won't mention names.) I'm not
trying to be a jerk about it, I really can't spare that kind of
bandwidth.

 If anyone has contributed any code or bug fixes, reported any
confirmed bugs, or is an emulator author I've spoken to in the past,
feel free to request a link to the WIP page from me in PM if desired.

               Sorry to everyone else. I'll have the new version out
as soon as possible.







> As for the AR advanced option, I think option 2 looks perfect.




 Done. I didn't add any sanity checks, so that people can have a
little fun with it. It's not going to be a GUI option, so only
advanced users can play with it anyway. Try setting a crazy aspect
like 3:1 and play a platform game. It's guaranteed to mess with your
mind.

 I also added the ... stuff, but I did not add the pause support back
in yet. Not sure if that'll make the next release, because it requires
some changes to the main loop functions that have been missing for a
while now. I usually like to leave more of a beta testing window
before messing with that stuff.







> byuu, you may know this, but on the front page of your web site, the
> link to your Philosophical Ramblings page is
> "http://localhost/index.php?page=articles/philosophy".




               Hahah, oops. No, I didn't notice that. I must have
copied the link from my browser instead of typing it manually.

 It's a stupid article, anyway. Didn't come out like I planned. I was
also reading some comments by Miguel de Icaza (supporting patent
pacts) and Linus Torvalds (bashing the hell out of C++ programmers),
and came across an interesting comment that got me thinking. Basically
that people who have any kind of notability really shouldn't go around
talking about things outside their specific expertise, because it
makes them look foolish when people take them too seriously (and
people do) as those two comments by de Icaza and Torvalds did. Lucky
for me I really _don't_ have any kind of notability, but it's a good
principle to adhere to anyway. Stick to talking about what I know
best. That goes against my whole personality though, so I probably
won't change at all anyway.

[No archive available]
2007-09-12 10:08:00 +00:00
byuu 1e130d7872 Update to bsnes v022r03 release.
Double post!

 I've uploaded a new WIP, which I'll make public (only to this forum,
please don't post about it anywhere else unless you mirror the file):







    http://byuu.org/files/bsnes_v022_wip03.zip




 This version adds all of Nach's MinGW fixes, updated libco, libui and
libfunctor, cleans up the code that detects if the main window has
focus or not (if not, ignore keyboard input), adds the new makefile
target win-mingw-lui, finally fixes all of the char* conversion
warnings with GCC 4.2.x (thanks [vEX]), and I'm sure there's more, but
I don't remember.

 The ZIP includes a Visual C++ generated binary, but it also works
with MinGW GCC4. It won't work with MinGW GCC3 because that one lacks
a C99-compliant vsnprintf function. You can hack your way around that
by editing src/lib/libstring_sprintf.cpp if you really want to use
MinGW GCC3.

 You may also need to change the CC / CPP variable names. I went with
the generic names mingw32-gcc and mingw32-g++, but the GCC4 binaries
have -sjlj or -dw2 appended to them. You'll also need to set
-I/path/to/directxheaders, or copy them all into
/path/to/mingw/include, since MinGW seems to ignore the include
environment variable.

 And finally, a bit of good news. It appears that MinGW GCC4 builds
binaries that are ~6% faster than Visual C++. That means with PGO
enabled, they should be at least ~16% faster than v0.022 official. If
I can figure out how to hide the ugly terminal window in the
background, I'll start making official releases with MinGW GCC4 from
now on.
2007-09-08 08:41:18 +00:00
byuu c57c733d7d Update to bsnes v022 release.
Today marks a milestone for bsnes, and possibly for SNES emulation as a whole. With this new release, bsnes' compatibility has now reached 100.0%, with zero game-specific hacks. With every last commercially released game tested by both FitzRoy and tetsuo55 for at least five minutes each, all known bugs have been resolved.
Now, needless to say, I am referring to the emulation of the base SNES unit. As many SNES cartridges contain additional coprocessors on their PCBs, there are still unplayable titles. So how can I claim compatibility of 100%? Because I don't consider special chips inside game cartridges as part of the base SNES hardware. I realize that many people enjoy these games, and I do actively attempt to emulate as many coprocessors as possible (six are supported thus far). However, coprocessors such as the SuperFX and SA-1 continue to pose very significant challenges.
So, after nearly three years of development, I've finally achieved my primary goal. But it wasn't a complete victory ... I've learned a lot over the years. Emulation accuracy is not black and white -- there are heavy costs to pay and forced tradeoffs to achieve it. I no longer believe there is only one absolute path for emulation, as I did in 2004.
So does this mean bsnes is now perfect? Of course not. There are many technical details that are not emulated correctly. This also does mean that there are no bugs, merely that there are no bugs that we are aware of. While absolute verification of 100% compatibility is obvioulsy impossible, even by actually beating every single game from start to finish, this very well should be the first time any SNES emulator could claim zero known bugs with all known games tested. I very much expect this announcement to entice many new users to begin actively searching for bugs, in an effort to discredit my above claim. My response? Go for it! I would very much appreciate any and all discovered bugs to be posted here, so that they can be verified and addressed.
One major thing that needs to be said, is that there consists of one major hack in all SNES emulators, including bsnes: the use of scanline-based PPU renderers. This necessitates global hacks in all emulators to minimize their inaccuracies. I was going to write up a very long post here, going into specifics, but I've decided an article would be a better place for that. I will hopefully be writing up this article in a few days to post here.
In the meantime, one very important issue does need to be addressed. This version fixes a bug in Uniracers 2-player mode, where the game writes to OAM during active display. Like other PPU global hacks, Uniracers required a special consession. But because this hack only affects one game, it can very fairly be seen as cheating. Suffice to say, bsnes does not contain a game-specific hack, and the change made to fix Uniracers affects all games, but I do still very much consider it to be a hack. The fix I have added is quite literally and honestly more accurate than the behavior of bsnes v0.021. Before, writes to OAM and CGRAM during active display went where a programmer would expect, which would cause bugs when ran on real hardware. Uniracers is the only game known to do this, and it is very dangerous to do so. The writes do go through, but not where one would expect. The access address basically changes as the screen is rendered. With a scanline-based PPU, it is not possible to emulate the individual 
steppings of the PPU, as there is not enough precision. Further, the entire SNES emulation community has virtually no information on how active display OAM and CGRAM writes work. Now, as Uniracers is the only game known to do this, I had the choice of either intentionally remapping the writes to an arbitrary location, or change it to the address Uniracers expects. Neither would be more accurate than the other, as both are completely wrong from a haradware standpoint. So the decision was to either fix Uniracers and deal with some calling it a game-specific hack, or to leave it broken with absolutely no gain to accuracy. Rather than decide for myself, I asked those who have supported me over the past three years for their opinions. The decision was unanimous to fix Uniracers. You can read the discussion, along with a more technical explanation of the issue, here. I will be addressing this topic in much greater detail in the article I will be writing up shortly.
Changelog:
    - Fixed buffer overflow that was manifesting as corrupted tiles in Lemmings 2
    - OAM and CGRAM addresses are now invalidated during active display, however the algorithms for how this address invalidation occurs is currently still unknown, so reads/writes are mapped to static addresses for now
    - Re-added cheat code editor.
    - Windows only: keypresses when main emulation window is not active are ignored once again
2007-08-04 19:54:35 +00:00
byuu e41aa25887 Update to bsnes v021r02? release.
I've posted a new private WIP. This one just adds the
cheat code editor back in again. Feedback on how it works is
appreciated. You'll notice it's a lot simpler than v0.019's cheat
editor ... I was going for simplicity this time. Editing a code means
deleting and re-adding it (or edit the text file directly). Yes, I
realize it's damn annoying entering codes because the emulator detects
your keypresses as controller presses (unless you're using a joypad).
Sorry, I still need to add code to determine if the active window is
the main emulator window or not for GTK+ before I can fix this on both
ports.

 Hopefully I can get that in before v0.022, but no promises. Worst
case, I'll add a dummy Window::active() function that always returns
true for GTK+ if needed.

 The cheat editor works the exact same on Windows and Linux, so this
should be the first release to allow Linux users to use it.

 Looking more at how useful bsnes is in its' current form ... I'm
simply not going to be able to walk away from it. Fuck it, I'll just
have to split the emulator and maintain two separate versions. It may
cost me some time, but whatever. It'll be good practice in trying to
streamline things to share as much code as possible. I'll keep them
together in one version as long as possible, too (using #defines and
such).

 If I do this, any suggestions on how to differentiate the two
versions? Different names? Different acronyms after bsnes (eg bsnes/AE
and bsnes/SE ...)? Different icons?







> One important point imo is the potential for "code longetivity".
> That is, I'd like the original, untouched code to continue to exist
> (while permitting derivative works) many decades from now.




 No matter the license, that won't change. People can close derived
works with PD, but it's their code that they added which becomes
closed, not mine. It won't make my code cease to be.

 It's not like it all matters that much. Regardless of license, anyone
is always free to get PD access to my code by asking. This is just me
trying to get a public consensus on whether or not I should allow
people to use my code without my permission, and to what extent I
should allow it.

 I was hoping the votes would be less 'all over the board' like the
Uniracers fix ... this vote isn't going very well. Sigh.

 It'd be annoying specifying licenses on everything. Maybe I'll just
bundle the core components (cpu, smp, ppu, dsp, memory, chip sans c4
(I don't own the rights to that)) and stick them in a separate
downloadable archive that's PD [or GPL w/permission exception]. That
way, I'm giving away the stuff that's important and can help others
the most, the emulation core. If someone can't be bothered to ask me
for mine, then they can write their own GUI. Call the package
something like 'libbsnes'. Meh.

[No archive available]
2007-08-03 08:23:00 +00:00
byuu 435f7d4371 Update to bsnes v021r01? release.
Alright, I've posted the new WIP.

             This one's really important, so please test it
thoroughly! :D
 I've ran it through my usual list of troublesome games, and
everything looks good, but it's possible I've overlooked something.

             The new config file settings are:
             ppu.hack.oam_address_invalidation
             ppu.hack.cgram_address_invalidation

 Set to true, OAM goes to 0x0218 (for Uniracers), CGRAM to 0x0000
(address is insignificant, we know of zero examples of this behavior,
so the address chosen does not matter for now). Set to false, the
writes are allowed and go where 'expected' (by programmers, not by
hardware).

 There's a slight difference in that OAM access is invalid even during
hblank, whereas CGRAM is obviously not (that's how games draw those
gradient fades and such).

             This WIP also has the Lemmings II fix.

             ---

 Now, I know I said I wouldn't bring this up again, but meh. So,
assuming I decide to go full force at this PPU renderer ... I still
want to let bsnes live on in its' current form, even if that means
losing my userbase to a competitor :(
 I'm planning for the next release to allow derivative works, in hopes
that someone will continue it. Does anyone have any objections to
that? Would it be better to use GPLv2/3 to ensure source availability
(even though I disagree with the notion of 'freedom through
restrictions' -- I liken it to becoming your enemies to defeat them),
or better to use PD to ensure the widest possible use of the code
(even if that means the source can be closed off to the public, and
the binary sold for profit -- which I also detest as immoral)? I
realize the latter means the value of all of my work will be lost, but
I never intended to profit from any of this anyway, so ...

 If you prefer GPL, please specify either v2 only, v2+ or v3. I can
use v3 and grant ZSNES an exception to use it under v2, so their v2
only license won't be a problem.

             Some examples:
             ZSNES is GPLv2, which got them the source to Zsnexbox.
 PocketNES is PD, which got the emulator used in commercial software
by Atlus, Hudson and Jaleco (though the assholes couldn't even be
bothered to send a thank you letter to the PocketNES devs).

 EDIT: I can also stick with the current license, a no-derivative one,
and do my best to maintain bsnes' old PPU renderer, if you like. But I
won't lie ... the pace of development _will_ slow down a lot on the
older version (it shouldn't affect my new PPU development speed much)
if we go with this option.

             Once again, I'll go with community opinion this time. I'm
personally not casting a vote for either.

[No archive available]
2007-08-02 08:46:00 +00:00
48 changed files with 745 additions and 345 deletions

No files matched your search

+2 -2
View File
@@ -53,11 +53,11 @@ 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, author: NSRT Team, license: GPL *
JMA, author: NSRT Team, license: GPL (*)
libco, author: byuu, license: public domain
libui, author: byuu, license: public domain
NTSC Filter, author: blargg, license: LGPL
S-DD1, author: Andreas Naive, license: public domain
zlib, 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.
+15 -9
View File
@@ -1,8 +1,8 @@
bsnes
Version 0.021
Version 0.023
Author: byuu
--------
General:
--------
bsnes is a Super Nintendo / Super Famicom emulator that began on
@@ -13,16 +13,22 @@ http://byuu.org/
Please see license.txt for important licensing information.
--------------
Shortcut Keys:
--------------
Esc - Toggle menubar visibility
F11 - Toggle fullscreen
------------------
Known Limitations:
------------------
S-CPU
- Invalid DMA / HDMA transfers (eg WRAM<>WRAM) not fully emulated
- Invalid DMA / HDMA transfers not fully emulated
- Multiply / Divide register delays not implemented
S-PPU
- Uses scanline-based renderer. This is very inaccurate, but very few games
rely on mid-scanline writes to function correctly
- Uses scanline-based renderer. This is very inaccurate, but few (if any)
games rely on mid-scanline writes to function correctly
- Does not support FirstSprite+Y priority
- OAM / CGRAM accesses during active display not supported correctly
- RTO flags are not calculated on frames that are skipped when frameskipping
@@ -31,10 +37,10 @@ S-PPU
Turning frameskipping off will allow RTO flag calculation on every frame
Hardware Bugs
- CPUr1 HDMA crashing bug not emulated
- CPU<>APU communication bus conflicts not emulated
- S-CPU.r1 HDMA crashing bug not emulated
- S-CPU<>S-SMP communication bus conflicts not emulated
---------------------
Unsupported Hardware:
---------------------
SA-1
@@ -79,7 +85,7 @@ Flash cartridge used by BS-X, as well as some standalone games by Asciisoft
Super Gameboy
Cartridge passthrough used for playing Gameboy games
------------------------
Unsupported Controllers:
------------------------
Mouse
+41 -4
View File
@@ -38,7 +38,7 @@ CC = cl
CFLAGS = /nologo /wd4996 /O2 /EHsc /DPLATFORM_WIN /DCOMPILER_VISUALC /DPROCESSOR_X86 /DUI_LUI
AS = nasm
ASFLAGS = -f win32 -DWIN32
LIBS = d3d9.lib ddraw.lib dsound.lib dinput8.lib dxguid.lib
LIBS = d3d9.lib ddraw.lib dsound.lib dinput8.lib dxguid.lib kernel32.lib user32.lib gdi32.lib shell32.lib winmm.lib comdlg32.lib comctl32.lib
LIBCO = libco_x86
LIBUI = libui_win
endif
@@ -49,7 +49,7 @@ CC = cl
CFLAGS = /nologo /wd4996 /O2 /GL /EHsc /DPLATFORM_WIN /DCOMPILER_VISUALC /DPROCESSOR_X86 /DUI_LUI
AS = nasm
ASFLAGS = -f win32 -DWIN32
LIBS = d3d9.lib ddraw.lib dsound.lib dinput8.lib dxguid.lib
LIBS = d3d9.lib ddraw.lib dsound.lib dinput8.lib dxguid.lib kernel32.lib user32.lib gdi32.lib shell32.lib winmm.lib comdlg32.lib comctl32.lib
LINK = /link /PGD:bsnes.pgd /LTCG:PGINSTRUMENT
LIBCO = libco_x86
LIBUI = libui_win
@@ -61,12 +61,34 @@ CC = cl
CFLAGS = /nologo /wd4996 /O2 /GL /EHsc /DPLATFORM_WIN /DCOMPILER_VISUALC /DPROCESSOR_X86 /DUI_LUI
AS = nasm
ASFLAGS = -f win32 -DWIN32
LIBS = d3d9.lib ddraw.lib dsound.lib dinput8.lib dxguid.lib
LIBS = d3d9.lib ddraw.lib dsound.lib dinput8.lib dxguid.lib kernel32.lib user32.lib gdi32.lib shell32.lib winmm.lib comdlg32.lib comctl32.lib
LINK = /link /PGD:bsnes.pgd /LTCG:PGOPTIMIZE
LIBCO = libco_x86
LIBUI = libui_win
endif
ifeq ($(PLATFORM),win-mingw-lui)
OS = win
CC = mingw32-gcc
CFLAGS = -mwindows -O3 -fomit-frame-pointer -DPLATFORM_WIN -DCOMPILER_GCC -DPROCESSOR_X86 -DUI_LUI
AS = nasm
ASFLAGS = -f win32 -DWIN32
LIBS = -ld3d9 -lddraw -ldsound -ldinput8 -ldxguid -luuid -lkernel32 -luser32 -lgdi32 -lshell32 -lwinmm -lcomdlg32 -lcomctl32
LIBCO = libco_x86
LIBUI = libui_win
endif
ifeq ($(PLATFORM),win-mingw4-lui)
OS = win
CC = mingw32-gcc-sjlj
CFLAGS = -mwindows -O3 -fomit-frame-pointer -DPLATFORM_WIN -DCOMPILER_GCC -DPROCESSOR_X86 -DUI_LUI
AS = nasm
ASFLAGS = -f win32 -DWIN32
LIBS = -ld3d9 -lddraw -ldsound -ldinput8 -ldxguid -luuid -lkernel32 -luser32 -lgdi32 -lshell32 -lwinmm -lcomdlg32 -lcomctl32
LIBCO = libco_x86
LIBUI = libui_win
endif
#####################################
### compiler / assembler switches ###
#####################################
@@ -79,6 +101,22 @@ CARGS = -c $< -o $@
DEFINE = -D
endif
ifeq ($(CC),mingw32-gcc)
OUT = -obsnes
CPP = mingw32-g++
OBJ = o
CARGS = -c $< -o $@
DEFINE = -D
endif
ifeq ($(CC),mingw32-gcc-sjlj)
OUT = -obsnes
CPP = mingw32-g++-sjlj
OBJ = o
CARGS = -c $< -o $@
DEFINE = -D
endif
ifeq ($(CC),cl)
OUT = /Febsnes
CPP = cl
@@ -106,7 +144,6 @@ endif
ifeq ($(OS),win)
OUT := $(OUT).exe
RM = del
LIBS += kernel32.lib user32.lib gdi32.lib shell32.lib winmm.lib comdlg32.lib comctl32.lib
endif
####################################
+5 -4
View File
@@ -1,4 +1,4 @@
#define BSNES_VERSION "0.021"
#define BSNES_VERSION "0.023"
#define BSNES_TITLE "bsnes v" BSNES_VERSION
#define MEMCORE bMemBus
@@ -31,6 +31,7 @@
#error "unsupported processor"
#endif
#include "lib/libfunctor.h"
#include "lib/libsort.h"
#include "lib/libarray.h"
#include "lib/libvector.h"
@@ -38,9 +39,9 @@
#include "lib/libconfig.h"
//platform-specific global functions
void alert(char*, ...);
void dprintf(char*, ...);
void dprintf(uint, char*, ...);
void alert(const char*, ...);
void dprintf(const char*, ...);
void dprintf(uint, const char*, ...);
namespace source {
enum {
+15
View File
@@ -89,6 +89,21 @@ IntegerSetting PPU::Hack::obj_cache(&config_file, "ppu.hack.obj_cache",
"This is technically closer to the actual operation of the SNES,\n"
"but can cause problems in some games if enabled",
IntegerSetting::Boolean, false);
IntegerSetting PPU::Hack::oam_address_invalidation(&config_file, "ppu.hack.oam_address_invalidation",
"OAM access address changes during active display, as the S-PPU reads\n"
"data to render the display. Thusly, the address retrieved when accessing\n"
"OAM during active display is unpredictable. Unfortunately, the exact\n"
"algorithm for this is completely unknown at this time. It is more hardware\n"
"accurate to enable this setting, but one must *not* rely on the actual\n"
"address to match hardware under emulation.",
IntegerSetting::Boolean, true);
IntegerSetting PPU::Hack::cgram_address_invalidation(&config_file, "ppu.hack.cgram_address_invalidation",
"CGRAM access address changes during active display (excluding hblank), as\n"
"the S-PPU reads data to render the display. Thusly, as with OAM, the access\n"
"address is unpredictable. Again, enabling this setting is more hardware\n"
"accurate, but one must *not* rely on the actual address to match hardware\n"
"under emulation.",
IntegerSetting::Boolean, true);
IntegerSetting PPU::opt_enable(0, "ppu.opt_enable", "Enable offset-per-tile effects", IntegerSetting::Boolean, true);
IntegerSetting PPU::bg1_pri0_enable(0, "ppu.bg1_pri0_enable", "Enable BG1 Priority 0", IntegerSetting::Boolean, true);
+2
View File
@@ -30,6 +30,8 @@ extern struct PPU {
struct Hack {
static IntegerSetting render_scanline_position;
static IntegerSetting obj_cache;
static IntegerSetting oam_address_invalidation;
static IntegerSetting cgram_address_invalidation;
} hack;
static IntegerSetting opt_enable;
+11 -10
View File
@@ -1,5 +1,5 @@
/*
libbase : version 0.10 ~byuu (2007-06-04)
libbase : version 0.11 ~byuu (2007-09-08)
license: public domain
*/
@@ -18,7 +18,7 @@
#include <new>
#if defined(_MSC_VER)
#if defined(_MSC_VER) || defined(__MINGW32__)
#include <io.h>
#include <direct.h>
#include <shlobj.h>
@@ -34,6 +34,10 @@
#define NOMINMAX
#define PATH_MAX _MAX_PATH
#define va_copy(dst, src) ((dst) = (src))
#endif
#if defined(_MSC_VER) || defined(__MINGW32__)
#define getcwd _getcwd
#define ftruncate _chsize
#define mkdir _mkdir
@@ -41,11 +45,10 @@
#define rmdir _rmdir
#define vsnprintf _vsnprintf
#define va_copy(dst, src) ((dst) = (src))
static char *realpath(const char *file_name, char *resolved_name) {
return _fullpath(resolved_name, file_name, PATH_MAX);
}
#elif defined(__GNUC__)
#else
#define mkdir(path) (mkdir)(path, 0755);
#endif
@@ -90,13 +93,13 @@ typedef int64_t int64;
//userpath(output) retrieves path to user's home folder
//output must be at least as large as PATH_MAX
#if defined(_MSC_VER)
#if defined(_MSC_VER) || defined(__MINGW32__)
static char *userpath(char *output) {
strcpy(output, "."); //failsafe
SHGetFolderPath(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, output);
return output;
}
#elif defined(__GNUC__)
#else
static char *userpath(char *output) {
strcpy(output, "."); //failsafe
struct passwd *userinfo = getpwuid(getuid());
@@ -136,14 +139,10 @@ T z = x;
y = z;
}
#ifdef min
#undef min
#endif
#define min(x, y) (((x) < (y)) ? (x) : (y))
#ifdef max
#undef max
#endif
#define max(x, y) (((x) > (y)) ? (x) : (y))
template<int min, int max, typename T> inline T minmax(const T x) {
@@ -170,6 +169,8 @@ enum { b = 1U << (bits - 1), m = (1U << bits) - 1 };
return ((x & m) ^ b) - b;
}
//bit shifting functions are deprecated
template<int n, typename T> inline T rol(const T x) {
enum { s = (sizeof(T) << 3) - n };
return (x << n) | (x >> s);
+14 -33
View File
@@ -1,14 +1,14 @@
;*****
;libco_x86 : version 0.10 ~byuu (2007-04-18)
;libco_x86 : version 0.10 ~byuu (2007-09-08)
;cross-platform x86 implementation of libco
;special thanks to Aaron Giles and Joel Yliluoma for various optimizations
;
;[ABI compatibility]
;- visual c++; windows-x86
;- mingw; windows-x86
;- gcc; osx86
;- gcc; linux-x86
;- gcc; freebsd-x86
;- visual c++; windows; x86
;- mingw; windows; x86
;- gcc; mac os x; x86
;- gcc; linux; x86
;- gcc; freebsd; x86
;
;[nonvolatile registers]
;- esp, ebp, edi, esi, ebx
@@ -28,7 +28,7 @@
%define free _free
%define co_active @co_active@0
%define co_create @co_create@12
%define co_create @co_create@8
%define co_delete @co_delete@4
%define co_switch @co_switch@4
%endif
@@ -76,11 +76,10 @@ co_active:
ret
;*****
;extern "C" cothread_t fastcall co_create(unsigned int heapsize, void (*coentry)(void *data), void *data);
;ecx = heapsize
;edx = coentry
;[esp+4] = data
;return = eax
;extern "C" cothread_t fastcall co_create(unsigned int heapsize, void (*coentry)());
;ecx = heapsize
;edx = coentry
;return = eax
;*****
align 16
@@ -101,7 +100,7 @@ co_create:
and ecx,-16 ;force 16-byte alignment of stack heap
;store thread entry point + registers, so that first call to co_switch will execute coentry
mov dword[ecx-4],co_entrypoint ;entry point
mov dword[ecx-4],edx ;entry point
mov dword[ecx-8],0 ;ebp
mov dword[ecx-12],0 ;esi
mov dword[ecx-16],0 ;edi
@@ -109,11 +108,8 @@ co_create:
sub ecx,20
;initialize context memory heap and return
mov [eax],ecx ;cothread_t[ 0- 3] = stack heap pointer (esp)
mov [eax+4],edx ;cothread_t[ 4- 7] = coentry
mov ecx,[esp+4]
mov [eax+8],ecx ;cothread_t[ 8-11] = data
ret 4 ;return allocated memory block as thread handle
mov [eax],ecx ;*cothread_t = stack heap pointer (esp)
ret ;return allocated memory block as thread handle
;*****
;extern "C" void fastcall co_delete(cothread_t cothread);
@@ -150,18 +146,3 @@ co_switch:
pop ebp
ret
;*****
;void fastcall co_entrypoint();
;*****
align 16
co_entrypoint:
mov ebp,esp
.loop
mov eax,[co_active_context]
mov ecx,[eax+8] ;fastcall
push ecx ;stdcall
call [eax+4]
mov esp,ebp ;cdecl
jmp .loop
+14 -14
View File
@@ -1,26 +1,26 @@
/*
libco_x86 : version 0.10 ~byuu (2007-06-04)
libco_x86 : version 0.10 ~byuu (2007-09-08)
license: public domain
*/
#ifndef LIBCO_H
#define LIBCO_H
#undef fastcall
#if defined(_MSC_VER)
#define fastcall __fastcall
#elif defined(__GNUC__)
#define fastcall __attribute__((fastcall))
#else
#error "fastcall undefined"
#if !defined(fastcall)
#if defined(_MSC_VER)
#define fastcall __fastcall
#elif defined(__GNUC__)
#define fastcall __attribute__((fastcall))
#else
#error "fastcall undefined"
#endif
#endif
#define cocall fastcall
typedef void (*cothread_t);
typedef void *cothread_t;
extern "C" cothread_t cocall co_active();
extern "C" cothread_t cocall co_create(unsigned int heapsize, void (cocall *coentry)(void *data), void *data);
extern "C" void cocall co_delete(cothread_t cothread);
extern "C" void cocall co_switch(cothread_t cothread);
extern "C" cothread_t fastcall co_active();
extern "C" cothread_t fastcall co_create(unsigned int heapsize, void (*coentry)());
extern "C" void fastcall co_delete(cothread_t cothread);
extern "C" void fastcall co_switch(cothread_t cothread);
#endif
+6 -22
View File
@@ -1,12 +1,12 @@
;*****
;libco_x86_64 : version 0.10 ~byuu (2007-04-18)
;libco_x86_64 : version 0.10 ~byuu (2007-09-08)
;cross-platform x86-64 implementation of libco
;special thanks to Aaron Giles and Joel Yliluoma for various optimizations
;
;[ABI compatibility]
;- SystemV ( http://refspecs.freestandards.org/elf/x86_64-SysV-psABI.pdf )
;- gcc; linux-x86-64
;- gcc; freebsd-x86-64
;- gcc; linux; x86-64
;- gcc; freebsd; x86-64
;
;[nonvolatile registers]
;- rsp, rbp, rbx, r12, r13, r14, r15
@@ -50,10 +50,9 @@ co_active:
ret
;*****
;extern "C" cothread_t co_create(unsigned int heapsize, void (*coentry)(void *data), void *data);
;extern "C" cothread_t co_create(unsigned int heapsize, void (*coentry)());
;rdi = heapsize
;rsi = coentry
;rdx = data
;return = rax
;*****
@@ -63,11 +62,9 @@ co_create:
add rdi,512 ;allocate extra memory for contextual info
push rdi
push rsi
push rdx
call malloc ;rax = malloc(rdi)
pop rdx
pop rsi
pop rdi
@@ -75,7 +72,7 @@ co_create:
and rdi,-16 ;force 16-byte alignment of stack heap
;store thread entry point + registers, so that first call to co_switch will execute coentry
mov qword[rdi-8],co_entrypoint ;entry point
mov qword[rdi-8],rsi ;entry point
mov qword[rdi-16],0 ;r15
mov qword[rdi-24],0 ;r14
mov qword[rdi-32],0 ;r13
@@ -85,9 +82,7 @@ co_create:
sub rdi,56
;initialize context memory heap and return
mov [rax],rdi ;cothread_t[ 0- 7] = stack heap pointer (rsp)
mov [rax+8],rsi ;cothread_t[ 8-15] = coentry
mov [rax+16],rdx ;cothread_t[16-23] = data
mov [rax],rdi ;*cothread_t = stack heap pointer (rsp)
ret ;return allocated memory block as thread handle
;*****
@@ -126,14 +121,3 @@ co_switch:
pop rbp
ret
;*****
;void co_entrypoint();
;*****
align 16
co_entrypoint:
mov rax,[co_active_context wrt rip]
mov rdi,[rax+16]
call [rax+8]
jmp co_entrypoint
+4 -4
View File
@@ -1,15 +1,15 @@
/*
libco_x86_64 : version 0.10 ~byuu (2007-04-18)
libco_x86_64 : version 0.10 ~byuu (2007-09-08)
license: public domain
*/
#ifndef LIBCO_H
#define LIBCO_H
#define cocall
typedef void (*cothread_t);
typedef void *cothread_t;
extern "C" cothread_t co_active();
extern "C" cothread_t co_create(unsigned int heapsize, void (cocall *coentry)(void *data), void *data);
extern "C" cothread_t co_create(unsigned int heapsize, void (*coentry)());
extern "C" void co_delete(cothread_t cothread);
extern "C" void co_switch(cothread_t cothread);
+2 -2
View File
@@ -1,5 +1,5 @@
/*
libconfig : version 0.14 ~byuu (2007-06-10)
libconfig : version 0.14 ~byuu (2007-06-12)
license: public domain
*/
@@ -103,7 +103,7 @@ string data;
bool operator==(const char *x) { return !strcmp(data, x); }
bool operator!=(const char *x) { return strcmp(data, x); }
StringSetting(Config *parent, const char *r_name, const char *r_desc, char *r_data) {
StringSetting(Config *parent, const char *r_name, const char *r_desc, const char *r_data) {
type = Setting::String;
name = strdup(r_name);
desc = strdup(r_desc);
+100
View File
@@ -0,0 +1,100 @@
/*
libfunctor : version 0.05 ~byuu (2007-09-08)
license: public domain
*/
#ifndef LIBFUNCTOR_H
#define LIBFUNCTOR_H
//prologue
#define TN typename
template<typename T> class functor;
//parameters = 0
#define cat(n) n
#define TL typename R
#define PL
#define CL
#include "libfunctor_impl.h"
//parameters = 1
#define cat(n) , n
#define TL TN R, TN P1
#define PL P1 p1
#define CL p1
#include "libfunctor_impl.h"
//parameters = 2
#define cat(n) , n
#define TL TN R, TN P1, TN P2
#define PL P1 p1, P2 p2
#define CL p1, p2
#include "libfunctor_impl.h"
//parameters = 3
#define cat(n) , n
#define TL TN R, TN P1, TN P2, TN P3
#define PL P1 p1, P2 p2, P3 p3
#define CL p1, p2, p3
#include "libfunctor_impl.h"
//parameters = 4
#define cat(n) , n
#define TL TN R, TN P1, TN P2, TN P3, TN P4
#define PL P1 p1, P2 p2, P3 p3, P4 p4
#define CL p1, p2, p3, p4
#include "libfunctor_impl.h"
//parameters = 5
#define cat(n) , n
#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5
#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5
#define CL p1, p2, p3, p4, p5
#include "libfunctor_impl.h"
//parameters = 6
#define cat(n) , n
#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5, TN P6
#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6
#define CL p1, p2, p3, p4, p5, p6
#include "libfunctor_impl.h"
//parameters = 7
#define cat(n) , n
#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5, TN P6, TN P7
#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7
#define CL p1, p2, p3, p4, p5, p6, p7
#include "libfunctor_impl.h"
//parameters = 8
#define cat(n) , n
#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5, TN P6, TN P7, TN P8
#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8
#define CL p1, p2, p3, p4, p5, p6, p7, p8
#include "libfunctor_impl.h"
//epilogue
#undef TN
#endif
+81
View File
@@ -0,0 +1,81 @@
#ifdef LIBFUNCTOR_H
template<TL>
class functor<R (PL)> {
private:
struct base1 { virtual void func1(PL) {} };
struct base2 { virtual void func2(PL) {} };
struct derived : base1, virtual base2 {};
struct data_t {
R (*fn_call)(const data_t& cat(PL));
union {
R (*fn_global)(PL);
struct {
R (derived::*fn_member)(PL);
void *object;
};
};
} data;
static R fn_call_global(const data_t &d cat(PL)) {
return d.fn_global(CL);
}
template<typename C>
static R fn_call_member(const data_t &d cat(PL)) {
return (((C*)d.object)->*((R (C::*&)(PL))d.fn_member))(CL);
}
public:
R operator()(PL) const { return data.fn_call(data cat(CL)); }
operator bool() const { return data.fn_call; }
functor() { data.fn_call = 0; }
functor(R (*fn)(PL)) {
data.fn_call = &fn_call_global;
data.fn_global = fn;
}
template<typename C>
functor(R (C::*fn)(PL), C *obj) {
data.fn_call = &fn_call_member<C>;
(R (C::*&)(PL))data.fn_member = fn;
assert(sizeof data.fn_member >= sizeof fn);
data.object = obj;
}
template<typename C>
functor(R (C::*fn)(PL) const, C *obj) {
data.fn_call = &fn_call_member<C>;
(R (C::*&)(PL))data.fn_member = (R (C::*&)(PL))fn;
assert(sizeof data.fn_member >= sizeof fn);
data.object = obj;
}
functor &operator=(const functor &source) { memcpy(&data, &source.data, sizeof(data_t)); return *this; }
functor(const functor &source) { memcpy(&data, &source.data, sizeof(data_t)); }
};
template<TL>
functor<R (PL)> bind(R (*fn)(PL)) {
return functor<R (PL)>(fn);
}
template<typename C, TL>
functor<R (PL)> bind(R (C::*fn)(PL), C *obj) {
return functor<R (PL)>(fn, obj);
}
template<typename C, TL>
functor<R (PL)> bind(R (C::*fn)(PL) const, C *obj) {
return functor<R (PL)>(fn, obj);
}
#undef cat
#undef TL
#undef PL
#undef CL
#endif
+2
View File
@@ -1,3 +1,5 @@
#define _WIN32_IE 0x0600
#include "libui_win.h"
#include "libui_win_window.cpp"
#include "libui_win_control.cpp"
+6 -2
View File
@@ -148,8 +148,12 @@ struct {
void cgram_write(uint16 addr, uint8 value);
uint16 get_vram_address();
uint8 vram_mmio_read (uint16 addr);
void vram_mmio_write(uint16 addr, uint8 data);
uint8 vram_mmio_read (uint16 addr);
void vram_mmio_write (uint16 addr, uint8 data);
uint8 oam_mmio_read (uint16 addr);
void oam_mmio_write (uint16 addr, uint8 data);
uint8 cgram_mmio_read (uint16 addr);
void cgram_mmio_write(uint16 addr, uint8 data);
void mmio_w2100(uint8 value); //INIDISP
void mmio_w2101(uint8 value); //OBSEL
+92 -16
View File
@@ -16,6 +16,11 @@ uint16 addr;
return (addr << 1);
}
//NOTE: all VRAM writes during active display are invalid. Unlike OAM and CGRAM, they will
//not be written anywhere at all. The below address ranges for where writes are invalid have
//been validated on hardware, as has the edge case where the S-CPU MDR can be written if the
//write occurs during the very last clock cycle of vblank.
uint8 bPPU::vram_mmio_read(uint16 addr) {
if(regs.display_disabled == true) {
return vram_read(addr);
@@ -46,20 +51,17 @@ uint16 ls = (r_cpu->region_scanlines() >> 1) - 1;
void bPPU::vram_mmio_write(uint16 addr, uint8 data) {
if(regs.display_disabled == true) {
vram_write(addr, data);
return;
return vram_write(addr, data);
}
uint16 v = r_cpu->vcounter();
uint16 hc = r_cpu->hclock();
if(v == 0) {
if(hc <= 4) {
vram_write(addr, data);
return;
return vram_write(addr, data);
}
if(hc == 6) {
vram_write(addr, r_cpu->regs.mdr);
return;
return vram_write(addr, r_cpu->regs.mdr);
}
return;
}
@@ -72,13 +74,87 @@ uint16 hc = r_cpu->hclock();
if(hc <= 4) {
return;
}
vram_write(addr, data);
return;
return vram_write(addr, data);
}
vram_write(addr, data);
}
//NOTE: OAM accesses during active display are rerouted to 0x0218 ... this can be considered
//a hack. The actual address varies during rendering, as the S-PPU reads in data itself for
//processing. Unfortunately, we have yet to determine how this works. The algorithm cannot be
//reverse engineered using a scanline renderer such as this, and at this time, there does not
//exist a more accurate SNES PPU emulator to work from. The only known game to actually access
//OAM during active display is Uniracers. It expects accesses to map to offset 0x0218.
//It was decided by public consensus to map writes to this address to match Uniracers, primarily
//because it is the only game observed to do this, but also because mapping to this address does
//not contradict any of our findings, because we have no findings whatsoever on this behavior.
//Think of this what you will, I openly admit that this is a hack. But it is more accurate than
//writing to the 'expected' address set by $2102,$2103, and will catch problems in software that
//accidentally accesses OAM during active display by virtue of not returning the expected data.
//You may disable this behavior by setting config::ppu.hack.oam_address_invalidation to false,
//or by changing the address from 0x0218 to 0x0000 below if it bothers you that greatly.
uint8 bPPU::oam_mmio_read(uint16 addr) {
if(config::ppu.hack.oam_address_invalidation == false || regs.display_disabled == true) {
return oam_read(addr);
}
uint16 v = r_cpu->vcounter();
if(v < (!r_cpu->overscan() ? 225 : 240)) {
return oam_read(0x0218);
}
return oam_read(addr);
}
void bPPU::oam_mmio_write(uint16 addr, uint8 data) {
if(config::ppu.hack.oam_address_invalidation == false || regs.display_disabled == true) {
return oam_write(addr, data);
}
uint16 v = r_cpu->vcounter();
if(v < (!r_cpu->overscan() ? 225 : 240)) {
return oam_write(0x0218, data);
}
oam_write(addr, data);
}
//NOTE: CGRAM writes during hblank are valid. During active display, the actual address the
//data is written to varies, as the S-PPU itself changes the address. Like OAM, we do not know
//the exact algorithm used, but we have zero known examples of any commercial software that
//attempts to do this. Therefore, the addresses are mapped to 0x0000. There is nothing special
//about this address, it is simply more accurate to invalidate the 'expected' address than not.
uint8 bPPU::cgram_mmio_read(uint16 addr) {
if(config::ppu.hack.cgram_address_invalidation == false || regs.display_disabled == true) {
return cgram_read(addr);
}
uint16 v = r_cpu->vcounter();
uint16 hc = r_cpu->hclock();
if(v < (!r_cpu->overscan() ? 225 : 240) && hc > 0 && hc < 1096) {
return cgram_read(0x0000);
}
return cgram_read(addr);
}
void bPPU::cgram_mmio_write(uint16 addr, uint8 data) {
if(config::ppu.hack.cgram_address_invalidation == false || regs.display_disabled == true) {
return cgram_write(addr, data);
}
uint16 v = r_cpu->vcounter();
uint16 hc = r_cpu->hclock();
if(v < (!r_cpu->overscan() ? 225 : 240) && hc > 0 && hc < 1096) {
return cgram_write(0x0000, data);
}
cgram_write(addr, data);
}
//INIDISP
void bPPU::mmio_w2100(uint8 value) {
if(regs.display_disabled == true && r_cpu->vcounter() == (!r_cpu->overscan() ? 225 : 240)) {
@@ -117,12 +193,12 @@ void bPPU::mmio_w2103(uint8 data) {
//OAMDATA
void bPPU::mmio_w2104(uint8 data) {
if(regs.oam_addr & 0x0200) {
oam_write(regs.oam_addr, data);
oam_mmio_write(regs.oam_addr, data);
} else if((regs.oam_addr & 1) == 0) {
regs.oam_latchdata = data;
} else {
oam_write((regs.oam_addr & ~1) + 0, regs.oam_latchdata);
oam_write((regs.oam_addr & ~1) + 1, data);
oam_mmio_write((regs.oam_addr & ~1) + 0, regs.oam_latchdata);
oam_mmio_write((regs.oam_addr & ~1) + 1, data);
}
regs.oam_addr++;
@@ -353,8 +429,8 @@ void bPPU::mmio_w2122(uint8 value) {
if(!(regs.cgram_addr & 1)) {
regs.cgram_latchdata = value;
} else {
cgram_write((regs.cgram_addr & 0x01fe), regs.cgram_latchdata);
cgram_write((regs.cgram_addr & 0x01fe) + 1, value & 0x7f);
cgram_mmio_write((regs.cgram_addr & 0x01fe), regs.cgram_latchdata);
cgram_mmio_write((regs.cgram_addr & 0x01fe) + 1, value & 0x7f);
}
regs.cgram_addr++;
regs.cgram_addr &= 0x01ff;
@@ -540,7 +616,7 @@ uint8 bPPU::mmio_r2137() {
//OAMDATAREAD
uint8 bPPU::mmio_r2138() {
regs.ppu1_mdr = oam_read(regs.oam_addr);
regs.ppu1_mdr = oam_mmio_read(regs.oam_addr);
regs.oam_addr++;
regs.oam_addr &= 0x03ff;
@@ -581,10 +657,10 @@ uint16 addr = get_vram_address() + 1;
//update bit 7 of the PPU2 MDR.
uint8 bPPU::mmio_r213b() {
if(!(regs.cgram_addr & 1)) {
regs.ppu2_mdr = cgram_read(regs.cgram_addr) & 0xff;
regs.ppu2_mdr = cgram_mmio_read(regs.cgram_addr) & 0xff;
} else {
regs.ppu2_mdr &= 0x80;
regs.ppu2_mdr |= cgram_read(regs.cgram_addr) & 0x7f;
regs.ppu2_mdr |= cgram_mmio_read(regs.cgram_addr) & 0x7f;
}
regs.cgram_addr++;
regs.cgram_addr &= 0x01ff;
+3 -1
View File
@@ -27,7 +27,9 @@ uint16 bPPU::bg_get_tile(uint8 bg, uint16 x, uint16 y) {
uint16 pos = ((y & 0x1f) << 5) + (x & 0x1f);
if(y & 0x20)pos += bg_info[bg].scy;
if(x & 0x20)pos += bg_info[bg].scx;
return read16(vram, regs.bg_scaddr[bg] + (pos << 1));
uint16 addr = regs.bg_scaddr[bg] + (pos << 1);
return (vram_read(addr + 0) << 0) | (vram_read(addr + 1) << 8);
}
#define setpixel_main(x) \
+4 -4
View File
@@ -61,14 +61,14 @@ tclr_addr_a(0x4e, &~) {
5:op_writeaddr(dp, rd $1 regs.a);
}
incw_dp(0x3a, rd++),
decw_dp(0x1a, rd--) {
incw_dp(0x3a, ++),
decw_dp(0x1a, --) {
1:dp = op_readpc();
2:rd = op_readdp(dp);
$1;
rd$1;
3:op_writedp(dp++, rd);
4:rd += op_readdp(dp) << 8;
5:op_write(dp, rd >> 8);
5:op_writedp(dp, rd >> 8);
regs.p.n = !!(rd & 0x8000);
regs.p.z = (rd == 0);
}
+2 -2
View File
@@ -212,7 +212,7 @@ void sSMP::op_incw_dp() {
rd++;
op_writedp(dp++, rd);
rd += op_readdp(dp) << 8;
op_write(dp, rd >> 8);
op_writedp(dp, rd >> 8);
regs.p.n = !!(rd & 0x8000);
regs.p.z = (rd == 0);
}
@@ -223,7 +223,7 @@ void sSMP::op_decw_dp() {
rd--;
op_writedp(dp++, rd);
rd += op_readdp(dp) << 8;
op_write(dp, rd >> 8);
op_writedp(dp, rd >> 8);
regs.p.n = !!(rd & 0x8000);
regs.p.z = (rd == 0);
}
+1
View File
@@ -12,6 +12,7 @@ class SNESInterface { public:
void audio_sample(uint16 l_sample, uint16 r_sample);
functor<bool ()> input_ready;
void input_poll();
bool input_poll(uint deviceid, uint button);
+8 -8
View File
@@ -2,10 +2,10 @@ Scheduler scheduler;
//
void cocall threadentry_cpu(void*) { r_cpu->enter(); }
void cocall threadentry_smp(void*) { r_smp->enter(); }
void cocall threadentry_ppu(void*) {} //currently unused
void cocall threadentry_dsp(void*) { r_dsp->enter(); }
void threadentry_cpu() { r_cpu->enter(); }
void threadentry_smp() { r_smp->enter(); }
void threadentry_ppu() { } //currently unused
void threadentry_dsp() { r_dsp->enter(); }
//
@@ -41,10 +41,10 @@ void Scheduler::init() {
if(thread_dsp)co_delete(thread_dsp);
thread_snes = co_active();
thread_cpu = co_create(sizeof(void*) * 64 * 1024, threadentry_cpu, 0);
thread_smp = co_create(sizeof(void*) * 64 * 1024, threadentry_smp, 0);
thread_ppu = co_create(sizeof(void*) * 64 * 1024, threadentry_ppu, 0);
thread_dsp = co_create(sizeof(void*) * 64 * 1024, threadentry_dsp, 0);
thread_cpu = co_create(sizeof(void*) * 64 * 1024, threadentry_cpu);
thread_smp = co_create(sizeof(void*) * 64 * 1024, threadentry_smp);
thread_ppu = co_create(sizeof(void*) * 64 * 1024, threadentry_ppu);
thread_dsp = co_create(sizeof(void*) * 64 * 1024, threadentry_dsp);
}
//
+1 -1
View File
@@ -1,6 +1,6 @@
Tracer tracer;
void tprintf(char *s, ...) {
void tprintf(const char *s, ...) {
if(tracer.enabled() == false) { return; }
char str[4096];
+2 -2
View File
@@ -1,4 +1,4 @@
void tprintf(char *s, ...);
void tprintf(const char *s, ...);
class Tracer {
private:
@@ -43,7 +43,7 @@ public:
Tracer();
~Tracer();
friend void tprintf(char *s, ...);
friend void tprintf(const char *s, ...);
};
extern Tracer tracer;
+1 -97
View File
@@ -1,100 +1,4 @@
/*
* interpolation routines are located in: /src/lib/libinterp.h
*/
/*
inline uint Audio::bind_range(uint min, uint max, uint index) {
return index < min ? min : index > max ? max : index;
}
void Audio::resample_point(
uint32 *output, uint32 *input, uint output_samples, uint input_samples
) {
double scalar = double(input_samples--) / double(output_samples--); //convert lengths to upper bounds
double sindex = 0.0;
for(uint x = 0; x <= output_samples; x++) {
uint32 y0 = input[bind_range(0, input_samples, uint32(sindex) + 0)];
uint32 y1 = input[bind_range(0, input_samples, uint32(sindex) + 1)];
double mu = sindex - uint(sindex); //calculate fractional portion of step
uint16 yl = interpolate_point(mu, int16(y0 >> 0), int16(y1 >> 0));
uint16 yr = interpolate_point(mu, int16(y0 >> 16), int16(y1 >> 16));
output[x] = (yl << 0) + (yr << 16);
sindex += scalar;
}
}
void Audio::resample_linear(
uint32 *output, uint32 *input, uint output_samples, uint input_samples
) {
double scalar = double(input_samples--) / double(output_samples--); //convert lengths to upper bounds
double sindex = 0.0;
for(uint x = 0; x <= output_samples; x++) {
uint32 y0 = input[bind_range(0, input_samples, uint32(sindex) + 0)];
uint32 y1 = input[bind_range(0, input_samples, uint32(sindex) + 1)];
double mu = sindex - uint(sindex); //calculate fractional portion of step
uint16 yl = interpolate_linear(mu, int16(y0 >> 0), int16(y1 >> 0));
uint16 yr = interpolate_linear(mu, int16(y0 >> 16), int16(y1 >> 16));
output[x] = (yl << 0) + (yr << 16);
sindex += scalar;
}
}
void Audio::resample_cosine(
uint32 *output, uint32 *input, uint output_samples, uint input_samples
) {
double scalar = double(input_samples--) / double(output_samples--); //convert lengths to upper bounds
double sindex = 0.0;
for(uint x = 0; x <= output_samples; x++) {
uint32 y0 = input[bind_range(0, input_samples, uint32(sindex) + 0)];
uint32 y1 = input[bind_range(0, input_samples, uint32(sindex) + 1)];
double mu = sindex - uint(sindex); //calculate fractional portion of step
uint16 yl = interpolate_cosine(mu, int16(y0 >> 0), int16(y1 >> 0));
uint16 yr = interpolate_cosine(mu, int16(y0 >> 16), int16(y1 >> 16));
output[x] = (yl << 0) + (yr << 16);
sindex += scalar;
}
}
void Audio::resample_cubic(
uint32 *output, uint32 *input, uint output_samples, uint input_samples
) {
double scalar = double(input_samples--) / double(output_samples--); //convert lengths to upper bounds
double sindex = 0.0;
for(uint x = 0; x <= output_samples; x++) {
uint32 y0 = input[bind_range(0, input_samples, uint32(sindex) - 1)];
uint32 y1 = input[bind_range(0, input_samples, uint32(sindex) + 0)];
uint32 y2 = input[bind_range(0, input_samples, uint32(sindex) + 1)];
uint32 y3 = input[bind_range(0, input_samples, uint32(sindex) + 2)];
double mu = sindex - uint(sindex); //calculate fractional portion of step
uint16 yl = sclamp<16>( interpolate_cubic(mu, int16(y0 >> 0), int16(y1 >> 0), int16(y2 >> 0), int16(y3 >> 0)) );
uint16 yr = sclamp<16>( interpolate_cubic(mu, int16(y0 >> 16), int16(y1 >> 16), int16(y2 >> 16), int16(y3 >> 16)) );
output[x] = (yl << 0) + (yr << 16);
sindex += scalar;
}
}
void Audio::resample_hermite(
uint32 *output, uint32 *input, uint output_samples, uint input_samples
) {
double scalar = double(input_samples--) / double(output_samples--); //convert lengths to upper bounds
double sindex = 0.0;
for(uint x = 0; x <= output_samples; x++) {
uint32 y0 = input[bind_range(0, input_samples, uint32(sindex) - 1)];
uint32 y1 = input[bind_range(0, input_samples, uint32(sindex) + 0)];
uint32 y2 = input[bind_range(0, input_samples, uint32(sindex) + 1)];
uint32 y3 = input[bind_range(0, input_samples, uint32(sindex) + 2)];
double mu = sindex - uint(sindex); //calculate fractional portion of step
uint16 yl = sclamp<16>( interpolate_hermite(mu, 0.0, 0.0, int16(y0 >> 0), int16(y1 >> 0), int16(y2 >> 0), int16(y3 >> 0)) );
uint16 yr = sclamp<16>( interpolate_hermite(mu, 0.0, 0.0, int16(y0 >> 16), int16(y1 >> 16), int16(y2 >> 16), int16(y3 >> 16)) );
output[x] = (yl << 0) + (yr << 16);
sindex += scalar;
}
}
*/
//
#include "audio.h"
void Audio::update_frequency() {
uint freq = config::audio.frequency;
+5 -9
View File
@@ -1,3 +1,6 @@
#ifndef AUDIO_H
#define AUDIO_H
class Audio {
public:
uint frequency, latency;
@@ -7,14 +10,7 @@ uint frequency, latency;
virtual void init() {}
virtual void term() {}
/*
uint bind_range(uint min, uint max, uint index);
void resample_point (uint32 *output, uint32 *input, uint output_samples, uint input_samples);
void resample_linear (uint32 *output, uint32 *input, uint output_samples, uint input_samples);
void resample_cosine (uint32 *output, uint32 *input, uint output_samples, uint input_samples);
void resample_cubic (uint32 *output, uint32 *input, uint output_samples, uint input_samples);
void resample_hermite(uint32 *output, uint32 *input, uint output_samples, uint input_samples);
*/
Audio();
} *uiAudio;
#endif
+3 -3
View File
@@ -2,7 +2,7 @@ void AudioDS::sample(uint16 l_sample, uint16 r_sample) {
data.buffer[data.buffer_pos++] = (l_sample << 0) + (r_sample << 16);
if(data.buffer_pos < latency)return;
uint32 ring_pos, pos, size;
DWORD ring_pos, pos, size;
for(;;) {
dsb_b->GetCurrentPosition(&pos, 0);
ring_pos = pos / data.ring_size;
@@ -30,7 +30,7 @@ void *output;
data.buffer[data.buffer_pos++] = (l_sample << 0) + (r_sample << 16);
//if(data.buffer_pos & 15)return;
uint32 ring_pos, pos, size;
DWORD ring_pos, pos, size;
dsb_b->GetCurrentPosition(&pos, 0);
ring_pos = pos / data.ring_size;
if(ring_pos == data.ring_pos)return;
@@ -66,7 +66,7 @@ void AudioDS::clear_audio() {
dsb_b->Stop();
dsb_b->SetCurrentPosition(0);
uint32 size;
DWORD size;
void *output;
dsb_b->Lock(0, data.ring_size * 3, &output, &size, 0, 0, 0);
memset(output, 0, size);
+11 -1
View File
@@ -29,6 +29,7 @@ struct Video {
static IntegerSetting synchronize;
static IntegerSetting fullscreen;
static IntegerSetting multiplier, aspect_correction, region;
static IntegerSetting aspect_ntsc_x, aspect_ntsc_y, aspect_pal_x, aspect_pal_y;
static IntegerSetting hardware_filter, software_filter;
static IntegerSetting frameskip;
static IntegerSetting use_vram;
@@ -40,10 +41,19 @@ IntegerSetting Video::multiplier(&config_file, "video.multiplier", "Video output
"2 = 2x (~512x448)\n"
"etc.",
IntegerSetting::Decimal, 2);
IntegerSetting Video::aspect_correction(&config_file, "video.aspect_correction", "Correct video aspect ratio", IntegerSetting::Boolean, true);
IntegerSetting Video::aspect_correction(&config_file, "video.aspect_correction",
"Correct video aspect ratio\n"
"Formula: width = width * video.aspect_<region>_x / video.aspect_<region>_y",
IntegerSetting::Boolean, true);
IntegerSetting Video::region(&config_file, "video.region", "Video output region\n"
"0 = NTSC, 1 = PAL",
IntegerSetting::Decimal, 0);
IntegerSetting Video::aspect_ntsc_x(&config_file, "video.aspect_ntsc_x", "", IntegerSetting::Decimal, 54);
IntegerSetting Video::aspect_ntsc_y(&config_file, "video.aspect_ntsc_y", "", IntegerSetting::Decimal, 47);
IntegerSetting Video::aspect_pal_x (&config_file, "video.aspect_pal_x", "", IntegerSetting::Decimal, 32);
IntegerSetting Video::aspect_pal_y (&config_file, "video.aspect_pal_y", "", IntegerSetting::Decimal, 23);
IntegerSetting Video::hardware_filter(&config_file, "video.hardware_filter", "Video hardware filter\n"
"0 = Point\n"
"1 = Linear\n",
+6 -2
View File
@@ -1,10 +1,14 @@
#include "dinput.h"
void InputDI::clear_input() {
memset(keystate, 0, sizeof keystate);
}
void InputDI::poll() {
clear_input();
HRESULT hr;
DIJOYSTATE2 js;
memset(keystate, 0, sizeof(keystate));
if(di_key) {
hr = di_key->GetDeviceState(256, keystate);
if(FAILED(hr)) {
+1
View File
@@ -15,6 +15,7 @@ LPDIRECTINPUT8 di;
LPDIRECTINPUTDEVICE8 di_key, di_joy[DIRECTINPUT_JOYMAX];
uint32 di_joy_count;
void clear_input();
void poll();
void init();
void term();
+1
View File
@@ -0,0 +1 @@
#include "input.h"
-3
View File
@@ -5,9 +5,6 @@ class Input { public:
virtual bool key_down(uint16 key) { return false; }
virtual bool key_up (uint16 key) { return !key_down(key); }
virtual void signal_key_down(uint16 key) {}
virtual void signal_key_up (uint16 key) {}
virtual void clear_input() {}
virtual void poll() {}
virtual void init() {}
-1
View File
@@ -1 +0,0 @@
#include "inputui.h"
-28
View File
@@ -1,28 +0,0 @@
/*****
* InputUI
*
* Input wrapper to capture UI key presses. Useful for when OS does not
* support direct input polling methods (eg Xorg, etc)
*****/
#ifndef INPUTUI_H
#define INPUTUI_H
class InputUI : public Input {
private:
bool keystate[65536];
public:
void init() {
Input::init();
memset(keystate, 0, sizeof(keystate));
}
bool key_down(uint16 key) { return keystate[key]; }
void signal_key_down(uint16 key) { keystate[key] = true; }
void signal_key_up (uint16 key) { keystate[key] = false; }
void clear_input() { memset(keystate, 0, sizeof(keystate)); }
};
#endif
+143
View File
@@ -0,0 +1,143 @@
#include "xinput.h"
bool InputX::key_down(uint16 key) {
#define map(i) (keymap[i >> 3] & (1 << (i & 7)))
switch(key) {
case keymap::esc: return map(0x09);
case keymap::f1: return map(0x43);
case keymap::f2: return map(0x44);
case keymap::f3: return map(0x45);
case keymap::f4: return map(0x46);
case keymap::f5: return map(0x47);
case keymap::f6: return map(0x48);
case keymap::f7: return map(0x49);
case keymap::f8: return map(0x4a);
case keymap::f9: return map(0x4b);
case keymap::f10: return map(0x4c);
case keymap::f11: return map(0x5f);
case keymap::f12: return map(0x60);
case keymap::print_screen: return map(0x6f);
case keymap::scroll_lock: return map(0x4e);
case keymap::pause: return map(0x6e);
case keymap::grave: return map(0x31);
case keymap::num_1: return map(0x0a);
case keymap::num_2: return map(0x0b);
case keymap::num_3: return map(0x0c);
case keymap::num_4: return map(0x0d);
case keymap::num_5: return map(0x0e);
case keymap::num_6: return map(0x0f);
case keymap::num_7: return map(0x10);
case keymap::num_8: return map(0x11);
case keymap::num_9: return map(0x12);
case keymap::num_0: return map(0x13);
case keymap::minus: return map(0x14);
case keymap::equal: return map(0x15);
case keymap::backspace: return map(0x16);
case keymap::ins: return map(0x6a);
case keymap::del: return map(0x6b);
case keymap::home: return map(0x61);
case keymap::end: return map(0x67);
case keymap::page_up: return map(0x63);
case keymap::page_down: return map(0x69);
case keymap::a: return map(0x26);
case keymap::b: return map(0x38);
case keymap::c: return map(0x36);
case keymap::d: return map(0x28);
case keymap::e: return map(0x1a);
case keymap::f: return map(0x29);
case keymap::g: return map(0x2a);
case keymap::h: return map(0x2b);
case keymap::i: return map(0x1f);
case keymap::j: return map(0x2c);
case keymap::k: return map(0x2d);
case keymap::l: return map(0x2e);
case keymap::m: return map(0x3a);
case keymap::n: return map(0x39);
case keymap::o: return map(0x20);
case keymap::p: return map(0x21);
case keymap::q: return map(0x18);
case keymap::r: return map(0x1b);
case keymap::s: return map(0x27);
case keymap::t: return map(0x1c);
case keymap::u: return map(0x1e);
case keymap::v: return map(0x37);
case keymap::w: return map(0x19);
case keymap::x: return map(0x35);
case keymap::y: return map(0x1d);
case keymap::z: return map(0x34);
case keymap::lbracket: return map(0x22);
case keymap::rbracket: return map(0x23);
case keymap::backslash: return map(0x33);
case keymap::semicolon: return map(0x2f);
case keymap::apostrophe: return map(0x30);
case keymap::comma: return map(0x3b);
case keymap::period: return map(0x3c);
case keymap::slash: return map(0x3d);
case keymap::kp_1: return map(0x57);
case keymap::kp_2: return map(0x58);
case keymap::kp_3: return map(0x59);
case keymap::kp_4: return map(0x53);
case keymap::kp_5: return map(0x54);
case keymap::kp_6: return map(0x55);
case keymap::kp_7: return map(0x4f);
case keymap::kp_8: return map(0x50);
case keymap::kp_9: return map(0x51);
case keymap::kp_plus: return map(0x56);
case keymap::kp_minus: return map(0x52);
case keymap::kp_mul: return map(0x3f);
case keymap::kp_div: return map(0x70);
case keymap::kp_enter: return map(0x6c);
case keymap::num_lock: return map(0x4d);
case keymap::caps_lock: return map(0x42);
case keymap::up: return map(0x62);
case keymap::down: return map(0x68);
case keymap::left: return map(0x64);
case keymap::right: return map(0x66);
case keymap::tab: return map(0x17);
case keymap::enter: return map(0x24);
case keymap::space: return map(0x41);
case keymap::lctrl: return map(0x25);
case keymap::rctrl: return map(0x6d);
case keymap::lalt: return map(0x40);
case keymap::ralt: return map(0x71);
case keymap::lshift: return map(0x32);
case keymap::rshift: return map(0x3e);
case keymap::lsuper: return map(0x73);
case keymap::rsuper: return map(0x74);
case keymap::menu: return map(0x75);
}
#undef map
return false;
}
void InputX::clear_input() {
memset(keymap, 0, sizeof keymap);
}
void InputX::poll() {
XQueryKeymap(display, keymap);
}
void InputX::init() {
Input::init();
display = XOpenDisplay(0);
}
void InputX::term() {
Input::term();
}
+26
View File
@@ -0,0 +1,26 @@
#ifndef XINPUT_H
#define XINPUT_H
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xv.h>
#include <X11/extensions/Xvlib.h>
#include <X11/extensions/XShm.h>
class InputX : public Input { public:
bool key_down(uint16 key);
void clear_input();
void poll();
void init();
void term();
private:
Display *display;
char keymap[32];
};
#endif
+5 -1
View File
@@ -40,7 +40,11 @@ void SNESInterface::audio_sample(uint16 l_sample, uint16 r_sample) {
//input
void SNESInterface::input_poll() {
uiInput->poll();
if(input_ready && input_ready() == false) {
uiInput->clear_input();
} else {
uiInput->poll();
}
input_manager.poll();
}
+6 -4
View File
@@ -16,10 +16,10 @@ uint multiplier = minmax<1, 5>(uint(config::video.multiplier));
width *= multiplier;
height *= multiplier;
if(config::video.aspect_correction == true) {
if(config::video.region == 0) {
width = uint( double(width) * 8.0 / 7.0 ); //NTSC
} else {
width = uint( double(width) * 48.0 / 35.0 ); //PAL
if(config::video.region == 0) { //NTSC
width = uint( double(width) * double(config::video.aspect_ntsc_x) / double(config::video.aspect_ntsc_y) );
} else { //PAL
width = uint( double(width) * double(config::video.aspect_pal_x) / double(config::video.aspect_pal_y) );
}
}
@@ -103,6 +103,7 @@ char fn[PATH_MAX];
cartridge.load(fn);
cartridge.load_end();
snes.power();
window_cheat_editor.refresh();
}
void unload_rom() {
@@ -112,6 +113,7 @@ void unload_rom() {
uiAudio->clear_audio();
}
window_main.set_text(BSNES_TITLE);
window_cheat_editor.refresh();
}
void reset() {
+8 -3
View File
@@ -18,7 +18,7 @@ bool _term_ = false;
#include "ui.cpp"
#include "event.cpp"
void alert(char *s, ...) {
void alert(const char *s, ...) {
char str[4096];
va_list args;
va_start(args, s);
@@ -27,7 +27,7 @@ va_list args;
fprintf(stdout, "%s\r\n", str);
}
void dprintf(char *s, ...) {
void dprintf(const char *s, ...) {
char str[4096];
va_list args;
va_start(args, s);
@@ -36,7 +36,7 @@ va_list args;
fprintf(stdout, "%s\r\n", str);
}
void dprintf(uint source, char *s, ...) {
void dprintf(uint source, const char *s, ...) {
char str[4096];
va_list args;
va_start(args, s);
@@ -58,6 +58,11 @@ void run() {
snes.runtoframe();
event::update_frame_counter();
}
#if defined(PLATFORM_WIN)
//prevent bsnes from consuming 100% CPU resources when idle
else { Sleep(1); }
#endif
}
#if defined(PLATFORM_WIN)
+59 -6
View File
@@ -1,4 +1,62 @@
void CheatEditorWindow::refresh() {
list.reset();
for(uint i = 0; i < cheat.count(); i++) {
bool enabled;
uint32 addr;
uint8 data;
char s_code[256], s_desc[256], s_result[1024];
cheat.get(i, enabled, addr, data, s_code, s_desc);
sprintf(s_result, "%s|%s|%s", enabled ? "Enabled" : "Disabled", s_code, s_desc);
list.add_item(s_result);
}
list.autosize_columns();
//enable controls only if cartridge is loaded
bool loaded = cartridge.loaded();
add_code.enable(loaded);
toggle_code.enable(loaded);
delete_code.enable(loaded);
}
bool CheatEditorWindow::message(uint id, uintptr_t param) {
ui::Control *control = (ui::Control*)param;
if(id == ui::Message::Clicked && control == &add_code) {
char s_code[256], s_desc[256];
code.get_text(s_code, sizeof s_code);
desc.get_text(s_desc, sizeof s_desc);
cheat.add(false, s_code, s_desc); //param 0 = new codes disabled by default
refresh();
return true;
}
if((id == ui::Message::Clicked && control == &toggle_code) ||
(id == ui::Message::DoubleClicked && control == &list)) {
int index = list.get_selection();
if(index >= 0 && index < cheat.count()) {
cheat.enabled(index) ? cheat.disable(index) : cheat.enable(index);
bool enabled;
uint32 addr;
uint8 data;
char s_code[256], s_desc[256], s_result[1024];
cheat.get(index, enabled, addr, data, s_code, s_desc);
sprintf(s_result, "%s|%s|%s", enabled ? "Enabled" : "Disabled", s_code, s_desc);
list.set_item(index, s_result);
}
return true;
}
if(id == ui::Message::Clicked && control == &delete_code) {
int index = list.get_selection();
if(index >= 0 && index < cheat.count()) {
cheat.remove(index);
refresh();
}
return true;
}
return true;
}
@@ -7,9 +65,6 @@ void CheatEditorWindow::setup() {
int x = 0, y = 0;
list.create(*this, ui::Listbox::Header | ui::Listbox::VerticalScrollAlways, x, y, 475, 285, "Status|Code|Description");
list.add_item("Enabled|0123-4567|Infinite Energy");
list.add_item("Disabled|89ab-cdef|Infinite Lives");
list.autosize_columns();
y += 290;
add_code.create (*this, 0, x, y, 155, 30, "Add Code");
@@ -20,7 +75,5 @@ int x = 0, y = 0;
code.create(*this, 0, x, y, 155, 30, "<code>");
desc.create(*this, 0, x + 160, y, 315, 30, "<description>");
add_code.disable();
toggle_code.disable();
delete_code.disable();
refresh();
}
+12 -10
View File
@@ -1,10 +1,12 @@
class CheatEditorWindow : public ui::Window { public:
ui::Listbox list;
ui::Button add_code;
ui::Button toggle_code;
ui::Button delete_code;
ui::Editbox code;
ui::Editbox desc;
bool message(uint id, uintptr_t param);
void setup();
} window_cheat_editor;
class CheatEditorWindow : public ui::Window { public:
ui::Listbox list;
ui::Button add_code;
ui::Button toggle_code;
ui::Button delete_code;
ui::Editbox code;
ui::Editbox desc;
bool message(uint id, uintptr_t param);
void setup();
void refresh();
} window_cheat_editor;
-20
View File
@@ -99,16 +99,6 @@ void InputConfigWindow::refresh_list() {
bool InputConfigWindow::message(uint id, uintptr_t param) {
ui::Control *control = (ui::Control*)param;
if(id == ui::Message::KeyDown) {
if(uiInput) { uiInput->signal_key_down(param); }
return true;
}
if(id == ui::Message::KeyUp) {
if(uiInput) { uiInput->signal_key_up(param); }
return true;
}
if(id == ui::Message::Changed && control == &list) {
int pos = list.get_selection();
setkey.enable(pos >= 0);
@@ -180,16 +170,6 @@ bool InputCaptureWindow::message(uint id, uintptr_t param) {
return false;
}
if(id == ui::Message::KeyDown) {
if(uiInput) { uiInput->signal_key_down(param); }
return true;
}
if(id == ui::Message::KeyUp) {
if(uiInput) { uiInput->signal_key_up(param); }
return true;
}
return true;
}
+2 -4
View File
@@ -20,10 +20,9 @@
#include "../video/xv.cpp"
#include "../video/gtk.cpp"
#include "../audio/ao.cpp"
#include "../input/xinput.cpp"
#endif
#include "../input/inputui.cpp"
void ui_init() {
window_main.setup();
window_about.setup();
@@ -49,7 +48,6 @@ void ui_init() {
(Audio*)new AudioDS(window_main.handle());
uiInput =
config::system.input == "none" ? (Input*)new Input() :
config::system.input == "ui" ? (Input*)new InputUI() :
(Input*)new InputDI(window_main.handle());
#elif defined(PLATFORM_X)
uiVideo =
@@ -61,7 +59,7 @@ void ui_init() {
(Audio*)new AudioAO(config::system.audio_flags);
uiInput =
config::system.input == "none" ? (Input*)new Input() :
(Input*)new InputUI();
(Input*)new InputX();
#endif
uiVideo->init();
+15 -9
View File
@@ -1,3 +1,13 @@
bool MainWindow::input_ready() {
#if defined(PLATFORM_X)
//FIXME: focused() is broken in X port
return true;
#endif
//only allow input when main window is focused
return focused() == true;
}
bool MainWindow::message(uint id, uintptr_t param) {
ui::Control *control = (ui::Control*)param;
if(id == ui::Message::Close) {
@@ -12,17 +22,11 @@ ui::Control *control = (ui::Control*)param;
}
if(id == ui::Message::KeyDown) {
if(uiInput) { uiInput->signal_key_down(param); }
if(param == keymap::esc) { event::toggle_menu(); }
if(param == keymap::f11) { event::toggle_fullscreen(); }
return true;
}
if(id == ui::Message::KeyUp) {
if(uiInput) { uiInput->signal_key_up(param); }
return true;
}
if(id == ui::Message::Clicked) {
if(control == &menu_file_load) {
event::load_rom();
@@ -108,13 +112,15 @@ ui::Control *control = (ui::Control*)param;
}
void MainWindow::setup() {
snesinterface.input_ready = bind(&MainWindow::input_ready, this);
ui::ControlGroup group;
create(ui::Window::Center, 256, 224, BSNES_TITLE);
set_background_color(0, 0, 0);
menu.create(*this);
menu_file.create(menu, "File");
menu_file_load.create(menu_file, "Load Cartridge");
menu_file_load.create(menu_file, "Load Cartridge ...");
menu_file_unload.create(menu_file, "Unload");
menu_file_sep1.create(menu_file);
menu_file_reset.create(menu_file, "Reset");
@@ -175,7 +181,7 @@ ui::ControlGroup group;
group.add(menu_settings_videoframeskip_7);
group.add(menu_settings_videoframeskip_8);
group.add(menu_settings_videoframeskip_9);
menu_settings_videoframeskip_0.create(menu_settings_videoframeskip, group, "0 (off)");
menu_settings_videoframeskip_0.create(menu_settings_videoframeskip, group, "0");
menu_settings_videoframeskip_sep1.create(menu_settings_videoframeskip);
menu_settings_videoframeskip_1.create(menu_settings_videoframeskip, group, "1");
menu_settings_videoframeskip_2.create(menu_settings_videoframeskip, group, "2");
@@ -210,7 +216,7 @@ ui::ControlGroup group;
menu_settings_speedreg.finish();
menu_settings_sep3.create(menu_settings);
menu_settings_config.create(menu_settings, "Configuration");
menu_settings_config.create(menu_settings, "Configuration ...");
menu_settings.finish();
menu_misc.create(menu, "Misc");
+2
View File
@@ -60,6 +60,8 @@ ui::MenuGroup menu_misc;
ui::Container view;
//
bool input_ready();
bool message(uint id, uintptr_t param = 0);
void setup();
void setup_menu();
-4
View File
@@ -11,10 +11,6 @@ void term_snes();
* hardware abstraction layer
*****/
#include "video/video.h"
#include "audio/audio.h"
#include "input/input.h"
#include "video/video.cpp"
#include "audio/audio.cpp"
#include "input/input.cpp"
+1
View File
@@ -0,0 +1 @@
#include "video.h"
+5
View File
@@ -1,3 +1,6 @@
#ifndef VIDEO_H
#define VIDEO_H
class Video { public:
enum Filter {
@@ -18,3 +21,5 @@ enum Filter {
Video() {}
virtual ~Video() {}
} *uiVideo;
#endif